aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/target
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2015-04-07 17:53:27 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2015-04-14 15:37:38 -0400
commitc8e639852ad720499912acedfd6b072325fd2807 (patch)
tree9a36ded92c2e3dab7c7e8a0bd5a5f0aa0d7dd884 /drivers/target
parent9ac8928e6a3e1ed02e632e45aa766129fe6b1802 (diff)
target: Fix COMPARE_AND_WRITE with SG_TO_MEM_NOALLOC handling
This patch fixes a bug for COMPARE_AND_WRITE handling with fabrics using SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC. It adds the missing allocation for cmd->t_bidi_data_sg within transport_generic_new_cmd() that is used by COMPARE_AND_WRITE for the initial READ payload, even if the fabric is already providing a pre-allocated buffer for cmd->t_data_sg. Also, fix zero-length COMPARE_AND_WRITE handling within the compare_and_write_callback() and target_complete_ok_work() to queue the response, skipping the initial READ. This fixes COMPARE_AND_WRITE emulation with loopback, vhost, and xen-backend fabric drivers using SG_TO_MEM_NOALLOC. Reported-by: Christoph Hellwig <hch@lst.de> Cc: Christoph Hellwig <hch@lst.de> Cc: <stable@vger.kernel.org> # v3.12+ Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r--drivers/target/target_core_sbc.c15
-rw-r--r--drivers/target/target_core_transport.c37
2 files changed, 43 insertions, 9 deletions
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 315ff641408b..0064ffe9a219 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -321,7 +321,7 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
321 return 0; 321 return 0;
322} 322}
323 323
324static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd) 324static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success)
325{ 325{
326 unsigned char *buf, *addr; 326 unsigned char *buf, *addr;
327 struct scatterlist *sg; 327 struct scatterlist *sg;
@@ -385,7 +385,7 @@ sbc_execute_rw(struct se_cmd *cmd)
385 cmd->data_direction); 385 cmd->data_direction);
386} 386}
387 387
388static sense_reason_t compare_and_write_post(struct se_cmd *cmd) 388static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
389{ 389{
390 struct se_device *dev = cmd->se_dev; 390 struct se_device *dev = cmd->se_dev;
391 391
@@ -408,7 +408,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd)
408 return TCM_NO_SENSE; 408 return TCM_NO_SENSE;
409} 409}
410 410
411static sense_reason_t compare_and_write_callback(struct se_cmd *cmd) 411static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success)
412{ 412{
413 struct se_device *dev = cmd->se_dev; 413 struct se_device *dev = cmd->se_dev;
414 struct scatterlist *write_sg = NULL, *sg; 414 struct scatterlist *write_sg = NULL, *sg;
@@ -423,11 +423,16 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd)
423 423
424 /* 424 /*
425 * Handle early failure in transport_generic_request_failure(), 425 * Handle early failure in transport_generic_request_failure(),
426 * which will not have taken ->caw_mutex yet.. 426 * which will not have taken ->caw_sem yet..
427 */ 427 */
428 if (!cmd->t_data_sg || !cmd->t_bidi_data_sg) 428 if (!success && (!cmd->t_data_sg || !cmd->t_bidi_data_sg))
429 return TCM_NO_SENSE; 429 return TCM_NO_SENSE;
430 /* 430 /*
431 * Handle special case for zero-length COMPARE_AND_WRITE
432 */
433 if (!cmd->data_length)
434 goto out;
435 /*
431 * Immediately exit + release dev->caw_sem if command has already 436 * Immediately exit + release dev->caw_sem if command has already
432 * been failed with a non-zero SCSI status. 437 * been failed with a non-zero SCSI status.
433 */ 438 */
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index f884198a8511..47334e5c47bf 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1647,11 +1647,11 @@ void transport_generic_request_failure(struct se_cmd *cmd,
1647 transport_complete_task_attr(cmd); 1647 transport_complete_task_attr(cmd);
1648 /* 1648 /*
1649 * Handle special case for COMPARE_AND_WRITE failure, where the 1649 * Handle special case for COMPARE_AND_WRITE failure, where the
1650 * callback is expected to drop the per device ->caw_mutex. 1650 * callback is expected to drop the per device ->caw_sem.
1651 */ 1651 */
1652 if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) && 1652 if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
1653 cmd->transport_complete_callback) 1653 cmd->transport_complete_callback)
1654 cmd->transport_complete_callback(cmd); 1654 cmd->transport_complete_callback(cmd, false);
1655 1655
1656 switch (sense_reason) { 1656 switch (sense_reason) {
1657 case TCM_NON_EXISTENT_LUN: 1657 case TCM_NON_EXISTENT_LUN:
@@ -2048,8 +2048,12 @@ static void target_complete_ok_work(struct work_struct *work)
2048 if (cmd->transport_complete_callback) { 2048 if (cmd->transport_complete_callback) {
2049 sense_reason_t rc; 2049 sense_reason_t rc;
2050 2050
2051 rc = cmd->transport_complete_callback(cmd); 2051 rc = cmd->transport_complete_callback(cmd, true);
2052 if (!rc && !(cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE_POST)) { 2052 if (!rc && !(cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE_POST)) {
2053 if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
2054 !cmd->data_length)
2055 goto queue_rsp;
2056
2053 return; 2057 return;
2054 } else if (rc) { 2058 } else if (rc) {
2055 ret = transport_send_check_condition_and_sense(cmd, 2059 ret = transport_send_check_condition_and_sense(cmd,
@@ -2063,6 +2067,7 @@ static void target_complete_ok_work(struct work_struct *work)
2063 } 2067 }
2064 } 2068 }
2065 2069
2070queue_rsp:
2066 switch (cmd->data_direction) { 2071 switch (cmd->data_direction) {
2067 case DMA_FROM_DEVICE: 2072 case DMA_FROM_DEVICE:
2068 spin_lock(&cmd->se_lun->lun_sep_lock); 2073 spin_lock(&cmd->se_lun->lun_sep_lock);
@@ -2166,6 +2171,16 @@ static inline void transport_reset_sgl_orig(struct se_cmd *cmd)
2166static inline void transport_free_pages(struct se_cmd *cmd) 2171static inline void transport_free_pages(struct se_cmd *cmd)
2167{ 2172{
2168 if (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) { 2173 if (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) {
2174 /*
2175 * Release special case READ buffer payload required for
2176 * SG_TO_MEM_NOALLOC to function with COMPARE_AND_WRITE
2177 */
2178 if (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) {
2179 transport_free_sgl(cmd->t_bidi_data_sg,
2180 cmd->t_bidi_data_nents);
2181 cmd->t_bidi_data_sg = NULL;
2182 cmd->t_bidi_data_nents = 0;
2183 }
2169 transport_reset_sgl_orig(cmd); 2184 transport_reset_sgl_orig(cmd);
2170 return; 2185 return;
2171 } 2186 }
@@ -2318,6 +2333,7 @@ sense_reason_t
2318transport_generic_new_cmd(struct se_cmd *cmd) 2333transport_generic_new_cmd(struct se_cmd *cmd)
2319{ 2334{
2320 int ret = 0; 2335 int ret = 0;
2336 bool zero_flag = !(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB);
2321 2337
2322 /* 2338 /*
2323 * Determine is the TCM fabric module has already allocated physical 2339 * Determine is the TCM fabric module has already allocated physical
@@ -2326,7 +2342,6 @@ transport_generic_new_cmd(struct se_cmd *cmd)
2326 */ 2342 */
2327 if (!(cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) && 2343 if (!(cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) &&
2328 cmd->data_length) { 2344 cmd->data_length) {
2329 bool zero_flag = !(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB);
2330 2345
2331 if ((cmd->se_cmd_flags & SCF_BIDI) || 2346 if ((cmd->se_cmd_flags & SCF_BIDI) ||
2332 (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE)) { 2347 (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE)) {
@@ -2357,6 +2372,20 @@ transport_generic_new_cmd(struct se_cmd *cmd)
2357 cmd->data_length, zero_flag); 2372 cmd->data_length, zero_flag);
2358 if (ret < 0) 2373 if (ret < 0)
2359 return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; 2374 return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
2375 } else if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
2376 cmd->data_length) {
2377 /*
2378 * Special case for COMPARE_AND_WRITE with fabrics
2379 * using SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC.
2380 */
2381 u32 caw_length = cmd->t_task_nolb *
2382 cmd->se_dev->dev_attrib.block_size;
2383
2384 ret = target_alloc_sgl(&cmd->t_bidi_data_sg,
2385 &cmd->t_bidi_data_nents,
2386 caw_length, zero_flag);
2387 if (ret < 0)
2388 return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
2360 } 2389 }
2361 /* 2390 /*
2362 * If this command is not a write we can execute it right here, 2391 * If this command is not a write we can execute it right here,