iommufd: Add vIOMMU infrastructure (Part-2: vDEVICE)
Subsystems (4)
v7 cover iommufd: Add vIOMMU infrastructure (Part-2: vDEVICE)
Following the previous vIOMMU series, this adds another vDEVICE structure, representing the association from an iommufd_device to an iommufd_viommu. This gives the whole architecture a new "v" layer:
_______________________________________________________________________
| iommufd (with vIOMMU/vDEVICE) |
| _____________ _____________ |
| | | | | |
| |----------------| vIOMMU |<---| vDEVICE |<------| |
| | | | |_____________| | |
| | ______ | | _____________ ___|____ |
| | | | | | | | | | |
| | | IOAS |<---|(HWPT_PAGING)|<---| HWPT_NESTED |<--| DEVICE | |
| | |______| |_____________| |_____________| |________| |
|______|________|______________|__________________|_______________|_____|
| | | | |
______v_____ | ______v_____ ______v_____ ___v__
| struct | | PFN | (paging) | | (nested) | |struct|
|iommu_device| |------>|iommu_domain|<----|iommu_domain|<----|device|
|____________| storage|____________| |____________| |______|This vDEVICE object is used to collect and store all vIOMMU-related device information/attributes in a VM. As an initial series for vDEVICE, add only the virt_id to the vDEVICE, which is a vIOMMU specific device ID in a VM:
e.g. vSID of ARM SMMUv3, vDeviceID of AMD IOMMU, and vRID of Intel VT-d to a Context Table. This virt_id helps IOMMU drivers to link the vID to a pID of the device against the physical IOMMU instance. This is essential for a vIOMMU-based invalidation, where the request contains a device's vID for a device cache flush, e.g. ATC invalidation.
Therefore, with this vDEVICE object, support a vIOMMU-based invalidation, by reusing IOMMUFD_CMD_HWPT_INVALIDATE for a vIOMMU object to flush cache with a given driver data.
As for the implementation of the series, add driver support in ARM SMMUv3 for a real world use case.
This series is on Github:
https://github.com/nicolinc/iommufd/commits/iommufd_viommu_p2-v7
(QEMU branch for testing will be provided in Jason's nesting series)
Changelog
v7
* Added "Reviewed-by" from Jason
* Corrected a line of comments in iommufd_vdevice_destroy()
v6
https://lore.kernel.org/all/[email protected]/
* Fixed kdoc in the uAPI header
* Fixed indentations in iommufd.rst
* Replaced vdev->idev with vdev->dev
* Added "Reviewed-by" from Kevin and Jason
* Updated kdoc of struct iommu_vdevice_alloc
* Fixed lockdep function call in iommufd_viommu_find_dev
* Added missing iommu_dev validation between viommu and idev
* Skipped SMMUv3 driver changes (to post in a separate series)
* Replaced !cache_invalidate_user in WARN_ON of the allocation path with cache_invalidate_user validation in iommufd_hwpt_invalidate
v5
https://lore.kernel.org/all/[email protected]/
* Dropped driver-allocated vDEVICE support
* Changed vdev_to_dev helper to iommufd_viommu_find_dev
v4
https://lore.kernel.org/all/[email protected]/
* Added missing brackets in switch-case
* Fixed the unreleased idev refcount issue
* Reworked the iommufd_vdevice_alloc allocator
* Dropped support for IOMMU_VIOMMU_TYPE_DEFAULT
* Added missing TEST_LENGTH and fail_nth coverages
* Added a verification to the driver-allocated vDEVICE object
* Added an iommufd_vdevice_abort for a missing mutex protection
* Added a u64 structure arm_vsmmu_invalidation_cmd for user command conversion
v3
https://lore.kernel.org/all/[email protected]/
* Added Jason's Reviewed-by
* Split this invalidation part out of the part-1 series
* Repurposed VDEV_ID ioctl to a wider vDEVICE structure and ioctl
* Reduced viommu_api functions by allowing drivers to access viommu and vdevice structure directly
* Dropped vdevs_rwsem by using xa_lock instead
* Dropped arm_smmu_cache_invalidate_user
v2
https://lore.kernel.org/all/[email protected]/
* Limited vdev_id to one per idev
* Added a rw_sem to protect the vdev_id list
* Reworked driver-level APIs with proper lockings
* Added a new viommu_api file for IOMMUFD_DRIVER config
* Dropped useless iommu_dev point from the viommu structure
* Added missing index numnbers to new types in the uAPI header
* Dropped IOMMU_VIOMMU_INVALIDATE uAPI; Instead, reuse the HWPT one
* Reworked mock_viommu_cache_invalidate() using the new iommu helper
* Reordered details of set/unset_vdev_id handlers for proper lockings
v1
https://lore.kernel.org/all/[email protected]/
Thanks!
Nicolin
Jason Gunthorpe (1):
iommu: Add iommu_copy_struct_from_full_user_array helper
Nicolin Chen (9):
iommufd/viommu: Add IOMMUFD_OBJ_VDEVICE and IOMMU_VDEVICE_ALLOC ioctl iommufd/selftest: Add IOMMU_VDEVICE_ALLOC test coverage iommu/viommu: Add cache_invalidate to iommufd_viommu_ops
iommufd: Allow hwpt_id to carry viommu_id for IOMMU_HWPT_INVALIDATE iommufd/viommu: Add iommufd_viommu_find_dev helper iommufd/selftest: Add mock_viommu_cache_invalidate iommufd/selftest: Add IOMMU_TEST_OP_DEV_CHECK_CACHE test command iommufd/selftest: Add vIOMMU coverage for IOMMU_HWPT_INVALIDATE ioctl
Documentation: userspace-api: iommufd: Update vDEVICE
drivers/iommu/iommufd/iommufd_private.h | 18 ++ drivers/iommu/iommufd/iommufd_test.h | 30 +++ include/linux/iommu.h | 48 ++++- include/linux/iommufd.h | 22 ++ include/uapi/linux/iommufd.h | 31 ++- tools/testing/selftests/iommu/iommufd_utils.h | 83 +++++++ drivers/iommu/iommufd/driver.c | 13 ++ drivers/iommu/iommufd/hw_pagetable.c | 40 +++- drivers/iommu/iommufd/main.c | 6 + drivers/iommu/iommufd/selftest.c | 98 ++++++++- drivers/iommu/iommufd/viommu.c | 76 +++++++ tools/testing/selftests/iommu/iommufd.c | 204 +++++++++++++++++- .../selftests/iommu/iommufd_fail_nth.c | 4 + Documentation/userspace-api/iommufd.rst | 41 +++- 14 files changed, 688 insertions(+), 26 deletions(-)
base-commit: 0780dd4af09a5360392f5c376c35ffc2599a9c0e
Signature · 1 line
2.43.0
PATCH v7 1/10 iommufd/viommu: Add IOMMUFD_OBJ_VDEVICE and IOMMU_VDEVICE_ALLOC ioctl
Introduce a new IOMMUFD_OBJ_VDEVICE to represent a physical device (struct device) against a vIOMMU (struct iommufd_viommu) object in a VM.
This vDEVICE object (and its structure) holds all the infos and attributes in the VM, regarding the device related to the vIOMMU.
As an initial patch, add a per-vIOMMU virtual ID. This can be:
- Virtual StreamID on a nested ARM SMMUv3, an index to a Stream Table
- Virtual DeviceID on a nested AMD IOMMU, an index to a Device Table
- Virtual RID on a nested Intel VT-D IOMMU, an index to a Context Table
Potentially, this vDEVICE structure would hold some vData for Confidential Compute Architecture (CCA). Use this virtual ID to index an "vdevs" xarray that belongs to a vIOMMU object.
Add a new ioctl for vDEVICE allocations. Since a vDEVICE is a connection of a device object and an iommufd_viommu object, take two refcounts in the ioctl handler.
--- drivers/iommu/iommufd/iommufd_private.h | 18 ++++++ include/linux/iommufd.h | 4 ++ include/uapi/linux/iommufd.h | 22 +++++++ drivers/iommu/iommufd/main.c | 6 ++ drivers/iommu/iommufd/viommu.c | 76 +++++++++++++++++++++++++ 5 files changed, 126 insertions(+)
drivers/iommu/iommufd/iommufd_private.h +18 -0
@@ -507,8 +507,26 @@ static inline int iommufd_hwpt_replace_device(struct iommufd_device *idev,return iommu_group_replace_domain (idev->igroup->group, hwpt->domain); } +static inline struct iommufd_viommu * +iommufd_get_viommu (struct iommufd_ucmd *ucmd,u32 id) +{ +return container_of (iommufd_get_object (ucmd->ictx, id, +IOMMUFD_OBJ_VIOMMU ), + struct iommufd_viommu, obj); +} +int iommufd_viommu_alloc_ioctl (struct iommufd_ucmd *ucmd);void iommufd_viommu_destroy (struct iommufd_object *obj); +int iommufd_vdevice_alloc_ioctl (struct iommufd_ucmd *ucmd); +void iommufd_vdevice_destroy (struct iommufd_object *obj); + +struct iommufd_vdevice { +struct iommufd_object obj; +struct iommufd_ctx *ictx; +struct iommufd_viommu *viommu; +struct device *dev; +u64 id;/* per-vIOMMU virtual ID */ +};#ifdef CONFIG_IOMMUFD_TEST int iommufd_test (struct iommufd_ucmd *ucmd);
include/linux/iommufd.h +4 -0
@@ -10,6 +10,7 @@#include <linux/errno.h> #include <linux/refcount.h> #include <linux/types.h> +#include <linux/xarray.h> struct device ;struct file ;
@@ -31,6 +32,7 @@ enum iommufd_object_type {IOMMUFD_OBJ_ACCESS ,IOMMUFD_OBJ_FAULT ,IOMMUFD_OBJ_VIOMMU , +IOMMUFD_OBJ_VDEVICE , #ifdef CONFIG_IOMMUFD_TEST IOMMUFD_OBJ_SELFTEST, #endif
@@ -89,6 +91,8 @@ struct iommufd_viommu {const struct iommufd_viommu_ops *ops; +struct xarray vdevs; +unsigned int type; };
include/uapi/linux/iommufd.h +22 -0
@@ -53,6 +53,7 @@ enum {IOMMUFD_CMD_FAULT_QUEUE_ALLOC =0x8e ,IOMMUFD_CMD_IOAS_MAP_FILE =0x8f ,IOMMUFD_CMD_VIOMMU_ALLOC =0x90 , + IOMMUFD_CMD_VDEVICE_ALLOC = 0x91, };/**
@@ -864,4 +865,25 @@ struct iommu_viommu_alloc { __u32 out_viommu_id; };#define IOMMU_VIOMMU_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VIOMMU_ALLOC) + +/** +* struct iommu_vdevice_alloc - ioctl(IOMMU_VDEVICE_ALLOC) +* @size: sizeof(struct iommu_vdevice_alloc) +* @viommu_id: vIOMMU ID to associate with the virtual device +* @dev_id: The physical device to allocate a virtual instance on the vIOMMU +* @out_vdevice_id: Object handle for the vDevice. Pass to IOMMU_DESTORY +* @virt_id: Virtual device ID per vIOMMU, e.g. vSID of ARM SMMUv3, vDeviceID +* of AMD IOMMU, and vRID of a nested Intel VT-d to a Context Table +* +* Allocate a virtual device instance (for a physical device) against a vIOMMU. +* This instance holds the device's information (related to its vIOMMU) in a VM. +*/ +struct iommu_vdevice_alloc { +__u32 size; +__u32 viommu_id; +__u32 dev_id; +__u32 out_vdevice_id; +__aligned_u64 virt_id; +}; +#define IOMMU_VDEVICE_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VDEVICE_ALLOC)#endif
drivers/iommu/iommufd/main.c +6 -0
@@ -308,6 +308,7 @@ union ucmd_buffer {struct iommu_option option;struct iommu_vfio_ioas vfio_ioas;struct iommu_viommu_alloc viommu; +struct iommu_vdevice_alloc vdev;#ifdef CONFIG_IOMMUFD_TEST struct iommu_test_cmd test;#endif
@@ -363,6 +364,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = { __reserved),IOCTL_OP (IOMMU_VIOMMU_ALLOC ,iommufd_viommu_alloc_ioctl ,struct iommu_viommu_alloc ,out_viommu_id ), +IOCTL_OP (IOMMU_VDEVICE_ALLOC ,iommufd_vdevice_alloc_ioctl , +struct iommu_vdevice_alloc ,virt_id ), #ifdef CONFIG_IOMMUFD_TEST IOCTL_OP(IOMMU_TEST_CMD, iommufd_test, struct iommu_test_cmd, last), #endif
@@ -501,6 +504,9 @@ static const struct iommufd_object_ops iommufd_object_ops[] = { [IOMMUFD_OBJ_VIOMMU ] = { .destroy = iommufd_viommu_destroy, }, + [IOMMUFD_OBJ_VDEVICE ] = { + .destroy = iommufd_vdevice_destroy, + }, #ifdef CONFIG_IOMMUFD_TEST [IOMMUFD_OBJ_SELFTEST ] = { .destroy = iommufd_selftest_destroy,
drivers/iommu/iommufd/viommu.c +76 -0
91 lines not on this page · raw message
Signature · 1 line
2.43.0
PATCH v7 2/10 iommufd/selftest: Add IOMMU_VDEVICE_ALLOC test coverage
In reply to Nicolin ChenAdd a vdevice_alloc op to the viommu mock_viommu_ops for the coverage of IOMMU_VIOMMU_TYPE_SELFTEST allocations. Then, add a vdevice_alloc TEST_F to cover the IOMMU_VDEVICE_ALLOC ioctl.
--- tools/testing/selftests/iommu/iommufd_utils.h | 27 +++++++++++++++++++ tools/testing/selftests/iommu/iommufd.c | 20 ++++++++++++++ .../selftests/iommu/iommufd_fail_nth.c | 4 +++ 3 files changed, 51 insertions(+)
tools/testing/selftests/iommu/iommufd_utils.h +27 -0
@@ -847,3 +847,30 @@ static int _test_cmd_viommu_alloc(int fd, __u32 device_id, __u32 hwpt_id,EXPECT_ERRNO (_errno, \_test_cmd_viommu_alloc (self->fd, device_id, hwpt_id, \ type, 0, viommu_id)) + +static int _test_cmd_vdevice_alloc (int fd,__u32 viommu_id,__u32 idev_id, +__u64 virt_id,__u32 *vdev_id) +{ +struct iommu_vdevice_alloc cmd = { + .size =sizeof (cmd), + .dev_id = idev_id, + .viommu_id = viommu_id, + .virt_id = virt_id, + }; +int ret; + + ret =ioctl (fd,IOMMU_VDEVICE_ALLOC , &cmd); +if (ret) +return ret; +if (vdev_id) + *vdev_id = cmd.out_vdevice_id; +return 0 ; +} + +#define test_cmd_vdevice_alloc (viommu_id, idev_id, virt_id, vdev_id) \ + ASSERT_EQ(0, _test_cmd_vdevice_alloc(self->fd, viommu_id, idev_id, \ + virt_id, vdev_id)) +#define test_err_vdevice_alloc (_errno, viommu_id, idev_id, virt_id, vdev_id) \ + EXPECT_ERRNO(_errno, \ + _test_cmd_vdevice_alloc(self->fd, viommu_id, idev_id, \ + virt_id, vdev_id))
tools/testing/selftests/iommu/iommufd.c +20 -0
@@ -134,6 +134,7 @@ TEST_F(iommufd, cmd_length)TEST_LENGTH (iommu_vfio_ioas,IOMMU_VFIO_IOAS , __reserved);TEST_LENGTH (iommu_ioas_map_file,IOMMU_IOAS_MAP_FILE , iova);TEST_LENGTH (iommu_viommu_alloc,IOMMU_VIOMMU_ALLOC , out_viommu_id); +TEST_LENGTH (iommu_vdevice_alloc,IOMMU_VDEVICE_ALLOC , virt_id);#undef TEST_LENGTH }
@@ -2617,4 +2618,23 @@ TEST_F(iommufd_viommu, viommu_alloc_nested_iopf) } } +TEST_F(iommufd_viommu, vdevice_alloc) +{ +uint32_t viommu_id = self->viommu_id; +uint32_t dev_id = self->device_id; +uint32_t vdev_id =0 ; + +if (dev_id) { +/* Set vdev_id to 0x99, unset it, and set to 0x88 */ +test_cmd_vdevice_alloc (viommu_id, dev_id,0x99 , &vdev_id); +test_err_vdevice_alloc (EEXIST , viommu_id, dev_id,0x99 , + &vdev_id); +test_ioctl_destroy (vdev_id); +test_cmd_vdevice_alloc (viommu_id, dev_id,0x88 , &vdev_id); +test_ioctl_destroy (vdev_id); + }else { +test_err_vdevice_alloc (ENOENT , viommu_id, dev_id,0x99 ,NULL ); + } +} +TEST_HARNESS_MAIN
tools/testing/selftests/iommu/iommufd_fail_nth.c +4 -0
@@ -622,6 +622,7 @@ TEST_FAIL_NTH(basic_fail_nth, device)uint32_t idev_id;uint32_t hwpt_id;uint32_t viommu_id; +uint32_t vdev_id;__u64 iova; self->fd =open ("/dev/iommu" ,O_RDWR );
@@ -674,6 +675,9 @@ TEST_FAIL_NTH(basic_fail_nth, device) IOMMU_VIOMMU_TYPE_SELFTEST, 0, &viommu_id))return -1 ; +if (_test_cmd_vdevice_alloc (self->fd, viommu_id, idev_id,0 , &vdev_id)) +return -1 ; +return 0 ; }
Signature · 1 line
2.43.0
PATCH v7 3/10 iommu/viommu: Add cache_invalidate to iommufd_viommu_ops
In reply to Nicolin ChenThis per-vIOMMU cache_invalidate op is like the cache_invalidate_user op in struct iommu_domain_ops, but wider, supporting device cache (e.g. PCI ATC invaldiations).
--- include/linux/iommufd.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
include/linux/iommufd.h +10 -0
@@ -16,6 +16,7 @@ struct device;struct file ;struct iommu_group ;struct iommu_user_data ; +struct iommu_user_data_array ;struct iommufd_access ;struct iommufd_ctx ;struct iommufd_device ;
@@ -105,12 +106,21 @@ struct iommufd_viommu {* must be defined in include/uapi/linux/iommufd.h. * It must fully initialize the new iommu_domain before * returning. Upon failure, ERR_PTR must be returned. +* @cache_invalidate: Flush hardware cache used by a vIOMMU. It can be used for +* any IOMMU hardware specific cache: TLB and device cache. +* The @array passes in the cache invalidation requests, in +* form of a driver data structure. A driver must update the +* array->entry_num to report the number of handled requests. +* The data structure of the array entry must be defined in +* include/uapi/linux/iommufd.h */ struct iommufd_viommu_ops {void (*destroy)(struct iommufd_viommu *viommu);struct iommu_domain *(*alloc_domain_nested)(struct iommufd_viommu *viommu,u32 flags,const struct iommu_user_data *user_data); +int (*cache_invalidate)(struct iommufd_viommu *viommu, +struct iommu_user_data_array *array); };#if IS_ENABLED (CONFIG_IOMMUFD )
Signature · 1 line
2.43.0
PATCH v7 4/10 iommufd: Allow hwpt_id to carry viommu_id for IOMMU_HWPT_INVALIDATE
In reply to Nicolin ChenWith a vIOMMU object, use space can flush any IOMMU related cache that can be directed via a vIOMMU object. It is similar to the IOMMU_HWPT_INVALIDATE uAPI, but can cover a wider range than IOTLB, e.g. device/desciprtor cache.
Allow hwpt_id of the iommu_hwpt_invalidate structure to carry a viommu_id, and reuse the IOMMU_HWPT_INVALIDATE uAPI for vIOMMU invalidations. Drivers can define different structures for vIOMMU invalidations v.s. HWPT ones.
Since both the HWPT-based and vIOMMU-based invalidation pathways check own cache invalidation op, remove the WARN_ON_ONCE in the allocator.
Update the uAPI, kdoc, and selftest case accordingly.
--- include/uapi/linux/iommufd.h | 9 ++++-- drivers/iommu/iommufd/hw_pagetable.c | 40 +++++++++++++++++++------ tools/testing/selftests/iommu/iommufd.c | 4 +-- 3 files changed, 39 insertions(+), 14 deletions(-)
include/uapi/linux/iommufd.h +6 -3
@@ -700,7 +700,7 @@ struct iommu_hwpt_vtd_s1_invalidate {/** * struct iommu_hwpt_invalidate - ioctl(IOMMU_HWPT_INVALIDATE) * @size: sizeof(struct iommu_hwpt_invalidate) -* @hwpt_id: ID of a nested HWPT for cache invalidation +* @hwpt_id: ID of a nested HWPT or a vIOMMU, for cache invalidation * @data_uptr: User pointer to an array of driver-specific cache invalidation * data. * @data_type: One of enum iommu_hwpt_invalidate_data_type, defining the data
@@ -711,8 +711,11 @@ struct iommu_hwpt_vtd_s1_invalidate {* Output the number of requests successfully handled by kernel. * @__reserved: Must be 0. * -* Invalidate the iommu cache for user-managed page table. Modifications on a -* user-managed page table should be followed by this operation to sync cache. +* Invalidate iommu cache for user-managed page table or vIOMMU. Modifications +* on a user-managed page table should be followed by this operation, if a HWPT +* is passed in via @hwpt_id. Other caches, such as device cache or descriptor +* cache can be flushed if a vIOMMU is passed in via the @hwpt_id field. +* * Each ioctl can support one or more cache invalidation requests in the array * that has a total size of @entry_len * @entry_num. *
drivers/iommu/iommufd/hw_pagetable.c +31 -9
@@ -251,8 +251,7 @@ iommufd_hwpt_nested_alloc(struct iommufd_ctx *ictx, } hwpt->domain->owner = ops; -if (WARN_ON_ONCE (hwpt->domain->type !=IOMMU_DOMAIN_NESTED || - !hwpt->domain->ops->cache_invalidate_user)) { +if (WARN_ON_ONCE (hwpt->domain->type !=IOMMU_DOMAIN_NESTED )) { rc = -EINVAL ; goto out_abort; }
@@ -483,7 +482,7 @@ int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd) .entry_len = cmd->entry_len, .entry_num = cmd->entry_num, }; -struct iommufd_hw_pagetable *hwpt; +struct iommufd_object *pt_obj;u32 done_num =0 ;int rc;
@@ -497,17 +496,40 @@ int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd) goto out; } - hwpt =iommufd_get_hwpt_nested (ucmd, cmd->hwpt_id); -if (IS_ERR (hwpt)) { - rc =PTR_ERR (hwpt); + pt_obj =iommufd_get_object (ucmd->ictx, cmd->hwpt_id,IOMMUFD_OBJ_ANY ); +if (IS_ERR (pt_obj)) { + rc =PTR_ERR (pt_obj); goto out; } +if (pt_obj->type ==IOMMUFD_OBJ_HWPT_NESTED ) { +struct iommufd_hw_pagetable *hwpt = + container_of(pt_obj, struct iommufd_hw_pagetable, obj); + +if (!hwpt->domain->ops || + !hwpt->domain->ops->cache_invalidate_user) { + rc = -EOPNOTSUPP ; + goto out_put_pt; + } + rc = hwpt->domain->ops->cache_invalidate_user (hwpt->domain, + &data_array); + }else if (pt_obj->type ==IOMMUFD_OBJ_VIOMMU ) { +struct iommufd_viommu *viommu = + container_of(pt_obj, struct iommufd_viommu, obj); + +if (!viommu->ops || !viommu->ops->cache_invalidate) { + rc = -EOPNOTSUPP ; + goto out_put_pt; + } + rc = viommu->ops->cache_invalidate (viommu, &data_array); + }else { + rc = -EINVAL ; + goto out_put_pt; + } - rc = hwpt->domain->ops->cache_invalidate_user (hwpt->domain, - &data_array); done_num = data_array.entry_num; -iommufd_put_object (ucmd->ictx, &hwpt->obj); +out_put_pt: +iommufd_put_object (ucmd->ictx, pt_obj); out: cmd->entry_num = done_num;if (iommufd_ucmd_respond (ucmd,sizeof (*cmd)))
tools/testing/selftests/iommu/iommufd.c +2 -2
@@ -367,9 +367,9 @@ TEST_F(iommufd_ioas, alloc_hwpt_nested)EXPECT_ERRNO (EBUSY ,_test_ioctl_destroy (self->fd, parent_hwpt_id)); -/* hwpt_invalidate only supports a user-managed hwpt (nested) */ +/* hwpt_invalidate does not support a parent hwpt */ num_inv =1 ; -test_err_hwpt_invalidate (ENOENT , parent_hwpt_id, inv_reqs, +test_err_hwpt_invalidate (EINVAL , parent_hwpt_id, inv_reqs,IOMMU_HWPT_INVALIDATE_DATA_SELFTEST ,sizeof (*inv_reqs), &num_inv);assert (!num_inv);
Signature · 1 line
2.43.0
PATCH v7 5/10 iommu: Add iommu_copy_struct_from_full_user_array helper
In reply to Nicolin ChenFrom: Jason Gunthorpe <[email protected]>
The iommu_copy_struct_from_user_array helper can be used to copy a single entry from a user array which might not be efficient if the array is big.
Add a new iommu_copy_struct_from_full_user_array to copy the entire user array at once. Update the existing iommu_copy_struct_from_user_array kdoc accordingly.
--- include/linux/iommu.h | 48 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-)
include/linux/iommu.h +47 -1
@@ -493,7 +493,9 @@ static inline int __iommu_copy_struct_from_user_array(* @index: Index to the location in the array to copy user data from * @min_last: The last member of the data structure @kdst points in the * initial version. -* Return 0 for success, otherwise -error. +* +* Copy a single entry from a user array. Return 0 for success, otherwise +* -error. */ #define iommu_copy_struct_from_user_array (kdst, user_array, data_type, index, \ min_last) \
@@ -501,6 +503,50 @@ static inline int __iommu_copy_struct_from_user_array( kdst, user_array, data_type, index, sizeof(*(kdst)), \ offsetofend(typeof(*(kdst)), min_last)) +/** +* iommu_copy_struct_from_full_user_array - Copy iommu driver specific user +* space data from an iommu_user_data_array +* @kdst: Pointer to an iommu driver specific user data that is defined in +* include/uapi/linux/iommufd.h +* @kdst_entry_size: sizeof(*kdst) +* @user_array: Pointer to a struct iommu_user_data_array for a user space +* array +* @data_type: The data type of the @kdst. Must match with @user_array->type +* +* Copy the entire user array. kdst must have room for kdst_entry_size * +* user_array->entry_num bytes. Return 0 for success, otherwise -error. +*/ +static inline int +iommu_copy_struct_from_full_user_array (void *kdst,size_t kdst_entry_size, +struct iommu_user_data_array *user_array, +unsigned int data_type) +{ +unsigned int i; +int ret; + +if (user_array->type != data_type) +return -EINVAL ; +if (!user_array->entry_num) +return -EINVAL ; +if (likely (user_array->entry_len == kdst_entry_size)) { +if (copy_from_user (kdst, user_array->uptr, + user_array->entry_num * + user_array->entry_len)) +return -EFAULT ; + } + +/* Copy item by item */ +for (i =0 ; i != user_array->entry_num; i++) { + ret =copy_struct_from_user ( + kdst + kdst_entry_size * i, kdst_entry_size, + user_array->uptr + user_array->entry_len * i, + user_array->entry_len); +if (ret) +return ret; + } +return 0 ; +} +/** * struct iommu_ops - iommu ops and capabilities * @capable: check capability
Signature · 1 line
2.43.0
PATCH v7 6/10 iommufd/viommu: Add iommufd_viommu_find_dev helper
In reply to Nicolin ChenThis avoids a bigger trouble of exposing struct iommufd_device and struct iommufd_vdevice in the public header.
--- include/linux/iommufd.h | 8 ++++++++ drivers/iommu/iommufd/driver.c | 13 +++++++++++++ 2 files changed, 21 insertions(+)
include/linux/iommufd.h +8 -0
@@ -185,6 +185,8 @@ static inline int iommufd_vfio_compat_set_no_iommu(struct iommufd_ctx *ictx)struct iommufd_object *_iommufd_object_alloc (struct iommufd_ctx *ictx,size_t size,enum iommufd_object_type type); +struct device *iommufd_viommu_find_dev (struct iommufd_viommu *viommu, +unsigned long vdev_id);#else /* !CONFIG_IOMMUFD_DRIVER_CORE */ static inline struct iommufd_object * _iommufd_object_alloc(struct iommufd_ctx *ictx, size_t size,
@@ -192,6 +194,12 @@ _iommufd_object_alloc(struct iommufd_ctx *ictx, size_t size, {return ERR_PTR (-EOPNOTSUPP ); } + +static inline struct device * +iommufd_viommu_find_dev (struct iommufd_viommu *viommu,unsigned long vdev_id) +{ +return NULL ; +}#endif /* CONFIG_IOMMUFD_DRIVER_CORE */ /*
drivers/iommu/iommufd/driver.c +13 -0
@@ -36,5 +36,18 @@ struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx, }EXPORT_SYMBOL_NS_GPL (_iommufd_object_alloc,IOMMUFD ); +/* Caller should xa_lock(&viommu->vdevs) to protect the return value */ +struct device *iommufd_viommu_find_dev (struct iommufd_viommu *viommu, +unsigned long vdev_id) +{ +struct iommufd_vdevice *vdev; + +lockdep_assert_held (&viommu->vdevs.xa_lock); + + vdev =xa_load (&viommu->vdevs, vdev_id); +return vdev ? vdev->dev :NULL ; +} +EXPORT_SYMBOL_NS_GPL (iommufd_viommu_find_dev,IOMMUFD ); +MODULE_DESCRIPTION ("iommufd code shared with builtin modules" );MODULE_LICENSE ("GPL" );
Signature · 1 line
2.43.0
PATCH v7 7/10 iommufd/selftest: Add mock_viommu_cache_invalidate
In reply to Nicolin ChenSimilar to the coverage of cache_invalidate_user for iotlb invalidation, add a device cache and a viommu_cache_invalidate function to test it out.
--- drivers/iommu/iommufd/iommufd_test.h | 25 +++++++++ drivers/iommu/iommufd/selftest.c | 76 +++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 1 deletion(-)
drivers/iommu/iommufd/iommufd_test.h +25 -0
@@ -54,6 +54,11 @@ enum { MOCK_NESTED_DOMAIN_IOTLB_NUM = 4, }; +enum { +MOCK_DEV_CACHE_ID_MAX =3 , +MOCK_DEV_CACHE_NUM =4 , +}; +struct iommu_test_cmd {__u32 size;__u32 op;
@@ -152,6 +157,7 @@ struct iommu_test_hw_info {/* Should not be equal to any defined value in enum iommu_hwpt_data_type */ #define IOMMU_HWPT_DATA_SELFTEST 0xdead#define IOMMU_TEST_IOTLB_DEFAULT 0xbadbeef +#define IOMMU_TEST_DEV_CACHE_DEFAULT 0xbaddad/** * struct iommu_hwpt_selftest
@@ -182,4 +188,23 @@ struct iommu_hwpt_invalidate_selftest {#define IOMMU_VIOMMU_TYPE_SELFTEST 0xdeadbeef +/* Should not be equal to any defined value in enum iommu_viommu_invalidate_data_type */ +#define IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST 0xdeadbeef +#define IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST_INVALID 0xdadbeef + +/** +* struct iommu_viommu_invalidate_selftest - Invalidation data for Mock VIOMMU +* (IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST) +* @flags: Invalidate flags +* @cache_id: Invalidate cache entry index +* +* If IOMMU_TEST_INVALIDATE_ALL is set in @flags, @cache_id will be ignored +*/ +struct iommu_viommu_invalidate_selftest { +#define IOMMU_TEST_INVALIDATE_FLAG_ALL (1 << 0) +__u32 flags; +__u32 vdev_id; +__u32 cache_id; +}; +#endif
drivers/iommu/iommufd/selftest.c +75 -1
103 lines not on this page · raw message
Signature · 1 line
2.43.0
PATCH v7 8/10 iommufd/selftest: Add IOMMU_TEST_OP_DEV_CHECK_CACHE test command
In reply to Nicolin ChenSimilar to IOMMU_TEST_OP_MD_CHECK_IOTLB verifying a mock_domain's iotlb, IOMMU_TEST_OP_DEV_CHECK_CACHE will be used to verify a mock_dev's cache.
--- drivers/iommu/iommufd/iommufd_test.h | 5 ++++ tools/testing/selftests/iommu/iommufd_utils.h | 24 +++++++++++++++++++ drivers/iommu/iommufd/selftest.c | 22 +++++++++++++++++ tools/testing/selftests/iommu/iommufd.c | 7 +++++- 4 files changed, 57 insertions(+), 1 deletion(-)
drivers/iommu/iommufd/iommufd_test.h +5 -0
@@ -23,6 +23,7 @@ enum {IOMMU_TEST_OP_DIRTY ,IOMMU_TEST_OP_MD_CHECK_IOTLB ,IOMMU_TEST_OP_TRIGGER_IOPF , + IOMMU_TEST_OP_DEV_CHECK_CACHE, }; enum {
@@ -140,6 +141,10 @@ struct iommu_test_cmd {__u32 perm;__u64 addr; } trigger_iopf; +struct { +__u32 id; +__u32 cache; + } check_dev_cache; };__u32 last; };
tools/testing/selftests/iommu/iommufd_utils.h +24 -0
@@ -250,6 +250,30 @@ static int _test_cmd_hwpt_alloc(int fd, __u32 device_id, __u32 pt_id, __u32 ft_itest_cmd_hwpt_check_iotlb (hwpt_id, i, expected); \ }) +#define test_cmd_dev_check_cache (device_id, cache_id, expected) \ + ({ \ + struct iommu_test_cmd test_cmd = { \ + .size = sizeof(test_cmd), \ + .op = IOMMU_TEST_OP_DEV_CHECK_CACHE, \ + .id = device_id, \ + .check_dev_cache = { \ + .id = cache_id, \ + .cache = expected, \ + }, \ + }; \ + ASSERT_EQ(0, ioctl(self->fd, \ + _IOMMU_TEST_CMD( \ + IOMMU_TEST_OP_DEV_CHECK_CACHE), \ + &test_cmd)); \ + }) + +#define test_cmd_dev_check_cache_all (device_id, expected) \ + ({ \ + int c; \ + for (c = 0; c < MOCK_DEV_CACHE_NUM; c++) \ + test_cmd_dev_check_cache(device_id, c, expected); \ + }) +static int _test_cmd_hwpt_invalidate (int fd,__u32 hwpt_id,void *reqs,uint32_t data_type,uint32_t lreq,uint32_t *nreqs)
drivers/iommu/iommufd/selftest.c +22 -0
@@ -1125,6 +1125,24 @@ static int iommufd_test_md_check_iotlb(struct iommufd_ucmd *ucmd,return rc; } +static int iommufd_test_dev_check_cache (struct iommufd_ucmd *ucmd,u32 idev_id, +unsigned int cache_id,u32 cache) +{ +struct iommufd_device *idev; +struct mock_dev *mdev; +int rc =0 ; + + idev =iommufd_get_device (ucmd, idev_id); +if (IS_ERR (idev)) +return PTR_ERR (idev); + mdev = container_of(idev->dev, struct mock_dev, dev); + +if (cache_id >MOCK_DEV_CACHE_ID_MAX || mdev->cache[cache_id] != cache) + rc = -EINVAL ; +iommufd_put_object (ucmd->ictx, &idev->obj); +return rc; +} +struct selftest_access {struct iommufd_access *access;struct file *file;
@@ -1634,6 +1652,10 @@ int iommufd_test(struct iommufd_ucmd *ucmd)return iommufd_test_md_check_iotlb (ucmd, cmd->id, cmd->check_iotlb.id, cmd->check_iotlb.iotlb); +case IOMMU_TEST_OP_DEV_CHECK_CACHE : +return iommufd_test_dev_check_cache (ucmd, cmd->id, + cmd->check_dev_cache.id, + cmd->check_dev_cache.cache);case IOMMU_TEST_OP_CREATE_ACCESS :return iommufd_test_create_access (ucmd, cmd->id, cmd->create_access.flags);
tools/testing/selftests/iommu/iommufd.c +6 -1
@@ -227,6 +227,8 @@ FIXTURE_SETUP(iommufd_ioas)for (i =0 ; i != variant->mock_domains; i++) {test_cmd_mock_domain (self->ioas_id, &self->stdev_id, &self->hwpt_id, &self->device_id); +test_cmd_dev_check_cache_all (self->device_id, +IOMMU_TEST_DEV_CACHE_DEFAULT ); self->base_iova =MOCK_APERTURE_START ; } }
@@ -1392,9 +1394,12 @@ FIXTURE_SETUP(iommufd_mock_domain)ASSERT_GE (ARRAY_SIZE (self->hwpt_ids), variant->mock_domains); -for (i =0 ; i != variant->mock_domains; i++) +for (i =0 ; i != variant->mock_domains; i++) {test_cmd_mock_domain (self->ioas_id, &self->stdev_ids[i], &self->hwpt_ids[i], &self->idev_ids[i]); +test_cmd_dev_check_cache_all (self->idev_ids[0 ], +IOMMU_TEST_DEV_CACHE_DEFAULT ); + } self->hwpt_id = self->hwpt_ids[0 ]; self->mmap_flags =MAP_SHARED |MAP_ANONYMOUS ;
Signature · 1 line
2.43.0
PATCH v7 9/10 iommufd/selftest: Add vIOMMU coverage for IOMMU_HWPT_INVALIDATE ioctl
In reply to Nicolin ChenAdd a viommu_cache test function to cover vIOMMU invalidations using the updated IOMMU_HWPT_INVALIDATE ioctl, which now allows passing in a vIOMMU via its hwpt_id field.
--- tools/testing/selftests/iommu/iommufd_utils.h | 32 ++++ tools/testing/selftests/iommu/iommufd.c | 173 ++++++++++++++++++ 2 files changed, 205 insertions(+)
tools/testing/selftests/iommu/iommufd_utils.h +32 -0
@@ -305,6 +305,38 @@ static int _test_cmd_hwpt_invalidate(int fd, __u32 hwpt_id, void *reqs, data_type, lreq, nreqs)); \ }) +static int _test_cmd_viommu_invalidate (int fd,__u32 viommu_id,void *reqs, +uint32_t data_type,uint32_t lreq, +uint32_t *nreqs) +{ +struct iommu_hwpt_invalidate cmd = { + .size =sizeof (cmd), + .hwpt_id = viommu_id, + .data_type = data_type, + .data_uptr = (uint64_t )reqs, + .entry_len = lreq, + .entry_num = *nreqs, + }; +int rc =ioctl (fd,IOMMU_HWPT_INVALIDATE , &cmd); + *nreqs = cmd.entry_num; +return rc; +} + +#define test_cmd_viommu_invalidate (viommu, reqs, lreq, nreqs) \ + ({ \ + ASSERT_EQ(0, \ + _test_cmd_viommu_invalidate(self->fd, viommu, reqs, \ + IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST, \ + lreq, nreqs)); \ + }) +#define test_err_viommu_invalidate (_errno, viommu_id, reqs, data_type, lreq, \ + nreqs) \ + ({ \ + EXPECT_ERRNO(_errno, _test_cmd_viommu_invalidate( \ + self->fd, viommu_id, reqs, \ + data_type, lreq, nreqs)); \ + }) +static int _test_cmd_access_replace_ioas (int fd,__u32 access_id,unsigned int ioas_id) {
tools/testing/selftests/iommu/iommufd.c +173 -0
177 lines not on this page · raw message
Signature · 1 line
2.43.0
PATCH v7 10/10 Documentation: userspace-api: iommufd: Update vDEVICE
In reply to Nicolin ChenWith the introduction of the new object and its infrastructure, update the doc and the vIOMMU graph to reflect that.
--- Documentation/userspace-api/iommufd.rst | 41 +++++++++++++++++++------ 1 file changed, 32 insertions(+), 9 deletions(-)
Documentation/userspace-api/iommufd.rst +32 -9
@@ -94,6 +94,19 @@ Following IOMMUFD objects are exposed to userspace: backed by corresponding vIOMMU objects, in which case a guest OS would do the "dispatch" naturally instead of VMM trappings. +- IOMMUFD_OBJ_VDEVICE, representing a virtual device for an IOMMUFD_OBJ_DEVICE + against an IOMMUFD_OBJ_VIOMMU. This virtual device holds the device's virtual + information or attributes (related to the vIOMMU) in a VM. An immediate vDATA + example can be the virtual ID of the device on a vIOMMU, which is a unique ID + that VMM assigns to the device for a translation channel/port of the vIOMMU, + e.g. vSID of ARM SMMUv3, vDeviceID of AMD IOMMU, and vRID of Intel VT-d to a + Context Table. Potential use cases of some advanced security information can + be forwarded via this object too, such as security level or realm information + in a Confidential Compute Architecture. A VMM should create a vDEVICE object + to forward all the device information in a VM, when it connects a device to a + vIOMMU, which is a separate ioctl call from attaching the same device to an + HWPT_PAGING that the vIOMMU holds. + All user-visible objects are destroyed via the IOMMU_DESTROY uAPI. The diagrams below show relationships between user-visible objects and kernel
@@ -133,16 +146,16 @@ creating the objects and links:: |____________| |____________| |______| _______________________________________________________________________ - | iommufd (with vIOMMU) | + | iommufd (with vIOMMU/vDEVICE) | | | - | [5] | - | _____________ | - | | | | - | |----------------| vIOMMU | | - | | | | | - | | | | | - | | [1] | | [4] [2] | - | | ______ | | _____________ ________ | + | [5] [6] | + | _____________ _____________ | + | | | | | | + | |----------------| vIOMMU |<---| vDEVICE |<----| | + | | | | |_____________| | | + | | | | | | + | | [1] | | [4] | [2] | + | | ______ | | _____________ _|______ | | | | | | [3] | | | | | | | | | IOAS |<---|(HWPT_PAGING)|<---| HWPT_NESTED |<--| DEVICE | | | | |______| |_____________| |_____________| |________| |
@@ -215,6 +228,15 @@ creating the objects and links:: the vIOMMU object and the HWPT_PAGING, then this vIOMMU object can be used as a nesting parent object to allocate an HWPT_NESTED object described above. +6. IOMMUFD_OBJ_VDEVICE can be only manually created via the IOMMU_VDEVICE_ALLOC + uAPI, provided a viommu_id for an iommufd_viommu object and a dev_id for an + iommufd_device object. The vDEVICE object will be the binding between these + two parent objects. Another @virt_id will be also set via the uAPI providing + the iommufd core an index to store the vDEVICE object to a vDEVICE array per + vIOMMU. If necessary, the IOMMU driver may choose to implement a vdevce_alloc + op to init its HW for virtualization feature related to a vDEVICE. Successful + completion of this operation sets up the linkages between vIOMMU and device. + A device can only bind to an iommufd due to DMA ownership claim and attach to at most one IOAS object (no support of PASID yet).
@@ -228,6 +250,7 @@ User visible objects are backed by following datastructures: - iommufd_hwpt_paging for IOMMUFD_OBJ_HWPT_PAGING. - iommufd_hwpt_nested for IOMMUFD_OBJ_HWPT_NESTED. - iommufd_viommu for IOMMUFD_OBJ_VIOMMU. +- iommufd_vdevice for IOMMUFD_OBJ_VDEVICE. Several terminologies when looking at these datastructures:
Signature · 1 line
2.43.0
Re: [PATCH v7 10/10] Documentation: userspace-api: iommufd: Update vDEVICE
Quoted 3 lines · Nicolin Chen
With the introduction of the new object and its infrastructure, update the doc and the vIOMMU graph to reflect that.
The docs LGTM, thanks!
Signature · 1 line
An old man doll... just what I always wanted! - Clara
Quoted 13 lines · Nicolin Chen
Jason Gunthorpe (1):
iommu: Addiommu_copy_struct_from_full_user_arrayhelperNicolin Chen (9):
iommufd/viommu: AddIOMMUFD_OBJ_VDEVICEandIOMMU_VDEVICE_ALLOCioctl iommufd/selftest: AddIOMMU_VDEVICE_ALLOCtest coverage iommu/viommu: Addcache_invalidatetoiommufd_viommu_ops
iommufd: Allowhwpt_idto carryviommu_idforIOMMU_HWPT_INVALIDATEiommufd/viommu: Addiommufd_viommu_find_devhelper iommufd/selftest: Addmock_viommu_cache_invalidateiommufd/selftest: AddIOMMU_TEST_OP_DEV_CHECK_CACHEtest command iommufd/selftest: Add vIOMMU coverage forIOMMU_HWPT_INVALIDATEioctl
Documentation: userspace-api: iommufd: Update vDEVICE
Applied to iommufd for-next
Thanks,
Jason
Quoted 15 lines · Jason Gunthorpe
Quoted 13 lines · Nicolin Chen
Jason Gunthorpe (1):
iommu: Addiommu_copy_struct_from_full_user_arrayhelperNicolin Chen (9):
iommufd/viommu: AddIOMMUFD_OBJ_VDEVICEandIOMMU_VDEVICE_ALLOCioctl iommufd/selftest: AddIOMMU_VDEVICE_ALLOCtest coverage iommu/viommu: Addcache_invalidatetoiommufd_viommu_ops
iommufd: Allowhwpt_idto carryviommu_idforIOMMU_HWPT_INVALIDATEiommufd/viommu: Addiommufd_viommu_find_devhelper iommufd/selftest: Addmock_viommu_cache_invalidateiommufd/selftest: AddIOMMU_TEST_OP_DEV_CHECK_CACHEtest command iommufd/selftest: Add vIOMMU coverage forIOMMU_HWPT_INVALIDATEioctl
Documentation: userspace-api: iommufd: Update vDEVICE
Quoted 1 line
Applied to iommufd for-next
Is QEMU support coming soon, or any WIP patches to try? I am trying d0375b6e85be5c71 from Nicolin's wip/for_iommufd_veventq-v9 for now. Thanks,
Quoted 3 lines
Thanks,
Jason
Signature · 1 line
Alexey
Quoted 16 lines · Alexey Kardashevskiy
Quoted 15 lines · Jason Gunthorpe
Quoted 13 lines · Nicolin Chen
Jason Gunthorpe (1):
iommu: Addiommu_copy_struct_from_full_user_arrayhelperNicolin Chen (9):
iommufd/viommu: AddIOMMUFD_OBJ_VDEVICEandIOMMU_VDEVICE_ALLOCioctl iommufd/selftest: AddIOMMU_VDEVICE_ALLOCtest coverage iommu/viommu: Addcache_invalidatetoiommufd_viommu_ops
iommufd: Allowhwpt_idto carryviommu_idforIOMMU_HWPT_INVALIDATEiommufd/viommu: Addiommufd_viommu_find_devhelper iommufd/selftest: Addmock_viommu_cache_invalidateiommufd/selftest: AddIOMMU_TEST_OP_DEV_CHECK_CACHEtest command iommufd/selftest: Add vIOMMU coverage forIOMMU_HWPT_INVALIDATEioctl
Documentation: userspace-api: iommufd: Update vDEVICE
Quoted 4 lines
Quoted 1 line
Applied to iommufd for-next
Is QEMU support coming soon, or any WIP patches to try? I am trying
d0375b6e85be5c71from Nicolin's wip/for_iommufd_veventq-v9 for now. Thanks,
Shameer sent RFCv2 recently:
https://lore.kernel.org/qemu-devel/[email protected]/
Thanks
Nicolin