IB/mlx5: Reduce spinlock contention by moving free operations outside

The functions kfree() and kvfree() can occasionally trigger a long
chain of calls or face contention in the slab allocator. Executing
these inside a spinlock increases the risk of CPU stalls and increases
lock contention under heavy event load.

Move the memory freeing logic out of the critical sections in devx.c
by using temporary lists and local flags. This narrows the lock's
scope to only protect the list integrity and state transitions.

Signed-off-by: Li RongQing <lirongqing@baidu.com>
Link: https://patch.msgid.link/20260503023349.1959-1-lirongqing@baidu.com
Signed-off-by: Leon Romanovsky <leon@kernel.org>
This commit is contained in:
Li RongQing 2026-05-02 22:33:49 -04:00 committed by Leon Romanovsky
parent 43f8f79468
commit d38e69adc2

View File

@ -2528,7 +2528,7 @@ static int deliver_event(struct devx_event_subscription *event_sub,
const void *data)
{
struct devx_async_event_file *ev_file;
struct devx_async_event_data *event_data;
struct devx_async_event_data *event_data, *to_free;
unsigned long flags;
ev_file = event_sub->ev_file;
@ -2559,12 +2559,17 @@ static int deliver_event(struct devx_event_subscription *event_sub,
event_data->hdr.cookie = event_sub->cookie;
memcpy(event_data->hdr.out_data, data, sizeof(struct mlx5_eqe));
to_free = NULL;
spin_lock_irqsave(&ev_file->lock, flags);
if (!ev_file->is_destroyed)
list_add_tail(&event_data->list, &ev_file->event_list);
else
kfree(event_data);
to_free = event_data;
spin_unlock_irqrestore(&ev_file->lock, flags);
kfree(to_free);
wake_up_interruptible(&ev_file->poll_wait);
return 0;
@ -2958,6 +2963,7 @@ static void devx_async_cmd_event_destroy_uobj(struct ib_uobject *uobj,
uobj);
struct devx_async_event_queue *ev_queue = &comp_ev_file->ev_queue;
struct devx_async_data *entry, *tmp;
LIST_HEAD(tmp_list);
spin_lock_irq(&ev_queue->lock);
ev_queue->is_destroyed = 1;
@ -2967,12 +2973,15 @@ static void devx_async_cmd_event_destroy_uobj(struct ib_uobject *uobj,
mlx5_cmd_cleanup_async_ctx(&comp_ev_file->async_ctx);
spin_lock_irq(&comp_ev_file->ev_queue.lock);
list_for_each_entry_safe(entry, tmp,
&comp_ev_file->ev_queue.event_list, list) {
/* Move all entries to a temporary list and free them outside lock */
list_splice_init(&comp_ev_file->ev_queue.event_list, &tmp_list);
spin_unlock_irq(&comp_ev_file->ev_queue.lock);
/* Free memory outside of critical section */
list_for_each_entry_safe(entry, tmp, &tmp_list, list) {
list_del(&entry->list);
kvfree(entry);
}
spin_unlock_irq(&comp_ev_file->ev_queue.lock);
};
static void devx_async_event_destroy_uobj(struct ib_uobject *uobj,
@ -2982,7 +2991,9 @@ static void devx_async_event_destroy_uobj(struct ib_uobject *uobj,
container_of(uobj, struct devx_async_event_file,
uobj);
struct devx_event_subscription *event_sub, *event_sub_tmp;
struct devx_async_event_data *entry, *tmp;
struct mlx5_ib_dev *dev = ev_file->dev;
LIST_HEAD(tmp_list);
spin_lock_irq(&ev_file->lock);
ev_file->is_destroyed = 1;
@ -2996,18 +3007,19 @@ static void devx_async_event_destroy_uobj(struct ib_uobject *uobj,
list_del_init(&event_sub->event_list);
} else {
struct devx_async_event_data *entry, *tmp;
list_for_each_entry_safe(entry, tmp, &ev_file->event_list,
list) {
list_del(&entry->list);
kfree(entry);
}
/* Move all entries to a temporary list */
list_splice_init(&ev_file->event_list, &tmp_list);
}
spin_unlock_irq(&ev_file->lock);
wake_up_interruptible(&ev_file->poll_wait);
/* Free event data outside of critical section */
list_for_each_entry_safe(entry, tmp, &tmp_list, list) {
list_del(&entry->list);
kfree(entry);
}
mutex_lock(&dev->devx_event_table.event_xa_lock);
/* delete the subscriptions which are related to this FD */
list_for_each_entry_safe(event_sub, event_sub_tmp,