Skip to content

Commit ebb9098

Browse files
Steve WiseRoland Dreier
authored andcommitted
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>
1 parent 6bbcea0 commit ebb9098

File tree

1 file changed

+25
-22
lines changed

1 file changed

+25
-22
lines changed

drivers/infiniband/core/iwcm.c

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -146,28 +146,27 @@ static int copy_private_data(struct iw_cm_event *event)
146146
return 0;
147147
}
148148

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+
149155
/*
150156
* Release a reference on cm_id. If the last reference is being
151157
* released, enable the waiting thread (in iw_destroy_cm_id) to
152158
* get woken up, and return 1 if a thread is already waiting.
153159
*/
154160
static int iwcm_deref_id(struct iwcm_id_private *cm_id_priv)
155161
{
156-
int ret = 0;
157-
158162
BUG_ON(atomic_read(&cm_id_priv->refcount)==0);
159163
if (atomic_dec_and_test(&cm_id_priv->refcount)) {
160164
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-
}
167165
complete(&cm_id_priv->destroy_comp);
166+
return 1;
168167
}
169168

170-
return ret;
169+
return 0;
171170
}
172171

173172
static void add_ref(struct iw_cm_id *cm_id)
@@ -181,7 +180,11 @@ static void rem_ref(struct iw_cm_id *cm_id)
181180
{
182181
struct iwcm_id_private *cm_id_priv;
183182
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+
}
185188
}
186189

187190
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)
355358
case IW_CM_STATE_CONN_RECV:
356359
/*
357360
* 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.
359364
*/
360365
cm_id_priv->state = IW_CM_STATE_DESTROYING;
361366
break;
@@ -391,9 +396,7 @@ void iw_destroy_cm_id(struct iw_cm_id *cm_id)
391396

392397
wait_for_completion(&cm_id_priv->destroy_comp);
393398

394-
dealloc_work_entries(cm_id_priv);
395-
396-
kfree(cm_id_priv);
399+
free_cm_id(cm_id_priv);
397400
}
398401
EXPORT_SYMBOL(iw_destroy_cm_id);
399402

@@ -647,10 +650,11 @@ static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv,
647650
/* Call the client CM handler */
648651
ret = cm_id->cm_handler(cm_id, iw_event);
649652
if (ret) {
653+
iw_cm_reject(cm_id, NULL, 0);
650654
set_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags);
651655
destroy_cm_id(cm_id);
652656
if (atomic_read(&cm_id_priv->refcount)==0)
653-
kfree(cm_id);
657+
free_cm_id(cm_id_priv);
654658
}
655659

656660
out:
@@ -854,13 +858,12 @@ static void cm_work_handler(struct work_struct *_work)
854858
destroy_cm_id(&cm_id_priv->id);
855859
}
856860
BUG_ON(atomic_read(&cm_id_priv->refcount)==0);
857-
if (iwcm_deref_id(cm_id_priv))
858-
return;
859-
860-
if (atomic_read(&cm_id_priv->refcount)==0 &&
861-
test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)) {
862-
dealloc_work_entries(cm_id_priv);
863-
kfree(cm_id_priv);
861+
if (iwcm_deref_id(cm_id_priv)) {
862+
if (test_bit(IWCM_F_CALLBACK_DESTROY,
863+
&cm_id_priv->flags)) {
864+
BUG_ON(!list_empty(&cm_id_priv->work_list));
865+
free_cm_id(cm_id_priv);
866+
}
864867
return;
865868
}
866869
spin_lock_irqsave(&cm_id_priv->lock, flags);

0 commit comments

Comments
 (0)