diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2015-04-07 17:53:27 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2015-04-14 15:37:38 -0400 |
commit | c8e639852ad720499912acedfd6b072325fd2807 (patch) | |
tree | 9a36ded92c2e3dab7c7e8a0bd5a5f0aa0d7dd884 /drivers/target | |
parent | 9ac8928e6a3e1ed02e632e45aa766129fe6b1802 (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.c | 15 | ||||
-rw-r--r-- | drivers/target/target_core_transport.c | 37 |
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 | ||
324 | static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd) | 324 | static 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 | ||
388 | static sense_reason_t compare_and_write_post(struct se_cmd *cmd) | 388 | static 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 | ||
411 | static sense_reason_t compare_and_write_callback(struct se_cmd *cmd) | 411 | static 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 | ||
2070 | queue_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) | |||
2166 | static inline void transport_free_pages(struct se_cmd *cmd) | 2171 | static 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 | |||
2318 | transport_generic_new_cmd(struct se_cmd *cmd) | 2333 | transport_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, |