18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/****************************************************************************** 38c2ecf20Sopenharmony_ci * This file contains error recovery level zero functions used by 48c2ecf20Sopenharmony_ci * the iSCSI Target driver. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * (c) Copyright 2007-2013 Datera, Inc. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci ******************************************************************************/ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <scsi/iscsi_proto.h> 158c2ecf20Sopenharmony_ci#include <target/target_core_base.h> 168c2ecf20Sopenharmony_ci#include <target/target_core_fabric.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <target/iscsi/iscsi_target_core.h> 198c2ecf20Sopenharmony_ci#include "iscsi_target_seq_pdu_list.h" 208c2ecf20Sopenharmony_ci#include "iscsi_target_erl0.h" 218c2ecf20Sopenharmony_ci#include "iscsi_target_erl1.h" 228c2ecf20Sopenharmony_ci#include "iscsi_target_erl2.h" 238c2ecf20Sopenharmony_ci#include "iscsi_target_util.h" 248c2ecf20Sopenharmony_ci#include "iscsi_target.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * Used to set values in struct iscsi_cmd that iscsit_dataout_check_sequence() 288c2ecf20Sopenharmony_ci * checks against to determine a PDU's Offset+Length is within the current 298c2ecf20Sopenharmony_ci * DataOUT Sequence. Used for DataSequenceInOrder=Yes only. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_civoid iscsit_set_dataout_sequence_values( 328c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 358c2ecf20Sopenharmony_ci /* 368c2ecf20Sopenharmony_ci * Still set seq_start_offset and seq_end_offset for Unsolicited 378c2ecf20Sopenharmony_ci * DataOUT, even if DataSequenceInOrder=No. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci if (cmd->unsolicited_data) { 408c2ecf20Sopenharmony_ci cmd->seq_start_offset = cmd->write_data_done; 418c2ecf20Sopenharmony_ci cmd->seq_end_offset = min(cmd->se_cmd.data_length, 428c2ecf20Sopenharmony_ci conn->sess->sess_ops->FirstBurstLength); 438c2ecf20Sopenharmony_ci return; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (!conn->sess->sess_ops->DataSequenceInOrder) 478c2ecf20Sopenharmony_ci return; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (!cmd->seq_start_offset && !cmd->seq_end_offset) { 508c2ecf20Sopenharmony_ci cmd->seq_start_offset = cmd->write_data_done; 518c2ecf20Sopenharmony_ci cmd->seq_end_offset = (cmd->se_cmd.data_length > 528c2ecf20Sopenharmony_ci conn->sess->sess_ops->MaxBurstLength) ? 538c2ecf20Sopenharmony_ci (cmd->write_data_done + 548c2ecf20Sopenharmony_ci conn->sess->sess_ops->MaxBurstLength) : cmd->se_cmd.data_length; 558c2ecf20Sopenharmony_ci } else { 568c2ecf20Sopenharmony_ci cmd->seq_start_offset = cmd->seq_end_offset; 578c2ecf20Sopenharmony_ci cmd->seq_end_offset = ((cmd->seq_end_offset + 588c2ecf20Sopenharmony_ci conn->sess->sess_ops->MaxBurstLength) >= 598c2ecf20Sopenharmony_ci cmd->se_cmd.data_length) ? cmd->se_cmd.data_length : 608c2ecf20Sopenharmony_ci (cmd->seq_end_offset + 618c2ecf20Sopenharmony_ci conn->sess->sess_ops->MaxBurstLength); 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int iscsit_dataout_within_command_recovery_check( 668c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 678c2ecf20Sopenharmony_ci unsigned char *buf) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 708c2ecf20Sopenharmony_ci struct iscsi_data *hdr = (struct iscsi_data *) buf; 718c2ecf20Sopenharmony_ci u32 payload_length = ntoh24(hdr->dlength); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* 748c2ecf20Sopenharmony_ci * We do the within-command recovery checks here as it is 758c2ecf20Sopenharmony_ci * the first function called in iscsi_check_pre_dataout(). 768c2ecf20Sopenharmony_ci * Basically, if we are in within-command recovery and 778c2ecf20Sopenharmony_ci * the PDU does not contain the offset the sequence needs, 788c2ecf20Sopenharmony_ci * dump the payload. 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * This only applies to DataPDUInOrder=Yes, for 818c2ecf20Sopenharmony_ci * DataPDUInOrder=No we only re-request the failed PDU 828c2ecf20Sopenharmony_ci * and check that all PDUs in a sequence are received 838c2ecf20Sopenharmony_ci * upon end of sequence. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) { 868c2ecf20Sopenharmony_ci if ((cmd->cmd_flags & ICF_WITHIN_COMMAND_RECOVERY) && 878c2ecf20Sopenharmony_ci cmd->write_data_done != be32_to_cpu(hdr->offset)) 888c2ecf20Sopenharmony_ci goto dump; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci cmd->cmd_flags &= ~ICF_WITHIN_COMMAND_RECOVERY; 918c2ecf20Sopenharmony_ci } else { 928c2ecf20Sopenharmony_ci struct iscsi_seq *seq; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci seq = iscsit_get_seq_holder(cmd, be32_to_cpu(hdr->offset), 958c2ecf20Sopenharmony_ci payload_length); 968c2ecf20Sopenharmony_ci if (!seq) 978c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 988c2ecf20Sopenharmony_ci /* 998c2ecf20Sopenharmony_ci * Set the struct iscsi_seq pointer to reuse later. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci cmd->seq_ptr = seq; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataPDUInOrder) { 1048c2ecf20Sopenharmony_ci if (seq->status == 1058c2ecf20Sopenharmony_ci DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY && 1068c2ecf20Sopenharmony_ci (seq->offset != be32_to_cpu(hdr->offset) || 1078c2ecf20Sopenharmony_ci seq->data_sn != be32_to_cpu(hdr->datasn))) 1088c2ecf20Sopenharmony_ci goto dump; 1098c2ecf20Sopenharmony_ci } else { 1108c2ecf20Sopenharmony_ci if (seq->status == 1118c2ecf20Sopenharmony_ci DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY && 1128c2ecf20Sopenharmony_ci seq->data_sn != be32_to_cpu(hdr->datasn)) 1138c2ecf20Sopenharmony_ci goto dump; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (seq->status == DATAOUT_SEQUENCE_COMPLETE) 1178c2ecf20Sopenharmony_ci goto dump; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (seq->status != DATAOUT_SEQUENCE_COMPLETE) 1208c2ecf20Sopenharmony_ci seq->status = 0; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return DATAOUT_NORMAL; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cidump: 1268c2ecf20Sopenharmony_ci pr_err("Dumping DataOUT PDU Offset: %u Length: %d DataSN:" 1278c2ecf20Sopenharmony_ci " 0x%08x\n", hdr->offset, payload_length, hdr->datasn); 1288c2ecf20Sopenharmony_ci return iscsit_dump_data_payload(conn, payload_length, 1); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int iscsit_dataout_check_unsolicited_sequence( 1328c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 1338c2ecf20Sopenharmony_ci unsigned char *buf) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci u32 first_burst_len; 1368c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 1378c2ecf20Sopenharmony_ci struct iscsi_data *hdr = (struct iscsi_data *) buf; 1388c2ecf20Sopenharmony_ci u32 payload_length = ntoh24(hdr->dlength); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if ((be32_to_cpu(hdr->offset) < cmd->seq_start_offset) || 1428c2ecf20Sopenharmony_ci ((be32_to_cpu(hdr->offset) + payload_length) > cmd->seq_end_offset)) { 1438c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x with Offset: %u," 1448c2ecf20Sopenharmony_ci " Length: %u outside of Unsolicited Sequence %u:%u while" 1458c2ecf20Sopenharmony_ci " DataSequenceInOrder=Yes.\n", cmd->init_task_tag, 1468c2ecf20Sopenharmony_ci be32_to_cpu(hdr->offset), payload_length, cmd->seq_start_offset, 1478c2ecf20Sopenharmony_ci cmd->seq_end_offset); 1488c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci first_burst_len = (cmd->first_burst_len + payload_length); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (first_burst_len > conn->sess->sess_ops->FirstBurstLength) { 1548c2ecf20Sopenharmony_ci pr_err("Total %u bytes exceeds FirstBurstLength: %u" 1558c2ecf20Sopenharmony_ci " for this Unsolicited DataOut Burst.\n", 1568c2ecf20Sopenharmony_ci first_burst_len, conn->sess->sess_ops->FirstBurstLength); 1578c2ecf20Sopenharmony_ci transport_send_check_condition_and_sense(&cmd->se_cmd, 1588c2ecf20Sopenharmony_ci TCM_INCORRECT_AMOUNT_OF_DATA, 0); 1598c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity 1648c2ecf20Sopenharmony_ci * checks for the current Unsolicited DataOUT Sequence. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci if (hdr->flags & ISCSI_FLAG_CMD_FINAL) { 1678c2ecf20Sopenharmony_ci /* 1688c2ecf20Sopenharmony_ci * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of 1698c2ecf20Sopenharmony_ci * sequence checks are handled in 1708c2ecf20Sopenharmony_ci * iscsit_dataout_datapduinorder_no_fbit(). 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci if (!conn->sess->sess_ops->DataPDUInOrder) 1738c2ecf20Sopenharmony_ci goto out; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if ((first_burst_len != cmd->se_cmd.data_length) && 1768c2ecf20Sopenharmony_ci (first_burst_len != conn->sess->sess_ops->FirstBurstLength)) { 1778c2ecf20Sopenharmony_ci pr_err("Unsolicited non-immediate data" 1788c2ecf20Sopenharmony_ci " received %u does not equal FirstBurstLength: %u, and" 1798c2ecf20Sopenharmony_ci " does not equal ExpXferLen %u.\n", first_burst_len, 1808c2ecf20Sopenharmony_ci conn->sess->sess_ops->FirstBurstLength, 1818c2ecf20Sopenharmony_ci cmd->se_cmd.data_length); 1828c2ecf20Sopenharmony_ci transport_send_check_condition_and_sense(&cmd->se_cmd, 1838c2ecf20Sopenharmony_ci TCM_INCORRECT_AMOUNT_OF_DATA, 0); 1848c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci } else { 1878c2ecf20Sopenharmony_ci if (first_burst_len == conn->sess->sess_ops->FirstBurstLength) { 1888c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x reached" 1898c2ecf20Sopenharmony_ci " FirstBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol" 1908c2ecf20Sopenharmony_ci " error.\n", cmd->init_task_tag, 1918c2ecf20Sopenharmony_ci conn->sess->sess_ops->FirstBurstLength); 1928c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci if (first_burst_len == cmd->se_cmd.data_length) { 1958c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x reached" 1968c2ecf20Sopenharmony_ci " ExpXferLen: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol" 1978c2ecf20Sopenharmony_ci " error.\n", cmd->init_task_tag, cmd->se_cmd.data_length); 1988c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ciout: 2038c2ecf20Sopenharmony_ci return DATAOUT_NORMAL; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int iscsit_dataout_check_sequence( 2078c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 2088c2ecf20Sopenharmony_ci unsigned char *buf) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci u32 next_burst_len; 2118c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 2128c2ecf20Sopenharmony_ci struct iscsi_seq *seq = NULL; 2138c2ecf20Sopenharmony_ci struct iscsi_data *hdr = (struct iscsi_data *) buf; 2148c2ecf20Sopenharmony_ci u32 payload_length = ntoh24(hdr->dlength); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * For DataSequenceInOrder=Yes: Check that the offset and offset+length 2188c2ecf20Sopenharmony_ci * is within range as defined by iscsi_set_dataout_sequence_values(). 2198c2ecf20Sopenharmony_ci * 2208c2ecf20Sopenharmony_ci * For DataSequenceInOrder=No: Check that an struct iscsi_seq exists for 2218c2ecf20Sopenharmony_ci * offset+length tuple. 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) { 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * Due to possibility of recovery DataOUT sent by the initiator 2268c2ecf20Sopenharmony_ci * fullfilling an Recovery R2T, it's best to just dump the 2278c2ecf20Sopenharmony_ci * payload here, instead of erroring out. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci if ((be32_to_cpu(hdr->offset) < cmd->seq_start_offset) || 2308c2ecf20Sopenharmony_ci ((be32_to_cpu(hdr->offset) + payload_length) > cmd->seq_end_offset)) { 2318c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x with Offset: %u," 2328c2ecf20Sopenharmony_ci " Length: %u outside of Sequence %u:%u while" 2338c2ecf20Sopenharmony_ci " DataSequenceInOrder=Yes.\n", cmd->init_task_tag, 2348c2ecf20Sopenharmony_ci be32_to_cpu(hdr->offset), payload_length, cmd->seq_start_offset, 2358c2ecf20Sopenharmony_ci cmd->seq_end_offset); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) 2388c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 2398c2ecf20Sopenharmony_ci return DATAOUT_WITHIN_COMMAND_RECOVERY; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci next_burst_len = (cmd->next_burst_len + payload_length); 2438c2ecf20Sopenharmony_ci } else { 2448c2ecf20Sopenharmony_ci seq = iscsit_get_seq_holder(cmd, be32_to_cpu(hdr->offset), 2458c2ecf20Sopenharmony_ci payload_length); 2468c2ecf20Sopenharmony_ci if (!seq) 2478c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 2488c2ecf20Sopenharmony_ci /* 2498c2ecf20Sopenharmony_ci * Set the struct iscsi_seq pointer to reuse later. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci cmd->seq_ptr = seq; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (seq->status == DATAOUT_SEQUENCE_COMPLETE) { 2548c2ecf20Sopenharmony_ci if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) 2558c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 2568c2ecf20Sopenharmony_ci return DATAOUT_WITHIN_COMMAND_RECOVERY; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci next_burst_len = (seq->next_burst_len + payload_length); 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (next_burst_len > conn->sess->sess_ops->MaxBurstLength) { 2638c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x, NextBurstLength: %u and" 2648c2ecf20Sopenharmony_ci " Length: %u exceeds MaxBurstLength: %u. protocol" 2658c2ecf20Sopenharmony_ci " error.\n", cmd->init_task_tag, 2668c2ecf20Sopenharmony_ci (next_burst_len - payload_length), 2678c2ecf20Sopenharmony_ci payload_length, conn->sess->sess_ops->MaxBurstLength); 2688c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* 2728c2ecf20Sopenharmony_ci * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity 2738c2ecf20Sopenharmony_ci * checks for the current DataOUT Sequence. 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_ci if (hdr->flags & ISCSI_FLAG_CMD_FINAL) { 2768c2ecf20Sopenharmony_ci /* 2778c2ecf20Sopenharmony_ci * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of 2788c2ecf20Sopenharmony_ci * sequence checks are handled in 2798c2ecf20Sopenharmony_ci * iscsit_dataout_datapduinorder_no_fbit(). 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci if (!conn->sess->sess_ops->DataPDUInOrder) 2828c2ecf20Sopenharmony_ci goto out; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) { 2858c2ecf20Sopenharmony_ci if ((next_burst_len < 2868c2ecf20Sopenharmony_ci conn->sess->sess_ops->MaxBurstLength) && 2878c2ecf20Sopenharmony_ci ((cmd->write_data_done + payload_length) < 2888c2ecf20Sopenharmony_ci cmd->se_cmd.data_length)) { 2898c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL" 2908c2ecf20Sopenharmony_ci " before end of DataOUT sequence, protocol" 2918c2ecf20Sopenharmony_ci " error.\n", cmd->init_task_tag); 2928c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci } else { 2958c2ecf20Sopenharmony_ci if (next_burst_len < seq->xfer_len) { 2968c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL" 2978c2ecf20Sopenharmony_ci " before end of DataOUT sequence, protocol" 2988c2ecf20Sopenharmony_ci " error.\n", cmd->init_task_tag); 2998c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci } else { 3038c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) { 3048c2ecf20Sopenharmony_ci if (next_burst_len == 3058c2ecf20Sopenharmony_ci conn->sess->sess_ops->MaxBurstLength) { 3068c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x reached" 3078c2ecf20Sopenharmony_ci " MaxBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is" 3088c2ecf20Sopenharmony_ci " not set, protocol error.", cmd->init_task_tag, 3098c2ecf20Sopenharmony_ci conn->sess->sess_ops->MaxBurstLength); 3108c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci if ((cmd->write_data_done + payload_length) == 3138c2ecf20Sopenharmony_ci cmd->se_cmd.data_length) { 3148c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x reached" 3158c2ecf20Sopenharmony_ci " last DataOUT PDU in sequence but ISCSI_FLAG_" 3168c2ecf20Sopenharmony_ci "CMD_FINAL is not set, protocol error.\n", 3178c2ecf20Sopenharmony_ci cmd->init_task_tag); 3188c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci } else { 3218c2ecf20Sopenharmony_ci if (next_burst_len == seq->xfer_len) { 3228c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x reached" 3238c2ecf20Sopenharmony_ci " last DataOUT PDU in sequence but ISCSI_FLAG_" 3248c2ecf20Sopenharmony_ci "CMD_FINAL is not set, protocol error.\n", 3258c2ecf20Sopenharmony_ci cmd->init_task_tag); 3268c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ciout: 3328c2ecf20Sopenharmony_ci return DATAOUT_NORMAL; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int iscsit_dataout_check_datasn( 3368c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 3378c2ecf20Sopenharmony_ci unsigned char *buf) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci u32 data_sn = 0; 3408c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 3418c2ecf20Sopenharmony_ci struct iscsi_data *hdr = (struct iscsi_data *) buf; 3428c2ecf20Sopenharmony_ci u32 payload_length = ntoh24(hdr->dlength); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* 3458c2ecf20Sopenharmony_ci * Considering the target has no method of re-requesting DataOUT 3468c2ecf20Sopenharmony_ci * by DataSN, if we receieve a greater DataSN than expected we 3478c2ecf20Sopenharmony_ci * assume the functions for DataPDUInOrder=[Yes,No] below will 3488c2ecf20Sopenharmony_ci * handle it. 3498c2ecf20Sopenharmony_ci * 3508c2ecf20Sopenharmony_ci * If the DataSN is less than expected, dump the payload. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) 3538c2ecf20Sopenharmony_ci data_sn = cmd->data_sn; 3548c2ecf20Sopenharmony_ci else { 3558c2ecf20Sopenharmony_ci struct iscsi_seq *seq = cmd->seq_ptr; 3568c2ecf20Sopenharmony_ci data_sn = seq->data_sn; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (be32_to_cpu(hdr->datasn) > data_sn) { 3608c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x" 3618c2ecf20Sopenharmony_ci " higher than expected 0x%08x.\n", cmd->init_task_tag, 3628c2ecf20Sopenharmony_ci be32_to_cpu(hdr->datasn), data_sn); 3638c2ecf20Sopenharmony_ci goto recover; 3648c2ecf20Sopenharmony_ci } else if (be32_to_cpu(hdr->datasn) < data_sn) { 3658c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x" 3668c2ecf20Sopenharmony_ci " lower than expected 0x%08x, discarding payload.\n", 3678c2ecf20Sopenharmony_ci cmd->init_task_tag, be32_to_cpu(hdr->datasn), data_sn); 3688c2ecf20Sopenharmony_ci goto dump; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return DATAOUT_NORMAL; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cirecover: 3748c2ecf20Sopenharmony_ci if (!conn->sess->sess_ops->ErrorRecoveryLevel) { 3758c2ecf20Sopenharmony_ci pr_err("Unable to perform within-command recovery" 3768c2ecf20Sopenharmony_ci " while ERL=0.\n"); 3778c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_cidump: 3808c2ecf20Sopenharmony_ci if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) 3818c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return DATAOUT_WITHIN_COMMAND_RECOVERY; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int iscsit_dataout_pre_datapduinorder_yes( 3878c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 3888c2ecf20Sopenharmony_ci unsigned char *buf) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci int dump = 0, recovery = 0; 3918c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 3928c2ecf20Sopenharmony_ci struct iscsi_data *hdr = (struct iscsi_data *) buf; 3938c2ecf20Sopenharmony_ci u32 payload_length = ntoh24(hdr->dlength); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* 3968c2ecf20Sopenharmony_ci * For DataSequenceInOrder=Yes: If the offset is greater than the global 3978c2ecf20Sopenharmony_ci * DataPDUInOrder=Yes offset counter in struct iscsi_cmd a protcol error has 3988c2ecf20Sopenharmony_ci * occurred and fail the connection. 3998c2ecf20Sopenharmony_ci * 4008c2ecf20Sopenharmony_ci * For DataSequenceInOrder=No: If the offset is greater than the per 4018c2ecf20Sopenharmony_ci * sequence DataPDUInOrder=Yes offset counter in struct iscsi_seq a protocol 4028c2ecf20Sopenharmony_ci * error has occurred and fail the connection. 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) { 4058c2ecf20Sopenharmony_ci if (be32_to_cpu(hdr->offset) != cmd->write_data_done) { 4068c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x, received offset" 4078c2ecf20Sopenharmony_ci " %u different than expected %u.\n", cmd->init_task_tag, 4088c2ecf20Sopenharmony_ci be32_to_cpu(hdr->offset), cmd->write_data_done); 4098c2ecf20Sopenharmony_ci recovery = 1; 4108c2ecf20Sopenharmony_ci goto recover; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci } else { 4138c2ecf20Sopenharmony_ci struct iscsi_seq *seq = cmd->seq_ptr; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (be32_to_cpu(hdr->offset) > seq->offset) { 4168c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x, received offset" 4178c2ecf20Sopenharmony_ci " %u greater than expected %u.\n", cmd->init_task_tag, 4188c2ecf20Sopenharmony_ci be32_to_cpu(hdr->offset), seq->offset); 4198c2ecf20Sopenharmony_ci recovery = 1; 4208c2ecf20Sopenharmony_ci goto recover; 4218c2ecf20Sopenharmony_ci } else if (be32_to_cpu(hdr->offset) < seq->offset) { 4228c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x, received offset" 4238c2ecf20Sopenharmony_ci " %u less than expected %u, discarding payload.\n", 4248c2ecf20Sopenharmony_ci cmd->init_task_tag, be32_to_cpu(hdr->offset), 4258c2ecf20Sopenharmony_ci seq->offset); 4268c2ecf20Sopenharmony_ci dump = 1; 4278c2ecf20Sopenharmony_ci goto dump; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return DATAOUT_NORMAL; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cirecover: 4348c2ecf20Sopenharmony_ci if (!conn->sess->sess_ops->ErrorRecoveryLevel) { 4358c2ecf20Sopenharmony_ci pr_err("Unable to perform within-command recovery" 4368c2ecf20Sopenharmony_ci " while ERL=0.\n"); 4378c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_cidump: 4408c2ecf20Sopenharmony_ci if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) 4418c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return (recovery) ? iscsit_recover_dataout_sequence(cmd, 4448c2ecf20Sopenharmony_ci be32_to_cpu(hdr->offset), payload_length) : 4458c2ecf20Sopenharmony_ci (dump) ? DATAOUT_WITHIN_COMMAND_RECOVERY : DATAOUT_NORMAL; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int iscsit_dataout_pre_datapduinorder_no( 4498c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 4508c2ecf20Sopenharmony_ci unsigned char *buf) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct iscsi_pdu *pdu; 4538c2ecf20Sopenharmony_ci struct iscsi_data *hdr = (struct iscsi_data *) buf; 4548c2ecf20Sopenharmony_ci u32 payload_length = ntoh24(hdr->dlength); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci pdu = iscsit_get_pdu_holder(cmd, be32_to_cpu(hdr->offset), 4578c2ecf20Sopenharmony_ci payload_length); 4588c2ecf20Sopenharmony_ci if (!pdu) 4598c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci cmd->pdu_ptr = pdu; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci switch (pdu->status) { 4648c2ecf20Sopenharmony_ci case ISCSI_PDU_NOT_RECEIVED: 4658c2ecf20Sopenharmony_ci case ISCSI_PDU_CRC_FAILED: 4668c2ecf20Sopenharmony_ci case ISCSI_PDU_TIMED_OUT: 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci case ISCSI_PDU_RECEIVED_OK: 4698c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x received already gotten" 4708c2ecf20Sopenharmony_ci " Offset: %u, Length: %u\n", cmd->init_task_tag, 4718c2ecf20Sopenharmony_ci be32_to_cpu(hdr->offset), payload_length); 4728c2ecf20Sopenharmony_ci return iscsit_dump_data_payload(cmd->conn, payload_length, 1); 4738c2ecf20Sopenharmony_ci default: 4748c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return DATAOUT_NORMAL; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int iscsit_dataout_update_r2t(struct iscsi_cmd *cmd, u32 offset, u32 length) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct iscsi_r2t *r2t; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (cmd->unsolicited_data) 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci r2t = iscsit_get_r2t_for_eos(cmd, offset, length); 4888c2ecf20Sopenharmony_ci if (!r2t) 4898c2ecf20Sopenharmony_ci return -1; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci spin_lock_bh(&cmd->r2t_lock); 4928c2ecf20Sopenharmony_ci r2t->seq_complete = 1; 4938c2ecf20Sopenharmony_ci cmd->outstanding_r2ts--; 4948c2ecf20Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci return 0; 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic int iscsit_dataout_update_datapduinorder_no( 5008c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 5018c2ecf20Sopenharmony_ci u32 data_sn, 5028c2ecf20Sopenharmony_ci int f_bit) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci int ret = 0; 5058c2ecf20Sopenharmony_ci struct iscsi_pdu *pdu = cmd->pdu_ptr; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci pdu->data_sn = data_sn; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci switch (pdu->status) { 5108c2ecf20Sopenharmony_ci case ISCSI_PDU_NOT_RECEIVED: 5118c2ecf20Sopenharmony_ci pdu->status = ISCSI_PDU_RECEIVED_OK; 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci case ISCSI_PDU_CRC_FAILED: 5148c2ecf20Sopenharmony_ci pdu->status = ISCSI_PDU_RECEIVED_OK; 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci case ISCSI_PDU_TIMED_OUT: 5178c2ecf20Sopenharmony_ci pdu->status = ISCSI_PDU_RECEIVED_OK; 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci default: 5208c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (f_bit) { 5248c2ecf20Sopenharmony_ci ret = iscsit_dataout_datapduinorder_no_fbit(cmd, pdu); 5258c2ecf20Sopenharmony_ci if (ret == DATAOUT_CANNOT_RECOVER) 5268c2ecf20Sopenharmony_ci return ret; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return DATAOUT_NORMAL; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic int iscsit_dataout_post_crc_passed( 5338c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 5348c2ecf20Sopenharmony_ci unsigned char *buf) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci int ret, send_r2t = 0; 5378c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 5388c2ecf20Sopenharmony_ci struct iscsi_seq *seq = NULL; 5398c2ecf20Sopenharmony_ci struct iscsi_data *hdr = (struct iscsi_data *) buf; 5408c2ecf20Sopenharmony_ci u32 payload_length = ntoh24(hdr->dlength); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (cmd->unsolicited_data) { 5438c2ecf20Sopenharmony_ci if ((cmd->first_burst_len + payload_length) == 5448c2ecf20Sopenharmony_ci conn->sess->sess_ops->FirstBurstLength) { 5458c2ecf20Sopenharmony_ci if (iscsit_dataout_update_r2t(cmd, be32_to_cpu(hdr->offset), 5468c2ecf20Sopenharmony_ci payload_length) < 0) 5478c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 5488c2ecf20Sopenharmony_ci send_r2t = 1; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (!conn->sess->sess_ops->DataPDUInOrder) { 5528c2ecf20Sopenharmony_ci ret = iscsit_dataout_update_datapduinorder_no(cmd, 5538c2ecf20Sopenharmony_ci be32_to_cpu(hdr->datasn), 5548c2ecf20Sopenharmony_ci (hdr->flags & ISCSI_FLAG_CMD_FINAL)); 5558c2ecf20Sopenharmony_ci if (ret == DATAOUT_CANNOT_RECOVER) 5568c2ecf20Sopenharmony_ci return ret; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci cmd->first_burst_len += payload_length; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) 5628c2ecf20Sopenharmony_ci cmd->data_sn++; 5638c2ecf20Sopenharmony_ci else { 5648c2ecf20Sopenharmony_ci seq = cmd->seq_ptr; 5658c2ecf20Sopenharmony_ci seq->data_sn++; 5668c2ecf20Sopenharmony_ci seq->offset += payload_length; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (send_r2t) { 5708c2ecf20Sopenharmony_ci if (seq) 5718c2ecf20Sopenharmony_ci seq->status = DATAOUT_SEQUENCE_COMPLETE; 5728c2ecf20Sopenharmony_ci cmd->first_burst_len = 0; 5738c2ecf20Sopenharmony_ci cmd->unsolicited_data = 0; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci } else { 5768c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) { 5778c2ecf20Sopenharmony_ci if ((cmd->next_burst_len + payload_length) == 5788c2ecf20Sopenharmony_ci conn->sess->sess_ops->MaxBurstLength) { 5798c2ecf20Sopenharmony_ci if (iscsit_dataout_update_r2t(cmd, 5808c2ecf20Sopenharmony_ci be32_to_cpu(hdr->offset), 5818c2ecf20Sopenharmony_ci payload_length) < 0) 5828c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 5838c2ecf20Sopenharmony_ci send_r2t = 1; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (!conn->sess->sess_ops->DataPDUInOrder) { 5878c2ecf20Sopenharmony_ci ret = iscsit_dataout_update_datapduinorder_no( 5888c2ecf20Sopenharmony_ci cmd, be32_to_cpu(hdr->datasn), 5898c2ecf20Sopenharmony_ci (hdr->flags & ISCSI_FLAG_CMD_FINAL)); 5908c2ecf20Sopenharmony_ci if (ret == DATAOUT_CANNOT_RECOVER) 5918c2ecf20Sopenharmony_ci return ret; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci cmd->next_burst_len += payload_length; 5958c2ecf20Sopenharmony_ci cmd->data_sn++; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (send_r2t) 5988c2ecf20Sopenharmony_ci cmd->next_burst_len = 0; 5998c2ecf20Sopenharmony_ci } else { 6008c2ecf20Sopenharmony_ci seq = cmd->seq_ptr; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if ((seq->next_burst_len + payload_length) == 6038c2ecf20Sopenharmony_ci seq->xfer_len) { 6048c2ecf20Sopenharmony_ci if (iscsit_dataout_update_r2t(cmd, 6058c2ecf20Sopenharmony_ci be32_to_cpu(hdr->offset), 6068c2ecf20Sopenharmony_ci payload_length) < 0) 6078c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 6088c2ecf20Sopenharmony_ci send_r2t = 1; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (!conn->sess->sess_ops->DataPDUInOrder) { 6128c2ecf20Sopenharmony_ci ret = iscsit_dataout_update_datapduinorder_no( 6138c2ecf20Sopenharmony_ci cmd, be32_to_cpu(hdr->datasn), 6148c2ecf20Sopenharmony_ci (hdr->flags & ISCSI_FLAG_CMD_FINAL)); 6158c2ecf20Sopenharmony_ci if (ret == DATAOUT_CANNOT_RECOVER) 6168c2ecf20Sopenharmony_ci return ret; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci seq->data_sn++; 6208c2ecf20Sopenharmony_ci seq->offset += payload_length; 6218c2ecf20Sopenharmony_ci seq->next_burst_len += payload_length; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (send_r2t) { 6248c2ecf20Sopenharmony_ci seq->next_burst_len = 0; 6258c2ecf20Sopenharmony_ci seq->status = DATAOUT_SEQUENCE_COMPLETE; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (send_r2t && conn->sess->sess_ops->DataSequenceInOrder) 6318c2ecf20Sopenharmony_ci cmd->data_sn = 0; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci cmd->write_data_done += payload_length; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (cmd->write_data_done == cmd->se_cmd.data_length) 6368c2ecf20Sopenharmony_ci return DATAOUT_SEND_TO_TRANSPORT; 6378c2ecf20Sopenharmony_ci else if (send_r2t) 6388c2ecf20Sopenharmony_ci return DATAOUT_SEND_R2T; 6398c2ecf20Sopenharmony_ci else 6408c2ecf20Sopenharmony_ci return DATAOUT_NORMAL; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic int iscsit_dataout_post_crc_failed( 6448c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 6458c2ecf20Sopenharmony_ci unsigned char *buf) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 6488c2ecf20Sopenharmony_ci struct iscsi_pdu *pdu; 6498c2ecf20Sopenharmony_ci struct iscsi_data *hdr = (struct iscsi_data *) buf; 6508c2ecf20Sopenharmony_ci u32 payload_length = ntoh24(hdr->dlength); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataPDUInOrder) 6538c2ecf20Sopenharmony_ci goto recover; 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * The rest of this function is only called when DataPDUInOrder=No. 6568c2ecf20Sopenharmony_ci */ 6578c2ecf20Sopenharmony_ci pdu = cmd->pdu_ptr; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci switch (pdu->status) { 6608c2ecf20Sopenharmony_ci case ISCSI_PDU_NOT_RECEIVED: 6618c2ecf20Sopenharmony_ci pdu->status = ISCSI_PDU_CRC_FAILED; 6628c2ecf20Sopenharmony_ci break; 6638c2ecf20Sopenharmony_ci case ISCSI_PDU_CRC_FAILED: 6648c2ecf20Sopenharmony_ci break; 6658c2ecf20Sopenharmony_ci case ISCSI_PDU_TIMED_OUT: 6668c2ecf20Sopenharmony_ci pdu->status = ISCSI_PDU_CRC_FAILED; 6678c2ecf20Sopenharmony_ci break; 6688c2ecf20Sopenharmony_ci default: 6698c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cirecover: 6738c2ecf20Sopenharmony_ci return iscsit_recover_dataout_sequence(cmd, be32_to_cpu(hdr->offset), 6748c2ecf20Sopenharmony_ci payload_length); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci/* 6788c2ecf20Sopenharmony_ci * Called from iscsit_handle_data_out() before DataOUT Payload is received 6798c2ecf20Sopenharmony_ci * and CRC computed. 6808c2ecf20Sopenharmony_ci */ 6818c2ecf20Sopenharmony_ciint iscsit_check_pre_dataout( 6828c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 6838c2ecf20Sopenharmony_ci unsigned char *buf) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci int ret; 6868c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci ret = iscsit_dataout_within_command_recovery_check(cmd, buf); 6898c2ecf20Sopenharmony_ci if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) || 6908c2ecf20Sopenharmony_ci (ret == DATAOUT_CANNOT_RECOVER)) 6918c2ecf20Sopenharmony_ci return ret; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci ret = iscsit_dataout_check_datasn(cmd, buf); 6948c2ecf20Sopenharmony_ci if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) || 6958c2ecf20Sopenharmony_ci (ret == DATAOUT_CANNOT_RECOVER)) 6968c2ecf20Sopenharmony_ci return ret; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (cmd->unsolicited_data) { 6998c2ecf20Sopenharmony_ci ret = iscsit_dataout_check_unsolicited_sequence(cmd, buf); 7008c2ecf20Sopenharmony_ci if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) || 7018c2ecf20Sopenharmony_ci (ret == DATAOUT_CANNOT_RECOVER)) 7028c2ecf20Sopenharmony_ci return ret; 7038c2ecf20Sopenharmony_ci } else { 7048c2ecf20Sopenharmony_ci ret = iscsit_dataout_check_sequence(cmd, buf); 7058c2ecf20Sopenharmony_ci if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) || 7068c2ecf20Sopenharmony_ci (ret == DATAOUT_CANNOT_RECOVER)) 7078c2ecf20Sopenharmony_ci return ret; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return (conn->sess->sess_ops->DataPDUInOrder) ? 7118c2ecf20Sopenharmony_ci iscsit_dataout_pre_datapduinorder_yes(cmd, buf) : 7128c2ecf20Sopenharmony_ci iscsit_dataout_pre_datapduinorder_no(cmd, buf); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/* 7168c2ecf20Sopenharmony_ci * Called from iscsit_handle_data_out() after DataOUT Payload is received 7178c2ecf20Sopenharmony_ci * and CRC computed. 7188c2ecf20Sopenharmony_ci */ 7198c2ecf20Sopenharmony_ciint iscsit_check_post_dataout( 7208c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 7218c2ecf20Sopenharmony_ci unsigned char *buf, 7228c2ecf20Sopenharmony_ci u8 data_crc_failed) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci cmd->dataout_timeout_retries = 0; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (!data_crc_failed) 7298c2ecf20Sopenharmony_ci return iscsit_dataout_post_crc_passed(cmd, buf); 7308c2ecf20Sopenharmony_ci else { 7318c2ecf20Sopenharmony_ci if (!conn->sess->sess_ops->ErrorRecoveryLevel) { 7328c2ecf20Sopenharmony_ci pr_err("Unable to recover from DataOUT CRC" 7338c2ecf20Sopenharmony_ci " failure while ERL=0, closing session.\n"); 7348c2ecf20Sopenharmony_ci iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR, 7358c2ecf20Sopenharmony_ci buf); 7368c2ecf20Sopenharmony_ci return DATAOUT_CANNOT_RECOVER; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR, buf); 7408c2ecf20Sopenharmony_ci return iscsit_dataout_post_crc_failed(cmd, buf); 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_civoid iscsit_handle_time2retain_timeout(struct timer_list *t) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci struct iscsi_session *sess = from_timer(sess, t, time2retain_timer); 7478c2ecf20Sopenharmony_ci struct iscsi_portal_group *tpg = sess->tpg; 7488c2ecf20Sopenharmony_ci struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci spin_lock_bh(&se_tpg->session_lock); 7518c2ecf20Sopenharmony_ci if (sess->time2retain_timer_flags & ISCSI_TF_STOP) { 7528c2ecf20Sopenharmony_ci spin_unlock_bh(&se_tpg->session_lock); 7538c2ecf20Sopenharmony_ci return; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci if (atomic_read(&sess->session_reinstatement)) { 7568c2ecf20Sopenharmony_ci pr_err("Exiting Time2Retain handler because" 7578c2ecf20Sopenharmony_ci " session_reinstatement=1\n"); 7588c2ecf20Sopenharmony_ci spin_unlock_bh(&se_tpg->session_lock); 7598c2ecf20Sopenharmony_ci return; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci sess->time2retain_timer_flags |= ISCSI_TF_EXPIRED; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci pr_err("Time2Retain timer expired for SID: %u, cleaning up" 7648c2ecf20Sopenharmony_ci " iSCSI session.\n", sess->sid); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci iscsit_fill_cxn_timeout_err_stats(sess); 7678c2ecf20Sopenharmony_ci spin_unlock_bh(&se_tpg->session_lock); 7688c2ecf20Sopenharmony_ci iscsit_close_session(sess); 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_civoid iscsit_start_time2retain_handler(struct iscsi_session *sess) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci int tpg_active; 7748c2ecf20Sopenharmony_ci /* 7758c2ecf20Sopenharmony_ci * Only start Time2Retain timer when the associated TPG is still in 7768c2ecf20Sopenharmony_ci * an ACTIVE (eg: not disabled or shutdown) state. 7778c2ecf20Sopenharmony_ci */ 7788c2ecf20Sopenharmony_ci spin_lock(&sess->tpg->tpg_state_lock); 7798c2ecf20Sopenharmony_ci tpg_active = (sess->tpg->tpg_state == TPG_STATE_ACTIVE); 7808c2ecf20Sopenharmony_ci spin_unlock(&sess->tpg->tpg_state_lock); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (!tpg_active) 7838c2ecf20Sopenharmony_ci return; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (sess->time2retain_timer_flags & ISCSI_TF_RUNNING) 7868c2ecf20Sopenharmony_ci return; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci pr_debug("Starting Time2Retain timer for %u seconds on" 7898c2ecf20Sopenharmony_ci " SID: %u\n", sess->sess_ops->DefaultTime2Retain, sess->sid); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci sess->time2retain_timer_flags &= ~ISCSI_TF_STOP; 7928c2ecf20Sopenharmony_ci sess->time2retain_timer_flags |= ISCSI_TF_RUNNING; 7938c2ecf20Sopenharmony_ci mod_timer(&sess->time2retain_timer, 7948c2ecf20Sopenharmony_ci jiffies + sess->sess_ops->DefaultTime2Retain * HZ); 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ciint iscsit_stop_time2retain_timer(struct iscsi_session *sess) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci struct iscsi_portal_group *tpg = sess->tpg; 8008c2ecf20Sopenharmony_ci struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci lockdep_assert_held(&se_tpg->session_lock); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED) 8058c2ecf20Sopenharmony_ci return -1; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (!(sess->time2retain_timer_flags & ISCSI_TF_RUNNING)) 8088c2ecf20Sopenharmony_ci return 0; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci sess->time2retain_timer_flags |= ISCSI_TF_STOP; 8118c2ecf20Sopenharmony_ci spin_unlock(&se_tpg->session_lock); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci del_timer_sync(&sess->time2retain_timer); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci spin_lock(&se_tpg->session_lock); 8168c2ecf20Sopenharmony_ci sess->time2retain_timer_flags &= ~ISCSI_TF_RUNNING; 8178c2ecf20Sopenharmony_ci pr_debug("Stopped Time2Retain Timer for SID: %u\n", 8188c2ecf20Sopenharmony_ci sess->sid); 8198c2ecf20Sopenharmony_ci return 0; 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_civoid iscsit_connection_reinstatement_rcfr(struct iscsi_conn *conn) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci spin_lock_bh(&conn->state_lock); 8258c2ecf20Sopenharmony_ci if (atomic_read(&conn->connection_exit)) { 8268c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->state_lock); 8278c2ecf20Sopenharmony_ci goto sleep; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (atomic_read(&conn->transport_failed)) { 8318c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->state_lock); 8328c2ecf20Sopenharmony_ci goto sleep; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->state_lock); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (conn->tx_thread && conn->tx_thread_active) 8378c2ecf20Sopenharmony_ci send_sig(SIGINT, conn->tx_thread, 1); 8388c2ecf20Sopenharmony_ci if (conn->rx_thread && conn->rx_thread_active) 8398c2ecf20Sopenharmony_ci send_sig(SIGINT, conn->rx_thread, 1); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cisleep: 8428c2ecf20Sopenharmony_ci wait_for_completion(&conn->conn_wait_rcfr_comp); 8438c2ecf20Sopenharmony_ci complete(&conn->conn_post_wait_comp); 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_civoid iscsit_cause_connection_reinstatement(struct iscsi_conn *conn, int sleep) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci spin_lock_bh(&conn->state_lock); 8498c2ecf20Sopenharmony_ci if (atomic_read(&conn->connection_exit)) { 8508c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->state_lock); 8518c2ecf20Sopenharmony_ci return; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (atomic_read(&conn->transport_failed)) { 8558c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->state_lock); 8568c2ecf20Sopenharmony_ci return; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (atomic_read(&conn->connection_reinstatement)) { 8608c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->state_lock); 8618c2ecf20Sopenharmony_ci return; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (conn->tx_thread && conn->tx_thread_active) 8658c2ecf20Sopenharmony_ci send_sig(SIGINT, conn->tx_thread, 1); 8668c2ecf20Sopenharmony_ci if (conn->rx_thread && conn->rx_thread_active) 8678c2ecf20Sopenharmony_ci send_sig(SIGINT, conn->rx_thread, 1); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci atomic_set(&conn->connection_reinstatement, 1); 8708c2ecf20Sopenharmony_ci if (!sleep) { 8718c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->state_lock); 8728c2ecf20Sopenharmony_ci return; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci atomic_set(&conn->sleep_on_conn_wait_comp, 1); 8768c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->state_lock); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci wait_for_completion(&conn->conn_wait_comp); 8798c2ecf20Sopenharmony_ci complete(&conn->conn_post_wait_comp); 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_cause_connection_reinstatement); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_civoid iscsit_fall_back_to_erl0(struct iscsi_session *sess) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci pr_debug("Falling back to ErrorRecoveryLevel=0 for SID:" 8868c2ecf20Sopenharmony_ci " %u\n", sess->sid); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci atomic_set(&sess->session_fall_back_to_erl0, 1); 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic void iscsit_handle_connection_cleanup(struct iscsi_conn *conn) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci struct iscsi_session *sess = conn->sess; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if ((sess->sess_ops->ErrorRecoveryLevel == 2) && 8968c2ecf20Sopenharmony_ci !atomic_read(&sess->session_reinstatement) && 8978c2ecf20Sopenharmony_ci !atomic_read(&sess->session_fall_back_to_erl0)) 8988c2ecf20Sopenharmony_ci iscsit_connection_recovery_transport_reset(conn); 8998c2ecf20Sopenharmony_ci else { 9008c2ecf20Sopenharmony_ci pr_debug("Performing cleanup for failed iSCSI" 9018c2ecf20Sopenharmony_ci " Connection ID: %hu from %s\n", conn->cid, 9028c2ecf20Sopenharmony_ci sess->sess_ops->InitiatorName); 9038c2ecf20Sopenharmony_ci iscsit_close_connection(conn); 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_civoid iscsit_take_action_for_connection_exit(struct iscsi_conn *conn, bool *conn_freed) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci *conn_freed = false; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci spin_lock_bh(&conn->state_lock); 9128c2ecf20Sopenharmony_ci if (atomic_read(&conn->connection_exit)) { 9138c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->state_lock); 9148c2ecf20Sopenharmony_ci return; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci atomic_set(&conn->connection_exit, 1); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) { 9198c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->state_lock); 9208c2ecf20Sopenharmony_ci iscsit_close_connection(conn); 9218c2ecf20Sopenharmony_ci *conn_freed = true; 9228c2ecf20Sopenharmony_ci return; 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci if (conn->conn_state == TARG_CONN_STATE_CLEANUP_WAIT) { 9268c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->state_lock); 9278c2ecf20Sopenharmony_ci return; 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n"); 9318c2ecf20Sopenharmony_ci conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT; 9328c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->state_lock); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci iscsit_handle_connection_cleanup(conn); 9358c2ecf20Sopenharmony_ci *conn_freed = true; 9368c2ecf20Sopenharmony_ci} 937