Vasu Dev wrote:
> 1. Updates fcoe_rcv() to queue incoming frames to the fcoe per cpu
> thread on which this frame's exch was originated but in case of
> request exch not originated by initiator, simply use current cpu.
>
> 2. Updates fc_exch_em_alloc, fc_exch_delete, fc_exch_find to use
> exches pool. The fc_exch_mgr_delete_ep is renamed to
> fc_exch_delete since ep now managed within pools of EM and new
> name is more brief.
>
> The exch id and its index into exches array is mapped using
> cpu_mask, cpu_order and min_xid of EM in above updated functions
> as per detailed explanation about mapping exch id between exches
> pool array. Added cpu_mask and cpu_order fields to fc_exch_mgr
> to have access to these fields in fc_exch_em_alloc, fc_exch_delete,
> fc_exch_find in exch id mapping to exches array.
>
> 3. Adds exches pool ptr to fc_exch to free exch to its pool in
> fc_exch_delete.
>
> 4. Updates fc_exch_mgr_reset to reset all exches pools of an EM,
> this required adding fc_exch_pool_reset func to reset exches
> in pool and then have fc_exch_mgr_reset call fc_exch_pool_reset
> for each pool within each EM of lport.
>
> 5. Removes no longer needed exches array, em_lock, next_xid, and
> total_exches from struct fc_exch_mgr, these are not needed after
> use of exches pool, also removes not used max_read, last_read
> from struct fc_exch_mgr.
>
> 6. Updates locking notes for exches pool lock with fc_exch lock.
>
> Signed-off-by: Vasu Dev <[email protected]>
> ---
>
> drivers/scsi/fcoe/fcoe.c | 19 ++--
> drivers/scsi/libfc/fc_exch.c | 190
> +++++++++++++++++++++++-------------------
> include/scsi/libfc.h | 9 +-
> 3 files changed, 117 insertions(+), 101 deletions(-)
>
> diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
> index e2f9d0c..bde1104 100644
> --- a/drivers/scsi/fcoe/fcoe.c
> +++ b/drivers/scsi/fcoe/fcoe.c
> @@ -911,8 +911,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
> struct fcoe_softc *fc;
> struct fc_frame_header *fh;
> struct fcoe_percpu_s *fps;
> - unsigned short oxid;
> - unsigned int cpu = 0;
> + unsigned int cpu;
>
> fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
> lp = fc->ctlr.lp;
> @@ -946,20 +945,20 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device
> *dev,
> skb_set_transport_header(skb, sizeof(struct fcoe_hdr));
> fh = (struct fc_frame_header *) skb_transport_header(skb);
>
> - oxid = ntohs(fh->fh_ox_id);
> -
> fr = fcoe_dev_from_skb(skb);
> fr->fr_dev = lp;
> fr->ptype = ptype;
>
> -#ifdef CONFIG_SMP
> /*
> - * The incoming frame exchange id(oxid) is ANDed with num of online
> - * cpu bits to get cpu and then this cpu is used for selecting
> - * a per cpu kernel thread from fcoe_percpu.
> + * In case the incoming frame's exchange is originated from
> + * the initiator, then received frame's exchange id is ANDed
s/the initiator/this host/
Someday we'll do targets, I hope.
> + * with cpu_mask bits to get the same cpu on which exchange
> + * was originated, otherwise just use the current cpu.
> */
> - cpu = oxid & (num_online_cpus() - 1);
> -#endif
> + if (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX)
Note: I think the compiler is able to optimize this to get
rid of the ntoh24 so this will be equivalent to:
if (fh->fh_f_ctl[0] & 0x80)
If not, we could write it as:
if (fh->fh_f_ctl[0] & (FC_FC_EX_CTX >> 16))
> + cpu = ntohs(fh->fh_ox_id) & lp->cpu_mask;
> + else
> + cpu = smp_processor_id();
>
> fps = &per_cpu(fcoe_percpu, cpu);
> spin_lock_bh(&fps->fcoe_rx_list.lock);
> diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
> index 609590d..f364483 100644
> --- a/drivers/scsi/libfc/fc_exch.c
> +++ b/drivers/scsi/libfc/fc_exch.c
> @@ -64,16 +64,12 @@ struct fc_exches_pool {
> struct fc_exch_mgr {
> enum fc_class class; /* default class for sequences */
> struct kref kref; /* exchange mgr reference count */
> - spinlock_t em_lock; /* exchange manager lock,
> - must be taken before ex_lock */
> - u16 next_xid; /* next possible free 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 */
> mempool_t *ep_pool; /* reserve ep's */
> + u16 cpu_mask; /* used in calculating cpu from xid */
> + u16 cpu_order; /* used in calculating exches index */
> u16 pool_max_xid; /* max exchange id per pool */
> struct fc_exches_pool *pool; /* per cpu exches pool */
>
> @@ -90,7 +86,6 @@ 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)
>
> @@ -183,8 +178,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.
> */
>
> /*
> @@ -313,17 +308,17 @@ static int fc_exch_done_locked(struct fc_exch *ep)
> return rc;
> }
>
> -static void fc_exch_mgr_delete_ep(struct fc_exch *ep)
> +static void fc_exch_delete(struct fc_exch *ep)
> {
> - struct fc_exch_mgr *mp;
> + struct fc_exches_pool *pool;
>
> - 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;
> + pool = ep->pool;
> + spin_lock_bh(&pool->lock);
> + WARN_ON(pool->total_exches <= 0);
> + pool->total_exches--;
> + pool->exches[(ep->xid - ep->em->min_xid) >> ep->em->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 */
> }
>
> @@ -441,7 +436,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);
> @@ -485,10 +480,9 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport
> *lport,
> struct fc_exch_mgr *mp)
> {
> struct fc_exch *ep;
> - u16 min, max, xid;
> -
> - min = mp->min_xid;
> - max = mp->max_xid;
> + unsigned int cpu;
> + u16 xid;
> + struct fc_exches_pool *pool;
>
> /* allocate memory for exchange */
> ep = mempool_alloc(mp->ep_pool, GFP_ATOMIC);
> @@ -498,15 +492,17 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport
> *lport,
> }
> memset(ep, 0, sizeof(*ep));
>
> - spin_lock_bh(&mp->em_lock);
> - xid = mp->next_xid;
> + cpu = smp_processor_id();
> + pool = per_cpu_ptr(mp->pool, cpu);
> + spin_lock_bh(&pool->lock);
> + xid = pool->next_xid;
> /* alloc a new xid */
> - while (mp->exches[xid - min]) {
> - xid = (xid == max) ? min : xid + 1;
> - if (xid == mp->next_xid)
> + while (pool->exches[xid]) {
> + xid = xid == mp->pool_max_xid ? 0 : xid + 1;
We should call xid something else since it isn't
scaled by cpu_order at this point. If it's just the index
into the pool, we should call it that. Also, next_xid is
really being used here as the index into the pool.
Or maybe we should increase by cpu_mask + 1 each time?
Put some BUG_ON()s temporarily during your tests to make
sure you're getting XIDs in the right pool, etc.
> + if (xid == pool->next_xid)
> goto err;
> }
> - mp->next_xid = (xid == max) ? min : xid + 1;
> + pool->next_xid = xid == mp->pool_max_xid ? 0 : xid + 1;
>
> fc_exch_hold(ep); /* hold for exch in mp */
> spin_lock_init(&ep->ex_lock);
> @@ -517,17 +513,18 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport
> *lport,
> */
> spin_lock_bh(&ep->ex_lock);
>
> - mp->exches[xid - mp->min_xid] = ep;
> - list_add_tail(&ep->ex_list, &mp->ex_list);
> + pool->exches[xid] = 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 = (xid << lport->cpu_order | cpu) + mp->min_xid;
min_xid would have to be aligned such that (min_xid & cpu_mask) == 0.
Is that guaranteed somewhere?
> ep->em = mp;
> + ep->pool = pool;
> ep->lp = lport;
> ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */
> ep->rxid = FC_XID_UNKNOWN;
> @@ -536,7 +533,7 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport
> *lport,
> 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;
> @@ -573,16 +570,18 @@ EXPORT_SYMBOL(fc_exch_alloc);
> */
> static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
> {
> + struct fc_exches_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 & mp->cpu_mask);
> + spin_lock_bh(&pool->lock);
> + ep = pool->exches[(xid - mp->min_xid) >> mp->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;
> }
> @@ -596,7 +595,7 @@ 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);
>
> @@ -1189,7 +1188,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);
> }
>
> /*
> @@ -1299,7 +1298,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);
> @@ -1442,48 +1441,76 @@ 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() - Resets an per cpu exches pool.
> + * @lport: ptr to the local port
> + * @pool: ptr to the per cpu exches pool
> + * @sid: source FC ID
> + * @did: destination FC ID
> + *
> + * Resets an per cpu exches pool, releasing its all sequences
> + * and exchanges. If sid is non-zero, then reset only exchanges
> + * we sourced from that FID. If did is non-zero, reset only
> + * exchanges destined to that 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_exches_pool *pool,
> + u32 sid, u32 did)
> {
> struct fc_exch *ep;
> struct fc_exch *next;
> - struct fc_exch_mgr *mp;
> - struct fc_exch_mgr_anchor *ema;
>
> - list_for_each_entry(ema, &lp->ema_list, ema_list) {
> - mp = ema->mp;
> - 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 ((lp == ep->lp) &&
> - (sid == 0 || sid == ep->sid) &&
> - (did == 0 || did == ep->did)) {
> - fc_exch_hold(ep);
> - spin_unlock_bh(&mp->em_lock);
> -
> - fc_exch_reset(ep);
> -
> - fc_exch_release(ep);
> - spin_lock_bh(&mp->em_lock);
> -
> - /*
> - * must restart loop incase while lock
> - * was down multiple eps were released.
> - */
> - goto restart;
> - }
> + 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(&pool->lock);
> +
> + fc_exch_reset(ep);
> +
> + fc_exch_release(ep);
> + spin_lock_bh(&pool->lock);
> +
> + /*
> + * 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() - Resets all EMs of a lport
> + * @lport: ptr to the local port
> + * @sid: source FC ID
> + * @did: destination FC ID
> + *
> + * Reset all EMs of a lport, releasing its all sequences and
> + * exchanges. If sid is non-zero, then reset only exchanges
> + * we sourced from that FID. If did is non-zero, reset only
> + * exchanges destined to that 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);
> @@ -1746,11 +1773,6 @@ static void fc_exch_mgr_destroy(struct kref *kref)
> {
> struct fc_exch_mgr *mp = container_of(kref, struct fc_exch_mgr, kref);
>
> - /*
> - * The total exch count must be zero
> - * before freeing exchange manager.
> - */
> - WARN_ON(mp->total_exches != 0);
> mempool_destroy(mp->ep_pool);
> free_percpu(mp->pool);
> kfree(mp);
> @@ -1771,7 +1793,6 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport
> *lp,
> bool (*match)(struct fc_frame *))
> {
> struct fc_exch_mgr *mp;
> - size_t len;
> u16 pool_xid;
> size_t psize;
> unsigned int cpu;
> @@ -1784,30 +1805,23 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport
> *lp,
> }
>
> /*
> - * Memory need for EM
> + * allocate memory for EM
> */
> - 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);
> /* adjust em exch xid range for offload */
> mp->min_xid = min_xid;
> mp->max_xid = max_xid;
> - mp->next_xid = min_xid;
> -
> - 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;
>
> + mp->cpu_mask = lp->cpu_mask;
> + mp->cpu_order = lp->cpu_order;
> /*
> * Setup per cpu exches pool with entire exchange
> * id range equally divided across all cpus.
> @@ -1912,7 +1926,7 @@ err:
> rc = fc_exch_done_locked(ep);
> spin_unlock_bh(&ep->ex_lock);
> if (!rc)
> - fc_exch_mgr_delete_ep(ep);
> + fc_exch_delete(ep);
> return NULL;
> }
> EXPORT_SYMBOL(fc_exch_seq_send);
> diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
> index 6c69b3a..e2d69f6 100644
> --- a/include/scsi/libfc.h
> +++ b/include/scsi/libfc.h
> @@ -376,6 +376,7 @@ struct fc_seq {
> */
> struct fc_exch {
> struct fc_exch_mgr *em; /* exchange manager */
> + struct fc_exches_pool *pool; /* per cpu exches pool */
> u32 state; /* internal driver state */
> u16 xid; /* our exchange ID */
> struct list_head ex_list; /* free or busy list linkage */
> @@ -1060,10 +1061,12 @@ struct fc_exch *fc_exch_alloc(struct fc_lport *lport,
> struct fc_frame *fp);
> */
> struct fc_seq *fc_seq_start_next(struct fc_seq *sp);
>
> +
> /*
> - * Reset an exchange manager, completing all sequences and exchanges.
> - * If s_id is non-zero, reset only exchanges originating from that FID.
> - * If d_id is non-zero, reset only exchanges sending to that FID.
> + * Reset all EMs of a lport, releasing its all sequences and
> + * exchanges. If sid is non-zero, then reset only exchanges
> + * we sourced from that FID. If did is non-zero, reset only
> + * exchanges destined to that FID.
> */
> void fc_exch_mgr_reset(struct fc_lport *, u32 s_id, u32 d_id);
>
>
> _______________________________________________
> devel mailing list
> [email protected]
> http://www.open-fcoe.org/mailman/listinfo/devel
_______________________________________________
devel mailing list
[email protected]
http://www.open-fcoe.org/mailman/listinfo/devel