linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/3] FC subsystem update
@ 2010-05-12 20:20 Robert Love
  2010-05-12 20:20 ` [RFC PATCH 1/3] fc: Create FC sybsystem Robert Love
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Robert Love @ 2010-05-12 20:20 UTC (permalink / raw)
  To: linux-scsi; +Cc: james.smart, hare, christof.schmitt, james.bottomley

This series represents the continuation of the RFC archived here:
http://marc.info/?l=linux-scsi&m=126463466126962&w=2. The sysfs organization has
taken shape and object allocations make more sense than the previous RFC. The
patches have undergone my own developer testing, which has mostly focused on
NPIV port creation and deletion and some I/O.

This series creates a Fibre Channel subsystem that has an improved sysfs device
tree (the port's view of the fabric) and begins to abstract itself from SCSI.

This RFC is to solicit any feedback before I continue to refine these patches.
I've created two diagrams to help people understand the change. The first shows
the current sysfs device layout and the second shows the allocation scheme for
libfc and fcoe's primary data structures.

http://open-fcoe.org/rwlove/fc_sysfs.jpg
http://open-fcoe.org/rwlove/libfc_alloc.jpg

These patches apply to scsi-misc + the recently submitted fcoe patches, which
is the same as the fcoe-next tree on open-fcoe.org.

Overview of N_Port creation/login (for fcoe.ko):

1) First, fc.ko is installed and then scsi_transport_fcp.ko is installed.
   scsi_transport_fcp.ko registers itself (SCSI initiator) as a FC4 with
   fc.ko.

2) When a fcoe port is "created" from
   'echo ethX > /sys/module/fcoe/parameters/create' a fcport is allocated and
   added to sysfs. All of the port's physical properties are added as
   attributes.

<PCI root>/fcport_0

3) At this point libfc/libfcoe/fcoe needs an lport and an fcoe_port. The lport
   and fcoe_ports are private data of a fcvport so a fcvport is allocated,
   but not added to sysfs.

4) libfcoe.ko discovers FCFs and allocates/adds them as fcfabrics to sysfs
   as they are discovered.

   [ I think that we may want to revisit the idea of having a fcfport between
     the fcport and fcfabric. Right now I'm creating a fcfabric for each FCF
     that is discovered at the FIP phase, but that doesn't make much sense
     since multiple FCFs on the same fabric can be discovered. Adding a fcfport
     for each FCF discovered might be more correct. ]

<PCI root>/fcport_0/fcfabric_0

5) libfc.ko will FLOGI into the fabric and upon the FLOGI ACC the fcvport
   will be added to sysfs.

<PCI root>/fcport_0/fcfabric_0/fcvport_0

5.1) The FC layer will call fc4_init_add in the fc4_template. If SCSI
     is the FC4, fcp_init_add (scsi_transport_fcp) will be called. This
     routine calls scsi_host_alloc/scsi_add_host. There is also a callback
     down to the LLD so that it can setup any SCSI-FCP private data.

     [ I'm not sure how non-FCoE HBAs will do this. I imagine they will just
       create all three sysfs devices (fcport, fcfabric and fcvport) on the
       FLOGI ACC since they do not rely on a discovery protocol. ]

<PCI root>/fcport_0/fcfabric_0/fcvport_0/fcpinit_X/hostX

6) libfc will start discovery and as remote ports are discovered libfc will
   notify FC to create and add fcrports to sysfs.

<PCI root>/fcport_0/fcfabric_0/fcvport_0/fcpinit_X/hostX
					/fcrport_0

6.1) When the fcrports are created the FC layer will call fc4_targ_add in
     the fc4_template. If SCSI is the FC4, fcp_targ_add (scsi_transport_fcp)
     will be called. This routine will allocate a fcptarg device, add it to
     sysfs and notify SCSI to scan it.

<PCI root>/fcport_0/fcfabric_0/fcvport_0/fcpinit_X/hostX
					/fcrport_0/fcptargX:0-0


The current series shortcomings are:

1) The FC4 interface needs work to become more FC4 agnostic.
   (end of include/fc/fc.h)

2) Point-to-Point mode needs to be addressed

3) BSG needs to be tested and STGT should become another FC4 that is
   registered with FC. Since libfc/fcoe doesn't plug into STGT yet this
   will be a challenge.

4) user space updates - For the fcoe-utils package, libhbalinux (user
   space HBA API library) needs to be updated to read the new layout before
   heavy testing of the new layout can be done.

5) Miscellaneous problems like error handling, fcfabric deletion cleaning
   up vports, using attribute containers / scsi transport style for
   SCSI-FCP devices, warnings, etc...

6) FCoE attributes...

Other thoughts:

I would love to collaborate with someone who maintains a HBA that supports
native FC since I'm not aware of the specific needs of traditional HBAs.

I would hope that this new layout could be added to the kernel (when
ready) and the existing FC Transport deprecated as I'm not sure how to get
all HBA maintainers to switch their drivers at the same time. I think that
only adding FCoE attributes to this layout would be an incentive to get LLDs
to migrate.

I've tried to keep this covermail brief, so there are plenty of details
left out. Please ask if you have a question.

Thanks, //Rob

---

Robert Love (3):
      libfc, libfcoe, fcoe: Make use of FC subsystem
      scsi_transport_fcp: Create FC/SCSI interaction layer
      fc: Create FC sybsystem


 drivers/Kconfig                   |    2 
 drivers/Makefile                  |    1 
 drivers/fc/Kconfig                |    8 
 drivers/fc/Makefile               |    7 
 drivers/fc/fcfabric.c             |  425 ++++++++++
 drivers/fc/fcport.c               |  205 +++++
 drivers/fc/fcrport.c              | 1034 ++++++++++++++++++++++++
 drivers/fc/fcsysfs.c              |  180 ++++
 drivers/fc/fcsysfs.h              |   76 ++
 drivers/fc/fcvport.c              |  714 +++++++++++++++++
 drivers/scsi/Kconfig              |   11 
 drivers/scsi/Makefile             |    1 
 drivers/scsi/fcoe/fcoe.c          |  619 ++++++++------
 drivers/scsi/fcoe/libfcoe.c       |  167 +++-
 drivers/scsi/libfc/fc_disc.c      |    4 
 drivers/scsi/libfc/fc_exch.c      |    2 
 drivers/scsi/libfc/fc_fcp.c       |  130 +--
 drivers/scsi/libfc/fc_libfc.h     |   28 -
 drivers/scsi/libfc/fc_lport.c     |  195 ++---
 drivers/scsi/libfc/fc_npiv.c      |   44 -
 drivers/scsi/libfc/fc_rport.c     |   15 
 drivers/scsi/scsi_transport_fcp.c | 1598 +++++++++++++++++++++++++++++++++++++
 include/fc/fc.h                   |  843 ++++++++++++++++++++
 include/scsi/fc_encode.h          |   34 -
 include/scsi/libfc.h              |  119 +--
 include/scsi/libfcoe.h            |   17 
 include/scsi/scsi_transport_fcp.h |  325 ++++++++
 27 files changed, 6241 insertions(+), 563 deletions(-)
 create mode 100644 drivers/fc/Kconfig
 create mode 100644 drivers/fc/Makefile
 create mode 100644 drivers/fc/fcfabric.c
 create mode 100644 drivers/fc/fcport.c
 create mode 100644 drivers/fc/fcrport.c
 create mode 100644 drivers/fc/fcsysfs.c
 create mode 100644 drivers/fc/fcsysfs.h
 create mode 100644 drivers/fc/fcvport.c
 create mode 100644 drivers/scsi/scsi_transport_fcp.c
 create mode 100644 include/fc/fc.h
 create mode 100644 include/scsi/scsi_transport_fcp.h

-- 

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

* [RFC PATCH 1/3] fc: Create FC sybsystem
  2010-05-12 20:20 [RFC PATCH 0/3] FC subsystem update Robert Love
@ 2010-05-12 20:20 ` Robert Love
  2010-05-12 20:20 ` [RFC PATCH 2/3] scsi_transport_fcp: Create FC/SCSI interaction layer Robert Love
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 8+ messages in thread
From: Robert Love @ 2010-05-12 20:20 UTC (permalink / raw)
  To: linux-scsi; +Cc: james.smart, hare, christof.schmitt, james.bottomley

This patch creates a Fibre Channel subsystem. This new FC layer allows different
FC4 types to register with it; it also allows LLDs to register with it. When a
LLD supports one of the FC4s registered, the FC layer allows the FC4 to be
transported over the LLD.

This patch creates files under drivers/fc/ that expose APIs to both the FC4s
and LLDs. The API to the FC4s allow FC4s to register with FC so that they can
be used by LLDs. The API to the LLDs allows the LLDs to add fcports, fcfabrics,
fcvports and fcrports to sysfs and to make use of the registered FC4s.

The patch creates the following FC devices:

fcport: Represents physical port
	Attributes: maxframe_size
		    supported_speeds
		    speed (misplaced?)
		    supported_fc4s
		    active_fc4s (misplaced?)
		    supported_classes
		    serial_number

fcfabric: Represents the FC fabric and switch (FCF for FCoE)
	  Attributes: fabric_name
	  	      max_npiv_vports
		      npiv_vports_inuse
		      vport_create
		      vport_delete

fcvport: Represents either an N_Port or VN_Port
	 Attributes: port_id
	 	     node_name
		     port_name
		     port_type
		     vport_state
		     vport_last_state
		     roles
		     vport_type (duplicate)
		     symbolic_name
		     tgtid_bind_type
		     issue_lip

fcrport: Represents FC remote ports
	 Attributes: fast_io_fail_tmo
	 	     port_id
		     port_state
		     roles
		     node_name
		     port_name
		     supported_classes
		     maxframe_size
		     dev_loss_tmo

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

 drivers/Kconfig       |    2 
 drivers/Makefile      |    1 
 drivers/fc/Kconfig    |    8 
 drivers/fc/Makefile   |    7 
 drivers/fc/fcfabric.c |  425 ++++++++++++++++++++
 drivers/fc/fcport.c   |  205 ++++++++++
 drivers/fc/fcrport.c  | 1034 +++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/fc/fcsysfs.c  |  180 +++++++++
 drivers/fc/fcsysfs.h  |   76 ++++
 drivers/fc/fcvport.c  |  714 ++++++++++++++++++++++++++++++++++
 include/fc/fc.h       |  843 ++++++++++++++++++++++++++++++++++++++++
 11 files changed, 3495 insertions(+), 0 deletions(-)
 create mode 100644 drivers/fc/Kconfig
 create mode 100644 drivers/fc/Makefile
 create mode 100644 drivers/fc/fcfabric.c
 create mode 100644 drivers/fc/fcport.c
 create mode 100644 drivers/fc/fcrport.c
 create mode 100644 drivers/fc/fcsysfs.c
 create mode 100644 drivers/fc/fcsysfs.h
 create mode 100644 drivers/fc/fcvport.c
 create mode 100644 include/fc/fc.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index a2b902f..6eb6e22 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -28,6 +28,8 @@ source "drivers/md/Kconfig"
 
 source "drivers/message/fusion/Kconfig"
 
+source "drivers/fc/Kconfig"
+
 source "drivers/firewire/Kconfig"
 
 source "drivers/message/i2o/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 34f1e10..bf15da8 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_SPI)		+= spi/
 obj-y				+= net/
 obj-$(CONFIG_ATM)		+= atm/
 obj-$(CONFIG_FUSION)		+= message/
+obj-$(CONFIG_FC)		+= fc/
 obj-$(CONFIG_FIREWIRE)		+= firewire/
 obj-y				+= ieee1394/
 obj-$(CONFIG_UIO)		+= uio/
diff --git a/drivers/fc/Kconfig b/drivers/fc/Kconfig
new file mode 100644
index 0000000..bcd5444
--- /dev/null
+++ b/drivers/fc/Kconfig
@@ -0,0 +1,8 @@
+menu "Experimental Fibre Channel Support"
+
+config FC
+       tristate "Fibre Channel Support"
+       ---help---
+	 Experimental Fibre Channel Support
+
+endmenu
diff --git a/drivers/fc/Makefile b/drivers/fc/Makefile
new file mode 100644
index 0000000..fa1d60e
--- /dev/null
+++ b/drivers/fc/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_FC) += fc.o
+
+fc-objs := fcsysfs.o \
+	   fcport.o \
+	   fcfabric.o \
+	   fcvport.o \
+	   fcrport.o
diff --git a/drivers/fc/fcfabric.c b/drivers/fc/fcfabric.c
new file mode 100644
index 0000000..2588cf0
--- /dev/null
+++ b/drivers/fc/fcfabric.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright(c) 2010 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.
+ */
+
+#include "fcsysfs.h"
+
+int dev_is_nport(struct device *dev, void *ignored)
+{
+	return fc_fcvport_is_nport(dev_to_fcvport(dev));
+}
+
+#define fc_private_fcfabric_rd_attr(field, format_string, sz)		\
+	fc_always_show_function(fcfabric, field, format_string, sz, )	\
+	static FC_DEVICE_ATTR(fcfabric, field, S_IRUGO,			\
+			      show_fcfabric_##field, NULL)
+
+#define fcfabric_rd_attr_cast(field, format_string, sz, cast)		\
+	fc_conditional_show_function(fcfabric, field, format_string, sz, (cast)) \
+	static FC_DEVICE_ATTR(fcfabric, field, S_IRUGO,			\
+			      show_fcfabric_##field, NULL)
+
+fcfabric_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long);
+fc_private_fcfabric_rd_attr(max_npiv_vports, "%u\n", 20);
+fc_private_fcfabric_rd_attr(npiv_vports_inuse, "%u\n", 20);
+
+static atomic_t fcfabric_num;
+
+static int fc_parse_wwn(const char *ns, u64 *nm)
+{
+	unsigned int i, j;
+	u8 wwn[8];
+
+	memset(wwn, 0, sizeof(wwn));
+
+	/* Validate and store the new name */
+	for (i=0, j=0; i < 16; i++) {
+		if ((*ns >= 'a') && (*ns <= 'f'))
+			j = ((j << 4) | ((*ns++ -'a') + 10));
+		else if ((*ns >= 'A') && (*ns <= 'F'))
+			j = ((j << 4) | ((*ns++ -'A') + 10));
+		else if ((*ns >= '0') && (*ns <= '9'))
+			j = ((j << 4) | (*ns++ -'0'));
+		else
+			return -EINVAL;
+		if (i % 2) {
+			wwn[i/2] = j & 0xff;
+			j = 0;
+		}
+	}
+
+	*nm = wwn_to_u64(wwn);
+
+	return 0;
+}
+
+/**
+ * fc_vport_setup - allocates and creates a FC virtual port.
+ * @shost:	scsi host the virtual port is connected to.
+ * @channel:	Channel on shost port connected to.
+ * @pdev:	parent device for vport
+ * @ids:	The world wide names, FC4 port roles, etc for
+ *              the virtual port.
+ * @ret_vport:	The pointer to the created vport.
+ *
+ * Allocates and creates the vport structure, calls the parent host
+ * to instantiate the vport, the completes w/ class and sysfs creation.
+ *
+ * Notes:
+ *	This routine assumes no locks are held on entry.
+ */
+static int fc_vport_setup(struct fc_fcfabric *fcfabric, int channel,
+			  struct fc_vport_identifiers  *ids, struct fc_fcvport **ret_vport)
+{
+	struct fc_fcport *fcport = fcfabric_to_fcport(fcfabric);
+	struct fc_fcvport *fcvport;
+	unsigned long flags;
+
+	*ret_vport = NULL;
+
+	if (!fcfabric->f->vport_create)
+		return -ENOENT;
+
+	fcvport = fc_fcvport_alloc(fcfabric, ids, fcfabric->fcvport_f,
+				   fcfabric->f->dd_fcvport_size, fcport);
+	if (unlikely(!fcvport)) {
+		printk(KERN_ERR "%s: allocation failure\n", __func__);
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&fcfabric->lock, flags);
+
+	if (fcfabric->npiv_vports_inuse >= fcfabric->max_npiv_vports) {
+		spin_unlock_irqrestore(&fcfabric->lock, flags);
+		kfree(fcvport);
+		return -ENOSPC;
+	}
+
+	fcfabric->npiv_vports_inuse++;
+	fcvport->number = fcfabric->next_vport_number++;
+	list_add_tail(&fcvport->peers, &fcfabric->vports);
+
+	spin_unlock_irqrestore(&fcfabric->lock, flags);
+
+	return 0;
+}
+
+/**
+ * fc_vport_create - Admin App or LLDD requests creation of a vport
+ * @shost:	scsi host the virtual port is connected to.
+ * @channel:	channel on shost port connected to.
+ * @ids:	The world wide names, FC4 port roles, etc for
+ *              the virtual port.
+ *
+ * Notes:
+ *	This routine assumes no locks are held on entry.
+ */
+struct fc_fcvport *fc_vport_create(struct fc_fcfabric *fcfabric,
+				   int channel, struct fc_vport_identifiers *ids)
+{
+	int stat;
+	struct fc_fcvport *vport;
+
+	stat = fc_vport_setup(fcfabric, channel, ids, &vport);
+
+	return stat ? NULL : vport;
+}
+
+/**
+ * fc_vport_terminate - Admin App or LLDD requests termination of a vport
+ * @vport:	fc_vport to be terminated
+ *
+ * Calls the LLDD vport_delete() function, then deallocates and removes
+ * the vport from the shost and object tree.
+ *
+ * Notes:
+ *	This routine assumes no locks are held on entry.
+ */
+int fc_vport_terminate(struct fc_fcfabric *fcfabric, struct fc_fcvport *vport)
+{
+	struct fc_fcvport *fcnport;
+	unsigned long flags;
+	int stat = 0;
+
+	spin_lock_irqsave(&vport->lock, flags);
+	if (vport->flags & FC_VPORT_CREATING) {
+		spin_unlock_irqrestore(&vport->lock, flags);
+		return -EBUSY;
+	}
+	if (vport->flags & (FC_VPORT_DEL)) {
+		spin_unlock_irqrestore(&vport->lock, flags);
+		return -EALREADY;
+	}
+	vport->flags |= FC_VPORT_DELETING;
+	spin_unlock_irqrestore(&vport->lock, flags);
+
+	/*
+	 * The fabric needs to be aware of whether the port being
+	 * removed is a VN_Port or an N_Port. The LLD is only
+	 * notified if a VN_Port is being deleted.
+	 */
+	if (fcfabric->f->vport_delete) {
+		fcnport = fc_fcfabric_find_nport(fcfabric);
+		if (fcnport)
+			stat = fcfabric->f->vport_delete(fcvport_priv(fcnport), vport);
+	} else
+		stat = -ENOENT;
+
+	/*
+	 * TODO: This is incorrect.. Which lock is protecting the
+	 * vport->flags? Above it is the vport->lock (which makes
+	 * sense) but here it's the fcfabric->lock!
+	 */
+
+	spin_lock_irqsave(&fcfabric->lock, flags);
+	vport->flags &= ~FC_VPORT_DELETING;
+	if (!stat) {
+		vport->flags |= FC_VPORT_DELETED;
+		list_del(&vport->peers);
+		fcfabric->npiv_vports_inuse--;
+	}
+	spin_unlock_irqrestore(&fcfabric->lock, flags);
+
+	if (stat)
+		return stat;
+
+	/*
+	 * TODO: What is this stuff? I probably need to do something here.
+
+	if (dev->parent != &shost->shost_gendev)
+		sysfs_remove_link(&shost->shost_gendev.kobj, dev_name(dev));
+	*/
+
+	return 0; /* SUCCESS */
+}
+
+/*
+ * "Short-cut" sysfs variable to create a new vport on a FC Host.
+ * Input is a string of the form "<WWPN>:<WWNN>". Other attributes
+ * will default to a NPIV-based FCP_Initiator; The WWNs are specified
+ * as hex characters, and may *not* contain any prefixes (e.g. 0x, x, etc)
+ */
+static ssize_t
+store_fcfabric_vport_create(struct device *dev, struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	struct fc_fcfabric *fcfabric = dev_to_fcfabric(dev);
+	struct fc_vport_identifiers vid;
+	struct fc_fcvport *fcvport;
+	unsigned int cnt = count;
+	int stat;
+
+	memset(&vid, 0, sizeof(vid));
+
+	/* count may include a LF at end of string */
+	if (buf[cnt-1] == '\n')
+		cnt--;
+
+	/* validate we have enough characters for WWPN */
+	if ((cnt != (16+1+16)) || (buf[16] != ':'))
+		return -EINVAL;
+
+	stat = fc_parse_wwn(&buf[0], &vid.port_name);
+	if (stat)
+		return stat;
+
+	stat = fc_parse_wwn(&buf[17], &vid.node_name);
+	if (stat)
+		return stat;
+
+	vid.roles = FC_PORT_ROLE_FCP_INITIATOR;
+	vid.vport_type = FC_PORTTYPE_NPIV;
+	/* vid.symbolic_name is already zero/NULL's */
+	vid.disable = false;		/* always enabled */
+
+	/* we only allow support on Channel 0 !!! */
+	stat = fc_vport_setup(fcfabric, 0, &vid, &fcvport);
+
+	return stat ? stat : count;
+}
+static FC_DEVICE_ATTR(fcfabric, vport_create, S_IWUSR, NULL,
+		      store_fcfabric_vport_create);
+
+
+/*
+ * "Short-cut" sysfs variable to delete a vport on a FC Host.
+ * Vport is identified by a string containing "<WWPN>:<WWNN>".
+ * The WWNs are specified as hex characters, and may *not* contain
+ * any prefixes (e.g. 0x, x, etc)
+ */
+static ssize_t
+store_fcfabric_vport_delete(struct device *dev, struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	struct fc_fcfabric *fcfabric = dev_to_fcfabric(dev);
+	struct fc_fcvport *vport;
+	u64 wwpn, wwnn;
+	unsigned long flags;
+	unsigned int cnt=count;
+	int stat, match;
+
+	/* count may include a LF at end of string */
+	if (buf[cnt-1] == '\n')
+		cnt--;
+
+	/* validate we have enough characters for WWPN */
+	if ((cnt != (16+1+16)) || (buf[16] != ':'))
+		return -EINVAL;
+
+	stat = fc_parse_wwn(&buf[0], &wwpn);
+	if (stat)
+		return stat;
+
+	stat = fc_parse_wwn(&buf[17], &wwnn);
+	if (stat)
+		return stat;
+
+	spin_lock_irqsave(&fcfabric->lock, flags);
+	match = 0;
+	/* we only allow support on Channel 0 !!! */
+	list_for_each_entry(vport, &fcfabric->vports, peers) {
+		if ((vport->channel == 0) &&
+		    (vport->port_name == wwpn) && (vport->node_name == wwnn)) {
+			match = 1;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&fcfabric->lock, flags);
+
+	if (!match)
+		return -ENODEV;
+
+	stat = fc_vport_terminate(fcfabric, vport);
+	return stat ? stat : count;
+}
+static FC_DEVICE_ATTR(fcfabric, vport_delete, S_IWUSR, NULL,
+		      store_fcfabric_vport_delete);
+
+static void fc_fcfabric_release(struct device *dev)
+{
+	struct fc_fcfabric *fcfabric = dev_to_fcfabric(dev);
+
+	/*
+	 * TODO: Remove this memset. It's helpful when checking
+	 * reference counting becuase it forces NULL pointer
+	 * exceptions for things that are incorrectly using the
+	 * object that's about to be free'd.
+	 */
+	memset(fcfabric, 0, sizeof(*fcfabric));
+	kfree(fcfabric);
+}
+
+void fc_fcfabric_del(struct fc_fcfabric *fcfabric)
+{
+	device_del(&fcfabric->dev);
+	put_device(fcfabric->dev.parent);
+	put_device(&fcfabric->dev); /* self-reference */
+}
+EXPORT_SYMBOL(fc_fcfabric_del);
+
+struct class fcfabric_class = {
+	.name = "fcfabric",
+	.dev_release = fc_fcfabric_release,
+};
+EXPORT_SYMBOL(fcfabric_class);
+
+struct fc_fcfabric *fc_fcfabric_add(struct fc_fcport *fcport,
+				    struct fcfabric_function_template *fcn_tmpl,
+				    const u64 name)
+{
+	struct fc_fcfabric *fcfabric;
+	int error = 0;
+	int count = 0, max = 0;
+
+	fcfabric = kzalloc(sizeof(struct fc_fcfabric), GFP_ATOMIC);
+
+	device_initialize(&fcfabric->dev);
+	get_device(&fcfabric->dev); /* For vports list */
+	fcfabric->dev.parent = get_device(&fcport->dev);
+	fcfabric->dev.class = &fcfabric_class;
+	fcfabric->f = fcn_tmpl;
+	fcfabric->id = atomic_inc_return(&fcfabric_num) - 1;
+
+	dev_set_name(&fcfabric->dev, "fcfabric_%d",
+		     fcfabric->id);
+
+	spin_lock_init(&fcfabric->lock);
+	INIT_LIST_HEAD(&fcfabric->vports);
+
+	fcfabric->fabric_name = name;
+	fcfabric->max_npiv_vports = 0;
+	fcfabric->next_vport_number = 0;
+	fcfabric->npiv_vports_inuse = 0;
+
+	error = device_add(&fcfabric->dev);
+	if (error)
+		goto out_del_dev;
+
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(fcfabric, fabric_name);
+
+	BUG_ON(count > FCFABRIC_NUM_ATTRS);
+	max = count;
+	FC_CREATE_ATTRIBUTES(fcfabric, count, 0);
+
+	fcfabric->attr_count = max;
+
+	if (error || count != 0)
+		goto out_del_dev;
+
+	return fcfabric;
+
+out_del_dev:
+	device_del(&fcfabric->dev);
+	kfree(fcfabric);
+	return NULL;
+}
+EXPORT_SYMBOL(fc_fcfabric_add);
+
+struct fc_fcvport *fc_fcfabric_find_nport(struct fc_fcfabric *fcfabric)
+{
+	struct device *dev = device_find_child(&fcfabric->dev, NULL, dev_is_nport);
+	if (!dev)
+		return NULL;
+	return dev_to_fcvport(dev);
+}
+EXPORT_SYMBOL(fc_fcfabric_find_nport);
+
+/*
+ * TODO: The way the vport_* attributes are aded to the fcfabric
+ * is a bit odd. However, we don't want the vport_create interface
+ * created before the N_Port is logged in to the fabric.
+ */
+void fc_fcfabric_add_npiv(struct fc_fcfabric *fcfabric)
+{
+	int count = fcfabric->attr_count;
+	int error;
+	int max;
+
+	if (fcfabric->f->vport_create) {
+		FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(fcfabric, max_npiv_vports);
+		FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(fcfabric, npiv_vports_inuse);
+		FC_SETUP_ALWAYS_ATTRIBUTE_RW(fcfabric, vport_create);
+	}
+
+	if (fcfabric->f->vport_delete)
+		FC_SETUP_ALWAYS_ATTRIBUTE_RW(fcfabric, vport_delete);
+
+	BUG_ON(count > FCFABRIC_NUM_ATTRS);
+	max = count;
+	FC_CREATE_ATTRIBUTES(fcfabric, count, fcfabric->attr_count);
+
+	fcfabric->attr_count = max;
+}
diff --git a/drivers/fc/fcport.c b/drivers/fc/fcport.c
new file mode 100644
index 0000000..7389ca8
--- /dev/null
+++ b/drivers/fc/fcport.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright(c) 2010 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.
+ */
+
+#include "fcsysfs.h"
+
+static atomic_t fcport_num;
+
+/* Convert FC_PORTSPEED bit values to ascii string name */
+static const struct {
+	u32 			value;
+	char			*name;
+} fc_port_speed_names[] = {
+	{ FC_PORTSPEED_1GBIT,		"1 Gbit" },
+	{ FC_PORTSPEED_2GBIT,		"2 Gbit" },
+	{ FC_PORTSPEED_4GBIT,		"4 Gbit" },
+	{ FC_PORTSPEED_10GBIT,		"10 Gbit" },
+	{ FC_PORTSPEED_8GBIT,		"8 Gbit" },
+	{ FC_PORTSPEED_16GBIT,		"16 Gbit" },
+	{ FC_PORTSPEED_NOT_NEGOTIATED,	"Not Negotiated" },
+};
+fc_bitfield_name_search(port_speed, fc_port_speed_names)
+
+#define fc_fcport_rd_attr(field, format_string, sz)			\
+	fc_always_show_function(fcport, field, format_string, sz, )	\
+	static FC_DEVICE_ATTR(fcport, field, S_IRUGO,			\
+			      show_fcport_##field, NULL)
+
+static int show_fc_fc4s(char *buf, u8 *fc4_list)
+{
+	int i, len=0;
+
+	for (i = 0; i < FC_FC4_LIST_SIZE; i++, fc4_list++)
+		len += sprintf(buf + len , "0x%02x ", *fc4_list);
+	len += sprintf(buf + len, "\n");
+	return len;
+}
+
+static ssize_t show_fcport_supported_fc4s(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct fc_fcport *fcport = dev_to_fcport(dev);
+	return (ssize_t)show_fc_fc4s(buf, fcport_supported_fc4s(fcport));
+}
+static FC_DEVICE_ATTR(fcport, supported_fc4s, S_IRUGO,
+		      show_fcport_supported_fc4s, NULL);
+
+static ssize_t show_fcport_active_fc4s (struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct fc_fcport *fcport = dev_to_fcport(dev);
+	if (fcport->f->get_fcport_active_fc4s)
+		fcport->f->get_fcport_active_fc4s(fcport);
+
+	return (ssize_t)show_fc_fc4s(buf, fcport_active_fc4s(fcport));
+}
+static FC_DEVICE_ATTR(fcport, active_fc4s, S_IRUGO,
+		      show_fcport_active_fc4s, NULL);
+
+static ssize_t show_fcport_speed(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct fc_fcport *fcport = dev_to_fcport(dev);
+
+	if (fcport->f->get_fcport_speed)
+		fcport->f->get_fcport_speed(fcport);
+
+	if (fcport_speed(fcport) == FC_PORTSPEED_UNKNOWN)
+		return snprintf(buf, 20, "unknown\n");
+
+	return get_fc_port_speed_names(fcport_speed(fcport), buf);
+}
+static FC_DEVICE_ATTR(fcport, speed, S_IRUGO, show_fcport_speed, NULL);
+
+fc_fcport_rd_attr(maxframe_size, "%u bytes\n", 20);
+fc_fcport_rd_attr(serial_number, "%s\n", (FC_SERIAL_NUMBER_SIZE +1));
+
+static ssize_t show_fcport_supported_speeds(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	struct fc_fcport *fcport = dev_to_fcport(dev);
+	if (fcport_supported_speeds(fcport) == FC_PORTSPEED_UNKNOWN)
+		return snprintf(buf, 20, "unknown\n");
+
+	return get_fc_port_speed_names(fcport_supported_speeds(fcport), buf);
+}
+static FC_DEVICE_ATTR(fcport, supported_speeds, S_IRUGO,
+		      show_fcport_supported_speeds, NULL);
+
+static ssize_t show_fcport_supported_classes(struct device *dev,
+					     struct device_attribute *attr,
+					     char *buf)
+{
+	struct fc_fcport *fcport = dev_to_fcport(dev);
+
+	if (fcport_supported_classes(fcport) == FC_COS_UNSPECIFIED)
+		return snprintf(buf, 20, "unspecified\n");
+
+	return get_fc_cos_names(fcport_supported_classes(fcport), buf);
+}
+static FC_DEVICE_ATTR(fcport, supported_classes, S_IRUGO,
+		      show_fcport_supported_classes, NULL);
+
+
+static void fc_fcport_release(struct device *dev)
+{
+	struct fc_fcport *fcport = dev_to_fcport(dev);
+	/*
+	 * TODO: Remove this memset. It's helpful when checking
+	 * reference counting becuase it forces NULL pointer
+	 * exceptions for things that are incorrectly using the
+	 * object that's about to be free'd.
+	 */
+	memset(fcport, 0, sizeof(*fcport));
+	kfree(fcport);
+}
+
+struct class fcport_class = {
+	.name = "fcport",
+	.dev_release = fc_fcport_release,
+};
+EXPORT_SYMBOL(fcport_class);
+
+void fc_fcport_del(struct fc_fcport *fcport)
+{
+	device_del(&fcport->dev);
+	put_device(fcport->dev.parent);
+	put_device(&fcport->dev); /* self-reference */
+}
+EXPORT_SYMBOL(fc_fcport_del);
+
+struct fc_fcport *fc_fcport_add(struct device *pdev,
+				struct fcport_function_template *fcn_tmpl,
+				void *fc4_f)
+{
+	struct fc_fcport *fcport;
+	int count = 0;
+	int error = 0;
+
+	fcport = kzalloc(sizeof(struct fc_fcport), GFP_KERNEL);
+	if (!fcport)
+		goto out;
+	fcport->id = atomic_inc_return(&fcport_num) - 1;
+	device_initialize(&fcport->dev);
+	fcport->dev.parent = get_device(pdev);
+	fcport->dev.class = &fcport_class;
+	fcport->f = fcn_tmpl;
+	fcport->fc4_f = fc4_f;
+	dev_set_name(&fcport->dev, "fcport_%d", fcport->id);
+
+	error = device_add(&fcport->dev);
+	if (error)
+		goto out_del_dev;
+
+	fcport->maxframe_size = -1;
+	fcport->supported_classes = FC_COS_UNSPECIFIED;
+	fcport->supported_speeds = FC_PORTSPEED_UNKNOWN;
+	fcport->speed = FC_PORTSPEED_UNKNOWN;
+	memset(fcport->supported_fc4s, 0,
+	       sizeof(fcport->supported_fc4s));
+	memset(fcport->active_fc4s, 0,
+	       sizeof(fcport->active_fc4s));
+	memset(fcport->serial_number, 0,
+	       sizeof(fcport->serial_number));
+
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(fcport, maxframe_size);
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(fcport, supported_speeds);
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(fcport, speed);
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(fcport, supported_fc4s);
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(fcport, active_fc4s);
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(fcport, supported_classes);
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(fcport, serial_number);
+
+	BUG_ON(count > FCPORT_NUM_ATTRS);
+	FC_CREATE_ATTRIBUTES(fcport, count, 0);
+
+	if (error || count != 0)
+		goto out_del_dev;
+
+	return fcport;
+
+out_del_dev:
+	device_del(&fcport->dev);
+	kfree(fcport);
+out:
+	return NULL;
+}
+EXPORT_SYMBOL(fc_fcport_add);
diff --git a/drivers/fc/fcrport.c b/drivers/fc/fcrport.c
new file mode 100644
index 0000000..37b7a1d
--- /dev/null
+++ b/drivers/fc/fcrport.c
@@ -0,0 +1,1034 @@
+/*
+ * Copyright(c) 2010 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+
+#include "fcsysfs.h"
+
+/* TODO: This is dup from scsi_priv.h */
+#define FC_DEVICE_BLOCK_MAX_TIMEOUT   600     /* units in seconds */
+
+static void fc_timeout_deleted_rport(struct work_struct *work);
+static void fc_timeout_fail_rport_io(struct work_struct *work);
+
+static void fc_rport_dev_release(struct device *dev)
+{
+	struct fc_rport *rport = dev_to_rport(dev);
+	/*
+	 * TODO: Remove this memset. It's helpful when checking
+	 * reference counting becuase it forces NULL pointer
+	 * exceptions for things that are incorrectly using the
+	 * object that's about to be free'd.
+	 */
+	memset(rport, 0, sizeof(*rport));
+	kfree(rport);
+}
+
+struct class fcrport_class = {
+	.name = "fcrport",
+	.dev_release = fc_rport_dev_release,
+};
+EXPORT_SYMBOL(fcrport_class);
+
+/*
+ * FC Remote Port Attribute Management
+ */
+
+#define fc_rport_show_function(field, format_string, sz, cast)		\
+static ssize_t								\
+show_fc_rport_##field (struct device *dev, 				\
+		       struct device_attribute *attr, char *buf)	\
+{									\
+	struct fc_rport *rport = dev_to_rport(dev);			\
+	if ((rport->f->get_rport_##field) &&				\
+	    !((rport->port_state == FC_PORTSTATE_BLOCKED) ||		\
+	      (rport->port_state == FC_PORTSTATE_DELETED) ||		\
+	      (rport->port_state == FC_PORTSTATE_NOTPRESENT)))		\
+		rport->f->get_rport_##field(rport);			\
+	return snprintf(buf, sz, format_string, cast rport->field); 	\
+}
+
+
+
+#define fc_rport_store_function(field)					\
+static ssize_t								\
+store_fc_rport_##field(struct device *dev,				\
+		       struct device_attribute *attr,			\
+		       const char *buf,	size_t count)			\
+{									\
+	int val;							\
+	struct fc_rport *rport = dev_to_rport(dev);			\
+	char *cp;							\
+	if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||		\
+	    (rport->port_state == FC_PORTSTATE_DELETED) ||		\
+	    (rport->port_state == FC_PORTSTATE_NOTPRESENT))		\
+		return -EBUSY;						\
+	val = simple_strtoul(buf, &cp, 0);				\
+	if (*cp && (*cp != '\n'))					\
+		return -EINVAL;						\
+	rport->f->set_rport_##field(rport, val);			\
+	return count;							\
+}
+
+static ssize_t
+show_fc_rport_supported_classes(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct fc_rport *rport = dev_to_rport(dev);
+	if (rport->supported_classes == FC_COS_UNSPECIFIED)
+		return snprintf(buf, 20, "unspecified\n");
+	return get_fc_cos_names(rport->supported_classes, buf);
+}
+static FC_DEVICE_ATTR(rport, supported_classes, S_IRUGO,
+		      show_fc_rport_supported_classes, NULL);
+
+
+fc_rport_show_function(dev_loss_tmo, "%d\n", 20, )
+static ssize_t
+store_fc_rport_dev_loss_tmo(struct device *dev, struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	int val;
+	struct fc_rport *rport = dev_to_rport(dev);
+	char *cp;
+	if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
+	    (rport->port_state == FC_PORTSTATE_DELETED) ||
+	    (rport->port_state == FC_PORTSTATE_NOTPRESENT))
+		return -EBUSY;
+	val = simple_strtoul(buf, &cp, 0);
+	if ((*cp && (*cp != '\n')) || (val < 0))
+		return -EINVAL;
+
+	/*
+	 * If fast_io_fail is off we have to cap
+	 * dev_loss_tmo at FC_DEVICE_BLOCK_MAX_TIMEOUT
+	 */
+	if (rport->fast_io_fail_tmo == -1 &&
+	    val > FC_DEVICE_BLOCK_MAX_TIMEOUT)
+		return -EINVAL;
+
+	rport->f->set_rport_dev_loss_tmo(rport, val);
+	return count;
+}
+static FC_DEVICE_ATTR(rport, dev_loss_tmo, S_IRUGO | S_IWUSR,
+		      show_fc_rport_dev_loss_tmo, store_fc_rport_dev_loss_tmo);
+
+static ssize_t
+show_fc_rport_roles(struct device *dev, struct device_attribute *attr,
+		    char *buf)
+{
+	struct fc_rport *rport = dev_to_rport(dev);
+
+	/* identify any roles that are port_id specific */
+	if ((rport->port_id != -1) &&
+	    (rport->port_id & FC_WELLKNOWN_PORTID_MASK) ==
+					FC_WELLKNOWN_PORTID_MASK) {
+		switch (rport->port_id & FC_WELLKNOWN_ROLE_MASK) {
+		case FC_FPORT_PORTID:
+			return snprintf(buf, 30, "Fabric Port\n");
+		case FC_FABCTLR_PORTID:
+			return snprintf(buf, 30, "Fabric Controller\n");
+		case FC_DIRSRVR_PORTID:
+			return snprintf(buf, 30, "Directory Server\n");
+		case FC_TIMESRVR_PORTID:
+			return snprintf(buf, 30, "Time Server\n");
+		case FC_MGMTSRVR_PORTID:
+			return snprintf(buf, 30, "Management Server\n");
+		default:
+			return snprintf(buf, 30, "Unknown Fabric Entity\n");
+		}
+	} else {
+		if (rport->roles == FC_PORT_ROLE_UNKNOWN)
+			return snprintf(buf, 20, "unknown\n");
+		return get_fc_port_roles_names(rport->roles, buf);
+	}
+}
+static FC_DEVICE_ATTR(rport, roles, S_IRUGO,
+		      show_fc_rport_roles, NULL);
+
+
+#define fc_rport_rd_enum_attr(title, maxlen)                    \
+static ssize_t                                                          \
+show_rport_##title(struct device *dev,			\
+		   struct device_attribute *attr, char *buf)		\
+{                                                                       \
+	struct fc_rport *rport = dev_to_rport(dev);			\
+	const char *name;                                               \
+	name = get_fc_##title##_name(rport_##title(rport));		\
+	if (!name)                                                      \
+		return -EINVAL;                                         \
+	return snprintf(buf, maxlen, "%s\n", name);                     \
+}                                                                       \
+static FC_DEVICE_ATTR(rport, title, S_IRUGO,				\
+		      show_rport_##title, NULL)
+
+fc_rport_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN);
+
+/*
+#define rport_store_function(field)					\
+static ssize_t								\
+store_rport_##field(struct device *dev, 				\
+		    struct device_attribute *attr,			\
+		    const char *buf, size_t count)			\
+{									\
+	struct fc_rport *rport = dev_to_rport(dev);		\
+	int val;							\
+	char *cp;							\
+									\
+	val = simple_strtoul(buf, &cp, 0);				\
+	if (*cp && (*cp != '\n'))					\
+		return -EINVAL;						\
+	rport->f->set_rport_##field(rport, val);			\
+	return count;							\
+}
+*/
+
+#define fc_rport_rw_attr(field, format_string, sz)			\
+	fc_always_show_function(rport, field, format_string, sz, )	\
+		rport_store_function(field)				\
+		static FC_DEVICE_ATTR(rport, field, S_IRUGO,		\
+				      show_rport_##field,		\
+				      store_rport_##field)
+
+#define fc_rport_rd_attr(field, format_string, sz)			\
+	fc_always_show_function(rport, field, format_string, sz, )	\
+		static FC_DEVICE_ATTR(rport, field, S_IRUGO,		\
+				      show_rport_##field, NULL)
+
+#define fc_rport_rd_attr_cast(field, format_string, sz, cast)		\
+	fc_always_show_function(rport, field, format_string, sz, (cast)) \
+	static FC_DEVICE_ATTR(rport, field, S_IRUGO,			\
+			      show_rport_##field, NULL)
+
+/* Fixed Remote Port Attributes */
+fc_rport_rd_attr(maxframe_size, "%u bytes\n", 20);
+fc_rport_rd_attr(port_id, "0x%06x\n", 20);
+fc_rport_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
+fc_rport_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
+
+
+/*
+ * fast_io_fail_tmo attribute
+*/
+
+static ssize_t
+show_fc_rport_fast_io_fail_tmo(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct fc_rport *rport = dev_to_rport(dev);
+
+	if (rport->fast_io_fail_tmo == -1)
+		return snprintf(buf, 5, "off\n");
+	return snprintf(buf, 20, "%d\n", rport->fast_io_fail_tmo);
+}
+
+
+static ssize_t
+store_fc_rport_fast_io_fail_tmo(struct device *dev,
+				struct device_attribute *attr, const char *buf,
+				size_t count)
+{
+	int val;
+	char *cp;
+	struct fc_rport *rport = dev_to_rport(dev);
+
+	if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
+	    (rport->port_state == FC_PORTSTATE_DELETED) ||
+	    (rport->port_state == FC_PORTSTATE_NOTPRESENT))
+		return -EBUSY;
+	if (strncmp(buf, "off", 3) == 0)
+		rport->fast_io_fail_tmo = -1;
+	else {
+		val = simple_strtoul(buf, &cp, 0);
+		if ((*cp && (*cp != '\n')) || (val < 0))
+			return -EINVAL;
+		/*
+		 * Cap fast_io_fail by dev_loss_tmo or
+		 * FC_DEVICE_BLOCK_MAX_TIMEOUT.
+		 */
+		if ((val >= rport->dev_loss_tmo) ||
+		    (val > FC_DEVICE_BLOCK_MAX_TIMEOUT))
+			return -EINVAL;
+
+		rport->fast_io_fail_tmo = val;
+	}
+	return count;
+}
+static FC_DEVICE_ATTR(rport, fast_io_fail_tmo, S_IRUGO | S_IWUSR,
+		      show_fc_rport_fast_io_fail_tmo,
+		      store_fc_rport_fast_io_fail_tmo);
+
+
+/**
+ * fc_rport_final_delete - finish rport termination and delete it.
+ * @work:	remote port to be deleted.
+ */
+static void fc_rport_final_delete(struct work_struct *work)
+{
+	struct fc_rport *rport =
+		container_of(work, struct fc_rport, rport_delete_work);
+	struct device *dev = &rport->dev;
+	struct fc_fcvport *fcvport = rport_to_fcvport(rport);
+	unsigned long flags;
+	int do_callback = 0;
+
+	/*
+	 * if a scan is pending, flush the SCSI Host work_q so that
+	 * that we can reclaim the rport scan work element.
+	 */
+
+	/*
+	 * TODO: What work is being flushed?
+	 */
+	if (rport->flags & FC_RPORT_SCAN_PENDING)
+		fc4->fc4_init_scsi_flush_work(fcvport);
+
+	fc4->fc4_targ_terminate_io(rport);
+
+	/*
+	 * Cancel any outstanding timers. These should really exist
+	 * only when rmmod'ing the LLDD and we're asking for
+	 * immediate termination of the rports
+	 */
+	spin_lock_irqsave(&fcvport->lock, flags);
+	if (rport->flags & FC_RPORT_DEVLOSS_PENDING) {
+		spin_unlock_irqrestore(&fcvport->lock, flags);
+		if (!cancel_delayed_work(&rport->fail_io_work))
+			fc_flush_devloss(fcvport);
+		if (!cancel_delayed_work(&rport->dev_loss_work))
+			fc_flush_devloss(fcvport);
+		spin_lock_irqsave(&fcvport->lock, flags);
+		rport->flags &= ~FC_RPORT_DEVLOSS_PENDING;
+	}
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+
+	/*
+	 * Notify the driver that the rport is now dead. The LLDD will
+	 * also guarantee that any communication to the rport is terminated
+	 *
+	 * Avoid this call if we already called it when we preserved the
+	 * rport for the binding.
+	 */
+	spin_lock_irqsave(&fcvport->lock, flags);
+	if (!(rport->flags & FC_RPORT_DEVLOSS_CALLBK_DONE) &&
+	    (rport->f->dev_loss_tmo_callbk)) {
+		rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
+		do_callback = 1;
+	}
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+
+	if (do_callback)
+		rport->f->dev_loss_tmo_callbk(rport);
+
+	/*
+	 * TODO: In what order should the fcptarg be deleted?
+	 * What do the LLDs need to do to cleanup?
+	 */
+	fc4->fc4_targ_del(rport);
+
+	device_del(dev);
+	put_device(dev->parent);
+	put_device(dev);	/* for self-reference */
+}
+
+/**
+ * fc_rport_create - allocates and creates a remote FC port.
+ * @channel:	Channel on the port is connected to.
+ * @ids:	The world wide names, fc address, and FC4 port
+ *		roles for the remote port.
+ *
+ * Allocates and creates the remoter port structure, including the
+ * class and sysfs creation.
+ *
+ * Notes:
+ *	This routine assumes no locks are held on entry.
+ */
+static struct fc_rport *
+fc_rport_create(struct fc_fcvport *fcvport,
+		struct fcrport_function_template *f,
+		int channel, struct fc_rport_identifiers *ids)
+{
+	struct fc_rport *rport;
+	struct device *dev;
+	unsigned long flags;
+	int error = 0;
+	int count = 0;
+	size_t size;
+
+	size = (sizeof(struct fc_rport) + f->dd_fcrport_size);
+	rport = kzalloc(size, GFP_KERNEL);
+	if (unlikely(!rport)) {
+		printk(KERN_ERR "%s: allocation failure\n", __func__);
+		return NULL;
+	}
+
+	rport->f = f;
+
+	rport->maxframe_size = -1;
+	rport->supported_classes = FC_COS_UNSPECIFIED;
+	rport->dev_loss_tmo = fc_dev_loss_tmo;
+	memcpy(&rport->node_name, &ids->node_name, sizeof(rport->node_name));
+	memcpy(&rport->port_name, &ids->port_name, sizeof(rport->port_name));
+	rport->port_id = ids->port_id;
+	rport->roles = ids->roles;
+	rport->port_state = FC_PORTSTATE_ONLINE;
+	if (f->dd_fcrport_size)
+		rport->dd_data = &rport[1];
+	rport->channel = channel;
+	rport->fast_io_fail_tmo = -1;
+
+	INIT_DELAYED_WORK(&rport->dev_loss_work, fc_timeout_deleted_rport);
+	INIT_DELAYED_WORK(&rport->fail_io_work, fc_timeout_fail_rport_io);
+	INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete);
+
+	spin_lock_irqsave(&fcvport->lock, flags);
+	rport->number = fcvport->next_rport_number++;
+
+	dev = &rport->dev;
+	device_initialize(dev); /* takes self reference */
+	dev->parent = get_device(&fcvport->dev); /* parent reference */
+	dev->class = &fcrport_class;
+	dev_set_name(dev, "rport-%d:%d-%d",
+		     fcvport->id, channel, rport->number);
+	error = device_add(dev);
+	if (error) {
+		printk(KERN_ERR "FC Remote Port device_add failed\n");
+		goto delete_rport;
+	}
+
+	/*
+	 * TODO: Should return something and check it here.
+	 */
+	/*
+	 * Called withing fcvport lock so that the FC4 layer can
+	 * check the rport role without worrying about it changing.
+	 */
+	fc4->fc4_targ_add(rport, fcvport, channel);
+
+	list_add_tail(&rport->peers, &fcvport->rports);
+
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+
+	/*
+	 * Setup attributes
+	 */
+	/* Should this be _NS? Does that mean No Store */
+	FC_SETUP_ALWAYS_ATTRIBUTE_RW(rport, fast_io_fail_tmo);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(rport, port_id);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(rport, port_state);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(rport, roles);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(rport, node_name);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(rport, port_name);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(rport, supported_classes);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(rport, maxframe_size);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RW(rport, dev_loss_tmo);
+
+	BUG_ON(count > FCRPORT_NUM_ATTRS);
+	FC_CREATE_ATTRIBUTES(rport, count, 0);
+	if (error || count != 0) {
+		/*
+		 * TODO: Clean up if there is an error
+		 */
+		return NULL;
+	}
+
+	if (rport->roles & FC_PORT_ROLE_FCP_TARGET) {
+		/* initiate a scan of the target */
+		rport->flags |= FC_RPORT_SCAN_PENDING;
+		fc4->fc4_targ_queue_scan(rport);
+	}
+
+	return rport;
+
+delete_rport:
+	spin_lock_irqsave(&fcvport->lock, flags);
+	list_del(&rport->peers);
+	put_device(&fcvport->dev);	/* for fcvport->rport list */
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+	put_device(dev->parent);
+
+	kfree(rport);
+	return NULL;
+}
+
+/**
+ * fc_fcrport_add - notify fc transport of the existence of a remote FC port.
+ * @channel:	Channel on port connected to.
+ * @ids:	The world wide names, fc address, and FC4 port
+ *		roles for the remote port.
+ *
+ * The LLDD calls this routine to notify the transport of the existence
+ * of a remote port. The LLDD provides the unique identifiers (wwpn,wwn)
+ * of the port, it's FC address (port_id), and the FC4 roles that are
+ * active for the port.
+ *
+ * For ports that are FCP targets (aka scsi targets), the FC transport
+ * maintains consistent target id bindings on behalf of the LLDD.
+ * A consistent target id binding is an assignment of a target id to
+ * a remote port identifier, which persists while the scsi host is
+ * attached. The remote port can disappear, then later reappear, and
+ * it's target id assignment remains the same. This allows for shifts
+ * in FC addressing (if binding by wwpn or wwnn) with no apparent
+ * changes to the scsi subsystem which is based on scsi host number and
+ * target id values.  Bindings are only valid during the attachment of
+ * the scsi host. If the host detaches, then later re-attaches, target
+ * id bindings may change.
+ *
+ * This routine is responsible for returning a remote port structure.
+ * The routine will search the list of remote ports it maintains
+ * internally on behalf of consistent target id mappings. If found, the
+ * remote port structure will be reused. Otherwise, a new remote port
+ * structure will be allocated.
+ *
+ * Whenever a remote port is allocated, a new fc_remote_port class
+ * device is created.
+ *
+ * Should not be called from interrupt context.
+ *
+ * Notes:
+ *	This routine assumes no locks are held on entry.
+ */
+struct fc_rport *fc_fcrport_add(struct fc_fcvport *fcvport,
+				struct fcrport_function_template *f,
+				int channel,
+				struct fc_rport_identifiers *ids)
+{
+	struct fc_rport *rport;
+	unsigned long flags;
+	int match = 0;
+
+	/* ensure any stgt delete functions are done */
+	fc_flush_work(fcvport);
+
+	/*
+	 * Search the list of "active" rports, for an rport that has been
+	 * deleted, but we've held off the real delete while the target
+	 * is in a "blocked" state.
+	 */
+	spin_lock_irqsave(&fcvport->lock, flags);
+
+	list_for_each_entry(rport, &fcvport->rports, peers) {
+
+		if ((rport->port_state == FC_PORTSTATE_BLOCKED) &&
+			(rport->channel == channel)) {
+
+			switch (fcvport->tgtid_bind_type) {
+			case FC_TGTID_BIND_BY_WWPN:
+			case FC_TGTID_BIND_NONE:
+				if (rport->port_name == ids->port_name)
+					match = 1;
+				break;
+			case FC_TGTID_BIND_BY_WWNN:
+				if (rport->node_name == ids->node_name)
+					match = 1;
+				break;
+			case FC_TGTID_BIND_BY_ID:
+				if (rport->port_id == ids->port_id)
+					match = 1;
+				break;
+			}
+
+			if (match) {
+
+				memcpy(&rport->node_name, &ids->node_name,
+					sizeof(rport->node_name));
+				memcpy(&rport->port_name, &ids->port_name,
+					sizeof(rport->port_name));
+				rport->port_id = ids->port_id;
+
+				rport->port_state = FC_PORTSTATE_ONLINE;
+				rport->roles = ids->roles;
+
+				spin_unlock_irqrestore(&fcvport->lock, flags);
+
+				if (f->dd_fcrport_size)
+					memset(rport->dd_data, 0,
+					       f->dd_fcrport_size);
+
+				/*
+				 * If we were not a target, cancel the
+				 * io terminate and rport timers, and
+				 * we're done.
+				 *
+				 * If we were a target, but our new role
+				 * doesn't indicate a target, leave the
+				 * timers running expecting the role to
+				 * change as the target fully logs in. If
+				 * it doesn't, the target will be torn down.
+				 *
+				 * If we were a target, and our role shows
+				 * we're still a target, cancel the timers
+				 * and kick off a scan.
+				 */
+
+				/* was a target, not in roles */
+				/*
+				 * TODO: This was previously checking the
+				 * scsi_target_id, which was just checking
+				 * to see if the rport had been initialized
+				 * as a TARGET or not. It also indicated that
+				 * the target had been scanned.
+				 *
+				 * Now we're just checking to see if the FC4
+				 * has been initialized or not. That should be
+				 * the same check.
+				 */
+				if ((!rport->fc4_priv) &&
+				    (!(ids->roles & FC_PORT_ROLE_FCP_TARGET)))
+					return rport;
+
+				/*
+				 * Stop the fail io and dev_loss timers.
+				 * If they flush, the port_state will
+				 * be checked and will NOOP the function.
+				 */
+				if (!cancel_delayed_work(&rport->fail_io_work))
+					fc_flush_devloss(fcvport);
+				if (!cancel_delayed_work(&rport->dev_loss_work))
+					fc_flush_devloss(fcvport);
+
+				spin_lock_irqsave(&fcvport->lock, flags);
+
+				rport->flags &= ~(FC_RPORT_FAST_FAIL_TIMEDOUT |
+						  FC_RPORT_DEVLOSS_PENDING |
+						  FC_RPORT_DEVLOSS_CALLBK_DONE);
+
+				/*
+				 * TODO: All this SCSI-ml stuff needs to go into
+				 * scsi_transport_fcp.c
+				 */
+
+				/* if target, initiate a scan */
+				if (rport->fc4_priv) {
+					rport->flags |= FC_RPORT_SCAN_PENDING;
+					fc4->fc4_targ_queue_scan(rport);
+					spin_unlock_irqrestore(&fcvport->lock,
+							       flags);
+
+					fc4->fc4_targ_unblock(rport);
+				} else
+					spin_unlock_irqrestore(&fcvport->lock,
+							       flags);
+
+				fc4->fc4_bsg_goose_queue(rport);
+
+				return rport;
+			}
+		}
+	}
+
+	/*
+	 * Search the bindings array
+	 * Note: if never a FCP target, you won't be on this list
+	 */
+	if (fcvport->tgtid_bind_type != FC_TGTID_BIND_NONE) {
+
+		/* search for a matching consistent binding */
+
+		list_for_each_entry(rport, &fcvport->rport_bindings,
+				    peers) {
+			if (rport->channel != channel)
+				continue;
+
+			switch (fcvport->tgtid_bind_type) {
+			case FC_TGTID_BIND_BY_WWPN:
+				if (rport->port_name == ids->port_name)
+					match = 1;
+				break;
+			case FC_TGTID_BIND_BY_WWNN:
+				if (rport->node_name == ids->node_name)
+					match = 1;
+				break;
+			case FC_TGTID_BIND_BY_ID:
+				if (rport->port_id == ids->port_id)
+					match = 1;
+				break;
+			case FC_TGTID_BIND_NONE: /* to keep compiler happy */
+				break;
+			}
+
+			if (match) {
+				list_move_tail(&rport->peers, &fcvport->rports);
+				break;
+			}
+		}
+
+		if (match) {
+			memcpy(&rport->node_name, &ids->node_name,
+				sizeof(rport->node_name));
+			memcpy(&rport->port_name, &ids->port_name,
+				sizeof(rport->port_name));
+			rport->port_id = ids->port_id;
+			rport->roles = ids->roles;
+			rport->port_state = FC_PORTSTATE_ONLINE;
+			rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT;
+
+			if (f->dd_fcrport_size)
+				memset(rport->dd_data, 0,
+				       f->dd_fcrport_size);
+
+			if (rport->roles & FC_PORT_ROLE_FCP_TARGET) {
+				/* initiate a scan of the target */
+				rport->flags |= FC_RPORT_SCAN_PENDING;
+				fc4->fc4_targ_queue_scan(rport);
+
+				spin_unlock_irqrestore(&fcvport->lock, flags);
+				fc4->fc4_targ_unblock(rport);
+			} else
+				spin_unlock_irqrestore(&fcvport->lock, flags);
+
+			return rport;
+		}
+	}
+
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+
+	/* No consistent binding found - create new remote port entry */
+	rport = fc_rport_create(fcvport, f, channel, ids);
+
+	return rport;
+}
+EXPORT_SYMBOL(fc_fcrport_add);
+
+
+/**
+ * fc_fcrport_del - notifies the fc transport that a remote port is no longer in existence.
+ * @rport:	The remote port that no longer exists
+ *
+ * The LLDD calls this routine to notify the transport that a remote
+ * port is no longer part of the topology. Note: Although a port
+ * may no longer be part of the topology, it may persist in the remote
+ * ports displayed by the fcpinit. We do this under 2 conditions:
+ * 1) If the port was a scsi target, we delay its deletion by "blocking" it.
+ *   This allows the port to temporarily disappear, then reappear without
+ *   disrupting the SCSI device tree attached to it. During the "blocked"
+ *   period the port will still exist.
+ * 2) If the port was a scsi target and disappears for longer than we
+ *   expect, we'll delete the port and the tear down the SCSI device tree
+ *   attached to it. However, we want to semi-persist the target id assigned
+ *   to that port if it eventually does exist. The port structure will
+ *   remain (although with minimal information) so that the target id
+ *   bindings remails.
+ *
+ * If the remote port is not an FCP Target, it will be fully torn down
+ * and deallocated, including the fc_remote_port class device.
+ *
+ * If the remote port is an FCP Target, the port will be placed in a
+ * temporary blocked state. From the LLDD's perspective, the rport no
+ * longer exists. From the SCSI midlayer's perspective, the SCSI target
+ * exists, but all sdevs on it are blocked from further I/O. The following
+ * is then expected.
+ *
+ *   If the remote port does not return (signaled by a LLDD call to
+ *   fc_fcrport_add()) within the dev_loss_tmo timeout, then the
+ *   scsi target is removed - killing all outstanding i/o and removing the
+ *   scsi devices attached ot it. The port structure will be marked Not
+ *   Present and be partially cleared, leaving only enough information to
+ *   recognize the remote port relative to the scsi target id binding if
+ *   it later appears.  The port will remain as long as there is a valid
+ *   binding (e.g. until the user changes the binding type or unloads the
+ *   scsi host with the binding).
+ *
+ *   If the remote port returns within the dev_loss_tmo value (and matches
+ *   according to the target id binding type), the port structure will be
+ *   reused. If it is no longer a SCSI target, the target will be torn
+ *   down. If it continues to be a SCSI target, then the target will be
+ *   unblocked (allowing i/o to be resumed), and a scan will be activated
+ *   to ensure that all luns are detected.
+ *
+ * Called from normal process context only - cannot be called from interrupt.
+ *
+ * Notes:
+ *	This routine assumes no locks are held on entry.
+ */
+void fc_fcrport_del(struct fc_rport *rport)
+{
+	struct fc_fcvport *fcvport = rport_to_fcvport(rport);
+	int timeout = rport->dev_loss_tmo;
+	unsigned long flags;
+
+	/*
+	 * No need to flush the fcpinit work_q's, as all adds are synchronous.
+	 *
+	 * We do need to reclaim the rport scan work element, so eventually
+	 * (in fc_rport_final_delete()) we'll flush the scsi host work_q if
+	 * there's still a scan pending.
+	 */
+
+	spin_lock_irqsave(&fcvport->lock, flags);
+
+	if (rport->port_state != FC_PORTSTATE_ONLINE) {
+		spin_unlock_irqrestore(&fcvport->lock, flags);
+		return;
+	}
+
+	/*
+	 * In the past, we if this was not an FCP-Target, we would
+	 * unconditionally just jump to deleting the rport.
+	 * However, rports can be used as node containers by the LLDD,
+	 * and its not appropriate to just terminate the rport at the
+	 * first sign of a loss in connectivity. The LLDD may want to
+	 * send ELS traffic to re-validate the login. If the rport is
+	 * immediately deleted, it makes it inappropriate for a node
+	 * container.
+	 * So... we now unconditionally wait dev_loss_tmo before
+	 * destroying an rport.
+	 */
+
+	rport->port_state = FC_PORTSTATE_BLOCKED;
+
+	rport->flags |= FC_RPORT_DEVLOSS_PENDING;
+
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+
+	fc4->fc4_targ_final_delete(rport);
+
+	fc4->fc4_targ_block(rport);
+
+	/* see if we need to kill io faster than waiting for device loss */
+	if ((rport->fast_io_fail_tmo != -1) &&
+	    (rport->fast_io_fail_tmo < timeout))
+		fc_queue_devloss_work(fcvport, &rport->fail_io_work,
+				      rport->fast_io_fail_tmo * HZ);
+
+	/* cap the length the devices can be blocked until they are deleted */
+	fc_queue_devloss_work(fcvport, &rport->dev_loss_work, timeout * HZ);
+}
+EXPORT_SYMBOL(fc_fcrport_del);
+
+/**
+ * fc_fcrport_rolechg - notifies the fc transport that the roles on a remote may have changed.
+ * @rport:	The remote port that changed.
+ * @roles:      New roles for this port.
+ *
+ * Description: The LLDD calls this routine to notify the transport that the
+ * roles on a remote port may have changed. The largest effect of this is
+ * if a port now becomes a FCP Target, it must be allocated a
+ * scsi target id.  If the port is no longer a FCP target, any
+ * scsi target id value assigned to it will persist in case the
+ * role changes back to include FCP Target. No changes in the scsi
+ * midlayer will be invoked if the role changes (in the expectation
+ * that the role will be resumed. If it doesn't normal error processing
+ * will take place).
+ *
+ * Should not be called from interrupt context.
+ *
+ * Notes:
+ *	This routine assumes no locks are held on entry.
+ */
+void fc_fcrport_rolechg(struct fc_rport *rport, u32 roles)
+{
+	struct fc_fcvport *fcvport = rport_to_fcvport(rport);
+	unsigned long flags;
+	int create = 0;
+
+	spin_lock_irqsave(&fcvport->lock, flags);
+	create = fc4->fc4_targ_rolechg(rport, roles);
+	rport->roles = roles;
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+
+	if (create) {
+		/*
+		 * There may have been a delete timer running on the
+		 * port. Ensure that it is cancelled as we now know
+		 * the port is an FCP Target.
+		 * Note: we know the rport is exists and in an online
+		 *  state as the LLDD would not have had an rport
+		 *  reference to pass us.
+		 *
+		 * Take no action on the del_timer failure as the state
+		 * machine state change will validate the
+		 * transaction.
+		 */
+		if (!cancel_delayed_work(&rport->fail_io_work))
+			fc_flush_devloss(fcvport);
+		if (!cancel_delayed_work(&rport->dev_loss_work))
+			fc_flush_devloss(fcvport);
+
+		spin_lock_irqsave(&fcvport->lock, flags);
+		rport->flags &= ~(FC_RPORT_FAST_FAIL_TIMEDOUT |
+				  FC_RPORT_DEVLOSS_PENDING);
+		spin_unlock_irqrestore(&fcvport->lock, flags);
+
+		/* ensure any stgt delete functions are done */
+		fc_flush_work(fcvport);
+
+		/* initiate a scan of the target */
+		spin_lock_irqsave(&fcvport->lock, flags);
+		rport->flags |= FC_RPORT_SCAN_PENDING;
+		fc4->fc4_targ_queue_scan(rport);
+		spin_unlock_irqrestore(&fcvport->lock, flags);
+		fc4->fc4_targ_unblock(rport);
+	}
+}
+EXPORT_SYMBOL(fc_fcrport_rolechg);
+
+/**
+ * fc_timeout_deleted_rport - Timeout handler for a deleted remote port.
+ * @work:	rport target that failed to reappear in the allotted time.
+ *
+ * Description: An attempt to delete a remote port blocks, and if it fails
+ *              to return in the allotted time this gets called.
+ */
+static void fc_timeout_deleted_rport(struct work_struct *work)
+{
+	struct fc_rport *rport =
+		container_of(work, struct fc_rport, dev_loss_work.work);
+	struct fc_fcvport *fcvport = rport_to_fcvport(rport);
+	unsigned long flags;
+	int do_callback = 0;
+
+	spin_lock_irqsave(&fcvport->lock, flags);
+
+	rport->flags &= ~FC_RPORT_DEVLOSS_PENDING;
+
+	/*
+	 * If the port is ONLINE, then it came back. If it was a SCSI
+	 * target, validate it still is. If not, tear down the
+	 * scsi_target on it.
+	 */
+	if ((rport->port_state == FC_PORTSTATE_ONLINE) &&
+	    !(rport->roles & FC_PORT_ROLE_FCP_TARGET)) {
+		dev_printk(KERN_ERR, &rport->dev,
+			"blocked FC remote port time out: no longer"
+			" a FCP target, removing starget\n");
+		spin_unlock_irqrestore(&fcvport->lock, flags);
+		fc4->fc4_targ_unblock(rport);
+		fc4->fc4_queue_starget_delete(rport);
+		return;
+	}
+
+	/* NOOP state - we're flushing workq's */
+	if (rport->port_state != FC_PORTSTATE_BLOCKED) {
+		spin_unlock_irqrestore(&fcvport->lock, flags);
+		dev_printk(KERN_ERR, &rport->dev,
+			   "blocked FC remote port time out: leaving"
+			   " rport%s alone\n",
+			   (rport->fc4_priv) ?  " and starget" : "");
+		return;
+	}
+
+	if ((fcvport->tgtid_bind_type == FC_TGTID_BIND_NONE) ||
+	    !(rport->roles & FC_PORT_ROLE_FCP_TARGET)) {
+		list_del(&rport->peers);
+		rport->port_state = FC_PORTSTATE_DELETED;
+		dev_printk(KERN_ERR, &rport->dev,
+			   "blocked FC remote port time out: removing"
+			   " rport%s\n",
+			   (rport->fc4_priv) ?  " and starget" : "");
+		fc_queue_work(fcvport, &rport->rport_delete_work);
+		spin_unlock_irqrestore(&fcvport->lock, flags);
+		return;
+	}
+
+	dev_printk(KERN_ERR, &rport->dev,
+		"blocked FC remote port time out: removing target and "
+		"saving binding\n");
+
+	list_move_tail(&rport->peers, &fcvport->rport_bindings);
+
+	/*
+	 * Note: We do not remove or clear the hostdata area. This allows
+	 *   host-specific target data to persist along with the
+	 *   scsi_target_id. It's up to the host to manage it's hostdata area.
+	 */
+
+	/*
+	 * Reinitialize port attributes that may change if the port comes back.
+	 */
+	rport->maxframe_size = -1;
+	rport->supported_classes = FC_COS_UNSPECIFIED;
+	rport->roles = FC_PORT_ROLE_UNKNOWN;
+	rport->port_state = FC_PORTSTATE_NOTPRESENT;
+	rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT;
+
+	/*
+	 * Pre-emptively kill I/O rather than waiting for the work queue
+	 * item to teardown the starget. (FCOE libFC folks prefer this
+	 * and to have the rport_port_id still set when it's done).
+	 */
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+	fc4->fc4_targ_terminate_io(rport);
+
+	spin_lock_irqsave(&fcvport->lock, flags);
+
+	if (rport->port_state == FC_PORTSTATE_NOTPRESENT) {	/* still missing */
+
+		/* remove the identifiers that aren't used in the consisting binding */
+		switch (fcvport->tgtid_bind_type) {
+		case FC_TGTID_BIND_BY_WWPN:
+			rport->node_name = -1;
+			rport->port_id = -1;
+			break;
+		case FC_TGTID_BIND_BY_WWNN:
+			rport->port_name = -1;
+			rport->port_id = -1;
+			break;
+		case FC_TGTID_BIND_BY_ID:
+			rport->node_name = -1;
+			rport->port_name = -1;
+			break;
+		case FC_TGTID_BIND_NONE:	/* to keep compiler happy */
+			break;
+		}
+
+		/*
+		 * As this only occurs if the remote port (scsi target)
+		 * went away and didn't come back - we'll remove
+		 * all attached scsi devices.
+		 */
+		rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
+		fc4->fc4_queue_starget_delete(rport);
+
+		do_callback = 1;
+	}
+
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+
+	/*
+	 * Notify the driver that the rport is now dead. The LLDD will
+	 * also guarantee that any communication to the rport is terminated
+	 *
+	 * Note: we set the CALLBK_DONE flag above to correspond
+	 */
+	if (do_callback && rport->f->dev_loss_tmo_callbk)
+		rport->f->dev_loss_tmo_callbk(rport);
+}
+
+
+/**
+ * fc_timeout_fail_rport_io - Timeout handler for a fast io failing on a disconnected SCSI target.
+ * @work:	rport to terminate io on.
+ *
+ * Notes: Only requests the failure of the io, not that all are flushed
+ *    prior to returning.
+ */
+static void
+fc_timeout_fail_rport_io(struct work_struct *work)
+{
+	struct fc_rport *rport =
+		container_of(work, struct fc_rport, fail_io_work.work);
+
+	if (rport->port_state != FC_PORTSTATE_BLOCKED)
+		return;
+
+	rport->flags |= FC_RPORT_FAST_FAIL_TIMEDOUT;
+	fc4->fc4_targ_terminate_io(rport);
+}
diff --git a/drivers/fc/fcsysfs.c b/drivers/fc/fcsysfs.c
new file mode 100644
index 0000000..fd751a2
--- /dev/null
+++ b/drivers/fc/fcsysfs.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright(c) 2010 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.
+ */
+
+#include "fcsysfs.h"
+
+
+/*
+ * Original Author:  Martin Hicks
+ * Revised by:       James Smart
+*/
+MODULE_AUTHOR("Robert Love");
+MODULE_DESCRIPTION("Fibre Channel");
+MODULE_LICENSE("GPL");
+
+/*
+ * dev_loss_tmo: the default number of seconds that the FC transport
+ *   should insulate the loss of a remote port.
+ *   The maximum will be capped by the value of SCSI_DEVICE_BLOCK_MAX_TIMEOUT.
+ */
+unsigned int fc_dev_loss_tmo = 60;		/* seconds */
+module_param_named(dev_loss_tmo, fc_dev_loss_tmo, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(dev_loss_tmo,
+		 "Maximum number of seconds that the FC transport should"
+		 " insulate the loss of a remote port. Once this value is"
+		 " exceeded, the scsi target is removed. Value should be"
+		 " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT if"
+		 " fast_io_fail_tmo is not set.");
+
+/*
+ * Netlink Infrastructure
+ */
+
+static atomic_t fc_event_seq;
+
+/**
+ * fc_get_event_number - Obtain the next sequential FC event number
+ *
+ * Notes:
+ *   We could have inlined this, but it would have required fc_event_seq to
+ *   be exposed. For now, live with the subroutine call.
+ *   Atomic used to avoid lock/unlock...
+ */
+u32 fc_get_event_number(void)
+{
+	return atomic_add_return(1, &fc_event_seq);
+}
+//EXPORT_SYMBOL(fc_get_event_number);
+
+/* Private Host Attributes */
+
+static int __init fc_init(void)
+{
+	int error;
+
+	error = class_register(&fcport_class);
+	if (error)
+		return error;
+
+	error = class_register(&fcfabric_class);
+	if (error)
+		goto unreg_fcport;
+
+	error = class_register(&fcvport_class);
+	if (error)
+		goto unreg_fcfabric;
+
+	error = class_register(&fcrport_class);
+	if (error)
+		goto unreg_fcvport;
+
+	return 0;
+
+unreg_fcvport:
+	class_unregister(&fcvport_class);
+unreg_fcfabric:
+	class_unregister(&fcfabric_class);
+unreg_fcport:
+	class_unregister(&fcport_class);
+	return error;
+}
+module_init(fc_init);
+
+static void __exit fc_exit(void)
+{
+	class_unregister(&fcrport_class);
+	class_unregister(&fcvport_class);
+	class_unregister(&fcfabric_class);
+	class_unregister(&fcport_class);
+}
+module_exit(fc_exit);
+
+struct _fc_port_types fc_port_type_names[] = {
+	{ FC_PORTTYPE_UNKNOWN,		"Unknown" },
+	{ FC_PORTTYPE_OTHER,		"Other" },
+	{ FC_PORTTYPE_NOTPRESENT,	"Not Present" },
+	{ FC_PORTTYPE_NPORT,	"NPort (fabric via point-to-point)" },
+	{ FC_PORTTYPE_NLPORT,	"NLPort (fabric via loop)" },
+	{ FC_PORTTYPE_LPORT,	"LPort (private loop)" },
+	{ FC_PORTTYPE_PTP,	"Point-To-Point (direct nport connection)" },
+	{ FC_PORTTYPE_NPIV,		"NPIV VPORT" },
+};
+/* Convert fc_port_type values to ascii string name */
+fc_enum_name_search(port_type, fc_port_type, fc_port_type_names)
+EXPORT_SYMBOL(get_fc_port_type_name);
+fc_enum_name_search(vport_type, fc_port_type, fc_port_type_names)
+EXPORT_SYMBOL(get_fc_vport_type_name);
+
+struct _fc_port_role_names  fc_port_role_names[] = {
+	{ FC_PORT_ROLE_FCP_TARGET,	"FCP Target" },
+	{ FC_PORT_ROLE_FCP_INITIATOR,	"FCP Initiator" },
+	{ FC_PORT_ROLE_IP_PORT,		"IP Port" },
+};
+/* Convert FC_PORT_ROLE bit values to ascii string name */
+fc_bitfield_name_search(port_roles, fc_port_role_names)
+EXPORT_SYMBOL(get_fc_port_roles_names);
+
+struct _fc_cos_names fc_cos_names[] = {
+	{ FC_COS_CLASS1,	"Class 1" },
+	{ FC_COS_CLASS2,	"Class 2" },
+	{ FC_COS_CLASS3,	"Class 3" },
+	{ FC_COS_CLASS4,	"Class 4" },
+	{ FC_COS_CLASS6,	"Class 6" },
+};
+
+/* Convert FC_COS bit values to ascii string name */
+fc_bitfield_name_search(cos, fc_cos_names)
+EXPORT_SYMBOL(get_fc_cos_names);
+
+/* Reuse fc_port_type enum function for vport_type */
+//#define get_fc_vport_type_name get_fc_port_type_name
+
+
+/* Convert fc_port_state values to ascii string name */
+static struct {
+	enum fc_port_state	value;
+	char			*name;
+} fc_port_state_names[] = {
+	{ FC_PORTSTATE_UNKNOWN,		"Unknown" },
+	{ FC_PORTSTATE_NOTPRESENT,	"Not Present" },
+	{ FC_PORTSTATE_ONLINE,		"Online" },
+	{ FC_PORTSTATE_OFFLINE,		"Offline" },
+	{ FC_PORTSTATE_BLOCKED,		"Blocked" },
+	{ FC_PORTSTATE_BYPASSED,	"Bypassed" },
+	{ FC_PORTSTATE_DIAGNOSTICS,	"Diagnostics" },
+	{ FC_PORTSTATE_LINKDOWN,	"Linkdown" },
+	{ FC_PORTSTATE_ERROR,		"Error" },
+	{ FC_PORTSTATE_LOOPBACK,	"Loopback" },
+	{ FC_PORTSTATE_DELETED,		"Deleted" },
+};
+fc_enum_name_search(port_state, fc_port_state, fc_port_state_names)
+EXPORT_SYMBOL(get_fc_port_state_name);
+
+struct fc4_template *fc4;
+
+int register_fc4(struct fc4_template *tmpl)
+{
+	fc4 = tmpl;
+	return 0;
+}
+EXPORT_SYMBOL(register_fc4);
+
+void unregister_fc4()
+{
+	fc4 = NULL;
+}
+EXPORT_SYMBOL(unregister_fc4);
diff --git a/drivers/fc/fcsysfs.h b/drivers/fc/fcsysfs.h
new file mode 100644
index 0000000..2df0f9a
--- /dev/null
+++ b/drivers/fc/fcsysfs.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright(c) 2010 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.
+ */
+
+#ifndef _FC_SYSFS_H_
+#define _FC_SYSFS_H_
+
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <fc/fc.h>
+
+#define fc_bitfield_name_search(title, table)			\
+ssize_t get_fc_##title##_names(u32 table_key, char *buf)	\
+{								\
+	char *prefix = "";					\
+	ssize_t len = 0;					\
+	int i;							\
+								\
+	for (i = 0; i < ARRAY_SIZE(table); i++) {		\
+		if (table[i].value & table_key) {		\
+			len += sprintf(buf + len, "%s%s",	\
+				prefix, table[i].name);		\
+			prefix = ", ";				\
+		}						\
+	}							\
+	len += sprintf(buf + len, "\n");			\
+	return len;						\
+}
+
+/*
+ * Define roles that are specific to port_id. Values are relative to ROLE_MASK.
+ */
+#define FC_WELLKNOWN_PORTID_MASK	0xfffff0
+#define FC_WELLKNOWN_ROLE_MASK  	0x00000f
+#define FC_FPORT_PORTID			0x00000e
+#define FC_FABCTLR_PORTID		0x00000d
+#define FC_DIRSRVR_PORTID		0x00000c
+#define FC_TIMESRVR_PORTID		0x00000b
+#define FC_MGMTSRVR_PORTID		0x00000a
+
+extern struct class fcport_class;
+extern struct class fcfabric_class;
+extern struct class fcvport_class;
+extern struct class fcrport_class;
+
+extern unsigned int fc_dev_loss_tmo;
+
+extern struct fc4_template *fc4;
+
+int fc_vport_terminate(struct fc_fcfabric *, struct fc_fcvport *vport);
+void fc_flush_devloss(struct fc_fcvport *fcvport);
+int fc_queue_devloss_work(struct fc_fcvport *fcvport, struct delayed_work *work,
+			  unsigned long delay);
+
+#define FC_PORTTYPE_MAX_NAMELEN		50
+const char *get_fc_port_type_name(enum fc_port_type table_key);
+const char *get_fc_vport_type_name(enum fc_port_type table_key);
+ssize_t get_fc_port_roles_names(u32 table_key, char *buf);
+ssize_t get_fc_cos_names(u32 table_key, char *buf);
+
+void fc_fcfabric_add_npiv(struct fc_fcfabric *fcfabric);
+
+#endif /*_FC_SYSFS_H_*/
diff --git a/drivers/fc/fcvport.c b/drivers/fc/fcvport.c
new file mode 100644
index 0000000..2079b5e
--- /dev/null
+++ b/drivers/fc/fcvport.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright(c) 2010 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.
+ */
+
+#include "fcsysfs.h"
+
+static atomic_t fcvport_num;
+
+static void fc_vport_sched_delete(struct work_struct *work);
+
+#define fcvport_rd_attr(field, format_string, sz)			\
+	fc_conditional_show_function(fcvport, field, format_string, sz, ) \
+	static FC_DEVICE_ATTR(fcvport, field, S_IRUGO,			\
+			      show_fcvport_##field, NULL)
+
+#define fc_private_fcvport_rd_attr(field, format_string, sz)		\
+	fc_always_show_function(fcvport, field, format_string, sz, )	\
+	static FC_DEVICE_ATTR(fcvport, field, S_IRUGO,			\
+			      show_fcvport_##field, NULL)
+
+#define fc_private_fcvport_rd_attr_cast(field, format_string, sz, cast)	\
+	fc_always_show_function(fcvport, field, format_string, sz, (cast)) \
+	static FC_DEVICE_ATTR(fcvport, field, S_IRUGO,			\
+			      show_fcvport_##field, NULL)
+
+/* Convert fc_vport_state values to ascii string name */
+static struct {
+	enum fc_vport_state	value;
+	char			*name;
+} fc_vport_state_names[] = {
+	{ FC_VPORT_UNKNOWN,		"Unknown" },
+	{ FC_VPORT_ACTIVE,		"Active" },
+	{ FC_VPORT_DISABLED,		"Disabled" },
+	{ FC_VPORT_LINKDOWN,		"Linkdown" },
+	{ FC_VPORT_INITIALIZING,	"Initializing" },
+	{ FC_VPORT_NO_FABRIC_SUPP,	"No Fabric Support" },
+	{ FC_VPORT_NO_FABRIC_RSCS,	"No Fabric Resources" },
+	{ FC_VPORT_FABRIC_LOGOUT,	"Fabric Logout" },
+	{ FC_VPORT_FABRIC_REJ_WWN,	"Fabric Rejected WWN" },
+	{ FC_VPORT_FAILED,		"VPort Failed" },
+};
+fc_enum_name_search(vport_state, fc_vport_state, fc_vport_state_names)
+#define FC_VPORTSTATE_MAX_NAMELEN	24
+
+/* Reuse fc_vport_state enum function for vport_last_state */
+#define get_fc_vport_last_state_name get_fc_vport_state_name
+
+/*
+ * TODO: Notce that we're still using get_fc_##title##_name and not
+ * a get_fcvport_##title##_name routine. The port_type structure,
+ * related lookups and names need to be somewhere global for rports
+ * and other fc_host obects.
+ */
+#define fcvport_rd_enum_attr(title, maxlen)				\
+	static ssize_t show_fcvport_##title (struct device *dev,	\
+					     struct device_attribute *attr, \
+					     char *buf)			\
+{									\
+	struct fc_fcvport *fcvport = dev_to_fcvport(dev);		\
+	const char *name;						\
+	if (fcvport->f->get_fcvport_##title)				\
+		fcvport->f->get_fcvport_##title(fcvport);		\
+	name = get_fc_##title##_name(fcvport_##title(fcvport));		\
+	if (!name)							\
+		return -EINVAL;						\
+	return snprintf(buf, maxlen, "%s\n", name);			\
+}									\
+static FC_DEVICE_ATTR(fcvport, title, S_IRUGO, show_fcvport_##title, NULL)
+
+#define fcvport_store_function(field)					\
+	static ssize_t store_fcvport_##field(struct device *dev,	\
+					     struct device_attribute *attr, \
+					     const char *buf, size_t count) \
+{									\
+	int val;							\
+	struct fc_vport *vport = dev_to_vport(dev);			\
+	char *cp;							\
+	if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))		\
+		return -EBUSY;						\
+	val = simple_strtoul(buf, &cp, 0);				\
+	if (*cp && (*cp != '\n'))					\
+		return -EINVAL;						\
+	vport->f->set_vport_##field(vport, val);			\
+	return count;							\
+}
+
+#define fcvport_store_str_function(field, slen)				\
+	static ssize_t store_fcvport_##field(struct device *dev,	\
+					     struct device_attribute *attr, \
+					     const char *buf, size_t count) \
+{									\
+	struct fc_fcvport *fcvport = dev_to_fcvport(dev);		\
+	unsigned int cnt = count;					\
+									\
+	/* count may include a LF at end of string */			\
+	if (buf[cnt-1] == '\n')						\
+		cnt--;							\
+	if (cnt > ((slen) - 1))						\
+		return -EINVAL;						\
+	memcpy(fcvport->field, buf, cnt);				\
+	fcvport->f->set_fcvport_##field(fcvport);			\
+	return count;							\
+}
+
+#define fcvport_rd_attr(field, format_string, sz)			\
+	fc_conditional_show_function(fcvport, field, format_string, sz, ) \
+	static FC_DEVICE_ATTR(fcvport, field, S_IRUGO,			\
+			      show_fcvport_##field, NULL)
+
+#define fcvport_rd_attr_cast(field, format_string, sz, cast)		\
+	fc_conditional_show_function(fcvport, field, format_string, sz, (cast)) \
+	static FC_DEVICE_ATTR(fcvport, field, S_IRUGO,			\
+			      show_fcvport_##field, NULL)
+
+#define fc_vport_rw_attr(field, format_string, sz)			\
+	fc_conditional_show_function(fcvport, field, format_string, sz, ) \
+	fcvport_store_function(field)					\
+	static FC_DEVICE_ATTR(fcvport, field, S_IRUGO | S_IWUSR,	\
+			      show_fcvport_##field,			\
+			      store_fcvport_##field)
+
+#define fc_private_fcvport_store_u32_function(field)			\
+	static ssize_t store_fcvport_##field(struct device *dev,	\
+					     struct device_attribute *attr, \
+					     const char *buf, size_t count) \
+{									\
+	u32 val;							\
+	struct fc_fcvport *fcvport = dev_to_fcvport(dev);		\
+	char *cp;							\
+	if (fcvport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))	\
+		return -EBUSY;						\
+	val = simple_strtoul(buf, &cp, 0);				\
+	if (*cp && (*cp != '\n'))					\
+		return -EINVAL;						\
+	fcvport->field = val;						\
+	return count;							\
+}
+
+
+#define fc_private_fcvport_rw_u32_attr(field, format_string, sz)	\
+	fc_always_show_function(fcvport, field, format_string, sz, )	\
+	fc_private_fcvport_store_u32_function(field)			\
+	static FC_DEVICE_ATTR(fcvport, field, S_IRUGO | S_IWUSR,	\
+			      show_fcvport_##field,			\
+			      store_fcvport_##field)
+
+
+#define fc_private_fcvport_rd_enum_attr(title, maxlen)			\
+	static ssize_t show_fcvport_##title (struct device *dev,	\
+					     struct device_attribute *attr, \
+					     char *buf)			\
+{									\
+	struct fc_fcvport *fcvport = dev_to_fcvport(dev);		\
+	const char *name;						\
+	name = get_fc_##title##_name(fcvport->title);			\
+	if (!name)							\
+		return -EINVAL;						\
+	return snprintf(buf, maxlen, "%s\n", name);			\
+}									\
+	static FC_DEVICE_ATTR(fcvport, title, S_IRUGO,			\
+			      show_fcvport_##title, NULL)
+
+/*
+ * TODO: I'm not sure how this macro is supposed to work. Why would there
+ * be a "field" in the function template? It's for vport_delete and
+ * vport_destroy, but I don't get it!
+ */
+#define SETUP_FCVPORT_ATTRIBUTE_WR(field)				\
+	if (fcvport->f->field) {					\
+		fcvport->attrs[count] = device_attr_fcvport_##field;	\
+		device_create_file(&fcvport->dev, &fcvport->attrs[count]); \
+		count++;						\
+	}
+	/* NOTE: Above MACRO differs: checks function */
+
+#define SETUP_FCVPORT_ATTRIBUTE_RW(field)				\
+	if (!fcvport->f->set_fcvport_##field) {				\
+		fcvport->attrs[count] = device_attr_fcvport_##field;	\
+		fcvport->attrs[count].attr.mode = S_IRUGO;		\
+		fcvport->attrs[count].store = NULL;			\
+		count++;						\
+	}
+	/* NOTE: Above MACRO differs: does not check show bit */
+
+#define SETUP_PRIVATE_FCVPORT_ATTRIBUTE_RW(field)			\
+{									\
+	fcvport->attrs[count] = device_attr_fcvport_##field;		\
+	count++;							\
+}
+
+
+/* The FC Transport Virtual Port Attributes: */
+
+/* Fixed Virtual Port Attributes */
+
+/* Dynamic Virtual Port Attributes */
+
+/* Private Virtual Port Attributes */
+
+fc_private_fcvport_rd_enum_attr(vport_state, FC_VPORTSTATE_MAX_NAMELEN);
+fc_private_fcvport_rd_enum_attr(vport_last_state, FC_VPORTSTATE_MAX_NAMELEN);
+fc_private_fcvport_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
+fc_private_fcvport_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
+
+static ssize_t show_fcvport_roles(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	struct fc_fcvport *fcvport = dev_to_fcvport(dev);
+
+	if (fcvport->roles == FC_PORT_ROLE_UNKNOWN)
+		return snprintf(buf, 20, "unknown\n");
+	return get_fc_port_roles_names(fcvport->roles, buf);
+}
+static FC_DEVICE_ATTR(fcvport, roles, S_IRUGO, show_fcvport_roles, NULL);
+
+fc_private_fcvport_rd_enum_attr(vport_type, FC_PORTTYPE_MAX_NAMELEN);
+
+fc_always_show_function(fcvport, symbolic_name, "%s\n",
+			FC_VPORT_SYMBOLIC_NAMELEN + 1, )
+fcvport_store_str_function(symbolic_name, FC_VPORT_SYMBOLIC_NAMELEN)
+static FC_DEVICE_ATTR(fcvport, symbolic_name, S_IRUGO | S_IWUSR,
+		      show_fcvport_symbolic_name, store_fcvport_symbolic_name);
+
+static ssize_t store_fcvport_issue_lip(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t count)
+{
+	struct fc_fcvport *fcvport = dev_to_fcvport(dev);
+	int ret;
+
+	/* ignore any data value written to the attribute */
+	if (fcvport->f->issue_fcvport_lip) {
+		ret = fcvport->f->issue_fcvport_lip(fcvport);
+		return ret ? ret: count;
+	}
+
+	return -ENOENT;
+}
+
+static FC_DEVICE_ATTR(fcvport, issue_lip, S_IWUSR, NULL,
+		      store_fcvport_issue_lip);
+
+/* Convert fc_tgtid_binding_type values to ascii string name */
+static const struct {
+	enum fc_tgtid_binding_type	value;
+	char				*name;
+	int				matchlen;
+} fc_tgtid_binding_type_names[] = {
+	{ FC_TGTID_BIND_NONE, "none", 4 },
+	{ FC_TGTID_BIND_BY_WWPN, "wwpn (World Wide Port Name)", 4 },
+	{ FC_TGTID_BIND_BY_WWNN, "wwnn (World Wide Node Name)", 4 },
+	{ FC_TGTID_BIND_BY_ID, "port_id (FC Address)", 7 },
+};
+fc_enum_name_search(tgtid_bind_type, fc_tgtid_binding_type,
+		    fc_tgtid_binding_type_names)
+fc_enum_name_match(tgtid_bind_type, fc_tgtid_binding_type,
+		   fc_tgtid_binding_type_names)
+#define FC_BINDTYPE_MAX_NAMELEN	30
+
+static ssize_t show_fcvport_tgtid_bind_type(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	struct fc_fcvport *fcvport = dev_to_fcvport(dev);
+	const char *name;
+
+	name = get_fc_tgtid_bind_type_name(fcvport_tgtid_bind_type(fcvport));
+	if (!name)
+		return -EINVAL;
+	return snprintf(buf, FC_BINDTYPE_MAX_NAMELEN, "%s\n", name);
+}
+
+#define get_list_head_entry(pos, head, member)			\
+	pos = list_entry((head)->next, typeof(*pos), member)
+
+static ssize_t store_fcvport_tgtid_bind_type(struct device *dev,
+					     struct device_attribute *attr,
+					     const char *buf, size_t count)
+{
+	struct fc_fcvport *fcvport = dev_to_fcvport(dev);
+	struct fc_rport *rport;
+	enum fc_tgtid_binding_type val;
+	unsigned long flags;
+
+	if (get_fc_tgtid_bind_type_match(buf, &val))
+		return -EINVAL;
+
+	/* if changing bind type, purge all unused consistent bindings */
+	if (val != fcvport_tgtid_bind_type(fcvport)) {
+		spin_lock_irqsave(&fcvport->lock, flags);
+		while (!list_empty(&fcvport_rport_bindings(fcvport))) {
+			get_list_head_entry(rport,
+				&fcvport_rport_bindings(fcvport), peers);
+			list_del(&rport->peers);
+			rport->port_state = FC_PORTSTATE_DELETED;
+			fc_queue_work(fcvport, &rport->rport_delete_work);
+		}
+		spin_unlock_irqrestore(&fcvport->lock, flags);
+	}
+
+	fcvport_tgtid_bind_type(fcvport) = val;
+	return count;
+}
+
+static FC_DEVICE_ATTR(fcvport, tgtid_bind_type, S_IRUGO | S_IWUSR,
+		      show_fcvport_tgtid_bind_type,
+		      store_fcvport_tgtid_bind_type);
+
+/*
+static ssize_t
+store_fc_vport_delete(struct device *dev, struct device_attribute *attr,
+		      const char *buf, size_t count)
+{
+	struct fc_fcvport *fcvport = dev_to_fcvport(dev);
+	struct fc_fcfabric *fcfabric = fcvport_to_fcfabric(fcvport);
+
+	fc4->fc4_queue_work(fcpinit, &vport->vport_delete_work);
+	return count;
+}
+static FC_DEVICE_ATTR(vport, vport_delete, S_IWUSR,
+			NULL, store_fc_vport_delete);
+
+
+*
+ * Enable/Disable vport
+ *  Write "1" to disable, write "0" to enable
+ *
+static ssize_t
+store_fc_vport_disable(struct device *dev, struct device_attribute *attr,
+		       const char *buf,
+			   size_t count)
+{
+	struct fc_vport *vport = transport_class_to_vport(dev);
+	struct Scsi_Host *shost = vport_to_shost(vport);
+	struct fc_internal *i = to_fc_internal(shost->transportt);
+	int stat;
+
+	if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))
+		return -EBUSY;
+
+	if (*buf == '0') {
+		if (vport->vport_state != FC_VPORT_DISABLED)
+			return -EALREADY;
+	} else if (*buf == '1') {
+		if (vport->vport_state == FC_VPORT_DISABLED)
+			return -EALREADY;
+	} else
+		return -EINVAL;
+
+	stat = i->f->vport_disable(vport, ((*buf == '0') ? false : true));
+	return stat ? stat : count;
+}
+static FC_DEVICE_ATTR(vport, vport_disable, S_IWUSR,
+		NULL, store_fc_vport_disable);
+*/
+
+fcvport_rd_attr(port_id, "0x%06x\n", 20);
+fcvport_rd_enum_attr(port_type, FC_PORTTYPE_MAX_NAMELEN);
+
+/**
+ * fc_queue_work - Queue work to the fcpinit workqueue.
+ * @shost:      Pointer to Scsi_Host bound to fcpinit.
+ * @work:       Work to queue for execution.
+ *
+ * Return value:
+ *      1 - work queued for execution
+ *      0 - work is already queued
+ *      -EINVAL - work queue doesn't exist
+ */
+int fc_queue_work(struct fc_fcvport *fcvport, struct work_struct *work)
+{
+	if (unlikely(!fcvport_work_q(fcvport))) {
+		printk(KERN_ERR
+		       "ERROR: FC VN_Port '%d' attempted to queue work, "
+		       "when no workqueue created.\n", fcvport->id);
+		dump_stack();
+
+		return -EINVAL;
+	}
+
+	return queue_work(fcvport_work_q(fcvport), work);
+}
+EXPORT_SYMBOL(fc_queue_work);
+
+/**
+ * fc_flush_work - Flush a fcpinit's workqueue.
+ * @shost:	Pointer to Scsi_Host bound to fcpinit.
+ */
+void fc_flush_work(struct fc_fcvport *fcvport)
+{
+	if (!fcvport_work_q(fcvport)) {
+		printk(KERN_ERR
+		       "ERROR: FC VN_Port '%d' attempted to flush work, "
+		       "when no workqueue created.\n", fcvport->id);
+		dump_stack();
+		return;
+	}
+
+	flush_workqueue(fcvport_work_q(fcvport));
+}
+
+/**
+ * fc_queue_devloss_work - Schedule work for the fcpinit devloss workqueue.
+ * @shost:      Pointer to Scsi_Host bound to fcpinit.
+ * @work:       Work to queue for execution.
+ * @delay:      jiffies to delay the work queuing
+ *
+ * Return value:
+ *      1 on success / 0 already queued / < 0 for error
+ */
+int fc_queue_devloss_work(struct fc_fcvport *fcvport, struct delayed_work *work,
+			  unsigned long delay)
+{
+	if (unlikely(!fcvport_devloss_work_q(fcvport))) {
+		printk(KERN_ERR
+		       "ERROR: FC VN_Port '%d' attempted to queue work, "
+		       "when no workqueue created.\n", fcvport->id);
+		dump_stack();
+
+		return -EINVAL;
+	}
+
+	return queue_delayed_work(fcvport_devloss_work_q(fcvport), work, delay);
+}
+
+/**
+ * fc_flush_devloss - Flush a fcpinit's devloss workqueue.
+ * @shost:	Pointer to Scsi_Host bound to fcpinit.
+ */
+void fc_flush_devloss(struct fc_fcvport *fcvport)
+{
+	if (!fcvport_devloss_work_q(fcvport)) {
+		printk(KERN_ERR
+		       "ERROR: FC VN_Port '%d' attempted to flush work, "
+		       "when no workqueue created.\n", fcvport->id);
+		dump_stack();
+		return;
+	}
+
+	flush_workqueue(fcvport_devloss_work_q(fcvport));
+}
+
+static void fc_fcvport_release(struct device *dev)
+{
+	struct fc_fcvport *fcvport = dev_to_fcvport(dev);
+	/*
+	 * TODO: Remove this memset. It's helpful when checking
+	 * reference counting becuase it forces NULL pointer
+	 * exceptions for things that are incorrectly using the
+	 * object that's about to be free'd.
+	 */
+	memset(fcvport, 0, sizeof(*fcvport));
+	kfree(fcvport);
+}
+
+void fc_fcvport_del(struct fc_fcvport *fcvport)
+{
+	struct fc_rport *rport = NULL, *next_rport = NULL;
+	struct workqueue_struct *work_q;
+	unsigned long flags;
+
+	/*
+	 * TODO: fc_vport_terminate checks the vport->flags to
+	 * determine if the vport is being created, deleted, etc...
+	 * That stuff does need to be checked before calling this
+	 * routine in case the user spams sysfs but does this
+	 * routine not care about the vport->flags at all?
+	 */
+
+	spin_lock_irqsave(&fcvport->lock, flags);
+
+	/* Remove any remote ports */
+	list_for_each_entry_safe(rport, next_rport,
+				 &fcvport->rports, peers) {
+		list_del(&rport->peers);
+		rport->port_state = FC_PORTSTATE_DELETED;
+		fc_queue_work(fcvport, &rport->rport_delete_work);
+	}
+
+	list_for_each_entry_safe(rport, next_rport,
+				 &fcvport->rport_bindings, peers) {
+		list_del(&rport->peers);
+		rport->port_state = FC_PORTSTATE_DELETED;
+		fc_queue_work(fcvport, &rport->rport_delete_work);
+	}
+
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+
+	/* flush all stgt delete, and rport delete work items, then kill it  */
+	if (fcvport->work_q) {
+		work_q = fcvport->work_q;
+		fcvport->work_q = NULL;
+		destroy_workqueue(work_q);
+	}
+
+	/* flush all devloss work items, then kill it  */
+	if (fcvport->devloss_work_q) {
+		work_q = fcvport->devloss_work_q;
+		fcvport->devloss_work_q = NULL;
+		destroy_workqueue(work_q);
+	}
+
+	fc4->fc4_init_del(fcvport);
+
+	put_device(&fcvport->dev); /* For rports list */
+	device_del(&fcvport->dev);
+	put_device(fcvport->dev.parent);
+}
+EXPORT_SYMBOL(fc_fcvport_del);
+
+void fc_fcvport_free(struct fc_fcvport *fcvport)
+{
+	put_device(&fcvport->dev); /* self-reference */
+}
+EXPORT_SYMBOL(fc_fcvport_free);
+
+struct class fcvport_class = {
+	.name = "fcvport",
+	.dev_release = fc_fcvport_release,
+};
+EXPORT_SYMBOL(fcvport_class);
+
+struct fc_fcvport *fc_fcvport_alloc(struct fc_fcfabric *fcfabric,
+				    struct fc_vport_identifiers *ids,
+				    struct fcvport_function_template *f,
+				    int priv_size,
+				    struct fc_fcport *fcport)
+{
+	struct fc_fcvport *fcvport, *fcnport;
+	unsigned long flags;
+	int error = 0;
+
+	fcvport = kzalloc(sizeof(struct fc_fcvport) + priv_size, GFP_KERNEL);
+	if (!fcvport)
+		return NULL;
+
+	fcvport->vport_state = FC_VPORT_UNKNOWN;
+	fcvport->vport_last_state = FC_VPORT_UNKNOWN;
+	fcvport->node_name = ids->node_name;
+	fcvport->port_name = ids->port_name;
+	fcvport->roles = ids->roles;
+	fcvport->vport_type = ids->vport_type;
+	fcvport->id = atomic_inc_return(&fcvport_num) - 1;
+	fcvport->port_type = FC_PORTTYPE_UNKNOWN;
+	fcvport->tgtid_bind_type = FC_TGTID_BIND_BY_WWPN;
+
+	INIT_LIST_HEAD(&fcvport->rports);
+	INIT_LIST_HEAD(&fcvport->rport_bindings);
+	fcvport->next_rport_number = 0;
+
+	INIT_WORK(&fcvport->vport_delete_work, fc_vport_sched_delete);
+	spin_lock_init(&fcvport->lock);
+	device_initialize(&fcvport->dev); /* Takes a self-reference */
+
+	fcvport->dev.class = &fcvport_class;
+	fcvport->f = f;
+
+	dev_set_name(&fcvport->dev, "fcvport_%d", fcvport->id);
+	get_device(&fcvport->dev); /* For rports list */
+
+	/*
+	 * rport management initialization
+	 */
+	fcvport->tgtid_bind_type = FC_TGTID_BIND_BY_WWPN;
+
+	snprintf(fcvport->work_q_name, sizeof(fcvport->work_q_name),
+		 "fc_wq_%d", fcvport->id);
+	fcvport->work_q = create_singlethread_workqueue(
+		fcvport->work_q_name);
+	if (!fcvport->work_q)
+		return NULL;
+
+	snprintf(fcvport->devloss_work_q_name,
+		 sizeof(fcvport->devloss_work_q_name),
+		 "fc_dl_%d", fcvport->id);
+	fcvport->devloss_work_q = create_singlethread_workqueue(
+		fcvport->devloss_work_q_name);
+	if (!fcvport->devloss_work_q) {
+		destroy_workqueue(fcvport->work_q);
+		fcvport->work_q = NULL;
+		return NULL;
+	}
+
+	spin_lock_irqsave(&fcvport->lock, flags);
+	fcvport->flags &= ~FC_VPORT_CREATING;
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+
+	/*
+	 * TODO: This is ugly. We're doing different cases for N_Ports
+	 * and VN_Ports since there's no fcfabric passed in for N_Ports.
+	 */
+	if (fcfabric) {
+		fcnport = fc_fcfabric_find_nport(fcfabric);
+		if (!fcnport)
+			return NULL;
+
+		error = fcfabric->f->vport_create(fcvport_priv(fcnport),
+						  fcvport, 0);
+		if (error)
+			goto delete_vport_all;
+	}
+
+	/*
+	 * TODO: We probably want to re-add a dev_printk here
+	 dev_printk(KERN_NOTICE, pdev,
+	 "%s created via shost%d channel %d\n", dev_name(dev),
+	 shost->host_no, channel);
+	*/
+	return fcvport;
+
+delete_vport_all:
+/*
+ * TODO: Double check this routines error handling,
+ * we probably need to clean up more here.
+ */
+	kfree(fcvport);
+	return NULL;
+}
+EXPORT_SYMBOL(fc_fcvport_alloc);
+
+int fc_fcvport_add(struct fc_fcvport *fcvport,
+		   struct fc_fcfabric *fcfabric)
+{
+	struct fc_fcport *fcport = fcfabric_to_fcport(fcfabric);
+	int count = 0;
+	int error = 0;
+
+	fcvport->dev.parent = get_device(&fcfabric->dev);
+
+	/*
+	 * TODO: Should the device be free'd if the
+	 * device_add() fails?
+	 */
+	error = device_add(&fcvport->dev);
+	if (error)
+		return error;
+
+	/* TODO: Check for failure */
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(fcvport, port_id);
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(fcvport, node_name);
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(fcvport, port_name);
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(fcvport, port_type);
+
+	/*
+	 * Setup Virtual Port Attributes.
+	 */
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(fcvport, vport_state);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(fcvport, vport_last_state);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(fcvport, roles);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(fcvport, vport_type);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(fcvport, symbolic_name);
+
+	FC_SETUP_ALWAYS_ATTRIBUTE_RW(fcvport, tgtid_bind_type);
+
+	if (fc_fcvport_is_nport(fcvport))
+		fc_fcfabric_add_npiv(fcfabric);
+
+	if (fcvport->f->issue_fcvport_lip)
+		FC_SETUP_ALWAYS_ATTRIBUTE_RW(fcvport, issue_lip);
+
+	BUG_ON(count > FCVPORT_NUM_ATTRS);
+	FC_CREATE_ATTRIBUTES(fcvport, count, 0);
+
+	if (error || count != 0)
+		return error;
+
+	fc4->fc4_init_add(fcvport, fcport->fc4_f);
+
+	return 0;
+}
+EXPORT_SYMBOL(fc_fcvport_add);
+
+int fc_fcvport_is_nport(struct fc_fcvport *fcvport)
+{
+	if (fcvport_port_type(fcvport) == FC_PORTTYPE_NPORT)
+		return 1;
+	return 0;
+}
+EXPORT_SYMBOL(fc_fcvport_is_nport);
+
+/**
+ * fc_vport_sched_delete - workq-based delete request for a vport
+ * @work:	vport to be deleted.
+ */
+static void
+fc_vport_sched_delete(struct work_struct *work)
+{
+	struct fc_fcvport *vport =
+		container_of(work, struct fc_fcvport, vport_delete_work);
+	struct fc_fcfabric *fcfabric = fcvport_to_fcfabric(vport);
+	int stat;
+
+	stat = fc_vport_terminate(fcfabric, vport);
+	if (stat)
+		dev_printk(KERN_ERR, vport->dev.parent,
+			   "%s: %s could not be deleted created via "
+			   "fcfabric %d channel %d - error %d\n", __func__,
+			   dev_name(&vport->dev), fcfabric->id,
+			   vport->channel, stat);
+}
diff --git a/include/fc/fc.h b/include/fc/fc.h
new file mode 100644
index 0000000..2487262
--- /dev/null
+++ b/include/fc/fc.h
@@ -0,0 +1,843 @@
+/*
+ * Copyright(c) 2010 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.
+ */
+
+#ifndef _FC_H_
+#define _FC_H_
+
+#include <linux/device.h>
+#include <linux/sched.h>
+
+struct fc_fcport;
+struct fc_fcfabric;
+struct fc_fcvport;
+struct fc_rport;
+
+struct fcvport_function_template;
+
+/*
+ * FC Port definitions - Following FC HBAAPI guidelines
+ *
+ * Note: Not all binary values for the different fields match HBAAPI.
+ *  Instead, we use densely packed ordinal values or enums.
+ *  We get away with this as we never present the actual binary values
+ *  externally. For sysfs, we always present the string that describes
+ *  the value. Thus, an admin doesn't need a magic HBAAPI decoder ring
+ *  to understand the values. The HBAAPI user-space library is free to
+ *  convert the strings into the HBAAPI-specified binary values.
+ *
+ * Note: Not all HBAAPI-defined values are contained in the definitions
+ *  below. Those not appropriate to an fc_host (e.g. FCP initiator) have
+ *  been removed.
+ */
+
+/*
+ * fc_port_state: If you alter this, you also need to alter scsi_transport_fc.c
+ * (for the ascii descriptions).
+ */
+enum fc_port_state {
+	FC_PORTSTATE_UNKNOWN,
+	FC_PORTSTATE_NOTPRESENT,
+	FC_PORTSTATE_ONLINE,
+	FC_PORTSTATE_OFFLINE,		/* User has taken Port Offline */
+	FC_PORTSTATE_BLOCKED,
+	FC_PORTSTATE_BYPASSED,
+	FC_PORTSTATE_DIAGNOSTICS,
+	FC_PORTSTATE_LINKDOWN,
+	FC_PORTSTATE_ERROR,
+	FC_PORTSTATE_LOOPBACK,
+	FC_PORTSTATE_DELETED,
+};
+/* Names are in fcsysfs.c */
+#define FC_PORTSTATE_MAX_NAMELEN	20
+
+/*
+ * fc_port_type: If you alter this, you also need to alter scsi_transport_fc.c
+ * (for the ascii descriptions).
+ */
+enum fc_port_type {
+	FC_PORTTYPE_UNKNOWN,
+	FC_PORTTYPE_OTHER,
+	FC_PORTTYPE_NOTPRESENT,
+	FC_PORTTYPE_NPORT,		/* Attached to FPort */
+	FC_PORTTYPE_NLPORT,		/* (Public) Loop w/ FLPort */
+	FC_PORTTYPE_LPORT,		/* (Private) Loop w/o FLPort */
+	FC_PORTTYPE_PTP,		/* Point to Point w/ another NPort */
+	FC_PORTTYPE_NPIV,		/* VPORT based on NPIV */
+};
+
+/*
+ * FC Classes of Service
+ * Note: values are not enumerated, as they can be "or'd" together
+ * for reporting (e.g. report supported_classes). If you alter this list,
+ * you also need to alter scsi_transport_fc.c (for the ascii descriptions).
+ */
+#define FC_COS_UNSPECIFIED		0
+#define FC_COS_CLASS1			2
+#define FC_COS_CLASS2			4
+#define FC_COS_CLASS3			8
+#define FC_COS_CLASS4			0x10
+#define FC_COS_CLASS6			0x40
+
+/*
+ * FC Port Speeds
+ * Note: values are not enumerated, as they can be "or'd" together
+ * for reporting (e.g. report supported_speeds). If you alter this list,
+ * you also need to alter scsi_transport_fc.c (for the ascii descriptions).
+ */
+#define FC_PORTSPEED_UNKNOWN		0 /* Unknown - transceiver
+					     incapable of reporting */
+#define FC_PORTSPEED_1GBIT		1
+#define FC_PORTSPEED_2GBIT		2
+#define FC_PORTSPEED_4GBIT		4
+#define FC_PORTSPEED_10GBIT		8
+#define FC_PORTSPEED_8GBIT		0x10
+#define FC_PORTSPEED_16GBIT		0x20
+#define FC_PORTSPEED_NOT_NEGOTIATED	(1 << 15) /* Speed not established */
+
+/*
+ * fc_tgtid_binding_type: If you alter this, you also need to alter
+ * scsi_transport_fc.c (for the ascii descriptions).
+ */
+enum fc_tgtid_binding_type  {
+	FC_TGTID_BIND_NONE,
+	FC_TGTID_BIND_BY_WWPN,
+	FC_TGTID_BIND_BY_WWNN,
+	FC_TGTID_BIND_BY_ID,
+};
+
+/*
+ * FC Port Roles
+ * Note: values are not enumerated, as they can be "or'd" together
+ * for reporting (e.g. report roles). If you alter this list,
+ * you also need to alter scsi_transport_fc.c (for the ascii descriptions).
+ */
+#define FC_PORT_ROLE_UNKNOWN			0x00
+#define FC_PORT_ROLE_FCP_TARGET			0x01
+#define FC_PORT_ROLE_FCP_INITIATOR		0x02
+#define FC_PORT_ROLE_IP_PORT			0x04
+
+/* The following are for compatibility */
+#define FC_RPORT_ROLE_UNKNOWN			FC_PORT_ROLE_UNKNOWN
+#define FC_RPORT_ROLE_FCP_TARGET		FC_PORT_ROLE_FCP_TARGET
+#define FC_RPORT_ROLE_FCP_INITIATOR		FC_PORT_ROLE_FCP_INITIATOR
+#define FC_RPORT_ROLE_IP_PORT			FC_PORT_ROLE_IP_PORT
+
+/* TODO: Delete this macro, is duplicated in fcvport.c
+
+ Macro for use in defining Virtual Port attributes
+ #define FC_VPORT_ATTR(_name,_mode,_show,_store)	\
+struct device_attribute dev_attr_vport_##_name =	\
+	__ATTR(_name,_mode,_show,_store)
+*/
+
+/*
+ * fc_vport_identifiers: This set of data contains all elements
+ * to uniquely identify and instantiate a FC virtual port.
+ *
+ * Notes:
+ *   symbolic_name: The driver is to append the symbolic_name string data
+ *      to the symbolic_node_name data that it generates by default.
+ *      the resulting combination should then be registered with the switch.
+ *      It is expected that things like Xen may stuff a VM title into
+ *      this field.
+ */
+#define FC_VPORT_SYMBOLIC_NAMELEN		64
+struct fc_vport_identifiers {
+	u64 node_name;
+	u64 port_name;
+	u32 roles;
+	bool disable;
+	enum fc_port_type vport_type;	/* only FC_PORTTYPE_NPIV allowed */
+	char symbolic_name[FC_VPORT_SYMBOLIC_NAMELEN];
+};
+
+/* bit field values for struct fc_vport "flags" field: */
+#define FC_VPORT_CREATING		0x01
+#define FC_VPORT_DELETING		0x02
+#define FC_VPORT_DELETED		0x04
+#define FC_VPORT_DEL			0x06	/* Any DELETE state */
+
+/* Error return codes for vport_create() callback */
+#define VPCERR_UNSUPPORTED		-ENOSYS		/* no driver/adapter
+							   support */
+#define VPCERR_BAD_WWN			-ENOTUNIQ	/* driver validation
+							   of WWNs failed */
+#define VPCERR_NO_FABRIC_SUPP		-EOPNOTSUPP	/* Fabric connection
+							   is loop or the
+							   Fabric Port does
+							   not support NPIV */
+
+/*
+ * fc_rport_identifiers: This set of data contains all elements
+ * to uniquely identify a remote FC port. The driver uses this data
+ * to report the existence of a remote FC port in the topology. Internally,
+ * the transport uses this data for attributes and to manage consistent
+ * target id bindings.
+ */
+struct fc_rport_identifiers {
+	u64 node_name;
+	u64 port_name;
+	u32 port_id;
+	u32 roles;
+};
+
+#define fc_enum_name_match(title, table_type, table)			\
+int get_fc_##title##_match(const char *table_key,		\
+			   enum table_type *value)			\
+{									\
+	int i;								\
+									\
+	for (i = 0; i < ARRAY_SIZE(table); i++) {			\
+		if (strncmp(table_key, table[i].name,			\
+				table[i].matchlen) == 0) {		\
+			*value = table[i].value;			\
+			return 0; /* success */				\
+		}							\
+	}								\
+	return 1; /* failure */						\
+}
+
+#define fc_enum_name_search(title, table_type, table)			\
+const char *get_fc_##title##_name(enum table_type table_key)	\
+{									\
+	int i;								\
+	char *name = NULL;						\
+									\
+	for (i = 0; i < ARRAY_SIZE(table); i++) {			\
+		if (table[i].value == table_key) {			\
+			name = table[i].name;				\
+			break;						\
+		}							\
+	}								\
+	return name;							\
+}
+
+/* Macro for use in defining Remote Port attributes */
+#define FC_RPORT_ATTR(_name, _mode, _show, _store)				\
+struct device_attribute dev_attr_rport_##_name = 	\
+	__ATTR(_name, _mode, _show, _store)
+
+
+/*
+ * FC Local Port (Host) Attributes
+ *
+ * Attributes are based on HBAAPI V2.0 definitions.
+ * Note: OSDeviceName is determined by user-space library
+ *
+ * Fixed attributes are not expected to change. The driver is
+ * expected to set these values after successfully calling scsi_add_host().
+ * The transport fully manages all get functions w/o driver interaction.
+ *
+ * Dynamic attributes are expected to change. The driver participates
+ * in all get/set operations via functions provided by the driver.
+ *
+ * Private attributes are transport-managed values. They are fully
+ * managed by the transport w/o driver interaction.
+ */
+
+#define FC_FC4_LIST_SIZE		32
+#define FC_SYMBOLIC_NAME_SIZE          256
+
+u32 fc_get_event_number(void);
+	/* Note: when specifying vendor_id to fcpinit_post_vendor_event()
+	 *   be sure to read the Vendor Type and ID formatting requirements
+	 *   specified in scsi_netlink.h
+	 */
+
+struct _fc_port_types {
+	enum fc_port_type value;
+	char             *name;
+};
+
+struct _fc_port_role_names {
+	u32   value;
+	char *name;
+};
+
+struct _fc_cos_names {
+	u32   value;
+	char *name;
+};
+
+const char *get_fc_port_state_name(enum fc_port_state table_key);
+
+/*
+ * fc_vport_state: If you alter this, you also need to alter
+ * scsi_transport_fc.c (for the ascii descriptions).
+ */
+enum fc_vport_state {
+	FC_VPORT_UNKNOWN,
+	FC_VPORT_ACTIVE,
+	FC_VPORT_DISABLED,
+	FC_VPORT_LINKDOWN,
+	FC_VPORT_INITIALIZING,
+	FC_VPORT_NO_FABRIC_SUPP,
+	FC_VPORT_NO_FABRIC_RSCS,
+	FC_VPORT_FABRIC_LOGOUT,
+	FC_VPORT_FABRIC_REJ_WWN,
+	FC_VPORT_FAILED,
+};
+
+#define FC_SERIAL_NUMBER_SIZE		80
+
+#define dev_to_fcport(d)				\
+	container_of((d), struct fc_fcport, dev)
+
+#define dev_to_fcfabric(d)				\
+	container_of((d), struct fc_fcfabric, dev)
+
+#define dev_to_fcvport(d)				\
+	container_of((d), struct fc_fcvport, dev)
+
+/*
+ * TODO: Double check these maximum attribute defines
+ */
+#define FCPORT_NUM_ATTRS    7
+#define FCVPORT_NUM_ATTRS  12
+#define FCRPORT_NUM_ATTRS   9
+#define FCFABRIC_NUM_ATTRS  5
+
+struct fcport_function_template {
+	void (*get_fcport_speed)(struct fc_fcport *);
+	void (*get_fcport_active_fc4s)(struct fc_fcport *);
+
+	unsigned long show_fcport_maxframe_size:1;
+	unsigned long show_fcport_supported_speeds:1;
+	unsigned long show_fcport_speed:1;
+	unsigned long show_fcport_supported_fc4s:1;
+	unsigned long show_fcport_active_fc4s:1;
+	unsigned long show_fcport_supported_classes:1;
+	unsigned long show_fcport_serial_number:1;
+};
+
+struct fc_fcport {
+	u32              id;
+	struct device    dev;
+	struct fcport_function_template *f;
+	void *fc4_f;
+	struct device_attribute attrs[FCPORT_NUM_ATTRS];
+
+	/* Fixed Attributes */
+	u8   supported_fc4s[FC_FC4_LIST_SIZE];
+	u32  maxframe_size;
+	u32  supported_classes;
+	char serial_number[FC_SERIAL_NUMBER_SIZE];
+
+	/* Dynamic Attributes*/
+	u8 active_fc4s[FC_FC4_LIST_SIZE];
+
+	/*
+	 * TODO: For FCoE supported_speeds and speed
+	 * can change on a link event. Previously they
+	 * were listed under the "Fixed Attributes" comment,
+	 * but maybe they should be moved under the
+	 * "Dynamic Attributes" comment. Does this have
+	 * an impact on the functionality?
+	 */
+	u32 supported_speeds;
+	u32 speed;
+};
+#define fcport_maxframe_size(x)			\
+	((x)->maxframe_size)
+#define fcport_supported_speeds(x)		\
+	((x)->supported_speeds)
+#define fcport_speed(x)				\
+	((x)->speed)
+#define fcport_supported_fc4s(x)		\
+	((x)->supported_fc4s)
+#define fcport_active_fc4s(x)			\
+	((x)->active_fc4s)
+#define fcport_supported_classes(x)		\
+	((x)->supported_classes)
+#define fcport_serial_number(x)			\
+	((x)->serial_number)
+
+struct fcfabric_function_template {
+	void (*get_fcfabric_fabric_name)(struct fc_fcfabric *);
+	int  (*vport_create)(void *, struct fc_fcvport *, bool);
+	int  (*vport_disable)(struct fc_fcvport *, bool);
+	int  (*vport_delete)(void *, struct fc_fcvport *);
+
+	unsigned long	show_fcfabric_fabric_name:1;
+
+	/*
+	 * TODO: This seems misplaced, but the vport_create
+	 * code in fcfabric needs it.
+	 */
+	u32 dd_fcvport_size;
+};
+
+struct fc_fcfabric {
+	struct device    dev;
+	struct fcfabric_function_template *f;
+
+	/* Fixed Attributes */
+	u64 fabric_name;
+	u16 max_npiv_vports;
+
+	/* Dynamic Attributes */
+	u16 npiv_vports_inuse;
+
+	/* Internal Data */
+	u32 next_vport_number;
+
+	u32 id;
+
+	/*
+	 * TODO: Can this be moved out of the fabric?
+	 * It is needed for vport_create which orignates
+	 * from the fabric.
+	 */
+	struct fcvport_function_template *fcvport_f;
+
+	int attr_count;
+
+	struct list_head vports;
+
+	/* Replacement for shost->host_lock, protects vports list */
+	spinlock_t              lock;
+
+	struct device_attribute attrs[FCFABRIC_NUM_ATTRS];
+};
+
+#define fcfabric_fabric_name(x)			\
+	((x)->fabric_name)
+#define fcfabric_max_npiv_vports(x)		\
+	((x)->max_npiv_vports)
+#define fcfabric_next_vport_number(x)		\
+	((x)->next_vport_number)
+#define fcfabric_npiv_vports_inuse(x)		\
+	((x)->npiv_vports_inuse)
+#define fcfabric_vports(x)			\
+	((x)->vports)
+#define fcfabric_to_fcport(x)			\
+	dev_to_fcport((x)->dev.parent)
+
+struct fcvport_function_template {
+	void (*get_fcvport_port_id)(struct fc_fcvport *);
+	void (*get_fcvport_symbolic_name)(struct fc_fcvport *);
+	void (*get_fcvport_port_type)(struct fc_fcvport *);
+	void (*set_fcvport_symbolic_name)(struct fc_fcvport *);
+
+	int (*issue_fcvport_lip)(struct fc_fcvport *);
+
+	unsigned long show_fcvport_port_id:1;
+	unsigned long show_fcvport_symbolic_name:1;
+	unsigned long show_fcvport_node_name:1;
+	unsigned long show_fcvport_port_name:1;
+	unsigned long show_fcvport_port_type:1;
+};
+
+/*
+ * FC Virtual Port Attributes
+ *
+ * This structure exists for each FC port is a virtual FC port. Virtual
+ * ports share the physical link with the Physical port. Each virtual
+ * ports has a unique presense on the SAN, and may be instantiated via
+ * NPIV, Virtual Fabrics, or via additional ALPAs. As the vport is a
+ * unique presense, each vport has it's own view of the fabric,
+ * authentication privilege, and priorities.
+ *
+ * A virtual port may support 1 or more FC4 roles. Typically it is a
+ * FCP Initiator. It could be a FCP Target, or exist sole for an IP over FC
+ * roles. FC port attributes for the vport will be reported on any
+ * fc_host class object allocated for an FCP Initiator.
+ *
+ * --
+ *
+ * Fixed attributes are not expected to change. The driver is
+ * expected to set these values after receiving the fc_vport structure
+ * via the vport_create() call from the transport.
+ * The transport fully manages all get functions w/o driver interaction.
+ *
+ * Dynamic attributes are expected to change. The driver participates
+ * in all get/set operations via functions provided by the driver.
+ *
+ * Private attributes are transport-managed values. They are fully
+ * managed by the transport w/o driver interaction.
+ */
+struct fc_fcvport {
+	struct device                    dev;
+	struct fcvport_function_template *f;
+	u32                              id;
+
+	u32                              port_id;
+	char symbolic_name[FC_SYMBOLIC_NAME_SIZE];
+	u64                              node_name;
+	u64                              port_name;
+	enum fc_port_type                port_type;
+
+	struct device_attribute attrs[FCVPORT_NUM_ATTRS];
+
+	/*
+	 * The upper layer fc4 protocol needs a way get
+	 * from a vport to its internal object. Is there
+	 * a better way to do this other than a void *
+	 */
+	void *fc4_priv;
+
+	/* Fixed Attributes */
+
+	/* Dynamic Attributes */
+
+	/* Private (Transport-managed) Attributes */
+	enum fc_vport_state vport_state;
+	enum fc_vport_state vport_last_state;
+	u32 roles;
+	u32 vport_id;		/* Admin Identifier for the vport */
+	enum fc_port_type vport_type;
+
+	/* internal data */
+	unsigned int channel;
+	u32 number;
+	u8 flags;
+
+	/* Private (Transport-managed) Attributes */
+	enum fc_tgtid_binding_type  tgtid_bind_type;
+
+	/* internal data */
+	struct list_head rports;
+	struct list_head rport_bindings;
+	u32 next_rport_number;
+
+	/* work queues for rport state manipulation */
+	char work_q_name[20];
+	struct workqueue_struct *work_q;
+	char devloss_work_q_name[20];
+	struct workqueue_struct *devloss_work_q;
+
+	/*
+	 * Replacement for shost->host_lock, protects vport_state,
+	 * the scsi_target_id (needs to change) and the rport list
+	 */
+	spinlock_t              lock;
+
+	struct list_head peers;
+
+	struct work_struct vport_delete_work;
+
+} __attribute__((aligned(sizeof(unsigned long))));
+
+static inline void *fcvport_priv(const struct fc_fcvport *fcvport)
+{
+	return (void *)(fcvport + 1);
+}
+
+#define fcvport_port_id(x)			\
+	((x)->port_id)
+#define fcvport_symbolic_name(x)		\
+	((x)->symbolic_name)
+#define fcvport_node_name(x)			\
+	((x)->node_name)
+#define fcvport_port_name(x)			\
+	((x)->port_name)
+#define fcvport_port_type(x)			\
+	((x)->port_type)
+#define fcvport_tgtid_bind_type(x)		\
+	((x)->tgtid_bind_type)
+#define fcvport_rports(x)			\
+	((x)->rports)
+#define fcvport_rport_bindings(x)		\
+	((x)->rport_bindings)
+#define fcvport_next_rport_number(x)		\
+	((x)->next_rport_number)
+#define fcvport_work_q_name(x)			\
+	((x)->work_q_name)
+#define fcvport_work_q(x)			\
+	((x)->work_q)
+#define fcvport_devloss_work_q_name(x)		\
+	((x)->devloss_work_q_name)
+#define fcvport_devloss_work_q(x)		\
+	((x)->devloss_work_q)
+#define fcvport_to_fcfabric(x)			\
+	dev_to_fcfabric((x)->dev.parent)
+
+struct fcrport_function_template {
+	void (*get_rport_dev_loss_tmo)(struct fc_rport *);
+	void (*set_rport_dev_loss_tmo)(struct fc_rport *, u32);
+
+	void (*dev_loss_tmo_callbk)(struct fc_rport *);
+	void (*terminate_rport_io)(struct fc_rport *);
+
+	/* remote port fixed attributes */
+	unsigned long	show_rport_maxframe_size:1;
+	unsigned long	show_rport_supported_classes:1;
+	unsigned long   show_rport_dev_loss_tmo:1;
+
+	/* allocation lengths for host-specific data */
+	u32 dd_fcrport_size;
+
+	/*
+	 * target dynamic attributes
+	 * These should all be "1" if the driver uses the remote port
+	 * add/delete functions (so attributes reflect rport values).
+	 *
+	unsigned long	show_starget_node_name:1;
+	unsigned long	show_starget_port_name:1;
+	unsigned long	show_starget_port_id:1;
+	*/
+	unsigned long	disable_target_scan:1;
+};
+
+/*
+ * FC Remote Port Attributes
+ *
+ * This structure exists for each remote FC port that a LLDD notifies
+ * the subsystem of.  A remote FC port may or may not be a SCSI Target,
+ * also be a SCSI initiator, IP endpoint, etc. As such, the remote
+ * port is considered a separate entity, independent of "role" (such
+ * as scsi target).
+ *
+ * --
+ *
+ * Attributes are based on HBAAPI V2.0 definitions. Only those
+ * attributes that are determinable by the local port (aka Host)
+ * are contained.
+ *
+ * Fixed attributes are not expected to change. The driver is
+ * expected to set these values after successfully calling
+ * fc_fcrport_add(). The transport fully manages all get functions
+ * w/o driver interaction.
+ *
+ * Dynamic attributes are expected to change. The driver participates
+ * in all get/set operations via functions provided by the driver.
+ *
+ * Private attributes are transport-managed values. They are fully
+ * managed by the transport w/o driver interaction.
+ */
+
+struct fc_rport {	/* aka fc_starget_attrs */
+	struct fcrport_function_template *f;
+	struct device_attribute attrs[FCRPORT_NUM_ATTRS];
+
+	/* Fixed Attributes */
+	u32 maxframe_size;
+	u32 supported_classes;
+
+	/* Dynamic Attributes */
+	u32 dev_loss_tmo;	/* Remote Port loss timeout in seconds. */
+
+	/* Private (Transport-managed) Attributes */
+	u64 node_name;
+	u64 port_name;
+	u32 port_id;
+	u32 roles;
+	enum fc_port_state port_state;	/* Will only be ONLINE or UNKNOWN */
+	u32 fast_io_fail_tmo;
+
+	/* exported data */
+	void *dd_data;			/* Used for driver-specific storage */
+
+	/* internal data */
+	unsigned int channel;
+	u32 number;
+	u8 flags;
+	struct list_head peers;
+	struct device dev;
+
+	struct delayed_work dev_loss_work;
+	struct delayed_work fail_io_work;
+	struct work_struct rport_delete_work;
+
+	void *fc4_priv;
+} __attribute__((aligned(sizeof(unsigned long))));
+
+#define rport_port_state(x)			\
+	((x)->port_state)
+#define rport_port_id(x)			\
+	((x)->port_id)
+#define rport_node_name(x)			\
+	((x)->node_name)
+#define rport_port_name(x)			\
+	((x)->port_name)
+#define rport_maxframe_size(x)			\
+	((x)->maxframe_size)
+
+
+/* bit field values for struct fc_rport "flags" field: */
+#define FC_RPORT_DEVLOSS_PENDING	0x01
+#define FC_RPORT_SCAN_PENDING		0x02
+#define FC_RPORT_FAST_FAIL_TIMEDOUT	0x04
+#define FC_RPORT_DEVLOSS_CALLBK_DONE	0x08
+
+#define	dev_to_rport(d)				\
+	container_of((d), struct fc_rport, dev)
+
+#define rport_to_fcvport(r)			\
+	dev_to_fcvport((r)->dev.parent)
+
+static inline u64 wwn_to_u64(u8 *wwn)
+{
+	return (u64)wwn[0] << 56 | (u64)wwn[1] << 48 |
+	    (u64)wwn[2] << 40 | (u64)wwn[3] << 32 |
+	    (u64)wwn[4] << 24 | (u64)wwn[5] << 16 |
+	    (u64)wwn[6] <<  8 | (u64)wwn[7];
+}
+
+static inline void u64_to_wwn(u64 inm, u8 *wwn)
+{
+	wwn[0] = (inm >> 56) & 0xff;
+	wwn[1] = (inm >> 48) & 0xff;
+	wwn[2] = (inm >> 40) & 0xff;
+	wwn[3] = (inm >> 32) & 0xff;
+	wwn[4] = (inm >> 24) & 0xff;
+	wwn[5] = (inm >> 16) & 0xff;
+	wwn[6] = (inm >> 8) & 0xff;
+	wwn[7] = inm & 0xff;
+}
+
+/**
+ * fc_vport_set_state() - called to set a vport's state. Saves the old state,
+ *   excepting the transitory states of initializing and sending the ELS
+ *   traffic to instantiate the vport on the link.
+ *
+ * Assumes the driver has surrounded this with the proper locking to ensure
+ * a coherent state change.
+ *
+ * @vport:	virtual port whose state is changing
+ * @new_state:  new state
+ **/
+static inline void
+fc_vport_set_state(struct fc_fcvport *fcvport, enum fc_vport_state new_state)
+{
+	if ((new_state != FC_VPORT_UNKNOWN) &&
+	    (new_state != FC_VPORT_INITIALIZING))
+		fcvport->vport_last_state = fcvport->vport_state;
+	fcvport->vport_state = new_state;
+}
+
+struct fc_fcvport *fc_fcvport_lookup(struct fc_fcfabric *fcfabric,
+				     const u32 id);
+
+struct fc_fcport *fc_fcport_add(struct device *pdev,
+				struct fcport_function_template *,
+				void *fc4_f);
+struct fc_fcfabric *fc_fcfabric_add(struct fc_fcport *fcport,
+				    struct fcfabric_function_template *,
+				    const u64 name);
+struct fc_fcvport *fc_fcvport_alloc(struct fc_fcfabric *fcfabric,
+				    struct fc_vport_identifiers *ids,
+				    struct fcvport_function_template *fcn_tmpl,
+				    int priv_size, struct fc_fcport *fcport);
+struct fc_fcvport *fc_vport_create(struct fc_fcfabric *, int channel,
+				   struct fc_vport_identifiers *);
+int fc_fcvport_add(struct fc_fcvport *fcvport,
+		   struct fc_fcfabric *fcfabric);
+
+void fc_fcport_del(struct fc_fcport *fcport);
+void fc_fcfabric_del(struct fc_fcfabric *fcfabric);
+void fc_fcvport_del(struct fc_fcvport *fcvport);
+void fc_fcvport_free(struct fc_fcvport *fcvport);
+
+int fc_fcvport_is_nport(struct fc_fcvport *fcvport);
+struct fc_fcvport *fc_fcfabric_find_nport(struct fc_fcfabric *fcfabric);
+
+#define FC_DEVICE_ATTR(_prefix, _name, _mode, _show, _store)		\
+	struct device_attribute device_attr_##_prefix##_##_name =	\
+		__ATTR(_name, _mode, _show, _store)
+
+#define FC_CREATE_ATTRIBUTES(_var, count, min)				\
+	while (count > min) {						\
+		error = device_create_file(&_var->dev, &_var->attrs[count-1]); \
+		if (error)						\
+			break;						\
+		count--;						\
+	}
+
+#define FC_SETUP_CONDITIONAL_ATTRIBUTE_RD(_var, field)			\
+	if (_var->f->show_##_var##_##field) {				\
+		_var->attrs[count] = device_attr_##_var##_##field;	\
+		_var->attrs[count].attr.mode = S_IRUGO;			\
+		_var->attrs[count].store = NULL;			\
+		count++;						\
+	}
+
+#define FC_SETUP_CONDITIONAL_ATTRIBUTE_RW(_var, field)			\
+{									\
+	if (_var->f->show_##_var##_##field) {				\
+		_var->attrs[count] = device_attr_##_var##_##field;	\
+		count++;						\
+	}								\
+}
+
+#define FC_SETUP_ALWAYS_ATTRIBUTE_RW(_var, field)			\
+{									\
+	_var->attrs[count] = device_attr_##_var##_##field;		\
+	count++;							\
+}
+
+#define FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(_var, field)			\
+	_var->attrs[count] = device_attr_##_var##_##field;		\
+	_var->attrs[count].attr.mode = S_IRUGO;				\
+	_var->attrs[count].store = NULL;				\
+	count++
+
+#define fc_always_show_function(_obj, field, format_string, sz, cast)	\
+	static ssize_t show_##_obj##_##field(struct device *dev,	\
+					     struct device_attribute *attr, \
+					     char *buf)			\
+{									\
+	struct fc_##_obj *_obj = dev_to_##_obj(dev);			\
+	return snprintf(buf, sz, format_string, cast _obj##_##field(_obj)); \
+}
+
+#define fc_conditional_show_function(_obj, field, format_string, sz, cast) \
+	static ssize_t show_##_obj##_##field(struct device *dev,	\
+					     struct device_attribute *attr, \
+					     char *buf)			\
+{									\
+	struct fc_##_obj *_obj = dev_to_##_obj(dev);			\
+	if (_obj->f->get_##_obj##_##field)				\
+		_obj->f->get_##_obj##_##field(_obj);			\
+	return snprintf(buf, sz, format_string, cast _obj##_##field(_obj)); \
+}
+
+struct fc_rport *fc_fcrport_add(struct fc_fcvport *fcvport,
+				struct fcrport_function_template *f,
+				int channel,
+				struct fc_rport_identifiers *ids);
+void fc_fcrport_del(struct fc_rport *rport);
+void fc_fcrport_rolechg(struct fc_rport *rport, u32 roles);
+
+int fc_queue_work(struct fc_fcvport *fcvport, struct work_struct *work);
+void fc_flush_work(struct fc_fcvport *fcvport);
+
+struct fc4_template {
+	int (*fc4_init_add)(struct fc_fcvport *fcvport,
+			    void *fc4_f);
+	void (*fc4_init_del)(struct fc_fcvport *fcvport);
+	int (*fc4_targ_add)(struct fc_rport *rport,
+			    struct fc_fcvport *fcvport,
+			    int channel);
+	void (*fc4_targ_del)(struct fc_rport *rport);
+
+	void (*fc4_bsg_goose_queue)(struct fc_rport *rport);
+	void (*fc4_targ_block)(struct fc_rport *rport);
+	void (*fc4_targ_unblock)(struct fc_rport *rport);
+	void (*fc4_targ_terminate_io)(struct fc_rport *rport);
+	void (*fc4_starget_delete)(struct fc_rport *rport);
+	void (*fc4_queue_starget_delete)(struct fc_rport *rport);
+	void (*fc4_targ_queue_scan)(struct fc_rport *rport);
+	void (*fc4_init_scsi_flush_work)(struct fc_fcvport *fcvport);
+	int  (*fc4_targ_rolechg)(struct fc_rport *rport, u32 roles);
+	void (*fc4_targ_final_delete)(struct fc_rport *rport);
+};
+
+int register_fc4(struct fc4_template *tmpl);
+void unregister_fc4(void);
+
+#endif /* _FC_H_ */


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

* [RFC PATCH 2/3] scsi_transport_fcp: Create FC/SCSI interaction layer
  2010-05-12 20:20 [RFC PATCH 0/3] FC subsystem update Robert Love
  2010-05-12 20:20 ` [RFC PATCH 1/3] fc: Create FC sybsystem Robert Love
@ 2010-05-12 20:20 ` Robert Love
  2010-05-12 20:20 ` [RFC PATCH 3/3] libfc, libfcoe, fcoe: Make use of FC subsystem Robert Love
  2010-05-13  0:15 ` [RFC PATCH 0/3] FC subsystem update Joe Eykholt
  3 siblings, 0 replies; 8+ messages in thread
From: Robert Love @ 2010-05-12 20:20 UTC (permalink / raw)
  To: linux-scsi; +Cc: james.smart, hare, christof.schmitt, james.bottomley

This patch adds SCSI (initiator) as FC4 type that registers with the FC layer.
This layer bridges the FC subsystem and the SCSI subsystem. When a fcvport is
created in the FC layer a callback is made to scsi_transport_fcp and a fcpinit
and Scsi_Host are allocated and added to sysfs. When a fcrport is created in the
FC layer a callback is made to scsi_transport_fcp and a fcptarg is allocated,
added to sysfs and SCSI is notified to scan.

The devices created by scsi_transport_fcp are:

fcpinit: Object that bridges fcvport and Scsi_Host
	 Attributes: permanent_port_name (misplaced?)
	 	     port_state (misplaced?)
		     system_hostname (misplaced?)

I'm not sure these are in the right place. These attributes seem like they
should be moved to the fcport within the FC subsystem.

fcptarg: Object that bridges fcrports and stargets
	 Attributes: scsi_target_id

The fcptarg is not linked to the scsi targets in sysfs right now.

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

 drivers/scsi/Kconfig              |   10 
 drivers/scsi/Makefile             |    1 
 drivers/scsi/scsi_transport_fcp.c | 1598 +++++++++++++++++++++++++++++++++++++
 include/scsi/scsi_transport_fcp.h |  325 ++++++++
 4 files changed, 1934 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/scsi_transport_fcp.c
 create mode 100644 include/scsi/scsi_transport_fcp.h

diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 75f2336..f0e56ce 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -299,6 +299,16 @@ config SCSI_FC_ATTRS
 	  each attached FiberChannel device to sysfs, say Y.
 	  Otherwise, say N.
 
+config SCSI_FCP_ATTRS
+       tristate "Experimental Fiber Channel Protocol Transport Attributes"
+       depends on SCSI
+       depends on FC
+       select SCSI_NETLINK
+       help
+         If you wish to export transport-specific information about
+	 each attached FiberChannel device to sysfs, say Y.
+	 Otherwise, say N.
+
 config SCSI_FC_TGT_ATTRS
 	bool "SCSI target support for FiberChannel Transport Attributes"
 	depends on SCSI_FC_ATTRS
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 1c7ac49..c68fcfa 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_RAID_ATTRS)	+= raid_class.o
 # --------------------------
 obj-$(CONFIG_SCSI_SPI_ATTRS)	+= scsi_transport_spi.o
 obj-$(CONFIG_SCSI_FC_ATTRS) 	+= scsi_transport_fc.o
+obj-$(CONFIG_SCSI_FCP_ATTRS) 	+= scsi_transport_fcp.o
 obj-$(CONFIG_SCSI_ISCSI_ATTRS)	+= scsi_transport_iscsi.o
 obj-$(CONFIG_SCSI_SAS_ATTRS)	+= scsi_transport_sas.o
 obj-$(CONFIG_SCSI_SAS_LIBSAS)	+= libsas/
diff --git a/drivers/scsi/scsi_transport_fcp.c b/drivers/scsi/scsi_transport_fcp.c
new file mode 100644
index 0000000..f4a0d3f
--- /dev/null
+++ b/drivers/scsi/scsi_transport_fcp.c
@@ -0,0 +1,1598 @@
+/*
+ * Copyright(c) 2010 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fcp.h>
+#include <scsi/scsi_cmnd.h>
+#include <linux/netlink.h>
+#include <net/netlink.h>
+#include <scsi/scsi_netlink_fc.h>
+#include <scsi/scsi_bsg_fc.h>
+#include "scsi_priv.h"
+#include "scsi_transport_fc_internal.h"
+
+/*
+ * Original Author:  Martin Hicks
+ * Revised by:       James Smart
+*/
+MODULE_AUTHOR("Robert Love");
+MODULE_DESCRIPTION("Fibre Channel to SCSI Bridge");
+MODULE_LICENSE("GPL");
+
+static int fc_bsg_hostadd(struct Scsi_Host *);
+static void fc_bsg_remove(struct request_queue *q);
+static int fc_bsg_fcptargadd(struct fc_fcpinit *fcpinit,
+			     struct fc_fcptarg *fcptarg);
+static void fc_bsg_request_handler(struct request_queue *q,
+				   struct Scsi_Host *shost,
+				   struct fc_fcptarg *fcptarg,
+				   struct device *dev);
+static void fc_bsg_softirq_done(struct request *rq);
+static enum blk_eh_timer_return fc_bsg_job_timeout(struct request *req);
+
+
+static void fcp_starget_delete(struct fc_rport *rport);
+static void fcp_starget_delete_work(struct work_struct *work);
+static void fcp_scsi_scan_rport(struct work_struct *work);
+
+/* Convert fcpinit_event_code values to ascii string name */
+static const struct {
+	enum fcpinit_event_code		value;
+	char				*name;
+} fcpinit_event_code_names[] = {
+	{ FCH_EVT_LIP,			"lip" },
+	{ FCH_EVT_LINKUP,		"link_up" },
+	{ FCH_EVT_LINKDOWN,		"link_down" },
+	{ FCH_EVT_LIPRESET,		"lip_reset" },
+	{ FCH_EVT_RSCN,			"rscn" },
+	{ FCH_EVT_ADAPTER_CHANGE,	"adapter_chg" },
+	{ FCH_EVT_PORT_UNKNOWN,		"port_unknown" },
+	{ FCH_EVT_PORT_ONLINE,		"port_online" },
+	{ FCH_EVT_PORT_OFFLINE,		"port_offline" },
+	{ FCH_EVT_PORT_FABRIC,		"port_fabric" },
+	{ FCH_EVT_LINK_UNKNOWN,		"link_unknown" },
+	{ FCH_EVT_VENDOR_UNIQUE,	"vendor_unique" },
+};
+fc_enum_name_search(fcpinit_event_code, fcpinit_event_code,
+		    fcpinit_event_code_names)
+#define FCPINIT_EVENT_CODE_MAX_NAMELEN	30
+
+#define fcpinit_store_function(field)					\
+static ssize_t								\
+store_fcpinit_##field(struct device *dev,				\
+		      struct device_attribute *attr,			\
+		      const char *buf,	size_t count)			\
+{									\
+	struct fc_fcpinit *fcpinit = dev_to_fcpinit(dev);		\
+	int val;							\
+	char *cp;							\
+									\
+	val = simple_strtoul(buf, &cp, 0);				\
+	if (*cp && (*cp != '\n'))					\
+		return -EINVAL;						\
+	fcpinit->f->set_fcpinit_##field(fcpinit, val);			\
+	return count;							\
+}
+
+#define fcpinit_store_str_function(field, slen)				\
+static ssize_t								\
+store_fcpinit_##field(struct device *dev,				\
+		      struct device_attribute *attr,			\
+		      const char *buf, size_t count)			\
+{									\
+	struct fc_fcpinit *fcpinit = dev_to_fcpinit(dev);		\
+	unsigned int cnt=count;						\
+									\
+	/* count may include a LF at end of string */			\
+	if (buf[cnt-1] == '\n')						\
+		cnt--;							\
+	if (cnt > ((slen) - 1))						\
+		return -EINVAL;						\
+	memcpy(fcpinit_##field(fcpinit), buf, cnt);			\
+	fcpinit->f->set_fcpinit_##field(fcpinit);			\
+	return count;							\
+}
+
+#define fcpinit_rd_attr(field, format_string, sz)			\
+	fc_always_show_function(fcpinit, field, format_string, sz, )	\
+	static FC_DEVICE_ATTR(fcpinit, field, S_IRUGO,			\
+			      show_fcpinit_##field, NULL)
+
+#define fcpinit_rd_attr_cast(field, format_string, sz, cast)		\
+	fc_always_show_function(fcpinit, field, format_string, sz, (cast)) \
+	static FC_DEVICE_ATTR(fcpinit, field, S_IRUGO,			\
+			      show_fcpinit_##field, NULL)
+
+#define fcpinit_rw_attr(field, format_string, sz)			\
+	fc_always_show_function(fcpinit, field, format_string, sz, )	\
+	fcpinit_store_function(field)					\
+	static FC_DEVICE_ATTR(fcpinit, field, S_IRUGO | S_IWUSR,	\
+			      show_fcpinit_##field,			\
+			      store_fcpinit_##field)
+
+#define fcpinit_rd_enum_attr(title, maxlen)				\
+static ssize_t								\
+show_fcpinit_##title (struct device *dev,				\
+		      struct device_attribute *attr, char *buf)		\
+{									\
+	struct fc_fcpinit *fcpinit = dev_to_fcpinit(dev);		\
+	const char *name;						\
+	if (fcpinit->f->get_fcpinit_##title)				\
+		fcpinit->f->get_fcpinit_##title(fcpinit);		\
+	name = get_fc_##title##_name(fcpinit_##title(fcpinit));		\
+	if (!name)							\
+		return -EINVAL;						\
+	return snprintf(buf, maxlen, "%s\n", name);			\
+}									\
+static FC_DEVICE_ATTR(fcpinit, title, S_IRUGO, show_fcpinit_##title, NULL)
+
+/* Fixed Host Attributes */
+
+fcpinit_rd_attr_cast(permanent_port_name, "0x%llx\n", 20,
+		     unsigned long long);
+
+/* Dynamic Host Attributes */
+
+fcpinit_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN);
+
+fc_always_show_function(fcpinit, system_hostname, "%s\n",
+			FC_SYMBOLIC_NAME_SIZE + 1, )
+
+fcpinit_store_str_function(system_hostname, FC_SYMBOLIC_NAME_SIZE)
+static FC_DEVICE_ATTR(fcpinit, system_hostname, S_IRUGO | S_IWUSR,
+		      show_fcpinit_system_hostname,
+		      store_fcpinit_system_hostname);
+
+#define fc_fcpinit_rd_attr_cast(field, format_string, sz, cast)		\
+	fc_always_show_function(fcpinit, field, format_string, sz, (cast)) \
+	static FC_DEVICE_ATTR(fcpinit, field, S_IRUGO,			\
+			      show_fcpinit_##field, NULL)
+
+/* Show a given an attribute in the statistics group */
+static ssize_t
+fc_stat_show(const struct device *dev, char *buf, unsigned long offset)
+{
+	struct fc_fcpinit *fcpinit = dev_to_fcpinit(dev);
+	struct fcpinit_statistics *stats;
+	ssize_t ret = -ENOENT;
+
+	if (offset > sizeof(struct fcpinit_statistics) ||
+	    offset % sizeof(u64) != 0)
+		WARN_ON(1);
+
+	if (fcpinit->f->get_fcpinit_stats) {
+		stats = (fcpinit->f->get_fcpinit_stats)(fcpinit);
+		if (stats)
+			ret = snprintf(buf, 20, "0x%llx\n",
+			      (unsigned long long)*(u64 *)(((u8 *) stats) + offset));
+	}
+	return ret;
+}
+
+
+/* generate a read-only statistics attribute */
+#define fcpinit_statistic(name)						\
+static ssize_t show_fcstat_##name(struct device *cd,			\
+				  struct device_attribute *attr,	\
+				  char *buf)				\
+{									\
+	return fc_stat_show(cd, buf,					\
+			    offsetof(struct fcpinit_statistics, name));	\
+}									\
+static FC_DEVICE_ATTR(host, name, S_IRUGO, show_fcstat_##name, NULL)
+
+fcpinit_statistic(seconds_since_last_reset);
+fcpinit_statistic(tx_frames);
+fcpinit_statistic(tx_words);
+fcpinit_statistic(rx_frames);
+fcpinit_statistic(rx_words);
+fcpinit_statistic(lip_count);
+fcpinit_statistic(nos_count);
+fcpinit_statistic(error_frames);
+fcpinit_statistic(dumped_frames);
+fcpinit_statistic(link_failure_count);
+fcpinit_statistic(loss_of_sync_count);
+fcpinit_statistic(loss_of_signal_count);
+fcpinit_statistic(prim_seq_protocol_err_count);
+fcpinit_statistic(invalid_tx_word_count);
+fcpinit_statistic(invalid_crc_count);
+fcpinit_statistic(fcp_input_requests);
+fcpinit_statistic(fcp_output_requests);
+fcpinit_statistic(fcp_control_requests);
+fcpinit_statistic(fcp_input_megabytes);
+fcpinit_statistic(fcp_output_megabytes);
+
+static ssize_t
+fc_reset_statistics(struct device *dev, struct device_attribute *attr,
+		    const char *buf, size_t count)
+{
+	struct fc_fcpinit *fcpinit = dev_to_fcpinit(dev);
+
+	/* ignore any data value written to the attribute */
+	if (fcpinit->f->reset_fcpinit_stats) {
+		fcpinit->f->reset_fcpinit_stats(fcpinit);
+		return count;
+	}
+
+	return -ENOENT;
+}
+static FC_DEVICE_ATTR(host, reset_statistics, S_IWUSR, NULL,
+		      fc_reset_statistics);
+
+static struct attribute *fc_statistics_attrs[] = {
+	&device_attr_host_seconds_since_last_reset.attr,
+	&device_attr_host_tx_frames.attr,
+	&device_attr_host_tx_words.attr,
+	&device_attr_host_rx_frames.attr,
+	&device_attr_host_rx_words.attr,
+	&device_attr_host_lip_count.attr,
+	&device_attr_host_nos_count.attr,
+	&device_attr_host_error_frames.attr,
+	&device_attr_host_dumped_frames.attr,
+	&device_attr_host_link_failure_count.attr,
+	&device_attr_host_loss_of_sync_count.attr,
+	&device_attr_host_loss_of_signal_count.attr,
+	&device_attr_host_prim_seq_protocol_err_count.attr,
+	&device_attr_host_invalid_tx_word_count.attr,
+	&device_attr_host_invalid_crc_count.attr,
+	&device_attr_host_fcp_input_requests.attr,
+	&device_attr_host_fcp_output_requests.attr,
+	&device_attr_host_fcp_control_requests.attr,
+	&device_attr_host_fcp_input_megabytes.attr,
+	&device_attr_host_fcp_output_megabytes.attr,
+	&device_attr_host_reset_statistics.attr,
+	NULL
+};
+
+static struct attribute_group fc_statistics_group = {
+	.name = "statistics",
+	.attrs = fc_statistics_attrs,
+};
+
+
+#define fc_fcptarg_rd_attr(field, format_string, sz)			\
+	fc_always_show_function(fcptarg, field, format_string, sz, )	\
+	static FC_DEVICE_ATTR(fcptarg, field, S_IRUGO,			\
+			      show_fcptarg_##field, NULL)
+
+fc_fcptarg_rd_attr(scsi_target_id, "%d\n", 20);
+
+
+/**
+ * fc_timed_out - FC Transport I/O timeout intercept handler
+ * @scmd:	The SCSI command which timed out
+ *
+ * This routine protects against error handlers getting invoked while a
+ * rport is in a blocked state, typically due to a temporarily loss of
+ * connectivity. If the error handlers are allowed to proceed, requests
+ * to abort i/o, reset the target, etc will likely fail as there is no way
+ * to communicate with the device to perform the requested function. These
+ * failures may result in the midlayer taking the device offline, requiring
+ * manual intervention to restore operation.
+ *
+ * This routine, called whenever an i/o times out, validates the state of
+ * the underlying rport. If the rport is blocked, it returns
+ * EH_RESET_TIMER, which will continue to reschedule the timeout.
+ * Eventually, either the device will return, or devloss_tmo will fire,
+ * and when the timeout then fires, it will be handled normally.
+ * If the rport is not blocked, normal error handling continues.
+ *
+ * Notes:
+ *	This routine assumes no locks are held on entry.
+ */
+static enum blk_eh_timer_return
+fc_timed_out(struct scsi_cmnd *scmd)
+{
+	struct scsi_target *starget = scsi_target(scmd->device);
+	struct fc_fcptarg *fcptarg = starget_to_fcptarg(starget);
+	struct fc_rport *rport = fcptarg->rport;
+
+	if (rport->port_state == FC_PORTSTATE_BLOCKED)
+		return BLK_EH_RESET_TIMER;
+
+	return BLK_EH_NOT_HANDLED;
+}
+
+/*
+ * Called by fc_user_scan to locate an rport on the shost that
+ * matches the channel and target id, and invoke scsi_scan_target()
+ * on the rport.
+ */
+static void
+fc_user_scan_tgt(struct Scsi_Host *shost, uint channel, uint id, uint lun)
+{
+	struct fc_fcpinit *fcpinit = shost_priv(shost);
+	struct fc_fcvport *fcvport = fcpinit_to_fcvport(fcpinit);
+	struct fc_rport *rport;
+	struct fc_fcptarg *fcptarg;
+	unsigned long flags;
+
+	spin_lock_irqsave(&fcvport->lock, flags);
+
+	list_for_each_entry(rport, &fcvport_rports(fcvport), peers) {
+		fcptarg = rport_to_fcptarg(rport);
+
+		if (fcptarg->scsi_target_id == -1)
+			continue;
+
+		if (rport->port_state != FC_PORTSTATE_ONLINE)
+			continue;
+
+		if ((channel == rport->channel) &&
+		    (id == fcptarg->scsi_target_id)) {
+			spin_unlock_irqrestore(&fcvport->lock, flags);
+			scsi_scan_target(&rport->dev, channel, id, lun, 1);
+			return;
+		}
+	}
+
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+}
+
+/*
+ * Called via sysfs scan routines. Necessary, as the FC transport
+ * wants to place all target objects below the rport object. So this
+ * routine must invoke the scsi_scan_target() routine with the rport
+ * object as the parent.
+ */
+static int fc_user_scan(struct Scsi_Host *shost, uint channel,
+			uint id, uint lun)
+{
+	uint chlo, chhi;
+	uint tgtlo, tgthi;
+
+	if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) ||
+	    ((id != SCAN_WILD_CARD) && (id >= shost->max_id)) ||
+	    ((lun != SCAN_WILD_CARD) && (lun > shost->max_lun)))
+		return -EINVAL;
+
+	if (channel == SCAN_WILD_CARD) {
+		chlo = 0;
+		chhi = shost->max_channel + 1;
+	} else {
+		chlo = channel;
+		chhi = channel + 1;
+	}
+
+	if (id == SCAN_WILD_CARD) {
+		tgtlo = 0;
+		tgthi = shost->max_id;
+	} else {
+		tgtlo = id;
+		tgthi = id + 1;
+	}
+
+	for ( ; chlo < chhi; chlo++)
+		for ( ; tgtlo < tgthi; tgtlo++)
+			fc_user_scan_tgt(shost, chlo, tgtlo, lun);
+
+	return 0;
+}
+
+static int fc_tsk_mgmt_response(struct Scsi_Host *shost, u64 nexus, u64 tm_id,
+				int result)
+{
+	/*
+	 * TODO: This seems far too simple, what am I missing?
+	 */
+	return shost->transportt->tsk_mgmt_response(shost, nexus,
+						    tm_id, result);
+}
+
+static int fc_it_nexus_response(struct Scsi_Host *shost, u64 nexus, int result)
+{
+	/*
+	 * TODO: This seems far too simple, what am I missing?
+	 */
+	return shost->transportt->it_nexus_response(shost, nexus, result);
+}
+
+static void fc_fcpinit_release(struct device *dev)
+{
+	struct fc_fcpinit *fcpinit = dev_to_fcpinit(dev);
+	struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+
+	scsi_host_put(shost);
+}
+
+static void fcp_init_del(struct fc_fcvport *fcvport)
+{
+	struct fc_fcpinit *fcpinit = fcvport_to_fcpinit(fcvport);
+	struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+
+	fc_bsg_remove(fcpinit->rqst_q);
+
+	/* flush all scan work items */
+	scsi_flush_work(shost);
+
+	device_del(&fcpinit->dev);
+	put_device(fcpinit->dev.parent);
+
+	/*
+	 * Detach from the scsi-ml. This frees the shost,
+	 * fcpinit and any LLD private data.
+	 */
+	scsi_remove_host(shost);
+
+	put_device(&fcpinit->dev);
+}
+
+static struct scsi_transport_template fcpinit_scsi_transport_template = {
+	/* Use the shost workq for scsi scanning */
+	.create_work_queue = 1,
+
+	.eh_timed_out = fc_timed_out,
+
+	.user_scan = fc_user_scan,
+
+	/* target-mode drivers' functions */
+	.tsk_mgmt_response = fc_tsk_mgmt_response,
+	.it_nexus_response = fc_it_nexus_response,
+};
+
+static int fcp_init_add(struct fc_fcvport *fcvport, void *v)
+{
+	struct fcp_template *f = v;
+	struct Scsi_Host *shost;
+	struct fc_fcpinit *fcpinit;
+	int error = 0;
+	int count = 0;
+
+	int priv_size = sizeof(struct fc_fcpinit) + f->fcpinit_priv_size;
+	shost = scsi_host_alloc(&f->shost_template, priv_size);
+	if (!shost)
+		return -ENOMEM;
+
+	shost->transportt = &fcpinit_scsi_transport_template;
+
+	fcpinit = shost_priv(shost);
+	fcpinit->f = f;
+
+	fcvport->fc4_priv = fcpinit;
+	fcpinit->fcvport = fcvport;
+	fcpinit->shost = shost;
+	fcpinit->next_target_id = 0;
+
+	device_initialize(&fcpinit->dev);
+	fcpinit->dev.release = fc_fcpinit_release;
+	dev_set_name(&fcpinit->dev, "fcpinit_%d", shost->host_no);
+
+	/*
+	 * Set default values easily detected by the midlayer as
+	 * failure cases.  The scsi lldd is responsible for initializing
+	 * all transport attributes to valid values per host.
+	 */
+
+	/*
+	 * TODO: Most of this stuff needs to move to fcvport because
+	 * rports will be attached to the vport not fcpinit
+	 */
+	fcpinit->permanent_port_name = -1;
+	fcpinit->port_state = FC_PORTSTATE_UNKNOWN;
+	memset(fcpinit->system_hostname, 0, sizeof(fcpinit->system_hostname));
+
+	/*
+	 * Need to call back to the LLD with priv memory
+	 */
+	f->fcp_fcpinit_callback(fcpinit);
+
+	fcpinit->dev.parent = get_device(&fcvport->dev);
+
+	error = device_add(&fcpinit->dev);
+	if (error)
+		return error;
+
+	/* add the new host to the SCSI-ml */
+	error = scsi_add_host(shost, &fcpinit->dev);
+	if (error)
+		return error;
+
+	fc_bsg_hostadd(shost);
+	/* ignore any bsg add error - we just can't do sgio */
+
+	/*
+	 * Setup SCSI Host Attributes.
+	 */
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(fcpinit, permanent_port_name);
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(fcpinit, port_state);
+	FC_SETUP_CONDITIONAL_ATTRIBUTE_RW(fcpinit, system_hostname);
+
+	BUG_ON(count > FCPINIT_NUM_ATTRS);
+	FC_CREATE_ATTRIBUTES(fcpinit, count, 0);
+
+	if (error || count != 0) {
+		/*
+		 * TODO: This isn't right, should I be freeing the
+		 * fcpinit if the device attributes couldn't be added?
+		 */
+		return -EINVAL;
+	}
+
+	return 0;
+
+/*
+ * TODO: Where's the error handling for this routine
+ */
+}
+
+/*
+ * TODO: I'm not sure how to handle these events at the moment.
+ * They're scsi-ml interactions so they belong in fcpinit.
+ */
+
+/**
+ * fcpinit_post_event - called to post an even on an fcpinit.
+ * @shost:		host the event occurred on
+ * @event_number:	fc event number obtained from get_fc_event_number()
+ * @event_code:		fcpinit event being posted
+ * @event_data:		32bits of data for the event being posted
+ *
+ * Notes:
+ *	This routine assumes no locks are held on entry.
+ */
+void fcpinit_post_event(struct Scsi_Host *shost, u32 event_number,
+			enum fcpinit_event_code event_code, u32 event_data)
+{
+	struct sk_buff *skb;
+	struct nlmsghdr	*nlh;
+	struct fc_nl_event *event;
+	const char *name;
+	u32 len, skblen;
+	int err;
+
+	if (!scsi_nl_sock) {
+		err = -ENOENT;
+		goto send_fail;
+	}
+
+	len = FC_NL_MSGALIGN(sizeof(*event));
+	skblen = NLMSG_SPACE(len);
+
+	skb = alloc_skb(skblen, GFP_KERNEL);
+	if (!skb) {
+		err = -ENOBUFS;
+		goto send_fail;
+	}
+
+	nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG,
+				skblen - sizeof(*nlh), 0);
+	if (!nlh) {
+		err = -ENOBUFS;
+		goto send_fail_skb;
+	}
+	event = NLMSG_DATA(nlh);
+
+	INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC,
+			 FC_NL_ASYNC_EVENT, len);
+	event->seconds = get_seconds();
+	event->vendor_id = 0;
+	event->host_no = shost->host_no;
+	event->event_datalen = sizeof(u32);	/* bytes */
+	event->event_num = event_number;
+	event->event_code = event_code;
+	event->event_data = event_data;
+
+	nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
+			GFP_KERNEL);
+	return;
+
+send_fail_skb:
+	kfree_skb(skb);
+send_fail:
+	name = get_fc_fcpinit_event_code_name(event_code);
+	printk(KERN_WARNING
+		"%s: Dropped Event : host %d %s data 0x%08x - err %d\n",
+		__func__, shost->host_no,
+		(name) ? name : "<unknown>", event_data, err);
+	return;
+}
+EXPORT_SYMBOL(fcpinit_post_event);
+
+/**
+ * fcpinit_post_vendor_event - called to post a vendor unique event on an fcpinit
+ * @shost:		host the event occurred on
+ * @event_number:	fc event number obtained from get_fc_event_number()
+ * @data_len:		amount, in bytes, of vendor unique data
+ * @data_buf:		pointer to vendor unique data
+ * @vendor_id:          Vendor id
+ *
+ * Notes:
+ *	This routine assumes no locks are held on entry.
+ */
+void fcpinit_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
+			       u32 data_len, char *data_buf, u64 vendor_id)
+{
+	struct sk_buff *skb;
+	struct nlmsghdr	*nlh;
+	struct fc_nl_event *event;
+	u32 len, skblen;
+	int err;
+
+	if (!scsi_nl_sock) {
+		err = -ENOENT;
+		goto send_vendor_fail;
+	}
+
+	len = FC_NL_MSGALIGN(sizeof(*event) + data_len);
+	skblen = NLMSG_SPACE(len);
+
+	skb = alloc_skb(skblen, GFP_KERNEL);
+	if (!skb) {
+		err = -ENOBUFS;
+		goto send_vendor_fail;
+	}
+
+	nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG,
+				skblen - sizeof(*nlh), 0);
+	if (!nlh) {
+		err = -ENOBUFS;
+		goto send_vendor_fail_skb;
+	}
+	event = NLMSG_DATA(nlh);
+
+	INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC,
+				FC_NL_ASYNC_EVENT, len);
+	event->seconds = get_seconds();
+	event->vendor_id = vendor_id;
+	event->host_no = shost->host_no;
+	event->event_datalen = data_len;	/* bytes */
+	event->event_num = event_number;
+	event->event_code = FCH_EVT_VENDOR_UNIQUE;
+	memcpy(&event->event_data, data_buf, data_len);
+
+	nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
+			GFP_KERNEL);
+	return;
+
+send_vendor_fail_skb:
+	kfree_skb(skb);
+send_vendor_fail:
+	printk(KERN_WARNING
+		"%s: Dropped Event : host %d vendor_unique - err %d\n",
+		__func__, shost->host_no, err);
+	return;
+}
+EXPORT_SYMBOL(fcpinit_post_vendor_event);
+
+/**
+ * fc_bsg_host_handler - handler for bsg requests for a fc host
+ * @q:		fc host request queue
+ */
+static void fc_bsg_host_handler(struct request_queue *q)
+{
+	struct Scsi_Host *shost = q->queuedata;
+
+	fc_bsg_request_handler(q, shost, NULL, &shost->shost_gendev);
+}
+
+/**
+ * fc_bsg_hostadd - Create and add the bsg hooks so we can receive requests
+ * @shost:	shost for fcpinit
+ */
+static int fc_bsg_hostadd(struct Scsi_Host *shost)
+{
+	struct fc_fcpinit *fcpinit = shost_priv(shost);
+	struct device *dev = &shost->shost_gendev;
+	struct request_queue *q;
+	int err;
+	char bsg_name[20];
+
+	fcpinit->rqst_q = NULL;
+
+	if (!fcpinit->f->bsg_request)
+		return -ENOTSUPP;
+
+	snprintf(bsg_name, sizeof(bsg_name),
+		 "fcpinit%d", shost->host_no);
+
+	q = __scsi_alloc_queue(shost, fc_bsg_host_handler);
+	if (!q) {
+		printk(KERN_ERR "fcpinit%d: bsg interface failed to "
+				"initialize - no request queue\n",
+				 shost->host_no);
+		return -ENOMEM;
+	}
+
+	q->queuedata = shost;
+	queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q);
+	blk_queue_softirq_done(q, fc_bsg_softirq_done);
+	blk_queue_rq_timed_out(q, fc_bsg_job_timeout);
+	blk_queue_rq_timeout(q, FC_DEFAULT_BSG_TIMEOUT);
+
+	err = bsg_register_queue(q, dev, bsg_name, NULL);
+	if (err) {
+		printk(KERN_ERR "fcpinit%d: bsg interface failed to "
+				"initialize - register queue\n",
+				shost->host_no);
+		blk_cleanup_queue(q);
+		return err;
+	}
+
+	fcpinit->rqst_q = q;
+	return 0;
+}
+
+/**
+ * fc_bsg_remove - Deletes the bsg hooks on fchosts/fcptargs
+ * @q:	the request_queue that is to be torn down.
+ */
+void fc_bsg_remove(struct request_queue *q)
+{
+	if (q) {
+		bsg_unregister_queue(q);
+		blk_cleanup_queue(q);
+	}
+}
+
+/* TARGET CODE */
+
+static void fc_fcptarg_dev_release(struct device *dev)
+{
+	struct fc_fcptarg *fcptarg = dev_to_fcptarg(dev);
+	kfree(fcptarg);
+}
+
+int scsi_is_fcptarg(const struct device *dev)
+{
+	return dev->release == fc_fcptarg_dev_release;
+}
+EXPORT_SYMBOL(scsi_is_fcptarg);
+
+static int fcp_targ_add(struct fc_rport *rport,
+			struct fc_fcvport *fcvport,
+			int channel)
+{
+	struct fc_fcpinit *fcpinit = fcvport_to_fcpinit(fcvport);
+	struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+	struct fc_fcptarg *fcptarg;
+	int error = 0;
+	int count = 0;
+
+	fcptarg = kzalloc(sizeof(struct fc_fcptarg), GFP_KERNEL);
+	if (!fcptarg)
+		return -ENOMEM;
+
+	rport->fc4_priv = fcptarg;
+	fcptarg->rport = rport;
+	fcptarg->fcpinit = fcpinit;
+
+	INIT_WORK(&fcptarg->stgt_delete_work, fcp_starget_delete_work);
+	INIT_WORK(&fcptarg->scan_work, fcp_scsi_scan_rport);
+
+	if (rport->roles & FC_PORT_ROLE_FCP_TARGET)
+		fcptarg->scsi_target_id = fcpinit->next_target_id++;
+	else
+		fcptarg->scsi_target_id = -1;
+
+	/*
+	 * TODO: dev is a workaround device since scsi_scan_target
+	 * wants a device whose parent is a shost. Setting the parent here
+	 * is incorrect since any rports created before fcpinit is allocated
+	 * will have invalid pointers. This workaround only works becuase
+	 * the only rports that will call scsi_scan_target will be created
+	 * after discovery, which is after fcp has been initialized, thus
+	 * ensuring that fcpvport/fcpinit/shost will all exist.
+	 */
+	device_initialize(&fcptarg->dev);  /* takes self reference */
+	fcptarg->dev.parent = get_device(&shost->shost_gendev);
+
+	/*
+	 * TODO: This is terrible. Becuase we're passing the dev up
+	 * into the scsi-ml it is using that device to setup scsi_target and
+	 * scsi_device relationships. The problem is that when the lld (libfc)
+	 * gets the slave_alloc (fc_slave_alloc) callback it tries to use the
+	 * starget_to_rport macro which checks scsi_is_fc_rport.
+	 * scsi_is_fc_rport looks at the device's release function. Since
+	 * we're using this dummy device we need to setup the release
+	 * routine as well.
+	 *
+	 * This is really a mess though. The scsi APIs (scsi_scan_target)
+	 * requires that a device be passed up that has a relationship to the
+	 * shost.
+	 *
+	 * We need to create a fcptarg that has that relationship to scsi
+	 * or we need to change the scsi api to allow us to pass up an
+	 * unrelated device and a shost.
+	 */
+	fcptarg->dev.release = fc_fcptarg_dev_release;
+	dev_set_name(&fcptarg->dev, "fcptarg-%d:%d-%d",
+		     shost->host_no, channel, rport->number);
+
+	/*
+	 * TODO: Do I need to do this since we're not using the
+	 * transport right now?
+	 */
+	transport_setup_device(&fcptarg->dev);
+
+	error = device_add(&fcptarg->dev);
+	if (error) {
+		printk(KERN_ERR "FC Remote Port device_add failed\n");
+		kfree(fcptarg);
+		return error;
+	}
+	transport_add_device(&fcptarg->dev);
+	transport_configure_device(&fcptarg->dev);
+
+	fc_bsg_fcptargadd(fcpinit, fcptarg);
+	/* ignore any bsg add error - we just can't do sgio */
+
+	FC_SETUP_ALWAYS_ATTRIBUTE_RD_NS(fcptarg, scsi_target_id);
+
+	BUG_ON(count > FCPTARG_NUM_ATTRS);
+	FC_CREATE_ATTRIBUTES(fcptarg, count, 0);
+
+	if (error || count != 0) {
+		/*
+		 * TODO: This isn't right, should I be freeing the
+		 * fcptarg if the device attributes couldn't be added?
+		 */
+		return -EINVAL;
+	}
+
+	return 0;
+
+	/*
+delete_fcptarg:
+
+	 * TODO: Fix error handling.
+
+	 put_device(&fcptarg->dev);
+	 put_device(&fcpinit->dev);	            for fcpinit->rport list
+	 put_device(&shost->shost_dev);
+ */
+}
+
+static void fcp_targ_del(struct fc_rport *rport)
+{
+	struct fc_fcptarg *fcptarg = rport_to_fcptarg(rport);
+	struct fc_fcpinit *fcpinit = fcptarg_to_fcpinit(fcptarg);
+	struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+	fc_bsg_remove(fcptarg->rqst_q);
+
+	/* Delete SCSI target and sdevs */
+	if (fcptarg->scsi_target_id != -1)
+		fcp_starget_delete(rport);
+
+	transport_remove_device(&fcptarg->dev);
+	device_del(&fcptarg->dev);
+	transport_destroy_device(&fcptarg->dev);
+	put_device(&shost->shost_gendev);
+	put_device(&fcptarg->dev);
+}
+
+/**
+ * fc_block_scsi_eh - Block SCSI eh thread for blocked fc_rport
+ * @cmnd: SCSI command that scsi_eh is trying to recover
+ *
+ * This routine can be called from a FC LLD scsi_eh callback. It
+ * blocks the scsi_eh thread until the fc_rport leaves the
+ * FC_PORTSTATE_BLOCKED. This is necessary to avoid the scsi_eh
+ * failing recovery actions for blocked rports which would lead to
+ * offlined SCSI devices.
+ */
+static void fc_block_scsi_eh(struct scsi_cmnd *cmnd)
+{
+	struct fc_fcptarg *fcptarg = starget_to_fcptarg(
+		scsi_target(cmnd->device));
+	struct fc_rport *rport = fcptarg->rport;
+	struct fc_fcpinit *fcpinit = fcptarg->fcpinit;
+	struct fc_fcvport *fcvport = fcpinit_to_fcvport(fcpinit);
+	unsigned long flags;
+
+	spin_lock_irqsave(&fcvport->lock, flags);
+	while (rport->port_state == FC_PORTSTATE_BLOCKED) {
+		spin_unlock_irqrestore(&fcvport->lock, flags);
+		msleep(1000);
+		spin_lock_irqsave(&fcvport->lock, flags);
+	}
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+}
+
+/*
+ * BSG support
+ */
+
+/**
+ * fc_destroy_bsgjob - routine to teardown/delete a fc bsg job
+ * @job:	fc_bsg_job that is to be torn down
+ */
+static void fc_destroy_bsgjob(struct fc_bsg_job *job)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&job->job_lock, flags);
+	if (job->ref_cnt) {
+		spin_unlock_irqrestore(&job->job_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&job->job_lock, flags);
+
+	put_device(job->dev);	/* release reference for the request */
+
+	kfree(job->request_payload.sg_list);
+	kfree(job->reply_payload.sg_list);
+	kfree(job);
+}
+
+/**
+ * fc_bsg_jobdone - completion routine for bsg requests that the LLD has
+ *                  completed
+ * @job:	fc_bsg_job that is complete
+ */
+static void fc_bsg_jobdone(struct fc_bsg_job *job)
+{
+	struct request *req = job->req;
+	struct request *rsp = req->next_rq;
+	int err;
+
+	err = job->req->errors = job->reply->result;
+
+	if (err < 0)
+		/* we're only returning the result field in the reply */
+		job->req->sense_len = sizeof(uint32_t);
+	else
+		job->req->sense_len = job->reply_len;
+
+	/* we assume all request payload was transferred, residual == 0 */
+	req->resid_len = 0;
+
+	if (rsp) {
+		WARN_ON(job->reply->reply_payload_rcv_len > rsp->resid_len);
+
+		/* set reply (bidi) residual */
+		rsp->resid_len -= min(job->reply->reply_payload_rcv_len,
+				      rsp->resid_len);
+	}
+	blk_complete_request(req);
+}
+
+/**
+ * fc_bsg_softirq_done - softirq done routine for destroying the bsg requests
+ * @rq:        BSG request that holds the job to be destroyed
+ */
+static void fc_bsg_softirq_done(struct request *rq)
+{
+	struct fc_bsg_job *job = rq->special;
+	unsigned long flags;
+
+	spin_lock_irqsave(&job->job_lock, flags);
+	job->state_flags |= FC_RQST_STATE_DONE;
+	job->ref_cnt--;
+	spin_unlock_irqrestore(&job->job_lock, flags);
+
+	blk_end_request_all(rq, rq->errors);
+	fc_destroy_bsgjob(job);
+}
+
+/**
+ * fc_bsg_job_timeout - handler for when a bsg request timesout
+ * @req:	request that timed out
+ */
+static enum blk_eh_timer_return fc_bsg_job_timeout(struct request *req)
+{
+	struct fc_bsg_job *job = (void *) req->special;
+	struct Scsi_Host *shost = job->shost;
+	struct fc_fcpinit *fcpinit = shost_priv(shost);
+	unsigned long flags;
+	int err = 0, done = 0;
+
+	if (job->rport && job->rport->port_state == FC_PORTSTATE_BLOCKED)
+		return BLK_EH_RESET_TIMER;
+
+	spin_lock_irqsave(&job->job_lock, flags);
+	if (job->state_flags & FC_RQST_STATE_DONE)
+		done = 1;
+	else
+		job->ref_cnt++;
+	spin_unlock_irqrestore(&job->job_lock, flags);
+
+	if (!done && fcpinit->f->bsg_timeout) {
+		/* call LLDD to abort the i/o as it has timed out */
+		err = fcpinit->f->bsg_timeout(job);
+		if (err == -EAGAIN) {
+			job->ref_cnt--;
+			return BLK_EH_RESET_TIMER;
+		} else if (err)
+			printk(KERN_ERR "ERROR: FC BSG request timeout - LLD "
+				"abort failed with status %d\n", err);
+	}
+
+	/* the blk_end_sync_io() doesn't check the error */
+	if (done)
+		return BLK_EH_NOT_HANDLED;
+	else
+		return BLK_EH_HANDLED;
+}
+
+static int fc_bsg_map_buffer(struct fc_bsg_buffer *buf, struct request *req)
+{
+	size_t sz = (sizeof(struct scatterlist) * req->nr_phys_segments);
+
+	BUG_ON(!req->nr_phys_segments);
+
+	buf->sg_list = kzalloc(sz, GFP_KERNEL);
+	if (!buf->sg_list)
+		return -ENOMEM;
+	sg_init_table(buf->sg_list, req->nr_phys_segments);
+	buf->sg_cnt = blk_rq_map_sg(req->q, req, buf->sg_list);
+	buf->payload_len = blk_rq_bytes(req);
+	return 0;
+}
+
+
+/**
+ * fc_req_to_bsgjob - Allocate/create the fc_bsg_job structure for the
+ *                   bsg request
+ * @shost:	SCSI Host corresponding to the bsg object
+ * @rport:	(optional) FC Remote Port corresponding to the bsg object
+ * @req:	BSG request that needs a job structure
+ */
+static int fc_req_to_bsgjob(struct Scsi_Host *shost, struct fc_rport *rport,
+			    struct request *req)
+{
+	struct fc_fcpinit *fcpinit = shost_priv(shost);
+	struct request *rsp = req->next_rq;
+	struct fc_bsg_job *job;
+	int ret;
+
+	BUG_ON(req->special);
+
+	job = kzalloc(sizeof(struct fc_bsg_job) + fcpinit->f->dd_bsg_size,
+		      GFP_KERNEL);
+	if (!job)
+		return -ENOMEM;
+
+	/*
+	 * Note: this is a bit silly.
+	 * The request gets formatted as a SGIO v4 ioctl request, which
+	 * then gets reformatted as a blk request, which then gets
+	 * reformatted as a fc bsg request. And on completion, we have
+	 * to wrap return results such that SGIO v4 thinks it was a scsi
+	 * status.  I hope this was all worth it.
+	 */
+
+	req->special = job;
+	job->shost = shost;
+	job->rport = rport;
+	job->req = req;
+	if (fcpinit->f->dd_bsg_size)
+		job->dd_data = (void *)&job[1];
+	spin_lock_init(&job->job_lock);
+	job->request = (struct fc_bsg_request *)req->cmd;
+	job->request_len = req->cmd_len;
+	job->reply = req->sense;
+	job->reply_len = SCSI_SENSE_BUFFERSIZE;	/* Size of sense buffer
+						 * allocated */
+	if (req->bio) {
+		ret = fc_bsg_map_buffer(&job->request_payload, req);
+		if (ret)
+			goto failjob_rls_job;
+	}
+	if (rsp && rsp->bio) {
+		ret = fc_bsg_map_buffer(&job->reply_payload, rsp);
+		if (ret)
+			goto failjob_rls_rqst_payload;
+	}
+	job->job_done = fc_bsg_jobdone;
+	if (rport)
+		job->dev = &rport->dev;
+	else
+		job->dev = &shost->shost_gendev;
+	get_device(job->dev);		/* take a reference for the request */
+
+	job->ref_cnt = 1;
+
+	return 0;
+
+
+failjob_rls_rqst_payload:
+	kfree(job->request_payload.sg_list);
+failjob_rls_job:
+	kfree(job);
+	return -ENOMEM;
+}
+
+
+enum fc_dispatch_result {
+	FC_DISPATCH_BREAK,	/* on return, q is locked, break from q loop */
+	FC_DISPATCH_LOCKED,	/* on return, q is locked, continue on */
+	FC_DISPATCH_UNLOCKED,	/* on return, q is unlocked, continue on */
+};
+
+
+/**
+ * fc_bsg_host_dispatch - process fc host bsg requests and dispatch to LLDD
+ * @q:		fc host request queue
+ * @shost:	scsi host rport attached to
+ * @job:	bsg job to be processed
+ */
+static enum fc_dispatch_result
+fc_bsg_host_dispatch(struct request_queue *q, struct Scsi_Host *shost,
+		     struct fc_bsg_job *job)
+{
+	struct fc_fcpinit *fcpinit = shost_priv(shost);
+	int cmdlen = sizeof(uint32_t);	/* start with length of msgcode */
+	int ret;
+
+	/* Validate the host command */
+	switch (job->request->msgcode) {
+	case FC_BSG_HST_ADD_RPORT:
+		cmdlen += sizeof(struct fc_bsg_host_add_rport);
+		break;
+
+	case FC_BSG_HST_DEL_RPORT:
+		cmdlen += sizeof(struct fc_bsg_host_del_rport);
+		break;
+
+	case FC_BSG_HST_ELS_NOLOGIN:
+		cmdlen += sizeof(struct fc_bsg_host_els);
+		/* there better be a xmt and rcv payloads */
+		if ((!job->request_payload.payload_len) ||
+		    (!job->reply_payload.payload_len)) {
+			ret = -EINVAL;
+			goto fail_host_msg;
+		}
+		break;
+
+	case FC_BSG_HST_CT:
+		cmdlen += sizeof(struct fc_bsg_host_ct);
+		/* there better be xmt and rcv payloads */
+		if ((!job->request_payload.payload_len) ||
+		    (!job->reply_payload.payload_len)) {
+			ret = -EINVAL;
+			goto fail_host_msg;
+		}
+		break;
+
+	case FC_BSG_HST_VENDOR:
+		cmdlen += sizeof(struct fc_bsg_host_vendor);
+		if ((shost->hostt->vendor_id == 0L) ||
+		    (job->request->rqst_data.h_vendor.vendor_id !=
+			shost->hostt->vendor_id)) {
+			ret = -ESRCH;
+			goto fail_host_msg;
+		}
+		break;
+
+	default:
+		ret = -EBADR;
+		goto fail_host_msg;
+	}
+
+	/* check if we really have all the request data needed */
+	if (job->request_len < cmdlen) {
+		ret = -ENOMSG;
+		goto fail_host_msg;
+	}
+
+	ret = fcpinit->f->bsg_request(job);
+	if (!ret)
+		return FC_DISPATCH_UNLOCKED;
+
+fail_host_msg:
+	/* return the errno failure code as the only status */
+	BUG_ON(job->reply_len < sizeof(uint32_t));
+	job->reply->reply_payload_rcv_len = 0;
+	job->reply->result = ret;
+	job->reply_len = sizeof(uint32_t);
+	fc_bsg_jobdone(job);
+	return FC_DISPATCH_UNLOCKED;
+}
+
+
+/*
+ * fc_bsg_goose_queue - restart rport queue in case it was stopped
+ * @rport:	rport to be restarted
+ */
+static void fc_bsg_goose_queue(struct fc_rport *rport)
+{
+	struct fc_fcptarg *fcptarg = rport_to_fcptarg(rport);
+	int flagset;
+	unsigned long flags;
+
+	if (!fcptarg->rqst_q)
+		return;
+
+	get_device(&fcptarg->dev);
+
+	spin_lock_irqsave(fcptarg->rqst_q->queue_lock, flags);
+	flagset = test_bit(QUEUE_FLAG_REENTER, &fcptarg->rqst_q->queue_flags) &&
+		  !test_bit(QUEUE_FLAG_REENTER, &fcptarg->rqst_q->queue_flags);
+	if (flagset)
+		queue_flag_set(QUEUE_FLAG_REENTER, fcptarg->rqst_q);
+	__blk_run_queue(fcptarg->rqst_q);
+	if (flagset)
+		queue_flag_clear(QUEUE_FLAG_REENTER, fcptarg->rqst_q);
+	spin_unlock_irqrestore(fcptarg->rqst_q->queue_lock, flags);
+
+	put_device(&fcptarg->dev);
+}
+
+/**
+ * fc_bsg_rport_dispatch - process rport bsg requests and dispatch to LLDD
+ * @q:		rport request queue
+ * @shost:	scsi host rport attached to
+ * @rport:	rport request destined to
+ * @job:	bsg job to be processed
+ */
+static enum fc_dispatch_result
+fc_bsg_rport_dispatch(struct request_queue *q, struct Scsi_Host *shost,
+			 struct fc_rport *rport, struct fc_bsg_job *job)
+{
+	struct fc_fcpinit *fcpinit = shost_priv(shost);
+	int cmdlen = sizeof(uint32_t);	/* start with length of msgcode */
+	int ret;
+
+	/* Validate the rport command */
+	switch (job->request->msgcode) {
+	case FC_BSG_RPT_ELS:
+		cmdlen += sizeof(struct fc_bsg_rport_els);
+		goto check_bidi;
+
+	case FC_BSG_RPT_CT:
+		cmdlen += sizeof(struct fc_bsg_rport_ct);
+check_bidi:
+		/* there better be xmt and rcv payloads */
+		if ((!job->request_payload.payload_len) ||
+		    (!job->reply_payload.payload_len)) {
+			ret = -EINVAL;
+			goto fail_rport_msg;
+		}
+		break;
+	default:
+		ret = -EBADR;
+		goto fail_rport_msg;
+	}
+
+	/* check if we really have all the request data needed */
+	if (job->request_len < cmdlen) {
+		ret = -ENOMSG;
+		goto fail_rport_msg;
+	}
+
+	ret = fcpinit->f->bsg_request(job);
+	if (!ret)
+		return FC_DISPATCH_UNLOCKED;
+
+fail_rport_msg:
+	/* return the errno failure code as the only status */
+	BUG_ON(job->reply_len < sizeof(uint32_t));
+	job->reply->reply_payload_rcv_len = 0;
+	job->reply->result = ret;
+	job->reply_len = sizeof(uint32_t);
+	fc_bsg_jobdone(job);
+	return FC_DISPATCH_UNLOCKED;
+}
+
+
+/**
+ * fc_bsg_request_handler - generic handler for bsg requests
+ * @q:		request queue to manage
+ * @shost:	Scsi_Host related to the bsg object
+ * @rport:	FC remote port related to the bsg object (optional)
+ * @dev:	device structure for bsg object
+ */
+static void fc_bsg_request_handler(struct request_queue *q,
+				   struct Scsi_Host *shost,
+				   struct fc_fcptarg *fcptarg,
+				   struct device *dev)
+{
+	struct fc_rport *rport = fcptarg->rport;
+	struct request *req;
+	struct fc_bsg_job *job;
+	enum fc_dispatch_result ret;
+
+	if (!get_device(dev))
+		return;
+
+	while (!blk_queue_plugged(q)) {
+		if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED) &&
+		    !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT))
+			break;
+
+		req = blk_fetch_request(q);
+		if (!req)
+			break;
+
+		if (rport && (rport->port_state != FC_PORTSTATE_ONLINE)) {
+			req->errors = -ENXIO;
+			spin_unlock_irq(q->queue_lock);
+			blk_end_request(req, -ENXIO, blk_rq_bytes(req));
+			spin_lock_irq(q->queue_lock);
+			continue;
+		}
+
+		spin_unlock_irq(q->queue_lock);
+
+		ret = fc_req_to_bsgjob(shost, rport, req);
+		if (ret) {
+			req->errors = ret;
+			blk_end_request(req, ret, blk_rq_bytes(req));
+			spin_lock_irq(q->queue_lock);
+			continue;
+		}
+
+		job = req->special;
+
+		/* check if we have the msgcode value at least */
+		if (job->request_len < sizeof(uint32_t)) {
+			BUG_ON(job->reply_len < sizeof(uint32_t));
+			job->reply->reply_payload_rcv_len = 0;
+			job->reply->result = -ENOMSG;
+			job->reply_len = sizeof(uint32_t);
+			fc_bsg_jobdone(job);
+			spin_lock_irq(q->queue_lock);
+			continue;
+		}
+
+		/* the dispatch routines will unlock the queue_lock */
+		if (rport)
+			ret = fc_bsg_rport_dispatch(q, shost, rport, job);
+		else
+			ret = fc_bsg_host_dispatch(q, shost, job);
+
+		/* did dispatcher hit state that can't process any more */
+		if (ret == FC_DISPATCH_BREAK)
+			break;
+
+		/* did dispatcher had released the lock */
+		if (ret == FC_DISPATCH_UNLOCKED)
+			spin_lock_irq(q->queue_lock);
+	}
+
+	spin_unlock_irq(q->queue_lock);
+	put_device(dev);
+	spin_lock_irq(q->queue_lock);
+}
+
+/**
+ * fc_bsg_rport_handler - handler for bsg requests for a fc rport
+ * @q:		rport request queue
+ */
+static void fc_bsg_fcptarg_handler(struct request_queue *q)
+{
+	struct fc_fcptarg *fcptarg = q->queuedata;
+	struct Scsi_Host *shost = fcptarg_to_shost(fcptarg);
+
+	/*
+	 * TODO: Unknown whether this call works, last argument is suspect.
+	 */
+	fc_bsg_request_handler(q, shost, fcptarg, &fcptarg->dev);
+}
+
+/**
+ * fc_bsg_fcptargadd - Create and add the bsg hooks so we can receive requests
+ * @shost:	shost that rport is attached to
+ * @rport:	rport that the bsg hooks are being attached to
+ */
+static int fc_bsg_fcptargadd(struct fc_fcpinit *fcpinit,
+			     struct fc_fcptarg *fcptarg)
+{
+	struct device *dev = &fcptarg->dev;
+	struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+	struct request_queue *q;
+	int err;
+
+	fcptarg->rqst_q = NULL;
+
+	if (!fcpinit->f->bsg_request)
+		return -ENOTSUPP;
+
+	q = __scsi_alloc_queue(shost, fc_bsg_fcptarg_handler);
+	if (!q) {
+		printk(KERN_ERR "%s: bsg interface failed to "
+		       "initialize - no request queue\n",
+		       dev->kobj.name);
+		return -ENOMEM;
+	}
+
+	q->queuedata = fcptarg;
+	queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q);
+	blk_queue_softirq_done(q, fc_bsg_softirq_done);
+	blk_queue_rq_timed_out(q, fc_bsg_job_timeout);
+	blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT);
+
+	err = bsg_register_queue(q, dev, NULL, NULL);
+	if (err) {
+		printk(KERN_ERR "%s: bsg interface failed to "
+				"initialize - register queue\n",
+				 dev->kobj.name);
+		blk_cleanup_queue(q);
+		return err;
+	}
+
+	fcptarg->rqst_q = q;
+	return 0;
+}
+
+static void fcp_targ_block(struct fc_rport *rport)
+{
+	struct fc_fcptarg *fcptarg = rport_to_fcptarg(rport);
+	scsi_target_block(&fcptarg->dev);
+}
+
+static void fcp_targ_unblock(struct fc_rport *rport)
+{
+	struct fc_fcptarg *fcptarg = rport_to_fcptarg(rport);
+	scsi_target_unblock(&fcptarg->dev);
+}
+
+static void fcp_targ_terminate_io(struct fc_rport *rport)
+{
+	/* Involve the LLDD if possible to terminate all io on the rport. */
+	if (rport->f->terminate_rport_io)
+		rport->f->terminate_rport_io(rport);
+
+	/*
+	 * must unblock to flush queued IO. The caller will have set
+	 * the port_state or flags, so that fc_remote_port_chkready will
+	 * fail IO.
+	 */
+	fcp_targ_unblock(rport);
+}
+
+static void fcp_starget_delete(struct fc_rport *rport)
+{
+	struct fc_fcptarg *fcptarg = rport_to_fcptarg(rport);
+	fcp_targ_terminate_io(rport);
+	scsi_remove_target(&fcptarg->dev);
+}
+
+/**
+ * fc_starget_delete - called to delete the scsi decendents of an rport
+ * @work:	remote port to be operated on.
+ *
+ * Deletes target and all sdevs.
+ */
+static void fcp_starget_delete_work(struct work_struct *work)
+{
+	struct fc_fcptarg *fcptarg =
+		container_of(work, struct fc_fcptarg, stgt_delete_work);
+	struct fc_rport *rport = fcptarg_to_rport(fcptarg);
+	fcp_starget_delete(rport);
+}
+
+static void fcp_queue_starget_delete(struct fc_rport *rport)
+{
+	struct fc_fcptarg *fcptarg = rport_to_fcptarg(rport);
+	struct fc_fcvport *fcvport = rport_to_fcvport(rport);
+	fc_queue_work(fcvport, &fcptarg->stgt_delete_work);
+}
+
+static void fcp_targ_queue_scan(struct fc_rport *rport)
+{
+	struct fc_fcptarg *fcptarg = rport_to_fcptarg(rport);
+	struct Scsi_Host *shost = fcptarg_to_shost(fcptarg);
+	scsi_queue_work(shost, &fcptarg->scan_work);
+}
+
+/**
+ * fcp_scsi_scan_rport - called to perform a scsi scan on a remote port.
+ * @work:	remote port to be scanned.
+ */
+static void fcp_scsi_scan_rport(struct work_struct *work)
+{
+	struct fc_fcptarg *fcptarg =
+		container_of(work, struct fc_fcptarg, scan_work);
+	struct fc_rport *rport;
+	struct fc_fcvport *fcvport;
+	unsigned long flags;
+
+	rport = fcptarg_to_rport(fcptarg);
+	fcvport = rport_to_fcvport(rport);
+
+	if ((rport->port_state == FC_PORTSTATE_ONLINE) &&
+	    (rport->roles & FC_PORT_ROLE_FCP_TARGET) &&
+	    !(rport->f->disable_target_scan)) {
+		scsi_scan_target(&fcptarg->dev,
+				 rport->channel,
+				 fcptarg->scsi_target_id,
+				 SCAN_WILD_CARD, 1);
+	}
+
+	spin_lock_irqsave(&fcvport->lock, flags);
+	rport->flags &= ~FC_RPORT_SCAN_PENDING;
+	spin_unlock_irqrestore(&fcvport->lock, flags);
+}
+
+static void fcp_init_scsi_flush_work(struct fc_fcvport *fcvport)
+{
+	struct fc_fcpinit *fcpinit = fcvport_to_fcpinit(fcvport);
+	struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+	scsi_flush_work(shost);
+}
+
+/*
+ * Return 1 to create, 0 to skip creation
+ */
+static int fcp_targ_rolechg(struct fc_rport *rport, u32 roles)
+{
+	struct fc_fcvport *fcvport = rport_to_fcvport(rport);
+	struct fc_fcpinit *fcpinit = fcvport_to_fcpinit(fcvport);
+	struct fc_fcptarg *fcptarg = rport_to_fcptarg(rport);
+	struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+	int create = 0;
+	int ret;
+
+	if (roles & FC_PORT_ROLE_FCP_TARGET) {
+		if (fcptarg->scsi_target_id == -1) {
+			fcptarg->scsi_target_id = fcpinit->next_target_id++;
+			create = 1;
+		} else if (!(rport->roles & FC_PORT_ROLE_FCP_TARGET))
+			create = 1;
+	} else if (shost->active_mode & MODE_TARGET) {
+		ret = fc_tgt_it_nexus_create(shost, (unsigned long)rport,
+					     (char *)&rport->node_name);
+		if (ret)
+			printk(KERN_ERR "FC Remore Port tgt nexus failed %d\n",
+			       ret);
+	}
+
+	return create;
+}
+
+static void fcp_targ_final_delete(struct fc_rport *rport)
+{
+	struct fc_fcvport *fcvport = rport_to_fcvport(rport);
+	struct fc_fcpinit *fcpinit = fcvport_to_fcpinit(fcvport);
+	struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+
+	if (rport->roles & FC_PORT_ROLE_FCP_INITIATOR &&
+	    shost->active_mode & MODE_TARGET)
+		fc_tgt_it_nexus_destroy(shost, (unsigned long)rport);
+}
+
+static struct fc4_template fcp_fc4_template = {
+	.fc4_init_add = fcp_init_add,
+	.fc4_init_del = fcp_init_del,
+
+	.fc4_targ_add = fcp_targ_add,
+	.fc4_targ_del = fcp_targ_del,
+
+	.fc4_bsg_goose_queue = fc_bsg_goose_queue,
+	.fc4_targ_block = fcp_targ_block,
+	.fc4_targ_unblock = fcp_targ_unblock,
+	.fc4_targ_terminate_io = fcp_targ_terminate_io,
+	.fc4_starget_delete = fcp_starget_delete,
+	.fc4_queue_starget_delete = fcp_queue_starget_delete,
+	.fc4_targ_queue_scan = fcp_targ_queue_scan,
+	.fc4_init_scsi_flush_work = fcp_init_scsi_flush_work,
+	.fc4_targ_rolechg = fcp_targ_rolechg,
+	.fc4_targ_final_delete = fcp_targ_final_delete,
+};
+
+static __init int fcp_transport_init(void)
+{
+	return register_fc4(&fcp_fc4_template);
+}
+
+static void __exit fcp_transport_exit(void)
+{
+	unregister_fc4();
+}
+
+/* Original Author:  Martin Hicks */
+/* Major Updates by: James Smart */
+MODULE_AUTHOR("Robert Love");
+MODULE_DESCRIPTION("FCP Transport Attributes");
+MODULE_LICENSE("GPL");
+
+module_init(fcp_transport_init);
+module_exit(fcp_transport_exit);
diff --git a/include/scsi/scsi_transport_fcp.h b/include/scsi/scsi_transport_fcp.h
new file mode 100644
index 0000000..7b41e73
--- /dev/null
+++ b/include/scsi/scsi_transport_fcp.h
@@ -0,0 +1,325 @@
+/*
+ * Copyright(c) 2010 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.
+ */
+
+#ifndef _FCP_H_
+#define _FCP_H_
+
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+
+#include <scsi/scsi_transport_fcp.h>
+#include <fc/fc.h>
+
+/*
+ * FC SCSI Target Attributes
+ *
+ * The SCSI Target is considered an extention of a remote port (as
+ * a remote port can be more than a SCSI Target). Within the scsi
+ * subsystem, we leave the Target as a separate entity. Doing so
+ * provides backward compatibility with prior FC transport api's,
+ * and lets remote ports be handled entirely within the FC transport
+ * and independently from the scsi subsystem. The drawback is that
+ * some data will be duplicated.
+ */
+
+struct fc_starget_attrs {	/* aka fc_target_attrs */
+	/* Dynamic Attributes */
+	u64 node_name;
+	u64 port_name;
+	u32 port_id;
+};
+
+#define fc_starget_node_name(x) \
+	(((struct fc_starget_attrs *)&(x)->starget_data)->node_name)
+#define fc_starget_port_name(x)	\
+	(((struct fc_starget_attrs *)&(x)->starget_data)->port_name)
+#define fc_starget_port_id(x) \
+	(((struct fc_starget_attrs *)&(x)->starget_data)->port_id)
+
+/*
+ * FC Local Port (Host) Statistics
+ */
+
+/* FC Statistics - Following FC HBAAPI v2.0 guidelines */
+struct fcpinit_statistics {
+	/* port statistics */
+	u64 seconds_since_last_reset;
+	u64 tx_frames;
+	u64 tx_words;
+	u64 rx_frames;
+	u64 rx_words;
+	u64 lip_count;
+	u64 nos_count;
+	u64 error_frames;
+	u64 dumped_frames;
+	u64 link_failure_count;
+	u64 loss_of_sync_count;
+	u64 loss_of_signal_count;
+	u64 prim_seq_protocol_err_count;
+	u64 invalid_tx_word_count;
+	u64 invalid_crc_count;
+
+	/* fc4 statistics  (only FCP supported currently) */
+	u64 fcp_input_requests;
+	u64 fcp_output_requests;
+	u64 fcp_control_requests;
+	u64 fcp_input_megabytes;
+	u64 fcp_output_megabytes;
+};
+
+/*
+ * FC Event Codes - Polled and Async, following FC HBAAPI v2.0 guidelines
+ */
+
+/*
+ * fcpinit_event_code: If you alter this, you also need to alter
+ * scsi_transport_fc.c (for the ascii descriptions).
+ */
+enum fcpinit_event_code  {
+	FCH_EVT_LIP			= 0x1,
+	FCH_EVT_LINKUP			= 0x2,
+	FCH_EVT_LINKDOWN		= 0x3,
+	FCH_EVT_LIPRESET		= 0x4,
+	FCH_EVT_RSCN			= 0x5,
+	FCH_EVT_ADAPTER_CHANGE		= 0x103,
+	FCH_EVT_PORT_UNKNOWN		= 0x200,
+	FCH_EVT_PORT_OFFLINE		= 0x201,
+	FCH_EVT_PORT_ONLINE		= 0x202,
+	FCH_EVT_PORT_FABRIC		= 0x204,
+	FCH_EVT_LINK_UNKNOWN		= 0x500,
+	FCH_EVT_VENDOR_UNIQUE		= 0xffff,
+};
+
+#define FC_SYMBOLIC_NAME_SIZE		256
+
+struct fc_bsg_buffer {
+	unsigned int payload_len;
+	int sg_cnt;
+	struct scatterlist *sg_list;
+};
+
+/* Values for fc_bsg_job->state_flags (bitflags) */
+#define FC_RQST_STATE_INPROGRESS	0
+#define FC_RQST_STATE_DONE		1
+
+struct fc_bsg_job {
+	struct Scsi_Host *shost;
+	struct fc_rport *rport;
+	struct device *dev;
+	struct request *req;
+	spinlock_t job_lock;
+	unsigned int state_flags;
+	unsigned int ref_cnt;
+	void (*job_done)(struct fc_bsg_job *);
+
+	struct fc_bsg_request *request;
+	struct fc_bsg_reply *reply;
+	unsigned int request_len;
+	unsigned int reply_len;
+	/*
+	 * On entry : reply_len indicates the buffer size allocated for
+	 * the reply.
+	 *
+	 * Upon completion : the message handler must set reply_len
+	 *  to indicates the size of the reply to be returned to the
+	 *  caller.
+	 */
+
+	/* DMA payloads for the request/response */
+	struct fc_bsg_buffer request_payload;
+	struct fc_bsg_buffer reply_payload;
+
+	void *dd_data;			/* Used for driver-specific storage */
+};
+
+#define FCPTARG_NUM_ATTRS   1
+struct fc_fcptarg {
+	struct device dev;
+	struct device_attribute attrs[FCPTARG_NUM_ATTRS];
+	struct fc_rport *rport;
+	struct fc_fcpinit *fcpinit;
+	struct request_queue *rqst_q;	/* bsg support */
+	struct work_struct stgt_delete_work;
+	struct work_struct scan_work;
+	u32 scsi_target_id;
+};
+
+#define	dev_to_fcptarg(d)					\
+	container_of((d), struct fc_fcptarg, dev)
+#define fcptarg_to_rport(t)			\
+	((t)->rport)
+#define fcptarg_scsi_target_id(x)		\
+	((x)->scsi_target_id)
+
+#define FCPINIT_NUM_ATTRS   4
+struct fc_fcpinit {
+	struct device    dev;
+	struct fcp_template *f;
+	struct device_attribute attrs[FCPINIT_NUM_ATTRS];
+
+	struct Scsi_Host *shost;
+	struct fc_fcvport *fcvport;
+
+	u32 next_target_id;
+
+	/* Fixed Attributes */
+	u64 permanent_port_name;
+
+	/* Dynamic Attributes */
+	enum fc_port_state port_state;
+	char system_hostname[FC_SYMBOLIC_NAME_SIZE];
+
+	/* bsg support */
+	struct request_queue *rqst_q;
+};
+
+#define fcpinit_next_target_id(x)		\
+	((x)->next_target_id)
+
+/* The functions by which the transport class and the driver communicate */
+struct fc_function_template {
+
+	/* target-mode drivers' functions */
+	int     (*tsk_mgmt_response)(struct Scsi_Host *, u64, u64, int);
+	int     (*it_nexus_response)(struct Scsi_Host *, u64, int);
+
+	/*
+	 * The driver sets these to tell the transport class it
+	 * wants the attributes displayed in sysfs.  If the show_ flag
+	 * is not set, the attribute will be private to the transport
+	 * class
+	 */
+};
+
+struct scsi_transport_template *fc_attach_transport(
+			struct fc_function_template *);
+void fc_release_transport(struct scsi_transport_template *);
+
+#define fcpinit_permanent_port_name(x)		\
+	((x)->permanent_port_name)
+#define fcpinit_port_state(x)			\
+	((x)->port_state)
+#define fcpinit_system_hostname(x)		\
+	((x)->system_hostname)
+
+#define dev_to_fcpinit(d)				\
+	container_of((d), struct fc_fcpinit, dev)
+
+#define fcpinit_to_fcvport(x)			\
+	((x)->fcvport)
+
+void fcpinit_post_event(struct Scsi_Host *shost, u32 event_number,
+			enum fcpinit_event_code event_code, u32 event_data);
+void fcpinit_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
+			       u32 data_len, char *data_buf, u64 vendor_id);
+
+
+int scsi_is_fcptarg(const struct device *dev);
+
+#define starget_to_fcptarg(s)						\
+	scsi_is_fcptarg(s->dev.parent) ? container_of((s->dev.parent), struct fc_fcptarg, dev) : NULL
+
+#define rport_to_fcptarg(r)			\
+	(struct fc_fcptarg *)((r)->fc4_priv)
+
+#define fcptarg_to_shost(t)						\
+	container_of(((t)->dev.parent), struct Scsi_Host, shost_gendev)
+
+#define fcptarg_to_fcpinit(t)			\
+	((t)->fcpinit)
+
+#define fcvport_to_fcpinit(v)			\
+	(struct fc_fcpinit *)((v)->fc4_priv)
+
+#define fcpinit_to_shost(i)			\
+	((i)->shost)
+
+/**
+ * fcpinit_priv() - Return the private data from a local port
+ */
+static inline void *fcpinit_priv(const struct fc_fcpinit *fcpinit)
+{
+	return (void *)(fcpinit + 1);
+}
+
+/**
+ * fc_remote_port_chkready - called to validate the remote port state
+ *   prior to initiating io to the port.
+ *
+ * Returns a scsi result code that can be returned by the LLDD.
+ *
+ * @rport:	remote port to be checked
+ **/
+static inline int
+fc_remote_port_chkready(struct fc_rport *rport)
+{
+	int result;
+
+	switch (rport->port_state) {
+	case FC_PORTSTATE_ONLINE:
+		if (rport->roles & FC_PORT_ROLE_FCP_TARGET)
+			result = 0;
+		else if (rport->flags & FC_RPORT_DEVLOSS_PENDING)
+			result = DID_IMM_RETRY << 16;
+		else
+			result = DID_NO_CONNECT << 16;
+		break;
+	case FC_PORTSTATE_BLOCKED:
+		if (rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)
+			result = DID_TRANSPORT_FAILFAST << 16;
+		else
+			result = DID_IMM_RETRY << 16;
+		break;
+	default:
+		result = DID_NO_CONNECT << 16;
+		break;
+	}
+	return result;
+}
+
+struct scsi_transport_template *fc_attach_transport(
+			struct fc_function_template *);
+void fc_release_transport(struct scsi_transport_template *);
+
+struct fcp_template {
+	int fcpinit_priv_size;
+	void (*fcp_fcpinit_callback)(struct fc_fcpinit *fcpinit);
+	struct scsi_host_template shost_template;
+
+	/* Stuff for fcpinit instance */
+	void (*get_fcpinit_port_state)(struct fc_fcpinit *);
+	void (*set_fcpinit_system_hostname)(struct fc_fcpinit *);
+
+	struct fcpinit_statistics * (*get_fcpinit_stats)(struct fc_fcpinit *);
+	void (*reset_fcpinit_stats)(struct fc_fcpinit *);
+
+	/* bsg support */
+	int	(*bsg_request)(struct fc_bsg_job *);
+	int	(*bsg_timeout)(struct fc_bsg_job *);
+
+	/* host fixed attributes */
+	unsigned long	show_fcpinit_permanent_port_name:1;
+
+	/* host dynamic attributes */
+	unsigned long	show_fcpinit_port_state:1;
+	unsigned long	show_fcpinit_system_hostname:1;
+
+	/* TODO: Is this in the right place? */
+	u32				dd_bsg_size;
+};
+
+#endif /* _FCP_H_ */


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

* [RFC PATCH 3/3] libfc, libfcoe, fcoe: Make use of FC subsystem
  2010-05-12 20:20 [RFC PATCH 0/3] FC subsystem update Robert Love
  2010-05-12 20:20 ` [RFC PATCH 1/3] fc: Create FC sybsystem Robert Love
  2010-05-12 20:20 ` [RFC PATCH 2/3] scsi_transport_fcp: Create FC/SCSI interaction layer Robert Love
@ 2010-05-12 20:20 ` Robert Love
  2010-05-13  0:15 ` [RFC PATCH 0/3] FC subsystem update Joe Eykholt
  3 siblings, 0 replies; 8+ messages in thread
From: Robert Love @ 2010-05-12 20:20 UTC (permalink / raw)
  To: linux-scsi; +Cc: james.smart, hare, christof.schmitt, james.bottomley

This patch makes libfc, libfcoe and fcoe use the FC subsystem instead of
directly interacting with existing FC Transport. The FC devices are allocated,
added to sysfs, deleted from sysfs and freed as follows:

Allocate/Add
* fcport allocated/added when an fcoe interface is created
* fcvport allocated when an fcoe interface is created
* fcfabric allocated/added when a FCF is discovered
* fcvport added when FLOGI ACC is received

Delete/Free
* fcvport deleted when LOGO sent
* fcfabric deleted/freed when FCFs are removed
* fcvport freed when an fcoe interface is created
* fcport deleted/freed when an fcoe interface is created

The allocation scheme for libfc, libfcoe and fcoe's primary data
structures is illustrated here: http://open-fcoe.org/rwlove/libfc_alloc.jpg

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

 drivers/scsi/Kconfig          |    1 
 drivers/scsi/fcoe/fcoe.c      |  619 ++++++++++++++++++++++++-----------------
 drivers/scsi/fcoe/libfcoe.c   |  167 +++++++++--
 drivers/scsi/libfc/fc_disc.c  |    4 
 drivers/scsi/libfc/fc_exch.c  |    2 
 drivers/scsi/libfc/fc_fcp.c   |  130 ++++-----
 drivers/scsi/libfc/fc_libfc.h |   28 +-
 drivers/scsi/libfc/fc_lport.c |  195 +++++++------
 drivers/scsi/libfc/fc_npiv.c  |   44 +--
 drivers/scsi/libfc/fc_rport.c |   15 +
 include/scsi/fc_encode.h      |   34 +-
 include/scsi/libfc.h          |  119 ++++----
 include/scsi/libfcoe.h        |   17 +
 13 files changed, 812 insertions(+), 563 deletions(-)

diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index f0e56ce..54ef2c7 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -682,6 +682,7 @@ config LIBFCOE
 config FCOE
 	tristate "FCoE module"
 	depends on PCI
+	select SCSI_FCP_ATTRS
 	select LIBFCOE
 	---help---
 	  Fibre Channel over Ethernet module
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index ba75a98..02ea681 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -33,14 +33,13 @@
 #include <linux/ctype.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
-#include <scsi/scsi_transport.h>
-#include <scsi/scsi_transport_fc.h>
 #include <net/rtnetlink.h>
 
 #include <scsi/fc/fc_encaps.h>
 #include <scsi/fc/fc_fip.h>
 
 #include <scsi/libfc.h>
+#include <fc/fc.h>
 #include <scsi/fc_frame.h>
 #include <scsi/libfcoe.h>
 
@@ -67,7 +66,7 @@ LIST_HEAD(fcoe_hostlist);
 DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
 
 /* Function Prototypes */
-static int fcoe_reset(struct Scsi_Host *);
+static int fcoe_reset(struct fc_fcvport *);
 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 *);
@@ -116,6 +115,12 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *,
 static void fcoe_recv_frame(struct sk_buff *skb);
 
 static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *);
+static void fcoe_set_symbolic_name(struct fc_lport *);
+static void fcoe_set_node_name(struct fc_lport *);
+static void fcoe_set_port_name(struct fc_lport *);
+static void fcoe_get_vport_ids(struct fc_lport *lport,
+			       struct fc_vport_identifiers *ids);
+
 
 module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
 __MODULE_PARM_TYPE(create, "string");
@@ -140,15 +145,43 @@ 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 int fcoe_vport_destroy(struct fc_vport *);
-static int fcoe_vport_create(struct fc_vport *, bool disabled);
-static int fcoe_vport_disable(struct fc_vport *, bool disable);
-static void fcoe_set_vport_symbolic_name(struct fc_vport *);
+static int fcoe_vport_destroy(void *, struct fc_fcvport *);
+static int fcoe_vport_create(void *, struct fc_fcvport *, bool disabled);
+static int fcoe_vport_disable(struct fc_fcvport *, bool disable);
+static void fcoe_set_vport_symbolic_name(struct fc_fcvport *);
 static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *);
 
+static void fcoe_fcpinit_callback(struct fc_fcpinit *fcpinit);
+
+static struct fcp_template fcoe_fcp_template = {
+	.fcpinit_priv_size = sizeof(struct fc_fcp_internal),
+	.fcp_fcpinit_callback = fcoe_fcpinit_callback,
+
+	.shost_template = {
+		.module = THIS_MODULE,
+		.name = "FCoE Driver",
+		.proc_name = FCOE_NAME,
+		.queuecommand = fc_queuecommand,
+		.eh_abort_handler = fc_eh_abort,
+		.eh_device_reset_handler = fc_eh_device_reset,
+		.eh_host_reset_handler = fc_eh_host_reset,
+		.slave_alloc = fc_slave_alloc,
+		.change_queue_depth = fc_change_queue_depth,
+		.change_queue_type = fc_change_queue_type,
+		.this_id = -1,
+		.cmd_per_lun = 3,
+		.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
+		.use_clustering = ENABLE_CLUSTERING,
+		.sg_tablesize = SG_ALL,
+		.max_sectors = 0xffff,
+	},
+
+	.get_fcpinit_port_state = fc_get_fcpinit_port_state,
+	.show_fcpinit_port_state = 1,
+	.get_fcpinit_stats = fc_get_fcpinit_stats,
+	.bsg_request = fc_lport_bsg_request,
+};
+
 static struct libfc_function_template fcoe_libfc_fcn_templ = {
 	.frame_send = fcoe_xmit,
 	.ddp_setup = fcoe_ddp_setup,
@@ -156,101 +189,138 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = {
 	.elsct_send = fcoe_elsct_send,
 	.get_lesb = fcoe_get_lesb,
 	.lport_set_port_id = fcoe_set_port_id,
+	.set_fcvport_symbolic_name = fcoe_set_symbolic_name,
+	.set_fcvport_node_name = fcoe_set_node_name,
+	.set_fcvport_port_name = fcoe_set_port_name,
+	.get_vport_ids = fcoe_get_vport_ids,
 };
 
-struct fc_function_template fcoe_transport_function = {
-	.show_host_node_name = 1,
-	.show_host_port_name = 1,
-	.show_host_supported_classes = 1,
-	.show_host_supported_fc4s = 1,
-	.show_host_active_fc4s = 1,
-	.show_host_maxframe_size = 1,
-
-	.show_host_port_id = 1,
-	.show_host_supported_speeds = 1,
-	.get_host_speed = fc_get_host_speed,
-	.show_host_speed = 1,
-	.show_host_port_type = 1,
-	.get_host_port_state = fc_get_host_port_state,
-	.show_host_port_state = 1,
-	.show_host_symbolic_name = 1,
-
+struct fcrport_function_template fcoe_fcrport_fcn_tmpl = {
 	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
 	.show_rport_maxframe_size = 1,
 	.show_rport_supported_classes = 1,
-
-	.show_host_fabric_name = 1,
-	.show_starget_node_name = 1,
-	.show_starget_port_name = 1,
-	.show_starget_port_id = 1,
 	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
 	.show_rport_dev_loss_tmo = 1,
-	.get_fc_host_stats = fc_get_host_stats,
-	.issue_fc_host_lip = fcoe_reset,
-
 	.terminate_rport_io = fc_rport_terminate_io,
+};
+
+struct fcport_function_template fcoe_fcport_fcn_tmpl = {
+	.show_fcport_maxframe_size = 1,
+	.show_fcport_supported_speeds = 1,
+	.show_fcport_supported_classes = 1,
+	.show_fcport_speed = 1,
+	.show_fcport_supported_fc4s = 1,
+	.show_fcport_active_fc4s = 1,
+	.show_fcport_serial_number = 1,
+};
 
+struct fcfabric_function_template fcoe_fcfabric_fcn_tmpl = {
 	.vport_create = fcoe_vport_create,
 	.vport_delete = fcoe_vport_destroy,
 	.vport_disable = fcoe_vport_disable,
-	.set_vport_symbolic_name = fcoe_set_vport_symbolic_name,
 
-	.bsg_request = fc_lport_bsg_request,
+	.show_fcfabric_fabric_name = 1,
+	.dd_fcvport_size = (sizeof(struct fc_lport) + sizeof(struct fcoe_port)),
 };
 
-struct fc_function_template fcoe_vport_transport_function = {
-	.show_host_node_name = 1,
-	.show_host_port_name = 1,
-	.show_host_supported_classes = 1,
-	.show_host_supported_fc4s = 1,
-	.show_host_active_fc4s = 1,
-	.show_host_maxframe_size = 1,
-
-	.show_host_port_id = 1,
-	.show_host_supported_speeds = 1,
-	.get_host_speed = fc_get_host_speed,
-	.show_host_speed = 1,
-	.show_host_port_type = 1,
-	.get_host_port_state = fc_get_host_port_state,
-	.show_host_port_state = 1,
-	.show_host_symbolic_name = 1,
+struct fcvport_function_template fcoe_fcvport_fcn_tmpl = {
+	.show_fcvport_port_id = 1,
+	.show_fcvport_symbolic_name = 1,
+	.show_fcvport_node_name = 1,
+	.show_fcvport_port_name = 1,
+	.show_fcvport_port_type = 1,
 
-	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
-	.show_rport_maxframe_size = 1,
-	.show_rport_supported_classes = 1,
+	.issue_fcvport_lip = fcoe_reset,
+	.set_fcvport_symbolic_name = fcoe_set_vport_symbolic_name,
+};
 
-	.show_host_fabric_name = 1,
-	.show_starget_node_name = 1,
-	.show_starget_port_name = 1,
-	.show_starget_port_id = 1,
-	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
-	.show_rport_dev_loss_tmo = 1,
-	.get_fc_host_stats = fc_get_host_stats,
-	.issue_fc_host_lip = fcoe_reset,
+static void _fcoe_get_vport_ids(struct fcoe_interface *fcoe,
+				struct fc_vport_identifiers *ids)
+{
+	int vid = 0;
 
-	.terminate_rport_io = fc_rport_terminate_io,
+	if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN)
+		vid = vlan_dev_vlan_id(fcoe->netdev);
 
-	.bsg_request = fc_lport_bsg_request,
-};
+	ids->node_name = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0);
+	ids->port_name = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 2, vid);
 
-static struct scsi_host_template fcoe_shost_template = {
-	.module = THIS_MODULE,
-	.name = "FCoE Driver",
-	.proc_name = FCOE_NAME,
-	.queuecommand = fc_queuecommand,
-	.eh_abort_handler = fc_eh_abort,
-	.eh_device_reset_handler = fc_eh_device_reset,
-	.eh_host_reset_handler = fc_eh_host_reset,
-	.slave_alloc = fc_slave_alloc,
-	.change_queue_depth = fc_change_queue_depth,
-	.change_queue_type = fc_change_queue_type,
-	.this_id = -1,
-	.cmd_per_lun = 3,
-	.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
-	.use_clustering = ENABLE_CLUSTERING,
-	.sg_tablesize = SG_ALL,
-	.max_sectors = 0xffff,
-};
+	/*
+	 * TODO: This needs to be determined, not hard coded.
+	 */
+	ids->roles = FC_PORT_ROLE_FCP_INITIATOR;
+
+	/*
+	 * TODO: I don't know where these values should come from,
+	 * guess that disable should be 0 and NPIV.
+	 */
+	ids->disable = 0;
+	ids->vport_type = FC_PORTTYPE_NPIV;
+
+	snprintf(ids->symbolic_name, FC_SYMBOLIC_NAME_SIZE,
+		 "%s v%s over %s", FCOE_NAME, FCOE_VERSION,
+		 fcoe->netdev->name);
+}
+
+static void fcoe_get_vport_ids(struct fc_lport *lport,
+			       struct fc_vport_identifiers *ids)
+{
+	struct fcoe_port *port = lport_priv(lport);
+	struct fcoe_interface *fcoe = port->fcoe;
+	_fcoe_get_vport_ids(fcoe, ids);
+}
+
+static void fcoe_set_symbolic_name(struct fc_lport *lport)
+{
+	snprintf(lport->fcvport->symbolic_name, FC_SYMBOLIC_NAME_SIZE,
+		 "%s v%s over %s", FCOE_NAME, FCOE_VERSION,
+		 fcoe_netdev(lport)->name);
+}
+
+static void fcoe_set_node_name(struct fc_lport *lport)
+{
+	struct fcoe_port *port = lport_priv(lport);
+	struct fcoe_interface *fcoe = port->fcoe;
+	int vid = 0;
+
+	/*
+	 * TODO: removed a if(!lport->vport) check, since this routine shoud
+	 * be used for all vports (real or virtual) I think it should be OK
+	 */
+
+	/*
+	 * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN:
+	 * For WWNN, we use NAA 1 w/ bit 27-16 of word 0 as 0.
+	 * For WWPN, we use NAA 2 w/ bit 27-16 of word 0 from VLAN ID
+	 */
+	if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN)
+		vid = vlan_dev_vlan_id(fcoe->netdev);
+	fcvport_node_name(lport->fcvport) =
+		fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0);
+}
+
+static void fcoe_set_port_name(struct fc_lport *lport)
+{
+	struct fcoe_port *port = lport_priv(lport);
+	struct fcoe_interface *fcoe = port->fcoe;
+	int vid = 0;
+
+	/*
+	 * TODO: removed a if(!lport->vport) check. Since this routine
+	 * should be used for all vports (real or virtual) I think it
+	 * should be OK
+	 */
+
+	/*
+	 * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN:
+	 * For WWNN, we use NAA 1 w/ bit 27-16 of word 0 as 0.
+	 * For WWPN, we use NAA 2 w/ bit 27-16 of word 0 from VLAN ID
+	 */
+	if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN)
+		vid = vlan_dev_vlan_id(fcoe->netdev);
+	fcvport_port_name(lport->fcvport) =
+		fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 2, vid);
+}
 
 /**
  * fcoe_interface_setup() - Setup a FCoE interface
@@ -520,8 +590,11 @@ static u8 *fcoe_get_src_mac(struct fc_lport *lport)
  */
 static int fcoe_lport_config(struct fc_lport *lport)
 {
+	lport->fcfabric_f = &fcoe_fcfabric_fcn_tmpl;
+	lport->fcvport_f = &fcoe_fcvport_fcn_tmpl;
+	lport->fcrport_f = &fcoe_fcrport_fcn_tmpl;
+
 	lport->link_up = 0;
-	lport->qfull = 0;
 	lport->max_retry_count = 3;
 	lport->max_rport_retry_count = 3;
 	lport->e_d_tov = 2 * 1000;	/* FC-FS default */
@@ -585,10 +658,8 @@ static int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type)
 static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
 {
 	u32 mfs;
-	u64 wwnn, wwpn;
 	struct fcoe_interface *fcoe;
 	struct fcoe_port *port;
-	int vid = 0;
 
 	/* Setup lport private data to point to fcoe softc */
 	port = lport_priv(lport);
@@ -632,69 +703,32 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
 	port->fcoe_pending_queue_active = 0;
 	setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport);
 
-	fcoe_link_speed_update(lport);
-
-	if (!lport->vport) {
-		/*
-		 * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN:
-		 * For WWNN, we use NAA 1 w/ bit 27-16 of word 0 as 0.
-		 * For WWPN, we use NAA 2 w/ bit 27-16 of word 0 from VLAN ID
-		 */
-		if (netdev->priv_flags & IFF_802_1Q_VLAN)
-			vid = vlan_dev_vlan_id(netdev);
-
-		if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN))
-			wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0);
-		fc_set_wwnn(lport, wwnn);
-		if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN))
-			wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr,
-						 2, vid);
-		fc_set_wwpn(lport, wwpn);
-	}
+	/*
+	 * TODO: This is a bad to have a special case for the N_Port
+	 * lport.
+	 */
+	if (lport->fcport)
+		fcoe_link_speed_update(lport);
 
 	return 0;
 }
 
-/**
- * fcoe_shost_config() - Set up the SCSI host associated with a local port
- * @lport: The local port
- * @dev:   The device associated with the SCSI host
- *
- * Must be called after fcoe_lport_config() and fcoe_netdev_config()
- *
- * Returns: 0 for success
- */
-static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)
+static void fcoe_fcpinit_callback(struct fc_fcpinit *fcpinit)
 {
-	int rc = 0;
-
-	/* lport scsi host config */
-	lport->host->max_lun = FCOE_MAX_LUN;
-	lport->host->max_id = FCOE_MAX_FCP_TARGET;
-	lport->host->max_channel = 0;
-	lport->host->max_cmd_len = FCOE_MAX_CMD_LEN;
-
-	if (lport->vport)
-		lport->host->transportt = fcoe_vport_transport_template;
-	else
-		lport->host->transportt = fcoe_transport_template;
+	struct fc_fcp_internal *si = fcpinit_priv(fcpinit);
 
-	/* add the new host to the SCSI-ml */
-	rc = scsi_add_host(lport->host, dev);
-	if (rc) {
-		FCOE_NETDEV_DBG(fcoe_netdev(lport), "fcoe_shost_config: "
-				"error on scsi_add_host\n");
-		return rc;
-	}
-
-	if (!lport->vport)
-		fc_host_max_npiv_vports(lport->host) = USHORT_MAX;
+	/*
+	 * Must be done after fcoe_libfc_config because that is where
+	 * the libfc function template is initialized.
+	 */
+	fc_fcp_init(fcpinit);
 
-	snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE,
-		 "%s v%s over %s", FCOE_NAME, FCOE_VERSION,
-		 fcoe_netdev(lport)->name);
+	/* lport scsi host config */
+	si->host->max_lun = FCOE_MAX_LUN;
+	si->host->max_id = FCOE_MAX_FCP_TARGET;
+	si->host->max_channel = 0;
 
-	return 0;
+	si->qfull = 0;
 }
 
 /**
@@ -821,7 +855,14 @@ static void fcoe_if_destroy(struct fc_lport *lport)
 
 	/* Cleanup the fc_lport */
 	fc_lport_destroy(lport);
-	fc_fcp_destroy(lport);
+
+	/*
+	 * TODO: Don't like lport->fcpinit, is there something
+	 *       better to check? Check should be here because
+	 *       eventually fcoe should know if it's init or targ.
+	 */
+	if (lport->fcpinit)
+		fc_fcp_destroy(lport);
 
 	/* Stop the transmit retry timer */
 	del_timer_sync(&port->timer);
@@ -839,18 +880,12 @@ static void fcoe_if_destroy(struct fc_lport *lport)
 	/* Free queued packets for the per-CPU receive threads */
 	fcoe_percpu_clean(lport);
 
-	/* Detach from the scsi-ml */
-	fc_remove_host(lport->host);
-	scsi_remove_host(lport->host);
-
 	/* There are no more rports or I/O, free the EM */
 	fc_exch_mgr_free(lport);
 
 	/* Free memory used by statistical counters */
 	fc_lport_free_stats(lport);
 
-	/* Release the Scsi_Host */
-	scsi_host_put(lport->host);
 	module_put(THIS_MODULE);
 }
 
@@ -902,34 +937,15 @@ static int fcoe_ddp_done(struct fc_lport *lport, u16 xid)
  *
  * Returns: The allocated fc_lport or an error pointer
  */
-static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
-				       struct device *parent, int npiv)
+static int fcoe_if_create(struct fc_lport *lport, struct fcoe_interface *fcoe,
+			  int npiv)
 {
 	struct net_device *netdev = fcoe->netdev;
-	struct fc_lport *lport = NULL;
-	struct fcoe_port *port;
-	int rc;
-	/*
-	 * parent is only a vport if npiv is 1,
-	 * but we'll only use vport in that case so go ahead and set it
-	 */
-	struct fc_vport *vport = dev_to_vport(parent);
+	struct fcoe_port *port = lport_priv(lport);
+	int rc = 0;
 
 	FCOE_NETDEV_DBG(netdev, "Create Interface\n");
 
-	if (!npiv) {
-		lport = libfc_host_alloc(&fcoe_shost_template,
-					 sizeof(struct fcoe_port));
-	} else	{
-		lport = libfc_vport_create(vport,
-					   sizeof(struct fcoe_port));
-	}
-	if (!lport) {
-		FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n");
-		rc = -ENOMEM;
-		goto out;
-	}
-	port = lport_priv(lport);
 	port->lport = lport;
 	port->fcoe = fcoe;
 	INIT_WORK(&port->destroy_work, fcoe_destroy_work);
@@ -939,15 +955,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
 	if (rc) {
 		FCOE_NETDEV_DBG(netdev, "Could not configure lport for the "
 				"interface\n");
-		goto out_host_put;
-	}
-
-	if (npiv) {
-		FCOE_NETDEV_DBG(netdev, "Setting vport names, "
-				"%16.16llx %16.16llx\n",
-				vport->node_name, vport->port_name);
-		fc_set_wwnn(lport, vport->node_name);
-		fc_set_wwpn(lport, vport->port_name);
+		goto out;
 	}
 
 	/* configure lport network properties */
@@ -958,22 +966,6 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
 		goto out_lp_destroy;
 	}
 
-	/* configure lport scsi host properties */
-	rc = fcoe_shost_config(lport, parent);
-	if (rc) {
-		FCOE_NETDEV_DBG(netdev, "Could not configure shost for the "
-				"interface\n");
-		goto out_lp_destroy;
-	}
-
-	/* Initialize the library */
-	rc = fcoe_libfc_config(lport, &fcoe_libfc_fcn_templ);
-	if (rc) {
-		FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the "
-				"interface\n");
-		goto out_lp_destroy;
-	}
-
 	if (!npiv) {
 		/*
 		 * fcoe_em_alloc() and fcoe_hostlist_add() both
@@ -995,14 +987,13 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
 	}
 
 	fcoe_interface_get(fcoe);
-	return lport;
+
+	return rc;
 
 out_lp_destroy:
 	fc_exch_mgr_free(lport);
-out_host_put:
-	scsi_host_put(lport->host);
 out:
-	return ERR_PTR(rc);
+	return rc;
 }
 
 /**
@@ -1014,16 +1005,6 @@ 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);
-
-	if (!fcoe_transport_template) {
-		printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n");
-		return -ENODEV;
-	}
-
 	return 0;
 }
 
@@ -1036,10 +1017,6 @@ 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;
 	return 0;
 }
 
@@ -1718,8 +1695,20 @@ int fcoe_percpu_receive_thread(void *arg)
 static void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb)
 {
 	struct fcoe_port *port = lport_priv(lport);
+	struct fc_fcp_internal *si;
 	int rc;
 
+	/*
+	 * TODO: I think this is a problem because we're not guaranteed
+	 * to have logged into the fabric yet. For example, this is called
+	 * from fcoe_xmit, which will be called for the FLOGI itself. I'm
+	 * pretty sure the si will never be set in that case, so what exactly
+	 * are we filling out later in this routine with si->qfull?
+	 *
+	 * Added bad lport->fcpinit checks at the end of this routine. I do
+	 * not trust them.
+	 */
+
 	spin_lock_bh(&port->fcoe_pending_queue.lock);
 
 	if (skb)
@@ -1748,14 +1737,22 @@ static void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb)
 		port->fcoe_pending_queue.qlen--;
 	}
 
-	if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
-		lport->qfull = 0;
+	if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH) {
+		if (lport->fcpinit) {
+			si = fc_get_scsi_internal(lport);
+			si->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;
+	if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) {
+		if (lport->fcpinit) {
+			si = fc_get_scsi_internal(lport);
+			si->qfull = 1;
+		}
+	}
 	spin_unlock_bh(&port->fcoe_pending_queue.lock);
 	return;
 }
@@ -1996,6 +1993,7 @@ out_nodev:
  */
 static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
 {
+	struct fc_lport *lport;
 	struct fcoe_interface *fcoe;
 	struct net_device *netdev;
 	int rc = 0;
@@ -2032,9 +2030,21 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
 		goto out_putdev;
 	}
 	list_del(&fcoe->list);
+
+	lport = fcoe->ctlr.lp;
+
+	/*
+	 * TODO: Should the fcport be deleted before or after
+	 * fcoe_interface_cleanup? Probably before.
+	 */
+	fc_fcport_del(lport->fcport);
+
 	fcoe_interface_cleanup(fcoe);
+
 	/* RTNL mutex is dropped by fcoe_if_destroy */
-	fcoe_if_destroy(fcoe->ctlr.lp);
+	fcoe_if_destroy(lport);
+
+	fc_fcvport_free(lport->fcvport);
 
 out_putdev:
 	dev_put(netdev);
@@ -2070,8 +2080,16 @@ static void fcoe_destroy_work(struct work_struct *work)
  */
 static int fcoe_create(const char *buffer, struct kernel_param *kp)
 {
-	int rc;
+	/*
+	 * TODO: rc and error can probably be consolidated.
+	 */
+	int rc = 0;
+	int error = 0;
+	int priv_size;
+	struct fc_vport_identifiers ids;
 	struct fcoe_interface *fcoe;
+	struct fc_fcport *fcport;
+	struct fc_fcvport *fcvport;
 	struct fc_lport *lport;
 	struct net_device *netdev;
 
@@ -2117,8 +2135,53 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 		goto out_putdev;
 	}
 
-	lport = fcoe_if_create(fcoe, &netdev->dev, 0);
-	if (IS_ERR(lport)) {
+	fcport = fc_fcport_add((struct device *)&netdev->dev.parent,
+			       &fcoe_fcport_fcn_tmpl, &fcoe_fcp_template);
+	if (!fcport) {
+		printk(KERN_ERR "Failed to add a fcport\n");
+		goto out_free;
+	}
+
+	/*
+	 * TODO: This is rather goofy. Is there a better way to
+	 * have the get_vport_ids callback and this call better
+	 * integrated? Does the callback need to pass an lport?
+	 * Probably, since we don't know the LLD is fcoe.ko.
+	 */
+	_fcoe_get_vport_ids(fcoe, &ids);
+
+	priv_size = sizeof(struct fc_lport) + sizeof(struct fcoe_port);
+
+	fcvport = fc_fcvport_alloc(NULL, &ids, &fcoe_fcvport_fcn_tmpl,
+				   priv_size, fcport);
+
+	if (!fcvport) {
+		/*
+		 * TODO: What is the correct thing to do if
+		 * we fail to allocate the N_Port?
+		 */
+		return rc;
+	}
+
+	lport = fcvport_priv(fcvport);
+	lport->fcport = fcport;
+	lport->fcvport = fcvport;
+
+	fcvport_port_type(fcvport) = FC_PORTTYPE_NPORT;
+
+	fc_lport_port_config(lport->fcport);
+
+	/* Initialize the library */
+	fcoe_libfc_config(lport, &fcoe_libfc_fcn_templ);
+
+	/*
+	 * TODO: Does this need to before fcoe_if_create?
+	 */
+	/* Make this the "master" N_Port */
+	fcoe->ctlr.lp = lport;
+
+	error = fcoe_if_create(lport, fcoe, 0);
+	if (error) {
 		printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
 		       netdev->name);
 		rc = -EIO;
@@ -2126,14 +2189,14 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 		goto out_free;
 	}
 
-	/* Make this the "master" N_Port */
-	fcoe->ctlr.lp = lport;
+	lport->fcport->maxframe_size = lport->mfs;
 
 	/* add to lports list */
 	fcoe_hostlist_add(lport);
 
 	/* start FIP Discovery and FLOGI */
 	lport->boot_time = jiffies;
+
 	fc_fabric_login(lport);
 	if (!fcoe_link_ok(lport))
 		fcoe_ctlr_link_up(&fcoe->ctlr);
@@ -2173,19 +2236,21 @@ int fcoe_link_speed_update(struct fc_lport *lport)
 	struct net_device *netdev = port->fcoe->netdev;
 	struct ethtool_cmd ecmd = { ETHTOOL_GSET };
 
+	u32 link_supported_speeds = FC_PORTSPEED_UNKNOWN;
+
 	if (!dev_ethtool_get_settings(netdev, &ecmd)) {
-		lport->link_supported_speeds &=
-			~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
 		if (ecmd.supported & (SUPPORTED_1000baseT_Half |
 				      SUPPORTED_1000baseT_Full))
-			lport->link_supported_speeds |= FC_PORTSPEED_1GBIT;
+			link_supported_speeds |= FC_PORTSPEED_1GBIT;
 		if (ecmd.supported & SUPPORTED_10000baseT_Full)
-			lport->link_supported_speeds |=
-				FC_PORTSPEED_10GBIT;
+			link_supported_speeds |= FC_PORTSPEED_10GBIT;
+
+		fcport_supported_speeds(lport->fcport) = link_supported_speeds;
+
 		if (ecmd.speed == SPEED_1000)
-			lport->link_speed = FC_PORTSPEED_1GBIT;
+			fcport_speed(lport->fcport) = FC_PORTSPEED_1GBIT;
 		if (ecmd.speed == SPEED_10000)
-			lport->link_speed = FC_PORTSPEED_10GBIT;
+			fcport_speed(lport->fcport) = FC_PORTSPEED_10GBIT;
 
 		return 0;
 	}
@@ -2289,9 +2354,9 @@ void fcoe_clean_pending_queue(struct fc_lport *lport)
  *
  * Returns: Always 0 (return value required by FC transport template)
  */
-int fcoe_reset(struct Scsi_Host *shost)
+int fcoe_reset(struct fc_fcvport *fcvport)
 {
-	struct fc_lport *lport = shost_priv(shost);
+	struct fc_lport *lport = fcvport_priv(fcvport);
 	fc_lport_reset(lport);
 	return 0;
 }
@@ -2540,32 +2605,65 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,
  *
  * Returns: 0 for success
  */
-static int fcoe_vport_create(struct fc_vport *vport, bool disabled)
+static int fcoe_vport_create(void *data, struct fc_fcvport *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 net_device *netdev = fcoe->netdev;
-	struct fc_lport *vn_port;
+	struct fc_lport *lport;
+	struct fcoe_port *port;
+	struct fcoe_interface *fcoe;
+	struct net_device *netdev;
+	struct fc_lport *vn_lport;
+	int error = 0;
+
+	lport = (struct fc_lport *)data;
+
+	port = lport_priv(lport);
+	fcoe = port->fcoe;
+	netdev = fcoe->netdev;
+
+	vn_lport = fcvport_priv(vport);
+	if (!vn_lport) {
+		FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n");
+		return -ENOMEM;
+	}
+
+	/* Initialize the library */
+	fcoe_libfc_config(vn_lport, &fcoe_libfc_fcn_templ);
 
 	mutex_lock(&fcoe_config_mutex);
-	vn_port = fcoe_if_create(fcoe, &vport->dev, 1);
+	error = fcoe_if_create(vn_lport, fcoe, 1);
 	mutex_unlock(&fcoe_config_mutex);
 
-	if (IS_ERR(vn_port)) {
-		printk(KERN_ERR "fcoe: fcoe_vport_create(%s) failed\n",
-		       netdev->name);
+	/*
+	 * TODO: Need to free the vn_port (lport) in this case.
+	 */
+	if (error)
+		return error;
+
+	/*
+	 * TODO: Check the failure case here.
+	 */
+	error = libfc_vport_config(lport, vn_lport, vport);
+	if (error)
 		return -EIO;
-	}
+
+	if (IS_ERR(vn_lport))
+		return -EIO;
+
+	/*
+	 * TODO: This routine is not currently setting a unique
+	 * name for the vports.
+	 */
+	fcoe_set_symbolic_name(lport);
 
 	if (disabled) {
 		fc_vport_set_state(vport, FC_VPORT_DISABLED);
 	} else {
-		vn_port->boot_time = jiffies;
-		fc_fabric_login(vn_port);
-		fc_vport_setlink(vn_port);
+		vn_lport->boot_time = jiffies;
+		fc_fabric_login(vn_lport);
+		fc_vport_setlink(vn_lport);
 	}
+
 	return 0;
 }
 
@@ -2575,11 +2673,10 @@ static int fcoe_vport_create(struct fc_vport *vport, bool disabled)
  *
  * Returns: 0 for success
  */
-static int fcoe_vport_destroy(struct fc_vport *vport)
+static int fcoe_vport_destroy(void *data, struct fc_fcvport *vport)
 {
-	struct Scsi_Host *shost = vport_to_shost(vport);
-	struct fc_lport *n_port = shost_priv(shost);
-	struct fc_lport *vn_port = vport->dd_data;
+	struct fc_lport *n_port = data;
+	struct fc_lport *vn_port = fcvport_priv(vport);
 	struct fcoe_port *port = lport_priv(vn_port);
 
 	mutex_lock(&n_port->lp_mutex);
@@ -2594,9 +2691,9 @@ static int fcoe_vport_destroy(struct fc_vport *vport)
  * @vport: vport to bring online/offline
  * @disable: should the vport be disabled?
  */
-static int fcoe_vport_disable(struct fc_vport *vport, bool disable)
+static int fcoe_vport_disable(struct fc_fcvport *vport, bool disable)
 {
-	struct fc_lport *lport = vport->dd_data;
+	struct fc_lport *lport = fcvport_priv(vport);
 
 	if (disable) {
 		fc_vport_set_state(vport, FC_VPORT_DISABLED);
@@ -2618,20 +2715,20 @@ static int fcoe_vport_disable(struct fc_vport *vport, bool disable)
  * sent to the name server.  There is no response handler, so if it fails
  * for some reason it will not be retried.
  */
-static void fcoe_set_vport_symbolic_name(struct fc_vport *vport)
+static void fcoe_set_vport_symbolic_name(struct fc_fcvport *vport)
 {
-	struct fc_lport *lport = vport->dd_data;
+	struct fc_lport *lport = fcvport_priv(vport);
 	struct fc_frame *fp;
 	size_t len;
 
-	snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE,
+	snprintf(fcvport_symbolic_name(lport->fcvport), FC_SYMBOLIC_NAME_SIZE,
 		 "%s v%s over %s : %s", FCOE_NAME, FCOE_VERSION,
 		 fcoe_netdev(lport)->name, vport->symbolic_name);
 
 	if (lport->state != LPORT_ST_READY)
 		return;
 
-	len = strnlen(fc_host_symbolic_name(lport->host), 255);
+	len = strnlen(fcvport_symbolic_name(lport->fcvport), 255);
 	fp = fc_frame_alloc(lport,
 			    sizeof(struct fc_ct_hdr) +
 			    sizeof(struct fc_ns_rspn) + len);
@@ -2647,7 +2744,7 @@ static void fcoe_set_vport_symbolic_name(struct fc_vport *vport)
  * @fc_lesb: the link error status block
  */
 static void fcoe_get_lesb(struct fc_lport *lport,
-			 struct fc_els_lesb *fc_lesb)
+			  struct fc_els_lesb *fc_lesb)
 {
 	unsigned int cpu;
 	u32 lfc, vlfc, mdac;
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 50aaa4b..4c6a5e8 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -40,6 +40,7 @@
 #include <scsi/fc/fc_encaps.h>
 #include <scsi/fc/fc_fcoe.h>
 
+#include <fc/fc.h>
 #include <scsi/libfc.h>
 #include <scsi/libfcoe.h>
 
@@ -53,6 +54,9 @@ MODULE_LICENSE("GPL v2");
 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 void fcoe_ctlr_fcf_work(struct work_struct *);
+static int fcoe_ctlr_queue_work(struct fcoe_ctlr *ctlr,
+				struct work_struct *work);
 
 static u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS;
 
@@ -77,8 +81,8 @@ do {							\
 
 #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);)
+			      printk(KERN_INFO "fcport%d: fip: " fmt,	\
+				     (fip)->lp->fcport->id, ##args);)
 
 /**
  * fcoe_ctlr_mtu_valid() - Check if a FCF's MTU is valid
@@ -119,6 +123,17 @@ void fcoe_ctlr_init(struct fcoe_ctlr *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);
+	/*
+	 * TODO: Need to uniquely identify fip->work_q_name.
+	 * Currently cannot have more than one fip->work_q
+	 * because the names are not unique. Can I just use
+	 * a pointer address?
+	 */
+
+	snprintf(fip->work_q_name, sizeof(fip->work_q_name),
+		 "fcoe_ctlr_wq_%d", 0);
+	fip->work_q = create_singlethread_workqueue(
+		fip->work_q_name);
 }
 EXPORT_SYMBOL(fcoe_ctlr_init);
 
@@ -134,9 +149,14 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
 	struct fcoe_fcf *next;
 
 	fip->sel_fcf = NULL;
+	/*
+	 * TODO: Does the fcfabric disassociation need to
+	 * be protected by the lp_lock?
+	 */
+	fip->lp->fcfabric = NULL;
 	list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
 		list_del(&fcf->list);
-		kfree(fcf);
+		fcoe_ctlr_queue_work(fip, &fcf->fcf_work);
 	}
 	fip->fcf_count = 0;
 	fip->sel_time = 0;
@@ -158,13 +178,15 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
 {
 	cancel_work_sync(&fip->recv_work);
 	skb_queue_purge(&fip->fip_recv_list);
-
 	spin_lock_bh(&fip->lock);
 	fip->state = FIP_ST_DISABLED;
+	fip->lp->fcfabric = NULL;
 	fcoe_ctlr_reset_fcfs(fip);
 	spin_unlock_bh(&fip->lock);
 	del_timer_sync(&fip->timer);
 	cancel_work_sync(&fip->timer_work);
+
+	destroy_workqueue(fip->work_q);
 }
 EXPORT_SYMBOL(fcoe_ctlr_destroy);
 
@@ -230,7 +252,8 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
 
 	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);
+	put_unaligned_be64(fcvport_node_name(fip->lp->fcvport),
+			   &sol->desc.wwnn.fd_wwn);
 
 	fcoe_size = fcoe_ctlr_fcoe_size(fip);
 	sol->desc.size.fd_desc.fip_dtype = FIP_DT_FCOE_SIZE;
@@ -300,12 +323,12 @@ int fcoe_ctlr_link_down(struct fcoe_ctlr *fip)
 	int link_dropped;
 
 	LIBFCOE_FIP_DBG(fip, "link down.\n");
+
 	spin_lock_bh(&fip->lock);
 	fcoe_ctlr_reset(fip);
 	link_dropped = fip->state != FIP_ST_LINK_WAIT;
 	fip->state = FIP_ST_LINK_WAIT;
 	spin_unlock_bh(&fip->lock);
-
 	if (link_dropped)
 		fc_linkdown(fip->lp);
 	return link_dropped;
@@ -365,7 +388,6 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip,
 	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);
@@ -375,7 +397,8 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip,
 		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);
+		put_unaligned_be64(fcvport_port_name(lport->fcvport),
+				   &vn->fd_wwpn);
 	}
 	skb_put(skb, len);
 	skb->protocol = htons(ETH_P_FIP);
@@ -568,6 +591,7 @@ EXPORT_SYMBOL(fcoe_ctlr_els_send);
  */
 static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
 {
+	struct fc_fcp_internal *si = fc_get_scsi_internal(fip->lp);
 	struct fcoe_fcf *fcf;
 	struct fcoe_fcf *next;
 	unsigned long sel_time = 0;
@@ -583,18 +607,20 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
 					    smp_processor_id());
 			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,
+			       "Advertisement for fab %llx count %lld\n",
+			       si->host->host_no, fcf->fabric_name,
 			       stats->MissDiscAdvCount);
 		}
 		if (time_after(jiffies, fcf->time + fcf->fka_period * 3 +
 			       msecs_to_jiffies(FIP_FCF_FUZZ * 3))) {
-			if (fip->sel_fcf == fcf)
+			if (fip->sel_fcf == fcf) {
 				fip->sel_fcf = NULL;
+				fip->lp->fcfabric = NULL;
+			}
 			list_del(&fcf->list);
 			WARN_ON(!fip->fcf_count);
 			fip->fcf_count--;
-			kfree(fcf);
+			fcoe_ctlr_queue_work(fip, &fcf->fcf_work);
 			stats = per_cpu_ptr(fip->lp->dev_stats,
 					    smp_processor_id());
 			stats->VLinkFailureCount++;
@@ -759,7 +785,29 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
 
 		fip->fcf_count++;
 		memcpy(fcf, &new, sizeof(new));
+
+		INIT_WORK(&fcf->fcf_work, fcoe_ctlr_fcf_work);
+		/*
+		 * The fcport and fcfabric_f need to be added
+		 * to the new fcf so that when the fcfabric is
+		 * added to the system it knows which fcport to
+		 * attach it to.
+		 *
+		 * This could probably be improved by adding a lport
+		 * pointer in the fcf. Right now the fcf can't get to
+		 * the lport in the fcf_work queue, so we have to do
+		 * some funky stuff so the fcf can get these objects.
+		 */
+		fcf->fcport = fip->lp->fcport;
+		fcf->fcfabric_f = fip->lp->fcfabric_f;
+		fcf->fcvport_f = fip->lp->fcvport_f;
+
+		/*
+		 * TODO: Should the fcfs be added to the list
+		 * in the work context?
+		 */
 		list_add(&fcf->list, &fip->fcfs);
+		fcoe_ctlr_queue_work(fip, &fcf->fcf_work);
 	} else {
 		/*
 		 * Flags in advertisements are ignored once the FCF is
@@ -778,6 +826,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
 		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 for fab %16.16llx "
@@ -809,9 +858,11 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
 		fip->sel_time = jiffies +
 			msecs_to_jiffies(FCOE_CTLR_START_DELAY);
 		if (!timer_pending(&fip->timer) ||
-		    time_before(fip->sel_time, fip->timer.expires))
+		    time_before(fip->sel_time, fip->timer.expires)) {
 			mod_timer(&fip->timer, fip->sel_time);
+		}
 	}
+
 out:
 	spin_unlock_bh(&fip->lock);
 }
@@ -986,7 +1037,7 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
 				return;
 			if (compare_ether_addr(vp->fd_mac,
 					       fip->get_src_addr(lport)) == 0 &&
-			    get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn &&
+			    get_unaligned_be64(&vp->fd_wwpn) == fcvport_port_name(lport->fcvport) &&
 			    ntoh24(vp->fd_fc_id) == lport->port_id)
 				desc_mask &= ~BIT(FIP_DT_VN_ID);
 			break;
@@ -1093,6 +1144,19 @@ drop:
 	return -1;
 }
 
+static void fcoe_ctlr_assign_fcf(struct fcoe_ctlr *fip,
+				 struct fcoe_fcf *fcf)
+{
+	/*
+	 * TODO: Does the fip->sel_fcf assignment need
+	 * to be protected and if it isn't in the current
+	 * code then why not? Maybe there should be a
+	 * comment explaining it.
+	 */
+	fip->sel_fcf = fcf;
+	fip->lp->fcfabric = fip->sel_fcf->fcfabric;
+}
+
 /**
  * fcoe_ctlr_select() - Select the best FCF (if possible)
  * @fip: The FCoE controller
@@ -1134,7 +1198,7 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip)
 		if (fcf->pri < best->pri)
 			best = fcf;
 	}
-	fip->sel_fcf = best;
+	fcoe_ctlr_assign_fcf(fip, best);
 }
 
 /**
@@ -1169,18 +1233,18 @@ static void fcoe_ctlr_timeout(unsigned long arg)
 	if (sel != fcf) {
 		fcf = sel;		/* the old FCF may have been freed */
 		if (sel) {
-			printk(KERN_INFO "libfcoe: host%d: FIP selected "
+			printk(KERN_INFO "libfcoe: fcport%d: FIP selected "
 			       "Fibre-Channel Forwarder MAC %pM\n",
-			       fip->lp->host->host_no, sel->fcf_mac);
+			       fip->lp->fcport->id, sel->fcf_mac);
 			memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
 			fip->port_ka_time = jiffies +
 				msecs_to_jiffies(FIP_VN_KA_PERIOD);
 			fip->ctlr_ka_time = jiffies + sel->fka_period;
 		} else {
-			printk(KERN_NOTICE "libfcoe: host%d: "
+			printk(KERN_NOTICE "libfcoe: fcport%d: "
 			       "FIP Fibre-Channel Forwarder timed out.	"
 			       "Starting FCF discovery.\n",
-			       fip->lp->host->host_no);
+			       fip->lp->fcport->id);
 			fip->reset_req = 1;
 			schedule_work(&fip->timer_work);
 		}
@@ -1234,7 +1298,6 @@ static void fcoe_ctlr_timer_work(struct work_struct *work)
 
 	if (reset)
 		fc_lport_reset(fip->lp);
-
 	if (fip->send_ctlr_ka) {
 		fip->send_ctlr_ka = 0;
 		fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr);
@@ -1385,23 +1448,71 @@ EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
  * fcoe_libfc_config() - Sets up libfc related properties for local port
  * @lp: The local port to configure libfc for
  * @tt: The libfc function template
- *
- * Returns : 0 for success
  */
-int fcoe_libfc_config(struct fc_lport *lport,
-		      struct libfc_function_template *tt)
+void fcoe_libfc_config(struct fc_lport *lport,
+		       struct libfc_function_template *tt)
 {
 	/* Set the function pointers set by the LLDD */
 	memcpy(&lport->tt, tt, sizeof(*tt));
-	if (fc_fcp_init(lport))
-		return -ENOMEM;
 	fc_exch_init(lport);
 	fc_elsct_init(lport);
 	fc_lport_init(lport);
 	fc_rport_init(lport);
 	fc_disc_init(lport);
-
-	return 0;
 }
 EXPORT_SYMBOL_GPL(fcoe_libfc_config);
 
+/**
+ * fcoe_ctlr_queue_work - Queue work to the fcoe_ctlr workqueue.
+ *
+ * Return value:
+ *      1 - work queued for execution
+ *      0 - work is already queued
+ *      -EINVAL - work queue doesn't exist
+ */
+static int fcoe_ctlr_queue_work(struct fcoe_ctlr *ctlr,
+				struct work_struct *work)
+{
+	if (unlikely(!fcoe_ctlr_work_q(ctlr))) {
+/*
+  printk(KERN_ERR
+  "ERROR: FC VN_Port '%d' attempted to queue work, "
+  "when no workqueue created.\n", fcvport->id);
+*/
+		dump_stack();
+
+		return -EINVAL;
+	}
+
+	return queue_work(fcoe_ctlr_work_q(ctlr), work);
+}
+
+static void fcoe_ctlr_fcf_work(struct work_struct *work)
+{
+	struct fcoe_fcf *fcf;
+
+	fcf = container_of(work, struct fcoe_fcf, fcf_work);
+	if (fcf->fcfabric) {
+		fc_fcfabric_del(fcf->fcfabric);
+		kfree(fcf);
+	} else {
+		fcf->fcfabric = fc_fcfabric_add(fcf->fcport,
+						fcf->fcfabric_f,
+						fcf->switch_name);
+
+		if (!fcf->fcfabric) {
+			/*
+			 * TODO: This needs to fail more severly because
+			 * vports will not be added to FC if there is no
+			 * fabric.
+			 */
+			return;
+		}
+		/*
+		 * TODO: I dislike putting the vport fcn tmpl in the
+		 *       lport and the fcfabric.
+		 */
+		fcf->fcfabric->fcvport_f = fcf->fcvport_f;
+		fcfabric_max_npiv_vports(fcf->fcfabric) = USHORT_MAX;
+	}
+}
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index c7985da..bae5014 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -388,6 +388,7 @@ err:
 static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
 {
 	struct fc_lport *lport;
+	struct fc_fcp_internal *si;
 	struct fc_gpn_ft_resp *np;
 	char *bp;
 	size_t plen;
@@ -397,6 +398,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
 	struct fc_rport_priv *rdata;
 
 	lport = disc->lport;
+	si = fc_get_scsi_internal(lport);
 	disc->seq_count++;
 
 	/*
@@ -441,7 +443,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
 		ids.port_name = ntohll(np->fp_wwpn);
 
 		if (ids.port_id != lport->port_id &&
-		    ids.port_name != lport->wwpn) {
+		    ids.port_name != fcvport_port_name(lport->fcvport)) {
 			rdata = lport->tt.rport_create(lport, ids.port_id);
 			if (rdata) {
 				rdata->ids.port_name = ids.port_name;
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 104e0fb..c73df63 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1905,11 +1905,13 @@ err:
 static void fc_exch_rrq(struct fc_exch *ep)
 {
 	struct fc_lport *lport;
+	struct fc_fcp_internal *si;
 	struct fc_els_rrq *rrq;
 	struct fc_frame *fp;
 	u32 did;
 
 	lport = ep->lp;
+	si = fc_get_scsi_internal(lport);
 
 	fp = fc_frame_alloc(lport, sizeof(*rrq));
 	if (!fp)
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index ec1f66c..e784df8 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -67,26 +67,6 @@ struct kmem_cache *scsi_pkt_cachep;
 #define CMD_SCSI_STATUS(Cmnd)	    ((Cmnd)->SCp.Status)
 #define CMD_RESID_LEN(Cmnd)	    ((Cmnd)->SCp.buffers_residual)
 
-/**
- * struct fc_fcp_internal - FCP layer internal data
- * @scsi_pkt_pool: Memory pool to draw FCP packets from
- * @scsi_queue_lock: Protects the scsi_pkt_queue
- * @scsi_pkt_queue: Current FCP packets
- * @last_can_queue_ramp_down_time: ramp down time
- * @last_can_queue_ramp_up_time: ramp up time
- * @max_can_queue: max can_queue size
- */
-struct fc_fcp_internal {
-	mempool_t		*scsi_pkt_pool;
-	spinlock_t		scsi_queue_lock;
-	struct list_head	scsi_pkt_queue;
-	unsigned long		last_can_queue_ramp_down_time;
-	unsigned long		last_can_queue_ramp_up_time;
-	int			max_can_queue;
-};
-
-#define fc_get_scsi_internal(x)	((struct fc_fcp_internal *)(x)->scsi_priv)
-
 /*
  * function prototypes
  * FC scsi I/O related functions
@@ -352,13 +332,13 @@ static void fc_fcp_can_queue_ramp_up(struct fc_lport *lport)
 
 	si->last_can_queue_ramp_up_time = jiffies;
 
-	can_queue = lport->host->can_queue << 1;
+	can_queue = si->host->can_queue << 1;
 	if (can_queue >= si->max_can_queue) {
 		can_queue = si->max_can_queue;
 		si->last_can_queue_ramp_down_time = 0;
 	}
-	lport->host->can_queue = can_queue;
-	shost_printk(KERN_ERR, lport->host, "libfc: increased "
+	si->host->can_queue = can_queue;
+	shost_printk(KERN_ERR, si->host, "libfc: increased "
 		     "can_queue to %d.\n", can_queue);
 }
 
@@ -386,12 +366,12 @@ static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport)
 
 	si->last_can_queue_ramp_down_time = jiffies;
 
-	can_queue = lport->host->can_queue;
+	can_queue = si->host->can_queue;
 	can_queue >>= 1;
 	if (!can_queue)
 		can_queue = 1;
-	lport->host->can_queue = can_queue;
-	shost_printk(KERN_ERR, lport->host, "libfc: Could not allocate frame.\n"
+	si->host->can_queue = can_queue;
+	shost_printk(KERN_ERR, si->host, "libfc: Could not allocate frame.\n"
 		     "Reducing can_queue to %d.\n", can_queue);
 }
 
@@ -406,6 +386,7 @@ static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport)
 static inline struct fc_frame *fc_fcp_frame_alloc(struct fc_lport *lport,
 						  size_t len)
 {
+	struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
 	struct fc_frame *fp;
 	unsigned long flags;
 
@@ -414,9 +395,10 @@ static inline struct fc_frame *fc_fcp_frame_alloc(struct fc_lport *lport,
 		return fp;
 
 	/* error case */
-	spin_lock_irqsave(lport->host->host_lock, flags);
+	spin_lock_irqsave(si->host->host_lock, flags);
 	fc_fcp_can_queue_ramp_down(lport);
-	spin_unlock_irqrestore(lport->host->host_lock, flags);
+	spin_unlock_irqrestore(si->host->host_lock, flags);
+
 	return NULL;
 }
 
@@ -1001,7 +983,7 @@ static void fc_fcp_cleanup_each_cmd(struct fc_lport *lport, unsigned int id,
 	struct scsi_cmnd *sc_cmd;
 	unsigned long flags;
 
-	spin_lock_irqsave(&si->scsi_queue_lock, flags);
+	spin_lock_irqsave(si->host->host_lock, flags);
 restart:
 	list_for_each_entry(fsp, &si->scsi_pkt_queue, list) {
 		sc_cmd = fsp->cmd;
@@ -1012,7 +994,7 @@ restart:
 			continue;
 
 		fc_fcp_pkt_hold(fsp);
-		spin_unlock_irqrestore(&si->scsi_queue_lock, flags);
+		spin_unlock_irqrestore(si->host->host_lock, flags);
 
 		if (!fc_fcp_lock_pkt(fsp)) {
 			fc_fcp_cleanup_cmd(fsp, error);
@@ -1021,14 +1003,14 @@ restart:
 		}
 
 		fc_fcp_pkt_release(fsp);
-		spin_lock_irqsave(&si->scsi_queue_lock, flags);
+		spin_lock_irqsave(si->host->host_lock, flags);
 		/*
 		 * while we dropped the lock multiple pkts could
 		 * have been released, so we have to start over.
 		 */
 		goto restart;
 	}
-	spin_unlock_irqrestore(&si->scsi_queue_lock, flags);
+	spin_unlock_irqrestore(si->host->host_lock, flags);
 }
 
 /**
@@ -1061,10 +1043,10 @@ static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)
 	int_to_scsilun(fsp->cmd->device->lun,
 		       (struct scsi_lun *)fsp->cdb_cmd.fc_lun);
 	memcpy(fsp->cdb_cmd.fc_cdb, fsp->cmd->cmnd, fsp->cmd->cmd_len);
-
 	spin_lock_irqsave(&si->scsi_queue_lock, flags);
 	list_add_tail(&fsp->list, &si->scsi_pkt_queue);
 	spin_unlock_irqrestore(&si->scsi_queue_lock, flags);
+
 	rc = lport->tt.fcp_cmd_send(lport, fsp, fc_fcp_recv);
 	if (unlikely(rc)) {
 		spin_lock_irqsave(&si->scsi_queue_lock, flags);
@@ -1366,8 +1348,10 @@ static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
 	struct fc_frame *fp;
 	struct fc_rport *rport;
 	struct fc_rport_libfc_priv *rpriv;
+	struct fc_fcp_internal *si;
 
 	lport = fsp->lp;
+	si = fc_get_scsi_internal(lport);
 	rport = fsp->rport;
 	rpriv = rport->dd_data;
 	if (!fsp->seq_ptr || rpriv->rp_state != RPORT_ST_READY) {
@@ -1745,7 +1729,7 @@ static inline int fc_fcp_lport_queue_ready(struct fc_lport *lport)
 {
 	/* lock ? */
 	return (lport->state == LPORT_ST_READY) &&
-		lport->link_up && !lport->qfull;
+		lport->link_up && !fc_get_scsi_internal(lport)->qfull;
 }
 
 /**
@@ -1758,17 +1742,19 @@ static inline int fc_fcp_lport_queue_ready(struct fc_lport *lport)
  */
 int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
 {
-	struct fc_lport *lport;
-	struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
+	struct scsi_target *starget = scsi_target(sc_cmd->device);
+	struct fc_fcptarg *fcptarg = starget_to_fcptarg(starget);
+	struct fc_rport *rport = fcptarg->rport;
+	struct fc_fcpinit *fcpinit = shost_priv(sc_cmd->device->host);
+	struct fc_fcp_internal *si = fcpinit_priv(fcpinit);
+	struct fc_lport *lport = si->lport;
 	struct fc_fcp_pkt *fsp;
 	struct fc_rport_libfc_priv *rpriv;
 	int rval;
 	int rc = 0;
 	struct fcoe_dev_stats *stats;
 
-	lport = shost_priv(sc_cmd->device->host);
-	spin_unlock_irq(lport->host->host_lock);
-
+	spin_unlock_irq(si->host->host_lock);
 	rval = fc_remote_port_chkready(rport);
 	if (rval) {
 		sc_cmd->result = rval;
@@ -1789,7 +1775,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
 	rpriv = rport->dd_data;
 
 	if (!fc_fcp_lport_queue_ready(lport)) {
-		if (lport->qfull)
+		if (si->qfull)
 			fc_fcp_can_queue_ramp_down(lport);
 		rc = SCSI_MLQUEUE_HOST_BUSY;
 		goto out;
@@ -1844,14 +1830,16 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
 	 * if we get -1 return then put the request in the pending
 	 * queue.
 	 */
+
 	rval = fc_fcp_pkt_send(lport, fsp);
 	if (rval != 0) {
 		fsp->state = FC_SRB_FREE;
 		fc_fcp_pkt_release(fsp);
 		rc = SCSI_MLQUEUE_HOST_BUSY;
 	}
+
 out:
-	spin_lock_irq(lport->host->host_lock);
+	spin_lock_irq(si->host->host_lock);
 	return rc;
 }
 EXPORT_SYMBOL(fc_queuecommand);
@@ -1992,27 +1980,28 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
  */
 int fc_eh_abort(struct scsi_cmnd *sc_cmd)
 {
+	struct fc_fcpinit *fcpinit = shost_priv(sc_cmd->device->host);
+	struct fc_fcp_internal *si = fcpinit_priv(fcpinit);
+	struct fc_lport *lport = si->lport;
 	struct fc_fcp_pkt *fsp;
-	struct fc_lport *lport;
 	int rc = FAILED;
 	unsigned long flags;
 
-	lport = shost_priv(sc_cmd->device->host);
 	if (lport->state != LPORT_ST_READY)
 		return rc;
 	else if (!lport->link_up)
 		return rc;
 
-	spin_lock_irqsave(lport->host->host_lock, flags);
+	spin_lock_irqsave(si->host->host_lock, flags);
 	fsp = CMD_SP(sc_cmd);
 	if (!fsp) {
 		/* command completed while scsi eh was setting up */
-		spin_unlock_irqrestore(lport->host->host_lock, flags);
+		spin_unlock_irqrestore(si->host->host_lock, flags);
 		return SUCCESS;
 	}
 	/* grab a ref so the fsp and sc_cmd cannot be relased from under us */
 	fc_fcp_pkt_hold(fsp);
-	spin_unlock_irqrestore(lport->host->host_lock, flags);
+	spin_unlock_irqrestore(si->host->host_lock, flags);
 
 	if (fc_fcp_lock_pkt(fsp)) {
 		/* completed while we were waiting for timer to be deleted */
@@ -2038,9 +2027,13 @@ EXPORT_SYMBOL(fc_eh_abort);
  */
 int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
 {
-	struct fc_lport *lport;
+	struct fc_fcpinit *fcpinit = shost_priv(sc_cmd->device->host);
+	struct fc_fcp_internal *si = fcpinit_priv(fcpinit);
+	struct fc_lport *lport = si->lport;
+	struct scsi_target *starget = scsi_target(sc_cmd->device);
+	struct fc_fcptarg *fcptarg = starget_to_fcptarg(starget);
+	struct fc_rport *rport = fcptarg->rport;
 	struct fc_fcp_pkt *fsp;
-	struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
 	int rc = FAILED;
 	int rval;
 
@@ -2048,8 +2041,6 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
 	if (rval)
 		goto out;
 
-	lport = shost_priv(sc_cmd->device->host);
-
 	if (lport->state != LPORT_ST_READY)
 		return rc;
 
@@ -2088,7 +2079,9 @@ EXPORT_SYMBOL(fc_eh_device_reset);
 int fc_eh_host_reset(struct scsi_cmnd *sc_cmd)
 {
 	struct Scsi_Host *shost = sc_cmd->device->host;
-	struct fc_lport *lport = shost_priv(shost);
+	struct fc_fcpinit *fcpinit = shost_priv(shost);
+	struct fc_fcp_internal *si = fcpinit_priv(fcpinit);
+	struct fc_lport *lport = si->lport;
 	unsigned long wait_tmo;
 
 	FC_SCSI_DBG(lport, "Resetting host\n");
@@ -2121,7 +2114,8 @@ EXPORT_SYMBOL(fc_eh_host_reset);
  */
 int fc_slave_alloc(struct scsi_device *sdev)
 {
-	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+	struct fc_fcptarg *fcptarg = starget_to_fcptarg(scsi_target(sdev));
+	struct fc_rport *rport = fcptarg->rport;
 
 	if (!rport || fc_remote_port_chkready(rport))
 		return -ENXIO;
@@ -2194,7 +2188,10 @@ void fc_fcp_destroy(struct fc_lport *lport)
 		       "port (%6.6x)\n", lport->port_id);
 
 	mempool_destroy(si->scsi_pkt_pool);
-	kfree(si);
+
+	/*
+	 * TODO: This should get set in the callback from FC
+	 */
 	lport->scsi_priv = NULL;
 }
 EXPORT_SYMBOL(fc_fcp_destroy);
@@ -2225,10 +2222,14 @@ void fc_destroy_fcp()
  * fc_fcp_init() - Initialize the FCP layer for a local port
  * @lport: The local port to initialize the exchange layer for
  */
-int fc_fcp_init(struct fc_lport *lport)
+int fc_fcp_init(struct fc_fcpinit *fcpinit)
 {
-	int rc;
-	struct fc_fcp_internal *si;
+	struct fc_fcvport *fcvport = fcpinit_to_fcvport(fcpinit);
+	struct fc_lport *lport = fcvport_priv(fcvport);
+	struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+	struct fc_fcp_internal *si = fcpinit_priv(fcpinit);
+
+	int rc = 0;
 
 	if (!lport->tt.fcp_cmd_send)
 		lport->tt.fcp_cmd_send = fc_fcp_cmd_send;
@@ -2239,23 +2240,18 @@ int fc_fcp_init(struct fc_lport *lport)
 	if (!lport->tt.fcp_abort_io)
 		lport->tt.fcp_abort_io = fc_fcp_abort_io;
 
-	si = kzalloc(sizeof(struct fc_fcp_internal), GFP_KERNEL);
-	if (!si)
-		return -ENOMEM;
+	lport->fcpinit = fcpinit;
+	si->host = shost;
 	lport->scsi_priv = si;
-	si->max_can_queue = lport->host->can_queue;
+	si->lport = lport;
+	si->max_can_queue = si->host->can_queue;
+
 	INIT_LIST_HEAD(&si->scsi_pkt_queue);
 	spin_lock_init(&si->scsi_queue_lock);
-
 	si->scsi_pkt_pool = mempool_create_slab_pool(2, scsi_pkt_cachep);
-	if (!si->scsi_pkt_pool) {
+	if (!si->scsi_pkt_pool)
 		rc = -ENOMEM;
-		goto free_internal;
-	}
-	return 0;
 
-free_internal:
-	kfree(si);
 	return rc;
 }
 EXPORT_SYMBOL(fc_fcp_init);
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
index f5c0ca4..e5ec03e 100644
--- a/drivers/scsi/libfc/fc_libfc.h
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -45,20 +45,20 @@ extern unsigned int fc_debug_logging;
 
 #define FC_LPORT_DBG(lport, fmt, args...)				\
 	FC_CHECK_LOGGING(FC_LPORT_LOGGING,				\
-			 printk(KERN_INFO "host%u: lport %6.6x: " fmt,	\
-				(lport)->host->host_no,			\
+			 printk(KERN_INFO "fcport%u: lport %6x: " fmt,	\
+				(lport)->fcport->id,			\
 				(lport)->port_id, ##args))
 
-#define FC_DISC_DBG(disc, fmt, args...)				\
-	FC_CHECK_LOGGING(FC_DISC_LOGGING,			\
-			 printk(KERN_INFO "host%u: disc: " fmt,	\
-				(disc)->lport->host->host_no,	\
+#define FC_DISC_DBG(disc, fmt, args...)					\
+	FC_CHECK_LOGGING(FC_DISC_LOGGING,				\
+			 printk(KERN_INFO "fcport%u: disc: " fmt,	\
+				(disc)->lport->fcport->id,		\
 				##args))
 
 #define FC_RPORT_ID_DBG(lport, port_id, fmt, args...)			\
 	FC_CHECK_LOGGING(FC_RPORT_LOGGING,				\
-			 printk(KERN_INFO "host%u: rport %6.6x: " fmt,	\
-				(lport)->host->host_no,			\
+			 printk(KERN_INFO "fcport%u: rport %6x: " fmt,	\
+				(lport)->fcport->id,			\
 				(port_id), ##args))
 
 #define FC_RPORT_DBG(rdata, fmt, args...)				\
@@ -66,20 +66,20 @@ extern unsigned int fc_debug_logging;
 
 #define FC_FCP_DBG(pkt, fmt, args...)					\
 	FC_CHECK_LOGGING(FC_FCP_LOGGING,				\
-			 printk(KERN_INFO "host%u: fcp: %6.6x: " fmt,	\
-				(pkt)->lp->host->host_no,		\
+			 printk(KERN_INFO "fcport%u: fcp: %6x: " fmt,	\
+				(pkt)->lp->fcport->id,			\
 				pkt->rport->port_id, ##args))
 
 #define FC_EXCH_DBG(exch, fmt, args...)					\
 	FC_CHECK_LOGGING(FC_EXCH_LOGGING,				\
-			 printk(KERN_INFO "host%u: xid %4x: " fmt,	\
-				(exch)->lp->host->host_no,		\
+			 printk(KERN_INFO "fcport%u: xid %4x: " fmt,	\
+				(exch)->lp->fcport->id,			\
 				exch->xid, ##args))
 
 #define FC_SCSI_DBG(lport, fmt, args...)				\
 	FC_CHECK_LOGGING(FC_SCSI_LOGGING,				\
-			 printk(KERN_INFO "host%u: scsi: " fmt,		\
-				(lport)->host->host_no,	##args))
+			 printk(KERN_INFO "fcport%u: scsi: " fmt,	\
+				(lport)->fcport->id,	##args))
 
 /*
  * Set up direct-data placement for this I/O request
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 79c9e3c..2850405 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -247,50 +247,39 @@ static void fc_lport_ptp_setup(struct fc_lport *lport,
  * fc_get_host_port_state() - Return the port state of the given Scsi_Host
  * @shost:  The SCSI host whose port state is to be determined
  */
-void fc_get_host_port_state(struct Scsi_Host *shost)
+void fc_get_fcpinit_port_state(struct fc_fcpinit *fcpinit)
 {
-	struct fc_lport *lport = shost_priv(shost);
+	struct fc_lport *lport = fcpinit_to_lport(fcpinit);
 
 	mutex_lock(&lport->lp_mutex);
 	if (!lport->link_up)
-		fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+		fcpinit_port_state(fcpinit) = FC_PORTSTATE_LINKDOWN;
 	else
 		switch (lport->state) {
 		case LPORT_ST_READY:
-			fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+			fcpinit_port_state(fcpinit) = FC_PORTSTATE_ONLINE;
 			break;
 		default:
-			fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+			fcpinit_port_state(fcpinit) = FC_PORTSTATE_OFFLINE;
 		}
 	mutex_unlock(&lport->lp_mutex);
 }
-EXPORT_SYMBOL(fc_get_host_port_state);
-
-/**
- * fc_get_host_speed() - Return the speed of the given Scsi_Host
- * @shost: The SCSI host whose port speed is to be determined
- */
-void fc_get_host_speed(struct Scsi_Host *shost)
-{
-	struct fc_lport *lport = shost_priv(shost);
-
-	fc_host_speed(shost) = lport->link_speed;
-}
-EXPORT_SYMBOL(fc_get_host_speed);
+EXPORT_SYMBOL(fc_get_fcpinit_port_state);
 
 /**
  * fc_get_host_stats() - Return the Scsi_Host's statistics
  * @shost: The SCSI host whose statistics are to be returned
  */
-struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
+struct fcpinit_statistics *fc_get_fcpinit_stats(struct fc_fcpinit *fcpinit)
 {
-	struct fc_host_statistics *fcoe_stats;
-	struct fc_lport *lport = shost_priv(shost);
+	struct fcpinit_statistics *fcoe_stats;
+	struct fc_lport *lport = fcpinit_to_lport(fcpinit);
+	struct fc_fcp_internal *si = fcpinit_priv(fcpinit);
 	struct timespec v0, v1;
 	unsigned int cpu;
 
-	fcoe_stats = &lport->host_stats;
-	memset(fcoe_stats, 0, sizeof(struct fc_host_statistics));
+	fcoe_stats = &si->fcpinit_stats;
+	memset(fcoe_stats, 0, sizeof(struct fcpinit_statistics));
 
 	jiffies_to_timespec(jiffies, &v0);
 	jiffies_to_timespec(lport->boot_time, &v1);
@@ -322,7 +311,7 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
 	fcoe_stats->dumped_frames = -1;
 	return fcoe_stats;
 }
-EXPORT_SYMBOL(fc_get_host_stats);
+EXPORT_SYMBOL(fc_get_fcpinit_stats);
 
 /**
  * fc_lport_flogi_fill() - Fill in FLOGI command for request
@@ -339,8 +328,8 @@ static void fc_lport_flogi_fill(struct fc_lport *lport,
 
 	memset(flogi, 0, sizeof(*flogi));
 	flogi->fl_cmd = (u8) op;
-	put_unaligned_be64(lport->wwpn, &flogi->fl_wwpn);
-	put_unaligned_be64(lport->wwnn, &flogi->fl_wwnn);
+	put_unaligned_be64(fcvport_port_name(lport->fcvport), &flogi->fl_wwpn);
+	put_unaligned_be64(fcvport_node_name(lport->fcvport), &flogi->fl_wwnn);
 	sp = &flogi->fl_csp;
 	sp->sp_hi_ver = 0x20;
 	sp->sp_lo_ver = 0x20;
@@ -483,8 +472,8 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
 			rp->rnid.rnid_cmd = ELS_LS_ACC;
 			rp->rnid.rnid_fmt = fmt;
 			rp->rnid.rnid_cid_len = sizeof(rp->cid);
-			rp->cid.rnid_wwpn = htonll(lport->wwpn);
-			rp->cid.rnid_wwnn = htonll(lport->wwnn);
+			rp->cid.rnid_wwpn = htonll(fcvport_port_name(lport->fcvport));
+			rp->cid.rnid_wwnn = htonll(fcvport_node_name(lport->fcvport));
 			if (fmt == ELS_RNIDF_GEN) {
 				rp->rnid.rnid_sid_len = sizeof(rp->gen);
 				memcpy(&rp->gen, &lport->rnid_gen,
@@ -552,7 +541,6 @@ void __fc_linkup(struct fc_lport *lport)
 {
 	if (!lport->link_up) {
 		lport->link_up = 1;
-
 		if (lport->state == LPORT_ST_RESET)
 			fc_lport_enter_flogi(lport);
 	}
@@ -564,8 +552,8 @@ void __fc_linkup(struct fc_lport *lport)
  */
 void fc_linkup(struct fc_lport *lport)
 {
-	printk(KERN_INFO "host%d: libfc: Link up on port (%6.6x)\n",
-	       lport->host->host_no, lport->port_id);
+	printk(KERN_INFO "fcport%d: libfc: Link up on port (%6x)\n",
+	       lport->fcport->id, lport->port_id);
 
 	mutex_lock(&lport->lp_mutex);
 	__fc_linkup(lport);
@@ -584,7 +572,8 @@ void __fc_linkdown(struct fc_lport *lport)
 	if (lport->link_up) {
 		lport->link_up = 0;
 		fc_lport_enter_reset(lport);
-		lport->tt.fcp_cleanup(lport);
+		if (lport->tt.fcp_cleanup)
+			lport->tt.fcp_cleanup(lport);
 	}
 }
 
@@ -594,8 +583,8 @@ void __fc_linkdown(struct fc_lport *lport)
  */
 void fc_linkdown(struct fc_lport *lport)
 {
-	printk(KERN_INFO "host%d: libfc: Link down on port (%6.6x)\n",
-	       lport->host->host_no, lport->port_id);
+	printk(KERN_INFO "fcport%d: libfc: Link down on port (%6x)\n",
+	       lport->fcport->id, lport->port_id);
 
 	mutex_lock(&lport->lp_mutex);
 	__fc_linkdown(lport);
@@ -644,7 +633,13 @@ int fc_lport_destroy(struct fc_lport *lport)
 	lport->tt.frame_send = fc_frame_drop;
 	mutex_unlock(&lport->lp_mutex);
 
-	lport->tt.fcp_abort_io(lport);
+	/*
+	 * TODO: What should we be checking here? The existence of fcpinit or
+	 * the existence fcp_abort_io()?
+	 */
+	if (lport->fcpinit)
+		lport->tt.fcp_abort_io(lport);
+
 	lport->tt.disc_stop_final(lport);
 	lport->tt.exch_mgr_reset(lport, 0, 0);
 	return 0;
@@ -695,9 +690,9 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
 		FC_LPORT_DBG(lport, "Discovery succeeded\n");
 		break;
 	case DISC_EV_FAILED:
-		printk(KERN_ERR "host%d: libfc: "
-		       "Discovery failed for port (%6.6x)\n",
-		       lport->host->host_no, lport->port_id);
+		printk(KERN_ERR "fcport%d: libfc: "
+		       "Discovery failed for port (%6x)\n",
+		       lport->fcport->id, lport->port_id);
 		mutex_lock(&lport->lp_mutex);
 		fc_lport_enter_reset(lport);
 		mutex_unlock(&lport->lp_mutex);
@@ -721,8 +716,8 @@ static void fc_lport_enter_ready(struct fc_lport *lport)
 		     fc_lport_state(lport));
 
 	fc_lport_state_enter(lport, LPORT_ST_READY);
-	if (lport->vport)
-		fc_vport_set_state(lport->vport, FC_VPORT_ACTIVE);
+	if (!fc_fcvport_is_nport(lport->fcvport))
+		fc_vport_set_state(lport->fcvport, FC_VPORT_ACTIVE);
 	fc_vports_linkchange(lport);
 
 	if (!lport->ptp_rdata)
@@ -742,13 +737,11 @@ static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id,
 				 struct fc_frame *fp)
 {
 	if (port_id)
-		printk(KERN_INFO "host%d: Assigned Port ID %6.6x\n",
-		       lport->host->host_no, port_id);
+		printk(KERN_INFO "fcport%d: Assigned Port ID %6x\n",
+		       lport->fcport->id, port_id);
 
 	lport->port_id = port_id;
-
-	/* Update the fc_host */
-	fc_host_port_id(lport->host) = port_id;
+	lport->fcvport->port_id = port_id;
 
 	if (lport->tt.lport_set_port_id)
 		lport->tt.lport_set_port_id(lport, port_id, fp);
@@ -791,10 +784,10 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
 	if (!flp)
 		goto out;
 	remote_wwpn = get_unaligned_be64(&flp->fl_wwpn);
-	if (remote_wwpn == lport->wwpn) {
-		printk(KERN_WARNING "host%d: libfc: Received FLOGI from port "
-		       "with same WWPN %16.16llx\n",
-		       lport->host->host_no, remote_wwpn);
+	if (remote_wwpn == fcvport_port_name(lport->fcvport)) {
+		printk(KERN_WARNING "fcport%d: libfc: Received FLOGI from port "
+		       "with same WWPN %llx\n",
+		       lport->fcport->id, remote_wwpn);
 		goto out;
 	}
 	FC_LPORT_DBG(lport, "FLOGI from port WWPN %16.16llx\n", remote_wwpn);
@@ -805,7 +798,7 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
 	 * But if so, both of us could end up with the same FID.
 	 */
 	local_fid = FC_LOCAL_PTP_FID_LO;
-	if (remote_wwpn < lport->wwpn) {
+	if (remote_wwpn < fcvport_port_name(lport->fcvport)) {
 		local_fid = FC_LOCAL_PTP_FID_HI;
 		if (!remote_fid || remote_fid == local_fid)
 			remote_fid = FC_LOCAL_PTP_FID_LO;
@@ -952,7 +945,9 @@ static void fc_lport_reset_locked(struct fc_lport *lport)
 	lport->tt.disc_stop(lport);
 
 	lport->tt.exch_mgr_reset(lport, 0, 0);
-	fc_host_fabric_name(lport->host) = 0;
+
+	if (lport->fcfabric)
+		fcfabric_fabric_name(lport->fcfabric) = 0;
 
 	if (lport->port_id)
 		fc_lport_set_port_id(lport, 0, NULL);
@@ -973,15 +968,18 @@ static void fc_lport_enter_reset(struct fc_lport *lport)
 	if (lport->state == LPORT_ST_DISABLED || lport->state == LPORT_ST_LOGO)
 		return;
 
-	if (lport->vport) {
+	if (!fc_fcvport_is_nport(lport->fcvport)) {
 		if (lport->link_up)
-			fc_vport_set_state(lport->vport, FC_VPORT_INITIALIZING);
+			fc_vport_set_state(lport->fcvport,
+					   FC_VPORT_INITIALIZING);
 		else
-			fc_vport_set_state(lport->vport, FC_VPORT_LINKDOWN);
+			fc_vport_set_state(lport->fcvport,
+					   FC_VPORT_LINKDOWN);
 	}
 	fc_lport_state_enter(lport, LPORT_ST_RESET);
 	fc_vports_linkchange(lport);
 	fc_lport_reset_locked(lport);
+
 	if (lport->link_up)
 		fc_lport_enter_flogi(lport);
 }
@@ -1228,7 +1226,7 @@ static void fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state)
 		size += sizeof(struct fc_ns_rn_id);
 		break;
 	case LPORT_ST_RSNN_NN:
-		len = strnlen(fc_host_symbolic_name(lport->host), 255);
+		len = strnlen(fcvport_symbolic_name(lport->fcvport), 255);
 		/* if there is no symbolic name, skip to RFT_ID */
 		if (!len)
 			return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
@@ -1236,7 +1234,7 @@ static void fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state)
 		size += sizeof(struct fc_ns_rsnn) + len;
 		break;
 	case LPORT_ST_RSPN_ID:
-		len = strnlen(fc_host_symbolic_name(lport->host), 255);
+		len = strnlen(fcvport_symbolic_name(lport->fcvport), 255);
 		/* if there is no symbolic name, skip to RFT_ID */
 		if (!len)
 			return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
@@ -1411,6 +1409,15 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
 	FC_LPORT_DBG(lport, "Entered LOGO state from %s state\n",
 		     fc_lport_state(lport));
 
+	/*
+	 * TODO: Is this check sufficient? The port_id is set in the
+	 * FLOGI response just after the vport is added. There's also
+	 * a vport->port_id that is set at the same time, but I don't
+	 * think that it is getting cleared.
+	 */
+	if (lport->port_id != 0)
+		fc_fcvport_del(lport->fcvport);
+
 	fc_lport_state_enter(lport, LPORT_ST_LOGO);
 	fc_vports_linkchange(lport);
 
@@ -1448,8 +1455,6 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 	unsigned int e_d_tov;
 	u16 mfs;
 
-	FC_LPORT_DBG(lport, "Received a FLOGI %s\n", fc_els_resp_type(fp));
-
 	if (fp == ERR_PTR(-FC_EX_CLOSED))
 		return;
 
@@ -1491,10 +1496,6 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 					lport->e_d_tov = e_d_tov;
 				lport->r_a_tov = 2 * e_d_tov;
 				fc_lport_set_port_id(lport, did, fp);
-				printk(KERN_INFO "host%d: libfc: "
-				       "Port (%6.6x) entered "
-				       "point-to-point mode\n",
-				       lport->host->host_no, did);
 				fc_lport_ptp_setup(lport, ntoh24(fh->fh_s_id),
 						   get_unaligned_be64(
 							   &flp->fl_wwpn),
@@ -1503,15 +1504,22 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 			} else {
 				lport->e_d_tov = e_d_tov;
 				lport->r_a_tov = r_a_tov;
-				fc_host_fabric_name(lport->host) =
-					get_unaligned_be64(&flp->fl_wwnn);
+
 				fc_lport_set_port_id(lport, did, fp);
+
+				fc_fcvport_add(lport->fcvport, lport->fcfabric);
+
+				/*
+				 * TODO: This seems redundant. Can't the vport
+				 * port_id get set in fc_lport_set_port_id()?
+				 */
+				fcvport_port_id(lport->fcvport) = lport->port_id;
+
 				fc_lport_enter_dns(lport);
 			}
 		}
-	} else {
+	} else
 		FC_LPORT_DBG(lport, "Bad FLOGI response\n");
-	}
 
 out:
 	fc_frame_free(fp);
@@ -1541,10 +1549,10 @@ void fc_lport_enter_flogi(struct fc_lport *lport)
 		return fc_lport_error(lport, fp);
 
 	if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp,
-				  lport->vport ? ELS_FDISC : ELS_FLOGI,
+				  fc_fcvport_is_nport(lport->fcvport) ? ELS_FLOGI : ELS_FDISC,
 				  fc_lport_flogi_resp, lport,
-				  lport->vport ? 2 * lport->r_a_tov :
-				  lport->e_d_tov))
+				  fc_fcvport_is_nport(lport->fcvport) ? lport->e_d_tov :
+				  2 * lport->r_a_tov))
 		fc_lport_error(lport, NULL);
 }
 
@@ -1554,11 +1562,19 @@ void fc_lport_enter_flogi(struct fc_lport *lport)
  */
 int fc_lport_config(struct fc_lport *lport)
 {
+	INIT_LIST_HEAD(&lport->ema_list);
+	INIT_LIST_HEAD(&lport->vports);
 	INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout);
 	mutex_init(&lport->lp_mutex);
 
 	fc_lport_state_enter(lport, LPORT_ST_DISABLED);
 
+	/*
+	 * TODO: This is a bit goofy. We either need to
+	 * use the fcport speeds and not have a lport copy
+	 * or have a lport copy and have a get_fcport_*speed*()
+	 * routine.
+	 */
 	fc_lport_add_fc4_type(lport, FC_TYPE_FCP);
 	fc_lport_add_fc4_type(lport, FC_TYPE_CT);
 
@@ -1566,6 +1582,26 @@ int fc_lport_config(struct fc_lport *lport)
 }
 EXPORT_SYMBOL(fc_lport_config);
 
+void fc_lport_port_config(struct fc_fcport *fcport)
+{
+	fcport_supported_fc4s(fcport)[2] = 1;
+	fcport_supported_fc4s(fcport)[7] = 1;
+	fcport_active_fc4s(fcport)[2] = 1;
+	fcport_active_fc4s(fcport)[7] = 1;
+
+	fcport_supported_classes(fcport) = FC_COS_CLASS3;
+	memset(fcport->supported_fc4s, 0,
+	       sizeof(fcport->supported_fc4s));
+	fcport->supported_fc4s[2] = 1;
+	fcport->supported_fc4s[7] = 1;
+
+	memset(fcport->active_fc4s, 0,
+	       sizeof(fcport->active_fc4s));
+	fcport->active_fc4s[2] = 1;
+	fcport->active_fc4s[7] = 1;
+}
+EXPORT_SYMBOL(fc_lport_port_config);
+
 /**
  * fc_lport_init() - Initialize the lport layer for a local port
  * @lport: The local port to initialize the exchange layer for
@@ -1578,27 +1614,6 @@ int fc_lport_init(struct fc_lport *lport)
 	if (!lport->tt.lport_reset)
 		lport->tt.lport_reset = fc_lport_reset;
 
-	fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
-	fc_host_node_name(lport->host) = lport->wwnn;
-	fc_host_port_name(lport->host) = lport->wwpn;
-	fc_host_supported_classes(lport->host) = FC_COS_CLASS3;
-	memset(fc_host_supported_fc4s(lport->host), 0,
-	       sizeof(fc_host_supported_fc4s(lport->host)));
-	fc_host_supported_fc4s(lport->host)[2] = 1;
-	fc_host_supported_fc4s(lport->host)[7] = 1;
-
-	/* This value is also unchanging */
-	memset(fc_host_active_fc4s(lport->host), 0,
-	       sizeof(fc_host_active_fc4s(lport->host)));
-	fc_host_active_fc4s(lport->host)[2] = 1;
-	fc_host_active_fc4s(lport->host)[7] = 1;
-	fc_host_maxframe_size(lport->host) = lport->mfs;
-	fc_host_supported_speeds(lport->host) = 0;
-	if (lport->link_supported_speeds & FC_PORTSPEED_1GBIT)
-		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;
-
 	return 0;
 }
 EXPORT_SYMBOL(fc_lport_init);
diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c
index dd2b43b..4154090 100644
--- a/drivers/scsi/libfc/fc_npiv.c
+++ b/drivers/scsi/libfc/fc_npiv.c
@@ -24,38 +24,32 @@
 #include <scsi/libfc.h>
 
 /**
- * fc_vport_create() - Create a new NPIV vport instance
+ * fc_vport_config() - Configure a new vport
  * @vport: fc_vport structure from scsi_transport_fc
  * @privsize: driver private data size to allocate along with the Scsi_Host
  */
-
-struct fc_lport *libfc_vport_create(struct fc_vport *vport, int privsize)
+int libfc_vport_config(struct fc_lport *n_port, struct fc_lport *vn_port,
+		       struct fc_fcvport *vport)
 {
-	struct Scsi_Host *shost = vport_to_shost(vport);
-	struct fc_lport *n_port = shost_priv(shost);
-	struct fc_lport *vn_port;
-
-	vn_port = libfc_host_alloc(shost->hostt, privsize);
-	if (!vn_port)
-		goto err_out;
 	if (fc_exch_mgr_list_clone(n_port, vn_port))
-		goto err_put;
+		return -ENOMEM;
 
-	vn_port->vport = vport;
-	vport->dd_data = vn_port;
+	vn_port->fcvport = vport;
+	vn_port->fcfabric = n_port->fcfabric;
+	vn_port->fcport = n_port->fcport;
+
+/*
+	FCOE_NETDEV_DBG(netdev, "Setting vport names, 0x%llX 0x%llX\n",
+			vport->node_name, vport->port_name);
+*/
 
 	mutex_lock(&n_port->lp_mutex);
 	list_add_tail(&vn_port->list, &n_port->vports);
 	mutex_unlock(&n_port->lp_mutex);
 
-	return vn_port;
-
-err_put:
-	scsi_host_put(vn_port->host);
-err_out:
-	return NULL;
+	return 0;
 }
-EXPORT_SYMBOL(libfc_vport_create);
+EXPORT_SYMBOL(libfc_vport_config);
 
 /**
  * fc_vport_id_lookup() - find NPIV lport that matches a given fabric ID
@@ -108,10 +102,11 @@ enum libfc_lport_mutex_class {
 static void __fc_vport_setlink(struct fc_lport *n_port,
 			       struct fc_lport *vn_port)
 {
-	struct fc_vport *vport = vn_port->vport;
+	struct fc_fcvport *vport = vn_port->fcvport;
 
-	if (vn_port->state == LPORT_ST_DISABLED)
+	if (vn_port->state == LPORT_ST_DISABLED) {
 		return;
+	}
 
 	if (n_port->state == LPORT_ST_READY) {
 		if (n_port->npiv_enabled) {
@@ -133,9 +128,8 @@ static void __fc_vport_setlink(struct fc_lport *n_port,
  */
 void fc_vport_setlink(struct fc_lport *vn_port)
 {
-	struct fc_vport *vport = vn_port->vport;
-	struct Scsi_Host *shost = vport_to_shost(vport);
-	struct fc_lport *n_port = shost_priv(shost);
+	struct fc_fcvport *vport = fc_fcfabric_find_nport(vn_port->fcfabric);
+	struct fc_lport *n_port = fcvport_priv(vport);
 
 	mutex_lock(&n_port->lp_mutex);
 	mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT);
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 39e440f..a1b445a 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -263,7 +263,9 @@ static void fc_rport_work(struct work_struct *work)
 		mutex_unlock(&rdata->rp_mutex);
 
 		if (!rport)
-			rport = fc_remote_port_add(lport->host, 0, &ids);
+			rport = fc_fcrport_add(lport->fcvport,
+					       lport->fcrport_f,
+					       0, &ids);
 		if (!rport) {
 			FC_RPORT_DBG(rdata, "Failed to add the rport\n");
 			lport->tt.rport_logoff(rdata);
@@ -334,7 +336,7 @@ static void fc_rport_work(struct work_struct *work)
 			mutex_lock(&rdata->rp_mutex);
 			rdata->rport = NULL;
 			mutex_unlock(&rdata->rp_mutex);
-			fc_remote_port_delete(rport);
+			fc_fcrport_del(rport);
 		}
 		if (restart) {
 			mutex_lock(&rdata->rp_mutex);
@@ -1113,13 +1115,14 @@ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
 
 {
 	struct fc_lport *lport = rdata->local_port;
+	struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
 	struct fc_frame *fp;
 	struct fc_exch *ep = fc_seq_exch(sp);
 	struct fc_els_rls *rls;
 	struct fc_els_rls_resp *rsp;
 	struct fc_els_lesb *lesb;
 	struct fc_seq_els_data rjt_data;
-	struct fc_host_statistics *hst;
+	struct fcpinit_statistics *hst;
 	u32 f_ctl;
 
 	FC_RPORT_DBG(rdata, "Received RLS request while in state %s\n",
@@ -1147,8 +1150,8 @@ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
 		/* get LESB from LLD if it supports it */
 		lport->tt.get_lesb(lport, lesb);
 	} else {
-		fc_get_host_stats(lport->host);
-		hst = &lport->host_stats;
+		fc_get_fcpinit_stats(lport->fcpinit);
+		hst = &si->fcpinit_stats;
 		lesb->lesb_link_fail = htonl(hst->link_failure_count);
 		lesb->lesb_sync_loss = htonl(hst->loss_of_sync_count);
 		lesb->lesb_sig_loss = htonl(hst->loss_of_signal_count);
@@ -1360,7 +1363,7 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
 		break;
 	case RPORT_ST_PLOGI:
 		FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI state\n");
-		if (rdata->ids.port_name < lport->wwpn) {
+		if (rdata->ids.port_name < fcvport_port_name(lport->fcvport)) {
 			mutex_unlock(&rdata->rp_mutex);
 			rjt_data.reason = ELS_RJT_INPROG;
 			rjt_data.explan = ELS_EXPL_NONE;
diff --git a/include/scsi/fc_encode.h b/include/scsi/fc_encode.h
index 9b4867c..bb5c2cc 100644
--- a/include/scsi/fc_encode.h
+++ b/include/scsi/fc_encode.h
@@ -72,8 +72,10 @@ static inline void fc_adisc_fill(struct fc_lport *lport, struct fc_frame *fp)
 	adisc = fc_frame_payload_get(fp, sizeof(*adisc));
 	memset(adisc, 0, sizeof(*adisc));
 	adisc->adisc_cmd = ELS_ADISC;
-	put_unaligned_be64(lport->wwpn, &adisc->adisc_wwpn);
-	put_unaligned_be64(lport->wwnn, &adisc->adisc_wwnn);
+	put_unaligned_be64(fcvport_port_name(lport->fcvport),
+			   &adisc->adisc_wwpn);
+	put_unaligned_be64(fcvport_node_name(lport->fcvport),
+			   &adisc->adisc_wwnn);
 	hton24(adisc->adisc_port_id, lport->port_id);
 }
 
@@ -144,24 +146,26 @@ static inline int fc_ct_fill(struct fc_lport *lport,
 	case FC_NS_RNN_ID:
 		ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rn_id));
 		hton24(ct->payload.rn.fr_fid.fp_fid, lport->port_id);
-		put_unaligned_be64(lport->wwnn, &ct->payload.rn.fr_wwn);
+		put_unaligned_be64(fcvport_node_name(lport->fcvport),
+				   &ct->payload.rn.fr_wwn);
 		break;
 
 	case FC_NS_RSPN_ID:
-		len = strnlen(fc_host_symbolic_name(lport->host), 255);
+		len = strnlen(fcvport_symbolic_name(lport->fcvport), 255);
 		ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rspn) + len);
 		hton24(ct->payload.spn.fr_fid.fp_fid, lport->port_id);
 		strncpy(ct->payload.spn.fr_name,
-			fc_host_symbolic_name(lport->host), len);
+			fcvport_symbolic_name(lport->fcvport), len);
 		ct->payload.spn.fr_name_len = len;
 		break;
 
 	case FC_NS_RSNN_NN:
-		len = strnlen(fc_host_symbolic_name(lport->host), 255);
+		len = strnlen(fcvport_symbolic_name(lport->fcvport), 255);
 		ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rsnn) + len);
-		put_unaligned_be64(lport->wwnn, &ct->payload.snn.fr_wwn);
+		put_unaligned_be64(fcvport_node_name(lport->fcvport),
+				   &ct->payload.snn.fr_wwn);
 		strncpy(ct->payload.snn.fr_name,
-			fc_host_symbolic_name(lport->host), len);
+			fcvport_symbolic_name(lport->fcvport), len);
 		ct->payload.snn.fr_name_len = len;
 		break;
 
@@ -186,8 +190,8 @@ static inline void fc_plogi_fill(struct fc_lport *lport, struct fc_frame *fp,
 	plogi = fc_frame_payload_get(fp, sizeof(*plogi));
 	memset(plogi, 0, sizeof(*plogi));
 	plogi->fl_cmd = (u8) op;
-	put_unaligned_be64(lport->wwpn, &plogi->fl_wwpn);
-	put_unaligned_be64(lport->wwnn, &plogi->fl_wwnn);
+	put_unaligned_be64(fcvport_port_name(lport->fcvport), &plogi->fl_wwpn);
+	put_unaligned_be64(fcvport_node_name(lport->fcvport), &plogi->fl_wwnn);
 
 	csp = &plogi->fl_csp;
 	csp->sp_hi_ver = 0x20;
@@ -218,8 +222,8 @@ static inline void fc_flogi_fill(struct fc_lport *lport, struct fc_frame *fp)
 	flogi = fc_frame_payload_get(fp, sizeof(*flogi));
 	memset(flogi, 0, sizeof(*flogi));
 	flogi->fl_cmd = (u8) ELS_FLOGI;
-	put_unaligned_be64(lport->wwpn, &flogi->fl_wwpn);
-	put_unaligned_be64(lport->wwnn, &flogi->fl_wwnn);
+	put_unaligned_be64(fcvport_port_name(lport->fcvport), &flogi->fl_wwpn);
+	put_unaligned_be64(fcvport_node_name(lport->fcvport), &flogi->fl_wwnn);
 	sp = &flogi->fl_csp;
 	sp->sp_hi_ver = 0x20;
 	sp->sp_lo_ver = 0x20;
@@ -243,8 +247,8 @@ static inline void fc_fdisc_fill(struct fc_lport *lport, struct fc_frame *fp)
 	fdisc = fc_frame_payload_get(fp, sizeof(*fdisc));
 	memset(fdisc, 0, sizeof(*fdisc));
 	fdisc->fl_cmd = (u8) ELS_FDISC;
-	put_unaligned_be64(lport->wwpn, &fdisc->fl_wwpn);
-	put_unaligned_be64(lport->wwnn, &fdisc->fl_wwnn);
+	put_unaligned_be64(fcvport_port_name(lport->fcvport), &fdisc->fl_wwpn);
+	put_unaligned_be64(fcvport_node_name(lport->fcvport), &fdisc->fl_wwnn);
 	sp = &fdisc->fl_csp;
 	sp->sp_hi_ver = 0x20;
 	sp->sp_lo_ver = 0x20;
@@ -265,7 +269,7 @@ static inline void fc_logo_fill(struct fc_lport *lport, struct fc_frame *fp)
 	memset(logo, 0, sizeof(*logo));
 	logo->fl_cmd = ELS_LOGO;
 	hton24(logo->fl_n_port_id, lport->port_id);
-	logo->fl_n_port_wwn = htonll(lport->wwpn);
+	logo->fl_n_port_wwn = htonll(fcvport_port_name(lport->fcvport));
 }
 
 /**
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 7495c0b..3e924fa 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -24,8 +24,9 @@
 #include <linux/if.h>
 #include <linux/percpu.h>
 
+#include <fc/fc.h>
 #include <scsi/scsi_transport.h>
-#include <scsi/scsi_transport_fc.h>
+#include <scsi/scsi_transport_fcp.h>
 #include <scsi/scsi_bsg_fc.h>
 
 #include <scsi/fc/fc_fcp.h>
@@ -35,6 +36,9 @@
 
 #include <scsi/fc_frame.h>
 
+#define fcpinit_to_lport(x)						\
+	fcvport_priv((struct fc_fcvport *)(fcpinit_to_fcvport(x)))
+
 /*
  * libfc error codes
  */
@@ -149,6 +153,7 @@ enum fc_rport_event {
 };
 
 struct fc_rport_priv;
+struct fc_fcp_internal;
 
 /**
  * struct fc_rport_operations - Operations for a remote port
@@ -725,6 +730,12 @@ struct libfc_function_template {
 	 * STATUS: OPTIONAL
 	 */
 	void (*disc_stop_final) (struct fc_lport *);
+
+	void (*set_fcvport_symbolic_name)(struct fc_lport *);
+	void (*set_fcvport_node_name)(struct fc_lport *);
+	void (*set_fcvport_port_name)(struct fc_lport *);
+	void (*get_vport_ids)(struct fc_lport *,
+			      struct fc_vport_identifiers *);
 };
 
 /**
@@ -805,24 +816,19 @@ struct fc_disc {
  */
 struct fc_lport {
 	/* Associations */
-	struct Scsi_Host	       *host;
 	struct list_head	       ema_list;
 	struct fc_rport_priv	       *dns_rdata;
 	struct fc_rport_priv	       *ptp_rdata;
-	void			       *scsi_priv;
 	struct fc_disc                 disc;
 
 	/* Virtual port information */
 	struct list_head	       vports;
-	struct fc_vport		       *vport;
 
 	/* Operational Information */
 	struct libfc_function_template tt;
 	u8			       link_up;
-	u8			       qfull;
 	enum fc_lport_state	       state;
 	unsigned long		       boot_time;
-	struct fc_host_statistics      host_stats;
 	struct fcoe_dev_stats	       *dev_stats;
 	u8			       retry_count;
 
@@ -830,6 +836,7 @@ struct fc_lport {
 	u32                            port_id;
 	u64			       wwpn;
 	u64			       wwnn;
+
 	unsigned int		       service_params;
 	unsigned int		       e_d_tov;
 	unsigned int		       r_a_tov;
@@ -851,12 +858,55 @@ struct fc_lport {
 	unsigned int		       lso_max;
 	struct fc_ns_fts	       fcts;
 
+	/* sysfs representation */
+	struct fc_fcport               *fcport;
+	struct fc_fcfabric             *fcfabric;
+	struct fc_fcvport              *fcvport;
+	struct fc_fcpinit              *fcpinit;
+
 	/* Miscellaneous */
 	struct mutex                   lp_mutex;
 	struct list_head               list;
 	struct delayed_work	       retry_work;
+
+	struct fcfabric_function_template *fcfabric_f;
+	struct fcvport_function_template *fcvport_f;
+	struct fcrport_function_template *fcrport_f;
+
+	void			       *scsi_priv;
+};
+
+/**
+ * struct fc_fcp_internal - FCP layer internal data
+ * @scsi_pkt_pool:  Memory pool to draw FCP packets from
+ * @scsi_pkt_queue: Current FCP packets
+ * @last_can_queue_ramp_down_time: ramp down time
+ * @last_can_queue_ramp_up_time: ramp up time
+ * @max_can_queue: max can_queue size
+ */
+struct fc_fcp_internal {
+	mempool_t	 *scsi_pkt_pool;
+	struct list_head scsi_pkt_queue;
+	spinlock_t       scsi_queue_lock;
+	unsigned long last_can_queue_ramp_down_time;
+	unsigned long last_can_queue_ramp_up_time;
+	int max_can_queue;
+
+	/* Associations */
+	struct fc_lport                *lport;
+	struct Scsi_Host	       *host;
+
+	/* Operational Information */
+	u8			       qfull;
+	struct fcpinit_statistics      fcpinit_stats;
 };
 
+#define lport_to_fcvport(x)			\
+	(struct fc_fcvport *)((x)->fcvport)
+
+#define fc_get_scsi_internal(x)						\
+	((struct fc_fcp_internal *)(fcpinit_priv(fcvport_to_fcpinit(lport_to_fcvport(x)))))
+
 /*
  * FC_LPORT HELPER FUNCTIONS
  *****************************/
@@ -871,26 +921,6 @@ static inline int fc_lport_test_ready(struct fc_lport *lport)
 }
 
 /**
- * fc_set_wwnn() - Set the World Wide Node Name of a local port
- * @lport: The local port whose WWNN is to be set
- * @wwnn:  The new WWNN
- */
-static inline void fc_set_wwnn(struct fc_lport *lport, u64 wwnn)
-{
-	lport->wwnn = wwnn;
-}
-
-/**
- * fc_set_wwpn() - Set the World Wide Port Name of a local port
- * @lport: The local port whose WWPN is to be set
- * @wwnn:  The new WWPN
- */
-static inline void fc_set_wwpn(struct fc_lport *lport, u64 wwnn)
-{
-	lport->wwpn = wwnn;
-}
-
-/**
  * fc_lport_state_enter() - Change a local port's state
  * @lport: The local port whose state is to change
  * @state: The new state
@@ -933,30 +963,6 @@ static inline void *lport_priv(const struct fc_lport *lport)
 	return (void *)(lport + 1);
 }
 
-/**
- * libfc_host_alloc() - Allocate a Scsi_Host with room for a local port and
- *                      LLD private data
- * @sht:       The SCSI host template
- * @priv_size: Size of private data
- *
- * Returns: libfc lport
- */
-static inline struct fc_lport *
-libfc_host_alloc(struct scsi_host_template *sht, int priv_size)
-{
-	struct fc_lport *lport;
-	struct Scsi_Host *shost;
-
-	shost = scsi_host_alloc(sht, sizeof(*lport) + priv_size);
-	if (!shost)
-		return NULL;
-	lport = shost_priv(shost);
-	lport->host = shost;
-	INIT_LIST_HEAD(&lport->ema_list);
-	INIT_LIST_HEAD(&lport->vports);
-	return lport;
-}
-
 /*
  * FC_FCP HELPER FUNCTIONS
  *****************************/
@@ -983,7 +989,8 @@ void fc_vports_linkchange(struct fc_lport *);
 int fc_lport_config(struct fc_lport *);
 int fc_lport_reset(struct fc_lport *);
 int fc_set_mfs(struct fc_lport *, u32 mfs);
-struct fc_lport *libfc_vport_create(struct fc_vport *, int privsize);
+int libfc_vport_config(struct fc_lport *n_port, struct fc_lport *vn_port,
+		       struct fc_fcvport *);
 struct fc_lport *fc_vport_id_lookup(struct fc_lport *, u32 port_id);
 int fc_lport_bsg_request(struct fc_bsg_job *);
 
@@ -1001,7 +1008,7 @@ int fc_disc_init(struct fc_lport *);
 /*
  * FCP LAYER
  *****************************/
-int fc_fcp_init(struct fc_lport *);
+int fc_fcp_init(struct fc_fcpinit *);
 void fc_fcp_destroy(struct fc_lport *);
 
 /*
@@ -1050,9 +1057,11 @@ void fc_exch_mgr_reset(struct fc_lport *, u32 s_id, u32 d_id);
 /*
  * Functions for fc_functions_template
  */
-void fc_get_host_speed(struct Scsi_Host *);
-void fc_get_host_port_state(struct Scsi_Host *);
+void fc_get_fcpinit_port_state(struct fc_fcpinit *);
 void fc_set_rport_loss_tmo(struct fc_rport *, u32 timeout);
-struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *);
+struct fcpinit_statistics *fc_get_fcpinit_stats(struct fc_fcpinit *);
+
+
+void fc_lport_port_config(struct fc_fcport *fcport);
 
 #endif /* _LIBFC_H_ */
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index ec13f51..c901b2f 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -114,11 +114,19 @@ struct fcoe_ctlr {
 	u8 dest_addr[ETH_ALEN];
 	u8 ctl_src_addr[ETH_ALEN];
 
+	char work_q_name[20];
+	struct workqueue_struct *work_q;
+
 	void (*send)(struct fcoe_ctlr *, struct sk_buff *);
 	void (*update_mac)(struct fc_lport *, u8 *addr);
 	u8 * (*get_src_addr)(struct fc_lport *);
 	spinlock_t lock;
 };
+#define fcoe_ctlr_work_q(x)			\
+	((x)->work_q)
+#define fcoe_ctlr_work_q_name(x)		\
+	((x)->work_q_name)
+
 
 /**
  * struct fcoe_fcf - Fibre-Channel Forwarder
@@ -154,6 +162,13 @@ struct fcoe_fcf {
 	u16 flags;
 	u32 fka_period;
 	u8 fd_flags:1;
+
+	struct fc_fcfabric *fcfabric;
+	struct fcfabric_function_template *fcfabric_f;
+	struct fcvport_function_template *fcvport_f;
+	struct fc_fcport *fcport;
+
+	struct work_struct fcf_work;
 };
 
 /* FIP API functions */
@@ -168,6 +183,6 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *, struct fc_lport *,
 
 /* libfcoe funcs */
 u64 fcoe_wwn_from_mac(unsigned char mac[], unsigned int, unsigned int);
-int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
+void fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
 
 #endif /* _LIBFCOE_H */


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

* Re: [RFC PATCH 0/3] FC subsystem update
  2010-05-12 20:20 [RFC PATCH 0/3] FC subsystem update Robert Love
                   ` (2 preceding siblings ...)
  2010-05-12 20:20 ` [RFC PATCH 3/3] libfc, libfcoe, fcoe: Make use of FC subsystem Robert Love
@ 2010-05-13  0:15 ` Joe Eykholt
  2010-05-13  0:41   ` Robert Love
  3 siblings, 1 reply; 8+ messages in thread
From: Joe Eykholt @ 2010-05-13  0:15 UTC (permalink / raw)
  To: Robert Love
  Cc: linux-scsi, james.smart, hare, christof.schmitt, james.bottomley

Robert Love wrote:
> This series represents the continuation of the RFC archived here:
> http://marc.info/?l=linux-scsi&m=126463466126962&w=2. The sysfs organization has
> taken shape and object allocations make more sense than the previous RFC. The
> patches have undergone my own developer testing, which has mostly focused on
> NPIV port creation and deletion and some I/O.
> 
> This series creates a Fibre Channel subsystem that has an improved sysfs device
> tree (the port's view of the fabric) and begins to abstract itself from SCSI.
> 
> This RFC is to solicit any feedback before I continue to refine these patches.
> I've created two diagrams to help people understand the change. The first shows
> the current sysfs device layout and the second shows the allocation scheme for
> libfc and fcoe's primary data structures.
> 
> http://open-fcoe.org/rwlove/fc_sysfs.jpg
> http://open-fcoe.org/rwlove/libfc_alloc.jpg
> 
> These patches apply to scsi-misc + the recently submitted fcoe patches, which
> is the same as the fcoe-next tree on open-fcoe.org.
> 
> Overview of N_Port creation/login (for fcoe.ko):
> 
> 1) First, fc.ko is installed and then scsi_transport_fcp.ko is installed.
>    scsi_transport_fcp.ko registers itself (SCSI initiator) as a FC4 with
>    fc.ko.
> 
> 2) When a fcoe port is "created" from
>    'echo ethX > /sys/module/fcoe/parameters/create' a fcport is allocated and
>    added to sysfs. All of the port's physical properties are added as
>    attributes.
> 
> <PCI root>/fcport_0
> 
> 3) At this point libfc/libfcoe/fcoe needs an lport and an fcoe_port. The lport
>    and fcoe_ports are private data of a fcvport so a fcvport is allocated,
>    but not added to sysfs.
> 
> 4) libfcoe.ko discovers FCFs and allocates/adds them as fcfabrics to sysfs
>    as they are discovered.
> 
>    [ I think that we may want to revisit the idea of having a fcfport between
>      the fcport and fcfabric. Right now I'm creating a fcfabric for each FCF
>      that is discovered at the FIP phase, but that doesn't make much sense
>      since multiple FCFs on the same fabric can be discovered. Adding a fcfport
>      for each FCF discovered might be more correct. ]
> 
> <PCI root>/fcport_0/fcfabric_0
> 
> 5) libfc.ko will FLOGI into the fabric and upon the FLOGI ACC the fcvport
>    will be added to sysfs.
> 
> <PCI root>/fcport_0/fcfabric_0/fcvport_0
> 
> 5.1) The FC layer will call fc4_init_add in the fc4_template. If SCSI
>      is the FC4, fcp_init_add (scsi_transport_fcp) will be called. This
>      routine calls scsi_host_alloc/scsi_add_host. There is also a callback
>      down to the LLD so that it can setup any SCSI-FCP private data.
> 
>      [ I'm not sure how non-FCoE HBAs will do this. I imagine they will just
>        create all three sysfs devices (fcport, fcfabric and fcvport) on the
>        FLOGI ACC since they do not rely on a discovery protocol. ]
> 
> <PCI root>/fcport_0/fcfabric_0/fcvport_0/fcpinit_X/hostX
> 
> 6) libfc will start discovery and as remote ports are discovered libfc will
>    notify FC to create and add fcrports to sysfs.
> 
> <PCI root>/fcport_0/fcfabric_0/fcvport_0/fcpinit_X/hostX
> 					/fcrport_0
> 
> 6.1) When the fcrports are created the FC layer will call fc4_targ_add in
>      the fc4_template. If SCSI is the FC4, fcp_targ_add (scsi_transport_fcp)
>      will be called. This routine will allocate a fcptarg device, add it to
>      sysfs and notify SCSI to scan it.
> 
> <PCI root>/fcport_0/fcfabric_0/fcvport_0/fcpinit_X/hostX
> 					/fcrport_0/fcptargX:0-0
> 
> 
> The current series shortcomings are:
> 
> 1) The FC4 interface needs work to become more FC4 agnostic.
>    (end of include/fc/fc.h)
> 
> 2) Point-to-Point mode needs to be addressed
> 
> 3) BSG needs to be tested and STGT should become another FC4 that is
>    registered with FC. Since libfc/fcoe doesn't plug into STGT yet this
>    will be a challenge.
> 
> 4) user space updates - For the fcoe-utils package, libhbalinux (user
>    space HBA API library) needs to be updated to read the new layout before
>    heavy testing of the new layout can be done.
> 
> 5) Miscellaneous problems like error handling, fcfabric deletion cleaning
>    up vports, using attribute containers / scsi transport style for
>    SCSI-FCP devices, warnings, etc...
> 
> 6) FCoE attributes...
> 
> Other thoughts:
> 
> I would love to collaborate with someone who maintains a HBA that supports
> native FC since I'm not aware of the specific needs of traditional HBAs.
> 
> I would hope that this new layout could be added to the kernel (when
> ready) and the existing FC Transport deprecated as I'm not sure how to get
> all HBA maintainers to switch their drivers at the same time. I think that
> only adding FCoE attributes to this layout would be an incentive to get LLDs
> to migrate.
> 
> I've tried to keep this covermail brief, so there are plenty of details
> left out. Please ask if you have a question.
> 
> Thanks, //Rob

<snip>

Hi Rob,

I'm running the patches and they work OK (very lightly used).

I'd prefer we kept the naming in /sys/class shorter and keep underscores
after "fc" like we currently have in fc_remote_port.  Maybe you could
continue to use the existing names where they apply, after getting the
other transport code converted.

Here are my suggested name changes:

	fcvport becomes fc_vport
	fcvport/fcvport_0 becomes fc_vport/0
	fcpinit_4 becomes fcp_init4
	fcptarg-X:0-1 becomes fcp_targ/1
	fcrport:xxx-0 becomes fc_rport/0 (or fc_remote_ports/xxx eventually)
	fcfabric/fcfabric_0 becomes fc_fabric/0
	fcport/fcport_0 becomes fc_port/0

When we have local FCP target ports (as opposed to remote ones) how will we
represent that?  You could include that in the diagram.

When we see remote initiators, how do we show them?  Maybe we just don't
create anything under fcport_N in that case.

I may have more comments later after I look through the code
a bit further.  I'm modifying fcc to handle the new trees.

Minor issues:  I noticed that fnic no longer compiles and checkpatch
spots some issues, but I'm sure those will be fixed later.

	Regards,
	Joe


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

* Re: [RFC PATCH 0/3] FC subsystem update
  2010-05-13  0:15 ` [RFC PATCH 0/3] FC subsystem update Joe Eykholt
@ 2010-05-13  0:41   ` Robert Love
  2010-05-13  0:55     ` Joe Eykholt
  0 siblings, 1 reply; 8+ messages in thread
From: Robert Love @ 2010-05-13  0:41 UTC (permalink / raw)
  To: Joe Eykholt
  Cc: linux-scsi@vger.kernel.org, james.smart@emulex.com, hare@suse.de,
	christof.schmitt@de.ibm.com, james.bottomley@suse.de

On Wed, 2010-05-12 at 17:15 -0700, Joe Eykholt wrote:
> Robert Love wrote:
> > This series represents the continuation of the RFC archived here:
> > http://marc.info/?l=linux-scsi&m=126463466126962&w=2. The sysfs organization has
> > taken shape and object allocations make more sense than the previous RFC. The
> > patches have undergone my own developer testing, which has mostly focused on
> > NPIV port creation and deletion and some I/O.
> > 
> > This series creates a Fibre Channel subsystem that has an improved sysfs device
> > tree (the port's view of the fabric) and begins to abstract itself from SCSI.
> > 
> > This RFC is to solicit any feedback before I continue to refine these patches.
> > I've created two diagrams to help people understand the change. The first shows
> > the current sysfs device layout and the second shows the allocation scheme for
> > libfc and fcoe's primary data structures.
> > 
> > http://open-fcoe.org/rwlove/fc_sysfs.jpg
> > http://open-fcoe.org/rwlove/libfc_alloc.jpg
> > 
> > These patches apply to scsi-misc + the recently submitted fcoe patches, which
> > is the same as the fcoe-next tree on open-fcoe.org.
> > 
> > Overview of N_Port creation/login (for fcoe.ko):
> > 
> > 1) First, fc.ko is installed and then scsi_transport_fcp.ko is installed.
> >    scsi_transport_fcp.ko registers itself (SCSI initiator) as a FC4 with
> >    fc.ko.
> > 
> > 2) When a fcoe port is "created" from
> >    'echo ethX > /sys/module/fcoe/parameters/create' a fcport is allocated and
> >    added to sysfs. All of the port's physical properties are added as
> >    attributes.
> > 
> > <PCI root>/fcport_0
> > 
> > 3) At this point libfc/libfcoe/fcoe needs an lport and an fcoe_port. The lport
> >    and fcoe_ports are private data of a fcvport so a fcvport is allocated,
> >    but not added to sysfs.
> > 
> > 4) libfcoe.ko discovers FCFs and allocates/adds them as fcfabrics to sysfs
> >    as they are discovered.
> > 
> >    [ I think that we may want to revisit the idea of having a fcfport between
> >      the fcport and fcfabric. Right now I'm creating a fcfabric for each FCF
> >      that is discovered at the FIP phase, but that doesn't make much sense
> >      since multiple FCFs on the same fabric can be discovered. Adding a fcfport
> >      for each FCF discovered might be more correct. ]
> > 
> > <PCI root>/fcport_0/fcfabric_0
> > 
> > 5) libfc.ko will FLOGI into the fabric and upon the FLOGI ACC the fcvport
> >    will be added to sysfs.
> > 
> > <PCI root>/fcport_0/fcfabric_0/fcvport_0
> > 
> > 5.1) The FC layer will call fc4_init_add in the fc4_template. If SCSI
> >      is the FC4, fcp_init_add (scsi_transport_fcp) will be called. This
> >      routine calls scsi_host_alloc/scsi_add_host. There is also a callback
> >      down to the LLD so that it can setup any SCSI-FCP private data.
> > 
> >      [ I'm not sure how non-FCoE HBAs will do this. I imagine they will just
> >        create all three sysfs devices (fcport, fcfabric and fcvport) on the
> >        FLOGI ACC since they do not rely on a discovery protocol. ]
> > 
> > <PCI root>/fcport_0/fcfabric_0/fcvport_0/fcpinit_X/hostX
> > 
> > 6) libfc will start discovery and as remote ports are discovered libfc will
> >    notify FC to create and add fcrports to sysfs.
> > 
> > <PCI root>/fcport_0/fcfabric_0/fcvport_0/fcpinit_X/hostX
> > 					/fcrport_0
> > 
> > 6.1) When the fcrports are created the FC layer will call fc4_targ_add in
> >      the fc4_template. If SCSI is the FC4, fcp_targ_add (scsi_transport_fcp)
> >      will be called. This routine will allocate a fcptarg device, add it to
> >      sysfs and notify SCSI to scan it.
> > 
> > <PCI root>/fcport_0/fcfabric_0/fcvport_0/fcpinit_X/hostX
> > 					/fcrport_0/fcptargX:0-0
> > 
> > 
> > The current series shortcomings are:
> > 
> > 1) The FC4 interface needs work to become more FC4 agnostic.
> >    (end of include/fc/fc.h)
> > 
> > 2) Point-to-Point mode needs to be addressed
> > 
> > 3) BSG needs to be tested and STGT should become another FC4 that is
> >    registered with FC. Since libfc/fcoe doesn't plug into STGT yet this
> >    will be a challenge.
> > 
> > 4) user space updates - For the fcoe-utils package, libhbalinux (user
> >    space HBA API library) needs to be updated to read the new layout before
> >    heavy testing of the new layout can be done.
> > 
> > 5) Miscellaneous problems like error handling, fcfabric deletion cleaning
> >    up vports, using attribute containers / scsi transport style for
> >    SCSI-FCP devices, warnings, etc...
> > 
> > 6) FCoE attributes...
> > 
> > Other thoughts:
> > 
> > I would love to collaborate with someone who maintains a HBA that supports
> > native FC since I'm not aware of the specific needs of traditional HBAs.
> > 
> > I would hope that this new layout could be added to the kernel (when
> > ready) and the existing FC Transport deprecated as I'm not sure how to get
> > all HBA maintainers to switch their drivers at the same time. I think that
> > only adding FCoE attributes to this layout would be an incentive to get LLDs
> > to migrate.
> > 
> > I've tried to keep this covermail brief, so there are plenty of details
> > left out. Please ask if you have a question.
> > 
> > Thanks, //Rob
> 
> <snip>
> 
> Hi Rob,
> 
Hi Joe, thanks for taking a look.

> I'm running the patches and they work OK (very lightly used).
> 
> I'd prefer we kept the naming in /sys/class shorter and keep underscores
> after "fc" like we currently have in fc_remote_port.  Maybe you could
> continue to use the existing names where they apply, after getting the
> other transport code converted.
> 
> Here are my suggested name changes:
> 
> 	fcvport becomes fc_vport
> 	fcvport/fcvport_0 becomes fc_vport/0
> 	fcpinit_4 becomes fcp_init4
> 	fcptarg-X:0-1 becomes fcp_targ/1
> 	fcrport:xxx-0 becomes fc_rport/0 (or fc_remote_ports/xxx eventually)
> 	fcfabric/fcfabric_0 becomes fc_fabric/0
> 	fcport/fcport_0 becomes fc_port/0
> 
Sure, that sounds reasonable. I think I've got too much fc_fc* stuff in
general- device names, function names, etc...

> When we have local FCP target ports (as opposed to remote ones) how will we
> represent that?  You could include that in the diagram.
> 
This is something that I've got to figure out so that I don't lose any
existing functionality. I need a way to test whatever scheme is agreed
upon and since stgt isn't integrated with libfc/fcoe I'll either need to
integrate it or use one of the out-of-tree target infrastructures to
create local target ports.

> When we see remote initiators, how do we show them?  Maybe we just don't
> create anything under fcport_N in that case.
> 
The FC layer could create an fc_rport and notify the registered FC4. I
haven't really thought out if the SCSI-FCP layer would do anything
though.

Another related question is if a discovered target is represented by the
same device/structure when the host is an initiator or target. The same
question applies to discovered initiators.

> I may have more comments later after I look through the code
> a bit further.  I'm modifying fcc to handle the new trees.
> 
Very cool. Let me know when you have it done as I'd love to use it for
testing.

> Minor issues:  I noticed that fnic no longer compiles and checkpatch
> spots some issues, but I'm sure those will be fixed later.

Yeah, sorry about fnic. :)

I tried to clean up a lot of the checkpatch warnings, many were the
result of code moved from scsi_transport_fc, so I tried to clean up all
of the issues that I introduced and some of the existing ones. I'll
clean up all that stuff once this code matures.



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

* Re: [RFC PATCH 0/3] FC subsystem update
  2010-05-13  0:41   ` Robert Love
@ 2010-05-13  0:55     ` Joe Eykholt
  2010-05-13  2:41       ` Nicholas A. Bellinger
  0 siblings, 1 reply; 8+ messages in thread
From: Joe Eykholt @ 2010-05-13  0:55 UTC (permalink / raw)
  To: Robert Love
  Cc: linux-scsi@vger.kernel.org, james.smart@emulex.com, hare@suse.de,
	christof.schmitt@de.ibm.com, james.bottomley@suse.de

Robert Love wrote:
> On Wed, 2010-05-12 at 17:15 -0700, Joe Eykholt wrote:
>> Robert Love wrote:
>>> This series represents the continuation of the RFC archived here:
>>> http://marc.info/?l=linux-scsi&m=126463466126962&w=2. The sysfs organization has
>>> taken shape and object allocations make more sense than the previous RFC. The
>>> patches have undergone my own developer testing, which has mostly focused on
>>> NPIV port creation and deletion and some I/O.
>>>
>>> This series creates a Fibre Channel subsystem that has an improved sysfs device
>>> tree (the port's view of the fabric) and begins to abstract itself from SCSI.
>>>
>>> This RFC is to solicit any feedback before I continue to refine these patches.
>>> I've created two diagrams to help people understand the change. The first shows
>>> the current sysfs device layout and the second shows the allocation scheme for
>>> libfc and fcoe's primary data structures.
>>>
>>> http://open-fcoe.org/rwlove/fc_sysfs.jpg
>>> http://open-fcoe.org/rwlove/libfc_alloc.jpg
>>>
>>> These patches apply to scsi-misc + the recently submitted fcoe patches, which
>>> is the same as the fcoe-next tree on open-fcoe.org.
>>>
>>> Overview of N_Port creation/login (for fcoe.ko):
>>>
>>> 1) First, fc.ko is installed and then scsi_transport_fcp.ko is installed.
>>>    scsi_transport_fcp.ko registers itself (SCSI initiator) as a FC4 with
>>>    fc.ko.
>>>
>>> 2) When a fcoe port is "created" from
>>>    'echo ethX > /sys/module/fcoe/parameters/create' a fcport is allocated and
>>>    added to sysfs. All of the port's physical properties are added as
>>>    attributes.
>>>
>>> <PCI root>/fcport_0
>>>
>>> 3) At this point libfc/libfcoe/fcoe needs an lport and an fcoe_port. The lport
>>>    and fcoe_ports are private data of a fcvport so a fcvport is allocated,
>>>    but not added to sysfs.
>>>
>>> 4) libfcoe.ko discovers FCFs and allocates/adds them as fcfabrics to sysfs
>>>    as they are discovered.
>>>
>>>    [ I think that we may want to revisit the idea of having a fcfport between
>>>      the fcport and fcfabric. Right now I'm creating a fcfabric for each FCF
>>>      that is discovered at the FIP phase, but that doesn't make much sense
>>>      since multiple FCFs on the same fabric can be discovered. Adding a fcfport
>>>      for each FCF discovered might be more correct. ]
>>>
>>> <PCI root>/fcport_0/fcfabric_0
>>>
>>> 5) libfc.ko will FLOGI into the fabric and upon the FLOGI ACC the fcvport
>>>    will be added to sysfs.
>>>
>>> <PCI root>/fcport_0/fcfabric_0/fcvport_0
>>>
>>> 5.1) The FC layer will call fc4_init_add in the fc4_template. If SCSI
>>>      is the FC4, fcp_init_add (scsi_transport_fcp) will be called. This
>>>      routine calls scsi_host_alloc/scsi_add_host. There is also a callback
>>>      down to the LLD so that it can setup any SCSI-FCP private data.
>>>
>>>      [ I'm not sure how non-FCoE HBAs will do this. I imagine they will just
>>>        create all three sysfs devices (fcport, fcfabric and fcvport) on the
>>>        FLOGI ACC since they do not rely on a discovery protocol. ]
>>>
>>> <PCI root>/fcport_0/fcfabric_0/fcvport_0/fcpinit_X/hostX
>>>
>>> 6) libfc will start discovery and as remote ports are discovered libfc will
>>>    notify FC to create and add fcrports to sysfs.
>>>
>>> <PCI root>/fcport_0/fcfabric_0/fcvport_0/fcpinit_X/hostX
>>> 					/fcrport_0
>>>
>>> 6.1) When the fcrports are created the FC layer will call fc4_targ_add in
>>>      the fc4_template. If SCSI is the FC4, fcp_targ_add (scsi_transport_fcp)
>>>      will be called. This routine will allocate a fcptarg device, add it to
>>>      sysfs and notify SCSI to scan it.
>>>
>>> <PCI root>/fcport_0/fcfabric_0/fcvport_0/fcpinit_X/hostX
>>> 					/fcrport_0/fcptargX:0-0
>>>
>>>
>>> The current series shortcomings are:
>>>
>>> 1) The FC4 interface needs work to become more FC4 agnostic.
>>>    (end of include/fc/fc.h)
>>>
>>> 2) Point-to-Point mode needs to be addressed
>>>
>>> 3) BSG needs to be tested and STGT should become another FC4 that is
>>>    registered with FC. Since libfc/fcoe doesn't plug into STGT yet this
>>>    will be a challenge.
>>>
>>> 4) user space updates - For the fcoe-utils package, libhbalinux (user
>>>    space HBA API library) needs to be updated to read the new layout before
>>>    heavy testing of the new layout can be done.
>>>
>>> 5) Miscellaneous problems like error handling, fcfabric deletion cleaning
>>>    up vports, using attribute containers / scsi transport style for
>>>    SCSI-FCP devices, warnings, etc...
>>>
>>> 6) FCoE attributes...
>>>
>>> Other thoughts:
>>>
>>> I would love to collaborate with someone who maintains a HBA that supports
>>> native FC since I'm not aware of the specific needs of traditional HBAs.
>>>
>>> I would hope that this new layout could be added to the kernel (when
>>> ready) and the existing FC Transport deprecated as I'm not sure how to get
>>> all HBA maintainers to switch their drivers at the same time. I think that
>>> only adding FCoE attributes to this layout would be an incentive to get LLDs
>>> to migrate.
>>>
>>> I've tried to keep this covermail brief, so there are plenty of details
>>> left out. Please ask if you have a question.
>>>
>>> Thanks, //Rob
>> <snip>
>>
>> Hi Rob,
>>
> Hi Joe, thanks for taking a look.
> 
>> I'm running the patches and they work OK (very lightly used).
>>
>> I'd prefer we kept the naming in /sys/class shorter and keep underscores
>> after "fc" like we currently have in fc_remote_port.  Maybe you could
>> continue to use the existing names where they apply, after getting the
>> other transport code converted.
>>
>> Here are my suggested name changes:
>>
>> 	fcvport becomes fc_vport
>> 	fcvport/fcvport_0 becomes fc_vport/0
>> 	fcpinit_4 becomes fcp_init4
>> 	fcptarg-X:0-1 becomes fcp_targ/1
>> 	fcrport:xxx-0 becomes fc_rport/0 (or fc_remote_ports/xxx eventually)
>> 	fcfabric/fcfabric_0 becomes fc_fabric/0
>> 	fcport/fcport_0 becomes fc_port/0
>>
> Sure, that sounds reasonable. I think I've got too much fc_fc* stuff in
> general- device names, function names, etc...
> 
>> When we have local FCP target ports (as opposed to remote ones) how will we
>> represent that?  You could include that in the diagram.
>>
> This is something that I've got to figure out so that I don't lose any
> existing functionality. I need a way to test whatever scheme is agreed
> upon and since stgt isn't integrated with libfc/fcoe I'll either need to
> integrate it or use one of the out-of-tree target infrastructures to
> create local target ports.

You wouldn't have to implement it, I just wanted to know what the
names would be.  Depending on the target module, they each
have their own management, and if/when they get into the kernel,
we can add something to this scheme.  scsi_tgt, being all
in user land, doesn't even show up in /sys.

>> When we see remote initiators, how do we show them?  Maybe we just don't
>> create anything under fcport_N in that case.
>>
> The FC layer could create an fc_rport and notify the registered FC4. I
> haven't really thought out if the SCSI-FCP layer would do anything
> though.
> 
> Another related question is if a discovered target is represented by the
> same device/structure when the host is an initiator or target. The same
> question applies to discovered initiators.
> 
>> I may have more comments later after I look through the code
>> a bit further.  I'm modifying fcc to handle the new trees.
>>
> Very cool. Let me know when you have it done as I'd love to use it for
> testing.
> 
>> Minor issues:  I noticed that fnic no longer compiles and checkpatch
>> spots some issues, but I'm sure those will be fixed later.
> 
> Yeah, sorry about fnic. :)

No biggie.  It makes sense to convert one LLD first and then then other.

> I tried to clean up a lot of the checkpatch warnings, many were the
> result of code moved from scsi_transport_fc, so I tried to clean up all
> of the issues that I introduced and some of the existing ones. I'll
> clean up all that stuff once this code matures.

Oh, right.  I remember that now.  Maybe checkpatch should be
smart enough to realize the code had just moved ... not easy, though.

	Regards,
	Joe

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

* Re: [RFC PATCH 0/3] FC subsystem update
  2010-05-13  0:55     ` Joe Eykholt
@ 2010-05-13  2:41       ` Nicholas A. Bellinger
  0 siblings, 0 replies; 8+ messages in thread
From: Nicholas A. Bellinger @ 2010-05-13  2:41 UTC (permalink / raw)
  To: Joe Eykholt
  Cc: Robert Love, linux-scsi@vger.kernel.org, james.smart@emulex.com,
	hare@suse.de, christof.schmitt@de.ibm.com,
	james.bottomley@suse.de

On Wed, 2010-05-12 at 17:55 -0700, Joe Eykholt wrote:
> Robert Love wrote:
> > On Wed, 2010-05-12 at 17:15 -0700, Joe Eykholt wrote:
> >> Robert Love wrote:
> >>> This series represents the continuation of the RFC archived here:
> >>> http://marc.info/?l=linux-scsi&m=126463466126962&w=2. The sysfs organization has
> >>> taken shape and object allocations make more sense than the previous RFC. The
> >>> patches have undergone my own developer testing, which has mostly focused on
> >>> NPIV port creation and deletion and some I/O.
> >>>
> >>> This series creates a Fibre Channel subsystem that has an improved sysfs device
> >>> tree (the port's view of the fabric) and begins to abstract itself from SCSI.
> >>>
> >>> This RFC is to solicit any feedback before I continue to refine these patches.
> >>> I've created two diagrams to help people understand the change. The first shows
> >>> the current sysfs device layout and the second shows the allocation scheme for
> >>> libfc and fcoe's primary data structures.
> >>>
> >>> http://open-fcoe.org/rwlove/fc_sysfs.jpg
> >>> http://open-fcoe.org/rwlove/libfc_alloc.jpg
> >>>

<SNIP>

> >>
> >> Here are my suggested name changes:
> >>
> >> 	fcvport becomes fc_vport
> >> 	fcvport/fcvport_0 becomes fc_vport/0
> >> 	fcpinit_4 becomes fcp_init4
> >> 	fcptarg-X:0-1 becomes fcp_targ/1
> >> 	fcrport:xxx-0 becomes fc_rport/0 (or fc_remote_ports/xxx eventually)
> >> 	fcfabric/fcfabric_0 becomes fc_fabric/0
> >> 	fcport/fcport_0 becomes fc_port/0
> >>
> > Sure, that sounds reasonable. I think I've got too much fc_fc* stuff in
> > general- device names, function names, etc...
> > 
> >> When we have local FCP target ports (as opposed to remote ones) how will we
> >> represent that?  You could include that in the diagram.
> >>
> > This is something that I've got to figure out so that I don't lose any
> > existing functionality. I need a way to test whatever scheme is agreed
> > upon and since stgt isn't integrated with libfc/fcoe I'll either need to
> > integrate it or use one of the out-of-tree target infrastructures to
> > create local target ports.
> 
> You wouldn't have to implement it, I just wanted to know what the
> names would be.  Depending on the target module, they each
> have their own management, and if/when they get into the kernel,
> we can add something to this scheme.  scsi_tgt, being all
> in user land, doesn't even show up in /sys.
> 

Hi Joe and Robert,

So for FC capable target fabric modules, one of this things that would
be really useful is a method exposed by libfc to allow the explict
creation/release of local FCP target ports as they are configured on the
fly by TCM_FC with configfs.  Currently when a individual lport WWPN is
created with:

    mkdir -p /sys/kernel/config/target/fc/XX:XX:XX:XX:XX:XX:XX:XX

the internal struct ft_lport_acl gets allocated, but we cannot tell if
the passed lport WWPN contains a reference to a underlying piece of FC
or FCoE capable hardware.  This is of course because the TCM_FC code is
driven by Joe's patched libfc that forwards PLOGI into struct
fc4_prov->ft_prli() that search for a matching struct ft_lport_acl->wwpn
to reference a individual TCM_FC target port.

So for making libfc explictly aware of externally configured FCP target
ports, we would need something like:

*) A function that accepts a TCM_FC provided lport WWPN string and
previously registered struct fc4_prov pointer that returns reference to
a libfc target port descriptor. (eg: looks matching hardware WWPN)

*) A function thats then accepts the above libfc target port descriptor
and creates FCP target port group+attributes in sysfs once the TCM_FC
lport has been successfully configured.

*) And a function to explictly release the FCP target port.  This order
is obviously going to depend a great deal on what target logic that
libfc ends up exposing.

I know that Joe's original patches to provide target mode hooks for
struct fc4_prov for I_T Nexus and I/O path logic still need to be
resolved for upstream, but I think having libfc support some form of
explict FCP target port configuration accessable by fabric modules would
be a reasonable next step.

Best,

--nab


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

end of thread, other threads:[~2010-05-13  2:41 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-05-12 20:20 [RFC PATCH 0/3] FC subsystem update Robert Love
2010-05-12 20:20 ` [RFC PATCH 1/3] fc: Create FC sybsystem Robert Love
2010-05-12 20:20 ` [RFC PATCH 2/3] scsi_transport_fcp: Create FC/SCSI interaction layer Robert Love
2010-05-12 20:20 ` [RFC PATCH 3/3] libfc, libfcoe, fcoe: Make use of FC subsystem Robert Love
2010-05-13  0:15 ` [RFC PATCH 0/3] FC subsystem update Joe Eykholt
2010-05-13  0:41   ` Robert Love
2010-05-13  0:55     ` Joe Eykholt
2010-05-13  2:41       ` Nicholas A. Bellinger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).