* [RFC PATCH v2 00/10] FC Sysfs and FCoE attributes
@ 2011-03-11 21:54 Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 01/10] libfcoe: Remove mutex_trylock/restart_syscall checks Robert Love
` (9 more replies)
0 siblings, 10 replies; 11+ messages in thread
From: Robert Love @ 2011-03-11 21:54 UTC (permalink / raw)
To: linux-scsi; +Cc: abjoglek, giridhar.malavali, james.smart, bprakash
v2: First posting came from a scsi-misc tree with two
additional fcoe patches. The posting did not include the
two initial fcoe patches. Those first two patches are
unrelated to this body of work, but are needed for later
patches to apply. V2 includes these two patches.
---
The following series adds a new FC subsystem/sysfs layout
to the kernel. It also adds FCoE attributes to the new devices.
An initial usage by libfc, libfcoe, fcoe and fnic is also in
this series.
This change is quite large and touches a lot of things. My
current TODO list is:
1) target support
2) user space
- libhbalinux: have a start on support, but not complete
- libVirt
3) bnx2fc
4) sysfs usage: not sure I'm doing things correctly
At this point I have working code, but I would appreciate
any review comments as there are a lot of design decisions
to be made.
I apologize for the very large patches. I am not sure how
to break them up better. I have tried a few ways, but the
resultant patches tend to be more complicated than when
they are merged. Any suggestions on how to make these
patches smaller or more easily reviewable would be appreciated
as well.
---
Robert Love (10):
libfcoe: Remove mutex_trylock/restart_syscall checks
fcoe: Remove mutex_trylock/restart_syscall checks
fc: Create FC sybsystem
scsi_transport_fcp: Create FC/SCSI interaction layer
libfc, libfcoe, fcoe: Make use of FC subsystem
fc: Add FCoE attributes to FC sysfs
libfcoe, fcoe: Use FCoE attributes
fnic: Convert to new FC Sysfs infrastructure
fc: Add a hbaapi_lib attribute to the fcport structure
Documentation: Add fc_sysfs document
Documentation/fc/fc_sysfs.txt | 192 ++++
Documentation/fc/libfc_libfcoe_fcoe.txt | 17
drivers/Kconfig | 2
drivers/Makefile | 1
drivers/fc/Kconfig | 8
drivers/fc/Makefile | 10
drivers/fc/fcfabric.c | 547 +++++++++++
drivers/fc/fcfabric_fcoe.c | 123 ++
drivers/fc/fcport.c | 453 +++++++++
drivers/fc/fcport_fcoe.c | 134 +++
drivers/fc/fcrport.c | 1012 ++++++++++++++++++++
drivers/fc/fcsysfs.c | 176 +++
drivers/fc/fcsysfs.h | 102 ++
drivers/fc/fcvport.c | 695 ++++++++++++++
drivers/fc/fcvport_fcoe.c | 118 ++
drivers/scsi/Kconfig | 12
drivers/scsi/Makefile | 1
drivers/scsi/fcoe/fcoe.c | 770 ++++++++-------
drivers/scsi/fcoe/fcoe.h | 6
drivers/scsi/fcoe/fcoe_ctlr.c | 310 ++++--
drivers/scsi/fcoe/fcoe_transport.c | 36 -
drivers/scsi/fcoe/libfcoe.h | 4
drivers/scsi/fnic/fnic.h | 33 -
drivers/scsi/fnic/fnic_fcs.c | 32 -
drivers/scsi/fnic/fnic_isr.c | 13
drivers/scsi/fnic/fnic_main.c | 412 ++++----
drivers/scsi/fnic/fnic_res.c | 47 -
drivers/scsi/fnic/fnic_scsi.c | 193 ++--
drivers/scsi/libfc/fc_disc.c | 4
drivers/scsi/libfc/fc_exch.c | 11
drivers/scsi/libfc/fc_fcp.c | 120 +-
drivers/scsi/libfc/fc_libfc.h | 40 -
drivers/scsi/libfc/fc_lport.c | 208 ++--
drivers/scsi/libfc/fc_npiv.c | 87 +-
drivers/scsi/libfc/fc_rport.c | 19
drivers/scsi/scsi_transport_fcp.c | 1571 +++++++++++++++++++++++++++++++
include/fc/fc.h | 1055 +++++++++++++++++++++
include/scsi/fc_encode.h | 40 -
include/scsi/libfc.h | 96 +-
include/scsi/libfcoe.h | 18
include/scsi/scsi_transport_fcp.h | 307 ++++++
41 files changed, 7926 insertions(+), 1109 deletions(-)
create mode 100644 Documentation/fc/fc_sysfs.txt
create mode 100644 Documentation/fc/libfc_libfcoe_fcoe.txt
create mode 100644 drivers/fc/Kconfig
create mode 100644 drivers/fc/Makefile
create mode 100644 drivers/fc/fcfabric.c
create mode 100644 drivers/fc/fcfabric_fcoe.c
create mode 100644 drivers/fc/fcport.c
create mode 100644 drivers/fc/fcport_fcoe.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/fc/fcvport_fcoe.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
--
Thanks, //Rob
^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC PATCH v2 01/10] libfcoe: Remove mutex_trylock/restart_syscall checks
2011-03-11 21:54 [RFC PATCH v2 00/10] FC Sysfs and FCoE attributes Robert Love
@ 2011-03-11 21:54 ` Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 02/10] fcoe: " Robert Love
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Robert Love @ 2011-03-11 21:54 UTC (permalink / raw)
To: linux-scsi; +Cc: abjoglek, giridhar.malavali, james.smart, bprakash
This code was incorrectly ported from fcoe.c when the
fcoe transport infrastructure was put into place. It
was originally needed in fcoe.c when dealing with
the rtnl mutex. In that code it was only needed to
avoid a lockdep false positive. In libfcoe we don't
deal with the rtnl mutex, we don't get the lockdep
false positive and therefore we don't need these
checks.
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
drivers/scsi/fcoe/fcoe_transport.c | 36 +++++++++++-------------------------
1 files changed, 11 insertions(+), 25 deletions(-)
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index 2586841..7b61d00 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -530,9 +530,6 @@ static int fcoe_transport_create(const char *buffer, struct kernel_param *kp)
struct fcoe_transport *ft = NULL;
enum fip_state fip_mode = (enum fip_state)(long)kp->arg;
- if (!mutex_trylock(&ft_mutex))
- return restart_syscall();
-
#ifdef CONFIG_LIBFCOE_MODULE
/*
* Make sure the module has been initialized, and is not about to be
@@ -543,6 +540,8 @@ static int fcoe_transport_create(const char *buffer, struct kernel_param *kp)
goto out_nodev;
#endif
+ mutex_lock(&ft_mutex);
+
netdev = fcoe_if_to_netdev(buffer);
if (!netdev) {
LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buffer);
@@ -586,10 +585,7 @@ out_putdev:
dev_put(netdev);
out_nodev:
mutex_unlock(&ft_mutex);
- if (rc == -ERESTARTSYS)
- return restart_syscall();
- else
- return rc;
+ return rc;
}
/**
@@ -608,9 +604,6 @@ static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp)
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
- if (!mutex_trylock(&ft_mutex))
- return restart_syscall();
-
#ifdef CONFIG_LIBFCOE_MODULE
/*
* Make sure the module has been initialized, and is not about to be
@@ -621,6 +614,8 @@ static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp)
goto out_nodev;
#endif
+ mutex_lock(&ft_mutex);
+
netdev = fcoe_if_to_netdev(buffer);
if (!netdev) {
LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buffer);
@@ -645,11 +640,7 @@ out_putdev:
dev_put(netdev);
out_nodev:
mutex_unlock(&ft_mutex);
-
- if (rc == -ERESTARTSYS)
- return restart_syscall();
- else
- return rc;
+ return rc;
}
/**
@@ -667,9 +658,6 @@ static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp)
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
- if (!mutex_trylock(&ft_mutex))
- return restart_syscall();
-
#ifdef CONFIG_LIBFCOE_MODULE
/*
* Make sure the module has been initialized, and is not about to be
@@ -680,6 +668,8 @@ static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp)
goto out_nodev;
#endif
+ mutex_lock(&ft_mutex);
+
netdev = fcoe_if_to_netdev(buffer);
if (!netdev)
goto out_nodev;
@@ -716,9 +706,6 @@ static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp)
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
- if (!mutex_trylock(&ft_mutex))
- return restart_syscall();
-
#ifdef CONFIG_LIBFCOE_MODULE
/*
* Make sure the module has been initialized, and is not about to be
@@ -729,6 +716,8 @@ static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp)
goto out_nodev;
#endif
+ mutex_lock(&ft_mutex);
+
netdev = fcoe_if_to_netdev(buffer);
if (!netdev)
goto out_nodev;
@@ -743,10 +732,7 @@ out_putdev:
dev_put(netdev);
out_nodev:
mutex_unlock(&ft_mutex);
- if (rc == -ERESTARTSYS)
- return restart_syscall();
- else
- return rc;
+ return rc;
}
/**
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v2 02/10] fcoe: Remove mutex_trylock/restart_syscall checks
2011-03-11 21:54 [RFC PATCH v2 00/10] FC Sysfs and FCoE attributes Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 01/10] libfcoe: Remove mutex_trylock/restart_syscall checks Robert Love
@ 2011-03-11 21:54 ` Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 03/10] fc: Create FC sybsystem Robert Love
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Robert Love @ 2011-03-11 21:54 UTC (permalink / raw)
To: linux-scsi; +Cc: abjoglek, giridhar.malavali, james.smart, bprakash
These checks were initially added to avoid a lockdep
false positive when dealing with the s_active, rtnl
and fcoe_config_mutex mutexes. Recently the create,
destroy, enable and disable sysfs entries were moved
from fcoe.ko to libfcoe.ko. With this change the mutex
usage was shuffled around and the lockdep false
positive stopped happening. We can now remove these
checks.
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
drivers/scsi/fcoe/fcoe.c | 24 ++++--------------------
1 files changed, 4 insertions(+), 20 deletions(-)
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 94fb480..488b7d9 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1797,11 +1797,7 @@ static int fcoe_disable(struct net_device *netdev)
}
#endif
- if (!rtnl_trylock()) {
- mutex_unlock(&fcoe_config_mutex);
- return -ERESTARTSYS;
- }
-
+ rtnl_lock();
fcoe = fcoe_hostlist_lookup_port(netdev);
rtnl_unlock();
@@ -1841,11 +1837,7 @@ static int fcoe_enable(struct net_device *netdev)
goto out_nodev;
}
#endif
- if (!rtnl_trylock()) {
- mutex_unlock(&fcoe_config_mutex);
- return -ERESTARTSYS;
- }
-
+ rtnl_lock();
fcoe = fcoe_hostlist_lookup_port(netdev);
rtnl_unlock();
@@ -1884,11 +1876,7 @@ static int fcoe_destroy(struct net_device *netdev)
goto out_nodev;
}
#endif
- if (!rtnl_trylock()) {
- mutex_unlock(&fcoe_config_mutex);
- return -ERESTARTSYS;
- }
-
+ rtnl_lock();
fcoe = fcoe_hostlist_lookup_port(netdev);
if (!fcoe) {
rtnl_unlock();
@@ -1950,11 +1938,7 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
struct fc_lport *lport;
mutex_lock(&fcoe_config_mutex);
-
- if (!rtnl_trylock()) {
- mutex_unlock(&fcoe_config_mutex);
- return -ERESTARTSYS;
- }
+ rtnl_lock();
#ifdef CONFIG_FCOE_MODULE
/*
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v2 03/10] fc: Create FC sybsystem
2011-03-11 21:54 [RFC PATCH v2 00/10] FC Sysfs and FCoE attributes Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 01/10] libfcoe: Remove mutex_trylock/restart_syscall checks Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 02/10] fcoe: " Robert Love
@ 2011-03-11 21:54 ` Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 04/10] scsi_transport_fcp: Create FC/SCSI interaction layer Robert Love
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Robert Love @ 2011-03-11 21:54 UTC (permalink / raw)
To: linux-scsi; +Cc: abjoglek, giridhar.malavali, james.smart, bprakash
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. The attributes for each new device
are taken from the existing FC Transport code.
fcport: Represents physical port
Attributes: maxframe_size
supported_speeds
speed
supported_fc4s
active_fc4s
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
vport_disable
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
One major thing to note is that there is not an object-wise
distinction between N_Ports and VN_Ports; all instances are
of type struct fc_vport.
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 | 540 ++++++++++++++++++++++++++
drivers/fc/fcport.c | 445 ++++++++++++++++++++++
drivers/fc/fcrport.c | 1012 +++++++++++++++++++++++++++++++++++++++++++++++++
drivers/fc/fcsysfs.c | 176 +++++++++
drivers/fc/fcsysfs.h | 92 ++++
drivers/fc/fcvport.c | 687 +++++++++++++++++++++++++++++++++
include/fc/fc.h | 952 ++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 3922 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 9bfb71f..b132b25 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -30,6 +30,8 @@ source "drivers/target/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 b423bb1..bf470c5 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -52,6 +52,7 @@ obj-y += net/
obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
obj-y += firewire/
+obj-$(CONFIG_FC) += fc/
obj-$(CONFIG_UIO) += uio/
obj-y += cdrom/
obj-y += auxdisplay/
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..b275351
--- /dev/null
+++ b/drivers/fc/fcfabric.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright(c) 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "fcsysfs.h"
+
+/* used by max_npiv_vports */
+/* used by npiv_vports_inuse */
+#define fc_fabric_rd_attr(field, format_string, sz) \
+ fc_show_function(fc, fabric, field, format_string, sz, ) \
+ static FC_DEVICE_ATTR(fcfabric, field, S_IRUGO, \
+ show_fabric_##field, NULL)
+
+/* used by fabric_name */
+#define fc_fabric_rd_attr_cast(field, format_string, sz, cast) \
+ fc_show_function(fc, fabric, field, format_string, sz, (cast)) \
+ static FC_DEVICE_ATTR(fcfabric, field, S_IRUGO, \
+ show_fabric_##field, NULL)
+
+fc_fabric_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long);
+fc_fabric_rd_attr(max_npiv_vports, "%u\n", 20);
+fc_fabric_rd_attr(npiv_vports_inuse, "%u\n", 20);
+
+fc_fabric_rd_attr(dev_loss_tmo, "%d\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.
+ * @fcfabric: The fabric that 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.
+ * @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_fabric *fcfabric, int channel,
+ struct fc_vport_identifiers *ids,
+ struct fc_vport **ret_vport)
+{
+ struct fc_vport *fcnport = fc_fabric_find_nport(fcfabric);
+ struct fc_port *fcport = fcfabric_to_fcport(fcfabric);
+ struct fc_vport *fcvport;
+
+ *ret_vport = NULL;
+
+ if (!fcnport->f->vport_create)
+ return -ENOENT;
+
+ fcvport = fc_vport_alloc(fcnport, ids, &fcport->f->fcvport_f,
+ channel, FC4_FCP_INITIATOR);
+ if (unlikely(!fcvport)) {
+ printk(KERN_ERR "%s: allocation failure\n", __func__);
+ return -ENOMEM;
+ }
+
+ mutex_lock(&fcport->lock);
+
+ if (fcfabric->npiv_vports_inuse >= fcfabric->max_npiv_vports) {
+ mutex_unlock(&fcport->lock);
+ kfree(fcvport);
+ return -ENOSPC;
+ }
+
+ fcfabric->npiv_vports_inuse++;
+ fcvport->number = fcfabric->next_vport_number++;
+ list_add_tail(&fcvport->peers, &fcfabric->vports);
+
+ mutex_unlock(&fcport->lock);
+
+ return 0;
+}
+
+/**
+ * fc_vport_create() - Admin App or LLDD requests creation of a vport
+ * @fcfabric: The fabric that 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_vport *fc_vport_create(struct fc_fabric *fcfabric,
+ int channel, struct fc_vport_identifiers *ids)
+{
+ int stat;
+ struct fc_vport *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
+ * @fcfabric: The fabric that the FC vport is attached to.
+ * @vport: The 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_fabric *fcfabric, struct fc_vport *vport)
+{
+ struct fc_vport *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.
+ */
+ fcnport = vport->fcnport;
+ if (fcnport && fcnport->f->vport_delete)
+ stat = fcnport->f->vport_delete(fcnport, vport);
+ else
+ stat = -ENOENT;
+
+ spin_lock_irqsave(&vport->lock, flags);
+ vport->flags &= ~FC_VPORT_DELETING;
+ if (!stat) {
+ vport->flags |= FC_VPORT_DELETED;
+ vport->fcfabric = NULL;
+ list_del(&vport->peers);
+ fcfabric->npiv_vports_inuse--;
+ }
+ spin_unlock_irqrestore(&vport->lock, flags);
+
+ fc_vport_free(vport);
+
+ if (stat)
+ return stat;
+
+ 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_fabric_vport_create(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fc_fabric *fcfabric = dev_to_fcfabric(dev);
+ struct fc_vport_identifiers vid;
+ struct fc_vport *fcnport, *fcvport;
+ unsigned int cnt = count;
+ int stat;
+
+ fcnport = fc_fabric_find_nport(fcfabric);
+ if (!fcnport)
+ return -ENODEV;
+ put_device(&fcnport->dev); /* from find_nport */
+
+ 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_fabric_vport_create);
+
+struct fc_vport *fc_vport_lookup(struct fc_fabric *fcfabric,
+ const u32 port_id)
+{
+ struct fc_port *fcport = fcfabric_to_fcport(fcfabric);
+ struct fc_vport *fcvport, *found = NULL;
+
+ mutex_lock(&fcport->lock);
+ list_for_each_entry(fcvport, &fcfabric->vports, peers) {
+ if (fcvport->port_id == port_id) {
+ found = fcvport;
+ break;
+ }
+ }
+ mutex_unlock(&fcport->lock);
+
+ return found;
+}
+EXPORT_SYMBOL(fc_vport_lookup);
+
+
+/*
+ * "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_fabric_vport_delete(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fc_fabric *fcfabric = dev_to_fcfabric(dev);
+ struct fc_port *fcport = fcfabric_to_fcport(fcfabric);
+ struct fc_vport *vport;
+ u64 wwpn, wwnn;
+ 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;
+
+ mutex_lock(&fcport->lock);
+ 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) &&
+ (!fc_vport_is_nport(vport))) {
+ match = 1;
+ break;
+ }
+ }
+ mutex_unlock(&fcport->lock);
+
+ 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_fabric_vport_delete);
+
+/**
+ * fc_fabric_release() - Release the FC fabric memory
+ * @dev: Pointer to the fabric's embedded device
+ *
+ * Called when the last FC fabric reference is released.
+ */
+static void fc_fabric_release(struct device *dev)
+{
+ struct fc_fabric *fcfabric = dev_to_fcfabric(dev);
+ put_device(fcfabric->dev.parent);
+ fcfabric->dev.parent = NULL;
+ kfree(fcfabric);
+}
+
+/**
+ * fc_fabric_final_delete() - Final delete routine
+ * @work: The FC fabric's embedded work struct
+ *
+ * Flush any vport work on the fabric's devloss workqueue
+ * in case the fabric is being deleted somewhere else. Then
+ * delete the fabric's N_Port, which will in turn delete
+ * any FC vports on the fabric. Finally, remove the device
+ * from sysfs and drop the last reference.
+ *
+ * It is expected that the fabric has been removed from
+ * the FC port's list before calling this routine.
+ */
+static void fc_fabric_final_delete(struct work_struct *work)
+{
+ struct fc_fabric *fcfabric =
+ container_of(work, struct fc_fabric, delete_work);
+ struct fc_port *fcport = fcfabric_to_fcport(fcfabric);
+ struct fc_vport *fcvport;
+
+ /*
+ * Cancel any outstanding timers. These should really exist
+ * only when rmmod'ing the LLDD and we're asking for
+ * immediate termination of the rports
+ */
+ mutex_lock(&fcport->lock);
+ if (fcfabric->flags & FC_FABRIC_DEVLOSS_PENDING) {
+ mutex_unlock(&fcport->lock);
+ if (!cancel_delayed_work(&fcfabric->dev_loss_work))
+ fc_port_flush_devloss(fcport);
+ mutex_lock(&fcport->lock);
+ fcfabric->flags &= ~FC_FABRIC_DEVLOSS_PENDING;
+ }
+
+ /*
+ * Deleting the N_Port will delete all NPIV vports too
+ */
+ fcvport = fc_fabric_find_nport(fcfabric);
+ if (fcvport) {
+ fc_vport_del(fcvport);
+ put_device(&fcvport->dev); /* drop from find_nport */
+ }
+
+ mutex_unlock(&fcport->lock);
+
+ device_del(&fcfabric->dev);
+ put_device(&fcfabric->dev); /* self-reference */
+}
+
+/**
+ * fc_timeout_deleted_fabric() - Delete a fabric when the devloss timer fires
+ * @work: The FC fabric's embedded work struct
+ *
+ * Removes the fabric from the FC port's list of fabrics and
+ * queues the final deletion.
+ */
+static void fc_timeout_deleted_fabric(struct work_struct *work)
+{
+ struct fc_fabric *fcfabric =
+ container_of(work, struct fc_fabric, dev_loss_work.work);
+ struct fc_port *fcport = fcfabric_to_fcport(fcfabric);
+
+ mutex_lock(&fcport->lock);
+
+ fcfabric->flags &= ~FC_FABRIC_DEVLOSS_PENDING;
+
+ if (fcfabric->state == FC_FABRICSTATE_CONNECTED) {
+ mutex_unlock(&fcport->lock);
+ return;
+ }
+
+ dev_printk(KERN_ERR, &fcfabric->dev,
+ "FC fabric connection time out: removing fabric\n");
+
+ list_del(&fcfabric->peers);
+ fc_port_queue_work(fcport, &fcfabric->delete_work);
+
+ mutex_unlock(&fcport->lock);
+}
+
+/**
+ * fc_fabric_del() - Delete a FC fabric
+ * @fcfabric: Pointer to the fabric which is to be deleted
+ *
+ * Queues the FC fabric on the devloss workqueue
+ *
+ * Expects the FC port lock to be held (from age_fcfs - fcoe)
+ */
+void fc_fabric_del(struct fc_fabric *fcfabric)
+{
+ struct fc_port *fcport = fcfabric_to_fcport(fcfabric);
+ int timeout = fcfabric->dev_loss_tmo;
+
+ if (fcfabric->state != FC_FABRICSTATE_CONNECTED)
+ return;
+
+ fcfabric->state = FC_FABRICSTATE_DISCONNECTED;
+ fcfabric->flags |= FC_FABRIC_DEVLOSS_PENDING;
+
+ fc_port_queue_devloss_work(fcport, &fcfabric->dev_loss_work,
+ timeout * HZ);
+}
+EXPORT_SYMBOL(fc_fabric_del);
+
+struct class fcfabric_class = {
+ .name = "fcfabric",
+ .dev_release = fc_fabric_release,
+};
+
+/**
+ * fc_fabric_add() - Add a FC fabric to sysfs
+ * @fcport: The FC port that the fabric is to be added to
+ * @fcn_tmpl: The FC fabric function template
+ * @name: The fabric name
+ *
+ * Expects the fcport lock to be held
+ */
+struct fc_fabric *fc_fabric_add(struct fc_port *fcport,
+ struct fc_fabric_function_template *fcn_tmpl,
+ struct fc_fabric *new_fcfabric)
+{
+ struct fc_fabric *fcfabric;
+ int error = 0;
+ int count = 0;
+ int size;
+
+ list_for_each_entry(fcfabric, &fcport->fabrics, peers) {
+ if (fcn_tmpl->fabric_match(new_fcfabric, fcfabric)) {
+ if (fcfabric->state == FC_FABRICSTATE_CONNECTED)
+ return fcfabric;
+
+ fcfabric->flags &= ~FC_FABRIC_DEVLOSS_PENDING;
+ fcfabric->state = FC_FABRICSTATE_CONNECTED;
+
+ if (!cancel_delayed_work(&fcfabric->dev_loss_work))
+ fc_port_flush_devloss(fcport);
+
+ return fcfabric;
+ }
+ }
+
+ size = (sizeof(struct fc_fabric) + fcn_tmpl->dd_fcfabric_size);
+ fcfabric = kzalloc(size, GFP_ATOMIC);
+ if (unlikely(!fcfabric))
+ goto out;
+
+ INIT_WORK(&fcfabric->delete_work, fc_fabric_final_delete);
+ INIT_DELAYED_WORK(&fcfabric->dev_loss_work, fc_timeout_deleted_fabric);
+
+ device_initialize(&fcfabric->dev);
+ fcfabric->dev.parent = get_device(&fcport->dev);
+ fcfabric->dev.class = &fcfabric_class;
+ fcfabric->f = fcn_tmpl;
+ fcfabric->id = atomic_inc_return(&fcfabric_num) - 1;
+ fcfabric->state = FC_FABRICSTATE_CONNECTED;
+
+ fcfabric->dev_loss_tmo = fcport->fab_dev_loss_tmo;
+
+ list_add_tail(&fcfabric->peers, &fcport->fabrics);
+
+ dev_set_name(&fcfabric->dev, "fcfabric_%d",
+ fcfabric->id);
+
+ INIT_LIST_HEAD(&fcfabric->vports);
+
+ fcfabric->fabric_name = new_fcfabric->fabric_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;
+
+ FC_SETUP_COND_ATTR_RD(fcfabric, fabric_name);
+ FC_SETUP_COND_ATTR_RD(fcfabric, dev_loss_tmo);
+
+ FC_SETUP_ATTR_RD_NS(fcfabric, max_npiv_vports);
+ FC_SETUP_ATTR_RD_NS(fcfabric, npiv_vports_inuse);
+ FC_SETUP_ATTR_RW_NS(fcfabric, vport_create);
+ FC_SETUP_ATTR_RW_NS(fcfabric, vport_delete);
+
+ BUG_ON(count > FC_FABRIC_NUM_ATTRS);
+ FC_CREATE_ATTRS(fcfabric, count);
+
+ if (error || count != 0)
+ goto out_del_dev;
+
+ return fcfabric;
+
+out_del_dev:
+ device_del(&fcfabric->dev);
+ list_del(&fcfabric->peers);
+out_del:
+ put_device(&fcport->dev);
+ kfree(fcfabric);
+out:
+ return NULL;
+}
+EXPORT_SYMBOL(fc_fabric_add);
diff --git a/drivers/fc/fcport.c b/drivers/fc/fcport.c
new file mode 100644
index 0000000..e9dc2b7
--- /dev/null
+++ b/drivers/fc/fcport.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright(c) 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#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)
+
+/* used by maxframe_size */
+/* used by serial_number */
+#define fc_port_rd_attr(field, format_string, sz) \
+ fc_show_function(fc, port, field, format_string, sz, ) \
+ static FC_DEVICE_ATTR(fcport, field, S_IRUGO, \
+ show_port_##field, NULL)
+
+#define fc_port_store_str_function(field, slen) \
+ static ssize_t store_port_##field(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+ { \
+ struct fc_port *fcport = dev_to_fcport(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(fc_port_##field(fcport), buf, cnt); \
+ fcport->f->set_fcport_##field(fcport); \
+ return count; \
+ }
+
+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;
+}
+
+/* used by supported_fc4s */
+static ssize_t show_port_supported_fc4s(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fc_port *fcport = dev_to_fcport(dev);
+ return (ssize_t)show_fc_fc4s(buf, fc_port_supported_fc4s(fcport));
+}
+static FC_DEVICE_ATTR(fcport, supported_fc4s, S_IRUGO,
+ show_port_supported_fc4s, NULL);
+
+/* used by active_fc4s */
+static ssize_t show_port_active_fc4s(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fc_port *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, fc_port_active_fc4s(fcport));
+}
+static FC_DEVICE_ATTR(fcport, active_fc4s, S_IRUGO,
+ show_port_active_fc4s, NULL);
+
+/* used by speed */
+static ssize_t show_port_speed(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fc_port *fcport = dev_to_fcport(dev);
+
+ if (fcport->f->get_fcport_speed)
+ fcport->f->get_fcport_speed(fcport);
+
+ if (fc_port_speed(fcport) == FC_PORTSPEED_UNKNOWN)
+ return snprintf(buf, 20, "unknown\n");
+
+ return get_fc_port_speed_names(fc_port_speed(fcport), buf);
+}
+static FC_DEVICE_ATTR(fcport, speed, S_IRUGO, show_port_speed, NULL);
+
+fc_port_rd_attr(maxframe_size, "%u bytes\n", 20);
+fc_port_rd_attr(serial_number, "%s\n", (FC_SERIAL_NUMBER_SIZE + 1));
+
+/* used by supported_speeds */
+static ssize_t show_port_supported_speeds(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fc_port *fcport = dev_to_fcport(dev);
+ if (fc_port_supported_speeds(fcport) == FC_PORTSPEED_UNKNOWN)
+ return snprintf(buf, 20, "unknown\n");
+
+ return get_fc_port_speed_names(fc_port_supported_speeds(fcport), buf);
+}
+static FC_DEVICE_ATTR(fcport, supported_speeds, S_IRUGO,
+ show_port_supported_speeds, NULL);
+
+/* used by supported_classes */
+static ssize_t show_port_supported_classes(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fc_port *fcport = dev_to_fcport(dev);
+
+ if (fc_port_supported_classes(fcport) == FC_COS_UNSPECIFIED)
+ return snprintf(buf, 20, "unspecified\n");
+
+ return get_fc_cos_names(fc_port_supported_classes(fcport), buf);
+}
+static FC_DEVICE_ATTR(fcport, supported_classes, S_IRUGO,
+ show_port_supported_classes, NULL);
+
+fc_show_function(fc, port, system_hostname, "%s\n",
+ FC_SYMBOLIC_NAME_SIZE + 1, )
+
+fc_port_store_str_function(system_hostname, FC_SYMBOLIC_NAME_SIZE)
+static FC_DEVICE_ATTR(fcport, system_hostname, S_IRUGO | S_IWUSR,
+ show_port_system_hostname,
+ store_port_system_hostname);
+
+fc_show_function(fc, port, fab_dev_loss_tmo, "%d\n", 20, )
+static ssize_t store_port_fab_dev_loss_tmo(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fc_port *fcport = dev_to_fcport(dev);
+ int val;
+ char *cp;
+ val = simple_strtoul(buf, &cp, 0);
+ if ((*cp && (*cp != '\n')) || (val < 0))
+ return -EINVAL;
+
+ fcport->fab_dev_loss_tmo = val;
+ return count;
+}
+static FC_DEVICE_ATTR(fcport, fab_dev_loss_tmo, S_IRUGO | S_IWUSR,
+ show_port_fab_dev_loss_tmo, store_port_fab_dev_loss_tmo);
+
+/**
+ * fc_port_release() - Release the FC port memory
+ * @dev: Pointer to the FC port's embedded device
+ *
+ * Called when the last FC port reference is released.
+ */
+static void fc_port_release(struct device *dev)
+{
+ struct fc_port *fcport = dev_to_fcport(dev);
+
+ put_device(fcport->dev.parent);
+ fcport->dev.parent = NULL;
+ kfree(fcport);
+}
+
+struct class fcport_class = {
+ .name = "fcport",
+ .dev_release = fc_port_release,
+};
+
+/**
+ * fc_port_del() - Delete a FC port and its subtree from sysfs
+ * @fcport: A pointer to the port to be deleted
+ *
+ * Deletes a FC port and any fabrics attached
+ * to it. Deleting fabrics will cause their childen
+ * to be deleted as well.
+ *
+ * The port is detached from sysfs and it's resources
+ * are freed (work q), but the memory is not freed
+ * until its last reference is released.
+ *
+ * This routine expects no locks to be held before
+ * calling.
+ */
+void fc_port_del(struct fc_port *fcport)
+{
+ struct fc_fabric *fcfabric, *next;
+
+ /* Remove any attached fabrics */
+ mutex_lock(&fcport->lock);
+ list_for_each_entry_safe(fcfabric, next,
+ &fcport->fabrics, peers) {
+ list_del(&fcfabric->peers);
+ fcfabric->state = FC_FABRICSTATE_DISCONNECTED;
+ fc_port_queue_work(fcport, &fcfabric->delete_work);
+ }
+ mutex_unlock(&fcport->lock);
+
+ fc_port_flush_work(fcport);
+
+ destroy_workqueue(fcport->devloss_work_q);
+ fcport->devloss_work_q = NULL;
+ destroy_workqueue(fcport->work_q);
+ fcport->work_q = NULL;
+
+ device_del(&fcport->dev);
+ put_device(&fcport->dev); /* self-reference */
+}
+EXPORT_SYMBOL(fc_port_del);
+
+/**
+ * fc_port_add() - Add a FC port to sysfs
+ * @pdev: The parent device to which the FC port should be attached
+ * @fcn_tmpl: The FC port function template
+ * @fc4_f: The FC4 function template for any vports created
+ *
+ * This routine allocates a FC port object with some additional memory
+ * for the LLD. The FC port is initialized, added to sysfs and then
+ * attributes are added to it.
+ */
+struct fc_port *fc_port_add(struct device *pdev,
+ struct fc_port_function_template *fcn_tmpl,
+ void *fc4_f)
+{
+ struct fc_port *fcport;
+ int count = 0;
+ int error = 0;
+
+ fcport = kzalloc(sizeof(struct fc_port) + fcn_tmpl->dd_fcport_size,
+ GFP_KERNEL);
+ if (!fcport)
+ goto out;
+
+ fcport->id = atomic_inc_return(&fcport_num) - 1;
+ INIT_LIST_HEAD(&fcport->fabrics);
+ mutex_init(&fcport->lock);
+ device_initialize(&fcport->dev);
+ fcport->dev.parent = get_device(pdev);
+ fcport->dev.class = &fcport_class;
+ fcport->f = fcn_tmpl;
+ fcport->fc4_f = fc4_f;
+
+ snprintf(fcport->work_q_name, sizeof(fcport->work_q_name),
+ "fcport_wq_%d", fcport->id);
+ fcport->work_q = create_singlethread_workqueue(
+ fcport->work_q_name);
+ if (!fcport->work_q)
+ goto out_del;
+
+ snprintf(fcport->devloss_work_q_name,
+ sizeof(fcport->devloss_work_q_name),
+ "fcport_dl_wq_%d", fcport->id);
+ fcport->devloss_work_q = create_singlethread_workqueue(
+ fcport->devloss_work_q_name);
+ if (!fcport->devloss_work_q)
+ goto out_del_q;
+
+ dev_set_name(&fcport->dev, "fcport_%d", fcport->id);
+ error = device_add(&fcport->dev);
+ if (error)
+ goto out_del_q2;
+
+ memset(fcport->system_hostname, 0, sizeof(fcport->system_hostname));
+ fcport->maxframe_size = -1;
+ fcport->supported_classes = FC_COS_UNSPECIFIED;
+ fcport->supported_speeds = FC_PORTSPEED_UNKNOWN;
+ fcport->speed = FC_PORTSPEED_UNKNOWN;
+ fcport->fab_dev_loss_tmo = 600; /* default to 10mins */
+ 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));
+
+ /*
+ * system_hostname may be set through sysfs, but
+ * not updated by the lld
+ */
+ FC_SETUP_COND_ATTR_RW(fcport, system_hostname);
+ FC_SETUP_COND_ATTR_RW(fcport, fab_dev_loss_tmo);
+
+ FC_SETUP_COND_ATTR_RD(fcport, maxframe_size);
+ FC_SETUP_COND_ATTR_RD(fcport, supported_speeds);
+ FC_SETUP_COND_ATTR_RD(fcport, supported_fc4s);
+ FC_SETUP_COND_ATTR_RD(fcport, active_fc4s);
+ FC_SETUP_COND_ATTR_RD(fcport, supported_classes);
+ FC_SETUP_COND_ATTR_RD(fcport, serial_number);
+ FC_SETUP_COND_ATTR_RD(fcport, speed);
+
+ BUG_ON(count > FC_PORT_NUM_ATTRS);
+ FC_CREATE_ATTRS(fcport, count);
+
+ if (error || count != 0)
+ goto out_del_dev;
+
+ return fcport;
+
+out_del_dev:
+ device_del(&fcport->dev);
+out_del_q2:
+ destroy_workqueue(fcport->devloss_work_q);
+ fcport->devloss_work_q = NULL;
+out_del_q:
+ destroy_workqueue(fcport->work_q);
+ fcport->work_q = NULL;
+out_del:
+ put_device(pdev);
+ kfree(fcport);
+out:
+ return NULL;
+}
+EXPORT_SYMBOL(fc_port_add);
+
+/**
+ * fc_port_del_fabrics() - Delete all fabrics on a FC port
+ * @fcport: The port whose fabrics are to be deleted
+ *
+ * Goes through a FC port's list of fabrics and queues
+ * a device loss deletion for each fabric. The devloss
+ * queue can be flushed after calling this routine to
+ * expediate the deletion.
+ *
+ * This routine expects the FC port's mutex to be locked.
+ */
+void fc_port_del_fabrics(struct fc_port *fcport)
+{
+ struct fc_fabric *fcfabric, *next_fcfabric;
+
+ list_for_each_entry_safe(fcfabric, next_fcfabric,
+ &fcport->fabrics, peers) {
+
+ if (fcfabric->flags & FC_FABRIC_DEVLOSS_PENDING)
+ continue;
+
+ fcfabric->state = FC_FABRICSTATE_DISCONNECTED;
+ fcfabric->flags |= FC_FABRIC_DEVLOSS_PENDING;
+ fc_port_queue_devloss_work(fcport, &fcfabric->dev_loss_work,
+ fcfabric->dev_loss_tmo * HZ);
+ }
+}
+EXPORT_SYMBOL(fc_port_del_fabrics);
+
+/**
+ * fc_port_flush_work() - Flush a FC port's workqueue
+ * @fcport: Pointer to the FC port whose workqueue is to be flushed
+ */
+void fc_port_flush_work(struct fc_port *fcport)
+{
+ if (!fc_port_work_q(fcport)) {
+ printk(KERN_ERR
+ "ERROR: FC Port '%d' attempted to flush work, "
+ "when no workqueue created.\n", fcport->id);
+ dump_stack();
+ return;
+ }
+
+ flush_workqueue(fc_port_work_q(fcport));
+}
+
+/**
+ * fc_port_queue_work() - Schedule work for a FC port's workqueue
+ * @fcport: Pointer to the FC port who owns the devloss workqueue
+ * @work: Work to queue for execution
+ *
+ * Return value:
+ * 1 on success / 0 already queued / < 0 for error
+ */
+int fc_port_queue_work(struct fc_port *fcport, struct work_struct *work)
+{
+ if (unlikely(!fc_port_work_q(fcport))) {
+ printk(KERN_ERR
+ "ERROR: FC Port '%d' attempted to queue work, "
+ "when no workqueue created.\n", fcport->id);
+ dump_stack();
+
+ return -EINVAL;
+ }
+
+ return queue_work(fc_port_work_q(fcport), work);
+}
+
+/**
+ * fc_port_flush_devloss() - Flush a FC port's devloss workqueue
+ * @fcport: Pointer to FC port whose workqueue is to be flushed
+ */
+void fc_port_flush_devloss(struct fc_port *fcport)
+{
+ if (!fc_port_devloss_work_q(fcport)) {
+ printk(KERN_ERR
+ "ERROR: FC Port '%d' attempted to flush work, "
+ "when no workqueue created.\n", fcport->id);
+ dump_stack();
+ return;
+ }
+
+ flush_workqueue(fc_port_devloss_work_q(fcport));
+}
+
+/**
+ * fc_port_queue_devloss_work() - Schedule work for a FC port's devloss workqueue
+ * @fcport: Pointer to the FC port who owns the devloss workqueue
+ * @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_port_queue_devloss_work(struct fc_port *fcport,
+ struct delayed_work *work,
+ unsigned long delay)
+{
+ if (unlikely(!fc_port_devloss_work_q(fcport))) {
+ printk(KERN_ERR
+ "ERROR: FC Port '%d' attempted to queue work, "
+ "when no workqueue created.\n", fcport->id);
+ dump_stack();
+
+ return -EINVAL;
+ }
+
+ return queue_delayed_work(fc_port_devloss_work_q(fcport), work, delay);
+}
diff --git a/drivers/fc/fcrport.c b/drivers/fc/fcrport.c
new file mode 100644
index 0000000..4fa6dff
--- /dev/null
+++ b/drivers/fc/fcrport.c
@@ -0,0 +1,1012 @@
+/*
+ * Copyright(c) 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "fcsysfs.h"
+
+/*
+ * TODO: This is dup from scsi_priv.h. We should get it from
+ * whatever FC4 is being transported. For now it should be fine
+ * to use a singular value.
+ */
+#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);
+
+/**
+ * fc_rport_dev_release() - Free the FC rport's memory
+ * @dev: The FC rport's embedded device
+ */
+static void fc_rport_dev_release(struct device *dev)
+{
+ struct fc_rport *rport = dev_to_fcrport(dev);
+ kfree(rport);
+}
+
+#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_fcrport(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_fcrport(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_fcrport(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_fcrport(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_fcrport(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_fcrport(dev); \
+ const char *name; \
+ name = get_fc_##title##_name(fc_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 fc_rport_rw_attr(field, format_string, sz) \
+ fc_show_function(fc, 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_show_function(fc, 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_show_function(fc, rport, field, format_string, sz, (cast)) \
+ static FC_DEVICE_ATTR(rport, field, S_IRUGO, \
+ show_rport_##field, NULL)
+
+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_fcrport(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_fcrport(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_vport *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.
+ */
+ if (rport->flags & FC_RPORT_SCAN_PENDING) {
+ if (fcvport->fc4_f)
+ fcvport->fc4_f->fc4_init_scsi_flush_work(fcvport);
+ }
+
+ if (fcvport->fc4_f && (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ fcvport->fc4_f->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_vport_flush_devloss(fcvport);
+ if (!cancel_delayed_work(&rport->dev_loss_work))
+ fc_vport_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);
+
+ device_del(dev);
+ put_device(dev->parent);
+ dev->parent = NULL;
+ put_device(dev); /* for self-reference */
+}
+
+/**
+ * fc_rport_create() - Allocate and add a FC remote port to sysfs.
+ * @fcvport: The vport on which the rport was discovered
+ * @f: The rport function template
+ * @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_vport *fcvport,
+ struct fc_rport_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))
+ goto out;
+
+ rport->f = f;
+
+ rport->maxframe_size = -1;
+ rport->supported_classes = FC_COS_UNSPECIFIED;
+ rport->dev_loss_tmo = fc_vport_rport_dev_loss_tmo(fcvport);
+ 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++;
+ spin_unlock_irqrestore(&fcvport->lock, flags);
+
+ dev = &rport->dev;
+ device_initialize(dev); /* takes self reference */
+ dev->parent = get_device(&fcvport->dev); /* parent reference */
+ dev->bus = &fc_rport_bus_type;
+ dev->release = fc_rport_dev_release;
+ dev_set_name(dev, "rport-%d:%d-%d",
+ fcvport->id, channel, rport->number);
+ error = device_add(dev);
+ if (error)
+ goto out_del;
+
+ spin_lock_irqsave(&fcvport->lock, flags);
+ list_add_tail(&rport->peers, &fcvport->rports);
+ spin_unlock_irqrestore(&fcvport->lock, flags);
+
+ /*
+ * Setup attributes
+ */
+ FC_SETUP_ATTR_RW_NS(rport, fast_io_fail_tmo);
+ FC_SETUP_ATTR_RD_NS(rport, port_id);
+ FC_SETUP_ATTR_RD_NS(rport, port_state);
+ FC_SETUP_ATTR_RD_NS(rport, roles);
+ FC_SETUP_ATTR_RD_NS(rport, node_name);
+ FC_SETUP_ATTR_RD_NS(rport, port_name);
+ FC_SETUP_ATTR_RD_NS(rport, supported_classes);
+ FC_SETUP_ATTR_RD_NS(rport, maxframe_size);
+ FC_SETUP_ATTR_RW_NS(rport, dev_loss_tmo);
+
+ BUG_ON(count > FC_RPORT_NUM_ATTRS);
+ FC_CREATE_ATTRS(rport, count);
+ if (error || count != 0)
+ goto out_del_dev;
+
+ if (rport->roles & FC_PORT_ROLE_FCP_TARGET) {
+ /* initiate a scan of the target */
+ rport->flags |= FC_RPORT_SCAN_PENDING;
+ if (fcvport->fc4_f && (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ fcvport->fc4_f->fc4_targ_queue_scan(rport);
+ }
+
+ return rport;
+
+out_del_dev:
+ device_del(&fcvport->dev);
+ spin_lock_irqsave(&fcvport->lock, flags);
+ list_del(&rport->peers);
+ spin_unlock_irqrestore(&fcvport->lock, flags);
+out_del:
+ put_device(dev->parent);
+ kfree(rport);
+out:
+ return NULL;
+}
+
+/**
+ * fc_rport_add() - notify fc transport of the existence of a remote FC port.
+ * @fcvport: The FC vport that the rport was discovered on
+ * @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_rport_add(struct fc_vport *fcvport,
+ struct fc_rport_identifiers *ids)
+{
+ struct fc_rport *rport;
+ struct fc_rport_function_template *f = &fcvport->f->fcrport_f;
+
+ unsigned long flags;
+ int match = 0;
+
+ /* ensure any stgt delete functions are done */
+ fc_vport_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 == fcvport->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 */
+ if ((!dev_get_drvdata(&rport->dev)) &&
+ (!(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_vport_flush_devloss(fcvport);
+ if (!cancel_delayed_work(&rport->dev_loss_work))
+ fc_vport_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);
+
+ /* if target, initiate a scan */
+ if (dev_get_drvdata(&rport->dev)) {
+ rport->flags |= FC_RPORT_SCAN_PENDING;
+ if (fcvport->fc4_f &&
+ (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ fcvport->fc4_f->fc4_targ_queue_scan(rport);
+ spin_unlock_irqrestore(&fcvport->lock,
+ flags);
+
+ if (fcvport->fc4_f &&
+ (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ fcvport->fc4_f->fc4_targ_unblock(rport);
+ } else
+ spin_unlock_irqrestore(&fcvport->lock,
+ flags);
+
+ if (fcvport->fc4_f &&
+ (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ fcvport->fc4_f->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 != fcvport->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;
+ if (fcvport->fc4_f &&
+ (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ fcvport->fc4_f->fc4_targ_queue_scan(rport);
+
+ spin_unlock_irqrestore(&fcvport->lock, flags);
+ if (fcvport->fc4_f &&
+ (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ fcvport->fc4_f->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, fcvport->channel, ids);
+
+ return rport;
+}
+EXPORT_SYMBOL(fc_rport_add);
+
+/**
+ * fc_rport_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_rport_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_rport_del(struct fc_rport *rport)
+{
+ struct fc_vport *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);
+
+ if (fcvport->fc4_f && (rport->roles & FC_PORT_ROLE_FCP_TARGET)) {
+ fcvport->fc4_f->fc4_targ_final_delete(rport);
+ fcvport->fc4_f->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_vport_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_vport_queue_devloss_work(fcvport, &rport->dev_loss_work,
+ timeout * HZ);
+}
+EXPORT_SYMBOL(fc_rport_del);
+
+/**
+ * fc_rport_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_rport_rolechg(struct fc_rport *rport, u32 roles)
+{
+ struct fc_vport *fcvport = rport_to_fcvport(rport);
+ unsigned long flags;
+ int create = 0;
+
+ spin_lock_irqsave(&fcvport->lock, flags);
+ if (fcvport->fc4_f && (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ create = fcvport->fc4_f->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_vport_flush_devloss(fcvport);
+ if (!cancel_delayed_work(&rport->dev_loss_work))
+ fc_vport_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_vport_flush_work(fcvport);
+
+ /* initiate a scan of the target */
+ spin_lock_irqsave(&fcvport->lock, flags);
+ rport->flags |= FC_RPORT_SCAN_PENDING;
+ if (fcvport->fc4_f && (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ fcvport->fc4_f->fc4_targ_queue_scan(rport);
+ spin_unlock_irqrestore(&fcvport->lock, flags);
+ if (fcvport->fc4_f && (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ fcvport->fc4_f->fc4_targ_unblock(rport);
+ }
+}
+EXPORT_SYMBOL(fc_rport_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_vport *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);
+ if (fcvport->fc4_f &&
+ (rport->roles & FC_PORT_ROLE_FCP_TARGET)) {
+ fcvport->fc4_f->fc4_targ_unblock(rport);
+ fcvport->fc4_f->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",
+ (dev_get_drvdata(&rport->dev)) ?
+ " 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",
+ (dev_get_drvdata(&rport->dev)) ?
+ " and starget" : "");
+ fc_vport_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);
+ if (fcvport->fc4_f && (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ fcvport->fc4_f->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;
+ if (fcvport->fc4_f && (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ fcvport->fc4_f->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);
+ struct fc_vport *fcvport = rport_to_fcvport(rport);
+
+ if (rport->port_state != FC_PORTSTATE_BLOCKED)
+ return;
+
+ rport->flags |= FC_RPORT_FAST_FAIL_TIMEDOUT;
+ if (fcvport->fc4_f && (rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ fcvport->fc4_f->fc4_targ_terminate_io(rport);
+}
+
+static int fc_rport_bus_match(struct device *dev,
+ struct device_driver *drv);
+
+struct bus_type fc_rport_bus_type = {
+ .name = "fc_rport",
+ .match = fc_rport_bus_match,
+};
+EXPORT_SYMBOL(fc_rport_bus_type);
+
+static int fc_rport_bus_match(struct device *dev,
+ struct device_driver *drv)
+{
+ if (dev->bus == &fc_rport_bus_type)
+ return 1;
+ return 0;
+}
+
+int fc_rport_bus_register(void)
+{
+ return bus_register(&fc_rport_bus_type);
+}
+
+void fc_rport_bus_unregister(void)
+{
+ bus_unregister(&fc_rport_bus_type);
+}
diff --git a/drivers/fc/fcsysfs.c b/drivers/fc/fcsysfs.c
new file mode 100644
index 0000000..3649e2c
--- /dev/null
+++ b/drivers/fc/fcsysfs.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright(c) 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#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);
+}
+
+/* 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 = fc_vport_bus_register();
+ if (error)
+ goto unreg_fcfabric;
+
+ error = fc_rport_bus_register();
+ if (error)
+ goto unreg_vport_bus;
+
+ return 0;
+
+unreg_vport_bus:
+ fc_vport_bus_unregister();
+unreg_fcfabric:
+ class_unregister(&fcfabric_class);
+unreg_fcport:
+ class_unregister(&fcport_class);
+ return error;
+}
+module_init(fc_init);
+
+static void __exit fc_exit(void)
+{
+ fc_rport_bus_unregister();
+ fc_vport_bus_unregister();
+ 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(fc, port_type, fc_port_type, fc_port_type_names)
+EXPORT_SYMBOL(get_fc_port_type_name);
+fc_enum_name_search(fc, 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);
+
+/* 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(fc, port_state, fc_port_state, fc_port_state_names)
+EXPORT_SYMBOL(get_fc_port_state_name);
+
+static struct {
+ enum fc_vport_state value;
+ char *name;
+} fc_vport_state_names[] = {
+ { FC_VPORT_ACTIVE, "Active" },
+ { FC_VPORT_DISABLED, "Disabled" },
+ { 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(fc, vport_state, fc_vport_state, fc_vport_state_names)
+EXPORT_SYMBOL(get_fc_vport_state_name);
diff --git a/drivers/fc/fcsysfs.h b/drivers/fc/fcsysfs.h
new file mode 100644
index 0000000..9bd7936
--- /dev/null
+++ b/drivers/fc/fcsysfs.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright(c) 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#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 *fc4s[NUM_FC4S];
+
+int fc_vport_terminate(struct fc_fabric *, struct fc_vport *vport);
+
+void fc_vport_flush_devloss(struct fc_vport *fcvport);
+int fc_vport_queue_devloss_work(struct fc_vport *fcvport,
+ struct delayed_work *work,
+ unsigned long delay);
+void fc_port_flush_devloss(struct fc_port *fcport);
+int fc_port_queue_devloss_work(struct fc_port *fcport,
+ struct delayed_work *work,
+ unsigned long delay);
+void fc_port_flush_work(struct fc_port *fcport);
+int fc_port_queue_work(struct fc_port *fcport, struct work_struct *work);
+
+int fc_vport_bus_register(void);
+void fc_vport_bus_unregister(void);
+int fc_rport_bus_register(void);
+void fc_rport_bus_unregister(void);
+
+#define FC_PORTTYPE_MAX_NAMELEN 50
+const char *get_fc_port_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);
+
+const char *get_fc_port_state_name(enum fc_port_state table_key);
+/* Reuse fc_port_state enum function for port_last_state */
+#define get_fc_port_last_state_name get_fc_port_state_name
+
+const char *get_fc_vport_state_name(enum fc_vport_state table_key);
+
+#endif /*_FC_SYSFS_H_*/
diff --git a/drivers/fc/fcvport.c b/drivers/fc/fcvport.c
new file mode 100644
index 0000000..3dcfb49
--- /dev/null
+++ b/drivers/fc/fcvport.c
@@ -0,0 +1,687 @@
+/*
+ * Copyright(c) 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "fcsysfs.h"
+
+static atomic_t fcvport_num;
+
+static void fc_vport_sched_delete(struct work_struct *work);
+
+/* used by node_name */
+/* used by port_name */
+/* used by permanaent_port_name */
+#define fc_vport_rd_attr_cast(field, format_string, sz, cast) \
+ fc_show_function(fc, vport, field, format_string, sz, (cast)) \
+ static FC_DEVICE_ATTR(fcvport, field, S_IRUGO, \
+ show_vport_##field, NULL)
+
+/*
+ * TODO: Notce that we're still using get_fc_##title##_name and not
+ * a get_vport_##title##_name routine. The port_type structure,
+ * related lookups and names need to be somewhere global for rports
+ * and other fc_host obects.
+ */
+
+/* used by port_state */
+/*
+ * TODO: Can't port_state use a fc_vport_update_show_function?
+ * No- because it's enum specific, but are there enough
+ * rd_enum_attr routines that there should be a common
+ * fc_update_rd_enum_attr?
+ */
+#define fc_vport_update_rd_enum_attr(title, maxlen) \
+ static ssize_t show_vport_##title(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+ struct fc_vport *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(fc_vport_##title(fcvport)); \
+ if (!name) \
+ return -EINVAL; \
+ return snprintf(buf, maxlen, "%s\n", name); \
+ } \
+ static FC_DEVICE_ATTR(fcvport, title, S_IRUGO, show_vport_##title, NULL)
+
+#define fc_vport_store_function(field) \
+ static ssize_t store_vport_##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 fc_vport_store_str_function(field, slen) \
+ static ssize_t store_vport_##field(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+ { \
+ struct fc_vport *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; \
+ }
+
+/* used by port_id */
+#define fc_vport_update_rd_attr(field, format_string, sz) \
+ fc_update_show_function(fc, vport, field, format_string, sz, ) \
+ static FC_DEVICE_ATTR(fcvport, field, S_IRUGO, \
+ show_vport_##field, NULL)
+
+/* used by port_last_state */
+#define fc_vport_rd_enum_attr(title, maxlen) \
+ static ssize_t show_vport_##title(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+ struct fc_vport *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_vport_##title, NULL)
+
+fc_vport_rd_enum_attr(vport_state, FC_VPORT_MAX_NAMELEN);
+fc_vport_update_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN);
+fc_vport_rd_enum_attr(port_last_state, FC_PORTSTATE_MAX_NAMELEN);
+fc_vport_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
+fc_vport_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
+fc_vport_rd_attr_cast(permanent_port_name, "0x%llx\n", 20,
+ unsigned long long);
+
+static ssize_t show_vport_roles(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fc_vport *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_vport_roles, NULL);
+
+fc_show_function(fc, vport, symbolic_name, "%s\n",
+ FC_VPORT_SYMBOLIC_NAMELEN + 1, )
+fc_vport_store_str_function(symbolic_name, FC_VPORT_SYMBOLIC_NAMELEN)
+static FC_DEVICE_ATTR(fcvport, symbolic_name, S_IRUGO | S_IWUSR,
+ show_vport_symbolic_name, store_vport_symbolic_name);
+
+static ssize_t store_vport_issue_lip(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fc_vport *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_vport_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(fc, 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_vport_tgtid_bind_type(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fc_vport *fcvport = dev_to_fcvport(dev);
+ const char *name;
+
+ name = get_fc_tgtid_bind_type_name(fc_vport_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_vport_tgtid_bind_type(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fc_vport *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 != fc_vport_tgtid_bind_type(fcvport)) {
+ spin_lock_irqsave(&fcvport->lock, flags);
+ while (!list_empty(&fc_vport_rport_bindings(fcvport))) {
+ get_list_head_entry(rport,
+ &fc_vport_rport_bindings(fcvport),
+ peers);
+ list_del(&rport->peers);
+ rport->port_state = FC_PORTSTATE_DELETED;
+ fc_vport_queue_work(fcvport, &rport->rport_delete_work);
+ }
+ spin_unlock_irqrestore(&fcvport->lock, flags);
+ }
+
+ fc_vport_tgtid_bind_type(fcvport) = val;
+ return count;
+}
+
+static FC_DEVICE_ATTR(fcvport, tgtid_bind_type, S_IRUGO | S_IWUSR,
+ show_vport_tgtid_bind_type,
+ store_vport_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_vport *fcvport = dev_to_fcvport(dev);
+
+ fc_vport_queue_work(fcvport, &fcvport->vport_delete_work);
+ return count;
+}
+static FC_DEVICE_ATTR(fcvport, 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 *fcvport = dev_to_fcvport(dev);
+ int stat;
+
+ if (fcvport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))
+ return -EBUSY;
+
+ /*
+ * TODO: This PORTSTATE_DISABLED is a hack, I somehow
+ * broke this when I merged the two port states.
+ */
+
+ if (*buf == '0') {
+ if (fcvport->port_state != FC_VPORT_DISABLED)
+ return -EALREADY;
+ } else if (*buf == '1') {
+ if (fcvport->port_state == FC_VPORT_DISABLED)
+ return -EALREADY;
+ } else
+ return -EINVAL;
+
+ stat = fcvport->f->vport_disable(fcvport,
+ ((*buf == '0') ? false : true));
+ return stat ? stat : count;
+}
+static FC_DEVICE_ATTR(fcvport, vport_disable, S_IWUSR,
+ NULL, store_fc_vport_disable);
+
+fc_vport_update_rd_attr(port_id, "0x%06x\n", 20);
+fc_vport_rd_enum_attr(port_type, FC_PORTTYPE_MAX_NAMELEN);
+
+/**
+ * fc_vport_release() - Release the FC vport memory
+ * @dev: Pointer to the vport's embedded device
+ *
+ * Called when the last FC vport reference is released.
+ */
+static void fc_vport_release(struct device *dev)
+{
+ struct fc_vport *fcvport = dev_to_fcvport(dev);
+
+ struct workqueue_struct *work_q;
+
+ fc_vport_flush_work(fcvport);
+
+ /* 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);
+ }
+
+ kfree(fcvport);
+}
+
+/**
+ * fc_vport_del() - Delete a FC vport
+ * @fcvport: The FC vport to be deleted
+ *
+ * If the FC vport is an N_Port then delete any NPIV vports
+ * associated with it. Delete any rports discovered on this
+ * vport and then detach the vport from sysfs. The final
+ * reference release is done in fc_vport_free().
+ */
+void fc_vport_del(struct fc_vport *fcvport)
+{
+ struct fc_fabric *fcfabric = fcvport_to_fcfabric(fcvport);
+ struct fc_vport *vport = NULL, *next_vport = NULL;
+ struct fc_rport *rport = NULL, *next_rport = NULL;
+ unsigned long flags;
+
+ if (!fc_vport_added(fcvport))
+ return;
+
+ spin_lock_irqsave(&fcvport->lock, flags);
+
+ if (fc_vport_is_nport(fcvport)) {
+ /* Remove any vports */
+ list_for_each_entry_safe(vport, next_vport,
+ &fcfabric->vports, peers) {
+ fc_vport_queue_work(fcvport, &vport->vport_delete_work);
+ }
+ }
+
+ /* 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_vport_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_vport_queue_work(fcvport, &rport->rport_delete_work);
+ }
+ spin_unlock_irqrestore(&fcvport->lock, flags);
+
+ fc_vport_flush_work(fcvport);
+
+ put_device(&fcvport->dev); /* For rports list */
+ device_del(&fcvport->dev);
+ put_device(fcvport->dev.parent);
+ fcvport->dev.parent = NULL;
+}
+EXPORT_SYMBOL(fc_vport_del);
+
+/**
+ * fc_vport_free() - Drop the final FC vport reference
+ * @fcvport: Pointer to the FC vport to be released
+ *
+ * Since vport's are allocated separately from their
+ * sysfs addition their tear-down routines are also
+ * split such that the callers first call fc_vport_del()
+ * and then fc_vport_free().
+ */
+void fc_vport_free(struct fc_vport *fcvport)
+{
+ put_device(&fcvport->dev); /* self-reference */
+}
+EXPORT_SYMBOL(fc_vport_free);
+
+/**
+ * fc_vport_alloc() - Allocate a FC vport (does not add to sysfs)
+ * @fcvport: The N_Port if the vport to be allocated is a VN_Port
+ * @ids: The FC vport identifiers
+ * @f: The vport function template
+ * @channel: The channel
+ * @fc4: The supported FC4s for this vport
+ *
+ * Some LLDs need the memory allocated with the N_Port vport before
+ * they are ready to create FC port or fabric devices. To accomodate
+ * this need alloc and add are split for vports. This allows these
+ * LLDs to allocate vports and then add them later. For LLDs without
+ * this issue they can just call add immediately after alloc.
+ */
+struct fc_vport *fc_vport_alloc(struct fc_vport *fcnport,
+ struct fc_vport_identifiers *ids,
+ struct fc_vport_function_template *f,
+ int channel, enum possible_fc4s fc4)
+{
+ struct fc_vport *fcvport;
+ unsigned long flags;
+ int error = 0;
+
+ fcvport = kzalloc(sizeof(struct fc_vport) + f->dd_fcvport_size,
+ GFP_KERNEL);
+ if (!fcvport)
+ goto out;
+
+ fcvport->port_state = FC_PORTSTATE_UNKNOWN;
+ fcvport->port_last_state = FC_PORTSTATE_UNKNOWN;
+ fcvport->permanent_port_name = -1;
+ fcvport->node_name = ids->node_name;
+ fcvport->port_name = ids->port_name;
+ fcvport->roles = ids->roles;
+ fcvport->port_type = ids->vport_type;
+ strncpy(fcvport->symbolic_name, ids->symbolic_name,
+ strlen(ids->symbolic_name));
+ fcvport->channel = channel;
+ fcvport->id = atomic_inc_return(&fcvport_num) - 1;
+ fcvport->tgtid_bind_type = FC_TGTID_BIND_BY_WWPN;
+ fcvport->rport_dev_loss_tmo = fc_dev_loss_tmo;
+
+ 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->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),
+ "fcvport_wq_%d", fcvport->id);
+ fcvport->work_q = create_singlethread_workqueue(
+ fcvport->work_q_name);
+ if (!fcvport->work_q)
+ goto out_del;
+
+ 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)
+ goto out_del_q;
+
+ spin_lock_irqsave(&fcvport->lock, flags);
+ fcvport->flags &= ~FC_VPORT_CREATING;
+ spin_unlock_irqrestore(&fcvport->lock, flags);
+
+ if (fcnport) {
+ fcvport->fcnport = fcnport;
+ error = fcvport->f->vport_create(fcnport, fcvport, 0);
+ if (error)
+ goto out_del_q2;
+ }
+
+ return fcvport;
+
+out_del_q2:
+ destroy_workqueue(fcvport->devloss_work_q);
+ fcvport->devloss_work_q = NULL;
+out_del_q:
+ destroy_workqueue(fcvport->work_q);
+ fcvport->work_q = NULL;
+out_del:
+ kfree(fcvport);
+out:
+ return NULL;
+}
+EXPORT_SYMBOL(fc_vport_alloc);
+
+/**
+ * fc_vport_add() - Add a FC vport to a FC fabric
+ * @fcvport: The FC vport to add
+ *
+ * Add the device to sysfs and then populate it with
+ * attributes.
+ */
+int fc_vport_add(struct fc_fabric *fcfabric,
+ struct fc_vport *fcvport)
+{
+ int count = 0;
+ int error = 0;
+
+ if (fc_vport_added(fcvport)) {
+ error = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * The libfc issue_lip causes a reset and when the
+ * local port gets the FLOGI response it will attempt
+ * to add the vport again. Check to see if it's
+ * already attached to the fabric before readding.
+ * The test is if the vport's parent is the fabric.
+ */
+ if (fcvport->dev.parent == &fcfabric->dev) {
+ error = -EINVAL;
+ goto out;
+ }
+
+ fcvport->fcfabric = fcfabric;
+
+ fcvport->dev.parent = get_device(&fcfabric->dev);
+ fcvport->dev.bus = &fc_vport_bus_type;
+ fcvport->dev.release = fc_vport_release;
+
+ error = device_add(&fcvport->dev);
+ if (error)
+ goto out;
+
+ FC_SETUP_COND_ATTR_RD(fcvport, port_id);
+ FC_SETUP_COND_ATTR_RD(fcvport, node_name);
+ FC_SETUP_COND_ATTR_RD(fcvport, port_name);
+ FC_SETUP_COND_ATTR_RD(fcvport, port_type);
+ FC_SETUP_COND_ATTR_RD(fcvport, permanent_port_name);
+
+ FC_SETUP_ATTR_RD_NS(fcvport, port_state);
+ FC_SETUP_ATTR_RD_NS(fcvport, port_last_state);
+ FC_SETUP_ATTR_RD_NS(fcvport, roles);
+ FC_SETUP_ATTR_RD_NS(fcvport, symbolic_name);
+ FC_SETUP_ATTR_RW_NS(fcvport, tgtid_bind_type);
+
+ FC_SETUP_ATTR_RD_NS(fcvport, vport_state);
+
+ FC_SETUP_ATTR_RW_NS(fcvport, vport_disable);
+ FC_SETUP_ATTR_RW_NS(fcvport, vport_delete);
+
+ if (fcvport->f->issue_fcvport_lip)
+ FC_SETUP_ATTR_RW_NS(fcvport, issue_lip);
+
+ BUG_ON(count > FC_VPORT_NUM_ATTRS);
+ FC_CREATE_ATTRS(fcvport, count);
+
+ if (error || count != 0)
+ goto out_del_dev;
+
+ return 0;
+
+out_del_dev:
+ device_del(&fcvport->dev);
+out:
+ return error;
+}
+EXPORT_SYMBOL(fc_vport_add);
+
+/**
+ * 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_vport *vport =
+ container_of(work, struct fc_vport, vport_delete_work);
+ struct fc_fabric *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 "
+ "fabric %d channel %d - error %d\n", __func__,
+ dev_name(&vport->dev), fcfabric->id,
+ vport->channel, stat);
+}
+
+/**
+ * fc_vport_queue_work() - Queue work to the fcpinit workqueue
+ * @fcvport: Pointer to the FC vport to who owns the workqueue
+ * @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_vport_queue_work(struct fc_vport *fcvport, struct work_struct *work)
+{
+ if (unlikely(!fc_vport_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(fc_vport_work_q(fcvport), work);
+}
+EXPORT_SYMBOL(fc_vport_queue_work);
+
+/**
+ * fc_vport_flush_work() - Flush a FC vport's workqueue
+ * @fcvport: Pointer to the FC vport whose workqueue is to be flushed
+ */
+void fc_vport_flush_work(struct fc_vport *fcvport)
+{
+ if (!fc_vport_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(fc_vport_work_q(fcvport));
+}
+
+/**
+ * fc_vport_queue_devloss_work() - Schedule work for a FC vport's devloss workqueue
+ * @fcvport: Pointer to the FC vport who owns the devloss workqueue
+ * @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_vport_queue_devloss_work(struct fc_vport *fcvport,
+ struct delayed_work *work,
+ unsigned long delay)
+{
+ if (unlikely(!fc_vport_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(fc_vport_devloss_work_q(fcvport),
+ work, delay);
+}
+
+/**
+ * fc_vport_flush_devloss() - Flush a FC vport's devloss workqueue
+ * @fcvport: Pointer to FC vport whose workqueue is to be flushed
+ */
+void fc_vport_flush_devloss(struct fc_vport *fcvport)
+{
+ if (!fc_vport_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(fc_vport_devloss_work_q(fcvport));
+}
+
+static int fc_vport_bus_match(struct device *dev,
+ struct device_driver *drv);
+
+struct bus_type fc_vport_bus_type = {
+ .name = "fc_vport",
+ .match = fc_vport_bus_match,
+};
+EXPORT_SYMBOL(fc_vport_bus_type);
+
+static int fc_vport_bus_match(struct device *dev,
+ struct device_driver *drv)
+{
+ if (dev->bus == &fc_vport_bus_type)
+ return 1;
+ return 0;
+}
+
+int fc_vport_bus_register(void)
+{
+ return bus_register(&fc_vport_bus_type);
+}
+
+void fc_vport_bus_unregister(void)
+{
+ bus_unregister(&fc_vport_bus_type);
+}
diff --git a/include/fc/fc.h b/include/fc/fc.h
new file mode 100644
index 0000000..c59d191
--- /dev/null
+++ b/include/fc/fc.h
@@ -0,0 +1,952 @@
+/*
+ * 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_port;
+struct fc_fabric;
+struct fc_vport;
+struct fc_rport;
+
+struct fc_vport_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,
+};
+
+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,
+};
+
+/* Names are in fcsysfs.c */
+#define FC_PORTSTATE_MAX_NAMELEN 20
+#define FC_VPORT_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,
+};
+
+#define NUM_FC4S 4
+enum possible_fc4s {
+ FC4_UNKNOWN,
+ FC4_FCP_TARGET,
+ FC4_FCP_INITIATOR,
+ FC4_IP,
+};
+
+/*
+ * 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
+
+/*
+ * 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;
+};
+
+/*
+ * 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_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(pre, title, table_type, table) \
+ const char *get_##pre##_##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)
+
+#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;
+};
+
+#define FC_SERIAL_NUMBER_SIZE 80
+
+#define dev_to_fcport(d) \
+ container_of((d), struct fc_port, dev)
+
+#define dev_to_fcfabric(d) \
+ container_of((d), struct fc_fabric, dev)
+
+#define dev_to_fcvport(d) \
+ container_of((d), struct fc_vport, dev)
+
+#define dev_to_fcrport(d) \
+ container_of((d), struct fc_rport, dev)
+
+#define FC_PORT_NUM_ATTRS 9
+#define FC_VPORT_NUM_ATTRS 16
+#define FC_RPORT_NUM_ATTRS 9
+#define FC_FABRIC_NUM_ATTRS 6
+
+struct fc_rport_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_rport_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 fc_rport_function_template *f;
+ struct device_attribute attrs[FC_RPORT_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;
+} __attribute__((aligned(sizeof(unsigned long))));
+
+#define fc_rport_port_state(x) \
+ ((x)->port_state)
+#define fc_rport_port_id(x) \
+ ((x)->port_id)
+#define fc_rport_node_name(x) \
+ ((x)->node_name)
+#define fc_rport_port_name(x) \
+ ((x)->port_name)
+#define fc_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 rport_to_fcvport(r) \
+ dev_to_fcvport((r)->dev.parent)
+
+struct fc_vport_function_template {
+ int (*vport_create)(struct fc_vport *nport,
+ struct fc_vport *vport, bool);
+ int (*vport_disable)(struct fc_vport *, bool);
+ int (*vport_delete)(struct fc_vport *nport,
+ struct fc_vport *vport);
+
+ void (*get_fcvport_port_id)(struct fc_vport *);
+ void (*get_fcvport_symbolic_name)(struct fc_vport *);
+ void (*get_fcvport_port_type)(struct fc_vport *);
+ void (*set_fcvport_symbolic_name)(struct fc_vport *);
+ void (*get_fcvport_port_state)(struct fc_vport *);
+
+ int (*issue_fcvport_lip)(struct fc_vport *);
+
+ unsigned long show_fcvport_permanent_port_name:1;
+ 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;
+
+ u32 dd_fcvport_size;
+
+ struct fc_rport_function_template fcrport_f;
+};
+
+struct fc_fabric_function_template {
+ int (*fabric_match)(struct fc_fabric *, struct fc_fabric *);
+
+ unsigned long show_fcfabric_fabric_name:1;
+ unsigned long show_fcfabric_dev_loss_tmo:1;
+
+ u32 dd_fcfabric_size;
+};
+
+/* bit field values for struct fc_fabric "flags" field */
+#define FC_FABRIC_DEVLOSS_PENDING 0x01
+
+/* fabric states */
+enum fabric_state {
+ FC_FABRICSTATE_UNKNOWN,
+ FC_FABRICSTATE_DISCONNECTED,
+ FC_FABRICSTATE_CONNECTED,
+};
+
+struct fc_fabric {
+ struct device dev;
+ struct fc_fabric_function_template *f;
+ struct list_head peers;
+ struct work_struct delete_work;
+ struct delayed_work dev_loss_work;
+ u32 dev_loss_tmo;
+
+ /*
+ * TODO: What is most appropriate for these, enum, mask?
+ */
+ u8 flags;
+ enum fabric_state state;
+
+ /* Fixed Attributes */
+ u64 fabric_name;
+ u16 max_npiv_vports;
+
+ /* Dynamic Attributes */
+ u16 npiv_vports_inuse;
+
+ /* Internal Data */
+ u32 next_vport_number;
+
+ u32 id;
+
+ struct list_head vports; /* uses the fcport->lock */
+ struct device_attribute attrs[FC_FABRIC_NUM_ATTRS];
+};
+
+static inline void *fc_fabric_priv(const struct fc_fabric *fcfabric)
+{
+ return (void *)(fcfabric + 1);
+}
+
+#define fc_fabric_fabric_name(x) \
+ ((x)->fabric_name)
+#define fc_fabric_dev_loss_tmo(x) \
+ ((x)->dev_loss_tmo)
+#define fc_fabric_max_npiv_vports(x) \
+ ((x)->max_npiv_vports)
+#define fc_fabric_next_vport_number(x) \
+ ((x)->next_vport_number)
+#define fc_fabric_npiv_vports_inuse(x) \
+ ((x)->npiv_vports_inuse)
+#define fc_fabric_vports(x) \
+ ((x)->vports)
+#define fcfabric_to_fcport(x) \
+ dev_to_fcport((x)->dev.parent)
+
+struct fc_port_function_template {
+ void (*get_fcport_speed)(struct fc_port *);
+ void (*get_fcport_active_fc4s)(struct fc_port *);
+ void (*set_fcport_system_hostname)(struct fc_port *);
+
+ unsigned long show_fcport_system_hostname:1;
+ unsigned long show_fcport_fab_dev_loss_tmo:1;
+ 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;
+
+ u32 dd_fcport_size;
+
+ struct fc_vport_function_template fcvport_f;
+};
+
+struct fc_port {
+ u32 id;
+ struct device dev;
+ struct list_head fabrics;
+ char work_q_name[20];
+ struct workqueue_struct *work_q;
+ char devloss_work_q_name[20];
+ struct workqueue_struct *devloss_work_q;
+ struct mutex lock;
+ struct fc_port_function_template *f;
+ void *fc4_f;
+ struct device_attribute attrs[FC_PORT_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];
+ char system_hostname[FC_SYMBOLIC_NAME_SIZE];
+
+ /*
+ * FCoE supported_speeds and speed can change on
+ * a link event so they are dynamic.
+ */
+ u32 supported_speeds;
+ u32 speed;
+
+ int fab_dev_loss_tmo;
+};
+
+static inline void *fc_port_priv(const struct fc_port *fcport)
+{
+ return (void *)(fcport + 1);
+}
+
+#define fc_port_id(x) \
+ ((x)->id)
+#define fc_port_work_q_name(x) \
+ ((x)->work_q_name)
+#define fc_port_work_q(x) \
+ ((x)->work_q)
+#define fc_port_devloss_work_q_name(x) \
+ ((x)->devloss_work_q_name)
+#define fc_port_devloss_work_q(x) \
+ ((x)->devloss_work_q)
+#define fc_port_system_hostname(x) \
+ ((x)->system_hostname)
+#define fc_port_maxframe_size(x) \
+ ((x)->maxframe_size)
+#define fc_port_supported_speeds(x) \
+ ((x)->supported_speeds)
+#define fc_port_speed(x) \
+ ((x)->speed)
+#define fc_port_fab_dev_loss_tmo(x) \
+ ((x)->fab_dev_loss_tmo)
+#define fc_port_supported_fc4s(x) \
+ ((x)->supported_fc4s)
+#define fc_port_active_fc4s(x) \
+ ((x)->active_fc4s)
+#define fc_port_supported_classes(x) \
+ ((x)->supported_classes)
+#define fc_port_serial_number(x) \
+ ((x)->serial_number)
+
+/*
+ * 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_vport {
+ struct device dev;
+ struct fc_vport_function_template *f;
+ u32 id;
+
+ /* Fixed Attributes */
+ u64 permanent_port_name;
+ u32 port_id;
+ char symbolic_name[FC_SYMBOLIC_NAME_SIZE];
+ u64 node_name;
+ u64 port_name;
+ enum fc_port_type port_type;
+ enum fc_vport_state vport_state;
+
+ struct fc_vport *fcnport; /* NULL if N_Port */
+ struct fc_fabric *fcfabric; /* fabric on which the
+ vport was created */
+ struct device_attribute attrs[FC_VPORT_NUM_ATTRS];
+
+ struct fc4_template *fc4_f;
+
+ /* Dynamic Attributes */
+ u32 rport_dev_loss_tmo; /* Remote Port loss timeout in seconds. */
+
+ /* Private (Transport-managed) Attributes */
+ enum fc_port_state port_state;
+ enum fc_port_state port_last_state;
+ u32 roles;
+ u32 vport_id; /* Admin Identifier for the vport */
+
+ /* 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 *fc_vport_priv(const struct fc_vport *fcvport)
+{
+ return (void *)(fcvport + 1);
+}
+
+#define fc_vport_port_state(x) \
+ ((x)->port_state)
+#define fc_vport_permanent_port_name(x) \
+ ((x)->permanent_port_name)
+#define fc_vport_port_id(x) \
+ ((x)->port_id)
+#define fc_vport_symbolic_name(x) \
+ ((x)->symbolic_name)
+#define fc_vport_node_name(x) \
+ ((x)->node_name)
+#define fc_vport_port_name(x) \
+ ((x)->port_name)
+#define fc_vport_port_type(x) \
+ ((x)->port_type)
+#define fc_vport_tgtid_bind_type(x) \
+ ((x)->tgtid_bind_type)
+#define fc_vport_rports(x) \
+ ((x)->rports)
+#define fc_vport_rport_dev_loss_tmo(x) \
+ ((x)->rport_dev_loss_tmo)
+#define fc_vport_rport_bindings(x) \
+ ((x)->rport_bindings)
+#define fc_vport_next_rport_number(x) \
+ ((x)->next_rport_number)
+#define fc_vport_work_q_name(x) \
+ ((x)->work_q_name)
+#define fc_vport_work_q(x) \
+ ((x)->work_q)
+#define fc_vport_devloss_work_q_name(x) \
+ ((x)->devloss_work_q_name)
+#define fc_vport_devloss_work_q(x) \
+ ((x)->devloss_work_q)
+#define fcvport_to_fcfabric(x) \
+ ((x)->fcfabric)
+
+static inline int fc_vport_added(const struct fc_vport *fcvport)
+{
+ return (fcvport->dev.parent != NULL) ? 1 : 0;
+}
+
+static inline int fc_vport_is_nport(struct fc_vport *fcvport)
+{
+ if (fc_vport_port_type(fcvport) == FC_PORTTYPE_NPORT)
+ return 1;
+ return 0;
+}
+
+/**
+ * dev_is_nport() - Is the device embedded in a FC vport an Nport
+ * @dev: Pointer to the device embedded in a FC vport
+ * @ignored: Mandatory void* needed by device_find_child()
+ */
+static inline int dev_is_nport(struct device *dev, void *ignored)
+{
+ return fc_vport_is_nport(dev_to_fcvport(dev));
+}
+
+/**
+ * fc_fabric_find_nport() - Find the NPort on a FC fabric
+ * @fcfabric: The fabric to search for the N_Port on
+ *
+ * device_find_child() grabs a reference to the FC vport
+ * representing the N_port. It must be released when the
+ * returned vport is not needed anymore.
+ */
+static inline struct fc_vport *fc_fabric_find_nport(struct fc_fabric *fcfabric)
+{
+ struct device *dev = device_find_child(&fcfabric->dev,
+ NULL, dev_is_nport);
+ if (!dev)
+ return NULL;
+ return dev_to_fcvport(dev);
+}
+
+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_vport *fcvport,
+ enum fc_vport_state new_state)
+{
+ if ((new_state != FC_VPORT_UNKNOWN) &&
+ (new_state != FC_VPORT_INITIALIZING))
+ fcvport->port_last_state = fcvport->port_state;
+ fcvport->port_state = new_state;
+}
+
+struct fc_vport *fc_vport_lookup(struct fc_fabric *fcfabric,
+ const u32 id);
+
+struct fc_port *fc_port_add(struct device *pdev,
+ struct fc_port_function_template *,
+ void *fc4_f);
+struct fc_fabric *fc_fabric_add(struct fc_port *fcport,
+ struct fc_fabric_function_template *,
+ struct fc_fabric *);
+struct fc_vport *fc_vport_alloc(struct fc_vport *fcnport,
+ struct fc_vport_identifiers *ids,
+ struct fc_vport_function_template *fcn_tmpl,
+ int channel, enum possible_fc4s fc4);
+struct fc_vport *fc_vport_create(struct fc_fabric *, int channel,
+ struct fc_vport_identifiers *);
+int fc_vport_add(struct fc_fabric *fcfabric,
+ struct fc_vport *fcvport);
+
+void fc_port_del(struct fc_port *fcport);
+void fc_port_del_fabrics(struct fc_port *fcport);
+void fc_fabric_del(struct fc_fabric *fcfabric);
+void fc_vport_del(struct fc_vport *fcvport);
+void fc_vport_free(struct fc_vport *fcvport);
+
+#define FC_DEVICE_ATTR(_prefix, _name, _mode, _show, _store) \
+ struct device_attribute device_attr_##_prefix##_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
+#define FC_CREATE_ATTRS(_var, count) \
+ while (count > 0) { \
+ sysfs_attr_init(&_var->attrs[count-1].attr); \
+ error = device_create_file(&_var->dev, &_var->attrs[count-1]); \
+ if (error) \
+ break; \
+ count--; \
+ }
+
+/* used by maxframe_size */
+/* used by supported_speeds */
+/* used by supported_fc4s */
+/* used by supported_classes */
+/* used by serial_number */
+/* used by fabric_name */
+/* used by speed */
+/* used by active_fc4s */
+/* used by port_id */
+/* used by node_name */
+/* used by port_name */
+/* used by permanent_port_name */
+#define FC_SETUP_COND_ATTR_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++; \
+ }
+
+/* used by system_hostname */
+#define FC_SETUP_COND_ATTR_RW(_var, field) \
+ { \
+ if (_var->f->show_##_var##_##field) { \
+ _var->attrs[count] = device_attr_##_var##_##field; \
+ count++; \
+ } \
+ }
+
+/* used by vport_create */
+/* used by vport_delete (fabric) */
+/* used by tgtid_bind_type */
+/* used by vport_delete (vport) */
+/* used by vport_disable */
+#define FC_SETUP_ATTR_RW_NS(_var, field) \
+ { \
+ _var->attrs[count] = device_attr_##_var##_##field; \
+ count++; \
+ }
+
+/* used by max_npiv_vports */
+/* used by npiv_vports_inuse */
+/* used by port_state */
+/* used by port_last_state */
+/* used by vport_state */
+/* used by symbolic_name */
+#define FC_SETUP_ATTR_RD_NS(_var, field) \
+ do { \
+ _var->attrs[count] = device_attr_##_var##_##field; \
+ _var->attrs[count].attr.mode = S_IRUGO; \
+ _var->attrs[count].store = NULL; \
+ count++; \
+ } while (0)
+
+/* used by fc_port_rd_attr */
+/* used by system_hostname */
+/* used by fc_fabric_rd_attr */
+#define fc_show_function(_pre, _obj, field, format_string, sz, cast) \
+ static ssize_t show_##_obj##_##field(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+ struct _pre##_##_obj *_obj = dev_to_##_pre##_obj(dev); \
+ return snprintf(buf, sz, format_string, \
+ cast _pre##_##_obj##_##field(_obj)); \
+ }
+
+#define fc_update_show_function(_pre, _obj, field, format_string, sz, cast) \
+ static ssize_t show_##_obj##_##field(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+ struct _pre##_##_obj *_obj = dev_to_##_pre##_obj(dev); \
+ if (_obj->f->get_##_pre##_obj##_##field) \
+ _obj->f->get_##_pre##_obj##_##field(_obj); \
+ return snprintf(buf, sz, format_string, \
+ cast _pre##_##_obj##_##field(_obj)); \
+ }
+
+struct fc_rport *fc_rport_add(struct fc_vport *fcvport,
+ struct fc_rport_identifiers *ids);
+void fc_rport_del(struct fc_rport *rport);
+void fc_rport_rolechg(struct fc_rport *rport, u32 roles);
+
+int fc_vport_queue_work(struct fc_vport *fcvport, struct work_struct *work);
+void fc_vport_flush_work(struct fc_vport *fcvport);
+
+struct fc4_template {
+ 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_vport *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, enum possible_fc4s fc4);
+void unregister_fc4(enum possible_fc4s fc4);
+
+extern struct bus_type fc_vport_bus_type;
+extern struct bus_type fc_rport_bus_type;
+
+#endif /* _FC_H_ */
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v2 04/10] scsi_transport_fcp: Create FC/SCSI interaction layer
2011-03-11 21:54 [RFC PATCH v2 00/10] FC Sysfs and FCoE attributes Robert Love
` (2 preceding siblings ...)
2011-03-11 21:54 ` [RFC PATCH v2 03/10] fc: Create FC sybsystem Robert Love
@ 2011-03-11 21:54 ` Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 05/10] libfc, libfcoe, fcoe: Make use of FC subsystem Robert Love
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Robert Love @ 2011-03-11 21:54 UTC (permalink / raw)
To: linux-scsi; +Cc: abjoglek, giridhar.malavali, james.smart, bprakash
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: fcp_statistics
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 | 1571 +++++++++++++++++++++++++++++++++++++
include/scsi/scsi_transport_fcp.h | 307 +++++++
4 files changed, 1889 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 4a1f029..47450a6 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 b57c532..4526c46 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..ca30566
--- /dev/null
+++ b/drivers/scsi/scsi_transport_fcp.c
@@ -0,0 +1,1571 @@
+/*
+ * Copyright(c) 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#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"
+
+static struct fc4_template fcp_fc4_template;
+
+static int fcp_bsg_hostadd(struct Scsi_Host *);
+static void fcp_bsg_remove(struct request_queue *q);
+static int fcp_bsg_targadd(struct fcp_init *fcpinit,
+ struct fcp_targ *fcptarg);
+static void fcp_bsg_request_handler(struct request_queue *q,
+ struct Scsi_Host *shost,
+ struct fcp_targ *fcptarg,
+ struct device *dev);
+static void fcp_bsg_softirq_done(struct request *rq);
+static enum blk_eh_timer_return fcp_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 fcp_init_event_code value;
+ char *name;
+} fcp_init_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(fcp, init_event_code, fcp_init_event_code,
+ fcp_init_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 fcp_init *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_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 fcp_init_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 fcp_get_stat(stats, offset) \
+ (unsigned long long)*(u64 *)(((u8 *) (stats)) + (offset))
+
+/* Show a given an attribute in the statistics group */
+static ssize_t fcp_stat_show(const struct device *dev, char *buf,
+ unsigned long offset)
+{
+ struct fcp_init *fcpinit = dev_to_fcpinit(dev);
+ struct fcp_init_statistics *stats;
+ ssize_t ret = -ENOENT;
+
+ if (offset > sizeof(struct fcp_init_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",
+ fcp_get_stat(stats, offset));
+ }
+
+ return ret;
+}
+
+/* generate a read-only statistics attribute */
+#define fcp_init_statistic(name) \
+ static ssize_t show_fcstat_##name(struct device *cd, \
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+ return fcp_stat_show(cd, buf, \
+ offsetof(struct fcp_init_statistics, \
+ name)); \
+ } \
+ static FC_DEVICE_ATTR(fcpinit, name, S_IRUGO, show_fcstat_##name, NULL)
+
+fcp_init_statistic(seconds_since_last_reset);
+fcp_init_statistic(tx_frames);
+fcp_init_statistic(tx_words);
+fcp_init_statistic(rx_frames);
+fcp_init_statistic(rx_words);
+fcp_init_statistic(lip_count);
+fcp_init_statistic(nos_count);
+fcp_init_statistic(error_frames);
+fcp_init_statistic(dumped_frames);
+fcp_init_statistic(link_failure_count);
+fcp_init_statistic(loss_of_sync_count);
+fcp_init_statistic(loss_of_signal_count);
+fcp_init_statistic(prim_seq_protocol_err_count);
+fcp_init_statistic(invalid_tx_word_count);
+fcp_init_statistic(invalid_crc_count);
+fcp_init_statistic(fcp_input_requests);
+fcp_init_statistic(fcp_output_requests);
+fcp_init_statistic(fcp_control_requests);
+fcp_init_statistic(fcp_input_megabytes);
+fcp_init_statistic(fcp_output_megabytes);
+
+static ssize_t fcp_reset_statistics(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fcp_init *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(fcpinit, reset_statistics, S_IWUSR, NULL,
+ fcp_reset_statistics);
+
+static struct attribute *fcp_statistics_attrs[] = {
+ &device_attr_fcpinit_seconds_since_last_reset.attr,
+ &device_attr_fcpinit_tx_frames.attr,
+ &device_attr_fcpinit_tx_words.attr,
+ &device_attr_fcpinit_rx_frames.attr,
+ &device_attr_fcpinit_rx_words.attr,
+ &device_attr_fcpinit_lip_count.attr,
+ &device_attr_fcpinit_nos_count.attr,
+ &device_attr_fcpinit_error_frames.attr,
+ &device_attr_fcpinit_dumped_frames.attr,
+ &device_attr_fcpinit_link_failure_count.attr,
+ &device_attr_fcpinit_loss_of_sync_count.attr,
+ &device_attr_fcpinit_loss_of_signal_count.attr,
+ &device_attr_fcpinit_prim_seq_protocol_err_count.attr,
+ &device_attr_fcpinit_invalid_tx_word_count.attr,
+ &device_attr_fcpinit_invalid_crc_count.attr,
+ &device_attr_fcpinit_fcp_input_requests.attr,
+ &device_attr_fcpinit_fcp_output_requests.attr,
+ &device_attr_fcpinit_fcp_control_requests.attr,
+ &device_attr_fcpinit_fcp_input_megabytes.attr,
+ &device_attr_fcpinit_fcp_output_megabytes.attr,
+ &device_attr_fcpinit_reset_statistics.attr,
+ NULL
+};
+
+static struct attribute_group fcp_statistics_group = {
+ .name = "statistics",
+ .attrs = fcp_statistics_attrs,
+};
+
+#define fcp_targ_rd_attr(field, format_string, sz) \
+ fc_show_function(fcp, targ, field, format_string, sz, ) \
+ static FC_DEVICE_ATTR(fcptarg, field, S_IRUGO, \
+ show_targ_##field, NULL)
+
+fcp_targ_rd_attr(scsi_target_id, "%d\n", 20);
+
+/**
+ * fcp_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 fcp_timed_out(struct scsi_cmnd *scmd)
+{
+ struct scsi_target *starget = scsi_target(scmd->device);
+ struct fcp_targ *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 fcp_user_scan_tgt(struct Scsi_Host *shost, uint channel,
+ uint id, uint lun)
+{
+ struct fcp_init *fcpinit = shost_priv(shost);
+ struct fc_vport *fcvport = fcpinit_to_fcvport(fcpinit);
+ struct fc_rport *rport;
+ struct fcp_targ *fcptarg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fcvport->lock, flags);
+
+ list_for_each_entry(rport, &fc_vport_rports(fcvport), peers) {
+ if (!(rport->roles & FC_PORT_ROLE_FCP_TARGET))
+ continue;
+
+ 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(&fcptarg->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 fcp_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++)
+ fcp_user_scan_tgt(shost, chlo, tgtlo, lun);
+
+ return 0;
+}
+
+static int fcp_tsk_mgmt_response(struct Scsi_Host *shost, u64 nexus, u64 tm_id,
+ int result)
+{
+ /*
+ * This was previously calling the fc_internal template's
+ * callback, why does the Scsi_Host have one too? Is there
+ * a problem with calling the Scsi_Host's callback directly?
+ */
+ return shost->transportt->tsk_mgmt_response(shost, nexus,
+ tm_id, result);
+}
+
+static int fcp_it_nexus_response(struct Scsi_Host *shost, u64 nexus, int result)
+{
+ /*
+ * This was previously calling the fc_internal template's
+ * callback, why does the Scsi_Host have one too? Is there
+ * a problem with calling the Scsi_Host's callback directly?
+ */
+ return shost->transportt->it_nexus_response(shost, nexus, result);
+}
+
+/**
+ * fcp_init_release() - Put the Scsi_Host reference to free the fcpinit
+ * @dev: The fcpinit's embedded device
+ */
+static void fcp_init_release(struct device *dev)
+{
+ struct fcp_init *fcpinit = dev_to_fcpinit(dev);
+ struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+
+ scsi_host_put(shost);
+}
+
+/**
+ * fcp_init_del() - Delete a init instance
+ * @fcvport: The FC vport associated with the fcpinit to be deleted
+ *
+ * Determine the fcpinit from the FC vport. Flush any work on the
+ * associated Scsi_Host, remove the fcpinit and Scsi_Host from
+ * sysfs, notify the LLD and drop the self-refrence to the fcpinit.
+ */
+static int fcp_init_remove(struct device *dev)
+{
+ struct fc_vport *fcvport = dev_to_fcvport(dev);
+ struct fcp_init *fcpinit = fcvport_to_fcpinit(fcvport);
+ struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+
+ fcp_bsg_remove(fcpinit->rqst_q);
+
+ /* flush all scan work items */
+ scsi_flush_work(shost);
+
+ device_del(&fcpinit->dev);
+ put_device(fcpinit->dev.parent);
+ fcpinit->dev.parent = NULL;
+
+ /*
+ * Detach from the scsi-ml. This frees the shost,
+ * fcpinit and any LLD private data.
+ */
+ scsi_remove_host(shost);
+
+ if (fcpinit->f->fcp_fcpinit_del)
+ fcpinit->f->fcp_fcpinit_del(fcpinit);
+
+ put_device(&fcpinit->dev); /* Self Reference from device_initialize */
+
+ return 0;
+}
+
+static struct scsi_transport_template fcpinit_scsi_transport_template = {
+ /* Use the shost workq for scsi scanning */
+ .create_work_queue = 1,
+
+ .eh_timed_out = fcp_timed_out,
+
+ .user_scan = fcp_user_scan,
+
+ /* target-mode drivers' functions */
+ .tsk_mgmt_response = fcp_tsk_mgmt_response,
+ .it_nexus_response = fcp_it_nexus_response,
+};
+
+/**
+ * fcp_init_probe() - Create and add a fcpinit device
+ * @dev: The FC vport
+ *
+ * Allocate the fcpinit structure with the Scsi_Host. Add
+ * it and the Scsi_Host to sysfs and notify the LLD.
+ */
+static int fcp_init_probe(struct device *dev)
+{
+ struct fc_vport *fcvport = dev_to_fcvport(dev);
+ struct fc_fabric *fcfabric = fcvport_to_fcfabric(fcvport);
+ struct fc_port *fcport = fcfabric_to_fcport(fcfabric);
+ struct fcp_template *f = fcport->fc4_f;
+ struct Scsi_Host *shost;
+ struct fcp_init *fcpinit;
+ int error = 0;
+
+ int priv_size = sizeof(struct fcp_init) + f->fcpinit_priv_size;
+ shost = scsi_host_alloc(&f->shost_template, priv_size);
+ if (unlikely(!shost)) {
+ error = -ENOMEM;
+ goto out;
+ }
+
+ shost->transportt = &fcpinit_scsi_transport_template;
+ fcvport->fc4_f = &fcp_fc4_template;
+
+ fcpinit = shost_priv(shost);
+ fcpinit->f = f;
+
+ dev_set_drvdata(dev, fcpinit);
+ fcpinit->fcvport = fcvport;
+ fcpinit->shost = shost;
+ fcpinit->next_target_id = 0;
+
+ device_initialize(&fcpinit->dev); /* Requires a put_device */
+ fcpinit->dev.release = fcp_init_release;
+ dev_set_name(&fcpinit->dev, "fcpinit_%d", shost->host_no);
+
+ /*
+ * Need to call back to the LLD with priv memory
+ */
+ if (fcpinit->f->fcp_fcpinit_add)
+ fcpinit->f->fcp_fcpinit_add(fcpinit);
+
+ fcpinit->dev.parent = get_device(&fcvport->dev);
+
+ error = device_add(&fcpinit->dev);
+ if (error)
+ goto out_del;
+
+ if (fcpinit->f->get_fcpinit_stats)
+ error = sysfs_update_group(&fcpinit->dev.kobj,
+ &fcp_statistics_group);
+
+ /* add the new host to the SCSI-ml */
+ error = scsi_add_host(shost, &fcpinit->dev);
+ if (error)
+ goto out_del_dev;
+
+ fcp_bsg_hostadd(shost);
+ /* ignore any bsg add error - we just can't do sgio */
+
+ dev_printk(KERN_NOTICE, fcpinit->dev.parent,
+ "%s created via shost%d channel %d\n",
+ dev_name(&fcpinit->dev), shost->host_no,
+ fcvport->channel);
+
+ return 0;
+
+out_del_dev:
+ device_del(&fcpinit->dev);
+out_del:
+ put_device(&fcvport->dev);
+ scsi_host_put(shost);
+out:
+ return error;
+}
+
+/**
+ * fcp_init_post_event() - called to post an even on an fcp_init.
+ * @shost: host the event occurred on
+ * @event_number: fc event number obtained from get_fc_event_number()
+ * @event_code: fcp_init event being posted
+ * @event_data: 32bits of data for the event being posted
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ */
+void fcp_init_post_event(struct Scsi_Host *shost, u32 event_number,
+ enum fcp_init_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_fcp_init_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(fcp_init_post_event);
+
+/**
+ * fcp_init_post_vendor_event() - called to post a vendor unique event on an fcp_init
+ * @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 fcp_init_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(fcp_init_post_vendor_event);
+
+/**
+ * fcp_bsg_host_handler() - handler for bsg requests for a fc host
+ * @q: fc host request queue
+ */
+static void fcp_bsg_host_handler(struct request_queue *q)
+{
+ struct Scsi_Host *shost = q->queuedata;
+
+ fcp_bsg_request_handler(q, shost, NULL, &shost->shost_gendev);
+}
+
+/**
+ * fcp_bsg_hostadd() - Create and add the bsg hooks so we can receive requests
+ * @shost: shost for fcp_init
+ */
+static int fcp_bsg_hostadd(struct Scsi_Host *shost)
+{
+ struct fcp_init *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, fcp_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, fcp_bsg_softirq_done);
+ blk_queue_rq_timed_out(q, fcp_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;
+}
+
+/**
+ * fcp_bsg_remove() - Deletes the bsg hooks on fchosts/fcptargs
+ * @q: the request_queue that is to be torn down.
+ */
+void fcp_bsg_remove(struct request_queue *q)
+{
+ if (q) {
+ bsg_unregister_queue(q);
+ blk_cleanup_queue(q);
+ }
+}
+
+/**
+ * fcp_targ_dev_release() - Free the fcptarg's memory
+ * @dev: The fcptarg's embedded device
+ *
+ * Called when the last fcptarg reference is released.
+ */
+static void fcp_targ_dev_release(struct device *dev)
+{
+ struct fcp_targ *fcptarg = dev_to_fcptarg(dev);
+ kfree(fcptarg);
+}
+
+/**
+ * scsi_is_fcptarg() - Determines if a device is a fcptarg
+ * @dev: The device to check
+ */
+int scsi_is_fcptarg(const struct device *dev)
+{
+ return dev->release == fcp_targ_dev_release;
+}
+EXPORT_SYMBOL(scsi_is_fcptarg);
+
+/**
+ * fcp_targ_probe() - Create a SCSI-FCP targ device
+ * @dev: The embedded device in the FC rport
+ *
+ * Called from the FC layer when an rport is added to
+ * a SCSI-FCP capable initiator. Attaches the FC rport
+ * to the scsi_target device.
+ */
+static int fcp_targ_probe(struct device *dev)
+{
+ struct fc_rport *rport = dev_to_fcrport(dev);
+ struct fc_vport *fcvport = rport_to_fcvport(rport);
+ struct fcp_init *fcpinit = fcvport_to_fcpinit(fcvport);
+ struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+ struct fcp_targ *fcptarg;
+ int error = 0;
+ int count = 0;
+
+ fcptarg = kzalloc(sizeof(struct fcp_targ), GFP_KERNEL);
+ if (unlikely(!fcptarg)) {
+ error = -ENOMEM;
+ goto out;
+ }
+
+ dev_set_drvdata(dev, 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;
+
+ device_initialize(&fcptarg->dev); /* takes self reference */
+ fcptarg->dev.parent = get_device(&shost->shost_gendev);
+
+ fcptarg->dev.release = fcp_targ_dev_release;
+ dev_set_name(&fcptarg->dev, "fcptarg-%d:%d-%d",
+ shost->host_no, rport->channel, rport->number);
+
+ error = device_add(&fcptarg->dev);
+ if (error)
+ goto out_del;
+
+ fcp_bsg_targadd(fcpinit, fcptarg);
+ /* ignore any bsg add error - we just can't do sgio */
+
+ FC_SETUP_ATTR_RD_NS(fcptarg, scsi_target_id);
+
+ BUG_ON(count > FCP_TARG_NUM_ATTRS);
+ FC_CREATE_ATTRS(fcptarg, count);
+
+ if (error || count != 0) {
+ error = -EINVAL;
+ goto out_del_dev;
+ }
+
+ return 0;
+
+out_del_dev:
+ put_device(&shost->shost_gendev);
+out_del:
+ kfree(fcptarg);
+out:
+ return error;
+}
+
+/**
+ * fcp_targ_remove() - Delete a fcptarg from sysfs
+ * @dev: The FC rport associated with the fcptarg
+ */
+static int fcp_targ_remove(struct device *dev)
+{
+ struct fc_rport *rport = dev_to_fcrport(dev);
+ struct fcp_targ *fcptarg = rport_to_fcptarg(rport);
+
+ fcp_bsg_remove(fcptarg->rqst_q);
+
+ /* Delete SCSI target and sdevs */
+ if (fcptarg->scsi_target_id != -1)
+ fcp_starget_delete(rport);
+
+ device_del(&fcptarg->dev);
+ put_device(fcptarg->dev.parent);
+ put_device(&fcptarg->dev);
+
+ return 0;
+}
+
+/**
+ * fcp_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.
+ */
+int fcp_block_scsi_eh(struct scsi_cmnd *cmnd)
+{
+ struct fcp_targ *fcptarg = starget_to_fcptarg(
+ scsi_target(cmnd->device));
+ struct fc_rport *rport = fcptarg->rport;
+ struct fcp_init *fcpinit = fcptarg->fcpinit;
+ struct fc_vport *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);
+
+ if (rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)
+ return FAST_IO_FAIL;
+
+ return 0;
+}
+EXPORT_SYMBOL(fcp_block_scsi_eh);
+
+/*
+ * BSG support
+ */
+
+/**
+ * fcp_destroy_bsgjob - routine to teardown/delete a fc bsg job
+ * @job: fcp_bsg_job that is to be torn down
+ */
+static void fcp_destroy_bsgjob(struct fcp_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);
+}
+
+/**
+ * fcp_bsg_jobdone - completion routine for bsg requests that the LLD has
+ * completed
+ * @job: fcp_bsg_job that is complete
+ */
+static void fcp_bsg_jobdone(struct fcp_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);
+}
+
+/**
+ * fcp_bsg_softirq_done - softirq done routine for destroying the bsg requests
+ * @rq: BSG request that holds the job to be destroyed
+ */
+static void fcp_bsg_softirq_done(struct request *rq)
+{
+ struct fcp_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);
+ fcp_destroy_bsgjob(job);
+}
+
+/**
+ * fcp_bsg_job_timeout - handler for when a bsg request timesout
+ * @req: request that timed out
+ */
+static enum blk_eh_timer_return fcp_bsg_job_timeout(struct request *req)
+{
+ struct fcp_bsg_job *job = (void *) req->special;
+ struct Scsi_Host *shost = job->shost;
+ struct fcp_init *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 fcp_bsg_map_buffer(struct fcp_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;
+}
+
+
+/**
+ * fcp_req_to_bsgjob - Allocate/create the fcp_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 fcp_req_to_bsgjob(struct Scsi_Host *shost, struct fc_rport *rport,
+ struct request *req)
+{
+ struct fcp_init *fcpinit = shost_priv(shost);
+ struct request *rsp = req->next_rq;
+ struct fcp_bsg_job *job;
+ int ret;
+
+ BUG_ON(req->special);
+
+ job = kzalloc(sizeof(struct fcp_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 = fcp_bsg_map_buffer(&job->request_payload, req);
+ if (ret)
+ goto failjob_rls_job;
+ }
+ if (rsp && rsp->bio) {
+ ret = fcp_bsg_map_buffer(&job->reply_payload, rsp);
+ if (ret)
+ goto failjob_rls_rqst_payload;
+ }
+ job->job_done = fcp_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 */
+};
+
+/**
+ * fcp_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
+fcp_bsg_host_dispatch(struct request_queue *q, struct Scsi_Host *shost,
+ struct fcp_bsg_job *job)
+{
+ struct fcp_init *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);
+ fcp_bsg_jobdone(job);
+ return FC_DISPATCH_UNLOCKED;
+}
+
+
+/*
+ * fcp_bsg_goose_queue - restart rport queue in case it was stopped
+ * @rport: rport to be restarted
+ */
+static void fcp_bsg_goose_queue(struct fc_rport *rport)
+{
+ struct fcp_targ *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);
+}
+
+/**
+ * fcp_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
+fcp_bsg_rport_dispatch(struct request_queue *q, struct Scsi_Host *shost,
+ struct fc_rport *rport, struct fcp_bsg_job *job)
+{
+ struct fcp_init *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);
+ fcp_bsg_jobdone(job);
+ return FC_DISPATCH_UNLOCKED;
+}
+
+
+/**
+ * fcp_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 fcp_bsg_request_handler(struct request_queue *q,
+ struct Scsi_Host *shost,
+ struct fcp_targ *fcptarg,
+ struct device *dev)
+{
+ struct fc_rport *rport = fcptarg->rport;
+ struct request *req;
+ struct fcp_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 = fcp_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);
+ fcp_bsg_jobdone(job);
+ spin_lock_irq(q->queue_lock);
+ continue;
+ }
+
+ /* the dispatch routines will unlock the queue_lock */
+ if (rport)
+ ret = fcp_bsg_rport_dispatch(q, shost, rport, job);
+ else
+ ret = fcp_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);
+}
+
+/**
+ * fcp_bsg_targ_handler - handler for bsg requests for a fc rport
+ * @q: rport request queue
+ */
+static void fcp_bsg_targ_handler(struct request_queue *q)
+{
+ struct fcp_targ *fcptarg = q->queuedata;
+ struct Scsi_Host *shost = fcptarg_to_shost(fcptarg);
+
+ fcp_bsg_request_handler(q, shost, fcptarg, &fcptarg->dev);
+}
+
+/**
+ * fcp_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 fcp_bsg_targadd(struct fcp_init *fcpinit,
+ struct fcp_targ *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, fcp_bsg_targ_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, fcp_bsg_softirq_done);
+ blk_queue_rq_timed_out(q, fcp_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 fcp_targ *fcptarg = rport_to_fcptarg(rport);
+ scsi_target_block(&fcptarg->dev);
+}
+
+static void fcp_targ_unblock(struct fc_rport *rport)
+{
+ struct fcp_targ *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 fcp_targ *fcptarg = rport_to_fcptarg(rport);
+ fcp_targ_terminate_io(rport);
+ scsi_remove_target(&fcptarg->dev);
+}
+
+/**
+ * fcp_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 fcp_targ *fcptarg =
+ container_of(work, struct fcp_targ, 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 fcp_targ *fcptarg = rport_to_fcptarg(rport);
+ struct fc_vport *fcvport = rport_to_fcvport(rport);
+ fc_vport_queue_work(fcvport, &fcptarg->stgt_delete_work);
+}
+
+static void fcp_targ_queue_scan(struct fc_rport *rport)
+{
+ struct fcp_targ *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 fcp_targ *fcptarg =
+ container_of(work, struct fcp_targ, scan_work);
+ struct fc_rport *rport;
+ struct fc_vport *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_vport *fcvport)
+{
+ struct fcp_init *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_vport *fcvport = rport_to_fcvport(rport);
+ struct fcp_init *fcpinit = fcvport_to_fcpinit(fcvport);
+ struct fcp_targ *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;
+}
+
+/**
+ * fcp_targ_final_delete() - Final delete routine for the fcp_targ
+ * @rport: The FC rport associated with the fcp_targ
+ */
+static void fcp_targ_final_delete(struct fc_rport *rport)
+{
+ struct fc_vport *fcvport = rport_to_fcvport(rport);
+ struct fcp_init *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_bsg_goose_queue = fcp_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,
+};
+
+struct device_driver fcp_targ_drv = {
+ .name = "fcp_target",
+ .bus = &fc_rport_bus_type,
+ .probe = fcp_targ_probe,
+ .remove = fcp_targ_remove,
+};
+
+struct device_driver fcp_init_drv = {
+ .name = "fcp_initiator",
+ .bus = &fc_vport_bus_type,
+ .probe = fcp_init_probe,
+ .remove = fcp_init_remove,
+};
+
+/**
+ * fcp_transport_init() - Initialize the FCP transport
+ *
+ * Registers the FCP transport with the FC module.
+ */
+static __init int fcp_transport_init(void)
+{
+ int error;
+ error = driver_register(&fcp_init_drv);
+ if (error)
+ goto err;
+
+ error = driver_register(&fcp_targ_drv);
+ if (error)
+ goto unreg_init;
+
+ return 0;
+
+unreg_init:
+ driver_unregister(&fcp_targ_drv);
+err:
+ return error;
+}
+
+/**
+ * fcp_transport_exit() - Tear down the FCP transport
+ *
+ * Unregisters the FCP transport with the FC module.
+ */
+static void __exit fcp_transport_exit(void)
+{
+ driver_unregister(&fcp_targ_drv);
+ driver_unregister(&fcp_init_drv);
+}
+
+/* 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..ca13b48
--- /dev/null
+++ b/include/scsi/scsi_transport_fcp.h
@@ -0,0 +1,307 @@
+/*
+ * Copyright(c) 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#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 fcp_init_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
+ */
+
+/*
+ * fcp_init_event_code: If you alter this, you also need to alter
+ * scsi_transport_fc.c (for the ascii descriptions).
+ */
+enum fcp_init_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 fcp_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 fcp_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 fcp_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 fcp_bsg_buffer request_payload;
+ struct fcp_bsg_buffer reply_payload;
+
+ void *dd_data; /* Used for driver-specific storage */
+};
+
+#define FCP_TARG_NUM_ATTRS 1
+struct fcp_targ {
+ struct device dev;
+ struct device_attribute attrs[FCP_TARG_NUM_ATTRS];
+ struct fc_rport *rport;
+ struct fcp_init *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 fcp_targ, dev)
+#define fcptarg_to_rport(t) \
+ ((t)->rport)
+#define fcp_targ_scsi_target_id(x) \
+ ((x)->scsi_target_id)
+
+struct fcp_init {
+ struct device dev;
+ struct fcp_template *f;
+
+ struct Scsi_Host *shost;
+ struct fc_vport *fcvport;
+
+ struct fcp_init_statistics fcp_stats;
+
+ u32 next_target_id;
+
+ /* bsg support */
+ struct request_queue *rqst_q;
+};
+
+#define fcpinit_to_fcvport(x) \
+ ((x)->fcvport)
+
+#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 dev_to_fcpinit(d) \
+ container_of((d), struct fcp_init, dev)
+
+#define fcpinit_to_fcvport(x) \
+ ((x)->fcvport)
+
+void fcpinit_post_event(struct Scsi_Host *shost, u32 event_number,
+ enum fcp_init_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);
+int fcp_block_scsi_eh(struct scsi_cmnd *cmnd);
+
+#define starget_to_fcptarg(s) \
+ scsi_is_fcptarg(s->dev.parent) ? \
+ container_of((s->dev.parent), \
+ struct fcp_targ, dev) : NULL
+
+#define rport_to_fcptarg(r) \
+ ((struct fcp_targ *)(dev_get_drvdata(&(r)->dev)))
+
+#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 fcp_init *)(dev_get_drvdata(&(v)->dev)))
+
+#define fcpinit_to_shost(i) \
+ ((i)->shost)
+
+/**
+ * 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;
+}
+
+/**
+ * fcp_init_priv() - Return the private data from a local port
+ */
+static inline void *fcp_init_priv(const struct fcp_init *fcpinit)
+{
+ return (void *)(fcpinit + 1);
+}
+
+struct fcp_template {
+ int fcpinit_priv_size;
+ void (*fcp_fcpinit_add)(struct fcp_init *fcpinit);
+ void (*fcp_fcpinit_del)(struct fcp_init *fcpinit);
+ struct scsi_host_template shost_template;
+
+ struct fcp_init_statistics * (*get_fcpinit_stats)(struct fcp_init *);
+ void (*reset_fcpinit_stats)(struct fcp_init *);
+
+ /* bsg support */
+ int (*bsg_request)(struct fcp_bsg_job *);
+ int (*bsg_timeout)(struct fcp_bsg_job *);
+
+ /* host dynamic attributes */
+ unsigned long show_fcpinit_port_state:1;
+
+ /* TODO: Is this in the right place? */
+ u32 dd_bsg_size;
+};
+
+#endif /* _FCP_H_ */
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v2 05/10] libfc, libfcoe, fcoe: Make use of FC subsystem
2011-03-11 21:54 [RFC PATCH v2 00/10] FC Sysfs and FCoE attributes Robert Love
` (3 preceding siblings ...)
2011-03-11 21:54 ` [RFC PATCH v2 04/10] scsi_transport_fcp: Create FC/SCSI interaction layer Robert Love
@ 2011-03-11 21:54 ` Robert Love
2011-03-11 21:55 ` [RFC PATCH v2 06/10] fc: Add FCoE attributes to FC sysfs Robert Love
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Robert Love @ 2011-03-11 21:54 UTC (permalink / raw)
To: linux-scsi; +Cc: abjoglek, giridhar.malavali, james.smart, bprakash
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://www.open-fcoe.org/open-fcoe/wiki/fc-sysfs-diagrams
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
drivers/scsi/Kconfig | 1
drivers/scsi/fcoe/fcoe.c | 643 +++++++++++++++++++++--------------------
drivers/scsi/fcoe/fcoe.h | 6
drivers/scsi/fcoe/fcoe_ctlr.c | 299 ++++++++++++-------
drivers/scsi/fcoe/libfcoe.h | 4
drivers/scsi/libfc/fc_disc.c | 4
drivers/scsi/libfc/fc_exch.c | 11 -
drivers/scsi/libfc/fc_fcp.c | 120 +++-----
drivers/scsi/libfc/fc_libfc.h | 40 +--
drivers/scsi/libfc/fc_lport.c | 206 +++++++------
drivers/scsi/libfc/fc_npiv.c | 87 +++---
drivers/scsi/libfc/fc_rport.c | 19 +
include/scsi/fc_encode.h | 40 ++-
include/scsi/libfc.h | 95 ++++--
include/scsi/libfcoe.h | 18 +
15 files changed, 878 insertions(+), 715 deletions(-)
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 47450a6..6b91d11 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -692,6 +692,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 488b7d9..baed0f4 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -32,16 +32,13 @@
#include <linux/sysfs.h>
#include <linux/ctype.h>
#include <linux/workqueue.h>
-#include <scsi/scsi_tcq.h>
-#include <scsi/scsicam.h>
-#include <scsi/scsi_transport.h>
-#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>
@@ -70,7 +67,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_vport *);
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 *);
@@ -118,6 +115,13 @@ 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);
+*/
/* notification function for packets from net device */
static struct notifier_block fcoe_notifier = {
@@ -129,15 +133,45 @@ static struct notifier_block fcoe_cpu_notifier = {
.notifier_call = fcoe_cpu_callback,
};
-static struct scsi_transport_template *fcoe_nport_scsi_transport;
-static struct scsi_transport_template *fcoe_vport_scsi_transport;
-
-static int fcoe_vport_destroy(struct fc_vport *);
-static int fcoe_vport_create(struct fc_vport *, bool disabled);
+static int fcoe_vport_destroy(struct fc_vport *nport, struct fc_vport *vport);
+static int fcoe_vport_create(struct fc_vport *nport, struct fc_vport *vport,
+ bool disabled);
static int fcoe_vport_disable(struct fc_vport *, bool disable);
static void fcoe_set_vport_symbolic_name(struct fc_vport *);
static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *);
+static void fcoe_fcpinit_add(struct fcp_init *fcpinit);
+static void fcoe_fcpinit_del(struct fcp_init *fcpinit);
+
+static struct fcp_template fcoe_fcp_template = {
+ .fcpinit_priv_size = sizeof(struct fc_fcp_internal),
+ .fcp_fcpinit_add = fcoe_fcpinit_add,
+ .fcp_fcpinit_del = fcoe_fcpinit_del,
+
+ .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,
+ },
+
+ .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,
@@ -147,99 +181,112 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = {
.lport_set_port_id = fcoe_set_port_id,
};
-struct fc_function_template fcoe_nport_fc_functions = {
- .show_host_node_name = 1,
- .show_host_port_name = 1,
- .show_host_supported_classes = 1,
- .show_host_supported_fc4s = 1,
- .show_host_active_fc4s = 1,
- .show_host_maxframe_size = 1,
-
- .show_host_port_id = 1,
- .show_host_supported_speeds = 1,
- .get_host_speed = fc_get_host_speed,
- .show_host_speed = 1,
- .show_host_port_type = 1,
- .get_host_port_state = fc_get_host_port_state,
- .show_host_port_state = 1,
- .show_host_symbolic_name = 1,
-
- .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
- .show_rport_maxframe_size = 1,
- .show_rport_supported_classes = 1,
-
- .show_host_fabric_name = 1,
- .show_starget_node_name = 1,
- .show_starget_port_name = 1,
- .show_starget_port_id = 1,
- .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
- .show_rport_dev_loss_tmo = 1,
- .get_fc_host_stats = fc_get_host_stats,
- .issue_fc_host_lip = fcoe_reset,
-
- .terminate_rport_io = fc_rport_terminate_io,
-
- .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,
+struct fc_port_function_template fcoe_fcport_fcn_tmpl = {
+ .show_fcport_system_hostname = 1,
+ .show_fcport_fab_dev_loss_tmo = 1,
+ .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,
+
+ .dd_fcport_size = sizeof(struct fcoe_interface),
+
+ .fcvport_f = {
+ .vport_create = fcoe_vport_create,
+ .vport_delete = fcoe_vport_destroy,
+ .vport_disable = fcoe_vport_disable,
+
+ .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,
+
+ .issue_fcvport_lip = fcoe_reset,
+ .set_fcvport_symbolic_name = fcoe_set_vport_symbolic_name,
+ .get_fcvport_port_state = fc_get_fcvport_port_state,
+ .dd_fcvport_size = (sizeof(struct fc_lport) +
+ sizeof(struct fcoe_port)),
+
+ .fcrport_f = {
+ .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+ .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+ .terminate_rport_io = fc_rport_terminate_io,
+ },
+ },
};
-struct fc_function_template fcoe_vport_fc_functions = {
- .show_host_node_name = 1,
- .show_host_port_name = 1,
- .show_host_supported_classes = 1,
- .show_host_supported_fc4s = 1,
- .show_host_active_fc4s = 1,
- .show_host_maxframe_size = 1,
-
- .show_host_port_id = 1,
- .show_host_supported_speeds = 1,
- .get_host_speed = fc_get_host_speed,
- .show_host_speed = 1,
- .show_host_port_type = 1,
- .get_host_port_state = fc_get_host_port_state,
- .show_host_port_state = 1,
- .show_host_symbolic_name = 1,
-
- .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
- .show_rport_maxframe_size = 1,
- .show_rport_supported_classes = 1,
-
- .show_host_fabric_name = 1,
- .show_starget_node_name = 1,
- .show_starget_port_name = 1,
- .show_starget_port_id = 1,
- .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
- .show_rport_dev_loss_tmo = 1,
- .get_fc_host_stats = fc_get_host_stats,
- .issue_fc_host_lip = fcoe_reset,
-
- .terminate_rport_io = fc_rport_terminate_io,
+struct fc_fabric_function_template fcoe_fcfabric_fcn_tmpl = {
+ .fabric_match = fcoe_fcf_match,
- .bsg_request = fc_lport_bsg_request,
+ .show_fcfabric_fabric_name = 1,
+ .show_fcfabric_dev_loss_tmo = 1,
+ .dd_fcfabric_size = sizeof(struct fcoe_fcf),
};
-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,
-};
+static void _fcoe_get_vport_ids(struct fcoe_interface *fcoe,
+ struct fc_vport_identifiers *ids)
+{
+ 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, 0);
+ ids->roles = FC_PORT_ROLE_FCP_INITIATOR;
+ ids->disable = 0;
+ ids->vport_type = FC_PORTTYPE_NPORT;
+ 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);
+
+ * _fcoe_get_vport_ids sets the vport_type to
+ * FC_PORTTYPE_NPORT, but we need to change that
+ * for VN_Ports.
+
+ ids->vport_type = FC_PORTTYPE_NPIV;
+}
+*/
+
+static void fcoe_set_symbolic_name(struct fc_lport *lport)
+{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
+ snprintf(fc_vport_symbolic_name(fcvport),
+ 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;
+
+ fc_vport_node_name(lport_to_fcvport(lport)) =
+ 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;
+
+ fc_vport_port_name(lport_to_fcvport(lport)) =
+ fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 2, 0);
+}
+*/
/**
* fcoe_interface_setup() - Setup a FCoE interface
@@ -260,6 +307,9 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
fcoe->netdev = netdev;
+ /* Initial fc port configuration */
+ fc_lport_port_config(fcoe_to_fcport(fcoe));
+
/* Let LLD initialize for FCoE */
ops = netdev->netdev_ops;
if (ops->ndo_fcoe_enable) {
@@ -337,6 +387,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
enum fip_state fip_mode)
{
+ struct fc_port *fcport;
struct fcoe_interface *fcoe;
int err;
@@ -347,12 +398,15 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
goto out;
}
- fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL);
- if (!fcoe) {
- FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n");
+ fcport = fc_port_add((struct device *)&netdev->dev.parent,
+ &fcoe_fcport_fcn_tmpl, &fcoe_fcp_template);
+ if (!fcport) {
+ printk(KERN_ERR "Failed to add a fcport\n");
fcoe = ERR_PTR(-ENOMEM);
- goto out_nomod;
- }
+ goto out;
+ }
+
+ fcoe = fc_port_priv(fcport);
dev_hold(netdev);
kref_init(&fcoe->kref);
@@ -361,23 +415,21 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
* Initialize FIP.
*/
fcoe_ctlr_init(&fcoe->ctlr, fip_mode);
+ fcoe->ctlr.fcfabric_f = &fcoe_fcfabric_fcn_tmpl;
fcoe->ctlr.send = fcoe_fip_send;
fcoe->ctlr.update_mac = fcoe_update_src_mac;
fcoe->ctlr.get_src_addr = fcoe_get_src_mac;
err = fcoe_interface_setup(fcoe, netdev);
- if (err) {
- fcoe_ctlr_destroy(&fcoe->ctlr);
- kfree(fcoe);
- dev_put(netdev);
- fcoe = ERR_PTR(err);
- goto out_nomod;
- }
+ if (err)
+ goto out_free;
- goto out;
+ return fcoe;
-out_nomod:
- module_put(THIS_MODULE);
+out_free:
+ fcoe_ctlr_destroy(&fcoe->ctlr);
+ fc_port_del(fcport);
+ dev_put(netdev);
out:
return fcoe;
}
@@ -438,9 +490,8 @@ static void fcoe_interface_release(struct kref *kref)
netdev = fcoe->netdev;
/* tear-down the FCoE controller */
fcoe_ctlr_destroy(&fcoe->ctlr);
- kfree(fcoe);
+ fc_port_del(fcoe_to_fcport(fcoe));
dev_put(netdev);
- module_put(THIS_MODULE);
}
/**
@@ -634,9 +685,11 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
u32 mfs;
u64 wwnn, wwpn;
struct fcoe_interface *fcoe;
+ struct fc_vport *fcvport;
struct fcoe_port *port;
/* Setup lport private data to point to fcoe softc */
+ fcvport = lport_to_fcvport(lport);
port = lport_priv(lport);
fcoe = port->priv;
@@ -661,9 +714,14 @@ 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);
+ /*
+ * TODO: Is the NPIV port's speed, wwnn and wwpn getting set?
+ */
+
+ if (fc_vport_is_nport(fcvport)) {
+
+ fcoe_link_speed_update(lport);
- if (!lport->vport) {
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);
@@ -676,46 +734,27 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
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_add(struct fcp_init *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;
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
- if (lport->vport)
- lport->host->transportt = fcoe_vport_scsi_transport;
- else
- lport->host->transportt = fcoe_nport_scsi_transport;
-
- /* 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) = USHRT_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;
+static void fcoe_fcpinit_del(struct fcp_init *fcpinit)
+{
+ struct fc_vport *fcvport = fcpinit_to_fcvport(fcpinit);
+ struct fc_lport *lport = fc_vport_priv(fcvport);
+ fc_fcp_destroy(lport);
}
/**
@@ -831,6 +870,7 @@ skip_oem:
*/
static void fcoe_if_destroy(struct fc_lport *lport)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fcoe_port *port = lport_priv(lport);
struct fcoe_interface *fcoe = port->priv;
struct net_device *netdev = fcoe->netdev;
@@ -859,21 +899,18 @@ 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);
-
- /* Destroy lport scsi_priv */
- fc_fcp_destroy(lport);
-
/* 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);
+ /*
+ * Release the last reference. This will free the NPIV
+ * vport/lport/fcoe_port, but the N_Port vport/lport/fcoe_port
+ * will not be free'd until fc_vport_free is called
+ */
+ put_device(&fcvport->dev);
}
/**
@@ -924,33 +961,25 @@ 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 fc_vport *fcvport = lport_to_fcvport(lport);
struct net_device *netdev = fcoe->netdev;
- struct fc_lport *lport, *n_port;
- struct fcoe_port *port;
- struct Scsi_Host *shost;
+ struct fcoe_port *port = lport_priv(lport);
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);
FCOE_NETDEV_DBG(netdev, "Create Interface\n");
- if (!npiv)
- lport = libfc_host_alloc(&fcoe_shost_template, sizeof(*port));
- else
- lport = libfc_vport_create(vport, sizeof(*port));
+ /*
+ * Since the rtnl lock needs to be held for fcoe_if_destroy,
+ * that work needs to be done in a different thread from the
+ * vport_delete callback. To enusure that the NPIV vport's
+ * fcoe_port instance (LLD priv) isn't free'd before
+ * fcoe_if_destroy we need to get another reference.
+ */
+ get_device(&fcvport->dev);
- if (!lport) {
- FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n");
- rc = -ENOMEM;
- goto out;
- }
- port = lport_priv(lport);
port->lport = lport;
port->priv = fcoe;
port->max_queue_depth = FCOE_MAX_QUEUE_DEPTH;
@@ -962,15 +991,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 */
@@ -981,21 +1002,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->ctlr, &fcoe_libfc_fcn_templ, 1);
- if (rc) {
- FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the "
- "interface\n");
- goto out_lp_destroy;
- }
/*
* fcoe_em_alloc() and fcoe_hostlist_add() both
@@ -1006,14 +1012,18 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
* This is currently handled through the fcoe_config_mutex
* begin held.
*/
+
+ /* lport exch manager allocation */
if (!npiv)
- /* lport exch manager allocation */
rc = fcoe_em_config(lport);
- else {
- shost = vport_to_shost(vport);
- n_port = shost_priv(shost);
- rc = fc_exch_mgr_list_clone(n_port, lport);
- }
+
+ /*
+ * TODO: There was some EM cloning code here that got
+ * a bit tricky over rebases. If the EM isn't being
+ * re-used by NPIV ports that's probably the reason.
+ *
+ * Need to double check this.
+ */
if (rc) {
FCOE_NETDEV_DBG(netdev, "Could not configure the EM\n");
@@ -1021,53 +1031,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);
-}
-
-/**
- * fcoe_if_init() - Initialization routine for fcoe.ko
- *
- * Attaches the SW FCoE transport to the FC transport
- *
- * Returns: 0 on success
- */
-static int __init fcoe_if_init(void)
-{
- /* attach to scsi transport */
- fcoe_nport_scsi_transport =
- fc_attach_transport(&fcoe_nport_fc_functions);
- fcoe_vport_scsi_transport =
- fc_attach_transport(&fcoe_vport_fc_functions);
-
- if (!fcoe_nport_scsi_transport) {
- printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-/**
- * fcoe_if_exit() - Tear down fcoe.ko
- *
- * Detaches the SW FCoE transport from the FC transport
- *
- * Returns: 0 on success
- */
-int __exit fcoe_if_exit(void)
-{
- fc_release_transport(fcoe_nport_scsi_transport);
- fc_release_transport(fcoe_vport_scsi_transport);
- fcoe_nport_scsi_transport = NULL;
- fcoe_vport_scsi_transport = NULL;
- return 0;
+ return rc;
}
/**
@@ -1861,6 +1831,7 @@ out_nodev:
*/
static int fcoe_destroy(struct net_device *netdev)
{
+ struct fc_lport *lport;
struct fcoe_interface *fcoe;
int rc = 0;
@@ -1885,8 +1856,15 @@ static int fcoe_destroy(struct net_device *netdev)
}
fcoe_interface_cleanup(fcoe);
list_del(&fcoe->list);
+
+ lport = fcoe->ctlr.lp;
+
/* RTNL mutex is dropped by fcoe_if_destroy */
- fcoe_if_destroy(fcoe->ctlr.lp);
+ fcoe_if_destroy(lport);
+
+ fc_vport_free(lport_to_fcvport(lport));
+
+ module_put(THIS_MODULE);
out_nodev:
mutex_unlock(&fcoe_config_mutex);
return rc;
@@ -1934,7 +1912,9 @@ static bool fcoe_match(struct net_device *netdev)
static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
{
int rc;
+ struct fc_vport_identifiers ids;
struct fcoe_interface *fcoe;
+ struct fc_vport *fcvport;
struct fc_lport *lport;
mutex_lock(&fcoe_config_mutex);
@@ -1958,24 +1938,42 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
goto out_nodev;
}
+ /* fcport allocate/added in fcoe_interface_create */
fcoe = fcoe_interface_create(netdev, fip_mode);
if (IS_ERR(fcoe)) {
rc = PTR_ERR(fcoe);
goto out_nodev;
}
- lport = fcoe_if_create(fcoe, &netdev->dev, 0);
- if (IS_ERR(lport)) {
- printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
- netdev->name);
- rc = -EIO;
- fcoe_interface_cleanup(fcoe);
+ _fcoe_get_vport_ids(fcoe, &ids);
+ fcvport = fc_vport_alloc(NULL, &ids, &fcoe_fcport_fcn_tmpl.fcvport_f,
+ 0, FC4_FCP_INITIATOR);
+ if (!fcvport) {
+ rc = -ENOMEM;
goto out_free;
}
+ fc_vport_port_type(fcvport) = FC_PORTTYPE_NPORT;
+
+ lport = fc_vport_priv(fcvport);
+ lport->fcport = fcoe_to_fcport(fcoe);
+
+ /* Initialize the library */
+ fcoe_libfc_config(lport, &fcoe->ctlr, &fcoe_libfc_fcn_templ);
+ lport->fcport->maxframe_size = lport->mfs;
+
/* Make this the "master" N_Port */
fcoe->ctlr.lp = lport;
+ rc = fcoe_if_create(lport, fcoe, 0);
+ if (rc) {
+ printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
+ netdev->name);
+ rc = -EIO;
+ fcoe_interface_cleanup(fcoe);
+ goto out_del;
+ }
+
/* add to lports list */
fcoe_hostlist_add(lport);
@@ -1994,6 +1992,8 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
mutex_unlock(&fcoe_config_mutex);
return 0;
+out_del:
+ fc_vport_del(fcvport);
out_free:
fcoe_interface_put(fcoe);
out_nodev:
@@ -2014,19 +2014,21 @@ int fcoe_link_speed_update(struct fc_lport *lport)
struct net_device *netdev = fcoe_netdev(lport);
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;
+
+ fc_port_supported_speeds(lport->fcport) = link_supported_speeds;
+
if (ecmd.speed == SPEED_1000)
- lport->link_speed = FC_PORTSPEED_1GBIT;
+ fc_port_speed(lport->fcport) = FC_PORTSPEED_1GBIT;
if (ecmd.speed == SPEED_10000)
- lport->link_speed = FC_PORTSPEED_10GBIT;
+ fc_port_speed(lport->fcport) = FC_PORTSPEED_10GBIT;
return 0;
}
@@ -2111,16 +2113,10 @@ void fcoe_percpu_clean(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_vport *fcvport)
{
- struct fc_lport *lport = shost_priv(shost);
- struct fcoe_port *port = lport_priv(lport);
- struct fcoe_interface *fcoe = port->priv;
-
- fcoe_ctlr_link_down(&fcoe->ctlr);
- fcoe_clean_pending_queue(fcoe->ctlr.lp);
- if (!fcoe_link_ok(fcoe->ctlr.lp))
- fcoe_ctlr_link_up(&fcoe->ctlr);
+ struct fc_lport *lport = fc_vport_priv(fcvport);
+ fc_lport_reset(lport);
return 0;
}
@@ -2237,10 +2233,6 @@ static int __init fcoe_init(void)
/* Setup link change notification */
fcoe_dev_setup();
- rc = fcoe_if_init();
- if (rc)
- goto out_free;
-
mutex_unlock(&fcoe_config_mutex);
return 0;
@@ -2292,13 +2284,6 @@ static void __exit fcoe_exit(void)
*/
destroy_workqueue(fcoe_wq);
- /*
- * Detaching from the scsi transport must happen after all
- * destroys are done on the fcoe_wq. destroy_workqueue will
- * enusre the fcoe_wq is flushed.
- */
- fcoe_if_exit();
-
/* detach from fcoe transport */
fcoe_transport_detach(&fcoe_sw_transport);
}
@@ -2398,34 +2383,57 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,
* @vport: fc_vport object to create a new fc_host for
* @disabled: start the new fc_host in a disabled state by default?
*
+ * Only called on NPIV ports, never the N_Port.
+ *
* Returns: 0 for success
*/
-static int fcoe_vport_create(struct fc_vport *vport, bool disabled)
+static int fcoe_vport_create(struct fc_vport *fcnport,
+ struct fc_vport *fcvport,
+ 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->priv;
- 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 = fc_vport_priv(fcnport);
+ port = lport_priv(lport);
+ fcoe = port->priv;
+ netdev = fcoe->netdev;
+
+ vn_lport = fc_vport_priv(fcvport);
+ if (!vn_lport) {
+ FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n");
+ return -ENOMEM;
+ }
+
+ vn_lport->fcfabric = lport->fcfabric;
+
+ /* Initialize the library */
+ fcoe_libfc_config(vn_lport, &fcoe->ctlr, &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 (error)
+ return -EIO;
- if (IS_ERR(vn_port)) {
- printk(KERN_ERR "fcoe: fcoe_vport_create(%s) failed\n",
- netdev->name);
+ error = libfc_vport_config(lport, vn_lport, fcvport);
+ if (error)
return -EIO;
- }
+
+ fcoe_set_symbolic_name(vn_lport);
if (disabled) {
- fc_vport_set_state(vport, FC_VPORT_DISABLED);
+ fc_vport_set_state(fcvport, 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;
}
@@ -2435,16 +2443,12 @@ 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(struct fc_vport *nport,
+ struct fc_vport *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 *vn_port = fc_vport_priv(vport);
struct fcoe_port *port = lport_priv(vn_port);
- mutex_lock(&n_port->lp_mutex);
- list_del(&vn_port->list);
- mutex_unlock(&n_port->lp_mutex);
queue_work(fcoe_wq, &port->destroy_work);
return 0;
}
@@ -2456,7 +2460,7 @@ static int fcoe_vport_destroy(struct fc_vport *vport)
*/
static int fcoe_vport_disable(struct fc_vport *vport, bool disable)
{
- struct fc_lport *lport = vport->dd_data;
+ struct fc_lport *lport = fc_vport_priv(vport);
if (disable) {
fc_vport_set_state(vport, FC_VPORT_DISABLED);
@@ -2480,18 +2484,19 @@ static int fcoe_vport_disable(struct fc_vport *vport, bool disable)
*/
static void fcoe_set_vport_symbolic_name(struct fc_vport *vport)
{
- struct fc_lport *lport = vport->dd_data;
+ struct fc_lport *lport = fc_vport_priv(vport);
struct fc_frame *fp;
size_t len;
- snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE,
- "%s v%s over %s : %s", FCOE_NAME, FCOE_VERSION,
- fcoe_netdev(lport)->name, vport->symbolic_name);
+ snprintf(fc_vport_symbolic_name(vport),
+ 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(fc_vport_symbolic_name(lport_to_fcvport(lport)), 255);
fp = fc_frame_alloc(lport,
sizeof(struct fc_ct_hdr) +
sizeof(struct fc_ns_rspn) + len);
@@ -2507,7 +2512,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/fcoe.h b/drivers/scsi/fcoe/fcoe.h
index 408a6fd..a052e5b 100644
--- a/drivers/scsi/fcoe/fcoe.h
+++ b/drivers/scsi/fcoe/fcoe.h
@@ -89,6 +89,12 @@ struct fcoe_interface {
#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr)
+#define fcoe_to_fcport(fcoe) \
+ ((struct fc_port *)(((struct fc_port *)(fcoe)) - 1))
+
+#define ctlr_to_fcport(x) \
+ fcoe_to_fcport(container_of((x), struct fcoe_interface, ctlr));
+
/**
* fcoe_netdev() - Return the net device associated with a local port
* @lport: The local port to get the net device from
diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c
index c93f007..501fcc0 100644
--- a/drivers/scsi/fcoe/fcoe_ctlr.c
+++ b/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -149,8 +149,6 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode)
{
fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
fip->mode = mode;
- INIT_LIST_HEAD(&fip->fcfs);
- mutex_init(&fip->ctlr_mutex);
spin_lock_init(&fip->ctlr_lock);
fip->flogi_oxid = FC_XID_UNKNOWN;
setup_timer(&fip->timer, fcoe_ctlr_timeout, (unsigned long)fip);
@@ -168,14 +166,11 @@ EXPORT_SYMBOL(fcoe_ctlr_init);
*/
static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
{
- struct fcoe_fcf *fcf;
- struct fcoe_fcf *next;
+ struct fc_port *fcport = fip->lp->fcport;
fip->sel_fcf = NULL;
- list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
- list_del(&fcf->list);
- kfree(fcf);
- }
+ fip->lp->fcfabric = NULL;
+ fc_port_del_fabrics(fcport);
fip->fcf_count = 0;
fip->sel_time = 0;
}
@@ -194,13 +189,17 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
*/
void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
{
+ struct fc_port *fcport = fip->lp->fcport;
+ struct fc_vport *fcvport = lport_to_fcvport(fip->lp);
+
cancel_work_sync(&fip->recv_work);
skb_queue_purge(&fip->fip_recv_list);
- mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&fcport->lock);
fcoe_ctlr_set_state(fip, FIP_ST_DISABLED);
+ fcvport->fcfabric = NULL;
fcoe_ctlr_reset_fcfs(fip);
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
del_timer_sync(&fip->timer);
cancel_work_sync(&fip->timer_work);
}
@@ -216,16 +215,21 @@ EXPORT_SYMBOL(fcoe_ctlr_destroy);
*/
static void fcoe_ctlr_announce(struct fcoe_ctlr *fip)
{
+ struct fc_fabric *fcfabric;
+ struct fc_lport *lport = fip->lp;
+ struct fc_port *fcport = lport->fcport;
struct fcoe_fcf *sel;
struct fcoe_fcf *fcf;
- mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&fcport->lock);
spin_lock_bh(&fip->ctlr_lock);
kfree_skb(fip->flogi_req);
fip->flogi_req = NULL;
- list_for_each_entry(fcf, &fip->fcfs, list)
+ list_for_each_entry(fcfabric, &fcport->fabrics, peers) {
+ fcf = fc_fabric_priv(fcfabric);
fcf->flogi_sent = 0;
+ }
spin_unlock_bh(&fip->ctlr_lock);
sel = fip->sel_fcf;
@@ -233,20 +237,20 @@ static void fcoe_ctlr_announce(struct fcoe_ctlr *fip)
if (sel && !compare_ether_addr(sel->fcf_mac, fip->dest_addr))
goto unlock;
if (!is_zero_ether_addr(fip->dest_addr)) {
- printk(KERN_NOTICE "libfcoe: host%d: "
+ printk(KERN_NOTICE "libfcoe: fcport%u: "
"FIP Fibre-Channel Forwarder MAC %pM deselected\n",
- fip->lp->host->host_no, fip->dest_addr);
+ fc_port_id(fcport), fip->dest_addr);
memset(fip->dest_addr, 0, ETH_ALEN);
}
if (sel) {
- printk(KERN_INFO "libfcoe: host%d: FIP selected "
+ printk(KERN_INFO "libfcoe: fcport%u: FIP selected "
"Fibre-Channel Forwarder MAC %pM\n",
- fip->lp->host->host_no, sel->fcf_mac);
+ fc_port_id(fcport), sel->fcf_mac);
memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
fip->map_dest = 0;
}
unlock:
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
}
/**
@@ -311,7 +315,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(fc_vport_node_name(lport_to_fcvport(fip->lp)),
+ &sol->desc.wwnn.fd_wwn);
fcoe_size = fcoe_ctlr_fcoe_size(fip);
sol->desc.size.fd_desc.fip_dtype = FIP_DT_FCOE_SIZE;
@@ -336,9 +341,10 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
*/
void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
{
- mutex_lock(&fip->ctlr_mutex);
+ struct fc_port *fcport = fip->lp->fcport;
+ mutex_lock(&fcport->lock);
if (fip->state == FIP_ST_NON_FIP || fip->state == FIP_ST_AUTO) {
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
fc_linkup(fip->lp);
} else if (fip->state == FIP_ST_LINK_WAIT) {
fcoe_ctlr_set_state(fip, fip->mode);
@@ -351,18 +357,18 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
/* fall-through */
case FIP_MODE_FABRIC:
case FIP_MODE_NON_FIP:
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
fc_linkup(fip->lp);
fcoe_ctlr_solicit(fip, NULL);
break;
case FIP_MODE_VN2VN:
fcoe_ctlr_vn_start(fip);
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
fc_linkup(fip->lp);
break;
}
} else
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
}
EXPORT_SYMBOL(fcoe_ctlr_link_up);
@@ -392,14 +398,15 @@ static void fcoe_ctlr_reset(struct fcoe_ctlr *fip)
*/
int fcoe_ctlr_link_down(struct fcoe_ctlr *fip)
{
+ struct fc_port *fcport = fip->lp->fcport;
int link_dropped;
LIBFCOE_FIP_DBG(fip, "link down.\n");
- mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&fcport->lock);
fcoe_ctlr_reset(fip);
link_dropped = fip->state != FIP_ST_LINK_WAIT;
fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
if (link_dropped)
fc_linkdown(fip->lp);
@@ -470,7 +477,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(fc_vport_port_name(lport_to_fcvport(lport)),
+ &vn->fd_wwpn);
}
skb_put(skb, len);
skb->protocol = htons(ETH_P_FIP);
@@ -709,8 +717,9 @@ EXPORT_SYMBOL(fcoe_ctlr_els_send);
*/
static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
{
+ struct fc_port *fcport = fip->lp->fcport;
+ struct fc_fabric *fcfabric, *next;
struct fcoe_fcf *fcf;
- struct fcoe_fcf *next;
unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
unsigned long deadline;
unsigned long sel_time = 0;
@@ -718,15 +727,16 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
stats = per_cpu_ptr(fip->lp->dev_stats, get_cpu());
- list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
+ list_for_each_entry_safe(fcfabric, next, &fcport->fabrics, peers) {
+ fcf = fc_fabric_priv(fcfabric);
deadline = fcf->time + fcf->fka_period + fcf->fka_period / 2;
if (fip->sel_fcf == fcf) {
if (time_after(jiffies, deadline)) {
stats->MissDiscAdvCount++;
- printk(KERN_INFO "libfcoe: host%d: "
+ printk(KERN_INFO "libfcoe: fcport%u: "
"Missing Discovery Advertisement "
"for fab %16.16llx count %lld\n",
- fip->lp->host->host_no, fcf->fabric_name,
+ fc_port_id(fcport), fcf->fabric_name,
stats->MissDiscAdvCount);
} else if (time_after(next_timer, deadline))
next_timer = deadline;
@@ -734,12 +744,13 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
deadline += fcf->fka_period;
if (time_after_eq(jiffies, deadline)) {
- if (fip->sel_fcf == fcf)
+ if (fip->sel_fcf == fcf) {
fip->sel_fcf = NULL;
- list_del(&fcf->list);
+ fip->lp->fcfabric = NULL;
+ }
WARN_ON(!fip->fcf_count);
fip->fcf_count--;
- kfree(fcf);
+ fc_fabric_del(fcf_to_fabric(fcf));
stats->VLinkFailureCount++;
} else {
if (time_after(next_timer, deadline))
@@ -890,16 +901,36 @@ len_err:
return -EINVAL;
}
+int fcoe_fcf_match(struct fc_fabric *new, struct fc_fabric *old)
+{
+ struct fcoe_fcf *new_fcf = fc_fabric_priv(new);
+ struct fcoe_fcf *old_fcf = fc_fabric_priv(old);
+
+ if (new_fcf->switch_name == old_fcf->switch_name &&
+ new_fcf->fabric_name == old_fcf->fabric_name &&
+ new_fcf->fc_map == old_fcf->fc_map &&
+ compare_ether_addr(new_fcf->fcf_mac, old_fcf->fcf_mac) == 0)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(fcoe_fcf_match);
+
/**
* fcoe_ctlr_recv_adv() - Handle an incoming advertisement
* @fip: The FCoE controller receiving the advertisement
* @skb: The received FIP packet
+ *
+ * This routine creates a temporary fcfabric and fcoe_fcf before
+ * calling fabric_add because that routine will search the list
+ * of fabrics for an existing entry.
*/
static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
+ struct fc_port *fcport = fip->lp->fcport;
+ struct fc_fabric *new_fcfabric;
+ struct fc_fabric *fcfabric;
struct fcoe_fcf *fcf;
struct fcoe_fcf new;
- struct fcoe_fcf *found;
unsigned long sol_tov = msecs_to_jiffies(FCOE_CTRL_SOL_TOV);
int first = 0;
int mtu_valid;
@@ -907,29 +938,37 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
if (fcoe_ctlr_parse_adv(fip, skb, &new))
return;
- mutex_lock(&fip->ctlr_mutex);
- first = list_empty(&fip->fcfs);
- found = NULL;
- list_for_each_entry(fcf, &fip->fcfs, list) {
- if (fcf->switch_name == new.switch_name &&
- fcf->fabric_name == new.fabric_name &&
- fcf->fc_map == new.fc_map &&
- compare_ether_addr(fcf->fcf_mac, new.fcf_mac) == 0) {
- found = fcf;
- break;
- }
- }
- if (!found) {
- if (fip->fcf_count >= FCOE_CTLR_FCF_LIMIT)
- goto out;
+ new_fcfabric = kzalloc(sizeof(struct fc_fabric) +
+ sizeof(struct fcoe_fcf), GFP_ATOMIC);
+ memcpy(fc_fabric_priv(new_fcfabric), &new, sizeof(struct fcoe_fcf));
+ new_fcfabric->fabric_name = new.fabric_name;
- fcf = kmalloc(sizeof(*fcf), GFP_ATOMIC);
- if (!fcf)
- goto out;
+ mutex_lock(&fcport->lock);
+
+ if (fip->fcf_count >= FCOE_CTLR_FCF_LIMIT)
+ goto out;
+ /*
+ * Must check if the list is empty before
+ * we do the fc_fabric_add, as it will add
+ * the new fabric to the list.
+ */
+ first = list_empty(&fcport->fabrics);
+
+ fcfabric = fc_fabric_add(fcport, fip->fcfabric_f,
+ new_fcfabric);
+ if (unlikely(!fcfabric))
+ goto out;
+
+ fcf = fc_fabric_priv(fcfabric);
+
+ /* Check to see if this is a duplicate FCF */
+ if (!fcoe_fcf_match(new_fcfabric, fcfabric)) {
+ fc_fabric_max_npiv_vports(fcfabric) = USHRT_MAX;
fip->fcf_count++;
memcpy(fcf, &new, sizeof(new));
- list_add(&fcf->list, &fip->fcfs);
+ LIBFCOE_FIP_DBG(fip, "New FCF fab %16.16llx mac %pM\n",
+ fcf->fabric_name, fcf->fcf_mac);
} else {
/*
* Update the FCF's keep-alive descriptor flags.
@@ -952,9 +991,6 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
}
mtu_valid = fcoe_ctlr_mtu_valid(fcf);
fcf->time = jiffies;
- if (!found)
- LIBFCOE_FIP_DBG(fip, "New FCF fab %16.16llx mac %pM\n",
- fcf->fabric_name, fcf->fcf_mac);
/*
* If this advertisement is not solicited and our max receive size
@@ -979,8 +1015,8 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
* are sending periodic multicast advertisements.
*/
if (mtu_valid) {
- list_del(&fcf->list);
- list_add(&fcf->list, &fip->fcfs);
+ list_del(&fcfabric->peers);
+ list_add(&fcfabric->peers, &fcport->fabrics);
}
/*
@@ -995,7 +1031,8 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
mod_timer(&fip->timer, fip->sel_time);
}
out:
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
+ kfree(new_fcfabric);
}
/**
@@ -1165,6 +1202,10 @@ drop:
static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
struct fip_header *fh)
{
+ struct fc_fabric *fcfabric = fcf_to_fabric(fip->sel_fcf);
+ struct fc_lport *lport = fip->lp;
+ struct fc_port *fcport = lport->fcport;
+ struct fc_vport *fcvport;
struct fip_desc *desc;
struct fip_mac_desc *mp;
struct fip_wwn_desc *wp;
@@ -1172,7 +1213,6 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
size_t rlen;
size_t dlen;
struct fcoe_fcf *fcf = fip->sel_fcf;
- struct fc_lport *lport = fip->lp;
struct fc_lport *vn_port = NULL;
u32 desc_mask;
int is_vn_port = 0;
@@ -1223,26 +1263,28 @@ 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) ==
+ fc_vport_port_name(lport_to_fcvport(lport)) &&
ntoh24(vp->fd_fc_id) == lport->port_id) {
desc_mask &= ~BIT(FIP_DT_VN_ID);
break;
}
/* check if clr_vlink is for NPIV port */
- mutex_lock(&lport->lp_mutex);
- list_for_each_entry(vn_port, &lport->vports, list) {
+ mutex_lock(&fcport->lock);
+ list_for_each_entry(fcvport, &fcfabric->vports, peers) {
+ vn_port = fc_vport_priv(fcvport);
if (compare_ether_addr(vp->fd_mac,
fip->get_src_addr(vn_port)) == 0 &&
(get_unaligned_be64(&vp->fd_wwpn)
== vn_port->wwpn) &&
(ntoh24(vp->fd_fc_id) ==
- fc_host_port_id(vn_port->host))) {
+ lport->port_id)) {
desc_mask &= ~BIT(FIP_DT_VN_ID);
is_vn_port = 1;
break;
}
}
- mutex_unlock(&lport->lp_mutex);
+ mutex_unlock(&fcport->lock);
break;
default:
@@ -1267,12 +1309,12 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
if (is_vn_port)
fc_lport_reset(vn_port);
else {
- mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&fcport->lock);
per_cpu_ptr(lport->dev_stats,
get_cpu())->VLinkFailureCount++;
put_cpu();
fcoe_ctlr_reset(fip);
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
fc_lport_reset(fip->lp);
fcoe_ctlr_solicit(fip, NULL);
@@ -1303,6 +1345,7 @@ EXPORT_SYMBOL(fcoe_ctlr_recv);
*/
static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
+ struct fc_port *fcport = fip->lp->fcport;
struct fip_header *fiph;
struct ethhdr *eh;
enum fip_state state;
@@ -1331,7 +1374,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
if (ntohs(fiph->fip_dl_len) * FIP_BPW + sizeof(*fiph) > skb->len)
goto drop;
- mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&fcport->lock);
state = fip->state;
if (state == FIP_ST_AUTO) {
fip->map_dest = 0;
@@ -1339,7 +1382,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
state = FIP_ST_ENABLED;
LIBFCOE_FIP_DBG(fip, "Using FIP mode\n");
}
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
if (fip->mode == FIP_MODE_VN2VN && op == FIP_OP_VN2VN)
return fcoe_ctlr_vn_recv(fip, skb);
@@ -1367,6 +1410,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 = fcf_to_fabric(fip->sel_fcf);
+}
+
/**
* fcoe_ctlr_select() - Select the best FCF (if possible)
* @fip: The FCoE controller
@@ -1382,19 +1438,28 @@ drop:
*/
static struct fcoe_fcf *fcoe_ctlr_select(struct fcoe_ctlr *fip)
{
+ struct fc_port *fcport = fip->lp->fcport;
+ struct fc_fabric *fcfabric;
struct fcoe_fcf *fcf;
struct fcoe_fcf *best = fip->sel_fcf;
struct fcoe_fcf *first;
- first = list_first_entry(&fip->fcfs, struct fcoe_fcf, list);
+ fcfabric = list_first_entry(&fcport->fabrics, struct fc_fabric, peers);
+ first = fc_fabric_priv(fcfabric);
- list_for_each_entry(fcf, &fip->fcfs, list) {
+ list_for_each_entry(fcfabric, &fcport->fabrics, peers) {
+ fcf = fc_fabric_priv(fcfabric);
LIBFCOE_FIP_DBG(fip, "consider FCF fab %16.16llx "
"VFID %d mac %pM map %x val %d "
"sent %u pri %u\n",
fcf->fabric_name, fcf->vfid, fcf->fcf_mac,
fcf->fc_map, fcoe_ctlr_mtu_valid(fcf),
fcf->flogi_sent, fcf->pri);
+
+ /*
+ * Can these checks be added to fcoe_fcf_match so
+ * that we can use that routine here?
+ */
if (fcf->fabric_name != first->fabric_name ||
fcf->vfid != first->vfid ||
fcf->fc_map != first->fc_map) {
@@ -1416,7 +1481,9 @@ static struct fcoe_fcf *fcoe_ctlr_select(struct fcoe_ctlr *fip)
if (!best || fcf->pri < best->pri || best->flogi_sent)
best = fcf;
}
- fip->sel_fcf = best;
+
+ fcoe_ctlr_assign_fcf(fip, best);
+
if (best) {
LIBFCOE_FIP_DBG(fip, "using FCF mac %pM\n", best->fcf_mac);
fip->port_ka_time = jiffies +
@@ -1477,10 +1544,11 @@ static int fcoe_ctlr_flogi_send_locked(struct fcoe_ctlr *fip)
*/
static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *fip)
{
+ struct fc_port *fcport = fip->lp->fcport;
struct fcoe_fcf *fcf;
int error;
- mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&fcport->lock);
spin_lock_bh(&fip->ctlr_lock);
LIBFCOE_FIP_DBG(fip, "re-sending FLOGI - reselect\n");
fcf = fcoe_ctlr_select(fip);
@@ -1493,7 +1561,7 @@ static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *fip)
error = fcoe_ctlr_flogi_send_locked(fip);
}
spin_unlock_bh(&fip->ctlr_lock);
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
return error;
}
@@ -1508,6 +1576,8 @@ static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *fip)
*/
static void fcoe_ctlr_flogi_send(struct fcoe_ctlr *fip)
{
+ struct fc_port *fcport = fip->lp->fcport;
+ struct fc_fabric *fcfabric;
struct fcoe_fcf *fcf;
spin_lock_bh(&fip->ctlr_lock);
@@ -1526,8 +1596,12 @@ static void fcoe_ctlr_flogi_send(struct fcoe_ctlr *fip)
fcf = fcoe_ctlr_select(fip);
if (!fcf || fcf->flogi_sent) {
LIBFCOE_FIP_DBG(fip, "sending FLOGI - clearing\n");
- list_for_each_entry(fcf, &fip->fcfs, list)
+ mutex_lock(&fcport->lock);
+ list_for_each_entry(fcfabric, &fcport->fabrics, peers) {
+ fcf = fc_fabric_priv(fcfabric);
fcf->flogi_sent = 0;
+ }
+ mutex_unlock(&fcport->lock);
fcf = fcoe_ctlr_select(fip);
}
}
@@ -1560,8 +1634,11 @@ static void fcoe_ctlr_timeout(unsigned long arg)
*/
static void fcoe_ctlr_timer_work(struct work_struct *work)
{
+ struct fc_port *fcport;
struct fcoe_ctlr *fip;
- struct fc_lport *vport;
+ struct fc_lport *vn_lport;
+ struct fc_vport *fcvport = NULL;
+ struct fc_fabric *fcfabric = NULL;
u8 *mac;
u8 reset = 0;
u8 send_ctlr_ka = 0;
@@ -1573,9 +1650,12 @@ static void fcoe_ctlr_timer_work(struct work_struct *work)
fip = container_of(work, struct fcoe_ctlr, timer_work);
if (fip->mode == FIP_MODE_VN2VN)
return fcoe_ctlr_vn_timeout(fip);
- mutex_lock(&fip->ctlr_mutex);
+
+ fcport = fip->lp->fcport;
+ mutex_lock(&fcport->lock);
+
if (fip->state == FIP_ST_DISABLED) {
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
return;
}
@@ -1612,9 +1692,9 @@ static void fcoe_ctlr_timer_work(struct work_struct *work)
if (time_after(next_timer, fip->port_ka_time))
next_timer = fip->port_ka_time;
}
- if (!list_empty(&fip->fcfs))
+ if (!list_empty(&fcport->fabrics))
mod_timer(&fip->timer, next_timer);
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
if (reset) {
fc_lport_reset(fip->lp);
@@ -1626,14 +1706,22 @@ static void fcoe_ctlr_timer_work(struct work_struct *work)
fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr);
if (send_port_ka) {
- mutex_lock(&fip->lp->lp_mutex);
+ /*
+ * TODO: lp_mutex previously protected the get_src_addr
+ * and send_keep_alive as well as the for_each vports search.
+ * Is it a problem that now only the fabric mutex is
+ * protecting the for_each vports search?
+ */
mac = fip->get_src_addr(fip->lp);
fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac);
- list_for_each_entry(vport, &fip->lp->vports, list) {
- mac = fip->get_src_addr(vport);
- fcoe_ctlr_send_keep_alive(fip, vport, 1, mac);
+ fcfabric = fcf_to_fabric(fip->sel_fcf);
+ mutex_lock(&fcport->lock);
+ list_for_each_entry(fcvport, &fcfabric->vports, peers) {
+ vn_lport = fc_vport_priv(fcvport);
+ mac = fip->get_src_addr(vn_lport);
+ fcoe_ctlr_send_keep_alive(fip, vn_lport, 1, mac);
}
- mutex_unlock(&fip->lp->lp_mutex);
+ mutex_unlock(&fcport->lock);
}
}
@@ -1669,6 +1757,7 @@ static void fcoe_ctlr_recv_work(struct work_struct *recv_work)
int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
struct fc_frame *fp)
{
+ struct fc_port *fcport = lport->fcport;
struct fc_frame_header *fh;
u8 op;
u8 *sa;
@@ -1682,9 +1771,9 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
fip->flogi_oxid == ntohs(fh->fh_ox_id)) {
- mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&fcport->lock);
if (fip->state != FIP_ST_AUTO && fip->state != FIP_ST_NON_FIP) {
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
return -EINVAL;
}
fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
@@ -1704,13 +1793,13 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
fip->map_dest = 0;
}
fip->flogi_oxid = FC_XID_UNKNOWN;
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
fc_fcoe_set_mac(fr_cb(fp)->granted_mac, fh->fh_d_id);
} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
/*
* Save source MAC for point-to-point responses.
*/
- mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&fcport->lock);
if (fip->state == FIP_ST_AUTO || fip->state == FIP_ST_NON_FIP) {
memcpy(fip->dest_addr, sa, ETH_ALEN);
fip->map_dest = 0;
@@ -1719,7 +1808,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
"Setting non-FIP mode\n");
fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
}
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
}
return 0;
}
@@ -1884,13 +1973,14 @@ static void fcoe_ctlr_vn_rport_callback(struct fc_lport *lport,
struct fc_rport_priv *rdata,
enum fc_rport_event event)
{
+ struct fc_port *fcport = lport->fcport;
struct fcoe_ctlr *fip = lport->disc.priv;
struct fcoe_rport *frport = fcoe_ctlr_rport(rdata);
LIBFCOE_FIP_DBG(fip, "vn_rport_callback %x event %d\n",
rdata->ids.port_id, event);
- mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&fcport->lock);
switch (event) {
case RPORT_EV_READY:
frport->login_count = 0;
@@ -1909,7 +1999,7 @@ static void fcoe_ctlr_vn_rport_callback(struct fc_lport *lport,
default:
break;
}
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
}
static struct fc_rport_operations fcoe_ctlr_vn_rport_ops = {
@@ -1938,11 +2028,11 @@ static void fcoe_ctlr_disc_stop_locked(struct fc_lport *lport)
*/
static void fcoe_ctlr_disc_stop(struct fc_lport *lport)
{
- struct fcoe_ctlr *fip = lport->disc.priv;
+ struct fc_port *fcport = lport->fcport;
- mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&fcport->lock);
fcoe_ctlr_disc_stop_locked(lport);
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
}
/**
@@ -2450,6 +2540,7 @@ static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip)
*/
static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
+ struct fc_port *fcport = fip->lp->fcport;
struct fip_header *fiph;
enum fip_vn2vn_subcode sub;
struct {
@@ -2467,7 +2558,7 @@ static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
goto drop;
}
- mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&fcport->lock);
switch (sub) {
case FIP_SC_VN_PROBE_REQ:
fcoe_ctlr_vn_probe_req(fip, &buf.rdata);
@@ -2489,7 +2580,7 @@ static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
rc = -1;
break;
}
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
drop:
kfree_skb(skb);
return rc;
@@ -2575,11 +2666,13 @@ static void fcoe_ctlr_vn_disc(struct fcoe_ctlr *fip)
*/
static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip)
{
+ struct fc_port *fcport = fip->lp->fcport;
+ struct fc_vport *fcvport = lport_to_fcvport(fip->lp);
unsigned long next_time;
u8 mac[ETH_ALEN];
u32 new_port_id = 0;
- mutex_lock(&fip->ctlr_mutex);
+ mutex_lock(&fcport->lock);
switch (fip->state) {
case FIP_ST_VNMP_START:
fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE1);
@@ -2636,11 +2729,13 @@ static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip)
}
mod_timer(&fip->timer, next_time);
unlock:
- mutex_unlock(&fip->ctlr_mutex);
+ mutex_unlock(&fcport->lock);
/* If port ID is new, notify local port after dropping ctlr_mutex */
- if (new_port_id)
+ if (new_port_id) {
+ fcvport->fcfabric = fc_fabric_add(fcport, fip->fcfabric_f, 0);
fc_lport_set_local_id(fip->lp, new_port_id);
+ }
}
/**
@@ -2653,12 +2748,10 @@ unlock:
* Returns : 0 for success
*/
int fcoe_libfc_config(struct fc_lport *lport, struct fcoe_ctlr *fip,
- const struct libfc_function_template *tt, int init_fcp)
+ const struct libfc_function_template *tt)
{
/* Set the function pointers set by the LLDD */
memcpy(&lport->tt, tt, sizeof(*tt));
- if (init_fcp && fc_fcp_init(lport))
- return -ENOMEM;
fc_exch_init(lport);
fc_elsct_init(lport);
fc_lport_init(lport);
diff --git a/drivers/scsi/fcoe/libfcoe.h b/drivers/scsi/fcoe/libfcoe.h
index 6af5fc3..b447955 100644
--- a/drivers/scsi/fcoe/libfcoe.h
+++ b/drivers/scsi/fcoe/libfcoe.h
@@ -20,8 +20,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%u: fip: " fmt, \
+ fc_port_id((fip)->lp->fcport), ##args);)
#define LIBFCOE_TRANSPORT_DBG(fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_TRANSPORT_LOGGING, \
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index 911b273..4310426 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -382,6 +382,7 @@ err:
*/
static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
{
+ struct fc_vport *fcvport;
struct fc_lport *lport;
struct fc_gpn_ft_resp *np;
char *bp;
@@ -392,6 +393,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
struct fc_rport_priv *rdata;
lport = fc_disc_lport(disc);
+ fcvport = lport_to_fcvport(lport);
disc->seq_count++;
/*
@@ -436,7 +438,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 != fc_vport_port_name(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 28231ba..bb2d31c 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1826,8 +1826,7 @@ static void fc_exch_els_rec(struct fc_frame *rfp)
rxid = ntohs(rp->rec_rx_id);
oxid = ntohs(rp->rec_ox_id);
- ep = fc_exch_lookup(lport,
- sid == fc_host_port_id(lport->host) ? oxid : rxid);
+ ep = fc_exch_lookup(lport, sid == lport->port_id ? oxid : rxid);
explan = ELS_EXPL_OXID_RXID;
if (!ep)
goto reject;
@@ -1994,13 +1993,11 @@ err:
*/
static void fc_exch_rrq(struct fc_exch *ep)
{
- struct fc_lport *lport;
+ struct fc_lport *lport = ep->lp;
struct fc_els_rrq *rrq;
struct fc_frame *fp;
u32 did;
- lport = ep->lp;
-
fp = fc_frame_alloc(lport, sizeof(*rrq));
if (!fp)
goto retry;
@@ -2060,8 +2057,8 @@ static void fc_exch_els_rrq(struct fc_frame *fp)
* lookup subject exchange.
*/
sid = ntoh24(rp->rrq_s_id); /* subject source */
- xid = fc_host_port_id(lport->host) == sid ?
- ntohs(rp->rrq_ox_id) : ntohs(rp->rrq_rx_id);
+ xid = lport->port_id == sid ?
+ ntohs(rp->rrq_ox_id) : ntohs(rp->rrq_rx_id);
ep = fc_exch_lookup(lport, xid);
explan = ELS_EXPL_OXID_RXID;
if (!ep)
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index b1b03af..fc9c97f 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -69,26 +69,6 @@ static 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
@@ -354,13 +334,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);
}
@@ -388,12 +368,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);
}
@@ -408,6 +388,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;
@@ -416,9 +397,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;
}
@@ -1016,7 +998,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;
@@ -1027,7 +1009,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);
@@ -1036,14 +1018,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);
}
/**
@@ -1796,8 +1778,12 @@ static inline int fc_fcp_lport_queue_ready(struct fc_lport *lport)
*/
int fc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc_cmd)
{
- struct fc_lport *lport = shost_priv(shost);
- struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
+ struct scsi_target *starget = scsi_target(sc_cmd->device);
+ struct fcp_targ *fcptarg = starget_to_fcptarg(starget);
+ struct fc_rport *rport = fcptarg->rport;
+ struct fcp_init *fcpinit = shost_priv(sc_cmd->device->host);
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
+ struct fc_lport *lport = si->lport;
struct fc_fcp_pkt *fsp;
struct fc_rport_libfc_priv *rpriv;
int rval;
@@ -1811,7 +1797,9 @@ int fc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc_cmd)
return 0;
}
- if (!*(struct fc_remote_port **)rport->dd_data) {
+ rpriv = rport->dd_data;
+
+ if (!rpriv->local_port) {
/*
* rport is transitioning from blocked/deleted to
* online
@@ -1821,8 +1809,6 @@ int fc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc_cmd)
goto out;
}
- rpriv = rport->dd_data;
-
if (!fc_fcp_lport_queue_ready(lport)) {
if (lport->qfull)
fc_fcp_can_queue_ramp_down(lport);
@@ -2028,29 +2014,28 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
*/
int fc_eh_abort(struct scsi_cmnd *sc_cmd)
{
+ struct fcp_init *fcpinit = shost_priv(sc_cmd->device->host);
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
+ struct fc_lport *lport = si->lport;
struct fc_fcp_pkt *fsp;
- struct fc_lport *lport;
- struct fc_fcp_internal *si;
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;
- si = fc_get_scsi_internal(lport);
- spin_lock_irqsave(&si->scsi_queue_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(&si->scsi_queue_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(&si->scsi_queue_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 */
@@ -2076,9 +2061,13 @@ EXPORT_SYMBOL(fc_eh_abort);
*/
int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
{
- struct fc_lport *lport;
+ struct fcp_init *fcpinit = shost_priv(sc_cmd->device->host);
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
+ struct fc_lport *lport = si->lport;
+ struct scsi_target *starget = scsi_target(sc_cmd->device);
+ struct fcp_targ *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;
@@ -2086,8 +2075,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;
@@ -2125,7 +2112,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 fcp_init *fcpinit = shost_priv(shost);
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
+ struct fc_lport *lport = si->lport;
unsigned long wait_tmo;
FC_SCSI_DBG(lport, "Resetting host\n");
@@ -2158,7 +2147,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 fcp_targ *fcptarg = starget_to_fcptarg(scsi_target(sdev));
+ struct fc_rport *rport = fcptarg->rport;
if (!rport || fc_remote_port_chkready(rport))
return -ENXIO;
@@ -2231,8 +2221,6 @@ void fc_fcp_destroy(struct fc_lport *lport)
"port (%6.6x)\n", lport->port_id);
mempool_destroy(si->scsi_pkt_pool);
- kfree(si);
- lport->scsi_priv = NULL;
}
EXPORT_SYMBOL(fc_fcp_destroy);
@@ -2262,10 +2250,14 @@ void fc_destroy_fcp(void)
* 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 fcp_init *fcpinit)
{
- int rc;
- struct fc_fcp_internal *si;
+ struct fc_vport *fcvport = fcpinit_to_fcvport(fcpinit);
+ struct fc_lport *lport = fc_vport_priv(fcvport);
+ struct Scsi_Host *shost = fcpinit_to_shost(fcpinit);
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
+
+ int rc = 0;
if (!lport->tt.fcp_cmd_send)
lport->tt.fcp_cmd_send = fc_fcp_cmd_send;
@@ -2276,23 +2268,17 @@ 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->scsi_priv = si;
- si->max_can_queue = lport->host->can_queue;
+ lport->fcpinit = fcpinit;
+ si->host = shost;
+ 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 fedc819..e65d893 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, \
+ fc_port_id((lport)->fcport), \
(lport)->port_id, ##args))
-#define FC_DISC_DBG(disc, fmt, args...) \
- FC_CHECK_LOGGING(FC_DISC_LOGGING, \
- printk(KERN_INFO "host%u: disc: " fmt, \
- fc_disc_lport(disc)->host->host_no, \
+#define FC_DISC_DBG(disc, fmt, args...) \
+ FC_CHECK_LOGGING(FC_DISC_LOGGING, \
+ printk(KERN_INFO "fcport%u: disc: " fmt, \
+ fc_port_id(fc_disc_lport(disc)->fcport), \
##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, \
+ fc_port_id((lport)->fcport), \
(port_id), ##args))
#define FC_RPORT_DBG(rdata, fmt, args...) \
@@ -70,28 +70,28 @@ extern unsigned int fc_debug_logging;
if ((pkt)->seq_ptr) { \
struct fc_exch *_ep = NULL; \
_ep = fc_seq_exch((pkt)->seq_ptr); \
- printk(KERN_INFO "host%u: fcp: %6.6x: " \
- "xid %04x-%04x: " fmt, \
- (pkt)->lp->host->host_no, \
- (pkt)->rport->port_id, \
- (_ep)->oxid, (_ep)->rxid, ##args); \
+ printk(KERN_INFO "fcport%u: fcp: %6.6x: " \
+ "xid %04x-%04x: " fmt, \
+ fc_port_id((pkt)->lp->fcport), \
+ (pkt)->rport->port_id, \
+ (_ep)->oxid, (_ep)->rxid, ##args); \
} else { \
- printk(KERN_INFO "host%u: fcp: %6.6x: " fmt, \
- (pkt)->lp->host->host_no, \
- (pkt)->rport->port_id, ##args); \
+ printk(KERN_INFO "fcport%u: fcp: %6.6x: " fmt, \
+ fc_port_id((pkt)->lp->fcport), \
+ (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, \
+ fc_port_id((exch)->lp->fcport), \
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, \
+ fc_port_id((lport)->fcport), ##args))
/*
* FC-4 Providers.
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 8c08b21..d3f2b0c 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -140,7 +140,7 @@ static const char *fc_lport_state_names[] = {
* @offset: The offset into the response data
*/
struct fc_bsg_info {
- struct fc_bsg_job *job;
+ struct fcp_bsg_job *job;
struct fc_lport *lport;
u16 rsp_code;
struct scatterlist *sg;
@@ -247,52 +247,41 @@ 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_fcvport_port_state(struct fc_vport *fcvport)
{
- struct fc_lport *lport = shost_priv(shost);
+ struct fc_lport *lport = fc_vport_priv(fcvport);
mutex_lock(&lport->lp_mutex);
if (!lport->link_up)
- fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+ fc_vport_port_state(fcvport) = FC_PORTSTATE_LINKDOWN;
else
switch (lport->state) {
case LPORT_ST_READY:
- fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ fc_vport_port_state(fcvport) = FC_PORTSTATE_ONLINE;
break;
default:
- fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+ fc_vport_port_state(fcvport) = 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_fcvport_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 fcp_init_statistics *fc_get_fcpinit_stats(struct fcp_init *fcpinit)
{
- struct fc_host_statistics *fcoe_stats;
- struct fc_lport *lport = shost_priv(shost);
+ struct fcp_init_statistics *fcoe_stats;
+ struct fc_lport *lport = fcpinit_to_lport(fcpinit);
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
struct timespec v0, v1;
unsigned int cpu;
u64 fcp_in_bytes = 0;
u64 fcp_out_bytes = 0;
- 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 fcp_init_statistics));
jiffies_to_timespec(jiffies, &v0);
jiffies_to_timespec(lport->boot_time, &v1);
@@ -326,7 +315,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
@@ -338,13 +327,14 @@ static void fc_lport_flogi_fill(struct fc_lport *lport,
struct fc_els_flogi *flogi,
unsigned int op)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_els_csp *sp;
struct fc_els_cssp *cp;
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(fc_vport_port_name(fcvport), &flogi->fl_wwpn);
+ put_unaligned_be64(fc_vport_node_name(fcvport), &flogi->fl_wwnn);
sp = &flogi->fl_csp;
sp->sp_hi_ver = 0x20;
sp->sp_lo_ver = 0x20;
@@ -441,6 +431,7 @@ static void fc_lport_recv_echo_req(struct fc_lport *lport,
static void fc_lport_recv_rnid_req(struct fc_lport *lport,
struct fc_frame *in_fp)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_frame *fp;
struct fc_els_rnid *req;
struct {
@@ -475,8 +466,8 @@ static void fc_lport_recv_rnid_req(struct fc_lport *lport,
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(fc_vport_port_name(fcvport));
+ rp->cid.rnid_wwnn = htonll(fc_vport_node_name(fcvport));
if (fmt == ELS_RNIDF_GEN) {
rp->rnid.rnid_sid_len = sizeof(rp->gen);
memcpy(&rp->gen, &lport->rnid_gen,
@@ -550,8 +541,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%u: libfc: Link up on port (%6x)\n",
+ fc_port_id(lport->fcport), lport->port_id);
mutex_lock(&lport->lp_mutex);
__fc_linkup(lport);
@@ -570,7 +561,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);
}
}
@@ -580,8 +572,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%u: libfc: Link down on port (%6x)\n",
+ fc_port_id(lport->fcport), lport->port_id);
mutex_lock(&lport->lp_mutex);
__fc_linkdown(lport);
@@ -630,7 +622,9 @@ 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);
+ if (lport->tt.fcp_abort_io)
+ lport->tt.fcp_abort_io(lport);
+
lport->tt.disc_stop_final(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
fc_fc4_del_lport(lport);
@@ -682,9 +676,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%u: libfc: "
+ "Discovery failed for port (%6x)\n",
+ fc_port_id(lport->fcport), lport->port_id);
mutex_lock(&lport->lp_mutex);
fc_lport_enter_reset(lport);
mutex_unlock(&lport->lp_mutex);
@@ -704,13 +698,15 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
*/
static void fc_lport_enter_ready(struct fc_lport *lport)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
FC_LPORT_DBG(lport, "Entered READY from state %s\n",
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_READY);
- if (lport->vport)
- fc_vport_set_state(lport->vport, FC_VPORT_ACTIVE);
- fc_vports_linkchange(lport);
+ if (!fc_vport_is_nport(fcvport))
+ fc_vport_set_state(fcvport, FC_VPORT_ACTIVE);
+ else
+ fc_vports_linkchange(lport);
if (!lport->ptp_rdata)
lport->tt.disc_start(fc_lport_disc_callback, lport);
@@ -728,14 +724,14 @@ static void fc_lport_enter_ready(struct fc_lport *lport)
static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id,
struct fc_frame *fp)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
+
if (port_id)
- printk(KERN_INFO "host%d: Assigned Port ID %6.6x\n",
- lport->host->host_no, port_id);
+ printk(KERN_INFO "fcport%u: Assigned Port ID %6x\n",
+ fc_port_id(lport->fcport), port_id);
lport->port_id = port_id;
-
- /* Update the fc_host */
- fc_host_port_id(lport->host) = port_id;
+ fc_vport_port_id(fcvport) = port_id;
if (lport->tt.lport_set_port_id)
lport->tt.lport_set_port_id(lport, port_id, fp);
@@ -752,6 +748,9 @@ static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id,
*/
void fc_lport_set_local_id(struct fc_lport *lport, u32 port_id)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
+ struct fc_port *fcport = lport->fcport;
+
mutex_lock(&lport->lp_mutex);
fc_lport_set_port_id(lport, port_id, NULL);
@@ -759,8 +758,13 @@ void fc_lport_set_local_id(struct fc_lport *lport, u32 port_id)
switch (lport->state) {
case LPORT_ST_RESET:
case LPORT_ST_FLOGI:
- if (port_id)
+ if (port_id) {
+ mutex_lock(&fcport->lock);
+ fc_vport_add(lport->fcfabric, fcvport);
+ mutex_unlock(&fcport->lock);
+
fc_lport_enter_ready(lport);
+ }
break;
default:
break;
@@ -784,6 +788,7 @@ EXPORT_SYMBOL(fc_lport_set_local_id);
static void fc_lport_recv_flogi_req(struct fc_lport *lport,
struct fc_frame *rx_fp)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_frame *fp;
struct fc_frame_header *fh;
struct fc_els_flogi *flp;
@@ -800,10 +805,10 @@ static void fc_lport_recv_flogi_req(struct fc_lport *lport,
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 == fc_vport_port_name(fcvport)) {
+ printk(KERN_WARNING "fcport%u: libfc: Received FLOGI from port "
+ "with same WWPN %llx\n",
+ fc_port_id(lport->fcport), remote_wwpn);
goto out;
}
FC_LPORT_DBG(lport, "FLOGI from port WWPN %16.16llx\n", remote_wwpn);
@@ -814,7 +819,7 @@ static void fc_lport_recv_flogi_req(struct fc_lport *lport,
* 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 < fc_vport_port_name(fcvport)) {
local_fid = FC_LOCAL_PTP_FID_HI;
if (!remote_fid || remote_fid == local_fid)
remote_fid = FC_LOCAL_PTP_FID_LO;
@@ -997,7 +1002,6 @@ 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->port_id && (!lport->point_to_multipoint || !lport->link_up))
fc_lport_set_port_id(lport, 0, NULL);
@@ -1012,21 +1016,26 @@ static void fc_lport_reset_locked(struct fc_lport *lport)
*/
static void fc_lport_enter_reset(struct fc_lport *lport)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
+
FC_LPORT_DBG(lport, "Entered RESET state from %s state\n",
fc_lport_state(lport));
if (lport->state == LPORT_ST_DISABLED || lport->state == LPORT_ST_LOGO)
return;
- if (lport->vport) {
+ if (!fc_vport_is_nport(fcvport)) {
if (lport->link_up)
- fc_vport_set_state(lport->vport, FC_VPORT_INITIALIZING);
+ fc_vport_set_state(fcvport,
+ FC_VPORT_INITIALIZING);
else
- fc_vport_set_state(lport->vport, FC_VPORT_LINKDOWN);
+ fc_vport_set_state(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);
}
@@ -1242,6 +1251,7 @@ static void fc_lport_enter_scr(struct fc_lport *lport)
*/
static void fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_frame *fp;
enum fc_ns_req cmd;
int size = sizeof(struct fc_ct_hdr);
@@ -1259,7 +1269,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(fc_vport_symbolic_name(fcvport), 255);
/* if there is no symbolic name, skip to RFT_ID */
if (!len)
return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
@@ -1267,7 +1277,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(fc_vport_symbolic_name(fcvport), 255);
/* if there is no symbolic name, skip to RFT_ID */
if (!len)
return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
@@ -1436,6 +1446,8 @@ EXPORT_SYMBOL(fc_lport_logo_resp);
*/
static void fc_lport_enter_logo(struct fc_lport *lport)
{
+ struct fc_port *fcport = lport->fcport;
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_frame *fp;
struct fc_els_logo *logo;
@@ -1455,6 +1467,10 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
fc_lport_logo_resp, lport,
2 * lport->r_a_tov))
fc_lport_error(lport, NULL);
+
+ mutex_lock(&fcport->lock);
+ fc_vport_del(fcvport);
+ mutex_unlock(&fcport->lock);
}
/**
@@ -1471,6 +1487,8 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
void *lp_arg)
{
struct fc_lport *lport = lp_arg;
+ struct fc_port *fcport = lport->fcport;
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_els_flogi *flp;
u32 did;
u16 csp_flags;
@@ -1478,8 +1496,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;
@@ -1520,10 +1536,12 @@ 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: "
+
+ printk(KERN_INFO "fcvport%d: libfc: "
"Port (%6.6x) entered "
"point-to-point mode\n",
- lport->host->host_no, did);
+ fcvport->id, did);
+
fc_lport_ptp_setup(lport, fc_frame_sid(fp),
get_unaligned_be64(
&flp->fl_wwpn),
@@ -1532,9 +1550,13 @@ 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);
+
+ mutex_lock(&fcport->lock);
+ fc_vport_add(lport->fcfabric, fcvport);
+ mutex_unlock(&fcport->lock);
+
fc_lport_enter_dns(lport);
}
}
@@ -1559,6 +1581,7 @@ EXPORT_SYMBOL(fc_lport_flogi_resp);
*/
void fc_lport_enter_flogi(struct fc_lport *lport)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_frame *fp;
FC_LPORT_DBG(lport, "Entered FLOGI state from %s state\n",
@@ -1577,10 +1600,11 @@ 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_vport_is_nport(fcvport) ?
+ ELS_FLOGI : ELS_FDISC,
fc_lport_flogi_resp, lport,
- lport->vport ? 2 * lport->r_a_tov :
- lport->e_d_tov))
+ fc_vport_is_nport(fcvport) ?
+ lport->e_d_tov : 2 * lport->r_a_tov))
fc_lport_error(lport, NULL);
}
@@ -1604,6 +1628,26 @@ int fc_lport_config(struct fc_lport *lport)
}
EXPORT_SYMBOL(fc_lport_config);
+void fc_lport_port_config(struct fc_port *fcport)
+{
+ fc_port_supported_fc4s(fcport)[2] = 1;
+ fc_port_supported_fc4s(fcport)[7] = 1;
+ fc_port_active_fc4s(fcport)[2] = 1;
+ fc_port_active_fc4s(fcport)[7] = 1;
+
+ fc_port_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
@@ -1616,26 +1660,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;
fc_fc4_add_lport(lport);
return 0;
@@ -1652,7 +1676,7 @@ static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp,
void *info_arg)
{
struct fc_bsg_info *info = info_arg;
- struct fc_bsg_job *job = info->job;
+ struct fcp_bsg_job *job = info->job;
struct fc_lport *lport = info->lport;
struct fc_frame_header *fh;
size_t len;
@@ -1714,7 +1738,7 @@ static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp,
* Locking Note: The lport lock is expected to be held before calling
* this routine.
*/
-static int fc_lport_els_request(struct fc_bsg_job *job,
+static int fc_lport_els_request(struct fcp_bsg_job *job,
struct fc_lport *lport,
u32 did, u32 tov)
{
@@ -1775,7 +1799,7 @@ static int fc_lport_els_request(struct fc_bsg_job *job,
* Locking Note: The lport lock is expected to be held before calling
* this routine.
*/
-static int fc_lport_ct_request(struct fc_bsg_job *job,
+static int fc_lport_ct_request(struct fcp_bsg_job *job,
struct fc_lport *lport, u32 did, u32 tov)
{
struct fc_bsg_info *info;
@@ -1831,7 +1855,7 @@ static int fc_lport_ct_request(struct fc_bsg_job *job,
* FC Passthrough requests
* @job: The BSG passthrough job
*/
-int fc_lport_bsg_request(struct fc_bsg_job *job)
+int fc_lport_bsg_request(struct fcp_bsg_job *job)
{
struct request *rsp = job->req->next_rq;
struct Scsi_Host *shost = job->shost;
diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c
index f33b897..24f81dd 100644
--- a/drivers/scsi/libfc/fc_npiv.c
+++ b/drivers/scsi/libfc/fc_npiv.c
@@ -24,31 +24,21 @@
#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_vport *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)
- return vn_port;
+ if (fc_exch_mgr_list_clone(n_port, vn_port))
+ return -ENOMEM;
- vn_port->vport = vport;
- vport->dd_data = vn_port;
+ vn_port->fcport = n_port->fcport;
- mutex_lock(&n_port->lp_mutex);
- list_add_tail(&vn_port->list, &n_port->vports);
- mutex_unlock(&n_port->lp_mutex);
-
- return vn_port;
+ 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
@@ -59,8 +49,9 @@ EXPORT_SYMBOL(libfc_vport_create);
*/
struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id)
{
- struct fc_lport *lport = NULL;
- struct fc_lport *vn_port;
+ struct fc_vport *fcvport = lport_to_fcvport(n_port);
+ struct fc_fabric *fcfabric = fcvport_to_fcfabric(fcvport);
+ struct fc_vport *fcvnport;
if (n_port->port_id == port_id)
return n_port;
@@ -68,16 +59,19 @@ struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id)
if (port_id == FC_FID_FLOGI)
return n_port; /* for point-to-point */
- mutex_lock(&n_port->lp_mutex);
- list_for_each_entry(vn_port, &n_port->vports, list) {
- if (vn_port->port_id == port_id) {
- lport = vn_port;
- break;
- }
- }
- mutex_unlock(&n_port->lp_mutex);
+ /*
+ * If there isn't a fabric then this is the N_Port. We
+ * haven't discovered a switch, haven't logged in and
+ * don't have any NPIV ports.
+ */
+ if (!fcfabric)
+ return NULL;
- return lport;
+ fcvnport = fc_vport_lookup(fcfabric, port_id);
+ if (fcvnport)
+ return fc_vport_priv(fcvnport);
+
+ return NULL;
}
EXPORT_SYMBOL(fc_vport_id_lookup);
@@ -102,7 +96,7 @@ 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_vport *vport = lport_to_fcvport(vn_port);
if (vn_port->state == LPORT_ST_DISABLED)
return;
@@ -127,9 +121,9 @@ 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_vport *fcvport = lport_to_fcvport(vn_port);
+ struct fc_vport *fcnport = fcvport->fcnport;
+ struct fc_lport *n_port = fc_vport_priv(fcnport);
mutex_lock(&n_port->lp_mutex);
mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT);
@@ -147,12 +141,37 @@ EXPORT_SYMBOL(fc_vport_setlink);
*/
void fc_vports_linkchange(struct fc_lport *n_port)
{
+ struct fc_vport *fcvport = lport_to_fcvport(n_port);
+ struct fc_fabric *fcfabric = fcvport->fcfabric;
+ struct fc_port *fcport;
+ struct fc_vport *vport;
struct fc_lport *vn_port;
- list_for_each_entry(vn_port, &n_port->vports, list) {
+ /*
+ * If this isn't the N_Port then there are no
+ * NPIV portst to update.
+ */
+ if (!fc_vport_is_nport(fcvport))
+ return;
+
+ /*
+ * If there isn't a fabric then this is the N_Port. We
+ * haven't discovered a switch, haven't logged in and
+ * don't have any NPIV ports whose link states need
+ * to be updated.
+ */
+ if (!fcfabric)
+ return;
+
+ fcport = fcfabric_to_fcport(fcfabric);
+
+ mutex_lock(&fcport->lock);
+ list_for_each_entry(vport, &fcfabric->vports, peers) {
+ vn_port = fc_vport_priv(vport);
mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT);
__fc_vport_setlink(n_port, vn_port);
mutex_unlock(&vn_port->lp_mutex);
}
+ mutex_unlock(&fcport->lock);
}
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 49e1ccc..c09fc9a 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -256,6 +256,7 @@ static void fc_rport_work(struct work_struct *work)
struct fc_rport_libfc_priv *rpriv;
enum fc_rport_event event;
struct fc_lport *lport = rdata->local_port;
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_rport_operations *rport_ops;
struct fc_rport_identifiers ids;
struct fc_rport *rport;
@@ -278,7 +279,7 @@ 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_rport_add(fcvport, &ids);
if (!rport) {
FC_RPORT_DBG(rdata, "Failed to add the rport\n");
lport->tt.rport_logoff(rdata);
@@ -293,11 +294,11 @@ static void fc_rport_work(struct work_struct *work)
rport->supported_classes = rdata->supported_classes;
rpriv = rport->dd_data;
- rpriv->local_port = lport;
rpriv->rp_state = rdata->rp_state;
rpriv->flags = rdata->flags;
rpriv->e_d_tov = rdata->e_d_tov;
rpriv->r_a_tov = rdata->r_a_tov;
+ rpriv->local_port = lport;
mutex_unlock(&rdata->rp_mutex);
if (rport_ops && rport_ops->event_callback) {
@@ -348,7 +349,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_rport_del(rport);
}
mutex_lock(&lport->disc.disc_mutex);
@@ -1102,7 +1103,7 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
}
fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rdata->ids.port_id,
- fc_host_port_id(lport->host), FC_TYPE_ELS,
+ lport->port_id, FC_TYPE_ELS,
FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
if (!lport->tt.exch_seq_send(lport, fp, fc_rport_prli_resp,
@@ -1387,12 +1388,13 @@ 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_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 fcp_init_statistics *hst;
FC_RPORT_DBG(rdata, "Received RLS request while in state %s\n",
fc_rport_state(rdata));
@@ -1419,8 +1421,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);
@@ -1564,6 +1566,7 @@ void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
static void fc_rport_recv_plogi_req(struct fc_lport *lport,
struct fc_frame *rx_fp)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_disc *disc;
struct fc_rport_priv *rdata;
struct fc_frame *fp = rx_fp;
@@ -1619,7 +1622,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 < fc_vport_port_name(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 be418d8..5fb5bdc 100644
--- a/include/scsi/fc_encode.h
+++ b/include/scsi/fc_encode.h
@@ -82,13 +82,16 @@ static inline void fc_fill_fc_hdr(struct fc_frame *fp, enum fc_rctl r_ctl,
*/
static inline void fc_adisc_fill(struct fc_lport *lport, struct fc_frame *fp)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_els_adisc *adisc;
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(fc_vport_port_name(fcvport),
+ &adisc->adisc_wwpn);
+ put_unaligned_be64(fc_vport_node_name(fcvport),
+ &adisc->adisc_wwnn);
hton24(adisc->adisc_port_id, lport->port_id);
}
@@ -126,6 +129,7 @@ static inline int fc_ct_fill(struct fc_lport *lport,
unsigned int op, enum fc_rctl *r_ctl,
enum fc_fh_type *fh_type)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_ct_req *ct;
size_t len;
@@ -159,24 +163,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(fc_vport_node_name(fcvport),
+ &ct->payload.rn.fr_wwn);
break;
case FC_NS_RSPN_ID:
- len = strnlen(fc_host_symbolic_name(lport->host), 255);
+ len = strnlen(fc_vport_symbolic_name(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);
+ fc_vport_symbolic_name(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(fc_vport_symbolic_name(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(fc_vport_node_name(fcvport),
+ &ct->payload.snn.fr_wwn);
strncpy(ct->payload.snn.fr_name,
- fc_host_symbolic_name(lport->host), len);
+ fc_vport_symbolic_name(fcvport), len);
ct->payload.snn.fr_name_len = len;
break;
@@ -194,6 +200,7 @@ static inline int fc_ct_fill(struct fc_lport *lport,
static inline void fc_plogi_fill(struct fc_lport *lport, struct fc_frame *fp,
unsigned int op)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_els_flogi *plogi;
struct fc_els_csp *csp;
struct fc_els_cssp *cp;
@@ -201,8 +208,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(fc_vport_port_name(fcvport), &plogi->fl_wwpn);
+ put_unaligned_be64(fc_vport_node_name(fcvport), &plogi->fl_wwnn);
csp = &plogi->fl_csp;
csp->sp_hi_ver = 0x20;
@@ -226,6 +233,7 @@ static inline void fc_plogi_fill(struct fc_lport *lport, struct fc_frame *fp,
*/
static inline void fc_flogi_fill(struct fc_lport *lport, struct fc_frame *fp)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_els_csp *sp;
struct fc_els_cssp *cp;
struct fc_els_flogi *flogi;
@@ -233,8 +241,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(fc_vport_port_name(fcvport), &flogi->fl_wwpn);
+ put_unaligned_be64(fc_vport_node_name(fcvport), &flogi->fl_wwnn);
sp = &flogi->fl_csp;
sp->sp_hi_ver = 0x20;
sp->sp_lo_ver = 0x20;
@@ -251,6 +259,7 @@ static inline void fc_flogi_fill(struct fc_lport *lport, struct fc_frame *fp)
*/
static inline void fc_fdisc_fill(struct fc_lport *lport, struct fc_frame *fp)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_els_csp *sp;
struct fc_els_cssp *cp;
struct fc_els_flogi *fdisc;
@@ -258,8 +267,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(fc_vport_port_name(fcvport), &fdisc->fl_wwpn);
+ put_unaligned_be64(fc_vport_node_name(fcvport), &fdisc->fl_wwnn);
sp = &fdisc->fl_csp;
sp->sp_hi_ver = 0x20;
sp->sp_lo_ver = 0x20;
@@ -274,13 +283,14 @@ static inline void fc_fdisc_fill(struct fc_lport *lport, struct fc_frame *fp)
*/
static inline void fc_logo_fill(struct fc_lport *lport, struct fc_frame *fp)
{
+ struct fc_vport *fcvport = lport_to_fcvport(lport);
struct fc_els_logo *logo;
logo = fc_frame_payload_get(fp, sizeof(*logo));
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(fc_vport_port_name(fcvport));
}
/**
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 24193c1..1086568 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>
@@ -37,6 +38,9 @@
#define FC_FC4_PROV_SIZE (FC_TYPE_FCP + 1) /* size of tables */
+#define fcpinit_to_lport(x) \
+ fc_vport_priv((struct fc_vport *)(fcpinit_to_fcvport(x)))
+
/*
* libfc error codes
*/
@@ -133,6 +137,7 @@ enum fc_rport_event {
};
struct fc_rport_priv;
+struct fc_fcp_internal;
/**
* struct fc_rport_operations - Operations for a remote port
@@ -800,7 +805,6 @@ enum fc_lport_event {
* @ema_list: Exchange manager anchor list
* @dns_rdata: The directory server remote port
* @ptp_rdata: Point to point remote port
- * @scsi_priv: FCP layer internal data
* @disc: Discovery context
* @vports: Child vports if N_Port
* @vport: Parent vport if VN_Port
@@ -834,22 +838,18 @@ enum fc_lport_event {
* @lso_max: The maximum large offload send size
* @fcts: FC-4 type mask
* @lp_mutex: Mutex to protect the local port
- * @list: Linkage on list of vport peers
* @retry_work: Handle to local port for delayed retry context
* @prov: Pointers available for use by passive FC-4 providers
* @lport_list: Linkage on module-wide list of local ports
*/
struct fc_lport {
/* Associations */
- 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 */
@@ -858,7 +858,6 @@ struct fc_lport {
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;
@@ -866,6 +865,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;
@@ -883,20 +883,56 @@ struct fc_lport {
u8 max_retry_count;
u8 max_rport_retry_count;
u16 rport_priv_size;
- u16 link_speed;
- u16 link_supported_speeds;
u16 lro_xid;
unsigned int lso_max;
struct fc_ns_fts fcts;
+ /* sysfs representation */
+ struct fc_port *fcport;
+ struct fc_fabric *fcfabric;
+ struct fcp_init *fcpinit;
+
/* Miscellaneous */
struct mutex lp_mutex;
- struct list_head list;
struct delayed_work retry_work;
void *prov[FC_FC4_PROV_SIZE];
struct list_head lport_list;
};
+#define lport_to_fcvport(lport) \
+ ((struct fc_vport *)(((struct fc_vport *)(lport)) - 1))
+
+/**
+ * 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 */
+ struct fcp_init_statistics fcpinit_stats;
+};
+
+/*
+ * TODO: This is really horrible, can this be removed?
+ */
+#define fc_get_scsi_internal(x) \
+ ((struct fc_fcp_internal *)(fcp_init_priv( \
+ fcvport_to_fcpinit(lport_to_fcvport(x)))))
+
/**
* struct fc4_prov - FC-4 provider registration
* @prli: Handler for incoming PRLI
@@ -995,30 +1031,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
*****************************/
@@ -1045,9 +1057,10 @@ 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_vport *);
struct fc_lport *fc_vport_id_lookup(struct fc_lport *, u32 port_id);
-int fc_lport_bsg_request(struct fc_bsg_job *);
+int fc_lport_bsg_request(struct fcp_bsg_job *);
void fc_lport_set_local_id(struct fc_lport *, u32 port_id);
void fc_lport_iterate(void (*func)(struct fc_lport *, void *), void *);
@@ -1070,7 +1083,7 @@ static inline struct fc_lport *fc_disc_lport(struct fc_disc *disc)
/*
* FCP LAYER
*****************************/
-int fc_fcp_init(struct fc_lport *);
+int fc_fcp_init(struct fcp_init *);
void fc_fcp_destroy(struct fc_lport *);
/*
@@ -1122,9 +1135,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_fcvport_port_state(struct fc_vport *);
void fc_set_rport_loss_tmo(struct fc_rport *, u32 timeout);
-struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *);
+struct fcp_init_statistics *fc_get_fcpinit_stats(struct fcp_init *);
+
+
+void fc_lport_port_config(struct fc_port *fcport);
#endif /* _LIBFC_H_ */
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index 8c1638b..9dc119f 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -88,7 +88,6 @@ enum fip_state {
* @mode: LLD-selected mode.
* @lp: &fc_lport: libfc local port.
* @sel_fcf: currently selected FCF, or NULL.
- * @fcfs: list of discovered FCFs.
* @fcf_count: number of discovered FCF entries.
* @sol_time: time when a multicast solicitation was last sent.
* @sel_time: time after which to select an FCF.
@@ -113,7 +112,6 @@ enum fip_state {
* @send: LLD-supplied function to handle sending FIP Ethernet frames
* @update_mac: LLD-supplied function to handle changes to MAC addresses.
* @get_src_addr: LLD-supplied function to supply a source MAC address.
- * @ctlr_mutex: lock protecting this structure.
* @ctlr_lock: spinlock covering flogi_req
*
* This structure is used by all FCoE drivers. It contains information
@@ -125,7 +123,6 @@ struct fcoe_ctlr {
enum fip_state mode;
struct fc_lport *lp;
struct fcoe_fcf *sel_fcf;
- struct list_head fcfs;
u16 fcf_count;
unsigned long sol_time;
unsigned long sel_time;
@@ -150,16 +147,19 @@ struct fcoe_ctlr {
u8 dest_addr[ETH_ALEN];
u8 ctl_src_addr[ETH_ALEN];
+ struct fc_fabric_function_template *fcfabric_f;
+
void (*send)(struct fcoe_ctlr *, struct sk_buff *);
void (*update_mac)(struct fc_lport *, u8 *addr);
u8 * (*get_src_addr)(struct fc_lport *);
- struct mutex ctlr_mutex;
+
spinlock_t ctlr_lock;
};
+
+
/**
* struct fcoe_fcf - Fibre-Channel Forwarder
- * @list: list linkage
* @time: system time (jiffies) when an advertisement was last received
* @switch_name: WWN of switch from advertisement
* @fabric_name: WWN of fabric from advertisement
@@ -179,7 +179,6 @@ struct fcoe_ctlr {
* @fcf_mac together form the lookup key.
*/
struct fcoe_fcf {
- struct list_head list;
unsigned long time;
u64 switch_name;
@@ -195,6 +194,9 @@ struct fcoe_fcf {
u8 fd_flags:1;
};
+#define fcf_to_fabric(fcf) \
+ ((struct fc_fabric *)(((struct fc_fabric *)(fcf)) - 1))
+
/**
* struct fcoe_rport - VN2VN remote port
* @time: time of create or last beacon packet received from node
@@ -226,9 +228,10 @@ 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 fcoe_ctlr *,
- const struct libfc_function_template *, int init_fcp);
+ const struct libfc_function_template *);
u32 fcoe_fc_crc(struct fc_frame *fp);
int fcoe_start_io(struct sk_buff *skb);
+int fcoe_fcf_match(struct fc_fabric *, struct fc_fabric *);
/**
* is_fip_mode() - returns true if FIP mode selected.
@@ -238,7 +241,6 @@ static inline bool is_fip_mode(struct fcoe_ctlr *fip)
{
return fip->state == FIP_ST_ENABLED;
}
-
/* helper for FCoE SW HBA drivers, can include subven and subdev if needed. The
* modpost would use pci_device_id table to auto-generate formatted module alias
* into the corresponding .mod.c file, but there may or may not be a pci device
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v2 06/10] fc: Add FCoE attributes to FC sysfs
2011-03-11 21:54 [RFC PATCH v2 00/10] FC Sysfs and FCoE attributes Robert Love
` (4 preceding siblings ...)
2011-03-11 21:54 ` [RFC PATCH v2 05/10] libfc, libfcoe, fcoe: Make use of FC subsystem Robert Love
@ 2011-03-11 21:55 ` Robert Love
2011-03-11 21:55 ` [RFC PATCH v2 07/10] libfcoe, fcoe: Use FCoE attributes Robert Love
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Robert Love @ 2011-03-11 21:55 UTC (permalink / raw)
To: linux-scsi; +Cc: abjoglek, giridhar.malavali, james.smart, bprakash
This patch adds fcoe attributes to the FC subsystem.
fcport: Represents physical port
Attributes: enode_mac_address
vlan_id
vn_port_mac_address
fcfabric: Represents the FC fabric and switch (FCF for FCoE)
Attributes: fc_map
fka_period
mac_address
priority
vfid
fcvport: Represents either an N_Port or VN_Port
Attributes: err_block_count (Link Error Status Block)
fcs_err_count (LESB)
link_failure_count (LESB)
miss_fka_count (LESB)
symb_err_count (LESB)
vlink_failure_count (LESB)
NOTE: Currently I'm using kobj directly to add a fcoe/
subdirectory under the fc devices. I don't think this is
the right thing to do. I would appreciate any advice on
the correct way to add a subdirectory.
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
drivers/fc/Makefile | 5 +-
drivers/fc/fcfabric.c | 9 ++-
drivers/fc/fcfabric_fcoe.c | 123 ++++++++++++++++++++++++++++++++++++++
drivers/fc/fcport.c | 8 ++
drivers/fc/fcport_fcoe.c | 134 +++++++++++++++++++++++++++++++++++++++++
drivers/fc/fcsysfs.h | 10 +++
drivers/fc/fcvport.c | 10 +++
drivers/fc/fcvport_fcoe.c | 118 ++++++++++++++++++++++++++++++++++++
drivers/scsi/fcoe/fcoe.c | 2 -
drivers/scsi/fcoe/fcoe_ctlr.c | 5 +-
drivers/scsi/libfc/fc_lport.c | 4 +
include/fc/fc.h | 103 ++++++++++++++++++++++++++++++--
12 files changed, 518 insertions(+), 13 deletions(-)
create mode 100644 drivers/fc/fcfabric_fcoe.c
create mode 100644 drivers/fc/fcport_fcoe.c
create mode 100644 drivers/fc/fcvport_fcoe.c
diff --git a/drivers/fc/Makefile b/drivers/fc/Makefile
index fa1d60e..5b4bc8f 100644
--- a/drivers/fc/Makefile
+++ b/drivers/fc/Makefile
@@ -4,4 +4,7 @@ fc-objs := fcsysfs.o \
fcport.o \
fcfabric.o \
fcvport.o \
- fcrport.o
+ fcrport.o \
+ fcport_fcoe.o \
+ fcfabric_fcoe.o \
+ fcvport_fcoe.o
diff --git a/drivers/fc/fcfabric.c b/drivers/fc/fcfabric.c
index b275351..ef4f5df 100644
--- a/drivers/fc/fcfabric.c
+++ b/drivers/fc/fcfabric.c
@@ -383,6 +383,9 @@ static void fc_fabric_final_delete(struct work_struct *work)
mutex_unlock(&fcport->lock);
+ if (fcfabric->private_fcfabric_fcoe_kobj)
+ fc_fabric_del_fcoe_attrs(fcfabric);
+
device_del(&fcfabric->dev);
put_device(&fcfabric->dev); /* self-reference */
}
@@ -457,7 +460,8 @@ struct class fcfabric_class = {
*/
struct fc_fabric *fc_fabric_add(struct fc_port *fcport,
struct fc_fabric_function_template *fcn_tmpl,
- struct fc_fabric *new_fcfabric)
+ struct fc_fabric *new_fcfabric,
+ struct fc_fabric_fcoe_attrs *fcoe_attrs)
{
struct fc_fabric *fcfabric;
int error = 0;
@@ -526,6 +530,9 @@ struct fc_fabric *fc_fabric_add(struct fc_port *fcport,
if (error || count != 0)
goto out_del_dev;
+ if (fcoe_attrs)
+ fc_fabric_add_fcoe_attrs(fcfabric, fcoe_attrs);
+
return fcfabric;
out_del_dev:
diff --git a/drivers/fc/fcfabric_fcoe.c b/drivers/fc/fcfabric_fcoe.c
new file mode 100644
index 0000000..c8b85c7
--- /dev/null
+++ b/drivers/fc/fcfabric_fcoe.c
@@ -0,0 +1,123 @@
+/*
+ * 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/genhd.h>
+#include "fcsysfs.h"
+
+/*
+ * Kobject attributes defined for /sys/class/fc_host/hostx/fcoe
+ * and /sys/class/fc_host/hostx/fcoe/statistics objects
+ */
+#define FCOE_KOBJECT_ATTR(_prefix, _name, _mode, _show, _store) \
+ struct kobj_attribute kobject_attr_fcoe_##_prefix##_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
+
+static ssize_t fc_fcoe_mac_addr_show(const struct kobject *kobj, char *buf,
+ unsigned long offset)
+{
+ struct device *dev = kobj_to_dev(kobj->parent);
+ struct fc_fabric *fcfabric = dev_to_fcfabric(dev);
+ ssize_t ret = -ENOENT;
+
+ if (fcfabric->f->show_fcfabric_mac_address)
+ ret = sysfs_format_mac(
+ buf, fcfabric->fcoe_attrs.mac_address, 6);
+
+ return ret;
+}
+
+/* generate a read-only FCoE MAC address host attribute */
+#define fc_fcoe_mac_addr(name) \
+static ssize_t fc_show_fcoe_mac_addr_##name(struct kobject *cd, \
+ struct kobj_attribute *attr, \
+ char *buf) \
+{ \
+ return fc_fcoe_mac_addr_show( \
+ cd, buf, offsetof(struct fc_fabric_fcoe_attrs, name)); \
+} \
+static FCOE_KOBJECT_ATTR(fcfabric, name, S_IRUGO, \
+ fc_show_fcoe_mac_addr_##name, NULL)
+
+fc_fcoe_mac_addr(mac_address);
+
+#define fc_fcoe_rd(name, format_string) \
+static ssize_t fc_show_fcoe_##name(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ char *buf) \
+{ \
+ struct device *dev = kobj_to_dev(kobj->parent); \
+ struct fc_fabric *fcfabric = dev_to_fcfabric(dev); \
+ \
+ if (fcfabric->f->show_fcfabric_##name) \
+ return snprintf(buf, 20, format_string, \
+ fcfabric->fcoe_attrs.name); \
+ return -ENOENT; \
+} \
+static FCOE_KOBJECT_ATTR(fcfabric, name, S_IRUGO, fc_show_fcoe_##name, NULL)
+
+fc_fcoe_rd(fc_map, "0x%x\n");
+fc_fcoe_rd(vfid, "0x%x\n");
+fc_fcoe_rd(priority, "%u\n");
+fc_fcoe_rd(fka_period, "%u\n");
+
+#define FCOE_SETUP_COND_ATTR_RD(_var, field) \
+if (_var->f->show_##_var##_##field) { \
+ _var->fcfabric_fcoe_attrs[count] = \
+ &kobject_attr_fcoe_##_var##_##field.attr; \
+ count++; \
+}
+
+void fc_fabric_del_fcoe_attrs(struct fc_fabric *fcfabric)
+{
+ kobject_put(fcfabric->private_fcfabric_fcoe_kobj);
+ fcfabric->private_fcfabric_fcoe_kobj = NULL;
+}
+
+int fc_fabric_add_fcoe_attrs(struct fc_fabric *fcfabric,
+ struct fc_fabric_fcoe_attrs *fcoe_attrs)
+{
+ int count = 0;
+ int error;
+
+ memcpy(&fcfabric->fcoe_attrs, fcoe_attrs,
+ sizeof(struct fc_fabric_fcoe_attrs));
+
+ FCOE_SETUP_COND_ATTR_RD(fcfabric, fc_map);
+ FCOE_SETUP_COND_ATTR_RD(fcfabric, vfid);
+ FCOE_SETUP_COND_ATTR_RD(fcfabric, mac_address);
+ FCOE_SETUP_COND_ATTR_RD(fcfabric, priority);
+ FCOE_SETUP_COND_ATTR_RD(fcfabric, fka_period);
+
+ fcfabric->fcoe_attrs_group.attrs = fcfabric->fcfabric_fcoe_attrs;
+
+ /* Create a fcoe sub-directory under /sys/class/fc_host/hostX/ */
+ fcfabric->private_fcfabric_fcoe_kobj =
+ kobject_create_and_add("fcoe", &fcfabric->dev.kobj);
+ if (fcfabric->private_fcfabric_fcoe_kobj) {
+ /* Create the files associated with this kobject */
+ error = sysfs_create_group(fcfabric->private_fcfabric_fcoe_kobj,
+ &fcfabric->fcoe_attrs_group);
+ if (error) {
+ kobject_put(fcfabric->private_fcfabric_fcoe_kobj);
+ goto error;
+ }
+ }
+
+error:
+ return 0;
+}
diff --git a/drivers/fc/fcport.c b/drivers/fc/fcport.c
index e9dc2b7..299f220 100644
--- a/drivers/fc/fcport.c
+++ b/drivers/fc/fcport.c
@@ -225,6 +225,9 @@ void fc_port_del(struct fc_port *fcport)
destroy_workqueue(fcport->work_q);
fcport->work_q = NULL;
+ if (fcport->private_fcport_fcoe_kobj)
+ fc_port_del_fcoe_attrs(fcport);
+
device_del(&fcport->dev);
put_device(&fcport->dev); /* self-reference */
}
@@ -242,7 +245,7 @@ EXPORT_SYMBOL(fc_port_del);
*/
struct fc_port *fc_port_add(struct device *pdev,
struct fc_port_function_template *fcn_tmpl,
- void *fc4_f)
+ void *fc4_f, struct fc_port_fcoe_attrs *fcoe_attrs)
{
struct fc_port *fcport;
int count = 0;
@@ -316,6 +319,9 @@ struct fc_port *fc_port_add(struct device *pdev,
if (error || count != 0)
goto out_del_dev;
+ if (fcoe_attrs)
+ fc_port_add_fcoe_attrs(fcport, fcoe_attrs);
+
return fcport;
out_del_dev:
diff --git a/drivers/fc/fcport_fcoe.c b/drivers/fc/fcport_fcoe.c
new file mode 100644
index 0000000..94609dc
--- /dev/null
+++ b/drivers/fc/fcport_fcoe.c
@@ -0,0 +1,134 @@
+/*
+ * 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/genhd.h>
+#include "fcsysfs.h"
+
+/*
+ * Kobject attributes defined for /sys/class/fc_host/hostx/fcoe
+ * and /sys/class/fc_host/hostx/fcoe/statistics objects
+ */
+#define FCOE_KOBJECT_ATTR(_prefix, _name, _mode, _show, _store) \
+ struct kobj_attribute kobject_attr_fcoe_##_prefix##_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
+#define fc_fcoe_rd(name, format_string) \
+static ssize_t fc_show_fcoe_##name(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ char *buf) \
+{ \
+ struct device *dev = kobj_to_dev(kobj->parent); \
+ struct fc_port *fcport = dev_to_fcport(dev); \
+ \
+ if (fcport->f->show_fcport_##name) \
+ return snprintf(buf, 20, format_string, \
+ fcport->fcoe_attrs.name); \
+ return -ENOENT; \
+} \
+static FCOE_KOBJECT_ATTR(fcport, name, S_IRUGO, fc_show_fcoe_##name, NULL)
+
+fc_fcoe_rd(vlan_id, "%u\n");
+
+#define fc_fcoe_static_mac_addr(name) \
+static ssize_t fc_show_fcoe_static_mac_addr_##name(struct kobject *kobj, \
+ struct kobj_attribute *attr,\
+ char *buf) \
+{ \
+ struct device *dev = kobj_to_dev(kobj->parent); \
+ struct fc_port *fcport = dev_to_fcport(dev); \
+ ssize_t ret = -ENOENT; \
+ \
+ if (fcport->f->show_fcport_##name) \
+ return sysfs_format_mac(buf, fcport->fcoe_attrs.name, 6); \
+ return ret; \
+} \
+static FCOE_KOBJECT_ATTR(fcport, name, S_IRUGO, \
+ fc_show_fcoe_static_mac_addr_##name, NULL)
+
+fc_fcoe_static_mac_addr(enode_mac_address);
+
+#define fc_fcoe_dyn_mac_addr(name) \
+static ssize_t fc_show_fcoe_dyn_mac_addr_##name(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ char *buf) \
+{ \
+ struct device *dev = kobj_to_dev(kobj->parent); \
+ struct fc_port *fcport = dev_to_fcport(dev); \
+ ssize_t ret = -ENOENT; \
+ \
+ if (fcport->f->get_fcport_##name) { \
+ fcport->f->get_fcport_##name(fcport); \
+ return sysfs_format_mac(buf, fcport->fcoe_attrs.name, 6); \
+ } \
+ return ret; \
+} \
+static FCOE_KOBJECT_ATTR(fcport, name, S_IRUGO, \
+ fc_show_fcoe_dyn_mac_addr_##name, NULL)
+
+fc_fcoe_dyn_mac_addr(vn_port_mac_address);
+
+#define FCOE_SETUP_COND_ATTR_RD(_var, field) \
+ if (_var->f->show_##_var##_##field) { \
+ _var->fcport_fcoe_attrs[count] = \
+ &kobject_attr_fcoe_##_var##_##field.attr; \
+ count++; \
+ }
+
+#define FCOE_SETUP_ATTR_RD_NS(_var, field) \
+ do { \
+ _var->fcport_fcoe_attrs[count] = \
+ &kobject_attr_fcoe_##_var##_##field.attr; \
+ count++; \
+ } while (0)
+
+void fc_port_del_fcoe_attrs(struct fc_port *fcport)
+{
+ kobject_put(fcport->private_fcport_fcoe_kobj);
+ fcport->private_fcport_fcoe_kobj = NULL;
+}
+
+int fc_port_add_fcoe_attrs(struct fc_port *fcport,
+ struct fc_port_fcoe_attrs *fcoe_attrs)
+{
+ int count = 0;
+ int error;
+
+ memcpy(&fcport->fcoe_attrs, fcoe_attrs,
+ sizeof(struct fc_port_fcoe_attrs));
+
+ FCOE_SETUP_COND_ATTR_RD(fcport, enode_mac_address);
+ FCOE_SETUP_ATTR_RD_NS(fcport, vn_port_mac_address);
+ FCOE_SETUP_COND_ATTR_RD(fcport, vlan_id);
+
+ fcport->fcoe_attrs_group.attrs = fcport->fcport_fcoe_attrs;
+
+ /* Create a fcoe sub-directory under /sys/class/fc_host/hostX/ */
+ fcport->private_fcport_fcoe_kobj =
+ kobject_create_and_add("fcoe", &fcport->dev.kobj);
+ if (fcport->private_fcport_fcoe_kobj) {
+ /* Create the files associated with this kobject */
+ error = sysfs_create_group(fcport->private_fcport_fcoe_kobj,
+ &fcport->fcoe_attrs_group);
+ if (error) {
+ kobject_put(fcport->private_fcport_fcoe_kobj);
+ goto error;
+ }
+ }
+
+error:
+ return 0;
+}
diff --git a/drivers/fc/fcsysfs.h b/drivers/fc/fcsysfs.h
index 9bd7936..2f00775 100644
--- a/drivers/fc/fcsysfs.h
+++ b/drivers/fc/fcsysfs.h
@@ -89,4 +89,14 @@ const char *get_fc_port_state_name(enum fc_port_state table_key);
const char *get_fc_vport_state_name(enum fc_vport_state table_key);
+/* FCoE Attribute Interfaces */
+int fc_port_add_fcoe_attrs(struct fc_port *fcport,
+ struct fc_port_fcoe_attrs *fcoe_attrs);
+void fc_port_del_fcoe_attrs(struct fc_port *fcport);
+int fc_fabric_add_fcoe_attrs(struct fc_fabric *fcfabric,
+ struct fc_fabric_fcoe_attrs *fcoe_attrs);
+void fc_fabric_del_fcoe_attrs(struct fc_fabric *fcfabric);
+int fc_vport_add_fcoe_attrs(struct fc_vport *fcvport);
+void fc_vport_del_fcoe_attrs(struct fc_vport *fcvport);
+
#endif /*_FC_SYSFS_H_*/
diff --git a/drivers/fc/fcvport.c b/drivers/fc/fcvport.c
index 3dcfb49..112303c 100644
--- a/drivers/fc/fcvport.c
+++ b/drivers/fc/fcvport.c
@@ -356,6 +356,9 @@ void fc_vport_del(struct fc_vport *fcvport)
fc_vport_flush_work(fcvport);
+ if (fcvport->private_fcvport_fcoe_kobj)
+ fc_vport_del_fcoe_attrs(fcvport);
+
put_device(&fcvport->dev); /* For rports list */
device_del(&fcvport->dev);
put_device(fcvport->dev.parent);
@@ -481,13 +484,15 @@ EXPORT_SYMBOL(fc_vport_alloc);
/**
* fc_vport_add() - Add a FC vport to a FC fabric
+ * @fcfabric: The Fabric to add the vport to
* @fcvport: The FC vport to add
+ * @fcoe: Is the FC vport a FCoE vport?
*
* Add the device to sysfs and then populate it with
* attributes.
*/
int fc_vport_add(struct fc_fabric *fcfabric,
- struct fc_vport *fcvport)
+ struct fc_vport *fcvport, int fcoe)
{
int count = 0;
int error = 0;
@@ -545,6 +550,9 @@ int fc_vport_add(struct fc_fabric *fcfabric,
if (error || count != 0)
goto out_del_dev;
+ if (fcoe)
+ fc_vport_add_fcoe_attrs(fcvport);
+
return 0;
out_del_dev:
diff --git a/drivers/fc/fcvport_fcoe.c b/drivers/fc/fcvport_fcoe.c
new file mode 100644
index 0000000..3fdc6dd
--- /dev/null
+++ b/drivers/fc/fcvport_fcoe.c
@@ -0,0 +1,118 @@
+/*
+ * 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/genhd.h>
+#include "fcsysfs.h"
+
+/*
+ * Kobject attributes defined for /sys/class/fc_host/hostx/fcoe
+ * and /sys/class/fc_host/hostx/fcoe/statistics objects
+ */
+#define FCOE_KOBJECT_ATTR(_name, _mode, _show, _store) \
+ struct kobj_attribute kobject_attr_vport_fcoe_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
+/* Read FCoE host statistics */
+static ssize_t fc_vport_fcoe_lesb_show(const struct kobject *kobj,
+ char *buf,
+ unsigned long offset)
+{
+ struct device *dev = kobj_to_dev(kobj->parent);
+ struct fc_vport *fcvport = dev_to_fcvport(dev);
+ struct fc_vport_fcoe_lesb lesb;
+ ssize_t ret = -ENOENT;
+
+ if (offset > sizeof(struct fc_vport_fcoe_lesb) ||
+ offset % sizeof(u32) != 0)
+ WARN_ON(1);
+
+ if (fcvport->f->get_fcvport_fcoe_lesb) {
+ if (!(fcvport->f->get_fcvport_fcoe_lesb)(fcvport, &lesb))
+ ret = snprintf(buf, 20, "0x%x\n",
+ (unsigned int)*(u32 *)(((u8 *) &lesb)
+ + offset));
+ }
+
+ return ret;
+}
+
+/* generate a read-only FCoE host staistics attribute */
+#define fc_vport_fcoe_lesb(name) \
+static ssize_t fc_show_vport_fcoe_lesb_##name(struct kobject *cd, \
+ struct kobj_attribute *attr, \
+ char *buf) \
+{ \
+ return fc_vport_fcoe_lesb_show(cd, buf, \
+ offsetof(struct fc_vport_fcoe_lesb, \
+ name)); \
+} \
+static FCOE_KOBJECT_ATTR(name, S_IRUGO, fc_show_vport_fcoe_lesb_##name, NULL)
+
+fc_vport_fcoe_lesb(link_failure_count);
+fc_vport_fcoe_lesb(vlink_failure_count);
+fc_vport_fcoe_lesb(miss_fka_count);
+fc_vport_fcoe_lesb(symb_err_count);
+fc_vport_fcoe_lesb(err_block_count);
+fc_vport_fcoe_lesb(fcs_err_count);
+
+static struct attribute *fcvport_fcoe_lesb[] = {
+ &kobject_attr_vport_fcoe_link_failure_count.attr,
+ &kobject_attr_vport_fcoe_vlink_failure_count.attr,
+ &kobject_attr_vport_fcoe_miss_fka_count.attr,
+ &kobject_attr_vport_fcoe_symb_err_count.attr,
+ &kobject_attr_vport_fcoe_err_block_count.attr,
+ &kobject_attr_vport_fcoe_fcs_err_count.attr,
+ NULL
+};
+
+static struct attribute_group fcvport_fcoe_lesb_group = {
+ .attrs = fcvport_fcoe_lesb,
+};
+
+void fc_vport_del_fcoe_attrs(struct fc_vport *fcvport)
+{
+ kobject_put(fcvport->private_fcvport_fcoe_kobj);
+ fcvport->private_fcvport_fcoe_kobj = NULL;
+}
+
+int fc_vport_add_fcoe_attrs(struct fc_vport *fcvport)
+{
+ int error = 0;
+
+ if (fcvport->f->get_fcvport_fcoe_lesb) {
+ /*
+ * Create a fcoe sub-directory under
+ * /sys/class/fc_host/hostX/
+ */
+ fcvport->private_fcvport_fcoe_kobj =
+ kobject_create_and_add("fcoe",
+ &fcvport->dev.kobj);
+ if (fcvport->private_fcvport_fcoe_kobj) {
+ /* Create the files associated with this kobject */
+ error = sysfs_create_group(
+ fcvport->private_fcvport_fcoe_kobj,
+ &fcvport_fcoe_lesb_group);
+ if (error) {
+ kobject_put(fcvport->private_fcvport_fcoe_kobj);
+ goto error;
+ }
+ }
+ }
+
+error:
+ return error;
+}
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index baed0f4..5064086 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -399,7 +399,7 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
}
fcport = fc_port_add((struct device *)&netdev->dev.parent,
- &fcoe_fcport_fcn_tmpl, &fcoe_fcp_template);
+ &fcoe_fcport_fcn_tmpl, &fcoe_fcp_template, NULL);
if (!fcport) {
printk(KERN_ERR "Failed to add a fcport\n");
fcoe = ERR_PTR(-ENOMEM);
diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c
index 501fcc0..27c5e67 100644
--- a/drivers/scsi/fcoe/fcoe_ctlr.c
+++ b/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -956,7 +956,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
first = list_empty(&fcport->fabrics);
fcfabric = fc_fabric_add(fcport, fip->fcfabric_f,
- new_fcfabric);
+ new_fcfabric, NULL);
if (unlikely(!fcfabric))
goto out;
@@ -2733,7 +2733,8 @@ unlock:
/* If port ID is new, notify local port after dropping ctlr_mutex */
if (new_port_id) {
- fcvport->fcfabric = fc_fabric_add(fcport, fip->fcfabric_f, 0);
+ fcvport->fcfabric = fc_fabric_add(fcport, fip->fcfabric_f,
+ 0, NULL);
fc_lport_set_local_id(fip->lp, new_port_id);
}
}
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index d3f2b0c..37a4bad 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -760,7 +760,7 @@ void fc_lport_set_local_id(struct fc_lport *lport, u32 port_id)
case LPORT_ST_FLOGI:
if (port_id) {
mutex_lock(&fcport->lock);
- fc_vport_add(lport->fcfabric, fcvport);
+ fc_vport_add(lport->fcfabric, fcvport, 0);
mutex_unlock(&fcport->lock);
fc_lport_enter_ready(lport);
@@ -1554,7 +1554,7 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
fc_lport_set_port_id(lport, did, fp);
mutex_lock(&fcport->lock);
- fc_vport_add(lport->fcfabric, fcvport);
+ fc_vport_add(lport->fcfabric, fcvport, 0);
mutex_unlock(&fcport->lock);
fc_lport_enter_dns(lport);
diff --git a/include/fc/fc.h b/include/fc/fc.h
index c59d191..6158095 100644
--- a/include/fc/fc.h
+++ b/include/fc/fc.h
@@ -18,6 +18,7 @@
#ifndef _FC_H_
#define _FC_H_
+#include <linux/etherdevice.h>
#include <linux/device.h>
#include <linux/sched.h>
@@ -26,8 +27,65 @@ struct fc_fabric;
struct fc_vport;
struct fc_rport;
+struct fc_port_function_template;
struct fc_vport_function_template;
+struct fc_fabric_fcoe_attrs;
+
+/*
+ * mac_addressing_mode: If you alter this, you also need to alter
+ * scsi_transport_fc.c (for the ascii descriptions).
+ */
+enum fcoe_mac_addressing_mode {
+ FCF_SELECTED,
+ SPMA_ONLY,
+ FPMA_ONLY,
+ SPMA_PREFERRED,
+ FPMA_PREFERRED,
+};
+
+/*
+ * FCoE Local Port (Host) Attributes
+ *
+ * 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.
+ *
+ */
+struct fc_port_fcoe_attrs {
+ /* Static Attributes */
+ uint8_t enode_mac_address[ETH_ALEN];
+ u16 vlan_id;
+
+ /* Dynamic Attributes */
+ uint8_t vn_port_mac_address[ETH_ALEN];
+};
+
+struct fc_fabric_fcoe_attrs {
+ /* Static Attributes */
+ u32 fc_map;
+ u16 vfid;
+ uint8_t mac_address[ETH_ALEN];
+ uint8_t priority;
+ u32 fka_period;
+};
+
+/*
+ * FCoE Statistics - Following proposal for FC_BB_E FC Link error
+ * status block representation from T11/09-204v0 guidelines
+ */
+struct fc_vport_fcoe_lesb {
+ u32 link_failure_count;
+ u32 vlink_failure_count;
+ u32 miss_fka_count;
+ u32 symb_err_count;
+ u32 err_block_count;
+ u32 fcs_err_count;
+};
+
/*
* FC Port definitions - Following FC HBAAPI guidelines
*
@@ -306,6 +364,13 @@ struct _fc_cos_names {
#define FC_RPORT_NUM_ATTRS 9
#define FC_FABRIC_NUM_ATTRS 6
+/*
+ * These defines are FCoE attributes + 1 so the last
+ * entry in the attribute_group is NULL
+ */
+#define FC_PORT_FCOE_NUM_ATTRS 4
+#define FC_FABRIC_FCOE_NUM_ATTRS 6
+
struct fc_rport_function_template {
void (*get_rport_dev_loss_tmo)(struct fc_rport *);
void (*set_rport_dev_loss_tmo)(struct fc_rport *, u32);
@@ -430,6 +495,9 @@ struct fc_vport_function_template {
int (*issue_fcvport_lip)(struct fc_vport *);
+ int (*get_fcvport_fcoe_lesb)(struct fc_vport *,
+ struct fc_vport_fcoe_lesb *);
+
unsigned long show_fcvport_permanent_port_name:1;
unsigned long show_fcvport_port_id:1;
unsigned long show_fcvport_symbolic_name:1;
@@ -448,6 +516,13 @@ struct fc_fabric_function_template {
unsigned long show_fcfabric_fabric_name:1;
unsigned long show_fcfabric_dev_loss_tmo:1;
+ /* FCoE Static Attributes */
+ unsigned long show_fcfabric_fc_map:1;
+ unsigned long show_fcfabric_vfid:1;
+ unsigned long show_fcfabric_mac_address:1;
+ unsigned long show_fcfabric_priority:1;
+ unsigned long show_fcfabric_fka_period:1;
+
u32 dd_fcfabric_size;
};
@@ -489,6 +564,11 @@ struct fc_fabric {
struct list_head vports; /* uses the fcport->lock */
struct device_attribute attrs[FC_FABRIC_NUM_ATTRS];
+ struct attribute *fcfabric_fcoe_attrs[FC_FABRIC_FCOE_NUM_ATTRS];
+ struct kobject *private_fcfabric_fcoe_kobj;
+ struct attribute_group fcoe_attrs_group;
+
+ struct fc_fabric_fcoe_attrs fcoe_attrs;
};
static inline void *fc_fabric_priv(const struct fc_fabric *fcfabric)
@@ -529,6 +609,13 @@ struct fc_port_function_template {
u32 dd_fcport_size;
struct fc_vport_function_template fcvport_f;
+
+ /* Static Attributes */
+ unsigned long show_fcport_enode_mac_address:1;
+ unsigned long show_fcport_vlan_id:1;
+
+ /* Dynamic Attributes */
+ void (*get_fcport_vn_port_mac_address)(struct fc_port *);
};
struct fc_port {
@@ -542,7 +629,11 @@ struct fc_port {
struct mutex lock;
struct fc_port_function_template *f;
void *fc4_f;
+
struct device_attribute attrs[FC_PORT_NUM_ATTRS];
+ struct attribute *fcport_fcoe_attrs[FC_PORT_FCOE_NUM_ATTRS];
+ struct kobject *private_fcport_fcoe_kobj;
+ struct attribute_group fcoe_attrs_group;
/* Fixed Attributes */
u8 supported_fc4s[FC_FC4_LIST_SIZE];
@@ -562,6 +653,8 @@ struct fc_port {
u32 speed;
int fab_dev_loss_tmo;
+
+ struct fc_port_fcoe_attrs fcoe_attrs;
};
static inline void *fc_port_priv(const struct fc_port *fcport)
@@ -644,6 +737,8 @@ struct fc_vport {
struct fc_fabric *fcfabric; /* fabric on which the
vport was created */
struct device_attribute attrs[FC_VPORT_NUM_ATTRS];
+ struct kobject *private_fcvport_fcoe_kobj;
+ struct attribute_group fcoe_attrs_group;
struct fc4_template *fc4_f;
@@ -811,10 +906,11 @@ struct fc_vport *fc_vport_lookup(struct fc_fabric *fcfabric,
struct fc_port *fc_port_add(struct device *pdev,
struct fc_port_function_template *,
- void *fc4_f);
+ void *fc4_f, struct fc_port_fcoe_attrs *);
struct fc_fabric *fc_fabric_add(struct fc_port *fcport,
struct fc_fabric_function_template *,
- struct fc_fabric *);
+ struct fc_fabric *,
+ struct fc_fabric_fcoe_attrs *);
struct fc_vport *fc_vport_alloc(struct fc_vport *fcnport,
struct fc_vport_identifiers *ids,
struct fc_vport_function_template *fcn_tmpl,
@@ -822,8 +918,7 @@ struct fc_vport *fc_vport_alloc(struct fc_vport *fcnport,
struct fc_vport *fc_vport_create(struct fc_fabric *, int channel,
struct fc_vport_identifiers *);
int fc_vport_add(struct fc_fabric *fcfabric,
- struct fc_vport *fcvport);
-
+ struct fc_vport *fcvport, int fcoe);
void fc_port_del(struct fc_port *fcport);
void fc_port_del_fabrics(struct fc_port *fcport);
void fc_fabric_del(struct fc_fabric *fcfabric);
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v2 07/10] libfcoe, fcoe: Use FCoE attributes
2011-03-11 21:54 [RFC PATCH v2 00/10] FC Sysfs and FCoE attributes Robert Love
` (5 preceding siblings ...)
2011-03-11 21:55 ` [RFC PATCH v2 06/10] fc: Add FCoE attributes to FC sysfs Robert Love
@ 2011-03-11 21:55 ` Robert Love
2011-03-11 21:55 ` [RFC PATCH v2 08/10] fnic: Convert to new FC Sysfs infrastructure Robert Love
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Robert Love @ 2011-03-11 21:55 UTC (permalink / raw)
To: linux-scsi; +Cc: abjoglek, giridhar.malavali, james.smart, bprakash
Use the new interfaces to add fcoe attributes to the
FC syfs layout.
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
drivers/scsi/fcoe/fcoe.c | 101 ++++++++++++++++++++++++++++++++++-------
drivers/scsi/fcoe/fcoe_ctlr.c | 24 ++++++----
drivers/scsi/libfc/fc_lport.c | 6 ++
include/scsi/libfc.h | 1
4 files changed, 104 insertions(+), 28 deletions(-)
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 5064086..8603b8c 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -123,6 +123,10 @@ static void fcoe_get_vport_ids(struct fc_lport *lport,
struct fc_vport_identifiers *ids);
*/
+static void fcoe_get_fcport_vn_port_mac_address(struct fc_port *);
+static int fcoe_get_fcvport_fcoe_lesb(struct fc_vport *,
+ struct fc_vport_fcoe_lesb *);
+
/* notification function for packets from net device */
static struct notifier_block fcoe_notifier = {
.notifier_call = fcoe_device_notification,
@@ -211,6 +215,8 @@ struct fc_port_function_template fcoe_fcport_fcn_tmpl = {
.dd_fcvport_size = (sizeof(struct fc_lport) +
sizeof(struct fcoe_port)),
+ .get_fcvport_fcoe_lesb = fcoe_get_fcvport_fcoe_lesb,
+
.fcrport_f = {
.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
.show_rport_maxframe_size = 1,
@@ -220,6 +226,11 @@ struct fc_port_function_template fcoe_fcport_fcn_tmpl = {
.terminate_rport_io = fc_rport_terminate_io,
},
},
+
+ .show_fcport_enode_mac_address = 1,
+ .show_fcport_vlan_id = 1,
+
+ .get_fcport_vn_port_mac_address = fcoe_get_fcport_vn_port_mac_address,
};
struct fc_fabric_function_template fcoe_fcfabric_fcn_tmpl = {
@@ -228,6 +239,12 @@ struct fc_fabric_function_template fcoe_fcfabric_fcn_tmpl = {
.show_fcfabric_fabric_name = 1,
.show_fcfabric_dev_loss_tmo = 1,
.dd_fcfabric_size = sizeof(struct fcoe_fcf),
+
+ .show_fcfabric_fc_map = 1,
+ .show_fcfabric_vfid = 1,
+ .show_fcfabric_mac_address = 1,
+ .show_fcfabric_priority = 1,
+ .show_fcfabric_fka_period = 1,
};
static void _fcoe_get_vport_ids(struct fcoe_interface *fcoe,
@@ -288,6 +305,32 @@ static void fcoe_set_port_name(struct fc_lport *lport)
}
*/
+
+static int fcoe_get_san_mac(struct net_device *netdev, u8 mac_addr[ETH_ALEN])
+{
+ struct netdev_hw_addr *ha;
+ struct net_device *real_dev;
+ int ret = -ENOENT;
+
+ /* look for SAN MAC address, if multiple SAN MACs exist, only
+ * use the first one for SPMA */
+ real_dev = (netdev->priv_flags & IFF_802_1Q_VLAN) ?
+ vlan_dev_real_dev(netdev) : netdev;
+ rcu_read_lock();
+ for_each_dev_addr(real_dev, ha) {
+ if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
+ (is_valid_ether_addr(ha->addr))) {
+ memcpy(mac_addr, ha->addr, ETH_ALEN);
+ ret = 0;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+
+
/**
* fcoe_interface_setup() - Setup a FCoE interface
* @fcoe: The new FCoE interface
@@ -300,8 +343,6 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
struct net_device *netdev)
{
struct fcoe_ctlr *fip = &fcoe->ctlr;
- struct netdev_hw_addr *ha;
- struct net_device *real_dev;
u8 flogi_maddr[ETH_ALEN];
const struct net_device_ops *ops;
@@ -326,20 +367,9 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
return -EOPNOTSUPP;
}
- /* look for SAN MAC address, if multiple SAN MACs exist, only
- * use the first one for SPMA */
- real_dev = (netdev->priv_flags & IFF_802_1Q_VLAN) ?
- vlan_dev_real_dev(netdev) : netdev;
- rcu_read_lock();
- for_each_dev_addr(real_dev, ha) {
- if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
- (is_valid_ether_addr(ha->addr))) {
- memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN);
- fip->spma = 1;
- break;
- }
- }
- rcu_read_unlock();
+ /* Will not change ctlr_src_addr if there is no SAN MAC */
+ if (!fcoe_get_san_mac(netdev, fip->ctl_src_addr))
+ fip->spma = 1;
/* setup Source Mac Address */
if (!fip->spma)
@@ -388,9 +418,14 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
enum fip_state fip_mode)
{
struct fc_port *fcport;
+ struct fc_port_fcoe_attrs fcoe_attrs;
struct fcoe_interface *fcoe;
int err;
+ fcoe_get_san_mac(netdev, fcoe_attrs.enode_mac_address);
+ if (netdev->priv_flags & IFF_802_1Q_VLAN)
+ fcoe_attrs.vlan_id = vlan_dev_vlan_id(netdev);
+
if (!try_module_get(THIS_MODULE)) {
FCOE_NETDEV_DBG(netdev,
"Could not get a reference to the module\n");
@@ -399,7 +434,8 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
}
fcport = fc_port_add((struct device *)&netdev->dev.parent,
- &fcoe_fcport_fcn_tmpl, &fcoe_fcp_template, NULL);
+ &fcoe_fcport_fcn_tmpl, &fcoe_fcp_template,
+ &fcoe_attrs);
if (!fcport) {
printk(KERN_ERR "Failed to add a fcport\n");
fcoe = ERR_PTR(-ENOMEM);
@@ -585,6 +621,7 @@ static u8 *fcoe_get_src_mac(struct fc_lport *lport)
*/
static int fcoe_lport_config(struct fc_lport *lport)
{
+ lport->fcoe = 1;
lport->link_up = 0;
lport->qfull = 0;
lport->max_retry_count = 3;
@@ -2559,3 +2596,33 @@ static void fcoe_set_port_id(struct fc_lport *lport,
if (fp && fc_frame_payload_op(fp) == ELS_FLOGI)
fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp);
}
+
+static void fcoe_get_fcport_vn_port_mac_address(struct fc_port *fcport)
+{
+ struct fcoe_interface *fcoe = fc_port_priv(fcport);
+ struct fc_lport *lport = fcoe->ctlr.lp;
+ struct fcoe_port *port = lport_priv(lport);
+
+ memcpy(fcport->fcoe_attrs.vn_port_mac_address,
+ port->data_src_addr, ETH_ALEN);
+}
+
+static int fcoe_get_fcvport_fcoe_lesb(struct fc_vport *fcvport,
+ struct fc_vport_fcoe_lesb *lesb)
+{
+ struct fc_lport *lport = fc_vport_priv(fcvport);
+ struct fc_els_lesb fc_lesb;
+ struct fcoe_fc_els_lesb *fcoe_lesb;
+
+ fcoe_get_lesb(lport, &fc_lesb);
+ fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb);
+
+ lesb->link_failure_count = fcoe_lesb->lesb_link_fail;
+ lesb->vlink_failure_count = fcoe_lesb->lesb_vlink_fail;
+ lesb->miss_fka_count = fcoe_lesb->lesb_miss_fka;
+ lesb->symb_err_count = fcoe_lesb->lesb_symb_err;
+ lesb->err_block_count = fcoe_lesb->lesb_err_block;
+ lesb->fcs_err_count = fcoe_lesb->lesb_fcs_error;
+
+ return 0;
+}
diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c
index 27c5e67..e8e191c 100644
--- a/drivers/scsi/fcoe/fcoe_ctlr.c
+++ b/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -190,14 +190,12 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
{
struct fc_port *fcport = fip->lp->fcport;
- struct fc_vport *fcvport = lport_to_fcvport(fip->lp);
cancel_work_sync(&fip->recv_work);
skb_queue_purge(&fip->fip_recv_list);
mutex_lock(&fcport->lock);
fcoe_ctlr_set_state(fip, FIP_ST_DISABLED);
- fcvport->fcfabric = NULL;
fcoe_ctlr_reset_fcfs(fip);
mutex_unlock(&fcport->lock);
del_timer_sync(&fip->timer);
@@ -931,6 +929,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
struct fc_fabric *fcfabric;
struct fcoe_fcf *fcf;
struct fcoe_fcf new;
+ struct fc_fabric_fcoe_attrs fcoe_attrs;
unsigned long sol_tov = msecs_to_jiffies(FCOE_CTRL_SOL_TOV);
int first = 0;
int mtu_valid;
@@ -955,8 +954,14 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
*/
first = list_empty(&fcport->fabrics);
+ fcoe_attrs.fc_map = new.fc_map;
+ fcoe_attrs.vfid = new.vfid;
+ memcpy(fcoe_attrs.mac_address, new.fcf_mac, ETH_ALEN);
+ fcoe_attrs.priority = new.pri;
+ fcoe_attrs.fka_period = new.fka_period;
+
fcfabric = fc_fabric_add(fcport, fip->fcfabric_f,
- new_fcfabric, NULL);
+ new_fcfabric, &fcoe_attrs);
if (unlikely(!fcfabric))
goto out;
@@ -2732,11 +2737,13 @@ unlock:
mutex_unlock(&fcport->lock);
/* If port ID is new, notify local port after dropping ctlr_mutex */
- if (new_port_id) {
- fcvport->fcfabric = fc_fabric_add(fcport, fip->fcfabric_f,
- 0, NULL);
- fc_lport_set_local_id(fip->lp, new_port_id);
- }
+ if (new_port_id) {
+ struct fc_fabric_fcoe_attrs fcoe_attrs;
+ /* Add a dummy fabric for VN2VN */
+ fcvport->fcfabric = fc_fabric_add(fcport, fip->fcfabric_f,
+ NULL, &fcoe_attrs);
+ fc_lport_set_local_id(fip->lp, new_port_id);
+ }
}
/**
@@ -2744,7 +2751,6 @@ unlock:
* @lp: The local port to configure libfc for
* @fip: The FCoE controller in use by the local port
* @tt: The libfc function template
- * @init_fcp: If non-zero, the FCP portion of libfc should be initialized
*
* Returns : 0 for success
*/
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 37a4bad..1bd4ddc 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -760,7 +760,8 @@ void fc_lport_set_local_id(struct fc_lport *lport, u32 port_id)
case LPORT_ST_FLOGI:
if (port_id) {
mutex_lock(&fcport->lock);
- fc_vport_add(lport->fcfabric, fcvport, 0);
+ fc_vport_add(lport->fcfabric, fcvport,
+ lport->fcoe);
mutex_unlock(&fcport->lock);
fc_lport_enter_ready(lport);
@@ -1554,7 +1555,8 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
fc_lport_set_port_id(lport, did, fp);
mutex_lock(&fcport->lock);
- fc_vport_add(lport->fcfabric, fcvport, 0);
+ fc_vport_add(lport->fcfabric, fcvport,
+ lport->fcoe);
mutex_unlock(&fcport->lock);
fc_lport_enter_dns(lport);
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 1086568..0c67b87 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -872,6 +872,7 @@ struct fc_lport {
struct fc_els_rnid_gen rnid_gen;
/* Capabilities */
+ u32 fcoe:1;
u32 sg_supp:1;
u32 seq_offload:1;
u32 crc_offload:1;
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v2 08/10] fnic: Convert to new FC Sysfs infrastructure
2011-03-11 21:54 [RFC PATCH v2 00/10] FC Sysfs and FCoE attributes Robert Love
` (6 preceding siblings ...)
2011-03-11 21:55 ` [RFC PATCH v2 07/10] libfcoe, fcoe: Use FCoE attributes Robert Love
@ 2011-03-11 21:55 ` Robert Love
2011-03-11 21:55 ` [RFC PATCH v2 09/10] fc: Add a hbaapi_lib attribute to the fcport structure Robert Love
2011-03-11 21:55 ` [RFC PATCH v2 10/10] Documentation: Add fc_sysfs document Robert Love
9 siblings, 0 replies; 11+ messages in thread
From: Robert Love @ 2011-03-11 21:55 UTC (permalink / raw)
To: linux-scsi; +Cc: abjoglek, giridhar.malavali, james.smart, bprakash
This patch converts the fnic driver to use the
new FC Sysfs infrastructure by removing the existing
FC transport and replacing it. This patch was only
compile tested due to not having hardware.
One open that I have is that most of fnic_get_fcport_speed
is currently commented out. The function receives a
fcport as an argument, but the speed information is
stored in a fnic instance. I'm not sure how to get from
the fcport to the fnic. Since fnic allocates a fcvport
at the same time it allocates/adds a fcport we could
put a pointer to the root fcvport in the fcport or maybe
just make it private data on the end of the fcport since
libfc doesn't need an N_Port fcvport pointer in the
fcport.
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
drivers/scsi/Kconfig | 1
drivers/scsi/fnic/fnic.h | 33 ++-
drivers/scsi/fnic/fnic_fcs.c | 32 ++-
drivers/scsi/fnic/fnic_isr.c | 13 +
drivers/scsi/fnic/fnic_main.c | 412 +++++++++++++++++++++++------------------
drivers/scsi/fnic/fnic_res.c | 47 ++---
drivers/scsi/fnic/fnic_scsi.c | 193 ++++++++++---------
7 files changed, 400 insertions(+), 331 deletions(-)
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 6b91d11..608ba3f 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -700,6 +700,7 @@ config FCOE
config FCOE_FNIC
tristate "Cisco FNIC Driver"
depends on PCI && X86
+ select SCSI_FCP_ATTRS
select LIBFCOE
help
This is support for the Cisco PCI-Express FCoE HBA.
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index 671cde9..48e4faf 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -89,21 +89,28 @@ do { \
} while (0); \
} while (0)
-#define FNIC_MAIN_DBG(kern_level, host, fmt, args...) \
- FNIC_CHECK_LOGGING(FNIC_MAIN_LOGGING, \
- shost_printk(kern_level, host, fmt, ##args);)
+#define FNIC_MAIN_DBG(kern_level, lport, fmt, args...) \
+ FNIC_CHECK_LOGGING(FNIC_MAIN_LOGGING, \
+ printk(KERN_INFO "fcport%u: " fmt, \
+ fc_port_id((lport)->fcport), \
+ ##args);)
-#define FNIC_FCS_DBG(kern_level, host, fmt, args...) \
- FNIC_CHECK_LOGGING(FNIC_FCS_LOGGING, \
- shost_printk(kern_level, host, fmt, ##args);)
+#define FNIC_FCS_DBG(kern_level, lport, fmt, args...) \
+ FNIC_CHECK_LOGGING(FNIC_FCS_LOGGING, \
+ printk(KERN_INFO "fcport%u: " fmt, \
+ fc_port_id((lport)->fcport), \
+ ##args);)
-#define FNIC_SCSI_DBG(kern_level, host, fmt, args...) \
- FNIC_CHECK_LOGGING(FNIC_SCSI_LOGGING, \
- shost_printk(kern_level, host, fmt, ##args);)
+#define FNIC_SCSI_DBG(kern_level, host, fmt, args...) \
+ FNIC_CHECK_LOGGING(FNIC_SCSI_LOGGING, \
+ shost_printk(kern_level, host, fmt, ##args);)
+
+#define FNIC_ISR_DBG(kern_level, lport, fmt, args...) \
+ FNIC_CHECK_LOGGING(FNIC_ISR_LOGGING, \
+ printk(KERN_INFO "fcport%u: " fmt, \
+ fc_port_id((lport)->fcport), \
+ ##args);)
-#define FNIC_ISR_DBG(kern_level, host, fmt, args...) \
- FNIC_CHECK_LOGGING(FNIC_ISR_LOGGING, \
- shost_printk(kern_level, host, fmt, ##args);)
extern const char *fnic_state_str[];
@@ -250,7 +257,7 @@ int fnic_queuecommand(struct Scsi_Host *, struct scsi_cmnd *);
int fnic_abort_cmd(struct scsi_cmnd *);
int fnic_device_reset(struct scsi_cmnd *);
int fnic_host_reset(struct scsi_cmnd *);
-int fnic_reset(struct Scsi_Host *);
+int fnic_fcvport_reset(struct fc_vport *);
void fnic_scsi_cleanup(struct fc_lport *);
void fnic_scsi_abort_io(struct fc_lport *);
void fnic_empty_scsi_cleanup(struct fc_lport *);
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 2b48d79..28298a0 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -41,6 +41,9 @@ static void fnic_set_eth_mode(struct fnic *);
void fnic_handle_link(struct work_struct *work)
{
struct fnic *fnic = container_of(work, struct fnic, link_work);
+ struct fc_lport *lport = fnic->lport;
+ struct fcp_init *fcpinit = lport->fcpinit;
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
unsigned long flags;
int old_link_status;
u32 old_link_down_cnt;
@@ -64,12 +67,12 @@ void fnic_handle_link(struct work_struct *work)
else {
if (old_link_down_cnt != fnic->link_down_cnt) {
/* UP -> DOWN -> UP */
- fnic->lport->host_stats.link_failure_count++;
+ si->fcpinit_stats.link_failure_count++;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport,
"link down\n");
fcoe_ctlr_link_down(&fnic->ctlr);
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport,
"link up\n");
fcoe_ctlr_link_up(&fnic->ctlr);
} else
@@ -79,13 +82,13 @@ void fnic_handle_link(struct work_struct *work)
} else if (fnic->link_status) {
/* DOWN -> UP */
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n");
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport, "link up\n");
fcoe_ctlr_link_up(&fnic->ctlr);
} else {
/* UP -> DOWN */
- fnic->lport->host_stats.link_failure_count++;
+ si->fcpinit_stats.link_failure_count++;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n");
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport, "link down\n");
fcoe_ctlr_link_down(&fnic->ctlr);
}
@@ -194,7 +197,7 @@ void fnic_update_mac_locked(struct fnic *fnic, u8 *new)
new = ctl;
if (!compare_ether_addr(data, new))
return;
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "update_mac %pM\n", new);
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport, "update_mac %pM\n", new);
if (!is_zero_ether_addr(data) && compare_ether_addr(data, ctl))
vnic_dev_del_addr(fnic->vdev, data);
memcpy(data, new, ETH_ALEN);
@@ -236,7 +239,7 @@ void fnic_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp)
u8 *mac;
int ret;
- FNIC_FCS_DBG(KERN_DEBUG, lport->host, "set port_id %x fp %p\n",
+ FNIC_FCS_DBG(KERN_DEBUG, lport, "set port_id %x fp %p\n",
port_id, fp);
/*
@@ -263,7 +266,7 @@ void fnic_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp)
if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE)
fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;
else {
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport,
"Unexpected fnic state %s while"
" processing flogi resp\n",
fnic_state_to_str(fnic->state));
@@ -344,7 +347,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
eth_hdrs_stripped = 0;
skb_trim(skb, bytes_written);
if (!fcs_ok) {
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport,
"fcs error. dropping packet.\n");
goto drop;
}
@@ -353,13 +356,13 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
} else {
/* wrong CQ type*/
- shost_printk(KERN_ERR, fnic->lport->host,
+ shost_printk(KERN_ERR, fnic->lport->fcpinit->shost,
"fnic rq_cmpl wrong cq type x%x\n", type);
goto drop;
}
if (!fcs_ok || packet_error || !fcoe_fc_crc_ok || fcoe_enc_error) {
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport,
"fnic rq_cmpl fcoe x%x fcsok x%x"
" pkterr x%x fcoe_fc_crc_ok x%x, fcoe_enc_err"
" x%x\n",
@@ -410,7 +413,8 @@ int fnic_rq_cmpl_handler(struct fnic *fnic, int rq_work_to_do)
if (cur_work_done) {
err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
if (err)
- shost_printk(KERN_ERR, fnic->lport->host,
+ shost_printk(KERN_ERR,
+ fnic->lport->fcpinit->shost,
"fnic_alloc_rq_frame cant alloc"
" frame\n");
}
@@ -435,7 +439,7 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq)
len = FC_FRAME_HEADROOM + FC_MAX_FRAME + FC_FRAME_TAILROOM;
skb = dev_alloc_skb(len);
if (!skb) {
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport,
"Unable to allocate RQ sk_buff\n");
return -ENOMEM;
}
diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c
index 5c1f223..41f7720 100644
--- a/drivers/scsi/fnic/fnic_isr.c
+++ b/drivers/scsi/fnic/fnic_isr.c
@@ -197,9 +197,10 @@ int fnic_request_intr(struct fnic *fnic)
fnic->msix[i].devname,
fnic->msix[i].devid);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "MSIX: request_irq"
- " failed %d\n", err);
+ printk(KERN_ERR "fcport%u: MSIX: request_irq"
+ " failed %d\n",
+ fc_port_id(fnic->lport->fcport),
+ err);
fnic_free_intr(fnic);
break;
}
@@ -249,7 +250,7 @@ int fnic_set_intr_mode(struct fnic *fnic)
fnic->intr_count = n + m + o + 1;
fnic->err_intr_offset = FNIC_MSIX_ERR_NOTIFY;
- FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_ISR_DBG(KERN_DEBUG, fnic->lport,
"Using MSI-X Interrupts\n");
vnic_dev_set_intr_mode(fnic->vdev,
VNIC_DEV_INTR_MODE_MSIX);
@@ -276,7 +277,7 @@ int fnic_set_intr_mode(struct fnic *fnic)
fnic->intr_count = 1;
fnic->err_intr_offset = 0;
- FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_ISR_DBG(KERN_DEBUG, fnic->lport,
"Using MSI Interrupts\n");
vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSI);
@@ -302,7 +303,7 @@ int fnic_set_intr_mode(struct fnic *fnic)
fnic->cq_count = 3;
fnic->intr_count = 3;
- FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_ISR_DBG(KERN_DEBUG, fnic->lport,
"Using Legacy Interrupts\n");
vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX);
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index bb63f1a..a302c94 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -27,14 +27,15 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/if_ether.h>
-#include <scsi/fc/fc_fip.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_transport.h>
-#include <scsi/scsi_transport_fc.h>
+
#include <scsi/scsi_tcq.h>
+#include <scsi/fc/fc_fip.h>
#include <scsi/libfc.h>
#include <scsi/fc_frame.h>
+#include <fc/fc.h>
+#include <scsi/scsi_transport_fcp.h>
+
#include "vnic_dev.h"
#include "vnic_intr.h"
#include "vnic_stats.h"
@@ -68,6 +69,8 @@ unsigned int fnic_log_level;
module_param(fnic_log_level, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels");
+static void fnic_get_fcport_speed(struct fc_port *fcport);
+static void fnic_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout);
static struct libfc_function_template fnic_transport_template = {
.frame_send = fnic_send,
@@ -79,7 +82,9 @@ static struct libfc_function_template fnic_transport_template = {
static int fnic_slave_alloc(struct scsi_device *sdev)
{
- struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+ struct scsi_target *starget = scsi_target(sdev);
+ struct fcp_targ *fcptarg = starget_to_fcptarg(starget);
+ struct fc_rport *rport = fcptarg->rport;
sdev->tagged_supported = 1;
@@ -90,23 +95,110 @@ static int fnic_slave_alloc(struct scsi_device *sdev)
return 0;
}
-static struct scsi_host_template fnic_host_template = {
- .module = THIS_MODULE,
- .name = DRV_NAME,
- .queuecommand = fnic_queuecommand,
- .eh_abort_handler = fnic_abort_cmd,
- .eh_device_reset_handler = fnic_device_reset,
- .eh_host_reset_handler = fnic_host_reset,
- .slave_alloc = fnic_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 = FNIC_MAX_IO_REQ,
- .use_clustering = ENABLE_CLUSTERING,
- .sg_tablesize = FNIC_MAX_SG_DESC_CNT,
- .max_sectors = 0xffff,
- .shost_attrs = fnic_attrs,
+static void fnic_fcpinit_add(struct fcp_init *fcpinit)
+{
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
+ struct fc_lport *lport = fcpinit_to_lport(fcpinit);
+ struct fnic *fnic = lport_priv(lport);
+ int err;
+
+ err = scsi_init_shared_tag_map(si->host, FNIC_MAX_IO_REQ);
+ if (err)
+ shost_printk(KERN_ERR, si->host,
+ "Unable to alloc shared tag map\n");
+
+ si->host->max_lun = fnic->config.luns_per_tgt;
+ si->host->max_id = FNIC_MAX_FCP_TARGET;
+ si->host->max_cmd_len = FCOE_MAX_CMD_LEN;
+}
+
+static void fnic_fcpinit_del(struct fcp_init *fcpinit)
+{
+ /* TODO: Anything to clean up here? */
+}
+
+static struct fcp_template fnic_fcp_template = {
+ .fcpinit_priv_size = sizeof(struct fc_fcp_internal),
+ .fcp_fcpinit_add = fnic_fcpinit_add,
+ .fcp_fcpinit_del = fnic_fcpinit_del,
+
+ .shost_template = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .queuecommand = fnic_queuecommand,
+ .eh_abort_handler = fnic_abort_cmd,
+ .eh_device_reset_handler = fnic_device_reset,
+ .eh_host_reset_handler = fnic_host_reset,
+ .slave_alloc = fnic_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 = FNIC_MAX_IO_REQ,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sg_tablesize = FNIC_MAX_SG_DESC_CNT,
+ .max_sectors = 0xffff,
+ .shost_attrs = fnic_attrs,
+ },
+
+ .show_fcpinit_port_state = 1,
+ .get_fcpinit_stats = fc_get_fcpinit_stats,
+ .bsg_request = fc_lport_bsg_request,
+};
+
+struct fc_port_function_template fnic_fcport_fcn_tmpl = {
+ .show_fcport_system_hostname = 1,
+ .show_fcport_fab_dev_loss_tmo = 1,
+ .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,
+ .show_fcport_hbaapi_lib = 1,
+
+ .get_fcport_speed = fnic_get_fcport_speed,
+
+ .show_fcport_enode_mac_address = 1,
+ .show_fcport_vlan_id = 1,
+
+ .fcvport_f = {
+ .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,
+
+ .issue_fcvport_lip = fnic_fcvport_reset,
+
+ .get_fcvport_port_state = fc_get_fcvport_port_state,
+ .dd_fcvport_size = (sizeof(struct fc_lport) +
+ (sizeof(struct fnic))),
+
+ .fcrport_f = {
+ .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+ .set_rport_dev_loss_tmo = fnic_set_rport_dev_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+ .terminate_rport_io = fnic_terminate_rport_io,
+ },
+ },
+};
+
+struct fc_fabric_function_template fnic_fcfabric_fcn_tmpl = {
+ .fabric_match = fcoe_fcf_match,
+
+ .show_fcfabric_fabric_name = 1,
+ .show_fcfabric_dev_loss_tmo = 1,
+ .dd_fcfabric_size = sizeof(struct fcoe_fcf),
+
+ .show_fcfabric_fc_map = 1,
+ .show_fcfabric_vfid = 1,
+ .show_fcfabric_mac_address = 1,
+ .show_fcfabric_priority = 1,
+ .show_fcfabric_fka_period = 1,
};
static void
@@ -118,48 +210,21 @@ fnic_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
rport->dev_loss_tmo = 1;
}
-static void fnic_get_host_speed(struct Scsi_Host *shost);
-static struct scsi_transport_template *fnic_fc_transport;
-static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *);
-
-static struct fc_function_template fnic_fc_functions = {
-
- .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 = fnic_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,
- .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,
- .show_rport_dev_loss_tmo = 1,
- .set_rport_dev_loss_tmo = fnic_set_rport_dev_loss_tmo,
- .issue_fc_host_lip = fnic_reset,
- .get_fc_host_stats = fnic_get_stats,
- .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
- .terminate_rport_io = fnic_terminate_rport_io,
- .bsg_request = fc_lport_bsg_request,
-};
-
-static void fnic_get_host_speed(struct Scsi_Host *shost)
+static void fnic_get_fcport_speed(struct fc_port *fcport)
{
+/*
+TODO readd
+
+problem is that fnic needs to get to a sub lport struct
+and all we have is the fcport with no priv data to get
+to that sub lport struct
+
+
struct fc_lport *lp = shost_priv(shost);
struct fnic *fnic = lport_priv(lp);
u32 port_speed = vnic_dev_port_speed(fnic->vdev);
- /* Add in other values as they get defined in fw */
+ Add in other values as they get defined in fw
switch (port_speed) {
case 10000:
fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
@@ -168,14 +233,16 @@ static void fnic_get_host_speed(struct Scsi_Host *shost)
fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
break;
}
+*/
}
-static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host)
+struct fcp_init_statistics *fnic_get_fcpinit_stats(struct fcp_init *fcpinit)
{
int ret;
- struct fc_lport *lp = shost_priv(host);
+ struct fc_lport *lp = fcpinit_to_lport(fcpinit);
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
struct fnic *fnic = lport_priv(lp);
- struct fc_host_statistics *stats = &lp->host_stats;
+ struct fcp_init_statistics *stats = &si->fcpinit_stats;
struct vnic_stats *vs;
unsigned long flags;
@@ -188,8 +255,7 @@ static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host)
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
if (ret) {
- FNIC_MAIN_DBG(KERN_DEBUG, fnic->lport->host,
- "fnic: Get vnic stats failed"
+ FNIC_MAIN_DBG(KERN_DEBUG, lp, "fnic: Get vnic stats failed"
" 0x%x", ret);
return stats;
}
@@ -216,25 +282,22 @@ void fnic_log_q_error(struct fnic *fnic)
for (i = 0; i < fnic->raw_wq_count; i++) {
error_status = ioread32(&fnic->wq[i].ctrl->error_status);
if (error_status)
- shost_printk(KERN_ERR, fnic->lport->host,
- "WQ[%d] error_status"
- " %d\n", i, error_status);
+ printk(KERN_ERR "fcport%u: WQ[%d] error_status %d\n",
+ fnic->lport->fcport->id, i, error_status);
}
for (i = 0; i < fnic->rq_count; i++) {
error_status = ioread32(&fnic->rq[i].ctrl->error_status);
if (error_status)
- shost_printk(KERN_ERR, fnic->lport->host,
- "RQ[%d] error_status"
- " %d\n", i, error_status);
+ printk(KERN_ERR "fcport%u: RQ[%d] error_status %d\n",
+ fnic->lport->fcport->id, i, error_status);
}
for (i = 0; i < fnic->wq_copy_count; i++) {
error_status = ioread32(&fnic->wq_copy[i].ctrl->error_status);
if (error_status)
- shost_printk(KERN_ERR, fnic->lport->host,
- "CWQ[%d] error_status"
- " %d\n", i, error_status);
+ printk(KERN_ERR "fcport%u: CWQ[%d] error_status %d\n",
+ fnic->lport->fcport->id, i, error_status);
}
}
@@ -268,10 +331,10 @@ static int fnic_notify_set(struct fnic *fnic)
err = vnic_dev_notify_set(fnic->vdev, FNIC_MSIX_ERR_NOTIFY);
break;
default:
- shost_printk(KERN_ERR, fnic->lport->host,
- "Interrupt mode should be set up"
- " before devcmd notify set %d\n",
- vnic_dev_get_intr_mode(fnic->vdev));
+ printk(KERN_ERR "fcport%u: Interrupt mode should be set up"
+ " before devcmd notify set %d\n",
+ fc_port_id(fnic->lport->fcport),
+ vnic_dev_get_intr_mode(fnic->vdev));
err = -1;
break;
}
@@ -413,7 +476,10 @@ static u8 *fnic_get_mac(struct fc_lport *lport)
static int __devinit fnic_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- struct Scsi_Host *host;
+ struct fc_port *fcport;
+ struct fc_vport *fcvport;
+ struct fc_vport_identifiers ids;
+ struct fc_port_fcoe_attrs fcoe_attrs;
struct fc_lport *lp;
struct fnic *fnic;
mempool_t *pool;
@@ -421,32 +487,45 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
int i;
unsigned long flags;
- /*
- * Allocate SCSI Host and set up association between host,
- * local port, and fnic
- */
- lp = libfc_host_alloc(&fnic_host_template, sizeof(struct fnic));
- if (!lp) {
- printk(KERN_ERR PFX "Unable to alloc libfc local port\n");
+ fcport = fc_port_add(&pdev->dev, &fnic_fcport_fcn_tmpl,
+ &fnic_fcp_template, &fcoe_attrs);
+ if (!fcport) {
+ printk(KERN_ERR PFX "Unable to alloc fc port\n");
err = -ENOMEM;
goto err_out;
}
- host = lp->host;
+
+ fcvport = fc_vport_alloc(NULL, &ids,
+ &fnic_fcport_fcn_tmpl.fcvport_f,
+ 0, FC4_FCP_INITIATOR);
+ if (!fcvport) {
+ printk(KERN_ERR PFX "Unable to alloc fc vport\n");
+ err = -ENOMEM;
+ goto err_out_free_port;
+ }
+
+ lp = fc_vport_priv(fcvport);
fnic = lport_priv(lp);
- fnic->lport = lp;
- fnic->ctlr.lp = lp;
- snprintf(fnic->name, sizeof(fnic->name) - 1, "%s%d", DRV_NAME,
- host->host_no);
+ /* Configure the fcport */
+ fc_port_maxframe_size(fcport) = lp->mfs;
+ strncpy(fc_port_hbaapi_lib(fcport), LIBHBALINUX,
+ strlen(LIBHBALINUX));
- host->transportt = fnic_fc_transport;
+ /* Configure the fcvport */
+ fc_vport_port_type(fcvport) = FC_PORTTYPE_NPORT;
+ fc_vport_rport_dev_loss_tmo(fcvport) =
+ fnic->config.port_down_timeout / 1000;
- err = scsi_init_shared_tag_map(host, FNIC_MAX_IO_REQ);
- if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Unable to alloc shared tag map\n");
- goto err_out_free_hba;
- }
+ /* Configure the lport */
+ lp->fcport = fcport;
+ fc_lport_port_config(fcport);
+
+ /* Configure the fnic */
+ fnic->lport = lp;
+ fnic->ctlr.lp = lp;
+ snprintf(fnic->name, sizeof(fnic->name) - 1, "%s%d", DRV_NAME,
+ fcport->id);
/* Setup PCI resources */
pci_set_drvdata(pdev, fnic);
@@ -455,15 +534,15 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
err = pci_enable_device(pdev);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Cannot enable PCI device, aborting.\n");
- goto err_out_free_hba;
+ printk(KERN_ERR "fcport%u: Cannot enable PCI device, aborting.\n",
+ fc_port_id(fcport));
+ goto err_out_free_vport;
}
err = pci_request_regions(pdev, DRV_NAME);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Cannot enable PCI resources, aborting\n");
+ printk(KERN_ERR "fcport%u: Cannot enable PCI resources, aborting\n",
+ fc_port_id(fcport));
goto err_out_disable_device;
}
@@ -477,32 +556,31 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
if (err) {
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "No usable DMA configuration "
- "aborting\n");
+ printk(KERN_ERR "fcport%u: No usable DMA configuration "
+ "aborting\n", fc_port_id(fcport));
goto err_out_release_regions;
}
err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Unable to obtain 32-bit DMA "
- "for consistent allocations, aborting.\n");
+ printk(KERN_ERR "fcport%u: Unable to obtain 32-bit DMA "
+ "for consistent allocations, aborting.\n",
+ fc_port_id(fcport));
goto err_out_release_regions;
}
} else {
err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(40));
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Unable to obtain 40-bit DMA "
- "for consistent allocations, aborting.\n");
+ printk(KERN_ERR "fcport%u: Unable to obtain 40-bit DMA "
+ "for consistent allocations, aborting.\n",
+ fc_port_id(fcport));
goto err_out_release_regions;
}
}
/* Map vNIC resources from BAR0 */
if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "BAR0 not memory-map'able, aborting.\n");
+ printk(KERN_ERR "fcport%u: BAR0 not memory-map'able, aborting.\n",
+ fc_port_id(fcport));
err = -ENODEV;
goto err_out_release_regions;
}
@@ -512,18 +590,16 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
fnic->bar0.len = pci_resource_len(pdev, 0);
if (!fnic->bar0.vaddr) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Cannot memory-map BAR0 res hdr, "
- "aborting.\n");
+ printk(KERN_ERR "fcport%u: Cannot memory-map BAR0 res hdr, "
+ "aborting.\n", fc_port_id(fcport));
err = -ENODEV;
goto err_out_release_regions;
}
fnic->vdev = vnic_dev_register(NULL, fnic, pdev, &fnic->bar0);
if (!fnic->vdev) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "vNIC registration failed, "
- "aborting.\n");
+ printk(KERN_ERR "fcport%u: vNIC registration failed, "
+ "aborting.\n", fc_port_id(fcport));
err = -ENODEV;
goto err_out_iounmap;
}
@@ -531,22 +607,22 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
err = fnic_dev_wait(fnic->vdev, vnic_dev_open,
vnic_dev_open_done, 0);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "vNIC dev open failed, aborting.\n");
+ printk(KERN_ERR "fcport%u: vNIC dev open failed, aborting.\n",
+ fc_port_id(fcport));
goto err_out_vnic_unregister;
}
err = vnic_dev_init(fnic->vdev, 0);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "vNIC dev init failed, aborting.\n");
+ printk(KERN_ERR "fcport%u: vNIC dev init failed, aborting.\n",
+ fc_port_id(fcport));
goto err_out_dev_close;
}
err = vnic_dev_mac_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "vNIC get MAC addr failed \n");
+ printk(KERN_ERR "fcport%u: vNIC get MAC addr failed.\n",
+ fc_port_id(fcport));
goto err_out_dev_close;
}
/* set data_src for point-to-point mode and to keep it non-zero */
@@ -555,34 +631,27 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
/* Get vNIC configuration */
err = fnic_get_vnic_config(fnic);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Get vNIC configuration failed, "
- "aborting.\n");
+ printk(KERN_ERR "fcport%u: Get vNIC configuration failed, "
+ "aborting.\n", fc_port_id(fcport));
goto err_out_dev_close;
}
- host->max_lun = fnic->config.luns_per_tgt;
- host->max_id = FNIC_MAX_FCP_TARGET;
- host->max_cmd_len = FCOE_MAX_CMD_LEN;
fnic_get_res_counts(fnic);
err = fnic_set_intr_mode(fnic);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Failed to set intr mode, "
- "aborting.\n");
+ printk(KERN_ERR "fcport%u: Failed to set intr mode, "
+ "aborting.\n", fc_port_id(fcport));
goto err_out_dev_close;
}
err = fnic_alloc_vnic_resources(fnic);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Failed to alloc vNIC resources, "
- "aborting.\n");
+ printk(KERN_ERR "fcport%u: Failed to alloc vNIC resources, "
+ "aborting.\n", fc_port_id(fcport));
goto err_out_clear_intr;
}
-
/* initialize all fnic locks */
spin_lock_init(&fnic->fnic_lock);
@@ -624,16 +693,16 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
fnic->ctlr.update_mac = fnic_update_mac;
fnic->ctlr.get_src_addr = fnic_get_mac;
if (fnic->config.flags & VFCF_FIP_CAPABLE) {
- shost_printk(KERN_INFO, fnic->lport->host,
- "firmware supports FIP\n");
+ printk(KERN_INFO "fcport%u: firmware supports FIP\n",
+ fc_port_id(fcport));
/* enable directed and multicast */
vnic_dev_packet_filter(fnic->vdev, 1, 1, 0, 0, 0);
vnic_dev_add_addr(fnic->vdev, FIP_ALL_ENODE_MACS);
vnic_dev_add_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_AUTO);
} else {
- shost_printk(KERN_INFO, fnic->lport->host,
- "firmware uses non-FIP mode\n");
+ printk(KERN_INFO "fcport%u: firmware uses non-FIP mode\n",
+ fc_port_id(fcport));
fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_NON_FIP);
}
fnic->state = FNIC_IN_FC_MODE;
@@ -644,8 +713,8 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
/* Setup notification buffer area */
err = fnic_notify_set(fnic);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Failed to alloc notify buffer, aborting.\n");
+ printk(KERN_ERR "fcport%u: Failed to alloc notify "
+ "buffer, aborting.\n", fc_port_id(fcport));
goto err_out_free_max_pool;
}
@@ -658,24 +727,12 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
for (i = 0; i < fnic->rq_count; i++) {
err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "fnic_alloc_rq_frame can't alloc "
- "frame\n");
+ printk(KERN_ERR "fcport%u: fnic_alloc_rq_frame "
+ "can't alloc frame\n", fc_port_id(fcport));
goto err_out_free_rq_buf;
}
}
- /*
- * Initialization done with PCI system, hardware, firmware.
- * Add host to SCSI
- */
- err = scsi_add_host(lp->host, &pdev->dev);
- if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "fnic: scsi_add_host failed...exiting\n");
- goto err_out_free_rq_buf;
- }
-
/* Start local port initiatialization */
lp->link_up = 0;
@@ -690,16 +747,16 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
lp->boot_time = jiffies;
lp->e_d_tov = fnic->config.ed_tov;
lp->r_a_tov = fnic->config.ra_tov;
- lp->link_supported_speeds = FC_PORTSPEED_10GBIT;
+ fc_port_supported_speeds(fcport) = FC_PORTSPEED_10GBIT;
fc_set_wwnn(lp, fnic->config.node_wwn);
fc_set_wwpn(lp, fnic->config.port_wwn);
- fcoe_libfc_config(lp, &fnic->ctlr, &fnic_transport_template, 0);
+ fcoe_libfc_config(lp, &fnic->ctlr, &fnic_transport_template);
if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, FCPIO_HOST_EXCH_RANGE_START,
FCPIO_HOST_EXCH_RANGE_END, NULL)) {
err = -ENOMEM;
- goto err_out_remove_scsi_host;
+ goto err_out_free_rq_buf;
}
fc_lport_init_stats(lp);
@@ -711,10 +768,8 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
err = -EINVAL;
goto err_out_free_exch_mgr;
}
- fc_host_maxframe_size(lp->host) = lp->mfs;
- fc_host_dev_loss_tmo(lp->host) = fnic->config.port_down_timeout / 1000;
- sprintf(fc_host_symbolic_name(lp->host),
+ sprintf(fc_vport_symbolic_name(fcvport),
DRV_NAME " v" DRV_VERSION " over %s", fnic->name);
spin_lock_irqsave(&fnic_list_lock, flags);
@@ -740,8 +795,8 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
err = fnic_request_intr(fnic);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Unable to request irq.\n");
+ printk(KERN_ERR "fcport%u: Unable to request irq.\n",
+ fc_port_id(fcport));
goto err_out_free_exch_mgr;
}
@@ -754,9 +809,6 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
err_out_free_exch_mgr:
fc_exch_mgr_free(lp);
-err_out_remove_scsi_host:
- fc_remove_host(lp->host);
- scsi_remove_host(lp->host);
err_out_free_rq_buf:
for (i = 0; i < fnic->rq_count; i++)
vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf);
@@ -781,8 +833,10 @@ err_out_release_regions:
pci_release_regions(pdev);
err_out_disable_device:
pci_disable_device(pdev);
-err_out_free_hba:
- scsi_host_put(lp->host);
+err_out_free_vport:
+ fc_vport_free(fcvport);
+err_out_free_port:
+ fc_port_del(fcport);
err_out:
return err;
}
@@ -791,6 +845,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
{
struct fnic *fnic = pci_get_drvdata(pdev);
struct fc_lport *lp = fnic->lport;
+ struct fc_port *fcport = lp->fcport;
unsigned long flags;
/*
@@ -842,8 +897,6 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
list_del(&fnic->list);
spin_unlock_irqrestore(&fnic_list_lock, flags);
- fc_remove_host(fnic->lport->host);
- scsi_remove_host(fnic->lport->host);
fc_exch_mgr_free(fnic->lport);
vnic_dev_notify_unset(fnic->vdev);
fnic_free_intr(fnic);
@@ -855,7 +908,8 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
- scsi_host_put(lp->host);
+ fc_vport_free(lport_to_fcvport(lp));
+ fc_port_del(fcport);
}
static struct pci_driver fnic_driver = {
@@ -916,13 +970,6 @@ static int __init fnic_init_module(void)
spin_lock_init(&fnic_list_lock);
INIT_LIST_HEAD(&fnic_list);
- fnic_fc_transport = fc_attach_transport(&fnic_fc_functions);
- if (!fnic_fc_transport) {
- printk(KERN_ERR PFX "fc_attach_transport error\n");
- err = -ENOMEM;
- goto err_fc_transport;
- }
-
/* register the driver with PCI system */
err = pci_register_driver(&fnic_driver);
if (err < 0) {
@@ -932,8 +979,6 @@ static int __init fnic_init_module(void)
return err;
err_pci_register:
- fc_release_transport(fnic_fc_transport);
-err_fc_transport:
destroy_workqueue(fnic_event_queue);
err_create_fnic_workq:
kmem_cache_destroy(fnic_io_req_cache);
@@ -952,7 +997,6 @@ static void __exit fnic_cleanup_module(void)
kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
kmem_cache_destroy(fnic_io_req_cache);
- fc_release_transport(fnic_fc_transport);
}
module_init(fnic_init_module);
diff --git a/drivers/scsi/fnic/fnic_res.c b/drivers/scsi/fnic/fnic_res.c
index 50488f8..db60467 100644
--- a/drivers/scsi/fnic/fnic_res.c
+++ b/drivers/scsi/fnic/fnic_res.c
@@ -36,17 +36,17 @@ int fnic_get_vnic_config(struct fnic *fnic)
struct vnic_fc_config *c = &fnic->config;
int err;
-#define GET_CONFIG(m) \
- do { \
- err = vnic_dev_spec(fnic->vdev, \
+#define GET_CONFIG(m) \
+ do { \
+ err = vnic_dev_spec(fnic->vdev, \
offsetof(struct vnic_fc_config, m), \
- sizeof(c->m), &c->m); \
- if (err) { \
- shost_printk(KERN_ERR, fnic->lport->host, \
- "Error getting %s, %d\n", #m, \
- err); \
- return err; \
- } \
+ sizeof(c->m), &c->m); \
+ if (err) { \
+ shost_printk(KERN_ERR, fnic->lport->fcpinit->shost, \
+ "Error getting %s, %d\n", #m, \
+ err); \
+ return err; \
+ } \
} while (0);
GET_CONFIG(node_wwn);
@@ -143,34 +143,34 @@ int fnic_get_vnic_config(struct fnic *fnic)
c->intr_timer = min_t(u16, VNIC_INTR_TIMER_MAX, c->intr_timer);
c->intr_timer_type = c->intr_timer_type;
- shost_printk(KERN_INFO, fnic->lport->host,
+ shost_printk(KERN_INFO, fnic->lport->fcpinit->shost,
"vNIC MAC addr %pM "
"wq/wq_copy/rq %d/%d/%d\n",
fnic->ctlr.ctl_src_addr,
c->wq_enet_desc_count, c->wq_copy_desc_count,
c->rq_desc_count);
- shost_printk(KERN_INFO, fnic->lport->host,
+ shost_printk(KERN_INFO, fnic->lport->fcpinit->shost,
"vNIC node wwn %llx port wwn %llx\n",
c->node_wwn, c->port_wwn);
- shost_printk(KERN_INFO, fnic->lport->host,
+ shost_printk(KERN_INFO, fnic->lport->fcpinit->shost,
"vNIC ed_tov %d ra_tov %d\n",
c->ed_tov, c->ra_tov);
- shost_printk(KERN_INFO, fnic->lport->host,
+ shost_printk(KERN_INFO, fnic->lport->fcpinit->shost,
"vNIC mtu %d intr timer %d\n",
c->maxdatafieldsize, c->intr_timer);
- shost_printk(KERN_INFO, fnic->lport->host,
+ shost_printk(KERN_INFO, fnic->lport->fcpinit->shost,
"vNIC flags 0x%x luns per tgt %d\n",
c->flags, c->luns_per_tgt);
- shost_printk(KERN_INFO, fnic->lport->host,
+ shost_printk(KERN_INFO, fnic->lport->fcpinit->shost,
"vNIC flogi_retries %d flogi timeout %d\n",
c->flogi_retries, c->flogi_timeout);
- shost_printk(KERN_INFO, fnic->lport->host,
+ shost_printk(KERN_INFO, fnic->lport->fcpinit->shost,
"vNIC plogi retries %d plogi timeout %d\n",
c->plogi_retries, c->plogi_timeout);
- shost_printk(KERN_INFO, fnic->lport->host,
+ shost_printk(KERN_INFO, fnic->lport->fcpinit->shost,
"vNIC io throttle count %d link dn timeout %d\n",
c->io_throttle_count, c->link_down_timeout);
- shost_printk(KERN_INFO, fnic->lport->host,
+ shost_printk(KERN_INFO, fnic->lport->fcpinit->shost,
"vNIC port dn io retries %d port dn timeout %d\n",
c->port_down_io_retries, c->port_down_timeout);
@@ -229,6 +229,7 @@ void fnic_free_vnic_resources(struct fnic *fnic)
int fnic_alloc_vnic_resources(struct fnic *fnic)
{
+ struct Scsi_Host *shost = fnic->lport->fcpinit->shost;
enum vnic_dev_intr_mode intr_mode;
unsigned int mask_on_assertion;
unsigned int interrupt_offset;
@@ -240,13 +241,13 @@ int fnic_alloc_vnic_resources(struct fnic *fnic)
intr_mode = vnic_dev_get_intr_mode(fnic->vdev);
- shost_printk(KERN_INFO, fnic->lport->host, "vNIC interrupt mode: %s\n",
+ shost_printk(KERN_INFO, shost, "vNIC interrupt mode: %s\n",
intr_mode == VNIC_DEV_INTR_MODE_INTX ? "legacy PCI INTx" :
intr_mode == VNIC_DEV_INTR_MODE_MSI ? "MSI" :
intr_mode == VNIC_DEV_INTR_MODE_MSIX ?
"MSI-X" : "unknown");
- shost_printk(KERN_INFO, fnic->lport->host, "vNIC resources avail: "
+ shost_printk(KERN_INFO, shost, "vNIC resources avail: "
"wq %d cp_wq %d raw_wq %d rq %d cq %d intr %d\n",
fnic->wq_count, fnic->wq_copy_count, fnic->raw_wq_count,
fnic->rq_count, fnic->cq_count, fnic->intr_count);
@@ -322,7 +323,7 @@ int fnic_alloc_vnic_resources(struct fnic *fnic)
RES_TYPE_INTR_PBA_LEGACY, 0);
if (!fnic->legacy_pba && intr_mode == VNIC_DEV_INTR_MODE_INTX) {
- shost_printk(KERN_ERR, fnic->lport->host,
+ shost_printk(KERN_ERR, shost,
"Failed to hook legacy pba resource\n");
err = -ENODEV;
goto err_out_cleanup;
@@ -426,7 +427,7 @@ int fnic_alloc_vnic_resources(struct fnic *fnic)
/* init the stats memory by making the first call here */
err = vnic_dev_stats_dump(fnic->vdev, &fnic->stats);
if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
+ shost_printk(KERN_ERR, shost,
"vnic_dev_stats_dump failed - x%x\n", err);
goto err_out_cleanup;
}
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
index 22d0240..0c6b77a 100644
--- a/drivers/scsi/fnic/fnic_scsi.c
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -191,10 +191,10 @@ int fnic_fw_reset_handler(struct fnic *fnic)
spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
if (!ret)
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
"Issued fw reset\n");
else
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
"Failed to issue fw reset\n");
return ret;
}
@@ -236,13 +236,13 @@ int fnic_flogi_reg_handler(struct fnic *fnic, u32 fc_id)
fc_id, gw_mac,
fnic->data_src_addr,
lp->r_a_tov, lp->e_d_tov);
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
"FLOGI FIP reg issued fcid %x src %pM dest %pM\n",
fc_id, fnic->data_src_addr, gw_mac);
} else {
fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG,
format, fc_id, gw_mac);
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
"FLOGI reg issued fcid %x map %d dest %pM\n",
fc_id, fnic->ctlr.map_dest, gw_mac);
}
@@ -262,8 +262,10 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic,
struct scsi_cmnd *sc,
int sg_count)
{
+ struct scsi_target *starget = scsi_target(sc->device);
+ struct fcp_targ *fcptarg = starget_to_fcptarg(starget);
+ struct fc_rport *rport = fcptarg->rport;
struct scatterlist *sg;
- struct fc_rport *rport = starget_to_rport(scsi_target(sc->device));
struct fc_rport_libfc_priv *rp = rport->dd_data;
struct host_sg_desc *desc;
u8 pri_tag = 0;
@@ -351,8 +353,12 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic,
*/
static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
{
- struct fc_lport *lp;
- struct fc_rport *rport;
+ struct scsi_target *starget = scsi_target(sc->device);
+ struct fcp_targ *fcptarg = starget_to_fcptarg(starget);
+ struct fc_rport *rport = fcptarg->rport;
+ struct fcp_init *fcpinit = shost_priv(sc->device->host);
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
+ struct fc_lport *lp = si->lport;
struct fnic_io_req *io_req;
struct fnic *fnic;
struct vnic_wq_copy *wq;
@@ -361,7 +367,6 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_
unsigned long flags;
unsigned long ptr;
- rport = starget_to_rport(scsi_target(sc->device));
ret = fc_remote_port_chkready(rport);
if (ret) {
sc->result = ret;
@@ -378,7 +383,7 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_
* Don't re-enable interrupts in case they were disabled prior to the
* caller disabling them.
*/
- spin_unlock(lp->host->host_lock);
+ spin_unlock(lp->fcpinit->shost->host_lock);
/* Get a new io_req for this SCSI IO */
fnic = lport_priv(lp);
@@ -453,7 +458,7 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_
}
out:
/* acquire host lock before returning to SCSI */
- spin_lock(lp->host->host_lock);
+ spin_lock(lp->fcpinit->shost->host_lock);
return ret;
}
@@ -483,13 +488,13 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE) {
/* Check status of reset completion */
if (!hdr_status) {
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
"reset cmpl success\n");
/* Ready to send flogi out */
fnic->state = FNIC_IN_ETH_MODE;
} else {
FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host,
+ fnic->lport->fcpinit->shost,
"fnic fw_reset : failed %s\n",
fnic_fcpio_status_to_str(hdr_status));
@@ -504,7 +509,7 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
}
} else {
FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host,
+ fnic->lport->fcpinit->shost,
"Unexpected state %s while processing"
" reset cmpl\n", fnic_state_to_str(fnic->state));
ret = -1;
@@ -554,19 +559,19 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
/* Check flogi registration completion status */
if (!hdr_status) {
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
"flog reg succeeded\n");
fnic->state = FNIC_IN_FC_MODE;
} else {
FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host,
+ fnic->lport->fcpinit->shost,
"fnic flogi reg :failed %s\n",
fnic_fcpio_status_to_str(hdr_status));
fnic->state = FNIC_IN_ETH_MODE;
ret = -1;
}
} else {
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
"Unexpected fnic state %s while"
" processing flogi reg completion\n",
fnic_state_to_str(fnic->state));
@@ -639,8 +644,11 @@ static inline void fnic_fcpio_ack_handler(struct fnic *fnic,
* Routine to handle icmnd completions
*/
static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
- struct fcpio_fw_req *desc)
+ struct fcpio_fw_req *desc)
{
+ struct fc_lport *lport = fnic->lport;
+ struct fcp_init *fcpinit = lport->fcpinit;
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
u8 type;
u8 hdr_status;
struct fcpio_tag tag;
@@ -659,7 +667,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
if (id >= FNIC_MAX_IO_REQ)
return;
- sc = scsi_host_find_tag(fnic->lport->host, id);
+ sc = scsi_host_find_tag(si->host, id);
WARN_ON_ONCE(!sc);
if (!sc)
return;
@@ -720,7 +728,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
if (qd == -1)
qd = t_sdev->host->cmd_per_lun;
shost_printk(KERN_INFO,
- fnic->lport->host,
+ si->host,
"scsi[%d:%d:%d:%d"
"] queue full detected,"
"new depth = %d\n",
@@ -757,7 +765,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
case FCPIO_MSS_INVALID: /* request was aborted due to mss error */
case FCPIO_FW_ERR: /* request was terminated due fw error */
default:
- shost_printk(KERN_ERR, fnic->lport->host, "hdr status = %s\n",
+ shost_printk(KERN_ERR, si->host, "hdr status = %s\n",
fnic_fcpio_status_to_str(hdr_status));
sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
break;
@@ -773,13 +781,13 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
mempool_free(io_req, fnic->io_req_pool);
if (sc->sc_data_direction == DMA_FROM_DEVICE) {
- fnic->lport->host_stats.fcp_input_requests++;
+ si->fcpinit_stats.fcp_input_requests++;
fnic->fcp_input_bytes += xfer_len;
} else if (sc->sc_data_direction == DMA_TO_DEVICE) {
- fnic->lport->host_stats.fcp_output_requests++;
+ si->fcpinit_stats.fcp_output_requests++;
fnic->fcp_output_bytes += xfer_len;
} else
- fnic->lport->host_stats.fcp_control_requests++;
+ si->fcpinit_stats.fcp_control_requests++;
/* Call SCSI completion function to complete the IO */
if (sc->scsi_done)
@@ -791,8 +799,9 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
* Routine to handle itmf completions
*/
static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
- struct fcpio_fw_req *desc)
+ struct fcpio_fw_req *desc)
{
+ struct Scsi_Host *shost = fnic->lport->fcpinit->shost;
u8 type;
u8 hdr_status;
struct fcpio_tag tag;
@@ -808,7 +817,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
if ((id & FNIC_TAG_MASK) >= FNIC_MAX_IO_REQ)
return;
- sc = scsi_host_find_tag(fnic->lport->host, id & FNIC_TAG_MASK);
+ sc = scsi_host_find_tag(shost, id & FNIC_TAG_MASK);
WARN_ON_ONCE(!sc);
if (!sc)
return;
@@ -832,7 +841,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
CMD_STATE(sc) = FNIC_IOREQ_ABTS_COMPLETE;
CMD_ABTS_STATUS(sc) = hdr_status;
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, shost,
"abts cmpl recd. id %d status %s\n",
(int)(id & FNIC_TAG_MASK),
fnic_fcpio_status_to_str(hdr_status));
@@ -846,7 +855,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
complete(io_req->abts_done);
spin_unlock_irqrestore(io_lock, flags);
} else {
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, shost,
"abts cmpl, completing IO\n");
CMD_SP(sc) = NULL;
sc->result = (DID_ERROR << 16);
@@ -863,7 +872,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
/* Completion of device reset */
CMD_LR_STATUS(sc) = hdr_status;
CMD_STATE(sc) = FNIC_IOREQ_CMD_COMPLETE;
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, shost,
"dev reset cmpl recd. id %d status %s\n",
(int)(id & FNIC_TAG_MASK),
fnic_fcpio_status_to_str(hdr_status));
@@ -872,7 +881,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
spin_unlock_irqrestore(io_lock, flags);
} else {
- shost_printk(KERN_ERR, fnic->lport->host,
+ shost_printk(KERN_ERR, shost,
"Unexpected itmf io state %s tag %x\n",
fnic_ioreq_state_to_str(CMD_STATE(sc)), id);
spin_unlock_irqrestore(io_lock, flags);
@@ -914,7 +923,7 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev,
break;
default:
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
"firmware completion type %d\n",
desc->hdr.type);
break;
@@ -955,7 +964,7 @@ static void fnic_cleanup_io(struct fnic *fnic, int exclude_id)
if (i == exclude_id)
continue;
- sc = scsi_host_find_tag(fnic->lport->host, i);
+ sc = scsi_host_find_tag(fnic->lport->fcpinit->shost, i);
if (!sc)
continue;
@@ -980,8 +989,8 @@ static void fnic_cleanup_io(struct fnic *fnic, int exclude_id)
cleanup_scsi_cmd:
sc->result = DID_TRANSPORT_DISRUPTED << 16;
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "fnic_cleanup_io:"
- " DID_TRANSPORT_DISRUPTED\n");
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
+ "fnic_cleanup_io: DID_TRANSPORT_DISRUPTED\n");
/* Complete the command to SCSI */
if (sc->scsi_done)
@@ -1006,7 +1015,7 @@ void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
if (id >= FNIC_MAX_IO_REQ)
return;
- sc = scsi_host_find_tag(fnic->lport->host, id);
+ sc = scsi_host_find_tag(fnic->lport->fcpinit->shost, id);
if (!sc)
return;
@@ -1032,8 +1041,8 @@ void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
wq_copy_cleanup_scsi_cmd:
sc->result = DID_NO_CONNECT << 16;
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "wq_copy_cleanup_handler:"
- " DID_NO_CONNECT\n");
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
+ "wq_copy_cleanup_handler: DID_NO_CONNECT\n");
if (sc->scsi_done)
sc->scsi_done(sc);
@@ -1074,7 +1083,7 @@ void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id)
enum fnic_ioreq_state old_ioreq_state;
FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host,
+ fnic->lport->fcpinit->shost,
"fnic_rport_reset_exch called portid 0x%06x\n",
port_id);
@@ -1082,7 +1091,7 @@ void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id)
return;
for (tag = 0; tag < FNIC_MAX_IO_REQ; tag++) {
- sc = scsi_host_find_tag(fnic->lport->host, tag);
+ sc = scsi_host_find_tag(fnic->lport->fcpinit->shost, tag);
if (!sc)
continue;
@@ -1110,7 +1119,7 @@ void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id)
BUG_ON(io_req->abts_done);
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
"fnic_rport_reset_exch: Issuing abts\n");
spin_unlock_irqrestore(io_lock, flags);
@@ -1140,20 +1149,24 @@ void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id)
void fnic_terminate_rport_io(struct fc_rport *rport)
{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct fcp_init *fcpinit = lport->fcpinit;
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
+ struct fnic *fnic = lport_priv(lport);
+ struct scsi_target *cmd_starget;
+ struct fcp_targ *cmd_fcptarg;
+ struct fc_rport *cmd_rport;
int tag;
struct fnic_io_req *io_req;
spinlock_t *io_lock;
unsigned long flags;
struct scsi_cmnd *sc;
struct scsi_lun fc_lun;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_lport *lport = rdata->local_port;
- struct fnic *fnic = lport_priv(lport);
- struct fc_rport *cmd_rport;
enum fnic_ioreq_state old_ioreq_state;
- FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host, "fnic_terminate_rport_io called"
+ FNIC_SCSI_DBG(KERN_DEBUG, si->host,
+ "fnic_terminate_rport_io called"
" wwpn 0x%llx, wwnn0x%llx, portid 0x%06x\n",
rport->port_name, rport->node_name,
rport->port_id);
@@ -1162,11 +1175,13 @@ void fnic_terminate_rport_io(struct fc_rport *rport)
return;
for (tag = 0; tag < FNIC_MAX_IO_REQ; tag++) {
- sc = scsi_host_find_tag(fnic->lport->host, tag);
+ sc = scsi_host_find_tag(si->host, tag);
if (!sc)
continue;
- cmd_rport = starget_to_rport(scsi_target(sc->device));
+ cmd_starget = scsi_target(sc->device);
+ cmd_fcptarg = starget_to_fcptarg(cmd_starget);
+ cmd_rport = cmd_fcptarg->rport;
if (rport != cmd_rport)
continue;
@@ -1195,7 +1210,7 @@ void fnic_terminate_rport_io(struct fc_rport *rport)
BUG_ON(io_req->abts_done);
FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host,
+ fnic->lport->fcpinit->shost,
"fnic_terminate_rport_io: Issuing abts\n");
spin_unlock_irqrestore(io_lock, flags);
@@ -1230,10 +1245,14 @@ void fnic_terminate_rport_io(struct fc_rport *rport)
*/
int fnic_abort_cmd(struct scsi_cmnd *sc)
{
- struct fc_lport *lp;
- struct fnic *fnic;
+ struct scsi_target *starget = scsi_target(sc->device);
+ struct fcp_targ *fcptarg = starget_to_fcptarg(starget);
+ struct fc_rport *rport = fcptarg->rport;
+ struct fcp_init *fcpinit = shost_priv(sc->device->host);
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
+ struct fc_lport *lp = si->lport;
+ struct fnic *fnic = lport_priv(lp);
struct fnic_io_req *io_req;
- struct fc_rport *rport;
spinlock_t *io_lock;
unsigned long flags;
int ret = SUCCESS;
@@ -1242,16 +1261,11 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
DECLARE_COMPLETION_ONSTACK(tm_done);
/* Wait for rport to unblock */
- fc_block_scsi_eh(sc);
-
- /* Get local-port, check ready and link up */
- lp = shost_priv(sc->device->host);
+ fcp_block_scsi_eh(sc);
- fnic = lport_priv(lp);
- rport = starget_to_rport(scsi_target(sc->device));
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "Abort Cmd called FCID 0x%x, LUN 0x%x TAG %d\n",
- rport->port_id, sc->device->lun, sc->request->tag);
+ FNIC_SCSI_DBG(KERN_DEBUG, si->host,
+ "Abort Cmd called FCID 0x%x, LUN 0x%x TAG %d\n",
+ rport->port_id, sc->device->lun, sc->request->tag);
if (lp->state != LPORT_ST_READY || !(lp->link_up)) {
ret = FAILED;
@@ -1363,7 +1377,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
mempool_free(io_req, fnic->io_req_pool);
fnic_abort_cmd_end:
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, si->host,
"Returning from abort cmd %s\n",
(ret == SUCCESS) ?
"SUCCESS" : "FAILED");
@@ -1423,7 +1437,7 @@ static int fnic_clean_pending_aborts(struct fnic *fnic,
DECLARE_COMPLETION_ONSTACK(tm_done);
for (tag = 0; tag < FNIC_MAX_IO_REQ; tag++) {
- sc = scsi_host_find_tag(fnic->lport->host, tag);
+ sc = scsi_host_find_tag(fnic->lport->fcpinit->shost, tag);
/*
* ignore this lun reset cmd or cmds that do not belong to
* this lun
@@ -1445,7 +1459,7 @@ static int fnic_clean_pending_aborts(struct fnic *fnic,
* Found IO that is still pending with firmware and
* belongs to the LUN that we are resetting
*/
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
"Found IO in %s on lun\n",
fnic_ioreq_state_to_str(CMD_STATE(sc)));
@@ -1509,10 +1523,14 @@ clean_pending_aborts_end:
*/
int fnic_device_reset(struct scsi_cmnd *sc)
{
- struct fc_lport *lp;
+ struct scsi_target *starget = scsi_target(sc->device);
+ struct fcp_targ *fcptarg = starget_to_fcptarg(starget);
+ struct fc_rport *rport = fcptarg->rport;
+ struct fcp_init *fcpinit = shost_priv(sc->device->host);
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
+ struct fc_lport *lp = si->lport;
struct fnic *fnic;
struct fnic_io_req *io_req;
- struct fc_rport *rport;
int status;
int ret = FAILED;
spinlock_t *io_lock;
@@ -1520,17 +1538,13 @@ int fnic_device_reset(struct scsi_cmnd *sc)
DECLARE_COMPLETION_ONSTACK(tm_done);
/* Wait for rport to unblock */
- fc_block_scsi_eh(sc);
-
- /* Get local-port, check ready and link up */
- lp = shost_priv(sc->device->host);
+ fcp_block_scsi_eh(sc);
fnic = lport_priv(lp);
- rport = starget_to_rport(scsi_target(sc->device));
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "Device reset called FCID 0x%x, LUN 0x%x\n",
- rport->port_id, sc->device->lun);
+ FNIC_SCSI_DBG(KERN_DEBUG, si->host,
+ "Device reset called FCID 0x%x, LUN 0x%x\n",
+ rport->port_id, sc->device->lun);
if (lp->state != LPORT_ST_READY || !(lp->link_up))
goto fnic_device_reset_end;
@@ -1562,7 +1576,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
CMD_LR_STATUS(sc) = FCPIO_INVALID_CODE;
spin_unlock_irqrestore(io_lock, flags);
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "TAG %d\n",
+ FNIC_SCSI_DBG(KERN_DEBUG, si->host, "TAG %d\n",
sc->request->tag);
/*
@@ -1600,7 +1614,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
* gets cleaned up during higher levels of EH
*/
if (status == FCPIO_INVALID_CODE) {
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, si->host,
"Device reset timed out\n");
goto fnic_device_reset_end;
}
@@ -1608,8 +1622,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
/* Completed, but not successful, clean up the io_req, return fail */
if (status != FCPIO_SUCCESS) {
spin_lock_irqsave(io_lock, flags);
- FNIC_SCSI_DBG(KERN_DEBUG,
- fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, si->host,
"Device reset completed - failed\n");
io_req = (struct fnic_io_req *)CMD_SP(sc);
goto fnic_device_reset_clean;
@@ -1625,7 +1638,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
if (fnic_clean_pending_aborts(fnic, sc)) {
spin_lock_irqsave(io_lock, flags);
io_req = (struct fnic_io_req *)CMD_SP(sc);
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, si->host,
"Device reset failed"
" since could not abort all IOs\n");
goto fnic_device_reset_clean;
@@ -1650,7 +1663,7 @@ fnic_device_reset_clean:
}
fnic_device_reset_end:
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, si->host,
"Returning from device reset %s\n",
(ret == SUCCESS) ?
"SUCCESS" : "FAILED");
@@ -1658,16 +1671,12 @@ fnic_device_reset_end:
}
/* Clean up all IOs, clean up libFC local port */
-int fnic_reset(struct Scsi_Host *shost)
+int fnic_fcvport_reset(struct fc_vport *fcvport)
{
- struct fc_lport *lp;
- struct fnic *fnic;
+ struct fc_lport *lp = fc_vport_priv(fcvport);
int ret = SUCCESS;
- lp = shost_priv(shost);
- fnic = lport_priv(lp);
-
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, lp->fcpinit->shost,
"fnic_reset called\n");
/*
@@ -1677,7 +1686,7 @@ int fnic_reset(struct Scsi_Host *shost)
if (lp->tt.lport_reset(lp))
ret = FAILED;
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, lp->fcpinit->shost,
"Returning from fnic reset %s\n",
(ret == SUCCESS) ?
"SUCCESS" : "FAILED");
@@ -1696,17 +1705,19 @@ int fnic_reset(struct Scsi_Host *shost)
*/
int fnic_host_reset(struct scsi_cmnd *sc)
{
+ struct fcp_init *fcpinit = shost_priv(sc->device->host);
+ struct fc_fcp_internal *si = fcp_init_priv(fcpinit);
+ struct fc_lport *lp = si->lport;
+ struct fc_vport *fcvport = lport_to_fcvport(lp);
int ret;
unsigned long wait_host_tmo;
- struct Scsi_Host *shost = sc->device->host;
- struct fc_lport *lp = shost_priv(shost);
/*
* If fnic_reset is successful, wait for fabric login to complete
* scsi-ml tries to send a TUR to every device if host reset is
* successful, so before returning to scsi, fabric should be up
*/
- ret = fnic_reset(shost);
+ ret = fnic_fcvport_reset(fcvport);
if (ret == SUCCESS) {
wait_host_tmo = jiffies + FNIC_HOST_RESET_SETTLE_TIME * HZ;
ret = FAILED;
@@ -1758,7 +1769,7 @@ void fnic_scsi_abort_io(struct fc_lport *lp)
spin_lock_irqsave(&fnic->fnic_lock, flags);
fnic->remove_wait = NULL;
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->fcpinit->shost,
"fnic_scsi_abort_io %s\n",
(fnic->state == FNIC_IN_ETH_MODE) ?
"SUCCESS" : "FAILED");
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v2 09/10] fc: Add a hbaapi_lib attribute to the fcport structure
2011-03-11 21:54 [RFC PATCH v2 00/10] FC Sysfs and FCoE attributes Robert Love
` (7 preceding siblings ...)
2011-03-11 21:55 ` [RFC PATCH v2 08/10] fnic: Convert to new FC Sysfs infrastructure Robert Love
@ 2011-03-11 21:55 ` Robert Love
2011-03-11 21:55 ` [RFC PATCH v2 10/10] Documentation: Add fc_sysfs document Robert Love
9 siblings, 0 replies; 11+ messages in thread
From: Robert Love @ 2011-03-11 21:55 UTC (permalink / raw)
To: linux-scsi; +Cc: abjoglek, giridhar.malavali, james.smart, bprakash
Currently, libhbalinux, a LGPL implementation of the
HBAAPI interface relies on the sub-string " over " in
the host's symbolic_name to determine if the library
is responsible for the driver. This is obviously error
prone, so this patch adds a new attribute so the LLD
can specify it's HBAAPI library.
Since both fcoe and fnic use libhbalinux I've update
those drivers to use this new attribute.
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
drivers/fc/fcport.c | 2 ++
drivers/scsi/fcoe/fcoe.c | 4 ++++
include/fc/fc.h | 10 +++++++++-
3 files changed, 15 insertions(+), 1 deletions(-)
diff --git a/drivers/fc/fcport.c b/drivers/fc/fcport.c
index 299f220..2a6cd5d 100644
--- a/drivers/fc/fcport.c
+++ b/drivers/fc/fcport.c
@@ -113,6 +113,7 @@ static FC_DEVICE_ATTR(fcport, speed, S_IRUGO, show_port_speed, NULL);
fc_port_rd_attr(maxframe_size, "%u bytes\n", 20);
fc_port_rd_attr(serial_number, "%s\n", (FC_SERIAL_NUMBER_SIZE + 1));
+fc_port_rd_attr(hbaapi_lib, "%s\n", (FC_HBAAPI_LIB_SIZE + 1));
/* used by supported_speeds */
static ssize_t show_port_supported_speeds(struct device *dev,
@@ -312,6 +313,7 @@ struct fc_port *fc_port_add(struct device *pdev,
FC_SETUP_COND_ATTR_RD(fcport, supported_classes);
FC_SETUP_COND_ATTR_RD(fcport, serial_number);
FC_SETUP_COND_ATTR_RD(fcport, speed);
+ FC_SETUP_COND_ATTR_RD(fcport, hbaapi_lib);
BUG_ON(count > FC_PORT_NUM_ATTRS);
FC_CREATE_ATTRS(fcport, count);
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 8603b8c..230f047 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -195,6 +195,7 @@ struct fc_port_function_template fcoe_fcport_fcn_tmpl = {
.show_fcport_supported_fc4s = 1,
.show_fcport_active_fc4s = 1,
.show_fcport_serial_number = 1,
+ .show_fcport_hbaapi_lib = 1,
.dd_fcport_size = sizeof(struct fcoe_interface),
@@ -442,6 +443,9 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
goto out;
}
+ strncpy(fc_port_hbaapi_lib(fcport), LIBHBALINUX,
+ strlen(LIBHBALINUX));
+
fcoe = fc_port_priv(fcport);
dev_hold(netdev);
diff --git a/include/fc/fc.h b/include/fc/fc.h
index 6158095..270be37 100644
--- a/include/fc/fc.h
+++ b/include/fc/fc.h
@@ -359,7 +359,7 @@ struct _fc_cos_names {
#define dev_to_fcrport(d) \
container_of((d), struct fc_rport, dev)
-#define FC_PORT_NUM_ATTRS 9
+#define FC_PORT_NUM_ATTRS 10
#define FC_VPORT_NUM_ATTRS 16
#define FC_RPORT_NUM_ATTRS 9
#define FC_FABRIC_NUM_ATTRS 6
@@ -591,6 +591,9 @@ static inline void *fc_fabric_priv(const struct fc_fabric *fcfabric)
#define fcfabric_to_fcport(x) \
dev_to_fcport((x)->dev.parent)
+#define FC_HBAAPI_LIB_SIZE 80
+#define LIBHBALINUX "libhbalinux"
+
struct fc_port_function_template {
void (*get_fcport_speed)(struct fc_port *);
void (*get_fcport_active_fc4s)(struct fc_port *);
@@ -605,6 +608,7 @@ struct fc_port_function_template {
unsigned long show_fcport_active_fc4s:1;
unsigned long show_fcport_supported_classes:1;
unsigned long show_fcport_serial_number:1;
+ unsigned long show_fcport_hbaapi_lib:1;
u32 dd_fcport_size;
@@ -640,6 +644,8 @@ struct fc_port {
u32 maxframe_size;
u32 supported_classes;
char serial_number[FC_SERIAL_NUMBER_SIZE];
+ char hbaapi_lib[FC_HBAAPI_LIB_SIZE];
+
/* Dynamic Attributes*/
u8 active_fc4s[FC_FC4_LIST_SIZE];
@@ -690,6 +696,8 @@ static inline void *fc_port_priv(const struct fc_port *fcport)
((x)->supported_classes)
#define fc_port_serial_number(x) \
((x)->serial_number)
+#define fc_port_hbaapi_lib(x) \
+ ((x)->hbaapi_lib)
/*
* FC Virtual Port Attributes
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH v2 10/10] Documentation: Add fc_sysfs document
2011-03-11 21:54 [RFC PATCH v2 00/10] FC Sysfs and FCoE attributes Robert Love
` (8 preceding siblings ...)
2011-03-11 21:55 ` [RFC PATCH v2 09/10] fc: Add a hbaapi_lib attribute to the fcport structure Robert Love
@ 2011-03-11 21:55 ` Robert Love
9 siblings, 0 replies; 11+ messages in thread
From: Robert Love @ 2011-03-11 21:55 UTC (permalink / raw)
To: linux-scsi; +Cc: abjoglek, giridhar.malavali, james.smart, bprakash
Document explaining some of the basics of the
Fibre Channel subsystem. It covers features,
design decisions and implementation details
regarding the libfc/libfcoe/fcoe usage of this
subsystem.
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
Documentation/fc/fc_sysfs.txt | 192 +++++++++++++++++++++++++++++++
Documentation/fc/libfc_libfcoe_fcoe.txt | 17 +++
2 files changed, 209 insertions(+), 0 deletions(-)
create mode 100644 Documentation/fc/fc_sysfs.txt
create mode 100644 Documentation/fc/libfc_libfcoe_fcoe.txt
diff --git a/Documentation/fc/fc_sysfs.txt b/Documentation/fc/fc_sysfs.txt
new file mode 100644
index 0000000..fff3316
--- /dev/null
+++ b/Documentation/fc/fc_sysfs.txt
@@ -0,0 +1,192 @@
+Fibre Channel sysfs Representation
+
+Summary
+-------------------------------
+
+The Fibre Channel subsystem is designed to represent a
+fabric (or fabric-less) topology in sysfs. It allows FC
+drivers to create the following instances as the driver
+gains awareness of them.
+
+fc_port - The physical port on a CNA/HBA/NIC.
+
+fc_fabric - The switch that the physical port has discovered.
+
+fc_vport - The host's representation of a single fabric login.
+
+fc_rport - The host's representation of a discovered fabric
+ entity (usually a target).
+
+
+Differences between FC and FCoE
+-------------------------------
+
+FCoE uses a discovery protocol named FIP (FCoE Initialization
+Protocol) to discover which switch/fabric it should login to.
+Traditional FC HBAs connect directly to their switch, so no
+discovery is needed.
+
+FCoE LLDs will first create a fc_port and then as FCFs (FCoE
+switchs) are discovered the LLD will add fc_fabric(s) to the
+system. FC LLDs will simply create a fc_fabric immediately
+after the fc_port is created sicne switch/fabric information
+will immediately be available.
+
+
+Locking
+-------------------------------
+
+Locking is based on the fact that no child devices should
+exist without their parent. For example, you should never
+have a fc_fabric without a fc_port. If the fc_port is deleted
+then all of its children, fc_fabric(s), should have been
+deleted first.
+
+fc_port
+* locking primative: mutex
+* Used to synchronize the addition and deletion of
+ fc_fabrics as well as lookup a fc_fabric within the
+ fc_port's list of fc_fabrics.
+* Used to synchronize the addition and deletion of
+ fc_vports on the fc_fabric as well as lookup a fc_vport
+ within the fc_fabric's list of fc_vports.
+* Used to synchronize the fc_fabric's npiv_vports_inuse field.
+
+fc_fabric
+* locking primative: none
+* The fc_fabric uses the fc_port's lock (see above).
+
+fc_vport
+* locking primative: spinlock_t
+* Used to protect the fc_vport's 'flags'.
+* Used to synchronize the addition and deletion of
+ fc_rports on the fc_vport as well as lookup a fc_rport
+ within the fc_vport's list of fc_rports, as well as
+ the fc_vport's list of fc_rport bindings.
+
+APIs
+----
+
+fc_port_add: No locking requirement
+fc_port_del: No locking requirement
+fc_port_del_fabrics: fc_port lock held
+fc_fabric_add: fc_port lock held
+fc_fabric_del: fc_port lock held
+fc_vport_alloc: No locking requirement
+fc_vport_free: No locking requirement
+fc_vport_add: fc_port lock held
+fc_rport_add: No locking requirement
+fc_rport_del: No locking requirement
+
+TODO: This is a bit odd that some routines need the
+lock held and others don't, can I remove the locking
+requirements and isolate the locking to drivers/fc/?
+
+
+FC4 Registration/Unregistration
+-------------------------------
+
+Since Fibre Channel is capable of transporting multiple
+protocols (the FC4 layer in FC terms) the FC subsystem
+allows the LLD to register the FC4s it supports with the
+fc_port and it selects the FC4 for use with each fc_vport.
+Currently only SCSI-FCP (Initiator) is supported and is
+implemented in the scsi_transport_fcp.{ch} files.
+
+A bus/driver model is used to represent this architecture.
+The fc_vport and fc_rport instances are each bus devices
+and fcp_init and fcp_targ instances are drivers for these
+busses, respectively.
+
+In the simplist sense when a fc_vport is created a
+fcp_init is also created. When a fc_rport is created
+a fcp_targ is created.
+
+You can think of the FC4 layer as a 'glue' layer that
+ties the FC4 protocol to the FC subsystem. If the LLD
+supports a FC4 protocol it must implement the necessary
+callbacks to initialize any FC4 instances created.
+
+
+SCSI-FCP Devices
+-------------------------------
+
+fcp_init
+* 1:1 with the Scsi_Host and fc_vport
+* Allocated with the Scsi_Host
+* Replaces the fc_host_attrs from the previous FC Transport
+
+fcp_targ
+* 1:1 with scsi_target (assuming the device is a target)
+ and the fc_rport
+* Not a child of the fc_rport in sysfs; its parent is the
+ scsi_target.
+
+
+Device Loss
+-------------------------------
+
+If the link between the HBA/CNA/NIC and the switch is
+broken it may be for a short or long period of time. If
+it is short we simply want to disable the discovered
+devices and then enable them when the link is
+re-established. Both the fc_rports and fc_fabrics use
+device loss timers to determine when they can be removed
+from the system.
+
+
+Memory Allocation
+-------------------------------
+
+The FC subsystem allows LLDs to specify a ammount of
+memory that the LLD can use for private data that will
+be associated with the devices that the FC subsystem
+creates.
+
+The fc_port, fc_fabric, fc_rport and fc_vport all allow
+for LLD private data. In addition the SCSI-FCP layer
+allows for LLD private data on the fcp_init device.
+
+libfc/libfcoe/fcoe
+------------------
+
+fc_port/fcoe_interface
+fc_fabric/fcoe_fcf
+fc_vport/fc_lport/fcoe_port
+fc_rport/fc_rport_libfc_priv
+
+fcp_init/fc_fcp_internal
+
+
+FCoE Attributes / Quirks
+-------------------------------
+
+Attributes
+----------
+
+FCoE attributes are added to FC subsystem devices if
+the LLD chooses to.
+
+The fc_port, fc_fabric and fc_vport devices all have
+FCoE attributes that can be added if desired.
+
+
+Multiple fc_fabrics
+-------------------
+
+Since FCoE discovers FCFs there needs to be a way to
+determine if a discovered FCF was previously discovered
+or not, otherwise the fc_port's list of fc_fabrics could
+contain duplicate entries.
+
+There isn't really any good FC attribute that can be used
+to uniquely identify a fc_fabric. Also, traditional FC HBAs
+will only add one fc_fabric to the system. This means that
+duplicate fc_fabrics is only a problem for FCoE HBA/CNA/NICs.
+
+The solution is for the LLD to provide a fabric_match
+callback that the FC subsystem can use to see if two
+fc_fabrics are really the same instance. This allows each
+LLD to match fc_fabrics however it wants. For FCoE LLDs
+it allows them to check FCoE attributes when looking
+for duplicates.
diff --git a/Documentation/fc/libfc_libfcoe_fcoe.txt b/Documentation/fc/libfc_libfcoe_fcoe.txt
new file mode 100644
index 0000000..a0b9901
--- /dev/null
+++ b/Documentation/fc/libfc_libfcoe_fcoe.txt
@@ -0,0 +1,17 @@
+libfc/libfcoe/fcoe Usage
+-------------------------------
+
+Every FC device created is created and added to the
+system with the same fc.h function: fc_port_add,
+fc_fabric_add, etc... Unfortunately, libfc/libfcoe/fcoe
+needs the fc_lport instance before a fc_fabric is
+discovered. Since the fc_lport is created with the
+fc_vport it needs to be allocated before FCF discovery.
+
+Because of this the fc_vport_add and fc_vport_del
+routines only add and remove an instance from sysfs.
+The fc_vport_alloc and fc_vport_free routines are used
+to simply allocate and free a fc_vport instance. This
+allows libfc/libfcoe/fcoe to allocate a fc_vport/fc_lport
+early and add that fc_vport to the system once it has
+logged into the fabric.
^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2011-03-11 21:55 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-11 21:54 [RFC PATCH v2 00/10] FC Sysfs and FCoE attributes Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 01/10] libfcoe: Remove mutex_trylock/restart_syscall checks Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 02/10] fcoe: " Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 03/10] fc: Create FC sybsystem Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 04/10] scsi_transport_fcp: Create FC/SCSI interaction layer Robert Love
2011-03-11 21:54 ` [RFC PATCH v2 05/10] libfc, libfcoe, fcoe: Make use of FC subsystem Robert Love
2011-03-11 21:55 ` [RFC PATCH v2 06/10] fc: Add FCoE attributes to FC sysfs Robert Love
2011-03-11 21:55 ` [RFC PATCH v2 07/10] libfcoe, fcoe: Use FCoE attributes Robert Love
2011-03-11 21:55 ` [RFC PATCH v2 08/10] fnic: Convert to new FC Sysfs infrastructure Robert Love
2011-03-11 21:55 ` [RFC PATCH v2 09/10] fc: Add a hbaapi_lib attribute to the fcport structure Robert Love
2011-03-11 21:55 ` [RFC PATCH v2 10/10] Documentation: Add fc_sysfs document Robert Love
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).