/* * Called from iscsit_handle_task_mgt_cmd().
*/ int iscsit_tmr_task_warm_reset( struct iscsit_conn *conn, struct iscsi_tmr_req *tmr_req, unsignedchar *buf)
{ struct iscsit_session *sess = conn->sess; struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
if (!na->tmr_warm_reset) {
pr_err("TMR Opcode TARGET_WARM_RESET authorization" " failed for Initiator Node: %s\n",
sess->se_sess->se_node_acl->initiatorname); return -1;
} /* * Do the real work in transport_generic_do_tmr().
*/ return 0;
}
if (!na->tmr_cold_reset) {
pr_err("TMR Opcode TARGET_COLD_RESET authorization" " failed for Initiator Node: %s\n",
sess->se_sess->se_node_acl->initiatorname); return -1;
} /* * Do the real work in transport_generic_do_tmr().
*/ return 0;
}
se_tmr->ref_task_tag = (__force u32)hdr->rtt;
tmr_req->ref_cmd = ref_cmd;
tmr_req->exp_data_sn = be32_to_cpu(hdr->exp_datasn);
tmr_req->conn_recovery = cr;
tmr_req->task_reassign = 1; /* * Command can now be reassigned to a new connection. * The task management response must be sent before the * reassignment actually happens. See iscsi_tmr_post_handler().
*/ return ISCSI_TMF_RSP_COMPLETE;
}
if (!cmd->cr) {
pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" " is NULL!\n", cmd->init_task_tag); return -1;
}
cr = cmd->cr;
/* * Reset the StatSN so a new one for this commands new connection * will be assigned. * Reset the ExpStatSN as well so we may receive Status SNACKs.
*/
cmd->stat_sn = cmd->exp_stat_sn = 0;
staticint iscsit_task_reassign_complete_write( struct iscsit_cmd *cmd, struct iscsi_tmr_req *tmr_req)
{ int no_build_r2ts = 0;
u32 length = 0, offset = 0; struct iscsit_conn *conn = cmd->conn; struct se_cmd *se_cmd = &cmd->se_cmd; /* * The Initiator must not send a R2T SNACK with a Begrun less than * the TMR TASK_REASSIGN's ExpDataSN.
*/ if (!tmr_req->exp_data_sn) {
cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK;
cmd->acked_data_sn = 0;
} else {
cmd->cmd_flags |= ICF_GOT_DATACK_SNACK;
cmd->acked_data_sn = (tmr_req->exp_data_sn - 1);
}
/* * The TMR TASK_REASSIGN's ExpDataSN contains the next R2TSN the * Initiator is expecting. The Target controls all WRITE operations * so if we have received all DataOUT we can safety ignore Initiator.
*/ if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) { if (!(cmd->se_cmd.transport_state & CMD_T_SENT)) {
pr_debug("WRITE ITT: 0x%08x: t_state: %d" " never sent to transport\n",
cmd->init_task_tag, cmd->se_cmd.t_state);
target_execute_cmd(se_cmd); return 0;
}
/* * Special case to deal with DataSequenceInOrder=No and Non-Immeidate * Unsolicited DataOut.
*/ if (cmd->unsolicited_data) {
cmd->unsolicited_data = 0;
if (no_build_r2ts) return 0;
} /* * iscsit_build_r2ts_for_cmd() can handle the rest from here.
*/ return conn->conn_transport->iscsit_get_dataout(conn, cmd, true);
}
staticint iscsit_task_reassign_complete_read( struct iscsit_cmd *cmd, struct iscsi_tmr_req *tmr_req)
{ struct iscsit_conn *conn = cmd->conn; struct iscsi_datain_req *dr; struct se_cmd *se_cmd = &cmd->se_cmd; /* * The Initiator must not send a Data SNACK with a BegRun less than * the TMR TASK_REASSIGN's ExpDataSN.
*/ if (!tmr_req->exp_data_sn) {
cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK;
cmd->acked_data_sn = 0;
} else {
cmd->cmd_flags |= ICF_GOT_DATACK_SNACK;
cmd->acked_data_sn = (tmr_req->exp_data_sn - 1);
}
if (!(cmd->se_cmd.transport_state & CMD_T_SENT)) {
pr_debug("READ ITT: 0x%08x: t_state: %d never sent to" " transport\n", cmd->init_task_tag,
cmd->se_cmd.t_state);
target_submit(se_cmd); return 0;
}
if (!(se_cmd->transport_state & CMD_T_COMPLETE)) {
pr_err("READ ITT: 0x%08x: t_state: %d, never returned" " from transport\n", cmd->init_task_tag,
cmd->se_cmd.t_state); return -1;
}
dr = iscsit_allocate_datain_req(); if (!dr) return -1; /* * The TMR TASK_REASSIGN's ExpDataSN contains the next DataSN the * Initiator is expecting.
*/
dr->data_sn = dr->begrun = tmr_req->exp_data_sn;
dr->runlength = 0;
dr->generate_recovery_values = 1;
dr->recovery = DATAIN_CONNECTION_RECOVERY;
if (!cmd->cr) {
pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" " is NULL!\n", cmd->init_task_tag); return -1;
}
cr = cmd->cr;
/* * Reset the StatSN so a new one for this commands new connection * will be assigned. * Reset the ExpStatSN as well so we may receive Status SNACKs.
*/
cmd->stat_sn = cmd->exp_stat_sn = 0;
switch (cmd->data_direction) { case DMA_TO_DEVICE: return iscsit_task_reassign_complete_write(cmd, tmr_req); case DMA_FROM_DEVICE: return iscsit_task_reassign_complete_read(cmd, tmr_req); case DMA_NONE: return iscsit_task_reassign_complete_none(cmd, tmr_req); default:
pr_err("Unknown cmd->data_direction: 0x%02x\n",
cmd->data_direction); return -1;
}
return 0;
}
staticint iscsit_task_reassign_complete( struct iscsi_tmr_req *tmr_req, struct iscsit_conn *conn)
{ struct iscsit_cmd *cmd; int ret = 0;
if (!tmr_req->ref_cmd) {
pr_err("TMR Request is missing a RefCmd struct iscsit_cmd.\n"); return -1;
}
cmd = tmr_req->ref_cmd;
cmd->conn = conn;
switch (cmd->iscsi_opcode) { case ISCSI_OP_NOOP_OUT:
ret = iscsit_task_reassign_complete_nop_out(tmr_req, conn); break; case ISCSI_OP_SCSI_CMD:
ret = iscsit_task_reassign_complete_scsi_cmnd(tmr_req, conn); break; default:
pr_err("Illegal iSCSI Opcode 0x%02x during" " command reallegiance\n", cmd->iscsi_opcode); return -1;
}
if (ret != 0) return ret;
pr_debug("Completed connection reallegiance for Opcode: 0x%02x," " ITT: 0x%08x to CID: %hu.\n", cmd->iscsi_opcode,
cmd->init_task_tag, conn->cid);
return 0;
}
/* * Handles special after-the-fact actions related to TMRs. * Right now the only one that its really needed for is * connection recovery releated TASK_REASSIGN.
*/ int iscsit_tmr_post_handler(struct iscsit_cmd *cmd, struct iscsit_conn *conn)
{ struct iscsi_tmr_req *tmr_req = cmd->tmr_req; struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req;
if (tmr_req->task_reassign &&
(se_tmr->response == ISCSI_TMF_RSP_COMPLETE)) return iscsit_task_reassign_complete(tmr_req, conn);
/* * Nothing to do here, but leave it for good measure. :-)
*/ staticint iscsit_task_reassign_prepare_read( struct iscsi_tmr_req *tmr_req, struct iscsit_conn *conn)
{ return 0;
}
/* * The command was in the process of receiving Unsolicited DataOUT when * the connection failed.
*/ if (cmd->unsolicited_data)
iscsit_task_reassign_prepare_unsolicited_dataout(cmd, conn);
/* * The Initiator is requesting R2Ts starting from zero, skip * checking acknowledged R2Ts and start checking struct iscsi_r2ts * greater than zero.
*/ if (!tmr_req->exp_data_sn) goto drop_unacknowledged_r2ts;
/* * We now check that the PDUs in DataOUT sequences below * the TMR TASK_REASSIGN ExpDataSN (R2TSN the Initiator is * expecting next) have all the DataOUT they require to complete * the DataOUT sequence. First scan from R2TSN 0 to TMR * TASK_REASSIGN ExpDataSN-1. * * If we have not received all DataOUT in question, we must * make sure to make the appropriate changes to values in * struct iscsit_cmd (and elsewhere depending on session parameters) * so iscsit_build_r2ts_for_cmd() in iscsit_task_reassign_complete_write() * will resend a new R2T for the DataOUT sequences in question.
*/
spin_lock_bh(&cmd->r2t_lock); if (list_empty(&cmd->cmd_r2t_list)) {
spin_unlock_bh(&cmd->r2t_lock); return -1;
}
if (r2t->r2t_sn >= tmr_req->exp_data_sn) continue; /* * Safely ignore Recovery R2Ts and R2Ts that have completed * DataOUT sequences.
*/ if (r2t->seq_complete) continue;
if (r2t->recovery_r2t) continue;
/* * DataSequenceInOrder=Yes: * * Taking into account the iSCSI implementation requirement of * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and * DataSequenceInOrder=Yes, we must take into consideration * the following: * * DataSequenceInOrder=No: * * Taking into account that the Initiator controls the (possibly * random) PDU Order in (possibly random) Sequence Order of * DataOUT the target requests with R2Ts, we must take into * consideration the following: * * DataPDUInOrder=Yes for DataSequenceInOrder=[Yes,No]: * * While processing non-complete R2T DataOUT sequence requests * the Target will re-request only the total sequence length * minus current received offset. This is because we must * assume the initiator will continue sending DataOUT from the * last PDU before the connection failed. * * DataPDUInOrder=No for DataSequenceInOrder=[Yes,No]: * * While processing non-complete R2T DataOUT sequence requests * the Target will re-request the entire DataOUT sequence if * any single PDU is missing from the sequence. This is because * we have no logical method to determine the next PDU offset, * and we must assume the Initiator will be sending any random * PDU offset in the current sequence after TASK_REASSIGN * has completed.
*/ if (conn->sess->sess_ops->DataSequenceInOrder) { if (!first_incomplete_r2t) {
cmd->r2t_offset -= r2t->xfer_len; goto next;
}
/* * We now drop all unacknowledged R2Ts, ie: ExpDataSN from TMR * TASK_REASSIGN to the last R2T in the list.. We are also careful * to check that the Initiator is not requesting R2Ts for DataOUT * sequences it has already completed. * * Free each R2T in question and adjust values in struct iscsit_cmd * accordingly so iscsit_build_r2ts_for_cmd() do the rest of * the work after the TMR TASK_REASSIGN Response is sent.
*/
drop_unacknowledged_r2ts:
spin_lock_bh(&cmd->r2t_lock);
list_for_each_entry_safe(r2t, r2t_tmp, &cmd->cmd_r2t_list, r2t_list) { /* * Skip up to the R2T Sequence number provided by the * iSCSI TASK_REASSIGN TMR
*/ if (r2t->r2t_sn < tmr_req->exp_data_sn) continue;
if (r2t->seq_complete) {
pr_err("Initiator is requesting R2Ts from" " R2TSN: 0x%08x, but R2TSN: 0x%08x, Offset: %u," " Length: %u is already complete." " BAD INITIATOR ERL=2 IMPLEMENTATION!\n",
tmr_req->exp_data_sn, r2t->r2t_sn,
r2t->offset, r2t->xfer_len);
spin_unlock_bh(&cmd->r2t_lock); return -1;
}
if (r2t->recovery_r2t) {
iscsit_free_r2t(r2t, cmd); continue;
}
/* DataSequenceInOrder=Yes: * * Taking into account the iSCSI implementation requirement of * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and * DataSequenceInOrder=Yes, it's safe to subtract the R2Ts * entire transfer length from the commands R2T offset marker. * * DataSequenceInOrder=No: * * We subtract the difference from struct iscsi_seq between the * current offset and original offset from cmd->write_data_done * for account for DataOUT PDUs already received. Then reset * the current offset to the original and zero out the current * burst length, to make sure we re-request the entire DataOUT * sequence.
*/ if (conn->sess->sess_ops->DataSequenceInOrder)
cmd->r2t_offset -= r2t->xfer_len; else
cmd->seq_send_order--;
/* * Performs sanity checks TMR TASK_REASSIGN's ExpDataSN for * a given struct iscsit_cmd.
*/ int iscsit_check_task_reassign_expdatasn( struct iscsi_tmr_req *tmr_req, struct iscsit_conn *conn)
{ struct iscsit_cmd *ref_cmd = tmr_req->ref_cmd;
if (ref_cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) return 0;
if (ref_cmd->se_cmd.se_cmd_flags & SCF_SENT_CHECK_CONDITION) return 0;
if (ref_cmd->data_direction == DMA_NONE) return 0;
/* * For READs the TMR TASK_REASSIGNs ExpDataSN contains the next DataSN * of DataIN the Initiator is expecting. * * Also check that the Initiator is not re-requesting DataIN that has * already been acknowledged with a DataAck SNACK.
*/ if (ref_cmd->data_direction == DMA_FROM_DEVICE) { if (tmr_req->exp_data_sn > ref_cmd->data_sn) {
pr_err("Received ExpDataSN: 0x%08x for READ" " in TMR TASK_REASSIGN greater than command's" " DataSN: 0x%08x.\n", tmr_req->exp_data_sn,
ref_cmd->data_sn); return -1;
} if ((ref_cmd->cmd_flags & ICF_GOT_DATACK_SNACK) &&
(tmr_req->exp_data_sn <= ref_cmd->acked_data_sn)) {
pr_err("Received ExpDataSN: 0x%08x for READ" " in TMR TASK_REASSIGN for previously" " acknowledged DataIN: 0x%08x," " protocol error\n", tmr_req->exp_data_sn,
ref_cmd->acked_data_sn); return -1;
} return iscsit_task_reassign_prepare_read(tmr_req, conn);
}
/* * For WRITEs the TMR TASK_REASSIGNs ExpDataSN contains the next R2TSN * for R2Ts the Initiator is expecting. * * Do the magic in iscsit_task_reassign_prepare_write().
*/ if (ref_cmd->data_direction == DMA_TO_DEVICE) { if (tmr_req->exp_data_sn > ref_cmd->r2t_sn) {
pr_err("Received ExpDataSN: 0x%08x for WRITE" " in TMR TASK_REASSIGN greater than command's" " R2TSN: 0x%08x.\n", tmr_req->exp_data_sn,
ref_cmd->r2t_sn); return -1;
} return iscsit_task_reassign_prepare_write(tmr_req, conn);
}
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.