* [PATCH v4 08/16] vfio: selftests: Build liveupdate library in VFIO selftests
From: Vipin Sharma @ 2026-05-11 23:47 UTC (permalink / raw)
To: kvm, linux-doc, linux-kernel, linux-kselftest, linux-pci
Cc: ajayachandra, alex, amastro, ankita, apopple, chrisl, corbet,
dmatlack, graf, jacob.pan, jgg, jgg, jrhilke, julianr, kevin.tian,
leon, leonro, lukas, michal.winiarski, parav, pasha.tatashin,
praan, pratyush, rananta, rientjes, rodrigo.vivi, rppt, saeedm,
skhan, skhawaja, vipinsh, vivek.kasireddy, witu, yanjun.zhu,
yi.l.liu
In-Reply-To: <20260511234802.2280368-1-vipinsh@google.com>
Import and build liveupdate selftest library in VFIO selftests.
It allows to use liveupdate ioctls in VFIO selftests
Co-developed-by: David Matlack <dmatlack@google.com>
Signed-off-by: David Matlack <dmatlack@google.com>
Signed-off-by: Vipin Sharma <vipinsh@google.com>
---
tools/testing/selftests/vfio/Makefile | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/vfio/Makefile b/tools/testing/selftests/vfio/Makefile
index 0684932d91bf..7865b2aa011c 100644
--- a/tools/testing/selftests/vfio/Makefile
+++ b/tools/testing/selftests/vfio/Makefile
@@ -20,6 +20,7 @@ TEST_FILES += scripts/setup.sh
include ../lib.mk
include lib/libvfio.mk
+include ../liveupdate/lib/libliveupdate.mk
CFLAGS += -I$(top_srcdir)/tools/include
CFLAGS += -MD
@@ -27,11 +28,15 @@ CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += -pthread
-$(TEST_GEN_PROGS): %: %.o $(LIBVFIO_O)
- $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< $(LIBVFIO_O) $(LDLIBS) -o $@
+LIBS_O := $(LIBVFIO_O)
+LIBS_O += $(LIBLIVEUPDATE_O)
+
+$(TEST_GEN_PROGS): %: %.o $(LIBS_O)
+ $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $< $(LIBS_O) $(LDLIBS) -o $@
TEST_GEN_PROGS_O = $(patsubst %, %.o, $(TEST_GEN_PROGS))
-TEST_DEP_FILES = $(patsubst %.o, %.d, $(TEST_GEN_PROGS_O) $(LIBVFIO_O))
+TEST_DEP_FILES := $(patsubst %.o, %.d, $(TEST_GEN_PROGS_O))
+TEST_DEP_FILES += $(patsubst %.o, %.d, $(LIBS_O))
-include $(TEST_DEP_FILES)
EXTRA_CLEAN += $(TEST_GEN_PROGS_O) $(TEST_DEP_FILES)
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related
* [PATCH v4 09/16] vfio: selftests: Add vfio_pci_liveupdate_uapi_test
From: Vipin Sharma @ 2026-05-11 23:47 UTC (permalink / raw)
To: kvm, linux-doc, linux-kernel, linux-kselftest, linux-pci
Cc: ajayachandra, alex, amastro, ankita, apopple, chrisl, corbet,
dmatlack, graf, jacob.pan, jgg, jgg, jrhilke, julianr, kevin.tian,
leon, leonro, lukas, michal.winiarski, parav, pasha.tatashin,
praan, pratyush, rananta, rientjes, rodrigo.vivi, rppt, saeedm,
skhan, skhawaja, vipinsh, vivek.kasireddy, witu, yanjun.zhu,
yi.l.liu
In-Reply-To: <20260511234802.2280368-1-vipinsh@google.com>
From: David Matlack <dmatlack@google.com>
Add a selftest to exercise preserving a various VFIO files through
/dev/liveupdate. Ensure that VFIO cdev device files can be preserved and
everything else (group-based device files, group files, and container
files) all fail.
Signed-off-by: David Matlack <dmatlack@google.com>
Signed-off-by: Vipin Sharma <vipinsh@google.com>
---
tools/testing/selftests/vfio/Makefile | 1 +
.../vfio/vfio_pci_liveupdate_uapi_test.c | 93 +++++++++++++++++++
2 files changed, 94 insertions(+)
create mode 100644 tools/testing/selftests/vfio/vfio_pci_liveupdate_uapi_test.c
diff --git a/tools/testing/selftests/vfio/Makefile b/tools/testing/selftests/vfio/Makefile
index 7865b2aa011c..df3e44660bd0 100644
--- a/tools/testing/selftests/vfio/Makefile
+++ b/tools/testing/selftests/vfio/Makefile
@@ -12,6 +12,7 @@ TEST_GEN_PROGS += vfio_iommufd_setup_test
TEST_GEN_PROGS += vfio_pci_device_test
TEST_GEN_PROGS += vfio_pci_device_init_perf_test
TEST_GEN_PROGS += vfio_pci_driver_test
+TEST_GEN_PROGS += vfio_pci_liveupdate_uapi_test
TEST_FILES += scripts/cleanup.sh
TEST_FILES += scripts/lib.sh
diff --git a/tools/testing/selftests/vfio/vfio_pci_liveupdate_uapi_test.c b/tools/testing/selftests/vfio/vfio_pci_liveupdate_uapi_test.c
new file mode 100644
index 000000000000..1d89b08ab0a4
--- /dev/null
+++ b/tools/testing/selftests/vfio/vfio_pci_liveupdate_uapi_test.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <libliveupdate.h>
+#include <libvfio.h>
+#include <kselftest_harness.h>
+
+static const char *device_bdf;
+
+FIXTURE(vfio_pci_liveupdate_uapi_test) {
+ int luo_fd;
+ int session_fd;
+ struct iommu *iommu;
+ struct vfio_pci_device *device;
+};
+
+FIXTURE_VARIANT(vfio_pci_liveupdate_uapi_test) {
+ const char *iommu_mode;
+};
+
+#define FIXTURE_VARIANT_ADD_IOMMU_MODE(_iommu_mode) \
+FIXTURE_VARIANT_ADD(vfio_pci_liveupdate_uapi_test, _iommu_mode) { \
+ .iommu_mode = #_iommu_mode, \
+}
+
+FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES();
+#undef FIXTURE_VARIANT_ADD_IOMMU_MODE
+
+FIXTURE_SETUP(vfio_pci_liveupdate_uapi_test)
+{
+ self->luo_fd = luo_open_device();
+ ASSERT_GE(self->luo_fd, 0);
+
+ self->session_fd = luo_create_session(self->luo_fd, "session");
+ ASSERT_GE(self->session_fd, 0);
+
+ self->iommu = iommu_init(variant->iommu_mode);
+ self->device = vfio_pci_device_init(device_bdf, self->iommu);
+}
+
+FIXTURE_TEARDOWN(vfio_pci_liveupdate_uapi_test)
+{
+ vfio_pci_device_cleanup(self->device);
+ iommu_cleanup(self->iommu);
+ close(self->session_fd);
+ close(self->luo_fd);
+}
+
+TEST_F(vfio_pci_liveupdate_uapi_test, preserve_device)
+{
+ int ret;
+
+ ret = luo_session_preserve_fd(self->session_fd, self->device->fd, 0);
+
+ /* Preservation should only be supported for VFIO cdev files. */
+ ASSERT_EQ(ret, self->iommu->iommufd ? 0 : -ENOENT);
+}
+
+TEST_F(vfio_pci_liveupdate_uapi_test, preserve_group_fails)
+{
+ int ret;
+
+ if (self->iommu->iommufd)
+ SKIP(return, "iommufd-mode does not have group files");
+
+ ret = luo_session_preserve_fd(self->session_fd, self->device->group_fd, 0);
+ ASSERT_EQ(ret, -ENOENT);
+}
+
+TEST_F(vfio_pci_liveupdate_uapi_test, preserve_container_fails)
+{
+ int ret;
+
+ if (self->iommu->iommufd)
+ SKIP(return, "iommufd-mode does not have container files");
+
+ ret = luo_session_preserve_fd(self->session_fd, self->iommu->container_fd, 0);
+ ASSERT_EQ(ret, -ENOENT);
+}
+
+int main(int argc, char *argv[])
+{
+ int fd;
+
+ fd = luo_open_device();
+ if (fd < 0)
+ ksft_exit_skip("open(%s) failed: %s, skipping\n",
+ LUO_DEVICE, strerror(errno));
+
+ close(fd);
+
+ device_bdf = vfio_selftests_get_bdf(&argc, argv);
+ return test_harness_run(argc, argv);
+}
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related
* [PATCH v4 10/16] vfio: selftests: Initialize vfio_pci_device using a VFIO cdev FD
From: Vipin Sharma @ 2026-05-11 23:47 UTC (permalink / raw)
To: kvm, linux-doc, linux-kernel, linux-kselftest, linux-pci
Cc: ajayachandra, alex, amastro, ankita, apopple, chrisl, corbet,
dmatlack, graf, jacob.pan, jgg, jgg, jrhilke, julianr, kevin.tian,
leon, leonro, lukas, michal.winiarski, parav, pasha.tatashin,
praan, pratyush, rananta, rientjes, rodrigo.vivi, rppt, saeedm,
skhan, skhawaja, vipinsh, vivek.kasireddy, witu, yanjun.zhu,
yi.l.liu
In-Reply-To: <20260511234802.2280368-1-vipinsh@google.com>
Use the given VFIO cdev FD to initialize vfio_pci_device in VFIO
selftests. Add the assertion to make sure that passed cdev FD is not
used with legacy VFIO APIs. If VFIO cdev FD is provided then do not open
the device instead use the FD for any interaction with the device.
This API will allow to write selftests where VFIO device FD is preserved
using liveupdate and retrieved later using liveupdate ioctl after kexec.
Co-developed-by: David Matlack <dmatlack@google.com>
Signed-off-by: David Matlack <dmatlack@google.com>
Signed-off-by: Vipin Sharma <vipinsh@google.com>
---
.../lib/include/libvfio/vfio_pci_device.h | 3 ++
.../selftests/vfio/lib/vfio_pci_device.c | 33 ++++++++++++++-----
2 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h b/tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h
index 2858885a89bb..896dfde88118 100644
--- a/tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h
+++ b/tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h
@@ -38,6 +38,9 @@ struct vfio_pci_device {
#define dev_info(_dev, _fmt, ...) printf("%s: " _fmt, (_dev)->bdf, ##__VA_ARGS__)
#define dev_err(_dev, _fmt, ...) fprintf(stderr, "%s: " _fmt, (_dev)->bdf, ##__VA_ARGS__)
+struct vfio_pci_device *__vfio_pci_device_init(const char *bdf,
+ struct iommu *iommu,
+ int device_fd);
struct vfio_pci_device *vfio_pci_device_init(const char *bdf, struct iommu *iommu);
void vfio_pci_device_cleanup(struct vfio_pci_device *device);
diff --git a/tools/testing/selftests/vfio/lib/vfio_pci_device.c b/tools/testing/selftests/vfio/lib/vfio_pci_device.c
index fc75e04ef010..7a81edb3245e 100644
--- a/tools/testing/selftests/vfio/lib/vfio_pci_device.c
+++ b/tools/testing/selftests/vfio/lib/vfio_pci_device.c
@@ -342,19 +342,27 @@ static void vfio_device_attach_iommufd_pt(int device_fd, u32 pt_id)
ioctl_assert(device_fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &args);
}
-static void vfio_pci_iommufd_setup(struct vfio_pci_device *device, const char *bdf)
+static void vfio_pci_iommufd_setup(struct vfio_pci_device *device,
+ const char *bdf, int device_fd)
{
- const char *cdev_path = vfio_pci_get_cdev_path(bdf);
+ const char *cdev_path;
- device->fd = open(cdev_path, O_RDWR);
- VFIO_ASSERT_GE(device->fd, 0);
- free((void *)cdev_path);
+ if (device_fd >= 0) {
+ device->fd = device_fd;
+ } else {
+ cdev_path = vfio_pci_get_cdev_path(bdf);
+ device->fd = open(cdev_path, O_RDWR);
+ VFIO_ASSERT_GE(device->fd, 0);
+ free((void *)cdev_path);
+ }
vfio_device_bind_iommufd(device->fd, device->iommu->iommufd);
vfio_device_attach_iommufd_pt(device->fd, device->iommu->ioas_id);
}
-struct vfio_pci_device *vfio_pci_device_init(const char *bdf, struct iommu *iommu)
+struct vfio_pci_device *__vfio_pci_device_init(const char *bdf,
+ struct iommu *iommu,
+ int device_fd)
{
struct vfio_pci_device *device;
@@ -365,10 +373,12 @@ struct vfio_pci_device *vfio_pci_device_init(const char *bdf, struct iommu *iomm
device->iommu = iommu;
device->bdf = bdf;
- if (iommu->mode->container_path)
+ if (iommu->mode->container_path) {
+ VFIO_ASSERT_EQ(device_fd, -1);
vfio_pci_container_setup(device, bdf);
- else
- vfio_pci_iommufd_setup(device, bdf);
+ } else {
+ vfio_pci_iommufd_setup(device, bdf, device_fd);
+ }
vfio_pci_device_setup(device);
vfio_pci_driver_probe(device);
@@ -376,6 +386,11 @@ struct vfio_pci_device *vfio_pci_device_init(const char *bdf, struct iommu *iomm
return device;
}
+struct vfio_pci_device *vfio_pci_device_init(const char *bdf, struct iommu *iommu)
+{
+ return __vfio_pci_device_init(bdf, iommu, /*device_fd=*/-1);
+}
+
void vfio_pci_device_cleanup(struct vfio_pci_device *device)
{
int i;
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related
* [PATCH v4 11/16] vfio: selftests: Add Makefile support for TEST_GEN_PROGS_EXTENDED
From: Vipin Sharma @ 2026-05-11 23:47 UTC (permalink / raw)
To: kvm, linux-doc, linux-kernel, linux-kselftest, linux-pci
Cc: ajayachandra, alex, amastro, ankita, apopple, chrisl, corbet,
dmatlack, graf, jacob.pan, jgg, jgg, jrhilke, julianr, kevin.tian,
leon, leonro, lukas, michal.winiarski, parav, pasha.tatashin,
praan, pratyush, rananta, rientjes, rodrigo.vivi, rppt, saeedm,
skhan, skhawaja, vipinsh, vivek.kasireddy, witu, yanjun.zhu,
yi.l.liu
In-Reply-To: <20260511234802.2280368-1-vipinsh@google.com>
From: David Matlack <dmatlack@google.com>
Add Makefile support for TEST_GEN_PROGS_EXTENDED targets. These tests
are not run by default.
TEST_GEN_PROGS_EXTENDED will be used for Live Update selftests in
subsequent commits. These selftests must be run manually because they
require the user/runner to perform additional actions, such as kexec,
during the test.
Signed-off-by: David Matlack <dmatlack@google.com>
Signed-off-by: Vipin Sharma <vipinsh@google.com>
---
tools/testing/selftests/vfio/Makefile | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/vfio/Makefile b/tools/testing/selftests/vfio/Makefile
index df3e44660bd0..ad081ec31fb2 100644
--- a/tools/testing/selftests/vfio/Makefile
+++ b/tools/testing/selftests/vfio/Makefile
@@ -32,14 +32,17 @@ LDFLAGS += -pthread
LIBS_O := $(LIBVFIO_O)
LIBS_O += $(LIBLIVEUPDATE_O)
-$(TEST_GEN_PROGS): %: %.o $(LIBS_O)
+$(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED): %: %.o $(LIBS_O)
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $< $(LIBS_O) $(LDLIBS) -o $@
-TEST_GEN_PROGS_O = $(patsubst %, %.o, $(TEST_GEN_PROGS))
-TEST_DEP_FILES := $(patsubst %.o, %.d, $(TEST_GEN_PROGS_O))
+TESTS_O := $(patsubst %, %.o, $(TEST_GEN_PROGS))
+TESTS_O += $(patsubst %, %.o, $(TEST_GEN_PROGS_EXTENDED))
+
+TEST_DEP_FILES := $(patsubst %.o, %.d, $(TESTS_O))
TEST_DEP_FILES += $(patsubst %.o, %.d, $(LIBS_O))
-include $(TEST_DEP_FILES)
-EXTRA_CLEAN += $(TEST_GEN_PROGS_O) $(TEST_DEP_FILES)
+EXTRA_CLEAN += $(TESTS_O)
+EXTRA_CLEAN += $(TEST_DEP_FILES)
endif
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related
* [PATCH v4 12/16] vfio: selftests: Add vfio_pci_liveupdate_kexec_test
From: Vipin Sharma @ 2026-05-11 23:47 UTC (permalink / raw)
To: kvm, linux-doc, linux-kernel, linux-kselftest, linux-pci
Cc: ajayachandra, alex, amastro, ankita, apopple, chrisl, corbet,
dmatlack, graf, jacob.pan, jgg, jgg, jrhilke, julianr, kevin.tian,
leon, leonro, lukas, michal.winiarski, parav, pasha.tatashin,
praan, pratyush, rananta, rientjes, rodrigo.vivi, rppt, saeedm,
skhan, skhawaja, vipinsh, vivek.kasireddy, witu, yanjun.zhu,
yi.l.liu
In-Reply-To: <20260511234802.2280368-1-vipinsh@google.com>
Add a selftest to exercise preserving a vfio-pci device across a Live
Update. For now the test is extremely simple and just verifies that the
device file can be preserved and retrieved. In the future this test will
be extended to verify more parts about device preservation as they are
implemented.
This test is added to TEST_GEN_PROGS_EXTENDED since it must be run
manually along with a kexec.
To run this test manually:
$ tools/testing/selftests/vfio/scripts/setup.sh 0000:00:04.0
$ tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test --stage 1 0000:00:04.0
$ kexec ... # NOTE: Exact method will be distro-dependent
$ tools/testing/selftests/vfio/scripts/setup.sh 0000:00:04.0
$ tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test --stage 2 0000:00:04.0
The second call to setup.sh is necessary because preserved devices are
not bound to a driver after Live Update. Such devices must be manually
bound by userspace after Live Update via driver_override.
This test is considered passing if all commands exit with 0.
Co-developed-by: David Matlack <dmatlack@google.com>
Signed-off-by: David Matlack <dmatlack@google.com>
Signed-off-by: Vipin Sharma <vipinsh@google.com>
---
tools/testing/selftests/vfio/Makefile | 4 +
.../vfio/vfio_pci_liveupdate_kexec_test.c | 89 +++++++++++++++++++
2 files changed, 93 insertions(+)
create mode 100644 tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c
diff --git a/tools/testing/selftests/vfio/Makefile b/tools/testing/selftests/vfio/Makefile
index ad081ec31fb2..17082a57aa9f 100644
--- a/tools/testing/selftests/vfio/Makefile
+++ b/tools/testing/selftests/vfio/Makefile
@@ -14,6 +14,10 @@ TEST_GEN_PROGS += vfio_pci_device_init_perf_test
TEST_GEN_PROGS += vfio_pci_driver_test
TEST_GEN_PROGS += vfio_pci_liveupdate_uapi_test
+# This test must be run manually since it requires the user/automation to
+# perform a kexec during the test.
+TEST_GEN_PROGS_EXTENDED += vfio_pci_liveupdate_kexec_test
+
TEST_FILES += scripts/cleanup.sh
TEST_FILES += scripts/lib.sh
TEST_FILES += scripts/run.sh
diff --git a/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c b/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c
new file mode 100644
index 000000000000..15b3e3af91d1
--- /dev/null
+++ b/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <libliveupdate.h>
+#include <libvfio.h>
+
+static const char *device_bdf;
+
+static char state_session[LIVEUPDATE_SESSION_NAME_LENGTH];
+static char device_session[LIVEUPDATE_SESSION_NAME_LENGTH];
+
+enum {
+ STATE_TOKEN,
+ DEVICE_TOKEN,
+};
+
+static void before_kexec(int luo_fd)
+{
+ struct vfio_pci_device *device;
+ struct iommu *iommu;
+ int session_fd;
+ int ret;
+
+ iommu = iommu_init("iommufd");
+ device = vfio_pci_device_init(device_bdf, iommu);
+
+ 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);
+
+ printf("Preserving device in session\n");
+ ret = luo_session_preserve_fd(session_fd, device->fd, DEVICE_TOKEN);
+ VFIO_ASSERT_EQ(ret, 0);
+
+ close(luo_fd);
+ daemonize_and_wait();
+}
+
+static void after_kexec(int luo_fd, int state_session_fd)
+{
+ struct vfio_pci_device *device;
+ struct iommu *iommu;
+ int session_fd;
+ int device_fd;
+ int stage;
+
+ restore_and_read_stage(state_session_fd, STATE_TOKEN, &stage);
+ VFIO_ASSERT_EQ(stage, 2);
+
+ session_fd = luo_retrieve_session(luo_fd, device_session);
+ VFIO_ASSERT_GE(session_fd, 0);
+
+ printf("Finishing the session before retrieving the device (should fail)\n");
+ VFIO_ASSERT_NE(luo_session_finish(session_fd), 0);
+
+ printf("Retrieving the device FD from LUO\n");
+ device_fd = luo_session_retrieve_fd(session_fd, DEVICE_TOKEN);
+ VFIO_ASSERT_GE(device_fd, 0);
+
+ printf("Finishing the session before binding to iommufd (should fail)\n");
+ VFIO_ASSERT_NE(luo_session_finish(session_fd), 0);
+
+ printf("Binding the device to an iommufd and setting it up\n");
+ iommu = iommu_init("iommufd");
+
+ /*
+ * 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);
+
+ printf("Finishing the session\n");
+ VFIO_ASSERT_EQ(luo_session_finish(session_fd), 0);
+
+ vfio_pci_device_cleanup(device);
+ iommu_cleanup(iommu);
+}
+
+int main(int argc, char *argv[])
+{
+ device_bdf = vfio_selftests_get_bdf(&argc, argv);
+
+ sprintf(device_session, "device-%s", device_bdf);
+ sprintf(state_session, "state-%s", device_bdf);
+
+ return luo_test(argc, argv, state_session, before_kexec, after_kexec);
+}
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related
* [PATCH v4 13/16] vfio: selftests: Expose iommu_modes to tests
From: Vipin Sharma @ 2026-05-11 23:47 UTC (permalink / raw)
To: kvm, linux-doc, linux-kernel, linux-kselftest, linux-pci
Cc: ajayachandra, alex, amastro, ankita, apopple, chrisl, corbet,
dmatlack, graf, jacob.pan, jgg, jgg, jrhilke, julianr, kevin.tian,
leon, leonro, lukas, michal.winiarski, parav, pasha.tatashin,
praan, pratyush, rananta, rientjes, rodrigo.vivi, rppt, saeedm,
skhan, skhawaja, vipinsh, vivek.kasireddy, witu, yanjun.zhu,
yi.l.liu
In-Reply-To: <20260511234802.2280368-1-vipinsh@google.com>
From: David Matlack <dmatlack@google.com>
Expose the list of iommu_modes to enable tests that want to iterate
through all possible iommu modes.
Signed-off-by: David Matlack <dmatlack@google.com>
Signed-off-by: Vipin Sharma <vipinsh@google.com>
---
tools/testing/selftests/vfio/lib/include/libvfio/iommu.h | 2 ++
tools/testing/selftests/vfio/lib/iommu.c | 4 +++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/vfio/lib/include/libvfio/iommu.h b/tools/testing/selftests/vfio/lib/include/libvfio/iommu.h
index e9a3386a4719..4b9cbe262159 100644
--- a/tools/testing/selftests/vfio/lib/include/libvfio/iommu.h
+++ b/tools/testing/selftests/vfio/lib/include/libvfio/iommu.h
@@ -15,6 +15,8 @@ struct iommu_mode {
unsigned long iommu_type;
};
+extern const struct iommu_mode iommu_modes[];
+extern const int nr_iommu_modes;
extern const char *default_iommu_mode;
struct dma_region {
diff --git a/tools/testing/selftests/vfio/lib/iommu.c b/tools/testing/selftests/vfio/lib/iommu.c
index 035dac069d60..95a494f829d2 100644
--- a/tools/testing/selftests/vfio/lib/iommu.c
+++ b/tools/testing/selftests/vfio/lib/iommu.c
@@ -23,7 +23,7 @@
const char *default_iommu_mode = MODE_IOMMUFD;
/* Reminder: Keep in sync with FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(). */
-static const struct iommu_mode iommu_modes[] = {
+const struct iommu_mode iommu_modes[] = {
{
.name = MODE_VFIO_TYPE1_IOMMU,
.container_path = "/dev/vfio/vfio",
@@ -49,6 +49,8 @@ static const struct iommu_mode iommu_modes[] = {
},
};
+const int nr_iommu_modes = ARRAY_SIZE(iommu_modes);
+
static const struct iommu_mode *lookup_iommu_mode(const char *iommu_mode)
{
int i;
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related
* [PATCH v4 14/16] vfio: selftests: Expose low-level helper routines for setting up struct vfio_pci_device
From: Vipin Sharma @ 2026-05-11 23:48 UTC (permalink / raw)
To: kvm, linux-doc, linux-kernel, linux-kselftest, linux-pci
Cc: ajayachandra, alex, amastro, ankita, apopple, chrisl, corbet,
dmatlack, graf, jacob.pan, jgg, jgg, jrhilke, julianr, kevin.tian,
leon, leonro, lukas, michal.winiarski, parav, pasha.tatashin,
praan, pratyush, rananta, rientjes, rodrigo.vivi, rppt, saeedm,
skhan, skhawaja, vipinsh, vivek.kasireddy, witu, yanjun.zhu,
yi.l.liu
In-Reply-To: <20260511234802.2280368-1-vipinsh@google.com>
From: David Matlack <dmatlack@google.com>
Expose a few low-level helper routings for setting up vfio_pci_device
structs. These routines will be used in a subsequent commit to assert
that VFIO_GROUP_GET_DEVICE_FD fails under certain conditions.
Signed-off-by: David Matlack <dmatlack@google.com>
Signed-off-by: Vipin Sharma <vipinsh@google.com>
---
.../lib/include/libvfio/vfio_pci_device.h | 5 +++
.../selftests/vfio/lib/vfio_pci_device.c | 33 +++++++++++++------
2 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h b/tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h
index 896dfde88118..2389c7698335 100644
--- a/tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h
+++ b/tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h
@@ -125,4 +125,9 @@ static inline bool vfio_pci_device_match(struct vfio_pci_device *device,
const char *vfio_pci_get_cdev_path(const char *bdf);
+/* Low-level routines for setting up a struct vfio_pci_device */
+struct vfio_pci_device *vfio_pci_device_alloc(const char *bdf, struct iommu *iommu);
+void vfio_pci_group_setup(struct vfio_pci_device *device);
+void vfio_pci_iommu_setup(struct vfio_pci_device *device);
+
#endif /* SELFTESTS_VFIO_LIB_INCLUDE_LIBVFIO_VFIO_PCI_DEVICE_H */
diff --git a/tools/testing/selftests/vfio/lib/vfio_pci_device.c b/tools/testing/selftests/vfio/lib/vfio_pci_device.c
index 7a81edb3245e..3152196261a2 100644
--- a/tools/testing/selftests/vfio/lib/vfio_pci_device.c
+++ b/tools/testing/selftests/vfio/lib/vfio_pci_device.c
@@ -222,7 +222,7 @@ static unsigned int vfio_pci_get_group_from_dev(const char *bdf)
return group;
}
-static void vfio_pci_group_setup(struct vfio_pci_device *device, const char *bdf)
+void vfio_pci_group_setup(struct vfio_pci_device *device)
{
struct vfio_group_status group_status = {
.argsz = sizeof(group_status),
@@ -230,7 +230,7 @@ static void vfio_pci_group_setup(struct vfio_pci_device *device, const char *bdf
char group_path[32];
int group;
- group = vfio_pci_get_group_from_dev(bdf);
+ group = vfio_pci_get_group_from_dev(device->bdf);
snprintf(group_path, sizeof(group_path), "/dev/vfio/%d", group);
device->group_fd = open(group_path, O_RDWR);
@@ -242,14 +242,12 @@ static void vfio_pci_group_setup(struct vfio_pci_device *device, const char *bdf
ioctl_assert(device->group_fd, VFIO_GROUP_SET_CONTAINER, &device->iommu->container_fd);
}
-static void vfio_pci_container_setup(struct vfio_pci_device *device, const char *bdf)
+void vfio_pci_iommu_setup(struct vfio_pci_device *device)
{
struct iommu *iommu = device->iommu;
unsigned long iommu_type = iommu->mode->iommu_type;
int ret;
- vfio_pci_group_setup(device, bdf);
-
ret = ioctl(iommu->container_fd, VFIO_CHECK_EXTENSION, iommu_type);
VFIO_ASSERT_GT(ret, 0, "VFIO IOMMU type %lu not supported\n", iommu_type);
@@ -259,8 +257,14 @@ static void vfio_pci_container_setup(struct vfio_pci_device *device, const char
* because the IOMMU type is already set.
*/
(void)ioctl(iommu->container_fd, VFIO_SET_IOMMU, (void *)iommu_type);
+}
- device->fd = ioctl(device->group_fd, VFIO_GROUP_GET_DEVICE_FD, bdf);
+static void vfio_pci_container_setup(struct vfio_pci_device *device)
+{
+ vfio_pci_group_setup(device);
+ vfio_pci_iommu_setup(device);
+
+ device->fd = ioctl(device->group_fd, VFIO_GROUP_GET_DEVICE_FD, device->bdf);
VFIO_ASSERT_GE(device->fd, 0);
}
@@ -360,9 +364,7 @@ static void vfio_pci_iommufd_setup(struct vfio_pci_device *device,
vfio_device_attach_iommufd_pt(device->fd, device->iommu->ioas_id);
}
-struct vfio_pci_device *__vfio_pci_device_init(const char *bdf,
- struct iommu *iommu,
- int device_fd)
+struct vfio_pci_device *vfio_pci_device_alloc(const char *bdf, struct iommu *iommu)
{
struct vfio_pci_device *device;
@@ -373,9 +375,20 @@ struct vfio_pci_device *__vfio_pci_device_init(const char *bdf,
device->iommu = iommu;
device->bdf = bdf;
+ return device;
+}
+
+struct vfio_pci_device *__vfio_pci_device_init(const char *bdf,
+ struct iommu *iommu,
+ int device_fd)
+{
+ struct vfio_pci_device *device;
+
+ device = vfio_pci_device_alloc(bdf, iommu);
+
if (iommu->mode->container_path) {
VFIO_ASSERT_EQ(device_fd, -1);
- vfio_pci_container_setup(device, bdf);
+ vfio_pci_container_setup(device);
} else {
vfio_pci_iommufd_setup(device, bdf, device_fd);
}
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related
* [PATCH v4 15/16] vfio: selftests: Verify that opening VFIO device fails during Live Update
From: Vipin Sharma @ 2026-05-11 23:48 UTC (permalink / raw)
To: kvm, linux-doc, linux-kernel, linux-kselftest, linux-pci
Cc: ajayachandra, alex, amastro, ankita, apopple, chrisl, corbet,
dmatlack, graf, jacob.pan, jgg, jgg, jrhilke, julianr, kevin.tian,
leon, leonro, lukas, michal.winiarski, parav, pasha.tatashin,
praan, pratyush, rananta, rientjes, rodrigo.vivi, rppt, saeedm,
skhan, skhawaja, vipinsh, vivek.kasireddy, witu, yanjun.zhu,
yi.l.liu
In-Reply-To: <20260511234802.2280368-1-vipinsh@google.com>
From: David Matlack <dmatlack@google.com>
Verify that opening a VFIO device through its cdev file and via
VFIO_GROUP_GET_DEVICE_FD both fail with -EBUSY if the device was
preserved across a Live Update. When a device file is preserve across a
Live Update, the file must be retrieved from /dev/liveupdate, not from
VFIO directly.
Signed-off-by: David Matlack <dmatlack@google.com>
Signed-off-by: Vipin Sharma <vipinsh@google.com>
---
.../vfio/vfio_pci_liveupdate_kexec_test.c | 38 +++++++++++++++++++
1 file changed, 38 insertions(+)
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 15b3e3af91d1..65c48196e44e 100644
--- a/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c
+++ b/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c
@@ -36,6 +36,42 @@ static void before_kexec(int luo_fd)
daemonize_and_wait();
}
+static void check_open_vfio_device_fails(void)
+{
+ const char *cdev_path = vfio_pci_get_cdev_path(device_bdf);
+ struct vfio_pci_device *device;
+ struct iommu *iommu;
+ int ret, i;
+
+ printf("Checking open(%s) fails\n", cdev_path);
+ ret = open(cdev_path, O_RDWR);
+ VFIO_ASSERT_EQ(ret, -1);
+ VFIO_ASSERT_EQ(errno, EBUSY);
+ free((void *)cdev_path);
+
+ for (i = 0; i < nr_iommu_modes; i++) {
+ if (!iommu_modes[i].container_path)
+ continue;
+
+ iommu = iommu_init(iommu_modes[i].name);
+
+ device = vfio_pci_device_alloc(device_bdf, iommu);
+ vfio_pci_group_setup(device);
+ vfio_pci_iommu_setup(device);
+
+ printf("Checking ioctl(group_fd, VFIO_GROUP_GET_DEVICE_FD, \"%s\") fails (%s)\n",
+ device_bdf, iommu_modes[i].name);
+
+ ret = ioctl(device->group_fd, VFIO_GROUP_GET_DEVICE_FD, device->bdf);
+ VFIO_ASSERT_EQ(ret, -1);
+ VFIO_ASSERT_EQ(errno, EBUSY);
+
+ close(device->group_fd);
+ free(device);
+ iommu_cleanup(iommu);
+ }
+}
+
static void after_kexec(int luo_fd, int state_session_fd)
{
struct vfio_pci_device *device;
@@ -44,6 +80,8 @@ static void after_kexec(int luo_fd, int state_session_fd)
int device_fd;
int stage;
+ check_open_vfio_device_fails();
+
restore_and_read_stage(state_session_fd, STATE_TOKEN, &stage);
VFIO_ASSERT_EQ(stage, 2);
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related
* [PATCH v4 16/16] vfio: selftests: Add continuous DMA to vfio_pci_liveupdate_kexec_test
From: Vipin Sharma @ 2026-05-11 23:48 UTC (permalink / raw)
To: kvm, linux-doc, linux-kernel, linux-kselftest, linux-pci
Cc: ajayachandra, alex, amastro, ankita, apopple, chrisl, corbet,
dmatlack, graf, jacob.pan, jgg, jgg, jrhilke, julianr, kevin.tian,
leon, leonro, lukas, michal.winiarski, parav, pasha.tatashin,
praan, pratyush, rananta, rientjes, rodrigo.vivi, rppt, saeedm,
skhan, skhawaja, vipinsh, vivek.kasireddy, witu, yanjun.zhu,
yi.l.liu
In-Reply-To: <20260511234802.2280368-1-vipinsh@google.com>
From: David Matlack <dmatlack@google.com>
Add a long-running DMA memcpy operation to
vfio_pci_liveupdate_kexec_test so that the device attempts to perform
DMAs continuously during the Live Update.
At this point iommufd preservation is not supported and bus mastering is
not kept enabled on the device during across the kexec, so most of these
DMAs will be dropped. However this test ensures that the current device
preservation support does not lead to system instability or crashes if
the device is active. And once iommufd and bus mastering are preserved,
this test can be relaxed to check that the DMA operations completed
successfully.
Signed-off-by: David Matlack <dmatlack@google.com>
Signed-off-by: Vipin Sharma <vipinsh@google.com>
---
.../vfio/vfio_pci_liveupdate_kexec_test.c | 129 ++++++++++++++++++
1 file changed, 129 insertions(+)
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 65c48196e44e..36bddfbb88ed 100644
--- a/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c
+++ b/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c
@@ -1,8 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/sizes.h>
+#include <sys/mman.h>
+
#include <libliveupdate.h>
#include <libvfio.h>
+#define MEMCPY_SIZE SZ_1G
+#define DRIVER_SIZE SZ_1M
+#define MEMFD_SIZE (MEMCPY_SIZE + DRIVER_SIZE)
+
+static struct dma_region memcpy_region;
static const char *device_bdf;
static char state_session[LIVEUPDATE_SESSION_NAME_LENGTH];
@@ -11,8 +19,89 @@ static char device_session[LIVEUPDATE_SESSION_NAME_LENGTH];
enum {
STATE_TOKEN,
DEVICE_TOKEN,
+ MEMFD_TOKEN,
};
+static void dma_memcpy_one(struct vfio_pci_device *device)
+{
+ void *src = memcpy_region.vaddr, *dst;
+ u64 size;
+
+ size = min_t(u64, memcpy_region.size / 2, device->driver.max_memcpy_size);
+ dst = src + size;
+
+ memset(src, 1, size);
+ memset(dst, 0, size);
+
+ printf("Kicking off 1 DMA memcpy operations of size 0x%lx...\n", size);
+ vfio_pci_driver_memcpy(device,
+ to_iova(device, src),
+ to_iova(device, dst),
+ size);
+
+ VFIO_ASSERT_EQ(memcmp(src, dst, size), 0);
+}
+
+static void dma_memcpy_start(struct vfio_pci_device *device)
+{
+ void *src = memcpy_region.vaddr, *dst;
+ u64 count, size;
+
+ size = min_t(u64, memcpy_region.size / 2, device->driver.max_memcpy_size);
+ dst = src + size;
+
+ /*
+ * 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, 1, size / 2);
+ memset(dst, 0, size / 2);
+
+ printf("Kicking off %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_memfd_map(struct vfio_pci_device *device, int fd)
+{
+ void *vaddr;
+
+ vaddr = mmap(NULL, MEMFD_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
+ VFIO_ASSERT_NE(vaddr, MAP_FAILED);
+
+ memcpy_region.iova = SZ_4G;
+ memcpy_region.size = MEMCPY_SIZE;
+ memcpy_region.vaddr = vaddr;
+ iommu_map(device->iommu, &memcpy_region);
+
+ device->driver.region.iova = memcpy_region.iova + memcpy_region.size;
+ device->driver.region.size = DRIVER_SIZE;
+ device->driver.region.vaddr = vaddr + memcpy_region.size;
+ iommu_map(device->iommu, &device->driver.region);
+}
+
+static void dma_memfd_setup(struct vfio_pci_device *device, int session_fd)
+{
+ int fd, ret;
+
+ fd = memfd_create("dma-buffer", 0);
+ VFIO_ASSERT_GE(fd, 0);
+
+ ret = fallocate(fd, 0, 0, MEMFD_SIZE);
+ VFIO_ASSERT_EQ(ret, 0);
+
+ printf("Preserving memfd of size 0x%x in session\n", MEMFD_SIZE);
+ ret = luo_session_preserve_fd(session_fd, fd, MEMFD_TOKEN);
+ VFIO_ASSERT_EQ(ret, 0);
+
+ dma_memfd_map(device, fd);
+}
+
static void before_kexec(int luo_fd)
{
struct vfio_pci_device *device;
@@ -32,6 +121,27 @@ static void before_kexec(int luo_fd)
ret = luo_session_preserve_fd(session_fd, device->fd, DEVICE_TOKEN);
VFIO_ASSERT_EQ(ret, 0);
+ dma_memfd_setup(device, session_fd);
+
+ /*
+ * If the device has a selftests driver, kick off a long-running DMA
+ * operation to exercise the device trying to DMA during a Live Update.
+ * Since iommufd preservation is not supported yet, these DMAs should be
+ * dropped. So this is just looking to verify that the system does not
+ * fall over and crash as a result of a busy device being preserved.
+ */
+ if (device->driver.ops) {
+ vfio_pci_driver_init(device);
+ dma_memcpy_start(device);
+
+ /*
+ * Disable interrupts on the device or freeze() will fail.
+ * Unfortunately there isn't a way to easily have a test for
+ * that here since the check happens during shutdown.
+ */
+ vfio_pci_msix_disable(device);
+ }
+
close(luo_fd);
daemonize_and_wait();
}
@@ -78,6 +188,7 @@ 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();
@@ -88,6 +199,10 @@ static void after_kexec(int luo_fd, int state_session_fd)
session_fd = luo_retrieve_session(luo_fd, device_session);
VFIO_ASSERT_GE(session_fd, 0);
+ printf("Retrieving memfd from LUO\n");
+ memfd = luo_session_retrieve_fd(session_fd, MEMFD_TOKEN);
+ VFIO_ASSERT_GE(memfd, 0);
+
printf("Finishing the session before retrieving the device (should fail)\n");
VFIO_ASSERT_NE(luo_session_finish(session_fd), 0);
@@ -109,9 +224,23 @@ static void after_kexec(int luo_fd, int state_session_fd)
*/
device = __vfio_pci_device_init(device_bdf, iommu, device_fd);
+ dma_memfd_map(device, memfd);
+
printf("Finishing the session\n");
VFIO_ASSERT_EQ(luo_session_finish(session_fd), 0);
+ /*
+ * Once iommufd preservation is supported and the device is kept fully
+ * running across the Live Update, this should wait for the long-
+ * running DMA memcpy operation kicked off in before_kexec() to
+ * complete. But for now we expect the device to be reset so just
+ * trigger a single memcpy to make sure it's still functional.
+ */
+ if (device->driver.ops) {
+ vfio_pci_driver_init(device);
+ dma_memcpy_one(device);
+ }
+
vfio_pci_device_cleanup(device);
iommu_cleanup(iommu);
}
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related
* [PATCH] docs: fix spelling of Shepherd in howto
From: Chen-Shi-Hong @ 2026-05-12 0:09 UTC (permalink / raw)
To: corbet; +Cc: skhan, workflows, linux-doc, linux-kernel, Chen-Shi-Hong
Correct the spelling of "Shepherd" in the acknowledgements section of
Documentation/process/howto.rst.
Signed-off-by: Chen-Shi-Hong <eric039eric@gmail.com>
---
Documentation/process/howto.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/process/howto.rst b/Documentation/process/howto.rst
index 9438e03d6f50..edf4412de112 100644
--- a/Documentation/process/howto.rst
+++ b/Documentation/process/howto.rst
@@ -616,7 +616,7 @@ Huizenga for some of the list of things you should and should not say.
Also thanks to Pat Mochel, Hanna Linder, Randy Dunlap, Kay Sievers,
Vojtech Pavlik, Jan Kara, Josh Boyer, Kees Cook, Andrew Morton, Andi
Kleen, Vadim Lobanov, Jesper Juhl, Adrian Bunk, Keri Harris, Frans Pop,
-David A. Wheeler, Junio Hamano, Michael Kerrisk, and Alex Shepard for
+David A. Wheeler, Junio Hamano, Michael Kerrisk, and Alex Shepherd for
their review, comments, and contributions. Without their help, this
document would not have been possible.
base-commit: 5d6919055dec134de3c40167a490f33c74c12581
--
2.53.0
^ permalink raw reply related
* Re: [PATCH] docs: fix spelling of Shepherd in howto
From: Randy Dunlap @ 2026-05-12 0:41 UTC (permalink / raw)
To: Chen-Shi-Hong, corbet; +Cc: skhan, workflows, linux-doc, linux-kernel
In-Reply-To: <20260512000946.3234-1-eric039eric@gmail.com>
Hi,
On 5/11/26 5:09 PM, Chen-Shi-Hong wrote:
> Correct the spelling of "Shepherd" in the acknowledgements section of
> Documentation/process/howto.rst.
How do you know that the last name is misspelled?
I found email from "Alex Shepard" on lore.kernel.org/all/ but none from
Alex Shepherd.
https://lore.kernel.org/all/1137702713.3205.43.camel@athena.sea.amer.gettywan.com/
Names can often be spelled many ways.
> Signed-off-by: Chen-Shi-Hong <eric039eric@gmail.com>
> ---
> Documentation/process/howto.rst | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/Documentation/process/howto.rst b/Documentation/process/howto.rst
> index 9438e03d6f50..edf4412de112 100644
> --- a/Documentation/process/howto.rst
> +++ b/Documentation/process/howto.rst
> @@ -616,7 +616,7 @@ Huizenga for some of the list of things you should and should not say.
> Also thanks to Pat Mochel, Hanna Linder, Randy Dunlap, Kay Sievers,
> Vojtech Pavlik, Jan Kara, Josh Boyer, Kees Cook, Andrew Morton, Andi
> Kleen, Vadim Lobanov, Jesper Juhl, Adrian Bunk, Keri Harris, Frans Pop,
> -David A. Wheeler, Junio Hamano, Michael Kerrisk, and Alex Shepard for
> +David A. Wheeler, Junio Hamano, Michael Kerrisk, and Alex Shepherd for
> their review, comments, and contributions. Without their help, this
> document would not have been possible.
>
>
> base-commit: 5d6919055dec134de3c40167a490f33c74c12581
--
~Randy
^ permalink raw reply
* [PATCH net-next v4 0/8] net: devmem: support devmem with netkit devices
From: Bobby Eshleman @ 2026-05-12 1:17 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan
Cc: dw, sdf.kernel, mohsin.bashr, willemb, jiang.kun2, xu.xin16,
wang.yaxin, netdev, linux-doc, linux-kernel, linux-rdma, bpf,
linux-kselftest, Stanislav Fomichev, Mina Almasry, netdev,
linux-doc, linux-kernel, linux-rdma, bpf, linux-kselftest,
Bobby Eshleman
This series enables TCP devmem TX through netkit devices.
Netkit now supports queue leasing. A physical NIC's RX queue can be
leased to a netkit guest interface inside a container namespace. This
gives the container a devmem-capable data path on the RX side (bind-rx,
etc...). On the TX side, the container process binds to its netkit guest
interface and sends traffic that netkit redirects (via BPF or ip
forwarding) to the physical NIC for DMA.
Two things in the existing devmem TX path prevent this from working:
1. validate_xmit_unreadable_skb() requires dev->netmem_tx before it will
forward a dmabuf-backed (unreadable) skb. This protects skbs from
landing on devices that don't have the IOMMU mappings for the backing
dmabuf or that don't speak netmem. Netkit, however, does not support
DMA, doesn't attempt to read unreadable skb pages and so doesn't
break netmem (it is pure skb routing and redirection). It is
functionally capable of routing unreadable skbs, but there is no way
for the TX validation pathway to distinguish between a device that
will actually attempt DMA-ing the skb and another device
(like netkit) that does not DMA but also does not break
netmem.
2. bind_tx_doit uses the bound device as the DMA device. When the user
binds devmem TX to the netkit guest, the bind handler attempts to
create DMA mappings against netkit, which has no DMA capability and
no IOMMU mappings.
This series solves these problems as follows:
1. Extend netmem_tx to two bits, assigned to one of three values:
NETMEM_TX_NONE - netmem not supported
NETMEM_TX_DMA - netmem supported and performs DMA
NETMEM_TX_NO_DMA - netmem supported, but does not DMA
With these bits, phys devices can set NETMEM_TX_DMA and devices like
netkit set NETMEM_TX_NO_DMA. The validation TX path ensures that any
DMA-capable netdev exactly matches the bound device, guaranteeing the
correct mapping of the bound dmabuf. The validation TX path also
allows devices with NETMEM_TX_NO_DMA to pass, knowing these devices
will not misuse netmem or run into IOMMU faults. After redirection or
routing and the skb finally makes its way through the stack to a
physical device's TX path, the above NETMEM_TX_DMA check is performed
again to guarantee the device has the appropriate binding/mappings.
2. On TX bind, the bind handler recognizes NETMEM_TX_NO_DMA devices and
finds the phys TX device and binds to that instead. For the netkit
case, if it has been leased a queue from a DMA-capable device
already, then the bind action is performed on the DMA-capable device
instead and the dmabuf is mapped correctly.
---
Changes in v4:
- remove enum list from netmem_tx comment (Stan)
- fold NETMEM_TX_NO_DMA check in validate_xmit_unreadable_skb() into
skb_frags_readable check (Stan, Jakub)
- change binding->vdev to void ptr cookie with comment (Jakub)
- Fixed the bad change list version number (Stan)
- Link to v3: https://lore.kernel.org/r/20260507-tcp-dm-netkit-v3-0-52821445867c@meta.com
Changes in v3:
- Fix validate_xmit_unreadable_skb() logic for non-devmem
unreadable niovs (should not be dropped) (Sashiko)
- Simplify lock handling in bind_tx, no premature release (Jakub)
- split NO_DMA changes into separate patch (Jakub)
- fixed some pylint issues, one required an additional patch ("selftests:
drv-net: make attr _nk_guest_ifname public") to rename a variable from
private to public
- see per-patch changelist for more detailed changes
- Link to v2: https://lore.kernel.org/r/20260504-tcp-dm-netkit-v2-0-56d52ac72fd4@meta.com
Changes in v2:
- Squash driver conversion patches (2-5) into patch 1 (Jakub)
- In validate_xmit_unreadable_skb() to check netmem_tx mode before inspecting
frags (Jakub)
- Lock bind_dev around netdev_queue_get_dma_dev() when bind_dev != netdev to
fix lockdep (Sashiko)
- Move require_devmem() into individual test functions so KsftSkipEx goes up to
ksft_run() (Sashiko)
- Add nk_devmem.py to TEST_PROGS in Makefile (Sashiko)
- Link to v1:
https://lore.kernel.org/all/20260428-tcp-dm-netkit-v1-0-719280eba4d2@meta.com/
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
Bobby Eshleman (8):
net: convert netmem_tx flag to enum
net: netkit: declare NETMEM_TX_NO_DMA mode
net: devmem: support TX over NETMEM_TX_NO_DMA devices
selftests: drv-net: ncdevmem: add -n flag to skip NIC configuration
selftests: drv-net: make attr _nk_guest_ifname public
selftests: drv-net: refactor devmem command builders into lib module
selftests: drv-net: add primary_rx_redirect support to NetDrvContEnv
selftests: drv-net: add netkit devmem tests
.../networking/net_cachelines/net_device.rst | 2 +-
Documentation/networking/netmem.rst | 8 +-
.../translations/zh_CN/networking/netmem.rst | 7 +-
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +-
drivers/net/ethernet/google/gve/gve_main.c | 2 +-
drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +-
drivers/net/ethernet/meta/fbnic/fbnic_netdev.c | 2 +-
drivers/net/netkit.c | 1 +
include/linux/netdevice.h | 10 +-
net/core/dev.c | 5 +-
net/core/devmem.c | 6 +-
net/core/devmem.h | 10 +-
net/core/netdev-genl.c | 65 +++++-
tools/testing/selftests/drivers/net/hw/Makefile | 1 +
tools/testing/selftests/drivers/net/hw/devmem.py | 77 ++------
.../selftests/drivers/net/hw/lib/py/devmem.py | 218 +++++++++++++++++++++
tools/testing/selftests/drivers/net/hw/ncdevmem.c | 58 +++---
.../testing/selftests/drivers/net/hw/nk_devmem.py | 55 ++++++
.../drivers/net/hw/nk_primary_rx_redirect.bpf.c | 39 ++++
.../testing/selftests/drivers/net/hw/nk_qlease.py | 8 +-
tools/testing/selftests/drivers/net/lib/py/env.py | 109 ++++++++---
21 files changed, 548 insertions(+), 139 deletions(-)
---
base-commit: 790ead9394860e7d70c5e0e50a35b243e909a618
change-id: 20260423-tcp-dm-netkit-2bd78b638d30
Best regards,
--
Bobby Eshleman <bobbyeshleman@meta.com>
^ permalink raw reply
* [PATCH net-next v4 1/8] net: convert netmem_tx flag to enum
From: Bobby Eshleman @ 2026-05-12 1:17 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan
Cc: dw, sdf.kernel, mohsin.bashr, willemb, jiang.kun2, xu.xin16,
wang.yaxin, netdev, linux-doc, linux-kernel, linux-rdma, bpf,
linux-kselftest, Stanislav Fomichev, Mina Almasry, netdev,
linux-doc, linux-kernel, linux-rdma, bpf, linux-kselftest,
Bobby Eshleman
In-Reply-To: <20260511-tcp-dm-netkit-v4-0-841b78b99d74@meta.com>
From: Bobby Eshleman <bobbyeshleman@meta.com>
Devices that support netmem TX previously set dev->netmem_tx = true.
This was checked in validate_xmit_unreadable_skb() to drop unreadable
skbs (skbs with dmabuf-backed frags) before they reach drivers that
would mishandle them or devices that would not have the iommu mappings
for them.
A subsequent patch will introduce a third state for virtual devices
that forward unreadable skbs without ever performing DMA on them. To
prepare for that, convert the boolean dev->netmem_tx into an enum:
NETMEM_TX_NONE - no netmem TX support (drop unreadable skbs)
NETMEM_TX_DMA - full support, device does DMA
Update the existing NIC drivers (bnxt, gve, mlx5, fbnic) and the
validators in net/core to use the new enum. No functional change.
Acked-by: Harshitha Ramamurthy <hramamurthy@google.com>
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
Changes in v4:
- netdevice.h: netmem_tx enum list -> only "device netmem TX mode" in
comment (Stan)
Changes in v3:
- Split NO_DMA changes into subsequent commit (Jakub)
- Move !netdev->netmem_tx -> netdev->netmem_tx ==
NETMEM_TX_NONE conversions to this patch (Jakub)
Changes in v2:
- Squash driver conversion patches (2-5) into patch 1 (Jakub)
---
Documentation/networking/netmem.rst | 5 ++++-
Documentation/translations/zh_CN/networking/netmem.rst | 4 +++-
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +-
drivers/net/ethernet/google/gve/gve_main.c | 2 +-
drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +-
drivers/net/ethernet/meta/fbnic/fbnic_netdev.c | 2 +-
include/linux/netdevice.h | 7 ++++++-
net/core/dev.c | 2 +-
net/core/netdev-genl.c | 2 +-
9 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/Documentation/networking/netmem.rst b/Documentation/networking/netmem.rst
index b63aded46337..5ccadba4f373 100644
--- a/Documentation/networking/netmem.rst
+++ b/Documentation/networking/netmem.rst
@@ -95,4 +95,7 @@ Driver TX Requirements
netdev@, or reach out to the maintainers and/or almasrymina@google.com for
help adding the netmem API.
-2. Driver should declare support by setting `netdev->netmem_tx = true`
+2. Driver should declare support by setting `netdev->netmem_tx` to the
+ appropriate mode:
+
+ - `NETMEM_TX_DMA`: for physical devices that perform DMA.
diff --git a/Documentation/translations/zh_CN/networking/netmem.rst b/Documentation/translations/zh_CN/networking/netmem.rst
index fe351a240f02..9c84423b7528 100644
--- a/Documentation/translations/zh_CN/networking/netmem.rst
+++ b/Documentation/translations/zh_CN/networking/netmem.rst
@@ -89,4 +89,6 @@ dma-mapping API 去处理。
使用某个还不存在的 netmem API,你可以自行添加并提交到 netdev@,也可以联系维护
人员或者发送邮件至 almasrymina@google.com 寻求帮助。
-2. 驱动程序应通过设置 netdev->netmem_tx = true 来表明自身支持 netmem 功能。
+2. 驱动程序应将 `netdev->netmem_tx` 设置为适当的模式:
+
+ - `NETMEM_TX_DMA`:适用于执行 DMA 的物理设备。
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 8c55874f44ca..ed9c22dc4a5a 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -17120,7 +17120,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->queue_mgmt_ops = &bnxt_queue_mgmt_ops_unsupp;
if (BNXT_SUPPORTS_QUEUE_API(bp))
dev->queue_mgmt_ops = &bnxt_queue_mgmt_ops;
- dev->netmem_tx = true;
+ dev->netmem_tx = NETMEM_TX_DMA;
rc = register_netdev(dev);
if (rc)
diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c
index 424d973c97f2..dd2b8f087163 100644
--- a/drivers/net/ethernet/google/gve/gve_main.c
+++ b/drivers/net/ethernet/google/gve/gve_main.c
@@ -2894,7 +2894,7 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto abort_with_wq;
if (!gve_is_gqi(priv) && !gve_is_qpl(priv))
- dev->netmem_tx = true;
+ dev->netmem_tx = NETMEM_TX_DMA;
err = register_netdev(dev);
if (err)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 5a46870c4b74..fc49aae38807 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -5924,7 +5924,7 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev)
netdev->priv_flags |= IFF_UNICAST_FLT;
- netdev->netmem_tx = true;
+ netdev->netmem_tx = NETMEM_TX_DMA;
netif_set_tso_max_size(netdev, GSO_MAX_SIZE);
mlx5e_set_xdp_feature(priv);
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c
index c406a3b56b37..138e522ef9b9 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c
@@ -752,7 +752,7 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd)
netdev->netdev_ops = &fbnic_netdev_ops;
netdev->stat_ops = &fbnic_stat_ops;
netdev->queue_mgmt_ops = &fbnic_queue_mgmt_ops;
- netdev->netmem_tx = true;
+ netdev->netmem_tx = NETMEM_TX_DMA;
fbnic_set_ethtool_ops(netdev);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 0e1e581efc5a..a54a37fab8d9 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1788,6 +1788,11 @@ enum netdev_stat_type {
NETDEV_PCPU_STAT_DSTATS, /* struct pcpu_dstats */
};
+enum netmem_tx_mode {
+ NETMEM_TX_NONE, /* no netmem TX support */
+ NETMEM_TX_DMA, /* DMA-capable netmem TX (real HW) */
+};
+
enum netdev_reg_state {
NETREG_UNINITIALIZED = 0,
NETREG_REGISTERED, /* completed register_netdevice */
@@ -1809,7 +1814,7 @@ enum netdev_reg_state {
* @lltx: device supports lockless Tx. Deprecated for real HW
* drivers. Mainly used by logical interfaces, such as
* bonding and tunnels
- * @netmem_tx: device support netmem_tx.
+ * @netmem_tx: device netmem TX mode
*
* @name: This is the first field of the "visible" part of this structure
* (i.e. as seen by users in the "Space.c" file). It is the name
diff --git a/net/core/dev.c b/net/core/dev.c
index 06c195906231..fbe4c328a367 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3996,7 +3996,7 @@ static struct sk_buff *validate_xmit_unreadable_skb(struct sk_buff *skb,
if (likely(skb_frags_readable(skb)))
goto out;
- if (!dev->netmem_tx)
+ if (dev->netmem_tx == NETMEM_TX_NONE)
goto out_free;
shinfo = skb_shinfo(skb);
diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c
index b8f6076d8007..4d2c49371cdb 100644
--- a/net/core/netdev-genl.c
+++ b/net/core/netdev-genl.c
@@ -1164,7 +1164,7 @@ int netdev_nl_bind_tx_doit(struct sk_buff *skb, struct genl_info *info)
goto err_unlock_netdev;
}
- if (!netdev->netmem_tx) {
+ if (netdev->netmem_tx == NETMEM_TX_NONE) {
err = -EOPNOTSUPP;
NL_SET_ERR_MSG(info->extack,
"Driver does not support netmem TX");
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v4 2/8] net: netkit: declare NETMEM_TX_NO_DMA mode
From: Bobby Eshleman @ 2026-05-12 1:17 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan
Cc: dw, sdf.kernel, mohsin.bashr, willemb, jiang.kun2, xu.xin16,
wang.yaxin, netdev, linux-doc, linux-kernel, linux-rdma, bpf,
linux-kselftest, Stanislav Fomichev, Mina Almasry, netdev,
linux-doc, linux-kernel, linux-rdma, bpf, linux-kselftest,
Bobby Eshleman
In-Reply-To: <20260511-tcp-dm-netkit-v4-0-841b78b99d74@meta.com>
From: Bobby Eshleman <bobbyeshleman@meta.com>
Some virtual devices like netkit (or ifb) never DMA and never touch frag
contents, they just forward the skb to another device. They are unable
to forward unreadable skbs, however, because they fail to pass TX
validation checks on dev->netmem_tx. The existing two-state
NETMEM_TX_NONE / NETMEM_TX_DMA doesn't give the TX validator enough
information to differentiate devices that will attempt DMA on the
unreadable skb from those that will simply route it untouched.
Add a third mode to the enum so drivers can indicate 1) if they have
netmem TX support, and 2) if they do, whether they are DMA-capable:
NETMEM_TX_NO_DMA - pass-through, device never DMAs
Widen dev->netmem_tx from a 1-bit field to 2 bits to fit the new value,
and declare netkit as NETMEM_TX_NO_DMA. Devmem TX support over these
devices comes in a follow-up patch.
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
Changes in v3:
- net_cachelines/net_device.rst: align the netmem_tx row's type column
with the rest of the table by using "unsigned_long:2" instead of
"unsigned long:2"
- Split this into a distinct patch (Jakub)
---
Documentation/networking/net_cachelines/net_device.rst | 2 +-
Documentation/networking/netmem.rst | 3 +++
Documentation/translations/zh_CN/networking/netmem.rst | 3 +++
drivers/net/netkit.c | 1 +
include/linux/netdevice.h | 3 ++-
5 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/Documentation/networking/net_cachelines/net_device.rst b/Documentation/networking/net_cachelines/net_device.rst
index 1c19bb7705df..7b3392553fd6 100644
--- a/Documentation/networking/net_cachelines/net_device.rst
+++ b/Documentation/networking/net_cachelines/net_device.rst
@@ -10,7 +10,7 @@ Type Name fastpath_tx_acce
=================================== =========================== =================== =================== ===================================================================================
unsigned_long:32 priv_flags read_mostly __dev_queue_xmit(tx)
unsigned_long:1 lltx read_mostly HARD_TX_LOCK,HARD_TX_TRYLOCK,HARD_TX_UNLOCK(tx)
-unsigned long:1 netmem_tx:1; read_mostly
+unsigned_long:2 netmem_tx:2; read_mostly
char name[16]
struct netdev_name_node* name_node
struct dev_ifalias* ifalias
diff --git a/Documentation/networking/netmem.rst b/Documentation/networking/netmem.rst
index 5ccadba4f373..217869d1108d 100644
--- a/Documentation/networking/netmem.rst
+++ b/Documentation/networking/netmem.rst
@@ -99,3 +99,6 @@ Driver TX Requirements
appropriate mode:
- `NETMEM_TX_DMA`: for physical devices that perform DMA.
+
+ - `NETMEM_TX_NO_DMA`: for virtual or passthrough devices that do
+ not DMA, but still support handling of netmem-backed skbs.
diff --git a/Documentation/translations/zh_CN/networking/netmem.rst b/Documentation/translations/zh_CN/networking/netmem.rst
index 9c84423b7528..320f3eacf51b 100644
--- a/Documentation/translations/zh_CN/networking/netmem.rst
+++ b/Documentation/translations/zh_CN/networking/netmem.rst
@@ -92,3 +92,6 @@ dma-mapping API 去处理。
2. 驱动程序应将 `netdev->netmem_tx` 设置为适当的模式:
- `NETMEM_TX_DMA`:适用于执行 DMA 的物理设备。
+
+ - `NETMEM_TX_NO_DMA`:适用于不执行 DMA 的虚拟或透传设备,但仍支持
+ 处理 netmem 支持的 skb。
diff --git a/drivers/net/netkit.c b/drivers/net/netkit.c
index 5e2eecc3165d..0ad6a806d7d5 100644
--- a/drivers/net/netkit.c
+++ b/drivers/net/netkit.c
@@ -466,6 +466,7 @@ static void netkit_setup(struct net_device *dev)
dev->priv_flags |= IFF_NO_QUEUE;
dev->priv_flags |= IFF_DISABLE_NETPOLL;
dev->lltx = true;
+ dev->netmem_tx = NETMEM_TX_NO_DMA;
dev->netdev_ops = &netkit_netdev_ops;
dev->ethtool_ops = &netkit_ethtool_ops;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index a54a37fab8d9..8ece36815ff9 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1791,6 +1791,7 @@ enum netdev_stat_type {
enum netmem_tx_mode {
NETMEM_TX_NONE, /* no netmem TX support */
NETMEM_TX_DMA, /* DMA-capable netmem TX (real HW) */
+ NETMEM_TX_NO_DMA, /* no DMA, e.g. passthrough for virtual devs */
};
enum netdev_reg_state {
@@ -2137,7 +2138,7 @@ struct net_device {
struct_group(priv_flags_fast,
unsigned long priv_flags:32;
unsigned long lltx:1;
- unsigned long netmem_tx:1;
+ unsigned long netmem_tx:2;
);
const struct net_device_ops *netdev_ops;
const struct header_ops *header_ops;
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v4 3/8] net: devmem: support TX over NETMEM_TX_NO_DMA devices
From: Bobby Eshleman @ 2026-05-12 1:17 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan
Cc: dw, sdf.kernel, mohsin.bashr, willemb, jiang.kun2, xu.xin16,
wang.yaxin, netdev, linux-doc, linux-kernel, linux-rdma, bpf,
linux-kselftest, Stanislav Fomichev, Mina Almasry, netdev,
linux-doc, linux-kernel, linux-rdma, bpf, linux-kselftest,
Bobby Eshleman
In-Reply-To: <20260511-tcp-dm-netkit-v4-0-841b78b99d74@meta.com>
From: Bobby Eshleman <bobbyeshleman@meta.com>
When a netkit virtual device leases queues from a physical NIC, devmem
TX bindings created on the netkit device must still result in the dmabuf
being mapped for dma by the physical device. This patch accomplishes
this by teaching the bind handler to search for the underlying
DMA-capable device by looking it up via leased rx queues. The function
netdev_find_netmem_tx_dev(), used for finding the underlying DMA-capable
device, can be extended to support other non-netkit NETMEM_TX_NO_DMA
devices in the future if needed.
Additionally, this patch extends validate_xmit_unreadable_skb() to
support the netkit case, where the skb is validated twice: once on the
netkit guest device and again on the physical NIC after BPF redirect or
ip forwarding.
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
Changes in v4:
- Fold the `NETMEM_TX_NO_DMA` check in `validate_xmit_unreadable_skb()`
(Stan, Jakub)
- Convert `binding->vdev` to void* opaque cookie with comment (Jakub)
Changes in v3:
- Fix validate_xmit_unreadable_skb() bug for non-devmem
unreadable niovs (should not be dropped)
- Major simplification of validate_xmit_unreadable_skb()
- Fix prematurely released lock in bind-tx handler (Jakub)
Changes in v2:
- In validate_xmit_unreadable_skb() to check netmem_tx mode before
inspecting frags (Jakub)
- Lock bind_dev around netdev_queue_get_dma_dev() when bind_dev !=
netdev to fix lockdep (Sashiko)
---
net/core/dev.c | 3 ++-
net/core/devmem.c | 6 +++--
net/core/devmem.h | 10 ++++++--
net/core/netdev-genl.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++----
4 files changed, 72 insertions(+), 10 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index fbe4c328a367..e1dc3ec88fe2 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3993,7 +3993,8 @@ static struct sk_buff *validate_xmit_unreadable_skb(struct sk_buff *skb,
struct skb_shared_info *shinfo;
struct net_iov *niov;
- if (likely(skb_frags_readable(skb)))
+ if (likely(skb_frags_readable(skb) ||
+ dev->netmem_tx == NETMEM_TX_NO_DMA))
goto out;
if (dev->netmem_tx == NETMEM_TX_NONE)
diff --git a/net/core/devmem.c b/net/core/devmem.c
index cde4c89bc146..1c67e7524246 100644
--- a/net/core/devmem.c
+++ b/net/core/devmem.c
@@ -181,7 +181,7 @@ int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx,
}
struct net_devmem_dmabuf_binding *
-net_devmem_bind_dmabuf(struct net_device *dev,
+net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
struct device *dma_dev,
enum dma_data_direction direction,
unsigned int dmabuf_fd, struct netdev_nl_sock *priv,
@@ -212,6 +212,7 @@ net_devmem_bind_dmabuf(struct net_device *dev,
}
binding->dev = dev;
+ binding->vdev = vdev;
xa_init_flags(&binding->bound_rxqs, XA_FLAGS_ALLOC);
err = percpu_ref_init(&binding->ref,
@@ -397,7 +398,8 @@ struct net_devmem_dmabuf_binding *net_devmem_get_binding(struct sock *sk,
*/
dst_dev = dst_dev_rcu(dst);
if (unlikely(!dst_dev) ||
- unlikely(dst_dev != READ_ONCE(binding->dev))) {
+ unlikely(dst_dev != READ_ONCE(binding->dev) &&
+ dst_dev != READ_ONCE(binding->vdev))) {
err = -ENODEV;
goto out_unlock;
}
diff --git a/net/core/devmem.h b/net/core/devmem.h
index 1c5c18581fcb..3852a56036cb 100644
--- a/net/core/devmem.h
+++ b/net/core/devmem.h
@@ -19,7 +19,13 @@ struct net_devmem_dmabuf_binding {
struct dma_buf *dmabuf;
struct dma_buf_attachment *attachment;
struct sg_table *sgt;
+ /* Physical NIC that does the actual DMA for this binding. */
struct net_device *dev;
+ /* Opaque cookie identifying the virtual device (e.g. netkit) the user
+ * called bind-tx on. Used only for pointer comparison. Never
+ * dereferenced.
+ */
+ void *vdev;
struct gen_pool *chunk_pool;
/* Protect dev */
struct mutex lock;
@@ -84,7 +90,7 @@ struct dmabuf_genpool_chunk_owner {
void __net_devmem_dmabuf_binding_free(struct work_struct *wq);
struct net_devmem_dmabuf_binding *
-net_devmem_bind_dmabuf(struct net_device *dev,
+net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
struct device *dma_dev,
enum dma_data_direction direction,
unsigned int dmabuf_fd, struct netdev_nl_sock *priv,
@@ -165,7 +171,7 @@ static inline void net_devmem_put_net_iov(struct net_iov *niov)
}
static inline struct net_devmem_dmabuf_binding *
-net_devmem_bind_dmabuf(struct net_device *dev,
+net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
struct device *dma_dev,
enum dma_data_direction direction,
unsigned int dmabuf_fd,
diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c
index 4d2c49371cdb..b4d48f3672a5 100644
--- a/net/core/netdev-genl.c
+++ b/net/core/netdev-genl.c
@@ -1077,7 +1077,7 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info)
goto err_rxq_bitmap;
}
- binding = net_devmem_bind_dmabuf(netdev, dma_dev, DMA_FROM_DEVICE,
+ binding = net_devmem_bind_dmabuf(netdev, NULL, dma_dev, DMA_FROM_DEVICE,
dmabuf_fd, priv, info->extack);
if (IS_ERR(binding)) {
err = PTR_ERR(binding);
@@ -1119,9 +1119,43 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info)
return err;
}
+/* Find the DMA-capable device for a netmem TX binding.
+ *
+ * For NETMEM_TX_DMA devices, return the device itself.
+ * For NETMEM_TX_NO_DMA devices, walk leased RX queues to find the underlying
+ * physical device and return it.
+ */
+static struct net_device *
+netdev_find_netmem_tx_dev(struct net_device *dev)
+{
+ struct netdev_rx_queue *lease_rxq;
+ struct net_device *phys_dev;
+ int i;
+
+ if (dev->netmem_tx == NETMEM_TX_DMA)
+ return dev;
+
+ if (dev->netmem_tx != NETMEM_TX_NO_DMA)
+ return NULL;
+
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ lease_rxq = READ_ONCE(__netif_get_rx_queue(dev, i)->lease);
+ if (!lease_rxq)
+ continue;
+
+ phys_dev = lease_rxq->dev;
+ if (netif_device_present(phys_dev) &&
+ phys_dev->netmem_tx == NETMEM_TX_DMA)
+ return phys_dev;
+ }
+
+ return NULL;
+}
+
int netdev_nl_bind_tx_doit(struct sk_buff *skb, struct genl_info *info)
{
struct net_devmem_dmabuf_binding *binding;
+ struct net_device *bind_dev;
struct netdev_nl_sock *priv;
struct net_device *netdev;
struct device *dma_dev;
@@ -1171,22 +1205,41 @@ int netdev_nl_bind_tx_doit(struct sk_buff *skb, struct genl_info *info)
goto err_unlock_netdev;
}
- dma_dev = netdev_queue_get_dma_dev(netdev, 0, NETDEV_QUEUE_TYPE_TX);
- binding = net_devmem_bind_dmabuf(netdev, dma_dev, DMA_TO_DEVICE,
- dmabuf_fd, priv, info->extack);
+ bind_dev = netdev_find_netmem_tx_dev(netdev);
+ if (!bind_dev) {
+ err = -EOPNOTSUPP;
+ NL_SET_ERR_MSG(info->extack,
+ "No DMA-capable device found for netmem TX");
+ goto err_unlock_netdev;
+ }
+
+ if (bind_dev != netdev)
+ netdev_lock(bind_dev);
+
+ dma_dev = netdev_queue_get_dma_dev(bind_dev, 0, NETDEV_QUEUE_TYPE_TX);
+
+ binding = net_devmem_bind_dmabuf(bind_dev,
+ bind_dev != netdev ? netdev : NULL,
+ dma_dev, DMA_TO_DEVICE, dmabuf_fd,
+ priv, info->extack);
if (IS_ERR(binding)) {
err = PTR_ERR(binding);
- goto err_unlock_netdev;
+ goto err_unlock_bind_dev;
}
nla_put_u32(rsp, NETDEV_A_DMABUF_ID, binding->id);
genlmsg_end(rsp, hdr);
+ if (bind_dev != netdev)
+ netdev_unlock(bind_dev);
netdev_unlock(netdev);
mutex_unlock(&priv->lock);
return genlmsg_reply(rsp, info);
+err_unlock_bind_dev:
+ if (bind_dev != netdev)
+ netdev_unlock(bind_dev);
err_unlock_netdev:
netdev_unlock(netdev);
err_unlock_sock:
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v4 5/8] selftests: drv-net: make attr _nk_guest_ifname public
From: Bobby Eshleman @ 2026-05-12 1:17 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan
Cc: dw, sdf.kernel, mohsin.bashr, willemb, jiang.kun2, xu.xin16,
wang.yaxin, netdev, linux-doc, linux-kernel, linux-rdma, bpf,
linux-kselftest, Stanislav Fomichev, Mina Almasry, netdev,
linux-doc, linux-kernel, linux-rdma, bpf, linux-kselftest,
Bobby Eshleman
In-Reply-To: <20260511-tcp-dm-netkit-v4-0-841b78b99d74@meta.com>
From: Bobby Eshleman <bobbyeshleman@meta.com>
Subsequent patches will use the _nk_guest_ifname as a public attr for
setting up devmem. Rename to nk_guest_ifname to avoid angering the
linter about the '_' prefix being used for a non-private attr.
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
tools/testing/selftests/drivers/net/hw/nk_qlease.py | 8 ++++----
tools/testing/selftests/drivers/net/lib/py/env.py | 16 ++++++++--------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/hw/nk_qlease.py b/tools/testing/selftests/drivers/net/hw/nk_qlease.py
index aa83dc321328..139a91ebd229 100755
--- a/tools/testing/selftests/drivers/net/hw/nk_qlease.py
+++ b/tools/testing/selftests/drivers/net/hw/nk_qlease.py
@@ -71,7 +71,7 @@ def test_iou_zcrx(cfg) -> None:
flow_rule_id = set_flow_rule(cfg)
defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}")
- rx_cmd = f"ip netns exec {cfg.netns.name} {cfg.bin_local} -s -p {cfg.port} -i {cfg._nk_guest_ifname} -q {cfg.nk_queue}"
+ rx_cmd = f"ip netns exec {cfg.netns.name} {cfg.bin_local} -s -p {cfg.port} -i {cfg.nk_guest_ifname} -q {cfg.nk_queue}"
tx_cmd = f"{cfg.bin_remote} -c -h {cfg.nk_guest_ipv6} -p {cfg.port} -l 12840"
with bkg(rx_cmd, exit_wait=True):
wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns)
@@ -128,7 +128,7 @@ def test_attach_xdp_with_mp(cfg) -> None:
netdevnl = NetdevFamily()
- rx_cmd = f"ip netns exec {cfg.netns.name} {cfg.bin_local} -s -p {cfg.port} -i {cfg._nk_guest_ifname} -q {cfg.nk_queue}"
+ rx_cmd = f"ip netns exec {cfg.netns.name} {cfg.bin_local} -s -p {cfg.port} -i {cfg.nk_guest_ifname} -q {cfg.nk_queue}"
with bkg(rx_cmd):
wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns)
@@ -178,7 +178,7 @@ def test_destroy(cfg) -> None:
ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}")
defer(ethtool, f"-X {cfg.ifname} default")
- rx_cmd = f"ip netns exec {cfg.netns.name} {cfg.bin_local} -s -p {cfg.port} -i {cfg._nk_guest_ifname} -q {cfg.nk_queue}"
+ rx_cmd = f"ip netns exec {cfg.netns.name} {cfg.bin_local} -s -p {cfg.port} -i {cfg.nk_guest_ifname} -q {cfg.nk_queue}"
rx_proc = cmd(rx_cmd, background=True)
wait_port_listen(cfg.port, proto="tcp", ns=cfg.netns)
@@ -196,7 +196,7 @@ def test_destroy(cfg) -> None:
ip(f"link del dev {cfg._nk_host_ifname}")
kill_timer.join()
cfg._nk_host_ifname = None
- cfg._nk_guest_ifname = None
+ cfg.nk_guest_ifname = None
queue_info = netdevnl.queue_get(
{"ifindex": cfg.ifindex, "id": cfg.src_queue, "type": "rx"}
diff --git a/tools/testing/selftests/drivers/net/lib/py/env.py b/tools/testing/selftests/drivers/net/lib/py/env.py
index 24ce122abd9c..409b41922245 100644
--- a/tools/testing/selftests/drivers/net/lib/py/env.py
+++ b/tools/testing/selftests/drivers/net/lib/py/env.py
@@ -339,7 +339,7 @@ class NetDrvContEnv(NetDrvEpEnv):
def __init__(self, src_path, rxqueues=1, **kwargs):
self.netns = None
self._nk_host_ifname = None
- self._nk_guest_ifname = None
+ self.nk_guest_ifname = None
self._tc_clsact_added = False
self._tc_attached = False
self._bpf_prog_pref = None
@@ -390,7 +390,7 @@ class NetDrvContEnv(NetDrvEpEnv):
netkit_links.sort(key=lambda x: x['ifindex'])
self._nk_host_ifname = netkit_links[1]['ifname']
- self._nk_guest_ifname = netkit_links[0]['ifname']
+ self.nk_guest_ifname = netkit_links[0]['ifname']
self.nk_host_ifindex = netkit_links[1]['ifindex']
self.nk_guest_ifindex = netkit_links[0]['ifindex']
@@ -409,7 +409,7 @@ class NetDrvContEnv(NetDrvEpEnv):
if self._nk_host_ifname:
cmd(f"ip link del dev {self._nk_host_ifname}")
self._nk_host_ifname = None
- self._nk_guest_ifname = None
+ self.nk_guest_ifname = None
if self._init_ns_attached:
cmd("ip netns del init", fail=False)
@@ -448,16 +448,16 @@ class NetDrvContEnv(NetDrvEpEnv):
cmd("ip netns attach init 1")
self._init_ns_attached = True
ip("netns set init 0", ns=self.netns)
- ip(f"link set dev {self._nk_guest_ifname} netns {self.netns.name}")
+ ip(f"link set dev {self.nk_guest_ifname} netns {self.netns.name}")
ip(f"link set dev {self._nk_host_ifname} up")
ip(f"-6 addr add fe80::1/64 dev {self._nk_host_ifname} nodad")
ip(f"-6 route add {self.nk_guest_ipv6}/128 via fe80::2 dev {self._nk_host_ifname}")
ip("link set lo up", ns=self.netns)
- ip(f"link set dev {self._nk_guest_ifname} up", ns=self.netns)
- ip(f"-6 addr add fe80::2/64 dev {self._nk_guest_ifname}", ns=self.netns)
- ip(f"-6 addr add {self.nk_guest_ipv6}/64 dev {self._nk_guest_ifname} nodad", ns=self.netns)
- ip(f"-6 route add default via fe80::1 dev {self._nk_guest_ifname}", ns=self.netns)
+ ip(f"link set dev {self.nk_guest_ifname} up", ns=self.netns)
+ ip(f"-6 addr add fe80::2/64 dev {self.nk_guest_ifname}", ns=self.netns)
+ ip(f"-6 addr add {self.nk_guest_ipv6}/64 dev {self.nk_guest_ifname} nodad", ns=self.netns)
+ ip(f"-6 route add default via fe80::1 dev {self.nk_guest_ifname}", ns=self.netns)
def _tc_ensure_clsact(self):
qdisc = json.loads(cmd(f"tc -j qdisc show dev {self.ifname}").stdout)
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v4 4/8] selftests: drv-net: ncdevmem: add -n flag to skip NIC configuration
From: Bobby Eshleman @ 2026-05-12 1:17 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan
Cc: dw, sdf.kernel, mohsin.bashr, willemb, jiang.kun2, xu.xin16,
wang.yaxin, netdev, linux-doc, linux-kernel, linux-rdma, bpf,
linux-kselftest, Stanislav Fomichev, Mina Almasry, netdev,
linux-doc, linux-kernel, linux-rdma, bpf, linux-kselftest,
Bobby Eshleman
In-Reply-To: <20260511-tcp-dm-netkit-v4-0-841b78b99d74@meta.com>
From: Bobby Eshleman <bobbyeshleman@meta.com>
Add a -n (skip_config) flag that causes ncdevmem to skip NIC
configuration when operating as an RX server. When -n is passed,
ncdevmem skips configuring header split, RSS, and flow steering, as well
as their teardown on exit.
This allows ksft tests to pre-configure the NIC in the host namespace
before launching ncdevmem in the guest namespace. This is needed for
netkit devmem tests where the test harness namespace has direct access
to the NIC and the ncdevmem namespace does not.
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
tools/testing/selftests/drivers/net/hw/ncdevmem.c | 58 +++++++++++++----------
1 file changed, 34 insertions(+), 24 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/hw/ncdevmem.c b/tools/testing/selftests/drivers/net/hw/ncdevmem.c
index e098d6534c3c..d96e8a3b5a65 100644
--- a/tools/testing/selftests/drivers/net/hw/ncdevmem.c
+++ b/tools/testing/selftests/drivers/net/hw/ncdevmem.c
@@ -93,6 +93,7 @@ static char *port;
static size_t do_validation;
static int start_queue = -1;
static int num_queues = -1;
+static int skip_config;
static char *ifname;
static unsigned int ifindex;
static unsigned int dmabuf_id;
@@ -828,7 +829,7 @@ static struct netdev_queue_id *create_queues(void)
static int do_server(struct memory_buffer *mem)
{
- struct ethtool_rings_get_rsp *ring_config;
+ struct ethtool_rings_get_rsp *ring_config = NULL;
char ctrl_data[sizeof(int) * 20000];
size_t non_page_aligned_frags = 0;
struct sockaddr_in6 client_addr;
@@ -851,27 +852,29 @@ static int do_server(struct memory_buffer *mem)
return -1;
}
- ring_config = get_ring_config();
- if (!ring_config) {
- pr_err("Failed to get current ring configuration");
- return -1;
- }
+ if (!skip_config) {
+ ring_config = get_ring_config();
+ if (!ring_config) {
+ pr_err("Failed to get current ring configuration");
+ return -1;
+ }
- if (configure_headersplit(ring_config, 1)) {
- pr_err("Failed to enable TCP header split");
- goto err_free_ring_config;
- }
+ if (configure_headersplit(ring_config, 1)) {
+ pr_err("Failed to enable TCP header split");
+ goto err_free_ring_config;
+ }
- /* Configure RSS to divert all traffic from our devmem queues */
- if (configure_rss()) {
- pr_err("Failed to configure rss");
- goto err_reset_headersplit;
- }
+ /* Configure RSS to divert all traffic from our devmem queues */
+ if (configure_rss()) {
+ pr_err("Failed to configure rss");
+ goto err_reset_headersplit;
+ }
- /* Flow steer our devmem flows to start_queue */
- if (configure_flow_steering(&server_sin)) {
- pr_err("Failed to configure flow steering");
- goto err_reset_rss;
+ /* Flow steer our devmem flows to start_queue */
+ if (configure_flow_steering(&server_sin)) {
+ pr_err("Failed to configure flow steering");
+ goto err_reset_rss;
+ }
}
if (bind_rx_queue(ifindex, mem->fd, create_queues(), num_queues, &ys)) {
@@ -1052,13 +1055,17 @@ static int do_server(struct memory_buffer *mem)
err_unbind:
ynl_sock_destroy(ys);
err_reset_flow_steering:
- reset_flow_steering();
+ if (!skip_config)
+ reset_flow_steering();
err_reset_rss:
- reset_rss();
+ if (!skip_config)
+ reset_rss();
err_reset_headersplit:
- restore_ring_config(ring_config);
+ if (!skip_config)
+ restore_ring_config(ring_config);
err_free_ring_config:
- ethtool_rings_get_rsp_free(ring_config);
+ if (!skip_config)
+ ethtool_rings_get_rsp_free(ring_config);
return err;
}
@@ -1404,7 +1411,7 @@ int main(int argc, char *argv[])
int is_server = 0, opt;
int ret, err = 1;
- while ((opt = getopt(argc, argv, "Lls:c:p:v:q:t:f:z:")) != -1) {
+ while ((opt = getopt(argc, argv, "Lls:c:p:v:q:t:f:z:n")) != -1) {
switch (opt) {
case 'L':
fail_on_linear = true;
@@ -1436,6 +1443,9 @@ int main(int argc, char *argv[])
case 'z':
max_chunk = atoi(optarg);
break;
+ case 'n':
+ skip_config = 1;
+ break;
case '?':
fprintf(stderr, "unknown option: %c\n", optopt);
break;
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v4 6/8] selftests: drv-net: refactor devmem command builders into lib module
From: Bobby Eshleman @ 2026-05-12 1:18 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan
Cc: dw, sdf.kernel, mohsin.bashr, willemb, jiang.kun2, xu.xin16,
wang.yaxin, netdev, linux-doc, linux-kernel, linux-rdma, bpf,
linux-kselftest, Stanislav Fomichev, Mina Almasry, netdev,
linux-doc, linux-kernel, linux-rdma, bpf, linux-kselftest,
Bobby Eshleman
In-Reply-To: <20260511-tcp-dm-netkit-v4-0-841b78b99d74@meta.com>
From: Bobby Eshleman <bobbyeshleman@meta.com>
Adding netkit-based devmem tests is a straight-forward copy of devmem
test commands plus some args for the nk cases, so this patch breaks out
these command builders into helpers used by both.
Though we tried to avoid libraries to avoid increasing the barrier of
entry/complexity (see selftests/drivers/net/README.md, section "Avoid
libraries and frameworks"), factoring out these functions seemed like
the lesser of two evils in this case of using the same commands, just
with slightly different args per environment.
I experimented with just having all of the tests in the same file to
avoid having helpers in a library file, but because ksft_run() is
limited to a single call per file, and the new tests will require
different environments (NetDrvContEnv/NetDrvEpEnv), it would have been
necessary to have each test set up its own environment instead of
sharing one for the entire ksft_run() run. This came at the cost of
ballooning the test time (from under 5s to 30s on my test system), so to
strike a balance these tests were placed in separate files so they could
keep a shared environment across a single ksft_run() run shared across
all tests using the same env type (introduced in subsequent patches).
The helpers work transparently with both plain and netkit environments
by inspecting cfg for netkit-specific attributes (netns, nk_queue,
etc...).
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
Changes in v4:
- Fixed bad change list version, v4 -> v3 (Stan)
Changes in v3:
- Make socat_send() always bind the source; drop its bind= parameter
and the matching bind=not_ns at the run_rx call site.
- Drop socat_send()'s nodelay= arg; have buf_size>0 imply TCP_NODELAY
since they are only meaningful together.
- configure_nic(): stash originals on cfg instead of using defer(); add
paired cleanup_nic() helper. Drop the per-test configure_nic() calls
from run_rx/run_tx/run_tx_chunks/run_rx_hds; the netkit test file
invokes configure_nic/cleanup_nic once around ksft_run().
- make cfg.devmem_supported and cfg.devmem_probed public attrs (no '_')
for sake of linting
- general cleanup of the code, linting fixes
- In setup_test, drop the unused cfg.listen_ns = getattr(cfg, 'netns',
None) assignment.
- In run_rx, pass flow_steer=not_ns to ncdevmem_rx and bind=not_ns to
socat_send to avoid changing functionality (we want just a straight
refactor here)
Changes in v2:
- Move require_devmem() into individual test functions so KsftSkipEx goes up to
ksft_run() (Sashiko)
- in ncdevmem_rx(), move -v 7 to take effect for both netns and
non-netns when verify=True
---
tools/testing/selftests/drivers/net/hw/devmem.py | 77 ++------
.../selftests/drivers/net/hw/lib/py/devmem.py | 218 +++++++++++++++++++++
2 files changed, 231 insertions(+), 64 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/hw/devmem.py b/tools/testing/selftests/drivers/net/hw/devmem.py
index ee863e90d1e0..dbc1e6a27b6a 100755
--- a/tools/testing/selftests/drivers/net/hw/devmem.py
+++ b/tools/testing/selftests/drivers/net/hw/devmem.py
@@ -2,91 +2,40 @@
# SPDX-License-Identifier: GPL-2.0
from os import path
-from lib.py import ksft_run, ksft_exit
-from lib.py import ksft_eq, KsftSkipEx
+from lib.py import ksft_run, ksft_exit, ksft_disruptive
from lib.py import NetDrvEpEnv
-from lib.py import bkg, cmd, rand_port, wait_port_listen
-from lib.py import ksft_disruptive
-
-
-def require_devmem(cfg):
- if not hasattr(cfg, "_devmem_probed"):
- probe_command = f"{cfg.bin_local} -f {cfg.ifname}"
- cfg._devmem_supported = cmd(probe_command, fail=False, shell=True).ret == 0
- cfg._devmem_probed = True
-
- if not cfg._devmem_supported:
- raise KsftSkipEx("Test requires devmem support")
+from lib.py.devmem import setup_test, run_rx, run_tx, run_tx_chunks, run_rx_hds
@ksft_disruptive
def check_rx(cfg) -> None:
- require_devmem(cfg)
-
- port = rand_port()
- socat = f"socat -u - TCP{cfg.addr_ipver}:{cfg.baddr}:{port},bind={cfg.remote_baddr}:{port}"
- listen_cmd = f"{cfg.bin_local} -l -f {cfg.ifname} -s {cfg.addr} -p {port} -c {cfg.remote_addr} -v 7"
-
- with bkg(listen_cmd, exit_wait=True) as ncdevmem:
- wait_port_listen(port)
- cmd(f"yes $(echo -e \x01\x02\x03\x04\x05\x06) | \
- head -c 1K | {socat}", host=cfg.remote, shell=True)
-
- ksft_eq(ncdevmem.ret, 0)
+ """Run the devmem RX test."""
+ run_rx(cfg)
@ksft_disruptive
def check_tx(cfg) -> None:
- require_devmem(cfg)
-
- port = rand_port()
- listen_cmd = f"socat -U - TCP{cfg.addr_ipver}-LISTEN:{port}"
-
- with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as socat:
- wait_port_listen(port, host=cfg.remote)
- cmd(f"echo -e \"hello\\nworld\"| {cfg.bin_local} -f {cfg.ifname} -s {cfg.remote_addr} -p {port}", shell=True)
-
- ksft_eq(socat.stdout.strip(), "hello\nworld")
+ """Run the devmem TX test."""
+ run_tx(cfg)
@ksft_disruptive
def check_tx_chunks(cfg) -> None:
- require_devmem(cfg)
-
- port = rand_port()
- listen_cmd = f"socat -U - TCP{cfg.addr_ipver}-LISTEN:{port}"
-
- with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as socat:
- wait_port_listen(port, host=cfg.remote)
- cmd(f"echo -e \"hello\\nworld\"| {cfg.bin_local} -f {cfg.ifname} -s {cfg.remote_addr} -p {port} -z 3", shell=True)
-
- ksft_eq(socat.stdout.strip(), "hello\nworld")
+ """Run the devmem TX chunking test."""
+ run_tx_chunks(cfg)
def check_rx_hds(cfg) -> None:
- """Test HDS splitting across payload sizes."""
- require_devmem(cfg)
-
- for size in [1, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192]:
- port = rand_port()
- listen_cmd = f"{cfg.bin_local} -L -l -f {cfg.ifname} -s {cfg.addr} -p {port}"
-
- with bkg(listen_cmd, exit_wait=True) as ncdevmem:
- wait_port_listen(port)
- cmd(f"dd if=/dev/zero bs={size} count=1 2>/dev/null | " +
- f"socat -b {size} -u - TCP{cfg.addr_ipver}:{cfg.baddr}:{port},nodelay",
- host=cfg.remote, shell=True)
-
- ksft_eq(ncdevmem.ret, 0, f"HDS failed for payload size {size}")
+ """Run the HDS test."""
+ run_rx_hds(cfg)
def main() -> None:
+ """Run the devmem test cases."""
with NetDrvEpEnv(__file__) as cfg:
- cfg.bin_local = path.abspath(path.dirname(__file__) + "/ncdevmem")
- cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)
-
+ setup_test(cfg, path.abspath(path.dirname(__file__) + "/ncdevmem"))
ksft_run([check_rx, check_tx, check_tx_chunks, check_rx_hds],
- args=(cfg, ))
+ args=(cfg,))
ksft_exit()
diff --git a/tools/testing/selftests/drivers/net/hw/lib/py/devmem.py b/tools/testing/selftests/drivers/net/hw/lib/py/devmem.py
new file mode 100644
index 000000000000..d3e7a3645cba
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/lib/py/devmem.py
@@ -0,0 +1,218 @@
+# SPDX-License-Identifier: GPL-2.0
+"""Shared helpers for devmem TCP selftests."""
+
+import re
+
+from net.lib.py import (bkg, cmd, defer, ethtool, rand_port, wait_port_listen,
+ ksft_eq, KsftSkipEx, NetNSEnter, EthtoolFamily,
+ NetdevFamily)
+
+
+def require_devmem(cfg):
+ """Probe ncdevmem on cfg.ifname and SKIP the test if devmem isn't supported."""
+ if not hasattr(cfg, "devmem_probed"):
+ probe_command = f"{cfg.bin_local} -f {cfg.ifname}"
+ cfg.devmem_supported = cmd(probe_command, fail=False, shell=True).ret == 0
+ cfg.devmem_probed = True
+
+ if not cfg.devmem_supported:
+ raise KsftSkipEx("Test requires devmem support")
+
+
+def configure_nic(cfg):
+ """Channels, rings, RSS, queue lease for netkit devmem."""
+ cfg.require_ipver('6')
+ ethnl = EthtoolFamily()
+
+ channels = ethnl.channels_get({'header': {'dev-index': cfg.ifindex}})
+ channels = channels['combined-count']
+ if channels < 2:
+ raise KsftSkipEx(
+ 'Test requires NETIF with at least 2 combined channels'
+ )
+
+ rings = ethnl.rings_get({'header': {'dev-index': cfg.ifindex}})
+ cfg.orig_rx_rings = rings['rx']
+ cfg.orig_hds_thresh = rings.get('hds-thresh', 0)
+ cfg.orig_data_split = rings.get('tcp-data-split', 'unknown')
+
+ ethnl.rings_set({'header': {'dev-index': cfg.ifindex},
+ 'tcp-data-split': 'enabled',
+ 'hds-thresh': 0,
+ 'rx': min(64, cfg.orig_rx_rings)})
+
+ cfg.src_queue = channels - 1
+ ethtool(f"-X {cfg.ifname} equal {cfg.src_queue}")
+
+ with NetNSEnter(str(cfg.netns)):
+ netdevnl = NetdevFamily()
+ lease_result = netdevnl.queue_create({
+ "ifindex": cfg.nk_guest_ifindex,
+ "type": "rx",
+ "lease": {
+ "ifindex": cfg.ifindex,
+ "queue": {"id": cfg.src_queue, "type": "rx"},
+ "netns-id": 0,
+ },
+ })
+ cfg.nk_queue = lease_result['id']
+
+
+def cleanup_nic(cfg):
+ """Undo configure_nic() by restoring RSS and ring settings."""
+ ethtool(f"-X {cfg.ifname} default")
+ EthtoolFamily().rings_set({'header': {'dev-index': cfg.ifindex},
+ 'tcp-data-split': cfg.orig_data_split,
+ 'hds-thresh': cfg.orig_hds_thresh,
+ 'rx': cfg.orig_rx_rings})
+
+
+def set_flow_rule(cfg, port):
+ """Install a flow rule steering to src_queue and return the flow rule ID."""
+ output = ethtool(
+ f"-N {cfg.ifname} flow-type tcp6 dst-port {port}"
+ f" action {cfg.src_queue}"
+ ).stdout
+ return int(re.search(r'ID (\d+)', output).group(1))
+
+
+def ncdevmem_rx(cfg, port, verify=True, fail_on_linear=False, flow_steer=False):
+ """Build the ncdevmem RX listener command."""
+ if hasattr(cfg, 'netns'):
+ flow_rule_id = set_flow_rule(cfg, port)
+ defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}")
+
+ ifname = cfg.nk_guest_ifname
+ addr = cfg.nk_guest_ipv6
+ extras = [f"-t {cfg.nk_queue}", "-q 1", "-n"]
+ else:
+ ifname = cfg.ifname
+ addr = cfg.addr
+ extras = []
+ if flow_steer:
+ extras.append(f"-c {cfg.remote_addr}")
+
+ if verify:
+ extras.append("-v 7")
+ if fail_on_linear:
+ extras.append("-L")
+
+ parts = [cfg.bin_local, "-l", f"-f {ifname}", f"-s {addr}",
+ f"-p {port}", *extras]
+ return " ".join(parts)
+
+
+def ncdevmem_tx(cfg, port, chunk_size=0):
+ """Build the ncdevmem TX send command."""
+ if hasattr(cfg, 'netns'):
+ ifname = cfg.nk_guest_ifname
+ addr = cfg.remote_addr_v['6']
+ extras = ["-t 0", "-q 1", "-n"]
+ else:
+ ifname = cfg.ifname
+ addr = cfg.remote_addr
+ extras = []
+
+ if chunk_size:
+ extras.append(f"-z {chunk_size}")
+
+ parts = [cfg.bin_local, f"-f {ifname}", f"-s {addr}",
+ f"-p {port}", *extras]
+ return " ".join(parts)
+
+
+def socat_send(cfg, port, buf_size=0):
+ """Socat command for sending to the devmem listener.
+
+ When buf_size > 0, force one TCP segment per write of exactly that size by
+ setting socat's buffer (-b) and disabling Nagle (TCP_NODELAY).
+ """
+ proto = f"TCP{cfg.addr_ipver}"
+
+ if hasattr(cfg, 'netns'):
+ addr = f"[{cfg.nk_guest_ipv6}]"
+ else:
+ addr = cfg.baddr
+
+ suffix = f",bind={cfg.remote_baddr}:{port}"
+
+ buf = ""
+ if buf_size:
+ buf = f"-b {buf_size}"
+ suffix += ",nodelay"
+
+ return f"socat {buf} -u - {proto}:{addr}:{port}{suffix}"
+
+
+def socat_listen(cfg, port):
+ """Socat listen command for TX tests."""
+ return f"socat -U - TCP{cfg.addr_ipver}-LISTEN:{port}"
+
+
+def setup_test(cfg, bin_local):
+ """Stash the local ncdevmem path on cfg and deploy it to the remote."""
+ cfg.bin_local = bin_local
+ cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)
+
+
+def run_rx(cfg):
+ """Run the devmem RX test."""
+ require_devmem(cfg)
+ port = rand_port()
+ socat = socat_send(cfg, port)
+ data_pipe = (f"yes $(echo -e \x01\x02\x03\x04\x05\x06) | head -c 1K"
+ f" | {socat}")
+ netns = getattr(cfg, "netns", None)
+
+ listen_cmd = ncdevmem_rx(cfg, port, flow_steer=not hasattr(cfg, 'netns'))
+ with bkg(listen_cmd, exit_wait=True, ns=netns) as ncdevmem:
+ wait_port_listen(port, proto="tcp", ns=netns)
+ cmd(data_pipe, host=cfg.remote, shell=True)
+ ksft_eq(ncdevmem.ret, 0)
+
+
+def run_tx(cfg):
+ """Run the devmem TX test."""
+ require_devmem(cfg)
+ netns = getattr(cfg, "netns", None)
+ port = rand_port()
+ tx_cmd = ncdevmem_tx(cfg, port)
+ listen_cmd = socat_listen(cfg, port)
+
+ with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as socat:
+ wait_port_listen(port, host=cfg.remote)
+ cmd(f"bash -c 'echo -e \"hello\\nworld\" | {tx_cmd}'", ns=netns, shell=True)
+ ksft_eq(socat.stdout.strip(), "hello\nworld")
+
+
+def run_tx_chunks(cfg):
+ """Run the devmem TX chunking test."""
+ require_devmem(cfg)
+ netns = getattr(cfg, "netns", None)
+ port = rand_port()
+ tx_cmd = ncdevmem_tx(cfg, port, chunk_size=3)
+ listen_cmd = socat_listen(cfg, port)
+
+ with bkg(listen_cmd, host=cfg.remote, exit_wait=True) as socat:
+ wait_port_listen(port, host=cfg.remote)
+ cmd(f"bash -c 'echo -e \"hello\\nworld\" | {tx_cmd}'", ns=netns, shell=True)
+ ksft_eq(socat.stdout.strip(), "hello\nworld")
+
+
+def run_rx_hds(cfg):
+ """Run the HDS test by running devmem RX across a segment size sweep."""
+ require_devmem(cfg)
+ netns = getattr(cfg, "netns", None)
+
+ for size in [1, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192]:
+ port = rand_port()
+
+ listen_cmd = ncdevmem_rx(cfg, port, verify=False,
+ fail_on_linear=True)
+ socat = socat_send(cfg, port, buf_size=size)
+
+ with bkg(listen_cmd, exit_wait=True, ns=netns) as ncdevmem:
+ wait_port_listen(port, proto="tcp", ns=netns)
+ cmd(f"dd if=/dev/zero bs={size} count=1 2>/dev/null | "
+ f"{socat}", host=cfg.remote, shell=True)
+ ksft_eq(ncdevmem.ret, 0, f"HDS failed for payload size {size}")
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v4 7/8] selftests: drv-net: add primary_rx_redirect support to NetDrvContEnv
From: Bobby Eshleman @ 2026-05-12 1:18 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan
Cc: dw, sdf.kernel, mohsin.bashr, willemb, jiang.kun2, xu.xin16,
wang.yaxin, netdev, linux-doc, linux-kernel, linux-rdma, bpf,
linux-kselftest, Stanislav Fomichev, Mina Almasry, netdev,
linux-doc, linux-kernel, linux-rdma, bpf, linux-kselftest,
Bobby Eshleman
In-Reply-To: <20260511-tcp-dm-netkit-v4-0-841b78b99d74@meta.com>
From: Bobby Eshleman <bobbyeshleman@meta.com>
When sending from a namespace that has access to a netkit device with a
leased queue, the nk primary in the host namespace needs to redirect its
RX to the physical device. This patch adds that redirection bpf program
and teaches the harness to install it.
Add primary_rx_redirect=False parameter to NetDrvContEnv.__init__().
When enabled, _attach_primary_rx_redirect_bpf() attaches a new BPF TC
program (nk_primary_rx_redirect.bpf.c) to the primary (host-side) netkit
interface. The program redirects non-ICMPv6 IPv6 packets to the physical
NIC via bpf_redirect_neigh(), with the physical ifindex configured via
the .bss map. ICMPv6 is left on the host's netkit primary so IPv6
neighbor discovery still work locally.
Extract _find_bss_map_id() from _attach_bpf() into a reusable helper so
other BPF attachment methods can use it.
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
Changes in v3:
- nk_primary_rx_redirect.bpf.c: add header includes to avoid hardcoding
values
- update commit message explaining why ICMP is passed through
- env.py: re-use _tc_ensure_clsact() (had to add ifname paramater)
- env.py: gate the remote IPv6 host route install on primary_rx_redirect
by moving it from _setup_ns() into _attach_primary_rx_redirect_bpf()
---
.../drivers/net/hw/nk_primary_rx_redirect.bpf.c | 39 +++++++++
tools/testing/selftests/drivers/net/lib/py/env.py | 93 +++++++++++++++++-----
2 files changed, 114 insertions(+), 18 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/hw/nk_primary_rx_redirect.bpf.c b/tools/testing/selftests/drivers/net/hw/nk_primary_rx_redirect.bpf.c
new file mode 100644
index 000000000000..46ff494b23de
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/nk_primary_rx_redirect.bpf.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <linux/pkt_cls.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/ipv6.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+
+#define ctx_ptr(field) ((void *)(long)(field))
+
+volatile __u32 phys_ifindex;
+
+SEC("tc/ingress")
+int nk_primary_rx_redirect(struct __sk_buff *skb)
+{
+ void *data_end = ctx_ptr(skb->data_end);
+ void *data = ctx_ptr(skb->data);
+ struct ethhdr *eth;
+ struct ipv6hdr *ip6h;
+
+ eth = data;
+ if ((void *)(eth + 1) > data_end)
+ return TC_ACT_OK;
+
+ if (eth->h_proto != bpf_htons(ETH_P_IPV6))
+ return TC_ACT_OK;
+
+ ip6h = data + sizeof(struct ethhdr);
+ if ((void *)(ip6h + 1) > data_end)
+ return TC_ACT_OK;
+
+ if (ip6h->nexthdr == IPPROTO_ICMPV6)
+ return TC_ACT_OK;
+
+ return bpf_redirect_neigh(phys_ifindex, NULL, 0, 0);
+}
+
+char __license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/drivers/net/lib/py/env.py b/tools/testing/selftests/drivers/net/lib/py/env.py
index 409b41922245..af8e1de8ed7b 100644
--- a/tools/testing/selftests/drivers/net/lib/py/env.py
+++ b/tools/testing/selftests/drivers/net/lib/py/env.py
@@ -336,15 +336,18 @@ class NetDrvContEnv(NetDrvEpEnv):
+---------------+
"""
- def __init__(self, src_path, rxqueues=1, **kwargs):
+ def __init__(self, src_path, rxqueues=1, primary_rx_redirect=False, **kwargs):
self.netns = None
self._nk_host_ifname = None
self.nk_guest_ifname = None
self._tc_clsact_added = False
self._tc_attached = False
+ self._primary_rx_redirect_attached = False
+ self._primary_rx_redirect_clsact_added = False
self._bpf_prog_pref = None
self._bpf_prog_id = None
self._init_ns_attached = False
+ self._remote_route_added = False
self._old_fwd = None
self._old_accept_ra = None
@@ -396,8 +399,18 @@ class NetDrvContEnv(NetDrvEpEnv):
self._setup_ns()
self._attach_bpf()
+ if primary_rx_redirect:
+ self._attach_primary_rx_redirect_bpf()
def __del__(self):
+ if self._primary_rx_redirect_attached:
+ cmd(f"tc filter del dev {self._nk_host_ifname} ingress", fail=False)
+ self._primary_rx_redirect_attached = False
+
+ if self._primary_rx_redirect_clsact_added:
+ cmd(f"tc qdisc del dev {self._nk_host_ifname} clsact", fail=False)
+ self._primary_rx_redirect_clsact_added = False
+
if self._tc_attached:
cmd(f"tc filter del dev {self.ifname} ingress pref {self._bpf_prog_pref}")
self._tc_attached = False
@@ -406,6 +419,11 @@ class NetDrvContEnv(NetDrvEpEnv):
cmd(f"tc qdisc del dev {self.ifname} clsact")
self._tc_clsact_added = False
+ if self._remote_route_added:
+ cmd(f"ip -6 route del {self.nk_guest_ipv6}/128",
+ host=self.remote, fail=False)
+ self._remote_route_added = False
+
if self._nk_host_ifname:
cmd(f"ip link del dev {self._nk_host_ifname}")
self._nk_host_ifname = None
@@ -459,13 +477,19 @@ class NetDrvContEnv(NetDrvEpEnv):
ip(f"-6 addr add {self.nk_guest_ipv6}/64 dev {self.nk_guest_ifname} nodad", ns=self.netns)
ip(f"-6 route add default via fe80::1 dev {self.nk_guest_ifname}", ns=self.netns)
- def _tc_ensure_clsact(self):
- qdisc = json.loads(cmd(f"tc -j qdisc show dev {self.ifname}").stdout)
+ def _tc_ensure_clsact(self, ifname=None):
+ """Ensure a clsact qdisc exists on @ifname.
+
+ Returns True if this call added the qdisc, otherwise returns False.
+ """
+ if ifname is None:
+ ifname = self.ifname
+ qdisc = json.loads(cmd(f"tc -j qdisc show dev {ifname}").stdout)
for q in qdisc:
if q['kind'] == 'clsact':
- return
- cmd(f"tc qdisc add dev {self.ifname} clsact")
- self._tc_clsact_added = True
+ return False
+ cmd(f"tc qdisc add dev {ifname} clsact")
+ return True
def _get_bpf_prog_ids(self):
filters = json.loads(cmd(f"tc -j filter show dev {self.ifname} ingress").stdout)
@@ -476,28 +500,28 @@ class NetDrvContEnv(NetDrvEpEnv):
return (bpf['pref'], bpf['options']['prog']['id'])
raise Exception("Failed to get BPF prog ID")
+ def _find_bss_map_id(self, prog_id):
+ """Find the .bss map ID for a loaded BPF program."""
+ prog_info = bpftool(f"prog show id {prog_id}", json=True)
+ for map_id in prog_info.get("map_ids", []):
+ map_info = bpftool(f"map show id {map_id}", json=True)
+ if map_info.get("name", "").endswith("bss"):
+ return map_id
+ raise Exception(f"Failed to find .bss map for prog {prog_id}")
+
def _attach_bpf(self):
bpf_obj = self.test_dir / "nk_forward.bpf.o"
if not bpf_obj.exists():
raise KsftSkipEx("BPF prog not found")
- self._tc_ensure_clsact()
+ if self._tc_ensure_clsact():
+ self._tc_clsact_added = True
cmd(f"tc filter add dev {self.ifname} ingress bpf obj {bpf_obj}"
" sec tc/ingress direct-action")
self._tc_attached = True
(self._bpf_prog_pref, self._bpf_prog_id) = self._get_bpf_prog_ids()
- prog_info = bpftool(f"prog show id {self._bpf_prog_id}", json=True)
- map_ids = prog_info.get("map_ids", [])
-
- bss_map_id = None
- for map_id in map_ids:
- map_info = bpftool(f"map show id {map_id}", json=True)
- if map_info.get("name").endswith("bss"):
- bss_map_id = map_id
-
- if bss_map_id is None:
- raise Exception("Failed to find .bss map")
+ bss_map_id = self._find_bss_map_id(self._bpf_prog_id)
ipv6_addr = ipaddress.IPv6Address(self.ipv6_prefix)
ipv6_bytes = ipv6_addr.packed
@@ -505,3 +529,36 @@ class NetDrvContEnv(NetDrvEpEnv):
value = ipv6_bytes + ifindex_bytes
value_hex = ' '.join(f'{b:02x}' for b in value)
bpftool(f"map update id {bss_map_id} key hex 00 00 00 00 value hex {value_hex}")
+
+ def _attach_primary_rx_redirect_bpf(self):
+ """Attach BPF redirect program on the primary netkit ingress."""
+ bpf_obj = self.test_dir / "nk_primary_rx_redirect.bpf.o"
+ if not bpf_obj.exists():
+ raise KsftSkipEx("Primary RX redirect BPF prog not found")
+
+ if self._tc_ensure_clsact(self._nk_host_ifname):
+ self._primary_rx_redirect_clsact_added = True
+ cmd(f"tc filter add dev {self._nk_host_ifname} ingress"
+ f" bpf obj {bpf_obj} sec tc/ingress direct-action")
+ self._primary_rx_redirect_attached = True
+
+ ip(f"-6 route add {self.nk_guest_ipv6}/128 via {self.addr_v['6']}",
+ host=self.remote)
+ self._remote_route_added = True
+
+ filters = json.loads(
+ cmd(f"tc -j filter show dev {self._nk_host_ifname} ingress").stdout)
+ redirect_prog_id = None
+ for bpf in filters:
+ if 'options' not in bpf:
+ continue
+ if bpf['options']['bpf_name'].startswith('nk_primary_rx_redirect'):
+ redirect_prog_id = bpf['options']['prog']['id']
+ break
+ if redirect_prog_id is None:
+ raise Exception("Failed to get primary RX redirect BPF prog ID")
+
+ bss_map_id = self._find_bss_map_id(redirect_prog_id)
+ phys_ifindex_bytes = self.ifindex.to_bytes(4, byteorder='little')
+ value_hex = ' '.join(f'{b:02x}' for b in phys_ifindex_bytes)
+ bpftool(f"map update id {bss_map_id} key hex 00 00 00 00 value hex {value_hex}")
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH net-next v4 8/8] selftests: drv-net: add netkit devmem tests
From: Bobby Eshleman @ 2026-05-12 1:18 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan, Alex Shi,
Yanteng Si, Dongliang Mu, Michael Chan, Pavan Chebbi,
Joshua Washington, Harshitha Ramamurthy, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Leon Romanovsky, Alexander Duyck,
kernel-team, Daniel Borkmann, Nikolay Aleksandrov, Shuah Khan
Cc: dw, sdf.kernel, mohsin.bashr, willemb, jiang.kun2, xu.xin16,
wang.yaxin, netdev, linux-doc, linux-kernel, linux-rdma, bpf,
linux-kselftest, Stanislav Fomichev, Mina Almasry, netdev,
linux-doc, linux-kernel, linux-rdma, bpf, linux-kselftest,
Bobby Eshleman
In-Reply-To: <20260511-tcp-dm-netkit-v4-0-841b78b99d74@meta.com>
From: Bobby Eshleman <bobbyeshleman@meta.com>
Add nk_devmem.py with four tests for TCP devmem through a netkit device:
These tests are just duplicates of the original devmem tests, with some
adjusted parameters such as telling ncdevmem to avoid device setup
(since it only has access to netkit, not a phys device).
Each test uses NetDrvContEnv with primary_rx_redirect=True to set up the
BPF redirect program on the primary netkit interface.
The NIC (HDS, RSS, queue lease) is configured once in main() before
ksft_run() and torn down in a finally block via cleanup_nic(), mirroring
the nk_qlease.py pattern. This avoids re-toggling NIC settings around
every test case.
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
Changes in v4:
- Call configure_nic()/cleanup_nic() once around ksft_run() rather than
relying on per-test configuration inside the run_* helpers.
Changes in v3:
- Reorder os.path expressions
- Drop @ksft_disruptive from check_nk_rx_hds to mirror the original
check_rx_hds in devmem.py
Changes in v2:
- Add nk_devmem.py to TEST_PROGS in Makefile (Sashiko)
---
tools/testing/selftests/drivers/net/hw/Makefile | 1 +
.../testing/selftests/drivers/net/hw/nk_devmem.py | 55 ++++++++++++++++++++++
2 files changed, 56 insertions(+)
diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile
index 85ca4d1ecf9e..2f78c6aec397 100644
--- a/tools/testing/selftests/drivers/net/hw/Makefile
+++ b/tools/testing/selftests/drivers/net/hw/Makefile
@@ -34,6 +34,7 @@ TEST_PROGS = \
irq.py \
loopback.sh \
nic_timestamp.py \
+ nk_devmem.py \
nk_netns.py \
nk_qlease.py \
ntuple.py \
diff --git a/tools/testing/selftests/drivers/net/hw/nk_devmem.py b/tools/testing/selftests/drivers/net/hw/nk_devmem.py
new file mode 100755
index 000000000000..0e36a0fa9688
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/nk_devmem.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+"""Test devmem TCP with netkit."""
+
+import os
+from lib.py import ksft_run, ksft_exit, ksft_disruptive
+from lib.py import NetDrvContEnv
+from lib.py.devmem import (setup_test, require_devmem, configure_nic,
+ cleanup_nic, run_rx, run_tx, run_tx_chunks,
+ run_rx_hds)
+
+
+@ksft_disruptive
+def check_nk_rx(cfg) -> None:
+ """Run the devmem RX test through netkit."""
+ run_rx(cfg)
+
+
+@ksft_disruptive
+def check_nk_tx(cfg) -> None:
+ """Run the devmem TX test through netkit."""
+ run_tx(cfg)
+
+
+@ksft_disruptive
+def check_nk_tx_chunks(cfg) -> None:
+ """Run the devmem TX chunking test through netkit."""
+ run_tx_chunks(cfg)
+
+
+def check_nk_rx_hds(cfg) -> None:
+ """Run the HDS test through netkit."""
+ run_rx_hds(cfg)
+
+
+def main() -> None:
+ """Configure the NIC once, then run the netkit devmem test cases."""
+ with NetDrvContEnv(__file__, rxqueues=2, primary_rx_redirect=True) as cfg:
+ setup_test(cfg,
+ os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ "ncdevmem"))
+
+ require_devmem(cfg)
+ configure_nic(cfg)
+ try:
+ ksft_run([check_nk_rx, check_nk_tx, check_nk_tx_chunks,
+ check_nk_rx_hds], args=(cfg,))
+ finally:
+ cleanup_nic(cfg)
+
+ ksft_exit()
+
+
+if __name__ == "__main__":
+ main()
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v3 0/4] clk: update kernel docs
From: Brian Masney @ 2026-05-12 1:35 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Maxime Ripard, Jonathan Corbet,
Shuah Khan
Cc: linux-clk, linux-kernel, linux-doc, Brian Masney
Here's a small series that updates the kernel documentation for struct
clk_core, adds a doc block for the clk flags, and updates the clk kunit
test to use HZ_PER_MHZ.
Since I've been doing a lot of work in the clk subsystem lately, I would
like to add some more documentation about this subsystem in the future
as I have time. There's a lot of other things that I know can be done
now, and I'd like to just start with these small changes merged.
I tested this by running 'make htmldocs' and opened
Documentation/output/driver-api/clk.html in my browser to ensure that
it was rendered as expected.
Changes in v3:
- Remove clk flags enum and replaced with a doc block.
- Add separate header for the flags in the rst file.
- Rebased onto next-20260508.
- Link to v2: https://lore.kernel.org/r/20260325-clk-docs-v2-0-bcf660e1ceb5@redhat.com
Changes in v2:
- Add clk_core_flags enum, and kernel doc for this.
- Update descriptions in clk_core based on feedback from Stephen.
- Update clk kernel docs to show some of the new API documentation.
- Link to v1: https://lore.kernel.org/r/20260304-clk-docs-v1-0-fee468db99f1@redhat.com
Merge Strategy
--------------
Given the new doc block, and cross tree dependency, it would be easiest
of all of this was merged together in Stephen's tree.
Signed-off-by: Brian Masney <bmasney@redhat.com>
---
Brian Masney (4):
clk: add kernel docs for the core flags
clk: add kernel docs for struct clk_core
docs: clk: include some identifiers to keep documentation up to date
clk: test: convert constants to use HZ_PER_MHZ
Documentation/driver-api/clk.rst | 58 +++++++++-------------------------------
drivers/clk/clk.c | 51 +++++++++++++++++++++++++++++++++++
drivers/clk/clk_test.c | 7 ++---
include/linux/clk-provider.h | 46 +++++++++++++++++++------------
4 files changed, 96 insertions(+), 66 deletions(-)
---
base-commit: e98d21c170b01ddef366f023bbfcf6b31509fa83
change-id: 20260304-clk-docs-0b578f421600
Best regards,
--
Brian Masney <bmasney@redhat.com>
^ permalink raw reply
* [PATCH v3 1/4] clk: add kernel docs for the core flags
From: Brian Masney @ 2026-05-12 1:35 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Maxime Ripard, Jonathan Corbet,
Shuah Khan
Cc: linux-clk, linux-kernel, linux-doc, Brian Masney
In-Reply-To: <20260511-clk-docs-v3-0-ed67e1065809@redhat.com>
Let's add a DOC section for the clk core flags, and move the
documentation for each flag into the doc header so that it can
be easily referenced in the generated kernel documentation.
Note: The comment about "Please update clk_flags..." is included as a
separate comment so it doesn't show up in the generated documents.
Signed-off-by: Brian Masney <bmasney@redhat.com>
---
include/linux/clk-provider.h | 46 ++++++++++++++++++++++++++++----------------
1 file changed, 29 insertions(+), 17 deletions(-)
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index b01a38fef8cf..151ebdc20a29 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -9,28 +9,40 @@
#include <linux/of.h>
#include <linux/of_clk.h>
-/*
- * flags used across common struct clk. these flags should only affect the
- * top-level framework. custom flags for dealing with hardware specifics
- * belong in struct clk_foo
+/**
+ * DOC: clk framework flags
*
- * Please update clk_flags[] in drivers/clk/clk.c when making changes here!
+ * Flags used across common struct clk. These flags should only affect the
+ * top-level framework. Custom flags for dealing with hardware specifics
+ * belong in struct clk_foo.
+ *
+ * * CLK_SET_RATE_GATE - must be gated across rate change
+ * * CLK_SET_PARENT_GATE - must be gated across re-parent
+ * * CLK_SET_RATE_PARENT - propagate rate change up one level
+ * * CLK_IGNORE_UNUSED - do not gate even if unused
+ * * CLK_GET_RATE_NOCACHE - do not use the cached clk rate
+ * * CLK_SET_RATE_NO_REPARENT - don't re-parent on rate change
+ * * CLK_GET_ACCURACY_NOCACHE - do not use the cached clk accuracy
+ * * CLK_RECALC_NEW_RATES - recalc rates after notifications
+ * * CLK_SET_RATE_UNGATE - clock needs to run to set rate
+ * * CLK_IS_CRITICAL - do not gate, ever
+ * * CLK_OPS_PARENT_ENABLE - parents need enable during gate/ungate, set rate and re-parent
+ * * CLK_DUTY_CYCLE_PARENT - duty cycle call may be forwarded to the parent clock
*/
-#define CLK_SET_RATE_GATE BIT(0) /* must be gated across rate change */
-#define CLK_SET_PARENT_GATE BIT(1) /* must be gated across re-parent */
-#define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */
-#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */
+/* Please update clk_flags[] in drivers/clk/clk.c when making changes here! */
+#define CLK_SET_RATE_GATE BIT(0)
+#define CLK_SET_PARENT_GATE BIT(1)
+#define CLK_SET_RATE_PARENT BIT(2)
+#define CLK_IGNORE_UNUSED BIT(3)
/* unused */
/* unused */
-#define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */
-#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */
-#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */
-#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */
-#define CLK_SET_RATE_UNGATE BIT(10) /* clock needs to run to set rate */
-#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */
-/* parents need enable during gate/ungate, set rate and re-parent */
+#define CLK_GET_RATE_NOCACHE BIT(6)
+#define CLK_SET_RATE_NO_REPARENT BIT(7)
+#define CLK_GET_ACCURACY_NOCACHE BIT(8)
+#define CLK_RECALC_NEW_RATES BIT(9)
+#define CLK_SET_RATE_UNGATE BIT(10)
+#define CLK_IS_CRITICAL BIT(11)
#define CLK_OPS_PARENT_ENABLE BIT(12)
-/* duty cycle call may be forwarded to the parent clock */
#define CLK_DUTY_CYCLE_PARENT BIT(13)
struct clk;
--
2.54.0
^ permalink raw reply related
* [PATCH v3 2/4] clk: add kernel docs for struct clk_core
From: Brian Masney @ 2026-05-12 1:35 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Maxime Ripard, Jonathan Corbet,
Shuah Khan
Cc: linux-clk, linux-kernel, linux-doc, Brian Masney
In-Reply-To: <20260511-clk-docs-v3-0-ed67e1065809@redhat.com>
Document all of the members of struct clk_core.
Signed-off-by: Brian Masney <bmasney@redhat.com>
---
drivers/clk/clk.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 048adfa86a5d..0b519d95bbcb 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -63,6 +63,57 @@ struct clk_parent_map {
int index;
};
+/**
+ * struct clk_core - The internal state of a clk in the clk tree.
+ * @name: Unique name of the clk for identification.
+ * @ops: Pointer to hardware-specific operations for this clk.
+ * @hw: Pointer for traversing from a struct clk to its
+ * corresponding hardware-specific structure.
+ * @owner: Kernel module owning this clk (for reference counting).
+ * @dev: Device associated with this clk (optional)
+ * @rpm_node: Node for runtime power management list management.
+ * @of_node: Device tree node associated with this clk (if applicable)
+ * @parent: Pointer to the current parent in the clock tree.
+ * @parents: Array of possible parents (for muxes/selectable parents).
+ * @num_parents: Number of possible parents.
+ * @new_parent_index: Index of the new parent during parent change operations.
+ * @rate: Current cached clock rate (Hz).
+ * @req_rate: The last rate requested by a call to clk_set_rate(). It's
+ * initialized to clk_core->rate. It's also updated to
+ * clk_core->rate every time the clock is reparented, and
+ * when we're doing the orphan -> !orphan transition.
+ * @new_rate: New rate to be set during a rate change operation.
+ * @new_parent: Pointer to new parent during parent change. This is also
+ * used when a clk's rate is changed.
+ * @new_child: Pointer to new child during reparenting. This is also
+ * used when a clk's rate is changed.
+ * @flags: Clock property and capability flags. See
+ * `clk framework flags`.
+ * @orphan: True if this clk is currently orphaned.
+ * @rpm_enabled: True if runtime power management is enabled for this clk.
+ * @enable_count: Reference count of enables.
+ * @prepare_count: Reference count of prepares.
+ * @protect_count: Protection reference count against disable.
+ * @min_rate: Minimum supported clock rate (Hz).
+ * @max_rate: Maximum supported clock rate (Hz).
+ * @accuracy: Accuracy of the clock rate (parts per billion).
+ * @phase: Current phase (degrees).
+ * @duty: Current duty cycle configuration (as ratio: num/den).
+ * @children: All of the children of this clk.
+ * @child_node: Node for linking as a child in the parent's list.
+ * @hashtable_node: Node for hash table that allows fast clk lookup by name.
+ * @clks: All of the clk consumers registered.
+ * @notifier_count: Number of notifiers registered for this clk.
+ * @dentry: DebugFS entry for this clk.
+ * @debug_node: DebugFS node for this clk.
+ * @ref: Reference count for structure lifetime management.
+ *
+ * Managed by the clk framework. Clk providers and consumers do not interact
+ * with this structure directly. Instead, clk operations flow through the
+ * framework and the framework manipulates this structure to keep track of
+ * parent/child relationships, rate, enable state, etc.
+ *
+ */
struct clk_core {
const char *name;
const struct clk_ops *ops;
--
2.54.0
^ permalink raw reply related
* [PATCH v3 3/4] docs: clk: include some identifiers to keep documentation up to date
From: Brian Masney @ 2026-05-12 1:35 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Maxime Ripard, Jonathan Corbet,
Shuah Khan
Cc: linux-clk, linux-kernel, linux-doc, Brian Masney
In-Reply-To: <20260511-clk-docs-v3-0-ed67e1065809@redhat.com>
The clk documentation currently has a separate list of some members of
struct clk_core and struct clk_ops. Now that all of these structures
have proper kernel docs, let's go ahead and just include them here via
the identifiers statement in kerneldoc.
While changes are being made here, let's also include the clk flags.
Signed-off-by: Brian Masney <bmasney@redhat.com>
---
Documentation/driver-api/clk.rst | 58 +++++++++-------------------------------
1 file changed, 12 insertions(+), 46 deletions(-)
diff --git a/Documentation/driver-api/clk.rst b/Documentation/driver-api/clk.rst
index c6aca8186a78..294ead519bbf 100644
--- a/Documentation/driver-api/clk.rst
+++ b/Documentation/driver-api/clk.rst
@@ -42,21 +42,8 @@ clock interface.
Common data structures and api
==============================
-Below is the common struct clk_core definition from
-drivers/clk/clk.c, modified for brevity::
-
- struct clk_core {
- const char *name;
- const struct clk_ops *ops;
- struct clk_hw *hw;
- struct module *owner;
- struct clk_core *parent;
- const char **parent_names;
- struct clk_core **parents;
- u8 num_parents;
- u8 new_parent_index;
- ...
- };
+.. kernel-doc:: drivers/clk/clk.c
+ :identifiers: struct clk_core
The members above make up the core of the clk tree topology. The clk
api itself defines several driver-facing functions which operate on
@@ -64,38 +51,17 @@ struct clk. That api is documented in include/linux/clk.h.
Platforms and devices utilizing the common struct clk_core use the struct
clk_ops pointer in struct clk_core to perform the hardware-specific parts of
-the operations defined in clk-provider.h::
+the operations defined in clk-provider.h, and can set one or more
+framework-level flags documented below.
- struct clk_ops {
- int (*prepare)(struct clk_hw *hw);
- void (*unprepare)(struct clk_hw *hw);
- int (*is_prepared)(struct clk_hw *hw);
- void (*unprepare_unused)(struct clk_hw *hw);
- int (*enable)(struct clk_hw *hw);
- void (*disable)(struct clk_hw *hw);
- int (*is_enabled)(struct clk_hw *hw);
- void (*disable_unused)(struct clk_hw *hw);
- unsigned long (*recalc_rate)(struct clk_hw *hw,
- unsigned long parent_rate);
- int (*determine_rate)(struct clk_hw *hw,
- struct clk_rate_request *req);
- int (*set_parent)(struct clk_hw *hw, u8 index);
- u8 (*get_parent)(struct clk_hw *hw);
- int (*set_rate)(struct clk_hw *hw,
- unsigned long rate,
- unsigned long parent_rate);
- int (*set_rate_and_parent)(struct clk_hw *hw,
- unsigned long rate,
- unsigned long parent_rate,
- u8 index);
- unsigned long (*recalc_accuracy)(struct clk_hw *hw,
- unsigned long parent_accuracy);
- int (*get_phase)(struct clk_hw *hw);
- int (*set_phase)(struct clk_hw *hw, int degrees);
- void (*init)(struct clk_hw *hw);
- void (*debug_init)(struct clk_hw *hw,
- struct dentry *dentry);
- };
+.. kernel-doc:: include/linux/clk-provider.h
+ :identifiers: struct clk_ops
+
+Core flags
+==========
+
+.. kernel-doc:: include/linux/clk-provider.h
+ :doc: clk framework flags
Hardware clk implementations
============================
--
2.54.0
^ permalink raw reply related
* [PATCH v3 4/4] clk: test: convert constants to use HZ_PER_MHZ
From: Brian Masney @ 2026-05-12 1:35 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Maxime Ripard, Jonathan Corbet,
Shuah Khan
Cc: linux-clk, linux-kernel, linux-doc, Brian Masney
In-Reply-To: <20260511-clk-docs-v3-0-ed67e1065809@redhat.com>
Convert the DUMMY_CLOCK_* constants over to use HZ_PER_MHZ.
Signed-off-by: Brian Masney <bmasney@redhat.com>
---
drivers/clk/clk_test.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c
index b1961daac5e2..497db4e54d03 100644
--- a/drivers/clk/clk_test.c
+++ b/drivers/clk/clk_test.c
@@ -7,6 +7,7 @@
#include <linux/clk/clk-conf.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/units.h>
/* Needed for clk_hw_get_clk() */
#include "clk.h"
@@ -21,9 +22,9 @@
static const struct clk_ops empty_clk_ops = { };
-#define DUMMY_CLOCK_INIT_RATE (42 * 1000 * 1000)
-#define DUMMY_CLOCK_RATE_1 (142 * 1000 * 1000)
-#define DUMMY_CLOCK_RATE_2 (242 * 1000 * 1000)
+#define DUMMY_CLOCK_INIT_RATE (42 * HZ_PER_MHZ)
+#define DUMMY_CLOCK_RATE_1 (142 * HZ_PER_MHZ)
+#define DUMMY_CLOCK_RATE_2 (242 * HZ_PER_MHZ)
struct clk_dummy_context {
struct clk_hw hw;
--
2.54.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox