[PATCH v2 4/6] scsi: bsg: release synchronous request before user copyback

From: Yingjie Wang

Date: Mon Oct 26 2026 - 04:30:44 EST


From: Yingjie Wang <x-professor@xxxxxxxxxxx>

The synchronous SCSI BSG ioctl copies sense data and unmaps its user
data buffer while retaining the completed request. Copyback can fault
and need I/O from the same queue, which cannot progress when completed
requests retain the available tags.

Fill the kernel-resident output fields and save the bounded sense data
before releasing the request. Perform both user copies afterward, using
the original BIO chain saved at mapping time. Preserve the copyback
error precedence established by the separate error-propagation fix.

The SG_IO v4 implementation predates scsi-mq. The fixed scsi-mq request
pool introduced this dependency; moving the callback into scsi_bsg.c
did not change its ownership ordering.

Fixes: d285203cf647 ("scsi: add support for a blk-mq based I/O path.")
Link: https://lore.kernel.org/r/20261022.scsi-copyback-review.31b8d6a70f4c@xxxxxxxxxxx
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Yingjie Wang <x-professor@xxxxxxxxxxx>
---
drivers/scsi/scsi_bsg.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/drivers/scsi/scsi_bsg.c b/drivers/scsi/scsi_bsg.c
index e4a65d7a23742..650facb16ed23 100644
--- a/drivers/scsi/scsi_bsg.c
+++ b/drivers/scsi/scsi_bsg.c
@@ -199,6 +199,8 @@ static int scsi_bsg_sg_io_fn(struct request_queue *q, struct sg_io_v4 *hdr,
struct scsi_cmnd *scmd;
struct request *rq;
struct bio *bio;
+ u8 sense_buffer[SCSI_SENSE_BUFFERSIZE];
+ unsigned int sense_len = 0;
int ret, unmap_ret;

if (hdr->protocol != BSG_PROTOCOL_SCSI ||
@@ -258,24 +260,28 @@ static int scsi_bsg_sg_io_fn(struct request_queue *q, struct sg_io_v4 *hdr,
hdr->response_len = 0;

if (scmd->sense_len && hdr->response) {
- int len = min_t(unsigned int, hdr->max_response_len,
- scmd->sense_len);
-
- if (copy_to_user(uptr64(hdr->response), scmd->sense_buffer,
- len))
- ret = -EFAULT;
- else
- hdr->response_len = len;
+ sense_len = min3(hdr->max_response_len, scmd->sense_len,
+ sizeof(sense_buffer));
+ memcpy(sense_buffer, scmd->sense_buffer, sense_len);
}

if (rq_data_dir(rq) == READ)
hdr->din_resid = scmd->resid_len;
else
hdr->dout_resid = scmd->resid_len;
+ blk_mq_free_request(rq);
+
+ if (sense_len) {
+ if (copy_to_user(uptr64(hdr->response), sense_buffer, sense_len))
+ ret = -EFAULT;
+ else
+ hdr->response_len = sense_len;
+ }

unmap_ret = blk_rq_unmap_user(bio);
if (!ret)
ret = unmap_ret;
+ return ret;

out_put_request:
blk_mq_free_request(rq);
--
2.43.0