Skip to content

Commit 7874eea

Browse files
jakemoronirleon
authored andcommitted
RDMA/iwcm: Fix workqueue list corruption by removing work_list
The commit e1168f0 ("RDMA/iwcm: Simplify cm_event_handler()") changed the work submission logic to unconditionally call queue_work() with the expectation that queue_work() would have no effect if work was already pending. The problem is that a free list of struct iwcm_work is used (for which struct work_struct is embedded), so each call to queue_work() is basically unique and therefore does indeed queue the work. This causes a problem in the work handler which walks the work_list until it's empty to process entries. This means that a single run of the work handler could process item N+1 and release it back to the free list while the actual workqueue entry is still queued. It could then get reused (INIT_WORK...) and lead to list corruption in the workqueue logic. Fix this by just removing the work_list. The workqueue already does this for us. This fixes the following error that was observed when stress testing with ucmatose on an Intel E830 in iWARP mode: [ 151.465780] list_del corruption. next->prev should be ffff9f0915c69c08, but was ffff9f0a1116be08. (next=ffff9f0a15b11c08) [ 151.466639] ------------[ cut here ]------------ [ 151.466986] kernel BUG at lib/list_debug.c:67! [ 151.467349] Oops: invalid opcode: 0000 [#1] SMP NOPTI [ 151.467753] CPU: 14 UID: 0 PID: 2306 Comm: kworker/u64:18 Not tainted 6.19.0-rc4+ #1 PREEMPT(voluntary) [ 151.468466] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 151.469192] Workqueue: 0x0 (iw_cm_wq) [ 151.469478] RIP: 0010:__list_del_entry_valid_or_report+0xf0/0x100 [ 151.469942] Code: c7 58 5f 4c b2 e8 10 50 aa ff 0f 0b 48 89 ef e8 36 57 cb ff 48 8b 55 08 48 89 e9 48 89 de 48 c7 c7 a8 5f 4c b2 e8 f0 4f aa ff <0f> 0b 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 90 90 90 90 90 90 [ 151.471323] RSP: 0000:ffffb15644e7bd68 EFLAGS: 00010046 [ 151.471712] RAX: 000000000000006d RBX: ffff9f0915c69c08 RCX: 0000000000000027 [ 151.472243] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff9f0a37d9c600 [ 151.472768] RBP: ffff9f0a15b11c08 R08: 0000000000000000 R09: c0000000ffff7fff [ 151.473294] R10: 0000000000000001 R11: ffffb15644e7bba8 R12: ffff9f092339ee68 [ 151.473817] R13: ffff9f0900059c28 R14: ffff9f092339ee78 R15: 0000000000000000 [ 151.474344] FS: 0000000000000000(0000) GS:ffff9f0a847b5000(0000) knlGS:0000000000000000 [ 151.474934] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 151.475362] CR2: 0000559e233a9088 CR3: 000000020296b004 CR4: 0000000000770ef0 [ 151.475895] PKRU: 55555554 [ 151.476118] Call Trace: [ 151.476331] <TASK> [ 151.476497] move_linked_works+0x49/0xa0 [ 151.476792] __pwq_activate_work.isra.46+0x2f/0xa0 [ 151.477151] pwq_dec_nr_in_flight+0x1e0/0x2f0 [ 151.477479] process_scheduled_works+0x1c8/0x410 [ 151.477823] worker_thread+0x125/0x260 [ 151.478108] ? __pfx_worker_thread+0x10/0x10 [ 151.478430] kthread+0xfe/0x240 [ 151.478671] ? __pfx_kthread+0x10/0x10 [ 151.478955] ? __pfx_kthread+0x10/0x10 [ 151.479240] ret_from_fork+0x208/0x270 [ 151.479523] ? __pfx_kthread+0x10/0x10 [ 151.479806] ret_from_fork_asm+0x1a/0x30 [ 151.480103] </TASK> Fixes: e1168f0 ("RDMA/iwcm: Simplify cm_event_handler()") Signed-off-by: Jacob Moroni <jmoroni@google.com> Link: https://patch.msgid.link/20260112020006.1352438-1-jmoroni@google.com Reviewed-by: Bart Van Assche <bvanassche@acm.org> Signed-off-by: Leon Romanovsky <leon@kernel.org>
1 parent 0beefd0 commit 7874eea

2 files changed

Lines changed: 21 additions & 36 deletions

File tree

drivers/infiniband/core/iwcm.c

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ static struct workqueue_struct *iwcm_wq;
9595
struct iwcm_work {
9696
struct work_struct work;
9797
struct iwcm_id_private *cm_id;
98-
struct list_head list;
9998
struct iw_cm_event event;
10099
struct list_head free_list;
101100
};
@@ -178,7 +177,6 @@ static int alloc_work_entries(struct iwcm_id_private *cm_id_priv, int count)
178177
return -ENOMEM;
179178
}
180179
work->cm_id = cm_id_priv;
181-
INIT_LIST_HEAD(&work->list);
182180
put_work(work);
183181
}
184182
return 0;
@@ -213,7 +211,6 @@ static void free_cm_id(struct iwcm_id_private *cm_id_priv)
213211
static bool iwcm_deref_id(struct iwcm_id_private *cm_id_priv)
214212
{
215213
if (refcount_dec_and_test(&cm_id_priv->refcount)) {
216-
BUG_ON(!list_empty(&cm_id_priv->work_list));
217214
free_cm_id(cm_id_priv);
218215
return true;
219216
}
@@ -260,7 +257,6 @@ struct iw_cm_id *iw_create_cm_id(struct ib_device *device,
260257
refcount_set(&cm_id_priv->refcount, 1);
261258
init_waitqueue_head(&cm_id_priv->connect_wait);
262259
init_completion(&cm_id_priv->destroy_comp);
263-
INIT_LIST_HEAD(&cm_id_priv->work_list);
264260
INIT_LIST_HEAD(&cm_id_priv->work_free_list);
265261

266262
return &cm_id_priv->id;
@@ -1007,13 +1003,13 @@ static int process_event(struct iwcm_id_private *cm_id_priv,
10071003
}
10081004

10091005
/*
1010-
* Process events on the work_list for the cm_id. If the callback
1011-
* function requests that the cm_id be deleted, a flag is set in the
1012-
* cm_id flags to indicate that when the last reference is
1013-
* removed, the cm_id is to be destroyed. This is necessary to
1014-
* distinguish between an object that will be destroyed by the app
1015-
* thread asleep on the destroy_comp list vs. an object destroyed
1016-
* here synchronously when the last reference is removed.
1006+
* Process events for the cm_id. If the callback function requests
1007+
* that the cm_id be deleted, a flag is set in the cm_id flags to
1008+
* indicate that when the last reference is removed, the cm_id is
1009+
* to be destroyed. This is necessary to distinguish between an
1010+
* object that will be destroyed by the app thread asleep on the
1011+
* destroy_comp list vs. an object destroyed here synchronously
1012+
* when the last reference is removed.
10171013
*/
10181014
static void cm_work_handler(struct work_struct *_work)
10191015
{
@@ -1024,35 +1020,26 @@ static void cm_work_handler(struct work_struct *_work)
10241020
int ret = 0;
10251021

10261022
spin_lock_irqsave(&cm_id_priv->lock, flags);
1027-
while (!list_empty(&cm_id_priv->work_list)) {
1028-
work = list_first_entry(&cm_id_priv->work_list,
1029-
struct iwcm_work, list);
1030-
list_del_init(&work->list);
1031-
levent = work->event;
1032-
put_work(work);
1033-
spin_unlock_irqrestore(&cm_id_priv->lock, flags);
1034-
1035-
if (!test_bit(IWCM_F_DROP_EVENTS, &cm_id_priv->flags)) {
1036-
ret = process_event(cm_id_priv, &levent);
1037-
if (ret) {
1038-
destroy_cm_id(&cm_id_priv->id);
1039-
WARN_ON_ONCE(iwcm_deref_id(cm_id_priv));
1040-
}
1041-
} else
1042-
pr_debug("dropping event %d\n", levent.event);
1043-
if (iwcm_deref_id(cm_id_priv))
1044-
return;
1045-
spin_lock_irqsave(&cm_id_priv->lock, flags);
1046-
}
1023+
levent = work->event;
1024+
put_work(work);
10471025
spin_unlock_irqrestore(&cm_id_priv->lock, flags);
1026+
1027+
if (!test_bit(IWCM_F_DROP_EVENTS, &cm_id_priv->flags)) {
1028+
ret = process_event(cm_id_priv, &levent);
1029+
if (ret) {
1030+
destroy_cm_id(&cm_id_priv->id);
1031+
WARN_ON_ONCE(iwcm_deref_id(cm_id_priv));
1032+
}
1033+
} else
1034+
pr_debug("dropping event %d\n", levent.event);
1035+
if (iwcm_deref_id(cm_id_priv))
1036+
return;
10481037
}
10491038

10501039
/*
10511040
* This function is called on interrupt context. Schedule events on
10521041
* the iwcm_wq thread to allow callback functions to downcall into
1053-
* the CM and/or block. Events are queued to a per-CM_ID
1054-
* work_list. If this is the first event on the work_list, the work
1055-
* element is also queued on the iwcm_wq thread.
1042+
* the CM and/or block.
10561043
*
10571044
* Each event holds a reference on the cm_id. Until the last posted
10581045
* event has been delivered and processed, the cm_id cannot be
@@ -1094,7 +1081,6 @@ static int cm_event_handler(struct iw_cm_id *cm_id,
10941081
}
10951082

10961083
refcount_inc(&cm_id_priv->refcount);
1097-
list_add_tail(&work->list, &cm_id_priv->work_list);
10981084
queue_work(iwcm_wq, &work->work);
10991085
out:
11001086
spin_unlock_irqrestore(&cm_id_priv->lock, flags);

drivers/infiniband/core/iwcm.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ struct iwcm_id_private {
5050
struct ib_qp *qp;
5151
struct completion destroy_comp;
5252
wait_queue_head_t connect_wait;
53-
struct list_head work_list;
5453
spinlock_t lock;
5554
refcount_t refcount;
5655
struct list_head work_free_list;

0 commit comments

Comments
 (0)