]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
IB/mlx5: Reduce spinlock contention by moving free operations outside
authorLi RongQing <lirongqing@baidu.com>
Sun, 3 May 2026 02:33:49 +0000 (22:33 -0400)
committerLeon Romanovsky <leon@kernel.org>
Mon, 18 May 2026 08:58:41 +0000 (04:58 -0400)
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>
drivers/infiniband/hw/mlx5/devx.c

index c2ae5a14047115ab9ce5f64e1b06a2b614d2405a..c5929d023ee38ebab502eeb557ca45b7f941c6f1 100644 (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,