On Wed, Oct 14, 2026 at 12:02:02PM +0100, Pavel Begunkov wrote:
> Premapped buffers don't require a generic bio_vec since these have
> already been dma mapped. Repurpose the bi_io_vec space to store dmabuf
> maps as they are mutually exclusive.
>
> The bio splitting differs from the normal path because it's already
> pre-mapped and for the block layer it's just an offset into the dma-buf.
> The actual segmentation is only available to the importer driver and not
> the block layer, however, it doesn't contain alignment gaps and we don't
> have to check it. For the same reason we can't precisely split by the
> number of segments, but we use ->min_seg_shift stored in the map to
> calculate the minimum number of bytes a request consisting of
> lim->max_segments full segments can cover and split by that. It's
> stricter and can add extra splitting. E.g. for a {4K, 4G} segmentation,
> the min segment size is 4K, and we'll split it into bios of
> (4K * lim->max_segments) bytes each, but it should be good enough for
> now to cover the most popular use cases.
>
> Suggested-by: Keith Busch <kbusch(a)kernel.org>
> Signed-off-by: Pavel Begunkov <asml.silence(a)gmail.com>
> ---
> block/bio.c | 15 +++++++++--
> block/blk-merge.c | 52 +++++++++++++++++++++++++++++++++++++++
> block/blk-mq.c | 6 +++++
> block/fops.c | 2 +-
> include/linux/bio.h | 9 ++++---
> include/linux/blk-mq.h | 7 ++++++
> include/linux/blk_types.h | 14 ++++++++++-
> include/linux/bvec.h | 3 ++-
> 8 files changed, 99 insertions(+), 9 deletions(-)
>
> diff --git a/block/bio.c b/block/bio.c
> index b48091c7663f..1e0d9714c541 100644
> --- a/block/bio.c
> +++ b/block/bio.c
> @@ -881,7 +881,11 @@ static int __bio_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp)
> bio->bi_write_stream = bio_src->bi_write_stream;
> bio->bi_bvec_gap_bit = bio_src->bi_bvec_gap_bit;
> bio->bi_iter = bio_src->bi_iter;
> - bio->bi_io_vec = bio_src->bi_io_vec;
> +
> + if (op_is_dmabuf(bio->bi_opf))
> + bio->bi_dmabuf_map = bio_src->bi_dmabuf_map;
> + else
> + bio->bi_io_vec = bio_src->bi_io_vec;
>
> if (bio->bi_bdev) {
> if (bio->bi_bdev == bio_src->bi_bdev &&
> @@ -1204,16 +1208,23 @@ EXPORT_SYMBOL_GPL(__bio_release_pages);
>
> bool bio_iov_iter_set(struct bio *bio, const struct iov_iter *iter)
> {
> - if (!iov_iter_is_bvec(iter))
> + if (!iov_iter_is_bvec(iter) && !iov_iter_is_dmabuf_map(iter))
> return false;
>
> WARN_ON_ONCE(bio->bi_max_vecs);
>
> + static_assert(offsetof(struct bio, bi_io_vec) ==
> + offsetof(struct bio, bi_dmabuf_map));
> + static_assert(offsetof(struct iov_iter, bvec) ==
> + offsetof(struct iov_iter, dmabuf_map));
> +
> bio->bi_io_vec = (struct bio_vec *)iter->bvec;
> bio->bi_iter.bi_idx = 0;
> bio->bi_iter.bi_offset = iter->iov_offset;
> bio->bi_iter.bi_size = iov_iter_count(iter);
> bio_set_flag(bio, BIO_CLONED);
> + if (iov_iter_is_dmabuf_map(iter))
> + bio->bi_opf |= REQ_NOMERGE | REQ_DMABUF;
> return true;
> }
>
> diff --git a/block/blk-merge.c b/block/blk-merge.c
> index 258a726071d1..501ddc01c449 100644
> --- a/block/blk-merge.c
> +++ b/block/blk-merge.c
> @@ -9,6 +9,7 @@
> #include <linux/blk-integrity.h>
> #include <linux/part_stat.h>
> #include <linux/blk-cgroup.h>
> +#include <linux/dma-buf-io.h>
>
> #include <trace/events/block.h>
>
> @@ -319,6 +320,41 @@ static inline unsigned int bvec_seg_gap(struct bio_vec *bvprv,
> return bv->bv_offset | (bvprv->bv_offset + bvprv->bv_len);
> }
>
> +static inline int bio_split_io_at_dmabuf(struct bio *bio,
> + const struct queue_limits *lim, unsigned *segs,
> + unsigned max_bytes, unsigned len_align_mask,
> + unsigned start_align_mask)
> +{
> + unsigned bytes = min(bio->bi_iter.bi_size, max_bytes);
> + unsigned seg_shift = bio->bi_dmabuf_map->min_seg_shift;
> + unsigned offset = bio->bi_iter.bi_offset & ((1U << seg_shift) - 1);
> + u64 max_segs_bytes;
> +
> + /*
> + * dma-buf maps don't expose the underlying segmentation, but they're
> + * guaranteed to not have alignment gaps, we only need to check the
> + * start and length alignment.
> + */
> + if ((bio->bi_iter.bi_offset & start_align_mask) ||
> + (bio->bi_iter.bi_size & len_align_mask))
> + return -EINVAL;
> +
> + /* Presented as a single contiguous range into the dma-buf */
> + *segs = 1;
> +
> + /*
> + * Limit by the number of segments by using the minimal segment size.
> + * Any I/O consisting of N full segments should be able to cover at
> + * least N multiplied by the segment size. It's stricter than walking
> + * the segments and might cause extra splitting.
> + */
> + max_segs_bytes = (u64)lim->max_segments << seg_shift;
> + bytes = min_t(u64, bytes, max_segs_bytes - offset);
> + if (bytes != bio->bi_iter.bi_size)
> + return bytes;
> + return 0;
> +}
> +
> /**
> * bio_split_io_at - check if and where to split a bio
> * @bio: [in] bio to be split
> @@ -346,6 +382,19 @@ int bio_split_io_at(struct bio *bio, const struct queue_limits *lim,
> len_align_mask |= (bc->bc_key->crypto_cfg.data_unit_size - 1);
> }
>
> + if (op_is_dmabuf(bio->bi_opf)) {
> + int ret;
> +
> + ret = bio_split_io_at_dmabuf(bio, lim, &nsegs, max_bytes,
> + len_align_mask, start_align_mask);
> + if (ret < 0)
> + return ret;
> + if (!ret)
> + goto out;
> + bytes = ret;
> + goto split;
> + }
> +
> bio_for_each_bvec(bv, bio, iter) {
> if (bv.bv_offset & start_align_mask ||
> bv.bv_len & len_align_mask)
> @@ -376,6 +425,7 @@ int bio_split_io_at(struct bio *bio, const struct queue_limits *lim,
> bvprvp = &bvprv;
> }
>
> +out:
> *segs = nsegs;
> bio->bi_bvec_gap_bit = ffs(gaps);
> return 0;
> @@ -493,6 +543,8 @@ unsigned int blk_recalc_rq_segments(struct request *rq)
>
> if (!rq->bio)
> return 0;
> + if (op_is_dmabuf(rq->bio->bi_opf))
> + return 1;
>
> switch (bio_op(rq->bio)) {
> case REQ_OP_DISCARD:
> diff --git a/block/blk-mq.c b/block/blk-mq.c
> index e929f94b25c7..1b7f8cbbda8b 100644
> --- a/block/blk-mq.c
> +++ b/block/blk-mq.c
> @@ -3150,6 +3150,12 @@ void blk_mq_submit_bio(struct bio *bio)
> goto queue_exit;
> }
>
> + if (unlikely(op_is_dmabuf(bio->bi_opf) &&
> + bdev_has_integrity_csum(bio->bi_bdev))) {
> + bio_endio_status(bio, BLK_STS_NOTSUPP);
Wrong indentation, conditions are never indented the same level as
the following code block, even with the weirdest preferences.
/me note the patch from Anuj on the list actually got this right..
Otherwise looks good:
Reviewed-by: Christoph Hellwig <hch(a)lst.de>
On Mon, Oct 12, 2026 at 02:21:41PM -0600, Jens Axboe wrote:
> I've queued this series up for some testing, in a separate branch. JFYI.
More testing it obviously good. I've been waiting for addressing
of the Sashiko comments on the nvme patches before doing more ACKs,
and while I can't really judge the code those in the dma-buf code
also look somewhat serious. So it would be useful to get them addressed
and the series reposted. Same for folding in the PI vs dmabuf fix
from Anuj, although making the combination work would of course be
even better.
Hi Karl,
On Tue, 13 Oct 2026 at 00:21, Karl Mehltretter <kmehltretter(a)gmail.com> wrote:
>
> On Mon, Oct 12, 2026 at 10:12:02AM +0100, Thorsten Leemhuis wrote:
> >
> > What's the status here given that 7.3 will likely be released in a few
> > days? Note, I can see from the discussion that this is a complex problem
> > that can't be quickly solved. That's how it is sometimes. But I wonder
> > if the current state of things is the best compromise for now or if
> > Rob's revert or some other option would be better.
> >
>
> I think reverting my commit 143755bdabaa ("dma-buf: Make DMABUF_DEBUG
> default to y on DEBUG_KERNEL kernels") is the best interim solution for
> v7.3.
>
> Christian, Sumit, could one of you apply Jianfeng's revert before the
> final release?
>
> https://lore.kernel.org/r/20260926022026.10539-1-liujianfeng1994@gmail.com/
>
> Thanks,
> Karl
I have pushed the revert to drm-misc-fixes; I'll request Dave to push
it out to Linus along with drm-fixes.
We should revisit this for the next cycle in a couple of weeks for sure.
Best,
Sumit.
Hi all,
A fix for DMABUF's file-release path, to set dmabuf->file = NULL when
the struct file itself is being freed and avoid a stale pointer
lingering. Previously it could be assumed that dmabuf->file was
always valid until .release was called, but commit 4ab59c3c638c6
("dma-buf: Move dma_buf_release() from fops to dentry_ops") separated
dentry release (calls .release) from file release (can happen before
.release). Setting the pointer to NULL clearly delimits the period in
which it points to a valid struct file.
Raw dmabuf->file access is bad news unless the file is known to have a
reference held (for example, during export). A single-threaded
problem scenario is a dentry held open (e.g. using O_PATH) but the
file closed; .release is not called, so the exporter was unaware that
dmabuf->file became stale. Exporter activities using dmabuf->file
then led to a UAF. Or, threads racing concurrent access with a file
closing.
VFIO uses get_file_active(&dmabuf->file), which is a UAF when the
pointer goes stale. Alex Mastro has created a VFIO-centric reproducer
[1], which shows up with KASAN.
To fix, dmabuf->file is either valid or NULL. get_file_active() is
then safe and a recommended way to access dmabuf->file without an
existing reference. The only possible outcomes are for it to get a
reference to the file, to observe a valid pointer but the file in the
DEAD state (being closed, fail), or NULL (fail).
Patch [3/3] sets the pointer NULL when the file closes, which
get_file_active() handles correctly.
vmwgfx was identified as doing a problematic dmabuf->file dereference.
Although already a UAF, making dmabuf->file = NULL turns it into an
oops. So, patch [1/3] correspondingly converts vmwgfx to use
get_file_active() too.
i915 GVT unsafely dereferences dmabuf->file for a trivial debug
printout after the file reference moves to the dmabuf's fd, so patch
[2/3] dereferences in the safe window.
Added comments to highlight that dmabuf->file could go away before
.release, and to use get_file_active() if a reference isn't otherwise
already held.
Thanks,
Matt
[1]: https://github.com/opsound/vfio-dmabuf-lab/blob/main/tests/vfio_dmabuf_opat…
Changes:
v3:
- Better comment for struct dma_buf's file (refer to synchronisation
against ->release).
- Added trivial i915 patch to remove unsafe debug dmabuf->file accesss.
v2: https://lore.kernel.org/all/20261009123601.94079-1-matt@ozlabs.org/
"v1" was the single "dma-buf: Annul dmabuf->file on file release"
patch:
https://lore.kernel.org/all/f8efaabd-c06e-4f04-8cfa-489c148e37ba@ozlabs.org/
Matt Evans (3):
drm/vmwgfx: Use get_file_active() for dmabuf->file
drm/i915/gvt: Read dmabuf->file only while valid
dma-buf: Annul dmabuf->file on file release
drivers/dma-buf/dma-buf.c | 21 ++++++++++++++++++---
drivers/gpu/drm/i915/gvt/dmabuf.c | 5 +++--
drivers/gpu/drm/vmwgfx/ttm_object.c | 2 +-
include/linux/dma-buf.h | 5 ++++-
4 files changed, 26 insertions(+), 7 deletions(-)
--
2.47.3
Hi Karl,
On Tue, 13 Oct, 2026, 12:21 am Karl Mehltretter, <kmehltretter(a)gmail.com>
wrote:
> On Mon, Oct 12, 2026 at 10:12:02AM +0100, Thorsten Leemhuis wrote:
> >
> > What's the status here given that 7.3 will likely be released in a few
> > days? Note, I can see from the discussion that this is a complex problem
> > that can't be quickly solved. That's how it is sometimes. But I wonder
> > if the current state of things is the best compromise for now or if
> > Rob's revert or some other option would be better.
> >
>
> I think reverting my commit 143755bdabaa ("dma-buf: Make DMABUF_DEBUG
> default to y on DEBUG_KERNEL kernels") is the best interim solution for
> v7.3.
>
> Christian, Sumit, could one of you apply Jianfeng's revert before the
> final release?
>
> https://lore.kernel.org/r/20260926022026.10539-1-liujianfeng1994@gmail.com/
>
>
Ok, I'll try and revert it for 7.3. We may need to reapply it for the next
cycle along with the solution Rob and folks are working on.
Thanks,
> Karl
>
Best,
Sumit.
>
Kiryl Shutsemau <kas(a)kernel.org> writes:
> On Thu, Sep 24, 2026 at 03:35:17PM +0530, Aneesh Kumar K.V (Arm) wrote:
>> diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c
>> index f904a636d449..1f1f39082391 100644
>> --- a/arch/x86/coco/tdx/tdx.c
>> +++ b/arch/x86/coco/tdx/tdx.c
>> @@ -978,6 +978,9 @@ static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc)
>> static int tdx_enc_status_change_prepare(unsigned long vaddr, int numpages,
>> bool enc)
>> {
>> + if (!enc)
>> + memset((void *)vaddr, 0, (size_t)numpages << PAGE_SHIFT);
>> +
>> /*
>> * Only handle shared->private conversion here.
>> * See the comment in tdx_early_init().
>
> This doesn't make sense to me.
>
> On TDX the memset zeroes a page that is about to be dropped.
> MapGPA(shared) makes the VMM back the GPA with different memory, and
> what the guest reads afterwards through the shared mapping is whatever
> the host has there.
>
Does TDX require the guest contents to be preserved when pages are
converted to shared?
The memset() is done here because CoCo shared-memory allocations now
ignore __GFP_ZERO and instead always zero the memory explicitly. For
CoCo architectures such as CCA, this must be done after the memory is
converted to shared because shared memory uses a different encryption
context.
The full patch avoids zeroing the memory twice. If __GFP_ZERO is used
for a CoCo shared-page allocation, the page is first zeroed during
allocation, then converted to shared, and must be zeroed again. I only
changed Arm CCA in this series because I was unsure whether the other
architectures required similar changes. Therefore, every CoCo
architecture other than CCA currently performs the memset() before
conversion, while CCA performs it afterward.
If the host expects to see the guest contents from before the conversion
to shared, we will have to pass the allocation flags down to these
helpers and perform the memset() only when __GFP_ZERO was requested,
along the lines of:
https://lore.kernel.org/all/yq5aa4p82sqt.fsf@kernel.org
To keep the changes minimal, the current approach always performs the
memset() rather than passing down a flag.
-aneesh