diff options
Diffstat (limited to 'drivers/target/iscsi/iscsi_target.c')
| -rw-r--r-- | drivers/target/iscsi/iscsi_target.c | 940 |
1 files changed, 514 insertions, 426 deletions
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index bd15a564fe24..a2dde08c8a62 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /******************************************************************************* * This file contains main functions related to the iSCSI Target Core Driver. * @@ -5,18 +6,9 @@ * * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. ******************************************************************************/ -#include <crypto/hash.h> +#include <linux/crc32c.h> #include <linux/string.h> #include <linux/kthread.h> #include <linux/completion.h> @@ -25,7 +17,7 @@ #include <linux/idr.h> #include <linux/delay.h> #include <linux/sched/signal.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/inet.h> #include <net/ipv6.h> #include <scsi/scsi_proto.h> @@ -34,6 +26,7 @@ #include <target/target_core_base.h> #include <target/target_core_fabric.h> +#include <target/target_core_backend.h> #include <target/iscsi/iscsi_target_core.h> #include "iscsi_target_parameters.h" #include "iscsi_target_seq_pdu_list.h" @@ -67,7 +60,7 @@ struct kmem_cache *lio_dr_cache; struct kmem_cache *lio_ooo_cache; struct kmem_cache *lio_r2t_cache; -static int iscsit_handle_immediate_data(struct iscsi_cmd *, +static int iscsit_handle_immediate_data(struct iscsit_cmd *, struct iscsi_scsi_req *, u32); struct iscsi_tiqn *iscsit_get_tiqn_for_login(unsigned char *buf) @@ -308,9 +301,6 @@ bool iscsit_check_np_match( return false; } -/* - * Called with mutex np_lock held - */ static struct iscsi_np *iscsit_get_np( struct sockaddr_storage *sockaddr, int network_transport) @@ -318,6 +308,8 @@ static struct iscsi_np *iscsit_get_np( struct iscsi_np *np; bool match; + lockdep_assert_held(&np_lock); + list_for_each_entry(np, &g_np_list, np_list) { spin_lock_bh(&np->np_thread_lock); if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) { @@ -372,8 +364,6 @@ struct iscsi_np *iscsit_add_np( init_completion(&np->np_restart_comp); INIT_LIST_HEAD(&np->np_list); - timer_setup(&np->np_login_timer, iscsi_handle_login_thread_timeout, 0); - ret = iscsi_target_setup_login_socket(np, sockaddr); if (ret != 0) { kfree(np); @@ -481,19 +471,18 @@ int iscsit_del_np(struct iscsi_np *np) return 0; } -static void iscsit_get_rx_pdu(struct iscsi_conn *); +static void iscsit_get_rx_pdu(struct iscsit_conn *); -int iscsit_queue_rsp(struct iscsi_conn *conn, struct iscsi_cmd *cmd) +int iscsit_queue_rsp(struct iscsit_conn *conn, struct iscsit_cmd *cmd) { return iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state); } EXPORT_SYMBOL(iscsit_queue_rsp); -void iscsit_aborted_task(struct iscsi_conn *conn, struct iscsi_cmd *cmd) +void iscsit_aborted_task(struct iscsit_conn *conn, struct iscsit_cmd *cmd) { spin_lock_bh(&conn->cmd_lock); - if (!list_empty(&cmd->i_conn_node) && - !(cmd->se_cmd.transport_state & CMD_T_FABRIC_STOP)) + if (!list_empty(&cmd->i_conn_node)) list_del_init(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); @@ -501,12 +490,12 @@ void iscsit_aborted_task(struct iscsi_conn *conn, struct iscsi_cmd *cmd) } EXPORT_SYMBOL(iscsit_aborted_task); -static void iscsit_do_crypto_hash_buf(struct ahash_request *, const void *, - u32, u32, const void *, void *); -static void iscsit_tx_thread_wait_for_tcp(struct iscsi_conn *); +static u32 iscsit_crc_buf(const void *buf, u32 payload_length, + u32 padding, const void *pad_bytes); +static void iscsit_tx_thread_wait_for_tcp(struct iscsit_conn *); static int -iscsit_xmit_nondatain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +iscsit_xmit_nondatain_pdu(struct iscsit_conn *conn, struct iscsit_cmd *cmd, const void *data_buf, u32 data_buf_len) { struct iscsi_hdr *hdr = (struct iscsi_hdr *)cmd->pdu; @@ -521,9 +510,7 @@ iscsit_xmit_nondatain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, if (conn->conn_ops->HeaderDigest) { u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; - iscsit_do_crypto_hash_buf(conn->conn_tx_hash, hdr, - ISCSI_HDR_LEN, 0, NULL, - header_digest); + *header_digest = iscsit_crc_buf(hdr, ISCSI_HDR_LEN, 0, NULL); iov[0].iov_len += ISCSI_CRC_LEN; tx_size += ISCSI_CRC_LEN; @@ -548,11 +535,9 @@ iscsit_xmit_nondatain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, } if (conn->conn_ops->DataDigest) { - iscsit_do_crypto_hash_buf(conn->conn_tx_hash, - data_buf, data_buf_len, - padding, &cmd->pad_bytes, - &cmd->data_crc); - + cmd->data_crc = iscsit_crc_buf(data_buf, data_buf_len, + padding, + &cmd->pad_bytes); iov[niov].iov_base = &cmd->data_crc; iov[niov++].iov_len = ISCSI_CRC_LEN; tx_size += ISCSI_CRC_LEN; @@ -574,12 +559,13 @@ iscsit_xmit_nondatain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, return 0; } -static int iscsit_map_iovec(struct iscsi_cmd *, struct kvec *, u32, u32); -static void iscsit_unmap_iovec(struct iscsi_cmd *); -static u32 iscsit_do_crypto_hash_sg(struct ahash_request *, struct iscsi_cmd *, - u32, u32, u32, u8 *); +static int iscsit_map_iovec(struct iscsit_cmd *cmd, struct kvec *iov, int nvec, + u32 data_offset, u32 data_length); +static void iscsit_unmap_iovec(struct iscsit_cmd *); +static u32 iscsit_crc_sglist(const struct iscsit_cmd *cmd, u32 data_length, + u32 padding, const u8 *pad_bytes); static int -iscsit_xmit_datain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +iscsit_xmit_datain_pdu(struct iscsit_conn *conn, struct iscsit_cmd *cmd, const struct iscsi_datain *datain) { struct kvec *iov; @@ -594,10 +580,8 @@ iscsit_xmit_datain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, if (conn->conn_ops->HeaderDigest) { u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; - iscsit_do_crypto_hash_buf(conn->conn_tx_hash, cmd->pdu, - ISCSI_HDR_LEN, 0, NULL, - header_digest); - + *header_digest = iscsit_crc_buf(cmd->pdu, ISCSI_HDR_LEN, 0, + NULL); iov[0].iov_len += ISCSI_CRC_LEN; tx_size += ISCSI_CRC_LEN; @@ -605,7 +589,8 @@ iscsit_xmit_datain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, *header_digest); } - iov_ret = iscsit_map_iovec(cmd, &cmd->iov_data[1], + iov_ret = iscsit_map_iovec(cmd, &cmd->iov_data[iov_count], + cmd->orig_iov_data_count - (iov_count + 2), datain->offset, datain->length); if (iov_ret < 0) return -1; @@ -623,12 +608,8 @@ iscsit_xmit_datain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, } if (conn->conn_ops->DataDigest) { - cmd->data_crc = iscsit_do_crypto_hash_sg(conn->conn_tx_hash, - cmd, datain->offset, - datain->length, - cmd->padding, - cmd->pad_bytes); - + cmd->data_crc = iscsit_crc_sglist(cmd, datain->length, + cmd->padding, cmd->pad_bytes); iov[iov_count].iov_base = &cmd->data_crc; iov[iov_count++].iov_len = ISCSI_CRC_LEN; tx_size += ISCSI_CRC_LEN; @@ -652,7 +633,7 @@ iscsit_xmit_datain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, return 0; } -static int iscsit_xmit_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +static int iscsit_xmit_pdu(struct iscsit_conn *conn, struct iscsit_cmd *cmd, struct iscsi_datain_req *dr, const void *buf, u32 buf_len) { @@ -662,7 +643,7 @@ static int iscsit_xmit_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd, return iscsit_xmit_nondatain_pdu(conn, cmd, buf, buf_len); } -static enum target_prot_op iscsit_get_sup_prot_ops(struct iscsi_conn *conn) +static enum target_prot_op iscsit_get_sup_prot_ops(struct iscsit_conn *conn) { return TARGET_PROT_NORMAL; } @@ -710,13 +691,19 @@ static int __init iscsi_target_init_module(void) if (!iscsit_global->ts_bitmap) goto configfs_out; + if (!zalloc_cpumask_var(&iscsit_global->allowed_cpumask, GFP_KERNEL)) { + pr_err("Unable to allocate iscsit_global->allowed_cpumask\n"); + goto bitmap_out; + } + cpumask_setall(iscsit_global->allowed_cpumask); + lio_qr_cache = kmem_cache_create("lio_qr_cache", sizeof(struct iscsi_queue_req), __alignof__(struct iscsi_queue_req), 0, NULL); if (!lio_qr_cache) { pr_err("Unable to kmem_cache_create() for" " lio_qr_cache\n"); - goto bitmap_out; + goto cpumask_out; } lio_dr_cache = kmem_cache_create("lio_dr_cache", @@ -761,6 +748,8 @@ dr_out: kmem_cache_destroy(lio_dr_cache); qr_out: kmem_cache_destroy(lio_qr_cache); +cpumask_out: + free_cpumask_var(iscsit_global->allowed_cpumask); bitmap_out: vfree(iscsit_global->ts_bitmap); configfs_out: @@ -790,16 +779,17 @@ static void __exit iscsi_target_cleanup_module(void) target_unregister_template(&iscsi_ops); + free_cpumask_var(iscsit_global->allowed_cpumask); vfree(iscsit_global->ts_bitmap); kfree(iscsit_global); } int iscsit_add_reject( - struct iscsi_conn *conn, + struct iscsit_conn *conn, u8 reason, unsigned char *buf) { - struct iscsi_cmd *cmd; + struct iscsit_cmd *cmd; cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE); if (!cmd) @@ -827,12 +817,12 @@ int iscsit_add_reject( EXPORT_SYMBOL(iscsit_add_reject); static int iscsit_add_reject_from_cmd( - struct iscsi_cmd *cmd, + struct iscsit_cmd *cmd, u8 reason, bool add_to_conn, unsigned char *buf) { - struct iscsi_conn *conn; + struct iscsit_conn *conn; const bool do_put = cmd->se_cmd.se_tfo != NULL; if (!cmd->conn) { @@ -871,13 +861,13 @@ static int iscsit_add_reject_from_cmd( return -1; } -static int iscsit_add_reject_cmd(struct iscsi_cmd *cmd, u8 reason, +static int iscsit_add_reject_cmd(struct iscsit_cmd *cmd, u8 reason, unsigned char *buf) { return iscsit_add_reject_from_cmd(cmd, reason, true, buf); } -int iscsit_reject_cmd(struct iscsi_cmd *cmd, u8 reason, unsigned char *buf) +int iscsit_reject_cmd(struct iscsit_cmd *cmd, u8 reason, unsigned char *buf) { return iscsit_add_reject_from_cmd(cmd, reason, false, buf); } @@ -887,13 +877,10 @@ EXPORT_SYMBOL(iscsit_reject_cmd); * Map some portion of the allocated scatterlist to an iovec, suitable for * kernel sockets to copy data in/out. */ -static int iscsit_map_iovec( - struct iscsi_cmd *cmd, - struct kvec *iov, - u32 data_offset, - u32 data_length) +static int iscsit_map_iovec(struct iscsit_cmd *cmd, struct kvec *iov, int nvec, + u32 data_offset, u32 data_length) { - u32 i = 0; + u32 i = 0, orig_data_length = data_length; struct scatterlist *sg; unsigned int page_off; @@ -902,9 +889,12 @@ static int iscsit_map_iovec( */ u32 ent = data_offset / PAGE_SIZE; + if (!data_length) + return 0; + if (ent >= cmd->se_cmd.t_data_nents) { pr_err("Initial page entry out-of-bounds\n"); - return -1; + goto overflow; } sg = &cmd->se_cmd.t_data_sg[ent]; @@ -914,7 +904,12 @@ static int iscsit_map_iovec( cmd->first_data_sg_off = page_off; while (data_length) { - u32 cur_len = min_t(u32, data_length, sg->length - page_off); + u32 cur_len; + + if (WARN_ON_ONCE(!sg || i >= nvec)) + goto overflow; + + cur_len = min_t(u32, data_length, sg->length - page_off); iov[i].iov_base = kmap(sg_page(sg)) + sg->offset + page_off; iov[i].iov_len = cur_len; @@ -928,9 +923,19 @@ static int iscsit_map_iovec( cmd->kmapped_nents = i; return i; + +overflow: + pr_err("offset %d + length %d overflow; %d/%d; sg-list:\n", + data_offset, orig_data_length, i, nvec); + for_each_sg(cmd->se_cmd.t_data_sg, sg, + cmd->se_cmd.t_data_nents, i) { + pr_err("[%d] off %d len %d\n", + i, sg->offset, sg->length); + } + return -1; } -static void iscsit_unmap_iovec(struct iscsi_cmd *cmd) +static void iscsit_unmap_iovec(struct iscsit_cmd *cmd) { u32 i; struct scatterlist *sg; @@ -941,10 +946,10 @@ static void iscsit_unmap_iovec(struct iscsi_cmd *cmd) kunmap(sg_page(&sg[i])); } -static void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn) +static void iscsit_ack_from_expstatsn(struct iscsit_conn *conn, u32 exp_statsn) { LIST_HEAD(ack_list); - struct iscsi_cmd *cmd, *cmd_p; + struct iscsit_cmd *cmd, *cmd_p; conn->exp_statsn = exp_statsn; @@ -971,7 +976,7 @@ static void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn) } } -static int iscsit_allocate_iovecs(struct iscsi_cmd *cmd) +static int iscsit_allocate_iovecs(struct iscsit_cmd *cmd) { u32 iov_count = max(1UL, DIV_ROUND_UP(cmd->se_cmd.data_length, PAGE_SIZE)); @@ -984,12 +989,14 @@ static int iscsit_allocate_iovecs(struct iscsi_cmd *cmd) return 0; } -int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, unsigned char *buf) { int data_direction, payload_length; + struct iscsi_ecdb_ahdr *ecdb_ahdr; struct iscsi_scsi_req *hdr; int iscsi_task_attr; + unsigned char *cdb; int sam_task_attr; atomic_long_inc(&conn->sess->cmd_pdus); @@ -1090,6 +1097,27 @@ int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, ISCSI_REASON_BOOKMARK_INVALID, buf); } + cdb = hdr->cdb; + + if (hdr->hlength) { + ecdb_ahdr = (struct iscsi_ecdb_ahdr *) (hdr + 1); + if (ecdb_ahdr->ahstype != ISCSI_AHSTYPE_CDB) { + pr_err("Additional Header Segment type %d not supported!\n", + ecdb_ahdr->ahstype); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_CMD_NOT_SUPPORTED, buf); + } + + cdb = kmalloc(be16_to_cpu(ecdb_ahdr->ahslength) + 15, + GFP_KERNEL); + if (cdb == NULL) + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); + memcpy(cdb, hdr->cdb, ISCSI_CDB_SIZE); + memcpy(cdb + ISCSI_CDB_SIZE, ecdb_ahdr->ecdb, + be16_to_cpu(ecdb_ahdr->ahslength) - 1); + } + data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE : (hdr->flags & ISCSI_FLAG_CMD_READ) ? DMA_FROM_DEVICE : DMA_NONE; @@ -1137,9 +1165,12 @@ int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, struct iscsi_datain_req *dr; dr = iscsit_allocate_datain_req(); - if (!dr) + if (!dr) { + if (cdb != hdr->cdb) + kfree(cdb); return iscsit_add_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); + } iscsit_attach_datain_req(cmd, dr); } @@ -1147,10 +1178,11 @@ int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, /* * Initialize struct se_cmd descriptor from target_core_mod infrastructure */ - transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops, - conn->sess->se_sess, be32_to_cpu(hdr->data_length), - cmd->data_direction, sam_task_attr, - cmd->sense_buffer + 2); + __target_init_cmd(&cmd->se_cmd, &iscsi_ops, + conn->sess->se_sess, be32_to_cpu(hdr->data_length), + cmd->data_direction, sam_task_attr, + cmd->sense_buffer + 2, scsilun_to_int(&hdr->lun), + conn->cmd_cnt); pr_debug("Got SCSI Command, ITT: 0x%08x, CmdSN: 0x%08x," " ExpXferLen: %u, Length: %u, CID: %hu\n", hdr->itt, @@ -1159,23 +1191,30 @@ int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, target_get_sess_cmd(&cmd->se_cmd, true); - cmd->sense_reason = transport_lookup_cmd_lun(&cmd->se_cmd, - scsilun_to_int(&hdr->lun)); - if (cmd->sense_reason) - goto attach_cmd; - - /* only used for printks or comparing with ->ref_task_tag */ cmd->se_cmd.tag = (__force u32)cmd->init_task_tag; - cmd->sense_reason = target_setup_cmd_from_cdb(&cmd->se_cmd, hdr->cdb); + cmd->sense_reason = target_cmd_init_cdb(&cmd->se_cmd, cdb, + GFP_KERNEL); + + if (cdb != hdr->cdb) + kfree(cdb); + if (cmd->sense_reason) { if (cmd->sense_reason == TCM_OUT_OF_RESOURCES) { return iscsit_add_reject_cmd(cmd, - ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); + ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); } goto attach_cmd; } + cmd->sense_reason = transport_lookup_cmd_lun(&cmd->se_cmd); + if (cmd->sense_reason) + goto attach_cmd; + + cmd->sense_reason = target_cmd_parse_cdb(&cmd->se_cmd); + if (cmd->sense_reason) + goto attach_cmd; + if (iscsit_build_pdu_and_seq_lists(cmd, payload_length) < 0) { return iscsit_add_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); @@ -1185,17 +1224,11 @@ attach_cmd: spin_lock_bh(&conn->cmd_lock); list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list); spin_unlock_bh(&conn->cmd_lock); - /* - * Check if we need to delay processing because of ALUA - * Active/NonOptimized primary access state.. - */ - core_alua_check_nonop_delay(&cmd->se_cmd); - return 0; } EXPORT_SYMBOL(iscsit_setup_scsi_cmd); -void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *cmd) +void iscsit_set_unsolicited_dataout(struct iscsit_cmd *cmd) { iscsit_set_dataout_sequence_values(cmd); @@ -1203,9 +1236,9 @@ void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *cmd) iscsit_start_dataout_timer(cmd, cmd->conn); spin_unlock_bh(&cmd->dataout_timeout_lock); } -EXPORT_SYMBOL(iscsit_set_unsoliticed_dataout); +EXPORT_SYMBOL(iscsit_set_unsolicited_dataout); -int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +int iscsit_process_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, struct iscsi_scsi_req *hdr) { int cmdsn_ret = 0; @@ -1237,7 +1270,7 @@ int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, */ if (!cmd->immediate_data) { if (!cmd->sense_reason && cmd->unsolicited_data) - iscsit_set_unsoliticed_dataout(cmd); + iscsit_set_unsolicited_dataout(cmd); if (!cmd->sense_reason) return 0; @@ -1265,31 +1298,31 @@ int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, EXPORT_SYMBOL(iscsit_process_scsi_cmd); static int -iscsit_get_immediate_data(struct iscsi_cmd *cmd, struct iscsi_scsi_req *hdr, +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) - goto after_immediate_data; - /* - * Check for underflow case where both EDTL and immediate data payload - * exceeds what is presented by CDB's TRANSFER LENGTH, and what has - * already been set in target_cmd_size_check() as se_cmd->data_length. - * - * For this special case, fail the command and dump the immediate data - * payload. - */ - if (cmd->first_burst_len > cmd->se_cmd.data_length) { - cmd->sense_reason = TCM_INVALID_CDB_FIELD; - goto after_immediate_data; + if (dump_payload) { + u32 length = min(cmd->se_cmd.data_length - cmd->write_data_done, + cmd->first_burst_len); + + pr_debug("Dumping min(%d - %d, %d) = %d bytes of immediate data\n", + cmd->se_cmd.data_length, cmd->write_data_done, + cmd->first_burst_len, length); + rc = iscsit_dump_data_payload(cmd->conn, length, 1); + pr_debug("Finished dumping immediate data\n"); + if (rc < 0) + immed_ret = IMMEDIATE_DATA_CANNOT_RECOVER; + } else { + immed_ret = iscsit_handle_immediate_data(cmd, hdr, + cmd->first_burst_len); } - immed_ret = iscsit_handle_immediate_data(cmd, hdr, - cmd->first_burst_len); -after_immediate_data: if (immed_ret == IMMEDIATE_DATA_NORMAL_OPERATION) { /* * A PDU/CmdSN carrying Immediate Data passed @@ -1302,14 +1335,11 @@ after_immediate_data: return -1; if (cmd->sense_reason || cmdsn_ret == CMDSN_LOWER_THAN_EXP) { - int rc; - - rc = iscsit_dump_data_payload(cmd->conn, - cmd->first_burst_len, 1); target_put_sess_cmd(&cmd->se_cmd); - return rc; + + return 0; } else if (cmd->unsolicited_data) - iscsit_set_unsoliticed_dataout(cmd); + iscsit_set_unsolicited_dataout(cmd); } else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) { /* @@ -1332,7 +1362,7 @@ after_immediate_data: } static int -iscsit_handle_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +iscsit_handle_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, unsigned char *buf) { struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)buf; @@ -1364,69 +1394,50 @@ iscsit_handle_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, return iscsit_get_immediate_data(cmd, hdr, dump_payload); } -static u32 iscsit_do_crypto_hash_sg( - struct ahash_request *hash, - struct iscsi_cmd *cmd, - u32 data_offset, - u32 data_length, - u32 padding, - u8 *pad_bytes) +static u32 iscsit_crc_sglist(const struct iscsit_cmd *cmd, u32 data_length, + u32 padding, const u8 *pad_bytes) { - u32 data_crc; - struct scatterlist *sg; - unsigned int page_off; - - crypto_ahash_init(hash); - - sg = cmd->first_data_sg; - page_off = cmd->first_data_sg_off; + struct scatterlist *sg = cmd->first_data_sg; + unsigned int page_off = cmd->first_data_sg_off; + u32 crc = ~0; while (data_length) { - u32 cur_len = min_t(u32, data_length, (sg->length - page_off)); + u32 cur_len = min_t(u32, data_length, sg->length - page_off); + const void *virt; - ahash_request_set_crypt(hash, sg, NULL, cur_len); - crypto_ahash_update(hash); + virt = kmap_local_page(sg_page(sg)) + sg->offset + page_off; + crc = crc32c(crc, virt, cur_len); + kunmap_local(virt); - data_length -= cur_len; - page_off = 0; /* iscsit_map_iovec has already checked for invalid sg pointers */ sg = sg_next(sg); - } - - if (padding) { - struct scatterlist pad_sg; - sg_init_one(&pad_sg, pad_bytes, padding); - ahash_request_set_crypt(hash, &pad_sg, (u8 *)&data_crc, - padding); - crypto_ahash_finup(hash); - } else { - ahash_request_set_crypt(hash, NULL, (u8 *)&data_crc, 0); - crypto_ahash_final(hash); + page_off = 0; + data_length -= cur_len; } - return data_crc; + if (padding) + crc = crc32c(crc, pad_bytes, padding); + + return ~crc; } -static void iscsit_do_crypto_hash_buf(struct ahash_request *hash, - const void *buf, u32 payload_length, u32 padding, - const void *pad_bytes, void *data_crc) +static u32 iscsit_crc_buf(const void *buf, u32 payload_length, + u32 padding, const void *pad_bytes) { - struct scatterlist sg[2]; + u32 crc = ~0; - sg_init_table(sg, ARRAY_SIZE(sg)); - sg_set_buf(sg, buf, payload_length); - if (padding) - sg_set_buf(sg + 1, pad_bytes, padding); + crc = crc32c(crc, buf, payload_length); - ahash_request_set_crypt(hash, sg, data_crc, payload_length + padding); + if (padding) + crc = crc32c(crc, pad_bytes, padding); - crypto_ahash_digest(hash); + return ~crc; } int -__iscsit_check_dataout_hdr(struct iscsi_conn *conn, void *buf, - struct iscsi_cmd *cmd, u32 payload_length, +__iscsit_check_dataout_hdr(struct iscsit_conn *conn, void *buf, + struct iscsit_cmd *cmd, u32 payload_length, bool *success) { struct iscsi_data *hdr = buf; @@ -1529,11 +1540,11 @@ __iscsit_check_dataout_hdr(struct iscsi_conn *conn, void *buf, EXPORT_SYMBOL(__iscsit_check_dataout_hdr); int -iscsit_check_dataout_hdr(struct iscsi_conn *conn, void *buf, - struct iscsi_cmd **out_cmd) +iscsit_check_dataout_hdr(struct iscsit_conn *conn, void *buf, + struct iscsit_cmd **out_cmd) { struct iscsi_data *hdr = buf; - struct iscsi_cmd *cmd; + struct iscsit_cmd *cmd; u32 payload_length = ntoh24(hdr->dlength); int rc; bool success = false; @@ -1564,19 +1575,21 @@ iscsit_check_dataout_hdr(struct iscsi_conn *conn, void *buf, EXPORT_SYMBOL(iscsit_check_dataout_hdr); static int -iscsit_get_dataout(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +iscsit_get_dataout(struct iscsit_conn *conn, struct iscsit_cmd *cmd, struct iscsi_data *hdr) { struct kvec *iov; u32 checksum, iov_count = 0, padding = 0, rx_got = 0, rx_size = 0; - u32 payload_length = ntoh24(hdr->dlength); + u32 payload_length; int iov_ret, data_crc_failed = 0; + payload_length = min_t(u32, cmd->se_cmd.data_length, + ntoh24(hdr->dlength)); rx_size += payload_length; iov = &cmd->iov_data[0]; - iov_ret = iscsit_map_iovec(cmd, iov, be32_to_cpu(hdr->offset), - payload_length); + iov_ret = iscsit_map_iovec(cmd, iov, cmd->orig_iov_data_count - 2, + be32_to_cpu(hdr->offset), payload_length); if (iov_ret < 0) return -1; @@ -1596,6 +1609,7 @@ iscsit_get_dataout(struct iscsi_conn *conn, struct iscsi_cmd *cmd, rx_size += ISCSI_CRC_LEN; } + WARN_ON_ONCE(iov_count > cmd->orig_iov_data_count); rx_got = rx_data(conn, &cmd->iov_data[0], iov_count, rx_size); iscsit_unmap_iovec(cmd); @@ -1606,11 +1620,8 @@ iscsit_get_dataout(struct iscsi_conn *conn, struct iscsi_cmd *cmd, if (conn->conn_ops->DataDigest) { u32 data_crc; - data_crc = iscsit_do_crypto_hash_sg(conn->conn_rx_hash, cmd, - be32_to_cpu(hdr->offset), - payload_length, padding, - cmd->pad_bytes); - + data_crc = iscsit_crc_sglist(cmd, payload_length, padding, + cmd->pad_bytes); if (checksum != data_crc) { pr_err("ITT: 0x%08x, Offset: %u, Length: %u," " DataSN: 0x%08x, CRC32C DataDigest 0x%08x" @@ -1629,10 +1640,10 @@ iscsit_get_dataout(struct iscsi_conn *conn, struct iscsi_cmd *cmd, } int -iscsit_check_dataout_payload(struct iscsi_cmd *cmd, struct iscsi_data *hdr, +iscsit_check_dataout_payload(struct iscsit_cmd *cmd, struct iscsi_data *hdr, bool data_crc_failed) { - struct iscsi_conn *conn = cmd->conn; + struct iscsit_conn *conn = cmd->conn; int rc, ooo_cmdsn; /* * Increment post receive data and CRC values or perform @@ -1667,9 +1678,9 @@ iscsit_check_dataout_payload(struct iscsi_cmd *cmd, struct iscsi_data *hdr, } EXPORT_SYMBOL(iscsit_check_dataout_payload); -static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf) +static int iscsit_handle_data_out(struct iscsit_conn *conn, unsigned char *buf) { - struct iscsi_cmd *cmd = NULL; + struct iscsit_cmd *cmd = NULL; struct iscsi_data *hdr = (struct iscsi_data *)buf; int rc; bool data_crc_failed = false; @@ -1689,7 +1700,7 @@ static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf) return iscsit_check_dataout_payload(cmd, hdr, data_crc_failed); } -int iscsit_setup_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +int iscsit_setup_nop_out(struct iscsit_conn *conn, struct iscsit_cmd *cmd, struct iscsi_nopout *hdr) { u32 payload_length = ntoh24(hdr->dlength); @@ -1737,7 +1748,7 @@ int iscsit_setup_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, * 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 iscsi_cmd, as both + * Either way, make sure we allocate an struct iscsit_cmd, as both * can contain ping data. */ if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) { @@ -1756,10 +1767,10 @@ int iscsit_setup_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, } EXPORT_SYMBOL(iscsit_setup_nop_out); -int iscsit_process_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +int iscsit_process_nop_out(struct iscsit_conn *conn, struct iscsit_cmd *cmd, struct iscsi_nopout *hdr) { - struct iscsi_cmd *cmd_p = NULL; + struct iscsit_cmd *cmd_p = NULL; int cmdsn_ret = 0; /* * Initiator is expecting a NopIN ping reply.. @@ -1818,7 +1829,7 @@ int iscsit_process_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, } EXPORT_SYMBOL(iscsit_process_nop_out); -static int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +static int iscsit_handle_nop_out(struct iscsit_conn *conn, struct iscsit_cmd *cmd, unsigned char *buf) { unsigned char *ping_data = NULL; @@ -1861,6 +1872,7 @@ static int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, rx_size += ISCSI_CRC_LEN; } + WARN_ON_ONCE(niov > ARRAY_SIZE(cmd->iov_misc)); rx_got = rx_data(conn, &cmd->iov_misc[0], niov, rx_size); if (rx_got != rx_size) { ret = -1; @@ -1868,10 +1880,8 @@ static int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, } if (conn->conn_ops->DataDigest) { - iscsit_do_crypto_hash_buf(conn->conn_rx_hash, ping_data, - payload_length, padding, - cmd->pad_bytes, &data_crc); - + data_crc = iscsit_crc_buf(ping_data, payload_length, + padding, cmd->pad_bytes); if (checksum != data_crc) { pr_err("Ping data CRC32C DataDigest" " 0x%08x does not match computed 0x%08x\n", @@ -1902,7 +1912,7 @@ static int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, ping_data[payload_length] = '\0'; /* - * Attach ping data to struct iscsi_cmd->buf_ptr. + * Attach ping data to struct iscsit_cmd->buf_ptr. */ cmd->buf_ptr = ping_data; cmd->buf_ptr_size = payload_length; @@ -1944,7 +1954,7 @@ static enum tcm_tmreq_table iscsit_convert_tmf(u8 iscsi_tmf) } int -iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +iscsit_handle_task_mgt_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, unsigned char *buf) { struct se_tmr_req *se_tmr; @@ -1989,9 +1999,11 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, buf); } - transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops, - conn->sess->se_sess, 0, DMA_NONE, - TCM_SIMPLE_TAG, cmd->sense_buffer + 2); + __target_init_cmd(&cmd->se_cmd, &iscsi_ops, + conn->sess->se_sess, 0, DMA_NONE, + TCM_SIMPLE_TAG, cmd->sense_buffer + 2, + scsilun_to_int(&hdr->lun), + conn->cmd_cnt); target_get_sess_cmd(&cmd->se_cmd, true); @@ -2029,8 +2041,7 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, * 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, - scsilun_to_int(&hdr->lun)); + ret = transport_lookup_tmr_lun(&cmd->se_cmd); if (ret < 0) { se_tmr->response = ISCSI_TMF_RSP_NO_LUN; goto attach; @@ -2125,7 +2136,7 @@ EXPORT_SYMBOL(iscsit_handle_task_mgt_cmd); /* #warning FIXME: Support Text Command parameters besides SendTargets */ int -iscsit_setup_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +iscsit_setup_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, struct iscsi_text *hdr) { u32 payload_length = ntoh24(hdr->dlength); @@ -2165,7 +2176,7 @@ iscsit_setup_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, EXPORT_SYMBOL(iscsit_setup_text_cmd); int -iscsit_process_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +iscsit_process_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, struct iscsi_text *hdr) { unsigned char *text_in = cmd->text_in_ptr, *text_ptr; @@ -2180,24 +2191,22 @@ iscsit_process_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, } goto empty_sendtargets; } - if (strncmp("SendTargets", text_in, 11) != 0) { + if (strncmp("SendTargets=", text_in, 12) != 0) { pr_err("Received Text Data that is not" " SendTargets, cannot continue.\n"); goto reject; } + /* '=' confirmed in strncmp */ text_ptr = strchr(text_in, '='); - if (!text_ptr) { - pr_err("No \"=\" separator found in Text Data," - " cannot continue.\n"); - goto reject; - } - if (!strncmp("=All", text_ptr, 4)) { + BUG_ON(!text_ptr); + if (!strncmp("=All", text_ptr, 5)) { cmd->cmd_flags |= ICF_SENDTARGETS_ALL; } else if (!strncmp("=iqn.", text_ptr, 5) || !strncmp("=eui.", text_ptr, 5)) { cmd->cmd_flags |= ICF_SENDTARGETS_SINGLE; } else { - pr_err("Unable to locate valid SendTargets=%s value\n", text_ptr); + pr_err("Unable to locate valid SendTargets%s value\n", + text_ptr); goto reject; } @@ -2226,7 +2235,7 @@ reject: EXPORT_SYMBOL(iscsit_process_text_cmd); static int -iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, unsigned char *buf) { struct iscsi_text *hdr = (struct iscsi_text *)buf; @@ -2241,43 +2250,38 @@ iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, rx_size = payload_length; if (payload_length) { u32 checksum = 0, data_crc = 0; - u32 padding = 0, pad_bytes = 0; + u32 padding = 0; int niov = 0, rx_got; - struct kvec iov[3]; + struct kvec iov[2]; - text_in = kzalloc(payload_length, GFP_KERNEL); + rx_size = ALIGN(payload_length, 4); + text_in = kzalloc(rx_size, GFP_KERNEL); if (!text_in) goto reject; cmd->text_in_ptr = text_in; - memset(iov, 0, 3 * sizeof(struct kvec)); + memset(iov, 0, sizeof(iov)); iov[niov].iov_base = text_in; - iov[niov++].iov_len = payload_length; + iov[niov++].iov_len = rx_size; - padding = ((-payload_length) & 3); - if (padding != 0) { - iov[niov].iov_base = &pad_bytes; - iov[niov++].iov_len = padding; - rx_size += padding; + padding = rx_size - payload_length; + if (padding) pr_debug("Receiving %u additional bytes" " for padding.\n", padding); - } if (conn->conn_ops->DataDigest) { iov[niov].iov_base = &checksum; iov[niov++].iov_len = ISCSI_CRC_LEN; rx_size += ISCSI_CRC_LEN; } + WARN_ON_ONCE(niov > ARRAY_SIZE(iov)); rx_got = rx_data(conn, &iov[0], niov, rx_size); if (rx_got != rx_size) goto reject; if (conn->conn_ops->DataDigest) { - iscsit_do_crypto_hash_buf(conn->conn_rx_hash, text_in, - payload_length, padding, - &pad_bytes, &data_crc); - + data_crc = iscsit_crc_buf(text_in, rx_size, 0, NULL); if (checksum != data_crc) { pr_err("Text data CRC32C DataDigest" " 0x%08x does not match computed" @@ -2317,10 +2321,10 @@ reject: return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf); } -int iscsit_logout_closesession(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +int iscsit_logout_closesession(struct iscsit_cmd *cmd, struct iscsit_conn *conn) { - struct iscsi_conn *conn_p; - struct iscsi_session *sess = conn->sess; + struct iscsit_conn *conn_p; + struct iscsit_session *sess = conn->sess; pr_debug("Received logout request CLOSESESSION on CID: %hu" " for SID: %u.\n", conn->cid, conn->sess->sid); @@ -2347,10 +2351,10 @@ int iscsit_logout_closesession(struct iscsi_cmd *cmd, struct iscsi_conn *conn) return 0; } -int iscsit_logout_closeconnection(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +int iscsit_logout_closeconnection(struct iscsit_cmd *cmd, struct iscsit_conn *conn) { - struct iscsi_conn *l_conn; - struct iscsi_session *sess = conn->sess; + struct iscsit_conn *l_conn; + struct iscsit_session *sess = conn->sess; pr_debug("Received logout request CLOSECONNECTION for CID:" " %hu on CID: %hu.\n", cmd->logout_cid, conn->cid); @@ -2395,9 +2399,9 @@ int iscsit_logout_closeconnection(struct iscsi_cmd *cmd, struct iscsi_conn *conn return 0; } -int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +int iscsit_logout_removeconnforrecovery(struct iscsit_cmd *cmd, struct iscsit_conn *conn) { - struct iscsi_session *sess = conn->sess; + struct iscsit_session *sess = conn->sess; pr_debug("Received explicit REMOVECONNFORRECOVERY logout for" " CID: %hu on CID: %hu.\n", cmd->logout_cid, conn->cid); @@ -2425,7 +2429,7 @@ int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *cmd, struct iscsi_conn } int -iscsit_handle_logout_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, +iscsit_handle_logout_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, unsigned char *buf) { int cmdsn_ret, logout_remove = 0; @@ -2506,7 +2510,7 @@ iscsit_handle_logout_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, EXPORT_SYMBOL(iscsit_handle_logout_cmd); int iscsit_handle_snack( - struct iscsi_conn *conn, + struct iscsit_conn *conn, unsigned char *buf) { struct iscsi_snack *hdr; @@ -2560,7 +2564,7 @@ int iscsit_handle_snack( } EXPORT_SYMBOL(iscsit_handle_snack); -static void iscsit_rx_thread_wait_for_tcp(struct iscsi_conn *conn) +static void iscsit_rx_thread_wait_for_tcp(struct iscsit_conn *conn) { if ((conn->sock->sk->sk_shutdown & SEND_SHUTDOWN) || (conn->sock->sk->sk_shutdown & RCV_SHUTDOWN)) { @@ -2571,22 +2575,42 @@ static void iscsit_rx_thread_wait_for_tcp(struct iscsi_conn *conn) } static int iscsit_handle_immediate_data( - struct iscsi_cmd *cmd, + struct iscsit_cmd *cmd, struct iscsi_scsi_req *hdr, u32 length) { int iov_ret, rx_got = 0, rx_size = 0; u32 checksum, iov_count = 0, padding = 0; - struct iscsi_conn *conn = cmd->conn; + struct iscsit_conn *conn = cmd->conn; struct kvec *iov; + void *overflow_buf = NULL; - iov_ret = iscsit_map_iovec(cmd, cmd->iov_data, cmd->write_data_done, length); + BUG_ON(cmd->write_data_done > cmd->se_cmd.data_length); + rx_size = min(cmd->se_cmd.data_length - cmd->write_data_done, length); + iov_ret = iscsit_map_iovec(cmd, cmd->iov_data, + cmd->orig_iov_data_count - 2, + cmd->write_data_done, rx_size); if (iov_ret < 0) return IMMEDIATE_DATA_CANNOT_RECOVER; - rx_size = length; iov_count = iov_ret; iov = &cmd->iov_data[0]; + if (rx_size < length) { + /* + * Special case: length of immediate data exceeds the data + * buffer size derived from the CDB. + */ + overflow_buf = kmalloc(length - rx_size, GFP_KERNEL); + if (!overflow_buf) { + iscsit_unmap_iovec(cmd); + return IMMEDIATE_DATA_CANNOT_RECOVER; + } + cmd->overflow_buf = overflow_buf; + iov[iov_count].iov_base = overflow_buf; + iov[iov_count].iov_len = length - rx_size; + iov_count++; + rx_size = length; + } padding = ((-length) & 3); if (padding != 0) { @@ -2601,6 +2625,7 @@ static int iscsit_handle_immediate_data( rx_size += ISCSI_CRC_LEN; } + WARN_ON_ONCE(iov_count > cmd->orig_iov_data_count); rx_got = rx_data(conn, &cmd->iov_data[0], iov_count, rx_size); iscsit_unmap_iovec(cmd); @@ -2613,10 +2638,8 @@ static int iscsit_handle_immediate_data( if (conn->conn_ops->DataDigest) { u32 data_crc; - data_crc = iscsit_do_crypto_hash_sg(conn->conn_rx_hash, cmd, - cmd->write_data_done, length, padding, - cmd->pad_bytes); - + data_crc = iscsit_crc_sglist(cmd, length, padding, + cmd->pad_bytes); if (checksum != data_crc) { pr_err("ImmediateData CRC32C DataDigest 0x%08x" " does not match computed 0x%08x\n", checksum, @@ -2655,17 +2678,16 @@ static int iscsit_handle_immediate_data( return IMMEDIATE_DATA_NORMAL_OPERATION; } -/* - * Called with sess->conn_lock held. - */ /* #warning iscsi_build_conn_drop_async_message() only sends out on connections with active network interface */ -static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn) +static void iscsit_build_conn_drop_async_message(struct iscsit_conn *conn) { - struct iscsi_cmd *cmd; - struct iscsi_conn *conn_p; + struct iscsit_cmd *cmd; + struct iscsit_conn *conn_p; bool found = false; + lockdep_assert_held(&conn->sess->conn_lock); + /* * Only send a Asynchronous Message on connections whos network * interface is still functional. @@ -2700,8 +2722,8 @@ static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn) } static int iscsit_send_conn_drop_async_message( - struct iscsi_cmd *cmd, - struct iscsi_conn *conn) + struct iscsit_cmd *cmd, + struct iscsit_conn *conn) { struct iscsi_async *hdr; @@ -2729,7 +2751,7 @@ static int iscsit_send_conn_drop_async_message( return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, NULL, 0); } -static void iscsit_tx_thread_wait_for_tcp(struct iscsi_conn *conn) +static void iscsit_tx_thread_wait_for_tcp(struct iscsit_conn *conn) { if ((conn->sock->sk->sk_shutdown & SEND_SHUTDOWN) || (conn->sock->sk->sk_shutdown & RCV_SHUTDOWN)) { @@ -2740,7 +2762,7 @@ static void iscsit_tx_thread_wait_for_tcp(struct iscsi_conn *conn) } void -iscsit_build_datain_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +iscsit_build_datain_pdu(struct iscsit_cmd *cmd, struct iscsit_conn *conn, struct iscsi_datain *datain, struct iscsi_data_rsp *hdr, bool set_statsn) { @@ -2785,7 +2807,7 @@ iscsit_build_datain_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn, } EXPORT_SYMBOL(iscsit_build_datain_pdu); -static int iscsit_send_datain(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +static int iscsit_send_datain(struct iscsit_cmd *cmd, struct iscsit_conn *conn) { struct iscsi_data_rsp *hdr = (struct iscsi_data_rsp *)&cmd->pdu[0]; struct iscsi_datain datain; @@ -2846,12 +2868,12 @@ static int iscsit_send_datain(struct iscsi_cmd *cmd, struct iscsi_conn *conn) } int -iscsit_build_logout_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +iscsit_build_logout_rsp(struct iscsit_cmd *cmd, struct iscsit_conn *conn, struct iscsi_logout_rsp *hdr) { - struct iscsi_conn *logout_conn = NULL; + struct iscsit_conn *logout_conn = NULL; struct iscsi_conn_recovery *cr = NULL; - struct iscsi_session *sess = conn->sess; + struct iscsit_session *sess = conn->sess; /* * The actual shutting down of Sessions and/or Connections * for CLOSESESSION and CLOSECONNECTION Logout Requests @@ -2941,7 +2963,7 @@ iscsit_build_logout_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, EXPORT_SYMBOL(iscsit_build_logout_rsp); static int -iscsit_send_logout(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +iscsit_send_logout(struct iscsit_cmd *cmd, struct iscsit_conn *conn) { int rc; @@ -2954,7 +2976,7 @@ iscsit_send_logout(struct iscsi_cmd *cmd, struct iscsi_conn *conn) } void -iscsit_build_nopin_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +iscsit_build_nopin_rsp(struct iscsit_cmd *cmd, struct iscsit_conn *conn, struct iscsi_nopin *hdr, bool nopout_response) { hdr->opcode = ISCSI_OP_NOOP_IN; @@ -2985,8 +3007,8 @@ EXPORT_SYMBOL(iscsit_build_nopin_rsp); * Unsolicited NOPIN, either requesting a response or not. */ static int iscsit_send_unsolicited_nopin( - struct iscsi_cmd *cmd, - struct iscsi_conn *conn, + struct iscsit_cmd *cmd, + struct iscsit_conn *conn, int want_response) { struct iscsi_nopin *hdr = (struct iscsi_nopin *)&cmd->pdu[0]; @@ -3010,15 +3032,15 @@ static int iscsit_send_unsolicited_nopin( } static int -iscsit_send_nopin(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +iscsit_send_nopin(struct iscsit_cmd *cmd, struct iscsit_conn *conn) { struct iscsi_nopin *hdr = (struct iscsi_nopin *)&cmd->pdu[0]; iscsit_build_nopin_rsp(cmd, conn, hdr, true); /* - * NOPOUT Ping Data is attached to struct iscsi_cmd->buf_ptr. - * NOPOUT DataSegmentLength is at struct iscsi_cmd->buf_ptr_size. + * NOPOUT Ping Data is attached to struct iscsit_cmd->buf_ptr. + * NOPOUT DataSegmentLength is at struct iscsit_cmd->buf_ptr_size. */ pr_debug("Echoing back %u bytes of ping data.\n", cmd->buf_ptr_size); @@ -3028,8 +3050,8 @@ iscsit_send_nopin(struct iscsi_cmd *cmd, struct iscsi_conn *conn) } static int iscsit_send_r2t( - struct iscsi_cmd *cmd, - struct iscsi_conn *conn) + struct iscsit_cmd *cmd, + struct iscsit_conn *conn) { struct iscsi_r2t *r2t; struct iscsi_r2t_rsp *hdr; @@ -3085,8 +3107,8 @@ static int iscsit_send_r2t( * connection recovery. */ int iscsit_build_r2ts_for_cmd( - struct iscsi_conn *conn, - struct iscsi_cmd *cmd, + struct iscsit_conn *conn, + struct iscsit_cmd *cmd, bool recovery) { int first_r2t = 1; @@ -3126,6 +3148,12 @@ int iscsit_build_r2ts_for_cmd( else xfer_len = conn->sess->sess_ops->MaxBurstLength; } + + if ((s32)xfer_len < 0) { + cmd->cmd_flags |= ICF_SENT_LAST_R2T; + break; + } + cmd->r2t_offset += xfer_len; if (cmd->r2t_offset == cmd->se_cmd.data_length) @@ -3162,7 +3190,7 @@ int iscsit_build_r2ts_for_cmd( } EXPORT_SYMBOL(iscsit_build_r2ts_for_cmd); -void iscsit_build_rsp_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +void iscsit_build_rsp_pdu(struct iscsit_cmd *cmd, struct iscsit_conn *conn, bool inc_stat_sn, struct iscsi_scsi_rsp *hdr) { if (inc_stat_sn) @@ -3196,7 +3224,7 @@ void iscsit_build_rsp_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn, } EXPORT_SYMBOL(iscsit_build_rsp_pdu); -static int iscsit_send_response(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +static int iscsit_send_response(struct iscsit_cmd *cmd, struct iscsit_conn *conn) { struct iscsi_scsi_rsp *hdr = (struct iscsi_scsi_rsp *)&cmd->pdu[0]; bool inc_stat_sn = (cmd->i_state == ISTATE_SEND_STATUS); @@ -3253,7 +3281,7 @@ static u8 iscsit_convert_tcm_tmr_rsp(struct se_tmr_req *se_tmr) } void -iscsit_build_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +iscsit_build_task_mgt_rsp(struct iscsit_cmd *cmd, struct iscsit_conn *conn, struct iscsi_tm_rsp *hdr) { struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; @@ -3276,7 +3304,7 @@ iscsit_build_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, EXPORT_SYMBOL(iscsit_build_task_mgt_rsp); static int -iscsit_send_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +iscsit_send_task_mgt_rsp(struct iscsit_cmd *cmd, struct iscsit_conn *conn) { struct iscsi_tm_rsp *hdr = (struct iscsi_tm_rsp *)&cmd->pdu[0]; @@ -3288,12 +3316,12 @@ iscsit_send_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn) #define SENDTARGETS_BUF_LIMIT 32768U static int -iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, +iscsit_build_sendtargets_response(struct iscsit_cmd *cmd, enum iscsit_transport_type network_transport, int skip_bytes, bool *completed) { char *payload = NULL; - struct iscsi_conn *conn = cmd->conn; + struct iscsit_conn *conn = cmd->conn; struct iscsi_portal_group *tpg; struct iscsi_tiqn *tiqn; struct iscsi_tpg_np *tpg_np; @@ -3391,7 +3419,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, } } - if (inet_addr_is_any((struct sockaddr *)&np->np_sockaddr)) + if (inet_addr_is_any(&np->np_sockaddr)) sockaddr = &conn->local_sockaddr; else sockaddr = &np->np_sockaddr; @@ -3438,7 +3466,7 @@ eob: } int -iscsit_build_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +iscsit_build_text_rsp(struct iscsit_cmd *cmd, struct iscsit_conn *conn, struct iscsi_text_rsp *hdr, enum iscsit_transport_type network_transport) { @@ -3488,8 +3516,8 @@ iscsit_build_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, EXPORT_SYMBOL(iscsit_build_text_rsp); static int iscsit_send_text_rsp( - struct iscsi_cmd *cmd, - struct iscsi_conn *conn) + struct iscsit_cmd *cmd, + struct iscsit_conn *conn) { struct iscsi_text_rsp *hdr = (struct iscsi_text_rsp *)cmd->pdu; int text_length; @@ -3505,7 +3533,7 @@ static int iscsit_send_text_rsp( } void -iscsit_build_reject(struct iscsi_cmd *cmd, struct iscsi_conn *conn, +iscsit_build_reject(struct iscsit_cmd *cmd, struct iscsit_conn *conn, struct iscsi_reject *hdr) { hdr->opcode = ISCSI_OP_REJECT; @@ -3522,8 +3550,8 @@ iscsit_build_reject(struct iscsi_cmd *cmd, struct iscsi_conn *conn, EXPORT_SYMBOL(iscsit_build_reject); static int iscsit_send_reject( - struct iscsi_cmd *cmd, - struct iscsi_conn *conn) + struct iscsit_cmd *cmd, + struct iscsit_conn *conn) { struct iscsi_reject *hdr = (struct iscsi_reject *)&cmd->pdu[0]; @@ -3537,9 +3565,11 @@ static int iscsit_send_reject( ISCSI_HDR_LEN); } -void iscsit_thread_get_cpumask(struct iscsi_conn *conn) +void iscsit_thread_get_cpumask(struct iscsit_conn *conn) { int ord, cpu; + cpumask_var_t conn_allowed_cpumask; + /* * bitmap_id is assigned from iscsit_global->ts_bitmap from * within iscsit_start_kthreads() @@ -3548,12 +3578,28 @@ void iscsit_thread_get_cpumask(struct iscsi_conn *conn) * iSCSI connection's RX/TX threads will be scheduled to * execute upon. */ - ord = conn->bitmap_id % cpumask_weight(cpu_online_mask); - for_each_online_cpu(cpu) { - if (ord-- == 0) { - cpumask_set_cpu(cpu, conn->conn_cpumask); - return; + if (!zalloc_cpumask_var(&conn_allowed_cpumask, GFP_KERNEL)) { + ord = conn->bitmap_id % cpumask_weight(cpu_online_mask); + for_each_online_cpu(cpu) { + if (ord-- == 0) { + cpumask_set_cpu(cpu, conn->conn_cpumask); + return; + } } + } else { + cpumask_and(conn_allowed_cpumask, iscsit_global->allowed_cpumask, + cpu_online_mask); + + cpumask_clear(conn->conn_cpumask); + ord = conn->bitmap_id % cpumask_weight(conn_allowed_cpumask); + for_each_cpu(cpu, conn_allowed_cpumask) { + if (ord-- == 0) { + cpumask_set_cpu(cpu, conn->conn_cpumask); + free_cpumask_var(conn_allowed_cpumask); + return; + } + } + free_cpumask_var(conn_allowed_cpumask); } /* * This should never be reached.. @@ -3562,8 +3608,64 @@ void iscsit_thread_get_cpumask(struct iscsi_conn *conn) cpumask_setall(conn->conn_cpumask); } +static void iscsit_thread_reschedule(struct iscsit_conn *conn) +{ + /* + * If iscsit_global->allowed_cpumask modified, reschedule iSCSI + * connection's RX/TX threads update conn->allowed_cpumask. + */ + if (!cpumask_equal(iscsit_global->allowed_cpumask, + conn->allowed_cpumask)) { + iscsit_thread_get_cpumask(conn); + conn->conn_tx_reset_cpumask = 1; + conn->conn_rx_reset_cpumask = 1; + cpumask_copy(conn->allowed_cpumask, + iscsit_global->allowed_cpumask); + } +} + +void iscsit_thread_check_cpumask( + struct iscsit_conn *conn, + struct task_struct *p, + int mode) +{ + /* + * The TX and RX threads maybe call iscsit_thread_check_cpumask() + * at the same time. The RX thread might be faster and return from + * iscsit_thread_reschedule() with conn_rx_reset_cpumask set to 0. + * Then the TX thread sets it back to 1. + * The next time the RX thread loops, it sees conn_rx_reset_cpumask + * set to 1 and calls set_cpus_allowed_ptr() again and set it to 0. + */ + iscsit_thread_reschedule(conn); + + /* + * mode == 1 signals iscsi_target_tx_thread() usage. + * mode == 0 signals iscsi_target_rx_thread() usage. + */ + if (mode == 1) { + if (!conn->conn_tx_reset_cpumask) + return; + } else { + if (!conn->conn_rx_reset_cpumask) + return; + } + + /* + * Update the CPU mask for this single kthread so that + * both TX and RX kthreads are scheduled to run on the + * same CPU. + */ + set_cpus_allowed_ptr(p, conn->conn_cpumask); + if (mode == 1) + conn->conn_tx_reset_cpumask = 0; + else + conn->conn_rx_reset_cpumask = 0; +} +EXPORT_SYMBOL(iscsit_thread_check_cpumask); + int -iscsit_immediate_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state) +iscsit_immediate_queue(struct iscsit_conn *conn, struct iscsit_cmd *cmd, int state) { int ret; @@ -3607,11 +3709,11 @@ err: EXPORT_SYMBOL(iscsit_immediate_queue); static int -iscsit_handle_immediate_queue(struct iscsi_conn *conn) +iscsit_handle_immediate_queue(struct iscsit_conn *conn) { struct iscsit_transport *t = conn->conn_transport; struct iscsi_queue_req *qr; - struct iscsi_cmd *cmd; + struct iscsit_cmd *cmd; u8 state; int ret; @@ -3630,7 +3732,7 @@ iscsit_handle_immediate_queue(struct iscsi_conn *conn) } int -iscsit_response_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state) +iscsit_response_queue(struct iscsit_conn *conn, struct iscsit_cmd *cmd, int state) { int ret; @@ -3706,7 +3808,7 @@ check_rsp_state: case ISTATE_SEND_LOGOUTRSP: if (!iscsit_logout_post_handler(cmd, conn)) return -ECONNRESET; - /* fall through */ + fallthrough; case ISTATE_SEND_STATUS: case ISTATE_SEND_ASYNCMSG: case ISTATE_SEND_NOPIN: @@ -3736,11 +3838,11 @@ err: } EXPORT_SYMBOL(iscsit_response_queue); -static int iscsit_handle_response_queue(struct iscsi_conn *conn) +static int iscsit_handle_response_queue(struct iscsit_conn *conn) { struct iscsit_transport *t = conn->conn_transport; struct iscsi_queue_req *qr; - struct iscsi_cmd *cmd; + struct iscsit_cmd *cmd; u8 state; int ret; @@ -3760,7 +3862,7 @@ static int iscsit_handle_response_queue(struct iscsi_conn *conn) int iscsi_target_tx_thread(void *arg) { int ret = 0; - struct iscsi_conn *conn = arg; + struct iscsit_conn *conn = arg; bool conn_freed = false; /* @@ -3815,10 +3917,10 @@ out: return 0; } -static int iscsi_target_rx_opcode(struct iscsi_conn *conn, unsigned char *buf) +static int iscsi_target_rx_opcode(struct iscsit_conn *conn, unsigned char *buf) { struct iscsi_hdr *hdr = (struct iscsi_hdr *)buf; - struct iscsi_cmd *cmd; + struct iscsit_cmd *cmd; int ret = 0; switch (hdr->opcode & ISCSI_OPCODE_MASK) { @@ -3892,7 +3994,7 @@ reject: return iscsit_add_reject(conn, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); } -static bool iscsi_target_check_conn_state(struct iscsi_conn *conn) +static bool iscsi_target_check_conn_state(struct iscsit_conn *conn) { bool ret; @@ -3903,11 +4005,12 @@ static bool iscsi_target_check_conn_state(struct iscsi_conn *conn) return ret; } -static void iscsit_get_rx_pdu(struct iscsi_conn *conn) +static void iscsit_get_rx_pdu(struct iscsit_conn *conn) { int ret; - u8 *buffer, opcode; + u8 *buffer, *tmp_buf, opcode; u32 checksum = 0, digest = 0; + struct iscsi_hdr *hdr; struct kvec iov; buffer = kcalloc(ISCSI_HDR_LEN, sizeof(*buffer), GFP_KERNEL); @@ -3932,6 +4035,25 @@ static void iscsit_get_rx_pdu(struct iscsi_conn *conn) break; } + hdr = (struct iscsi_hdr *) buffer; + if (hdr->hlength) { + iov.iov_len = hdr->hlength * 4; + tmp_buf = krealloc(buffer, + ISCSI_HDR_LEN + iov.iov_len, + GFP_KERNEL); + if (!tmp_buf) + break; + + buffer = tmp_buf; + iov.iov_base = &buffer[ISCSI_HDR_LEN]; + + ret = rx_data(conn, &iov, 1, iov.iov_len); + if (ret != iov.iov_len) { + iscsit_rx_thread_wait_for_tcp(conn); + break; + } + } + if (conn->conn_ops->HeaderDigest) { iov.iov_base = &digest; iov.iov_len = ISCSI_CRC_LEN; @@ -3942,10 +4064,8 @@ static void iscsit_get_rx_pdu(struct iscsi_conn *conn) break; } - iscsit_do_crypto_hash_buf(conn->conn_rx_hash, buffer, - ISCSI_HDR_LEN, 0, NULL, - &checksum); - + checksum = iscsit_crc_buf(buffer, ISCSI_HDR_LEN, 0, + NULL); if (digest != checksum) { pr_err("HeaderDigest CRC32C failed," " received 0x%08x, computed 0x%08x\n", @@ -3988,7 +4108,7 @@ static void iscsit_get_rx_pdu(struct iscsi_conn *conn) int iscsi_target_rx_thread(void *arg) { int rc; - struct iscsi_conn *conn = arg; + struct iscsit_conn *conn = arg; bool conn_freed = false; /* @@ -4023,11 +4143,11 @@ out: return 0; } -static void iscsit_release_commands_from_conn(struct iscsi_conn *conn) +static void iscsit_release_commands_from_conn(struct iscsit_conn *conn) { LIST_HEAD(tmp_list); - struct iscsi_cmd *cmd = NULL, *cmd_tmp = NULL; - struct iscsi_session *sess = conn->sess; + struct iscsit_cmd *cmd = NULL, *cmd_tmp = NULL; + struct iscsit_session *sess = conn->sess; /* * We expect this function to only ever be called from either RX or TX * thread context via iscsit_close_connection() once the other context @@ -4036,14 +4156,36 @@ static void iscsit_release_commands_from_conn(struct iscsi_conn *conn) spin_lock_bh(&conn->cmd_lock); list_splice_init(&conn->conn_cmd_list, &tmp_list); - list_for_each_entry(cmd, &tmp_list, i_conn_node) { + list_for_each_entry_safe(cmd, cmd_tmp, &tmp_list, i_conn_node) { struct se_cmd *se_cmd = &cmd->se_cmd; - if (se_cmd->se_tfo != NULL) { - spin_lock(&se_cmd->t_state_lock); + if (!se_cmd->se_tfo) + continue; + + spin_lock_irq(&se_cmd->t_state_lock); + if (se_cmd->transport_state & CMD_T_ABORTED) { + if (!(se_cmd->transport_state & CMD_T_TAS)) + /* + * LIO's abort path owns the cleanup for this, + * so put it back on the list and let + * aborted_task handle it. + */ + list_move_tail(&cmd->i_conn_node, + &conn->conn_cmd_list); + } else { se_cmd->transport_state |= CMD_T_FABRIC_STOP; - spin_unlock(&se_cmd->t_state_lock); } + + if (cmd->se_cmd.t_state == TRANSPORT_WRITE_PENDING) { + /* + * We never submitted the cmd to LIO core, so we have + * to tell LIO to perform the completion process. + */ + spin_unlock_irq(&se_cmd->t_state_lock); + target_complete_cmd(&cmd->se_cmd, SAM_STAT_TASK_ABORTED); + continue; + } + spin_unlock_irq(&se_cmd->t_state_lock); } spin_unlock_bh(&conn->cmd_lock); @@ -4054,12 +4196,22 @@ static void iscsit_release_commands_from_conn(struct iscsi_conn *conn) iscsit_free_cmd(cmd, true); } + + /* + * Wait on commands that were cleaned up via the aborted_task path. + * LLDs that implement iscsit_wait_conn will already have waited for + * commands. + */ + if (!conn->conn_transport->iscsit_wait_conn) { + target_stop_cmd_counter(conn->cmd_cnt); + target_wait_for_cmds(conn->cmd_cnt); + } } static void iscsit_stop_timers_for_cmds( - struct iscsi_conn *conn) + struct iscsit_conn *conn) { - struct iscsi_cmd *cmd; + struct iscsit_cmd *cmd; spin_lock_bh(&conn->cmd_lock); list_for_each_entry(cmd, &conn->conn_cmd_list, i_conn_node) { @@ -4070,10 +4222,10 @@ static void iscsit_stop_timers_for_cmds( } int iscsit_close_connection( - struct iscsi_conn *conn) + struct iscsit_conn *conn) { int conn_logout = (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT); - struct iscsi_session *sess = conn->sess; + struct iscsit_session *sess = conn->sess; pr_debug("Closing iSCSI connection CID %hu on SID:" " %u\n", conn->cid, sess->sid); @@ -4086,7 +4238,7 @@ int iscsit_close_connection( * However for iser-target, isert_wait4logout() is using conn_logout_comp * to signal logout response TX interrupt completion. Go ahead and skip * this for iser since isert_rx_opcode() does not wait on logout failure, - * and to avoid iscsi_conn pointer dereference in iser-target code. + * and to avoid iscsit_conn pointer dereference in iser-target code. */ if (!conn->conn_transport->rdma_shutdown) complete(&conn->conn_logout_comp); @@ -4111,8 +4263,8 @@ int iscsit_close_connection( spin_unlock(&iscsit_global->ts_bitmap_lock); iscsit_stop_timers_for_cmds(conn); - iscsit_stop_nopin_response_timer(conn); iscsit_stop_nopin_timer(conn); + iscsit_stop_nopin_response_timer(conn); if (conn->conn_transport->iscsit_wait_conn) conn->conn_transport->iscsit_wait_conn(conn); @@ -4124,7 +4276,7 @@ int iscsit_close_connection( * * During normal operation clear the out of order commands (but * do not free the struct iscsi_ooo_cmdsn's) and release all - * struct iscsi_cmds. + * struct iscsit_cmds. */ if (atomic_read(&conn->connection_recovery)) { iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(conn); @@ -4200,15 +4352,6 @@ int iscsit_close_connection( */ iscsit_check_conn_usage_count(conn); - ahash_request_free(conn->conn_tx_hash); - if (conn->conn_rx_hash) { - struct crypto_ahash *tfm; - - tfm = crypto_ahash_reqtfm(conn->conn_rx_hash); - ahash_request_free(conn->conn_rx_hash); - crypto_free_ahash(tfm); - } - if (conn->sock) sock_release(conn->sock); @@ -4221,7 +4364,7 @@ int iscsit_close_connection( spin_lock_bh(&sess->conn_lock); atomic_dec(&sess->nconn); - pr_debug("Decremented iSCSI connection count to %hu from node:" + pr_debug("Decremented iSCSI connection count to %d from node:" " %s\n", atomic_read(&sess->nconn), sess->sess_ops->InitiatorName); /* @@ -4270,30 +4413,37 @@ int iscsit_close_connection( if (!atomic_read(&sess->session_reinstatement) && atomic_read(&sess->session_fall_back_to_erl0)) { spin_unlock_bh(&sess->conn_lock); - iscsit_close_session(sess); + complete_all(&sess->session_wait_comp); + iscsit_close_session(sess, true); return 0; } else if (atomic_read(&sess->session_logout)) { pr_debug("Moving to TARG_SESS_STATE_FREE.\n"); sess->session_state = TARG_SESS_STATE_FREE; - spin_unlock_bh(&sess->conn_lock); - if (atomic_read(&sess->sleep_on_sess_wait_comp)) - complete(&sess->session_wait_comp); + if (atomic_read(&sess->session_close)) { + spin_unlock_bh(&sess->conn_lock); + complete_all(&sess->session_wait_comp); + iscsit_close_session(sess, true); + } else { + spin_unlock_bh(&sess->conn_lock); + } return 0; } else { pr_debug("Moving to TARG_SESS_STATE_FAILED.\n"); sess->session_state = TARG_SESS_STATE_FAILED; - if (!atomic_read(&sess->session_continuation)) { - spin_unlock_bh(&sess->conn_lock); + if (!atomic_read(&sess->session_continuation)) iscsit_start_time2retain_handler(sess); - } else - spin_unlock_bh(&sess->conn_lock); - if (atomic_read(&sess->sleep_on_sess_wait_comp)) - complete(&sess->session_wait_comp); + if (atomic_read(&sess->session_close)) { + spin_unlock_bh(&sess->conn_lock); + complete_all(&sess->session_wait_comp); + iscsit_close_session(sess, true); + } else { + spin_unlock_bh(&sess->conn_lock); + } return 0; } @@ -4303,7 +4453,7 @@ int iscsit_close_connection( * If the iSCSI Session for the iSCSI Initiator Node exists, * forcefully shutdown the iSCSI NEXUS. */ -int iscsit_close_session(struct iscsi_session *sess) +int iscsit_close_session(struct iscsit_session *sess, bool can_sleep) { struct iscsi_portal_group *tpg = sess->tpg; struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; @@ -4321,6 +4471,9 @@ int iscsit_close_session(struct iscsi_session *sess) iscsit_stop_time2retain_timer(sess); spin_unlock_bh(&se_tpg->session_lock); + if (sess->sess_ops->ErrorRecoveryLevel == 2) + iscsit_free_connection_recovery_entries(sess); + /* * transport_deregister_session_configfs() will clear the * struct se_node_acl->nacl_sess pointer now as a iscsi_np process context @@ -4336,22 +4489,14 @@ int iscsit_close_session(struct iscsi_session *sess) * time2retain handler) and contain and active session usage count we * restart the timer and exit. */ - if (!in_interrupt()) { - if (iscsit_check_session_usage_count(sess) == 1) - iscsit_stop_session(sess, 1, 1); - } else { - if (iscsit_check_session_usage_count(sess) == 2) { - atomic_set(&sess->session_logout, 0); - iscsit_start_time2retain_handler(sess); - return 0; - } + if (iscsit_check_session_usage_count(sess, can_sleep)) { + atomic_set(&sess->session_logout, 0); + iscsit_start_time2retain_handler(sess); + return 0; } transport_deregister_session(sess->se_sess); - if (sess->sess_ops->ErrorRecoveryLevel == 2) - iscsit_free_connection_recovery_entries(sess); - iscsit_free_all_ooo_cmdsns(sess); spin_lock_bh(&se_tpg->session_lock); @@ -4376,9 +4521,9 @@ int iscsit_close_session(struct iscsi_session *sess) } static void iscsit_logout_post_handler_closesession( - struct iscsi_conn *conn) + struct iscsit_conn *conn) { - struct iscsi_session *sess = conn->sess; + struct iscsit_session *sess = conn->sess; int sleep = 1; /* * Traditional iscsi/tcp will invoke this logic from TX thread @@ -4399,13 +4544,13 @@ static void iscsit_logout_post_handler_closesession( complete(&conn->conn_logout_comp); iscsit_dec_conn_usage_count(conn); + atomic_set(&sess->session_close, 1); iscsit_stop_session(sess, sleep, sleep); iscsit_dec_session_usage_count(sess); - iscsit_close_session(sess); } static void iscsit_logout_post_handler_samecid( - struct iscsi_conn *conn) + struct iscsit_conn *conn) { int sleep = 1; @@ -4423,11 +4568,11 @@ static void iscsit_logout_post_handler_samecid( } static void iscsit_logout_post_handler_diffcid( - struct iscsi_conn *conn, + struct iscsit_conn *conn, u16 cid) { - struct iscsi_conn *l_conn; - struct iscsi_session *sess = conn->sess; + struct iscsit_conn *l_conn; + struct iscsit_session *sess = conn->sess; bool conn_found = false; if (!sess) @@ -4462,8 +4607,8 @@ static void iscsit_logout_post_handler_diffcid( * Return of 0 causes the TX thread to restart. */ int iscsit_logout_post_handler( - struct iscsi_cmd *cmd, - struct iscsi_conn *conn) + struct iscsit_cmd *cmd, + struct iscsit_conn *conn) { int ret = 0; @@ -4476,7 +4621,6 @@ int iscsit_logout_post_handler( iscsit_logout_post_handler_closesession(conn); break; } - ret = 0; break; case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION: if (conn->cid == cmd->logout_cid) { @@ -4487,7 +4631,6 @@ int iscsit_logout_post_handler( iscsit_logout_post_handler_samecid(conn); break; } - ret = 0; } else { switch (cmd->logout_response) { case ISCSI_LOGOUT_SUCCESS: @@ -4521,76 +4664,16 @@ int iscsit_logout_post_handler( } EXPORT_SYMBOL(iscsit_logout_post_handler); -void iscsit_fail_session(struct iscsi_session *sess) -{ - struct iscsi_conn *conn; - - spin_lock_bh(&sess->conn_lock); - list_for_each_entry(conn, &sess->sess_conn_list, conn_list) { - pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n"); - conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT; - } - spin_unlock_bh(&sess->conn_lock); - - pr_debug("Moving to TARG_SESS_STATE_FAILED.\n"); - sess->session_state = TARG_SESS_STATE_FAILED; -} - -int iscsit_free_session(struct iscsi_session *sess) -{ - u16 conn_count = atomic_read(&sess->nconn); - struct iscsi_conn *conn, *conn_tmp = NULL; - int is_last; - - spin_lock_bh(&sess->conn_lock); - atomic_set(&sess->sleep_on_sess_wait_comp, 1); - - list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list, - conn_list) { - if (conn_count == 0) - break; - - if (list_is_last(&conn->conn_list, &sess->sess_conn_list)) { - is_last = 1; - } else { - iscsit_inc_conn_usage_count(conn_tmp); - is_last = 0; - } - iscsit_inc_conn_usage_count(conn); - - spin_unlock_bh(&sess->conn_lock); - iscsit_cause_connection_reinstatement(conn, 1); - spin_lock_bh(&sess->conn_lock); - - iscsit_dec_conn_usage_count(conn); - if (is_last == 0) - iscsit_dec_conn_usage_count(conn_tmp); - - conn_count--; - } - - if (atomic_read(&sess->nconn)) { - spin_unlock_bh(&sess->conn_lock); - wait_for_completion(&sess->session_wait_comp); - } else - spin_unlock_bh(&sess->conn_lock); - - iscsit_close_session(sess); - return 0; -} - void iscsit_stop_session( - struct iscsi_session *sess, + struct iscsit_session *sess, int session_sleep, int connection_sleep) { u16 conn_count = atomic_read(&sess->nconn); - struct iscsi_conn *conn, *conn_tmp = NULL; + struct iscsit_conn *conn, *conn_tmp = NULL; int is_last; spin_lock_bh(&sess->conn_lock); - if (session_sleep) - atomic_set(&sess->sleep_on_sess_wait_comp, 1); if (connection_sleep) { list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list, @@ -4629,7 +4712,7 @@ void iscsit_stop_session( int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force) { - struct iscsi_session *sess; + struct iscsit_session *sess; struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; struct se_session *se_sess, *se_sess_tmp; LIST_HEAD(free_list); @@ -4643,17 +4726,20 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force) list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list, sess_list) { - sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; + sess = (struct iscsit_session *)se_sess->fabric_sess_ptr; spin_lock(&sess->conn_lock); if (atomic_read(&sess->session_fall_back_to_erl0) || atomic_read(&sess->session_logout) || + atomic_read(&sess->session_close) || (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) { spin_unlock(&sess->conn_lock); continue; } + iscsit_inc_session_usage_count(sess); atomic_set(&sess->session_reinstatement, 1); atomic_set(&sess->session_fall_back_to_erl0, 1); + atomic_set(&sess->session_close, 1); spin_unlock(&sess->conn_lock); list_move_tail(&se_sess->sess_list, &free_list); @@ -4661,9 +4747,11 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force) spin_unlock_bh(&se_tpg->session_lock); list_for_each_entry_safe(se_sess, se_sess_tmp, &free_list, sess_list) { - sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; + sess = (struct iscsit_session *)se_sess->fabric_sess_ptr; - iscsit_free_session(sess); + list_del_init(&se_sess->sess_list); + iscsit_stop_session(sess, 1, 1); + iscsit_dec_session_usage_count(sess); session_count++; } |
