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

Reply via email to