* [PATCH v6 2/4] iomap: use BIO_COMPLETE_IN_TASK for dropbehind writeback
From: Tal Zussman @ 2026-05-14 21:51 UTC (permalink / raw)
To: Jens Axboe, Matthew Wilcox (Oracle), Christian Brauner,
Darrick J. Wong, Carlos Maiolino, Alexander Viro, Jan Kara,
Christoph Hellwig
Cc: Dave Chinner, Bart Van Assche, linux-block, linux-kernel,
linux-xfs, linux-fsdevel, linux-mm, Gao Xiang, Tal Zussman
In-Reply-To: <20260514-blk-dontcache-v6-0-782e2fa7477b@columbia.edu>
Set BIO_COMPLETE_IN_TASK on iomap writeback bios when a dropbehind folio
is added. This ensures that bi_end_io runs in task context, where
folio_end_dropbehind() can safely invalidate folios.
With the bio layer now handling task-context deferral generically,
IOMAP_IOEND_DONTCACHE is no longer needed, as XFS no longer needs to
route DONTCACHE ioends through its completion workqueue. Remove the flag
and its NOMERGE entry.
Without the NOMERGE, regular I/Os that get merged with a dropbehind
folio will also have their completion deferred to task context.
Signed-off-by: Tal Zussman <tz2294@columbia.edu>
---
fs/iomap/ioend.c | 5 +++--
fs/xfs/xfs_aops.c | 4 ----
include/linux/iomap.h | 5 +----
3 files changed, 4 insertions(+), 10 deletions(-)
diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c
index acf3cf98b23a..892dbfc77ae9 100644
--- a/fs/iomap/ioend.c
+++ b/fs/iomap/ioend.c
@@ -237,8 +237,6 @@ ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio,
if (wpc->iomap.flags & IOMAP_F_SHARED)
ioend_flags |= IOMAP_IOEND_SHARED;
- if (folio_test_dropbehind(folio))
- ioend_flags |= IOMAP_IOEND_DONTCACHE;
if (pos == wpc->iomap.offset && (wpc->iomap.flags & IOMAP_F_BOUNDARY))
ioend_flags |= IOMAP_IOEND_BOUNDARY;
@@ -255,6 +253,9 @@ ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio,
if (!bio_add_folio(&ioend->io_bio, folio, map_len, poff))
goto new_ioend;
+ if (folio_test_dropbehind(folio))
+ bio_set_flag(&ioend->io_bio, BIO_COMPLETE_IN_TASK);
+
/*
* Clamp io_offset and io_size to the incore EOF so that ondisk
* file size updates in the ioend completion are byte-accurate.
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index f279055fcea0..0dcf78beae8a 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -511,10 +511,6 @@ xfs_ioend_needs_wq_completion(
if (ioend->io_flags & (IOMAP_IOEND_UNWRITTEN | IOMAP_IOEND_SHARED))
return true;
- /* Page cache invalidation cannot be done in irq context. */
- if (ioend->io_flags & IOMAP_IOEND_DONTCACHE)
- return true;
-
return false;
}
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 2c5685adf3a9..fef04e01116f 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -399,16 +399,13 @@ sector_t iomap_bmap(struct address_space *mapping, sector_t bno,
#define IOMAP_IOEND_BOUNDARY (1U << 2)
/* is direct I/O */
#define IOMAP_IOEND_DIRECT (1U << 3)
-/* is DONTCACHE I/O */
-#define IOMAP_IOEND_DONTCACHE (1U << 4)
/*
* Flags that if set on either ioend prevent the merge of two ioends.
* (IOMAP_IOEND_BOUNDARY also prevents merges, but only one-way)
*/
#define IOMAP_IOEND_NOMERGE_FLAGS \
- (IOMAP_IOEND_SHARED | IOMAP_IOEND_UNWRITTEN | IOMAP_IOEND_DIRECT | \
- IOMAP_IOEND_DONTCACHE)
+ (IOMAP_IOEND_SHARED | IOMAP_IOEND_UNWRITTEN | IOMAP_IOEND_DIRECT)
/*
* Structure for writeback I/O completions.
--
2.39.5
^ permalink raw reply related
* [PATCH v6 3/4] buffer: add dropbehind writeback support
From: Tal Zussman @ 2026-05-14 21:51 UTC (permalink / raw)
To: Jens Axboe, Matthew Wilcox (Oracle), Christian Brauner,
Darrick J. Wong, Carlos Maiolino, Alexander Viro, Jan Kara,
Christoph Hellwig
Cc: Dave Chinner, Bart Van Assche, linux-block, linux-kernel,
linux-xfs, linux-fsdevel, linux-mm, Gao Xiang, Tal Zussman
In-Reply-To: <20260514-blk-dontcache-v6-0-782e2fa7477b@columbia.edu>
Add block_write_begin_iocb() which threads the kiocb through to
__filemap_get_folio() so that buffer_head-based I/O can use DONTCACHE
behavior. When the iocb has IOCB_DONTCACHE set, FGP_DONTCACHE is
passed to mark the folio for dropbehind. The existing
block_write_begin() is preserved as a wrapper that passes a NULL iocb.
Set BIO_COMPLETE_IN_TASK in submit_bh_wbc() when the folio has
dropbehind set, so that buffer_head writeback completions get deferred
to task context.
Signed-off-by: Tal Zussman <tz2294@columbia.edu>
---
fs/buffer.c | 19 +++++++++++++++++--
include/linux/buffer_head.h | 3 +++
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/fs/buffer.c b/fs/buffer.c
index b0b3792b1496..d0abaf44d782 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2138,14 +2138,19 @@ EXPORT_SYMBOL(block_commit_write);
*
* The filesystem needs to handle block truncation upon failure.
*/
-int block_write_begin(struct address_space *mapping, loff_t pos, unsigned len,
+int block_write_begin_iocb(const struct kiocb *iocb,
+ struct address_space *mapping, loff_t pos, unsigned len,
struct folio **foliop, get_block_t *get_block)
{
pgoff_t index = pos >> PAGE_SHIFT;
+ fgf_t fgp_flags = FGP_WRITEBEGIN;
struct folio *folio;
int status;
- folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
+ if (iocb && iocb->ki_flags & IOCB_DONTCACHE)
+ fgp_flags |= FGP_DONTCACHE;
+
+ folio = __filemap_get_folio(mapping, index, fgp_flags,
mapping_gfp_mask(mapping));
if (IS_ERR(folio))
return PTR_ERR(folio);
@@ -2160,6 +2165,13 @@ int block_write_begin(struct address_space *mapping, loff_t pos, unsigned len,
*foliop = folio;
return status;
}
+
+int block_write_begin(struct address_space *mapping, loff_t pos, unsigned len,
+ struct folio **foliop, get_block_t *get_block)
+{
+ return block_write_begin_iocb(NULL, mapping, pos, len, foliop,
+ get_block);
+}
EXPORT_SYMBOL(block_write_begin);
int block_write_end(loff_t pos, unsigned len, unsigned copied,
@@ -2715,6 +2727,9 @@ static void submit_bh_wbc(blk_opf_t opf, struct buffer_head *bh,
bio = bio_alloc(bh->b_bdev, 1, opf, GFP_NOIO);
+ if (folio_test_dropbehind(bh->b_folio))
+ bio_set_flag(bio, BIO_COMPLETE_IN_TASK);
+
if (IS_ENABLED(CONFIG_FS_ENCRYPTION))
buffer_set_crypto_ctx(bio, bh, GFP_NOIO);
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index e4939e33b4b5..4ce50882d621 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -260,6 +260,9 @@ int block_read_full_folio(struct folio *, get_block_t *);
bool block_is_partially_uptodate(struct folio *, size_t from, size_t count);
int block_write_begin(struct address_space *mapping, loff_t pos, unsigned len,
struct folio **foliop, get_block_t *get_block);
+int block_write_begin_iocb(const struct kiocb *iocb,
+ struct address_space *mapping, loff_t pos, unsigned len,
+ struct folio **foliop, get_block_t *get_block);
int __block_write_begin(struct folio *folio, loff_t pos, unsigned len,
get_block_t *get_block);
int block_write_end(loff_t pos, unsigned len, unsigned copied, struct folio *);
--
2.39.5
^ permalink raw reply related
* [PATCH v6 4/4] block: enable RWF_DONTCACHE for block devices
From: Tal Zussman @ 2026-05-14 21:51 UTC (permalink / raw)
To: Jens Axboe, Matthew Wilcox (Oracle), Christian Brauner,
Darrick J. Wong, Carlos Maiolino, Alexander Viro, Jan Kara,
Christoph Hellwig
Cc: Dave Chinner, Bart Van Assche, linux-block, linux-kernel,
linux-xfs, linux-fsdevel, linux-mm, Gao Xiang, Tal Zussman
In-Reply-To: <20260514-blk-dontcache-v6-0-782e2fa7477b@columbia.edu>
Block device buffered reads and writes already pass through
filemap_read() and iomap_file_buffered_write() respectively, both of
which handle IOCB_DONTCACHE. Enable RWF_DONTCACHE for block device files
by setting FOP_DONTCACHE in def_blk_fops.
For CONFIG_BUFFER_HEAD=y paths, use block_write_begin_iocb() in
blkdev_write_begin() to thread the kiocb through so that buffer_head
writeback gets dropbehind support.
CONFIG_BUFFER_HEAD=n paths are handled by the previously added iomap
BIO_COMPLETE_IN_TASK support.
This support is useful for databases that operate on raw block devices,
among other userspace applications.
Signed-off-by: Tal Zussman <tz2294@columbia.edu>
---
block/fops.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/block/fops.c b/block/fops.c
index bb6642b45937..31b073181d87 100644
--- a/block/fops.c
+++ b/block/fops.c
@@ -504,7 +504,8 @@ static int blkdev_write_begin(const struct kiocb *iocb,
unsigned len, struct folio **foliop,
void **fsdata)
{
- return block_write_begin(mapping, pos, len, foliop, blkdev_get_block);
+ return block_write_begin_iocb(iocb, mapping, pos, len, foliop,
+ blkdev_get_block);
}
static int blkdev_write_end(const struct kiocb *iocb,
@@ -966,7 +967,7 @@ const struct file_operations def_blk_fops = {
.splice_write = iter_file_splice_write,
.fallocate = blkdev_fallocate,
.uring_cmd = blkdev_uring_cmd,
- .fop_flags = FOP_BUFFER_RASYNC,
+ .fop_flags = FOP_BUFFER_RASYNC | FOP_DONTCACHE,
};
static __init int blkdev_init(void)
--
2.39.5
^ permalink raw reply related
* Re: [PATCH] Re:[PATCH v3] zram: fix use-after-free in zram_writeback_endio
From: Minchan Kim @ 2026-05-14 22:02 UTC (permalink / raw)
To: wang wei
Cc: richardycc, akpm, axboe, bgeffon, linux-block, linux-kernel,
linux-mm, liumartin, senozhatsky, stable
In-Reply-To: <20260513140218.7425-1-a929244872@163.com>
On Wed, May 13, 2026 at 10:02:18PM +0800, wang wei wrote:
> >@@ -847,7 +849,7 @@ static void release_wb_ctl(struct zram_wb_ctl *wb_ctl)
> > release_wb_req(req);
> > }
> >
> >- kfree(wb_ctl);
> >+ kfree_rcu(wb_ctl, rcu);
> > }
>
> Do we need to add a 'rcu_assign_pointer(wb_ctl, NULL);' before 'kfree_rcu(wb_ctl, rcu)'?
>
> Signed-off-by: wang wei <a929244872@163.com>
Why do we need it?
My understanding is rcu_assign_pointer() is typically used to publish NULL to
a shared pointer variable so that future RCU readers (using rcu_dereference)
won't access the object before kfree_rcu().
However, in our case, wb_ctl is not stored in any shared pointer variable.
It is a local variable in writeback_store() and RCU readers (zram_writeback_endio)
do not look up wb_ctl from a shared pointer. They obtain it directly from
bio->bi_private of the specific bio they are completing.
Please let me know if I missed anything.
^ permalink raw reply
* [PATCH v2] loop: Fix NULL pointer dereference by synchronizing lo_release and loop_queue_rq
From: Tetsuo Handa @ 2026-05-15 1:38 UTC (permalink / raw)
To: Bart Van Assche, Jens Axboe, Christoph Hellwig, Damien Le Moal
Cc: linux-block, LKML, Andrew Morton
In-Reply-To: <af2205e3-205e-4743-a767-8593c2c4747a@I-love.SAKURA.ne.jp>
The loop driver relies on lo_release() to automatically clear the loop
device via __loop_clr_fd() when the last file descriptor is closed
(LO_FLAGS_AUTOCLEAR). Although the backing file structure itself remains
allocated in memory thanks to proper file reference counting (f_count is
not zero), a severe race condition exists regarding the visibility of
the lo->lo_backing_file pointer.
This race window was exposed by commit 65565ca5f99b ("block: unify
the synchronous bi_end_io callbacks"). By unifying and optimizing
the synchronous I/O completion path, the timing and scheduling behavior of
the block layer altered significantly.
As a result, a highly-concurrent execution pipeline emerged where
lo_release() can progress to __loop_clr_fd() and nullify
lo->lo_backing_file while an already-scheduled asynchronous I/O work
(lo_rw_aio) is just about to be executed by a kworker thread.
Since the kworker enters lo_rw_aio() after lo->lo_backing_file has been
cleared, it attempts to dereference the now-NULL pointer when initializing
the kiocb, leading to the reported NULL pointer dereference bug.
To close this race safely without introducing heavy fast-path checks,
we must ensure that any running or scheduled dispatch threads have
completed before we nullify the pointer. Since loop_queue_rq() operates
within the block layer's RCU read-side critical section, invoke
synchronize_rcu() and drain_workqueue() in __loop_clr_fd() prior to
clearing lo->lo_backing_file.
Reported-by: syzbot+cd8a9a308e879a4e2c28@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=cd8a9a308e879a4e2c28
Reported-by: syzbot+bc273027d5643e48e5b3@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=bc273027d5643e48e5b3
Analyzed-by: AI Mode in Google Search (no mail address)
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
drivers/block/loop.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 0000913f7efc..ff117f340b2f 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1118,6 +1118,17 @@ static void __loop_clr_fd(struct loop_device *lo)
struct file *filp;
gfp_t gfp = lo->old_gfp_mask;
+ /*
+ * Now that loop_queue_rq() sees lo->lo_state != Lo_bound,
+ * wait for already started loop_queue_rq() to complete.
+ */
+ synchronize_rcu();
+ /*
+ * Now that no more works are scheduled by loop_queue_rq(),
+ * wait for already scheduled works to complete.
+ */
+ drain_workqueue(lo->workqueue);
+
spin_lock_irq(&lo->lo_lock);
filp = lo->lo_backing_file;
lo->lo_backing_file = NULL;
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v6 1/4] block: add task-context bio completion infrastructure
From: Hillf Danton @ 2026-05-15 2:38 UTC (permalink / raw)
To: Tal Zussman
Cc: Matthew Wilcox (Oracle), Christoph Hellwig, linux-block,
linux-kernel
In-Reply-To: <20260514-blk-dontcache-v6-1-782e2fa7477b@columbia.edu>
On Thu, 14 May 2026 17:51:14 -0400 Tal Zussman wrote:
> +
> +static void bio_complete_work_fn(struct work_struct *w)
> +{
> + struct delayed_work *dw = to_delayed_work(w);
> + struct bio_complete_batch *batch =
> + container_of(dw, struct bio_complete_batch, work);
> +
> + while (1) {
> + struct bio_list list;
> + struct bio *bio;
> +
> + local_lock_irq(&bio_complete_batch.lock);
> + list = batch->list;
> + bio_list_init(&batch->list);
> + local_unlock_irq(&bio_complete_batch.lock);
> +
> + if (bio_list_empty(&list))
> + break;
> +
> + while ((bio = bio_list_pop(&list)))
> + bio->bi_end_io(bio);
> +
> + if (need_resched()) {
> + bool is_empty;
> +
Checking resched is not needed as workqueue worker can be preempted
while processing bios.Given batch and delayed work, I suspect completing
more than batch, the bios accumulated within a jiff, makes sense.
> + local_lock_irq(&bio_complete_batch.lock);
> + is_empty = bio_list_empty(&batch->list);
> + local_unlock_irq(&bio_complete_batch.lock);
> + if (!is_empty)
> + mod_delayed_work_on(batch->cpu,
> + bio_complete_wq,
> + &batch->work, 0);
> + break;
> + }
> + }
> +}
> +
^ permalink raw reply
* Re: fix block layer bounce buffering for block size > PAGE_SIZE v2
From: Christoph Hellwig @ 2026-05-15 4:28 UTC (permalink / raw)
To: Jens Axboe
Cc: Christoph Hellwig, Christian Brauner, Darrick J. Wong,
Pankaj Raghav, linux-block, linux-xfs, linux-fsdevel
In-Reply-To: <30acedd3-e994-4810-8952-613b7d0bd9ca@kernel.dk>
On Wed, May 13, 2026 at 01:56:09PM -0600, Jens Axboe wrote:
> > Can we get some Fixes tags on these, please?
>
> Looked myself to not delay this any further, both marked with:
>
> Fixes: 8dd5e7c75d7b ("block: add helpers to bounce buffer an iov_iter into bios")
Sorry. The tag looks correct.
^ permalink raw reply
* Re: [PATCH V4 0/3] md/nvme: Enable PCI P2PDMA support for RAID0 and NVMe Multipath
From: Christoph Hellwig @ 2026-05-15 4:35 UTC (permalink / raw)
To: Chaitanya Kulkarni
Cc: song, yukuai, linan122, kbusch, axboe, sagi, linux-block,
linux-raid, linux-nvme, kmodukuri
In-Reply-To: <20260513185153.95552-1-kch@nvidia.com>
Still looks good to me as per the reviews.
^ permalink raw reply
* remove dead code and export
From: Christoph Hellwig @ 2026-05-15 4:55 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block
Hi Jens,
this series removes some minor dead code and dead exports in the block code.
Diffstat:
block/bio.c | 44 ++++++++++++++++++--------------------------
block/blk-core.c | 2 --
block/blk.h | 2 ++
include/linux/bio.h | 9 +--------
include/linux/blkdev.h | 1 -
5 files changed, 21 insertions(+), 37 deletions(-)
^ permalink raw reply
* [PATCH 1/5] block: remove zero_fill_bio_iter
From: Christoph Hellwig @ 2026-05-15 4:55 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block
In-Reply-To: <20260515045547.3790129-1-hch@lst.de>
Only used to implement zero_fill_bio, so directly implement that.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
block/bio.c | 6 +++---
include/linux/bio.h | 7 +------
2 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/block/bio.c b/block/bio.c
index b8972dba68a0..b990c453d72f 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -635,15 +635,15 @@ struct bio *bio_kmalloc(unsigned short nr_vecs, gfp_t gfp_mask)
}
EXPORT_SYMBOL(bio_kmalloc);
-void zero_fill_bio_iter(struct bio *bio, struct bvec_iter start)
+void zero_fill_bio(struct bio *bio)
{
struct bio_vec bv;
struct bvec_iter iter;
- __bio_for_each_segment(bv, bio, iter, start)
+ bio_for_each_segment(bv, bio, iter)
memzero_bvec(&bv);
}
-EXPORT_SYMBOL(zero_fill_bio_iter);
+EXPORT_SYMBOL(zero_fill_bio);
/**
* bio_truncate - truncate the bio to small size of @new_size
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 97d747320b35..84643fc0fb08 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -482,13 +482,8 @@ extern void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter,
struct bio *src, struct bvec_iter *src_iter);
extern void bio_copy_data(struct bio *dst, struct bio *src);
extern void bio_free_pages(struct bio *bio);
+void zero_fill_bio(struct bio *bio);
void guard_bio_eod(struct bio *bio);
-void zero_fill_bio_iter(struct bio *bio, struct bvec_iter iter);
-
-static inline void zero_fill_bio(struct bio *bio)
-{
- zero_fill_bio_iter(bio, bio->bi_iter);
-}
static inline void bio_release_pages(struct bio *bio, bool mark_dirty)
{
--
2.53.0
^ permalink raw reply related
* [PATCH 2/5] block: remove bio_copy_data_iter
From: Christoph Hellwig @ 2026-05-15 4:55 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block
In-Reply-To: <20260515045547.3790129-1-hch@lst.de>
Only used by bio_copy_data, so implement that directly.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
block/bio.c | 36 +++++++++++++++---------------------
include/linux/bio.h | 2 --
2 files changed, 15 insertions(+), 23 deletions(-)
diff --git a/block/bio.c b/block/bio.c
index b990c453d72f..57d5a87b3e2f 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1575,26 +1575,6 @@ void __bio_advance(struct bio *bio, unsigned bytes)
}
EXPORT_SYMBOL(__bio_advance);
-void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter,
- struct bio *src, struct bvec_iter *src_iter)
-{
- while (src_iter->bi_size && dst_iter->bi_size) {
- struct bio_vec src_bv = bio_iter_iovec(src, *src_iter);
- struct bio_vec dst_bv = bio_iter_iovec(dst, *dst_iter);
- unsigned int bytes = min(src_bv.bv_len, dst_bv.bv_len);
- void *src_buf = bvec_kmap_local(&src_bv);
- void *dst_buf = bvec_kmap_local(&dst_bv);
-
- memcpy(dst_buf, src_buf, bytes);
-
- kunmap_local(dst_buf);
- kunmap_local(src_buf);
-
- bio_advance_iter_single(src, src_iter, bytes);
- bio_advance_iter_single(dst, dst_iter, bytes);
- }
-}
-EXPORT_SYMBOL(bio_copy_data_iter);
/**
* bio_copy_data - copy contents of data buffers from one bio to another
@@ -1609,7 +1589,21 @@ void bio_copy_data(struct bio *dst, struct bio *src)
struct bvec_iter src_iter = src->bi_iter;
struct bvec_iter dst_iter = dst->bi_iter;
- bio_copy_data_iter(dst, &dst_iter, src, &src_iter);
+ while (src_iter.bi_size && dst_iter.bi_size) {
+ struct bio_vec src_bv = bio_iter_iovec(src, src_iter);
+ struct bio_vec dst_bv = bio_iter_iovec(dst, dst_iter);
+ unsigned int bytes = min(src_bv.bv_len, dst_bv.bv_len);
+ void *src_buf = bvec_kmap_local(&src_bv);
+ void *dst_buf = bvec_kmap_local(&dst_bv);
+
+ memcpy(dst_buf, src_buf, bytes);
+
+ kunmap_local(dst_buf);
+ kunmap_local(src_buf);
+
+ bio_advance_iter_single(src, &src_iter, bytes);
+ bio_advance_iter_single(dst, &dst_iter, bytes);
+ }
}
EXPORT_SYMBOL(bio_copy_data);
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 84643fc0fb08..85463981d0f5 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -478,8 +478,6 @@ extern void bio_check_pages_dirty(struct bio *bio);
int bio_iov_iter_bounce(struct bio *bio, struct iov_iter *iter, size_t maxlen);
void bio_iov_iter_unbounce(struct bio *bio, bool is_error, bool mark_dirty);
-extern void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter,
- struct bio *src, struct bvec_iter *src_iter);
extern void bio_copy_data(struct bio *dst, struct bio *src);
extern void bio_free_pages(struct bio *bio);
void zero_fill_bio(struct bio *bio);
--
2.53.0
^ permalink raw reply related
* [PATCH 3/5] block: unexport blk_io_schedule
From: Christoph Hellwig @ 2026-05-15 4:55 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block
In-Reply-To: <20260515045547.3790129-1-hch@lst.de>
Only used in built-in code.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
block/blk-core.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index 17450058ea6d..d7de87e86994 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1270,7 +1270,6 @@ void blk_io_schedule(void)
else
io_schedule();
}
-EXPORT_SYMBOL_GPL(blk_io_schedule);
int __init blk_dev_init(void)
{
--
2.53.0
^ permalink raw reply related
* [PATCH 4/5] block: unexport blk_status_to_str
From: Christoph Hellwig @ 2026-05-15 4:55 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block
In-Reply-To: <20260515045547.3790129-1-hch@lst.de>
Only used in core block code, so unexport and move the prototype to
blk.h.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
block/blk-core.c | 1 -
block/blk.h | 2 ++
include/linux/blkdev.h | 1 -
3 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index d7de87e86994..22af5dec112b 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -197,7 +197,6 @@ const char *blk_status_to_str(blk_status_t status)
return "<null>";
return blk_errors[idx].name;
}
-EXPORT_SYMBOL_GPL(blk_status_to_str);
/**
* blk_sync_queue - cancel any pending callbacks on a queue
diff --git a/block/blk.h b/block/blk.h
index b998a7761faf..bf1a80493ff1 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -49,6 +49,8 @@ struct blk_flush_queue *blk_alloc_flush_queue(int node, int cmd_size,
gfp_t flags);
void blk_free_flush_queue(struct blk_flush_queue *q);
+const char *blk_status_to_str(blk_status_t status);
+
bool __blk_mq_unfreeze_queue(struct request_queue *q, bool force_atomic);
bool blk_queue_start_drain(struct request_queue *q);
bool __blk_freeze_queue_start(struct request_queue *q,
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 890128cdea1c..17270a28c66d 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1040,7 +1040,6 @@ extern const char *blk_op_str(enum req_op op);
int blk_status_to_errno(blk_status_t status);
blk_status_t errno_to_blk_status(int errno);
-const char *blk_status_to_str(blk_status_t status);
/* only poll the hardware once, don't continue until a completion was found */
#define BLK_POLL_ONESHOT (1 << 0)
--
2.53.0
^ permalink raw reply related
* [PATCH 5/5] block: unexport bio_{set,check}_pages_dirty
From: Christoph Hellwig @ 2026-05-15 4:55 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block
In-Reply-To: <20260515045547.3790129-1-hch@lst.de>
Only used in built-in code.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
block/bio.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/block/bio.c b/block/bio.c
index 57d5a87b3e2f..2d880d1255fe 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1650,7 +1650,6 @@ void bio_set_pages_dirty(struct bio *bio)
folio_unlock(fi.folio);
}
}
-EXPORT_SYMBOL_GPL(bio_set_pages_dirty);
/*
* bio_check_pages_dirty() will check that all the BIO's pages are still dirty.
@@ -1709,7 +1708,6 @@ void bio_check_pages_dirty(struct bio *bio)
spin_unlock_irqrestore(&bio_dirty_lock, flags);
schedule_work(&bio_dirty_work);
}
-EXPORT_SYMBOL_GPL(bio_check_pages_dirty);
static inline bool bio_remaining_done(struct bio *bio)
{
--
2.53.0
^ permalink raw reply related
* [PATCH blktests] nbd/rc: redirect nbd-server stderr
From: Shin'ichiro Kawasaki @ 2026-05-15 5:30 UTC (permalink / raw)
To: linux-block, nbd; +Cc: Shin'ichiro Kawasaki
Recent nbd commit da5e07c057ab ("Reimplement daemonize() without using
daemon()") changed how nbd-server daemonizes itself. After this commit,
nbd-server prints messages to stderr after daemonization. This caused
nbd test cases to fail due to unexpected stderr output.
nbd/001 (resize a connected nbd device) [failed]
runtime 0.865s ... 0.883s
--- tests/nbd/001.out 2025-04-22 13:13:27.727873155 +0900
+++ /home/shin/Blktests/blktests/results/nodev/nbd/001.out.bad 2026-05-15 11:44:46.269000000 +0900
@@ -1,4 +1,5 @@
Running nbd/001
+Error: Session terminated by client
Disk /dev/nbd0: 10.7GB
nbd0 43:0 0 10G 0 disk
Setting size to 1gib
The commit is in nbd version v3.27, which is included in Fedora 44.
To avoid the failures, redirect nbd-server stderr to .full files.
Link: https://github.com/NetworkBlockDevice/nbd/commit/da5e07c057ab
Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
---
tests/nbd/rc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/nbd/rc b/tests/nbd/rc
index e200ba6..6205c49 100644
--- a/tests/nbd/rc
+++ b/tests/nbd/rc
@@ -72,7 +72,7 @@ allowlist=true
[export]
exportname=${TMPDIR}/export
EOF
- nbd-server -p "${TMPDIR}/nbd.pid" -C "${TMPDIR}/nbd.conf"
+ nbd-server -p "${TMPDIR}/nbd.pid" -C "${TMPDIR}/nbd.conf" 2> "$FULL"
# Wait for nbd-server start listening the port
for ((i = 0; i < 100; i++)); do
--
2.54.0
^ permalink raw reply related
* Re: [PATCH] sched: flush plug in schedule_preempt_disabled() to prevent deadlock
From: Xiaosen @ 2026-05-15 6:18 UTC (permalink / raw)
To: Ming Lei, Peter Zijlstra
Cc: Tejun Heo, Jens Axboe, linux-block, linux-kernel, Ingo Molnar,
Juri Lelli, Vincent Guittot, Michael Wu, Thomas Gleixner
In-Reply-To: <agQxg-pBzF8rp1Ii@fedora>
https://lore.kernel.org/lkml/20260427183848.698551-2-jstultz@google.com/
The above change can resolve the deadlock I reported before by setting
task's state to TASK_RUNNING before switching context.
There is the likely alternative fix below.
https://lore.kernel.org/lkml/20260512025635.2840817-1-jstultz@google.com/
Regards,
Xiaosen
On 5/13/2026 4:08 PM, Ming Lei wrote:
> On Wed, May 13, 2026 at 09:30:39AM +0200, Peter Zijlstra wrote:
>> On Wed, May 13, 2026 at 10:07:03AM +0800, Ming Lei wrote:
>>> On Tue, May 12, 2026 at 07:16:36AM -1000, Tejun Heo wrote:
>>>> Hello, Ming.
>>>>
>>>> On Tue, May 12, 2026 at 11:45:14PM +0800, Ming Lei wrote:
>>>>> On Tue, May 12, 2026 at 02:40:21PM +0200, Peter Zijlstra wrote:
>>>>>> On Tue, May 12, 2026 at 02:04:32PM +0200, Peter Zijlstra wrote:
>>>>>>> On Tue, May 12, 2026 at 04:59:39PM +0800, Ming Lei wrote:
>>>>>>>> On preemptible kernels, a deadlock can occur when a task with plugged IO
>>>>>>>> calls schedule_preempt_disabled():
>>>>>>>>
>>>>>>>> schedule_preempt_disabled()
>>>>>>>> sched_preempt_enable_no_resched() // preemption now enabled
>>>>>>>> schedule() // <-- preemption can happen here
>>>>>>>> sched_submit_work()
>>>>>>>> blk_flush_plug()
>>>>>>>>
>>>>>>>> After sched_preempt_enable_no_resched() re-enables preemption, the task
>>>>>>>> can be preempted (e.g., by a higher-priority RT task) before reaching
>>>>>>>> blk_flush_plug() in sched_submit_work(). Since the task's state is
>>>>>>>> already TASK_UNINTERRUPTIBLE (set by the mutex/rwsem slowpath caller),
>>>>>>>> requests in current->plug remain unflushed for an unbounded time.
>>>>>>>>
>>>>>>>> If another task depends on those plugged requests to make progress (e.g.,
>>>>>>>> to release a lock the sleeping task needs), a deadlock results:
>>>>>>>>
>>>>>>>> - Task A (writeback worker): holds plugged IO, preempted before
>>>>>>>> flushing, stuck on run queue behind higher-priority work
>>>>>>>> - Task B: waiting for IO completion from Task A's plug, holds a lock
>>>>>>>> that Task A needs to be woken up
>>>>
>>>> My memory is hazy around io_schedule but the above reads really weird to me.
>>>> A task, regardless of its current state stays on the runqueue when
>>>> preempted, so the condition is temporary. As soon as the preempted task can
>>>> get CPU, it should unwind the situation. That's not a deadlock. Is the
>>>> problem that there can be preemption-induced delay in flushing the plugs?
>>>
>>> IMO, preempting a `!TASK_RUNNING` task can be thought as effective sleep,
>>
>> No it cannot be. Preemption ignores task state.
>
> Yeah, I get similar conclusion too with AI's assistance.
>
> But both two reports show that the preempted task aren't switched back for
> long enough time, can you share any idea for Michael & Xiaosen to investigate
> further from scheduler side?
>
> https://lore.kernel.org/linux-block/20260417082744.30124-1-michael@allwinnertech.com/
>
> https://lore.kernel.org/linux-block/5660795d-87de-46f5-add4-7729a02225ef@oss.qualcomm.com/
>
>
> Thanks,
> Ming
^ permalink raw reply
* [PATCH] blk-cgroup: Fix UAF in blkcg_activate_policy() by using blkg_tryget()
From: Zizhi Wo @ 2026-05-15 6:15 UTC (permalink / raw)
To: axboe, tj, josef, yukuai, linux-block
Cc: cgroups, yangerkun, chengzhihao1, wozizhi
[BUG]
Our fuzz testing triggered a blkg use-after-free issue:
BUG: KASAN: slab-use-after-free in percpu_ref_put_many.constprop.0+0xbe/0xe0
Call Trace:
...
blkcg_activate_policy+0x347/0xfa0
bfq_create_group_hierarchy+0x5b/0x140
bfq_init_queue+0xc1b/0x1470
? mutex_init_generic+0x9f/0x100
? elevator_alloc+0x166/0x2b0
blk_mq_init_sched+0x2b0/0x730
elevator_switch+0x188/0x450
elevator_change+0x290/0x470
elv_iosched_store+0x30a/0x3a0
...
[CAUSE]
process1 process2
cgroup_rmdir
...
blkcg_destroy_blkgs
spin_trylock(&q->queue_lock)
blkg_destroy
percpu_ref_kill(&blkg->refcnt)
...
blkg_free
INIT_WORK(xxx, blkg_free_workfn)
schedule_work
spin_unlock(&q->queue_lock)
====================================schedule_work
blkg_free_workfn
elevator_change
...
bfq_create_group_hierarchy
blkcg_activate_policy
spin_lock_irq(&q->queue_lock)
blkg_get // get dead ref !!
pinned_blkg = blkg
spin_unlock_irq(&q->queue_lock)
spin_lock_irq(&q->queue_lock)
list_del_init(&blkg->q_node)
spin_unlock_irq(&q->queue_lock)
kfree(blkg)
blkg_put(pinned_blkg) // UAF !!
A blkg killed by blkg_destroy() stays on q->blkg_list until
blkg_free_workfn() grabs queue_lock and unlinks it. blkg_get() on a dead
percpu_ref does not resurrect the blkg, so the later blkg_put() hits freed
memory and triggers this issue.
[Fix]
Replace blkg_get() with blkg_tryget(), which fails on a dead ref and lets
the loop skip dying blkgs.
Also hoist the ref acquisition to the top of the loop so dying blkgs are
filtered out before a pd is allocated and attached. Otherwise a pd attached
to an already-destroyed blkg would never called pd_offline_fn().
Fixes: 9d179b865449 ("blkcg: Fix multiple bugs in blkcg_activate_policy()")
Signed-off-by: Zizhi Wo <wozizhi@huaweicloud.com>
---
block/blk-cgroup.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index 554c87bb4a86..03b6ce934848 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -1621,6 +1621,10 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)
if (blkg->pd[pol->plid])
continue;
+ /* a destroyed blkg may still be on q->blkg_list; skip it via tryget */
+ if (!blkg_tryget(blkg))
+ continue;
+
/* If prealloc matches, use it; otherwise try GFP_NOWAIT */
if (blkg == pinned_blkg) {
pd = pd_prealloc;
@@ -1637,7 +1641,6 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)
*/
if (pinned_blkg)
blkg_put(pinned_blkg);
- blkg_get(blkg);
pinned_blkg = blkg;
spin_unlock_irq(&q->queue_lock);
@@ -1666,6 +1669,8 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)
pd->online = true;
spin_unlock(&blkg->blkcg->lock);
+
+ blkg_put(blkg);
}
__set_bit(pol->plid, q->blkcg_pols);
--
2.52.0
^ permalink raw reply related
* Re: [PATCH blktests v3] nvme/068: add a test for multipath delayed removal
From: Shin'ichiro Kawasaki @ 2026-05-15 6:38 UTC (permalink / raw)
To: John Garry; +Cc: linux-block, linux-nvme, nilay, dwagner, Chaitanya Kulkarni
In-Reply-To: <20260430084635.2438048-1-john.g.garry@oracle.com>
On Apr 30, 2026 / 08:46, John Garry wrote:
> For NVMe multipath, the delayed removal feature allows the multipath
> gendisk to remain present when all available paths are gone. The purpose of
> this feature is to ensure that we keep the gendisk for intermittent path
> failures.
>
> The delayed removal works on a timer - when all paths are gone, a timer is
> kicked off; once the timer expires and no paths have returned, the gendisk
> is removed.
>
> When all paths are gone and the gendisk is still present, all reads and
> writes to the disk are queued. If a path returns before the timer
> expiration, the timer canceled and the queued IO is submitted;
> otherwise they fail when the timer expires.
>
> This testcase covers two scenarios in separate parts:
> a. test that IOs submitted after all paths are removed (and do not return)
> fail
> b. test that IOs submitted between all paths removed and a path
> returning succeed
>
> During the period of the timer being active, it must be ensured that the
> nvme-core module is not removed. Otherwise the driver may not be present
> to handle the timeout expiry. The kernel ensures this by taking a
> reference to the module. Ideally, we would try to remove the module during
> this test to prove that this is not possible (and the kernel behaves as
> expected), but that module will probably not be removable anyway due to
> many references. To test this feature, check that the refcount of the
> nvme-core module is incremented when the delayed timer is active.
>
> Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>
> Reviewed-by: Nilay Shroff <nilay@linux.ibm.com>
> Signed-off-by: John Garry <john.g.garry@oracle.com>
I applied this patch. Thanks!
^ permalink raw reply
* Re: [PATCH] blk-cgroup: Fix UAF in blkcg_activate_policy() by using blkg_tryget()
From: Zizhi Wo @ 2026-05-15 10:16 UTC (permalink / raw)
To: Zizhi Wo, axboe, tj, josef, yukuai, linux-block
Cc: cgroups, yangerkun, chengzhihao1
In-Reply-To: <20260515061516.3461291-1-wozizhi@huaweicloud.com>
I just realized that the fix has already been included in this patchset:
https://lore.kernel.org/all/20260304073809.3438679-5-yukuai@fnnas.com/
Please disregard this patch.
在 2026/5/15 14:15, Zizhi Wo 写道:
> [BUG]
> Our fuzz testing triggered a blkg use-after-free issue:
>
> BUG: KASAN: slab-use-after-free in percpu_ref_put_many.constprop.0+0xbe/0xe0
> Call Trace:
> ...
> blkcg_activate_policy+0x347/0xfa0
> bfq_create_group_hierarchy+0x5b/0x140
> bfq_init_queue+0xc1b/0x1470
> ? mutex_init_generic+0x9f/0x100
> ? elevator_alloc+0x166/0x2b0
> blk_mq_init_sched+0x2b0/0x730
> elevator_switch+0x188/0x450
> elevator_change+0x290/0x470
> elv_iosched_store+0x30a/0x3a0
> ...
>
> [CAUSE]
> process1 process2
> cgroup_rmdir
> ...
> blkcg_destroy_blkgs
> spin_trylock(&q->queue_lock)
> blkg_destroy
> percpu_ref_kill(&blkg->refcnt)
> ...
> blkg_free
> INIT_WORK(xxx, blkg_free_workfn)
> schedule_work
> spin_unlock(&q->queue_lock)
> ====================================schedule_work
> blkg_free_workfn
> elevator_change
> ...
> bfq_create_group_hierarchy
> blkcg_activate_policy
> spin_lock_irq(&q->queue_lock)
> blkg_get // get dead ref !!
> pinned_blkg = blkg
> spin_unlock_irq(&q->queue_lock)
> spin_lock_irq(&q->queue_lock)
> list_del_init(&blkg->q_node)
> spin_unlock_irq(&q->queue_lock)
> kfree(blkg)
> blkg_put(pinned_blkg) // UAF !!
>
> A blkg killed by blkg_destroy() stays on q->blkg_list until
> blkg_free_workfn() grabs queue_lock and unlinks it. blkg_get() on a dead
> percpu_ref does not resurrect the blkg, so the later blkg_put() hits freed
> memory and triggers this issue.
>
> [Fix]
> Replace blkg_get() with blkg_tryget(), which fails on a dead ref and lets
> the loop skip dying blkgs.
>
> Also hoist the ref acquisition to the top of the loop so dying blkgs are
> filtered out before a pd is allocated and attached. Otherwise a pd attached
> to an already-destroyed blkg would never called pd_offline_fn().
>
> Fixes: 9d179b865449 ("blkcg: Fix multiple bugs in blkcg_activate_policy()")
> Signed-off-by: Zizhi Wo <wozizhi@huaweicloud.com>
> ---
> block/blk-cgroup.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
> index 554c87bb4a86..03b6ce934848 100644
> --- a/block/blk-cgroup.c
> +++ b/block/blk-cgroup.c
> @@ -1621,6 +1621,10 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)
> if (blkg->pd[pol->plid])
> continue;
>
> + /* a destroyed blkg may still be on q->blkg_list; skip it via tryget */
> + if (!blkg_tryget(blkg))
> + continue;
> +
> /* If prealloc matches, use it; otherwise try GFP_NOWAIT */
> if (blkg == pinned_blkg) {
> pd = pd_prealloc;
> @@ -1637,7 +1641,6 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)
> */
> if (pinned_blkg)
> blkg_put(pinned_blkg);
> - blkg_get(blkg);
> pinned_blkg = blkg;
>
> spin_unlock_irq(&q->queue_lock);
> @@ -1666,6 +1669,8 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)
> pd->online = true;
>
> spin_unlock(&blkg->blkcg->lock);
> +
> + blkg_put(blkg);
> }
>
> __set_bit(pol->plid, q->blkcg_pols);
^ permalink raw reply
* Re: improve the swap_activate interface
From: Christoph Hellwig @ 2026-05-15 11:33 UTC (permalink / raw)
To: Steve French
Cc: Christoph Hellwig, Andrew Morton, Chris Li, Kairui Song,
Christian Brauner, Darrick J . Wong, Jens Axboe, David Sterba,
Theodore Ts'o, Jaegeuk Kim, Chao Yu, Trond Myklebust,
Anna Schumaker, Namjae Jeon, Hyunchul Lee, Steve French,
Paulo Alcantara, Carlos Maiolino, Damien Le Moal, Naohiro Aota,
linux-xfs, linux-fsdevel, linux-doc, linux-mm, linux-block,
linux-btrfs, linux-ext4, linux-f2fs-devel, linux-nfs, linux-cifs
In-Reply-To: <CAH2r5msnYVb3hhXHwqDVHGGC1h4E6mLCRS4ktCrQoD9zdUW81g@mail.gmail.com>
On Wed, May 13, 2026 at 03:34:03PM -0500, Steve French wrote:
> I just tried this on 7.1-rc3 with the swap patches (full kernel build,
> on Ubuntu 25,10) and boot failed with out of memory which I had never
> seen before. Any idea how to workaround this with the swap patch
> series, or is there a fix for this in the swap series already?
Is that a failure with the patches or also with the baseline?
^ permalink raw reply
* Re: [PATCH 0/1] drbd: fix false positive resync throttling (in-tree 8.4)
From: Ionut Nechita (Wind River) @ 2026-05-15 12:30 UTC (permalink / raw)
To: drbd-dev, linux-block
Cc: Philipp Reisner, Christoph Boehmwalder, Lars Ellenberg,
Jens Axboe, linux-kernel, Ionut Nechita
In-Reply-To: <20260513163905.562722-1-ionut.nechita@windriver.com>
From: Ionut Nechita <ionut.nechita@windriver.com>
Hi Philipp, Christoph,
Friendly ping on this patch. We are planning to integrate it into our
next release and our engineering team asked whether you could provide
a brief risk assessment or a Reviewed-by tag for the in-tree 8.4
backport.
To summarize the risk profile from our side:
- The change is confined to drbd_rs_c_min_rate_throttle() — only the
throttle decision, no data path or replication protocol changes.
- Worst-case failure mode (ap_bio_cnt not reflecting reality) is
equivalent to c-min-rate=0, which is already a supported config.
- The approach is identical to what drbd 9.x already uses.
Could you confirm the patch is correct as-is, or provide a Reviewed-by
/ Acked-by if you're comfortable with it? If there are any concerns
or suggested changes, happy to spin a v3.
Thanks,
Ionut
^ permalink raw reply
* Re: [PATCH] block: bio-integrity: fix memory leak in bio_integrity_map_user()
From: Christoph Hellwig @ 2026-05-15 13:13 UTC (permalink / raw)
To: Dmitry Antipov
Cc: Jens Axboe, Weidong Zhu, Chao Shi, Sungwoo Kim, Dave Tian,
Keith Busch, Caleb Sander Mateos, Christoph Hellwig, linux-block,
lvc-project, Kanchan Joshi, Anuj Gupta
In-Reply-To: <20260513070515.528861-1-dmantipov@yandex.ru>
On Wed, May 13, 2026 at 10:05:15AM +0300, Dmitry Antipov wrote:
> Since 'iov_iter_extract_pages()' may allocate new array of pages
> even when it returns non-zero error value, matching cleanup with
> 'vfree()' should be performed on all return paths afterwards. So
> adjust 'bio_integrity_map_user()' to ensure that both 'pages' and
> 'bvec' arrays are always freed on return.
>
> Fixes: 8582792cf23b ("block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user()")
> Fixes: 492c5d455969 ("block: bio-integrity: directly map user buffers")
> Signed-off-by: Dmitry Antipov <dmantipov@yandex.ru>
The change looks fine:
Reviewed-by: Christoph Hellwig <hch@lst.de>
We'd still be much better off converting it to iov_iter_extract_bvecs
to share common code, and make it more efficient by no needing the
separate pages allocation.
> ---
> block/bio-integrity.c | 21 ++++++---------------
> 1 file changed, 6 insertions(+), 15 deletions(-)
>
> diff --git a/block/bio-integrity.c b/block/bio-integrity.c
> index e796de1a749e..53fb04adb09b 100644
> --- a/block/bio-integrity.c
> +++ b/block/bio-integrity.c
> @@ -400,7 +400,7 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter)
> ret = iov_iter_extract_pages(iter, &pages, bytes, nr_vecs,
> extraction_flags, &offset);
> if (unlikely(ret < 0))
> - goto free_bvec;
> + goto out_free;
>
> /*
> * Handle partial pinning. This can happen when pin_user_pages_fast()
> @@ -414,16 +414,12 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter)
> for (i = 0; i < npinned; i++)
> unpin_user_page(pages[i]);
> }
> - if (pages != stack_pages)
> - kvfree(pages);
> ret = -EFAULT;
> - goto free_bvec;
> + goto out_free;
> }
>
> nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset,
> &is_p2p);
> - if (pages != stack_pages)
> - kvfree(pages);
> if (nr_bvecs > queue_max_integrity_segments(q))
> copy = true;
> if (is_p2p)
> @@ -434,15 +430,10 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter)
> else
> ret = bio_integrity_init_user(bio, bvec, nr_bvecs, bytes);
> if (ret)
> - goto release_pages;
> - if (bvec != stack_vec)
> - kfree(bvec);
> -
> - return 0;
> -
> -release_pages:
> - bio_integrity_unpin_bvec(bvec, nr_bvecs);
> -free_bvec:
> + bio_integrity_unpin_bvec(bvec, nr_bvecs);
> +out_free:
> + if (pages != stack_pages)
> + kvfree(pages);
> if (bvec != stack_vec)
> kfree(bvec);
> return ret;
> --
> 2.54.0
---end quoted text---
^ permalink raw reply
* [GIT PULL] Block fixes for 7.1-rc4
From: Jens Axboe @ 2026-05-15 14:20 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linux-block@vger.kernel.org
Hi Linus,
Set of fixes for the block area, which should go into the 7.1 kernel
release. This pull request contains:
- NVMe merge request via Keith
- Fix memory leak on a passthrough integrity mapping failure
(Keith)
- Hide secrets behind debug option (Hannes)
- Fix pci use-after-free for host memory buffer (Chia-Lin Kao)
- Fix tcp taregt use-after-free for data digest (Sagi)
- Revert a mistaken quirk (Alan Cui)
- Fix uevent and controller state race condition (Maurizio)
- Fix apple submission queue re-initialization (Nick Chan)
- Three fixes for blk-integrity, fixing an issue with the user data
mapping and two problems with recomputing number of segments.
- Two fixes for the iov_iter bounce buffering.
- Fix for the handling of dead zoned write plugs.
- ublk max_sectors validation fix, with associated selftest addition.
Please pull!
The following changes since commit f7700a4415afb3ac1767a556094e4ef8bd440e41:
ublk: fix use-after-free in ublk_cancel_cmd() (2026-05-08 06:44:42 -0600)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git tags/block-7.1-20260515
for you to fetch changes up to 4141f46daa4cf1f8caa14129f8b6db86f17452f5:
Merge tag 'nvme-7.1-2026-05-14' of git://git.infradead.org/nvme into block-7.1 (2026-05-14 19:14:33 -0600)
----------------------------------------------------------------
block-7.1-20260515
----------------------------------------------------------------
AlanCui4080 (1):
Revert "nvme: add quirk NVME_QUIRK_IGNORE_DEV_SUBNQN for 144d:a808"
Casey Chen (1):
block: recompute nr_integrity_segments in blk_insert_cloned_request
Chia-Lin Kao (AceLan) (1):
nvme-pci: fix use-after-free in nvme_free_host_mem()
Christoph Hellwig (2):
block: pass a minsize argument to bio_iov_iter_bounce
block: align down bounces bios
Damien Le Moal (1):
block: fix handling of dead zone write plugs
David Carlier (1):
block: don't overwrite bip_vcnt in bio_integrity_copy_user()
Hannes Reinecke (1):
nvmet-auth: Do not print DH-HMAC-CHAP secrets
Jens Axboe (1):
Merge tag 'nvme-7.1-2026-05-14' of git://git.infradead.org/nvme into block-7.1
Keith Busch (2):
nvme: make prp passthrough usage less scary
nvme: fix bio leak on mapping failure
Maurizio Lombardi (1):
nvme: fix race condition between connected uevent and STARTED_ONCE flag
Ming Lei (2):
ublk: reject max_sectors smaller than PAGE_SECTORS in parameter validation
selftests: ublk: cap nthreads to kernel's actual nr_hw_queues
Nick Chan (1):
nvme-apple: Reset q->sq_tail during queue init
Sagi Grimberg (1):
nvmet-tcp: Fix potential UAF when ddgst mismatch
Sungwoo Kim (1):
block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user()
block/bio-integrity.c | 19 ++++++++++++++++++-
block/bio.c | 27 +++++++++++++++------------
block/blk-mq.c | 19 +++++++++++++++++++
block/blk-zoned.c | 32 +++++++++++++++++++++++++++-----
drivers/block/ublk_drv.c | 3 +++
drivers/nvme/host/apple.c | 1 +
drivers/nvme/host/core.c | 6 +++++-
drivers/nvme/host/ioctl.c | 18 ++++--------------
drivers/nvme/host/pci.c | 8 ++++----
drivers/nvme/target/Kconfig | 9 +++++++++
drivers/nvme/target/auth.c | 13 ++++++++-----
drivers/nvme/target/tcp.c | 4 +++-
fs/iomap/direct-io.c | 2 +-
include/linux/bio.h | 3 ++-
tools/testing/selftests/ublk/kublk.c | 11 +++++++++++
15 files changed, 130 insertions(+), 45 deletions(-)
--
Jens Axboe
^ permalink raw reply
* [REGRESSION] block: virtio-blk + LVM raid1 spurious sector-0 read failures on libaio/threads submit since 5ff3f74e145a ("block: simplify direct io validity check")
From: Vjaceslavs Klimovs @ 2026-05-15 16:52 UTC (permalink / raw)
To: Jens Axboe
Cc: Keith Busch, Hannes Reinecke, Martin K. Petersen,
Christoph Hellwig, linux-block, linux-raid, dm-devel,
linux-kernel, regressions
Summary
-------
On v6.18, starting a libvirt/QEMU guest with virtio-blk backed by an
LVM "--type raid1" LV (drivers/md/dm-raid.c stacked on
drivers/md/raid1.c) makes md/raid1 register read failures at LV
sector 0 within seconds of "virsh start" and mark rimage_0 Faulty
once max_corrected_read_errors (default 20) is exceeded. Reads
succeed via the redirect path so guests boot, but every guest disk
ends up degraded on every VM start. Same workload on legacy
"--type mirror" (drivers/md/dm-raid1.c) crashes the host: a
zero-length READ reaches the NVMe controller, is rejected with
"Invalid Field in Command", and the dm-mirror recovery path oopses.
Symptom on dm-raid raid1 (post --type raid1)
--------------------------------------------
Per LV, at virsh start, in host dmesg:
kernel: raid1_end_read_request: 95 callbacks suppressed
kernel: raid1_read_request: 95 callbacks suppressed
kernel: md/raid1:mdX: dm-58: rescheduling sector 0
kernel: md/raid1:mdX: redirecting sector 0 to other mirror: dm-58
kernel: md/raid1:mdX: dm-58: rescheduling sector 0
kernel: md/raid1:mdX: redirecting sector 0 to other mirror: dm-58
[... 10 rescheduling/redirecting pairs ...]
kernel: md/raid1:mdX: dm-58: Raid device exceeded read_error
threshold [cur 21:max 20]
kernel: md/raid1:mdX: dm-58: Failing raid device
kernel: md/raid1:mdX: Disk failure on dm-58, disabling device.
kernel: md/raid1:mdX: Operation continuing on 1 devices.
dmeventd: WARNING: Device #0 of raid1 array, vg0-iris_boot, has failed.
dmeventd: WARNING: Waiting for resynchronization to finish before
initiating repair on RAID device vg0-iris_boot.
dmeventd: Use 'lvconvert --repair vg0/iris_boot' to replace failed device.
Subsequent "lvs -a":
WARNING: RaidLV vg0/iris_boot needs to be refreshed!
See character 'r' at position 9 in the RaidLV's attributes and its SubLV(s).
dmesg | grep nvme is EMPTY on this path. The NVMe driver is not
involved in producing the error; the failure originates between the
virtio-blk bio submission and raid1_end_read_request().
Symptom on legacy dm-mirror (pre-conversion --type mirror)
----------------------------------------------------------
Same workload on drivers/md/dm-raid1.c reaches the NVMe controller
as a zero-length READ and panics the host through dm-mirror's
recovery path:
kernel: operation not supported error, dev nvme1n1, sector 935446535
op 0x0:(READ) flags 0x0 phys_seg 0 prio class 2
kernel: nvme1n1: I/O Cmd(0x2) @ LBA 935446535, 0 blocks, I/O Error
(sct 0x0 / sc 0x2)
[... 10+ identical bursts at same timestamp ...]
dmeventd: Primary mirror device 252:58 read failed.
dmeventd: vg0-iris_boot is now in-sync.
[kernel oops in dm_mirror recovery path, full trace lost to console flash]
The "phys_seg 0", "0 blocks", "sct 0x0/sc 0x2" trio (NVMe Generic,
Invalid Field in Command, NVMe spec 4.1.1.2) is unambiguous: a bio
with bi_iter.bi_size == 0 and bi_vcnt == 0 left the block layer and
hit the controller. dm-raid raid1 hides this by retrying on the
surviving leg, but the upstream-of-md trigger is identical.
Bisect
------
git bisect, v6.12..v6.18, 16 deterministic GOOD/BAD steps, no skips,
~104 minutes:
5ff3f74e145adc79b49668adb8de276446acf6be is the first bad commit
block: simplify direct io validity check
--- a/block/fops.c
+++ b/block/fops.c
@@ -38,8 +38,8 @@ static blk_opf_t dio_bio_write_op(struct kiocb *iocb)
static bool blkdev_dio_invalid(struct block_device *bdev, struct kiocb *iocb,
struct iov_iter *iter)
{
- return iocb->ki_pos & (bdev_logical_block_size(bdev) - 1) ||
- !bdev_iter_is_aligned(bdev, iter);
+ return (iocb->ki_pos | iov_iter_count(iter)) &
+ (bdev_logical_block_size(bdev) - 1);
}
The dropped bdev_iter_is_aligned() used to walk the iov_iter and
reject per-segment misaligned/degenerate vectors at the blkdev fops
entry point. The replacement only validates ki_pos and total length
against the logical block size. Cases that now pass that no longer
get rejected:
- iter with iov_iter_count(iter) == 0 (degenerate; total length is
"sector-aligned" since 0 % 512 == 0)
- iter where total length is sector-aligned but a segment isn't
The commit message justifies the removal with "The block layer
checks all the segments for validity later". This is true for the
io_uring submit path (which enters __blkdev_direct_IO directly and
does its own validation) but not for the libaio aio_read/write_iter
or the worker-pool sync read/write_iter paths that enter via
blkdev_{read,write}_iter() -> blkdev_dio_invalid(). For those paths,
the segment check has no replacement.
Reproducing
----------------------------------------------------------
The trigger requires QEMU virtio-blk's specific submission shape AND
a non-io_uring submit. Userspace libaio alone, userspace
preadv-in-a-thread alone, and QEMU's raw-driver open probes (which
qemu-img info exercises identically) are all insufficient. The
combination that hits the bug is "guest-driven I/O through
virtio-blk-pci with cache.direct=on and aio in {native, threads}".
#regzbot introduced: 5ff3f74e145adc79b49668adb8de276446acf6be
Thanks,
Vjaceslavs Klimovs
^ permalink raw reply
* Re: [GIT PULL] Block fixes for 7.1-rc4
From: pr-tracker-bot @ 2026-05-15 20:10 UTC (permalink / raw)
To: Jens Axboe; +Cc: Linus Torvalds, linux-block@vger.kernel.org
In-Reply-To: <093dc3c7-5030-4400-9b38-727c41c5f511@kernel.dk>
The pull request you sent on Fri, 15 May 2026 08:20:27 -0600:
> https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git tags/block-7.1-20260515
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/d458a240344c4369bf6f3da203f2779515177738
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox