// SPDX-License-Identifier: GPL-2.0-or-later /******************************************************************************* * This file contains main functions related to the iSCSI Target Core Driver. * * (c) Copyright 2007-2013 Datera, Inc. * * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> *
******************************************************************************/
/* * Note that IQN formatting is expected to be done in userspace, and * no explict IQN format checks are done here.
*/ struct iscsi_tiqn *iscsit_add_tiqn(unsignedchar *buf)
{ struct iscsi_tiqn *tiqn = NULL; int ret;
staticvoid iscsit_wait_for_tiqn(struct iscsi_tiqn *tiqn)
{ /* * Wait for accesses to said struct iscsi_tiqn to end.
*/
spin_lock(&tiqn->tiqn_state_lock); while (tiqn->tiqn_access_count != 0) {
spin_unlock(&tiqn->tiqn_state_lock);
msleep(10);
spin_lock(&tiqn->tiqn_state_lock);
}
spin_unlock(&tiqn->tiqn_state_lock);
}
void iscsit_del_tiqn(struct iscsi_tiqn *tiqn)
{ /* * iscsit_set_tiqn_shutdown sets tiqn->tiqn_state = TIQN_STATE_SHUTDOWN * while holding tiqn->tiqn_state_lock. This means that all subsequent * attempts to access this struct iscsi_tiqn will fail from both transport * fabric and control code paths.
*/ if (iscsit_set_tiqn_shutdown(tiqn) < 0) {
pr_err("iscsit_set_tiqn_shutdown() failed\n"); return;
}
match = iscsit_check_np_match(sockaddr, np, network_transport); if (match) { /* * Increment the np_exports reference count now to * prevent iscsit_del_np() below from being called * while iscsi_tpg_add_network_portal() is called.
*/
np->np_exports++;
spin_unlock_bh(&np->np_thread_lock); return np;
}
spin_unlock_bh(&np->np_thread_lock);
}
return NULL;
}
struct iscsi_np *iscsit_add_np( struct sockaddr_storage *sockaddr, int network_transport)
{ struct iscsi_np *np; int ret;
mutex_lock(&np_lock);
/* * Locate the existing struct iscsi_np if already active..
*/
np = iscsit_get_np(sockaddr, network_transport); if (np) {
mutex_unlock(&np_lock); return np;
}
ret = iscsi_target_setup_login_socket(np, sockaddr); if (ret != 0) {
kfree(np);
mutex_unlock(&np_lock); return ERR_PTR(ret);
}
np->np_thread = kthread_run(iscsi_target_login_thread, np, "iscsi_np"); if (IS_ERR(np->np_thread)) {
pr_err("Unable to create kthread: iscsi_np\n");
ret = PTR_ERR(np->np_thread);
kfree(np);
mutex_unlock(&np_lock); return ERR_PTR(ret);
} /* * Increment the np_exports reference count now to prevent * iscsit_del_np() below from being run while a new call to * iscsi_tpg_add_network_portal() for a matching iscsi_np is * active. We don't need to hold np->np_thread_lock at this * point because iscsi_np has not been added to g_np_list yet.
*/
np->np_exports = 1;
np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
if (np->np_thread) { /* * We need to send the signal to wakeup Linux/Net * which may be sleeping in sock_accept()..
*/
send_sig(SIGINT, np->np_thread, 1);
kthread_stop(np->np_thread);
np->np_thread = NULL;
}
cmd->buf_ptr = kmemdup(buf, ISCSI_HDR_LEN, GFP_KERNEL); if (!cmd->buf_ptr) {
pr_err("Unable to allocate memory for cmd->buf_ptr\n");
iscsit_free_cmd(cmd, false); return -1;
}
if (add_to_conn) {
spin_lock_bh(&conn->cmd_lock);
list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
spin_unlock_bh(&conn->cmd_lock);
}
cmd->i_state = ISTATE_SEND_REJECT;
iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); /* * Perform the kref_put now if se_cmd has already been setup by * scsit_setup_scsi_cmd()
*/ if (do_put) {
pr_debug("iscsi reject: calling target_put_sess_cmd >>>>>>\n");
target_put_sess_cmd(&cmd->se_cmd);
} return -1;
}
/* * Map some portion of the allocated scatterlist to an iovec, suitable for * kernel sockets to copy data in/out.
*/ staticint iscsit_map_iovec(struct iscsit_cmd *cmd, struct kvec *iov, int nvec,
u32 data_offset, u32 data_length)
{
u32 i = 0, orig_data_length = data_length; struct scatterlist *sg; unsignedint page_off;
/* * We know each entry in t_data_sg contains a page.
*/
u32 ent = data_offset / PAGE_SIZE;
/* FIXME; Add checks for AdditionalHeaderSegment */
if (!(hdr->flags & ISCSI_FLAG_CMD_WRITE) &&
!(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {
pr_err("ISCSI_FLAG_CMD_WRITE & ISCSI_FLAG_CMD_FINAL" " not set. Bad iSCSI Initiator.\n"); return iscsit_add_reject_cmd(cmd,
ISCSI_REASON_BOOKMARK_INVALID, buf);
}
if (((hdr->flags & ISCSI_FLAG_CMD_READ) ||
(hdr->flags & ISCSI_FLAG_CMD_WRITE)) && !hdr->data_length) { /* * From RFC-3720 Section 10.3.1: * * "Either or both of R and W MAY be 1 when either the * Expected Data Transfer Length and/or Bidirectional Read * Expected Data Transfer Length are 0" * * For this case, go ahead and clear the unnecssary bits * to avoid any confusion with ->data_direction.
*/
hdr->flags &= ~ISCSI_FLAG_CMD_READ;
hdr->flags &= ~ISCSI_FLAG_CMD_WRITE;
pr_warn("ISCSI_FLAG_CMD_READ or ISCSI_FLAG_CMD_WRITE" " set when Expected Data Transfer Length is 0 for" " CDB: 0x%02x, Fixing up flags\n", hdr->cdb[0]);
}
if (!(hdr->flags & ISCSI_FLAG_CMD_READ) &&
!(hdr->flags & ISCSI_FLAG_CMD_WRITE) && (hdr->data_length != 0)) {
pr_err("ISCSI_FLAG_CMD_READ and/or ISCSI_FLAG_CMD_WRITE" " MUST be set if Expected Data Transfer Length is not 0." " Bad iSCSI Initiator\n"); return iscsit_add_reject_cmd(cmd,
ISCSI_REASON_BOOKMARK_INVALID, buf);
}
if ((hdr->flags & ISCSI_FLAG_CMD_READ) &&
(hdr->flags & ISCSI_FLAG_CMD_WRITE)) {
pr_err("Bidirectional operations not supported!\n"); return iscsit_add_reject_cmd(cmd,
ISCSI_REASON_BOOKMARK_INVALID, buf);
}
if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
pr_err("Illegally set Immediate Bit in iSCSI Initiator" " Scsi Command PDU.\n"); return iscsit_add_reject_cmd(cmd,
ISCSI_REASON_BOOKMARK_INVALID, buf);
}
if (payload_length && !conn->sess->sess_ops->ImmediateData) {
pr_err("ImmediateData=No but DataSegmentLength=%u," " protocol error.\n", payload_length); return iscsit_add_reject_cmd(cmd,
ISCSI_REASON_PROTOCOL_ERROR, buf);
}
if ((be32_to_cpu(hdr->data_length) == payload_length) &&
(!(hdr->flags & ISCSI_FLAG_CMD_FINAL))) {
pr_err("Expected Data Transfer Length and Length of" " Immediate Data are the same, but ISCSI_FLAG_CMD_FINAL" " bit is not set protocol error\n"); return iscsit_add_reject_cmd(cmd,
ISCSI_REASON_PROTOCOL_ERROR, buf);
}
int iscsit_process_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, struct iscsi_scsi_req *hdr)
{ int cmdsn_ret = 0; /* * Check the CmdSN against ExpCmdSN/MaxCmdSN here if * the Immediate Bit is not set, and no Immediate * Data is attached. * * A PDU/CmdSN carrying Immediate Data can only * be processed after the DataCRC has passed. * If the DataCRC fails, the CmdSN MUST NOT * be acknowledged. (See below)
*/ if (!cmd->immediate_data) {
cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
(unsignedchar *)hdr, hdr->cmdsn); if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) return -1; elseif (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
target_put_sess_cmd(&cmd->se_cmd); return 0;
}
}
/* * If no Immediate Data is attached, it's OK to return now.
*/ if (!cmd->immediate_data) { if (!cmd->sense_reason && cmd->unsolicited_data)
iscsit_set_unsolicited_dataout(cmd); if (!cmd->sense_reason) return 0;
target_put_sess_cmd(&cmd->se_cmd); return 0;
}
/* * Early CHECK_CONDITIONs with ImmediateData never make it to command * execution. These exceptions are processed in CmdSN order using * iscsit_check_received_cmdsn() in iscsit_get_immediate_data() below.
*/ if (cmd->sense_reason) return 1; /* * Call directly into transport_generic_new_cmd() to perform * the backend memory allocation.
*/
cmd->sense_reason = transport_generic_new_cmd(&cmd->se_cmd); if (cmd->sense_reason) return 1;
staticint
iscsit_get_immediate_data(struct iscsit_cmd *cmd, struct iscsi_scsi_req *hdr, bool dump_payload)
{ int cmdsn_ret = 0, immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION; int rc;
/* * Special case for Unsupported SAM WRITE Opcodes and ImmediateData=Yes.
*/ if (dump_payload) {
u32 length = min(cmd->se_cmd.data_length - cmd->write_data_done,
cmd->first_burst_len);
if (immed_ret == IMMEDIATE_DATA_NORMAL_OPERATION) { /* * A PDU/CmdSN carrying Immediate Data passed * DataCRC, check against ExpCmdSN/MaxCmdSN if * Immediate Bit is not set.
*/
cmdsn_ret = iscsit_sequence_cmd(cmd->conn, cmd,
(unsignedchar *)hdr, hdr->cmdsn); if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) return -1;
if (cmd->sense_reason || cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
target_put_sess_cmd(&cmd->se_cmd);
} elseif (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) { /* * Immediate Data failed DataCRC and ERL>=1, * silently drop this PDU and let the initiator * plug the CmdSN gap. * * FIXME: Send Unsolicited NOPIN with reserved * TTT here to help the initiator figure out * the missing CmdSN, although they should be * intelligent enough to determine the missing * CmdSN and issue a retry to plug the sequence.
*/
cmd->i_state = ISTATE_REMOVE;
iscsit_add_cmd_to_immediate_queue(cmd, cmd->conn, cmd->i_state);
} else/* immed_ret == IMMEDIATE_DATA_CANNOT_RECOVER */ return -1;
if (cmd->unsolicited_data) { int dump_unsolicited_data = 0;
if (conn->sess->sess_ops->InitialR2T) {
pr_err("Received unexpected unsolicited data" " while InitialR2T=Yes, protocol error.\n");
transport_send_check_condition_and_sense(&cmd->se_cmd,
TCM_UNEXPECTED_UNSOLICITED_DATA, 0); return -1;
} /* * Special case for dealing with Unsolicited DataOUT * and Unsupported SAM WRITE Opcodes and SE resource allocation * failures;
*/
/* Something's amiss if we're not in WRITE_PENDING state... */
WARN_ON(se_cmd->t_state != TRANSPORT_WRITE_PENDING); if (!(se_cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE))
dump_unsolicited_data = 1;
if (dump_unsolicited_data) { /* * Check if a delayed TASK_ABORTED status needs to * be sent now if the ISCSI_FLAG_CMD_FINAL has been * received with the unsolicited data out.
*/ if (hdr->flags & ISCSI_FLAG_CMD_FINAL)
iscsit_stop_dataout_timer(cmd);
return iscsit_dump_data_payload(conn, payload_length, 1);
}
} else { /* * For the normal solicited data path: * * Check for a delayed TASK_ABORTED status and dump any * incoming data out payload if one exists. Also, when the * ISCSI_FLAG_CMD_FINAL is set to denote the end of the current * data out sequence, we decrement outstanding_r2ts. Once * outstanding_r2ts reaches zero, go ahead and send the delayed * TASK_ABORTED status.
*/ if (se_cmd->transport_state & CMD_T_ABORTED) { if (hdr->flags & ISCSI_FLAG_CMD_FINAL &&
--cmd->outstanding_r2ts < 1)
iscsit_stop_dataout_timer(cmd);
if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {
pr_err("NopOUT Flag's, Left Most Bit not set, protocol error.\n"); if (!cmd) return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
(unsignedchar *)hdr);
if (hdr->itt == RESERVED_ITT && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
pr_err("NOPOUT ITT is reserved, but Immediate Bit is" " not set, protocol error.\n"); if (!cmd) return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
(unsignedchar *)hdr);
pr_debug("Got NOPOUT Ping %s ITT: 0x%08x, TTT: 0x%08x," " CmdSN: 0x%08x, ExpStatSN: 0x%08x, Length: %u\n",
hdr->itt == RESERVED_ITT ? "Response" : "Request",
hdr->itt, hdr->ttt, hdr->cmdsn, hdr->exp_statsn,
payload_length); /* * This is not a response to a Unsolicited NopIN, which means * it can either be a NOPOUT ping request (with a valid ITT), * or a NOPOUT not requesting a NOPIN (with a reserved ITT). * Either way, make sure we allocate an struct iscsit_cmd, as both * can contain ping data.
*/ if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
cmd->iscsi_opcode = ISCSI_OP_NOOP_OUT;
cmd->i_state = ISTATE_SEND_NOPIN;
cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ?
1 : 0);
conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
cmd->targ_xfer_tag = 0xFFFFFFFF;
cmd->cmd_sn = be32_to_cpu(hdr->cmdsn);
cmd->exp_stat_sn = be32_to_cpu(hdr->exp_statsn);
cmd->data_direction = DMA_NONE;
}
return 0;
}
EXPORT_SYMBOL(iscsit_setup_nop_out);
int iscsit_process_nop_out(struct iscsit_conn *conn, struct iscsit_cmd *cmd, struct iscsi_nopout *hdr)
{ struct iscsit_cmd *cmd_p = NULL; int cmdsn_ret = 0; /* * Initiator is expecting a NopIN ping reply..
*/ if (hdr->itt != RESERVED_ITT) { if (!cmd) return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
(unsignedchar *)hdr);
cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
(unsignedchar *)hdr, hdr->cmdsn); if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) return 0; if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) return -1;
return 0;
} /* * This was a response to a unsolicited NOPIN ping.
*/ if (hdr->ttt != cpu_to_be32(0xFFFFFFFF)) {
cmd_p = iscsit_find_cmd_from_ttt(conn, be32_to_cpu(hdr->ttt)); if (!cmd_p) return -EINVAL;
if ((function != ISCSI_TM_FUNC_ABORT_TASK) &&
((function != ISCSI_TM_FUNC_TASK_REASSIGN) &&
hdr->rtt != RESERVED_ITT)) {
pr_err("RefTaskTag should be set to 0xFFFFFFFF.\n");
hdr->rtt = RESERVED_ITT;
}
if ((function == ISCSI_TM_FUNC_TASK_REASSIGN) &&
!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
pr_err("Task Management Request TASK_REASSIGN not" " issued as immediate command, bad iSCSI Initiator" "implementation\n"); return iscsit_add_reject_cmd(cmd,
ISCSI_REASON_PROTOCOL_ERROR, buf);
} if ((function != ISCSI_TM_FUNC_ABORT_TASK) &&
be32_to_cpu(hdr->refcmdsn) != ISCSI_RESERVED_TAG)
hdr->refcmdsn = cpu_to_be32(ISCSI_RESERVED_TAG);
cmd->iscsi_opcode = ISCSI_OP_SCSI_TMFUNC;
cmd->i_state = ISTATE_SEND_TASKMGTRSP;
cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
cmd->init_task_tag = hdr->itt;
cmd->targ_xfer_tag = 0xFFFFFFFF;
cmd->cmd_sn = be32_to_cpu(hdr->cmdsn);
cmd->exp_stat_sn = be32_to_cpu(hdr->exp_statsn);
se_tmr = cmd->se_cmd.se_tmr_req;
tmr_req = cmd->tmr_req; /* * Locate the struct se_lun for all TMRs not related to ERL=2 TASK_REASSIGN
*/ if (function != ISCSI_TM_FUNC_TASK_REASSIGN) {
ret = transport_lookup_tmr_lun(&cmd->se_cmd); if (ret < 0) {
se_tmr->response = ISCSI_TMF_RSP_NO_LUN; goto attach;
}
}
switch (function) { case ISCSI_TM_FUNC_ABORT_TASK:
se_tmr->response = iscsit_tmr_abort_task(cmd, buf); if (se_tmr->response) goto attach; break; case ISCSI_TM_FUNC_ABORT_TASK_SET: case ISCSI_TM_FUNC_CLEAR_ACA: case ISCSI_TM_FUNC_CLEAR_TASK_SET: case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: break; case ISCSI_TM_FUNC_TARGET_WARM_RESET: if (iscsit_tmr_task_warm_reset(conn, tmr_req, buf) < 0) {
se_tmr->response = ISCSI_TMF_RSP_AUTH_FAILED; goto attach;
} break; case ISCSI_TM_FUNC_TARGET_COLD_RESET: if (iscsit_tmr_task_cold_reset(conn, tmr_req, buf) < 0) {
se_tmr->response = ISCSI_TMF_RSP_AUTH_FAILED; goto attach;
} break; case ISCSI_TM_FUNC_TASK_REASSIGN:
se_tmr->response = iscsit_tmr_task_reassign(cmd, buf); /* * Perform sanity checks on the ExpDataSN only if the * TASK_REASSIGN was successful.
*/ if (se_tmr->response) break;
if (out_of_order_cmdsn || !(hdr->opcode & ISCSI_OP_IMMEDIATE)) return 0; /* * Found the referenced task, send to transport for processing.
*/ if (se_tmr->call_transport) return transport_generic_handle_tmr(&cmd->se_cmd);
/* * Could not find the referenced LUN, task, or Task Management * command not authorized or supported. Change state and * let the tx_thread send the response. * * For connection recovery, this is also the default action for * TMR TASK_REASSIGN.
*/
iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
target_put_sess_cmd(&cmd->se_cmd); return 0;
}
EXPORT_SYMBOL(iscsit_handle_task_mgt_cmd);
/* #warning FIXME: Support Text Command parameters besides SendTargets */ int
iscsit_setup_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, struct iscsi_text *hdr)
{
u32 payload_length = ntoh24(hdr->dlength);
if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
pr_err("Unable to accept text parameter length: %u" "greater than MaxXmitDataSegmentLength %u.\n",
payload_length, conn->conn_ops->MaxXmitDataSegmentLength); return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
(unsignedchar *)hdr);
}
if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL) ||
(hdr->flags & ISCSI_FLAG_TEXT_CONTINUE)) {
pr_err("Multi sequence text commands currently not supported\n"); return iscsit_reject_cmd(cmd, ISCSI_REASON_CMD_NOT_SUPPORTED,
(unsignedchar *)hdr);
}
pr_debug("Received logout request CLOSECONNECTION for CID:" " %hu on CID: %hu.\n", cmd->logout_cid, conn->cid);
/* * A Logout Request with a CLOSECONNECTION reason code for a CID * can arrive on a connection with a differing CID.
*/ if (conn->cid == cmd->logout_cid) {
spin_lock_bh(&conn->state_lock);
pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
conn->conn_state = TARG_CONN_STATE_IN_LOGOUT;
spin_unlock_bh(&conn->state_lock);
} else { /* * Handle all different cid CLOSECONNECTION requests in * iscsit_logout_post_handler_diffcid() as to give enough * time for any non immediate command's CmdSN to be * acknowledged on the connection in question. * * Here we simply make sure the CID is still around.
*/
l_conn = iscsit_get_conn_from_cid(sess,
cmd->logout_cid); if (!l_conn) {
cmd->logout_response = ISCSI_LOGOUT_CID_NOT_FOUND;
iscsit_add_cmd_to_response_queue(cmd, conn,
cmd->i_state);
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.25 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.