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