Updating exch ref count based on timer_pending check can mess
up exch ref count if timer fires just after this check. Instead count
on del/mod_timer() return values to update exch ref count.

Also fixed a very unlikely but still possible race in fc_exch_release().

Signed-off-by: Vasu Dev <[EMAIL PROTECTED]>
---

 drivers/scsi/libfc/fc_exch.c |   46 +++++++++++++++++++++++-------------------
 1 files changed, 25 insertions(+), 21 deletions(-)


diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 4e552c0..342af7e 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -288,11 +288,21 @@ static void fc_exch_release(struct fc_exch *ep)
 
        if (atomic_dec_and_test(&ep->ex_refcnt)) {
                WARN_ON(!ep->esb_stat & ESB_ST_COMPLETE);
-               del_timer(&ep->ex_timer);
+               WARN_ON(timer_pending(&ep->ex_timer));
                mp = ep->em;
+               spin_lock_bh(&mp->em_lock);
+               /*
+                * ensure ep->ex_refcnt is still zero
+                * if not then skip freeing exchange
+                * since some new incoming frame took
+                * hold of this, which is unlikely.
+                */
+               if (atomic_read(&ep->ex_refcnt)) {
+                       spin_unlock_bh(&mp->em_lock);
+                       return;
+               }
                if (ep->lp->tt.exch_put)
                        ep->lp->tt.exch_put(ep->lp, mp, ep->xid);
-               spin_lock_bh(&mp->em_lock);
                WARN_ON(mp->total_exches <= 0);
                mp->total_exches--;
                mp->exches[ep->xid - mp->min_xid] = NULL;
@@ -305,12 +315,11 @@ static void fc_exch_release(struct fc_exch *ep)
 /*
  * Internal version of fc_exch_timer_set - used with lock held.
  */
-static void fc_exch_timer_set_locked(struct fc_exch *ep,
-                                    unsigned int timer_msec)
+static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
+                                           unsigned int timer_msec)
 {
-       if (!timer_pending(&ep->ex_timer))
+       if (!mod_timer(&ep->ex_timer, jiffies + msecs_to_jiffies(timer_msec)))
                fc_exch_hold(ep);               /* hold for timer */
-       mod_timer(&ep->ex_timer, jiffies + msecs_to_jiffies(timer_msec));
 }
 
 /*
@@ -559,13 +568,11 @@ static void fc_exch_complete_locked(struct fc_exch *ep)
        if (unlikely(ep->esb_stat & ESB_ST_REC_QUAL)) {
                fc_exch_timer_set_locked(ep, 10);
        } else {
-               if (timer_pending(&ep->ex_timer)) {
-                       del_timer(&ep->ex_timer);
-                       /*
-                        * drop hold for timer
-                        */
+               /*
+                * delete pending timer and drop hold for timer
+                */
+               if (del_timer(&ep->ex_timer))
                        atomic_dec(&ep->ex_refcnt);
-               }
                atomic_dec(&ep->ex_refcnt);
        }
 }
@@ -1410,10 +1417,9 @@ static void fc_exch_reset(struct fc_exch *ep)
        if (ep->esb_stat & ESB_ST_COMPLETE)
                resp = NULL;
        arg = ep->resp_arg;
-       if (timer_pending(&ep->ex_timer)) {
-               del_timer(&ep->ex_timer);
+       if (del_timer(&ep->ex_timer))
                atomic_dec(&ep->ex_refcnt);     /* drop hold for timer */
-       }
+
        sp = &ep->seq;
        ep->esb_stat |= ESB_ST_COMPLETE;
        spin_unlock_bh(&ep->ex_lock);
@@ -1635,10 +1641,9 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct 
fc_frame *fp)
                ep->esb_stat &= ~ESB_ST_REC_QUAL;
                atomic_dec(&ep->ex_refcnt);     /* drop hold for rec qual */
        }
-       if ((ep->esb_stat & ESB_ST_COMPLETE) && timer_pending(&ep->ex_timer)) {
-               del_timer(&ep->ex_timer);
+       if ((ep->esb_stat & ESB_ST_COMPLETE) && (del_timer(&ep->ex_timer)))
                atomic_dec(&ep->ex_refcnt);     /* drop hold for timer */
-       }
+
        spin_unlock_bh(&ep->ex_lock);
 
        /*
@@ -1728,10 +1733,9 @@ void fc_exch_done(struct fc_seq *sp)
        spin_lock_bh(&ep->ex_lock);
        ep->esb_stat |= ESB_ST_COMPLETE;
        ep->resp = NULL;
-       if (timer_pending(&ep->ex_timer)) {
-               del_timer(&ep->ex_timer);
+       if (del_timer(&ep->ex_timer))
                atomic_dec(&ep->ex_refcnt);     /* drop hold for timer */
-       }
+
        spin_unlock_bh(&ep->ex_lock);
        fc_exch_release(fc_seq_exch(sp));
 }

_______________________________________________
devel mailing list
[email protected]
http://www.open-fcoe.org/mailman/listinfo/devel

Reply via email to