diff options
author | Steve Wise <swise@opengridcomputing.com> | 2007-02-15 09:09:36 -0500 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2007-02-16 16:57:35 -0500 |
commit | ebb90986e183296086b5d6678a838f125d743982 (patch) | |
tree | 68cfbbd4bf32feda721cf34f220bcd9d84c75214 /drivers/infiniband | |
parent | 6bbcea0d42209ab5f0fae213050ad042c499ad8b (diff) |
RDMA/iwcm: iw_cm_id destruction race fixes
iwcm iw_cm_id destruction race condition fixes:
- iwcm_deref_id() always wakes up if there's another reference.
- clean up race condition in cm_work_handler().
- create static void free_cm_id() which deallocs the work entries and then
kfrees the cm_id memory. This reduces code replication.
- rem_ref() if this is the last reference -and- the IWCM owns freeing the
cm_id, then free it.
Signed-off-by: Steve Wise <swise@opengridcomputing.com>
Signed-off-by: Tom Tucker <tom@opengridcomputing.com>
Acked-by: Krishna Kumar <krkumar2@in.ibm.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r-- | drivers/infiniband/core/iwcm.c | 47 |
1 files changed, 25 insertions, 22 deletions
diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c index 1039ad57d53b..891d1fa7b2eb 100644 --- a/drivers/infiniband/core/iwcm.c +++ b/drivers/infiniband/core/iwcm.c | |||
@@ -146,6 +146,12 @@ static int copy_private_data(struct iw_cm_event *event) | |||
146 | return 0; | 146 | return 0; |
147 | } | 147 | } |
148 | 148 | ||
149 | static void free_cm_id(struct iwcm_id_private *cm_id_priv) | ||
150 | { | ||
151 | dealloc_work_entries(cm_id_priv); | ||
152 | kfree(cm_id_priv); | ||
153 | } | ||
154 | |||
149 | /* | 155 | /* |
150 | * Release a reference on cm_id. If the last reference is being | 156 | * Release a reference on cm_id. If the last reference is being |
151 | * released, enable the waiting thread (in iw_destroy_cm_id) to | 157 | * released, enable the waiting thread (in iw_destroy_cm_id) to |
@@ -153,21 +159,14 @@ static int copy_private_data(struct iw_cm_event *event) | |||
153 | */ | 159 | */ |
154 | static int iwcm_deref_id(struct iwcm_id_private *cm_id_priv) | 160 | static int iwcm_deref_id(struct iwcm_id_private *cm_id_priv) |
155 | { | 161 | { |
156 | int ret = 0; | ||
157 | |||
158 | BUG_ON(atomic_read(&cm_id_priv->refcount)==0); | 162 | BUG_ON(atomic_read(&cm_id_priv->refcount)==0); |
159 | if (atomic_dec_and_test(&cm_id_priv->refcount)) { | 163 | if (atomic_dec_and_test(&cm_id_priv->refcount)) { |
160 | BUG_ON(!list_empty(&cm_id_priv->work_list)); | 164 | BUG_ON(!list_empty(&cm_id_priv->work_list)); |
161 | if (waitqueue_active(&cm_id_priv->destroy_comp.wait)) { | ||
162 | BUG_ON(cm_id_priv->state != IW_CM_STATE_DESTROYING); | ||
163 | BUG_ON(test_bit(IWCM_F_CALLBACK_DESTROY, | ||
164 | &cm_id_priv->flags)); | ||
165 | ret = 1; | ||
166 | } | ||
167 | complete(&cm_id_priv->destroy_comp); | 165 | complete(&cm_id_priv->destroy_comp); |
166 | return 1; | ||
168 | } | 167 | } |
169 | 168 | ||
170 | return ret; | 169 | return 0; |
171 | } | 170 | } |
172 | 171 | ||
173 | static void add_ref(struct iw_cm_id *cm_id) | 172 | static void add_ref(struct iw_cm_id *cm_id) |
@@ -181,7 +180,11 @@ static void rem_ref(struct iw_cm_id *cm_id) | |||
181 | { | 180 | { |
182 | struct iwcm_id_private *cm_id_priv; | 181 | struct iwcm_id_private *cm_id_priv; |
183 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | 182 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); |
184 | iwcm_deref_id(cm_id_priv); | 183 | if (iwcm_deref_id(cm_id_priv) && |
184 | test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)) { | ||
185 | BUG_ON(!list_empty(&cm_id_priv->work_list)); | ||
186 | free_cm_id(cm_id_priv); | ||
187 | } | ||
185 | } | 188 | } |
186 | 189 | ||
187 | static int cm_event_handler(struct iw_cm_id *cm_id, struct iw_cm_event *event); | 190 | static int cm_event_handler(struct iw_cm_id *cm_id, struct iw_cm_event *event); |
@@ -355,7 +358,9 @@ static void destroy_cm_id(struct iw_cm_id *cm_id) | |||
355 | case IW_CM_STATE_CONN_RECV: | 358 | case IW_CM_STATE_CONN_RECV: |
356 | /* | 359 | /* |
357 | * App called destroy before/without calling accept after | 360 | * App called destroy before/without calling accept after |
358 | * receiving connection request event notification. | 361 | * receiving connection request event notification or |
362 | * returned non zero from the event callback function. | ||
363 | * In either case, must tell the provider to reject. | ||
359 | */ | 364 | */ |
360 | cm_id_priv->state = IW_CM_STATE_DESTROYING; | 365 | cm_id_priv->state = IW_CM_STATE_DESTROYING; |
361 | break; | 366 | break; |
@@ -391,9 +396,7 @@ void iw_destroy_cm_id(struct iw_cm_id *cm_id) | |||
391 | 396 | ||
392 | wait_for_completion(&cm_id_priv->destroy_comp); | 397 | wait_for_completion(&cm_id_priv->destroy_comp); |
393 | 398 | ||
394 | dealloc_work_entries(cm_id_priv); | 399 | free_cm_id(cm_id_priv); |
395 | |||
396 | kfree(cm_id_priv); | ||
397 | } | 400 | } |
398 | EXPORT_SYMBOL(iw_destroy_cm_id); | 401 | EXPORT_SYMBOL(iw_destroy_cm_id); |
399 | 402 | ||
@@ -647,10 +650,11 @@ static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv, | |||
647 | /* Call the client CM handler */ | 650 | /* Call the client CM handler */ |
648 | ret = cm_id->cm_handler(cm_id, iw_event); | 651 | ret = cm_id->cm_handler(cm_id, iw_event); |
649 | if (ret) { | 652 | if (ret) { |
653 | iw_cm_reject(cm_id, NULL, 0); | ||
650 | set_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); | 654 | set_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); |
651 | destroy_cm_id(cm_id); | 655 | destroy_cm_id(cm_id); |
652 | if (atomic_read(&cm_id_priv->refcount)==0) | 656 | if (atomic_read(&cm_id_priv->refcount)==0) |
653 | kfree(cm_id); | 657 | free_cm_id(cm_id_priv); |
654 | } | 658 | } |
655 | 659 | ||
656 | out: | 660 | out: |
@@ -854,13 +858,12 @@ static void cm_work_handler(struct work_struct *_work) | |||
854 | destroy_cm_id(&cm_id_priv->id); | 858 | destroy_cm_id(&cm_id_priv->id); |
855 | } | 859 | } |
856 | BUG_ON(atomic_read(&cm_id_priv->refcount)==0); | 860 | BUG_ON(atomic_read(&cm_id_priv->refcount)==0); |
857 | if (iwcm_deref_id(cm_id_priv)) | 861 | if (iwcm_deref_id(cm_id_priv)) { |
858 | return; | 862 | if (test_bit(IWCM_F_CALLBACK_DESTROY, |
859 | 863 | &cm_id_priv->flags)) { | |
860 | if (atomic_read(&cm_id_priv->refcount)==0 && | 864 | BUG_ON(!list_empty(&cm_id_priv->work_list)); |
861 | test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)) { | 865 | free_cm_id(cm_id_priv); |
862 | dealloc_work_entries(cm_id_priv); | 866 | } |
863 | kfree(cm_id_priv); | ||
864 | return; | 867 | return; |
865 | } | 868 | } |
866 | spin_lock_irqsave(&cm_id_priv->lock, flags); | 869 | spin_lock_irqsave(&cm_id_priv->lock, flags); |