| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /******************************************************************************* |
|---|
| 2 | 3 | * This file contains error recovery level one used by the iSCSI Target driver. |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> |
|---|
| 7 | 8 | * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 9 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 10 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 11 | | - * (at your option) any later version. |
|---|
| 12 | | - * |
|---|
| 13 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 14 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 15 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 16 | | - * GNU General Public License for more details. |
|---|
| 17 | 9 | ******************************************************************************/ |
|---|
| 18 | 10 | |
|---|
| 19 | 11 | #include <linux/list.h> |
|---|
| .. | .. |
|---|
| 48 | 40 | u32 buf_len, |
|---|
| 49 | 41 | int dump_padding_digest) |
|---|
| 50 | 42 | { |
|---|
| 51 | | - char *buf, pad_bytes[4]; |
|---|
| 43 | + char *buf; |
|---|
| 52 | 44 | int ret = DATAOUT_WITHIN_COMMAND_RECOVERY, rx_got; |
|---|
| 53 | | - u32 length, padding, offset = 0, size; |
|---|
| 45 | + u32 length, offset = 0, size; |
|---|
| 54 | 46 | struct kvec iov; |
|---|
| 55 | 47 | |
|---|
| 56 | 48 | if (conn->sess->sess_ops->RDMAExtensions) |
|---|
| 57 | 49 | return 0; |
|---|
| 50 | + |
|---|
| 51 | + if (dump_padding_digest) { |
|---|
| 52 | + buf_len = ALIGN(buf_len, 4); |
|---|
| 53 | + if (conn->conn_ops->DataDigest) |
|---|
| 54 | + buf_len += ISCSI_CRC_LEN; |
|---|
| 55 | + } |
|---|
| 58 | 56 | |
|---|
| 59 | 57 | length = min(buf_len, OFFLOAD_BUF_SIZE); |
|---|
| 60 | 58 | |
|---|
| .. | .. |
|---|
| 75 | 73 | rx_got = rx_data(conn, &iov, 1, size); |
|---|
| 76 | 74 | if (rx_got != size) { |
|---|
| 77 | 75 | ret = DATAOUT_CANNOT_RECOVER; |
|---|
| 78 | | - goto out; |
|---|
| 76 | + break; |
|---|
| 79 | 77 | } |
|---|
| 80 | 78 | |
|---|
| 81 | 79 | offset += size; |
|---|
| 82 | 80 | } |
|---|
| 83 | 81 | |
|---|
| 84 | | - if (!dump_padding_digest) |
|---|
| 85 | | - goto out; |
|---|
| 86 | | - |
|---|
| 87 | | - padding = ((-buf_len) & 3); |
|---|
| 88 | | - if (padding != 0) { |
|---|
| 89 | | - iov.iov_len = padding; |
|---|
| 90 | | - iov.iov_base = pad_bytes; |
|---|
| 91 | | - |
|---|
| 92 | | - rx_got = rx_data(conn, &iov, 1, padding); |
|---|
| 93 | | - if (rx_got != padding) { |
|---|
| 94 | | - ret = DATAOUT_CANNOT_RECOVER; |
|---|
| 95 | | - goto out; |
|---|
| 96 | | - } |
|---|
| 97 | | - } |
|---|
| 98 | | - |
|---|
| 99 | | - if (conn->conn_ops->DataDigest) { |
|---|
| 100 | | - u32 data_crc; |
|---|
| 101 | | - |
|---|
| 102 | | - iov.iov_len = ISCSI_CRC_LEN; |
|---|
| 103 | | - iov.iov_base = &data_crc; |
|---|
| 104 | | - |
|---|
| 105 | | - rx_got = rx_data(conn, &iov, 1, ISCSI_CRC_LEN); |
|---|
| 106 | | - if (rx_got != ISCSI_CRC_LEN) { |
|---|
| 107 | | - ret = DATAOUT_CANNOT_RECOVER; |
|---|
| 108 | | - goto out; |
|---|
| 109 | | - } |
|---|
| 110 | | - } |
|---|
| 111 | | - |
|---|
| 112 | | -out: |
|---|
| 113 | 82 | kfree(buf); |
|---|
| 114 | 83 | return ret; |
|---|
| 115 | 84 | } |
|---|
| .. | .. |
|---|
| 797 | 766 | return ooo_cmdsn; |
|---|
| 798 | 767 | } |
|---|
| 799 | 768 | |
|---|
| 800 | | -/* |
|---|
| 801 | | - * Called with sess->cmdsn_mutex held. |
|---|
| 802 | | - */ |
|---|
| 803 | 769 | static int iscsit_attach_ooo_cmdsn( |
|---|
| 804 | 770 | struct iscsi_session *sess, |
|---|
| 805 | 771 | struct iscsi_ooo_cmdsn *ooo_cmdsn) |
|---|
| 806 | 772 | { |
|---|
| 807 | 773 | struct iscsi_ooo_cmdsn *ooo_tail, *ooo_tmp; |
|---|
| 774 | + |
|---|
| 775 | + lockdep_assert_held(&sess->cmdsn_mutex); |
|---|
| 776 | + |
|---|
| 808 | 777 | /* |
|---|
| 809 | 778 | * We attach the struct iscsi_ooo_cmdsn entry to the out of order |
|---|
| 810 | 779 | * list in increasing CmdSN order. |
|---|
| .. | .. |
|---|
| 871 | 840 | mutex_unlock(&sess->cmdsn_mutex); |
|---|
| 872 | 841 | } |
|---|
| 873 | 842 | |
|---|
| 874 | | -/* |
|---|
| 875 | | - * Called with sess->cmdsn_mutex held. |
|---|
| 876 | | - */ |
|---|
| 877 | 843 | int iscsit_execute_ooo_cmdsns(struct iscsi_session *sess) |
|---|
| 878 | 844 | { |
|---|
| 879 | 845 | int ooo_count = 0; |
|---|
| 880 | 846 | struct iscsi_cmd *cmd = NULL; |
|---|
| 881 | 847 | struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp; |
|---|
| 848 | + |
|---|
| 849 | + lockdep_assert_held(&sess->cmdsn_mutex); |
|---|
| 882 | 850 | |
|---|
| 883 | 851 | list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp, |
|---|
| 884 | 852 | &sess->sess_ooo_cmdsn_list, ooo_list) { |
|---|
| .. | .. |
|---|
| 943 | 911 | return 0; |
|---|
| 944 | 912 | } |
|---|
| 945 | 913 | spin_unlock_bh(&cmd->istate_lock); |
|---|
| 946 | | - /* |
|---|
| 947 | | - * Determine if delayed TASK_ABORTED status for WRITEs |
|---|
| 948 | | - * should be sent now if no unsolicited data out |
|---|
| 949 | | - * payloads are expected, or if the delayed status |
|---|
| 950 | | - * should be sent after unsolicited data out with |
|---|
| 951 | | - * ISCSI_FLAG_CMD_FINAL set in iscsi_handle_data_out() |
|---|
| 952 | | - */ |
|---|
| 953 | | - if (transport_check_aborted_status(se_cmd, |
|---|
| 954 | | - (cmd->unsolicited_data == 0)) != 0) |
|---|
| 914 | + if (cmd->se_cmd.transport_state & CMD_T_ABORTED) |
|---|
| 955 | 915 | return 0; |
|---|
| 956 | | - /* |
|---|
| 957 | | - * Otherwise send CHECK_CONDITION and sense for |
|---|
| 958 | | - * exception |
|---|
| 959 | | - */ |
|---|
| 960 | 916 | return transport_send_check_condition_and_sense(se_cmd, |
|---|
| 961 | 917 | cmd->sense_reason, 0); |
|---|
| 962 | 918 | } |
|---|
| .. | .. |
|---|
| 974 | 930 | |
|---|
| 975 | 931 | if (!(cmd->cmd_flags & |
|---|
| 976 | 932 | ICF_NON_IMMEDIATE_UNSOLICITED_DATA)) { |
|---|
| 977 | | - /* |
|---|
| 978 | | - * Send the delayed TASK_ABORTED status for |
|---|
| 979 | | - * WRITEs if no more unsolicitied data is |
|---|
| 980 | | - * expected. |
|---|
| 981 | | - */ |
|---|
| 982 | | - if (transport_check_aborted_status(se_cmd, 1) |
|---|
| 983 | | - != 0) |
|---|
| 933 | + if (cmd->se_cmd.transport_state & CMD_T_ABORTED) |
|---|
| 984 | 934 | return 0; |
|---|
| 985 | 935 | |
|---|
| 986 | 936 | iscsit_set_dataout_sequence_values(cmd); |
|---|
| .. | .. |
|---|
| 995 | 945 | |
|---|
| 996 | 946 | if ((cmd->data_direction == DMA_TO_DEVICE) && |
|---|
| 997 | 947 | !(cmd->cmd_flags & ICF_NON_IMMEDIATE_UNSOLICITED_DATA)) { |
|---|
| 998 | | - /* |
|---|
| 999 | | - * Send the delayed TASK_ABORTED status for WRITEs if |
|---|
| 1000 | | - * no more nsolicitied data is expected. |
|---|
| 1001 | | - */ |
|---|
| 1002 | | - if (transport_check_aborted_status(se_cmd, 1) != 0) |
|---|
| 948 | + if (cmd->se_cmd.transport_state & CMD_T_ABORTED) |
|---|
| 1003 | 949 | return 0; |
|---|
| 1004 | 950 | |
|---|
| 1005 | | - iscsit_set_unsoliticed_dataout(cmd); |
|---|
| 951 | + iscsit_set_unsolicited_dataout(cmd); |
|---|
| 1006 | 952 | } |
|---|
| 1007 | 953 | return transport_handle_cdb_direct(&cmd->se_cmd); |
|---|
| 1008 | 954 | |
|---|
| .. | .. |
|---|
| 1169 | 1115 | na = iscsit_tpg_get_node_attrib(sess); |
|---|
| 1170 | 1116 | |
|---|
| 1171 | 1117 | if (!sess->sess_ops->ErrorRecoveryLevel) { |
|---|
| 1172 | | - pr_debug("Unable to recover from DataOut timeout while" |
|---|
| 1173 | | - " in ERL=0.\n"); |
|---|
| 1118 | + pr_err("Unable to recover from DataOut timeout while" |
|---|
| 1119 | + " in ERL=0, closing iSCSI connection for I_T Nexus" |
|---|
| 1120 | + " %s,i,0x%6phN,%s,t,0x%02x\n", |
|---|
| 1121 | + sess->sess_ops->InitiatorName, sess->isid, |
|---|
| 1122 | + sess->tpg->tpg_tiqn->tiqn, (u32)sess->tpg->tpgt); |
|---|
| 1174 | 1123 | goto failure; |
|---|
| 1175 | 1124 | } |
|---|
| 1176 | 1125 | |
|---|
| 1177 | 1126 | if (++cmd->dataout_timeout_retries == na->dataout_timeout_retries) { |
|---|
| 1178 | | - pr_debug("Command ITT: 0x%08x exceeded max retries" |
|---|
| 1179 | | - " for DataOUT timeout %u, closing iSCSI connection.\n", |
|---|
| 1180 | | - cmd->init_task_tag, na->dataout_timeout_retries); |
|---|
| 1127 | + pr_err("Command ITT: 0x%08x exceeded max retries" |
|---|
| 1128 | + " for DataOUT timeout %u, closing iSCSI connection for" |
|---|
| 1129 | + " I_T Nexus %s,i,0x%6phN,%s,t,0x%02x\n", |
|---|
| 1130 | + cmd->init_task_tag, na->dataout_timeout_retries, |
|---|
| 1131 | + sess->sess_ops->InitiatorName, sess->isid, |
|---|
| 1132 | + sess->tpg->tpg_tiqn->tiqn, (u32)sess->tpg->tpgt); |
|---|
| 1181 | 1133 | goto failure; |
|---|
| 1182 | 1134 | } |
|---|
| 1183 | 1135 | |
|---|
| .. | .. |
|---|
| 1224 | 1176 | |
|---|
| 1225 | 1177 | failure: |
|---|
| 1226 | 1178 | spin_unlock_bh(&cmd->dataout_timeout_lock); |
|---|
| 1179 | + iscsit_fill_cxn_timeout_err_stats(sess); |
|---|
| 1227 | 1180 | iscsit_cause_connection_reinstatement(conn, 0); |
|---|
| 1228 | 1181 | iscsit_dec_conn_usage_count(conn); |
|---|
| 1229 | 1182 | } |
|---|
| .. | .. |
|---|
| 1247 | 1200 | spin_unlock_bh(&cmd->dataout_timeout_lock); |
|---|
| 1248 | 1201 | } |
|---|
| 1249 | 1202 | |
|---|
| 1250 | | -/* |
|---|
| 1251 | | - * Called with cmd->dataout_timeout_lock held. |
|---|
| 1252 | | - */ |
|---|
| 1253 | 1203 | void iscsit_start_dataout_timer( |
|---|
| 1254 | 1204 | struct iscsi_cmd *cmd, |
|---|
| 1255 | 1205 | struct iscsi_conn *conn) |
|---|
| .. | .. |
|---|
| 1257 | 1207 | struct iscsi_session *sess = conn->sess; |
|---|
| 1258 | 1208 | struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); |
|---|
| 1259 | 1209 | |
|---|
| 1210 | + lockdep_assert_held(&cmd->dataout_timeout_lock); |
|---|
| 1211 | + |
|---|
| 1260 | 1212 | if (cmd->dataout_timer_flags & ISCSI_TF_RUNNING) |
|---|
| 1261 | 1213 | return; |
|---|
| 1262 | 1214 | |
|---|