All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39)
@ 2011-01-29  0:03 Robert Love
  2011-01-29  0:03 ` [PATCH 01/28] libfc: always initialize the FCoE DDP exchange id for fsp as FC_XID_UNKNOWN Robert Love
                   ` (27 more replies)
  0 siblings, 28 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:03 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

This is a fairly significant update to libfc, libfcoe and fcoe.
The two major changes are the addition hooks for a FC/FCoE target
based on the new target infrastructure and the addition of a fcoe
transport which will allow the upcoming bnx2fc driver to work
with libfc and libfcoe.

1) fc4 provider hooks

   These changes have been around for months and allow libfc to
respond (as a target) to commands sent from an initiator. There
are also a few small changes here and there to libfc to make a
target mode work. This changeset paves the way for TCM_FC which
will bridge the TCM core with libfc/fcoe.

2) fcoe transport

   libfcoe.c has now been split out into two new files:
fcoe_transport.c and fcoe_ctlr.c. fcoe_transport.c implements
a registration system for fcoe transports. The first transport
is SW FCoE (i.e. fcoe.ko), the second will be bnx2fc (a brdcm
fcoe driver). Any fcoe transport will simply provide a match
function to determine if a netdevice is managed by the transport.
fcoe.ko is the default and will be used if no other transport
is available.

   One interesting outcome of this is that the create/destroy/etc...
entry points are moving from fcoe.ko to libfcoe.ko. There are
corresponding changes to fcoe-utils (fcoe user space suite), but
I don't intend to commit them until all of this code is accepted
to mainline. fcoe-utils is tagged against mainline.

3) Miscellaneous

   a) Removal of scsi_host lock usage in fc_queuecommand
   b) rport event callback to LLD from libfc (will be used by
      bnx2fc)
   c) Tejun Heo's workqueue change
   d) Memory leak fix and version bump to fnic

All of these patches have been tested against today's scsi-misc.

---

Bhanu Prakash Gollapudi (2):
      libfc: introduce LLD event callback
      libfcoe: Move common code from fcoe to libfcoe module

Dan Carpenter (1):
      libfc: dereferencing ERR_PTR in fc_tm_done()

Hillf Danton (2):
      libfc: Return a valid return code in fc_fcp_pkt_abort()
      libfc: Cleanup return paths in fc_rport_error_retry

Joe Eykholt (6):
      libfc: add hook for FC-4 provider registration
      libfc: add method for setting handler for incoming exchange
      libfc: add local port hook for provider session lookup
      libfc: add hook to notify providers of local port changes
      libfc: use PRLI hook to get parameters when sending outgoing PRLI
      libfc: export seq_release() for users of seq_assign()

Kiran Patil (2):
      libfc: Enhanced exchange ID selection mechanism and fix related EMA selection logic.
      libfc: Extending lport's roles for target if there is a registered target.

Randy Dunlap (1):
      libfc: fix sparse static and non-ANSI warnings

Robert Love (2):
      fcoe: Fix module reference count for vports
      libfc: Remove usage of the Scsi_Host's host_lock

Tejun Heo (1):
      fcoe: use dedicated workqueue instead of system_wq

Vasu Dev (1):
      fcoe: drop FCoE LOGO in FIP mode

Venkata Siva Vijayendra Bhamidipati (2):
      fnic: fix memory leak
      fnic: Bumping up fnic version from 1.4.0.145 to 1.5.0.1.

Yi Zou (8):
      libfc: always initialize the FCoE DDP exchange id for fsp as FC_XID_UNKNOWN
      libfcoe: move logging macros into the local libfcoe.h header file
      libfcoe: add fcoe_transport structure defines to include/scsi/libfcoe.h
      libfcoe: add implementation to support fcoe transport
      libfcoe: rename libfcoe.c to fcoe_cltr.c for the coming fcoe_transport.c
      libfcoe: include fcoe_transport.c into kernel libfcoe module
      fcoe: prepare fcoe for using fcoe transport
      fcoe: convert fcoe.ko to become an fcoe transport provider driver


 drivers/scsi/fcoe/Makefile         |    2 
 drivers/scsi/fcoe/fcoe.c           |  563 +++----
 drivers/scsi/fcoe/fcoe.h           |   44 -
 drivers/scsi/fcoe/fcoe_ctlr.c      | 2682 ++++++++++++++++++++++++++++++++++++
 drivers/scsi/fcoe/fcoe_transport.c |  726 ++++++++++
 drivers/scsi/fcoe/libfcoe.c        | 2708 ------------------------------------
 drivers/scsi/fcoe/libfcoe.h        |   31 
 drivers/scsi/fnic/fnic.h           |    2 
 drivers/scsi/fnic/vnic_dev.c       |    2 
 drivers/scsi/libfc/fc_exch.c       |  103 +
 drivers/scsi/libfc/fc_fcp.c        |   39 -
 drivers/scsi/libfc/fc_libfc.c      |  120 ++
 drivers/scsi/libfc/fc_libfc.h      |   14 
 drivers/scsi/libfc/fc_lport.c      |   68 +
 drivers/scsi/libfc/fc_npiv.c       |    1 
 drivers/scsi/libfc/fc_rport.c      |  190 ++-
 include/scsi/libfc.h               |   74 +
 include/scsi/libfcoe.h             |   99 +
 18 files changed, 4252 insertions(+), 3216 deletions(-)
 create mode 100644 drivers/scsi/fcoe/fcoe_ctlr.c
 create mode 100644 drivers/scsi/fcoe/fcoe_transport.c
 delete mode 100644 drivers/scsi/fcoe/libfcoe.c
 create mode 100644 drivers/scsi/fcoe/libfcoe.h

-- 
Thanks, //Rob

 LocalWords:  shortlog diffstat

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

* [PATCH 01/28] libfc: always initialize the FCoE DDP exchange id for fsp as FC_XID_UNKNOWN
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
@ 2011-01-29  0:03 ` Robert Love
  2011-01-29  0:03 ` [PATCH 02/28] libfc: Return a valid return code in fc_fcp_pkt_abort() Robert Love
                   ` (26 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:03 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Yi Zou

From: Yi Zou <yi.zou@intel.com>

The fsp's xfer_ddp is used as indication of the exchange id for the DDPed
I/O. We should always initialize it as FC_XID_UNKNOWN for a newly allocated
fsp, otherwise the fsp allocated in fc_fcp, i.e., not from queuecommand like
LUN RESET that is not doing DDP may still think DDP is setup for it since xid
0 is valid and goes on to call fc_fcp_ddp_done() in fc_fcp_resp() from
fc_tm_done(). So, set xfer_ddp as FC_XID_UNKNOWN in fc_fcp_pkt_alloc() now.

Also removes the setting of fsp->lp as it's already done when fsp is allocated.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_fcp.c |    4 +---
 1 files changed, 1 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 5962d1a..21a64d4 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -155,6 +155,7 @@ static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lport, gfp_t gfp)
 	if (fsp) {
 		memset(fsp, 0, sizeof(*fsp));
 		fsp->lp = lport;
+		fsp->xfer_ddp = FC_XID_UNKNOWN;
 		atomic_set(&fsp->ref_cnt, 1);
 		init_timer(&fsp->timer);
 		INIT_LIST_HEAD(&fsp->list);
@@ -1842,9 +1843,7 @@ static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scs
 	 * build the libfc request pkt
 	 */
 	fsp->cmd = sc_cmd;	/* save the cmd */
-	fsp->lp = lport;	/* save the softc ptr */
 	fsp->rport = rport;	/* set the remote port ptr */
-	fsp->xfer_ddp = FC_XID_UNKNOWN;
 	sc_cmd->scsi_done = done;
 
 	/*
@@ -2112,7 +2111,6 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
 	 * the sc passed in is not setup for execution like when sent
 	 * through the queuecommand callout.
 	 */
-	fsp->lp = lport;	/* save the softc ptr */
 	fsp->rport = rport;	/* set the remote port ptr */
 
 	/*


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

* [PATCH 02/28] libfc: Return a valid return code in fc_fcp_pkt_abort()
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
  2011-01-29  0:03 ` [PATCH 01/28] libfc: always initialize the FCoE DDP exchange id for fsp as FC_XID_UNKNOWN Robert Love
@ 2011-01-29  0:03 ` Robert Love
  2011-01-29  0:03 ` [PATCH 03/28] libfc: Cleanup return paths in fc_rport_error_retry Robert Love
                   ` (25 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:03 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Hillf Danton

From: Hillf Danton <dhillf@gmail.com>

Here ticks_left is added to record the result of
wait_for_completion_timeout().

[ Patch title and description edited by Robert Love
  to make it more descriptive  ]

Signed-off-by: Hillf Danton <dhillf@gmail.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_fcp.c |    7 ++++---
 1 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 21a64d4..ba639fa 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -1202,6 +1202,7 @@ unlock:
 static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)
 {
 	int rc = FAILED;
+	unsigned long ticks_left;
 
 	if (fc_fcp_send_abort(fsp))
 		return FAILED;
@@ -1210,13 +1211,13 @@ static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)
 	fsp->wait_for_comp = 1;
 
 	spin_unlock_bh(&fsp->scsi_pkt_lock);
-	rc = wait_for_completion_timeout(&fsp->tm_done, FC_SCSI_TM_TOV);
+	ticks_left = wait_for_completion_timeout(&fsp->tm_done,
+							FC_SCSI_TM_TOV);
 	spin_lock_bh(&fsp->scsi_pkt_lock);
 	fsp->wait_for_comp = 0;
 
-	if (!rc) {
+	if (!ticks_left) {
 		FC_FCP_DBG(fsp, "target abort cmd  failed\n");
-		rc = FAILED;
 	} else if (fsp->state & FC_SRB_ABORTED) {
 		FC_FCP_DBG(fsp, "target abort cmd  passed\n");
 		rc = SUCCESS;


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

* [PATCH 03/28] libfc: Cleanup return paths in fc_rport_error_retry
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
  2011-01-29  0:03 ` [PATCH 01/28] libfc: always initialize the FCoE DDP exchange id for fsp as FC_XID_UNKNOWN Robert Love
  2011-01-29  0:03 ` [PATCH 02/28] libfc: Return a valid return code in fc_fcp_pkt_abort() Robert Love
@ 2011-01-29  0:03 ` Robert Love
  2011-01-29  0:03 ` [PATCH 04/28] libfc: dereferencing ERR_PTR in fc_tm_done() Robert Love
                   ` (24 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:03 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Hillf Danton

From: Hillf Danton <dhillf@gmail.com>

This patch makes it so that we only have one call to
fc_rport_error. This patch does not completely
consolidate return statements, there is still one return
used when not calling fc_rport_error, but alternative
solutions made the code more confusing.

[ Patch modified by Robert Love ]
[ Patch title and commit message edited by Robert Love
  to make it more relevant ]

Signed-off-by: Hillf Danton <dhillf@gmail.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_rport.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index a7175ad..325bc42 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -575,7 +575,7 @@ static void fc_rport_error_retry(struct fc_rport_priv *rdata,
 
 	/* make sure this isn't an FC_EX_CLOSED error, never retry those */
 	if (PTR_ERR(fp) == -FC_EX_CLOSED)
-		return fc_rport_error(rdata, fp);
+		goto out;
 
 	if (rdata->retries < rdata->local_port->max_rport_retry_count) {
 		FC_RPORT_DBG(rdata, "Error %ld in state %s, retrying\n",
@@ -588,7 +588,8 @@ static void fc_rport_error_retry(struct fc_rport_priv *rdata,
 		return;
 	}
 
-	return fc_rport_error(rdata, fp);
+out:
+	fc_rport_error(rdata, fp);
 }
 
 /**


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

* [PATCH 04/28] libfc: dereferencing ERR_PTR in fc_tm_done()
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (2 preceding siblings ...)
  2011-01-29  0:03 ` [PATCH 03/28] libfc: Cleanup return paths in fc_rport_error_retry Robert Love
@ 2011-01-29  0:03 ` Robert Love
  2011-01-29  0:03 ` [PATCH 05/28] fnic: fix memory leak Robert Love
                   ` (23 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:03 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Dan Carpenter

From: Dan Carpenter <error27@gmail.com>

If we goto out, then it tries to call kfree_skb() on an ERR_PTR which
will oops.  Just return directly.

Signed-off-by: Dan Carpenter <error27@gmail.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_fcp.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index ba639fa..f4eb1ab 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -1323,7 +1323,7 @@ static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
 		 *
 		 * scsi-eh will escalate for when either happens.
 		 */
-		goto out;
+		return;
 	}
 
 	if (fc_fcp_lock_pkt(fsp))


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

* [PATCH 05/28] fnic: fix memory leak
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (3 preceding siblings ...)
  2011-01-29  0:03 ` [PATCH 04/28] libfc: dereferencing ERR_PTR in fc_tm_done() Robert Love
@ 2011-01-29  0:03 ` Robert Love
  2011-01-29  0:03 ` [PATCH 06/28] fnic: Bumping up fnic version from 1.4.0.145 to 1.5.0.1 Robert Love
                   ` (22 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:03 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi
  Cc: Abhijeet Joglekar, Venkata Siva Vijayendra Bhamidipati

From: Venkata Siva Vijayendra Bhamidipati <vbhamidi@cisco.com>

Fix memory leak arising due to incorrect freeing of allocated memory
for vnic stats when unregistering a vnic.

Signed-off-by: Abhijeet Joglekar <abjoglek@cisco.com>
Signed-off-by: Venkata Siva Vijayendra Bhamidipati <vbhamidi@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fnic/vnic_dev.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c
index db71014..b576be7 100644
--- a/drivers/scsi/fnic/vnic_dev.c
+++ b/drivers/scsi/fnic/vnic_dev.c
@@ -654,7 +654,7 @@ void vnic_dev_unregister(struct vnic_dev *vdev)
 				vdev->linkstatus_pa);
 		if (vdev->stats)
 			pci_free_consistent(vdev->pdev,
-				sizeof(struct vnic_dev),
+				sizeof(struct vnic_stats),
 				vdev->stats, vdev->stats_pa);
 		if (vdev->fw_info)
 			pci_free_consistent(vdev->pdev,


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

* [PATCH 06/28] fnic: Bumping up fnic version from 1.4.0.145 to 1.5.0.1.
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (4 preceding siblings ...)
  2011-01-29  0:03 ` [PATCH 05/28] fnic: fix memory leak Robert Love
@ 2011-01-29  0:03 ` Robert Love
  2011-01-29  0:03 ` [PATCH 07/28] fcoe: Fix module reference count for vports Robert Love
                   ` (21 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:03 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Venkata Siva Vijayendra Bhamidipati

From: Venkata Siva Vijayendra Bhamidipati <vbhamidi@cisco.com>

Signed-off-by: Venkata Siva Vijayendra Bhamidipati <vbhamidi@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fnic/fnic.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index 92f1850..671cde9 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -37,7 +37,7 @@
 
 #define DRV_NAME		"fnic"
 #define DRV_DESCRIPTION		"Cisco FCoE HBA Driver"
-#define DRV_VERSION		"1.4.0.145"
+#define DRV_VERSION		"1.5.0.1"
 #define PFX			DRV_NAME ": "
 #define DFX                     DRV_NAME "%d: "
 


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

* [PATCH 07/28] fcoe: Fix module reference count for vports
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (5 preceding siblings ...)
  2011-01-29  0:03 ` [PATCH 06/28] fnic: Bumping up fnic version from 1.4.0.145 to 1.5.0.1 Robert Love
@ 2011-01-29  0:03 ` Robert Love
  2011-01-29  0:03 ` [PATCH 08/28] fcoe: drop FCoE LOGO in FIP mode Robert Love
                   ` (20 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:03 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Ross Brattain

vports are not grabbing module references but are
releasing them. This causes the module reference count
to decrement too many times and it wraps around past 0.

The solution is to do a module_put() in
fcoe_interface_release() so that the reference is only
released when the fcoe_interface is released. There is a
one-to-one relationship between the N_Port and the
fcoe_interface, so the module reference will only be
dropped when the N_Port is destroyed

To create symetry in the code this patch moves the
try_module_get() call into fcoe_interface_create(). This
means that only the N_Port will grab a reference to the
module when its corresponding fcoe_interface is created.

This patch also makes it so that the fcoe_interface_create()
routine encodes any error codes in the fcoe_interface
pointer returned. This way its caller, fcoe_create(), can
return an accurate error code.

Signed-off-by: Robert Love <robert.w.love@intel.com>
Tested-by: Ross Brattain <ross.b.brattain@intel.com>
---
 drivers/scsi/fcoe/fcoe.c |   33 ++++++++++++++++++++-------------
 1 files changed, 20 insertions(+), 13 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 9f9600b..452b421 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -356,10 +356,18 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
 	struct fcoe_interface *fcoe;
 	int err;
 
+	if (!try_module_get(THIS_MODULE)) {
+		FCOE_NETDEV_DBG(netdev,
+				"Could not get a reference to the module\n");
+		fcoe = ERR_PTR(-EBUSY);
+		goto out;
+	}
+
 	fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL);
 	if (!fcoe) {
 		FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n");
-		return NULL;
+		fcoe = ERR_PTR(-ENOMEM);
+		goto out_nomod;
 	}
 
 	dev_hold(netdev);
@@ -378,9 +386,15 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
 		fcoe_ctlr_destroy(&fcoe->ctlr);
 		kfree(fcoe);
 		dev_put(netdev);
-		return NULL;
+		fcoe = ERR_PTR(err);
+		goto out_nomod;
 	}
 
+	goto out;
+
+out_nomod:
+	module_put(THIS_MODULE);
+out:
 	return fcoe;
 }
 
@@ -442,6 +456,7 @@ static void fcoe_interface_release(struct kref *kref)
 	fcoe_ctlr_destroy(&fcoe->ctlr);
 	kfree(fcoe);
 	dev_put(netdev);
+	module_put(THIS_MODULE);
 }
 
 /**
@@ -886,7 +901,6 @@ static void fcoe_if_destroy(struct fc_lport *lport)
 
 	/* Release the Scsi_Host */
 	scsi_host_put(lport->host);
-	module_put(THIS_MODULE);
 }
 
 /**
@@ -2135,15 +2149,10 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 	 */
 	if (THIS_MODULE->state != MODULE_STATE_LIVE) {
 		rc = -ENODEV;
-		goto out_nomod;
+		goto out_nodev;
 	}
 #endif
 
-	if (!try_module_get(THIS_MODULE)) {
-		rc = -EINVAL;
-		goto out_nomod;
-	}
-
 	netdev = fcoe_if_to_netdev(buffer);
 	if (!netdev) {
 		rc = -ENODEV;
@@ -2157,8 +2166,8 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 	}
 
 	fcoe = fcoe_interface_create(netdev, fip_mode);
-	if (!fcoe) {
-		rc = -ENOMEM;
+	if (IS_ERR(fcoe)) {
+		rc = PTR_ERR(fcoe);
 		goto out_putdev;
 	}
 
@@ -2198,8 +2207,6 @@ out_free:
 out_putdev:
 	dev_put(netdev);
 out_nodev:
-	module_put(THIS_MODULE);
-out_nomod:
 	rtnl_unlock();
 	mutex_unlock(&fcoe_config_mutex);
 	return rc;


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

* [PATCH 08/28] fcoe: drop FCoE LOGO in FIP mode
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (6 preceding siblings ...)
  2011-01-29  0:03 ` [PATCH 07/28] fcoe: Fix module reference count for vports Robert Love
@ 2011-01-29  0:03 ` Robert Love
  2011-01-29  0:03 ` [PATCH 09/28] libfc: fix sparse static and non-ANSI warnings Robert Love
                   ` (19 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:03 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Vasu Dev

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

Allowing FCoE LOGO followed by CVL in this case prevents
FIP login back to the FCF and then keeps lport offline,
only FIP LOGO and CLV needs to be processed while in
FIP mode, therefore this patch drops FCoE LOGO in FIP mode.

Added fcoe_filter_frames() to filter out above mentioned LOGO
in fcoe rx path along with other existing filtering in code
for bad CRC frames. Adding separate fcoe_filter_frames function
helped with better code indentations and if needed then same
will allow adding more filters at one place in future.

This LOGO drop is added after FCP frames passed up to avoid
any additional checks on fast path for this.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c |   84 ++++++++++++++++++++++++++++++----------------
 1 files changed, 54 insertions(+), 30 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 452b421..d114699 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1620,6 +1620,56 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb)
 }
 
 /**
+ * fcoe_filter_frames() - filter out bad fcoe frames, i.e. bad CRC
+ * @lport: The local port the frame was received on
+ * @fp:	   The received frame
+ *
+ * Return: 0 on passing filtering checks
+ */
+static inline int fcoe_filter_frames(struct fc_lport *lport,
+				     struct fc_frame *fp)
+{
+	struct fcoe_interface *fcoe;
+	struct fc_frame_header *fh;
+	struct sk_buff *skb = (struct sk_buff *)fp;
+	struct fcoe_dev_stats *stats;
+
+	/*
+	 * 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 (lport->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
+		fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+	else
+		fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
+
+	fh = (struct fc_frame_header *) skb_transport_header(skb);
+	fh = fc_frame_header_get(fp);
+	if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && fh->fh_type == FC_TYPE_FCP)
+		return 0;
+
+	fcoe = ((struct fcoe_port *)lport_priv(lport))->fcoe;
+	if (is_fip_mode(&fcoe->ctlr) && fc_frame_payload_op(fp) == ELS_LOGO &&
+	    ntoh24(fh->fh_s_id) == FC_FID_FLOGI) {
+		FCOE_DBG("fcoe: dropping FCoE lport LOGO in fip mode\n");
+		return -EINVAL;
+	}
+
+	if (!fr_flags(fp) & FCPHF_CRC_UNCHECKED ||
+	    le32_to_cpu(fr_crc(fp)) == ~crc32(~0, skb->data, skb->len)) {
+		fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+		return 0;
+	}
+
+	stats = per_cpu_ptr(lport->dev_stats, get_cpu());
+	stats->InvalidCRCCount++;
+	if (stats->InvalidCRCCount < 5)
+		printk(KERN_WARNING "fcoe: dropping frame with CRC error\n");
+	return -EINVAL;
+}
+
+/**
  * fcoe_recv_frame() - process a single received frame
  * @skb: frame to process
  */
@@ -1629,7 +1679,6 @@ static void fcoe_recv_frame(struct sk_buff *skb)
 	struct fc_lport *lport;
 	struct fcoe_rcv_info *fr;
 	struct fcoe_dev_stats *stats;
-	struct fc_frame_header *fh;
 	struct fcoe_crc_eof crc_eof;
 	struct fc_frame *fp;
 	struct fcoe_port *port;
@@ -1660,7 +1709,6 @@ static void fcoe_recv_frame(struct sk_buff *skb)
 	 * was done in fcoe_rcv already.
 	 */
 	hp = (struct fcoe_hdr *) skb_network_header(skb);
-	fh = (struct fc_frame_header *) skb_transport_header(skb);
 
 	stats = per_cpu_ptr(lport->dev_stats, get_cpu());
 	if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
@@ -1693,35 +1741,11 @@ static void fcoe_recv_frame(struct sk_buff *skb)
 	if (pskb_trim(skb, fr_len))
 		goto drop;
 
-	/*
-	 * 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 (lport->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) &&
-	    (fr_flags(fp) & FCPHF_CRC_UNCHECKED)) {
-		if (le32_to_cpu(fr_crc(fp)) !=
-		    ~crc32(~0, skb->data, fr_len)) {
-			if (stats->InvalidCRCCount < 5)
-				printk(KERN_WARNING "fcoe: dropping "
-				       "frame with CRC error\n");
-			stats->InvalidCRCCount++;
-			goto drop;
-		}
-		fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+	if (!fcoe_filter_frames(lport, fp)) {
+		put_cpu();
+		fc_exch_recv(lport, fp);
+		return;
 	}
-	put_cpu();
-	fc_exch_recv(lport, fp);
-	return;
-
 drop:
 	stats->ErrorFrames++;
 	put_cpu();


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

* [PATCH 09/28] libfc: fix sparse static and non-ANSI warnings
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (7 preceding siblings ...)
  2011-01-29  0:03 ` [PATCH 08/28] fcoe: drop FCoE LOGO in FIP mode Robert Love
@ 2011-01-29  0:03 ` Robert Love
  2011-01-29  0:04 ` [PATCH 10/28] libfc: add hook for FC-4 provider registration Robert Love
                   ` (18 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:03 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Randy Dunlap

From: Randy Dunlap <randy.dunlap@oracle.com>

Fix sparse warning for non-ANSI function declaration.
Declare workqueue structs as static.

Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Cc:	Robert Love <robert.w.love@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_exch.c  |    6 +++---
 drivers/scsi/libfc/fc_fcp.c   |    6 +++---
 drivers/scsi/libfc/fc_rport.c |    6 +++---
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index d21367d..e0b5b15 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -38,7 +38,7 @@ u16	fc_cpu_mask;		/* cpu mask for possible cpus */
 EXPORT_SYMBOL(fc_cpu_mask);
 static u16	fc_cpu_order;	/* 2's power to represent total possible cpus */
 static struct kmem_cache *fc_em_cachep;	       /* cache for exchanges */
-struct workqueue_struct *fc_exch_workqueue;
+static struct workqueue_struct *fc_exch_workqueue;
 
 /*
  * Structure and function definitions for managing Fibre Channel Exchanges
@@ -2357,7 +2357,7 @@ EXPORT_SYMBOL(fc_exch_init);
 /**
  * fc_setup_exch_mgr() - Setup an exchange manager
  */
-int fc_setup_exch_mgr()
+int fc_setup_exch_mgr(void)
 {
 	fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch),
 					 0, SLAB_HWCACHE_ALIGN, NULL);
@@ -2395,7 +2395,7 @@ int fc_setup_exch_mgr()
 /**
  * fc_destroy_exch_mgr() - Destroy an exchange manager
  */
-void fc_destroy_exch_mgr()
+void fc_destroy_exch_mgr(void)
 {
 	destroy_workqueue(fc_exch_workqueue);
 	kmem_cache_destroy(fc_em_cachep);
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index f4eb1ab..383a982 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -42,7 +42,7 @@
 
 #include "fc_libfc.h"
 
-struct kmem_cache *scsi_pkt_cachep;
+static struct kmem_cache *scsi_pkt_cachep;
 
 /* SRB state definitions */
 #define FC_SRB_FREE		0		/* cmd is free */
@@ -2244,7 +2244,7 @@ void fc_fcp_destroy(struct fc_lport *lport)
 }
 EXPORT_SYMBOL(fc_fcp_destroy);
 
-int fc_setup_fcp()
+int fc_setup_fcp(void)
 {
 	int rc = 0;
 
@@ -2260,7 +2260,7 @@ int fc_setup_fcp()
 	return rc;
 }
 
-void fc_destroy_fcp()
+void fc_destroy_fcp(void)
 {
 	if (scsi_pkt_cachep)
 		kmem_cache_destroy(scsi_pkt_cachep);
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 325bc42..309e3e7 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -58,7 +58,7 @@
 
 #include "fc_libfc.h"
 
-struct workqueue_struct *rport_event_queue;
+static struct workqueue_struct *rport_event_queue;
 
 static void fc_rport_enter_flogi(struct fc_rport_priv *);
 static void fc_rport_enter_plogi(struct fc_rport_priv *);
@@ -1890,7 +1890,7 @@ EXPORT_SYMBOL(fc_rport_init);
 /**
  * fc_setup_rport() - Initialize the rport_event_queue
  */
-int fc_setup_rport()
+int fc_setup_rport(void)
 {
 	rport_event_queue = create_singlethread_workqueue("fc_rport_eq");
 	if (!rport_event_queue)
@@ -1901,7 +1901,7 @@ int fc_setup_rport()
 /**
  * fc_destroy_rport() - Destroy the rport_event_queue
  */
-void fc_destroy_rport()
+void fc_destroy_rport(void)
 {
 	destroy_workqueue(rport_event_queue);
 }


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

* [PATCH 10/28] libfc: add hook for FC-4 provider registration
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (8 preceding siblings ...)
  2011-01-29  0:03 ` [PATCH 09/28] libfc: fix sparse static and non-ANSI warnings Robert Love
@ 2011-01-29  0:04 ` Robert Love
  2011-01-29  0:04 ` [PATCH 11/28] libfc: add method for setting handler for incoming exchange Robert Love
                   ` (17 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:04 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

Allow FC-4 provider modules to hook into libfc, mostly for targets.
This should allow any FC-4 module to handle PRLI requests and maintain
process-association states.

Each provider registers its ops with libfc and then will be called for
any incoming PRLI for that FC-4 type on any instance.   The provider
can decide whether to handle that particular instance using any method
it likes, such as ACLs or other configuration information.

A count is kept of the number of successful PRLIs from the remote port.
Providers are called back with an implicit PRLO when the remote port
is about to be deleted or has been reset.

fc_lport_recv_req() now sends incoming FC-4 requests to FC-4 providers,
and there is a built-in provider always registered for handling
incoming ELS requests.

The call to provider recv() routines uses rcu_read_lock()
so that providers aren't removed during the call.  That lock is very
cheap and shouldn't affect any performance on ELS requests.
Providers can rely on the RCU lock to protect a session lookup as well.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_libfc.c |   60 ++++++++++++++++++
 drivers/scsi/libfc/fc_libfc.h |   11 +++
 drivers/scsi/libfc/fc_lport.c |   65 +++++++++++++++++---
 drivers/scsi/libfc/fc_rport.c |  133 +++++++++++++++++++++++++++++++++--------
 include/scsi/libfc.h          |   26 ++++++++
 5 files changed, 259 insertions(+), 36 deletions(-)

diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
index 6a48c28..ae3abef 100644
--- a/drivers/scsi/libfc/fc_libfc.c
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -35,6 +35,23 @@ unsigned int fc_debug_logging;
 module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
 
+DEFINE_MUTEX(fc_prov_mutex);
+
+/*
+ * Providers which primarily send requests and PRLIs.
+ */
+struct fc4_prov *fc_active_prov[FC_FC4_PROV_SIZE] = {
+	[0] = &fc_rport_t0_prov,
+	[FC_TYPE_FCP] = &fc_rport_fcp_init,
+};
+
+/*
+ * Providers which receive requests.
+ */
+struct fc4_prov *fc_passive_prov[FC_FC4_PROV_SIZE] = {
+	[FC_TYPE_ELS] = &fc_lport_els_prov,
+};
+
 /**
  * libfc_init() - Initialize libfc.ko
  */
@@ -210,3 +227,46 @@ void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp,
 	fc_fill_hdr(fp, in_fp, r_ctl, FC_FCTL_RESP, 0, parm_offset);
 }
 EXPORT_SYMBOL(fc_fill_reply_hdr);
+
+/**
+ * fc_fc4_register_provider() - register FC-4 upper-level provider.
+ * @type: FC-4 type, such as FC_TYPE_FCP
+ * @prov: structure describing provider including ops vector.
+ *
+ * Returns 0 on success, negative error otherwise.
+ */
+int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *prov)
+{
+	struct fc4_prov **prov_entry;
+	int ret = 0;
+
+	if (type >= FC_FC4_PROV_SIZE)
+		return -EINVAL;
+	mutex_lock(&fc_prov_mutex);
+	prov_entry = (prov->recv ? fc_passive_prov : fc_active_prov) + type;
+	if (*prov_entry)
+		ret = -EBUSY;
+	else
+		*prov_entry = prov;
+	mutex_unlock(&fc_prov_mutex);
+	return ret;
+}
+EXPORT_SYMBOL(fc_fc4_register_provider);
+
+/**
+ * fc_fc4_deregister_provider() - deregister FC-4 upper-level provider.
+ * @type: FC-4 type, such as FC_TYPE_FCP
+ * @prov: structure describing provider including ops vector.
+ */
+void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov)
+{
+	BUG_ON(type >= FC_FC4_PROV_SIZE);
+	mutex_lock(&fc_prov_mutex);
+	if (prov->recv)
+		rcu_assign_pointer(fc_passive_prov[type], NULL);
+	else
+		rcu_assign_pointer(fc_active_prov[type], NULL);
+	mutex_unlock(&fc_prov_mutex);
+	synchronize_rcu();
+}
+EXPORT_SYMBOL(fc_fc4_deregister_provider);
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
index eea0c35..205de28 100644
--- a/drivers/scsi/libfc/fc_libfc.h
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -94,6 +94,17 @@ extern unsigned int fc_debug_logging;
 				(lport)->host->host_no,	##args))
 
 /*
+ * FC-4 Providers.
+ */
+extern struct fc4_prov *fc_active_prov[];	/* providers without recv */
+extern struct fc4_prov *fc_passive_prov[];	/* providers with recv */
+extern struct mutex fc_prov_mutex;		/* lock over table changes */
+
+extern struct fc4_prov fc_rport_t0_prov;	/* type 0 provider */
+extern struct fc4_prov fc_lport_els_prov;	/* ELS provider */
+extern struct fc4_prov fc_rport_fcp_init;	/* FCP initiator provider */
+
+/*
  * Set up direct-data placement for this I/O request
  */
 void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid);
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index c5a10f9..e2cd087 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -849,7 +849,7 @@ out:
 }
 
 /**
- * fc_lport_recv_req() - The generic lport request handler
+ * fc_lport_recv_els_req() - The generic lport ELS request handler
  * @lport: The local port that received the request
  * @fp:	   The request frame
  *
@@ -859,9 +859,9 @@ out:
  * Locking Note: This function should not be called with the lport
  *		 lock held becuase it will grab the lock.
  */
-static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
+static void fc_lport_recv_els_req(struct fc_lport *lport,
+				  struct fc_frame *fp)
 {
-	struct fc_frame_header *fh = fc_frame_header_get(fp);
 	void (*recv)(struct fc_lport *, struct fc_frame *);
 
 	mutex_lock(&lport->lp_mutex);
@@ -873,8 +873,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
 	 */
 	if (!lport->link_up)
 		fc_frame_free(fp);
-	else if (fh->fh_type == FC_TYPE_ELS &&
-		 fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
+	else {
 		/*
 		 * Check opcode.
 		 */
@@ -903,14 +902,62 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
 		}
 
 		recv(lport, fp);
-	} else {
-		FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n",
-			     fr_eof(fp));
-		fc_frame_free(fp);
 	}
 	mutex_unlock(&lport->lp_mutex);
 }
 
+static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len,
+			     const struct fc_els_spp *spp_in,
+			     struct fc_els_spp *spp_out)
+{
+	return FC_SPP_RESP_INVL;
+}
+
+struct fc4_prov fc_lport_els_prov = {
+	.prli = fc_lport_els_prli,
+	.recv = fc_lport_recv_els_req,
+};
+
+/**
+ * fc_lport_recv_req() - The generic lport request handler
+ * @lport: The lport that received the request
+ * @fp: The frame the request is in
+ *
+ * Locking Note: This function should not be called with the lport
+ *		 lock held becuase it may grab the lock.
+ */
+static void fc_lport_recv_req(struct fc_lport *lport,
+			      struct fc_frame *fp)
+{
+	struct fc_frame_header *fh = fc_frame_header_get(fp);
+	struct fc_seq *sp = fr_seq(fp);
+	struct fc4_prov *prov;
+
+	/*
+	 * Use RCU read lock and module_lock to be sure module doesn't
+	 * deregister and get unloaded while we're calling it.
+	 * try_module_get() is inlined and accepts a NULL parameter.
+	 * Only ELSes and FCP target ops should come through here.
+	 * The locking is unfortunate, and a better scheme is being sought.
+	 */
+
+	rcu_read_lock();
+	if (fh->fh_type >= FC_FC4_PROV_SIZE)
+		goto drop;
+	prov = rcu_dereference(fc_passive_prov[fh->fh_type]);
+	if (!prov || !try_module_get(prov->module))
+		goto drop;
+	rcu_read_unlock();
+	prov->recv(lport, fp);
+	module_put(prov->module);
+	return;
+drop:
+	rcu_read_unlock();
+	FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type);
+	fc_frame_free(fp);
+	lport->tt.exch_done(sp);
+}
+
 /**
  * fc_lport_reset() - Reset a local port
  * @lport: The local port which should be reset
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 309e3e7..a92954c 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -257,6 +257,8 @@ static void fc_rport_work(struct work_struct *work)
 	struct fc_rport_operations *rport_ops;
 	struct fc_rport_identifiers ids;
 	struct fc_rport *rport;
+	struct fc4_prov *prov;
+	u8 type;
 
 	mutex_lock(&rdata->rp_mutex);
 	event = rdata->event;
@@ -306,6 +308,15 @@ static void fc_rport_work(struct work_struct *work)
 	case RPORT_EV_FAILED:
 	case RPORT_EV_LOGO:
 	case RPORT_EV_STOP:
+		if (rdata->prli_count) {
+			mutex_lock(&fc_prov_mutex);
+			for (type = 1; type < FC_FC4_PROV_SIZE; type++) {
+				prov = fc_passive_prov[type];
+				if (prov && prov->prlo)
+					prov->prlo(rdata);
+			}
+			mutex_unlock(&fc_prov_mutex);
+		}
 		port_id = rdata->ids.port_id;
 		mutex_unlock(&rdata->rp_mutex);
 
@@ -1643,9 +1654,9 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
 	unsigned int len;
 	unsigned int plen;
 	enum fc_els_spp_resp resp;
+	enum fc_els_spp_resp passive;
 	struct fc_seq_els_data rjt_data;
-	u32 fcp_parm;
-	u32 roles = FC_RPORT_ROLE_UNKNOWN;
+	struct fc4_prov *prov;
 
 	FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
 		     fc_rport_state(rdata));
@@ -1679,46 +1690,41 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
 	pp->prli.prli_len = htons(len);
 	len -= sizeof(struct fc_els_prli);
 
-	/* reinitialize remote port roles */
-	rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
-
 	/*
 	 * Go through all the service parameter pages and build
 	 * response.  If plen indicates longer SPP than standard,
 	 * use that.  The entire response has been pre-cleared above.
 	 */
 	spp = &pp->spp;
+	mutex_lock(&fc_prov_mutex);
 	while (len >= plen) {
 		spp->spp_type = rspp->spp_type;
 		spp->spp_type_ext = rspp->spp_type_ext;
-		spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
-		resp = FC_SPP_RESP_ACK;
-
-		switch (rspp->spp_type) {
-		case 0:	/* common to all FC-4 types */
-			break;
-		case FC_TYPE_FCP:
-			fcp_parm = ntohl(rspp->spp_params);
-			if (fcp_parm & FCP_SPPF_RETRY)
-				rdata->flags |= FC_RP_FLAGS_RETRY;
-			rdata->supported_classes = FC_COS_CLASS3;
-			if (fcp_parm & FCP_SPPF_INIT_FCN)
-				roles |= FC_RPORT_ROLE_FCP_INITIATOR;
-			if (fcp_parm & FCP_SPPF_TARG_FCN)
-				roles |= FC_RPORT_ROLE_FCP_TARGET;
-			rdata->ids.roles = roles;
-
-			spp->spp_params = htonl(lport->service_params);
-			break;
-		default:
-			resp = FC_SPP_RESP_INVL;
-			break;
+		resp = 0;
+
+		if (rspp->spp_type < FC_FC4_PROV_SIZE) {
+			prov = fc_active_prov[rspp->spp_type];
+			if (prov)
+				resp = prov->prli(rdata, plen, rspp, spp);
+			prov = fc_passive_prov[rspp->spp_type];
+			if (prov) {
+				passive = prov->prli(rdata, plen, rspp, spp);
+				if (!resp || passive == FC_SPP_RESP_ACK)
+					resp = passive;
+			}
+		}
+		if (!resp) {
+			if (spp->spp_flags & FC_SPP_EST_IMG_PAIR)
+				resp |= FC_SPP_RESP_CONF;
+			else
+				resp |= FC_SPP_RESP_INVL;
 		}
 		spp->spp_flags |= resp;
 		len -= plen;
 		rspp = (struct fc_els_spp *)((char *)rspp + plen);
 		spp = (struct fc_els_spp *)((char *)spp + plen);
 	}
+	mutex_unlock(&fc_prov_mutex);
 
 	/*
 	 * Send LS_ACC.	 If this fails, the originator should retry.
@@ -1888,6 +1894,79 @@ int fc_rport_init(struct fc_lport *lport)
 EXPORT_SYMBOL(fc_rport_init);
 
 /**
+ * fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator.
+ * @rdata: remote port private
+ * @spp_len: service parameter page length
+ * @rspp: received service parameter page
+ * @spp: response service parameter page
+ *
+ * Returns the value for the response code to be placed in spp_flags;
+ * Returns 0 if not an initiator.
+ */
+static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len,
+			     const struct fc_els_spp *rspp,
+			     struct fc_els_spp *spp)
+{
+	struct fc_lport *lport = rdata->local_port;
+	u32 fcp_parm;
+
+	fcp_parm = ntohl(rspp->spp_params);
+	rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+	if (fcp_parm & FCP_SPPF_INIT_FCN)
+		rdata->ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+	if (fcp_parm & FCP_SPPF_TARG_FCN)
+		rdata->ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
+	if (fcp_parm & FCP_SPPF_RETRY)
+		rdata->flags |= FC_RP_FLAGS_RETRY;
+	rdata->supported_classes = FC_COS_CLASS3;
+
+	if (!(lport->service_params & FC_RPORT_ROLE_FCP_INITIATOR))
+		return 0;
+
+	spp->spp_flags |= rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
+
+	/*
+	 * OR in our service parameters with other providers (target), if any.
+	 */
+	fcp_parm = ntohl(spp->spp_params);
+	spp->spp_params = htonl(fcp_parm | lport->service_params);
+	return FC_SPP_RESP_ACK;
+}
+
+/*
+ * FC-4 provider ops for FCP initiator.
+ */
+struct fc4_prov fc_rport_fcp_init = {
+	.prli = fc_rport_fcp_prli,
+};
+
+/**
+ * fc_rport_t0_prli() - Handle incoming PRLI parameters for type 0
+ * @rdata: remote port private
+ * @spp_len: service parameter page length
+ * @rspp: received service parameter page
+ * @spp: response service parameter page
+ */
+static int fc_rport_t0_prli(struct fc_rport_priv *rdata, u32 spp_len,
+			    const struct fc_els_spp *rspp,
+			    struct fc_els_spp *spp)
+{
+	if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR)
+		return FC_SPP_RESP_INVL;
+	return FC_SPP_RESP_ACK;
+}
+
+/*
+ * FC-4 provider ops for type 0 service parameters.
+ *
+ * This handles the special case of type 0 which is always successful
+ * but doesn't do anything otherwise.
+ */
+struct fc4_prov fc_rport_t0_prov = {
+	.prli = fc_rport_t0_prli,
+};
+
+/**
  * fc_setup_rport() - Initialize the rport_event_queue
  */
 int fc_setup_rport(void)
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index f53c8e3..3ae2a76 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -35,6 +35,8 @@
 
 #include <scsi/fc_frame.h>
 
+#define	FC_FC4_PROV_SIZE	(FC_TYPE_FCP + 1)	/* size of tables */
+
 /*
  * libfc error codes
  */
@@ -179,6 +181,7 @@ struct fc_rport_libfc_priv {
  * @rp_mutex:       The mutex that protects the remote port
  * @retry_work:     Handle for retries
  * @event_callback: Callback when READY, FAILED or LOGO states complete
+ * @prli_count:     Count of open PRLI sessions in providers
  * @rcu:	    Structure used for freeing in an RCU-safe manner
  */
 struct fc_rport_priv {
@@ -202,6 +205,7 @@ struct fc_rport_priv {
 	struct list_head            peers;
 	struct work_struct          event_work;
 	u32			    supported_classes;
+	u16                         prli_count;
 	struct rcu_head		    rcu;
 };
 
@@ -848,6 +852,28 @@ struct fc_lport {
 	struct delayed_work	       retry_work;
 };
 
+/**
+ * struct fc4_prov - FC-4 provider registration
+ * @prli:               Handler for incoming PRLI
+ * @prlo:               Handler for session reset
+ * @recv:		Handler for incoming request
+ * @module:		Pointer to module.  May be NULL.
+ */
+struct fc4_prov {
+	int (*prli)(struct fc_rport_priv *, u32 spp_len,
+		    const struct fc_els_spp *spp_in,
+		    struct fc_els_spp *spp_out);
+	void (*prlo)(struct fc_rport_priv *);
+	void (*recv)(struct fc_lport *, struct fc_frame *);
+	struct module *module;
+};
+
+/*
+ * Register FC-4 provider with libfc.
+ */
+int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *);
+void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *);
+
 /*
  * FC_LPORT HELPER FUNCTIONS
  *****************************/


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

* [PATCH 11/28] libfc: add method for setting handler for incoming exchange
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (9 preceding siblings ...)
  2011-01-29  0:04 ` [PATCH 10/28] libfc: add hook for FC-4 provider registration Robert Love
@ 2011-01-29  0:04 ` Robert Love
  2011-01-29  0:04 ` [PATCH 12/28] libfc: add local port hook for provider session lookup Robert Love
                   ` (16 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:04 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

Add a method for setting handler for incoming exchange.
For multi-sequence exchanges, this allows the target driver
to add a response handler for handling subsequent sequences,
and exchange manager resets.

The new function is called fc_seq_set_resp().

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_exch.c |   19 +++++++++++++++++++
 include/scsi/libfc.h         |   10 ++++++++++
 2 files changed, 29 insertions(+), 0 deletions(-)

diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index e0b5b15..1f124c0 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -558,6 +558,22 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
 	return sp;
 }
 
+/*
+ * Set the response handler for the exchange associated with a sequence.
+ */
+static void fc_seq_set_resp(struct fc_seq *sp,
+			    void (*resp)(struct fc_seq *, struct fc_frame *,
+					 void *),
+			    void *arg)
+{
+	struct fc_exch *ep = fc_seq_exch(sp);
+
+	spin_lock_bh(&ep->ex_lock);
+	ep->resp = resp;
+	ep->arg = arg;
+	spin_unlock_bh(&ep->ex_lock);
+}
+
 /**
  * fc_seq_exch_abort() - Abort an exchange and sequence
  * @req_sp:	The sequence to be aborted
@@ -2329,6 +2345,9 @@ int fc_exch_init(struct fc_lport *lport)
 	if (!lport->tt.seq_start_next)
 		lport->tt.seq_start_next = fc_seq_start_next;
 
+	if (!lport->tt.seq_set_resp)
+		lport->tt.seq_set_resp = fc_seq_set_resp;
+
 	if (!lport->tt.exch_seq_send)
 		lport->tt.exch_seq_send = fc_exch_seq_send;
 
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 3ae2a76..3b8f5d8 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -555,6 +555,16 @@ struct libfc_function_template {
 	struct fc_seq *(*seq_start_next)(struct fc_seq *);
 
 	/*
+	 * Set a response handler for the exchange of the sequence.
+	 *
+	 * STATUS: OPTIONAL
+	 */
+	void (*seq_set_resp)(struct fc_seq *sp,
+			     void (*resp)(struct fc_seq *, struct fc_frame *,
+					  void *),
+			     void *arg);
+
+	/*
 	 * Assign a sequence for an incoming request frame.
 	 *
 	 * STATUS: OPTIONAL


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

* [PATCH 12/28] libfc: add local port hook for provider session lookup
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (10 preceding siblings ...)
  2011-01-29  0:04 ` [PATCH 11/28] libfc: add method for setting handler for incoming exchange Robert Love
@ 2011-01-29  0:04 ` Robert Love
  2011-01-29  0:04 ` [PATCH 13/28] libfc: add hook to notify providers of local port changes Robert Love
                   ` (15 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:04 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

The target provider needs a per-instance lookup table
or other way to lookup sessions quickly without going through
a linear list or serializing too much.

Add a simple void * array indexed by FC-4 type to the fc_lport.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Committed-by: Nicholas A. Bellinger <nab@linux-iscsi.org>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 include/scsi/libfc.h |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 3b8f5d8..a9aff25 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -805,6 +805,7 @@ struct fc_disc {
  * @lp_mutex:              Mutex to protect the local port
  * @list:                  Handle for list of local ports
  * @retry_work:            Handle to local port for delayed retry context
+ * @prov:		   Pointers available for use by passive FC-4 providers
  */
 struct fc_lport {
 	/* Associations */
@@ -860,6 +861,7 @@ struct fc_lport {
 	struct mutex                   lp_mutex;
 	struct list_head               list;
 	struct delayed_work	       retry_work;
+	void			       *prov[FC_FC4_PROV_SIZE];
 };
 
 /**


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

* [PATCH 13/28] libfc: add hook to notify providers of local port changes
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (11 preceding siblings ...)
  2011-01-29  0:04 ` [PATCH 12/28] libfc: add local port hook for provider session lookup Robert Love
@ 2011-01-29  0:04 ` Robert Love
  2011-01-29  0:04 ` [PATCH 14/28] libfc: use PRLI hook to get parameters when sending outgoing PRLI Robert Love
                   ` (14 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:04 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

When an SCST provider is registered, it needs to know what
local ports are available for configuration as targets.

Add a notifier chain that is invoked when any local port
that is added or deleted.

Maintain a global list of local ports and add an
interator function that calls a given function for
every existing local port.  This is used when first
loading a provider.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_libfc.c |   41 +++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/libfc/fc_libfc.h |    2 ++
 drivers/scsi/libfc/fc_lport.c |    2 ++
 include/scsi/libfc.h          |   14 +++++++++++++-
 4 files changed, 58 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
index ae3abef..5e40dab 100644
--- a/drivers/scsi/libfc/fc_libfc.c
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -36,6 +36,10 @@ module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
 
 DEFINE_MUTEX(fc_prov_mutex);
+static LIST_HEAD(fc_local_ports);
+struct blocking_notifier_head fc_lport_notifier_head =
+		BLOCKING_NOTIFIER_INIT(fc_lport_notifier_head);
+EXPORT_SYMBOL(fc_lport_notifier_head);
 
 /*
  * Providers which primarily send requests and PRLIs.
@@ -228,6 +232,17 @@ void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp,
 }
 EXPORT_SYMBOL(fc_fill_reply_hdr);
 
+void fc_lport_iterate(void (*notify)(struct fc_lport *, void *), void *arg)
+{
+	struct fc_lport *lport;
+
+	mutex_lock(&fc_prov_mutex);
+	list_for_each_entry(lport, &fc_local_ports, lport_list)
+		notify(lport, arg);
+	mutex_unlock(&fc_prov_mutex);
+}
+EXPORT_SYMBOL(fc_lport_iterate);
+
 /**
  * fc_fc4_register_provider() - register FC-4 upper-level provider.
  * @type: FC-4 type, such as FC_TYPE_FCP
@@ -270,3 +285,29 @@ void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov)
 	synchronize_rcu();
 }
 EXPORT_SYMBOL(fc_fc4_deregister_provider);
+
+/**
+ * fc_fc4_add_lport() - add new local port to list and run notifiers.
+ * @lport:  The new local port.
+ */
+void fc_fc4_add_lport(struct fc_lport *lport)
+{
+	mutex_lock(&fc_prov_mutex);
+	list_add_tail(&lport->lport_list, &fc_local_ports);
+	blocking_notifier_call_chain(&fc_lport_notifier_head,
+				     FC_LPORT_EV_ADD, lport);
+	mutex_unlock(&fc_prov_mutex);
+}
+
+/**
+ * fc_fc4_del_lport() - remove local port from list and run notifiers.
+ * @lport:  The new local port.
+ */
+void fc_fc4_del_lport(struct fc_lport *lport)
+{
+	mutex_lock(&fc_prov_mutex);
+	list_del(&lport->lport_list);
+	blocking_notifier_call_chain(&fc_lport_notifier_head,
+				     FC_LPORT_EV_DEL, lport);
+	mutex_unlock(&fc_prov_mutex);
+}
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
index 205de28..8496f70 100644
--- a/drivers/scsi/libfc/fc_libfc.h
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -123,6 +123,8 @@ void fc_destroy_fcp(void);
  * Internal libfc functions
  */
 const char *fc_els_resp_type(struct fc_frame *);
+extern void fc_fc4_add_lport(struct fc_lport *);
+extern void fc_fc4_del_lport(struct fc_lport *);
 
 /*
  * Copies a buffer into an sg list
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index e2cd087..e0ef814 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -633,6 +633,7 @@ int fc_lport_destroy(struct fc_lport *lport)
 	lport->tt.fcp_abort_io(lport);
 	lport->tt.disc_stop_final(lport);
 	lport->tt.exch_mgr_reset(lport, 0, 0);
+	fc_fc4_del_lport(lport);
 	return 0;
 }
 EXPORT_SYMBOL(fc_lport_destroy);
@@ -1633,6 +1634,7 @@ int fc_lport_init(struct fc_lport *lport)
 		fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT;
 	if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
 		fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
+	fc_fc4_add_lport(lport);
 
 	return 0;
 }
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index a9aff25..79d1c76 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -763,6 +763,15 @@ struct fc_disc {
 			      enum fc_disc_event);
 };
 
+/*
+ * Local port notifier and events.
+ */
+extern struct blocking_notifier_head fc_lport_notifier_head;
+enum fc_lport_event {
+	FC_LPORT_EV_ADD,
+	FC_LPORT_EV_DEL,
+};
+
 /**
  * struct fc_lport - Local port
  * @host:                  The SCSI host associated with a local port
@@ -803,9 +812,10 @@ struct fc_disc {
  * @lso_max:               The maximum large offload send size
  * @fcts:                  FC-4 type mask
  * @lp_mutex:              Mutex to protect the local port
- * @list:                  Handle for list of local ports
+ * @list:                  Linkage on list of vport peers
  * @retry_work:            Handle to local port for delayed retry context
  * @prov:		   Pointers available for use by passive FC-4 providers
+ * @lport_list:            Linkage on module-wide list of local ports
  */
 struct fc_lport {
 	/* Associations */
@@ -862,6 +872,7 @@ struct fc_lport {
 	struct list_head               list;
 	struct delayed_work	       retry_work;
 	void			       *prov[FC_FC4_PROV_SIZE];
+	struct list_head               lport_list;
 };
 
 /**
@@ -1016,6 +1027,7 @@ struct fc_lport *libfc_vport_create(struct fc_vport *, int privsize);
 struct fc_lport *fc_vport_id_lookup(struct fc_lport *, u32 port_id);
 int fc_lport_bsg_request(struct fc_bsg_job *);
 void fc_lport_set_local_id(struct fc_lport *, u32 port_id);
+void fc_lport_iterate(void (*func)(struct fc_lport *, void *), void *);
 
 /*
  * REMOTE PORT LAYER


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

* [PATCH 14/28] libfc: use PRLI hook to get parameters when sending outgoing PRLI
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (12 preceding siblings ...)
  2011-01-29  0:04 ` [PATCH 13/28] libfc: add hook to notify providers of local port changes Robert Love
@ 2011-01-29  0:04 ` Robert Love
  2011-01-29  0:04 ` [PATCH 15/28] libfc: Remove usage of the Scsi_Host's host_lock Robert Love
                   ` (13 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:04 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

When sending an outgoing PRLI as an initiator, get the parameters
from registered providers so that they all get a chance to decide
on roles.

The passive provider is called last, and could override the
initiator role.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_rport.c |   27 ++++++++++++++++++++++++---
 1 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index a92954c..9ded612 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -961,6 +961,8 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 		struct fc_els_prli prli;
 		struct fc_els_spp spp;
 	} *pp;
+	struct fc_els_spp temp_spp;
+	struct fc4_prov *prov;
 	u32 roles = FC_RPORT_ROLE_UNKNOWN;
 	u32 fcp_parm = 0;
 	u8 op;
@@ -1009,6 +1011,13 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 		if (fcp_parm & FCP_SPPF_RETRY)
 			rdata->flags |= FC_RP_FLAGS_RETRY;
 
+		prov = fc_passive_prov[FC_TYPE_FCP];
+		if (prov) {
+			memset(&temp_spp, 0, sizeof(temp_spp));
+			prov->prli(rdata, pp->prli.prli_spp_len,
+				   &pp->spp, &temp_spp);
+		}
+
 		rdata->supported_classes = FC_COS_CLASS3;
 		if (fcp_parm & FCP_SPPF_INIT_FCN)
 			roles |= FC_RPORT_ROLE_FCP_INITIATOR;
@@ -1045,6 +1054,7 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
 		struct fc_els_spp spp;
 	} *pp;
 	struct fc_frame *fp;
+	struct fc4_prov *prov;
 
 	/*
 	 * If the rport is one of the well known addresses
@@ -1066,9 +1076,20 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
 		return;
 	}
 
-	if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PRLI,
-				  fc_rport_prli_resp, rdata,
-				  2 * lport->r_a_tov))
+	fc_prli_fill(lport, fp);
+
+	prov = fc_passive_prov[FC_TYPE_FCP];
+	if (prov) {
+		pp = fc_frame_payload_get(fp, sizeof(*pp));
+		prov->prli(rdata, sizeof(pp->spp), NULL, &pp->spp);
+	}
+
+	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rdata->ids.port_id,
+		       fc_host_port_id(lport->host), FC_TYPE_ELS,
+		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+	if (!lport->tt.exch_seq_send(lport, fp, fc_rport_prli_resp,
+				    NULL, rdata, 2 * lport->r_a_tov))
 		fc_rport_error_retry(rdata, NULL);
 	else
 		kref_get(&rdata->kref);


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

* [PATCH 15/28] libfc: Remove usage of the Scsi_Host's host_lock
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (13 preceding siblings ...)
  2011-01-29  0:04 ` [PATCH 14/28] libfc: use PRLI hook to get parameters when sending outgoing PRLI Robert Love
@ 2011-01-29  0:04 ` Robert Love
  2011-01-29  0:04 ` [PATCH 16/28] libfc: export seq_release() for users of seq_assign() Robert Love
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:04 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Ross Brattain, Nicholas A. Bellinger

This patch removes the use of the Scsi_Host's host_lock
within fc_queuecommand. It also removes the DEF_SCSI_QCMD
usage so that libfc has fully moved on to the new
queuecommand interface.

Signed-off-by: Robert Love <robert.w.love@intel.com>
Tested-by: Ross Brattain <ross.b.brattain@intel.com>
Reviewed-by: Nicholas A. Bellinger <nab@linux-iscsi.org>
---
 drivers/scsi/libfc/fc_fcp.c |   20 ++++++--------------
 1 files changed, 6 insertions(+), 14 deletions(-)

diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 383a982..b1b03af 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -1789,15 +1789,14 @@ static inline int fc_fcp_lport_queue_ready(struct fc_lport *lport)
 
 /**
  * fc_queuecommand() - The queuecommand function of the SCSI template
+ * @shost: The Scsi_Host that the command was issued to
  * @cmd:   The scsi_cmnd to be executed
- * @done:  The callback function to be called when the scsi_cmnd is complete
  *
- * This is the i/o strategy routine, called by the SCSI layer. This routine
- * is called with the host_lock held.
+ * This is the i/o strategy routine, called by the SCSI layer.
  */
-static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
+int fc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc_cmd)
 {
-	struct fc_lport *lport;
+	struct fc_lport *lport = shost_priv(shost);
 	struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
 	struct fc_fcp_pkt *fsp;
 	struct fc_rport_libfc_priv *rpriv;
@@ -1805,15 +1804,12 @@ static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scs
 	int rc = 0;
 	struct fcoe_dev_stats *stats;
 
-	lport = shost_priv(sc_cmd->device->host);
-
 	rval = fc_remote_port_chkready(rport);
 	if (rval) {
 		sc_cmd->result = rval;
-		done(sc_cmd);
+		sc_cmd->scsi_done(sc_cmd);
 		return 0;
 	}
-	spin_unlock_irq(lport->host->host_lock);
 
 	if (!*(struct fc_remote_port **)rport->dd_data) {
 		/*
@@ -1821,7 +1817,7 @@ static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scs
 		 * online
 		 */
 		sc_cmd->result = DID_IMM_RETRY << 16;
-		done(sc_cmd);
+		sc_cmd->scsi_done(sc_cmd);
 		goto out;
 	}
 
@@ -1845,7 +1841,6 @@ static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scs
 	 */
 	fsp->cmd = sc_cmd;	/* save the cmd */
 	fsp->rport = rport;	/* set the remote port ptr */
-	sc_cmd->scsi_done = done;
 
 	/*
 	 * set up the transfer length
@@ -1886,11 +1881,8 @@ static int fc_queuecommand_lck(struct scsi_cmnd *sc_cmd, void (*done)(struct scs
 		rc = SCSI_MLQUEUE_HOST_BUSY;
 	}
 out:
-	spin_lock_irq(lport->host->host_lock);
 	return rc;
 }
-
-DEF_SCSI_QCMD(fc_queuecommand)
 EXPORT_SYMBOL(fc_queuecommand);
 
 /**


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

* [PATCH 16/28] libfc: export seq_release() for users of seq_assign()
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (14 preceding siblings ...)
  2011-01-29  0:04 ` [PATCH 15/28] libfc: Remove usage of the Scsi_Host's host_lock Robert Love
@ 2011-01-29  0:04 ` Robert Love
  2011-01-29  0:04 ` [PATCH 17/28] libfc: Enhanced exchange ID selection mechanism and fix related EMA selection logic Robert Love
                   ` (11 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:04 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt, Nicholas A. Bellinger

From: Joe Eykholt <jeykholt@cisco.com>

Target modules using lport->tt.seq_assign() get a hold on the
exchange but have no way of releasing it.  Add that.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Acked-by: Nicholas A. Bellinger <nab@linux-iscsi.org>
---
 drivers/scsi/libfc/fc_exch.c |   14 ++++++++++++++
 include/scsi/libfc.h         |    7 +++++++
 2 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 1f124c0..a3d6402 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1282,6 +1282,8 @@ free:
  * @fp:    The request frame
  *
  * On success, the sequence pointer will be returned and also in fr_seq(@fp).
+ * A reference will be held on the exchange/sequence for the caller, which
+ * must call fc_seq_release().
  */
 static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
 {
@@ -1299,6 +1301,15 @@ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
 }
 
 /**
+ * fc_seq_release() - Release the hold
+ * @sp:    The sequence.
+ */
+static void fc_seq_release(struct fc_seq *sp)
+{
+	fc_exch_release(fc_seq_exch(sp));
+}
+
+/**
  * fc_exch_recv_req() - Handler for an incoming request
  * @lport: The local port that received the request
  * @mp:	   The EM that the exchange is on
@@ -2369,6 +2380,9 @@ int fc_exch_init(struct fc_lport *lport)
 	if (!lport->tt.seq_assign)
 		lport->tt.seq_assign = fc_seq_assign;
 
+	if (!lport->tt.seq_release)
+		lport->tt.seq_release = fc_seq_release;
+
 	return 0;
 }
 EXPORT_SYMBOL(fc_exch_init);
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 79d1c76..6d64e44 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -572,6 +572,13 @@ struct libfc_function_template {
 	struct fc_seq *(*seq_assign)(struct fc_lport *, struct fc_frame *);
 
 	/*
+	 * Release the reference on the sequence returned by seq_assign().
+	 *
+	 * STATUS: OPTIONAL
+	 */
+	void (*seq_release)(struct fc_seq *);
+
+	/*
 	 * Reset an exchange manager, completing all sequences and exchanges.
 	 * If s_id is non-zero, reset only exchanges originating from that FID.
 	 * If d_id is non-zero, reset only exchanges sending to that FID.


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

* [PATCH 17/28] libfc: Enhanced exchange ID selection mechanism and fix related EMA selection logic.
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (15 preceding siblings ...)
  2011-01-29  0:04 ` [PATCH 16/28] libfc: export seq_release() for users of seq_assign() Robert Love
@ 2011-01-29  0:04 ` Robert Love
  2011-01-29  0:04 ` [PATCH 18/28] libfcoe: move logging macros into the local libfcoe.h header file Robert Love
                   ` (10 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:04 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Kiran Patil

From: Kiran Patil <kiran.patil@intel.com>

Problem: In case of exchange responder case, EMA selection was defaulted to the last EMA
         from EMA list (lport.ema_list).  If exchange ID is selected from offload pool
         and not setup DDP, resulting into incorrect selection of EMA, and eventually
         dropping the packet because unable to find exchange.

Fix:     Enhanced the exchange ID selection (depending upon request type and exchange responder)
         Made necessary enhancement in EMA selection algorithm.

Technical Notes: N/A

Signed-off-by: Kiran Patil <kiran.patil@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_exch.c |   64 ++++++++++++++++++++++++++++--------------
 1 files changed, 43 insertions(+), 21 deletions(-)

diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index a3d6402..08bf5fa 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -2281,16 +2281,45 @@ void fc_exch_mgr_free(struct fc_lport *lport)
 EXPORT_SYMBOL(fc_exch_mgr_free);
 
 /**
+ * fc_find_ema() - Lookup and return appropriate Exchange Manager Anchor depending
+ * upon 'xid'.
+ * @f_ctl: f_ctl
+ * @lport: The local port the frame was received on
+ * @fh: The received frame header
+ */
+static struct fc_exch_mgr_anchor *fc_find_ema(u32 f_ctl,
+					      struct fc_lport *lport,
+					      struct fc_frame_header *fh)
+{
+	struct fc_exch_mgr_anchor *ema;
+	u16 xid;
+
+	if (f_ctl & FC_FC_EX_CTX)
+		xid = ntohs(fh->fh_ox_id);
+	else {
+		xid = ntohs(fh->fh_rx_id);
+		if (xid == FC_XID_UNKNOWN)
+			return list_entry(lport->ema_list.prev,
+					  typeof(*ema), ema_list);
+	}
+
+	list_for_each_entry(ema, &lport->ema_list, ema_list) {
+		if ((xid >= ema->mp->min_xid) &&
+		    (xid <= ema->mp->max_xid))
+			return ema;
+	}
+	return NULL;
+}
+/**
  * fc_exch_recv() - Handler for received frames
  * @lport: The local port the frame was received on
- * @fp:	   The received frame
+ * @fp:	The received frame
  */
 void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)
 {
 	struct fc_frame_header *fh = fc_frame_header_get(fp);
 	struct fc_exch_mgr_anchor *ema;
-	u32 f_ctl, found = 0;
-	u16 oxid;
+	u32 f_ctl;
 
 	/* lport lock ? */
 	if (!lport || lport->state == LPORT_ST_DISABLED) {
@@ -2301,24 +2330,17 @@ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)
 	}
 
 	f_ctl = ntoh24(fh->fh_f_ctl);
-	oxid = ntohs(fh->fh_ox_id);
-	if (f_ctl & FC_FC_EX_CTX) {
-		list_for_each_entry(ema, &lport->ema_list, ema_list) {
-			if ((oxid >= ema->mp->min_xid) &&
-			    (oxid <= ema->mp->max_xid)) {
-				found = 1;
-				break;
-			}
-		}
-
-		if (!found) {
-			FC_LPORT_DBG(lport, "Received response for out "
-				     "of range oxid:%hx\n", oxid);
-			fc_frame_free(fp);
-			return;
-		}
-	} else
-		ema = list_entry(lport->ema_list.prev, typeof(*ema), ema_list);
+	ema = fc_find_ema(f_ctl, lport, fh);
+	if (!ema) {
+		FC_LPORT_DBG(lport, "Unable to find Exchange Manager Anchor,"
+				    "fc_ctl <0x%x>, xid <0x%x>\n",
+				     f_ctl,
+				     (f_ctl & FC_FC_EX_CTX) ?
+				     ntohs(fh->fh_ox_id) :
+				     ntohs(fh->fh_rx_id));
+		fc_frame_free(fp);
+		return;
+	}
 
 	/*
 	 * If frame is marked invalid, just drop it.


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

* [PATCH 18/28] libfcoe: move logging macros into the local libfcoe.h header file
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (16 preceding siblings ...)
  2011-01-29  0:04 ` [PATCH 17/28] libfc: Enhanced exchange ID selection mechanism and fix related EMA selection logic Robert Love
@ 2011-01-29  0:04 ` Robert Love
  2011-01-29  0:04 ` [PATCH 19/28] libfcoe: add fcoe_transport structure defines to include/scsi/libfcoe.h Robert Love
                   ` (9 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:04 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Yi Zou

From: Yi Zou <yi.zou@intel.com>

libfcoe kernel module debug macros will used by the fcoe transport code
as well when later it gets added.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/libfcoe.c |   22 ++--------------------
 drivers/scsi/fcoe/libfcoe.h |   25 +++++++++++++++++++++++++
 2 files changed, 27 insertions(+), 20 deletions(-)
 create mode 100644 drivers/scsi/fcoe/libfcoe.h

diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 625c6be..a4757ca 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -44,6 +44,8 @@
 #include <scsi/libfc.h>
 #include <scsi/libfcoe.h>
 
+#include "libfcoe.h"
+
 MODULE_AUTHOR("Open-FCoE.org");
 MODULE_DESCRIPTION("FIP discovery protocol support for FCoE HBAs");
 MODULE_LICENSE("GPL v2");
@@ -70,26 +72,6 @@ unsigned int libfcoe_debug_logging;
 module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
 
-#define LIBFCOE_LOGGING	    0x01 /* General logging, not categorized */
-#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */
-
-#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD)		\
-do {							\
-	if (unlikely(libfcoe_debug_logging & LEVEL))	\
-		do {					\
-			CMD;				\
-		} while (0);				\
-} while (0)
-
-#define LIBFCOE_DBG(fmt, args...)					\
-	LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING,				\
-			      printk(KERN_INFO "libfcoe: " fmt, ##args);)
-
-#define LIBFCOE_FIP_DBG(fip, fmt, args...)				\
-	LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING,			\
-			      printk(KERN_INFO "host%d: fip: " fmt, 	\
-				     (fip)->lp->host->host_no, ##args);)
-
 static const char *fcoe_ctlr_states[] = {
 	[FIP_ST_DISABLED] =	"DISABLED",
 	[FIP_ST_LINK_WAIT] =	"LINK_WAIT",
diff --git a/drivers/scsi/fcoe/libfcoe.h b/drivers/scsi/fcoe/libfcoe.h
new file mode 100644
index 0000000..c3fe316
--- /dev/null
+++ b/drivers/scsi/fcoe/libfcoe.h
@@ -0,0 +1,25 @@
+#ifndef _FCOE_LIBFCOE_H_
+#define _FCOE_LIBFCOE_H_
+
+extern unsigned int libfcoe_debug_logging;
+#define LIBFCOE_LOGGING	    0x01 /* General logging, not categorized */
+#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */
+
+#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD)		\
+do {							\
+	if (unlikely(libfcoe_debug_logging & LEVEL))	\
+		do {					\
+			CMD;				\
+		} while (0);				\
+} while (0)
+
+#define LIBFCOE_DBG(fmt, args...)					\
+	LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING,				\
+			      printk(KERN_INFO "libfcoe: " fmt, ##args);)
+
+#define LIBFCOE_FIP_DBG(fip, fmt, args...)				\
+	LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING,			\
+			      printk(KERN_INFO "host%d: fip: " fmt,	\
+				     (fip)->lp->host->host_no, ##args);)
+
+#endif /* _FCOE_LIBFCOE_H_ */


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

* [PATCH 19/28] libfcoe: add fcoe_transport structure defines to include/scsi/libfcoe.h
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (17 preceding siblings ...)
  2011-01-29  0:04 ` [PATCH 18/28] libfcoe: move logging macros into the local libfcoe.h header file Robert Love
@ 2011-01-29  0:04 ` Robert Love
  2011-01-29  0:04 ` [PATCH 20/28] libfcoe: add implementation to support fcoe transport Robert Love
                   ` (8 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:04 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Bhanu Prakash Gollapudi, Yi Zou

From: Yi Zou <yi.zou@intel.com>

add the fcoe_transport struct to the common libfcoe.h header so all fcoe
transport provides can use it to attach itself as an fcoe transport. This
is the header part, and the next patch will be the transport code itself.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 include/scsi/libfcoe.h |   48 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 48 insertions(+), 0 deletions(-)

diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index feb6a94..efb6ae5 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -231,5 +231,53 @@ static inline bool is_fip_mode(struct fcoe_ctlr *fip)
 	return fip->state == FIP_ST_ENABLED;
 }
 
+/* helper for FCoE SW HBA drivers, can include subven and subdev if needed. The
+ * modpost would use pci_device_id table to auto-generate formatted module alias
+ * into the corresponding .mod.c file, but there may or may not be a pci device
+ * id table for FCoE drivers so we use the following helper for build the fcoe
+ * driver module alias.
+ */
+#define MODULE_ALIAS_FCOE_PCI(ven, dev) \
+	MODULE_ALIAS("fcoe-pci:"	\
+		"v" __stringify(ven)	\
+		"d" __stringify(dev) "sv*sd*bc*sc*i*")
+
+/* the name of the default FCoE transport driver fcoe.ko */
+#define FCOE_TRANSPORT_DEFAULT	"fcoe"
+
+/* struct fcoe_transport - The FCoE transport interface
+ * @name:	a vendor specific name for their FCoE transport driver
+ * @attached:	whether this transport is already attached
+ * @list:	list linkage to all attached transports
+ * @match:	handler to allow the transport driver to match up a given netdev
+ * @create:	handler to sysfs entry of create for FCoE instances
+ * @destroy:	handler to sysfs entry of destroy for FCoE instances
+ * @enable:	handler to sysfs entry of enable for FCoE instances
+ * @disable:	handler to sysfs entry of disable for FCoE instances
+ */
+struct fcoe_transport {
+	char name[IFNAMSIZ];
+	bool attached;
+	struct list_head list;
+	bool (*match) (struct net_device *device);
+	int (*create) (struct net_device *device, enum fip_state fip_mode);
+	int (*destroy) (struct net_device *device);
+	int (*enable) (struct net_device *device);
+	int (*disable) (struct net_device *device);
+};
+
+/**
+ * struct netdev_list
+ * A mapping from netdevice to fcoe_transport
+ */
+struct fcoe_netdev_mapping {
+	struct list_head list;
+	struct net_device *netdev;
+	struct fcoe_transport *ft;
+};
+
+/* fcoe transports registration and deregistration */
+int fcoe_transport_attach(struct fcoe_transport *ft);
+int fcoe_transport_detach(struct fcoe_transport *ft);
 
 #endif /* _LIBFCOE_H */


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

* [PATCH 20/28] libfcoe: add implementation to support fcoe transport
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (18 preceding siblings ...)
  2011-01-29  0:04 ` [PATCH 19/28] libfcoe: add fcoe_transport structure defines to include/scsi/libfcoe.h Robert Love
@ 2011-01-29  0:04 ` Robert Love
  2011-02-02  6:43   ` Mike Christie
  2011-01-29  0:05 ` [PATCH 21/28] libfcoe: rename libfcoe.c to fcoe_cltr.c for the coming fcoe_transport.c Robert Love
                   ` (7 subsequent siblings)
  27 siblings, 1 reply; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:04 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Bhanu Prakash Gollapudi, Yi Zou

From: Yi Zou <yi.zou@intel.com>

Add the new fcoe_transport.c file that implements basic fcoe transport
interface. Eventually, the sysfs entries to create/destroy/enable/disable
an FCoE instance will be coming to the fcoe transport layer, who does a
look-up to find the corresponding transport provide and pass the corresponding
action over to the identified provider.

The fcoe.ko will become the default fcoe transport provider that can support
FCoE on any given netdev interfaces, as the Open-FCoE.org's default software
FCoE HBA solution. Any vendor specific FCoE HBA driver that is built on top
of Open-FCoE's kernel stack of libfc & libfcoe as well as the user land tool
of fcoe-utils can easily plug-in and start running FCoE on their network
interfaces. The fcoe.ko will be converted to act as the default provider if
no vendor specific transport provider is found, as it is always added to the
very end of the list of attached transports.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe_transport.c |  518 ++++++++++++++++++++++++++++++++++++
 drivers/scsi/fcoe/libfcoe.h        |    6 
 2 files changed, 524 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/fcoe/fcoe_transport.c

diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
new file mode 100644
index 0000000..41d69be
--- /dev/null
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright(c) 2008 - 2011 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/types.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/errno.h>
+#include <scsi/libfcoe.h>
+
+#include "libfcoe.h"
+
+static int fcoe_transport_create(const char *, struct kernel_param *);
+static int fcoe_transport_destroy(const char *, struct kernel_param *);
+static int fcoe_transport_show(char *buffer, const struct kernel_param *kp);
+static struct fcoe_transport *fcoe_transport_lookup(struct net_device *device);
+static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *device);
+static int fcoe_transport_enable(const char *, struct kernel_param *);
+static int fcoe_transport_disable(const char *, struct kernel_param *);
+
+static LIST_HEAD(fcoe_transports);
+static LIST_HEAD(fcoe_netdevs);
+static DEFINE_MUTEX(ft_mutex);
+
+module_param_call(show, NULL, fcoe_transport_show, NULL, S_IRUSR);
+__MODULE_PARM_TYPE(show, "string");
+MODULE_PARM_DESC(show, " Show attached FCoE transports");
+
+module_param_call(create, fcoe_transport_create, NULL,
+		  (void *)FIP_MODE_FABRIC, S_IWUSR);
+__MODULE_PARM_TYPE(create, "string");
+MODULE_PARM_DESC(create, " Creates fcoe instance on a ethernet interface");
+
+module_param_call(create_vn2vn, fcoe_transport_create, NULL,
+		  (void *)FIP_MODE_VN2VN, S_IWUSR);
+__MODULE_PARM_TYPE(create_vn2vn, "string");
+MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance "
+		 "on an Ethernet interface");
+
+module_param_call(destroy, fcoe_transport_destroy, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(destroy, "string");
+MODULE_PARM_DESC(destroy, " Destroys fcoe instance on a ethernet interface");
+
+module_param_call(enable, fcoe_transport_enable, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(enable, "string");
+MODULE_PARM_DESC(enable, " Enables fcoe on a ethernet interface.");
+
+module_param_call(disable, fcoe_transport_disable, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(disable, "string");
+MODULE_PARM_DESC(disable, " Disables fcoe on a ethernet interface.");
+
+/**
+ * fcoe_transport_lookup - find an fcoe transport that matches a netdev
+ * @netdev: The netdev to look for from all attached transports
+ *
+ * Returns : ptr to the fcoe transport that supports this netdev or NULL
+ * if not found.
+ *
+ * The ft_mutex should be held when this is called
+ */
+static struct fcoe_transport *fcoe_transport_lookup(struct net_device *netdev)
+{
+	struct fcoe_transport *ft = NULL;
+
+	list_for_each_entry(ft, &fcoe_transports, list)
+		if (ft->match && ft->match(netdev))
+			return ft;
+	return NULL;
+}
+
+/**
+ * fcoe_transport_attach - Attaches an FCoE transport
+ * @ft: The fcoe transport to be attached
+ *
+ * Returns : 0 for success
+ */
+int fcoe_transport_attach(struct fcoe_transport *ft)
+{
+	int rc = 0;
+
+	mutex_lock(&ft_mutex);
+	if (ft->attached) {
+		LIBFCOE_TRANSPORT_DBG("transport %s already attached\n",
+				       ft->name);
+		rc = -EEXIST;
+		goto out_attach;
+	}
+
+	/* Add default transport to the tail */
+	if (strcmp(ft->name, FCOE_TRANSPORT_DEFAULT))
+		list_add(&ft->list, &fcoe_transports);
+	else
+		list_add_tail(&ft->list, &fcoe_transports);
+
+	ft->attached = true;
+	LIBFCOE_TRANSPORT_DBG("attaching transport %s\n", ft->name);
+
+out_attach:
+	mutex_unlock(&ft_mutex);
+	return rc;
+}
+EXPORT_SYMBOL(fcoe_transport_attach);
+
+/**
+ * fcoe_transport_attach - Detaches an FCoE transport
+ * @ft: The fcoe transport to be attached
+ *
+ * Returns : 0 for success
+ */
+int fcoe_transport_detach(struct fcoe_transport *ft)
+{
+	int rc = 0;
+
+	mutex_lock(&ft_mutex);
+	if (!ft->attached) {
+		LIBFCOE_TRANSPORT_DBG("transport %s already detached\n",
+			ft->name);
+		rc = -ENODEV;
+		goto out_attach;
+	}
+
+	list_del(&ft->list);
+	ft->attached = false;
+	LIBFCOE_TRANSPORT_DBG("detaching transport %s\n", ft->name);
+
+out_attach:
+	mutex_unlock(&ft_mutex);
+	return rc;
+
+}
+EXPORT_SYMBOL(fcoe_transport_detach);
+
+static int fcoe_transport_show(char *buffer, const struct kernel_param *kp)
+{
+	int i, j;
+	struct fcoe_transport *ft = NULL;
+
+	i = j = sprintf(buffer, "Attached FCoE transports:");
+	mutex_lock(&ft_mutex);
+	list_for_each_entry(ft, &fcoe_transports, list) {
+		i += snprintf(&buffer[i], IFNAMSIZ, "%s ", ft->name);
+		if (i >= PAGE_SIZE)
+			break;
+	}
+	mutex_unlock(&ft_mutex);
+	if (i == j)
+		i += snprintf(&buffer[i], IFNAMSIZ, "none");
+	return i;
+}
+
+static int __init fcoe_transport_init(void)
+{
+	return 0;
+}
+
+static int __exit fcoe_transport_exit(void)
+{
+	struct fcoe_transport *ft;
+
+	mutex_lock(&ft_mutex);
+	list_for_each_entry(ft, &fcoe_transports, list)
+		printk(KERN_ERR "FCoE transport %s is still attached!\n",
+		      ft->name);
+	mutex_unlock(&ft_mutex);
+	return 0;
+}
+
+
+static int fcoe_add_netdev_mapping(struct net_device *netdev,
+					struct fcoe_transport *ft)
+{
+	struct fcoe_netdev_mapping *nm;
+
+	nm = kmalloc(sizeof(*nm), GFP_KERNEL);
+	if (!nm) {
+		printk(KERN_ERR "Unable to allocate netdev_mapping");
+		return -ENOMEM;
+	}
+
+	nm->netdev = netdev;
+	nm->ft = ft;
+
+	list_add(&nm->list, &fcoe_netdevs);
+	return 0;
+}
+
+
+static void fcoe_del_netdev_mapping(struct net_device *netdev)
+{
+	struct fcoe_netdev_mapping *nm = NULL, *tmp;
+
+	list_for_each_entry_safe(nm, tmp, &fcoe_netdevs, list) {
+		if (nm->netdev == netdev) {
+			list_del(&nm->list);
+			kfree(nm);
+			return;
+		}
+	}
+}
+
+
+/**
+ * fcoe_netdev_map_lookup - find the fcoe transport that matches the netdev on which
+ * it was created
+ *
+ * Returns : ptr to the fcoe transport that supports this netdev or NULL
+ * if not found.
+ *
+ * The ft_mutex should be held when this is called
+ */
+static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *netdev)
+{
+	struct fcoe_transport *ft = NULL;
+	struct fcoe_netdev_mapping *nm;
+
+	list_for_each_entry(nm, &fcoe_netdevs, list) {
+		if (netdev == nm->netdev) {
+			ft = nm->ft;
+			return ft;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * fcoe_if_to_netdev() - Parse a name buffer to get a net device
+ * @buffer: The name of the net device
+ *
+ * Returns: NULL or a ptr to net_device
+ */
+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_transport_create() - Create a fcoe interface
+ * @buffer: The name of the Ethernet interface to create on
+ * @kp:	    The associated kernel param
+ *
+ * Called from sysfs. This holds the ft_mutex while calling the
+ * registered fcoe transport's create function.
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_transport_create(const char *buffer, struct kernel_param *kp)
+{
+	int rc = -ENODEV;
+	struct net_device *netdev = NULL;
+	struct fcoe_transport *ft = NULL;
+	enum fip_state fip_mode = (enum fip_state)(long)kp->arg;
+
+	if (!mutex_trylock(&ft_mutex))
+		return restart_syscall();
+
+#ifdef CONFIG_LIBFCOE_MODULE
+	/*
+	 * Make sure the module has been initialized, and is not about to be
+	 * removed.  Module parameter sysfs files are writable before the
+	 * module_init function is called and after module_exit.
+	 */
+	if (THIS_MODULE->state != MODULE_STATE_LIVE)
+		goto out_nodev;
+#endif
+
+	netdev = fcoe_if_to_netdev(buffer);
+	if (!netdev) {
+		LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buffer);
+		goto out_nodev;
+	}
+
+	ft = fcoe_netdev_map_lookup(netdev);
+	if (ft) {
+		LIBFCOE_TRANSPORT_DBG("transport %s already has existing "
+				      "FCoE instance on %s.\n",
+				      ft->name, netdev->name);
+		rc = -EEXIST;
+		goto out_putdev;
+	}
+
+	ft = fcoe_transport_lookup(netdev);
+	if (!ft) {
+		LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n",
+				      netdev->name);
+		goto out_putdev;
+	}
+
+	rc = fcoe_add_netdev_mapping(netdev, ft);
+	if (rc) {
+		LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping "
+				      "for FCoE transport %s for %s.\n",
+				      ft->name, netdev->name);
+		goto out_putdev;
+	}
+
+	/* pass to transport create */
+	rc = ft->create ? ft->create(netdev, fip_mode) : -ENODEV;
+	if (rc)
+		fcoe_del_netdev_mapping(netdev);
+
+	LIBFCOE_TRANSPORT_DBG("transport %s %s to create fcoe on %s.\n",
+			      ft->name, (rc) ? "failed" : "succeeded",
+			      netdev->name);
+
+out_putdev:
+	dev_put(netdev);
+out_nodev:
+	mutex_unlock(&ft_mutex);
+	if (rc == -ERESTARTSYS)
+		return restart_syscall();
+	else
+		return rc;
+}
+
+/**
+ * fcoe_transport_destroy() - Destroy a FCoE interface
+ * @buffer: The name of the Ethernet interface to be destroyed
+ * @kp:	    The associated kernel parameter
+ *
+ * Called from sysfs. This holds the ft_mutex while calling the
+ * registered fcoe transport's destroy function.
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp)
+{
+	int rc = -ENODEV;
+	struct net_device *netdev = NULL;
+	struct fcoe_transport *ft = NULL;
+
+	if (!mutex_trylock(&ft_mutex))
+		return restart_syscall();
+
+#ifdef CONFIG_LIBFCOE_MODULE
+	/*
+	 * Make sure the module has been initialized, and is not about to be
+	 * removed.  Module parameter sysfs files are writable before the
+	 * module_init function is called and after module_exit.
+	 */
+	if (THIS_MODULE->state != MODULE_STATE_LIVE)
+		goto out_nodev;
+#endif
+
+	netdev = fcoe_if_to_netdev(buffer);
+	if (!netdev) {
+		LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buffer);
+		goto out_nodev;
+	}
+
+	ft = fcoe_netdev_map_lookup(netdev);
+	if (!ft) {
+		LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n",
+				      netdev->name);
+		goto out_putdev;
+	}
+
+	/* pass to transport destroy */
+	rc = ft->destroy ? ft->destroy(netdev) : -ENODEV;
+	fcoe_del_netdev_mapping(netdev);
+	LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n",
+			      ft->name, (rc) ? "failed" : "succeeded",
+			      netdev->name);
+
+out_putdev:
+	dev_put(netdev);
+out_nodev:
+	mutex_unlock(&ft_mutex);
+
+	if (rc == -ERESTARTSYS)
+		return restart_syscall();
+	else
+		return rc;
+}
+
+/**
+ * fcoe_transport_disable() - Disables a FCoE interface
+ * @buffer: The name of the Ethernet interface to be disabled
+ * @kp:	    The associated kernel parameter
+ *
+ * Called from sysfs.
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp)
+{
+	int rc = -ENODEV;
+	struct net_device *netdev = NULL;
+	struct fcoe_transport *ft = NULL;
+
+	if (!mutex_trylock(&ft_mutex))
+		return restart_syscall();
+
+#ifdef CONFIG_LIBFCOE_MODULE
+	/*
+	 * Make sure the module has been initialized, and is not about to be
+	 * removed.  Module parameter sysfs files are writable before the
+	 * module_init function is called and after module_exit.
+	 */
+	if (THIS_MODULE->state != MODULE_STATE_LIVE)
+		goto out_nodev;
+#endif
+
+	netdev = fcoe_if_to_netdev(buffer);
+	if (!netdev)
+		goto out_nodev;
+
+	ft = fcoe_netdev_map_lookup(netdev);
+	if (!ft)
+		goto out_putdev;
+
+	rc = ft->disable ? ft->disable(netdev) : -ENODEV;
+
+out_putdev:
+	dev_put(netdev);
+out_nodev:
+	mutex_unlock(&ft_mutex);
+
+	if (rc == -ERESTARTSYS)
+		return restart_syscall();
+	else
+		return rc;
+}
+
+/**
+ * fcoe_transport_enable() - Enables a FCoE interface
+ * @buffer: The name of the Ethernet interface to be enabled
+ * @kp:     The associated kernel parameter
+ *
+ * Called from sysfs.
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp)
+{
+	int rc = -ENODEV;
+	struct net_device *netdev = NULL;
+	struct fcoe_transport *ft = NULL;
+
+	if (!mutex_trylock(&ft_mutex))
+		return restart_syscall();
+
+#ifdef CONFIG_LIBFCOE_MODULE
+	/*
+	 * Make sure the module has been initialized, and is not about to be
+	 * removed.  Module parameter sysfs files are writable before the
+	 * module_init function is called and after module_exit.
+	 */
+	if (THIS_MODULE->state != MODULE_STATE_LIVE)
+		goto out_nodev;
+#endif
+
+	netdev = fcoe_if_to_netdev(buffer);
+	if (!netdev)
+		goto out_nodev;
+
+	ft = fcoe_netdev_map_lookup(netdev);
+	if (!ft)
+		goto out_putdev;
+
+	rc = ft->enable ? ft->enable(netdev) : -ENODEV;
+
+out_putdev:
+	dev_put(netdev);
+out_nodev:
+	mutex_unlock(&ft_mutex);
+	if (rc == -ERESTARTSYS)
+		return restart_syscall();
+	else
+		return rc;
+}
+
+/**
+ * libfcoe_init() - Initialization routine for libfcoe.ko
+ */
+static int __init libfcoe_init(void)
+{
+	fcoe_transport_init();
+
+	return 0;
+}
+module_init(libfcoe_init);
+
+/**
+ * libfcoe_exit() - Tear down libfcoe.ko
+ */
+static void __exit libfcoe_exit(void)
+{
+	fcoe_transport_exit();
+}
+module_exit(libfcoe_exit);
diff --git a/drivers/scsi/fcoe/libfcoe.h b/drivers/scsi/fcoe/libfcoe.h
index c3fe316..6af5fc3 100644
--- a/drivers/scsi/fcoe/libfcoe.h
+++ b/drivers/scsi/fcoe/libfcoe.h
@@ -4,6 +4,7 @@
 extern unsigned int libfcoe_debug_logging;
 #define LIBFCOE_LOGGING	    0x01 /* General logging, not categorized */
 #define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */
+#define LIBFCOE_TRANSPORT_LOGGING	0x04 /* FCoE transport logging */
 
 #define LIBFCOE_CHECK_LOGGING(LEVEL, CMD)		\
 do {							\
@@ -22,4 +23,9 @@ do {							\
 			      printk(KERN_INFO "host%d: fip: " fmt,	\
 				     (fip)->lp->host->host_no, ##args);)
 
+#define LIBFCOE_TRANSPORT_DBG(fmt, args...)				\
+	LIBFCOE_CHECK_LOGGING(LIBFCOE_TRANSPORT_LOGGING,		\
+			      printk(KERN_INFO "%s: " fmt,		\
+				     __func__, ##args);)
+
 #endif /* _FCOE_LIBFCOE_H_ */


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

* [PATCH 21/28] libfcoe: rename libfcoe.c to fcoe_cltr.c for the coming fcoe_transport.c
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (19 preceding siblings ...)
  2011-01-29  0:04 ` [PATCH 20/28] libfcoe: add implementation to support fcoe transport Robert Love
@ 2011-01-29  0:05 ` Robert Love
  2011-01-29  0:05 ` [PATCH 22/28] libfcoe: include fcoe_transport.c into kernel libfcoe module Robert Love
                   ` (6 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:05 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Yi Zou

From: Yi Zou <yi.zou@intel.com>

The existing libfcoe.c is mostly for FIP support, rename it to reflect that
fact and so we can add fcoe_transport.c to the make file to include both
into the libfcoe kernel module.

[ Minor modifications by Robert Love converting a few
  "__attribute__((packed))" modifiers to "__packed" to remove new
  checkpatch.pl WARNINGS ]

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/Makefile    |    2 
 drivers/scsi/fcoe/fcoe_ctlr.c | 2690 +++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/fcoe/libfcoe.c   | 2690 -----------------------------------------
 3 files changed, 2692 insertions(+), 2690 deletions(-)
 create mode 100644 drivers/scsi/fcoe/fcoe_ctlr.c
 delete mode 100644 drivers/scsi/fcoe/libfcoe.c

diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index 950f276..b122b7a 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -1,2 +1,4 @@
 obj-$(CONFIG_FCOE) += fcoe.o
 obj-$(CONFIG_LIBFCOE) += libfcoe.o
+
+libfcoe-objs := fcoe_ctlr.o
diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c
new file mode 100644
index 0000000..c12d0a7
--- /dev/null
+++ b/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -0,0 +1,2690 @@
+/*
+ * 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,
+ * 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/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/errno.h>
+#include <linux/bitops.h>
+#include <linux/slab.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/fc/fc_fcp.h>
+
+#include <scsi/libfc.h>
+#include <scsi/libfcoe.h>
+
+#include "libfcoe.h"
+
+MODULE_AUTHOR("Open-FCoE.org");
+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_timer_work(struct work_struct *);
+static void fcoe_ctlr_recv_work(struct work_struct *);
+static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *);
+
+static void fcoe_ctlr_vn_start(struct fcoe_ctlr *);
+static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *, struct sk_buff *);
+static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *);
+static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *, u32, u8 *);
+
+static u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS;
+static u8 fcoe_all_enode[ETH_ALEN] = FIP_ALL_ENODE_MACS;
+static u8 fcoe_all_vn2vn[ETH_ALEN] = FIP_ALL_VN2VN_MACS;
+static u8 fcoe_all_p2p[ETH_ALEN] = FIP_ALL_P2P_MACS;
+
+unsigned int libfcoe_debug_logging;
+module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+
+static const char * const fcoe_ctlr_states[] = {
+	[FIP_ST_DISABLED] =	"DISABLED",
+	[FIP_ST_LINK_WAIT] =	"LINK_WAIT",
+	[FIP_ST_AUTO] =		"AUTO",
+	[FIP_ST_NON_FIP] =	"NON_FIP",
+	[FIP_ST_ENABLED] =	"ENABLED",
+	[FIP_ST_VNMP_START] =	"VNMP_START",
+	[FIP_ST_VNMP_PROBE1] =	"VNMP_PROBE1",
+	[FIP_ST_VNMP_PROBE2] =	"VNMP_PROBE2",
+	[FIP_ST_VNMP_CLAIM] =	"VNMP_CLAIM",
+	[FIP_ST_VNMP_UP] =	"VNMP_UP",
+};
+
+static const char *fcoe_ctlr_state(enum fip_state state)
+{
+	const char *cp = "unknown";
+
+	if (state < ARRAY_SIZE(fcoe_ctlr_states))
+		cp = fcoe_ctlr_states[state];
+	if (!cp)
+		cp = "unknown";
+	return cp;
+}
+
+/**
+ * fcoe_ctlr_set_state() - Set and do debug printing for the new FIP state.
+ * @fip: The FCoE controller
+ * @state: The new state
+ */
+static void fcoe_ctlr_set_state(struct fcoe_ctlr *fip, enum fip_state state)
+{
+	if (state == fip->state)
+		return;
+	if (fip->lp)
+		LIBFCOE_FIP_DBG(fip, "state %s -> %s\n",
+			fcoe_ctlr_state(fip->state), fcoe_ctlr_state(state));
+	fip->state = state;
+}
+
+/**
+ * fcoe_ctlr_mtu_valid() - Check if a FCF's MTU is valid
+ * @fcf: The FCF to check
+ *
+ * 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;
+}
+
+/**
+ * fcoe_ctlr_fcf_usable() - Check if a FCF is usable
+ * @fcf: The FCF to check
+ *
+ * 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_map_dest() - Set flag and OUI for mapping destination addresses
+ * @fip: The FCoE controller
+ */
+static void fcoe_ctlr_map_dest(struct fcoe_ctlr *fip)
+{
+	if (fip->mode == FIP_MODE_VN2VN)
+		hton24(fip->dest_addr, FIP_VN_FC_MAP);
+	else
+		hton24(fip->dest_addr, FIP_DEF_FC_MAP);
+	hton24(fip->dest_addr + 3, 0);
+	fip->map_dest = 1;
+}
+
+/**
+ * fcoe_ctlr_init() - Initialize the FCoE Controller instance
+ * @fip: The FCoE controller to initialize
+ */
+void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode)
+{
+	fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
+	fip->mode = mode;
+	INIT_LIST_HEAD(&fip->fcfs);
+	mutex_init(&fip->ctlr_mutex);
+	spin_lock_init(&fip->ctlr_lock);
+	fip->flogi_oxid = FC_XID_UNKNOWN;
+	setup_timer(&fip->timer, fcoe_ctlr_timeout, (unsigned long)fip);
+	INIT_WORK(&fip->timer_work, fcoe_ctlr_timer_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: The FCoE controller whose FCFs are to be reset
+ *
+ * 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_ctlr_destroy() - Disable and tear down a FCoE controller
+ * @fip: The FCoE controller to tear down
+ *
+ * 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)
+{
+	cancel_work_sync(&fip->recv_work);
+	skb_queue_purge(&fip->fip_recv_list);
+
+	mutex_lock(&fip->ctlr_mutex);
+	fcoe_ctlr_set_state(fip, FIP_ST_DISABLED);
+	fcoe_ctlr_reset_fcfs(fip);
+	mutex_unlock(&fip->ctlr_mutex);
+	del_timer_sync(&fip->timer);
+	cancel_work_sync(&fip->timer_work);
+}
+EXPORT_SYMBOL(fcoe_ctlr_destroy);
+
+/**
+ * fcoe_ctlr_announce() - announce new FCF selection
+ * @fip: The FCoE controller
+ *
+ * Also sets the destination MAC for FCoE and control packets
+ *
+ * Called with neither ctlr_mutex nor ctlr_lock held.
+ */
+static void fcoe_ctlr_announce(struct fcoe_ctlr *fip)
+{
+	struct fcoe_fcf *sel;
+	struct fcoe_fcf *fcf;
+
+	mutex_lock(&fip->ctlr_mutex);
+	spin_lock_bh(&fip->ctlr_lock);
+
+	kfree_skb(fip->flogi_req);
+	fip->flogi_req = NULL;
+	list_for_each_entry(fcf, &fip->fcfs, list)
+		fcf->flogi_sent = 0;
+
+	spin_unlock_bh(&fip->ctlr_lock);
+	sel = fip->sel_fcf;
+
+	if (sel && !compare_ether_addr(sel->fcf_mac, fip->dest_addr))
+		goto unlock;
+	if (!is_zero_ether_addr(fip->dest_addr)) {
+		printk(KERN_NOTICE "libfcoe: host%d: "
+		       "FIP Fibre-Channel Forwarder MAC %pM deselected\n",
+		       fip->lp->host->host_no, fip->dest_addr);
+		memset(fip->dest_addr, 0, ETH_ALEN);
+	}
+	if (sel) {
+		printk(KERN_INFO "libfcoe: host%d: FIP selected "
+		       "Fibre-Channel Forwarder MAC %pM\n",
+		       fip->lp->host->host_no, sel->fcf_mac);
+		memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
+		fip->map_dest = 0;
+	}
+unlock:
+	mutex_unlock(&fip->ctlr_mutex);
+}
+
+/**
+ * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port
+ * @fip: The FCoE controller to get the maximum FCoE size from
+ *
+ * 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 FIP solicitation
+ * @fip: The FCoE controller to send the solicitation on
+ * @fcf: The 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;
+		} __packed desc;
+	}  __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);
+	if (fip->spma)
+		sol->fip.fip_flags |= htons(FIP_FL_SPMA);
+
+	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_FIP);
+	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: The FCoE controller to start
+ *
+ * Called from the LLD when the network link is ready.
+ */
+void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
+{
+	mutex_lock(&fip->ctlr_mutex);
+	if (fip->state == FIP_ST_NON_FIP || fip->state == FIP_ST_AUTO) {
+		mutex_unlock(&fip->ctlr_mutex);
+		fc_linkup(fip->lp);
+	} else if (fip->state == FIP_ST_LINK_WAIT) {
+		fcoe_ctlr_set_state(fip, fip->mode);
+		switch (fip->mode) {
+		default:
+			LIBFCOE_FIP_DBG(fip, "invalid mode %d\n", fip->mode);
+			/* fall-through */
+		case FIP_MODE_AUTO:
+			LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n");
+			/* fall-through */
+		case FIP_MODE_FABRIC:
+		case FIP_MODE_NON_FIP:
+			mutex_unlock(&fip->ctlr_mutex);
+			fc_linkup(fip->lp);
+			fcoe_ctlr_solicit(fip, NULL);
+			break;
+		case FIP_MODE_VN2VN:
+			fcoe_ctlr_vn_start(fip);
+			mutex_unlock(&fip->ctlr_mutex);
+			fc_linkup(fip->lp);
+			break;
+		}
+	} else
+		mutex_unlock(&fip->ctlr_mutex);
+}
+EXPORT_SYMBOL(fcoe_ctlr_link_up);
+
+/**
+ * fcoe_ctlr_reset() - Reset a FCoE controller
+ * @fip:       The FCoE controller to reset
+ */
+static void fcoe_ctlr_reset(struct fcoe_ctlr *fip)
+{
+	fcoe_ctlr_reset_fcfs(fip);
+	del_timer(&fip->timer);
+	fip->ctlr_ka_time = 0;
+	fip->port_ka_time = 0;
+	fip->sol_time = 0;
+	fip->flogi_oxid = FC_XID_UNKNOWN;
+	fcoe_ctlr_map_dest(fip);
+}
+
+/**
+ * fcoe_ctlr_link_down() - Stop a FCoE controller
+ * @fip: The FCoE controller to be stopped
+ *
+ * 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)
+{
+	int link_dropped;
+
+	LIBFCOE_FIP_DBG(fip, "link down.\n");
+	mutex_lock(&fip->ctlr_mutex);
+	fcoe_ctlr_reset(fip);
+	link_dropped = fip->state != FIP_ST_LINK_WAIT;
+	fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
+	mutex_unlock(&fip->ctlr_mutex);
+
+	if (link_dropped)
+		fc_linkdown(fip->lp);
+	return link_dropped;
+}
+EXPORT_SYMBOL(fcoe_ctlr_link_down);
+
+/**
+ * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF
+ * @fip:   The FCoE controller to send the FKA on
+ * @lport: libfc fc_lport to send from
+ * @ports: 0 for controller keep-alive, 1 for port keep-alive
+ * @sa:	   The 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,
+				      struct fc_lport *lport,
+				      int ports, u8 *sa)
+{
+	struct sk_buff *skb;
+	struct fip_kal {
+		struct ethhdr eth;
+		struct fip_header fip;
+		struct fip_mac_desc mac;
+	} __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 || (ports && !lp->port_id))
+		return;
+
+	len = sizeof(*kal) + ports * 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);
+	if (fip->spma)
+		kal->fip.fip_flags |= htons(FIP_FL_SPMA);
+
+	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->get_src_addr(lport), ETH_ALEN);
+		hton24(vn->fd_fc_id, lport->port_id);
+		put_unaligned_be64(lport->wwpn, &vn->fd_wwpn);
+	}
+	skb_put(skb, len);
+	skb->protocol = htons(ETH_P_FIP);
+	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:   The FCoE controller for the ELS frame
+ * @dtype: The FIP descriptor type for the frame
+ * @skb:   The FCoE ELS frame including FC header but no FCoE headers
+ * @d_id:  The destination port ID.
+ *
+ * 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, struct fc_lport *lport,
+			    u8 dtype, struct sk_buff *skb, u32 d_id)
+{
+	struct fip_encaps_head {
+		struct ethhdr eth;
+		struct fip_header fip;
+		struct fip_encaps encaps;
+	} __packed * cap;
+	struct fc_frame_header *fh;
+	struct fip_mac_desc *mac;
+	struct fcoe_fcf *fcf;
+	size_t dlen;
+	u16 fip_flags;
+	u8 op;
+
+	fh = (struct fc_frame_header *)skb->data;
+	op = *(u8 *)(fh + 1);
+	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));
+
+	if (lport->point_to_multipoint) {
+		if (fcoe_ctlr_vn_lookup(fip, d_id, cap->eth.h_dest))
+			return -ENODEV;
+		fip_flags = 0;
+	} else {
+		fcf = fip->sel_fcf;
+		if (!fcf)
+			return -ENODEV;
+		fip_flags = fcf->flags;
+		fip_flags &= fip->spma ? FIP_FL_SPMA | FIP_FL_FPMA :
+					 FIP_FL_FPMA;
+		if (!fip_flags)
+			return -ENODEV;
+		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);
+	if (op == ELS_LS_ACC || op == ELS_LS_RJT)
+		cap->fip.fip_subcode = FIP_SC_REP;
+	else
+		cap->fip.fip_subcode = FIP_SC_REQ;
+	cap->fip.fip_flags = htons(fip_flags);
+
+	cap->encaps.fd_desc.fip_dtype = dtype;
+	cap->encaps.fd_desc.fip_dlen = dlen / FIP_BPW;
+
+	if (op != ELS_LS_RJT) {
+		dlen += sizeof(*mac);
+		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 != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) {
+			memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
+		} else if (fip->mode == FIP_MODE_VN2VN) {
+			hton24(mac->fd_mac, FIP_VN_FC_MAP);
+			hton24(mac->fd_mac + 3, fip->port_id);
+		} else if (fip_flags & FIP_FL_SPMA) {
+			LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with SPMA\n");
+			memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN);
+		} else {
+			LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with FPMA\n");
+			/* FPMA only FLOGI.  Must leave the MAC desc zeroed. */
+		}
+	}
+	cap->fip.fip_dl_len = htons(dlen / FIP_BPW);
+
+	skb->protocol = htons(ETH_P_FIP);
+	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.
+ * @lport:	libfc fc_lport to send from
+ * @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).
+ * The the skb must also be an fc_frame.
+ *
+ * This is called from the lower-level driver with spinlocks held,
+ * so we must not take a mutex here.
+ */
+int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
+		       struct sk_buff *skb)
+{
+	struct fc_frame *fp;
+	struct fc_frame_header *fh;
+	u16 old_xid;
+	u8 op;
+	u8 mac[ETH_ALEN];
+
+	fp = container_of(skb, struct fc_frame, skb);
+	fh = (struct fc_frame_header *)skb->data;
+	op = *(u8 *)(fh + 1);
+
+	if (op == ELS_FLOGI && fip->mode != FIP_MODE_VN2VN) {
+		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;
+			fcoe_ctlr_map_dest(fip);
+			return 0;
+		}
+		if (fip->state == FIP_ST_NON_FIP)
+			fcoe_ctlr_map_dest(fip);
+	}
+
+	if (fip->state == FIP_ST_NON_FIP)
+		return 0;
+	if (!fip->sel_fcf && fip->mode != FIP_MODE_VN2VN)
+		goto drop;
+	switch (op) {
+	case ELS_FLOGI:
+		op = FIP_DT_FLOGI;
+		if (fip->mode == FIP_MODE_VN2VN)
+			break;
+		spin_lock_bh(&fip->ctlr_lock);
+		kfree_skb(fip->flogi_req);
+		fip->flogi_req = skb;
+		fip->flogi_req_send = 1;
+		spin_unlock_bh(&fip->ctlr_lock);
+		schedule_work(&fip->timer_work);
+		return -EINPROGRESS;
+	case ELS_FDISC:
+		if (ntoh24(fh->fh_s_id))
+			return 0;
+		op = FIP_DT_FDISC;
+		break;
+	case ELS_LOGO:
+		if (fip->mode == FIP_MODE_VN2VN) {
+			if (fip->state != FIP_ST_VNMP_UP)
+				return -EINVAL;
+			if (ntoh24(fh->fh_d_id) == FC_FID_FLOGI)
+				return -EINVAL;
+		} else {
+			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 non-FIP, we may have 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.
+		 */
+		if (fip->state == FIP_ST_NON_FIP) {
+			if (fip->flogi_oxid == FC_XID_UNKNOWN)
+				return 0;
+			fip->flogi_oxid = FC_XID_UNKNOWN;
+			fc_fcoe_set_mac(mac, fh->fh_d_id);
+			fip->update_mac(lport, mac);
+		}
+		/* fall through */
+	case ELS_LS_RJT:
+		op = fr_encaps(fp);
+		if (op)
+			break;
+		return 0;
+	default:
+		if (fip->state != FIP_ST_ENABLED &&
+		    fip->state != FIP_ST_VNMP_UP)
+			goto drop;
+		return 0;
+	}
+	LIBFCOE_FIP_DBG(fip, "els_send op %u d_id %x\n",
+			op, ntoh24(fh->fh_d_id));
+	if (fcoe_ctlr_encaps(fip, lport, op, skb, ntoh24(fh->fh_d_id)))
+		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: The FCoE controller to free FCFs on
+ *
+ * Called with lock held and preemption disabled.
+ *
+ * An FCF is considered old if we have missed two advertisements.
+ * That is, there have been no valid advertisement from it for 2.5
+ * times its keep-alive period.
+ *
+ * In addition, determine the time when an FCF selection can occur.
+ *
+ * Also, increment the MissDiscAdvCount when no advertisement is received
+ * for the corresponding FCF for 1.5 * FKA_ADV_PERIOD (FC-BB-5 LESB).
+ *
+ * Returns the time in jiffies for the next call.
+ */
+static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
+{
+	struct fcoe_fcf *fcf;
+	struct fcoe_fcf *next;
+	unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
+	unsigned long deadline;
+	unsigned long sel_time = 0;
+	struct fcoe_dev_stats *stats;
+
+	stats = per_cpu_ptr(fip->lp->dev_stats, get_cpu());
+
+	list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
+		deadline = fcf->time + fcf->fka_period + fcf->fka_period / 2;
+		if (fip->sel_fcf == fcf) {
+			if (time_after(jiffies, deadline)) {
+				stats->MissDiscAdvCount++;
+				printk(KERN_INFO "libfcoe: host%d: "
+				       "Missing Discovery Advertisement "
+				       "for fab %16.16llx count %lld\n",
+				       fip->lp->host->host_no, fcf->fabric_name,
+				       stats->MissDiscAdvCount);
+			} else if (time_after(next_timer, deadline))
+				next_timer = deadline;
+		}
+
+		deadline += fcf->fka_period;
+		if (time_after_eq(jiffies, deadline)) {
+			if (fip->sel_fcf == fcf)
+				fip->sel_fcf = NULL;
+			list_del(&fcf->list);
+			WARN_ON(!fip->fcf_count);
+			fip->fcf_count--;
+			kfree(fcf);
+			stats->VLinkFailureCount++;
+		} else {
+			if (time_after(next_timer, deadline))
+				next_timer = deadline;
+			if (fcoe_ctlr_mtu_valid(fcf) &&
+			    (!sel_time || time_before(sel_time, fcf->time)))
+				sel_time = fcf->time;
+		}
+	}
+	put_cpu();
+	if (sel_time && !fip->sel_fcf && !fip->sel_time) {
+		sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY);
+		fip->sel_time = sel_time;
+	}
+
+	return next_timer;
+}
+
+/**
+ * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry
+ * @fip: The FCoE controller receiving the advertisement
+ * @skb: The received FIP advertisement frame
+ * @fcf: The resulting FCF entry
+ *
+ * Returns zero on a valid parsed advertisement,
+ * otherwise returns non zero value.
+ */
+static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
+			       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;
+	u32 desc_mask;
+
+	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);
+
+	/*
+	 * mask of required descriptors. validating each one clears its bit.
+	 */
+	desc_mask = BIT(FIP_DT_PRI) | BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) |
+			BIT(FIP_DT_FAB) | BIT(FIP_DT_FKA);
+
+	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;
+		/* Drop Adv if there are duplicate critical descriptors */
+		if ((desc->fip_dtype < 32) &&
+		    !(desc_mask & 1U << desc->fip_dtype)) {
+			LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
+					"Descriptors in FIP adv\n");
+			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;
+			desc_mask &= ~BIT(FIP_DT_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)) {
+				LIBFCOE_FIP_DBG(fip,
+					"Invalid MAC addr %pM in FIP adv\n",
+					fcf->fcf_mac);
+				return -EINVAL;
+			}
+			desc_mask &= ~BIT(FIP_DT_MAC);
+			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);
+			desc_mask &= ~BIT(FIP_DT_NAME);
+			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);
+			desc_mask &= ~BIT(FIP_DT_FAB);
+			break;
+		case FIP_DT_FKA:
+			if (dlen != sizeof(struct fip_fka_desc))
+				goto len_err;
+			fka = (struct fip_fka_desc *)desc;
+			if (fka->fd_flags & FIP_FKA_ADV_D)
+				fcf->fd_flags = 1;
+			t = ntohl(fka->fd_fka_period);
+			if (t >= FCOE_CTLR_MIN_FKA)
+				fcf->fka_period = msecs_to_jiffies(t);
+			desc_mask &= ~BIT(FIP_DT_FKA);
+			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:
+			LIBFCOE_FIP_DBG(fip, "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;
+			break;
+		}
+		desc = (struct fip_desc *)((char *)desc + dlen);
+		rlen -= dlen;
+	}
+	if (!fcf->fc_map || (fcf->fc_map & 0x10000))
+		return -EINVAL;
+	if (!fcf->switch_name)
+		return -EINVAL;
+	if (desc_mask) {
+		LIBFCOE_FIP_DBG(fip, "adv missing descriptors mask %x\n",
+				desc_mask);
+		return -EINVAL;
+	}
+	return 0;
+
+len_err:
+	LIBFCOE_FIP_DBG(fip, "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: The FCoE controller receiving the advertisement
+ * @skb: The 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(fip, skb, &new))
+		return;
+
+	mutex_lock(&fip->ctlr_mutex);
+	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 {
+		/*
+		 * Update the FCF's keep-alive descriptor flags.
+		 * Other flag changes from new advertisements are
+		 * ignored after a solicited advertisement is
+		 * received and the FCF is selectable (usable).
+		 */
+		fcf->fd_flags = new.fd_flags;
+		if (!fcoe_ctlr_fcf_usable(fcf))
+			fcf->flags = new.flags;
+
+		if (fcf == fip->sel_fcf && !fcf->fd_flags) {
+			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);
+		}
+		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;
+	if (!found)
+		LIBFCOE_FIP_DBG(fip, "New FCF fab %16.16llx mac %pM\n",
+				fcf->fabric_name, fcf->fcf_mac);
+
+	/*
+	 * 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);
+
+	/*
+	 * Put this FCF at the head of the list for priority among equals.
+	 * This helps in the case of an NPV switch which insists we use
+	 * the FCF that answers multicast solicitations, not the others that
+	 * are sending periodic multicast advertisements.
+	 */
+	if (mtu_valid) {
+		list_del(&fcf->list);
+		list_add(&fcf->list, &fip->fcfs);
+	}
+
+	/*
+	 * If this is the first validated FCF, note the time and
+	 * set a timer to trigger selection.
+	 */
+	if (mtu_valid && !fip->sel_fcf && 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:
+	mutex_unlock(&fip->ctlr_mutex);
+}
+
+/**
+ * fcoe_ctlr_recv_els() - Handle an incoming FIP encapsulated ELS frame
+ * @fip: The FCoE controller which received the packet
+ * @skb: The received FIP packet
+ */
+static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
+{
+	struct fc_lport *lport = fip->lp;
+	struct fip_header *fiph;
+	struct fc_frame *fp = (struct fc_frame *)skb;
+	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;
+	u32 desc_mask = 0;
+	u32 desc_cnt = 0;
+
+	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) {
+		desc_cnt++;
+		dlen = desc->fip_dlen * FIP_BPW;
+		if (dlen < sizeof(*desc) || dlen > rlen)
+			goto drop;
+		/* Drop ELS if there are duplicate critical descriptors */
+		if (desc->fip_dtype < 32) {
+			if (desc_mask & 1U << desc->fip_dtype) {
+				LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
+						"Descriptors in FIP ELS\n");
+				goto drop;
+			}
+			desc_mask |= (1 << desc->fip_dtype);
+		}
+		switch (desc->fip_dtype) {
+		case FIP_DT_MAC:
+			if (desc_cnt == 1) {
+				LIBFCOE_FIP_DBG(fip, "FIP descriptors "
+						"received out of order\n");
+				goto drop;
+			}
+
+			if (dlen != sizeof(struct fip_mac_desc))
+				goto len_err;
+			memcpy(granted_mac,
+			       ((struct fip_mac_desc *)desc)->fd_mac,
+			       ETH_ALEN);
+			break;
+		case FIP_DT_FLOGI:
+		case FIP_DT_FDISC:
+		case FIP_DT_LOGO:
+		case FIP_DT_ELP:
+			if (desc_cnt != 1) {
+				LIBFCOE_FIP_DBG(fip, "FIP descriptors "
+						"received out of order\n");
+				goto drop;
+			}
+			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:
+			LIBFCOE_FIP_DBG(fip, "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;
+			if (desc_cnt <= 2) {
+				LIBFCOE_FIP_DBG(fip, "FIP descriptors "
+						"received out of order\n");
+				goto drop;
+			}
+			break;
+		}
+		desc = (struct fip_desc *)((char *)desc + dlen);
+		rlen -= dlen;
+	}
+
+	if (!fh)
+		goto drop;
+	els_op = *(u8 *)(fh + 1);
+
+	if ((els_dtype == FIP_DT_FLOGI || els_dtype == FIP_DT_FDISC) &&
+	    sub == FIP_SC_REP && fip->mode != FIP_MODE_VN2VN) {
+		if (els_op == ELS_LS_ACC) {
+			if (!is_valid_ether_addr(granted_mac)) {
+				LIBFCOE_FIP_DBG(fip,
+					"Invalid MAC address %pM in FIP ELS\n",
+					granted_mac);
+				goto drop;
+			}
+			memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN);
+
+			if (fip->flogi_oxid == ntohs(fh->fh_ox_id)) {
+				fip->flogi_oxid = FC_XID_UNKNOWN;
+				if (els_dtype == FIP_DT_FLOGI)
+					fcoe_ctlr_announce(fip);
+			}
+		} else if (els_dtype == FIP_DT_FLOGI &&
+			   !fcoe_ctlr_flogi_retry(fip))
+			goto drop;	/* retrying FLOGI so drop reject */
+	}
+
+	if ((desc_cnt == 0) || ((els_op != ELS_LS_RJT) &&
+	    (!(1U << FIP_DT_MAC & desc_mask)))) {
+		LIBFCOE_FIP_DBG(fip, "Missing critical descriptors "
+				"in FIP ELS\n");
+		goto drop;
+	}
+
+	/*
+	 * 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) = lport;
+	fr_encaps(fp) = els_dtype;
+
+	stats = per_cpu_ptr(lport->dev_stats, get_cpu());
+	stats->RxFrames++;
+	stats->RxWords += skb->len / FIP_BPW;
+	put_cpu();
+
+	fc_exch_recv(lport, fp);
+	return;
+
+len_err:
+	LIBFCOE_FIP_DBG(fip, "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: The FCoE controller that received the frame
+ * @fh:	 The 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 *lport = fip->lp;
+	struct fc_lport *vn_port = NULL;
+	u32 desc_mask;
+	int is_vn_port = 0;
+
+	LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n");
+
+	if (!fcf || !lport->port_id)
+		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;
+		/* Drop CVL if there are duplicate critical descriptors */
+		if ((desc->fip_dtype < 32) &&
+		    !(desc_mask & 1U << desc->fip_dtype)) {
+			LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
+					"Descriptors in FIP CVL\n");
+			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->get_src_addr(lport)) == 0 &&
+			    get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn &&
+			    ntoh24(vp->fd_fc_id) == lport->port_id) {
+				desc_mask &= ~BIT(FIP_DT_VN_ID);
+				break;
+			}
+			/* check if clr_vlink is for NPIV port */
+			mutex_lock(&lport->lp_mutex);
+			list_for_each_entry(vn_port, &lport->vports, list) {
+				if (compare_ether_addr(vp->fd_mac,
+				    fip->get_src_addr(vn_port)) == 0 &&
+				    (get_unaligned_be64(&vp->fd_wwpn)
+							== vn_port->wwpn) &&
+				    (ntoh24(vp->fd_fc_id) ==
+					    fc_host_port_id(vn_port->host))) {
+					desc_mask &= ~BIT(FIP_DT_VN_ID);
+					is_vn_port = 1;
+					break;
+				}
+			}
+			mutex_unlock(&lport->lp_mutex);
+
+			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) {
+		LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n",
+				desc_mask);
+	} else {
+		LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n");
+
+		if (is_vn_port)
+			fc_lport_reset(vn_port);
+		else {
+			mutex_lock(&fip->ctlr_mutex);
+			per_cpu_ptr(lport->dev_stats,
+				    get_cpu())->VLinkFailureCount++;
+			put_cpu();
+			fcoe_ctlr_reset(fip);
+			mutex_unlock(&fip->ctlr_mutex);
+
+			fc_lport_reset(fip->lp);
+			fcoe_ctlr_solicit(fip, NULL);
+		}
+	}
+}
+
+/**
+ * fcoe_ctlr_recv() - Receive a FIP packet
+ * @fip: The FCoE controller that received the packet
+ * @skb: The received FIP packet
+ *
+ * This may be called from either NET_RX_SOFTIRQ or IRQ.
+ */
+void fcoe_ctlr_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
+{
+	skb_queue_tail(&fip->fip_recv_list, skb);
+	schedule_work(&fip->recv_work);
+}
+EXPORT_SYMBOL(fcoe_ctlr_recv);
+
+/**
+ * fcoe_ctlr_recv_handler() - Receive a FIP frame
+ * @fip: The FCoE controller that received the frame
+ * @skb: The received FIP frame
+ *
+ * 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 (fip->mode == FIP_MODE_VN2VN) {
+		if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) &&
+		    compare_ether_addr(eh->h_dest, fcoe_all_vn2vn) &&
+		    compare_ether_addr(eh->h_dest, fcoe_all_p2p))
+			goto drop;
+	} else if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) &&
+		   compare_ether_addr(eh->h_dest, fcoe_all_enode))
+		goto drop;
+	fiph = (struct fip_header *)skb->data;
+	op = ntohs(fiph->fip_op);
+	sub = fiph->fip_subcode;
+
+	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;
+
+	mutex_lock(&fip->ctlr_mutex);
+	state = fip->state;
+	if (state == FIP_ST_AUTO) {
+		fip->map_dest = 0;
+		fcoe_ctlr_set_state(fip, FIP_ST_ENABLED);
+		state = FIP_ST_ENABLED;
+		LIBFCOE_FIP_DBG(fip, "Using FIP mode\n");
+	}
+	mutex_unlock(&fip->ctlr_mutex);
+
+	if (fip->mode == FIP_MODE_VN2VN && op == FIP_OP_VN2VN)
+		return fcoe_ctlr_vn_recv(fip, skb);
+
+	if (state != FIP_ST_ENABLED && state != FIP_ST_VNMP_UP &&
+	    state != FIP_ST_VNMP_CLAIM)
+		goto drop;
+
+	if (op == FIP_OP_LS) {
+		fcoe_ctlr_recv_els(fip, skb);	/* consumes skb */
+		return 0;
+	}
+
+	if (state != FIP_ST_ENABLED)
+		goto drop;
+
+	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: The FCoE controller
+ *
+ * Returns the selected FCF, or NULL if none are usable.
+ *
+ * If there are conflicting advertisements, no FCF can be chosen.
+ *
+ * If there is already a selected FCF, this will choose a better one or
+ * an equivalent one that hasn't already been sent a FLOGI.
+ *
+ * Called with lock held.
+ */
+static struct fcoe_fcf *fcoe_ctlr_select(struct fcoe_ctlr *fip)
+{
+	struct fcoe_fcf *fcf;
+	struct fcoe_fcf *best = fip->sel_fcf;
+	struct fcoe_fcf *first;
+
+	first = list_first_entry(&fip->fcfs, struct fcoe_fcf, list);
+
+	list_for_each_entry(fcf, &fip->fcfs, list) {
+		LIBFCOE_FIP_DBG(fip, "consider FCF fab %16.16llx "
+				"VFID %d mac %pM map %x val %d "
+				"sent %u pri %u\n",
+				fcf->fabric_name, fcf->vfid, fcf->fcf_mac,
+				fcf->fc_map, fcoe_ctlr_mtu_valid(fcf),
+				fcf->flogi_sent, fcf->pri);
+		if (fcf->fabric_name != first->fabric_name ||
+		    fcf->vfid != first->vfid ||
+		    fcf->fc_map != first->fc_map) {
+			LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, "
+					"or FC-MAP\n");
+			return NULL;
+		}
+		if (fcf->flogi_sent)
+			continue;
+		if (!fcoe_ctlr_fcf_usable(fcf)) {
+			LIBFCOE_FIP_DBG(fip, "FCF for fab %16.16llx "
+					"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 || fcf->pri < best->pri || best->flogi_sent)
+			best = fcf;
+	}
+	fip->sel_fcf = best;
+	if (best) {
+		LIBFCOE_FIP_DBG(fip, "using FCF mac %pM\n", best->fcf_mac);
+		fip->port_ka_time = jiffies +
+			msecs_to_jiffies(FIP_VN_KA_PERIOD);
+		fip->ctlr_ka_time = jiffies + best->fka_period;
+		if (time_before(fip->ctlr_ka_time, fip->timer.expires))
+			mod_timer(&fip->timer, fip->ctlr_ka_time);
+	}
+	return best;
+}
+
+/**
+ * fcoe_ctlr_flogi_send_locked() - send FIP-encapsulated FLOGI to current FCF
+ * @fip: The FCoE controller
+ *
+ * Returns non-zero error if it could not be sent.
+ *
+ * Called with ctlr_mutex and ctlr_lock held.
+ * Caller must verify that fip->sel_fcf is not NULL.
+ */
+static int fcoe_ctlr_flogi_send_locked(struct fcoe_ctlr *fip)
+{
+	struct sk_buff *skb;
+	struct sk_buff *skb_orig;
+	struct fc_frame_header *fh;
+	int error;
+
+	skb_orig = fip->flogi_req;
+	if (!skb_orig)
+		return -EINVAL;
+
+	/*
+	 * Clone and send the FLOGI request.  If clone fails, use original.
+	 */
+	skb = skb_clone(skb_orig, GFP_ATOMIC);
+	if (!skb) {
+		skb = skb_orig;
+		fip->flogi_req = NULL;
+	}
+	fh = (struct fc_frame_header *)skb->data;
+	error = fcoe_ctlr_encaps(fip, fip->lp, FIP_DT_FLOGI, skb,
+				 ntoh24(fh->fh_d_id));
+	if (error) {
+		kfree_skb(skb);
+		return error;
+	}
+	fip->send(fip, skb);
+	fip->sel_fcf->flogi_sent = 1;
+	return 0;
+}
+
+/**
+ * fcoe_ctlr_flogi_retry() - resend FLOGI request to a new FCF if possible
+ * @fip: The FCoE controller
+ *
+ * Returns non-zero error code if there's no FLOGI request to retry or
+ * no alternate FCF available.
+ */
+static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *fip)
+{
+	struct fcoe_fcf *fcf;
+	int error;
+
+	mutex_lock(&fip->ctlr_mutex);
+	spin_lock_bh(&fip->ctlr_lock);
+	LIBFCOE_FIP_DBG(fip, "re-sending FLOGI - reselect\n");
+	fcf = fcoe_ctlr_select(fip);
+	if (!fcf || fcf->flogi_sent) {
+		kfree_skb(fip->flogi_req);
+		fip->flogi_req = NULL;
+		error = -ENOENT;
+	} else {
+		fcoe_ctlr_solicit(fip, NULL);
+		error = fcoe_ctlr_flogi_send_locked(fip);
+	}
+	spin_unlock_bh(&fip->ctlr_lock);
+	mutex_unlock(&fip->ctlr_mutex);
+	return error;
+}
+
+
+/**
+ * fcoe_ctlr_flogi_send() - Handle sending of FIP FLOGI.
+ * @fip: The FCoE controller that timed out
+ *
+ * Done here because fcoe_ctlr_els_send() can't get mutex.
+ *
+ * Called with ctlr_mutex held.  The caller must not hold ctlr_lock.
+ */
+static void fcoe_ctlr_flogi_send(struct fcoe_ctlr *fip)
+{
+	struct fcoe_fcf *fcf;
+
+	spin_lock_bh(&fip->ctlr_lock);
+	fcf = fip->sel_fcf;
+	if (!fcf || !fip->flogi_req_send)
+		goto unlock;
+
+	LIBFCOE_FIP_DBG(fip, "sending FLOGI\n");
+
+	/*
+	 * If this FLOGI is being sent due to a timeout retry
+	 * to the same FCF as before, select a different FCF if possible.
+	 */
+	if (fcf->flogi_sent) {
+		LIBFCOE_FIP_DBG(fip, "sending FLOGI - reselect\n");
+		fcf = fcoe_ctlr_select(fip);
+		if (!fcf || fcf->flogi_sent) {
+			LIBFCOE_FIP_DBG(fip, "sending FLOGI - clearing\n");
+			list_for_each_entry(fcf, &fip->fcfs, list)
+				fcf->flogi_sent = 0;
+			fcf = fcoe_ctlr_select(fip);
+		}
+	}
+	if (fcf) {
+		fcoe_ctlr_flogi_send_locked(fip);
+		fip->flogi_req_send = 0;
+	} else /* XXX */
+		LIBFCOE_FIP_DBG(fip, "No FCF selected - defer send\n");
+unlock:
+	spin_unlock_bh(&fip->ctlr_lock);
+}
+
+/**
+ * fcoe_ctlr_timeout() - FIP timeout handler
+ * @arg: The FCoE controller that timed out
+ */
+static void fcoe_ctlr_timeout(unsigned long arg)
+{
+	struct fcoe_ctlr *fip = (struct fcoe_ctlr *)arg;
+
+	schedule_work(&fip->timer_work);
+}
+
+/**
+ * fcoe_ctlr_timer_work() - Worker thread function for timer work
+ * @work: Handle to a FCoE controller
+ *
+ * Ages FCFs.  Triggers FCF selection if possible.
+ * Sends keep-alives and resets.
+ */
+static void fcoe_ctlr_timer_work(struct work_struct *work)
+{
+	struct fcoe_ctlr *fip;
+	struct fc_lport *vport;
+	u8 *mac;
+	u8 reset = 0;
+	u8 send_ctlr_ka = 0;
+	u8 send_port_ka = 0;
+	struct fcoe_fcf *sel;
+	struct fcoe_fcf *fcf;
+	unsigned long next_timer;
+
+	fip = container_of(work, struct fcoe_ctlr, timer_work);
+	if (fip->mode == FIP_MODE_VN2VN)
+		return fcoe_ctlr_vn_timeout(fip);
+	mutex_lock(&fip->ctlr_mutex);
+	if (fip->state == FIP_ST_DISABLED) {
+		mutex_unlock(&fip->ctlr_mutex);
+		return;
+	}
+
+	fcf = fip->sel_fcf;
+	next_timer = fcoe_ctlr_age_fcfs(fip);
+
+	sel = fip->sel_fcf;
+	if (!sel && fip->sel_time) {
+		if (time_after_eq(jiffies, fip->sel_time)) {
+			sel = fcoe_ctlr_select(fip);
+			fip->sel_time = 0;
+		} else if (time_after(next_timer, fip->sel_time))
+			next_timer = fip->sel_time;
+	}
+
+	if (sel && fip->flogi_req_send)
+		fcoe_ctlr_flogi_send(fip);
+	else if (!sel && fcf)
+		reset = 1;
+
+	if (sel && !sel->fd_flags) {
+		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;
+	}
+	if (!list_empty(&fip->fcfs))
+		mod_timer(&fip->timer, next_timer);
+	mutex_unlock(&fip->ctlr_mutex);
+
+	if (reset) {
+		fc_lport_reset(fip->lp);
+		/* restart things with a solicitation */
+		fcoe_ctlr_solicit(fip, NULL);
+	}
+
+	if (send_ctlr_ka)
+		fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr);
+
+	if (send_port_ka) {
+		mutex_lock(&fip->lp->lp_mutex);
+		mac = fip->get_src_addr(fip->lp);
+		fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac);
+		list_for_each_entry(vport, &fip->lp->vports, list) {
+			mac = fip->get_src_addr(vport);
+			fcoe_ctlr_send_keep_alive(fip, vport, 1, mac);
+		}
+		mutex_unlock(&fip->lp->lp_mutex);
+	}
+}
+
+/**
+ * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames
+ * @recv_work: Handle to a FCoE controller
+ */
+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);
+	while ((skb = skb_dequeue(&fip->fip_recv_list)))
+		fcoe_ctlr_recv_handler(fip, skb);
+}
+
+/**
+ * fcoe_ctlr_recv_flogi() - Snoop pre-FIP receipt of FLOGI response
+ * @fip: The FCoE controller
+ * @fp:	 The FC frame to snoop
+ *
+ * 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.
+ * Fill in the granted_mac address.
+ *
+ * Return non-zero if the frame should not be delivered to libfc.
+ */
+int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
+			 struct fc_frame *fp)
+{
+	struct fc_frame_header *fh;
+	u8 op;
+	u8 *sa;
+
+	sa = eth_hdr(&fp->skb)->h_source;
+	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)) {
+
+		mutex_lock(&fip->ctlr_mutex);
+		if (fip->state != FIP_ST_AUTO && fip->state != FIP_ST_NON_FIP) {
+			mutex_unlock(&fip->ctlr_mutex);
+			return -EINVAL;
+		}
+		fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
+		LIBFCOE_FIP_DBG(fip,
+				"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)) {
+			fcoe_ctlr_map_dest(fip);
+		} else {
+			memcpy(fip->dest_addr, sa, ETH_ALEN);
+			fip->map_dest = 0;
+		}
+		fip->flogi_oxid = FC_XID_UNKNOWN;
+		mutex_unlock(&fip->ctlr_mutex);
+		fc_fcoe_set_mac(fr_cb(fp)->granted_mac, fh->fh_d_id);
+	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
+		/*
+		 * Save source MAC for point-to-point responses.
+		 */
+		mutex_lock(&fip->ctlr_mutex);
+		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_AUTO)
+				LIBFCOE_FIP_DBG(fip, "received non-FIP FLOGI. "
+						"Setting non-FIP mode\n");
+			fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
+		}
+		mutex_unlock(&fip->ctlr_mutex);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(fcoe_ctlr_recv_flogi);
+
+/**
+ * fcoe_wwn_from_mac() - Converts a 48-bit IEEE MAC address to a 64-bit FC WWN
+ * @mac:    The MAC address to convert
+ * @scheme: The scheme to use when converting
+ * @port:   The 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_ctlr_rport() - return the fcoe_rport for a given fc_rport_priv
+ * @rdata: libfc remote port
+ */
+static inline struct fcoe_rport *fcoe_ctlr_rport(struct fc_rport_priv *rdata)
+{
+	return (struct fcoe_rport *)(rdata + 1);
+}
+
+/**
+ * fcoe_ctlr_vn_send() - Send a FIP VN2VN Probe Request or Reply.
+ * @fip: The FCoE controller
+ * @sub: sub-opcode for probe request, reply, or advertisement.
+ * @dest: The destination Ethernet MAC address
+ * @min_len: minimum size of the Ethernet payload to be sent
+ */
+static void fcoe_ctlr_vn_send(struct fcoe_ctlr *fip,
+			      enum fip_vn2vn_subcode sub,
+			      const u8 *dest, size_t min_len)
+{
+	struct sk_buff *skb;
+	struct fip_frame {
+		struct ethhdr eth;
+		struct fip_header fip;
+		struct fip_mac_desc mac;
+		struct fip_wwn_desc wwnn;
+		struct fip_vn_desc vn;
+	} __packed * frame;
+	struct fip_fc4_feat *ff;
+	struct fip_size_desc *size;
+	u32 fcp_feat;
+	size_t len;
+	size_t dlen;
+
+	len = sizeof(*frame);
+	dlen = 0;
+	if (sub == FIP_SC_VN_CLAIM_NOTIFY || sub == FIP_SC_VN_CLAIM_REP) {
+		dlen = sizeof(struct fip_fc4_feat) +
+		       sizeof(struct fip_size_desc);
+		len += dlen;
+	}
+	dlen += sizeof(frame->mac) + sizeof(frame->wwnn) + sizeof(frame->vn);
+	len = max(len, min_len + sizeof(struct ethhdr));
+
+	skb = dev_alloc_skb(len);
+	if (!skb)
+		return;
+
+	frame = (struct fip_frame *)skb->data;
+	memset(frame, 0, len);
+	memcpy(frame->eth.h_dest, dest, ETH_ALEN);
+	memcpy(frame->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
+	frame->eth.h_proto = htons(ETH_P_FIP);
+
+	frame->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
+	frame->fip.fip_op = htons(FIP_OP_VN2VN);
+	frame->fip.fip_subcode = sub;
+	frame->fip.fip_dl_len = htons(dlen / FIP_BPW);
+
+	frame->mac.fd_desc.fip_dtype = FIP_DT_MAC;
+	frame->mac.fd_desc.fip_dlen = sizeof(frame->mac) / FIP_BPW;
+	memcpy(frame->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
+
+	frame->wwnn.fd_desc.fip_dtype = FIP_DT_NAME;
+	frame->wwnn.fd_desc.fip_dlen = sizeof(frame->wwnn) / FIP_BPW;
+	put_unaligned_be64(fip->lp->wwnn, &frame->wwnn.fd_wwn);
+
+	frame->vn.fd_desc.fip_dtype = FIP_DT_VN_ID;
+	frame->vn.fd_desc.fip_dlen = sizeof(frame->vn) / FIP_BPW;
+	hton24(frame->vn.fd_mac, FIP_VN_FC_MAP);
+	hton24(frame->vn.fd_mac + 3, fip->port_id);
+	hton24(frame->vn.fd_fc_id, fip->port_id);
+	put_unaligned_be64(fip->lp->wwpn, &frame->vn.fd_wwpn);
+
+	/*
+	 * For claims, add FC-4 features.
+	 * TBD: Add interface to get fc-4 types and features from libfc.
+	 */
+	if (sub == FIP_SC_VN_CLAIM_NOTIFY || sub == FIP_SC_VN_CLAIM_REP) {
+		ff = (struct fip_fc4_feat *)(frame + 1);
+		ff->fd_desc.fip_dtype = FIP_DT_FC4F;
+		ff->fd_desc.fip_dlen = sizeof(*ff) / FIP_BPW;
+		ff->fd_fts = fip->lp->fcts;
+
+		fcp_feat = 0;
+		if (fip->lp->service_params & FCP_SPPF_INIT_FCN)
+			fcp_feat |= FCP_FEAT_INIT;
+		if (fip->lp->service_params & FCP_SPPF_TARG_FCN)
+			fcp_feat |= FCP_FEAT_TARG;
+		fcp_feat <<= (FC_TYPE_FCP * 4) % 32;
+		ff->fd_ff.fd_feat[FC_TYPE_FCP * 4 / 32] = htonl(fcp_feat);
+
+		size = (struct fip_size_desc *)(ff + 1);
+		size->fd_desc.fip_dtype = FIP_DT_FCOE_SIZE;
+		size->fd_desc.fip_dlen = sizeof(*size) / FIP_BPW;
+		size->fd_size = htons(fcoe_ctlr_fcoe_size(fip));
+	}
+
+	skb_put(skb, len);
+	skb->protocol = htons(ETH_P_FIP);
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+
+	fip->send(fip, skb);
+}
+
+/**
+ * fcoe_ctlr_vn_rport_callback - Event handler for rport events.
+ * @lport: The lport which is receiving the event
+ * @rdata: remote port private data
+ * @event: The event that occured
+ *
+ * Locking Note:  The rport lock must not be held when calling this function.
+ */
+static void fcoe_ctlr_vn_rport_callback(struct fc_lport *lport,
+					struct fc_rport_priv *rdata,
+					enum fc_rport_event event)
+{
+	struct fcoe_ctlr *fip = lport->disc.priv;
+	struct fcoe_rport *frport = fcoe_ctlr_rport(rdata);
+
+	LIBFCOE_FIP_DBG(fip, "vn_rport_callback %x event %d\n",
+			rdata->ids.port_id, event);
+
+	mutex_lock(&fip->ctlr_mutex);
+	switch (event) {
+	case RPORT_EV_READY:
+		frport->login_count = 0;
+		break;
+	case RPORT_EV_LOGO:
+	case RPORT_EV_FAILED:
+	case RPORT_EV_STOP:
+		frport->login_count++;
+		if (frport->login_count > FCOE_CTLR_VN2VN_LOGIN_LIMIT) {
+			LIBFCOE_FIP_DBG(fip,
+					"rport FLOGI limited port_id %6.6x\n",
+					rdata->ids.port_id);
+			lport->tt.rport_logoff(rdata);
+		}
+		break;
+	default:
+		break;
+	}
+	mutex_unlock(&fip->ctlr_mutex);
+}
+
+static struct fc_rport_operations fcoe_ctlr_vn_rport_ops = {
+	.event_callback = fcoe_ctlr_vn_rport_callback,
+};
+
+/**
+ * fcoe_ctlr_disc_stop_locked() - stop discovery in VN2VN mode
+ * @fip: The FCoE controller
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_disc_stop_locked(struct fc_lport *lport)
+{
+	mutex_lock(&lport->disc.disc_mutex);
+	lport->disc.disc_callback = NULL;
+	mutex_unlock(&lport->disc.disc_mutex);
+}
+
+/**
+ * fcoe_ctlr_disc_stop() - stop discovery in VN2VN mode
+ * @fip: The FCoE controller
+ *
+ * Called through the local port template for discovery.
+ * Called without the ctlr_mutex held.
+ */
+static void fcoe_ctlr_disc_stop(struct fc_lport *lport)
+{
+	struct fcoe_ctlr *fip = lport->disc.priv;
+
+	mutex_lock(&fip->ctlr_mutex);
+	fcoe_ctlr_disc_stop_locked(lport);
+	mutex_unlock(&fip->ctlr_mutex);
+}
+
+/**
+ * fcoe_ctlr_disc_stop_final() - stop discovery for shutdown in VN2VN mode
+ * @fip: The FCoE controller
+ *
+ * Called through the local port template for discovery.
+ * Called without the ctlr_mutex held.
+ */
+static void fcoe_ctlr_disc_stop_final(struct fc_lport *lport)
+{
+	fcoe_ctlr_disc_stop(lport);
+	lport->tt.rport_flush_queue();
+	synchronize_rcu();
+}
+
+/**
+ * fcoe_ctlr_vn_restart() - VN2VN probe restart with new port_id
+ * @fip: The FCoE controller
+ *
+ * Called with fcoe_ctlr lock held.
+ */
+static void fcoe_ctlr_vn_restart(struct fcoe_ctlr *fip)
+{
+	unsigned long wait;
+	u32 port_id;
+
+	fcoe_ctlr_disc_stop_locked(fip->lp);
+
+	/*
+	 * Get proposed port ID.
+	 * If this is the first try after link up, use any previous port_id.
+	 * If there was none, use the low bits of the port_name.
+	 * On subsequent tries, get the next random one.
+	 * Don't use reserved IDs, use another non-zero value, just as random.
+	 */
+	port_id = fip->port_id;
+	if (fip->probe_tries)
+		port_id = prandom32(&fip->rnd_state) & 0xffff;
+	else if (!port_id)
+		port_id = fip->lp->wwpn & 0xffff;
+	if (!port_id || port_id == 0xffff)
+		port_id = 1;
+	fip->port_id = port_id;
+
+	if (fip->probe_tries < FIP_VN_RLIM_COUNT) {
+		fip->probe_tries++;
+		wait = random32() % FIP_VN_PROBE_WAIT;
+	} else
+		wait = FIP_VN_RLIM_INT;
+	mod_timer(&fip->timer, jiffies + msecs_to_jiffies(wait));
+	fcoe_ctlr_set_state(fip, FIP_ST_VNMP_START);
+}
+
+/**
+ * fcoe_ctlr_vn_start() - Start in VN2VN mode
+ * @fip: The FCoE controller
+ *
+ * Called with fcoe_ctlr lock held.
+ */
+static void fcoe_ctlr_vn_start(struct fcoe_ctlr *fip)
+{
+	fip->probe_tries = 0;
+	prandom32_seed(&fip->rnd_state, fip->lp->wwpn);
+	fcoe_ctlr_vn_restart(fip);
+}
+
+/**
+ * fcoe_ctlr_vn_parse - parse probe request or response
+ * @fip: The FCoE controller
+ * @skb: incoming packet
+ * @rdata: buffer for resulting parsed VN entry plus fcoe_rport
+ *
+ * Returns non-zero error number on error.
+ * Does not consume the packet.
+ */
+static int fcoe_ctlr_vn_parse(struct fcoe_ctlr *fip,
+			      struct sk_buff *skb,
+			      struct fc_rport_priv *rdata)
+{
+	struct fip_header *fiph;
+	struct fip_desc *desc = NULL;
+	struct fip_mac_desc *macd = NULL;
+	struct fip_wwn_desc *wwn = NULL;
+	struct fip_vn_desc *vn = NULL;
+	struct fip_size_desc *size = NULL;
+	struct fcoe_rport *frport;
+	size_t rlen;
+	size_t dlen;
+	u32 desc_mask = 0;
+	u32 dtype;
+	u8 sub;
+
+	memset(rdata, 0, sizeof(*rdata) + sizeof(*frport));
+	frport = fcoe_ctlr_rport(rdata);
+
+	fiph = (struct fip_header *)skb->data;
+	frport->flags = ntohs(fiph->fip_flags);
+
+	sub = fiph->fip_subcode;
+	switch (sub) {
+	case FIP_SC_VN_PROBE_REQ:
+	case FIP_SC_VN_PROBE_REP:
+	case FIP_SC_VN_BEACON:
+		desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) |
+			    BIT(FIP_DT_VN_ID);
+		break;
+	case FIP_SC_VN_CLAIM_NOTIFY:
+	case FIP_SC_VN_CLAIM_REP:
+		desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) |
+			    BIT(FIP_DT_VN_ID) | BIT(FIP_DT_FC4F) |
+			    BIT(FIP_DT_FCOE_SIZE);
+		break;
+	default:
+		LIBFCOE_FIP_DBG(fip, "vn_parse unknown subcode %u\n", sub);
+		return -EINVAL;
+	}
+
+	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;
+
+		dtype = desc->fip_dtype;
+		if (dtype < 32) {
+			if (!(desc_mask & BIT(dtype))) {
+				LIBFCOE_FIP_DBG(fip,
+						"unexpected or duplicated desc "
+						"desc type %u in "
+						"FIP VN2VN subtype %u\n",
+						dtype, sub);
+				return -EINVAL;
+			}
+			desc_mask &= ~BIT(dtype);
+		}
+
+		switch (dtype) {
+		case FIP_DT_MAC:
+			if (dlen != sizeof(struct fip_mac_desc))
+				goto len_err;
+			macd = (struct fip_mac_desc *)desc;
+			if (!is_valid_ether_addr(macd->fd_mac)) {
+				LIBFCOE_FIP_DBG(fip,
+					"Invalid MAC addr %pM in FIP VN2VN\n",
+					 macd->fd_mac);
+				return -EINVAL;
+			}
+			memcpy(frport->enode_mac, macd->fd_mac, ETH_ALEN);
+			break;
+		case FIP_DT_NAME:
+			if (dlen != sizeof(struct fip_wwn_desc))
+				goto len_err;
+			wwn = (struct fip_wwn_desc *)desc;
+			rdata->ids.node_name = get_unaligned_be64(&wwn->fd_wwn);
+			break;
+		case FIP_DT_VN_ID:
+			if (dlen != sizeof(struct fip_vn_desc))
+				goto len_err;
+			vn = (struct fip_vn_desc *)desc;
+			memcpy(frport->vn_mac, vn->fd_mac, ETH_ALEN);
+			rdata->ids.port_id = ntoh24(vn->fd_fc_id);
+			rdata->ids.port_name = get_unaligned_be64(&vn->fd_wwpn);
+			break;
+		case FIP_DT_FC4F:
+			if (dlen != sizeof(struct fip_fc4_feat))
+				goto len_err;
+			break;
+		case FIP_DT_FCOE_SIZE:
+			if (dlen != sizeof(struct fip_size_desc))
+				goto len_err;
+			size = (struct fip_size_desc *)desc;
+			frport->fcoe_len = ntohs(size->fd_size);
+			break;
+		default:
+			LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
+					"in FIP probe\n", dtype);
+			/* standard says ignore unknown descriptors >= 128 */
+			if (dtype < FIP_DT_VENDOR_BASE)
+				return -EINVAL;
+			break;
+		}
+		desc = (struct fip_desc *)((char *)desc + dlen);
+		rlen -= dlen;
+	}
+	return 0;
+
+len_err:
+	LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
+			dtype, dlen);
+	return -EINVAL;
+}
+
+/**
+ * fcoe_ctlr_vn_send_claim() - send multicast FIP VN2VN Claim Notification.
+ * @fip: The FCoE controller
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_send_claim(struct fcoe_ctlr *fip)
+{
+	fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_NOTIFY, fcoe_all_vn2vn, 0);
+	fip->sol_time = jiffies;
+}
+
+/**
+ * fcoe_ctlr_vn_probe_req() - handle incoming VN2VN probe request.
+ * @fip: The FCoE controller
+ * @rdata: parsed remote port with frport from the probe request
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_probe_req(struct fcoe_ctlr *fip,
+				   struct fc_rport_priv *rdata)
+{
+	struct fcoe_rport *frport = fcoe_ctlr_rport(rdata);
+
+	if (rdata->ids.port_id != fip->port_id)
+		return;
+
+	switch (fip->state) {
+	case FIP_ST_VNMP_CLAIM:
+	case FIP_ST_VNMP_UP:
+		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP,
+				  frport->enode_mac, 0);
+		break;
+	case FIP_ST_VNMP_PROBE1:
+	case FIP_ST_VNMP_PROBE2:
+		/*
+		 * Decide whether to reply to the Probe.
+		 * Our selected address is never a "recorded" one, so
+		 * only reply if our WWPN is greater and the
+		 * Probe's REC bit is not set.
+		 * If we don't reply, we will change our address.
+		 */
+		if (fip->lp->wwpn > rdata->ids.port_name &&
+		    !(frport->flags & FIP_FL_REC_OR_P2P)) {
+			fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP,
+					  frport->enode_mac, 0);
+			break;
+		}
+		/* fall through */
+	case FIP_ST_VNMP_START:
+		fcoe_ctlr_vn_restart(fip);
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * fcoe_ctlr_vn_probe_reply() - handle incoming VN2VN probe reply.
+ * @fip: The FCoE controller
+ * @rdata: parsed remote port with frport from the probe request
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_probe_reply(struct fcoe_ctlr *fip,
+				   struct fc_rport_priv *rdata)
+{
+	if (rdata->ids.port_id != fip->port_id)
+		return;
+	switch (fip->state) {
+	case FIP_ST_VNMP_START:
+	case FIP_ST_VNMP_PROBE1:
+	case FIP_ST_VNMP_PROBE2:
+	case FIP_ST_VNMP_CLAIM:
+		fcoe_ctlr_vn_restart(fip);
+		break;
+	case FIP_ST_VNMP_UP:
+		fcoe_ctlr_vn_send_claim(fip);
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * fcoe_ctlr_vn_add() - Add a VN2VN entry to the list, based on a claim reply.
+ * @fip: The FCoE controller
+ * @new: newly-parsed remote port with frport as a template for new rdata
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_add(struct fcoe_ctlr *fip, struct fc_rport_priv *new)
+{
+	struct fc_lport *lport = fip->lp;
+	struct fc_rport_priv *rdata;
+	struct fc_rport_identifiers *ids;
+	struct fcoe_rport *frport;
+	u32 port_id;
+
+	port_id = new->ids.port_id;
+	if (port_id == fip->port_id)
+		return;
+
+	mutex_lock(&lport->disc.disc_mutex);
+	rdata = lport->tt.rport_create(lport, port_id);
+	if (!rdata) {
+		mutex_unlock(&lport->disc.disc_mutex);
+		return;
+	}
+
+	rdata->ops = &fcoe_ctlr_vn_rport_ops;
+	rdata->disc_id = lport->disc.disc_id;
+
+	ids = &rdata->ids;
+	if ((ids->port_name != -1 && ids->port_name != new->ids.port_name) ||
+	    (ids->node_name != -1 && ids->node_name != new->ids.node_name))
+		lport->tt.rport_logoff(rdata);
+	ids->port_name = new->ids.port_name;
+	ids->node_name = new->ids.node_name;
+	mutex_unlock(&lport->disc.disc_mutex);
+
+	frport = fcoe_ctlr_rport(rdata);
+	LIBFCOE_FIP_DBG(fip, "vn_add rport %6.6x %s\n",
+			port_id, frport->fcoe_len ? "old" : "new");
+	*frport = *fcoe_ctlr_rport(new);
+	frport->time = 0;
+}
+
+/**
+ * fcoe_ctlr_vn_lookup() - Find VN remote port's MAC address
+ * @fip: The FCoE controller
+ * @port_id:  The port_id of the remote VN_node
+ * @mac: buffer which will hold the VN_NODE destination MAC address, if found.
+ *
+ * Returns non-zero error if no remote port found.
+ */
+static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *fip, u32 port_id, u8 *mac)
+{
+	struct fc_lport *lport = fip->lp;
+	struct fc_rport_priv *rdata;
+	struct fcoe_rport *frport;
+	int ret = -1;
+
+	rcu_read_lock();
+	rdata = lport->tt.rport_lookup(lport, port_id);
+	if (rdata) {
+		frport = fcoe_ctlr_rport(rdata);
+		memcpy(mac, frport->enode_mac, ETH_ALEN);
+		ret = 0;
+	}
+	rcu_read_unlock();
+	return ret;
+}
+
+/**
+ * fcoe_ctlr_vn_claim_notify() - handle received FIP VN2VN Claim Notification
+ * @fip: The FCoE controller
+ * @new: newly-parsed remote port with frport as a template for new rdata
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_claim_notify(struct fcoe_ctlr *fip,
+				      struct fc_rport_priv *new)
+{
+	struct fcoe_rport *frport = fcoe_ctlr_rport(new);
+
+	if (frport->flags & FIP_FL_REC_OR_P2P) {
+		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
+		return;
+	}
+	switch (fip->state) {
+	case FIP_ST_VNMP_START:
+	case FIP_ST_VNMP_PROBE1:
+	case FIP_ST_VNMP_PROBE2:
+		if (new->ids.port_id == fip->port_id)
+			fcoe_ctlr_vn_restart(fip);
+		break;
+	case FIP_ST_VNMP_CLAIM:
+	case FIP_ST_VNMP_UP:
+		if (new->ids.port_id == fip->port_id) {
+			if (new->ids.port_name > fip->lp->wwpn) {
+				fcoe_ctlr_vn_restart(fip);
+				break;
+			}
+			fcoe_ctlr_vn_send_claim(fip);
+			break;
+		}
+		fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_REP, frport->enode_mac,
+				  min((u32)frport->fcoe_len,
+				      fcoe_ctlr_fcoe_size(fip)));
+		fcoe_ctlr_vn_add(fip, new);
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * fcoe_ctlr_vn_claim_resp() - handle received Claim Response
+ * @fip: The FCoE controller that received the frame
+ * @new: newly-parsed remote port with frport from the Claim Response
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_claim_resp(struct fcoe_ctlr *fip,
+				    struct fc_rport_priv *new)
+{
+	LIBFCOE_FIP_DBG(fip, "claim resp from from rport %x - state %s\n",
+			new->ids.port_id, fcoe_ctlr_state(fip->state));
+	if (fip->state == FIP_ST_VNMP_UP || fip->state == FIP_ST_VNMP_CLAIM)
+		fcoe_ctlr_vn_add(fip, new);
+}
+
+/**
+ * fcoe_ctlr_vn_beacon() - handle received beacon.
+ * @fip: The FCoE controller that received the frame
+ * @new: newly-parsed remote port with frport from the Beacon
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_beacon(struct fcoe_ctlr *fip,
+				struct fc_rport_priv *new)
+{
+	struct fc_lport *lport = fip->lp;
+	struct fc_rport_priv *rdata;
+	struct fcoe_rport *frport;
+
+	frport = fcoe_ctlr_rport(new);
+	if (frport->flags & FIP_FL_REC_OR_P2P) {
+		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
+		return;
+	}
+	mutex_lock(&lport->disc.disc_mutex);
+	rdata = lport->tt.rport_lookup(lport, new->ids.port_id);
+	if (rdata)
+		kref_get(&rdata->kref);
+	mutex_unlock(&lport->disc.disc_mutex);
+	if (rdata) {
+		if (rdata->ids.node_name == new->ids.node_name &&
+		    rdata->ids.port_name == new->ids.port_name) {
+			frport = fcoe_ctlr_rport(rdata);
+			if (!frport->time && fip->state == FIP_ST_VNMP_UP)
+				lport->tt.rport_login(rdata);
+			frport->time = jiffies;
+		}
+		kref_put(&rdata->kref, lport->tt.rport_destroy);
+		return;
+	}
+	if (fip->state != FIP_ST_VNMP_UP)
+		return;
+
+	/*
+	 * Beacon from a new neighbor.
+	 * Send a claim notify if one hasn't been sent recently.
+	 * Don't add the neighbor yet.
+	 */
+	LIBFCOE_FIP_DBG(fip, "beacon from new rport %x. sending claim notify\n",
+			new->ids.port_id);
+	if (time_after(jiffies,
+		       fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT)))
+		fcoe_ctlr_vn_send_claim(fip);
+}
+
+/**
+ * fcoe_ctlr_vn_age() - Check for VN_ports without recent beacons
+ * @fip: The FCoE controller
+ *
+ * Called with ctlr_mutex held.
+ * Called only in state FIP_ST_VNMP_UP.
+ * Returns the soonest time for next age-out or a time far in the future.
+ */
+static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip)
+{
+	struct fc_lport *lport = fip->lp;
+	struct fc_rport_priv *rdata;
+	struct fcoe_rport *frport;
+	unsigned long next_time;
+	unsigned long deadline;
+
+	next_time = jiffies + msecs_to_jiffies(FIP_VN_BEACON_INT * 10);
+	mutex_lock(&lport->disc.disc_mutex);
+	list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
+		frport = fcoe_ctlr_rport(rdata);
+		if (!frport->time)
+			continue;
+		deadline = frport->time +
+			   msecs_to_jiffies(FIP_VN_BEACON_INT * 25 / 10);
+		if (time_after_eq(jiffies, deadline)) {
+			frport->time = 0;
+			LIBFCOE_FIP_DBG(fip,
+				"port %16.16llx fc_id %6.6x beacon expired\n",
+				rdata->ids.port_name, rdata->ids.port_id);
+			lport->tt.rport_logoff(rdata);
+		} else if (time_before(deadline, next_time))
+			next_time = deadline;
+	}
+	mutex_unlock(&lport->disc.disc_mutex);
+	return next_time;
+}
+
+/**
+ * fcoe_ctlr_vn_recv() - Receive a FIP frame
+ * @fip: The FCoE controller that received the frame
+ * @skb: The received FIP frame
+ *
+ * Returns non-zero if the frame is dropped.
+ * Always consumes the frame.
+ */
+static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
+{
+	struct fip_header *fiph;
+	enum fip_vn2vn_subcode sub;
+	struct {
+		struct fc_rport_priv rdata;
+		struct fcoe_rport frport;
+	} buf;
+	int rc;
+
+	fiph = (struct fip_header *)skb->data;
+	sub = fiph->fip_subcode;
+
+	rc = fcoe_ctlr_vn_parse(fip, skb, &buf.rdata);
+	if (rc) {
+		LIBFCOE_FIP_DBG(fip, "vn_recv vn_parse error %d\n", rc);
+		goto drop;
+	}
+
+	mutex_lock(&fip->ctlr_mutex);
+	switch (sub) {
+	case FIP_SC_VN_PROBE_REQ:
+		fcoe_ctlr_vn_probe_req(fip, &buf.rdata);
+		break;
+	case FIP_SC_VN_PROBE_REP:
+		fcoe_ctlr_vn_probe_reply(fip, &buf.rdata);
+		break;
+	case FIP_SC_VN_CLAIM_NOTIFY:
+		fcoe_ctlr_vn_claim_notify(fip, &buf.rdata);
+		break;
+	case FIP_SC_VN_CLAIM_REP:
+		fcoe_ctlr_vn_claim_resp(fip, &buf.rdata);
+		break;
+	case FIP_SC_VN_BEACON:
+		fcoe_ctlr_vn_beacon(fip, &buf.rdata);
+		break;
+	default:
+		LIBFCOE_FIP_DBG(fip, "vn_recv unknown subcode %d\n", sub);
+		rc = -1;
+		break;
+	}
+	mutex_unlock(&fip->ctlr_mutex);
+drop:
+	kfree_skb(skb);
+	return rc;
+}
+
+/**
+ * fcoe_ctlr_disc_recv - discovery receive handler for VN2VN mode.
+ * @lport: The local port
+ * @fp: The received frame
+ *
+ * This should never be called since we don't see RSCNs or other
+ * fabric-generated ELSes.
+ */
+static void fcoe_ctlr_disc_recv(struct fc_lport *lport, struct fc_frame *fp)
+{
+	struct fc_seq_els_data rjt_data;
+
+	rjt_data.reason = ELS_RJT_UNSUP;
+	rjt_data.explan = ELS_EXPL_NONE;
+	lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
+	fc_frame_free(fp);
+}
+
+/**
+ * fcoe_ctlr_disc_recv - start discovery for VN2VN mode.
+ * @fip: The FCoE controller
+ *
+ * This sets a flag indicating that remote ports should be created
+ * and started for the peers we discover.  We use the disc_callback
+ * pointer as that flag.  Peers already discovered are created here.
+ *
+ * The lport lock is held during this call. The callback must be done
+ * later, without holding either the lport or discovery locks.
+ * The fcoe_ctlr lock may also be held during this call.
+ */
+static void fcoe_ctlr_disc_start(void (*callback)(struct fc_lport *,
+						  enum fc_disc_event),
+				 struct fc_lport *lport)
+{
+	struct fc_disc *disc = &lport->disc;
+	struct fcoe_ctlr *fip = disc->priv;
+
+	mutex_lock(&disc->disc_mutex);
+	disc->disc_callback = callback;
+	disc->disc_id = (disc->disc_id + 2) | 1;
+	disc->pending = 1;
+	schedule_work(&fip->timer_work);
+	mutex_unlock(&disc->disc_mutex);
+}
+
+/**
+ * fcoe_ctlr_vn_disc() - report FIP VN_port discovery results after claim state.
+ * @fip: The FCoE controller
+ *
+ * Starts the FLOGI and PLOGI login process to each discovered rport for which
+ * we've received at least one beacon.
+ * Performs the discovery complete callback.
+ */
+static void fcoe_ctlr_vn_disc(struct fcoe_ctlr *fip)
+{
+	struct fc_lport *lport = fip->lp;
+	struct fc_disc *disc = &lport->disc;
+	struct fc_rport_priv *rdata;
+	struct fcoe_rport *frport;
+	void (*callback)(struct fc_lport *, enum fc_disc_event);
+
+	mutex_lock(&disc->disc_mutex);
+	callback = disc->pending ? disc->disc_callback : NULL;
+	disc->pending = 0;
+	list_for_each_entry_rcu(rdata, &disc->rports, peers) {
+		frport = fcoe_ctlr_rport(rdata);
+		if (frport->time)
+			lport->tt.rport_login(rdata);
+	}
+	mutex_unlock(&disc->disc_mutex);
+	if (callback)
+		callback(lport, DISC_EV_SUCCESS);
+}
+
+/**
+ * fcoe_ctlr_vn_timeout - timer work function for VN2VN mode.
+ * @fip: The FCoE controller
+ */
+static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip)
+{
+	unsigned long next_time;
+	u8 mac[ETH_ALEN];
+	u32 new_port_id = 0;
+
+	mutex_lock(&fip->ctlr_mutex);
+	switch (fip->state) {
+	case FIP_ST_VNMP_START:
+		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE1);
+		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
+		next_time = jiffies + msecs_to_jiffies(FIP_VN_PROBE_WAIT);
+		break;
+	case FIP_ST_VNMP_PROBE1:
+		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE2);
+		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
+		next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
+		break;
+	case FIP_ST_VNMP_PROBE2:
+		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_CLAIM);
+		new_port_id = fip->port_id;
+		hton24(mac, FIP_VN_FC_MAP);
+		hton24(mac + 3, new_port_id);
+		fcoe_ctlr_map_dest(fip);
+		fip->update_mac(fip->lp, mac);
+		fcoe_ctlr_vn_send_claim(fip);
+		next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
+		break;
+	case FIP_ST_VNMP_CLAIM:
+		/*
+		 * This may be invoked either by starting discovery so don't
+		 * go to the next state unless it's been long enough.
+		 */
+		next_time = fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT);
+		if (time_after_eq(jiffies, next_time)) {
+			fcoe_ctlr_set_state(fip, FIP_ST_VNMP_UP);
+			fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON,
+					  fcoe_all_vn2vn, 0);
+			next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
+			fip->port_ka_time = next_time;
+		}
+		fcoe_ctlr_vn_disc(fip);
+		break;
+	case FIP_ST_VNMP_UP:
+		next_time = fcoe_ctlr_vn_age(fip);
+		if (time_after_eq(jiffies, fip->port_ka_time)) {
+			fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON,
+					  fcoe_all_vn2vn, 0);
+			fip->port_ka_time = jiffies +
+				 msecs_to_jiffies(FIP_VN_BEACON_INT +
+					(random32() % FIP_VN_BEACON_FUZZ));
+		}
+		if (time_before(fip->port_ka_time, next_time))
+			next_time = fip->port_ka_time;
+		break;
+	case FIP_ST_LINK_WAIT:
+		goto unlock;
+	default:
+		WARN(1, "unexpected state %d\n", fip->state);
+		goto unlock;
+	}
+	mod_timer(&fip->timer, next_time);
+unlock:
+	mutex_unlock(&fip->ctlr_mutex);
+
+	/* If port ID is new, notify local port after dropping ctlr_mutex */
+	if (new_port_id)
+		fc_lport_set_local_id(fip->lp, new_port_id);
+}
+
+/**
+ * fcoe_libfc_config() - Sets up libfc related properties for local port
+ * @lp: The local port to configure libfc for
+ * @fip: The FCoE controller in use by the local port
+ * @tt: The libfc function template
+ * @init_fcp: If non-zero, the FCP portion of libfc should be initialized
+ *
+ * Returns : 0 for success
+ */
+int fcoe_libfc_config(struct fc_lport *lport, struct fcoe_ctlr *fip,
+		      const struct libfc_function_template *tt, int init_fcp)
+{
+	/* Set the function pointers set by the LLDD */
+	memcpy(&lport->tt, tt, sizeof(*tt));
+	if (init_fcp && fc_fcp_init(lport))
+		return -ENOMEM;
+	fc_exch_init(lport);
+	fc_elsct_init(lport);
+	fc_lport_init(lport);
+	if (fip->mode == FIP_MODE_VN2VN)
+		lport->rport_priv_size = sizeof(struct fcoe_rport);
+	fc_rport_init(lport);
+	if (fip->mode == FIP_MODE_VN2VN) {
+		lport->point_to_multipoint = 1;
+		lport->tt.disc_recv_req = fcoe_ctlr_disc_recv;
+		lport->tt.disc_start = fcoe_ctlr_disc_start;
+		lport->tt.disc_stop = fcoe_ctlr_disc_stop;
+		lport->tt.disc_stop_final = fcoe_ctlr_disc_stop_final;
+		mutex_init(&lport->disc.disc_mutex);
+		INIT_LIST_HEAD(&lport->disc.rports);
+		lport->disc.priv = fip;
+	} else {
+		fc_disc_init(lport);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_libfc_config);
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
deleted file mode 100644
index a4757ca..0000000
--- a/drivers/scsi/fcoe/libfcoe.c
+++ /dev/null
@@ -1,2690 +0,0 @@
-/*
- * 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,
- * 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/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/errno.h>
-#include <linux/bitops.h>
-#include <linux/slab.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/fc/fc_fcp.h>
-
-#include <scsi/libfc.h>
-#include <scsi/libfcoe.h>
-
-#include "libfcoe.h"
-
-MODULE_AUTHOR("Open-FCoE.org");
-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_timer_work(struct work_struct *);
-static void fcoe_ctlr_recv_work(struct work_struct *);
-static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *);
-
-static void fcoe_ctlr_vn_start(struct fcoe_ctlr *);
-static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *, struct sk_buff *);
-static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *);
-static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *, u32, u8 *);
-
-static u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS;
-static u8 fcoe_all_enode[ETH_ALEN] = FIP_ALL_ENODE_MACS;
-static u8 fcoe_all_vn2vn[ETH_ALEN] = FIP_ALL_VN2VN_MACS;
-static u8 fcoe_all_p2p[ETH_ALEN] = FIP_ALL_P2P_MACS;
-
-unsigned int libfcoe_debug_logging;
-module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
-
-static const char *fcoe_ctlr_states[] = {
-	[FIP_ST_DISABLED] =	"DISABLED",
-	[FIP_ST_LINK_WAIT] =	"LINK_WAIT",
-	[FIP_ST_AUTO] =		"AUTO",
-	[FIP_ST_NON_FIP] =	"NON_FIP",
-	[FIP_ST_ENABLED] =	"ENABLED",
-	[FIP_ST_VNMP_START] =	"VNMP_START",
-	[FIP_ST_VNMP_PROBE1] =	"VNMP_PROBE1",
-	[FIP_ST_VNMP_PROBE2] =	"VNMP_PROBE2",
-	[FIP_ST_VNMP_CLAIM] =	"VNMP_CLAIM",
-	[FIP_ST_VNMP_UP] =	"VNMP_UP",
-};
-
-static const char *fcoe_ctlr_state(enum fip_state state)
-{
-	const char *cp = "unknown";
-
-	if (state < ARRAY_SIZE(fcoe_ctlr_states))
-		cp = fcoe_ctlr_states[state];
-	if (!cp)
-		cp = "unknown";
-	return cp;
-}
-
-/**
- * fcoe_ctlr_set_state() - Set and do debug printing for the new FIP state.
- * @fip: The FCoE controller
- * @state: The new state
- */
-static void fcoe_ctlr_set_state(struct fcoe_ctlr *fip, enum fip_state state)
-{
-	if (state == fip->state)
-		return;
-	if (fip->lp)
-		LIBFCOE_FIP_DBG(fip, "state %s -> %s\n",
-			fcoe_ctlr_state(fip->state), fcoe_ctlr_state(state));
-	fip->state = state;
-}
-
-/**
- * fcoe_ctlr_mtu_valid() - Check if a FCF's MTU is valid
- * @fcf: The FCF to check
- *
- * 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;
-}
-
-/**
- * fcoe_ctlr_fcf_usable() - Check if a FCF is usable
- * @fcf: The FCF to check
- *
- * 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_map_dest() - Set flag and OUI for mapping destination addresses
- * @fip: The FCoE controller
- */
-static void fcoe_ctlr_map_dest(struct fcoe_ctlr *fip)
-{
-	if (fip->mode == FIP_MODE_VN2VN)
-		hton24(fip->dest_addr, FIP_VN_FC_MAP);
-	else
-		hton24(fip->dest_addr, FIP_DEF_FC_MAP);
-	hton24(fip->dest_addr + 3, 0);
-	fip->map_dest = 1;
-}
-
-/**
- * fcoe_ctlr_init() - Initialize the FCoE Controller instance
- * @fip: The FCoE controller to initialize
- */
-void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode)
-{
-	fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
-	fip->mode = mode;
-	INIT_LIST_HEAD(&fip->fcfs);
-	mutex_init(&fip->ctlr_mutex);
-	spin_lock_init(&fip->ctlr_lock);
-	fip->flogi_oxid = FC_XID_UNKNOWN;
-	setup_timer(&fip->timer, fcoe_ctlr_timeout, (unsigned long)fip);
-	INIT_WORK(&fip->timer_work, fcoe_ctlr_timer_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: The FCoE controller whose FCFs are to be reset
- *
- * 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_ctlr_destroy() - Disable and tear down a FCoE controller
- * @fip: The FCoE controller to tear down
- *
- * 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)
-{
-	cancel_work_sync(&fip->recv_work);
-	skb_queue_purge(&fip->fip_recv_list);
-
-	mutex_lock(&fip->ctlr_mutex);
-	fcoe_ctlr_set_state(fip, FIP_ST_DISABLED);
-	fcoe_ctlr_reset_fcfs(fip);
-	mutex_unlock(&fip->ctlr_mutex);
-	del_timer_sync(&fip->timer);
-	cancel_work_sync(&fip->timer_work);
-}
-EXPORT_SYMBOL(fcoe_ctlr_destroy);
-
-/**
- * fcoe_ctlr_announce() - announce new FCF selection
- * @fip: The FCoE controller
- *
- * Also sets the destination MAC for FCoE and control packets
- *
- * Called with neither ctlr_mutex nor ctlr_lock held.
- */
-static void fcoe_ctlr_announce(struct fcoe_ctlr *fip)
-{
-	struct fcoe_fcf *sel;
-	struct fcoe_fcf *fcf;
-
-	mutex_lock(&fip->ctlr_mutex);
-	spin_lock_bh(&fip->ctlr_lock);
-
-	kfree_skb(fip->flogi_req);
-	fip->flogi_req = NULL;
-	list_for_each_entry(fcf, &fip->fcfs, list)
-		fcf->flogi_sent = 0;
-
-	spin_unlock_bh(&fip->ctlr_lock);
-	sel = fip->sel_fcf;
-
-	if (sel && !compare_ether_addr(sel->fcf_mac, fip->dest_addr))
-		goto unlock;
-	if (!is_zero_ether_addr(fip->dest_addr)) {
-		printk(KERN_NOTICE "libfcoe: host%d: "
-		       "FIP Fibre-Channel Forwarder MAC %pM deselected\n",
-		       fip->lp->host->host_no, fip->dest_addr);
-		memset(fip->dest_addr, 0, ETH_ALEN);
-	}
-	if (sel) {
-		printk(KERN_INFO "libfcoe: host%d: FIP selected "
-		       "Fibre-Channel Forwarder MAC %pM\n",
-		       fip->lp->host->host_no, sel->fcf_mac);
-		memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
-		fip->map_dest = 0;
-	}
-unlock:
-	mutex_unlock(&fip->ctlr_mutex);
-}
-
-/**
- * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port
- * @fip: The FCoE controller to get the maximum FCoE size from
- *
- * 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 FIP solicitation
- * @fip: The FCoE controller to send the solicitation on
- * @fcf: The 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);
-	if (fip->spma)
-		sol->fip.fip_flags |= htons(FIP_FL_SPMA);
-
-	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_FIP);
-	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: The FCoE controller to start
- *
- * Called from the LLD when the network link is ready.
- */
-void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
-{
-	mutex_lock(&fip->ctlr_mutex);
-	if (fip->state == FIP_ST_NON_FIP || fip->state == FIP_ST_AUTO) {
-		mutex_unlock(&fip->ctlr_mutex);
-		fc_linkup(fip->lp);
-	} else if (fip->state == FIP_ST_LINK_WAIT) {
-		fcoe_ctlr_set_state(fip, fip->mode);
-		switch (fip->mode) {
-		default:
-			LIBFCOE_FIP_DBG(fip, "invalid mode %d\n", fip->mode);
-			/* fall-through */
-		case FIP_MODE_AUTO:
-			LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n");
-			/* fall-through */
-		case FIP_MODE_FABRIC:
-		case FIP_MODE_NON_FIP:
-			mutex_unlock(&fip->ctlr_mutex);
-			fc_linkup(fip->lp);
-			fcoe_ctlr_solicit(fip, NULL);
-			break;
-		case FIP_MODE_VN2VN:
-			fcoe_ctlr_vn_start(fip);
-			mutex_unlock(&fip->ctlr_mutex);
-			fc_linkup(fip->lp);
-			break;
-		}
-	} else
-		mutex_unlock(&fip->ctlr_mutex);
-}
-EXPORT_SYMBOL(fcoe_ctlr_link_up);
-
-/**
- * fcoe_ctlr_reset() - Reset a FCoE controller
- * @fip:       The FCoE controller to reset
- */
-static void fcoe_ctlr_reset(struct fcoe_ctlr *fip)
-{
-	fcoe_ctlr_reset_fcfs(fip);
-	del_timer(&fip->timer);
-	fip->ctlr_ka_time = 0;
-	fip->port_ka_time = 0;
-	fip->sol_time = 0;
-	fip->flogi_oxid = FC_XID_UNKNOWN;
-	fcoe_ctlr_map_dest(fip);
-}
-
-/**
- * fcoe_ctlr_link_down() - Stop a FCoE controller
- * @fip: The FCoE controller to be stopped
- *
- * 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)
-{
-	int link_dropped;
-
-	LIBFCOE_FIP_DBG(fip, "link down.\n");
-	mutex_lock(&fip->ctlr_mutex);
-	fcoe_ctlr_reset(fip);
-	link_dropped = fip->state != FIP_ST_LINK_WAIT;
-	fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
-	mutex_unlock(&fip->ctlr_mutex);
-
-	if (link_dropped)
-		fc_linkdown(fip->lp);
-	return link_dropped;
-}
-EXPORT_SYMBOL(fcoe_ctlr_link_down);
-
-/**
- * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF
- * @fip:   The FCoE controller to send the FKA on
- * @lport: libfc fc_lport to send from
- * @ports: 0 for controller keep-alive, 1 for port keep-alive
- * @sa:	   The 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,
-				      struct fc_lport *lport,
-				      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 || (ports && !lp->port_id))
-		return;
-
-	len = sizeof(*kal) + ports * 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);
-	if (fip->spma)
-		kal->fip.fip_flags |= htons(FIP_FL_SPMA);
-
-	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->get_src_addr(lport), ETH_ALEN);
-		hton24(vn->fd_fc_id, lport->port_id);
-		put_unaligned_be64(lport->wwpn, &vn->fd_wwpn);
-	}
-	skb_put(skb, len);
-	skb->protocol = htons(ETH_P_FIP);
-	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:   The FCoE controller for the ELS frame
- * @dtype: The FIP descriptor type for the frame
- * @skb:   The FCoE ELS frame including FC header but no FCoE headers
- * @d_id:  The destination port ID.
- *
- * 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, struct fc_lport *lport,
-			    u8 dtype, struct sk_buff *skb, u32 d_id)
-{
-	struct fip_encaps_head {
-		struct ethhdr eth;
-		struct fip_header fip;
-		struct fip_encaps encaps;
-	} __attribute__((packed)) *cap;
-	struct fc_frame_header *fh;
-	struct fip_mac_desc *mac;
-	struct fcoe_fcf *fcf;
-	size_t dlen;
-	u16 fip_flags;
-	u8 op;
-
-	fh = (struct fc_frame_header *)skb->data;
-	op = *(u8 *)(fh + 1);
-	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));
-
-	if (lport->point_to_multipoint) {
-		if (fcoe_ctlr_vn_lookup(fip, d_id, cap->eth.h_dest))
-			return -ENODEV;
-		fip_flags = 0;
-	} else {
-		fcf = fip->sel_fcf;
-		if (!fcf)
-			return -ENODEV;
-		fip_flags = fcf->flags;
-		fip_flags &= fip->spma ? FIP_FL_SPMA | FIP_FL_FPMA :
-					 FIP_FL_FPMA;
-		if (!fip_flags)
-			return -ENODEV;
-		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);
-	if (op == ELS_LS_ACC || op == ELS_LS_RJT)
-		cap->fip.fip_subcode = FIP_SC_REP;
-	else
-		cap->fip.fip_subcode = FIP_SC_REQ;
-	cap->fip.fip_flags = htons(fip_flags);
-
-	cap->encaps.fd_desc.fip_dtype = dtype;
-	cap->encaps.fd_desc.fip_dlen = dlen / FIP_BPW;
-
-	if (op != ELS_LS_RJT) {
-		dlen += sizeof(*mac);
-		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 != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) {
-			memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
-		} else if (fip->mode == FIP_MODE_VN2VN) {
-			hton24(mac->fd_mac, FIP_VN_FC_MAP);
-			hton24(mac->fd_mac + 3, fip->port_id);
-		} else if (fip_flags & FIP_FL_SPMA) {
-			LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with SPMA\n");
-			memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN);
-		} else {
-			LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with FPMA\n");
-			/* FPMA only FLOGI.  Must leave the MAC desc zeroed. */
-		}
-	}
-	cap->fip.fip_dl_len = htons(dlen / FIP_BPW);
-
-	skb->protocol = htons(ETH_P_FIP);
-	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.
- * @lport:	libfc fc_lport to send from
- * @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).
- * The the skb must also be an fc_frame.
- *
- * This is called from the lower-level driver with spinlocks held,
- * so we must not take a mutex here.
- */
-int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
-		       struct sk_buff *skb)
-{
-	struct fc_frame *fp;
-	struct fc_frame_header *fh;
-	u16 old_xid;
-	u8 op;
-	u8 mac[ETH_ALEN];
-
-	fp = container_of(skb, struct fc_frame, skb);
-	fh = (struct fc_frame_header *)skb->data;
-	op = *(u8 *)(fh + 1);
-
-	if (op == ELS_FLOGI && fip->mode != FIP_MODE_VN2VN) {
-		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;
-			fcoe_ctlr_map_dest(fip);
-			return 0;
-		}
-		if (fip->state == FIP_ST_NON_FIP)
-			fcoe_ctlr_map_dest(fip);
-	}
-
-	if (fip->state == FIP_ST_NON_FIP)
-		return 0;
-	if (!fip->sel_fcf && fip->mode != FIP_MODE_VN2VN)
-		goto drop;
-	switch (op) {
-	case ELS_FLOGI:
-		op = FIP_DT_FLOGI;
-		if (fip->mode == FIP_MODE_VN2VN)
-			break;
-		spin_lock_bh(&fip->ctlr_lock);
-		kfree_skb(fip->flogi_req);
-		fip->flogi_req = skb;
-		fip->flogi_req_send = 1;
-		spin_unlock_bh(&fip->ctlr_lock);
-		schedule_work(&fip->timer_work);
-		return -EINPROGRESS;
-	case ELS_FDISC:
-		if (ntoh24(fh->fh_s_id))
-			return 0;
-		op = FIP_DT_FDISC;
-		break;
-	case ELS_LOGO:
-		if (fip->mode == FIP_MODE_VN2VN) {
-			if (fip->state != FIP_ST_VNMP_UP)
-				return -EINVAL;
-			if (ntoh24(fh->fh_d_id) == FC_FID_FLOGI)
-				return -EINVAL;
-		} else {
-			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 non-FIP, we may have 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.
-		 */
-		if (fip->state == FIP_ST_NON_FIP) {
-			if (fip->flogi_oxid == FC_XID_UNKNOWN)
-				return 0;
-			fip->flogi_oxid = FC_XID_UNKNOWN;
-			fc_fcoe_set_mac(mac, fh->fh_d_id);
-			fip->update_mac(lport, mac);
-		}
-		/* fall through */
-	case ELS_LS_RJT:
-		op = fr_encaps(fp);
-		if (op)
-			break;
-		return 0;
-	default:
-		if (fip->state != FIP_ST_ENABLED &&
-		    fip->state != FIP_ST_VNMP_UP)
-			goto drop;
-		return 0;
-	}
-	LIBFCOE_FIP_DBG(fip, "els_send op %u d_id %x\n",
-			op, ntoh24(fh->fh_d_id));
-	if (fcoe_ctlr_encaps(fip, lport, op, skb, ntoh24(fh->fh_d_id)))
-		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: The FCoE controller to free FCFs on
- *
- * Called with lock held and preemption disabled.
- *
- * An FCF is considered old if we have missed two advertisements.
- * That is, there have been no valid advertisement from it for 2.5
- * times its keep-alive period.
- *
- * In addition, determine the time when an FCF selection can occur.
- *
- * Also, increment the MissDiscAdvCount when no advertisement is received
- * for the corresponding FCF for 1.5 * FKA_ADV_PERIOD (FC-BB-5 LESB).
- *
- * Returns the time in jiffies for the next call.
- */
-static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
-{
-	struct fcoe_fcf *fcf;
-	struct fcoe_fcf *next;
-	unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
-	unsigned long deadline;
-	unsigned long sel_time = 0;
-	struct fcoe_dev_stats *stats;
-
-	stats = per_cpu_ptr(fip->lp->dev_stats, get_cpu());
-
-	list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
-		deadline = fcf->time + fcf->fka_period + fcf->fka_period / 2;
-		if (fip->sel_fcf == fcf) {
-			if (time_after(jiffies, deadline)) {
-				stats->MissDiscAdvCount++;
-				printk(KERN_INFO "libfcoe: host%d: "
-				       "Missing Discovery Advertisement "
-				       "for fab %16.16llx count %lld\n",
-				       fip->lp->host->host_no, fcf->fabric_name,
-				       stats->MissDiscAdvCount);
-			} else if (time_after(next_timer, deadline))
-				next_timer = deadline;
-		}
-
-		deadline += fcf->fka_period;
-		if (time_after_eq(jiffies, deadline)) {
-			if (fip->sel_fcf == fcf)
-				fip->sel_fcf = NULL;
-			list_del(&fcf->list);
-			WARN_ON(!fip->fcf_count);
-			fip->fcf_count--;
-			kfree(fcf);
-			stats->VLinkFailureCount++;
-		} else {
-			if (time_after(next_timer, deadline))
-				next_timer = deadline;
-			if (fcoe_ctlr_mtu_valid(fcf) &&
-			    (!sel_time || time_before(sel_time, fcf->time)))
-				sel_time = fcf->time;
-		}
-	}
-	put_cpu();
-	if (sel_time && !fip->sel_fcf && !fip->sel_time) {
-		sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY);
-		fip->sel_time = sel_time;
-	}
-
-	return next_timer;
-}
-
-/**
- * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry
- * @fip: The FCoE controller receiving the advertisement
- * @skb: The received FIP advertisement frame
- * @fcf: The resulting FCF entry
- *
- * Returns zero on a valid parsed advertisement,
- * otherwise returns non zero value.
- */
-static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
-			       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;
-	u32 desc_mask;
-
-	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);
-
-	/*
-	 * mask of required descriptors. validating each one clears its bit.
-	 */
-	desc_mask = BIT(FIP_DT_PRI) | BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) |
-			BIT(FIP_DT_FAB) | BIT(FIP_DT_FKA);
-
-	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;
-		/* Drop Adv if there are duplicate critical descriptors */
-		if ((desc->fip_dtype < 32) &&
-		    !(desc_mask & 1U << desc->fip_dtype)) {
-			LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
-					"Descriptors in FIP adv\n");
-			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;
-			desc_mask &= ~BIT(FIP_DT_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)) {
-				LIBFCOE_FIP_DBG(fip,
-					"Invalid MAC addr %pM in FIP adv\n",
-					fcf->fcf_mac);
-				return -EINVAL;
-			}
-			desc_mask &= ~BIT(FIP_DT_MAC);
-			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);
-			desc_mask &= ~BIT(FIP_DT_NAME);
-			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);
-			desc_mask &= ~BIT(FIP_DT_FAB);
-			break;
-		case FIP_DT_FKA:
-			if (dlen != sizeof(struct fip_fka_desc))
-				goto len_err;
-			fka = (struct fip_fka_desc *)desc;
-			if (fka->fd_flags & FIP_FKA_ADV_D)
-				fcf->fd_flags = 1;
-			t = ntohl(fka->fd_fka_period);
-			if (t >= FCOE_CTLR_MIN_FKA)
-				fcf->fka_period = msecs_to_jiffies(t);
-			desc_mask &= ~BIT(FIP_DT_FKA);
-			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:
-			LIBFCOE_FIP_DBG(fip, "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;
-			break;
-		}
-		desc = (struct fip_desc *)((char *)desc + dlen);
-		rlen -= dlen;
-	}
-	if (!fcf->fc_map || (fcf->fc_map & 0x10000))
-		return -EINVAL;
-	if (!fcf->switch_name)
-		return -EINVAL;
-	if (desc_mask) {
-		LIBFCOE_FIP_DBG(fip, "adv missing descriptors mask %x\n",
-				desc_mask);
-		return -EINVAL;
-	}
-	return 0;
-
-len_err:
-	LIBFCOE_FIP_DBG(fip, "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: The FCoE controller receiving the advertisement
- * @skb: The 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(fip, skb, &new))
-		return;
-
-	mutex_lock(&fip->ctlr_mutex);
-	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 {
-		/*
-		 * Update the FCF's keep-alive descriptor flags.
-		 * Other flag changes from new advertisements are
-		 * ignored after a solicited advertisement is
-		 * received and the FCF is selectable (usable).
-		 */
-		fcf->fd_flags = new.fd_flags;
-		if (!fcoe_ctlr_fcf_usable(fcf))
-			fcf->flags = new.flags;
-
-		if (fcf == fip->sel_fcf && !fcf->fd_flags) {
-			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);
-		}
-		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;
-	if (!found)
-		LIBFCOE_FIP_DBG(fip, "New FCF fab %16.16llx mac %pM\n",
-				fcf->fabric_name, fcf->fcf_mac);
-
-	/*
-	 * 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);
-
-	/*
-	 * Put this FCF at the head of the list for priority among equals.
-	 * This helps in the case of an NPV switch which insists we use
-	 * the FCF that answers multicast solicitations, not the others that
-	 * are sending periodic multicast advertisements.
-	 */
-	if (mtu_valid) {
-		list_del(&fcf->list);
-		list_add(&fcf->list, &fip->fcfs);
-	}
-
-	/*
-	 * If this is the first validated FCF, note the time and
-	 * set a timer to trigger selection.
-	 */
-	if (mtu_valid && !fip->sel_fcf && 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:
-	mutex_unlock(&fip->ctlr_mutex);
-}
-
-/**
- * fcoe_ctlr_recv_els() - Handle an incoming FIP encapsulated ELS frame
- * @fip: The FCoE controller which received the packet
- * @skb: The received FIP packet
- */
-static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
-{
-	struct fc_lport *lport = fip->lp;
-	struct fip_header *fiph;
-	struct fc_frame *fp = (struct fc_frame *)skb;
-	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;
-	u32 desc_mask = 0;
-	u32 desc_cnt = 0;
-
-	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) {
-		desc_cnt++;
-		dlen = desc->fip_dlen * FIP_BPW;
-		if (dlen < sizeof(*desc) || dlen > rlen)
-			goto drop;
-		/* Drop ELS if there are duplicate critical descriptors */
-		if (desc->fip_dtype < 32) {
-			if (desc_mask & 1U << desc->fip_dtype) {
-				LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
-						"Descriptors in FIP ELS\n");
-				goto drop;
-			}
-			desc_mask |= (1 << desc->fip_dtype);
-		}
-		switch (desc->fip_dtype) {
-		case FIP_DT_MAC:
-			if (desc_cnt == 1) {
-				LIBFCOE_FIP_DBG(fip, "FIP descriptors "
-						"received out of order\n");
-				goto drop;
-			}
-
-			if (dlen != sizeof(struct fip_mac_desc))
-				goto len_err;
-			memcpy(granted_mac,
-			       ((struct fip_mac_desc *)desc)->fd_mac,
-			       ETH_ALEN);
-			break;
-		case FIP_DT_FLOGI:
-		case FIP_DT_FDISC:
-		case FIP_DT_LOGO:
-		case FIP_DT_ELP:
-			if (desc_cnt != 1) {
-				LIBFCOE_FIP_DBG(fip, "FIP descriptors "
-						"received out of order\n");
-				goto drop;
-			}
-			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:
-			LIBFCOE_FIP_DBG(fip, "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;
-			if (desc_cnt <= 2) {
-				LIBFCOE_FIP_DBG(fip, "FIP descriptors "
-						"received out of order\n");
-				goto drop;
-			}
-			break;
-		}
-		desc = (struct fip_desc *)((char *)desc + dlen);
-		rlen -= dlen;
-	}
-
-	if (!fh)
-		goto drop;
-	els_op = *(u8 *)(fh + 1);
-
-	if ((els_dtype == FIP_DT_FLOGI || els_dtype == FIP_DT_FDISC) &&
-	    sub == FIP_SC_REP && fip->mode != FIP_MODE_VN2VN) {
-		if (els_op == ELS_LS_ACC) {
-			if (!is_valid_ether_addr(granted_mac)) {
-				LIBFCOE_FIP_DBG(fip,
-					"Invalid MAC address %pM in FIP ELS\n",
-					granted_mac);
-				goto drop;
-			}
-			memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN);
-
-			if (fip->flogi_oxid == ntohs(fh->fh_ox_id)) {
-				fip->flogi_oxid = FC_XID_UNKNOWN;
-				if (els_dtype == FIP_DT_FLOGI)
-					fcoe_ctlr_announce(fip);
-			}
-		} else if (els_dtype == FIP_DT_FLOGI &&
-			   !fcoe_ctlr_flogi_retry(fip))
-			goto drop;	/* retrying FLOGI so drop reject */
-	}
-
-	if ((desc_cnt == 0) || ((els_op != ELS_LS_RJT) &&
-	    (!(1U << FIP_DT_MAC & desc_mask)))) {
-		LIBFCOE_FIP_DBG(fip, "Missing critical descriptors "
-				"in FIP ELS\n");
-		goto drop;
-	}
-
-	/*
-	 * 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) = lport;
-	fr_encaps(fp) = els_dtype;
-
-	stats = per_cpu_ptr(lport->dev_stats, get_cpu());
-	stats->RxFrames++;
-	stats->RxWords += skb->len / FIP_BPW;
-	put_cpu();
-
-	fc_exch_recv(lport, fp);
-	return;
-
-len_err:
-	LIBFCOE_FIP_DBG(fip, "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: The FCoE controller that received the frame
- * @fh:	 The 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 *lport = fip->lp;
-	struct fc_lport *vn_port = NULL;
-	u32 desc_mask;
-	int is_vn_port = 0;
-
-	LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n");
-
-	if (!fcf || !lport->port_id)
-		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;
-		/* Drop CVL if there are duplicate critical descriptors */
-		if ((desc->fip_dtype < 32) &&
-		    !(desc_mask & 1U << desc->fip_dtype)) {
-			LIBFCOE_FIP_DBG(fip, "Duplicate Critical "
-					"Descriptors in FIP CVL\n");
-			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->get_src_addr(lport)) == 0 &&
-			    get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn &&
-			    ntoh24(vp->fd_fc_id) == lport->port_id) {
-				desc_mask &= ~BIT(FIP_DT_VN_ID);
-				break;
-			}
-			/* check if clr_vlink is for NPIV port */
-			mutex_lock(&lport->lp_mutex);
-			list_for_each_entry(vn_port, &lport->vports, list) {
-				if (compare_ether_addr(vp->fd_mac,
-				    fip->get_src_addr(vn_port)) == 0 &&
-				    (get_unaligned_be64(&vp->fd_wwpn)
-							== vn_port->wwpn) &&
-				    (ntoh24(vp->fd_fc_id) ==
-					    fc_host_port_id(vn_port->host))) {
-					desc_mask &= ~BIT(FIP_DT_VN_ID);
-					is_vn_port = 1;
-					break;
-				}
-			}
-			mutex_unlock(&lport->lp_mutex);
-
-			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) {
-		LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n",
-				desc_mask);
-	} else {
-		LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n");
-
-		if (is_vn_port)
-			fc_lport_reset(vn_port);
-		else {
-			mutex_lock(&fip->ctlr_mutex);
-			per_cpu_ptr(lport->dev_stats,
-				    get_cpu())->VLinkFailureCount++;
-			put_cpu();
-			fcoe_ctlr_reset(fip);
-			mutex_unlock(&fip->ctlr_mutex);
-
-			fc_lport_reset(fip->lp);
-			fcoe_ctlr_solicit(fip, NULL);
-		}
-	}
-}
-
-/**
- * fcoe_ctlr_recv() - Receive a FIP packet
- * @fip: The FCoE controller that received the packet
- * @skb: The received FIP packet
- *
- * This may be called from either NET_RX_SOFTIRQ or IRQ.
- */
-void fcoe_ctlr_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
-{
-	skb_queue_tail(&fip->fip_recv_list, skb);
-	schedule_work(&fip->recv_work);
-}
-EXPORT_SYMBOL(fcoe_ctlr_recv);
-
-/**
- * fcoe_ctlr_recv_handler() - Receive a FIP frame
- * @fip: The FCoE controller that received the frame
- * @skb: The received FIP frame
- *
- * 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 (fip->mode == FIP_MODE_VN2VN) {
-		if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) &&
-		    compare_ether_addr(eh->h_dest, fcoe_all_vn2vn) &&
-		    compare_ether_addr(eh->h_dest, fcoe_all_p2p))
-			goto drop;
-	} else if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) &&
-		   compare_ether_addr(eh->h_dest, fcoe_all_enode))
-		goto drop;
-	fiph = (struct fip_header *)skb->data;
-	op = ntohs(fiph->fip_op);
-	sub = fiph->fip_subcode;
-
-	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;
-
-	mutex_lock(&fip->ctlr_mutex);
-	state = fip->state;
-	if (state == FIP_ST_AUTO) {
-		fip->map_dest = 0;
-		fcoe_ctlr_set_state(fip, FIP_ST_ENABLED);
-		state = FIP_ST_ENABLED;
-		LIBFCOE_FIP_DBG(fip, "Using FIP mode\n");
-	}
-	mutex_unlock(&fip->ctlr_mutex);
-
-	if (fip->mode == FIP_MODE_VN2VN && op == FIP_OP_VN2VN)
-		return fcoe_ctlr_vn_recv(fip, skb);
-
-	if (state != FIP_ST_ENABLED && state != FIP_ST_VNMP_UP &&
-	    state != FIP_ST_VNMP_CLAIM)
-		goto drop;
-
-	if (op == FIP_OP_LS) {
-		fcoe_ctlr_recv_els(fip, skb);	/* consumes skb */
-		return 0;
-	}
-
-	if (state != FIP_ST_ENABLED)
-		goto drop;
-
-	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: The FCoE controller
- *
- * Returns the selected FCF, or NULL if none are usable.
- *
- * If there are conflicting advertisements, no FCF can be chosen.
- *
- * If there is already a selected FCF, this will choose a better one or
- * an equivalent one that hasn't already been sent a FLOGI.
- *
- * Called with lock held.
- */
-static struct fcoe_fcf *fcoe_ctlr_select(struct fcoe_ctlr *fip)
-{
-	struct fcoe_fcf *fcf;
-	struct fcoe_fcf *best = fip->sel_fcf;
-	struct fcoe_fcf *first;
-
-	first = list_first_entry(&fip->fcfs, struct fcoe_fcf, list);
-
-	list_for_each_entry(fcf, &fip->fcfs, list) {
-		LIBFCOE_FIP_DBG(fip, "consider FCF fab %16.16llx "
-				"VFID %d mac %pM map %x val %d "
-				"sent %u pri %u\n",
-				fcf->fabric_name, fcf->vfid, fcf->fcf_mac,
-				fcf->fc_map, fcoe_ctlr_mtu_valid(fcf),
-				fcf->flogi_sent, fcf->pri);
-		if (fcf->fabric_name != first->fabric_name ||
-		    fcf->vfid != first->vfid ||
-		    fcf->fc_map != first->fc_map) {
-			LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, "
-					"or FC-MAP\n");
-			return NULL;
-		}
-		if (fcf->flogi_sent)
-			continue;
-		if (!fcoe_ctlr_fcf_usable(fcf)) {
-			LIBFCOE_FIP_DBG(fip, "FCF for fab %16.16llx "
-					"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 || fcf->pri < best->pri || best->flogi_sent)
-			best = fcf;
-	}
-	fip->sel_fcf = best;
-	if (best) {
-		LIBFCOE_FIP_DBG(fip, "using FCF mac %pM\n", best->fcf_mac);
-		fip->port_ka_time = jiffies +
-			msecs_to_jiffies(FIP_VN_KA_PERIOD);
-		fip->ctlr_ka_time = jiffies + best->fka_period;
-		if (time_before(fip->ctlr_ka_time, fip->timer.expires))
-			mod_timer(&fip->timer, fip->ctlr_ka_time);
-	}
-	return best;
-}
-
-/**
- * fcoe_ctlr_flogi_send_locked() - send FIP-encapsulated FLOGI to current FCF
- * @fip: The FCoE controller
- *
- * Returns non-zero error if it could not be sent.
- *
- * Called with ctlr_mutex and ctlr_lock held.
- * Caller must verify that fip->sel_fcf is not NULL.
- */
-static int fcoe_ctlr_flogi_send_locked(struct fcoe_ctlr *fip)
-{
-	struct sk_buff *skb;
-	struct sk_buff *skb_orig;
-	struct fc_frame_header *fh;
-	int error;
-
-	skb_orig = fip->flogi_req;
-	if (!skb_orig)
-		return -EINVAL;
-
-	/*
-	 * Clone and send the FLOGI request.  If clone fails, use original.
-	 */
-	skb = skb_clone(skb_orig, GFP_ATOMIC);
-	if (!skb) {
-		skb = skb_orig;
-		fip->flogi_req = NULL;
-	}
-	fh = (struct fc_frame_header *)skb->data;
-	error = fcoe_ctlr_encaps(fip, fip->lp, FIP_DT_FLOGI, skb,
-				 ntoh24(fh->fh_d_id));
-	if (error) {
-		kfree_skb(skb);
-		return error;
-	}
-	fip->send(fip, skb);
-	fip->sel_fcf->flogi_sent = 1;
-	return 0;
-}
-
-/**
- * fcoe_ctlr_flogi_retry() - resend FLOGI request to a new FCF if possible
- * @fip: The FCoE controller
- *
- * Returns non-zero error code if there's no FLOGI request to retry or
- * no alternate FCF available.
- */
-static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *fip)
-{
-	struct fcoe_fcf *fcf;
-	int error;
-
-	mutex_lock(&fip->ctlr_mutex);
-	spin_lock_bh(&fip->ctlr_lock);
-	LIBFCOE_FIP_DBG(fip, "re-sending FLOGI - reselect\n");
-	fcf = fcoe_ctlr_select(fip);
-	if (!fcf || fcf->flogi_sent) {
-		kfree_skb(fip->flogi_req);
-		fip->flogi_req = NULL;
-		error = -ENOENT;
-	} else {
-		fcoe_ctlr_solicit(fip, NULL);
-		error = fcoe_ctlr_flogi_send_locked(fip);
-	}
-	spin_unlock_bh(&fip->ctlr_lock);
-	mutex_unlock(&fip->ctlr_mutex);
-	return error;
-}
-
-
-/**
- * fcoe_ctlr_flogi_send() - Handle sending of FIP FLOGI.
- * @fip: The FCoE controller that timed out
- *
- * Done here because fcoe_ctlr_els_send() can't get mutex.
- *
- * Called with ctlr_mutex held.  The caller must not hold ctlr_lock.
- */
-static void fcoe_ctlr_flogi_send(struct fcoe_ctlr *fip)
-{
-	struct fcoe_fcf *fcf;
-
-	spin_lock_bh(&fip->ctlr_lock);
-	fcf = fip->sel_fcf;
-	if (!fcf || !fip->flogi_req_send)
-		goto unlock;
-
-	LIBFCOE_FIP_DBG(fip, "sending FLOGI\n");
-
-	/*
-	 * If this FLOGI is being sent due to a timeout retry
-	 * to the same FCF as before, select a different FCF if possible.
-	 */
-	if (fcf->flogi_sent) {
-		LIBFCOE_FIP_DBG(fip, "sending FLOGI - reselect\n");
-		fcf = fcoe_ctlr_select(fip);
-		if (!fcf || fcf->flogi_sent) {
-			LIBFCOE_FIP_DBG(fip, "sending FLOGI - clearing\n");
-			list_for_each_entry(fcf, &fip->fcfs, list)
-				fcf->flogi_sent = 0;
-			fcf = fcoe_ctlr_select(fip);
-		}
-	}
-	if (fcf) {
-		fcoe_ctlr_flogi_send_locked(fip);
-		fip->flogi_req_send = 0;
-	} else /* XXX */
-		LIBFCOE_FIP_DBG(fip, "No FCF selected - defer send\n");
-unlock:
-	spin_unlock_bh(&fip->ctlr_lock);
-}
-
-/**
- * fcoe_ctlr_timeout() - FIP timeout handler
- * @arg: The FCoE controller that timed out
- */
-static void fcoe_ctlr_timeout(unsigned long arg)
-{
-	struct fcoe_ctlr *fip = (struct fcoe_ctlr *)arg;
-
-	schedule_work(&fip->timer_work);
-}
-
-/**
- * fcoe_ctlr_timer_work() - Worker thread function for timer work
- * @work: Handle to a FCoE controller
- *
- * Ages FCFs.  Triggers FCF selection if possible.
- * Sends keep-alives and resets.
- */
-static void fcoe_ctlr_timer_work(struct work_struct *work)
-{
-	struct fcoe_ctlr *fip;
-	struct fc_lport *vport;
-	u8 *mac;
-	u8 reset = 0;
-	u8 send_ctlr_ka = 0;
-	u8 send_port_ka = 0;
-	struct fcoe_fcf *sel;
-	struct fcoe_fcf *fcf;
-	unsigned long next_timer;
-
-	fip = container_of(work, struct fcoe_ctlr, timer_work);
-	if (fip->mode == FIP_MODE_VN2VN)
-		return fcoe_ctlr_vn_timeout(fip);
-	mutex_lock(&fip->ctlr_mutex);
-	if (fip->state == FIP_ST_DISABLED) {
-		mutex_unlock(&fip->ctlr_mutex);
-		return;
-	}
-
-	fcf = fip->sel_fcf;
-	next_timer = fcoe_ctlr_age_fcfs(fip);
-
-	sel = fip->sel_fcf;
-	if (!sel && fip->sel_time) {
-		if (time_after_eq(jiffies, fip->sel_time)) {
-			sel = fcoe_ctlr_select(fip);
-			fip->sel_time = 0;
-		} else if (time_after(next_timer, fip->sel_time))
-			next_timer = fip->sel_time;
-	}
-
-	if (sel && fip->flogi_req_send)
-		fcoe_ctlr_flogi_send(fip);
-	else if (!sel && fcf)
-		reset = 1;
-
-	if (sel && !sel->fd_flags) {
-		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;
-	}
-	if (!list_empty(&fip->fcfs))
-		mod_timer(&fip->timer, next_timer);
-	mutex_unlock(&fip->ctlr_mutex);
-
-	if (reset) {
-		fc_lport_reset(fip->lp);
-		/* restart things with a solicitation */
-		fcoe_ctlr_solicit(fip, NULL);
-	}
-
-	if (send_ctlr_ka)
-		fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr);
-
-	if (send_port_ka) {
-		mutex_lock(&fip->lp->lp_mutex);
-		mac = fip->get_src_addr(fip->lp);
-		fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac);
-		list_for_each_entry(vport, &fip->lp->vports, list) {
-			mac = fip->get_src_addr(vport);
-			fcoe_ctlr_send_keep_alive(fip, vport, 1, mac);
-		}
-		mutex_unlock(&fip->lp->lp_mutex);
-	}
-}
-
-/**
- * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames
- * @recv_work: Handle to a FCoE controller
- */
-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);
-	while ((skb = skb_dequeue(&fip->fip_recv_list)))
-		fcoe_ctlr_recv_handler(fip, skb);
-}
-
-/**
- * fcoe_ctlr_recv_flogi() - Snoop pre-FIP receipt of FLOGI response
- * @fip: The FCoE controller
- * @fp:	 The FC frame to snoop
- *
- * 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.
- * Fill in the granted_mac address.
- *
- * Return non-zero if the frame should not be delivered to libfc.
- */
-int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
-			 struct fc_frame *fp)
-{
-	struct fc_frame_header *fh;
-	u8 op;
-	u8 *sa;
-
-	sa = eth_hdr(&fp->skb)->h_source;
-	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)) {
-
-		mutex_lock(&fip->ctlr_mutex);
-		if (fip->state != FIP_ST_AUTO && fip->state != FIP_ST_NON_FIP) {
-			mutex_unlock(&fip->ctlr_mutex);
-			return -EINVAL;
-		}
-		fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
-		LIBFCOE_FIP_DBG(fip,
-				"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)) {
-			fcoe_ctlr_map_dest(fip);
-		} else {
-			memcpy(fip->dest_addr, sa, ETH_ALEN);
-			fip->map_dest = 0;
-		}
-		fip->flogi_oxid = FC_XID_UNKNOWN;
-		mutex_unlock(&fip->ctlr_mutex);
-		fc_fcoe_set_mac(fr_cb(fp)->granted_mac, fh->fh_d_id);
-	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
-		/*
-		 * Save source MAC for point-to-point responses.
-		 */
-		mutex_lock(&fip->ctlr_mutex);
-		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_AUTO)
-				LIBFCOE_FIP_DBG(fip, "received non-FIP FLOGI. "
-						"Setting non-FIP mode\n");
-			fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
-		}
-		mutex_unlock(&fip->ctlr_mutex);
-	}
-	return 0;
-}
-EXPORT_SYMBOL(fcoe_ctlr_recv_flogi);
-
-/**
- * fcoe_wwn_from_mac() - Converts a 48-bit IEEE MAC address to a 64-bit FC WWN
- * @mac:    The MAC address to convert
- * @scheme: The scheme to use when converting
- * @port:   The 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_ctlr_rport() - return the fcoe_rport for a given fc_rport_priv
- * @rdata: libfc remote port
- */
-static inline struct fcoe_rport *fcoe_ctlr_rport(struct fc_rport_priv *rdata)
-{
-	return (struct fcoe_rport *)(rdata + 1);
-}
-
-/**
- * fcoe_ctlr_vn_send() - Send a FIP VN2VN Probe Request or Reply.
- * @fip: The FCoE controller
- * @sub: sub-opcode for probe request, reply, or advertisement.
- * @dest: The destination Ethernet MAC address
- * @min_len: minimum size of the Ethernet payload to be sent
- */
-static void fcoe_ctlr_vn_send(struct fcoe_ctlr *fip,
-			      enum fip_vn2vn_subcode sub,
-			      const u8 *dest, size_t min_len)
-{
-	struct sk_buff *skb;
-	struct fip_frame {
-		struct ethhdr eth;
-		struct fip_header fip;
-		struct fip_mac_desc mac;
-		struct fip_wwn_desc wwnn;
-		struct fip_vn_desc vn;
-	} __attribute__((packed)) *frame;
-	struct fip_fc4_feat *ff;
-	struct fip_size_desc *size;
-	u32 fcp_feat;
-	size_t len;
-	size_t dlen;
-
-	len = sizeof(*frame);
-	dlen = 0;
-	if (sub == FIP_SC_VN_CLAIM_NOTIFY || sub == FIP_SC_VN_CLAIM_REP) {
-		dlen = sizeof(struct fip_fc4_feat) +
-		       sizeof(struct fip_size_desc);
-		len += dlen;
-	}
-	dlen += sizeof(frame->mac) + sizeof(frame->wwnn) + sizeof(frame->vn);
-	len = max(len, min_len + sizeof(struct ethhdr));
-
-	skb = dev_alloc_skb(len);
-	if (!skb)
-		return;
-
-	frame = (struct fip_frame *)skb->data;
-	memset(frame, 0, len);
-	memcpy(frame->eth.h_dest, dest, ETH_ALEN);
-	memcpy(frame->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
-	frame->eth.h_proto = htons(ETH_P_FIP);
-
-	frame->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
-	frame->fip.fip_op = htons(FIP_OP_VN2VN);
-	frame->fip.fip_subcode = sub;
-	frame->fip.fip_dl_len = htons(dlen / FIP_BPW);
-
-	frame->mac.fd_desc.fip_dtype = FIP_DT_MAC;
-	frame->mac.fd_desc.fip_dlen = sizeof(frame->mac) / FIP_BPW;
-	memcpy(frame->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
-
-	frame->wwnn.fd_desc.fip_dtype = FIP_DT_NAME;
-	frame->wwnn.fd_desc.fip_dlen = sizeof(frame->wwnn) / FIP_BPW;
-	put_unaligned_be64(fip->lp->wwnn, &frame->wwnn.fd_wwn);
-
-	frame->vn.fd_desc.fip_dtype = FIP_DT_VN_ID;
-	frame->vn.fd_desc.fip_dlen = sizeof(frame->vn) / FIP_BPW;
-	hton24(frame->vn.fd_mac, FIP_VN_FC_MAP);
-	hton24(frame->vn.fd_mac + 3, fip->port_id);
-	hton24(frame->vn.fd_fc_id, fip->port_id);
-	put_unaligned_be64(fip->lp->wwpn, &frame->vn.fd_wwpn);
-
-	/*
-	 * For claims, add FC-4 features.
-	 * TBD: Add interface to get fc-4 types and features from libfc.
-	 */
-	if (sub == FIP_SC_VN_CLAIM_NOTIFY || sub == FIP_SC_VN_CLAIM_REP) {
-		ff = (struct fip_fc4_feat *)(frame + 1);
-		ff->fd_desc.fip_dtype = FIP_DT_FC4F;
-		ff->fd_desc.fip_dlen = sizeof(*ff) / FIP_BPW;
-		ff->fd_fts = fip->lp->fcts;
-
-		fcp_feat = 0;
-		if (fip->lp->service_params & FCP_SPPF_INIT_FCN)
-			fcp_feat |= FCP_FEAT_INIT;
-		if (fip->lp->service_params & FCP_SPPF_TARG_FCN)
-			fcp_feat |= FCP_FEAT_TARG;
-		fcp_feat <<= (FC_TYPE_FCP * 4) % 32;
-		ff->fd_ff.fd_feat[FC_TYPE_FCP * 4 / 32] = htonl(fcp_feat);
-
-		size = (struct fip_size_desc *)(ff + 1);
-		size->fd_desc.fip_dtype = FIP_DT_FCOE_SIZE;
-		size->fd_desc.fip_dlen = sizeof(*size) / FIP_BPW;
-		size->fd_size = htons(fcoe_ctlr_fcoe_size(fip));
-	}
-
-	skb_put(skb, len);
-	skb->protocol = htons(ETH_P_FIP);
-	skb_reset_mac_header(skb);
-	skb_reset_network_header(skb);
-
-	fip->send(fip, skb);
-}
-
-/**
- * fcoe_ctlr_vn_rport_callback - Event handler for rport events.
- * @lport: The lport which is receiving the event
- * @rdata: remote port private data
- * @event: The event that occured
- *
- * Locking Note:  The rport lock must not be held when calling this function.
- */
-static void fcoe_ctlr_vn_rport_callback(struct fc_lport *lport,
-					struct fc_rport_priv *rdata,
-					enum fc_rport_event event)
-{
-	struct fcoe_ctlr *fip = lport->disc.priv;
-	struct fcoe_rport *frport = fcoe_ctlr_rport(rdata);
-
-	LIBFCOE_FIP_DBG(fip, "vn_rport_callback %x event %d\n",
-			rdata->ids.port_id, event);
-
-	mutex_lock(&fip->ctlr_mutex);
-	switch (event) {
-	case RPORT_EV_READY:
-		frport->login_count = 0;
-		break;
-	case RPORT_EV_LOGO:
-	case RPORT_EV_FAILED:
-	case RPORT_EV_STOP:
-		frport->login_count++;
-		if (frport->login_count > FCOE_CTLR_VN2VN_LOGIN_LIMIT) {
-			LIBFCOE_FIP_DBG(fip,
-					"rport FLOGI limited port_id %6.6x\n",
-					rdata->ids.port_id);
-			lport->tt.rport_logoff(rdata);
-		}
-		break;
-	default:
-		break;
-	}
-	mutex_unlock(&fip->ctlr_mutex);
-}
-
-static struct fc_rport_operations fcoe_ctlr_vn_rport_ops = {
-	.event_callback = fcoe_ctlr_vn_rport_callback,
-};
-
-/**
- * fcoe_ctlr_disc_stop_locked() - stop discovery in VN2VN mode
- * @fip: The FCoE controller
- *
- * Called with ctlr_mutex held.
- */
-static void fcoe_ctlr_disc_stop_locked(struct fc_lport *lport)
-{
-	mutex_lock(&lport->disc.disc_mutex);
-	lport->disc.disc_callback = NULL;
-	mutex_unlock(&lport->disc.disc_mutex);
-}
-
-/**
- * fcoe_ctlr_disc_stop() - stop discovery in VN2VN mode
- * @fip: The FCoE controller
- *
- * Called through the local port template for discovery.
- * Called without the ctlr_mutex held.
- */
-static void fcoe_ctlr_disc_stop(struct fc_lport *lport)
-{
-	struct fcoe_ctlr *fip = lport->disc.priv;
-
-	mutex_lock(&fip->ctlr_mutex);
-	fcoe_ctlr_disc_stop_locked(lport);
-	mutex_unlock(&fip->ctlr_mutex);
-}
-
-/**
- * fcoe_ctlr_disc_stop_final() - stop discovery for shutdown in VN2VN mode
- * @fip: The FCoE controller
- *
- * Called through the local port template for discovery.
- * Called without the ctlr_mutex held.
- */
-static void fcoe_ctlr_disc_stop_final(struct fc_lport *lport)
-{
-	fcoe_ctlr_disc_stop(lport);
-	lport->tt.rport_flush_queue();
-	synchronize_rcu();
-}
-
-/**
- * fcoe_ctlr_vn_restart() - VN2VN probe restart with new port_id
- * @fip: The FCoE controller
- *
- * Called with fcoe_ctlr lock held.
- */
-static void fcoe_ctlr_vn_restart(struct fcoe_ctlr *fip)
-{
-	unsigned long wait;
-	u32 port_id;
-
-	fcoe_ctlr_disc_stop_locked(fip->lp);
-
-	/*
-	 * Get proposed port ID.
-	 * If this is the first try after link up, use any previous port_id.
-	 * If there was none, use the low bits of the port_name.
-	 * On subsequent tries, get the next random one.
-	 * Don't use reserved IDs, use another non-zero value, just as random.
-	 */
-	port_id = fip->port_id;
-	if (fip->probe_tries)
-		port_id = prandom32(&fip->rnd_state) & 0xffff;
-	else if (!port_id)
-		port_id = fip->lp->wwpn & 0xffff;
-	if (!port_id || port_id == 0xffff)
-		port_id = 1;
-	fip->port_id = port_id;
-
-	if (fip->probe_tries < FIP_VN_RLIM_COUNT) {
-		fip->probe_tries++;
-		wait = random32() % FIP_VN_PROBE_WAIT;
-	} else
-		wait = FIP_VN_RLIM_INT;
-	mod_timer(&fip->timer, jiffies + msecs_to_jiffies(wait));
-	fcoe_ctlr_set_state(fip, FIP_ST_VNMP_START);
-}
-
-/**
- * fcoe_ctlr_vn_start() - Start in VN2VN mode
- * @fip: The FCoE controller
- *
- * Called with fcoe_ctlr lock held.
- */
-static void fcoe_ctlr_vn_start(struct fcoe_ctlr *fip)
-{
-	fip->probe_tries = 0;
-	prandom32_seed(&fip->rnd_state, fip->lp->wwpn);
-	fcoe_ctlr_vn_restart(fip);
-}
-
-/**
- * fcoe_ctlr_vn_parse - parse probe request or response
- * @fip: The FCoE controller
- * @skb: incoming packet
- * @rdata: buffer for resulting parsed VN entry plus fcoe_rport
- *
- * Returns non-zero error number on error.
- * Does not consume the packet.
- */
-static int fcoe_ctlr_vn_parse(struct fcoe_ctlr *fip,
-			      struct sk_buff *skb,
-			      struct fc_rport_priv *rdata)
-{
-	struct fip_header *fiph;
-	struct fip_desc *desc = NULL;
-	struct fip_mac_desc *macd = NULL;
-	struct fip_wwn_desc *wwn = NULL;
-	struct fip_vn_desc *vn = NULL;
-	struct fip_size_desc *size = NULL;
-	struct fcoe_rport *frport;
-	size_t rlen;
-	size_t dlen;
-	u32 desc_mask = 0;
-	u32 dtype;
-	u8 sub;
-
-	memset(rdata, 0, sizeof(*rdata) + sizeof(*frport));
-	frport = fcoe_ctlr_rport(rdata);
-
-	fiph = (struct fip_header *)skb->data;
-	frport->flags = ntohs(fiph->fip_flags);
-
-	sub = fiph->fip_subcode;
-	switch (sub) {
-	case FIP_SC_VN_PROBE_REQ:
-	case FIP_SC_VN_PROBE_REP:
-	case FIP_SC_VN_BEACON:
-		desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) |
-			    BIT(FIP_DT_VN_ID);
-		break;
-	case FIP_SC_VN_CLAIM_NOTIFY:
-	case FIP_SC_VN_CLAIM_REP:
-		desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) |
-			    BIT(FIP_DT_VN_ID) | BIT(FIP_DT_FC4F) |
-			    BIT(FIP_DT_FCOE_SIZE);
-		break;
-	default:
-		LIBFCOE_FIP_DBG(fip, "vn_parse unknown subcode %u\n", sub);
-		return -EINVAL;
-	}
-
-	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;
-
-		dtype = desc->fip_dtype;
-		if (dtype < 32) {
-			if (!(desc_mask & BIT(dtype))) {
-				LIBFCOE_FIP_DBG(fip,
-						"unexpected or duplicated desc "
-						"desc type %u in "
-						"FIP VN2VN subtype %u\n",
-						dtype, sub);
-				return -EINVAL;
-			}
-			desc_mask &= ~BIT(dtype);
-		}
-
-		switch (dtype) {
-		case FIP_DT_MAC:
-			if (dlen != sizeof(struct fip_mac_desc))
-				goto len_err;
-			macd = (struct fip_mac_desc *)desc;
-			if (!is_valid_ether_addr(macd->fd_mac)) {
-				LIBFCOE_FIP_DBG(fip,
-					"Invalid MAC addr %pM in FIP VN2VN\n",
-					 macd->fd_mac);
-				return -EINVAL;
-			}
-			memcpy(frport->enode_mac, macd->fd_mac, ETH_ALEN);
-			break;
-		case FIP_DT_NAME:
-			if (dlen != sizeof(struct fip_wwn_desc))
-				goto len_err;
-			wwn = (struct fip_wwn_desc *)desc;
-			rdata->ids.node_name = get_unaligned_be64(&wwn->fd_wwn);
-			break;
-		case FIP_DT_VN_ID:
-			if (dlen != sizeof(struct fip_vn_desc))
-				goto len_err;
-			vn = (struct fip_vn_desc *)desc;
-			memcpy(frport->vn_mac, vn->fd_mac, ETH_ALEN);
-			rdata->ids.port_id = ntoh24(vn->fd_fc_id);
-			rdata->ids.port_name = get_unaligned_be64(&vn->fd_wwpn);
-			break;
-		case FIP_DT_FC4F:
-			if (dlen != sizeof(struct fip_fc4_feat))
-				goto len_err;
-			break;
-		case FIP_DT_FCOE_SIZE:
-			if (dlen != sizeof(struct fip_size_desc))
-				goto len_err;
-			size = (struct fip_size_desc *)desc;
-			frport->fcoe_len = ntohs(size->fd_size);
-			break;
-		default:
-			LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
-					"in FIP probe\n", dtype);
-			/* standard says ignore unknown descriptors >= 128 */
-			if (dtype < FIP_DT_VENDOR_BASE)
-				return -EINVAL;
-			break;
-		}
-		desc = (struct fip_desc *)((char *)desc + dlen);
-		rlen -= dlen;
-	}
-	return 0;
-
-len_err:
-	LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
-			dtype, dlen);
-	return -EINVAL;
-}
-
-/**
- * fcoe_ctlr_vn_send_claim() - send multicast FIP VN2VN Claim Notification.
- * @fip: The FCoE controller
- *
- * Called with ctlr_mutex held.
- */
-static void fcoe_ctlr_vn_send_claim(struct fcoe_ctlr *fip)
-{
-	fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_NOTIFY, fcoe_all_vn2vn, 0);
-	fip->sol_time = jiffies;
-}
-
-/**
- * fcoe_ctlr_vn_probe_req() - handle incoming VN2VN probe request.
- * @fip: The FCoE controller
- * @rdata: parsed remote port with frport from the probe request
- *
- * Called with ctlr_mutex held.
- */
-static void fcoe_ctlr_vn_probe_req(struct fcoe_ctlr *fip,
-				   struct fc_rport_priv *rdata)
-{
-	struct fcoe_rport *frport = fcoe_ctlr_rport(rdata);
-
-	if (rdata->ids.port_id != fip->port_id)
-		return;
-
-	switch (fip->state) {
-	case FIP_ST_VNMP_CLAIM:
-	case FIP_ST_VNMP_UP:
-		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP,
-				  frport->enode_mac, 0);
-		break;
-	case FIP_ST_VNMP_PROBE1:
-	case FIP_ST_VNMP_PROBE2:
-		/*
-		 * Decide whether to reply to the Probe.
-		 * Our selected address is never a "recorded" one, so
-		 * only reply if our WWPN is greater and the
-		 * Probe's REC bit is not set.
-		 * If we don't reply, we will change our address.
-		 */
-		if (fip->lp->wwpn > rdata->ids.port_name &&
-		    !(frport->flags & FIP_FL_REC_OR_P2P)) {
-			fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP,
-					  frport->enode_mac, 0);
-			break;
-		}
-		/* fall through */
-	case FIP_ST_VNMP_START:
-		fcoe_ctlr_vn_restart(fip);
-		break;
-	default:
-		break;
-	}
-}
-
-/**
- * fcoe_ctlr_vn_probe_reply() - handle incoming VN2VN probe reply.
- * @fip: The FCoE controller
- * @rdata: parsed remote port with frport from the probe request
- *
- * Called with ctlr_mutex held.
- */
-static void fcoe_ctlr_vn_probe_reply(struct fcoe_ctlr *fip,
-				   struct fc_rport_priv *rdata)
-{
-	if (rdata->ids.port_id != fip->port_id)
-		return;
-	switch (fip->state) {
-	case FIP_ST_VNMP_START:
-	case FIP_ST_VNMP_PROBE1:
-	case FIP_ST_VNMP_PROBE2:
-	case FIP_ST_VNMP_CLAIM:
-		fcoe_ctlr_vn_restart(fip);
-		break;
-	case FIP_ST_VNMP_UP:
-		fcoe_ctlr_vn_send_claim(fip);
-		break;
-	default:
-		break;
-	}
-}
-
-/**
- * fcoe_ctlr_vn_add() - Add a VN2VN entry to the list, based on a claim reply.
- * @fip: The FCoE controller
- * @new: newly-parsed remote port with frport as a template for new rdata
- *
- * Called with ctlr_mutex held.
- */
-static void fcoe_ctlr_vn_add(struct fcoe_ctlr *fip, struct fc_rport_priv *new)
-{
-	struct fc_lport *lport = fip->lp;
-	struct fc_rport_priv *rdata;
-	struct fc_rport_identifiers *ids;
-	struct fcoe_rport *frport;
-	u32 port_id;
-
-	port_id = new->ids.port_id;
-	if (port_id == fip->port_id)
-		return;
-
-	mutex_lock(&lport->disc.disc_mutex);
-	rdata = lport->tt.rport_create(lport, port_id);
-	if (!rdata) {
-		mutex_unlock(&lport->disc.disc_mutex);
-		return;
-	}
-
-	rdata->ops = &fcoe_ctlr_vn_rport_ops;
-	rdata->disc_id = lport->disc.disc_id;
-
-	ids = &rdata->ids;
-	if ((ids->port_name != -1 && ids->port_name != new->ids.port_name) ||
-	    (ids->node_name != -1 && ids->node_name != new->ids.node_name))
-		lport->tt.rport_logoff(rdata);
-	ids->port_name = new->ids.port_name;
-	ids->node_name = new->ids.node_name;
-	mutex_unlock(&lport->disc.disc_mutex);
-
-	frport = fcoe_ctlr_rport(rdata);
-	LIBFCOE_FIP_DBG(fip, "vn_add rport %6.6x %s\n",
-			port_id, frport->fcoe_len ? "old" : "new");
-	*frport = *fcoe_ctlr_rport(new);
-	frport->time = 0;
-}
-
-/**
- * fcoe_ctlr_vn_lookup() - Find VN remote port's MAC address
- * @fip: The FCoE controller
- * @port_id:  The port_id of the remote VN_node
- * @mac: buffer which will hold the VN_NODE destination MAC address, if found.
- *
- * Returns non-zero error if no remote port found.
- */
-static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *fip, u32 port_id, u8 *mac)
-{
-	struct fc_lport *lport = fip->lp;
-	struct fc_rport_priv *rdata;
-	struct fcoe_rport *frport;
-	int ret = -1;
-
-	rcu_read_lock();
-	rdata = lport->tt.rport_lookup(lport, port_id);
-	if (rdata) {
-		frport = fcoe_ctlr_rport(rdata);
-		memcpy(mac, frport->enode_mac, ETH_ALEN);
-		ret = 0;
-	}
-	rcu_read_unlock();
-	return ret;
-}
-
-/**
- * fcoe_ctlr_vn_claim_notify() - handle received FIP VN2VN Claim Notification
- * @fip: The FCoE controller
- * @new: newly-parsed remote port with frport as a template for new rdata
- *
- * Called with ctlr_mutex held.
- */
-static void fcoe_ctlr_vn_claim_notify(struct fcoe_ctlr *fip,
-				      struct fc_rport_priv *new)
-{
-	struct fcoe_rport *frport = fcoe_ctlr_rport(new);
-
-	if (frport->flags & FIP_FL_REC_OR_P2P) {
-		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
-		return;
-	}
-	switch (fip->state) {
-	case FIP_ST_VNMP_START:
-	case FIP_ST_VNMP_PROBE1:
-	case FIP_ST_VNMP_PROBE2:
-		if (new->ids.port_id == fip->port_id)
-			fcoe_ctlr_vn_restart(fip);
-		break;
-	case FIP_ST_VNMP_CLAIM:
-	case FIP_ST_VNMP_UP:
-		if (new->ids.port_id == fip->port_id) {
-			if (new->ids.port_name > fip->lp->wwpn) {
-				fcoe_ctlr_vn_restart(fip);
-				break;
-			}
-			fcoe_ctlr_vn_send_claim(fip);
-			break;
-		}
-		fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_REP, frport->enode_mac,
-				  min((u32)frport->fcoe_len,
-				      fcoe_ctlr_fcoe_size(fip)));
-		fcoe_ctlr_vn_add(fip, new);
-		break;
-	default:
-		break;
-	}
-}
-
-/**
- * fcoe_ctlr_vn_claim_resp() - handle received Claim Response
- * @fip: The FCoE controller that received the frame
- * @new: newly-parsed remote port with frport from the Claim Response
- *
- * Called with ctlr_mutex held.
- */
-static void fcoe_ctlr_vn_claim_resp(struct fcoe_ctlr *fip,
-				    struct fc_rport_priv *new)
-{
-	LIBFCOE_FIP_DBG(fip, "claim resp from from rport %x - state %s\n",
-			new->ids.port_id, fcoe_ctlr_state(fip->state));
-	if (fip->state == FIP_ST_VNMP_UP || fip->state == FIP_ST_VNMP_CLAIM)
-		fcoe_ctlr_vn_add(fip, new);
-}
-
-/**
- * fcoe_ctlr_vn_beacon() - handle received beacon.
- * @fip: The FCoE controller that received the frame
- * @new: newly-parsed remote port with frport from the Beacon
- *
- * Called with ctlr_mutex held.
- */
-static void fcoe_ctlr_vn_beacon(struct fcoe_ctlr *fip,
-				struct fc_rport_priv *new)
-{
-	struct fc_lport *lport = fip->lp;
-	struct fc_rport_priv *rdata;
-	struct fcoe_rport *frport;
-
-	frport = fcoe_ctlr_rport(new);
-	if (frport->flags & FIP_FL_REC_OR_P2P) {
-		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
-		return;
-	}
-	mutex_lock(&lport->disc.disc_mutex);
-	rdata = lport->tt.rport_lookup(lport, new->ids.port_id);
-	if (rdata)
-		kref_get(&rdata->kref);
-	mutex_unlock(&lport->disc.disc_mutex);
-	if (rdata) {
-		if (rdata->ids.node_name == new->ids.node_name &&
-		    rdata->ids.port_name == new->ids.port_name) {
-			frport = fcoe_ctlr_rport(rdata);
-			if (!frport->time && fip->state == FIP_ST_VNMP_UP)
-				lport->tt.rport_login(rdata);
-			frport->time = jiffies;
-		}
-		kref_put(&rdata->kref, lport->tt.rport_destroy);
-		return;
-	}
-	if (fip->state != FIP_ST_VNMP_UP)
-		return;
-
-	/*
-	 * Beacon from a new neighbor.
-	 * Send a claim notify if one hasn't been sent recently.
-	 * Don't add the neighbor yet.
-	 */
-	LIBFCOE_FIP_DBG(fip, "beacon from new rport %x. sending claim notify\n",
-			new->ids.port_id);
-	if (time_after(jiffies,
-		       fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT)))
-		fcoe_ctlr_vn_send_claim(fip);
-}
-
-/**
- * fcoe_ctlr_vn_age() - Check for VN_ports without recent beacons
- * @fip: The FCoE controller
- *
- * Called with ctlr_mutex held.
- * Called only in state FIP_ST_VNMP_UP.
- * Returns the soonest time for next age-out or a time far in the future.
- */
-static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip)
-{
-	struct fc_lport *lport = fip->lp;
-	struct fc_rport_priv *rdata;
-	struct fcoe_rport *frport;
-	unsigned long next_time;
-	unsigned long deadline;
-
-	next_time = jiffies + msecs_to_jiffies(FIP_VN_BEACON_INT * 10);
-	mutex_lock(&lport->disc.disc_mutex);
-	list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
-		frport = fcoe_ctlr_rport(rdata);
-		if (!frport->time)
-			continue;
-		deadline = frport->time +
-			   msecs_to_jiffies(FIP_VN_BEACON_INT * 25 / 10);
-		if (time_after_eq(jiffies, deadline)) {
-			frport->time = 0;
-			LIBFCOE_FIP_DBG(fip,
-				"port %16.16llx fc_id %6.6x beacon expired\n",
-				rdata->ids.port_name, rdata->ids.port_id);
-			lport->tt.rport_logoff(rdata);
-		} else if (time_before(deadline, next_time))
-			next_time = deadline;
-	}
-	mutex_unlock(&lport->disc.disc_mutex);
-	return next_time;
-}
-
-/**
- * fcoe_ctlr_vn_recv() - Receive a FIP frame
- * @fip: The FCoE controller that received the frame
- * @skb: The received FIP frame
- *
- * Returns non-zero if the frame is dropped.
- * Always consumes the frame.
- */
-static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
-{
-	struct fip_header *fiph;
-	enum fip_vn2vn_subcode sub;
-	struct {
-		struct fc_rport_priv rdata;
-		struct fcoe_rport frport;
-	} buf;
-	int rc;
-
-	fiph = (struct fip_header *)skb->data;
-	sub = fiph->fip_subcode;
-
-	rc = fcoe_ctlr_vn_parse(fip, skb, &buf.rdata);
-	if (rc) {
-		LIBFCOE_FIP_DBG(fip, "vn_recv vn_parse error %d\n", rc);
-		goto drop;
-	}
-
-	mutex_lock(&fip->ctlr_mutex);
-	switch (sub) {
-	case FIP_SC_VN_PROBE_REQ:
-		fcoe_ctlr_vn_probe_req(fip, &buf.rdata);
-		break;
-	case FIP_SC_VN_PROBE_REP:
-		fcoe_ctlr_vn_probe_reply(fip, &buf.rdata);
-		break;
-	case FIP_SC_VN_CLAIM_NOTIFY:
-		fcoe_ctlr_vn_claim_notify(fip, &buf.rdata);
-		break;
-	case FIP_SC_VN_CLAIM_REP:
-		fcoe_ctlr_vn_claim_resp(fip, &buf.rdata);
-		break;
-	case FIP_SC_VN_BEACON:
-		fcoe_ctlr_vn_beacon(fip, &buf.rdata);
-		break;
-	default:
-		LIBFCOE_FIP_DBG(fip, "vn_recv unknown subcode %d\n", sub);
-		rc = -1;
-		break;
-	}
-	mutex_unlock(&fip->ctlr_mutex);
-drop:
-	kfree_skb(skb);
-	return rc;
-}
-
-/**
- * fcoe_ctlr_disc_recv - discovery receive handler for VN2VN mode.
- * @lport: The local port
- * @fp: The received frame
- *
- * This should never be called since we don't see RSCNs or other
- * fabric-generated ELSes.
- */
-static void fcoe_ctlr_disc_recv(struct fc_lport *lport, struct fc_frame *fp)
-{
-	struct fc_seq_els_data rjt_data;
-
-	rjt_data.reason = ELS_RJT_UNSUP;
-	rjt_data.explan = ELS_EXPL_NONE;
-	lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
-	fc_frame_free(fp);
-}
-
-/**
- * fcoe_ctlr_disc_recv - start discovery for VN2VN mode.
- * @fip: The FCoE controller
- *
- * This sets a flag indicating that remote ports should be created
- * and started for the peers we discover.  We use the disc_callback
- * pointer as that flag.  Peers already discovered are created here.
- *
- * The lport lock is held during this call. The callback must be done
- * later, without holding either the lport or discovery locks.
- * The fcoe_ctlr lock may also be held during this call.
- */
-static void fcoe_ctlr_disc_start(void (*callback)(struct fc_lport *,
-						  enum fc_disc_event),
-				 struct fc_lport *lport)
-{
-	struct fc_disc *disc = &lport->disc;
-	struct fcoe_ctlr *fip = disc->priv;
-
-	mutex_lock(&disc->disc_mutex);
-	disc->disc_callback = callback;
-	disc->disc_id = (disc->disc_id + 2) | 1;
-	disc->pending = 1;
-	schedule_work(&fip->timer_work);
-	mutex_unlock(&disc->disc_mutex);
-}
-
-/**
- * fcoe_ctlr_vn_disc() - report FIP VN_port discovery results after claim state.
- * @fip: The FCoE controller
- *
- * Starts the FLOGI and PLOGI login process to each discovered rport for which
- * we've received at least one beacon.
- * Performs the discovery complete callback.
- */
-static void fcoe_ctlr_vn_disc(struct fcoe_ctlr *fip)
-{
-	struct fc_lport *lport = fip->lp;
-	struct fc_disc *disc = &lport->disc;
-	struct fc_rport_priv *rdata;
-	struct fcoe_rport *frport;
-	void (*callback)(struct fc_lport *, enum fc_disc_event);
-
-	mutex_lock(&disc->disc_mutex);
-	callback = disc->pending ? disc->disc_callback : NULL;
-	disc->pending = 0;
-	list_for_each_entry_rcu(rdata, &disc->rports, peers) {
-		frport = fcoe_ctlr_rport(rdata);
-		if (frport->time)
-			lport->tt.rport_login(rdata);
-	}
-	mutex_unlock(&disc->disc_mutex);
-	if (callback)
-		callback(lport, DISC_EV_SUCCESS);
-}
-
-/**
- * fcoe_ctlr_vn_timeout - timer work function for VN2VN mode.
- * @fip: The FCoE controller
- */
-static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip)
-{
-	unsigned long next_time;
-	u8 mac[ETH_ALEN];
-	u32 new_port_id = 0;
-
-	mutex_lock(&fip->ctlr_mutex);
-	switch (fip->state) {
-	case FIP_ST_VNMP_START:
-		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE1);
-		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
-		next_time = jiffies + msecs_to_jiffies(FIP_VN_PROBE_WAIT);
-		break;
-	case FIP_ST_VNMP_PROBE1:
-		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE2);
-		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
-		next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
-		break;
-	case FIP_ST_VNMP_PROBE2:
-		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_CLAIM);
-		new_port_id = fip->port_id;
-		hton24(mac, FIP_VN_FC_MAP);
-		hton24(mac + 3, new_port_id);
-		fcoe_ctlr_map_dest(fip);
-		fip->update_mac(fip->lp, mac);
-		fcoe_ctlr_vn_send_claim(fip);
-		next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
-		break;
-	case FIP_ST_VNMP_CLAIM:
-		/*
-		 * This may be invoked either by starting discovery so don't
-		 * go to the next state unless it's been long enough.
-		 */
-		next_time = fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT);
-		if (time_after_eq(jiffies, next_time)) {
-			fcoe_ctlr_set_state(fip, FIP_ST_VNMP_UP);
-			fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON,
-					  fcoe_all_vn2vn, 0);
-			next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
-			fip->port_ka_time = next_time;
-		}
-		fcoe_ctlr_vn_disc(fip);
-		break;
-	case FIP_ST_VNMP_UP:
-		next_time = fcoe_ctlr_vn_age(fip);
-		if (time_after_eq(jiffies, fip->port_ka_time)) {
-			fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON,
-					  fcoe_all_vn2vn, 0);
-			fip->port_ka_time = jiffies +
-				 msecs_to_jiffies(FIP_VN_BEACON_INT +
-					(random32() % FIP_VN_BEACON_FUZZ));
-		}
-		if (time_before(fip->port_ka_time, next_time))
-			next_time = fip->port_ka_time;
-		break;
-	case FIP_ST_LINK_WAIT:
-		goto unlock;
-	default:
-		WARN(1, "unexpected state %d\n", fip->state);
-		goto unlock;
-	}
-	mod_timer(&fip->timer, next_time);
-unlock:
-	mutex_unlock(&fip->ctlr_mutex);
-
-	/* If port ID is new, notify local port after dropping ctlr_mutex */
-	if (new_port_id)
-		fc_lport_set_local_id(fip->lp, new_port_id);
-}
-
-/**
- * fcoe_libfc_config() - Sets up libfc related properties for local port
- * @lp: The local port to configure libfc for
- * @fip: The FCoE controller in use by the local port
- * @tt: The libfc function template
- * @init_fcp: If non-zero, the FCP portion of libfc should be initialized
- *
- * Returns : 0 for success
- */
-int fcoe_libfc_config(struct fc_lport *lport, struct fcoe_ctlr *fip,
-		      const struct libfc_function_template *tt, int init_fcp)
-{
-	/* Set the function pointers set by the LLDD */
-	memcpy(&lport->tt, tt, sizeof(*tt));
-	if (init_fcp && fc_fcp_init(lport))
-		return -ENOMEM;
-	fc_exch_init(lport);
-	fc_elsct_init(lport);
-	fc_lport_init(lport);
-	if (fip->mode == FIP_MODE_VN2VN)
-		lport->rport_priv_size = sizeof(struct fcoe_rport);
-	fc_rport_init(lport);
-	if (fip->mode == FIP_MODE_VN2VN) {
-		lport->point_to_multipoint = 1;
-		lport->tt.disc_recv_req = fcoe_ctlr_disc_recv;
-		lport->tt.disc_start = fcoe_ctlr_disc_start;
-		lport->tt.disc_stop = fcoe_ctlr_disc_stop;
-		lport->tt.disc_stop_final = fcoe_ctlr_disc_stop_final;
-		mutex_init(&lport->disc.disc_mutex);
-		INIT_LIST_HEAD(&lport->disc.rports);
-		lport->disc.priv = fip;
-	} else {
-		fc_disc_init(lport);
-	}
-	return 0;
-}
-EXPORT_SYMBOL_GPL(fcoe_libfc_config);


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

* [PATCH 22/28] libfcoe: include fcoe_transport.c into kernel libfcoe module
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (20 preceding siblings ...)
  2011-01-29  0:05 ` [PATCH 21/28] libfcoe: rename libfcoe.c to fcoe_cltr.c for the coming fcoe_transport.c Robert Love
@ 2011-01-29  0:05 ` Robert Love
  2011-01-29  0:05 ` [PATCH 23/28] fcoe: prepare fcoe for using fcoe transport Robert Love
                   ` (5 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:05 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Bhanu Prakash Gollapudi, Yi Zou

From: Yi Zou <yi.zou@intel.com>

Now we can include the fcoe_transport.c to the build of the kernel libfcoe
module. Move the module information to fcoe_transport, and it will have
all the module parameters later for the create/destroy/enable/disable of an
FCoE instance.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/Makefile         |    2 +-
 drivers/scsi/fcoe/fcoe_ctlr.c      |    8 --------
 drivers/scsi/fcoe/fcoe_transport.c |    8 ++++++++
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
index b122b7a..f6d37d0 100644
--- a/drivers/scsi/fcoe/Makefile
+++ b/drivers/scsi/fcoe/Makefile
@@ -1,4 +1,4 @@
 obj-$(CONFIG_FCOE) += fcoe.o
 obj-$(CONFIG_LIBFCOE) += libfcoe.o
 
-libfcoe-objs := fcoe_ctlr.o
+libfcoe-objs := fcoe_ctlr.o fcoe_transport.o
diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c
index c12d0a7..c93f007 100644
--- a/drivers/scsi/fcoe/fcoe_ctlr.c
+++ b/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -46,10 +46,6 @@
 
 #include "libfcoe.h"
 
-MODULE_AUTHOR("Open-FCoE.org");
-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) */
 
@@ -68,10 +64,6 @@ static u8 fcoe_all_enode[ETH_ALEN] = FIP_ALL_ENODE_MACS;
 static u8 fcoe_all_vn2vn[ETH_ALEN] = FIP_ALL_VN2VN_MACS;
 static u8 fcoe_all_p2p[ETH_ALEN] = FIP_ALL_P2P_MACS;
 
-unsigned int libfcoe_debug_logging;
-module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
-
 static const char * const fcoe_ctlr_states[] = {
 	[FIP_ST_DISABLED] =	"DISABLED",
 	[FIP_ST_LINK_WAIT] =	"LINK_WAIT",
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index 41d69be..e5aef56 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -27,6 +27,10 @@
 
 #include "libfcoe.h"
 
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("FIP discovery protocol and FCoE transport for FCoE HBAs");
+MODULE_LICENSE("GPL v2");
+
 static int fcoe_transport_create(const char *, struct kernel_param *);
 static int fcoe_transport_destroy(const char *, struct kernel_param *);
 static int fcoe_transport_show(char *buffer, const struct kernel_param *kp);
@@ -39,6 +43,10 @@ static LIST_HEAD(fcoe_transports);
 static LIST_HEAD(fcoe_netdevs);
 static DEFINE_MUTEX(ft_mutex);
 
+unsigned int libfcoe_debug_logging;
+module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+
 module_param_call(show, NULL, fcoe_transport_show, NULL, S_IRUSR);
 __MODULE_PARM_TYPE(show, "string");
 MODULE_PARM_DESC(show, " Show attached FCoE transports");


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

* [PATCH 23/28] fcoe: prepare fcoe for using fcoe transport
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (21 preceding siblings ...)
  2011-01-29  0:05 ` [PATCH 22/28] libfcoe: include fcoe_transport.c into kernel libfcoe module Robert Love
@ 2011-01-29  0:05 ` Robert Love
  2011-01-29  0:05 ` [PATCH 24/28] fcoe: convert fcoe.ko to become an fcoe transport provider driver Robert Love
                   ` (4 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:05 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Yi Zou

From: Yi Zou <yi.zou@intel.com>

Prepare the fcoe to convert it to use the newly added fcoe transport, making
it as the default fcoe transport provider for libfcoe. This patch is to rename
some of the variables to avoid any confusing names later as now there are
several transports in the same file.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c |   29 +++++++++++++++--------------
 1 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index d114699..6d6c8bf 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -145,8 +145,8 @@ static struct notifier_block fcoe_cpu_notifier = {
 	.notifier_call = fcoe_cpu_callback,
 };
 
-static struct scsi_transport_template *fcoe_transport_template;
-static struct scsi_transport_template *fcoe_vport_transport_template;
+static struct scsi_transport_template *fcoe_nport_scsi_transport;
+static struct scsi_transport_template *fcoe_vport_scsi_transport;
 
 static int fcoe_vport_destroy(struct fc_vport *);
 static int fcoe_vport_create(struct fc_vport *, bool disabled);
@@ -163,7 +163,7 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = {
 	.lport_set_port_id = fcoe_set_port_id,
 };
 
-struct fc_function_template fcoe_transport_function = {
+struct fc_function_template fcoe_nport_fc_functions = {
 	.show_host_node_name = 1,
 	.show_host_port_name = 1,
 	.show_host_supported_classes = 1,
@@ -203,7 +203,7 @@ struct fc_function_template fcoe_transport_function = {
 	.bsg_request = fc_lport_bsg_request,
 };
 
-struct fc_function_template fcoe_vport_transport_function = {
+struct fc_function_template fcoe_vport_fc_functions = {
 	.show_host_node_name = 1,
 	.show_host_port_name = 1,
 	.show_host_supported_classes = 1,
@@ -723,9 +723,9 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)
 	lport->host->max_cmd_len = FCOE_MAX_CMD_LEN;
 
 	if (lport->vport)
-		lport->host->transportt = fcoe_vport_transport_template;
+		lport->host->transportt = fcoe_vport_scsi_transport;
 	else
-		lport->host->transportt = fcoe_transport_template;
+		lport->host->transportt = fcoe_nport_scsi_transport;
 
 	/* add the new host to the SCSI-ml */
 	rc = scsi_add_host(lport->host, dev);
@@ -1064,11 +1064,12 @@ out:
 static int __init fcoe_if_init(void)
 {
 	/* attach to scsi transport */
-	fcoe_transport_template = fc_attach_transport(&fcoe_transport_function);
-	fcoe_vport_transport_template =
-		fc_attach_transport(&fcoe_vport_transport_function);
+	fcoe_nport_scsi_transport =
+		fc_attach_transport(&fcoe_nport_fc_functions);
+	fcoe_vport_scsi_transport =
+		fc_attach_transport(&fcoe_vport_fc_functions);
 
-	if (!fcoe_transport_template) {
+	if (!fcoe_nport_scsi_transport) {
 		printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n");
 		return -ENODEV;
 	}
@@ -1085,10 +1086,10 @@ static int __init fcoe_if_init(void)
  */
 int __exit fcoe_if_exit(void)
 {
-	fc_release_transport(fcoe_transport_template);
-	fc_release_transport(fcoe_vport_transport_template);
-	fcoe_transport_template = NULL;
-	fcoe_vport_transport_template = NULL;
+	fc_release_transport(fcoe_nport_scsi_transport);
+	fc_release_transport(fcoe_vport_scsi_transport);
+	fcoe_nport_scsi_transport = NULL;
+	fcoe_vport_scsi_transport = NULL;
 	return 0;
 }
 


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

* [PATCH 24/28] fcoe: convert fcoe.ko to become an fcoe transport provider driver
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (22 preceding siblings ...)
  2011-01-29  0:05 ` [PATCH 23/28] fcoe: prepare fcoe for using fcoe transport Robert Love
@ 2011-01-29  0:05 ` Robert Love
  2011-01-29  0:05 ` [PATCH 25/28] libfc: Extending lport's roles for target if there is a registered target Robert Love
                   ` (3 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:05 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Bhanu Prakash Gollapudi, Yi Zou

From: Yi Zou <yi.zou@intel.com>

Remove the existing sysfs entry points of the fcoe.ko module parameters that
are used to create/destroy/enable/disable an FCoE instance, rather, use the
newly added fcoe transport code to attach itself as an FCoE transport provider
when fcoe.ko gets loaded. There is no functionality change on the logic of
fcoe interacts with upper libfc and lower netdev. The fcoe transport only acts
as thin layer to provide a unified interface for all fcoe transport providers
so all FCoE instances on any network interfaces from all vendors can be
managed through the same Open-FCoE.org's user space tool package, which also
has full DCB support.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c |  170 +++++++++++++++++-----------------------------
 1 files changed, 62 insertions(+), 108 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 6d6c8bf..8a1005d 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -101,10 +101,11 @@ static int fcoe_ddp_done(struct fc_lport *, u16);
 
 static int fcoe_cpu_callback(struct notifier_block *, unsigned long, void *);
 
-static int fcoe_create(const char *, struct kernel_param *);
-static int fcoe_destroy(const char *, struct kernel_param *);
-static int fcoe_enable(const char *, struct kernel_param *);
-static int fcoe_disable(const char *, struct kernel_param *);
+static bool fcoe_match(struct net_device *netdev);
+static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode);
+static int fcoe_destroy(struct net_device *netdev);
+static int fcoe_enable(struct net_device *netdev);
+static int fcoe_disable(struct net_device *netdev);
 
 static struct fc_seq *fcoe_elsct_send(struct fc_lport *,
 				      u32 did, struct fc_frame *,
@@ -117,24 +118,6 @@ static void fcoe_recv_frame(struct sk_buff *skb);
 
 static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *);
 
-module_param_call(create, fcoe_create, NULL, (void *)FIP_MODE_FABRIC, S_IWUSR);
-__MODULE_PARM_TYPE(create, "string");
-MODULE_PARM_DESC(create, " Creates fcoe instance on a ethernet interface");
-module_param_call(create_vn2vn, fcoe_create, NULL,
-		  (void *)FIP_MODE_VN2VN, S_IWUSR);
-__MODULE_PARM_TYPE(create_vn2vn, "string");
-MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance "
-		 "on an Ethernet interface");
-module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, " Destroys fcoe instance on a ethernet interface");
-module_param_call(enable, fcoe_enable, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(enable, "string");
-MODULE_PARM_DESC(enable, " Enables fcoe on a ethernet interface.");
-module_param_call(disable, fcoe_disable, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(disable, "string");
-MODULE_PARM_DESC(disable, " Disables fcoe on a ethernet interface.");
-
 /* notification function for packets from net device */
 static struct notifier_block fcoe_notifier = {
 	.notifier_call = fcoe_device_notification,
@@ -1939,39 +1922,16 @@ out:
 }
 
 /**
- * fcoe_if_to_netdev() - Parse a name buffer to get a net device
- * @buffer: The name of the net device
- *
- * Returns: NULL or a ptr to net_device
- */
-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_disable() - Disables a FCoE interface
- * @buffer: The name of the Ethernet interface to be disabled
- * @kp:	    The associated kernel parameter
+ * @netdev  : The net_device object the Ethernet interface to create on
  *
- * Called from sysfs.
+ * Called from fcoe transport.
  *
  * Returns: 0 for success
  */
-static int fcoe_disable(const char *buffer, struct kernel_param *kp)
+static int fcoe_disable(struct net_device *netdev)
 {
 	struct fcoe_interface *fcoe;
-	struct net_device *netdev;
 	int rc = 0;
 
 	mutex_lock(&fcoe_config_mutex);
@@ -1987,16 +1947,9 @@ static int fcoe_disable(const char *buffer, struct kernel_param *kp)
 	}
 #endif
 
-	netdev = fcoe_if_to_netdev(buffer);
-	if (!netdev) {
-		rc = -ENODEV;
-		goto out_nodev;
-	}
-
 	if (!rtnl_trylock()) {
-		dev_put(netdev);
 		mutex_unlock(&fcoe_config_mutex);
-		return restart_syscall();
+		return -ERESTARTSYS;
 	}
 
 	fcoe = fcoe_hostlist_lookup_port(netdev);
@@ -2008,7 +1961,6 @@ static int fcoe_disable(const char *buffer, struct kernel_param *kp)
 	} else
 		rc = -ENODEV;
 
-	dev_put(netdev);
 out_nodev:
 	mutex_unlock(&fcoe_config_mutex);
 	return rc;
@@ -2016,17 +1968,15 @@ out_nodev:
 
 /**
  * fcoe_enable() - Enables a FCoE interface
- * @buffer: The name of the Ethernet interface to be enabled
- * @kp:     The associated kernel parameter
+ * @netdev  : The net_device object the Ethernet interface to create on
  *
- * Called from sysfs.
+ * Called from fcoe transport.
  *
  * Returns: 0 for success
  */
-static int fcoe_enable(const char *buffer, struct kernel_param *kp)
+static int fcoe_enable(struct net_device *netdev)
 {
 	struct fcoe_interface *fcoe;
-	struct net_device *netdev;
 	int rc = 0;
 
 	mutex_lock(&fcoe_config_mutex);
@@ -2041,17 +1991,9 @@ static int fcoe_enable(const char *buffer, struct kernel_param *kp)
 		goto out_nodev;
 	}
 #endif
-
-	netdev = fcoe_if_to_netdev(buffer);
-	if (!netdev) {
-		rc = -ENODEV;
-		goto out_nodev;
-	}
-
 	if (!rtnl_trylock()) {
-		dev_put(netdev);
 		mutex_unlock(&fcoe_config_mutex);
-		return restart_syscall();
+		return -ERESTARTSYS;
 	}
 
 	fcoe = fcoe_hostlist_lookup_port(netdev);
@@ -2062,7 +2004,6 @@ static int fcoe_enable(const char *buffer, struct kernel_param *kp)
 	else if (!fcoe_link_ok(fcoe->ctlr.lp))
 		fcoe_ctlr_link_up(&fcoe->ctlr);
 
-	dev_put(netdev);
 out_nodev:
 	mutex_unlock(&fcoe_config_mutex);
 	return rc;
@@ -2070,17 +2011,15 @@ out_nodev:
 
 /**
  * fcoe_destroy() - Destroy a FCoE interface
- * @buffer: The name of the Ethernet interface to be destroyed
- * @kp:	    The associated kernel parameter
+ * @netdev  : The net_device object the Ethernet interface to create on
  *
- * Called from sysfs.
+ * Called from fcoe transport
  *
  * Returns: 0 for success
  */
-static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
+static int fcoe_destroy(struct net_device *netdev)
 {
 	struct fcoe_interface *fcoe;
-	struct net_device *netdev;
 	int rc = 0;
 
 	mutex_lock(&fcoe_config_mutex);
@@ -2095,32 +2034,21 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
 		goto out_nodev;
 	}
 #endif
-
-	netdev = fcoe_if_to_netdev(buffer);
-	if (!netdev) {
-		rc = -ENODEV;
-		goto out_nodev;
-	}
-
 	if (!rtnl_trylock()) {
-		dev_put(netdev);
 		mutex_unlock(&fcoe_config_mutex);
-		return restart_syscall();
+		return -ERESTARTSYS;
 	}
 
 	fcoe = fcoe_hostlist_lookup_port(netdev);
 	if (!fcoe) {
 		rtnl_unlock();
 		rc = -ENODEV;
-		goto out_putdev;
+		goto out_nodev;
 	}
 	fcoe_interface_cleanup(fcoe);
 	list_del(&fcoe->list);
 	/* RTNL mutex is dropped by fcoe_if_destroy */
 	fcoe_if_destroy(fcoe->ctlr.lp);
-
-out_putdev:
-	dev_put(netdev);
 out_nodev:
 	mutex_unlock(&fcoe_config_mutex);
 	return rc;
@@ -2143,27 +2071,39 @@ static void fcoe_destroy_work(struct work_struct *work)
 }
 
 /**
+ * fcoe_match() - Check if the FCoE is supported on the given netdevice
+ * @netdev  : The net_device object the Ethernet interface to create on
+ *
+ * Called from fcoe transport.
+ *
+ * Returns: always returns true as this is the default FCoE transport,
+ * i.e., support all netdevs.
+ */
+static bool fcoe_match(struct net_device *netdev)
+{
+	return true;
+}
+
+/**
  * fcoe_create() - Create a fcoe interface
- * @buffer: The name of the Ethernet interface to create on
- * @kp:	    The associated kernel param
+ * @netdev  : The net_device object the Ethernet interface to create on
+ * @fip_mode: The FIP mode for this creation
  *
- * Called from sysfs.
+ * Called from fcoe transport
  *
  * Returns: 0 for success
  */
-static int fcoe_create(const char *buffer, struct kernel_param *kp)
+static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
 {
-	enum fip_state fip_mode = (enum fip_state)(long)kp->arg;
 	int rc;
 	struct fcoe_interface *fcoe;
 	struct fc_lport *lport;
-	struct net_device *netdev;
 
 	mutex_lock(&fcoe_config_mutex);
 
 	if (!rtnl_trylock()) {
 		mutex_unlock(&fcoe_config_mutex);
-		return restart_syscall();
+		return -ERESTARTSYS;
 	}
 
 #ifdef CONFIG_FCOE_MODULE
@@ -2178,22 +2118,16 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 	}
 #endif
 
-	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;
+		goto out_nodev;
 	}
 
 	fcoe = fcoe_interface_create(netdev, fip_mode);
 	if (IS_ERR(fcoe)) {
 		rc = PTR_ERR(fcoe);
-		goto out_putdev;
+		goto out_nodev;
 	}
 
 	lport = fcoe_if_create(fcoe, &netdev->dev, 0);
@@ -2222,15 +2156,12 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 	 * should be holding a reference taken in fcoe_if_create().
 	 */
 	fcoe_interface_put(fcoe);
-	dev_put(netdev);
 	rtnl_unlock();
 	mutex_unlock(&fcoe_config_mutex);
 
 	return 0;
 out_free:
 	fcoe_interface_put(fcoe);
-out_putdev:
-	dev_put(netdev);
 out_nodev:
 	rtnl_unlock();
 	mutex_unlock(&fcoe_config_mutex);
@@ -2433,6 +2364,18 @@ static int fcoe_hostlist_add(const struct fc_lport *lport)
 	return 0;
 }
 
+
+static struct fcoe_transport fcoe_sw_transport = {
+	.name = {FCOE_TRANSPORT_DEFAULT},
+	.attached = false,
+	.list = LIST_HEAD_INIT(fcoe_sw_transport.list),
+	.match = fcoe_match,
+	.create = fcoe_create,
+	.destroy = fcoe_destroy,
+	.enable = fcoe_enable,
+	.disable = fcoe_disable,
+};
+
 /**
  * fcoe_init() - Initialize fcoe.ko
  *
@@ -2444,6 +2387,14 @@ static int __init fcoe_init(void)
 	unsigned int cpu;
 	int rc = 0;
 
+	/* register as a fcoe transport */
+	rc = fcoe_transport_attach(&fcoe_sw_transport);
+	if (rc) {
+		printk(KERN_ERR "failed to register an fcoe transport, check "
+			"if libfcoe is loaded\n");
+		return rc;
+	}
+
 	mutex_lock(&fcoe_config_mutex);
 
 	for_each_possible_cpu(cpu) {
@@ -2520,6 +2471,9 @@ static void __exit fcoe_exit(void)
 	/* detach from scsi transport
 	 * must happen after all destroys are done, therefor after the flush */
 	fcoe_if_exit();
+
+	/* detach from fcoe transport */
+	fcoe_transport_detach(&fcoe_sw_transport);
 }
 module_exit(fcoe_exit);
 


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

* [PATCH 25/28] libfc: Extending lport's roles for target if there is a registered target.
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (23 preceding siblings ...)
  2011-01-29  0:05 ` [PATCH 24/28] fcoe: convert fcoe.ko to become an fcoe transport provider driver Robert Love
@ 2011-01-29  0:05 ` Robert Love
  2011-01-29  0:05 ` [PATCH 26/28] libfc: introduce LLD event callback Robert Love
                   ` (2 subsequent siblings)
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:05 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Kiran Patil

From: Kiran Patil <kiran.patil@intel.com>

Reason: From initaitor machine, when queried role of target (other end of connection),
        it is "initiator", hence SCSI-ml doesn't send any LUN Inquiry commands.

Fix:    If there is a registered target for FC_TYPE_FCP, extend lport's params (capability)
        to be target as well, By default lport params are INITIATOR only. Having this fix,
        caused initiator to send SCSI LUN inquiry command to target.

Technical Details: N/A

Signed-off-by: Kiran Patil <kiran.patil@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_libfc.c |   19 +++++++++++++++++++
 drivers/scsi/libfc/fc_libfc.h |    1 +
 drivers/scsi/libfc/fc_lport.c |    1 +
 3 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
index 5e40dab..b773512 100644
--- a/drivers/scsi/libfc/fc_libfc.c
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -232,6 +232,25 @@ void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp,
 }
 EXPORT_SYMBOL(fc_fill_reply_hdr);
 
+/**
+ * fc_fc4_conf_lport_params() - Modify "service_params" of specified lport
+ * if there is service provider (target provider) registered with libfc
+ * for specified "fc_ft_type"
+ * @lport: Local port which service_params needs to be modified
+ * @type: FC-4 type, such as FC_TYPE_FCP
+ */
+void fc_fc4_conf_lport_params(struct fc_lport *lport, enum fc_fh_type type)
+{
+	struct fc4_prov *prov_entry;
+	BUG_ON(type >= FC_FC4_PROV_SIZE);
+	BUG_ON(!lport);
+	prov_entry = fc_passive_prov[type];
+	if (type == FC_TYPE_FCP) {
+		if (prov_entry && prov_entry->recv)
+			lport->service_params |= FCP_SPPF_TARG_FCN;
+	}
+}
+
 void fc_lport_iterate(void (*notify)(struct fc_lport *, void *), void *arg)
 {
 	struct fc_lport *lport;
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
index 8496f70..fedc819 100644
--- a/drivers/scsi/libfc/fc_libfc.h
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -125,6 +125,7 @@ void fc_destroy_fcp(void);
 const char *fc_els_resp_type(struct fc_frame *);
 extern void fc_fc4_add_lport(struct fc_lport *);
 extern void fc_fc4_del_lport(struct fc_lport *);
+extern void fc_fc4_conf_lport_params(struct fc_lport *, enum fc_fh_type);
 
 /*
  * Copies a buffer into an sg list
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index e0ef814..735f1f8 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -1597,6 +1597,7 @@ int fc_lport_config(struct fc_lport *lport)
 
 	fc_lport_add_fc4_type(lport, FC_TYPE_FCP);
 	fc_lport_add_fc4_type(lport, FC_TYPE_CT);
+	fc_fc4_conf_lport_params(lport, FC_TYPE_FCP);
 
 	return 0;
 }


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

* [PATCH 26/28] libfc: introduce LLD event callback
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (24 preceding siblings ...)
  2011-01-29  0:05 ` [PATCH 25/28] libfc: Extending lport's roles for target if there is a registered target Robert Love
@ 2011-01-29  0:05 ` Robert Love
  2011-01-29  0:05 ` [PATCH 27/28] fcoe: use dedicated workqueue instead of system_wq Robert Love
  2011-01-29  0:05 ` [PATCH 28/28] libfcoe: Move common code from fcoe to libfcoe module Robert Love
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:05 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Bhanu Prakash Gollapudi

From: Bhanu Prakash Gollapudi <bprakash@broadcom.com>

This patch enables LLD to listen to rport events and perform LLD
specific operations based on the rport event. This patch also stores
sp_features and spp_type in rdata for further reference by LLD.

Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_npiv.c  |    1 +
 drivers/scsi/libfc/fc_rport.c |   19 ++++++++++++++++++-
 include/scsi/libfc.h          |   15 +++++++++++++++
 3 files changed, 34 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c
index dd2b43b..076cd5f 100644
--- a/drivers/scsi/libfc/fc_npiv.c
+++ b/drivers/scsi/libfc/fc_npiv.c
@@ -86,6 +86,7 @@ struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id)
 
 	return lport;
 }
+EXPORT_SYMBOL(fc_vport_id_lookup);
 
 /*
  * When setting the link state of vports during an lport state change, it's
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 9ded612..59b16bb 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -145,8 +145,10 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
 	rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;
 	INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout);
 	INIT_WORK(&rdata->event_work, fc_rport_work);
-	if (port_id != FC_FID_DIR_SERV)
+	if (port_id != FC_FID_DIR_SERV) {
+		rdata->lld_event_callback = lport->tt.rport_event_callback;
 		list_add_rcu(&rdata->peers, &lport->disc.rports);
+	}
 	return rdata;
 }
 
@@ -302,6 +304,10 @@ static void fc_rport_work(struct work_struct *work)
 			FC_RPORT_DBG(rdata, "callback ev %d\n", event);
 			rport_ops->event_callback(lport, rdata, event);
 		}
+		if (rdata->lld_event_callback) {
+			FC_RPORT_DBG(rdata, "lld callback ev %d\n", event);
+			rdata->lld_event_callback(lport, rdata, event);
+		}
 		kref_put(&rdata->kref, lport->tt.rport_destroy);
 		break;
 
@@ -324,6 +330,10 @@ static void fc_rport_work(struct work_struct *work)
 			FC_RPORT_DBG(rdata, "callback ev %d\n", event);
 			rport_ops->event_callback(lport, rdata, event);
 		}
+		if (rdata->lld_event_callback) {
+			FC_RPORT_DBG(rdata, "lld callback ev %d\n", event);
+			rdata->lld_event_callback(lport, rdata, event);
+		}
 		cancel_delayed_work_sync(&rdata->retry_work);
 
 		/*
@@ -890,6 +900,9 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 		rdata->ids.port_name = get_unaligned_be64(&plp->fl_wwpn);
 		rdata->ids.node_name = get_unaligned_be64(&plp->fl_wwnn);
 
+		/* save plogi response sp_features for further reference */
+		rdata->sp_features = ntohs(plp->fl_csp.sp_features);
+
 		if (lport->point_to_multipoint)
 			fc_rport_login_complete(rdata, fp);
 		csp_seq = ntohs(plp->fl_csp.sp_tot_seq);
@@ -997,6 +1010,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 		resp_code = (pp->spp.spp_flags & FC_SPP_RESP_MASK);
 		FC_RPORT_DBG(rdata, "PRLI spp_flags = 0x%x\n",
 			     pp->spp.spp_flags);
+		rdata->spp_type = pp->spp.spp_type;
 		if (resp_code != FC_SPP_RESP_ACK) {
 			if (resp_code == FC_SPP_RESP_CONF)
 				fc_rport_error(rdata, fp);
@@ -1010,6 +1024,8 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 		fcp_parm = ntohl(pp->spp.spp_params);
 		if (fcp_parm & FCP_SPPF_RETRY)
 			rdata->flags |= FC_RP_FLAGS_RETRY;
+		if (fcp_parm & FCP_SPPF_CONF_COMPL)
+			rdata->flags |= FC_RP_FLAGS_CONF_REQ;
 
 		prov = fc_passive_prov[FC_TYPE_FCP];
 		if (prov) {
@@ -1719,6 +1735,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
 	spp = &pp->spp;
 	mutex_lock(&fc_prov_mutex);
 	while (len >= plen) {
+		rdata->spp_type = rspp->spp_type;
 		spp->spp_type = rspp->spp_type;
 		spp->spp_type_ext = rspp->spp_type_ext;
 		resp = 0;
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 6d64e44..24193c1 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -158,6 +158,7 @@ struct fc_rport_libfc_priv {
 	#define FC_RP_FLAGS_REC_SUPPORTED	(1 << 0)
 	#define FC_RP_FLAGS_RETRY		(1 << 1)
 	#define FC_RP_STARTED			(1 << 2)
+	#define FC_RP_FLAGS_CONF_REQ		(1 << 3)
 	unsigned int	           e_d_tov;
 	unsigned int	           r_a_tov;
 };
@@ -207,6 +208,11 @@ struct fc_rport_priv {
 	u32			    supported_classes;
 	u16                         prli_count;
 	struct rcu_head		    rcu;
+	u16			    sp_features;
+	u8			    spp_type;
+	void			    (*lld_event_callback)(struct fc_lport *,
+						      struct fc_rport_priv *,
+						      enum fc_rport_event);
 };
 
 /**
@@ -677,6 +683,15 @@ struct libfc_function_template {
 	void (*rport_destroy)(struct kref *);
 
 	/*
+	 * Callback routine after the remote port is logged in
+	 *
+	 * STATUS: OPTIONAL
+	 */
+	void (*rport_event_callback)(struct fc_lport *,
+				     struct fc_rport_priv *,
+				     enum fc_rport_event);
+
+	/*
 	 * Send a fcp cmd from fsp pkt.
 	 * Called with the SCSI host lock unlocked and irqs disabled.
 	 *


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

* [PATCH 27/28] fcoe: use dedicated workqueue instead of system_wq
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (25 preceding siblings ...)
  2011-01-29  0:05 ` [PATCH 26/28] libfc: introduce LLD event callback Robert Love
@ 2011-01-29  0:05 ` Robert Love
  2011-01-29  0:05 ` [PATCH 28/28] libfcoe: Move common code from fcoe to libfcoe module Robert Love
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:05 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Tejun Heo

From: Tejun Heo <tj@kernel.org>

fcoe uses the system_wq to destroy ports and the work items need to be
flushed before the driver is unloaded.  As the work items free the
containing data structure, they can't be flushed directly.  The
workqueue should be flushed instead.

Also, the destruction works can be chained - ie. destruction of a port
may lead to destruction of another port where the work item for the
former queues the work for the latter.  Currently, the depth of chain
can be at most two and fcoe_exit() makes sure everything is complete
by calling flush_scheduled_work() twice.

With commit c8efcc25 (workqueue: allow chained queueing during
destruction), destroy_workqueue() can take care of chained works on
workqueue destruction.  Add and use fcoe_wq instead.  Simply
destroying fcoe_wq on driver unload takes care of flushing.

Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c |   32 +++++++++++++++++++++-----------
 1 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 8a1005d..46c57e5 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -31,6 +31,7 @@
 #include <linux/fs.h>
 #include <linux/sysfs.h>
 #include <linux/ctype.h>
+#include <linux/workqueue.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
 #include <scsi/scsi_transport.h>
@@ -58,6 +59,8 @@ MODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for "	\
 
 DEFINE_MUTEX(fcoe_config_mutex);
 
+static struct workqueue_struct *fcoe_wq;
+
 /* fcoe_percpu_clean completion.  Waiter protected by fcoe_create_mutex */
 static DECLARE_COMPLETION(fcoe_flush_completion);
 
@@ -1896,7 +1899,7 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 		list_del(&fcoe->list);
 		port = lport_priv(fcoe->ctlr.lp);
 		fcoe_interface_cleanup(fcoe);
-		schedule_work(&port->destroy_work);
+		queue_work(fcoe_wq, &port->destroy_work);
 		goto out;
 		break;
 	case NETDEV_FEAT_CHANGE:
@@ -2387,6 +2390,10 @@ static int __init fcoe_init(void)
 	unsigned int cpu;
 	int rc = 0;
 
+	fcoe_wq = alloc_workqueue("fcoe", 0, 0);
+	if (!fcoe_wq)
+		return -ENOMEM;
+
 	/* register as a fcoe transport */
 	rc = fcoe_transport_attach(&fcoe_sw_transport);
 	if (rc) {
@@ -2425,6 +2432,7 @@ out_free:
 		fcoe_percpu_thread_destroy(cpu);
 	}
 	mutex_unlock(&fcoe_config_mutex);
+	destroy_workqueue(fcoe_wq);
 	return rc;
 }
 module_init(fcoe_init);
@@ -2450,7 +2458,7 @@ static void __exit fcoe_exit(void)
 		list_del(&fcoe->list);
 		port = lport_priv(fcoe->ctlr.lp);
 		fcoe_interface_cleanup(fcoe);
-		schedule_work(&port->destroy_work);
+		queue_work(fcoe_wq, &port->destroy_work);
 	}
 	rtnl_unlock();
 
@@ -2461,15 +2469,17 @@ static void __exit fcoe_exit(void)
 
 	mutex_unlock(&fcoe_config_mutex);
 
-	/* flush any asyncronous interface destroys,
-	 * this should happen after the netdev notifier is unregistered */
-	flush_scheduled_work();
-	/* That will flush out all the N_Ports on the hostlist, but now we
-	 * may have NPIV VN_Ports scheduled for destruction */
-	flush_scheduled_work();
+	/*
+	 * destroy_work's may be chained but destroy_workqueue()
+	 * can take care of them. Just kill the fcoe_wq.
+	 */
+	destroy_workqueue(fcoe_wq);
 
-	/* detach from scsi transport
-	 * must happen after all destroys are done, therefor after the flush */
+	/*
+	 * Detaching from the scsi transport must happen after all
+	 * destroys are done on the fcoe_wq. destroy_workqueue will
+	 * enusre the fcoe_wq is flushed.
+	 */
 	fcoe_if_exit();
 
 	/* detach from fcoe transport */
@@ -2618,7 +2628,7 @@ static int fcoe_vport_destroy(struct fc_vport *vport)
 	mutex_lock(&n_port->lp_mutex);
 	list_del(&vn_port->list);
 	mutex_unlock(&n_port->lp_mutex);
-	schedule_work(&port->destroy_work);
+	queue_work(fcoe_wq, &port->destroy_work);
 	return 0;
 }
 


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

* [PATCH 28/28] libfcoe: Move common code from fcoe to libfcoe module
  2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
                   ` (26 preceding siblings ...)
  2011-01-29  0:05 ` [PATCH 27/28] fcoe: use dedicated workqueue instead of system_wq Robert Love
@ 2011-01-29  0:05 ` Robert Love
  27 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-01-29  0:05 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Bhanu Prakash Gollapudi

From: Bhanu Prakash Gollapudi <bprakash@broadcom.com>

To facilitate LLDDs to reuse the code, skb queue related functions are moved to
libfcoe, so that both fcoe and bnx2fc drivers can use them. The common structures
fcoe_port, fcoe_percpu_s are moved to libfcoe. fcoe_port will now have an
opaque pointer that points to corresponding driver's interface structure.
Also, fcoe_start_io and fcoe_fc_crc are moved to libfcoe.

As part of this change, fixed fcoe_start_io to return ENOMEM if
skb_clone fails.

Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c           |  217 +++---------------------------------
 drivers/scsi/fcoe/fcoe.h           |   44 -------
 drivers/scsi/fcoe/fcoe_transport.c |  200 +++++++++++++++++++++++++++++++++
 include/scsi/libfcoe.h             |   51 ++++++++
 4 files changed, 275 insertions(+), 237 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 46c57e5..495456f 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -75,7 +75,6 @@ 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 *);
-static void fcoe_clean_pending_queue(struct fc_lport *);
 static void fcoe_percpu_clean(struct fc_lport *);
 static int fcoe_link_speed_update(struct fc_lport *);
 static int fcoe_link_ok(struct fc_lport *);
@@ -83,7 +82,6 @@ static int fcoe_link_ok(struct fc_lport *);
 static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
 static int fcoe_hostlist_add(const struct fc_lport *);
 
-static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *);
 static int fcoe_device_notification(struct notifier_block *, ulong, void *);
 static void fcoe_dev_setup(void);
 static void fcoe_dev_cleanup(void);
@@ -506,7 +504,7 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
 static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr)
 {
 	struct fcoe_port *port = lport_priv(lport);
-	struct fcoe_interface *fcoe = port->fcoe;
+	struct fcoe_interface *fcoe = port->priv;
 
 	rtnl_lock();
 	if (!is_zero_ether_addr(port->data_src_addr))
@@ -562,17 +560,6 @@ static int fcoe_lport_config(struct fc_lport *lport)
 }
 
 /**
- * fcoe_queue_timer() - The fcoe queue timer
- * @lport: The local port
- *
- * Calls fcoe_check_wait_queue on timeout
- */
-static void fcoe_queue_timer(ulong lport)
-{
-	fcoe_check_wait_queue((struct fc_lport *)lport, NULL);
-}
-
-/**
  * fcoe_get_wwn() - Get the world wide name from LLD if it supports it
  * @netdev: the associated net device
  * @wwn: the output WWN
@@ -651,7 +638,7 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
 
 	/* Setup lport private data to point to fcoe softc */
 	port = lport_priv(lport);
-	fcoe = port->fcoe;
+	fcoe = port->priv;
 
 	/*
 	 * Determine max frame size based on underlying device and optional
@@ -761,7 +748,7 @@ bool fcoe_oem_match(struct fc_frame *fp)
 static inline int fcoe_em_config(struct fc_lport *lport)
 {
 	struct fcoe_port *port = lport_priv(lport);
-	struct fcoe_interface *fcoe = port->fcoe;
+	struct fcoe_interface *fcoe = port->priv;
 	struct fcoe_interface *oldfcoe = NULL;
 	struct net_device *old_real_dev, *cur_real_dev;
 	u16 min_xid = FCOE_MIN_XID;
@@ -845,7 +832,7 @@ skip_oem:
 static void fcoe_if_destroy(struct fc_lport *lport)
 {
 	struct fcoe_port *port = lport_priv(lport);
-	struct fcoe_interface *fcoe = port->fcoe;
+	struct fcoe_interface *fcoe = port->priv;
 	struct net_device *netdev = fcoe->netdev;
 
 	FCOE_NETDEV_DBG(netdev, "Destroying interface\n");
@@ -966,7 +953,9 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
 	}
 	port = lport_priv(lport);
 	port->lport = lport;
-	port->fcoe = fcoe;
+	port->priv = fcoe;
+	port->max_queue_depth = FCOE_MAX_QUEUE_DEPTH;
+	port->min_queue_depth = FCOE_MIN_QUEUE_DEPTH;
 	INIT_WORK(&port->destroy_work, fcoe_destroy_work);
 
 	/* configure a fc_lport including the exchange manager */
@@ -1362,108 +1351,22 @@ err2:
 }
 
 /**
- * fcoe_start_io() - Start FCoE I/O
- * @skb: The packet to be transmitted
- *
- * This routine is called from the net device to start transmitting
- * FCoE packets.
- *
- * Returns: 0 for success
- */
-static inline int fcoe_start_io(struct sk_buff *skb)
-{
-	struct sk_buff *nskb;
-	int rc;
-
-	nskb = skb_clone(skb, GFP_ATOMIC);
-	rc = dev_queue_xmit(nskb);
-	if (rc != 0)
-		return rc;
-	kfree_skb(skb);
-	return 0;
-}
-
-/**
- * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC
+ * fcoe_alloc_paged_crc_eof() - Allocate a page to be used for the trailer CRC
  * @skb:  The packet to be transmitted
  * @tlen: The total length of the trailer
  *
- * This routine allocates a page for frame trailers. The page is re-used if
- * there is enough room left on it for the current trailer. If there isn't
- * enough buffer left a new page is allocated for the trailer. Reference to
- * the page from this function as well as the skbs using the page fragments
- * ensure that the page is freed at the appropriate time.
- *
  * Returns: 0 for success
  */
-static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
+static int fcoe_alloc_paged_crc_eof(struct sk_buff *skb, int tlen)
 {
 	struct fcoe_percpu_s *fps;
-	struct page *page;
+	int rc;
 
 	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);
-	}
+	rc = fcoe_get_paged_crc_eof(skb, tlen, fps);
 	put_cpu_var(fcoe_percpu);
-	return 0;
-}
 
-/**
- * fcoe_fc_crc() - Calculates the CRC for a given frame
- * @fp: The frame to be checksumed
- *
- * This uses crc32() routine to calculate the CRC for a frame
- *
- * Return: The 32 bit CRC value
- */
-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;
+	return rc;
 }
 
 /**
@@ -1486,7 +1389,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
 	unsigned int tlen;		/* trailer length */
 	unsigned int elen;		/* eth header, may include vlan */
 	struct fcoe_port *port = lport_priv(lport);
-	struct fcoe_interface *fcoe = port->fcoe;
+	struct fcoe_interface *fcoe = port->priv;
 	u8 sof, eof;
 	struct fcoe_hdr *hp;
 
@@ -1527,7 +1430,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
 	/* copy port crc and eof to the skb buff */
 	if (skb_is_nonlinear(skb)) {
 		skb_frag_t *frag;
-		if (fcoe_get_paged_crc_eof(skb, tlen)) {
+		if (fcoe_alloc_paged_crc_eof(skb, tlen)) {
 			kfree_skb(skb);
 			return -ENOMEM;
 		}
@@ -1636,7 +1539,7 @@ static inline int fcoe_filter_frames(struct fc_lport *lport,
 	if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && fh->fh_type == FC_TYPE_FCP)
 		return 0;
 
-	fcoe = ((struct fcoe_port *)lport_priv(lport))->fcoe;
+	fcoe = ((struct fcoe_port *)lport_priv(lport))->priv;
 	if (is_fip_mode(&fcoe->ctlr) && fc_frame_payload_op(fp) == ELS_LOGO &&
 	    ntoh24(fh->fh_s_id) == FC_FID_FLOGI) {
 		FCOE_DBG("fcoe: dropping FCoE lport LOGO in fip mode\n");
@@ -1771,64 +1674,6 @@ int fcoe_percpu_receive_thread(void *arg)
 }
 
 /**
- * fcoe_check_wait_queue() - Attempt to clear the transmit backlog
- * @lport: The local port whose backlog is to be cleared
- *
- * This empties the wait_queue, dequeues the head of the wait_queue queue
- * and calls fcoe_start_io() for each packet. If all skb have been
- * transmitted it returns the qlen. If an error occurs it restores
- * wait_queue (to try again later) and returns -1.
- *
- * The wait_queue is used when the skb transmit fails. The failed skb
- * will go in the wait_queue which will be emptied by the timer function or
- * by the next skb transmit.
- */
-static void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb)
-{
-	struct fcoe_port *port = lport_priv(lport);
-	int rc;
-
-	spin_lock_bh(&port->fcoe_pending_queue.lock);
-
-	if (skb)
-		__skb_queue_tail(&port->fcoe_pending_queue, skb);
-
-	if (port->fcoe_pending_queue_active)
-		goto out;
-	port->fcoe_pending_queue_active = 1;
-
-	while (port->fcoe_pending_queue.qlen) {
-		/* keep qlen > 0 until fcoe_start_io succeeds */
-		port->fcoe_pending_queue.qlen++;
-		skb = __skb_dequeue(&port->fcoe_pending_queue);
-
-		spin_unlock_bh(&port->fcoe_pending_queue.lock);
-		rc = fcoe_start_io(skb);
-		spin_lock_bh(&port->fcoe_pending_queue.lock);
-
-		if (rc) {
-			__skb_queue_head(&port->fcoe_pending_queue, skb);
-			/* undo temporary increment above */
-			port->fcoe_pending_queue.qlen--;
-			break;
-		}
-		/* undo temporary increment above */
-		port->fcoe_pending_queue.qlen--;
-	}
-
-	if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
-		lport->qfull = 0;
-	if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer))
-		mod_timer(&port->timer, jiffies + 2);
-	port->fcoe_pending_queue_active = 0;
-out:
-	if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
-		lport->qfull = 1;
-	spin_unlock_bh(&port->fcoe_pending_queue.lock);
-	return;
-}
-
-/**
  * fcoe_dev_setup() - Setup the link change notification interface
  */
 static void fcoe_dev_setup(void)
@@ -2180,8 +2025,7 @@ out_nodev:
  */
 int fcoe_link_speed_update(struct fc_lport *lport)
 {
-	struct fcoe_port *port = lport_priv(lport);
-	struct net_device *netdev = port->fcoe->netdev;
+	struct net_device *netdev = fcoe_netdev(lport);
 	struct ethtool_cmd ecmd = { ETHTOOL_GSET };
 
 	if (!dev_ethtool_get_settings(netdev, &ecmd)) {
@@ -2212,8 +2056,7 @@ int fcoe_link_speed_update(struct fc_lport *lport)
  */
 int fcoe_link_ok(struct fc_lport *lport)
 {
-	struct fcoe_port *port = lport_priv(lport);
-	struct net_device *netdev = port->fcoe->netdev;
+	struct net_device *netdev = fcoe_netdev(lport);
 
 	if (netif_oper_up(netdev))
 		return 0;
@@ -2277,24 +2120,6 @@ void fcoe_percpu_clean(struct fc_lport *lport)
 }
 
 /**
- * fcoe_clean_pending_queue() - Dequeue a skb and free it
- * @lport: The local port to dequeue a skb on
- */
-void fcoe_clean_pending_queue(struct fc_lport *lport)
-{
-	struct fcoe_port  *port = lport_priv(lport);
-	struct sk_buff *skb;
-
-	spin_lock_bh(&port->fcoe_pending_queue.lock);
-	while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) {
-		spin_unlock_bh(&port->fcoe_pending_queue.lock);
-		kfree_skb(skb);
-		spin_lock_bh(&port->fcoe_pending_queue.lock);
-	}
-	spin_unlock_bh(&port->fcoe_pending_queue.lock);
-}
-
-/**
  * fcoe_reset() - Reset a local port
  * @shost: The SCSI host associated with the local port to be reset
  *
@@ -2361,7 +2186,7 @@ static int fcoe_hostlist_add(const struct fc_lport *lport)
 	fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport));
 	if (!fcoe) {
 		port = lport_priv(lport);
-		fcoe = port->fcoe;
+		fcoe = port->priv;
 		list_add_tail(&fcoe->list, &fcoe_hostlist);
 	}
 	return 0;
@@ -2555,7 +2380,7 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,
 				      void *arg, u32 timeout)
 {
 	struct fcoe_port *port = lport_priv(lport);
-	struct fcoe_interface *fcoe = port->fcoe;
+	struct fcoe_interface *fcoe = port->priv;
 	struct fcoe_ctlr *fip = &fcoe->ctlr;
 	struct fc_frame_header *fh = fc_frame_header_get(fp);
 
@@ -2588,7 +2413,7 @@ static int fcoe_vport_create(struct fc_vport *vport, bool disabled)
 	struct Scsi_Host *shost = vport_to_shost(vport);
 	struct fc_lport *n_port = shost_priv(shost);
 	struct fcoe_port *port = lport_priv(n_port);
-	struct fcoe_interface *fcoe = port->fcoe;
+	struct fcoe_interface *fcoe = port->priv;
 	struct net_device *netdev = fcoe->netdev;
 	struct fc_lport *vn_port;
 
@@ -2732,7 +2557,7 @@ static void fcoe_set_port_id(struct fc_lport *lport,
 			     u32 port_id, struct fc_frame *fp)
 {
 	struct fcoe_port *port = lport_priv(lport);
-	struct fcoe_interface *fcoe = port->fcoe;
+	struct fcoe_interface *fcoe = port->priv;
 
 	if (fp && fc_frame_payload_op(fp) == ELS_FLOGI)
 		fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp);
diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h
index c69b2c5..d775128 100644
--- a/drivers/scsi/fcoe/fcoe.h
+++ b/drivers/scsi/fcoe/fcoe.h
@@ -24,7 +24,7 @@
 #include <linux/kthread.h>
 
 #define FCOE_MAX_QUEUE_DEPTH	256
-#define FCOE_LOW_QUEUE_DEPTH	32
+#define FCOE_MIN_QUEUE_DEPTH	32
 
 #define FCOE_WORD_TO_BYTE	4
 
@@ -71,21 +71,6 @@ do {                                                            	\
 				  netdev->name, ##args);)
 
 /**
- * struct fcoe_percpu_s - The per-CPU context for FCoE receive threads
- * @thread:	    The thread context
- * @fcoe_rx_list:   The queue of pending packets to process
- * @page:	    The memory page for calculating frame trailer CRCs
- * @crc_eof_offset: The offset into the CRC page pointing to available
- *		    memory for a new trailer
- */
-struct fcoe_percpu_s {
-	struct task_struct *thread;
-	struct sk_buff_head fcoe_rx_list;
-	struct page *crc_eof_page;
-	int crc_eof_offset;
-};
-
-/**
  * struct fcoe_interface - A FCoE interface
  * @list:	      Handle for a list of FCoE interfaces
  * @netdev:	      The associated net device
@@ -108,30 +93,6 @@ struct fcoe_interface {
 	struct kref	   kref;
 };
 
-/**
- * struct fcoe_port - The FCoE private structure
- * @fcoe:		       The associated fcoe interface
- * @lport:		       The associated local port
- * @fcoe_pending_queue:	       The pending Rx queue of skbs
- * @fcoe_pending_queue_active: Indicates if the pending queue is active
- * @timer:		       The queue timer
- * @destroy_work:	       Handle for work context
- *			       (to prevent RTNL deadlocks)
- * @data_srt_addr:	       Source address for data
- *
- * An instance of this structure is to be allocated along with the
- * Scsi_Host and libfc fc_lport structures.
- */
-struct fcoe_port {
-	struct fcoe_interface *fcoe;
-	struct fc_lport	      *lport;
-	struct sk_buff_head   fcoe_pending_queue;
-	u8		      fcoe_pending_queue_active;
-	struct timer_list     timer;
-	struct work_struct    destroy_work;
-	u8		      data_src_addr[ETH_ALEN];
-};
-
 #define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr)
 
 /**
@@ -140,7 +101,8 @@ struct fcoe_port {
  */
 static inline struct net_device *fcoe_netdev(const struct fc_lport *lport)
 {
-	return ((struct fcoe_port *)lport_priv(lport))->fcoe->netdev;
+	return ((struct fcoe_interface *)
+			((struct fcoe_port *)lport_priv(lport))->priv)->netdev;
 }
 
 #endif /* _FCOE_H_ */
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index e5aef56..745eb9a 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -23,6 +23,7 @@
 #include <linux/list.h>
 #include <linux/netdevice.h>
 #include <linux/errno.h>
+#include <linux/crc32.h>
 #include <scsi/libfcoe.h>
 
 #include "libfcoe.h"
@@ -75,6 +76,205 @@ __MODULE_PARM_TYPE(disable, "string");
 MODULE_PARM_DESC(disable, " Disables fcoe on a ethernet interface.");
 
 /**
+ * fcoe_fc_crc() - Calculates the CRC for a given frame
+ * @fp: The frame to be checksumed
+ *
+ * This uses crc32() routine to calculate the CRC for a frame
+ *
+ * Return: The 32 bit CRC value
+ */
+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_start_io() - Start FCoE I/O
+ * @skb: The packet to be transmitted
+ *
+ * This routine is called from the net device to start transmitting
+ * FCoE packets.
+ *
+ * Returns: 0 for success
+ */
+int fcoe_start_io(struct sk_buff *skb)
+{
+	struct sk_buff *nskb;
+	int rc;
+
+	nskb = skb_clone(skb, GFP_ATOMIC);
+	if (!nskb)
+		return -ENOMEM;
+	rc = dev_queue_xmit(nskb);
+	if (rc != 0)
+		return rc;
+	kfree_skb(skb);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_start_io);
+
+
+/**
+ * fcoe_clean_pending_queue() - Dequeue a skb and free it
+ * @lport: The local port to dequeue a skb on
+ */
+void fcoe_clean_pending_queue(struct fc_lport *lport)
+{
+	struct fcoe_port  *port = lport_priv(lport);
+	struct sk_buff *skb;
+
+	spin_lock_bh(&port->fcoe_pending_queue.lock);
+	while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) {
+		spin_unlock_bh(&port->fcoe_pending_queue.lock);
+		kfree_skb(skb);
+		spin_lock_bh(&port->fcoe_pending_queue.lock);
+	}
+	spin_unlock_bh(&port->fcoe_pending_queue.lock);
+}
+EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
+
+/**
+ * fcoe_check_wait_queue() - Attempt to clear the transmit backlog
+ * @lport: The local port whose backlog is to be cleared
+ *
+ * This empties the wait_queue, dequeues the head of the wait_queue queue
+ * and calls fcoe_start_io() for each packet. If all skb have been
+ * transmitted it returns the qlen. If an error occurs it restores
+ * wait_queue (to try again later) and returns -1.
+ *
+ * The wait_queue is used when the skb transmit fails. The failed skb
+ * will go in the wait_queue which will be emptied by the timer function or
+ * by the next skb transmit.
+ */
+void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb)
+{
+	struct fcoe_port *port = lport_priv(lport);
+	int rc;
+
+	spin_lock_bh(&port->fcoe_pending_queue.lock);
+
+	if (skb)
+		__skb_queue_tail(&port->fcoe_pending_queue, skb);
+
+	if (port->fcoe_pending_queue_active)
+		goto out;
+	port->fcoe_pending_queue_active = 1;
+
+	while (port->fcoe_pending_queue.qlen) {
+		/* keep qlen > 0 until fcoe_start_io succeeds */
+		port->fcoe_pending_queue.qlen++;
+		skb = __skb_dequeue(&port->fcoe_pending_queue);
+
+		spin_unlock_bh(&port->fcoe_pending_queue.lock);
+		rc = fcoe_start_io(skb);
+		spin_lock_bh(&port->fcoe_pending_queue.lock);
+
+		if (rc) {
+			__skb_queue_head(&port->fcoe_pending_queue, skb);
+			/* undo temporary increment above */
+			port->fcoe_pending_queue.qlen--;
+			break;
+		}
+		/* undo temporary increment above */
+		port->fcoe_pending_queue.qlen--;
+	}
+
+	if (port->fcoe_pending_queue.qlen < port->min_queue_depth)
+		lport->qfull = 0;
+	if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer))
+		mod_timer(&port->timer, jiffies + 2);
+	port->fcoe_pending_queue_active = 0;
+out:
+	if (port->fcoe_pending_queue.qlen > port->max_queue_depth)
+		lport->qfull = 1;
+	spin_unlock_bh(&port->fcoe_pending_queue.lock);
+}
+EXPORT_SYMBOL_GPL(fcoe_check_wait_queue);
+
+/**
+ * fcoe_queue_timer() - The fcoe queue timer
+ * @lport: The local port
+ *
+ * Calls fcoe_check_wait_queue on timeout
+ */
+void fcoe_queue_timer(ulong lport)
+{
+	fcoe_check_wait_queue((struct fc_lport *)lport, NULL);
+}
+EXPORT_SYMBOL_GPL(fcoe_queue_timer);
+
+/**
+ * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC
+ * @skb:  The packet to be transmitted
+ * @tlen: The total length of the trailer
+ * @fps:  The fcoe context
+ *
+ * This routine allocates a page for frame trailers. The page is re-used if
+ * there is enough room left on it for the current trailer. If there isn't
+ * enough buffer left a new page is allocated for the trailer. Reference to
+ * the page from this function as well as the skbs using the page fragments
+ * ensure that the page is freed at the appropriate time.
+ *
+ * Returns: 0 for success
+ */
+int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen,
+			   struct fcoe_percpu_s *fps)
+{
+	struct page *page;
+
+	page = fps->crc_eof_page;
+	if (!page) {
+		page = alloc_page(GFP_ATOMIC);
+		if (!page)
+			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);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_get_paged_crc_eof);
+
+/**
  * fcoe_transport_lookup - find an fcoe transport that matches a netdev
  * @netdev: The netdev to look for from all attached transports
  *
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index efb6ae5..e502463 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -221,6 +221,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *, struct fc_lport *,
 u64 fcoe_wwn_from_mac(unsigned char mac[], unsigned int, unsigned int);
 int fcoe_libfc_config(struct fc_lport *, struct fcoe_ctlr *,
 		      const struct libfc_function_template *, int init_fcp);
+u32 fcoe_fc_crc(struct fc_frame *fp);
+int fcoe_start_io(struct sk_buff *skb);
 
 /**
  * is_fip_mode() - returns true if FIP mode selected.
@@ -267,6 +269,55 @@ struct fcoe_transport {
 };
 
 /**
+ * struct fcoe_percpu_s - The context for FCoE receive thread(s)
+ * @thread:	    The thread context
+ * @fcoe_rx_list:   The queue of pending packets to process
+ * @page:	    The memory page for calculating frame trailer CRCs
+ * @crc_eof_offset: The offset into the CRC page pointing to available
+ *		    memory for a new trailer
+ */
+struct fcoe_percpu_s {
+	struct task_struct *thread;
+	struct sk_buff_head fcoe_rx_list;
+	struct page *crc_eof_page;
+	int crc_eof_offset;
+};
+
+/**
+ * struct fcoe_port - The FCoE private structure
+ * @priv:		       The associated fcoe interface. The structure is
+ *			       defined by the low level driver
+ * @lport:		       The associated local port
+ * @fcoe_pending_queue:	       The pending Rx queue of skbs
+ * @fcoe_pending_queue_active: Indicates if the pending queue is active
+ * @max_queue_depth:	       Max queue depth of pending queue
+ * @min_queue_depth:	       Min queue depth of pending queue
+ * @timer:		       The queue timer
+ * @destroy_work:	       Handle for work context
+ *			       (to prevent RTNL deadlocks)
+ * @data_srt_addr:	       Source address for data
+ *
+ * An instance of this structure is to be allocated along with the
+ * Scsi_Host and libfc fc_lport structures.
+ */
+struct fcoe_port {
+	void		      *priv;
+	struct fc_lport	      *lport;
+	struct sk_buff_head   fcoe_pending_queue;
+	u8		      fcoe_pending_queue_active;
+	u32		      max_queue_depth;
+	u32		      min_queue_depth;
+	struct timer_list     timer;
+	struct work_struct    destroy_work;
+	u8		      data_src_addr[ETH_ALEN];
+};
+void fcoe_clean_pending_queue(struct fc_lport *);
+void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb);
+void fcoe_queue_timer(ulong lport);
+int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen,
+			   struct fcoe_percpu_s *fps);
+
+/**
  * struct netdev_list
  * A mapping from netdevice to fcoe_transport
  */


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

* Re: [PATCH 20/28] libfcoe: add implementation to support fcoe transport
  2011-01-29  0:04 ` [PATCH 20/28] libfcoe: add implementation to support fcoe transport Robert Love
@ 2011-02-02  6:43   ` Mike Christie
  2011-02-03  2:13     ` Robert Love
  0 siblings, 1 reply; 31+ messages in thread
From: Mike Christie @ 2011-02-02  6:43 UTC (permalink / raw)
  To: Robert Love; +Cc: James.Bottomley, linux-scsi, Bhanu Prakash Gollapudi, Yi Zou

On 01/28/2011 06:04 PM, Robert Love wrote:
> +static int fcoe_transport_create(const char *buffer, struct kernel_param *kp)
> +{
> +	int rc = -ENODEV;
> +	struct net_device *netdev = NULL;
> +	struct fcoe_transport *ft = NULL;
> +	enum fip_state fip_mode = (enum fip_state)(long)kp->arg;
> +
> +	if (!mutex_trylock(&ft_mutex))
> +		return restart_syscall();

I was just wondering what is the reason for this. I see it in other 
network related sysfs code but no where else. Is there some networky reason?

> +
> +#ifdef CONFIG_LIBFCOE_MODULE
> +	/*
> +	 * Make sure the module has been initialized, and is not about to be
> +	 * removed.  Module parameter sysfs files are writable before the
> +	 * module_init function is called and after module_exit.
> +	 */
> +	if (THIS_MODULE->state != MODULE_STATE_LIVE)
> +		goto out_nodev;
> +#endif

It seems that this would be a problem for every driver that uses module 
params. Should the module code be getting a module refcount or doing 
this test?

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

* Re: [PATCH 20/28] libfcoe: add implementation to support fcoe transport
  2011-02-02  6:43   ` Mike Christie
@ 2011-02-03  2:13     ` Robert Love
  0 siblings, 0 replies; 31+ messages in thread
From: Robert Love @ 2011-02-03  2:13 UTC (permalink / raw)
  To: Mike Christie
  Cc: James.Bottomley, linux-scsi, Bhanu Prakash Gollapudi, Zou, Yi

On Tue, 2011-02-01 at 22:43 -0800, Mike Christie wrote:
> On 01/28/2011 06:04 PM, Robert Love wrote:
> > +static int fcoe_transport_create(const char *buffer, struct kernel_param *kp)
> > +{
> > +	int rc = -ENODEV;
> > +	struct net_device *netdev = NULL;
> > +	struct fcoe_transport *ft = NULL;
> > +	enum fip_state fip_mode = (enum fip_state)(long)kp->arg;
> > +
> > +	if (!mutex_trylock(&ft_mutex))
> > +		return restart_syscall();
> 
> I was just wondering what is the reason for this. I see it in other 
> network related sysfs code but no where else. Is there some networky reason?

I think this may be unnecessary. We initially added a rtnl_trylock in
fcoe_create/destroy (fcoe.ko's sysfs entry-points) with change
34ce27bcf96f5f366e1fa8c4729ffc8a55de4cc3. I cannot think of a good
reason why this is a mutex_trylock/restart_syscall and not just a simple
mutex_lock.

> 
> > +
> > +#ifdef CONFIG_LIBFCOE_MODULE
> > +	/*
> > +	 * Make sure the module has been initialized, and is not about to be
> > +	 * removed.  Module parameter sysfs files are writable before the
> > +	 * module_init function is called and after module_exit.
> > +	 */
> > +	if (THIS_MODULE->state != MODULE_STATE_LIVE)
> > +		goto out_nodev;
> > +#endif
> 
> It seems that this would be a problem for every driver that uses module 
> params. Should the module code be getting a module refcount or doing 
> this test?

I think the problem is that the sysfs entries are created and are
writable before the module is truly initialized, so we could start using
uninitialized variables. It looks like a global solution would be to not
add sysfs files (or at least don't make them writable) until the module
initialization has completed. I have no idea how feasible this is.

//Rob


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

end of thread, other threads:[~2011-02-03  2:13 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-01-29  0:03 [PATCH 00/28] libfc/libfcoe/fcoe updates for scsi-misc (2.6.39) Robert Love
2011-01-29  0:03 ` [PATCH 01/28] libfc: always initialize the FCoE DDP exchange id for fsp as FC_XID_UNKNOWN Robert Love
2011-01-29  0:03 ` [PATCH 02/28] libfc: Return a valid return code in fc_fcp_pkt_abort() Robert Love
2011-01-29  0:03 ` [PATCH 03/28] libfc: Cleanup return paths in fc_rport_error_retry Robert Love
2011-01-29  0:03 ` [PATCH 04/28] libfc: dereferencing ERR_PTR in fc_tm_done() Robert Love
2011-01-29  0:03 ` [PATCH 05/28] fnic: fix memory leak Robert Love
2011-01-29  0:03 ` [PATCH 06/28] fnic: Bumping up fnic version from 1.4.0.145 to 1.5.0.1 Robert Love
2011-01-29  0:03 ` [PATCH 07/28] fcoe: Fix module reference count for vports Robert Love
2011-01-29  0:03 ` [PATCH 08/28] fcoe: drop FCoE LOGO in FIP mode Robert Love
2011-01-29  0:03 ` [PATCH 09/28] libfc: fix sparse static and non-ANSI warnings Robert Love
2011-01-29  0:04 ` [PATCH 10/28] libfc: add hook for FC-4 provider registration Robert Love
2011-01-29  0:04 ` [PATCH 11/28] libfc: add method for setting handler for incoming exchange Robert Love
2011-01-29  0:04 ` [PATCH 12/28] libfc: add local port hook for provider session lookup Robert Love
2011-01-29  0:04 ` [PATCH 13/28] libfc: add hook to notify providers of local port changes Robert Love
2011-01-29  0:04 ` [PATCH 14/28] libfc: use PRLI hook to get parameters when sending outgoing PRLI Robert Love
2011-01-29  0:04 ` [PATCH 15/28] libfc: Remove usage of the Scsi_Host's host_lock Robert Love
2011-01-29  0:04 ` [PATCH 16/28] libfc: export seq_release() for users of seq_assign() Robert Love
2011-01-29  0:04 ` [PATCH 17/28] libfc: Enhanced exchange ID selection mechanism and fix related EMA selection logic Robert Love
2011-01-29  0:04 ` [PATCH 18/28] libfcoe: move logging macros into the local libfcoe.h header file Robert Love
2011-01-29  0:04 ` [PATCH 19/28] libfcoe: add fcoe_transport structure defines to include/scsi/libfcoe.h Robert Love
2011-01-29  0:04 ` [PATCH 20/28] libfcoe: add implementation to support fcoe transport Robert Love
2011-02-02  6:43   ` Mike Christie
2011-02-03  2:13     ` Robert Love
2011-01-29  0:05 ` [PATCH 21/28] libfcoe: rename libfcoe.c to fcoe_cltr.c for the coming fcoe_transport.c Robert Love
2011-01-29  0:05 ` [PATCH 22/28] libfcoe: include fcoe_transport.c into kernel libfcoe module Robert Love
2011-01-29  0:05 ` [PATCH 23/28] fcoe: prepare fcoe for using fcoe transport Robert Love
2011-01-29  0:05 ` [PATCH 24/28] fcoe: convert fcoe.ko to become an fcoe transport provider driver Robert Love
2011-01-29  0:05 ` [PATCH 25/28] libfc: Extending lport's roles for target if there is a registered target Robert Love
2011-01-29  0:05 ` [PATCH 26/28] libfc: introduce LLD event callback Robert Love
2011-01-29  0:05 ` [PATCH 27/28] fcoe: use dedicated workqueue instead of system_wq Robert Love
2011-01-29  0:05 ` [PATCH 28/28] libfcoe: Move common code from fcoe to libfcoe module 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.