static int iscsit_dataout_within_command_recovery_check( struct iscsi_cmd *cmd, unsigned char *buf) { struct iscsi_conn *conn = cmd->conn; struct iscsi_data *hdr = (struct iscsi_data *) buf; u32 payload_length = ntoh24(hdr->dlength); if (conn->sess->sess_ops->DataSequenceInOrder) { if ((cmd->cmd_flags & ICF_WITHIN_COMMAND_RECOVERY) && (cmd->write_data_done != hdr->offset)) goto dump; cmd->cmd_flags &= ~ICF_WITHIN_COMMAND_RECOVERY; } else { struct iscsi_seq *seq; seq = iscsit_get_seq_holder(cmd, hdr->offset, payload_length); if (!seq) return DATAOUT_CANNOT_RECOVER; cmd->seq_ptr = seq; if (conn->sess->sess_ops->DataPDUInOrder) { if ((seq->status == DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY) && ((seq->offset != hdr->offset) || (seq->data_sn != hdr->datasn))) goto dump; } else { if ((seq->status == DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY) && (seq->data_sn != hdr->datasn)) goto dump; } if (seq->status == DATAOUT_SEQUENCE_COMPLETE) goto dump; if (seq->status != DATAOUT_SEQUENCE_COMPLETE) seq->status = 0; } return DATAOUT_NORMAL; dump: pr_err("Dumping DataOUT PDU Offset: %u Length: %d DataSN:" " 0x%08x\n", hdr->offset, payload_length, hdr->datasn); return iscsit_dump_data_payload(conn, payload_length, 1); }
static int iscsit_dataout_within_command_recovery_check( struct iscsi_cmd *cmd, unsigned char *buf) { struct iscsi_conn *conn = cmd->conn; struct iscsi_data *hdr = (struct iscsi_data *) buf; u32 payload_length = ntoh24(hdr->dlength); /* * We do the within-command recovery checks here as it is * the first function called in iscsi_check_pre_dataout(). * Basically, if we are in within-command recovery and * the PDU does not contain the offset the sequence needs, * dump the payload. * * This only applies to DataPDUInOrder=Yes, for * DataPDUInOrder=No we only re-request the failed PDU * and check that all PDUs in a sequence are received * upon end of sequence. */ if (conn->sess->sess_ops->DataSequenceInOrder) { if ((cmd->cmd_flags & ICF_WITHIN_COMMAND_RECOVERY) && cmd->write_data_done != be32_to_cpu(hdr->offset)) goto dump; cmd->cmd_flags &= ~ICF_WITHIN_COMMAND_RECOVERY; } else { struct iscsi_seq *seq; seq = iscsit_get_seq_holder(cmd, be32_to_cpu(hdr->offset), payload_length); if (!seq) return DATAOUT_CANNOT_RECOVER; /* * Set the struct iscsi_seq pointer to reuse later. */ cmd->seq_ptr = seq; if (conn->sess->sess_ops->DataPDUInOrder) { if (seq->status == DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY && (seq->offset != be32_to_cpu(hdr->offset) || seq->data_sn != be32_to_cpu(hdr->datasn))) goto dump; } else { if (seq->status == DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY && seq->data_sn != be32_to_cpu(hdr->datasn)) goto dump; } if (seq->status == DATAOUT_SEQUENCE_COMPLETE) goto dump; if (seq->status != DATAOUT_SEQUENCE_COMPLETE) seq->status = 0; } return DATAOUT_NORMAL; dump: pr_err("Dumping DataOUT PDU Offset: %u Length: %d DataSN:" " 0x%08x\n", hdr->offset, payload_length, hdr->datasn); return iscsit_dump_data_payload(conn, payload_length, 1); }
static int iscsit_dataout_check_sequence( struct iscsi_cmd *cmd, unsigned char *buf) { u32 next_burst_len; struct iscsi_conn *conn = cmd->conn; struct iscsi_seq *seq = NULL; struct iscsi_data *hdr = (struct iscsi_data *) buf; u32 payload_length = ntoh24(hdr->dlength); /* * For DataSequenceInOrder=Yes: Check that the offset and offset+length * is within range as defined by iscsi_set_dataout_sequence_values(). * * For DataSequenceInOrder=No: Check that an struct iscsi_seq exists for * offset+length tuple. */ if (conn->sess->sess_ops->DataSequenceInOrder) { /* * Due to possibility of recovery DataOUT sent by the initiator * fullfilling an Recovery R2T, it's best to just dump the * payload here, instead of erroring out. */ if ((be32_to_cpu(hdr->offset) < cmd->seq_start_offset) || ((be32_to_cpu(hdr->offset) + payload_length) > cmd->seq_end_offset)) { pr_err("Command ITT: 0x%08x with Offset: %u," " Length: %u outside of Sequence %u:%u while" " DataSequenceInOrder=Yes.\n", cmd->init_task_tag, be32_to_cpu(hdr->offset), payload_length, cmd->seq_start_offset, cmd->seq_end_offset); if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) return DATAOUT_CANNOT_RECOVER; return DATAOUT_WITHIN_COMMAND_RECOVERY; } next_burst_len = (cmd->next_burst_len + payload_length); } else { seq = iscsit_get_seq_holder(cmd, be32_to_cpu(hdr->offset), payload_length); if (!seq) return DATAOUT_CANNOT_RECOVER; /* * Set the struct iscsi_seq pointer to reuse later. */ cmd->seq_ptr = seq; if (seq->status == DATAOUT_SEQUENCE_COMPLETE) { if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) return DATAOUT_CANNOT_RECOVER; return DATAOUT_WITHIN_COMMAND_RECOVERY; } next_burst_len = (seq->next_burst_len + payload_length); } if (next_burst_len > conn->sess->sess_ops->MaxBurstLength) { pr_err("Command ITT: 0x%08x, NextBurstLength: %u and" " Length: %u exceeds MaxBurstLength: %u. protocol" " error.\n", cmd->init_task_tag, (next_burst_len - payload_length), payload_length, conn->sess->sess_ops->MaxBurstLength); return DATAOUT_CANNOT_RECOVER; } /* * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity * checks for the current DataOUT Sequence. */ if (hdr->flags & ISCSI_FLAG_CMD_FINAL) { /* * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of * sequence checks are handled in * iscsit_dataout_datapduinorder_no_fbit(). */ if (!conn->sess->sess_ops->DataPDUInOrder) goto out; if (conn->sess->sess_ops->DataSequenceInOrder) { if ((next_burst_len < conn->sess->sess_ops->MaxBurstLength) && ((cmd->write_data_done + payload_length) < cmd->se_cmd.data_length)) { pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL" " before end of DataOUT sequence, protocol" " error.\n", cmd->init_task_tag); return DATAOUT_CANNOT_RECOVER; } } else { if (next_burst_len < seq->xfer_len) { pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL" " before end of DataOUT sequence, protocol" " error.\n", cmd->init_task_tag); return DATAOUT_CANNOT_RECOVER; } } } else { if (conn->sess->sess_ops->DataSequenceInOrder) { if (next_burst_len == conn->sess->sess_ops->MaxBurstLength) { pr_err("Command ITT: 0x%08x reached" " MaxBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is" " not set, protocol error.", cmd->init_task_tag, conn->sess->sess_ops->MaxBurstLength); return DATAOUT_CANNOT_RECOVER; } if ((cmd->write_data_done + payload_length) == cmd->se_cmd.data_length) { pr_err("Command ITT: 0x%08x reached" " last DataOUT PDU in sequence but ISCSI_FLAG_" "CMD_FINAL is not set, protocol error.\n", cmd->init_task_tag); return DATAOUT_CANNOT_RECOVER; } } else { if (next_burst_len == seq->xfer_len) { pr_err("Command ITT: 0x%08x reached" " last DataOUT PDU in sequence but ISCSI_FLAG_" "CMD_FINAL is not set, protocol error.\n", cmd->init_task_tag); return DATAOUT_CANNOT_RECOVER; } } } out: return DATAOUT_NORMAL; }
static int iscsit_dataout_check_sequence( struct iscsi_cmd *cmd, unsigned char *buf) { u32 next_burst_len; struct iscsi_conn *conn = cmd->conn; struct iscsi_seq *seq = NULL; struct iscsi_data *hdr = (struct iscsi_data *) buf; u32 payload_length = ntoh24(hdr->dlength); if (conn->sess->sess_ops->DataSequenceInOrder) { if ((hdr->offset < cmd->seq_start_offset) || ((hdr->offset + payload_length) > cmd->seq_end_offset)) { pr_err("Command ITT: 0x%08x with Offset: %u," " Length: %u outside of Sequence %u:%u while" " DataSequenceInOrder=Yes.\n", cmd->init_task_tag, hdr->offset, payload_length, cmd->seq_start_offset, cmd->seq_end_offset); if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) return DATAOUT_CANNOT_RECOVER; return DATAOUT_WITHIN_COMMAND_RECOVERY; } next_burst_len = (cmd->next_burst_len + payload_length); } else { seq = iscsit_get_seq_holder(cmd, hdr->offset, payload_length); if (!seq) return DATAOUT_CANNOT_RECOVER; cmd->seq_ptr = seq; if (seq->status == DATAOUT_SEQUENCE_COMPLETE) { if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) return DATAOUT_CANNOT_RECOVER; return DATAOUT_WITHIN_COMMAND_RECOVERY; } next_burst_len = (seq->next_burst_len + payload_length); } if (next_burst_len > conn->sess->sess_ops->MaxBurstLength) { pr_err("Command ITT: 0x%08x, NextBurstLength: %u and" " Length: %u exceeds MaxBurstLength: %u. protocol" " error.\n", cmd->init_task_tag, (next_burst_len - payload_length), payload_length, conn->sess->sess_ops->MaxBurstLength); return DATAOUT_CANNOT_RECOVER; } if (hdr->flags & ISCSI_FLAG_CMD_FINAL) { if (!conn->sess->sess_ops->DataPDUInOrder) goto out; if (conn->sess->sess_ops->DataSequenceInOrder) { if ((next_burst_len < conn->sess->sess_ops->MaxBurstLength) && ((cmd->write_data_done + payload_length) < cmd->data_length)) { pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL" " before end of DataOUT sequence, protocol" " error.\n", cmd->init_task_tag); return DATAOUT_CANNOT_RECOVER; } } else { if (next_burst_len < seq->xfer_len) { pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL" " before end of DataOUT sequence, protocol" " error.\n", cmd->init_task_tag); return DATAOUT_CANNOT_RECOVER; } } } else { if (conn->sess->sess_ops->DataSequenceInOrder) { if (next_burst_len == conn->sess->sess_ops->MaxBurstLength) { pr_err("Command ITT: 0x%08x reached" " MaxBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is" " not set, protocol error.", cmd->init_task_tag, conn->sess->sess_ops->MaxBurstLength); return DATAOUT_CANNOT_RECOVER; } if ((cmd->write_data_done + payload_length) == cmd->data_length) { pr_err("Command ITT: 0x%08x reached" " last DataOUT PDU in sequence but ISCSI_FLAG_" "CMD_FINAL is not set, protocol error.\n", cmd->init_task_tag); return DATAOUT_CANNOT_RECOVER; } } else { if (next_burst_len == seq->xfer_len) { pr_err("Command ITT: 0x%08x reached" " last DataOUT PDU in sequence but ISCSI_FLAG_" "CMD_FINAL is not set, protocol error.\n", cmd->init_task_tag); return DATAOUT_CANNOT_RECOVER; } } } out: return DATAOUT_NORMAL; }