Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
31906f9
Remove dead BAR1 P2P address plumbing
ducphuc Aug 4, 2026
ee4cbbc
Leave P2P atomics at the platform default
ducphuc Aug 4, 2026
e402034
Reject unsafe hugetlb fast-path ranges
ducphuc Aug 4, 2026
11d8868
Restore IOVA lifetime diagnostics
ducphuc Aug 4, 2026
2fb0e5f
Restore dynamic-granularity unregister accounting
ducphuc Aug 4, 2026
9fac5a9
Synchronize the Resizable BAR registry mirror
ducphuc Aug 4, 2026
cc19db2
Document current P2P safety constraints
ducphuc Aug 4, 2026
9c35a0e
Make PCIe mailbox P2P setup transactional
ducphuc Jul 9, 2026
d682adc
Enable display-aware BAR1 P2P using runtime coverage
ducphuc Aug 5, 2026
3543e68
Fix BAR1 diagnostic formatting
ducphuc Aug 4, 2026
e9ab701
Restore UVM coherence without registering Blackwell BAR1 as P2PDMA
ducphuc Aug 4, 2026
ae8144c
Record GB206 BAR1 boundary validation
ducphuc Aug 5, 2026
543ba97
Make BAR1 policy test reproducible
ducphuc Aug 4, 2026
39c4f0b
Add full MIT notice to BAR1 policy header
ducphuc Aug 6, 2026
26e1ff5
Generalize partial BAR1 coverage using runtime geometry
ducphuc Aug 6, 2026
7e24c0b
Serialize FORCE_PCIE DMA-BUF physical-address operations
ducphuc Aug 7, 2026
7952e6c
Add generic non-coherent DMA-BUF GDR policy
ducphuc Aug 7, 2026
fd9cf24
Allow safe non-coherent FORCE_PCIE DMA-BUF exports
ducphuc Aug 7, 2026
989a586
Validate non-coherent DMA-BUF GDR importers
ducphuc Aug 7, 2026
6ad4e52
Record same-host DMA-BUF GDR transport validation
ducphuc Aug 7, 2026
85e9d1d
Add same-host DMA-BUF GDR reproduction plan
ducphuc Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# NVIDIA driver 610.43.03 with P2P for RTX 3090, RTX 4090, and RTX 5090

This enables P2P on consumer GPUs with the 610.43.03 driver version. No kernel parameters
are needed for the default behavior, just build, install, and go.
This enables P2P on consumer GPUs with the 610.43.03 driver version. The current branch
requires the IOMMU passthrough configuration described below.

See the [tinygrad 550.54.15-p2p README](https://github.com/tinygrad/open-gpu-kernel-modules/blob/550.54.15-p2p/README.md)
for the original description of the approach.
Expand All @@ -13,6 +13,7 @@ for the original description of the approach.
| RTX 3090 | Pairwise NVLink where available, PCIe BAR1 otherwise |
| RTX 4090 | PCIe BAR1 |
| RTX 5090 | PCIe BAR1 |
| RTX 5060 Ti / 5060 (GB206) | PCIe BAR1, including with a display attached |

P2P also works between different devices of the same generation, for example RTX 5090
to RTX PRO 6000 Blackwell.
Expand All @@ -23,10 +24,18 @@ This enables BAR1 P2P on consumer GPUs where NVLink isn't available, and falls b
NVLink where it is. For PCIe pairs, transfers write directly to the other GPU's physical
address over DMA.

On property-enabled GPUs, display-aware static BAR1 placement is used whenever runtime
geometry leaves a non-empty aligned static window after fixed console and mailbox
reservations. Partial windows support allocations wholly inside that window; allocations
spanning or outside it are rejected by the CUDA API because there is currently no
transparent dynamic-mapping fallback. GB206 cards (RTX 5060 Ti / 5060) are the
hardware-validated partial-coverage example, not an implementation allowlist.

> [!WARNING]
> IOMMU must be in passthrough mode (`iommu=pt`), not translating, or DMA will go through
> IOMMU page tables and transfers will fail. This is very dangerous if you run untrusted
> software or devices.
> IOMMU must currently be in passthrough mode (`iommu=pt`), not translating. In particular,
> the experimental hugetlb registration path does not yet handle scatterlist entries merged
> by a translated IOMMU. Do not use translated mode until that path is fixed and validated.
> Passthrough mode weakens DMA isolation and is unsafe with untrusted software or devices.

## How to use

Expand All @@ -52,17 +61,19 @@ options nvidia NVreg_RegistryDwords="RMForceP2PType=1"

This branch also includes an experimental path that accelerates `cudaHostRegister` by
several orders of magnitude when the registered buffer is backed by 1G hugepages, and
shrinks the device page tables used for such mappings. It is enabled automatically. This
path skips some of the per-4K-page bookkeeping the stock driver performs, so it may
misbehave in edge cases the stock driver handles correctly.
shrinks the device page tables used for such mappings. It is enabled automatically for a
non-empty registration that is hugepage-aligned, is an exact multiple of the hugepage
size, and stays within one hugetlb VMA. Other layouts use the normal per-page array path.
The fast path still skips some base-page bookkeeping and remains experimental.

## Potential issues

If P2P transfers are slow, make sure your IOMMU is in passthrough (`pt`) mode and that ACS
is disabled. ACS on root ports forces all GPU-to-GPU traffic through the CPU root complex,
killing P2P bandwidth. ACS can be disabled in BIOS, with the
`pcie_acs_override=downstream,multifunction` kernel parameter (if your kernel supports it),
or with an ACS override patch applied to the kernel.
redirect is not forcing GPU-to-GPU traffic through the root complex. Prefer a firmware ACS
control. If the kernel supports the upstream per-device option, use a narrowly scoped
`pci=disable_acs_redir=<BDF>[;<BDF>...]` setting and verify the resulting IOMMU groups.
Disabling ACS redirect weakens device isolation; do not use the broad `pcie_acs_override`
patch or kernel parameter.

## Sample `p2pBandwidthLatencyTest` output

Expand Down
2 changes: 1 addition & 1 deletion kernel-open/common/inc/nv-pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ int nv_pci_count_devices(void);
NvU8 nv_find_pci_capability(struct pci_dev *, NvU8);
int nvidia_dev_get_pci_info(const NvU8 *, struct pci_dev **, NvU64 *, NvU64 *);
nv_linux_state_t * find_pci(NvU32, NvU8, NvU8, NvU8);
NvBool nv_pci_is_valid_topology_for_direct_pci(nv_state_t *, struct pci_dev *);
NvBool nv_pci_is_valid_topology_for_direct_pci(nv_state_t *, struct pci_dev *, NvBool *);
NvBool nv_pci_has_common_pci_switch(nv_state_t *nv, struct pci_dev *);
void nv_pci_tegra_boost_clocks(struct device *dev);

Expand Down
3 changes: 3 additions & 0 deletions kernel-open/common/inc/nv.h
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,9 @@ typedef struct nv_state_t
/* Bool to check if dma-buf is supported */
NvBool dma_buf_supported;

/* Default-off non-coherent DMA-BUF GDR validation state from RM */
NvBool experimental_dmabuf_p2p_enabled;

/* Bool to check if the device received a shutdown notification */
NvBool is_shutdown;

Expand Down
12 changes: 12 additions & 0 deletions kernel-open/nvidia-uvm/uvm_devmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,18 @@ void uvm_devmem_device_p2p_init(uvm_parent_gpu_t *parent_gpu)

parent_gpu->device_p2p_initialised = false;

if (parent_gpu->rm_info.gpuArch >= NV2080_CTRL_MC_ARCH_INFO_ARCHITECTURE_GB100) {
// Static BAR1 is also the GPU peer aperture on non-coherent Blackwell.
// Registering it as P2PDMA memory would replace its pagemap operations
// and conflict with the BAR1-as-sysmem PTEs used for GPU peer access.
UVM_DBG_PRINT("Skipping PCI P2PDMA static BAR1 registration on non-coherent GPU %s "
"(size 0x%llx, write-combined %u)\n",
uvm_parent_gpu_name(parent_gpu),
parent_gpu->static_bar1_size,
parent_gpu->static_bar1_write_combined);
return;
}

// RM sets static_bar1_size when it has created a contiguous BAR mapping
// large enough to cover all of GPU memory that will be allocated to
// userspace buffers. This is required to support the P2PDMA feature to
Expand Down
7 changes: 0 additions & 7 deletions kernel-open/nvidia-uvm/uvm_gpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -1825,13 +1825,6 @@ NvU64 uvm_parent_gpu_canonical_address(uvm_parent_gpu_t *parent_gpu, NvU64 addr)

static bool uvm_parent_gpu_is_coherent(const uvm_parent_gpu_t *parent_gpu)
{
// Blackwell+ consumer GPUs (e.g. 5090) use BAR1 P2P via the SYS_COH
// aperture rewrite in nvGpuOpsBuildExternalAllocPtes. UVM's P2P
// registration path must take the coherent route to match, otherwise
// the ZONE_DEVICE peer DMA setup conflicts with BAR1-as-sysmem PTEs.
if (parent_gpu->rm_info.gpuArch >= NV2080_CTRL_MC_ARCH_INFO_ARCHITECTURE_GB100)
return true;

return parent_gpu->system_bus.memory_window_end > parent_gpu->system_bus.memory_window_start;
}

Expand Down
44 changes: 44 additions & 0 deletions kernel-open/nvidia/dmabuf-gdr-topology-policy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2026 Duc P. Tran
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

#ifndef DMABUF_GDR_TOPOLOGY_POLICY_H
#define DMABUF_GDR_TOPOLOGY_POLICY_H

/* Overflow-safe check that the importer can address the complete BAR. */
#define DMABUF_GDR_BAR_ADDRESSABLE(barStart, barSize, dmaMask) \
(((barSize) != 0) && ((barStart) <= (dmaMask)) && \
(((barSize) - 1) <= ((dmaMask) - (barStart))))

/*
* Keep the non-coherent topology exception deliberately narrow. Linux must
* approve the complete P2PDMA path and the importer must be in an identity
* IOMMU domain. The experimental path then uses dma_map_resource() instead of
* the stock FORCE_PCIE IOMMU bypass.
*/
#define DMABUF_GDR_TOPOLOGY_ALLOWED(enabled, identityIommu, p2pDistance, \
barStart, barSize, dmaMask) \
((enabled) && (identityIommu) && \
((p2pDistance) >= 0) && \
DMABUF_GDR_BAR_ADDRESSABLE((barStart), (barSize), (dmaMask)))

#endif // DMABUF_GDR_TOPOLOGY_POLICY_H
38 changes: 29 additions & 9 deletions kernel-open/nvidia/nv-dmabuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,15 @@ nv_dma_buf_put_phys_addresses (
return;
}

if (!priv->static_phys_addrs)
//
// See the matching comment in nv_dma_buf_get_phys_addresses(): locking
// can be skipped only for MAPPING_TYPE_DEFAULT static phys addr configs.
// MAPPING_TYPE_FORCE_PCIE unmap still calls into kbusUnmapFbAperture_HAL(),
// which updates per-GPU RUSD statistics and is not safe to run
// concurrently without the GPU lock.
//
if (!priv->static_phys_addrs ||
(priv->mapping_type != NV_DMABUF_EXPORT_MAPPING_TYPE_DEFAULT))
{
status = rm_acquire_api_lock(sp);
if (WARN_ON(status != NV_OK))
Expand Down Expand Up @@ -633,11 +641,18 @@ nv_dma_buf_get_phys_addresses (
}

//
// Locking is not needed for static phys address configs because the memdesc
// is not expected to change in this case and we hold the refcount on the
// owner GPU and memory before referencing it.
// Locking can be skipped for static phys address configs because the
// memdesc is not expected to change in this case and we hold the
// refcount on the owner GPU and memory before referencing it. This only
// holds for MAPPING_TYPE_DEFAULT: RM's static_phys_addrs determination
// also covers MAPPING_TYPE_FORCE_PCIE (any GPU with static BAR1
// enabled), but that mapping type does a real BAR1 aperture
// map/unmap and a per-GPU RUSD statistics update on every call
// (see kbusMapFbApertureSingle()/kbusUpdateRusdStatistics()), which
// are not safe to run concurrently without the GPU lock.
//
if (!priv->static_phys_addrs)
if (!priv->static_phys_addrs ||
(priv->mapping_type != NV_DMABUF_EXPORT_MAPPING_TYPE_DEFAULT))
{
status = rm_acquire_api_lock(sp);
if (status != NV_OK)
Expand Down Expand Up @@ -1029,8 +1044,11 @@ nv_dma_buf_attach(

if (priv->mapping_type == NV_DMABUF_EXPORT_MAPPING_TYPE_FORCE_PCIE)
{
NvBool skip_iommu;

if(!nv_pci_is_valid_topology_for_direct_pci(priv->nv,
to_pci_dev(attachment->dev)))
to_pci_dev(attachment->dev),
&skip_iommu))
{
nv_printf(NV_DBG_ERRORS,
"NVRM: dma-buf attach failed: "
Expand All @@ -1039,7 +1057,7 @@ nv_dma_buf_attach(
goto unlock_priv;
}

priv->skip_iommu = NV_TRUE;
priv->skip_iommu = skip_iommu;
}
else
{
Expand Down Expand Up @@ -1108,8 +1126,10 @@ nv_dma_buf_map(
}

//
// For MAPPING_TYPE_FORCE_PCIE on coherent platforms,
// get the BAR1 PFN scatterlist instead of C2C pages.
// For MAPPING_TYPE_FORCE_PCIE, get the BAR1 PFN scatterlist instead of
// C2C pages. Stock coherent platforms bypass IOMMU mapping after their
// existing topology check. The experimental non-coherent path instead
// maps BAR1 through the importer's DMA API.
//
// If nv->coherent is true, that could mean two things:
// 1. GPU memory has struct page from memory onlining(NUMA)
Expand Down
89 changes: 81 additions & 8 deletions kernel-open/nvidia/nv-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@
#include "nv-msi.h"
#include "nv-hypervisor.h"
#include "nv-reg.h"
#include "dmabuf-gdr-topology-policy.h"

#if defined(NV_VGPU_KVM_BUILD)
#include "nv-vgpu-vfio-interface.h"
#endif
#include <linux/iommu.h>
#if defined(CONFIG_PCI_P2PDMA)
#include <linux/pci-p2pdma.h>
#endif

#include <linux/clk.h>
#include <linux/device.h>
Expand Down Expand Up @@ -2890,24 +2894,93 @@ nv_pci_count_devices(void)
*/
NvBool nv_pci_is_valid_topology_for_direct_pci(
nv_state_t *nv,
struct pci_dev *peer
struct pci_dev *peer,
NvBool *skip_iommu
)
{
struct pci_dev *pdev0 = to_pci_dev(nv->dma_dev->dev);
struct pci_dev *pdev1 = peer;
NvBool result = NV_FALSE;
NvBool identity_iommu = NV_FALSE;
NvBool bar_addressable = NV_FALSE;
NvBool experimental = nv->experimental_dmabuf_p2p_enabled;
NvS32 p2p_distance = -1;
NvU64 dma_mask = dma_get_mask(&pdev1->dev);

*skip_iommu = NV_TRUE;

if (!nv->coherent)
{
return NV_FALSE;
}
#if defined(CONFIG_PCI_P2PDMA) && defined(NV_IOMMU_IS_DMA_DOMAIN_PRESENT)
struct iommu_domain *domain;
domain = iommu_get_domain_for_dev(&pdev1->dev);
if (domain != NULL)
{
identity_iommu = (domain->type == IOMMU_DOMAIN_IDENTITY);
}

if (pdev0->dev.iommu_group == pdev1->dev.iommu_group)
return NV_TRUE;
bar_addressable = DMABUF_GDR_BAR_ADDRESSABLE(
nv->bars[NV_GPU_BAR_INDEX_FB].cpu_address,
nv->bars[NV_GPU_BAR_INDEX_FB].size,
dma_mask);

if (pdev1->dev.iommu_group == NULL)
return nv_pci_has_common_pci_switch(nv, peer);
if (experimental && identity_iommu && bar_addressable)
{
p2p_distance = pci_p2pdma_distance(pdev0, &pdev1->dev, NV_TRUE);
}

return NV_FALSE;
result = DMABUF_GDR_TOPOLOGY_ALLOWED(
experimental,
identity_iommu,
p2p_distance,
nv->bars[NV_GPU_BAR_INDEX_FB].cpu_address,
nv->bars[NV_GPU_BAR_INDEX_FB].size,
dma_mask);

if (result)
{
// Map BAR1 through the importer's DMA API; do not bypass its IOMMU.
*skip_iommu = NV_FALSE;
}
#endif
}
else if (pdev0->dev.iommu_group == pdev1->dev.iommu_group)
{
result = NV_TRUE;
}
else if (pdev1->dev.iommu_group == NULL)
{
result = nv_pci_has_common_pci_switch(nv, peer);
}
else
{
result = NV_FALSE;
}

if (experimental && !nv->coherent)
{
nv_printf(
NV_DBG_INFO,
"NVRM: DMA-BUF GDR topology: "
"gpu=%04x:%02x:%02x.%u importer=%04x:%02x:%02x.%u "
"identityIommu=%u p2pDistance=%d barAddressable=%u "
"skipIommu=%u result=%u\n",
nv->pci_info.domain,
nv->pci_info.bus,
nv->pci_info.slot,
nv->pci_info.function,
pci_domain_nr(pdev1->bus),
pdev1->bus->number,
PCI_SLOT(pdev1->devfn),
PCI_FUNC(pdev1->devfn),
identity_iommu,
p2p_distance,
bar_addressable,
*skip_iommu,
result);
}

return result;
}

NvBool nv_pci_has_common_pci_switch(
Expand Down
Loading