From: Vipin Sharma <vipinsh@google.com>
To: kexec@lists.infradead.org, linux-kernel@vger.kernel.org,
linux-doc@vger.kernel.org, kvm@vger.kernel.org,
linux-mm@kvack.org, linux-kselftest@vger.kernel.org,
linux-pci@vger.kernel.org
Cc: ajayachandra@nvidia.com, alex@shazbot.org, amastro@fb.com,
ankita@nvidia.com, apopple@nvidia.com, bhelgaas@google.com,
chrisl@kernel.org, christian.koenig@amd.com, corbet@lwn.net,
dmatlack@google.com, graf@amazon.com,
jacob.pan@linux.microsoft.com, jgg@nvidia.com, jgg@ziepe.ca,
jrhilke@google.com, julianr@linux.ibm.com, kees@kernel.org,
kevin.tian@intel.com, leon@kernel.org, leonro@nvidia.com,
lukas@wunner.de, mattev@meta.com, michal.winiarski@intel.com,
parav@nvidia.com, pasha.tatashin@soleen.com, praan@google.com,
pratyush@kernel.org, rananta@google.com, rdunlap@infradead.org,
rientjes@google.com, rodrigo.vivi@intel.com, rppt@kernel.org,
saeedm@nvidia.com, schnelle@linux.ibm.com,
skhan@linuxfoundation.org, skhawaja@google.com,
vipinsh@google.com, vivek.kasireddy@intel.com, witu@nvidia.com,
yanjun.zhu@linux.dev, yi.l.liu@intel.com
Subject: [PATCH v6 27/27] vfio: selftests: Add continuous DMA to vfio_pci_liveupdate_kexec_test
Date: Mon, 2 Nov 2026 07:36:56 -0800 [thread overview]
Message-ID: <20261102153656.1431701-28-vipinsh@google.com> (raw)
In-Reply-To: <20261102153656.1431701-1-vipinsh@google.com>
Extend vfio_pci_liveupdate_kexec_test to exercise continuous DMA across
a Live Update when the device is in noiommu mode.
Allocate a shmem memfd backed by three 2MB THP folios (for the driver
region, source buffer, and destination buffer), map each folio at
IOVA == PA via iommu_map_identity(), and preserve the memfd in the same
Live Update session before the device so that LUO's LIFO finish order
keeps the memory alive until after the device is finished.
Before kexec, initialize the selftest driver, run a sanity memcpy, kick
off a long-running DMA memcpy, and disable MSI-X so freeze() succeeds.
After kexec, take over the device via __vfio_pci_device_init(), retrieve
and map the preserved memfd verifying that all three 2MB folios stayed
at the exact same physical addresses, attach the driver without resetting
the device, wait for the pre-kexec memcpy to complete, and run another
memcpy without reinitializing the device.
Co-developed-by: David Matlack <dmatlack@google.com>
Signed-off-by: David Matlack <dmatlack@google.com>
Signed-off-by: Vipin Sharma <vipinsh@google.com>
---
.../vfio/vfio_pci_liveupdate_kexec_test.c | 266 +++++++++++++++++-
1 file changed, 257 insertions(+), 9 deletions(-)
diff --git a/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c b/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c
index 9610fe593cf6..c33b62118523 100644
--- a/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c
+++ b/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c
@@ -1,22 +1,120 @@
// SPDX-License-Identifier: GPL-2.0-only
+#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/sizes.h>
#include <linux/vfio.h>
+
#include <libliveupdate.h>
#include <libvfio.h>
+#define DRIVER_REGION_SIZE SZ_2M
+#define MEMCPY_FOLIO_SIZE SZ_2M
+#define NR_MEMFD_FOLIOS 3
+#define MEMFD_SIZE (NR_MEMFD_FOLIOS * SZ_2M)
+
+#define MEMCPY_TIMEOUT_S (30 * 60)
+
+static struct dma_region src_region;
+static struct dma_region dst_region;
+static void *memfd_vaddr;
+static u64 folio_pa[NR_MEMFD_FOLIOS];
+
static const char *device_bdf;
static char state_session[LIVEUPDATE_SESSION_NAME_LENGTH];
+static char data_session[LIVEUPDATE_SESSION_NAME_LENGTH];
static char device_session[LIVEUPDATE_SESSION_NAME_LENGTH];
enum {
STATE_TOKEN,
DEVICE_TOKEN,
+ MEMFD_TOKEN,
+ DATA_TOKEN,
};
+static u64 memcpy_size(struct vfio_pci_device *device)
+{
+ return min_t(u64, src_region.size, device->driver.max_memcpy_size);
+}
+
+static bool is_filled(const char *buf, u64 size, char c)
+{
+ return buf[0] == c && !memcmp(buf, buf + 1, size - 1);
+}
+
+static void dma_memcpy_one(struct vfio_pci_device *device, char c)
+{
+ u64 size = memcpy_size(device);
+ void *src = src_region.vaddr;
+ void *dst = dst_region.vaddr;
+
+ memset(src, c, size);
+ memset(dst, 0, size);
+
+ VFIO_ASSERT_EQ(vfio_pci_driver_memcpy(device, to_iova(device, src),
+ to_iova(device, dst), size), 0);
+ VFIO_ASSERT_TRUE(is_filled(dst, size, c));
+}
+
+static void dma_memcpy_start(struct vfio_pci_device *device)
+{
+ u64 size = memcpy_size(device);
+ void *src = src_region.vaddr;
+ void *dst = dst_region.vaddr;
+ u64 count;
+
+ /*
+ * Rough Math: If we assume the device will perform memcpy at a rate of
+ * 30GB/s then 7200GB of transfers will run for about 4 minutes.
+ */
+ count = (u64)7200 * SZ_1G / size;
+ count = min_t(u64, count, device->driver.max_memcpy_count);
+
+ memset(src, 'x', size);
+ memset(dst, 0, size);
+
+ ksft_print_msg("Starting %lu DMA memcpy operations of size 0x%lx\n",
+ count, size);
+ vfio_pci_driver_memcpy_start(device, to_iova(device, src),
+ to_iova(device, dst), size, count);
+}
+
+static void dma_memcpy_wait(struct vfio_pci_device *device)
+{
+ u64 size = memcpy_size(device);
+ void *src = src_region.vaddr;
+ void *dst = dst_region.vaddr;
+ struct timespec start, end;
+ long ms;
+
+ VFIO_ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &start), 0);
+
+ /* Fail rather than hang if the device stopped, e.g. as it was reset. */
+ alarm(MEMCPY_TIMEOUT_S);
+ VFIO_ASSERT_EQ(vfio_pci_driver_memcpy_wait(device), 0);
+ alarm(0);
+
+ VFIO_ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &end), 0);
+ ms = (end.tv_sec - start.tv_sec) * 1000 +
+ (end.tv_nsec - start.tv_nsec) / 1000000;
+
+ VFIO_ASSERT_TRUE(is_filled(src, size, 'x'));
+ VFIO_ASSERT_TRUE(is_filled(dst, size, 'x'));
+
+ ksft_print_msg("Pre-kexec DMA memcpy operations completed after %ld ms\n",
+ ms);
+}
+
static void check_noiommu(const char *bdf)
{
const char *cdev_path = __vfio_pci_get_cdev_path(bdf, true);
@@ -35,26 +133,162 @@ static void check_noiommu(const char *bdf)
free((void *)cdev_path);
}
+static void dma_memfd_regions_map(struct vfio_pci_device *device, int fd,
+ bool before_kexec)
+{
+ struct dma_region *regions[NR_MEMFD_FOLIOS] = {
+ &device->driver.region,
+ &src_region,
+ &dst_region,
+ };
+ int i;
+
+ memfd_vaddr = vfio_memfd_mmap_aligned(fd, MEMFD_SIZE, SZ_2M);
+
+ for (i = 0; i < NR_MEMFD_FOLIOS; i++) {
+ struct dma_region *r = regions[i];
+
+ r->vaddr = memfd_vaddr + (u64)i * SZ_2M;
+ r->size = SZ_2M;
+ iommu_map_identity(device->iommu, r);
+
+ if (before_kexec)
+ folio_pa[i] = r->iova;
+ else
+ VFIO_ASSERT_EQ(r->iova, folio_pa[i],
+ "Folio %d moved across kexec (0x%lx != 0x%lx)\n",
+ i, r->iova, folio_pa[i]);
+ }
+}
+
+static void dma_memfd_recheck_pa(struct vfio_pci_device *device)
+{
+ struct dma_region *regions[NR_MEMFD_FOLIOS] = {
+ &device->driver.region,
+ &src_region,
+ &dst_region,
+ };
+ int i;
+
+ for (i = 0; i < NR_MEMFD_FOLIOS; i++) {
+ u64 pa, len;
+
+ VFIO_ASSERT_EQ(__iommu_noiommu_get_pa(device->iommu,
+ regions[i]->iova, SZ_2M,
+ &pa, &len), 0);
+ VFIO_ASSERT_EQ(pa, folio_pa[i]);
+ VFIO_ASSERT_EQ(len, (u64)SZ_2M);
+ }
+}
+
+static void dma_memfd_regions_unmap(struct vfio_pci_device *device)
+{
+ iommu_unmap(device->iommu, &dst_region);
+ iommu_unmap(device->iommu, &src_region);
+ iommu_unmap(device->iommu, &device->driver.region);
+ VFIO_ASSERT_EQ(munmap(memfd_vaddr, MEMFD_SIZE), 0);
+ memfd_vaddr = NULL;
+}
+
+static void save_data_session(int luo_fd)
+{
+ char buf[256];
+ int data_fd;
+
+ data_fd = luo_create_session(luo_fd, data_session);
+ VFIO_ASSERT_GE(data_fd, 0);
+
+ snprintf_assert(buf, sizeof(buf), "0x%lx 0x%lx 0x%lx",
+ folio_pa[0], folio_pa[1], folio_pa[2]);
+ VFIO_ASSERT_EQ(create_and_preserve_memfd(data_fd, DATA_TOKEN, buf), 0);
+}
+
+static void load_data_session(int luo_fd)
+{
+ char buf[256] = {};
+ char *end, *cur;
+ int data_fd;
+ int mfd;
+ int i;
+
+ data_fd = luo_retrieve_session(luo_fd, data_session);
+ VFIO_ASSERT_GE(data_fd, 0);
+
+ mfd = luo_session_retrieve_fd(data_fd, DATA_TOKEN);
+ VFIO_ASSERT_GE(mfd, 0);
+ VFIO_ASSERT_GT(pread(mfd, buf, sizeof(buf) - 1, 0), 0);
+ VFIO_ASSERT_EQ(close(mfd), 0);
+
+ cur = buf;
+ for (i = 0; i < NR_MEMFD_FOLIOS; i++) {
+ errno = 0;
+ folio_pa[i] = strtoull(cur, &end, 0);
+ VFIO_ASSERT_EQ(errno, 0);
+ VFIO_ASSERT_NE(end, cur);
+ cur = end;
+ }
+
+ VFIO_ASSERT_EQ(luo_session_finish(data_fd), 0);
+ VFIO_ASSERT_EQ(close(data_fd), 0);
+}
+
static void before_kexec(int luo_fd)
{
struct vfio_pci_device *device;
struct iommu *iommu;
+ void *init_vaddr = NULL;
int session_fd;
+ int memfd;
int ret;
check_noiommu(device_bdf);
+ memfd = vfio_thp_memfd_alloc(MEMFD_SIZE, &init_vaddr);
+ if (memfd < 0)
+ ksft_exit_skip("Failed to allocate 2MB THP memfd: %s\n",
+ strerror(-memfd));
+ VFIO_ASSERT_EQ(munmap(init_vaddr, MEMFD_SIZE), 0);
+
iommu = iommu_init(MODE_IOMMUFD_NOIOMMU);
device = vfio_pci_device_init(device_bdf, iommu);
+ if (!device->driver.ops || !device->driver.ops->attach)
+ ksft_exit_skip("No driver that supports attach() found for %s\n",
+ device_bdf);
+
+ dma_memfd_regions_map(device, memfd, true);
+
create_state_file(luo_fd, state_session, STATE_TOKEN, /*next_stage=*/2);
session_fd = luo_create_session(luo_fd, device_session);
VFIO_ASSERT_GE(session_fd, 0);
+ /*
+ * Preserve the DMA memfd in the same session before the device so that
+ * LUO's LIFO finish order resets an unclaimed device before freeing
+ * the memfd folios.
+ */
+ ret = luo_session_preserve_fd(session_fd, memfd, MEMFD_TOKEN);
+ VFIO_ASSERT_EQ(ret, 0);
+
+ dma_memfd_recheck_pa(device);
+
ret = luo_session_preserve_fd(session_fd, device->fd, DEVICE_TOKEN);
VFIO_ASSERT_EQ(ret, 0);
+ vfio_pci_driver_init(device);
+ dma_memcpy_one(device, 'a');
+
+ dma_memcpy_start(device);
+
+ /*
+ * Disable interrupts on the device or freeze() will fail. The memcpy
+ * operations do not generate any.
+ */
+ vfio_pci_msix_disable(device);
+
+ save_data_session(luo_fd);
+
close(luo_fd);
daemonize_and_wait();
}
@@ -98,13 +332,16 @@ static void after_kexec(int luo_fd, int state_session_fd)
struct iommu *iommu;
int session_fd;
int device_fd;
+ int memfd;
int stage;
- check_open_vfio_device_fails();
-
restore_and_read_stage(state_session_fd, STATE_TOKEN, &stage);
VFIO_ASSERT_EQ(stage, 2);
+ load_data_session(luo_fd);
+
+ check_open_vfio_device_fails();
+
session_fd = luo_retrieve_session(luo_fd, device_session);
VFIO_ASSERT_GE(session_fd, 0);
@@ -112,16 +349,25 @@ static void after_kexec(int luo_fd, int state_session_fd)
VFIO_ASSERT_GE(device_fd, 0);
iommu = iommu_init(MODE_IOMMUFD_NOIOMMU);
-
- /*
- * This will invoke various ioctls on device_fd such as
- * VFIO_DEVICE_GET_INFO. So this is a decent sanity test
- * that LUO actually handed us back a valid VFIO device
- * file and not something else.
- */
device = __vfio_pci_device_init(device_bdf, iommu, device_fd);
+ memfd = luo_session_retrieve_fd(session_fd, MEMFD_TOKEN);
+ VFIO_ASSERT_GE(memfd, 0);
+
+ dma_memfd_regions_map(device, memfd, false);
+
+ vfio_pci_driver_attach(device, /*memcpy_in_progress=*/true);
+ dma_memcpy_wait(device);
+
+ /* The device was not reset, so it can be used without reinitializing it. */
+ dma_memcpy_one(device, 'y');
+
VFIO_ASSERT_EQ(luo_session_finish(session_fd), 0);
+ VFIO_ASSERT_EQ(close(session_fd), 0);
+
+ vfio_pci_driver_remove(device);
+ dma_memfd_regions_unmap(device);
+ VFIO_ASSERT_EQ(close(memfd), 0);
vfio_pci_device_cleanup(device);
iommu_cleanup(iommu);
@@ -133,6 +379,8 @@ int main(int argc, char *argv[])
snprintf_assert(device_session, sizeof(device_session),
"vfio-lu-device-%s", device_bdf);
+ snprintf_assert(data_session, sizeof(data_session),
+ "vfio-lu-data-%s", device_bdf);
snprintf_assert(state_session, sizeof(state_session),
"vfio-lu-state-%s", device_bdf);
--
2.56.0.317.g454f8302e1-goog
prev parent reply other threads:[~2026-11-02 15:37 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-02 15:36 [PATCH v6 00/27] VFIO PCI Live Update (noiommu mode) Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 01/27] vfio/pci: Factor out the reset logic in VFIO PCI device close path Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 02/27] vfio: Export various helpers from VFIO Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 03/27] liveupdate: Export symbols needed by modules Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 04/27] vfio/pci: Register a file handler with Live Update Orchestrator Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 05/27] PCI: liveupdate: Add helpers to preserve struct pci_saved_state Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 06/27] vfio: Add helpers to identify device files that can be preserved Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 07/27] vfio/pci: Preserve vfio-pci device files across Live Update Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 08/27] vfio: Introduce vfio_find_device() helper Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 09/27] vfio: Refactor vfio_device_fops_cdev_open() Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 10/27] vfio: Add API to open cdev device for Live Update restore Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 11/27] vfio/pci: Retrieve preserved device files after Live Update Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 12/27] vfio: Enforce preserved devices are retrieved via LIVEUPDATE_SESSION_RETRIEVE_FD Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 13/27] vfio/pci: Protect incoming preserved devices until they finish Live Update Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 14/27] vfio/pci: Take over preserved devices without resetting them Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 15/27] docs: liveupdate: Add documentation for VFIO PCI Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 16/27] vfio: selftests: Build liveupdate library in VFIO selftests Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 17/27] vfio: selftests: Add vfio_pci_liveupdate_uapi_test Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 18/27] vfio: selftests: Initialize vfio_pci_device using a VFIO cdev FD Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 19/27] vfio: selftests: Add Makefile support for TEST_GEN_PROGS_EXTENDED Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 20/27] vfio: selftests: Add iommufd noiommu mode Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 21/27] vfio: selftests: Add helpers to map DMA memory at IOVA == PA in " Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 22/27] vfio: selftests: Add helpers for Live Update DMA memory and sysfs state Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 23/27] vfio: selftests: Add an attach() driver op Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 24/27] vfio: selftests: Test preserving noiommu devices in vfio_pci_liveupdate_uapi_test Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 25/27] vfio: selftests: Add vfio_pci_liveupdate_kexec_test Vipin Sharma
2026-11-02 15:36 ` [PATCH v6 26/27] vfio: selftests: Verify that opening VFIO device fails during Live Update Vipin Sharma
2026-11-02 15:36 ` Vipin Sharma [this message]
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=20261102153656.1431701-28-vipinsh@google.com \
--to=vipinsh@google.com \
--cc=ajayachandra@nvidia.com \
--cc=alex@shazbot.org \
--cc=amastro@fb.com \
--cc=ankita@nvidia.com \
--cc=apopple@nvidia.com \
--cc=bhelgaas@google.com \
--cc=chrisl@kernel.org \
--cc=christian.koenig@amd.com \
--cc=corbet@lwn.net \
--cc=dmatlack@google.com \
--cc=graf@amazon.com \
--cc=jacob.pan@linux.microsoft.com \
--cc=jgg@nvidia.com \
--cc=jgg@ziepe.ca \
--cc=jrhilke@google.com \
--cc=julianr@linux.ibm.com \
--cc=kees@kernel.org \
--cc=kevin.tian@intel.com \
--cc=kexec@lists.infradead.org \
--cc=kvm@vger.kernel.org \
--cc=leon@kernel.org \
--cc=leonro@nvidia.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux-pci@vger.kernel.org \
--cc=lukas@wunner.de \
--cc=mattev@meta.com \
--cc=michal.winiarski@intel.com \
--cc=parav@nvidia.com \
--cc=pasha.tatashin@soleen.com \
--cc=praan@google.com \
--cc=pratyush@kernel.org \
--cc=rananta@google.com \
--cc=rdunlap@infradead.org \
--cc=rientjes@google.com \
--cc=rodrigo.vivi@intel.com \
--cc=rppt@kernel.org \
--cc=saeedm@nvidia.com \
--cc=schnelle@linux.ibm.com \
--cc=skhan@linuxfoundation.org \
--cc=skhawaja@google.com \
--cc=vivek.kasireddy@intel.com \
--cc=witu@nvidia.com \
--cc=yanjun.zhu@linux.dev \
--cc=yi.l.liu@intel.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®