From: Ankit Agrawal <ankita@nvidia.com>
To: <gregkh@linuxfoundation.org>, <jai.luthra@ideasonboard.com>,
<ilpo.jarvinen@linux.intel.com>
Cc: <W_Armin@gmx.de>, <alex@shazbot.org>, <jgg@nvidia.com>,
<vsethi@nvidia.com>, <mhonap@nvidia.com>, <mochs@nvidia.com>,
<ankita@nvidia.com>, <clg@redhat.com>,
<linux-kernel@vger.kernel.org>
Subject: [PATCH 2/4] platform/nvidia: Implement mmap and memory scrubbing for EGM chardev
Date: Thu, 2 Jul 2026 19:25:30 +0000 [thread overview]
Message-ID: <20260702192532.455400-3-ankita@nvidia.com> (raw)
In-Reply-To: <20260702192532.455400-1-ankita@nvidia.com>
Setup the file operations (open, close and mmap) for the EGM char device.
Memory scrubbing:
- The EGM region is invisible to the host Linux kernel and is not
managed by the page allocator. So the driver is responsible for
zeroing it before handing it to a VM.
- Clear the entire EGM region on the first open() call using memremap()
and memset() in 1 GiB chunks with cond_resched() between iterations
to avoid RCU stalls on large regions.
- The region is handed to a single VM at a time and must be scrubbed
before each handover. Serialise openers with a mutex guarding open_count
and refuse a second opener with -EBUSY while the region is still held.
mmap:
- Implement nvgrace_egm_mmap() using remap_pfn_range(). The EGM region
is not part of the host EFI map and has no struct pages making
remap_pfn_range() the correct interface for mapping it directly into
QEMU's virtual address space.
- Validate that the requested range fits within the EGM region. Reject a
vm_pgoff that already lies outside the region before it is shifted into
a byte count so that the page offset cannot overflow and slip past the
range check.
Suggested-by: Aniket Agashe <aniketa@nvidia.com>
Suggested-by: Vikram Sethi <vsethi@nvidia.com>
Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Ankit Agrawal <ankita@nvidia.com>
---
drivers/platform/nvidia/egm.c | 125 ++++++++++++++++++++++++++++++++--
1 file changed, 121 insertions(+), 4 deletions(-)
diff --git a/drivers/platform/nvidia/egm.c b/drivers/platform/nvidia/egm.c
index a340c4fcbc7a..09ab00213de2 100644
--- a/drivers/platform/nvidia/egm.c
+++ b/drivers/platform/nvidia/egm.c
@@ -3,6 +3,9 @@
* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved
*/
+#include <linux/mutex.h>
+#include <linux/sched/signal.h>
+#include <linux/sizes.h>
#include <linux/vfio_pci_core.h>
#define NVGRACE_EGM_DEV_NAME "egm"
@@ -19,6 +22,10 @@ struct gpu_node {
struct nvgrace_egm_dev {
struct device device;
struct cdev cdev;
+ /* serialises the first-open scrub */
+ struct mutex open_lock;
+ /* protected by open_lock */
+ unsigned int open_count;
phys_addr_t egmphys;
size_t egmlength;
u64 egmpxm;
@@ -39,21 +46,129 @@ static LIST_HEAD(egm_chardevs);
static int nvgrace_egm_open(struct inode *inode, struct file *file)
{
- return 0;
+ struct nvgrace_egm_dev *egm_dev =
+ container_of(inode->i_cdev, struct nvgrace_egm_dev, cdev);
+ phys_addr_t phys;
+ size_t remaining, chunk_size;
+ void *chunk_addr;
+ int ret;
+
+ file->private_data = egm_dev;
+
+ /*
+ * The EGM region is a physical carveout handed to a single VM at a
+ * time and must be scrubbed before each handover. Take open_lock
+ * killably around the scrub which can run for long time, so that
+ * the waiters stay killable.
+ */
+ if (mutex_lock_killable(&egm_dev->open_lock))
+ return -EINTR;
+
+ /*
+ * Refuse a second opener while the region is still held. release()
+ * runs only when the last reference to the struct file drops but an
+ * mmap keeps that reference (and hence open_count) alive past
+ * close for the lifetime of the VMA. So open_count returns to zero
+ * only when every mapping is gone. A concurrent opener cannot be
+ * handed the region: it cannot be re-scrubbed without destroying the
+ * current consumer's live data.
+ */
+ if (egm_dev->open_count) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ /*
+ * nvgrace-egm module is responsible to manage the EGM memory as
+ * the host kernel has no knowledge of it. Clear the region before
+ * handing over to userspace.
+ *
+ * The EGM region can be very large (hundreds of GiB). So Map and zero
+ * one chunk at a time rather than mapping the whole region at once.
+ */
+ phys = egm_dev->egmphys;
+ remaining = egm_dev->egmlength;
+
+ while (remaining > 0) {
+ /*
+ * The scrub holds the lock for a long time; let it be
+ * SIGKILL'd.
+ */
+ if (fatal_signal_pending(current)) {
+ ret = -EINTR;
+ goto unlock;
+ }
+
+ chunk_size = min(remaining, SZ_1G);
+
+ chunk_addr = memremap(phys, chunk_size, MEMREMAP_WB);
+ if (!chunk_addr) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ memset(chunk_addr, 0, chunk_size);
+ memunmap(chunk_addr);
+ cond_resched();
+
+ phys += chunk_size;
+ remaining -= chunk_size;
+ }
+
+ /*
+ * Mark the device open only after the scrub completes so that a
+ * concurrent opener cannot observe a non-zero count and proceed
+ * before the region has been cleared.
+ */
+ egm_dev->open_count = 1;
+ ret = 0;
+
+unlock:
+ mutex_unlock(&egm_dev->open_lock);
+ return ret;
}
static int nvgrace_egm_release(struct inode *inode, struct file *file)
{
+ struct nvgrace_egm_dev *egm_dev =
+ container_of(inode->i_cdev, struct nvgrace_egm_dev, cdev);
+
+ guard(mutex)(&egm_dev->open_lock);
+
+ if (!--egm_dev->open_count)
+ file->private_data = NULL;
+
return 0;
}
static int nvgrace_egm_mmap(struct file *file, struct vm_area_struct *vma)
{
+ struct nvgrace_egm_dev *egm_dev = file->private_data;
+ u64 req_len, pgoff, end;
+ unsigned long start_pfn, num_pages;
+
+ pgoff = vma->vm_pgoff;
+ num_pages = egm_dev->egmlength >> PAGE_SHIFT;
+
+ /* Reject a page offset that already lies outside the EGM region. */
+ if (pgoff >= num_pages)
+ return -EINVAL;
+
+ if (check_sub_overflow(vma->vm_end, vma->vm_start, &req_len) ||
+ check_add_overflow(PHYS_PFN(egm_dev->egmphys), pgoff, &start_pfn) ||
+ check_add_overflow(PFN_PHYS(pgoff), req_len, &end))
+ return -EOVERFLOW;
+
+ if (end > egm_dev->egmlength)
+ return -EINVAL;
+
/*
- * Mapping the EGM region into userspace is implemented by a later
- * patch. Until then refuse the mmap.
+ * EGM memory is invisible to the host kernel and is not managed
+ * by it. Map the usermode VMA to the EGM region.
*/
- return -EOPNOTSUPP;
+ return remap_pfn_range(vma, vma->vm_start,
+ start_pfn, req_len,
+ vma->vm_page_prot);
}
static const struct file_operations file_ops = {
@@ -122,6 +237,7 @@ static void egm_chardev_release(struct device *dev)
struct nvgrace_egm_dev *egm_chardev = container_of(dev, struct nvgrace_egm_dev, device);
remove_gpus(egm_chardev);
+ mutex_destroy(&egm_chardev->open_lock);
kfree(egm_chardev);
}
@@ -155,6 +271,7 @@ static struct nvgrace_egm_dev *setup_egm_chardev(u64 egmphys, u64 egmlength,
egm_chardev->egmphys = egmphys;
egm_chardev->egmlength = egmlength;
egm_chardev->egmpxm = egmpxm;
+ mutex_init(&egm_chardev->open_lock);
INIT_LIST_HEAD(&egm_chardev->gpus);
egm_chardev->device.devt = MKDEV(MAJOR(dev), egm_chardev->egmpxm);
--
2.34.1
next prev parent reply other threads:[~2026-07-02 19:26 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-02 19:25 [PATCH 0/4] Introduce nvgrace-egm driver for Extended GPU Memory Ankit Agrawal
2026-07-02 19:25 ` [PATCH 1/4] platform/nvidia: Introduce nvgrace-egm driver and enumerate EGM regions Ankit Agrawal
2026-07-02 22:22 ` Armin Wolf
2026-07-03 13:59 ` Jason Gunthorpe
2026-08-05 23:18 ` Alex Williamson
2026-08-06 20:39 ` Jason Gunthorpe
2026-07-02 19:25 ` Ankit Agrawal [this message]
2026-08-05 23:18 ` [PATCH 2/4] platform/nvidia: Implement mmap and memory scrubbing for EGM chardev Alex Williamson
2026-08-06 20:39 ` Jason Gunthorpe
2026-07-02 19:25 ` [PATCH 3/4] platform/nvidia: Handle retired ECC pages and expose via ioctl Ankit Agrawal
2026-08-05 23:18 ` Alex Williamson
2026-08-06 20:39 ` Jason Gunthorpe
2026-07-02 19:25 ` [PATCH 4/4] platform/nvidia: Register EGM PFNMAP range with memory_failure Ankit Agrawal
2026-08-03 16:43 ` [PATCH 0/4] Introduce nvgrace-egm driver for Extended GPU Memory Ankit Agrawal
2026-08-04 7:24 ` gregkh
2026-08-04 14:14 ` Jason Gunthorpe
2026-08-04 14:50 ` Ankit Agrawal
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260702192532.455400-3-ankita@nvidia.com \
--to=ankita@nvidia.com \
--cc=W_Armin@gmx.de \
--cc=alex@shazbot.org \
--cc=clg@redhat.com \
--cc=gregkh@linuxfoundation.org \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=jai.luthra@ideasonboard.com \
--cc=jgg@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mhonap@nvidia.com \
--cc=mochs@nvidia.com \
--cc=vsethi@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®