summaryrefslogtreecommitdiffstats
path: root/drivers/infiniband
diff options
context:
space:
mode:
authorKrishna Kumar <krkumar2@in.ibm.com>2006-09-29 20:51:49 +0200
committerRoland Dreier <rolandd@cisco.com>2006-10-02 23:52:15 +0200
commit6e35aabee125999f4b3c01326f5339fa74a89259 (patch)
tree8cc49d3d79b5dda31b0b947e80b55cd1d42b0583 /drivers/infiniband
parentRDMA/cma: Fix leak of cm_ids in case of failures (diff)
downloadlinux-6e35aabee125999f4b3c01326f5339fa74a89259.tar.xz
linux-6e35aabee125999f4b3c01326f5339fa74a89259.zip
RDMA/cma: Fix device removal race
The race is as follows: A process : cma_process_remove() calls cma_remove_id_dev(), which sets id state to CMA_DEVICE_REMOVAL and calls wait_event(dev_remove). B process : cma_req_handler() had incremented dev_remove, and calls cma_acquire_ib_dev() and on failure calls cma_release_remove(), which does a wake_up of cma_process_remove(). Then cma_req_handler() calls rdma_destroy_id(); A Process : cma_remove_id_dev() gets woken and checks the state of id, and since it is still (wrongly) CMA_DEVICE_REMOVAL, it calls notify_user(id) and if that fails, the caller - cma_process_remove() calls rdma_destroy_id(id). Two processes can call rdma_destroy_id(), resulting in one de-referencing kfreed id_priv. Fix is for process B to set CMA_DESTROYING in cma_req_handler() so that process A will return instead of doing a rdma_destroy_id(). Signed-off-by: Krishna Kumar <krkumar2@in.ibm.com> Signed-off-by: Sean Hefty <sean.hefty@intel.com> Signed-off-by: Roland Dreier <rolandd@cisco.com>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r--drivers/infiniband/core/cma.c1
1 files changed, 1 insertions, 0 deletions
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 69bb0892e887..f383a4f50ab0 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -932,6 +932,7 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
mutex_unlock(&lock);
if (ret) {
ret = -ENODEV;
+ cma_exch(conn_id, CMA_DESTROYING);
cma_release_remove(conn_id);
rdma_destroy_id(&conn_id->id);
goto out;