iommufd: Add vIOMMU infrastructure (Part-3: vEVENTQ)
Subsystems (5)
v7 cover iommufd: Add vIOMMU infrastructure (Part-3: vEVENTQ)
As the vIOMMU infrastructure series part-3, this introduces a new vEVENTQ object. The existing FAULT object provides a nice notification pathway to the user space with a queue already, so let vEVENTQ reuse that.
Mimicing the HWPT structure, add a common EVENTQ structure to support its
derivatives: IOMMUFD_OBJ_FAULT (existing) and IOMMUFD_OBJ_VEVENTQ (new).
An IOMMUFD_CMD_VEVENTQ_ALLOC is introduced to allocate vEVENTQ object for vIOMMUs. One vIOMMU can have multiple vEVENTQs in different types but can not support multiple vEVENTQs in the same type.
The forwarding part is fairly simple but might need to replace a physical device ID with a virtual device ID in a driver-level event data structure. So, this also adds some helpers for drivers to use.
As usual, this series comes with the selftest coverage for this new ioctl and with a real world use case in the ARM SMMUv3 driver.
This is on Github:
https://github.com/nicolinc/iommufd/commits/iommufd_veventq-v7
Paring QEMU branch for testing:
https://github.com/nicolinc/qemu/commits/wip/for_iommufd_veventq-v7
Changelog
v7
* Rebase on Jason's for-next tree for latest fault.c
* Add Reviewed-by
* Update commit logs
* Add __reserved field sanity
* Skip kfree() on the static header
* Replace "bool on_list" with list_is_last()
* Use u32 for flags in iommufd_vevent_header
* Drop casting in iommufd_viommu_get_vdev_id()
* Update the bounding logic to veventq->sequence
* Add missing cpu_to_le64() around STRTAB_STE_1_MEV
* Reuse veventq->common.lock to fence sequence and num_events
* Rename overflow to lost_events and log it in upon kmalloc failure
* Correct the error handling part in iommufd_veventq_deliver_fetch()
* Add an arm_smmu_clear_vmaster() to simplify identity/blocked domain attach ops
* Add additional four event records to forward to user space VM, and update the uAPI doc
* Reuse the existing smmu->streams_mutex lock to fence master->vmaster pointer, instead of adding a new rwsem
v6
https://lore.kernel.org/all/[email protected]/
* Drop supports_veventq viommu op
* Split bug/cosmetics fixes out of the series
* Drop the blocking mutex around copy_to_user()
* Add veventq_depth in uAPI to limit vEVENTQ size
* Revise the documentation for a clear description
* Fix sparse warnings in arm_vmaster_report_event()
* Rework iommufd_viommu_get_vdev_id() to return -ENOENT v.s. 0
* Allow Abort/Bypass STEs to allocate vEVENTQ and set STE.MEV for DoS mitigations
v5
https://lore.kernel.org/all/[email protected]/
* Add Reviewed-by from Baolu
* Reorder the OBJ list as well
* Fix alphabetical order after renaming in v4
* Add supports_veventq viommu op for vEVENTQ type validation
v4
https://lore.kernel.org/all/[email protected]/
* Rename "vIRQ" to "vEVENTQ"
* Use flexible array in struct iommufd_vevent
* Add the new ioctl command to union ucmd_buffer
* Fix the alphabetical order in union ucmd_buffer too
* Rename _TYPE_NONE to _TYPE_DEFAULT aligning with vIOMMU naming
v3
https://lore.kernel.org/all/[email protected]/
* Rebase on Will's for-joerg/arm-smmu/updates for arm_smmu_event series
* Add "Reviewed-by" lines from Kevin
* Fix typos in comments, kdocs, and jump tags
* Add a patch to sort struct iommufd_ioctl_op
* Update iommufd's userpsace-api documentation
* Update uAPI kdoc to quote SMMUv3 offical spec
* Drop the unused workqueue in struct iommufd_virq
* Drop might_sleep() in iommufd_viommu_report_irq() helper
* Add missing "break" in iommufd_viommu_get_vdev_id() helper
* Shrink the scope of the vmaster's read lock in SMMUv3 driver
* Pass in two arguments to iommufd_eventq_virq_handler() helper
* Move "!ops || !ops->read" validation into iommufd_eventq_init()
* Move "fault->ictx = ictx" closer to iommufd_ctx_get(fault->ictx)
* Update commit message for arm_smmu_attach_prepare/commit_vmaster()
* Keep "iommufd_fault" as-is and rename "iommufd_eventq_virq" to just "iommufd_virq"
v2
https://lore.kernel.org/all/[email protected]/
* Rebase on v6.13-rc1
* Add IOPF and vIRQ in iommufd.rst (userspace-api)
* Add a proper locking in iommufd_event_virq_destroy
* Add iommufd_event_virq_abort with a lockdep_assert_held
* Rename "EVENT_*" to "EVENTQ_*" to describe the objects better
* Reorganize flows in iommufd_eventq_virq_alloc for abort() to work
* Adde struct arm_smmu_vmaster to store vSID upon attaching to a nested domain, calling a newly added iommufd_viommu_get_vdev_id helper
* Adde an arm_vmaster_report_event helper in arm-smmu-v3-iommufd file to simplify the routine in arm_smmu_handle_evt() of the main driver
v1
https://lore.kernel.org/all/[email protected]/
Thanks!
Nicolin
Nicolin Chen (14):
iommufd/fault: Move two fault functions out of the header iommufd/fault: Add an iommufd_fault_init() helper
iommufd: Abstract an iommufd_eventq from iommufd_fault
iommufd: Rename fault.c to eventq.c
iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC iommufd/viommu: Add iommufd_viommu_get_vdev_id helper iommufd/viommu: Add iommufd_viommu_report_event helper iommufd/selftest: Require vdev_id when attaching to a nested domain iommufd/selftest: Add IOMMU_TEST_OP_TRIGGER_VEVENT for vEVENTQ
coverage
iommufd/selftest: Add IOMMU_VEVENTQ_ALLOC test coverage
Documentation: userspace-api: iommufd: Update FAULT and VEVENTQ iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster iommu/arm-smmu-v3: Report events that belong to devices attached to
vIOMMU
iommu/arm-smmu-v3: Set MEV bit in nested STE for DoS mitigations
drivers/iommu/iommufd/Makefile | 2 +- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 35 + drivers/iommu/iommufd/iommufd_private.h | 135 +++- drivers/iommu/iommufd/iommufd_test.h | 10 + include/linux/iommufd.h | 23 + include/uapi/linux/iommufd.h | 105 +++ tools/testing/selftests/iommu/iommufd_utils.h | 115 ++++ .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 72 +++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 79 ++- drivers/iommu/iommufd/driver.c | 72 +++ drivers/iommu/iommufd/eventq.c | 597 ++++++++++++++++++ drivers/iommu/iommufd/fault.c | 342 ---------- drivers/iommu/iommufd/hw_pagetable.c | 6 +- drivers/iommu/iommufd/main.c | 7 + drivers/iommu/iommufd/selftest.c | 54 ++ drivers/iommu/iommufd/viommu.c | 2 + tools/testing/selftests/iommu/iommufd.c | 36 ++ .../selftests/iommu/iommufd_fail_nth.c | 7 + Documentation/userspace-api/iommufd.rst | 17 + 19 files changed, 1308 insertions(+), 408 deletions(-) create mode 100644 drivers/iommu/iommufd/eventq.c delete mode 100644 drivers/iommu/iommufd/fault.c
base-commit: 598749522d4254afb33b8a6c1bea614a95896868
Signature · 1 line
2.43.0
PATCH v7 1/14 iommufd/fault: Move two fault functions out of the header
There is no need to keep them in the header. The vEVENTQ version of these two functions will turn out to be a different implementation and will not share with this fault version. Thus, move them out of the header.
--- drivers/iommu/iommufd/iommufd_private.h | 25 ------------------------- drivers/iommu/iommufd/fault.c | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 25 deletions(-)
drivers/iommu/iommufd/iommufd_private.h +0 -25
@@ -472,31 +472,6 @@ struct iommufd_fault {struct wait_queue_head wait_queue; }; -/* Fetch the first node out of the fault->deliver list */ -static inline struct iopf_group * -iommufd_fault_deliver_fetch (struct iommufd_fault *fault) -{ -struct list_head *list = &fault->deliver; -struct iopf_group *group =NULL ; - -spin_lock (&fault->lock); -if (!list_empty (list)) { - group = list_first_entry(list, struct iopf_group, node); -list_del (&group->node); - } -spin_unlock (&fault->lock); -return group; -} - -/* Restore a node back to the head of the fault->deliver list */ -static inline void iommufd_fault_deliver_restore (struct iommufd_fault *fault, -struct iopf_group *group) -{ -spin_lock (&fault->lock); -list_add (&group->node, &fault->deliver); -spin_unlock (&fault->lock); -} -struct iommufd_attach_handle {struct iommu_attach_handle handle;struct iommufd_device *idev;
drivers/iommu/iommufd/fault.c +25 -0
@@ -138,6 +138,31 @@ static void iommufd_compose_fault_message(struct iommu_fault *fault, hwpt_fault->cookie = cookie; } +/* Fetch the first node out of the fault->deliver list */ +static struct iopf_group * +iommufd_fault_deliver_fetch (struct iommufd_fault *fault) +{ +struct list_head *list = &fault->deliver; +struct iopf_group *group =NULL ; + +spin_lock (&fault->lock); +if (!list_empty (list)) { + group = list_first_entry(list, struct iopf_group, node); +list_del (&group->node); + } +spin_unlock (&fault->lock); +return group; +} + +/* Restore a node back to the head of the fault->deliver list */ +static void iommufd_fault_deliver_restore (struct iommufd_fault *fault, +struct iopf_group *group) +{ +spin_lock (&fault->lock); +list_add (&group->node, &fault->deliver); +spin_unlock (&fault->lock); +} +static ssize_t iommufd_fault_fops_read (struct file *filep,char __user *buf,size_t count,loff_t *ppos) {
Signature · 1 line
2.43.0
PATCH v7 2/14 iommufd/fault: Add an iommufd_fault_init() helper
In reply to Nicolin ChenThe infrastructure of a fault object will be shared with a new vEVENTQ object in a following change. Add an iommufd_fault_init helper and an INIT_EVENTQ_FOPS marco for a vEVENTQ allocator to use too.
Reorder the iommufd_ctx_get and refcount_inc, to keep them symmetrical with the iommufd_fault_fops_release().
Since the new vEVENTQ doesn't need "response" and its "mutex", so keep the xa_init_flags and mutex_init in their original locations.
--- drivers/iommu/iommufd/fault.c | 70 +++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 28 deletions(-)
drivers/iommu/iommufd/fault.c +42 -28
@@ -280,20 +280,49 @@ static int iommufd_fault_fops_release(struct inode *inode, struct file *filep)return 0 ; } -static const struct file_operations iommufd_fault_fops = { - .owner =THIS_MODULE , - .open = nonseekable_open, - .read = iommufd_fault_fops_read, - .write = iommufd_fault_fops_write, - .poll = iommufd_fault_fops_poll, - .release = iommufd_fault_fops_release, -}; +#define INIT_FAULT_FOPS (read_op, write_op) \ + ((const struct file_operations){ \ + .owner = THIS_MODULE, \ + .open = nonseekable_open, \ + .read = read_op, \ + .write = write_op, \ + .poll = iommufd_fault_fops_poll, \ + .release = iommufd_fault_fops_release, \ + }) + +static int iommufd_fault_init (struct iommufd_fault *fault,char *name, +struct iommufd_ctx *ictx, +const struct file_operations *fops) +{ +struct file *filep; +int fdno; + +spin_lock_init (&fault->lock); +INIT_LIST_HEAD (&fault->deliver); +init_waitqueue_head (&fault->wait_queue); + + filep =anon_inode_getfile (name, fops, fault,O_RDWR ); +if (IS_ERR (filep)) +return PTR_ERR (filep); + + fault->ictx = ictx; +iommufd_ctx_get (fault->ictx); + fault->filep = filep; +refcount_inc (&fault->obj.users); + + fdno =get_unused_fd_flags (O_CLOEXEC ); +if (fdno <0 ) +fput (filep); +return fdno; +} + +static const struct file_operations iommufd_fault_fops = +INIT_FAULT_FOPS (iommufd_fault_fops_read, iommufd_fault_fops_write);int iommufd_fault_alloc (struct iommufd_ucmd *ucmd) {struct iommu_fault_alloc *cmd = ucmd->cmd;struct iommufd_fault *fault; -struct file *filep;int fdno;int rc;
@@ -304,28 +333,14 @@ int iommufd_fault_alloc(struct iommufd_ucmd *ucmd)if (IS_ERR (fault))return PTR_ERR (fault); - fault->ictx = ucmd->ictx; -INIT_LIST_HEAD (&fault->deliver);xa_init_flags (&fault->response,XA_FLAGS_ALLOC1 );mutex_init (&fault->mutex); -spin_lock_init (&fault->lock); -init_waitqueue_head (&fault->wait_queue); - - filep =anon_inode_getfile ("[iommufd-pgfault]" , &iommufd_fault_fops, - fault,O_RDWR ); -if (IS_ERR (filep)) { - rc =PTR_ERR (filep); - goto out_abort; - } -refcount_inc (&fault->obj.users); -iommufd_ctx_get (fault->ictx); - fault->filep = filep; - - fdno =get_unused_fd_flags (O_CLOEXEC ); + fdno =iommufd_fault_init (fault,"[iommufd-pgfault]" , ucmd->ictx, + &iommufd_fault_fops);if (fdno <0 ) { rc = fdno; - goto out_fput; + goto out_abort; } cmd->out_fault_id = fault->obj.id;
@@ -341,8 +356,7 @@ int iommufd_fault_alloc(struct iommufd_ucmd *ucmd)return 0 ; out_put_fdno:put_unused_fd (fdno); -out_fput: -fput (filep); +fput (fault->filep); out_abort:iommufd_object_abort_and_destroy (ucmd->ictx, &fault->obj);
Signature · 1 line
2.43.0
PATCH v7 3/14 iommufd: Abstract an iommufd_eventq from iommufd_fault
In reply to Nicolin ChenThe fault object was designed exclusively for hwpt's IO page faults (PRI). But its queue implementation can be reused for other purposes too, such as hardware IRQ and event injections to user space.
Meanwhile, a fault object holds a list of faults. So it's more accurate to call it a "fault queue". Combining the reusing idea above, abstract a new iommufd_eventq as a common structure embedded into struct iommufd_fault, similar to hwpt_paging holding a common hwpt.
Add a common iommufd_eventq_ops and iommufd_eventq_init to prepare for an IOMMUFD_OBJ_VEVENTQ (vIOMMU Event Queue).
--- drivers/iommu/iommufd/iommufd_private.h | 28 ++++-- drivers/iommu/iommufd/fault.c | 111 +++++++++++++----------- drivers/iommu/iommufd/hw_pagetable.c | 6 +- 3 files changed, 82 insertions(+), 63 deletions(-)
drivers/iommu/iommufd/iommufd_private.h +19 -9
@@ -454,20 +454,13 @@ void iopt_remove_access(struct io_pagetable *iopt, u32 iopt_access_list_id);void iommufd_access_destroy_object (struct iommufd_object *obj); -/* -* An iommufd_fault object represents an interface to deliver I/O page faults -* to the user space. These objects are created/destroyed by the user space and -* associated with hardware page table objects during page-table allocation. -*/ -struct iommufd_fault { +struct iommufd_eventq {struct iommufd_object obj;struct iommufd_ctx *ictx;struct file *filep;spinlock_t lock;/* protects the deliver list */ struct list_head deliver; -struct mutex mutex;/* serializes response flows */ -struct xarray response;struct wait_queue_head wait_queue; };
@@ -480,12 +473,29 @@ struct iommufd_attach_handle {/* Convert an iommu attach handle to iommufd handle. */ #define to_iommufd_handle (hdl) container_of(hdl, struct iommufd_attach_handle, handle) +/* +* An iommufd_fault object represents an interface to deliver I/O page faults +* to the user space. These objects are created/destroyed by the user space and +* associated with hardware page table objects during page-table allocation. +*/ +struct iommufd_fault { +struct iommufd_eventq common; +struct mutex mutex;/* serializes response flows */ +struct xarray response; +}; + +static inline struct iommufd_fault * +eventq_to_fault (struct iommufd_eventq *eventq) +{ + return container_of(eventq, struct iommufd_fault, common); +} +static inline struct iommufd_fault *iommufd_get_fault (struct iommufd_ucmd *ucmd,u32 id) {return container_of (iommufd_get_object (ucmd->ictx, id,IOMMUFD_OBJ_FAULT ), - struct iommufd_fault, obj); + struct iommufd_fault, common.obj); }int iommufd_fault_alloc (struct iommufd_ucmd *ucmd);
drivers/iommu/iommufd/fault.c +60 -51
244 lines not on this page · raw message
drivers/iommu/iommufd/hw_pagetable.c +3 -3
@@ -14,7 +14,7 @@ static void __iommufd_hwpt_destroy(struct iommufd_hw_pagetable *hwpt)iommu_domain_free (hwpt->domain);if (hwpt->fault) -refcount_dec (&hwpt->fault->obj.users); +refcount_dec (&hwpt->fault->common.obj.users); }void iommufd_hwpt_paging_destroy (struct iommufd_object *obj)
@@ -409,8 +409,8 @@ int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd) } hwpt->fault = fault; hwpt->domain->iopf_handler = iommufd_fault_iopf_handler; -refcount_inc (&fault->obj.users); -iommufd_put_object (ucmd->ictx, &fault->obj); +refcount_inc (&fault->common.obj.users); +iommufd_put_object (ucmd->ictx, &fault->common.obj); } hwpt->domain->iommufd_hwpt = hwpt;
Signature · 1 line
2.43.0
PATCH v7 4/14 iommufd: Rename fault.c to eventq.c
In reply to Nicolin ChenRename the file, aligning with the new eventq object.
---
drivers/iommu/iommufd/Makefile | 2 +-
drivers/iommu/iommufd/{fault.c => eventq.c} | 0
2 files changed, 1 insertion(+), 1 deletion(-)
rename drivers/iommu/iommufd/{fault.c => eventq.c} (100%)drivers/iommu/iommufd/Makefile +1 -1
@@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only iommufd-y := \ device.o \ - fault.o \ + eventq.o \ hw_pagetable.o \ io_pagetable.o \ ioas.o \
drivers/iommu/iommufd/eventq.c renamed from drivers/iommu/iommufd/fault.c +0 -0
Signature · 1 line
2.43.0
PATCH v7 5/14 iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC
In reply to Nicolin ChenIntroduce a new IOMMUFD_OBJ_VEVENTQ object for vIOMMU Event Queue that provides user space (VMM) another FD to read the vIOMMU Events.
Allow a vIOMMU object to allocate vEVENTQs, with a condition that each vIOMMU can only have one single vEVENTQ per type.
Add iommufd_veventq_alloc() with iommufd_veventq_ops for the new ioctl.
--- drivers/iommu/iommufd/iommufd_private.h | 82 ++++++++++ include/linux/iommufd.h | 3 + include/uapi/linux/iommufd.h | 82 ++++++++++ drivers/iommu/iommufd/eventq.c | 209 +++++++++++++++++++++++- drivers/iommu/iommufd/main.c | 7 + drivers/iommu/iommufd/viommu.c | 2 + 6 files changed, 384 insertions(+), 1 deletion(-)
drivers/iommu/iommufd/iommufd_private.h +82 -0
@@ -507,6 +507,74 @@ void iommufd_fault_iopf_disable(struct iommufd_device *idev);void iommufd_auto_response_faults (struct iommufd_hw_pagetable *hwpt,struct iommufd_attach_handle *handle); +/* An iommufd_vevent represents a vIOMMU event in an iommufd_veventq */ +struct iommufd_vevent { +struct iommufd_vevent_header header; +struct list_head node;/* for iommufd_eventq::deliver */ +ssize_t data_len; + u64 event_data[] __counted_by(data_len); +}; + +#define vevent_for_lost_events_header (vevent) \ + (vevent->header.flags & IOMMU_VEVENTQ_FLAG_LOST_EVENTS) + +/* +* An iommufd_veventq object represents an interface to deliver vIOMMU events to +* the user space. It is created/destroyed by the user space and associated with +* a vIOMMU object during the allocations. +*/ +struct iommufd_veventq { +struct iommufd_eventq common; +struct iommufd_viommu *viommu; +struct list_head node;/* for iommufd_viommu::veventqs */ +struct iommufd_vevent lost_events_header; + +unsigned int type; +unsigned int depth; + +/* Use common.lock for protection */ +u32 num_events; +u32 sequence; +}; + +static inline struct iommufd_veventq * +eventq_to_veventq (struct iommufd_eventq *eventq) +{ + return container_of(eventq, struct iommufd_veventq, common); +} + +static inline struct iommufd_veventq * +iommufd_get_veventq (struct iommufd_ucmd *ucmd,u32 id) +{ +return container_of (iommufd_get_object (ucmd->ictx, id, +IOMMUFD_OBJ_VEVENTQ ), + struct iommufd_veventq, common.obj); +} + +int iommufd_veventq_alloc (struct iommufd_ucmd *ucmd); +void iommufd_veventq_destroy (struct iommufd_object *obj); +void iommufd_veventq_abort (struct iommufd_object *obj); + +static inline void iommufd_vevent_handler (struct iommufd_veventq *veventq, +struct iommufd_vevent *vevent) +{ +struct iommufd_eventq *eventq = &veventq->common; + +lockdep_assert_held (&eventq->lock); + +/* +* Remove the lost_events_header and add the new node at the same time. +* Note the new node can be lost_events_header, for a sequence update. +*/ +if (list_is_last (&veventq->lost_events_header.node, &eventq->deliver)) +list_del (&veventq->lost_events_header.node); +list_add_tail (&vevent->node, &eventq->deliver); + vevent->header.sequence = veventq->sequence; + veventq->sequence = (veventq->sequence +1 ) &INT_MAX ; + +wake_up_interruptible (&eventq->wait_queue); +} +static inline struct iommufd_viommu *iommufd_get_viommu (struct iommufd_ucmd *ucmd,u32 id) {
@@ -515,6 +583,20 @@ iommufd_get_viommu(struct iommufd_ucmd *ucmd, u32 id) struct iommufd_viommu, obj); } +static inline struct iommufd_veventq * +iommufd_viommu_find_veventq (struct iommufd_viommu *viommu,u32 type) +{ +struct iommufd_veventq *veventq, *next; + +lockdep_assert_held (&viommu->veventqs_rwsem); + +list_for_each_entry_safe (veventq, next, &viommu->veventqs, node) { +if (veventq->type == type) +return veventq; + } +return NULL ; +} +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);
include/linux/iommufd.h +3 -0
@@ -34,6 +34,7 @@ enum iommufd_object_type {IOMMUFD_OBJ_FAULT ,IOMMUFD_OBJ_VIOMMU ,IOMMUFD_OBJ_VDEVICE , +IOMMUFD_OBJ_VEVENTQ , #ifdef CONFIG_IOMMUFD_TEST IOMMUFD_OBJ_SELFTEST, #endif
@@ -93,6 +94,8 @@ struct iommufd_viommu {const struct iommufd_viommu_ops *ops;struct xarray vdevs; +struct list_head veventqs; +struct rw_semaphore veventqs_rwsem;unsigned int type; };
include/uapi/linux/iommufd.h +82 -0
92 lines not on this page · raw message
drivers/iommu/iommufd/eventq.c +208 -1
224 lines not on this page · raw message
drivers/iommu/iommufd/main.c +7 -0
@@ -317,6 +317,7 @@ union ucmd_buffer {struct iommu_ioas_unmap unmap;struct iommu_option option;struct iommu_vdevice_alloc vdev; +struct iommu_veventq_alloc veventq;struct iommu_vfio_ioas vfio_ioas;struct iommu_viommu_alloc viommu;#ifdef CONFIG_IOMMUFD_TEST
@@ -372,6 +373,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = { IOCTL_OP(IOMMU_OPTION, iommufd_option, struct iommu_option, val64),IOCTL_OP (IOMMU_VDEVICE_ALLOC , iommufd_vdevice_alloc_ioctl, struct iommu_vdevice_alloc, virt_id), +IOCTL_OP (IOMMU_VEVENTQ_ALLOC , iommufd_veventq_alloc, + struct iommu_veventq_alloc, out_veventq_fd), IOCTL_OP(IOMMU_VFIO_IOAS, iommufd_vfio_ioas, struct iommu_vfio_ioas, __reserved), IOCTL_OP(IOMMU_VIOMMU_ALLOC, iommufd_viommu_alloc_ioctl,
@@ -514,6 +517,10 @@ static const struct iommufd_object_ops iommufd_object_ops[] = { [IOMMUFD_OBJ_VDEVICE] = { .destroy = iommufd_vdevice_destroy, }, + [IOMMUFD_OBJ_VEVENTQ] = { + .destroy = iommufd_veventq_destroy, + .abort = iommufd_veventq_abort, + }, [IOMMUFD_OBJ_VIOMMU] = { .destroy = iommufd_viommu_destroy, },
drivers/iommu/iommufd/viommu.c +2 -0
@@ -59,6 +59,8 @@ int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) viommu->ictx = ucmd->ictx; viommu->hwpt = hwpt_paging;refcount_inc (&viommu->hwpt->common.obj.users); +INIT_LIST_HEAD (&viommu->veventqs); +init_rwsem (&viommu->veventqs_rwsem);/* * It is the most likely case that a physical IOMMU is unpluggable. A * pluggable IOMMU instance (if exists) is responsible for refcounting
Signature · 1 line
2.43.0
Re: [PATCH v7 05/14] iommufd: Add IOMMUFD_OBJ_VEVENTQ and IOMMUFD_CMD_VEVENTQ_ALLOC
Quoted 11 lines · Nicolin Chen
Introduce a new
IOMMUFD_OBJ_VEVENTQobject for vIOMMU Event Queue that provides user space (VMM) another FD to read the vIOMMU Events.Allow a vIOMMU object to allocate vEVENTQs, with a condition that each vIOMMU can only have one single vEVENTQ per type.
Add
iommufd_veventq_alloc()withiommufd_veventq_opsfor the new ioctl.Reviewed-by Lu Baolu <[email protected]>Reviewed-by Kevin Tian <[email protected]>Signed-off-by Nicolin Chen <[email protected]>
Quoted 8 lines
--- drivers/iommu/iommufd/iommufd_private.h | 82 ++++++++++ include/linux/iommufd.h | 3 + include/uapi/linux/iommufd.h | 82 ++++++++++ drivers/iommu/iommufd/eventq.c | 209 +++++++++++++++++++++++- drivers/iommu/iommufd/main.c | 7 + drivers/iommu/iommufd/viommu.c | 2 + 6 files changed, 384 insertions(+), 1 deletion(-)
Jason
PATCH v7 6/14 iommufd/viommu: Add iommufd_viommu_get_vdev_id helper
In reply to Nicolin ChenThis is a reverse search v.s. iommufd_viommu_find_dev, as drivers may want to convert a struct device pointer (physical) to its virtual device ID for an event injection to the user space VM.
Again, this avoids exposing more core structures to the drivers, than the iommufd_viommu alone.
--- include/linux/iommufd.h | 9 +++++++++ drivers/iommu/iommufd/driver.c | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+)
include/linux/iommufd.h +9 -0
@@ -190,6 +190,8 @@ struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx, enum iommufd_object_type type);struct device *iommufd_viommu_find_dev (struct iommufd_viommu *viommu,unsigned long vdev_id); +int iommufd_viommu_get_vdev_id (struct iommufd_viommu *viommu, +struct device *dev,unsigned long *vdev_id);#else /* !CONFIG_IOMMUFD_DRIVER_CORE */ static inline struct iommufd_object * _iommufd_object_alloc(struct iommufd_ctx *ictx, size_t size,
@@ -203,6 +205,13 @@ iommufd_viommu_find_dev(struct iommufd_viommu *viommu, unsigned long vdev_id) {return NULL ; } + +static inline int iommufd_viommu_get_vdev_id (struct iommufd_viommu *viommu, +struct device *dev, +unsigned long *vdev_id) +{ +return -ENOENT ; +}#endif /* CONFIG_IOMMUFD_DRIVER_CORE */ /*
drivers/iommu/iommufd/driver.c +24 -0
@@ -49,5 +49,29 @@ struct device *iommufd_viommu_find_dev(struct iommufd_viommu *viommu, }EXPORT_SYMBOL_NS_GPL (iommufd_viommu_find_dev,"IOMMUFD" ); +/* Return -ENOENT if device is not associated to the vIOMMU */ +int iommufd_viommu_get_vdev_id (struct iommufd_viommu *viommu, +struct device *dev,unsigned long *vdev_id) +{ +struct iommufd_vdevice *vdev; +unsigned long index; +int rc = -ENOENT ; + +if (WARN_ON_ONCE (!vdev_id)) +return -EINVAL ; + +xa_lock (&viommu->vdevs); +xa_for_each (&viommu->vdevs, index, vdev) { +if (vdev->dev == dev) { + *vdev_id = vdev->id; + rc =0 ; +break ; + } + } +xa_unlock (&viommu->vdevs); +return rc; +} +EXPORT_SYMBOL_NS_GPL (iommufd_viommu_get_vdev_id,"IOMMUFD" ); +MODULE_DESCRIPTION ("iommufd code shared with builtin modules" );MODULE_LICENSE ("GPL" );
Signature · 1 line
2.43.0
PATCH v7 7/14 iommufd/viommu: Add iommufd_viommu_report_event helper
In reply to Nicolin ChenSimilar to iommu_report_device_fault, this allows IOMMU drivers to report vIOMMU events from threaded IRQ handlers to user space hypervisors.
--- include/linux/iommufd.h | 11 ++++++++ drivers/iommu/iommufd/driver.c | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+)
include/linux/iommufd.h +11 -0
@@ -11,6 +11,7 @@#include <linux/refcount.h> #include <linux/types.h> #include <linux/xarray.h> +#include <uapi/linux/iommufd.h> struct device ;struct file ;
@@ -192,6 +193,9 @@ struct device *iommufd_viommu_find_dev(struct iommufd_viommu *viommu, unsigned long vdev_id);int iommufd_viommu_get_vdev_id (struct iommufd_viommu *viommu,struct device *dev,unsigned long *vdev_id); +int iommufd_viommu_report_event (struct iommufd_viommu *viommu, +enum iommu_veventq_type type,void *event_data, +size_t data_len);#else /* !CONFIG_IOMMUFD_DRIVER_CORE */ static inline struct iommufd_object * _iommufd_object_alloc(struct iommufd_ctx *ictx, size_t size,
@@ -212,6 +216,13 @@ static inline int iommufd_viommu_get_vdev_id(struct iommufd_viommu *viommu, {return -ENOENT ; } + +static inline int iommufd_viommu_report_event (struct iommufd_viommu *viommu, +enum iommu_veventq_type type, +void *event_data,size_t data_len) +{ +return -EOPNOTSUPP ; +}#endif /* CONFIG_IOMMUFD_DRIVER_CORE */ /*
drivers/iommu/iommufd/driver.c +48 -0
53 lines not on this page · raw message
Signature · 1 line
2.43.0
Re: [PATCH v7 07/14] iommufd/viommu: Add iommufd_viommu_report_event helper
Quoted 10 lines · Nicolin Chen
Similar to
iommu_report_device_fault, this allows IOMMU drivers to report vIOMMU events from threaded IRQ handlers to user space hypervisors.Reviewed-by Lu Baolu <[email protected]>Reviewed-by Kevin Tian <[email protected]>Signed-off-by Nicolin Chen <[email protected]>--- include/linux/iommufd.h | 11 ++++++++ drivers/iommu/iommufd/driver.c | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+)
Jason
PATCH v7 8/14 iommufd/selftest: Require vdev_id when attaching to a nested domain
In reply to Nicolin ChenWhen attaching a device to a vIOMMU-based nested domain, vdev_id must be present. Add a piece of code hard-requesting it, preparing for a vEVENTQ support in the following patch. Then, update the TEST_F.
A HWPT-based nested domain will return a NULL new_viommu, thus no such a vDEVICE requirement.
--- drivers/iommu/iommufd/selftest.c | 24 ++++++++++++++++++++++++ tools/testing/selftests/iommu/iommufd.c | 5 +++++ 2 files changed, 29 insertions(+)
drivers/iommu/iommufd/selftest.c +24 -0
@@ -161,7 +161,10 @@ enum selftest_obj_type {struct mock_dev {struct device dev; +struct mock_viommu *viommu; +struct rw_semaphore viommu_rwsem;unsigned long flags; +unsigned long vdev_id;int id;u32 cache[MOCK_DEV_CACHE_NUM ]; };
@@ -193,10 +196,30 @@ static int mock_domain_nop_attach(struct iommu_domain *domain, struct device *dev) {struct mock_dev *mdev =to_mock_dev (dev); +struct mock_viommu *new_viommu =NULL ; +unsigned long vdev_id =0 ; +int rc;if (domain->dirty_ops && (mdev->flags &MOCK_FLAGS_DEVICE_NO_DIRTY ))return -EINVAL ; +iommu_group_mutex_assert (dev); +if (domain->type ==IOMMU_DOMAIN_NESTED ) { + new_viommu =to_mock_nested (domain)->mock_viommu; +if (new_viommu) { + rc =iommufd_viommu_get_vdev_id (&new_viommu->core, dev, + &vdev_id); +if (rc) +return rc; + } + } +if (new_viommu != mdev->viommu) { +down_write (&mdev->viommu_rwsem); + mdev->viommu = new_viommu; + mdev->vdev_id = vdev_id; +up_write (&mdev->viommu_rwsem); + } +return 0 ; }
@@ -850,6 +873,7 @@ static struct mock_dev *mock_dev_create(unsigned long dev_flags)if (!mdev)return ERR_PTR (-ENOMEM ); +init_rwsem (&mdev->viommu_rwsem);device_initialize (&mdev->dev); mdev->flags = dev_flags; mdev->dev.release = mock_dev_release;
tools/testing/selftests/iommu/iommufd.c +5 -0
@@ -2736,6 +2736,7 @@ TEST_F(iommufd_viommu, viommu_alloc_nested_iopf)uint32_t iopf_hwpt_id;uint32_t fault_id;uint32_t fault_fd; +uint32_t vdev_id;if (self->device_id) {test_ioctl_fault_alloc (&fault_id, &fault_fd);
@@ -2752,6 +2753,10 @@ TEST_F(iommufd_viommu, viommu_alloc_nested_iopf) &iopf_hwpt_id,IOMMU_HWPT_DATA_SELFTEST , &data, sizeof(data)); +/* Must allocate vdevice before attaching to a nested hwpt */ +test_err_mock_domain_replace (ENOENT , self->stdev_id, + iopf_hwpt_id); +test_cmd_vdevice_alloc (viommu_id, dev_id,0x99 , &vdev_id);test_cmd_mock_domain_replace (self->stdev_id, iopf_hwpt_id);EXPECT_ERRNO (EBUSY ,_test_ioctl_destroy (self->fd, iopf_hwpt_id));
Signature · 1 line
2.43.0
PATCH v7 9/14 iommufd/selftest: Add IOMMU_TEST_OP_TRIGGER_VEVENT for vEVENTQ coverage
In reply to Nicolin ChenThe handler will get vDEVICE object from the given mdev and convert it to its per-vIOMMU virtual ID to mimic a real IOMMU driver.
--- drivers/iommu/iommufd/iommufd_test.h | 10 ++++++++++ drivers/iommu/iommufd/selftest.c | 30 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+)
drivers/iommu/iommufd/iommufd_test.h +10 -0
@@ -24,6 +24,7 @@ enum {IOMMU_TEST_OP_MD_CHECK_IOTLB ,IOMMU_TEST_OP_TRIGGER_IOPF ,IOMMU_TEST_OP_DEV_CHECK_CACHE , + IOMMU_TEST_OP_TRIGGER_VEVENT, }; enum {
@@ -145,6 +146,9 @@ struct iommu_test_cmd {__u32 id;__u32 cache; } check_dev_cache; +struct { +__u32 dev_id; + } trigger_vevent; };__u32 last; };
@@ -212,4 +216,10 @@ struct iommu_viommu_invalidate_selftest { __u32 cache_id; }; +#define IOMMU_VEVENTQ_TYPE_SELFTEST 0xbeefbeef + +struct iommu_viommu_event_selftest { +__u32 virt_id; +}; +#endif
drivers/iommu/iommufd/selftest.c +30 -0
@@ -1621,6 +1621,34 @@ static int iommufd_test_trigger_iopf(struct iommufd_ucmd *ucmd,return 0 ; } +static int iommufd_test_trigger_vevent (struct iommufd_ucmd *ucmd, +struct iommu_test_cmd *cmd) +{ +struct iommu_viommu_event_selftest test = {}; +struct iommufd_device *idev; +struct mock_dev *mdev; +int rc = -ENOENT ; + + idev =iommufd_get_device (ucmd, cmd->trigger_vevent.dev_id); +if (IS_ERR (idev)) +return PTR_ERR (idev); + mdev =to_mock_dev (idev->dev); + +down_read (&mdev->viommu_rwsem); +if (!mdev->viommu || !mdev->vdev_id) + goto out_unlock; + + test.virt_id = mdev->vdev_id; + rc =iommufd_viommu_report_event (&mdev->viommu->core, +IOMMU_VEVENTQ_TYPE_SELFTEST , &test, +sizeof (test)); +out_unlock: +up_read (&mdev->viommu_rwsem); +iommufd_put_object (ucmd->ictx, &idev->obj); + +return rc; +} +void iommufd_selftest_destroy (struct iommufd_object *obj) {struct selftest_obj *sobj =to_selftest_obj (obj);
@@ -1702,6 +1730,8 @@ int iommufd_test(struct iommufd_ucmd *ucmd) cmd->dirty.flags);case IOMMU_TEST_OP_TRIGGER_IOPF :return iommufd_test_trigger_iopf (ucmd, cmd); +case IOMMU_TEST_OP_TRIGGER_VEVENT : +return iommufd_test_trigger_vevent (ucmd, cmd);default :return -EOPNOTSUPP ; }
Signature · 1 line
2.43.0
PATCH v7 10/14 iommufd/selftest: Add IOMMU_VEVENTQ_ALLOC test coverage
In reply to Nicolin ChenTrigger vEVENTs by feeding an idev ID and validating the returned output virt_ids whether they equal to the value that was set to the vDEVICE.
--- tools/testing/selftests/iommu/iommufd_utils.h | 115 ++++++++++++++++++ tools/testing/selftests/iommu/iommufd.c | 31 +++++ .../selftests/iommu/iommufd_fail_nth.c | 7 ++ 3 files changed, 153 insertions(+)
tools/testing/selftests/iommu/iommufd_utils.h +115 -0
@@ -9,6 +9,7 @@#include <sys/ioctl.h> #include <stdint.h> #include <assert.h> +#include <poll.h> #include "../kselftest_harness.h" #include "../../../../drivers/iommu/iommufd/iommufd_test.h"
@@ -936,3 +937,117 @@ static int _test_cmd_vdevice_alloc(int fd, __u32 viommu_id, __u32 idev_id,EXPECT_ERRNO (_errno, \_test_cmd_vdevice_alloc (self->fd, viommu_id, idev_id, \ virt_id, vdev_id)) + +static int _test_cmd_veventq_alloc (int fd,__u32 viommu_id,__u32 type, +__u32 *veventq_id,__u32 *veventq_fd) +{ +struct iommu_veventq_alloc cmd = { + .size =sizeof (cmd), + .type = type, + .veventq_depth =2 , + .viommu_id = viommu_id, + }; +int ret; + + ret =ioctl (fd,IOMMU_VEVENTQ_ALLOC , &cmd); +if (ret) +return ret; +if (veventq_id) + *veventq_id = cmd.out_veventq_id; +if (veventq_fd) + *veventq_fd = cmd.out_veventq_fd; +return 0 ; +} + +#define test_cmd_veventq_alloc (viommu_id, type, veventq_id, veventq_fd) \ + ASSERT_EQ(0, _test_cmd_veventq_alloc(self->fd, viommu_id, type, \ + veventq_id, veventq_fd)) +#define test_err_veventq_alloc (_errno, viommu_id, type, veventq_id, \ + veventq_fd) \ + EXPECT_ERRNO(_errno, \ + _test_cmd_veventq_alloc(self->fd, viommu_id, type, \ + veventq_id, veventq_fd)) + +static int _test_cmd_trigger_vevents (int fd,__u32 dev_id,__u32 nvevents) +{ +struct iommu_test_cmd trigger_vevent_cmd = { + .size =sizeof (trigger_vevent_cmd), + .op =IOMMU_TEST_OP_TRIGGER_VEVENT , + .trigger_vevent = { + .dev_id = dev_id, + }, + }; +int ret; + +while (nvevents--) { + ret =ioctl (fd,_IOMMU_TEST_CMD (IOMMU_TEST_OP_TRIGGER_VEVENT ), + &trigger_vevent_cmd); +if (ret <0 ) +return -1 ; + } +return ret; +} + +#define test_cmd_trigger_vevents (dev_id, nvevents) \ + ASSERT_EQ(0, _test_cmd_trigger_vevents(self->fd, dev_id, nvevents)) + +static int _test_cmd_read_vevents (int fd,__u32 event_fd,__u32 nvevents, +__u32 virt_id,int *prev_seq) +{ +struct pollfd pollfd = { .fd = event_fd, .events =POLLIN }; +struct iommu_viommu_event_selftest *event; +struct iommufd_vevent_header *hdr; +ssize_t bytes; +void *data; +int ret, i; + + ret =poll (&pollfd,1 ,1000 ); +if (ret <0 ) +return -1 ; + + data =calloc (nvevents,sizeof (*hdr) +sizeof (*event)); +if (!data) { + errno =ENOMEM ; +return -1 ; + } + + bytes =read (event_fd, data, + nvevents * (sizeof (*hdr) +sizeof (*event))); +if (bytes <=0 ) { + errno =EFAULT ; + ret =-1 ; + goto out_free; + } + +for (i =0 ; i < nvevents; i++) { + hdr = data + i * (sizeof (*hdr) +sizeof (*event)); + +if (hdr->flags &IOMMU_VEVENTQ_FLAG_LOST_EVENTS || + hdr->sequence - *prev_seq >1 ) { + *prev_seq = hdr->sequence; + errno =EOVERFLOW ; + ret =-1 ; + goto out_free; + } + *prev_seq = hdr->sequence; + event = data +sizeof (*hdr); +if (event->virt_id != virt_id) { + errno =EINVAL ; + ret =-1 ; + goto out_free; + } + } + + ret =0 ; +out_free: +free (data); +return ret; +} + +#define test_cmd_read_vevents (event_fd, nvevents, virt_id, prev_seq) \ + ASSERT_EQ(0, _test_cmd_read_vevents(self->fd, event_fd, nvevents, \ + virt_id, prev_seq)) +#define test_err_read_vevents (_errno, event_fd, nvevents, virt_id, prev_seq) \ + EXPECT_ERRNO(_errno, \ + _test_cmd_read_vevents(self->fd, event_fd, nvevents, \ + virt_id, prev_seq))
tools/testing/selftests/iommu/iommufd.c +31 -0
@@ -2774,15 +2774,46 @@ TEST_F(iommufd_viommu, vdevice_alloc)uint32_t viommu_id = self->viommu_id;uint32_t dev_id = self->device_id;uint32_t vdev_id =0 ; +uint32_t veventq_id; +uint32_t veventq_fd; +int prev_seq =-1 ;if (dev_id) { +/* Must allocate vdevice before attaching to a nested hwpt */ +test_err_mock_domain_replace (ENOENT , self->stdev_id, + self->nested_hwpt_id); + +/* Allocate a vEVENTQ with veventq_depth=2 */ +test_cmd_veventq_alloc (viommu_id,IOMMU_VEVENTQ_TYPE_SELFTEST , + &veventq_id, &veventq_fd); +test_err_veventq_alloc (EEXIST , viommu_id, +IOMMU_VEVENTQ_TYPE_SELFTEST ,NULL ,NULL );/* Set vdev_id to 0x99, unset it, and set to 0x88 */ test_cmd_vdevice_alloc (viommu_id, dev_id,0x99 , &vdev_id); +test_cmd_mock_domain_replace (self->stdev_id, + self->nested_hwpt_id); +test_cmd_trigger_vevents (dev_id,1 ); +test_cmd_read_vevents (veventq_fd,1 ,0x99 , &prev_seq);test_err_vdevice_alloc (EEXIST , viommu_id, dev_id,0x99 , &vdev_id); +test_cmd_mock_domain_replace (self->stdev_id, self->ioas_id);test_ioctl_destroy (vdev_id); + +/* Try again with 0x88 */ test_cmd_vdevice_alloc (viommu_id, dev_id,0x88 , &vdev_id); +test_cmd_mock_domain_replace (self->stdev_id, + self->nested_hwpt_id); +/* Trigger an overflow with three events */ +test_cmd_trigger_vevents (dev_id,3 ); +test_err_read_vevents (EOVERFLOW , veventq_fd,3 ,0x88 , + &prev_seq); +/* Overflow must be gone after the previous reads */ +test_cmd_trigger_vevents (dev_id,1 ); +test_cmd_read_vevents (veventq_fd,1 ,0x88 , &prev_seq); +close (veventq_fd); +test_cmd_mock_domain_replace (self->stdev_id, self->ioas_id);test_ioctl_destroy (vdev_id); +test_ioctl_destroy (veventq_id); }else {test_err_vdevice_alloc (ENOENT , viommu_id, dev_id,0x99 ,NULL ); }
tools/testing/selftests/iommu/iommufd_fail_nth.c +7 -0
@@ -620,6 +620,7 @@ TEST_FAIL_NTH(basic_fail_nth, device) };struct iommu_test_hw_info info;uint32_t fault_id, fault_fd; +uint32_t veventq_id, veventq_fd;uint32_t fault_hwpt_id;uint32_t ioas_id;uint32_t ioas_id2;
@@ -692,6 +693,12 @@ TEST_FAIL_NTH(basic_fail_nth, device) IOMMU_HWPT_DATA_SELFTEST, &data, sizeof(data)))return -1 ; +if (_test_cmd_veventq_alloc (self->fd, viommu_id, +IOMMU_VEVENTQ_TYPE_SELFTEST , &veventq_id, + &veventq_fd)) +return -1 ; +close (veventq_fd); +return 0 ; }
Signature · 1 line
2.43.0
PATCH v7 11/14 Documentation: userspace-api: iommufd: Update FAULT and VEVENTQ
In reply to Nicolin ChenWith the introduction of the new objects, update the doc to reflect that.
--- Documentation/userspace-api/iommufd.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
Documentation/userspace-api/iommufd.rst +17 -0
@@ -63,6 +63,13 @@ Following IOMMUFD objects are exposed to userspace: space usually has mappings from guest-level I/O virtual addresses to guest- level physical addresses. +- IOMMUFD_FAULT, representing a software queue for an HWPT reporting IO page + faults using the IOMMU HW's PRI (Page Request Interface). This queue object + provides user space an FD to poll the page fault events and also to respond + to those events. A FAULT object must be created first to get a fault_id that + could be then used to allocate a fault-enabled HWPT via the IOMMU_HWPT_ALLOC + command by setting the IOMMU_HWPT_FAULT_ID_VALID bit in its flags field. + - IOMMUFD_OBJ_VIOMMU, representing a slice of the physical IOMMU instance, passed to or shared with a VM. It may be some HW-accelerated virtualization features and some SW resources used by the VM. For examples:
@@ -109,6 +116,14 @@ Following IOMMUFD objects are exposed to userspace: vIOMMU, which is a separate ioctl call from attaching the same device to an HWPT_PAGING that the vIOMMU holds. +- IOMMUFD_OBJ_VEVENTQ, representing a software queue for a vIOMMU to report its + events such as translation faults occurred to a nested stage-1 (excluding I/O + page faults that should go through IOMMUFD_OBJ_FAULT) and HW-specific events. + This queue object provides user space an FD to poll/read the vIOMMU events. A + vIOMMU object must be created first to get its viommu_id, which could be then + used to allocate a vEVENTQ. Each vIOMMU can support multiple types of vEVENTS, + but is confined to one vEVENTQ per vEVENTQ type. + All user-visible objects are destroyed via the IOMMU_DESTROY uAPI. The diagrams below show relationships between user-visible objects and kernel
@@ -251,8 +266,10 @@ User visible objects are backed by following datastructures: - iommufd_device for IOMMUFD_OBJ_DEVICE. - iommufd_hwpt_paging for IOMMUFD_OBJ_HWPT_PAGING. - iommufd_hwpt_nested for IOMMUFD_OBJ_HWPT_NESTED. +- iommufd_fault for IOMMUFD_OBJ_FAULT. - iommufd_viommu for IOMMUFD_OBJ_VIOMMU. - iommufd_vdevice for IOMMUFD_OBJ_VDEVICE. +- iommufd_veventq for IOMMUFD_OBJ_VEVENTQ. Several terminologies when looking at these datastructures:
Signature · 1 line
2.43.0
PATCH v7 12/14 iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster
In reply to Nicolin ChenUse it to store all vSMMU-related data. The vsid (Virtual Stream ID) will be the first use case. Since the vsid reader will be the eventq handler that already holds a streams_mutex, reuse that to fenche the vmaster too.
Also add a pair of arm_smmu_attach_prepare/commit_vmaster helpers to set or unset the master->vmaster point. Put these helpers inside the existing arm_smmu_attach_prepare/commit().
For identity/blocked ops that don't call arm_smmu_attach_prepare/commit(), add a simpler arm_smmu_master_clear_vmaster helper to unset the vmaster.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 27 ++++++++++ .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 53 +++++++++++++++++++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 15 +++++- 3 files changed, 94 insertions(+), 1 deletion(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +27 -0
@@ -799,6 +799,11 @@ struct arm_smmu_stream {struct rb_node node; }; +struct arm_smmu_vmaster { +struct arm_vsmmu *vsmmu; +unsigned long vsid; +}; +struct arm_smmu_event {u8 stall :1 , ssv : 1,
@@ -824,6 +829,8 @@ struct arm_smmu_master {struct arm_smmu_device *smmu;struct device *dev;struct arm_smmu_stream *streams; +/* Locked by smmu->streams_mutex */ +struct arm_smmu_vmaster *vmaster;/* Locked by the iommu core using the group mutex */ struct arm_smmu_ctx_desc_cfg cd_table;unsigned int num_streams;
@@ -972,6 +979,7 @@ struct arm_smmu_attach_state {bool disable_ats;ioasid_t ssid;/* Resulting state */ +struct arm_smmu_vmaster *vmaster;bool ats_enabled; };
@@ -1055,9 +1063,28 @@ struct iommufd_viommu *arm_vsmmu_alloc(struct device *dev,struct iommu_domain *parent, struct iommufd_ctx *ictx, unsigned int viommu_type); +int arm_smmu_attach_prepare_vmaster (struct arm_smmu_attach_state *state, +struct iommu_domain *domain); +void arm_smmu_attach_commit_vmaster (struct arm_smmu_attach_state *state); +void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master);#else #define arm_smmu_hw_info NULL#define arm_vsmmu_alloc NULL + +static inline int +arm_smmu_attach_prepare_vmaster (struct arm_smmu_attach_state *state, +struct iommu_domain *domain) +{ +return 0 ;/* NOP */ +} + +static inline void +arm_smmu_attach_commit_vmaster (struct arm_smmu_attach_state *state) +{ +} +static inline void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master) +{ +}#endif /* CONFIG_ARM_SMMU_V3_IOMMUFD */ #endif /* _ARM_SMMU_V3_H */
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +53 -0
59 lines not on this page · raw message
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +14 -1
@@ -2803,6 +2803,7 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,struct arm_smmu_domain *smmu_domain =to_smmu_domain_devices (new_domain);unsigned long flags; +int ret;/* * arm_smmu_share_asid() must not see two domains pointing to the same
@@ -2832,9 +2833,15 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state, }if (smmu_domain) { + ret =arm_smmu_attach_prepare_vmaster (state, new_domain); +if (ret) +return ret; + master_domain =kzalloc (sizeof (*master_domain),GFP_KERNEL ); -if (!master_domain) +if (!master_domain) { +kfree (state->vmaster);return -ENOMEM ; + } master_domain->master = master; master_domain->ssid = state->ssid;if (new_domain->type ==IOMMU_DOMAIN_NESTED )
@@ -2861,6 +2868,7 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,spin_unlock_irqrestore (&smmu_domain->devices_lock, flags);kfree (master_domain); +kfree (state->vmaster);return -EINVAL ; }
@@ -2893,6 +2901,8 @@ void arm_smmu_attach_commit(struct arm_smmu_attach_state *state)lockdep_assert_held (&arm_smmu_asid_lock); +arm_smmu_attach_commit_vmaster (state); +if (state->ats_enabled && !master->ats_enabled) {arm_smmu_enable_ats (master); }else if (state->ats_enabled && master->ats_enabled) {
@@ -3162,6 +3172,7 @@ static int arm_smmu_attach_dev_identity(struct iommu_domain *domain,struct arm_smmu_ste ste;struct arm_smmu_master *master =dev_iommu_priv_get (dev); +arm_smmu_master_clear_vmaster (master);arm_smmu_make_bypass_ste (master->smmu, &ste);arm_smmu_attach_dev_ste (domain, dev, &ste,STRTAB_STE_1_S1DSS_BYPASS );return 0 ;
@@ -3180,7 +3191,9 @@ static int arm_smmu_attach_dev_blocked(struct iommu_domain *domain, struct device *dev) {struct arm_smmu_ste ste; +struct arm_smmu_master *master =dev_iommu_priv_get (dev); +arm_smmu_master_clear_vmaster (master);arm_smmu_make_abort_ste (&ste);arm_smmu_attach_dev_ste (domain, dev, &ste,STRTAB_STE_1_S1DSS_TERMINATE );
Signature · 1 line
2.43.0
Re: [PATCH v7 12/14] iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster
Quoted 40 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +32
-0@@ -85,6 +85,59 @@ static void arm_smmu_make_nested_domain_ste( } } +int arm_smmu_attach_prepare_vmaster (struct arm_smmu_attach_state *state, +struct iommu_domain *domain) +{ +struct arm_smmu_nested_domain *nested_domain; +struct arm_smmu_vmaster *vmaster; +unsigned long vsid; +int ret; + +iommu_group_mutex_assert (state->master->dev); + +if (domain->type !=IOMMU_DOMAIN_NESTED ) +return 0 ; + nested_domain =to_smmu_nested_domain (domain); + +/* Skip invalid vSTE */ +if (!(nested_domain->ste[0 ] &cpu_to_le64 (STRTAB_STE_0_V ))) +return 0 ; + + ret =iommufd_viommu_get_vdev_id (&nested_domain->vsmmu->core, + state->master->dev, &vsid); +if (ret) +return ret; + + vmaster =kzalloc (sizeof (*vmaster),GFP_KERNEL ); +if (!vmaster) +return -ENOMEM ; + vmaster->vsmmu = nested_domain->vsmmu; + vmaster->vsid = vsid; + state->vmaster = vmaster; + +return 0 ; +}
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c static void arm_smmu_make_nested_domain_ste( · line 129
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +10
-0+ +void arm_smmu_attach_commit_vmaster (struct arm_smmu_attach_state *state) +{ +struct arm_smmu_master *master = state->master; + +mutex_lock (&master->smmu->streams_mutex); +if (state->vmaster != master->vmaster) { +kfree (master->vmaster); + master->vmaster = state->vmaster; + }
Does this condition suggest that we might end up calling arm_smmu_attach_prepare_vmaster() multiple times before __actually__ commiting to a vmaster?
Quoted 27 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +11
-0+mutex_unlock (&master->smmu->streams_mutex); +} + +void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master) +{ +mutex_lock (&master->smmu->streams_mutex); +kfree (master->vmaster); + master->vmaster =NULL ; +mutex_unlock (&master->smmu->streams_mutex); +} +static int arm_smmu_attach_dev_nested (struct iommu_domain *domain,struct device *dev) {drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1
-0@@ -2803,6 +2803,7 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,struct arm_smmu_domain *smmu_domain =to_smmu_domain_devices (new_domain);unsigned long flags; +int ret;/* * arm_smmu_share_asid() must not see two domains pointing to the same
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state, · line 2836
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1
-0@@ -2832,9 +2833,15 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state, }if (smmu_domain) { + ret =arm_smmu_attach_prepare_vmaster (state, new_domain);
IMO, this adds a little confusion for folks not using iommufd.
I guess it'd be cleaner if we invoke this below within the:if (new_domain->type == IOMMU_DOMAIN_NESTED) condition instead of simply returning from the function if the new_domain->type isn't NESTED.
Quoted 39 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +10
-1+if (ret) +return ret; + master_domain =kzalloc (sizeof (*master_domain),GFP_KERNEL ); -if (!master_domain) +if (!master_domain) { +kfree (state->vmaster);return -ENOMEM ; + } master_domain->master = master; master_domain->ssid = state->ssid;if (new_domain->type ==IOMMU_DOMAIN_NESTED )@@ -2861,6 +2868,7 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,spin_unlock_irqrestore (&smmu_domain->devices_lock, flags);kfree (master_domain); +kfree (state->vmaster);return -EINVAL ; }@@ -2893,6 +2901,8 @@ void arm_smmu_attach_commit(struct arm_smmu_attach_state *state)lockdep_assert_held (&arm_smmu_asid_lock); +arm_smmu_attach_commit_vmaster (state); +if (state->ats_enabled && !master->ats_enabled) {arm_smmu_enable_ats (master); }else if (state->ats_enabled && master->ats_enabled) {@@ -3162,6 +3172,7 @@ static int arm_smmu_attach_dev_identity(struct iommu_domain *domain,struct arm_smmu_ste ste;struct arm_smmu_master *master =dev_iommu_priv_get (dev); +arm_smmu_master_clear_vmaster (master);arm_smmu_make_bypass_ste (master->smmu, &ste);arm_smmu_attach_dev_ste (domain, dev, &ste,STRTAB_STE_1_S1DSS_BYPASS );return 0 ;
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_attach_dev_blocked(struct iommu_domain *domain, · line 3199
Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2
-0@@ -3180,7 +3191,9 @@ static int arm_smmu_attach_dev_blocked(struct iommu_domain *domain, struct device *dev) {struct arm_smmu_ste ste; +struct arm_smmu_master *master =dev_iommu_priv_get (dev); +arm_smmu_master_clear_vmaster (master);arm_smmu_make_abort_ste (&ste);arm_smmu_attach_dev_ste (domain, dev, &ste,STRTAB_STE_1_S1DSS_TERMINATE );
Thanks,
Praan
Re: [PATCH v7 12/14] iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster
Quoted 47 lines · Pranjal Shrivastava
Quoted 46 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +38
-0@@ -85,6 +85,59 @@ static void arm_smmu_make_nested_domain_ste( } } +int arm_smmu_attach_prepare_vmaster (struct arm_smmu_attach_state *state, +struct iommu_domain *domain) +{ +struct arm_smmu_nested_domain *nested_domain; +struct arm_smmu_vmaster *vmaster; +unsigned long vsid; +int ret; + +iommu_group_mutex_assert (state->master->dev); + +if (domain->type !=IOMMU_DOMAIN_NESTED ) +return 0 ; + nested_domain =to_smmu_nested_domain (domain); + +/* Skip invalid vSTE */ +if (!(nested_domain->ste[0 ] &cpu_to_le64 (STRTAB_STE_0_V ))) +return 0 ; + + ret =iommufd_viommu_get_vdev_id (&nested_domain->vsmmu->core, + state->master->dev, &vsid); +if (ret) +return ret; + + vmaster =kzalloc (sizeof (*vmaster),GFP_KERNEL ); +if (!vmaster) +return -ENOMEM ; + vmaster->vsmmu = nested_domain->vsmmu; + vmaster->vsid = vsid; + state->vmaster = vmaster; + +return 0 ; +} + +void arm_smmu_attach_commit_vmaster (struct arm_smmu_attach_state *state) +{ +struct arm_smmu_master *master = state->master; + +mutex_lock (&master->smmu->streams_mutex);
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c static void arm_smmu_make_nested_domain_ste( · line 129
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +4
-0+if (state->vmaster != master->vmaster) { +kfree (master->vmaster); + master->vmaster = state->vmaster; + }Does this condition suggest that we might end up calling
arm_smmu_attach_prepare_vmaster()multiple times before__actually__commiting to a vmaster?
No. prepare() and commit() are 1:1. How is it interpreted to have "multiple times"?
Quoted 29 lines
Quoted 29 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +11
-0+mutex_unlock (&master->smmu->streams_mutex); +} + +void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master) +{ +mutex_lock (&master->smmu->streams_mutex); +kfree (master->vmaster); + master->vmaster =NULL ; +mutex_unlock (&master->smmu->streams_mutex); +} +static int arm_smmu_attach_dev_nested (struct iommu_domain *domain,struct device *dev) {drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1
-0@@ -2803,6 +2803,7 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,struct arm_smmu_domain *smmu_domain =to_smmu_domain_devices (new_domain);unsigned long flags; +int ret;/* * arm_smmu_share_asid() must not see two domains pointing to the same @@ -2832,9 +2833,15 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state, }
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state, · line 2836
Quoted 2 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1
-0if (smmu_domain) { + ret =arm_smmu_attach_prepare_vmaster (state, new_domain);IMO, this adds a little confusion for folks not using iommufd.
I guess it'd be cleaner if we invoke this below within the:
if (new_domain->type == IOMMU_DOMAIN_NESTED)condition instead of simply returning from the function if thenew_domain->typeisn't NESTED.
But the arm_smmu_attach_commit_vmaster() still has to be unconditional as !NESTED domain should clean the vamster away..
Nicolin
Re: [PATCH v7 12/14] iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster
Quoted 57 lines · Nicolin Chen
Quoted 47 lines · Pranjal Shrivastava
Quoted 46 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +38
-0@@ -85,6 +85,59 @@ static void arm_smmu_make_nested_domain_ste( } } +int arm_smmu_attach_prepare_vmaster (struct arm_smmu_attach_state *state, +struct iommu_domain *domain) +{ +struct arm_smmu_nested_domain *nested_domain; +struct arm_smmu_vmaster *vmaster; +unsigned long vsid; +int ret; + +iommu_group_mutex_assert (state->master->dev); + +if (domain->type !=IOMMU_DOMAIN_NESTED ) +return 0 ; + nested_domain =to_smmu_nested_domain (domain); + +/* Skip invalid vSTE */ +if (!(nested_domain->ste[0 ] &cpu_to_le64 (STRTAB_STE_0_V ))) +return 0 ; + + ret =iommufd_viommu_get_vdev_id (&nested_domain->vsmmu->core, + state->master->dev, &vsid); +if (ret) +return ret; + + vmaster =kzalloc (sizeof (*vmaster),GFP_KERNEL ); +if (!vmaster) +return -ENOMEM ; + vmaster->vsmmu = nested_domain->vsmmu; + vmaster->vsid = vsid; + state->vmaster = vmaster; + +return 0 ; +} + +void arm_smmu_attach_commit_vmaster (struct arm_smmu_attach_state *state) +{ +struct arm_smmu_master *master = state->master; + +mutex_lock (&master->smmu->streams_mutex);Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c static void arm_smmu_make_nested_domain_ste( · line 129
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +4
-0+if (state->vmaster != master->vmaster) { +kfree (master->vmaster); + master->vmaster = state->vmaster; + }Does this condition suggest that we might end up calling
arm_smmu_attach_prepare_vmaster()multiple times before__actually__commiting to a vmaster?
Quoted 2 lines
No.
prepare()andcommit()are 1:1. How is it interpreted to have "multiple times"?
Ohh alright. I was just confused about why do we need to check:if (state->vmaster != master->vmaster) ?
Quoted 35 lines
Quoted 35 lines
Quoted 28 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +11
-0+mutex_unlock (&master->smmu->streams_mutex); +} + +void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master) +{ +mutex_lock (&master->smmu->streams_mutex); +kfree (master->vmaster); + master->vmaster =NULL ; +mutex_unlock (&master->smmu->streams_mutex); +} +static int arm_smmu_attach_dev_nested (struct iommu_domain *domain,struct device *dev) {drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1
-0@@ -2803,6 +2803,7 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,struct arm_smmu_domain *smmu_domain =to_smmu_domain_devices (new_domain);unsigned long flags; +int ret;/* * arm_smmu_share_asid() must not see two domains pointing to the same drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state, · line 2836
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1
-0@@ -2832,9 +2833,15 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state, }if (smmu_domain) { + ret =arm_smmu_attach_prepare_vmaster (state, new_domain);IMO, this adds a little confusion for folks not using iommufd.
Quoted 7 lines
Quoted 3 lines
I guess it'd be cleaner if we invoke this below within the:
if (new_domain->type == IOMMU_DOMAIN_NESTED)condition instead of simply returning from the function if thenew_domain->typeisn't NESTED.But the
arm_smmu_attach_commit_vmaster()still has to be unconditional as !NESTED domain should clean the vamster away..
Ack. Right.
Thanks,
Praan
Re: [PATCH v7 12/14] iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster
Quoted 58 lines · Pranjal Shrivastava
Quoted 57 lines · Nicolin Chen
Quoted 55 lines · Pranjal Shrivastava
Quoted 40 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +32
-0@@ -85,6 +85,59 @@ static void arm_smmu_make_nested_domain_ste( } } +int arm_smmu_attach_prepare_vmaster (struct arm_smmu_attach_state *state, +struct iommu_domain *domain) +{ +struct arm_smmu_nested_domain *nested_domain; +struct arm_smmu_vmaster *vmaster; +unsigned long vsid; +int ret; + +iommu_group_mutex_assert (state->master->dev); + +if (domain->type !=IOMMU_DOMAIN_NESTED ) +return 0 ; + nested_domain =to_smmu_nested_domain (domain); + +/* Skip invalid vSTE */ +if (!(nested_domain->ste[0 ] &cpu_to_le64 (STRTAB_STE_0_V ))) +return 0 ; + + ret =iommufd_viommu_get_vdev_id (&nested_domain->vsmmu->core, + state->master->dev, &vsid); +if (ret) +return ret; + + vmaster =kzalloc (sizeof (*vmaster),GFP_KERNEL ); +if (!vmaster) +return -ENOMEM ; + vmaster->vsmmu = nested_domain->vsmmu; + vmaster->vsid = vsid; + state->vmaster = vmaster; + +return 0 ; +}drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c static void arm_smmu_make_nested_domain_ste( · line 129
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +10
-0+ +void arm_smmu_attach_commit_vmaster (struct arm_smmu_attach_state *state) +{ +struct arm_smmu_master *master = state->master; + +mutex_lock (&master->smmu->streams_mutex); +if (state->vmaster != master->vmaster) { +kfree (master->vmaster); + master->vmaster = state->vmaster; + }Does this condition suggest that we might end up calling
arm_smmu_attach_prepare_vmaster()multiple times before__actually__commiting to a vmaster?
Quoted 5 lines
Quoted 2 lines
No.
prepare()andcommit()are 1:1. How is it interpreted to have "multiple times"?Ohh alright. I was just confused about why do we need to check:
if (state->vmaster != master->vmaster)?
Hmm, it's probably not necessary, since we always allocate a new vmaster pointer to the "state" or set a NULL.
I will clean that up a bit.
Thanks
Nicolin
Re: [PATCH v7 12/14] iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster
Quoted 62 lines · Nicolin Chen
Quoted 61 lines · Pranjal Shrivastava
Quoted 59 lines · Nicolin Chen
Quoted 47 lines · Pranjal Shrivastava
Quoted 46 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +38
-0@@ -85,6 +85,59 @@ static void arm_smmu_make_nested_domain_ste( } } +int arm_smmu_attach_prepare_vmaster (struct arm_smmu_attach_state *state, +struct iommu_domain *domain) +{ +struct arm_smmu_nested_domain *nested_domain; +struct arm_smmu_vmaster *vmaster; +unsigned long vsid; +int ret; + +iommu_group_mutex_assert (state->master->dev); + +if (domain->type !=IOMMU_DOMAIN_NESTED ) +return 0 ; + nested_domain =to_smmu_nested_domain (domain); + +/* Skip invalid vSTE */ +if (!(nested_domain->ste[0 ] &cpu_to_le64 (STRTAB_STE_0_V ))) +return 0 ; + + ret =iommufd_viommu_get_vdev_id (&nested_domain->vsmmu->core, + state->master->dev, &vsid); +if (ret) +return ret; + + vmaster =kzalloc (sizeof (*vmaster),GFP_KERNEL ); +if (!vmaster) +return -ENOMEM ; + vmaster->vsmmu = nested_domain->vsmmu; + vmaster->vsid = vsid; + state->vmaster = vmaster; + +return 0 ; +} + +void arm_smmu_attach_commit_vmaster (struct arm_smmu_attach_state *state) +{ +struct arm_smmu_master *master = state->master; + +mutex_lock (&master->smmu->streams_mutex);Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c static void arm_smmu_make_nested_domain_ste( · line 129
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +4
-0+if (state->vmaster != master->vmaster) { +kfree (master->vmaster); + master->vmaster = state->vmaster; + }Does this condition suggest that we might end up calling
arm_smmu_attach_prepare_vmaster()multiple times before__actually__commiting to a vmaster?No.
prepare()andcommit()are 1:1. How is it interpreted to have "multiple times"?
Quoted 7 lines
Quoted 2 lines
Ohh alright. I was just confused about why do we need to check:
if (state->vmaster != master->vmaster)?Hmm, it's probably not necessary, since we always allocate a new vmaster pointer to the "state" or set a NULL.
I will clean that up a bit.
I made the following change on top of this patch (will squash):
-------------------------------------------------------------
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +1 -13
@@ -95,8 +95,6 @@ int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,iommu_group_mutex_assert (state->master->dev); -if (domain->type !=IOMMU_DOMAIN_NESTED ) -return 0 ; nested_domain =to_smmu_nested_domain (domain);/* Skip invalid vSTE */
@@ -122,19 +120,9 @@ void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state) {struct arm_smmu_master *master = state->master; -mutex_lock (&master->smmu->streams_mutex); -if (state->vmaster != master->vmaster) { -kfree (master->vmaster); - master->vmaster = state->vmaster; - } -mutex_unlock (&master->smmu->streams_mutex); -} - -void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master) -{mutex_lock (&master->smmu->streams_mutex);kfree (master->vmaster); - master->vmaster =NULL ; + master->vmaster = state->vmaster;mutex_unlock (&master->smmu->streams_mutex); }
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +9 -5
@@ -2833,9 +2833,11 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state, }if (smmu_domain) { - ret =arm_smmu_attach_prepare_vmaster (state, new_domain); -if (ret) -return ret; +if (new_domain->type ==IOMMU_DOMAIN_NESTED ) { + ret =arm_smmu_attach_prepare_vmaster (state, new_domain); +if (ret) +return ret; + } master_domain =kzalloc (sizeof (*master_domain),GFP_KERNEL );if (!master_domain) {
@@ -3171,8 +3173,9 @@ static int arm_smmu_attach_dev_identity(struct iommu_domain *domain, {struct arm_smmu_ste ste;struct arm_smmu_master *master =dev_iommu_priv_get (dev); +struct arm_smmu_attach_state state = { .master = master }; -arm_smmu_master_clear_vmaster (master); +arm_smmu_attach_commit_vmaster (&state);arm_smmu_make_bypass_ste (master->smmu, &ste);arm_smmu_attach_dev_ste (domain, dev, &ste,STRTAB_STE_1_S1DSS_BYPASS );return 0 ;
@@ -3192,8 +3195,9 @@ static int arm_smmu_attach_dev_blocked(struct iommu_domain *domain, {struct arm_smmu_ste ste;struct arm_smmu_master *master =dev_iommu_priv_get (dev); +struct arm_smmu_attach_state state = { .master = master }; -arm_smmu_master_clear_vmaster (master); +arm_smmu_attach_commit_vmaster (&state);arm_smmu_make_abort_ste (&ste);arm_smmu_attach_dev_ste (domain, dev, &ste,STRTAB_STE_1_S1DSS_TERMINATE );
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +0 -4
@@ -1066,7 +1066,6 @@ struct iommufd_viommu *arm_vsmmu_alloc(struct device *dev,int arm_smmu_attach_prepare_vmaster (struct arm_smmu_attach_state *state,struct iommu_domain *domain);void arm_smmu_attach_commit_vmaster (struct arm_smmu_attach_state *state); -void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master);#else #define arm_smmu_hw_info NULL#define arm_vsmmu_alloc NULL
@@ -1082,9 +1081,6 @@ static inline void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state) { } -static inline void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master) -{ -}#endif /* CONFIG_ARM_SMMU_V3_IOMMUFD */ #endif /* _ARM_SMMU_V3_H */
-------------------------------------------------------------
Thanks
Nicolin
Re: [PATCH v7 12/14] iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster
Quoted 21 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +0
-8@@ -95,8 +95,6 @@ int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,iommu_group_mutex_assert (state->master->dev); -if (domain->type !=IOMMU_DOMAIN_NESTED ) -return 0 ; nested_domain =to_smmu_nested_domain (domain);/* Skip invalid vSTE */ @@ -122,19 +120,9 @@ void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state) {struct arm_smmu_master *master = state->master; -mutex_lock (&master->smmu->streams_mutex); -if (state->vmaster != master->vmaster) { -kfree (master->vmaster); - master->vmaster = state->vmaster; - } -mutex_unlock (&master->smmu->streams_mutex);
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state) · line 127
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +1
-5-} - -void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master) -{mutex_lock (&master->smmu->streams_mutex);kfree (master->vmaster); - master->vmaster =NULL ; + master->vmaster = state->vmaster;mutex_unlock (&master->smmu->streams_mutex); }
I'd leave the clear_vmaster just for clarity. Commit should not be unpaired with prepare in the other functions.
It looks fine with this on top too
Jason
Re: [PATCH v7 12/14] iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster
Quoted 37 lines · Jason Gunthorpe
Quoted 21 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +0
-8@@ -95,8 +95,6 @@ int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,iommu_group_mutex_assert (state->master->dev); -if (domain->type !=IOMMU_DOMAIN_NESTED ) -return 0 ; nested_domain =to_smmu_nested_domain (domain);/* Skip invalid vSTE */ @@ -122,19 +120,9 @@ void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state) {struct arm_smmu_master *master = state->master; -mutex_lock (&master->smmu->streams_mutex); -if (state->vmaster != master->vmaster) { -kfree (master->vmaster); - master->vmaster = state->vmaster; - } -mutex_unlock (&master->smmu->streams_mutex);drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state) · line 127
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +1
-5-} - -void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master) -{mutex_lock (&master->smmu->streams_mutex);kfree (master->vmaster); - master->vmaster =NULL ; + master->vmaster = state->vmaster;mutex_unlock (&master->smmu->streams_mutex); }I'd leave the
clear_vmasterjust for clarity. Commit should not be unpaired with prepare in the other functions.
Quoted 3 lines
It looks fine with this on top too
Reviewed-by Jason Gunthorpe <[email protected]>
Ack. I added it back and a #ifdef to the vmaster:
+void arm_smmu_master_clear_vmaster(struct arm_smmu_master *master) +{
+ struct arm_smmu_attach_state state = { .master = master };
+
+ arm_smmu_attach_commit_vmaster(&state);
+}
[...]
@@ -824,6 +829,9 @@ struct arm_smmu_master {
struct arm_smmu_device *smmu;
struct device *dev;
struct arm_smmu_stream *streams;
+#ifdef CONFIG_ARM_SMMU_V3_IOMMUFD
+ struct arm_smmu_vmaster *vmaster; /* use smmu->streams_mutex */
+#endif
/* Locked by the iommu core using the group mutex */
struct arm_smmu_ctx_desc_cfg cd_table;
unsigned int num_streams;
@@ -972,6 +980,9 @@ struct arm_smmu_attach_state {
bool disable_ats;
ioasid_t ssid;
/* Resulting state */
+#ifdef CONFIG_ARM_SMMU_V3_IOMMUFD
+ struct arm_smmu_vmaster *vmaster;
+#endif
bool ats_enabled;};
Thanks
Nicolin
Re: [PATCH v7 12/14] iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster
Quoted 62 lines · Nicolin Chen
Quoted 37 lines · Jason Gunthorpe
Quoted 21 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +0
-8@@ -95,8 +95,6 @@ int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,iommu_group_mutex_assert (state->master->dev); -if (domain->type !=IOMMU_DOMAIN_NESTED ) -return 0 ; nested_domain =to_smmu_nested_domain (domain);/* Skip invalid vSTE */ @@ -122,19 +120,9 @@ void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state) {struct arm_smmu_master *master = state->master; -mutex_lock (&master->smmu->streams_mutex); -if (state->vmaster != master->vmaster) { -kfree (master->vmaster); - master->vmaster = state->vmaster; - } -mutex_unlock (&master->smmu->streams_mutex);drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state) · line 127
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +1
-5-} - -void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master) -{mutex_lock (&master->smmu->streams_mutex);kfree (master->vmaster); - master->vmaster =NULL ; + master->vmaster = state->vmaster;mutex_unlock (&master->smmu->streams_mutex); }I'd leave the
clear_vmasterjust for clarity. Commit should not be unpaired with prepare in the other functions.Quoted 3 lines
It looks fine with this on top too
Reviewed-by Jason Gunthorpe <[email protected]>Ack. I added it back and a #ifdef to the vmaster:
(quoted diff) +9
-0+void arm_smmu_master_clear_vmaster(struct arm_smmu_master *master) +{ + struct arm_smmu_attach_state state = { .master = master }; + + arm_smmu_attach_commit_vmaster(&state); +}@@ -824,6 +829,9 @@ struct arm_smmu_master { struct arm_smmu_device *smmu; struct device *dev; struct arm_smmu_stream *streams; +#ifdef CONFIG_ARM_SMMU_V3_IOMMUFD + struct arm_smmu_vmaster *vmaster; /* use smmu->streams_mutex */ +#endif /* Locked by the iommu core using the group mutex */ struct arm_smmu_ctx_desc_cfg cd_table; unsigned int num_streams;
Review struct arm_smmu_attach_state { · line 988
Quoted 9 lines
(quoted diff) +3
-0@@ -972,6 +980,9 @@ struct arm_smmu_attach_state { bool disable_ats; ioasid_t ssid; /* Resulting state */ +#ifdef CONFIG_ARM_SMMU_V3_IOMMUFD + struct arm_smmu_vmaster *vmaster; +#endif bool ats_enabled; };
Umm.. I'm not too sure how I feel about these #ifdefs _between_ a struct definition. Given that currently, the arm_smmu_v3.h file doesn't have such #ifdef CONFIGs between structs. I'd say, in case CONFIG_ARM_SMMU_V3_IOMMUFD is turned off, we can simply leave the vmaster ptr NULL?
-Praan
Re: [PATCH v7 12/14] iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster
Quoted 18 lines · Pranjal Shrivastava
Quoted 18 lines
(quoted diff) +6
-0@@ -824,6 +829,9 @@ struct arm_smmu_master { struct arm_smmu_device *smmu; struct device *dev; struct arm_smmu_stream *streams; +#ifdef CONFIG_ARM_SMMU_V3_IOMMUFD + struct arm_smmu_vmaster *vmaster; /* use smmu->streams_mutex */ +#endif /* Locked by the iommu core using the group mutex */ struct arm_smmu_ctx_desc_cfg cd_table; unsigned int num_streams;@@ -972,6 +980,9 @@ struct arm_smmu_attach_state { bool disable_ats; ioasid_t ssid; /* Resulting state */ +#ifdef CONFIG_ARM_SMMU_V3_IOMMUFD + struct arm_smmu_vmaster *vmaster; +#endif bool ats_enabled;
Quoted 8 lines
Review struct arm_smmu_attach_state { · line 988
Quoted 2 lines
(quoted diff) +0
-0};Umm.. I'm not too sure how I feel about these #ifdefs _between_ a struct definition. Given that currently, the
arm_smmu_v3.hfile doesn't have such#ifdef CONFIGs between structs. I'd say, in caseCONFIG_ARM_SMMU_V3_IOMMUFDis turned off, we can simply leave the vmaster ptr NULL?
OK. Will drop..
Nicolin
Pranjal Shrivastava
Reviewed-byRe: [PATCH v7 12/14] iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster
In reply to Jason GunthorpeQuoted 28 lines · Jason Gunthorpe
Quoted 26 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +0
-12@@ -95,8 +95,6 @@ int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,iommu_group_mutex_assert (state->master->dev); -if (domain->type !=IOMMU_DOMAIN_NESTED ) -return 0 ; nested_domain =to_smmu_nested_domain (domain);/* Skip invalid vSTE */ @@ -122,19 +120,9 @@ void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state) {struct arm_smmu_master *master = state->master; -mutex_lock (&master->smmu->streams_mutex); -if (state->vmaster != master->vmaster) { -kfree (master->vmaster); - master->vmaster = state->vmaster; - } -mutex_unlock (&master->smmu->streams_mutex); -} - -void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master) -{mutex_lock (&master->smmu->streams_mutex);
Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state) · line 127
Quoted 5 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +1
-1kfree (master->vmaster); - master->vmaster =NULL ; + master->vmaster = state->vmaster;mutex_unlock (&master->smmu->streams_mutex); }I'd leave the
clear_vmasterjust for clarity. Commit should not be unpaired with prepare in the other functions.
Ack. I'd like to pair prepare and commit as well. I'm just confused about the check if (state->vmaster != master->vmaster). Maybe a helpful comment about what are we checking for would make things cleaner.
With this nit:
Thanks,
Praan
PATCH v7 13/14 iommu/arm-smmu-v3: Report events that belong to devices attached to vIOMMU
In reply to Nicolin ChenAside from the IOPF framework, iommufd provides an additional pathway to report hardware events, via the vEVENTQ of vIOMMU infrastructure.
Define an iommu_vevent_arm_smmuv3 uAPI structure, and report stage-1 events in the threaded IRQ handler. Also, add another four event record types that can be forwarded to a VM.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 7 +++ include/uapi/linux/iommufd.h | 23 +++++++ .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 17 ++++++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 60 +++++++++++-------- 4 files changed, 82 insertions(+), 25 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +7 -0
@@ -1067,6 +1067,7 @@ int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state, struct iommu_domain *domain);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);#else #define arm_smmu_hw_info NULL#define arm_vsmmu_alloc NULL
@@ -1085,6 +1086,12 @@ arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state)static inline void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master) { } + +static inline int arm_vmaster_report_event (struct arm_smmu_vmaster *vmaster, +u64 *evt) +{ +return -EOPNOTSUPP ; +}#endif /* CONFIG_ARM_SMMU_V3_IOMMUFD */ #endif /* _ARM_SMMU_V3_H */
include/uapi/linux/iommufd.h +23 -0
@@ -1054,9 +1054,32 @@ 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 */ enum iommu_veventq_type {IOMMU_VEVENTQ_TYPE_DEFAULT =0 , +IOMMU_VEVENTQ_TYPE_ARM_SMMUV3 =1 , +}; + +/** +* struct iommu_vevent_arm_smmuv3 - ARM SMMUv3 Virtual Event +* (IOMMU_VEVENTQ_TYPE_ARM_SMMUV3) +* @evt: 256-bit ARM SMMUv3 Event record, little-endian. +* Reported event records: (Refer to "7.3 Event records" in SMMUv3 HW Spec) +* - 0x04 C_BAD_STE +* - 0x06 F_STREAM_DISABLED +* - 0x08 C_BAD_SUBSTREAMID +* - 0x0a C_BAD_CD +* - 0x10 F_TRANSLATION +* - 0x11 F_ADDR_SIZE +* - 0x12 F_ACCESS +* - 0x13 F_PERMISSION +* +* StreamID field reports a virtual device ID. To receive a virtual event for a +* device, a vDEVICE must be allocated via IOMMU_VDEVICE_ALLOC. +*/ +struct iommu_vevent_arm_smmuv3 { +__aligned_le64 evt[4 ]; };/**
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +17 -0
@@ -445,4 +445,21 @@ struct iommufd_viommu *arm_vsmmu_alloc(struct device *dev,return &vsmmu->core; } +int arm_vmaster_report_event (struct arm_smmu_vmaster *vmaster,u64 *evt) +{ +struct iommu_vevent_arm_smmuv3 vevt; +int i; + +lockdep_assert_held (&vmaster->vsmmu->smmu->streams_mutex); + + vevt.evt[0 ] =cpu_to_le64 ((evt[0 ] & ~EVTQ_0_SID ) | +FIELD_PREP (EVTQ_0_SID , vmaster->vsid)); +for (i =1 ; i <EVTQ_ENT_DWORDS ; i++) + vevt.evt[i] =cpu_to_le64 (evt[i]); + +return iommufd_viommu_report_event (&vmaster->vsmmu->core, +IOMMU_VEVENTQ_TYPE_ARM_SMMUV3 , &vevt, +sizeof (vevt)); +} +MODULE_IMPORT_NS ("IOMMUFD" );
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +35 -25
94 lines not on this page · raw message
Signature · 1 line
2.43.0
Re: [PATCH v7 13/14] iommu/arm-smmu-v3: Report events that belong to devices attached to vIOMMU
Quoted 87 lines · Nicolin Chen
Aside from the IOPF framework, iommufd provides an additional pathway to report hardware events, via the vEVENTQ of vIOMMU infrastructure.
Define an
iommu_vevent_arm_smmuv3uAPI structure, and report stage-1 events in the threaded IRQ handler. Also, add another four event record types that can be forwarded to a VM.Reviewed-by Kevin Tian <[email protected]>Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 7 +++ include/uapi/linux/iommufd.h | 23 +++++++ .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 17 ++++++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 60 +++++++++++-------- 4 files changed, 82 insertions(+), 25 deletions(-)drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +7
-0@@ -1067,6 +1067,7 @@ int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state, struct iommu_domain *domain);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);#else #define arm_smmu_hw_info NULL#define arm_vsmmu_alloc NULL@@ -1085,6 +1086,12 @@ arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state)static inline void arm_smmu_master_clear_vmaster (struct arm_smmu_master *master) { } + +static inline int arm_vmaster_report_event (struct arm_smmu_vmaster *vmaster, +u64 *evt) +{ +return -EOPNOTSUPP ; +}#endif /* CONFIG_ARM_SMMU_V3_IOMMUFD */ #endif /* _ARM_SMMU_V3_H */ include/uapi/linux/iommufd.h +23
-0@@ -1054,9 +1054,32 @@ 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 */ enum iommu_veventq_type {IOMMU_VEVENTQ_TYPE_DEFAULT =0 , +IOMMU_VEVENTQ_TYPE_ARM_SMMUV3 =1 , +}; + +/** +* struct iommu_vevent_arm_smmuv3 - ARM SMMUv3 Virtual Event +* (IOMMU_VEVENTQ_TYPE_ARM_SMMUV3) +* @evt: 256-bit ARM SMMUv3 Event record, little-endian. +* Reported event records: (Refer to "7.3 Event records" in SMMUv3 HW Spec) +* - 0x04 C_BAD_STE +* - 0x06 F_STREAM_DISABLED +* - 0x08 C_BAD_SUBSTREAMID +* - 0x0a C_BAD_CD +* - 0x10 F_TRANSLATION +* - 0x11 F_ADDR_SIZE +* - 0x12 F_ACCESS +* - 0x13 F_PERMISSION +* +* StreamID field reports a virtual device ID. To receive a virtual event for a +* device, a vDEVICE must be allocated via IOMMU_VDEVICE_ALLOC. +*/ +struct iommu_vevent_arm_smmuv3 { +__aligned_le64 evt[4 ]; };/** drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +1
-0@@ -445,4 +445,21 @@ struct iommufd_viommu *arm_vsmmu_alloc(struct device *dev,return &vsmmu->core; } +int arm_vmaster_report_event (struct arm_smmu_vmaster *vmaster,u64 *evt)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c struct iommufd_viommu *arm_vsmmu_alloc(struct device *dev, · line 458
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +10
-0+{ +struct iommu_vevent_arm_smmuv3 vevt; +int i; + +lockdep_assert_held (&vmaster->vsmmu->smmu->streams_mutex); + + vevt.evt[0 ] =cpu_to_le64 ((evt[0 ] & ~EVTQ_0_SID ) | +FIELD_PREP (EVTQ_0_SID , vmaster->vsid)); +for (i =1 ; i <EVTQ_ENT_DWORDS ; i++) + vevt.evt[i] =cpu_to_le64 (evt[i]);
Just thinking out loud here:
I understand the goal here is to "emulate" an IOMMU. But I'm just wondering if we could report struct events instead of the raw event?
For example, can't we have something like arm_smmu_event here with the sid changed to vsid?
Are we taking the raw event since we want to keep the u64 event_data[] field within struct iommufd_vevent generic to all architectures?
Quoted 88 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +6
-0+ +return iommufd_viommu_report_event (&vmaster->vsmmu->core, +IOMMU_VEVENTQ_TYPE_ARM_SMMUV3 , &vevt, +sizeof (vevt)); +} +MODULE_IMPORT_NS ("IOMMUFD" );drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +26
-23@@ -1813,8 +1813,8 @@ static void arm_smmu_decode_event(struct arm_smmu_device *smmu, u64 *raw,mutex_unlock (&smmu->streams_mutex); } -static int arm_smmu_handle_event (struct arm_smmu_device *smmu, -struct arm_smmu_event *event) +static int arm_smmu_handle_event (struct arm_smmu_device *smmu,u64 *evt, +struct arm_smmu_event *event) {int ret =0 ;u32 perm =0 ;@@ -1823,6 +1823,10 @@ static int arm_smmu_handle_event(struct arm_smmu_device *smmu,struct iommu_fault *flt = &fault_evt.fault;switch (event->id) { +case EVT_ID_BAD_STE_CONFIG : +case EVT_ID_STREAM_DISABLED_FAULT : +case EVT_ID_BAD_SUBSTREAMID_CONFIG : +case EVT_ID_BAD_CD_CONFIG :case EVT_ID_TRANSLATION_FAULT :case EVT_ID_ADDR_SIZE_FAULT :case EVT_ID_ACCESS_FAULT :@@ -1832,31 +1836,30 @@ static int arm_smmu_handle_event(struct arm_smmu_device *smmu,return -EOPNOTSUPP ; } -if (!event->stall) -return -EOPNOTSUPP ; - -if (event->read) - perm |=IOMMU_FAULT_PERM_READ ; -else - perm |=IOMMU_FAULT_PERM_WRITE ; +if (event->stall) { +if (event->read) + perm |=IOMMU_FAULT_PERM_READ ; +else + perm |=IOMMU_FAULT_PERM_WRITE ; -if (event->instruction) - perm |=IOMMU_FAULT_PERM_EXEC ; +if (event->instruction) + perm |=IOMMU_FAULT_PERM_EXEC ; -if (event->privileged) - perm |=IOMMU_FAULT_PERM_PRIV ; +if (event->privileged) + perm |=IOMMU_FAULT_PERM_PRIV ; - flt->type =IOMMU_FAULT_PAGE_REQ ; - flt->prm = (struct iommu_fault_page_request ) { - .flags =IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE , - .grpid = event->stag, - .perm = perm, - .addr = event->iova, - }; + flt->type =IOMMU_FAULT_PAGE_REQ ; + flt->prm = (struct iommu_fault_page_request ){ + .flags =IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE , + .grpid = event->stag, + .perm = perm, + .addr = event->iova, + }; -if (event->ssv) { - flt->prm.flags |=IOMMU_FAULT_PAGE_REQUEST_PASID_VALID ; - flt->prm.pasid = event->ssid; +if (event->ssv) { + flt->prm.flags |=IOMMU_FAULT_PAGE_REQUEST_PASID_VALID ; + flt->prm.pasid = event->ssid; + } }mutex_lock (&smmu->streams_mutex);@@ -1866,7 +1869,14 @@ static int arm_smmu_handle_event(struct arm_smmu_device *smmu, goto out_unlock; }
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_handle_event(struct arm_smmu_device *smmu, · line 1879
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +8
-1- ret =iommu_report_device_fault (master->dev, &fault_evt); +if (event->stall) { + ret =iommu_report_device_fault (master->dev, &fault_evt); + }else { +if (master->vmaster && !event->s2) + ret =arm_vmaster_report_event (master->vmaster, evt); +else + ret = -EFAULT ;/* Unhandled events should be pinned */ + }
Nit:
I don't see the arm_smmu_handle_event being called elsewhere, is there a reason to return -EFAULT instead of -EOPNOTSUPP here?
I think the current behavior here is to return -EOPNOTSUPP if (!event->stall). Whereas, what we're doing here is:
if (event->stall) {
...
/* do legacy stuff */
...
}
else {
if (master->vmaster && !event->s2)
arm_vmaster_report_event(vmaster, evt);
else
ret = -EFAULT
}
mutex_unlock(&smmu->streams_mutex);
return ret;Thus, we end up returning -EFAULT instead of -EOPNOTSUPP in case event->stall == false. I agree that we aren't really checking the return value in the evtq_thread handler, but I'm wondering if we should ensure that we end up retaining the same behaviour as we have right now?
Quoted 15 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1
-1out_unlock:mutex_unlock (&smmu->streams_mutex);return ret;@@ -1944,7 +1954,7 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev) do { while (!queue_remove_raw(q, evt)) { arm_smmu_decode_event(smmu, evt, &event); - if (arm_smmu_handle_event(smmu, &event)) + if (arm_smmu_handle_event(smmu, evt, &event)) arm_smmu_dump_event(smmu, evt, &event, &rs); put_device(event.dev);Signature · 1 line
2.43.0
Thanks,
Praan
Re: [PATCH v7 13/14] iommu/arm-smmu-v3: Report events that belong to devices attached to vIOMMU
Quoted 17 lines · Pranjal Shrivastava
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c struct iommufd_viommu *arm_vsmmu_alloc(struct device *dev, · line 458
Quoted 11 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +11
-0+int arm_vmaster_report_event (struct arm_smmu_vmaster *vmaster,u64 *evt) +{ +struct iommu_vevent_arm_smmuv3 vevt; +int i; + +lockdep_assert_held (&vmaster->vsmmu->smmu->streams_mutex); + + vevt.evt[0 ] =cpu_to_le64 ((evt[0 ] & ~EVTQ_0_SID ) | +FIELD_PREP (EVTQ_0_SID , vmaster->vsid)); +for (i =1 ; i <EVTQ_ENT_DWORDS ; i++) + vevt.evt[i] =cpu_to_le64 (evt[i]);Just thinking out loud here:
I understand the goal here is to "emulate" an IOMMU. But I'm just wondering if we could report struct events instead of the raw event?
Quoted 5 lines
For example, can't we have something like
arm_smmu_eventhere with the sid changed to vsid?Are we taking the raw event since we want to keep the
u64 event_data[]field withinstruct iommufd_veventgeneric to all architectures?
The ABIs for vSMMU are defined in the HW languange, e.g. cmd, ste. Thus, here evt in raw too.
Quoted 32 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_handle_event(struct arm_smmu_device *smmu, · line 1879
Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +8
-1- ret =iommu_report_device_fault (master->dev, &fault_evt); +if (event->stall) { + ret =iommu_report_device_fault (master->dev, &fault_evt); + }else { +if (master->vmaster && !event->s2) + ret =arm_vmaster_report_event (master->vmaster, evt); +else + ret = -EFAULT ;/* Unhandled events should be pinned */ + }Nit:
I don't see thearm_smmu_handle_eventbeing called elsewhere, is there a reason to return-EFAULTinstead of-EOPNOTSUPPhere?I think the current behavior here is to return
-EOPNOTSUPPif (!event->stall). Whereas, what we're doing here is:if (event->stall) { ... /* do legacy stuff */ ... } else { if (master->vmaster && !event->s2) arm_vmaster_report_event(vmaster, evt); else ret = -EFAULT } mutex_unlock(&smmu->streams_mutex); return ret;
Quoted 4 lines
Thus, we end up returning
-EFAULTinstead of-EOPNOTSUPPin caseevent->stall== false. I agree that we aren't really checking the return value in theevtq_threadhandler, but I'm wondering if we should ensure that we end up retaining the same behaviour as we have right now?
Oh, it looks like -EOPNOTSUPP should be returned here. Will fix.
Thanks
Nicolin
Jason Gunthorpe
maintainerRe: [PATCH v7 13/14] iommu/arm-smmu-v3: Report events that belong to devices attached to vIOMMU
Quoted 4 lines · Nicolin Chen
Quoted 4 lines
Just thinking out loud here:
I understand the goal here is to "emulate" an IOMMU. But I'm just wondering if we could report struct events instead of the raw event?
Quoted 8 lines
Quoted 5 lines
For example, can't we have something like
arm_smmu_eventhere with the sid changed to vsid?Are we taking the raw event since we want to keep the
u64 event_data[]field withinstruct iommufd_veventgeneric to all architectures?The ABIs for vSMMU are defined in the HW languange, e.g. cmd, ste. Thus, here evt in raw too.
Right, the point is that it gives as a safe uABI that is effectively being managed by ARM.
If we make our own thing then we have to take the responsiblity to make it safe and extensible. I don't see a justification to do that..
It is the same discussion we had around the vSTE as input, the raw invalidation command and the IDRs. Since we've already done 'follow the SMMU spec' so many times already now we should keep doing it.
Jason
Re: [PATCH v7 13/14] iommu/arm-smmu-v3: Report events that belong to devices attached to vIOMMU
Quoted 18 lines · Jason Gunthorpe
Quoted 4 lines · Nicolin Chen
Quoted 4 lines
Just thinking out loud here:
I understand the goal here is to "emulate" an IOMMU. But I'm just wondering if we could report struct events instead of the raw event?Quoted 8 lines
Quoted 5 lines
For example, can't we have something like
arm_smmu_eventhere with the sid changed to vsid?Are we taking the raw event since we want to keep the
u64 event_data[]field withinstruct iommufd_veventgeneric to all architectures?The ABIs for vSMMU are defined in the HW languange, e.g. cmd, ste. Thus, here evt in raw too.
Right, the point is that it gives as a safe uABI that is effectively being managed by ARM.
Quoted 6 lines
If we make our own thing then we have to take the responsiblity to make it safe and extensible. I don't see a justification to do that..
It is the same discussion we had around the vSTE as input, the raw invalidation command and the IDRs. Since we've already done 'follow the SMMU spec' so many times already now we should keep doing it.
Ack. Undertsood. Thanks for the explanation :)
-Praan
Pranjal Shrivastava
Reviewed-byRe: [PATCH v7 13/14] iommu/arm-smmu-v3: Report events that belong to devices attached to vIOMMU
In reply to Nicolin ChenQuoted 18 lines · Nicolin Chen
Quoted 17 lines · Pranjal Shrivastava
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c struct iommufd_viommu *arm_vsmmu_alloc(struct device *dev, · line 458
Quoted 11 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +11
-0+int arm_vmaster_report_event (struct arm_smmu_vmaster *vmaster,u64 *evt) +{ +struct iommu_vevent_arm_smmuv3 vevt; +int i; + +lockdep_assert_held (&vmaster->vsmmu->smmu->streams_mutex); + + vevt.evt[0 ] =cpu_to_le64 ((evt[0 ] & ~EVTQ_0_SID ) | +FIELD_PREP (EVTQ_0_SID , vmaster->vsid)); +for (i =1 ; i <EVTQ_ENT_DWORDS ; i++) + vevt.evt[i] =cpu_to_le64 (evt[i]);Just thinking out loud here:
I understand the goal here is to "emulate" an IOMMU. But I'm just wondering if we could report struct events instead of the raw event?
Quoted 9 lines
Quoted 5 lines
For example, can't we have something like
arm_smmu_eventhere with the sid changed to vsid?Are we taking the raw event since we want to keep the
u64 event_data[]field withinstruct iommufd_veventgeneric to all architectures?The ABIs for vSMMU are defined in the HW languange, e.g. cmd, ste. Thus, here evt in raw too.
Ack. Makes sense.
Quoted 32 lines
Quoted 32 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_handle_event(struct arm_smmu_device *smmu, · line 1879
Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +8
-1- ret =iommu_report_device_fault (master->dev, &fault_evt); +if (event->stall) { + ret =iommu_report_device_fault (master->dev, &fault_evt); + }else { +if (master->vmaster && !event->s2) + ret =arm_vmaster_report_event (master->vmaster, evt); +else + ret = -EFAULT ;/* Unhandled events should be pinned */ + }Nit:
I don't see thearm_smmu_handle_eventbeing called elsewhere, is there a reason to return-EFAULTinstead of-EOPNOTSUPPhere?I think the current behavior here is to return
-EOPNOTSUPPif (!event->stall). Whereas, what we're doing here is:if (event->stall) { ... /* do legacy stuff */ ... } else { if (master->vmaster && !event->s2) arm_vmaster_report_event(vmaster, evt); else ret = -EFAULT } mutex_unlock(&smmu->streams_mutex); return ret;
Quoted 7 lines
Quoted 4 lines
Thus, we end up returning
-EFAULTinstead of-EOPNOTSUPPin caseevent->stall== false. I agree that we aren't really checking the return value in theevtq_threadhandler, but I'm wondering if we should ensure that we end up retaining the same behaviour as we have right now?Oh, it looks like
-EOPNOTSUPPshould be returned here. Will fix.
With the fix to return -EOPNOTSUPP:
Thanks,
Praan
Re: [PATCH v7 13/14] iommu/arm-smmu-v3: Report events that belong to devices attached to vIOMMU
In reply to Nicolin ChenQuoted 7 lines · Nicolin Chen
Aside from the IOPF framework, iommufd provides an additional pathway to report hardware events, via the vEVENTQ of vIOMMU infrastructure.
Define an
iommu_vevent_arm_smmuv3uAPI structure, and report stage-1 events in the threaded IRQ handler. Also, add another four event record types that can be forwarded to a VM.
Quoted 8 lines
Reviewed-by Kevin Tian <[email protected]>Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 7 +++ include/uapi/linux/iommufd.h | 23 +++++++ .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 17 ++++++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 60 +++++++++++-------- 4 files changed, 82 insertions(+), 25 deletions(-)
Jason
PATCH v7 14/14 iommu/arm-smmu-v3: Set MEV bit in nested STE for DoS mitigations
In reply to Nicolin ChenThere is a DoS concern on the shared hardware event queue among devices passed through to VMs, that too many translation failures that belong to VMs could overflow the shared hardware event queue if those VMs or their VMMs don't handle/recover the devices properly.
The MEV bit in the STE allows to configure the SMMU HW to merge similar event records, though there is no guarantee. Set it in a nested STE for DoS mitigations.
In the future, we might want to enable the MEV for non-nested cases too such as domain->type == IOMMU_DOMAIN_UNMANAGED or even IOMMU_DOMAIN_DMA.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 2 ++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +1 -0
@@ -266,6 +266,7 @@ static inline u32 arm_smmu_strtab_l2_idx(u32 sid)#define STRTAB_STE_1_S1COR GENMASK_ULL(5, 4)#define STRTAB_STE_1_S1CSH GENMASK_ULL(7, 6) +#define STRTAB_STE_1_MEV (1UL << 19)#define STRTAB_STE_1_S2FWB (1UL << 25)#define STRTAB_STE_1_S1STALLD (1UL << 27)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +2 -0
@@ -43,6 +43,8 @@ static void arm_smmu_make_nested_cd_table_ste( target->data[0 ] |= nested_domain->ste[0 ] & ~cpu_to_le64 (STRTAB_STE_0_CFG ); target->data[1 ] |= nested_domain->ste[1 ]; +/* Merge events for DoS mitigations on eventq */ + target->data[1 ] |=cpu_to_le64 (STRTAB_STE_1_MEV ); }/*
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2 -2
@@ -1052,7 +1052,7 @@ void arm_smmu_get_ste_used(const __le64 *ent, __le64 *used_bits)cpu_to_le64 (STRTAB_STE_1_S1DSS |STRTAB_STE_1_S1CIR |STRTAB_STE_1_S1COR |STRTAB_STE_1_S1CSH |STRTAB_STE_1_S1STALLD |STRTAB_STE_1_STRW | -STRTAB_STE_1_EATS ); +STRTAB_STE_1_EATS |STRTAB_STE_1_MEV ); used_bits[2 ] |=cpu_to_le64 (STRTAB_STE_2_S2VMID );/*
@@ -1068,7 +1068,7 @@ void arm_smmu_get_ste_used(const __le64 *ent, __le64 *used_bits)if (cfg &BIT (1 )) { used_bits[1 ] |=cpu_to_le64 (STRTAB_STE_1_S2FWB |STRTAB_STE_1_EATS | -STRTAB_STE_1_SHCFG ); +STRTAB_STE_1_SHCFG |STRTAB_STE_1_MEV ); used_bits[2 ] |= cpu_to_le64(STRTAB_STE_2_S2VMID | STRTAB_STE_2_VTCR | STRTAB_STE_2_S2AA64 | STRTAB_STE_2_S2ENDI |
Signature · 1 line
2.43.0
Re: [PATCH v7 14/14] iommu/arm-smmu-v3: Set MEV bit in nested STE for DoS mitigations
Quoted 12 lines · Nicolin Chen
There is a DoS concern on the shared hardware event queue among devices passed through to VMs, that too many translation failures that belong to VMs could overflow the shared hardware event queue if those VMs or their VMMs don't handle/recover the devices properly.
The MEV bit in the STE allows to configure the SMMU HW to merge similar event records, though there is no guarantee. Set it in a nested STE for DoS mitigations.
In the future, we might want to enable the MEV for non-nested cases too such as
domain->type==IOMMU_DOMAIN_UNMANAGEDor evenIOMMU_DOMAIN_DMA.
Quoted 8 lines
Reviewed-by Jason Gunthorpe <[email protected]>Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 2 ++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-)
Thanks,
Praan