kernel null pointer at nvme_tcp_init_iter+0x7d/0xd0 [nvme_tcp]

Chaitanya Kulkarni Chaitanya.Kulkarni at wdc.com
Sun Feb 7 02:14:32 EST 2021


Sagi,

On 2/5/21 19:19, Yi Zhang wrote:
> Hello
>
> We found this kernel NULL pointer issue with latest linux-block/for-next and it's 100% reproduced, let me know if you need more info/testing, thanks 
>
> Kernel repo: https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git
> Commit: 11f8b6fd0db9 - Merge branch 'for-5.12/io_uring' into for-next
>
> Reproducer: blktests nvme-tcp/012
>
>
> [  124.458121] run blktests nvme/012 at 2021-02-05 21:53:34 
> [  125.525568] BUG: kernel NULL pointer dereference, address: 0000000000000008 
> [  125.532524] #PF: supervisor read access in kernel mode 
> [  125.537665] #PF: error_code(0x0000) - not-present page 
> [  125.542803] PGD 0 P4D 0  
> [  125.545343] Oops: 0000 [#1] SMP NOPTI 
> [  125.549009] CPU: 15 PID: 12069 Comm: kworker/15:2H Tainted: G S        I       5.11.0-rc6+ #1 
> [  125.557528] Hardware name: Dell Inc. PowerEdge R640/06NR82, BIOS 2.10.0 11/12/2020 
> [  125.565093] Workqueue: kblockd blk_mq_run_work_fn 
> [  125.569797] RIP: 0010:nvme_tcp_init_iter+0x7d/0xd0 [nvme_tcp] 
> [  125.575544] Code: 8b 75 68 44 8b 45 28 44 8b 7d 30 49 89 d4 48 c1 e2 04 4c 01 f2 45 89 fb 44 89 c7 85 ff 74 4d 44 89 e0 44 8b 55 10 48 c1 e0 04 <41> 8b 5c 06 08 45 0f b6 ca 89 d8 44 29 d8 39 f8 0f 47 c7 41 83 e9 
> [  125.594290] RSP: 0018:ffffbd084447bd18 EFLAGS: 00010246 
> [  125.599515] RAX: 0000000000000000 RBX: ffffa0bba9f3ce80 RCX: 0000000000000000 
> [  125.606648] RDX: 0000000000000000 RSI: 0000000000000001 RDI: 0000000002000000 
> [  125.613781] RBP: ffffa0ba8ac6fec0 R08: 0000000002000000 R09: 0000000000000000 
> [  125.620914] R10: 0000000002800809 R11: 0000000000000000 R12: 0000000000000000 
> [  125.628045] R13: ffffa0bba9f3cf90 R14: 0000000000000000 R15: 0000000000000000 
> [  125.635178] FS:  0000000000000000(0000) GS:ffffa0c9ff9c0000(0000) knlGS:0000000000000000 
> [  125.643264] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033 
> [  125.649009] CR2: 0000000000000008 CR3: 00000001c9c6c005 CR4: 00000000007706e0 
> [  125.656142] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 
> [  125.663274] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 
> [  125.670407] PKRU: 55555554 
> [  125.673119] Call Trace: 
> [  125.675575]  nvme_tcp_queue_rq+0xef/0x330 [nvme_tcp] 
> [  125.680537]  blk_mq_dispatch_rq_list+0x11c/0x7c0 
> [  125.685157]  ? blk_mq_flush_busy_ctxs+0xf6/0x110 
> [  125.689775]  __blk_mq_sched_dispatch_requests+0x12b/0x170 
> [  125.695175]  blk_mq_sched_dispatch_requests+0x30/0x60 
> [  125.700227]  __blk_mq_run_hw_queue+0x2b/0x60 
> [  125.704500]  process_one_work+0x1cb/0x360 
> [  125.708513]  ? process_one_work+0x360/0x360 
> [  125.712699]  worker_thread+0x30/0x370 
> [  125.716365]  ? process_one_work+0x360/0x360 
> [  125.720550]  kthread+0x116/0x130 
> [  125.723782]  ? kthread_park+0x80/0x80 
> [  125.727448]  ret_from_fork+0x1f/0x30 
The NVMe TCP does support merging for non-admin queues
(nvme_tcp_alloc_tagset()).

Based on the what is been done for the bvecs in other places in
kernel especially when merging is enabled bio split case seems to be
missing from tcp when building bvec. What I mean is following
completely untested patch :-

diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index 619b0d8f6e38..dabb2633b28c 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -222,7 +222,9 @@ static void nvme_tcp_init_iter(struct
nvme_tcp_request *req,
         unsigned int dir)
 {
     struct request *rq = blk_mq_rq_from_pdu(req);
-    struct bio_vec *vec;
+    struct req_iterator rq_iter;
+    struct bio_vec *vec, *tvec;
+    struct bio_vec tmp;
     unsigned int size;
     int nr_bvec;
     size_t offset;
@@ -233,17 +235,29 @@ static void nvme_tcp_init_iter(struct
nvme_tcp_request *req,
         size = blk_rq_payload_bytes(rq);
         offset = 0;
     } else {
-        struct bio *bio = req->curr_bio;
-        struct bvec_iter bi;
-        struct bio_vec bv;
-
-        vec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter);
-        nr_bvec = 0;
-        bio_for_each_bvec(bv, bio, bi) {
+        rq_for_each_bvec(tmp, rq, rq_iter)
             nr_bvec++;
+
+        if (rq->bio != rq->biotail) {
+            vec = kmalloc_array(nr_bvec, sizeof(struct bio_vec),
+                    GFP_NOIO);
+            if (!vec)
+                return;
+
+            tvec = vec;
+            rq_for_each_bvec(tmp, rq, rq_iter) {
+                *vec = tmp;
+                vec++;
+            }
+            vec = tvec;
+            offset = 0;
+        } else {
+            struct bio *bio = req->curr_bio;
+
+            vec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter);
+            size = bio->bi_iter.bi_size;
+            offset = bio->bi_iter.bi_bvec_done;
         }
-        size = bio->bi_iter.bi_size;
-        offset = bio->bi_iter.bi_bvec_done;
     }
 
     iov_iter_bvec(&req->iter, dir, vec, nr_bvec, size);
@@ -2271,8 +2285,11 @@ static blk_status_t nvme_tcp_setup_cmd_pdu(struct
nvme_ns *ns,
     req->data_len = blk_rq_nr_phys_segments(rq) ?
                 blk_rq_payload_bytes(rq) : 0;
     req->curr_bio = rq->bio;
-    if (req->curr_bio)
+    if (req->curr_bio) {
+        if (rq->bio != rq->biotail)
+            printk(KERN_INFO"%s %d req->bio != req->biotail\n",
__func__, __LINE__);
         nvme_tcp_init_iter(req, rq_data_dir(rq));
+    }
 
     if (rq_data_dir(rq) == WRITE &&
         req->data_len <= nvme_tcp_inline_data_size(queue))
-- 
2.22.1


Feel free to ignore this as I might be *completely wrong* here since I don't
have a reproducer, everything is working just fine on my machine.










More information about the Linux-nvme mailing list