[SCSI] Merge scsi-misc-2.6 into scsi-rc-fixes-2.6
[safe/jmp/linux-2.6] / drivers / scsi / libfc / fc_exch.c
index 2bc22be..104e0fb 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include <linux/timer.h>
-#include <linux/gfp.h>
+#include <linux/slab.h>
 #include <linux/err.h>
 
 #include <scsi/fc/fc_fc2.h>
 #include <scsi/libfc.h>
 #include <scsi/fc_encode.h>
 
-static struct kmem_cache *fc_em_cachep;        /* cache for exchanges */
+#include "fc_libfc.h"
+
+u16    fc_cpu_mask;            /* cpu mask for possible cpus */
+EXPORT_SYMBOL(fc_cpu_mask);
+static u16     fc_cpu_order;   /* 2's power to represent total possible cpus */
+static struct kmem_cache *fc_em_cachep;               /* cache for exchanges */
+struct workqueue_struct *fc_exch_workqueue;
 
 /*
  * Structure and function definitions for managing Fibre Channel Exchanges
@@ -47,25 +53,46 @@ static struct kmem_cache *fc_em_cachep;        /* cache for exchanges */
  * fc_seq holds the state for an individual sequence.
  */
 
-/*
- * Exchange manager.
+/**
+ * struct fc_exch_pool - Per cpu exchange pool
+ * @next_index:          Next possible free exchange index
+ * @total_exches: Total allocated exchanges
+ * @lock:        Exch pool lock
+ * @ex_list:     List of exchanges
+ *
+ * This structure manages per cpu exchanges in array of exchange pointers.
+ * This array is allocated followed by struct fc_exch_pool memory for
+ * assigned range of exchanges to per cpu pool.
+ */
+struct fc_exch_pool {
+       u16              next_index;
+       u16              total_exches;
+       spinlock_t       lock;
+       struct list_head ex_list;
+};
+
+/**
+ * struct fc_exch_mgr - The Exchange Manager (EM).
+ * @class:         Default class for new sequences
+ * @kref:          Reference counter
+ * @min_xid:       Minimum exchange ID
+ * @max_xid:       Maximum exchange ID
+ * @ep_pool:       Reserved exchange pointers
+ * @pool_max_index: Max exch array index in exch pool
+ * @pool:          Per cpu exch pool
+ * @stats:         Statistics structure
  *
  * This structure is the center for creating exchanges and sequences.
  * It manages the allocation of exchange IDs.
  */
 struct fc_exch_mgr {
-       enum fc_class   class;          /* default class for sequences */
-       spinlock_t      em_lock;        /* exchange manager lock,
-                                          must be taken before ex_lock */
-       u16             last_xid;       /* last allocated exchange ID */
-       u16             min_xid;        /* min exchange ID */
-       u16             max_xid;        /* max exchange ID */
-       u16             max_read;       /* max exchange ID for read */
-       u16             last_read;      /* last xid allocated for read */
-       u32     total_exches;           /* total allocated exchanges */
-       struct list_head        ex_list;        /* allocated exchanges list */
-       struct fc_lport *lp;            /* fc device instance */
-       mempool_t       *ep_pool;       /* reserve ep's */
+       enum fc_class   class;
+       struct kref     kref;
+       u16             min_xid;
+       u16             max_xid;
+       mempool_t       *ep_pool;
+       u16             pool_max_index;
+       struct fc_exch_pool *pool;
 
        /*
         * currently exchange mgr stats are updated but not used.
@@ -80,17 +107,33 @@ struct fc_exch_mgr {
                atomic_t seq_not_found;
                atomic_t non_bls_resp;
        } stats;
-       struct fc_exch **exches;        /* for exch pointers indexed by xid */
 };
 #define        fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
 
+/**
+ * struct fc_exch_mgr_anchor - primary structure for list of EMs
+ * @ema_list: Exchange Manager Anchor list
+ * @mp:              Exchange Manager associated with this anchor
+ * @match:    Routine to determine if this anchor's EM should be used
+ *
+ * When walking the list of anchors the match routine will be called
+ * for each anchor to determine if that EM should be used. The last
+ * anchor in the list will always match to handle any exchanges not
+ * handled by other EMs. The non-default EMs would be added to the
+ * anchor list by HW that provides FCoE offloads.
+ */
+struct fc_exch_mgr_anchor {
+       struct list_head ema_list;
+       struct fc_exch_mgr *mp;
+       bool (*match)(struct fc_frame *);
+};
+
 static void fc_exch_rrq(struct fc_exch *);
 static void fc_seq_ls_acc(struct fc_seq *);
 static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason,
                          enum fc_els_rjt_explan);
 static void fc_exch_els_rec(struct fc_seq *, struct fc_frame *);
 static void fc_exch_els_rrq(struct fc_seq *, struct fc_frame *);
-static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp);
 
 /*
  * Internal implementation notes.
@@ -167,8 +210,8 @@ static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp);
  * sequence allocation and deallocation must be locked.
  *  - exchange refcnt can be done atomicly without locks.
  *  - sequence allocation must be locked by exch lock.
- *  - If the em_lock and ex_lock must be taken at the same time, then the
- *    em_lock must be taken before the ex_lock.
+ *  - If the EM pool lock and ex_lock must be taken at the same time, then the
+ *    EM pool lock must be taken before the ex_lock.
  */
 
 /*
@@ -178,6 +221,15 @@ static char *fc_exch_rctl_names[] = FC_RCTL_NAMES_INIT;
 
 #define FC_TABLE_SIZE(x)   (sizeof(x) / sizeof(x[0]))
 
+/**
+ * fc_exch_name_lookup() - Lookup name by opcode
+ * @op:               Opcode to be looked up
+ * @table:     Opcode/name table
+ * @max_index: Index not to be exceeded
+ *
+ * This routine is used to determine a human-readable string identifying
+ * a R_CTL opcode.
+ */
 static inline const char *fc_exch_name_lookup(unsigned int op, char **table,
                                              unsigned int max_index)
 {
@@ -190,25 +242,34 @@ static inline const char *fc_exch_name_lookup(unsigned int op, char **table,
        return name;
 }
 
+/**
+ * fc_exch_rctl_name() - Wrapper routine for fc_exch_name_lookup()
+ * @op: The opcode to be looked up
+ */
 static const char *fc_exch_rctl_name(unsigned int op)
 {
        return fc_exch_name_lookup(op, fc_exch_rctl_names,
                                   FC_TABLE_SIZE(fc_exch_rctl_names));
 }
 
-/*
- * Hold an exchange - keep it from being freed.
+/**
+ * fc_exch_hold() - Increment an exchange's reference count
+ * @ep: Echange to be held
  */
-static void fc_exch_hold(struct fc_exch *ep)
+static inline void fc_exch_hold(struct fc_exch *ep)
 {
        atomic_inc(&ep->ex_refcnt);
 }
 
-/*
- * setup fc hdr by initializing few more FC header fields and sof/eof.
- * Initialized fields by this func:
- *     - fh_ox_id, fh_rx_id, fh_seq_id, fh_seq_cnt
- *     - sof and eof
+/**
+ * fc_exch_setup_hdr() - Initialize a FC header by initializing some fields
+ *                      and determine SOF and EOF.
+ * @ep:           The exchange to that will use the header
+ * @fp:           The frame whose header is to be modified
+ * @f_ctl: F_CTL bits that will be used for the frame header
+ *
+ * The fields initialized by this routine are: fh_ox_id, fh_rx_id,
+ * fh_seq_id, fh_seq_cnt and the SOF and EOF.
  */
 static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
                              u32 f_ctl)
@@ -225,7 +286,7 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
                if (fc_sof_needs_ack(ep->class))
                        fr_eof(fp) = FC_EOF_N;
                /*
-                * Form f_ctl.
+                * From F_CTL.
                 * The number of fill bytes to make the length a 4-byte
                 * multiple is the low order 2-bits of the f_ctl.
                 * The fill itself will have been cleared by the frame
@@ -255,10 +316,12 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
        fh->fh_seq_cnt = htons(ep->seq.cnt);
 }
 
-
-/*
- * Release a reference to an exchange.
- * If the refcnt goes to zero and the exchange is complete, it is freed.
+/**
+ * fc_exch_release() - Decrement an exchange's reference count
+ * @ep: Exchange to be released
+ *
+ * If the reference count reaches zero and the exchange is complete,
+ * it is freed.
  */
 static void fc_exch_release(struct fc_exch *ep)
 {
@@ -268,13 +331,15 @@ static void fc_exch_release(struct fc_exch *ep)
                mp = ep->em;
                if (ep->destructor)
                        ep->destructor(&ep->seq, ep->arg);
-               if (ep->lp->tt.exch_put)
-                       ep->lp->tt.exch_put(ep->lp, mp, ep->xid);
                WARN_ON(!(ep->esb_stat & ESB_ST_COMPLETE));
                mempool_free(ep, mp->ep_pool);
        }
 }
 
+/**
+ * fc_exch_done_locked() - Complete an exchange with the exchange lock held
+ * @ep: The exchange that is complete
+ */
 static int fc_exch_done_locked(struct fc_exch *ep)
 {
        int rc = 1;
@@ -299,22 +364,61 @@ static int fc_exch_done_locked(struct fc_exch *ep)
        return rc;
 }
 
-static void fc_exch_mgr_delete_ep(struct fc_exch *ep)
+/**
+ * fc_exch_ptr_get() - Return an exchange from an exchange pool
+ * @pool:  Exchange Pool to get an exchange from
+ * @index: Index of the exchange within the pool
+ *
+ * Use the index to get an exchange from within an exchange pool. exches
+ * will point to an array of exchange pointers. The index will select
+ * the exchange within the array.
+ */
+static inline struct fc_exch *fc_exch_ptr_get(struct fc_exch_pool *pool,
+                                             u16 index)
 {
-       struct fc_exch_mgr *mp;
+       struct fc_exch **exches = (struct fc_exch **)(pool + 1);
+       return exches[index];
+}
 
-       mp = ep->em;
-       spin_lock_bh(&mp->em_lock);
-       WARN_ON(mp->total_exches <= 0);
-       mp->total_exches--;
-       mp->exches[ep->xid - mp->min_xid] = NULL;
+/**
+ * fc_exch_ptr_set() - Assign an exchange to a slot in an exchange pool
+ * @pool:  The pool to assign the exchange to
+ * @index: The index in the pool where the exchange will be assigned
+ * @ep:           The exchange to assign to the pool
+ */
+static inline void fc_exch_ptr_set(struct fc_exch_pool *pool, u16 index,
+                                  struct fc_exch *ep)
+{
+       ((struct fc_exch **)(pool + 1))[index] = ep;
+}
+
+/**
+ * fc_exch_delete() - Delete an exchange
+ * @ep: The exchange to be deleted
+ */
+static void fc_exch_delete(struct fc_exch *ep)
+{
+       struct fc_exch_pool *pool;
+
+       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);
        list_del(&ep->ex_list);
-       spin_unlock_bh(&mp->em_lock);
+       spin_unlock_bh(&pool->lock);
        fc_exch_release(ep);    /* drop hold for exch in mp */
 }
 
-/*
- * Internal version of fc_exch_timer_set - used with lock held.
+/**
+ * fc_exch_timer_set_locked() - Start a timer for an exchange w/ the
+ *                             the exchange lock held
+ * @ep:                The exchange whose timer will start
+ * @timer_msec: The timeout period
+ *
+ * Used for upper level protocols to time out the exchange.
+ * The timer is cancelled when it fires or when the exchange completes.
  */
 static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
                                            unsigned int timer_msec)
@@ -322,19 +426,17 @@ static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
        if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
                return;
 
-       FC_EXCH_DBG(ep, "Exchange timed out, notifying the upper layer\n");
+       FC_EXCH_DBG(ep, "Exchange timer armed\n");
 
-       if (schedule_delayed_work(&ep->timeout_work,
-                                 msecs_to_jiffies(timer_msec)))
+       if (queue_delayed_work(fc_exch_workqueue, &ep->timeout_work,
+                              msecs_to_jiffies(timer_msec)))
                fc_exch_hold(ep);               /* hold for timer */
 }
 
-/*
- * Set timer for an exchange.
- * The time is a minimum delay in milliseconds until the timer fires.
- * Used for upper level protocols to time out the exchange.
- * The timer is cancelled when it fires or when the exchange completes.
- * Returns non-zero if a timer couldn't be allocated.
+/**
+ * fc_exch_timer_set() - Lock the exchange and set the timer
+ * @ep:                The exchange whose timer will start
+ * @timer_msec: The timeout period
  */
 static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
 {
@@ -343,7 +445,115 @@ static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
        spin_unlock_bh(&ep->ex_lock);
 }
 
-int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
+/**
+ * fc_seq_send() - Send a frame using existing sequence/exchange pair
+ * @lport: The local port that the exchange will be sent on
+ * @sp:           The sequence to be sent
+ * @fp:           The frame to be sent on the exchange
+ */
+static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp,
+                      struct fc_frame *fp)
+{
+       struct fc_exch *ep;
+       struct fc_frame_header *fh = fc_frame_header_get(fp);
+       int error;
+       u32 f_ctl;
+
+       ep = fc_seq_exch(sp);
+       WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT);
+
+       f_ctl = ntoh24(fh->fh_f_ctl);
+       fc_exch_setup_hdr(ep, fp, f_ctl);
+
+       /*
+        * update sequence count if this frame is carrying
+        * multiple FC frames when sequence offload is enabled
+        * by LLD.
+        */
+       if (fr_max_payload(fp))
+               sp->cnt += DIV_ROUND_UP((fr_len(fp) - sizeof(*fh)),
+                                       fr_max_payload(fp));
+       else
+               sp->cnt++;
+
+       /*
+        * Send the frame.
+        */
+       error = lport->tt.frame_send(lport, fp);
+
+       /*
+        * Update the exchange and sequence flags,
+        * assuming all frames for the sequence have been sent.
+        * We can only be called to send once for each sequence.
+        */
+       spin_lock_bh(&ep->ex_lock);
+       ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ;   /* not first seq */
+       if (f_ctl & FC_FC_SEQ_INIT)
+               ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+       spin_unlock_bh(&ep->ex_lock);
+       return error;
+}
+
+/**
+ * fc_seq_alloc() - Allocate a sequence for a given exchange
+ * @ep:            The exchange to allocate a new sequence for
+ * @seq_id: The sequence ID to be used
+ *
+ * We don't support multiple originated sequences on the same exchange.
+ * By implication, any previously originated sequence on this exchange
+ * is complete, and we reallocate the same sequence.
+ */
+static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id)
+{
+       struct fc_seq *sp;
+
+       sp = &ep->seq;
+       sp->ssb_stat = 0;
+       sp->cnt = 0;
+       sp->id = seq_id;
+       return sp;
+}
+
+/**
+ * fc_seq_start_next_locked() - Allocate a new sequence on the same
+ *                             exchange as the supplied sequence
+ * @sp: The sequence/exchange to get a new sequence for
+ */
+static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp)
+{
+       struct fc_exch *ep = fc_seq_exch(sp);
+
+       sp = fc_seq_alloc(ep, ep->seq_id++);
+       FC_EXCH_DBG(ep, "f_ctl %6x seq %2x\n",
+                   ep->f_ctl, sp->id);
+       return sp;
+}
+
+/**
+ * fc_seq_start_next() - Lock the exchange and get a new sequence
+ *                      for a given sequence/exchange pair
+ * @sp: The sequence/exchange to get a new exchange for
+ */
+static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
+{
+       struct fc_exch *ep = fc_seq_exch(sp);
+
+       spin_lock_bh(&ep->ex_lock);
+       sp = fc_seq_start_next_locked(sp);
+       spin_unlock_bh(&ep->ex_lock);
+
+       return sp;
+}
+
+/**
+ * fc_seq_exch_abort() - Abort an exchange and sequence
+ * @req_sp:    The sequence to be aborted
+ * @timer_msec: The period of time to wait before aborting
+ *
+ * Generally called because of a timeout or an abort from the upper layer.
+ */
+static int fc_seq_exch_abort(const struct fc_seq *req_sp,
+                            unsigned int timer_msec)
 {
        struct fc_seq *sp;
        struct fc_exch *ep;
@@ -392,11 +602,10 @@ int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
                error = -ENOBUFS;
        return error;
 }
-EXPORT_SYMBOL(fc_seq_exch_abort);
 
-/*
- * Exchange timeout - handle exchange timer expiration.
- * The timer will have been cancelled before this is called.
+/**
+ * fc_exch_timeout() - Handle exchange timer expiration
+ * @work: The work_struct identifying the exchange that timed out
  */
 static void fc_exch_timeout(struct work_struct *work)
 {
@@ -408,6 +617,8 @@ static void fc_exch_timeout(struct work_struct *work)
        u32 e_stat;
        int rc = 1;
 
+       FC_EXCH_DBG(ep, "Exchange timed out\n");
+
        spin_lock_bh(&ep->ex_lock);
        if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
                goto unlock;
@@ -415,9 +626,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;
@@ -427,7 +638,7 @@ static void fc_exch_timeout(struct work_struct *work)
                        rc = fc_exch_done_locked(ep);
                spin_unlock_bh(&ep->ex_lock);
                if (!rc)
-                       fc_exch_mgr_delete_ep(ep);
+                       fc_exch_delete(ep);
                if (resp)
                        resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg);
                fc_seq_exch_abort(sp, 2 * ep->r_a_tov);
@@ -442,83 +653,20 @@ done:
        fc_exch_release(ep);
 }
 
-/*
- * Allocate a sequence.
- *
- * We don't support multiple originated sequences on the same exchange.
- * By implication, any previously originated sequence on this exchange
- * is complete, and we reallocate the same sequence.
- */
-static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id)
-{
-       struct fc_seq *sp;
-
-       sp = &ep->seq;
-       sp->ssb_stat = 0;
-       sp->cnt = 0;
-       sp->id = seq_id;
-       return sp;
-}
-
-/*
- * fc_em_alloc_xid - returns an xid based on request type
- * @lp : ptr to associated lport
- * @fp : ptr to the assocated frame
- *
- * check the associated fc_fsp_pkt to get scsi command type and
- * command direction to decide from which range this exch id
- * will be allocated from.
- *
- * Returns : 0 or an valid xid
- */
-static u16 fc_em_alloc_xid(struct fc_exch_mgr *mp, const struct fc_frame *fp)
-{
-       u16 xid, min, max;
-       u16 *plast;
-       struct fc_exch *ep = NULL;
-
-       if (mp->max_read) {
-               if (fc_fcp_is_read(fr_fsp(fp))) {
-                       min = mp->min_xid;
-                       max = mp->max_read;
-                       plast = &mp->last_read;
-               } else {
-                       min = mp->max_read + 1;
-                       max = mp->max_xid;
-                       plast = &mp->last_xid;
-               }
-       } else {
-               min = mp->min_xid;
-               max = mp->max_xid;
-               plast = &mp->last_xid;
-       }
-       xid = *plast;
-       do {
-               xid = (xid == max) ? min : xid + 1;
-               ep = mp->exches[xid - mp->min_xid];
-       } while ((ep != NULL) && (xid != *plast));
-
-       if (unlikely(ep))
-               xid = 0;
-       else
-               *plast = xid;
-
-       return xid;
-}
-
-/*
- * fc_exch_alloc - allocate an exchange.
- * @mp : ptr to the exchange manager
- * @xid: input xid
+/**
+ * fc_exch_em_alloc() - Allocate an exchange from a specified EM.
+ * @lport: The local port that the exchange is for
+ * @mp:           The exchange manager that will allocate the exchange
  *
- * if xid is supplied zero then assign next free exchange ID
- * from exchange manager, otherwise use supplied xid.
- * Returns with exch lock held.
+ * Returns pointer to allocated fc_exch with exch lock held.
  */
-struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
-                             struct fc_frame *fp, u16 xid)
+static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,
+                                       struct fc_exch_mgr *mp)
 {
        struct fc_exch *ep;
+       unsigned int cpu;
+       u16 index;
+       struct fc_exch_pool *pool;
 
        /* allocate memory for exchange */
        ep = mempool_alloc(mp->ep_pool, GFP_ATOMIC);
@@ -528,16 +676,18 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
        }
        memset(ep, 0, sizeof(*ep));
 
-       spin_lock_bh(&mp->em_lock);
-       /* alloc xid if input xid 0 */
-       if (!xid) {
-               /* alloc a new xid */
-               xid = fc_em_alloc_xid(mp, fp);
-               if (!xid) {
-                       printk(KERN_WARNING "libfc: Failed to allocate an exhange\n");
+       cpu = get_cpu();
+       pool = per_cpu_ptr(mp->pool, cpu);
+       spin_lock_bh(&pool->lock);
+       put_cpu();
+       index = pool->next_index;
+       /* allocate new exch from pool */
+       while (fc_exch_ptr_get(pool, index)) {
+               index = index == mp->pool_max_index ? 0 : index + 1;
+               if (index == pool->next_index)
                        goto err;
-               }
        }
+       pool->next_index = index == mp->pool_max_index ? 0 : index + 1;
 
        fc_exch_hold(ep);       /* hold for exch in mp */
        spin_lock_init(&ep->ex_lock);
@@ -548,18 +698,19 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
         */
        spin_lock_bh(&ep->ex_lock);
 
-       mp->exches[xid - mp->min_xid] = ep;
-       list_add_tail(&ep->ex_list, &mp->ex_list);
+       fc_exch_ptr_set(pool, index, ep);
+       list_add_tail(&ep->ex_list, &pool->ex_list);
        fc_seq_alloc(ep, ep->seq_id++);
-       mp->total_exches++;
-       spin_unlock_bh(&mp->em_lock);
+       pool->total_exches++;
+       spin_unlock_bh(&pool->lock);
 
        /*
         *  update exchange
         */
-       ep->oxid = ep->xid = xid;
+       ep->oxid = ep->xid = (index << fc_cpu_order | cpu) + mp->min_xid;
        ep->em = mp;
-       ep->lp = mp->lp;
+       ep->pool = pool;
+       ep->lp = lport;
        ep->f_ctl = FC_FC_FIRST_SEQ;    /* next seq is first seq */
        ep->rxid = FC_XID_UNKNOWN;
        ep->class = mp->class;
@@ -567,33 +718,64 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
 out:
        return ep;
 err:
-       spin_unlock_bh(&mp->em_lock);
+       spin_unlock_bh(&pool->lock);
        atomic_inc(&mp->stats.no_free_exch_xid);
        mempool_free(ep, mp->ep_pool);
        return NULL;
 }
-EXPORT_SYMBOL(fc_exch_alloc);
 
-/*
- * Lookup and hold an exchange.
+/**
+ * fc_exch_alloc() - Allocate an exchange from an EM on a
+ *                  local port's list of EMs.
+ * @lport: The local port that will own the exchange
+ * @fp:           The FC frame that the exchange will be for
+ *
+ * This function walks the list of exchange manager(EM)
+ * anchors to select an EM for a new exchange allocation. The
+ * EM is selected when a NULL match function pointer is encountered
+ * or when a call to a match function returns true.
+ */
+static inline struct fc_exch *fc_exch_alloc(struct fc_lport *lport,
+                                           struct fc_frame *fp)
+{
+       struct fc_exch_mgr_anchor *ema;
+
+       list_for_each_entry(ema, &lport->ema_list, ema_list)
+               if (!ema->match || ema->match(fp))
+                       return fc_exch_em_alloc(lport, ema->mp);
+       return NULL;
+}
+
+/**
+ * fc_exch_find() - Lookup and hold an exchange
+ * @mp:         The exchange manager to lookup the exchange from
+ * @xid: The XID of the exchange to look up
  */
 static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
 {
+       struct fc_exch_pool *pool;
        struct fc_exch *ep = NULL;
 
        if ((xid >= mp->min_xid) && (xid <= mp->max_xid)) {
-               spin_lock_bh(&mp->em_lock);
-               ep = mp->exches[xid - mp->min_xid];
+               pool = per_cpu_ptr(mp->pool, xid & fc_cpu_mask);
+               spin_lock_bh(&pool->lock);
+               ep = fc_exch_ptr_get(pool, (xid - mp->min_xid) >> fc_cpu_order);
                if (ep) {
                        fc_exch_hold(ep);
                        WARN_ON(ep->xid != xid);
                }
-               spin_unlock_bh(&mp->em_lock);
+               spin_unlock_bh(&pool->lock);
        }
        return ep;
 }
 
-void fc_exch_done(struct fc_seq *sp)
+
+/**
+ * fc_exch_done() - Indicate that an exchange/sequence tuple is complete and
+ *                 the memory allocated for the related objects may be freed.
+ * @sp: The sequence that has completed
+ */
+static void fc_exch_done(struct fc_seq *sp)
 {
        struct fc_exch *ep = fc_seq_exch(sp);
        int rc;
@@ -602,20 +784,25 @@ void fc_exch_done(struct fc_seq *sp)
        rc = fc_exch_done_locked(ep);
        spin_unlock_bh(&ep->ex_lock);
        if (!rc)
-               fc_exch_mgr_delete_ep(ep);
+               fc_exch_delete(ep);
 }
-EXPORT_SYMBOL(fc_exch_done);
 
-/*
- * Allocate a new exchange as responder.
+/**
+ * fc_exch_resp() - Allocate a new exchange for a response frame
+ * @lport: The local port that the exchange was for
+ * @mp:           The exchange manager to allocate the exchange from
+ * @fp:           The response frame
+ *
  * Sets the responder ID in the frame header.
  */
-static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
+static struct fc_exch *fc_exch_resp(struct fc_lport *lport,
+                                   struct fc_exch_mgr *mp,
+                                   struct fc_frame *fp)
 {
        struct fc_exch *ep;
        struct fc_frame_header *fh;
 
-       ep = mp->lp->tt.exch_get(mp->lp, fp);
+       ep = fc_exch_alloc(lport, fp);
        if (ep) {
                ep->class = fc_frame_class(fp);
 
@@ -641,17 +828,23 @@ static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
                        ep->esb_stat &= ~ESB_ST_SEQ_INIT;
 
                fc_exch_hold(ep);       /* hold for caller */
-               spin_unlock_bh(&ep->ex_lock);   /* lock from exch_get */
+               spin_unlock_bh(&ep->ex_lock);   /* lock from fc_exch_alloc */
        }
        return ep;
 }
 
-/*
- * Find a sequence for receive where the other end is originating the sequence.
+/**
+ * fc_seq_lookup_recip() - Find a sequence where the other end
+ *                        originated the sequence
+ * @lport: The local port that the frame was sent to
+ * @mp:           The Exchange Manager to lookup the exchange from
+ * @fp:           The frame associated with the sequence we're looking for
+ *
  * If fc_pf_rjt_reason is FC_RJT_NONE then this function will have a hold
  * on the ep that should be released by the caller.
  */
-static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_exch_mgr *mp,
+static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport,
+                                                struct fc_exch_mgr *mp,
                                                 struct fc_frame *fp)
 {
        struct fc_frame_header *fh = fc_frame_header_get(fp);
@@ -705,7 +898,7 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_exch_mgr *mp,
                                reject = FC_RJT_RX_ID;
                                goto rel;
                        }
-                       ep = fc_exch_resp(mp, fp);
+                       ep = fc_exch_resp(lport, mp, fp);
                        if (!ep) {
                                reject = FC_RJT_EXCH_EST;       /* XXX */
                                goto out;
@@ -723,13 +916,9 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_exch_mgr *mp,
         * Find or create the sequence.
         */
        if (fc_sof_is_init(fr_sof(fp))) {
-               sp = fc_seq_start_next(&ep->seq);
-               if (!sp) {
-                       reject = FC_RJT_SEQ_XS; /* exchange shortage */
-                       goto rel;
-               }
-               sp->id = fh->fh_seq_id;
+               sp = &ep->seq;
                sp->ssb_stat |= SSB_ST_RESP;
+               sp->id = fh->fh_seq_id;
        } else {
                sp = &ep->seq;
                if (sp->id != fh->fh_seq_id) {
@@ -752,10 +941,12 @@ rel:
        return reject;
 }
 
-/*
- * Find the sequence for a frame being received.
- * We originated the sequence, so it should be found.
- * We may or may not have originated the exchange.
+/**
+ * fc_seq_lookup_orig() - Find a sequence where this end
+ *                       originated the sequence
+ * @mp:           The Exchange Manager to lookup the exchange from
+ * @fp:           The frame associated with the sequence we're looking for
+ *
  * Does not hold the sequence for the caller.
  */
 static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp,
@@ -787,8 +978,12 @@ static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp,
        return sp;
 }
 
-/*
- * Set addresses for an exchange.
+/**
+ * fc_exch_set_addr() - Set the source and destination IDs for an exchange
+ * @ep:             The exchange to set the addresses for
+ * @orig_id: The originator's ID
+ * @resp_id: The responder's ID
+ *
  * Note this must be done before the first sequence of the exchange is sent.
  */
 static void fc_exch_set_addr(struct fc_exch *ep,
@@ -804,77 +999,15 @@ static void fc_exch_set_addr(struct fc_exch *ep,
        }
 }
 
-static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp)
-{
-       struct fc_exch *ep = fc_seq_exch(sp);
-
-       sp = fc_seq_alloc(ep, ep->seq_id++);
-       FC_EXCH_DBG(ep, "f_ctl %6x seq %2x\n",
-                   ep->f_ctl, sp->id);
-       return sp;
-}
-/*
- * Allocate a new sequence on the same exchange as the supplied sequence.
- * This will never return NULL.
+/**
+ * fc_seq_els_rsp_send() - Send an ELS response using infomation from
+ *                        the existing sequence/exchange.
+ * @sp:              The sequence/exchange to get information from
+ * @els_cmd:  The ELS command to be sent
+ * @els_data: The ELS data to be sent
  */
-struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
-{
-       struct fc_exch *ep = fc_seq_exch(sp);
-
-       spin_lock_bh(&ep->ex_lock);
-       WARN_ON((ep->esb_stat & ESB_ST_COMPLETE) != 0);
-       sp = fc_seq_start_next_locked(sp);
-       spin_unlock_bh(&ep->ex_lock);
-
-       return sp;
-}
-EXPORT_SYMBOL(fc_seq_start_next);
-
-int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp)
-{
-       struct fc_exch *ep;
-       struct fc_frame_header *fh = fc_frame_header_get(fp);
-       int error;
-       u32     f_ctl;
-
-       ep = fc_seq_exch(sp);
-       WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT);
-
-       f_ctl = ntoh24(fh->fh_f_ctl);
-       fc_exch_setup_hdr(ep, fp, f_ctl);
-
-       /*
-        * update sequence count if this frame is carrying
-        * multiple FC frames when sequence offload is enabled
-        * by LLD.
-        */
-       if (fr_max_payload(fp))
-               sp->cnt += DIV_ROUND_UP((fr_len(fp) - sizeof(*fh)),
-                                       fr_max_payload(fp));
-       else
-               sp->cnt++;
-
-       /*
-        * Send the frame.
-        */
-       error = lp->tt.frame_send(lp, fp);
-
-       /*
-        * Update the exchange and sequence flags,
-        * assuming all frames for the sequence have been sent.
-        * We can only be called to send once for each sequence.
-        */
-       spin_lock_bh(&ep->ex_lock);
-       ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ;   /* not first seq */
-       if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT))
-               ep->esb_stat &= ~ESB_ST_SEQ_INIT;
-       spin_unlock_bh(&ep->ex_lock);
-       return error;
-}
-EXPORT_SYMBOL(fc_seq_send);
-
-void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
-                        struct fc_seq_els_data *els_data)
+static void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
+                               struct fc_seq_els_data *els_data)
 {
        switch (els_cmd) {
        case ELS_LS_RJT:
@@ -893,10 +1026,13 @@ void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
                FC_EXCH_DBG(fc_seq_exch(sp), "Invalid ELS CMD:%x\n", els_cmd);
        }
 }
-EXPORT_SYMBOL(fc_seq_els_rsp_send);
 
-/*
- * Send a sequence, which is also the last sequence in the exchange.
+/**
+ * fc_seq_send_last() - Send a sequence that is the last in the exchange
+ * @sp:             The sequence that is to be sent
+ * @fp:             The frame that will be sent on the sequence
+ * @rctl:    The R_CTL information to be sent
+ * @fh_type: The frame header type
  */
 static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,
                             enum fc_rctl rctl, enum fc_fh_type fh_type)
@@ -910,9 +1046,12 @@ static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,
        fc_seq_send(ep->lp, sp, fp);
 }
 
-/*
+/**
+ * fc_seq_send_ack() - Send an acknowledgement that we've received a frame
+ * @sp:           The sequence to send the ACK on
+ * @rx_fp: The received frame that is being acknoledged
+ *
  * Send ACK_1 (or equiv.) indicating we received something.
- * The frame we're acking is supplied.
  */
 static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
 {
@@ -920,14 +1059,14 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
        struct fc_frame_header *rx_fh;
        struct fc_frame_header *fh;
        struct fc_exch *ep = fc_seq_exch(sp);
-       struct fc_lport *lp = ep->lp;
+       struct fc_lport *lport = ep->lp;
        unsigned int f_ctl;
 
        /*
         * Don't send ACKs for class 3.
         */
        if (fc_sof_needs_ack(fr_sof(rx_fp))) {
-               fp = fc_frame_alloc(lp, 0);
+               fp = fc_frame_alloc(lport, 0);
                if (!fp)
                        return;
 
@@ -962,12 +1101,16 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
                else
                        fr_eof(fp) = FC_EOF_N;
 
-               (void) lp->tt.frame_send(lp, fp);
+               lport->tt.frame_send(lport, fp);
        }
 }
 
-/*
- * Send BLS Reject.
+/**
+ * fc_exch_send_ba_rjt() - Send BLS Reject
+ * @rx_fp:  The frame being rejected
+ * @reason: The reason the frame is being rejected
+ * @explan: The explaination for the rejection
+ *
  * This is for rejecting BA_ABTS only.
  */
 static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
@@ -978,11 +1121,11 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
        struct fc_frame_header *rx_fh;
        struct fc_frame_header *fh;
        struct fc_ba_rjt *rp;
-       struct fc_lport *lp;
+       struct fc_lport *lport;
        unsigned int f_ctl;
 
-       lp = fr_dev(rx_fp);
-       fp = fc_frame_alloc(lp, sizeof(*rp));
+       lport = fr_dev(rx_fp);
+       fp = fc_frame_alloc(lport, sizeof(*rp));
        if (!fp)
                return;
        fh = fc_frame_header_get(fp);
@@ -999,8 +1142,8 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
         */
        memcpy(fh->fh_s_id, rx_fh->fh_d_id, 3);
        memcpy(fh->fh_d_id, rx_fh->fh_s_id, 3);
-       fh->fh_ox_id = rx_fh->fh_rx_id;
-       fh->fh_rx_id = rx_fh->fh_ox_id;
+       fh->fh_ox_id = rx_fh->fh_ox_id;
+       fh->fh_rx_id = rx_fh->fh_rx_id;
        fh->fh_seq_cnt = rx_fh->fh_seq_cnt;
        fh->fh_r_ctl = FC_RCTL_BA_RJT;
        fh->fh_type = FC_TYPE_BLS;
@@ -1027,13 +1170,17 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
        if (fc_sof_needs_ack(fr_sof(fp)))
                fr_eof(fp) = FC_EOF_N;
 
-       (void) lp->tt.frame_send(lp, fp);
+       lport->tt.frame_send(lport, fp);
 }
 
-/*
- * Handle an incoming ABTS.  This would be for target mode usually,
- * but could be due to lost FCP transfer ready, confirm or RRQ.
- * We always handle this as an exchange abort, ignoring the parameter.
+/**
+ * fc_exch_recv_abts() - Handle an incoming ABTS
+ * @ep:           The exchange the abort was on
+ * @rx_fp: The ABTS frame
+ *
+ * This would be for target mode usually, but could be due to lost
+ * FCP transfer ready, confirm or RRQ. We always handle this as an
+ * exchange abort, ignoring the parameter.
  */
 static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp)
 {
@@ -1082,28 +1229,35 @@ free:
        fc_frame_free(rx_fp);
 }
 
-/*
- * Handle receive where the other end is originating the sequence.
+/**
+ * fc_exch_recv_req() - Handler for an incoming request where is other
+ *                     end is originating the sequence
+ * @lport: The local port that received the request
+ * @mp:           The EM that the exchange is on
+ * @fp:           The request frame
  */
-static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
+static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,
                             struct fc_frame *fp)
 {
        struct fc_frame_header *fh = fc_frame_header_get(fp);
        struct fc_seq *sp = NULL;
        struct fc_exch *ep = NULL;
-       enum fc_sof sof;
-       enum fc_eof eof;
-       u32 f_ctl;
        enum fc_pf_rjt_reason reject;
 
+       /* We can have the wrong fc_lport at this point with NPIV, which is a
+        * problem now that we know a new exchange needs to be allocated
+        */
+       lport = fc_vport_id_lookup(lport, ntoh24(fh->fh_d_id));
+       if (!lport) {
+               fc_frame_free(fp);
+               return;
+       }
+
        fr_seq(fp) = NULL;
-       reject = fc_seq_lookup_recip(mp, fp);
+       reject = fc_seq_lookup_recip(lport, mp, fp);
        if (reject == FC_RJT_NONE) {
                sp = fr_seq(fp);        /* sequence will be held */
                ep = fc_seq_exch(sp);
-               sof = fr_sof(fp);
-               eof = fr_eof(fp);
-               f_ctl = ntoh24(fh->fh_f_ctl);
                fc_seq_send_ack(sp, fp);
 
                /*
@@ -1120,17 +1274,21 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
                if (ep->resp)
                        ep->resp(sp, fp, ep->arg);
                else
-                       lp->tt.lport_recv(lp, sp, fp);
+                       lport->tt.lport_recv(lport, sp, fp);
                fc_exch_release(ep);    /* release from lookup */
        } else {
-               FC_EM_DBG(mp, "exch/seq lookup failed: reject %x\n", reject);
+               FC_LPORT_DBG(lport, "exch/seq lookup failed: reject %x\n",
+                            reject);
                fc_frame_free(fp);
        }
 }
 
-/*
- * Handle receive where the other end is originating the sequence in
- * response to our exchange.
+/**
+ * fc_exch_recv_seq_resp() - Handler for an incoming response where the other
+ *                          end is the originator of the sequence that is a
+ *                          response to our initial exchange
+ * @mp: The EM that the exchange is on
+ * @fp: The response frame
  */
 static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
 {
@@ -1164,17 +1322,15 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
                goto rel;
        }
        sof = fr_sof(fp);
+       sp = &ep->seq;
        if (fc_sof_is_init(sof)) {
-               sp = fc_seq_start_next(&ep->seq);
-               sp->id = fh->fh_seq_id;
                sp->ssb_stat |= SSB_ST_RESP;
-       } else {
-               sp = &ep->seq;
-               if (sp->id != fh->fh_seq_id) {
-                       atomic_inc(&mp->stats.seq_not_found);
-                       goto rel;
-               }
+               sp->id = fh->fh_seq_id;
+       } else if (sp->id != fh->fh_seq_id) {
+               atomic_inc(&mp->stats.seq_not_found);
+               goto rel;
        }
+
        f_ctl = ntoh24(fh->fh_f_ctl);
        fr_seq(fp) = sp;
        if (f_ctl & FC_FC_SEQ_INIT)
@@ -1193,7 +1349,7 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
                WARN_ON(fc_seq_exch(sp) != ep);
                spin_unlock_bh(&ep->ex_lock);
                if (!rc)
-                       fc_exch_mgr_delete_ep(ep);
+                       fc_exch_delete(ep);
        }
 
        /*
@@ -1221,27 +1377,33 @@ out:
        fc_frame_free(fp);
 }
 
-/*
- * Handle receive for a sequence where other end is responding to our sequence.
+/**
+ * fc_exch_recv_resp() - Handler for a sequence where other end is
+ *                      responding to our sequence
+ * @mp: The EM that the exchange is on
+ * @fp: The response frame
  */
 static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
 {
        struct fc_seq *sp;
 
        sp = fc_seq_lookup_orig(mp, fp);        /* doesn't hold sequence */
-       if (!sp) {
+
+       if (!sp)
                atomic_inc(&mp->stats.xid_not_found);
-               FC_EM_DBG(mp, "seq lookup failed\n");
-       } else {
+       else
                atomic_inc(&mp->stats.non_bls_resp);
-               FC_EM_DBG(mp, "non-BLS response to sequence");
-       }
+
        fc_frame_free(fp);
 }
 
-/*
- * Handle the response to an ABTS for exchange or sequence.
- * This can be BA_ACC or BA_RJT.
+/**
+ * fc_exch_abts_resp() - Handler for a response to an ABT
+ * @ep: The exchange that the frame is on
+ * @fp: The response frame
+ *
+ * This response would be to an ABTS cancelling an exchange or sequence.
+ * The response can be either BA_ACC or BA_RJT
  */
 static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
 {
@@ -1304,7 +1466,7 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
                rc = fc_exch_done_locked(ep);
        spin_unlock_bh(&ep->ex_lock);
        if (!rc)
-               fc_exch_mgr_delete_ep(ep);
+               fc_exch_delete(ep);
 
        if (resp)
                resp(sp, fp, ex_resp_arg);
@@ -1316,9 +1478,12 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
 
 }
 
-/*
- * Receive BLS sequence.
- * This is always a sequence initiated by the remote side.
+/**
+ * fc_exch_recv_bls() - Handler for a BLS sequence
+ * @mp: The EM that the exchange is on
+ * @fp: The request frame
+ *
+ * The BLS frame is always a sequence initiated by the remote side.
  * We may be either the originator or recipient of the exchange.
  */
 static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
@@ -1375,8 +1540,10 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
                fc_exch_release(ep);    /* release hold taken by fc_exch_find */
 }
 
-/*
- * Accept sequence with LS_ACC.
+/**
+ * fc_seq_ls_acc() - Accept sequence with LS_ACC
+ * @req_sp: The request sequence
+ *
  * If this fails due to allocation or transmit congestion, assume the
  * originator will repeat the sequence.
  */
@@ -1396,8 +1563,12 @@ static void fc_seq_ls_acc(struct fc_seq *req_sp)
        }
 }
 
-/*
- * Reject sequence with ELS LS_RJT.
+/**
+ * fc_seq_ls_rjt() - Reject a sequence with ELS LS_RJT
+ * @req_sp: The request sequence
+ * @reason: The reason the sequence is being rejected
+ * @explan: The explaination for the rejection
+ *
  * If this fails due to allocation or transmit congestion, assume the
  * originator will repeat the sequence.
  */
@@ -1420,6 +1591,10 @@ static void fc_seq_ls_rjt(struct fc_seq *req_sp, enum fc_els_rjt_reason reason,
        }
 }
 
+/**
+ * fc_exch_reset() - Reset an exchange
+ * @ep: The exchange to be reset
+ */
 static void fc_exch_reset(struct fc_exch *ep)
 {
        struct fc_seq *sp;
@@ -1429,12 +1604,6 @@ static void fc_exch_reset(struct fc_exch *ep)
 
        spin_lock_bh(&ep->ex_lock);
        ep->state |= FC_EX_RST_CLEANUP;
-       /*
-        * we really want to call del_timer_sync, but cannot due
-        * to the lport calling with the lport lock held (some resp
-        * functions can also grab the lport lock which could cause
-        * a deadlock).
-        */
        if (cancel_delayed_work(&ep->timeout_work))
                atomic_dec(&ep->ex_refcnt);     /* drop hold for timer */
        resp = ep->resp;
@@ -1447,49 +1616,85 @@ static void fc_exch_reset(struct fc_exch *ep)
        rc = fc_exch_done_locked(ep);
        spin_unlock_bh(&ep->ex_lock);
        if (!rc)
-               fc_exch_mgr_delete_ep(ep);
+               fc_exch_delete(ep);
 
        if (resp)
                resp(sp, ERR_PTR(-FC_EX_CLOSED), arg);
 }
 
-/*
- * Reset an exchange manager, releasing all sequences and exchanges.
- * If sid is non-zero, reset only exchanges we source from that FID.
- * If did is non-zero, reset only exchanges destined to that FID.
+/**
+ * fc_exch_pool_reset() - Reset a per cpu exchange pool
+ * @lport: The local port that the exchange pool is on
+ * @pool:  The exchange pool to be reset
+ * @sid:   The source ID
+ * @did:   The destination ID
+ *
+ * Resets a per cpu exches pool, releasing all of its sequences
+ * and exchanges. If sid is non-zero then reset only exchanges
+ * we sourced from the local port's FID. If did is non-zero then
+ * only reset exchanges destined for the local port's FID.
  */
-void fc_exch_mgr_reset(struct fc_lport *lp, u32 sid, u32 did)
+static void fc_exch_pool_reset(struct fc_lport *lport,
+                              struct fc_exch_pool *pool,
+                              u32 sid, u32 did)
 {
        struct fc_exch *ep;
        struct fc_exch *next;
-       struct fc_exch_mgr *mp = lp->emp;
 
-       spin_lock_bh(&mp->em_lock);
+       spin_lock_bh(&pool->lock);
 restart:
-       list_for_each_entry_safe(ep, next, &mp->ex_list, ex_list) {
-               if ((sid == 0 || sid == ep->sid) &&
+       list_for_each_entry_safe(ep, next, &pool->ex_list, ex_list) {
+               if ((lport == ep->lp) &&
+                   (sid == 0 || sid == ep->sid) &&
                    (did == 0 || did == ep->did)) {
                        fc_exch_hold(ep);
-                       spin_unlock_bh(&mp->em_lock);
+                       spin_unlock_bh(&pool->lock);
 
                        fc_exch_reset(ep);
 
                        fc_exch_release(ep);
-                       spin_lock_bh(&mp->em_lock);
+                       spin_lock_bh(&pool->lock);
 
                        /*
-                        * must restart loop incase while lock was down
-                        * multiple eps were released.
+                        * must restart loop incase while lock
+                        * was down multiple eps were released.
                         */
                        goto restart;
                }
        }
-       spin_unlock_bh(&mp->em_lock);
+       spin_unlock_bh(&pool->lock);
+}
+
+/**
+ * fc_exch_mgr_reset() - Reset all EMs of a local port
+ * @lport: The local port whose EMs are to be reset
+ * @sid:   The source ID
+ * @did:   The destination ID
+ *
+ * Reset all EMs associated with a given local port. Release all
+ * sequences and exchanges. If sid is non-zero then reset only the
+ * exchanges sent from the local port's FID. If did is non-zero then
+ * reset only exchanges destined for the local port's FID.
+ */
+void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did)
+{
+       struct fc_exch_mgr_anchor *ema;
+       unsigned int cpu;
+
+       list_for_each_entry(ema, &lport->ema_list, ema_list) {
+               for_each_possible_cpu(cpu)
+                       fc_exch_pool_reset(lport,
+                                          per_cpu_ptr(ema->mp->pool, cpu),
+                                          sid, did);
+       }
 }
 EXPORT_SYMBOL(fc_exch_mgr_reset);
 
-/*
- * Handle incoming ELS REC - Read Exchange Concise.
+/**
+ * fc_exch_els_rec() - Handler for ELS REC (Read Exchange Concise) requests
+ * @sp:         The sequence the REC is on
+ * @rfp: The REC frame
+ *
  * Note that the requesting port may be different than the S_ID in the request.
  */
 static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)
@@ -1542,7 +1747,6 @@ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)
                fc_exch_done(sp);
                goto out;
        }
-       sp = fc_seq_start_next(sp);
        acc = fc_frame_payload_get(fp, sizeof(*acc));
        memset(acc, 0, sizeof(*acc));
        acc->reca_cmd = ELS_LS_ACC;
@@ -1571,10 +1775,11 @@ reject:
        fc_frame_free(rfp);
 }
 
-/*
- * Handle response from RRQ.
- * Not much to do here, really.
- * Should report errors.
+/**
+ * fc_exch_rrq_resp() - Handler for RRQ responses
+ * @sp:         The sequence that the RRQ is on
+ * @fp:         The RRQ frame
+ * @arg: The exchange that the RRQ is on
  *
  * TODO: fix error handler.
  */
@@ -1614,24 +1819,102 @@ cleanup:
        fc_exch_release(aborted_ep);
 }
 
-/*
- * Send ELS RRQ - Reinstate Recovery Qualifier.
+
+/**
+ * fc_exch_seq_send() - Send a frame using a new exchange and sequence
+ * @lport:     The local port to send the frame on
+ * @fp:                The frame to be sent
+ * @resp:      The response handler for this request
+ * @destructor: The destructor for the exchange
+ * @arg:       The argument to be passed to the response handler
+ * @timer_msec: The timeout period for the exchange
+ *
+ * The frame pointer with some of the header's fields must be
+ * filled before calling this routine, those fields are:
+ *
+ * - routing control
+ * - FC port did
+ * - FC port sid
+ * - FC header type
+ * - frame control
+ * - parameter or relative offset
+ */
+static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,
+                                      struct fc_frame *fp,
+                                      void (*resp)(struct fc_seq *,
+                                                   struct fc_frame *fp,
+                                                   void *arg),
+                                      void (*destructor)(struct fc_seq *,
+                                                         void *),
+                                      void *arg, u32 timer_msec)
+{
+       struct fc_exch *ep;
+       struct fc_seq *sp = NULL;
+       struct fc_frame_header *fh;
+       int rc = 1;
+
+       ep = fc_exch_alloc(lport, fp);
+       if (!ep) {
+               fc_frame_free(fp);
+               return NULL;
+       }
+       ep->esb_stat |= ESB_ST_SEQ_INIT;
+       fh = fc_frame_header_get(fp);
+       fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id));
+       ep->resp = resp;
+       ep->destructor = destructor;
+       ep->arg = arg;
+       ep->r_a_tov = FC_DEF_R_A_TOV;
+       ep->lp = lport;
+       sp = &ep->seq;
+
+       ep->fh_type = fh->fh_type; /* save for possbile timeout handling */
+       ep->f_ctl = ntoh24(fh->fh_f_ctl);
+       fc_exch_setup_hdr(ep, fp, ep->f_ctl);
+       sp->cnt++;
+
+       if (ep->xid <= lport->lro_xid && fh->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD)
+               fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
+
+       if (unlikely(lport->tt.frame_send(lport, fp)))
+               goto err;
+
+       if (timer_msec)
+               fc_exch_timer_set_locked(ep, timer_msec);
+       ep->f_ctl &= ~FC_FC_FIRST_SEQ;  /* not first seq */
+
+       if (ep->f_ctl & FC_FC_SEQ_INIT)
+               ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+       spin_unlock_bh(&ep->ex_lock);
+       return sp;
+err:
+       rc = fc_exch_done_locked(ep);
+       spin_unlock_bh(&ep->ex_lock);
+       if (!rc)
+               fc_exch_delete(ep);
+       return NULL;
+}
+
+/**
+ * fc_exch_rrq() - Send an ELS RRQ (Reinstate Recovery Qualifier) command
+ * @ep: The exchange to send the RRQ on
+ *
  * This tells the remote port to stop blocking the use of
  * the exchange and the seq_cnt range.
  */
 static void fc_exch_rrq(struct fc_exch *ep)
 {
-       struct fc_lport *lp;
+       struct fc_lport *lport;
        struct fc_els_rrq *rrq;
        struct fc_frame *fp;
-       struct fc_seq *rrq_sp;
        u32 did;
 
-       lp = ep->lp;
+       lport = ep->lp;
 
-       fp = fc_frame_alloc(lp, sizeof(*rrq));
+       fp = fc_frame_alloc(lport, 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;
@@ -1644,25 +1927,35 @@ static void fc_exch_rrq(struct fc_exch *ep)
                did = ep->sid;
 
        fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, did,
-                      fc_host_port_id(lp->host), FC_TYPE_ELS,
+                      lport->port_id, 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(lport, fp, fc_exch_rrq_resp, NULL, ep,
+                            lport->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);
 }
 
 
-/*
- * Handle incoming ELS RRQ - Reset Recovery Qualifier.
+/**
+ * fc_exch_els_rrq() - Handler for ELS RRQ (Reset Recovery Qualifier) requests
+ * @sp: The sequence that the RRQ is on
+ * @fp: The RRQ frame
  */
 static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
 {
-       struct fc_exch *ep;             /* request or subject exchange */
+       struct fc_exch *ep = NULL;      /* request or subject exchange */
        struct fc_els_rrq *rp;
        u32 sid;
        u16 xid;
@@ -1712,173 +2005,231 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
         * Send LS_ACC.
         */
        fc_seq_ls_acc(sp);
-       fc_frame_free(fp);
-       return;
+       goto out;
 
 unlock_reject:
        spin_unlock_bh(&ep->ex_lock);
-       fc_exch_release(ep);    /* drop hold from fc_exch_find */
 reject:
        fc_seq_ls_rjt(sp, ELS_RJT_LOGIC, explan);
+out:
        fc_frame_free(fp);
+       if (ep)
+               fc_exch_release(ep);    /* drop hold from fc_exch_find */
+}
+
+/**
+ * fc_exch_mgr_add() - Add an exchange manager to a local port's list of EMs
+ * @lport: The local port to add the exchange manager to
+ * @mp:           The exchange manager to be added to the local port
+ * @match: The match routine that indicates when this EM should be used
+ */
+struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport,
+                                          struct fc_exch_mgr *mp,
+                                          bool (*match)(struct fc_frame *))
+{
+       struct fc_exch_mgr_anchor *ema;
+
+       ema = kmalloc(sizeof(*ema), GFP_ATOMIC);
+       if (!ema)
+               return ema;
+
+       ema->mp = mp;
+       ema->match = match;
+       /* add EM anchor to EM anchors list */
+       list_add_tail(&ema->ema_list, &lport->ema_list);
+       kref_get(&mp->kref);
+       return ema;
 }
+EXPORT_SYMBOL(fc_exch_mgr_add);
 
-struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
+/**
+ * fc_exch_mgr_destroy() - Destroy an exchange manager
+ * @kref: The reference to the EM to be destroyed
+ */
+static void fc_exch_mgr_destroy(struct kref *kref)
+{
+       struct fc_exch_mgr *mp = container_of(kref, struct fc_exch_mgr, kref);
+
+       mempool_destroy(mp->ep_pool);
+       free_percpu(mp->pool);
+       kfree(mp);
+}
+
+/**
+ * fc_exch_mgr_del() - Delete an EM from a local port's list
+ * @ema: The exchange manager anchor identifying the EM to be deleted
+ */
+void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema)
+{
+       /* remove EM anchor from EM anchors list */
+       list_del(&ema->ema_list);
+       kref_put(&ema->mp->kref, fc_exch_mgr_destroy);
+       kfree(ema);
+}
+EXPORT_SYMBOL(fc_exch_mgr_del);
+
+/**
+ * fc_exch_mgr_list_clone() - Share all exchange manager objects
+ * @src: Source lport to clone exchange managers from
+ * @dst: New lport that takes references to all the exchange managers
+ */
+int fc_exch_mgr_list_clone(struct fc_lport *src, struct fc_lport *dst)
+{
+       struct fc_exch_mgr_anchor *ema, *tmp;
+
+       list_for_each_entry(ema, &src->ema_list, ema_list) {
+               if (!fc_exch_mgr_add(dst, ema->mp, ema->match))
+                       goto err;
+       }
+       return 0;
+err:
+       list_for_each_entry_safe(ema, tmp, &dst->ema_list, ema_list)
+               fc_exch_mgr_del(ema);
+       return -ENOMEM;
+}
+
+/**
+ * fc_exch_mgr_alloc() - Allocate an exchange manager
+ * @lport:   The local port that the new EM will be associated with
+ * @class:   The default FC class for new exchanges
+ * @min_xid: The minimum XID for exchanges from the new EM
+ * @max_xid: The maximum XID for exchanges from the new EM
+ * @match:   The match routine for the new EM
+ */
+struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
                                      enum fc_class class,
-                                     u16 min_xid, u16 max_xid)
+                                     u16 min_xid, u16 max_xid,
+                                     bool (*match)(struct fc_frame *))
 {
        struct fc_exch_mgr *mp;
-       size_t len;
-
-       if (max_xid <= min_xid || min_xid == 0 || max_xid == FC_XID_UNKNOWN) {
-               FC_LPORT_DBG(lp, "Invalid min_xid 0x:%x and max_xid 0x:%x\n",
+       u16 pool_exch_range;
+       size_t pool_size;
+       unsigned int cpu;
+       struct fc_exch_pool *pool;
+
+       if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN ||
+           (min_xid & fc_cpu_mask) != 0) {
+               FC_LPORT_DBG(lport, "Invalid min_xid 0x:%x and max_xid 0x:%x\n",
                             min_xid, max_xid);
                return NULL;
        }
 
        /*
-        * Memory need for EM
+        * allocate memory for EM
         */
-#define xid_ok(i, m1, m2) (((i) >= (m1)) && ((i) <= (m2)))
-       len = (max_xid - min_xid + 1) * (sizeof(struct fc_exch *));
-       len += sizeof(struct fc_exch_mgr);
-
-       mp = kzalloc(len, GFP_ATOMIC);
+       mp = kzalloc(sizeof(struct fc_exch_mgr), GFP_ATOMIC);
        if (!mp)
                return NULL;
 
        mp->class = class;
-       mp->total_exches = 0;
-       mp->exches = (struct fc_exch **)(mp + 1);
-       mp->lp = lp;
        /* adjust em exch xid range for offload */
        mp->min_xid = min_xid;
        mp->max_xid = max_xid;
-       mp->last_xid = min_xid - 1;
-       mp->max_read = 0;
-       mp->last_read = 0;
-       if (lp->lro_enabled && xid_ok(lp->lro_xid, min_xid, max_xid)) {
-               mp->max_read = lp->lro_xid;
-               mp->last_read = min_xid - 1;
-               mp->last_xid = mp->max_read;
-       } else {
-               /* disable lro if no xid control over read */
-               lp->lro_enabled = 0;
-       }
-
-       INIT_LIST_HEAD(&mp->ex_list);
-       spin_lock_init(&mp->em_lock);
 
        mp->ep_pool = mempool_create_slab_pool(2, fc_em_cachep);
        if (!mp->ep_pool)
                goto free_mp;
 
-       return mp;
+       /*
+        * Setup per cpu exch pool with entire exchange id range equally
+        * divided across all cpus. The exch pointers array memory is
+        * allocated for exch range per pool.
+        */
+       pool_exch_range = (mp->max_xid - mp->min_xid + 1) / (fc_cpu_mask + 1);
+       mp->pool_max_index = pool_exch_range - 1;
 
-free_mp:
-       kfree(mp);
-       return NULL;
-}
-EXPORT_SYMBOL(fc_exch_mgr_alloc);
+       /*
+        * Allocate and initialize per cpu exch pool
+        */
+       pool_size = sizeof(*pool) + pool_exch_range * sizeof(struct fc_exch *);
+       mp->pool = __alloc_percpu(pool_size, __alignof__(struct fc_exch_pool));
+       if (!mp->pool)
+               goto free_mempool;
+       for_each_possible_cpu(cpu) {
+               pool = per_cpu_ptr(mp->pool, cpu);
+               spin_lock_init(&pool->lock);
+               INIT_LIST_HEAD(&pool->ex_list);
+       }
+
+       kref_init(&mp->kref);
+       if (!fc_exch_mgr_add(lport, mp, match)) {
+               free_percpu(mp->pool);
+               goto free_mempool;
+       }
 
-void fc_exch_mgr_free(struct fc_exch_mgr *mp)
-{
-       WARN_ON(!mp);
        /*
-        * The total exch count must be zero
-        * before freeing exchange manager.
+        * Above kref_init() sets mp->kref to 1 and then
+        * call to fc_exch_mgr_add incremented mp->kref again,
+        * so adjust that extra increment.
         */
-       WARN_ON(mp->total_exches != 0);
+       kref_put(&mp->kref, fc_exch_mgr_destroy);
+       return mp;
+
+free_mempool:
        mempool_destroy(mp->ep_pool);
+free_mp:
        kfree(mp);
+       return NULL;
 }
-EXPORT_SYMBOL(fc_exch_mgr_free);
+EXPORT_SYMBOL(fc_exch_mgr_alloc);
 
-struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp)
+/**
+ * fc_exch_mgr_free() - Free all exchange managers on a local port
+ * @lport: The local port whose EMs are to be freed
+ */
+void fc_exch_mgr_free(struct fc_lport *lport)
 {
-       if (!lp || !lp->emp)
-               return NULL;
+       struct fc_exch_mgr_anchor *ema, *next;
 
-       return fc_exch_alloc(lp->emp, fp, 0);
+       flush_workqueue(fc_exch_workqueue);
+       list_for_each_entry_safe(ema, next, &lport->ema_list, ema_list)
+               fc_exch_mgr_del(ema);
 }
-EXPORT_SYMBOL(fc_exch_get);
-
-struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
-                               struct fc_frame *fp,
-                               void (*resp)(struct fc_seq *,
-                                            struct fc_frame *fp,
-                                            void *arg),
-                               void (*destructor)(struct fc_seq *, void *),
-                               void *arg, u32 timer_msec)
-{
-       struct fc_exch *ep;
-       struct fc_seq *sp = NULL;
-       struct fc_frame_header *fh;
-       int rc = 1;
-
-       ep = lp->tt.exch_get(lp, fp);
-       if (!ep) {
-               fc_frame_free(fp);
-               return NULL;
-       }
-       ep->esb_stat |= ESB_ST_SEQ_INIT;
-       fh = fc_frame_header_get(fp);
-       fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id));
-       ep->resp = resp;
-       ep->destructor = destructor;
-       ep->arg = arg;
-       ep->r_a_tov = FC_DEF_R_A_TOV;
-       ep->lp = lp;
-       sp = &ep->seq;
-
-       ep->fh_type = fh->fh_type; /* save for possbile timeout handling */
-       ep->f_ctl = ntoh24(fh->fh_f_ctl);
-       fc_exch_setup_hdr(ep, fp, ep->f_ctl);
-       sp->cnt++;
-
-       fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
-
-       if (unlikely(lp->tt.frame_send(lp, fp)))
-               goto err;
-
-       if (timer_msec)
-               fc_exch_timer_set_locked(ep, timer_msec);
-       ep->f_ctl &= ~FC_FC_FIRST_SEQ;  /* not first seq */
-
-       if (ep->f_ctl & FC_FC_SEQ_INIT)
-               ep->esb_stat &= ~ESB_ST_SEQ_INIT;
-       spin_unlock_bh(&ep->ex_lock);
-       return sp;
-err:
-       rc = fc_exch_done_locked(ep);
-       spin_unlock_bh(&ep->ex_lock);
-       if (!rc)
-               fc_exch_mgr_delete_ep(ep);
-       return NULL;
-}
-EXPORT_SYMBOL(fc_exch_seq_send);
+EXPORT_SYMBOL(fc_exch_mgr_free);
 
-/*
- * Receive a frame
+/**
+ * fc_exch_recv() - Handler for received frames
+ * @lport: The local port the frame was received on
+ * @fp:           The received frame
  */
-void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
-                 struct fc_frame *fp)
+void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)
 {
        struct fc_frame_header *fh = fc_frame_header_get(fp);
-       u32 f_ctl;
+       struct fc_exch_mgr_anchor *ema;
+       u32 f_ctl, found = 0;
+       u16 oxid;
 
        /* lport lock ? */
-       if (!lp || !mp || (lp->state == LPORT_ST_NONE)) {
-               FC_LPORT_DBG(lp, "Receiving frames for an lport that "
+       if (!lport || lport->state == LPORT_ST_DISABLED) {
+               FC_LPORT_DBG(lport, "Receiving frames for an lport that "
                             "has not been initialized correctly\n");
                fc_frame_free(fp);
                return;
        }
 
+       f_ctl = ntoh24(fh->fh_f_ctl);
+       oxid = ntohs(fh->fh_ox_id);
+       if (f_ctl & FC_FC_EX_CTX) {
+               list_for_each_entry(ema, &lport->ema_list, ema_list) {
+                       if ((oxid >= ema->mp->min_xid) &&
+                           (oxid <= ema->mp->max_xid)) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found) {
+                       FC_LPORT_DBG(lport, "Received response for out "
+                                    "of range oxid:%hx\n", oxid);
+                       fc_frame_free(fp);
+                       return;
+               }
+       } else
+               ema = list_entry(lport->ema_list.prev, typeof(*ema), ema_list);
+
        /*
         * If frame is marked invalid, just drop it.
         */
-       f_ctl = ntoh24(fh->fh_f_ctl);
        switch (fr_eof(fp)) {
        case FC_EOF_T:
                if (f_ctl & FC_FC_END_SEQ)
@@ -1886,69 +2237,97 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
                /* fall through */
        case FC_EOF_N:
                if (fh->fh_type == FC_TYPE_BLS)
-                       fc_exch_recv_bls(mp, fp);
+                       fc_exch_recv_bls(ema->mp, fp);
                else if ((f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) ==
                         FC_FC_EX_CTX)
-                       fc_exch_recv_seq_resp(mp, fp);
+                       fc_exch_recv_seq_resp(ema->mp, fp);
                else if (f_ctl & FC_FC_SEQ_CTX)
-                       fc_exch_recv_resp(mp, fp);
+                       fc_exch_recv_resp(ema->mp, fp);
                else
-                       fc_exch_recv_req(lpmp, fp);
+                       fc_exch_recv_req(lport, ema->mp, fp);
                break;
        default:
-               FC_EM_DBG(mp, "dropping invalid frame (eof %x)", fr_eof(fp));
+               FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)",
+                            fr_eof(fp));
                fc_frame_free(fp);
-               break;
        }
 }
 EXPORT_SYMBOL(fc_exch_recv);
 
-int fc_exch_init(struct fc_lport *lp)
+/**
+ * fc_exch_init() - Initialize the exchange layer for a local port
+ * @lport: The local port to initialize the exchange layer for
+ */
+int fc_exch_init(struct fc_lport *lport)
 {
-       if (!lp->tt.exch_get) {
-               /*
-                *  exch_put() should be NULL if
-                *  exch_get() is NULL
-                */
-               WARN_ON(lp->tt.exch_put);
-               lp->tt.exch_get = fc_exch_get;
-       }
+       if (!lport->tt.seq_start_next)
+               lport->tt.seq_start_next = fc_seq_start_next;
 
-       if (!lp->tt.seq_start_next)
-               lp->tt.seq_start_next = fc_seq_start_next;
+       if (!lport->tt.exch_seq_send)
+               lport->tt.exch_seq_send = fc_exch_seq_send;
 
-       if (!lp->tt.exch_seq_send)
-               lp->tt.exch_seq_send = fc_exch_seq_send;
+       if (!lport->tt.seq_send)
+               lport->tt.seq_send = fc_seq_send;
 
-       if (!lp->tt.seq_send)
-               lp->tt.seq_send = fc_seq_send;
+       if (!lport->tt.seq_els_rsp_send)
+               lport->tt.seq_els_rsp_send = fc_seq_els_rsp_send;
 
-       if (!lp->tt.seq_els_rsp_send)
-               lp->tt.seq_els_rsp_send = fc_seq_els_rsp_send;
+       if (!lport->tt.exch_done)
+               lport->tt.exch_done = fc_exch_done;
 
-       if (!lp->tt.exch_done)
-               lp->tt.exch_done = fc_exch_done;
+       if (!lport->tt.exch_mgr_reset)
+               lport->tt.exch_mgr_reset = fc_exch_mgr_reset;
 
-       if (!lp->tt.exch_mgr_reset)
-               lp->tt.exch_mgr_reset = fc_exch_mgr_reset;
-
-       if (!lp->tt.seq_exch_abort)
-               lp->tt.seq_exch_abort = fc_seq_exch_abort;
+       if (!lport->tt.seq_exch_abort)
+               lport->tt.seq_exch_abort = fc_seq_exch_abort;
 
        return 0;
 }
 EXPORT_SYMBOL(fc_exch_init);
 
-int fc_setup_exch_mgr(void)
+/**
+ * fc_setup_exch_mgr() - Setup an exchange manager
+ */
+int fc_setup_exch_mgr()
 {
        fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch),
                                         0, SLAB_HWCACHE_ALIGN, NULL);
        if (!fc_em_cachep)
                return -ENOMEM;
+
+       /*
+        * Initialize fc_cpu_mask and fc_cpu_order. The
+        * fc_cpu_mask is set for nr_cpu_ids rounded up
+        * to order of 2's * power and order is stored
+        * in fc_cpu_order as this is later required in
+        * mapping between an exch id and exch array index
+        * in per cpu exch pool.
+        *
+        * This round up is required to align fc_cpu_mask
+        * to exchange id's lower bits such that all incoming
+        * frames of an exchange gets delivered to the same
+        * cpu on which exchange originated by simple bitwise
+        * AND operation between fc_cpu_mask and exchange id.
+        */
+       fc_cpu_mask = 1;
+       fc_cpu_order = 0;
+       while (fc_cpu_mask < nr_cpu_ids) {
+               fc_cpu_mask <<= 1;
+               fc_cpu_order++;
+       }
+       fc_cpu_mask--;
+
+       fc_exch_workqueue = create_singlethread_workqueue("fc_exch_workqueue");
+       if (!fc_exch_workqueue)
+               return -ENOMEM;
        return 0;
 }
 
-void fc_destroy_exch_mgr(void)
+/**
+ * fc_destroy_exch_mgr() - Destroy an exchange manager
+ */
+void fc_destroy_exch_mgr()
 {
+       destroy_workqueue(fc_exch_workqueue);
        kmem_cache_destroy(fc_em_cachep);
 }