Re: [PATCH rc 2/2] iommufd/fault: Use a separate spinlock to protect fault->deliver list

From: Baolu Lu
Date: Wed Jan 15 2025 - 00:27:16 EST


On 1/15/25 07:28, Nicolin Chen wrote:
The fault->mutex was to serialize the fault read()/write() fops and the
iommufd_fault_auto_response_faults(). And it was also conveniently used
to protect fault->deliver in poll() and iommufd_fault_iopf_handler().

However, copy_from/to_user() may sleep if pagefaults are enabled. Thus,
they could take a long time to wait for user pages to swap in, blocking
iommufd_fault_iopf_handler() and its caller that is typically a shared
IRQ handler of an IOMMU driver, resulting in a potential global DOS.

Instead of resuing the mutex to protect the fault->deliver list, add a
separate spinlock to do the job, so iommufd_fault_iopf_handler() would
no longer be blocked by copy_from/to_user().

Provide two list manipulation helpers for fault->deliver:
- Extract the first iopf_group out of the fault->deliver list
- Restore an iopf_group back to the head of the fault->deliver list

Replace list_first_entry and list_for_each accordingly.

Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object")
Cc:stable@xxxxxxxxxxxxxxx
Suggested-by: Jason Gunthorpe<jgg@xxxxxxxxxx>
Signed-off-by: Nicolin Chen<nicolinc@xxxxxxxxxx>
---
drivers/iommu/iommufd/iommufd_private.h | 26 +++++++++++++++
drivers/iommu/iommufd/fault.c | 43 ++++++++++++++-----------
2 files changed, 50 insertions(+), 19 deletions(-)

diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index b6d706cf2c66..d3097c857abf 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -445,12 +445,38 @@ struct iommufd_fault {
/* The lists of outstanding faults protected by below mutex. */

It's better to update above comment as well.

struct mutex mutex;
+ spinlock_t lock; /* protects the deliver list */
struct list_head deliver;
struct xarray response;
struct wait_queue_head wait_queue;
};
+/* Extract the first node out of the fault->deliver list */
+static inline struct iopf_group *
+iommufd_fault_deliver_extract(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 in fault->deliver */
+static inline void iommufd_fault_deliver_restore(struct iommufd_fault *fault,
+ struct iopf_group *group)
+{
+ spin_lock(&fault->lock);
+ list_add(&fault->deliver, &group->node);

This is not right. It should be

list_add(&group->node, &fault->deliver);

+ spin_unlock(&fault->lock);
+}

Others look good to me. With above addressed,

Reviewed-by: Lu Baolu <baolu.lu@xxxxxxxxxxxxxxx>