[SCSI] libfc: fix a circular locking warning during sending RRQ
authorVasu Dev <vasu.dev@intel.com>
Wed, 29 Jul 2009 00:33:37 +0000 (17:33 -0700)
committerJames Bottomley <James.Bottomley@HansenPartnership.com>
Thu, 30 Jul 2009 13:50:02 +0000 (08:50 -0500)
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.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
drivers/scsi/libfc/fc_exch.c

index 2bc22be..145ab9b 100644 (file)
@@ -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);
 }