Skip to contentSkip to conversation

RDMA/addr: Fix race with netevent_callback()/rdma_addr_cancel()

PATCH Applied Leon Romanovsky 2 participants 2 messages Sep 30, 2020 InfiniBand

lore
Share & tools
lore
#0 lore raw

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.

Fixes 44e75052bc2a ("RDMA/rdma_cm: Make rdma_addr_cancel into a fence")
---
 drivers/infiniband/core/addr.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)
drivers/infiniband/core/addr.c +5 -6
index 3a98439bba83..0abce004a959 100644
@@ -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

maintainer Applied
#1 lore raw
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")
---
 drivers/infiniband/core/addr.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

Applied to for-next

Jason