From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 46114288AD for ; Wed, 3 Jun 2026 00:14:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780445644; cv=none; b=JEoff7T3ye1/juHhEd+Lxr+FT9/E40RdRNrOe9Wp6lXByoKUZ/Ui6ikqnXkSktmBWv6V4lx/rHKu9W0jeaotTA9aAdzHbhSl4aDO3MXQSijReaQsDi/Sri/bZRs8Q26hDdLzPI+zWMSsm6NBKi2kDK4UT1DoFfKwyZOXyeNrclE= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780445644; c=relaxed/simple; bh=mm6gIzHOWGbrvkXaFbokVrxr3Q0o/TKu8pJ9KA+4QtU=; h=Date:From:To:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=pgtoOHyecAHDKU2HMmltrUiAlRr1MrvH9llJqHmT3NG7TqWQaEZ5rDsWwsH1ldihuwoKqauniuP2JxHJADrNRZE1jBnUJIp1bwBU1wY12oSZhVrIXTUs69d69lNufHCiFbGPyHOWsODM1joF5YnxRid4NlCU0TFCAOgjlBQx6FA= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=Ea9dOxiE; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="Ea9dOxiE" Received: from localhost (unknown [20.236.10.163]) by linux.microsoft.com (Postfix) with ESMTPSA id A601F20B7167; Tue, 2 Jun 2026 17:13:45 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com A601F20B7167 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1780445626; bh=QvHYLeeq22LXTOOSt4403Vmw/wB8KeMBnUaZfgPXSSY=; h=Date:From:To:Cc:Subject:In-Reply-To:References:From; b=Ea9dOxiE5c6woNZHSoWWN2BfMM+pqVwoeI9Vb/2ORXj5DOoa/HVubB3RkNXbS3/ch 7nxzt8Kn7dm0nUmvXKdUJNaghig79qPcQ1YAbhnVa/k2jpzPtA41yyCpiX+DEzAP31 y0vaJooCW+h03dvuIC9byhgQNUrx8evqDjpNdkHI= Date: Tue, 2 Jun 2026 17:13:57 -0700 From: Jacob Pan To: David Matlack Cc: linux-kernel@vger.kernel.org, "iommu@lists.linux.dev" , Jason Gunthorpe , Alex Williamson , Joerg Roedel , Mostafa Saleh , Robin Murphy , Nicolin Chen , "Tian, Kevin" , Yi Liu , Baolu Lu , Saurabh Sengar , skhawaja@google.com, pasha.tatashin@soleen.com, Will Deacon , jacob.pan@linux.microsoft.com Subject: Re: [PATCH v6 6/7] selftests/vfio: Add iommufd noiommu mode selftest for cdev Message-ID: <20260602171357.00001666@linux.microsoft.com> In-Reply-To: References: <20260521221155.1375144-1-jacob.pan@linux.microsoft.com> <20260521221155.1375144-7-jacob.pan@linux.microsoft.com> Organization: LSG X-Mailer: Claws Mail 3.21.0 (GTK+ 2.24.33; x86_64-w64-mingw32) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Hi David, On Thu, 21 May 2026 22:39:34 +0000 David Matlack wrote: > On 2026-05-21 03:11 PM, Jacob Pan wrote: >=20 > For the shortlog, please use "vfio: selftests: ..." >=20 > > Add comprehensive selftest for VFIO device operations with iommufd > > in noiommu mode. Tests cover: > > - Device binding to iommufd > > - IOAS (I/O Address Space) allocation, mapping with dummy IOVA > > - Retrieve PA from dummy IOVA > > - Device attach/detach operations as usual =20 >=20 > High level feedback: Can you use the library for all the standard > setup and ioas mapping instead of reimplementing it in this test? >=20 > iommu =3D iommu_init(MODE_IOMMUFD); > device =3D vfio_pci_device_init(iommu, bdf); >=20 > __iommu_map(...); > __iommu_unma(...); >=20 > iommu_cleanup(iommu); > vfio_pci_device_cleanup(device); >=20 > If not, what are the gaps? It would be useful to fill in those gaps so > that it is easier to use VFIO selftests with noiommu setups. >=20 I will use the library, there is no gap, just that the test was written a while ago did not catch up with the current VFIO selftest helpers. > >=20 > > Signed-off-by: Jacob Pan > > --- > > v6: > > - Add test cases for get_pa length limit > > v4: > > - squash DSA specific selftest changes > > v2: > > - New selftest for generic noiommu bind/unbind > > --- > > tools/testing/selftests/vfio/Makefile | 1 + > > .../lib/include/libvfio/vfio_pci_device.h | 16 + > > .../selftests/vfio/lib/vfio_pci_device.c | 5 +- > > .../vfio/vfio_iommufd_noiommu_test.c | 664 > > ++++++++++++++++++ 4 files changed, 684 insertions(+), 2 > > deletions(-) create mode 100644 > > tools/testing/selftests/vfio/vfio_iommufd_noiommu_test.c > >=20 > > diff --git a/tools/testing/selftests/vfio/Makefile > > b/tools/testing/selftests/vfio/Makefile index > > 0684932d91bf..c9c02fdfd946 100644 --- > > a/tools/testing/selftests/vfio/Makefile +++ > > b/tools/testing/selftests/vfio/Makefile @@ -9,6 +9,7 @@ CFLAGS =3D > > $(KHDR_INCLUDES) TEST_GEN_PROGS +=3D vfio_dma_mapping_test > > TEST_GEN_PROGS +=3D vfio_dma_mapping_mmio_test > > TEST_GEN_PROGS +=3D vfio_iommufd_setup_test > > +TEST_GEN_PROGS +=3D vfio_iommufd_noiommu_test > > TEST_GEN_PROGS +=3D vfio_pci_device_test > > TEST_GEN_PROGS +=3D vfio_pci_device_init_perf_test > > TEST_GEN_PROGS +=3D vfio_pci_driver_test > > 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..6218c91776b3 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 > > @@ -122,4 +122,20 @@ static inline bool > > vfio_pci_device_match(struct vfio_pci_device *device, const char > > *vfio_pci_get_cdev_path(const char *bdf); +static inline bool > > vfio_pci_noiommu_mode_enabled(void) +{ > > + char buf[8] =3D {}; > > + int fd, n; > > + > > + fd =3D > > open("/sys/module/vfio/parameters/enable_unsafe_noiommu_mode", > > + O_RDONLY); =20 >=20 > Can you rebase on top of the latest changes Alex merged for 7.2? It > introduces the sysfs library from Raghu. Please add a helper there for > reading module parameters in a precursor patch. >=20 ok, or if the timing is better, I will wait for 7.2 then submit this selftest separately? > > + if (fd < 0) > > + return false; > > + > > + n =3D read(fd, buf, sizeof(buf) - 1); > > + close(fd); > > + > > + return n > 0 && buf[0] =3D=3D 'Y'; > > +} > > + > > #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 > > fc75e04ef010..1a91658e812d 100644 --- > > a/tools/testing/selftests/vfio/lib/vfio_pci_device.c +++ > > b/tools/testing/selftests/vfio/lib/vfio_pci_device.c @@ -308,8 > > +308,9 @@ const char *vfio_pci_get_cdev_path(const char *bdf) > > VFIO_ASSERT_NOT_NULL(dir, "Failed to open directory %s\n", > > dir_path); while ((entry =3D readdir(dir)) !=3D NULL) { > > - /* Find the file that starts with "vfio" */ > > - if (strncmp("vfio", entry->d_name, 4)) > > + /* Find the file that starts with "vfio" or > > "noiommu-vfio" */ > > + if (strncmp("vfio", entry->d_name, 4) && > > + strncmp("noiommu-vfio", entry->d_name, 12)) > > continue; > > =20 > > snprintf(cdev_path, PATH_MAX, > > "/dev/vfio/devices/%s", entry->d_name); diff --git > > a/tools/testing/selftests/vfio/vfio_iommufd_noiommu_test.c > > b/tools/testing/selftests/vfio/vfio_iommufd_noiommu_test.c new file > > mode 100644 index 000000000000..d91b505fc60d --- /dev/null > > +++ b/tools/testing/selftests/vfio/vfio_iommufd_noiommu_test.c > > @@ -0,0 +1,664 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * VFIO iommufd NoIOMMU Mode Selftest > > + * > > + * Tests VFIO device operations with iommufd in noiommu mode, > > including: > > + * - Device binding to iommufd > > + * - IOAS (I/O Address Space) allocation and management > > + * - Device attach/detach to IOAS > > + * - Memory mapping in IOAS > > + * - Device info queries and reset > > + */ > > + > > +#include > > +#include > > +#include > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include > > +#include "kselftest_harness.h" > > + > > +static const char iommu_dev_path[] =3D "/dev/iommu"; =20 >=20 > I don't see why this needs to be global variables. >=20 > > +static const char *cdev_path; > > + > > +static char *vfio_noiommu_get_device_id(const char *bdf) > > +{ > > + char *path =3D NULL; > > + char *vfio_id =3D NULL; > > + struct dirent *dentry; > > + DIR *dp; > > + > > + if (asprintf(&path, "/sys/bus/pci/devices/%s/vfio-dev", > > bdf) < 0) > > + return NULL; > > + > > + dp =3D opendir(path); > > + if (!dp) { > > + free(path); > > + return NULL; > > + } > > + > > + while ((dentry =3D readdir(dp)) !=3D NULL) { > > + if (strncmp("noiommu-vfio", dentry->d_name, 12) =3D=3D > > 0) { > > + vfio_id =3D strdup(dentry->d_name); > > + break; > > + } > > + } > > + > > + closedir(dp); > > + free(path); > > + return vfio_id; > > +} > > + > > +static char *vfio_noiommu_get_cdev_path(const char *bdf) > > +{ > > + char *vfio_id =3D vfio_noiommu_get_device_id(bdf); > > + char *cdev =3D NULL; > > + > > + if (vfio_id) { > > + asprintf(&cdev, "/dev/vfio/devices/%s", vfio_id); > > + free(vfio_id); > > + } > > + return cdev; > > +} =20 >=20 > Can we put this in the library and find a way to share code with > vfio_pci_get_cdev_path()? >=20 I will delete/merge this, vfio_pci_get_cdev_path will scan /sys/bus/pci/devices//vfio-dev/ and accepts both vfio* and noiommu-vfio*, then returns /dev/vfio/devices/. > > + > > +static int vfio_device_bind_iommufd_ioctl(int cdev_fd, int iommufd) > > +{ > > + struct vfio_device_bind_iommufd bind_args =3D { > > + .argsz =3D sizeof(bind_args), > > + .iommufd =3D iommufd, > > + }; > > + > > + return ioctl(cdev_fd, VFIO_DEVICE_BIND_IOMMUFD, > > &bind_args); +} =20 >=20 > Please add the ioctl wrappers to the library so they can be used by > other tests or library code in the future. >=20 > VFIO device ioctls can go in vfio_pci_device.c and iommufd ioctls can > go in iommu.c. >=20 ok, will do. > > + > > +static int vfio_device_get_info_ioctl(int cdev_fd, > > + struct vfio_device_info > > *info) +{ > > + info->argsz =3D sizeof(*info); > > + return ioctl(cdev_fd, VFIO_DEVICE_GET_INFO, info); > > +} > > + > > +static int vfio_device_ioas_alloc_ioctl(int iommufd, > > + struct iommu_ioas_alloc > > *alloc_args) +{ > > + alloc_args->size =3D sizeof(*alloc_args); > > + alloc_args->flags =3D 0; > > + return ioctl(iommufd, IOMMU_IOAS_ALLOC, alloc_args); > > +} > > + > > +static int vfio_device_attach_iommufd_pt_ioctl(int cdev_fd, u32 > > pt_id) +{ > > + struct vfio_device_attach_iommufd_pt attach_args =3D { > > + .argsz =3D sizeof(attach_args), > > + .pt_id =3D pt_id, > > + }; > > + > > + return ioctl(cdev_fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, > > &attach_args); +} > > + > > +static int vfio_device_detach_iommufd_pt_ioctl(int cdev_fd) > > +{ > > + struct vfio_device_detach_iommufd_pt detach_args =3D { > > + .argsz =3D sizeof(detach_args), > > + }; > > + > > + return ioctl(cdev_fd, VFIO_DEVICE_DETACH_IOMMUFD_PT, > > &detach_args); +} > > + > > +static int vfio_device_get_region_info_ioctl(int cdev_fd, uint32_t > > index, > > + struct > > vfio_region_info *info) +{ > > + info->argsz =3D sizeof(*info); > > + info->index =3D index; > > + return ioctl(cdev_fd, VFIO_DEVICE_GET_REGION_INFO, info); > > +} > > + > > +static int vfio_device_reset_ioctl(int cdev_fd) > > +{ > > + return ioctl(cdev_fd, VFIO_DEVICE_RESET); > > +} > > + > > +static int ioas_map_pages(int iommufd, uint32_t ioas_id, uint64_t > > iova, > > + size_t length, bool hugepages) > > +{ > > + struct iommu_ioas_map map_args =3D { > > + .size =3D sizeof(map_args), > > + .ioas_id =3D ioas_id, > > + .iova =3D iova, > > + .length =3D length, > > + .flags =3D IOMMU_IOAS_MAP_READABLE | > > IOMMU_IOAS_MAP_WRITEABLE | IOMMU_IOAS_MAP_FIXED_IOVA, > > + }; > > + void *pages; > > + int ret; > > + > > + /* Allocate test pages */ > > + if (hugepages) > > + pages =3D mmap(NULL, length, PROT_READ | PROT_WRITE, > > + MAP_PRIVATE | MAP_ANONYMOUS | > > MAP_HUGETLB, -1, 0); > > + else > > + pages =3D mmap(NULL, length, PROT_READ | PROT_WRITE, > > + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); > > + if (pages =3D=3D MAP_FAILED) { > > + printf("mmap failed for length 0x%lx\n", (unsigned > > long)length); > > + return -ENOMEM; > > + } > > + > > + /* Set up page pointer for mapping */ > > + map_args.user_va =3D (uintptr_t)pages; > > + > > + printf(" ioas_map_pages: ioas_id=3D%u, iova=3D0x%lx, > > length=3D0x%lx, user_va=3D%p\n", > > + ioas_id, (unsigned long)iova, (unsigned > > long)length, pages); + > > + /* Map into IOAS */ > > + ret =3D ioctl(iommufd, IOMMU_IOAS_MAP, &map_args); > > + if (ret !=3D 0) > > + printf(" IOMMU_IOAS_MAP failed: %d (%s)\n", ret, > > strerror(errno)); > > + else > > + printf(" IOMMU_IOAS_MAP succeeded, IOVA=3D0x%lx\n", > > (unsigned long)map_args.iova); + > > + munmap(pages, length); > > + return ret; > > +} > > + > > +static int ioas_unmap_pages(int iommufd, uint32_t ioas_id, > > uint64_t iova, > > + size_t length) > > +{ > > + struct iommu_ioas_unmap unmap_args =3D { > > + .size =3D sizeof(unmap_args), > > + .ioas_id =3D ioas_id, > > + .iova =3D iova, > > + .length =3D length, > > + }; > > + > > + return ioctl(iommufd, IOMMU_IOAS_UNMAP, &unmap_args); > > +} > > + > > +static int ioas_destroy_ioctl(int iommufd, uint32_t ioas_id) > > +{ > > + struct iommu_destroy destroy_args =3D { > > + .size =3D sizeof(destroy_args), > > + .id =3D ioas_id, > > + }; > > + > > + return ioctl(iommufd, IOMMU_DESTROY, &destroy_args); > > +} > > + > > +static int ioas_noiommu_get_pa_ioctl_len(int iommufd, uint32_t > > ioas_id, > > + uint64_t iova, uint64_t max_length, > > + uint64_t *phys_out, uint64_t > > *length_out) +{ > > + struct iommu_ioas_noiommu_get_pa get_pa =3D { > > + .size =3D sizeof(get_pa), > > + .flags =3D 0, > > + .ioas_id =3D ioas_id, > > + .iova =3D iova, > > + .length =3D max_length, > > + }; > > + > > + printf(" ioas_noiommu_get_pa_ioctl: ioas_id=3D%u, > > iova=3D0x%lx, max_length=3D0x%lx\n", > > + ioas_id, (unsigned long)iova, (unsigned > > long)max_length); + > > + if (ioctl(iommufd, IOMMU_IOAS_NOIOMMU_GET_PA, &get_pa) !=3D > > 0) { > > + printf(" IOMMU_IOAS_NOIOMMU_GET_PA failed: %s > > (errno=3D%d)\n", > > + strerror(errno), errno); > > + return -1; > > + } > > + > > + printf(" IOMMU_IOAS_NOIOMMU_GET_PA succeeded: PA=3D0x%lx, > > length=3D0x%lx\n", > > + (unsigned long)get_pa.out_phys, (unsigned > > long)get_pa.length); + > > + if (phys_out) > > + *phys_out =3D get_pa.out_phys; > > + if (length_out) > > + *length_out =3D get_pa.length; > > + > > + return 0; > > +} > > + > > +static int ioas_noiommu_get_pa_ioctl(int iommufd, uint32_t > > ioas_id, uint64_t iova, > > + uint64_t *phys_out, uint64_t > > *length_out) +{ > > + return ioas_noiommu_get_pa_ioctl_len(iommufd, ioas_id, > > iova, 0, > > + phys_out, length_out); > > +} > > + > > +FIXTURE(vfio_noiommu) { > > + int cdev_fd; > > + int iommufd; > > +}; > > + > > +FIXTURE_SETUP(vfio_noiommu) > > +{ > > + ASSERT_LE(0, (self->cdev_fd =3D open(cdev_path, O_RDWR, 0))); > > + ASSERT_LE(0, (self->iommufd =3D open(iommu_dev_path, O_RDWR, > > 0))); +} > > + > > +FIXTURE_TEARDOWN(vfio_noiommu) > > +{ > > + if (self->cdev_fd >=3D 0) > > + close(self->cdev_fd); > > + if (self->iommufd >=3D 0) > > + close(self->iommufd); > > +} > > + > > +/* > > + * Test: Device cdev can be opened > > + */ > > +TEST_F(vfio_noiommu, device_cdev_open) > > +{ > > + ASSERT_LE(0, self->cdev_fd); > > +} =20 >=20 > This is already tested by the FIXTURE_SETUP(). No need for a TEST_F(). >=20 will remove > > + > > +/* > > + * Test: Device can be bound to iommufd > > + */ > > +TEST_F(vfio_noiommu, device_bind_iommufd) > > +{ > > + ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); +} > > + > > +/* > > + * Test: Device info can be queried after binding > > + */ > > +TEST_F(vfio_noiommu, device_get_info_after_bind) > > +{ > > + struct vfio_device_info info; > > + > > + ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); > > + ASSERT_EQ(0, vfio_device_get_info_ioctl(self->cdev_fd, > > &info)); > > + ASSERT_NE(0, info.argsz); > > +} > > + > > +/* > > + * Test: Getting device info fails without bind > > + */ > > +TEST_F(vfio_noiommu, device_get_info_without_bind_fails) > > +{ > > + struct vfio_device_info info; > > + > > + ASSERT_NE(0, vfio_device_get_info_ioctl(self->cdev_fd, > > &info)); +} > > + > > +/* > > + * Test: Binding with invalid iommufd fails > > + */ > > +TEST_F(vfio_noiommu, device_bind_bad_iommufd_fails) > > +{ > > + ASSERT_NE(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > -2)); +} =20 >=20 > Are all these tests really specific to noiommu? >=20 no, will remove this and other duplicated tests. > > + > > +/* > > + * Test: Cannot bind twice to same device > > + */ > > +TEST_F(vfio_noiommu, device_repeated_bind_fails) > > +{ > > + ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); > > + ASSERT_NE(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); +} > > + > > +/* > > + * Test: IOAS can be allocated > > + */ > > +TEST_F(vfio_noiommu, ioas_alloc) > > +{ > > + struct iommu_ioas_alloc alloc_args; > > + > > + ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, > > + &alloc_args)); > > + ASSERT_NE(0, alloc_args.out_ioas_id); > > +} > > + > > +/* > > + * Test: IOAS can be destroyed > > + */ > > +TEST_F(vfio_noiommu, ioas_destroy) > > +{ > > + struct iommu_ioas_alloc alloc_args; > > + > > + ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, > > + &alloc_args)); > > + ASSERT_EQ(0, ioas_destroy_ioctl(self->iommufd, > > + alloc_args.out_ioas_id)); > > +} > > + > > +/* > > + * Test: Device can attach to IOAS after binding > > + */ > > +TEST_F(vfio_noiommu, device_attach_to_ioas) > > +{ > > + struct iommu_ioas_alloc alloc_args; > > + > > + ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); > > + ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, > > + &alloc_args)); > > + ASSERT_EQ(0, > > vfio_device_attach_iommufd_pt_ioctl(self->cdev_fd, > > + > > alloc_args.out_ioas_id)); +} > > + > > +/* > > + * Test: Attaching to invalid IOAS fails > > + */ > > +TEST_F(vfio_noiommu, device_attach_invalid_ioas_fails) > > +{ > > + ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); > > + ASSERT_NE(0, > > vfio_device_attach_iommufd_pt_ioctl(self->cdev_fd, > > + > > UINT32_MAX)); +} > > + > > +/* > > + * Test: Device can detach from IOAS > > + */ > > +TEST_F(vfio_noiommu, device_detach_from_ioas) > > +{ > > + struct iommu_ioas_alloc alloc_args; > > + > > + ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); > > + ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, > > + &alloc_args)); > > + ASSERT_EQ(0, > > vfio_device_attach_iommufd_pt_ioctl(self->cdev_fd, > > + > > alloc_args.out_ioas_id)); > > + ASSERT_EQ(0, > > vfio_device_detach_iommufd_pt_ioctl(self->cdev_fd)); +} > > + > > +/* > > + * Test: Full lifecycle - bind, attach, detach, reset > > + */ > > +TEST_F(vfio_noiommu, device_lifecycle) > > +{ > > + struct iommu_ioas_alloc alloc_args; > > + struct vfio_device_info info; > > + > > + /* Bind device to iommufd */ > > + ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); + > > + /* Allocate IOAS */ > > + ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, > > + &alloc_args)); > > + > > + /* Attach device to IOAS */ > > + ASSERT_EQ(0, > > vfio_device_attach_iommufd_pt_ioctl(self->cdev_fd, > > + > > alloc_args.out_ioas_id)); + > > + /* Query device info */ > > + ASSERT_EQ(0, vfio_device_get_info_ioctl(self->cdev_fd, > > &info)); + > > + /* Detach device from IOAS */ > > + ASSERT_EQ(0, > > vfio_device_detach_iommufd_pt_ioctl(self->cdev_fd)); + > > + /* Reset device */ > > + ASSERT_EQ(0, vfio_device_reset_ioctl(self->cdev_fd)); > > +} > > + > > +/* > > + * Test: Get region info > > + */ > > +TEST_F(vfio_noiommu, device_get_region_info) > > +{ > > + struct vfio_device_info dev_info; > > + struct vfio_region_info region_info; > > + > > + ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); > > + ASSERT_EQ(0, vfio_device_get_info_ioctl(self->cdev_fd, > > &dev_info)); + > > + /* Try to get first region info if device has regions */ > > + if (dev_info.num_regions > 0) { > > + ASSERT_EQ(0, > > vfio_device_get_region_info_ioctl(self->cdev_fd, 0, > > + > > ®ion_info)); > > + ASSERT_NE(0, region_info.argsz); > > + } > > +} > > + > > +TEST_F(vfio_noiommu, device_reset) > > +{ > > + ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); > > + ASSERT_EQ(0, vfio_device_reset_ioctl(self->cdev_fd)); > > +} > > + > > +TEST_F(vfio_noiommu, ioas_map_pages) > > +{ > > + struct iommu_ioas_alloc alloc_args; > > + long page_size =3D sysconf(_SC_PAGESIZE); > > + uint64_t iova =3D 0x10000; > > + int i; > > + > > + ASSERT_GT(page_size, 0); > > + > > + ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, > > + &alloc_args)); > > + > > + printf("Page size: %ld bytes\n", page_size); > > + /* Test mapping regions of different sizes: 1, 2, 4, 8 > > pages */ > > + for (i =3D 0; i < 4; i++) { > > + size_t map_size =3D page_size * (1 << i); /* 1, 2, > > 4, 8 pages */ > > + uint64_t test_iova =3D iova + (i * 0x100000); > > + > > + /* Attempt to map each region (may fail if not > > supported) */ > > + ioas_map_pages(self->iommufd, > > alloc_args.out_ioas_id, > > + test_iova, map_size, false); > > + } > > +} > > + > > +TEST_F(vfio_noiommu, multiple_ioas_alloc) > > +{ > > + struct iommu_ioas_alloc alloc1, alloc2; > > + > > + ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, > > &alloc1)); > > + ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, > > &alloc2)); > > + ASSERT_NE(alloc1.out_ioas_id, alloc2.out_ioas_id); > > +} > > + > > +/* > > + * Test: Query physical address for IOVA > > + * Tests IOMMU_IOAS_NOIOMMU_GET_PA ioctl to translate IOVA to > > physical address > > + * Note: Device must be attached to IOAS for PA query to work > > + */ > > +#define NR_PAGES 32 > > +TEST_F(vfio_noiommu, ioas_noiommu_get_pa_mapped) > > +{ > > + struct iommu_ioas_alloc alloc_args; > > + long page_size =3D sysconf(_SC_PAGESIZE); > > + uint64_t iova =3D 0x200000; > > + uint64_t phys =3D 0; > > + uint64_t length =3D 0; > > + int ret; > > + > > + ASSERT_GT(page_size, 0); > > + > > + ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); + > > + ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, > > + &alloc_args)); > > + > > + ASSERT_EQ(0, > > vfio_device_attach_iommufd_pt_ioctl(self->cdev_fd, > > + > > alloc_args.out_ioas_id)); + > > + /* > > + * Map a page into an arbitrary IOAS, used as a cookie for > > lookup. > > + * Use hugepages to test contiguous PA. Make sure > > hugepages are > > + * available. e.g. echo 64 > /proc/sys/vm/nr_hugepages > > + */ > > + ret =3D ioas_map_pages(self->iommufd, alloc_args.out_ioas_id, > > + iova, page_size * NR_PAGES, true); > > + if (ret !=3D 0) > > + return; > > + > > + /* Query the physical address for the mapped dummy IOVA */ > > + ret =3D ioas_noiommu_get_pa_ioctl(self->iommufd, > > alloc_args.out_ioas_id, > > + iova, &phys, &length); > > + > > + if (ret =3D=3D 0) { > > + /* If we got a result, verify it's valid */ > > + ASSERT_NE(0, phys); > > + ASSERT_GE((uint64_t)page_size * NR_PAGES, length); > > + } > > + > > + /* > > + * Query with a non-page-aligned IOVA. The returned length > > must > > + * not exceed the actual contiguous range starting from > > that > > + * offset, i.e. it must be reduced by the sub-page offset. > > + */ > > + phys =3D 0; > > + length =3D 0; > > + ret =3D ioas_noiommu_get_pa_ioctl(self->iommufd, > > alloc_args.out_ioas_id, > > + iova + 0x80, &phys, &length); > > + if (ret =3D=3D 0) { > > + ASSERT_NE(0, phys); > > + /* Length must account for the sub-page offset */ > > + ASSERT_GE((uint64_t)page_size * NR_PAGES - 0x80, > > length); > > + ASSERT_LE(length, (uint64_t)page_size * NR_PAGES - > > 0x80); > > + /* Must not overshoot into the next page boundary > > */ > > + ASSERT_EQ(0, (phys + length) % page_size); > > + } > > +} > > + > > +TEST_F(vfio_noiommu, ioas_noiommu_get_pa_unmapped_fails) > > +{ > > + struct iommu_ioas_alloc alloc_args; > > + > > + ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, > > + &alloc_args)); > > + > > + /* Try to retrieve unmapped IOVA (should fail) */ > > + ASSERT_NE(0, ioas_noiommu_get_pa_ioctl(self->iommufd, > > alloc_args.out_ioas_id, > > + 0x10000, NULL, NULL)); > > +} > > + > > +/* > > + * Test: length =3D=3D 0 means no limit (backward compat default) > > + */ > > +TEST_F(vfio_noiommu, ioas_noiommu_get_pa_length_zero_no_limit) > > +{ > > + struct iommu_ioas_alloc alloc_args; > > + long page_size =3D sysconf(_SC_PAGESIZE); > > + uint64_t iova =3D 0x200000; > > + uint64_t phys_nolimit =3D 0, phys_zero =3D 0; > > + uint64_t len_nolimit =3D 0, len_zero =3D 0; > > + int ret; > > + > > + ASSERT_GT(page_size, 0); > > + > > + ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); > > + ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, > > &alloc_args)); > > + ASSERT_EQ(0, > > vfio_device_attach_iommufd_pt_ioctl(self->cdev_fd, > > + > > alloc_args.out_ioas_id)); + > > + ret =3D ioas_map_pages(self->iommufd, alloc_args.out_ioas_id, > > + iova, page_size * NR_PAGES, true); > > + if (ret !=3D 0) > > + return; > > + > > + /* Query with length=3D0 (no limit, default behavior) */ > > + ret =3D ioas_noiommu_get_pa_ioctl_len(self->iommufd, > > alloc_args.out_ioas_id, > > + iova, 0, &phys_zero, > > &len_zero); > > + if (ret !=3D 0) > > + return; > > + > > + /* Query with the wrapper (also passes 0) =E2=80=94 must match */ > > + ret =3D ioas_noiommu_get_pa_ioctl(self->iommufd, > > alloc_args.out_ioas_id, > > + iova, &phys_nolimit, > > &len_nolimit); > > + ASSERT_EQ(0, ret); > > + ASSERT_EQ(phys_zero, phys_nolimit); > > + ASSERT_EQ(len_zero, len_nolimit); > > +} > > + > > +/* > > + * Test: length caps the returned contiguous range > > + */ > > +TEST_F(vfio_noiommu, ioas_noiommu_get_pa_length_capped) > > +{ > > + struct iommu_ioas_alloc alloc_args; > > + long page_size =3D sysconf(_SC_PAGESIZE); > > + uint64_t iova =3D 0x200000; > > + uint64_t phys =3D 0; > > + uint64_t len_full =3D 0, len_capped =3D 0; > > + uint64_t cap; > > + int ret; > > + > > + ASSERT_GT(page_size, 0); > > + > > + ASSERT_EQ(0, vfio_device_bind_iommufd_ioctl(self->cdev_fd, > > + > > self->iommufd)); > > + ASSERT_EQ(0, vfio_device_ioas_alloc_ioctl(self->iommufd, > > &alloc_args)); > > + ASSERT_EQ(0, > > vfio_device_attach_iommufd_pt_ioctl(self->cdev_fd, > > + > > alloc_args.out_ioas_id)); + > > + ret =3D ioas_map_pages(self->iommufd, alloc_args.out_ioas_id, > > + iova, page_size * NR_PAGES, true); > > + if (ret !=3D 0) > > + return; > > + > > + /* First get the full uncapped length */ > > + ret =3D ioas_noiommu_get_pa_ioctl(self->iommufd, > > alloc_args.out_ioas_id, > > + iova, &phys, &len_full); > > + if (ret !=3D 0) > > + return; > > + > > + ASSERT_NE(0, phys); > > + ASSERT_NE(0, len_full); > > + > > + /* Cap to a single page =E2=80=94 returned length must not exceed > > it */ > > + cap =3D page_size; > > + ret =3D ioas_noiommu_get_pa_ioctl_len(self->iommufd, > > alloc_args.out_ioas_id, > > + iova, cap, &phys, > > &len_capped); > > + ASSERT_EQ(0, ret); > > + ASSERT_LE(len_capped, cap); > > + ASSERT_NE(0, len_capped); > > + > > + /* > > + * If full length was larger than one page, confirm > > capping works. > > + * Otherwise the mapping wasn't contiguous enough to test. > > + */ > > + if (len_full > cap) > > + ASSERT_GT(len_full, len_capped); > > + > > + /* Cap to a very large value =E2=80=94 should return the same as > > uncapped */ > > + ret =3D ioas_noiommu_get_pa_ioctl_len(self->iommufd, > > alloc_args.out_ioas_id, > > + iova, UINT64_MAX, > > &phys, &len_capped); > > + ASSERT_EQ(0, ret); > > + ASSERT_EQ(len_full, len_capped); > > +} > > + > > +int main(int argc, char *argv[]) > > +{ > > + const char *device_bdf =3D vfio_selftests_get_bdf(&argc, > > argv); > > + char *cdev =3D NULL; > > + > > + if (!device_bdf) { > > + ksft_print_msg("No device BDF provided\n"); > > + return KSFT_SKIP; > > + } =20 >=20 > vfio_selftests_get_bdf() already handles exiting with KSFT_SKIP if it > can't find a BDF. >=20 will remove. > > + > > + cdev =3D vfio_noiommu_get_cdev_path(device_bdf); > > + if (!cdev) { > > + ksft_print_msg("Could not find cdev for device > > %s\n", > > + device_bdf); =20 >=20 > nit: "Could not find niommu cdev for ..." >=20 not needed as no-IOMMU-specific helper will be removed :) > > + return KSFT_SKIP; > > + } > > + > > + cdev_path =3D cdev; > > + ksft_print_msg("Using cdev device %s for BDF %s\n", > > cdev_path, > > + device_bdf); > > + > > + return test_harness_run(argc, argv); > > +} > > --=20 > > 2.43.0 > > =20