* [PATCH v1 0/2] Register device memory for poison handling
@ 2026-01-08 15:35 ankita
2026-01-08 15:35 ` [PATCH v1 1/2] mm: add stubs for PFNMAP memory failure registration functions ankita
2026-01-08 15:35 ` [PATCH v1 2/2] vfio/nvgrace-gpu: register device memory for poison handling ankita
0 siblings, 2 replies; 5+ messages in thread
From: ankita @ 2026-01-08 15:35 UTC (permalink / raw)
To: ankita, vsethi, jgg, mochs, jgg, skolothumtho, alex, linmiaohe,
nao.horiguchi
Cc: cjia, zhiw, kjaju, yishaih, kevin.tian, kvm, linux-kernel, linux-mm
From: Ankit Agrawal <ankita@nvidia.com>
Linux MM provides interfaces to allow a driver to [un]register device
memory not backed by struct page for poison handling through
memory_failure.
The device memory on NVIDIA Grace based systems are not added to the
kernel and are not backed by struct pages. So nvgrace-gpu module
which manages the device memory can make use of these interfaces to
get the benefit of poison handling. Make nvgrace-gpu register the device
memory with the MM on open.
Moreover, the stubs are added to accommodate for CONFIG_MEMORY_FAILURE
being disabled.
Patch 1/2 introduces stubs for CONFIG_MEMORY_FAILURE disabled.
Patch 2/2 registers the device memory at the time of open instead of mmap.
Note that this is a reposting of an earlier series [1] which is partly
(patch 1/3) merged to v6.19-rc4. This one addresses the leftover patching.
Many thanks to Jason Gunthorpe (jgg@nvidia.com) and Alex Williamson
(alex@shazbot.org) for valuable suggestions.
Link: https://lore.kernel.org/all/20251213044708.3610-1-ankita@nvidia.com/ [1]
Ankit Agrawal (2):
mm: add stubs for PFNMAP memory failure registration functions
vfio/nvgrace-gpu: register device memory for poison handling
drivers/vfio/pci/nvgrace-gpu/main.c | 116 +++++++++++++++++++++++++++-
include/linux/memory-failure.h | 13 +++-
2 files changed, 123 insertions(+), 6 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v1 1/2] mm: add stubs for PFNMAP memory failure registration functions
2026-01-08 15:35 [PATCH v1 0/2] Register device memory for poison handling ankita
@ 2026-01-08 15:35 ` ankita
2026-01-08 15:35 ` [PATCH v1 2/2] vfio/nvgrace-gpu: register device memory for poison handling ankita
1 sibling, 0 replies; 5+ messages in thread
From: ankita @ 2026-01-08 15:35 UTC (permalink / raw)
To: ankita, vsethi, jgg, mochs, jgg, skolothumtho, alex, linmiaohe,
nao.horiguchi
Cc: cjia, zhiw, kjaju, yishaih, kevin.tian, kvm, linux-kernel, linux-mm
From: Ankit Agrawal <ankita@nvidia.com>
Add stubs to address CONFIG_MEMORY_FAILURE disabled.
Suggested-by: Alex Williamson <alex@shazbot.org>
Signed-off-by: Ankit Agrawal <ankita@nvidia.com>
---
include/linux/memory-failure.h | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/include/linux/memory-failure.h b/include/linux/memory-failure.h
index 7b5e11cf905f..d333dcdbeae7 100644
--- a/include/linux/memory-failure.h
+++ b/include/linux/memory-failure.h
@@ -4,8 +4,6 @@
#include <linux/interval_tree.h>
-struct pfn_address_space;
-
struct pfn_address_space {
struct interval_tree_node node;
struct address_space *mapping;
@@ -13,7 +11,18 @@ struct pfn_address_space {
unsigned long pfn, pgoff_t *pgoff);
};
+#ifdef CONFIG_MEMORY_FAILURE
int register_pfn_address_space(struct pfn_address_space *pfn_space);
void unregister_pfn_address_space(struct pfn_address_space *pfn_space);
+#else
+static inline int register_pfn_address_space(struct pfn_address_space *pfn_space)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void unregister_pfn_address_space(struct pfn_address_space *pfn_space)
+{
+}
+#endif /* CONFIG_MEMORY_FAILURE */
#endif /* _LINUX_MEMORY_FAILURE_H */
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v1 2/2] vfio/nvgrace-gpu: register device memory for poison handling
2026-01-08 15:35 [PATCH v1 0/2] Register device memory for poison handling ankita
2026-01-08 15:35 ` [PATCH v1 1/2] mm: add stubs for PFNMAP memory failure registration functions ankita
@ 2026-01-08 15:35 ` ankita
2026-01-08 17:00 ` Jiaqi Yan
1 sibling, 1 reply; 5+ messages in thread
From: ankita @ 2026-01-08 15:35 UTC (permalink / raw)
To: ankita, vsethi, jgg, mochs, jgg, skolothumtho, alex, linmiaohe,
nao.horiguchi
Cc: cjia, zhiw, kjaju, yishaih, kevin.tian, kvm, linux-kernel, linux-mm
From: Ankit Agrawal <ankita@nvidia.com>
The nvgrace-gpu module [1] maps the device memory to the user VA (Qemu)
without adding the memory to the kernel. The device memory pages are PFNMAP
and not backed by struct page. The module can thus utilize the MM's PFNMAP
memory_failure mechanism that handles ECC/poison on regions with no struct
pages.
The kernel MM code exposes register/unregister APIs allowing modules to
register the device memory for memory_failure handling. Make nvgrace-gpu
register the GPU memory with the MM on open.
The module registers its memory region, the address_space with the
kernel MM for ECC handling and implements a callback function to convert
the PFN to the file page offset. The callback functions checks if the
PFN belongs to the device memory region and is also contained in the
VMA range, an error is returned otherwise.
Link: https://lore.kernel.org/all/20240220115055.23546-1-ankita@nvidia.com/ [1]
Suggested-by: Alex Williamson <alex@shazbot.org>
Suggested-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Ankit Agrawal <ankita@nvidia.com>
---
drivers/vfio/pci/nvgrace-gpu/main.c | 116 +++++++++++++++++++++++++++-
1 file changed, 112 insertions(+), 4 deletions(-)
diff --git a/drivers/vfio/pci/nvgrace-gpu/main.c b/drivers/vfio/pci/nvgrace-gpu/main.c
index b45a24d00387..d3e5fee29180 100644
--- a/drivers/vfio/pci/nvgrace-gpu/main.c
+++ b/drivers/vfio/pci/nvgrace-gpu/main.c
@@ -9,6 +9,7 @@
#include <linux/jiffies.h>
#include <linux/pci-p2pdma.h>
#include <linux/pm_runtime.h>
+#include <linux/memory-failure.h>
/*
* The device memory usable to the workloads running in the VM is cached
@@ -49,6 +50,7 @@ struct mem_region {
void *memaddr;
void __iomem *ioaddr;
}; /* Base virtual address of the region */
+ struct pfn_address_space pfn_address_space;
};
struct nvgrace_gpu_pci_core_device {
@@ -88,6 +90,83 @@ nvgrace_gpu_memregion(int index,
return NULL;
}
+static int pfn_memregion_offset(struct nvgrace_gpu_pci_core_device *nvdev,
+ unsigned int index,
+ unsigned long pfn,
+ pgoff_t *pfn_offset_in_region)
+{
+ struct mem_region *region;
+ unsigned long start_pfn, num_pages;
+
+ region = nvgrace_gpu_memregion(index, nvdev);
+ if (!region)
+ return -EINVAL;
+
+ start_pfn = PHYS_PFN(region->memphys);
+ num_pages = region->memlength >> PAGE_SHIFT;
+
+ if (pfn < start_pfn || pfn >= start_pfn + num_pages)
+ return -EFAULT;
+
+ *pfn_offset_in_region = pfn - start_pfn;
+
+ return 0;
+}
+
+static inline
+struct nvgrace_gpu_pci_core_device *vma_to_nvdev(struct vm_area_struct *vma);
+
+static int nvgrace_gpu_pfn_to_vma_pgoff(struct vm_area_struct *vma,
+ unsigned long pfn,
+ pgoff_t *pgoff)
+{
+ struct nvgrace_gpu_pci_core_device *nvdev;
+ unsigned int index =
+ vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT);
+ pgoff_t vma_offset_in_region = vma->vm_pgoff &
+ ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
+ pgoff_t pfn_offset_in_region;
+ int ret;
+
+ nvdev = vma_to_nvdev(vma);
+ if (!nvdev)
+ return -ENOENT;
+
+ ret = pfn_memregion_offset(nvdev, index, pfn, &pfn_offset_in_region);
+ if (ret)
+ return ret;
+
+ /* Ensure PFN is not before VMA's start within the region */
+ if (pfn_offset_in_region < vma_offset_in_region)
+ return -EFAULT;
+
+ /* Calculate offset from VMA start */
+ *pgoff = vma->vm_pgoff +
+ (pfn_offset_in_region - vma_offset_in_region);
+
+ return 0;
+}
+
+static int
+nvgrace_gpu_vfio_pci_register_pfn_range(struct vfio_device *core_vdev,
+ struct mem_region *region)
+{
+ int ret;
+ unsigned long pfn, nr_pages;
+
+ pfn = PHYS_PFN(region->memphys);
+ nr_pages = region->memlength >> PAGE_SHIFT;
+
+ region->pfn_address_space.node.start = pfn;
+ region->pfn_address_space.node.last = pfn + nr_pages - 1;
+ region->pfn_address_space.mapping = core_vdev->inode->i_mapping;
+ region->pfn_address_space.pfn_to_vma_pgoff = nvgrace_gpu_pfn_to_vma_pgoff;
+
+ ret = register_pfn_address_space(®ion->pfn_address_space);
+
+ return ret;
+}
+
static int nvgrace_gpu_open_device(struct vfio_device *core_vdev)
{
struct vfio_pci_core_device *vdev =
@@ -114,14 +193,28 @@ static int nvgrace_gpu_open_device(struct vfio_device *core_vdev)
* memory mapping.
*/
ret = vfio_pci_core_setup_barmap(vdev, 0);
- if (ret) {
- vfio_pci_core_disable(vdev);
- return ret;
+ if (ret)
+ goto error_exit;
+
+ if (nvdev->resmem.memlength) {
+ ret = nvgrace_gpu_vfio_pci_register_pfn_range(core_vdev, &nvdev->resmem);
+ if (ret && ret != -EOPNOTSUPP)
+ goto error_exit;
}
- vfio_pci_core_finish_enable(vdev);
+ ret = nvgrace_gpu_vfio_pci_register_pfn_range(core_vdev, &nvdev->usemem);
+ if (ret && ret != -EOPNOTSUPP)
+ goto register_mem_failed;
+ vfio_pci_core_finish_enable(vdev);
return 0;
+
+register_mem_failed:
+ if (nvdev->resmem.memlength)
+ unregister_pfn_address_space(&nvdev->resmem.pfn_address_space);
+error_exit:
+ vfio_pci_core_disable(vdev);
+ return ret;
}
static void nvgrace_gpu_close_device(struct vfio_device *core_vdev)
@@ -130,6 +223,11 @@ static void nvgrace_gpu_close_device(struct vfio_device *core_vdev)
container_of(core_vdev, struct nvgrace_gpu_pci_core_device,
core_device.vdev);
+ if (nvdev->resmem.memlength)
+ unregister_pfn_address_space(&nvdev->resmem.pfn_address_space);
+
+ unregister_pfn_address_space(&nvdev->usemem.pfn_address_space);
+
/* Unmap the mapping to the device memory cached region */
if (nvdev->usemem.memaddr) {
memunmap(nvdev->usemem.memaddr);
@@ -247,6 +345,16 @@ static const struct vm_operations_struct nvgrace_gpu_vfio_pci_mmap_ops = {
#endif
};
+static inline
+struct nvgrace_gpu_pci_core_device *vma_to_nvdev(struct vm_area_struct *vma)
+{
+ /* Check if this VMA belongs to us */
+ if (vma->vm_ops != &nvgrace_gpu_vfio_pci_mmap_ops)
+ return NULL;
+
+ return vma->vm_private_data;
+}
+
static int nvgrace_gpu_mmap(struct vfio_device *core_vdev,
struct vm_area_struct *vma)
{
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v1 2/2] vfio/nvgrace-gpu: register device memory for poison handling
2026-01-08 15:35 ` [PATCH v1 2/2] vfio/nvgrace-gpu: register device memory for poison handling ankita
@ 2026-01-08 17:00 ` Jiaqi Yan
2026-01-08 19:21 ` Ankit Agrawal
0 siblings, 1 reply; 5+ messages in thread
From: Jiaqi Yan @ 2026-01-08 17:00 UTC (permalink / raw)
To: ankita
Cc: vsethi, jgg, mochs, jgg, skolothumtho, alex, linmiaohe,
nao.horiguchi, cjia, zhiw, kjaju, yishaih, kevin.tian, kvm,
linux-kernel, linux-mm
On Thu, Jan 8, 2026 at 7:36 AM <ankita@nvidia.com> wrote:
>
> From: Ankit Agrawal <ankita@nvidia.com>
>
> The nvgrace-gpu module [1] maps the device memory to the user VA (Qemu)
> without adding the memory to the kernel. The device memory pages are PFNMAP
> and not backed by struct page. The module can thus utilize the MM's PFNMAP
> memory_failure mechanism that handles ECC/poison on regions with no struct
> pages.
>
> The kernel MM code exposes register/unregister APIs allowing modules to
> register the device memory for memory_failure handling. Make nvgrace-gpu
> register the GPU memory with the MM on open.
>
> The module registers its memory region, the address_space with the
> kernel MM for ECC handling and implements a callback function to convert
> the PFN to the file page offset. The callback functions checks if the
> PFN belongs to the device memory region and is also contained in the
> VMA range, an error is returned otherwise.
>
> Link: https://lore.kernel.org/all/20240220115055.23546-1-ankita@nvidia.com/ [1]
>
> Suggested-by: Alex Williamson <alex@shazbot.org>
> Suggested-by: Jason Gunthorpe <jgg@nvidia.com>
> Signed-off-by: Ankit Agrawal <ankita@nvidia.com>
> ---
> drivers/vfio/pci/nvgrace-gpu/main.c | 116 +++++++++++++++++++++++++++-
> 1 file changed, 112 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/vfio/pci/nvgrace-gpu/main.c b/drivers/vfio/pci/nvgrace-gpu/main.c
> index b45a24d00387..d3e5fee29180 100644
> --- a/drivers/vfio/pci/nvgrace-gpu/main.c
> +++ b/drivers/vfio/pci/nvgrace-gpu/main.c
> @@ -9,6 +9,7 @@
> #include <linux/jiffies.h>
> #include <linux/pci-p2pdma.h>
> #include <linux/pm_runtime.h>
> +#include <linux/memory-failure.h>
>
> /*
> * The device memory usable to the workloads running in the VM is cached
> @@ -49,6 +50,7 @@ struct mem_region {
> void *memaddr;
> void __iomem *ioaddr;
> }; /* Base virtual address of the region */
> + struct pfn_address_space pfn_address_space;
> };
>
> struct nvgrace_gpu_pci_core_device {
> @@ -88,6 +90,83 @@ nvgrace_gpu_memregion(int index,
> return NULL;
> }
>
> +static int pfn_memregion_offset(struct nvgrace_gpu_pci_core_device *nvdev,
> + unsigned int index,
> + unsigned long pfn,
> + pgoff_t *pfn_offset_in_region)
> +{
> + struct mem_region *region;
> + unsigned long start_pfn, num_pages;
> +
> + region = nvgrace_gpu_memregion(index, nvdev);
> + if (!region)
> + return -EINVAL;
> +
> + start_pfn = PHYS_PFN(region->memphys);
> + num_pages = region->memlength >> PAGE_SHIFT;
> +
> + if (pfn < start_pfn || pfn >= start_pfn + num_pages)
> + return -EFAULT;
> +
> + *pfn_offset_in_region = pfn - start_pfn;
> +
> + return 0;
> +}
> +
> +static inline
> +struct nvgrace_gpu_pci_core_device *vma_to_nvdev(struct vm_area_struct *vma);
Any reason not to define vma_to_nvdev() here directly, but later?
> +
> +static int nvgrace_gpu_pfn_to_vma_pgoff(struct vm_area_struct *vma,
> + unsigned long pfn,
> + pgoff_t *pgoff)
> +{
> + struct nvgrace_gpu_pci_core_device *nvdev;
> + unsigned int index =
> + vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT);
> + pgoff_t vma_offset_in_region = vma->vm_pgoff &
> + ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
> + pgoff_t pfn_offset_in_region;
> + int ret;
> +
> + nvdev = vma_to_nvdev(vma);
> + if (!nvdev)
> + return -ENOENT;
> +
> + ret = pfn_memregion_offset(nvdev, index, pfn, &pfn_offset_in_region);
> + if (ret)
> + return ret;
> +
> + /* Ensure PFN is not before VMA's start within the region */
> + if (pfn_offset_in_region < vma_offset_in_region)
> + return -EFAULT;
> +
> + /* Calculate offset from VMA start */
> + *pgoff = vma->vm_pgoff +
> + (pfn_offset_in_region - vma_offset_in_region);
> +
> + return 0;
> +}
> +
> +static int
> +nvgrace_gpu_vfio_pci_register_pfn_range(struct vfio_device *core_vdev,
> + struct mem_region *region)
> +{
> + int ret;
> + unsigned long pfn, nr_pages;
> +
> + pfn = PHYS_PFN(region->memphys);
> + nr_pages = region->memlength >> PAGE_SHIFT;
> +
> + region->pfn_address_space.node.start = pfn;
> + region->pfn_address_space.node.last = pfn + nr_pages - 1;
> + region->pfn_address_space.mapping = core_vdev->inode->i_mapping;
> + region->pfn_address_space.pfn_to_vma_pgoff = nvgrace_gpu_pfn_to_vma_pgoff;
> +
> + ret = register_pfn_address_space(®ion->pfn_address_space);
> +
> + return ret;
nit: I believe "ret" is unnecessary here.
> +}
> +
> static int nvgrace_gpu_open_device(struct vfio_device *core_vdev)
> {
> struct vfio_pci_core_device *vdev =
> @@ -114,14 +193,28 @@ static int nvgrace_gpu_open_device(struct vfio_device *core_vdev)
> * memory mapping.
> */
> ret = vfio_pci_core_setup_barmap(vdev, 0);
> - if (ret) {
> - vfio_pci_core_disable(vdev);
> - return ret;
> + if (ret)
> + goto error_exit;
> +
> + if (nvdev->resmem.memlength) {
> + ret = nvgrace_gpu_vfio_pci_register_pfn_range(core_vdev, &nvdev->resmem);
> + if (ret && ret != -EOPNOTSUPP)
> + goto error_exit;
> }
>
> - vfio_pci_core_finish_enable(vdev);
> + ret = nvgrace_gpu_vfio_pci_register_pfn_range(core_vdev, &nvdev->usemem);
> + if (ret && ret != -EOPNOTSUPP)
> + goto register_mem_failed;
>
> + vfio_pci_core_finish_enable(vdev);
> return 0;
> +
> +register_mem_failed:
> + if (nvdev->resmem.memlength)
> + unregister_pfn_address_space(&nvdev->resmem.pfn_address_space);
> +error_exit:
> + vfio_pci_core_disable(vdev);
> + return ret;
> }
>
> static void nvgrace_gpu_close_device(struct vfio_device *core_vdev)
> @@ -130,6 +223,11 @@ static void nvgrace_gpu_close_device(struct vfio_device *core_vdev)
> container_of(core_vdev, struct nvgrace_gpu_pci_core_device,
> core_device.vdev);
>
> + if (nvdev->resmem.memlength)
> + unregister_pfn_address_space(&nvdev->resmem.pfn_address_space);
> +
> + unregister_pfn_address_space(&nvdev->usemem.pfn_address_space);
> +
> /* Unmap the mapping to the device memory cached region */
> if (nvdev->usemem.memaddr) {
> memunmap(nvdev->usemem.memaddr);
> @@ -247,6 +345,16 @@ static const struct vm_operations_struct nvgrace_gpu_vfio_pci_mmap_ops = {
> #endif
> };
>
> +static inline
> +struct nvgrace_gpu_pci_core_device *vma_to_nvdev(struct vm_area_struct *vma)
> +{
> + /* Check if this VMA belongs to us */
> + if (vma->vm_ops != &nvgrace_gpu_vfio_pci_mmap_ops)
> + return NULL;
> +
> + return vma->vm_private_data;
> +}
> +
> static int nvgrace_gpu_mmap(struct vfio_device *core_vdev,
> struct vm_area_struct *vma)
> {
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v1 2/2] vfio/nvgrace-gpu: register device memory for poison handling
2026-01-08 17:00 ` Jiaqi Yan
@ 2026-01-08 19:21 ` Ankit Agrawal
0 siblings, 0 replies; 5+ messages in thread
From: Ankit Agrawal @ 2026-01-08 19:21 UTC (permalink / raw)
To: Jiaqi Yan
Cc: Vikram Sethi, Jason Gunthorpe, Matt Ochs, jgg, Shameer Kolothum,
alex, linmiaohe, nao.horiguchi, Neo Jia, Zhi Wang,
Krishnakant Jaju, Yishai Hadas, kevin.tian, kvm, linux-kernel,
linux-mm
>> +static inline
>> +struct nvgrace_gpu_pci_core_device *vma_to_nvdev(struct vm_area_struct *vma);
>
> Any reason not to define vma_to_nvdev() here directly, but later?
Actually since it uses nvgrace_gpu_vfio_pci_mmap_ops; which the compiler
complains to be undeclared if vma_to_nvdev is moved up.
>> + ret = register_pfn_address_space(®ion->pfn_address_space);
>> +
>> + return ret;
>
> nit: I believe "ret" is unnecessary here.
Yes, I'll address that.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-01-08 19:21 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-08 15:35 [PATCH v1 0/2] Register device memory for poison handling ankita
2026-01-08 15:35 ` [PATCH v1 1/2] mm: add stubs for PFNMAP memory failure registration functions ankita
2026-01-08 15:35 ` [PATCH v1 2/2] vfio/nvgrace-gpu: register device memory for poison handling ankita
2026-01-08 17:00 ` Jiaqi Yan
2026-01-08 19:21 ` Ankit Agrawal
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox