From: Vasu Dev <[email protected]> Previously when a netdev supported offloaded Rx it was indicating the range of XIDs for offload to the singular EM that was associated with the lport on the port.
Previous patches have made it so an EM can be shared across multiple lports. This patch adds an offload EM to the fcoe_softc and allows the fcoe module to override the exch_get() function poiner in libfc's template so that requests for exchanges are handled by fcoe. fcoe can then choose from either the default EM associated with the lport the exchange is on or the offload EM associated with the lport's netdev. To accomplish this the patch does the following: - Adds oem (offload EM) pointer to the fcoe_softc - Reorganize EM freeing to consider the offload EM - Reorganize fcoe_em_alloc() (previously fcoe_em_config()) to assign the same EMs to any VN_Ports (lports) associated with the same netdev->phys_dev. - Implements fcoe_exch_recv() to handle incoming frames and determine with EM should handle the inbound exchange. - Overrides the exch_get() routine of the libfc template so that fcoe can use offload EM when offload criteria is met (i.e. exchange will be for a read request). Signed-off-by: Vasu Dev <[email protected]> Signed-off-by: Robert Love <[email protected]> --- drivers/scsi/fcoe/fcoe.c | 134 +++++++++++++++++++++++++++++++++++++++--- drivers/scsi/fcoe/fcoe.h | 1 drivers/scsi/libfc/fc_exch.c | 92 +++++------------------------ include/scsi/libfc.h | 5 -- 4 files changed, 141 insertions(+), 91 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index e4ba631..f589a47 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -407,20 +407,78 @@ static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost, } /** - * fcoe_em_config() - allocates em for this lport + * fcoe_em_alloc() - allocates em for this lport * @lp: the port that em is to allocated for * * Returns : 0 on success */ -static inline int fcoe_em_config(struct fc_lport *lp) +static inline int fcoe_em_alloc(struct fc_lport *lp) { + struct fcoe_softc *fc = lport_priv(lp); + struct fcoe_softc *oldfc = NULL; + int rc = 0; + BUG_ON(lp->emp); + /* + * Check if need to allocate an em instance for + * offload exchange ids to be shared across all VN_PORTs/lport. + */ + if (!lp->lro_enabled || !lp->lro_xid || (lp->lro_xid >= FCOE_MAX_XID)) { + lp->lro_xid = 0; + goto skip_oem; + } + + /* + * Reuse exitsing offload em instance in case + * it is already allocated on phys_dev + */ + write_lock(&fcoe_hostlist_lock); + list_for_each_entry(oldfc, &fcoe_hostlist, list) { + if (oldfc->phys_dev == fc->phys_dev) { + fc->oem = oldfc->oem; + break; + } + } + write_unlock(&fcoe_hostlist_lock); + + fc->oem = fc_exch_mgr_alloc(lp, fc->oem, FC_CLASS_3, + FCOE_MIN_XID, lp->lro_xid); + + if (!fc->oem) { + printk(KERN_ERR "fcoe_em_alloc: failed to " + "allocate em for offload%s\n", fc->real_dev->name); + rc = -ENOMEM; + goto out; + } + +skip_oem: lp->emp = fc_exch_mgr_alloc(lp, NULL, FC_CLASS_3, - FCOE_MIN_XID, FCOE_MAX_XID); - if (!lp->emp) - return -ENOMEM; + FCOE_MIN_XID + lp->lro_xid, + FCOE_MAX_XID); + + if (!lp->emp) { + printk(KERN_ERR "Failed to allocate an Exchange " + "Manager for %s\n", fc->real_dev->name); + rc = -ENOMEM; + } +out: + return rc; +} +/** + * fcoe_em_free() - frees allocated em for a lport + * @lp: the port that em is to allocated for + * + * Returns : 0 on success + */ +static inline int fcoe_em_free(struct fc_lport *lp) +{ + struct fcoe_softc *fc = lport_priv(lp); + + if (fc->oem) + fc_exch_mgr_free(fc->oem); + fc_exch_mgr_free(lp->emp); return 0; } @@ -468,7 +526,7 @@ static int fcoe_if_destroy(struct net_device *netdev) /* There are no more rports or I/O, free the EM */ if (lp->emp) - fc_exch_mgr_free(lp->emp); + fcoe_em_free(lp); /* Free the per-CPU revieve threads */ fcoe_percpu_clean(lp); @@ -486,6 +544,59 @@ static int fcoe_if_destroy(struct net_device *netdev) return 0; } +/** + * fcoe_exch_recv() - receive a incoming frame + * @lp: the associated local port + * @fp: received fc_frame + * + * Locates exchange manager instance for received frame + * and pass it up to libfc using fc_exch_recv api. + */ +static void fcoe_exch_recv(struct fc_lport *lp, struct fc_frame *fp) +{ + struct fcoe_softc *fc = lport_priv(lp); + struct fc_frame_header *fh = fc_frame_header_get(fp); + u16 oxid = ntohs(fh->fh_ox_id); + u32 ctx; + + if (lp->lro_enabled && (oxid <= lp->lro_xid)) { + ctx = ntoh24(fh->fh_f_ctl) & (FC_FC_EX_CTX | FC_FC_SEQ_CTX); + if (ctx == FC_FC_EX_CTX) { + fc_exch_recv(lp, fc->oem, fp); + return; + } + } + fc_exch_recv(lp, lp->emp, fp); +} + +/** + * fcoe_exch_get() - allocates an exchange + * @lp: the associated local port + * @fp: the fc_frame to be transmitted + * + * Allocates exchange from fc->oem for offload eligible frames + * otherwise allocates from exhcange from the default EM instance. + */ +static struct fc_exch *fcoe_exch_get(struct fc_lport *lp, struct fc_frame *fp) +{ + struct fcoe_softc *fc = lport_priv(lp); + struct fc_exch *ep = NULL; + + if (fc_fcp_is_read(fr_fsp(fp))) + ep = fc_exch_alloc(fc->oem); + + /* + * if valid ep allocated from fc->ofemp + * then set up ddp here, else allocate + * ep from other em. + */ + if (ep) + fc_fcp_ddp_setup(fr_fsp(fp), ep->xid); + else + ep = fc_exch_alloc(lp->emp); + return ep; +} + /* * fcoe_ddp_setup - calls LLD's ddp_setup through net_device * @lp: the corresponding fc_lport @@ -590,6 +701,9 @@ static int fcoe_if_create(struct net_device *netdev) goto out_netdev_cleanup; } + /* set exch_get if lro enabled */ + if (lp->lro_enabled) + fcoe_libfc_fcn_templ.exch_get = fcoe_exch_get; /* Initialize the library */ rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ); if (rc) { @@ -598,7 +712,7 @@ static int fcoe_if_create(struct net_device *netdev) } /* lport exch manager allocation */ - rc = fcoe_em_config(lp); + rc = fcoe_em_alloc(lp); if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the " "interface\n"); @@ -620,7 +734,7 @@ static int fcoe_if_create(struct net_device *netdev) return rc; out_lp_destroy: - fc_exch_mgr_free(lp->emp); /* Free the EM */ + fcoe_em_free(lp); out_netdev_cleanup: fcoe_netdev_cleanup(fc); out_host_put: @@ -1279,7 +1393,7 @@ int fcoe_percpu_receive_thread(void *arg) fh = fc_frame_header_get(fp); if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && fh->fh_type == FC_TYPE_FCP) { - fc_exch_recv(lp, lp->emp, fp); + fcoe_exch_recv(lp, fp); continue; } if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { @@ -1300,7 +1414,7 @@ int fcoe_percpu_receive_thread(void *arg) fc_frame_free(fp); continue; } - fc_exch_recv(lp, lp->emp, fp); + fcoe_exch_recv(lp, fp); } return 0; } diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 917aae8..4db2330 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -57,6 +57,7 @@ struct fcoe_softc { struct list_head list; struct net_device *real_dev; struct net_device *phys_dev; /* device with ethtool_ops */ + struct fc_exch_mgr *oem; /* offload exchane manager */ struct packet_type fcoe_packet_type; struct packet_type fip_packet_type; struct sk_buff_head fcoe_pending_queue; diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index d429e8b..0664366 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -72,9 +72,7 @@ struct fc_exch_mgr { 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 */ + u32 total_exches; /* total allocated exchanges */ struct list_head ex_list; /* allocated exchanges list */ struct list_head em_list; /* fc_exch_mgr list */ struct fc_lport *lp; /* fc device instance */ @@ -474,64 +472,15 @@ static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id) } /* - * 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. + * fc_exch_alloc - allocate an exchange and its exchange id. * @mp : ptr to the exchange manager - * @xid: input xid * - * if xid is supplied zero then assign next free exchange ID - * from exchange manager, otherwise use supplied xid. * Returns with exch lock held. */ -struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, - struct fc_frame *fp, u16 xid) +struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp) { struct fc_exch *ep; + u16 min, max, xid; /* allocate memory for exchange */ ep = mempool_alloc(mp->ep_pool, GFP_ATOMIC); @@ -541,16 +490,18 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, } memset(ep, 0, sizeof(*ep)); + min = mp->min_xid; + max = mp->max_xid; + 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_ERR "fc_em_alloc_xid() failed\n"); + xid = mp->last_xid; + /* alloc a new xid */ + while (mp->exches[xid - min]) { + xid = (xid == max) ? min : xid + 1; + if (xid == mp->last_xid) goto err; - } } + mp->last_xid = xid; fc_exch_hold(ep); /* hold for exch in mp */ spin_lock_init(&ep->ex_lock); @@ -1761,7 +1712,6 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, /* * Memory need 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); @@ -1775,17 +1725,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *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; - } + mp->last_xid = min_xid; INIT_LIST_HEAD(&mp->ex_list); spin_lock_init(&mp->em_lock); @@ -1830,7 +1770,7 @@ struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp) if (!lp || !lp->emp) return NULL; - return fc_exch_alloc(lp->emp, fp, 0); + return fc_exch_alloc(lp->emp); } EXPORT_SYMBOL(fc_exch_get); @@ -1867,8 +1807,6 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp, 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; diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 68f7a61..99d687c 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -992,11 +992,8 @@ struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp); /* * Allocate a new exchange and sequence pair. - * if ex_id is zero then next free exchange id - * from specified exchange manger mp will be assigned. */ -struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, - struct fc_frame *fp, u16 ex_id); +struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp); /* * Start a new sequence on the same exchange as the supplied sequence. */ _______________________________________________ devel mailing list [email protected] http://www.open-fcoe.org/mailman/listinfo/devel
