Le 28/09/2026 à 15:32, Pavel Begunkov a écrit :
> The goal is to be able to natively use dma-buf in the read-write / IO
> path. This patch adds basic building blocks serving as a glue and API
> between drivers and upper layer subsystems providing the uAPI. Later
> patches implement it for NVMe raw block devices and expose it to the
> user space via io_uring.
>
> There are two main objects. struct dma_buf_io_ctx and struct
> dma_buf_io_map. The ctx is used during initial registration and serves
> as an interface between the upper layer user like io_uring and to the
> importer subsystem / driver. The map represents the actual dma map
> established for the target device[s] with dma_buf_map_attachment() and
> stored in a device specific format. The context is created via a new
> file operation ->init_dma_buf_io_ctx.
>
> The ctx-map separation exists to support map invalidation (see
> dma_buf_io_invalidate_mappings()). A ctx can create
> multiple maps during its lifetime, but there can only be no more than
> one (active) map attached to it. Invalidation drops the active map
> if present, and the next map will only be attempted to be created
> once there is a new request that wants to use the dma-buf IO ctx.
>
> The primary task of the dma_buf_io_map object is to count requests
> using it and to wait for their completion when we want to destroy the
> DMA map.
>
> [un]mapping and any work with dma addresses is delegated to the
> importer driver via an ops table stored in the ctx, see struct
> dma_buf_io_ops. Only the target driver / subsystem knows about devices
> it wants to use the dma-buf with, especially in case of multi-device
> filesystems or stacking in the future.
>
> Signed-off-by: Pavel Begunkov <asml.silence(a)gmail.com>
> ---
Hi,
a few nitpicks below, should it help
> +static void dma_buf_io_kill_maps(struct dma_buf_io_ctx *ctx, bool final)
> +{
> + struct dma_buf_io_map *map;
> +
> + scoped_guard(mutex, &ctx->map_mutex) {
guard() is enough. This saves indentation.
> + if (final)
> + ctx->maps_killed = true;
> +
> + map = rcu_dereference_protected(ctx->map,
> + lockdep_is_held(&ctx->map_mutex));
> + if (!map)
> + return;
> + rcu_assign_pointer(ctx->map, NULL);
> + percpu_ref_kill(&map->refs);
> + }
> +}
...
> +int dma_buf_io_ctx_create(struct file *file,
> + struct dma_buf *dmabuf,
> + enum dma_data_direction dir,
> + struct dma_buf_io_ctx **out_ctx)
> +{
> + struct dma_buf_io_ctx *ctx;
> + int ret;
> +
> + if (!file->f_op->init_dma_buf_io_ctx)
> + return -EOPNOTSUPP;
> +
> + ctx = kmalloc_obj(*ctx);
kzalloc_obj() and save the memset() below
> + if (!ctx)
> + return -ENOMEM;
> +
> + memset(ctx, 0, sizeof(*ctx));
> + ctx->dir = dir;
> + ctx->dmabuf = dmabuf;
> + get_dma_buf(dmabuf);
> + mutex_init(&ctx->map_mutex);
> + mutex_init(&ctx->map_create_mutex);
> + atomic_set(&ctx->active_maps, 0);
> + atomic_set(&ctx->all_maps, 0);
> + refcount_set(&ctx->refs, 1);
> + init_waitqueue_head(&ctx->maps_wq);
> +
> + ret = file->f_op->init_dma_buf_io_ctx(file, ctx);
> + if (ret) {
> + kfree(ctx);
> + dma_buf_put(dmabuf);
> + return ret;
> + }
> +
> + if (WARN_ON_ONCE(!ctx->dev_ops ||
> + !ctx->dev_ops->map ||
> + !ctx->dev_ops->unmap ||
> + !ctx->dev_ops->release))
> + return -EINVAL;
> +
> + *out_ctx = ctx;
> + return 0;
> +}
...
CJ
On 9/26/26 09:12, Karl Mehltretter wrote:
> get_sg_table() merges physically contiguous pages without accounting
> for the mapping device's maximum segment size. This affects both
> importer mappings and the udmabuf misc device mapping used for CPU
> access.
>
> With DMA_API_DEBUG enabled, DMA_BUF_IOCTL_SYNC on a 64 MiB udmabuf
> reports:
>
> DMA-API: misc udmabuf: mapping sg segment longer than device claims to support [len=65884160] [max=65536]
>
> Use sg_alloc_table_from_pages_segment() with the mapping device's
> maximum segment size. Keep a PAGE_SIZE minimum because the allocator
> warns and returns -EINVAL for smaller limits.
>
> Before commit 5bf888673e0d ("udmabuf: Do not create malformed
> scatterlists"), each entry covered one page.
>
> Fixes: 5bf888673e0d ("udmabuf: Do not create malformed scatterlists")
> Assisted-by: LLM
> Signed-off-by: Karl Mehltretter <kmehltretter(a)gmail.com>
> ---
>
> Notes:
> Tested on v7.3-rc4-70-gfe2ec83746e5 in QEMU (x86_64, TCG) with
> DMA_API_DEBUG (all_errors=1) and DMABUF_DEBUG, A/B against the same
> base:
>
> before after
> DMA_BUF_IOCTL_SYNC, 64 MiB udmabuf 1 report 0
> vivid import, 4 MiB udmabuf 2 reports 0
> vivid import, 2 MiB hugetlb udmabuf 2 reports 0
> frames captured 5/5 5/5
>
> vb2-dma-contig rejected the non-contiguous import in both runs.
>
> drivers/dma-buf/udmabuf.c | 10 +++++++---
> 1 file changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
> index df6dd00462423..09f1eb8432f19 100644
> --- a/drivers/dma-buf/udmabuf.c
> +++ b/drivers/dma-buf/udmabuf.c
> @@ -139,9 +139,13 @@ static struct sg_table *get_sg_table(struct device *dev, struct dma_buf *buf,
> if (!sg)
> return ERR_PTR(-ENOMEM);
>
> - ret = sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->pagecount, 0,
> - ubuf->pagecount << PAGE_SHIFT,
> - GFP_KERNEL);
> + /* The SG allocator requires a segment limit of at least PAGE_SIZE. */
> + ret = sg_alloc_table_from_pages_segment(sg, ubuf->pages, ubuf->pagecount,
> + 0, ubuf->pagecount << PAGE_SHIFT,
> + max_t(unsigned int,
> + dma_get_max_seg_size(dev),
> + PAGE_SIZE),
Please return -EINVAL instead when dma_get_max_seg_size() returns that the segment size is smaller than a page.
In general I think that the sg_alloc_table_from_pages_segment() approach is because of the broken design of the old DMA API. Stuff like that should be handled by the iterator going over the DMA segments instead. But yeah that is not something you can fix in one patch.
So apart from the error handling the patch looks good to me.
Regards,
Christian.
> + GFP_KERNEL);
> if (ret < 0)
> goto err_alloc;
>
On Tue, Sep 29, 2026 at 08:56:01AM +0200, Karl Mehltretter wrote:
> On Tue, Sep 29, 2026 at 06:00:27AM +0000, sashiko-bot(a)kernel.org wrote:
> > On architectures with a 256KB PAGE_SIZE (such as Hexagon or PowerPC), this
> > new validation evaluates to 65536 < 262144, unconditionally returning
> > -EINVAL and breaking all CPU mappings.
>
> Right, the misc device has no dma_parms, so it gets the 64K default and
> sync fails with 256K pages
I would ignore this, it seems fictional to me, and if someone really
needs to support this rediculous combination it should be done inside
sg_alloc_table_from_pages_segment(:)
> The misc device has no real segment limit, so for v3 I'll give it
> dma_parms with an unlimited max segment size and keep the -EINVAL for
> importers that report a limit below PAGE_SIZE.
The params have to come from the *importing* device, not the udmabuf
misc device. You don't get to control what they are, and there is no
reason to put a dma_parms on a misc deivce.
Jason
On 9/28/26 12:36, Rob Clark wrote:
> On Mon, Sep 28, 2026 at 1:19 AM Christian König
> <christian.koenig(a)amd.com> wrote:
>>
>> On 9/26/26 04:20, Jianfeng Liu wrote:
>>> That commit fixed a dangling reference in the DMABUF_DEBUG default
>>> and thereby enabled the option - and with it the page-stripping
>>> sg_table wrapper that dma_buf_map_attachment() hands to importers -
>>> on every kernel with DEBUG_KERNEL=y, i.e. virtually every distro
>>> kernel.
>>>
>>> drm/msm is broken by the wrapper. Both of msm's map paths consume
>>> sg->length and sg_phys() of the attachment sg_table:
>>> msm_iommu_pagetable_map() for the per-process GPU pagetables, and
>>> iommu_map_sg() (via iommu_map_sgtable()) for scanout. The wrapper
>>> zeroes sg->length and strips the page pointers, so mappings of
>>> imported dma-bufs silently map nothing, and userspace observes
>>> arm-smmu translation faults from UCHE, e.g. during hardware video
>>> decode (clapper, chromium) on Adreno systems:
>>>
>>> gpu fault: ttbr0=000000088a889000 iova=000000010741c000 dir=READ
>>> type=TRANSLATION source=UCHE
>>>
>>> Bisected on a Snapdragon X1E78100 laptop as v7.3-rc3 good,
>>> v7.3-rc4 bad, culprit 143755bdabaa9.
>>>
>>> Switching msm to sg_dma_address()/sg_dma_len() is not a trivial fix
>>> either: those fields are only valid for sg_tables that msm has
>>> dma-mapped itself, which native non-MSM_BO_WC objects' sg_tables
>>> are not, so the conversion needs more work. The msm maintainer has
>>> therefore requested restoring the previous default for v7.3, to be
>>> revisited once msm no longer consumes struct page and sg->length of
>>> imported sg_tables.
>>
>> Yeah as I said before the problematic part is MSM here. We have enforced correct driver behavior for over 5 years now when that option is enabled.
>
> The problem is bigger than MSM here
Well, so far I have only heard about MSM.
But yes I mean the config option is doing exactly what it is supposed to do, pointing out when driver need some work to get this fixed.
I also agree that we shouldn't have allowed driver to touch that stuff in the first place and better document how to do things but yeah I can't change the past I can only try to fix it now.
>> What we can do is to mark MSM as broken and/or give a warning in MSM when DMABUF_DEBUG is enabled and you try to import a DMA-buf.
>
> sorry, no, we can't mark MSM as broken.. we can mark DMABUF_DEBUG as BROKEN
As I wrote the debug functionality to enforce not using struct pages has been around for over 5 years now, it was just not enabled by default.
What I can offer is to set it to default N for another few month to give you more time to fix things.
Regards,
Christian.
>
> BR,
> -R
>
>> But making the check not default to enable on debug kernels is not an option. This check here is exactly to point out broken drivers and you can manually disable it.
>>
>> Regards,
>> Christian.
>>
>>>
>>> Link: https://lore.kernel.org/linux-arm-msm/20260923074256.9357-1-liujianfeng1994…
>>> Suggested-by: Rob Clark <robin.clark(a)oss.qualcomm.com>
>>> Cc: Christian König <christian.koenig(a)amd.com>
>>> Cc: Sumit Semwal <sumit.semwal(a)linaro.org>
>>> Cc: Karl Mehltretter <kmehltretter(a)gmail.com>
>>>
>>> Signed-off-by: Jianfeng Liu <liujianfeng1994(a)gmail.com>
>>> ---
>>>
>>> drivers/dma-buf/Kconfig | 2 +-
>>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig
>>> index e4f078a326a41..7efc0f0d07126 100644
>>> --- a/drivers/dma-buf/Kconfig
>>> +++ b/drivers/dma-buf/Kconfig
>>> @@ -43,7 +43,7 @@ config UDMABUF
>>> config DMABUF_DEBUG
>>> bool "DMA-BUF debug checks"
>>> depends on DMA_SHARED_BUFFER
>>> - default y if DEBUG_KERNEL
>>> + default y if DEBUG
>>> help
>>> This option enables additional checks for DMA-BUF importers and
>>> exporters. Specifically it validates that importers do not peek at the
>>> ---
>>> base-commit: 93f51579e7df248780214094418f205253383cc5
>>> branch: revert-dmabuf-debug-for-7.3
>>>
>>
On 9/26/26 20:30, Rob Clark wrote:
> This was always the way it was supposed to work, and when we drop the
> page array for imported dma-bufs our fault handling path will no longer
> work.
>
> Signed-off-by: Rob Clark <robin.clark(a)oss.qualcomm.com>
Nice to see that finally happening.
Reviewed-by: Christian König <christian.koenig(a)amd.com> for this patch here, Acked-by: Christian König <christian.koenig(a)amd.com> for the rest of the series.
Regards,
Christian.
> ---
> drivers/gpu/drm/msm/msm_gem.c | 24 ++++++++++++++++++++++++
> 1 file changed, 24 insertions(+)
>
> diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
> index e8390ebd5dd5..c90336b3b231 100644
> --- a/drivers/gpu/drm/msm/msm_gem.c
> +++ b/drivers/gpu/drm/msm/msm_gem.c
> @@ -23,6 +23,8 @@
> #include "msm_gpu.h"
> #include "msm_kms.h"
>
> +MODULE_IMPORT_NS("DMA_BUF");
> +
> static void update_device_mem(struct msm_drm_private *priv, ssize_t size)
> {
> uint64_t total_mem = atomic64_add_return(size, &priv->total_mem);
> @@ -338,6 +340,9 @@ static vm_fault_t msm_gem_fault(struct vm_fault *vmf)
> int err;
> vm_fault_t ret;
>
> + if (drm_WARN_ON_ONCE(obj->dev, drm_gem_is_imported(obj)))
> + return VM_FAULT_SIGBUS;
> +
> /*
> * vm_ops.open/drm_gem_mmap_obj and close get and put
> * a reference on obj. So, we dont need to hold one here.
> @@ -1126,6 +1131,25 @@ static int msm_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct
> {
> struct msm_gem_object *msm_obj = to_msm_bo(obj);
>
> + if (drm_gem_is_imported(obj)) {
> + int ret;
> +
> + /* Reset both vm_ops and vm_private_data, so we don't end up with
> + * vm_ops pointing to our implementation if the dma-buf backend
> + * doesn't set those fields.
> + */
> + vma->vm_private_data = NULL;
> + vma->vm_ops = NULL;
> +
> + ret = dma_buf_mmap(obj->dma_buf, vma, 0);
> +
> + /* Drop the reference drm_gem_mmap_obj() acquired.*/
> + if (!ret)
> + drm_gem_object_put(obj);
> +
> + return ret;
> + }
> +
> vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP);
> vma->vm_page_prot = msm_gem_pgprot(msm_obj, vma_get_page_prot(vma));
>
On 9/26/26 04:20, Jianfeng Liu wrote:
> That commit fixed a dangling reference in the DMABUF_DEBUG default
> and thereby enabled the option - and with it the page-stripping
> sg_table wrapper that dma_buf_map_attachment() hands to importers -
> on every kernel with DEBUG_KERNEL=y, i.e. virtually every distro
> kernel.
>
> drm/msm is broken by the wrapper. Both of msm's map paths consume
> sg->length and sg_phys() of the attachment sg_table:
> msm_iommu_pagetable_map() for the per-process GPU pagetables, and
> iommu_map_sg() (via iommu_map_sgtable()) for scanout. The wrapper
> zeroes sg->length and strips the page pointers, so mappings of
> imported dma-bufs silently map nothing, and userspace observes
> arm-smmu translation faults from UCHE, e.g. during hardware video
> decode (clapper, chromium) on Adreno systems:
>
> gpu fault: ttbr0=000000088a889000 iova=000000010741c000 dir=READ
> type=TRANSLATION source=UCHE
>
> Bisected on a Snapdragon X1E78100 laptop as v7.3-rc3 good,
> v7.3-rc4 bad, culprit 143755bdabaa9.
>
> Switching msm to sg_dma_address()/sg_dma_len() is not a trivial fix
> either: those fields are only valid for sg_tables that msm has
> dma-mapped itself, which native non-MSM_BO_WC objects' sg_tables
> are not, so the conversion needs more work. The msm maintainer has
> therefore requested restoring the previous default for v7.3, to be
> revisited once msm no longer consumes struct page and sg->length of
> imported sg_tables.
Yeah as I said before the problematic part is MSM here. We have enforced correct driver behavior for over 5 years now when that option is enabled.
What we can do is to mark MSM as broken and/or give a warning in MSM when DMABUF_DEBUG is enabled and you try to import a DMA-buf.
But making the check not default to enable on debug kernels is not an option. This check here is exactly to point out broken drivers and you can manually disable it.
Regards,
Christian.
>
> Link: https://lore.kernel.org/linux-arm-msm/20260923074256.9357-1-liujianfeng1994…
> Suggested-by: Rob Clark <robin.clark(a)oss.qualcomm.com>
> Cc: Christian König <christian.koenig(a)amd.com>
> Cc: Sumit Semwal <sumit.semwal(a)linaro.org>
> Cc: Karl Mehltretter <kmehltretter(a)gmail.com>
>
> Signed-off-by: Jianfeng Liu <liujianfeng1994(a)gmail.com>
> ---
>
> drivers/dma-buf/Kconfig | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig
> index e4f078a326a41..7efc0f0d07126 100644
> --- a/drivers/dma-buf/Kconfig
> +++ b/drivers/dma-buf/Kconfig
> @@ -43,7 +43,7 @@ config UDMABUF
> config DMABUF_DEBUG
> bool "DMA-BUF debug checks"
> depends on DMA_SHARED_BUFFER
> - default y if DEBUG_KERNEL
> + default y if DEBUG
> help
> This option enables additional checks for DMA-BUF importers and
> exporters. Specifically it validates that importers do not peek at the
> ---
> base-commit: 93f51579e7df248780214094418f205253383cc5
> branch: revert-dmabuf-debug-for-7.3
>
On Thu, Sep 24, 2026 at 02:55:08PM +0800, Yili Zhang wrote:
> The dma_buf_phys_vec_to_sgt() routine links every physical range into
> the allocated IOVA space at offset zero instead of advancing the offset
> by the already mapped length. This breaks the THRU_HOST_BRIDGE flow
> whenever phys_vec contains more than one entry: the second and
> subsequent dma_iova_link() calls try to install mappings on top of
> PTEs that already exist, which fails and makes the whole conversion
> return an error.
>
> Fix it by passing the accumulated mapped_len as the IOVA offset. This
> matches the pattern used by all other dma_iova_link() callers and is
> consistent with the rest of the function: dma_iova_sync() and the
> error path in dma_iova_destroy() both operate on the [0, mapped_len)
> prefix of the IOVA space, which assumes that the ranges were linked
> contiguously starting at offset 0.
>
> Fixes: 3aa31a8bb11e ("dma-buf: provide phys_vec to scatter-gather mapping routine")
> Cc: stable(a)vger.kernel.org
> Signed-off-by: Yili Zhang <zhangyili01(a)baidu.com>
> ---
> drivers/dma-buf/dma-buf-mapping.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
Thanks,
Reviewed-by: Leon Romanovsky <leon(a)kernel.org>
Hi Longfang,
On 22/09/2026 10:16, liulongfang wrote:
> On 2026/9/21 21:24, Matt Evans wrote:
>> Hi Longfang,
>>
>> On 15/09/2026 13:16, liulongfang wrote:
>>> On 2026/9/12 5:41, Matt Evans wrote:
>>>> [snip]
>>>> +
>>>> + /*
>>>> + * The DMABUF begins from the mmap()'s BAR offset, i.e. the
>>>> + * start of the VMA corresponds to byte 0 of the DMABUF and
>>>> + * byte (vma_pgoff << PAGE_SHIFT) of the BAR.
>>>> + *
>>>> + * vfio_pci_dma_buf_find_pfn() reverses this offset using
>>>> + * vma_pgoff_adjust, so that ultimately a fault's offset from
>>>> + * the start of the _VMA_ has a consistent usage whether the
>>>> + * VMA originates from an mmap() of the VFIO device here or a
>>>> + * direct DMABUF mmap(). Note vma_pgoff_adjust also includes
>>>> + * the encoded VFIO region index, which cancels out the index
>>>> + * encoded in vm_pgoff.
>>>> + */
>>>> + priv->vdev = vdev;
>>>> + priv->size = req_len;
>>>> + priv->nr_ranges = 1;
>>>> + priv->vma_pgoff_adjust = vma->vm_pgoff;
>>>> +
>>>> + priv->provider = pcim_p2pdma_provider(vdev->pdev, res_index);
>>>> + if (!priv->provider) {
>>>> + ret = -EINVAL;
>>>> + goto err_free_name;
>>>> + }
>>>> +
>>>> + priv->phys_vec[0].paddr = phys_start + ((u64)vma_pgoff << PAGE_SHIFT);
>>>> + priv->phys_vec[0].len = priv->size;
>>>> +
>>>> + ret = vfio_pci_dmabuf_export(vdev, priv, O_RDWR);
>>>> + if (ret)
>>>> + goto err_free_name;
>>>> +
>>>
>>> In the current patch, the PCIe device's BAR2 configuration space can be mapped as a DMABUF.
>>> However, on an OS with a 64K page size, if a VF device's BAR2 is smaller than 64K,
>>> a problem arises where the space is forced to page-align to 64K, it will causing the VM to
>>> access memory beyond the actual size of the VF device's BAR2 space.
>>>
>>> How does your solution handle these cases where the BAR2 space is smaller than the Host OS's page size?
>>
>> Even on a 4K host, there can be BARs < PAGE_SIZE so 64K (or 16K) hosts
>> aren't a new case. These small BARs cannot be mmap()ed and DMABUFs
>> cannot be exported from them. (vfio_pci_core_mmap() errors out when
>> !bar_mmap_supported[index]. And, a DMABUF needs to be an aligned
>> multiple of PAGE_SIZE, plus vfio_pci_core_fill_phys_vec() won't allow a
>> DMABUF to be created off the end of a BAR.)
>>
>> So, although this series allows a DMABUF to be mmap()ed, the preexisting
>> checks prevent a sub-page DMABUF from existing and so there is no new
>> route to mapping a sub-page BAR.
>>
>> What's the concern on BAR2 specifically, out of interest? This logic is
>> applied to all resources equally, and tests pci_resource_len(...) so
>> there shouldn't be a PF/VF distinction either.
>>
>
> However, the typical boundary check found in VFIO, such as:
>
> if (req_start + req_len > phys_len)
> return -EINVAL;
>
> seems to be missing here.
Isn't it covered by that statement in vfio_pci_core_mmap() just before
this function is called? This helper is intended to do as it's told by
a caller that has validated the range is correct (it doesn't duplicate
the checks already done by vfio_pci_core_mmap()).
Thanks,
Matt