All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window
@ 2009-03-17 18:41 Robert Love
  2009-03-17 18:41 ` [PATCH 01/14] fcoe: Initialize all possilbe skb_queue(s) when module is loaded Robert Love
                   ` (13 more replies)
  0 siblings, 14 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:41 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

The following series implements a few fixes, some preparation work and the implementation
of FIP.

1) Fix of CPU hotplug handling.

2) Removal of fc_transport_fcoe.[ch]. This code was written so a LLD could load a transport
   for Ethernet devices that support FCoE offloads. This aproach was discarded in favor of
   directly providing offload capabilities through netdev. FCoE offload patches have been
   Ack'd by netdev and are in the scsi-misc tree already. In this set we just clean up the
   previous and unused FCoE transport method.
   
3) fcoe/* reorgainzation. Since both FIP and fnic are targeting the 2.6.30 merge window we
   wanted to do some cleanup of the fcoe layer. We've better defined each module so that
   fcoe.ko will only have the netdev related SW implementation (fnic does not use) and
   libfcoe.ko which will have common FCoE routines available to all FCoE LLDs.

4) FIP implementation. FIP == FCoE Initialization Protocol. This is a new standard way to
   discover FCoE capable switches. This code becomes the majority of libfcoe.ko. These
   patches had previously been sent to linux-scsi. This series resends them as they've
   been slightly modified to better fit into the reorganization. Functionality is the
   same as previously posted.

This patchset is built on scsi-misc-2.6 and are intended for 2.6.30.

---

Joe Eykholt (2):
      fcoe, libfcoe: Add support for FIP. FCoE discovery and keep-alive.
      fcoe: Add a header file defining the FIP protocol for FCoE.

Robert Love (4):
      fcoe: create/destroy fcoe Rx threads on CPU hotplug events
      fcoe: Use per-CPU kernel function for dev_stats instead of an array
      fcoe: Use percpu kernel funcs for struct fcoe_percpu_s
      fcoe: Initialize all possilbe skb_queue(s) when module is loaded

Vasu Dev (8):
      fcoe, libfc: fix double fcoe_softc memory alloc
      fcoe: cleans up libfcoe.h and adds fcoe.h for fcoe module
      fcoe: moves common FCoE library API functions to libfcoe module
      fcoe, libfc, scsi: adds libfcoe module
      fcoe: renames libfcoe.c to fcoe.c as the only fcoe module file
      fcoe: removes default sw transport code file fcoe_sw.c
      fcoe: removes fc_transport_fcoe.[ch] code files
      fcoe: prep work to completely remove fc_transport_fcoe code


 drivers/scsi/Kconfig                  |    8 
 drivers/scsi/Makefile                 |    1 
 drivers/scsi/fcoe/Makefile            |    8 
 drivers/scsi/fcoe/fc_transport_fcoe.c |  443 -------
 drivers/scsi/fcoe/fcoe.c              | 1865 ++++++++++++++++++++++++++++
 drivers/scsi/fcoe/fcoe.h              |   75 +
 drivers/scsi/fcoe/fcoe_sw.c           |  561 --------
 drivers/scsi/fcoe/libfcoe.c           | 2216 +++++++++++++++------------------
 drivers/scsi/libfc/fc_fcp.c           |   10 
 drivers/scsi/libfc/fc_lport.c         |   11 
 include/scsi/fc/fc_fip.h              |  237 ++++
 include/scsi/fc_transport_fcoe.h      |   54 -
 include/scsi/libfc.h                  |   45 +
 include/scsi/libfcoe.h                |  227 ++-
 14 files changed, 3396 insertions(+), 2365 deletions(-)
 delete mode 100644 drivers/scsi/fcoe/fc_transport_fcoe.c
 create mode 100644 drivers/scsi/fcoe/fcoe.c
 create mode 100644 drivers/scsi/fcoe/fcoe.h
 delete mode 100644 drivers/scsi/fcoe/fcoe_sw.c
 create mode 100644 include/scsi/fc/fc_fip.h
 delete mode 100644 include/scsi/fc_transport_fcoe.h

-- 
//Rob

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH 01/14] fcoe: Initialize all possilbe skb_queue(s) when module is loaded
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
@ 2009-03-17 18:41 ` Robert Love
  2009-03-17 18:41 ` [PATCH 02/14] fcoe: Use percpu kernel funcs for struct fcoe_percpu_s Robert Love
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:41 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

Currently the skb_queue is initialized every time the associated
CPU goes online. This patch has libfcoe initializing the skb_queue
for all possible CPUs when the module is loaded.

This patch also re-orders some declarations in the fcoe_rcv()
function so the structure declarations are grouped before
the primitive declarations.

Lastly, this patch converts all CPU indicies to use unsigned int
since CPU indicies should not be negative.

Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/libfcoe.c |   21 ++++++++++++---------
 include/scsi/libfcoe.h      |    2 +-
 2 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 0d6f5be..3865d93 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -97,7 +97,7 @@ static struct notifier_block fcoe_cpu_notifier = {
  *
  * Returns: none
  */
-static void fcoe_create_percpu_data(int cpu)
+static void fcoe_create_percpu_data(unsigned int cpu)
 {
 	struct fc_lport *lp;
 	struct fcoe_softc *fc;
@@ -121,7 +121,7 @@ static void fcoe_create_percpu_data(int cpu)
  *
  * Retuns: none
  */
-static void fcoe_destroy_percpu_data(int cpu)
+static void fcoe_destroy_percpu_data(unsigned int cpu)
 {
 	struct fc_lport *lp;
 	struct fcoe_softc *fc;
@@ -183,9 +183,9 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 	struct fcoe_softc *fc;
 	struct fcoe_dev_stats *stats;
 	struct fc_frame_header *fh;
-	unsigned short oxid;
-	int cpu_idx;
 	struct fcoe_percpu_s *fps;
+	unsigned short oxid;
+	unsigned int cpu_idx;
 
 	fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
 	lp = fc->lp;
@@ -292,7 +292,7 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
 {
 	struct fcoe_percpu_s *fps;
 	struct page *page;
-	int cpu_idx;
+	unsigned int cpu_idx;
 
 	cpu_idx = get_cpu();
 	fps = fcoe_percpu[cpu_idx];
@@ -1366,10 +1366,9 @@ EXPORT_SYMBOL_GPL(fcoe_libfc_config);
  */
 static int __init fcoe_init(void)
 {
-	int cpu;
+	unsigned int cpu;
 	struct fcoe_percpu_s *p;
 
-
 	INIT_LIST_HEAD(&fcoe_hostlist);
 	rwlock_init(&fcoe_hostlist_lock);
 
@@ -1377,6 +1376,12 @@ static int __init fcoe_init(void)
 	register_cpu_notifier(&fcoe_cpu_notifier);
 #endif /* CONFIG_HOTPLUG_CPU */
 
+	for_each_possible_cpu(cpu) {
+		p = fcoe_percpu[cpu];
+		p->cpu = cpu;
+		skb_queue_head_init(&p->fcoe_rx_list);
+	}
+
 	/*
 	 * initialize per CPU interrupt thread
 	 */
@@ -1392,9 +1397,7 @@ static int __init fcoe_init(void)
 			 * initialize the semaphore and skb queue head
 			 */
 			if (likely(!IS_ERR(p->thread))) {
-				p->cpu = cpu;
 				fcoe_percpu[cpu] = p;
-				skb_queue_head_init(&p->fcoe_rx_list);
 				kthread_bind(p->thread, cpu);
 				wake_up_process(p->thread);
 			} else {
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index c41f7d0..124dc5b 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -29,7 +29,7 @@
  * this percpu struct for fcoe
  */
 struct fcoe_percpu_s {
-	int		cpu;
+	unsigned int cpu;
 	struct task_struct *thread;
 	struct sk_buff_head fcoe_rx_list;
 	struct page *crc_eof_page;


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 02/14] fcoe: Use percpu kernel funcs for struct fcoe_percpu_s
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
  2009-03-17 18:41 ` [PATCH 01/14] fcoe: Initialize all possilbe skb_queue(s) when module is loaded Robert Love
@ 2009-03-17 18:41 ` Robert Love
  2009-03-17 18:41 ` [PATCH 03/14] fcoe: Use per-CPU kernel function for dev_stats instead of an array Robert Love
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:41 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

Convert fcoe_percpu array to use the per-cpu variables
that the kernel provides. Use the kernel's functions to
access this structure.

The cpu member of the fcoe_percpu_s is no longer needed,
so this patch removes it too.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/libfcoe.c |   94 ++++++++++++++++++++-----------------------
 include/scsi/libfcoe.h      |    1 
 2 files changed, 43 insertions(+), 52 deletions(-)

diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 3865d93..d22275b 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -65,7 +65,7 @@ MODULE_LICENSE("GPL");
 LIST_HEAD(fcoe_hostlist);
 DEFINE_RWLOCK(fcoe_hostlist_lock);
 DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
-struct fcoe_percpu_s *fcoe_percpu[NR_CPUS];
+DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
 
 
 /* Function Prototyes */
@@ -226,6 +226,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 	fr->fr_dev = lp;
 	fr->ptype = ptype;
 	cpu_idx = 0;
+
 #ifdef CONFIG_SMP
 	/*
 	 * The incoming frame exchange id(oxid) is ANDed with num of online
@@ -235,10 +236,12 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 	 * initialize to first online cpu index.
 	 */
 	cpu_idx = oxid & (num_online_cpus() - 1);
-	if (!fcoe_percpu[cpu_idx] || !cpu_online(cpu_idx))
+	if (!cpu_online(cpu_idx))
 		cpu_idx = first_cpu(cpu_online_map);
+
 #endif
-	fps = fcoe_percpu[cpu_idx];
+
+	fps = &per_cpu(fcoe_percpu, cpu_idx);
 
 	spin_lock_bh(&fps->fcoe_rx_list.lock);
 	__skb_queue_tail(&fps->fcoe_rx_list, skb);
@@ -292,15 +295,13 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
 {
 	struct fcoe_percpu_s *fps;
 	struct page *page;
-	unsigned int cpu_idx;
 
-	cpu_idx = get_cpu();
-	fps = fcoe_percpu[cpu_idx];
+	fps = &get_cpu_var(fcoe_percpu);
 	page = fps->crc_eof_page;
 	if (!page) {
 		page = alloc_page(GFP_ATOMIC);
 		if (!page) {
-			put_cpu();
+			put_cpu_var(fcoe_percpu);
 			return -ENOMEM;
 		}
 		fps->crc_eof_page = page;
@@ -320,7 +321,7 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
 		fps->crc_eof_offset = 0;
 		put_page(page);
 	}
-	put_cpu();
+	put_cpu_var(fcoe_percpu);
 	return 0;
 }
 
@@ -1122,30 +1123,28 @@ EXPORT_SYMBOL_GPL(fcoe_link_ok);
  */
 void fcoe_percpu_clean(struct fc_lport *lp)
 {
-	int idx;
 	struct fcoe_percpu_s *pp;
 	struct fcoe_rcv_info *fr;
 	struct sk_buff_head *list;
 	struct sk_buff *skb, *next;
 	struct sk_buff *head;
+	unsigned int cpu;
 
-	for (idx = 0; idx < NR_CPUS; idx++) {
-		if (fcoe_percpu[idx]) {
-			pp = fcoe_percpu[idx];
-			spin_lock_bh(&pp->fcoe_rx_list.lock);
-			list = &pp->fcoe_rx_list;
-			head = list->next;
-			for (skb = head; skb != (struct sk_buff *)list;
-			     skb = next) {
-				next = skb->next;
-				fr = fcoe_dev_from_skb(skb);
-				if (fr->fr_dev == lp) {
-					__skb_unlink(skb, list);
-					kfree_skb(skb);
-				}
+	for_each_possible_cpu(cpu) {
+		pp = &per_cpu(fcoe_percpu, cpu);
+		spin_lock_bh(&pp->fcoe_rx_list.lock);
+		list = &pp->fcoe_rx_list;
+		head = list->next;
+		for (skb = head; skb != (struct sk_buff *)list;
+		     skb = next) {
+			next = skb->next;
+			fr = fcoe_dev_from_skb(skb);
+			if (fr->fr_dev == lp) {
+				__skb_unlink(skb, list);
+				kfree_skb(skb);
 			}
-			spin_unlock_bh(&pp->fcoe_rx_list.lock);
 		}
+		spin_unlock_bh(&pp->fcoe_rx_list.lock);
 	}
 }
 EXPORT_SYMBOL_GPL(fcoe_percpu_clean);
@@ -1377,8 +1376,7 @@ static int __init fcoe_init(void)
 #endif /* CONFIG_HOTPLUG_CPU */
 
 	for_each_possible_cpu(cpu) {
-		p = fcoe_percpu[cpu];
-		p->cpu = cpu;
+		p = &per_cpu(fcoe_percpu, cpu);
 		skb_queue_head_init(&p->fcoe_rx_list);
 	}
 
@@ -1386,24 +1384,19 @@ static int __init fcoe_init(void)
 	 * initialize per CPU interrupt thread
 	 */
 	for_each_online_cpu(cpu) {
-		p = kzalloc(sizeof(struct fcoe_percpu_s), GFP_KERNEL);
-		if (p) {
-			p->thread = kthread_create(fcoe_percpu_receive_thread,
-						   (void *)p,
-						   "fcoethread/%d", cpu);
+		p = &per_cpu(fcoe_percpu, cpu);
+		p->thread = kthread_create(fcoe_percpu_receive_thread,
+					   (void *)p, "fcoethread/%d", cpu);
 
-			/*
-			 * if there is no error then bind the thread to the cpu
-			 * initialize the semaphore and skb queue head
-			 */
-			if (likely(!IS_ERR(p->thread))) {
-				fcoe_percpu[cpu] = p;
-				kthread_bind(p->thread, cpu);
-				wake_up_process(p->thread);
-			} else {
-				fcoe_percpu[cpu] = NULL;
-				kfree(p);
-			}
+		/*
+		 * If there is no error then bind the thread to the CPU
+		 * and wake it up.
+		 */
+		if (!IS_ERR(p->thread)) {
+			kthread_bind(p->thread, cpu);
+			wake_up_process(p->thread);
+		} else {
+			p->thread = NULL;
 		}
 	}
 
@@ -1432,7 +1425,7 @@ module_init(fcoe_init);
  */
 static void __exit fcoe_exit(void)
 {
-	u32 idx;
+	unsigned int cpu;
 	struct fcoe_softc *fc, *tmp;
 	struct fcoe_percpu_s *p;
 	struct sk_buff *skb;
@@ -1454,17 +1447,16 @@ static void __exit fcoe_exit(void)
 	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
 		fcoe_transport_release(fc->real_dev);
 
-	for (idx = 0; idx < NR_CPUS; idx++) {
-		if (fcoe_percpu[idx]) {
-			kthread_stop(fcoe_percpu[idx]->thread);
-			p = fcoe_percpu[idx];
+	for_each_possible_cpu(cpu) {
+		p = &per_cpu(fcoe_percpu, cpu);
+		if (p->thread) {
+			kthread_stop(p->thread);
 			spin_lock_bh(&p->fcoe_rx_list.lock);
 			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
 				kfree_skb(skb);
 			spin_unlock_bh(&p->fcoe_rx_list.lock);
-			if (fcoe_percpu[idx]->crc_eof_page)
-				put_page(fcoe_percpu[idx]->crc_eof_page);
-			kfree(fcoe_percpu[idx]);
+			if (p->crc_eof_page)
+				put_page(p->crc_eof_page);
 		}
 	}
 
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index 124dc5b..1ad4f93 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -29,7 +29,6 @@
  * this percpu struct for fcoe
  */
 struct fcoe_percpu_s {
-	unsigned int cpu;
 	struct task_struct *thread;
 	struct sk_buff_head fcoe_rx_list;
 	struct page *crc_eof_page;


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 03/14] fcoe: Use per-CPU kernel function for dev_stats instead of an array
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
  2009-03-17 18:41 ` [PATCH 01/14] fcoe: Initialize all possilbe skb_queue(s) when module is loaded Robert Love
  2009-03-17 18:41 ` [PATCH 02/14] fcoe: Use percpu kernel funcs for struct fcoe_percpu_s Robert Love
@ 2009-03-17 18:41 ` Robert Love
  2009-03-31 22:51   ` [PATCH 3/14 v2] " Robert Love
  2009-03-17 18:41 ` [PATCH 04/14] fcoe: create/destroy fcoe Rx threads on CPU hotplug events Robert Love
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:41 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

Remove the hotplug creation of dev_stats, we allocate for all possible CPUs
now when we allocate the lport.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/fcoe_sw.c   |   13 +---
 drivers/scsi/fcoe/libfcoe.c   |  143 +++++------------------------------------
 drivers/scsi/libfc/fc_fcp.c   |    8 +-
 drivers/scsi/libfc/fc_lport.c |   11 ++-
 include/scsi/libfc.h          |   23 ++++++-
 5 files changed, 53 insertions(+), 145 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c
index 2bbbe3c..a675390 100644
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -113,8 +113,6 @@ static struct scsi_host_template fcoe_sw_shost_template = {
  */
 static int fcoe_sw_lport_config(struct fc_lport *lp)
 {
-	int i = 0;
-
 	lp->link_up = 0;
 	lp->qfull = 0;
 	lp->max_retry_count = 3;
@@ -123,12 +121,7 @@ static int fcoe_sw_lport_config(struct fc_lport *lp)
 	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
 			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
 
-	/*
-	 * allocate per cpu stats block
-	 */
-	for_each_online_cpu(i)
-		lp->dev_stats[i] = kzalloc(sizeof(struct fcoe_dev_stats),
-					   GFP_KERNEL);
+	fc_lport_init_stats(lp);
 
 	/* lport fc_lport related configuration */
 	fc_lport_config(lp);
@@ -311,7 +304,6 @@ static inline int fcoe_sw_em_config(struct fc_lport *lp)
  */
 static int fcoe_sw_destroy(struct net_device *netdev)
 {
-	int cpu;
 	struct fc_lport *lp = NULL;
 	struct fcoe_softc *fc;
 	u8 flogi_maddr[ETH_ALEN];
@@ -363,8 +355,7 @@ static int fcoe_sw_destroy(struct net_device *netdev)
 	fcoe_clean_pending_queue(lp);
 
 	/* Free memory used by statistical counters */
-	for_each_online_cpu(cpu)
-		kfree(lp->dev_stats[cpu]);
+	fc_lport_free_stats(lp);
 
 	/* Release the net_device and Scsi_Host */
 	dev_put(fc->real_dev);
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index d22275b..648a2fc 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -71,9 +71,6 @@ DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
 /* Function Prototyes */
 static int fcoe_check_wait_queue(struct fc_lport *);
 static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
-#ifdef CONFIG_HOTPLUG_CPU
-static int fcoe_cpu_callback(struct notifier_block *, ulong, void *);
-#endif /* CONFIG_HOTPLUG_CPU */
 static int fcoe_device_notification(struct notifier_block *, ulong, void *);
 static void fcoe_dev_setup(void);
 static void fcoe_dev_cleanup(void);
@@ -83,87 +80,6 @@ static struct notifier_block fcoe_notifier = {
 	.notifier_call = fcoe_device_notification,
 };
 
-
-#ifdef CONFIG_HOTPLUG_CPU
-static struct notifier_block fcoe_cpu_notifier = {
-	.notifier_call = fcoe_cpu_callback,
-};
-
-/**
- * fcoe_create_percpu_data() - creates the associated cpu data
- * @cpu: index for the cpu where fcoe cpu data will be created
- *
- * create percpu stats block, from cpu add notifier
- *
- * Returns: none
- */
-static void fcoe_create_percpu_data(unsigned int cpu)
-{
-	struct fc_lport *lp;
-	struct fcoe_softc *fc;
-
-	write_lock_bh(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		lp = fc->lp;
-		if (lp->dev_stats[cpu] == NULL)
-			lp->dev_stats[cpu] =
-				kzalloc(sizeof(struct fcoe_dev_stats),
-					GFP_KERNEL);
-	}
-	write_unlock_bh(&fcoe_hostlist_lock);
-}
-
-/**
- * fcoe_destroy_percpu_data() - destroys the associated cpu data
- * @cpu: index for the cpu where fcoe cpu data will destroyed
- *
- * destroy percpu stats block called by cpu add/remove notifier
- *
- * Retuns: none
- */
-static void fcoe_destroy_percpu_data(unsigned int cpu)
-{
-	struct fc_lport *lp;
-	struct fcoe_softc *fc;
-
-	write_lock_bh(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		lp = fc->lp;
-		kfree(lp->dev_stats[cpu]);
-		lp->dev_stats[cpu] = NULL;
-	}
-	write_unlock_bh(&fcoe_hostlist_lock);
-}
-
-/**
- * fcoe_cpu_callback() - fcoe cpu hotplug event callback
- * @nfb: callback data block
- * @action: event triggering the callback
- * @hcpu: index for the cpu of this event
- *
- * this creates or destroys per cpu data for fcoe
- *
- * Returns NOTIFY_OK always.
- */
-static int fcoe_cpu_callback(struct notifier_block *nfb, unsigned long action,
-			     void *hcpu)
-{
-	unsigned int cpu = (unsigned long)hcpu;
-
-	switch (action) {
-	case CPU_ONLINE:
-		fcoe_create_percpu_data(cpu);
-		break;
-	case CPU_DEAD:
-		fcoe_destroy_percpu_data(cpu);
-		break;
-	default:
-		break;
-	}
-	return NOTIFY_OK;
-}
-#endif /* CONFIG_HOTPLUG_CPU */
-
 /**
  * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ
  * @skb: the receive skb
@@ -181,7 +97,6 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 	struct fc_lport *lp;
 	struct fcoe_rcv_info *fr;
 	struct fcoe_softc *fc;
-	struct fcoe_dev_stats *stats;
 	struct fc_frame_header *fh;
 	struct fcoe_percpu_s *fps;
 	unsigned short oxid;
@@ -252,13 +167,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 
 	return 0;
 err:
-#ifdef CONFIG_SMP
-	stats = lp->dev_stats[smp_processor_id()];
-#else
-	stats = lp->dev_stats[0];
-#endif
-	if (stats)
-		stats->ErrorFrames++;
+	fc_lport_get_stats(lp)->ErrorFrames++;
 
 err2:
 	kfree_skb(skb);
@@ -495,11 +404,9 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 	}
 #endif
 	/* update tx stats: regardless if LLD fails */
-	stats = lp->dev_stats[smp_processor_id()];
-	if (stats) {
-		stats->TxFrames++;
-		stats->TxWords += wlen;
-	}
+	stats = fc_lport_get_stats(lp);
+	stats->TxFrames++;
+	stats->TxWords += wlen;
 
 	/* send down to lld */
 	fr_dev(fp) = lp;
@@ -565,8 +472,6 @@ int fcoe_percpu_receive_thread(void *arg)
 			continue;
 		}
 
-		stats = lp->dev_stats[smp_processor_id()];
-
 		if (unlikely(debug_fcoe)) {
 			FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p "
 			       "tail:%p end:%p sum:%d dev:%s",
@@ -593,13 +498,16 @@ int fcoe_percpu_receive_thread(void *arg)
 		hp = (struct fcoe_hdr *) skb_network_header(skb);
 		fh = (struct fc_frame_header *) skb_transport_header(skb);
 
+		stats = fc_lport_get_stats(lp);
 		if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
-			if (stats) {
-				if (stats->ErrorFrames < 5)
-					FC_DBG("unknown FCoE version %x",
-					       FC_FCOE_DECAPS_VER(hp));
-				stats->ErrorFrames++;
-			}
+			if (stats->ErrorFrames < 5)
+				printk(KERN_WARNING "FCoE version "
+				       "mismatch: The frame has "
+				       "version %x, but the "
+				       "initiator supports version "
+				       "%x\n", FC_FCOE_DECAPS_VER(hp),
+				       FC_FCOE_VER);
+			stats->ErrorFrames++;
 			kfree_skb(skb);
 			continue;
 		}
@@ -607,10 +515,8 @@ int fcoe_percpu_receive_thread(void *arg)
 		skb_pull(skb, sizeof(struct fcoe_hdr));
 		fr_len = skb->len - sizeof(struct fcoe_crc_eof);
 
-		if (stats) {
-			stats->RxFrames++;
-			stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
-		}
+		stats->RxFrames++;
+		stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
 
 		fp = (struct fc_frame *)skb;
 		fc_frame_init(fp);
@@ -885,9 +791,8 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 		if (new_link_up)
 			fc_linkup(lp);
 		else {
-			stats = lp->dev_stats[smp_processor_id()];
-			if (stats)
-				stats->LinkFailureCount++;
+			stats = fc_lport_get_stats(lp);
+			stats->LinkFailureCount++;
 			fc_linkdown(lp);
 			fcoe_clean_pending_queue(lp);
 		}
@@ -1371,10 +1276,6 @@ static int __init fcoe_init(void)
 	INIT_LIST_HEAD(&fcoe_hostlist);
 	rwlock_init(&fcoe_hostlist_lock);
 
-#ifdef CONFIG_HOTPLUG_CPU
-	register_cpu_notifier(&fcoe_cpu_notifier);
-#endif /* CONFIG_HOTPLUG_CPU */
-
 	for_each_possible_cpu(cpu) {
 		p = &per_cpu(fcoe_percpu, cpu);
 		skb_queue_head_init(&p->fcoe_rx_list);
@@ -1430,17 +1331,9 @@ static void __exit fcoe_exit(void)
 	struct fcoe_percpu_s *p;
 	struct sk_buff *skb;
 
-	/*
-	 * Stop all call back interfaces
-	 */
-#ifdef CONFIG_HOTPLUG_CPU
-	unregister_cpu_notifier(&fcoe_cpu_notifier);
-#endif /* CONFIG_HOTPLUG_CPU */
 	fcoe_dev_cleanup();
 
-	/*
-	 * stop timer
-	 */
+	/* Stop the timer */
 	del_timer_sync(&fcoe_timer);
 
 	/* releases the associated fcoe transport for each lport */
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index a5725f3..0997e8b 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -407,10 +407,12 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
 
 		if (~crc != le32_to_cpu(fr_crc(fp))) {
 crc_err:
-			stats = lp->dev_stats[smp_processor_id()];
+			stats = fc_lport_get_stats(lp);
 			stats->ErrorFrames++;
+			/* FIXME - per cpu count, not total count! */
 			if (stats->InvalidCRCCount++ < 5)
-				FC_DBG("CRC error on data frame\n");
+				printk(KERN_WARNING "CRC error on data frame for port (%6x)\n",
+				       fc_host_port_id(lp->host));
 			/*
 			 * Assume the frame is total garbage.
 			 * We may have copied it over the good part
@@ -1752,7 +1754,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
 	/*
 	 * setup the data direction
 	 */
-	stats = lp->dev_stats[smp_processor_id()];
+	stats = fc_lport_get_stats(lp);
 	if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
 		fsp->req_flags = FC_SRB_READ;
 		stats->InputRequests++;
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 7ef4450..ae6ca6a 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -267,10 +267,10 @@ EXPORT_SYMBOL(fc_get_host_speed);
 
 struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
 {
-	int i;
 	struct fc_host_statistics *fcoe_stats;
 	struct fc_lport *lp = shost_priv(shost);
 	struct timespec v0, v1;
+	unsigned int cpu;
 
 	fcoe_stats = &lp->host_stats;
 	memset(fcoe_stats, 0, sizeof(struct fc_host_statistics));
@@ -279,10 +279,11 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
 	jiffies_to_timespec(lp->boot_time, &v1);
 	fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec);
 
-	for_each_online_cpu(i) {
-		struct fcoe_dev_stats *stats = lp->dev_stats[i];
-		if (stats == NULL)
-			continue;
+	for_each_possible_cpu(cpu) {
+		struct fcoe_dev_stats *stats;
+
+		stats = percpu_ptr(lp->dev_stats, cpu);
+
 		fcoe_stats->tx_frames += stats->TxFrames;
 		fcoe_stats->tx_words += stats->TxWords;
 		fcoe_stats->rx_frames += stats->RxFrames;
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index a70eafa..4e1d394 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -22,6 +22,7 @@
 
 #include <linux/timer.h>
 #include <linux/if.h>
+#include <linux/percpu.h>
 
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_fc.h>
@@ -661,7 +662,8 @@ struct fc_lport {
 	unsigned long		boot_time;
 
 	struct fc_host_statistics host_stats;
-	struct fcoe_dev_stats	*dev_stats[NR_CPUS];
+	struct fcoe_dev_stats	*dev_stats;
+
 	u64			wwpn;
 	u64			wwnn;
 	u8			retry_count;
@@ -722,6 +724,25 @@ static inline void fc_lport_state_enter(struct fc_lport *lp,
 	lp->state = state;
 }
 
+static inline int fc_lport_init_stats(struct fc_lport *lp)
+{
+	/* allocate per cpu stats block */
+	lp->dev_stats = alloc_percpu(struct fcoe_dev_stats);
+	if (!lp->dev_stats)
+		return -ENOMEM;
+	return 0;
+}
+
+static inline void fc_lport_free_stats(struct fc_lport *lp)
+{
+	free_percpu(lp->dev_stats);
+}
+
+static inline struct fcoe_dev_stats *fc_lport_get_stats(struct fc_lport *lp)
+{
+	return per_cpu_ptr(lp->dev_stats, smp_processor_id());
+}
+
 
 /*
  * LOCAL PORT LAYER


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 04/14] fcoe: create/destroy fcoe Rx threads on CPU hotplug events
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
                   ` (2 preceding siblings ...)
  2009-03-17 18:41 ` [PATCH 03/14] fcoe: Use per-CPU kernel function for dev_stats instead of an array Robert Love
@ 2009-03-17 18:41 ` Robert Love
  2009-03-23  0:59   ` James Bottomley
  2009-03-17 18:41 ` [PATCH 05/14] fcoe: prep work to completely remove fc_transport_fcoe code Robert Love
                   ` (9 subsequent siblings)
  13 siblings, 1 reply; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:41 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

This patch adds support for dynamically created Rx threads
upon CPU hotplug events.

There were existing synchronization problems that this patch
attempts to resolve. The main problem had to do with fcoe_rcv()
running in a different context than the hotplug notifications.
This opened the possiblity that fcoe_rcv() would target a Rx
thread for a skb. However, that thread could become NULL if
the CPU was made offline.

This patch uses the Rx queue's (a skb_queue) lock to protect
the thread it's associated with and we use the 'thread' member
of the fcoe_percpu_s to determine if the thread is ready to
accept new skbs.

The patch also attempts to do a better job of cleaning up, both
if hotplug registration fails as well as when the module is
removed.

Contribution provided by Joe Eykholt <jeykholt@cisco.com> to
fix incorrect use of __cpuinitdata.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/libfcoe.c |  246 +++++++++++++++++++++++++++++++++++--------
 1 files changed, 199 insertions(+), 47 deletions(-)

diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 648a2fc..951d244 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -81,6 +81,156 @@ static struct notifier_block fcoe_notifier = {
 };
 
 /**
+ * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
+ * @cpu: cpu index for the online cpu
+ */
+static void fcoe_percpu_thread_create(unsigned int cpu)
+{
+	struct fcoe_percpu_s *p;
+	struct task_struct *thread;
+
+	p = &per_cpu(fcoe_percpu, cpu);
+
+	thread = kthread_create(fcoe_percpu_receive_thread,
+				(void *)p, "fcoethread/%d", cpu);
+
+	if (likely(!IS_ERR(p->thread))) {
+		kthread_bind(thread, cpu);
+		wake_up_process(thread);
+
+		spin_lock_bh(&p->fcoe_rx_list.lock);
+		p->thread = thread;
+		spin_unlock_bh(&p->fcoe_rx_list.lock);
+	}
+}
+
+/**
+ * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
+ * @cpu: cpu index the rx thread is to be removed
+ *
+ * Destroys a per-CPU Rx thread. Any pending skbs are moved to the
+ * current CPU's Rx thread. If the thread being destroyed is bound to
+ * the CPU processing this context the skbs will be freed.
+ */
+static void fcoe_percpu_thread_destroy(unsigned int cpu)
+{
+	struct fcoe_percpu_s *p;
+	struct task_struct *thread;
+	struct page *crc_eof;
+	struct sk_buff *skb;
+#ifdef CONFIG_SMP
+	struct fcoe_percpu_s *p0;
+	unsigned targ_cpu = smp_processor_id();
+#endif /* CONFIG_SMP */
+
+	printk(KERN_DEBUG "fcoe: Destroying receive thread for CPU %d\n", cpu);
+
+	/* Prevent any new skbs from being queued for this CPU. */
+	p = &per_cpu(fcoe_percpu, cpu);
+	spin_lock_bh(&p->fcoe_rx_list.lock);
+	thread = p->thread;
+	p->thread = NULL;
+	crc_eof = p->crc_eof_page;
+	p->crc_eof_page = NULL;
+	p->crc_eof_offset = 0;
+	spin_unlock_bh(&p->fcoe_rx_list.lock);
+
+#ifdef CONFIG_SMP
+	/*
+	 * Don't bother moving the skb's if this context is running
+	 * on the same CPU that is having its thread destroyed. This
+	 * can easily happen when the module is removed.
+	 */
+	if (cpu != targ_cpu) {
+		p0 = &per_cpu(fcoe_percpu, targ_cpu);
+		spin_lock_bh(&p0->fcoe_rx_list.lock);
+		if (p0->thread) {
+			FC_DBG("Moving frames from CPU %d to CPU %d\n",
+			       cpu, targ_cpu);
+
+			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+				__skb_queue_tail(&p0->fcoe_rx_list, skb);
+			spin_unlock_bh(&p0->fcoe_rx_list.lock);
+		} else {
+			/*
+			 * The targeted CPU is not initialized and cannot accept
+			 * new  skbs. Unlock the targeted CPU and drop the skbs
+			 * on the CPU that is going offline.
+			 */
+			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+				kfree_skb(skb);
+			spin_unlock_bh(&p0->fcoe_rx_list.lock);
+		}
+	} else {
+		/*
+		 * This scenario occurs when the module is being removed
+		 * and all threads are being destroyed. skbs will continue
+		 * to be shifted from the CPU thread that is being removed
+		 * to the CPU thread associated with the CPU that is processing
+		 * the module removal. Once there is only one CPU Rx thread it
+		 * will reach this case and we will drop all skbs and later
+		 * stop the thread.
+		 */
+		spin_lock_bh(&p->fcoe_rx_list.lock);
+		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+			kfree_skb(skb);
+		spin_unlock_bh(&p->fcoe_rx_list.lock);
+	}
+#else
+	/*
+	 * This a non-SMP scenario where the singluar Rx thread is
+	 * being removed. Free all skbs and stop the thread.
+	 */
+	spin_lock_bh(&p->fcoe_rx_list.lock);
+	while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+		kfree_skb(skb);
+	spin_unlock_bh(&p->fcoe_rx_list.lock);
+#endif
+
+	if (thread)
+		kthread_stop(thread);
+
+	if (crc_eof)
+		put_page(crc_eof);
+}
+
+/**
+ * fcoe_cpu_callback() - fcoe cpu hotplug event callback
+ * @nfb: callback data block
+ * @action: event triggering the callback
+ * @hcpu: index for the cpu of this event
+ *
+ * This creates or destroys per cpu data for fcoe
+ *
+ * Returns NOTIFY_OK always.
+ */
+static int fcoe_cpu_callback(struct notifier_block *nfb,
+			     unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		FC_DBG("CPU %x online: Create Rx thread\n", cpu);
+		fcoe_percpu_thread_create(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		FC_DBG("CPU %x offline: Remove Rx thread\n", cpu);
+		fcoe_percpu_thread_destroy(cpu);
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block fcoe_cpu_notifier = {
+	.notifier_call = fcoe_cpu_callback,
+};
+
+/**
  * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ
  * @skb: the receive skb
  * @dev: associated net device
@@ -100,7 +250,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 	struct fc_frame_header *fh;
 	struct fcoe_percpu_s *fps;
 	unsigned short oxid;
-	unsigned int cpu_idx;
+	unsigned int cpu = 0;
 
 	fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
 	lp = fc->lp;
@@ -140,25 +290,42 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 	fr = fcoe_dev_from_skb(skb);
 	fr->fr_dev = lp;
 	fr->ptype = ptype;
-	cpu_idx = 0;
 
 #ifdef CONFIG_SMP
 	/*
 	 * The incoming frame exchange id(oxid) is ANDed with num of online
-	 * cpu bits to get cpu_idx and then this cpu_idx is used for selecting
-	 * a per cpu kernel thread from fcoe_percpu. In case the cpu is
-	 * offline or no kernel thread for derived cpu_idx then cpu_idx is
-	 * initialize to first online cpu index.
+	 * cpu bits to get cpu and then this cpu is used for selecting
+	 * a per cpu kernel thread from fcoe_percpu.
 	 */
-	cpu_idx = oxid & (num_online_cpus() - 1);
-	if (!cpu_online(cpu_idx))
-		cpu_idx = first_cpu(cpu_online_map);
-
+	cpu = oxid & (num_online_cpus() - 1);
 #endif
 
-	fps = &per_cpu(fcoe_percpu, cpu_idx);
-
+	fps = &per_cpu(fcoe_percpu, cpu);
 	spin_lock_bh(&fps->fcoe_rx_list.lock);
+	if (unlikely(!fps->thread)) {
+		/*
+		 * The targeted CPU is not ready, let's target
+		 * the first CPU now. For non-SMP systems this
+		 * will check the same CPU twice.
+		 */
+		FC_DBG("CPU is online, but no receive thread ready "
+		       "for incoming skb- using first online CPU.\n");
+
+		spin_unlock_bh(&fps->fcoe_rx_list.lock);
+		cpu = first_cpu(cpu_online_map);
+		fps = &per_cpu(fcoe_percpu, cpu);
+		spin_lock_bh(&fps->fcoe_rx_list.lock);
+		if (!fps->thread) {
+			spin_unlock_bh(&fps->fcoe_rx_list.lock);
+			goto err;
+		}
+	}
+
+	/*
+	 * We now have a valid CPU that we're targeting for
+	 * this skb. We also have this receive thread locked,
+	 * so we're free to queue skbs into it's queue.
+	 */
 	__skb_queue_tail(&fps->fcoe_rx_list, skb);
 	if (fps->fcoe_rx_list.qlen == 1)
 		wake_up_process(fps->thread);
@@ -214,7 +381,7 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
 			return -ENOMEM;
 		}
 		fps->crc_eof_page = page;
-		WARN_ON(fps->crc_eof_offset != 0);
+		fps->crc_eof_offset = 0;
 	}
 
 	get_page(page);
@@ -1271,6 +1438,7 @@ EXPORT_SYMBOL_GPL(fcoe_libfc_config);
 static int __init fcoe_init(void)
 {
 	unsigned int cpu;
+	int rc = 0;
 	struct fcoe_percpu_s *p;
 
 	INIT_LIST_HEAD(&fcoe_hostlist);
@@ -1281,29 +1449,15 @@ static int __init fcoe_init(void)
 		skb_queue_head_init(&p->fcoe_rx_list);
 	}
 
-	/*
-	 * initialize per CPU interrupt thread
-	 */
-	for_each_online_cpu(cpu) {
-		p = &per_cpu(fcoe_percpu, cpu);
-		p->thread = kthread_create(fcoe_percpu_receive_thread,
-					   (void *)p, "fcoethread/%d", cpu);
+	for_each_online_cpu(cpu)
+		fcoe_percpu_thread_create(cpu);
 
-		/*
-		 * If there is no error then bind the thread to the CPU
-		 * and wake it up.
-		 */
-		if (!IS_ERR(p->thread)) {
-			kthread_bind(p->thread, cpu);
-			wake_up_process(p->thread);
-		} else {
-			p->thread = NULL;
-		}
-	}
+	/* Initialize per CPU interrupt thread */
+	rc = register_hotcpu_notifier(&fcoe_cpu_notifier);
+	if (rc)
+		goto out_free;
 
-	/*
-	 * setup link change notification
-	 */
+	/* Setup link change notification */
 	fcoe_dev_setup();
 
 	setup_timer(&fcoe_timer, fcoe_watchdog, 0);
@@ -1316,6 +1470,13 @@ static int __init fcoe_init(void)
 	fcoe_sw_init();
 
 	return 0;
+
+out_free:
+	for_each_online_cpu(cpu) {
+		fcoe_percpu_thread_destroy(cpu);
+	}
+
+	return rc;
 }
 module_init(fcoe_init);
 
@@ -1328,8 +1489,6 @@ static void __exit fcoe_exit(void)
 {
 	unsigned int cpu;
 	struct fcoe_softc *fc, *tmp;
-	struct fcoe_percpu_s *p;
-	struct sk_buff *skb;
 
 	fcoe_dev_cleanup();
 
@@ -1340,17 +1499,10 @@ static void __exit fcoe_exit(void)
 	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
 		fcoe_transport_release(fc->real_dev);
 
-	for_each_possible_cpu(cpu) {
-		p = &per_cpu(fcoe_percpu, cpu);
-		if (p->thread) {
-			kthread_stop(p->thread);
-			spin_lock_bh(&p->fcoe_rx_list.lock);
-			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-				kfree_skb(skb);
-			spin_unlock_bh(&p->fcoe_rx_list.lock);
-			if (p->crc_eof_page)
-				put_page(p->crc_eof_page);
-		}
+	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
+
+	for_each_online_cpu(cpu) {
+		fcoe_percpu_thread_destroy(cpu);
 	}
 
 	/* remove sw trasnport */


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 05/14] fcoe: prep work to completely remove fc_transport_fcoe code
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
                   ` (3 preceding siblings ...)
  2009-03-17 18:41 ` [PATCH 04/14] fcoe: create/destroy fcoe Rx threads on CPU hotplug events Robert Love
@ 2009-03-17 18:41 ` Robert Love
  2009-03-24 23:19   ` [PATCH] " Robert Love
  2009-03-27 16:03   ` [PATCH] fcoe: prep work to completely remove fc_transport_fcoe code Robert Love
  2009-03-17 18:41 ` [PATCH 06/14] fcoe: removes fc_transport_fcoe.[ch] code files Robert Love
                   ` (8 subsequent siblings)
  13 siblings, 2 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:41 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

The fcoe transport code was added for generic FCoE transport
infrastructure to allow additional offload related module loading
on demand, this is not required anymore after recently added
different offload approach by having offload related func ops
in netdev.

This patch removes fcoe transport related code use, calls functions
directly between existing libfcoe.c and fcoe_sw.c for now, for
example fcoe_sw_destroy and fcoe_sw_create calling.

The fcoe_sw.c and libfcoe.c code will be further consolidated in
later patches and then also the default fcoe sw transport code
file fcoe_sw.c will be completely removed.

The fcoe transport code files are completely removed in next
patch to keep this patch simple for reviewing.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/fcoe_sw.c |   38 ++++----------------------------------
 drivers/scsi/fcoe/libfcoe.c |   25 ++++++-------------------
 include/scsi/libfcoe.h      |    2 ++
 3 files changed, 12 insertions(+), 53 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c
index a675390..a2b3418 100644
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -36,7 +36,6 @@
 
 #include <scsi/libfc.h>
 #include <scsi/libfcoe.h>
-#include <scsi/fc_transport_fcoe.h>
 
 #define FCOE_SW_VERSION	"0.1"
 #define	FCOE_SW_NAME	"fcoesw"
@@ -302,7 +301,7 @@ static inline int fcoe_sw_em_config(struct fc_lport *lp)
  *
  * Returns: 0 if link is OK for use by FCoE.
  */
-static int fcoe_sw_destroy(struct net_device *netdev)
+int fcoe_sw_destroy(struct net_device *netdev)
 {
 	struct fc_lport *lp = NULL;
 	struct fcoe_softc *fc;
@@ -415,7 +414,7 @@ static struct libfc_function_template fcoe_sw_libfc_fcn_templ = {
  *
  * Returns : 0 on success
  */
-static int fcoe_sw_create(struct net_device *netdev)
+int fcoe_sw_create(struct net_device *netdev)
 {
 	int rc;
 	struct fc_lport *lp = NULL;
@@ -494,28 +493,7 @@ out_host_put:
 }
 
 /**
- * fcoe_sw_match() - The FCoE SW transport match function
- *
- * Returns : false always
- */
-static bool fcoe_sw_match(struct net_device *netdev)
-{
-	/* FIXME - for sw transport, always return false */
-	return false;
-}
-
-/* the sw hba fcoe transport */
-struct fcoe_transport fcoe_sw_transport = {
-	.name = "fcoesw",
-	.create = fcoe_sw_create,
-	.destroy = fcoe_sw_destroy,
-	.match = fcoe_sw_match,
-	.vendor = 0x0,
-	.device = 0xffff,
-};
-
-/**
- * fcoe_sw_init() - Registers fcoe_sw_transport
+ * fcoe_sw_init() - attach to scsi transport
  *
  * Returns : 0 on success
  */
@@ -530,23 +508,15 @@ int __init fcoe_sw_init(void)
 		return -ENODEV;
 	}
 
-	mutex_init(&fcoe_sw_transport.devlock);
-	INIT_LIST_HEAD(&fcoe_sw_transport.devlist);
-
-	/* register sw transport */
-	fcoe_transport_register(&fcoe_sw_transport);
 	return 0;
 }
 
 /**
- * fcoe_sw_exit() - Unregisters fcoe_sw_transport
+ * fcoe_sw_exit() - dummy for now
  *
  * Returns : 0 on success
  */
 int __exit fcoe_sw_exit(void)
 {
-	/* dettach the transport */
-	fc_release_transport(scsi_transport_fcoe_sw);
-	fcoe_transport_unregister(&fcoe_sw_transport);
 	return 0;
 }
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 951d244..334db95 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -44,7 +44,6 @@
 #include <scsi/libfc.h>
 #include <scsi/fc_frame.h>
 #include <scsi/libfcoe.h>
-#include <scsi/fc_transport_fcoe.h>
 
 static int debug_fcoe;
 
@@ -1081,10 +1080,9 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
 		rc = -ENODEV;
 		goto out_putdev;
 	}
-	/* pass to transport */
-	rc = fcoe_transport_release(netdev);
+	rc = fcoe_sw_destroy(netdev);
 	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_transport_release(%s) failed\n",
+		printk(KERN_ERR "fcoe: fcoe_sw_destroy(%s) failed\n",
 		       netdev->name);
 		rc = -EIO;
 		goto out_putdev;
@@ -1121,10 +1119,9 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 	}
 	fcoe_ethdrv_get(netdev);
 
-	/* pass to transport */
-	rc = fcoe_transport_attach(netdev);
+	rc = fcoe_sw_create(netdev);
 	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_transport_attach(%s) failed\n",
+		printk(KERN_ERR "fcoe: fcoe_sw_create(%s) failed\n",
 		       netdev->name);
 		fcoe_ethdrv_put(netdev);
 		rc = -EIO;
@@ -1429,10 +1426,6 @@ EXPORT_SYMBOL_GPL(fcoe_libfc_config);
 /**
  * fcoe_init() - fcoe module loading initialization
  *
- * Initialization routine
- * 1. Will create fc transport software structure
- * 2. initialize the link list of port information structure
- *
  * Returns 0 on success, negative on failure
  */
 static int __init fcoe_init(void)
@@ -1464,9 +1457,6 @@ static int __init fcoe_init(void)
 
 	mod_timer(&fcoe_timer, jiffies + (10 * HZ));
 
-	/* initiatlize the fcoe transport */
-	fcoe_transport_init();
-
 	fcoe_sw_init();
 
 	return 0;
@@ -1495,9 +1485,9 @@ static void __exit fcoe_exit(void)
 	/* Stop the timer */
 	del_timer_sync(&fcoe_timer);
 
-	/* releases the associated fcoe transport for each lport */
+	/* releases the associated fcoe hosts */
 	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
-		fcoe_transport_release(fc->real_dev);
+		fcoe_sw_destroy(fc->real_dev);
 
 	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
 
@@ -1507,8 +1497,5 @@ static void __exit fcoe_exit(void)
 
 	/* remove sw trasnport */
 	fcoe_sw_exit();
-
-	/* detach the transport */
-	fcoe_transport_exit();
 }
 module_exit(fcoe_exit);
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index 1ad4f93..dc64405 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -149,4 +149,6 @@ int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
 /* fcoe sw hba */
 int __init fcoe_sw_init(void);
 int __exit fcoe_sw_exit(void);
+int fcoe_sw_create(struct net_device *);
+int fcoe_sw_destroy(struct net_device *);
 #endif /* _LIBFCOE_H */


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 06/14] fcoe: removes fc_transport_fcoe.[ch] code files
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
                   ` (4 preceding siblings ...)
  2009-03-17 18:41 ` [PATCH 05/14] fcoe: prep work to completely remove fc_transport_fcoe code Robert Love
@ 2009-03-17 18:41 ` Robert Love
  2009-03-24 23:24   ` [PATCH] " Robert Love
  2009-03-27 16:05   ` Robert Love
  2009-03-17 18:42 ` [PATCH 07/14] fcoe: removes default sw transport code file fcoe_sw.c Robert Love
                   ` (7 subsequent siblings)
  13 siblings, 2 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:41 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

Remove unused fc_transport_fcoe.c and fc_transport_fcoe.h
files.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/Makefile            |    3 
 drivers/scsi/fcoe/fc_transport_fcoe.c |  443 ---------------------------------
 include/scsi/fc_transport_fcoe.h      |   54 ----
 3 files changed, 1 insertions(+), 499 deletions(-)
 delete mode 100644 drivers/scsi/fcoe/fc_transport_fcoe.c
 delete mode 100644 include/scsi/fc_transport_fcoe.h

diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index b78da06..e950adf 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -4,5 +4,4 @@ obj-$(CONFIG_FCOE) += fcoe.o
 
 fcoe-y := \
 	libfcoe.o \
-	fcoe_sw.o \
-	fc_transport_fcoe.o
+	fcoe_sw.o
diff --git a/drivers/scsi/fcoe/fc_transport_fcoe.c b/drivers/scsi/fcoe/fc_transport_fcoe.c
deleted file mode 100644
index 8862758..0000000
--- a/drivers/scsi/fcoe/fc_transport_fcoe.c
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Maintained at www.Open-FCoE.org
- */
-
-#include <linux/pci.h>
-#include <scsi/libfcoe.h>
-#include <scsi/fc_transport_fcoe.h>
-
-/* internal fcoe transport */
-struct fcoe_transport_internal {
-	struct fcoe_transport *t;
-	struct net_device *netdev;
-	struct list_head list;
-};
-
-/* fcoe transports list and its lock */
-static LIST_HEAD(fcoe_transports);
-static DEFINE_MUTEX(fcoe_transports_lock);
-
-/**
- * fcoe_transport_default() - Returns ptr to the default transport fcoe_sw
- */
-struct fcoe_transport *fcoe_transport_default(void)
-{
-	return &fcoe_sw_transport;
-}
-
-/**
- * fcoe_transport_to_pcidev() - get the pci dev from a netdev
- * @netdev: the netdev that pci dev will be retrived from
- *
- * Returns: NULL or the corrsponding pci_dev
- */
-struct pci_dev *fcoe_transport_pcidev(const struct net_device *netdev)
-{
-	if (!netdev->dev.parent)
-		return NULL;
-	return to_pci_dev(netdev->dev.parent);
-}
-
-/**
- * fcoe_transport_device_lookup() - Lookup a transport
- * @netdev: the netdev the transport to be attached to
- *
- * This will look for existing offload driver, if not found, it falls back to
- * the default sw hba (fcoe_sw) as its fcoe transport.
- *
- * Returns: 0 for success
- */
-static struct fcoe_transport_internal *
-fcoe_transport_device_lookup(struct fcoe_transport *t,
-			     struct net_device *netdev)
-{
-	struct fcoe_transport_internal *ti;
-
-	/* assign the transpor to this device */
-	mutex_lock(&t->devlock);
-	list_for_each_entry(ti, &t->devlist, list) {
-		if (ti->netdev == netdev) {
-			mutex_unlock(&t->devlock);
-			return ti;
-		}
-	}
-	mutex_unlock(&t->devlock);
-	return NULL;
-}
-/**
- * fcoe_transport_device_add() - Assign a transport to a device
- * @netdev: the netdev the transport to be attached to
- *
- * This will look for existing offload driver, if not found, it falls back to
- * the default sw hba (fcoe_sw) as its fcoe transport.
- *
- * Returns: 0 for success
- */
-static int fcoe_transport_device_add(struct fcoe_transport *t,
-				     struct net_device *netdev)
-{
-	struct fcoe_transport_internal *ti;
-
-	ti = fcoe_transport_device_lookup(t, netdev);
-	if (ti) {
-		printk(KERN_DEBUG "fcoe_transport_device_add:"
-		       "device %s is already added to transport %s\n",
-		       netdev->name, t->name);
-		return -EEXIST;
-	}
-	/* allocate an internal struct to host the netdev and the list */
-	ti = kzalloc(sizeof(*ti), GFP_KERNEL);
-	if (!ti)
-		return -ENOMEM;
-
-	ti->t = t;
-	ti->netdev = netdev;
-	INIT_LIST_HEAD(&ti->list);
-	dev_hold(ti->netdev);
-
-	mutex_lock(&t->devlock);
-	list_add(&ti->list, &t->devlist);
-	mutex_unlock(&t->devlock);
-
-	printk(KERN_DEBUG "fcoe_transport_device_add:"
-		       "device %s added to transport %s\n",
-		       netdev->name, t->name);
-
-	return 0;
-}
-
-/**
- * fcoe_transport_device_remove() - Remove a device from its transport
- * @netdev: the netdev the transport to be attached to
- *
- * This removes the device from the transport so the given transport will
- * not manage this device any more
- *
- * Returns: 0 for success
- */
-static int fcoe_transport_device_remove(struct fcoe_transport *t,
-					struct net_device *netdev)
-{
-	struct fcoe_transport_internal *ti;
-
-	ti = fcoe_transport_device_lookup(t, netdev);
-	if (!ti) {
-		printk(KERN_DEBUG "fcoe_transport_device_remove:"
-		       "device %s is not managed by transport %s\n",
-		       netdev->name, t->name);
-		return -ENODEV;
-	}
-	mutex_lock(&t->devlock);
-	list_del(&ti->list);
-	mutex_unlock(&t->devlock);
-	printk(KERN_DEBUG "fcoe_transport_device_remove:"
-	       "device %s removed from transport %s\n",
-	       netdev->name, t->name);
-	dev_put(ti->netdev);
-	kfree(ti);
-	return 0;
-}
-
-/**
- * fcoe_transport_device_remove_all() - Remove all from transport devlist
- *
- * This removes the device from the transport so the given transport will
- * not manage this device any more
- *
- * Returns: 0 for success
- */
-static void fcoe_transport_device_remove_all(struct fcoe_transport *t)
-{
-	struct fcoe_transport_internal *ti, *tmp;
-
-	mutex_lock(&t->devlock);
-	list_for_each_entry_safe(ti, tmp, &t->devlist, list) {
-		list_del(&ti->list);
-		kfree(ti);
-	}
-	mutex_unlock(&t->devlock);
-}
-
-/**
- * fcoe_transport_match() - Use the bus device match function to match the hw
- * @t: The fcoe transport to check
- * @netdev: The netdev to match against
- *
- * This function is used to check if the given transport wants to manage the
- * input netdev. if the transports implements the match function, it will be
- * called, o.w. we just compare the pci vendor and device id.
- *
- * Returns: true for match up
- */
-static bool fcoe_transport_match(struct fcoe_transport *t,
-				 struct net_device *netdev)
-{
-	/* match transport by vendor and device id */
-	struct pci_dev *pci;
-
-	pci = fcoe_transport_pcidev(netdev);
-
-	if (pci) {
-		printk(KERN_DEBUG "fcoe_transport_match:"
-		       "%s:%x:%x -- %s:%x:%x\n",
-		       t->name, t->vendor, t->device,
-		       netdev->name, pci->vendor, pci->device);
-
-		/* if transport supports match */
-		if (t->match)
-			return t->match(netdev);
-
-		/* else just compare the vendor and device id: pci only */
-		return (t->vendor == pci->vendor) && (t->device == pci->device);
-	}
-	return false;
-}
-
-/**
- * fcoe_transport_lookup() - Check if the transport is already registered
- * @t: the transport to be looked up
- *
- * This compares the parent device (pci) vendor and device id
- *
- * Returns: NULL if not found
- *
- * TODO: return default sw transport if no other transport is found
- */
-static struct fcoe_transport *
-fcoe_transport_lookup(struct net_device *netdev)
-{
-	struct fcoe_transport *t;
-
-	mutex_lock(&fcoe_transports_lock);
-	list_for_each_entry(t, &fcoe_transports, list) {
-		if (fcoe_transport_match(t, netdev)) {
-			mutex_unlock(&fcoe_transports_lock);
-			return t;
-		}
-	}
-	mutex_unlock(&fcoe_transports_lock);
-
-	printk(KERN_DEBUG "fcoe_transport_lookup:"
-	       "use default transport for %s\n", netdev->name);
-	return fcoe_transport_default();
-}
-
-/**
- * fcoe_transport_register() - Adds a fcoe transport to the fcoe transports list
- * @t: ptr to the fcoe transport to be added
- *
- * Returns: 0 for success
- */
-int fcoe_transport_register(struct fcoe_transport *t)
-{
-	struct fcoe_transport *tt;
-
-	/* TODO - add fcoe_transport specific initialization here */
-	mutex_lock(&fcoe_transports_lock);
-	list_for_each_entry(tt, &fcoe_transports, list) {
-		if (tt == t) {
-			mutex_unlock(&fcoe_transports_lock);
-			return -EEXIST;
-		}
-	}
-	list_add_tail(&t->list, &fcoe_transports);
-	mutex_unlock(&fcoe_transports_lock);
-
-	printk(KERN_DEBUG "fcoe_transport_register:%s\n", t->name);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_transport_register);
-
-/**
- * fcoe_transport_unregister() - Remove the tranport fro the fcoe transports list
- * @t: ptr to the fcoe transport to be removed
- *
- * Returns: 0 for success
- */
-int fcoe_transport_unregister(struct fcoe_transport *t)
-{
-	struct fcoe_transport *tt, *tmp;
-
-	mutex_lock(&fcoe_transports_lock);
-	list_for_each_entry_safe(tt, tmp, &fcoe_transports, list) {
-		if (tt == t) {
-			list_del(&t->list);
-			mutex_unlock(&fcoe_transports_lock);
-			fcoe_transport_device_remove_all(t);
-			printk(KERN_DEBUG "fcoe_transport_unregister:%s\n",
-			       t->name);
-			return 0;
-		}
-	}
-	mutex_unlock(&fcoe_transports_lock);
-	return -ENODEV;
-}
-EXPORT_SYMBOL_GPL(fcoe_transport_unregister);
-
-/**
- * fcoe_load_transport_driver() - Load an offload driver by alias name
- * @netdev: the target net device
- *
- * Requests for an offload driver module as the fcoe transport, if fails, it
- * falls back to use the SW HBA (fcoe_sw) as its transport
- *
- * TODO -
- * 	1. supports only PCI device
- * 	2. needs fix for VLAn and bonding
- * 	3. pure hw fcoe hba may not have netdev
- *
- * Returns: 0 for success
- */
-int fcoe_load_transport_driver(struct net_device *netdev)
-{
-	struct pci_dev *pci;
-	struct device *dev = netdev->dev.parent;
-
-	if (fcoe_transport_lookup(netdev)) {
-		/* load default transport */
-		printk(KERN_DEBUG "fcoe: already loaded transport for %s\n",
-		       netdev->name);
-		return -EEXIST;
-	}
-
-	pci = to_pci_dev(dev);
-	if (dev->bus != &pci_bus_type) {
-		printk(KERN_DEBUG "fcoe: support noly PCI device\n");
-		return -ENODEV;
-	}
-	printk(KERN_DEBUG "fcoe: loading driver fcoe-pci-0x%04x-0x%04x\n",
-	       pci->vendor, pci->device);
-
-	return request_module("fcoe-pci-0x%04x-0x%04x",
-			      pci->vendor, pci->device);
-
-}
-EXPORT_SYMBOL_GPL(fcoe_load_transport_driver);
-
-/**
- * fcoe_transport_attach() - Load transport to fcoe
- * @netdev: the netdev the transport to be attached to
- *
- * This will look for existing offload driver, if not found, it falls back to
- * the default sw hba (fcoe_sw) as its fcoe transport.
- *
- * Returns: 0 for success
- */
-int fcoe_transport_attach(struct net_device *netdev)
-{
-	struct fcoe_transport *t;
-
-	/* find the corresponding transport */
-	t = fcoe_transport_lookup(netdev);
-	if (!t) {
-		printk(KERN_DEBUG "fcoe_transport_attach"
-		       ":no transport for %s:use %s\n",
-		       netdev->name, t->name);
-		return -ENODEV;
-	}
-	/* add to the transport */
-	if (fcoe_transport_device_add(t, netdev)) {
-		printk(KERN_DEBUG "fcoe_transport_attach"
-		       ":failed to add %s to tramsport %s\n",
-		       netdev->name, t->name);
-		return -EIO;
-	}
-	/* transport create function */
-	if (t->create)
-		t->create(netdev);
-
-	printk(KERN_DEBUG "fcoe_transport_attach:transport %s for %s\n",
-	       t->name, netdev->name);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_transport_attach);
-
-/**
- * fcoe_transport_release() - Unload transport from fcoe
- * @netdev: the net device on which fcoe is to be released
- *
- * Returns: 0 for success
- */
-int fcoe_transport_release(struct net_device *netdev)
-{
-	struct fcoe_transport *t;
-
-	/* find the corresponding transport */
-	t = fcoe_transport_lookup(netdev);
-	if (!t) {
-		printk(KERN_DEBUG "fcoe_transport_release:"
-		       "no transport for %s:use %s\n",
-		       netdev->name, t->name);
-		return -ENODEV;
-	}
-	/* remove the device from the transport */
-	if (fcoe_transport_device_remove(t, netdev)) {
-		printk(KERN_DEBUG "fcoe_transport_release:"
-		       "failed to add %s to tramsport %s\n",
-		       netdev->name, t->name);
-		return -EIO;
-	}
-	/* transport destroy function */
-	if (t->destroy)
-		t->destroy(netdev);
-
-	printk(KERN_DEBUG "fcoe_transport_release:"
-	       "device %s dettached from transport %s\n",
-	       netdev->name, t->name);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_transport_release);
-
-/**
- * fcoe_transport_init() - Initializes fcoe transport layer
- *
- * This prepares for the fcoe transport layer
- *
- * Returns: none
- */
-int __init fcoe_transport_init(void)
-{
-	INIT_LIST_HEAD(&fcoe_transports);
-	mutex_init(&fcoe_transports_lock);
-	return 0;
-}
-
-/**
- * fcoe_transport_exit() - Cleans up the fcoe transport layer
- *
- * This cleans up the fcoe transport layer. removing any transport on the list,
- * note that the transport destroy func is not called here.
- *
- * Returns: none
- */
-int __exit fcoe_transport_exit(void)
-{
-	struct fcoe_transport *t, *tmp;
-
-	mutex_lock(&fcoe_transports_lock);
-	list_for_each_entry_safe(t, tmp, &fcoe_transports, list) {
-		list_del(&t->list);
-		mutex_unlock(&fcoe_transports_lock);
-		fcoe_transport_device_remove_all(t);
-		mutex_lock(&fcoe_transports_lock);
-	}
-	mutex_unlock(&fcoe_transports_lock);
-	return 0;
-}
diff --git a/include/scsi/fc_transport_fcoe.h b/include/scsi/fc_transport_fcoe.h
deleted file mode 100644
index 8dca2af..0000000
--- a/include/scsi/fc_transport_fcoe.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef FC_TRANSPORT_FCOE_H
-#define FC_TRANSPORT_FCOE_H
-
-#include <linux/device.h>
-#include <linux/netdevice.h>
-#include <scsi/scsi_host.h>
-#include <scsi/libfc.h>
-
-/**
- * struct fcoe_transport - FCoE transport struct for generic transport
- * for Ethernet devices as well as pure HBAs
- *
- * @name: name for thsi transport
- * @bus: physical bus type (pci_bus_type)
- * @driver: physical bus driver for network device
- * @create: entry create function
- * @destroy: exit destroy function
- * @list: list of transports
- */
-struct fcoe_transport {
-	char *name;
-	unsigned short vendor;
-	unsigned short device;
-	struct bus_type *bus;
-	struct device_driver *driver;
-	int (*create)(struct net_device *device);
-	int (*destroy)(struct net_device *device);
-	bool (*match)(struct net_device *device);
-	struct list_head list;
-	struct list_head devlist;
-	struct mutex devlock;
-};
-
-/**
- * MODULE_ALIAS_FCOE_PCI
- *
- * some care must be taken with this, vendor and device MUST be a hex value
- * preceded with 0x and with letters in lower case (0x12ab, not 0x12AB or 12AB)
- */
-#define MODULE_ALIAS_FCOE_PCI(vendor, device) \
-	MODULE_ALIAS("fcoe-pci-" __stringify(vendor) "-" __stringify(device))
-
-/* exported funcs */
-int fcoe_transport_attach(struct net_device *netdev);
-int fcoe_transport_release(struct net_device *netdev);
-int fcoe_transport_register(struct fcoe_transport *t);
-int fcoe_transport_unregister(struct fcoe_transport *t);
-int fcoe_load_transport_driver(struct net_device *netdev);
-int __init fcoe_transport_init(void);
-int __exit fcoe_transport_exit(void);
-
-/* fcow_sw is the default transport */
-extern struct fcoe_transport fcoe_sw_transport;
-#endif /* FC_TRANSPORT_FCOE_H */


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 07/14] fcoe: removes default sw transport code file fcoe_sw.c
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
                   ` (5 preceding siblings ...)
  2009-03-17 18:41 ` [PATCH 06/14] fcoe: removes fc_transport_fcoe.[ch] code files Robert Love
@ 2009-03-17 18:42 ` Robert Love
  2009-03-24 23:27   ` [PATCH] " Robert Love
  2009-03-27 16:06   ` Robert Love
  2009-03-17 18:42 ` [PATCH 08/14] fcoe: renames libfcoe.c to fcoe.c as the only fcoe module file Robert Love
                   ` (6 subsequent siblings)
  13 siblings, 2 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:42 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

Moves only required code from fcoe_sw.c to libfcoe.c towards having
just one source file for fcoe module, this gets rid off default sw
transport code in a separate fcoe_sw.c file.

Very minor renaming along this move, dropped _sw_ or _SW_ use
in names and replaced them by _if_ as a auxiliary interface
functions. Now some of these funcs can be removed or merged with
other func after fcoe transport is gone, but that should be
in another patch to keep this patch simple.

Now the libfcoe.c file name for fcoe module doesn't go along well,
so the libfcoe.c file renaming to fcoe.c as the only single fcoe
module file is done in next patch to keep this patch clean
and small for review.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/Makefile  |    3 
 drivers/scsi/fcoe/fcoe_sw.c |  522 -------------------------------------------
 drivers/scsi/fcoe/libfcoe.c |  489 ++++++++++++++++++++++++++++++++++++++++
 include/scsi/libfcoe.h      |    6 
 4 files changed, 481 insertions(+), 539 deletions(-)
 delete mode 100644 drivers/scsi/fcoe/fcoe_sw.c

diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index e950adf..2d9dae4 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -3,5 +3,4 @@
 obj-$(CONFIG_FCOE) += fcoe.o
 
 fcoe-y := \
-	libfcoe.o \
-	fcoe_sw.o
+	libfcoe.o
diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c
deleted file mode 100644
index a2b3418..0000000
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Maintained at www.Open-FCoE.org
- */
-
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if_vlan.h>
-#include <net/rtnetlink.h>
-
-#include <scsi/fc/fc_els.h>
-#include <scsi/fc/fc_encaps.h>
-#include <scsi/fc/fc_fs.h>
-#include <scsi/scsi_transport.h>
-#include <scsi/scsi_transport_fc.h>
-
-#include <scsi/libfc.h>
-#include <scsi/libfcoe.h>
-
-#define FCOE_SW_VERSION	"0.1"
-#define	FCOE_SW_NAME	"fcoesw"
-#define	FCOE_SW_VENDOR	"Open-FCoE.org"
-
-#define FCOE_MAX_LUN		255
-#define FCOE_MAX_FCP_TARGET	256
-
-#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
-
-#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
-#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
-
-static struct scsi_transport_template *scsi_transport_fcoe_sw;
-
-struct fc_function_template fcoe_sw_transport_function = {
-	.show_host_node_name = 1,
-	.show_host_port_name = 1,
-	.show_host_supported_classes = 1,
-	.show_host_supported_fc4s = 1,
-	.show_host_active_fc4s = 1,
-	.show_host_maxframe_size = 1,
-
-	.show_host_port_id = 1,
-	.show_host_supported_speeds = 1,
-	.get_host_speed = fc_get_host_speed,
-	.show_host_speed = 1,
-	.show_host_port_type = 1,
-	.get_host_port_state = fc_get_host_port_state,
-	.show_host_port_state = 1,
-	.show_host_symbolic_name = 1,
-
-	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
-	.show_rport_maxframe_size = 1,
-	.show_rport_supported_classes = 1,
-
-	.show_host_fabric_name = 1,
-	.show_starget_node_name = 1,
-	.show_starget_port_name = 1,
-	.show_starget_port_id = 1,
-	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
-	.show_rport_dev_loss_tmo = 1,
-	.get_fc_host_stats = fc_get_host_stats,
-	.issue_fc_host_lip = fcoe_reset,
-
-	.terminate_rport_io = fc_rport_terminate_io,
-};
-
-static struct scsi_host_template fcoe_sw_shost_template = {
-	.module = THIS_MODULE,
-	.name = "FCoE Driver",
-	.proc_name = FCOE_SW_NAME,
-	.queuecommand = fc_queuecommand,
-	.eh_abort_handler = fc_eh_abort,
-	.eh_device_reset_handler = fc_eh_device_reset,
-	.eh_host_reset_handler = fc_eh_host_reset,
-	.slave_alloc = fc_slave_alloc,
-	.change_queue_depth = fc_change_queue_depth,
-	.change_queue_type = fc_change_queue_type,
-	.this_id = -1,
-	.cmd_per_lun = 32,
-	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
-	.use_clustering = ENABLE_CLUSTERING,
-	.sg_tablesize = SG_ALL,
-	.max_sectors = 0xffff,
-};
-
-/**
- * fcoe_sw_lport_config() - sets up the fc_lport
- * @lp: ptr to the fc_lport
- * @shost: ptr to the parent scsi host
- *
- * Returns: 0 for success
- */
-static int fcoe_sw_lport_config(struct fc_lport *lp)
-{
-	lp->link_up = 0;
-	lp->qfull = 0;
-	lp->max_retry_count = 3;
-	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
-	lp->r_a_tov = 2 * 2 * 1000;
-	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
-			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
-
-	fc_lport_init_stats(lp);
-
-	/* lport fc_lport related configuration */
-	fc_lport_config(lp);
-
-	/* offload related configuration */
-	lp->crc_offload = 0;
-	lp->seq_offload = 0;
-	lp->lro_enabled = 0;
-	lp->lro_xid = 0;
-	lp->lso_max = 0;
-
-	return 0;
-}
-
-/**
- * fcoe_sw_netdev_config() - Set up netdev for SW FCoE
- * @lp : ptr to the fc_lport
- * @netdev : ptr to the associated netdevice struct
- *
- * Must be called after fcoe_sw_lport_config() as it will use lport mutex
- *
- * Returns : 0 for success
- */
-static int fcoe_sw_netdev_config(struct fc_lport *lp, struct net_device *netdev)
-{
-	u32 mfs;
-	u64 wwnn, wwpn;
-	struct fcoe_softc *fc;
-	u8 flogi_maddr[ETH_ALEN];
-
-	/* Setup lport private data to point to fcoe softc */
-	fc = lport_priv(lp);
-	fc->lp = lp;
-	fc->real_dev = netdev;
-	fc->phys_dev = netdev;
-
-	/* Require support for get_pauseparam ethtool op. */
-	if (netdev->priv_flags & IFF_802_1Q_VLAN)
-		fc->phys_dev = vlan_dev_real_dev(netdev);
-
-	/* Do not support for bonding device */
-	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
-	    (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
-	    (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
-		return -EOPNOTSUPP;
-	}
-
-	/*
-	 * Determine max frame size based on underlying device and optional
-	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
-	 * will return 0, so do this first.
-	 */
-	mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
-				   sizeof(struct fcoe_crc_eof));
-	if (fc_set_mfs(lp, mfs))
-		return -EINVAL;
-
-	if (!fcoe_link_ok(lp))
-		lp->link_up = 1;
-
-	/* offload features support */
-	if (fc->real_dev->features & NETIF_F_SG)
-		lp->sg_supp = 1;
-
-#ifdef NETIF_F_FCOE_CRC
-	if (netdev->features & NETIF_F_FCOE_CRC) {
-		lp->crc_offload = 1;
-		printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
-		       netdev->name);
-	}
-#endif
-#ifdef NETIF_F_FSO
-	if (netdev->features & NETIF_F_FSO) {
-		lp->seq_offload = 1;
-		lp->lso_max = netdev->gso_max_size;
-		printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
-		       netdev->name, lp->lso_max);
-	}
-#endif
-	if (netdev->fcoe_ddp_xid) {
-		lp->lro_enabled = 1;
-		lp->lro_xid = netdev->fcoe_ddp_xid;
-		printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
-		       netdev->name, lp->lro_xid);
-	}
-	skb_queue_head_init(&fc->fcoe_pending_queue);
-	fc->fcoe_pending_queue_active = 0;
-
-	/* setup Source Mac Address */
-	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
-	       fc->real_dev->addr_len);
-
-	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
-	fc_set_wwnn(lp, wwnn);
-	/* XXX - 3rd arg needs to be vlan id */
-	wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
-	fc_set_wwpn(lp, wwpn);
-
-	/*
-	 * Add FCoE MAC address as second unicast MAC address
-	 * or enter promiscuous mode if not capable of listening
-	 * for multiple unicast MACs.
-	 */
-	rtnl_lock();
-	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-	dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
-	rtnl_unlock();
-
-	/*
-	 * setup the receive function from ethernet driver
-	 * on the ethertype for the given device
-	 */
-	fc->fcoe_packet_type.func = fcoe_rcv;
-	fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
-	fc->fcoe_packet_type.dev = fc->real_dev;
-	dev_add_pack(&fc->fcoe_packet_type);
-
-	return 0;
-}
-
-/**
- * fcoe_sw_shost_config() - Sets up fc_lport->host
- * @lp : ptr to the fc_lport
- * @shost : ptr to the associated scsi host
- * @dev : device associated to scsi host
- *
- * Must be called after fcoe_sw_lport_config() and fcoe_sw_netdev_config()
- *
- * Returns : 0 for success
- */
-static int fcoe_sw_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
-				struct device *dev)
-{
-	int rc = 0;
-
-	/* lport scsi host config */
-	lp->host = shost;
-
-	lp->host->max_lun = FCOE_MAX_LUN;
-	lp->host->max_id = FCOE_MAX_FCP_TARGET;
-	lp->host->max_channel = 0;
-	lp->host->transportt = scsi_transport_fcoe_sw;
-
-	/* add the new host to the SCSI-ml */
-	rc = scsi_add_host(lp->host, dev);
-	if (rc) {
-		FC_DBG("fcoe_sw_shost_config:error on scsi_add_host\n");
-		return rc;
-	}
-	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
-		FCOE_SW_NAME, FCOE_SW_VERSION,
-		fcoe_netdev(lp)->name);
-
-	return 0;
-}
-
-/**
- * fcoe_sw_em_config() - allocates em for this lport
- * @lp: the port that em is to allocated for
- *
- * Returns : 0 on success
- */
-static inline int fcoe_sw_em_config(struct fc_lport *lp)
-{
-	BUG_ON(lp->emp);
-
-	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
-				    FCOE_MIN_XID, FCOE_MAX_XID);
-	if (!lp->emp)
-		return -ENOMEM;
-
-	return 0;
-}
-
-/**
- * fcoe_sw_destroy() - FCoE software HBA tear-down function
- * @netdev: ptr to the associated net_device
- *
- * Returns: 0 if link is OK for use by FCoE.
- */
-int fcoe_sw_destroy(struct net_device *netdev)
-{
-	struct fc_lport *lp = NULL;
-	struct fcoe_softc *fc;
-	u8 flogi_maddr[ETH_ALEN];
-
-	BUG_ON(!netdev);
-
-	printk(KERN_DEBUG "fcoe_sw_destroy:interface on %s\n",
-	       netdev->name);
-
-	lp = fcoe_hostlist_lookup(netdev);
-	if (!lp)
-		return -ENODEV;
-
-	fc = lport_priv(lp);
-
-	/* Logout of the fabric */
-	fc_fabric_logoff(lp);
-
-	/* Remove the instance from fcoe's list */
-	fcoe_hostlist_remove(lp);
-
-	/* Don't listen for Ethernet packets anymore */
-	dev_remove_pack(&fc->fcoe_packet_type);
-
-	/* Cleanup the fc_lport */
-	fc_lport_destroy(lp);
-	fc_fcp_destroy(lp);
-
-	/* Detach from the scsi-ml */
-	fc_remove_host(lp->host);
-	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);
-
-	/* Delete secondary MAC addresses */
-	rtnl_lock();
-	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
-	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
-		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
-	rtnl_unlock();
-
-	/* Free the per-CPU revieve threads */
-	fcoe_percpu_clean(lp);
-
-	/* Free existing skbs */
-	fcoe_clean_pending_queue(lp);
-
-	/* Free memory used by statistical counters */
-	fc_lport_free_stats(lp);
-
-	/* Release the net_device and Scsi_Host */
-	dev_put(fc->real_dev);
-	scsi_host_put(lp->host);
-
-	return 0;
-}
-
-/*
- * fcoe_sw_ddp_setup - calls LLD's ddp_setup through net_device
- * @lp:	the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- * @sgl: the scatterlist describing this transfer
- * @sgc: number of sg items
- *
- * Returns : 0 no ddp
- */
-static int fcoe_sw_ddp_setup(struct fc_lport *lp, u16 xid,
-			     struct scatterlist *sgl, unsigned int sgc)
-{
-	struct net_device *n = fcoe_netdev(lp);
-
-	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
-		return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
-
-	return 0;
-}
-
-/*
- * fcoe_sw_ddp_done - calls LLD's ddp_done through net_device
- * @lp:	the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- *
- * Returns : the length of data that have been completed by ddp
- */
-static int fcoe_sw_ddp_done(struct fc_lport *lp, u16 xid)
-{
-	struct net_device *n = fcoe_netdev(lp);
-
-	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
-		return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
-	return 0;
-}
-
-static struct libfc_function_template fcoe_sw_libfc_fcn_templ = {
-	.frame_send = fcoe_xmit,
-	.ddp_setup = fcoe_sw_ddp_setup,
-	.ddp_done = fcoe_sw_ddp_done,
-};
-
-/**
- * fcoe_sw_create() - this function creates the fcoe interface
- * @netdev: pointer the associated netdevice
- *
- * Creates fc_lport struct and scsi_host for lport, configures lport
- * and starts fabric login.
- *
- * Returns : 0 on success
- */
-int fcoe_sw_create(struct net_device *netdev)
-{
-	int rc;
-	struct fc_lport *lp = NULL;
-	struct fcoe_softc *fc;
-	struct Scsi_Host *shost;
-
-	BUG_ON(!netdev);
-
-	printk(KERN_DEBUG "fcoe_sw_create:interface on %s\n",
-	       netdev->name);
-
-	lp = fcoe_hostlist_lookup(netdev);
-	if (lp)
-		return -EEXIST;
-
-	shost = fcoe_host_alloc(&fcoe_sw_shost_template,
-				sizeof(struct fcoe_softc));
-	if (!shost) {
-		FC_DBG("Could not allocate host structure\n");
-		return -ENOMEM;
-	}
-	lp = shost_priv(shost);
-	fc = lport_priv(lp);
-
-	/* configure fc_lport, e.g., em */
-	rc = fcoe_sw_lport_config(lp);
-	if (rc) {
-		FC_DBG("Could not configure lport\n");
-		goto out_host_put;
-	}
-
-	/* configure lport network properties */
-	rc = fcoe_sw_netdev_config(lp, netdev);
-	if (rc) {
-		FC_DBG("Could not configure netdev for lport\n");
-		goto out_host_put;
-	}
-
-	/* configure lport scsi host properties */
-	rc = fcoe_sw_shost_config(lp, shost, &netdev->dev);
-	if (rc) {
-		FC_DBG("Could not configure shost for lport\n");
-		goto out_host_put;
-	}
-
-	/* lport exch manager allocation */
-	rc = fcoe_sw_em_config(lp);
-	if (rc) {
-		FC_DBG("Could not configure em for lport\n");
-		goto out_host_put;
-	}
-
-	/* Initialize the library */
-	rc = fcoe_libfc_config(lp, &fcoe_sw_libfc_fcn_templ);
-	if (rc) {
-		FC_DBG("Could not configure libfc for lport!\n");
-		goto out_lp_destroy;
-	}
-
-	/* add to lports list */
-	fcoe_hostlist_add(lp);
-
-	lp->boot_time = jiffies;
-
-	fc_fabric_login(lp);
-
-	dev_hold(netdev);
-
-	return rc;
-
-out_lp_destroy:
-	fc_exch_mgr_free(lp->emp); /* Free the EM */
-out_host_put:
-	scsi_host_put(lp->host);
-	return rc;
-}
-
-/**
- * fcoe_sw_init() - attach to scsi transport
- *
- * Returns : 0 on success
- */
-int __init fcoe_sw_init(void)
-{
-	/* attach to scsi transport */
-	scsi_transport_fcoe_sw =
-		fc_attach_transport(&fcoe_sw_transport_function);
-
-	if (!scsi_transport_fcoe_sw) {
-		printk(KERN_ERR "fcoe_sw_init:fc_attach_transport() failed\n");
-		return -ENODEV;
-	}
-
-	return 0;
-}
-
-/**
- * fcoe_sw_exit() - dummy for now
- *
- * Returns : 0 on success
- */
-int __exit fcoe_sw_exit(void)
-{
-	return 0;
-}
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 334db95..cbebbf7 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -56,6 +56,18 @@ static int debug_fcoe;
 
 #define FCOE_WORD_TO_BYTE  4
 
+#define FCOE_VERSION	"0.1"
+#define	FCOE_NAME	"fcoe"
+#define	FCOE_VENDOR	"Open-FCoE.org"
+
+#define FCOE_MAX_LUN		255
+#define FCOE_MAX_FCP_TARGET	256
+
+#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
+
+#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
+#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
+
 MODULE_AUTHOR("Open-FCoE.org");
 MODULE_DESCRIPTION("FCoE");
 MODULE_LICENSE("GPL");
@@ -79,6 +91,468 @@ static struct notifier_block fcoe_notifier = {
 	.notifier_call = fcoe_device_notification,
 };
 
+static struct scsi_transport_template *scsi_transport_fcoe_sw;
+
+struct fc_function_template fcoe_transport_function = {
+	.show_host_node_name = 1,
+	.show_host_port_name = 1,
+	.show_host_supported_classes = 1,
+	.show_host_supported_fc4s = 1,
+	.show_host_active_fc4s = 1,
+	.show_host_maxframe_size = 1,
+
+	.show_host_port_id = 1,
+	.show_host_supported_speeds = 1,
+	.get_host_speed = fc_get_host_speed,
+	.show_host_speed = 1,
+	.show_host_port_type = 1,
+	.get_host_port_state = fc_get_host_port_state,
+	.show_host_port_state = 1,
+	.show_host_symbolic_name = 1,
+
+	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+	.show_rport_maxframe_size = 1,
+	.show_rport_supported_classes = 1,
+
+	.show_host_fabric_name = 1,
+	.show_starget_node_name = 1,
+	.show_starget_port_name = 1,
+	.show_starget_port_id = 1,
+	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+	.show_rport_dev_loss_tmo = 1,
+	.get_fc_host_stats = fc_get_host_stats,
+	.issue_fc_host_lip = fcoe_reset,
+
+	.terminate_rport_io = fc_rport_terminate_io,
+};
+
+static struct scsi_host_template fcoe_shost_template = {
+	.module = THIS_MODULE,
+	.name = "FCoE Driver",
+	.proc_name = FCOE_NAME,
+	.queuecommand = fc_queuecommand,
+	.eh_abort_handler = fc_eh_abort,
+	.eh_device_reset_handler = fc_eh_device_reset,
+	.eh_host_reset_handler = fc_eh_host_reset,
+	.slave_alloc = fc_slave_alloc,
+	.change_queue_depth = fc_change_queue_depth,
+	.change_queue_type = fc_change_queue_type,
+	.this_id = -1,
+	.cmd_per_lun = 32,
+	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
+	.use_clustering = ENABLE_CLUSTERING,
+	.sg_tablesize = SG_ALL,
+	.max_sectors = 0xffff,
+};
+
+/**
+ * fcoe_lport_config() - sets up the fc_lport
+ * @lp: ptr to the fc_lport
+ * @shost: ptr to the parent scsi host
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_lport_config(struct fc_lport *lp)
+{
+	lp->link_up = 0;
+	lp->qfull = 0;
+	lp->max_retry_count = 3;
+	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
+	lp->r_a_tov = 2 * 2 * 1000;
+	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+
+	fc_lport_init_stats(lp);
+
+	/* lport fc_lport related configuration */
+	fc_lport_config(lp);
+
+	/* offload related configuration */
+	lp->crc_offload = 0;
+	lp->seq_offload = 0;
+	lp->lro_enabled = 0;
+	lp->lro_xid = 0;
+	lp->lso_max = 0;
+
+	return 0;
+}
+
+/**
+ * fcoe_netdev_config() - Set up netdev for SW FCoE
+ * @lp : ptr to the fc_lport
+ * @netdev : ptr to the associated netdevice struct
+ *
+ * Must be called after fcoe_lport_config() as it will use lport mutex
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
+{
+	u32 mfs;
+	u64 wwnn, wwpn;
+	struct fcoe_softc *fc;
+	u8 flogi_maddr[ETH_ALEN];
+
+	/* Setup lport private data to point to fcoe softc */
+	fc = lport_priv(lp);
+	fc->lp = lp;
+	fc->real_dev = netdev;
+	fc->phys_dev = netdev;
+
+	/* Require support for get_pauseparam ethtool op. */
+	if (netdev->priv_flags & IFF_802_1Q_VLAN)
+		fc->phys_dev = vlan_dev_real_dev(netdev);
+
+	/* Do not support for bonding device */
+	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
+	    (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
+	    (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
+		return -EOPNOTSUPP;
+	}
+
+	/*
+	 * Determine max frame size based on underlying device and optional
+	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
+	 * will return 0, so do this first.
+	 */
+	mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
+				   sizeof(struct fcoe_crc_eof));
+	if (fc_set_mfs(lp, mfs))
+		return -EINVAL;
+
+	if (!fcoe_link_ok(lp))
+		lp->link_up = 1;
+
+	/* offload features support */
+	if (fc->real_dev->features & NETIF_F_SG)
+		lp->sg_supp = 1;
+
+#ifdef NETIF_F_FCOE_CRC
+	if (netdev->features & NETIF_F_FCOE_CRC) {
+		lp->crc_offload = 1;
+		printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
+		       netdev->name);
+	}
+#endif
+#ifdef NETIF_F_FSO
+	if (netdev->features & NETIF_F_FSO) {
+		lp->seq_offload = 1;
+		lp->lso_max = netdev->gso_max_size;
+		printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
+		       netdev->name, lp->lso_max);
+	}
+#endif
+	if (netdev->fcoe_ddp_xid) {
+		lp->lro_enabled = 1;
+		lp->lro_xid = netdev->fcoe_ddp_xid;
+		printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
+		       netdev->name, lp->lro_xid);
+	}
+	skb_queue_head_init(&fc->fcoe_pending_queue);
+	fc->fcoe_pending_queue_active = 0;
+
+	/* setup Source Mac Address */
+	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
+	       fc->real_dev->addr_len);
+
+	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
+	fc_set_wwnn(lp, wwnn);
+	/* XXX - 3rd arg needs to be vlan id */
+	wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
+	fc_set_wwpn(lp, wwpn);
+
+	/*
+	 * Add FCoE MAC address as second unicast MAC address
+	 * or enter promiscuous mode if not capable of listening
+	 * for multiple unicast MACs.
+	 */
+	rtnl_lock();
+	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+	dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
+	rtnl_unlock();
+
+	/*
+	 * setup the receive function from ethernet driver
+	 * on the ethertype for the given device
+	 */
+	fc->fcoe_packet_type.func = fcoe_rcv;
+	fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+	fc->fcoe_packet_type.dev = fc->real_dev;
+	dev_add_pack(&fc->fcoe_packet_type);
+
+	return 0;
+}
+
+/**
+ * fcoe_shost_config() - Sets up fc_lport->host
+ * @lp : ptr to the fc_lport
+ * @shost : ptr to the associated scsi host
+ * @dev : device associated to scsi host
+ *
+ * Must be called after fcoe_lport_config() and fcoe_netdev_config()
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
+				struct device *dev)
+{
+	int rc = 0;
+
+	/* lport scsi host config */
+	lp->host = shost;
+
+	lp->host->max_lun = FCOE_MAX_LUN;
+	lp->host->max_id = FCOE_MAX_FCP_TARGET;
+	lp->host->max_channel = 0;
+	lp->host->transportt = scsi_transport_fcoe_sw;
+
+	/* add the new host to the SCSI-ml */
+	rc = scsi_add_host(lp->host, dev);
+	if (rc) {
+		FC_DBG("fcoe_shost_config:error on scsi_add_host\n");
+		return rc;
+	}
+	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
+		FCOE_NAME, FCOE_VERSION,
+		fcoe_netdev(lp)->name);
+
+	return 0;
+}
+
+/**
+ * fcoe_em_config() - 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)
+{
+	BUG_ON(lp->emp);
+
+	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
+				    FCOE_MIN_XID, FCOE_MAX_XID);
+	if (!lp->emp)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/**
+ * fcoe_if_destroy() - FCoE software HBA tear-down function
+ * @netdev: ptr to the associated net_device
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ */
+static int fcoe_if_destroy(struct net_device *netdev)
+{
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+	u8 flogi_maddr[ETH_ALEN];
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_if_destroy:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (!lp)
+		return -ENODEV;
+
+	fc = lport_priv(lp);
+
+	/* Logout of the fabric */
+	fc_fabric_logoff(lp);
+
+	/* Remove the instance from fcoe's list */
+	fcoe_hostlist_remove(lp);
+
+	/* Don't listen for Ethernet packets anymore */
+	dev_remove_pack(&fc->fcoe_packet_type);
+
+	/* Cleanup the fc_lport */
+	fc_lport_destroy(lp);
+	fc_fcp_destroy(lp);
+
+	/* Detach from the scsi-ml */
+	fc_remove_host(lp->host);
+	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);
+
+	/* Delete secondary MAC addresses */
+	rtnl_lock();
+	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
+	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+	rtnl_unlock();
+
+	/* Free the per-CPU revieve threads */
+	fcoe_percpu_clean(lp);
+
+	/* Free existing skbs */
+	fcoe_clean_pending_queue(lp);
+
+	/* Free memory used by statistical counters */
+	fc_lport_free_stats(lp);
+
+	/* Release the net_device and Scsi_Host */
+	dev_put(fc->real_dev);
+	scsi_host_put(lp->host);
+
+	return 0;
+}
+
+/*
+ * fcoe_ddp_setup - calls LLD's ddp_setup through net_device
+ * @lp:	the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ * @sgl: the scatterlist describing this transfer
+ * @sgc: number of sg items
+ *
+ * Returns : 0 no ddp
+ */
+static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid,
+			     struct scatterlist *sgl, unsigned int sgc)
+{
+	struct net_device *n = fcoe_netdev(lp);
+
+	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
+		return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
+
+	return 0;
+}
+
+/*
+ * fcoe_ddp_done - calls LLD's ddp_done through net_device
+ * @lp:	the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ *
+ * Returns : the length of data that have been completed by ddp
+ */
+static int fcoe_ddp_done(struct fc_lport *lp, u16 xid)
+{
+	struct net_device *n = fcoe_netdev(lp);
+
+	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
+		return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
+	return 0;
+}
+
+static struct libfc_function_template fcoe_libfc_fcn_templ = {
+	.frame_send = fcoe_xmit,
+	.ddp_setup = fcoe_ddp_setup,
+	.ddp_done = fcoe_ddp_done,
+};
+
+/**
+ * fcoe_if_create() - this function creates the fcoe interface
+ * @netdev: pointer the associated netdevice
+ *
+ * Creates fc_lport struct and scsi_host for lport, configures lport
+ * and starts fabric login.
+ *
+ * Returns : 0 on success
+ */
+static int fcoe_if_create(struct net_device *netdev)
+{
+	int rc;
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+	struct Scsi_Host *shost;
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_if_create:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (lp)
+		return -EEXIST;
+
+	shost = fcoe_host_alloc(&fcoe_shost_template,
+				sizeof(struct fcoe_softc));
+	if (!shost) {
+		FC_DBG("Could not allocate host structure\n");
+		return -ENOMEM;
+	}
+	lp = shost_priv(shost);
+	fc = lport_priv(lp);
+
+	/* configure fc_lport, e.g., em */
+	rc = fcoe_lport_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure lport\n");
+		goto out_host_put;
+	}
+
+	/* configure lport network properties */
+	rc = fcoe_netdev_config(lp, netdev);
+	if (rc) {
+		FC_DBG("Could not configure netdev for lport\n");
+		goto out_host_put;
+	}
+
+	/* configure lport scsi host properties */
+	rc = fcoe_shost_config(lp, shost, &netdev->dev);
+	if (rc) {
+		FC_DBG("Could not configure shost for lport\n");
+		goto out_host_put;
+	}
+
+	/* lport exch manager allocation */
+	rc = fcoe_em_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure em for lport\n");
+		goto out_host_put;
+	}
+
+	/* Initialize the library */
+	rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ);
+	if (rc) {
+		FC_DBG("Could not configure libfc for lport!\n");
+		goto out_lp_destroy;
+	}
+
+	/* add to lports list */
+	fcoe_hostlist_add(lp);
+
+	lp->boot_time = jiffies;
+
+	fc_fabric_login(lp);
+
+	dev_hold(netdev);
+
+	return rc;
+
+out_lp_destroy:
+	fc_exch_mgr_free(lp->emp); /* Free the EM */
+out_host_put:
+	scsi_host_put(lp->host);
+	return rc;
+}
+
+/**
+ * fcoe_if_init() - attach to scsi transport
+ *
+ * Returns : 0 on success
+ */
+static int __init fcoe_if_init(void)
+{
+	/* attach to scsi transport */
+	scsi_transport_fcoe_sw =
+		fc_attach_transport(&fcoe_transport_function);
+
+	if (!scsi_transport_fcoe_sw) {
+		printk(KERN_ERR "fcoe_init:fc_attach_transport() failed\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
 /**
  * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
  * @cpu: cpu index for the online cpu
@@ -1080,9 +1554,9 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
 		rc = -ENODEV;
 		goto out_putdev;
 	}
-	rc = fcoe_sw_destroy(netdev);
+	rc = fcoe_if_destroy(netdev);
 	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_sw_destroy(%s) failed\n",
+		printk(KERN_ERR "fcoe: fcoe_if_destroy(%s) failed\n",
 		       netdev->name);
 		rc = -EIO;
 		goto out_putdev;
@@ -1119,9 +1593,9 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 	}
 	fcoe_ethdrv_get(netdev);
 
-	rc = fcoe_sw_create(netdev);
+	rc = fcoe_if_create(netdev);
 	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_sw_create(%s) failed\n",
+		printk(KERN_ERR "fcoe: fcoe_if_create(%s) failed\n",
 		       netdev->name);
 		fcoe_ethdrv_put(netdev);
 		rc = -EIO;
@@ -1457,7 +1931,7 @@ static int __init fcoe_init(void)
 
 	mod_timer(&fcoe_timer, jiffies + (10 * HZ));
 
-	fcoe_sw_init();
+	fcoe_if_init();
 
 	return 0;
 
@@ -1487,15 +1961,12 @@ static void __exit fcoe_exit(void)
 
 	/* releases the associated fcoe hosts */
 	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
-		fcoe_sw_destroy(fc->real_dev);
+		fcoe_if_destroy(fc->real_dev);
 
 	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
 
 	for_each_online_cpu(cpu) {
 		fcoe_percpu_thread_destroy(cpu);
 	}
-
-	/* remove sw trasnport */
-	fcoe_sw_exit();
 }
 module_exit(fcoe_exit);
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index dc64405..e99633c 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -145,10 +145,4 @@ int fcoe_hostlist_remove(const struct fc_lport *);
 
 struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *, int);
 int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
-
-/* fcoe sw hba */
-int __init fcoe_sw_init(void);
-int __exit fcoe_sw_exit(void);
-int fcoe_sw_create(struct net_device *);
-int fcoe_sw_destroy(struct net_device *);
 #endif /* _LIBFCOE_H */


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 08/14] fcoe: renames libfcoe.c to fcoe.c as the only fcoe module file
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
                   ` (6 preceding siblings ...)
  2009-03-17 18:42 ` [PATCH 07/14] fcoe: removes default sw transport code file fcoe_sw.c Robert Love
@ 2009-03-17 18:42 ` Robert Love
  2009-03-24 23:27   ` [PATCH] " Robert Love
  2009-03-27 16:07   ` Robert Love
  2009-03-17 18:42 ` [PATCH 09/14] fcoe, libfc, scsi: adds libfcoe module Robert Love
                   ` (5 subsequent siblings)
  13 siblings, 2 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:42 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

Renames libfcoe.c to fcoe.c, fcoe.c becomes the only
.c file for fcoe.ko.

Also deleted "$Id: Makefile" from fcoe module Makefle.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/Makefile  |    5 
 drivers/scsi/fcoe/fcoe.c    | 1972 +++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/fcoe/libfcoe.c | 1972 -------------------------------------------
 3 files changed, 1972 insertions(+), 1977 deletions(-)
 create mode 100644 drivers/scsi/fcoe/fcoe.c
 delete mode 100644 drivers/scsi/fcoe/libfcoe.c

diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index 2d9dae4..9b590ef 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -1,6 +1 @@
-# $Id: Makefile
-
 obj-$(CONFIG_FCOE) += fcoe.o
-
-fcoe-y := \
-	libfcoe.o
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
new file mode 100644
index 0000000..cbebbf7
--- /dev/null
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -0,0 +1,1972 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/kthread.h>
+#include <linux/crc32.h>
+#include <linux/cpu.h>
+#include <linux/fs.h>
+#include <linux/sysfs.h>
+#include <linux/ctype.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+#include <net/rtnetlink.h>
+
+#include <scsi/fc/fc_encaps.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
+#include <scsi/libfcoe.h>
+
+static int debug_fcoe;
+
+#define FCOE_MAX_QUEUE_DEPTH  256
+#define FCOE_LOW_QUEUE_DEPTH  32
+
+/* destination address mode */
+#define FCOE_GW_ADDR_MODE	    0x00
+#define FCOE_FCOUI_ADDR_MODE	    0x01
+
+#define FCOE_WORD_TO_BYTE  4
+
+#define FCOE_VERSION	"0.1"
+#define	FCOE_NAME	"fcoe"
+#define	FCOE_VENDOR	"Open-FCoE.org"
+
+#define FCOE_MAX_LUN		255
+#define FCOE_MAX_FCP_TARGET	256
+
+#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
+
+#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
+#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
+
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("FCoE");
+MODULE_LICENSE("GPL");
+
+/* fcoe host list */
+LIST_HEAD(fcoe_hostlist);
+DEFINE_RWLOCK(fcoe_hostlist_lock);
+DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
+DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
+
+
+/* Function Prototyes */
+static int fcoe_check_wait_queue(struct fc_lport *);
+static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
+static int fcoe_device_notification(struct notifier_block *, ulong, void *);
+static void fcoe_dev_setup(void);
+static void fcoe_dev_cleanup(void);
+
+/* notification function from net device */
+static struct notifier_block fcoe_notifier = {
+	.notifier_call = fcoe_device_notification,
+};
+
+static struct scsi_transport_template *scsi_transport_fcoe_sw;
+
+struct fc_function_template fcoe_transport_function = {
+	.show_host_node_name = 1,
+	.show_host_port_name = 1,
+	.show_host_supported_classes = 1,
+	.show_host_supported_fc4s = 1,
+	.show_host_active_fc4s = 1,
+	.show_host_maxframe_size = 1,
+
+	.show_host_port_id = 1,
+	.show_host_supported_speeds = 1,
+	.get_host_speed = fc_get_host_speed,
+	.show_host_speed = 1,
+	.show_host_port_type = 1,
+	.get_host_port_state = fc_get_host_port_state,
+	.show_host_port_state = 1,
+	.show_host_symbolic_name = 1,
+
+	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+	.show_rport_maxframe_size = 1,
+	.show_rport_supported_classes = 1,
+
+	.show_host_fabric_name = 1,
+	.show_starget_node_name = 1,
+	.show_starget_port_name = 1,
+	.show_starget_port_id = 1,
+	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+	.show_rport_dev_loss_tmo = 1,
+	.get_fc_host_stats = fc_get_host_stats,
+	.issue_fc_host_lip = fcoe_reset,
+
+	.terminate_rport_io = fc_rport_terminate_io,
+};
+
+static struct scsi_host_template fcoe_shost_template = {
+	.module = THIS_MODULE,
+	.name = "FCoE Driver",
+	.proc_name = FCOE_NAME,
+	.queuecommand = fc_queuecommand,
+	.eh_abort_handler = fc_eh_abort,
+	.eh_device_reset_handler = fc_eh_device_reset,
+	.eh_host_reset_handler = fc_eh_host_reset,
+	.slave_alloc = fc_slave_alloc,
+	.change_queue_depth = fc_change_queue_depth,
+	.change_queue_type = fc_change_queue_type,
+	.this_id = -1,
+	.cmd_per_lun = 32,
+	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
+	.use_clustering = ENABLE_CLUSTERING,
+	.sg_tablesize = SG_ALL,
+	.max_sectors = 0xffff,
+};
+
+/**
+ * fcoe_lport_config() - sets up the fc_lport
+ * @lp: ptr to the fc_lport
+ * @shost: ptr to the parent scsi host
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_lport_config(struct fc_lport *lp)
+{
+	lp->link_up = 0;
+	lp->qfull = 0;
+	lp->max_retry_count = 3;
+	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
+	lp->r_a_tov = 2 * 2 * 1000;
+	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+
+	fc_lport_init_stats(lp);
+
+	/* lport fc_lport related configuration */
+	fc_lport_config(lp);
+
+	/* offload related configuration */
+	lp->crc_offload = 0;
+	lp->seq_offload = 0;
+	lp->lro_enabled = 0;
+	lp->lro_xid = 0;
+	lp->lso_max = 0;
+
+	return 0;
+}
+
+/**
+ * fcoe_netdev_config() - Set up netdev for SW FCoE
+ * @lp : ptr to the fc_lport
+ * @netdev : ptr to the associated netdevice struct
+ *
+ * Must be called after fcoe_lport_config() as it will use lport mutex
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
+{
+	u32 mfs;
+	u64 wwnn, wwpn;
+	struct fcoe_softc *fc;
+	u8 flogi_maddr[ETH_ALEN];
+
+	/* Setup lport private data to point to fcoe softc */
+	fc = lport_priv(lp);
+	fc->lp = lp;
+	fc->real_dev = netdev;
+	fc->phys_dev = netdev;
+
+	/* Require support for get_pauseparam ethtool op. */
+	if (netdev->priv_flags & IFF_802_1Q_VLAN)
+		fc->phys_dev = vlan_dev_real_dev(netdev);
+
+	/* Do not support for bonding device */
+	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
+	    (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
+	    (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
+		return -EOPNOTSUPP;
+	}
+
+	/*
+	 * Determine max frame size based on underlying device and optional
+	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
+	 * will return 0, so do this first.
+	 */
+	mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
+				   sizeof(struct fcoe_crc_eof));
+	if (fc_set_mfs(lp, mfs))
+		return -EINVAL;
+
+	if (!fcoe_link_ok(lp))
+		lp->link_up = 1;
+
+	/* offload features support */
+	if (fc->real_dev->features & NETIF_F_SG)
+		lp->sg_supp = 1;
+
+#ifdef NETIF_F_FCOE_CRC
+	if (netdev->features & NETIF_F_FCOE_CRC) {
+		lp->crc_offload = 1;
+		printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
+		       netdev->name);
+	}
+#endif
+#ifdef NETIF_F_FSO
+	if (netdev->features & NETIF_F_FSO) {
+		lp->seq_offload = 1;
+		lp->lso_max = netdev->gso_max_size;
+		printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
+		       netdev->name, lp->lso_max);
+	}
+#endif
+	if (netdev->fcoe_ddp_xid) {
+		lp->lro_enabled = 1;
+		lp->lro_xid = netdev->fcoe_ddp_xid;
+		printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
+		       netdev->name, lp->lro_xid);
+	}
+	skb_queue_head_init(&fc->fcoe_pending_queue);
+	fc->fcoe_pending_queue_active = 0;
+
+	/* setup Source Mac Address */
+	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
+	       fc->real_dev->addr_len);
+
+	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
+	fc_set_wwnn(lp, wwnn);
+	/* XXX - 3rd arg needs to be vlan id */
+	wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
+	fc_set_wwpn(lp, wwpn);
+
+	/*
+	 * Add FCoE MAC address as second unicast MAC address
+	 * or enter promiscuous mode if not capable of listening
+	 * for multiple unicast MACs.
+	 */
+	rtnl_lock();
+	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+	dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
+	rtnl_unlock();
+
+	/*
+	 * setup the receive function from ethernet driver
+	 * on the ethertype for the given device
+	 */
+	fc->fcoe_packet_type.func = fcoe_rcv;
+	fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+	fc->fcoe_packet_type.dev = fc->real_dev;
+	dev_add_pack(&fc->fcoe_packet_type);
+
+	return 0;
+}
+
+/**
+ * fcoe_shost_config() - Sets up fc_lport->host
+ * @lp : ptr to the fc_lport
+ * @shost : ptr to the associated scsi host
+ * @dev : device associated to scsi host
+ *
+ * Must be called after fcoe_lport_config() and fcoe_netdev_config()
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
+				struct device *dev)
+{
+	int rc = 0;
+
+	/* lport scsi host config */
+	lp->host = shost;
+
+	lp->host->max_lun = FCOE_MAX_LUN;
+	lp->host->max_id = FCOE_MAX_FCP_TARGET;
+	lp->host->max_channel = 0;
+	lp->host->transportt = scsi_transport_fcoe_sw;
+
+	/* add the new host to the SCSI-ml */
+	rc = scsi_add_host(lp->host, dev);
+	if (rc) {
+		FC_DBG("fcoe_shost_config:error on scsi_add_host\n");
+		return rc;
+	}
+	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
+		FCOE_NAME, FCOE_VERSION,
+		fcoe_netdev(lp)->name);
+
+	return 0;
+}
+
+/**
+ * fcoe_em_config() - 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)
+{
+	BUG_ON(lp->emp);
+
+	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
+				    FCOE_MIN_XID, FCOE_MAX_XID);
+	if (!lp->emp)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/**
+ * fcoe_if_destroy() - FCoE software HBA tear-down function
+ * @netdev: ptr to the associated net_device
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ */
+static int fcoe_if_destroy(struct net_device *netdev)
+{
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+	u8 flogi_maddr[ETH_ALEN];
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_if_destroy:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (!lp)
+		return -ENODEV;
+
+	fc = lport_priv(lp);
+
+	/* Logout of the fabric */
+	fc_fabric_logoff(lp);
+
+	/* Remove the instance from fcoe's list */
+	fcoe_hostlist_remove(lp);
+
+	/* Don't listen for Ethernet packets anymore */
+	dev_remove_pack(&fc->fcoe_packet_type);
+
+	/* Cleanup the fc_lport */
+	fc_lport_destroy(lp);
+	fc_fcp_destroy(lp);
+
+	/* Detach from the scsi-ml */
+	fc_remove_host(lp->host);
+	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);
+
+	/* Delete secondary MAC addresses */
+	rtnl_lock();
+	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
+	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+	rtnl_unlock();
+
+	/* Free the per-CPU revieve threads */
+	fcoe_percpu_clean(lp);
+
+	/* Free existing skbs */
+	fcoe_clean_pending_queue(lp);
+
+	/* Free memory used by statistical counters */
+	fc_lport_free_stats(lp);
+
+	/* Release the net_device and Scsi_Host */
+	dev_put(fc->real_dev);
+	scsi_host_put(lp->host);
+
+	return 0;
+}
+
+/*
+ * fcoe_ddp_setup - calls LLD's ddp_setup through net_device
+ * @lp:	the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ * @sgl: the scatterlist describing this transfer
+ * @sgc: number of sg items
+ *
+ * Returns : 0 no ddp
+ */
+static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid,
+			     struct scatterlist *sgl, unsigned int sgc)
+{
+	struct net_device *n = fcoe_netdev(lp);
+
+	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
+		return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
+
+	return 0;
+}
+
+/*
+ * fcoe_ddp_done - calls LLD's ddp_done through net_device
+ * @lp:	the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ *
+ * Returns : the length of data that have been completed by ddp
+ */
+static int fcoe_ddp_done(struct fc_lport *lp, u16 xid)
+{
+	struct net_device *n = fcoe_netdev(lp);
+
+	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
+		return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
+	return 0;
+}
+
+static struct libfc_function_template fcoe_libfc_fcn_templ = {
+	.frame_send = fcoe_xmit,
+	.ddp_setup = fcoe_ddp_setup,
+	.ddp_done = fcoe_ddp_done,
+};
+
+/**
+ * fcoe_if_create() - this function creates the fcoe interface
+ * @netdev: pointer the associated netdevice
+ *
+ * Creates fc_lport struct and scsi_host for lport, configures lport
+ * and starts fabric login.
+ *
+ * Returns : 0 on success
+ */
+static int fcoe_if_create(struct net_device *netdev)
+{
+	int rc;
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+	struct Scsi_Host *shost;
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_if_create:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (lp)
+		return -EEXIST;
+
+	shost = fcoe_host_alloc(&fcoe_shost_template,
+				sizeof(struct fcoe_softc));
+	if (!shost) {
+		FC_DBG("Could not allocate host structure\n");
+		return -ENOMEM;
+	}
+	lp = shost_priv(shost);
+	fc = lport_priv(lp);
+
+	/* configure fc_lport, e.g., em */
+	rc = fcoe_lport_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure lport\n");
+		goto out_host_put;
+	}
+
+	/* configure lport network properties */
+	rc = fcoe_netdev_config(lp, netdev);
+	if (rc) {
+		FC_DBG("Could not configure netdev for lport\n");
+		goto out_host_put;
+	}
+
+	/* configure lport scsi host properties */
+	rc = fcoe_shost_config(lp, shost, &netdev->dev);
+	if (rc) {
+		FC_DBG("Could not configure shost for lport\n");
+		goto out_host_put;
+	}
+
+	/* lport exch manager allocation */
+	rc = fcoe_em_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure em for lport\n");
+		goto out_host_put;
+	}
+
+	/* Initialize the library */
+	rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ);
+	if (rc) {
+		FC_DBG("Could not configure libfc for lport!\n");
+		goto out_lp_destroy;
+	}
+
+	/* add to lports list */
+	fcoe_hostlist_add(lp);
+
+	lp->boot_time = jiffies;
+
+	fc_fabric_login(lp);
+
+	dev_hold(netdev);
+
+	return rc;
+
+out_lp_destroy:
+	fc_exch_mgr_free(lp->emp); /* Free the EM */
+out_host_put:
+	scsi_host_put(lp->host);
+	return rc;
+}
+
+/**
+ * fcoe_if_init() - attach to scsi transport
+ *
+ * Returns : 0 on success
+ */
+static int __init fcoe_if_init(void)
+{
+	/* attach to scsi transport */
+	scsi_transport_fcoe_sw =
+		fc_attach_transport(&fcoe_transport_function);
+
+	if (!scsi_transport_fcoe_sw) {
+		printk(KERN_ERR "fcoe_init:fc_attach_transport() failed\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
+ * @cpu: cpu index for the online cpu
+ */
+static void fcoe_percpu_thread_create(unsigned int cpu)
+{
+	struct fcoe_percpu_s *p;
+	struct task_struct *thread;
+
+	p = &per_cpu(fcoe_percpu, cpu);
+
+	thread = kthread_create(fcoe_percpu_receive_thread,
+				(void *)p, "fcoethread/%d", cpu);
+
+	if (likely(!IS_ERR(p->thread))) {
+		kthread_bind(thread, cpu);
+		wake_up_process(thread);
+
+		spin_lock_bh(&p->fcoe_rx_list.lock);
+		p->thread = thread;
+		spin_unlock_bh(&p->fcoe_rx_list.lock);
+	}
+}
+
+/**
+ * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
+ * @cpu: cpu index the rx thread is to be removed
+ *
+ * Destroys a per-CPU Rx thread. Any pending skbs are moved to the
+ * current CPU's Rx thread. If the thread being destroyed is bound to
+ * the CPU processing this context the skbs will be freed.
+ */
+static void fcoe_percpu_thread_destroy(unsigned int cpu)
+{
+	struct fcoe_percpu_s *p;
+	struct task_struct *thread;
+	struct page *crc_eof;
+	struct sk_buff *skb;
+#ifdef CONFIG_SMP
+	struct fcoe_percpu_s *p0;
+	unsigned targ_cpu = smp_processor_id();
+#endif /* CONFIG_SMP */
+
+	printk(KERN_DEBUG "fcoe: Destroying receive thread for CPU %d\n", cpu);
+
+	/* Prevent any new skbs from being queued for this CPU. */
+	p = &per_cpu(fcoe_percpu, cpu);
+	spin_lock_bh(&p->fcoe_rx_list.lock);
+	thread = p->thread;
+	p->thread = NULL;
+	crc_eof = p->crc_eof_page;
+	p->crc_eof_page = NULL;
+	p->crc_eof_offset = 0;
+	spin_unlock_bh(&p->fcoe_rx_list.lock);
+
+#ifdef CONFIG_SMP
+	/*
+	 * Don't bother moving the skb's if this context is running
+	 * on the same CPU that is having its thread destroyed. This
+	 * can easily happen when the module is removed.
+	 */
+	if (cpu != targ_cpu) {
+		p0 = &per_cpu(fcoe_percpu, targ_cpu);
+		spin_lock_bh(&p0->fcoe_rx_list.lock);
+		if (p0->thread) {
+			FC_DBG("Moving frames from CPU %d to CPU %d\n",
+			       cpu, targ_cpu);
+
+			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+				__skb_queue_tail(&p0->fcoe_rx_list, skb);
+			spin_unlock_bh(&p0->fcoe_rx_list.lock);
+		} else {
+			/*
+			 * The targeted CPU is not initialized and cannot accept
+			 * new  skbs. Unlock the targeted CPU and drop the skbs
+			 * on the CPU that is going offline.
+			 */
+			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+				kfree_skb(skb);
+			spin_unlock_bh(&p0->fcoe_rx_list.lock);
+		}
+	} else {
+		/*
+		 * This scenario occurs when the module is being removed
+		 * and all threads are being destroyed. skbs will continue
+		 * to be shifted from the CPU thread that is being removed
+		 * to the CPU thread associated with the CPU that is processing
+		 * the module removal. Once there is only one CPU Rx thread it
+		 * will reach this case and we will drop all skbs and later
+		 * stop the thread.
+		 */
+		spin_lock_bh(&p->fcoe_rx_list.lock);
+		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+			kfree_skb(skb);
+		spin_unlock_bh(&p->fcoe_rx_list.lock);
+	}
+#else
+	/*
+	 * This a non-SMP scenario where the singluar Rx thread is
+	 * being removed. Free all skbs and stop the thread.
+	 */
+	spin_lock_bh(&p->fcoe_rx_list.lock);
+	while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+		kfree_skb(skb);
+	spin_unlock_bh(&p->fcoe_rx_list.lock);
+#endif
+
+	if (thread)
+		kthread_stop(thread);
+
+	if (crc_eof)
+		put_page(crc_eof);
+}
+
+/**
+ * fcoe_cpu_callback() - fcoe cpu hotplug event callback
+ * @nfb: callback data block
+ * @action: event triggering the callback
+ * @hcpu: index for the cpu of this event
+ *
+ * This creates or destroys per cpu data for fcoe
+ *
+ * Returns NOTIFY_OK always.
+ */
+static int fcoe_cpu_callback(struct notifier_block *nfb,
+			     unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		FC_DBG("CPU %x online: Create Rx thread\n", cpu);
+		fcoe_percpu_thread_create(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		FC_DBG("CPU %x offline: Remove Rx thread\n", cpu);
+		fcoe_percpu_thread_destroy(cpu);
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block fcoe_cpu_notifier = {
+	.notifier_call = fcoe_cpu_callback,
+};
+
+/**
+ * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ
+ * @skb: the receive skb
+ * @dev: associated net device
+ * @ptype: context
+ * @odldev: last device
+ *
+ * this function will receive the packet and build fc frame and pass it up
+ *
+ * Returns: 0 for success
+ */
+int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
+	     struct packet_type *ptype, struct net_device *olddev)
+{
+	struct fc_lport *lp;
+	struct fcoe_rcv_info *fr;
+	struct fcoe_softc *fc;
+	struct fc_frame_header *fh;
+	struct fcoe_percpu_s *fps;
+	unsigned short oxid;
+	unsigned int cpu = 0;
+
+	fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
+	lp = fc->lp;
+	if (unlikely(lp == NULL)) {
+		FC_DBG("cannot find hba structure");
+		goto err2;
+	}
+
+	if (unlikely(debug_fcoe)) {
+		FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p "
+		       "end:%p sum:%d dev:%s", skb->len, skb->data_len,
+		       skb->head, skb->data, skb_tail_pointer(skb),
+		       skb_end_pointer(skb), skb->csum,
+		       skb->dev ? skb->dev->name : "<NULL>");
+
+	}
+
+	/* check for FCOE packet type */
+	if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
+		FC_DBG("wrong FC type frame");
+		goto err;
+	}
+
+	/*
+	 * Check for minimum frame length, and make sure required FCoE
+	 * and FC headers are pulled into the linear data area.
+	 */
+	if (unlikely((skb->len < FCOE_MIN_FRAME) ||
+	    !pskb_may_pull(skb, FCOE_HEADER_LEN)))
+		goto err;
+
+	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.
+	 */
+	cpu = oxid & (num_online_cpus() - 1);
+#endif
+
+	fps = &per_cpu(fcoe_percpu, cpu);
+	spin_lock_bh(&fps->fcoe_rx_list.lock);
+	if (unlikely(!fps->thread)) {
+		/*
+		 * The targeted CPU is not ready, let's target
+		 * the first CPU now. For non-SMP systems this
+		 * will check the same CPU twice.
+		 */
+		FC_DBG("CPU is online, but no receive thread ready "
+		       "for incoming skb- using first online CPU.\n");
+
+		spin_unlock_bh(&fps->fcoe_rx_list.lock);
+		cpu = first_cpu(cpu_online_map);
+		fps = &per_cpu(fcoe_percpu, cpu);
+		spin_lock_bh(&fps->fcoe_rx_list.lock);
+		if (!fps->thread) {
+			spin_unlock_bh(&fps->fcoe_rx_list.lock);
+			goto err;
+		}
+	}
+
+	/*
+	 * We now have a valid CPU that we're targeting for
+	 * this skb. We also have this receive thread locked,
+	 * so we're free to queue skbs into it's queue.
+	 */
+	__skb_queue_tail(&fps->fcoe_rx_list, skb);
+	if (fps->fcoe_rx_list.qlen == 1)
+		wake_up_process(fps->thread);
+
+	spin_unlock_bh(&fps->fcoe_rx_list.lock);
+
+	return 0;
+err:
+	fc_lport_get_stats(lp)->ErrorFrames++;
+
+err2:
+	kfree_skb(skb);
+	return -1;
+}
+EXPORT_SYMBOL_GPL(fcoe_rcv);
+
+/**
+ * fcoe_start_io() - pass to netdev to start xmit for fcoe
+ * @skb: the skb to be xmitted
+ *
+ * Returns: 0 for success
+ */
+static inline int fcoe_start_io(struct sk_buff *skb)
+{
+	int rc;
+
+	skb_get(skb);
+	rc = dev_queue_xmit(skb);
+	if (rc != 0)
+		return rc;
+	kfree_skb(skb);
+	return 0;
+}
+
+/**
+ * fcoe_get_paged_crc_eof() - in case we need alloc a page for crc_eof
+ * @skb: the skb to be xmitted
+ * @tlen: total len
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
+{
+	struct fcoe_percpu_s *fps;
+	struct page *page;
+
+	fps = &get_cpu_var(fcoe_percpu);
+	page = fps->crc_eof_page;
+	if (!page) {
+		page = alloc_page(GFP_ATOMIC);
+		if (!page) {
+			put_cpu_var(fcoe_percpu);
+			return -ENOMEM;
+		}
+		fps->crc_eof_page = page;
+		fps->crc_eof_offset = 0;
+	}
+
+	get_page(page);
+	skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page,
+			   fps->crc_eof_offset, tlen);
+	skb->len += tlen;
+	skb->data_len += tlen;
+	skb->truesize += tlen;
+	fps->crc_eof_offset += sizeof(struct fcoe_crc_eof);
+
+	if (fps->crc_eof_offset >= PAGE_SIZE) {
+		fps->crc_eof_page = NULL;
+		fps->crc_eof_offset = 0;
+		put_page(page);
+	}
+	put_cpu_var(fcoe_percpu);
+	return 0;
+}
+
+/**
+ * fcoe_fc_crc() - calculates FC CRC in this fcoe skb
+ * @fp: the fc_frame containg data to be checksummed
+ *
+ * This uses crc32() to calculate the crc for fc frame
+ * Return   : 32 bit crc
+ */
+u32 fcoe_fc_crc(struct fc_frame *fp)
+{
+	struct sk_buff *skb = fp_skb(fp);
+	struct skb_frag_struct *frag;
+	unsigned char *data;
+	unsigned long off, len, clen;
+	u32 crc;
+	unsigned i;
+
+	crc = crc32(~0, skb->data, skb_headlen(skb));
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		frag = &skb_shinfo(skb)->frags[i];
+		off = frag->page_offset;
+		len = frag->size;
+		while (len > 0) {
+			clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK));
+			data = kmap_atomic(frag->page + (off >> PAGE_SHIFT),
+					   KM_SKB_DATA_SOFTIRQ);
+			crc = crc32(crc, data + (off & ~PAGE_MASK), clen);
+			kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ);
+			off += clen;
+			len -= clen;
+		}
+	}
+	return crc;
+}
+EXPORT_SYMBOL_GPL(fcoe_fc_crc);
+
+/**
+ * fcoe_xmit() - FCoE frame transmit function
+ * @lp:	the associated local port
+ * @fp: the fc_frame to be transmitted
+ *
+ * Return   : 0 for success
+ */
+int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
+{
+	int wlen, rc = 0;
+	u32 crc;
+	struct ethhdr *eh;
+	struct fcoe_crc_eof *cp;
+	struct sk_buff *skb;
+	struct fcoe_dev_stats *stats;
+	struct fc_frame_header *fh;
+	unsigned int hlen;		/* header length implies the version */
+	unsigned int tlen;		/* trailer length */
+	unsigned int elen;		/* eth header, may include vlan */
+	int flogi_in_progress = 0;
+	struct fcoe_softc *fc;
+	u8 sof, eof;
+	struct fcoe_hdr *hp;
+
+	WARN_ON((fr_len(fp) % sizeof(u32)) != 0);
+
+	fc = lport_priv(lp);
+	/*
+	 * if it is a flogi then we need to learn gw-addr
+	 * and my own fcid
+	 */
+	fh = fc_frame_header_get(fp);
+	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
+		if (fc_frame_payload_op(fp) == ELS_FLOGI) {
+			fc->flogi_oxid = ntohs(fh->fh_ox_id);
+			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
+			fc->flogi_progress = 1;
+			flogi_in_progress = 1;
+		} else if (fc->flogi_progress && ntoh24(fh->fh_s_id) != 0) {
+			/*
+			 * Here we must've gotten an SID by accepting an FLOGI
+			 * from a point-to-point connection.  Switch to using
+			 * the source mac based on the SID.  The destination
+			 * MAC in this case would have been set by receving the
+			 * FLOGI.
+			 */
+			fc_fcoe_set_mac(fc->data_src_addr, fh->fh_s_id);
+			fc->flogi_progress = 0;
+		}
+	}
+
+	skb = fp_skb(fp);
+	sof = fr_sof(fp);
+	eof = fr_eof(fp);
+
+	elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ?
+		sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr);
+	hlen = sizeof(struct fcoe_hdr);
+	tlen = sizeof(struct fcoe_crc_eof);
+	wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
+
+	/* crc offload */
+	if (likely(lp->crc_offload)) {
+		skb->ip_summed = CHECKSUM_PARTIAL;
+		skb->csum_start = skb_headroom(skb);
+		skb->csum_offset = skb->len;
+		crc = 0;
+	} else {
+		skb->ip_summed = CHECKSUM_NONE;
+		crc = fcoe_fc_crc(fp);
+	}
+
+	/* copy fc crc and eof to the skb buff */
+	if (skb_is_nonlinear(skb)) {
+		skb_frag_t *frag;
+		if (fcoe_get_paged_crc_eof(skb, tlen)) {
+			kfree_skb(skb);
+			return -ENOMEM;
+		}
+		frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
+		cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
+			+ frag->page_offset;
+	} else {
+		cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
+	}
+
+	memset(cp, 0, sizeof(*cp));
+	cp->fcoe_eof = eof;
+	cp->fcoe_crc32 = cpu_to_le32(~crc);
+
+	if (skb_is_nonlinear(skb)) {
+		kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ);
+		cp = NULL;
+	}
+
+	/* adjust skb netowrk/transport offsets to match mac/fcoe/fc */
+	skb_push(skb, elen + hlen);
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	skb->mac_len = elen;
+	skb->protocol = htons(ETH_P_FCOE);
+	skb->dev = fc->real_dev;
+
+	/* fill up mac and fcoe headers */
+	eh = eth_hdr(skb);
+	eh->h_proto = htons(ETH_P_FCOE);
+	if (fc->address_mode == FCOE_FCOUI_ADDR_MODE)
+		fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
+	else
+		/* insert GW address */
+		memcpy(eh->h_dest, fc->dest_addr, ETH_ALEN);
+
+	if (unlikely(flogi_in_progress))
+		memcpy(eh->h_source, fc->ctl_src_addr, ETH_ALEN);
+	else
+		memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN);
+
+	hp = (struct fcoe_hdr *)(eh + 1);
+	memset(hp, 0, sizeof(*hp));
+	if (FC_FCOE_VER)
+		FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
+	hp->fcoe_sof = sof;
+
+#ifdef NETIF_F_FSO
+	/* fcoe lso, mss is in max_payload which is non-zero for FCP data */
+	if (lp->seq_offload && fr_max_payload(fp)) {
+		skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
+		skb_shinfo(skb)->gso_size = fr_max_payload(fp);
+	} else {
+		skb_shinfo(skb)->gso_type = 0;
+		skb_shinfo(skb)->gso_size = 0;
+	}
+#endif
+	/* update tx stats: regardless if LLD fails */
+	stats = fc_lport_get_stats(lp);
+	stats->TxFrames++;
+	stats->TxWords += wlen;
+
+	/* send down to lld */
+	fr_dev(fp) = lp;
+	if (fc->fcoe_pending_queue.qlen)
+		rc = fcoe_check_wait_queue(lp);
+
+	if (rc == 0)
+		rc = fcoe_start_io(skb);
+
+	if (rc) {
+		spin_lock_bh(&fc->fcoe_pending_queue.lock);
+		__skb_queue_tail(&fc->fcoe_pending_queue, skb);
+		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+		if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+			lp->qfull = 1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_xmit);
+
+/**
+ * fcoe_percpu_receive_thread() - recv thread per cpu
+ * @arg: ptr to the fcoe per cpu struct
+ *
+ * Return: 0 for success
+ */
+int fcoe_percpu_receive_thread(void *arg)
+{
+	struct fcoe_percpu_s *p = arg;
+	u32 fr_len;
+	struct fc_lport *lp;
+	struct fcoe_rcv_info *fr;
+	struct fcoe_dev_stats *stats;
+	struct fc_frame_header *fh;
+	struct sk_buff *skb;
+	struct fcoe_crc_eof crc_eof;
+	struct fc_frame *fp;
+	u8 *mac = NULL;
+	struct fcoe_softc *fc;
+	struct fcoe_hdr *hp;
+
+	set_user_nice(current, -20);
+
+	while (!kthread_should_stop()) {
+
+		spin_lock_bh(&p->fcoe_rx_list.lock);
+		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			spin_unlock_bh(&p->fcoe_rx_list.lock);
+			schedule();
+			set_current_state(TASK_RUNNING);
+			if (kthread_should_stop())
+				return 0;
+			spin_lock_bh(&p->fcoe_rx_list.lock);
+		}
+		spin_unlock_bh(&p->fcoe_rx_list.lock);
+		fr = fcoe_dev_from_skb(skb);
+		lp = fr->fr_dev;
+		if (unlikely(lp == NULL)) {
+			FC_DBG("invalid HBA Structure");
+			kfree_skb(skb);
+			continue;
+		}
+
+		if (unlikely(debug_fcoe)) {
+			FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p "
+			       "tail:%p end:%p sum:%d dev:%s",
+			       skb->len, skb->data_len,
+			       skb->head, skb->data, skb_tail_pointer(skb),
+			       skb_end_pointer(skb), skb->csum,
+			       skb->dev ? skb->dev->name : "<NULL>");
+		}
+
+		/*
+		 * Save source MAC address before discarding header.
+		 */
+		fc = lport_priv(lp);
+		if (unlikely(fc->flogi_progress))
+			mac = eth_hdr(skb)->h_source;
+
+		if (skb_is_nonlinear(skb))
+			skb_linearize(skb);	/* not ideal */
+
+		/*
+		 * Frame length checks and setting up the header pointers
+		 * was done in fcoe_rcv already.
+		 */
+		hp = (struct fcoe_hdr *) skb_network_header(skb);
+		fh = (struct fc_frame_header *) skb_transport_header(skb);
+
+		stats = fc_lport_get_stats(lp);
+		if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
+			if (stats->ErrorFrames < 5)
+				printk(KERN_WARNING "FCoE version "
+				       "mismatch: The frame has "
+				       "version %x, but the "
+				       "initiator supports version "
+				       "%x\n", FC_FCOE_DECAPS_VER(hp),
+				       FC_FCOE_VER);
+			stats->ErrorFrames++;
+			kfree_skb(skb);
+			continue;
+		}
+
+		skb_pull(skb, sizeof(struct fcoe_hdr));
+		fr_len = skb->len - sizeof(struct fcoe_crc_eof);
+
+		stats->RxFrames++;
+		stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
+
+		fp = (struct fc_frame *)skb;
+		fc_frame_init(fp);
+		fr_dev(fp) = lp;
+		fr_sof(fp) = hp->fcoe_sof;
+
+		/* Copy out the CRC and EOF trailer for access */
+		if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
+			kfree_skb(skb);
+			continue;
+		}
+		fr_eof(fp) = crc_eof.fcoe_eof;
+		fr_crc(fp) = crc_eof.fcoe_crc32;
+		if (pskb_trim(skb, fr_len)) {
+			kfree_skb(skb);
+			continue;
+		}
+
+		/*
+		 * We only check CRC if no offload is available and if it is
+		 * it's solicited data, in which case, the FCP layer would
+		 * check it during the copy.
+		 */
+		if (lp->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
+			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+		else
+			fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
+
+		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);
+			continue;
+		}
+		if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+			if (le32_to_cpu(fr_crc(fp)) !=
+			    ~crc32(~0, skb->data, fr_len)) {
+				if (debug_fcoe || stats->InvalidCRCCount < 5)
+					printk(KERN_WARNING "fcoe: dropping "
+					       "frame with CRC error\n");
+				stats->InvalidCRCCount++;
+				stats->ErrorFrames++;
+				fc_frame_free(fp);
+				continue;
+			}
+			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+		}
+		/* non flogi and non data exchanges are handled here */
+		if (unlikely(fc->flogi_progress))
+			fcoe_recv_flogi(fc, fp, mac);
+		fc_exch_recv(lp, lp->emp, fp);
+	}
+	return 0;
+}
+
+/**
+ * fcoe_recv_flogi() - flogi receive function
+ * @fc: associated fcoe_softc
+ * @fp: the recieved frame
+ * @sa: the source address of this flogi
+ *
+ * This is responsible to parse the flogi response and sets the corresponding
+ * mac address for the initiator, eitehr OUI based or GW based.
+ *
+ * Returns: none
+ */
+static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa)
+{
+	struct fc_frame_header *fh;
+	u8 op;
+
+	fh = fc_frame_header_get(fp);
+	if (fh->fh_type != FC_TYPE_ELS)
+		return;
+	op = fc_frame_payload_op(fp);
+	if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
+	    fc->flogi_oxid == ntohs(fh->fh_ox_id)) {
+		/*
+		 * FLOGI accepted.
+		 * If the src mac addr is FC_OUI-based, then we mark the
+		 * address_mode flag to use FC_OUI-based Ethernet DA.
+		 * Otherwise we use the FCoE gateway addr
+		 */
+		if (!compare_ether_addr(sa, (u8[6]) FC_FCOE_FLOGI_MAC)) {
+			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
+		} else {
+			memcpy(fc->dest_addr, sa, ETH_ALEN);
+			fc->address_mode = FCOE_GW_ADDR_MODE;
+		}
+
+		/*
+		 * Remove any previously-set unicast MAC filter.
+		 * Add secondary FCoE MAC address filter for our OUI.
+		 */
+		rtnl_lock();
+		if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+			dev_unicast_delete(fc->real_dev, fc->data_src_addr,
+					   ETH_ALEN);
+		fc_fcoe_set_mac(fc->data_src_addr, fh->fh_d_id);
+		dev_unicast_add(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+		rtnl_unlock();
+
+		fc->flogi_progress = 0;
+	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
+		/*
+		 * Save source MAC for point-to-point responses.
+		 */
+		memcpy(fc->dest_addr, sa, ETH_ALEN);
+		fc->address_mode = FCOE_GW_ADDR_MODE;
+	}
+}
+
+/**
+ * fcoe_watchdog() - fcoe timer callback
+ * @vp:
+ *
+ * This checks the pending queue length for fcoe and set lport qfull
+ * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the
+ * fcoe_hostlist.
+ *
+ * Returns: 0 for success
+ */
+void fcoe_watchdog(ulong vp)
+{
+	struct fcoe_softc *fc;
+
+	read_lock(&fcoe_hostlist_lock);
+	list_for_each_entry(fc, &fcoe_hostlist, list) {
+		if (fc->lp)
+			fcoe_check_wait_queue(fc->lp);
+	}
+	read_unlock(&fcoe_hostlist_lock);
+
+	fcoe_timer.expires = jiffies + (1 * HZ);
+	add_timer(&fcoe_timer);
+}
+
+
+/**
+ * fcoe_check_wait_queue() - put the skb into fcoe pending xmit queue
+ * @lp: the fc_port for this skb
+ * @skb: the associated skb to be xmitted
+ *
+ * This empties the wait_queue, dequeue the head of the wait_queue queue
+ * and calls fcoe_start_io() for each packet, if all skb have been
+ * transmitted, return qlen or -1 if a error occurs, then restore
+ * wait_queue and  try again later.
+ *
+ * The wait_queue is used when the skb transmit fails. skb will go
+ * in the wait_queue which will be emptied by the time function OR
+ * by the next skb transmit.
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_check_wait_queue(struct fc_lport *lp)
+{
+	struct fcoe_softc *fc = lport_priv(lp);
+	struct sk_buff *skb;
+	int rc = -1;
+
+	spin_lock_bh(&fc->fcoe_pending_queue.lock);
+	if (fc->fcoe_pending_queue_active)
+		goto out;
+	fc->fcoe_pending_queue_active = 1;
+
+	while (fc->fcoe_pending_queue.qlen) {
+		/* keep qlen > 0 until fcoe_start_io succeeds */
+		fc->fcoe_pending_queue.qlen++;
+		skb = __skb_dequeue(&fc->fcoe_pending_queue);
+
+		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+		rc = fcoe_start_io(skb);
+		spin_lock_bh(&fc->fcoe_pending_queue.lock);
+
+		if (rc) {
+			__skb_queue_head(&fc->fcoe_pending_queue, skb);
+			/* undo temporary increment above */
+			fc->fcoe_pending_queue.qlen--;
+			break;
+		}
+		/* undo temporary increment above */
+		fc->fcoe_pending_queue.qlen--;
+	}
+
+	if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
+		lp->qfull = 0;
+	fc->fcoe_pending_queue_active = 0;
+	rc = fc->fcoe_pending_queue.qlen;
+out:
+	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+	return rc;
+}
+
+/**
+ * fcoe_dev_setup() - setup link change notification interface
+ */
+static void fcoe_dev_setup()
+{
+	/*
+	 * here setup a interface specific wd time to
+	 * monitor the link state
+	 */
+	register_netdevice_notifier(&fcoe_notifier);
+}
+
+/**
+ * fcoe_dev_setup() - cleanup link change notification interface
+ */
+static void fcoe_dev_cleanup(void)
+{
+	unregister_netdevice_notifier(&fcoe_notifier);
+}
+
+/**
+ * fcoe_device_notification() - netdev event notification callback
+ * @notifier: context of the notification
+ * @event: type of event
+ * @ptr: fixed array for output parsed ifname
+ *
+ * This function is called by the ethernet driver in case of link change event
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_device_notification(struct notifier_block *notifier,
+				    ulong event, void *ptr)
+{
+	struct fc_lport *lp = NULL;
+	struct net_device *real_dev = ptr;
+	struct fcoe_softc *fc;
+	struct fcoe_dev_stats *stats;
+	u32 new_link_up;
+	u32 mfs;
+	int rc = NOTIFY_OK;
+
+	read_lock(&fcoe_hostlist_lock);
+	list_for_each_entry(fc, &fcoe_hostlist, list) {
+		if (fc->real_dev == real_dev) {
+			lp = fc->lp;
+			break;
+		}
+	}
+	read_unlock(&fcoe_hostlist_lock);
+	if (lp == NULL) {
+		rc = NOTIFY_DONE;
+		goto out;
+	}
+
+	new_link_up = lp->link_up;
+	switch (event) {
+	case NETDEV_DOWN:
+	case NETDEV_GOING_DOWN:
+		new_link_up = 0;
+		break;
+	case NETDEV_UP:
+	case NETDEV_CHANGE:
+		new_link_up = !fcoe_link_ok(lp);
+		break;
+	case NETDEV_CHANGEMTU:
+		mfs = fc->real_dev->mtu -
+			(sizeof(struct fcoe_hdr) +
+			 sizeof(struct fcoe_crc_eof));
+		if (mfs >= FC_MIN_MAX_FRAME)
+			fc_set_mfs(lp, mfs);
+		new_link_up = !fcoe_link_ok(lp);
+		break;
+	case NETDEV_REGISTER:
+		break;
+	default:
+		FC_DBG("unknown event %ld call", event);
+	}
+	if (lp->link_up != new_link_up) {
+		if (new_link_up)
+			fc_linkup(lp);
+		else {
+			stats = fc_lport_get_stats(lp);
+			stats->LinkFailureCount++;
+			fc_linkdown(lp);
+			fcoe_clean_pending_queue(lp);
+		}
+	}
+out:
+	return rc;
+}
+
+/**
+ * fcoe_if_to_netdev() - parse a name buffer to get netdev
+ * @ifname: fixed array for output parsed ifname
+ * @buffer: incoming buffer to be copied
+ *
+ * Returns: NULL or ptr to netdeive
+ */
+static struct net_device *fcoe_if_to_netdev(const char *buffer)
+{
+	char *cp;
+	char ifname[IFNAMSIZ + 2];
+
+	if (buffer) {
+		strlcpy(ifname, buffer, IFNAMSIZ);
+		cp = ifname + strlen(ifname);
+		while (--cp >= ifname && *cp == '\n')
+			*cp = '\0';
+		return dev_get_by_name(&init_net, ifname);
+	}
+	return NULL;
+}
+
+/**
+ * fcoe_netdev_to_module_owner() - finds out the nic drive moddule of the netdev
+ * @netdev: the target netdev
+ *
+ * Returns: ptr to the struct module, NULL for failure
+ */
+static struct module *
+fcoe_netdev_to_module_owner(const struct net_device *netdev)
+{
+	struct device *dev;
+
+	if (!netdev)
+		return NULL;
+
+	dev = netdev->dev.parent;
+	if (!dev)
+		return NULL;
+
+	if (!dev->driver)
+		return NULL;
+
+	return dev->driver->owner;
+}
+
+/**
+ * fcoe_ethdrv_get() - Hold the Ethernet driver
+ * @netdev: the target netdev
+ *
+ * Holds the Ethernet driver module by try_module_get() for
+ * the corresponding netdev.
+ *
+ * Returns: 0 for succsss
+ */
+static int fcoe_ethdrv_get(const struct net_device *netdev)
+{
+	struct module *owner;
+
+	owner = fcoe_netdev_to_module_owner(netdev);
+	if (owner) {
+		printk(KERN_DEBUG "fcoe:hold driver module %s for %s\n",
+		       module_name(owner), netdev->name);
+		return  try_module_get(owner);
+	}
+	return -ENODEV;
+}
+
+/**
+ * fcoe_ethdrv_put() - Release the Ethernet driver
+ * @netdev: the target netdev
+ *
+ * Releases the Ethernet driver module by module_put for
+ * the corresponding netdev.
+ *
+ * Returns: 0 for succsss
+ */
+static int fcoe_ethdrv_put(const struct net_device *netdev)
+{
+	struct module *owner;
+
+	owner = fcoe_netdev_to_module_owner(netdev);
+	if (owner) {
+		printk(KERN_DEBUG "fcoe:release driver module %s for %s\n",
+		       module_name(owner), netdev->name);
+		module_put(owner);
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/**
+ * fcoe_destroy() - handles the destroy from sysfs
+ * @buffer: expcted to be a eth if name
+ * @kp: associated kernel param
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
+{
+	int rc;
+	struct net_device *netdev;
+
+	netdev = fcoe_if_to_netdev(buffer);
+	if (!netdev) {
+		rc = -ENODEV;
+		goto out_nodev;
+	}
+	/* look for existing lport */
+	if (!fcoe_hostlist_lookup(netdev)) {
+		rc = -ENODEV;
+		goto out_putdev;
+	}
+	rc = fcoe_if_destroy(netdev);
+	if (rc) {
+		printk(KERN_ERR "fcoe: fcoe_if_destroy(%s) failed\n",
+		       netdev->name);
+		rc = -EIO;
+		goto out_putdev;
+	}
+	fcoe_ethdrv_put(netdev);
+	rc = 0;
+out_putdev:
+	dev_put(netdev);
+out_nodev:
+	return rc;
+}
+
+/**
+ * fcoe_create() - Handles the create call from sysfs
+ * @buffer: expcted to be a eth if name
+ * @kp: associated kernel param
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_create(const char *buffer, struct kernel_param *kp)
+{
+	int rc;
+	struct net_device *netdev;
+
+	netdev = fcoe_if_to_netdev(buffer);
+	if (!netdev) {
+		rc = -ENODEV;
+		goto out_nodev;
+	}
+	/* look for existing lport */
+	if (fcoe_hostlist_lookup(netdev)) {
+		rc = -EEXIST;
+		goto out_putdev;
+	}
+	fcoe_ethdrv_get(netdev);
+
+	rc = fcoe_if_create(netdev);
+	if (rc) {
+		printk(KERN_ERR "fcoe: fcoe_if_create(%s) failed\n",
+		       netdev->name);
+		fcoe_ethdrv_put(netdev);
+		rc = -EIO;
+		goto out_putdev;
+	}
+	rc = 0;
+out_putdev:
+	dev_put(netdev);
+out_nodev:
+	return rc;
+}
+
+module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(create, "string");
+MODULE_PARM_DESC(create, "Create fcoe port using net device passed in.");
+module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(destroy, "string");
+MODULE_PARM_DESC(destroy, "Destroy fcoe port");
+
+/**
+ * fcoe_link_ok() - Check if link is ok for the fc_lport
+ * @lp: ptr to the fc_lport
+ *
+ * Any permanently-disqualifying conditions have been previously checked.
+ * This also updates the speed setting, which may change with link for 100/1000.
+ *
+ * This function should probably be checking for PAUSE support at some point
+ * in the future. Currently Per-priority-pause is not determinable using
+ * ethtool, so we shouldn't be restrictive until that problem is resolved.
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ *
+ */
+int fcoe_link_ok(struct fc_lport *lp)
+{
+	struct fcoe_softc *fc = lport_priv(lp);
+	struct net_device *dev = fc->real_dev;
+	struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+	int rc = 0;
+
+	if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) {
+		dev = fc->phys_dev;
+		if (dev->ethtool_ops->get_settings) {
+			dev->ethtool_ops->get_settings(dev, &ecmd);
+			lp->link_supported_speeds &=
+				~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
+			if (ecmd.supported & (SUPPORTED_1000baseT_Half |
+					      SUPPORTED_1000baseT_Full))
+				lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
+			if (ecmd.supported & SUPPORTED_10000baseT_Full)
+				lp->link_supported_speeds |=
+					FC_PORTSPEED_10GBIT;
+			if (ecmd.speed == SPEED_1000)
+				lp->link_speed = FC_PORTSPEED_1GBIT;
+			if (ecmd.speed == SPEED_10000)
+				lp->link_speed = FC_PORTSPEED_10GBIT;
+		}
+	} else
+		rc = -1;
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(fcoe_link_ok);
+
+/**
+ * fcoe_percpu_clean() - Clear the pending skbs for an lport
+ * @lp: the fc_lport
+ */
+void fcoe_percpu_clean(struct fc_lport *lp)
+{
+	struct fcoe_percpu_s *pp;
+	struct fcoe_rcv_info *fr;
+	struct sk_buff_head *list;
+	struct sk_buff *skb, *next;
+	struct sk_buff *head;
+	unsigned int cpu;
+
+	for_each_possible_cpu(cpu) {
+		pp = &per_cpu(fcoe_percpu, cpu);
+		spin_lock_bh(&pp->fcoe_rx_list.lock);
+		list = &pp->fcoe_rx_list;
+		head = list->next;
+		for (skb = head; skb != (struct sk_buff *)list;
+		     skb = next) {
+			next = skb->next;
+			fr = fcoe_dev_from_skb(skb);
+			if (fr->fr_dev == lp) {
+				__skb_unlink(skb, list);
+				kfree_skb(skb);
+			}
+		}
+		spin_unlock_bh(&pp->fcoe_rx_list.lock);
+	}
+}
+EXPORT_SYMBOL_GPL(fcoe_percpu_clean);
+
+/**
+ * fcoe_clean_pending_queue() - Dequeue a skb and free it
+ * @lp: the corresponding fc_lport
+ *
+ * Returns: none
+ */
+void fcoe_clean_pending_queue(struct fc_lport *lp)
+{
+	struct fcoe_softc  *fc = lport_priv(lp);
+	struct sk_buff *skb;
+
+	spin_lock_bh(&fc->fcoe_pending_queue.lock);
+	while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
+		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+		kfree_skb(skb);
+		spin_lock_bh(&fc->fcoe_pending_queue.lock);
+	}
+	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+}
+EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
+
+/**
+ * libfc_host_alloc() - Allocate a Scsi_Host with room for the fc_lport
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ * TODO: to libfc?
+ */
+static inline struct Scsi_Host *
+libfc_host_alloc(struct scsi_host_template *sht, int priv_size)
+{
+	return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
+}
+
+/**
+ * fcoe_host_alloc() - Allocate a Scsi_Host with room for the fcoe_softc
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ */
+struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *sht, int priv_size)
+{
+	return libfc_host_alloc(sht, sizeof(struct fcoe_softc) + priv_size);
+}
+EXPORT_SYMBOL_GPL(fcoe_host_alloc);
+
+/**
+ * fcoe_reset() - Resets the fcoe
+ * @shost: shost the reset is from
+ *
+ * Returns: always 0
+ */
+int fcoe_reset(struct Scsi_Host *shost)
+{
+	struct fc_lport *lport = shost_priv(shost);
+	fc_lport_reset(lport);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_reset);
+
+/**
+ * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN.
+ * @mac: mac address
+ * @scheme: check port
+ * @port: port indicator for converting
+ *
+ * Returns: u64 fc world wide name
+ */
+u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
+		      unsigned int scheme, unsigned int port)
+{
+	u64 wwn;
+	u64 host_mac;
+
+	/* The MAC is in NO, so flip only the low 48 bits */
+	host_mac = ((u64) mac[0] << 40) |
+		((u64) mac[1] << 32) |
+		((u64) mac[2] << 24) |
+		((u64) mac[3] << 16) |
+		((u64) mac[4] << 8) |
+		(u64) mac[5];
+
+	WARN_ON(host_mac >= (1ULL << 48));
+	wwn = host_mac | ((u64) scheme << 60);
+	switch (scheme) {
+	case 1:
+		WARN_ON(port != 0);
+		break;
+	case 2:
+		WARN_ON(port >= 0xfff);
+		wwn |= (u64) port << 48;
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+
+	return wwn;
+}
+EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
+
+/**
+ * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device
+ * @device: this is currently ptr to net_device
+ *
+ * Returns: NULL or the located fcoe_softc
+ */
+static struct fcoe_softc *
+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);
+			return fc;
+		}
+	}
+	read_unlock(&fcoe_hostlist_lock);
+	return NULL;
+}
+
+/**
+ * fcoe_hostlist_lookup() - Find the corresponding lport by netdev
+ * @netdev: ptr to net_device
+ *
+ * Returns: 0 for success
+ */
+struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
+{
+	struct fcoe_softc *fc;
+
+	fc = fcoe_hostlist_lookup_softc(netdev);
+
+	return (fc) ? fc->lp : NULL;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup);
+
+/**
+ * fcoe_hostlist_add() - Add a lport to lports list
+ * @lp: ptr to the fc_lport to badded
+ *
+ * Returns: 0 for success
+ */
+int fcoe_hostlist_add(const struct fc_lport *lp)
+{
+	struct fcoe_softc *fc;
+
+	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;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_add);
+
+/**
+ * fcoe_hostlist_remove() - remove a lport from lports list
+ * @lp: ptr to the fc_lport to badded
+ *
+ * Returns: 0 for success
+ */
+int fcoe_hostlist_remove(const struct fc_lport *lp)
+{
+	struct fcoe_softc *fc;
+
+	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);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_remove);
+
+/**
+ * fcoe_libfc_config() - sets up libfc related properties for lport
+ * @lp: ptr to the fc_lport
+ * @tt: libfc function template
+ *
+ * Returns : 0 for success
+ */
+int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
+{
+	/* Set the function pointers set by the LLDD */
+	memcpy(&lp->tt, tt, sizeof(*tt));
+	if (fc_fcp_init(lp))
+		return -ENOMEM;
+	fc_exch_init(lp);
+	fc_elsct_init(lp);
+	fc_lport_init(lp);
+	fc_rport_init(lp);
+	fc_disc_init(lp);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
+/**
+ * fcoe_init() - fcoe module loading initialization
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int __init fcoe_init(void)
+{
+	unsigned int cpu;
+	int rc = 0;
+	struct fcoe_percpu_s *p;
+
+	INIT_LIST_HEAD(&fcoe_hostlist);
+	rwlock_init(&fcoe_hostlist_lock);
+
+	for_each_possible_cpu(cpu) {
+		p = &per_cpu(fcoe_percpu, cpu);
+		skb_queue_head_init(&p->fcoe_rx_list);
+	}
+
+	for_each_online_cpu(cpu)
+		fcoe_percpu_thread_create(cpu);
+
+	/* Initialize per CPU interrupt thread */
+	rc = register_hotcpu_notifier(&fcoe_cpu_notifier);
+	if (rc)
+		goto out_free;
+
+	/* Setup link change notification */
+	fcoe_dev_setup();
+
+	setup_timer(&fcoe_timer, fcoe_watchdog, 0);
+
+	mod_timer(&fcoe_timer, jiffies + (10 * HZ));
+
+	fcoe_if_init();
+
+	return 0;
+
+out_free:
+	for_each_online_cpu(cpu) {
+		fcoe_percpu_thread_destroy(cpu);
+	}
+
+	return rc;
+}
+module_init(fcoe_init);
+
+/**
+ * fcoe_exit() - fcoe module unloading cleanup
+ *
+ * Returns 0 on success, negative on failure
+ */
+static void __exit fcoe_exit(void)
+{
+	unsigned int cpu;
+	struct fcoe_softc *fc, *tmp;
+
+	fcoe_dev_cleanup();
+
+	/* Stop the timer */
+	del_timer_sync(&fcoe_timer);
+
+	/* releases the associated fcoe hosts */
+	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
+		fcoe_if_destroy(fc->real_dev);
+
+	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
+
+	for_each_online_cpu(cpu) {
+		fcoe_percpu_thread_destroy(cpu);
+	}
+}
+module_exit(fcoe_exit);
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
deleted file mode 100644
index cbebbf7..0000000
--- a/drivers/scsi/fcoe/libfcoe.c
+++ /dev/null
@@ -1,1972 +0,0 @@
-/*
- * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Maintained at www.Open-FCoE.org
- */
-
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/if_ether.h>
-#include <linux/if_vlan.h>
-#include <linux/kthread.h>
-#include <linux/crc32.h>
-#include <linux/cpu.h>
-#include <linux/fs.h>
-#include <linux/sysfs.h>
-#include <linux/ctype.h>
-#include <scsi/scsi_tcq.h>
-#include <scsi/scsicam.h>
-#include <scsi/scsi_transport.h>
-#include <scsi/scsi_transport_fc.h>
-#include <net/rtnetlink.h>
-
-#include <scsi/fc/fc_encaps.h>
-
-#include <scsi/libfc.h>
-#include <scsi/fc_frame.h>
-#include <scsi/libfcoe.h>
-
-static int debug_fcoe;
-
-#define FCOE_MAX_QUEUE_DEPTH  256
-#define FCOE_LOW_QUEUE_DEPTH  32
-
-/* destination address mode */
-#define FCOE_GW_ADDR_MODE	    0x00
-#define FCOE_FCOUI_ADDR_MODE	    0x01
-
-#define FCOE_WORD_TO_BYTE  4
-
-#define FCOE_VERSION	"0.1"
-#define	FCOE_NAME	"fcoe"
-#define	FCOE_VENDOR	"Open-FCoE.org"
-
-#define FCOE_MAX_LUN		255
-#define FCOE_MAX_FCP_TARGET	256
-
-#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
-
-#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
-#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
-
-MODULE_AUTHOR("Open-FCoE.org");
-MODULE_DESCRIPTION("FCoE");
-MODULE_LICENSE("GPL");
-
-/* fcoe host list */
-LIST_HEAD(fcoe_hostlist);
-DEFINE_RWLOCK(fcoe_hostlist_lock);
-DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
-DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
-
-
-/* Function Prototyes */
-static int fcoe_check_wait_queue(struct fc_lport *);
-static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
-static int fcoe_device_notification(struct notifier_block *, ulong, void *);
-static void fcoe_dev_setup(void);
-static void fcoe_dev_cleanup(void);
-
-/* notification function from net device */
-static struct notifier_block fcoe_notifier = {
-	.notifier_call = fcoe_device_notification,
-};
-
-static struct scsi_transport_template *scsi_transport_fcoe_sw;
-
-struct fc_function_template fcoe_transport_function = {
-	.show_host_node_name = 1,
-	.show_host_port_name = 1,
-	.show_host_supported_classes = 1,
-	.show_host_supported_fc4s = 1,
-	.show_host_active_fc4s = 1,
-	.show_host_maxframe_size = 1,
-
-	.show_host_port_id = 1,
-	.show_host_supported_speeds = 1,
-	.get_host_speed = fc_get_host_speed,
-	.show_host_speed = 1,
-	.show_host_port_type = 1,
-	.get_host_port_state = fc_get_host_port_state,
-	.show_host_port_state = 1,
-	.show_host_symbolic_name = 1,
-
-	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
-	.show_rport_maxframe_size = 1,
-	.show_rport_supported_classes = 1,
-
-	.show_host_fabric_name = 1,
-	.show_starget_node_name = 1,
-	.show_starget_port_name = 1,
-	.show_starget_port_id = 1,
-	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
-	.show_rport_dev_loss_tmo = 1,
-	.get_fc_host_stats = fc_get_host_stats,
-	.issue_fc_host_lip = fcoe_reset,
-
-	.terminate_rport_io = fc_rport_terminate_io,
-};
-
-static struct scsi_host_template fcoe_shost_template = {
-	.module = THIS_MODULE,
-	.name = "FCoE Driver",
-	.proc_name = FCOE_NAME,
-	.queuecommand = fc_queuecommand,
-	.eh_abort_handler = fc_eh_abort,
-	.eh_device_reset_handler = fc_eh_device_reset,
-	.eh_host_reset_handler = fc_eh_host_reset,
-	.slave_alloc = fc_slave_alloc,
-	.change_queue_depth = fc_change_queue_depth,
-	.change_queue_type = fc_change_queue_type,
-	.this_id = -1,
-	.cmd_per_lun = 32,
-	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
-	.use_clustering = ENABLE_CLUSTERING,
-	.sg_tablesize = SG_ALL,
-	.max_sectors = 0xffff,
-};
-
-/**
- * fcoe_lport_config() - sets up the fc_lport
- * @lp: ptr to the fc_lport
- * @shost: ptr to the parent scsi host
- *
- * Returns: 0 for success
- */
-static int fcoe_lport_config(struct fc_lport *lp)
-{
-	lp->link_up = 0;
-	lp->qfull = 0;
-	lp->max_retry_count = 3;
-	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
-	lp->r_a_tov = 2 * 2 * 1000;
-	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
-			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
-
-	fc_lport_init_stats(lp);
-
-	/* lport fc_lport related configuration */
-	fc_lport_config(lp);
-
-	/* offload related configuration */
-	lp->crc_offload = 0;
-	lp->seq_offload = 0;
-	lp->lro_enabled = 0;
-	lp->lro_xid = 0;
-	lp->lso_max = 0;
-
-	return 0;
-}
-
-/**
- * fcoe_netdev_config() - Set up netdev for SW FCoE
- * @lp : ptr to the fc_lport
- * @netdev : ptr to the associated netdevice struct
- *
- * Must be called after fcoe_lport_config() as it will use lport mutex
- *
- * Returns : 0 for success
- */
-static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
-{
-	u32 mfs;
-	u64 wwnn, wwpn;
-	struct fcoe_softc *fc;
-	u8 flogi_maddr[ETH_ALEN];
-
-	/* Setup lport private data to point to fcoe softc */
-	fc = lport_priv(lp);
-	fc->lp = lp;
-	fc->real_dev = netdev;
-	fc->phys_dev = netdev;
-
-	/* Require support for get_pauseparam ethtool op. */
-	if (netdev->priv_flags & IFF_802_1Q_VLAN)
-		fc->phys_dev = vlan_dev_real_dev(netdev);
-
-	/* Do not support for bonding device */
-	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
-	    (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
-	    (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
-		return -EOPNOTSUPP;
-	}
-
-	/*
-	 * Determine max frame size based on underlying device and optional
-	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
-	 * will return 0, so do this first.
-	 */
-	mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
-				   sizeof(struct fcoe_crc_eof));
-	if (fc_set_mfs(lp, mfs))
-		return -EINVAL;
-
-	if (!fcoe_link_ok(lp))
-		lp->link_up = 1;
-
-	/* offload features support */
-	if (fc->real_dev->features & NETIF_F_SG)
-		lp->sg_supp = 1;
-
-#ifdef NETIF_F_FCOE_CRC
-	if (netdev->features & NETIF_F_FCOE_CRC) {
-		lp->crc_offload = 1;
-		printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
-		       netdev->name);
-	}
-#endif
-#ifdef NETIF_F_FSO
-	if (netdev->features & NETIF_F_FSO) {
-		lp->seq_offload = 1;
-		lp->lso_max = netdev->gso_max_size;
-		printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
-		       netdev->name, lp->lso_max);
-	}
-#endif
-	if (netdev->fcoe_ddp_xid) {
-		lp->lro_enabled = 1;
-		lp->lro_xid = netdev->fcoe_ddp_xid;
-		printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
-		       netdev->name, lp->lro_xid);
-	}
-	skb_queue_head_init(&fc->fcoe_pending_queue);
-	fc->fcoe_pending_queue_active = 0;
-
-	/* setup Source Mac Address */
-	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
-	       fc->real_dev->addr_len);
-
-	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
-	fc_set_wwnn(lp, wwnn);
-	/* XXX - 3rd arg needs to be vlan id */
-	wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
-	fc_set_wwpn(lp, wwpn);
-
-	/*
-	 * Add FCoE MAC address as second unicast MAC address
-	 * or enter promiscuous mode if not capable of listening
-	 * for multiple unicast MACs.
-	 */
-	rtnl_lock();
-	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-	dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
-	rtnl_unlock();
-
-	/*
-	 * setup the receive function from ethernet driver
-	 * on the ethertype for the given device
-	 */
-	fc->fcoe_packet_type.func = fcoe_rcv;
-	fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
-	fc->fcoe_packet_type.dev = fc->real_dev;
-	dev_add_pack(&fc->fcoe_packet_type);
-
-	return 0;
-}
-
-/**
- * fcoe_shost_config() - Sets up fc_lport->host
- * @lp : ptr to the fc_lport
- * @shost : ptr to the associated scsi host
- * @dev : device associated to scsi host
- *
- * Must be called after fcoe_lport_config() and fcoe_netdev_config()
- *
- * Returns : 0 for success
- */
-static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
-				struct device *dev)
-{
-	int rc = 0;
-
-	/* lport scsi host config */
-	lp->host = shost;
-
-	lp->host->max_lun = FCOE_MAX_LUN;
-	lp->host->max_id = FCOE_MAX_FCP_TARGET;
-	lp->host->max_channel = 0;
-	lp->host->transportt = scsi_transport_fcoe_sw;
-
-	/* add the new host to the SCSI-ml */
-	rc = scsi_add_host(lp->host, dev);
-	if (rc) {
-		FC_DBG("fcoe_shost_config:error on scsi_add_host\n");
-		return rc;
-	}
-	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
-		FCOE_NAME, FCOE_VERSION,
-		fcoe_netdev(lp)->name);
-
-	return 0;
-}
-
-/**
- * fcoe_em_config() - 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)
-{
-	BUG_ON(lp->emp);
-
-	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
-				    FCOE_MIN_XID, FCOE_MAX_XID);
-	if (!lp->emp)
-		return -ENOMEM;
-
-	return 0;
-}
-
-/**
- * fcoe_if_destroy() - FCoE software HBA tear-down function
- * @netdev: ptr to the associated net_device
- *
- * Returns: 0 if link is OK for use by FCoE.
- */
-static int fcoe_if_destroy(struct net_device *netdev)
-{
-	struct fc_lport *lp = NULL;
-	struct fcoe_softc *fc;
-	u8 flogi_maddr[ETH_ALEN];
-
-	BUG_ON(!netdev);
-
-	printk(KERN_DEBUG "fcoe_if_destroy:interface on %s\n",
-	       netdev->name);
-
-	lp = fcoe_hostlist_lookup(netdev);
-	if (!lp)
-		return -ENODEV;
-
-	fc = lport_priv(lp);
-
-	/* Logout of the fabric */
-	fc_fabric_logoff(lp);
-
-	/* Remove the instance from fcoe's list */
-	fcoe_hostlist_remove(lp);
-
-	/* Don't listen for Ethernet packets anymore */
-	dev_remove_pack(&fc->fcoe_packet_type);
-
-	/* Cleanup the fc_lport */
-	fc_lport_destroy(lp);
-	fc_fcp_destroy(lp);
-
-	/* Detach from the scsi-ml */
-	fc_remove_host(lp->host);
-	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);
-
-	/* Delete secondary MAC addresses */
-	rtnl_lock();
-	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
-	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
-		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
-	rtnl_unlock();
-
-	/* Free the per-CPU revieve threads */
-	fcoe_percpu_clean(lp);
-
-	/* Free existing skbs */
-	fcoe_clean_pending_queue(lp);
-
-	/* Free memory used by statistical counters */
-	fc_lport_free_stats(lp);
-
-	/* Release the net_device and Scsi_Host */
-	dev_put(fc->real_dev);
-	scsi_host_put(lp->host);
-
-	return 0;
-}
-
-/*
- * fcoe_ddp_setup - calls LLD's ddp_setup through net_device
- * @lp:	the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- * @sgl: the scatterlist describing this transfer
- * @sgc: number of sg items
- *
- * Returns : 0 no ddp
- */
-static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid,
-			     struct scatterlist *sgl, unsigned int sgc)
-{
-	struct net_device *n = fcoe_netdev(lp);
-
-	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
-		return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
-
-	return 0;
-}
-
-/*
- * fcoe_ddp_done - calls LLD's ddp_done through net_device
- * @lp:	the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- *
- * Returns : the length of data that have been completed by ddp
- */
-static int fcoe_ddp_done(struct fc_lport *lp, u16 xid)
-{
-	struct net_device *n = fcoe_netdev(lp);
-
-	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
-		return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
-	return 0;
-}
-
-static struct libfc_function_template fcoe_libfc_fcn_templ = {
-	.frame_send = fcoe_xmit,
-	.ddp_setup = fcoe_ddp_setup,
-	.ddp_done = fcoe_ddp_done,
-};
-
-/**
- * fcoe_if_create() - this function creates the fcoe interface
- * @netdev: pointer the associated netdevice
- *
- * Creates fc_lport struct and scsi_host for lport, configures lport
- * and starts fabric login.
- *
- * Returns : 0 on success
- */
-static int fcoe_if_create(struct net_device *netdev)
-{
-	int rc;
-	struct fc_lport *lp = NULL;
-	struct fcoe_softc *fc;
-	struct Scsi_Host *shost;
-
-	BUG_ON(!netdev);
-
-	printk(KERN_DEBUG "fcoe_if_create:interface on %s\n",
-	       netdev->name);
-
-	lp = fcoe_hostlist_lookup(netdev);
-	if (lp)
-		return -EEXIST;
-
-	shost = fcoe_host_alloc(&fcoe_shost_template,
-				sizeof(struct fcoe_softc));
-	if (!shost) {
-		FC_DBG("Could not allocate host structure\n");
-		return -ENOMEM;
-	}
-	lp = shost_priv(shost);
-	fc = lport_priv(lp);
-
-	/* configure fc_lport, e.g., em */
-	rc = fcoe_lport_config(lp);
-	if (rc) {
-		FC_DBG("Could not configure lport\n");
-		goto out_host_put;
-	}
-
-	/* configure lport network properties */
-	rc = fcoe_netdev_config(lp, netdev);
-	if (rc) {
-		FC_DBG("Could not configure netdev for lport\n");
-		goto out_host_put;
-	}
-
-	/* configure lport scsi host properties */
-	rc = fcoe_shost_config(lp, shost, &netdev->dev);
-	if (rc) {
-		FC_DBG("Could not configure shost for lport\n");
-		goto out_host_put;
-	}
-
-	/* lport exch manager allocation */
-	rc = fcoe_em_config(lp);
-	if (rc) {
-		FC_DBG("Could not configure em for lport\n");
-		goto out_host_put;
-	}
-
-	/* Initialize the library */
-	rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ);
-	if (rc) {
-		FC_DBG("Could not configure libfc for lport!\n");
-		goto out_lp_destroy;
-	}
-
-	/* add to lports list */
-	fcoe_hostlist_add(lp);
-
-	lp->boot_time = jiffies;
-
-	fc_fabric_login(lp);
-
-	dev_hold(netdev);
-
-	return rc;
-
-out_lp_destroy:
-	fc_exch_mgr_free(lp->emp); /* Free the EM */
-out_host_put:
-	scsi_host_put(lp->host);
-	return rc;
-}
-
-/**
- * fcoe_if_init() - attach to scsi transport
- *
- * Returns : 0 on success
- */
-static int __init fcoe_if_init(void)
-{
-	/* attach to scsi transport */
-	scsi_transport_fcoe_sw =
-		fc_attach_transport(&fcoe_transport_function);
-
-	if (!scsi_transport_fcoe_sw) {
-		printk(KERN_ERR "fcoe_init:fc_attach_transport() failed\n");
-		return -ENODEV;
-	}
-
-	return 0;
-}
-
-/**
- * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
- * @cpu: cpu index for the online cpu
- */
-static void fcoe_percpu_thread_create(unsigned int cpu)
-{
-	struct fcoe_percpu_s *p;
-	struct task_struct *thread;
-
-	p = &per_cpu(fcoe_percpu, cpu);
-
-	thread = kthread_create(fcoe_percpu_receive_thread,
-				(void *)p, "fcoethread/%d", cpu);
-
-	if (likely(!IS_ERR(p->thread))) {
-		kthread_bind(thread, cpu);
-		wake_up_process(thread);
-
-		spin_lock_bh(&p->fcoe_rx_list.lock);
-		p->thread = thread;
-		spin_unlock_bh(&p->fcoe_rx_list.lock);
-	}
-}
-
-/**
- * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
- * @cpu: cpu index the rx thread is to be removed
- *
- * Destroys a per-CPU Rx thread. Any pending skbs are moved to the
- * current CPU's Rx thread. If the thread being destroyed is bound to
- * the CPU processing this context the skbs will be freed.
- */
-static void fcoe_percpu_thread_destroy(unsigned int cpu)
-{
-	struct fcoe_percpu_s *p;
-	struct task_struct *thread;
-	struct page *crc_eof;
-	struct sk_buff *skb;
-#ifdef CONFIG_SMP
-	struct fcoe_percpu_s *p0;
-	unsigned targ_cpu = smp_processor_id();
-#endif /* CONFIG_SMP */
-
-	printk(KERN_DEBUG "fcoe: Destroying receive thread for CPU %d\n", cpu);
-
-	/* Prevent any new skbs from being queued for this CPU. */
-	p = &per_cpu(fcoe_percpu, cpu);
-	spin_lock_bh(&p->fcoe_rx_list.lock);
-	thread = p->thread;
-	p->thread = NULL;
-	crc_eof = p->crc_eof_page;
-	p->crc_eof_page = NULL;
-	p->crc_eof_offset = 0;
-	spin_unlock_bh(&p->fcoe_rx_list.lock);
-
-#ifdef CONFIG_SMP
-	/*
-	 * Don't bother moving the skb's if this context is running
-	 * on the same CPU that is having its thread destroyed. This
-	 * can easily happen when the module is removed.
-	 */
-	if (cpu != targ_cpu) {
-		p0 = &per_cpu(fcoe_percpu, targ_cpu);
-		spin_lock_bh(&p0->fcoe_rx_list.lock);
-		if (p0->thread) {
-			FC_DBG("Moving frames from CPU %d to CPU %d\n",
-			       cpu, targ_cpu);
-
-			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-				__skb_queue_tail(&p0->fcoe_rx_list, skb);
-			spin_unlock_bh(&p0->fcoe_rx_list.lock);
-		} else {
-			/*
-			 * The targeted CPU is not initialized and cannot accept
-			 * new  skbs. Unlock the targeted CPU and drop the skbs
-			 * on the CPU that is going offline.
-			 */
-			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-				kfree_skb(skb);
-			spin_unlock_bh(&p0->fcoe_rx_list.lock);
-		}
-	} else {
-		/*
-		 * This scenario occurs when the module is being removed
-		 * and all threads are being destroyed. skbs will continue
-		 * to be shifted from the CPU thread that is being removed
-		 * to the CPU thread associated with the CPU that is processing
-		 * the module removal. Once there is only one CPU Rx thread it
-		 * will reach this case and we will drop all skbs and later
-		 * stop the thread.
-		 */
-		spin_lock_bh(&p->fcoe_rx_list.lock);
-		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-			kfree_skb(skb);
-		spin_unlock_bh(&p->fcoe_rx_list.lock);
-	}
-#else
-	/*
-	 * This a non-SMP scenario where the singluar Rx thread is
-	 * being removed. Free all skbs and stop the thread.
-	 */
-	spin_lock_bh(&p->fcoe_rx_list.lock);
-	while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-		kfree_skb(skb);
-	spin_unlock_bh(&p->fcoe_rx_list.lock);
-#endif
-
-	if (thread)
-		kthread_stop(thread);
-
-	if (crc_eof)
-		put_page(crc_eof);
-}
-
-/**
- * fcoe_cpu_callback() - fcoe cpu hotplug event callback
- * @nfb: callback data block
- * @action: event triggering the callback
- * @hcpu: index for the cpu of this event
- *
- * This creates or destroys per cpu data for fcoe
- *
- * Returns NOTIFY_OK always.
- */
-static int fcoe_cpu_callback(struct notifier_block *nfb,
-			     unsigned long action, void *hcpu)
-{
-	unsigned cpu = (unsigned long)hcpu;
-
-	switch (action) {
-	case CPU_ONLINE:
-	case CPU_ONLINE_FROZEN:
-		FC_DBG("CPU %x online: Create Rx thread\n", cpu);
-		fcoe_percpu_thread_create(cpu);
-		break;
-	case CPU_DEAD:
-	case CPU_DEAD_FROZEN:
-		FC_DBG("CPU %x offline: Remove Rx thread\n", cpu);
-		fcoe_percpu_thread_destroy(cpu);
-		break;
-	default:
-		break;
-	}
-	return NOTIFY_OK;
-}
-
-static struct notifier_block fcoe_cpu_notifier = {
-	.notifier_call = fcoe_cpu_callback,
-};
-
-/**
- * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ
- * @skb: the receive skb
- * @dev: associated net device
- * @ptype: context
- * @odldev: last device
- *
- * this function will receive the packet and build fc frame and pass it up
- *
- * Returns: 0 for success
- */
-int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
-	     struct packet_type *ptype, struct net_device *olddev)
-{
-	struct fc_lport *lp;
-	struct fcoe_rcv_info *fr;
-	struct fcoe_softc *fc;
-	struct fc_frame_header *fh;
-	struct fcoe_percpu_s *fps;
-	unsigned short oxid;
-	unsigned int cpu = 0;
-
-	fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
-	lp = fc->lp;
-	if (unlikely(lp == NULL)) {
-		FC_DBG("cannot find hba structure");
-		goto err2;
-	}
-
-	if (unlikely(debug_fcoe)) {
-		FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p "
-		       "end:%p sum:%d dev:%s", skb->len, skb->data_len,
-		       skb->head, skb->data, skb_tail_pointer(skb),
-		       skb_end_pointer(skb), skb->csum,
-		       skb->dev ? skb->dev->name : "<NULL>");
-
-	}
-
-	/* check for FCOE packet type */
-	if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
-		FC_DBG("wrong FC type frame");
-		goto err;
-	}
-
-	/*
-	 * Check for minimum frame length, and make sure required FCoE
-	 * and FC headers are pulled into the linear data area.
-	 */
-	if (unlikely((skb->len < FCOE_MIN_FRAME) ||
-	    !pskb_may_pull(skb, FCOE_HEADER_LEN)))
-		goto err;
-
-	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.
-	 */
-	cpu = oxid & (num_online_cpus() - 1);
-#endif
-
-	fps = &per_cpu(fcoe_percpu, cpu);
-	spin_lock_bh(&fps->fcoe_rx_list.lock);
-	if (unlikely(!fps->thread)) {
-		/*
-		 * The targeted CPU is not ready, let's target
-		 * the first CPU now. For non-SMP systems this
-		 * will check the same CPU twice.
-		 */
-		FC_DBG("CPU is online, but no receive thread ready "
-		       "for incoming skb- using first online CPU.\n");
-
-		spin_unlock_bh(&fps->fcoe_rx_list.lock);
-		cpu = first_cpu(cpu_online_map);
-		fps = &per_cpu(fcoe_percpu, cpu);
-		spin_lock_bh(&fps->fcoe_rx_list.lock);
-		if (!fps->thread) {
-			spin_unlock_bh(&fps->fcoe_rx_list.lock);
-			goto err;
-		}
-	}
-
-	/*
-	 * We now have a valid CPU that we're targeting for
-	 * this skb. We also have this receive thread locked,
-	 * so we're free to queue skbs into it's queue.
-	 */
-	__skb_queue_tail(&fps->fcoe_rx_list, skb);
-	if (fps->fcoe_rx_list.qlen == 1)
-		wake_up_process(fps->thread);
-
-	spin_unlock_bh(&fps->fcoe_rx_list.lock);
-
-	return 0;
-err:
-	fc_lport_get_stats(lp)->ErrorFrames++;
-
-err2:
-	kfree_skb(skb);
-	return -1;
-}
-EXPORT_SYMBOL_GPL(fcoe_rcv);
-
-/**
- * fcoe_start_io() - pass to netdev to start xmit for fcoe
- * @skb: the skb to be xmitted
- *
- * Returns: 0 for success
- */
-static inline int fcoe_start_io(struct sk_buff *skb)
-{
-	int rc;
-
-	skb_get(skb);
-	rc = dev_queue_xmit(skb);
-	if (rc != 0)
-		return rc;
-	kfree_skb(skb);
-	return 0;
-}
-
-/**
- * fcoe_get_paged_crc_eof() - in case we need alloc a page for crc_eof
- * @skb: the skb to be xmitted
- * @tlen: total len
- *
- * Returns: 0 for success
- */
-static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
-{
-	struct fcoe_percpu_s *fps;
-	struct page *page;
-
-	fps = &get_cpu_var(fcoe_percpu);
-	page = fps->crc_eof_page;
-	if (!page) {
-		page = alloc_page(GFP_ATOMIC);
-		if (!page) {
-			put_cpu_var(fcoe_percpu);
-			return -ENOMEM;
-		}
-		fps->crc_eof_page = page;
-		fps->crc_eof_offset = 0;
-	}
-
-	get_page(page);
-	skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page,
-			   fps->crc_eof_offset, tlen);
-	skb->len += tlen;
-	skb->data_len += tlen;
-	skb->truesize += tlen;
-	fps->crc_eof_offset += sizeof(struct fcoe_crc_eof);
-
-	if (fps->crc_eof_offset >= PAGE_SIZE) {
-		fps->crc_eof_page = NULL;
-		fps->crc_eof_offset = 0;
-		put_page(page);
-	}
-	put_cpu_var(fcoe_percpu);
-	return 0;
-}
-
-/**
- * fcoe_fc_crc() - calculates FC CRC in this fcoe skb
- * @fp: the fc_frame containg data to be checksummed
- *
- * This uses crc32() to calculate the crc for fc frame
- * Return   : 32 bit crc
- */
-u32 fcoe_fc_crc(struct fc_frame *fp)
-{
-	struct sk_buff *skb = fp_skb(fp);
-	struct skb_frag_struct *frag;
-	unsigned char *data;
-	unsigned long off, len, clen;
-	u32 crc;
-	unsigned i;
-
-	crc = crc32(~0, skb->data, skb_headlen(skb));
-
-	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-		frag = &skb_shinfo(skb)->frags[i];
-		off = frag->page_offset;
-		len = frag->size;
-		while (len > 0) {
-			clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK));
-			data = kmap_atomic(frag->page + (off >> PAGE_SHIFT),
-					   KM_SKB_DATA_SOFTIRQ);
-			crc = crc32(crc, data + (off & ~PAGE_MASK), clen);
-			kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ);
-			off += clen;
-			len -= clen;
-		}
-	}
-	return crc;
-}
-EXPORT_SYMBOL_GPL(fcoe_fc_crc);
-
-/**
- * fcoe_xmit() - FCoE frame transmit function
- * @lp:	the associated local port
- * @fp: the fc_frame to be transmitted
- *
- * Return   : 0 for success
- */
-int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
-{
-	int wlen, rc = 0;
-	u32 crc;
-	struct ethhdr *eh;
-	struct fcoe_crc_eof *cp;
-	struct sk_buff *skb;
-	struct fcoe_dev_stats *stats;
-	struct fc_frame_header *fh;
-	unsigned int hlen;		/* header length implies the version */
-	unsigned int tlen;		/* trailer length */
-	unsigned int elen;		/* eth header, may include vlan */
-	int flogi_in_progress = 0;
-	struct fcoe_softc *fc;
-	u8 sof, eof;
-	struct fcoe_hdr *hp;
-
-	WARN_ON((fr_len(fp) % sizeof(u32)) != 0);
-
-	fc = lport_priv(lp);
-	/*
-	 * if it is a flogi then we need to learn gw-addr
-	 * and my own fcid
-	 */
-	fh = fc_frame_header_get(fp);
-	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
-		if (fc_frame_payload_op(fp) == ELS_FLOGI) {
-			fc->flogi_oxid = ntohs(fh->fh_ox_id);
-			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
-			fc->flogi_progress = 1;
-			flogi_in_progress = 1;
-		} else if (fc->flogi_progress && ntoh24(fh->fh_s_id) != 0) {
-			/*
-			 * Here we must've gotten an SID by accepting an FLOGI
-			 * from a point-to-point connection.  Switch to using
-			 * the source mac based on the SID.  The destination
-			 * MAC in this case would have been set by receving the
-			 * FLOGI.
-			 */
-			fc_fcoe_set_mac(fc->data_src_addr, fh->fh_s_id);
-			fc->flogi_progress = 0;
-		}
-	}
-
-	skb = fp_skb(fp);
-	sof = fr_sof(fp);
-	eof = fr_eof(fp);
-
-	elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ?
-		sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr);
-	hlen = sizeof(struct fcoe_hdr);
-	tlen = sizeof(struct fcoe_crc_eof);
-	wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
-
-	/* crc offload */
-	if (likely(lp->crc_offload)) {
-		skb->ip_summed = CHECKSUM_PARTIAL;
-		skb->csum_start = skb_headroom(skb);
-		skb->csum_offset = skb->len;
-		crc = 0;
-	} else {
-		skb->ip_summed = CHECKSUM_NONE;
-		crc = fcoe_fc_crc(fp);
-	}
-
-	/* copy fc crc and eof to the skb buff */
-	if (skb_is_nonlinear(skb)) {
-		skb_frag_t *frag;
-		if (fcoe_get_paged_crc_eof(skb, tlen)) {
-			kfree_skb(skb);
-			return -ENOMEM;
-		}
-		frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
-		cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
-			+ frag->page_offset;
-	} else {
-		cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
-	}
-
-	memset(cp, 0, sizeof(*cp));
-	cp->fcoe_eof = eof;
-	cp->fcoe_crc32 = cpu_to_le32(~crc);
-
-	if (skb_is_nonlinear(skb)) {
-		kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ);
-		cp = NULL;
-	}
-
-	/* adjust skb netowrk/transport offsets to match mac/fcoe/fc */
-	skb_push(skb, elen + hlen);
-	skb_reset_mac_header(skb);
-	skb_reset_network_header(skb);
-	skb->mac_len = elen;
-	skb->protocol = htons(ETH_P_FCOE);
-	skb->dev = fc->real_dev;
-
-	/* fill up mac and fcoe headers */
-	eh = eth_hdr(skb);
-	eh->h_proto = htons(ETH_P_FCOE);
-	if (fc->address_mode == FCOE_FCOUI_ADDR_MODE)
-		fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
-	else
-		/* insert GW address */
-		memcpy(eh->h_dest, fc->dest_addr, ETH_ALEN);
-
-	if (unlikely(flogi_in_progress))
-		memcpy(eh->h_source, fc->ctl_src_addr, ETH_ALEN);
-	else
-		memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN);
-
-	hp = (struct fcoe_hdr *)(eh + 1);
-	memset(hp, 0, sizeof(*hp));
-	if (FC_FCOE_VER)
-		FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
-	hp->fcoe_sof = sof;
-
-#ifdef NETIF_F_FSO
-	/* fcoe lso, mss is in max_payload which is non-zero for FCP data */
-	if (lp->seq_offload && fr_max_payload(fp)) {
-		skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
-		skb_shinfo(skb)->gso_size = fr_max_payload(fp);
-	} else {
-		skb_shinfo(skb)->gso_type = 0;
-		skb_shinfo(skb)->gso_size = 0;
-	}
-#endif
-	/* update tx stats: regardless if LLD fails */
-	stats = fc_lport_get_stats(lp);
-	stats->TxFrames++;
-	stats->TxWords += wlen;
-
-	/* send down to lld */
-	fr_dev(fp) = lp;
-	if (fc->fcoe_pending_queue.qlen)
-		rc = fcoe_check_wait_queue(lp);
-
-	if (rc == 0)
-		rc = fcoe_start_io(skb);
-
-	if (rc) {
-		spin_lock_bh(&fc->fcoe_pending_queue.lock);
-		__skb_queue_tail(&fc->fcoe_pending_queue, skb);
-		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-		if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
-			lp->qfull = 1;
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_xmit);
-
-/**
- * fcoe_percpu_receive_thread() - recv thread per cpu
- * @arg: ptr to the fcoe per cpu struct
- *
- * Return: 0 for success
- */
-int fcoe_percpu_receive_thread(void *arg)
-{
-	struct fcoe_percpu_s *p = arg;
-	u32 fr_len;
-	struct fc_lport *lp;
-	struct fcoe_rcv_info *fr;
-	struct fcoe_dev_stats *stats;
-	struct fc_frame_header *fh;
-	struct sk_buff *skb;
-	struct fcoe_crc_eof crc_eof;
-	struct fc_frame *fp;
-	u8 *mac = NULL;
-	struct fcoe_softc *fc;
-	struct fcoe_hdr *hp;
-
-	set_user_nice(current, -20);
-
-	while (!kthread_should_stop()) {
-
-		spin_lock_bh(&p->fcoe_rx_list.lock);
-		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) {
-			set_current_state(TASK_INTERRUPTIBLE);
-			spin_unlock_bh(&p->fcoe_rx_list.lock);
-			schedule();
-			set_current_state(TASK_RUNNING);
-			if (kthread_should_stop())
-				return 0;
-			spin_lock_bh(&p->fcoe_rx_list.lock);
-		}
-		spin_unlock_bh(&p->fcoe_rx_list.lock);
-		fr = fcoe_dev_from_skb(skb);
-		lp = fr->fr_dev;
-		if (unlikely(lp == NULL)) {
-			FC_DBG("invalid HBA Structure");
-			kfree_skb(skb);
-			continue;
-		}
-
-		if (unlikely(debug_fcoe)) {
-			FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p "
-			       "tail:%p end:%p sum:%d dev:%s",
-			       skb->len, skb->data_len,
-			       skb->head, skb->data, skb_tail_pointer(skb),
-			       skb_end_pointer(skb), skb->csum,
-			       skb->dev ? skb->dev->name : "<NULL>");
-		}
-
-		/*
-		 * Save source MAC address before discarding header.
-		 */
-		fc = lport_priv(lp);
-		if (unlikely(fc->flogi_progress))
-			mac = eth_hdr(skb)->h_source;
-
-		if (skb_is_nonlinear(skb))
-			skb_linearize(skb);	/* not ideal */
-
-		/*
-		 * Frame length checks and setting up the header pointers
-		 * was done in fcoe_rcv already.
-		 */
-		hp = (struct fcoe_hdr *) skb_network_header(skb);
-		fh = (struct fc_frame_header *) skb_transport_header(skb);
-
-		stats = fc_lport_get_stats(lp);
-		if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
-			if (stats->ErrorFrames < 5)
-				printk(KERN_WARNING "FCoE version "
-				       "mismatch: The frame has "
-				       "version %x, but the "
-				       "initiator supports version "
-				       "%x\n", FC_FCOE_DECAPS_VER(hp),
-				       FC_FCOE_VER);
-			stats->ErrorFrames++;
-			kfree_skb(skb);
-			continue;
-		}
-
-		skb_pull(skb, sizeof(struct fcoe_hdr));
-		fr_len = skb->len - sizeof(struct fcoe_crc_eof);
-
-		stats->RxFrames++;
-		stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
-
-		fp = (struct fc_frame *)skb;
-		fc_frame_init(fp);
-		fr_dev(fp) = lp;
-		fr_sof(fp) = hp->fcoe_sof;
-
-		/* Copy out the CRC and EOF trailer for access */
-		if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
-			kfree_skb(skb);
-			continue;
-		}
-		fr_eof(fp) = crc_eof.fcoe_eof;
-		fr_crc(fp) = crc_eof.fcoe_crc32;
-		if (pskb_trim(skb, fr_len)) {
-			kfree_skb(skb);
-			continue;
-		}
-
-		/*
-		 * We only check CRC if no offload is available and if it is
-		 * it's solicited data, in which case, the FCP layer would
-		 * check it during the copy.
-		 */
-		if (lp->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
-			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
-		else
-			fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
-
-		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);
-			continue;
-		}
-		if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
-			if (le32_to_cpu(fr_crc(fp)) !=
-			    ~crc32(~0, skb->data, fr_len)) {
-				if (debug_fcoe || stats->InvalidCRCCount < 5)
-					printk(KERN_WARNING "fcoe: dropping "
-					       "frame with CRC error\n");
-				stats->InvalidCRCCount++;
-				stats->ErrorFrames++;
-				fc_frame_free(fp);
-				continue;
-			}
-			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
-		}
-		/* non flogi and non data exchanges are handled here */
-		if (unlikely(fc->flogi_progress))
-			fcoe_recv_flogi(fc, fp, mac);
-		fc_exch_recv(lp, lp->emp, fp);
-	}
-	return 0;
-}
-
-/**
- * fcoe_recv_flogi() - flogi receive function
- * @fc: associated fcoe_softc
- * @fp: the recieved frame
- * @sa: the source address of this flogi
- *
- * This is responsible to parse the flogi response and sets the corresponding
- * mac address for the initiator, eitehr OUI based or GW based.
- *
- * Returns: none
- */
-static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa)
-{
-	struct fc_frame_header *fh;
-	u8 op;
-
-	fh = fc_frame_header_get(fp);
-	if (fh->fh_type != FC_TYPE_ELS)
-		return;
-	op = fc_frame_payload_op(fp);
-	if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
-	    fc->flogi_oxid == ntohs(fh->fh_ox_id)) {
-		/*
-		 * FLOGI accepted.
-		 * If the src mac addr is FC_OUI-based, then we mark the
-		 * address_mode flag to use FC_OUI-based Ethernet DA.
-		 * Otherwise we use the FCoE gateway addr
-		 */
-		if (!compare_ether_addr(sa, (u8[6]) FC_FCOE_FLOGI_MAC)) {
-			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
-		} else {
-			memcpy(fc->dest_addr, sa, ETH_ALEN);
-			fc->address_mode = FCOE_GW_ADDR_MODE;
-		}
-
-		/*
-		 * Remove any previously-set unicast MAC filter.
-		 * Add secondary FCoE MAC address filter for our OUI.
-		 */
-		rtnl_lock();
-		if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
-			dev_unicast_delete(fc->real_dev, fc->data_src_addr,
-					   ETH_ALEN);
-		fc_fcoe_set_mac(fc->data_src_addr, fh->fh_d_id);
-		dev_unicast_add(fc->real_dev, fc->data_src_addr, ETH_ALEN);
-		rtnl_unlock();
-
-		fc->flogi_progress = 0;
-	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
-		/*
-		 * Save source MAC for point-to-point responses.
-		 */
-		memcpy(fc->dest_addr, sa, ETH_ALEN);
-		fc->address_mode = FCOE_GW_ADDR_MODE;
-	}
-}
-
-/**
- * fcoe_watchdog() - fcoe timer callback
- * @vp:
- *
- * This checks the pending queue length for fcoe and set lport qfull
- * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the
- * fcoe_hostlist.
- *
- * Returns: 0 for success
- */
-void fcoe_watchdog(ulong vp)
-{
-	struct fcoe_softc *fc;
-
-	read_lock(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		if (fc->lp)
-			fcoe_check_wait_queue(fc->lp);
-	}
-	read_unlock(&fcoe_hostlist_lock);
-
-	fcoe_timer.expires = jiffies + (1 * HZ);
-	add_timer(&fcoe_timer);
-}
-
-
-/**
- * fcoe_check_wait_queue() - put the skb into fcoe pending xmit queue
- * @lp: the fc_port for this skb
- * @skb: the associated skb to be xmitted
- *
- * This empties the wait_queue, dequeue the head of the wait_queue queue
- * and calls fcoe_start_io() for each packet, if all skb have been
- * transmitted, return qlen or -1 if a error occurs, then restore
- * wait_queue and  try again later.
- *
- * The wait_queue is used when the skb transmit fails. skb will go
- * in the wait_queue which will be emptied by the time function OR
- * by the next skb transmit.
- *
- * Returns: 0 for success
- */
-static int fcoe_check_wait_queue(struct fc_lport *lp)
-{
-	struct fcoe_softc *fc = lport_priv(lp);
-	struct sk_buff *skb;
-	int rc = -1;
-
-	spin_lock_bh(&fc->fcoe_pending_queue.lock);
-	if (fc->fcoe_pending_queue_active)
-		goto out;
-	fc->fcoe_pending_queue_active = 1;
-
-	while (fc->fcoe_pending_queue.qlen) {
-		/* keep qlen > 0 until fcoe_start_io succeeds */
-		fc->fcoe_pending_queue.qlen++;
-		skb = __skb_dequeue(&fc->fcoe_pending_queue);
-
-		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-		rc = fcoe_start_io(skb);
-		spin_lock_bh(&fc->fcoe_pending_queue.lock);
-
-		if (rc) {
-			__skb_queue_head(&fc->fcoe_pending_queue, skb);
-			/* undo temporary increment above */
-			fc->fcoe_pending_queue.qlen--;
-			break;
-		}
-		/* undo temporary increment above */
-		fc->fcoe_pending_queue.qlen--;
-	}
-
-	if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
-		lp->qfull = 0;
-	fc->fcoe_pending_queue_active = 0;
-	rc = fc->fcoe_pending_queue.qlen;
-out:
-	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-	return rc;
-}
-
-/**
- * fcoe_dev_setup() - setup link change notification interface
- */
-static void fcoe_dev_setup()
-{
-	/*
-	 * here setup a interface specific wd time to
-	 * monitor the link state
-	 */
-	register_netdevice_notifier(&fcoe_notifier);
-}
-
-/**
- * fcoe_dev_setup() - cleanup link change notification interface
- */
-static void fcoe_dev_cleanup(void)
-{
-	unregister_netdevice_notifier(&fcoe_notifier);
-}
-
-/**
- * fcoe_device_notification() - netdev event notification callback
- * @notifier: context of the notification
- * @event: type of event
- * @ptr: fixed array for output parsed ifname
- *
- * This function is called by the ethernet driver in case of link change event
- *
- * Returns: 0 for success
- */
-static int fcoe_device_notification(struct notifier_block *notifier,
-				    ulong event, void *ptr)
-{
-	struct fc_lport *lp = NULL;
-	struct net_device *real_dev = ptr;
-	struct fcoe_softc *fc;
-	struct fcoe_dev_stats *stats;
-	u32 new_link_up;
-	u32 mfs;
-	int rc = NOTIFY_OK;
-
-	read_lock(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		if (fc->real_dev == real_dev) {
-			lp = fc->lp;
-			break;
-		}
-	}
-	read_unlock(&fcoe_hostlist_lock);
-	if (lp == NULL) {
-		rc = NOTIFY_DONE;
-		goto out;
-	}
-
-	new_link_up = lp->link_up;
-	switch (event) {
-	case NETDEV_DOWN:
-	case NETDEV_GOING_DOWN:
-		new_link_up = 0;
-		break;
-	case NETDEV_UP:
-	case NETDEV_CHANGE:
-		new_link_up = !fcoe_link_ok(lp);
-		break;
-	case NETDEV_CHANGEMTU:
-		mfs = fc->real_dev->mtu -
-			(sizeof(struct fcoe_hdr) +
-			 sizeof(struct fcoe_crc_eof));
-		if (mfs >= FC_MIN_MAX_FRAME)
-			fc_set_mfs(lp, mfs);
-		new_link_up = !fcoe_link_ok(lp);
-		break;
-	case NETDEV_REGISTER:
-		break;
-	default:
-		FC_DBG("unknown event %ld call", event);
-	}
-	if (lp->link_up != new_link_up) {
-		if (new_link_up)
-			fc_linkup(lp);
-		else {
-			stats = fc_lport_get_stats(lp);
-			stats->LinkFailureCount++;
-			fc_linkdown(lp);
-			fcoe_clean_pending_queue(lp);
-		}
-	}
-out:
-	return rc;
-}
-
-/**
- * fcoe_if_to_netdev() - parse a name buffer to get netdev
- * @ifname: fixed array for output parsed ifname
- * @buffer: incoming buffer to be copied
- *
- * Returns: NULL or ptr to netdeive
- */
-static struct net_device *fcoe_if_to_netdev(const char *buffer)
-{
-	char *cp;
-	char ifname[IFNAMSIZ + 2];
-
-	if (buffer) {
-		strlcpy(ifname, buffer, IFNAMSIZ);
-		cp = ifname + strlen(ifname);
-		while (--cp >= ifname && *cp == '\n')
-			*cp = '\0';
-		return dev_get_by_name(&init_net, ifname);
-	}
-	return NULL;
-}
-
-/**
- * fcoe_netdev_to_module_owner() - finds out the nic drive moddule of the netdev
- * @netdev: the target netdev
- *
- * Returns: ptr to the struct module, NULL for failure
- */
-static struct module *
-fcoe_netdev_to_module_owner(const struct net_device *netdev)
-{
-	struct device *dev;
-
-	if (!netdev)
-		return NULL;
-
-	dev = netdev->dev.parent;
-	if (!dev)
-		return NULL;
-
-	if (!dev->driver)
-		return NULL;
-
-	return dev->driver->owner;
-}
-
-/**
- * fcoe_ethdrv_get() - Hold the Ethernet driver
- * @netdev: the target netdev
- *
- * Holds the Ethernet driver module by try_module_get() for
- * the corresponding netdev.
- *
- * Returns: 0 for succsss
- */
-static int fcoe_ethdrv_get(const struct net_device *netdev)
-{
-	struct module *owner;
-
-	owner = fcoe_netdev_to_module_owner(netdev);
-	if (owner) {
-		printk(KERN_DEBUG "fcoe:hold driver module %s for %s\n",
-		       module_name(owner), netdev->name);
-		return  try_module_get(owner);
-	}
-	return -ENODEV;
-}
-
-/**
- * fcoe_ethdrv_put() - Release the Ethernet driver
- * @netdev: the target netdev
- *
- * Releases the Ethernet driver module by module_put for
- * the corresponding netdev.
- *
- * Returns: 0 for succsss
- */
-static int fcoe_ethdrv_put(const struct net_device *netdev)
-{
-	struct module *owner;
-
-	owner = fcoe_netdev_to_module_owner(netdev);
-	if (owner) {
-		printk(KERN_DEBUG "fcoe:release driver module %s for %s\n",
-		       module_name(owner), netdev->name);
-		module_put(owner);
-		return 0;
-	}
-	return -ENODEV;
-}
-
-/**
- * fcoe_destroy() - handles the destroy from sysfs
- * @buffer: expcted to be a eth if name
- * @kp: associated kernel param
- *
- * Returns: 0 for success
- */
-static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
-{
-	int rc;
-	struct net_device *netdev;
-
-	netdev = fcoe_if_to_netdev(buffer);
-	if (!netdev) {
-		rc = -ENODEV;
-		goto out_nodev;
-	}
-	/* look for existing lport */
-	if (!fcoe_hostlist_lookup(netdev)) {
-		rc = -ENODEV;
-		goto out_putdev;
-	}
-	rc = fcoe_if_destroy(netdev);
-	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_if_destroy(%s) failed\n",
-		       netdev->name);
-		rc = -EIO;
-		goto out_putdev;
-	}
-	fcoe_ethdrv_put(netdev);
-	rc = 0;
-out_putdev:
-	dev_put(netdev);
-out_nodev:
-	return rc;
-}
-
-/**
- * fcoe_create() - Handles the create call from sysfs
- * @buffer: expcted to be a eth if name
- * @kp: associated kernel param
- *
- * Returns: 0 for success
- */
-static int fcoe_create(const char *buffer, struct kernel_param *kp)
-{
-	int rc;
-	struct net_device *netdev;
-
-	netdev = fcoe_if_to_netdev(buffer);
-	if (!netdev) {
-		rc = -ENODEV;
-		goto out_nodev;
-	}
-	/* look for existing lport */
-	if (fcoe_hostlist_lookup(netdev)) {
-		rc = -EEXIST;
-		goto out_putdev;
-	}
-	fcoe_ethdrv_get(netdev);
-
-	rc = fcoe_if_create(netdev);
-	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_if_create(%s) failed\n",
-		       netdev->name);
-		fcoe_ethdrv_put(netdev);
-		rc = -EIO;
-		goto out_putdev;
-	}
-	rc = 0;
-out_putdev:
-	dev_put(netdev);
-out_nodev:
-	return rc;
-}
-
-module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(create, "string");
-MODULE_PARM_DESC(create, "Create fcoe port using net device passed in.");
-module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, "Destroy fcoe port");
-
-/**
- * fcoe_link_ok() - Check if link is ok for the fc_lport
- * @lp: ptr to the fc_lport
- *
- * Any permanently-disqualifying conditions have been previously checked.
- * This also updates the speed setting, which may change with link for 100/1000.
- *
- * This function should probably be checking for PAUSE support at some point
- * in the future. Currently Per-priority-pause is not determinable using
- * ethtool, so we shouldn't be restrictive until that problem is resolved.
- *
- * Returns: 0 if link is OK for use by FCoE.
- *
- */
-int fcoe_link_ok(struct fc_lport *lp)
-{
-	struct fcoe_softc *fc = lport_priv(lp);
-	struct net_device *dev = fc->real_dev;
-	struct ethtool_cmd ecmd = { ETHTOOL_GSET };
-	int rc = 0;
-
-	if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) {
-		dev = fc->phys_dev;
-		if (dev->ethtool_ops->get_settings) {
-			dev->ethtool_ops->get_settings(dev, &ecmd);
-			lp->link_supported_speeds &=
-				~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
-			if (ecmd.supported & (SUPPORTED_1000baseT_Half |
-					      SUPPORTED_1000baseT_Full))
-				lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
-			if (ecmd.supported & SUPPORTED_10000baseT_Full)
-				lp->link_supported_speeds |=
-					FC_PORTSPEED_10GBIT;
-			if (ecmd.speed == SPEED_1000)
-				lp->link_speed = FC_PORTSPEED_1GBIT;
-			if (ecmd.speed == SPEED_10000)
-				lp->link_speed = FC_PORTSPEED_10GBIT;
-		}
-	} else
-		rc = -1;
-
-	return rc;
-}
-EXPORT_SYMBOL_GPL(fcoe_link_ok);
-
-/**
- * fcoe_percpu_clean() - Clear the pending skbs for an lport
- * @lp: the fc_lport
- */
-void fcoe_percpu_clean(struct fc_lport *lp)
-{
-	struct fcoe_percpu_s *pp;
-	struct fcoe_rcv_info *fr;
-	struct sk_buff_head *list;
-	struct sk_buff *skb, *next;
-	struct sk_buff *head;
-	unsigned int cpu;
-
-	for_each_possible_cpu(cpu) {
-		pp = &per_cpu(fcoe_percpu, cpu);
-		spin_lock_bh(&pp->fcoe_rx_list.lock);
-		list = &pp->fcoe_rx_list;
-		head = list->next;
-		for (skb = head; skb != (struct sk_buff *)list;
-		     skb = next) {
-			next = skb->next;
-			fr = fcoe_dev_from_skb(skb);
-			if (fr->fr_dev == lp) {
-				__skb_unlink(skb, list);
-				kfree_skb(skb);
-			}
-		}
-		spin_unlock_bh(&pp->fcoe_rx_list.lock);
-	}
-}
-EXPORT_SYMBOL_GPL(fcoe_percpu_clean);
-
-/**
- * fcoe_clean_pending_queue() - Dequeue a skb and free it
- * @lp: the corresponding fc_lport
- *
- * Returns: none
- */
-void fcoe_clean_pending_queue(struct fc_lport *lp)
-{
-	struct fcoe_softc  *fc = lport_priv(lp);
-	struct sk_buff *skb;
-
-	spin_lock_bh(&fc->fcoe_pending_queue.lock);
-	while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
-		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-		kfree_skb(skb);
-		spin_lock_bh(&fc->fcoe_pending_queue.lock);
-	}
-	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-}
-EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
-
-/**
- * libfc_host_alloc() - Allocate a Scsi_Host with room for the fc_lport
- * @sht: ptr to the scsi host templ
- * @priv_size: size of private data after fc_lport
- *
- * Returns: ptr to Scsi_Host
- * TODO: to libfc?
- */
-static inline struct Scsi_Host *
-libfc_host_alloc(struct scsi_host_template *sht, int priv_size)
-{
-	return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
-}
-
-/**
- * fcoe_host_alloc() - Allocate a Scsi_Host with room for the fcoe_softc
- * @sht: ptr to the scsi host templ
- * @priv_size: size of private data after fc_lport
- *
- * Returns: ptr to Scsi_Host
- */
-struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *sht, int priv_size)
-{
-	return libfc_host_alloc(sht, sizeof(struct fcoe_softc) + priv_size);
-}
-EXPORT_SYMBOL_GPL(fcoe_host_alloc);
-
-/**
- * fcoe_reset() - Resets the fcoe
- * @shost: shost the reset is from
- *
- * Returns: always 0
- */
-int fcoe_reset(struct Scsi_Host *shost)
-{
-	struct fc_lport *lport = shost_priv(shost);
-	fc_lport_reset(lport);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_reset);
-
-/**
- * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN.
- * @mac: mac address
- * @scheme: check port
- * @port: port indicator for converting
- *
- * Returns: u64 fc world wide name
- */
-u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
-		      unsigned int scheme, unsigned int port)
-{
-	u64 wwn;
-	u64 host_mac;
-
-	/* The MAC is in NO, so flip only the low 48 bits */
-	host_mac = ((u64) mac[0] << 40) |
-		((u64) mac[1] << 32) |
-		((u64) mac[2] << 24) |
-		((u64) mac[3] << 16) |
-		((u64) mac[4] << 8) |
-		(u64) mac[5];
-
-	WARN_ON(host_mac >= (1ULL << 48));
-	wwn = host_mac | ((u64) scheme << 60);
-	switch (scheme) {
-	case 1:
-		WARN_ON(port != 0);
-		break;
-	case 2:
-		WARN_ON(port >= 0xfff);
-		wwn |= (u64) port << 48;
-		break;
-	default:
-		WARN_ON(1);
-		break;
-	}
-
-	return wwn;
-}
-EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
-
-/**
- * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device
- * @device: this is currently ptr to net_device
- *
- * Returns: NULL or the located fcoe_softc
- */
-static struct fcoe_softc *
-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);
-			return fc;
-		}
-	}
-	read_unlock(&fcoe_hostlist_lock);
-	return NULL;
-}
-
-/**
- * fcoe_hostlist_lookup() - Find the corresponding lport by netdev
- * @netdev: ptr to net_device
- *
- * Returns: 0 for success
- */
-struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
-{
-	struct fcoe_softc *fc;
-
-	fc = fcoe_hostlist_lookup_softc(netdev);
-
-	return (fc) ? fc->lp : NULL;
-}
-EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup);
-
-/**
- * fcoe_hostlist_add() - Add a lport to lports list
- * @lp: ptr to the fc_lport to badded
- *
- * Returns: 0 for success
- */
-int fcoe_hostlist_add(const struct fc_lport *lp)
-{
-	struct fcoe_softc *fc;
-
-	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;
-}
-EXPORT_SYMBOL_GPL(fcoe_hostlist_add);
-
-/**
- * fcoe_hostlist_remove() - remove a lport from lports list
- * @lp: ptr to the fc_lport to badded
- *
- * Returns: 0 for success
- */
-int fcoe_hostlist_remove(const struct fc_lport *lp)
-{
-	struct fcoe_softc *fc;
-
-	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);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_hostlist_remove);
-
-/**
- * fcoe_libfc_config() - sets up libfc related properties for lport
- * @lp: ptr to the fc_lport
- * @tt: libfc function template
- *
- * Returns : 0 for success
- */
-int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
-{
-	/* Set the function pointers set by the LLDD */
-	memcpy(&lp->tt, tt, sizeof(*tt));
-	if (fc_fcp_init(lp))
-		return -ENOMEM;
-	fc_exch_init(lp);
-	fc_elsct_init(lp);
-	fc_lport_init(lp);
-	fc_rport_init(lp);
-	fc_disc_init(lp);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_libfc_config);
-
-/**
- * fcoe_init() - fcoe module loading initialization
- *
- * Returns 0 on success, negative on failure
- */
-static int __init fcoe_init(void)
-{
-	unsigned int cpu;
-	int rc = 0;
-	struct fcoe_percpu_s *p;
-
-	INIT_LIST_HEAD(&fcoe_hostlist);
-	rwlock_init(&fcoe_hostlist_lock);
-
-	for_each_possible_cpu(cpu) {
-		p = &per_cpu(fcoe_percpu, cpu);
-		skb_queue_head_init(&p->fcoe_rx_list);
-	}
-
-	for_each_online_cpu(cpu)
-		fcoe_percpu_thread_create(cpu);
-
-	/* Initialize per CPU interrupt thread */
-	rc = register_hotcpu_notifier(&fcoe_cpu_notifier);
-	if (rc)
-		goto out_free;
-
-	/* Setup link change notification */
-	fcoe_dev_setup();
-
-	setup_timer(&fcoe_timer, fcoe_watchdog, 0);
-
-	mod_timer(&fcoe_timer, jiffies + (10 * HZ));
-
-	fcoe_if_init();
-
-	return 0;
-
-out_free:
-	for_each_online_cpu(cpu) {
-		fcoe_percpu_thread_destroy(cpu);
-	}
-
-	return rc;
-}
-module_init(fcoe_init);
-
-/**
- * fcoe_exit() - fcoe module unloading cleanup
- *
- * Returns 0 on success, negative on failure
- */
-static void __exit fcoe_exit(void)
-{
-	unsigned int cpu;
-	struct fcoe_softc *fc, *tmp;
-
-	fcoe_dev_cleanup();
-
-	/* Stop the timer */
-	del_timer_sync(&fcoe_timer);
-
-	/* releases the associated fcoe hosts */
-	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
-		fcoe_if_destroy(fc->real_dev);
-
-	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
-
-	for_each_online_cpu(cpu) {
-		fcoe_percpu_thread_destroy(cpu);
-	}
-}
-module_exit(fcoe_exit);


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 09/14] fcoe, libfc, scsi: adds libfcoe module
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
                   ` (7 preceding siblings ...)
  2009-03-17 18:42 ` [PATCH 08/14] fcoe: renames libfcoe.c to fcoe.c as the only fcoe module file Robert Love
@ 2009-03-17 18:42 ` Robert Love
  2009-03-17 18:42 ` [PATCH 10/14] fcoe: moves common FCoE library API functions to " Robert Love
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:42 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

Just sets up build environment for libfcoe module towards a
libfcoe library for libfc LLDs using FCoE as libfc transport.

Common library code to libfcoe is added in next patch.

Also, updated MODULE_LICENSE from "GPL" string to "GPL v2" for
libfc, libfcoe and fcoe modules to accurately match the licenses.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/Kconfig        |    8 +++++++-
 drivers/scsi/Makefile       |    1 +
 drivers/scsi/fcoe/Makefile  |    1 +
 drivers/scsi/fcoe/fcoe.c    |    2 +-
 drivers/scsi/fcoe/libfcoe.c |   24 ++++++++++++++++++++++++
 drivers/scsi/libfc/fc_fcp.c |    2 +-
 6 files changed, 35 insertions(+), 3 deletions(-)
 create mode 100644 drivers/scsi/fcoe/libfcoe.c

diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index e2f44e6..bbec3a8 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -614,10 +614,16 @@ config LIBFC
 	---help---
 	  Fibre Channel library module
 
+config LIBFCOE
+	tristate "LibFCoE module"
+	select LIBFC
+	---help---
+	  Library for Fibre Channel over Ethernet module
+
 config FCOE
 	tristate "FCoE module"
 	depends on PCI
-	select LIBFC
+	select LIBFCOE
 	---help---
 	  Fibre Channel over Ethernet module
 
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index cf79296..e7c861a 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_SCSI_SRP_ATTRS)	+= scsi_transport_srp.o
 obj-$(CONFIG_SCSI_DH)		+= device_handler/
 
 obj-$(CONFIG_LIBFC)		+= libfc/
+obj-$(CONFIG_LIBFCOE)		+= fcoe/
 obj-$(CONFIG_FCOE)		+= fcoe/
 obj-$(CONFIG_ISCSI_TCP) 	+= libiscsi.o	libiscsi_tcp.o iscsi_tcp.o
 obj-$(CONFIG_INFINIBAND_ISER) 	+= libiscsi.o
diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index 9b590ef..950f276 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_FCOE) += fcoe.o
+obj-$(CONFIG_LIBFCOE) += libfcoe.o
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index cbebbf7..af40410 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -70,7 +70,7 @@ static int debug_fcoe;
 
 MODULE_AUTHOR("Open-FCoE.org");
 MODULE_DESCRIPTION("FCoE");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 
 /* fcoe host list */
 LIST_HEAD(fcoe_hostlist);
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
new file mode 100644
index 0000000..17acbcc
--- /dev/null
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("FCoE");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 0997e8b..f555ae9 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -41,7 +41,7 @@
 
 MODULE_AUTHOR("Open-FCoE.org");
 MODULE_DESCRIPTION("libfc");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 
 static int fc_fcp_debug;
 


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 10/14] fcoe: moves common FCoE library API functions to libfcoe module
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
                   ` (8 preceding siblings ...)
  2009-03-17 18:42 ` [PATCH 09/14] fcoe, libfc, scsi: adds libfcoe module Robert Love
@ 2009-03-17 18:42 ` Robert Love
  2009-03-17 18:42 ` [PATCH 11/14] fcoe: cleans up libfcoe.h and adds fcoe.h for fcoe module Robert Love
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:42 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

Moves these functions as-is from fcoe.c to libfcoe.c, since
they're are common routines:

	- fcoe_wwn_from_mac
	- fcoe_libfc_config

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/fcoe.c    |   64 -----------------------------------------
 drivers/scsi/fcoe/libfcoe.c |   67 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+), 64 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index af40410..e9c3d26 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1755,47 +1755,6 @@ int fcoe_reset(struct Scsi_Host *shost)
 EXPORT_SYMBOL_GPL(fcoe_reset);
 
 /**
- * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN.
- * @mac: mac address
- * @scheme: check port
- * @port: port indicator for converting
- *
- * Returns: u64 fc world wide name
- */
-u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
-		      unsigned int scheme, unsigned int port)
-{
-	u64 wwn;
-	u64 host_mac;
-
-	/* The MAC is in NO, so flip only the low 48 bits */
-	host_mac = ((u64) mac[0] << 40) |
-		((u64) mac[1] << 32) |
-		((u64) mac[2] << 24) |
-		((u64) mac[3] << 16) |
-		((u64) mac[4] << 8) |
-		(u64) mac[5];
-
-	WARN_ON(host_mac >= (1ULL << 48));
-	wwn = host_mac | ((u64) scheme << 60);
-	switch (scheme) {
-	case 1:
-		WARN_ON(port != 0);
-		break;
-	case 2:
-		WARN_ON(port >= 0xfff);
-		wwn |= (u64) port << 48;
-		break;
-	default:
-		WARN_ON(1);
-		break;
-	}
-
-	return wwn;
-}
-EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
-
-/**
  * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device
  * @device: this is currently ptr to net_device
  *
@@ -1875,29 +1834,6 @@ int fcoe_hostlist_remove(const struct fc_lport *lp)
 EXPORT_SYMBOL_GPL(fcoe_hostlist_remove);
 
 /**
- * fcoe_libfc_config() - sets up libfc related properties for lport
- * @lp: ptr to the fc_lport
- * @tt: libfc function template
- *
- * Returns : 0 for success
- */
-int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
-{
-	/* Set the function pointers set by the LLDD */
-	memcpy(&lp->tt, tt, sizeof(*tt));
-	if (fc_fcp_init(lp))
-		return -ENOMEM;
-	fc_exch_init(lp);
-	fc_elsct_init(lp);
-	fc_lport_init(lp);
-	fc_rport_init(lp);
-	fc_disc_init(lp);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_libfc_config);
-
-/**
  * fcoe_init() - fcoe module loading initialization
  *
  * Returns 0 on success, negative on failure
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 17acbcc..dff97fc 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -18,7 +18,74 @@
  */
 
 #include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include <scsi/libfc.h>
 
 MODULE_AUTHOR("Open-FCoE.org");
 MODULE_DESCRIPTION("FCoE");
 MODULE_LICENSE("GPL v2");
+
+/**
+ * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN.
+ * @mac: mac address
+ * @scheme: check port
+ * @port: port indicator for converting
+ *
+ * Returns: u64 fc world wide name
+ */
+u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
+		      unsigned int scheme, unsigned int port)
+{
+	u64 wwn;
+	u64 host_mac;
+
+	/* The MAC is in NO, so flip only the low 48 bits */
+	host_mac = ((u64) mac[0] << 40) |
+		((u64) mac[1] << 32) |
+		((u64) mac[2] << 24) |
+		((u64) mac[3] << 16) |
+		((u64) mac[4] << 8) |
+		(u64) mac[5];
+
+	WARN_ON(host_mac >= (1ULL << 48));
+	wwn = host_mac | ((u64) scheme << 60);
+	switch (scheme) {
+	case 1:
+		WARN_ON(port != 0);
+		break;
+	case 2:
+		WARN_ON(port >= 0xfff);
+		wwn |= (u64) port << 48;
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+
+	return wwn;
+}
+EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
+
+/**
+ * fcoe_libfc_config() - sets up libfc related properties for lport
+ * @lp: ptr to the fc_lport
+ * @tt: libfc function template
+ *
+ * Returns : 0 for success
+ */
+int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
+{
+	/* Set the function pointers set by the LLDD */
+	memcpy(&lp->tt, tt, sizeof(*tt));
+	if (fc_fcp_init(lp))
+		return -ENOMEM;
+	fc_exch_init(lp);
+	fc_elsct_init(lp);
+	fc_lport_init(lp);
+	fc_rport_init(lp);
+	fc_disc_init(lp);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_libfc_config);


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 11/14] fcoe: cleans up libfcoe.h and adds fcoe.h for fcoe module
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
                   ` (9 preceding siblings ...)
  2009-03-17 18:42 ` [PATCH 10/14] fcoe: moves common FCoE library API functions to " Robert Love
@ 2009-03-17 18:42 ` Robert Love
  2009-03-17 18:42 ` [PATCH 12/14] fcoe, libfc: fix double fcoe_softc memory alloc Robert Love
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:42 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

Removes no where used several inline functions prefixed with skb_*
and be16_to_cpu.

Moves fcoe module specific func prototypes to fcoe.c from libfcoe.h,
moved only need for build.

Adds fcoe module header file fcoe.h and then moves fcoe module
specific fcoe_percpu_s and fcoe_softc to fcoe.h from libfcoe.h.

Moves all defines from fcoe.c to fcoe.h since now fcoe module
has its own header file fcoe.h.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/fcoe.c |   42 +++++++---------
 drivers/scsi/fcoe/fcoe.h |   86 +++++++++++++++++++++++++++++++++
 include/scsi/libfcoe.h   |  120 +---------------------------------------------
 3 files changed, 105 insertions(+), 143 deletions(-)
 create mode 100644 drivers/scsi/fcoe/fcoe.h

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index e9c3d26..c76ff5d 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -19,15 +19,12 @@
 
 #include <linux/module.h>
 #include <linux/version.h>
-#include <linux/kernel.h>
 #include <linux/spinlock.h>
-#include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
-#include <linux/kthread.h>
 #include <linux/crc32.h>
 #include <linux/cpu.h>
 #include <linux/fs.h>
@@ -45,28 +42,9 @@
 #include <scsi/fc_frame.h>
 #include <scsi/libfcoe.h>
 
-static int debug_fcoe;
-
-#define FCOE_MAX_QUEUE_DEPTH  256
-#define FCOE_LOW_QUEUE_DEPTH  32
-
-/* destination address mode */
-#define FCOE_GW_ADDR_MODE	    0x00
-#define FCOE_FCOUI_ADDR_MODE	    0x01
-
-#define FCOE_WORD_TO_BYTE  4
-
-#define FCOE_VERSION	"0.1"
-#define	FCOE_NAME	"fcoe"
-#define	FCOE_VENDOR	"Open-FCoE.org"
-
-#define FCOE_MAX_LUN		255
-#define FCOE_MAX_FCP_TARGET	256
+#include "fcoe.h"
 
-#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
-
-#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
-#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
+static int debug_fcoe;
 
 MODULE_AUTHOR("Open-FCoE.org");
 MODULE_DESCRIPTION("FCoE");
@@ -78,8 +56,22 @@ DEFINE_RWLOCK(fcoe_hostlist_lock);
 DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
 DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
 
-
 /* Function Prototyes */
+static int fcoe_reset(struct Scsi_Host *shost);
+static int fcoe_xmit(struct fc_lport *, struct fc_frame *);
+static int fcoe_rcv(struct sk_buff *, struct net_device *,
+		    struct packet_type *, struct net_device *);
+static int fcoe_percpu_receive_thread(void *arg);
+static void fcoe_clean_pending_queue(struct fc_lport *lp);
+static void fcoe_percpu_clean(struct fc_lport *lp);
+static int fcoe_link_ok(struct fc_lport *lp);
+
+static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
+static int fcoe_hostlist_add(const struct fc_lport *);
+static int fcoe_hostlist_remove(const struct fc_lport *);
+
+static struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *, int);
+
 static int fcoe_check_wait_queue(struct fc_lport *);
 static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
 static int fcoe_device_notification(struct notifier_block *, ulong, void *);
diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h
new file mode 100644
index 0000000..4a9611f
--- /dev/null
+++ b/drivers/scsi/fcoe/fcoe.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FCOE_H_
+#define _FCOE_H_
+
+#include <linux/skbuff.h>
+#include <linux/kthread.h>
+
+#define FCOE_MAX_QUEUE_DEPTH	256
+#define FCOE_LOW_QUEUE_DEPTH	32
+
+/* destination address mode */
+#define FCOE_GW_ADDR_MODE	0x00
+#define FCOE_FCOUI_ADDR_MODE	0x01
+
+#define FCOE_WORD_TO_BYTE	4
+
+#define FCOE_VERSION	"0.1"
+#define FCOE_NAME	"fcoe"
+#define FCOE_VENDOR	"Open-FCoE.org"
+
+#define FCOE_MAX_LUN		255
+#define FCOE_MAX_FCP_TARGET	256
+
+#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
+
+#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
+#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
+
+/*
+ * this percpu struct for fcoe
+ */
+struct fcoe_percpu_s {
+	struct task_struct *thread;
+	struct sk_buff_head fcoe_rx_list;
+	struct page *crc_eof_page;
+	int crc_eof_offset;
+};
+
+/*
+ * the fcoe sw transport private data
+ */
+struct fcoe_softc {
+	struct list_head list;
+	struct fc_lport *lp;
+	struct net_device *real_dev;
+	struct net_device *phys_dev;		/* device with ethtool_ops */
+	struct packet_type  fcoe_packet_type;
+	struct sk_buff_head fcoe_pending_queue;
+	u8	fcoe_pending_queue_active;
+
+	u8 dest_addr[ETH_ALEN];
+	u8 ctl_src_addr[ETH_ALEN];
+	u8 data_src_addr[ETH_ALEN];
+	/*
+	 * fcoe protocol address learning related stuff
+	 */
+	u16 flogi_oxid;
+	u8 flogi_progress;
+	u8 address_mode;
+};
+
+static inline struct net_device *fcoe_netdev(
+	const struct fc_lport *lp)
+{
+	return ((struct fcoe_softc *)lport_priv(lp))->real_dev;
+}
+
+#endif /* _FCOE_H_ */
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index e99633c..d07ebe6 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -25,124 +25,8 @@
 #include <scsi/fc/fc_fcoe.h>
 #include <scsi/libfc.h>
 
-/*
- * this percpu struct for fcoe
- */
-struct fcoe_percpu_s {
-	struct task_struct *thread;
-	struct sk_buff_head fcoe_rx_list;
-	struct page *crc_eof_page;
-	int crc_eof_offset;
-};
-
-/*
- * the fcoe sw transport private data
- */
-struct fcoe_softc {
-	struct list_head list;
-	struct fc_lport *lp;
-	struct net_device *real_dev;
-	struct net_device *phys_dev;		/* device with ethtool_ops */
-	struct packet_type  fcoe_packet_type;
-	struct sk_buff_head fcoe_pending_queue;
-	u8	fcoe_pending_queue_active;
-
-	u8 dest_addr[ETH_ALEN];
-	u8 ctl_src_addr[ETH_ALEN];
-	u8 data_src_addr[ETH_ALEN];
-	/*
-	 * fcoe protocol address learning related stuff
-	 */
-	u16 flogi_oxid;
-	u8 flogi_progress;
-	u8 address_mode;
-};
-
-static inline struct net_device *fcoe_netdev(
-	const struct fc_lport *lp)
-{
-	return ((struct fcoe_softc *)lport_priv(lp))->real_dev;
-}
-
-static inline struct fcoe_hdr *skb_fcoe_header(const struct sk_buff *skb)
-{
-	return (struct fcoe_hdr *)skb_network_header(skb);
-}
-
-static inline int skb_fcoe_offset(const struct sk_buff *skb)
-{
-	return skb_network_offset(skb);
-}
-
-static inline struct fc_frame_header *skb_fc_header(const struct sk_buff *skb)
-{
-	return (struct fc_frame_header *)skb_transport_header(skb);
-}
-
-static inline int skb_fc_offset(const struct sk_buff *skb)
-{
-	return skb_transport_offset(skb);
-}
-
-static inline void skb_reset_fc_header(struct sk_buff *skb)
-{
-	skb_reset_network_header(skb);
-	skb_set_transport_header(skb, skb_network_offset(skb) +
-				 sizeof(struct fcoe_hdr));
-}
-
-static inline bool skb_fc_is_data(const struct sk_buff *skb)
-{
-	return skb_fc_header(skb)->fh_r_ctl == FC_RCTL_DD_SOL_DATA;
-}
-
-static inline bool skb_fc_is_cmd(const struct sk_buff *skb)
-{
-	return skb_fc_header(skb)->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD;
-}
-
-static inline bool skb_fc_has_exthdr(const struct sk_buff *skb)
-{
-	return (skb_fc_header(skb)->fh_r_ctl == FC_RCTL_VFTH) ||
-	    (skb_fc_header(skb)->fh_r_ctl == FC_RCTL_IFRH) ||
-	    (skb_fc_header(skb)->fh_r_ctl == FC_RCTL_ENCH);
-}
-
-static inline bool skb_fc_is_roff(const struct sk_buff *skb)
-{
-	return skb_fc_header(skb)->fh_f_ctl[2] & FC_FC_REL_OFF;
-}
-
-static inline u16 skb_fc_oxid(const struct sk_buff *skb)
-{
-	return be16_to_cpu(skb_fc_header(skb)->fh_ox_id);
-}
-
-static inline u16 skb_fc_rxid(const struct sk_buff *skb)
-{
-	return be16_to_cpu(skb_fc_header(skb)->fh_rx_id);
-}
-
 /* libfcoe funcs */
-int fcoe_reset(struct Scsi_Host *shost);
-u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
-		      unsigned int scheme, unsigned int port);
-
-u32 fcoe_fc_crc(struct fc_frame *fp);
-int fcoe_xmit(struct fc_lport *, struct fc_frame *);
-int fcoe_rcv(struct sk_buff *, struct net_device *,
-	     struct packet_type *, struct net_device *);
-
-int fcoe_percpu_receive_thread(void *arg);
-void fcoe_clean_pending_queue(struct fc_lport *lp);
-void fcoe_percpu_clean(struct fc_lport *lp);
-void fcoe_watchdog(ulong vp);
-int fcoe_link_ok(struct fc_lport *lp);
-
-struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
-int fcoe_hostlist_add(const struct fc_lport *);
-int fcoe_hostlist_remove(const struct fc_lport *);
-
-struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *, int);
+u64 fcoe_wwn_from_mac(unsigned char mac[], unsigned int, unsigned int);
 int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
+
 #endif /* _LIBFCOE_H */


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 12/14] fcoe, libfc: fix double fcoe_softc memory alloc
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
                   ` (10 preceding siblings ...)
  2009-03-17 18:42 ` [PATCH 11/14] fcoe: cleans up libfcoe.h and adds fcoe.h for fcoe module Robert Love
@ 2009-03-17 18:42 ` Robert Love
  2009-03-17 18:42 ` [PATCH 13/14] fcoe: Add a header file defining the FIP protocol for FCoE Robert Love
  2009-03-17 18:42 ` [PATCH 14/14] fcoe, libfcoe: Add support for FIP. FCoE discovery and keep-alive Robert Love
  13 siblings, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:42 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

The foce_softc mem was reserved by libfc_host_alloc as well as
by fcoe_host_alloc.

Removes one liner fcoe_host_alloc completely, instead directly calls
libfc_host_alloc to alloc scsi_host with libfc for just one fcoe_softc
as fcoe private data.

Moves libfc_host_alloc to libfc.h since it is a libfc API, placed
lport_priv API adjacent to libfc_host_alloc since this is related
to scsi_host priv data.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/fcoe.c |   33 ++-------------------------------
 include/scsi/libfc.h     |   22 +++++++++++++++++-----
 2 files changed, 19 insertions(+), 36 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index c76ff5d..ef60144 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -70,8 +70,6 @@ static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
 static int fcoe_hostlist_add(const struct fc_lport *);
 static int fcoe_hostlist_remove(const struct fc_lport *);
 
-static struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *, int);
-
 static int fcoe_check_wait_queue(struct fc_lport *);
 static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
 static int fcoe_device_notification(struct notifier_block *, ulong, void *);
@@ -464,8 +462,8 @@ static int fcoe_if_create(struct net_device *netdev)
 	if (lp)
 		return -EEXIST;
 
-	shost = fcoe_host_alloc(&fcoe_shost_template,
-				sizeof(struct fcoe_softc));
+	shost = libfc_host_alloc(&fcoe_shost_template,
+				 sizeof(struct fcoe_softc));
 	if (!shost) {
 		FC_DBG("Could not allocate host structure\n");
 		return -ENOMEM;
@@ -1706,33 +1704,6 @@ void fcoe_clean_pending_queue(struct fc_lport *lp)
 EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
 
 /**
- * libfc_host_alloc() - Allocate a Scsi_Host with room for the fc_lport
- * @sht: ptr to the scsi host templ
- * @priv_size: size of private data after fc_lport
- *
- * Returns: ptr to Scsi_Host
- * TODO: to libfc?
- */
-static inline struct Scsi_Host *
-libfc_host_alloc(struct scsi_host_template *sht, int priv_size)
-{
-	return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
-}
-
-/**
- * fcoe_host_alloc() - Allocate a Scsi_Host with room for the fcoe_softc
- * @sht: ptr to the scsi host templ
- * @priv_size: size of private data after fc_lport
- *
- * Returns: ptr to Scsi_Host
- */
-struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *sht, int priv_size)
-{
-	return libfc_host_alloc(sht, sizeof(struct fcoe_softc) + priv_size);
-}
-EXPORT_SYMBOL_GPL(fcoe_host_alloc);
-
-/**
  * fcoe_reset() - Resets the fcoe
  * @shost: shost the reset is from
  *
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 4e1d394..0303a6a 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -696,11 +696,6 @@ struct fc_lport {
 /*
  * FC_LPORT HELPER FUNCTIONS
  *****************************/
-static inline void *lport_priv(const struct fc_lport *lp)
-{
-	return (void *)(lp + 1);
-}
-
 static inline int fc_lport_test_ready(struct fc_lport *lp)
 {
 	return lp->state == LPORT_ST_READY;
@@ -743,6 +738,23 @@ static inline struct fcoe_dev_stats *fc_lport_get_stats(struct fc_lport *lp)
 	return per_cpu_ptr(lp->dev_stats, smp_processor_id());
 }
 
+static inline void *lport_priv(const struct fc_lport *lp)
+{
+	return (void *)(lp + 1);
+}
+
+/**
+ * libfc_host_alloc() - Allocate a Scsi_Host with room for the fc_lport
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ */
+static inline struct Scsi_Host *
+libfc_host_alloc(struct scsi_host_template *sht, int priv_size)
+{
+	return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
+}
 
 /*
  * LOCAL PORT LAYER


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 13/14] fcoe: Add a header file defining the FIP protocol for FCoE.
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
                   ` (11 preceding siblings ...)
  2009-03-17 18:42 ` [PATCH 12/14] fcoe, libfc: fix double fcoe_softc memory alloc Robert Love
@ 2009-03-17 18:42 ` Robert Love
  2009-03-17 18:42 ` [PATCH 14/14] fcoe, libfcoe: Add support for FIP. FCoE discovery and keep-alive Robert Love
  13 siblings, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:42 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Joe Eykholt <jeykholt@cisco.com>

Adds include/scsi/fc/fc_fip.h for FIP protocol definitions.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 include/scsi/fc/fc_fip.h |  237 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 237 insertions(+), 0 deletions(-)
 create mode 100644 include/scsi/fc/fc_fip.h

diff --git a/include/scsi/fc/fc_fip.h b/include/scsi/fc/fc_fip.h
new file mode 100644
index 0000000..0627a9a
--- /dev/null
+++ b/include/scsi/fc/fc_fip.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2008 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _FC_FIP_H_
+#define _FC_FIP_H_
+
+/*
+ * This version is based on:
+ * http://www.t11.org/ftp/t11/pub/fc/bb-5/08-543v1.pdf
+ */
+
+/*
+ * The FIP ethertype eventually goes in net/if_ether.h.
+ */
+#ifndef ETH_P_FIP
+#define ETH_P_FIP	0x8914	/* FIP Ethertype */
+#endif
+
+#define FIP_DEF_PRI	128	/* default selection priority */
+#define FIP_DEF_FC_MAP	0x0efc00 /* default FCoE MAP (MAC OUI) value */
+#define FIP_DEF_FKA	8000	/* default FCF keep-alive/advert period (mS) */
+#define FIP_VN_KA_PERIOD 90000	/* required VN_port keep-alive period (mS) */
+#define FIP_FCF_FUZZ	100	/* random time added by FCF (mS) */
+
+/*
+ * Multicast MAC addresses.  T11-adopted.
+ */
+#define FIP_ALL_FCOE_MACS	((u8[6]) { 1, 0x10, 0x18, 1, 0, 0 })
+#define FIP_ALL_ENODE_MACS	((u8[6]) { 1, 0x10, 0x18, 1, 0, 1 })
+#define FIP_ALL_FCF_MACS	((u8[6]) { 1, 0x10, 0x18, 1, 0, 2 })
+
+#define FIP_VER		1		/* version for fip_header */
+
+struct fip_header {
+	__u8	fip_ver;		/* upper 4 bits are the version */
+	__u8	fip_resv1;		/* reserved */
+	__be16	fip_op;			/* operation code */
+	__u8	fip_resv2;		/* reserved */
+	__u8	fip_subcode;		/* lower 4 bits are sub-code */
+	__be16	fip_dl_len;		/* length of descriptors in words */
+	__be16	fip_flags;		/* header flags */
+} __attribute__((packed));
+
+#define FIP_VER_SHIFT	4
+#define FIP_VER_ENCAPS(v) ((v) << FIP_VER_SHIFT)
+#define FIP_VER_DECAPS(v) ((v) >> FIP_VER_SHIFT)
+#define FIP_BPW		4		/* bytes per word for lengths */
+
+/*
+ * fip_op.
+ */
+enum fip_opcode {
+	FIP_OP_DISC =	1,		/* discovery, advertisement, etc. */
+	FIP_OP_LS =	2,		/* Link Service request or reply */
+	FIP_OP_CTRL =	3,		/* Keep Alive / Link Reset */
+	FIP_OP_VLAN =	4,		/* VLAN discovery */
+	FIP_OP_VENDOR_MIN = 0xfff8,	/* min vendor-specific opcode */
+	FIP_OP_VENDOR_MAX = 0xfffe,	/* max vendor-specific opcode */
+};
+
+/*
+ * Subcodes for FIP_OP_DISC.
+ */
+enum fip_disc_subcode {
+	FIP_SC_SOL =	1,		/* solicitation */
+	FIP_SC_ADV =	2,		/* advertisement */
+};
+
+/*
+ * Subcodes for FIP_OP_LS.
+ */
+enum fip_trans_subcode {
+	FIP_SC_REQ =	1,		/* request */
+	FIP_SC_REP =	2,		/* reply */
+};
+
+/*
+ * Subcodes for FIP_OP_RESET.
+ */
+enum fip_reset_subcode {
+	FIP_SC_KEEP_ALIVE = 1,		/* keep-alive from VN_Port */
+	FIP_SC_CLR_VLINK = 2,		/* clear virtual link from VF_Port */
+};
+
+/*
+ * Subcodes for FIP_OP_VLAN.
+ */
+enum fip_vlan_subcode {
+	FIP_SC_VL_REQ =	1,		/* request */
+	FIP_SC_VL_REP =	2,		/* reply */
+};
+
+/*
+ * flags in header fip_flags.
+ */
+enum fip_flag {
+	FIP_FL_FPMA =	0x8000,		/* supports FPMA fabric-provided MACs */
+	FIP_FL_SPMA =	0x4000,		/* supports SPMA server-provided MACs */
+	FIP_FL_AVAIL =	0x0004,		/* available for FLOGI/ELP */
+	FIP_FL_SOL =	0x0002,		/* this is a solicited message */
+	FIP_FL_FPORT =	0x0001,		/* sent from an F port */
+};
+
+/*
+ * Common descriptor header format.
+ */
+struct fip_desc {
+	__u8	fip_dtype;		/* type - see below */
+	__u8	fip_dlen;		/* length - in 32-bit words */
+};
+
+enum fip_desc_type {
+	FIP_DT_PRI =	1,		/* priority for forwarder selection */
+	FIP_DT_MAC =	2,		/* MAC address */
+	FIP_DT_MAP_OUI = 3,		/* FC-MAP OUI */
+	FIP_DT_NAME =	4,		/* switch name or node name */
+	FIP_DT_FAB =	5,		/* fabric descriptor */
+	FIP_DT_FCOE_SIZE = 6,		/* max FCoE frame size */
+	FIP_DT_FLOGI =	7,		/* FLOGI request or response */
+	FIP_DT_FDISC =	8,		/* FDISC request or response */
+	FIP_DT_LOGO =	9,		/* LOGO request or response */
+	FIP_DT_ELP =	10,		/* ELP request or response */
+	FIP_DT_VN_ID =	11,		/* VN_Node Identifier */
+	FIP_DT_FKA =	12,		/* advertisement keep-alive period */
+	FIP_DT_VENDOR =	13,		/* vendor ID */
+	FIP_DT_VLAN =	14,		/* vlan number */
+	FIP_DT_LIMIT,			/* max defined desc_type + 1 */
+	FIP_DT_VENDOR_BASE = 128,	/* first vendor-specific desc_type */
+};
+
+/*
+ * FIP_DT_PRI - priority descriptor.
+ */
+struct fip_pri_desc {
+	struct fip_desc fd_desc;
+	__u8		fd_resvd;
+	__u8		fd_pri;		/* FCF priority:  higher is better */
+} __attribute__((packed));
+
+/*
+ * FIP_DT_MAC - MAC address descriptor.
+ */
+struct fip_mac_desc {
+	struct fip_desc fd_desc;
+	__u8		fd_mac[ETH_ALEN];
+} __attribute__((packed));
+
+/*
+ * FIP_DT_MAP - descriptor.
+ */
+struct fip_map_desc {
+	struct fip_desc fd_desc;
+	__u8		fd_resvd[3];
+	__u8		fd_map[3];
+} __attribute__((packed));
+
+/*
+ * FIP_DT_NAME descriptor.
+ */
+struct fip_wwn_desc {
+	struct fip_desc fd_desc;
+	__u8		fd_resvd[2];
+	__be64		fd_wwn;		/* 64-bit WWN, unaligned */
+} __attribute__((packed));
+
+/*
+ * FIP_DT_FAB descriptor.
+ */
+struct fip_fab_desc {
+	struct fip_desc fd_desc;
+	__be16		fd_vfid;	/* virtual fabric ID */
+	__u8		fd_resvd;
+	__u8		fd_map[3];	/* FC-MAP value */
+	__be64		fd_wwn;		/* fabric name, unaligned */
+} __attribute__((packed));
+
+/*
+ * FIP_DT_FCOE_SIZE descriptor.
+ */
+struct fip_size_desc {
+	struct fip_desc fd_desc;
+	__be16		fd_size;
+} __attribute__((packed));
+
+/*
+ * Descriptor that encapsulates an ELS or ILS frame.
+ * The encapsulated frame immediately follows this header, without
+ * SOF, EOF, or CRC.
+ */
+struct fip_encaps {
+	struct fip_desc fd_desc;
+	__u8		fd_resvd[2];
+} __attribute__((packed));
+
+/*
+ * FIP_DT_VN_ID - VN_Node Identifier descriptor.
+ */
+struct fip_vn_desc {
+	struct fip_desc fd_desc;
+	__u8		fd_mac[ETH_ALEN];
+	__u8		fd_resvd;
+	__u8		fd_fc_id[3];
+	__be64		fd_wwpn;	/* port name, unaligned */
+} __attribute__((packed));
+
+/*
+ * FIP_DT_FKA - Advertisement keep-alive period.
+ */
+struct fip_fka_desc {
+	struct fip_desc fd_desc;
+	__u8		fd_resvd[2];
+	__be32		fd_fka_period;	/* adv./keep-alive period in mS */
+} __attribute__((packed));
+
+/*
+ * FIP_DT_VENDOR descriptor.
+ */
+struct fip_vendor_desc {
+	struct fip_desc fd_desc;
+	__u8		fd_resvd[2];
+	__u8		fd_vendor_id[8];
+} __attribute__((packed));
+
+#endif /* _FC_FIP_H_ */


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 14/14] fcoe, libfcoe: Add support for FIP. FCoE discovery and keep-alive.
  2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
                   ` (12 preceding siblings ...)
  2009-03-17 18:42 ` [PATCH 13/14] fcoe: Add a header file defining the FIP protocol for FCoE Robert Love
@ 2009-03-17 18:42 ` Robert Love
  13 siblings, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-17 18:42 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Joe Eykholt <jeykholt@cisco.com>

FIP is the new standard way to discover Fibre-Channel Forwarders (FCFs)
by sending solicitations and listening for advertisements from FCFs.

It also provides for keep-alives and period advertisements so that both
parties know they have connectivity.  If the FCF loses connectivity to
the storage fabric, it can send a Link Reset to inform the E_node.

This version is also compatible with pre-FIP implementations, so no
configured selection between FIP mode and non-FIP mode is required.

We wait a couple seconds after sending the initial solicitation
and then send an old-style FLOGI.  If we receive any FIP frames,
we use FIP only mode.  If the old FLOGI receives a response,
we disable FIP mode.  After every reset or link up, this
determination is repeated.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/fcoe.c    |  232 ++++----
 drivers/scsi/fcoe/fcoe.h    |   19 -
 drivers/scsi/fcoe/libfcoe.c | 1251 +++++++++++++++++++++++++++++++++++++++++++
 include/scsi/libfcoe.h      |  134 +++++
 4 files changed, 1499 insertions(+), 137 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index ef60144..919c79c 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -37,6 +37,7 @@
 #include <net/rtnetlink.h>
 
 #include <scsi/fc/fc_encaps.h>
+#include <scsi/fc/fc_fip.h>
 
 #include <scsi/libfc.h>
 #include <scsi/fc_frame.h>
@@ -71,7 +72,6 @@ static int fcoe_hostlist_add(const struct fc_lport *);
 static int fcoe_hostlist_remove(const struct fc_lport *);
 
 static int fcoe_check_wait_queue(struct fc_lport *);
-static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
 static int fcoe_device_notification(struct notifier_block *, ulong, void *);
 static void fcoe_dev_setup(void);
 static void fcoe_dev_cleanup(void);
@@ -185,7 +185,7 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
 
 	/* Setup lport private data to point to fcoe softc */
 	fc = lport_priv(lp);
-	fc->lp = lp;
+	fc->ctlr.lp = lp;
 	fc->real_dev = netdev;
 	fc->phys_dev = netdev;
 
@@ -210,9 +210,6 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
 	if (fc_set_mfs(lp, mfs))
 		return -EINVAL;
 
-	if (!fcoe_link_ok(lp))
-		lp->link_up = 1;
-
 	/* offload features support */
 	if (fc->real_dev->features & NETIF_F_SG)
 		lp->sg_supp = 1;
@@ -242,7 +239,7 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
 	fc->fcoe_pending_queue_active = 0;
 
 	/* setup Source Mac Address */
-	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
+	memcpy(fc->ctlr.ctl_src_addr, fc->real_dev->dev_addr,
 	       fc->real_dev->addr_len);
 
 	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
@@ -358,6 +355,8 @@ static int fcoe_if_destroy(struct net_device *netdev)
 
 	/* Don't listen for Ethernet packets anymore */
 	dev_remove_pack(&fc->fcoe_packet_type);
+	dev_remove_pack(&fc->fip_packet_type);
+	fcoe_ctlr_destroy(&fc->ctlr);
 
 	/* Cleanup the fc_lport */
 	fc_lport_destroy(lp);
@@ -375,8 +374,10 @@ static int fcoe_if_destroy(struct net_device *netdev)
 	rtnl_lock();
 	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
 	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
-	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
-		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+	if (!is_zero_ether_addr(fc->ctlr.data_src_addr))
+		dev_unicast_delete(fc->real_dev,
+				   fc->ctlr.data_src_addr, ETH_ALEN);
+	dev_mc_delete(fc->real_dev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
 	rtnl_unlock();
 
 	/* Free the per-CPU revieve threads */
@@ -438,6 +439,58 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = {
 };
 
 /**
+ * fcoe_fip_recv - handle a received FIP frame.
+ * @skb: the receive skb
+ * @dev: associated &net_device
+ * @ptype: the &packet_type structure which was used to register this handler.
+ * @orig_dev: original receive &net_device, in case @dev is a bond.
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
+			 struct packet_type *ptype,
+			 struct net_device *orig_dev)
+{
+	struct fcoe_softc *fc;
+
+	fc = container_of(ptype, struct fcoe_softc, fip_packet_type);
+	fcoe_ctlr_recv(&fc->ctlr, skb);
+	return 0;
+}
+
+/**
+ * fcoe_fip_send() - send an Ethernet-encapsulated FIP frame.
+ * @fip: FCoE controller.
+ * @skb: FIP Packet.
+ */
+static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
+{
+	skb->dev = fcoe_from_ctlr(fip)->real_dev;
+	dev_queue_xmit(skb);
+}
+
+/**
+ * fcoe_update_src_mac() - Update Ethernet MAC filters.
+ * @fip: FCoE controller.
+ * @old: Unicast MAC address to delete if the MAC is non-zero.
+ * @new: Unicast MAC address to add.
+ *
+ * Remove any previously-set unicast MAC filter.
+ * Add secondary FCoE MAC address filter for our OUI.
+ */
+static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new)
+{
+	struct fcoe_softc *fc;
+
+	fc = fcoe_from_ctlr(fip);
+	rtnl_lock();
+	if (!is_zero_ether_addr(old))
+		dev_unicast_delete(fc->real_dev, old, ETH_ALEN);
+	dev_unicast_add(fc->real_dev, new, ETH_ALEN);
+	rtnl_unlock();
+}
+
+/**
  * fcoe_if_create() - this function creates the fcoe interface
  * @netdev: pointer the associated netdevice
  *
@@ -485,6 +538,18 @@ static int fcoe_if_create(struct net_device *netdev)
 		goto out_host_put;
 	}
 
+	/*
+	 * Initialize FIP.
+	 */
+	fcoe_ctlr_init(&fc->ctlr);
+	fc->ctlr.send = fcoe_fip_send;
+	fc->ctlr.update_mac = fcoe_update_src_mac;
+
+	fc->fip_packet_type.func = fcoe_fip_recv;
+	fc->fip_packet_type.type = htons(ETH_P_FIP);
+	fc->fip_packet_type.dev = fc->real_dev;
+	dev_add_pack(&fc->fip_packet_type);
+
 	/* configure lport scsi host properties */
 	rc = fcoe_shost_config(lp, shost, &netdev->dev);
 	if (rc) {
@@ -513,6 +578,9 @@ static int fcoe_if_create(struct net_device *netdev)
 
 	fc_fabric_login(lp);
 
+	if (!fcoe_link_ok(lp))
+		fcoe_ctlr_link_up(&fc->ctlr);
+
 	dev_hold(netdev);
 
 	return rc;
@@ -716,11 +784,13 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 	unsigned int cpu = 0;
 
 	fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
-	lp = fc->lp;
+	lp = fc->ctlr.lp;
 	if (unlikely(lp == NULL)) {
 		FC_DBG("cannot find hba structure");
 		goto err2;
 	}
+	if (!lp->link_up)
+		goto err2;
 
 	if (unlikely(debug_fcoe)) {
 		FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p "
@@ -919,7 +989,6 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 	unsigned int hlen;		/* header length implies the version */
 	unsigned int tlen;		/* trailer length */
 	unsigned int elen;		/* eth header, may include vlan */
-	int flogi_in_progress = 0;
 	struct fcoe_softc *fc;
 	u8 sof, eof;
 	struct fcoe_hdr *hp;
@@ -927,31 +996,19 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 	WARN_ON((fr_len(fp) % sizeof(u32)) != 0);
 
 	fc = lport_priv(lp);
-	/*
-	 * if it is a flogi then we need to learn gw-addr
-	 * and my own fcid
-	 */
 	fh = fc_frame_header_get(fp);
-	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
-		if (fc_frame_payload_op(fp) == ELS_FLOGI) {
-			fc->flogi_oxid = ntohs(fh->fh_ox_id);
-			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
-			fc->flogi_progress = 1;
-			flogi_in_progress = 1;
-		} else if (fc->flogi_progress && ntoh24(fh->fh_s_id) != 0) {
-			/*
-			 * Here we must've gotten an SID by accepting an FLOGI
-			 * from a point-to-point connection.  Switch to using
-			 * the source mac based on the SID.  The destination
-			 * MAC in this case would have been set by receving the
-			 * FLOGI.
-			 */
-			fc_fcoe_set_mac(fc->data_src_addr, fh->fh_s_id);
-			fc->flogi_progress = 0;
-		}
+	skb = fp_skb(fp);
+	wlen = skb->len / FCOE_WORD_TO_BYTE;
+
+	if (!lp->link_up) {
+		kfree(skb);
+		return 0;
 	}
 
-	skb = fp_skb(fp);
+	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
+	    fcoe_ctlr_els_send(&fc->ctlr, skb))
+		return 0;
+
 	sof = fr_sof(fp);
 	eof = fr_eof(fp);
 
@@ -1006,16 +1063,16 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 	/* fill up mac and fcoe headers */
 	eh = eth_hdr(skb);
 	eh->h_proto = htons(ETH_P_FCOE);
-	if (fc->address_mode == FCOE_FCOUI_ADDR_MODE)
+	if (fc->ctlr.map_dest)
 		fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
 	else
 		/* insert GW address */
-		memcpy(eh->h_dest, fc->dest_addr, ETH_ALEN);
+		memcpy(eh->h_dest, fc->ctlr.dest_addr, ETH_ALEN);
 
-	if (unlikely(flogi_in_progress))
-		memcpy(eh->h_source, fc->ctl_src_addr, ETH_ALEN);
+	if (unlikely(fc->ctlr.flogi_oxid != FC_XID_UNKNOWN))
+		memcpy(eh->h_source, fc->ctlr.ctl_src_addr, ETH_ALEN);
 	else
-		memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN);
+		memcpy(eh->h_source, fc->ctlr.data_src_addr, ETH_ALEN);
 
 	hp = (struct fcoe_hdr *)(eh + 1);
 	memset(hp, 0, sizeof(*hp));
@@ -1115,11 +1172,9 @@ int fcoe_percpu_receive_thread(void *arg)
 		 * Save source MAC address before discarding header.
 		 */
 		fc = lport_priv(lp);
-		if (unlikely(fc->flogi_progress))
-			mac = eth_hdr(skb)->h_source;
-
 		if (skb_is_nonlinear(skb))
 			skb_linearize(skb);	/* not ideal */
+		mac = eth_hdr(skb)->h_source;
 
 		/*
 		 * Frame length checks and setting up the header pointers
@@ -1194,72 +1249,17 @@ int fcoe_percpu_receive_thread(void *arg)
 			}
 			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
 		}
-		/* non flogi and non data exchanges are handled here */
-		if (unlikely(fc->flogi_progress))
-			fcoe_recv_flogi(fc, fp, mac);
+		if (unlikely(fc->ctlr.flogi_oxid != FC_XID_UNKNOWN) &&
+		    fcoe_ctlr_recv_flogi(&fc->ctlr, fp, mac)) {
+			fc_frame_free(fp);
+			continue;
+		}
 		fc_exch_recv(lp, lp->emp, fp);
 	}
 	return 0;
 }
 
 /**
- * fcoe_recv_flogi() - flogi receive function
- * @fc: associated fcoe_softc
- * @fp: the recieved frame
- * @sa: the source address of this flogi
- *
- * This is responsible to parse the flogi response and sets the corresponding
- * mac address for the initiator, eitehr OUI based or GW based.
- *
- * Returns: none
- */
-static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa)
-{
-	struct fc_frame_header *fh;
-	u8 op;
-
-	fh = fc_frame_header_get(fp);
-	if (fh->fh_type != FC_TYPE_ELS)
-		return;
-	op = fc_frame_payload_op(fp);
-	if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
-	    fc->flogi_oxid == ntohs(fh->fh_ox_id)) {
-		/*
-		 * FLOGI accepted.
-		 * If the src mac addr is FC_OUI-based, then we mark the
-		 * address_mode flag to use FC_OUI-based Ethernet DA.
-		 * Otherwise we use the FCoE gateway addr
-		 */
-		if (!compare_ether_addr(sa, (u8[6]) FC_FCOE_FLOGI_MAC)) {
-			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
-		} else {
-			memcpy(fc->dest_addr, sa, ETH_ALEN);
-			fc->address_mode = FCOE_GW_ADDR_MODE;
-		}
-
-		/*
-		 * Remove any previously-set unicast MAC filter.
-		 * Add secondary FCoE MAC address filter for our OUI.
-		 */
-		rtnl_lock();
-		if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
-			dev_unicast_delete(fc->real_dev, fc->data_src_addr,
-					   ETH_ALEN);
-		fc_fcoe_set_mac(fc->data_src_addr, fh->fh_d_id);
-		dev_unicast_add(fc->real_dev, fc->data_src_addr, ETH_ALEN);
-		rtnl_unlock();
-
-		fc->flogi_progress = 0;
-	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
-		/*
-		 * Save source MAC for point-to-point responses.
-		 */
-		memcpy(fc->dest_addr, sa, ETH_ALEN);
-		fc->address_mode = FCOE_GW_ADDR_MODE;
-	}
-}
-
-/**
  * fcoe_watchdog() - fcoe timer callback
  * @vp:
  *
@@ -1275,8 +1275,8 @@ void fcoe_watchdog(ulong vp)
 
 	read_lock(&fcoe_hostlist_lock);
 	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		if (fc->lp)
-			fcoe_check_wait_queue(fc->lp);
+		if (fc->ctlr.lp)
+			fcoe_check_wait_queue(fc->ctlr.lp);
 	}
 	read_unlock(&fcoe_hostlist_lock);
 
@@ -1377,14 +1377,14 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 	struct net_device *real_dev = ptr;
 	struct fcoe_softc *fc;
 	struct fcoe_dev_stats *stats;
-	u32 new_link_up;
+	u32 link_possible = 1;
 	u32 mfs;
 	int rc = NOTIFY_OK;
 
 	read_lock(&fcoe_hostlist_lock);
 	list_for_each_entry(fc, &fcoe_hostlist, list) {
 		if (fc->real_dev == real_dev) {
-			lp = fc->lp;
+			lp = fc->ctlr.lp;
 			break;
 		}
 	}
@@ -1394,15 +1394,13 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 		goto out;
 	}
 
-	new_link_up = lp->link_up;
 	switch (event) {
 	case NETDEV_DOWN:
 	case NETDEV_GOING_DOWN:
-		new_link_up = 0;
+		link_possible = 0;
 		break;
 	case NETDEV_UP:
 	case NETDEV_CHANGE:
-		new_link_up = !fcoe_link_ok(lp);
 		break;
 	case NETDEV_CHANGEMTU:
 		mfs = fc->real_dev->mtu -
@@ -1410,22 +1408,18 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 			 sizeof(struct fcoe_crc_eof));
 		if (mfs >= FC_MIN_MAX_FRAME)
 			fc_set_mfs(lp, mfs);
-		new_link_up = !fcoe_link_ok(lp);
 		break;
 	case NETDEV_REGISTER:
 		break;
 	default:
-		FC_DBG("unknown event %ld call", event);
+		FC_DBG("Unknown event %ld from netdev netlink\n", event);
 	}
-	if (lp->link_up != new_link_up) {
-		if (new_link_up)
-			fc_linkup(lp);
-		else {
-			stats = fc_lport_get_stats(lp);
-			stats->LinkFailureCount++;
-			fc_linkdown(lp);
-			fcoe_clean_pending_queue(lp);
-		}
+	if (link_possible && !fcoe_link_ok(lp))
+		fcoe_ctlr_link_up(&fc->ctlr);
+	else if (fcoe_ctlr_link_down(&fc->ctlr)) {
+		stats = fc_lport_get_stats(lp);
+		stats->LinkFailureCount++;
+		fcoe_clean_pending_queue(lp);
 	}
 out:
 	return rc;
@@ -1751,7 +1745,7 @@ struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
 
 	fc = fcoe_hostlist_lookup_softc(netdev);
 
-	return (fc) ? fc->lp : NULL;
+	return (fc) ? fc->ctlr.lp : NULL;
 }
 EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup);
 
diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h
index 4a9611f..917aae8 100644
--- a/drivers/scsi/fcoe/fcoe.h
+++ b/drivers/scsi/fcoe/fcoe.h
@@ -26,10 +26,6 @@
 #define FCOE_MAX_QUEUE_DEPTH	256
 #define FCOE_LOW_QUEUE_DEPTH	32
 
-/* destination address mode */
-#define FCOE_GW_ADDR_MODE	0x00
-#define FCOE_FCOUI_ADDR_MODE	0x01
-
 #define FCOE_WORD_TO_BYTE	4
 
 #define FCOE_VERSION	"0.1"
@@ -59,24 +55,17 @@ struct fcoe_percpu_s {
  */
 struct fcoe_softc {
 	struct list_head list;
-	struct fc_lport *lp;
 	struct net_device *real_dev;
 	struct net_device *phys_dev;		/* device with ethtool_ops */
 	struct packet_type  fcoe_packet_type;
+	struct packet_type  fip_packet_type;
 	struct sk_buff_head fcoe_pending_queue;
 	u8	fcoe_pending_queue_active;
-
-	u8 dest_addr[ETH_ALEN];
-	u8 ctl_src_addr[ETH_ALEN];
-	u8 data_src_addr[ETH_ALEN];
-	/*
-	 * fcoe protocol address learning related stuff
-	 */
-	u16 flogi_oxid;
-	u8 flogi_progress;
-	u8 address_mode;
+	struct fcoe_ctlr ctlr;
 };
 
+#define fcoe_from_ctlr(fc) container_of(fc, struct fcoe_softc, ctlr)
+
 static inline struct net_device *fcoe_netdev(
 	const struct fc_lport *lp)
 {
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index dff97fc..f410f4a 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -1,5 +1,6 @@
 /*
- * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ * Copyright (c) 2008-2009 Cisco Systems, Inc.  All rights reserved.
+ * Copyright (c) 2009 Intel Corporation.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -17,15 +18,1261 @@
  * Maintained at www.Open-FCoE.org
  */
 
+#include <linux/types.h>
 #include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
 #include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/netdevice.h>
+#include <linux/errno.h>
+#include <linux/bitops.h>
+#include <net/rtnetlink.h>
+
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_fs.h>
+#include <scsi/fc/fc_fip.h>
+#include <scsi/fc/fc_encaps.h>
+#include <scsi/fc/fc_fcoe.h>
 
 #include <scsi/libfc.h>
+#include <scsi/libfcoe.h>
 
 MODULE_AUTHOR("Open-FCoE.org");
-MODULE_DESCRIPTION("FCoE");
+MODULE_DESCRIPTION("FIP discovery protocol support for FCoE HBAs");
 MODULE_LICENSE("GPL v2");
 
+#define	FCOE_CTLR_MIN_FKA	500		/* min keep alive (mS) */
+#define	FCOE_CTLR_DEF_FKA	FIP_DEF_FKA	/* default keep alive (mS) */
+
+static void fcoe_ctlr_timeout(unsigned long);
+static void fcoe_ctlr_link_work(struct work_struct *);
+static void fcoe_ctlr_recv_work(struct work_struct *);
+
+static u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS;
+
+static u32 fcoe_ctlr_debug;	/* 1 for basic, 2 for noisy debug */
+
+#define FIP_DBG_LVL(level, fmt, args...) 				\
+		do {							\
+			if (fcoe_ctlr_debug >= (level))			\
+				FC_DBG(fmt, ##args);			\
+		} while (0)
+
+#define FIP_DBG(fmt, args...)	FIP_DBG_LVL(1, fmt, ##args)
+
+/*
+ * Return non-zero if FCF fcoe_size has been validated.
+ */
+static inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf)
+{
+	return (fcf->flags & FIP_FL_SOL) != 0;
+}
+
+/*
+ * Return non-zero if the FCF is usable.
+ */
+static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf)
+{
+	u16 flags = FIP_FL_SOL | FIP_FL_AVAIL;
+
+	return (fcf->flags & flags) == flags;
+}
+
+/**
+ * fcoe_ctlr_init() - Initialize the FCoE Controller instance.
+ * @fip:	FCoE controller.
+ */
+void fcoe_ctlr_init(struct fcoe_ctlr *fip)
+{
+	fip->state = FIP_ST_LINK_WAIT;
+	INIT_LIST_HEAD(&fip->fcfs);
+	spin_lock_init(&fip->lock);
+	fip->flogi_oxid = FC_XID_UNKNOWN;
+	setup_timer(&fip->timer, fcoe_ctlr_timeout, (unsigned long)fip);
+	INIT_WORK(&fip->link_work, fcoe_ctlr_link_work);
+	INIT_WORK(&fip->recv_work, fcoe_ctlr_recv_work);
+	skb_queue_head_init(&fip->fip_recv_list);
+}
+EXPORT_SYMBOL(fcoe_ctlr_init);
+
+/**
+ * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller.
+ * @fip:	FCoE controller.
+ *
+ * Called with &fcoe_ctlr lock held.
+ */
+static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
+{
+	struct fcoe_fcf *fcf;
+	struct fcoe_fcf *next;
+
+	fip->sel_fcf = NULL;
+	list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
+		list_del(&fcf->list);
+		kfree(fcf);
+	}
+	fip->fcf_count = 0;
+	fip->sel_time = 0;
+}
+
+/**
+ * fcoe_ctrl_destroy() - Disable and tear-down the FCoE controller.
+ * @fip:	FCoE controller.
+ *
+ * This is called by FCoE drivers before freeing the &fcoe_ctlr.
+ *
+ * The receive handler will have been deleted before this to guarantee
+ * that no more recv_work will be scheduled.
+ *
+ * The timer routine will simply return once we set FIP_ST_DISABLED.
+ * This guarantees that no further timeouts or work will be scheduled.
+ */
+void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
+{
+	flush_work(&fip->recv_work);
+	spin_lock_bh(&fip->lock);
+	fip->state = FIP_ST_DISABLED;
+	fcoe_ctlr_reset_fcfs(fip);
+	spin_unlock_bh(&fip->lock);
+	del_timer_sync(&fip->timer);
+	flush_work(&fip->link_work);
+}
+EXPORT_SYMBOL(fcoe_ctlr_destroy);
+
+/**
+ * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port.
+ * @fip:	FCoE controller.
+ *
+ * Returns the maximum packet size including the FCoE header and trailer,
+ * but not including any Ethernet or VLAN headers.
+ */
+static inline u32 fcoe_ctlr_fcoe_size(struct fcoe_ctlr *fip)
+{
+	/*
+	 * Determine the max FCoE frame size allowed, including
+	 * FCoE header and trailer.
+	 * Note:  lp->mfs is currently the payload size, not the frame size.
+	 */
+	return fip->lp->mfs + sizeof(struct fc_frame_header) +
+		sizeof(struct fcoe_hdr) + sizeof(struct fcoe_crc_eof);
+}
+
+/**
+ * fcoe_ctlr_solicit() - Send a solicitation.
+ * @fip:	FCoE controller.
+ * @fcf:	Destination FCF.  If NULL, a multicast solicitation is sent.
+ */
+static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
+{
+	struct sk_buff *skb;
+	struct fip_sol {
+		struct ethhdr eth;
+		struct fip_header fip;
+		struct {
+			struct fip_mac_desc mac;
+			struct fip_wwn_desc wwnn;
+			struct fip_size_desc size;
+		} __attribute__((packed)) desc;
+	}  __attribute__((packed)) *sol;
+	u32 fcoe_size;
+
+	skb = dev_alloc_skb(sizeof(*sol));
+	if (!skb)
+		return;
+
+	sol = (struct fip_sol *)skb->data;
+
+	memset(sol, 0, sizeof(*sol));
+	memcpy(sol->eth.h_dest, fcf ? fcf->fcf_mac : fcoe_all_fcfs, ETH_ALEN);
+	memcpy(sol->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
+	sol->eth.h_proto = htons(ETH_P_FIP);
+
+	sol->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
+	sol->fip.fip_op = htons(FIP_OP_DISC);
+	sol->fip.fip_subcode = FIP_SC_SOL;
+	sol->fip.fip_dl_len = htons(sizeof(sol->desc) / FIP_BPW);
+	sol->fip.fip_flags = htons(FIP_FL_FPMA);
+
+	sol->desc.mac.fd_desc.fip_dtype = FIP_DT_MAC;
+	sol->desc.mac.fd_desc.fip_dlen = sizeof(sol->desc.mac) / FIP_BPW;
+	memcpy(sol->desc.mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
+
+	sol->desc.wwnn.fd_desc.fip_dtype = FIP_DT_NAME;
+	sol->desc.wwnn.fd_desc.fip_dlen = sizeof(sol->desc.wwnn) / FIP_BPW;
+	put_unaligned_be64(fip->lp->wwnn, &sol->desc.wwnn.fd_wwn);
+
+	fcoe_size = fcoe_ctlr_fcoe_size(fip);
+	sol->desc.size.fd_desc.fip_dtype = FIP_DT_FCOE_SIZE;
+	sol->desc.size.fd_desc.fip_dlen = sizeof(sol->desc.size) / FIP_BPW;
+	sol->desc.size.fd_size = htons(fcoe_size);
+
+	skb_put(skb, sizeof(*sol));
+	skb->protocol = htons(ETH_P_802_3);
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	fip->send(fip, skb);
+
+	if (!fcf)
+		fip->sol_time = jiffies;
+}
+
+/**
+ * fcoe_ctlr_link_up() - Start FCoE controller.
+ * @fip:	FCoE controller.
+ *
+ * Called from the LLD when the network link is ready.
+ */
+void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
+{
+	spin_lock_bh(&fip->lock);
+	if (fip->state == FIP_ST_NON_FIP || fip->state == FIP_ST_AUTO) {
+		fip->last_link = 1;
+		fip->link = 1;
+		spin_unlock_bh(&fip->lock);
+		fc_linkup(fip->lp);
+	} else if (fip->state == FIP_ST_LINK_WAIT) {
+		fip->state = FIP_ST_AUTO;
+		fip->last_link = 1;
+		fip->link = 1;
+		spin_unlock_bh(&fip->lock);
+		FIP_DBG("%s", "setting AUTO mode.\n");
+		fc_linkup(fip->lp);
+		fcoe_ctlr_solicit(fip, NULL);
+	} else
+		spin_unlock_bh(&fip->lock);
+}
+EXPORT_SYMBOL(fcoe_ctlr_link_up);
+
+/**
+ * fcoe_ctlr_reset() - Reset FIP.
+ * @fip:	FCoE controller.
+ * @new_state:	FIP state to be entered.
+ *
+ * Returns non-zero if the link was up and now isn't.
+ */
+static int fcoe_ctlr_reset(struct fcoe_ctlr *fip, enum fip_state new_state)
+{
+	struct fc_lport *lp = fip->lp;
+	int link_dropped;
+
+	spin_lock_bh(&fip->lock);
+	fcoe_ctlr_reset_fcfs(fip);
+	del_timer(&fip->timer);
+	fip->state = new_state;
+	fip->ctlr_ka_time = 0;
+	fip->port_ka_time = 0;
+	fip->sol_time = 0;
+	fip->flogi_oxid = FC_XID_UNKNOWN;
+	fip->map_dest = 0;
+	fip->last_link = 0;
+	link_dropped = fip->link;
+	fip->link = 0;
+	spin_unlock_bh(&fip->lock);
+
+	if (link_dropped)
+		fc_linkdown(lp);
+
+	if (new_state == FIP_ST_ENABLED) {
+		fcoe_ctlr_solicit(fip, NULL);
+		fc_linkup(lp);
+		link_dropped = 0;
+	}
+	return link_dropped;
+}
+
+/**
+ * fcoe_ctlr_link_down() - Stop FCoE controller.
+ * @fip:	FCoE controller.
+ *
+ * Returns non-zero if the link was up and now isn't.
+ *
+ * Called from the LLD when the network link is not ready.
+ * There may be multiple calls while the link is down.
+ */
+int fcoe_ctlr_link_down(struct fcoe_ctlr *fip)
+{
+	return fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT);
+}
+EXPORT_SYMBOL(fcoe_ctlr_link_down);
+
+/**
+ * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF.
+ * @fip:	FCoE controller.
+ * @ports:	0 for controller keep-alive, 1 for port keep-alive.
+ * @sa:		source MAC address.
+ *
+ * A controller keep-alive is sent every fka_period (typically 8 seconds).
+ * The source MAC is the native MAC address.
+ *
+ * A port keep-alive is sent every 90 seconds while logged in.
+ * The source MAC is the assigned mapped source address.
+ * The destination is the FCF's F-port.
+ */
+static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
+{
+	struct sk_buff *skb;
+	struct fip_kal {
+		struct ethhdr eth;
+		struct fip_header fip;
+		struct fip_mac_desc mac;
+	} __attribute__((packed)) *kal;
+	struct fip_vn_desc *vn;
+	u32 len;
+	struct fc_lport *lp;
+	struct fcoe_fcf *fcf;
+
+	fcf = fip->sel_fcf;
+	lp = fip->lp;
+	if (!fcf || !fc_host_port_id(lp->host))
+		return;
+
+	len = fcoe_ctlr_fcoe_size(fip) + sizeof(struct ethhdr);
+	BUG_ON(len < sizeof(*kal) + sizeof(*vn));
+	skb = dev_alloc_skb(len);
+	if (!skb)
+		return;
+
+	kal = (struct fip_kal *)skb->data;
+	memset(kal, 0, len);
+	memcpy(kal->eth.h_dest, fcf->fcf_mac, ETH_ALEN);
+	memcpy(kal->eth.h_source, sa, ETH_ALEN);
+	kal->eth.h_proto = htons(ETH_P_FIP);
+
+	kal->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
+	kal->fip.fip_op = htons(FIP_OP_CTRL);
+	kal->fip.fip_subcode = FIP_SC_KEEP_ALIVE;
+	kal->fip.fip_dl_len = htons((sizeof(kal->mac) +
+				    ports * sizeof(*vn)) / FIP_BPW);
+	kal->fip.fip_flags = htons(FIP_FL_FPMA);
+
+	kal->mac.fd_desc.fip_dtype = FIP_DT_MAC;
+	kal->mac.fd_desc.fip_dlen = sizeof(kal->mac) / FIP_BPW;
+	memcpy(kal->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
+
+	if (ports) {
+		vn = (struct fip_vn_desc *)(kal + 1);
+		vn->fd_desc.fip_dtype = FIP_DT_VN_ID;
+		vn->fd_desc.fip_dlen = sizeof(*vn) / FIP_BPW;
+		memcpy(vn->fd_mac, fip->data_src_addr, ETH_ALEN);
+		hton24(vn->fd_fc_id, fc_host_port_id(lp->host));
+		put_unaligned_be64(lp->wwpn, &vn->fd_wwpn);
+	}
+
+	skb_put(skb, len);
+	skb->protocol = htons(ETH_P_802_3);
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	fip->send(fip, skb);
+}
+
+/**
+ * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it.
+ * @fip:	FCoE controller.
+ * @dtype:	FIP descriptor type for the frame.
+ * @skb:	FCoE ELS frame including FC header but no FCoE headers.
+ *
+ * Returns non-zero error code on failure.
+ *
+ * The caller must check that the length is a multiple of 4.
+ *
+ * The @skb must have enough headroom (28 bytes) and tailroom (8 bytes).
+ * Headroom includes the FIP encapsulation description, FIP header, and
+ * Ethernet header.  The tailroom is for the FIP MAC descriptor.
+ */
+static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
+			    u8 dtype, struct sk_buff *skb)
+{
+	struct fip_encaps_head {
+		struct ethhdr eth;
+		struct fip_header fip;
+		struct fip_encaps encaps;
+	} __attribute__((packed)) *cap;
+	struct fip_mac_desc *mac;
+	struct fcoe_fcf *fcf;
+	size_t dlen;
+
+	fcf = fip->sel_fcf;
+	if (!fcf)
+		return -ENODEV;
+	dlen = sizeof(struct fip_encaps) + skb->len;	/* len before push */
+	cap = (struct fip_encaps_head *)skb_push(skb, sizeof(*cap));
+
+	memset(cap, 0, sizeof(*cap));
+	memcpy(cap->eth.h_dest, fcf->fcf_mac, ETH_ALEN);
+	memcpy(cap->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
+	cap->eth.h_proto = htons(ETH_P_FIP);
+
+	cap->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
+	cap->fip.fip_op = htons(FIP_OP_LS);
+	cap->fip.fip_subcode = FIP_SC_REQ;
+	cap->fip.fip_dl_len = htons((dlen + sizeof(*mac)) / FIP_BPW);
+	cap->fip.fip_flags = htons(FIP_FL_FPMA);
+
+	cap->encaps.fd_desc.fip_dtype = dtype;
+	cap->encaps.fd_desc.fip_dlen = dlen / FIP_BPW;
+
+	mac = (struct fip_mac_desc *)skb_put(skb, sizeof(*mac));
+	memset(mac, 0, sizeof(mac));
+	mac->fd_desc.fip_dtype = FIP_DT_MAC;
+	mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW;
+	if (dtype != ELS_FLOGI)
+		memcpy(mac->fd_mac, fip->data_src_addr, ETH_ALEN);
+
+	skb->protocol = htons(ETH_P_802_3);
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	return 0;
+}
+
+/**
+ * fcoe_ctlr_els_send() - Send an ELS frame encapsulated by FIP if appropriate.
+ * @fip:	FCoE controller.
+ * @skb:	FCoE ELS frame including FC header but no FCoE headers.
+ *
+ * Returns a non-zero error code if the frame should not be sent.
+ * Returns zero if the caller should send the frame with FCoE encapsulation.
+ *
+ * The caller must check that the length is a multiple of 4.
+ * The SKB must have enough headroom (28 bytes) and tailroom (8 bytes).
+ */
+int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
+{
+	struct fc_frame_header *fh;
+	u16 old_xid;
+	u8 op;
+
+	if (fip->state == FIP_ST_NON_FIP)
+		return 0;
+
+	fh = (struct fc_frame_header *)skb->data;
+	op = *(u8 *)(fh + 1);
+
+	switch (op) {
+	case ELS_FLOGI:
+		old_xid = fip->flogi_oxid;
+		fip->flogi_oxid = ntohs(fh->fh_ox_id);
+		if (fip->state == FIP_ST_AUTO) {
+			if (old_xid == FC_XID_UNKNOWN)
+				fip->flogi_count = 0;
+			fip->flogi_count++;
+			if (fip->flogi_count < 3)
+				goto drop;
+			fip->map_dest = 1;
+			return 0;
+		}
+		op = FIP_DT_FLOGI;
+		break;
+	case ELS_FDISC:
+		if (ntoh24(fh->fh_s_id))
+			return 0;
+		op = FIP_DT_FDISC;
+		break;
+	case ELS_LOGO:
+		if (fip->state != FIP_ST_ENABLED)
+			return 0;
+		if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI)
+			return 0;
+		op = FIP_DT_LOGO;
+		break;
+	case ELS_LS_ACC:
+		if (fip->flogi_oxid == FC_XID_UNKNOWN)
+			return 0;
+		if (!ntoh24(fh->fh_s_id))
+			return 0;
+		if (fip->state == FIP_ST_AUTO)
+			return 0;
+		/*
+		 * Here we must've gotten an SID by accepting an FLOGI
+		 * from a point-to-point connection.  Switch to using
+		 * the source mac based on the SID.  The destination
+		 * MAC in this case would have been set by receving the
+		 * FLOGI.
+		 */
+		fip->flogi_oxid = FC_XID_UNKNOWN;
+		fc_fcoe_set_mac(fip->data_src_addr, fh->fh_s_id);
+		return 0;
+	default:
+		if (fip->state != FIP_ST_ENABLED)
+			goto drop;
+		return 0;
+	}
+	if (fcoe_ctlr_encaps(fip, op, skb))
+		goto drop;
+	fip->send(fip, skb);
+	return -EINPROGRESS;
+drop:
+	kfree_skb(skb);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(fcoe_ctlr_els_send);
+
+/*
+ * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller.
+ * @fip:	FCoE controller.
+ *
+ * Called with lock held.
+ *
+ * An FCF is considered old if we have missed three advertisements.
+ * That is, there have been no valid advertisement from it for three
+ * times its keep-alive period including fuzz.
+ *
+ * In addition, determine the time when an FCF selection can occur.
+ */
+static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
+{
+	struct fcoe_fcf *fcf;
+	struct fcoe_fcf *next;
+	unsigned long sel_time = 0;
+
+	list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
+		if (time_after(jiffies, fcf->time + fcf->fka_period * 3 +
+			       msecs_to_jiffies(FIP_FCF_FUZZ * 3))) {
+			if (fip->sel_fcf == fcf)
+				fip->sel_fcf = NULL;
+			list_del(&fcf->list);
+			WARN_ON(!fip->fcf_count);
+			fip->fcf_count--;
+			kfree(fcf);
+		} else if (fcoe_ctlr_mtu_valid(fcf) &&
+			   (!sel_time || time_before(sel_time, fcf->time))) {
+			sel_time = fcf->time;
+		}
+	}
+	if (sel_time) {
+		sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY);
+		fip->sel_time = sel_time;
+		if (time_before(sel_time, fip->timer.expires))
+			mod_timer(&fip->timer, sel_time);
+	} else {
+		fip->sel_time = 0;
+	}
+}
+
+/**
+ * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry.
+ * @skb:	received FIP advertisement frame
+ * @fcf:	resulting FCF entry.
+ *
+ * Returns zero on a valid parsed advertisement,
+ * otherwise returns non zero value.
+ */
+static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
+{
+	struct fip_header *fiph;
+	struct fip_desc *desc = NULL;
+	struct fip_wwn_desc *wwn;
+	struct fip_fab_desc *fab;
+	struct fip_fka_desc *fka;
+	unsigned long t;
+	size_t rlen;
+	size_t dlen;
+
+	memset(fcf, 0, sizeof(*fcf));
+	fcf->fka_period = msecs_to_jiffies(FCOE_CTLR_DEF_FKA);
+
+	fiph = (struct fip_header *)skb->data;
+	fcf->flags = ntohs(fiph->fip_flags);
+
+	rlen = ntohs(fiph->fip_dl_len) * 4;
+	if (rlen + sizeof(*fiph) > skb->len)
+		return -EINVAL;
+
+	desc = (struct fip_desc *)(fiph + 1);
+	while (rlen > 0) {
+		dlen = desc->fip_dlen * FIP_BPW;
+		if (dlen < sizeof(*desc) || dlen > rlen)
+			return -EINVAL;
+		switch (desc->fip_dtype) {
+		case FIP_DT_PRI:
+			if (dlen != sizeof(struct fip_pri_desc))
+				goto len_err;
+			fcf->pri = ((struct fip_pri_desc *)desc)->fd_pri;
+			break;
+		case FIP_DT_MAC:
+			if (dlen != sizeof(struct fip_mac_desc))
+				goto len_err;
+			memcpy(fcf->fcf_mac,
+			       ((struct fip_mac_desc *)desc)->fd_mac,
+			       ETH_ALEN);
+			if (!is_valid_ether_addr(fcf->fcf_mac)) {
+				FIP_DBG("invalid MAC addr in FIP adv\n");
+				return -EINVAL;
+			}
+			break;
+		case FIP_DT_NAME:
+			if (dlen != sizeof(struct fip_wwn_desc))
+				goto len_err;
+			wwn = (struct fip_wwn_desc *)desc;
+			fcf->switch_name = get_unaligned_be64(&wwn->fd_wwn);
+			break;
+		case FIP_DT_FAB:
+			if (dlen != sizeof(struct fip_fab_desc))
+				goto len_err;
+			fab = (struct fip_fab_desc *)desc;
+			fcf->fabric_name = get_unaligned_be64(&fab->fd_wwn);
+			fcf->vfid = ntohs(fab->fd_vfid);
+			fcf->fc_map = ntoh24(fab->fd_map);
+			break;
+		case FIP_DT_FKA:
+			if (dlen != sizeof(struct fip_fka_desc))
+				goto len_err;
+			fka = (struct fip_fka_desc *)desc;
+			t = ntohl(fka->fd_fka_period);
+			if (t >= FCOE_CTLR_MIN_FKA)
+				fcf->fka_period = msecs_to_jiffies(t);
+			break;
+		case FIP_DT_MAP_OUI:
+		case FIP_DT_FCOE_SIZE:
+		case FIP_DT_FLOGI:
+		case FIP_DT_FDISC:
+		case FIP_DT_LOGO:
+		case FIP_DT_ELP:
+		default:
+			FIP_DBG("unexpected descriptor type %x in FIP adv\n",
+				desc->fip_dtype);
+			/* standard says ignore unknown descriptors >= 128 */
+			if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
+				return -EINVAL;
+			continue;
+		}
+		desc = (struct fip_desc *)((char *)desc + dlen);
+		rlen -= dlen;
+	}
+	if (!fcf->fc_map || (fcf->fc_map & 0x10000))
+		return -EINVAL;
+	if (!fcf->switch_name || !fcf->fabric_name)
+		return -EINVAL;
+	return 0;
+
+len_err:
+	FIP_DBG("FIP length error in descriptor type %x len %zu\n",
+		desc->fip_dtype, dlen);
+	return -EINVAL;
+}
+
+/**
+ * fcoe_ctlr_recv_adv() - Handle an incoming advertisement.
+ * @fip:	FCoE controller.
+ * @skb:	Received FIP packet.
+ */
+static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
+{
+	struct fcoe_fcf *fcf;
+	struct fcoe_fcf new;
+	struct fcoe_fcf *found;
+	unsigned long sol_tov = msecs_to_jiffies(FCOE_CTRL_SOL_TOV);
+	int first = 0;
+	int mtu_valid;
+
+	if (fcoe_ctlr_parse_adv(skb, &new))
+		return;
+
+	spin_lock_bh(&fip->lock);
+	first = list_empty(&fip->fcfs);
+	found = NULL;
+	list_for_each_entry(fcf, &fip->fcfs, list) {
+		if (fcf->switch_name == new.switch_name &&
+		    fcf->fabric_name == new.fabric_name &&
+		    fcf->fc_map == new.fc_map &&
+		    compare_ether_addr(fcf->fcf_mac, new.fcf_mac) == 0) {
+			found = fcf;
+			break;
+		}
+	}
+	if (!found) {
+		if (fip->fcf_count >= FCOE_CTLR_FCF_LIMIT)
+			goto out;
+
+		fcf = kmalloc(sizeof(*fcf), GFP_ATOMIC);
+		if (!fcf)
+			goto out;
+
+		fip->fcf_count++;
+		memcpy(fcf, &new, sizeof(new));
+		list_add(&fcf->list, &fip->fcfs);
+	} else {
+		/*
+		 * Flags in advertisements are ignored once the FCF is
+		 * selected.  Flags in unsolicited advertisements are
+		 * ignored after a usable solicited advertisement
+		 * has been received.
+		 */
+		if (fcf == fip->sel_fcf) {
+			fip->ctlr_ka_time -= fcf->fka_period;
+			fip->ctlr_ka_time += new.fka_period;
+			if (time_before(fip->ctlr_ka_time, fip->timer.expires))
+				mod_timer(&fip->timer, fip->ctlr_ka_time);
+		} else if (!fcoe_ctlr_fcf_usable(fcf))
+			fcf->flags = new.flags;
+		fcf->fka_period = new.fka_period;
+		memcpy(fcf->fcf_mac, new.fcf_mac, ETH_ALEN);
+	}
+	mtu_valid = fcoe_ctlr_mtu_valid(fcf);
+	fcf->time = jiffies;
+	FIP_DBG_LVL(found ? 2 : 1, "%s FCF for fab %llx map %x val %d\n",
+		    found ? "old" : "new",
+		    fcf->fabric_name, fcf->fc_map, mtu_valid);
+
+	/*
+	 * If this advertisement is not solicited and our max receive size
+	 * hasn't been verified, send a solicited advertisement.
+	 */
+	if (!mtu_valid)
+		fcoe_ctlr_solicit(fip, fcf);
+
+	/*
+	 * If its been a while since we did a solicit, and this is
+	 * the first advertisement we've received, do a multicast
+	 * solicitation to gather as many advertisements as we can
+	 * before selection occurs.
+	 */
+	if (first && time_after(jiffies, fip->sol_time + sol_tov))
+		fcoe_ctlr_solicit(fip, NULL);
+
+	/*
+	 * If this is the first validated FCF, note the time and
+	 * set a timer to trigger selection.
+	 */
+	if (mtu_valid && !fip->sel_time && fcoe_ctlr_fcf_usable(fcf)) {
+		fip->sel_time = jiffies +
+				msecs_to_jiffies(FCOE_CTLR_START_DELAY);
+		if (!timer_pending(&fip->timer) ||
+		    time_before(fip->sel_time, fip->timer.expires))
+			mod_timer(&fip->timer, fip->sel_time);
+	}
+out:
+	spin_unlock_bh(&fip->lock);
+}
+
+/**
+ * fcoe_ctlr_recv_els() - Handle an incoming FIP-encapsulated ELS frame.
+ * @fip:	FCoE controller.
+ * @skb:	Received FIP packet.
+ */
+static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
+{
+	struct fc_lport *lp = fip->lp;
+	struct fip_header *fiph;
+	struct fc_frame *fp;
+	struct fc_frame_header *fh = NULL;
+	struct fip_desc *desc;
+	struct fip_encaps *els;
+	struct fcoe_dev_stats *stats;
+	enum fip_desc_type els_dtype = 0;
+	u8 els_op;
+	u8 sub;
+	u8 granted_mac[ETH_ALEN] = { 0 };
+	size_t els_len = 0;
+	size_t rlen;
+	size_t dlen;
+
+	fiph = (struct fip_header *)skb->data;
+	sub = fiph->fip_subcode;
+	if (sub != FIP_SC_REQ && sub != FIP_SC_REP)
+		goto drop;
+
+	rlen = ntohs(fiph->fip_dl_len) * 4;
+	if (rlen + sizeof(*fiph) > skb->len)
+		goto drop;
+
+	desc = (struct fip_desc *)(fiph + 1);
+	while (rlen > 0) {
+		dlen = desc->fip_dlen * FIP_BPW;
+		if (dlen < sizeof(*desc) || dlen > rlen)
+			goto drop;
+		switch (desc->fip_dtype) {
+		case FIP_DT_MAC:
+			if (dlen != sizeof(struct fip_mac_desc))
+				goto len_err;
+			memcpy(granted_mac,
+			       ((struct fip_mac_desc *)desc)->fd_mac,
+			       ETH_ALEN);
+			if (!is_valid_ether_addr(granted_mac)) {
+				FIP_DBG("invalid MAC addrs in FIP ELS\n");
+				goto drop;
+			}
+			break;
+		case FIP_DT_FLOGI:
+		case FIP_DT_FDISC:
+		case FIP_DT_LOGO:
+		case FIP_DT_ELP:
+			if (fh)
+				goto drop;
+			if (dlen < sizeof(*els) + sizeof(*fh) + 1)
+				goto len_err;
+			els_len = dlen - sizeof(*els);
+			els = (struct fip_encaps *)desc;
+			fh = (struct fc_frame_header *)(els + 1);
+			els_dtype = desc->fip_dtype;
+			break;
+		default:
+			FIP_DBG("unexpected descriptor type %x "
+				"in FIP adv\n", desc->fip_dtype);
+			/* standard says ignore unknown descriptors >= 128 */
+			if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
+				goto drop;
+			continue;
+		}
+		desc = (struct fip_desc *)((char *)desc + dlen);
+		rlen -= dlen;
+	}
+
+	if (!fh)
+		goto drop;
+	els_op = *(u8 *)(fh + 1);
+
+	if (els_dtype == FIP_DT_FLOGI && sub == FIP_SC_REP &&
+	    fip->flogi_oxid == ntohs(fh->fh_ox_id) &&
+	    els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac)) {
+		fip->flogi_oxid = FC_XID_UNKNOWN;
+		fip->update_mac(fip, fip->data_src_addr, granted_mac);
+		memcpy(fip->data_src_addr, granted_mac, ETH_ALEN);
+	}
+
+	/*
+	 * Convert skb into an fc_frame containing only the ELS.
+	 */
+	skb_pull(skb, (u8 *)fh - skb->data);
+	skb_trim(skb, els_len);
+	fp = (struct fc_frame *)skb;
+	fc_frame_init(fp);
+	fr_sof(fp) = FC_SOF_I3;
+	fr_eof(fp) = FC_EOF_T;
+	fr_dev(fp) = lp;
+
+	stats = fc_lport_get_stats(lp);
+	stats->RxFrames++;
+	stats->RxWords += skb->len / FIP_BPW;
+
+	fc_exch_recv(lp, lp->emp, fp);
+	return;
+
+len_err:
+	FIP_DBG("FIP length error in descriptor type %x len %zu\n",
+		desc->fip_dtype, dlen);
+drop:
+	kfree_skb(skb);
+}
+
+/**
+ * fcoe_ctlr_recv_els() - Handle an incoming link reset frame.
+ * @fip:	FCoE controller.
+ * @fh:		Received FIP header.
+ *
+ * There may be multiple VN_Port descriptors.
+ * The overall length has already been checked.
+ */
+static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
+				      struct fip_header *fh)
+{
+	struct fip_desc *desc;
+	struct fip_mac_desc *mp;
+	struct fip_wwn_desc *wp;
+	struct fip_vn_desc *vp;
+	size_t rlen;
+	size_t dlen;
+	struct fcoe_fcf *fcf = fip->sel_fcf;
+	struct fc_lport *lp = fip->lp;
+	u32	desc_mask;
+
+	FIP_DBG("Clear Virtual Link received\n");
+	if (!fcf)
+		return;
+	if (!fcf || !fc_host_port_id(lp->host))
+		return;
+
+	/*
+	 * mask of required descriptors.  Validating each one clears its bit.
+	 */
+	desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) | BIT(FIP_DT_VN_ID);
+
+	rlen = ntohs(fh->fip_dl_len) * FIP_BPW;
+	desc = (struct fip_desc *)(fh + 1);
+	while (rlen >= sizeof(*desc)) {
+		dlen = desc->fip_dlen * FIP_BPW;
+		if (dlen > rlen)
+			return;
+		switch (desc->fip_dtype) {
+		case FIP_DT_MAC:
+			mp = (struct fip_mac_desc *)desc;
+			if (dlen < sizeof(*mp))
+				return;
+			if (compare_ether_addr(mp->fd_mac, fcf->fcf_mac))
+				return;
+			desc_mask &= ~BIT(FIP_DT_MAC);
+			break;
+		case FIP_DT_NAME:
+			wp = (struct fip_wwn_desc *)desc;
+			if (dlen < sizeof(*wp))
+				return;
+			if (get_unaligned_be64(&wp->fd_wwn) != fcf->switch_name)
+				return;
+			desc_mask &= ~BIT(FIP_DT_NAME);
+			break;
+		case FIP_DT_VN_ID:
+			vp = (struct fip_vn_desc *)desc;
+			if (dlen < sizeof(*vp))
+				return;
+			if (compare_ether_addr(vp->fd_mac,
+			    fip->data_src_addr) == 0 &&
+			    get_unaligned_be64(&vp->fd_wwpn) == lp->wwpn &&
+			    ntoh24(vp->fd_fc_id) == fc_host_port_id(lp->host))
+				desc_mask &= ~BIT(FIP_DT_VN_ID);
+			break;
+		default:
+			/* standard says ignore unknown descriptors >= 128 */
+			if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
+				return;
+			break;
+		}
+		desc = (struct fip_desc *)((char *)desc + dlen);
+		rlen -= dlen;
+	}
+
+	/*
+	 * reset only if all required descriptors were present and valid.
+	 */
+	if (desc_mask) {
+		FIP_DBG("missing descriptors mask %x\n", desc_mask);
+	} else {
+		FIP_DBG("performing Clear Virtual Link\n");
+		fcoe_ctlr_reset(fip, FIP_ST_ENABLED);
+	}
+}
+
+/**
+ * fcoe_ctlr_recv() - Receive a FIP frame.
+ * @fip:	FCoE controller.
+ * @skb:	Received FIP packet.
+ *
+ * This is called from NET_RX_SOFTIRQ.
+ */
+void fcoe_ctlr_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
+{
+	spin_lock_bh(&fip->fip_recv_list.lock);
+	__skb_queue_tail(&fip->fip_recv_list, skb);
+	spin_unlock_bh(&fip->fip_recv_list.lock);
+	schedule_work(&fip->recv_work);
+}
+EXPORT_SYMBOL(fcoe_ctlr_recv);
+
+/**
+ * fcoe_ctlr_recv_handler() - Receive a FIP frame.
+ * @fip:	FCoE controller.
+ * @skb:	Received FIP packet.
+ *
+ * Returns non-zero if the frame is dropped.
+ */
+static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
+{
+	struct fip_header *fiph;
+	struct ethhdr *eh;
+	enum fip_state state;
+	u16 op;
+	u8 sub;
+
+	if (skb_linearize(skb))
+		goto drop;
+	if (skb->len < sizeof(*fiph))
+		goto drop;
+	eh = eth_hdr(skb);
+	if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) &&
+	    compare_ether_addr(eh->h_dest, FIP_ALL_ENODE_MACS))
+		goto drop;
+	fiph = (struct fip_header *)skb->data;
+	op = ntohs(fiph->fip_op);
+	sub = fiph->fip_subcode;
+
+	FIP_DBG_LVL(2, "ver %x op %x/%x dl %x fl %x\n",
+		    FIP_VER_DECAPS(fiph->fip_ver), op, sub,
+		    ntohs(fiph->fip_dl_len), ntohs(fiph->fip_flags));
+
+	if (FIP_VER_DECAPS(fiph->fip_ver) != FIP_VER)
+		goto drop;
+	if (ntohs(fiph->fip_dl_len) * FIP_BPW + sizeof(*fiph) > skb->len)
+		goto drop;
+
+	spin_lock_bh(&fip->lock);
+	state = fip->state;
+	if (state == FIP_ST_AUTO) {
+		fip->map_dest = 0;
+		fip->state = FIP_ST_ENABLED;
+		state = FIP_ST_ENABLED;
+		FIP_DBG("using FIP mode\n");
+	}
+	spin_unlock_bh(&fip->lock);
+	if (state != FIP_ST_ENABLED)
+		goto drop;
+
+	if (op == FIP_OP_LS) {
+		fcoe_ctlr_recv_els(fip, skb);	/* consumes skb */
+		return 0;
+	}
+	if (op == FIP_OP_DISC && sub == FIP_SC_ADV)
+		fcoe_ctlr_recv_adv(fip, skb);
+	else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK)
+		fcoe_ctlr_recv_clr_vlink(fip, fiph);
+	kfree_skb(skb);
+	return 0;
+drop:
+	kfree_skb(skb);
+	return -1;
+}
+
+/**
+ * fcoe_ctlr_select() - Select the best FCF, if possible.
+ * @fip:	FCoE controller.
+ *
+ * If there are conflicting advertisements, no FCF can be chosen.
+ *
+ * Called with lock held.
+ */
+static void fcoe_ctlr_select(struct fcoe_ctlr *fip)
+{
+	struct fcoe_fcf *fcf;
+	struct fcoe_fcf *best = NULL;
+
+	list_for_each_entry(fcf, &fip->fcfs, list) {
+		FIP_DBG("consider FCF for fab %llx VFID %d map %x val %d\n",
+			fcf->fabric_name, fcf->vfid,
+			fcf->fc_map, fcoe_ctlr_mtu_valid(fcf));
+		if (!fcoe_ctlr_fcf_usable(fcf)) {
+			FIP_DBG("FCF for fab %llx map %x %svalid %savailable\n",
+				fcf->fabric_name, fcf->fc_map,
+				(fcf->flags & FIP_FL_SOL) ? "" : "in",
+				(fcf->flags & FIP_FL_AVAIL) ? "" : "un");
+			continue;
+		}
+		if (!best) {
+			best = fcf;
+			continue;
+		}
+		if (fcf->fabric_name != best->fabric_name ||
+		    fcf->vfid != best->vfid ||
+		    fcf->fc_map != best->fc_map) {
+			FIP_DBG("conflicting fabric, VFID, or FC-MAP\n");
+			return;
+		}
+		if (fcf->pri < best->pri)
+			best = fcf;
+	}
+	fip->sel_fcf = best;
+}
+
+/**
+ * fcoe_ctlr_timeout() - FIP timer function.
+ * @arg:	&fcoe_ctlr pointer.
+ *
+ * Ages FCFs.  Triggers FCF selection if possible.  Sends keep-alives.
+ */
+static void fcoe_ctlr_timeout(unsigned long arg)
+{
+	struct fcoe_ctlr *fip = (struct fcoe_ctlr *)arg;
+	struct fcoe_fcf *sel;
+	struct fcoe_fcf *fcf;
+	unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
+	DECLARE_MAC_BUF(buf);
+	u8 send_ctlr_ka;
+	u8 send_port_ka;
+
+	spin_lock_bh(&fip->lock);
+	if (fip->state == FIP_ST_DISABLED) {
+		spin_unlock_bh(&fip->lock);
+		return;
+	}
+
+	fcf = fip->sel_fcf;
+	fcoe_ctlr_age_fcfs(fip);
+
+	sel = fip->sel_fcf;
+	if (!sel && fip->sel_time && time_after_eq(jiffies, fip->sel_time)) {
+		fcoe_ctlr_select(fip);
+		sel = fip->sel_fcf;
+		fip->sel_time = 0;
+	}
+
+	if (sel != fcf) {
+		fcf = sel;		/* the old FCF may have been freed */
+		if (sel) {
+			printk(KERN_INFO "host%d: FIP selected "
+			       "Fibre-Channel Forwarder MAC %s\n",
+			       fip->lp->host->host_no,
+			       print_mac(buf, sel->fcf_mac));
+			memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
+			fip->port_ka_time = jiffies +
+					    msecs_to_jiffies(FIP_VN_KA_PERIOD);
+			fip->ctlr_ka_time = jiffies + sel->fka_period;
+			fip->link = 1;
+		} else {
+			printk(KERN_NOTICE "host%d: "
+			       "FIP Fibre-Channel Forwarder timed out.  "
+			       "Starting FCF discovery.\n",
+			       fip->lp->host->host_no);
+			fip->link = 0;
+		}
+		schedule_work(&fip->link_work);
+	}
+
+	send_ctlr_ka = 0;
+	send_port_ka = 0;
+	if (sel) {
+		if (time_after_eq(jiffies, fip->ctlr_ka_time)) {
+			fip->ctlr_ka_time = jiffies + sel->fka_period;
+			send_ctlr_ka = 1;
+		}
+		if (time_after(next_timer, fip->ctlr_ka_time))
+			next_timer = fip->ctlr_ka_time;
+
+		if (time_after_eq(jiffies, fip->port_ka_time)) {
+			fip->port_ka_time += jiffies +
+					msecs_to_jiffies(FIP_VN_KA_PERIOD);
+			send_port_ka = 1;
+		}
+		if (time_after(next_timer, fip->port_ka_time))
+			next_timer = fip->port_ka_time;
+		mod_timer(&fip->timer, next_timer);
+	} else if (fip->sel_time) {
+		next_timer = fip->sel_time +
+				msecs_to_jiffies(FCOE_CTLR_START_DELAY);
+		mod_timer(&fip->timer, next_timer);
+	}
+	spin_unlock_bh(&fip->lock);
+
+	if (send_ctlr_ka)
+		fcoe_ctlr_send_keep_alive(fip, 0, fip->ctl_src_addr);
+	if (send_port_ka)
+		fcoe_ctlr_send_keep_alive(fip, 1, fip->data_src_addr);
+}
+
+/**
+ * fcoe_ctlr_link_work() - worker thread function for link changes.
+ * @work:	pointer to link_work member inside &fcoe_ctlr.
+ *
+ * See if the link status has changed and if so, report it.
+ *
+ * This is here because fc_linkup() and fc_linkdown() must not
+ * be called from the timer directly, since they use a mutex.
+ */
+static void fcoe_ctlr_link_work(struct work_struct *work)
+{
+	struct fcoe_ctlr *fip;
+	int link;
+	int last_link;
+
+	fip = container_of(work, struct fcoe_ctlr, link_work);
+	spin_lock_bh(&fip->lock);
+	last_link = fip->last_link;
+	link = fip->link;
+	fip->last_link = link;
+	spin_unlock_bh(&fip->lock);
+
+	if (last_link != link) {
+		if (link)
+			fc_linkup(fip->lp);
+		else
+			fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT);
+	}
+}
+
+/**
+ * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames.
+ * @recv_work:	pointer to recv_work member inside &fcoe_ctlr.
+ */
+static void fcoe_ctlr_recv_work(struct work_struct *recv_work)
+{
+	struct fcoe_ctlr *fip;
+	struct sk_buff *skb;
+
+	fip = container_of(recv_work, struct fcoe_ctlr, recv_work);
+	spin_lock_bh(&fip->fip_recv_list.lock);
+	while ((skb = __skb_dequeue(&fip->fip_recv_list))) {
+		spin_unlock_bh(&fip->fip_recv_list.lock);
+		fcoe_ctlr_recv_handler(fip, skb);
+		spin_lock_bh(&fip->fip_recv_list.lock);
+	}
+	spin_unlock_bh(&fip->fip_recv_list.lock);
+}
+
+/**
+ * fcoe_ctlr_recv_flogi() - snoop Pre-FIP receipt of FLOGI response or request.
+ * @fip:	FCoE controller.
+ * @fp:		FC frame.
+ * @sa:		Ethernet source MAC address from received FCoE frame.
+ *
+ * Snoop potential response to FLOGI or even incoming FLOGI.
+ *
+ * The caller has checked that we are waiting for login as indicated
+ * by fip->flogi_oxid != FC_XID_UNKNOWN.
+ *
+ * The caller is responsible for freeing the frame.
+ *
+ * Return non-zero if the frame should not be delivered to libfc.
+ */
+int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
+{
+	struct fc_frame_header *fh;
+	u8 op;
+	u8 mac[ETH_ALEN];
+
+	fh = fc_frame_header_get(fp);
+	if (fh->fh_type != FC_TYPE_ELS)
+		return 0;
+
+	op = fc_frame_payload_op(fp);
+	if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
+	    fip->flogi_oxid == ntohs(fh->fh_ox_id)) {
+
+		spin_lock_bh(&fip->lock);
+		if (fip->state != FIP_ST_AUTO && fip->state != FIP_ST_NON_FIP) {
+			spin_unlock_bh(&fip->lock);
+			return -EINVAL;
+		}
+		fip->state = FIP_ST_NON_FIP;
+		FIP_DBG("received FLOGI LS_ACC using non-FIP mode\n");
+
+		/*
+		 * FLOGI accepted.
+		 * If the src mac addr is FC_OUI-based, then we mark the
+		 * address_mode flag to use FC_OUI-based Ethernet DA.
+		 * Otherwise we use the FCoE gateway addr
+		 */
+		if (!compare_ether_addr(sa, (u8[6])FC_FCOE_FLOGI_MAC)) {
+			fip->map_dest = 1;
+		} else {
+			memcpy(fip->dest_addr, sa, ETH_ALEN);
+			fip->map_dest = 0;
+		}
+		fip->flogi_oxid = FC_XID_UNKNOWN;
+		memcpy(mac, fip->data_src_addr, ETH_ALEN);
+		fc_fcoe_set_mac(fip->data_src_addr, fh->fh_d_id);
+		spin_unlock_bh(&fip->lock);
+
+		fip->update_mac(fip, mac, fip->data_src_addr);
+	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
+		/*
+		 * Save source MAC for point-to-point responses.
+		 */
+		spin_lock_bh(&fip->lock);
+		if (fip->state == FIP_ST_AUTO || fip->state == FIP_ST_NON_FIP) {
+			memcpy(fip->dest_addr, sa, ETH_ALEN);
+			fip->map_dest = 0;
+			if (fip->state == FIP_ST_NON_FIP)
+				FIP_DBG("received FLOGI REQ, "
+						"using non-FIP mode\n");
+			fip->state = FIP_ST_NON_FIP;
+		}
+		spin_unlock_bh(&fip->lock);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(fcoe_ctlr_recv_flogi);
+
 /**
  * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN.
  * @mac: mac address
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index d07ebe6..666cc13 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -1,5 +1,6 @@
 /*
- * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ * Copyright (c) 2008-2009 Cisco Systems, Inc.  All rights reserved.
+ * Copyright (c) 2007-2008 Intel Corporation.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -20,11 +21,142 @@
 #ifndef _LIBFCOE_H
 #define _LIBFCOE_H
 
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/workqueue.h>
 #include <scsi/fc/fc_fcoe.h>
 #include <scsi/libfc.h>
 
+/*
+ * FIP tunable parameters.
+ */
+#define FCOE_CTLR_START_DELAY	2000	/* mS after first adv. to choose FCF */
+#define FCOE_CTRL_SOL_TOV	2000	/* min. solicitation interval (mS) */
+#define FCOE_CTLR_FCF_LIMIT	20	/* max. number of FCF entries */
+
+/**
+ * enum fip_state - internal state of FCoE controller.
+ * @FIP_ST_DISABLED: 	controller has been disabled or not yet enabled.
+ * @FIP_ST_LINK_WAIT:	the physical link is down or unusable.
+ * @FIP_ST_AUTO:	determining whether to use FIP or non-FIP mode.
+ * @FIP_ST_NON_FIP:	non-FIP mode selected.
+ * @FIP_ST_ENABLED:	FIP mode selected.
+ */
+enum fip_state {
+	FIP_ST_DISABLED,
+	FIP_ST_LINK_WAIT,
+	FIP_ST_AUTO,
+	FIP_ST_NON_FIP,
+	FIP_ST_ENABLED,
+};
+
+/**
+ * struct fcoe_ctlr - FCoE Controller and FIP state.
+ * @state:	internal FIP state for network link and FIP or non-FIP mode.
+ * @lp:		&fc_lport: libfc local port.
+ * @sel_fcf:	currently selected FCF, or NULL.
+ * @fcfs:	list of discovered FCFs.
+ * @fcf_count:	number of discovered FCF entries.
+ * @sol_time:	time when a multicast solicitation was last sent.
+ * @sel_time:	time after which to select an FCF.
+ * @port_ka_time: time of next port keep-alive.
+ * @ctlr_ka_time: time of next controller keep-alive.
+ * @timer:	timer struct used for all delayed events.
+ * @link_work:	&work_struct for doing FCF selection.
+ * @recv_work:	&work_struct for receiving FIP frames.
+ * @fip_recv_list: list of received FIP frames.
+ * @user_mfs:	configured maximum FC frame size, including FC header.
+ * @flogi_oxid: exchange ID of most recent fabric login.
+ * @flogi_count: number of FLOGI attempts in AUTO mode.
+ * @link:	current link status for libfc.
+ * @last_link:	last link state reported to libfc.
+ * @map_dest:	use the FC_MAP mode for destination MAC addresses.
+ * @dest_addr:	MAC address of the selected FC forwarder.
+ * @ctl_src_addr: the native MAC address of our local port.
+ * @data_src_addr: the assigned MAC address for the local port after FLOGI.
+ * @send:	LLD-supplied function to handle sending of FIP Ethernet frames.
+ * @update_mac: LLD-supplied function to handle changes to MAC addresses.
+ * @lock:	lock protecting this structure.
+ *
+ * This structure is used by all FCoE drivers.  It contains information
+ * needed by all FCoE low-level drivers (LLDs) as well as internal state
+ * for FIP, and fields shared with the LLDS.
+ */
+struct fcoe_ctlr {
+	enum fip_state state;
+	struct fc_lport *lp;
+	struct fcoe_fcf *sel_fcf;
+	struct list_head fcfs;
+	u16 fcf_count;
+	unsigned long sol_time;
+	unsigned long sel_time;
+	unsigned long port_ka_time;
+	unsigned long ctlr_ka_time;
+	struct timer_list timer;
+	struct work_struct link_work;
+	struct work_struct recv_work;
+	struct sk_buff_head fip_recv_list;
+	u16 user_mfs;
+	u16 flogi_oxid;
+	u8 flogi_count;
+	u8 link;
+	u8 last_link;
+	u8 map_dest;
+	u8 dest_addr[ETH_ALEN];
+	u8 ctl_src_addr[ETH_ALEN];
+	u8 data_src_addr[ETH_ALEN];
+
+	void (*send)(struct fcoe_ctlr *, struct sk_buff *);
+	void (*update_mac)(struct fcoe_ctlr *, u8 *old, u8 *new);
+	spinlock_t lock;
+};
+
+/*
+ * struct fcoe_fcf - Fibre-Channel Forwarder.
+ * @list:	list linkage.
+ * @time:	system time (jiffies) when an advertisement was last received.
+ * @switch_name: WWN of switch from advertisement.
+ * @fabric_name: WWN of fabric from advertisement.
+ * @fc_map:	FC_MAP value from advertisement.
+ * @fcf_mac:	Ethernet address of the FCF.
+ * @vfid:	virtual fabric ID.
+ * @pri:	seletion priority, smaller values are better.
+ * @flags:	flags received from advertisement.
+ * @fka_period:	keep-alive period, in jiffies.
+ *
+ * A Fibre-Channel Forwarder (FCF) is the entity on the Ethernet that
+ * passes FCoE frames on to an FC fabric.  This structure represents
+ * one FCF from which advertisements have been received.
+ *
+ * When looking up an FCF, @switch_name, @fabric_name, @fc_map, @vfid, and
+ * @fcf_mac together form the lookup key.
+ */
+struct fcoe_fcf {
+	struct list_head list;
+	unsigned long time;
+
+	u64 switch_name;
+	u64 fabric_name;
+	u32 fc_map;
+	u16 vfid;
+	u8 fcf_mac[ETH_ALEN];
+
+	u8 pri;
+	u16 flags;
+	u32 fka_period;
+};
+
+/* FIP API functions */
+void fcoe_ctlr_init(struct fcoe_ctlr *);
+void fcoe_ctlr_destroy(struct fcoe_ctlr *);
+void fcoe_ctlr_link_up(struct fcoe_ctlr *);
+int fcoe_ctlr_link_down(struct fcoe_ctlr *);
+int fcoe_ctlr_els_send(struct fcoe_ctlr *, struct sk_buff *);
+void fcoe_ctlr_recv(struct fcoe_ctlr *, struct sk_buff *);
+int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *, struct fc_frame *fp, u8 *sa);
+
 /* libfcoe funcs */
 u64 fcoe_wwn_from_mac(unsigned char mac[], unsigned int, unsigned int);
 int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH 04/14] fcoe: create/destroy fcoe Rx threads on CPU hotplug events
  2009-03-17 18:41 ` [PATCH 04/14] fcoe: create/destroy fcoe Rx threads on CPU hotplug events Robert Love
@ 2009-03-23  0:59   ` James Bottomley
  2009-03-23 19:42     ` Robert Love
  0 siblings, 1 reply; 30+ messages in thread
From: James Bottomley @ 2009-03-23  0:59 UTC (permalink / raw)
  To: Robert Love; +Cc: linux-scsi

On Tue, 2009-03-17 at 11:41 -0700, Robert Love wrote:
> This patch adds support for dynamically created Rx threads
> upon CPU hotplug events.
> 
> There were existing synchronization problems that this patch
> attempts to resolve. The main problem had to do with fcoe_rcv()
> running in a different context than the hotplug notifications.
> This opened the possiblity that fcoe_rcv() would target a Rx
> thread for a skb. However, that thread could become NULL if
> the CPU was made offline.
> 
> This patch uses the Rx queue's (a skb_queue) lock to protect
> the thread it's associated with and we use the 'thread' member
> of the fcoe_percpu_s to determine if the thread is ready to
> accept new skbs.
> 
> The patch also attempts to do a better job of cleaning up, both
> if hotplug registration fails as well as when the module is
> removed.
> 
> Contribution provided by Joe Eykholt <jeykholt@cisco.com> to
> fix incorrect use of __cpuinitdata.
> 
> Signed-off-by: Yi Zou <yi.zou@intel.com>
> Signed-off-by: Robert Love <robert.w.love@intel.com>
> ---
> 
>  drivers/scsi/fcoe/libfcoe.c |  246 +++++++++++++++++++++++++++++++++++--------
>  1 files changed, 199 insertions(+), 47 deletions(-)
> 
> diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
> index 648a2fc..951d244 100644
> --- a/drivers/scsi/fcoe/libfcoe.c
> +++ b/drivers/scsi/fcoe/libfcoe.c
> @@ -81,6 +81,156 @@ static struct notifier_block fcoe_notifier = {
>  };
>  
>  /**
> + * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
> + * @cpu: cpu index for the online cpu
> + */
> +static void fcoe_percpu_thread_create(unsigned int cpu)
> +{
> +	struct fcoe_percpu_s *p;
> +	struct task_struct *thread;
> +
> +	p = &per_cpu(fcoe_percpu, cpu);
> +
> +	thread = kthread_create(fcoe_percpu_receive_thread,
> +				(void *)p, "fcoethread/%d", cpu);
> +
> +	if (likely(!IS_ERR(p->thread))) {
> +		kthread_bind(thread, cpu);
> +		wake_up_process(thread);
> +
> +		spin_lock_bh(&p->fcoe_rx_list.lock);
> +		p->thread = thread;
> +		spin_unlock_bh(&p->fcoe_rx_list.lock);
> +	}
> +}
> +
> +/**
> + * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
> + * @cpu: cpu index the rx thread is to be removed
> + *
> + * Destroys a per-CPU Rx thread. Any pending skbs are moved to the
> + * current CPU's Rx thread. If the thread being destroyed is bound to
> + * the CPU processing this context the skbs will be freed.
> + */
> +static void fcoe_percpu_thread_destroy(unsigned int cpu)
> +{
> +	struct fcoe_percpu_s *p;
> +	struct task_struct *thread;
> +	struct page *crc_eof;
> +	struct sk_buff *skb;
> +#ifdef CONFIG_SMP
> +	struct fcoe_percpu_s *p0;
> +	unsigned targ_cpu = smp_processor_id();
> +#endif /* CONFIG_SMP */
> +
> +	printk(KERN_DEBUG "fcoe: Destroying receive thread for CPU %d\n", cpu);
> +
> +	/* Prevent any new skbs from being queued for this CPU. */
> +	p = &per_cpu(fcoe_percpu, cpu);
> +	spin_lock_bh(&p->fcoe_rx_list.lock);
> +	thread = p->thread;
> +	p->thread = NULL;
> +	crc_eof = p->crc_eof_page;
> +	p->crc_eof_page = NULL;
> +	p->crc_eof_offset = 0;
> +	spin_unlock_bh(&p->fcoe_rx_list.lock);
> +
> +#ifdef CONFIG_SMP
> +	/*
> +	 * Don't bother moving the skb's if this context is running
> +	 * on the same CPU that is having its thread destroyed. This
> +	 * can easily happen when the module is removed.
> +	 */
> +	if (cpu != targ_cpu) {
> +		p0 = &per_cpu(fcoe_percpu, targ_cpu);
> +		spin_lock_bh(&p0->fcoe_rx_list.lock);
> +		if (p0->thread) {
> +			FC_DBG("Moving frames from CPU %d to CPU %d\n",
> +			       cpu, targ_cpu);
> +
> +			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
> +				__skb_queue_tail(&p0->fcoe_rx_list, skb);
> +			spin_unlock_bh(&p0->fcoe_rx_list.lock);
> +		} else {
> +			/*
> +			 * The targeted CPU is not initialized and cannot accept
> +			 * new  skbs. Unlock the targeted CPU and drop the skbs
> +			 * on the CPU that is going offline.
> +			 */
> +			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
> +				kfree_skb(skb);
> +			spin_unlock_bh(&p0->fcoe_rx_list.lock);
> +		}
> +	} else {
> +		/*
> +		 * This scenario occurs when the module is being removed
> +		 * and all threads are being destroyed. skbs will continue
> +		 * to be shifted from the CPU thread that is being removed
> +		 * to the CPU thread associated with the CPU that is processing
> +		 * the module removal. Once there is only one CPU Rx thread it
> +		 * will reach this case and we will drop all skbs and later
> +		 * stop the thread.
> +		 */
> +		spin_lock_bh(&p->fcoe_rx_list.lock);
> +		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
> +			kfree_skb(skb);
> +		spin_unlock_bh(&p->fcoe_rx_list.lock);
> +	}
> +#else
> +	/*
> +	 * This a non-SMP scenario where the singluar Rx thread is
> +	 * being removed. Free all skbs and stop the thread.
> +	 */
> +	spin_lock_bh(&p->fcoe_rx_list.lock);
> +	while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
> +		kfree_skb(skb);
> +	spin_unlock_bh(&p->fcoe_rx_list.lock);
> +#endif
> +
> +	if (thread)
> +		kthread_stop(thread);

Don't you need a kthread_unbind() somewhere in here?  Under most of your
calling conditions (CPU_DEAD events) the bound CPU is toast, so the
thread isn't going to be able to wake up on it.

James



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 04/14] fcoe: create/destroy fcoe Rx threads on CPU hotplug events
  2009-03-23  0:59   ` James Bottomley
@ 2009-03-23 19:42     ` Robert Love
  2009-03-24  3:50       ` James Bottomley
  0 siblings, 1 reply; 30+ messages in thread
From: Robert Love @ 2009-03-23 19:42 UTC (permalink / raw)
  To: James Bottomley; +Cc: linux-scsi

On Sun, 2009-03-22 at 17:59 -0700, James Bottomley wrote: 
> On Tue, 2009-03-17 at 11:41 -0700, Robert Love wrote:
> > This patch adds support for dynamically created Rx threads
> > upon CPU hotplug events.
> > 
> > There were existing synchronization problems that this patch
> > attempts to resolve. The main problem had to do with fcoe_rcv()
> > running in a different context than the hotplug notifications.
> > This opened the possiblity that fcoe_rcv() would target a Rx
> > thread for a skb. However, that thread could become NULL if
> > the CPU was made offline.
> > 
> > This patch uses the Rx queue's (a skb_queue) lock to protect
> > the thread it's associated with and we use the 'thread' member
> > of the fcoe_percpu_s to determine if the thread is ready to
> > accept new skbs.
> > 
> > The patch also attempts to do a better job of cleaning up, both
> > if hotplug registration fails as well as when the module is
> > removed.
> > 
> > Contribution provided by Joe Eykholt <jeykholt@cisco.com> to
> > fix incorrect use of __cpuinitdata.
> > 
> > Signed-off-by: Yi Zou <yi.zou@intel.com>
> > Signed-off-by: Robert Love <robert.w.love@intel.com>
> > ---
> > 
> >  drivers/scsi/fcoe/libfcoe.c |  246 +++++++++++++++++++++++++++++++++++--------
> >  1 files changed, 199 insertions(+), 47 deletions(-)
> > 
> > diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
> > index 648a2fc..951d244 100644
> > --- a/drivers/scsi/fcoe/libfcoe.c
> > +++ b/drivers/scsi/fcoe/libfcoe.c
> > @@ -81,6 +81,156 @@ static struct notifier_block fcoe_notifier = {
> >  };
> >  
> >  /**
> > + * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
> > + * @cpu: cpu index for the online cpu
> > + */
> > +static void fcoe_percpu_thread_create(unsigned int cpu)
> > +{
> > +	struct fcoe_percpu_s *p;
> > +	struct task_struct *thread;
> > +
> > +	p = &per_cpu(fcoe_percpu, cpu);
> > +
> > +	thread = kthread_create(fcoe_percpu_receive_thread,
> > +				(void *)p, "fcoethread/%d", cpu);
> > +
> > +	if (likely(!IS_ERR(p->thread))) {
> > +		kthread_bind(thread, cpu);
> > +		wake_up_process(thread);
> > +
> > +		spin_lock_bh(&p->fcoe_rx_list.lock);
> > +		p->thread = thread;
> > +		spin_unlock_bh(&p->fcoe_rx_list.lock);
> > +	}
> > +}
> > +
> > +/**
> > + * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
> > + * @cpu: cpu index the rx thread is to be removed
> > + *
> > + * Destroys a per-CPU Rx thread. Any pending skbs are moved to the
> > + * current CPU's Rx thread. If the thread being destroyed is bound to
> > + * the CPU processing this context the skbs will be freed.
> > + */
> > +static void fcoe_percpu_thread_destroy(unsigned int cpu)
> > +{
> > +	struct fcoe_percpu_s *p;
> > +	struct task_struct *thread;
> > +	struct page *crc_eof;
> > +	struct sk_buff *skb;
> > +#ifdef CONFIG_SMP
> > +	struct fcoe_percpu_s *p0;
> > +	unsigned targ_cpu = smp_processor_id();
> > +#endif /* CONFIG_SMP */
> > +
> > +	printk(KERN_DEBUG "fcoe: Destroying receive thread for CPU %d\n", cpu);
> > +
> > +	/* Prevent any new skbs from being queued for this CPU. */
> > +	p = &per_cpu(fcoe_percpu, cpu);
> > +	spin_lock_bh(&p->fcoe_rx_list.lock);
> > +	thread = p->thread;
> > +	p->thread = NULL;
> > +	crc_eof = p->crc_eof_page;
> > +	p->crc_eof_page = NULL;
> > +	p->crc_eof_offset = 0;
> > +	spin_unlock_bh(&p->fcoe_rx_list.lock);
> > +
> > +#ifdef CONFIG_SMP
> > +	/*
> > +	 * Don't bother moving the skb's if this context is running
> > +	 * on the same CPU that is having its thread destroyed. This
> > +	 * can easily happen when the module is removed.
> > +	 */
> > +	if (cpu != targ_cpu) {
> > +		p0 = &per_cpu(fcoe_percpu, targ_cpu);
> > +		spin_lock_bh(&p0->fcoe_rx_list.lock);
> > +		if (p0->thread) {
> > +			FC_DBG("Moving frames from CPU %d to CPU %d\n",
> > +			       cpu, targ_cpu);
> > +
> > +			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
> > +				__skb_queue_tail(&p0->fcoe_rx_list, skb);
> > +			spin_unlock_bh(&p0->fcoe_rx_list.lock);
> > +		} else {
> > +			/*
> > +			 * The targeted CPU is not initialized and cannot accept
> > +			 * new  skbs. Unlock the targeted CPU and drop the skbs
> > +			 * on the CPU that is going offline.
> > +			 */
> > +			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
> > +				kfree_skb(skb);
> > +			spin_unlock_bh(&p0->fcoe_rx_list.lock);
> > +		}
> > +	} else {
> > +		/*
> > +		 * This scenario occurs when the module is being removed
> > +		 * and all threads are being destroyed. skbs will continue
> > +		 * to be shifted from the CPU thread that is being removed
> > +		 * to the CPU thread associated with the CPU that is processing
> > +		 * the module removal. Once there is only one CPU Rx thread it
> > +		 * will reach this case and we will drop all skbs and later
> > +		 * stop the thread.
> > +		 */
> > +		spin_lock_bh(&p->fcoe_rx_list.lock);
> > +		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
> > +			kfree_skb(skb);
> > +		spin_unlock_bh(&p->fcoe_rx_list.lock);
> > +	}
> > +#else
> > +	/*
> > +	 * This a non-SMP scenario where the singluar Rx thread is
> > +	 * being removed. Free all skbs and stop the thread.
> > +	 */
> > +	spin_lock_bh(&p->fcoe_rx_list.lock);
> > +	while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
> > +		kfree_skb(skb);
> > +	spin_unlock_bh(&p->fcoe_rx_list.lock);
> > +#endif
> > +
> > +	if (thread)
> > +		kthread_stop(thread);
> 
> Don't you need a kthread_unbind() somewhere in here?  Under most of your
> calling conditions (CPU_DEAD events) the bound CPU is toast, so the
> thread isn't going to be able to wake up on it.
> 
Is there a kthread_unbind()? I can't find it, maybe it was removed
recently.

This function should be moving all work off of the CPU that's going away
and should also prevent any new work from being scheduled to it (by
setting p->thread = NULL), so I'm not expecting this thread to wake up
and do something.



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 04/14] fcoe: create/destroy fcoe Rx threads on CPU hotplug events
  2009-03-23 19:42     ` Robert Love
@ 2009-03-24  3:50       ` James Bottomley
  0 siblings, 0 replies; 30+ messages in thread
From: James Bottomley @ 2009-03-24  3:50 UTC (permalink / raw)
  To: Robert Love; +Cc: linux-scsi

On Mon, 2009-03-23 at 12:42 -0700, Robert Love wrote:
> On Sun, 2009-03-22 at 17:59 -0700, James Bottomley wrote: 
> > On Tue, 2009-03-17 at 11:41 -0700, Robert Love wrote:
> > > This patch adds support for dynamically created Rx threads
> > > upon CPU hotplug events.
> > > 
> > > There were existing synchronization problems that this patch
> > > attempts to resolve. The main problem had to do with fcoe_rcv()
> > > running in a different context than the hotplug notifications.
> > > This opened the possiblity that fcoe_rcv() would target a Rx
> > > thread for a skb. However, that thread could become NULL if
> > > the CPU was made offline.
> > > 
> > > This patch uses the Rx queue's (a skb_queue) lock to protect
> > > the thread it's associated with and we use the 'thread' member
> > > of the fcoe_percpu_s to determine if the thread is ready to
> > > accept new skbs.
> > > 
> > > The patch also attempts to do a better job of cleaning up, both
> > > if hotplug registration fails as well as when the module is
> > > removed.
> > > 
> > > Contribution provided by Joe Eykholt <jeykholt@cisco.com> to
> > > fix incorrect use of __cpuinitdata.
> > > 
> > > Signed-off-by: Yi Zou <yi.zou@intel.com>
> > > Signed-off-by: Robert Love <robert.w.love@intel.com>
> > > ---
> > > 
> > >  drivers/scsi/fcoe/libfcoe.c |  246 +++++++++++++++++++++++++++++++++++--------
> > >  1 files changed, 199 insertions(+), 47 deletions(-)
> > > 
> > > diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
> > > index 648a2fc..951d244 100644
> > > --- a/drivers/scsi/fcoe/libfcoe.c
> > > +++ b/drivers/scsi/fcoe/libfcoe.c
> > > @@ -81,6 +81,156 @@ static struct notifier_block fcoe_notifier = {
> > >  };
> > >  
> > >  /**
> > > + * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
> > > + * @cpu: cpu index for the online cpu
> > > + */
> > > +static void fcoe_percpu_thread_create(unsigned int cpu)
> > > +{
> > > +	struct fcoe_percpu_s *p;
> > > +	struct task_struct *thread;
> > > +
> > > +	p = &per_cpu(fcoe_percpu, cpu);
> > > +
> > > +	thread = kthread_create(fcoe_percpu_receive_thread,
> > > +				(void *)p, "fcoethread/%d", cpu);
> > > +
> > > +	if (likely(!IS_ERR(p->thread))) {
> > > +		kthread_bind(thread, cpu);
> > > +		wake_up_process(thread);
> > > +
> > > +		spin_lock_bh(&p->fcoe_rx_list.lock);
> > > +		p->thread = thread;
> > > +		spin_unlock_bh(&p->fcoe_rx_list.lock);
> > > +	}
> > > +}
> > > +
> > > +/**
> > > + * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
> > > + * @cpu: cpu index the rx thread is to be removed
> > > + *
> > > + * Destroys a per-CPU Rx thread. Any pending skbs are moved to the
> > > + * current CPU's Rx thread. If the thread being destroyed is bound to
> > > + * the CPU processing this context the skbs will be freed.
> > > + */
> > > +static void fcoe_percpu_thread_destroy(unsigned int cpu)
> > > +{
> > > +	struct fcoe_percpu_s *p;
> > > +	struct task_struct *thread;
> > > +	struct page *crc_eof;
> > > +	struct sk_buff *skb;
> > > +#ifdef CONFIG_SMP
> > > +	struct fcoe_percpu_s *p0;
> > > +	unsigned targ_cpu = smp_processor_id();
> > > +#endif /* CONFIG_SMP */
> > > +
> > > +	printk(KERN_DEBUG "fcoe: Destroying receive thread for CPU %d\n", cpu);
> > > +
> > > +	/* Prevent any new skbs from being queued for this CPU. */
> > > +	p = &per_cpu(fcoe_percpu, cpu);
> > > +	spin_lock_bh(&p->fcoe_rx_list.lock);
> > > +	thread = p->thread;
> > > +	p->thread = NULL;
> > > +	crc_eof = p->crc_eof_page;
> > > +	p->crc_eof_page = NULL;
> > > +	p->crc_eof_offset = 0;
> > > +	spin_unlock_bh(&p->fcoe_rx_list.lock);
> > > +
> > > +#ifdef CONFIG_SMP
> > > +	/*
> > > +	 * Don't bother moving the skb's if this context is running
> > > +	 * on the same CPU that is having its thread destroyed. This
> > > +	 * can easily happen when the module is removed.
> > > +	 */
> > > +	if (cpu != targ_cpu) {
> > > +		p0 = &per_cpu(fcoe_percpu, targ_cpu);
> > > +		spin_lock_bh(&p0->fcoe_rx_list.lock);
> > > +		if (p0->thread) {
> > > +			FC_DBG("Moving frames from CPU %d to CPU %d\n",
> > > +			       cpu, targ_cpu);
> > > +
> > > +			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
> > > +				__skb_queue_tail(&p0->fcoe_rx_list, skb);
> > > +			spin_unlock_bh(&p0->fcoe_rx_list.lock);
> > > +		} else {
> > > +			/*
> > > +			 * The targeted CPU is not initialized and cannot accept
> > > +			 * new  skbs. Unlock the targeted CPU and drop the skbs
> > > +			 * on the CPU that is going offline.
> > > +			 */
> > > +			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
> > > +				kfree_skb(skb);
> > > +			spin_unlock_bh(&p0->fcoe_rx_list.lock);
> > > +		}
> > > +	} else {
> > > +		/*
> > > +		 * This scenario occurs when the module is being removed
> > > +		 * and all threads are being destroyed. skbs will continue
> > > +		 * to be shifted from the CPU thread that is being removed
> > > +		 * to the CPU thread associated with the CPU that is processing
> > > +		 * the module removal. Once there is only one CPU Rx thread it
> > > +		 * will reach this case and we will drop all skbs and later
> > > +		 * stop the thread.
> > > +		 */
> > > +		spin_lock_bh(&p->fcoe_rx_list.lock);
> > > +		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
> > > +			kfree_skb(skb);
> > > +		spin_unlock_bh(&p->fcoe_rx_list.lock);
> > > +	}
> > > +#else
> > > +	/*
> > > +	 * This a non-SMP scenario where the singluar Rx thread is
> > > +	 * being removed. Free all skbs and stop the thread.
> > > +	 */
> > > +	spin_lock_bh(&p->fcoe_rx_list.lock);
> > > +	while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
> > > +		kfree_skb(skb);
> > > +	spin_unlock_bh(&p->fcoe_rx_list.lock);
> > > +#endif
> > > +
> > > +	if (thread)
> > > +		kthread_stop(thread);
> > 
> > Don't you need a kthread_unbind() somewhere in here?  Under most of your
> > calling conditions (CPU_DEAD events) the bound CPU is toast, so the
> > thread isn't going to be able to wake up on it.
> > 
> Is there a kthread_unbind()? I can't find it, maybe it was removed
> recently.
> 
> This function should be moving all work off of the CPU that's going away
> and should also prevent any new work from being scheduled to it (by
> setting p->thread = NULL), so I'm not expecting this thread to wake up
> and do something.

Yes, sorry, it looks like the API is kthread_bind(task,
cpumask_any(cpu_online_mask)  ... although some in kernel users do this
and some don't ...  I suspect it's not something that's often tested.

James



^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH] fcoe: prep work to completely remove fc_transport_fcoe code
  2009-03-17 18:41 ` [PATCH 05/14] fcoe: prep work to completely remove fc_transport_fcoe code Robert Love
@ 2009-03-24 23:19   ` Robert Love
  2009-03-26  3:03     ` [PATCH] PM port setting and attached SATA port selector in discover Andy Yan
  2009-03-27 16:03   ` [PATCH] fcoe: prep work to completely remove fc_transport_fcoe code Robert Love
  1 sibling, 1 reply; 30+ messages in thread
From: Robert Love @ 2009-03-24 23:19 UTC (permalink / raw)
  To: james.bottomeley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

The fcoe transport code was added for generic FCoE transport
infrastructure to allow additional offload related module loading
on demand, this is not required anymore after recently added
different offload approach by having offload related func ops
in netdev.

This patch removes fcoe transport related code use, calls functions
directly between existing libfcoe.c and fcoe_sw.c for now, for
example fcoe_sw_destroy and fcoe_sw_create calling.

The fcoe_sw.c and libfcoe.c code will be further consolidated in
later patches and then also the default fcoe sw transport code
file fcoe_sw.c will be completely removed.

The fcoe transport code files are completely removed in next
patch to keep this patch simple for reviewing.

--

This patch is an update to a previous patch. This update
resolves a build error as well as fixes a defect related to
not calling fc_release_transport().

The following updates to the other patches in the series
are just adjustments to the changes this update introduces.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/Makefile  |    3 +--
 drivers/scsi/fcoe/fcoe_sw.c |   37 ++++---------------------------------
 drivers/scsi/fcoe/libfcoe.c |   25 ++++++-------------------
 include/scsi/libfcoe.h      |    2 ++
 4 files changed, 13 insertions(+), 54 deletions(-)

diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index b78da06..e950adf 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -4,5 +4,4 @@ obj-$(CONFIG_FCOE) += fcoe.o
 
 fcoe-y := \
 	libfcoe.o \
-	fcoe_sw.o \
-	fc_transport_fcoe.o
+	fcoe_sw.o
diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c
index a675390..f060220 100644
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -36,7 +36,6 @@
 
 #include <scsi/libfc.h>
 #include <scsi/libfcoe.h>
-#include <scsi/fc_transport_fcoe.h>
 
 #define FCOE_SW_VERSION	"0.1"
 #define	FCOE_SW_NAME	"fcoesw"
@@ -302,7 +301,7 @@ static inline int fcoe_sw_em_config(struct fc_lport *lp)
  *
  * Returns: 0 if link is OK for use by FCoE.
  */
-static int fcoe_sw_destroy(struct net_device *netdev)
+int fcoe_sw_destroy(struct net_device *netdev)
 {
 	struct fc_lport *lp = NULL;
 	struct fcoe_softc *fc;
@@ -415,7 +414,7 @@ static struct libfc_function_template fcoe_sw_libfc_fcn_templ = {
  *
  * Returns : 0 on success
  */
-static int fcoe_sw_create(struct net_device *netdev)
+int fcoe_sw_create(struct net_device *netdev)
 {
 	int rc;
 	struct fc_lport *lp = NULL;
@@ -494,28 +493,7 @@ out_host_put:
 }
 
 /**
- * fcoe_sw_match() - The FCoE SW transport match function
- *
- * Returns : false always
- */
-static bool fcoe_sw_match(struct net_device *netdev)
-{
-	/* FIXME - for sw transport, always return false */
-	return false;
-}
-
-/* the sw hba fcoe transport */
-struct fcoe_transport fcoe_sw_transport = {
-	.name = "fcoesw",
-	.create = fcoe_sw_create,
-	.destroy = fcoe_sw_destroy,
-	.match = fcoe_sw_match,
-	.vendor = 0x0,
-	.device = 0xffff,
-};
-
-/**
- * fcoe_sw_init() - Registers fcoe_sw_transport
+ * fcoe_sw_init() - attach to scsi transport
  *
  * Returns : 0 on success
  */
@@ -530,23 +508,16 @@ int __init fcoe_sw_init(void)
 		return -ENODEV;
 	}
 
-	mutex_init(&fcoe_sw_transport.devlock);
-	INIT_LIST_HEAD(&fcoe_sw_transport.devlist);
-
-	/* register sw transport */
-	fcoe_transport_register(&fcoe_sw_transport);
 	return 0;
 }
 
 /**
- * fcoe_sw_exit() - Unregisters fcoe_sw_transport
+ * fcoe_sw_exit() - detach from scsi transport
  *
  * Returns : 0 on success
  */
 int __exit fcoe_sw_exit(void)
 {
-	/* dettach the transport */
 	fc_release_transport(scsi_transport_fcoe_sw);
-	fcoe_transport_unregister(&fcoe_sw_transport);
 	return 0;
 }
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 951d244..334db95 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -44,7 +44,6 @@
 #include <scsi/libfc.h>
 #include <scsi/fc_frame.h>
 #include <scsi/libfcoe.h>
-#include <scsi/fc_transport_fcoe.h>
 
 static int debug_fcoe;
 
@@ -1081,10 +1080,9 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
 		rc = -ENODEV;
 		goto out_putdev;
 	}
-	/* pass to transport */
-	rc = fcoe_transport_release(netdev);
+	rc = fcoe_sw_destroy(netdev);
 	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_transport_release(%s) failed\n",
+		printk(KERN_ERR "fcoe: fcoe_sw_destroy(%s) failed\n",
 		       netdev->name);
 		rc = -EIO;
 		goto out_putdev;
@@ -1121,10 +1119,9 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 	}
 	fcoe_ethdrv_get(netdev);
 
-	/* pass to transport */
-	rc = fcoe_transport_attach(netdev);
+	rc = fcoe_sw_create(netdev);
 	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_transport_attach(%s) failed\n",
+		printk(KERN_ERR "fcoe: fcoe_sw_create(%s) failed\n",
 		       netdev->name);
 		fcoe_ethdrv_put(netdev);
 		rc = -EIO;
@@ -1429,10 +1426,6 @@ EXPORT_SYMBOL_GPL(fcoe_libfc_config);
 /**
  * fcoe_init() - fcoe module loading initialization
  *
- * Initialization routine
- * 1. Will create fc transport software structure
- * 2. initialize the link list of port information structure
- *
  * Returns 0 on success, negative on failure
  */
 static int __init fcoe_init(void)
@@ -1464,9 +1457,6 @@ static int __init fcoe_init(void)
 
 	mod_timer(&fcoe_timer, jiffies + (10 * HZ));
 
-	/* initiatlize the fcoe transport */
-	fcoe_transport_init();
-
 	fcoe_sw_init();
 
 	return 0;
@@ -1495,9 +1485,9 @@ static void __exit fcoe_exit(void)
 	/* Stop the timer */
 	del_timer_sync(&fcoe_timer);
 
-	/* releases the associated fcoe transport for each lport */
+	/* releases the associated fcoe hosts */
 	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
-		fcoe_transport_release(fc->real_dev);
+		fcoe_sw_destroy(fc->real_dev);
 
 	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
 
@@ -1507,8 +1497,5 @@ static void __exit fcoe_exit(void)
 
 	/* remove sw trasnport */
 	fcoe_sw_exit();
-
-	/* detach the transport */
-	fcoe_transport_exit();
 }
 module_exit(fcoe_exit);
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index 1ad4f93..dc64405 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -149,4 +149,6 @@ int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
 /* fcoe sw hba */
 int __init fcoe_sw_init(void);
 int __exit fcoe_sw_exit(void);
+int fcoe_sw_create(struct net_device *);
+int fcoe_sw_destroy(struct net_device *);
 #endif /* _LIBFCOE_H */


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH] fcoe: removes fc_transport_fcoe.[ch] code files
  2009-03-17 18:41 ` [PATCH 06/14] fcoe: removes fc_transport_fcoe.[ch] code files Robert Love
@ 2009-03-24 23:24   ` Robert Love
  2009-03-27 16:05   ` Robert Love
  1 sibling, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-24 23:24 UTC (permalink / raw)
  To: james.bottomeley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

Remove unused fc_transport_fcoe.c and fc_transport_fcoe.h
files.
---

 drivers/scsi/fcoe/fc_transport_fcoe.c |  443 ---------------------------------
 include/scsi/fc_transport_fcoe.h      |   54 ----
 2 files changed, 0 insertions(+), 497 deletions(-)
 delete mode 100644 drivers/scsi/fcoe/fc_transport_fcoe.c
 delete mode 100644 include/scsi/fc_transport_fcoe.h

diff --git a/drivers/scsi/fcoe/fc_transport_fcoe.c b/drivers/scsi/fcoe/fc_transport_fcoe.c
deleted file mode 100644
index 8862758..0000000
--- a/drivers/scsi/fcoe/fc_transport_fcoe.c
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Maintained at www.Open-FCoE.org
- */
-
-#include <linux/pci.h>
-#include <scsi/libfcoe.h>
-#include <scsi/fc_transport_fcoe.h>
-
-/* internal fcoe transport */
-struct fcoe_transport_internal {
-	struct fcoe_transport *t;
-	struct net_device *netdev;
-	struct list_head list;
-};
-
-/* fcoe transports list and its lock */
-static LIST_HEAD(fcoe_transports);
-static DEFINE_MUTEX(fcoe_transports_lock);
-
-/**
- * fcoe_transport_default() - Returns ptr to the default transport fcoe_sw
- */
-struct fcoe_transport *fcoe_transport_default(void)
-{
-	return &fcoe_sw_transport;
-}
-
-/**
- * fcoe_transport_to_pcidev() - get the pci dev from a netdev
- * @netdev: the netdev that pci dev will be retrived from
- *
- * Returns: NULL or the corrsponding pci_dev
- */
-struct pci_dev *fcoe_transport_pcidev(const struct net_device *netdev)
-{
-	if (!netdev->dev.parent)
-		return NULL;
-	return to_pci_dev(netdev->dev.parent);
-}
-
-/**
- * fcoe_transport_device_lookup() - Lookup a transport
- * @netdev: the netdev the transport to be attached to
- *
- * This will look for existing offload driver, if not found, it falls back to
- * the default sw hba (fcoe_sw) as its fcoe transport.
- *
- * Returns: 0 for success
- */
-static struct fcoe_transport_internal *
-fcoe_transport_device_lookup(struct fcoe_transport *t,
-			     struct net_device *netdev)
-{
-	struct fcoe_transport_internal *ti;
-
-	/* assign the transpor to this device */
-	mutex_lock(&t->devlock);
-	list_for_each_entry(ti, &t->devlist, list) {
-		if (ti->netdev == netdev) {
-			mutex_unlock(&t->devlock);
-			return ti;
-		}
-	}
-	mutex_unlock(&t->devlock);
-	return NULL;
-}
-/**
- * fcoe_transport_device_add() - Assign a transport to a device
- * @netdev: the netdev the transport to be attached to
- *
- * This will look for existing offload driver, if not found, it falls back to
- * the default sw hba (fcoe_sw) as its fcoe transport.
- *
- * Returns: 0 for success
- */
-static int fcoe_transport_device_add(struct fcoe_transport *t,
-				     struct net_device *netdev)
-{
-	struct fcoe_transport_internal *ti;
-
-	ti = fcoe_transport_device_lookup(t, netdev);
-	if (ti) {
-		printk(KERN_DEBUG "fcoe_transport_device_add:"
-		       "device %s is already added to transport %s\n",
-		       netdev->name, t->name);
-		return -EEXIST;
-	}
-	/* allocate an internal struct to host the netdev and the list */
-	ti = kzalloc(sizeof(*ti), GFP_KERNEL);
-	if (!ti)
-		return -ENOMEM;
-
-	ti->t = t;
-	ti->netdev = netdev;
-	INIT_LIST_HEAD(&ti->list);
-	dev_hold(ti->netdev);
-
-	mutex_lock(&t->devlock);
-	list_add(&ti->list, &t->devlist);
-	mutex_unlock(&t->devlock);
-
-	printk(KERN_DEBUG "fcoe_transport_device_add:"
-		       "device %s added to transport %s\n",
-		       netdev->name, t->name);
-
-	return 0;
-}
-
-/**
- * fcoe_transport_device_remove() - Remove a device from its transport
- * @netdev: the netdev the transport to be attached to
- *
- * This removes the device from the transport so the given transport will
- * not manage this device any more
- *
- * Returns: 0 for success
- */
-static int fcoe_transport_device_remove(struct fcoe_transport *t,
-					struct net_device *netdev)
-{
-	struct fcoe_transport_internal *ti;
-
-	ti = fcoe_transport_device_lookup(t, netdev);
-	if (!ti) {
-		printk(KERN_DEBUG "fcoe_transport_device_remove:"
-		       "device %s is not managed by transport %s\n",
-		       netdev->name, t->name);
-		return -ENODEV;
-	}
-	mutex_lock(&t->devlock);
-	list_del(&ti->list);
-	mutex_unlock(&t->devlock);
-	printk(KERN_DEBUG "fcoe_transport_device_remove:"
-	       "device %s removed from transport %s\n",
-	       netdev->name, t->name);
-	dev_put(ti->netdev);
-	kfree(ti);
-	return 0;
-}
-
-/**
- * fcoe_transport_device_remove_all() - Remove all from transport devlist
- *
- * This removes the device from the transport so the given transport will
- * not manage this device any more
- *
- * Returns: 0 for success
- */
-static void fcoe_transport_device_remove_all(struct fcoe_transport *t)
-{
-	struct fcoe_transport_internal *ti, *tmp;
-
-	mutex_lock(&t->devlock);
-	list_for_each_entry_safe(ti, tmp, &t->devlist, list) {
-		list_del(&ti->list);
-		kfree(ti);
-	}
-	mutex_unlock(&t->devlock);
-}
-
-/**
- * fcoe_transport_match() - Use the bus device match function to match the hw
- * @t: The fcoe transport to check
- * @netdev: The netdev to match against
- *
- * This function is used to check if the given transport wants to manage the
- * input netdev. if the transports implements the match function, it will be
- * called, o.w. we just compare the pci vendor and device id.
- *
- * Returns: true for match up
- */
-static bool fcoe_transport_match(struct fcoe_transport *t,
-				 struct net_device *netdev)
-{
-	/* match transport by vendor and device id */
-	struct pci_dev *pci;
-
-	pci = fcoe_transport_pcidev(netdev);
-
-	if (pci) {
-		printk(KERN_DEBUG "fcoe_transport_match:"
-		       "%s:%x:%x -- %s:%x:%x\n",
-		       t->name, t->vendor, t->device,
-		       netdev->name, pci->vendor, pci->device);
-
-		/* if transport supports match */
-		if (t->match)
-			return t->match(netdev);
-
-		/* else just compare the vendor and device id: pci only */
-		return (t->vendor == pci->vendor) && (t->device == pci->device);
-	}
-	return false;
-}
-
-/**
- * fcoe_transport_lookup() - Check if the transport is already registered
- * @t: the transport to be looked up
- *
- * This compares the parent device (pci) vendor and device id
- *
- * Returns: NULL if not found
- *
- * TODO: return default sw transport if no other transport is found
- */
-static struct fcoe_transport *
-fcoe_transport_lookup(struct net_device *netdev)
-{
-	struct fcoe_transport *t;
-
-	mutex_lock(&fcoe_transports_lock);
-	list_for_each_entry(t, &fcoe_transports, list) {
-		if (fcoe_transport_match(t, netdev)) {
-			mutex_unlock(&fcoe_transports_lock);
-			return t;
-		}
-	}
-	mutex_unlock(&fcoe_transports_lock);
-
-	printk(KERN_DEBUG "fcoe_transport_lookup:"
-	       "use default transport for %s\n", netdev->name);
-	return fcoe_transport_default();
-}
-
-/**
- * fcoe_transport_register() - Adds a fcoe transport to the fcoe transports list
- * @t: ptr to the fcoe transport to be added
- *
- * Returns: 0 for success
- */
-int fcoe_transport_register(struct fcoe_transport *t)
-{
-	struct fcoe_transport *tt;
-
-	/* TODO - add fcoe_transport specific initialization here */
-	mutex_lock(&fcoe_transports_lock);
-	list_for_each_entry(tt, &fcoe_transports, list) {
-		if (tt == t) {
-			mutex_unlock(&fcoe_transports_lock);
-			return -EEXIST;
-		}
-	}
-	list_add_tail(&t->list, &fcoe_transports);
-	mutex_unlock(&fcoe_transports_lock);
-
-	printk(KERN_DEBUG "fcoe_transport_register:%s\n", t->name);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_transport_register);
-
-/**
- * fcoe_transport_unregister() - Remove the tranport fro the fcoe transports list
- * @t: ptr to the fcoe transport to be removed
- *
- * Returns: 0 for success
- */
-int fcoe_transport_unregister(struct fcoe_transport *t)
-{
-	struct fcoe_transport *tt, *tmp;
-
-	mutex_lock(&fcoe_transports_lock);
-	list_for_each_entry_safe(tt, tmp, &fcoe_transports, list) {
-		if (tt == t) {
-			list_del(&t->list);
-			mutex_unlock(&fcoe_transports_lock);
-			fcoe_transport_device_remove_all(t);
-			printk(KERN_DEBUG "fcoe_transport_unregister:%s\n",
-			       t->name);
-			return 0;
-		}
-	}
-	mutex_unlock(&fcoe_transports_lock);
-	return -ENODEV;
-}
-EXPORT_SYMBOL_GPL(fcoe_transport_unregister);
-
-/**
- * fcoe_load_transport_driver() - Load an offload driver by alias name
- * @netdev: the target net device
- *
- * Requests for an offload driver module as the fcoe transport, if fails, it
- * falls back to use the SW HBA (fcoe_sw) as its transport
- *
- * TODO -
- * 	1. supports only PCI device
- * 	2. needs fix for VLAn and bonding
- * 	3. pure hw fcoe hba may not have netdev
- *
- * Returns: 0 for success
- */
-int fcoe_load_transport_driver(struct net_device *netdev)
-{
-	struct pci_dev *pci;
-	struct device *dev = netdev->dev.parent;
-
-	if (fcoe_transport_lookup(netdev)) {
-		/* load default transport */
-		printk(KERN_DEBUG "fcoe: already loaded transport for %s\n",
-		       netdev->name);
-		return -EEXIST;
-	}
-
-	pci = to_pci_dev(dev);
-	if (dev->bus != &pci_bus_type) {
-		printk(KERN_DEBUG "fcoe: support noly PCI device\n");
-		return -ENODEV;
-	}
-	printk(KERN_DEBUG "fcoe: loading driver fcoe-pci-0x%04x-0x%04x\n",
-	       pci->vendor, pci->device);
-
-	return request_module("fcoe-pci-0x%04x-0x%04x",
-			      pci->vendor, pci->device);
-
-}
-EXPORT_SYMBOL_GPL(fcoe_load_transport_driver);
-
-/**
- * fcoe_transport_attach() - Load transport to fcoe
- * @netdev: the netdev the transport to be attached to
- *
- * This will look for existing offload driver, if not found, it falls back to
- * the default sw hba (fcoe_sw) as its fcoe transport.
- *
- * Returns: 0 for success
- */
-int fcoe_transport_attach(struct net_device *netdev)
-{
-	struct fcoe_transport *t;
-
-	/* find the corresponding transport */
-	t = fcoe_transport_lookup(netdev);
-	if (!t) {
-		printk(KERN_DEBUG "fcoe_transport_attach"
-		       ":no transport for %s:use %s\n",
-		       netdev->name, t->name);
-		return -ENODEV;
-	}
-	/* add to the transport */
-	if (fcoe_transport_device_add(t, netdev)) {
-		printk(KERN_DEBUG "fcoe_transport_attach"
-		       ":failed to add %s to tramsport %s\n",
-		       netdev->name, t->name);
-		return -EIO;
-	}
-	/* transport create function */
-	if (t->create)
-		t->create(netdev);
-
-	printk(KERN_DEBUG "fcoe_transport_attach:transport %s for %s\n",
-	       t->name, netdev->name);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_transport_attach);
-
-/**
- * fcoe_transport_release() - Unload transport from fcoe
- * @netdev: the net device on which fcoe is to be released
- *
- * Returns: 0 for success
- */
-int fcoe_transport_release(struct net_device *netdev)
-{
-	struct fcoe_transport *t;
-
-	/* find the corresponding transport */
-	t = fcoe_transport_lookup(netdev);
-	if (!t) {
-		printk(KERN_DEBUG "fcoe_transport_release:"
-		       "no transport for %s:use %s\n",
-		       netdev->name, t->name);
-		return -ENODEV;
-	}
-	/* remove the device from the transport */
-	if (fcoe_transport_device_remove(t, netdev)) {
-		printk(KERN_DEBUG "fcoe_transport_release:"
-		       "failed to add %s to tramsport %s\n",
-		       netdev->name, t->name);
-		return -EIO;
-	}
-	/* transport destroy function */
-	if (t->destroy)
-		t->destroy(netdev);
-
-	printk(KERN_DEBUG "fcoe_transport_release:"
-	       "device %s dettached from transport %s\n",
-	       netdev->name, t->name);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_transport_release);
-
-/**
- * fcoe_transport_init() - Initializes fcoe transport layer
- *
- * This prepares for the fcoe transport layer
- *
- * Returns: none
- */
-int __init fcoe_transport_init(void)
-{
-	INIT_LIST_HEAD(&fcoe_transports);
-	mutex_init(&fcoe_transports_lock);
-	return 0;
-}
-
-/**
- * fcoe_transport_exit() - Cleans up the fcoe transport layer
- *
- * This cleans up the fcoe transport layer. removing any transport on the list,
- * note that the transport destroy func is not called here.
- *
- * Returns: none
- */
-int __exit fcoe_transport_exit(void)
-{
-	struct fcoe_transport *t, *tmp;
-
-	mutex_lock(&fcoe_transports_lock);
-	list_for_each_entry_safe(t, tmp, &fcoe_transports, list) {
-		list_del(&t->list);
-		mutex_unlock(&fcoe_transports_lock);
-		fcoe_transport_device_remove_all(t);
-		mutex_lock(&fcoe_transports_lock);
-	}
-	mutex_unlock(&fcoe_transports_lock);
-	return 0;
-}
diff --git a/include/scsi/fc_transport_fcoe.h b/include/scsi/fc_transport_fcoe.h
deleted file mode 100644
index 8dca2af..0000000
--- a/include/scsi/fc_transport_fcoe.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef FC_TRANSPORT_FCOE_H
-#define FC_TRANSPORT_FCOE_H
-
-#include <linux/device.h>
-#include <linux/netdevice.h>
-#include <scsi/scsi_host.h>
-#include <scsi/libfc.h>
-
-/**
- * struct fcoe_transport - FCoE transport struct for generic transport
- * for Ethernet devices as well as pure HBAs
- *
- * @name: name for thsi transport
- * @bus: physical bus type (pci_bus_type)
- * @driver: physical bus driver for network device
- * @create: entry create function
- * @destroy: exit destroy function
- * @list: list of transports
- */
-struct fcoe_transport {
-	char *name;
-	unsigned short vendor;
-	unsigned short device;
-	struct bus_type *bus;
-	struct device_driver *driver;
-	int (*create)(struct net_device *device);
-	int (*destroy)(struct net_device *device);
-	bool (*match)(struct net_device *device);
-	struct list_head list;
-	struct list_head devlist;
-	struct mutex devlock;
-};
-
-/**
- * MODULE_ALIAS_FCOE_PCI
- *
- * some care must be taken with this, vendor and device MUST be a hex value
- * preceded with 0x and with letters in lower case (0x12ab, not 0x12AB or 12AB)
- */
-#define MODULE_ALIAS_FCOE_PCI(vendor, device) \
-	MODULE_ALIAS("fcoe-pci-" __stringify(vendor) "-" __stringify(device))
-
-/* exported funcs */
-int fcoe_transport_attach(struct net_device *netdev);
-int fcoe_transport_release(struct net_device *netdev);
-int fcoe_transport_register(struct fcoe_transport *t);
-int fcoe_transport_unregister(struct fcoe_transport *t);
-int fcoe_load_transport_driver(struct net_device *netdev);
-int __init fcoe_transport_init(void);
-int __exit fcoe_transport_exit(void);
-
-/* fcow_sw is the default transport */
-extern struct fcoe_transport fcoe_sw_transport;
-#endif /* FC_TRANSPORT_FCOE_H */


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH] fcoe: removes default sw transport code file fcoe_sw.c
  2009-03-17 18:42 ` [PATCH 07/14] fcoe: removes default sw transport code file fcoe_sw.c Robert Love
@ 2009-03-24 23:27   ` Robert Love
  2009-03-27 16:06   ` Robert Love
  1 sibling, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-24 23:27 UTC (permalink / raw)
  To: james.bottomeley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

Moves only required code from fcoe_sw.c to libfcoe.c towards having
just one source file for fcoe module, this gets rid off default sw
transport code in a separate fcoe_sw.c file.

Very minor renaming along this move, dropped _sw_ or _SW_ use
in names and replaced them by _if_ as a auxiliary interface
functions. Now some of these funcs can be removed or merged with
other func after fcoe transport is gone, but that should be
in another patch to keep this patch simple.

Now the libfcoe.c file name for fcoe module doesn't go along well,
so the libfcoe.c file renaming to fcoe.c as the only single fcoe
module file is done in next patch to keep this patch clean
and small for review.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/Makefile  |    3 
 drivers/scsi/fcoe/fcoe_sw.c |  523 -------------------------------------------
 drivers/scsi/fcoe/libfcoe.c |  501 +++++++++++++++++++++++++++++++++++++++++
 include/scsi/libfcoe.h      |    6 
 4 files changed, 494 insertions(+), 539 deletions(-)
 delete mode 100644 drivers/scsi/fcoe/fcoe_sw.c

diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index e950adf..2d9dae4 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -3,5 +3,4 @@
 obj-$(CONFIG_FCOE) += fcoe.o
 
 fcoe-y := \
-	libfcoe.o \
-	fcoe_sw.o
+	libfcoe.o
diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c
deleted file mode 100644
index f060220..0000000
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ /dev/null
@@ -1,523 +0,0 @@
-/*
- * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Maintained at www.Open-FCoE.org
- */
-
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if_vlan.h>
-#include <net/rtnetlink.h>
-
-#include <scsi/fc/fc_els.h>
-#include <scsi/fc/fc_encaps.h>
-#include <scsi/fc/fc_fs.h>
-#include <scsi/scsi_transport.h>
-#include <scsi/scsi_transport_fc.h>
-
-#include <scsi/libfc.h>
-#include <scsi/libfcoe.h>
-
-#define FCOE_SW_VERSION	"0.1"
-#define	FCOE_SW_NAME	"fcoesw"
-#define	FCOE_SW_VENDOR	"Open-FCoE.org"
-
-#define FCOE_MAX_LUN		255
-#define FCOE_MAX_FCP_TARGET	256
-
-#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
-
-#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
-#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
-
-static struct scsi_transport_template *scsi_transport_fcoe_sw;
-
-struct fc_function_template fcoe_sw_transport_function = {
-	.show_host_node_name = 1,
-	.show_host_port_name = 1,
-	.show_host_supported_classes = 1,
-	.show_host_supported_fc4s = 1,
-	.show_host_active_fc4s = 1,
-	.show_host_maxframe_size = 1,
-
-	.show_host_port_id = 1,
-	.show_host_supported_speeds = 1,
-	.get_host_speed = fc_get_host_speed,
-	.show_host_speed = 1,
-	.show_host_port_type = 1,
-	.get_host_port_state = fc_get_host_port_state,
-	.show_host_port_state = 1,
-	.show_host_symbolic_name = 1,
-
-	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
-	.show_rport_maxframe_size = 1,
-	.show_rport_supported_classes = 1,
-
-	.show_host_fabric_name = 1,
-	.show_starget_node_name = 1,
-	.show_starget_port_name = 1,
-	.show_starget_port_id = 1,
-	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
-	.show_rport_dev_loss_tmo = 1,
-	.get_fc_host_stats = fc_get_host_stats,
-	.issue_fc_host_lip = fcoe_reset,
-
-	.terminate_rport_io = fc_rport_terminate_io,
-};
-
-static struct scsi_host_template fcoe_sw_shost_template = {
-	.module = THIS_MODULE,
-	.name = "FCoE Driver",
-	.proc_name = FCOE_SW_NAME,
-	.queuecommand = fc_queuecommand,
-	.eh_abort_handler = fc_eh_abort,
-	.eh_device_reset_handler = fc_eh_device_reset,
-	.eh_host_reset_handler = fc_eh_host_reset,
-	.slave_alloc = fc_slave_alloc,
-	.change_queue_depth = fc_change_queue_depth,
-	.change_queue_type = fc_change_queue_type,
-	.this_id = -1,
-	.cmd_per_lun = 32,
-	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
-	.use_clustering = ENABLE_CLUSTERING,
-	.sg_tablesize = SG_ALL,
-	.max_sectors = 0xffff,
-};
-
-/**
- * fcoe_sw_lport_config() - sets up the fc_lport
- * @lp: ptr to the fc_lport
- * @shost: ptr to the parent scsi host
- *
- * Returns: 0 for success
- */
-static int fcoe_sw_lport_config(struct fc_lport *lp)
-{
-	lp->link_up = 0;
-	lp->qfull = 0;
-	lp->max_retry_count = 3;
-	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
-	lp->r_a_tov = 2 * 2 * 1000;
-	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
-			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
-
-	fc_lport_init_stats(lp);
-
-	/* lport fc_lport related configuration */
-	fc_lport_config(lp);
-
-	/* offload related configuration */
-	lp->crc_offload = 0;
-	lp->seq_offload = 0;
-	lp->lro_enabled = 0;
-	lp->lro_xid = 0;
-	lp->lso_max = 0;
-
-	return 0;
-}
-
-/**
- * fcoe_sw_netdev_config() - Set up netdev for SW FCoE
- * @lp : ptr to the fc_lport
- * @netdev : ptr to the associated netdevice struct
- *
- * Must be called after fcoe_sw_lport_config() as it will use lport mutex
- *
- * Returns : 0 for success
- */
-static int fcoe_sw_netdev_config(struct fc_lport *lp, struct net_device *netdev)
-{
-	u32 mfs;
-	u64 wwnn, wwpn;
-	struct fcoe_softc *fc;
-	u8 flogi_maddr[ETH_ALEN];
-
-	/* Setup lport private data to point to fcoe softc */
-	fc = lport_priv(lp);
-	fc->lp = lp;
-	fc->real_dev = netdev;
-	fc->phys_dev = netdev;
-
-	/* Require support for get_pauseparam ethtool op. */
-	if (netdev->priv_flags & IFF_802_1Q_VLAN)
-		fc->phys_dev = vlan_dev_real_dev(netdev);
-
-	/* Do not support for bonding device */
-	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
-	    (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
-	    (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
-		return -EOPNOTSUPP;
-	}
-
-	/*
-	 * Determine max frame size based on underlying device and optional
-	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
-	 * will return 0, so do this first.
-	 */
-	mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
-				   sizeof(struct fcoe_crc_eof));
-	if (fc_set_mfs(lp, mfs))
-		return -EINVAL;
-
-	if (!fcoe_link_ok(lp))
-		lp->link_up = 1;
-
-	/* offload features support */
-	if (fc->real_dev->features & NETIF_F_SG)
-		lp->sg_supp = 1;
-
-#ifdef NETIF_F_FCOE_CRC
-	if (netdev->features & NETIF_F_FCOE_CRC) {
-		lp->crc_offload = 1;
-		printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
-		       netdev->name);
-	}
-#endif
-#ifdef NETIF_F_FSO
-	if (netdev->features & NETIF_F_FSO) {
-		lp->seq_offload = 1;
-		lp->lso_max = netdev->gso_max_size;
-		printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
-		       netdev->name, lp->lso_max);
-	}
-#endif
-	if (netdev->fcoe_ddp_xid) {
-		lp->lro_enabled = 1;
-		lp->lro_xid = netdev->fcoe_ddp_xid;
-		printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
-		       netdev->name, lp->lro_xid);
-	}
-	skb_queue_head_init(&fc->fcoe_pending_queue);
-	fc->fcoe_pending_queue_active = 0;
-
-	/* setup Source Mac Address */
-	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
-	       fc->real_dev->addr_len);
-
-	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
-	fc_set_wwnn(lp, wwnn);
-	/* XXX - 3rd arg needs to be vlan id */
-	wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
-	fc_set_wwpn(lp, wwpn);
-
-	/*
-	 * Add FCoE MAC address as second unicast MAC address
-	 * or enter promiscuous mode if not capable of listening
-	 * for multiple unicast MACs.
-	 */
-	rtnl_lock();
-	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-	dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
-	rtnl_unlock();
-
-	/*
-	 * setup the receive function from ethernet driver
-	 * on the ethertype for the given device
-	 */
-	fc->fcoe_packet_type.func = fcoe_rcv;
-	fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
-	fc->fcoe_packet_type.dev = fc->real_dev;
-	dev_add_pack(&fc->fcoe_packet_type);
-
-	return 0;
-}
-
-/**
- * fcoe_sw_shost_config() - Sets up fc_lport->host
- * @lp : ptr to the fc_lport
- * @shost : ptr to the associated scsi host
- * @dev : device associated to scsi host
- *
- * Must be called after fcoe_sw_lport_config() and fcoe_sw_netdev_config()
- *
- * Returns : 0 for success
- */
-static int fcoe_sw_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
-				struct device *dev)
-{
-	int rc = 0;
-
-	/* lport scsi host config */
-	lp->host = shost;
-
-	lp->host->max_lun = FCOE_MAX_LUN;
-	lp->host->max_id = FCOE_MAX_FCP_TARGET;
-	lp->host->max_channel = 0;
-	lp->host->transportt = scsi_transport_fcoe_sw;
-
-	/* add the new host to the SCSI-ml */
-	rc = scsi_add_host(lp->host, dev);
-	if (rc) {
-		FC_DBG("fcoe_sw_shost_config:error on scsi_add_host\n");
-		return rc;
-	}
-	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
-		FCOE_SW_NAME, FCOE_SW_VERSION,
-		fcoe_netdev(lp)->name);
-
-	return 0;
-}
-
-/**
- * fcoe_sw_em_config() - allocates em for this lport
- * @lp: the port that em is to allocated for
- *
- * Returns : 0 on success
- */
-static inline int fcoe_sw_em_config(struct fc_lport *lp)
-{
-	BUG_ON(lp->emp);
-
-	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
-				    FCOE_MIN_XID, FCOE_MAX_XID);
-	if (!lp->emp)
-		return -ENOMEM;
-
-	return 0;
-}
-
-/**
- * fcoe_sw_destroy() - FCoE software HBA tear-down function
- * @netdev: ptr to the associated net_device
- *
- * Returns: 0 if link is OK for use by FCoE.
- */
-int fcoe_sw_destroy(struct net_device *netdev)
-{
-	struct fc_lport *lp = NULL;
-	struct fcoe_softc *fc;
-	u8 flogi_maddr[ETH_ALEN];
-
-	BUG_ON(!netdev);
-
-	printk(KERN_DEBUG "fcoe_sw_destroy:interface on %s\n",
-	       netdev->name);
-
-	lp = fcoe_hostlist_lookup(netdev);
-	if (!lp)
-		return -ENODEV;
-
-	fc = lport_priv(lp);
-
-	/* Logout of the fabric */
-	fc_fabric_logoff(lp);
-
-	/* Remove the instance from fcoe's list */
-	fcoe_hostlist_remove(lp);
-
-	/* Don't listen for Ethernet packets anymore */
-	dev_remove_pack(&fc->fcoe_packet_type);
-
-	/* Cleanup the fc_lport */
-	fc_lport_destroy(lp);
-	fc_fcp_destroy(lp);
-
-	/* Detach from the scsi-ml */
-	fc_remove_host(lp->host);
-	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);
-
-	/* Delete secondary MAC addresses */
-	rtnl_lock();
-	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
-	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
-		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
-	rtnl_unlock();
-
-	/* Free the per-CPU revieve threads */
-	fcoe_percpu_clean(lp);
-
-	/* Free existing skbs */
-	fcoe_clean_pending_queue(lp);
-
-	/* Free memory used by statistical counters */
-	fc_lport_free_stats(lp);
-
-	/* Release the net_device and Scsi_Host */
-	dev_put(fc->real_dev);
-	scsi_host_put(lp->host);
-
-	return 0;
-}
-
-/*
- * fcoe_sw_ddp_setup - calls LLD's ddp_setup through net_device
- * @lp:	the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- * @sgl: the scatterlist describing this transfer
- * @sgc: number of sg items
- *
- * Returns : 0 no ddp
- */
-static int fcoe_sw_ddp_setup(struct fc_lport *lp, u16 xid,
-			     struct scatterlist *sgl, unsigned int sgc)
-{
-	struct net_device *n = fcoe_netdev(lp);
-
-	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
-		return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
-
-	return 0;
-}
-
-/*
- * fcoe_sw_ddp_done - calls LLD's ddp_done through net_device
- * @lp:	the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- *
- * Returns : the length of data that have been completed by ddp
- */
-static int fcoe_sw_ddp_done(struct fc_lport *lp, u16 xid)
-{
-	struct net_device *n = fcoe_netdev(lp);
-
-	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
-		return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
-	return 0;
-}
-
-static struct libfc_function_template fcoe_sw_libfc_fcn_templ = {
-	.frame_send = fcoe_xmit,
-	.ddp_setup = fcoe_sw_ddp_setup,
-	.ddp_done = fcoe_sw_ddp_done,
-};
-
-/**
- * fcoe_sw_create() - this function creates the fcoe interface
- * @netdev: pointer the associated netdevice
- *
- * Creates fc_lport struct and scsi_host for lport, configures lport
- * and starts fabric login.
- *
- * Returns : 0 on success
- */
-int fcoe_sw_create(struct net_device *netdev)
-{
-	int rc;
-	struct fc_lport *lp = NULL;
-	struct fcoe_softc *fc;
-	struct Scsi_Host *shost;
-
-	BUG_ON(!netdev);
-
-	printk(KERN_DEBUG "fcoe_sw_create:interface on %s\n",
-	       netdev->name);
-
-	lp = fcoe_hostlist_lookup(netdev);
-	if (lp)
-		return -EEXIST;
-
-	shost = fcoe_host_alloc(&fcoe_sw_shost_template,
-				sizeof(struct fcoe_softc));
-	if (!shost) {
-		FC_DBG("Could not allocate host structure\n");
-		return -ENOMEM;
-	}
-	lp = shost_priv(shost);
-	fc = lport_priv(lp);
-
-	/* configure fc_lport, e.g., em */
-	rc = fcoe_sw_lport_config(lp);
-	if (rc) {
-		FC_DBG("Could not configure lport\n");
-		goto out_host_put;
-	}
-
-	/* configure lport network properties */
-	rc = fcoe_sw_netdev_config(lp, netdev);
-	if (rc) {
-		FC_DBG("Could not configure netdev for lport\n");
-		goto out_host_put;
-	}
-
-	/* configure lport scsi host properties */
-	rc = fcoe_sw_shost_config(lp, shost, &netdev->dev);
-	if (rc) {
-		FC_DBG("Could not configure shost for lport\n");
-		goto out_host_put;
-	}
-
-	/* lport exch manager allocation */
-	rc = fcoe_sw_em_config(lp);
-	if (rc) {
-		FC_DBG("Could not configure em for lport\n");
-		goto out_host_put;
-	}
-
-	/* Initialize the library */
-	rc = fcoe_libfc_config(lp, &fcoe_sw_libfc_fcn_templ);
-	if (rc) {
-		FC_DBG("Could not configure libfc for lport!\n");
-		goto out_lp_destroy;
-	}
-
-	/* add to lports list */
-	fcoe_hostlist_add(lp);
-
-	lp->boot_time = jiffies;
-
-	fc_fabric_login(lp);
-
-	dev_hold(netdev);
-
-	return rc;
-
-out_lp_destroy:
-	fc_exch_mgr_free(lp->emp); /* Free the EM */
-out_host_put:
-	scsi_host_put(lp->host);
-	return rc;
-}
-
-/**
- * fcoe_sw_init() - attach to scsi transport
- *
- * Returns : 0 on success
- */
-int __init fcoe_sw_init(void)
-{
-	/* attach to scsi transport */
-	scsi_transport_fcoe_sw =
-		fc_attach_transport(&fcoe_sw_transport_function);
-
-	if (!scsi_transport_fcoe_sw) {
-		printk(KERN_ERR "fcoe_sw_init:fc_attach_transport() failed\n");
-		return -ENODEV;
-	}
-
-	return 0;
-}
-
-/**
- * fcoe_sw_exit() - detach from scsi transport
- *
- * Returns : 0 on success
- */
-int __exit fcoe_sw_exit(void)
-{
-	fc_release_transport(scsi_transport_fcoe_sw);
-	return 0;
-}
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 334db95..a81a8ec 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -56,6 +56,18 @@ static int debug_fcoe;
 
 #define FCOE_WORD_TO_BYTE  4
 
+#define FCOE_VERSION	"0.1"
+#define	FCOE_NAME	"fcoe"
+#define	FCOE_VENDOR	"Open-FCoE.org"
+
+#define FCOE_MAX_LUN		255
+#define FCOE_MAX_FCP_TARGET	256
+
+#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
+
+#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
+#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
+
 MODULE_AUTHOR("Open-FCoE.org");
 MODULE_DESCRIPTION("FCoE");
 MODULE_LICENSE("GPL");
@@ -79,6 +91,479 @@ static struct notifier_block fcoe_notifier = {
 	.notifier_call = fcoe_device_notification,
 };
 
+static struct scsi_transport_template *scsi_transport_fcoe_sw;
+
+struct fc_function_template fcoe_transport_function = {
+	.show_host_node_name = 1,
+	.show_host_port_name = 1,
+	.show_host_supported_classes = 1,
+	.show_host_supported_fc4s = 1,
+	.show_host_active_fc4s = 1,
+	.show_host_maxframe_size = 1,
+
+	.show_host_port_id = 1,
+	.show_host_supported_speeds = 1,
+	.get_host_speed = fc_get_host_speed,
+	.show_host_speed = 1,
+	.show_host_port_type = 1,
+	.get_host_port_state = fc_get_host_port_state,
+	.show_host_port_state = 1,
+	.show_host_symbolic_name = 1,
+
+	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+	.show_rport_maxframe_size = 1,
+	.show_rport_supported_classes = 1,
+
+	.show_host_fabric_name = 1,
+	.show_starget_node_name = 1,
+	.show_starget_port_name = 1,
+	.show_starget_port_id = 1,
+	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+	.show_rport_dev_loss_tmo = 1,
+	.get_fc_host_stats = fc_get_host_stats,
+	.issue_fc_host_lip = fcoe_reset,
+
+	.terminate_rport_io = fc_rport_terminate_io,
+};
+
+static struct scsi_host_template fcoe_shost_template = {
+	.module = THIS_MODULE,
+	.name = "FCoE Driver",
+	.proc_name = FCOE_NAME,
+	.queuecommand = fc_queuecommand,
+	.eh_abort_handler = fc_eh_abort,
+	.eh_device_reset_handler = fc_eh_device_reset,
+	.eh_host_reset_handler = fc_eh_host_reset,
+	.slave_alloc = fc_slave_alloc,
+	.change_queue_depth = fc_change_queue_depth,
+	.change_queue_type = fc_change_queue_type,
+	.this_id = -1,
+	.cmd_per_lun = 32,
+	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
+	.use_clustering = ENABLE_CLUSTERING,
+	.sg_tablesize = SG_ALL,
+	.max_sectors = 0xffff,
+};
+
+/**
+ * fcoe_lport_config() - sets up the fc_lport
+ * @lp: ptr to the fc_lport
+ * @shost: ptr to the parent scsi host
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_lport_config(struct fc_lport *lp)
+{
+	lp->link_up = 0;
+	lp->qfull = 0;
+	lp->max_retry_count = 3;
+	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
+	lp->r_a_tov = 2 * 2 * 1000;
+	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+
+	fc_lport_init_stats(lp);
+
+	/* lport fc_lport related configuration */
+	fc_lport_config(lp);
+
+	/* offload related configuration */
+	lp->crc_offload = 0;
+	lp->seq_offload = 0;
+	lp->lro_enabled = 0;
+	lp->lro_xid = 0;
+	lp->lso_max = 0;
+
+	return 0;
+}
+
+/**
+ * fcoe_netdev_config() - Set up netdev for SW FCoE
+ * @lp : ptr to the fc_lport
+ * @netdev : ptr to the associated netdevice struct
+ *
+ * Must be called after fcoe_lport_config() as it will use lport mutex
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
+{
+	u32 mfs;
+	u64 wwnn, wwpn;
+	struct fcoe_softc *fc;
+	u8 flogi_maddr[ETH_ALEN];
+
+	/* Setup lport private data to point to fcoe softc */
+	fc = lport_priv(lp);
+	fc->lp = lp;
+	fc->real_dev = netdev;
+	fc->phys_dev = netdev;
+
+	/* Require support for get_pauseparam ethtool op. */
+	if (netdev->priv_flags & IFF_802_1Q_VLAN)
+		fc->phys_dev = vlan_dev_real_dev(netdev);
+
+	/* Do not support for bonding device */
+	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
+	    (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
+	    (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
+		return -EOPNOTSUPP;
+	}
+
+	/*
+	 * Determine max frame size based on underlying device and optional
+	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
+	 * will return 0, so do this first.
+	 */
+	mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
+				   sizeof(struct fcoe_crc_eof));
+	if (fc_set_mfs(lp, mfs))
+		return -EINVAL;
+
+	if (!fcoe_link_ok(lp))
+		lp->link_up = 1;
+
+	/* offload features support */
+	if (fc->real_dev->features & NETIF_F_SG)
+		lp->sg_supp = 1;
+
+#ifdef NETIF_F_FCOE_CRC
+	if (netdev->features & NETIF_F_FCOE_CRC) {
+		lp->crc_offload = 1;
+		printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
+		       netdev->name);
+	}
+#endif
+#ifdef NETIF_F_FSO
+	if (netdev->features & NETIF_F_FSO) {
+		lp->seq_offload = 1;
+		lp->lso_max = netdev->gso_max_size;
+		printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
+		       netdev->name, lp->lso_max);
+	}
+#endif
+	if (netdev->fcoe_ddp_xid) {
+		lp->lro_enabled = 1;
+		lp->lro_xid = netdev->fcoe_ddp_xid;
+		printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
+		       netdev->name, lp->lro_xid);
+	}
+	skb_queue_head_init(&fc->fcoe_pending_queue);
+	fc->fcoe_pending_queue_active = 0;
+
+	/* setup Source Mac Address */
+	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
+	       fc->real_dev->addr_len);
+
+	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
+	fc_set_wwnn(lp, wwnn);
+	/* XXX - 3rd arg needs to be vlan id */
+	wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
+	fc_set_wwpn(lp, wwpn);
+
+	/*
+	 * Add FCoE MAC address as second unicast MAC address
+	 * or enter promiscuous mode if not capable of listening
+	 * for multiple unicast MACs.
+	 */
+	rtnl_lock();
+	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+	dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
+	rtnl_unlock();
+
+	/*
+	 * setup the receive function from ethernet driver
+	 * on the ethertype for the given device
+	 */
+	fc->fcoe_packet_type.func = fcoe_rcv;
+	fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+	fc->fcoe_packet_type.dev = fc->real_dev;
+	dev_add_pack(&fc->fcoe_packet_type);
+
+	return 0;
+}
+
+/**
+ * fcoe_shost_config() - Sets up fc_lport->host
+ * @lp : ptr to the fc_lport
+ * @shost : ptr to the associated scsi host
+ * @dev : device associated to scsi host
+ *
+ * Must be called after fcoe_lport_config() and fcoe_netdev_config()
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
+				struct device *dev)
+{
+	int rc = 0;
+
+	/* lport scsi host config */
+	lp->host = shost;
+
+	lp->host->max_lun = FCOE_MAX_LUN;
+	lp->host->max_id = FCOE_MAX_FCP_TARGET;
+	lp->host->max_channel = 0;
+	lp->host->transportt = scsi_transport_fcoe_sw;
+
+	/* add the new host to the SCSI-ml */
+	rc = scsi_add_host(lp->host, dev);
+	if (rc) {
+		FC_DBG("fcoe_shost_config:error on scsi_add_host\n");
+		return rc;
+	}
+	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
+		FCOE_NAME, FCOE_VERSION,
+		fcoe_netdev(lp)->name);
+
+	return 0;
+}
+
+/**
+ * fcoe_em_config() - 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)
+{
+	BUG_ON(lp->emp);
+
+	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
+				    FCOE_MIN_XID, FCOE_MAX_XID);
+	if (!lp->emp)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/**
+ * fcoe_if_destroy() - FCoE software HBA tear-down function
+ * @netdev: ptr to the associated net_device
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ */
+static int fcoe_if_destroy(struct net_device *netdev)
+{
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+	u8 flogi_maddr[ETH_ALEN];
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_if_destroy:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (!lp)
+		return -ENODEV;
+
+	fc = lport_priv(lp);
+
+	/* Logout of the fabric */
+	fc_fabric_logoff(lp);
+
+	/* Remove the instance from fcoe's list */
+	fcoe_hostlist_remove(lp);
+
+	/* Don't listen for Ethernet packets anymore */
+	dev_remove_pack(&fc->fcoe_packet_type);
+
+	/* Cleanup the fc_lport */
+	fc_lport_destroy(lp);
+	fc_fcp_destroy(lp);
+
+	/* Detach from the scsi-ml */
+	fc_remove_host(lp->host);
+	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);
+
+	/* Delete secondary MAC addresses */
+	rtnl_lock();
+	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
+	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+	rtnl_unlock();
+
+	/* Free the per-CPU revieve threads */
+	fcoe_percpu_clean(lp);
+
+	/* Free existing skbs */
+	fcoe_clean_pending_queue(lp);
+
+	/* Free memory used by statistical counters */
+	fc_lport_free_stats(lp);
+
+	/* Release the net_device and Scsi_Host */
+	dev_put(fc->real_dev);
+	scsi_host_put(lp->host);
+
+	return 0;
+}
+
+/*
+ * fcoe_ddp_setup - calls LLD's ddp_setup through net_device
+ * @lp:	the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ * @sgl: the scatterlist describing this transfer
+ * @sgc: number of sg items
+ *
+ * Returns : 0 no ddp
+ */
+static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid,
+			     struct scatterlist *sgl, unsigned int sgc)
+{
+	struct net_device *n = fcoe_netdev(lp);
+
+	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
+		return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
+
+	return 0;
+}
+
+/*
+ * fcoe_ddp_done - calls LLD's ddp_done through net_device
+ * @lp:	the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ *
+ * Returns : the length of data that have been completed by ddp
+ */
+static int fcoe_ddp_done(struct fc_lport *lp, u16 xid)
+{
+	struct net_device *n = fcoe_netdev(lp);
+
+	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
+		return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
+	return 0;
+}
+
+static struct libfc_function_template fcoe_libfc_fcn_templ = {
+	.frame_send = fcoe_xmit,
+	.ddp_setup = fcoe_ddp_setup,
+	.ddp_done = fcoe_ddp_done,
+};
+
+/**
+ * fcoe_if_create() - this function creates the fcoe interface
+ * @netdev: pointer the associated netdevice
+ *
+ * Creates fc_lport struct and scsi_host for lport, configures lport
+ * and starts fabric login.
+ *
+ * Returns : 0 on success
+ */
+static int fcoe_if_create(struct net_device *netdev)
+{
+	int rc;
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+	struct Scsi_Host *shost;
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_if_create:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (lp)
+		return -EEXIST;
+
+	shost = fcoe_host_alloc(&fcoe_shost_template,
+				sizeof(struct fcoe_softc));
+	if (!shost) {
+		FC_DBG("Could not allocate host structure\n");
+		return -ENOMEM;
+	}
+	lp = shost_priv(shost);
+	fc = lport_priv(lp);
+
+	/* configure fc_lport, e.g., em */
+	rc = fcoe_lport_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure lport\n");
+		goto out_host_put;
+	}
+
+	/* configure lport network properties */
+	rc = fcoe_netdev_config(lp, netdev);
+	if (rc) {
+		FC_DBG("Could not configure netdev for lport\n");
+		goto out_host_put;
+	}
+
+	/* configure lport scsi host properties */
+	rc = fcoe_shost_config(lp, shost, &netdev->dev);
+	if (rc) {
+		FC_DBG("Could not configure shost for lport\n");
+		goto out_host_put;
+	}
+
+	/* lport exch manager allocation */
+	rc = fcoe_em_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure em for lport\n");
+		goto out_host_put;
+	}
+
+	/* Initialize the library */
+	rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ);
+	if (rc) {
+		FC_DBG("Could not configure libfc for lport!\n");
+		goto out_lp_destroy;
+	}
+
+	/* add to lports list */
+	fcoe_hostlist_add(lp);
+
+	lp->boot_time = jiffies;
+
+	fc_fabric_login(lp);
+
+	dev_hold(netdev);
+
+	return rc;
+
+out_lp_destroy:
+	fc_exch_mgr_free(lp->emp); /* Free the EM */
+out_host_put:
+	scsi_host_put(lp->host);
+	return rc;
+}
+
+/**
+ * fcoe_if_init() - attach to scsi transport
+ *
+ * Returns : 0 on success
+ */
+static int __init fcoe_if_init(void)
+{
+	/* attach to scsi transport */
+	scsi_transport_fcoe_sw =
+		fc_attach_transport(&fcoe_transport_function);
+
+	if (!scsi_transport_fcoe_sw) {
+		printk(KERN_ERR "fcoe_init:fc_attach_transport() failed\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * fcoe_if_exit() - detach from scsi transport
+ *
+ * Returns : 0 on success
+ */
+int __exit fcoe_if_exit(void)
+{
+	fc_release_transport(scsi_transport_fcoe_sw);
+	return 0;
+}
+
 /**
  * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
  * @cpu: cpu index for the online cpu
@@ -1080,9 +1565,9 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
 		rc = -ENODEV;
 		goto out_putdev;
 	}
-	rc = fcoe_sw_destroy(netdev);
+	rc = fcoe_if_destroy(netdev);
 	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_sw_destroy(%s) failed\n",
+		printk(KERN_ERR "fcoe: fcoe_if_destroy(%s) failed\n",
 		       netdev->name);
 		rc = -EIO;
 		goto out_putdev;
@@ -1119,9 +1604,9 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 	}
 	fcoe_ethdrv_get(netdev);
 
-	rc = fcoe_sw_create(netdev);
+	rc = fcoe_if_create(netdev);
 	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_sw_create(%s) failed\n",
+		printk(KERN_ERR "fcoe: fcoe_if_create(%s) failed\n",
 		       netdev->name);
 		fcoe_ethdrv_put(netdev);
 		rc = -EIO;
@@ -1457,7 +1942,7 @@ static int __init fcoe_init(void)
 
 	mod_timer(&fcoe_timer, jiffies + (10 * HZ));
 
-	fcoe_sw_init();
+	fcoe_if_init();
 
 	return 0;
 
@@ -1487,7 +1972,7 @@ static void __exit fcoe_exit(void)
 
 	/* releases the associated fcoe hosts */
 	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
-		fcoe_sw_destroy(fc->real_dev);
+		fcoe_if_destroy(fc->real_dev);
 
 	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
 
@@ -1495,7 +1980,7 @@ static void __exit fcoe_exit(void)
 		fcoe_percpu_thread_destroy(cpu);
 	}
 
-	/* remove sw trasnport */
-	fcoe_sw_exit();
+	/* detach from scsi transport */
+	fcoe_if_exit();
 }
 module_exit(fcoe_exit);
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index dc64405..e99633c 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -145,10 +145,4 @@ int fcoe_hostlist_remove(const struct fc_lport *);
 
 struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *, int);
 int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
-
-/* fcoe sw hba */
-int __init fcoe_sw_init(void);
-int __exit fcoe_sw_exit(void);
-int fcoe_sw_create(struct net_device *);
-int fcoe_sw_destroy(struct net_device *);
 #endif /* _LIBFCOE_H */


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH] fcoe: renames libfcoe.c to fcoe.c as the only fcoe module file
  2009-03-17 18:42 ` [PATCH 08/14] fcoe: renames libfcoe.c to fcoe.c as the only fcoe module file Robert Love
@ 2009-03-24 23:27   ` Robert Love
  2009-03-27 16:07   ` Robert Love
  1 sibling, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-24 23:27 UTC (permalink / raw)
  To: james.bottomeley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

Renames libfcoe.c to fcoe.c, fcoe.c becomes the only
.c file for fcoe.ko.

Also deleted "$Id: Makefile" from fcoe module Makefle.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/Makefile  |    5 
 drivers/scsi/fcoe/fcoe.c    | 1986 +++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/fcoe/libfcoe.c | 1986 -------------------------------------------
 3 files changed, 1986 insertions(+), 1991 deletions(-)
 create mode 100644 drivers/scsi/fcoe/fcoe.c
 delete mode 100644 drivers/scsi/fcoe/libfcoe.c

diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index 2d9dae4..9b590ef 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -1,6 +1 @@
-# $Id: Makefile
-
 obj-$(CONFIG_FCOE) += fcoe.o
-
-fcoe-y := \
-	libfcoe.o
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
new file mode 100644
index 0000000..a81a8ec
--- /dev/null
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -0,0 +1,1986 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/kthread.h>
+#include <linux/crc32.h>
+#include <linux/cpu.h>
+#include <linux/fs.h>
+#include <linux/sysfs.h>
+#include <linux/ctype.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+#include <net/rtnetlink.h>
+
+#include <scsi/fc/fc_encaps.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
+#include <scsi/libfcoe.h>
+
+static int debug_fcoe;
+
+#define FCOE_MAX_QUEUE_DEPTH  256
+#define FCOE_LOW_QUEUE_DEPTH  32
+
+/* destination address mode */
+#define FCOE_GW_ADDR_MODE	    0x00
+#define FCOE_FCOUI_ADDR_MODE	    0x01
+
+#define FCOE_WORD_TO_BYTE  4
+
+#define FCOE_VERSION	"0.1"
+#define	FCOE_NAME	"fcoe"
+#define	FCOE_VENDOR	"Open-FCoE.org"
+
+#define FCOE_MAX_LUN		255
+#define FCOE_MAX_FCP_TARGET	256
+
+#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
+
+#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
+#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
+
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("FCoE");
+MODULE_LICENSE("GPL");
+
+/* fcoe host list */
+LIST_HEAD(fcoe_hostlist);
+DEFINE_RWLOCK(fcoe_hostlist_lock);
+DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
+DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
+
+
+/* Function Prototyes */
+static int fcoe_check_wait_queue(struct fc_lport *);
+static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
+static int fcoe_device_notification(struct notifier_block *, ulong, void *);
+static void fcoe_dev_setup(void);
+static void fcoe_dev_cleanup(void);
+
+/* notification function from net device */
+static struct notifier_block fcoe_notifier = {
+	.notifier_call = fcoe_device_notification,
+};
+
+static struct scsi_transport_template *scsi_transport_fcoe_sw;
+
+struct fc_function_template fcoe_transport_function = {
+	.show_host_node_name = 1,
+	.show_host_port_name = 1,
+	.show_host_supported_classes = 1,
+	.show_host_supported_fc4s = 1,
+	.show_host_active_fc4s = 1,
+	.show_host_maxframe_size = 1,
+
+	.show_host_port_id = 1,
+	.show_host_supported_speeds = 1,
+	.get_host_speed = fc_get_host_speed,
+	.show_host_speed = 1,
+	.show_host_port_type = 1,
+	.get_host_port_state = fc_get_host_port_state,
+	.show_host_port_state = 1,
+	.show_host_symbolic_name = 1,
+
+	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+	.show_rport_maxframe_size = 1,
+	.show_rport_supported_classes = 1,
+
+	.show_host_fabric_name = 1,
+	.show_starget_node_name = 1,
+	.show_starget_port_name = 1,
+	.show_starget_port_id = 1,
+	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+	.show_rport_dev_loss_tmo = 1,
+	.get_fc_host_stats = fc_get_host_stats,
+	.issue_fc_host_lip = fcoe_reset,
+
+	.terminate_rport_io = fc_rport_terminate_io,
+};
+
+static struct scsi_host_template fcoe_shost_template = {
+	.module = THIS_MODULE,
+	.name = "FCoE Driver",
+	.proc_name = FCOE_NAME,
+	.queuecommand = fc_queuecommand,
+	.eh_abort_handler = fc_eh_abort,
+	.eh_device_reset_handler = fc_eh_device_reset,
+	.eh_host_reset_handler = fc_eh_host_reset,
+	.slave_alloc = fc_slave_alloc,
+	.change_queue_depth = fc_change_queue_depth,
+	.change_queue_type = fc_change_queue_type,
+	.this_id = -1,
+	.cmd_per_lun = 32,
+	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
+	.use_clustering = ENABLE_CLUSTERING,
+	.sg_tablesize = SG_ALL,
+	.max_sectors = 0xffff,
+};
+
+/**
+ * fcoe_lport_config() - sets up the fc_lport
+ * @lp: ptr to the fc_lport
+ * @shost: ptr to the parent scsi host
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_lport_config(struct fc_lport *lp)
+{
+	lp->link_up = 0;
+	lp->qfull = 0;
+	lp->max_retry_count = 3;
+	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
+	lp->r_a_tov = 2 * 2 * 1000;
+	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+
+	fc_lport_init_stats(lp);
+
+	/* lport fc_lport related configuration */
+	fc_lport_config(lp);
+
+	/* offload related configuration */
+	lp->crc_offload = 0;
+	lp->seq_offload = 0;
+	lp->lro_enabled = 0;
+	lp->lro_xid = 0;
+	lp->lso_max = 0;
+
+	return 0;
+}
+
+/**
+ * fcoe_netdev_config() - Set up netdev for SW FCoE
+ * @lp : ptr to the fc_lport
+ * @netdev : ptr to the associated netdevice struct
+ *
+ * Must be called after fcoe_lport_config() as it will use lport mutex
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
+{
+	u32 mfs;
+	u64 wwnn, wwpn;
+	struct fcoe_softc *fc;
+	u8 flogi_maddr[ETH_ALEN];
+
+	/* Setup lport private data to point to fcoe softc */
+	fc = lport_priv(lp);
+	fc->lp = lp;
+	fc->real_dev = netdev;
+	fc->phys_dev = netdev;
+
+	/* Require support for get_pauseparam ethtool op. */
+	if (netdev->priv_flags & IFF_802_1Q_VLAN)
+		fc->phys_dev = vlan_dev_real_dev(netdev);
+
+	/* Do not support for bonding device */
+	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
+	    (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
+	    (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
+		return -EOPNOTSUPP;
+	}
+
+	/*
+	 * Determine max frame size based on underlying device and optional
+	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
+	 * will return 0, so do this first.
+	 */
+	mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
+				   sizeof(struct fcoe_crc_eof));
+	if (fc_set_mfs(lp, mfs))
+		return -EINVAL;
+
+	if (!fcoe_link_ok(lp))
+		lp->link_up = 1;
+
+	/* offload features support */
+	if (fc->real_dev->features & NETIF_F_SG)
+		lp->sg_supp = 1;
+
+#ifdef NETIF_F_FCOE_CRC
+	if (netdev->features & NETIF_F_FCOE_CRC) {
+		lp->crc_offload = 1;
+		printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
+		       netdev->name);
+	}
+#endif
+#ifdef NETIF_F_FSO
+	if (netdev->features & NETIF_F_FSO) {
+		lp->seq_offload = 1;
+		lp->lso_max = netdev->gso_max_size;
+		printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
+		       netdev->name, lp->lso_max);
+	}
+#endif
+	if (netdev->fcoe_ddp_xid) {
+		lp->lro_enabled = 1;
+		lp->lro_xid = netdev->fcoe_ddp_xid;
+		printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
+		       netdev->name, lp->lro_xid);
+	}
+	skb_queue_head_init(&fc->fcoe_pending_queue);
+	fc->fcoe_pending_queue_active = 0;
+
+	/* setup Source Mac Address */
+	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
+	       fc->real_dev->addr_len);
+
+	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
+	fc_set_wwnn(lp, wwnn);
+	/* XXX - 3rd arg needs to be vlan id */
+	wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
+	fc_set_wwpn(lp, wwpn);
+
+	/*
+	 * Add FCoE MAC address as second unicast MAC address
+	 * or enter promiscuous mode if not capable of listening
+	 * for multiple unicast MACs.
+	 */
+	rtnl_lock();
+	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+	dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
+	rtnl_unlock();
+
+	/*
+	 * setup the receive function from ethernet driver
+	 * on the ethertype for the given device
+	 */
+	fc->fcoe_packet_type.func = fcoe_rcv;
+	fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+	fc->fcoe_packet_type.dev = fc->real_dev;
+	dev_add_pack(&fc->fcoe_packet_type);
+
+	return 0;
+}
+
+/**
+ * fcoe_shost_config() - Sets up fc_lport->host
+ * @lp : ptr to the fc_lport
+ * @shost : ptr to the associated scsi host
+ * @dev : device associated to scsi host
+ *
+ * Must be called after fcoe_lport_config() and fcoe_netdev_config()
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
+				struct device *dev)
+{
+	int rc = 0;
+
+	/* lport scsi host config */
+	lp->host = shost;
+
+	lp->host->max_lun = FCOE_MAX_LUN;
+	lp->host->max_id = FCOE_MAX_FCP_TARGET;
+	lp->host->max_channel = 0;
+	lp->host->transportt = scsi_transport_fcoe_sw;
+
+	/* add the new host to the SCSI-ml */
+	rc = scsi_add_host(lp->host, dev);
+	if (rc) {
+		FC_DBG("fcoe_shost_config:error on scsi_add_host\n");
+		return rc;
+	}
+	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
+		FCOE_NAME, FCOE_VERSION,
+		fcoe_netdev(lp)->name);
+
+	return 0;
+}
+
+/**
+ * fcoe_em_config() - 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)
+{
+	BUG_ON(lp->emp);
+
+	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
+				    FCOE_MIN_XID, FCOE_MAX_XID);
+	if (!lp->emp)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/**
+ * fcoe_if_destroy() - FCoE software HBA tear-down function
+ * @netdev: ptr to the associated net_device
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ */
+static int fcoe_if_destroy(struct net_device *netdev)
+{
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+	u8 flogi_maddr[ETH_ALEN];
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_if_destroy:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (!lp)
+		return -ENODEV;
+
+	fc = lport_priv(lp);
+
+	/* Logout of the fabric */
+	fc_fabric_logoff(lp);
+
+	/* Remove the instance from fcoe's list */
+	fcoe_hostlist_remove(lp);
+
+	/* Don't listen for Ethernet packets anymore */
+	dev_remove_pack(&fc->fcoe_packet_type);
+
+	/* Cleanup the fc_lport */
+	fc_lport_destroy(lp);
+	fc_fcp_destroy(lp);
+
+	/* Detach from the scsi-ml */
+	fc_remove_host(lp->host);
+	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);
+
+	/* Delete secondary MAC addresses */
+	rtnl_lock();
+	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
+	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+	rtnl_unlock();
+
+	/* Free the per-CPU revieve threads */
+	fcoe_percpu_clean(lp);
+
+	/* Free existing skbs */
+	fcoe_clean_pending_queue(lp);
+
+	/* Free memory used by statistical counters */
+	fc_lport_free_stats(lp);
+
+	/* Release the net_device and Scsi_Host */
+	dev_put(fc->real_dev);
+	scsi_host_put(lp->host);
+
+	return 0;
+}
+
+/*
+ * fcoe_ddp_setup - calls LLD's ddp_setup through net_device
+ * @lp:	the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ * @sgl: the scatterlist describing this transfer
+ * @sgc: number of sg items
+ *
+ * Returns : 0 no ddp
+ */
+static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid,
+			     struct scatterlist *sgl, unsigned int sgc)
+{
+	struct net_device *n = fcoe_netdev(lp);
+
+	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
+		return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
+
+	return 0;
+}
+
+/*
+ * fcoe_ddp_done - calls LLD's ddp_done through net_device
+ * @lp:	the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ *
+ * Returns : the length of data that have been completed by ddp
+ */
+static int fcoe_ddp_done(struct fc_lport *lp, u16 xid)
+{
+	struct net_device *n = fcoe_netdev(lp);
+
+	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
+		return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
+	return 0;
+}
+
+static struct libfc_function_template fcoe_libfc_fcn_templ = {
+	.frame_send = fcoe_xmit,
+	.ddp_setup = fcoe_ddp_setup,
+	.ddp_done = fcoe_ddp_done,
+};
+
+/**
+ * fcoe_if_create() - this function creates the fcoe interface
+ * @netdev: pointer the associated netdevice
+ *
+ * Creates fc_lport struct and scsi_host for lport, configures lport
+ * and starts fabric login.
+ *
+ * Returns : 0 on success
+ */
+static int fcoe_if_create(struct net_device *netdev)
+{
+	int rc;
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+	struct Scsi_Host *shost;
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_if_create:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (lp)
+		return -EEXIST;
+
+	shost = fcoe_host_alloc(&fcoe_shost_template,
+				sizeof(struct fcoe_softc));
+	if (!shost) {
+		FC_DBG("Could not allocate host structure\n");
+		return -ENOMEM;
+	}
+	lp = shost_priv(shost);
+	fc = lport_priv(lp);
+
+	/* configure fc_lport, e.g., em */
+	rc = fcoe_lport_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure lport\n");
+		goto out_host_put;
+	}
+
+	/* configure lport network properties */
+	rc = fcoe_netdev_config(lp, netdev);
+	if (rc) {
+		FC_DBG("Could not configure netdev for lport\n");
+		goto out_host_put;
+	}
+
+	/* configure lport scsi host properties */
+	rc = fcoe_shost_config(lp, shost, &netdev->dev);
+	if (rc) {
+		FC_DBG("Could not configure shost for lport\n");
+		goto out_host_put;
+	}
+
+	/* lport exch manager allocation */
+	rc = fcoe_em_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure em for lport\n");
+		goto out_host_put;
+	}
+
+	/* Initialize the library */
+	rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ);
+	if (rc) {
+		FC_DBG("Could not configure libfc for lport!\n");
+		goto out_lp_destroy;
+	}
+
+	/* add to lports list */
+	fcoe_hostlist_add(lp);
+
+	lp->boot_time = jiffies;
+
+	fc_fabric_login(lp);
+
+	dev_hold(netdev);
+
+	return rc;
+
+out_lp_destroy:
+	fc_exch_mgr_free(lp->emp); /* Free the EM */
+out_host_put:
+	scsi_host_put(lp->host);
+	return rc;
+}
+
+/**
+ * fcoe_if_init() - attach to scsi transport
+ *
+ * Returns : 0 on success
+ */
+static int __init fcoe_if_init(void)
+{
+	/* attach to scsi transport */
+	scsi_transport_fcoe_sw =
+		fc_attach_transport(&fcoe_transport_function);
+
+	if (!scsi_transport_fcoe_sw) {
+		printk(KERN_ERR "fcoe_init:fc_attach_transport() failed\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * fcoe_if_exit() - detach from scsi transport
+ *
+ * Returns : 0 on success
+ */
+int __exit fcoe_if_exit(void)
+{
+	fc_release_transport(scsi_transport_fcoe_sw);
+	return 0;
+}
+
+/**
+ * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
+ * @cpu: cpu index for the online cpu
+ */
+static void fcoe_percpu_thread_create(unsigned int cpu)
+{
+	struct fcoe_percpu_s *p;
+	struct task_struct *thread;
+
+	p = &per_cpu(fcoe_percpu, cpu);
+
+	thread = kthread_create(fcoe_percpu_receive_thread,
+				(void *)p, "fcoethread/%d", cpu);
+
+	if (likely(!IS_ERR(p->thread))) {
+		kthread_bind(thread, cpu);
+		wake_up_process(thread);
+
+		spin_lock_bh(&p->fcoe_rx_list.lock);
+		p->thread = thread;
+		spin_unlock_bh(&p->fcoe_rx_list.lock);
+	}
+}
+
+/**
+ * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
+ * @cpu: cpu index the rx thread is to be removed
+ *
+ * Destroys a per-CPU Rx thread. Any pending skbs are moved to the
+ * current CPU's Rx thread. If the thread being destroyed is bound to
+ * the CPU processing this context the skbs will be freed.
+ */
+static void fcoe_percpu_thread_destroy(unsigned int cpu)
+{
+	struct fcoe_percpu_s *p;
+	struct task_struct *thread;
+	struct page *crc_eof;
+	struct sk_buff *skb;
+#ifdef CONFIG_SMP
+	struct fcoe_percpu_s *p0;
+	unsigned targ_cpu = smp_processor_id();
+#endif /* CONFIG_SMP */
+
+	printk(KERN_DEBUG "fcoe: Destroying receive thread for CPU %d\n", cpu);
+
+	/* Prevent any new skbs from being queued for this CPU. */
+	p = &per_cpu(fcoe_percpu, cpu);
+	spin_lock_bh(&p->fcoe_rx_list.lock);
+	thread = p->thread;
+	p->thread = NULL;
+	crc_eof = p->crc_eof_page;
+	p->crc_eof_page = NULL;
+	p->crc_eof_offset = 0;
+	spin_unlock_bh(&p->fcoe_rx_list.lock);
+
+#ifdef CONFIG_SMP
+	/*
+	 * Don't bother moving the skb's if this context is running
+	 * on the same CPU that is having its thread destroyed. This
+	 * can easily happen when the module is removed.
+	 */
+	if (cpu != targ_cpu) {
+		p0 = &per_cpu(fcoe_percpu, targ_cpu);
+		spin_lock_bh(&p0->fcoe_rx_list.lock);
+		if (p0->thread) {
+			FC_DBG("Moving frames from CPU %d to CPU %d\n",
+			       cpu, targ_cpu);
+
+			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+				__skb_queue_tail(&p0->fcoe_rx_list, skb);
+			spin_unlock_bh(&p0->fcoe_rx_list.lock);
+		} else {
+			/*
+			 * The targeted CPU is not initialized and cannot accept
+			 * new  skbs. Unlock the targeted CPU and drop the skbs
+			 * on the CPU that is going offline.
+			 */
+			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+				kfree_skb(skb);
+			spin_unlock_bh(&p0->fcoe_rx_list.lock);
+		}
+	} else {
+		/*
+		 * This scenario occurs when the module is being removed
+		 * and all threads are being destroyed. skbs will continue
+		 * to be shifted from the CPU thread that is being removed
+		 * to the CPU thread associated with the CPU that is processing
+		 * the module removal. Once there is only one CPU Rx thread it
+		 * will reach this case and we will drop all skbs and later
+		 * stop the thread.
+		 */
+		spin_lock_bh(&p->fcoe_rx_list.lock);
+		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+			kfree_skb(skb);
+		spin_unlock_bh(&p->fcoe_rx_list.lock);
+	}
+#else
+	/*
+	 * This a non-SMP scenario where the singluar Rx thread is
+	 * being removed. Free all skbs and stop the thread.
+	 */
+	spin_lock_bh(&p->fcoe_rx_list.lock);
+	while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+		kfree_skb(skb);
+	spin_unlock_bh(&p->fcoe_rx_list.lock);
+#endif
+
+	if (thread)
+		kthread_stop(thread);
+
+	if (crc_eof)
+		put_page(crc_eof);
+}
+
+/**
+ * fcoe_cpu_callback() - fcoe cpu hotplug event callback
+ * @nfb: callback data block
+ * @action: event triggering the callback
+ * @hcpu: index for the cpu of this event
+ *
+ * This creates or destroys per cpu data for fcoe
+ *
+ * Returns NOTIFY_OK always.
+ */
+static int fcoe_cpu_callback(struct notifier_block *nfb,
+			     unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		FC_DBG("CPU %x online: Create Rx thread\n", cpu);
+		fcoe_percpu_thread_create(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		FC_DBG("CPU %x offline: Remove Rx thread\n", cpu);
+		fcoe_percpu_thread_destroy(cpu);
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block fcoe_cpu_notifier = {
+	.notifier_call = fcoe_cpu_callback,
+};
+
+/**
+ * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ
+ * @skb: the receive skb
+ * @dev: associated net device
+ * @ptype: context
+ * @odldev: last device
+ *
+ * this function will receive the packet and build fc frame and pass it up
+ *
+ * Returns: 0 for success
+ */
+int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
+	     struct packet_type *ptype, struct net_device *olddev)
+{
+	struct fc_lport *lp;
+	struct fcoe_rcv_info *fr;
+	struct fcoe_softc *fc;
+	struct fc_frame_header *fh;
+	struct fcoe_percpu_s *fps;
+	unsigned short oxid;
+	unsigned int cpu = 0;
+
+	fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
+	lp = fc->lp;
+	if (unlikely(lp == NULL)) {
+		FC_DBG("cannot find hba structure");
+		goto err2;
+	}
+
+	if (unlikely(debug_fcoe)) {
+		FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p "
+		       "end:%p sum:%d dev:%s", skb->len, skb->data_len,
+		       skb->head, skb->data, skb_tail_pointer(skb),
+		       skb_end_pointer(skb), skb->csum,
+		       skb->dev ? skb->dev->name : "<NULL>");
+
+	}
+
+	/* check for FCOE packet type */
+	if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
+		FC_DBG("wrong FC type frame");
+		goto err;
+	}
+
+	/*
+	 * Check for minimum frame length, and make sure required FCoE
+	 * and FC headers are pulled into the linear data area.
+	 */
+	if (unlikely((skb->len < FCOE_MIN_FRAME) ||
+	    !pskb_may_pull(skb, FCOE_HEADER_LEN)))
+		goto err;
+
+	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.
+	 */
+	cpu = oxid & (num_online_cpus() - 1);
+#endif
+
+	fps = &per_cpu(fcoe_percpu, cpu);
+	spin_lock_bh(&fps->fcoe_rx_list.lock);
+	if (unlikely(!fps->thread)) {
+		/*
+		 * The targeted CPU is not ready, let's target
+		 * the first CPU now. For non-SMP systems this
+		 * will check the same CPU twice.
+		 */
+		FC_DBG("CPU is online, but no receive thread ready "
+		       "for incoming skb- using first online CPU.\n");
+
+		spin_unlock_bh(&fps->fcoe_rx_list.lock);
+		cpu = first_cpu(cpu_online_map);
+		fps = &per_cpu(fcoe_percpu, cpu);
+		spin_lock_bh(&fps->fcoe_rx_list.lock);
+		if (!fps->thread) {
+			spin_unlock_bh(&fps->fcoe_rx_list.lock);
+			goto err;
+		}
+	}
+
+	/*
+	 * We now have a valid CPU that we're targeting for
+	 * this skb. We also have this receive thread locked,
+	 * so we're free to queue skbs into it's queue.
+	 */
+	__skb_queue_tail(&fps->fcoe_rx_list, skb);
+	if (fps->fcoe_rx_list.qlen == 1)
+		wake_up_process(fps->thread);
+
+	spin_unlock_bh(&fps->fcoe_rx_list.lock);
+
+	return 0;
+err:
+	fc_lport_get_stats(lp)->ErrorFrames++;
+
+err2:
+	kfree_skb(skb);
+	return -1;
+}
+EXPORT_SYMBOL_GPL(fcoe_rcv);
+
+/**
+ * fcoe_start_io() - pass to netdev to start xmit for fcoe
+ * @skb: the skb to be xmitted
+ *
+ * Returns: 0 for success
+ */
+static inline int fcoe_start_io(struct sk_buff *skb)
+{
+	int rc;
+
+	skb_get(skb);
+	rc = dev_queue_xmit(skb);
+	if (rc != 0)
+		return rc;
+	kfree_skb(skb);
+	return 0;
+}
+
+/**
+ * fcoe_get_paged_crc_eof() - in case we need alloc a page for crc_eof
+ * @skb: the skb to be xmitted
+ * @tlen: total len
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
+{
+	struct fcoe_percpu_s *fps;
+	struct page *page;
+
+	fps = &get_cpu_var(fcoe_percpu);
+	page = fps->crc_eof_page;
+	if (!page) {
+		page = alloc_page(GFP_ATOMIC);
+		if (!page) {
+			put_cpu_var(fcoe_percpu);
+			return -ENOMEM;
+		}
+		fps->crc_eof_page = page;
+		fps->crc_eof_offset = 0;
+	}
+
+	get_page(page);
+	skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page,
+			   fps->crc_eof_offset, tlen);
+	skb->len += tlen;
+	skb->data_len += tlen;
+	skb->truesize += tlen;
+	fps->crc_eof_offset += sizeof(struct fcoe_crc_eof);
+
+	if (fps->crc_eof_offset >= PAGE_SIZE) {
+		fps->crc_eof_page = NULL;
+		fps->crc_eof_offset = 0;
+		put_page(page);
+	}
+	put_cpu_var(fcoe_percpu);
+	return 0;
+}
+
+/**
+ * fcoe_fc_crc() - calculates FC CRC in this fcoe skb
+ * @fp: the fc_frame containg data to be checksummed
+ *
+ * This uses crc32() to calculate the crc for fc frame
+ * Return   : 32 bit crc
+ */
+u32 fcoe_fc_crc(struct fc_frame *fp)
+{
+	struct sk_buff *skb = fp_skb(fp);
+	struct skb_frag_struct *frag;
+	unsigned char *data;
+	unsigned long off, len, clen;
+	u32 crc;
+	unsigned i;
+
+	crc = crc32(~0, skb->data, skb_headlen(skb));
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		frag = &skb_shinfo(skb)->frags[i];
+		off = frag->page_offset;
+		len = frag->size;
+		while (len > 0) {
+			clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK));
+			data = kmap_atomic(frag->page + (off >> PAGE_SHIFT),
+					   KM_SKB_DATA_SOFTIRQ);
+			crc = crc32(crc, data + (off & ~PAGE_MASK), clen);
+			kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ);
+			off += clen;
+			len -= clen;
+		}
+	}
+	return crc;
+}
+EXPORT_SYMBOL_GPL(fcoe_fc_crc);
+
+/**
+ * fcoe_xmit() - FCoE frame transmit function
+ * @lp:	the associated local port
+ * @fp: the fc_frame to be transmitted
+ *
+ * Return   : 0 for success
+ */
+int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
+{
+	int wlen, rc = 0;
+	u32 crc;
+	struct ethhdr *eh;
+	struct fcoe_crc_eof *cp;
+	struct sk_buff *skb;
+	struct fcoe_dev_stats *stats;
+	struct fc_frame_header *fh;
+	unsigned int hlen;		/* header length implies the version */
+	unsigned int tlen;		/* trailer length */
+	unsigned int elen;		/* eth header, may include vlan */
+	int flogi_in_progress = 0;
+	struct fcoe_softc *fc;
+	u8 sof, eof;
+	struct fcoe_hdr *hp;
+
+	WARN_ON((fr_len(fp) % sizeof(u32)) != 0);
+
+	fc = lport_priv(lp);
+	/*
+	 * if it is a flogi then we need to learn gw-addr
+	 * and my own fcid
+	 */
+	fh = fc_frame_header_get(fp);
+	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
+		if (fc_frame_payload_op(fp) == ELS_FLOGI) {
+			fc->flogi_oxid = ntohs(fh->fh_ox_id);
+			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
+			fc->flogi_progress = 1;
+			flogi_in_progress = 1;
+		} else if (fc->flogi_progress && ntoh24(fh->fh_s_id) != 0) {
+			/*
+			 * Here we must've gotten an SID by accepting an FLOGI
+			 * from a point-to-point connection.  Switch to using
+			 * the source mac based on the SID.  The destination
+			 * MAC in this case would have been set by receving the
+			 * FLOGI.
+			 */
+			fc_fcoe_set_mac(fc->data_src_addr, fh->fh_s_id);
+			fc->flogi_progress = 0;
+		}
+	}
+
+	skb = fp_skb(fp);
+	sof = fr_sof(fp);
+	eof = fr_eof(fp);
+
+	elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ?
+		sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr);
+	hlen = sizeof(struct fcoe_hdr);
+	tlen = sizeof(struct fcoe_crc_eof);
+	wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
+
+	/* crc offload */
+	if (likely(lp->crc_offload)) {
+		skb->ip_summed = CHECKSUM_PARTIAL;
+		skb->csum_start = skb_headroom(skb);
+		skb->csum_offset = skb->len;
+		crc = 0;
+	} else {
+		skb->ip_summed = CHECKSUM_NONE;
+		crc = fcoe_fc_crc(fp);
+	}
+
+	/* copy fc crc and eof to the skb buff */
+	if (skb_is_nonlinear(skb)) {
+		skb_frag_t *frag;
+		if (fcoe_get_paged_crc_eof(skb, tlen)) {
+			kfree_skb(skb);
+			return -ENOMEM;
+		}
+		frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
+		cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
+			+ frag->page_offset;
+	} else {
+		cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
+	}
+
+	memset(cp, 0, sizeof(*cp));
+	cp->fcoe_eof = eof;
+	cp->fcoe_crc32 = cpu_to_le32(~crc);
+
+	if (skb_is_nonlinear(skb)) {
+		kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ);
+		cp = NULL;
+	}
+
+	/* adjust skb netowrk/transport offsets to match mac/fcoe/fc */
+	skb_push(skb, elen + hlen);
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	skb->mac_len = elen;
+	skb->protocol = htons(ETH_P_FCOE);
+	skb->dev = fc->real_dev;
+
+	/* fill up mac and fcoe headers */
+	eh = eth_hdr(skb);
+	eh->h_proto = htons(ETH_P_FCOE);
+	if (fc->address_mode == FCOE_FCOUI_ADDR_MODE)
+		fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
+	else
+		/* insert GW address */
+		memcpy(eh->h_dest, fc->dest_addr, ETH_ALEN);
+
+	if (unlikely(flogi_in_progress))
+		memcpy(eh->h_source, fc->ctl_src_addr, ETH_ALEN);
+	else
+		memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN);
+
+	hp = (struct fcoe_hdr *)(eh + 1);
+	memset(hp, 0, sizeof(*hp));
+	if (FC_FCOE_VER)
+		FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
+	hp->fcoe_sof = sof;
+
+#ifdef NETIF_F_FSO
+	/* fcoe lso, mss is in max_payload which is non-zero for FCP data */
+	if (lp->seq_offload && fr_max_payload(fp)) {
+		skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
+		skb_shinfo(skb)->gso_size = fr_max_payload(fp);
+	} else {
+		skb_shinfo(skb)->gso_type = 0;
+		skb_shinfo(skb)->gso_size = 0;
+	}
+#endif
+	/* update tx stats: regardless if LLD fails */
+	stats = fc_lport_get_stats(lp);
+	stats->TxFrames++;
+	stats->TxWords += wlen;
+
+	/* send down to lld */
+	fr_dev(fp) = lp;
+	if (fc->fcoe_pending_queue.qlen)
+		rc = fcoe_check_wait_queue(lp);
+
+	if (rc == 0)
+		rc = fcoe_start_io(skb);
+
+	if (rc) {
+		spin_lock_bh(&fc->fcoe_pending_queue.lock);
+		__skb_queue_tail(&fc->fcoe_pending_queue, skb);
+		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+		if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+			lp->qfull = 1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_xmit);
+
+/**
+ * fcoe_percpu_receive_thread() - recv thread per cpu
+ * @arg: ptr to the fcoe per cpu struct
+ *
+ * Return: 0 for success
+ */
+int fcoe_percpu_receive_thread(void *arg)
+{
+	struct fcoe_percpu_s *p = arg;
+	u32 fr_len;
+	struct fc_lport *lp;
+	struct fcoe_rcv_info *fr;
+	struct fcoe_dev_stats *stats;
+	struct fc_frame_header *fh;
+	struct sk_buff *skb;
+	struct fcoe_crc_eof crc_eof;
+	struct fc_frame *fp;
+	u8 *mac = NULL;
+	struct fcoe_softc *fc;
+	struct fcoe_hdr *hp;
+
+	set_user_nice(current, -20);
+
+	while (!kthread_should_stop()) {
+
+		spin_lock_bh(&p->fcoe_rx_list.lock);
+		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			spin_unlock_bh(&p->fcoe_rx_list.lock);
+			schedule();
+			set_current_state(TASK_RUNNING);
+			if (kthread_should_stop())
+				return 0;
+			spin_lock_bh(&p->fcoe_rx_list.lock);
+		}
+		spin_unlock_bh(&p->fcoe_rx_list.lock);
+		fr = fcoe_dev_from_skb(skb);
+		lp = fr->fr_dev;
+		if (unlikely(lp == NULL)) {
+			FC_DBG("invalid HBA Structure");
+			kfree_skb(skb);
+			continue;
+		}
+
+		if (unlikely(debug_fcoe)) {
+			FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p "
+			       "tail:%p end:%p sum:%d dev:%s",
+			       skb->len, skb->data_len,
+			       skb->head, skb->data, skb_tail_pointer(skb),
+			       skb_end_pointer(skb), skb->csum,
+			       skb->dev ? skb->dev->name : "<NULL>");
+		}
+
+		/*
+		 * Save source MAC address before discarding header.
+		 */
+		fc = lport_priv(lp);
+		if (unlikely(fc->flogi_progress))
+			mac = eth_hdr(skb)->h_source;
+
+		if (skb_is_nonlinear(skb))
+			skb_linearize(skb);	/* not ideal */
+
+		/*
+		 * Frame length checks and setting up the header pointers
+		 * was done in fcoe_rcv already.
+		 */
+		hp = (struct fcoe_hdr *) skb_network_header(skb);
+		fh = (struct fc_frame_header *) skb_transport_header(skb);
+
+		stats = fc_lport_get_stats(lp);
+		if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
+			if (stats->ErrorFrames < 5)
+				printk(KERN_WARNING "FCoE version "
+				       "mismatch: The frame has "
+				       "version %x, but the "
+				       "initiator supports version "
+				       "%x\n", FC_FCOE_DECAPS_VER(hp),
+				       FC_FCOE_VER);
+			stats->ErrorFrames++;
+			kfree_skb(skb);
+			continue;
+		}
+
+		skb_pull(skb, sizeof(struct fcoe_hdr));
+		fr_len = skb->len - sizeof(struct fcoe_crc_eof);
+
+		stats->RxFrames++;
+		stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
+
+		fp = (struct fc_frame *)skb;
+		fc_frame_init(fp);
+		fr_dev(fp) = lp;
+		fr_sof(fp) = hp->fcoe_sof;
+
+		/* Copy out the CRC and EOF trailer for access */
+		if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
+			kfree_skb(skb);
+			continue;
+		}
+		fr_eof(fp) = crc_eof.fcoe_eof;
+		fr_crc(fp) = crc_eof.fcoe_crc32;
+		if (pskb_trim(skb, fr_len)) {
+			kfree_skb(skb);
+			continue;
+		}
+
+		/*
+		 * We only check CRC if no offload is available and if it is
+		 * it's solicited data, in which case, the FCP layer would
+		 * check it during the copy.
+		 */
+		if (lp->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
+			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+		else
+			fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
+
+		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);
+			continue;
+		}
+		if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+			if (le32_to_cpu(fr_crc(fp)) !=
+			    ~crc32(~0, skb->data, fr_len)) {
+				if (debug_fcoe || stats->InvalidCRCCount < 5)
+					printk(KERN_WARNING "fcoe: dropping "
+					       "frame with CRC error\n");
+				stats->InvalidCRCCount++;
+				stats->ErrorFrames++;
+				fc_frame_free(fp);
+				continue;
+			}
+			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+		}
+		/* non flogi and non data exchanges are handled here */
+		if (unlikely(fc->flogi_progress))
+			fcoe_recv_flogi(fc, fp, mac);
+		fc_exch_recv(lp, lp->emp, fp);
+	}
+	return 0;
+}
+
+/**
+ * fcoe_recv_flogi() - flogi receive function
+ * @fc: associated fcoe_softc
+ * @fp: the recieved frame
+ * @sa: the source address of this flogi
+ *
+ * This is responsible to parse the flogi response and sets the corresponding
+ * mac address for the initiator, eitehr OUI based or GW based.
+ *
+ * Returns: none
+ */
+static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa)
+{
+	struct fc_frame_header *fh;
+	u8 op;
+
+	fh = fc_frame_header_get(fp);
+	if (fh->fh_type != FC_TYPE_ELS)
+		return;
+	op = fc_frame_payload_op(fp);
+	if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
+	    fc->flogi_oxid == ntohs(fh->fh_ox_id)) {
+		/*
+		 * FLOGI accepted.
+		 * If the src mac addr is FC_OUI-based, then we mark the
+		 * address_mode flag to use FC_OUI-based Ethernet DA.
+		 * Otherwise we use the FCoE gateway addr
+		 */
+		if (!compare_ether_addr(sa, (u8[6]) FC_FCOE_FLOGI_MAC)) {
+			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
+		} else {
+			memcpy(fc->dest_addr, sa, ETH_ALEN);
+			fc->address_mode = FCOE_GW_ADDR_MODE;
+		}
+
+		/*
+		 * Remove any previously-set unicast MAC filter.
+		 * Add secondary FCoE MAC address filter for our OUI.
+		 */
+		rtnl_lock();
+		if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+			dev_unicast_delete(fc->real_dev, fc->data_src_addr,
+					   ETH_ALEN);
+		fc_fcoe_set_mac(fc->data_src_addr, fh->fh_d_id);
+		dev_unicast_add(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+		rtnl_unlock();
+
+		fc->flogi_progress = 0;
+	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
+		/*
+		 * Save source MAC for point-to-point responses.
+		 */
+		memcpy(fc->dest_addr, sa, ETH_ALEN);
+		fc->address_mode = FCOE_GW_ADDR_MODE;
+	}
+}
+
+/**
+ * fcoe_watchdog() - fcoe timer callback
+ * @vp:
+ *
+ * This checks the pending queue length for fcoe and set lport qfull
+ * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the
+ * fcoe_hostlist.
+ *
+ * Returns: 0 for success
+ */
+void fcoe_watchdog(ulong vp)
+{
+	struct fcoe_softc *fc;
+
+	read_lock(&fcoe_hostlist_lock);
+	list_for_each_entry(fc, &fcoe_hostlist, list) {
+		if (fc->lp)
+			fcoe_check_wait_queue(fc->lp);
+	}
+	read_unlock(&fcoe_hostlist_lock);
+
+	fcoe_timer.expires = jiffies + (1 * HZ);
+	add_timer(&fcoe_timer);
+}
+
+
+/**
+ * fcoe_check_wait_queue() - put the skb into fcoe pending xmit queue
+ * @lp: the fc_port for this skb
+ * @skb: the associated skb to be xmitted
+ *
+ * This empties the wait_queue, dequeue the head of the wait_queue queue
+ * and calls fcoe_start_io() for each packet, if all skb have been
+ * transmitted, return qlen or -1 if a error occurs, then restore
+ * wait_queue and  try again later.
+ *
+ * The wait_queue is used when the skb transmit fails. skb will go
+ * in the wait_queue which will be emptied by the time function OR
+ * by the next skb transmit.
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_check_wait_queue(struct fc_lport *lp)
+{
+	struct fcoe_softc *fc = lport_priv(lp);
+	struct sk_buff *skb;
+	int rc = -1;
+
+	spin_lock_bh(&fc->fcoe_pending_queue.lock);
+	if (fc->fcoe_pending_queue_active)
+		goto out;
+	fc->fcoe_pending_queue_active = 1;
+
+	while (fc->fcoe_pending_queue.qlen) {
+		/* keep qlen > 0 until fcoe_start_io succeeds */
+		fc->fcoe_pending_queue.qlen++;
+		skb = __skb_dequeue(&fc->fcoe_pending_queue);
+
+		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+		rc = fcoe_start_io(skb);
+		spin_lock_bh(&fc->fcoe_pending_queue.lock);
+
+		if (rc) {
+			__skb_queue_head(&fc->fcoe_pending_queue, skb);
+			/* undo temporary increment above */
+			fc->fcoe_pending_queue.qlen--;
+			break;
+		}
+		/* undo temporary increment above */
+		fc->fcoe_pending_queue.qlen--;
+	}
+
+	if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
+		lp->qfull = 0;
+	fc->fcoe_pending_queue_active = 0;
+	rc = fc->fcoe_pending_queue.qlen;
+out:
+	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+	return rc;
+}
+
+/**
+ * fcoe_dev_setup() - setup link change notification interface
+ */
+static void fcoe_dev_setup()
+{
+	/*
+	 * here setup a interface specific wd time to
+	 * monitor the link state
+	 */
+	register_netdevice_notifier(&fcoe_notifier);
+}
+
+/**
+ * fcoe_dev_setup() - cleanup link change notification interface
+ */
+static void fcoe_dev_cleanup(void)
+{
+	unregister_netdevice_notifier(&fcoe_notifier);
+}
+
+/**
+ * fcoe_device_notification() - netdev event notification callback
+ * @notifier: context of the notification
+ * @event: type of event
+ * @ptr: fixed array for output parsed ifname
+ *
+ * This function is called by the ethernet driver in case of link change event
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_device_notification(struct notifier_block *notifier,
+				    ulong event, void *ptr)
+{
+	struct fc_lport *lp = NULL;
+	struct net_device *real_dev = ptr;
+	struct fcoe_softc *fc;
+	struct fcoe_dev_stats *stats;
+	u32 new_link_up;
+	u32 mfs;
+	int rc = NOTIFY_OK;
+
+	read_lock(&fcoe_hostlist_lock);
+	list_for_each_entry(fc, &fcoe_hostlist, list) {
+		if (fc->real_dev == real_dev) {
+			lp = fc->lp;
+			break;
+		}
+	}
+	read_unlock(&fcoe_hostlist_lock);
+	if (lp == NULL) {
+		rc = NOTIFY_DONE;
+		goto out;
+	}
+
+	new_link_up = lp->link_up;
+	switch (event) {
+	case NETDEV_DOWN:
+	case NETDEV_GOING_DOWN:
+		new_link_up = 0;
+		break;
+	case NETDEV_UP:
+	case NETDEV_CHANGE:
+		new_link_up = !fcoe_link_ok(lp);
+		break;
+	case NETDEV_CHANGEMTU:
+		mfs = fc->real_dev->mtu -
+			(sizeof(struct fcoe_hdr) +
+			 sizeof(struct fcoe_crc_eof));
+		if (mfs >= FC_MIN_MAX_FRAME)
+			fc_set_mfs(lp, mfs);
+		new_link_up = !fcoe_link_ok(lp);
+		break;
+	case NETDEV_REGISTER:
+		break;
+	default:
+		FC_DBG("unknown event %ld call", event);
+	}
+	if (lp->link_up != new_link_up) {
+		if (new_link_up)
+			fc_linkup(lp);
+		else {
+			stats = fc_lport_get_stats(lp);
+			stats->LinkFailureCount++;
+			fc_linkdown(lp);
+			fcoe_clean_pending_queue(lp);
+		}
+	}
+out:
+	return rc;
+}
+
+/**
+ * fcoe_if_to_netdev() - parse a name buffer to get netdev
+ * @ifname: fixed array for output parsed ifname
+ * @buffer: incoming buffer to be copied
+ *
+ * Returns: NULL or ptr to netdeive
+ */
+static struct net_device *fcoe_if_to_netdev(const char *buffer)
+{
+	char *cp;
+	char ifname[IFNAMSIZ + 2];
+
+	if (buffer) {
+		strlcpy(ifname, buffer, IFNAMSIZ);
+		cp = ifname + strlen(ifname);
+		while (--cp >= ifname && *cp == '\n')
+			*cp = '\0';
+		return dev_get_by_name(&init_net, ifname);
+	}
+	return NULL;
+}
+
+/**
+ * fcoe_netdev_to_module_owner() - finds out the nic drive moddule of the netdev
+ * @netdev: the target netdev
+ *
+ * Returns: ptr to the struct module, NULL for failure
+ */
+static struct module *
+fcoe_netdev_to_module_owner(const struct net_device *netdev)
+{
+	struct device *dev;
+
+	if (!netdev)
+		return NULL;
+
+	dev = netdev->dev.parent;
+	if (!dev)
+		return NULL;
+
+	if (!dev->driver)
+		return NULL;
+
+	return dev->driver->owner;
+}
+
+/**
+ * fcoe_ethdrv_get() - Hold the Ethernet driver
+ * @netdev: the target netdev
+ *
+ * Holds the Ethernet driver module by try_module_get() for
+ * the corresponding netdev.
+ *
+ * Returns: 0 for succsss
+ */
+static int fcoe_ethdrv_get(const struct net_device *netdev)
+{
+	struct module *owner;
+
+	owner = fcoe_netdev_to_module_owner(netdev);
+	if (owner) {
+		printk(KERN_DEBUG "fcoe:hold driver module %s for %s\n",
+		       module_name(owner), netdev->name);
+		return  try_module_get(owner);
+	}
+	return -ENODEV;
+}
+
+/**
+ * fcoe_ethdrv_put() - Release the Ethernet driver
+ * @netdev: the target netdev
+ *
+ * Releases the Ethernet driver module by module_put for
+ * the corresponding netdev.
+ *
+ * Returns: 0 for succsss
+ */
+static int fcoe_ethdrv_put(const struct net_device *netdev)
+{
+	struct module *owner;
+
+	owner = fcoe_netdev_to_module_owner(netdev);
+	if (owner) {
+		printk(KERN_DEBUG "fcoe:release driver module %s for %s\n",
+		       module_name(owner), netdev->name);
+		module_put(owner);
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/**
+ * fcoe_destroy() - handles the destroy from sysfs
+ * @buffer: expcted to be a eth if name
+ * @kp: associated kernel param
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
+{
+	int rc;
+	struct net_device *netdev;
+
+	netdev = fcoe_if_to_netdev(buffer);
+	if (!netdev) {
+		rc = -ENODEV;
+		goto out_nodev;
+	}
+	/* look for existing lport */
+	if (!fcoe_hostlist_lookup(netdev)) {
+		rc = -ENODEV;
+		goto out_putdev;
+	}
+	rc = fcoe_if_destroy(netdev);
+	if (rc) {
+		printk(KERN_ERR "fcoe: fcoe_if_destroy(%s) failed\n",
+		       netdev->name);
+		rc = -EIO;
+		goto out_putdev;
+	}
+	fcoe_ethdrv_put(netdev);
+	rc = 0;
+out_putdev:
+	dev_put(netdev);
+out_nodev:
+	return rc;
+}
+
+/**
+ * fcoe_create() - Handles the create call from sysfs
+ * @buffer: expcted to be a eth if name
+ * @kp: associated kernel param
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_create(const char *buffer, struct kernel_param *kp)
+{
+	int rc;
+	struct net_device *netdev;
+
+	netdev = fcoe_if_to_netdev(buffer);
+	if (!netdev) {
+		rc = -ENODEV;
+		goto out_nodev;
+	}
+	/* look for existing lport */
+	if (fcoe_hostlist_lookup(netdev)) {
+		rc = -EEXIST;
+		goto out_putdev;
+	}
+	fcoe_ethdrv_get(netdev);
+
+	rc = fcoe_if_create(netdev);
+	if (rc) {
+		printk(KERN_ERR "fcoe: fcoe_if_create(%s) failed\n",
+		       netdev->name);
+		fcoe_ethdrv_put(netdev);
+		rc = -EIO;
+		goto out_putdev;
+	}
+	rc = 0;
+out_putdev:
+	dev_put(netdev);
+out_nodev:
+	return rc;
+}
+
+module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(create, "string");
+MODULE_PARM_DESC(create, "Create fcoe port using net device passed in.");
+module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(destroy, "string");
+MODULE_PARM_DESC(destroy, "Destroy fcoe port");
+
+/**
+ * fcoe_link_ok() - Check if link is ok for the fc_lport
+ * @lp: ptr to the fc_lport
+ *
+ * Any permanently-disqualifying conditions have been previously checked.
+ * This also updates the speed setting, which may change with link for 100/1000.
+ *
+ * This function should probably be checking for PAUSE support at some point
+ * in the future. Currently Per-priority-pause is not determinable using
+ * ethtool, so we shouldn't be restrictive until that problem is resolved.
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ *
+ */
+int fcoe_link_ok(struct fc_lport *lp)
+{
+	struct fcoe_softc *fc = lport_priv(lp);
+	struct net_device *dev = fc->real_dev;
+	struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+	int rc = 0;
+
+	if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) {
+		dev = fc->phys_dev;
+		if (dev->ethtool_ops->get_settings) {
+			dev->ethtool_ops->get_settings(dev, &ecmd);
+			lp->link_supported_speeds &=
+				~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
+			if (ecmd.supported & (SUPPORTED_1000baseT_Half |
+					      SUPPORTED_1000baseT_Full))
+				lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
+			if (ecmd.supported & SUPPORTED_10000baseT_Full)
+				lp->link_supported_speeds |=
+					FC_PORTSPEED_10GBIT;
+			if (ecmd.speed == SPEED_1000)
+				lp->link_speed = FC_PORTSPEED_1GBIT;
+			if (ecmd.speed == SPEED_10000)
+				lp->link_speed = FC_PORTSPEED_10GBIT;
+		}
+	} else
+		rc = -1;
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(fcoe_link_ok);
+
+/**
+ * fcoe_percpu_clean() - Clear the pending skbs for an lport
+ * @lp: the fc_lport
+ */
+void fcoe_percpu_clean(struct fc_lport *lp)
+{
+	struct fcoe_percpu_s *pp;
+	struct fcoe_rcv_info *fr;
+	struct sk_buff_head *list;
+	struct sk_buff *skb, *next;
+	struct sk_buff *head;
+	unsigned int cpu;
+
+	for_each_possible_cpu(cpu) {
+		pp = &per_cpu(fcoe_percpu, cpu);
+		spin_lock_bh(&pp->fcoe_rx_list.lock);
+		list = &pp->fcoe_rx_list;
+		head = list->next;
+		for (skb = head; skb != (struct sk_buff *)list;
+		     skb = next) {
+			next = skb->next;
+			fr = fcoe_dev_from_skb(skb);
+			if (fr->fr_dev == lp) {
+				__skb_unlink(skb, list);
+				kfree_skb(skb);
+			}
+		}
+		spin_unlock_bh(&pp->fcoe_rx_list.lock);
+	}
+}
+EXPORT_SYMBOL_GPL(fcoe_percpu_clean);
+
+/**
+ * fcoe_clean_pending_queue() - Dequeue a skb and free it
+ * @lp: the corresponding fc_lport
+ *
+ * Returns: none
+ */
+void fcoe_clean_pending_queue(struct fc_lport *lp)
+{
+	struct fcoe_softc  *fc = lport_priv(lp);
+	struct sk_buff *skb;
+
+	spin_lock_bh(&fc->fcoe_pending_queue.lock);
+	while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
+		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+		kfree_skb(skb);
+		spin_lock_bh(&fc->fcoe_pending_queue.lock);
+	}
+	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+}
+EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
+
+/**
+ * libfc_host_alloc() - Allocate a Scsi_Host with room for the fc_lport
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ * TODO: to libfc?
+ */
+static inline struct Scsi_Host *
+libfc_host_alloc(struct scsi_host_template *sht, int priv_size)
+{
+	return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
+}
+
+/**
+ * fcoe_host_alloc() - Allocate a Scsi_Host with room for the fcoe_softc
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ */
+struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *sht, int priv_size)
+{
+	return libfc_host_alloc(sht, sizeof(struct fcoe_softc) + priv_size);
+}
+EXPORT_SYMBOL_GPL(fcoe_host_alloc);
+
+/**
+ * fcoe_reset() - Resets the fcoe
+ * @shost: shost the reset is from
+ *
+ * Returns: always 0
+ */
+int fcoe_reset(struct Scsi_Host *shost)
+{
+	struct fc_lport *lport = shost_priv(shost);
+	fc_lport_reset(lport);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_reset);
+
+/**
+ * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN.
+ * @mac: mac address
+ * @scheme: check port
+ * @port: port indicator for converting
+ *
+ * Returns: u64 fc world wide name
+ */
+u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
+		      unsigned int scheme, unsigned int port)
+{
+	u64 wwn;
+	u64 host_mac;
+
+	/* The MAC is in NO, so flip only the low 48 bits */
+	host_mac = ((u64) mac[0] << 40) |
+		((u64) mac[1] << 32) |
+		((u64) mac[2] << 24) |
+		((u64) mac[3] << 16) |
+		((u64) mac[4] << 8) |
+		(u64) mac[5];
+
+	WARN_ON(host_mac >= (1ULL << 48));
+	wwn = host_mac | ((u64) scheme << 60);
+	switch (scheme) {
+	case 1:
+		WARN_ON(port != 0);
+		break;
+	case 2:
+		WARN_ON(port >= 0xfff);
+		wwn |= (u64) port << 48;
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+
+	return wwn;
+}
+EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
+
+/**
+ * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device
+ * @device: this is currently ptr to net_device
+ *
+ * Returns: NULL or the located fcoe_softc
+ */
+static struct fcoe_softc *
+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);
+			return fc;
+		}
+	}
+	read_unlock(&fcoe_hostlist_lock);
+	return NULL;
+}
+
+/**
+ * fcoe_hostlist_lookup() - Find the corresponding lport by netdev
+ * @netdev: ptr to net_device
+ *
+ * Returns: 0 for success
+ */
+struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
+{
+	struct fcoe_softc *fc;
+
+	fc = fcoe_hostlist_lookup_softc(netdev);
+
+	return (fc) ? fc->lp : NULL;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup);
+
+/**
+ * fcoe_hostlist_add() - Add a lport to lports list
+ * @lp: ptr to the fc_lport to badded
+ *
+ * Returns: 0 for success
+ */
+int fcoe_hostlist_add(const struct fc_lport *lp)
+{
+	struct fcoe_softc *fc;
+
+	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;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_add);
+
+/**
+ * fcoe_hostlist_remove() - remove a lport from lports list
+ * @lp: ptr to the fc_lport to badded
+ *
+ * Returns: 0 for success
+ */
+int fcoe_hostlist_remove(const struct fc_lport *lp)
+{
+	struct fcoe_softc *fc;
+
+	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);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_remove);
+
+/**
+ * fcoe_libfc_config() - sets up libfc related properties for lport
+ * @lp: ptr to the fc_lport
+ * @tt: libfc function template
+ *
+ * Returns : 0 for success
+ */
+int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
+{
+	/* Set the function pointers set by the LLDD */
+	memcpy(&lp->tt, tt, sizeof(*tt));
+	if (fc_fcp_init(lp))
+		return -ENOMEM;
+	fc_exch_init(lp);
+	fc_elsct_init(lp);
+	fc_lport_init(lp);
+	fc_rport_init(lp);
+	fc_disc_init(lp);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
+/**
+ * fcoe_init() - fcoe module loading initialization
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int __init fcoe_init(void)
+{
+	unsigned int cpu;
+	int rc = 0;
+	struct fcoe_percpu_s *p;
+
+	INIT_LIST_HEAD(&fcoe_hostlist);
+	rwlock_init(&fcoe_hostlist_lock);
+
+	for_each_possible_cpu(cpu) {
+		p = &per_cpu(fcoe_percpu, cpu);
+		skb_queue_head_init(&p->fcoe_rx_list);
+	}
+
+	for_each_online_cpu(cpu)
+		fcoe_percpu_thread_create(cpu);
+
+	/* Initialize per CPU interrupt thread */
+	rc = register_hotcpu_notifier(&fcoe_cpu_notifier);
+	if (rc)
+		goto out_free;
+
+	/* Setup link change notification */
+	fcoe_dev_setup();
+
+	setup_timer(&fcoe_timer, fcoe_watchdog, 0);
+
+	mod_timer(&fcoe_timer, jiffies + (10 * HZ));
+
+	fcoe_if_init();
+
+	return 0;
+
+out_free:
+	for_each_online_cpu(cpu) {
+		fcoe_percpu_thread_destroy(cpu);
+	}
+
+	return rc;
+}
+module_init(fcoe_init);
+
+/**
+ * fcoe_exit() - fcoe module unloading cleanup
+ *
+ * Returns 0 on success, negative on failure
+ */
+static void __exit fcoe_exit(void)
+{
+	unsigned int cpu;
+	struct fcoe_softc *fc, *tmp;
+
+	fcoe_dev_cleanup();
+
+	/* Stop the timer */
+	del_timer_sync(&fcoe_timer);
+
+	/* releases the associated fcoe hosts */
+	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
+		fcoe_if_destroy(fc->real_dev);
+
+	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
+
+	for_each_online_cpu(cpu) {
+		fcoe_percpu_thread_destroy(cpu);
+	}
+
+	/* detach from scsi transport */
+	fcoe_if_exit();
+}
+module_exit(fcoe_exit);
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
deleted file mode 100644
index a81a8ec..0000000
--- a/drivers/scsi/fcoe/libfcoe.c
+++ /dev/null
@@ -1,1986 +0,0 @@
-/*
- * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Maintained at www.Open-FCoE.org
- */
-
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/if_ether.h>
-#include <linux/if_vlan.h>
-#include <linux/kthread.h>
-#include <linux/crc32.h>
-#include <linux/cpu.h>
-#include <linux/fs.h>
-#include <linux/sysfs.h>
-#include <linux/ctype.h>
-#include <scsi/scsi_tcq.h>
-#include <scsi/scsicam.h>
-#include <scsi/scsi_transport.h>
-#include <scsi/scsi_transport_fc.h>
-#include <net/rtnetlink.h>
-
-#include <scsi/fc/fc_encaps.h>
-
-#include <scsi/libfc.h>
-#include <scsi/fc_frame.h>
-#include <scsi/libfcoe.h>
-
-static int debug_fcoe;
-
-#define FCOE_MAX_QUEUE_DEPTH  256
-#define FCOE_LOW_QUEUE_DEPTH  32
-
-/* destination address mode */
-#define FCOE_GW_ADDR_MODE	    0x00
-#define FCOE_FCOUI_ADDR_MODE	    0x01
-
-#define FCOE_WORD_TO_BYTE  4
-
-#define FCOE_VERSION	"0.1"
-#define	FCOE_NAME	"fcoe"
-#define	FCOE_VENDOR	"Open-FCoE.org"
-
-#define FCOE_MAX_LUN		255
-#define FCOE_MAX_FCP_TARGET	256
-
-#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
-
-#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
-#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
-
-MODULE_AUTHOR("Open-FCoE.org");
-MODULE_DESCRIPTION("FCoE");
-MODULE_LICENSE("GPL");
-
-/* fcoe host list */
-LIST_HEAD(fcoe_hostlist);
-DEFINE_RWLOCK(fcoe_hostlist_lock);
-DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
-DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
-
-
-/* Function Prototyes */
-static int fcoe_check_wait_queue(struct fc_lport *);
-static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
-static int fcoe_device_notification(struct notifier_block *, ulong, void *);
-static void fcoe_dev_setup(void);
-static void fcoe_dev_cleanup(void);
-
-/* notification function from net device */
-static struct notifier_block fcoe_notifier = {
-	.notifier_call = fcoe_device_notification,
-};
-
-static struct scsi_transport_template *scsi_transport_fcoe_sw;
-
-struct fc_function_template fcoe_transport_function = {
-	.show_host_node_name = 1,
-	.show_host_port_name = 1,
-	.show_host_supported_classes = 1,
-	.show_host_supported_fc4s = 1,
-	.show_host_active_fc4s = 1,
-	.show_host_maxframe_size = 1,
-
-	.show_host_port_id = 1,
-	.show_host_supported_speeds = 1,
-	.get_host_speed = fc_get_host_speed,
-	.show_host_speed = 1,
-	.show_host_port_type = 1,
-	.get_host_port_state = fc_get_host_port_state,
-	.show_host_port_state = 1,
-	.show_host_symbolic_name = 1,
-
-	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
-	.show_rport_maxframe_size = 1,
-	.show_rport_supported_classes = 1,
-
-	.show_host_fabric_name = 1,
-	.show_starget_node_name = 1,
-	.show_starget_port_name = 1,
-	.show_starget_port_id = 1,
-	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
-	.show_rport_dev_loss_tmo = 1,
-	.get_fc_host_stats = fc_get_host_stats,
-	.issue_fc_host_lip = fcoe_reset,
-
-	.terminate_rport_io = fc_rport_terminate_io,
-};
-
-static struct scsi_host_template fcoe_shost_template = {
-	.module = THIS_MODULE,
-	.name = "FCoE Driver",
-	.proc_name = FCOE_NAME,
-	.queuecommand = fc_queuecommand,
-	.eh_abort_handler = fc_eh_abort,
-	.eh_device_reset_handler = fc_eh_device_reset,
-	.eh_host_reset_handler = fc_eh_host_reset,
-	.slave_alloc = fc_slave_alloc,
-	.change_queue_depth = fc_change_queue_depth,
-	.change_queue_type = fc_change_queue_type,
-	.this_id = -1,
-	.cmd_per_lun = 32,
-	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
-	.use_clustering = ENABLE_CLUSTERING,
-	.sg_tablesize = SG_ALL,
-	.max_sectors = 0xffff,
-};
-
-/**
- * fcoe_lport_config() - sets up the fc_lport
- * @lp: ptr to the fc_lport
- * @shost: ptr to the parent scsi host
- *
- * Returns: 0 for success
- */
-static int fcoe_lport_config(struct fc_lport *lp)
-{
-	lp->link_up = 0;
-	lp->qfull = 0;
-	lp->max_retry_count = 3;
-	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
-	lp->r_a_tov = 2 * 2 * 1000;
-	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
-			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
-
-	fc_lport_init_stats(lp);
-
-	/* lport fc_lport related configuration */
-	fc_lport_config(lp);
-
-	/* offload related configuration */
-	lp->crc_offload = 0;
-	lp->seq_offload = 0;
-	lp->lro_enabled = 0;
-	lp->lro_xid = 0;
-	lp->lso_max = 0;
-
-	return 0;
-}
-
-/**
- * fcoe_netdev_config() - Set up netdev for SW FCoE
- * @lp : ptr to the fc_lport
- * @netdev : ptr to the associated netdevice struct
- *
- * Must be called after fcoe_lport_config() as it will use lport mutex
- *
- * Returns : 0 for success
- */
-static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
-{
-	u32 mfs;
-	u64 wwnn, wwpn;
-	struct fcoe_softc *fc;
-	u8 flogi_maddr[ETH_ALEN];
-
-	/* Setup lport private data to point to fcoe softc */
-	fc = lport_priv(lp);
-	fc->lp = lp;
-	fc->real_dev = netdev;
-	fc->phys_dev = netdev;
-
-	/* Require support for get_pauseparam ethtool op. */
-	if (netdev->priv_flags & IFF_802_1Q_VLAN)
-		fc->phys_dev = vlan_dev_real_dev(netdev);
-
-	/* Do not support for bonding device */
-	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
-	    (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
-	    (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
-		return -EOPNOTSUPP;
-	}
-
-	/*
-	 * Determine max frame size based on underlying device and optional
-	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
-	 * will return 0, so do this first.
-	 */
-	mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
-				   sizeof(struct fcoe_crc_eof));
-	if (fc_set_mfs(lp, mfs))
-		return -EINVAL;
-
-	if (!fcoe_link_ok(lp))
-		lp->link_up = 1;
-
-	/* offload features support */
-	if (fc->real_dev->features & NETIF_F_SG)
-		lp->sg_supp = 1;
-
-#ifdef NETIF_F_FCOE_CRC
-	if (netdev->features & NETIF_F_FCOE_CRC) {
-		lp->crc_offload = 1;
-		printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
-		       netdev->name);
-	}
-#endif
-#ifdef NETIF_F_FSO
-	if (netdev->features & NETIF_F_FSO) {
-		lp->seq_offload = 1;
-		lp->lso_max = netdev->gso_max_size;
-		printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
-		       netdev->name, lp->lso_max);
-	}
-#endif
-	if (netdev->fcoe_ddp_xid) {
-		lp->lro_enabled = 1;
-		lp->lro_xid = netdev->fcoe_ddp_xid;
-		printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
-		       netdev->name, lp->lro_xid);
-	}
-	skb_queue_head_init(&fc->fcoe_pending_queue);
-	fc->fcoe_pending_queue_active = 0;
-
-	/* setup Source Mac Address */
-	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
-	       fc->real_dev->addr_len);
-
-	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
-	fc_set_wwnn(lp, wwnn);
-	/* XXX - 3rd arg needs to be vlan id */
-	wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
-	fc_set_wwpn(lp, wwpn);
-
-	/*
-	 * Add FCoE MAC address as second unicast MAC address
-	 * or enter promiscuous mode if not capable of listening
-	 * for multiple unicast MACs.
-	 */
-	rtnl_lock();
-	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-	dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
-	rtnl_unlock();
-
-	/*
-	 * setup the receive function from ethernet driver
-	 * on the ethertype for the given device
-	 */
-	fc->fcoe_packet_type.func = fcoe_rcv;
-	fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
-	fc->fcoe_packet_type.dev = fc->real_dev;
-	dev_add_pack(&fc->fcoe_packet_type);
-
-	return 0;
-}
-
-/**
- * fcoe_shost_config() - Sets up fc_lport->host
- * @lp : ptr to the fc_lport
- * @shost : ptr to the associated scsi host
- * @dev : device associated to scsi host
- *
- * Must be called after fcoe_lport_config() and fcoe_netdev_config()
- *
- * Returns : 0 for success
- */
-static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
-				struct device *dev)
-{
-	int rc = 0;
-
-	/* lport scsi host config */
-	lp->host = shost;
-
-	lp->host->max_lun = FCOE_MAX_LUN;
-	lp->host->max_id = FCOE_MAX_FCP_TARGET;
-	lp->host->max_channel = 0;
-	lp->host->transportt = scsi_transport_fcoe_sw;
-
-	/* add the new host to the SCSI-ml */
-	rc = scsi_add_host(lp->host, dev);
-	if (rc) {
-		FC_DBG("fcoe_shost_config:error on scsi_add_host\n");
-		return rc;
-	}
-	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
-		FCOE_NAME, FCOE_VERSION,
-		fcoe_netdev(lp)->name);
-
-	return 0;
-}
-
-/**
- * fcoe_em_config() - 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)
-{
-	BUG_ON(lp->emp);
-
-	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
-				    FCOE_MIN_XID, FCOE_MAX_XID);
-	if (!lp->emp)
-		return -ENOMEM;
-
-	return 0;
-}
-
-/**
- * fcoe_if_destroy() - FCoE software HBA tear-down function
- * @netdev: ptr to the associated net_device
- *
- * Returns: 0 if link is OK for use by FCoE.
- */
-static int fcoe_if_destroy(struct net_device *netdev)
-{
-	struct fc_lport *lp = NULL;
-	struct fcoe_softc *fc;
-	u8 flogi_maddr[ETH_ALEN];
-
-	BUG_ON(!netdev);
-
-	printk(KERN_DEBUG "fcoe_if_destroy:interface on %s\n",
-	       netdev->name);
-
-	lp = fcoe_hostlist_lookup(netdev);
-	if (!lp)
-		return -ENODEV;
-
-	fc = lport_priv(lp);
-
-	/* Logout of the fabric */
-	fc_fabric_logoff(lp);
-
-	/* Remove the instance from fcoe's list */
-	fcoe_hostlist_remove(lp);
-
-	/* Don't listen for Ethernet packets anymore */
-	dev_remove_pack(&fc->fcoe_packet_type);
-
-	/* Cleanup the fc_lport */
-	fc_lport_destroy(lp);
-	fc_fcp_destroy(lp);
-
-	/* Detach from the scsi-ml */
-	fc_remove_host(lp->host);
-	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);
-
-	/* Delete secondary MAC addresses */
-	rtnl_lock();
-	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
-	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
-		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
-	rtnl_unlock();
-
-	/* Free the per-CPU revieve threads */
-	fcoe_percpu_clean(lp);
-
-	/* Free existing skbs */
-	fcoe_clean_pending_queue(lp);
-
-	/* Free memory used by statistical counters */
-	fc_lport_free_stats(lp);
-
-	/* Release the net_device and Scsi_Host */
-	dev_put(fc->real_dev);
-	scsi_host_put(lp->host);
-
-	return 0;
-}
-
-/*
- * fcoe_ddp_setup - calls LLD's ddp_setup through net_device
- * @lp:	the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- * @sgl: the scatterlist describing this transfer
- * @sgc: number of sg items
- *
- * Returns : 0 no ddp
- */
-static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid,
-			     struct scatterlist *sgl, unsigned int sgc)
-{
-	struct net_device *n = fcoe_netdev(lp);
-
-	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
-		return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
-
-	return 0;
-}
-
-/*
- * fcoe_ddp_done - calls LLD's ddp_done through net_device
- * @lp:	the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- *
- * Returns : the length of data that have been completed by ddp
- */
-static int fcoe_ddp_done(struct fc_lport *lp, u16 xid)
-{
-	struct net_device *n = fcoe_netdev(lp);
-
-	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
-		return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
-	return 0;
-}
-
-static struct libfc_function_template fcoe_libfc_fcn_templ = {
-	.frame_send = fcoe_xmit,
-	.ddp_setup = fcoe_ddp_setup,
-	.ddp_done = fcoe_ddp_done,
-};
-
-/**
- * fcoe_if_create() - this function creates the fcoe interface
- * @netdev: pointer the associated netdevice
- *
- * Creates fc_lport struct and scsi_host for lport, configures lport
- * and starts fabric login.
- *
- * Returns : 0 on success
- */
-static int fcoe_if_create(struct net_device *netdev)
-{
-	int rc;
-	struct fc_lport *lp = NULL;
-	struct fcoe_softc *fc;
-	struct Scsi_Host *shost;
-
-	BUG_ON(!netdev);
-
-	printk(KERN_DEBUG "fcoe_if_create:interface on %s\n",
-	       netdev->name);
-
-	lp = fcoe_hostlist_lookup(netdev);
-	if (lp)
-		return -EEXIST;
-
-	shost = fcoe_host_alloc(&fcoe_shost_template,
-				sizeof(struct fcoe_softc));
-	if (!shost) {
-		FC_DBG("Could not allocate host structure\n");
-		return -ENOMEM;
-	}
-	lp = shost_priv(shost);
-	fc = lport_priv(lp);
-
-	/* configure fc_lport, e.g., em */
-	rc = fcoe_lport_config(lp);
-	if (rc) {
-		FC_DBG("Could not configure lport\n");
-		goto out_host_put;
-	}
-
-	/* configure lport network properties */
-	rc = fcoe_netdev_config(lp, netdev);
-	if (rc) {
-		FC_DBG("Could not configure netdev for lport\n");
-		goto out_host_put;
-	}
-
-	/* configure lport scsi host properties */
-	rc = fcoe_shost_config(lp, shost, &netdev->dev);
-	if (rc) {
-		FC_DBG("Could not configure shost for lport\n");
-		goto out_host_put;
-	}
-
-	/* lport exch manager allocation */
-	rc = fcoe_em_config(lp);
-	if (rc) {
-		FC_DBG("Could not configure em for lport\n");
-		goto out_host_put;
-	}
-
-	/* Initialize the library */
-	rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ);
-	if (rc) {
-		FC_DBG("Could not configure libfc for lport!\n");
-		goto out_lp_destroy;
-	}
-
-	/* add to lports list */
-	fcoe_hostlist_add(lp);
-
-	lp->boot_time = jiffies;
-
-	fc_fabric_login(lp);
-
-	dev_hold(netdev);
-
-	return rc;
-
-out_lp_destroy:
-	fc_exch_mgr_free(lp->emp); /* Free the EM */
-out_host_put:
-	scsi_host_put(lp->host);
-	return rc;
-}
-
-/**
- * fcoe_if_init() - attach to scsi transport
- *
- * Returns : 0 on success
- */
-static int __init fcoe_if_init(void)
-{
-	/* attach to scsi transport */
-	scsi_transport_fcoe_sw =
-		fc_attach_transport(&fcoe_transport_function);
-
-	if (!scsi_transport_fcoe_sw) {
-		printk(KERN_ERR "fcoe_init:fc_attach_transport() failed\n");
-		return -ENODEV;
-	}
-
-	return 0;
-}
-
-/**
- * fcoe_if_exit() - detach from scsi transport
- *
- * Returns : 0 on success
- */
-int __exit fcoe_if_exit(void)
-{
-	fc_release_transport(scsi_transport_fcoe_sw);
-	return 0;
-}
-
-/**
- * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
- * @cpu: cpu index for the online cpu
- */
-static void fcoe_percpu_thread_create(unsigned int cpu)
-{
-	struct fcoe_percpu_s *p;
-	struct task_struct *thread;
-
-	p = &per_cpu(fcoe_percpu, cpu);
-
-	thread = kthread_create(fcoe_percpu_receive_thread,
-				(void *)p, "fcoethread/%d", cpu);
-
-	if (likely(!IS_ERR(p->thread))) {
-		kthread_bind(thread, cpu);
-		wake_up_process(thread);
-
-		spin_lock_bh(&p->fcoe_rx_list.lock);
-		p->thread = thread;
-		spin_unlock_bh(&p->fcoe_rx_list.lock);
-	}
-}
-
-/**
- * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
- * @cpu: cpu index the rx thread is to be removed
- *
- * Destroys a per-CPU Rx thread. Any pending skbs are moved to the
- * current CPU's Rx thread. If the thread being destroyed is bound to
- * the CPU processing this context the skbs will be freed.
- */
-static void fcoe_percpu_thread_destroy(unsigned int cpu)
-{
-	struct fcoe_percpu_s *p;
-	struct task_struct *thread;
-	struct page *crc_eof;
-	struct sk_buff *skb;
-#ifdef CONFIG_SMP
-	struct fcoe_percpu_s *p0;
-	unsigned targ_cpu = smp_processor_id();
-#endif /* CONFIG_SMP */
-
-	printk(KERN_DEBUG "fcoe: Destroying receive thread for CPU %d\n", cpu);
-
-	/* Prevent any new skbs from being queued for this CPU. */
-	p = &per_cpu(fcoe_percpu, cpu);
-	spin_lock_bh(&p->fcoe_rx_list.lock);
-	thread = p->thread;
-	p->thread = NULL;
-	crc_eof = p->crc_eof_page;
-	p->crc_eof_page = NULL;
-	p->crc_eof_offset = 0;
-	spin_unlock_bh(&p->fcoe_rx_list.lock);
-
-#ifdef CONFIG_SMP
-	/*
-	 * Don't bother moving the skb's if this context is running
-	 * on the same CPU that is having its thread destroyed. This
-	 * can easily happen when the module is removed.
-	 */
-	if (cpu != targ_cpu) {
-		p0 = &per_cpu(fcoe_percpu, targ_cpu);
-		spin_lock_bh(&p0->fcoe_rx_list.lock);
-		if (p0->thread) {
-			FC_DBG("Moving frames from CPU %d to CPU %d\n",
-			       cpu, targ_cpu);
-
-			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-				__skb_queue_tail(&p0->fcoe_rx_list, skb);
-			spin_unlock_bh(&p0->fcoe_rx_list.lock);
-		} else {
-			/*
-			 * The targeted CPU is not initialized and cannot accept
-			 * new  skbs. Unlock the targeted CPU and drop the skbs
-			 * on the CPU that is going offline.
-			 */
-			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-				kfree_skb(skb);
-			spin_unlock_bh(&p0->fcoe_rx_list.lock);
-		}
-	} else {
-		/*
-		 * This scenario occurs when the module is being removed
-		 * and all threads are being destroyed. skbs will continue
-		 * to be shifted from the CPU thread that is being removed
-		 * to the CPU thread associated with the CPU that is processing
-		 * the module removal. Once there is only one CPU Rx thread it
-		 * will reach this case and we will drop all skbs and later
-		 * stop the thread.
-		 */
-		spin_lock_bh(&p->fcoe_rx_list.lock);
-		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-			kfree_skb(skb);
-		spin_unlock_bh(&p->fcoe_rx_list.lock);
-	}
-#else
-	/*
-	 * This a non-SMP scenario where the singluar Rx thread is
-	 * being removed. Free all skbs and stop the thread.
-	 */
-	spin_lock_bh(&p->fcoe_rx_list.lock);
-	while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-		kfree_skb(skb);
-	spin_unlock_bh(&p->fcoe_rx_list.lock);
-#endif
-
-	if (thread)
-		kthread_stop(thread);
-
-	if (crc_eof)
-		put_page(crc_eof);
-}
-
-/**
- * fcoe_cpu_callback() - fcoe cpu hotplug event callback
- * @nfb: callback data block
- * @action: event triggering the callback
- * @hcpu: index for the cpu of this event
- *
- * This creates or destroys per cpu data for fcoe
- *
- * Returns NOTIFY_OK always.
- */
-static int fcoe_cpu_callback(struct notifier_block *nfb,
-			     unsigned long action, void *hcpu)
-{
-	unsigned cpu = (unsigned long)hcpu;
-
-	switch (action) {
-	case CPU_ONLINE:
-	case CPU_ONLINE_FROZEN:
-		FC_DBG("CPU %x online: Create Rx thread\n", cpu);
-		fcoe_percpu_thread_create(cpu);
-		break;
-	case CPU_DEAD:
-	case CPU_DEAD_FROZEN:
-		FC_DBG("CPU %x offline: Remove Rx thread\n", cpu);
-		fcoe_percpu_thread_destroy(cpu);
-		break;
-	default:
-		break;
-	}
-	return NOTIFY_OK;
-}
-
-static struct notifier_block fcoe_cpu_notifier = {
-	.notifier_call = fcoe_cpu_callback,
-};
-
-/**
- * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ
- * @skb: the receive skb
- * @dev: associated net device
- * @ptype: context
- * @odldev: last device
- *
- * this function will receive the packet and build fc frame and pass it up
- *
- * Returns: 0 for success
- */
-int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
-	     struct packet_type *ptype, struct net_device *olddev)
-{
-	struct fc_lport *lp;
-	struct fcoe_rcv_info *fr;
-	struct fcoe_softc *fc;
-	struct fc_frame_header *fh;
-	struct fcoe_percpu_s *fps;
-	unsigned short oxid;
-	unsigned int cpu = 0;
-
-	fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
-	lp = fc->lp;
-	if (unlikely(lp == NULL)) {
-		FC_DBG("cannot find hba structure");
-		goto err2;
-	}
-
-	if (unlikely(debug_fcoe)) {
-		FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p "
-		       "end:%p sum:%d dev:%s", skb->len, skb->data_len,
-		       skb->head, skb->data, skb_tail_pointer(skb),
-		       skb_end_pointer(skb), skb->csum,
-		       skb->dev ? skb->dev->name : "<NULL>");
-
-	}
-
-	/* check for FCOE packet type */
-	if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
-		FC_DBG("wrong FC type frame");
-		goto err;
-	}
-
-	/*
-	 * Check for minimum frame length, and make sure required FCoE
-	 * and FC headers are pulled into the linear data area.
-	 */
-	if (unlikely((skb->len < FCOE_MIN_FRAME) ||
-	    !pskb_may_pull(skb, FCOE_HEADER_LEN)))
-		goto err;
-
-	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.
-	 */
-	cpu = oxid & (num_online_cpus() - 1);
-#endif
-
-	fps = &per_cpu(fcoe_percpu, cpu);
-	spin_lock_bh(&fps->fcoe_rx_list.lock);
-	if (unlikely(!fps->thread)) {
-		/*
-		 * The targeted CPU is not ready, let's target
-		 * the first CPU now. For non-SMP systems this
-		 * will check the same CPU twice.
-		 */
-		FC_DBG("CPU is online, but no receive thread ready "
-		       "for incoming skb- using first online CPU.\n");
-
-		spin_unlock_bh(&fps->fcoe_rx_list.lock);
-		cpu = first_cpu(cpu_online_map);
-		fps = &per_cpu(fcoe_percpu, cpu);
-		spin_lock_bh(&fps->fcoe_rx_list.lock);
-		if (!fps->thread) {
-			spin_unlock_bh(&fps->fcoe_rx_list.lock);
-			goto err;
-		}
-	}
-
-	/*
-	 * We now have a valid CPU that we're targeting for
-	 * this skb. We also have this receive thread locked,
-	 * so we're free to queue skbs into it's queue.
-	 */
-	__skb_queue_tail(&fps->fcoe_rx_list, skb);
-	if (fps->fcoe_rx_list.qlen == 1)
-		wake_up_process(fps->thread);
-
-	spin_unlock_bh(&fps->fcoe_rx_list.lock);
-
-	return 0;
-err:
-	fc_lport_get_stats(lp)->ErrorFrames++;
-
-err2:
-	kfree_skb(skb);
-	return -1;
-}
-EXPORT_SYMBOL_GPL(fcoe_rcv);
-
-/**
- * fcoe_start_io() - pass to netdev to start xmit for fcoe
- * @skb: the skb to be xmitted
- *
- * Returns: 0 for success
- */
-static inline int fcoe_start_io(struct sk_buff *skb)
-{
-	int rc;
-
-	skb_get(skb);
-	rc = dev_queue_xmit(skb);
-	if (rc != 0)
-		return rc;
-	kfree_skb(skb);
-	return 0;
-}
-
-/**
- * fcoe_get_paged_crc_eof() - in case we need alloc a page for crc_eof
- * @skb: the skb to be xmitted
- * @tlen: total len
- *
- * Returns: 0 for success
- */
-static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
-{
-	struct fcoe_percpu_s *fps;
-	struct page *page;
-
-	fps = &get_cpu_var(fcoe_percpu);
-	page = fps->crc_eof_page;
-	if (!page) {
-		page = alloc_page(GFP_ATOMIC);
-		if (!page) {
-			put_cpu_var(fcoe_percpu);
-			return -ENOMEM;
-		}
-		fps->crc_eof_page = page;
-		fps->crc_eof_offset = 0;
-	}
-
-	get_page(page);
-	skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page,
-			   fps->crc_eof_offset, tlen);
-	skb->len += tlen;
-	skb->data_len += tlen;
-	skb->truesize += tlen;
-	fps->crc_eof_offset += sizeof(struct fcoe_crc_eof);
-
-	if (fps->crc_eof_offset >= PAGE_SIZE) {
-		fps->crc_eof_page = NULL;
-		fps->crc_eof_offset = 0;
-		put_page(page);
-	}
-	put_cpu_var(fcoe_percpu);
-	return 0;
-}
-
-/**
- * fcoe_fc_crc() - calculates FC CRC in this fcoe skb
- * @fp: the fc_frame containg data to be checksummed
- *
- * This uses crc32() to calculate the crc for fc frame
- * Return   : 32 bit crc
- */
-u32 fcoe_fc_crc(struct fc_frame *fp)
-{
-	struct sk_buff *skb = fp_skb(fp);
-	struct skb_frag_struct *frag;
-	unsigned char *data;
-	unsigned long off, len, clen;
-	u32 crc;
-	unsigned i;
-
-	crc = crc32(~0, skb->data, skb_headlen(skb));
-
-	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-		frag = &skb_shinfo(skb)->frags[i];
-		off = frag->page_offset;
-		len = frag->size;
-		while (len > 0) {
-			clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK));
-			data = kmap_atomic(frag->page + (off >> PAGE_SHIFT),
-					   KM_SKB_DATA_SOFTIRQ);
-			crc = crc32(crc, data + (off & ~PAGE_MASK), clen);
-			kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ);
-			off += clen;
-			len -= clen;
-		}
-	}
-	return crc;
-}
-EXPORT_SYMBOL_GPL(fcoe_fc_crc);
-
-/**
- * fcoe_xmit() - FCoE frame transmit function
- * @lp:	the associated local port
- * @fp: the fc_frame to be transmitted
- *
- * Return   : 0 for success
- */
-int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
-{
-	int wlen, rc = 0;
-	u32 crc;
-	struct ethhdr *eh;
-	struct fcoe_crc_eof *cp;
-	struct sk_buff *skb;
-	struct fcoe_dev_stats *stats;
-	struct fc_frame_header *fh;
-	unsigned int hlen;		/* header length implies the version */
-	unsigned int tlen;		/* trailer length */
-	unsigned int elen;		/* eth header, may include vlan */
-	int flogi_in_progress = 0;
-	struct fcoe_softc *fc;
-	u8 sof, eof;
-	struct fcoe_hdr *hp;
-
-	WARN_ON((fr_len(fp) % sizeof(u32)) != 0);
-
-	fc = lport_priv(lp);
-	/*
-	 * if it is a flogi then we need to learn gw-addr
-	 * and my own fcid
-	 */
-	fh = fc_frame_header_get(fp);
-	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
-		if (fc_frame_payload_op(fp) == ELS_FLOGI) {
-			fc->flogi_oxid = ntohs(fh->fh_ox_id);
-			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
-			fc->flogi_progress = 1;
-			flogi_in_progress = 1;
-		} else if (fc->flogi_progress && ntoh24(fh->fh_s_id) != 0) {
-			/*
-			 * Here we must've gotten an SID by accepting an FLOGI
-			 * from a point-to-point connection.  Switch to using
-			 * the source mac based on the SID.  The destination
-			 * MAC in this case would have been set by receving the
-			 * FLOGI.
-			 */
-			fc_fcoe_set_mac(fc->data_src_addr, fh->fh_s_id);
-			fc->flogi_progress = 0;
-		}
-	}
-
-	skb = fp_skb(fp);
-	sof = fr_sof(fp);
-	eof = fr_eof(fp);
-
-	elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ?
-		sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr);
-	hlen = sizeof(struct fcoe_hdr);
-	tlen = sizeof(struct fcoe_crc_eof);
-	wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
-
-	/* crc offload */
-	if (likely(lp->crc_offload)) {
-		skb->ip_summed = CHECKSUM_PARTIAL;
-		skb->csum_start = skb_headroom(skb);
-		skb->csum_offset = skb->len;
-		crc = 0;
-	} else {
-		skb->ip_summed = CHECKSUM_NONE;
-		crc = fcoe_fc_crc(fp);
-	}
-
-	/* copy fc crc and eof to the skb buff */
-	if (skb_is_nonlinear(skb)) {
-		skb_frag_t *frag;
-		if (fcoe_get_paged_crc_eof(skb, tlen)) {
-			kfree_skb(skb);
-			return -ENOMEM;
-		}
-		frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
-		cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
-			+ frag->page_offset;
-	} else {
-		cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
-	}
-
-	memset(cp, 0, sizeof(*cp));
-	cp->fcoe_eof = eof;
-	cp->fcoe_crc32 = cpu_to_le32(~crc);
-
-	if (skb_is_nonlinear(skb)) {
-		kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ);
-		cp = NULL;
-	}
-
-	/* adjust skb netowrk/transport offsets to match mac/fcoe/fc */
-	skb_push(skb, elen + hlen);
-	skb_reset_mac_header(skb);
-	skb_reset_network_header(skb);
-	skb->mac_len = elen;
-	skb->protocol = htons(ETH_P_FCOE);
-	skb->dev = fc->real_dev;
-
-	/* fill up mac and fcoe headers */
-	eh = eth_hdr(skb);
-	eh->h_proto = htons(ETH_P_FCOE);
-	if (fc->address_mode == FCOE_FCOUI_ADDR_MODE)
-		fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
-	else
-		/* insert GW address */
-		memcpy(eh->h_dest, fc->dest_addr, ETH_ALEN);
-
-	if (unlikely(flogi_in_progress))
-		memcpy(eh->h_source, fc->ctl_src_addr, ETH_ALEN);
-	else
-		memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN);
-
-	hp = (struct fcoe_hdr *)(eh + 1);
-	memset(hp, 0, sizeof(*hp));
-	if (FC_FCOE_VER)
-		FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
-	hp->fcoe_sof = sof;
-
-#ifdef NETIF_F_FSO
-	/* fcoe lso, mss is in max_payload which is non-zero for FCP data */
-	if (lp->seq_offload && fr_max_payload(fp)) {
-		skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
-		skb_shinfo(skb)->gso_size = fr_max_payload(fp);
-	} else {
-		skb_shinfo(skb)->gso_type = 0;
-		skb_shinfo(skb)->gso_size = 0;
-	}
-#endif
-	/* update tx stats: regardless if LLD fails */
-	stats = fc_lport_get_stats(lp);
-	stats->TxFrames++;
-	stats->TxWords += wlen;
-
-	/* send down to lld */
-	fr_dev(fp) = lp;
-	if (fc->fcoe_pending_queue.qlen)
-		rc = fcoe_check_wait_queue(lp);
-
-	if (rc == 0)
-		rc = fcoe_start_io(skb);
-
-	if (rc) {
-		spin_lock_bh(&fc->fcoe_pending_queue.lock);
-		__skb_queue_tail(&fc->fcoe_pending_queue, skb);
-		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-		if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
-			lp->qfull = 1;
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_xmit);
-
-/**
- * fcoe_percpu_receive_thread() - recv thread per cpu
- * @arg: ptr to the fcoe per cpu struct
- *
- * Return: 0 for success
- */
-int fcoe_percpu_receive_thread(void *arg)
-{
-	struct fcoe_percpu_s *p = arg;
-	u32 fr_len;
-	struct fc_lport *lp;
-	struct fcoe_rcv_info *fr;
-	struct fcoe_dev_stats *stats;
-	struct fc_frame_header *fh;
-	struct sk_buff *skb;
-	struct fcoe_crc_eof crc_eof;
-	struct fc_frame *fp;
-	u8 *mac = NULL;
-	struct fcoe_softc *fc;
-	struct fcoe_hdr *hp;
-
-	set_user_nice(current, -20);
-
-	while (!kthread_should_stop()) {
-
-		spin_lock_bh(&p->fcoe_rx_list.lock);
-		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) {
-			set_current_state(TASK_INTERRUPTIBLE);
-			spin_unlock_bh(&p->fcoe_rx_list.lock);
-			schedule();
-			set_current_state(TASK_RUNNING);
-			if (kthread_should_stop())
-				return 0;
-			spin_lock_bh(&p->fcoe_rx_list.lock);
-		}
-		spin_unlock_bh(&p->fcoe_rx_list.lock);
-		fr = fcoe_dev_from_skb(skb);
-		lp = fr->fr_dev;
-		if (unlikely(lp == NULL)) {
-			FC_DBG("invalid HBA Structure");
-			kfree_skb(skb);
-			continue;
-		}
-
-		if (unlikely(debug_fcoe)) {
-			FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p "
-			       "tail:%p end:%p sum:%d dev:%s",
-			       skb->len, skb->data_len,
-			       skb->head, skb->data, skb_tail_pointer(skb),
-			       skb_end_pointer(skb), skb->csum,
-			       skb->dev ? skb->dev->name : "<NULL>");
-		}
-
-		/*
-		 * Save source MAC address before discarding header.
-		 */
-		fc = lport_priv(lp);
-		if (unlikely(fc->flogi_progress))
-			mac = eth_hdr(skb)->h_source;
-
-		if (skb_is_nonlinear(skb))
-			skb_linearize(skb);	/* not ideal */
-
-		/*
-		 * Frame length checks and setting up the header pointers
-		 * was done in fcoe_rcv already.
-		 */
-		hp = (struct fcoe_hdr *) skb_network_header(skb);
-		fh = (struct fc_frame_header *) skb_transport_header(skb);
-
-		stats = fc_lport_get_stats(lp);
-		if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
-			if (stats->ErrorFrames < 5)
-				printk(KERN_WARNING "FCoE version "
-				       "mismatch: The frame has "
-				       "version %x, but the "
-				       "initiator supports version "
-				       "%x\n", FC_FCOE_DECAPS_VER(hp),
-				       FC_FCOE_VER);
-			stats->ErrorFrames++;
-			kfree_skb(skb);
-			continue;
-		}
-
-		skb_pull(skb, sizeof(struct fcoe_hdr));
-		fr_len = skb->len - sizeof(struct fcoe_crc_eof);
-
-		stats->RxFrames++;
-		stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
-
-		fp = (struct fc_frame *)skb;
-		fc_frame_init(fp);
-		fr_dev(fp) = lp;
-		fr_sof(fp) = hp->fcoe_sof;
-
-		/* Copy out the CRC and EOF trailer for access */
-		if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
-			kfree_skb(skb);
-			continue;
-		}
-		fr_eof(fp) = crc_eof.fcoe_eof;
-		fr_crc(fp) = crc_eof.fcoe_crc32;
-		if (pskb_trim(skb, fr_len)) {
-			kfree_skb(skb);
-			continue;
-		}
-
-		/*
-		 * We only check CRC if no offload is available and if it is
-		 * it's solicited data, in which case, the FCP layer would
-		 * check it during the copy.
-		 */
-		if (lp->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
-			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
-		else
-			fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
-
-		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);
-			continue;
-		}
-		if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
-			if (le32_to_cpu(fr_crc(fp)) !=
-			    ~crc32(~0, skb->data, fr_len)) {
-				if (debug_fcoe || stats->InvalidCRCCount < 5)
-					printk(KERN_WARNING "fcoe: dropping "
-					       "frame with CRC error\n");
-				stats->InvalidCRCCount++;
-				stats->ErrorFrames++;
-				fc_frame_free(fp);
-				continue;
-			}
-			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
-		}
-		/* non flogi and non data exchanges are handled here */
-		if (unlikely(fc->flogi_progress))
-			fcoe_recv_flogi(fc, fp, mac);
-		fc_exch_recv(lp, lp->emp, fp);
-	}
-	return 0;
-}
-
-/**
- * fcoe_recv_flogi() - flogi receive function
- * @fc: associated fcoe_softc
- * @fp: the recieved frame
- * @sa: the source address of this flogi
- *
- * This is responsible to parse the flogi response and sets the corresponding
- * mac address for the initiator, eitehr OUI based or GW based.
- *
- * Returns: none
- */
-static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa)
-{
-	struct fc_frame_header *fh;
-	u8 op;
-
-	fh = fc_frame_header_get(fp);
-	if (fh->fh_type != FC_TYPE_ELS)
-		return;
-	op = fc_frame_payload_op(fp);
-	if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
-	    fc->flogi_oxid == ntohs(fh->fh_ox_id)) {
-		/*
-		 * FLOGI accepted.
-		 * If the src mac addr is FC_OUI-based, then we mark the
-		 * address_mode flag to use FC_OUI-based Ethernet DA.
-		 * Otherwise we use the FCoE gateway addr
-		 */
-		if (!compare_ether_addr(sa, (u8[6]) FC_FCOE_FLOGI_MAC)) {
-			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
-		} else {
-			memcpy(fc->dest_addr, sa, ETH_ALEN);
-			fc->address_mode = FCOE_GW_ADDR_MODE;
-		}
-
-		/*
-		 * Remove any previously-set unicast MAC filter.
-		 * Add secondary FCoE MAC address filter for our OUI.
-		 */
-		rtnl_lock();
-		if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
-			dev_unicast_delete(fc->real_dev, fc->data_src_addr,
-					   ETH_ALEN);
-		fc_fcoe_set_mac(fc->data_src_addr, fh->fh_d_id);
-		dev_unicast_add(fc->real_dev, fc->data_src_addr, ETH_ALEN);
-		rtnl_unlock();
-
-		fc->flogi_progress = 0;
-	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
-		/*
-		 * Save source MAC for point-to-point responses.
-		 */
-		memcpy(fc->dest_addr, sa, ETH_ALEN);
-		fc->address_mode = FCOE_GW_ADDR_MODE;
-	}
-}
-
-/**
- * fcoe_watchdog() - fcoe timer callback
- * @vp:
- *
- * This checks the pending queue length for fcoe and set lport qfull
- * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the
- * fcoe_hostlist.
- *
- * Returns: 0 for success
- */
-void fcoe_watchdog(ulong vp)
-{
-	struct fcoe_softc *fc;
-
-	read_lock(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		if (fc->lp)
-			fcoe_check_wait_queue(fc->lp);
-	}
-	read_unlock(&fcoe_hostlist_lock);
-
-	fcoe_timer.expires = jiffies + (1 * HZ);
-	add_timer(&fcoe_timer);
-}
-
-
-/**
- * fcoe_check_wait_queue() - put the skb into fcoe pending xmit queue
- * @lp: the fc_port for this skb
- * @skb: the associated skb to be xmitted
- *
- * This empties the wait_queue, dequeue the head of the wait_queue queue
- * and calls fcoe_start_io() for each packet, if all skb have been
- * transmitted, return qlen or -1 if a error occurs, then restore
- * wait_queue and  try again later.
- *
- * The wait_queue is used when the skb transmit fails. skb will go
- * in the wait_queue which will be emptied by the time function OR
- * by the next skb transmit.
- *
- * Returns: 0 for success
- */
-static int fcoe_check_wait_queue(struct fc_lport *lp)
-{
-	struct fcoe_softc *fc = lport_priv(lp);
-	struct sk_buff *skb;
-	int rc = -1;
-
-	spin_lock_bh(&fc->fcoe_pending_queue.lock);
-	if (fc->fcoe_pending_queue_active)
-		goto out;
-	fc->fcoe_pending_queue_active = 1;
-
-	while (fc->fcoe_pending_queue.qlen) {
-		/* keep qlen > 0 until fcoe_start_io succeeds */
-		fc->fcoe_pending_queue.qlen++;
-		skb = __skb_dequeue(&fc->fcoe_pending_queue);
-
-		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-		rc = fcoe_start_io(skb);
-		spin_lock_bh(&fc->fcoe_pending_queue.lock);
-
-		if (rc) {
-			__skb_queue_head(&fc->fcoe_pending_queue, skb);
-			/* undo temporary increment above */
-			fc->fcoe_pending_queue.qlen--;
-			break;
-		}
-		/* undo temporary increment above */
-		fc->fcoe_pending_queue.qlen--;
-	}
-
-	if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
-		lp->qfull = 0;
-	fc->fcoe_pending_queue_active = 0;
-	rc = fc->fcoe_pending_queue.qlen;
-out:
-	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-	return rc;
-}
-
-/**
- * fcoe_dev_setup() - setup link change notification interface
- */
-static void fcoe_dev_setup()
-{
-	/*
-	 * here setup a interface specific wd time to
-	 * monitor the link state
-	 */
-	register_netdevice_notifier(&fcoe_notifier);
-}
-
-/**
- * fcoe_dev_setup() - cleanup link change notification interface
- */
-static void fcoe_dev_cleanup(void)
-{
-	unregister_netdevice_notifier(&fcoe_notifier);
-}
-
-/**
- * fcoe_device_notification() - netdev event notification callback
- * @notifier: context of the notification
- * @event: type of event
- * @ptr: fixed array for output parsed ifname
- *
- * This function is called by the ethernet driver in case of link change event
- *
- * Returns: 0 for success
- */
-static int fcoe_device_notification(struct notifier_block *notifier,
-				    ulong event, void *ptr)
-{
-	struct fc_lport *lp = NULL;
-	struct net_device *real_dev = ptr;
-	struct fcoe_softc *fc;
-	struct fcoe_dev_stats *stats;
-	u32 new_link_up;
-	u32 mfs;
-	int rc = NOTIFY_OK;
-
-	read_lock(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		if (fc->real_dev == real_dev) {
-			lp = fc->lp;
-			break;
-		}
-	}
-	read_unlock(&fcoe_hostlist_lock);
-	if (lp == NULL) {
-		rc = NOTIFY_DONE;
-		goto out;
-	}
-
-	new_link_up = lp->link_up;
-	switch (event) {
-	case NETDEV_DOWN:
-	case NETDEV_GOING_DOWN:
-		new_link_up = 0;
-		break;
-	case NETDEV_UP:
-	case NETDEV_CHANGE:
-		new_link_up = !fcoe_link_ok(lp);
-		break;
-	case NETDEV_CHANGEMTU:
-		mfs = fc->real_dev->mtu -
-			(sizeof(struct fcoe_hdr) +
-			 sizeof(struct fcoe_crc_eof));
-		if (mfs >= FC_MIN_MAX_FRAME)
-			fc_set_mfs(lp, mfs);
-		new_link_up = !fcoe_link_ok(lp);
-		break;
-	case NETDEV_REGISTER:
-		break;
-	default:
-		FC_DBG("unknown event %ld call", event);
-	}
-	if (lp->link_up != new_link_up) {
-		if (new_link_up)
-			fc_linkup(lp);
-		else {
-			stats = fc_lport_get_stats(lp);
-			stats->LinkFailureCount++;
-			fc_linkdown(lp);
-			fcoe_clean_pending_queue(lp);
-		}
-	}
-out:
-	return rc;
-}
-
-/**
- * fcoe_if_to_netdev() - parse a name buffer to get netdev
- * @ifname: fixed array for output parsed ifname
- * @buffer: incoming buffer to be copied
- *
- * Returns: NULL or ptr to netdeive
- */
-static struct net_device *fcoe_if_to_netdev(const char *buffer)
-{
-	char *cp;
-	char ifname[IFNAMSIZ + 2];
-
-	if (buffer) {
-		strlcpy(ifname, buffer, IFNAMSIZ);
-		cp = ifname + strlen(ifname);
-		while (--cp >= ifname && *cp == '\n')
-			*cp = '\0';
-		return dev_get_by_name(&init_net, ifname);
-	}
-	return NULL;
-}
-
-/**
- * fcoe_netdev_to_module_owner() - finds out the nic drive moddule of the netdev
- * @netdev: the target netdev
- *
- * Returns: ptr to the struct module, NULL for failure
- */
-static struct module *
-fcoe_netdev_to_module_owner(const struct net_device *netdev)
-{
-	struct device *dev;
-
-	if (!netdev)
-		return NULL;
-
-	dev = netdev->dev.parent;
-	if (!dev)
-		return NULL;
-
-	if (!dev->driver)
-		return NULL;
-
-	return dev->driver->owner;
-}
-
-/**
- * fcoe_ethdrv_get() - Hold the Ethernet driver
- * @netdev: the target netdev
- *
- * Holds the Ethernet driver module by try_module_get() for
- * the corresponding netdev.
- *
- * Returns: 0 for succsss
- */
-static int fcoe_ethdrv_get(const struct net_device *netdev)
-{
-	struct module *owner;
-
-	owner = fcoe_netdev_to_module_owner(netdev);
-	if (owner) {
-		printk(KERN_DEBUG "fcoe:hold driver module %s for %s\n",
-		       module_name(owner), netdev->name);
-		return  try_module_get(owner);
-	}
-	return -ENODEV;
-}
-
-/**
- * fcoe_ethdrv_put() - Release the Ethernet driver
- * @netdev: the target netdev
- *
- * Releases the Ethernet driver module by module_put for
- * the corresponding netdev.
- *
- * Returns: 0 for succsss
- */
-static int fcoe_ethdrv_put(const struct net_device *netdev)
-{
-	struct module *owner;
-
-	owner = fcoe_netdev_to_module_owner(netdev);
-	if (owner) {
-		printk(KERN_DEBUG "fcoe:release driver module %s for %s\n",
-		       module_name(owner), netdev->name);
-		module_put(owner);
-		return 0;
-	}
-	return -ENODEV;
-}
-
-/**
- * fcoe_destroy() - handles the destroy from sysfs
- * @buffer: expcted to be a eth if name
- * @kp: associated kernel param
- *
- * Returns: 0 for success
- */
-static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
-{
-	int rc;
-	struct net_device *netdev;
-
-	netdev = fcoe_if_to_netdev(buffer);
-	if (!netdev) {
-		rc = -ENODEV;
-		goto out_nodev;
-	}
-	/* look for existing lport */
-	if (!fcoe_hostlist_lookup(netdev)) {
-		rc = -ENODEV;
-		goto out_putdev;
-	}
-	rc = fcoe_if_destroy(netdev);
-	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_if_destroy(%s) failed\n",
-		       netdev->name);
-		rc = -EIO;
-		goto out_putdev;
-	}
-	fcoe_ethdrv_put(netdev);
-	rc = 0;
-out_putdev:
-	dev_put(netdev);
-out_nodev:
-	return rc;
-}
-
-/**
- * fcoe_create() - Handles the create call from sysfs
- * @buffer: expcted to be a eth if name
- * @kp: associated kernel param
- *
- * Returns: 0 for success
- */
-static int fcoe_create(const char *buffer, struct kernel_param *kp)
-{
-	int rc;
-	struct net_device *netdev;
-
-	netdev = fcoe_if_to_netdev(buffer);
-	if (!netdev) {
-		rc = -ENODEV;
-		goto out_nodev;
-	}
-	/* look for existing lport */
-	if (fcoe_hostlist_lookup(netdev)) {
-		rc = -EEXIST;
-		goto out_putdev;
-	}
-	fcoe_ethdrv_get(netdev);
-
-	rc = fcoe_if_create(netdev);
-	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_if_create(%s) failed\n",
-		       netdev->name);
-		fcoe_ethdrv_put(netdev);
-		rc = -EIO;
-		goto out_putdev;
-	}
-	rc = 0;
-out_putdev:
-	dev_put(netdev);
-out_nodev:
-	return rc;
-}
-
-module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(create, "string");
-MODULE_PARM_DESC(create, "Create fcoe port using net device passed in.");
-module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, "Destroy fcoe port");
-
-/**
- * fcoe_link_ok() - Check if link is ok for the fc_lport
- * @lp: ptr to the fc_lport
- *
- * Any permanently-disqualifying conditions have been previously checked.
- * This also updates the speed setting, which may change with link for 100/1000.
- *
- * This function should probably be checking for PAUSE support at some point
- * in the future. Currently Per-priority-pause is not determinable using
- * ethtool, so we shouldn't be restrictive until that problem is resolved.
- *
- * Returns: 0 if link is OK for use by FCoE.
- *
- */
-int fcoe_link_ok(struct fc_lport *lp)
-{
-	struct fcoe_softc *fc = lport_priv(lp);
-	struct net_device *dev = fc->real_dev;
-	struct ethtool_cmd ecmd = { ETHTOOL_GSET };
-	int rc = 0;
-
-	if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) {
-		dev = fc->phys_dev;
-		if (dev->ethtool_ops->get_settings) {
-			dev->ethtool_ops->get_settings(dev, &ecmd);
-			lp->link_supported_speeds &=
-				~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
-			if (ecmd.supported & (SUPPORTED_1000baseT_Half |
-					      SUPPORTED_1000baseT_Full))
-				lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
-			if (ecmd.supported & SUPPORTED_10000baseT_Full)
-				lp->link_supported_speeds |=
-					FC_PORTSPEED_10GBIT;
-			if (ecmd.speed == SPEED_1000)
-				lp->link_speed = FC_PORTSPEED_1GBIT;
-			if (ecmd.speed == SPEED_10000)
-				lp->link_speed = FC_PORTSPEED_10GBIT;
-		}
-	} else
-		rc = -1;
-
-	return rc;
-}
-EXPORT_SYMBOL_GPL(fcoe_link_ok);
-
-/**
- * fcoe_percpu_clean() - Clear the pending skbs for an lport
- * @lp: the fc_lport
- */
-void fcoe_percpu_clean(struct fc_lport *lp)
-{
-	struct fcoe_percpu_s *pp;
-	struct fcoe_rcv_info *fr;
-	struct sk_buff_head *list;
-	struct sk_buff *skb, *next;
-	struct sk_buff *head;
-	unsigned int cpu;
-
-	for_each_possible_cpu(cpu) {
-		pp = &per_cpu(fcoe_percpu, cpu);
-		spin_lock_bh(&pp->fcoe_rx_list.lock);
-		list = &pp->fcoe_rx_list;
-		head = list->next;
-		for (skb = head; skb != (struct sk_buff *)list;
-		     skb = next) {
-			next = skb->next;
-			fr = fcoe_dev_from_skb(skb);
-			if (fr->fr_dev == lp) {
-				__skb_unlink(skb, list);
-				kfree_skb(skb);
-			}
-		}
-		spin_unlock_bh(&pp->fcoe_rx_list.lock);
-	}
-}
-EXPORT_SYMBOL_GPL(fcoe_percpu_clean);
-
-/**
- * fcoe_clean_pending_queue() - Dequeue a skb and free it
- * @lp: the corresponding fc_lport
- *
- * Returns: none
- */
-void fcoe_clean_pending_queue(struct fc_lport *lp)
-{
-	struct fcoe_softc  *fc = lport_priv(lp);
-	struct sk_buff *skb;
-
-	spin_lock_bh(&fc->fcoe_pending_queue.lock);
-	while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
-		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-		kfree_skb(skb);
-		spin_lock_bh(&fc->fcoe_pending_queue.lock);
-	}
-	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-}
-EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
-
-/**
- * libfc_host_alloc() - Allocate a Scsi_Host with room for the fc_lport
- * @sht: ptr to the scsi host templ
- * @priv_size: size of private data after fc_lport
- *
- * Returns: ptr to Scsi_Host
- * TODO: to libfc?
- */
-static inline struct Scsi_Host *
-libfc_host_alloc(struct scsi_host_template *sht, int priv_size)
-{
-	return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
-}
-
-/**
- * fcoe_host_alloc() - Allocate a Scsi_Host with room for the fcoe_softc
- * @sht: ptr to the scsi host templ
- * @priv_size: size of private data after fc_lport
- *
- * Returns: ptr to Scsi_Host
- */
-struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *sht, int priv_size)
-{
-	return libfc_host_alloc(sht, sizeof(struct fcoe_softc) + priv_size);
-}
-EXPORT_SYMBOL_GPL(fcoe_host_alloc);
-
-/**
- * fcoe_reset() - Resets the fcoe
- * @shost: shost the reset is from
- *
- * Returns: always 0
- */
-int fcoe_reset(struct Scsi_Host *shost)
-{
-	struct fc_lport *lport = shost_priv(shost);
-	fc_lport_reset(lport);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_reset);
-
-/**
- * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN.
- * @mac: mac address
- * @scheme: check port
- * @port: port indicator for converting
- *
- * Returns: u64 fc world wide name
- */
-u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
-		      unsigned int scheme, unsigned int port)
-{
-	u64 wwn;
-	u64 host_mac;
-
-	/* The MAC is in NO, so flip only the low 48 bits */
-	host_mac = ((u64) mac[0] << 40) |
-		((u64) mac[1] << 32) |
-		((u64) mac[2] << 24) |
-		((u64) mac[3] << 16) |
-		((u64) mac[4] << 8) |
-		(u64) mac[5];
-
-	WARN_ON(host_mac >= (1ULL << 48));
-	wwn = host_mac | ((u64) scheme << 60);
-	switch (scheme) {
-	case 1:
-		WARN_ON(port != 0);
-		break;
-	case 2:
-		WARN_ON(port >= 0xfff);
-		wwn |= (u64) port << 48;
-		break;
-	default:
-		WARN_ON(1);
-		break;
-	}
-
-	return wwn;
-}
-EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
-
-/**
- * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device
- * @device: this is currently ptr to net_device
- *
- * Returns: NULL or the located fcoe_softc
- */
-static struct fcoe_softc *
-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);
-			return fc;
-		}
-	}
-	read_unlock(&fcoe_hostlist_lock);
-	return NULL;
-}
-
-/**
- * fcoe_hostlist_lookup() - Find the corresponding lport by netdev
- * @netdev: ptr to net_device
- *
- * Returns: 0 for success
- */
-struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
-{
-	struct fcoe_softc *fc;
-
-	fc = fcoe_hostlist_lookup_softc(netdev);
-
-	return (fc) ? fc->lp : NULL;
-}
-EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup);
-
-/**
- * fcoe_hostlist_add() - Add a lport to lports list
- * @lp: ptr to the fc_lport to badded
- *
- * Returns: 0 for success
- */
-int fcoe_hostlist_add(const struct fc_lport *lp)
-{
-	struct fcoe_softc *fc;
-
-	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;
-}
-EXPORT_SYMBOL_GPL(fcoe_hostlist_add);
-
-/**
- * fcoe_hostlist_remove() - remove a lport from lports list
- * @lp: ptr to the fc_lport to badded
- *
- * Returns: 0 for success
- */
-int fcoe_hostlist_remove(const struct fc_lport *lp)
-{
-	struct fcoe_softc *fc;
-
-	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);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_hostlist_remove);
-
-/**
- * fcoe_libfc_config() - sets up libfc related properties for lport
- * @lp: ptr to the fc_lport
- * @tt: libfc function template
- *
- * Returns : 0 for success
- */
-int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
-{
-	/* Set the function pointers set by the LLDD */
-	memcpy(&lp->tt, tt, sizeof(*tt));
-	if (fc_fcp_init(lp))
-		return -ENOMEM;
-	fc_exch_init(lp);
-	fc_elsct_init(lp);
-	fc_lport_init(lp);
-	fc_rport_init(lp);
-	fc_disc_init(lp);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_libfc_config);
-
-/**
- * fcoe_init() - fcoe module loading initialization
- *
- * Returns 0 on success, negative on failure
- */
-static int __init fcoe_init(void)
-{
-	unsigned int cpu;
-	int rc = 0;
-	struct fcoe_percpu_s *p;
-
-	INIT_LIST_HEAD(&fcoe_hostlist);
-	rwlock_init(&fcoe_hostlist_lock);
-
-	for_each_possible_cpu(cpu) {
-		p = &per_cpu(fcoe_percpu, cpu);
-		skb_queue_head_init(&p->fcoe_rx_list);
-	}
-
-	for_each_online_cpu(cpu)
-		fcoe_percpu_thread_create(cpu);
-
-	/* Initialize per CPU interrupt thread */
-	rc = register_hotcpu_notifier(&fcoe_cpu_notifier);
-	if (rc)
-		goto out_free;
-
-	/* Setup link change notification */
-	fcoe_dev_setup();
-
-	setup_timer(&fcoe_timer, fcoe_watchdog, 0);
-
-	mod_timer(&fcoe_timer, jiffies + (10 * HZ));
-
-	fcoe_if_init();
-
-	return 0;
-
-out_free:
-	for_each_online_cpu(cpu) {
-		fcoe_percpu_thread_destroy(cpu);
-	}
-
-	return rc;
-}
-module_init(fcoe_init);
-
-/**
- * fcoe_exit() - fcoe module unloading cleanup
- *
- * Returns 0 on success, negative on failure
- */
-static void __exit fcoe_exit(void)
-{
-	unsigned int cpu;
-	struct fcoe_softc *fc, *tmp;
-
-	fcoe_dev_cleanup();
-
-	/* Stop the timer */
-	del_timer_sync(&fcoe_timer);
-
-	/* releases the associated fcoe hosts */
-	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
-		fcoe_if_destroy(fc->real_dev);
-
-	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
-
-	for_each_online_cpu(cpu) {
-		fcoe_percpu_thread_destroy(cpu);
-	}
-
-	/* detach from scsi transport */
-	fcoe_if_exit();
-}
-module_exit(fcoe_exit);


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH] PM port setting and attached SATA port selector in discover
  2009-03-24 23:19   ` [PATCH] " Robert Love
@ 2009-03-26  3:03     ` Andy Yan
  2009-03-27 16:52       ` James Bottomley
  0 siblings, 1 reply; 30+ messages in thread
From: Andy Yan @ 2009-03-26  3:03 UTC (permalink / raw)
  To: james.bottomeley; +Cc: linux-scsi, Michael Wang, Jacky Feng, Ying Chu

>From 9937ccded1e182b50f1128e80d70768b16d2b5e7 Mon Sep 17 00:00:00 2001
From: Andy Yan <ayan@marvell.com>
Date: Thu, 26 Mar 2009 18:32:31 +0800
Subject: [PATCH] PM port setting and attached SATA port selector in
discover

Default PM port should be set 0 other than 1.For most SATA disk, it is
ignored,
But I got that some expander reports attached SATA port selector 1 and
won't clear it. Host should pass this check and send command with PMport
0.

---
 drivers/scsi/libsas/sas_ata.c      |    2 +-
 drivers/scsi/libsas/sas_expander.c |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/libsas/sas_ata.c
b/drivers/scsi/libsas/sas_ata.c
index e155011..bea3deb 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -174,7 +174,7 @@ static unsigned int sas_ata_qc_issue(struct
ata_queued_cmd *qc)
 		qc->tf.nsect = 0;
 	}
 
-	ata_tf_to_fis(&qc->tf, 1, 0, (u8*)&task->ata_task.fis);
+	ata_tf_to_fis(&qc->tf, 0, 0, (u8*)&task->ata_task.fis);
 	task->uldd_task = qc;
 	if (ata_is_atapi(qc->tf.protocol)) {
 		memcpy(task->ata_task.atapi_packet, qc->cdb,
qc->dev->cdb_len);
diff --git a/drivers/scsi/libsas/sas_expander.c
b/drivers/scsi/libsas/sas_expander.c
index 3da02e4..75ca00b 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -642,7 +642,7 @@ static struct domain_device
*sas_ex_discover_end_dev(
 	struct sas_rphy *rphy;
 	int res;
 
-	if (phy->attached_sata_host || phy->attached_sata_ps)
+	if (phy->attached_sata_host)
 		return NULL;
 
 	child = kzalloc(sizeof(*child), GFP_KERNEL);
-- 
1.6.2.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH] fcoe: prep work to completely remove fc_transport_fcoe code
  2009-03-17 18:41 ` [PATCH 05/14] fcoe: prep work to completely remove fc_transport_fcoe code Robert Love
  2009-03-24 23:19   ` [PATCH] " Robert Love
@ 2009-03-27 16:03   ` Robert Love
  2009-03-27 16:12     ` Love, Robert W
  1 sibling, 1 reply; 30+ messages in thread
From: Robert Love @ 2009-03-27 16:03 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

The fcoe transport code was added for generic FCoE transport
infrastructure to allow additional offload related module loading
on demand, this is not required anymore after recently added
different offload approach by having offload related func ops
in netdev.

This patch removes fcoe transport related code use, calls functions
directly between existing libfcoe.c and fcoe_sw.c for now, for
example fcoe_sw_destroy and fcoe_sw_create calling.

The fcoe_sw.c and libfcoe.c code will be further consolidated in
later patches and then also the default fcoe sw transport code
file fcoe_sw.c will be completely removed.

The fcoe transport code files are completely removed in next
patch to keep this patch simple for reviewing.

--

This patch is an update to a previous patch. This update
resolves a build error as well as fixes a defect related to
not calling fc_release_transport().

The following updates to the other patches in the series
are just adjustments to the changes this update introduces.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/Makefile  |    3 +--
 drivers/scsi/fcoe/fcoe_sw.c |   37 ++++---------------------------------
 drivers/scsi/fcoe/libfcoe.c |   25 ++++++-------------------
 include/scsi/libfcoe.h      |    2 ++
 4 files changed, 13 insertions(+), 54 deletions(-)

diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index b78da06..e950adf 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -4,5 +4,4 @@ obj-$(CONFIG_FCOE) += fcoe.o
 
 fcoe-y := \
 	libfcoe.o \
-	fcoe_sw.o \
-	fc_transport_fcoe.o
+	fcoe_sw.o
diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c
index a675390..f060220 100644
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -36,7 +36,6 @@
 
 #include <scsi/libfc.h>
 #include <scsi/libfcoe.h>
-#include <scsi/fc_transport_fcoe.h>
 
 #define FCOE_SW_VERSION	"0.1"
 #define	FCOE_SW_NAME	"fcoesw"
@@ -302,7 +301,7 @@ static inline int fcoe_sw_em_config(struct fc_lport *lp)
  *
  * Returns: 0 if link is OK for use by FCoE.
  */
-static int fcoe_sw_destroy(struct net_device *netdev)
+int fcoe_sw_destroy(struct net_device *netdev)
 {
 	struct fc_lport *lp = NULL;
 	struct fcoe_softc *fc;
@@ -415,7 +414,7 @@ static struct libfc_function_template fcoe_sw_libfc_fcn_templ = {
  *
  * Returns : 0 on success
  */
-static int fcoe_sw_create(struct net_device *netdev)
+int fcoe_sw_create(struct net_device *netdev)
 {
 	int rc;
 	struct fc_lport *lp = NULL;
@@ -494,28 +493,7 @@ out_host_put:
 }
 
 /**
- * fcoe_sw_match() - The FCoE SW transport match function
- *
- * Returns : false always
- */
-static bool fcoe_sw_match(struct net_device *netdev)
-{
-	/* FIXME - for sw transport, always return false */
-	return false;
-}
-
-/* the sw hba fcoe transport */
-struct fcoe_transport fcoe_sw_transport = {
-	.name = "fcoesw",
-	.create = fcoe_sw_create,
-	.destroy = fcoe_sw_destroy,
-	.match = fcoe_sw_match,
-	.vendor = 0x0,
-	.device = 0xffff,
-};
-
-/**
- * fcoe_sw_init() - Registers fcoe_sw_transport
+ * fcoe_sw_init() - attach to scsi transport
  *
  * Returns : 0 on success
  */
@@ -530,23 +508,16 @@ int __init fcoe_sw_init(void)
 		return -ENODEV;
 	}
 
-	mutex_init(&fcoe_sw_transport.devlock);
-	INIT_LIST_HEAD(&fcoe_sw_transport.devlist);
-
-	/* register sw transport */
-	fcoe_transport_register(&fcoe_sw_transport);
 	return 0;
 }
 
 /**
- * fcoe_sw_exit() - Unregisters fcoe_sw_transport
+ * fcoe_sw_exit() - detach from scsi transport
  *
  * Returns : 0 on success
  */
 int __exit fcoe_sw_exit(void)
 {
-	/* dettach the transport */
 	fc_release_transport(scsi_transport_fcoe_sw);
-	fcoe_transport_unregister(&fcoe_sw_transport);
 	return 0;
 }
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 951d244..334db95 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -44,7 +44,6 @@
 #include <scsi/libfc.h>
 #include <scsi/fc_frame.h>
 #include <scsi/libfcoe.h>
-#include <scsi/fc_transport_fcoe.h>
 
 static int debug_fcoe;
 
@@ -1081,10 +1080,9 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
 		rc = -ENODEV;
 		goto out_putdev;
 	}
-	/* pass to transport */
-	rc = fcoe_transport_release(netdev);
+	rc = fcoe_sw_destroy(netdev);
 	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_transport_release(%s) failed\n",
+		printk(KERN_ERR "fcoe: fcoe_sw_destroy(%s) failed\n",
 		       netdev->name);
 		rc = -EIO;
 		goto out_putdev;
@@ -1121,10 +1119,9 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 	}
 	fcoe_ethdrv_get(netdev);
 
-	/* pass to transport */
-	rc = fcoe_transport_attach(netdev);
+	rc = fcoe_sw_create(netdev);
 	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_transport_attach(%s) failed\n",
+		printk(KERN_ERR "fcoe: fcoe_sw_create(%s) failed\n",
 		       netdev->name);
 		fcoe_ethdrv_put(netdev);
 		rc = -EIO;
@@ -1429,10 +1426,6 @@ EXPORT_SYMBOL_GPL(fcoe_libfc_config);
 /**
  * fcoe_init() - fcoe module loading initialization
  *
- * Initialization routine
- * 1. Will create fc transport software structure
- * 2. initialize the link list of port information structure
- *
  * Returns 0 on success, negative on failure
  */
 static int __init fcoe_init(void)
@@ -1464,9 +1457,6 @@ static int __init fcoe_init(void)
 
 	mod_timer(&fcoe_timer, jiffies + (10 * HZ));
 
-	/* initiatlize the fcoe transport */
-	fcoe_transport_init();
-
 	fcoe_sw_init();
 
 	return 0;
@@ -1495,9 +1485,9 @@ static void __exit fcoe_exit(void)
 	/* Stop the timer */
 	del_timer_sync(&fcoe_timer);
 
-	/* releases the associated fcoe transport for each lport */
+	/* releases the associated fcoe hosts */
 	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
-		fcoe_transport_release(fc->real_dev);
+		fcoe_sw_destroy(fc->real_dev);
 
 	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
 
@@ -1507,8 +1497,5 @@ static void __exit fcoe_exit(void)
 
 	/* remove sw trasnport */
 	fcoe_sw_exit();
-
-	/* detach the transport */
-	fcoe_transport_exit();
 }
 module_exit(fcoe_exit);
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index 1ad4f93..dc64405 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -149,4 +149,6 @@ int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
 /* fcoe sw hba */
 int __init fcoe_sw_init(void);
 int __exit fcoe_sw_exit(void);
+int fcoe_sw_create(struct net_device *);
+int fcoe_sw_destroy(struct net_device *);
 #endif /* _LIBFCOE_H */


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH] fcoe: removes fc_transport_fcoe.[ch] code files
  2009-03-17 18:41 ` [PATCH 06/14] fcoe: removes fc_transport_fcoe.[ch] code files Robert Love
  2009-03-24 23:24   ` [PATCH] " Robert Love
@ 2009-03-27 16:05   ` Robert Love
  1 sibling, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-27 16:05 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

Remove unused fc_transport_fcoe.c and fc_transport_fcoe.h
files.
---

 drivers/scsi/fcoe/fc_transport_fcoe.c |  443 ---------------------------------
 include/scsi/fc_transport_fcoe.h      |   54 ----
 2 files changed, 0 insertions(+), 497 deletions(-)
 delete mode 100644 drivers/scsi/fcoe/fc_transport_fcoe.c
 delete mode 100644 include/scsi/fc_transport_fcoe.h

diff --git a/drivers/scsi/fcoe/fc_transport_fcoe.c b/drivers/scsi/fcoe/fc_transport_fcoe.c
deleted file mode 100644
index 8862758..0000000
--- a/drivers/scsi/fcoe/fc_transport_fcoe.c
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Maintained at www.Open-FCoE.org
- */
-
-#include <linux/pci.h>
-#include <scsi/libfcoe.h>
-#include <scsi/fc_transport_fcoe.h>
-
-/* internal fcoe transport */
-struct fcoe_transport_internal {
-	struct fcoe_transport *t;
-	struct net_device *netdev;
-	struct list_head list;
-};
-
-/* fcoe transports list and its lock */
-static LIST_HEAD(fcoe_transports);
-static DEFINE_MUTEX(fcoe_transports_lock);
-
-/**
- * fcoe_transport_default() - Returns ptr to the default transport fcoe_sw
- */
-struct fcoe_transport *fcoe_transport_default(void)
-{
-	return &fcoe_sw_transport;
-}
-
-/**
- * fcoe_transport_to_pcidev() - get the pci dev from a netdev
- * @netdev: the netdev that pci dev will be retrived from
- *
- * Returns: NULL or the corrsponding pci_dev
- */
-struct pci_dev *fcoe_transport_pcidev(const struct net_device *netdev)
-{
-	if (!netdev->dev.parent)
-		return NULL;
-	return to_pci_dev(netdev->dev.parent);
-}
-
-/**
- * fcoe_transport_device_lookup() - Lookup a transport
- * @netdev: the netdev the transport to be attached to
- *
- * This will look for existing offload driver, if not found, it falls back to
- * the default sw hba (fcoe_sw) as its fcoe transport.
- *
- * Returns: 0 for success
- */
-static struct fcoe_transport_internal *
-fcoe_transport_device_lookup(struct fcoe_transport *t,
-			     struct net_device *netdev)
-{
-	struct fcoe_transport_internal *ti;
-
-	/* assign the transpor to this device */
-	mutex_lock(&t->devlock);
-	list_for_each_entry(ti, &t->devlist, list) {
-		if (ti->netdev == netdev) {
-			mutex_unlock(&t->devlock);
-			return ti;
-		}
-	}
-	mutex_unlock(&t->devlock);
-	return NULL;
-}
-/**
- * fcoe_transport_device_add() - Assign a transport to a device
- * @netdev: the netdev the transport to be attached to
- *
- * This will look for existing offload driver, if not found, it falls back to
- * the default sw hba (fcoe_sw) as its fcoe transport.
- *
- * Returns: 0 for success
- */
-static int fcoe_transport_device_add(struct fcoe_transport *t,
-				     struct net_device *netdev)
-{
-	struct fcoe_transport_internal *ti;
-
-	ti = fcoe_transport_device_lookup(t, netdev);
-	if (ti) {
-		printk(KERN_DEBUG "fcoe_transport_device_add:"
-		       "device %s is already added to transport %s\n",
-		       netdev->name, t->name);
-		return -EEXIST;
-	}
-	/* allocate an internal struct to host the netdev and the list */
-	ti = kzalloc(sizeof(*ti), GFP_KERNEL);
-	if (!ti)
-		return -ENOMEM;
-
-	ti->t = t;
-	ti->netdev = netdev;
-	INIT_LIST_HEAD(&ti->list);
-	dev_hold(ti->netdev);
-
-	mutex_lock(&t->devlock);
-	list_add(&ti->list, &t->devlist);
-	mutex_unlock(&t->devlock);
-
-	printk(KERN_DEBUG "fcoe_transport_device_add:"
-		       "device %s added to transport %s\n",
-		       netdev->name, t->name);
-
-	return 0;
-}
-
-/**
- * fcoe_transport_device_remove() - Remove a device from its transport
- * @netdev: the netdev the transport to be attached to
- *
- * This removes the device from the transport so the given transport will
- * not manage this device any more
- *
- * Returns: 0 for success
- */
-static int fcoe_transport_device_remove(struct fcoe_transport *t,
-					struct net_device *netdev)
-{
-	struct fcoe_transport_internal *ti;
-
-	ti = fcoe_transport_device_lookup(t, netdev);
-	if (!ti) {
-		printk(KERN_DEBUG "fcoe_transport_device_remove:"
-		       "device %s is not managed by transport %s\n",
-		       netdev->name, t->name);
-		return -ENODEV;
-	}
-	mutex_lock(&t->devlock);
-	list_del(&ti->list);
-	mutex_unlock(&t->devlock);
-	printk(KERN_DEBUG "fcoe_transport_device_remove:"
-	       "device %s removed from transport %s\n",
-	       netdev->name, t->name);
-	dev_put(ti->netdev);
-	kfree(ti);
-	return 0;
-}
-
-/**
- * fcoe_transport_device_remove_all() - Remove all from transport devlist
- *
- * This removes the device from the transport so the given transport will
- * not manage this device any more
- *
- * Returns: 0 for success
- */
-static void fcoe_transport_device_remove_all(struct fcoe_transport *t)
-{
-	struct fcoe_transport_internal *ti, *tmp;
-
-	mutex_lock(&t->devlock);
-	list_for_each_entry_safe(ti, tmp, &t->devlist, list) {
-		list_del(&ti->list);
-		kfree(ti);
-	}
-	mutex_unlock(&t->devlock);
-}
-
-/**
- * fcoe_transport_match() - Use the bus device match function to match the hw
- * @t: The fcoe transport to check
- * @netdev: The netdev to match against
- *
- * This function is used to check if the given transport wants to manage the
- * input netdev. if the transports implements the match function, it will be
- * called, o.w. we just compare the pci vendor and device id.
- *
- * Returns: true for match up
- */
-static bool fcoe_transport_match(struct fcoe_transport *t,
-				 struct net_device *netdev)
-{
-	/* match transport by vendor and device id */
-	struct pci_dev *pci;
-
-	pci = fcoe_transport_pcidev(netdev);
-
-	if (pci) {
-		printk(KERN_DEBUG "fcoe_transport_match:"
-		       "%s:%x:%x -- %s:%x:%x\n",
-		       t->name, t->vendor, t->device,
-		       netdev->name, pci->vendor, pci->device);
-
-		/* if transport supports match */
-		if (t->match)
-			return t->match(netdev);
-
-		/* else just compare the vendor and device id: pci only */
-		return (t->vendor == pci->vendor) && (t->device == pci->device);
-	}
-	return false;
-}
-
-/**
- * fcoe_transport_lookup() - Check if the transport is already registered
- * @t: the transport to be looked up
- *
- * This compares the parent device (pci) vendor and device id
- *
- * Returns: NULL if not found
- *
- * TODO: return default sw transport if no other transport is found
- */
-static struct fcoe_transport *
-fcoe_transport_lookup(struct net_device *netdev)
-{
-	struct fcoe_transport *t;
-
-	mutex_lock(&fcoe_transports_lock);
-	list_for_each_entry(t, &fcoe_transports, list) {
-		if (fcoe_transport_match(t, netdev)) {
-			mutex_unlock(&fcoe_transports_lock);
-			return t;
-		}
-	}
-	mutex_unlock(&fcoe_transports_lock);
-
-	printk(KERN_DEBUG "fcoe_transport_lookup:"
-	       "use default transport for %s\n", netdev->name);
-	return fcoe_transport_default();
-}
-
-/**
- * fcoe_transport_register() - Adds a fcoe transport to the fcoe transports list
- * @t: ptr to the fcoe transport to be added
- *
- * Returns: 0 for success
- */
-int fcoe_transport_register(struct fcoe_transport *t)
-{
-	struct fcoe_transport *tt;
-
-	/* TODO - add fcoe_transport specific initialization here */
-	mutex_lock(&fcoe_transports_lock);
-	list_for_each_entry(tt, &fcoe_transports, list) {
-		if (tt == t) {
-			mutex_unlock(&fcoe_transports_lock);
-			return -EEXIST;
-		}
-	}
-	list_add_tail(&t->list, &fcoe_transports);
-	mutex_unlock(&fcoe_transports_lock);
-
-	printk(KERN_DEBUG "fcoe_transport_register:%s\n", t->name);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_transport_register);
-
-/**
- * fcoe_transport_unregister() - Remove the tranport fro the fcoe transports list
- * @t: ptr to the fcoe transport to be removed
- *
- * Returns: 0 for success
- */
-int fcoe_transport_unregister(struct fcoe_transport *t)
-{
-	struct fcoe_transport *tt, *tmp;
-
-	mutex_lock(&fcoe_transports_lock);
-	list_for_each_entry_safe(tt, tmp, &fcoe_transports, list) {
-		if (tt == t) {
-			list_del(&t->list);
-			mutex_unlock(&fcoe_transports_lock);
-			fcoe_transport_device_remove_all(t);
-			printk(KERN_DEBUG "fcoe_transport_unregister:%s\n",
-			       t->name);
-			return 0;
-		}
-	}
-	mutex_unlock(&fcoe_transports_lock);
-	return -ENODEV;
-}
-EXPORT_SYMBOL_GPL(fcoe_transport_unregister);
-
-/**
- * fcoe_load_transport_driver() - Load an offload driver by alias name
- * @netdev: the target net device
- *
- * Requests for an offload driver module as the fcoe transport, if fails, it
- * falls back to use the SW HBA (fcoe_sw) as its transport
- *
- * TODO -
- * 	1. supports only PCI device
- * 	2. needs fix for VLAn and bonding
- * 	3. pure hw fcoe hba may not have netdev
- *
- * Returns: 0 for success
- */
-int fcoe_load_transport_driver(struct net_device *netdev)
-{
-	struct pci_dev *pci;
-	struct device *dev = netdev->dev.parent;
-
-	if (fcoe_transport_lookup(netdev)) {
-		/* load default transport */
-		printk(KERN_DEBUG "fcoe: already loaded transport for %s\n",
-		       netdev->name);
-		return -EEXIST;
-	}
-
-	pci = to_pci_dev(dev);
-	if (dev->bus != &pci_bus_type) {
-		printk(KERN_DEBUG "fcoe: support noly PCI device\n");
-		return -ENODEV;
-	}
-	printk(KERN_DEBUG "fcoe: loading driver fcoe-pci-0x%04x-0x%04x\n",
-	       pci->vendor, pci->device);
-
-	return request_module("fcoe-pci-0x%04x-0x%04x",
-			      pci->vendor, pci->device);
-
-}
-EXPORT_SYMBOL_GPL(fcoe_load_transport_driver);
-
-/**
- * fcoe_transport_attach() - Load transport to fcoe
- * @netdev: the netdev the transport to be attached to
- *
- * This will look for existing offload driver, if not found, it falls back to
- * the default sw hba (fcoe_sw) as its fcoe transport.
- *
- * Returns: 0 for success
- */
-int fcoe_transport_attach(struct net_device *netdev)
-{
-	struct fcoe_transport *t;
-
-	/* find the corresponding transport */
-	t = fcoe_transport_lookup(netdev);
-	if (!t) {
-		printk(KERN_DEBUG "fcoe_transport_attach"
-		       ":no transport for %s:use %s\n",
-		       netdev->name, t->name);
-		return -ENODEV;
-	}
-	/* add to the transport */
-	if (fcoe_transport_device_add(t, netdev)) {
-		printk(KERN_DEBUG "fcoe_transport_attach"
-		       ":failed to add %s to tramsport %s\n",
-		       netdev->name, t->name);
-		return -EIO;
-	}
-	/* transport create function */
-	if (t->create)
-		t->create(netdev);
-
-	printk(KERN_DEBUG "fcoe_transport_attach:transport %s for %s\n",
-	       t->name, netdev->name);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_transport_attach);
-
-/**
- * fcoe_transport_release() - Unload transport from fcoe
- * @netdev: the net device on which fcoe is to be released
- *
- * Returns: 0 for success
- */
-int fcoe_transport_release(struct net_device *netdev)
-{
-	struct fcoe_transport *t;
-
-	/* find the corresponding transport */
-	t = fcoe_transport_lookup(netdev);
-	if (!t) {
-		printk(KERN_DEBUG "fcoe_transport_release:"
-		       "no transport for %s:use %s\n",
-		       netdev->name, t->name);
-		return -ENODEV;
-	}
-	/* remove the device from the transport */
-	if (fcoe_transport_device_remove(t, netdev)) {
-		printk(KERN_DEBUG "fcoe_transport_release:"
-		       "failed to add %s to tramsport %s\n",
-		       netdev->name, t->name);
-		return -EIO;
-	}
-	/* transport destroy function */
-	if (t->destroy)
-		t->destroy(netdev);
-
-	printk(KERN_DEBUG "fcoe_transport_release:"
-	       "device %s dettached from transport %s\n",
-	       netdev->name, t->name);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_transport_release);
-
-/**
- * fcoe_transport_init() - Initializes fcoe transport layer
- *
- * This prepares for the fcoe transport layer
- *
- * Returns: none
- */
-int __init fcoe_transport_init(void)
-{
-	INIT_LIST_HEAD(&fcoe_transports);
-	mutex_init(&fcoe_transports_lock);
-	return 0;
-}
-
-/**
- * fcoe_transport_exit() - Cleans up the fcoe transport layer
- *
- * This cleans up the fcoe transport layer. removing any transport on the list,
- * note that the transport destroy func is not called here.
- *
- * Returns: none
- */
-int __exit fcoe_transport_exit(void)
-{
-	struct fcoe_transport *t, *tmp;
-
-	mutex_lock(&fcoe_transports_lock);
-	list_for_each_entry_safe(t, tmp, &fcoe_transports, list) {
-		list_del(&t->list);
-		mutex_unlock(&fcoe_transports_lock);
-		fcoe_transport_device_remove_all(t);
-		mutex_lock(&fcoe_transports_lock);
-	}
-	mutex_unlock(&fcoe_transports_lock);
-	return 0;
-}
diff --git a/include/scsi/fc_transport_fcoe.h b/include/scsi/fc_transport_fcoe.h
deleted file mode 100644
index 8dca2af..0000000
--- a/include/scsi/fc_transport_fcoe.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef FC_TRANSPORT_FCOE_H
-#define FC_TRANSPORT_FCOE_H
-
-#include <linux/device.h>
-#include <linux/netdevice.h>
-#include <scsi/scsi_host.h>
-#include <scsi/libfc.h>
-
-/**
- * struct fcoe_transport - FCoE transport struct for generic transport
- * for Ethernet devices as well as pure HBAs
- *
- * @name: name for thsi transport
- * @bus: physical bus type (pci_bus_type)
- * @driver: physical bus driver for network device
- * @create: entry create function
- * @destroy: exit destroy function
- * @list: list of transports
- */
-struct fcoe_transport {
-	char *name;
-	unsigned short vendor;
-	unsigned short device;
-	struct bus_type *bus;
-	struct device_driver *driver;
-	int (*create)(struct net_device *device);
-	int (*destroy)(struct net_device *device);
-	bool (*match)(struct net_device *device);
-	struct list_head list;
-	struct list_head devlist;
-	struct mutex devlock;
-};
-
-/**
- * MODULE_ALIAS_FCOE_PCI
- *
- * some care must be taken with this, vendor and device MUST be a hex value
- * preceded with 0x and with letters in lower case (0x12ab, not 0x12AB or 12AB)
- */
-#define MODULE_ALIAS_FCOE_PCI(vendor, device) \
-	MODULE_ALIAS("fcoe-pci-" __stringify(vendor) "-" __stringify(device))
-
-/* exported funcs */
-int fcoe_transport_attach(struct net_device *netdev);
-int fcoe_transport_release(struct net_device *netdev);
-int fcoe_transport_register(struct fcoe_transport *t);
-int fcoe_transport_unregister(struct fcoe_transport *t);
-int fcoe_load_transport_driver(struct net_device *netdev);
-int __init fcoe_transport_init(void);
-int __exit fcoe_transport_exit(void);
-
-/* fcow_sw is the default transport */
-extern struct fcoe_transport fcoe_sw_transport;
-#endif /* FC_TRANSPORT_FCOE_H */


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH] fcoe: removes default sw transport code file fcoe_sw.c
  2009-03-17 18:42 ` [PATCH 07/14] fcoe: removes default sw transport code file fcoe_sw.c Robert Love
  2009-03-24 23:27   ` [PATCH] " Robert Love
@ 2009-03-27 16:06   ` Robert Love
  1 sibling, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-27 16:06 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

Moves only required code from fcoe_sw.c to libfcoe.c towards having
just one source file for fcoe module, this gets rid off default sw
transport code in a separate fcoe_sw.c file.

Very minor renaming along this move, dropped _sw_ or _SW_ use
in names and replaced them by _if_ as a auxiliary interface
functions. Now some of these funcs can be removed or merged with
other func after fcoe transport is gone, but that should be
in another patch to keep this patch simple.

Now the libfcoe.c file name for fcoe module doesn't go along well,
so the libfcoe.c file renaming to fcoe.c as the only single fcoe
module file is done in next patch to keep this patch clean
and small for review.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/Makefile  |    3 
 drivers/scsi/fcoe/fcoe_sw.c |  523 -------------------------------------------
 drivers/scsi/fcoe/libfcoe.c |  501 +++++++++++++++++++++++++++++++++++++++++
 include/scsi/libfcoe.h      |    6 
 4 files changed, 494 insertions(+), 539 deletions(-)
 delete mode 100644 drivers/scsi/fcoe/fcoe_sw.c

diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index e950adf..2d9dae4 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -3,5 +3,4 @@
 obj-$(CONFIG_FCOE) += fcoe.o
 
 fcoe-y := \
-	libfcoe.o \
-	fcoe_sw.o
+	libfcoe.o
diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c
deleted file mode 100644
index f060220..0000000
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ /dev/null
@@ -1,523 +0,0 @@
-/*
- * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Maintained at www.Open-FCoE.org
- */
-
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if_vlan.h>
-#include <net/rtnetlink.h>
-
-#include <scsi/fc/fc_els.h>
-#include <scsi/fc/fc_encaps.h>
-#include <scsi/fc/fc_fs.h>
-#include <scsi/scsi_transport.h>
-#include <scsi/scsi_transport_fc.h>
-
-#include <scsi/libfc.h>
-#include <scsi/libfcoe.h>
-
-#define FCOE_SW_VERSION	"0.1"
-#define	FCOE_SW_NAME	"fcoesw"
-#define	FCOE_SW_VENDOR	"Open-FCoE.org"
-
-#define FCOE_MAX_LUN		255
-#define FCOE_MAX_FCP_TARGET	256
-
-#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
-
-#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
-#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
-
-static struct scsi_transport_template *scsi_transport_fcoe_sw;
-
-struct fc_function_template fcoe_sw_transport_function = {
-	.show_host_node_name = 1,
-	.show_host_port_name = 1,
-	.show_host_supported_classes = 1,
-	.show_host_supported_fc4s = 1,
-	.show_host_active_fc4s = 1,
-	.show_host_maxframe_size = 1,
-
-	.show_host_port_id = 1,
-	.show_host_supported_speeds = 1,
-	.get_host_speed = fc_get_host_speed,
-	.show_host_speed = 1,
-	.show_host_port_type = 1,
-	.get_host_port_state = fc_get_host_port_state,
-	.show_host_port_state = 1,
-	.show_host_symbolic_name = 1,
-
-	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
-	.show_rport_maxframe_size = 1,
-	.show_rport_supported_classes = 1,
-
-	.show_host_fabric_name = 1,
-	.show_starget_node_name = 1,
-	.show_starget_port_name = 1,
-	.show_starget_port_id = 1,
-	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
-	.show_rport_dev_loss_tmo = 1,
-	.get_fc_host_stats = fc_get_host_stats,
-	.issue_fc_host_lip = fcoe_reset,
-
-	.terminate_rport_io = fc_rport_terminate_io,
-};
-
-static struct scsi_host_template fcoe_sw_shost_template = {
-	.module = THIS_MODULE,
-	.name = "FCoE Driver",
-	.proc_name = FCOE_SW_NAME,
-	.queuecommand = fc_queuecommand,
-	.eh_abort_handler = fc_eh_abort,
-	.eh_device_reset_handler = fc_eh_device_reset,
-	.eh_host_reset_handler = fc_eh_host_reset,
-	.slave_alloc = fc_slave_alloc,
-	.change_queue_depth = fc_change_queue_depth,
-	.change_queue_type = fc_change_queue_type,
-	.this_id = -1,
-	.cmd_per_lun = 32,
-	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
-	.use_clustering = ENABLE_CLUSTERING,
-	.sg_tablesize = SG_ALL,
-	.max_sectors = 0xffff,
-};
-
-/**
- * fcoe_sw_lport_config() - sets up the fc_lport
- * @lp: ptr to the fc_lport
- * @shost: ptr to the parent scsi host
- *
- * Returns: 0 for success
- */
-static int fcoe_sw_lport_config(struct fc_lport *lp)
-{
-	lp->link_up = 0;
-	lp->qfull = 0;
-	lp->max_retry_count = 3;
-	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
-	lp->r_a_tov = 2 * 2 * 1000;
-	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
-			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
-
-	fc_lport_init_stats(lp);
-
-	/* lport fc_lport related configuration */
-	fc_lport_config(lp);
-
-	/* offload related configuration */
-	lp->crc_offload = 0;
-	lp->seq_offload = 0;
-	lp->lro_enabled = 0;
-	lp->lro_xid = 0;
-	lp->lso_max = 0;
-
-	return 0;
-}
-
-/**
- * fcoe_sw_netdev_config() - Set up netdev for SW FCoE
- * @lp : ptr to the fc_lport
- * @netdev : ptr to the associated netdevice struct
- *
- * Must be called after fcoe_sw_lport_config() as it will use lport mutex
- *
- * Returns : 0 for success
- */
-static int fcoe_sw_netdev_config(struct fc_lport *lp, struct net_device *netdev)
-{
-	u32 mfs;
-	u64 wwnn, wwpn;
-	struct fcoe_softc *fc;
-	u8 flogi_maddr[ETH_ALEN];
-
-	/* Setup lport private data to point to fcoe softc */
-	fc = lport_priv(lp);
-	fc->lp = lp;
-	fc->real_dev = netdev;
-	fc->phys_dev = netdev;
-
-	/* Require support for get_pauseparam ethtool op. */
-	if (netdev->priv_flags & IFF_802_1Q_VLAN)
-		fc->phys_dev = vlan_dev_real_dev(netdev);
-
-	/* Do not support for bonding device */
-	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
-	    (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
-	    (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
-		return -EOPNOTSUPP;
-	}
-
-	/*
-	 * Determine max frame size based on underlying device and optional
-	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
-	 * will return 0, so do this first.
-	 */
-	mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
-				   sizeof(struct fcoe_crc_eof));
-	if (fc_set_mfs(lp, mfs))
-		return -EINVAL;
-
-	if (!fcoe_link_ok(lp))
-		lp->link_up = 1;
-
-	/* offload features support */
-	if (fc->real_dev->features & NETIF_F_SG)
-		lp->sg_supp = 1;
-
-#ifdef NETIF_F_FCOE_CRC
-	if (netdev->features & NETIF_F_FCOE_CRC) {
-		lp->crc_offload = 1;
-		printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
-		       netdev->name);
-	}
-#endif
-#ifdef NETIF_F_FSO
-	if (netdev->features & NETIF_F_FSO) {
-		lp->seq_offload = 1;
-		lp->lso_max = netdev->gso_max_size;
-		printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
-		       netdev->name, lp->lso_max);
-	}
-#endif
-	if (netdev->fcoe_ddp_xid) {
-		lp->lro_enabled = 1;
-		lp->lro_xid = netdev->fcoe_ddp_xid;
-		printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
-		       netdev->name, lp->lro_xid);
-	}
-	skb_queue_head_init(&fc->fcoe_pending_queue);
-	fc->fcoe_pending_queue_active = 0;
-
-	/* setup Source Mac Address */
-	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
-	       fc->real_dev->addr_len);
-
-	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
-	fc_set_wwnn(lp, wwnn);
-	/* XXX - 3rd arg needs to be vlan id */
-	wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
-	fc_set_wwpn(lp, wwpn);
-
-	/*
-	 * Add FCoE MAC address as second unicast MAC address
-	 * or enter promiscuous mode if not capable of listening
-	 * for multiple unicast MACs.
-	 */
-	rtnl_lock();
-	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-	dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
-	rtnl_unlock();
-
-	/*
-	 * setup the receive function from ethernet driver
-	 * on the ethertype for the given device
-	 */
-	fc->fcoe_packet_type.func = fcoe_rcv;
-	fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
-	fc->fcoe_packet_type.dev = fc->real_dev;
-	dev_add_pack(&fc->fcoe_packet_type);
-
-	return 0;
-}
-
-/**
- * fcoe_sw_shost_config() - Sets up fc_lport->host
- * @lp : ptr to the fc_lport
- * @shost : ptr to the associated scsi host
- * @dev : device associated to scsi host
- *
- * Must be called after fcoe_sw_lport_config() and fcoe_sw_netdev_config()
- *
- * Returns : 0 for success
- */
-static int fcoe_sw_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
-				struct device *dev)
-{
-	int rc = 0;
-
-	/* lport scsi host config */
-	lp->host = shost;
-
-	lp->host->max_lun = FCOE_MAX_LUN;
-	lp->host->max_id = FCOE_MAX_FCP_TARGET;
-	lp->host->max_channel = 0;
-	lp->host->transportt = scsi_transport_fcoe_sw;
-
-	/* add the new host to the SCSI-ml */
-	rc = scsi_add_host(lp->host, dev);
-	if (rc) {
-		FC_DBG("fcoe_sw_shost_config:error on scsi_add_host\n");
-		return rc;
-	}
-	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
-		FCOE_SW_NAME, FCOE_SW_VERSION,
-		fcoe_netdev(lp)->name);
-
-	return 0;
-}
-
-/**
- * fcoe_sw_em_config() - allocates em for this lport
- * @lp: the port that em is to allocated for
- *
- * Returns : 0 on success
- */
-static inline int fcoe_sw_em_config(struct fc_lport *lp)
-{
-	BUG_ON(lp->emp);
-
-	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
-				    FCOE_MIN_XID, FCOE_MAX_XID);
-	if (!lp->emp)
-		return -ENOMEM;
-
-	return 0;
-}
-
-/**
- * fcoe_sw_destroy() - FCoE software HBA tear-down function
- * @netdev: ptr to the associated net_device
- *
- * Returns: 0 if link is OK for use by FCoE.
- */
-int fcoe_sw_destroy(struct net_device *netdev)
-{
-	struct fc_lport *lp = NULL;
-	struct fcoe_softc *fc;
-	u8 flogi_maddr[ETH_ALEN];
-
-	BUG_ON(!netdev);
-
-	printk(KERN_DEBUG "fcoe_sw_destroy:interface on %s\n",
-	       netdev->name);
-
-	lp = fcoe_hostlist_lookup(netdev);
-	if (!lp)
-		return -ENODEV;
-
-	fc = lport_priv(lp);
-
-	/* Logout of the fabric */
-	fc_fabric_logoff(lp);
-
-	/* Remove the instance from fcoe's list */
-	fcoe_hostlist_remove(lp);
-
-	/* Don't listen for Ethernet packets anymore */
-	dev_remove_pack(&fc->fcoe_packet_type);
-
-	/* Cleanup the fc_lport */
-	fc_lport_destroy(lp);
-	fc_fcp_destroy(lp);
-
-	/* Detach from the scsi-ml */
-	fc_remove_host(lp->host);
-	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);
-
-	/* Delete secondary MAC addresses */
-	rtnl_lock();
-	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
-	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
-		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
-	rtnl_unlock();
-
-	/* Free the per-CPU revieve threads */
-	fcoe_percpu_clean(lp);
-
-	/* Free existing skbs */
-	fcoe_clean_pending_queue(lp);
-
-	/* Free memory used by statistical counters */
-	fc_lport_free_stats(lp);
-
-	/* Release the net_device and Scsi_Host */
-	dev_put(fc->real_dev);
-	scsi_host_put(lp->host);
-
-	return 0;
-}
-
-/*
- * fcoe_sw_ddp_setup - calls LLD's ddp_setup through net_device
- * @lp:	the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- * @sgl: the scatterlist describing this transfer
- * @sgc: number of sg items
- *
- * Returns : 0 no ddp
- */
-static int fcoe_sw_ddp_setup(struct fc_lport *lp, u16 xid,
-			     struct scatterlist *sgl, unsigned int sgc)
-{
-	struct net_device *n = fcoe_netdev(lp);
-
-	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
-		return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
-
-	return 0;
-}
-
-/*
- * fcoe_sw_ddp_done - calls LLD's ddp_done through net_device
- * @lp:	the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- *
- * Returns : the length of data that have been completed by ddp
- */
-static int fcoe_sw_ddp_done(struct fc_lport *lp, u16 xid)
-{
-	struct net_device *n = fcoe_netdev(lp);
-
-	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
-		return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
-	return 0;
-}
-
-static struct libfc_function_template fcoe_sw_libfc_fcn_templ = {
-	.frame_send = fcoe_xmit,
-	.ddp_setup = fcoe_sw_ddp_setup,
-	.ddp_done = fcoe_sw_ddp_done,
-};
-
-/**
- * fcoe_sw_create() - this function creates the fcoe interface
- * @netdev: pointer the associated netdevice
- *
- * Creates fc_lport struct and scsi_host for lport, configures lport
- * and starts fabric login.
- *
- * Returns : 0 on success
- */
-int fcoe_sw_create(struct net_device *netdev)
-{
-	int rc;
-	struct fc_lport *lp = NULL;
-	struct fcoe_softc *fc;
-	struct Scsi_Host *shost;
-
-	BUG_ON(!netdev);
-
-	printk(KERN_DEBUG "fcoe_sw_create:interface on %s\n",
-	       netdev->name);
-
-	lp = fcoe_hostlist_lookup(netdev);
-	if (lp)
-		return -EEXIST;
-
-	shost = fcoe_host_alloc(&fcoe_sw_shost_template,
-				sizeof(struct fcoe_softc));
-	if (!shost) {
-		FC_DBG("Could not allocate host structure\n");
-		return -ENOMEM;
-	}
-	lp = shost_priv(shost);
-	fc = lport_priv(lp);
-
-	/* configure fc_lport, e.g., em */
-	rc = fcoe_sw_lport_config(lp);
-	if (rc) {
-		FC_DBG("Could not configure lport\n");
-		goto out_host_put;
-	}
-
-	/* configure lport network properties */
-	rc = fcoe_sw_netdev_config(lp, netdev);
-	if (rc) {
-		FC_DBG("Could not configure netdev for lport\n");
-		goto out_host_put;
-	}
-
-	/* configure lport scsi host properties */
-	rc = fcoe_sw_shost_config(lp, shost, &netdev->dev);
-	if (rc) {
-		FC_DBG("Could not configure shost for lport\n");
-		goto out_host_put;
-	}
-
-	/* lport exch manager allocation */
-	rc = fcoe_sw_em_config(lp);
-	if (rc) {
-		FC_DBG("Could not configure em for lport\n");
-		goto out_host_put;
-	}
-
-	/* Initialize the library */
-	rc = fcoe_libfc_config(lp, &fcoe_sw_libfc_fcn_templ);
-	if (rc) {
-		FC_DBG("Could not configure libfc for lport!\n");
-		goto out_lp_destroy;
-	}
-
-	/* add to lports list */
-	fcoe_hostlist_add(lp);
-
-	lp->boot_time = jiffies;
-
-	fc_fabric_login(lp);
-
-	dev_hold(netdev);
-
-	return rc;
-
-out_lp_destroy:
-	fc_exch_mgr_free(lp->emp); /* Free the EM */
-out_host_put:
-	scsi_host_put(lp->host);
-	return rc;
-}
-
-/**
- * fcoe_sw_init() - attach to scsi transport
- *
- * Returns : 0 on success
- */
-int __init fcoe_sw_init(void)
-{
-	/* attach to scsi transport */
-	scsi_transport_fcoe_sw =
-		fc_attach_transport(&fcoe_sw_transport_function);
-
-	if (!scsi_transport_fcoe_sw) {
-		printk(KERN_ERR "fcoe_sw_init:fc_attach_transport() failed\n");
-		return -ENODEV;
-	}
-
-	return 0;
-}
-
-/**
- * fcoe_sw_exit() - detach from scsi transport
- *
- * Returns : 0 on success
- */
-int __exit fcoe_sw_exit(void)
-{
-	fc_release_transport(scsi_transport_fcoe_sw);
-	return 0;
-}
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 334db95..a81a8ec 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -56,6 +56,18 @@ static int debug_fcoe;
 
 #define FCOE_WORD_TO_BYTE  4
 
+#define FCOE_VERSION	"0.1"
+#define	FCOE_NAME	"fcoe"
+#define	FCOE_VENDOR	"Open-FCoE.org"
+
+#define FCOE_MAX_LUN		255
+#define FCOE_MAX_FCP_TARGET	256
+
+#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
+
+#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
+#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
+
 MODULE_AUTHOR("Open-FCoE.org");
 MODULE_DESCRIPTION("FCoE");
 MODULE_LICENSE("GPL");
@@ -79,6 +91,479 @@ static struct notifier_block fcoe_notifier = {
 	.notifier_call = fcoe_device_notification,
 };
 
+static struct scsi_transport_template *scsi_transport_fcoe_sw;
+
+struct fc_function_template fcoe_transport_function = {
+	.show_host_node_name = 1,
+	.show_host_port_name = 1,
+	.show_host_supported_classes = 1,
+	.show_host_supported_fc4s = 1,
+	.show_host_active_fc4s = 1,
+	.show_host_maxframe_size = 1,
+
+	.show_host_port_id = 1,
+	.show_host_supported_speeds = 1,
+	.get_host_speed = fc_get_host_speed,
+	.show_host_speed = 1,
+	.show_host_port_type = 1,
+	.get_host_port_state = fc_get_host_port_state,
+	.show_host_port_state = 1,
+	.show_host_symbolic_name = 1,
+
+	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+	.show_rport_maxframe_size = 1,
+	.show_rport_supported_classes = 1,
+
+	.show_host_fabric_name = 1,
+	.show_starget_node_name = 1,
+	.show_starget_port_name = 1,
+	.show_starget_port_id = 1,
+	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+	.show_rport_dev_loss_tmo = 1,
+	.get_fc_host_stats = fc_get_host_stats,
+	.issue_fc_host_lip = fcoe_reset,
+
+	.terminate_rport_io = fc_rport_terminate_io,
+};
+
+static struct scsi_host_template fcoe_shost_template = {
+	.module = THIS_MODULE,
+	.name = "FCoE Driver",
+	.proc_name = FCOE_NAME,
+	.queuecommand = fc_queuecommand,
+	.eh_abort_handler = fc_eh_abort,
+	.eh_device_reset_handler = fc_eh_device_reset,
+	.eh_host_reset_handler = fc_eh_host_reset,
+	.slave_alloc = fc_slave_alloc,
+	.change_queue_depth = fc_change_queue_depth,
+	.change_queue_type = fc_change_queue_type,
+	.this_id = -1,
+	.cmd_per_lun = 32,
+	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
+	.use_clustering = ENABLE_CLUSTERING,
+	.sg_tablesize = SG_ALL,
+	.max_sectors = 0xffff,
+};
+
+/**
+ * fcoe_lport_config() - sets up the fc_lport
+ * @lp: ptr to the fc_lport
+ * @shost: ptr to the parent scsi host
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_lport_config(struct fc_lport *lp)
+{
+	lp->link_up = 0;
+	lp->qfull = 0;
+	lp->max_retry_count = 3;
+	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
+	lp->r_a_tov = 2 * 2 * 1000;
+	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+
+	fc_lport_init_stats(lp);
+
+	/* lport fc_lport related configuration */
+	fc_lport_config(lp);
+
+	/* offload related configuration */
+	lp->crc_offload = 0;
+	lp->seq_offload = 0;
+	lp->lro_enabled = 0;
+	lp->lro_xid = 0;
+	lp->lso_max = 0;
+
+	return 0;
+}
+
+/**
+ * fcoe_netdev_config() - Set up netdev for SW FCoE
+ * @lp : ptr to the fc_lport
+ * @netdev : ptr to the associated netdevice struct
+ *
+ * Must be called after fcoe_lport_config() as it will use lport mutex
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
+{
+	u32 mfs;
+	u64 wwnn, wwpn;
+	struct fcoe_softc *fc;
+	u8 flogi_maddr[ETH_ALEN];
+
+	/* Setup lport private data to point to fcoe softc */
+	fc = lport_priv(lp);
+	fc->lp = lp;
+	fc->real_dev = netdev;
+	fc->phys_dev = netdev;
+
+	/* Require support for get_pauseparam ethtool op. */
+	if (netdev->priv_flags & IFF_802_1Q_VLAN)
+		fc->phys_dev = vlan_dev_real_dev(netdev);
+
+	/* Do not support for bonding device */
+	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
+	    (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
+	    (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
+		return -EOPNOTSUPP;
+	}
+
+	/*
+	 * Determine max frame size based on underlying device and optional
+	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
+	 * will return 0, so do this first.
+	 */
+	mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
+				   sizeof(struct fcoe_crc_eof));
+	if (fc_set_mfs(lp, mfs))
+		return -EINVAL;
+
+	if (!fcoe_link_ok(lp))
+		lp->link_up = 1;
+
+	/* offload features support */
+	if (fc->real_dev->features & NETIF_F_SG)
+		lp->sg_supp = 1;
+
+#ifdef NETIF_F_FCOE_CRC
+	if (netdev->features & NETIF_F_FCOE_CRC) {
+		lp->crc_offload = 1;
+		printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
+		       netdev->name);
+	}
+#endif
+#ifdef NETIF_F_FSO
+	if (netdev->features & NETIF_F_FSO) {
+		lp->seq_offload = 1;
+		lp->lso_max = netdev->gso_max_size;
+		printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
+		       netdev->name, lp->lso_max);
+	}
+#endif
+	if (netdev->fcoe_ddp_xid) {
+		lp->lro_enabled = 1;
+		lp->lro_xid = netdev->fcoe_ddp_xid;
+		printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
+		       netdev->name, lp->lro_xid);
+	}
+	skb_queue_head_init(&fc->fcoe_pending_queue);
+	fc->fcoe_pending_queue_active = 0;
+
+	/* setup Source Mac Address */
+	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
+	       fc->real_dev->addr_len);
+
+	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
+	fc_set_wwnn(lp, wwnn);
+	/* XXX - 3rd arg needs to be vlan id */
+	wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
+	fc_set_wwpn(lp, wwpn);
+
+	/*
+	 * Add FCoE MAC address as second unicast MAC address
+	 * or enter promiscuous mode if not capable of listening
+	 * for multiple unicast MACs.
+	 */
+	rtnl_lock();
+	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+	dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
+	rtnl_unlock();
+
+	/*
+	 * setup the receive function from ethernet driver
+	 * on the ethertype for the given device
+	 */
+	fc->fcoe_packet_type.func = fcoe_rcv;
+	fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+	fc->fcoe_packet_type.dev = fc->real_dev;
+	dev_add_pack(&fc->fcoe_packet_type);
+
+	return 0;
+}
+
+/**
+ * fcoe_shost_config() - Sets up fc_lport->host
+ * @lp : ptr to the fc_lport
+ * @shost : ptr to the associated scsi host
+ * @dev : device associated to scsi host
+ *
+ * Must be called after fcoe_lport_config() and fcoe_netdev_config()
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
+				struct device *dev)
+{
+	int rc = 0;
+
+	/* lport scsi host config */
+	lp->host = shost;
+
+	lp->host->max_lun = FCOE_MAX_LUN;
+	lp->host->max_id = FCOE_MAX_FCP_TARGET;
+	lp->host->max_channel = 0;
+	lp->host->transportt = scsi_transport_fcoe_sw;
+
+	/* add the new host to the SCSI-ml */
+	rc = scsi_add_host(lp->host, dev);
+	if (rc) {
+		FC_DBG("fcoe_shost_config:error on scsi_add_host\n");
+		return rc;
+	}
+	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
+		FCOE_NAME, FCOE_VERSION,
+		fcoe_netdev(lp)->name);
+
+	return 0;
+}
+
+/**
+ * fcoe_em_config() - 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)
+{
+	BUG_ON(lp->emp);
+
+	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
+				    FCOE_MIN_XID, FCOE_MAX_XID);
+	if (!lp->emp)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/**
+ * fcoe_if_destroy() - FCoE software HBA tear-down function
+ * @netdev: ptr to the associated net_device
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ */
+static int fcoe_if_destroy(struct net_device *netdev)
+{
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+	u8 flogi_maddr[ETH_ALEN];
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_if_destroy:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (!lp)
+		return -ENODEV;
+
+	fc = lport_priv(lp);
+
+	/* Logout of the fabric */
+	fc_fabric_logoff(lp);
+
+	/* Remove the instance from fcoe's list */
+	fcoe_hostlist_remove(lp);
+
+	/* Don't listen for Ethernet packets anymore */
+	dev_remove_pack(&fc->fcoe_packet_type);
+
+	/* Cleanup the fc_lport */
+	fc_lport_destroy(lp);
+	fc_fcp_destroy(lp);
+
+	/* Detach from the scsi-ml */
+	fc_remove_host(lp->host);
+	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);
+
+	/* Delete secondary MAC addresses */
+	rtnl_lock();
+	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
+	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+	rtnl_unlock();
+
+	/* Free the per-CPU revieve threads */
+	fcoe_percpu_clean(lp);
+
+	/* Free existing skbs */
+	fcoe_clean_pending_queue(lp);
+
+	/* Free memory used by statistical counters */
+	fc_lport_free_stats(lp);
+
+	/* Release the net_device and Scsi_Host */
+	dev_put(fc->real_dev);
+	scsi_host_put(lp->host);
+
+	return 0;
+}
+
+/*
+ * fcoe_ddp_setup - calls LLD's ddp_setup through net_device
+ * @lp:	the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ * @sgl: the scatterlist describing this transfer
+ * @sgc: number of sg items
+ *
+ * Returns : 0 no ddp
+ */
+static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid,
+			     struct scatterlist *sgl, unsigned int sgc)
+{
+	struct net_device *n = fcoe_netdev(lp);
+
+	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
+		return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
+
+	return 0;
+}
+
+/*
+ * fcoe_ddp_done - calls LLD's ddp_done through net_device
+ * @lp:	the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ *
+ * Returns : the length of data that have been completed by ddp
+ */
+static int fcoe_ddp_done(struct fc_lport *lp, u16 xid)
+{
+	struct net_device *n = fcoe_netdev(lp);
+
+	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
+		return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
+	return 0;
+}
+
+static struct libfc_function_template fcoe_libfc_fcn_templ = {
+	.frame_send = fcoe_xmit,
+	.ddp_setup = fcoe_ddp_setup,
+	.ddp_done = fcoe_ddp_done,
+};
+
+/**
+ * fcoe_if_create() - this function creates the fcoe interface
+ * @netdev: pointer the associated netdevice
+ *
+ * Creates fc_lport struct and scsi_host for lport, configures lport
+ * and starts fabric login.
+ *
+ * Returns : 0 on success
+ */
+static int fcoe_if_create(struct net_device *netdev)
+{
+	int rc;
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+	struct Scsi_Host *shost;
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_if_create:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (lp)
+		return -EEXIST;
+
+	shost = fcoe_host_alloc(&fcoe_shost_template,
+				sizeof(struct fcoe_softc));
+	if (!shost) {
+		FC_DBG("Could not allocate host structure\n");
+		return -ENOMEM;
+	}
+	lp = shost_priv(shost);
+	fc = lport_priv(lp);
+
+	/* configure fc_lport, e.g., em */
+	rc = fcoe_lport_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure lport\n");
+		goto out_host_put;
+	}
+
+	/* configure lport network properties */
+	rc = fcoe_netdev_config(lp, netdev);
+	if (rc) {
+		FC_DBG("Could not configure netdev for lport\n");
+		goto out_host_put;
+	}
+
+	/* configure lport scsi host properties */
+	rc = fcoe_shost_config(lp, shost, &netdev->dev);
+	if (rc) {
+		FC_DBG("Could not configure shost for lport\n");
+		goto out_host_put;
+	}
+
+	/* lport exch manager allocation */
+	rc = fcoe_em_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure em for lport\n");
+		goto out_host_put;
+	}
+
+	/* Initialize the library */
+	rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ);
+	if (rc) {
+		FC_DBG("Could not configure libfc for lport!\n");
+		goto out_lp_destroy;
+	}
+
+	/* add to lports list */
+	fcoe_hostlist_add(lp);
+
+	lp->boot_time = jiffies;
+
+	fc_fabric_login(lp);
+
+	dev_hold(netdev);
+
+	return rc;
+
+out_lp_destroy:
+	fc_exch_mgr_free(lp->emp); /* Free the EM */
+out_host_put:
+	scsi_host_put(lp->host);
+	return rc;
+}
+
+/**
+ * fcoe_if_init() - attach to scsi transport
+ *
+ * Returns : 0 on success
+ */
+static int __init fcoe_if_init(void)
+{
+	/* attach to scsi transport */
+	scsi_transport_fcoe_sw =
+		fc_attach_transport(&fcoe_transport_function);
+
+	if (!scsi_transport_fcoe_sw) {
+		printk(KERN_ERR "fcoe_init:fc_attach_transport() failed\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * fcoe_if_exit() - detach from scsi transport
+ *
+ * Returns : 0 on success
+ */
+int __exit fcoe_if_exit(void)
+{
+	fc_release_transport(scsi_transport_fcoe_sw);
+	return 0;
+}
+
 /**
  * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
  * @cpu: cpu index for the online cpu
@@ -1080,9 +1565,9 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
 		rc = -ENODEV;
 		goto out_putdev;
 	}
-	rc = fcoe_sw_destroy(netdev);
+	rc = fcoe_if_destroy(netdev);
 	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_sw_destroy(%s) failed\n",
+		printk(KERN_ERR "fcoe: fcoe_if_destroy(%s) failed\n",
 		       netdev->name);
 		rc = -EIO;
 		goto out_putdev;
@@ -1119,9 +1604,9 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 	}
 	fcoe_ethdrv_get(netdev);
 
-	rc = fcoe_sw_create(netdev);
+	rc = fcoe_if_create(netdev);
 	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_sw_create(%s) failed\n",
+		printk(KERN_ERR "fcoe: fcoe_if_create(%s) failed\n",
 		       netdev->name);
 		fcoe_ethdrv_put(netdev);
 		rc = -EIO;
@@ -1457,7 +1942,7 @@ static int __init fcoe_init(void)
 
 	mod_timer(&fcoe_timer, jiffies + (10 * HZ));
 
-	fcoe_sw_init();
+	fcoe_if_init();
 
 	return 0;
 
@@ -1487,7 +1972,7 @@ static void __exit fcoe_exit(void)
 
 	/* releases the associated fcoe hosts */
 	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
-		fcoe_sw_destroy(fc->real_dev);
+		fcoe_if_destroy(fc->real_dev);
 
 	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
 
@@ -1495,7 +1980,7 @@ static void __exit fcoe_exit(void)
 		fcoe_percpu_thread_destroy(cpu);
 	}
 
-	/* remove sw trasnport */
-	fcoe_sw_exit();
+	/* detach from scsi transport */
+	fcoe_if_exit();
 }
 module_exit(fcoe_exit);
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index dc64405..e99633c 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -145,10 +145,4 @@ int fcoe_hostlist_remove(const struct fc_lport *);
 
 struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *, int);
 int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
-
-/* fcoe sw hba */
-int __init fcoe_sw_init(void);
-int __exit fcoe_sw_exit(void);
-int fcoe_sw_create(struct net_device *);
-int fcoe_sw_destroy(struct net_device *);
 #endif /* _LIBFCOE_H */


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH] fcoe: renames libfcoe.c to fcoe.c as the only fcoe module file
  2009-03-17 18:42 ` [PATCH 08/14] fcoe: renames libfcoe.c to fcoe.c as the only fcoe module file Robert Love
  2009-03-24 23:27   ` [PATCH] " Robert Love
@ 2009-03-27 16:07   ` Robert Love
  1 sibling, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-27 16:07 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

From: Vasu Dev <vasu.dev@intel.com>

Renames libfcoe.c to fcoe.c, fcoe.c becomes the only
.c file for fcoe.ko.

Also deleted "$Id: Makefile" from fcoe module Makefle.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/Makefile  |    5 
 drivers/scsi/fcoe/fcoe.c    | 1986 +++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/fcoe/libfcoe.c | 1986 -------------------------------------------
 3 files changed, 1986 insertions(+), 1991 deletions(-)
 create mode 100644 drivers/scsi/fcoe/fcoe.c
 delete mode 100644 drivers/scsi/fcoe/libfcoe.c

diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index 2d9dae4..9b590ef 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -1,6 +1 @@
-# $Id: Makefile
-
 obj-$(CONFIG_FCOE) += fcoe.o
-
-fcoe-y := \
-	libfcoe.o
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
new file mode 100644
index 0000000..a81a8ec
--- /dev/null
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -0,0 +1,1986 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/kthread.h>
+#include <linux/crc32.h>
+#include <linux/cpu.h>
+#include <linux/fs.h>
+#include <linux/sysfs.h>
+#include <linux/ctype.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+#include <net/rtnetlink.h>
+
+#include <scsi/fc/fc_encaps.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
+#include <scsi/libfcoe.h>
+
+static int debug_fcoe;
+
+#define FCOE_MAX_QUEUE_DEPTH  256
+#define FCOE_LOW_QUEUE_DEPTH  32
+
+/* destination address mode */
+#define FCOE_GW_ADDR_MODE	    0x00
+#define FCOE_FCOUI_ADDR_MODE	    0x01
+
+#define FCOE_WORD_TO_BYTE  4
+
+#define FCOE_VERSION	"0.1"
+#define	FCOE_NAME	"fcoe"
+#define	FCOE_VENDOR	"Open-FCoE.org"
+
+#define FCOE_MAX_LUN		255
+#define FCOE_MAX_FCP_TARGET	256
+
+#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
+
+#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
+#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
+
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("FCoE");
+MODULE_LICENSE("GPL");
+
+/* fcoe host list */
+LIST_HEAD(fcoe_hostlist);
+DEFINE_RWLOCK(fcoe_hostlist_lock);
+DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
+DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
+
+
+/* Function Prototyes */
+static int fcoe_check_wait_queue(struct fc_lport *);
+static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
+static int fcoe_device_notification(struct notifier_block *, ulong, void *);
+static void fcoe_dev_setup(void);
+static void fcoe_dev_cleanup(void);
+
+/* notification function from net device */
+static struct notifier_block fcoe_notifier = {
+	.notifier_call = fcoe_device_notification,
+};
+
+static struct scsi_transport_template *scsi_transport_fcoe_sw;
+
+struct fc_function_template fcoe_transport_function = {
+	.show_host_node_name = 1,
+	.show_host_port_name = 1,
+	.show_host_supported_classes = 1,
+	.show_host_supported_fc4s = 1,
+	.show_host_active_fc4s = 1,
+	.show_host_maxframe_size = 1,
+
+	.show_host_port_id = 1,
+	.show_host_supported_speeds = 1,
+	.get_host_speed = fc_get_host_speed,
+	.show_host_speed = 1,
+	.show_host_port_type = 1,
+	.get_host_port_state = fc_get_host_port_state,
+	.show_host_port_state = 1,
+	.show_host_symbolic_name = 1,
+
+	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+	.show_rport_maxframe_size = 1,
+	.show_rport_supported_classes = 1,
+
+	.show_host_fabric_name = 1,
+	.show_starget_node_name = 1,
+	.show_starget_port_name = 1,
+	.show_starget_port_id = 1,
+	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+	.show_rport_dev_loss_tmo = 1,
+	.get_fc_host_stats = fc_get_host_stats,
+	.issue_fc_host_lip = fcoe_reset,
+
+	.terminate_rport_io = fc_rport_terminate_io,
+};
+
+static struct scsi_host_template fcoe_shost_template = {
+	.module = THIS_MODULE,
+	.name = "FCoE Driver",
+	.proc_name = FCOE_NAME,
+	.queuecommand = fc_queuecommand,
+	.eh_abort_handler = fc_eh_abort,
+	.eh_device_reset_handler = fc_eh_device_reset,
+	.eh_host_reset_handler = fc_eh_host_reset,
+	.slave_alloc = fc_slave_alloc,
+	.change_queue_depth = fc_change_queue_depth,
+	.change_queue_type = fc_change_queue_type,
+	.this_id = -1,
+	.cmd_per_lun = 32,
+	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
+	.use_clustering = ENABLE_CLUSTERING,
+	.sg_tablesize = SG_ALL,
+	.max_sectors = 0xffff,
+};
+
+/**
+ * fcoe_lport_config() - sets up the fc_lport
+ * @lp: ptr to the fc_lport
+ * @shost: ptr to the parent scsi host
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_lport_config(struct fc_lport *lp)
+{
+	lp->link_up = 0;
+	lp->qfull = 0;
+	lp->max_retry_count = 3;
+	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
+	lp->r_a_tov = 2 * 2 * 1000;
+	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+
+	fc_lport_init_stats(lp);
+
+	/* lport fc_lport related configuration */
+	fc_lport_config(lp);
+
+	/* offload related configuration */
+	lp->crc_offload = 0;
+	lp->seq_offload = 0;
+	lp->lro_enabled = 0;
+	lp->lro_xid = 0;
+	lp->lso_max = 0;
+
+	return 0;
+}
+
+/**
+ * fcoe_netdev_config() - Set up netdev for SW FCoE
+ * @lp : ptr to the fc_lport
+ * @netdev : ptr to the associated netdevice struct
+ *
+ * Must be called after fcoe_lport_config() as it will use lport mutex
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
+{
+	u32 mfs;
+	u64 wwnn, wwpn;
+	struct fcoe_softc *fc;
+	u8 flogi_maddr[ETH_ALEN];
+
+	/* Setup lport private data to point to fcoe softc */
+	fc = lport_priv(lp);
+	fc->lp = lp;
+	fc->real_dev = netdev;
+	fc->phys_dev = netdev;
+
+	/* Require support for get_pauseparam ethtool op. */
+	if (netdev->priv_flags & IFF_802_1Q_VLAN)
+		fc->phys_dev = vlan_dev_real_dev(netdev);
+
+	/* Do not support for bonding device */
+	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
+	    (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
+	    (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
+		return -EOPNOTSUPP;
+	}
+
+	/*
+	 * Determine max frame size based on underlying device and optional
+	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
+	 * will return 0, so do this first.
+	 */
+	mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
+				   sizeof(struct fcoe_crc_eof));
+	if (fc_set_mfs(lp, mfs))
+		return -EINVAL;
+
+	if (!fcoe_link_ok(lp))
+		lp->link_up = 1;
+
+	/* offload features support */
+	if (fc->real_dev->features & NETIF_F_SG)
+		lp->sg_supp = 1;
+
+#ifdef NETIF_F_FCOE_CRC
+	if (netdev->features & NETIF_F_FCOE_CRC) {
+		lp->crc_offload = 1;
+		printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
+		       netdev->name);
+	}
+#endif
+#ifdef NETIF_F_FSO
+	if (netdev->features & NETIF_F_FSO) {
+		lp->seq_offload = 1;
+		lp->lso_max = netdev->gso_max_size;
+		printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
+		       netdev->name, lp->lso_max);
+	}
+#endif
+	if (netdev->fcoe_ddp_xid) {
+		lp->lro_enabled = 1;
+		lp->lro_xid = netdev->fcoe_ddp_xid;
+		printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
+		       netdev->name, lp->lro_xid);
+	}
+	skb_queue_head_init(&fc->fcoe_pending_queue);
+	fc->fcoe_pending_queue_active = 0;
+
+	/* setup Source Mac Address */
+	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
+	       fc->real_dev->addr_len);
+
+	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
+	fc_set_wwnn(lp, wwnn);
+	/* XXX - 3rd arg needs to be vlan id */
+	wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
+	fc_set_wwpn(lp, wwpn);
+
+	/*
+	 * Add FCoE MAC address as second unicast MAC address
+	 * or enter promiscuous mode if not capable of listening
+	 * for multiple unicast MACs.
+	 */
+	rtnl_lock();
+	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+	dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
+	rtnl_unlock();
+
+	/*
+	 * setup the receive function from ethernet driver
+	 * on the ethertype for the given device
+	 */
+	fc->fcoe_packet_type.func = fcoe_rcv;
+	fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+	fc->fcoe_packet_type.dev = fc->real_dev;
+	dev_add_pack(&fc->fcoe_packet_type);
+
+	return 0;
+}
+
+/**
+ * fcoe_shost_config() - Sets up fc_lport->host
+ * @lp : ptr to the fc_lport
+ * @shost : ptr to the associated scsi host
+ * @dev : device associated to scsi host
+ *
+ * Must be called after fcoe_lport_config() and fcoe_netdev_config()
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
+				struct device *dev)
+{
+	int rc = 0;
+
+	/* lport scsi host config */
+	lp->host = shost;
+
+	lp->host->max_lun = FCOE_MAX_LUN;
+	lp->host->max_id = FCOE_MAX_FCP_TARGET;
+	lp->host->max_channel = 0;
+	lp->host->transportt = scsi_transport_fcoe_sw;
+
+	/* add the new host to the SCSI-ml */
+	rc = scsi_add_host(lp->host, dev);
+	if (rc) {
+		FC_DBG("fcoe_shost_config:error on scsi_add_host\n");
+		return rc;
+	}
+	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
+		FCOE_NAME, FCOE_VERSION,
+		fcoe_netdev(lp)->name);
+
+	return 0;
+}
+
+/**
+ * fcoe_em_config() - 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)
+{
+	BUG_ON(lp->emp);
+
+	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
+				    FCOE_MIN_XID, FCOE_MAX_XID);
+	if (!lp->emp)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/**
+ * fcoe_if_destroy() - FCoE software HBA tear-down function
+ * @netdev: ptr to the associated net_device
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ */
+static int fcoe_if_destroy(struct net_device *netdev)
+{
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+	u8 flogi_maddr[ETH_ALEN];
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_if_destroy:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (!lp)
+		return -ENODEV;
+
+	fc = lport_priv(lp);
+
+	/* Logout of the fabric */
+	fc_fabric_logoff(lp);
+
+	/* Remove the instance from fcoe's list */
+	fcoe_hostlist_remove(lp);
+
+	/* Don't listen for Ethernet packets anymore */
+	dev_remove_pack(&fc->fcoe_packet_type);
+
+	/* Cleanup the fc_lport */
+	fc_lport_destroy(lp);
+	fc_fcp_destroy(lp);
+
+	/* Detach from the scsi-ml */
+	fc_remove_host(lp->host);
+	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);
+
+	/* Delete secondary MAC addresses */
+	rtnl_lock();
+	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
+	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+	rtnl_unlock();
+
+	/* Free the per-CPU revieve threads */
+	fcoe_percpu_clean(lp);
+
+	/* Free existing skbs */
+	fcoe_clean_pending_queue(lp);
+
+	/* Free memory used by statistical counters */
+	fc_lport_free_stats(lp);
+
+	/* Release the net_device and Scsi_Host */
+	dev_put(fc->real_dev);
+	scsi_host_put(lp->host);
+
+	return 0;
+}
+
+/*
+ * fcoe_ddp_setup - calls LLD's ddp_setup through net_device
+ * @lp:	the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ * @sgl: the scatterlist describing this transfer
+ * @sgc: number of sg items
+ *
+ * Returns : 0 no ddp
+ */
+static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid,
+			     struct scatterlist *sgl, unsigned int sgc)
+{
+	struct net_device *n = fcoe_netdev(lp);
+
+	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
+		return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
+
+	return 0;
+}
+
+/*
+ * fcoe_ddp_done - calls LLD's ddp_done through net_device
+ * @lp:	the corresponding fc_lport
+ * @xid: the exchange id for this ddp transfer
+ *
+ * Returns : the length of data that have been completed by ddp
+ */
+static int fcoe_ddp_done(struct fc_lport *lp, u16 xid)
+{
+	struct net_device *n = fcoe_netdev(lp);
+
+	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
+		return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
+	return 0;
+}
+
+static struct libfc_function_template fcoe_libfc_fcn_templ = {
+	.frame_send = fcoe_xmit,
+	.ddp_setup = fcoe_ddp_setup,
+	.ddp_done = fcoe_ddp_done,
+};
+
+/**
+ * fcoe_if_create() - this function creates the fcoe interface
+ * @netdev: pointer the associated netdevice
+ *
+ * Creates fc_lport struct and scsi_host for lport, configures lport
+ * and starts fabric login.
+ *
+ * Returns : 0 on success
+ */
+static int fcoe_if_create(struct net_device *netdev)
+{
+	int rc;
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+	struct Scsi_Host *shost;
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_if_create:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (lp)
+		return -EEXIST;
+
+	shost = fcoe_host_alloc(&fcoe_shost_template,
+				sizeof(struct fcoe_softc));
+	if (!shost) {
+		FC_DBG("Could not allocate host structure\n");
+		return -ENOMEM;
+	}
+	lp = shost_priv(shost);
+	fc = lport_priv(lp);
+
+	/* configure fc_lport, e.g., em */
+	rc = fcoe_lport_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure lport\n");
+		goto out_host_put;
+	}
+
+	/* configure lport network properties */
+	rc = fcoe_netdev_config(lp, netdev);
+	if (rc) {
+		FC_DBG("Could not configure netdev for lport\n");
+		goto out_host_put;
+	}
+
+	/* configure lport scsi host properties */
+	rc = fcoe_shost_config(lp, shost, &netdev->dev);
+	if (rc) {
+		FC_DBG("Could not configure shost for lport\n");
+		goto out_host_put;
+	}
+
+	/* lport exch manager allocation */
+	rc = fcoe_em_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure em for lport\n");
+		goto out_host_put;
+	}
+
+	/* Initialize the library */
+	rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ);
+	if (rc) {
+		FC_DBG("Could not configure libfc for lport!\n");
+		goto out_lp_destroy;
+	}
+
+	/* add to lports list */
+	fcoe_hostlist_add(lp);
+
+	lp->boot_time = jiffies;
+
+	fc_fabric_login(lp);
+
+	dev_hold(netdev);
+
+	return rc;
+
+out_lp_destroy:
+	fc_exch_mgr_free(lp->emp); /* Free the EM */
+out_host_put:
+	scsi_host_put(lp->host);
+	return rc;
+}
+
+/**
+ * fcoe_if_init() - attach to scsi transport
+ *
+ * Returns : 0 on success
+ */
+static int __init fcoe_if_init(void)
+{
+	/* attach to scsi transport */
+	scsi_transport_fcoe_sw =
+		fc_attach_transport(&fcoe_transport_function);
+
+	if (!scsi_transport_fcoe_sw) {
+		printk(KERN_ERR "fcoe_init:fc_attach_transport() failed\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * fcoe_if_exit() - detach from scsi transport
+ *
+ * Returns : 0 on success
+ */
+int __exit fcoe_if_exit(void)
+{
+	fc_release_transport(scsi_transport_fcoe_sw);
+	return 0;
+}
+
+/**
+ * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
+ * @cpu: cpu index for the online cpu
+ */
+static void fcoe_percpu_thread_create(unsigned int cpu)
+{
+	struct fcoe_percpu_s *p;
+	struct task_struct *thread;
+
+	p = &per_cpu(fcoe_percpu, cpu);
+
+	thread = kthread_create(fcoe_percpu_receive_thread,
+				(void *)p, "fcoethread/%d", cpu);
+
+	if (likely(!IS_ERR(p->thread))) {
+		kthread_bind(thread, cpu);
+		wake_up_process(thread);
+
+		spin_lock_bh(&p->fcoe_rx_list.lock);
+		p->thread = thread;
+		spin_unlock_bh(&p->fcoe_rx_list.lock);
+	}
+}
+
+/**
+ * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
+ * @cpu: cpu index the rx thread is to be removed
+ *
+ * Destroys a per-CPU Rx thread. Any pending skbs are moved to the
+ * current CPU's Rx thread. If the thread being destroyed is bound to
+ * the CPU processing this context the skbs will be freed.
+ */
+static void fcoe_percpu_thread_destroy(unsigned int cpu)
+{
+	struct fcoe_percpu_s *p;
+	struct task_struct *thread;
+	struct page *crc_eof;
+	struct sk_buff *skb;
+#ifdef CONFIG_SMP
+	struct fcoe_percpu_s *p0;
+	unsigned targ_cpu = smp_processor_id();
+#endif /* CONFIG_SMP */
+
+	printk(KERN_DEBUG "fcoe: Destroying receive thread for CPU %d\n", cpu);
+
+	/* Prevent any new skbs from being queued for this CPU. */
+	p = &per_cpu(fcoe_percpu, cpu);
+	spin_lock_bh(&p->fcoe_rx_list.lock);
+	thread = p->thread;
+	p->thread = NULL;
+	crc_eof = p->crc_eof_page;
+	p->crc_eof_page = NULL;
+	p->crc_eof_offset = 0;
+	spin_unlock_bh(&p->fcoe_rx_list.lock);
+
+#ifdef CONFIG_SMP
+	/*
+	 * Don't bother moving the skb's if this context is running
+	 * on the same CPU that is having its thread destroyed. This
+	 * can easily happen when the module is removed.
+	 */
+	if (cpu != targ_cpu) {
+		p0 = &per_cpu(fcoe_percpu, targ_cpu);
+		spin_lock_bh(&p0->fcoe_rx_list.lock);
+		if (p0->thread) {
+			FC_DBG("Moving frames from CPU %d to CPU %d\n",
+			       cpu, targ_cpu);
+
+			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+				__skb_queue_tail(&p0->fcoe_rx_list, skb);
+			spin_unlock_bh(&p0->fcoe_rx_list.lock);
+		} else {
+			/*
+			 * The targeted CPU is not initialized and cannot accept
+			 * new  skbs. Unlock the targeted CPU and drop the skbs
+			 * on the CPU that is going offline.
+			 */
+			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+				kfree_skb(skb);
+			spin_unlock_bh(&p0->fcoe_rx_list.lock);
+		}
+	} else {
+		/*
+		 * This scenario occurs when the module is being removed
+		 * and all threads are being destroyed. skbs will continue
+		 * to be shifted from the CPU thread that is being removed
+		 * to the CPU thread associated with the CPU that is processing
+		 * the module removal. Once there is only one CPU Rx thread it
+		 * will reach this case and we will drop all skbs and later
+		 * stop the thread.
+		 */
+		spin_lock_bh(&p->fcoe_rx_list.lock);
+		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+			kfree_skb(skb);
+		spin_unlock_bh(&p->fcoe_rx_list.lock);
+	}
+#else
+	/*
+	 * This a non-SMP scenario where the singluar Rx thread is
+	 * being removed. Free all skbs and stop the thread.
+	 */
+	spin_lock_bh(&p->fcoe_rx_list.lock);
+	while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+		kfree_skb(skb);
+	spin_unlock_bh(&p->fcoe_rx_list.lock);
+#endif
+
+	if (thread)
+		kthread_stop(thread);
+
+	if (crc_eof)
+		put_page(crc_eof);
+}
+
+/**
+ * fcoe_cpu_callback() - fcoe cpu hotplug event callback
+ * @nfb: callback data block
+ * @action: event triggering the callback
+ * @hcpu: index for the cpu of this event
+ *
+ * This creates or destroys per cpu data for fcoe
+ *
+ * Returns NOTIFY_OK always.
+ */
+static int fcoe_cpu_callback(struct notifier_block *nfb,
+			     unsigned long action, void *hcpu)
+{
+	unsigned cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+	case CPU_ONLINE_FROZEN:
+		FC_DBG("CPU %x online: Create Rx thread\n", cpu);
+		fcoe_percpu_thread_create(cpu);
+		break;
+	case CPU_DEAD:
+	case CPU_DEAD_FROZEN:
+		FC_DBG("CPU %x offline: Remove Rx thread\n", cpu);
+		fcoe_percpu_thread_destroy(cpu);
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block fcoe_cpu_notifier = {
+	.notifier_call = fcoe_cpu_callback,
+};
+
+/**
+ * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ
+ * @skb: the receive skb
+ * @dev: associated net device
+ * @ptype: context
+ * @odldev: last device
+ *
+ * this function will receive the packet and build fc frame and pass it up
+ *
+ * Returns: 0 for success
+ */
+int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
+	     struct packet_type *ptype, struct net_device *olddev)
+{
+	struct fc_lport *lp;
+	struct fcoe_rcv_info *fr;
+	struct fcoe_softc *fc;
+	struct fc_frame_header *fh;
+	struct fcoe_percpu_s *fps;
+	unsigned short oxid;
+	unsigned int cpu = 0;
+
+	fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
+	lp = fc->lp;
+	if (unlikely(lp == NULL)) {
+		FC_DBG("cannot find hba structure");
+		goto err2;
+	}
+
+	if (unlikely(debug_fcoe)) {
+		FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p "
+		       "end:%p sum:%d dev:%s", skb->len, skb->data_len,
+		       skb->head, skb->data, skb_tail_pointer(skb),
+		       skb_end_pointer(skb), skb->csum,
+		       skb->dev ? skb->dev->name : "<NULL>");
+
+	}
+
+	/* check for FCOE packet type */
+	if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
+		FC_DBG("wrong FC type frame");
+		goto err;
+	}
+
+	/*
+	 * Check for minimum frame length, and make sure required FCoE
+	 * and FC headers are pulled into the linear data area.
+	 */
+	if (unlikely((skb->len < FCOE_MIN_FRAME) ||
+	    !pskb_may_pull(skb, FCOE_HEADER_LEN)))
+		goto err;
+
+	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.
+	 */
+	cpu = oxid & (num_online_cpus() - 1);
+#endif
+
+	fps = &per_cpu(fcoe_percpu, cpu);
+	spin_lock_bh(&fps->fcoe_rx_list.lock);
+	if (unlikely(!fps->thread)) {
+		/*
+		 * The targeted CPU is not ready, let's target
+		 * the first CPU now. For non-SMP systems this
+		 * will check the same CPU twice.
+		 */
+		FC_DBG("CPU is online, but no receive thread ready "
+		       "for incoming skb- using first online CPU.\n");
+
+		spin_unlock_bh(&fps->fcoe_rx_list.lock);
+		cpu = first_cpu(cpu_online_map);
+		fps = &per_cpu(fcoe_percpu, cpu);
+		spin_lock_bh(&fps->fcoe_rx_list.lock);
+		if (!fps->thread) {
+			spin_unlock_bh(&fps->fcoe_rx_list.lock);
+			goto err;
+		}
+	}
+
+	/*
+	 * We now have a valid CPU that we're targeting for
+	 * this skb. We also have this receive thread locked,
+	 * so we're free to queue skbs into it's queue.
+	 */
+	__skb_queue_tail(&fps->fcoe_rx_list, skb);
+	if (fps->fcoe_rx_list.qlen == 1)
+		wake_up_process(fps->thread);
+
+	spin_unlock_bh(&fps->fcoe_rx_list.lock);
+
+	return 0;
+err:
+	fc_lport_get_stats(lp)->ErrorFrames++;
+
+err2:
+	kfree_skb(skb);
+	return -1;
+}
+EXPORT_SYMBOL_GPL(fcoe_rcv);
+
+/**
+ * fcoe_start_io() - pass to netdev to start xmit for fcoe
+ * @skb: the skb to be xmitted
+ *
+ * Returns: 0 for success
+ */
+static inline int fcoe_start_io(struct sk_buff *skb)
+{
+	int rc;
+
+	skb_get(skb);
+	rc = dev_queue_xmit(skb);
+	if (rc != 0)
+		return rc;
+	kfree_skb(skb);
+	return 0;
+}
+
+/**
+ * fcoe_get_paged_crc_eof() - in case we need alloc a page for crc_eof
+ * @skb: the skb to be xmitted
+ * @tlen: total len
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
+{
+	struct fcoe_percpu_s *fps;
+	struct page *page;
+
+	fps = &get_cpu_var(fcoe_percpu);
+	page = fps->crc_eof_page;
+	if (!page) {
+		page = alloc_page(GFP_ATOMIC);
+		if (!page) {
+			put_cpu_var(fcoe_percpu);
+			return -ENOMEM;
+		}
+		fps->crc_eof_page = page;
+		fps->crc_eof_offset = 0;
+	}
+
+	get_page(page);
+	skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page,
+			   fps->crc_eof_offset, tlen);
+	skb->len += tlen;
+	skb->data_len += tlen;
+	skb->truesize += tlen;
+	fps->crc_eof_offset += sizeof(struct fcoe_crc_eof);
+
+	if (fps->crc_eof_offset >= PAGE_SIZE) {
+		fps->crc_eof_page = NULL;
+		fps->crc_eof_offset = 0;
+		put_page(page);
+	}
+	put_cpu_var(fcoe_percpu);
+	return 0;
+}
+
+/**
+ * fcoe_fc_crc() - calculates FC CRC in this fcoe skb
+ * @fp: the fc_frame containg data to be checksummed
+ *
+ * This uses crc32() to calculate the crc for fc frame
+ * Return   : 32 bit crc
+ */
+u32 fcoe_fc_crc(struct fc_frame *fp)
+{
+	struct sk_buff *skb = fp_skb(fp);
+	struct skb_frag_struct *frag;
+	unsigned char *data;
+	unsigned long off, len, clen;
+	u32 crc;
+	unsigned i;
+
+	crc = crc32(~0, skb->data, skb_headlen(skb));
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		frag = &skb_shinfo(skb)->frags[i];
+		off = frag->page_offset;
+		len = frag->size;
+		while (len > 0) {
+			clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK));
+			data = kmap_atomic(frag->page + (off >> PAGE_SHIFT),
+					   KM_SKB_DATA_SOFTIRQ);
+			crc = crc32(crc, data + (off & ~PAGE_MASK), clen);
+			kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ);
+			off += clen;
+			len -= clen;
+		}
+	}
+	return crc;
+}
+EXPORT_SYMBOL_GPL(fcoe_fc_crc);
+
+/**
+ * fcoe_xmit() - FCoE frame transmit function
+ * @lp:	the associated local port
+ * @fp: the fc_frame to be transmitted
+ *
+ * Return   : 0 for success
+ */
+int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
+{
+	int wlen, rc = 0;
+	u32 crc;
+	struct ethhdr *eh;
+	struct fcoe_crc_eof *cp;
+	struct sk_buff *skb;
+	struct fcoe_dev_stats *stats;
+	struct fc_frame_header *fh;
+	unsigned int hlen;		/* header length implies the version */
+	unsigned int tlen;		/* trailer length */
+	unsigned int elen;		/* eth header, may include vlan */
+	int flogi_in_progress = 0;
+	struct fcoe_softc *fc;
+	u8 sof, eof;
+	struct fcoe_hdr *hp;
+
+	WARN_ON((fr_len(fp) % sizeof(u32)) != 0);
+
+	fc = lport_priv(lp);
+	/*
+	 * if it is a flogi then we need to learn gw-addr
+	 * and my own fcid
+	 */
+	fh = fc_frame_header_get(fp);
+	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
+		if (fc_frame_payload_op(fp) == ELS_FLOGI) {
+			fc->flogi_oxid = ntohs(fh->fh_ox_id);
+			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
+			fc->flogi_progress = 1;
+			flogi_in_progress = 1;
+		} else if (fc->flogi_progress && ntoh24(fh->fh_s_id) != 0) {
+			/*
+			 * Here we must've gotten an SID by accepting an FLOGI
+			 * from a point-to-point connection.  Switch to using
+			 * the source mac based on the SID.  The destination
+			 * MAC in this case would have been set by receving the
+			 * FLOGI.
+			 */
+			fc_fcoe_set_mac(fc->data_src_addr, fh->fh_s_id);
+			fc->flogi_progress = 0;
+		}
+	}
+
+	skb = fp_skb(fp);
+	sof = fr_sof(fp);
+	eof = fr_eof(fp);
+
+	elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ?
+		sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr);
+	hlen = sizeof(struct fcoe_hdr);
+	tlen = sizeof(struct fcoe_crc_eof);
+	wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
+
+	/* crc offload */
+	if (likely(lp->crc_offload)) {
+		skb->ip_summed = CHECKSUM_PARTIAL;
+		skb->csum_start = skb_headroom(skb);
+		skb->csum_offset = skb->len;
+		crc = 0;
+	} else {
+		skb->ip_summed = CHECKSUM_NONE;
+		crc = fcoe_fc_crc(fp);
+	}
+
+	/* copy fc crc and eof to the skb buff */
+	if (skb_is_nonlinear(skb)) {
+		skb_frag_t *frag;
+		if (fcoe_get_paged_crc_eof(skb, tlen)) {
+			kfree_skb(skb);
+			return -ENOMEM;
+		}
+		frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
+		cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
+			+ frag->page_offset;
+	} else {
+		cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
+	}
+
+	memset(cp, 0, sizeof(*cp));
+	cp->fcoe_eof = eof;
+	cp->fcoe_crc32 = cpu_to_le32(~crc);
+
+	if (skb_is_nonlinear(skb)) {
+		kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ);
+		cp = NULL;
+	}
+
+	/* adjust skb netowrk/transport offsets to match mac/fcoe/fc */
+	skb_push(skb, elen + hlen);
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	skb->mac_len = elen;
+	skb->protocol = htons(ETH_P_FCOE);
+	skb->dev = fc->real_dev;
+
+	/* fill up mac and fcoe headers */
+	eh = eth_hdr(skb);
+	eh->h_proto = htons(ETH_P_FCOE);
+	if (fc->address_mode == FCOE_FCOUI_ADDR_MODE)
+		fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
+	else
+		/* insert GW address */
+		memcpy(eh->h_dest, fc->dest_addr, ETH_ALEN);
+
+	if (unlikely(flogi_in_progress))
+		memcpy(eh->h_source, fc->ctl_src_addr, ETH_ALEN);
+	else
+		memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN);
+
+	hp = (struct fcoe_hdr *)(eh + 1);
+	memset(hp, 0, sizeof(*hp));
+	if (FC_FCOE_VER)
+		FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
+	hp->fcoe_sof = sof;
+
+#ifdef NETIF_F_FSO
+	/* fcoe lso, mss is in max_payload which is non-zero for FCP data */
+	if (lp->seq_offload && fr_max_payload(fp)) {
+		skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
+		skb_shinfo(skb)->gso_size = fr_max_payload(fp);
+	} else {
+		skb_shinfo(skb)->gso_type = 0;
+		skb_shinfo(skb)->gso_size = 0;
+	}
+#endif
+	/* update tx stats: regardless if LLD fails */
+	stats = fc_lport_get_stats(lp);
+	stats->TxFrames++;
+	stats->TxWords += wlen;
+
+	/* send down to lld */
+	fr_dev(fp) = lp;
+	if (fc->fcoe_pending_queue.qlen)
+		rc = fcoe_check_wait_queue(lp);
+
+	if (rc == 0)
+		rc = fcoe_start_io(skb);
+
+	if (rc) {
+		spin_lock_bh(&fc->fcoe_pending_queue.lock);
+		__skb_queue_tail(&fc->fcoe_pending_queue, skb);
+		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+		if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+			lp->qfull = 1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_xmit);
+
+/**
+ * fcoe_percpu_receive_thread() - recv thread per cpu
+ * @arg: ptr to the fcoe per cpu struct
+ *
+ * Return: 0 for success
+ */
+int fcoe_percpu_receive_thread(void *arg)
+{
+	struct fcoe_percpu_s *p = arg;
+	u32 fr_len;
+	struct fc_lport *lp;
+	struct fcoe_rcv_info *fr;
+	struct fcoe_dev_stats *stats;
+	struct fc_frame_header *fh;
+	struct sk_buff *skb;
+	struct fcoe_crc_eof crc_eof;
+	struct fc_frame *fp;
+	u8 *mac = NULL;
+	struct fcoe_softc *fc;
+	struct fcoe_hdr *hp;
+
+	set_user_nice(current, -20);
+
+	while (!kthread_should_stop()) {
+
+		spin_lock_bh(&p->fcoe_rx_list.lock);
+		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			spin_unlock_bh(&p->fcoe_rx_list.lock);
+			schedule();
+			set_current_state(TASK_RUNNING);
+			if (kthread_should_stop())
+				return 0;
+			spin_lock_bh(&p->fcoe_rx_list.lock);
+		}
+		spin_unlock_bh(&p->fcoe_rx_list.lock);
+		fr = fcoe_dev_from_skb(skb);
+		lp = fr->fr_dev;
+		if (unlikely(lp == NULL)) {
+			FC_DBG("invalid HBA Structure");
+			kfree_skb(skb);
+			continue;
+		}
+
+		if (unlikely(debug_fcoe)) {
+			FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p "
+			       "tail:%p end:%p sum:%d dev:%s",
+			       skb->len, skb->data_len,
+			       skb->head, skb->data, skb_tail_pointer(skb),
+			       skb_end_pointer(skb), skb->csum,
+			       skb->dev ? skb->dev->name : "<NULL>");
+		}
+
+		/*
+		 * Save source MAC address before discarding header.
+		 */
+		fc = lport_priv(lp);
+		if (unlikely(fc->flogi_progress))
+			mac = eth_hdr(skb)->h_source;
+
+		if (skb_is_nonlinear(skb))
+			skb_linearize(skb);	/* not ideal */
+
+		/*
+		 * Frame length checks and setting up the header pointers
+		 * was done in fcoe_rcv already.
+		 */
+		hp = (struct fcoe_hdr *) skb_network_header(skb);
+		fh = (struct fc_frame_header *) skb_transport_header(skb);
+
+		stats = fc_lport_get_stats(lp);
+		if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
+			if (stats->ErrorFrames < 5)
+				printk(KERN_WARNING "FCoE version "
+				       "mismatch: The frame has "
+				       "version %x, but the "
+				       "initiator supports version "
+				       "%x\n", FC_FCOE_DECAPS_VER(hp),
+				       FC_FCOE_VER);
+			stats->ErrorFrames++;
+			kfree_skb(skb);
+			continue;
+		}
+
+		skb_pull(skb, sizeof(struct fcoe_hdr));
+		fr_len = skb->len - sizeof(struct fcoe_crc_eof);
+
+		stats->RxFrames++;
+		stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
+
+		fp = (struct fc_frame *)skb;
+		fc_frame_init(fp);
+		fr_dev(fp) = lp;
+		fr_sof(fp) = hp->fcoe_sof;
+
+		/* Copy out the CRC and EOF trailer for access */
+		if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
+			kfree_skb(skb);
+			continue;
+		}
+		fr_eof(fp) = crc_eof.fcoe_eof;
+		fr_crc(fp) = crc_eof.fcoe_crc32;
+		if (pskb_trim(skb, fr_len)) {
+			kfree_skb(skb);
+			continue;
+		}
+
+		/*
+		 * We only check CRC if no offload is available and if it is
+		 * it's solicited data, in which case, the FCP layer would
+		 * check it during the copy.
+		 */
+		if (lp->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
+			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+		else
+			fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
+
+		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);
+			continue;
+		}
+		if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+			if (le32_to_cpu(fr_crc(fp)) !=
+			    ~crc32(~0, skb->data, fr_len)) {
+				if (debug_fcoe || stats->InvalidCRCCount < 5)
+					printk(KERN_WARNING "fcoe: dropping "
+					       "frame with CRC error\n");
+				stats->InvalidCRCCount++;
+				stats->ErrorFrames++;
+				fc_frame_free(fp);
+				continue;
+			}
+			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+		}
+		/* non flogi and non data exchanges are handled here */
+		if (unlikely(fc->flogi_progress))
+			fcoe_recv_flogi(fc, fp, mac);
+		fc_exch_recv(lp, lp->emp, fp);
+	}
+	return 0;
+}
+
+/**
+ * fcoe_recv_flogi() - flogi receive function
+ * @fc: associated fcoe_softc
+ * @fp: the recieved frame
+ * @sa: the source address of this flogi
+ *
+ * This is responsible to parse the flogi response and sets the corresponding
+ * mac address for the initiator, eitehr OUI based or GW based.
+ *
+ * Returns: none
+ */
+static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa)
+{
+	struct fc_frame_header *fh;
+	u8 op;
+
+	fh = fc_frame_header_get(fp);
+	if (fh->fh_type != FC_TYPE_ELS)
+		return;
+	op = fc_frame_payload_op(fp);
+	if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
+	    fc->flogi_oxid == ntohs(fh->fh_ox_id)) {
+		/*
+		 * FLOGI accepted.
+		 * If the src mac addr is FC_OUI-based, then we mark the
+		 * address_mode flag to use FC_OUI-based Ethernet DA.
+		 * Otherwise we use the FCoE gateway addr
+		 */
+		if (!compare_ether_addr(sa, (u8[6]) FC_FCOE_FLOGI_MAC)) {
+			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
+		} else {
+			memcpy(fc->dest_addr, sa, ETH_ALEN);
+			fc->address_mode = FCOE_GW_ADDR_MODE;
+		}
+
+		/*
+		 * Remove any previously-set unicast MAC filter.
+		 * Add secondary FCoE MAC address filter for our OUI.
+		 */
+		rtnl_lock();
+		if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+			dev_unicast_delete(fc->real_dev, fc->data_src_addr,
+					   ETH_ALEN);
+		fc_fcoe_set_mac(fc->data_src_addr, fh->fh_d_id);
+		dev_unicast_add(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+		rtnl_unlock();
+
+		fc->flogi_progress = 0;
+	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
+		/*
+		 * Save source MAC for point-to-point responses.
+		 */
+		memcpy(fc->dest_addr, sa, ETH_ALEN);
+		fc->address_mode = FCOE_GW_ADDR_MODE;
+	}
+}
+
+/**
+ * fcoe_watchdog() - fcoe timer callback
+ * @vp:
+ *
+ * This checks the pending queue length for fcoe and set lport qfull
+ * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the
+ * fcoe_hostlist.
+ *
+ * Returns: 0 for success
+ */
+void fcoe_watchdog(ulong vp)
+{
+	struct fcoe_softc *fc;
+
+	read_lock(&fcoe_hostlist_lock);
+	list_for_each_entry(fc, &fcoe_hostlist, list) {
+		if (fc->lp)
+			fcoe_check_wait_queue(fc->lp);
+	}
+	read_unlock(&fcoe_hostlist_lock);
+
+	fcoe_timer.expires = jiffies + (1 * HZ);
+	add_timer(&fcoe_timer);
+}
+
+
+/**
+ * fcoe_check_wait_queue() - put the skb into fcoe pending xmit queue
+ * @lp: the fc_port for this skb
+ * @skb: the associated skb to be xmitted
+ *
+ * This empties the wait_queue, dequeue the head of the wait_queue queue
+ * and calls fcoe_start_io() for each packet, if all skb have been
+ * transmitted, return qlen or -1 if a error occurs, then restore
+ * wait_queue and  try again later.
+ *
+ * The wait_queue is used when the skb transmit fails. skb will go
+ * in the wait_queue which will be emptied by the time function OR
+ * by the next skb transmit.
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_check_wait_queue(struct fc_lport *lp)
+{
+	struct fcoe_softc *fc = lport_priv(lp);
+	struct sk_buff *skb;
+	int rc = -1;
+
+	spin_lock_bh(&fc->fcoe_pending_queue.lock);
+	if (fc->fcoe_pending_queue_active)
+		goto out;
+	fc->fcoe_pending_queue_active = 1;
+
+	while (fc->fcoe_pending_queue.qlen) {
+		/* keep qlen > 0 until fcoe_start_io succeeds */
+		fc->fcoe_pending_queue.qlen++;
+		skb = __skb_dequeue(&fc->fcoe_pending_queue);
+
+		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+		rc = fcoe_start_io(skb);
+		spin_lock_bh(&fc->fcoe_pending_queue.lock);
+
+		if (rc) {
+			__skb_queue_head(&fc->fcoe_pending_queue, skb);
+			/* undo temporary increment above */
+			fc->fcoe_pending_queue.qlen--;
+			break;
+		}
+		/* undo temporary increment above */
+		fc->fcoe_pending_queue.qlen--;
+	}
+
+	if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
+		lp->qfull = 0;
+	fc->fcoe_pending_queue_active = 0;
+	rc = fc->fcoe_pending_queue.qlen;
+out:
+	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+	return rc;
+}
+
+/**
+ * fcoe_dev_setup() - setup link change notification interface
+ */
+static void fcoe_dev_setup()
+{
+	/*
+	 * here setup a interface specific wd time to
+	 * monitor the link state
+	 */
+	register_netdevice_notifier(&fcoe_notifier);
+}
+
+/**
+ * fcoe_dev_setup() - cleanup link change notification interface
+ */
+static void fcoe_dev_cleanup(void)
+{
+	unregister_netdevice_notifier(&fcoe_notifier);
+}
+
+/**
+ * fcoe_device_notification() - netdev event notification callback
+ * @notifier: context of the notification
+ * @event: type of event
+ * @ptr: fixed array for output parsed ifname
+ *
+ * This function is called by the ethernet driver in case of link change event
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_device_notification(struct notifier_block *notifier,
+				    ulong event, void *ptr)
+{
+	struct fc_lport *lp = NULL;
+	struct net_device *real_dev = ptr;
+	struct fcoe_softc *fc;
+	struct fcoe_dev_stats *stats;
+	u32 new_link_up;
+	u32 mfs;
+	int rc = NOTIFY_OK;
+
+	read_lock(&fcoe_hostlist_lock);
+	list_for_each_entry(fc, &fcoe_hostlist, list) {
+		if (fc->real_dev == real_dev) {
+			lp = fc->lp;
+			break;
+		}
+	}
+	read_unlock(&fcoe_hostlist_lock);
+	if (lp == NULL) {
+		rc = NOTIFY_DONE;
+		goto out;
+	}
+
+	new_link_up = lp->link_up;
+	switch (event) {
+	case NETDEV_DOWN:
+	case NETDEV_GOING_DOWN:
+		new_link_up = 0;
+		break;
+	case NETDEV_UP:
+	case NETDEV_CHANGE:
+		new_link_up = !fcoe_link_ok(lp);
+		break;
+	case NETDEV_CHANGEMTU:
+		mfs = fc->real_dev->mtu -
+			(sizeof(struct fcoe_hdr) +
+			 sizeof(struct fcoe_crc_eof));
+		if (mfs >= FC_MIN_MAX_FRAME)
+			fc_set_mfs(lp, mfs);
+		new_link_up = !fcoe_link_ok(lp);
+		break;
+	case NETDEV_REGISTER:
+		break;
+	default:
+		FC_DBG("unknown event %ld call", event);
+	}
+	if (lp->link_up != new_link_up) {
+		if (new_link_up)
+			fc_linkup(lp);
+		else {
+			stats = fc_lport_get_stats(lp);
+			stats->LinkFailureCount++;
+			fc_linkdown(lp);
+			fcoe_clean_pending_queue(lp);
+		}
+	}
+out:
+	return rc;
+}
+
+/**
+ * fcoe_if_to_netdev() - parse a name buffer to get netdev
+ * @ifname: fixed array for output parsed ifname
+ * @buffer: incoming buffer to be copied
+ *
+ * Returns: NULL or ptr to netdeive
+ */
+static struct net_device *fcoe_if_to_netdev(const char *buffer)
+{
+	char *cp;
+	char ifname[IFNAMSIZ + 2];
+
+	if (buffer) {
+		strlcpy(ifname, buffer, IFNAMSIZ);
+		cp = ifname + strlen(ifname);
+		while (--cp >= ifname && *cp == '\n')
+			*cp = '\0';
+		return dev_get_by_name(&init_net, ifname);
+	}
+	return NULL;
+}
+
+/**
+ * fcoe_netdev_to_module_owner() - finds out the nic drive moddule of the netdev
+ * @netdev: the target netdev
+ *
+ * Returns: ptr to the struct module, NULL for failure
+ */
+static struct module *
+fcoe_netdev_to_module_owner(const struct net_device *netdev)
+{
+	struct device *dev;
+
+	if (!netdev)
+		return NULL;
+
+	dev = netdev->dev.parent;
+	if (!dev)
+		return NULL;
+
+	if (!dev->driver)
+		return NULL;
+
+	return dev->driver->owner;
+}
+
+/**
+ * fcoe_ethdrv_get() - Hold the Ethernet driver
+ * @netdev: the target netdev
+ *
+ * Holds the Ethernet driver module by try_module_get() for
+ * the corresponding netdev.
+ *
+ * Returns: 0 for succsss
+ */
+static int fcoe_ethdrv_get(const struct net_device *netdev)
+{
+	struct module *owner;
+
+	owner = fcoe_netdev_to_module_owner(netdev);
+	if (owner) {
+		printk(KERN_DEBUG "fcoe:hold driver module %s for %s\n",
+		       module_name(owner), netdev->name);
+		return  try_module_get(owner);
+	}
+	return -ENODEV;
+}
+
+/**
+ * fcoe_ethdrv_put() - Release the Ethernet driver
+ * @netdev: the target netdev
+ *
+ * Releases the Ethernet driver module by module_put for
+ * the corresponding netdev.
+ *
+ * Returns: 0 for succsss
+ */
+static int fcoe_ethdrv_put(const struct net_device *netdev)
+{
+	struct module *owner;
+
+	owner = fcoe_netdev_to_module_owner(netdev);
+	if (owner) {
+		printk(KERN_DEBUG "fcoe:release driver module %s for %s\n",
+		       module_name(owner), netdev->name);
+		module_put(owner);
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/**
+ * fcoe_destroy() - handles the destroy from sysfs
+ * @buffer: expcted to be a eth if name
+ * @kp: associated kernel param
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
+{
+	int rc;
+	struct net_device *netdev;
+
+	netdev = fcoe_if_to_netdev(buffer);
+	if (!netdev) {
+		rc = -ENODEV;
+		goto out_nodev;
+	}
+	/* look for existing lport */
+	if (!fcoe_hostlist_lookup(netdev)) {
+		rc = -ENODEV;
+		goto out_putdev;
+	}
+	rc = fcoe_if_destroy(netdev);
+	if (rc) {
+		printk(KERN_ERR "fcoe: fcoe_if_destroy(%s) failed\n",
+		       netdev->name);
+		rc = -EIO;
+		goto out_putdev;
+	}
+	fcoe_ethdrv_put(netdev);
+	rc = 0;
+out_putdev:
+	dev_put(netdev);
+out_nodev:
+	return rc;
+}
+
+/**
+ * fcoe_create() - Handles the create call from sysfs
+ * @buffer: expcted to be a eth if name
+ * @kp: associated kernel param
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_create(const char *buffer, struct kernel_param *kp)
+{
+	int rc;
+	struct net_device *netdev;
+
+	netdev = fcoe_if_to_netdev(buffer);
+	if (!netdev) {
+		rc = -ENODEV;
+		goto out_nodev;
+	}
+	/* look for existing lport */
+	if (fcoe_hostlist_lookup(netdev)) {
+		rc = -EEXIST;
+		goto out_putdev;
+	}
+	fcoe_ethdrv_get(netdev);
+
+	rc = fcoe_if_create(netdev);
+	if (rc) {
+		printk(KERN_ERR "fcoe: fcoe_if_create(%s) failed\n",
+		       netdev->name);
+		fcoe_ethdrv_put(netdev);
+		rc = -EIO;
+		goto out_putdev;
+	}
+	rc = 0;
+out_putdev:
+	dev_put(netdev);
+out_nodev:
+	return rc;
+}
+
+module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(create, "string");
+MODULE_PARM_DESC(create, "Create fcoe port using net device passed in.");
+module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(destroy, "string");
+MODULE_PARM_DESC(destroy, "Destroy fcoe port");
+
+/**
+ * fcoe_link_ok() - Check if link is ok for the fc_lport
+ * @lp: ptr to the fc_lport
+ *
+ * Any permanently-disqualifying conditions have been previously checked.
+ * This also updates the speed setting, which may change with link for 100/1000.
+ *
+ * This function should probably be checking for PAUSE support at some point
+ * in the future. Currently Per-priority-pause is not determinable using
+ * ethtool, so we shouldn't be restrictive until that problem is resolved.
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ *
+ */
+int fcoe_link_ok(struct fc_lport *lp)
+{
+	struct fcoe_softc *fc = lport_priv(lp);
+	struct net_device *dev = fc->real_dev;
+	struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+	int rc = 0;
+
+	if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) {
+		dev = fc->phys_dev;
+		if (dev->ethtool_ops->get_settings) {
+			dev->ethtool_ops->get_settings(dev, &ecmd);
+			lp->link_supported_speeds &=
+				~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
+			if (ecmd.supported & (SUPPORTED_1000baseT_Half |
+					      SUPPORTED_1000baseT_Full))
+				lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
+			if (ecmd.supported & SUPPORTED_10000baseT_Full)
+				lp->link_supported_speeds |=
+					FC_PORTSPEED_10GBIT;
+			if (ecmd.speed == SPEED_1000)
+				lp->link_speed = FC_PORTSPEED_1GBIT;
+			if (ecmd.speed == SPEED_10000)
+				lp->link_speed = FC_PORTSPEED_10GBIT;
+		}
+	} else
+		rc = -1;
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(fcoe_link_ok);
+
+/**
+ * fcoe_percpu_clean() - Clear the pending skbs for an lport
+ * @lp: the fc_lport
+ */
+void fcoe_percpu_clean(struct fc_lport *lp)
+{
+	struct fcoe_percpu_s *pp;
+	struct fcoe_rcv_info *fr;
+	struct sk_buff_head *list;
+	struct sk_buff *skb, *next;
+	struct sk_buff *head;
+	unsigned int cpu;
+
+	for_each_possible_cpu(cpu) {
+		pp = &per_cpu(fcoe_percpu, cpu);
+		spin_lock_bh(&pp->fcoe_rx_list.lock);
+		list = &pp->fcoe_rx_list;
+		head = list->next;
+		for (skb = head; skb != (struct sk_buff *)list;
+		     skb = next) {
+			next = skb->next;
+			fr = fcoe_dev_from_skb(skb);
+			if (fr->fr_dev == lp) {
+				__skb_unlink(skb, list);
+				kfree_skb(skb);
+			}
+		}
+		spin_unlock_bh(&pp->fcoe_rx_list.lock);
+	}
+}
+EXPORT_SYMBOL_GPL(fcoe_percpu_clean);
+
+/**
+ * fcoe_clean_pending_queue() - Dequeue a skb and free it
+ * @lp: the corresponding fc_lport
+ *
+ * Returns: none
+ */
+void fcoe_clean_pending_queue(struct fc_lport *lp)
+{
+	struct fcoe_softc  *fc = lport_priv(lp);
+	struct sk_buff *skb;
+
+	spin_lock_bh(&fc->fcoe_pending_queue.lock);
+	while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
+		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+		kfree_skb(skb);
+		spin_lock_bh(&fc->fcoe_pending_queue.lock);
+	}
+	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+}
+EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
+
+/**
+ * libfc_host_alloc() - Allocate a Scsi_Host with room for the fc_lport
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ * TODO: to libfc?
+ */
+static inline struct Scsi_Host *
+libfc_host_alloc(struct scsi_host_template *sht, int priv_size)
+{
+	return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
+}
+
+/**
+ * fcoe_host_alloc() - Allocate a Scsi_Host with room for the fcoe_softc
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ */
+struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *sht, int priv_size)
+{
+	return libfc_host_alloc(sht, sizeof(struct fcoe_softc) + priv_size);
+}
+EXPORT_SYMBOL_GPL(fcoe_host_alloc);
+
+/**
+ * fcoe_reset() - Resets the fcoe
+ * @shost: shost the reset is from
+ *
+ * Returns: always 0
+ */
+int fcoe_reset(struct Scsi_Host *shost)
+{
+	struct fc_lport *lport = shost_priv(shost);
+	fc_lport_reset(lport);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_reset);
+
+/**
+ * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN.
+ * @mac: mac address
+ * @scheme: check port
+ * @port: port indicator for converting
+ *
+ * Returns: u64 fc world wide name
+ */
+u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
+		      unsigned int scheme, unsigned int port)
+{
+	u64 wwn;
+	u64 host_mac;
+
+	/* The MAC is in NO, so flip only the low 48 bits */
+	host_mac = ((u64) mac[0] << 40) |
+		((u64) mac[1] << 32) |
+		((u64) mac[2] << 24) |
+		((u64) mac[3] << 16) |
+		((u64) mac[4] << 8) |
+		(u64) mac[5];
+
+	WARN_ON(host_mac >= (1ULL << 48));
+	wwn = host_mac | ((u64) scheme << 60);
+	switch (scheme) {
+	case 1:
+		WARN_ON(port != 0);
+		break;
+	case 2:
+		WARN_ON(port >= 0xfff);
+		wwn |= (u64) port << 48;
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+
+	return wwn;
+}
+EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
+
+/**
+ * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device
+ * @device: this is currently ptr to net_device
+ *
+ * Returns: NULL or the located fcoe_softc
+ */
+static struct fcoe_softc *
+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);
+			return fc;
+		}
+	}
+	read_unlock(&fcoe_hostlist_lock);
+	return NULL;
+}
+
+/**
+ * fcoe_hostlist_lookup() - Find the corresponding lport by netdev
+ * @netdev: ptr to net_device
+ *
+ * Returns: 0 for success
+ */
+struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
+{
+	struct fcoe_softc *fc;
+
+	fc = fcoe_hostlist_lookup_softc(netdev);
+
+	return (fc) ? fc->lp : NULL;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup);
+
+/**
+ * fcoe_hostlist_add() - Add a lport to lports list
+ * @lp: ptr to the fc_lport to badded
+ *
+ * Returns: 0 for success
+ */
+int fcoe_hostlist_add(const struct fc_lport *lp)
+{
+	struct fcoe_softc *fc;
+
+	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;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_add);
+
+/**
+ * fcoe_hostlist_remove() - remove a lport from lports list
+ * @lp: ptr to the fc_lport to badded
+ *
+ * Returns: 0 for success
+ */
+int fcoe_hostlist_remove(const struct fc_lport *lp)
+{
+	struct fcoe_softc *fc;
+
+	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);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_remove);
+
+/**
+ * fcoe_libfc_config() - sets up libfc related properties for lport
+ * @lp: ptr to the fc_lport
+ * @tt: libfc function template
+ *
+ * Returns : 0 for success
+ */
+int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
+{
+	/* Set the function pointers set by the LLDD */
+	memcpy(&lp->tt, tt, sizeof(*tt));
+	if (fc_fcp_init(lp))
+		return -ENOMEM;
+	fc_exch_init(lp);
+	fc_elsct_init(lp);
+	fc_lport_init(lp);
+	fc_rport_init(lp);
+	fc_disc_init(lp);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
+/**
+ * fcoe_init() - fcoe module loading initialization
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int __init fcoe_init(void)
+{
+	unsigned int cpu;
+	int rc = 0;
+	struct fcoe_percpu_s *p;
+
+	INIT_LIST_HEAD(&fcoe_hostlist);
+	rwlock_init(&fcoe_hostlist_lock);
+
+	for_each_possible_cpu(cpu) {
+		p = &per_cpu(fcoe_percpu, cpu);
+		skb_queue_head_init(&p->fcoe_rx_list);
+	}
+
+	for_each_online_cpu(cpu)
+		fcoe_percpu_thread_create(cpu);
+
+	/* Initialize per CPU interrupt thread */
+	rc = register_hotcpu_notifier(&fcoe_cpu_notifier);
+	if (rc)
+		goto out_free;
+
+	/* Setup link change notification */
+	fcoe_dev_setup();
+
+	setup_timer(&fcoe_timer, fcoe_watchdog, 0);
+
+	mod_timer(&fcoe_timer, jiffies + (10 * HZ));
+
+	fcoe_if_init();
+
+	return 0;
+
+out_free:
+	for_each_online_cpu(cpu) {
+		fcoe_percpu_thread_destroy(cpu);
+	}
+
+	return rc;
+}
+module_init(fcoe_init);
+
+/**
+ * fcoe_exit() - fcoe module unloading cleanup
+ *
+ * Returns 0 on success, negative on failure
+ */
+static void __exit fcoe_exit(void)
+{
+	unsigned int cpu;
+	struct fcoe_softc *fc, *tmp;
+
+	fcoe_dev_cleanup();
+
+	/* Stop the timer */
+	del_timer_sync(&fcoe_timer);
+
+	/* releases the associated fcoe hosts */
+	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
+		fcoe_if_destroy(fc->real_dev);
+
+	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
+
+	for_each_online_cpu(cpu) {
+		fcoe_percpu_thread_destroy(cpu);
+	}
+
+	/* detach from scsi transport */
+	fcoe_if_exit();
+}
+module_exit(fcoe_exit);
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
deleted file mode 100644
index a81a8ec..0000000
--- a/drivers/scsi/fcoe/libfcoe.c
+++ /dev/null
@@ -1,1986 +0,0 @@
-/*
- * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Maintained at www.Open-FCoE.org
- */
-
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/if_ether.h>
-#include <linux/if_vlan.h>
-#include <linux/kthread.h>
-#include <linux/crc32.h>
-#include <linux/cpu.h>
-#include <linux/fs.h>
-#include <linux/sysfs.h>
-#include <linux/ctype.h>
-#include <scsi/scsi_tcq.h>
-#include <scsi/scsicam.h>
-#include <scsi/scsi_transport.h>
-#include <scsi/scsi_transport_fc.h>
-#include <net/rtnetlink.h>
-
-#include <scsi/fc/fc_encaps.h>
-
-#include <scsi/libfc.h>
-#include <scsi/fc_frame.h>
-#include <scsi/libfcoe.h>
-
-static int debug_fcoe;
-
-#define FCOE_MAX_QUEUE_DEPTH  256
-#define FCOE_LOW_QUEUE_DEPTH  32
-
-/* destination address mode */
-#define FCOE_GW_ADDR_MODE	    0x00
-#define FCOE_FCOUI_ADDR_MODE	    0x01
-
-#define FCOE_WORD_TO_BYTE  4
-
-#define FCOE_VERSION	"0.1"
-#define	FCOE_NAME	"fcoe"
-#define	FCOE_VENDOR	"Open-FCoE.org"
-
-#define FCOE_MAX_LUN		255
-#define FCOE_MAX_FCP_TARGET	256
-
-#define FCOE_MAX_OUTSTANDING_COMMANDS	1024
-
-#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
-#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
-
-MODULE_AUTHOR("Open-FCoE.org");
-MODULE_DESCRIPTION("FCoE");
-MODULE_LICENSE("GPL");
-
-/* fcoe host list */
-LIST_HEAD(fcoe_hostlist);
-DEFINE_RWLOCK(fcoe_hostlist_lock);
-DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
-DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
-
-
-/* Function Prototyes */
-static int fcoe_check_wait_queue(struct fc_lport *);
-static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
-static int fcoe_device_notification(struct notifier_block *, ulong, void *);
-static void fcoe_dev_setup(void);
-static void fcoe_dev_cleanup(void);
-
-/* notification function from net device */
-static struct notifier_block fcoe_notifier = {
-	.notifier_call = fcoe_device_notification,
-};
-
-static struct scsi_transport_template *scsi_transport_fcoe_sw;
-
-struct fc_function_template fcoe_transport_function = {
-	.show_host_node_name = 1,
-	.show_host_port_name = 1,
-	.show_host_supported_classes = 1,
-	.show_host_supported_fc4s = 1,
-	.show_host_active_fc4s = 1,
-	.show_host_maxframe_size = 1,
-
-	.show_host_port_id = 1,
-	.show_host_supported_speeds = 1,
-	.get_host_speed = fc_get_host_speed,
-	.show_host_speed = 1,
-	.show_host_port_type = 1,
-	.get_host_port_state = fc_get_host_port_state,
-	.show_host_port_state = 1,
-	.show_host_symbolic_name = 1,
-
-	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
-	.show_rport_maxframe_size = 1,
-	.show_rport_supported_classes = 1,
-
-	.show_host_fabric_name = 1,
-	.show_starget_node_name = 1,
-	.show_starget_port_name = 1,
-	.show_starget_port_id = 1,
-	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
-	.show_rport_dev_loss_tmo = 1,
-	.get_fc_host_stats = fc_get_host_stats,
-	.issue_fc_host_lip = fcoe_reset,
-
-	.terminate_rport_io = fc_rport_terminate_io,
-};
-
-static struct scsi_host_template fcoe_shost_template = {
-	.module = THIS_MODULE,
-	.name = "FCoE Driver",
-	.proc_name = FCOE_NAME,
-	.queuecommand = fc_queuecommand,
-	.eh_abort_handler = fc_eh_abort,
-	.eh_device_reset_handler = fc_eh_device_reset,
-	.eh_host_reset_handler = fc_eh_host_reset,
-	.slave_alloc = fc_slave_alloc,
-	.change_queue_depth = fc_change_queue_depth,
-	.change_queue_type = fc_change_queue_type,
-	.this_id = -1,
-	.cmd_per_lun = 32,
-	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
-	.use_clustering = ENABLE_CLUSTERING,
-	.sg_tablesize = SG_ALL,
-	.max_sectors = 0xffff,
-};
-
-/**
- * fcoe_lport_config() - sets up the fc_lport
- * @lp: ptr to the fc_lport
- * @shost: ptr to the parent scsi host
- *
- * Returns: 0 for success
- */
-static int fcoe_lport_config(struct fc_lport *lp)
-{
-	lp->link_up = 0;
-	lp->qfull = 0;
-	lp->max_retry_count = 3;
-	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
-	lp->r_a_tov = 2 * 2 * 1000;
-	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
-			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
-
-	fc_lport_init_stats(lp);
-
-	/* lport fc_lport related configuration */
-	fc_lport_config(lp);
-
-	/* offload related configuration */
-	lp->crc_offload = 0;
-	lp->seq_offload = 0;
-	lp->lro_enabled = 0;
-	lp->lro_xid = 0;
-	lp->lso_max = 0;
-
-	return 0;
-}
-
-/**
- * fcoe_netdev_config() - Set up netdev for SW FCoE
- * @lp : ptr to the fc_lport
- * @netdev : ptr to the associated netdevice struct
- *
- * Must be called after fcoe_lport_config() as it will use lport mutex
- *
- * Returns : 0 for success
- */
-static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
-{
-	u32 mfs;
-	u64 wwnn, wwpn;
-	struct fcoe_softc *fc;
-	u8 flogi_maddr[ETH_ALEN];
-
-	/* Setup lport private data to point to fcoe softc */
-	fc = lport_priv(lp);
-	fc->lp = lp;
-	fc->real_dev = netdev;
-	fc->phys_dev = netdev;
-
-	/* Require support for get_pauseparam ethtool op. */
-	if (netdev->priv_flags & IFF_802_1Q_VLAN)
-		fc->phys_dev = vlan_dev_real_dev(netdev);
-
-	/* Do not support for bonding device */
-	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
-	    (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
-	    (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
-		return -EOPNOTSUPP;
-	}
-
-	/*
-	 * Determine max frame size based on underlying device and optional
-	 * user-configured limit.  If the MFS is too low, fcoe_link_ok()
-	 * will return 0, so do this first.
-	 */
-	mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
-				   sizeof(struct fcoe_crc_eof));
-	if (fc_set_mfs(lp, mfs))
-		return -EINVAL;
-
-	if (!fcoe_link_ok(lp))
-		lp->link_up = 1;
-
-	/* offload features support */
-	if (fc->real_dev->features & NETIF_F_SG)
-		lp->sg_supp = 1;
-
-#ifdef NETIF_F_FCOE_CRC
-	if (netdev->features & NETIF_F_FCOE_CRC) {
-		lp->crc_offload = 1;
-		printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n",
-		       netdev->name);
-	}
-#endif
-#ifdef NETIF_F_FSO
-	if (netdev->features & NETIF_F_FSO) {
-		lp->seq_offload = 1;
-		lp->lso_max = netdev->gso_max_size;
-		printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n",
-		       netdev->name, lp->lso_max);
-	}
-#endif
-	if (netdev->fcoe_ddp_xid) {
-		lp->lro_enabled = 1;
-		lp->lro_xid = netdev->fcoe_ddp_xid;
-		printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n",
-		       netdev->name, lp->lro_xid);
-	}
-	skb_queue_head_init(&fc->fcoe_pending_queue);
-	fc->fcoe_pending_queue_active = 0;
-
-	/* setup Source Mac Address */
-	memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
-	       fc->real_dev->addr_len);
-
-	wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
-	fc_set_wwnn(lp, wwnn);
-	/* XXX - 3rd arg needs to be vlan id */
-	wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
-	fc_set_wwpn(lp, wwpn);
-
-	/*
-	 * Add FCoE MAC address as second unicast MAC address
-	 * or enter promiscuous mode if not capable of listening
-	 * for multiple unicast MACs.
-	 */
-	rtnl_lock();
-	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-	dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
-	rtnl_unlock();
-
-	/*
-	 * setup the receive function from ethernet driver
-	 * on the ethertype for the given device
-	 */
-	fc->fcoe_packet_type.func = fcoe_rcv;
-	fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
-	fc->fcoe_packet_type.dev = fc->real_dev;
-	dev_add_pack(&fc->fcoe_packet_type);
-
-	return 0;
-}
-
-/**
- * fcoe_shost_config() - Sets up fc_lport->host
- * @lp : ptr to the fc_lport
- * @shost : ptr to the associated scsi host
- * @dev : device associated to scsi host
- *
- * Must be called after fcoe_lport_config() and fcoe_netdev_config()
- *
- * Returns : 0 for success
- */
-static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
-				struct device *dev)
-{
-	int rc = 0;
-
-	/* lport scsi host config */
-	lp->host = shost;
-
-	lp->host->max_lun = FCOE_MAX_LUN;
-	lp->host->max_id = FCOE_MAX_FCP_TARGET;
-	lp->host->max_channel = 0;
-	lp->host->transportt = scsi_transport_fcoe_sw;
-
-	/* add the new host to the SCSI-ml */
-	rc = scsi_add_host(lp->host, dev);
-	if (rc) {
-		FC_DBG("fcoe_shost_config:error on scsi_add_host\n");
-		return rc;
-	}
-	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
-		FCOE_NAME, FCOE_VERSION,
-		fcoe_netdev(lp)->name);
-
-	return 0;
-}
-
-/**
- * fcoe_em_config() - 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)
-{
-	BUG_ON(lp->emp);
-
-	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
-				    FCOE_MIN_XID, FCOE_MAX_XID);
-	if (!lp->emp)
-		return -ENOMEM;
-
-	return 0;
-}
-
-/**
- * fcoe_if_destroy() - FCoE software HBA tear-down function
- * @netdev: ptr to the associated net_device
- *
- * Returns: 0 if link is OK for use by FCoE.
- */
-static int fcoe_if_destroy(struct net_device *netdev)
-{
-	struct fc_lport *lp = NULL;
-	struct fcoe_softc *fc;
-	u8 flogi_maddr[ETH_ALEN];
-
-	BUG_ON(!netdev);
-
-	printk(KERN_DEBUG "fcoe_if_destroy:interface on %s\n",
-	       netdev->name);
-
-	lp = fcoe_hostlist_lookup(netdev);
-	if (!lp)
-		return -ENODEV;
-
-	fc = lport_priv(lp);
-
-	/* Logout of the fabric */
-	fc_fabric_logoff(lp);
-
-	/* Remove the instance from fcoe's list */
-	fcoe_hostlist_remove(lp);
-
-	/* Don't listen for Ethernet packets anymore */
-	dev_remove_pack(&fc->fcoe_packet_type);
-
-	/* Cleanup the fc_lport */
-	fc_lport_destroy(lp);
-	fc_fcp_destroy(lp);
-
-	/* Detach from the scsi-ml */
-	fc_remove_host(lp->host);
-	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);
-
-	/* Delete secondary MAC addresses */
-	rtnl_lock();
-	memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-	dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
-	if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
-		dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
-	rtnl_unlock();
-
-	/* Free the per-CPU revieve threads */
-	fcoe_percpu_clean(lp);
-
-	/* Free existing skbs */
-	fcoe_clean_pending_queue(lp);
-
-	/* Free memory used by statistical counters */
-	fc_lport_free_stats(lp);
-
-	/* Release the net_device and Scsi_Host */
-	dev_put(fc->real_dev);
-	scsi_host_put(lp->host);
-
-	return 0;
-}
-
-/*
- * fcoe_ddp_setup - calls LLD's ddp_setup through net_device
- * @lp:	the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- * @sgl: the scatterlist describing this transfer
- * @sgc: number of sg items
- *
- * Returns : 0 no ddp
- */
-static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid,
-			     struct scatterlist *sgl, unsigned int sgc)
-{
-	struct net_device *n = fcoe_netdev(lp);
-
-	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
-		return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
-
-	return 0;
-}
-
-/*
- * fcoe_ddp_done - calls LLD's ddp_done through net_device
- * @lp:	the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- *
- * Returns : the length of data that have been completed by ddp
- */
-static int fcoe_ddp_done(struct fc_lport *lp, u16 xid)
-{
-	struct net_device *n = fcoe_netdev(lp);
-
-	if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
-		return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
-	return 0;
-}
-
-static struct libfc_function_template fcoe_libfc_fcn_templ = {
-	.frame_send = fcoe_xmit,
-	.ddp_setup = fcoe_ddp_setup,
-	.ddp_done = fcoe_ddp_done,
-};
-
-/**
- * fcoe_if_create() - this function creates the fcoe interface
- * @netdev: pointer the associated netdevice
- *
- * Creates fc_lport struct and scsi_host for lport, configures lport
- * and starts fabric login.
- *
- * Returns : 0 on success
- */
-static int fcoe_if_create(struct net_device *netdev)
-{
-	int rc;
-	struct fc_lport *lp = NULL;
-	struct fcoe_softc *fc;
-	struct Scsi_Host *shost;
-
-	BUG_ON(!netdev);
-
-	printk(KERN_DEBUG "fcoe_if_create:interface on %s\n",
-	       netdev->name);
-
-	lp = fcoe_hostlist_lookup(netdev);
-	if (lp)
-		return -EEXIST;
-
-	shost = fcoe_host_alloc(&fcoe_shost_template,
-				sizeof(struct fcoe_softc));
-	if (!shost) {
-		FC_DBG("Could not allocate host structure\n");
-		return -ENOMEM;
-	}
-	lp = shost_priv(shost);
-	fc = lport_priv(lp);
-
-	/* configure fc_lport, e.g., em */
-	rc = fcoe_lport_config(lp);
-	if (rc) {
-		FC_DBG("Could not configure lport\n");
-		goto out_host_put;
-	}
-
-	/* configure lport network properties */
-	rc = fcoe_netdev_config(lp, netdev);
-	if (rc) {
-		FC_DBG("Could not configure netdev for lport\n");
-		goto out_host_put;
-	}
-
-	/* configure lport scsi host properties */
-	rc = fcoe_shost_config(lp, shost, &netdev->dev);
-	if (rc) {
-		FC_DBG("Could not configure shost for lport\n");
-		goto out_host_put;
-	}
-
-	/* lport exch manager allocation */
-	rc = fcoe_em_config(lp);
-	if (rc) {
-		FC_DBG("Could not configure em for lport\n");
-		goto out_host_put;
-	}
-
-	/* Initialize the library */
-	rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ);
-	if (rc) {
-		FC_DBG("Could not configure libfc for lport!\n");
-		goto out_lp_destroy;
-	}
-
-	/* add to lports list */
-	fcoe_hostlist_add(lp);
-
-	lp->boot_time = jiffies;
-
-	fc_fabric_login(lp);
-
-	dev_hold(netdev);
-
-	return rc;
-
-out_lp_destroy:
-	fc_exch_mgr_free(lp->emp); /* Free the EM */
-out_host_put:
-	scsi_host_put(lp->host);
-	return rc;
-}
-
-/**
- * fcoe_if_init() - attach to scsi transport
- *
- * Returns : 0 on success
- */
-static int __init fcoe_if_init(void)
-{
-	/* attach to scsi transport */
-	scsi_transport_fcoe_sw =
-		fc_attach_transport(&fcoe_transport_function);
-
-	if (!scsi_transport_fcoe_sw) {
-		printk(KERN_ERR "fcoe_init:fc_attach_transport() failed\n");
-		return -ENODEV;
-	}
-
-	return 0;
-}
-
-/**
- * fcoe_if_exit() - detach from scsi transport
- *
- * Returns : 0 on success
- */
-int __exit fcoe_if_exit(void)
-{
-	fc_release_transport(scsi_transport_fcoe_sw);
-	return 0;
-}
-
-/**
- * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
- * @cpu: cpu index for the online cpu
- */
-static void fcoe_percpu_thread_create(unsigned int cpu)
-{
-	struct fcoe_percpu_s *p;
-	struct task_struct *thread;
-
-	p = &per_cpu(fcoe_percpu, cpu);
-
-	thread = kthread_create(fcoe_percpu_receive_thread,
-				(void *)p, "fcoethread/%d", cpu);
-
-	if (likely(!IS_ERR(p->thread))) {
-		kthread_bind(thread, cpu);
-		wake_up_process(thread);
-
-		spin_lock_bh(&p->fcoe_rx_list.lock);
-		p->thread = thread;
-		spin_unlock_bh(&p->fcoe_rx_list.lock);
-	}
-}
-
-/**
- * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
- * @cpu: cpu index the rx thread is to be removed
- *
- * Destroys a per-CPU Rx thread. Any pending skbs are moved to the
- * current CPU's Rx thread. If the thread being destroyed is bound to
- * the CPU processing this context the skbs will be freed.
- */
-static void fcoe_percpu_thread_destroy(unsigned int cpu)
-{
-	struct fcoe_percpu_s *p;
-	struct task_struct *thread;
-	struct page *crc_eof;
-	struct sk_buff *skb;
-#ifdef CONFIG_SMP
-	struct fcoe_percpu_s *p0;
-	unsigned targ_cpu = smp_processor_id();
-#endif /* CONFIG_SMP */
-
-	printk(KERN_DEBUG "fcoe: Destroying receive thread for CPU %d\n", cpu);
-
-	/* Prevent any new skbs from being queued for this CPU. */
-	p = &per_cpu(fcoe_percpu, cpu);
-	spin_lock_bh(&p->fcoe_rx_list.lock);
-	thread = p->thread;
-	p->thread = NULL;
-	crc_eof = p->crc_eof_page;
-	p->crc_eof_page = NULL;
-	p->crc_eof_offset = 0;
-	spin_unlock_bh(&p->fcoe_rx_list.lock);
-
-#ifdef CONFIG_SMP
-	/*
-	 * Don't bother moving the skb's if this context is running
-	 * on the same CPU that is having its thread destroyed. This
-	 * can easily happen when the module is removed.
-	 */
-	if (cpu != targ_cpu) {
-		p0 = &per_cpu(fcoe_percpu, targ_cpu);
-		spin_lock_bh(&p0->fcoe_rx_list.lock);
-		if (p0->thread) {
-			FC_DBG("Moving frames from CPU %d to CPU %d\n",
-			       cpu, targ_cpu);
-
-			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-				__skb_queue_tail(&p0->fcoe_rx_list, skb);
-			spin_unlock_bh(&p0->fcoe_rx_list.lock);
-		} else {
-			/*
-			 * The targeted CPU is not initialized and cannot accept
-			 * new  skbs. Unlock the targeted CPU and drop the skbs
-			 * on the CPU that is going offline.
-			 */
-			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-				kfree_skb(skb);
-			spin_unlock_bh(&p0->fcoe_rx_list.lock);
-		}
-	} else {
-		/*
-		 * This scenario occurs when the module is being removed
-		 * and all threads are being destroyed. skbs will continue
-		 * to be shifted from the CPU thread that is being removed
-		 * to the CPU thread associated with the CPU that is processing
-		 * the module removal. Once there is only one CPU Rx thread it
-		 * will reach this case and we will drop all skbs and later
-		 * stop the thread.
-		 */
-		spin_lock_bh(&p->fcoe_rx_list.lock);
-		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-			kfree_skb(skb);
-		spin_unlock_bh(&p->fcoe_rx_list.lock);
-	}
-#else
-	/*
-	 * This a non-SMP scenario where the singluar Rx thread is
-	 * being removed. Free all skbs and stop the thread.
-	 */
-	spin_lock_bh(&p->fcoe_rx_list.lock);
-	while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-		kfree_skb(skb);
-	spin_unlock_bh(&p->fcoe_rx_list.lock);
-#endif
-
-	if (thread)
-		kthread_stop(thread);
-
-	if (crc_eof)
-		put_page(crc_eof);
-}
-
-/**
- * fcoe_cpu_callback() - fcoe cpu hotplug event callback
- * @nfb: callback data block
- * @action: event triggering the callback
- * @hcpu: index for the cpu of this event
- *
- * This creates or destroys per cpu data for fcoe
- *
- * Returns NOTIFY_OK always.
- */
-static int fcoe_cpu_callback(struct notifier_block *nfb,
-			     unsigned long action, void *hcpu)
-{
-	unsigned cpu = (unsigned long)hcpu;
-
-	switch (action) {
-	case CPU_ONLINE:
-	case CPU_ONLINE_FROZEN:
-		FC_DBG("CPU %x online: Create Rx thread\n", cpu);
-		fcoe_percpu_thread_create(cpu);
-		break;
-	case CPU_DEAD:
-	case CPU_DEAD_FROZEN:
-		FC_DBG("CPU %x offline: Remove Rx thread\n", cpu);
-		fcoe_percpu_thread_destroy(cpu);
-		break;
-	default:
-		break;
-	}
-	return NOTIFY_OK;
-}
-
-static struct notifier_block fcoe_cpu_notifier = {
-	.notifier_call = fcoe_cpu_callback,
-};
-
-/**
- * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ
- * @skb: the receive skb
- * @dev: associated net device
- * @ptype: context
- * @odldev: last device
- *
- * this function will receive the packet and build fc frame and pass it up
- *
- * Returns: 0 for success
- */
-int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
-	     struct packet_type *ptype, struct net_device *olddev)
-{
-	struct fc_lport *lp;
-	struct fcoe_rcv_info *fr;
-	struct fcoe_softc *fc;
-	struct fc_frame_header *fh;
-	struct fcoe_percpu_s *fps;
-	unsigned short oxid;
-	unsigned int cpu = 0;
-
-	fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
-	lp = fc->lp;
-	if (unlikely(lp == NULL)) {
-		FC_DBG("cannot find hba structure");
-		goto err2;
-	}
-
-	if (unlikely(debug_fcoe)) {
-		FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p "
-		       "end:%p sum:%d dev:%s", skb->len, skb->data_len,
-		       skb->head, skb->data, skb_tail_pointer(skb),
-		       skb_end_pointer(skb), skb->csum,
-		       skb->dev ? skb->dev->name : "<NULL>");
-
-	}
-
-	/* check for FCOE packet type */
-	if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
-		FC_DBG("wrong FC type frame");
-		goto err;
-	}
-
-	/*
-	 * Check for minimum frame length, and make sure required FCoE
-	 * and FC headers are pulled into the linear data area.
-	 */
-	if (unlikely((skb->len < FCOE_MIN_FRAME) ||
-	    !pskb_may_pull(skb, FCOE_HEADER_LEN)))
-		goto err;
-
-	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.
-	 */
-	cpu = oxid & (num_online_cpus() - 1);
-#endif
-
-	fps = &per_cpu(fcoe_percpu, cpu);
-	spin_lock_bh(&fps->fcoe_rx_list.lock);
-	if (unlikely(!fps->thread)) {
-		/*
-		 * The targeted CPU is not ready, let's target
-		 * the first CPU now. For non-SMP systems this
-		 * will check the same CPU twice.
-		 */
-		FC_DBG("CPU is online, but no receive thread ready "
-		       "for incoming skb- using first online CPU.\n");
-
-		spin_unlock_bh(&fps->fcoe_rx_list.lock);
-		cpu = first_cpu(cpu_online_map);
-		fps = &per_cpu(fcoe_percpu, cpu);
-		spin_lock_bh(&fps->fcoe_rx_list.lock);
-		if (!fps->thread) {
-			spin_unlock_bh(&fps->fcoe_rx_list.lock);
-			goto err;
-		}
-	}
-
-	/*
-	 * We now have a valid CPU that we're targeting for
-	 * this skb. We also have this receive thread locked,
-	 * so we're free to queue skbs into it's queue.
-	 */
-	__skb_queue_tail(&fps->fcoe_rx_list, skb);
-	if (fps->fcoe_rx_list.qlen == 1)
-		wake_up_process(fps->thread);
-
-	spin_unlock_bh(&fps->fcoe_rx_list.lock);
-
-	return 0;
-err:
-	fc_lport_get_stats(lp)->ErrorFrames++;
-
-err2:
-	kfree_skb(skb);
-	return -1;
-}
-EXPORT_SYMBOL_GPL(fcoe_rcv);
-
-/**
- * fcoe_start_io() - pass to netdev to start xmit for fcoe
- * @skb: the skb to be xmitted
- *
- * Returns: 0 for success
- */
-static inline int fcoe_start_io(struct sk_buff *skb)
-{
-	int rc;
-
-	skb_get(skb);
-	rc = dev_queue_xmit(skb);
-	if (rc != 0)
-		return rc;
-	kfree_skb(skb);
-	return 0;
-}
-
-/**
- * fcoe_get_paged_crc_eof() - in case we need alloc a page for crc_eof
- * @skb: the skb to be xmitted
- * @tlen: total len
- *
- * Returns: 0 for success
- */
-static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
-{
-	struct fcoe_percpu_s *fps;
-	struct page *page;
-
-	fps = &get_cpu_var(fcoe_percpu);
-	page = fps->crc_eof_page;
-	if (!page) {
-		page = alloc_page(GFP_ATOMIC);
-		if (!page) {
-			put_cpu_var(fcoe_percpu);
-			return -ENOMEM;
-		}
-		fps->crc_eof_page = page;
-		fps->crc_eof_offset = 0;
-	}
-
-	get_page(page);
-	skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page,
-			   fps->crc_eof_offset, tlen);
-	skb->len += tlen;
-	skb->data_len += tlen;
-	skb->truesize += tlen;
-	fps->crc_eof_offset += sizeof(struct fcoe_crc_eof);
-
-	if (fps->crc_eof_offset >= PAGE_SIZE) {
-		fps->crc_eof_page = NULL;
-		fps->crc_eof_offset = 0;
-		put_page(page);
-	}
-	put_cpu_var(fcoe_percpu);
-	return 0;
-}
-
-/**
- * fcoe_fc_crc() - calculates FC CRC in this fcoe skb
- * @fp: the fc_frame containg data to be checksummed
- *
- * This uses crc32() to calculate the crc for fc frame
- * Return   : 32 bit crc
- */
-u32 fcoe_fc_crc(struct fc_frame *fp)
-{
-	struct sk_buff *skb = fp_skb(fp);
-	struct skb_frag_struct *frag;
-	unsigned char *data;
-	unsigned long off, len, clen;
-	u32 crc;
-	unsigned i;
-
-	crc = crc32(~0, skb->data, skb_headlen(skb));
-
-	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-		frag = &skb_shinfo(skb)->frags[i];
-		off = frag->page_offset;
-		len = frag->size;
-		while (len > 0) {
-			clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK));
-			data = kmap_atomic(frag->page + (off >> PAGE_SHIFT),
-					   KM_SKB_DATA_SOFTIRQ);
-			crc = crc32(crc, data + (off & ~PAGE_MASK), clen);
-			kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ);
-			off += clen;
-			len -= clen;
-		}
-	}
-	return crc;
-}
-EXPORT_SYMBOL_GPL(fcoe_fc_crc);
-
-/**
- * fcoe_xmit() - FCoE frame transmit function
- * @lp:	the associated local port
- * @fp: the fc_frame to be transmitted
- *
- * Return   : 0 for success
- */
-int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
-{
-	int wlen, rc = 0;
-	u32 crc;
-	struct ethhdr *eh;
-	struct fcoe_crc_eof *cp;
-	struct sk_buff *skb;
-	struct fcoe_dev_stats *stats;
-	struct fc_frame_header *fh;
-	unsigned int hlen;		/* header length implies the version */
-	unsigned int tlen;		/* trailer length */
-	unsigned int elen;		/* eth header, may include vlan */
-	int flogi_in_progress = 0;
-	struct fcoe_softc *fc;
-	u8 sof, eof;
-	struct fcoe_hdr *hp;
-
-	WARN_ON((fr_len(fp) % sizeof(u32)) != 0);
-
-	fc = lport_priv(lp);
-	/*
-	 * if it is a flogi then we need to learn gw-addr
-	 * and my own fcid
-	 */
-	fh = fc_frame_header_get(fp);
-	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
-		if (fc_frame_payload_op(fp) == ELS_FLOGI) {
-			fc->flogi_oxid = ntohs(fh->fh_ox_id);
-			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
-			fc->flogi_progress = 1;
-			flogi_in_progress = 1;
-		} else if (fc->flogi_progress && ntoh24(fh->fh_s_id) != 0) {
-			/*
-			 * Here we must've gotten an SID by accepting an FLOGI
-			 * from a point-to-point connection.  Switch to using
-			 * the source mac based on the SID.  The destination
-			 * MAC in this case would have been set by receving the
-			 * FLOGI.
-			 */
-			fc_fcoe_set_mac(fc->data_src_addr, fh->fh_s_id);
-			fc->flogi_progress = 0;
-		}
-	}
-
-	skb = fp_skb(fp);
-	sof = fr_sof(fp);
-	eof = fr_eof(fp);
-
-	elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ?
-		sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr);
-	hlen = sizeof(struct fcoe_hdr);
-	tlen = sizeof(struct fcoe_crc_eof);
-	wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
-
-	/* crc offload */
-	if (likely(lp->crc_offload)) {
-		skb->ip_summed = CHECKSUM_PARTIAL;
-		skb->csum_start = skb_headroom(skb);
-		skb->csum_offset = skb->len;
-		crc = 0;
-	} else {
-		skb->ip_summed = CHECKSUM_NONE;
-		crc = fcoe_fc_crc(fp);
-	}
-
-	/* copy fc crc and eof to the skb buff */
-	if (skb_is_nonlinear(skb)) {
-		skb_frag_t *frag;
-		if (fcoe_get_paged_crc_eof(skb, tlen)) {
-			kfree_skb(skb);
-			return -ENOMEM;
-		}
-		frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
-		cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
-			+ frag->page_offset;
-	} else {
-		cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
-	}
-
-	memset(cp, 0, sizeof(*cp));
-	cp->fcoe_eof = eof;
-	cp->fcoe_crc32 = cpu_to_le32(~crc);
-
-	if (skb_is_nonlinear(skb)) {
-		kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ);
-		cp = NULL;
-	}
-
-	/* adjust skb netowrk/transport offsets to match mac/fcoe/fc */
-	skb_push(skb, elen + hlen);
-	skb_reset_mac_header(skb);
-	skb_reset_network_header(skb);
-	skb->mac_len = elen;
-	skb->protocol = htons(ETH_P_FCOE);
-	skb->dev = fc->real_dev;
-
-	/* fill up mac and fcoe headers */
-	eh = eth_hdr(skb);
-	eh->h_proto = htons(ETH_P_FCOE);
-	if (fc->address_mode == FCOE_FCOUI_ADDR_MODE)
-		fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
-	else
-		/* insert GW address */
-		memcpy(eh->h_dest, fc->dest_addr, ETH_ALEN);
-
-	if (unlikely(flogi_in_progress))
-		memcpy(eh->h_source, fc->ctl_src_addr, ETH_ALEN);
-	else
-		memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN);
-
-	hp = (struct fcoe_hdr *)(eh + 1);
-	memset(hp, 0, sizeof(*hp));
-	if (FC_FCOE_VER)
-		FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
-	hp->fcoe_sof = sof;
-
-#ifdef NETIF_F_FSO
-	/* fcoe lso, mss is in max_payload which is non-zero for FCP data */
-	if (lp->seq_offload && fr_max_payload(fp)) {
-		skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
-		skb_shinfo(skb)->gso_size = fr_max_payload(fp);
-	} else {
-		skb_shinfo(skb)->gso_type = 0;
-		skb_shinfo(skb)->gso_size = 0;
-	}
-#endif
-	/* update tx stats: regardless if LLD fails */
-	stats = fc_lport_get_stats(lp);
-	stats->TxFrames++;
-	stats->TxWords += wlen;
-
-	/* send down to lld */
-	fr_dev(fp) = lp;
-	if (fc->fcoe_pending_queue.qlen)
-		rc = fcoe_check_wait_queue(lp);
-
-	if (rc == 0)
-		rc = fcoe_start_io(skb);
-
-	if (rc) {
-		spin_lock_bh(&fc->fcoe_pending_queue.lock);
-		__skb_queue_tail(&fc->fcoe_pending_queue, skb);
-		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-		if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
-			lp->qfull = 1;
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_xmit);
-
-/**
- * fcoe_percpu_receive_thread() - recv thread per cpu
- * @arg: ptr to the fcoe per cpu struct
- *
- * Return: 0 for success
- */
-int fcoe_percpu_receive_thread(void *arg)
-{
-	struct fcoe_percpu_s *p = arg;
-	u32 fr_len;
-	struct fc_lport *lp;
-	struct fcoe_rcv_info *fr;
-	struct fcoe_dev_stats *stats;
-	struct fc_frame_header *fh;
-	struct sk_buff *skb;
-	struct fcoe_crc_eof crc_eof;
-	struct fc_frame *fp;
-	u8 *mac = NULL;
-	struct fcoe_softc *fc;
-	struct fcoe_hdr *hp;
-
-	set_user_nice(current, -20);
-
-	while (!kthread_should_stop()) {
-
-		spin_lock_bh(&p->fcoe_rx_list.lock);
-		while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) {
-			set_current_state(TASK_INTERRUPTIBLE);
-			spin_unlock_bh(&p->fcoe_rx_list.lock);
-			schedule();
-			set_current_state(TASK_RUNNING);
-			if (kthread_should_stop())
-				return 0;
-			spin_lock_bh(&p->fcoe_rx_list.lock);
-		}
-		spin_unlock_bh(&p->fcoe_rx_list.lock);
-		fr = fcoe_dev_from_skb(skb);
-		lp = fr->fr_dev;
-		if (unlikely(lp == NULL)) {
-			FC_DBG("invalid HBA Structure");
-			kfree_skb(skb);
-			continue;
-		}
-
-		if (unlikely(debug_fcoe)) {
-			FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p "
-			       "tail:%p end:%p sum:%d dev:%s",
-			       skb->len, skb->data_len,
-			       skb->head, skb->data, skb_tail_pointer(skb),
-			       skb_end_pointer(skb), skb->csum,
-			       skb->dev ? skb->dev->name : "<NULL>");
-		}
-
-		/*
-		 * Save source MAC address before discarding header.
-		 */
-		fc = lport_priv(lp);
-		if (unlikely(fc->flogi_progress))
-			mac = eth_hdr(skb)->h_source;
-
-		if (skb_is_nonlinear(skb))
-			skb_linearize(skb);	/* not ideal */
-
-		/*
-		 * Frame length checks and setting up the header pointers
-		 * was done in fcoe_rcv already.
-		 */
-		hp = (struct fcoe_hdr *) skb_network_header(skb);
-		fh = (struct fc_frame_header *) skb_transport_header(skb);
-
-		stats = fc_lport_get_stats(lp);
-		if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
-			if (stats->ErrorFrames < 5)
-				printk(KERN_WARNING "FCoE version "
-				       "mismatch: The frame has "
-				       "version %x, but the "
-				       "initiator supports version "
-				       "%x\n", FC_FCOE_DECAPS_VER(hp),
-				       FC_FCOE_VER);
-			stats->ErrorFrames++;
-			kfree_skb(skb);
-			continue;
-		}
-
-		skb_pull(skb, sizeof(struct fcoe_hdr));
-		fr_len = skb->len - sizeof(struct fcoe_crc_eof);
-
-		stats->RxFrames++;
-		stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
-
-		fp = (struct fc_frame *)skb;
-		fc_frame_init(fp);
-		fr_dev(fp) = lp;
-		fr_sof(fp) = hp->fcoe_sof;
-
-		/* Copy out the CRC and EOF trailer for access */
-		if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
-			kfree_skb(skb);
-			continue;
-		}
-		fr_eof(fp) = crc_eof.fcoe_eof;
-		fr_crc(fp) = crc_eof.fcoe_crc32;
-		if (pskb_trim(skb, fr_len)) {
-			kfree_skb(skb);
-			continue;
-		}
-
-		/*
-		 * We only check CRC if no offload is available and if it is
-		 * it's solicited data, in which case, the FCP layer would
-		 * check it during the copy.
-		 */
-		if (lp->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
-			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
-		else
-			fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
-
-		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);
-			continue;
-		}
-		if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
-			if (le32_to_cpu(fr_crc(fp)) !=
-			    ~crc32(~0, skb->data, fr_len)) {
-				if (debug_fcoe || stats->InvalidCRCCount < 5)
-					printk(KERN_WARNING "fcoe: dropping "
-					       "frame with CRC error\n");
-				stats->InvalidCRCCount++;
-				stats->ErrorFrames++;
-				fc_frame_free(fp);
-				continue;
-			}
-			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
-		}
-		/* non flogi and non data exchanges are handled here */
-		if (unlikely(fc->flogi_progress))
-			fcoe_recv_flogi(fc, fp, mac);
-		fc_exch_recv(lp, lp->emp, fp);
-	}
-	return 0;
-}
-
-/**
- * fcoe_recv_flogi() - flogi receive function
- * @fc: associated fcoe_softc
- * @fp: the recieved frame
- * @sa: the source address of this flogi
- *
- * This is responsible to parse the flogi response and sets the corresponding
- * mac address for the initiator, eitehr OUI based or GW based.
- *
- * Returns: none
- */
-static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa)
-{
-	struct fc_frame_header *fh;
-	u8 op;
-
-	fh = fc_frame_header_get(fp);
-	if (fh->fh_type != FC_TYPE_ELS)
-		return;
-	op = fc_frame_payload_op(fp);
-	if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
-	    fc->flogi_oxid == ntohs(fh->fh_ox_id)) {
-		/*
-		 * FLOGI accepted.
-		 * If the src mac addr is FC_OUI-based, then we mark the
-		 * address_mode flag to use FC_OUI-based Ethernet DA.
-		 * Otherwise we use the FCoE gateway addr
-		 */
-		if (!compare_ether_addr(sa, (u8[6]) FC_FCOE_FLOGI_MAC)) {
-			fc->address_mode = FCOE_FCOUI_ADDR_MODE;
-		} else {
-			memcpy(fc->dest_addr, sa, ETH_ALEN);
-			fc->address_mode = FCOE_GW_ADDR_MODE;
-		}
-
-		/*
-		 * Remove any previously-set unicast MAC filter.
-		 * Add secondary FCoE MAC address filter for our OUI.
-		 */
-		rtnl_lock();
-		if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
-			dev_unicast_delete(fc->real_dev, fc->data_src_addr,
-					   ETH_ALEN);
-		fc_fcoe_set_mac(fc->data_src_addr, fh->fh_d_id);
-		dev_unicast_add(fc->real_dev, fc->data_src_addr, ETH_ALEN);
-		rtnl_unlock();
-
-		fc->flogi_progress = 0;
-	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
-		/*
-		 * Save source MAC for point-to-point responses.
-		 */
-		memcpy(fc->dest_addr, sa, ETH_ALEN);
-		fc->address_mode = FCOE_GW_ADDR_MODE;
-	}
-}
-
-/**
- * fcoe_watchdog() - fcoe timer callback
- * @vp:
- *
- * This checks the pending queue length for fcoe and set lport qfull
- * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the
- * fcoe_hostlist.
- *
- * Returns: 0 for success
- */
-void fcoe_watchdog(ulong vp)
-{
-	struct fcoe_softc *fc;
-
-	read_lock(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		if (fc->lp)
-			fcoe_check_wait_queue(fc->lp);
-	}
-	read_unlock(&fcoe_hostlist_lock);
-
-	fcoe_timer.expires = jiffies + (1 * HZ);
-	add_timer(&fcoe_timer);
-}
-
-
-/**
- * fcoe_check_wait_queue() - put the skb into fcoe pending xmit queue
- * @lp: the fc_port for this skb
- * @skb: the associated skb to be xmitted
- *
- * This empties the wait_queue, dequeue the head of the wait_queue queue
- * and calls fcoe_start_io() for each packet, if all skb have been
- * transmitted, return qlen or -1 if a error occurs, then restore
- * wait_queue and  try again later.
- *
- * The wait_queue is used when the skb transmit fails. skb will go
- * in the wait_queue which will be emptied by the time function OR
- * by the next skb transmit.
- *
- * Returns: 0 for success
- */
-static int fcoe_check_wait_queue(struct fc_lport *lp)
-{
-	struct fcoe_softc *fc = lport_priv(lp);
-	struct sk_buff *skb;
-	int rc = -1;
-
-	spin_lock_bh(&fc->fcoe_pending_queue.lock);
-	if (fc->fcoe_pending_queue_active)
-		goto out;
-	fc->fcoe_pending_queue_active = 1;
-
-	while (fc->fcoe_pending_queue.qlen) {
-		/* keep qlen > 0 until fcoe_start_io succeeds */
-		fc->fcoe_pending_queue.qlen++;
-		skb = __skb_dequeue(&fc->fcoe_pending_queue);
-
-		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-		rc = fcoe_start_io(skb);
-		spin_lock_bh(&fc->fcoe_pending_queue.lock);
-
-		if (rc) {
-			__skb_queue_head(&fc->fcoe_pending_queue, skb);
-			/* undo temporary increment above */
-			fc->fcoe_pending_queue.qlen--;
-			break;
-		}
-		/* undo temporary increment above */
-		fc->fcoe_pending_queue.qlen--;
-	}
-
-	if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
-		lp->qfull = 0;
-	fc->fcoe_pending_queue_active = 0;
-	rc = fc->fcoe_pending_queue.qlen;
-out:
-	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-	return rc;
-}
-
-/**
- * fcoe_dev_setup() - setup link change notification interface
- */
-static void fcoe_dev_setup()
-{
-	/*
-	 * here setup a interface specific wd time to
-	 * monitor the link state
-	 */
-	register_netdevice_notifier(&fcoe_notifier);
-}
-
-/**
- * fcoe_dev_setup() - cleanup link change notification interface
- */
-static void fcoe_dev_cleanup(void)
-{
-	unregister_netdevice_notifier(&fcoe_notifier);
-}
-
-/**
- * fcoe_device_notification() - netdev event notification callback
- * @notifier: context of the notification
- * @event: type of event
- * @ptr: fixed array for output parsed ifname
- *
- * This function is called by the ethernet driver in case of link change event
- *
- * Returns: 0 for success
- */
-static int fcoe_device_notification(struct notifier_block *notifier,
-				    ulong event, void *ptr)
-{
-	struct fc_lport *lp = NULL;
-	struct net_device *real_dev = ptr;
-	struct fcoe_softc *fc;
-	struct fcoe_dev_stats *stats;
-	u32 new_link_up;
-	u32 mfs;
-	int rc = NOTIFY_OK;
-
-	read_lock(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		if (fc->real_dev == real_dev) {
-			lp = fc->lp;
-			break;
-		}
-	}
-	read_unlock(&fcoe_hostlist_lock);
-	if (lp == NULL) {
-		rc = NOTIFY_DONE;
-		goto out;
-	}
-
-	new_link_up = lp->link_up;
-	switch (event) {
-	case NETDEV_DOWN:
-	case NETDEV_GOING_DOWN:
-		new_link_up = 0;
-		break;
-	case NETDEV_UP:
-	case NETDEV_CHANGE:
-		new_link_up = !fcoe_link_ok(lp);
-		break;
-	case NETDEV_CHANGEMTU:
-		mfs = fc->real_dev->mtu -
-			(sizeof(struct fcoe_hdr) +
-			 sizeof(struct fcoe_crc_eof));
-		if (mfs >= FC_MIN_MAX_FRAME)
-			fc_set_mfs(lp, mfs);
-		new_link_up = !fcoe_link_ok(lp);
-		break;
-	case NETDEV_REGISTER:
-		break;
-	default:
-		FC_DBG("unknown event %ld call", event);
-	}
-	if (lp->link_up != new_link_up) {
-		if (new_link_up)
-			fc_linkup(lp);
-		else {
-			stats = fc_lport_get_stats(lp);
-			stats->LinkFailureCount++;
-			fc_linkdown(lp);
-			fcoe_clean_pending_queue(lp);
-		}
-	}
-out:
-	return rc;
-}
-
-/**
- * fcoe_if_to_netdev() - parse a name buffer to get netdev
- * @ifname: fixed array for output parsed ifname
- * @buffer: incoming buffer to be copied
- *
- * Returns: NULL or ptr to netdeive
- */
-static struct net_device *fcoe_if_to_netdev(const char *buffer)
-{
-	char *cp;
-	char ifname[IFNAMSIZ + 2];
-
-	if (buffer) {
-		strlcpy(ifname, buffer, IFNAMSIZ);
-		cp = ifname + strlen(ifname);
-		while (--cp >= ifname && *cp == '\n')
-			*cp = '\0';
-		return dev_get_by_name(&init_net, ifname);
-	}
-	return NULL;
-}
-
-/**
- * fcoe_netdev_to_module_owner() - finds out the nic drive moddule of the netdev
- * @netdev: the target netdev
- *
- * Returns: ptr to the struct module, NULL for failure
- */
-static struct module *
-fcoe_netdev_to_module_owner(const struct net_device *netdev)
-{
-	struct device *dev;
-
-	if (!netdev)
-		return NULL;
-
-	dev = netdev->dev.parent;
-	if (!dev)
-		return NULL;
-
-	if (!dev->driver)
-		return NULL;
-
-	return dev->driver->owner;
-}
-
-/**
- * fcoe_ethdrv_get() - Hold the Ethernet driver
- * @netdev: the target netdev
- *
- * Holds the Ethernet driver module by try_module_get() for
- * the corresponding netdev.
- *
- * Returns: 0 for succsss
- */
-static int fcoe_ethdrv_get(const struct net_device *netdev)
-{
-	struct module *owner;
-
-	owner = fcoe_netdev_to_module_owner(netdev);
-	if (owner) {
-		printk(KERN_DEBUG "fcoe:hold driver module %s for %s\n",
-		       module_name(owner), netdev->name);
-		return  try_module_get(owner);
-	}
-	return -ENODEV;
-}
-
-/**
- * fcoe_ethdrv_put() - Release the Ethernet driver
- * @netdev: the target netdev
- *
- * Releases the Ethernet driver module by module_put for
- * the corresponding netdev.
- *
- * Returns: 0 for succsss
- */
-static int fcoe_ethdrv_put(const struct net_device *netdev)
-{
-	struct module *owner;
-
-	owner = fcoe_netdev_to_module_owner(netdev);
-	if (owner) {
-		printk(KERN_DEBUG "fcoe:release driver module %s for %s\n",
-		       module_name(owner), netdev->name);
-		module_put(owner);
-		return 0;
-	}
-	return -ENODEV;
-}
-
-/**
- * fcoe_destroy() - handles the destroy from sysfs
- * @buffer: expcted to be a eth if name
- * @kp: associated kernel param
- *
- * Returns: 0 for success
- */
-static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
-{
-	int rc;
-	struct net_device *netdev;
-
-	netdev = fcoe_if_to_netdev(buffer);
-	if (!netdev) {
-		rc = -ENODEV;
-		goto out_nodev;
-	}
-	/* look for existing lport */
-	if (!fcoe_hostlist_lookup(netdev)) {
-		rc = -ENODEV;
-		goto out_putdev;
-	}
-	rc = fcoe_if_destroy(netdev);
-	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_if_destroy(%s) failed\n",
-		       netdev->name);
-		rc = -EIO;
-		goto out_putdev;
-	}
-	fcoe_ethdrv_put(netdev);
-	rc = 0;
-out_putdev:
-	dev_put(netdev);
-out_nodev:
-	return rc;
-}
-
-/**
- * fcoe_create() - Handles the create call from sysfs
- * @buffer: expcted to be a eth if name
- * @kp: associated kernel param
- *
- * Returns: 0 for success
- */
-static int fcoe_create(const char *buffer, struct kernel_param *kp)
-{
-	int rc;
-	struct net_device *netdev;
-
-	netdev = fcoe_if_to_netdev(buffer);
-	if (!netdev) {
-		rc = -ENODEV;
-		goto out_nodev;
-	}
-	/* look for existing lport */
-	if (fcoe_hostlist_lookup(netdev)) {
-		rc = -EEXIST;
-		goto out_putdev;
-	}
-	fcoe_ethdrv_get(netdev);
-
-	rc = fcoe_if_create(netdev);
-	if (rc) {
-		printk(KERN_ERR "fcoe: fcoe_if_create(%s) failed\n",
-		       netdev->name);
-		fcoe_ethdrv_put(netdev);
-		rc = -EIO;
-		goto out_putdev;
-	}
-	rc = 0;
-out_putdev:
-	dev_put(netdev);
-out_nodev:
-	return rc;
-}
-
-module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(create, "string");
-MODULE_PARM_DESC(create, "Create fcoe port using net device passed in.");
-module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, "Destroy fcoe port");
-
-/**
- * fcoe_link_ok() - Check if link is ok for the fc_lport
- * @lp: ptr to the fc_lport
- *
- * Any permanently-disqualifying conditions have been previously checked.
- * This also updates the speed setting, which may change with link for 100/1000.
- *
- * This function should probably be checking for PAUSE support at some point
- * in the future. Currently Per-priority-pause is not determinable using
- * ethtool, so we shouldn't be restrictive until that problem is resolved.
- *
- * Returns: 0 if link is OK for use by FCoE.
- *
- */
-int fcoe_link_ok(struct fc_lport *lp)
-{
-	struct fcoe_softc *fc = lport_priv(lp);
-	struct net_device *dev = fc->real_dev;
-	struct ethtool_cmd ecmd = { ETHTOOL_GSET };
-	int rc = 0;
-
-	if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) {
-		dev = fc->phys_dev;
-		if (dev->ethtool_ops->get_settings) {
-			dev->ethtool_ops->get_settings(dev, &ecmd);
-			lp->link_supported_speeds &=
-				~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
-			if (ecmd.supported & (SUPPORTED_1000baseT_Half |
-					      SUPPORTED_1000baseT_Full))
-				lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
-			if (ecmd.supported & SUPPORTED_10000baseT_Full)
-				lp->link_supported_speeds |=
-					FC_PORTSPEED_10GBIT;
-			if (ecmd.speed == SPEED_1000)
-				lp->link_speed = FC_PORTSPEED_1GBIT;
-			if (ecmd.speed == SPEED_10000)
-				lp->link_speed = FC_PORTSPEED_10GBIT;
-		}
-	} else
-		rc = -1;
-
-	return rc;
-}
-EXPORT_SYMBOL_GPL(fcoe_link_ok);
-
-/**
- * fcoe_percpu_clean() - Clear the pending skbs for an lport
- * @lp: the fc_lport
- */
-void fcoe_percpu_clean(struct fc_lport *lp)
-{
-	struct fcoe_percpu_s *pp;
-	struct fcoe_rcv_info *fr;
-	struct sk_buff_head *list;
-	struct sk_buff *skb, *next;
-	struct sk_buff *head;
-	unsigned int cpu;
-
-	for_each_possible_cpu(cpu) {
-		pp = &per_cpu(fcoe_percpu, cpu);
-		spin_lock_bh(&pp->fcoe_rx_list.lock);
-		list = &pp->fcoe_rx_list;
-		head = list->next;
-		for (skb = head; skb != (struct sk_buff *)list;
-		     skb = next) {
-			next = skb->next;
-			fr = fcoe_dev_from_skb(skb);
-			if (fr->fr_dev == lp) {
-				__skb_unlink(skb, list);
-				kfree_skb(skb);
-			}
-		}
-		spin_unlock_bh(&pp->fcoe_rx_list.lock);
-	}
-}
-EXPORT_SYMBOL_GPL(fcoe_percpu_clean);
-
-/**
- * fcoe_clean_pending_queue() - Dequeue a skb and free it
- * @lp: the corresponding fc_lport
- *
- * Returns: none
- */
-void fcoe_clean_pending_queue(struct fc_lport *lp)
-{
-	struct fcoe_softc  *fc = lport_priv(lp);
-	struct sk_buff *skb;
-
-	spin_lock_bh(&fc->fcoe_pending_queue.lock);
-	while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
-		spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-		kfree_skb(skb);
-		spin_lock_bh(&fc->fcoe_pending_queue.lock);
-	}
-	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
-}
-EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
-
-/**
- * libfc_host_alloc() - Allocate a Scsi_Host with room for the fc_lport
- * @sht: ptr to the scsi host templ
- * @priv_size: size of private data after fc_lport
- *
- * Returns: ptr to Scsi_Host
- * TODO: to libfc?
- */
-static inline struct Scsi_Host *
-libfc_host_alloc(struct scsi_host_template *sht, int priv_size)
-{
-	return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
-}
-
-/**
- * fcoe_host_alloc() - Allocate a Scsi_Host with room for the fcoe_softc
- * @sht: ptr to the scsi host templ
- * @priv_size: size of private data after fc_lport
- *
- * Returns: ptr to Scsi_Host
- */
-struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *sht, int priv_size)
-{
-	return libfc_host_alloc(sht, sizeof(struct fcoe_softc) + priv_size);
-}
-EXPORT_SYMBOL_GPL(fcoe_host_alloc);
-
-/**
- * fcoe_reset() - Resets the fcoe
- * @shost: shost the reset is from
- *
- * Returns: always 0
- */
-int fcoe_reset(struct Scsi_Host *shost)
-{
-	struct fc_lport *lport = shost_priv(shost);
-	fc_lport_reset(lport);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_reset);
-
-/**
- * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN.
- * @mac: mac address
- * @scheme: check port
- * @port: port indicator for converting
- *
- * Returns: u64 fc world wide name
- */
-u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
-		      unsigned int scheme, unsigned int port)
-{
-	u64 wwn;
-	u64 host_mac;
-
-	/* The MAC is in NO, so flip only the low 48 bits */
-	host_mac = ((u64) mac[0] << 40) |
-		((u64) mac[1] << 32) |
-		((u64) mac[2] << 24) |
-		((u64) mac[3] << 16) |
-		((u64) mac[4] << 8) |
-		(u64) mac[5];
-
-	WARN_ON(host_mac >= (1ULL << 48));
-	wwn = host_mac | ((u64) scheme << 60);
-	switch (scheme) {
-	case 1:
-		WARN_ON(port != 0);
-		break;
-	case 2:
-		WARN_ON(port >= 0xfff);
-		wwn |= (u64) port << 48;
-		break;
-	default:
-		WARN_ON(1);
-		break;
-	}
-
-	return wwn;
-}
-EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
-
-/**
- * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device
- * @device: this is currently ptr to net_device
- *
- * Returns: NULL or the located fcoe_softc
- */
-static struct fcoe_softc *
-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);
-			return fc;
-		}
-	}
-	read_unlock(&fcoe_hostlist_lock);
-	return NULL;
-}
-
-/**
- * fcoe_hostlist_lookup() - Find the corresponding lport by netdev
- * @netdev: ptr to net_device
- *
- * Returns: 0 for success
- */
-struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
-{
-	struct fcoe_softc *fc;
-
-	fc = fcoe_hostlist_lookup_softc(netdev);
-
-	return (fc) ? fc->lp : NULL;
-}
-EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup);
-
-/**
- * fcoe_hostlist_add() - Add a lport to lports list
- * @lp: ptr to the fc_lport to badded
- *
- * Returns: 0 for success
- */
-int fcoe_hostlist_add(const struct fc_lport *lp)
-{
-	struct fcoe_softc *fc;
-
-	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;
-}
-EXPORT_SYMBOL_GPL(fcoe_hostlist_add);
-
-/**
- * fcoe_hostlist_remove() - remove a lport from lports list
- * @lp: ptr to the fc_lport to badded
- *
- * Returns: 0 for success
- */
-int fcoe_hostlist_remove(const struct fc_lport *lp)
-{
-	struct fcoe_softc *fc;
-
-	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);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_hostlist_remove);
-
-/**
- * fcoe_libfc_config() - sets up libfc related properties for lport
- * @lp: ptr to the fc_lport
- * @tt: libfc function template
- *
- * Returns : 0 for success
- */
-int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
-{
-	/* Set the function pointers set by the LLDD */
-	memcpy(&lp->tt, tt, sizeof(*tt));
-	if (fc_fcp_init(lp))
-		return -ENOMEM;
-	fc_exch_init(lp);
-	fc_elsct_init(lp);
-	fc_lport_init(lp);
-	fc_rport_init(lp);
-	fc_disc_init(lp);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_libfc_config);
-
-/**
- * fcoe_init() - fcoe module loading initialization
- *
- * Returns 0 on success, negative on failure
- */
-static int __init fcoe_init(void)
-{
-	unsigned int cpu;
-	int rc = 0;
-	struct fcoe_percpu_s *p;
-
-	INIT_LIST_HEAD(&fcoe_hostlist);
-	rwlock_init(&fcoe_hostlist_lock);
-
-	for_each_possible_cpu(cpu) {
-		p = &per_cpu(fcoe_percpu, cpu);
-		skb_queue_head_init(&p->fcoe_rx_list);
-	}
-
-	for_each_online_cpu(cpu)
-		fcoe_percpu_thread_create(cpu);
-
-	/* Initialize per CPU interrupt thread */
-	rc = register_hotcpu_notifier(&fcoe_cpu_notifier);
-	if (rc)
-		goto out_free;
-
-	/* Setup link change notification */
-	fcoe_dev_setup();
-
-	setup_timer(&fcoe_timer, fcoe_watchdog, 0);
-
-	mod_timer(&fcoe_timer, jiffies + (10 * HZ));
-
-	fcoe_if_init();
-
-	return 0;
-
-out_free:
-	for_each_online_cpu(cpu) {
-		fcoe_percpu_thread_destroy(cpu);
-	}
-
-	return rc;
-}
-module_init(fcoe_init);
-
-/**
- * fcoe_exit() - fcoe module unloading cleanup
- *
- * Returns 0 on success, negative on failure
- */
-static void __exit fcoe_exit(void)
-{
-	unsigned int cpu;
-	struct fcoe_softc *fc, *tmp;
-
-	fcoe_dev_cleanup();
-
-	/* Stop the timer */
-	del_timer_sync(&fcoe_timer);
-
-	/* releases the associated fcoe hosts */
-	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
-		fcoe_if_destroy(fc->real_dev);
-
-	unregister_hotcpu_notifier(&fcoe_cpu_notifier);
-
-	for_each_online_cpu(cpu) {
-		fcoe_percpu_thread_destroy(cpu);
-	}
-
-	/* detach from scsi transport */
-	fcoe_if_exit();
-}
-module_exit(fcoe_exit);


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* RE: [PATCH] fcoe: prep work to completely remove fc_transport_fcoe code
  2009-03-27 16:03   ` [PATCH] fcoe: prep work to completely remove fc_transport_fcoe code Robert Love
@ 2009-03-27 16:12     ` Love, Robert W
  0 siblings, 0 replies; 30+ messages in thread
From: Love, Robert W @ 2009-03-27 16:12 UTC (permalink / raw)
  To: Love, Robert W, James.Bottomley, linux-scsi

Robert Love wrote:
> From: Vasu Dev <vasu.dev@intel.com>
> 
> The fcoe transport code was added for generic FCoE transport
> infrastructure to allow additional offload related module loading
> on demand, this is not required anymore after recently added
> different offload approach by having offload related func ops
> in netdev.
> 
> This patch removes fcoe transport related code use, calls functions
> directly between existing libfcoe.c and fcoe_sw.c for now, for
> example fcoe_sw_destroy and fcoe_sw_create calling.
> 
> The fcoe_sw.c and libfcoe.c code will be further consolidated in
> later patches and then also the default fcoe sw transport code
> file fcoe_sw.c will be completely removed.
> 
> The fcoe transport code files are completely removed in next
> patch to keep this patch simple for reviewing.
> 
> --
> 
> This patch is an update to a previous patch. This update
> resolves a build error as well as fixes a defect related to
> not calling fc_release_transport().
> 
> The following updates to the other patches in the series
> are just adjustments to the changes this update introduces.
> 
> Signed-off-by: Vasu Dev <vasu.dev@intel.com>
> Signed-off-by: Robert Love <robert.w.love@intel.com>
> ---
> 
Nothing has changed since these 4 updated pathces made it to
linux-scsi two days ago. I just misspelled James' e-mail
address in when I originally sent them.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH] PM port setting and attached SATA port selector in discover
  2009-03-26  3:03     ` [PATCH] PM port setting and attached SATA port selector in discover Andy Yan
@ 2009-03-27 16:52       ` James Bottomley
  0 siblings, 0 replies; 30+ messages in thread
From: James Bottomley @ 2009-03-27 16:52 UTC (permalink / raw)
  To: Andy Yan; +Cc: linux-scsi, Michael Wang, Jacky Feng, Ying Chu

On Wed, 2009-03-25 at 20:03 -0700, Andy Yan wrote:
> >From 9937ccded1e182b50f1128e80d70768b16d2b5e7 Mon Sep 17 00:00:00 2001
> From: Andy Yan <ayan@marvell.com>
> Date: Thu, 26 Mar 2009 18:32:31 +0800
> Subject: [PATCH] PM port setting and attached SATA port selector in
> discover
> 
> Default PM port should be set 0 other than 1.For most SATA disk, it is
> ignored,
> But I got that some expander reports attached SATA port selector 1 and
> won't clear it. Host should pass this check and send command with PMport
> 0.
> 
> ---
>  drivers/scsi/libsas/sas_ata.c      |    2 +-
>  drivers/scsi/libsas/sas_expander.c |    2 +-
>  2 files changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/scsi/libsas/sas_ata.c
> b/drivers/scsi/libsas/sas_ata.c
> index e155011..bea3deb 100644
> --- a/drivers/scsi/libsas/sas_ata.c
> +++ b/drivers/scsi/libsas/sas_ata.c
> @@ -174,7 +174,7 @@ static unsigned int sas_ata_qc_issue(struct
> ata_queued_cmd *qc)
>  		qc->tf.nsect = 0;
>  	}
>  
> -	ata_tf_to_fis(&qc->tf, 1, 0, (u8*)&task->ata_task.fis);
> +	ata_tf_to_fis(&qc->tf, 0, 0, (u8*)&task->ata_task.fis);
>  	task->uldd_task = qc;
>  	if (ata_is_atapi(qc->tf.protocol)) {
>  		memcpy(task->ata_task.atapi_packet, qc->cdb,
> qc->dev->cdb_len);
> diff --git a/drivers/scsi/libsas/sas_expander.c
> b/drivers/scsi/libsas/sas_expander.c
> index 3da02e4..75ca00b 100644
> --- a/drivers/scsi/libsas/sas_expander.c
> +++ b/drivers/scsi/libsas/sas_expander.c
> @@ -642,7 +642,7 @@ static struct domain_device
> *sas_ex_discover_end_dev(
>  	struct sas_rphy *rphy;
>  	int res;
>  
> -	if (phy->attached_sata_host || phy->attached_sata_ps)
> +	if (phy->attached_sata_host)

In general this looks OK.  What it's going to cause to happen is that we
now try to discover SATA port selectors, which aren't supported in
libsas ... can you check that nothing nasty happens when libsas finds
one?

James



^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH 3/14 v2] fcoe: Use per-CPU kernel function for dev_stats instead of an array
  2009-03-17 18:41 ` [PATCH 03/14] fcoe: Use per-CPU kernel function for dev_stats instead of an array Robert Love
@ 2009-03-31 22:51   ` Robert Love
  0 siblings, 0 replies; 30+ messages in thread
From: Robert Love @ 2009-03-31 22:51 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

Remove the hotplug creation of dev_stats, we allocate for all possible CPUs
now when we allocate the lport.

v2: Durring the 2.6.30 merge window, before these patches were comitted,
'percpu_ptr' was renamed 'per_cpu_ptr'. This latest update updates this
patch for the name change.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---

 drivers/scsi/fcoe/fcoe_sw.c   |   13 +---
 drivers/scsi/fcoe/libfcoe.c   |  143 +++++------------------------------------
 drivers/scsi/libfc/fc_fcp.c   |    8 +-
 drivers/scsi/libfc/fc_lport.c |   11 ++-
 include/scsi/libfc.h          |   23 ++++++-
 5 files changed, 53 insertions(+), 145 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c
index 2bbbe3c..a675390 100644
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -113,8 +113,6 @@ static struct scsi_host_template fcoe_sw_shost_template = {
  */
 static int fcoe_sw_lport_config(struct fc_lport *lp)
 {
-	int i = 0;
-
 	lp->link_up = 0;
 	lp->qfull = 0;
 	lp->max_retry_count = 3;
@@ -123,12 +121,7 @@ static int fcoe_sw_lport_config(struct fc_lport *lp)
 	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
 			      FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
 
-	/*
-	 * allocate per cpu stats block
-	 */
-	for_each_online_cpu(i)
-		lp->dev_stats[i] = kzalloc(sizeof(struct fcoe_dev_stats),
-					   GFP_KERNEL);
+	fc_lport_init_stats(lp);
 
 	/* lport fc_lport related configuration */
 	fc_lport_config(lp);
@@ -311,7 +304,6 @@ static inline int fcoe_sw_em_config(struct fc_lport *lp)
  */
 static int fcoe_sw_destroy(struct net_device *netdev)
 {
-	int cpu;
 	struct fc_lport *lp = NULL;
 	struct fcoe_softc *fc;
 	u8 flogi_maddr[ETH_ALEN];
@@ -363,8 +355,7 @@ static int fcoe_sw_destroy(struct net_device *netdev)
 	fcoe_clean_pending_queue(lp);
 
 	/* Free memory used by statistical counters */
-	for_each_online_cpu(cpu)
-		kfree(lp->dev_stats[cpu]);
+	fc_lport_free_stats(lp);
 
 	/* Release the net_device and Scsi_Host */
 	dev_put(fc->real_dev);
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index d22275b..648a2fc 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -71,9 +71,6 @@ DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
 /* Function Prototyes */
 static int fcoe_check_wait_queue(struct fc_lport *);
 static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
-#ifdef CONFIG_HOTPLUG_CPU
-static int fcoe_cpu_callback(struct notifier_block *, ulong, void *);
-#endif /* CONFIG_HOTPLUG_CPU */
 static int fcoe_device_notification(struct notifier_block *, ulong, void *);
 static void fcoe_dev_setup(void);
 static void fcoe_dev_cleanup(void);
@@ -83,87 +80,6 @@ static struct notifier_block fcoe_notifier = {
 	.notifier_call = fcoe_device_notification,
 };
 
-
-#ifdef CONFIG_HOTPLUG_CPU
-static struct notifier_block fcoe_cpu_notifier = {
-	.notifier_call = fcoe_cpu_callback,
-};
-
-/**
- * fcoe_create_percpu_data() - creates the associated cpu data
- * @cpu: index for the cpu where fcoe cpu data will be created
- *
- * create percpu stats block, from cpu add notifier
- *
- * Returns: none
- */
-static void fcoe_create_percpu_data(unsigned int cpu)
-{
-	struct fc_lport *lp;
-	struct fcoe_softc *fc;
-
-	write_lock_bh(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		lp = fc->lp;
-		if (lp->dev_stats[cpu] == NULL)
-			lp->dev_stats[cpu] =
-				kzalloc(sizeof(struct fcoe_dev_stats),
-					GFP_KERNEL);
-	}
-	write_unlock_bh(&fcoe_hostlist_lock);
-}
-
-/**
- * fcoe_destroy_percpu_data() - destroys the associated cpu data
- * @cpu: index for the cpu where fcoe cpu data will destroyed
- *
- * destroy percpu stats block called by cpu add/remove notifier
- *
- * Retuns: none
- */
-static void fcoe_destroy_percpu_data(unsigned int cpu)
-{
-	struct fc_lport *lp;
-	struct fcoe_softc *fc;
-
-	write_lock_bh(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		lp = fc->lp;
-		kfree(lp->dev_stats[cpu]);
-		lp->dev_stats[cpu] = NULL;
-	}
-	write_unlock_bh(&fcoe_hostlist_lock);
-}
-
-/**
- * fcoe_cpu_callback() - fcoe cpu hotplug event callback
- * @nfb: callback data block
- * @action: event triggering the callback
- * @hcpu: index for the cpu of this event
- *
- * this creates or destroys per cpu data for fcoe
- *
- * Returns NOTIFY_OK always.
- */
-static int fcoe_cpu_callback(struct notifier_block *nfb, unsigned long action,
-			     void *hcpu)
-{
-	unsigned int cpu = (unsigned long)hcpu;
-
-	switch (action) {
-	case CPU_ONLINE:
-		fcoe_create_percpu_data(cpu);
-		break;
-	case CPU_DEAD:
-		fcoe_destroy_percpu_data(cpu);
-		break;
-	default:
-		break;
-	}
-	return NOTIFY_OK;
-}
-#endif /* CONFIG_HOTPLUG_CPU */
-
 /**
  * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ
  * @skb: the receive skb
@@ -181,7 +97,6 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 	struct fc_lport *lp;
 	struct fcoe_rcv_info *fr;
 	struct fcoe_softc *fc;
-	struct fcoe_dev_stats *stats;
 	struct fc_frame_header *fh;
 	struct fcoe_percpu_s *fps;
 	unsigned short oxid;
@@ -252,13 +167,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 
 	return 0;
 err:
-#ifdef CONFIG_SMP
-	stats = lp->dev_stats[smp_processor_id()];
-#else
-	stats = lp->dev_stats[0];
-#endif
-	if (stats)
-		stats->ErrorFrames++;
+	fc_lport_get_stats(lp)->ErrorFrames++;
 
 err2:
 	kfree_skb(skb);
@@ -495,11 +404,9 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 	}
 #endif
 	/* update tx stats: regardless if LLD fails */
-	stats = lp->dev_stats[smp_processor_id()];
-	if (stats) {
-		stats->TxFrames++;
-		stats->TxWords += wlen;
-	}
+	stats = fc_lport_get_stats(lp);
+	stats->TxFrames++;
+	stats->TxWords += wlen;
 
 	/* send down to lld */
 	fr_dev(fp) = lp;
@@ -565,8 +472,6 @@ int fcoe_percpu_receive_thread(void *arg)
 			continue;
 		}
 
-		stats = lp->dev_stats[smp_processor_id()];
-
 		if (unlikely(debug_fcoe)) {
 			FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p "
 			       "tail:%p end:%p sum:%d dev:%s",
@@ -593,13 +498,16 @@ int fcoe_percpu_receive_thread(void *arg)
 		hp = (struct fcoe_hdr *) skb_network_header(skb);
 		fh = (struct fc_frame_header *) skb_transport_header(skb);
 
+		stats = fc_lport_get_stats(lp);
 		if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
-			if (stats) {
-				if (stats->ErrorFrames < 5)
-					FC_DBG("unknown FCoE version %x",
-					       FC_FCOE_DECAPS_VER(hp));
-				stats->ErrorFrames++;
-			}
+			if (stats->ErrorFrames < 5)
+				printk(KERN_WARNING "FCoE version "
+				       "mismatch: The frame has "
+				       "version %x, but the "
+				       "initiator supports version "
+				       "%x\n", FC_FCOE_DECAPS_VER(hp),
+				       FC_FCOE_VER);
+			stats->ErrorFrames++;
 			kfree_skb(skb);
 			continue;
 		}
@@ -607,10 +515,8 @@ int fcoe_percpu_receive_thread(void *arg)
 		skb_pull(skb, sizeof(struct fcoe_hdr));
 		fr_len = skb->len - sizeof(struct fcoe_crc_eof);
 
-		if (stats) {
-			stats->RxFrames++;
-			stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
-		}
+		stats->RxFrames++;
+		stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
 
 		fp = (struct fc_frame *)skb;
 		fc_frame_init(fp);
@@ -885,9 +791,8 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 		if (new_link_up)
 			fc_linkup(lp);
 		else {
-			stats = lp->dev_stats[smp_processor_id()];
-			if (stats)
-				stats->LinkFailureCount++;
+			stats = fc_lport_get_stats(lp);
+			stats->LinkFailureCount++;
 			fc_linkdown(lp);
 			fcoe_clean_pending_queue(lp);
 		}
@@ -1371,10 +1276,6 @@ static int __init fcoe_init(void)
 	INIT_LIST_HEAD(&fcoe_hostlist);
 	rwlock_init(&fcoe_hostlist_lock);
 
-#ifdef CONFIG_HOTPLUG_CPU
-	register_cpu_notifier(&fcoe_cpu_notifier);
-#endif /* CONFIG_HOTPLUG_CPU */
-
 	for_each_possible_cpu(cpu) {
 		p = &per_cpu(fcoe_percpu, cpu);
 		skb_queue_head_init(&p->fcoe_rx_list);
@@ -1430,17 +1331,9 @@ static void __exit fcoe_exit(void)
 	struct fcoe_percpu_s *p;
 	struct sk_buff *skb;
 
-	/*
-	 * Stop all call back interfaces
-	 */
-#ifdef CONFIG_HOTPLUG_CPU
-	unregister_cpu_notifier(&fcoe_cpu_notifier);
-#endif /* CONFIG_HOTPLUG_CPU */
 	fcoe_dev_cleanup();
 
-	/*
-	 * stop timer
-	 */
+	/* Stop the timer */
 	del_timer_sync(&fcoe_timer);
 
 	/* releases the associated fcoe transport for each lport */
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index a5725f3..0997e8b 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -407,10 +407,12 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
 
 		if (~crc != le32_to_cpu(fr_crc(fp))) {
 crc_err:
-			stats = lp->dev_stats[smp_processor_id()];
+			stats = fc_lport_get_stats(lp);
 			stats->ErrorFrames++;
+			/* FIXME - per cpu count, not total count! */
 			if (stats->InvalidCRCCount++ < 5)
-				FC_DBG("CRC error on data frame\n");
+				printk(KERN_WARNING "CRC error on data frame for port (%6x)\n",
+				       fc_host_port_id(lp->host));
 			/*
 			 * Assume the frame is total garbage.
 			 * We may have copied it over the good part
@@ -1752,7 +1754,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
 	/*
 	 * setup the data direction
 	 */
-	stats = lp->dev_stats[smp_processor_id()];
+	stats = fc_lport_get_stats(lp);
 	if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
 		fsp->req_flags = FC_SRB_READ;
 		stats->InputRequests++;
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 7ef4450..b8178ef 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -267,10 +267,10 @@ EXPORT_SYMBOL(fc_get_host_speed);
 
 struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
 {
-	int i;
 	struct fc_host_statistics *fcoe_stats;
 	struct fc_lport *lp = shost_priv(shost);
 	struct timespec v0, v1;
+	unsigned int cpu;
 
 	fcoe_stats = &lp->host_stats;
 	memset(fcoe_stats, 0, sizeof(struct fc_host_statistics));
@@ -279,10 +279,11 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
 	jiffies_to_timespec(lp->boot_time, &v1);
 	fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec);
 
-	for_each_online_cpu(i) {
-		struct fcoe_dev_stats *stats = lp->dev_stats[i];
-		if (stats == NULL)
-			continue;
+	for_each_possible_cpu(cpu) {
+		struct fcoe_dev_stats *stats;
+
+		stats = per_cpu_ptr(lp->dev_stats, cpu);
+
 		fcoe_stats->tx_frames += stats->TxFrames;
 		fcoe_stats->tx_words += stats->TxWords;
 		fcoe_stats->rx_frames += stats->RxFrames;
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index a70eafa..4e1d394 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -22,6 +22,7 @@
 
 #include <linux/timer.h>
 #include <linux/if.h>
+#include <linux/percpu.h>
 
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_fc.h>
@@ -661,7 +662,8 @@ struct fc_lport {
 	unsigned long		boot_time;
 
 	struct fc_host_statistics host_stats;
-	struct fcoe_dev_stats	*dev_stats[NR_CPUS];
+	struct fcoe_dev_stats	*dev_stats;
+
 	u64			wwpn;
 	u64			wwnn;
 	u8			retry_count;
@@ -722,6 +724,25 @@ static inline void fc_lport_state_enter(struct fc_lport *lp,
 	lp->state = state;
 }
 
+static inline int fc_lport_init_stats(struct fc_lport *lp)
+{
+	/* allocate per cpu stats block */
+	lp->dev_stats = alloc_percpu(struct fcoe_dev_stats);
+	if (!lp->dev_stats)
+		return -ENOMEM;
+	return 0;
+}
+
+static inline void fc_lport_free_stats(struct fc_lport *lp)
+{
+	free_percpu(lp->dev_stats);
+}
+
+static inline struct fcoe_dev_stats *fc_lport_get_stats(struct fc_lport *lp)
+{
+	return per_cpu_ptr(lp->dev_stats, smp_processor_id());
+}
+
 
 /*
  * LOCAL PORT LAYER


^ permalink raw reply related	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2009-03-31 22:51 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-03-17 18:41 [PATCH 00/14] Open-FCoE fixes and features for 2.6.30 merge window Robert Love
2009-03-17 18:41 ` [PATCH 01/14] fcoe: Initialize all possilbe skb_queue(s) when module is loaded Robert Love
2009-03-17 18:41 ` [PATCH 02/14] fcoe: Use percpu kernel funcs for struct fcoe_percpu_s Robert Love
2009-03-17 18:41 ` [PATCH 03/14] fcoe: Use per-CPU kernel function for dev_stats instead of an array Robert Love
2009-03-31 22:51   ` [PATCH 3/14 v2] " Robert Love
2009-03-17 18:41 ` [PATCH 04/14] fcoe: create/destroy fcoe Rx threads on CPU hotplug events Robert Love
2009-03-23  0:59   ` James Bottomley
2009-03-23 19:42     ` Robert Love
2009-03-24  3:50       ` James Bottomley
2009-03-17 18:41 ` [PATCH 05/14] fcoe: prep work to completely remove fc_transport_fcoe code Robert Love
2009-03-24 23:19   ` [PATCH] " Robert Love
2009-03-26  3:03     ` [PATCH] PM port setting and attached SATA port selector in discover Andy Yan
2009-03-27 16:52       ` James Bottomley
2009-03-27 16:03   ` [PATCH] fcoe: prep work to completely remove fc_transport_fcoe code Robert Love
2009-03-27 16:12     ` Love, Robert W
2009-03-17 18:41 ` [PATCH 06/14] fcoe: removes fc_transport_fcoe.[ch] code files Robert Love
2009-03-24 23:24   ` [PATCH] " Robert Love
2009-03-27 16:05   ` Robert Love
2009-03-17 18:42 ` [PATCH 07/14] fcoe: removes default sw transport code file fcoe_sw.c Robert Love
2009-03-24 23:27   ` [PATCH] " Robert Love
2009-03-27 16:06   ` Robert Love
2009-03-17 18:42 ` [PATCH 08/14] fcoe: renames libfcoe.c to fcoe.c as the only fcoe module file Robert Love
2009-03-24 23:27   ` [PATCH] " Robert Love
2009-03-27 16:07   ` Robert Love
2009-03-17 18:42 ` [PATCH 09/14] fcoe, libfc, scsi: adds libfcoe module Robert Love
2009-03-17 18:42 ` [PATCH 10/14] fcoe: moves common FCoE library API functions to " Robert Love
2009-03-17 18:42 ` [PATCH 11/14] fcoe: cleans up libfcoe.h and adds fcoe.h for fcoe module Robert Love
2009-03-17 18:42 ` [PATCH 12/14] fcoe, libfc: fix double fcoe_softc memory alloc Robert Love
2009-03-17 18:42 ` [PATCH 13/14] fcoe: Add a header file defining the FIP protocol for FCoE Robert Love
2009-03-17 18:42 ` [PATCH 14/14] fcoe, libfcoe: Add support for FIP. FCoE discovery and keep-alive Robert Love

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.