]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
nvme-pci: fix queue_rqs list splitting
authorKeith Busch <kbusch@kernel.org>
Wed, 5 Jan 2022 17:05:18 +0000 (09:05 -0800)
committerJens Axboe <axboe@kernel.dk>
Wed, 5 Jan 2022 19:25:42 +0000 (12:25 -0700)
If command prep fails, current handling will orphan subsequent requests
in the list. Consider a simple example:

  rqlist = [ 1 -> 2 ]

When prep for request '1' fails, it will be appended to the
'requeue_list', leaving request '2' disconnected from the original
rqlist and no longer tracked. Meanwhile, rqlist is still pointing to the
failed request '1' and will attempt to submit the unprepped command.

Fix this by updating the rqlist accordingly using the request list
helper functions.

Fixes: d62cbcf62f2f ("nvme: add support for mq_ops->queue_rqs()")
Signed-off-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/r/20220105170518.3181469-5-kbusch@kernel.org
Signed-off-by: Jens Axboe <axboe@kernel.dk>
drivers/nvme/host/pci.c

index 50deb8b69c4099785a0e3e2a1eb54dfedbf42ce0..d8585df2c2fd796acd1fae81ae357053851231ee 100644 (file)
@@ -999,30 +999,30 @@ static bool nvme_prep_rq_batch(struct nvme_queue *nvmeq, struct request *req)
 
 static void nvme_queue_rqs(struct request **rqlist)
 {
-       struct request *req = rq_list_peek(rqlist), *prev = NULL;
+       struct request *req, *next, *prev = NULL;
        struct request *requeue_list = NULL;
 
-       do {
+       rq_list_for_each_safe(rqlist, req, next) {
                struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
 
                if (!nvme_prep_rq_batch(nvmeq, req)) {
                        /* detach 'req' and add to remainder list */
-                       if (prev)
-                               prev->rq_next = req->rq_next;
-                       rq_list_add(&requeue_list, req);
-               } else {
-                       prev = req;
+                       rq_list_move(rqlist, &requeue_list, req, prev);
+
+                       req = prev;
+                       if (!req)
+                               continue;
                }
 
-               req = rq_list_next(req);
-               if (!req || (prev && req->mq_hctx != prev->mq_hctx)) {
+               if (!next || req->mq_hctx != next->mq_hctx) {
                        /* detach rest of list, and submit */
-                       if (prev)
-                               prev->rq_next = NULL;
+                       req->rq_next = NULL;
                        nvme_submit_cmds(nvmeq, rqlist);
-                       *rqlist = req;
-               }
-       } while (req);
+                       *rqlist = next;
+                       prev = NULL;
+               } else
+                       prev = req;
+       }
 
        *rqlist = requeue_list;
 }