From bcac364a24c894c4cf8cf219b7863c192cd34079 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Sat, 27 Aug 2011 21:33:16 -0700 Subject: [PATCH 1/6] target: Fix race between multiple invocations of target_qf_do_work() When work is scheduled with schedule_work(), the work can end up running on multiple CPUs at the same time -- this happens if the work is already running on one CPU and schedule_work() is called on another CPU. This leads to list corruption with target_qf_do_work(), which is roughly doing: spin_lock(...); list_for_each_entry_safe(...) { list_del(...); spin_unlock(...); // do stuff spin_lock(...); } With multiple CPUs running this code, one CPU can end up deleting the list entry that the other CPU is about to work on. Fix this by splicing the list entries onto a local list and then operating on that in the work function. This way, each invocation of target_qf_do_work() operates on its own local list and so multiple invocations don't corrupt each other's list. This also avoids dropping and reacquiring the lock for each list entry. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 8d0c58ea6316..a4b0a8d27f25 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -977,15 +977,17 @@ static void target_qf_do_work(struct work_struct *work) { struct se_device *dev = container_of(work, struct se_device, qf_work_queue); + LIST_HEAD(qf_cmd_list); struct se_cmd *cmd, *cmd_tmp; spin_lock_irq(&dev->qf_cmd_lock); - list_for_each_entry_safe(cmd, cmd_tmp, &dev->qf_cmd_list, se_qf_node) { + list_splice_init(&dev->qf_cmd_list, &qf_cmd_list); + spin_unlock_irq(&dev->qf_cmd_lock); + list_for_each_entry_safe(cmd, cmd_tmp, &qf_cmd_list, se_qf_node) { list_del(&cmd->se_qf_node); atomic_dec(&dev->dev_qf_count); smp_mb__after_atomic_dec(); - spin_unlock_irq(&dev->qf_cmd_lock); pr_debug("Processing %s cmd: %p QUEUE_FULL in work queue" " context: %s\n", cmd->se_tfo->get_fabric_name(), cmd, @@ -997,10 +999,7 @@ static void target_qf_do_work(struct work_struct *work) * has been added to head of queue */ transport_add_cmd_to_queue(cmd, cmd->t_state); - - spin_lock_irq(&dev->qf_cmd_lock); } - spin_unlock_irq(&dev->qf_cmd_lock); } unsigned char *transport_dump_cmd_direction(struct se_cmd *cmd) From 079587b4eb4d3b78a4d65d142f662aa9d7eedab4 Mon Sep 17 00:00:00 2001 From: Kiran Patil Date: Fri, 26 Aug 2011 09:25:25 -0700 Subject: [PATCH 2/6] tcm_fc: Invalidation of DDP context for FCoE target in error conditions Problem: HW DDP context wasn;t invalidated in case of ABORTS, etc... This leads to the problem where memory pages which are used for DDP as user descriptor could get reused for some other purpose (such as to satisfy new memory allocation request either by kernel or user mode threads) and since HW DDP context was not invalidated, HW continue to write to those pages, hence causing memory corruption. Fix: Either on incoming ABORTS or due to exchange time out, allowed the target to cleanup HW DDP context if it was setup for respective ft_cmd. Added new function to perform this cleanup, furthur it can be enhanced for other cleanup activity. Additinal Notes: To avoid calling ddp_done from multiple places, composed the functionality in helper function "ft_invl_hw_context" and it is being called from multiple places. Cleaned up code in function "ft_recv_write_data" w.r.t DDP. Signed-off-by: Kiran Patil Signed-off-by: Nicholas Bellinger --- drivers/target/tcm_fc/tfc_io.c | 62 ++++++++++++++++------------------ 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c index c37f4cd96452..d35ea5a3d56c 100644 --- a/drivers/target/tcm_fc/tfc_io.c +++ b/drivers/target/tcm_fc/tfc_io.c @@ -219,43 +219,41 @@ void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *fp) if (cmd->was_ddp_setup) { BUG_ON(!ep); BUG_ON(!lport); - } - - /* - * Doesn't expect payload if DDP is setup. Payload - * is expected to be copied directly to user buffers - * due to DDP (Large Rx offload), - */ - buf = fc_frame_payload_get(fp, 1); - if (buf) - pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, " + /* + * Since DDP (Large Rx offload) was setup for this request, + * payload is expected to be copied directly to user buffers. + */ + buf = fc_frame_payload_get(fp, 1); + if (buf) + pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, " "cmd->sg_cnt 0x%x. DDP was setup" " hence not expected to receive frame with " - "payload, Frame will be dropped if " - "'Sequence Initiative' bit in f_ctl is " + "payload, Frame will be dropped if" + "'Sequence Initiative' bit in f_ctl is" "not set\n", __func__, ep->xid, f_ctl, cmd->sg, cmd->sg_cnt); - /* - * Invalidate HW DDP context if it was setup for respective - * command. Invalidation of HW DDP context is requited in both - * situation (success and error). - */ - ft_invl_hw_context(cmd); + /* + * Invalidate HW DDP context if it was setup for respective + * command. Invalidation of HW DDP context is requited in both + * situation (success and error). + */ + ft_invl_hw_context(cmd); - /* - * If "Sequence Initiative (TSI)" bit set in f_ctl, means last - * write data frame is received successfully where payload is - * posted directly to user buffer and only the last frame's - * header is posted in receive queue. - * - * If "Sequence Initiative (TSI)" bit is not set, means error - * condition w.r.t. DDP, hence drop the packet and let explict - * ABORTS from other end of exchange timer trigger the recovery. - */ - if (f_ctl & FC_FC_SEQ_INIT) - goto last_frame; - else - goto drop; + /* + * If "Sequence Initiative (TSI)" bit set in f_ctl, means last + * write data frame is received successfully where payload is + * posted directly to user buffer and only the last frame's + * header is posted in receive queue. + * + * If "Sequence Initiative (TSI)" bit is not set, means error + * condition w.r.t. DDP, hence drop the packet and let explict + * ABORTS from other end of exchange timer trigger the recovery. + */ + if (f_ctl & FC_FC_SEQ_INIT) + goto last_frame; + else + goto drop; + } rel_off = ntohl(fh->fh_parm_offset); frame_len = fr_len(fp); From 58fc73d10f3e92bfcd1e9a8391eb3e49b68df8e5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 26 Aug 2011 09:25:38 -0700 Subject: [PATCH 3/6] tcm_fc: Work queue based approach instead of managing own thread and event based mechanism Problem: Changed from wake_up_interruptible -> wake_up_process and wait_event_interruptible-> schedule_timeout_interruptible broke the FCoE target. Earlier approach of wake_up_interruptible was also looking at 'queue_cnt' which is not necessary, because it increment of 'queue_cnt' with wake_up_inetrriptible / waker_up_process introduces race condition. Fix: Instead of fixing the code which used wake_up_process and remove 'queue_cnt', using work_queue based approach is cleaner and acheives same result. As well, work queue based approach has less programming overhead and OS manages threads which processes work queues. This patch is developed by Christoph Hellwig and reviwed+validated by Kiran Patil. Signed-off-by: Christoph Hellwig Signed-off-by: Kiran Patil Signed-off-by: Nicholas Bellinger --- drivers/target/tcm_fc/tcm_fc.h | 12 +---- drivers/target/tcm_fc/tfc_cmd.c | 90 ++++---------------------------- drivers/target/tcm_fc/tfc_conf.c | 7 ++- 3 files changed, 16 insertions(+), 93 deletions(-) diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h index bd4fe21a23b8..3749d8b4b423 100644 --- a/drivers/target/tcm_fc/tcm_fc.h +++ b/drivers/target/tcm_fc/tcm_fc.h @@ -98,8 +98,7 @@ struct ft_tpg { struct list_head list; /* linkage in ft_lport_acl tpg_list */ struct list_head lun_list; /* head of LUNs */ struct se_portal_group se_tpg; - struct task_struct *thread; /* processing thread */ - struct se_queue_obj qobj; /* queue for processing thread */ + struct workqueue_struct *workqueue; }; struct ft_lport_acl { @@ -110,16 +109,10 @@ struct ft_lport_acl { struct se_wwn fc_lport_wwn; }; -enum ft_cmd_state { - FC_CMD_ST_NEW = 0, - FC_CMD_ST_REJ -}; - /* * Commands */ struct ft_cmd { - enum ft_cmd_state state; u32 lun; /* LUN from request */ struct ft_sess *sess; /* session held for cmd */ struct fc_seq *seq; /* sequence in exchange mgr */ @@ -127,7 +120,7 @@ struct ft_cmd { struct fc_frame *req_frame; unsigned char *cdb; /* pointer to CDB inside frame */ u32 write_data_len; /* data received on writes */ - struct se_queue_req se_req; + struct work_struct work; /* Local sense buffer */ unsigned char ft_sense_buffer[TRANSPORT_SENSE_BUFFER]; u32 was_ddp_setup:1; /* Set only if ddp is setup */ @@ -177,7 +170,6 @@ int ft_is_state_remove(struct se_cmd *); /* * other internal functions. */ -int ft_thread(void *); void ft_recv_req(struct ft_sess *, struct fc_frame *); struct ft_tpg *ft_lport_find_tpg(struct fc_lport *); struct ft_node_acl *ft_acl_get(struct ft_tpg *, struct fc_rport_priv *); diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index 5654dc22f7ae..80fbcde00cb6 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -62,8 +62,8 @@ void ft_dump_cmd(struct ft_cmd *cmd, const char *caller) int count; se_cmd = &cmd->se_cmd; - pr_debug("%s: cmd %p state %d sess %p seq %p se_cmd %p\n", - caller, cmd, cmd->state, cmd->sess, cmd->seq, se_cmd); + pr_debug("%s: cmd %p sess %p seq %p se_cmd %p\n", + caller, cmd, cmd->sess, cmd->seq, se_cmd); pr_debug("%s: cmd %p cdb %p\n", caller, cmd, cmd->cdb); pr_debug("%s: cmd %p lun %d\n", caller, cmd, cmd->lun); @@ -90,38 +90,6 @@ void ft_dump_cmd(struct ft_cmd *cmd, const char *caller) 16, 4, cmd->cdb, MAX_COMMAND_SIZE, 0); } -static void ft_queue_cmd(struct ft_sess *sess, struct ft_cmd *cmd) -{ - struct ft_tpg *tpg = sess->tport->tpg; - struct se_queue_obj *qobj = &tpg->qobj; - unsigned long flags; - - qobj = &sess->tport->tpg->qobj; - spin_lock_irqsave(&qobj->cmd_queue_lock, flags); - list_add_tail(&cmd->se_req.qr_list, &qobj->qobj_list); - atomic_inc(&qobj->queue_cnt); - spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); - - wake_up_process(tpg->thread); -} - -static struct ft_cmd *ft_dequeue_cmd(struct se_queue_obj *qobj) -{ - unsigned long flags; - struct se_queue_req *qr; - - spin_lock_irqsave(&qobj->cmd_queue_lock, flags); - if (list_empty(&qobj->qobj_list)) { - spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); - return NULL; - } - qr = list_first_entry(&qobj->qobj_list, struct se_queue_req, qr_list); - list_del(&qr->qr_list); - atomic_dec(&qobj->queue_cnt); - spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); - return container_of(qr, struct ft_cmd, se_req); -} - static void ft_free_cmd(struct ft_cmd *cmd) { struct fc_frame *fp; @@ -282,9 +250,7 @@ u32 ft_get_task_tag(struct se_cmd *se_cmd) int ft_get_cmd_state(struct se_cmd *se_cmd) { - struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); - - return cmd->state; + return 0; } int ft_is_state_remove(struct se_cmd *se_cmd) @@ -505,6 +471,8 @@ int ft_queue_tm_resp(struct se_cmd *se_cmd) return 0; } +static void ft_send_work(struct work_struct *work); + /* * Handle incoming FCP command. */ @@ -523,7 +491,9 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp) goto busy; } cmd->req_frame = fp; /* hold frame during cmd */ - ft_queue_cmd(sess, cmd); + + INIT_WORK(&cmd->work, ft_send_work); + queue_work(sess->tport->tpg->workqueue, &cmd->work); return; busy: @@ -563,12 +533,13 @@ void ft_recv_req(struct ft_sess *sess, struct fc_frame *fp) /* * Send new command to target. */ -static void ft_send_cmd(struct ft_cmd *cmd) +static void ft_send_work(struct work_struct *work) { + struct ft_cmd *cmd = container_of(work, struct ft_cmd, work); struct fc_frame_header *fh = fc_frame_header_get(cmd->req_frame); struct se_cmd *se_cmd; struct fcp_cmnd *fcp; - int data_dir; + int data_dir = 0; u32 data_len; int task_attr; int ret; @@ -675,42 +646,3 @@ static void ft_send_cmd(struct ft_cmd *cmd) err: ft_send_resp_code_and_free(cmd, FCP_CMND_FIELDS_INVALID); } - -/* - * Handle request in the command thread. - */ -static void ft_exec_req(struct ft_cmd *cmd) -{ - pr_debug("cmd state %x\n", cmd->state); - switch (cmd->state) { - case FC_CMD_ST_NEW: - ft_send_cmd(cmd); - break; - default: - break; - } -} - -/* - * Processing thread. - * Currently one thread per tpg. - */ -int ft_thread(void *arg) -{ - struct ft_tpg *tpg = arg; - struct se_queue_obj *qobj = &tpg->qobj; - struct ft_cmd *cmd; - - while (!kthread_should_stop()) { - schedule_timeout_interruptible(MAX_SCHEDULE_TIMEOUT); - if (kthread_should_stop()) - goto out; - - cmd = ft_dequeue_cmd(qobj); - if (cmd) - ft_exec_req(cmd); - } - -out: - return 0; -} diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index b15879d43e22..8fa39b74f22c 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -327,7 +327,6 @@ static struct se_portal_group *ft_add_tpg( tpg->index = index; tpg->lport_acl = lacl; INIT_LIST_HEAD(&tpg->lun_list); - transport_init_queue_obj(&tpg->qobj); ret = core_tpg_register(&ft_configfs->tf_ops, wwn, &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL); @@ -336,8 +335,8 @@ static struct se_portal_group *ft_add_tpg( return NULL; } - tpg->thread = kthread_run(ft_thread, tpg, "ft_tpg%lu", index); - if (IS_ERR(tpg->thread)) { + tpg->workqueue = alloc_workqueue("tcm_fc", 0, 1); + if (!tpg->workqueue) { kfree(tpg); return NULL; } @@ -356,7 +355,7 @@ static void ft_del_tpg(struct se_portal_group *se_tpg) pr_debug("del tpg %s\n", config_item_name(&tpg->se_tpg.tpg_group.cg_item)); - kthread_stop(tpg->thread); + destroy_workqueue(tpg->workqueue); /* Wait for sessions to be freed thru RCU, for BUG_ON below */ synchronize_rcu(); From 784eb99ebad91db4c8c231c4b17f203147ab827b Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 16 Sep 2011 01:31:28 -0700 Subject: [PATCH 4/6] target: Skip non hex characters for VPD=0x83 NAA IEEE Registered Extended This patch adds target_parse_naa_6h_vendor_specific() to address a bug where the conversion of PRODUCT SERIAL NUMBER to use hex2bin() in target_emulate_evpd_83() was not doing proper isxdigit() checking. This conversion of the vpd_unit_serial configifs attribute is done while generating a VPD=0x83 NAA IEEE Registered Extended DESIGNATOR format's 100 bits of unique VENDOR SPECIFIC IDENTIFIER + VENDOR SPECIFIC IDENTIFIER EXTENSION area. This patch allows vpd_unit_serial (VPD=0x80) and the T10 Vendor ID DESIGNATOR format (VPD=0x83) to continue to use free-form variable length ASCII values, and now skips any non hex characters for fixed length NAA IEEE Registered Extended DESIGNATOR format (VPD=0x83) requring the binary conversion. This was originally reported by Martin after the v3.1-rc1 change to use hex2bin() in commit 11650b859681e03fdbf26277fcfc5f1f62186703 where the use of non hex characters in vpd_unit_serial generated different values than the original v3.0 internal hex -> binary code. This v3.1 change caused a problem with filesystems who write a NAA DESIGNATOR onto it's ondisk metadata, and this patch will (again) change existing values to ensure that non hex characters are not included in the fixed length NAA DESIGNATOR. Note this patch still expects vpd_unit_serial to be set via existing userspace methods of uuid generation, and does not do strict formatting via configfs input. The original bug report and thread can be found here: NAA breakage http://www.spinics.net/lists/target-devel/msg00477.html The v3.1-rc1 formatting of VPD=0x83 w/o this patch: VPD INQUIRY: Device Identification page Designation descriptor number 1, descriptor length: 20 designator_type: NAA, code_set: Binary associated with the addressed logical unit NAA 6, IEEE Company_id: 0x1405 Vendor Specific Identifier: 0xffde35ebf Vendor Specific Identifier Extension: 0x3092f498ffa820f9 [0x6001405ffde35ebf3092f498ffa820f9] Designation descriptor number 2, descriptor length: 56 designator_type: T10 vendor identification, code_set: ASCII associated with the addressed logical unit vendor id: LIO-ORG vendor specific: IBLOCK:ffde35ec-3092-4980-a820-917636ca54f1 The v3.1-final formatting of VPD=0x83 w/ this patch: VPD INQUIRY: Device Identification page Designation descriptor number 1, descriptor length: 20 designator_type: NAA, code_set: Binary associated with the addressed logical unit NAA 6, IEEE Company_id: 0x1405 Vendor Specific Identifier: 0xffde35ec3 Vendor Specific Identifier Extension: 0x924980a82091763 [0x6001405ffde35ec30924980a82091763] Designation descriptor number 2, descriptor length: 56 designator_type: T10 vendor identification, code_set: ASCII associated with the addressed logical unit vendor id: LIO-ORG vendor specific: IBLOCK:ffde35ec-3092-4980-a820-917636ca54f1 (v2: Fix parsing code to dereference + check for string terminator instead of null pointer to ensure a zeroed payload for vpd_unit_serial less than 100 bits of NAA DESIGNATOR VENDOR SPECIFIC area. Also, remove the unnecessary bitwise assignment) Reported-by: Martin Svec Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_cdb.c | 35 ++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 89ae923c5da6..f04d4ef99dca 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -24,6 +24,7 @@ */ #include +#include #include #include @@ -154,6 +155,37 @@ target_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf) return 0; } +static void +target_parse_naa_6h_vendor_specific(struct se_device *dev, unsigned char *buf_off) +{ + unsigned char *p = &dev->se_sub_dev->t10_wwn.unit_serial[0]; + unsigned char *buf = buf_off; + int cnt = 0, next = 1; + /* + * Generate up to 36 bits of VENDOR SPECIFIC IDENTIFIER starting on + * byte 3 bit 3-0 for NAA IEEE Registered Extended DESIGNATOR field + * format, followed by 64 bits of VENDOR SPECIFIC IDENTIFIER EXTENSION + * to complete the payload. These are based from VPD=0x80 PRODUCT SERIAL + * NUMBER set via vpd_unit_serial in target_core_configfs.c to ensure + * per device uniqeness. + */ + while (*p != '\0') { + if (cnt >= 13) + break; + if (!isxdigit(*p)) { + p++; + continue; + } + if (next != 0) { + buf[cnt++] |= hex_to_bin(*p++); + next = 0; + } else { + buf[cnt] = hex_to_bin(*p++) << 4; + next = 1; + } + } +} + /* * Device identification VPD, for a complete list of * DESIGNATOR TYPEs see spc4r17 Table 459. @@ -219,8 +251,7 @@ target_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf) * VENDOR_SPECIFIC_IDENTIFIER and * VENDOR_SPECIFIC_IDENTIFIER_EXTENTION */ - buf[off++] |= hex_to_bin(dev->se_sub_dev->t10_wwn.unit_serial[0]); - hex2bin(&buf[off], &dev->se_sub_dev->t10_wwn.unit_serial[1], 12); + target_parse_naa_6h_vendor_specific(dev, &buf[off]); len = 20; off = (len + 4); From 2ff017f5b4299e24a7f22d9a336dd162bf52bb54 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 16 Sep 2011 01:44:54 -0700 Subject: [PATCH 5/6] iscsi-target: Disable markers + remove dangerous local scope array usage This patch makes iscsi-target explictly disable OFMarker=Yes and IFMarker=yes parameter key usage during iscsi login by setting IFMarkInt_Reject and OFMarkInt_Reject values in iscsi_enforce_integrity_rules() to effectively disable iscsi marker usage. With this patch, an initiator proposer asking to enable either marker parameter keys will be issued a 'No' response, and the target sets OFMarkInt + IFMarkInt parameter key response to 'Irrelevant'. With markers disabled during iscsi login, this patch removes the problematic on-stack local-scope array for marker intervals in iscsit_do_rx_data() + iscsit_do_tx_data(), and other related marker code in iscsi_target_util.c. This fixes a potentional stack smashing scenario with small range markers enabled and a large MRDSL as reported by DanC here: [bug report] target: stack can be smashed http://www.spinics.net/lists/target-devel/msg00453.html Reported-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- .../target/iscsi/iscsi_target_parameters.c | 2 +- drivers/target/iscsi/iscsi_target_util.c | 248 +----------------- 2 files changed, 7 insertions(+), 243 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index 497b2e718a76..5b773160200f 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -1430,7 +1430,7 @@ static int iscsi_enforce_integrity_rules( u8 DataSequenceInOrder = 0; u8 ErrorRecoveryLevel = 0, SessionType = 0; u8 IFMarker = 0, OFMarker = 0; - u8 IFMarkInt_Reject = 0, OFMarkInt_Reject = 0; + u8 IFMarkInt_Reject = 1, OFMarkInt_Reject = 1; u32 FirstBurstLength = 0, MaxBurstLength = 0; struct iscsi_param *param = NULL; diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index a0d23bc0fc98..1d1b4fe33e43 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -874,40 +874,6 @@ void iscsit_inc_session_usage_count(struct iscsi_session *sess) spin_unlock_bh(&sess->session_usage_lock); } -/* - * Used before iscsi_do[rx,tx]_data() to determine iov and [rx,tx]_marker - * array counts needed for sync and steering. - */ -static int iscsit_determine_sync_and_steering_counts( - struct iscsi_conn *conn, - struct iscsi_data_count *count) -{ - u32 length = count->data_length; - u32 marker, markint; - - count->sync_and_steering = 1; - - marker = (count->type == ISCSI_RX_DATA) ? - conn->of_marker : conn->if_marker; - markint = (count->type == ISCSI_RX_DATA) ? - (conn->conn_ops->OFMarkInt * 4) : - (conn->conn_ops->IFMarkInt * 4); - count->ss_iov_count = count->iov_count; - - while (length > 0) { - if (length >= marker) { - count->ss_iov_count += 3; - count->ss_marker_count += 2; - - length -= marker; - marker = markint; - } else - length = 0; - } - - return 0; -} - /* * Setup conn->if_marker and conn->of_marker values based upon * the initial marker-less interval. (see iSCSI v19 A.2) @@ -1431,8 +1397,7 @@ static int iscsit_do_rx_data( struct iscsi_data_count *count) { int data = count->data_length, rx_loop = 0, total_rx = 0, iov_len; - u32 rx_marker_val[count->ss_marker_count], rx_marker_iov = 0; - struct kvec iov[count->ss_iov_count], *iov_p; + struct kvec *iov_p; struct msghdr msg; if (!conn || !conn->sock || !conn->conn_ops) @@ -1440,93 +1405,8 @@ static int iscsit_do_rx_data( memset(&msg, 0, sizeof(struct msghdr)); - if (count->sync_and_steering) { - int size = 0; - u32 i, orig_iov_count = 0; - u32 orig_iov_len = 0, orig_iov_loc = 0; - u32 iov_count = 0, per_iov_bytes = 0; - u32 *rx_marker, old_rx_marker = 0; - struct kvec *iov_record; - - memset(&rx_marker_val, 0, - count->ss_marker_count * sizeof(u32)); - memset(&iov, 0, count->ss_iov_count * sizeof(struct kvec)); - - iov_record = count->iov; - orig_iov_count = count->iov_count; - rx_marker = &conn->of_marker; - - i = 0; - size = data; - orig_iov_len = iov_record[orig_iov_loc].iov_len; - while (size > 0) { - pr_debug("rx_data: #1 orig_iov_len %u," - " orig_iov_loc %u\n", orig_iov_len, orig_iov_loc); - pr_debug("rx_data: #2 rx_marker %u, size" - " %u\n", *rx_marker, size); - - if (orig_iov_len >= *rx_marker) { - iov[iov_count].iov_len = *rx_marker; - iov[iov_count++].iov_base = - (iov_record[orig_iov_loc].iov_base + - per_iov_bytes); - - iov[iov_count].iov_len = (MARKER_SIZE / 2); - iov[iov_count++].iov_base = - &rx_marker_val[rx_marker_iov++]; - iov[iov_count].iov_len = (MARKER_SIZE / 2); - iov[iov_count++].iov_base = - &rx_marker_val[rx_marker_iov++]; - old_rx_marker = *rx_marker; - - /* - * OFMarkInt is in 32-bit words. - */ - *rx_marker = (conn->conn_ops->OFMarkInt * 4); - size -= old_rx_marker; - orig_iov_len -= old_rx_marker; - per_iov_bytes += old_rx_marker; - - pr_debug("rx_data: #3 new_rx_marker" - " %u, size %u\n", *rx_marker, size); - } else { - iov[iov_count].iov_len = orig_iov_len; - iov[iov_count++].iov_base = - (iov_record[orig_iov_loc].iov_base + - per_iov_bytes); - - per_iov_bytes = 0; - *rx_marker -= orig_iov_len; - size -= orig_iov_len; - - if (size) - orig_iov_len = - iov_record[++orig_iov_loc].iov_len; - - pr_debug("rx_data: #4 new_rx_marker" - " %u, size %u\n", *rx_marker, size); - } - } - data += (rx_marker_iov * (MARKER_SIZE / 2)); - - iov_p = &iov[0]; - iov_len = iov_count; - - if (iov_count > count->ss_iov_count) { - pr_err("iov_count: %d, count->ss_iov_count:" - " %d\n", iov_count, count->ss_iov_count); - return -1; - } - if (rx_marker_iov > count->ss_marker_count) { - pr_err("rx_marker_iov: %d, count->ss_marker" - "_count: %d\n", rx_marker_iov, - count->ss_marker_count); - return -1; - } - } else { - iov_p = count->iov; - iov_len = count->iov_count; - } + iov_p = count->iov; + iov_len = count->iov_count; while (total_rx < data) { rx_loop = kernel_recvmsg(conn->sock, &msg, iov_p, iov_len, @@ -1541,16 +1421,6 @@ static int iscsit_do_rx_data( rx_loop, total_rx, data); } - if (count->sync_and_steering) { - int j; - for (j = 0; j < rx_marker_iov; j++) { - pr_debug("rx_data: #5 j: %d, offset: %d\n", - j, rx_marker_val[j]); - conn->of_marker_offset = rx_marker_val[j]; - } - total_rx -= (rx_marker_iov * (MARKER_SIZE / 2)); - } - return total_rx; } @@ -1559,8 +1429,7 @@ static int iscsit_do_tx_data( struct iscsi_data_count *count) { int data = count->data_length, total_tx = 0, tx_loop = 0, iov_len; - u32 tx_marker_val[count->ss_marker_count], tx_marker_iov = 0; - struct kvec iov[count->ss_iov_count], *iov_p; + struct kvec *iov_p; struct msghdr msg; if (!conn || !conn->sock || !conn->conn_ops) @@ -1573,98 +1442,8 @@ static int iscsit_do_tx_data( memset(&msg, 0, sizeof(struct msghdr)); - if (count->sync_and_steering) { - int size = 0; - u32 i, orig_iov_count = 0; - u32 orig_iov_len = 0, orig_iov_loc = 0; - u32 iov_count = 0, per_iov_bytes = 0; - u32 *tx_marker, old_tx_marker = 0; - struct kvec *iov_record; - - memset(&tx_marker_val, 0, - count->ss_marker_count * sizeof(u32)); - memset(&iov, 0, count->ss_iov_count * sizeof(struct kvec)); - - iov_record = count->iov; - orig_iov_count = count->iov_count; - tx_marker = &conn->if_marker; - - i = 0; - size = data; - orig_iov_len = iov_record[orig_iov_loc].iov_len; - while (size > 0) { - pr_debug("tx_data: #1 orig_iov_len %u," - " orig_iov_loc %u\n", orig_iov_len, orig_iov_loc); - pr_debug("tx_data: #2 tx_marker %u, size" - " %u\n", *tx_marker, size); - - if (orig_iov_len >= *tx_marker) { - iov[iov_count].iov_len = *tx_marker; - iov[iov_count++].iov_base = - (iov_record[orig_iov_loc].iov_base + - per_iov_bytes); - - tx_marker_val[tx_marker_iov] = - (size - *tx_marker); - iov[iov_count].iov_len = (MARKER_SIZE / 2); - iov[iov_count++].iov_base = - &tx_marker_val[tx_marker_iov++]; - iov[iov_count].iov_len = (MARKER_SIZE / 2); - iov[iov_count++].iov_base = - &tx_marker_val[tx_marker_iov++]; - old_tx_marker = *tx_marker; - - /* - * IFMarkInt is in 32-bit words. - */ - *tx_marker = (conn->conn_ops->IFMarkInt * 4); - size -= old_tx_marker; - orig_iov_len -= old_tx_marker; - per_iov_bytes += old_tx_marker; - - pr_debug("tx_data: #3 new_tx_marker" - " %u, size %u\n", *tx_marker, size); - pr_debug("tx_data: #4 offset %u\n", - tx_marker_val[tx_marker_iov-1]); - } else { - iov[iov_count].iov_len = orig_iov_len; - iov[iov_count++].iov_base - = (iov_record[orig_iov_loc].iov_base + - per_iov_bytes); - - per_iov_bytes = 0; - *tx_marker -= orig_iov_len; - size -= orig_iov_len; - - if (size) - orig_iov_len = - iov_record[++orig_iov_loc].iov_len; - - pr_debug("tx_data: #5 new_tx_marker" - " %u, size %u\n", *tx_marker, size); - } - } - - data += (tx_marker_iov * (MARKER_SIZE / 2)); - - iov_p = &iov[0]; - iov_len = iov_count; - - if (iov_count > count->ss_iov_count) { - pr_err("iov_count: %d, count->ss_iov_count:" - " %d\n", iov_count, count->ss_iov_count); - return -1; - } - if (tx_marker_iov > count->ss_marker_count) { - pr_err("tx_marker_iov: %d, count->ss_marker" - "_count: %d\n", tx_marker_iov, - count->ss_marker_count); - return -1; - } - } else { - iov_p = count->iov; - iov_len = count->iov_count; - } + iov_p = count->iov; + iov_len = count->iov_count; while (total_tx < data) { tx_loop = kernel_sendmsg(conn->sock, &msg, iov_p, iov_len, @@ -1679,9 +1458,6 @@ static int iscsit_do_tx_data( tx_loop, total_tx, data); } - if (count->sync_and_steering) - total_tx -= (tx_marker_iov * (MARKER_SIZE / 2)); - return total_tx; } @@ -1702,12 +1478,6 @@ int rx_data( c.data_length = data; c.type = ISCSI_RX_DATA; - if (conn->conn_ops->OFMarker && - (conn->conn_state >= TARG_CONN_STATE_LOGGED_IN)) { - if (iscsit_determine_sync_and_steering_counts(conn, &c) < 0) - return -1; - } - return iscsit_do_rx_data(conn, &c); } @@ -1728,12 +1498,6 @@ int tx_data( c.data_length = data; c.type = ISCSI_TX_DATA; - if (conn->conn_ops->IFMarker && - (conn->conn_state >= TARG_CONN_STATE_LOGGED_IN)) { - if (iscsit_determine_sync_and_steering_counts(conn, &c) < 0) - return -1; - } - return iscsit_do_tx_data(conn, &c); } From 40b054970afcf067896d62cd6f7e617c62665304 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 16 Sep 2011 16:55:47 -0700 Subject: [PATCH 6/6] iscsi-target: Fix sendpage breakage with proper padding+DataDigest iovec offsets This patch fixes a bug in the iscsit_fe_sendpage_sg() transmit codepath that was originally introduced with the v3.1 iscsi-target merge that incorrectly uses hardcoded cmd->iov_data_count values to determine cmd->iov_data[] offsets for extra outgoing padding and DataDigest payload vectors. This code is obviously incorrect for the DataDigest enabled case with sendpage offload, and this fix ensures correct operation for padding + DataDigest, padding only, and DataDigest only cases. The bug was introduced during a pre-merge change in iscsit_fe_sendpage_sg() to natively use struct scatterlist instead of the legacy v3.0 struct se_mem logic. Cc: Andy Grover Cc: Christoph Hellwig Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_util.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 1d1b4fe33e43..f00137f377b2 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -1256,7 +1256,7 @@ int iscsit_fe_sendpage_sg( struct kvec iov; u32 tx_hdr_size, data_len; u32 offset = cmd->first_data_sg_off; - int tx_sent; + int tx_sent, iov_off; send_hdr: tx_hdr_size = ISCSI_HDR_LEN; @@ -1276,9 +1276,19 @@ send_hdr: } data_len = cmd->tx_size - tx_hdr_size - cmd->padding; - if (conn->conn_ops->DataDigest) + /* + * Set iov_off used by padding and data digest tx_data() calls below + * in order to determine proper offset into cmd->iov_data[] + */ + if (conn->conn_ops->DataDigest) { data_len -= ISCSI_CRC_LEN; - + if (cmd->padding) + iov_off = (cmd->iov_data_count - 2); + else + iov_off = (cmd->iov_data_count - 1); + } else { + iov_off = (cmd->iov_data_count - 1); + } /* * Perform sendpage() for each page in the scatterlist */ @@ -1307,8 +1317,7 @@ send_pg: send_padding: if (cmd->padding) { - struct kvec *iov_p = - &cmd->iov_data[cmd->iov_data_count-1]; + struct kvec *iov_p = &cmd->iov_data[iov_off++]; tx_sent = tx_data(conn, iov_p, 1, cmd->padding); if (cmd->padding != tx_sent) { @@ -1322,8 +1331,7 @@ send_padding: send_datacrc: if (conn->conn_ops->DataDigest) { - struct kvec *iov_d = - &cmd->iov_data[cmd->iov_data_count]; + struct kvec *iov_d = &cmd->iov_data[iov_off]; tx_sent = tx_data(conn, iov_d, 1, ISCSI_CRC_LEN); if (ISCSI_CRC_LEN != tx_sent) {