mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Yingjie Wang <x-professor@outlook.com>
To: "Martin K . Petersen" <mkp@kernel.org>,
	"James E . J . Bottomley" <James.Bottomley@HansenPartnership.com>,
	Jens Axboe <axboe@kernel.dk>
Cc: Yingjie Wang <x-professor@outlook.com>,
	"linux-scsi@vger.kernel.org" <linux-scsi@vger.kernel.org>,
	Bart Van Assche <bvanassche@acm.org>,
	Yang Xiuwei <yangxiuwei@kylinos.cn>,
	James Houghton <jthoughton@google.com>,
	Pavel Begunkov <asml.silence@gmail.com>,
	"io-uring@vger.kernel.org" <io-uring@vger.kernel.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Subject: [RFC PATCH v3 6/6] scsi: bsg: release io_uring requests from end-I/O
Date: Sat, 7 Nov 2026 09:50:30 +0000	[thread overview]
Message-ID: <40c9297f2f2d3bb758837863bf002b1ac04fddc3.1794045010.git.x-professor@outlook.com> (raw)
In-Reply-To: <cover.1794045010.git.x-professor@outlook.com>

From: Yingjie Wang <x-professor@outlook.com>

The BSG io_uring end-I/O callback leaves completed requests for task work
to release. The request keeps its tag until that work runs. If the
submitting task first faults on memory backed by the same queue, it can
block waiting for the retained tag and never run the task work that frees
it.

Snapshot the SCSI result and bounded sense data in end-I/O, queue task
work, and return RQ_END_IO_FREE for every completion. Task work now only
unmaps the saved BIO, copies the saved sense data, and posts the CQE; it
never dereferences the request or scsi_cmnd.

Store the sense snapshot in io_uring completion data that was already
allocated for the command SQE copy. This adds no per-command allocation
and does not grow the request. If task work is canceled or the sense copy
fails, clear the reported sense length because no bytes reached user
space.

Fixes: 7b6d3255e7f8 ("scsi: bsg: add io_uring passthrough handler")
Link: https://lore.kernel.org/r/20261022.scsi-copyback-review.31b8d6a70f4c@outlook.com
Cc: stable@vger.kernel.org
Signed-off-by: Yingjie Wang <x-professor@outlook.com>
---
 drivers/scsi/scsi_bsg.c | 94 ++++++++++++++++++++++++++++-------------
 1 file changed, 64 insertions(+), 30 deletions(-)

diff --git a/drivers/scsi/scsi_bsg.c b/drivers/scsi/scsi_bsg.c
index 5ffbd1843861..4794278374b1 100644
--- a/drivers/scsi/scsi_bsg.c
+++ b/drivers/scsi/scsi_bsg.c
@@ -16,9 +16,13 @@
  */
 struct scsi_bsg_uring_cmd_pdu {
 	struct bio *bio;		/* mapped user buffer, unmap in task work */
-	struct request *req;		/* block request, freed in task work */
+	u8 *sense_buffer;		/* completion storage owned by io_uring */
 	u64 response_addr;		/* user space response buffer address */
-	u32 max_response_len;		/* user response buffer size */
+	/* max_response_len is replaced by res2 after the end-I/O snapshot. */
+	union {
+		u32 max_response_len;	/* maximum sense snapshot size */
+		u64 res2;		/* saved completion result */
+	};
 };
 static_assert(sizeof(struct scsi_bsg_uring_cmd_pdu) <= sizeof_field(struct io_uring_cmd, pdu));
 
@@ -28,17 +32,22 @@ static inline struct scsi_bsg_uring_cmd_pdu *scsi_bsg_uring_cmd_pdu(
 	return io_uring_cmd_to_pdu(ioucmd, struct scsi_bsg_uring_cmd_pdu);
 }
 
-/* Task work: build res2 (layout in uapi/linux/bsg.h) and copy sense to user. */
+static u64 scsi_bsg_res2_clear_sense_len(u64 res2)
+{
+	return bsg_scsi_res2_build(bsg_scsi_res2_device_status(res2),
+				   bsg_scsi_res2_driver_status(res2),
+				   bsg_scsi_res2_host_status(res2), 0,
+				   bsg_scsi_res2_resid_len(res2));
+}
+
+/* Task work: unmap the saved BIO and copy sense to user space. */
 static void scsi_bsg_uring_task_cb(struct io_tw_req tw_req, io_tw_token_t tw)
 {
 	struct io_uring_cmd *ioucmd = io_uring_cmd_from_tw(tw_req);
 	struct scsi_bsg_uring_cmd_pdu *pdu = scsi_bsg_uring_cmd_pdu(ioucmd);
-	struct request *rq = pdu->req;
-	struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
-	u64 res2;
+	u64 res2 = pdu->res2;
+	u8 sense_len = bsg_scsi_res2_sense_len(res2);
 	int ret = 0;
-	u8 driver_status = 0;
-	u8 sense_len_wr = 0;
 
 	if (pdu->bio)
 		ret = blk_rq_unmap_user(pdu->bio);
@@ -48,27 +57,20 @@ static void scsi_bsg_uring_task_cb(struct io_tw_req tw_req, io_tw_token_t tw)
 	 * has exited, this may run from a kworker or in a different task, so
 	 * don't copy the sense data to the submitter's address.
 	 */
-	if (unlikely(tw.cancel))
+	if (unlikely(tw.cancel)) {
 		ret = -ECANCELED;
-
-	if (scsi_status_is_check_condition(scmd->result)) {
-		driver_status = DRIVER_SENSE;
-		if (pdu->response_addr && !tw.cancel)
-			sense_len_wr = min_t(unsigned int, pdu->max_response_len,
-					     scmd->sense_len);
-	}
-
-	if (sense_len_wr) {
-		if (copy_to_user(uptr64(pdu->response_addr), scmd->sense_buffer,
-				 sense_len_wr))
+		sense_len = 0;
+	} else if (sense_len) {
+		if (copy_to_user(uptr64(pdu->response_addr), pdu->sense_buffer,
+				 sense_len)) {
 			ret = -EFAULT;
+			sense_len = 0;
+		}
 	}
 
-	res2 = bsg_scsi_res2_build(status_byte(scmd->result), driver_status,
-				  host_byte(scmd->result), sense_len_wr,
-				  scmd->resid_len);
+	if (!sense_len)
+		res2 = scsi_bsg_res2_clear_sense_len(res2);
 
-	blk_mq_free_request(rq);
 	io_uring_cmd_done32(ioucmd, ret, res2,
 			    IO_URING_CMD_TASK_WORK_ISSUE_FLAGS);
 }
@@ -78,9 +80,25 @@ static enum rq_end_io_ret scsi_bsg_uring_cmd_done(struct request *req,
 						  const struct io_comp_batch *iocb)
 {
 	struct io_uring_cmd *ioucmd = req->end_io_data;
+	struct scsi_bsg_uring_cmd_pdu *pdu = scsi_bsg_uring_cmd_pdu(ioucmd);
+	struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(req);
+	u8 driver_status = 0;
+	u8 sense_len = 0;
 
+	if (scsi_status_is_check_condition(scmd->result)) {
+		driver_status = DRIVER_SENSE;
+		if (pdu->response_addr) {
+			sense_len = min_t(unsigned int, pdu->max_response_len,
+					  scmd->sense_len);
+			memcpy(pdu->sense_buffer, scmd->sense_buffer,
+			       sense_len);
+		}
+	}
+	pdu->res2 = bsg_scsi_res2_build(status_byte(scmd->result),
+					driver_status, host_byte(scmd->result),
+					sense_len, scmd->resid_len);
 	io_uring_cmd_do_in_task_lazy(ioucmd, scsi_bsg_uring_task_cb);
-	return RQ_END_IO_NONE;
+	return RQ_END_IO_FREE;
 }
 
 static int scsi_bsg_map_user_buffer(struct request *req,
@@ -123,8 +141,15 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, struct io_uring_cmd *iouc
 	u32 dout_xfer_len = READ_ONCE(cmd->dout_xfer_len);
 	u64 din_xferp = READ_ONCE(cmd->din_xferp);
 	u32 din_xfer_len = READ_ONCE(cmd->din_xfer_len);
+	u64 response_addr = READ_ONCE(cmd->response);
+	u32 max_response_len = min_t(u32, READ_ONCE(cmd->max_response_len),
+				     SCSI_SENSE_BUFFERSIZE);
+	u32 timeout_ms = READ_ONCE(cmd->timeout_ms);
 	int ret;
 
+	if (ioucmd->flags & IORING_URING_CMD_MULTISHOT)
+		return -EOPNOTSUPP;
+
 	if (cmd->protocol != BSG_PROTOCOL_SCSI ||
 	    cmd->subprotocol != BSG_SUB_PROTOCOL_SCSI_CMD)
 		return -EINVAL;
@@ -168,8 +193,8 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, struct io_uring_cmd *iouc
 		goto out_free_req;
 	}
 
-	pdu->response_addr = cmd->response;
-	pdu->max_response_len = cmd->max_response_len;
+	pdu->response_addr = response_addr;
+	pdu->max_response_len = max_response_len;
 
 	if (dout_xfer_len || din_xfer_len) {
 		bool is_write = dout_xfer_len > 0;
@@ -186,16 +211,25 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, struct io_uring_cmd *iouc
 		pdu->bio = NULL;
 	}
 
-	req->timeout = cmd->timeout_ms ?
-		msecs_to_jiffies(cmd->timeout_ms) : BLK_DEFAULT_SG_TIMEOUT;
+	req->timeout = timeout_ms ? msecs_to_jiffies(timeout_ms) :
+				   BLK_DEFAULT_SG_TIMEOUT;
+
+	pdu->sense_buffer =
+		io_uring_cmd_get_completion_data(ioucmd, SCSI_SENSE_BUFFERSIZE);
+	if (WARN_ON_ONCE(!pdu->sense_buffer)) {
+		ret = -EIO;
+		goto out_unmap_user;
+	}
 
 	req->end_io = scsi_bsg_uring_cmd_done;
 	req->end_io_data = ioucmd;
-	pdu->req = req;
 
 	blk_execute_rq_nowait(req, false);
 	return -EIOCBQUEUED;
 
+out_unmap_user:
+	if (pdu->bio)
+		blk_rq_unmap_user(pdu->bio);
 out_free_req:
 	blk_mq_free_request(req);
 	return ret;
-- 
2.43.0


      parent reply	other threads:[~2026-11-07  9:51 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-07  9:50 [RFC PATCH v3 0/6] scsi: release completed passthrough requests before user copyback Yingjie Wang
2026-11-07  9:50 ` [RFC PATCH v3 1/6] scsi: core: release SG_IO request " Yingjie Wang
2026-11-07  9:50 ` [RFC PATCH v3 2/6] scsi: core: release legacy ioctl " Yingjie Wang
2026-11-07  9:50 ` [RFC PATCH v3 3/6] scsi: sr: release CDDA " Yingjie Wang
2026-11-07  9:50 ` [RFC PATCH v3 4/6] scsi: bsg: release synchronous " Yingjie Wang
2026-11-07  9:50 ` [RFC PATCH v3 5/6] io_uring/cmd: allow reusing SQE storage for completion data Yingjie Wang
2026-11-07  9:50 ` Yingjie Wang [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=40c9297f2f2d3bb758837863bf002b1ac04fddc3.1794045010.git.x-professor@outlook.com \
    --to=x-professor@outlook.com \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=asml.silence@gmail.com \
    --cc=axboe@kernel.dk \
    --cc=bvanassche@acm.org \
    --cc=io-uring@vger.kernel.org \
    --cc=jthoughton@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=mkp@kernel.org \
    --cc=yangxiuwei@kylinos.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®