libfc: fix a circular locking warning during sending RRQ
Subsystems (2)
PATCH 1/1 libfc: fix a circular locking warning during sending RRQ
From: Vasu Dev <[email protected]>
Currently the fc_exch_rrq is called with fc_exch's ex_lock held. The fc_exch_rrq allocates new exch and that requires taking ex_lock again after EM lock. This locking order causes warning, see more details on this warning at :-
http://www.open-fcoe.org/pipermail/devel/2009-July/003251.html
This patch fixes this by dropping the ex_lock before calling fc_exch_rrq().
The fc_exch_rrq needs to grab ex_lock lock again to schedule RRQ retry and in the meanwhile fc_exch_reset could occur before ex_lock is grabbed inside fc_exch_rrq. So to handle this case, this patch adds additional check to detect fc_exch_reset after ex_lock acquired and in case the fc_exch_reset occurred then abandons the RRQ retry and releases the exch.
---
drivers/scsi/libfc/fc_exch.c | 23 +++++++++++++++-------- 1 files changed, 15 insertions(+), 8 deletions(-)
drivers/scsi/libfc/fc_exch.c +15 -8
@@ -415,9 +415,9 @@ static void fc_exch_timeout(struct work_struct *work) e_stat = ep->esb_stat;if (e_stat &ESB_ST_COMPLETE ) { ep->esb_stat = e_stat & ~ESB_ST_REC_QUAL ; +spin_unlock_bh (&ep->ex_lock);if (e_stat &ESB_ST_REC_QUAL )fc_exch_rrq (ep); -spin_unlock_bh (&ep->ex_lock); goto done; }else { resp = ep->resp;
@@ -1624,14 +1624,14 @@ static void fc_exch_rrq(struct fc_exch *ep)struct fc_lport *lp;struct fc_els_rrq *rrq;struct fc_frame *fp; -struct fc_seq *rrq_sp;u32 did; lp = ep->lp; fp =fc_frame_alloc (lp,sizeof (*rrq));if (!fp) -return ; + goto retry; + rrq =fc_frame_payload_get (fp,sizeof (*rrq));memset (rrq,0 ,sizeof (*rrq)); rrq->rrq_cmd =ELS_RRQ ;
@@ -1647,13 +1647,20 @@ static void fc_exch_rrq(struct fc_exch *ep)fc_host_port_id (lp->host),FC_TYPE_ELS , FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); - rrq_sp =fc_exch_seq_send (lp, fp, fc_exch_rrq_resp,NULL , ep, - lp->e_d_tov); -if (!rrq_sp) { - ep->esb_stat |=ESB_ST_REC_QUAL ; -fc_exch_timer_set_locked (ep, ep->r_a_tov); +if (fc_exch_seq_send (lp, fp, fc_exch_rrq_resp,NULL , ep, lp->e_d_tov)) +return ; + +retry: +spin_lock_bh (&ep->ex_lock); +if (ep->state & (FC_EX_RST_CLEANUP |FC_EX_DONE )) { +spin_unlock_bh (&ep->ex_lock); +/* drop hold for rec qual */ +fc_exch_release (ep);return ; } + ep->esb_stat |=ESB_ST_REC_QUAL ; +fc_exch_timer_set_locked (ep, ep->r_a_tov); +spin_unlock_bh (&ep->ex_lock); }