Adds offload exchange manager(oemp) to fcoe_softc. Allocates oemp only if existing fc->oemp instance is not found on associated eth device, if existing fc->oemp instance is found then simply use it for new VN_PORT/lport on eth device and increase its usages count by calling fc_exch_mgr_inc.
The fcoe_em_config is renamed to fcoe_em_alloc. Modified fcoe_em_alloc handles allocation for both oemp and default lp->emp allocation under fcoe_hostlist_lock. The fcoe_em_alloc and addition of new fcoe_softc, both are called atomic under fcoe_hostlist_lock to ensure only one oemp per eth device gets used. Added fcoe_em_free frees both oemp and lp->emp em instance for a lport. Signed-off-by: Vasu Dev <[email protected]> --- drivers/scsi/fcoe/fcoe.c | 114 ++++++++++++++++++++++++++++++++++++---------- drivers/scsi/fcoe/fcoe.h | 1 2 files changed, 91 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index ab238fb..536b734 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -418,25 +418,85 @@ 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 * + * Called with write fcoe_hostlist_lock held. + * * 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; + BUG_ON(lp->emp); - lp->oemp = NULL; + lp->oemp = fc->oemp = NULL; + /* + * 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 existing offload em instance in case + * it is already allocated on phys_dev + */ + list_for_each_entry(oldfc, &fcoe_hostlist, list) { + if (oldfc->phys_dev == fc->phys_dev) { + fc->oemp = oldfc->oemp; + break; + } + } + + if (fc->oemp) + fc_exch_mgr_inc(fc->oemp); + else + fc->oemp = fc_exch_mgr_alloc(lp, FC_CLASS_3, + FCOE_MIN_XID, lp->lro_xid); + + if (!fc->oemp) { + printk(KERN_ERR "fcoe_em_alloc: failed to allocate em for " + "offload exches on interface:%s\n", fc->real_dev->name); + return -ENOMEM; + } + lp->oemp = fc->oemp; + +skip_oem: lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3, - FCOE_MIN_XID, FCOE_MAX_XID); - if (!lp->emp) + FCOE_MIN_XID + lp->lro_xid, + FCOE_MAX_XID); + + if (!lp->emp) { + printk(KERN_ERR "fcoe_em_alloc: failed to " + "allocate em for on interface %s\n", fc->real_dev->name); return -ENOMEM; + } return 0; } /** + * 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->oemp) + fc_exch_mgr_free(fc->oemp); + fc_exch_mgr_free(lp->emp); + return 0; +} + +/** * fcoe_if_destroy() - FCoE software HBA tear-down function * @netdev: ptr to the associated net_device * @@ -478,8 +538,7 @@ static int fcoe_if_destroy(struct net_device *netdev) scsi_remove_host(lp->host); /* 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 receive threads */ fcoe_percpu_clean(lp); @@ -606,24 +665,33 @@ static int fcoe_if_create(struct net_device *netdev) goto out_netdev_cleanup; } - /* lport exch manager allocation */ - rc = fcoe_em_config(lp); + /* Initialize the library */ + rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ); if (rc) { - FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the " + FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the " "interface\n"); goto out_netdev_cleanup; } - /* Initialize the library */ - rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ); + /* + * fcoe_em_alloc() and fcoe_hostlist_add() both + * need to be atomic under fcoe_hostlist_lock + * since fcoe_em_alloc() looks for existing EM instance + * on host list updated fcoe_hostlist_add(). + */ + write_lock(&fcoe_hostlist_lock); + /* lport exch manager allocation */ + rc = fcoe_em_alloc(lp); if (rc) { - FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the " + FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the " "interface\n"); - goto out_lp_destroy; + write_unlock(&fcoe_hostlist_lock); + goto out_netdev_cleanup; } /* add to lports list */ fcoe_hostlist_add(lp); + write_unlock(&fcoe_hostlist_lock); lp->boot_time = jiffies; @@ -636,8 +704,6 @@ static int fcoe_if_create(struct net_device *netdev) return rc; -out_lp_destroy: - fc_exch_mgr_free(lp->emp); /* Free the EM */ out_netdev_cleanup: fcoe_netdev_cleanup(fc); out_host_put: @@ -1731,6 +1797,8 @@ int fcoe_reset(struct Scsi_Host *shost) * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device * @dev: this is currently ptr to net_device * + * Called with fcoe_hostlist_lock held. + * * Returns: NULL or the located fcoe_softc */ static struct fcoe_softc * @@ -1738,14 +1806,10 @@ fcoe_hostlist_lookup_softc(const struct net_device *dev) { struct fcoe_softc *fc; - read_lock(&fcoe_hostlist_lock); list_for_each_entry(fc, &fcoe_hostlist, list) { - if (fc->real_dev == dev) { - read_unlock(&fcoe_hostlist_lock); + if (fc->real_dev == dev) return fc; - } } - read_unlock(&fcoe_hostlist_lock); return NULL; } @@ -1759,7 +1823,9 @@ struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) { struct fcoe_softc *fc; + read_lock(&fcoe_hostlist_lock); fc = fcoe_hostlist_lookup_softc(netdev); + read_unlock(&fcoe_hostlist_lock); return (fc) ? fc->ctlr.lp : NULL; } @@ -1768,6 +1834,8 @@ struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) * fcoe_hostlist_add() - Add a lport to lports list * @lp: ptr to the fc_lport to be added * + * Called with write fcoe_hostlist_lock held. + * * Returns: 0 for success */ int fcoe_hostlist_add(const struct fc_lport *lp) @@ -1777,9 +1845,7 @@ int fcoe_hostlist_add(const struct fc_lport *lp) fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp)); if (!fc) { fc = lport_priv(lp); - write_lock_bh(&fcoe_hostlist_lock); list_add_tail(&fc->list, &fcoe_hostlist); - write_unlock_bh(&fcoe_hostlist_lock); } return 0; } @@ -1794,9 +1860,9 @@ int fcoe_hostlist_remove(const struct fc_lport *lp) { struct fcoe_softc *fc; + write_lock_bh(&fcoe_hostlist_lock); fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp)); BUG_ON(!fc); - write_lock_bh(&fcoe_hostlist_lock); list_del(&fc->list); write_unlock_bh(&fcoe_hostlist_lock); diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 0d724fa..7a058d7 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -81,6 +81,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 *oemp; /* offload exchange manager */ struct packet_type fcoe_packet_type; struct packet_type fip_packet_type; struct sk_buff_head fcoe_pending_queue; _______________________________________________ devel mailing list [email protected] http://www.open-fcoe.org/mailman/listinfo/devel
