On 9/22/26 03:23, Jiakai Xu wrote:
> Commit 44e9eb5a7621 ("dma-buf/udmabuf: Disable the size limit by
> default") raised the default size_limit_mb from 64 to INT_MAX.
>
> With 4 KiB pages on a 64-bit system, creating a buffer larger than
> about 1 TiB makes the two per-page pointer arrays in init_udmabuf()
> exceed what kvmalloc() can handle. __kvmalloc_node_noprof() warns and
> fails when the requested size exceeds INT_MAX bytes, which is
> trivially triggerable by unprivileged users through the UDMABUF_CREATE
> ioctls:
>
> WARNING: CPU: 0 PID: 412 at mm/slub.c:7021 __kvmalloc_node_noprof
> Call Trace:
> udmabuf_create
> udmabuf_ioctl_create_list
>
> Instead of using a fixed megabyte value, derive the default limit from
> the kvmalloc() threshold:
>
> (((INT_MAX / sizeof(struct folio *)) - 1) >> 20) << PAGE_SHIFT
>
> On a 64-bit system with 4 KiB pages, this evaluates to 1044480 MB
> (1020 GiB), allowing buffers a little below 1 TiB while keeping the
> per-page pointer array allocations below INT_MAX bytes.
>
> Fixes: 44e9eb5a7621 ("dma-buf/udmabuf: Disable the size limit by default")
> Suggested-by: Christian König <christian.koenig(a)amd.com>
> Assisted-by: OpenCode:DeepSeek-V4-Flash
> Signed-off-by: Jiakai Xu <xujiakai24(a)mails.ucas.ac.cn>
Reviewed-by: Christian König <christian.koenig(a)amd.com>
Going to push that to drm-misc-fixes later today.
Regards,
Christian.
> ---
> V2 -> V3:
> - Replace the fixed 256 MB default with a limit derived from the
> kvmalloc() INT_MAX threshold, as suggested by Christian König. On
> 64-bit systems with 4 KiB pages, this allows buffers just below
> 1 TiB.
>
> V1 -> V2:
> - Instead of adding a pgcnt check in udmabuf_create(), restore a
> reasonable default size limit (256 MB), as suggested by Christian
> König.
> - Drop the second Fixes tag; the regression is solely due to
> 44e9eb5a7621.
> - Drop the "Cc: stable" tag; 44e9eb5a7621 is only in v7.3-rc1 and has
> not shipped in any released kernel yet.
>
> V1: https://lore.kernel.org/all/20260918013434.1849135-1-xujiakai24@mails.ucas.…
> V2: https://lore.kernel.org/all/20260919015731.2077604-1-xujiakai24@mails.ucas.…
> ---
> drivers/dma-buf/udmabuf.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
> index df6dd00462423..7ec269809fb49 100644
> --- a/drivers/dma-buf/udmabuf.c
> +++ b/drivers/dma-buf/udmabuf.c
> @@ -20,9 +20,10 @@ static int list_limit = 1024;
> module_param(list_limit, int, 0644);
> MODULE_PARM_DESC(list_limit, "udmabuf_create_list->count limit. Default is 1024.");
>
> -static int size_limit_mb = INT_MAX;
> +static int size_limit_mb = (((INT_MAX / sizeof(struct folio *)) - 1) >> 20) << PAGE_SHIFT;
> module_param(size_limit_mb, int, 0644);
> -MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is INT_MAX.");
> +MODULE_PARM_DESC(size_limit_mb,
> + "Max size of a dmabuf, in megabytes. Default is derived from the kvmalloc() limit.");
>
> struct udmabuf {
> pgoff_t pagecount;
On Mon, 2026-09-21 at 10:10 -0300, Jason Gunthorpe wrote:
> On Mon, Sep 21, 2026 at 08:40:41AM +0200, Thomas Hellström wrote:
> > On Fri, 2026-09-18 at 14:05 -0300, Jason Gunthorpe wrote:
> > > On Fri, Sep 18, 2026 at 03:42:28PM +0200, Thomas Hellström wrote:
> > > >
> > > > 1) Xe attachment check if pci_p2pdma_distance() returns OK for
> > > > the
> > > > path. Then Xe always sets up dma-addresses using
> > > > dma_map_resource().
> > >
> > > Open coding pci_p2pdma_distance() in drivers is a hack. Using
> > > dma_map_resource() like this was never "allowed".
> > >
> > > We've fixed things so these hacks are not needed, the drivers
> > > need to
> > > move over to things like dma_buf_phys_vec_to_sgt() and the hmm
> > > helpers
> > > to use the DMA API correctly.
> > >
> > > > 1a) Let's say the importer (Xe) has ATS enabled, and the
> > > > exporter
> > > > is
> > > > supposed to map dma_addresses.
> > > > In between there is a switch that allows direct traffic.
> > > > Now with your patch series, How would the exporter know that Xe
> > > > has
> > > > ATS
> > > > enabled and therefore should return IOVA mappings rather than
> > > > the
> > > > BUS
> > > > addresses representing the shortcut.
> > >
> > > The thing that does the DMA map must be aware of what PCI TLPs
> > > the
> > > PCIe initiator will use to compute the proper P2P mapping mode.
> > >
> > > ATS always needs an IOVA but that still doesn't mean the P2P will
> > > work.
> > >
> > > > It seems to me that a pci-device settable flag "ATS always
> > > > enabled"
> > > > should be enough to fix both issues?
> > >
> > > It should be be per-mapping to support the NIC workflow that
> > > isn't a
> > > global operation.
> >
> > OK. It looked to me from the series like the NIC workflow was to
> > adapt
> > its ATS settings per mapping depending on the p2p mapping type
> > returned
> > rather than vice versa. If so it would just clear "ATS always
> > enabled".
> >
> > Admittedly a per-mapping flag would be more flexible, but also
> > would
> > require changed internal APIs?
>
> Ah you didn't send this to the list, was it intentional?
No, Just hit the wrong reply-button. +lists again.
>
> mlx5 has both cases, some DMA is flexable and can do either, some is
> non-ATS only.
OK.
>
> Jason
Thanks,
Thomas
PCI P2PDMA applies Request and Completion Redirect throughout both paths.
This misclassifies asymmetric and nested switches, and reports one answer
for every kind of TLP.
Three ACS controls act on TLP attributes the client chooses rather than
on the topology: Translation Blocking and Direct Translated P2P act on
a Request's Address Type, and Completion Redirect skips Completions carrying
Relaxed Ordering.
Evaluate each direction at the path divergence, decide every class from the
one walk, expose the provider to dma-buf importers, and let mlx5 ask rather
than assume.
Signed-off-by: Leon Romanovsky <leonro(a)nvidia.com>
---
Changes in v6:
- Changed DMABUF to use callbacks and not directly stored pointer.
- Link to v5: https://patch.msgid.link/20260910-fix-p2p-acs-v4-0-v5-0-856087f63c0d@nvidia…
Changes in v5:
- Rebase on the posted fixes.
- Dropped tags from changed patches.
- Remove Egress Control Vector interpretation and coverage.
- Keep enabled Egress Control conservative as a Request redirect.
- Use pci_dbg()/dev_dbg() for diagnostics and drop the "debug" prefix.
- Removed code comments from "Document the pdev->p2pdma lifetime and RCU
rules" patch and reduced description to actual lifetime explanation.
- Added note that Linux assumes that TLPs are in strict-ordering and
untranslated.
- Added code to calculate p2p paths per-TLP type.
- Converted mlx5 to use that new proposed API.
- Link to https://patch.msgid.link/20260821-fix-p2p-acs-v4-0-v4-0-94426b96de73@nvidia…
Changes in v4:
- Reject ACS Violations and unreadable routing state instead of treating
them as host-bridge redirects
- Added Tested-by tags from Tushar Dave
- Added support to asymmetric ACS routing
- Limited redirect checks to the two ports at the path divergence
- Added standalone ACS routing diagnostics for hardware retesting
- Dropped " PCI: Account for Direct Translated P2P in ACS isolation checks" patch
- Link to v3: https://patch.msgid.link/20260811-fix-p2p-acs-v3-0-efc488ee7c03@nvidia.com
Changes in v3:
- Fixed pci_p2pdma_add_resource() error unwinding
- Made pdev->p2pdma teardown wait unconditionally for RCU readers
- Restricted pci_p2pmem_find_many() to pool-backed providers
- Documented the pdev->p2pdma lifetime and RCU rules
- Fixed calc_map_type_and_dist() handling of the verbose argument
- Required the ACS port and target to share a bus before indexing the
Egress Control Vector
- Gave pci_acs_enabled() and pci_acs_path_enabled() a scope, so the ACS
Direct Translated P2P rule no longer stops pci_enable_pasid() from
enabling PASID
- Dropped "Report ACS ports when the paths share no upstream bridge":
the mapping type cannot change without a shared upstream bridge, so
the pci=disable_acs_redir= hint was not actionable there and the ACS
walk only cost config space reads
- Folded the Request Redirect rule into pci_acs_rr_ineffective(), so
pci_acs_flags_enabled() and the Intel SPT PCH quirk share one copy
- Renamed pci_acs_egress_ctrl_set() to pci_acs_egress_ctrl_is_set(), it
reads the bit rather than setting it
- Reworded the blocked-path warning: ACS may also leave the direct route
indeterminate rather than blocked
- Added KUnit coverage for the shared-bus guard, a device with no ACS
capability and an unreadable ACS Control register
- Added the missing Fixes: tags, a second one on the
pci_p2pdma_add_resource() unwinding fix (the dangling devres action
dates to f58ef9d1d135) and one on the Egress Control isolation change
- Link to v2: https://patch.msgid.link/20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com
Changes in v2:
- Added Logan's ROB tags
- Added commas in Documentation patch
- Link to v1: https://patch.msgid.link/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com
---
Leon Romanovsky (18):
PCI/P2PDMA: Document pdev->p2pdma lifetime rules
PCI/P2PDMA: Document the TLP attribute assumptions
PCI/P2PDMA: Derive routing from directional ACS controls
PCI: Reject unreadable ACS controls in isolation checks
PCI/P2PDMA: Evaluate ACS controls at the path divergence
PCI/P2PDMA: Document directional ACS routing
PCI/P2PDMA: Collect the path's ACS controls before deciding
PCI/P2PDMA: Answer routing per TLP class
PCI/P2PDMA: Route Relaxed Ordering Completions directly
PCI/P2PDMA: Reject Translated Requests blocked by Translation Blocking
PCI/P2PDMA: Route Translated Requests under Direct Translated P2P
PCI/P2PDMA: Log detailed ACS routing diagnostics
PCI/P2PDMA: Add KUnit tests for the ACS routing decisions
PCI/P2PDMA: Test the ACS P2P routing walk
PCI: Add KUnit coverage for ACS isolation checks
PCI/P2PDMA: Document TLP-class routing
dma-buf: Let importers ask how peer-to-peer traffic is routed
RDMA/mlx5: Ask P2PDMA whether ATS takes a direct peer-to-peer route
Documentation/admin-guide/kernel-parameters.txt | 9 +-
Documentation/driver-api/pci/p2pdma.rst | 69 +++
drivers/dma-buf/dma-buf-mapping.c | 32 ++
drivers/infiniband/core/uverbs_std_types_dmabuf.c | 12 +
drivers/infiniband/hw/mlx5/mlx5_ib.h | 36 +-
drivers/infiniband/hw/mlx5/mr.c | 40 ++
drivers/pci/Kconfig | 15 +
drivers/pci/Makefile | 1 +
drivers/pci/p2pdma.c | 637 +++++++++++++++++++---
drivers/pci/pci.c | 7 +-
drivers/pci/pci.h | 26 +
drivers/pci/pci_acs_test.c | 609 +++++++++++++++++++++
drivers/pci/quirks.c | 6 +-
drivers/vfio/pci/vfio_pci_dmabuf.c | 12 +
include/linux/dma-buf-mapping.h | 3 +
include/linux/dma-buf.h | 17 +
include/linux/pci-p2pdma.h | 57 +-
17 files changed, 1467 insertions(+), 121 deletions(-)
---
base-commit: 08dbfad3f5040f5bdb6c529da20d6d4e81fefd72
change-id: 20260821-fix-p2p-acs-v4-0-e72455e3a261
prerequisite-message-id: <20260830-batch-p2p-fixes-v1-0-5044e8dfbe2e(a)nvidia.com>
prerequisite-patch-id: 6b25c7fcf164cdfc14e9fac5b908d97fcf6509d7
prerequisite-patch-id: 0d083c281001365aae4b35544cf28891a6ab9a96
prerequisite-patch-id: bfd9dabf271f3cc9a3a61f46387d20c20311363d
prerequisite-patch-id: fad0275efc722830fc591509506c0a5e4f581073
prerequisite-patch-id: 0c83bee688fec1f6d1564654df7c630fa6a4a978
Best regards,
--
Leon Romanovsky <leonro(a)nvidia.com>
Hi Longfang,
On 15/09/2026 13:16, liulongfang wrote:
> On 2026/9/12 5:41, Matt Evans wrote:
>> This helper, vfio_pci_core_mmap_prep_dmabuf(), creates a single-range
>> DMABUF for the purpose of mapping a PCI BAR. This is used in a future
>> commit by VFIO's ordinary mmap() path.
>>
>> This function transfers ownership of the VFIO device fd to the
>> DMABUF, which fput()s when it's released.
>>
>> Refactor the existing vfio_pci_core_feature_dma_buf() to split out
>> export code common to the two paths, VFIO_DEVICE_FEATURE_DMA_BUF and
>> this new VFIO_BAR mmap().
>>
>> By exchanging the VMA file, we lose the original device path in
>> /proc/<pid>/maps, lsof, etc. Generate a debug-oriented synthetic
>> 'filename' for BAR mappings based on the cdev, plus BDF, plus resource
>> index. (This does not apply to explicitly-exported DMABUFs which are
>> named by DMA_BUF_SET_NAME.)
>>
>> Signed-off-by: Matt Evans <matt(a)ozlabs.org>
>> ---
>> drivers/vfio/pci/vfio_pci_dmabuf.c | 211 +++++++++++++++++++++++------
>> drivers/vfio/pci/vfio_pci_priv.h | 5 +
>> 2 files changed, 171 insertions(+), 45 deletions(-)
>>
>> diff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c
>> index 9f10b10fc436..faa9239e66f8 100644
>> --- a/drivers/vfio/pci/vfio_pci_dmabuf.c
>> +++ b/drivers/vfio/pci/vfio_pci_dmabuf.c
>> @@ -3,6 +3,7 @@
>> */
>> #include <linux/dma-buf-mapping.h>
>> #include <linux/pci-p2pdma.h>
>> +#include <linux/dma-buf.h>
>> #include <linux/dma-resv.h>
>>
>> #include "vfio_pci_priv.h"
>> @@ -82,6 +83,8 @@ static void vfio_pci_dma_buf_release(struct dma_buf *dmabuf)
>> up_write(&priv->vdev->dmabuf_lock);
>> vfio_device_put_registration(&priv->vdev->vdev);
>> }
>> + if (priv->vfile)
>> + fput(priv->vfile);
>> kfree(priv->phys_vec);
>> kfree(priv);
>> }
>> @@ -246,6 +249,167 @@ int vfio_pci_dma_buf_find_pfn(struct vfio_pci_core_device *vdev,
>> return ret;
>> }
>>
>> +/*
>> + * Create a DMABUF corresponding to priv, add it to vdev->dmabufs list
>> + * for tracking (meaning cleanup or revocation will zap it), and take
>> + * a vfio_device registration.
>> + */
>> +static int vfio_pci_dmabuf_export(struct vfio_pci_core_device *vdev,
>> + struct vfio_pci_dma_buf *priv, u32 flags)
>> +{
>> + DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
>> +
>> + if (!vfio_device_try_get_registration(&vdev->vdev))
>> + return -ENODEV;
>> +
>> + exp_info.ops = &vfio_pci_dmabuf_ops;
>> + exp_info.size = priv->size;
>> + exp_info.flags = flags;
>> + exp_info.priv = priv;
>> +
>> + priv->dmabuf = dma_buf_export(&exp_info);
>> + if (IS_ERR(priv->dmabuf)) {
>> + vfio_device_put_registration(&vdev->vdev);
>> + return PTR_ERR(priv->dmabuf);
>> + }
>> +
>> + kref_init(&priv->kref);
>> + init_completion(&priv->comp);
>> +
>> + /* dma_buf_put() now frees priv */
>> + INIT_LIST_HEAD(&priv->dmabufs_elm);
>> +
>> + /*
>> + * dmabuf_lock synchronises access (R) or updates (W) to the
>> + * vdev->dmabufs list and to bars_revoked (see below). The
>> + * revocation state of DMABUF elements in the list is written
>> + * holding both dmabuf_lock(W) and resv, and tested with
>> + * either.
>> + *
>> + * (memory_lock, if held ->) dmabuf_lock -> resv
>> + *
>> + * NOTE: memory_lock is strictly avoided here, to avoid a
>> + * dependency on memory_lock when mmap_lock is held, when
>> + * mmap() leads to export. vfio-pci variant drivers are
>> + * permitted to hold memory_lock across actions that might
>> + * fault (such as user access); a deadlock could result when
>> + * that fault path attempts to take mmap_lock (if held by an
>> + * export waiting for memory_lock).
>> + *
>> + * vdev->bars_revoked tracks the BAR revocation status updated
>> + * via vfio_pci_dma_buf_move(), so the initial DMABUF state
>> + * follows the same criteria that later update the DMABUF
>> + * state (BAR zap, etc.).
>> + */
>> + lockdep_assert_not_held(&vdev->memory_lock);
>> +
>> + down_write(&vdev->dmabuf_lock);
>> + dma_resv_lock(priv->dmabuf->resv, NULL);
>> + priv->revoked = vdev->bars_revoked;
>> + list_add_tail(&priv->dmabufs_elm, &vdev->dmabufs);
>> + dma_resv_unlock(priv->dmabuf->resv);
>> + up_write(&vdev->dmabuf_lock);
>> +
>> + return 0;
>> +}
>> +
>> +int vfio_pci_core_mmap_prep_dmabuf(struct vfio_pci_core_device *vdev,
>> + struct vm_area_struct *vma,
>> + u64 phys_start, u64 req_len,
>> + unsigned int res_index)
>> +{
>> + struct vfio_pci_dma_buf *priv;
>> + unsigned long vma_pgoff = vma->vm_pgoff & (VFIO_PCI_OFFSET_MASK >> PAGE_SHIFT);
>> + char *bufname;
>> + int ret;
>> +
>> + priv = kzalloc_obj(*priv);
>> + if (!priv)
>> + return -ENOMEM;
>> +
>> + priv->phys_vec = kzalloc_obj(*priv->phys_vec);
>> + if (!priv->phys_vec) {
>> + ret = -ENOMEM;
>> + goto err_free_priv;
>> + }
>> +
>> + /*
>> + * Maximum size of the friendly debug name is
>> + * vfio1048575:ffff:ff:1f.7/5 = 26. This fits within
>> + * DMA_BUF_NAME_LEN, so dma_buf_set_name() below won't fail.
>> + */
>> + bufname = kasprintf(GFP_KERNEL, "%s:%s/%x",
>> + dev_name(&vdev->vdev.device), pci_name(vdev->pdev),
>> + res_index);
>> +
>> + if (!bufname) {
>> + ret = -ENOMEM;
>> + goto err_free_phys;
>> + }
>> +
>> + /*
>> + * 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.
Matt
>
> Thanks.
> Longfang.
>
>> + if (dma_buf_set_name(priv->dmabuf, bufname)) {
>> + /* Shouldn't happen, but don't leak if it does: */
>> + dev_dbg_ratelimited(&vdev->pdev->dev,
>> + "Failed to set map name '%s'\n",
>> + bufname);
>> + kfree(bufname);
>> + }
>> +
>> + /*
>> + * Ownership of the DMABUF file transfers to the VMA so that
>> + * other users can locate the DMABUF via a VA. Ownership of
>> + * the original VFIO device file being mmap()ed transfers to
>> + * priv, and is put when the DMABUF is released. This
>> + * intentionally does not use get_file()/vma_set_file()
>> + * because the references are already held, and ownership
>> + * moves.
>> + */
>> + priv->vfile = vma->vm_file;
>> + vma->vm_file = priv->dmabuf->file;
>> + vma->vm_private_data = priv;
>> +
>> + return 0;
>> +
>> +err_free_name:
>> + kfree(bufname);
>> +err_free_phys:
>> + kfree(priv->phys_vec);
>> +err_free_priv:
>> + kfree(priv);
>> + return ret;
>> +}
>> +
>> /*
>> * This is a temporary "private interconnect" between VFIO DMABUF and iommufd.
>> * It allows the two co-operating drivers to exchange the physical address of
>> @@ -364,7 +528,6 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
>> {
>> struct vfio_device_feature_dma_buf get_dma_buf = {};
>> struct vfio_region_dma_range *dma_ranges;
>> - DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
>> struct vfio_pci_dma_buf *priv;
>> size_t length;
>> int ret;
>> @@ -424,49 +587,9 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
>> kfree(dma_ranges);
>> dma_ranges = NULL;
>>
>> - if (!vfio_device_try_get_registration(&vdev->vdev)) {
>> - ret = -ENODEV;
>> + ret = vfio_pci_dmabuf_export(vdev, priv, get_dma_buf.open_flags);
>> + if (ret)
>> goto err_free_phys;
>> - }
>> -
>> - exp_info.ops = &vfio_pci_dmabuf_ops;
>> - exp_info.size = priv->size;
>> - exp_info.flags = get_dma_buf.open_flags;
>> - exp_info.priv = priv;
>> -
>> - priv->dmabuf = dma_buf_export(&exp_info);
>> - if (IS_ERR(priv->dmabuf)) {
>> - ret = PTR_ERR(priv->dmabuf);
>> - goto err_dev_put;
>> - }
>> -
>> - kref_init(&priv->kref);
>> - init_completion(&priv->comp);
>> -
>> - /* dma_buf_put() now frees priv */
>> - INIT_LIST_HEAD(&priv->dmabufs_elm);
>> -
>> - /*
>> - * dmabuf_lock synchronises access (R) or updates (W) to the
>> - * vdev->dmabufs list and to bars_revoked (see below). The
>> - * revocation state of DMABUF elements in the list is written
>> - * holding both dmabuf_lock(W) and resv, and tested with
>> - * either.
>> - *
>> - * dmabuf_lock -> resv
>> - *
>> - * vdev->bars_revoked tracks the BAR revocation status updated
>> - * via vfio_pci_dma_buf_move(), so the initial DMABUF state
>> - * follows the same criteria that later update the DMABUF
>> - * state (BAR zap, etc.).
>> - */
>> - down_write(&vdev->dmabuf_lock);
>> - dma_resv_lock(priv->dmabuf->resv, NULL);
>> - priv->revoked = vdev->bars_revoked;
>> - list_add_tail(&priv->dmabufs_elm, &vdev->dmabufs);
>> - dma_resv_unlock(priv->dmabuf->resv);
>> - up_write(&vdev->dmabuf_lock);
>> -
>> /*
>> * dma_buf_fd() consumes the reference, when the file closes the dmabuf
>> * will be released.
>> @@ -477,8 +600,6 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
>>
>> return ret;
>>
>> -err_dev_put:
>> - vfio_device_put_registration(&vdev->vdev);
>> err_free_phys:
>> kfree(priv->phys_vec);
>> err_free_priv:
>> diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h
>> index 48d9f574a3df..3ec676e12e21 100644
>> --- a/drivers/vfio/pci/vfio_pci_priv.h
>> +++ b/drivers/vfio/pci/vfio_pci_priv.h
>> @@ -30,6 +30,7 @@ struct vfio_pci_dma_buf {
>> size_t size;
>> struct phys_vec *phys_vec;
>> struct p2pdma_provider *provider;
>> + struct file *vfile;
>> u32 nr_ranges;
>> struct kref kref;
>> struct completion comp;
>> @@ -134,6 +135,10 @@ int vfio_pci_dma_buf_find_pfn(struct vfio_pci_core_device *vdev,
>> unsigned long address,
>> unsigned int order,
>> unsigned long *out_pfn);
>> +int vfio_pci_core_mmap_prep_dmabuf(struct vfio_pci_core_device *vdev,
>> + struct vm_area_struct *vma,
>> + u64 phys_start, u64 req_len,
>> + unsigned int res_index);
>>
>> #ifdef CONFIG_VFIO_PCI_DMABUF
>> int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
>>
On 9/21/26 12:09, Janghyuck Kim wrote:
> dma_buf_vmap() escalates any non-zero return from dmabuf->ops->vmap()
> into a WARN_ON_ONCE(). But a failing vmap() is not, by itself,
> evidence of a violated invariant or a driver bug -- it's an ordinary
> runtime failure of a fallible operation, no different from any other
> allocation-shaped kernel API that can return an error. The dma-buf
> core has no visibility into what a given exporter's vmap()
> implementation considers a normal failure versus an internal bug;
> that distinction, if it matters at all, belongs to the exporter, not
> to this generic passthrough.
Well not quite, apart from some not so relevant HW workarounds the vmap callback is mostly used for fbdev emulation.
> -ENOMEM is a concrete, easily reproduced example of this. Page
> allocation on the vmap()/vmalloc() path can fail purely because the
> calling task has a fatal signal pending: should_reclaim_retry() in
> mm/page_alloc.c intentionally gives up reclaim and returns false
> once fatal_signal_pending(current) is true, so that a task that is
> already being killed (e.g. by a userspace low-memory killer) isn't
> stuck retrying reclaim on its way out. The resulting -ENOMEM from
> dmabuf->ops->vmap() is expected behaviour in that case, not a bug in
> the exporter -- and it is far from the only way ops->vmap() can
> legitimately fail (an exporter may just as well refuse to vmap a
> buffer for other, unrelated reasons of its own).
>
> This is exactly the kind of condition Documentation/process/coding-style.rst
> warns against for WARN*():
>
> WARN*() must not be used for a condition that is expected to
> trigger easily, for example, by user space actions.
>
> A failing vmap() is reachable purely by driving the system into low
> memory and then issuing an ioctl that happens to map a dma-buf -- no
> driver misbehaviour required. Turning that into a WARN_ON_ONCE(),
> and on panic_on_warn kernels into an outright panic, doesn't help
> debugging; it just turns an otherwise-recoverable error into a fatal
> one.
WOW STOP! A driver allowing to vmap() through an IOCTL is a major no-go!
vmap() on a DMA-buf has very very limited use cases which should mostly only be trigger able as superuser.
At least on 32bit system you can otherwise trivially exhaust the vmap area of the kernel and that is even worse than a simple WARN_ON_ONCE().
Regards,
Christian.
>
> Drop the WARN_ON_ONCE() and simply propagate the error, consistent
> with how other allocation-failure paths in the kernel are handled.
> The dma_buf_vmap_unlocked()/dma_buf_vmap() contract is unchanged:
> both still return the negative errno from dmabuf->ops->vmap() to the
> caller. Only the WARN_ON_ONCE() splat (and, transitively, the panic
> on panic_on_warn kernels) is removed.
>
> Signed-off-by: Janghyuck Kim <janghyuck.kim(a)samsung.com>
> ---
> drivers/dma-buf/dma-buf.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
> index 36cdc342627e..252495761a21 100644
> --- a/drivers/dma-buf/dma-buf.c
> +++ b/drivers/dma-buf/dma-buf.c
> @@ -1670,7 +1670,7 @@ int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map)
> BUG_ON(iosys_map_is_set(&dmabuf->vmap_ptr));
>
> ret = dmabuf->ops->vmap(dmabuf, &ptr);
> - if (WARN_ON_ONCE(ret))
> + if (ret)
> return ret;
>
> dmabuf->vmap_ptr = ptr;
> --
> 2.34.1
>
PCI P2PDMA applies Request and Completion Redirect throughout both paths.
This misclassifies asymmetric and nested switches, and reports one answer
for every kind of TLP.
Three ACS controls act on TLP attributes the client chooses rather than
on the topology: Translation Blocking and Direct Translated P2P act on
a Request's Address Type, and Completion Redirect skips Completions carrying
Relaxed Ordering.
Evaluate each direction at the path divergence, decide every class from the
one walk, expose the provider to dma-buf importers, and let mlx5 ask rather
than assume.
Signed-off-by: Leon Romanovsky <leonro(a)nvidia.com>
---
Changes in v7:
- Removed "Document pdev->p2pdma lifetime rules" patch, it gives nothing
after p2pmem fix.
- Split dmabuf patch.
- Tushar retested the series, so added his Tested-by.
- Added Logan's ROB tags and fixed minor documentation issues pointed
by him.
- Link to v6: https://lore.kernel.org/all/20260914-fix-p2p-acs-v4-0-v6-0-5ef07ec9ef06@nvi…
Changes in v6:
- Changed DMABUF to use callbacks and not directly stored pointer.
- Link to v5: https://patch.msgid.link/20260910-fix-p2p-acs-v4-0-v5-0-856087f63c0d@nvidia…
Changes in v5:
- Rebase on the posted fixes.
- Dropped tags from changed patches.
- Remove Egress Control Vector interpretation and coverage.
- Keep enabled Egress Control conservative as a Request redirect.
- Use pci_dbg()/dev_dbg() for diagnostics and drop the "debug" prefix.
- Removed code comments from "Document the pdev->p2pdma lifetime and RCU
rules" patch and reduced description to actual lifetime explanation.
- Added note that Linux assumes that TLPs are in strict-ordering and
untranslated.
- Added code to calculate p2p paths per-TLP type.
- Converted mlx5 to use that new proposed API.
- Link to https://patch.msgid.link/20260821-fix-p2p-acs-v4-0-v4-0-94426b96de73@nvidia…
Changes in v4:
- Reject ACS Violations and unreadable routing state instead of treating
them as host-bridge redirects
- Added Tested-by tags from Tushar Dave
- Added support to asymmetric ACS routing
- Limited redirect checks to the two ports at the path divergence
- Added standalone ACS routing diagnostics for hardware retesting
- Dropped " PCI: Account for Direct Translated P2P in ACS isolation checks" patch
- Link to v3: https://patch.msgid.link/20260811-fix-p2p-acs-v3-0-efc488ee7c03@nvidia.com
Changes in v3:
- Fixed pci_p2pdma_add_resource() error unwinding
- Made pdev->p2pdma teardown wait unconditionally for RCU readers
- Restricted pci_p2pmem_find_many() to pool-backed providers
- Documented the pdev->p2pdma lifetime and RCU rules
- Fixed calc_map_type_and_dist() handling of the verbose argument
- Required the ACS port and target to share a bus before indexing the
Egress Control Vector
- Gave pci_acs_enabled() and pci_acs_path_enabled() a scope, so the ACS
Direct Translated P2P rule no longer stops pci_enable_pasid() from
enabling PASID
- Dropped "Report ACS ports when the paths share no upstream bridge":
the mapping type cannot change without a shared upstream bridge, so
the pci=disable_acs_redir= hint was not actionable there and the ACS
walk only cost config space reads
- Folded the Request Redirect rule into pci_acs_rr_ineffective(), so
pci_acs_flags_enabled() and the Intel SPT PCH quirk share one copy
- Renamed pci_acs_egress_ctrl_set() to pci_acs_egress_ctrl_is_set(), it
reads the bit rather than setting it
- Reworded the blocked-path warning: ACS may also leave the direct route
indeterminate rather than blocked
- Added KUnit coverage for the shared-bus guard, a device with no ACS
capability and an unreadable ACS Control register
- Added the missing Fixes: tags, a second one on the
pci_p2pdma_add_resource() unwinding fix (the dangling devres action
dates to f58ef9d1d135) and one on the Egress Control isolation change
- Link to v2: https://patch.msgid.link/20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com
Changes in v2:
- Added Logan's ROB tags
- Added commas in Documentation patch
- Link to v1: https://patch.msgid.link/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com
---
Leon Romanovsky (19):
PCI/P2PDMA: Document the TLP attribute assumptions
PCI/P2PDMA: Derive routing from directional ACS controls
PCI: Reject unreadable ACS controls in isolation checks
PCI/P2PDMA: Evaluate ACS controls at the path divergence
PCI/P2PDMA: Document directional ACS routing
PCI/P2PDMA: Collect the path's ACS controls before deciding
PCI/P2PDMA: Answer routing per TLP class
PCI/P2PDMA: Route Relaxed Ordering Completions directly
PCI/P2PDMA: Reject Translated Requests blocked by Translation Blocking
PCI/P2PDMA: Route Translated Requests under Direct Translated P2P
PCI/P2PDMA: Log detailed ACS routing diagnostics
PCI/P2PDMA: Add KUnit tests for the ACS routing decisions
PCI/P2PDMA: Test the ACS P2P routing walk
PCI: Add KUnit coverage for ACS isolation checks
PCI/P2PDMA: Document TLP-class routing
dma-buf: Let importers ask how peer-to-peer traffic is routed
vfio/pci: Hand out the P2PDMA provider behind a dma-buf
RDMA/uverbs: Hand out the P2PDMA provider behind a dma-buf
RDMA/mlx5: Ask P2PDMA whether ATS takes a direct peer-to-peer route
Documentation/admin-guide/kernel-parameters.txt | 15 +-
Documentation/driver-api/pci/p2pdma.rst | 71 +++
drivers/dma-buf/dma-buf-mapping.c | 39 ++
drivers/infiniband/core/uverbs_std_types_dmabuf.c | 12 +
drivers/infiniband/hw/mlx5/mlx5_ib.h | 36 +-
drivers/infiniband/hw/mlx5/mr.c | 47 ++
drivers/pci/Kconfig | 15 +
drivers/pci/Makefile | 1 +
drivers/pci/p2pdma.c | 630 +++++++++++++++---
drivers/pci/pci.c | 7 +-
drivers/pci/pci.h | 26 +
drivers/pci/pci_acs_test.c | 737 ++++++++++++++++++++++
drivers/pci/quirks.c | 6 +-
drivers/vfio/pci/vfio_pci_dmabuf.c | 12 +
include/linux/dma-buf-mapping.h | 3 +
include/linux/dma-buf.h | 19 +
include/linux/pci-p2pdma.h | 57 +-
17 files changed, 1611 insertions(+), 122 deletions(-)
---
base-commit: 08dbfad3f5040f5bdb6c529da20d6d4e81fefd72
change-id: 20260821-fix-p2p-acs-v4-0-e72455e3a261
prerequisite-message-id: <20260830-batch-p2p-fixes-v1-0-5044e8dfbe2e(a)nvidia.com>
prerequisite-patch-id: 6b25c7fcf164cdfc14e9fac5b908d97fcf6509d7
prerequisite-patch-id: 0d083c281001365aae4b35544cf28891a6ab9a96
prerequisite-patch-id: bfd9dabf271f3cc9a3a61f46387d20c20311363d
prerequisite-patch-id: fad0275efc722830fc591509506c0a5e4f581073
prerequisite-patch-id: 0c83bee688fec1f6d1564654df7c630fa6a4a978
Best regards,
--
Leon Romanovsky <leonro(a)nvidia.com>
On Fri, Sep 18, 2026 at 01:39:55PM -0600, Logan Gunthorpe wrote:
>
>
> On 2026-09-14 05:22, Leon Romanovsky wrote:
> > From: Leon Romanovsky <leonro(a)nvidia.com>
> >
> > pci_bridge_has_acs_redir() treats Request and Completion Redirect as
> > interchangeable. On asymmetric fabrics, a control for only the reverse TLP
> > direction can unnecessarily force P2PDMA through the host bridge.
> >
> > Evaluate Request Redirect for client Requests and Completion Redirect for
> > provider read Completions. Continue treating enabled Egress Control
> > conservatively as a Request redirect.
> >
> > Fixes: 52916982af48 ("PCI/P2PDMA: Support peer-to-peer memory")
> > Signed-off-by: Leon Romanovsky <leonro(a)nvidia.com>
>
> Core patch seems fine, minus one minor point below:
>
> Reviewed-by: Logan Gunthorpe <logang(a)deltatee.com>
>
> > @@ -1132,10 +1173,10 @@ EXPORT_SYMBOL_GPL(pci_p2pmem_publish);
> > /**
> > * pci_p2pdma_map_type - Determine the mapping type for P2PDMA transfers
> > * @provider: P2PDMA provider structure
> > - * @dev: Target device for the transfer
> > + * @dev: Client device that initiates the transfer
> > *
> > * Determines how peer-to-peer DMA transfers should be mapped between
> > - * the provider and the target device. The mapping type indicates whether
> > + * the provider and the client device. The mapping type indicates whether
> > * the transfer can be done directly through PCI switches or must go
> > * through the host bridge.
> > */
> >
>
> This hunk does not seem like it belongs in this patch.
This is the first patch that distinguishes between client and provider
and starts using that terminology instead of target/initiator. Therefore,
the change belongs here.
Thanks