[PATCH v3 2/2] iommu: Make iopf_group_response() free groups
From: Peiyang He
Date: Mon Jul 20 2026 - 04:53:54 EST
Every normal iopf_group_response() caller frees the group immediately
after sending the response. Make iopf_group_response() do both operations,
so callers no longer have to free the group separately.
Split the response operation into a helper that requires fault_param->lock,
a helper that acquires the lock without freeing, and the exported helper
that sends the response and frees the group.
iommu_report_device_fault() retains its special stack-allocated abort_group
handling. It uses the non-freeing helper, then keeps the existing choice
between __iopf_free_group() and iopf_free_group().
iopf_queue_remove_device() already holds the fault-parameter lock, so it
uses the locked helper before freeing each pending group.
Suggested-by: Kevin Tian <kevin.tian@xxxxxxxxx>
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Peiyang He <peiyang_he@xxxxxxxxxxxxxxxx>
---
drivers/iommu/io-pgfault.c | 69 +++++++++++++++++++---------------
drivers/iommu/iommu-sva.c | 1 -
drivers/iommu/iommufd/eventq.c | 5 ---
include/linux/iommu.h | 5 ---
4 files changed, 38 insertions(+), 42 deletions(-)
diff --git a/drivers/iommu/io-pgfault.c b/drivers/iommu/io-pgfault.c
index c16ff1fc4b95..52366e2e9d76 100644
--- a/drivers/iommu/io-pgfault.c
+++ b/drivers/iommu/io-pgfault.c
@@ -52,12 +52,11 @@ static void __iopf_free_group(struct iopf_group *group)
iopf_put_dev_fault_param(group->fault_param);
}
-void iopf_free_group(struct iopf_group *group)
+static void iopf_free_group(struct iopf_group *group)
{
__iopf_free_group(group);
kfree(group);
}
-EXPORT_SYMBOL_GPL(iopf_free_group);
/* Non-last request of a group. Postpone until the last one. */
static int report_partial_fault(struct iommu_fault_param *fault_param,
@@ -168,6 +167,35 @@ static void iopf_error_response(struct device *dev, struct iopf_fault *evt)
ops->page_response(dev, evt, &resp);
}
+static void __iopf_group_response_locked(struct iopf_group *group,
+ enum iommu_page_response_code status)
+{
+ struct iommu_fault_param *fault_param = group->fault_param;
+ struct iopf_fault *iopf = &group->last_fault;
+ struct device *dev = fault_param->dev;
+ const struct iommu_ops *ops = dev_iommu_ops(dev);
+ struct iommu_page_response resp = {
+ .pasid = iopf->fault.prm.pasid,
+ .grpid = iopf->fault.prm.grpid,
+ .code = status,
+ };
+
+ lockdep_assert_held(&fault_param->lock);
+
+ ops->page_response(dev, &group->last_fault, &resp);
+ list_del_init(&group->pending_node);
+}
+
+static void __iopf_group_response(struct iopf_group *group,
+ enum iommu_page_response_code status)
+{
+ struct iommu_fault_param *fault_param = group->fault_param;
+
+ mutex_lock(&fault_param->lock);
+ __iopf_group_response_locked(group, status);
+ mutex_unlock(&fault_param->lock);
+}
+
/**
* iommu_report_device_fault() - Report fault event to device driver
* @dev: the device
@@ -255,10 +283,7 @@ int iommu_report_device_fault(struct device *dev, struct iopf_fault *evt)
group->attach_handle = attach_handle;
- /*
- * On success iopf_handler must call iopf_group_response() and
- * iopf_free_group()
- */
+ /* On success iopf_handler must call iopf_group_response(). */
if (group->attach_handle->domain->iopf_handler(group))
goto err_abort;
@@ -267,7 +292,7 @@ int iommu_report_device_fault(struct device *dev, struct iopf_fault *evt)
err_abort:
dev_warn_ratelimited(dev, "iopf with pasid %d aborted\n",
fault->prm.pasid);
- iopf_group_response(group, IOMMU_PAGE_RESP_FAILURE);
+ __iopf_group_response(group, IOMMU_PAGE_RESP_FAILURE);
if (group == &abort_group)
__iopf_free_group(group);
else
@@ -318,24 +343,15 @@ EXPORT_SYMBOL_GPL(iopf_queue_flush_dev);
* iopf_group_response - Respond a group of page faults
* @group: the group of faults with the same group id
* @status: the response code
+ *
+ * The group will be freed as well and must not be accessed after
+ * this function returns.
*/
void iopf_group_response(struct iopf_group *group,
enum iommu_page_response_code status)
{
- struct iommu_fault_param *fault_param = group->fault_param;
- struct iopf_fault *iopf = &group->last_fault;
- struct device *dev = group->fault_param->dev;
- const struct iommu_ops *ops = dev_iommu_ops(dev);
- struct iommu_page_response resp = {
- .pasid = iopf->fault.prm.pasid,
- .grpid = iopf->fault.prm.grpid,
- .code = status,
- };
-
- mutex_lock(&fault_param->lock);
- ops->page_response(dev, &group->last_fault, &resp);
- list_del_init(&group->pending_node);
- mutex_unlock(&fault_param->lock);
+ __iopf_group_response(group, status);
+ iopf_free_group(group);
}
EXPORT_SYMBOL_GPL(iopf_group_response);
@@ -468,7 +484,6 @@ void iopf_queue_remove_device(struct iopf_queue *queue, struct device *dev)
struct iopf_group *group, *temp;
struct dev_iommu *param = dev->iommu;
struct iommu_fault_param *fault_param;
- const struct iommu_ops *ops = dev_iommu_ops(dev);
mutex_lock(&queue->lock);
mutex_lock(¶m->lock);
@@ -483,15 +498,7 @@ void iopf_queue_remove_device(struct iopf_queue *queue, struct device *dev)
kfree(partial_iopf);
list_for_each_entry_safe(group, temp, &fault_param->faults, pending_node) {
- struct iopf_fault *iopf = &group->last_fault;
- struct iommu_page_response resp = {
- .pasid = iopf->fault.prm.pasid,
- .grpid = iopf->fault.prm.grpid,
- .code = IOMMU_PAGE_RESP_INVALID
- };
-
- ops->page_response(dev, iopf, &resp);
- list_del_init(&group->pending_node);
+ __iopf_group_response_locked(group, IOMMU_PAGE_RESP_INVALID);
iopf_free_group(group);
}
mutex_unlock(&fault_param->lock);
diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c
index bc7c7232a43e..caccaa315461 100644
--- a/drivers/iommu/iommu-sva.c
+++ b/drivers/iommu/iommu-sva.c
@@ -292,7 +292,6 @@ static void iommu_sva_handle_iopf(struct work_struct *work)
}
iopf_group_response(group, status);
- iopf_free_group(group);
}
static int iommu_sva_iopf_handler(struct iopf_group *group)
diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c
index 747dd5155121..62110fe8f78f 100644
--- a/drivers/iommu/iommufd/eventq.c
+++ b/drivers/iommu/iommufd/eventq.c
@@ -40,7 +40,6 @@ void iommufd_auto_response_faults(struct iommufd_hw_pagetable *hwpt,
list_for_each_entry_safe(group, next, &free_list, node) {
list_del(&group->node);
iopf_group_response(group, IOMMU_PAGE_RESP_INVALID);
- iopf_free_group(group);
}
xa_for_each(&fault->response, index, group) {
@@ -48,7 +47,6 @@ void iommufd_auto_response_faults(struct iommufd_hw_pagetable *hwpt,
continue;
xa_erase(&fault->response, index);
iopf_group_response(group, IOMMU_PAGE_RESP_INVALID);
- iopf_free_group(group);
}
mutex_unlock(&fault->mutex);
}
@@ -70,12 +68,10 @@ void iommufd_fault_destroy(struct iommufd_object *obj)
list_for_each_entry_safe(group, next, &fault->common.deliver, node) {
list_del(&group->node);
iopf_group_response(group, IOMMU_PAGE_RESP_INVALID);
- iopf_free_group(group);
}
xa_for_each(&fault->response, index, group) {
xa_erase(&fault->response, index);
iopf_group_response(group, IOMMU_PAGE_RESP_INVALID);
- iopf_free_group(group);
}
xa_destroy(&fault->response);
mutex_destroy(&fault->mutex);
@@ -217,7 +213,6 @@ static ssize_t iommufd_fault_fops_write(struct file *filep, const char __user *b
}
iopf_group_response(group, response.code);
- iopf_free_group(group);
done += response_size;
}
mutex_unlock(&fault->mutex);
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index ac43b8b93f14..f16c88d92661 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -1700,7 +1700,6 @@ int iopf_queue_flush_dev(struct device *dev);
struct iopf_queue *iopf_queue_alloc(const char *name);
void iopf_queue_free(struct iopf_queue *queue);
int iopf_queue_discard_partial(struct iopf_queue *queue);
-void iopf_free_group(struct iopf_group *group);
int iommu_report_device_fault(struct device *dev, struct iopf_fault *evt);
void iopf_group_response(struct iopf_group *group,
enum iommu_page_response_code status);
@@ -1736,10 +1735,6 @@ static inline int iopf_queue_discard_partial(struct iopf_queue *queue)
return -ENODEV;
}
-static inline void iopf_free_group(struct iopf_group *group)
-{
-}
-
static inline int
iommu_report_device_fault(struct device *dev, struct iopf_fault *evt)
{
--
2.43.0