diff mbox

libfc: replace 'rp_mutex' with 'rp_lock'

Message ID 1461571293-953-1-git-send-email-hare@suse.de (mailing list archive)
State New, archived
Headers show

Commit Message

Hannes Reinecke April 25, 2016, 8:01 a.m. UTC
We cannot use an embedded mutex in a structure with reference
counting, as mutex unlock might be delayed, and the waiters
might then access an already freed memory area.
So convert it to a spinlock.

For details cf https://lkml.org/lkml/2015/2/11/245

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 drivers/scsi/libfc/fc_rport.c | 90 +++++++++++++++++++++----------------------
 include/scsi/libfc.h          |  4 +-
 2 files changed, 47 insertions(+), 47 deletions(-)

Comments

Ewan Milne April 25, 2016, 8:26 p.m. UTC | #1
On Mon, 2016-04-25 at 10:01 +0200, Hannes Reinecke wrote:
> We cannot use an embedded mutex in a structure with reference
> counting, as mutex unlock might be delayed, and the waiters
> might then access an already freed memory area.
> So convert it to a spinlock.
> 
> For details cf https://lkml.org/lkml/2015/2/11/245
> 
> Signed-off-by: Hannes Reinecke <hare@suse.de>

There are comments at the beginning of fc_rport.c in the section
RPORT LOCKING that still refer to the rport mutex.  (I assume this is
really the fc_rport_priv mutex since fc_rport does not have a mutex.)
These comments should be updated if the locking is changed.  Also
see the comment about the mutex being held near fc_rport_enter_delete().

We may have a reproducible test case for our FCoE issues, I will
see if we can tell if this fixes our problem.

-Ewan

> ---
>  drivers/scsi/libfc/fc_rport.c | 90 +++++++++++++++++++++----------------------
>  include/scsi/libfc.h          |  4 +-
>  2 files changed, 47 insertions(+), 47 deletions(-)
> 
> diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
> index 589ff9a..4520b5a 100644
> --- a/drivers/scsi/libfc/fc_rport.c
> +++ b/drivers/scsi/libfc/fc_rport.c
> @@ -136,7 +136,7 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
>  	rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
>  
>  	kref_init(&rdata->kref);
> -	mutex_init(&rdata->rp_mutex);
> +	spin_lock_init(&rdata->rp_lock);
>  	rdata->local_port = lport;
>  	rdata->rp_state = RPORT_ST_INIT;
>  	rdata->event = RPORT_EV_NONE;
> @@ -251,7 +251,7 @@ static void fc_rport_work(struct work_struct *work)
>  	struct fc4_prov *prov;
>  	u8 type;
>  
> -	mutex_lock(&rdata->rp_mutex);
> +	spin_lock(&rdata->rp_lock);
>  	event = rdata->event;
>  	rport_ops = rdata->ops;
>  	rport = rdata->rport;
> @@ -264,7 +264,7 @@ static void fc_rport_work(struct work_struct *work)
>  		rdata->event = RPORT_EV_NONE;
>  		rdata->major_retries = 0;
>  		kref_get(&rdata->kref);
> -		mutex_unlock(&rdata->rp_mutex);
> +		spin_unlock(&rdata->rp_lock);
>  
>  		if (!rport)
>  			rport = fc_remote_port_add(lport->host, 0, &ids);
> @@ -274,7 +274,7 @@ static void fc_rport_work(struct work_struct *work)
>  			kref_put(&rdata->kref, lport->tt.rport_destroy);
>  			return;
>  		}
> -		mutex_lock(&rdata->rp_mutex);
> +		spin_lock(&rdata->rp_lock);
>  		if (rdata->rport)
>  			FC_RPORT_DBG(rdata, "rport already allocated\n");
>  		rdata->rport = rport;
> @@ -287,7 +287,7 @@ static void fc_rport_work(struct work_struct *work)
>  		rpriv->flags = rdata->flags;
>  		rpriv->e_d_tov = rdata->e_d_tov;
>  		rpriv->r_a_tov = rdata->r_a_tov;
> -		mutex_unlock(&rdata->rp_mutex);
> +		spin_unlock(&rdata->rp_lock);
>  
>  		if (rport_ops && rport_ops->event_callback) {
>  			FC_RPORT_DBG(rdata, "callback ev %d\n", event);
> @@ -313,7 +313,7 @@ static void fc_rport_work(struct work_struct *work)
>  			mutex_unlock(&fc_prov_mutex);
>  		}
>  		port_id = rdata->ids.port_id;
> -		mutex_unlock(&rdata->rp_mutex);
> +		spin_unlock(&rdata->rp_lock);
>  
>  		if (rport_ops && rport_ops->event_callback) {
>  			FC_RPORT_DBG(rdata, "callback ev %d\n", event);
> @@ -334,18 +334,18 @@ static void fc_rport_work(struct work_struct *work)
>  		if (rport) {
>  			rpriv = rport->dd_data;
>  			rpriv->rp_state = RPORT_ST_DELETE;
> -			mutex_lock(&rdata->rp_mutex);
> +			spin_lock(&rdata->rp_lock);
>  			rdata->rport = NULL;
> -			mutex_unlock(&rdata->rp_mutex);
> +			spin_unlock(&rdata->rp_lock);
>  			fc_remote_port_delete(rport);
>  		}
>  
>  		mutex_lock(&lport->disc.disc_mutex);
> -		mutex_lock(&rdata->rp_mutex);
> +		spin_lock(&rdata->rp_lock);
>  		if (rdata->rp_state == RPORT_ST_DELETE) {
>  			if (port_id == FC_FID_DIR_SERV) {
>  				rdata->event = RPORT_EV_NONE;
> -				mutex_unlock(&rdata->rp_mutex);
> +				spin_unlock(&rdata->rp_lock);
>  				kref_put(&rdata->kref, lport->tt.rport_destroy);
>  			} else if ((rdata->flags & FC_RP_STARTED) &&
>  				   rdata->major_retries <
> @@ -354,11 +354,11 @@ static void fc_rport_work(struct work_struct *work)
>  				rdata->event = RPORT_EV_NONE;
>  				FC_RPORT_DBG(rdata, "work restart\n");
>  				fc_rport_enter_flogi(rdata);
> -				mutex_unlock(&rdata->rp_mutex);
> +				spin_unlock(&rdata->rp_lock);
>  			} else {
>  				FC_RPORT_DBG(rdata, "work delete\n");
>  				list_del_rcu(&rdata->peers);
> -				mutex_unlock(&rdata->rp_mutex);
> +				spin_unlock(&rdata->rp_lock);
>  				kref_put(&rdata->kref, lport->tt.rport_destroy);
>  			}
>  		} else {
> @@ -368,13 +368,13 @@ static void fc_rport_work(struct work_struct *work)
>  			rdata->event = RPORT_EV_NONE;
>  			if (rdata->rp_state == RPORT_ST_READY)
>  				fc_rport_enter_ready(rdata);
> -			mutex_unlock(&rdata->rp_mutex);
> +			spin_unlock(&rdata->rp_lock);
>  		}
>  		mutex_unlock(&lport->disc.disc_mutex);
>  		break;
>  
>  	default:
> -		mutex_unlock(&rdata->rp_mutex);
> +		spin_unlock(&rdata->rp_lock);
>  		break;
>  	}
>  }
> @@ -393,7 +393,7 @@ static void fc_rport_work(struct work_struct *work)
>   */
>  static int fc_rport_login(struct fc_rport_priv *rdata)
>  {
> -	mutex_lock(&rdata->rp_mutex);
> +	spin_lock(&rdata->rp_lock);
>  
>  	rdata->flags |= FC_RP_STARTED;
>  	switch (rdata->rp_state) {
> @@ -409,7 +409,7 @@ static int fc_rport_login(struct fc_rport_priv *rdata)
>  		fc_rport_enter_flogi(rdata);
>  		break;
>  	}
> -	mutex_unlock(&rdata->rp_mutex);
> +	spin_unlock(&rdata->rp_lock);
>  
>  	return 0;
>  }
> @@ -453,7 +453,7 @@ static void fc_rport_enter_delete(struct fc_rport_priv *rdata,
>   */
>  static int fc_rport_logoff(struct fc_rport_priv *rdata)
>  {
> -	mutex_lock(&rdata->rp_mutex);
> +	spin_lock(&rdata->rp_lock);
>  
>  	FC_RPORT_DBG(rdata, "Remove port\n");
>  
> @@ -470,7 +470,7 @@ static int fc_rport_logoff(struct fc_rport_priv *rdata)
>  	 */
>  	fc_rport_enter_delete(rdata, RPORT_EV_STOP);
>  out:
> -	mutex_unlock(&rdata->rp_mutex);
> +	spin_unlock(&rdata->rp_lock);
>  	return 0;
>  }
>  
> @@ -505,7 +505,7 @@ static void fc_rport_timeout(struct work_struct *work)
>  	struct fc_rport_priv *rdata =
>  		container_of(work, struct fc_rport_priv, retry_work.work);
>  
> -	mutex_lock(&rdata->rp_mutex);
> +	spin_lock(&rdata->rp_lock);
>  
>  	switch (rdata->rp_state) {
>  	case RPORT_ST_FLOGI:
> @@ -530,7 +530,7 @@ static void fc_rport_timeout(struct work_struct *work)
>  		break;
>  	}
>  
> -	mutex_unlock(&rdata->rp_mutex);
> +	spin_unlock(&rdata->rp_lock);
>  }
>  
>  /**
> @@ -666,7 +666,7 @@ static void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
>  	if (fp == ERR_PTR(-FC_EX_CLOSED))
>  		goto put;
>  
> -	mutex_lock(&rdata->rp_mutex);
> +	spin_lock(&rdata->rp_lock);
>  
>  	if (rdata->rp_state != RPORT_ST_FLOGI) {
>  		FC_RPORT_DBG(rdata, "Received a FLOGI response, but in state "
> @@ -700,7 +700,7 @@ static void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
>  out:
>  	fc_frame_free(fp);
>  err:
> -	mutex_unlock(&rdata->rp_mutex);
> +	spin_unlock(&rdata->rp_lock);
>  put:
>  	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
>  	return;
> @@ -783,7 +783,7 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
>  		rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR;
>  		goto reject;
>  	}
> -	mutex_lock(&rdata->rp_mutex);
> +	spin_lock(&rdata->rp_lock);
>  
>  	FC_RPORT_DBG(rdata, "Received FLOGI in %s state\n",
>  		     fc_rport_state(rdata));
> @@ -805,7 +805,7 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
>  		if (lport->point_to_multipoint)
>  			break;
>  	case RPORT_ST_DELETE:
> -		mutex_unlock(&rdata->rp_mutex);
> +		spin_unlock(&rdata->rp_lock);
>  		rjt_data.reason = ELS_RJT_FIP;
>  		rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR;
>  		goto reject;
> @@ -822,13 +822,13 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
>  		 * This queues work to be sure exchanges are reset.
>  		 */
>  		fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
> -		mutex_unlock(&rdata->rp_mutex);
> +		spin_unlock(&rdata->rp_lock);
>  		rjt_data.reason = ELS_RJT_BUSY;
>  		rjt_data.explan = ELS_EXPL_NONE;
>  		goto reject;
>  	}
>  	if (fc_rport_login_complete(rdata, fp)) {
> -		mutex_unlock(&rdata->rp_mutex);
> +		spin_unlock(&rdata->rp_lock);
>  		rjt_data.reason = ELS_RJT_LOGIC;
>  		rjt_data.explan = ELS_EXPL_NONE;
>  		goto reject;
> @@ -850,7 +850,7 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
>  	else
>  		fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
>  out:
> -	mutex_unlock(&rdata->rp_mutex);
> +	spin_unlock(&rdata->rp_lock);
>  	mutex_unlock(&disc->disc_mutex);
>  	fc_frame_free(rx_fp);
>  	return;
> @@ -881,7 +881,7 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
>  	u16 cssp_seq;
>  	u8 op;
>  
> -	mutex_lock(&rdata->rp_mutex);
> +	spin_lock(&rdata->rp_lock);
>  
>  	FC_RPORT_DBG(rdata, "Received a PLOGI %s\n", fc_els_resp_type(fp));
>  
> @@ -922,7 +922,7 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
>  out:
>  	fc_frame_free(fp);
>  err:
> -	mutex_unlock(&rdata->rp_mutex);
> +	spin_unlock(&rdata->rp_lock);
>  	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
>  }
>  
> @@ -1005,7 +1005,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
>  	u8 op;
>  	u8 resp_code = 0;
>  
> -	mutex_lock(&rdata->rp_mutex);
> +	spin_lock(&rdata->rp_lock);
>  
>  	FC_RPORT_DBG(rdata, "Received a PRLI %s\n", fc_els_resp_type(fp));
>  
> @@ -1075,7 +1075,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
>  out:
>  	fc_frame_free(fp);
>  err:
> -	mutex_unlock(&rdata->rp_mutex);
> +	spin_unlock(&rdata->rp_lock);
>  	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
>  }
>  
> @@ -1153,7 +1153,7 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
>  	struct fc_rport_priv *rdata = rdata_arg;
>  	u8 op;
>  
> -	mutex_lock(&rdata->rp_mutex);
> +	spin_lock(&rdata->rp_lock);
>  
>  	FC_RPORT_DBG(rdata, "Received a RTV %s\n", fc_els_resp_type(fp));
>  
> @@ -1197,7 +1197,7 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
>  out:
>  	fc_frame_free(fp);
>  err:
> -	mutex_unlock(&rdata->rp_mutex);
> +	spin_unlock(&rdata->rp_lock);
>  	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
>  }
>  
> @@ -1289,7 +1289,7 @@ static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
>  	struct fc_els_adisc *adisc;
>  	u8 op;
>  
> -	mutex_lock(&rdata->rp_mutex);
> +	spin_lock(&rdata->rp_lock);
>  
>  	FC_RPORT_DBG(rdata, "Received a ADISC response\n");
>  
> @@ -1326,7 +1326,7 @@ static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
>  out:
>  	fc_frame_free(fp);
>  err:
> -	mutex_unlock(&rdata->rp_mutex);
> +	spin_unlock(&rdata->rp_lock);
>  	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
>  }
>  
> @@ -1483,7 +1483,7 @@ static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
>  		mutex_unlock(&lport->disc.disc_mutex);
>  		goto reject;
>  	}
> -	mutex_lock(&rdata->rp_mutex);
> +	spin_lock(&rdata->rp_lock);
>  	mutex_unlock(&lport->disc.disc_mutex);
>  
>  	switch (rdata->rp_state) {
> @@ -1493,7 +1493,7 @@ static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
>  	case RPORT_ST_ADISC:
>  		break;
>  	default:
> -		mutex_unlock(&rdata->rp_mutex);
> +		spin_unlock(&rdata->rp_lock);
>  		goto reject;
>  	}
>  
> @@ -1523,7 +1523,7 @@ static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
>  		break;
>  	}
>  
> -	mutex_unlock(&rdata->rp_mutex);
> +	spin_unlock(&rdata->rp_lock);
>  	return;
>  
>  reject:
> @@ -1616,7 +1616,7 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
>  		goto reject;
>  	}
>  
> -	mutex_lock(&rdata->rp_mutex);
> +	spin_lock(&rdata->rp_lock);
>  	mutex_unlock(&disc->disc_mutex);
>  
>  	rdata->ids.port_name = get_unaligned_be64(&pl->fl_wwpn);
> @@ -1643,7 +1643,7 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
>  	case RPORT_ST_PLOGI:
>  		FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI state\n");
>  		if (rdata->ids.port_name < lport->wwpn) {
> -			mutex_unlock(&rdata->rp_mutex);
> +			spin_unlock(&rdata->rp_lock);
>  			rjt_data.reason = ELS_RJT_INPROG;
>  			rjt_data.explan = ELS_EXPL_NONE;
>  			goto reject;
> @@ -1661,14 +1661,14 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
>  	case RPORT_ST_DELETE:
>  		FC_RPORT_DBG(rdata, "Received PLOGI in state %s - send busy\n",
>  			     fc_rport_state(rdata));
> -		mutex_unlock(&rdata->rp_mutex);
> +		spin_unlock(&rdata->rp_lock);
>  		rjt_data.reason = ELS_RJT_BUSY;
>  		rjt_data.explan = ELS_EXPL_NONE;
>  		goto reject;
>  	}
>  	if (!fc_rport_compatible_roles(lport, rdata)) {
>  		FC_RPORT_DBG(rdata, "Received PLOGI for incompatible role\n");
> -		mutex_unlock(&rdata->rp_mutex);
> +		spin_unlock(&rdata->rp_lock);
>  		rjt_data.reason = ELS_RJT_LOGIC;
>  		rjt_data.explan = ELS_EXPL_NONE;
>  		goto reject;
> @@ -1691,7 +1691,7 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
>  	lport->tt.frame_send(lport, fp);
>  	fc_rport_enter_prli(rdata);
>  out:
> -	mutex_unlock(&rdata->rp_mutex);
> +	spin_unlock(&rdata->rp_lock);
>  	fc_frame_free(rx_fp);
>  	return;
>  
> @@ -1910,12 +1910,12 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
>  	mutex_lock(&lport->disc.disc_mutex);
>  	rdata = lport->tt.rport_lookup(lport, sid);
>  	if (rdata) {
> -		mutex_lock(&rdata->rp_mutex);
> +		spin_lock(&rdata->rp_lock);
>  		FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n",
>  			     fc_rport_state(rdata));
>  
>  		fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
> -		mutex_unlock(&rdata->rp_mutex);
> +		spin_unlock(&rdata->rp_lock);
>  	} else
>  		FC_RPORT_ID_DBG(lport, sid,
>  				"Received LOGO from non-logged-in port\n");
> diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
> index 93d14da..7d63d23 100644
> --- a/include/scsi/libfc.h
> +++ b/include/scsi/libfc.h
> @@ -187,7 +187,7 @@ struct fc_rport_libfc_priv {
>   * @major_retries:  The retry count for the entire PLOGI/PRLI state machine
>   * @e_d_tov:        Error detect timeout value (in msec)
>   * @r_a_tov:        Resource allocation timeout value (in msec)
> - * @rp_mutex:       The mutex that protects the remote port
> + * @rp_lock:        The lock that protects the remote port
>   * @retry_work:     Handle for retries
>   * @event_callback: Callback when READY, FAILED or LOGO states complete
>   * @prli_count:     Count of open PRLI sessions in providers
> @@ -207,7 +207,7 @@ struct fc_rport_priv {
>  	unsigned int	            major_retries;
>  	unsigned int	            e_d_tov;
>  	unsigned int	            r_a_tov;
> -	struct mutex                rp_mutex;
> +	spinlock_t		    rp_lock;
>  	struct delayed_work	    retry_work;
>  	enum fc_rport_event         event;
>  	struct fc_rport_operations  *ops;


--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Johannes Thumshirn May 9, 2016, 12:29 p.m. UTC | #2
On Mon, Apr 25, 2016 at 10:01:33AM +0200, Hannes Reinecke wrote:
> We cannot use an embedded mutex in a structure with reference
> counting, as mutex unlock might be delayed, and the waiters
> might then access an already freed memory area.
> So convert it to a spinlock.
> 
> For details cf https://lkml.org/lkml/2015/2/11/245
> 
> Signed-off-by: Hannes Reinecke <hare@suse.de>

Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
Bart Van Assche May 10, 2016, 6:33 p.m. UTC | #3
On 04/25/2016 01:01 AM, Hannes Reinecke wrote:
> We cannot use an embedded mutex in a structure with reference
> counting, as mutex unlock might be delayed, and the waiters
> might then access an already freed memory area.
> So convert it to a spinlock.
 >
 > For details cf https://lkml.org/lkml/2015/2/11/245

Hello Hannes,

Is what you describe a theoretical concern or have you observed any 
issues that could have been caused by the rport mutex? I'm asking this 
because my interpretation of the thread you refer to is different. My 
conclusion is that it is safe to embed a mutex in a structure that uses 
reference counting but that the mutex_unlock() call may trigger a 
spurious wakeup. I think that the conclusion of that thread was that 
glibc and kernel code should tolerate such spurious wakeups.

Bart.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Martin K. Petersen May 11, 2016, 1:48 a.m. UTC | #4
>>>>> "Bart" == Bart Van Assche <bart.vanassche@sandisk.com> writes:

Bart> Is what you describe a theoretical concern or have you observed
Bart> any issues that could have been caused by the rport mutex?

I believe Ewan had some data. Ewan?
Hannes Reinecke May 11, 2016, 5:49 a.m. UTC | #5
On 05/10/2016 08:33 PM, Bart Van Assche wrote:
> On 04/25/2016 01:01 AM, Hannes Reinecke wrote:
>> We cannot use an embedded mutex in a structure with reference
>> counting, as mutex unlock might be delayed, and the waiters
>> might then access an already freed memory area.
>> So convert it to a spinlock.
>>
>> For details cf https://lkml.org/lkml/2015/2/11/245
> 
> Hello Hannes,
> 
> Is what you describe a theoretical concern or have you observed any
> issues that could have been caused by the rport mutex? I'm asking
> this because my interpretation of the thread you refer to is
> different. My conclusion is that it is safe to embed a mutex in a
> structure that uses reference counting but that the mutex_unlock()
> call may trigger a spurious wakeup. I think that the conclusion of
> that thread was that glibc and kernel code should tolerate such
> spurious wakeups.
> 
We have several bugzillas referring to that specific code.
Most notably triggered when removing target ports with and open-fcoe
HBA.

And this patch seems to resolve it.
Read: with this patch the issue doesn't occur anymore.

Cheers,

Hannes
Hannes Reinecke May 11, 2016, 6:07 a.m. UTC | #6
On 05/11/2016 07:49 AM, Hannes Reinecke wrote:
> On 05/10/2016 08:33 PM, Bart Van Assche wrote:
>> On 04/25/2016 01:01 AM, Hannes Reinecke wrote:
>>> We cannot use an embedded mutex in a structure with reference
>>> counting, as mutex unlock might be delayed, and the waiters
>>> might then access an already freed memory area.
>>> So convert it to a spinlock.
>>>
>>> For details cf https://lkml.org/lkml/2015/2/11/245
>>
>> Hello Hannes,
>>
>> Is what you describe a theoretical concern or have you observed any
>> issues that could have been caused by the rport mutex? I'm asking
>> this because my interpretation of the thread you refer to is
>> different. My conclusion is that it is safe to embed a mutex in a
>> structure that uses reference counting but that the mutex_unlock()
>> call may trigger a spurious wakeup. I think that the conclusion of
>> that thread was that glibc and kernel code should tolerate such
>> spurious wakeups.
>>
> We have several bugzillas referring to that specific code.
> Most notably triggered when removing target ports with and open-fcoe
> HBA.
> 
> And this patch seems to resolve it.
> Read: with this patch the issue doesn't occur anymore.
> 
And for the unbelievers here's the crash:

general protection fault: 0000 [#1] SMP
Modules linked in: raw rpcsec_gss_krb5 auth_rpcgss nfsv4 dns_reso
lver nfs lockd sunrpc fscache iscsi_ibft iscsi_boot_sysfs af_packet
xfs ext4 libcrc32c crc16 mbcache jbd2 joydev coretemp kvm_intel kvm
crct10dif_pclmul crc32_pclmul iTCO_wdt iTCO_vendor_support
aesni_intel aes_x86_64 lrw gf128mul glue_helper ablk_helper cryptd
pcspkr lpc_ich mfd_core enic ipmi_si ipmi_msghandler wmi shpchp
acpi_power_meter processor button ac hid_generic usbhid btrfs xor
raid6_pq mgag200 syscopyarea ehci_pci sysfillrect sysimgblt
i2c_algo_bit ehci_hcd drm_kms_helper ttm usbcore drm crc32c_intel
usb_common megaraid_sas dm_service_time sd_mod scsi_dh_rdac
scsi_dh_emc scsi_dh_alua fnic(OEX) libfcoe libfc scsi_transpor
t_fc scsi_tgt dm_multipath scsi_dh sg dm_mod scsi_mod autofs4
Supported: Yes, External
CPU: 0 PID: 16868 Comm: kworker/u25:2 Tainted: G        W  OE X 3
.12.55-52.42.1.10435.0.PTF.962846-default #1
Workqueue: fnic_event_wq fnic_handle_frame [fnic]
task: ffff88080021e140 ti: ffff8807cf076000 task.ti: ffff8807cf076000
RIP: 0010:[<ffffffffa00b2adb>]  [<ffffffffa00b2adb>]
fc_rport_lookup+0x4b/0x70 [libfc]
RSP: 0018:ffff8807cf077d20  EFLAGS: 00010202
RAX: 8500a090df03ff00 RBX: ffff8808560386f0 RCX: ffff880846351400
RDX: 8500a090df040000 RSI: 0000000000610c00 RDI: ffff880856038738
RBP: ffff8808560386f0 R08: 0000000000000001 R09: 0000000000000000
R10: ffff8808402f8e40 R11: ffff880856127600 R12: ffff8807cf077d78
R13: 0000000000610c00 R14: ffff880846351400 R15: 0000000000000000
FS:  0000000000000000(0000) GS:ffff88087fc00000(0000)
knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ff6a540f000 CR3: 0000000846831000 CR4: 00000000001407f0
Stack:
8500a090df040000 ffffffffa00b2e17 ffff8808042fe0c0 ffff8808560386f0
ffff8807cf077d78 ffff880856038750 ffffffffa00a9f81 ffff880800007530
ffff8808402f8e40 ffff880856127600 0000000000000001 ffff880846351408
Call Trace:
  [<ffffffffa00b2e17>] fc_rport_create+0x17/0x1b0 [libfc]
  [<ffffffffa00a9f81>] fc_disc_recv_req+0x261/0x480 [libfc]
  [<ffffffffa00b1008>] fc_lport_recv_els_req+0x68/0x130 [libfc]
  [<ffffffffa00afd5a>] fc_lport_recv_req+0x9a/0xf0 [libfc]
  [<ffffffffa00e8333>] fnic_handle_frame+0x63/0xd0 [fnic]
  [<ffffffff8106fd52>] process_one_work+0x172/0x420
  [<ffffffff810709ca>] worker_thread+0x11a/0x3c0
  [<ffffffff81077344>] kthread+0xb4/0xc0
  [<ffffffff81521318>] ret_from_fork+0x58/0x90
Code: ff ff ff 75 26 eb 39 66 0f 1f 84 00 00 00 00 00 48 8b 80 00
 01 00 00 48 89 04 24 48 8b 14 24 48 39 d7 48 8d 82 00 ff ff ff 74
15 <39> b2 28 ff ff ff 75 dd 48 83 c4 08 c3 0f 1f 84 00 00 00 00 00
RIP  [<ffffffffa00b2adb>] fc_rport_lookup+0x4b/0x70 [libfc]

So no, it's not theoretical.

Cheers,

Hannes
Bart Van Assche May 11, 2016, 2:44 p.m. UTC | #7
On 05/10/16 23:07, Hannes Reinecke wrote:
> On 05/11/2016 07:49 AM, Hannes Reinecke wrote:
> RIP: 0010:[<ffffffffa00b2adb>]  [<ffffffffa00b2adb>]
> fc_rport_lookup+0x4b/0x70 [libfc]
> Call Trace:
>    [<ffffffffa00b2e17>] fc_rport_create+0x17/0x1b0 [libfc]
>    [<ffffffffa00a9f81>] fc_disc_recv_req+0x261/0x480 [libfc]
>    [<ffffffffa00b1008>] fc_lport_recv_els_req+0x68/0x130 [libfc]
>    [<ffffffffa00afd5a>] fc_lport_recv_req+0x9a/0xf0 [libfc]
>    [<ffffffffa00e8333>] fnic_handle_frame+0x63/0xd0 [fnic]
>    [<ffffffff8106fd52>] process_one_work+0x172/0x420
>    [<ffffffff810709ca>] worker_thread+0x11a/0x3c0
>    [<ffffffff81077344>] kthread+0xb4/0xc0
>    [<ffffffff81521318>] ret_from_fork+0x58/0x90

Hello Hannes,

Thanks for sharing this information. fc_disc_recv_req() protects the 
fc_rport_create() call via a mutex (disc_mutex). Since a mutex_lock() 
call may sleep it can trigger the start of an RCU grace period. I think 
this may result in freeing of an rport while fc_rport_lookup() is 
examining it. Have you already considered to add a 
rcu_read_lock()/rcu_read_unlock() pair in fc_rport_lookup()?

Thanks,

Bart.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Hannes Reinecke May 17, 2016, 6:46 a.m. UTC | #8
On 05/11/2016 04:44 PM, Bart Van Assche wrote:
> On 05/10/16 23:07, Hannes Reinecke wrote:
>> On 05/11/2016 07:49 AM, Hannes Reinecke wrote:
>> RIP: 0010:[<ffffffffa00b2adb>]  [<ffffffffa00b2adb>]
>> fc_rport_lookup+0x4b/0x70 [libfc]
>> Call Trace:
>>    [<ffffffffa00b2e17>] fc_rport_create+0x17/0x1b0 [libfc]
>>    [<ffffffffa00a9f81>] fc_disc_recv_req+0x261/0x480 [libfc]
>>    [<ffffffffa00b1008>] fc_lport_recv_els_req+0x68/0x130 [libfc]
>>    [<ffffffffa00afd5a>] fc_lport_recv_req+0x9a/0xf0 [libfc]
>>    [<ffffffffa00e8333>] fnic_handle_frame+0x63/0xd0 [fnic]
>>    [<ffffffff8106fd52>] process_one_work+0x172/0x420
>>    [<ffffffff810709ca>] worker_thread+0x11a/0x3c0
>>    [<ffffffff81077344>] kthread+0xb4/0xc0
>>    [<ffffffff81521318>] ret_from_fork+0x58/0x90
>
> Hello Hannes,
>
> Thanks for sharing this information. fc_disc_recv_req() protects the
> fc_rport_create() call via a mutex (disc_mutex). Since a mutex_lock()
> call may sleep it can trigger the start of an RCU grace period. I think
> this may result in freeing of an rport while fc_rport_lookup() is
> examining it. Have you already considered to add a
> rcu_read_lock()/rcu_read_unlock() pair in fc_rport_lookup()?
>
No, I haven't so far.
This issue is hard to trigger, and I've only got the customer report to 
go by.

Also, when using an rcu_read_lock() here one probably needs to revisit 
the entire locking structure in libfc:
rport list is an RCU-proctected list, so in principle one only needs to 
hold the rport mutex when _assigning_ new rports, and _could_ drop the 
mutex usage for a simple lookup.
But this really needs some more thought and testing, so I haven't 
attempted it.

Also, with your suggestion I would need to take the mutex_lock _and_ 
call rcu_read_lock(), which just looks wrong.
Hence I prefer to use the spin_lock method (which, incidentally, is also 
suggested in the RCU documentation).

Cheers,

Hannes
Bart Van Assche May 17, 2016, 6:04 p.m. UTC | #9
On 05/16/2016 11:46 PM, Hannes Reinecke wrote:
> On 05/11/2016 04:44 PM, Bart Van Assche wrote:
>> On 05/10/16 23:07, Hannes Reinecke wrote:
>>> On 05/11/2016 07:49 AM, Hannes Reinecke wrote:
>>> RIP: 0010:[<ffffffffa00b2adb>]  [<ffffffffa00b2adb>]
>>> fc_rport_lookup+0x4b/0x70 [libfc]
>>> Call Trace:
>>>    [<ffffffffa00b2e17>] fc_rport_create+0x17/0x1b0 [libfc]
>>>    [<ffffffffa00a9f81>] fc_disc_recv_req+0x261/0x480 [libfc]
>>>    [<ffffffffa00b1008>] fc_lport_recv_els_req+0x68/0x130 [libfc]
>>>    [<ffffffffa00afd5a>] fc_lport_recv_req+0x9a/0xf0 [libfc]
>>>    [<ffffffffa00e8333>] fnic_handle_frame+0x63/0xd0 [fnic]
>>>    [<ffffffff8106fd52>] process_one_work+0x172/0x420
>>>    [<ffffffff810709ca>] worker_thread+0x11a/0x3c0
>>>    [<ffffffff81077344>] kthread+0xb4/0xc0
>>>    [<ffffffff81521318>] ret_from_fork+0x58/0x90
>>
>> Hello Hannes,
>>
>> Thanks for sharing this information. fc_disc_recv_req() protects the
>> fc_rport_create() call via a mutex (disc_mutex). Since a mutex_lock()
>> call may sleep it can trigger the start of an RCU grace period. I think
>> this may result in freeing of an rport while fc_rport_lookup() is
>> examining it. Have you already considered to add a
>> rcu_read_lock()/rcu_read_unlock() pair in fc_rport_lookup()?
>>
> No, I haven't so far.
> This issue is hard to trigger, and I've only got the customer report to
> go by.
>
> Also, when using an rcu_read_lock() here one probably needs to revisit
> the entire locking structure in libfc:
> rport list is an RCU-proctected list, so in principle one only needs to
> hold the rport mutex when _assigning_ new rports, and _could_ drop the
> mutex usage for a simple lookup.
> But this really needs some more thought and testing, so I haven't
> attempted it.
>
> Also, with your suggestion I would need to take the mutex_lock _and_
> call rcu_read_lock(), which just looks wrong.
> Hence I prefer to use the spin_lock method (which, incidentally, is also
> suggested in the RCU documentation).

Hello Hannes,

I think the following section from Documentation/RCU/checklist.txt 
applies to the code in fc_rport_lookup():

        "Do the RCU read-side critical sections make proper use of
	rcu_read_lock() and friends?  These primitives are needed
	to prevent grace periods from ending prematurely, which
	could result in data being unceremoniously freed out from
	under your read-side code, which can greatly increase the
	actuarial risk of your kernel."

Thanks,

Bart.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 589ff9a..4520b5a 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -136,7 +136,7 @@  static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
 	rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
 
 	kref_init(&rdata->kref);
-	mutex_init(&rdata->rp_mutex);
+	spin_lock_init(&rdata->rp_lock);
 	rdata->local_port = lport;
 	rdata->rp_state = RPORT_ST_INIT;
 	rdata->event = RPORT_EV_NONE;
@@ -251,7 +251,7 @@  static void fc_rport_work(struct work_struct *work)
 	struct fc4_prov *prov;
 	u8 type;
 
-	mutex_lock(&rdata->rp_mutex);
+	spin_lock(&rdata->rp_lock);
 	event = rdata->event;
 	rport_ops = rdata->ops;
 	rport = rdata->rport;
@@ -264,7 +264,7 @@  static void fc_rport_work(struct work_struct *work)
 		rdata->event = RPORT_EV_NONE;
 		rdata->major_retries = 0;
 		kref_get(&rdata->kref);
-		mutex_unlock(&rdata->rp_mutex);
+		spin_unlock(&rdata->rp_lock);
 
 		if (!rport)
 			rport = fc_remote_port_add(lport->host, 0, &ids);
@@ -274,7 +274,7 @@  static void fc_rport_work(struct work_struct *work)
 			kref_put(&rdata->kref, lport->tt.rport_destroy);
 			return;
 		}
-		mutex_lock(&rdata->rp_mutex);
+		spin_lock(&rdata->rp_lock);
 		if (rdata->rport)
 			FC_RPORT_DBG(rdata, "rport already allocated\n");
 		rdata->rport = rport;
@@ -287,7 +287,7 @@  static void fc_rport_work(struct work_struct *work)
 		rpriv->flags = rdata->flags;
 		rpriv->e_d_tov = rdata->e_d_tov;
 		rpriv->r_a_tov = rdata->r_a_tov;
-		mutex_unlock(&rdata->rp_mutex);
+		spin_unlock(&rdata->rp_lock);
 
 		if (rport_ops && rport_ops->event_callback) {
 			FC_RPORT_DBG(rdata, "callback ev %d\n", event);
@@ -313,7 +313,7 @@  static void fc_rport_work(struct work_struct *work)
 			mutex_unlock(&fc_prov_mutex);
 		}
 		port_id = rdata->ids.port_id;
-		mutex_unlock(&rdata->rp_mutex);
+		spin_unlock(&rdata->rp_lock);
 
 		if (rport_ops && rport_ops->event_callback) {
 			FC_RPORT_DBG(rdata, "callback ev %d\n", event);
@@ -334,18 +334,18 @@  static void fc_rport_work(struct work_struct *work)
 		if (rport) {
 			rpriv = rport->dd_data;
 			rpriv->rp_state = RPORT_ST_DELETE;
-			mutex_lock(&rdata->rp_mutex);
+			spin_lock(&rdata->rp_lock);
 			rdata->rport = NULL;
-			mutex_unlock(&rdata->rp_mutex);
+			spin_unlock(&rdata->rp_lock);
 			fc_remote_port_delete(rport);
 		}
 
 		mutex_lock(&lport->disc.disc_mutex);
-		mutex_lock(&rdata->rp_mutex);
+		spin_lock(&rdata->rp_lock);
 		if (rdata->rp_state == RPORT_ST_DELETE) {
 			if (port_id == FC_FID_DIR_SERV) {
 				rdata->event = RPORT_EV_NONE;
-				mutex_unlock(&rdata->rp_mutex);
+				spin_unlock(&rdata->rp_lock);
 				kref_put(&rdata->kref, lport->tt.rport_destroy);
 			} else if ((rdata->flags & FC_RP_STARTED) &&
 				   rdata->major_retries <
@@ -354,11 +354,11 @@  static void fc_rport_work(struct work_struct *work)
 				rdata->event = RPORT_EV_NONE;
 				FC_RPORT_DBG(rdata, "work restart\n");
 				fc_rport_enter_flogi(rdata);
-				mutex_unlock(&rdata->rp_mutex);
+				spin_unlock(&rdata->rp_lock);
 			} else {
 				FC_RPORT_DBG(rdata, "work delete\n");
 				list_del_rcu(&rdata->peers);
-				mutex_unlock(&rdata->rp_mutex);
+				spin_unlock(&rdata->rp_lock);
 				kref_put(&rdata->kref, lport->tt.rport_destroy);
 			}
 		} else {
@@ -368,13 +368,13 @@  static void fc_rport_work(struct work_struct *work)
 			rdata->event = RPORT_EV_NONE;
 			if (rdata->rp_state == RPORT_ST_READY)
 				fc_rport_enter_ready(rdata);
-			mutex_unlock(&rdata->rp_mutex);
+			spin_unlock(&rdata->rp_lock);
 		}
 		mutex_unlock(&lport->disc.disc_mutex);
 		break;
 
 	default:
-		mutex_unlock(&rdata->rp_mutex);
+		spin_unlock(&rdata->rp_lock);
 		break;
 	}
 }
@@ -393,7 +393,7 @@  static void fc_rport_work(struct work_struct *work)
  */
 static int fc_rport_login(struct fc_rport_priv *rdata)
 {
-	mutex_lock(&rdata->rp_mutex);
+	spin_lock(&rdata->rp_lock);
 
 	rdata->flags |= FC_RP_STARTED;
 	switch (rdata->rp_state) {
@@ -409,7 +409,7 @@  static int fc_rport_login(struct fc_rport_priv *rdata)
 		fc_rport_enter_flogi(rdata);
 		break;
 	}
-	mutex_unlock(&rdata->rp_mutex);
+	spin_unlock(&rdata->rp_lock);
 
 	return 0;
 }
@@ -453,7 +453,7 @@  static void fc_rport_enter_delete(struct fc_rport_priv *rdata,
  */
 static int fc_rport_logoff(struct fc_rport_priv *rdata)
 {
-	mutex_lock(&rdata->rp_mutex);
+	spin_lock(&rdata->rp_lock);
 
 	FC_RPORT_DBG(rdata, "Remove port\n");
 
@@ -470,7 +470,7 @@  static int fc_rport_logoff(struct fc_rport_priv *rdata)
 	 */
 	fc_rport_enter_delete(rdata, RPORT_EV_STOP);
 out:
-	mutex_unlock(&rdata->rp_mutex);
+	spin_unlock(&rdata->rp_lock);
 	return 0;
 }
 
@@ -505,7 +505,7 @@  static void fc_rport_timeout(struct work_struct *work)
 	struct fc_rport_priv *rdata =
 		container_of(work, struct fc_rport_priv, retry_work.work);
 
-	mutex_lock(&rdata->rp_mutex);
+	spin_lock(&rdata->rp_lock);
 
 	switch (rdata->rp_state) {
 	case RPORT_ST_FLOGI:
@@ -530,7 +530,7 @@  static void fc_rport_timeout(struct work_struct *work)
 		break;
 	}
 
-	mutex_unlock(&rdata->rp_mutex);
+	spin_unlock(&rdata->rp_lock);
 }
 
 /**
@@ -666,7 +666,7 @@  static void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 	if (fp == ERR_PTR(-FC_EX_CLOSED))
 		goto put;
 
-	mutex_lock(&rdata->rp_mutex);
+	spin_lock(&rdata->rp_lock);
 
 	if (rdata->rp_state != RPORT_ST_FLOGI) {
 		FC_RPORT_DBG(rdata, "Received a FLOGI response, but in state "
@@ -700,7 +700,7 @@  static void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 out:
 	fc_frame_free(fp);
 err:
-	mutex_unlock(&rdata->rp_mutex);
+	spin_unlock(&rdata->rp_lock);
 put:
 	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
 	return;
@@ -783,7 +783,7 @@  static void fc_rport_recv_flogi_req(struct fc_lport *lport,
 		rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR;
 		goto reject;
 	}
-	mutex_lock(&rdata->rp_mutex);
+	spin_lock(&rdata->rp_lock);
 
 	FC_RPORT_DBG(rdata, "Received FLOGI in %s state\n",
 		     fc_rport_state(rdata));
@@ -805,7 +805,7 @@  static void fc_rport_recv_flogi_req(struct fc_lport *lport,
 		if (lport->point_to_multipoint)
 			break;
 	case RPORT_ST_DELETE:
-		mutex_unlock(&rdata->rp_mutex);
+		spin_unlock(&rdata->rp_lock);
 		rjt_data.reason = ELS_RJT_FIP;
 		rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR;
 		goto reject;
@@ -822,13 +822,13 @@  static void fc_rport_recv_flogi_req(struct fc_lport *lport,
 		 * This queues work to be sure exchanges are reset.
 		 */
 		fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
-		mutex_unlock(&rdata->rp_mutex);
+		spin_unlock(&rdata->rp_lock);
 		rjt_data.reason = ELS_RJT_BUSY;
 		rjt_data.explan = ELS_EXPL_NONE;
 		goto reject;
 	}
 	if (fc_rport_login_complete(rdata, fp)) {
-		mutex_unlock(&rdata->rp_mutex);
+		spin_unlock(&rdata->rp_lock);
 		rjt_data.reason = ELS_RJT_LOGIC;
 		rjt_data.explan = ELS_EXPL_NONE;
 		goto reject;
@@ -850,7 +850,7 @@  static void fc_rport_recv_flogi_req(struct fc_lport *lport,
 	else
 		fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
 out:
-	mutex_unlock(&rdata->rp_mutex);
+	spin_unlock(&rdata->rp_lock);
 	mutex_unlock(&disc->disc_mutex);
 	fc_frame_free(rx_fp);
 	return;
@@ -881,7 +881,7 @@  static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 	u16 cssp_seq;
 	u8 op;
 
-	mutex_lock(&rdata->rp_mutex);
+	spin_lock(&rdata->rp_lock);
 
 	FC_RPORT_DBG(rdata, "Received a PLOGI %s\n", fc_els_resp_type(fp));
 
@@ -922,7 +922,7 @@  static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 out:
 	fc_frame_free(fp);
 err:
-	mutex_unlock(&rdata->rp_mutex);
+	spin_unlock(&rdata->rp_lock);
 	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
 }
 
@@ -1005,7 +1005,7 @@  static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 	u8 op;
 	u8 resp_code = 0;
 
-	mutex_lock(&rdata->rp_mutex);
+	spin_lock(&rdata->rp_lock);
 
 	FC_RPORT_DBG(rdata, "Received a PRLI %s\n", fc_els_resp_type(fp));
 
@@ -1075,7 +1075,7 @@  static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 out:
 	fc_frame_free(fp);
 err:
-	mutex_unlock(&rdata->rp_mutex);
+	spin_unlock(&rdata->rp_lock);
 	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
 }
 
@@ -1153,7 +1153,7 @@  static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
 	struct fc_rport_priv *rdata = rdata_arg;
 	u8 op;
 
-	mutex_lock(&rdata->rp_mutex);
+	spin_lock(&rdata->rp_lock);
 
 	FC_RPORT_DBG(rdata, "Received a RTV %s\n", fc_els_resp_type(fp));
 
@@ -1197,7 +1197,7 @@  static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
 out:
 	fc_frame_free(fp);
 err:
-	mutex_unlock(&rdata->rp_mutex);
+	spin_unlock(&rdata->rp_lock);
 	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
 }
 
@@ -1289,7 +1289,7 @@  static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
 	struct fc_els_adisc *adisc;
 	u8 op;
 
-	mutex_lock(&rdata->rp_mutex);
+	spin_lock(&rdata->rp_lock);
 
 	FC_RPORT_DBG(rdata, "Received a ADISC response\n");
 
@@ -1326,7 +1326,7 @@  static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
 out:
 	fc_frame_free(fp);
 err:
-	mutex_unlock(&rdata->rp_mutex);
+	spin_unlock(&rdata->rp_lock);
 	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
 }
 
@@ -1483,7 +1483,7 @@  static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
 		mutex_unlock(&lport->disc.disc_mutex);
 		goto reject;
 	}
-	mutex_lock(&rdata->rp_mutex);
+	spin_lock(&rdata->rp_lock);
 	mutex_unlock(&lport->disc.disc_mutex);
 
 	switch (rdata->rp_state) {
@@ -1493,7 +1493,7 @@  static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
 	case RPORT_ST_ADISC:
 		break;
 	default:
-		mutex_unlock(&rdata->rp_mutex);
+		spin_unlock(&rdata->rp_lock);
 		goto reject;
 	}
 
@@ -1523,7 +1523,7 @@  static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
 		break;
 	}
 
-	mutex_unlock(&rdata->rp_mutex);
+	spin_unlock(&rdata->rp_lock);
 	return;
 
 reject:
@@ -1616,7 +1616,7 @@  static void fc_rport_recv_plogi_req(struct fc_lport *lport,
 		goto reject;
 	}
 
-	mutex_lock(&rdata->rp_mutex);
+	spin_lock(&rdata->rp_lock);
 	mutex_unlock(&disc->disc_mutex);
 
 	rdata->ids.port_name = get_unaligned_be64(&pl->fl_wwpn);
@@ -1643,7 +1643,7 @@  static void fc_rport_recv_plogi_req(struct fc_lport *lport,
 	case RPORT_ST_PLOGI:
 		FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI state\n");
 		if (rdata->ids.port_name < lport->wwpn) {
-			mutex_unlock(&rdata->rp_mutex);
+			spin_unlock(&rdata->rp_lock);
 			rjt_data.reason = ELS_RJT_INPROG;
 			rjt_data.explan = ELS_EXPL_NONE;
 			goto reject;
@@ -1661,14 +1661,14 @@  static void fc_rport_recv_plogi_req(struct fc_lport *lport,
 	case RPORT_ST_DELETE:
 		FC_RPORT_DBG(rdata, "Received PLOGI in state %s - send busy\n",
 			     fc_rport_state(rdata));
-		mutex_unlock(&rdata->rp_mutex);
+		spin_unlock(&rdata->rp_lock);
 		rjt_data.reason = ELS_RJT_BUSY;
 		rjt_data.explan = ELS_EXPL_NONE;
 		goto reject;
 	}
 	if (!fc_rport_compatible_roles(lport, rdata)) {
 		FC_RPORT_DBG(rdata, "Received PLOGI for incompatible role\n");
-		mutex_unlock(&rdata->rp_mutex);
+		spin_unlock(&rdata->rp_lock);
 		rjt_data.reason = ELS_RJT_LOGIC;
 		rjt_data.explan = ELS_EXPL_NONE;
 		goto reject;
@@ -1691,7 +1691,7 @@  static void fc_rport_recv_plogi_req(struct fc_lport *lport,
 	lport->tt.frame_send(lport, fp);
 	fc_rport_enter_prli(rdata);
 out:
-	mutex_unlock(&rdata->rp_mutex);
+	spin_unlock(&rdata->rp_lock);
 	fc_frame_free(rx_fp);
 	return;
 
@@ -1910,12 +1910,12 @@  static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
 	mutex_lock(&lport->disc.disc_mutex);
 	rdata = lport->tt.rport_lookup(lport, sid);
 	if (rdata) {
-		mutex_lock(&rdata->rp_mutex);
+		spin_lock(&rdata->rp_lock);
 		FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n",
 			     fc_rport_state(rdata));
 
 		fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
-		mutex_unlock(&rdata->rp_mutex);
+		spin_unlock(&rdata->rp_lock);
 	} else
 		FC_RPORT_ID_DBG(lport, sid,
 				"Received LOGO from non-logged-in port\n");
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 93d14da..7d63d23 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -187,7 +187,7 @@  struct fc_rport_libfc_priv {
  * @major_retries:  The retry count for the entire PLOGI/PRLI state machine
  * @e_d_tov:        Error detect timeout value (in msec)
  * @r_a_tov:        Resource allocation timeout value (in msec)
- * @rp_mutex:       The mutex that protects the remote port
+ * @rp_lock:        The lock that protects the remote port
  * @retry_work:     Handle for retries
  * @event_callback: Callback when READY, FAILED or LOGO states complete
  * @prli_count:     Count of open PRLI sessions in providers
@@ -207,7 +207,7 @@  struct fc_rport_priv {
 	unsigned int	            major_retries;
 	unsigned int	            e_d_tov;
 	unsigned int	            r_a_tov;
-	struct mutex                rp_mutex;
+	spinlock_t		    rp_lock;
 	struct delayed_work	    retry_work;
 	enum fc_rport_event         event;
 	struct fc_rport_operations  *ops;