iommufd: Add vIOMMU infrastructure (Part-4 HW QUEUE)
Subsystems (7)
v4 cover iommufd: Add vIOMMU infrastructure (Part-4 HW QUEUE)
The vIOMMU object is designed to represent a slice of an IOMMU HW for its virtualization features shared with or passed to user space (a VM mostly) in a way of HW acceleration. This extended the HWPT-based design for more advanced virtualization feature.
HW QUEUE introduced by this series as a part of the vIOMMU infrastructure represents a HW accelerated queue/buffer for VM to use exclusively, e.g.
- NVIDIA's Virtual Command Queue
- AMD vIOMMU's Command Buffer, Event Log Buffer, and PPR Log Buffer
each of which allows its IOMMU HW to directly access a queue memory owned by a guest VM and allows a guest OS to control the HW queue direclty, to avoid VM Exit overheads to improve the performance.
Introduce IOMMUFD_OBJ_HW_QUEUE and its pairing IOMMUFD_CMD_HW_QUEUE_ALLOC allowing VMM to forward the IOMMU-specific queue info, such as queue base address, size, and etc.
Meanwhile, a guest-owned queue needs the guest kernel to control the queue by reading/writing its consumer and producer indexes, via MMIO acceses to the hardware MMIO registers. Introduce an mmap infrastructure for iommufd to support passing through a piece of MMIO region from the host physical address space to the guest physical address space. The mmap info (offset/ length) used by an mmap syscall must be pre-allocated and returned to the user space via an output driver-data during an IOMMUFD_CMD_HW_QUEUE_ALLOC call. Thus, it requires a driver-specific user data support in the vIOMMU allocation flow.
As a real-world use case, this series implements a HW QUEUE support in the tegra241-cmdqv driver for VCMDQs on NVIDIA Grace CPU. In another word, it is also the Tegra CMDQV series Part-2 (user-space support), reworked from Previous RFCv1:
https://lore.kernel.org/all/[email protected]/
This enables the HW accelerated feature for NVIDIA Grace CPU. Compared to the standard SMMUv3 operating in the nested translation mode trapping CMDQ for TLBI and ATC_INV commands, this gives a huge performance improvement:
70% to 90% reductions of invalidation time were measured by various DMA unmap tests running in a guest OS.
// Unmap latencies from "dma_map_benchmark -g @granule -t @threads", // by toggling "/sys/kernel/debug/iommu/tegra241_cmdqv/bypass_vcmdq" @granule | @threads | bypass_vcmdq=1 | bypass_vcmdq=0
4KB 1 35.7 us 5.3 us
16KB 1 41.8 us 6.8 us
64KB 1 68.9 us 9.9 us
128KB 1 109.0 us 12.6 us
256KB 1 187.1 us 18.0 us
4KB 2 96.9 us 6.8 us
16KB 2 97.8 us 7.5 us
64KB 2 151.5 us 10.7 us
128KB 2 257.8 us 12.7 us
256KB 2 443.0 us 17.9 usThis is on Github:
https://github.com/nicolinc/iommufd/commits/iommufd_hw_queue-v4
Paring QEMU branch for testing:
https://github.com/nicolinc/qemu/commits/wip/for_iommufd_hw_queue-v4
Changelog
v4
* Rebase on v6.15-rc5
* Add Reviewed-by from Vasant
* Rename "vQUEUE" to "HW QUEUE"
* Use "offset" and "length" for all mmap-related variables
* [iommufd] Use u64 for guest PA
* [iommufd] Fix typo in uAPI doc
* [iommufd] Rename immap_id to offset
* [iommufd] Drop the partial-size mmap support
* [iommufd] Do not replace WARN_ON with WARN_ON_ONCE
* [iommufd] Use "u64 base_addr" for queue base address
* [iommufd] Use u64 base_pfn/num_pfns for immap structure
* [iommufd] Correct the size passed in to mtree_alloc_range()
* [iommufd] Add IOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PA to viommu_ops
v3
https://lore.kernel.org/all/[email protected]/
* Add Reviewed-by from Baolu, Pranjal, and Alok
* Revise kdocs, uAPI docs, and commit logs
* Rename "vCMDQ" back to "vQUEUE" for AMD cases
* [tegra] Add tegra241_vcmdq_hw_flush_timeout()
* [tegra] Rename vsmmu_alloc to alloc_vintf_user
* [tegra] Use writel for SID replacement registers
* [tegra] Move mmap removal call to vsmmu_destroy op
* [tegra] Fix revert in tegra241_vintf_alloc_lvcmdq_user()
* [iommufd] Replace "& ~PAGE_MASK" with PAGE_ALIGNED()
* [iommufd] Add an object-type "owner" to immap structure
* [iommufd] Drop the ictx input in the new for-driver APIs
* [iommufd] Add iommufd_vma_ops to keep track of mmap lifecycle
* [iommufd] Add viommu-based iommufd_viommu_alloc/destroy_mmap helpers
* [iommufd] Rename iommufd_ctx_alloc/free_mmap to _iommufd_alloc/destroy_mmap
v2
https://lore.kernel.org/all/[email protected]/
* Add Reviewed-by from Jason
* [smmu] Fix vsmmu initial value
* [smmu] Support impl for hw_info
* [tegra] Rename "slot" to "vsid"
* [tegra] Update kdocs and commit logs
* [tegra] Map/unmap LVCMDQ dynamically
* [tegra] Refcount the previous LVCMDQ
* [tegra] Return -EEXIST if LVCMDQ exists
* [tegra] Simplify VINTF cleanup routine
* [tegra] Use vmid and s2_domain in vsmmu
* [tegra] Rename "mmap_pgoff" to "immap_id"
* [tegra] Add more addr and length validation
* [iommufd] Add more narrative to mmap's kdoc
* [iommufd] Add iommufd_struct_depend/undepend()
* [iommufd] Rename vcmdq_free op to vcmdq_destroy
* [iommufd] Fix bug in iommu_copy_struct_to_user()
* [iommufd] Drop is_io from iommufd_ctx_alloc_mmap()
* [iommufd] Test the queue memory for its contiguity
* [iommufd] Return -ENXIO if address or length fails
* [iommufd] Do not change @min_last in mock_viommu_alloc()
* [iommufd] Generalize TEGRA241_VCMDQ data in core structure
* [iommufd] Add selftest coverage for IOMMUFD_CMD_VCMDQ_ALLOC
* [iommufd] Add iopt_pin_pages() to prevent queue memory from unmapping
v1
https://lore.kernel.org/all/[email protected]/
Thanks
Nicolin
Nicolin Chen (23):
iommufd/viommu: Add driver-allocated vDEVICE support
iommu: Pass in a driver-level user data structure to viommu_alloc op iommufd/viommu: Allow driver-specific user data for a vIOMMU object
iommu: Add iommu_copy_struct_to_user helper iommufd/driver: Let iommufd_viommu_alloc helper save ictx to
viommu->ictx
iommufd/driver: Add iommufd_struct_destroy to revert
iommufd_viommu_alloc
iommufd/selftest: Support user_data in mock_viommu_alloc
iommufd/selftest: Add covearge for viommu data
iommufd: Abstract iopt_pin_pages and iopt_unpin_pages helpers
iommufd/viommu: Introduce IOMMUFD_OBJ_HW_QUEUE and its related struct
iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
iommufd/driver: Add iommufd_hw_queue_depend/undepend() helpers
iommufd/selftest: Add coverage for IOMMUFD_CMD_HW_QUEUE_ALLOC
iommufd: Add mmap interface
iommufd/selftest: Add coverage for the new mmap interface
Documentation: userspace-api: iommufd: Update HW QUEUE
iommu/arm-smmu-v3-iommufd: Add vsmmu_alloc impl op
iommu/arm-smmu-v3-iommufd: Support implementation-defined hw_info
iommu/tegra241-cmdqv: Use request_threaded_irq
iommu/tegra241-cmdqv: Simplify deinit flow in
tegra241_cmdqv_remove_vintf() iommu/tegra241-cmdqv: Do not statically map LVCMDQs iommu/tegra241-cmdqv: Add user-space use support iommu/tegra241-cmdqv: Add IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV support
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 25 +- drivers/iommu/iommufd/io_pagetable.h | 8 + drivers/iommu/iommufd/iommufd_private.h | 28 +- drivers/iommu/iommufd/iommufd_test.h | 20 + include/linux/iommu.h | 43 +- include/linux/iommufd.h | 186 ++++++- include/uapi/linux/iommufd.h | 116 ++++- tools/testing/selftests/iommu/iommufd_utils.h | 52 +- .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 43 +- .../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 490 +++++++++++++++++- drivers/iommu/iommufd/device.c | 117 +---- drivers/iommu/iommufd/driver.c | 94 ++++ drivers/iommu/iommufd/io_pagetable.c | 95 ++++ drivers/iommu/iommufd/main.c | 80 ++- drivers/iommu/iommufd/selftest.c | 133 ++++- drivers/iommu/iommufd/viommu.c | 121 ++++- tools/testing/selftests/iommu/iommufd.c | 97 +++- .../selftests/iommu/iommufd_fail_nth.c | 11 +- Documentation/userspace-api/iommufd.rst | 12 + 19 files changed, 1577 insertions(+), 194 deletions(-)
base-commit: 92a09c47464d040866cf2b4cd052bc60555185fb
Signature · 1 line
2.43.0
PATCH v4 1/23 iommufd/viommu: Add driver-allocated vDEVICE support
To allow IOMMU drivers to allocate own vDEVICE structures, move the struct iommufd_vdevice to the public header and provide a pair of viommu ops.
The iommufd_vdevice_alloc_ioctl will prioritize the callback function from the viommu ops, i.e. a driver-allocated vDEVICE.
--- drivers/iommu/iommufd/iommufd_private.h | 8 ------ include/linux/iommufd.h | 38 +++++++++++++++++++++++++ drivers/iommu/iommufd/viommu.c | 9 +++++- 3 files changed, 46 insertions(+), 9 deletions(-)
drivers/iommu/iommufd/iommufd_private.h +0 -8
@@ -607,14 +607,6 @@ 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);void iommufd_selftest_destroy (struct iommufd_object *obj);
include/linux/iommufd.h +38 -0
@@ -104,6 +104,14 @@ struct iommufd_viommu {unsigned int type; }; +struct iommufd_vdevice { +struct iommufd_object obj; +struct iommufd_ctx *ictx; +struct iommufd_viommu *viommu; +struct device *dev; +u64 id;/* per-vIOMMU virtual ID */ +}; +/** * struct iommufd_viommu_ops - vIOMMU specific operations * @destroy: Clean up all driver-specific parts of an iommufd_viommu. The memory
@@ -120,6 +128,15 @@ struct iommufd_viommu {* 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 +* @vdevice_alloc: Allocate a vDEVICE object and init its driver-level structure +* or HW procedure. Note that the core-level structure is filled +* by the iommufd core after calling this op. @virt_id carries a +* per-vIOMMU virtual ID (refer to struct iommu_vdevice_alloc in +* include/uapi/linux/iommufd.h) for the driver to initialize HW +* for an attached physical device. +* @vdevice_destroy: Clean up all driver-specific parts of an iommufd_vdevice. +* The memory of the vDEVICE will be free-ed by iommufd core +* after calling this op */ struct iommufd_viommu_ops {void (*destroy)(struct iommufd_viommu *viommu);
@@ -128,6 +145,10 @@ struct iommufd_viommu_ops { const struct iommu_user_data *user_data);int (*cache_invalidate)(struct iommufd_viommu *viommu,struct iommu_user_data_array *array); +struct iommufd_vdevice *(*vdevice_alloc)(struct iommufd_viommu *viommu, +struct device *dev, +u64 virt_id); +void (*vdevice_destroy)(struct iommufd_vdevice *vdev); };#if IS_ENABLED (CONFIG_IOMMUFD )
@@ -245,4 +266,21 @@ static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, ret->member.ops = viommu_ops; \ ret; \ }) + +#define iommufd_vdevice_alloc (viommu, drv_struct, member) \ + ({ \ + drv_struct *ret; \ + \ + static_assert(__same_type(struct iommufd_viommu, *viommu)); \ + static_assert(__same_type(struct iommufd_vdevice, \ + ((drv_struct *)NULL)->member)); \ + static_assert(offsetof(drv_struct, member.obj) == 0); \ + ret = (drv_struct *)_iommufd_object_alloc( \ + viommu->ictx, sizeof(drv_struct), IOMMUFD_OBJ_VDEVICE);\ + if (!IS_ERR(ret)) { \ + ret->member.viommu = viommu; \ + ret->member.ictx = viommu->ictx; \ + } \ + ret; \ + })#endif
drivers/iommu/iommufd/viommu.c +8 -1
@@ -90,6 +90,9 @@ void iommufd_vdevice_destroy(struct iommufd_object *obj) container_of(obj, struct iommufd_vdevice, obj);struct iommufd_viommu *viommu = vdev->viommu; +if (viommu->ops && viommu->ops->vdevice_destroy) + viommu->ops->vdevice_destroy (vdev); +/* xa_cmpxchg is okay to fail if alloc failed xa_cmpxchg previously */ xa_cmpxchg (&viommu->vdevs, vdev->id, vdev,NULL ,GFP_KERNEL );refcount_dec (&viommu->obj.users);
@@ -124,7 +127,11 @@ int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) goto out_put_idev; } - vdev =iommufd_object_alloc (ucmd->ictx, vdev,IOMMUFD_OBJ_VDEVICE ); +if (viommu->ops && viommu->ops->vdevice_alloc) + vdev = viommu->ops->vdevice_alloc (viommu, idev->dev, virt_id); +else + vdev =iommufd_object_alloc (ucmd->ictx, vdev, +IOMMUFD_OBJ_VDEVICE );if (IS_ERR (vdev)) { rc =PTR_ERR (vdev); goto out_put_idev;
Signature · 1 line
2.43.0
RE: [PATCH v4 01/23] iommufd/viommu: Add driver-allocated vDEVICE support
Quoted 17 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:02 AMinclude/linux/iommufd.h +6
-0+ +#define iommufd_vdevice_alloc (viommu, drv_struct, member) \ + ({ \ + drv_struct *ret; \ + \ + static_assert(__same_type(struct iommufd_viommu,*viommu)); \
(quoted diff) +2
-0+ static_assert(__same_type(struct iommufd_vdevice, \ + ((drv_struct *)NULL)->member));\
include/linux/iommufd.h +3
-0+ static_assert(offsetof(drv_struct, member.obj) == 0); \ + ret = (drv_struct *)_iommufd_object_alloc( \ + viommu->ictx, sizeof(drv_struct),IOMMUFD_OBJ_VDEVICE);\
include/linux/iommufd.h static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, · line 285
Quoted 6 lines
include/linux/iommufd.h +6
-0+if (!IS_ERR (ret)) { \ + ret->member.viommu = viommu; \ + ret->member.ictx = viommu->ictx; \ + } \ + ret; \ + })
viommu->ictx is set in patch5, so logically this is better put after that.
Re: [PATCH v4 01/23] iommufd/viommu: Add driver-allocated vDEVICE support
Quoted 27 lines · Kevin
Quoted 17 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:02 AMinclude/linux/iommufd.h +6
-0+ +#define iommufd_vdevice_alloc (viommu, drv_struct, member) \ + ({ \ + drv_struct *ret; \ + \ + static_assert(__same_type(struct iommufd_viommu,*viommu)); \
(quoted diff) +2
-0+ static_assert(__same_type(struct iommufd_vdevice, \ + ((drv_struct *)NULL)->member));\
include/linux/iommufd.h +3
-0+ static_assert(offsetof(drv_struct, member.obj) == 0); \ + ret = (drv_struct *)_iommufd_object_alloc( \ + viommu->ictx, sizeof(drv_struct),IOMMUFD_OBJ_VDEVICE);\
include/linux/iommufd.h static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, · line 285
Quoted 6 lines
include/linux/iommufd.h +6
-0+if (!IS_ERR (ret)) { \ + ret->member.viommu = viommu; \ + ret->member.ictx = viommu->ictx; \ + } \ + ret; \ + })
viommu->ictxis set in patch5, so logically this is better put after that.
Quoted 1 line
Reviewed-by Kevin Tian <[email protected]>
OK. Will rearrange a bit.
Thanks
Nicolin
PATCH v4 2/23 iommu: Pass in a driver-level user data structure to viommu_alloc op
In reply to Nicolin ChenThe new type of vIOMMU for tegra241-cmdqv allows user space VM to use one of its virtual command queue HW resources exclusively. This requires user space to mmap the corresponding MMIO page from kernel space for direct HW control.
To forward the mmap info (offset and length), iommufd should add a driver specific data structure to the IOMMUFD_CMD_VIOMMU_ALLOC ioctl, for driver to output the info during the allocation back to user space.
Similar to the existing ioctls and their IOMMU handlers, add a user_data to viommu_alloc op to bridge between iommufd and drivers.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 3 ++- include/linux/iommu.h | 3 ++- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 3 ++- drivers/iommu/iommufd/selftest.c | 8 ++++---- drivers/iommu/iommufd/viommu.c | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +2 -1
@@ -1062,7 +1062,8 @@ void *arm_smmu_hw_info(struct device *dev, u32 *length, u32 *type);struct iommufd_viommu *arm_vsmmu_alloc (struct device *dev,struct iommu_domain *parent,struct iommufd_ctx *ictx, -unsigned int viommu_type); +unsigned int viommu_type, +const struct iommu_user_data *user_data);int arm_smmu_attach_prepare_vmaster (struct arm_smmu_attach_state *state,struct arm_smmu_nested_domain *nested_domain);void arm_smmu_attach_commit_vmaster (struct arm_smmu_attach_state *state);
include/linux/iommu.h +2 -1
@@ -662,7 +662,8 @@ struct iommu_ops {struct iommufd_viommu *(*viommu_alloc)(struct device *dev,struct iommu_domain *parent_domain, -struct iommufd_ctx *ictx,unsigned int viommu_type); +struct iommufd_ctx *ictx,unsigned int viommu_type, +const struct iommu_user_data *user_data);const struct iommu_domain_ops *default_domain_ops;unsigned long pgsize_bitmap;
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +2 -1
@@ -385,7 +385,8 @@ static const struct iommufd_viommu_ops arm_vsmmu_ops = {struct iommufd_viommu *arm_vsmmu_alloc (struct device *dev,struct iommu_domain *parent,struct iommufd_ctx *ictx, -unsigned int viommu_type) +unsigned int viommu_type, +const struct iommu_user_data *user_data) {struct arm_smmu_device *smmu = iommu_get_iommu_dev(dev, struct arm_smmu_device, iommu);
drivers/iommu/iommufd/selftest.c +4 -4
@@ -733,10 +733,10 @@ static struct iommufd_viommu_ops mock_viommu_ops = { .cache_invalidate = mock_viommu_cache_invalidate, }; -static struct iommufd_viommu *mock_viommu_alloc (struct device *dev, -struct iommu_domain *domain, -struct iommufd_ctx *ictx, -unsigned int viommu_type) +static struct iommufd_viommu * +mock_viommu_alloc (struct device *dev,struct iommu_domain *domain, +struct iommufd_ctx *ictx,unsigned int viommu_type, +const struct iommu_user_data *user_data) {struct mock_iommu_device *mock_iommu = iommu_get_iommu_dev(dev, struct mock_iommu_device, iommu_dev);
drivers/iommu/iommufd/viommu.c +1 -1
@@ -48,7 +48,7 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) } viommu = ops->viommu_alloc (idev->dev, hwpt_paging->common.domain, - ucmd->ictx, cmd->type); + ucmd->ictx, cmd->type,NULL );if (IS_ERR (viommu)) { rc =PTR_ERR (viommu); goto out_put_hwpt;
Signature · 1 line
2.43.0
RE: [PATCH v4 02/23] iommu: Pass in a driver-level user data structure to viommu_alloc op
Quoted 15 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:02 AMThe new type of vIOMMU for tegra241-cmdqv allows user space VM to use one
of its virtual command queue HW resources exclusively. This requires user space to mmap the corresponding MMIO page from kernel space for direct HW
control.To forward the mmap info (offset and length), iommufd should add a driver specific data structure to the
IOMMUFD_CMD_VIOMMU_ALLOCioctl, for driver
to output the info during the allocation back to user space.
Quoted 7 lines
Similar to the existing ioctls and their IOMMU handlers, add a
user_datatoviommu_allocop to bridge between iommufd and drivers.Reviewed-by Jason Gunthorpe <[email protected]>Reviewed-by Lu Baolu <[email protected]>Reviewed-by Pranjal Shrivastava <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
PATCH v4 3/23 iommufd/viommu: Allow driver-specific user data for a vIOMMU object
In reply to Nicolin ChenThe new type of vIOMMU for tegra241-cmdqv driver needs a driver-specific user data. So, add data_len/uptr to the iommu_viommu_alloc uAPI and pass it in via the viommu_alloc iommu op.
--- include/uapi/linux/iommufd.h | 6 ++++++ drivers/iommu/iommufd/viommu.c | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-)
include/uapi/linux/iommufd.h +6 -0
@@ -965,6 +965,9 @@ enum iommu_viommu_type {* @dev_id: The device's physical IOMMU will be used to back the virtual IOMMU * @hwpt_id: ID of a nesting parent HWPT to associate to * @out_viommu_id: Output virtual IOMMU ID for the allocated object +* @data_len: Length of the type specific data +* @__reserved: Must be 0 +* @data_uptr: User pointer to an array of driver-specific virtual IOMMU data * * Allocate a virtual IOMMU object, representing the underlying physical IOMMU's * virtualization support that is a security-isolated slice of the real IOMMU HW
@@ -985,6 +988,9 @@ struct iommu_viommu_alloc {__u32 dev_id;__u32 hwpt_id;__u32 out_viommu_id; +__u32 data_len; +__u32 __reserved; +__aligned_u64 data_uptr; };#define IOMMU_VIOMMU_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VIOMMU_ALLOC)
drivers/iommu/iommufd/viommu.c +7 -1
@@ -17,6 +17,11 @@ void iommufd_viommu_destroy(struct iommufd_object *obj)int iommufd_viommu_alloc_ioctl (struct iommufd_ucmd *ucmd) {struct iommu_viommu_alloc *cmd = ucmd->cmd; +const struct iommu_user_data user_data = { + .type = cmd->type, + .uptr =u64_to_user_ptr (cmd->data_uptr), + .len = cmd->data_len, + };struct iommufd_hwpt_paging *hwpt_paging;struct iommufd_viommu *viommu;struct iommufd_device *idev;
@@ -48,7 +53,8 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) } viommu = ops->viommu_alloc (idev->dev, hwpt_paging->common.domain, - ucmd->ictx, cmd->type,NULL ); + ucmd->ictx, cmd->type, + user_data.len ? &user_data :NULL );if (IS_ERR (viommu)) { rc =PTR_ERR (viommu); goto out_put_hwpt;
Signature · 1 line
2.43.0
RE: [PATCH v4 03/23] iommufd/viommu: Allow driver-specific user data for a vIOMMU object
Quoted 7 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:02 AM+ * @data_len: Length of the type specific data
+ * @__reserved: Must be 0
+ * @data_uptr: User pointer to an array of driver-specific virtual IOMMU data
I don't think there is 'an array'.
Re: [PATCH v4 03/23] iommufd/viommu: Allow driver-specific user data for a vIOMMU object
Quoted 11 lines · Kevin
Quoted 7 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:02 AM+ * @data_len: Length of the type specific data
+ * @__reserved: Must be 0
+ * @data_uptr: User pointer to an array of driver-specific virtual IOMMU dataI don't think there is 'an array'.
Reviewed-by Kevin Tian <[email protected]>
Ah, copy-n-paste mistake. Will fix.
Thanks!
Nicolin
PATCH v4 4/23 iommu: Add iommu_copy_struct_to_user helper
In reply to Nicolin ChenSimilar to the iommu_copy_struct_from_user helper receiving data from the user space, add an iommu_copy_struct_to_user helper to report output data back to the user space data pointer.
--- include/linux/iommu.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
include/linux/iommu.h +40 -0
@@ -562,6 +562,46 @@ iommu_copy_struct_from_full_user_array(void *kdst, size_t kdst_entry_size,return 0 ; } +/** +* __iommu_copy_struct_to_user - Report iommu driver specific user space data +* @dst_data: Pointer to a struct iommu_user_data for user space data location +* @src_data: Pointer to an iommu driver specific user data that is defined in +* include/uapi/linux/iommufd.h +* @data_type: The data type of the @src_data. Must match with @dst_data.type +* @data_len: Length of current user data structure, i.e. sizeof(struct _src) +* @min_len: Initial length of user data structure for backward compatibility. +* This should be offsetofend using the last member in the user data +* struct that was initially added to include/uapi/linux/iommufd.h +*/ +static inline int +__iommu_copy_struct_to_user (const struct iommu_user_data *dst_data, +void *src_data,unsigned int data_type, +size_t data_len,size_t min_len) +{ +if (WARN_ON (!dst_data || !src_data)) +return -EINVAL ; +if (dst_data->type != data_type) +return -EINVAL ; +if (dst_data->len < min_len || data_len < dst_data->len) +return -EINVAL ; +return copy_struct_to_user (dst_data->uptr, dst_data->len, src_data, + data_len,NULL ); +} + +/** +* iommu_copy_struct_to_user - Report iommu driver specific user space data +* @user_data: Pointer to a struct iommu_user_data for user space data location +* @ksrc: Pointer to an iommu driver specific user data that is defined in +* include/uapi/linux/iommufd.h +* @data_type: The data type of the @ksrc. Must match with @user_data->type +* @min_last: The last member of the data structure @ksrc points in the initial +* version. +* Return 0 for success, otherwise -error. +*/ +#define iommu_copy_struct_to_user (user_data, ksrc, data_type, min_last) \ + __iommu_copy_struct_to_user(user_data, ksrc, data_type, sizeof(*ksrc), \ + offsetofend(typeof(*ksrc), min_last)) +/** * struct iommu_ops - iommu ops and capabilities * @capable: check capability
Signature · 1 line
2.43.0
RE: [PATCH v4 04/23] iommu: Add iommu_copy_struct_to_user helper
Quoted 11 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:02 AMSimilar to the
iommu_copy_struct_from_userhelper receiving data from the user space, add aniommu_copy_struct_to_userhelper to report output data back to the user space data pointer.Reviewed-by Jason Gunthorpe <[email protected]>Reviewed-by Lu Baolu <[email protected]>Reviewed-by Pranjal Shrivastava <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
PATCH v4 5/23 iommufd/driver: Let iommufd_viommu_alloc helper save ictx to viommu->ictx
In reply to Nicolin ChenWhen an IOMMU driver calls iommufd_viommu_alloc(), it must pass in an ictx pointer as the underlying _iommufd_object_alloc() helper function requires that to allocate a new object. However, neither the iommufd_viommu_alloc() nor its underlying _iommufd_object_alloc() saves the ictx in the allocated viommu object, although viommu could hold an ictx pointer.
When the IOMMU driver wants to use another iommufd function passing in the allocated viommu, it could have avoided passing in the ictx pointer again, if viommu->ictx is valid.
Save ictx to viommu->ictx in the iommufd_viommu_alloc(), in order to ease a new vIOMMU-based helper that would then get the ictx from viommu->ictx.
--- include/linux/iommufd.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
include/linux/iommufd.h +3 -1
@@ -262,8 +262,10 @@ static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, static_assert(offsetof(drv_struct, member.obj) == 0); \ ret = (drv_struct *)_iommufd_object_alloc ( \ ictx,sizeof (drv_struct),IOMMUFD_OBJ_VIOMMU ); \ -if (!IS_ERR (ret)) \ +if (!IS_ERR (ret)) { \ ret->member.ops = viommu_ops; \ + ret->member.ictx = ictx; \ + } \ ret; \ })
Signature · 1 line
2.43.0
Re: [PATCH v4 05/23] iommufd/driver: Let iommufd_viommu_alloc helper save ictx to viommu->ictx
Quoted 10 lines · Nicolin Chen
When an IOMMU driver calls
iommufd_viommu_alloc(), it must pass in an ictx pointer as the underlying_iommufd_object_alloc()helper function requires that to allocate a new object. However, neither theiommufd_viommu_alloc()nor its underlying_iommufd_object_alloc()saves the ictx in the allocated viommu object, although viommu could hold an ictx pointer.When the IOMMU driver wants to use another iommufd function passing in the allocated viommu, it could have avoided passing in the ictx pointer again, if
viommu->ictxis valid.
Quoted 7 lines
Save ictx to
viommu->ictxin theiommufd_viommu_alloc(), in order to ease a new vIOMMU-based helper that would then get the ictx fromviommu->ictx.Signed-off-by Nicolin Chen <[email protected]>--- include/linux/iommufd.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
It is OK, but please think carefully if the ictx is actually needed. The reason most objects don't have an ictx in them is because their contexts are always inside an ioctl so they get the ictx from there.
We don't have a lot of viommu allocations so it isn't such a big deal, but just generally that is how it was built that the ictx comes from the ioctl not the object.
Jason
Re: [PATCH v4 05/23] iommufd/driver: Let iommufd_viommu_alloc helper save ictx to viommu->ictx
Quoted 19 lines · Jason Gunthorpe
Quoted 10 lines · Nicolin Chen
When an IOMMU driver calls
iommufd_viommu_alloc(), it must pass in an ictx pointer as the underlying_iommufd_object_alloc()helper function requires that to allocate a new object. However, neither theiommufd_viommu_alloc()nor its underlying_iommufd_object_alloc()saves the ictx in the allocated viommu object, although viommu could hold an ictx pointer.When the IOMMU driver wants to use another iommufd function passing in the allocated viommu, it could have avoided passing in the ictx pointer again, if
viommu->ictxis valid.Quoted 7 lines
Save ictx to
viommu->ictxin theiommufd_viommu_alloc(), in order to ease a new vIOMMU-based helper that would then get the ictx fromviommu->ictx.Signed-off-by Nicolin Chen <[email protected]>--- include/linux/iommufd.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
Quoted 8 lines
It is OK, but please think carefully if the ictx is actually needed. The reason most objects don't have an ictx in them is because their contexts are always inside an ioctl so they get the ictx from there.
We don't have a lot of viommu allocations so it isn't such a big deal, but just generally that is how it was built that the ictx comes from the ioctl not the object.
It's more of providing cleaner for-driver APIs.
Especially with the new ucmd changes, it can be just:
// in my_viommu_alloc() my_viommu = iommufd_viommu_alloc(ucmd, ...); iommufd_viommu_alloc_mmap(my_viommu, ...); iommufd_viommu_destroy_mmap(my_viommu, ...); // in my_viommu_destory() iommufd_viommu_destroy_mmap(my_viommu, ...);
Otherwise, they would be something like:
// in my_viommu_alloc() my_viommu = iommufd_viommu_alloc(ucmd, ...); iommufd_viommu_alloc_mmap(ucmd->ictx, my_viommu, ...); iommufd_viommu_destroy_mmap(ucmd->ictx, my_viommu, ...); // in my_viommu_destory() iommufd_viommu_destroy_mmap(my_viommu->ictx, my_viommu ...);
That being said, I ended up doing something more in this patch for the ucmd rework and Kevin's comments:
drivers/iommu/iommufd/iommufd_private.h +0 -8
@@ -136,14 +136,6 @@ int iopt_pin_pages(struct io_pagetable *iopt, unsigned long iova,void iopt_unpin_pages (struct io_pagetable *iopt,unsigned long iova,unsigned long length); -struct iommufd_ucmd { -struct iommufd_ctx *ictx; -void __user *ubuffer; -u32 user_size; -void *cmd; -struct iommufd_object *alloced_obj; -}; -int iommufd_vfio_ioctl (struct iommufd_ctx *ictx,unsigned int cmd,unsigned long arg);
drivers/iommu/iommufd/viommu.c +6 -1
@@ -60,9 +60,14 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) goto out_put_hwpt; } +/* The iommufd_viommu_alloc helper saves ucmd->ictx in viommu->ictx */ +if (WARN_ON_ONCE (viommu->ictx != ucmd->ictx)) { + rc = -EINVAL ; + goto out_put_hwpt; + } +xa_init (&viommu->vdevs); viommu->type = cmd->type; - viommu->ictx = ucmd->ictx; viommu->hwpt = hwpt_paging;refcount_inc (&viommu->hwpt->common.obj.users);INIT_LIST_HEAD (&viommu->veventqs);
include/linux/iommufd.h +11 -1
@@ -52,6 +52,14 @@ struct iommufd_object {unsigned int id; }; +struct iommufd_ucmd { +struct iommufd_ctx *ictx; +void __user *ubuffer; +u32 user_size; +void *cmd; +struct iommufd_object *alloced_obj; +}; +struct iommufd_device *iommufd_device_bind (struct iommufd_ctx *ictx,struct device *dev,u32 *id);void iommufd_device_unbind (struct iommufd_device *idev);
@@ -252,8 +260,10 @@ static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, static_assert(offsetof(drv_struct, member.obj) == 0); \ ret = (drv_struct *)_iommufd_object_alloc_ucmd ( \ ucmd,sizeof (drv_struct),IOMMUFD_OBJ_VIOMMU ); \ -if (!IS_ERR (ret)) \ +if (!IS_ERR (ret)) { \ ret->member.ops = viommu_ops; \ + ret->member.ictx = ucmd->ictx; \ + } \ ret; \ })#endif
Thanks
Nicolin
Jason Gunthorpe
maintainerRe: [PATCH v4 05/23] iommufd/driver: Let iommufd_viommu_alloc helper save ictx to viommu->ictx
Quoted 7 lines · Nicolin Chen
Otherwise, they would be something like:
// in my_viommu_alloc() my_viommu = iommufd_viommu_alloc(ucmd, ...); iommufd_viommu_alloc_mmap(ucmd->ictx, my_viommu, ...); iommufd_viommu_destroy_mmap(ucmd->ictx, my_viommu, ...); // in my_viommu_destory() iommufd_viommu_destroy_mmap(my_viommu->ictx, my_viommu ...);
Yes, this is how the other objects work..
Quoted 8 lines
That being said, I ended up doing something more in this patch for the ucmd rework and Kevin's comments:
drivers/iommu/iommufd/iommufd_private.h +0
-0
drivers/iommu/iommufd/iommufd_private.h int iopt_pin_pages(struct io_pagetable *iopt, unsigned long iova, · line 145
Quoted 10 lines
drivers/iommu/iommufd/iommufd_private.h +0
-7@@ -136,14 +136,6 @@ int iopt_pin_pages(struct io_pagetable *iopt, unsigned long iova,void iopt_unpin_pages (struct io_pagetable *iopt,unsigned long iova,unsigned long length); -struct iommufd_ucmd { -struct iommufd_ctx *ictx; -void __user *ubuffer; -u32 user_size; -void *cmd; -struct iommufd_object *alloced_obj; -};
You don't need to move this unless you are using inlines. Just use a forward declaration.
Jason
Re: [PATCH v4 05/23] iommufd/driver: Let iommufd_viommu_alloc helper save ictx to viommu->ictx
Quoted 23 lines · Jason Gunthorpe
Quoted 7 lines · Nicolin Chen
Otherwise, they would be something like:
// in my_viommu_alloc() my_viommu = iommufd_viommu_alloc(ucmd, ...); iommufd_viommu_alloc_mmap(ucmd->ictx, my_viommu, ...); iommufd_viommu_destroy_mmap(ucmd->ictx, my_viommu, ...); // in my_viommu_destory() iommufd_viommu_destroy_mmap(my_viommu->ictx, my_viommu ...);Yes, this is how the other objects work..
Quoted 11 lines
That being said, I ended up doing something more in this patch for the ucmd rework and Kevin's comments:
drivers/iommu/iommufd/iommufd_private.h +0
-0@@ -136,14 +136,6 @@ int iopt_pin_pages(struct io_pagetable *iopt, unsigned long iova,void iopt_unpin_pages (struct io_pagetable *iopt,unsigned long iova,unsigned long length);
Quoted 10 lines
drivers/iommu/iommufd/iommufd_private.h int iopt_pin_pages(struct io_pagetable *iopt, unsigned long iova, · line 145
Quoted 7 lines
drivers/iommu/iommufd/iommufd_private.h +0
-7-struct iommufd_ucmd { -struct iommufd_ctx *ictx; -void __user *ubuffer; -u32 user_size; -void *cmd; -struct iommufd_object *alloced_obj; -};You don't need to move this unless you are using inlines. Just use a forward declaration.
Since we forward ucmd now, ictx is in the ucmd so we need this structure for:
- if (!IS_ERR(ret)) \
+ if (!IS_ERR(ret)) { \
ret->member.ops = viommu_ops; \
+ ret->member.ictx = ucmd->ictx; \
+ } \
Or, we could have a smaller:
#define ucmd_to_ictx(ucmd) (struct iommufd_ctx *)(ucmd)
and add a line of comments in struct iommufd_ucmd:
struct iommufd_ucmd {
- struct iommufd_ctx *ictx;
+ struct iommufd_ctx *ictx; /* Do not move; must be the first member */
void __user *ubuffer;
u32 user_size;
void *cmd;
struct iommufd_object *alloced_obj;
};
Thanks
Nicolin
Jason Gunthorpe
maintainerRe: [PATCH v4 05/23] iommufd/driver: Let iommufd_viommu_alloc helper save ictx to viommu->ictx
include/linux/iommufd.h static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, · line 266
Quoted 11 lines · Nicolin Chen
Quoted 2 lines
You don't need to move this unless you are using inlines. Just use a forward declaration.
Since we forward ucmd now, ictx is in the ucmd so we need this structure for:
include/linux/iommufd.h +3
-1-if (!IS_ERR (ret)) \ +if (!IS_ERR (ret)) { \ ret->member.ops = viommu_ops; \ + ret->member.ictx = ucmd->ictx; \ + } \
De-inline more of that function probably..
Also seem my other remarks about not storing ictx so much..
Jason
Re: [PATCH v4 05/23] iommufd/driver: Let iommufd_viommu_alloc helper save ictx to viommu->ictx
Quoted 16 lines · Jason Gunthorpe
Review
Quoted 11 lines · Nicolin Chen
Quoted 2 lines
You don't need to move this unless you are using inlines. Just use a forward declaration.
Since we forward ucmd now, ictx is in the ucmd so we need this structure for:
(quoted diff) +3
-1- if (!IS_ERR(ret)) \ + if (!IS_ERR(ret)) { \ ret->member.ops = viommu_ops; \ + ret->member.ictx = ucmd->ictx; \ + } \De-inline more of that function probably..
Quoted 1 line
Also seem my other remarks about not storing ictx so much..
I found that all other ictx pointers in vdev/hw_queue are unused, as the core simply gets an ictx from their viommu pointers. This means that only this viommu allocator here needs such a storing.
With that, how about a change like this v.s. inline:
[ Sol-1: Store ucmd ]
----------------------------------------------------------------------------
drivers/iommu/iommufd/viommu.c +7 -1
@@ -60,9 +60,15 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) goto out_put_hwpt; } +/* Verify if the driver correctly calls iommufd_viommu_alloc() */ + static_assert(offsetof(typeof(*ucmd), ictx) == 0); +if (WARN_ON_ONCE (viommu->ictx != ucmd->ictx)) { + rc = -EINVAL ; + goto out_put_hwpt; + } +xa_init (&viommu->vdevs); viommu->type = cmd->type; - viommu->ictx = ucmd->ictx; viommu->hwpt = hwpt_paging;refcount_inc (&viommu->hwpt->common.obj.users);INIT_LIST_HEAD (&viommu->veventqs);
include/linux/iommufd.h +7 -2
@@ -104,7 +104,10 @@ void iommufd_ctx_get(struct iommufd_ctx *ictx);struct iommufd_viommu {struct iommufd_object obj; -struct iommufd_ctx *ictx; +union { +struct iommufd_ctx *ictx; +struct iommufd_ucmd *ucmd;/* for allocation only */ + };struct iommu_device *iommu_dev;struct iommufd_hwpt_paging *hwpt;
@@ -262,8 +265,10 @@ static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, \ ret = (drv_struct *)__iommufd_object_alloc_ucmd ( \ ucmd, ret,IOMMUFD_OBJ_VIOMMU , member.obj); \ -if (!IS_ERR (ret)) \ +if (!IS_ERR (ret)) { \ ret->member.ops = viommu_ops; \ + ret->member.ucmd = ucmd; \ + } \ ret; \ })#endif
----------------------------------------------------------------------------
[ Sol-2: De-inline ]
----------------------------------------------------------------------------
drivers/iommu/iommufd/driver.c +17 -0
@@ -54,6 +54,23 @@ struct iommufd_object *_iommufd_object_alloc_ucmd(struct iommufd_ucmd *ucmd, }EXPORT_SYMBOL_NS_GPL (_iommufd_object_alloc_ucmd,"IOMMUFD" ); +struct iommufd_viommu * +_iommufd_viommu_alloc_ucmd (struct iommufd_ucmd *ucmd,size_t size, +const struct iommufd_viommu_ops *viommu_ops) +{ +struct iommufd_viommu *viommu; + + static_assert(offsetof(typeof(*viommu), obj) == 0); + viommu = (struct iommufd_viommu *)_iommufd_object_alloc_ucmd ( + ucmd, size,IOMMUFD_OBJ_VIOMMU ); +if (IS_ERR (viommu)) +return viommu; + viommu->ops = viommu_ops; + viommu->ictx = ucmd->ictx; +return viommu; +} +EXPORT_SYMBOL_NS_GPL (_iommufd_viommu_alloc_ucmd,"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)
drivers/iommu/iommufd/viommu.c +6 -1
@@ -60,9 +60,14 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) goto out_put_hwpt; } +/* Verify if the driver correctly calls iommufd_viommu_alloc() */ +if (WARN_ON_ONCE (viommu->ictx != ucmd->ictx)) { + rc = -EINVAL ; + goto out_put_hwpt; + } +xa_init (&viommu->vdevs); viommu->type = cmd->type; - viommu->ictx = ucmd->ictx; viommu->hwpt = hwpt_paging;refcount_inc (&viommu->hwpt->common.obj.users);INIT_LIST_HEAD (&viommu->veventqs);
include/linux/iommufd.h +13 -9
@@ -209,6 +209,9 @@ struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx,struct iommufd_object *_iommufd_object_alloc_ucmd (struct iommufd_ucmd *ucmd,size_t size,enum iommufd_object_type type); +struct iommufd_viommu * +_iommufd_viommu_alloc_ucmd (struct iommufd_ucmd *ucmd,size_t size, +const struct iommufd_viommu_ops *viommu_ops);struct device *iommufd_viommu_find_dev (struct iommufd_viommu *viommu,unsigned long vdev_id); int iommufd_viommu_get_vdev_id(struct iommufd_viommu *viommu,
@@ -231,6 +234,13 @@ _iommufd_object_alloc_ucmd(struct iommufd_ucmd *ucmd, size_t size,return ERR_PTR (-EOPNOTSUPP ); } +static inline struct iommufd_viommu * +_iommufd_viommu_alloc_ucmd (struct iommufd_ucmd *ucmd,size_t size, +const struct iommufd_viommu_ops *viommu_ops) +{ +return ERR_PTR (-EOPNOTSUPP ); +} +static inline struct device *iommufd_viommu_find_dev (struct iommufd_viommu *viommu,unsigned long vdev_id) {
@@ -257,13 +267,7 @@ static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu,* the iommufd core. The free op will be called prior to freeing the memory. */ #define iommufd_viommu_alloc (ucmd, drv_struct, member, viommu_ops) \ - ({ \ - drv_struct *ret; \ - \ - ret = (drv_struct *)__iommufd_object_alloc_ucmd( \ - ucmd, ret, IOMMUFD_OBJ_VIOMMU, member.obj); \ - if (!IS_ERR(ret)) \ - ret->member.ops = viommu_ops; \ - ret; \ - }) + container_of(_iommufd_viommu_alloc_ucmd( \ + ucmd, sizeof(drv_struct), viommu_ops), \ + typeof(drv_struct), member)#endif
----------------------------------------------------------------------------
Thanks
Nicolin
Jason Gunthorpe
maintainerRe: [PATCH v4 05/23] iommufd/driver: Let iommufd_viommu_alloc helper save ictx to viommu->ictx
Quoted 17 lines · Nicolin Chen
Quoted 16 lines · Jason Gunthorpe
include/linux/iommufd.h static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, · line 271
Quoted 11 lines · Nicolin Chen
Quoted 2 lines
You don't need to move this unless you are using inlines. Just use a forward declaration.
Since we forward ucmd now, ictx is in the ucmd so we need this structure for:
include/linux/iommufd.h +3
-1-if (!IS_ERR (ret)) \ +if (!IS_ERR (ret)) { \ ret->member.ops = viommu_ops; \ + ret->member.ictx = ucmd->ictx; \ + } \De-inline more of that function probably..
Quoted 7 lines
Quoted 1 line
Also seem my other remarks about not storing ictx so much..
I found that all other ictx pointers in vdev/hw_queue are unused, as the core simply gets an ictx from their viommu pointers. This means that only this viommu allocator here needs such a storing.
With that, how about a change like this v.s. inline:
So I think the other suggestion with init vs alloc supersedes this and makes it all pretty clean?
Jason
Re: [PATCH v4 05/23] iommufd/driver: Let iommufd_viommu_alloc helper save ictx to viommu->ictx
Quoted 20 lines · Jason Gunthorpe
Quoted 19 lines · Nicolin Chen
Quoted 17 lines · Jason Gunthorpe
include/linux/iommufd.h static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, · line 271
Quoted 11 lines · Nicolin Chen
Quoted 2 lines
You don't need to move this unless you are using inlines. Just use a forward declaration.
Since we forward ucmd now, ictx is in the ucmd so we need this structure for:
include/linux/iommufd.h +3
-1-if (!IS_ERR (ret)) \ +if (!IS_ERR (ret)) { \ ret->member.ops = viommu_ops; \ + ret->member.ictx = ucmd->ictx; \ + } \De-inline more of that function probably..
Also seem my other remarks about not storing ictx so much..
Quoted 8 lines
Quoted 5 lines
I found that all other ictx pointers in vdev/hw_queue are unused, as the core simply gets an ictx from their viommu pointers. This means that only this viommu allocator here needs such a storing.
With that, how about a change like this v.s. inline:
So I think the other suggestion with init vs alloc supersedes this and makes it all pretty clean?
I believe so!
Nicolin
RE: [PATCH v4 05/23] iommufd/driver: Let iommufd_viommu_alloc helper save ictx to viommu->ictx
In reply to Nicolin ChenQuoted 15 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:02 AMWhen an IOMMU driver calls
iommufd_viommu_alloc(), it must pass in an ictx
pointer as the underlying_iommufd_object_alloc()helper function requires that to allocate a new object. However, neither theiommufd_viommu_alloc()nor its underlying_iommufd_object_alloc()saves the ictx in the allocated viommu object, although viommu could hold an ictx pointer.When the IOMMU driver wants to use another iommufd function passing in the
allocated viommu, it could have avoided passing in the ictx pointer again, ifviommu->ictxis valid.
Quoted 4 lines
Save ictx to
viommu->ictxin theiommufd_viommu_alloc(), in order to ease a new vIOMMU-based helper that would then get the ictx fromviommu->ictx.Signed-off-by Nicolin Chen <[email protected]>
PATCH v4 6/23 iommufd/driver: Add iommufd_struct_destroy to revert iommufd_viommu_alloc
In reply to Nicolin ChenAn IOMMU driver that allocated a vIOMMU may want to revert the allocation, if it encounters an internal error after the allocation. So, there needs a destroy helper for drivers to use. For instance:
static my_viommu_alloc()
{
...
my_viommu = iommufd_viommu_alloc(viomm, struct my_viommu, core);
...
ret = init_my_viommu();
if (ret) {
/* Need to destroy the my_viommu->core */
return ERR_PTR(ret);
}
return &my_viommu->core;
}Move iommufd_object_abort() to the driver.c file and the public header, to introduce common iommufd_struct_destroy() helper that will abort all kinds of driver structures, not confined to iommufd_viommu but also the new ones being added in the future.
--- drivers/iommu/iommufd/iommufd_private.h | 1 - include/linux/iommufd.h | 16 ++++++++++++++++ drivers/iommu/iommufd/driver.c | 14 ++++++++++++++ drivers/iommu/iommufd/main.c | 13 ------------- 4 files changed, 30 insertions(+), 14 deletions(-)
drivers/iommu/iommufd/iommufd_private.h +0 -1
@@ -180,7 +180,6 @@ static inline void iommufd_put_object(struct iommufd_ctx *ictx,wake_up_interruptible_all (&ictx->destroy_wait); } -void iommufd_object_abort (struct iommufd_ctx *ictx,struct iommufd_object *obj);void iommufd_object_abort_and_destroy (struct iommufd_ctx *ictx,struct iommufd_object *obj); void iommufd_object_finalize(struct iommufd_ctx *ictx,
include/linux/iommufd.h +16 -0
@@ -213,6 +213,7 @@ 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); +void iommufd_object_abort (struct iommufd_ctx *ictx,struct iommufd_object *obj);struct device *iommufd_viommu_find_dev (struct iommufd_viommu *viommu,unsigned long vdev_id); int iommufd_viommu_get_vdev_id(struct iommufd_viommu *viommu,
@@ -228,6 +229,11 @@ _iommufd_object_alloc(struct iommufd_ctx *ictx, size_t size,return ERR_PTR (-EOPNOTSUPP ); } +static inline void iommufd_object_abort (struct iommufd_ctx *ictx, +struct iommufd_object *obj) +{ +} +static inline struct device *iommufd_viommu_find_dev (struct iommufd_viommu *viommu,unsigned long vdev_id) {
@@ -285,4 +291,14 @@ static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, } \ ret; \ }) + +/* Helper for IOMMU driver to destroy structures created by allocators above */ +#define iommufd_struct_destroy (drv_struct, member) \ + ({ \ + static_assert(__same_type(struct iommufd_object, \ + drv_struct->member.obj)); \ + static_assert(offsetof(typeof(*drv_struct), member.obj) == 0); \ + iommufd_object_abort(drv_struct->member.ictx, \ + &drv_struct->member.obj); \ + })#endif
drivers/iommu/iommufd/driver.c +14 -0
@@ -36,6 +36,20 @@ struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx, }EXPORT_SYMBOL_NS_GPL (_iommufd_object_alloc,"IOMMUFD" ); +/* Undo _iommufd_object_alloc() if iommufd_object_finalize() was not called */ +void iommufd_object_abort (struct iommufd_ctx *ictx,struct iommufd_object *obj) +{ +XA_STATE (xas, &ictx->objects, obj->id); +void *old; + +xa_lock (&ictx->objects); + old =xas_store (&xas,NULL ); +xa_unlock (&ictx->objects); +WARN_ON (old !=XA_ZERO_ENTRY ); +kfree (obj); +} +EXPORT_SYMBOL_NS_GPL (iommufd_object_abort,"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)
drivers/iommu/iommufd/main.c +0 -13
@@ -51,19 +51,6 @@ void iommufd_object_finalize(struct iommufd_ctx *ictx,WARN_ON (old !=XA_ZERO_ENTRY ); } -/* Undo _iommufd_object_alloc() if iommufd_object_finalize() was not called */ -void iommufd_object_abort (struct iommufd_ctx *ictx,struct iommufd_object *obj) -{ -XA_STATE (xas, &ictx->objects, obj->id); -void *old; - -xa_lock (&ictx->objects); - old =xas_store (&xas,NULL ); -xa_unlock (&ictx->objects); -WARN_ON (old !=XA_ZERO_ENTRY ); -kfree (obj); -} -/* * Abort an object that has been fully initialized and needs destroy, but has * not been finalized.
Signature · 1 line
2.43.0
Jason Gunthorpe
maintainerRe: [PATCH v4 06/23] iommufd/driver: Add iommufd_struct_destroy to revert iommufd_viommu_alloc
Quoted 25 lines · Nicolin Chen
An IOMMU driver that allocated a vIOMMU may want to revert the allocation, if it encounters an internal error after the allocation. So, there needs a destroy helper for drivers to use. For instance:
static
my_viommu_alloc(){ ... my_viommu = iommufd_viommu_alloc(viomm, struct my_viommu, core); ... ret = init_my_viommu(); if (ret) { /* Need to destroy the my_viommu->core */ return ERR_PTR(ret); } return &my_viommu->core; }Move
iommufd_object_abort()to thedriver.cfile and the public header, to introduce commoniommufd_struct_destroy()helper that will abort all kinds of driver structures, not confined toiommufd_viommubut also the new ones being added in the future.Reviewed-by Jason Gunthorpe <[email protected]>Reviewed-by Lu Baolu <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
Quoted 6 lines
--- drivers/iommu/iommufd/iommufd_private.h | 1 - include/linux/iommufd.h | 16 ++++++++++++++++ drivers/iommu/iommufd/driver.c | 14 ++++++++++++++ drivers/iommu/iommufd/main.c | 13 ------------- 4 files changed, 30 insertions(+), 14 deletions(-)
One idea that struck me when I was looking at this was to copy the technique from rdma.
When an object is allocated we keep track of it in the struct iommufd_ucmd.
Then when the command is over the core code either aborts or finalizes the objects in the iommufd_ucmd. This way you don't have to expose abort and related to drivers.
Something like this:
drivers/iommu/iommufd/driver.c +10 -0
@@ -36,6 +36,16 @@ struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx, }EXPORT_SYMBOL_NS_GPL (_iommufd_object_alloc,"IOMMUFD" ); +struct iommufd_object *_iommufd_object_alloc_ucmd (struct iommufd_ucmd *ucmd, +size_t size, +enum iommufd_object_type type) +{ +if (ucmd->alloced_obj) +return ERR_PTR (-EBUSY ); + ucmd->alloced_obj =_iommufd_object_alloc (ucmd->ictx, size, type); +return ucmd->alloced_obj; +} +/* Undo _iommufd_object_alloc() if iommufd_object_finalize() was not called */ void iommufd_object_abort (struct iommufd_ctx *ictx,struct iommufd_object *obj) {
drivers/iommu/iommufd/eventq.c +3 -9
@@ -473,7 +473,7 @@ int iommufd_fault_alloc(struct iommufd_ucmd *ucmd)if (cmd->flags)return -EOPNOTSUPP ; - fault =__iommufd_object_alloc (ucmd->ictx, fault,IOMMUFD_OBJ_FAULT , + fault =__iommufd_object_alloc_ucmd (ucmd, fault,IOMMUFD_OBJ_FAULT , common.obj);if (IS_ERR (fault))return PTR_ERR (fault);
@@ -483,10 +483,8 @@ int iommufd_fault_alloc(struct iommufd_ucmd *ucmd) fdno =iommufd_eventq_init (&fault->common,"[iommufd-pgfault]" , ucmd->ictx, &iommufd_fault_fops); -if (fdno <0 ) { - rc = fdno; - goto out_abort; - } +if (fdno <0 ) +return fdno; cmd->out_fault_id = fault->common.obj.id; cmd->out_fault_fd = fdno;
@@ -494,7 +492,6 @@ int iommufd_fault_alloc(struct iommufd_ucmd *ucmd) rc =iommufd_ucmd_respond (ucmd,sizeof (*cmd));if (rc) goto out_put_fdno; -iommufd_object_finalize (ucmd->ictx, &fault->common.obj);fd_install (fdno, fault->common.filep);
@@ -502,9 +499,6 @@ int iommufd_fault_alloc(struct iommufd_ucmd *ucmd) out_put_fdno:put_unused_fd (fdno);fput (fault->common.filep); -out_abort: -iommufd_object_abort_and_destroy (ucmd->ictx, &fault->common.obj); -return rc; }
drivers/iommu/iommufd/iommufd_private.h +10 -0
@@ -152,6 +152,7 @@ struct iommufd_ucmd {void __user *ubuffer;u32 user_size;void *cmd; +struct iommufd_object *alloced_obj; }; int iommufd_vfio_ioctl(struct iommufd_ctx *ictx, unsigned int cmd,
@@ -258,6 +259,15 @@ iommufd_object_put_and_try_destroy(struct iommufd_ctx *ictx,#define iommufd_object_alloc (ictx, ptr, type) \ __iommufd_object_alloc(ictx, ptr, type, obj) +#define __iommufd_object_alloc_uctx (uctx, ptr, type, obj) \ + container_of(_iommufd_object_alloc_ucmd( \ + uctx, \ + sizeof(*(ptr)) + BUILD_BUG_ON_ZERO( \ + offsetof(typeof(*(ptr)), \ + obj) != 0), \ + type), \ + typeof(*(ptr)), obj) +/* * The IO Address Space (IOAS) pagetable is a virtual page table backed by the * io_pagetable object. It is a user controlled mapping of IOVA -> PFNs. The
drivers/iommu/iommufd/main.c +8 -0
@@ -408,6 +408,14 @@ static long iommufd_fops_ioctl(struct file *filp, unsigned int cmd,if (ret)return ret; ret = op->execute (&ucmd); + +if (ucmd.alloced_obj) { +if (ret) +iommufd_object_abort_and_destroy (ictx, + ucmd.alloced_obj); +else +iommufd_object_finalize (ictx, ucmd.alloced_obj); + }return ret; }
Re: [PATCH v4 06/23] iommufd/driver: Add iommufd_struct_destroy to revert iommufd_viommu_alloc
Quoted 36 lines · Jason Gunthorpe
Quoted 25 lines · Nicolin Chen
An IOMMU driver that allocated a vIOMMU may want to revert the allocation, if it encounters an internal error after the allocation. So, there needs a destroy helper for drivers to use. For instance:
static
my_viommu_alloc(){ ... my_viommu = iommufd_viommu_alloc(viomm, struct my_viommu, core); ... ret = init_my_viommu(); if (ret) { /* Need to destroy the my_viommu->core */ return ERR_PTR(ret); } return &my_viommu->core; }Move
iommufd_object_abort()to thedriver.cfile and the public header, to introduce commoniommufd_struct_destroy()helper that will abort all kinds of driver structures, not confined toiommufd_viommubut also the new ones being added in the future.Reviewed-by Jason Gunthorpe <[email protected]>Reviewed-by Lu Baolu <[email protected]>Signed-off-by Nicolin Chen <[email protected]>Quoted 6 lines
--- drivers/iommu/iommufd/iommufd_private.h | 1 - include/linux/iommufd.h | 16 ++++++++++++++++ drivers/iommu/iommufd/driver.c | 14 ++++++++++++++ drivers/iommu/iommufd/main.c | 13 ------------- 4 files changed, 30 insertions(+), 14 deletions(-)One idea that struck me when I was looking at this was to copy the technique from rdma.
Quoted 6 lines
When an object is allocated we keep track of it in the struct
iommufd_ucmd.Then when the command is over the core code either aborts or finalizes the objects in the
iommufd_ucmd. This way you don't have to expose abort and related to drivers.
I see! Do you want this to apply to the all objects or just driver allocated ones?
We would need a bigger preparatory series to roll out that to all the allocators, and need to be careful at the existing abort() that intertwines with other steps like an unlock().
Thanks
Nicolin
Jason Gunthorpe
maintainerRe: [PATCH v4 06/23] iommufd/driver: Add iommufd_struct_destroy to revert iommufd_viommu_alloc
Quoted 6 lines · Nicolin Chen
Quoted 3 lines
Then when the command is over the core code either aborts or finalizes the objects in the
iommufd_ucmd. This way you don't have to expose abort and related to drivers.I see! Do you want this to apply to the all objects or just driver allocated ones?
I would do all the ones that can work that way easily
I think it would just be one patch, replace this patch with that one.
Quoted 3 lines
We would need a bigger preparatory series to roll out that to all the allocators, and need to be careful at the existing
abort()that intertwines with other steps like anunlock().
Those cases with special locking couldn't use it.
Jason
Re: [PATCH v4 06/23] iommufd/driver: Add iommufd_struct_destroy to revert iommufd_viommu_alloc
Quoted 10 lines · Jason Gunthorpe
Quoted 6 lines · Nicolin Chen
Quoted 3 lines
Then when the command is over the core code either aborts or finalizes the objects in the
iommufd_ucmd. This way you don't have to expose abort and related to drivers.I see! Do you want this to apply to the all objects or just driver allocated ones?
I would do all the ones that can work that way easily
Quoted 7 lines
I think it would just be one patch, replace this patch with that one.
Quoted 3 lines
We would need a bigger preparatory series to roll out that to all the allocators, and need to be careful at the existing
abort()that intertwines with other steps like anunlock().Those cases with special locking couldn't use it.
OK. I will add a patch in v5, replacing this one.
Thanks
Nicolin
PATCH v4 7/23 iommufd/selftest: Support user_data in mock_viommu_alloc
In reply to Nicolin ChenAdd a simple user_data for an input-to-output loopback test.
--- drivers/iommu/iommufd/iommufd_test.h | 13 +++++++++++++ drivers/iommu/iommufd/selftest.c | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+)
drivers/iommu/iommufd/iommufd_test.h +13 -0
@@ -227,6 +227,19 @@ struct iommu_hwpt_invalidate_selftest {#define IOMMU_VIOMMU_TYPE_SELFTEST 0xdeadbeef +/** +* struct iommu_viommu_selftest - vIOMMU data for Mock driver +* (IOMMU_VIOMMU_TYPE_SELFTEST) +* @in_data: Input random data from user space +* @out_data: Output data (matching @in_data) to user space +* +* Simply set @out_data=@in_data for a loopback test +*/ +struct iommu_viommu_selftest { +__u32 in_data; +__u32 out_data; +}; +/* 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
drivers/iommu/iommufd/selftest.c +19 -0
@@ -740,16 +740,35 @@ mock_viommu_alloc(struct device *dev, struct iommu_domain *domain, {struct mock_iommu_device *mock_iommu = iommu_get_iommu_dev(dev, struct mock_iommu_device, iommu_dev); +struct iommu_viommu_selftest data;struct mock_viommu *mock_viommu; +int rc;if (viommu_type !=IOMMU_VIOMMU_TYPE_SELFTEST )return ERR_PTR (-EOPNOTSUPP ); +if (user_data) { + rc =iommu_copy_struct_from_user ( + &data, user_data,IOMMU_VIOMMU_TYPE_SELFTEST , out_data); +if (rc) +return ERR_PTR (rc); + } + mock_viommu = iommufd_viommu_alloc(ictx, struct mock_viommu, core, &mock_viommu_ops);if (IS_ERR (mock_viommu))return ERR_CAST (mock_viommu); +if (user_data) { + data.out_data = data.in_data; + rc =iommu_copy_struct_to_user ( + user_data, &data,IOMMU_VIOMMU_TYPE_SELFTEST , out_data); +if (rc) { +iommufd_struct_destroy (mock_viommu, core); +return ERR_PTR (rc); + } + } +refcount_inc (&mock_iommu->users);return &mock_viommu->core; }
Signature · 1 line
2.43.0
RE: [PATCH v4 07/23] iommufd/selftest: Support user_data in mock_viommu_alloc
Quoted 7 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:02 AMAdd a simple
user_datafor an input-to-output loopback test.Reviewed-by Pranjal Shrivastava <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
PATCH v4 8/23 iommufd/selftest: Add covearge for viommu data
In reply to Nicolin ChenExtend the existing test_cmd/err_viommu_alloc helpers to accept optional user data. And add a TEST_F for a loopback test.
--- tools/testing/selftests/iommu/iommufd_utils.h | 21 ++++++++----- tools/testing/selftests/iommu/iommufd.c | 30 ++++++++++++++----- .../selftests/iommu/iommufd_fail_nth.c | 5 ++-- 3 files changed, 39 insertions(+), 17 deletions(-)
tools/testing/selftests/iommu/iommufd_utils.h +13 -8
@@ -897,7 +897,8 @@ static int _test_cmd_trigger_iopf(int fd, __u32 device_id, __u32 pasid, pasid, fault_fd))static int _test_cmd_viommu_alloc (int fd,__u32 device_id,__u32 hwpt_id, -__u32 type,__u32 flags,__u32 *viommu_id) +__u32 flags,__u32 type,void *data, +__u32 data_len,__u32 *viommu_id) {struct iommu_viommu_alloc cmd = { .size =sizeof (cmd),
@@ -905,6 +906,8 @@ static int _test_cmd_viommu_alloc(int fd, __u32 device_id, __u32 hwpt_id, .type = type, .dev_id = device_id, .hwpt_id = hwpt_id, + .data_uptr = (uint64_t)data, + .data_len = data_len, };int ret;
@@ -916,13 +919,15 @@ static int _test_cmd_viommu_alloc(int fd, __u32 device_id, __u32 hwpt_id,return 0 ; } -#define test_cmd_viommu_alloc (device_id, hwpt_id, type, viommu_id) \ - ASSERT_EQ(0, _test_cmd_viommu_alloc(self->fd, device_id, hwpt_id, \ - type, 0, viommu_id)) -#define test_err_viommu_alloc (_errno, device_id, hwpt_id, type, viommu_id) \ - EXPECT_ERRNO(_errno, \ - _test_cmd_viommu_alloc(self->fd, device_id, hwpt_id, \ - type, 0, viommu_id)) +#define test_cmd_viommu_alloc (device_id, hwpt_id, type, data, data_len, \ + viommu_id) \ + ASSERT_EQ(0, _test_cmd_viommu_alloc(self->fd, device_id, hwpt_id, 0, \ + type, data, data_len, viommu_id)) +#define test_err_viommu_alloc (_errno, device_id, hwpt_id, type, data, \ + data_len, viommu_id) \ + EXPECT_ERRNO(_errno, \ + _test_cmd_viommu_alloc(self->fd, device_id, hwpt_id, 0, \ + type, data, data_len, viommu_id))static int _test_cmd_vdevice_alloc (int fd,__u32 viommu_id,__u32 idev_id,__u64 virt_id,__u32 *vdev_id)
tools/testing/selftests/iommu/iommufd.c +23 -7
@@ -2688,7 +2688,7 @@ FIXTURE_SETUP(iommufd_viommu)/* Allocate a vIOMMU taking refcount of the parent hwpt */ test_cmd_viommu_alloc (self->device_id, self->hwpt_id, -IOMMU_VIOMMU_TYPE_SELFTEST , +IOMMU_VIOMMU_TYPE_SELFTEST ,NULL ,0 , &self->viommu_id);/* Allocate a regular nested hwpt */
@@ -2727,24 +2727,27 @@ TEST_F(iommufd_viommu, viommu_negative_tests)if (self->device_id) {/* Negative test -- invalid hwpt (hwpt_id=0) */ test_err_viommu_alloc (ENOENT , device_id,0 , -IOMMU_VIOMMU_TYPE_SELFTEST ,NULL ); +IOMMU_VIOMMU_TYPE_SELFTEST ,NULL ,0 , +NULL );/* Negative test -- not a nesting parent hwpt */ test_cmd_hwpt_alloc (device_id, ioas_id,0 , &hwpt_id);test_err_viommu_alloc (EINVAL , device_id, hwpt_id, -IOMMU_VIOMMU_TYPE_SELFTEST ,NULL ); +IOMMU_VIOMMU_TYPE_SELFTEST ,NULL ,0 , +NULL );test_ioctl_destroy (hwpt_id);/* Negative test -- unsupported viommu type */ test_err_viommu_alloc (EOPNOTSUPP , device_id, self->hwpt_id, -0xdead ,NULL ); +0xdead ,NULL ,0 ,NULL );EXPECT_ERRNO (EBUSY ,_test_ioctl_destroy (self->fd, self->hwpt_id));EXPECT_ERRNO (EBUSY ,_test_ioctl_destroy (self->fd, self->viommu_id)); }else {test_err_viommu_alloc (ENOENT , self->device_id, self->hwpt_id, -IOMMU_VIOMMU_TYPE_SELFTEST ,NULL ); +IOMMU_VIOMMU_TYPE_SELFTEST ,NULL ,0 , +NULL ); } }
@@ -2791,6 +2794,20 @@ TEST_F(iommufd_viommu, viommu_alloc_nested_iopf) } } +TEST_F(iommufd_viommu, viommu_alloc_with_data) +{ +struct iommu_viommu_selftest data = { + .in_data =0xbeef , + }; + +if (self->device_id) { +test_cmd_viommu_alloc (self->device_id, self->hwpt_id, +IOMMU_VIOMMU_TYPE_SELFTEST , &data, +sizeof (data), &self->viommu_id); +assert (data.out_data == data.in_data); + } +} + TEST_F(iommufd_viommu, vdevice_alloc) {uint32_t viommu_id = self->viommu_id;
@@ -3105,8 +3122,7 @@ TEST_F(iommufd_device_pasid, pasid_attach)/* Allocate a regular nested hwpt based on viommu */ test_cmd_viommu_alloc (self->device_id, parent_hwpt_id, -IOMMU_VIOMMU_TYPE_SELFTEST , - &viommu_id); +IOMMU_VIOMMU_TYPE_SELFTEST ,NULL ,0 , &viommu_id); test_cmd_hwpt_alloc_nested(self->device_id, viommu_id, IOMMU_HWPT_ALLOC_PASID, &nested_hwpt_id[2],
tools/testing/selftests/iommu/iommufd_fail_nth.c +3 -2
@@ -688,8 +688,9 @@ TEST_FAIL_NTH(basic_fail_nth, device) IOMMU_HWPT_DATA_NONE, 0, 0))return -1 ; -if (_test_cmd_viommu_alloc (self->fd, idev_id, hwpt_id, -IOMMU_VIOMMU_TYPE_SELFTEST ,0 , &viommu_id)) +if (_test_cmd_viommu_alloc (self->fd, idev_id, hwpt_id,0 , +IOMMU_VIOMMU_TYPE_SELFTEST ,NULL ,0 , + &viommu_id))return -1 ;if (_test_cmd_vdevice_alloc (self->fd, viommu_id, idev_id,0 , &vdev_id))
Signature · 1 line
2.43.0
RE: [PATCH v4 08/23] iommufd/selftest: Add covearge for viommu data
Quoted 8 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:02 AMExtend the existing test_cmd/err_viommu_alloc helpers to accept optional user data. And add a
TEST_Ffor a loopback test.Reviewed-by Pranjal Shrivastava <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
PATCH v4 9/23 iommufd: Abstract iopt_pin_pages and iopt_unpin_pages helpers
In reply to Nicolin ChenThe new HW QUEUE object will be added for HW to access the guest queue for HW-accelerated virtualization feature. It needs to ensure the guest memory pages are pinned when HW accesses them and they are contiguous in physical address space.
This is very like the existing iommufd_access_pin_pages() that outputs the pinned page list for the caller to test its contiguity.
Move those code from iommufd_access_pin/unpin_pages() and related function for a pair of iopt helpers that can be shared with the HW QUEUE allocator.
Rename check_area_prot() to align with the existing iopt_area helpers, and inline it to the header since iommufd_access_rw() still uses it.
--- drivers/iommu/iommufd/io_pagetable.h | 8 ++ drivers/iommu/iommufd/iommufd_private.h | 6 ++ drivers/iommu/iommufd/device.c | 117 ++---------------------- drivers/iommu/iommufd/io_pagetable.c | 95 +++++++++++++++++++ 4 files changed, 117 insertions(+), 109 deletions(-)
drivers/iommu/iommufd/io_pagetable.h +8 -0
@@ -114,6 +114,14 @@ static inline unsigned long iopt_area_iova_to_index(struct iopt_area *area,return iopt_area_start_byte (area, iova) /PAGE_SIZE ; } +static inline bool iopt_area_check_prot (struct iopt_area *area, +unsigned int flags) +{ +if (flags &IOMMUFD_ACCESS_RW_WRITE ) +return area->iommu_prot &IOMMU_WRITE ; +return area->iommu_prot &IOMMU_READ ; +} +#define __make_iopt_iter (name) \ static inline struct iopt_##name *iopt_##name##_iter_first( \ struct io_pagetable *iopt, unsigned long start, \
drivers/iommu/iommufd/iommufd_private.h +6 -0
@@ -130,6 +130,12 @@ int iopt_cut_iova(struct io_pagetable *iopt, unsigned long *iovas,void iopt_enable_large_pages (struct io_pagetable *iopt);int iopt_disable_large_pages (struct io_pagetable *iopt); +int iopt_pin_pages (struct io_pagetable *iopt,unsigned long iova, +unsigned long length,struct page **out_pages, +unsigned int flags); +void iopt_unpin_pages (struct io_pagetable *iopt,unsigned long iova, +unsigned long length); +struct iommufd_ucmd {struct iommufd_ctx *ictx;void __user *ubuffer;
drivers/iommu/iommufd/device.c +8 -109
149 lines not on this page · raw message
drivers/iommu/iommufd/io_pagetable.c +95 -0
98 lines not on this page · raw message
Signature · 1 line
2.43.0
Re: [PATCH v4 09/23] iommufd: Abstract iopt_pin_pages and iopt_unpin_pages helpers
Quoted 14 lines · Nicolin Chen
The new HW QUEUE object will be added for HW to access the guest queue for HW-accelerated virtualization feature. It needs to ensure the guest memory pages are pinned when HW accesses them and they are contiguous in physical address space.
This is very like the existing
iommufd_access_pin_pages()that outputs the pinned page list for the caller to test its contiguity.Move those code from iommufd_access_pin/unpin_pages() and related function for a pair of iopt helpers that can be shared with the HW QUEUE allocator.
Rename
check_area_prot()to align with the existingiopt_areahelpers, and inline it to the header sinceiommufd_access_rw()still uses it.
Quoted 8 lines
Reviewed-by Pranjal Shrivastava <[email protected]>Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/iommufd/io_pagetable.h | 8 ++ drivers/iommu/iommufd/iommufd_private.h | 6 ++ drivers/iommu/iommufd/device.c | 117 ++---------------------- drivers/iommu/iommufd/io_pagetable.c | 95 +++++++++++++++++++ 4 files changed, 117 insertions(+), 109 deletions(-)
Jason
RE: [PATCH v4 09/23] iommufd: Abstract iopt_pin_pages and iopt_unpin_pages helpers
In reply to Nicolin ChenQuoted 9 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMThe new HW QUEUE object will be added for HW to access the guest queue for
HW-accelerated virtualization feature. It needs to ensure the guest memory pages are pinned when HW accesses them and they are contiguous in physical
address space.
'pin' is the generic requirement but contiguity is vendor specific. Let's make it clear.
Quoted 8 lines
This is very like the existing
iommufd_access_pin_pages()that outputs the pinned page list for the caller to test its contiguity.Move those code from iommufd_access_pin/unpin_pages() and related function
for a pair of iopt helpers that can be shared with the HW QUEUE allocator.
Quoted 5 lines
Rename
check_area_prot()to align with the existingiopt_areahelpers, and inline it to the header sinceiommufd_access_rw()still uses it.Reviewed-by Pranjal Shrivastava <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
PATCH v4 10/23 iommufd/viommu: Introduce IOMMUFD_OBJ_HW_QUEUE and its related struct
In reply to Nicolin ChenAdd IOMMUFD_OBJ_HW_QUEUE with an iommufd_hw_queue structure, representing a HW-accelerated queue type of IOMMU's physical queue that can be passed through to a user space VM for direct hardware control, such as:
- NVIDIA's Virtual Command Queue
- AMD vIOMMU's Command Buffer, Event Log Buffer, and PPR Log Buffer
Introduce an allocator iommufd_hw_queue_alloc(). And add a pair of viommu ops for iommufd to forward user space ioctls to IOMMU drivers.
Given that the first user of this HW QUEUE (tegra241-cmdqv) will need to ensure the queue memory to be physically contiguous, add a flag property in iommufd_viommu_ops and IOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PA to allow driver to flag it so that the core will validate the physical pages of a given guest queue.
--- include/linux/iommufd.h | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
include/linux/iommufd.h +45 -0
@@ -37,6 +37,7 @@ enum iommufd_object_type {IOMMUFD_OBJ_VIOMMU ,IOMMUFD_OBJ_VDEVICE ,IOMMUFD_OBJ_VEVENTQ , +IOMMUFD_OBJ_HW_QUEUE , #ifdef CONFIG_IOMMUFD_TEST IOMMUFD_OBJ_SELFTEST, #endif
@@ -112,6 +113,18 @@ struct iommufd_vdevice {u64 id;/* per-vIOMMU virtual ID */ }; +struct iommufd_hw_queue { +struct iommufd_object obj; +struct iommufd_ctx *ictx; +struct iommufd_viommu *viommu; +u64 base_addr;/* in guest physical address space */ +size_t length; +}; + +enum iommufd_viommu_flags { +IOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PA =1 <<0 , +}; +/** * struct iommufd_viommu_ops - vIOMMU specific operations * @destroy: Clean up all driver-specific parts of an iommufd_viommu. The memory
@@ -137,8 +150,18 @@ struct iommufd_vdevice {* @vdevice_destroy: Clean up all driver-specific parts of an iommufd_vdevice. * The memory of the vDEVICE will be free-ed by iommufd core * after calling this op +* @hw_queue_alloc: Allocate a HW QUEUE object for a HW-accelerated queue given +* the @type (must be defined in include/uapi/linux/iommufd.h) +* for the @viommu. @index carries the logical HW QUEUE ID per +* @viommu in a guest VM, for a multi-queue case; @addr carries +* the guest physical base address of the queue memory; @length +* carries the size of the queue +* @hw_queue_destroy: Clean up all driver-specific parts of an iommufd_hw_queue. +* The memory of the HW QUEUE will be free-ed by iommufd core +* after calling this op */ struct iommufd_viommu_ops { +u32 flags;void (*destroy)(struct iommufd_viommu *viommu); struct iommu_domain *(*alloc_domain_nested)( struct iommufd_viommu *viommu, u32 flags,
@@ -149,6 +172,10 @@ struct iommufd_viommu_ops {struct device *dev, u64 virt_id);void (*vdevice_destroy)(struct iommufd_vdevice *vdev); +struct iommufd_hw_queue *(*hw_queue_alloc)( +struct iommufd_viommu *viommu,unsigned int type,u32 index, +u64 base_addr,size_t length); +void (*hw_queue_destroy)(struct iommufd_hw_queue *hw_queue); };#if IS_ENABLED (CONFIG_IOMMUFD )
@@ -292,6 +319,24 @@ static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, ret; \ }) +#define iommufd_hw_queue_alloc (viommu, drv_struct, member) \ + ({ \ + drv_struct *ret; \ + \ + static_assert(__same_type(struct iommufd_viommu, *viommu)); \ + static_assert(__same_type(struct iommufd_hw_queue, \ + ((drv_struct *)NULL)->member)); \ + static_assert(offsetof(drv_struct, member.obj) == 0); \ + ret = (drv_struct *)_iommufd_object_alloc( \ + viommu->ictx, sizeof(drv_struct), \ + IOMMUFD_OBJ_HW_QUEUE); \ + if (!IS_ERR(ret)) { \ + ret->member.viommu = viommu; \ + ret->member.ictx = viommu->ictx; \ + } \ + ret; \ + }) +/* Helper for IOMMU driver to destroy structures created by allocators above */ #define iommufd_struct_destroy (drv_struct, member) \ ({ \
Signature · 1 line
2.43.0
RE: [PATCH v4 10/23] iommufd/viommu: Introduce IOMMUFD_OBJ_HW_QUEUE and its related struct
Quoted 14 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMAdd
IOMMUFD_OBJ_HW_QUEUEwith aniommufd_hw_queuestructure, representing
a HW-accelerated queue type of IOMMU's physical queue that can be passed through to a user space VM for direct hardware control, such as:
- NVIDIA's Virtual Command Queue
- AMD vIOMMU's Command Buffer, Event Log Buffer, and PPR Log BufferIntroduce an allocator
iommufd_hw_queue_alloc(). And add a pair of viommu
ops for iommufd to forward user space ioctls to IOMMU drivers.
Quoted 6 lines
Given that the first user of this HW QUEUE (tegra241-cmdqv) will need to ensure the queue memory to be physically contiguous, add a flag property in
iommufd_viommu_opsandIOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PAto allow
driver to flag it so that the core will validate the physical pages of a given guest queue.
'READS' is confusing here. What about xxx_CONTIG_PAS?
Quoted 6 lines
+ * @hw_queue_alloc: Allocate a HW QUEUE object for a HW-accelerated queue given
+ * the @type (must be defined in include/uapi/linux/iommufd.h) + * for the @viommu. @index carries the logical HW QUEUE ID per + * @viommu in a guest VM, for a multi-queue case; @addr carries + * the guest physical base address of the queue memory;
s/@addr/@base_addr/
Re: [PATCH v4 10/23] iommufd/viommu: Introduce IOMMUFD_OBJ_HW_QUEUE and its related struct
Quoted 14 lines · Kevin
Quoted 14 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMAdd
IOMMUFD_OBJ_HW_QUEUEwith aniommufd_hw_queuestructure, representing
a HW-accelerated queue type of IOMMU's physical queue that can be passed through to a user space VM for direct hardware control, such as:
- NVIDIA's Virtual Command Queue
- AMD vIOMMU's Command Buffer, Event Log Buffer, and PPR Log BufferIntroduce an allocator
iommufd_hw_queue_alloc(). And add a pair of viommu
ops for iommufd to forward user space ioctls to IOMMU drivers.
Quoted 8 lines
Quoted 6 lines
Given that the first user of this HW QUEUE (tegra241-cmdqv) will need to ensure the queue memory to be physically contiguous, add a flag property in
iommufd_viommu_opsandIOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PAto allow
driver to flag it so that the core will validate the physical pages of a given guest queue.'READS' is confusing here. What about
xxx_CONTIG_PAS?
Sure.
Quoted 10 lines
Quoted 6 lines
+ * @hw_queue_alloc: Allocate a HW QUEUE object for a HW-accelerated queue given
+ * the @type (must be defined in include/uapi/linux/iommufd.h) + * for the @viommu. @index carries the logical HW QUEUE ID per + * @viommu in a guest VM, for a multi-queue case; @addr carries + * the guest physical base address of the queue memory;s/@addr/@base_addr/
Reviewed-by Kevin Tian <[email protected]>
Thanks!
Nicolin
Re: [PATCH v4 10/23] iommufd/viommu: Introduce IOMMUFD_OBJ_HW_QUEUE and its related struct
In reply to Tian, KevinQuoted 14 lines · Kevin
Quoted 14 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMAdd
IOMMUFD_OBJ_HW_QUEUEwith aniommufd_hw_queuestructure, representing
a HW-accelerated queue type of IOMMU's physical queue that can be passed through to a user space VM for direct hardware control, such as:
- NVIDIA's Virtual Command Queue
- AMD vIOMMU's Command Buffer, Event Log Buffer, and PPR Log BufferIntroduce an allocator
iommufd_hw_queue_alloc(). And add a pair of viommu
ops for iommufd to forward user space ioctls to IOMMU drivers.
Quoted 8 lines
Quoted 6 lines
Given that the first user of this HW QUEUE (tegra241-cmdqv) will need to ensure the queue memory to be physically contiguous, add a flag property in
iommufd_viommu_opsandIOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PAto allow
driver to flag it so that the core will validate the physical pages of a given guest queue.'READS' is confusing here. What about
xxx_CONTIG_PAS?
Combining Jason's first comments here:
https://lore.kernel.org/linux-iommu/[email protected]/
So, pinning should be optional too. And I think there would be unlikely a case where HW needs contiguous physical pages while not requiring to pin the pages, right?
So, we need an flag that could indicate to do both tests. Yet, "xxx_CONTIG_PAS" doesn't sound very fitting, compared to this "IOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PA".
Perhaps, we should just add some comments to clarify a bit. Or do you have some better naming?
Thanks
Nicolin
Tian, Kevin
maintainerRE: [PATCH v4 10/23] iommufd/viommu: Introduce IOMMUFD_OBJ_HW_QUEUE and its related struct
Quoted 37 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 16, 2025 10:30 AMQuoted 6 lines · Kevin
Quoted 6 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMAdd
IOMMUFD_OBJ_HW_QUEUEwith aniommufd_hw_queuestructure, representing
a HW-accelerated queue type of IOMMU's physical queue that can bepassed
Quoted 10 lines
Quoted 10 lines
through to a user space VM for direct hardware control, such as:
- NVIDIA's Virtual Command Queue
- AMD vIOMMU's Command Buffer, Event Log Buffer, and PPR Log BufferIntroduce an allocator
iommufd_hw_queue_alloc(). And add a pair of viommu
ops for iommufd to forward user space ioctls to IOMMU drivers.Given that the first user of this HW QUEUE (tegra241-cmdqv) will need to ensure the queue memory to be physically contiguous, add a flag
property
Quoted 6 lines
Quoted 4 lines
in
iommufd_viommu_opsandIOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PAto allow
driver to flag it so that the core will validate the physical pages of a given guest queue.'READS' is confusing here. What about
xxx_CONTIG_PAS?Combining Jason's first comments here:
https://lore.kernel.org/linux-
iommu/[email protected]/So, pinning should be optional too. And I think there would be unlikely a case where HW needs contiguous physical pages while not requiring to pin the pages, right?
Quoted 7 lines
So, we need an flag that could indicate to do both tests. Yet, "
xxx_CONTIG_PAS" doesn't sound very fitting, compared to this "IOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PA".Perhaps, we should just add some comments to clarify a bit. Or do you have some better naming?
let's wait until that open is closed, i.e. whether we still let the core manage it and whether AMD requires pinning even when IOVA
is used.
Re: [PATCH v4 10/23] iommufd/viommu: Introduce IOMMUFD_OBJ_HW_QUEUE and its related struct
Kevin, Nicolin,
Quoted 29 lines · Kevin
Quoted 29 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 16, 2025 10:30 AMQuoted 6 lines · Kevin
Quoted 6 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMAdd
IOMMUFD_OBJ_HW_QUEUEwith aniommufd_hw_queuestructure, representing
a HW-accelerated queue type of IOMMU's physical queue that can bepassed
Quoted 10 lines
Quoted 10 lines
through to a user space VM for direct hardware control, such as:
- NVIDIA's Virtual Command Queue
- AMD vIOMMU's Command Buffer, Event Log Buffer, and PPR Log BufferIntroduce an allocator
iommufd_hw_queue_alloc(). And add a pair of viommu
ops for iommufd to forward user space ioctls to IOMMU drivers.Given that the first user of this HW QUEUE (tegra241-cmdqv) will need to ensure the queue memory to be physically contiguous, add a flag
property
Quoted 6 lines
Quoted 4 lines
in
iommufd_viommu_opsandIOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PAto allow
driver to flag it so that the core will validate the physical pages of a given guest queue.'READS' is confusing here. What about
xxx_CONTIG_PAS?
Quoted 7 lines
Quoted 7 lines
Combining Jason's first comments here:
https://lore.kernel.org/linux-
iommu/[email protected]/So, pinning should be optional too. And I think there would be unlikely a case where HW needs contiguous physical pages while not requiring to pin the pages, right?
AMD IOMMU needs contiguous GPA space for buffer (like command buffer), not contiguous physical address.
Quoted 5 lines
Quoted 5 lines
So, we need an flag that could indicate to do both tests. Yet, "
xxx_CONTIG_PAS" doesn't sound very fitting, compared to this "IOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PA".
Quoted 7 lines
Quoted 3 lines
Perhaps, we should just add some comments to clarify a bit. Or do you have some better naming?
let's wait until that open is closed, i.e. whether we still let the core manage it and whether AMD requires pinning even when IOVA
is used.
I think we may still want to pin those buffer address.
-Vasant
Jason Gunthorpe
maintainerRe: [PATCH v4 10/23] iommufd/viommu: Introduce IOMMUFD_OBJ_HW_QUEUE and its related struct
In reply to Nicolin ChenQuoted 7 lines · Nicolin Chen
include/linux/iommufd.h +7
-0+#define iommufd_hw_queue_alloc (viommu, drv_struct, member) \ + ({ \ + drv_struct *ret; \ + \ + static_assert(__same_type(struct iommufd_viommu, *viommu)); \ + static_assert(__same_type(struct iommufd_hw_queue, \ + ((drv_struct *)NULL)->member)); \
include/linux/iommufd.h static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, · line 338
Quoted 10 lines
include/linux/iommufd.h +10
-0+ static_assert(offsetof(drv_struct, member.obj) == 0); \ + ret = (drv_struct *)_iommufd_object_alloc ( \ + viommu->ictx,sizeof (drv_struct), \ +IOMMUFD_OBJ_HW_QUEUE ); \ +if (!IS_ERR (ret)) { \ + ret->member.viommu = viommu; \ + ret->member.ictx = viommu->ictx; \ + } \ + ret; \ + })
This should just call
__iommufd_object_alloc(viommu->ictx, ret, IOMMUFD_OBJ_HW_QUEUE member.obj)
And all the casting and asserts should be in that macro, move it to this header
Quoted 3 lines
/* Helper for IOMMU driver to destroy structures created by allocators above */
#defineiommufd_struct_destroy(drv_struct, member)\
({ \
This is abort not destroy, the names should be consistent. But looking more at the series I think it will be better to do the little rework I suggested and not give this function to the driver in the first place.
Jason
Re: [PATCH v4 10/23] iommufd/viommu: Introduce IOMMUFD_OBJ_HW_QUEUE and its related struct
Quoted 21 lines · Jason Gunthorpe
Quoted 7 lines · Nicolin Chen
include/linux/iommufd.h +7
-0+#define iommufd_hw_queue_alloc (viommu, drv_struct, member) \ + ({ \ + drv_struct *ret; \ + \ + static_assert(__same_type(struct iommufd_viommu, *viommu)); \ + static_assert(__same_type(struct iommufd_hw_queue, \ + ((drv_struct *)NULL)->member)); \include/linux/iommufd.h static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, · line 338
Quoted 10 lines
include/linux/iommufd.h +10
-0+ static_assert(offsetof(drv_struct, member.obj) == 0); \ + ret = (drv_struct *)_iommufd_object_alloc ( \ + viommu->ictx,sizeof (drv_struct), \ +IOMMUFD_OBJ_HW_QUEUE ); \ +if (!IS_ERR (ret)) { \ + ret->member.viommu = viommu; \ + ret->member.ictx = viommu->ictx; \ + } \ + ret; \ + })This should just call
Quoted 4 lines
__iommufd_object_alloc(viommu->ictx, ret, IOMMUFD_OBJ_HW_QUEUE member.obj)And all the casting and asserts should be in that macro, move it to this header
Ack.
Quoted 7 lines
Quoted 3 lines
/* Helper for IOMMU driver to destroy structures created by allocators above */
#defineiommufd_struct_destroy(drv_struct, member)\
({ \This is abort not destroy, the names should be consistent. But looking more at the series I think it will be better to do the little rework I suggested and not give this function to the driver in the first place.
Yea, it will be replaced in v5.
Thanks
Nicolin
PATCH v4 11/23 iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
In reply to Nicolin ChenIntroduce a new IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl for user space to allocate a HW QUEUE object for a vIOMMU specific HW-accelerated queue, e.g.:
- NVIDIA's Virtual Command Queue
- AMD vIOMMU's Command Buffer, Event Log Buffer, and PPR Log Buffer
This is a vIOMMU based ioctl. Simply increase the refcount of the vIOMMU.
--- drivers/iommu/iommufd/iommufd_private.h | 2 + include/uapi/linux/iommufd.h | 42 ++++++++++ drivers/iommu/iommufd/main.c | 6 ++ drivers/iommu/iommufd/viommu.c | 104 ++++++++++++++++++++++++ 4 files changed, 154 insertions(+)
drivers/iommu/iommufd/iommufd_private.h +2 -0
@@ -611,6 +611,8 @@ 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); +int iommufd_hw_queue_alloc_ioctl (struct iommufd_ucmd *ucmd); +void iommufd_hw_queue_destroy (struct iommufd_object *obj);#ifdef CONFIG_IOMMUFD_TEST int iommufd_test (struct iommufd_ucmd *ucmd);
include/uapi/linux/iommufd.h +42 -0
52 lines not on this page · raw message
drivers/iommu/iommufd/main.c +6 -0
@@ -292,6 +292,7 @@ union ucmd_buffer {struct iommu_destroy destroy;struct iommu_fault_alloc fault;struct iommu_hw_info info; +struct iommu_hw_queue_alloc hw_queue;struct iommu_hwpt_alloc hwpt;struct iommu_hwpt_get_dirty_bitmap get_dirty_bitmap;struct iommu_hwpt_invalidate cache;
@@ -334,6 +335,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = { struct iommu_fault_alloc, out_fault_fd),IOCTL_OP (IOMMU_GET_HW_INFO ,iommufd_get_hw_info ,struct iommu_hw_info ,__reserved ), +IOCTL_OP (IOMMU_HW_QUEUE_ALLOC ,iommufd_hw_queue_alloc_ioctl , +struct iommu_hw_queue_alloc ,length ),IOCTL_OP (IOMMU_HWPT_ALLOC ,iommufd_hwpt_alloc ,struct iommu_hwpt_alloc , __reserved), IOCTL_OP(IOMMU_HWPT_GET_DIRTY_BITMAP, iommufd_hwpt_get_dirty_bitmap,
@@ -490,6 +493,9 @@ static const struct iommufd_object_ops iommufd_object_ops[] = { [IOMMUFD_OBJ_FAULT] = { .destroy = iommufd_fault_destroy, }, + [IOMMUFD_OBJ_HW_QUEUE] = { + .destroy = iommufd_hw_queue_destroy, + }, [IOMMUFD_OBJ_HWPT_PAGING] = { .destroy = iommufd_hwpt_paging_destroy, .abort = iommufd_hwpt_paging_abort,
drivers/iommu/iommufd/viommu.c +104 -0
107 lines not on this page · raw message
Signature · 1 line
2.43.0
Tian, Kevin
maintainerRE: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 11 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMinclude/uapi/linux/iommufd.h +8
-0+ +/** +* struct iommu_hw_queue_alloc - ioctl(IOMMU_HW_QUEUE_ALLOC) +* @size: sizeof(struct iommu_hw_queue_alloc) +* @flags: Must be 0 +* @viommu_id: Virtual IOMMU ID to associate the HW queue with +* @type: One of enum iommu_hw_queue_type +* @index: The logical index to the HW queue per virtual IOMMU for a
Quoted 2 lines
multi-queue
+ * model
I'm thinking of an alternative way w/o having the user to assign index and allowing the driver to poke object dependency (next patch).
Let's say the index is internally assigned by the driver. so this cmd is just for allowing a hw queue and it's the driver to decide the allocation policy, e.g. in ascending order.
Introduce a new flag in viommu_ops to indicate to core that the new hw queue should hold a reference to the previous hw queue.
core maintains a last_queue field in viommu. Upon success return from @hw_queue_alloc() the core increments the users refcnt of last_queue, records the dependency in iommufd_hw_queue struct, and update viommu->last_queue.
Then the destroy order is naturally guaranteed.
Quoted 5 lines
+ * @out_hw_queue_id: The ID of the new HW queue
+ * @base_addr: Base address of the queue memory in guest physical address space
+ * @length: Length of the queue memory in the guest physical address space
length is agnostic to address space.
Quoted 9 lines
drivers/iommu/iommufd/viommu.c +9
-0+int iommufd_hw_queue_alloc_ioctl (struct iommufd_ucmd *ucmd) +{ +struct iommu_hw_queue_alloc *cmd = ucmd->cmd; +struct iommufd_hw_queue *hw_queue; +struct iommufd_hwpt_paging *hwpt; +struct iommufd_viommu *viommu; +struct page **pages; +int max_npages, i; +u64 end;
drivers/iommu/iommufd/viommu.c int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) · line 205
Quoted 10 lines
drivers/iommu/iommufd/viommu.c +10
-0+int rc; + +if (cmd->flags || cmd->type ==IOMMU_HW_QUEUE_TYPE_DEFAULT ) +return -EOPNOTSUPP ; +if (!cmd->base_addr || !cmd->length) +return -EINVAL ; +if (check_add_overflow (cmd->base_addr, cmd->length -1 , &end)) +return -EOVERFLOW ; + + max_npages =DIV_ROUND_UP (cmd->length,PAGE_SIZE );
what about [base_addr, base_addr+length) spanning two pages but 'length' is smaller than the size of one page?
drivers/iommu/iommufd/viommu.c int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) · line 208
Quoted 3 lines
drivers/iommu/iommufd/viommu.c +3
-0+ pages =kcalloc (max_npages,sizeof (*pages),GFP_KERNEL ); +if (!pages) +return -ENOMEM ;
this could be moved to right before iopt_pin_pages().
Quoted 16 lines
drivers/iommu/iommufd/viommu.c +15
-0+ + viommu =iommufd_get_viommu (ucmd, cmd->viommu_id); +if (IS_ERR (viommu)) { + rc =PTR_ERR (viommu); + goto out_free; + } + hwpt = viommu->hwpt; + +if (!viommu->ops || !viommu->ops->hw_queue_alloc) { + rc = -EOPNOTSUPP ; + goto out_put_viommu; + } + +/* Quick test on the base address */ +if (!iommu_iova_to_phys (hwpt->common.domain, cmd->base_addr)){
drivers/iommu/iommufd/viommu.c int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) · line 226
Quoted 3 lines
drivers/iommu/iommufd/viommu.c +3
-0+ rc = -ENXIO ; + goto out_put_viommu; + }
this check is redundant. Actually it's not future proof, assuming that S2 must be pinned before the user attempts to call this cmd. But what if one day iommufd supports unpinned S2 (if a device is 100% PRI faultable) then this path will be broken.
Quoted 28 lines
drivers/iommu/iommufd/viommu.c +3
-0+ +/* +* The underlying physical pages must be pinned to prevent them from
(quoted diff) +1
-0+ * being unmapped (via IOMMUFD_CMD_IOAS_UNMAP) during thelife cycle
drivers/iommu/iommufd/viommu.c +3
-0+* of the HW QUEUE object. +*/ + rc = iopt_pin_pages(&hwpt->ioas->iopt, cmd->base_addr, cmd-Quoted 1 line
length,
drivers/iommu/iommufd/viommu.c +5
-0+ pages, 0); +if (rc) + goto out_put_viommu; + + if (viommu->ops->flags &
IOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PA) {drivers/iommu/iommufd/viommu.c +11
-0+/* Validate if the underlying physical pages are contiguous */ +for (i =1 ; i < max_npages && pages[i]; i++) { +if (page_to_pfn (pages[i]) == +page_to_pfn (pages[i -1 ]) +1 ) +continue ; + rc = -EFAULT ; + goto out_unpin; + } + } + + hw_queue = viommu->ops->hw_queue_alloc(viommu, cmd->type,
cmd->index,
drivers/iommu/iommufd/viommu.c int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) · line 258
Quoted 9 lines
drivers/iommu/iommufd/viommu.c +9
-0+ cmd->base_addr, cmd->length); +if (IS_ERR (hw_queue)) { + rc =PTR_ERR (hw_queue); + goto out_unpin; + } + + hw_queue->viommu = viommu; +refcount_inc (&viommu->obj.users); + hw_queue->ictx = ucmd->ictx;
viommu/ictx are already set by iommufd_hw_queue_alloc().
Quoted 22 lines
drivers/iommu/iommufd/viommu.c +6
-0+ hw_queue->length = cmd->length; + hw_queue->base_addr = cmd->base_addr; + cmd->out_hw_queue_id = hw_queue->obj.id; + rc =iommufd_ucmd_respond (ucmd,sizeof (*cmd)); +if (rc) + iommufd_object_abort_and_destroy(ucmd->ictx,
&hw_queue->obj);drivers/iommu/iommufd/viommu.c +6
-0+else +iommufd_object_finalize (ucmd->ictx, &hw_queue->obj); + goto out_put_viommu; + +out_unpin: + iopt_unpin_pages(&hwpt->ioas->iopt, cmd->base_addr, cmd-Quoted 1 line
length);
drivers/iommu/iommufd/viommu.c +6
-1+out_put_viommu: +iommufd_put_object (ucmd->ictx, &viommu->obj); +out_free: +kfree (pages); +return rc; +} --2.43.0
Re: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 24 lines · Kevin
Quoted 11 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMinclude/uapi/linux/iommufd.h +8
-0+ +/** +* struct iommu_hw_queue_alloc - ioctl(IOMMU_HW_QUEUE_ALLOC) +* @size: sizeof(struct iommu_hw_queue_alloc) +* @flags: Must be 0 +* @viommu_id: Virtual IOMMU ID to associate the HW queue with +* @type: One of enum iommu_hw_queue_type +* @index: The logical index to the HW queue per virtual IOMMU for a Quoted 2 lines
multi-queue
+ * modelI'm thinking of an alternative way w/o having the user to assign index and allowing the driver to poke object dependency (next patch).
Let's say the index is internally assigned by the driver. so this cmd is just for allowing a hw queue and it's the driver to decide the allocation policy, e.g. in ascending order.
Introduce a new flag in
viommu_opsto indicate to core that the new hw queue should hold a reference to the previous hw queue.
Quoted 6 lines
core maintains a
last_queuefield in viommu. Upon success return from @hw_queue_alloc() the core increments the users refcnt oflast_queue, records the dependency iniommufd_hw_queuestruct, and updateviommu->last_queue.Then the destroy order is naturally guaranteed.
I have thought about that too. It's nice that the core can easily maintain the dependency for the driver.
But there would still need an out_index to mark each dynamically allocated queue. So VMM would know where it should map the queue.
For example, if VMM wants to allocate a queue at its own index=1 without allocating index=0 first, kernel cannot fail that as VMM doesn't provide the index. The only way left for kernel would be to output the allocated queue with index=0 and then wish VMM can validate it, which doesn't sound safe..
Quoted 7 lines
Quoted 5 lines
+ * @out_hw_queue_id: The ID of the new HW queue
+ * @base_addr: Base address of the queue memory in guest physical address space
+ * @length: Length of the queue memory in the guest physical address spacelength is agnostic to address space.
Ack.
* @length: Length of the queue memory
Quoted 14 lines
Quoted 14 lines
drivers/iommu/iommufd/viommu.c +14
-0+int iommufd_hw_queue_alloc_ioctl (struct iommufd_ucmd *ucmd) +{ +struct iommu_hw_queue_alloc *cmd = ucmd->cmd; +struct iommufd_hw_queue *hw_queue; +struct iommufd_hwpt_paging *hwpt; +struct iommufd_viommu *viommu; +struct page **pages; +int max_npages, i; +u64 end; +int rc; + +if (cmd->flags || cmd->type ==IOMMU_HW_QUEUE_TYPE_DEFAULT ) +return -EOPNOTSUPP ; +if (!cmd->base_addr || !cmd->length)
Quoted 8 lines
drivers/iommu/iommufd/viommu.c int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) · line 205
Quoted 5 lines
drivers/iommu/iommufd/viommu.c +5
-0+return -EINVAL ; +if (check_add_overflow (cmd->base_addr, cmd->length -1 , &end)) +return -EOVERFLOW ; + + max_npages =DIV_ROUND_UP (cmd->length,PAGE_SIZE );what about [
base_addr, base_addr+length) spanning two pages but 'length' is smaller than the size of one page?
Ah, right! Probably should be something like:
offset = cmd->base_addr - PAGE_ALIGN(cmd->base_addr); max_npages = DIV_ROUND_UP(offset + cmd->length, PAGE_SIZE);
Quoted 5 lines
drivers/iommu/iommufd/viommu.c int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) · line 208
Quoted 3 lines
drivers/iommu/iommufd/viommu.c +3
-0+ pages =kcalloc (max_npages,sizeof (*pages),GFP_KERNEL ); +if (!pages) +return -ENOMEM ;this could be moved to right before
iopt_pin_pages().
Ack.
Quoted 16 lines
Quoted 16 lines
drivers/iommu/iommufd/viommu.c +15
-0+ + viommu =iommufd_get_viommu (ucmd, cmd->viommu_id); +if (IS_ERR (viommu)) { + rc =PTR_ERR (viommu); + goto out_free; + } + hwpt = viommu->hwpt; + +if (!viommu->ops || !viommu->ops->hw_queue_alloc) { + rc = -EOPNOTSUPP ; + goto out_put_viommu; + } + +/* Quick test on the base address */ +if (!iommu_iova_to_phys (hwpt->common.domain, cmd->base_addr)){
Quoted 8 lines
drivers/iommu/iommufd/viommu.c int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) · line 226
Quoted 3 lines
drivers/iommu/iommufd/viommu.c +3
-0+ rc = -ENXIO ; + goto out_put_viommu; + }this check is redundant. Actually it's not future proof, assuming that S2 must be pinned before the user attempts to call this cmd. But what if one day iommufd supports unpinned S2 (if a device is 100% PRI faultable) then this path will be broken.
OK. Let's drop it.
Quoted 5 lines
Quoted 5 lines
(quoted diff) +1
-0+ hw_queue = viommu->ops->hw_queue_alloc(viommu, cmd->type,
cmd->index,drivers/iommu/iommufd/viommu.c +3
-0+ cmd->base_addr, cmd->length); +if (IS_ERR (hw_queue)) { + rc =PTR_ERR (hw_queue);
Quoted 8 lines
drivers/iommu/iommufd/viommu.c int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) · line 258
Quoted 6 lines
drivers/iommu/iommufd/viommu.c +6
-0+ goto out_unpin; + } + + hw_queue->viommu = viommu; +refcount_inc (&viommu->obj.users); + hw_queue->ictx = ucmd->ictx;viommu/ictx are already set by
iommufd_hw_queue_alloc().
OK. We'd need to be careful if someday there is a core-allocated hw_queue that doesn't call iommufd_hw_queue_alloc(). Maybe I can put a note here.
Thanks
Nicolin
Tian, Kevin
maintainerRE: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 38 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 16, 2025 2:45 AMQuoted 24 lines · Kevin
Quoted 11 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMinclude/uapi/linux/iommufd.h +8
-0+ +/** +* struct iommu_hw_queue_alloc - ioctl(IOMMU_HW_QUEUE_ALLOC) +* @size: sizeof(struct iommu_hw_queue_alloc) +* @flags: Must be 0 +* @viommu_id: Virtual IOMMU ID to associate the HW queue with +* @type: One of enum iommu_hw_queue_type +* @index: The logical index to the HW queue per virtual IOMMU for a Quoted 2 lines
multi-queue
+ * modelI'm thinking of an alternative way w/o having the user to assign index and allowing the driver to poke object dependency (next patch).
Let's say the index is internally assigned by the driver. so this cmd is just for allowing a hw queue and it's the driver to decide the allocation policy, e.g. in ascending order.
Introduce a new flag in
viommu_opsto indicate to core that the new hw queue should hold a reference to the previous hw queue.Quoted 6 lines
core maintains a
last_queuefield in viommu. Upon success return from @hw_queue_alloc() the core increments the users refcnt oflast_queue, records the dependency iniommufd_hw_queuestruct, and updateviommu->last_queue.Then the destroy order is naturally guaranteed.
I have thought about that too. It's nice that the core can easily maintain the dependency for the driver.
Quoted 9 lines
But there would still need an
out_indexto mark each dynamically allocated queue. So VMM would know where it should map the queue.For example, if VMM wants to allocate a queue at its own index=1 without allocating index=0 first, kernel cannot fail that as VMM doesn't provide the index. The only way left for kernel would be to output the allocated queue with index=0 and then wish VMM can validate it, which doesn't sound safe..
VMM's index is virtual which could be mapped to whatever queue object created at its own disposal.
the uAPI just requires VMM to remember a sequential list of allocated queue objects and destroy them in reverse order of allocation, instead of in the reverse order of virtual indexes.
Re: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 48 lines · Kevin
Quoted 38 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 16, 2025 2:45 AMQuoted 24 lines · Kevin
Quoted 11 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMinclude/uapi/linux/iommufd.h +8
-0+ +/** +* struct iommu_hw_queue_alloc - ioctl(IOMMU_HW_QUEUE_ALLOC) +* @size: sizeof(struct iommu_hw_queue_alloc) +* @flags: Must be 0 +* @viommu_id: Virtual IOMMU ID to associate the HW queue with +* @type: One of enum iommu_hw_queue_type +* @index: The logical index to the HW queue per virtual IOMMU for a Quoted 2 lines
multi-queue
+ * modelI'm thinking of an alternative way w/o having the user to assign index and allowing the driver to poke object dependency (next patch).
Let's say the index is internally assigned by the driver. so this cmd is just for allowing a hw queue and it's the driver to decide the allocation policy, e.g. in ascending order.
Introduce a new flag in
viommu_opsto indicate to core that the new hw queue should hold a reference to the previous hw queue.Quoted 6 lines
core maintains a
last_queuefield in viommu. Upon success return from @hw_queue_alloc() the core increments the users refcnt oflast_queue, records the dependency iniommufd_hw_queuestruct, and updateviommu->last_queue.Then the destroy order is naturally guaranteed.
I have thought about that too. It's nice that the core can easily maintain the dependency for the driver.
Quoted 9 lines
But there would still need an
out_indexto mark each dynamically allocated queue. So VMM would know where it should map the queue.For example, if VMM wants to allocate a queue at its own index=1 without allocating index=0 first, kernel cannot fail that as VMM doesn't provide the index. The only way left for kernel would be to output the allocated queue with index=0 and then wish VMM can validate it, which doesn't sound safe..
Quoted 6 lines
VMM's index is virtual which could be mapped to whatever queue object created at its own disposal.
the uAPI just requires VMM to remember a sequential list of allocated queue objects and destroy them in reverse order of allocation, instead of in the reverse order of virtual indexes.
But that's not going to work for VCMDQ.
VINTF mmaps only a single page that controls multiple queues. And all queues have to be mapped correctly between HW and VM indexes. Otherwise, it won't work if VMM maps:
HW-level VINTF1 LVCMDQ0 <==> VM-level VINTF0 LVCMDQ1 HW-level VINTF1 LVCMDQ1 <==> VM-level VINTF0 LVCMDQ0
So, one way or another, kernel has to ensure the static mappings of the indexes. And I think it's safer in the way that VMM tells what index to allocate..
Thanks
Nicolin
Tian, Kevin
maintainerRE: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 66 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 16, 2025 11:17 AMQuoted 7 lines · Kevin
Quoted 7 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 16, 2025 2:45 AMQuoted 3 lines · Kevin
Quoted 3 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMQuoted 8 lines
Quoted 8 lines
Quoted 8 lines
include/uapi/linux/iommufd.h struct iommu_veventq_alloc {
Quoted 8 lines
include/uapi/linux/iommufd.h +8
-0+ +/** +* struct iommu_hw_queue_alloc - ioctl(IOMMU_HW_QUEUE_ALLOC) +* @size: sizeof(struct iommu_hw_queue_alloc) +* @flags: Must be 0 +* @viommu_id: Virtual IOMMU ID to associate the HW queue with +* @type: One of enum iommu_hw_queue_type +* @index: The logical index to the HW queue per virtual IOMMU for a
Quoted 33 lines
Quoted 23 lines
Quoted 13 lines
Quoted 2 lines
multi-queue
+ * modelI'm thinking of an alternative way w/o having the user to assign index and allowing the driver to poke object dependency (next patch).
Let's say the index is internally assigned by the driver. so this cmd is just for allowing a hw queue and it's the driver to decide the allocation policy, e.g. in ascending order.
Introduce a new flag in
viommu_opsto indicate to core that the new hw queue should hold a reference to the previous hw queue.Quoted 6 lines
core maintains a
last_queuefield in viommu. Upon success return from @hw_queue_alloc() the core increments the users refcnt oflast_queue, records the dependency iniommufd_hw_queuestruct, and updateviommu->last_queue.Then the destroy order is naturally guaranteed.
I have thought about that too. It's nice that the core can easily maintain the dependency for the driver.
Quoted 9 lines
But there would still need an
out_indexto mark each dynamically allocated queue. So VMM would know where it should map the queue.For example, if VMM wants to allocate a queue at its own index=1 without allocating index=0 first, kernel cannot fail that as VMM doesn't provide the index. The only way left for kernel would be to output the allocated queue with index=0 and then wish VMM can validate it, which doesn't sound safe..
Quoted 6 lines
VMM's index is virtual which could be mapped to whatever queue object created at its own disposal.
the uAPI just requires VMM to remember a sequential list of allocated queue objects and destroy them in reverse order of allocation, instead of in the reverse order of virtual indexes.
But that's not going to work for VCMDQ.
VINTF mmaps only a single page that controls multiple queues. And all queues have to be mapped correctly between HW and VM indexes. Otherwise, it won't work if VMM maps:
Quoted 7 lines
HW-level VINTF1 LVCMDQ0 <==> VM-level VINTF0 LVCMDQ1 HW-level VINTF1 LVCMDQ1 <==> VM-level VINTF0 LVCMDQ0
So, one way or another, kernel has to ensure the static mappings of the indexes. And I think it's safer in the way that VMM tells what index to allocate..
Okay, that's a valid point.
But hey, we are already adding various restrictions to the uAPI about dependency, contiguity, etc. which the VMM should conform to. What hurts if we further say that the VMM should allocate virtual index in an ascending order along with hw queue allocation?
Re: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 74 lines · Kevin
Quoted 66 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 16, 2025 11:17 AMQuoted 7 lines · Kevin
Quoted 7 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 16, 2025 2:45 AMQuoted 3 lines · Kevin
Quoted 3 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMQuoted 8 lines
Quoted 8 lines
Quoted 8 lines
include/uapi/linux/iommufd.h struct iommu_veventq_alloc {
Quoted 8 lines
include/uapi/linux/iommufd.h +8
-0+ +/** +* struct iommu_hw_queue_alloc - ioctl(IOMMU_HW_QUEUE_ALLOC) +* @size: sizeof(struct iommu_hw_queue_alloc) +* @flags: Must be 0 +* @viommu_id: Virtual IOMMU ID to associate the HW queue with +* @type: One of enum iommu_hw_queue_type +* @index: The logical index to the HW queue per virtual IOMMU for a
Quoted 33 lines
Quoted 23 lines
Quoted 13 lines
Quoted 2 lines
multi-queue
+ * modelI'm thinking of an alternative way w/o having the user to assign index and allowing the driver to poke object dependency (next patch).
Let's say the index is internally assigned by the driver. so this cmd is just for allowing a hw queue and it's the driver to decide the allocation policy, e.g. in ascending order.
Introduce a new flag in
viommu_opsto indicate to core that the new hw queue should hold a reference to the previous hw queue.Quoted 6 lines
core maintains a
last_queuefield in viommu. Upon success return from @hw_queue_alloc() the core increments the users refcnt oflast_queue, records the dependency iniommufd_hw_queuestruct, and updateviommu->last_queue.Then the destroy order is naturally guaranteed.
I have thought about that too. It's nice that the core can easily maintain the dependency for the driver.
Quoted 9 lines
But there would still need an
out_indexto mark each dynamically allocated queue. So VMM would know where it should map the queue.For example, if VMM wants to allocate a queue at its own index=1 without allocating index=0 first, kernel cannot fail that as VMM doesn't provide the index. The only way left for kernel would be to output the allocated queue with index=0 and then wish VMM can validate it, which doesn't sound safe..
Quoted 6 lines
VMM's index is virtual which could be mapped to whatever queue object created at its own disposal.
the uAPI just requires VMM to remember a sequential list of allocated queue objects and destroy them in reverse order of allocation, instead of in the reverse order of virtual indexes.
But that's not going to work for VCMDQ.
VINTF mmaps only a single page that controls multiple queues. And all queues have to be mapped correctly between HW and VM indexes. Otherwise, it won't work if VMM maps:
Quoted 7 lines
HW-level VINTF1 LVCMDQ0 <==> VM-level VINTF0 LVCMDQ1 HW-level VINTF1 LVCMDQ1 <==> VM-level VINTF0 LVCMDQ0
So, one way or another, kernel has to ensure the static mappings of the indexes. And I think it's safer in the way that VMM tells what index to allocate..
Quoted 6 lines
Okay, that's a valid point.
But hey, we are already adding various restrictions to the uAPI about dependency, contiguity, etc. which the VMM should conform to. What hurts if we further say that the VMM should allocate virtual index in an ascending order along with hw queue allocation?
You mean adding another flag to manage the dependency in the core, right?
I talked with Jason offline when adding that depend API. He didn't want it to be in the core, saying that is a driver thing.
But that was before we added pin and contiguity, which he doesn't really enjoy being in the core either.
So, yea, I think you have a point here..
@Jason?
Thanks
Nicolin
Re: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Hi Kevin,
Quoted 9 lines · Nicolin Chen
Quoted 4 lines · Kevin
But hey, we are already adding various restrictions to the uAPI about dependency, contiguity, etc. which the VMM should conform to. What hurts if we further say that the VMM should allocate virtual index in an ascending order along with hw queue allocation?
You mean adding another flag to manage the dependency in the core, right?
Quoted 7 lines
I talked with Jason offline when adding that depend API. He didn't want it to be in the core, saying that is a driver thing.
But that was before we added pin and contiguity, which he doesn't really enjoy being in the core either.
So, yea, I think you have a point here..
It seems Jason is out of office. And in the last sync w.r.t this, he thinks that this ascending order stuff is too unique/weird to make sense as a feature in the core, that there would be unlikely a second HW wanting this..
I think that's a valid point too. The pin/contiguity requirement at least serves for HW that reads in physical address space, and it could result in a slightly faster memory access since it does not need a translation, which though gives software some trouble yet still makes sense IMHO.
So, in v5, I kept the dependency APIs rather than moving to the core. I think we can move to the core later if we see another HW doing the same thing.
Thanks
Nicolin
Jason Gunthorpe
maintainerRe: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
In reply to Nicolin ChenQuoted 9 lines · Nicolin Chen
+/**
+ * structiommu_hw_queue_alloc-ioctl(IOMMU_HW_QUEUE_ALLOC)
+ * @size:sizeof(struct iommu_hw_queue_alloc)
+ * @flags: Must be 0
+ * @viommu_id: Virtual IOMMU ID to associate the HW queue with
+ * @type: One of enumiommu_hw_queue_type
+ * @index: The logical index to the HW queue per virtual IOMMU for a multi-queue
+ * model
+ * @out_hw_queue_id: The ID of the new HW queue
Quoted 8 lines
+ * @base_addr: Base address of the queue memory in guest physical address space
+ * @length: Length of the queue memory in the guest physical address space
+ *
+ * Allocate a HW queue object for a vIOMMU-specific HW-accelerated queue, which
+ * allows HW to access a guest queue memory described by @base_addr and @length.
+ * Upon success, the underlying physical pages of the guest queue memory will be
+ * pinned to prevent VMM from unmapping them in the IOAS until the HW queue gets
+ * destroyed.
Do we have way to make the pinning optional?
As I understand AMD's system the iommu HW itself translates the base_addr through the S2 page table automatically, so it doesn't need pinned memory and physical addresses but just the IOVA.
Perhaps for this reason the pinning should be done with a function call from the driver?
include/uapi/linux/iommufd.h struct iommu_veventq_alloc { · line 1188
Quoted 8 lines
include/uapi/linux/iommufd.h +8
-0+struct iommu_hw_queue_alloc { +__u32 size; +__u32 flags; +__u32 viommu_id; +__u32 type; +__u32 index; +__u32 out_hw_queue_id; +__aligned_u64 base_addr;
base addr should probably be called nesting_parent_iova to match how we described the viommu hwpt:
* @hwpt_id: ID of a nesting parent HWPT to associate to
drivers/iommu/iommufd/viommu.c int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) · line 234
Quoted 7 lines
drivers/iommu/iommufd/viommu.c +7
-0+/* +* The underlying physical pages must be pinned to prevent them from +* being unmapped (via IOMMUFD_CMD_IOAS_UNMAP) during the life cycle +* of the HW QUEUE object. +*/ + rc =iopt_pin_pages (&hwpt->ioas->iopt, cmd->base_addr, cmd->length, + pages,0 );
I don't think this actually works like this without an unmap callback. unmap will break:
iommufd_access_notify_unmap(iopt, area_first, length); /* Something is not responding to unmap requests. */ tries++; if (WARN_ON(tries > 100)) return -EDEADLOCK;
If it can't shoot down the pinning.
Why did this need to change away from just a normal access? That ops and unmap callback are not optional things.
What vcmdq would do in the unmap callback is question I'm not sure of..
This is more reason to put the pin/access in the driver so it can provide an unmap callback that can fix it up. I think this should have been done just by using the normal access mechanism, maybe with a simplifying wrapper for in-driver use. ie no need for patch #9
Jason
Re: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 19 lines · Jason Gunthorpe
Quoted 9 lines · Nicolin Chen
+/**
+ * structiommu_hw_queue_alloc-ioctl(IOMMU_HW_QUEUE_ALLOC)
+ * @size:sizeof(struct iommu_hw_queue_alloc)
+ * @flags: Must be 0
+ * @viommu_id: Virtual IOMMU ID to associate the HW queue with
+ * @type: One of enumiommu_hw_queue_type
+ * @index: The logical index to the HW queue per virtual IOMMU for a multi-queue
+ * model
+ * @out_hw_queue_id: The ID of the new HW queueQuoted 8 lines
+ * @base_addr: Base address of the queue memory in guest physical address space
+ * @length: Length of the queue memory in the guest physical address space
+ *
+ * Allocate a HW queue object for a vIOMMU-specific HW-accelerated queue, which
+ * allows HW to access a guest queue memory described by @base_addr and @length.
+ * Upon success, the underlying physical pages of the guest queue memory will be
+ * pinned to prevent VMM from unmapping them in the IOAS until the HW queue gets
+ * destroyed.
Quoted 5 lines
Do we have way to make the pinning optional?
As I understand AMD's system the iommu HW itself translates the
base_addrthrough the S2 page table automatically, so it doesn't need pinned memory and physical addresses but just the IOVA.
Right. That's why we invented a flag, and it should be probably extended to cover the pin step as well.
Quoted 2 lines
Perhaps for this reason the pinning should be done with a function call from the driver?
But the whole point of doing in the core was to avoid the entire iopt related structure/function from being exposed to the driver, which would otherwise hugely increase the size of the driver.o?
Quoted 12 lines
include/uapi/linux/iommufd.h struct iommu_veventq_alloc { · line 1188
Quoted 8 lines
include/uapi/linux/iommufd.h +8
-0+struct iommu_hw_queue_alloc { +__u32 size; +__u32 flags; +__u32 viommu_id; +__u32 type; +__u32 index; +__u32 out_hw_queue_id; +__aligned_u64 base_addr;base addr should probably be called
nesting_parent_iovato match how we described the viommu hwpt:
Quoted 1 line
* @hwpt_id: ID of a nesting parent HWPT to associate to
Ack.
Quoted 11 lines
drivers/iommu/iommufd/viommu.c int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) · line 234
Quoted 7 lines
drivers/iommu/iommufd/viommu.c +7
-0+/* +* The underlying physical pages must be pinned to prevent them from +* being unmapped (via IOMMUFD_CMD_IOAS_UNMAP) during the life cycle +* of the HW QUEUE object. +*/ + rc =iopt_pin_pages (&hwpt->ioas->iopt, cmd->base_addr, cmd->length, + pages,0 );I don't think this actually works like this without an unmap callback. unmap will break:
Quoted 7 lines
iommufd_access_notify_unmap(iopt, area_first, length); /* Something is not responding to unmap requests. */ tries++; if (WARN_ON(tries > 100)) return -EDEADLOCK;If it can't shoot down the pinning.
Hmm, I thought we want the unmap to fail until VMM releases the HW QUEUE first? In what case, does VMM wants to unmap while holding the queue pages?
Quoted 8 lines
Why did this need to change away from just a normal access? That ops and unmap callback are not optional things.
What vcmdq would do in the unmap callback is question I'm not sure of..
This is more reason to put the pin/access in the driver so it can provide an unmap callback that can fix it up.
As there are two types of "access" here.. you mean iommufd_access, i.e. vcmdq driver should hold an iommufd_access like an emulated vfio device driver?
Quoted 3 lines
I think this should have
been done just by using the normal access mechanism, maybe with a simplifying wrapper for in-driver use. ie no need for patch #9
Still, the driver.o file will be very large, unlike VFIO that just depends on CONFIG_IOMMUFD?
Thanks
Nicolin
Jason Gunthorpe
maintainerRe: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 6 lines · Nicolin Chen
Quoted 3 lines
As I understand AMD's system the iommu HW itself translates the
base_addrthrough the S2 page table automatically, so it doesn't need pinned memory and physical addresses but just the IOVA.Right. That's why we invented a flag, and it should be probably extended to cover the pin step as well.
Yes, no pin
Quoted 6 lines
Quoted 2 lines
Perhaps for this reason the pinning should be done with a function call from the driver?
But the whole point of doing in the core was to avoid the entire iopt related structure/function from being exposed to the driver, which would otherwise hugely increase the size of the driver.o?
Ugh, yes, but also, maybe we need to figure something else out for this. Pass down a function pointers struct to the driver or something like that?
Quoted 9 lines
Quoted 9 lines
I don't think this actually works like this without an unmap callback. unmap will break:
iommufd_access_notify_unmap(iopt, area_first, length); /* Something is not responding to unmap requests. */ tries++; if (WARN_ON(tries > 100)) return -EDEADLOCK;
Quoted 5 lines
Quoted 1 line
If it can't shoot down the pinning.
Hmm, I thought we want the unmap to fail until VMM releases the HW QUEUE first? In what case, does VMM wants to unmap while holding the queue pages?
Well, if that is what we want to do then this needs to be revised somehow..
Quoted 6 lines
Quoted 2 lines
This is more reason to put the pin/access in the driver so it can provide an unmap callback that can fix it up.
As there are two types of "access" here.. you mean
iommufd_access, i.e. vcmdq driver should hold aniommufd_accesslike an emulated vfio device driver?
Yes.
Jason
Re: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 10 lines · Jason Gunthorpe
Quoted 9 lines · Nicolin Chen
Quoted 9 lines
I don't think this actually works like this without an unmap callback. unmap will break:
iommufd_access_notify_unmap(iopt, area_first, length); /* Something is not responding to unmap requests. */ tries++; if (WARN_ON(tries > 100)) return -EDEADLOCK;
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
If it can't shoot down the pinning.
Hmm, I thought we want the unmap to fail until VMM releases the HW QUEUE first? In what case, does VMM wants to unmap while holding the queue pages?
Well, if that is what we want to do then this needs to be revised somehow..
Yea, unless we have a strong reason to allow unmap while holding the HW queue.
I think we could set a new flag:
enum {
IOMMUFD_ACCESS_RW_READ = 0,
IOMMUFD_ACCESS_RW_WRITE = 1 << 0,
/* Set if the caller is in a kthread then rw will use kthread_use_mm() */ IOMMUFD_ACCESS_RW_KTHREAD = 1 << 1,
+ IOMMUFD_ACCESS_NO_UNMAP = 1 << 3,
/* Only for use by selftest */
__IOMMUFD_ACCESS_RW_SLOW_PATH = 1 << 2,
};
and reject iopt_unmap_iova_range().
Thanks
Nicolin
Jason Gunthorpe
maintainerRe: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 36 lines · Nicolin Chen
Quoted 10 lines · Jason Gunthorpe
Quoted 9 lines · Nicolin Chen
Quoted 9 lines
I don't think this actually works like this without an unmap callback. unmap will break:
iommufd_access_notify_unmap(iopt, area_first, length); /* Something is not responding to unmap requests. */ tries++; if (WARN_ON(tries > 100)) return -EDEADLOCK;Quoted 8 lines
Quoted 5 lines
Quoted 1 line
If it can't shoot down the pinning.
Hmm, I thought we want the unmap to fail until VMM releases the HW QUEUE first? In what case, does VMM wants to unmap while holding the queue pages?
Well, if that is what we want to do then this needs to be revised somehow..
Yea, unless we have a strong reason to allow unmap while holding the HW queue.
I think we could set a new flag:
enum {
(quoted diff) +1
-0IOMMUFD_ACCESS_RW_READ = 0, IOMMUFD_ACCESS_RW_WRITE = 1 << 0, /* Set if the caller is in a kthread then rw will use kthread_use_mm() */ IOMMUFD_ACCESS_RW_KTHREAD = 1 << 1, + IOMMUFD_ACCESS_NO_UNMAP = 1 << 3, /* Only for use by selftest */ __IOMMUFD_ACCESS_RW_SLOW_PATH = 1 << 2, };
Quoted 1 line
and reject
iopt_unmap_iova_range().
Okay, it would need a patch for this too. I think we wanted to limit this no_unmap behavior though. Linking it to deliberate action that the user took to create a vqueue with a user provided address seems reasonable
I would probably put the flag out of the public header though, just to prevent abuse from mdev drivers.
Jason
Tian, Kevin
maintainerRE: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
In reply to Jason GunthorpeQuoted 9 lines
From: Jason Gunthorpe <[email protected]>
Sent: Friday, May 16, 2025 12:06 AMDo we have way to make the pinning optional?
As I understand AMD's system the iommu HW itself translates the
base_addrthrough the S2 page table automatically, so it doesn't need pinned memory and physical addresses but just the IOVA.
Though using IOVA could eliminate pinning conceptually, implementation wise an IOMMU may not tolerate translation errors in its access to guest queues with assumption that S2 is pinned.
@Vasant, can you help confirm?
Jason Gunthorpe
maintainerRe: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 5 lines · Kevin
Quoted 5 lines
From: Jason Gunthorpe <[email protected]>
Sent: Friday, May 16, 2025 12:06 AMDo we have way to make the pinning optional?
Quoted 8 lines
Quoted 4 lines
As I understand AMD's system the iommu HW itself translates the
base_addrthrough the S2 page table automatically, so it doesn't need pinned memory and physical addresses but just the IOVA.Though using IOVA could eliminate pinning conceptually, implementation wise an IOMMU may not tolerate translation errors in its access to guest queues with assumption that S2 is pinned.
Yes, but the entire S2 is pinned today. This isn't about transient unmap..
If the VMM decides to unmap the memory, eg with hotunplug or something, then I'd fully expect the IOMMU to take a fault and forward the error to the guest. Guest make a mistake to put the queue in memory that was hot-unplugged.
Jason
Re: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
In reply to Jason GunthorpeJason, Nicolin, Kevin,
Quoted 19 lines · Jason Gunthorpe
Quoted 9 lines · Nicolin Chen
+/**
+ * structiommu_hw_queue_alloc-ioctl(IOMMU_HW_QUEUE_ALLOC)
+ * @size:sizeof(struct iommu_hw_queue_alloc)
+ * @flags: Must be 0
+ * @viommu_id: Virtual IOMMU ID to associate the HW queue with
+ * @type: One of enumiommu_hw_queue_type
+ * @index: The logical index to the HW queue per virtual IOMMU for a multi-queue
+ * model
+ * @out_hw_queue_id: The ID of the new HW queueQuoted 8 lines
+ * @base_addr: Base address of the queue memory in guest physical address space
+ * @length: Length of the queue memory in the guest physical address space
+ *
+ * Allocate a HW queue object for a vIOMMU-specific HW-accelerated queue, which
+ * allows HW to access a guest queue memory described by @base_addr and @length.
+ * Upon success, the underlying physical pages of the guest queue memory will be
+ * pinned to prevent VMM from unmapping them in the IOAS until the HW queue gets
+ * destroyed.
Quoted 5 lines
Do we have way to make the pinning optional?
As I understand AMD's system the iommu HW itself translates the
base_addrthrough the S2 page table automatically, so it doesn't need pinned memory and physical addresses but just the IOVA.
Correct. HW will translate GPA -> SPA automatically using below information.
AMD IOMMU need special device ID to setup with GPA -> SPA mapping per VM. and its programmed in VF Control BAR (VFCntlMMIO Offset {16’b[GuestID], 6’b01_0000} Guest Miscellaneous Control Register). IOMMU HW will use this address for GPA to SPA translation for buffers like command buffer.
So HW will use Base address (GPA), head/tail pointer to get the offset from Base. Then it will use GPA -> SPA translation.
Quoted 3 lines
Perhaps for this reason the pinning should be done with a function call from the driver?
We still need to make sure memory allocated for page is present in memory so that IOMMU HW can access it.
Pinning at the time of guest boot is enough here -OR- do we need to increase reference in queue_alloc() path ?
-Vasant
Re: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 41 lines · Vasant Hegde
Jason, Nicolin, Kevin,
Quoted 19 lines · Jason Gunthorpe
Quoted 9 lines · Nicolin Chen
+/**
+ * structiommu_hw_queue_alloc-ioctl(IOMMU_HW_QUEUE_ALLOC)
+ * @size:sizeof(struct iommu_hw_queue_alloc)
+ * @flags: Must be 0
+ * @viommu_id: Virtual IOMMU ID to associate the HW queue with
+ * @type: One of enumiommu_hw_queue_type
+ * @index: The logical index to the HW queue per virtual IOMMU for a multi-queue
+ * model
+ * @out_hw_queue_id: The ID of the new HW queueQuoted 8 lines
+ * @base_addr: Base address of the queue memory in guest physical address space
+ * @length: Length of the queue memory in the guest physical address space
+ *
+ * Allocate a HW queue object for a vIOMMU-specific HW-accelerated queue, which
+ * allows HW to access a guest queue memory described by @base_addr and @length.
+ * Upon success, the underlying physical pages of the guest queue memory will be
+ * pinned to prevent VMM from unmapping them in the IOAS until the HW queue gets
+ * destroyed.Quoted 5 lines
Do we have way to make the pinning optional?
As I understand AMD's system the iommu HW itself translates the
base_addrthrough the S2 page table automatically, so it doesn't need pinned memory and physical addresses but just the IOVA.Correct. HW will translate GPA -> SPA automatically using below information.
AMD IOMMU need special device ID to setup with GPA -> SPA mapping per VM. and its programmed in VF Control BAR (VFCntlMMIO Offset {16’b[GuestID], 6’b01_0000} Guest Miscellaneous Control Register). IOMMU HW will use this address for GPA to SPA translation for buffers like command buffer.
So HW will use Base address (GPA), head/tail pointer to get the offset from Base. Then it will use GPA -> SPA translation.
Quoted 1 line
Quoted 8 lines
Quoted 2 lines
Perhaps for this reason the pinning should be done with a function call from the driver?
We still need to make sure memory allocated for page is present in memory so that IOMMU HW can access it.
Pinning at the time of guest boot is enough here -OR- do we need to increase reference in
queue_alloc()path ?
For NVIDIA's vCMDQ that reads host PA directly, pages should be pinned once when stage 2 mappings are created for the guest RAM, and iommu_hw_queue_alloc() should pin the pages again to prevent the gPA from being unmapped in the stage 2 page table. Otherwise it will be a security hole, as HW continues to read the unmapped memory through physical address space.
I understand that AMD Command Buffer also needs the S2 mappings to be present in order to work correctly. But what happens if a queue memory that isn't pinned (or even gets unmapped)? Will it raise a translation fault v.s. HW reading the unmapped memory?
If so, I think this is Jason's point: there would be unlikely a security hole, i.e. for AMD, iommu_hw_queue_alloc() pinning the physical pages is likely optional.
Thanks
Nicolin
Re: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Hi Nicolin,
Quoted 58 lines · Nicolin Chen
Quoted 41 lines · Vasant Hegde
Jason, Nicolin, Kevin,
Quoted 19 lines · Jason Gunthorpe
Quoted 9 lines · Nicolin Chen
+/**
+ * structiommu_hw_queue_alloc-ioctl(IOMMU_HW_QUEUE_ALLOC)
+ * @size:sizeof(struct iommu_hw_queue_alloc)
+ * @flags: Must be 0
+ * @viommu_id: Virtual IOMMU ID to associate the HW queue with
+ * @type: One of enumiommu_hw_queue_type
+ * @index: The logical index to the HW queue per virtual IOMMU for a multi-queue
+ * model
+ * @out_hw_queue_id: The ID of the new HW queueQuoted 8 lines
+ * @base_addr: Base address of the queue memory in guest physical address space
+ * @length: Length of the queue memory in the guest physical address space
+ *
+ * Allocate a HW queue object for a vIOMMU-specific HW-accelerated queue, which
+ * allows HW to access a guest queue memory described by @base_addr and @length.
+ * Upon success, the underlying physical pages of the guest queue memory will be
+ * pinned to prevent VMM from unmapping them in the IOAS until the HW queue gets
+ * destroyed.Quoted 5 lines
Do we have way to make the pinning optional?
As I understand AMD's system the iommu HW itself translates the
base_addrthrough the S2 page table automatically, so it doesn't need pinned memory and physical addresses but just the IOVA.Correct. HW will translate GPA -> SPA automatically using below information.
AMD IOMMU need special device ID to setup with GPA -> SPA mapping per VM. and its programmed in VF Control BAR (VFCntlMMIO Offset {16’b[GuestID], 6’b01_0000} Guest Miscellaneous Control Register). IOMMU HW will use this address for GPA to SPA translation for buffers like command buffer.
So HW will use Base address (GPA), head/tail pointer to get the offset from Base. Then it will use GPA -> SPA translation.
Quoted 1 line
Quoted 8 lines
Quoted 2 lines
Perhaps for this reason the pinning should be done with a function call from the driver?
We still need to make sure memory allocated for page is present in memory so that IOMMU HW can access it.
Pinning at the time of guest boot is enough here -OR- do we need to increase reference in
queue_alloc()path ?For NVIDIA's vCMDQ that reads host PA directly, pages should be pinned once when stage 2 mappings are created for the guest RAM, and
iommu_hw_queue_alloc()should pin the pages again to prevent the gPA from being unmapped in the stage 2 page table. Otherwise it will be a security hole, as HW continues to read the unmapped memory through physical address space.
Quoted 4 lines
I understand that AMD Command Buffer also needs the S2 mappings to be present in order to work correctly. But what happens if a queue memory that isn't pinned (or even gets unmapped)? Will it raise a translation fault v.s. HW reading the unmapped memory?
If page is unmapped then stage 2 (Host page table) gets updated. IOMMU will not be able to find page and logs fault.
Quoted 4 lines
If so, I think this is Jason's point: there would be unlikely a security hole, i.e. for AMD,
iommu_hw_queue_alloc()pinning the physical pages is likely optional.
I think so.
-Vasant
Tian, Kevin
maintainerRE: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 79 lines
From: Vasant Hegde <[email protected]>
Sent: Tuesday, May 20, 2025 4:39 PMHi Nicolin,
Quoted 5 lines · Nicolin Chen
Quoted 4 lines · Vasant Hegde
Jason, Nicolin, Kevin,
On 5/15/2025 9:36 PM, Jason Gunthorpe wrote:
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines · Nicolin Chen
Quoted 7 lines
+/**
+ * structiommu_hw_queue_alloc-ioctl(IOMMU_HW_QUEUE_ALLOC)
+ * @size:sizeof(struct iommu_hw_queue_alloc)
+ * @flags: Must be 0
+ * @viommu_id: Virtual IOMMU ID to associate the HW queue with
+ * @type: One of enumiommu_hw_queue_type
+ * @index: The logical index to the HW queue per virtual IOMMU for amulti-queue
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
+ * model
+ * @out_hw_queue_id: The ID of the new HW queue
+ * @base_addr: Base address of the queue memory in guest physicaladdress space
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
+ * @length: Length of the queue memory in the guest physical address
space
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
+ *
+ * Allocate a HW queue object for a vIOMMU-specific HW-acceleratedqueue, which
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
+ * allows HW to access a guest queue memory described by
@base_addr and @length.
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
+ * Upon success, the underlying physical pages of the guest queue
memory will be
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
+ * pinned to prevent VMM from unmapping them in the IOAS until the
HW queue gets
Quoted 9 lines
Quoted 9 lines
Quoted 7 lines
Quoted 1 line
+ * destroyed.
Do we have way to make the pinning optional?
As I understand AMD's system the iommu HW itself translates the
base_addrthrough the S2 page table automatically, so it doesn't need pinned memory and physical addresses but just the IOVA.Correct. HW will translate GPA -> SPA automatically using below
information.
Quoted 2 lines
Quoted 2 lines
AMD IOMMU need special device ID to setup with GPA -> SPA mapping
per VM.
Quoted 1 line
Quoted 1 line
and its programmed in VF Control BAR (VFCntlMMIO Offset
{16’b[GuestID],
Quoted 1 line
Quoted 1 line
6’b01_0000} Guest Miscellaneous Control Register). IOMMU HW will use
this
Quoted 3 lines
Quoted 3 lines
address for GPA to SPA translation for buffers like command buffer.
So HW will use Base address (GPA), head/tail pointer to get the offset
from
Quoted 8 lines
Quoted 8 lines
Base. Then it will use GPA -> SPA translation.
Quoted 3 lines
Perhaps for this reason the pinning should be done with a function call from the driver?
We still need to make sure memory allocated for page is present in
memory so
Quoted 3 lines
Quoted 3 lines
that IOMMU HW can access it.
Pinning at the time of guest boot is enough here -OR- do we need to
increase
Quoted 9 lines
Quoted 1 line
reference in
queue_alloc()path ?For NVIDIA's vCMDQ that reads host PA directly, pages should be pinned once when stage 2 mappings are created for the guest RAM, and
iommu_hw_queue_alloc()should pin the pages again to prevent the gPA from being unmapped in the stage 2 page table. Otherwise it will be a security hole, as HW continues to read the unmapped memory through physical address space.
Quoted 9 lines
Quoted 4 lines
I understand that AMD Command Buffer also needs the S2 mappings to be present in order to work correctly. But what happens if a queue memory that isn't pinned (or even gets unmapped)? Will it raise a translation fault v.s. HW reading the unmapped memory?
If page is unmapped then stage 2 (Host page table) gets updated. IOMMU will not
be able to find page and logs fault.
As long as the fault is contained only for the relevant queue, yes we don't need another pinning from the driver.
Jason Gunthorpe
maintainerRe: [PATCH v4 11/23] iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl
Quoted 6 lines · Kevin
Quoted 3 lines
If page is unmapped then stage 2 (Host page table) gets updated. IOMMU will not
be able to find page and logs fault.As long as the fault is contained only for the relevant queue, yes we don't need another pinning from the driver.
Yeah, that's my expectation too. If the guest uses a bad address then the fault should be logged in HW and forwarded to the guest. No need to validate the guest IOVA for queues.
Jason
PATCH v4 12/23 iommufd/driver: Add iommufd_hw_queue_depend/undepend() helpers
In reply to Nicolin ChenNVIDIA Virtual Command Queue is one of the iommufd users exposing vIOMMU features to user space VMs. Its hardware has a strict rule when mapping and unmapping multiple global CMDQVs to/from a VM-owned VINTF, requiring mappings in ascending order and unmappings in descending order.
The tegra241-cmdqv driver can apply the rule for a mapping in the LVCMDQ allocation handler. However, it can't do the same for an unmapping since user space could start random destroy calls breaking the rule, while the destroy op in the driver level can't reject a destroy call as it returns void.
Add iommufd_hw_queue_depend/undepend for-driver helpers, allowing LVCMDQ allocator to refcount_inc() a sibling LVCMDQ object and LVCMDQ destroyer to refcount_dec(), so that iommufd core will help block a random destroy call that breaks the rule.
This is a bit of compromise, because a driver might end up with abusing the API that deadlocks the objects. So restrict the API to a dependency between two driver-allocated objects of the same type, as iommufd would unlikely build any core-level dependency in this case. And encourage to use the macro version that currently supports the HW QUEUE objects only.
--- include/linux/iommufd.h | 44 ++++++++++++++++++++++++++++++++++ drivers/iommu/iommufd/driver.c | 28 ++++++++++++++++++++++ 2 files changed, 72 insertions(+)
include/linux/iommufd.h +44 -0
@@ -241,6 +241,10 @@ struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx,size_t size, enum iommufd_object_type type);void iommufd_object_abort (struct iommufd_ctx *ictx,struct iommufd_object *obj); +int _iommufd_object_depend (struct iommufd_object *obj_dependent, +struct iommufd_object *obj_depended); +void _iommufd_object_undepend (struct iommufd_object *obj_dependent, +struct iommufd_object *obj_depended);struct device *iommufd_viommu_find_dev (struct iommufd_viommu *viommu,unsigned long vdev_id); int iommufd_viommu_get_vdev_id(struct iommufd_viommu *viommu,
@@ -261,6 +265,18 @@ static inline void iommufd_object_abort(struct iommufd_ctx *ictx, { } +static inline int _iommufd_object_depend (struct iommufd_object *obj_dependent, +struct iommufd_object *obj_depended) +{ +return -EOPNOTSUPP ; +} + +static inline void +_iommufd_object_undepend (struct iommufd_object *obj_dependent, +struct iommufd_object *obj_depended) +{ +} +static inline struct device *iommufd_viommu_find_dev (struct iommufd_viommu *viommu,unsigned long vdev_id) {
@@ -346,4 +362,32 @@ static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu,iommufd_object_abort (drv_struct->member.ictx, \ &drv_struct->member.obj); \ }) + +/* +* Helpers for IOMMU driver to build/destroy a dependency between two sibling +* structures created by one of the allocators above +*/ +#define iommufd_hw_queue_depend (dependent, depended, member) \ + ({ \ + static_assert(__same_type(struct iommufd_hw_queue, \ + dependent->member)); \ + static_assert(offsetof(typeof(*dependent), member.obj) == 0); \ + static_assert(__same_type(struct iommufd_hw_queue, \ + depended->member)); \ + static_assert(offsetof(typeof(*depended), member.obj) == 0); \ + _iommufd_object_depend(&dependent->member.obj, \ + &depended->member.obj); \ + }) + +#define iommufd_hw_queue_undepend (dependent, depended, member) \ + ({ \ + static_assert(__same_type(struct iommufd_hw_queue, \ + dependent->member)); \ + static_assert(offsetof(typeof(*dependent), member.obj) == 0); \ + static_assert(__same_type(struct iommufd_hw_queue, \ + depended->member)); \ + static_assert(offsetof(typeof(*depended), member.obj) == 0); \ + _iommufd_object_undepend(&dependent->member.obj, \ + &depended->member.obj); \ + })#endif
drivers/iommu/iommufd/driver.c +28 -0
@@ -50,6 +50,34 @@ void iommufd_object_abort(struct iommufd_ctx *ictx, struct iommufd_object *obj) }EXPORT_SYMBOL_NS_GPL (iommufd_object_abort,"IOMMUFD" ); +/* Driver should use a per-structure helper in include/linux/iommufd.h */ +int _iommufd_object_depend (struct iommufd_object *obj_dependent, +struct iommufd_object *obj_depended) +{ +/* Reject self dependency that dead locks */ +if (obj_dependent == obj_depended) +return -EINVAL ; +/* Only support dependency between two objects of the same type */ +if (obj_dependent->type != obj_depended->type) +return -EINVAL ; + +refcount_inc (&obj_depended->users); +return 0 ; +} +EXPORT_SYMBOL_NS_GPL (_iommufd_object_depend,"IOMMUFD" ); + +/* Driver should use a per-structure helper in include/linux/iommufd.h */ +void _iommufd_object_undepend (struct iommufd_object *obj_dependent, +struct iommufd_object *obj_depended) +{ +if (WARN_ON_ONCE (obj_dependent == obj_depended || + obj_dependent->type != obj_depended->type)) +return ; + +refcount_dec (&obj_depended->users); +} +EXPORT_SYMBOL_NS_GPL (_iommufd_object_undepend,"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)
Signature · 1 line
2.43.0
Jason Gunthorpe
maintainerRe: [PATCH v4 12/23] iommufd/driver: Add iommufd_hw_queue_depend/undepend() helpers
Quoted 5 lines · Nicolin Chen
include/linux/iommufd.h +5
-0+/* +* Helpers for IOMMU driver to build/destroy a dependency between two sibling +* structures created by one of the allocators above +*/ +#define iommufd_hw_queue_depend (dependent, depended, member) \
include/linux/iommufd.h static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, · line 380
Quoted 10 lines
include/linux/iommufd.h +10
-0+ ({ \ +static_assert (__same_type(struct iommufd_hw_queue , \ + dependent->member)); \ + static_assert(offsetof(typeof(*dependent), member.obj) == 0); \ +static_assert (__same_type(struct iommufd_hw_queue , \ + depended->member)); \ + static_assert(offsetof(typeof(*depended), member.obj) == 0); \ +_iommufd_object_depend (&dependent->member.obj, \ + &depended->member.obj); \ + })
This doesn't need the offsetof == 0 checks, it isn't an allocator. And you want to check that the two structs have the same type:
static_assert(__same_type(struct iommufd_hw_queue, dependent->member)); static_assert(__same_type(typeof(*dependent), *dependend));
Jason
Re: [PATCH v4 12/23] iommufd/driver: Add iommufd_hw_queue_depend/undepend() helpers
Quoted 21 lines · Jason Gunthorpe
Quoted 5 lines · Nicolin Chen
include/linux/iommufd.h +5
-0+/* +* Helpers for IOMMU driver to build/destroy a dependency between two sibling +* structures created by one of the allocators above +*/ +#define iommufd_hw_queue_depend (dependent, depended, member) \include/linux/iommufd.h static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu, · line 380
Quoted 10 lines
include/linux/iommufd.h +10
-0+ ({ \ +static_assert (__same_type(struct iommufd_hw_queue , \ + dependent->member)); \ + static_assert(offsetof(typeof(*dependent), member.obj) == 0); \ +static_assert (__same_type(struct iommufd_hw_queue , \ + depended->member)); \ + static_assert(offsetof(typeof(*depended), member.obj) == 0); \ +_iommufd_object_depend (&dependent->member.obj, \ + &depended->member.obj); \ + })This doesn't need the offsetof == 0 checks, it isn't an allocator. And you want to check that the two structs have the same type:
Quoted 2 lines
static_assert(__same_type(struct iommufd_hw_queue, dependent->member));static_assert(__same_type(typeof(*dependent), *dependend));
Ack. I also added ictx comparison:
+#define iommufd_hw_queue_depend(dependent, depended, member) \
+ ({ \
+ int ret = -EINVAL; \
+ \
+ static_assert(__same_type(struct iommufd_hw_queue, \
+ dependent->member)); \
+ static_assert(__same_type(typeof(*dependent), *depended)); \
+ if (!WARN_ON_ONCE(dependent->member.ictx != \
+ depended->member.ictx)) \
+ ret = _iommufd_object_depend(&dependent->member.obj, \
+ &depended->member.obj); \
+ ret; \
+ })
+
+#define iommufd_hw_queue_undepend(dependent, depended, member) \
+ ({ \
+ static_assert(__same_type(struct iommufd_hw_queue, \
+ dependent->member)); \
+ static_assert(__same_type(typeof(*dependent), *depended)); \+ WARN_ON_ONCE(dependent->member.ictx != depended->member.ictx); \
+ _iommufd_object_undepend(&dependent->member.obj, \
+ &depended->member.obj); \
+ })
Thanks
Nicolin
PATCH v4 13/23 iommufd/selftest: Add coverage for IOMMUFD_CMD_HW_QUEUE_ALLOC
In reply to Nicolin ChenSome simple tests for IOMMUFD_CMD_HW_QUEUE_ALLOC infrastructure covering the new iommufd_hw_queue_depend/undepend() helpers.
Since the iommufd_hw_queue_alloc_ioctl() will call iommu_iova_to_phys(), for input address validation. It might intentionally pass in a bad input for a negative test. So drop the WARN_ON() in mock_domain_iova_to_phys().
--- drivers/iommu/iommufd/iommufd_test.h | 3 + tools/testing/selftests/iommu/iommufd_utils.h | 31 ++++++++ drivers/iommu/iommufd/selftest.c | 73 ++++++++++++++++++- tools/testing/selftests/iommu/iommufd.c | 60 +++++++++++++++ .../selftests/iommu/iommufd_fail_nth.c | 6 ++ 5 files changed, 172 insertions(+), 1 deletion(-)
drivers/iommu/iommufd/iommufd_test.h +3 -0
@@ -265,4 +265,7 @@ struct iommu_viommu_event_selftest { __u32 virt_id; }; +#define IOMMU_HW_QUEUE_TYPE_SELFTEST 0xdeadbeef +#define IOMMU_TEST_HW_QUEUE_MAX 2 +#endif
tools/testing/selftests/iommu/iommufd_utils.h +31 -0
@@ -956,6 +956,37 @@ static int _test_cmd_vdevice_alloc(int fd, __u32 viommu_id, __u32 idev_id, _test_cmd_vdevice_alloc(self->fd, viommu_id, idev_id, \ virt_id, vdev_id)) +static int _test_cmd_hw_queue_alloc (int fd,__u32 viommu_id,__u32 type, +__u32 idx,__u64 base_addr,__u64 length, +__u32 *hw_queue_id) +{ +struct iommu_hw_queue_alloc cmd = { + .size =sizeof (cmd), + .viommu_id = viommu_id, + .type = type, + .index = idx, + .base_addr = base_addr, + .length = length, + }; +int ret; + + ret =ioctl (fd,IOMMU_HW_QUEUE_ALLOC , &cmd); +if (ret) +return ret; +if (hw_queue_id) + *hw_queue_id = cmd.out_hw_queue_id; +return 0 ; +} + +#define test_cmd_hw_queue_alloc (viommu_id, type, idx, base_addr, len, out_qid) \ + ASSERT_EQ(0, _test_cmd_hw_queue_alloc(self->fd, viommu_id, type, idx, \ + base_addr, len, out_qid)) +#define test_err_hw_queue_alloc (_errno, viommu_id, type, idx, base_addr, len, \ + out_qid) \ + EXPECT_ERRNO(_errno, \ + _test_cmd_hw_queue_alloc(self->fd, viommu_id, type, idx, \ + base_addr, len, out_qid)) +static int _test_cmd_veventq_alloc (int fd,__u32 viommu_id,__u32 type,__u32 *veventq_id,__u32 *veventq_fd) {
drivers/iommu/iommufd/selftest.c +72 -1
101 lines not on this page · raw message
tools/testing/selftests/iommu/iommufd.c +60 -0
66 lines not on this page · raw message
tools/testing/selftests/iommu/iommufd_fail_nth.c +6 -0
@@ -634,6 +634,7 @@ TEST_FAIL_NTH(basic_fail_nth, device)uint32_t idev_id;uint32_t hwpt_id;uint32_t viommu_id; +uint32_t hw_queue_id;uint32_t vdev_id;__u64 iova;
@@ -696,6 +697,11 @@ TEST_FAIL_NTH(basic_fail_nth, device)if (_test_cmd_vdevice_alloc (self->fd, viommu_id, idev_id,0 , &vdev_id))return -1 ; +if (_test_cmd_hw_queue_alloc (self->fd, viommu_id, +IOMMU_HW_QUEUE_TYPE_SELFTEST ,0 , iova, +PAGE_SIZE , &hw_queue_id)) +return -1 ; +if (_test_ioctl_fault_alloc (self->fd, &fault_id, &fault_fd))return -1 ;close (fault_fd);
Signature · 1 line
2.43.0
PATCH v4 14/23 iommufd: Add mmap interface
In reply to Nicolin ChenFor vIOMMU passing through HW resources to user space (VMs), allowing a VM to control the passed through HW directly by accessing hardware registers, add an mmap infrastructure to map the physical MMIO pages to user space.
Maintain an maple tree per ictx as a translation table managing mmappable regions, from an allocated for-user mmap offset to an iommufd_mmap struct, where it stores the real PFN range for a remap_pfn_range call.
To allow an IOMMU driver to add and delete mmappable regions onto/from the maple tree, add iommufd_viommu_alloc/destroy_mmap helpers.
--- drivers/iommu/iommufd/iommufd_private.h | 11 +++++ include/linux/iommufd.h | 39 ++++++++++++++++ drivers/iommu/iommufd/driver.c | 52 +++++++++++++++++++++ drivers/iommu/iommufd/main.c | 61 +++++++++++++++++++++++++ 4 files changed, 163 insertions(+)
drivers/iommu/iommufd/iommufd_private.h +11 -0
@@ -7,6 +7,7 @@#include <linux/iommu.h> #include <linux/iommufd.h> #include <linux/iova_bitmap.h> +#include <linux/maple_tree.h> #include <linux/rwsem.h> #include <linux/uaccess.h> #include <linux/xarray.h>
@@ -44,6 +45,7 @@ struct iommufd_ctx {struct xarray groups;wait_queue_head_t destroy_wait;struct rw_semaphore ioas_creation_lock; +struct maple_tree mt_mmap;struct mutex sw_msi_lock;struct list_head sw_msi_list;
@@ -55,6 +57,15 @@ struct iommufd_ctx {struct iommufd_ioas *vfio_ioas; }; +/* Entry for iommufd_ctx::mt_mmap */ +struct iommufd_mmap { +struct iommufd_object *owner; + +/* Physical range for remap_pfn_range() */ +unsigned long base_pfn; +unsigned long num_pfns; +}; +/* * The IOVA to PFN map. The map automatically copies the PFNs into multiple * domains and permits sharing of PFNs between io_pagetable instances. This
include/linux/iommufd.h +39 -0
@@ -245,6 +245,10 @@ int _iommufd_object_depend(struct iommufd_object *obj_dependent, struct iommufd_object *obj_depended);void _iommufd_object_undepend (struct iommufd_object *obj_dependent,struct iommufd_object *obj_depended); +int _iommufd_alloc_mmap (struct iommufd_ctx *ictx,struct iommufd_object *owner, +phys_addr_t base,size_t length,unsigned long *offset); +void _iommufd_destroy_mmap (struct iommufd_ctx *ictx, +struct iommufd_object *owner,unsigned long offset);struct device *iommufd_viommu_find_dev (struct iommufd_viommu *viommu,unsigned long vdev_id); int iommufd_viommu_get_vdev_id(struct iommufd_viommu *viommu,
@@ -277,6 +281,20 @@ _iommufd_object_undepend(struct iommufd_object *obj_dependent, { } +static inline int _iommufd_alloc_mmap (struct iommufd_ctx *ictx, +struct iommufd_object *owner, +phys_addr_t base,size_t length, +unsigned long *offset) +{ +return -EOPNOTSUPP ; +} + +static inline void _iommufd_destroy_mmap (struct iommufd_ctx *ictx, +struct iommufd_object *owner, +unsigned long offset) +{ +} +static inline struct device *iommufd_viommu_find_dev (struct iommufd_viommu *viommu,unsigned long vdev_id) {
@@ -390,4 +408,25 @@ static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu,_iommufd_object_undepend (&dependent->member.obj, \ &depended->member.obj); \ }) + +/* +* Helpers for IOMMU driver to alloc/destroy an mmapable area for a structure. +* Driver should report the @out_offset and @length to user space for an mmap() +*/ +#define iommufd_viommu_alloc_mmap (viommu, member, base, length, out_offset) \ + ({ \ + static_assert(__same_type(struct iommufd_viommu, \ + viommu->member)); \ + static_assert(offsetof(typeof(*viommu), member.obj) == 0); \ + _iommufd_alloc_mmap(viommu->member.ictx, &viommu->member.obj, \ + base, length, out_offset); \ + }) +#define iommufd_viommu_destroy_mmap (viommu, member, offset) \ + ({ \ + static_assert(__same_type(struct iommufd_viommu, \ + viommu->member)); \ + static_assert(offsetof(typeof(*viommu), member.obj) == 0); \ + _iommufd_destroy_mmap(viommu->member.ictx, \ + &viommu->member.obj, offset); \ + })#endif
drivers/iommu/iommufd/driver.c +52 -0
58 lines not on this page · raw message
drivers/iommu/iommufd/main.c +61 -0
78 lines not on this page · raw message
Signature · 1 line
2.43.0
kernel test robot
build botRe: [PATCH v4 14/23] iommufd: Add mmap interface
kernel test robot noticed the following build warnings:
Hi Nicolin,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 92a09c47464d040866cf2b4cd052bc60555185fb]
url: https://github.com/intel-lab-lkp/linux/commits/Nicolin-Chen/iommufd-viommu-Add-driver-allocated-vDEVICE-support/20250509-110521 base: 92a09c47464d040866cf2b4cd052bc60555185fb patch link: https://lore.kernel.org/r/ee9ee287264fd75eb4fc64a63f20d03e9ba18161.1746757630.git.nicolinc%40nvidia.com patch subject: [PATCH v4 14/23] iommufd: Add mmap interface config: i386-buildonly-randconfig-003-20250509 (https://download.01.org/0day-ci/archive/20250509/[email protected]/config) compiler: clang version 20.1.2 (https://github.com/llvm/llvm-project 58df0ef89dd64126512e4ee27b4ac3fd8ddf6247) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250509/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/iommu/iommufd/driver.c:91:6: warning: variable 'num_pfns' set but not used [-Wunused-but-set-variable]
91 | int num_pfns, rc;
| ^1 warning generated.
vim +/num_pfns +91 drivers/iommu/iommufd/driver.c
80
81 /*
82 * Allocate an @offset to return to user space to use for an mmap() syscall
83 *
84 * Driver should use a per-structure helper in include/linux/iommufd.h
85 */
86 int _iommufd_alloc_mmap(struct iommufd_ctx *ictx, struct iommufd_object *owner,
87 phys_addr_t base, size_t length, unsigned long *offset)
88 {
89 struct iommufd_mmap *immap;
90 unsigned long startp;
> 91 int num_pfns, rc;
92
93 if (WARN_ON_ONCE(!offset))
94 return -EINVAL;
95 if (!PAGE_ALIGNED(base))
96 return -EINVAL;
97 if (!length || !PAGE_ALIGNED(length))
98 return -EINVAL;
99 num_pfns = length >> PAGE_SHIFT; 100
101 immap = kzalloc(sizeof(*immap), GFP_KERNEL);
102 if (!immap)
103 return -ENOMEM;
104 immap->owner = owner;
105 immap->base_pfn = base >> PAGE_SHIFT;
106 immap->num_pfns = length >> PAGE_SHIFT;
107
108 rc = mtree_alloc_range(&ictx->mt_mmap, &startp, immap, immap->num_pfns,
109 0, U32_MAX >> PAGE_SHIFT, GFP_KERNEL);
110 if (rc < 0) {
111 kfree(immap);
112 return rc;
113 }
114
115 /* mmap() syscall will right-shift the offset in vma->vm_pgoff */
116 *offset = startp << PAGE_SHIFT;
117 return 0;
118 }
119 EXPORT_SYMBOL_NS_GPL(_iommufd_alloc_mmap, "IOMMUFD");
120
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
Re: [PATCH v4 14/23] iommufd: Add mmap interface
In reply to Nicolin ChenQuoted 3 lines · Nicolin Chen
drivers/iommu/iommufd/driver.c +3
-0+int _iommufd_alloc_mmap (struct iommufd_ctx *ictx,struct iommufd_object *owner, +phys_addr_t base,size_t length,unsigned long *offset) +{
...
Quoted 1 line
drivers/iommu/iommufd/driver.c +1
-0+int num_pfns, rc;
...
drivers/iommu/iommufd/driver.c void _iommufd_object_undepend(struct iommufd_object *obj_dependent, · line 99
Quoted 1 line
drivers/iommu/iommufd/driver.c +1
-0+ num_pfns = length >>PAGE_SHIFT ;
I forgot to clean away this num_pfns, as "length >> PAGE_SHIFT" stores to immap->num_pfns already.
Will drop this in v5.
Thanks
Nicolin
RE: [PATCH v4 14/23] iommufd: Add mmap interface
In reply to Nicolin ChenQuoted 12 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AM+/*
+ * Kernel driver must first use the for-driver helpers to register an mmappable
+ * MMIO region to the iommufd core to allocate an offset. Then, it should report
+ * to user space this offset and the length of the MMIO region for mmap syscall,
+ * via a priorIOMMU_VIOMMU_ALLOCioctl.
+ */
this comment better suits _iommufd_alloc_mmap()
Quoted 30 lines
drivers/iommu/iommufd/main.c +26
-0+static int iommufd_fops_mmap (struct file *filp,struct vm_area_struct *vma) +{ +struct iommufd_ctx *ictx = filp->private_data; +size_t length = vma->vm_end - vma->vm_start; +struct iommufd_mmap *immap; +int rc; + +if (!PAGE_ALIGNED (length)) +return -EINVAL ; +if (!(vma->vm_flags &VM_SHARED )) +return -EINVAL ; +if (vma->vm_flags &VM_EXEC ) +return -EPERM ; + +/* vma->vm_pgoff carries an index to an mtree entry (immap) */ + immap =mtree_load (&ictx->mt_mmap, vma->vm_pgoff); +if (!immap) +return -ENXIO ; +if (length >>PAGE_SHIFT != immap->num_pfns) +return -ENXIO ; + + vma->vm_pgoff =0 ; + vma->vm_private_data = immap; + vma->vm_ops = &iommufd_vma_ops; + vma->vm_page_prot =pgprot_noncached (vma->vm_page_prot); + vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND |
VM_DONTDUMP|VM_IO);(quoted diff) +2
-0+ + rc = remap_pfn_range(vma, vma->vm_start, immap->base_pfn,length,
drivers/iommu/iommufd/main.c static long iommufd_fops_ioctl(struct file *filp, unsigned int cmd, · line 470
Quoted 4 lines
drivers/iommu/iommufd/main.c +4
-0+ vma->vm_page_prot); +if (!rc)/* vm_ops.open won't be called for mmap itself. */ +refcount_inc (&immap->owner->users); +return rc;
let's add some words for this refcnt thing in the commit msg.
Jason Gunthorpe
maintainerRe: [PATCH v4 14/23] iommufd: Add mmap interface
In reply to Nicolin Chendrivers/iommu/iommufd/driver.c void _iommufd_object_undepend(struct iommufd_object *obj_dependent, · line 94
Quoted 9 lines · Nicolin Chen
drivers/iommu/iommufd/driver.c +9
-0+int _iommufd_alloc_mmap (struct iommufd_ctx *ictx,struct iommufd_object *owner, +phys_addr_t base,size_t length,unsigned long *offset) +{ +struct iommufd_mmap *immap; +unsigned long startp; +int num_pfns, rc; + +if (WARN_ON_ONCE (!offset)) +return -EINVAL ;
We don't need checks like this, just let it oops
drivers/iommu/iommufd/driver.c void _iommufd_object_undepend(struct iommufd_object *obj_dependent, · line 105
Quoted 11 lines
drivers/iommu/iommufd/driver.c +11
-0+if (!PAGE_ALIGNED (base)) +return -EINVAL ; +if (!length || !PAGE_ALIGNED (length)) +return -EINVAL ; + num_pfns = length >>PAGE_SHIFT ; + + immap =kzalloc (sizeof (*immap),GFP_KERNEL ); +if (!immap) +return -ENOMEM ; + immap->owner = owner; + immap->base_pfn = base >>PAGE_SHIFT ;
'base' is a confusing name for the mmio address. Call it mmio_pfn or something
Quoted 10 lines
drivers/iommu/iommufd/main.c +10
-0+static int iommufd_fops_mmap (struct file *filp,struct vm_area_struct *vma) +{ +struct iommufd_ctx *ictx = filp->private_data; +size_t length = vma->vm_end - vma->vm_start; +struct iommufd_mmap *immap; +int rc; + +if (!PAGE_ALIGNED (length)) +return -EINVAL ; +if (!(vma->vm_flags &VM_SHARED ))
drivers/iommu/iommufd/main.c static long iommufd_fops_ioctl(struct file *filp, unsigned int cmd, · line 458
Quoted 10 lines
drivers/iommu/iommufd/main.c +10
-0+return -EINVAL ; +if (vma->vm_flags &VM_EXEC ) +return -EPERM ; + +/* vma->vm_pgoff carries an index to an mtree entry (immap) */ + immap =mtree_load (&ictx->mt_mmap, vma->vm_pgoff); +if (!immap) +return -ENXIO ; +if (length >>PAGE_SHIFT != immap->num_pfns) +return -ENXIO ;
This needs to validate that vm_pgoff is at the start of the immap or num_pfns is the wrong thing to validate length against.
length >> PAGE_SHIFT will truncate non-zero bits which will not check it properly.
drivers/iommu/iommufd/main.c static long iommufd_fops_ioctl(struct file *filp, unsigned int cmd, · line 464
Quoted 5 lines
drivers/iommu/iommufd/main.c +5
-0+ vma->vm_pgoff =0 ; + vma->vm_private_data = immap; + vma->vm_ops = &iommufd_vma_ops; + vma->vm_page_prot =pgprot_noncached (vma->vm_page_prot); +vm_flags_set (vma,VM_PFNMAP |VM_DONTEXPAND |VM_DONTDUMP |VM_IO );
remap_pfn_range already sets these vm_flags
drivers/iommu/iommufd/main.c static long iommufd_fops_ioctl(struct file *filp, unsigned int cmd, · line 467
Quoted 2 lines
drivers/iommu/iommufd/main.c +2
-0+ rc =remap_pfn_range (vma, vma->vm_start, immap->base_pfn, length, + vma->vm_page_prot);
This shoudl be io_remap_pfn_range() if it is mmio
drivers/iommu/iommufd/main.c static long iommufd_fops_ioctl(struct file *filp, unsigned int cmd, · line 470
Quoted 3 lines
drivers/iommu/iommufd/main.c +3
-0+if (!rc)/* vm_ops.open won't be called for mmap itself. */ +refcount_inc (&immap->owner->users); +return rc;
Success oriented flow
Jason
Tian, Kevin
maintainerRE: [PATCH v4 14/23] iommufd: Add mmap interface
Quoted 4 lines
From: Jason Gunthorpe <[email protected]>
Sent: Friday, May 16, 2025 12:47 AMOn Thu, May 08, 2025 at 08:02:35PM -0700, Nicolin Chen wrote:
Quoted 10 lines
drivers/iommu/iommufd/main.c static long iommufd_fops_ioctl(struct file *filp, unsigned int cmd, · line 458
Quoted 6 lines
drivers/iommu/iommufd/main.c +6
-0+/* vma->vm_pgoff carries an index to an mtree entry (immap) */ + immap =mtree_load (&ictx->mt_mmap, vma->vm_pgoff); +if (!immap) +return -ENXIO ; +if (length >>PAGE_SHIFT != immap->num_pfns) +return -ENXIO ;This needs to validate that
vm_pgoffis at the start of the immap ornum_pfnsis the wrong thing to validate length against.
vm_pgoff is the index into mtree. If it's wrong mtree_load() will fail already?
Jason Gunthorpe
maintainerRe: [PATCH v4 14/23] iommufd: Add mmap interface
Quoted 15 lines · Kevin
Quoted 4 lines
From: Jason Gunthorpe <[email protected]>
Sent: Friday, May 16, 2025 12:47 AMOn Thu, May 08, 2025 at 08:02:35PM -0700, Nicolin Chen wrote:
Quoted 10 lines
drivers/iommu/iommufd/main.c static long iommufd_fops_ioctl(struct file *filp, unsigned int cmd, · line 458
Quoted 6 lines
drivers/iommu/iommufd/main.c +6
-0+/* vma->vm_pgoff carries an index to an mtree entry (immap) */ + immap =mtree_load (&ictx->mt_mmap, vma->vm_pgoff); +if (!immap) +return -ENXIO ; +if (length >>PAGE_SHIFT != immap->num_pfns) +return -ENXIO ;This needs to validate that
vm_pgoffis at the start of the immap ornum_pfnsis the wrong thing to validate length against.
Quoted 2 lines
vm_pgoffis the index into mtree. If it's wrongmtree_load()will fail already?
I'm not sure? I thought mtree_load will return any range that intersects with the given index?
Otherwise what is the point of having a range based datastructure?
Jason
Re: [PATCH v4 14/23] iommufd: Add mmap interface
Quoted 16 lines · Jason Gunthorpe
Quoted 15 lines · Kevin
Quoted 14 lines
From: Jason Gunthorpe <[email protected]>
Sent: Friday, May 16, 2025 12:47 AMdrivers/iommu/iommufd/main.c static long iommufd_fops_ioctl(struct file *filp, unsigned int cmd, · line 458
Quoted 6 lines · Nicolin Chen
drivers/iommu/iommufd/main.c +6
-0+/* vma->vm_pgoff carries an index to an mtree entry (immap) */ + immap =mtree_load (&ictx->mt_mmap, vma->vm_pgoff); +if (!immap) +return -ENXIO ; +if (length >>PAGE_SHIFT != immap->num_pfns) +return -ENXIO ;This needs to validate that
vm_pgoffis at the start of the immap ornum_pfnsis the wrong thing to validate length against.
Quoted 7 lines
Quoted 2 lines
vm_pgoffis the index into mtree. If it's wrongmtree_load()will fail already?I'm not sure? I thought
mtree_loadwill return any range that intersects with the given index?Otherwise what is the point of having a range based datastructure?
Yea, I can confirm that providing a vm_pgoff that's in the range (though not the startp) could get immap too.
I am adding negative test coverage for the vm_pgoff/length input for the following ifs:
+ /* vma->vm_pgoff carries an index to an mtree entry (immap) */ + immap = mtree_load(&ictx->mt_mmap, vma->vm_pgoff); + if (!immap)
+ return -ENXIO;
+ /* Validate the vm_pgoff and length against the registered region */
+ if (vma->vm_pgoff != immap->startp)
+ return -ENXIO;
+ if (length != immap->num_pfns << PAGE_SHIFT)
+ return -ENXIO;
Thanks
Nicolin
PATCH v4 15/23 iommufd/selftest: Add coverage for the new mmap interface
In reply to Nicolin ChenExtend the loopback test to a new mmap page.
--- drivers/iommu/iommufd/iommufd_test.h | 4 +++ drivers/iommu/iommufd/selftest.c | 41 ++++++++++++++++++++++--- tools/testing/selftests/iommu/iommufd.c | 7 +++++ 3 files changed, 48 insertions(+), 4 deletions(-)
drivers/iommu/iommufd/iommufd_test.h +4 -0
@@ -232,12 +232,16 @@ struct iommu_hwpt_invalidate_selftest {* (IOMMU_VIOMMU_TYPE_SELFTEST) * @in_data: Input random data from user space * @out_data: Output data (matching @in_data) to user space +* @out_mmap_offset: The offset argument for mmap syscall +* @out_mmap_length: The length argument for mmap syscall * * Simply set @out_data=@in_data for a loopback test */ struct iommu_viommu_selftest {__u32 in_data;__u32 out_data; +__aligned_u64 out_mmap_offset; +__aligned_u64 out_mmap_length; };/* Should not be equal to any defined value in enum iommu_viommu_invalidate_data_type */
drivers/iommu/iommufd/selftest.c +37 -4
69 lines not on this page · raw message
tools/testing/selftests/iommu/iommufd.c +7 -0
@@ -2799,12 +2799,19 @@ TEST_F(iommufd_viommu, viommu_alloc_with_data)struct iommu_viommu_selftest data = { .in_data =0xbeef , }; +uint32_t *test;if (self->device_id) {test_cmd_viommu_alloc (self->device_id, self->hwpt_id,IOMMU_VIOMMU_TYPE_SELFTEST , &data,sizeof (data), &self->viommu_id);assert (data.out_data == data.in_data); + test =mmap (NULL , data.out_mmap_length,PROT_READ |PROT_WRITE , +MAP_SHARED , self->fd, data.out_mmap_offset); +assert (test && *test == data.in_data); +EXPECT_ERRNO (EBUSY , +_test_ioctl_destroy (self->fd, self->viommu_id)); +munmap (test, data.out_mmap_length); } }
Signature · 1 line
2.43.0
PATCH v4 16/23 Documentation: userspace-api: iommufd: Update HW QUEUE
In reply to Nicolin ChenWith the introduction of the new object and its infrastructure, update the doc to reflect that.
--- Documentation/userspace-api/iommufd.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+)
Documentation/userspace-api/iommufd.rst +12 -0
@@ -124,6 +124,17 @@ Following IOMMUFD objects are exposed to userspace: used to allocate a vEVENTQ. Each vIOMMU can support multiple types of vEVENTS, but is confined to one vEVENTQ per vEVENTQ type. +- IOMMUFD_OBJ_HW_QUEUE, representing a hardware accelerated queue, as a subset + of IOMMU's virtualization features, for the IOMMU HW to directly read or write + the virtual queue memory owned by a guest OS. This HW-acceleration feature can + allow VM to work with the IOMMU HW directly without a VM Exit, so as to reduce + overhead from the hypercalls. Along with the HW QUEUE object, iommufd provides + user space an mmap interface for VMM to mmap a physical MMIO region from the + host physical address space to the guest physical address space, allowing the + guest OS to directly control the allocated HW QUEUE. Thus, when allocating a + HW QUEUE, the VMM must request a pair of mmap info (offset/length) and pass in + exactly to an mmap syscall via its offset and length arguments. + All user-visible objects are destroyed via the IOMMU_DESTROY uAPI. The diagrams below show relationships between user-visible objects and kernel
@@ -270,6 +281,7 @@ User visible objects are backed by following datastructures: - iommufd_viommu for IOMMUFD_OBJ_VIOMMU. - iommufd_vdevice for IOMMUFD_OBJ_VDEVICE. - iommufd_veventq for IOMMUFD_OBJ_VEVENTQ. +- iommufd_hw_queue for IOMMUFD_OBJ_HW_QUEUE. Several terminologies when looking at these datastructures:
Signature · 1 line
2.43.0
RE: [PATCH v4 16/23] Documentation: userspace-api: iommufd: Update HW QUEUE
Quoted 7 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMWith the introduction of the new object and its infrastructure, update the doc to reflect that.
Signed-off-by Nicolin Chen <[email protected]>
Re: [PATCH v4 16/23] Documentation: userspace-api: iommufd: Update HW QUEUE
In reply to Nicolin ChenQuoted 7 lines · Nicolin Chen
With the introduction of the new object and its infrastructure, update the doc to reflect that.
Signed-off-by Nicolin Chen <[email protected]>--- Documentation/userspace-api/iommufd.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+)
Jason
PATCH v4 17/23 iommu/arm-smmu-v3-iommufd: Add vsmmu_alloc impl op
In reply to Nicolin ChenAn impl driver might want to allocate its own type of vIOMMU object or the standard IOMMU_VIOMMU_TYPE_ARM_SMMUV3 by setting up its own SW/HW bits, as the tegra241-cmdqv driver will add IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV.
Add a vsmmu_alloc op and prioritize it in arm_vsmmu_alloc().
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 6 ++++++ .../iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 17 +++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +6 -0
@@ -16,6 +16,7 @@#include <linux/sizes.h> struct arm_smmu_device ; +struct arm_smmu_domain ;/* MMIO registers */ #define ARM_SMMU_IDR0 0x0
@@ -720,6 +721,11 @@ struct arm_smmu_impl_ops {int (*init_structures)(struct arm_smmu_device *smmu);struct arm_smmu_cmdq *(*get_secondary_cmdq)(struct arm_smmu_device *smmu,struct arm_smmu_cmdq_ent *ent); +struct arm_vsmmu *(*vsmmu_alloc)( +struct arm_smmu_device *smmu, +struct arm_smmu_domain *smmu_domain,struct iommufd_ctx *ictx, +unsigned int viommu_type, +const struct iommu_user_data *user_data); };/* An SMMUv3 instance */
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +11 -6
@@ -392,10 +392,7 @@ struct iommufd_viommu *arm_vsmmu_alloc(struct device *dev, iommu_get_iommu_dev(dev, struct arm_smmu_device, iommu);struct arm_smmu_master *master =dev_iommu_priv_get (dev);struct arm_smmu_domain *s2_parent =to_smmu_domain (parent); -struct arm_vsmmu *vsmmu; - -if (viommu_type !=IOMMU_VIOMMU_TYPE_ARM_SMMUV3 ) -return ERR_PTR (-EOPNOTSUPP ); +struct arm_vsmmu *vsmmu =ERR_PTR (-EOPNOTSUPP );if (!(smmu->features &ARM_SMMU_FEAT_NESTING ))return ERR_PTR (-EOPNOTSUPP );
@@ -423,8 +420,16 @@ struct iommufd_viommu *arm_vsmmu_alloc(struct device *dev, !(smmu->features & ARM_SMMU_FEAT_S2FWB))return ERR_PTR (-EOPNOTSUPP ); - vsmmu = iommufd_viommu_alloc(ictx, struct arm_vsmmu, core, - &arm_vsmmu_ops); +/* Prioritize the impl that may support IOMMU_VIOMMU_TYPE_ARM_SMMUV3 */ +if (master->smmu->impl_ops && master->smmu->impl_ops->vsmmu_alloc) + vsmmu = master->smmu->impl_ops->vsmmu_alloc ( + master->smmu, s2_parent, ictx, viommu_type, user_data); +if (PTR_ERR (vsmmu) == -EOPNOTSUPP ) { +/* Otherwise, allocate an IOMMU_VIOMMU_TYPE_ARM_SMMUV3 here */ +if (viommu_type ==IOMMU_VIOMMU_TYPE_ARM_SMMUV3 ) + vsmmu = iommufd_viommu_alloc(ictx, struct arm_vsmmu, + core, &arm_vsmmu_ops); + }if (IS_ERR (vsmmu))return ERR_CAST (vsmmu);
Signature · 1 line
2.43.0
RE: [PATCH v4 17/23] iommu/arm-smmu-v3-iommufd: Add vsmmu_alloc impl op
Quoted 9 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMAn impl driver might want to allocate its own type of vIOMMU object or the standard
IOMMU_VIOMMU_TYPE_ARM_SMMUV3by setting up its own SW/HW bits, as
the tegra241-cmdqv driver will addIOMMU_VIOMMU_TYPE_TEGRA241_CMDQV.
Quoted 4 lines
Add a
vsmmu_allocop and prioritize it inarm_vsmmu_alloc().Reviewed-by Pranjal Shrivastava <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
Jason Gunthorpe
maintainerRe: [PATCH v4 17/23] iommu/arm-smmu-v3-iommufd: Add vsmmu_alloc impl op
In reply to Nicolin ChenQuoted 26 lines · Nicolin Chen
An impl driver might want to allocate its own type of vIOMMU object or the standard
IOMMU_VIOMMU_TYPE_ARM_SMMUV3by setting up its own SW/HW bits, as the tegra241-cmdqv driver will addIOMMU_VIOMMU_TYPE_TEGRA241_CMDQV.Add a
vsmmu_allocop and prioritize it inarm_vsmmu_alloc().Reviewed-by Pranjal Shrivastava <[email protected]>Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 6 ++++++ .../iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 17 +++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-)drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +1
-0@@ -16,6 +16,7 @@#include <linux/sizes.h> struct arm_smmu_device ; +struct arm_smmu_domain ;/* MMIO registers */ #define ARM_SMMU_IDR0 0x0
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h struct arm_smmu_impl_ops { · line 729
Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +5
-0@@ -720,6 +721,11 @@ struct arm_smmu_impl_ops {int (*init_structures)(struct arm_smmu_device *smmu);struct arm_smmu_cmdq *(*get_secondary_cmdq)(struct arm_smmu_device *smmu,struct arm_smmu_cmdq_ent *ent); +struct arm_vsmmu *(*vsmmu_alloc)( +struct arm_smmu_device *smmu, +struct arm_smmu_domain *smmu_domain,struct iommufd_ctx *ictx, +unsigned int viommu_type, +const struct iommu_user_data *user_data); };
I think you should put the supported viommu type here in the ops struct and match it here:
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h struct arm_smmu_impl_ops {
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +4
-0+/* Prioritize the impl that may support IOMMU_VIOMMU_TYPE_ARM_SMMUV3 */ +if (master->smmu->impl_ops && master->smmu->impl_ops->vsmmu_alloc) + vsmmu = master->smmu->impl_ops->vsmmu_alloc ( + master->smmu, s2_parent, ictx, viommu_type, user_data);
instead of the EOPNOTSUPP dance. Either the impl_ops supports the requested viommu as an extension or we are running in the normal mode?
Is there a reason to allocate a different viommu if the userspace does not enable the implementation specific features?
Jason
Re: [PATCH v4 17/23] iommu/arm-smmu-v3-iommufd: Add vsmmu_alloc impl op
Quoted 24 lines · Jason Gunthorpe
Quoted 23 lines · Nicolin Chen
An impl driver might want to allocate its own type of vIOMMU object or the standard
IOMMU_VIOMMU_TYPE_ARM_SMMUV3by setting up its own SW/HW bits, as the tegra241-cmdqv driver will addIOMMU_VIOMMU_TYPE_TEGRA241_CMDQV.Add a
vsmmu_allocop and prioritize it inarm_vsmmu_alloc().Reviewed-by Pranjal Shrivastava <[email protected]>Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 6 ++++++ .../iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 17 +++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-)drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +1
-0@@ -16,6 +16,7 @@#include <linux/sizes.h> struct arm_smmu_device ; +struct arm_smmu_domain ;
Quoted 15 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h struct arm_smmu_impl_ops { · line 729
Quoted 12 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +5
-0/* MMIO registers */ #define ARM_SMMU_IDR0 0x0@@ -720,6 +721,11 @@ struct arm_smmu_impl_ops {int (*init_structures)(struct arm_smmu_device *smmu);struct arm_smmu_cmdq *(*get_secondary_cmdq)(struct arm_smmu_device *smmu,struct arm_smmu_cmdq_ent *ent); +struct arm_vsmmu *(*vsmmu_alloc)( +struct arm_smmu_device *smmu, +struct arm_smmu_domain *smmu_domain,struct iommufd_ctx *ictx, +unsigned int viommu_type, +const struct iommu_user_data *user_data); };I think you should put the supported viommu type here in the ops struct and match it here:
OK. A single type per impl might be enough for now, so it can be a static one.
Quoted 7 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h struct arm_smmu_impl_ops {
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +4
-0+/* Prioritize the impl that may support IOMMU_VIOMMU_TYPE_ARM_SMMUV3 */ +if (master->smmu->impl_ops && master->smmu->impl_ops->vsmmu_alloc) + vsmmu = master->smmu->impl_ops->vsmmu_alloc ( + master->smmu, s2_parent, ictx, viommu_type, user_data);instead of the EOPNOTSUPP dance. Either the
impl_opssupports the requested viommu as an extension or we are running in the normal mode?
I think we can only do normal mode if requested viommu is the normal SMMUV3 type, i.e. still need to reject a type other than !CMDQV nor !SMMUV3, right?
Quoted 2 lines
Is there a reason to allocate a different viommu if the userspace does not enable the implementation specific features?
Hmm, what is this different viommu?
If VMM doesn't want VCMDQ, it should go with the normal SMMUV3 type.
Thanks
Nicolin
PATCH v4 18/23 iommu/arm-smmu-v3-iommufd: Support implementation-defined hw_info
In reply to Nicolin ChenRepurpose the @__reserved field in the struct iommu_hw_info_arm_smmuv3, to an HW implementation-defined field @impl.
This will be used by Tegra241 CMDQV implementation on top of a standard ARM SMMUv3 IOMMU. The @impl will be only valid if @flags is set with an implementation-defined flag.
Thus in the driver-level, add an hw_info impl op that will return such a flag and fill the impl field.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 + include/uapi/linux/iommufd.h | 4 ++-- .../iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 17 ++++++++++++++--- 3 files changed, 17 insertions(+), 5 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +1 -0
@@ -726,6 +726,7 @@ struct arm_smmu_impl_ops { struct arm_smmu_domain *smmu_domain, struct iommufd_ctx *ictx, unsigned int viommu_type, const struct iommu_user_data *user_data); +u32 (*hw_info)(struct arm_smmu_device *smmu,u32 *impl); };/* An SMMUv3 instance */
include/uapi/linux/iommufd.h +2 -2
@@ -554,7 +554,7 @@ struct iommu_hw_info_vtd {* (IOMMU_HW_INFO_TYPE_ARM_SMMUV3) * * @flags: Must be set to 0 -* @__reserved: Must be 0 +* @impl: Must be 0 * @idr: Implemented features for ARM SMMU Non-secure programming interface * @iidr: Information about the implementation and implementer of ARM SMMU, * and architecture version supported
@@ -585,7 +585,7 @@ struct iommu_hw_info_vtd {*/ struct iommu_hw_info_arm_smmuv3 {__u32 flags; -__u32 __reserved; +__u32 impl;__u32 idr[6 ];__u32 iidr;__u32 aidr;
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +14 -3
@@ -10,6 +10,7 @@void *arm_smmu_hw_info (struct device *dev,u32 *length,u32 *type) {struct arm_smmu_master *master =dev_iommu_priv_get (dev); +struct arm_smmu_device *smmu = master->smmu;struct iommu_hw_info_arm_smmuv3 *info;u32 __iomem *base_idr;unsigned int i;
@@ -18,15 +19,25 @@ void *arm_smmu_hw_info(struct device *dev, u32 *length, u32 *type)if (!info)return ERR_PTR (-ENOMEM ); - base_idr = master->smmu->base +ARM_SMMU_IDR0 ; + base_idr = smmu->base +ARM_SMMU_IDR0 ;for (i =0 ; i <=5 ; i++) info->idr[i] =readl_relaxed (base_idr + i); - info->iidr =readl_relaxed (master->smmu->base +ARM_SMMU_IIDR ); - info->aidr =readl_relaxed (master->smmu->base +ARM_SMMU_AIDR ); + info->iidr =readl_relaxed (smmu->base +ARM_SMMU_IIDR ); + info->aidr =readl_relaxed (smmu->base +ARM_SMMU_AIDR ); *length =sizeof (*info); *type =IOMMU_HW_INFO_TYPE_ARM_SMMUV3 ; +if (smmu->impl_ops && smmu->impl_ops->hw_info) { +u32 flags, impl; + + flags = smmu->impl_ops->hw_info (smmu, &impl); +if (flags) { + info->impl = impl; + info->flags |= flags; + } + } +return info; }
Signature · 1 line
2.43.0
RE: [PATCH v4 18/23] iommu/arm-smmu-v3-iommufd: Support implementation-defined hw_info
Quoted 11 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMRepurpose the @__reserved field in the struct
iommu_hw_info_arm_smmuv3,
to an HW implementation-defined field @impl.This will be used by Tegra241 CMDQV implementation on top of a standard ARM SMMUv3 IOMMU. The @impl will be only valid if @flags is set with an implementation-defined flag.
Quoted 5 lines
Thus in the driver-level, add an
hw_infoimpl op that will return such a flag and fill the impl field.Reviewed-by Pranjal Shrivastava <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
Jason Gunthorpe
maintainerRe: [PATCH v4 18/23] iommu/arm-smmu-v3-iommufd: Support implementation-defined hw_info
In reply to Nicolin ChenQuoted 2 lines · Nicolin Chen
Repurpose the @__reserved field in the struct
iommu_hw_info_arm_smmuv3, to an HW implementation-defined field @impl.
It would be nicer to have a tegra/cmdq specific struct and a way for iommu_hw_info to select it. 'impl' isn't going to scale very well if something else wants to use this.
We have out_data_type but we could have an input sub_data_type too. 0 means what we have today, then a simple enum to select another info struct.
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h struct arm_smmu_impl_ops { · line 730
Quoted 6 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +1
-0@@ -726,6 +726,7 @@ struct arm_smmu_impl_ops { struct arm_smmu_domain *smmu_domain, struct iommufd_ctx *ictx, unsigned int viommu_type, const struct iommu_user_data *user_data); +u32 (*hw_info)(struct arm_smmu_device *smmu,u32 *impl); };
Then the dispatch here is just having a sub-struct enum # in arm_smmu_impl_ops for dispatching.
Jason
Re: [PATCH v4 18/23] iommu/arm-smmu-v3-iommufd: Support implementation-defined hw_info
Quoted 11 lines · Jason Gunthorpe
Quoted 2 lines · Nicolin Chen
Repurpose the @__reserved field in the struct
iommu_hw_info_arm_smmuv3, to an HW implementation-defined field @impl.It would be nicer to have a tegra/cmdq specific struct and a way for
iommu_hw_infoto select it. 'impl' isn't going to scale very well if something else wants to use this.We have
out_data_typebut we could have an inputsub_data_typetoo. 0 means what we have today, then a simple enum to select another info struct.
So, there will be two hw_info calls back to back right?
Should the first call return out_data_type=CMDQV while returning the arm_smmu_v3 hw_info data? Otherwise, VMM wouldn't know what to set in the input sub_data_type of the 2nd ioctl?
Thanks
Nicolin
Jason Gunthorpe
maintainerRe: [PATCH v4 18/23] iommu/arm-smmu-v3-iommufd: Support implementation-defined hw_info
Quoted 9 lines · Nicolin Chen
Quoted 8 lines · Jason Gunthorpe
Quoted 2 lines · Nicolin Chen
Repurpose the @__reserved field in the struct
iommu_hw_info_arm_smmuv3, to an HW implementation-defined field @impl.It would be nicer to have a tegra/cmdq specific struct and a way for
iommu_hw_infoto select it. 'impl' isn't going to scale very well if something else wants to use this.
Quoted 5 lines
Quoted 3 lines
We have
out_data_typebut we could have an inputsub_data_typetoo. 0 means what we have today, then a simple enum to select another info struct.So, there will be two
hw_infocalls back to back right?
yes
Quoted 3 lines
Should the first call return out_data_type=CMDQV while returning the
arm_smmu_v3hw_infodata? Otherwise, VMM wouldn't know what to set in the inputsub_data_typeof the 2nd ioctl?
No, either set a flag in the smmu_v3 hw_info, as you were doing here, or just have the vmm probe it. Given the VMM is likely to be told to run in vCMDQ mode on the command line try-and-fail doesn't sound so bad.
And I guess we don't need a "sub type" just a "requested type" where 0 means return the best one and non-zero means return a specific one or fail with EOPNOTSUPP.
Jason
Re: [PATCH v4 18/23] iommu/arm-smmu-v3-iommufd: Support implementation-defined hw_info
Quoted 7 lines · Jason Gunthorpe
Quoted 5 lines · Nicolin Chen
Quoted 1 line · Jason Gunthorpe
On Thu, May 08, 2025 at 08:02:39PM -0700, Nicolin Chen wrote:
Should the first call return out_data_type=CMDQV while returning the
arm_smmu_v3hw_infodata? Otherwise, VMM wouldn't know what to set in the inputsub_data_typeof the 2nd ioctl?
Quoted 8 lines
No, either set a flag in the
smmu_v3hw_info, as you were doing here, or just have the vmm probe it. Given the VMM is likely to be told to run in vCMDQ mode on the command line try-and-fail doesn't sound so bad.And I guess we don't need a "sub type" just a "requested type" where 0 means return the best one and non-zero means return a specific one or fail with EOPNOTSUPP.
OK. I think this would work:hw_info (req_type=0) => out_data_type=SMMU_V3, flags=HAS_CMDQVhw_info (req_type=CMDQV) => out_data_type=CMDQV, flags=0
Or, would it be simpler by having a sub_data_uptr:hw_info => out_data_type=SMMU_V3, sub_data_type=CMDQV,
data_uptr=iommu_hw_info_arm_smmuv3, sub_data_uptr=iommu_hw_info_tegra241_cmdqv
?
Thanks
Nicolin
Jason Gunthorpe
maintainerRe: [PATCH v4 18/23] iommu/arm-smmu-v3-iommufd: Support implementation-defined hw_info
Quoted 13 lines · Nicolin Chen
Quoted 12 lines · Jason Gunthorpe
Quoted 5 lines · Nicolin Chen
Quoted 1 line · Jason Gunthorpe
On Thu, May 08, 2025 at 08:02:39PM -0700, Nicolin Chen wrote:
Should the first call return out_data_type=CMDQV while returning the
arm_smmu_v3hw_infodata? Otherwise, VMM wouldn't know what to set in the inputsub_data_typeof the 2nd ioctl?No, either set a flag in the
smmu_v3hw_info, as you were doing here, or just have the vmm probe it. Given the VMM is likely to be told to run in vCMDQ mode on the command line try-and-fail doesn't sound so bad.
Quoted 7 lines
Quoted 3 lines
And I guess we don't need a "sub type" just a "requested type" where 0 means return the best one and non-zero means return a specific one or fail with EOPNOTSUPP.
OK. I think this would work:
hw_info(req_type=0) => out_data_type=SMMU_V3, flags=HAS_CMDQVhw_info(req_type=CMDQV) => out_data_type=CMDQV, flags=0
Yeah
Quoted 5 lines
Or, would it be simpler by having a
sub_data_uptr:hw_info=> out_data_type=SMMU_V3, sub_data_type=CMDQV,
data_uptr=iommu_hw_info_arm_smmuv3, sub_data_uptr=iommu_hw_info_tegra241_cmdqv
?
I think the former is simpler to code, you can just add the req_type to the signatures and if the driver comes back with a type != req_type the core code will return EOPNOTSUPP
Then just match the impl_ops for req_type in the arm driver.
Finally we end up with only one ioctl enum number space for the types, which seems appealing.
Jason
Re: [PATCH v4 18/23] iommu/arm-smmu-v3-iommufd: Support implementation-defined hw_info
Quoted 30 lines · Jason Gunthorpe
Quoted 13 lines · Nicolin Chen
Quoted 12 lines · Jason Gunthorpe
Quoted 5 lines · Nicolin Chen
Quoted 1 line · Jason Gunthorpe
On Thu, May 08, 2025 at 08:02:39PM -0700, Nicolin Chen wrote:
Should the first call return out_data_type=CMDQV while returning the
arm_smmu_v3hw_infodata? Otherwise, VMM wouldn't know what to set in the inputsub_data_typeof the 2nd ioctl?No, either set a flag in the
smmu_v3hw_info, as you were doing here, or just have the vmm probe it. Given the VMM is likely to be told to run in vCMDQ mode on the command line try-and-fail doesn't sound so bad.Quoted 7 lines
Quoted 3 lines
And I guess we don't need a "sub type" just a "requested type" where 0 means return the best one and non-zero means return a specific one or fail with EOPNOTSUPP.
OK. I think this would work:
hw_info(req_type=0) => out_data_type=SMMU_V3, flags=HAS_CMDQVhw_info(req_type=CMDQV) => out_data_type=CMDQV, flags=0Yeah
Quoted 5 lines
Or, would it be simpler by having a
sub_data_uptr:hw_info=> out_data_type=SMMU_V3, sub_data_type=CMDQV,
data_uptr=iommu_hw_info_arm_smmuv3, sub_data_uptr=iommu_hw_info_tegra241_cmdqv
?
Quoted 3 lines
I think the former is simpler to code, you can just add the
req_typeto the signatures and if the driver comes back with a type !=req_typethe core code will return EOPNOTSUPP
OK.
Maybe just turn the out_data_type to be bidirectional?
Then we would only need to update the docs:
/**
* enum iommu_hw_info_type - IOMMU Hardware Info Types
- * @IOMMU_HW_INFO_TYPE_NONE: Used by the drivers that do not report hardware
- * info
+ * @IOMMU_HW_INFO_TYPE_NONE: (for output) used by the drivers that do not report
+ * hardware info
+ * @IOMMU_HW_INFO_TYPE_DEFAULT: (for input) Used to request the default type
* @IOMMU_HW_INFO_TYPE_INTEL_VTD: Intel VT-d iommu info type
* @IOMMU_HW_INFO_TYPE_ARM_SMMUV3: ARM SMMUv3 iommu info type
+ * @IOMMU_HW_INFO_TYPE_TEGRA241_CMDQV: Subtype of ARM SMMUv3 for Tegra241 CMDQV */
enum iommu_hw_info_type {
IOMMU_HW_INFO_TYPE_NONE = 0,
+ IOMMU_HW_INFO_TYPE_DEFAULT = 0,
IOMMU_HW_INFO_TYPE_INTEL_VTD = 1, IOMMU_HW_INFO_TYPE_ARM_SMMUV3 = 2,
+ IOMMU_HW_INFO_TYPE_TEGRA241_CMDQV =3,
};
- * @out_data_type: Output the iommu hardware info type as defined in the enum
- * iommu_hw_info_type.
+ * @data_type: Bidirectional property.
+ * Input the requested iommu hardware info type as defined in the + * enum iommu_hw_info_type. Requesting IOMMU_HW_INFO_TYPE_DEFAULT + * lets kernel pick the default type to output, otherwise kernel + * will validate the input type and may reject with -EOPNOTSUPP. + * Output the supported iommu hardware info type as defined in the + * same enum iommu_hw_info_type
And similarly in the iommu API kdoc too.
Quoted 2 lines
Finally we end up with only one ioctl enum number space for the types, which seems appealing.
Yea. Avoiding a sub enum is nicer.
Thanks
Nicolin
Jason Gunthorpe
maintainerRe: [PATCH v4 18/23] iommu/arm-smmu-v3-iommufd: Support implementation-defined hw_info
Quoted 7 lines · Nicolin Chen
Quoted 3 lines
I think the former is simpler to code, you can just add the
req_typeto the signatures and if the driver comes back with a type !=req_typethe core code will return EOPNOTSUPPOK.
Maybe just turn the
out_data_typeto be bidirectional?
You can do that, bu you need to add a flag to turn it on. We didn't validate that it was 0 on input before so we can't start now.
Jason
Re: [PATCH v4 18/23] iommu/arm-smmu-v3-iommufd: Support implementation-defined hw_info
Quoted 11 lines · Jason Gunthorpe
Quoted 7 lines · Nicolin Chen
Quoted 3 lines
I think the former is simpler to code, you can just add the
req_typeto the signatures and if the driver comes back with a type !=req_typethe core code will return EOPNOTSUPPOK.
Maybe just turn the
out_data_typeto be bidirectional?You can do that, bu you need to add a flag to turn it on. We didn't validate that it was 0 on input before so we can't start now.
Sure. I feel adding a flag would have less footprints throughout the call down to the driver. Will try doing that.
Thanks
Nicolin
PATCH v4 19/23 iommu/tegra241-cmdqv: Use request_threaded_irq
In reply to Nicolin ChenA vEVENT can be reported only from a threaded IRQ context. Change to using request_threaded_irq to support that.
--- drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +3 -2
@@ -824,8 +824,9 @@ __tegra241_cmdqv_probe(struct arm_smmu_device *smmu, struct resource *res, cmdqv->dev = smmu->impl_dev;if (cmdqv->irq >0 ) { - ret =request_irq (irq, tegra241_cmdqv_isr,0 ,"tegra241-cmdqv" , - cmdqv); + ret =request_threaded_irq (irq,NULL , tegra241_cmdqv_isr, +IRQF_ONESHOT ,"tegra241-cmdqv" , + cmdqv);if (ret) {dev_err (cmdqv->dev,"failed to request irq (%d): %d\n" , cmdqv->irq, ret);
Signature · 1 line
2.43.0
RE: [PATCH v4 19/23] iommu/tegra241-cmdqv: Use request_threaded_irq
Quoted 8 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMA vEVENT can be reported only from a threaded IRQ context. Change to using
request_threaded_irqto support that.Signed-off-by Nicolin Chen <[email protected]>
Re: [PATCH v4 19/23] iommu/tegra241-cmdqv: Use request_threaded_irq
In reply to Nicolin ChenQuoted 8 lines · Nicolin Chen
A vEVENT can be reported only from a threaded IRQ context. Change to using
request_threaded_irqto support that.Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
Jason
PATCH v4 20/23 iommu/tegra241-cmdqv: Simplify deinit flow in tegra241_cmdqv_remove_vintf()
In reply to Nicolin ChenThe current flow of tegra241_cmdqv_remove_vintf() is:
1. For each LVCMDQ, tegra241_vintf_remove_lvcmdq():
a. Disable the LVCMDQ HW
b. Release the LVCMDQ SW resource
2. For current VINTF, tegra241_vintf_hw_deinit():
c. Disable all LVCMDQ HWs
d. Disable VINTF HW
Obviously, the step 1.a and the step 2.c are redundant.
Since tegra241_vintf_hw_deinit() disables all of its LVCMDQ HWs, it could simplify the flow in tegra241_cmdqv_remove_vintf() by calling that first:
1. For current VINTF, tegra241_vintf_hw_deinit():
a. Disable all LVCMDQ HWs
b. Disable VINTF HW
2. Release all LVCMDQ SW resources
Drop tegra241_vintf_remove_lvcmdq(), and move tegra241_vintf_free_lvcmdq() as the new step 2.
--- drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-)
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +3 -10
@@ -628,24 +628,17 @@ static int tegra241_cmdqv_init_vintf(struct tegra241_cmdqv *cmdqv, u16 max_idx,/* Remove Helpers */ -static void tegra241_vintf_remove_lvcmdq (struct tegra241_vintf *vintf,u16 lidx) -{ -tegra241_vcmdq_hw_deinit (vintf->lvcmdqs[lidx]); -tegra241_vintf_free_lvcmdq (vintf, lidx); -} -static void tegra241_cmdqv_remove_vintf (struct tegra241_cmdqv *cmdqv,u16 idx) {struct tegra241_vintf *vintf = cmdqv->vintfs[idx];u16 lidx; +tegra241_vintf_hw_deinit (vintf); +/* Remove LVCMDQ resources */ for (lidx =0 ; lidx < vintf->cmdqv->num_lvcmdqs_per_vintf; lidx++)if (vintf->lvcmdqs[lidx]) -tegra241_vintf_remove_lvcmdq (vintf, lidx); - -/* Remove VINTF resources */ -tegra241_vintf_hw_deinit (vintf); +tegra241_vintf_free_lvcmdq (vintf, lidx);dev_dbg (cmdqv->dev,"VINTF%u: deallocated\n" , vintf->idx);tegra241_cmdqv_deinit_vintf (cmdqv, idx);
Signature · 1 line
2.43.0
RE: [PATCH v4 20/23] iommu/tegra241-cmdqv: Simplify deinit flow in tegra241_cmdqv_remove_vintf()
Quoted 20 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMThe current flow of
tegra241_cmdqv_remove_vintf()is:
1. For each LVCMDQ,tegra241_vintf_remove_lvcmdq():
a. Disable the LVCMDQ HW
b. Release the LVCMDQ SW resource
2. For current VINTF,tegra241_vintf_hw_deinit():
c. Disable all LVCMDQ HWs
d. Disable VINTF HWObviously, the step 1.a and the step 2.c are redundant.
Since
tegra241_vintf_hw_deinit()disables all of its LVCMDQ HWs, it could simplify the flow integra241_cmdqv_remove_vintf()by calling that first:
1. For current VINTF,tegra241_vintf_hw_deinit():
a. Disable all LVCMDQ HWs
b. Disable VINTF HW
2. Release all LVCMDQ SW resources
Quoted 6 lines
Drop
tegra241_vintf_remove_lvcmdq(), and movetegra241_vintf_free_lvcmdq()
as the new step 2.Signed-off-by Nicolin Chen <[email protected]>
Re: [PATCH v4 20/23] iommu/tegra241-cmdqv: Simplify deinit flow in tegra241_cmdqv_remove_vintf()
In reply to Nicolin ChenQuoted 17 lines · Nicolin Chen
The current flow of
tegra241_cmdqv_remove_vintf()is:
1. For each LVCMDQ,tegra241_vintf_remove_lvcmdq():
a. Disable the LVCMDQ HW
b. Release the LVCMDQ SW resource
2. For current VINTF,tegra241_vintf_hw_deinit():
c. Disable all LVCMDQ HWs
d. Disable VINTF HWObviously, the step 1.a and the step 2.c are redundant.
Since
tegra241_vintf_hw_deinit()disables all of its LVCMDQ HWs, it could simplify the flow integra241_cmdqv_remove_vintf()by calling that first:
1. For current VINTF,tegra241_vintf_hw_deinit():
a. Disable all LVCMDQ HWs
b. Disable VINTF HW
2. Release all LVCMDQ SW resources
Quoted 8 lines
Drop
tegra241_vintf_remove_lvcmdq(), and movetegra241_vintf_free_lvcmdq()as the new step 2.Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-)
Jason
PATCH v4 21/23 iommu/tegra241-cmdqv: Do not statically map LVCMDQs
In reply to Nicolin ChenTo simplify the mappings from global VCMDQs to VINTFs' LVCMDQs, the design chose to do static allocations and mappings in the global reset function.
However, with the user-owned VINTF support, it exposes a security concern:
if user space VM only wants one LVCMDQ for a VINTF, statically mapping two or more LVCMDQs creates a hidden VCMDQ that user space could DoS attack by writing random stuff to overwhelm the kernel with unhandleable IRQs.
Thus, to support the user-owned VINTF feature, a LVCMDQ mapping has to be done dynamically.
HW allows pre-assigning global VCMDQs in the CMDQ_ALLOC registers, without finalizing the mappings by keeping CMDQV_CMDQ_ALLOCATED=0. So, add a pair of map/unmap helper that simply sets/clears that bit.
Delay the LVCMDQ mappings to tegra241_vintf_hw_init(), and the unmappings to tegra241_vintf_hw_deinit().
However, the dynamic LVCMDQ mapping/unmapping can complicate the timing of calling tegra241_vcmdq_hw_init/deinit(), which write LVCMDQ address space, i.e. requiring LVCMDQ to be mapped. Highlight that with a note to the top of either of them.
--- .../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-)
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +33 -4
@@ -351,6 +351,7 @@ tegra241_cmdqv_get_cmdq(struct arm_smmu_device *smmu,/* HW Reset Functions */ +/* This function is for LVCMDQ, so @vcmdq must not be unmapped yet */ static void tegra241_vcmdq_hw_deinit (struct tegra241_vcmdq *vcmdq) {char header[64 ], *h =lvcmdq_error_header (vcmdq, header,64 );
@@ -379,6 +380,7 @@ static void tegra241_vcmdq_hw_deinit(struct tegra241_vcmdq *vcmdq)dev_dbg (vcmdq->cmdqv->dev,"%sdeinited\n" , h); } +/* This function is for LVCMDQ, so @vcmdq must be mapped prior */ static int tegra241_vcmdq_hw_init (struct tegra241_vcmdq *vcmdq) {char header[64 ], *h =lvcmdq_error_header (vcmdq, header,64 );
@@ -404,16 +406,42 @@ static int tegra241_vcmdq_hw_init(struct tegra241_vcmdq *vcmdq)return 0 ; } +/* Unmap a global VCMDQ from the pre-assigned LVCMDQ */ +static void tegra241_vcmdq_unmap_lvcmdq (struct tegra241_vcmdq *vcmdq) +{ +u32 regval =readl (REG_CMDQV (vcmdq->cmdqv,CMDQ_ALLOC (vcmdq->idx))); +char header[64 ], *h =lvcmdq_error_header (vcmdq, header,64 ); + +writel (regval & ~CMDQV_CMDQ_ALLOCATED , +REG_CMDQV (vcmdq->cmdqv,CMDQ_ALLOC (vcmdq->idx))); +dev_dbg (vcmdq->cmdqv->dev,"%sunmapped\n" , h); +} +static void tegra241_vintf_hw_deinit (struct tegra241_vintf *vintf) { -u16 lidx; +u16 lidx = vintf->cmdqv->num_lvcmdqs_per_vintf; -for (lidx =0 ; lidx < vintf->cmdqv->num_lvcmdqs_per_vintf; lidx++) -if (vintf->lvcmdqs && vintf->lvcmdqs[lidx]) +/* HW requires to unmap LVCMDQs in descending order */ +while (lidx--) { +if (vintf->lvcmdqs && vintf->lvcmdqs[lidx]) {tegra241_vcmdq_hw_deinit (vintf->lvcmdqs[lidx]); +tegra241_vcmdq_unmap_lvcmdq (vintf->lvcmdqs[lidx]); + } + }vintf_write_config (vintf,0 ); } +/* Map a global VCMDQ to the pre-assigned LVCMDQ */ +static void tegra241_vcmdq_map_lvcmdq (struct tegra241_vcmdq *vcmdq) +{ +u32 regval =readl (REG_CMDQV (vcmdq->cmdqv,CMDQ_ALLOC (vcmdq->idx))); +char header[64 ], *h =lvcmdq_error_header (vcmdq, header,64 ); + +writel (regval |CMDQV_CMDQ_ALLOCATED , +REG_CMDQV (vcmdq->cmdqv,CMDQ_ALLOC (vcmdq->idx))); +dev_dbg (vcmdq->cmdqv->dev,"%smapped\n" , h); +} +static int tegra241_vintf_hw_init (struct tegra241_vintf *vintf,bool hyp_own) {u32 regval;
@@ -441,8 +469,10 @@ static int tegra241_vintf_hw_init(struct tegra241_vintf *vintf, bool hyp_own)*/ vintf->hyp_own = !!(VINTF_HYP_OWN &readl (REG_VINTF (vintf,CONFIG ))); +/* HW requires to map LVCMDQs in ascending order */ for (lidx =0 ; lidx < vintf->cmdqv->num_lvcmdqs_per_vintf; lidx++) {if (vintf->lvcmdqs && vintf->lvcmdqs[lidx]) { +tegra241_vcmdq_map_lvcmdq (vintf->lvcmdqs[lidx]); ret =tegra241_vcmdq_hw_init (vintf->lvcmdqs[lidx]);if (ret) {tegra241_vintf_hw_deinit (vintf);
@@ -476,7 +506,6 @@ static int tegra241_cmdqv_hw_reset(struct arm_smmu_device *smmu)for (lidx =0 ; lidx < cmdqv->num_lvcmdqs_per_vintf; lidx++) { regval =FIELD_PREP (CMDQV_CMDQ_ALLOC_VINTF , idx); regval |=FIELD_PREP (CMDQV_CMDQ_ALLOC_LVCMDQ , lidx); - regval |=CMDQV_CMDQ_ALLOCATED ;writel_relaxed (regval,REG_CMDQV (cmdqv,CMDQ_ALLOC (qidx++))); }
Signature · 1 line
2.43.0
Tian, Kevin
maintainerRE: [PATCH v4 21/23] iommu/tegra241-cmdqv: Do not statically map LVCMDQs
Quoted 23 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMTo simplify the mappings from global VCMDQs to VINTFs' LVCMDQs, the design
chose to do static allocations and mappings in the global reset function.However, with the user-owned VINTF support, it exposes a security concern:
if user space VM only wants one LVCMDQ for a VINTF, statically mapping two or more LVCMDQs creates a hidden VCMDQ that user space could DoS attack by
writing random stuff to overwhelm the kernel with unhandleable IRQs.Thus, to support the user-owned VINTF feature, a LVCMDQ mapping has to be
done dynamically.HW allows pre-assigning global VCMDQs in the
CMDQ_ALLOCregisters, without
finalizing the mappings by keeping CMDQV_CMDQ_ALLOCATED=0. So, add a pair
of map/unmap helper that simply sets/clears that bit.
Quoted 3 lines
Delay the LVCMDQ mappings to
tegra241_vintf_hw_init(), and the unmappings
totegra241_vintf_hw_deinit().
I don't know the specifics of tegra241-cmdqv. But the current description is a bit misleading. for native tegra241_vintf_hw_init() is called from reset so the mapping is still enabled in that path. for user-owned then tegra241_vcmdq_map_lvcmdq() is called from tegra241_vintf_alloc_lvcmdq_user() instead of tegra241_vintf_hw_init().
so nothing is actually delayed in this patch.
Re: [PATCH v4 21/23] iommu/tegra241-cmdqv: Do not statically map LVCMDQs
Quoted 27 lines · Kevin
Quoted 23 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AMTo simplify the mappings from global VCMDQs to VINTFs' LVCMDQs, the design
chose to do static allocations and mappings in the global reset function.However, with the user-owned VINTF support, it exposes a security concern:
if user space VM only wants one LVCMDQ for a VINTF, statically mapping two or more LVCMDQs creates a hidden VCMDQ that user space could DoS attack by
writing random stuff to overwhelm the kernel with unhandleable IRQs.Thus, to support the user-owned VINTF feature, a LVCMDQ mapping has to be
done dynamically.HW allows pre-assigning global VCMDQs in the
CMDQ_ALLOCregisters, without
finalizing the mappings by keeping CMDQV_CMDQ_ALLOCATED=0. So, add a pair
of map/unmap helper that simply sets/clears that bit.Quoted 3 lines
Delay the LVCMDQ mappings to
tegra241_vintf_hw_init(), and the unmappings
totegra241_vintf_hw_deinit().
Quoted 7 lines
I don't know the specifics of tegra241-cmdqv. But the current description is a bit misleading. for native
tegra241_vintf_hw_init()is called from reset so the mapping is still enabled in that path. for user-owned thentegra241_vcmdq_map_lvcmdq()is called fromtegra241_vintf_alloc_lvcmdq_user()instead oftegra241_vintf_hw_init().so nothing is actually delayed in this patch.
It is delayed from tegra241_cmdqv_hw_reset() to later functions.
The in-kernel VINTF0 is not literally delayed fundamentally as you mentioned because it needs to be allocated/mapped in probe().
The user-space VINTFs was previously allocated/mapped in probe(), now it's moved to user space handler, i.e. no mapping until user space asks for one.
Perhaps I can make this "delay" statement more narrative for a clarification.
Thanks
Nicolin
PATCH v4 22/23 iommu/tegra241-cmdqv: Add user-space use support
In reply to Nicolin ChenThe CMDQV HW supports a user-space use for virtualization cases. It allows the VM to issue guest-level TLBI or ATC_INV commands directly to the queue and executes them without a VMEXIT, as HW will replace the VMID field in a TLBI command and the SID field in an ATC_INV command with the preset VMID and SID.
This is built upon the vIOMMU infrastructure by allowing VMM to allocate a VINTF (as a vIOMMU object) and assign VCMDQs (HW QUEUE objs) to the VINTF.
So firstly, replace the standard vSMMU model with the VINTF implementation but reuse the standard cache_invalidate op (for unsupported commands) and the standard alloc_domain_nested op (for standard nested STE).
Each VINTF has two 64KB MMIO pages (128B per logical VCMDQ):
- Page0 (directly accessed by guest) has all the control and status bits.
- Page1 (trapped by VMM) has guest-owned queue memory location/size info.
VMM should trap the emulated VINTF0's page1 of the guest VM for the guest- level VCMDQ location/size info and forward that to the kernel to translate to a physical memory location to program the VCMDQ HW during an allocation call. Then, it should mmap the assigned VINTF's page0 to the VINTF0 page0 of the guest VM. This allows the guest OS to read and write the guest-own VINTF's page0 for direct control of the VCMDQ HW.
For ATC invalidation commands that hold an SID, it requires all devices to register their virtual SIDs to the SID_MATCH registers and their physical SIDs to the pairing SID_REPLACE registers, so that HW can use those as a lookup table to replace those virtual SIDs with the correct physical SIDs. Thus, implement the driver-allocated vDEVICE op with a tegra241_vintf_sid structure to allocate SID_REPLACE and to program the SIDs accordingly.
This enables the HW accelerated feature for NVIDIA Grace CPU. Compared to the standard SMMUv3 operating in the nested translation mode trapping CMDQ for TLBI and ATC_INV commands, this gives a huge performance improvement:
70% to 90% reductions of invalidation time were measured by various DMA unmap tests running in a guest OS.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 15 + include/uapi/linux/iommufd.h | 51 ++- .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 6 +- .../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 413 +++++++++++++++++- 4 files changed, 476 insertions(+), 9 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +15 -0
@@ -1000,6 +1000,14 @@ int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu, struct arm_smmu_cmdq *cmdq, u64 *cmds, int n, bool sync); +static inline phys_addr_t +arm_smmu_domain_ipa_to_pa (struct arm_smmu_domain *smmu_domain,u64 ipa) +{ +if (WARN_ON_ONCE (smmu_domain->stage !=ARM_SMMU_DOMAIN_S2 )) +return 0 ; +return iommu_iova_to_phys (&smmu_domain->domain, ipa); +} +#ifdef CONFIG_ARM_SMMU_V3_SVA bool arm_smmu_sva_supported (struct arm_smmu_device *smmu);bool arm_smmu_master_sva_supported (struct arm_smmu_master *master);
@@ -1076,9 +1084,16 @@ int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,void arm_smmu_attach_commit_vmaster (struct arm_smmu_attach_state *state);void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master);int arm_vmaster_report_event (struct arm_smmu_vmaster *vmaster,u64 *evt); +struct iommu_domain * +arm_vsmmu_alloc_domain_nested (struct iommufd_viommu *viommu,u32 flags, +const struct iommu_user_data *user_data); +int arm_vsmmu_cache_invalidate (struct iommufd_viommu *viommu, +struct iommu_user_data_array *array);#else #define arm_smmu_hw_info NULL#define arm_vsmmu_alloc NULL +#define arm_vsmmu_alloc_domain_nested NULL +#define arm_vsmmu_cache_invalidate NULL static inline int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,
include/uapi/linux/iommufd.h +49 -2
80 lines not on this page · raw message
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +3 -3
@@ -227,7 +227,7 @@ static int arm_smmu_validate_vste(struct iommu_hwpt_arm_smmuv3 *arg,return 0 ; } -static struct iommu_domain * +struct iommu_domain *arm_vsmmu_alloc_domain_nested (struct iommufd_viommu *viommu,u32 flags,const struct iommu_user_data *user_data) {
@@ -338,8 +338,8 @@ static int arm_vsmmu_convert_user_cmd(struct arm_vsmmu *vsmmu,return 0 ; } -static int arm_vsmmu_cache_invalidate (struct iommufd_viommu *viommu, -struct iommu_user_data_array *array) +int arm_vsmmu_cache_invalidate (struct iommufd_viommu *viommu, +struct iommu_user_data_array *array) { struct arm_vsmmu *vsmmu = container_of(viommu, struct arm_vsmmu, core);struct arm_smmu_device *smmu = vsmmu->smmu;
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +409 -4
579 lines not on this page · raw message
Signature · 1 line
2.43.0
Tian, Kevin
maintainerRE: [PATCH v4 22/23] iommu/tegra241-cmdqv: Add user-space use support
Quoted 13 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AM/**
* structiommu_hw_info_arm_smmuv3- ARM SMMUv3 hardware
information
* (IOMMU_HW_INFO_TYPE_ARM_SMMUV3)
*
- * @flags: Must be set to 0
- * @impl: Must be 0
+ * @flags: Combination of enumiommu_hw_info_arm_smmuv3_flags
+ * @impl: Implementation-defined bits when the following flags are set:
+ * -IOMMU_HW_INFO_ARM_SMMUV3_HAS_TEGRA241_CMDQV
Quoted 4 lines
+ * Bits[15:12] - Log2 of the total number of SID replacements + * Bits[11:08] - Log2 of the total number of VINTFs per vIOMMU + * Bits[07:04] - Log2 of the total number of VCMDQs per vIOMMU + * Bits[03:00] - Version number for the CMDQ-V HW
hmm throughout this series I drew an equation between VINTF and vIOMMU. Not sure how multiple VINTFs can be represented w/o introducing more objects. Do we want to keep such info here?
include/uapi/linux/iommufd.h struct iommu_veventq_alloc { · line 1204
Quoted 7 lines
(quoted diff) +1
-0+ * - suggest to back the queue memory with contiguous physicalpages or
(quoted diff) +1
-0+ * a single huge page with alignment of the queue size, limitvSMMU's
include/uapi/linux/iommufd.h +3
-0+* IDR1.CMDQS to the huge page size divided by 16 bytes +*/ + IOMMU_HW_QUEUE_TYPE_TEGRA241_CMDQV = 1,
Not sure about the last sentence. 'limit' refers to a certain action which the user should perform?
Quoted 7 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +7
-0+ + ret =tegra241_vintf_init_lvcmdq (vintf, lidx, vcmdq); +if (ret) + goto undepend_vcmdq; + +dev_dbg (cmdqv->dev,"%sallocated\n" , +lvcmdq_error_header (vcmdq, header,64 ));
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c struct arm_smmu_device *tegra241_cmdqv_probe(struct arm_smmu_device *smmu) · line 1125
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +10
-0+ +tegra241_vcmdq_map_lvcmdq (vcmdq); + + vcmdq->cmdq.q.q_base = q_base &VCMDQ_ADDR ; + vcmdq->cmdq.q.q_base |= log2size; + + ret =tegra241_vcmdq_hw_init_user (vcmdq); +if (ret) + goto unmap_lvcmdq; + vintf->lvcmdqs[lidx] = vcmdq;
this is already done in tegra241_vintf_init_lvcmdq().
Re: [PATCH v4 22/23] iommu/tegra241-cmdqv: Add user-space use support
Quoted 18 lines · Kevin
Quoted 13 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AM/**
* structiommu_hw_info_arm_smmuv3- ARM SMMUv3 hardware
information
* (IOMMU_HW_INFO_TYPE_ARM_SMMUV3)
*
- * @flags: Must be set to 0
- * @impl: Must be 0
+ * @flags: Combination of enumiommu_hw_info_arm_smmuv3_flags
+ * @impl: Implementation-defined bits when the following flags are set:
+ * -IOMMU_HW_INFO_ARM_SMMUV3_HAS_TEGRA241_CMDQVQuoted 4 lines
+ * Bits[15:12] - Log2 of the total number of SID replacements + * Bits[11:08] - Log2 of the total number of VINTFs per vIOMMU + * Bits[07:04] - Log2 of the total number of VCMDQs per vIOMMU + * Bits[03:00] - Version number for the CMDQ-V HW
Quoted 3 lines
hmm throughout this series I drew an equation between VINTF and vIOMMU. Not sure how multiple VINTFs can be represented w/o introducing more objects. Do we want to keep such info here?
You are right that VINTF=vIOMMU. This is a per SMMU instance ioctl. So, each VM should only have one VTINF/vIOMMU per SMMU instance.
For multi-VINTF (multi-vIOMMU) case, there needs to be more SMMUs backing passthrough devices being assigned to the VM.
What exactly the concern of keeping this info here?
Quoted 10 lines
include/uapi/linux/iommufd.h struct iommu_veventq_alloc { · line 1204
Quoted 7 lines
(quoted diff) +1
-0+ * - suggest to back the queue memory with contiguous physicalpages or
(quoted diff) +1
-0+ * a single huge page with alignment of the queue size, limitvSMMU's
include/uapi/linux/iommufd.h +3
-0+* IDR1.CMDQS to the huge page size divided by 16 bytes +*/ + IOMMU_HW_QUEUE_TYPE_TEGRA241_CMDQV = 1,Not sure about the last sentence. 'limit' refers to a certain action which the user should perform?
Yes, set vSMMU's IDR1.CMDQS field up to the huge page size divided by 16 bytes, e.g. if using one 2MB huge page backing the queue memory, VMM should set IDR1.CMDQS no higher than 17:
2MB = (1 << 17) * 16B
Certainly, it can set to lower than 17. So it's an upper "limit".
Or any better word in your mind that can be less confusing?
Quoted 11 lines
Quoted 11 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +11
-0+ + ret =tegra241_vintf_init_lvcmdq (vintf, lidx, vcmdq); +if (ret) + goto undepend_vcmdq; + +dev_dbg (cmdqv->dev,"%sallocated\n" , +lvcmdq_error_header (vcmdq, header,64 )); + +tegra241_vcmdq_map_lvcmdq (vcmdq); + + vcmdq->cmdq.q.q_base = q_base &VCMDQ_ADDR ;
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c struct arm_smmu_device *tegra241_cmdqv_probe(struct arm_smmu_device *smmu) · line 1125
Quoted 6 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +6
-0+ vcmdq->cmdq.q.q_base |= log2size; + + ret =tegra241_vcmdq_hw_init_user (vcmdq); +if (ret) + goto unmap_lvcmdq; + vintf->lvcmdqs[lidx] = vcmdq;this is already done in
tegra241_vintf_init_lvcmdq().
Oh, will drop that.
Thanks
Nicolin
Tian, Kevin
maintainerRE: [PATCH v4 22/23] iommu/tegra241-cmdqv: Add user-space use support
Quoted 26 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 16, 2025 1:14 AMQuoted 18 lines · Kevin
Quoted 13 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AM/**
* structiommu_hw_info_arm_smmuv3- ARM SMMUv3 hardware
information
* (IOMMU_HW_INFO_TYPE_ARM_SMMUV3)
*
- * @flags: Must be set to 0
- * @impl: Must be 0
+ * @flags: Combination of enumiommu_hw_info_arm_smmuv3_flags
+ * @impl: Implementation-defined bits when the following flags are set:
+ * -IOMMU_HW_INFO_ARM_SMMUV3_HAS_TEGRA241_CMDQVQuoted 4 lines
+ * Bits[15:12] - Log2 of the total number of SID replacements + * Bits[11:08] - Log2 of the total number of VINTFs per vIOMMU + * Bits[07:04] - Log2 of the total number of VCMDQs per vIOMMU + * Bits[03:00] - Version number for the CMDQ-V HWQuoted 3 lines
hmm throughout this series I drew an equation between VINTF and vIOMMU. Not sure how multiple VINTFs can be represented w/o introducing more objects. Do we want to keep such info here?
Quoted 7 lines
You are right that VINTF=vIOMMU. This is a per SMMU instance ioctl. So, each VM should only have one VTINF/vIOMMU per SMMU instance.
For multi-VINTF (multi-vIOMMU) case, there needs to be more SMMUs backing passthrough devices being assigned to the VM.
What exactly the concern of keeping this info here?
First, you agreed that VINTF=vIOMMU, then "total number of VINTFs per vIOMMU" doesn't make sense as it's fixed to 1 in concept.
Then, each VM can only get one VINTF/vIOMMU per SMMU instance, and this ioctl is per SMMU instance. This also implies that only one VINTF can be reported in the ioctl.
In multi-VINTF case, the VM should get 1VINTF per ioctl from each SMMU backing passthrough devices.
Then what is the point of " Bits[11:08] - Log2 of the total number of VINTFs per vIOMMU "?
Quoted 12 lines
Quoted 11 lines
include/uapi/linux/iommufd.h struct iommu_veventq_alloc { · line 1204
Quoted 8 lines
(quoted diff) +1
-0+ * - suggest to back the queue memory with contiguous physicalpages or
(quoted diff) +1
-0+ * a single huge page with alignment of the queue size, limitvSMMU's
include/uapi/linux/iommufd.h +3
-0+* IDR1.CMDQS to the huge page size divided by 16 bytes +*/ + IOMMU_HW_QUEUE_TYPE_TEGRA241_CMDQV = 1,Not sure about the last sentence. 'limit' refers to a certain action which the user should perform?
Quoted 8 lines
Yes, set vSMMU's IDR1.CMDQS field up to the huge page size divided by 16 bytes, e.g. if using one 2MB huge page backing the queue memory, VMM should set IDR1.CMDQS no higher than 17:
2MB = (1 << 17) * 16BCertainly, it can set to lower than 17. So it's an upper "limit".
Or any better word in your mind that can be less confusing?
No. I misread 'vSMMU' as 'SMMU'.
Re: [PATCH v4 22/23] iommu/tegra241-cmdqv: Add user-space use support
Quoted 41 lines · Kevin
Quoted 26 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 16, 2025 1:14 AMQuoted 18 lines · Kevin
Quoted 13 lines
From: Nicolin Chen <[email protected]>
Sent: Friday, May 9, 2025 11:03 AM/**
* structiommu_hw_info_arm_smmuv3- ARM SMMUv3 hardware
information
* (IOMMU_HW_INFO_TYPE_ARM_SMMUV3)
*
- * @flags: Must be set to 0
- * @impl: Must be 0
+ * @flags: Combination of enumiommu_hw_info_arm_smmuv3_flags
+ * @impl: Implementation-defined bits when the following flags are set:
+ * -IOMMU_HW_INFO_ARM_SMMUV3_HAS_TEGRA241_CMDQVQuoted 4 lines
+ * Bits[15:12] - Log2 of the total number of SID replacements + * Bits[11:08] - Log2 of the total number of VINTFs per vIOMMU + * Bits[07:04] - Log2 of the total number of VCMDQs per vIOMMU + * Bits[03:00] - Version number for the CMDQ-V HWQuoted 3 lines
hmm throughout this series I drew an equation between VINTF and vIOMMU. Not sure how multiple VINTFs can be represented w/o introducing more objects. Do we want to keep such info here?
Quoted 7 lines
You are right that VINTF=vIOMMU. This is a per SMMU instance ioctl. So, each VM should only have one VTINF/vIOMMU per SMMU instance.
For multi-VINTF (multi-vIOMMU) case, there needs to be more SMMUs backing passthrough devices being assigned to the VM.
What exactly the concern of keeping this info here?
First, you agreed that VINTF=vIOMMU, then "total number of VINTFs per vIOMMU" doesn't make sense as it's fixed to 1 in concept.
Then, each VM can only get one VINTF/vIOMMU per SMMU instance, and this ioctl is per SMMU instance. This also implies that only one VINTF can be reported in the ioctl.
Quoted 5 lines
In multi-VINTF case, the VM should get 1VINTF per ioctl from each SMMU backing passthrough devices.
Then what is the point of " Bits[11:08] - Log2 of the total number of VINTFs per vIOMMU "?
It was not there in the previous version. Pranjal pointed out that it was missing a field.
You are right that this field must set to 0, indicating only single VINTF is allowed.
Given Jason's comments about this impl rework (to a data structure), I think I will just drop this number of VTINFs. Instead will add a line of comments say that VMM should set this field to 0, i.e. only provide VM one VINTF per SMMU.
Thanks
Nicolin
PATCH v4 23/23 iommu/tegra241-cmdqv: Add IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV support
In reply to Nicolin ChenAdd a new vEVENTQ type for VINTFs that are assigned to the user space. Simply report the two 64-bit LVCMDQ_ERR_MAPs register values.
--- include/uapi/linux/iommufd.h | 15 +++++++++++++ .../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 22 +++++++++++++++++++ 2 files changed, 37 insertions(+)
include/uapi/linux/iommufd.h +15 -0
@@ -1114,10 +1114,12 @@ struct iommufd_vevent_header {* enum iommu_veventq_type - Virtual Event Queue Type * @IOMMU_VEVENTQ_TYPE_DEFAULT: Reserved for future use * @IOMMU_VEVENTQ_TYPE_ARM_SMMUV3: ARM SMMUv3 Virtual Event Queue +* @IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV: NVIDIA Tegra241 CMDQV Extension IRQ */ enum iommu_veventq_type {IOMMU_VEVENTQ_TYPE_DEFAULT =0 ,IOMMU_VEVENTQ_TYPE_ARM_SMMUV3 =1 , +IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV =2 , };/**
@@ -1141,6 +1143,19 @@ struct iommu_vevent_arm_smmuv3 {__aligned_le64 evt[4 ]; }; +/** +* struct iommu_vevent_tegra241_cmdqv - Tegra241 CMDQV IRQ +* (IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV) +* @lvcmdq_err_map: 128-bit logical vcmdq error map, little-endian. +* (Refer to register LVCMDQ_ERR_MAPs per VINTF ) +* +* The 128-bit register value from HW exclusively reflect the error bits for a +* Virtual Interface represented by a vIOMMU object. Read and report directly. +*/ +struct iommu_vevent_tegra241_cmdqv { +__aligned_le64 lvcmdq_err_map[2 ]; +}; +/** * struct iommu_veventq_alloc - ioctl(IOMMU_VEVENTQ_ALLOC) * @size: sizeof(struct iommu_veventq_alloc)
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +22 -0
@@ -292,6 +292,20 @@ static inline int vcmdq_write_config(struct tegra241_vcmdq *vcmdq, u32 regval)/* ISR Functions */ +static void tegra241_vintf_user_handle_error (struct tegra241_vintf *vintf) +{ +struct iommufd_viommu *viommu = &vintf->vsmmu.core; +struct iommu_vevent_tegra241_cmdqv vevent_data; +int i; + +for (i =0 ; i <LVCMDQ_ERR_MAP_NUM_64 ; i++) + vevent_data.lvcmdq_err_map[i] = +readq_relaxed (REG_VINTF (vintf,LVCMDQ_ERR_MAP_64 (i))); + +iommufd_viommu_report_event (viommu,IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV , + &vevent_data,sizeof (vevent_data)); +} +static void tegra241_vintf0_handle_error (struct tegra241_vintf *vintf) {int i;
@@ -337,6 +351,14 @@ static irqreturn_t tegra241_cmdqv_isr(int irq, void *devid) vintf_map &= ~BIT_ULL (0 ); } +/* Handle other user VINTFs and their LVCMDQs */ +while (vintf_map) { +unsigned long idx =__ffs64 (vintf_map); + +tegra241_vintf_user_handle_error (cmdqv->vintfs[idx]); + vintf_map &= ~BIT_ULL (idx); + } +return IRQ_HANDLED ; }
Signature · 1 line
2.43.0