Commit 54102dd4 authored by Krishnamraju Eraparaju's avatar Krishnamraju Eraparaju Committed by Doug Ledford
Browse files

RDMA/iwcm: move iw_rem_ref() calls out of spinlock



kref release routines usually perform memory release operations,
hence, they should not be called with spinlocks held.
one such case is: SIW kref release routine siw_free_qp(), which
can sleep via vfree() while freeing queue memory.

Hence, all iw_rem_ref() calls in IWCM are moved out of spinlocks.

Fixes: 922a8e9f ("RDMA: iWARP Connection Manager.")
Signed-off-by: default avatarKrishnamraju Eraparaju <krishna2@chelsio.com>
Reviewed-by: default avatarBernard Metzler <bmt@zurich.ibm.com>
Link: https://lore.kernel.org/r/20191007102627.12568-1-krishna2@chelsio.com


Signed-off-by: default avatarDoug Ledford <dledford@redhat.com>
parent 612e0486
Loading
Loading
Loading
Loading
+29 −23
Original line number Diff line number Diff line
@@ -372,6 +372,7 @@ EXPORT_SYMBOL(iw_cm_disconnect);
static void destroy_cm_id(struct iw_cm_id *cm_id)
{
	struct iwcm_id_private *cm_id_priv;
	struct ib_qp *qp;
	unsigned long flags;

	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
@@ -389,6 +390,9 @@ static void destroy_cm_id(struct iw_cm_id *cm_id)
	set_bit(IWCM_F_DROP_EVENTS, &cm_id_priv->flags);

	spin_lock_irqsave(&cm_id_priv->lock, flags);
	qp = cm_id_priv->qp;
	cm_id_priv->qp = NULL;

	switch (cm_id_priv->state) {
	case IW_CM_STATE_LISTEN:
		cm_id_priv->state = IW_CM_STATE_DESTROYING;
@@ -401,7 +405,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);
		/* Abrupt close of the connection */
		(void)iwcm_modify_qp_err(cm_id_priv->qp);
		(void)iwcm_modify_qp_err(qp);
		spin_lock_irqsave(&cm_id_priv->lock, flags);
		break;
	case IW_CM_STATE_IDLE:
@@ -426,11 +430,9 @@ static void destroy_cm_id(struct iw_cm_id *cm_id)
		BUG();
		break;
	}
	if (cm_id_priv->qp) {
		cm_id_priv->id.device->ops.iw_rem_ref(cm_id_priv->qp);
		cm_id_priv->qp = NULL;
	}
	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
	if (qp)
		cm_id_priv->id.device->ops.iw_rem_ref(qp);

	if (cm_id->mapped) {
		iwpm_remove_mapinfo(&cm_id->local_addr, &cm_id->m_local_addr);
@@ -671,11 +673,11 @@ int iw_cm_accept(struct iw_cm_id *cm_id,
		BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV);
		cm_id_priv->state = IW_CM_STATE_IDLE;
		spin_lock_irqsave(&cm_id_priv->lock, flags);
		if (cm_id_priv->qp) {
			cm_id->device->ops.iw_rem_ref(qp);
		qp = cm_id_priv->qp;
		cm_id_priv->qp = NULL;
		}
		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
		if (qp)
			cm_id->device->ops.iw_rem_ref(qp);
		clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
		wake_up_all(&cm_id_priv->connect_wait);
	}
@@ -696,7 +698,7 @@ int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
	struct iwcm_id_private *cm_id_priv;
	int ret;
	unsigned long flags;
	struct ib_qp *qp;
	struct ib_qp *qp = NULL;

	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);

@@ -730,13 +732,13 @@ int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
		return 0;	/* success */

	spin_lock_irqsave(&cm_id_priv->lock, flags);
	if (cm_id_priv->qp) {
		cm_id->device->ops.iw_rem_ref(qp);
	qp = cm_id_priv->qp;
	cm_id_priv->qp = NULL;
	}
	cm_id_priv->state = IW_CM_STATE_IDLE;
err:
	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
	if (qp)
		cm_id->device->ops.iw_rem_ref(qp);
	clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
	wake_up_all(&cm_id_priv->connect_wait);
	return ret;
@@ -878,6 +880,7 @@ static int cm_conn_est_handler(struct iwcm_id_private *cm_id_priv,
static int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv,
			       struct iw_cm_event *iw_event)
{
	struct ib_qp *qp = NULL;
	unsigned long flags;
	int ret;

@@ -896,11 +899,13 @@ static int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv,
		cm_id_priv->state = IW_CM_STATE_ESTABLISHED;
	} else {
		/* REJECTED or RESET */
		cm_id_priv->id.device->ops.iw_rem_ref(cm_id_priv->qp);
		qp = cm_id_priv->qp;
		cm_id_priv->qp = NULL;
		cm_id_priv->state = IW_CM_STATE_IDLE;
	}
	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
	if (qp)
		cm_id_priv->id.device->ops.iw_rem_ref(qp);
	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);

	if (iw_event->private_data_len)
@@ -942,21 +947,18 @@ static void cm_disconnect_handler(struct iwcm_id_private *cm_id_priv,
static int cm_close_handler(struct iwcm_id_private *cm_id_priv,
				  struct iw_cm_event *iw_event)
{
	struct ib_qp *qp;
	unsigned long flags;
	int ret = 0;
	int ret = 0, notify_event = 0;
	spin_lock_irqsave(&cm_id_priv->lock, flags);

	if (cm_id_priv->qp) {
		cm_id_priv->id.device->ops.iw_rem_ref(cm_id_priv->qp);
	qp = cm_id_priv->qp;
	cm_id_priv->qp = NULL;
	}

	switch (cm_id_priv->state) {
	case IW_CM_STATE_ESTABLISHED:
	case IW_CM_STATE_CLOSING:
		cm_id_priv->state = IW_CM_STATE_IDLE;
		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
		ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
		spin_lock_irqsave(&cm_id_priv->lock, flags);
		notify_event = 1;
		break;
	case IW_CM_STATE_DESTROYING:
		break;
@@ -965,6 +967,10 @@ static int cm_close_handler(struct iwcm_id_private *cm_id_priv,
	}
	spin_unlock_irqrestore(&cm_id_priv->lock, flags);

	if (qp)
		cm_id_priv->id.device->ops.iw_rem_ref(qp);
	if (notify_event)
		ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
	return ret;
}