RDMA/addr: Fix race with netevent_callback()/rdma_addr_cancel()
PATCH 1/1 RDMA/addr: Fix race with netevent_callback()/rdma_addr_cancel()
From: Jason Gunthorpe <[email protected]>
This three thread race can result in the work being run once the callback becomes NULL:
CPU1 CPU2 CPU3
netevent_callback()
process_one_req() rdma_addr_cancel()
[..]
spin_lock_bh()
set_timeout()
spin_unlock_bh()
spin_lock_bh()
list_del_init(&req->list);
spin_unlock_bh()
req->callback = NULL
spin_lock_bh()
if (!list_empty(&req->list))
// Skipped!
// cancel_delayed_work(&req->work);
spin_unlock_bh()
process_one_req() // again
req->callback() // BOOM
cancel_delayed_work_sync()The solution is to always cancel the work once it is completed so any in between set_timeout() does not result in it running again.
--- drivers/infiniband/core/addr.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
drivers/infiniband/core/addr.c +5 -6
@@ -647,13 +647,12 @@ static void process_one_req(struct work_struct *_work) req->callback =NULL ;spin_lock_bh (&lock); +/* +* Although the work will normally have been canceled by the workqueue, +* it can still be requeued as long as it is on the req_list. +*/ +cancel_delayed_work (&req->work);if (!list_empty (&req->list)) { -/* -* Although the work will normally have been canceled by the -* workqueue, it can still be requeued as long as it is on the -* req_list. -*/ -cancel_delayed_work (&req->work);list_del_init (&req->list);kfree (req); }
--
2.26.2
Quoted 31 lines · Leon Romanovsky
From: Jason Gunthorpe <[email protected]>
This three thread race can result in the work being run once the callback becomes NULL:
CPU1 CPU2 CPU3
netevent_callback()process_one_req() rdma_addr_cancel() [..] spin_lock_bh() set_timeout() spin_unlock_bh() spin_lock_bh() list_del_init(&req->list); spin_unlock_bh() req->callback = NULL spin_lock_bh() if (!list_empty(&req->list)) // Skipped! // cancel_delayed_work(&req->work); spin_unlock_bh() process_one_req() // again req->callback() // BOOM cancel_delayed_work_sync()The solution is to always cancel the work once it is completed so any in between
set_timeout()does not result in it running again.
Quoted 7 lines
Fixes 44e75052bc2a ("RDMA/rdma_cm: Make rdma_addr_cancel into a fence")Reported-by Dan Aloni <[email protected]>Signed-off-by Jason Gunthorpe <[email protected]>Signed-off-by Leon Romanovsky <[email protected]>--- drivers/infiniband/core/addr.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
Applied to for-next
Jason