diff options
Diffstat (limited to 'drivers/infiniband/core/iwcm.c')
| -rw-r--r-- | drivers/infiniband/core/iwcm.c | 43 | 
1 files changed, 27 insertions, 16 deletions
diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c index 2a1e9ae134b..3d2e489ab73 100644 --- a/drivers/infiniband/core/iwcm.c +++ b/drivers/infiniband/core/iwcm.c @@ -45,6 +45,7 @@  #include <linux/workqueue.h>  #include <linux/completion.h>  #include <linux/slab.h> +#include <linux/module.h>  #include <rdma/iw_cm.h>  #include <rdma/ib_addr.h> @@ -180,9 +181,16 @@ static void add_ref(struct iw_cm_id *cm_id)  static void rem_ref(struct iw_cm_id *cm_id)  {  	struct iwcm_id_private *cm_id_priv; +	int cb_destroy; +  	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); -	if (iwcm_deref_id(cm_id_priv) && -	    test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)) { + +	/* +	 * Test bit before deref in case the cm_id gets freed on another +	 * thread. +	 */ +	cb_destroy = test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); +	if (iwcm_deref_id(cm_id_priv) && cb_destroy) {  		BUG_ON(!list_empty(&cm_id_priv->work_list));  		free_cm_id(cm_id_priv);  	} @@ -326,7 +334,6 @@ static void destroy_cm_id(struct iw_cm_id *cm_id)  {  	struct iwcm_id_private *cm_id_priv;  	unsigned long flags; -	int ret;  	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);  	/* @@ -342,7 +349,7 @@ static void destroy_cm_id(struct iw_cm_id *cm_id)  		cm_id_priv->state = IW_CM_STATE_DESTROYING;  		spin_unlock_irqrestore(&cm_id_priv->lock, flags);  		/* destroy the listening endpoint */ -		ret = cm_id->device->iwcm->destroy_listen(cm_id); +		cm_id->device->iwcm->destroy_listen(cm_id);  		spin_lock_irqsave(&cm_id_priv->lock, flags);  		break;  	case IW_CM_STATE_ESTABLISHED: @@ -623,17 +630,6 @@ static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv,  	 */  	BUG_ON(iw_event->status); -	/* -	 * We could be destroying the listening id. If so, ignore this -	 * upcall. -	 */ -	spin_lock_irqsave(&listen_id_priv->lock, flags); -	if (listen_id_priv->state != IW_CM_STATE_LISTEN) { -		spin_unlock_irqrestore(&listen_id_priv->lock, flags); -		goto out; -	} -	spin_unlock_irqrestore(&listen_id_priv->lock, flags); -  	cm_id = iw_create_cm_id(listen_id_priv->id.device,  				listen_id_priv->id.cm_handler,  				listen_id_priv->id.context); @@ -648,6 +644,19 @@ static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv,  	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);  	cm_id_priv->state = IW_CM_STATE_CONN_RECV; +	/* +	 * We could be destroying the listening id. If so, ignore this +	 * upcall. +	 */ +	spin_lock_irqsave(&listen_id_priv->lock, flags); +	if (listen_id_priv->state != IW_CM_STATE_LISTEN) { +		spin_unlock_irqrestore(&listen_id_priv->lock, flags); +		iw_cm_reject(cm_id, NULL, 0); +		iw_destroy_cm_id(cm_id); +		goto out; +	} +	spin_unlock_irqrestore(&listen_id_priv->lock, flags); +  	ret = alloc_work_entries(cm_id_priv, 3);  	if (ret) {  		iw_cm_reject(cm_id, NULL, 0); @@ -725,7 +734,7 @@ static int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv,  	 */  	clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);  	BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_SENT); -	if (iw_event->status == IW_CM_EVENT_STATUS_ACCEPTED) { +	if (iw_event->status == 0) {  		cm_id_priv->id.local_addr = iw_event->local_addr;  		cm_id_priv->id.remote_addr = iw_event->remote_addr;  		cm_id_priv->state = IW_CM_STATE_ESTABLISHED; @@ -875,6 +884,8 @@ static void cm_work_handler(struct work_struct *_work)  			}  			return;  		} +		if (empty) +			return;  		spin_lock_irqsave(&cm_id_priv->lock, flags);  	}  	spin_unlock_irqrestore(&cm_id_priv->lock, flags);  | 
