diff mbox series

[V2,13/17] block: ublk_drv: grab request reference when the request is handled by userspace

Message ID 20230307141520.793891-14-ming.lei@redhat.com (mailing list archive)
State New
Headers show
Series io_uring/ublk: add IORING_OP_FUSED_CMD | expand

Commit Message

Ming Lei March 7, 2023, 2:15 p.m. UTC
Add one reference counter into request pdu data, and hold this reference
in the request's lifetime. This way is always safe. In theory, the ublk
request won't be completed until fused commands are done. However, it
is userspace, and application can submit fused command at will.

Prepare for supporting zero copy, which needs to retrieve request buffer
by fused command, so we have to guarantee:

- the fused command can't succeed unless the request isn't queued

- when any fused command is successful, this request can't be freed
until all fused commands on this request are done.

Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
 drivers/block/ublk_drv.c | 67 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 64 insertions(+), 3 deletions(-)

Comments

kernel test robot March 15, 2023, 5:20 a.m. UTC | #1
Hi Ming,

I love your patch! Perhaps something to improve:

[auto build test WARNING on axboe-block/for-next]
[also build test WARNING on char-misc/char-misc-testing char-misc/char-misc-next char-misc/char-misc-linus linus/master v6.3-rc2 next-20230314]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Ming-Lei/io_uring-add-IO_URING_F_FUSED-and-prepare-for-supporting-OP_FUSED_CMD/20230307-222928
base:   https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git for-next
patch link:    https://lore.kernel.org/r/20230307141520.793891-14-ming.lei%40redhat.com
patch subject: [PATCH V2 13/17] block: ublk_drv: grab request reference when the request is handled by userspace
config: microblaze-randconfig-s033-20230308 (https://download.01.org/0day-ci/archive/20230315/202303151232.tHK2H9T3-lkp@intel.com/config)
compiler: microblaze-linux-gcc (GCC) 12.1.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # apt-get install sparse
        # sparse version: v0.6.4-39-gce1a6720-dirty
        # https://github.com/intel-lab-lkp/linux/commit/8e1a2115a8d58ff04cbc1aad6192805e29d0b63e
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Ming-Lei/io_uring-add-IO_URING_F_FUSED-and-prepare-for-supporting-OP_FUSED_CMD/20230307-222928
        git checkout 8e1a2115a8d58ff04cbc1aad6192805e29d0b63e
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=microblaze olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=microblaze SHELL=/bin/bash drivers/block/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202303151232.tHK2H9T3-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> drivers/block/ublk_drv.c:688:21: sparse: sparse: incorrect type in assignment (different base types) @@     expected int res @@     got restricted blk_status_t [usertype] @@
   drivers/block/ublk_drv.c:688:21: sparse:     expected int res
   drivers/block/ublk_drv.c:688:21: sparse:     got restricted blk_status_t [usertype]
   drivers/block/ublk_drv.c:697:21: sparse: sparse: incorrect type in assignment (different base types) @@     expected int res @@     got restricted blk_status_t @@
   drivers/block/ublk_drv.c:697:21: sparse:     expected int res
   drivers/block/ublk_drv.c:697:21: sparse:     got restricted blk_status_t
   drivers/block/ublk_drv.c:728:33: sparse: sparse: incorrect type in argument 2 (different base types) @@     expected restricted blk_status_t [usertype] error @@     got int res @@
   drivers/block/ublk_drv.c:728:33: sparse:     expected restricted blk_status_t [usertype] error
   drivers/block/ublk_drv.c:728:33: sparse:     got int res
>> drivers/block/ublk_drv.c:688:21: sparse: sparse: incorrect type in assignment (different base types) @@     expected int res @@     got restricted blk_status_t [usertype] @@
   drivers/block/ublk_drv.c:688:21: sparse:     expected int res
   drivers/block/ublk_drv.c:688:21: sparse:     got restricted blk_status_t [usertype]
   drivers/block/ublk_drv.c:697:21: sparse: sparse: incorrect type in assignment (different base types) @@     expected int res @@     got restricted blk_status_t @@
   drivers/block/ublk_drv.c:697:21: sparse:     expected int res
   drivers/block/ublk_drv.c:697:21: sparse:     got restricted blk_status_t
   drivers/block/ublk_drv.c:728:33: sparse: sparse: incorrect type in argument 2 (different base types) @@     expected restricted blk_status_t [usertype] error @@     got int res @@
   drivers/block/ublk_drv.c:728:33: sparse:     expected restricted blk_status_t [usertype] error
   drivers/block/ublk_drv.c:728:33: sparse:     got int res
>> drivers/block/ublk_drv.c:688:21: sparse: sparse: incorrect type in assignment (different base types) @@     expected int res @@     got restricted blk_status_t [usertype] @@
   drivers/block/ublk_drv.c:688:21: sparse:     expected int res
   drivers/block/ublk_drv.c:688:21: sparse:     got restricted blk_status_t [usertype]
   drivers/block/ublk_drv.c:697:21: sparse: sparse: incorrect type in assignment (different base types) @@     expected int res @@     got restricted blk_status_t @@
   drivers/block/ublk_drv.c:697:21: sparse:     expected int res
   drivers/block/ublk_drv.c:697:21: sparse:     got restricted blk_status_t
   drivers/block/ublk_drv.c:728:33: sparse: sparse: incorrect type in argument 2 (different base types) @@     expected restricted blk_status_t [usertype] error @@     got int res @@
   drivers/block/ublk_drv.c:728:33: sparse:     expected restricted blk_status_t [usertype] error
   drivers/block/ublk_drv.c:728:33: sparse:     got int res

vim +688 drivers/block/ublk_drv.c

   677	
   678	/* todo: handle partial completion */
   679	static inline void __ublk_complete_rq(struct request *req)
   680	{
   681		struct ublk_queue *ubq = req->mq_hctx->driver_data;
   682		struct ublk_io *io = &ubq->ios[req->tag];
   683		unsigned int unmapped_bytes;
   684		int res = BLK_STS_OK;
   685	
   686		/* called from ublk_abort_queue() code path */
   687		if (io->flags & UBLK_IO_FLAG_ABORTED) {
 > 688			res = BLK_STS_IOERR;
   689			goto exit;
   690		}
   691	
   692		/* failed read IO if nothing is read */
   693		if (!io->res && req_op(req) == REQ_OP_READ)
   694			io->res = -EIO;
   695	
   696		if (io->res < 0) {
   697			res = errno_to_blk_status(io->res);
   698			goto exit;
   699		}
   700	
   701		/*
   702		 * FLUSH, DISCARD or WRITE_ZEROES usually won't return bytes returned, so end them
   703		 * directly.
   704		 *
   705		 * Both the two needn't unmap.
   706		 */
   707		if (req_op(req) != REQ_OP_READ && req_op(req) != REQ_OP_WRITE)
   708			goto exit;
   709	
   710		/* for READ request, writing data in iod->addr to rq buffers */
   711		unmapped_bytes = ublk_unmap_io(ubq, req, io);
   712	
   713		/*
   714		 * Extremely impossible since we got data filled in just before
   715		 *
   716		 * Re-read simply for this unlikely case.
   717		 */
   718		if (unlikely(unmapped_bytes < io->res))
   719			io->res = unmapped_bytes;
   720	
   721		if (blk_update_request(req, BLK_STS_OK, io->res))
   722			blk_mq_requeue_request(req, true);
   723		else
   724			__blk_mq_end_request(req, BLK_STS_OK);
   725	
   726		return;
   727	exit:
   728		blk_mq_end_request(req, res);
   729	}
   730
diff mbox series

Patch

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index f07eb8a54357..ba252932951c 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -43,6 +43,7 @@ 
 #include <asm/page.h>
 #include <linux/task_work.h>
 #include <linux/namei.h>
+#include <linux/kref.h>
 #include <uapi/linux/ublk_cmd.h>
 
 #define UBLK_MINORS		(1U << MINORBITS)
@@ -62,6 +63,17 @@ 
 struct ublk_rq_data {
 	struct llist_node node;
 	struct callback_head work;
+
+	/*
+	 * Only for applying fused command to support zero copy:
+	 *
+	 * - if there is any fused command aiming at this request, not complete
+	 *   request until all fused commands are done
+	 *
+	 * - fused command has to fail unless this reference is grabbed
+	 *   successfully
+	 */
+	struct kref ref;
 };
 
 struct ublk_uring_cmd_pdu {
@@ -180,6 +192,9 @@  struct ublk_params_header {
 	__u32	types;
 };
 
+static inline void __ublk_complete_rq(struct request *req);
+static void ublk_complete_rq(struct kref *ref);
+
 static dev_t ublk_chr_devt;
 static struct class *ublk_chr_class;
 
@@ -288,6 +303,35 @@  static int ublk_apply_params(struct ublk_device *ub)
 	return 0;
 }
 
+static inline bool ublk_support_zc(const struct ublk_queue *ubq)
+{
+	return ubq->flags & UBLK_F_SUPPORT_ZERO_COPY;
+}
+
+static inline bool ublk_get_req_ref(const struct ublk_queue *ubq,
+		struct request *req)
+{
+	if (ublk_support_zc(ubq)) {
+		struct ublk_rq_data *data = blk_mq_rq_to_pdu(req);
+
+		return kref_get_unless_zero(&data->ref);
+	}
+
+	return true;
+}
+
+static inline void ublk_put_req_ref(const struct ublk_queue *ubq,
+		struct request *req)
+{
+	if (ublk_support_zc(ubq)) {
+		struct ublk_rq_data *data = blk_mq_rq_to_pdu(req);
+
+		kref_put(&data->ref, ublk_complete_rq);
+	} else {
+		__ublk_complete_rq(req);
+	}
+}
+
 static inline bool ublk_can_use_task_work(const struct ublk_queue *ubq)
 {
 	if (IS_BUILTIN(CONFIG_BLK_DEV_UBLK) &&
@@ -632,13 +676,19 @@  static inline bool ubq_daemon_is_dying(struct ublk_queue *ubq)
 }
 
 /* todo: handle partial completion */
-static void ublk_complete_rq(struct request *req)
+static inline void __ublk_complete_rq(struct request *req)
 {
 	struct ublk_queue *ubq = req->mq_hctx->driver_data;
 	struct ublk_io *io = &ubq->ios[req->tag];
 	unsigned int unmapped_bytes;
 	int res = BLK_STS_OK;
 
+	/* called from ublk_abort_queue() code path */
+	if (io->flags & UBLK_IO_FLAG_ABORTED) {
+		res = BLK_STS_IOERR;
+		goto exit;
+	}
+
 	/* failed read IO if nothing is read */
 	if (!io->res && req_op(req) == REQ_OP_READ)
 		io->res = -EIO;
@@ -678,6 +728,15 @@  static void ublk_complete_rq(struct request *req)
 	blk_mq_end_request(req, res);
 }
 
+static void ublk_complete_rq(struct kref *ref)
+{
+	struct ublk_rq_data *data = container_of(ref, struct ublk_rq_data,
+			ref);
+	struct request *req = blk_mq_rq_from_pdu(data);
+
+	__ublk_complete_rq(req);
+}
+
 /*
  * Since __ublk_rq_task_work always fails requests immediately during
  * exiting, __ublk_fail_req() is only called from abort context during
@@ -696,7 +755,7 @@  static void __ublk_fail_req(struct ublk_queue *ubq, struct ublk_io *io,
 		if (ublk_queue_can_use_recovery_reissue(ubq))
 			blk_mq_requeue_request(req, false);
 		else
-			blk_mq_end_request(req, BLK_STS_IOERR);
+			ublk_put_req_ref(ubq, req);
 	}
 }
 
@@ -732,6 +791,7 @@  static inline void __ublk_abort_rq(struct ublk_queue *ubq,
 static inline void __ublk_rq_task_work(struct request *req)
 {
 	struct ublk_queue *ubq = req->mq_hctx->driver_data;
+	struct ublk_rq_data *data = blk_mq_rq_to_pdu(req);
 	int tag = req->tag;
 	struct ublk_io *io = &ubq->ios[tag];
 	unsigned int mapped_bytes;
@@ -803,6 +863,7 @@  static inline void __ublk_rq_task_work(struct request *req)
 			mapped_bytes >> 9;
 	}
 
+	kref_init(&data->ref);
 	ubq_complete_io_cmd(io, UBLK_IO_RES_OK);
 }
 
@@ -1013,7 +1074,7 @@  static void ublk_commit_completion(struct ublk_device *ub,
 	req = blk_mq_tag_to_rq(ub->tag_set.tags[qid], tag);
 
 	if (req && likely(!blk_should_fake_timeout(req->q)))
-		ublk_complete_rq(req);
+		ublk_put_req_ref(ubq, req);
 }
 
 /*