From 782f9660079e5d38a4b9a51f2e18040435cc990d Mon Sep 17 00:00:00 2001 From: Ferry Meng Date: Tue, 12 May 2026 18:09:40 +0800 Subject: [PATCH 1/3] anolis: blk-mq: use __GFP_NOFAIL for dynamic rq alloc ANBZ: #35677 In blk_mq_rq_ctx_init(), when BLK_MQ_F_DYN_ALLOC is set and the tag is beyond nr_static_rqs, the code calls kmalloc() to allocate a new request structure but does not check the return value; the following dereference of tags->static_rqs[tag] would oops on allocation failure under memory pressure. Plumbing a NULL return up through all the __blk_mq_alloc_requests*() paths is intrusive and complicates the fast path. The allocation is small (sizeof(struct request) + cmd_size) and happens in a context where a blocking wait is acceptable, so simply add __GFP_NOFAIL to guarantee kmalloc() succeeds (possibly after reclaim). This removes the NULL-deref without any caller-side changes. Fixes: 06bdadbe6643 ("anolis: blk-mq: support dynamic request alloc") Signed-off-by: Ferry Meng Reviewed-by: Joseph Qi Link: https://gitee.com/anolis/cloud-kernel/pulls/6942 Signed-off-by: Ferry Meng --- block/blk-mq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/blk-mq.c b/block/blk-mq.c index 63df4b48768c..d72b96d469b6 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -329,7 +329,8 @@ static struct request *blk_mq_rq_ctx_init(struct blk_mq_alloc_data *data, if ((set->flags & BLK_MQ_F_DYN_ALLOC) && (tag >= set->nr_static_rqs)) tags->static_rqs[tag] = kmalloc(sizeof(struct request) + - set->cmd_size, GFP_KERNEL | __GFP_ZERO); + set->cmd_size, + GFP_KERNEL | __GFP_ZERO | __GFP_NOFAIL); rq = tags->static_rqs[tag]; -- Gitee From 2c90da74a3a09c395b52b1a0a8f5fd3cfc706e71 Mon Sep 17 00:00:00 2001 From: Ferry Meng Date: Tue, 12 May 2026 18:09:46 +0800 Subject: [PATCH 2/3] anolis: block: relabel sglist_last trackers in blk_rq_map_sg_bidir ANBZ: #35677 blk_rq_map_sg_bidir() maintains two parallel arrays used while walking the bidirectional request's bios: sglist[2] = { sglist_read, sglist_write }; sglist_last[2] = { &last_sg_write, &last_sg_read }; The inner helper __blk_bios_map_sg_bidir() indexes both arrays with the boolean 'write' flag derived from op_is_write(bio_op(bio)). With the original initialisation, index 1 (write=true) yields sglist_write but last_sg_read, and index 0 yields sglist_read but last_sg_write. Note that due to the specifics of how __blk_bvec_map_sg() is used -- it appends to sglist[write] while updating *sg[write] to point at the new tail -- this mislabel does NOT produce a functional bug: the WRITE bvecs end up writing the tail position into last_sg_read, and the READ bvecs into last_sg_write. The subsequent sg_mark_end(last_sg_write); /* actually the tail of the READ list */ sg_mark_end(last_sg_read); /* actually the tail of the WRITE list */ therefore still terminate both sglists correctly -- the two swaps cancel out. Only the variable names are wrong. However the mislabel is confusing and fragile (e.g. if a third consumer of the trackers is added, or the order of the sg_mark_end() calls is changed, the code will silently start corrupting end markers). Swap the sglist_last entries so the indexing matches sglist[]: index 0 -> read, index 1 -> write. This is a readability cleanup, not a runtime fix. Fixes: fb9c8a40b378 ("anolis: virtio-blk: add blk_rq_map_sg_bidirectional helper") Signed-off-by: Ferry Meng Reviewed-by: Joseph Qi Link: https://gitee.com/anolis/cloud-kernel/pulls/6942 Signed-off-by: Ferry Meng --- block/blk-merge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/blk-merge.c b/block/blk-merge.c index 4de085431fbf..e6f8367646fc 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -599,8 +599,8 @@ int blk_rq_map_sg_bidir(struct request_queue *q, struct request *rq, { int nsegs = 0; struct scatterlist *sglist[2] = {sglist_read, sglist_write}; - struct scatterlist *last_sg_write = NULL, *last_sg_read = NULL; - struct scatterlist **sglist_last[2] = {&last_sg_write, &last_sg_read}; + struct scatterlist *last_sg_read = NULL, *last_sg_write = NULL; + struct scatterlist **sglist_last[2] = {&last_sg_read, &last_sg_write}; if (rq->bio) nsegs = __blk_bios_map_sg_bidir(q, rq->bio, sglist, sglist_last); -- Gitee From 08581d69cfd1045a66e49ea36af2c9e13436563c Mon Sep 17 00:00:00 2001 From: Ferry Meng Date: Tue, 12 May 2026 18:10:25 +0800 Subject: [PATCH 3/3] anolis: virtio-blk: fix ring-pair init error handling ANBZ: #35677 Two issues exist in the ring-pair init_vq error path after virtio_find_vqs() has already succeeded: 1) vblk->num_vqs is assigned only in the success tail, after the cq_req allocation loop. If kmalloc_array() for cq_req fails at iteration i, 'goto out' reaches virtblk_kfree_vqs_cq_reqs(), but that helper iterates up to vblk->num_vqs, which is still 0 from probe initialisation. All previously-allocated cq_req arrays from earlier iterations are therefore leaked. 2) On cq_req allocation failure the virtqueues discovered by virtio_find_vqs() are not torn down: del_vqs() is currently only invoked after a virtblk_prefill_res() failure. The virtqueues are leaked on the cq_req failure path. Fix both by publishing vblk->num_vqs right after virtio_find_vqs() succeeds, so the shared cleanup in virtblk_kfree_vqs_cq_reqs() can walk all allocated slots, and by calling vdev->config->del_vqs() when the cq_req allocation fails. Fixes: f082416f33f4 ("anolis: virtio-blk: add ring pair support") Signed-off-by: Ferry Meng Reviewed-by: Joseph Qi Link: https://gitee.com/anolis/cloud-kernel/pulls/6942 Signed-off-by: Ferry Meng --- drivers/block/virtio_blk.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index ee7003eb3502..295c6cf8f537 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -1456,6 +1456,13 @@ static int init_vq_rpair(struct virtio_blk *vblk) if (err) goto out; + /* + * Publish num_vqs before the cq_req allocation loop so that the + * error path in virtblk_kfree_vqs_cq_reqs() can iterate over all + * already-allocated cq_req arrays when one of the allocations fails. + */ + vblk->num_vqs = num_vqs; + for (i = 0; i < num_vqs; i++) { vring_size = virtqueue_get_vring_size(vqs[i]); if ((i % VIRTBLK_RING_NUM) == VIRTBLK_RING_CQ) { @@ -1464,6 +1471,7 @@ static int init_vq_rpair(struct virtio_blk *vblk) GFP_KERNEL | __GFP_ZERO); if (!vblk->vqs[i].cq_req) { err = -ENOMEM; + vdev->config->del_vqs(vdev); goto out; } } else { @@ -1479,8 +1487,6 @@ static int init_vq_rpair(struct virtio_blk *vblk) if (err < 0) vdev->config->del_vqs(vdev); - vblk->num_vqs = num_vqs; - out: kfree(vqs); kfree(callbacks); -- Gitee