Skip to content

Commit 800194f

Browse files
committed
iommufd/fault: Use a separate spinlock to protect fault->deliver list
JIRA: https://issues.redhat.com/browse/RHEL-75942 commit 3d49020 Author: Nicolin Chen <nicolinc@nvidia.com> Date: Fri Jan 17 11:29:01 2025 -0800 iommufd/fault: Use a separate spinlock to protect fault->deliver list The fault->mutex serializes the fault read()/write() fops and the iommufd_fault_auto_response_faults(), mainly for fault->response. Also, it was conveniently used to fence the fault->deliver in poll() fop 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 reusing the mutex to protect the fault->deliver list, add a separate spinlock, nested under the mutex, to do the job. iommufd_fault_iopf_handler() would no longer be blocked by copy_from/to_user(). Add a free_list in iommufd_auto_response_faults(), so the spinlock can simply fence a fast list_for_each_entry_safe routine. Provide two deliver list helpers for iommufd_fault_fops_read() to use: - Fetch the first iopf_group out of the fault->deliver list - Restore an iopf_group back to the head of the fault->deliver list Lastly, move the mutex closer to the response in the fault structure, and update its kdoc accordingly. Fixes: 07838f7 ("iommufd: Add iommufd fault object") Link: https://patch.msgid.link/r/20250117192901.79491-1-nicolinc@nvidia.com Cc: stable@vger.kernel.org Suggested-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com> Signed-off-by: Eder Zulian <ezulian@redhat.com>
1 parent ab337d7 commit 800194f

File tree

2 files changed

+49
-14
lines changed

2 files changed

+49
-14
lines changed

drivers/iommu/iommufd/fault.c

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,23 @@ static void iommufd_auto_response_faults(struct iommufd_hw_pagetable *hwpt,
9898
{
9999
struct iommufd_fault *fault = hwpt->fault;
100100
struct iopf_group *group, *next;
101+
struct list_head free_list;
101102
unsigned long index;
102103

103104
if (!fault)
104105
return;
106+
INIT_LIST_HEAD(&free_list);
105107

106108
mutex_lock(&fault->mutex);
109+
spin_lock(&fault->lock);
107110
list_for_each_entry_safe(group, next, &fault->deliver, node) {
108111
if (group->attach_handle != &handle->handle)
109112
continue;
113+
list_move(&group->node, &free_list);
114+
}
115+
spin_unlock(&fault->lock);
116+
117+
list_for_each_entry_safe(group, next, &free_list, node) {
110118
list_del(&group->node);
111119
iopf_group_response(group, IOMMU_PAGE_RESP_INVALID);
112120
iopf_free_group(group);
@@ -261,17 +269,19 @@ static ssize_t iommufd_fault_fops_read(struct file *filep, char __user *buf,
261269
return -ESPIPE;
262270

263271
mutex_lock(&fault->mutex);
264-
while (!list_empty(&fault->deliver) && count > done) {
265-
group = list_first_entry(&fault->deliver,
266-
struct iopf_group, node);
267-
268-
if (group->fault_count * fault_size > count - done)
272+
while ((group = iommufd_fault_deliver_fetch(fault))) {
273+
if (done >= count ||
274+
group->fault_count * fault_size > count - done) {
275+
iommufd_fault_deliver_restore(fault, group);
269276
break;
277+
}
270278

271279
rc = xa_alloc(&fault->response, &group->cookie, group,
272280
xa_limit_32b, GFP_KERNEL);
273-
if (rc)
281+
if (rc) {
282+
iommufd_fault_deliver_restore(fault, group);
274283
break;
284+
}
275285

276286
idev = to_iommufd_handle(group->attach_handle)->idev;
277287
list_for_each_entry(iopf, &group->faults, list) {
@@ -280,13 +290,12 @@ static ssize_t iommufd_fault_fops_read(struct file *filep, char __user *buf,
280290
group->cookie);
281291
if (copy_to_user(buf + done, &data, fault_size)) {
282292
xa_erase(&fault->response, group->cookie);
293+
iommufd_fault_deliver_restore(fault, group);
283294
rc = -EFAULT;
284295
break;
285296
}
286297
done += fault_size;
287298
}
288-
289-
list_del(&group->node);
290299
}
291300
mutex_unlock(&fault->mutex);
292301

@@ -344,10 +353,10 @@ static __poll_t iommufd_fault_fops_poll(struct file *filep,
344353
__poll_t pollflags = EPOLLOUT;
345354

346355
poll_wait(filep, &fault->wait_queue, wait);
347-
mutex_lock(&fault->mutex);
356+
spin_lock(&fault->lock);
348357
if (!list_empty(&fault->deliver))
349358
pollflags |= EPOLLIN | EPOLLRDNORM;
350-
mutex_unlock(&fault->mutex);
359+
spin_unlock(&fault->lock);
351360

352361
return pollflags;
353362
}
@@ -390,6 +399,7 @@ int iommufd_fault_alloc(struct iommufd_ucmd *ucmd)
390399
INIT_LIST_HEAD(&fault->deliver);
391400
xa_init_flags(&fault->response, XA_FLAGS_ALLOC1);
392401
mutex_init(&fault->mutex);
402+
spin_lock_init(&fault->lock);
393403
init_waitqueue_head(&fault->wait_queue);
394404

395405
filep = anon_inode_getfile("[iommufd-pgfault]", &iommufd_fault_fops,
@@ -440,9 +450,9 @@ int iommufd_fault_iopf_handler(struct iopf_group *group)
440450
hwpt = group->attach_handle->domain->fault_data;
441451
fault = hwpt->fault;
442452

443-
mutex_lock(&fault->mutex);
453+
spin_lock(&fault->lock);
444454
list_add_tail(&group->node, &fault->deliver);
445-
mutex_unlock(&fault->mutex);
455+
spin_unlock(&fault->lock);
446456

447457
wake_up_interruptible(&fault->wait_queue);
448458

drivers/iommu/iommufd/iommufd_private.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,14 +462,39 @@ struct iommufd_fault {
462462
struct iommufd_ctx *ictx;
463463
struct file *filep;
464464

465-
/* The lists of outstanding faults protected by below mutex. */
466-
struct mutex mutex;
465+
spinlock_t lock; /* protects the deliver list */
467466
struct list_head deliver;
467+
struct mutex mutex; /* serializes response flows */
468468
struct xarray response;
469469

470470
struct wait_queue_head wait_queue;
471471
};
472472

473+
/* Fetch the first node out of the fault->deliver list */
474+
static inline struct iopf_group *
475+
iommufd_fault_deliver_fetch(struct iommufd_fault *fault)
476+
{
477+
struct list_head *list = &fault->deliver;
478+
struct iopf_group *group = NULL;
479+
480+
spin_lock(&fault->lock);
481+
if (!list_empty(list)) {
482+
group = list_first_entry(list, struct iopf_group, node);
483+
list_del(&group->node);
484+
}
485+
spin_unlock(&fault->lock);
486+
return group;
487+
}
488+
489+
/* Restore a node back to the head of the fault->deliver list */
490+
static inline void iommufd_fault_deliver_restore(struct iommufd_fault *fault,
491+
struct iopf_group *group)
492+
{
493+
spin_lock(&fault->lock);
494+
list_add(&group->node, &fault->deliver);
495+
spin_unlock(&fault->lock);
496+
}
497+
473498
struct iommufd_attach_handle {
474499
struct iommu_attach_handle handle;
475500
struct iommufd_device *idev;

0 commit comments

Comments
 (0)