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
On Fri, Sep 18, 2026 at 12:25:01PM -0600, Logan Gunthorpe wrote:
>
>
> On 2026-09-14 05:22, Leon Romanovsky wrote:
> > From: Leon Romanovsky <leonro(a)nvidia.com>
> >
> > pdev->p2pdma is normally accessed through provider-owned references, but
> > pci_p2pmem_find_many() and the p2pmem sysfs attributes may race with driver
> > unbind.
> >
> > Document initialization, teardown, and how the release grace period
> > protects both the P2PDMA state and its optional allocation pool.
> >
> > Cc: Alex Williamson <alex(a)shazbot.org>
> > Cc: Matt Evans <matt(a)ozlabs.org>
> > Signed-off-by: Leon Romanovsky <leonro(a)nvidia.com>
> Reads correct to me.
>
> Reviewed-by: Logan Gunthorpe <logang(a)deltatee.com>
Thanks, Logan. Unfortunately, I'm inclined to drop this patch. AI review
makes it nearly impossible to write the description without complaints.
If I use my own words to describe the lifetime, the tools complain about
almost every word or sentence. If I let the tools write it instead, the
result is complete junk that merely describes basics already obvious from
the code itself.
Thanks
On 8/19/26 15:44, Taimuraz Kaitmazov wrote:
> amdxdna_drm_sync_bo_ioctl() calls amdxdna_hwctx_sync_debug_bo() for every
> FROM_DEVICE sync, which answers -EINVAL when the BO's assigned_hwctx names
> no context. Only a BO attached with ATTACH_DEBUG_BO is ever given one, so
> an ordinary read-back sync reports failure after its flush has already run.
>
> Ask for the debug sync only when the BO has a context. An unattached BO
> carries AMDXDNA_INVALID_CTX_HANDLE and hwctx ids are allocated above it, so
> the test is exact, -EINVAL keeps meaning that the named context is gone,
> and the handle is not resolved twice. The field is written under dev_lock
> and read here without it; the context is still resolved under that lock, so
> a racing attach only decides whether this sync sees the buffer.
>
> Suggested-by: Lizhi Hou <lizhi.hou(a)amd.com>
> Signed-off-by: Taimuraz Kaitmazov <taimuraz(a)kaitmazov.com>
> ---
> drivers/accel/amdxdna/amdxdna_gem.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c
> index 4be5298d1062..2613c94dd842 100644
> --- a/drivers/accel/amdxdna/amdxdna_gem.c
> +++ b/drivers/accel/amdxdna/amdxdna_gem.c
> @@ -1319,7 +1319,8 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev,
> XDNA_DBG(xdna, "Sync bo %d offset 0x%llx, size 0x%llx\n",
> args->handle, args->offset, args->size);
>
> - if (args->direction == SYNC_DIRECT_FROM_DEVICE)
> + if (abo->assigned_hwctx != AMDXDNA_INVALID_CTX_HANDLE &&
> + args->direction == SYNC_DIRECT_FROM_DEVICE)
> ret = amdxdna_hwctx_sync_debug_bo(client, args->handle);
Reviewed-by: Lizhi Hou <lizhi.hou(a)amd.com>
>
> put_obj:
On 8/19/26 15:44, Taimuraz Kaitmazov wrote:
> amdxdna_drm_sync_bo_ioctl() answers a failed amdxdna_flush_bo() with
> drm_WARN(). Both of that function's error returns are decided by the
> ioctl's arguments, so SYNC_BO with an offset past the end of the BO
> splats and taints the kernel from an unprivileged caller.
>
> Log it at debug level, since the same caller can repeat it.
>
> Signed-off-by: Taimuraz Kaitmazov <taimuraz(a)kaitmazov.com>
> ---
> drivers/accel/amdxdna/amdxdna_gem.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c
> index 77a9493cd7ba..0e0f844526ca 100644
> --- a/drivers/accel/amdxdna/amdxdna_gem.c
> +++ b/drivers/accel/amdxdna/amdxdna_gem.c
> @@ -1310,7 +1310,7 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev,
> amdxdna_gem_unpin(abo);
>
> if (ret) {
> - drm_WARN(&xdna->ddev, 1, "Can not get flush memory");
> + XDNA_DBG(xdna, "Flush BO %d failed, ret %d", args->handle, ret);
Reviewed-by: Lizhi Hou <lizhi.hou(a)amd.com>
> goto put_obj;
> }
> }