]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/scsi/libfc/fc_exch.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / scsi / libfc / fc_exch.c
index ec2a1aec2350279bdd0b177b321f5694ff9a948b..d21367d3305fd6de6bd68f0505618ff97b6b927b 100644 (file)
@@ -67,6 +67,11 @@ struct workqueue_struct *fc_exch_workqueue;
 struct fc_exch_pool {
        u16              next_index;
        u16              total_exches;
+
+       /* two cache of free slot in exch array */
+       u16              left;
+       u16              right;
+
        spinlock_t       lock;
        struct list_head ex_list;
 };
@@ -108,7 +113,6 @@ struct fc_exch_mgr {
                atomic_t non_bls_resp;
        } stats;
 };
-#define        fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
 
 /**
  * struct fc_exch_mgr_anchor - primary structure for list of EMs
@@ -397,13 +401,23 @@ static inline void fc_exch_ptr_set(struct fc_exch_pool *pool, u16 index,
 static void fc_exch_delete(struct fc_exch *ep)
 {
        struct fc_exch_pool *pool;
+       u16 index;
 
        pool = ep->pool;
        spin_lock_bh(&pool->lock);
        WARN_ON(pool->total_exches <= 0);
        pool->total_exches--;
-       fc_exch_ptr_set(pool, (ep->xid - ep->em->min_xid) >> fc_cpu_order,
-                       NULL);
+
+       /* update cache of free slot */
+       index = (ep->xid - ep->em->min_xid) >> fc_cpu_order;
+       if (pool->left == FC_XID_UNKNOWN)
+               pool->left = index;
+       else if (pool->right == FC_XID_UNKNOWN)
+               pool->right = index;
+       else
+               pool->next_index = index;
+
+       fc_exch_ptr_set(pool, index, NULL);
        list_del(&ep->ex_list);
        spin_unlock_bh(&pool->lock);
        fc_exch_release(ep);    /* drop hold for exch in mp */
@@ -636,10 +650,13 @@ static void fc_exch_timeout(struct work_struct *work)
                if (e_stat & ESB_ST_ABNORMAL)
                        rc = fc_exch_done_locked(ep);
                spin_unlock_bh(&ep->ex_lock);
-               if (!rc)
-                       fc_exch_delete(ep);
                if (resp)
                        resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg);
+               if (!rc) {
+                       /* delete the exchange if it's already being aborted */
+                       fc_exch_delete(ep);
+                       return;
+               }
                fc_seq_exch_abort(sp, 2 * ep->r_a_tov);
                goto done;
        }
@@ -679,6 +696,19 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,
        pool = per_cpu_ptr(mp->pool, cpu);
        spin_lock_bh(&pool->lock);
        put_cpu();
+
+       /* peek cache of free slot */
+       if (pool->left != FC_XID_UNKNOWN) {
+               index = pool->left;
+               pool->left = FC_XID_UNKNOWN;
+               goto hit;
+       }
+       if (pool->right != FC_XID_UNKNOWN) {
+               index = pool->right;
+               pool->right = FC_XID_UNKNOWN;
+               goto hit;
+       }
+
        index = pool->next_index;
        /* allocate new exch from pool */
        while (fc_exch_ptr_get(pool, index)) {
@@ -687,7 +717,7 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,
                        goto err;
        }
        pool->next_index = index == mp->pool_max_index ? 0 : index + 1;
-
+hit:
        fc_exch_hold(ep);       /* hold for exch in mp */
        spin_lock_init(&ep->ex_lock);
        /*
@@ -1247,7 +1277,7 @@ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
 
        list_for_each_entry(ema, &lport->ema_list, ema_list)
                if ((!ema->match || ema->match(fp)) &&
-                   fc_seq_lookup_recip(lport, ema->mp, fp) != FC_RJT_NONE)
+                   fc_seq_lookup_recip(lport, ema->mp, fp) == FC_RJT_NONE)
                        break;
        return fr_seq(fp);
 }
@@ -1343,7 +1373,7 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
        }
        if (ep->esb_stat & ESB_ST_COMPLETE) {
                atomic_inc(&mp->stats.xid_not_found);
-               goto out;
+               goto rel;
        }
        if (ep->rxid == FC_XID_UNKNOWN)
                ep->rxid = ntohs(fh->fh_rx_id);
@@ -2181,6 +2211,8 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
                goto free_mempool;
        for_each_possible_cpu(cpu) {
                pool = per_cpu_ptr(mp->pool, cpu);
+               pool->left = FC_XID_UNKNOWN;
+               pool->right = FC_XID_UNKNOWN;
                spin_lock_init(&pool->lock);
                INIT_LIST_HEAD(&pool->ex_list);
        }