This patch adds a new target module for SCST and libfc
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Sat, 20 Feb 2010 20:02:02 +0000 (20:02 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Sat, 20 Feb 2010 20:02:02 +0000 (20:02 +0000)
that accepts FCP requests from libfc HBAs running Fibre Channel
over Ethernet (FCoE) and passes them to SCST.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@1514 d57e44dd-8a1f-0410-8b47-8ef2f437770f

23 files changed:
fcst/Kconfig [new file with mode: 0644]
fcst/Makefile [new file with mode: 0644]
fcst/Makefile_in-tree [new file with mode: 0644]
fcst/README [new file with mode: 0644]
fcst/fcst.h [new file with mode: 0644]
fcst/ft_cmd.c [new file with mode: 0644]
fcst/ft_io.c [new file with mode: 0644]
fcst/ft_scst.c [new file with mode: 0644]
fcst/ft_sess.c [new file with mode: 0644]
fcst/linux-patches/01-prli-clean [new file with mode: 0644]
fcst/linux-patches/02-fc4 [new file with mode: 0644]
fcst/linux-patches/03-seq-init [new file with mode: 0644]
fcst/linux-patches/04-seq-set-resp [new file with mode: 0644]
fcst/linux-patches/05-lport-hook [new file with mode: 0644]
fcst/linux-patches/06-lport-notify [new file with mode: 0644]
fcst/linux-patches/07-pta-mask [new file with mode: 0644]
fcst/linux-patches/08-ptp-ref [new file with mode: 0644]
fcst/linux-patches/09-ptp-type [new file with mode: 0644]
fcst/linux-patches/10-non-fip [new file with mode: 0644]
fcst/linux-patches/11-ptp-flogi-recv [new file with mode: 0644]
fcst/linux-patches/12-ptp-npiv [new file with mode: 0644]
fcst/linux-patches/13-ptp-acc [new file with mode: 0644]
fcst/linux-patches/series-2.6.33-rc8 [new file with mode: 0644]

diff --git a/fcst/Kconfig b/fcst/Kconfig
new file mode 100644 (file)
index 0000000..4c8890a
--- /dev/null
@@ -0,0 +1,5 @@
+config FCST
+       tristate "SCST target module for Fibre Channel using libfc"
+       depends on LIBFC && SCST
+       ---help---
+         Supports using libfc HBAs as target adapters with SCST
diff --git a/fcst/Makefile b/fcst/Makefile
new file mode 100644 (file)
index 0000000..72bd2b8
--- /dev/null
@@ -0,0 +1,106 @@
+#
+#  FCST: libfc SCSI target makefile
+#  Based on ../mvsas_tgt/Makefile
+#
+#  Copyright (C) 2006 - 2008 Jacky Feng <jfeng@marvell.com>
+#  
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation, version 2
+#  of the License.
+# 
+#  This program is distributed in the hope that 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.
+#
+#
+# Main targets:
+#    all (the default) : make all
+#    clean             : clean files
+#    extraclean        : clean + clean dependencies
+#    install           : install 
+#    uninstall         : uninstall 
+#
+# Notes :
+#    - install and uninstall must be made as root
+#
+
+ifeq ($(KVER),)
+  ifeq ($(KDIR),)
+    KVER = $(shell uname -r)
+    KDIR := /lib/modules/$(KVER)/build
+  endif
+else
+  KDIR := /lib/modules/$(KVER)/build
+endif
+
+export PWD    := $(shell pwd)
+export CONFIG_FCST   := m
+
+#SCST_INC_DIR := /usr/local/include/scst
+#SCST_DIR := $(SCST_INC_DIR)
+SCST_INC_DIR := $(SUBDIRS)/../scst/include
+SCST_DIR := $(shell pwd)/../scst/src
+
+EXTRA_CFLAGS += -I$(SCST_INC_DIR)
+
+EXTRA_CFLAGS +=  -DSUPPORT_TARGET
+MODULE_NAME = fcst
+
+EXTRA_CFLAGS += -DMV_DEBUG
+
+INSTALL_DIR := /lib/modules/$(KVER)/extra
+
+#EXTRA_CFLAGS += -DCONFIG_SCST_TRACING
+#EXTRA_CFLAGS += -DDEBUG_WORK_IN_THREAD
+#EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG
+
+ifneq ($(KERNELRELEASE),)
+include $(SUBDIRS)/Makefile_in-tree
+else
+all: Modules.symvers Module.symvers
+       $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m
+
+tgt: Modules.symvers Module.symvers
+       $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=n
+
+install: all
+       $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \
+               modules_install
+       -depmod -a $(KVER)
+
+ins:
+       ./config
+       insmod fcst.ko
+       
+SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Modules.symvers 2>/dev/null)
+ifneq ($(SCST_MOD_VERS),)
+Modules.symvers: $(SCST_DIR)/Modules.symvers
+       cp $(SCST_DIR)/Modules.symvers .
+else
+.PHONY: Modules.symvers
+endif
+
+# It's renamed in 2.6.18
+SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Module.symvers 2>/dev/null)
+ifneq ($(SCST_MOD_VERS),)
+Module.symvers: $(SCST_DIR)/Module.symvers
+       cp $(SCST_DIR)/Module.symvers .
+else
+.PHONY: Module.symvers
+endif
+
+uninstall:
+       rm -f $(INSTALL_DIR)/$(MODULE_NAME).ko
+       -/sbin/depmod -a $(KVER)
+endif
+
+clean:
+       rm -f *.o *.ko .*.cmd *.mod.c .*.d .depend *~ Modules.symvers \
+               Module.symvers Module.markers modules.order
+       rm -rf .tmp_versions
+
+extraclean: clean
+
+.PHONY: all tgt install uninstall clean extraclean
diff --git a/fcst/Makefile_in-tree b/fcst/Makefile_in-tree
new file mode 100644 (file)
index 0000000..a9c003e
--- /dev/null
@@ -0,0 +1,9 @@
+ccflags-y += -Iinclude/scst
+
+obj-$(CONFIG_FCST) += fcst.o
+
+fcst-objs := \
+       ft_cmd.o \
+       ft_io.o \
+       ft_scst.o \
+       ft_sess.o
diff --git a/fcst/README b/fcst/README
new file mode 100644 (file)
index 0000000..712827e
--- /dev/null
@@ -0,0 +1,98 @@
+fcst README                                            v0.1 02/17/2010
+
+$Id$
+
+FCST is a module that depends on libfc and SCST to provide FC target support.
+
+To build for linux-2.6.33-rc8, do:
+
+1. Get the kernel:
+
+       KERNEL=linux-2.6.33-rc8
+
+       cd /usr/src/kernels
+       URL_DIR=http://www.kernel.org/pub/linux/kernel/v2.6/testing/
+       TARFILE=$VER.tar.bz2
+       wget -o $TARFILE $URL_DIR/$TARFILE
+       tar xfj $TARFILE
+       cd $KERNEL
+
+2. Apply patches needed for libfc target hooks and point-to-point fixes:
+
+       KDIR=/usr/src/kernels/$KERNEL
+       PDIR=/usr/src/scst/trunk/fcst/linux-patches     # use your dir here
+
+       cd $PDIR
+       for patch in `grep -v '^#' series-$KERNEL`
+       do
+               (cd $KDIR; patch -p1) < $patch
+       done
+
+3.  Apply SCST patches to the kernel
+       See trunk/scst/README
+       The readahead patches are not needed in 2.6.33.
+       The io_context patch needs to be adjusted for 2.6.33.  Skip it for now.
+
+4.  Configure and make your kernel
+
+5.  Install SCST
+       See trunk/scst/README. Make sure you are building sysfs SCST build, because
+       FCST supports only it.
+
+6.  Make FCST
+       In this directory, just do
+               make
+               make install
+
+7.  Install the FCoE admin tools, including dcbd and fcoeadm.
+       Some distros may have these.
+       You should be able to use the source at
+       http://www.open-fcoe.org/openfc/downloads/2.6.32/open-fcoe-2.6.32.tar.gz
+
+8.  Bring up SCST and configure the devices.
+
+9.  Bring up an FCoE initiator (we'll enable target mode on it later):
+       fcoeadm -c eth3
+
+    The other end can be an initiator as well, in point-to-point mode
+    over a full-duplex loss-less link (enable pause on both sides),
+    or a link to an FCoE switch.
+
+10. Use fcc (part of the open-fcoe debug tools in step 7) to see the 
+    initiator setup.  To get the FCoE port name for eth3 
+
+       # fcc
+       FC HBAs:
+       HBA       Port Name                Port ID   State     Device         
+       host4     20:00:00:1b:21:06:58:21  01:01:02  Online    eth3           
+
+       host4 Remote Ports:
+       Path      Port Name                Port ID   State     Roles          
+       4:0-0     10:00:50:41:4c:4f:3b:00  01:01:01  Online    FCP Initiator  
+
+    In the above example, there's one local host on eth3, and it's in
+    a point-to-point connection with the remote initiator with Port_id 010101.
+
+11.  Load fcst and create an ini-group for the remote initiator.  Do:
+
+       LPORT=20:00:00:1b:21:06:58:21           # the local Port_Name
+       INIT_PORT=10:00:50:41:4c:4f:3b:00       # remote Port Name
+       GROUP=test                              # your choice ini_group name
+       GDIR=$LPORT/ini_group/$GROUP
+
+       modprobe fcst
+       cd /sys/kernel/scst_tgt/targets/fcst
+       echo create $GROUP > $LPORT/ini_group/mgmt
+       echo add $INIT_PORT > $GDIR/initiators/mgmt
+
+12.  Add any disks you want to the LUNs in that init group.
+
+       echo add disk-name 0 > $GDIR/luns/mgmt
+       
+13.  Enable the initiator:
+
+       echo 1 > $LPORT/enabled
+
+14.  As a temporary workaround, you may need to reset the interface
+     on the initiator side so it sees the SCST device as a target and
+     discovers LUNs.  You can avoid this by bringing up the initiator last.
diff --git a/fcst/fcst.h b/fcst/fcst.h
new file mode 100644 (file)
index 0000000..e80b628
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * $Id$
+ */
+#ifndef __SCSI_FCST_H__
+#define __SCSI_FCST_H__
+
+#include "scst.h"
+
+#define FT_VERSION     "0.3"
+#define FT_MODULE      "fcst"
+
+/*
+ * Debug options.
+ */
+#define FT_DEBUG_CONF  0x01    /* configuration messages */
+#define FT_DEBUG_SESS  0x02    /* session messages */
+#define FT_DEBUG_IO    0x04    /* I/O operations */
+
+extern unsigned int ft_debug_logging;  /* debug options */
+
+#define FT_ERR(fmt, args...)                                           \
+       printk(KERN_ERR FT_MODULE ": %s: " fmt, __func__, ##args)
+
+#define FT_DEBUG(mask, fmt, args...)                                   \
+       do {                                                            \
+               if (ft_debug_logging & (mask))                          \
+                       printk(KERN_INFO FT_MODULE ": %s: " fmt,        \
+                              __func__, ##args);                       \
+       } while (0)
+
+#define FT_CONF_DBG(fmt, args...)      FT_DEBUG(FT_DEBUG_CONF, fmt, ##args)
+#define FT_SESS_DBG(fmt, args...)      FT_DEBUG(FT_DEBUG_SESS, fmt, ##args)
+#define FT_IO_DBG(fmt, args...)                FT_DEBUG(FT_DEBUG_IO, fmt, ##args)
+
+#define FT_NAMELEN     32              /* length of ASCI WWPNs including pad */
+
+/*
+ * Session (remote port).
+ */
+struct ft_sess {
+       u32 port_id;                    /* for hash lookup use only */
+       u32 params;
+       u16 max_payload;                /* max transmitted payload size */
+       u32 max_lso_payload;            /* max offloaded payload size */
+       u64 port_name;                  /* port name for transport ID */
+       struct ft_tport *tport;
+       struct hlist_node hash;         /* linkage in ft_sess_hash table */
+       struct rcu_head rcu;
+       struct kref kref;               /* ref for hash and outstanding I/Os */
+       struct scst_session *scst_sess;
+};
+
+/*
+ * Hash table of sessions per local port.
+ * Hash lookup by remote port FC_ID.
+ */
+#define FT_SESS_HASH_BITS      6
+#define FT_SESS_HASH_SIZE      (1 << FT_SESS_HASH_BITS)
+
+/*
+ * Per local port data.
+ * This is created when the first session logs into the local port.
+ * Deleted when tpg is deleted or last session is logged off.
+ */
+struct ft_tport {
+       u32     sess_count;             /* number of sessions in hash */
+       u8      enabled:1;
+       struct rcu_head rcu;
+       struct hlist_head hash[FT_SESS_HASH_SIZE];      /* list of sessions */
+       struct fc_lport *lport;
+       struct scst_tgt *tgt;
+};
+
+/*
+ * Commands
+ */
+struct ft_cmd {
+       int serial;                     /* order received, for debugging */
+       struct fc_seq *seq;             /* sequence in exchange mgr */
+       struct fc_frame *req_frame;     /* original request frame */
+       u32 write_data_len;             /* data received from initiator */
+       u32 read_data_len;              /* data sent to initiator */
+       u32 xfer_rdy_len;               /* max xfer ready offset */
+       u32 max_lso_payload;            /* max offloaded (LSO) data payload */
+       u16 max_payload;                /* max transmitted data payload */
+       struct scst_cmd *scst_cmd;
+};
+
+extern struct list_head ft_lport_list;
+extern struct mutex ft_lport_lock;
+extern struct scst_tgt_template ft_scst_template;
+
+/*
+ * libfc interface.
+ */
+int ft_prli(struct fc_rport_priv *, u32 spp_len,
+           const struct fc_els_spp *, struct fc_els_spp *);
+void ft_prlo(struct fc_rport_priv *);
+void ft_recv(struct fc_lport *, struct fc_seq *, struct fc_frame *);
+
+/*
+ * SCST interface.
+ */
+int ft_send_response(struct scst_cmd *);
+int ft_send_xfer_rdy(struct scst_cmd *);
+void ft_cmd_timeout(struct scst_cmd *);
+void ft_cmd_free(struct scst_cmd *);
+void ft_cmd_tm_done(struct scst_mgmt_cmd *);
+int ft_tgt_detect(struct scst_tgt_template *);
+int ft_tgt_release(struct scst_tgt *);
+ssize_t ft_tgt_enable(struct scst_tgt *, const char *, size_t);
+bool ft_tgt_enabled(struct scst_tgt *);
+int ft_report_aen(struct scst_aen *);
+
+/*
+ * Session interface.
+ */
+int ft_lport_notify(struct notifier_block *, unsigned long, void *);
+void ft_lport_add(struct fc_lport *, void *);
+void ft_lport_del(struct fc_lport *, void *);
+
+/*
+ * other internal functions.
+ */
+int ft_thread(void *);
+void ft_recv_req(struct ft_sess *, struct fc_seq *, struct fc_frame *);
+void ft_recv_write_data(struct scst_cmd *, struct fc_frame *);
+int ft_send_read_data(struct scst_cmd *);
+struct ft_tpg *ft_lport_find_tpg(struct fc_lport *);
+struct ft_node_acl *ft_acl_get(struct ft_tpg *, struct fc_rport_priv *);
+void ft_cmd_dump(struct scst_cmd *, const char *);
+
+#endif /* __SCSI_FCST_H__ */
diff --git a/fcst/ft_cmd.c b/fcst/ft_cmd.c
new file mode 100644 (file)
index 0000000..de5806b
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+#include "fcst.h"
+
+/*
+ * Append string to buffer safely.
+ * Also prepends a space if there's already something the buf.
+ */
+static void ft_cmd_flag(char *buf, size_t len, const char *desc)
+{
+       if (buf[0])
+               strlcat(buf, " ", len);
+       strlcat(buf, desc, len);
+}
+
+/*
+ * Debug: dump command.
+ */
+void ft_cmd_dump(struct scst_cmd *cmd, const char *caller)
+{
+       static atomic_t serial;
+       struct ft_cmd *fcmd;
+       struct fc_exch *ep;
+       char prefix[30];
+       char buf[150];
+
+       if (!(ft_debug_logging & FT_DEBUG_IO))
+               return;
+
+       fcmd = scst_cmd_get_tgt_priv(cmd);
+       ep = fc_seq_exch(fcmd->seq);
+       snprintf(prefix, sizeof(prefix), FT_MODULE ": cmd %2x",
+               atomic_inc_return(&serial) & 0xff);
+
+       printk(KERN_INFO "%s %s oid %x oxid %x resp_len %u\n",
+               prefix, caller, ep->oid, ep->oxid,
+               scst_cmd_get_resp_data_len(cmd));
+       printk(KERN_INFO "%s scst_cmd %p wlen %u rlen %u\n",
+               prefix, cmd, fcmd->write_data_len, fcmd->read_data_len);
+       printk(KERN_INFO "%s exp_dir %x exp_xfer_len %d exp_in_len %d\n",
+               prefix, cmd->expected_data_direction,
+               cmd->expected_transfer_len, cmd->expected_in_transfer_len);
+       printk(KERN_INFO "%s dir %x data_len %d bufflen %d in_bufflen %d\n",
+               prefix, cmd->data_direction, cmd->data_len,
+               cmd->bufflen, cmd->in_bufflen);
+       printk(KERN_INFO "%s sg_cnt reg %d in %d tgt %d tgt_in %d\n",
+               prefix, cmd->sg_cnt, cmd->in_sg_cnt,
+               cmd->tgt_sg_cnt, cmd->tgt_in_sg_cnt);
+
+       buf[0] = '\0';
+       if (cmd->sent_for_exec)
+               ft_cmd_flag(buf, sizeof(buf), "sent");
+       if (cmd->completed)
+               ft_cmd_flag(buf, sizeof(buf), "comp");
+       if (cmd->ua_ignore)
+               ft_cmd_flag(buf, sizeof(buf), "ua_ign");
+       if (cmd->atomic)
+               ft_cmd_flag(buf, sizeof(buf), "atom");
+       if (cmd->double_ua_possible)
+               ft_cmd_flag(buf, sizeof(buf), "dbl_ua_poss");
+       if (cmd->is_send_status)
+               ft_cmd_flag(buf, sizeof(buf), "send_stat");
+       if (cmd->retry)
+               ft_cmd_flag(buf, sizeof(buf), "retry");
+       if (cmd->internal)
+               ft_cmd_flag(buf, sizeof(buf), "internal");
+       if (cmd->inc_blocking)
+               ft_cmd_flag(buf, sizeof(buf), "inc_blk");
+       if (cmd->needs_unblocking)
+               ft_cmd_flag(buf, sizeof(buf), "needs_unblk");
+       if (cmd->dec_on_dev_needed)
+               ft_cmd_flag(buf, sizeof(buf), "dec_on_dev");
+       if (cmd->cmd_hw_pending)
+               ft_cmd_flag(buf, sizeof(buf), "hw_pend");
+       if (cmd->tgt_need_alloc_data_buf)
+               ft_cmd_flag(buf, sizeof(buf), "tgt_need_alloc");
+       if (cmd->tgt_data_buf_alloced)
+               ft_cmd_flag(buf, sizeof(buf), "tgt_alloced");
+       if (cmd->dh_data_buf_alloced)
+               ft_cmd_flag(buf, sizeof(buf), "dh_alloced");
+       if (cmd->expected_values_set)
+               ft_cmd_flag(buf, sizeof(buf), "exp_val");
+       if (cmd->sg_buff_modified)
+               ft_cmd_flag(buf, sizeof(buf), "sg_buf_mod");
+       if (cmd->preprocessing_only)
+               ft_cmd_flag(buf, sizeof(buf), "pre_only");
+       if (cmd->sn_set)
+               ft_cmd_flag(buf, sizeof(buf), "sn_set");
+       if (cmd->hq_cmd_inced)
+               ft_cmd_flag(buf, sizeof(buf), "hq_cmd_inc");
+       if (cmd->set_sn_on_restart_cmd)
+               ft_cmd_flag(buf, sizeof(buf), "set_sn_on_restart");
+       if (cmd->no_sgv)
+               ft_cmd_flag(buf, sizeof(buf), "no_sgv");
+       if (cmd->may_need_dma_sync)
+               ft_cmd_flag(buf, sizeof(buf), "dma_sync");
+       if (cmd->out_of_sn)
+               ft_cmd_flag(buf, sizeof(buf), "oo_sn");
+       if (cmd->inc_expected_sn_on_done)
+               ft_cmd_flag(buf, sizeof(buf), "inc_sn_exp");
+       if (cmd->done)
+               ft_cmd_flag(buf, sizeof(buf), "done");
+       if (cmd->finished)
+               ft_cmd_flag(buf, sizeof(buf), "fin");
+       if (cmd->tm_dbg_delayed)
+               ft_cmd_flag(buf, sizeof(buf), "tm_dbg_del");
+       if (cmd->tm_dbg_immut)
+               ft_cmd_flag(buf, sizeof(buf), "tm_dbg_immut");
+
+       printk(KERN_INFO "%s flags %s\n", prefix, buf);
+       printk(KERN_INFO "%s lun %lld sn %d tag %lld cmd_flags %lx\n",
+               prefix, cmd->lun, cmd->sn, cmd->tag, cmd->cmd_flags);
+       printk(KERN_INFO "%s tgt_sn %d op_flags %x op %s\n",
+               prefix, cmd->tgt_sn, cmd->op_flags, cmd->op_name);
+       printk(KERN_INFO "%s status %x msg_status %x "
+               "host_status %x driver_status %x\n",
+               prefix, cmd->status, cmd->msg_status,
+               cmd->host_status, cmd->driver_status);
+       printk(KERN_INFO "%s cdb_len %d ext_cdb_len %u\n",
+               prefix, cmd->cdb_len, cmd->ext_cdb_len);
+       snprintf(buf, sizeof(buf), "%s cdb ", prefix);
+       print_hex_dump(KERN_INFO, buf, DUMP_PREFIX_NONE,
+               16, 4, cmd->cdb, SCST_MAX_CDB_SIZE, 0);
+}
+
+/*
+ * Debug: dump mgmt command.
+ */
+void ft_cmd_tm_dump(struct scst_mgmt_cmd *mcmd, const char *caller)
+{
+       struct ft_cmd *fcmd;
+       struct fc_exch *ep;
+       char prefix[30];
+       char buf[150];
+
+       if (!(ft_debug_logging & FT_DEBUG_IO))
+               return;
+       fcmd = scst_mgmt_cmd_get_tgt_priv(mcmd);
+       ep = fc_seq_exch(fcmd->seq);
+
+       snprintf(prefix, sizeof(prefix), FT_MODULE ": mcmd");
+
+       printk(KERN_INFO "%s %s oid %x oxid %x lun %lld\n",
+               prefix, caller, ep->oid, ep->oxid,
+               (unsigned long long)mcmd->lun);
+       printk(KERN_INFO "%s state %d fn %d fin_wait %d done_wait %d comp %d\n",
+               prefix, mcmd->state, mcmd->fn,
+               mcmd->cmd_finish_wait_count, mcmd->cmd_done_wait_count,
+               mcmd->completed_cmd_count);
+       buf[0] = '\0';
+       if (mcmd->completed)
+               ft_cmd_flag(buf, sizeof(buf), "comp");
+       if (mcmd->needs_unblocking)
+               ft_cmd_flag(buf, sizeof(buf), "needs_unblock");
+       if (mcmd->lun_set)
+               ft_cmd_flag(buf, sizeof(buf), "lun_set");
+       if (mcmd->cmd_sn_set)
+               ft_cmd_flag(buf, sizeof(buf), "cmd_sn_set");
+       if (mcmd->affected_cmds_done_called)
+               ft_cmd_flag(buf, sizeof(buf), "cmds_done");
+       printk(KERN_INFO "%s flags %s\n", prefix, buf);
+       if (mcmd->cmd_to_abort)
+               ft_cmd_dump(mcmd->cmd_to_abort, caller);
+}
+
+/*
+ * Free command.
+ */
+void ft_cmd_free(struct scst_cmd *cmd)
+{
+       struct ft_cmd *fcmd;
+
+       fcmd = scst_cmd_get_tgt_priv(cmd);
+       if (fcmd) {
+               scst_cmd_set_tgt_priv(cmd, NULL);
+               fc_frame_free(fcmd->req_frame);
+               kfree(fcmd);
+       }
+}
+
+/*
+ * Send response, after data if applicable.
+ */
+int ft_send_response(struct scst_cmd *cmd)
+{
+       struct ft_cmd *fcmd;
+       struct fc_frame *fp;
+       struct fcp_resp_with_ext *fcp;
+       struct fc_lport *lport;
+       struct fc_exch *ep;
+       unsigned int slen;
+       size_t len;
+       int resid = 0;
+       int bi_resid = 0;
+       int error;
+       int dir;
+       u32 status;
+
+       ft_cmd_dump(cmd, __func__);
+       fcmd = scst_cmd_get_tgt_priv(cmd);
+       ep = fc_seq_exch(fcmd->seq);
+       lport = ep->lp;
+
+       if (scst_cmd_aborted(cmd)) {
+               scst_set_delivery_status(cmd, SCST_CMD_DELIVERY_ABORTED);
+               lport->tt.exch_done(fcmd->seq);
+               return SCST_TGT_RES_SUCCESS;
+       }
+
+       if (!scst_cmd_get_is_send_status(cmd)) {
+               FT_IO_DBG("send status not set.  feature not implemented\n");
+               return SCST_TGT_RES_FATAL_ERROR;
+       }
+
+       status = scst_cmd_get_status(cmd);
+       dir = scst_cmd_get_data_direction(cmd);
+
+       slen = scst_cmd_get_sense_buffer_len(cmd);
+       len = sizeof(*fcp) + slen;
+
+       /*
+        * Send read data and set underflow/overflow residual count.
+        * For bi-directional comands, the bi_resid is for the read direction.
+        */
+       if (dir & SCST_DATA_WRITE)
+               resid = (signed)scst_cmd_get_bufflen(cmd) -
+                       fcmd->write_data_len;
+       if (dir & SCST_DATA_READ) {
+               error = ft_send_read_data(cmd);
+               if (error) {
+                       FT_ERR("ft_send_read_data returned %d\n", error);
+                       return error;
+               }
+
+               if (dir == SCST_DATA_BIDI) {
+                       bi_resid = (signed)scst_cmd_get_in_bufflen(cmd) -
+                                  scst_cmd_get_resp_data_len(cmd);
+                       if (bi_resid)
+                               len += sizeof(__be32);
+               } else
+                       resid = (signed)scst_cmd_get_bufflen(cmd) -
+                               scst_cmd_get_resp_data_len(cmd);
+       }
+
+       fp = fc_frame_alloc(lport, len);
+       if (!fp)
+               return SCST_TGT_RES_QUEUE_FULL;
+
+       fcp = fc_frame_payload_get(fp, len);
+       memset(fcp, 0, sizeof(*fcp));
+       fcp->resp.fr_status = status;
+
+       if (slen) {
+               fcp->resp.fr_flags |= FCP_SNS_LEN_VAL;
+               fcp->ext.fr_sns_len = htonl(slen);
+               memcpy(fcp + 1, scst_cmd_get_sense_buffer(cmd), slen);
+       }
+       if (bi_resid) {
+               if (bi_resid < 0) {
+                       fcp->resp.fr_flags |= FCP_BIDI_READ_OVER;
+                       bi_resid = -bi_resid;
+               } else
+                       fcp->resp.fr_flags |= FCP_BIDI_READ_UNDER;
+               *(__be32 *)((u8 *)(fcp + 1) + slen) = htonl(bi_resid);
+       }
+       if (resid) {
+               if (resid < 0) {
+                       resid = -resid;
+                       fcp->resp.fr_flags |= FCP_RESID_OVER;
+               } else
+                       fcp->resp.fr_flags |= FCP_RESID_UNDER;
+               fcp->ext.fr_resid = htonl(resid);
+       }
+       FT_IO_DBG("response did %x oxid %x\n", ep->did, ep->oxid);
+
+       /*
+        * Send response.
+        */
+       fcmd->seq = lport->tt.seq_start_next(fcmd->seq);
+       fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
+                      FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);
+
+       lport->tt.seq_send(lport, fcmd->seq, fp);
+       lport->tt.exch_done(fcmd->seq);
+       scst_tgt_cmd_done(cmd, SCST_CONTEXT_SAME);
+       return SCST_TGT_RES_SUCCESS;
+}
+
+/*
+ * FC sequence response handler for follow-on sequences (data) and aborts.
+ */
+static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg)
+{
+       struct scst_cmd *cmd = arg;
+       struct fc_frame_header *fh;
+
+       /*
+        * If an error is being reported, it must be FC_EX_CLOSED.
+        * Timeouts don't occur on incoming requests, and there are
+        * currently no other errors.
+        * The PRLO handler will be also called by libfc to delete
+        * the session and all pending commands, so we ignore this response.
+        */
+       if (IS_ERR(fp)) {
+               FT_IO_DBG("exchange error %ld - not handled\n", -PTR_ERR(fp));
+               return;
+       }
+
+       fh = fc_frame_header_get(fp);
+       switch (fh->fh_r_ctl) {
+       case FC_RCTL_DD_SOL_DATA:       /* write data */
+               ft_recv_write_data(cmd, fp);
+               break;
+       case FC_RCTL_DD_UNSOL_CTL:      /* command */
+       case FC_RCTL_DD_SOL_CTL:        /* transfer ready */
+       case FC_RCTL_DD_DATA_DESC:      /* transfer ready */
+       default:
+               printk(KERN_INFO "%s: unhandled frame r_ctl %x\n",
+                      __func__, fh->fh_r_ctl);
+               fc_frame_free(fp);
+               break;
+       }
+}
+
+/*
+ * Command is about to be sent to device.
+ */
+int ft_pre_exec(struct scst_cmd *cmd)
+{
+       scst_restart_cmd(cmd, SCST_PREPROCESS_STATUS_SUCCESS,
+                        SCST_CONTEXT_SAME);
+       return SCST_PREPROCESS_STATUS_SUCCESS;
+}
+
+/*
+ * Command timeout.
+ * SCST calls this when the command has taken too long in the device handler.
+ */
+void ft_cmd_timeout(struct scst_cmd *cmd)
+{
+       FT_IO_DBG("timeout not implemented\n"); /* XXX TBD */
+}
+
+/*
+ * Send TX_RDY (transfer ready).
+ */
+static int ft_send_xfer_rdy_off(struct scst_cmd *cmd, u32 offset, u32 len)
+{
+       struct ft_cmd *fcmd;
+       struct fc_frame *fp;
+       struct fcp_txrdy *txrdy;
+       struct fc_lport *lport;
+       struct fc_exch *ep;
+
+       fcmd = scst_cmd_get_tgt_priv(cmd);
+       if (fcmd->xfer_rdy_len < len + offset)
+               fcmd->xfer_rdy_len = len + offset;
+
+       ep = fc_seq_exch(fcmd->seq);
+       lport = ep->lp;
+       fp = fc_frame_alloc(lport, sizeof(*txrdy));
+       if (!fp)
+               return SCST_TGT_RES_QUEUE_FULL;
+
+       txrdy = fc_frame_payload_get(fp, sizeof(*txrdy));
+       memset(txrdy, 0, sizeof(*txrdy));
+       txrdy->ft_data_ro = htonl(offset);
+       txrdy->ft_burst_len = htonl(len);
+
+       fcmd->seq = lport->tt.seq_start_next(fcmd->seq);
+       fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP,
+                      FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+       lport->tt.seq_send(lport, fcmd->seq, fp);
+       return SCST_TGT_RES_SUCCESS;
+}
+
+/*
+ * Send TX_RDY (transfer ready).
+ */
+int ft_send_xfer_rdy(struct scst_cmd *cmd)
+{
+       return ft_send_xfer_rdy_off(cmd, 0, scst_cmd_get_bufflen(cmd));
+}
+
+/*
+ * Send a FCP response including SCSI status and optional FCP rsp_code.
+ * status is SAM_STAT_GOOD (zero) if code is valid.
+ * This is used in error cases, such as allocation failures.
+ */
+static void ft_send_resp_status(struct fc_seq *sp, u32 status,
+                               enum fcp_resp_rsp_codes code)
+{
+       struct fc_frame *fp;
+       size_t len;
+       struct fcp_resp_with_ext *fcp;
+       struct fcp_resp_rsp_info *info;
+       struct fc_lport *lport;
+       struct fc_exch *ep;
+
+       ep = fc_seq_exch(sp);
+
+       FT_IO_DBG("FCP error response: did %x oxid %x status %x code %x\n",
+                 ep->did, ep->oxid, status, code);
+       lport = ep->lp;
+       len = sizeof(*fcp);
+       if (status == SAM_STAT_GOOD)
+               len += sizeof(*info);
+       fp = fc_frame_alloc(lport, len);
+       if (!fp)
+               goto out;
+       fcp = fc_frame_payload_get(fp, len);
+       memset(fcp, 0, len);
+       fcp->resp.fr_status = status;
+       if (status == SAM_STAT_GOOD) {
+               fcp->ext.fr_rsp_len = htonl(sizeof(*info));
+               fcp->resp.fr_flags |= FCP_RSP_LEN_VAL;
+               info = (struct fcp_resp_rsp_info *)(fcp + 1);
+               info->rsp_code = code;
+       }
+
+       sp = lport->tt.seq_start_next(sp);
+       fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
+                      FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);
+
+       lport->tt.seq_send(lport, sp, fp);
+out:
+       lport->tt.exch_done(sp);
+}
+
+/*
+ * Send error or task management response.
+ * Always frees the fcmd and associated state.
+ */
+static void ft_send_resp_code(struct ft_cmd *fcmd, enum fcp_resp_rsp_codes code)
+{
+       ft_send_resp_status(fcmd->seq, SAM_STAT_GOOD, code);
+       fc_frame_free(fcmd->req_frame);
+       kfree(fcmd);
+}
+
+void ft_cmd_tm_done(struct scst_mgmt_cmd *mcmd)
+{
+       struct ft_cmd *fcmd;
+       enum fcp_resp_rsp_codes code;
+
+       ft_cmd_tm_dump(mcmd, __func__);
+       fcmd = scst_mgmt_cmd_get_tgt_priv(mcmd);
+       switch (scst_mgmt_cmd_get_status(mcmd)) {
+       case SCST_MGMT_STATUS_SUCCESS:
+               code = FCP_TMF_CMPL;
+               break;
+       case SCST_MGMT_STATUS_REJECTED:
+               code = FCP_TMF_REJECTED;
+               break;
+       case SCST_MGMT_STATUS_LUN_NOT_EXIST:
+               code = FCP_TMF_INVALID_LUN;
+               break;
+       case SCST_MGMT_STATUS_TASK_NOT_EXIST:
+       case SCST_MGMT_STATUS_FN_NOT_SUPPORTED:
+       case SCST_MGMT_STATUS_FAILED:
+       default:
+               code = FCP_TMF_FAILED;
+               break;
+       }
+       FT_IO_DBG("tm cmd done fn %d code %d\n", mcmd->fn, code);
+       ft_send_resp_code(fcmd, code);
+}
+
+/*
+ * Handle an incoming FCP task management command frame.
+ * Note that this may be called directly from the softirq context.
+ */
+static void ft_recv_tm(struct scst_session *scst_sess,
+                      struct ft_cmd *fcmd, struct fcp_cmnd *fcp)
+{
+       struct scst_rx_mgmt_params params;
+       int ret;
+
+       memset(&params, 0, sizeof(params));
+       params.lun = fcp->fc_lun;
+       params.lun_len = sizeof(fcp->fc_lun);
+       params.lun_set = 1;
+       params.atomic = SCST_ATOMIC;
+       params.tgt_priv = fcmd;
+
+       switch (fcp->fc_tm_flags) {
+       case FCP_TMF_LUN_RESET:
+               params.fn = SCST_LUN_RESET;
+               break;
+       case FCP_TMF_TGT_RESET:
+               params.fn = SCST_TARGET_RESET;
+               params.lun_set = 0;
+               break;
+       case FCP_TMF_CLR_TASK_SET:
+               params.fn = SCST_CLEAR_TASK_SET;
+               break;
+       case FCP_TMF_ABT_TASK_SET:
+               params.fn = SCST_ABORT_TASK_SET;
+               break;
+       case FCP_TMF_CLR_ACA:
+               params.fn = SCST_CLEAR_ACA;
+               break;
+       default:
+               /*
+                * FCP4r01 indicates having a combination of
+                * tm_flags set is invalid.
+                */
+               FT_IO_DBG("invalid FCP tm_flags %x\n", fcp->fc_tm_flags);
+               ft_send_resp_code(fcmd, FCP_CMND_FIELDS_INVALID);
+               return;
+       }
+       FT_IO_DBG("submit tm cmd fn %d\n", params.fn);
+       ret = scst_rx_mgmt_fn(scst_sess, &params);
+       FT_IO_DBG("scst_rx_mgmt_fn ret %d\n", ret);
+       if (ret)
+               ft_send_resp_code(fcmd, FCP_TMF_FAILED);
+}
+
+/*
+ * Handle an incoming FCP command frame.
+ * Note that this may be called directly from the softirq context.
+ */
+static void ft_recv_cmd(struct ft_sess *sess, struct fc_seq *sp,
+                       struct fc_frame *fp)
+{
+       static atomic_t serial;
+       struct scst_cmd *cmd;
+       struct ft_cmd *fcmd;
+       struct fcp_cmnd *fcp;
+       struct fc_lport *lport;
+       int data_dir;
+       u32 data_len;
+       int cdb_len;
+
+       lport = fc_seq_exch(sp)->lp;
+       fcmd = kzalloc(sizeof(*fcmd), GFP_ATOMIC);
+       if (!fcmd)
+               goto busy;
+       fcmd->serial = atomic_inc_return(&serial);      /* debug only */
+       fcmd->seq = sp;
+       fcmd->max_payload = sess->max_payload;
+       fcmd->max_lso_payload = sess->max_lso_payload;
+       fcmd->req_frame = fp;
+
+       fcp = fc_frame_payload_get(fp, sizeof(*fcp));
+       if (!fcp)
+               goto err;
+       if (fcp->fc_tm_flags) {
+               ft_recv_tm(sess->scst_sess, fcmd, fcp);
+               return;
+       }
+
+       /*
+        * re-check length including specified CDB length.
+        * data_len is just after the CDB.
+        */
+       cdb_len = fcp->fc_flags & FCP_CFL_LEN_MASK;
+       fcp = fc_frame_payload_get(fp, sizeof(*fcp) + cdb_len);
+       if (!fcp)
+               goto err;
+       cdb_len += sizeof(fcp->fc_cdb);
+       data_len = ntohl(*(__be32 *)(fcp->fc_cdb + cdb_len));
+
+       cmd = scst_rx_cmd(sess->scst_sess, fcp->fc_lun, sizeof(fcp->fc_lun),
+                         fcp->fc_cdb, cdb_len, SCST_ATOMIC);
+       if (!cmd) {
+               kfree(fcmd);
+               goto busy;
+       }
+       fcmd->scst_cmd = cmd;
+       scst_cmd_set_tgt_priv(cmd, fcmd);
+
+       switch (fcp->fc_flags & (FCP_CFL_RDDATA | FCP_CFL_WRDATA)) {
+       case 0:
+               data_dir = SCST_DATA_NONE;
+               break;
+       case FCP_CFL_RDDATA:
+               data_dir = SCST_DATA_READ;
+               break;
+       case FCP_CFL_WRDATA:
+               data_dir = SCST_DATA_WRITE;
+               break;
+       case FCP_CFL_RDDATA | FCP_CFL_WRDATA:
+               data_dir = SCST_DATA_BIDI;
+               break;
+       }
+       scst_cmd_set_expected(cmd, data_dir, data_len);
+
+       switch (fcp->fc_pri_ta & FCP_PTA_MASK) {
+       case FCP_PTA_SIMPLE:
+               cmd->queue_type = SCST_CMD_QUEUE_SIMPLE;
+               break;
+       case FCP_PTA_HEADQ:
+               cmd->queue_type = SCST_CMD_QUEUE_HEAD_OF_QUEUE;
+               break;
+       case FCP_PTA_ACA:
+               cmd->queue_type = SCST_CMD_QUEUE_ACA;
+               break;
+       case FCP_PTA_ORDERED:
+       default:
+               cmd->queue_type = SCST_CMD_QUEUE_ORDERED;
+               break;
+       }
+
+       lport->tt.seq_set_resp(sp, ft_recv_seq, cmd);
+       scst_cmd_init_done(cmd, SCST_CONTEXT_THREAD);
+       return;
+
+err:
+       ft_send_resp_code(fcmd, FCP_CMND_FIELDS_INVALID);
+       return;
+
+busy:
+       FT_IO_DBG("cmd allocation failure - sending BUSY\n");
+       ft_send_resp_status(sp, SAM_STAT_BUSY, 0);
+       fc_frame_free(fp);
+}
+
+/*
+ * Send FCP ELS-4 Reject.
+ */
+static void ft_cmd_ls_rjt(struct fc_seq *sp, enum fc_els_rjt_reason reason,
+                         enum fc_els_rjt_explan explan)
+{
+       struct fc_frame *fp;
+       struct fc_els_ls_rjt *rjt;
+       struct fc_lport *lport;
+       struct fc_exch *ep;
+
+       ep = fc_seq_exch(sp);
+       lport = ep->lp;
+       fp = fc_frame_alloc(lport, sizeof(*rjt));
+       if (!fp)
+               return;
+
+       rjt = fc_frame_payload_get(fp, sizeof(*rjt));
+       memset(rjt, 0, sizeof(*rjt));
+       rjt->er_cmd = ELS_LS_RJT;
+       rjt->er_reason = reason;
+       rjt->er_explan = explan;
+
+       sp = lport->tt.seq_start_next(sp);
+       fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, FC_TYPE_FCP,
+                      FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_LAST_SEQ, 0);
+       lport->tt.seq_send(lport, sp, fp);
+}
+
+/*
+ * Handle an incoming FCP ELS-4 command frame.
+ * Note that this may be called directly from the softirq context.
+ */
+static void ft_recv_els4(struct ft_sess *sess, struct fc_seq *sp,
+                        struct fc_frame *fp)
+{
+       u8 op = fc_frame_payload_op(fp);
+
+       switch (op) {
+       case ELS_SRR:                   /* TBD */
+       default:
+               FT_IO_DBG("unsupported ELS-4 op %x\n", op);
+               ft_cmd_ls_rjt(sp, ELS_RJT_INVAL, ELS_EXPL_NONE);
+               fc_frame_free(fp);
+               break;
+       }
+}
+
+/*
+ * Handle an incoming FCP frame.
+ * Note that this may be called directly from the softirq context.
+ */
+void ft_recv_req(struct ft_sess *sess, struct fc_seq *sp, struct fc_frame *fp)
+{
+       struct fc_frame_header *fh = fc_frame_header_get(fp);
+
+       switch (fh->fh_r_ctl) {
+       case FC_RCTL_DD_UNSOL_CMD:
+               ft_recv_cmd(sess, sp, fp);
+               break;
+       case FC_RCTL_ELS4_REQ:
+               ft_recv_els4(sess, sp, fp);
+               break;
+       default:
+               printk(KERN_INFO "%s: unhandled frame r_ctl %x\n",
+                      __func__, fh->fh_r_ctl);
+               fc_frame_free(fp);
+               sess->tport->lport->tt.exch_done(sp);
+               break;
+       }
+}
diff --git a/fcst/ft_io.c b/fcst/ft_io.c
new file mode 100644 (file)
index 0000000..fe550aa
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * Portions based on drivers/scsi/libfc/fc_fcp.c and subject to the following:
+ *
+ * Copyright (c) 2007 Intel Corporation. All rights reserved.
+ * Copyright (c) 2008 Red Hat, Inc.  All rights reserved.
+ * Copyright (c) 2008 Mike Christie
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+#include "fcst.h"
+
+/*
+ * Receive write data frame.
+ */
+void ft_recv_write_data(struct scst_cmd *cmd, struct fc_frame *fp)
+{
+       struct ft_cmd *fcmd;
+       struct fc_frame_header *fh;
+       unsigned int bufflen;
+       u32 rel_off;
+       size_t frame_len;
+       size_t mem_len;
+       size_t tlen;
+       void *from;
+       void *to;
+       int dir;
+       u8 *buf;
+
+       dir = scst_cmd_get_data_direction(cmd);
+       if (dir == SCST_DATA_BIDI) {
+               mem_len = scst_get_in_buf_first(cmd, &buf);
+               bufflen = scst_cmd_get_in_bufflen(cmd);
+       } else {
+               mem_len = scst_get_buf_first(cmd, &buf);
+               bufflen = scst_cmd_get_bufflen(cmd);
+       }
+       to = buf;
+
+       fcmd = scst_cmd_get_tgt_priv(cmd);
+       fh = fc_frame_header_get(fp);
+       frame_len = fr_len(fp);
+       rel_off = ntohl(fh->fh_parm_offset);
+
+       FT_IO_DBG("sid %x oxid %x payload_len %zd rel_off %x\n",
+                 ntoh24(fh->fh_s_id), ntohs(fh->fh_ox_id),
+                 frame_len - sizeof(*fh), rel_off);
+
+       if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF))
+               goto drop;
+       if (frame_len <= sizeof(*fh))
+               goto drop;
+       frame_len -= sizeof(*fh);
+       from = fc_frame_payload_get(fp, 0);
+
+       if (rel_off >= bufflen)
+               goto drop;
+       if (frame_len + rel_off > bufflen)
+               frame_len = bufflen - rel_off;
+
+       while (frame_len) {
+               if (!mem_len) {
+                       if (dir == SCST_DATA_BIDI) {
+                               scst_put_in_buf(cmd, buf);
+                               mem_len = scst_get_in_buf_next(cmd, &buf);
+                       } else {
+                               scst_put_buf(cmd, buf);
+                               mem_len = scst_get_buf_next(cmd, &buf);
+                       }
+                       to = buf;
+                       if (!mem_len)
+                               break;
+               }
+               if (rel_off) {
+                       if (rel_off >= mem_len) {
+                               rel_off -= mem_len;
+                               mem_len = 0;
+                               continue;
+                       }
+                       mem_len -= rel_off;
+                       to += rel_off;
+                       rel_off = 0;
+               }
+
+               tlen = min(mem_len, frame_len);
+               memcpy(to, from, tlen);
+
+               from += tlen;
+               frame_len -= tlen;
+               mem_len -= tlen;
+               to += tlen;
+               fcmd->write_data_len += tlen;
+       }
+       if (mem_len) {
+               if (dir == SCST_DATA_BIDI)
+                       scst_put_in_buf(cmd, buf);
+               else
+                       scst_put_buf(cmd, buf);
+       }
+       if (fcmd->write_data_len == cmd->data_len)
+               scst_rx_data(cmd, SCST_RX_STATUS_SUCCESS, SCST_CONTEXT_THREAD);
+drop:
+       fc_frame_free(fp);
+}
+
+/*
+ * Send read data back to initiator.
+ */
+int ft_send_read_data(struct scst_cmd *cmd)
+{
+       struct ft_cmd *fcmd;
+       struct fc_frame *fp = NULL;
+       struct fc_exch *ep;
+       struct fc_lport *lport;
+       size_t remaining;
+       u32 fh_off = 0;
+       u32 frame_off;
+       size_t frame_len = 0;
+       size_t mem_len;
+       u32 mem_off;
+       size_t tlen;
+       struct page *page;
+       int use_sg;
+       int error;
+       void *to = NULL;
+       u8 *from = NULL;
+       int loop_limit = 10000;
+
+       fcmd = scst_cmd_get_tgt_priv(cmd);
+       ep = fc_seq_exch(fcmd->seq);
+       lport = ep->lp;
+
+       frame_off = fcmd->read_data_len;
+       tlen = scst_cmd_get_resp_data_len(cmd);
+       FT_IO_DBG("oid %x oxid %x resp_len %zd frame_off %u\n",
+                 ep->oid, ep->oxid, tlen, frame_off);
+       if (tlen <= frame_off)
+               return SCST_TGT_RES_SUCCESS;
+       remaining = tlen - frame_off;
+       if (remaining > UINT_MAX)
+               FT_ERR("oid %x oxid %x resp_len %zd frame_off %u\n",
+                      ep->oid, ep->oxid, tlen, frame_off);
+
+       mem_len = scst_get_buf_first(cmd, &from);
+       mem_off = 0;
+       if (!mem_len) {
+               FT_IO_DBG("mem_len 0\n");
+               return SCST_TGT_RES_SUCCESS;
+       }
+       FT_IO_DBG("sid %x oxid %x mem_len %zd frame_off %u remaining %zd\n",
+                ep->sid, ep->oxid, mem_len, frame_off, remaining);
+
+       /*
+        * If we've already transferred some of the data, skip through
+        * the buffer over the data already sent and continue with the
+        * same sequence.  Otherwise, get a new sequence for the data.
+        */
+       if (frame_off) {
+               tlen = frame_off;
+               while (mem_len <= tlen) {
+                       tlen -= mem_len;
+                       scst_put_buf(cmd, from);
+                       mem_len = scst_get_buf_next(cmd, &from);
+                       if (!mem_len)
+                               return SCST_TGT_RES_SUCCESS;
+               }
+               mem_len -= tlen;
+               mem_off = tlen;
+       } else
+               fcmd->seq = lport->tt.seq_start_next(fcmd->seq);
+
+       /* no scatter/gather in skb for odd word length due to fc_seq_send() */
+       use_sg = !(remaining % 4) && lport->sg_supp;
+
+       while (remaining) {
+               if (!loop_limit) {
+                       FT_ERR("hit loop limit.  remaining %zx mem_len %zx "
+                              "frame_len %zx tlen %zx\n",
+                              remaining, mem_len, frame_len, tlen);
+                       break;
+               }
+               loop_limit--;
+               if (!mem_len) {
+                       scst_put_buf(cmd, from);
+                       mem_len = scst_get_buf_next(cmd, &from);
+                       mem_off = 0;
+                       if (!mem_len) {
+                               FT_ERR("mem_len 0 from get_buf_next\n");
+                               break;
+                       }
+               }
+               if (!frame_len) {
+                       frame_len = fcmd->max_lso_payload;
+                       frame_len = min(frame_len, remaining);
+                       fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len);
+                       if (!fp) {
+                               FT_IO_DBG("frame_alloc failed. "
+                                         "use_sg %d frame_len %zd\n",
+                                         use_sg, frame_len);
+                               break;
+                       }
+                       fr_max_payload(fp) = fcmd->max_payload;
+                       to = fc_frame_payload_get(fp, 0);
+                       fh_off = frame_off;
+                       frame_off += frame_len;
+               }
+               tlen = min(mem_len, frame_len);
+               BUG_ON(!tlen);
+               BUG_ON(tlen > remaining);
+               BUG_ON(tlen > mem_len);
+               BUG_ON(tlen > frame_len);
+
+               if (use_sg) {
+                       page = virt_to_page(from + mem_off);
+                       get_page(page);
+                       tlen = min_t(size_t, tlen,
+                                    PAGE_SIZE - (mem_off & ~PAGE_MASK));
+                       skb_fill_page_desc(fp_skb(fp),
+                                          skb_shinfo(fp_skb(fp))->nr_frags,
+                                          page, mem_off, tlen);
+                       fr_len(fp) += tlen;
+                       fp_skb(fp)->data_len += tlen;
+                       fp_skb(fp)->truesize +=
+                                       PAGE_SIZE << compound_order(page);
+               } else {
+                       memcpy(to, from + mem_off, tlen);
+                       to += tlen;
+               }
+
+               mem_len -= tlen;
+               mem_off += tlen;
+               frame_len -= tlen;
+               remaining -= tlen;
+
+               if (frame_len)
+                       continue;
+               fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
+                              FC_TYPE_FCP,
+                              remaining ? (FC_FC_EX_CTX | FC_FC_REL_OFF) :
+                              (FC_FC_EX_CTX | FC_FC_REL_OFF | FC_FC_END_SEQ),
+                              fh_off);
+               error = lport->tt.seq_send(lport, fcmd->seq, fp);
+               if (error) {
+                       WARN_ON(1);
+                       /* XXX For now, initiator will retry */
+               } else
+                       fcmd->read_data_len = frame_off;
+       }
+       if (mem_len)
+               scst_put_buf(cmd, from);
+       if (remaining) {
+               FT_IO_DBG("remaining read data %zd\n", remaining);
+               return SCST_TGT_RES_QUEUE_FULL;
+       }
+       return SCST_TGT_RES_SUCCESS;
+}
diff --git a/fcst/ft_scst.c b/fcst/ft_scst.c
new file mode 100644 (file)
index 0000000..f02925b
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <scsi/libfc.h>
+#include "fcst.h"
+
+MODULE_AUTHOR("Joe Eykholt <jeykholt@cisco.com>");
+MODULE_DESCRIPTION("Fibre-Channel SCST target");
+MODULE_LICENSE("GPL v2");
+
+unsigned int ft_debug_logging;
+module_param_named(debug_logging, ft_debug_logging, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug_logging, "log levels bigmask");
+
+DEFINE_MUTEX(ft_lport_lock);
+
+/*
+ * Provider ops for libfc.
+ */
+static struct fc4_prov ft_prov = {
+       .prli = ft_prli,
+       .prlo = ft_prlo,
+       .recv = ft_recv,
+       .module = THIS_MODULE,
+};
+
+static struct notifier_block ft_notifier = {
+       .notifier_call = ft_lport_notify
+};
+
+/*
+ * SCST target ops and configuration.
+ * XXX - re-check uninitialized fields
+ */
+struct scst_tgt_template ft_scst_template = {
+       .sg_tablesize = 128,            /* XXX get true limit from libfc */
+       .xmit_response_atomic = 1,
+       .rdy_to_xfer_atomic = 1,
+       .xmit_response =        ft_send_response,
+       .rdy_to_xfer =          ft_send_xfer_rdy,
+       .on_hw_pending_cmd_timeout = ft_cmd_timeout,
+       .on_free_cmd =          ft_cmd_free,
+       .task_mgmt_fn_done =    ft_cmd_tm_done,
+       .detect =               ft_tgt_detect,
+       .release =              ft_tgt_release,
+       .report_aen =           ft_report_aen,
+       .enable_target =        ft_tgt_enable,
+       .is_target_enabled =    ft_tgt_enabled,
+       .name =                 FT_MODULE,
+};
+
+static int __init ft_module_init(void)
+{
+       int err;
+
+       err = scst_register_target_template(&ft_scst_template);
+       if (err)
+               return err;
+       err = fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov);
+       if (err) {
+               scst_unregister_target_template(&ft_scst_template);
+               return err;
+       }
+       blocking_notifier_chain_register(&fc_lport_notifier_head, &ft_notifier);
+       fc_lport_iterate(ft_lport_add, NULL);
+       return 0;
+}
+module_init(ft_module_init);
+
+static void __exit ft_module_exit(void)
+{
+       blocking_notifier_chain_unregister(&fc_lport_notifier_head,
+                                          &ft_notifier);
+       fc_fc4_deregister_provider(FC_TYPE_FCP, &ft_prov);
+       fc_lport_iterate(ft_lport_del, NULL);
+       scst_unregister_target_template(&ft_scst_template);
+       synchronize_rcu();
+}
+module_exit(ft_module_exit);
diff --git a/fcst/ft_sess.c b/fcst/ft_sess.c
new file mode 100644 (file)
index 0000000..5580dc0
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/hash.h>
+#include <asm/unaligned.h>
+#include <scsi/libfc.h>
+#include <scsi/fc/fc_els.h>
+#include "fcst.h"
+
+static int ft_tport_count;
+
+static ssize_t ft_format_wwn(char *buf, size_t len, u64 wwn)
+{
+       u8 b[8];
+
+       put_unaligned_be64(wwn, b);
+       return snprintf(buf, len,
+                "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+                b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+}
+
+/*
+ * Lookup or allocate target local port.
+ * Caller holds ft_lport_lock.
+ */
+static struct ft_tport *ft_tport_create(struct fc_lport *lport)
+{
+       struct ft_tport *tport;
+       char name[FT_NAMELEN];
+       int i;
+
+       ft_format_wwn(name, sizeof(name), lport->wwpn);
+       FT_SESS_DBG("create %s\n", name);
+
+       tport = rcu_dereference(lport->prov[FC_TYPE_FCP]);
+       if (tport)
+               return tport;
+
+       tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+       if (!tport)
+               return NULL;
+
+       tport->tgt = scst_register(&ft_scst_template, name);
+       if (!tport->tgt) {
+               kfree(tport);
+               return NULL;
+       }
+       scst_tgt_set_tgt_priv(tport->tgt, tport);
+       ft_tport_count++;
+
+       tport->lport = lport;
+       for (i = 0; i < FT_SESS_HASH_SIZE; i++)
+               INIT_HLIST_HEAD(&tport->hash[i]);
+
+       rcu_assign_pointer(lport->prov[FC_TYPE_FCP], tport);
+       return tport;
+}
+
+/*
+ * Free tport via RCU.
+ */
+static void ft_tport_rcu_free(struct rcu_head *rcu)
+{
+       struct ft_tport *tport = container_of(rcu, struct ft_tport, rcu);
+
+       kfree(tport);
+}
+
+/*
+ * Delete target local port, if any, associated with the local port.
+ * Caller holds ft_lport_lock.
+ */
+static void ft_tport_delete(struct ft_tport *tport)
+{
+       struct fc_lport *lport;
+       struct scst_tgt *tgt;
+
+       tgt = tport->tgt;
+       BUG_ON(!tgt);
+       FT_SESS_DBG("delete %s\n", scst_get_tgt_name(tgt));
+       scst_unregister(tgt);
+       lport = tport->lport;
+       BUG_ON(tport != lport->prov[FC_TYPE_FCP]);
+       rcu_assign_pointer(lport->prov[FC_TYPE_FCP], NULL);
+       tport->lport = NULL;
+       call_rcu(&tport->rcu, ft_tport_rcu_free);
+       ft_tport_count--;
+}
+
+/*
+ * Add local port.
+ * Called thru fc_lport_iterate().
+ */
+void ft_lport_add(struct fc_lport *lport, void *arg)
+{
+       mutex_lock(&ft_lport_lock);
+       ft_tport_create(lport);
+       mutex_unlock(&ft_lport_lock);
+}
+
+/*
+ * Delete local port.
+ * Called thru fc_lport_iterate().
+ */
+void ft_lport_del(struct fc_lport *lport, void *arg)
+{
+       struct ft_tport *tport;
+
+       mutex_lock(&ft_lport_lock);
+       tport = lport->prov[FC_TYPE_FCP];
+       if (tport)
+               ft_tport_delete(tport);
+       mutex_unlock(&ft_lport_lock);
+}
+
+/*
+ * Notification of local port change from libfc.
+ * Create or delete local port and associated tport.
+ */
+int ft_lport_notify(struct notifier_block *nb, unsigned long event, void *arg)
+{
+       struct fc_lport *lport = arg;
+
+       switch (event) {
+       case FC_LPORT_EV_ADD:
+               ft_lport_add(lport, NULL);
+               break;
+       case FC_LPORT_EV_DEL:
+               ft_lport_del(lport, NULL);
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+/*
+ * Find session in local port.
+ * Sessions and hash lists are RCU-protected.
+ * A reference is taken which must be eventually freed.
+ */
+static struct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id)
+{
+       struct ft_tport *tport;
+       struct hlist_head *head;
+       struct hlist_node *pos;
+       struct ft_sess *sess = NULL;
+
+       rcu_read_lock();
+       tport = rcu_dereference(lport->prov[FC_TYPE_FCP]);
+       if (!tport)
+               goto out;
+
+       head = &tport->hash[hash_32(port_id, FT_SESS_HASH_BITS)];
+       hlist_for_each_entry_rcu(sess, pos, head, hash) {
+               if (sess->port_id == port_id) {
+                       kref_get(&sess->kref);
+                       rcu_read_unlock();
+                       FT_SESS_DBG("port_id %x found %p\n", port_id, sess);
+                       return sess;
+               }
+       }
+out:
+       rcu_read_unlock();
+       FT_SESS_DBG("port_id %x not found\n", port_id);
+       return NULL;
+}
+
+/*
+ * Allocate session and enter it in the hash for the local port.
+ * Caller holds ft_lport_lock.
+ */
+static int ft_sess_create(struct ft_tport *tport, struct fc_rport_priv *rdata,
+                         u32 fcp_parm)
+{
+       struct ft_sess *sess;
+       struct scst_session *scst_sess;
+       struct hlist_head *head;
+       struct hlist_node *pos;
+       u32 port_id;
+       char name[FT_NAMELEN];
+
+       port_id = rdata->ids.port_id;
+       if (!rdata->maxframe_size) {
+               FT_SESS_DBG("port_id %x maxframe_size 0\n", port_id);
+               return FC_SPP_RESP_CONF;
+       }
+
+       head = &tport->hash[hash_32(port_id, FT_SESS_HASH_BITS)];
+       hlist_for_each_entry_rcu(sess, pos, head, hash) {
+               if (sess->port_id == port_id) {
+                       sess->params = fcp_parm;
+                       return 0;
+               }
+       }
+
+       sess = kzalloc(sizeof(*sess), GFP_KERNEL);
+       if (!sess)
+               return FC_SPP_RESP_RES;         /* out of resources */
+
+       ft_format_wwn(name, sizeof(name), rdata->ids.port_name);
+       FT_SESS_DBG("register %s\n", name);
+       scst_sess = scst_register_session(tport->tgt, 0, name, NULL, NULL);
+       if (!scst_sess) {
+               kfree(sess);
+               return FC_SPP_RESP_RES;         /* out of resources */
+       }
+       sess->scst_sess = scst_sess;
+       sess->tport = tport;
+       sess->port_id = port_id;
+       kref_init(&sess->kref);                 /* ref for table entry */
+       hlist_add_head_rcu(&sess->hash, head);
+       tport->sess_count++;
+
+       FT_SESS_DBG("port_id %x sess %p\n", port_id, sess);
+
+       sess->port_name = rdata->ids.port_name;
+       sess->max_payload = rdata->maxframe_size;
+       sess->max_lso_payload = rdata->maxframe_size;
+       if (tport->lport->seq_offload)
+               sess->max_lso_payload = tport->lport->lso_max;
+       sess->params = fcp_parm;
+
+       rdata->prli_count++;
+       scst_sess_set_tgt_priv(scst_sess, sess);
+       return 0;
+}
+
+/*
+ * Unhash the session.
+ * Caller holds ft_lport_lock.
+ */
+static void ft_sess_unhash(struct ft_sess *sess)
+{
+       struct ft_tport *tport = sess->tport;
+
+       hlist_del_rcu(&sess->hash);
+       BUG_ON(!tport->sess_count);
+       tport->sess_count--;
+       sess->port_id = -1;
+       sess->params = 0;
+}
+
+/*
+ * Delete session from hash.
+ * Caller holds ft_lport_lock.
+ */
+static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id)
+{
+       struct hlist_head *head;
+       struct hlist_node *pos;
+       struct ft_sess *sess;
+
+       head = &tport->hash[hash_32(port_id, FT_SESS_HASH_BITS)];
+       hlist_for_each_entry_rcu(sess, pos, head, hash) {
+               if (sess->port_id == port_id) {
+                       ft_sess_unhash(sess);
+                       return sess;
+               }
+       }
+       return NULL;
+}
+
+/*
+ * Remove session and send PRLO.
+ * This is called when the target is being deleted.
+ * Caller holds ft_lport_lock.
+ */
+static void ft_sess_close(struct ft_sess *sess)
+{
+       struct fc_lport *lport;
+       u32 port_id;
+
+       lport = sess->tport->lport;
+       port_id = sess->port_id;
+       if (port_id == -1)
+               return;
+       FT_SESS_DBG("port_id %x\n", port_id);
+       ft_sess_unhash(sess);
+       /* XXX should send LOGO or PRLO to rport */
+}
+
+/*
+ * libfc ops involving sessions.
+ */
+
+/*
+ * Handle PRLI (process login) request.
+ * This could be a PRLI we're sending or receiving.
+ * Caller holds ft_lport_lock.
+ */
+static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
+                         const struct fc_els_spp *rspp, struct fc_els_spp *spp)
+{
+       struct ft_tport *tport;
+       u32 fcp_parm;
+       int ret;
+
+       if (rspp->spp_flags & (FC_SPP_OPA_VAL | FC_SPP_RPA_VAL))
+               return FC_SPP_RESP_NO_PA;
+
+       /*
+        * If both target and initiator bits are off, the SPP is invalid.
+        */
+       fcp_parm = ntohl(rspp->spp_params);     /* requested parameters */
+       if (!(fcp_parm & (FCP_SPPF_INIT_FCN | FCP_SPPF_TARG_FCN)))
+               return FC_SPP_RESP_INVL;
+
+       /*
+        * Create session (image pair) only if requested by
+        * EST_IMG_PAIR flag and if the requestor is an initiator.
+        */
+       if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) {
+               spp->spp_flags |= FC_SPP_EST_IMG_PAIR;
+
+               if (!(fcp_parm & FCP_SPPF_INIT_FCN))
+                       return FC_SPP_RESP_CONF;
+               tport = rcu_dereference(rdata->local_port->prov[FC_TYPE_FCP]);
+               if (!tport || !tport->enabled)
+                       return 0;       /* not a target for this local port */
+
+               ret = ft_sess_create(tport, rdata, fcp_parm);
+               if (ret)
+                       return ret;
+       }
+
+       /*
+        * OR in our service parameters with other provider (initiator), if any.
+        * If the initiator indicates RETRY, we must support that, too.
+        * Don't force RETRY on the initiator, though.
+        */
+       fcp_parm = ntohl(spp->spp_params);      /* response parameters */
+       spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN);
+       return FC_SPP_RESP_ACK;
+}
+
+/**
+ * tcm_fcp_prli() - Handle incoming or outgoing PRLI for the FCP target
+ * @rdata: remote port private
+ * @spp_len: service parameter page length
+ * @rspp: received service parameter page (NULL for outgoing PRLI)
+ * @spp: response service parameter page
+ *
+ * Returns spp response code.
+ */
+int ft_prli(struct fc_rport_priv *rdata, u32 spp_len,
+           const struct fc_els_spp *rspp, struct fc_els_spp *spp)
+{
+       int ret;
+
+       FT_SESS_DBG("starting PRLI port_id %x\n", rdata->ids.port_id);
+       mutex_lock(&ft_lport_lock);
+       ret = ft_prli_locked(rdata, spp_len, rspp, spp);
+       mutex_unlock(&ft_lport_lock);
+       FT_SESS_DBG("port_id %x flags %x parms %x ret %x\n",
+                       rdata->ids.port_id,
+                       rspp->spp_flags,
+                       ntohl(spp->spp_params), ret);
+       return ret;
+}
+
+static void ft_sess_rcu_free(struct rcu_head *rcu)
+{
+       struct ft_sess *sess = container_of(rcu, struct ft_sess, rcu);
+
+       kfree(sess);
+}
+
+static void ft_sess_free(struct kref *kref)
+{
+       struct ft_sess *sess = container_of(kref, struct ft_sess, kref);
+       struct scst_session *scst_sess;
+
+       scst_sess = sess->scst_sess;
+       FT_SESS_DBG("unregister %s\n", scst_sess->initiator_name);
+       scst_unregister_session(scst_sess, 0, NULL);
+       call_rcu(&sess->rcu, ft_sess_rcu_free);
+}
+
+static void ft_sess_put(struct ft_sess *sess)
+{
+       int sess_held = atomic_read(&sess->kref.refcount);
+
+       BUG_ON(!sess_held);
+       kref_put(&sess->kref, ft_sess_free);
+}
+
+/*
+ * Delete ft_sess for PRLO.
+ * Called with ft_lport_lock held.
+ */
+static struct ft_sess *ft_sess_lookup_delete(struct fc_rport_priv *rdata)
+{
+       struct ft_sess *sess;
+       struct ft_tport *tport;
+
+       tport = rcu_dereference(rdata->local_port->prov[FC_TYPE_FCP]);
+       if (!tport)
+               return NULL;
+       sess = ft_sess_delete(tport, rdata->ids.port_id);
+       if (sess)
+               sess->params = 0;
+       return sess;
+}
+
+/*
+ * Handle PRLO.
+ */
+void ft_prlo(struct fc_rport_priv *rdata)
+{
+       struct ft_sess *sess;
+
+       mutex_lock(&ft_lport_lock);
+       sess = ft_sess_lookup_delete(rdata);
+       mutex_unlock(&ft_lport_lock);
+       if (!sess)
+               return;
+
+       /*
+        * Release the session hold from the table.
+        * When all command-starting threads have returned,
+        * kref will call ft_sess_free which will unregister
+        * the session.
+        * fcmds referencing the session are safe.
+        */
+       ft_sess_put(sess);              /* release from table */
+       rdata->prli_count--;
+}
+
+/*
+ * Handle incoming FCP request.
+ *
+ * Caller has verified that the frame is type FCP.
+ * Note that this may be called directly from the softirq context.
+ */
+void ft_recv(struct fc_lport *lport, struct fc_seq *sp, struct fc_frame *fp)
+{
+       struct ft_sess *sess;
+       struct fc_frame_header *fh;
+       u32 sid;
+
+       fh = fc_frame_header_get(fp);
+       sid = ntoh24(fh->fh_s_id);
+
+       FT_SESS_DBG("sid %x preempt %x\n", sid, preempt_count());
+
+       sess = ft_sess_get(lport, sid);
+       if (!sess) {
+               FT_SESS_DBG("sid %x sess lookup failed\n", sid);
+               lport->tt.exch_done(sp);
+               /* TBD XXX - if FCP_CMND, send LOGO */
+               fc_frame_free(fp);
+               return;
+       }
+       FT_SESS_DBG("sid %x sess lookup returned %p preempt %x\n",
+                       sid, sess, preempt_count());
+       ft_recv_req(sess, sp, fp);
+       ft_sess_put(sess);
+}
+
+/*
+ * Release all sessions for a target.
+ * Called through scst_unregister() as well as directly.
+ * Caller holds ft_lport_lock.
+ */
+int ft_tgt_release(struct scst_tgt *tgt)
+{
+       struct ft_tport *tport;
+       struct hlist_head *head;
+       struct hlist_node *pos;
+       struct ft_sess *sess;
+
+       tport = scst_tgt_get_tgt_priv(tgt);
+       tport->enabled = 0;
+       tport->lport->service_params &= ~FCP_SPPF_TARG_FCN;
+
+       for (head = tport->hash; head < &tport->hash[FT_SESS_HASH_SIZE]; head++)
+               hlist_for_each_entry_rcu(sess, pos, head, hash)
+                       ft_sess_close(sess);
+
+       synchronize_rcu();
+       return 0;
+}
+
+ssize_t ft_tgt_enable(struct scst_tgt *tgt, const char *buf, size_t len)
+{
+       struct ft_tport *tport;
+       ssize_t ret = len;
+
+       mutex_lock(&ft_lport_lock);
+       switch (buf[0]) {
+       case '0':
+               FT_SESS_DBG("disable tgt %s\n", tgt->tgt_name);
+               ft_tgt_release(tgt);
+               break;
+       case '1':
+               FT_SESS_DBG("enable tgt %s\n", tgt->tgt_name);
+               tport = scst_tgt_get_tgt_priv(tgt);
+               tport->enabled = 1;
+               tport->lport->service_params |= FCP_SPPF_TARG_FCN;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       mutex_unlock(&ft_lport_lock);
+       return ret;
+}
+
+bool ft_tgt_enabled(struct scst_tgt *tgt)
+{
+       struct ft_tport *tport;
+
+       tport = scst_tgt_get_tgt_priv(tgt);
+       return tport->enabled;
+}
+
+int ft_tgt_detect(struct scst_tgt_template *tt)
+{
+       return ft_tport_count;
+}
+
+/*
+ * Report AEN (Asynchronous Event Notification) from device to initiator.
+ * See notes in scst.h.
+ */
+int ft_report_aen(struct scst_aen *aen)
+{
+       struct ft_sess *sess;
+
+       sess = scst_sess_get_tgt_priv(scst_aen_get_sess(aen));
+       FT_SESS_DBG("AEN event %d sess to %x lun %lld\n",
+                   aen->event_fn, sess->port_id, scst_aen_get_lun(aen));
+       return SCST_AEN_RES_FAILED;     /* XXX TBD */
+}
diff --git a/fcst/linux-patches/01-prli-clean b/fcst/linux-patches/01-prli-clean
new file mode 100644 (file)
index 0000000..a57dc69
--- /dev/null
@@ -0,0 +1,241 @@
+libfc: recode incoming PRLI handling
+
+Reduce indentation in fc_rport_recv_prli_req() using gotos.
+Also add payload length checks.
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ drivers/scsi/libfc/fc_rport.c |  195 ++++++++++++++++++-----------------------
+ 1 files changed, 87 insertions(+), 108 deletions(-)
+
+
+---
+diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
+index 97923bb..09ec635 100644
+--- a/drivers/scsi/libfc/fc_rport.c
++++ b/drivers/scsi/libfc/fc_rport.c
+@@ -1441,136 +1441,115 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
+       struct fc_els_spp *spp; /* response spp */
+       unsigned int len;
+       unsigned int plen;
+-      enum fc_els_rjt_reason reason = ELS_RJT_UNAB;
+-      enum fc_els_rjt_explan explan = ELS_EXPL_NONE;
+       enum fc_els_spp_resp resp;
+       struct fc_seq_els_data rjt_data;
+       u32 f_ctl;
+       u32 fcp_parm;
+       u32 roles = FC_RPORT_ROLE_UNKNOWN;
+-      rjt_data.fp = NULL;
++      rjt_data.fp = NULL;
+       fh = fc_frame_header_get(rx_fp);
+       FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
+                    fc_rport_state(rdata));
+-      switch (rdata->rp_state) {
+-      case RPORT_ST_PRLI:
+-      case RPORT_ST_RTV:
+-      case RPORT_ST_READY:
+-      case RPORT_ST_ADISC:
+-              reason = ELS_RJT_NONE;
+-              break;
+-      default:
+-              fc_frame_free(rx_fp);
+-              return;
+-              break;
+-      }
+       len = fr_len(rx_fp) - sizeof(*fh);
+       pp = fc_frame_payload_get(rx_fp, sizeof(*pp));
+-      if (pp == NULL) {
+-              reason = ELS_RJT_PROT;
+-              explan = ELS_EXPL_INV_LEN;
+-      } else {
+-              plen = ntohs(pp->prli.prli_len);
+-              if ((plen % 4) != 0 || plen > len) {
+-                      reason = ELS_RJT_PROT;
+-                      explan = ELS_EXPL_INV_LEN;
+-              } else if (plen < len) {
+-                      len = plen;
+-              }
+-              plen = pp->prli.prli_spp_len;
+-              if ((plen % 4) != 0 || plen < sizeof(*spp) ||
+-                  plen > len || len < sizeof(*pp)) {
+-                      reason = ELS_RJT_PROT;
+-                      explan = ELS_EXPL_INV_LEN;
+-              }
+-              rspp = &pp->spp;
++      if (!pp)
++              goto reject_len;
++      plen = ntohs(pp->prli.prli_len);
++      if ((plen % 4) != 0 || plen > len || plen < 16)
++              goto reject_len;
++      if (plen < len)
++              len = plen;
++      plen = pp->prli.prli_spp_len;
++      if ((plen % 4) != 0 || plen < sizeof(*spp) ||
++          plen > len || len < sizeof(*pp) || plen < 12)
++              goto reject_len;
++      rspp = &pp->spp;
++
++      fp = fc_frame_alloc(lport, len);
++      if (!fp) {
++              rjt_data.reason = ELS_RJT_UNAB;
++              rjt_data.explan = ELS_EXPL_INSUF_RES;
++              goto reject;
+       }
+-      if (reason != ELS_RJT_NONE ||
+-          (fp = fc_frame_alloc(lport, len)) == NULL) {
+-              rjt_data.reason = reason;
+-              rjt_data.explan = explan;
+-              lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+-      } else {
+-              sp = lport->tt.seq_start_next(sp);
+-              WARN_ON(!sp);
+-              pp = fc_frame_payload_get(fp, len);
+-              WARN_ON(!pp);
+-              memset(pp, 0, len);
+-              pp->prli.prli_cmd = ELS_LS_ACC;
+-              pp->prli.prli_spp_len = plen;
+-              pp->prli.prli_len = htons(len);
+-              len -= sizeof(struct fc_els_prli);
+-
+-              /* reinitialize remote port roles */
+-              rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+-
+-              /*
+-               * Go through all the service parameter pages and build
+-               * response.  If plen indicates longer SPP than standard,
+-               * use that.  The entire response has been pre-cleared above.
+-               */
+-              spp = &pp->spp;
+-              while (len >= plen) {
+-                      spp->spp_type = rspp->spp_type;
+-                      spp->spp_type_ext = rspp->spp_type_ext;
+-                      spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
+-                      resp = FC_SPP_RESP_ACK;
+-                      if (rspp->spp_flags & FC_SPP_RPA_VAL)
+-                              resp = FC_SPP_RESP_NO_PA;
+-                      switch (rspp->spp_type) {
+-                      case 0: /* common to all FC-4 types */
+-                              break;
+-                      case FC_TYPE_FCP:
+-                              fcp_parm = ntohl(rspp->spp_params);
+-                              if (fcp_parm & FCP_SPPF_RETRY)
+-                                      rdata->flags |= FC_RP_FLAGS_RETRY;
+-                              rdata->supported_classes = FC_COS_CLASS3;
+-                              if (fcp_parm & FCP_SPPF_INIT_FCN)
+-                                      roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+-                              if (fcp_parm & FCP_SPPF_TARG_FCN)
+-                                      roles |= FC_RPORT_ROLE_FCP_TARGET;
+-                              rdata->ids.roles = roles;
+-
+-                              spp->spp_params =
+-                                      htonl(lport->service_params);
+-                              break;
+-                      default:
+-                              resp = FC_SPP_RESP_INVL;
+-                              break;
+-                      }
+-                      spp->spp_flags |= resp;
+-                      len -= plen;
+-                      rspp = (struct fc_els_spp *)((char *)rspp + plen);
+-                      spp = (struct fc_els_spp *)((char *)spp + plen);
+-              }
++      sp = lport->tt.seq_start_next(sp);
++      WARN_ON(!sp);
++      pp = fc_frame_payload_get(fp, len);
++      WARN_ON(!pp);
++      memset(pp, 0, len);
++      pp->prli.prli_cmd = ELS_LS_ACC;
++      pp->prli.prli_spp_len = plen;
++      pp->prli.prli_len = htons(len);
++      len -= sizeof(struct fc_els_prli);
+-              /*
+-               * Send LS_ACC.  If this fails, the originator should retry.
+-               */
+-              f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+-              f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+-              ep = fc_seq_exch(sp);
+-              fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+-                             FC_TYPE_ELS, f_ctl, 0);
+-              lport->tt.seq_send(lport, sp, fp);
++      /* reinitialize remote port roles */
++      rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+-              /*
+-               * Get lock and re-check state.
+-               */
+-              switch (rdata->rp_state) {
+-              case RPORT_ST_PRLI:
+-                      fc_rport_enter_ready(rdata);
++      /*
++       * Go through all the service parameter pages and build
++       * response.  If plen indicates longer SPP than standard,
++       * use that.  The entire response has been pre-cleared above.
++       */
++      spp = &pp->spp;
++      while (len >= plen) {
++              spp->spp_type = rspp->spp_type;
++              spp->spp_type_ext = rspp->spp_type_ext;
++              spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
++              resp = FC_SPP_RESP_ACK;
++
++              switch (rspp->spp_type) {
++              case 0: /* common to all FC-4 types */
+                       break;
+-              case RPORT_ST_READY:
+-              case RPORT_ST_ADISC:
++              case FC_TYPE_FCP:
++                      fcp_parm = ntohl(rspp->spp_params);
++                      if (fcp_parm & FCP_SPPF_RETRY)
++                              rdata->flags |= FC_RP_FLAGS_RETRY;
++                      rdata->supported_classes = FC_COS_CLASS3;
++                      if (fcp_parm & FCP_SPPF_INIT_FCN)
++                              roles |= FC_RPORT_ROLE_FCP_INITIATOR;
++                      if (fcp_parm & FCP_SPPF_TARG_FCN)
++                              roles |= FC_RPORT_ROLE_FCP_TARGET;
++                      rdata->ids.roles = roles;
++
++                      spp->spp_params = htonl(lport->service_params);
+                       break;
+               default:
++                      resp = FC_SPP_RESP_INVL;
+                       break;
+               }
++              spp->spp_flags |= resp;
++              len -= plen;
++              rspp = (struct fc_els_spp *)((char *)rspp + plen);
++              spp = (struct fc_els_spp *)((char *)spp + plen);
++      }
++
++      /*
++       * Send LS_ACC.  If this fails, the originator should retry.
++       */
++      f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
++      f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
++      ep = fc_seq_exch(sp);
++      fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
++                     FC_TYPE_ELS, f_ctl, 0);
++      lport->tt.seq_send(lport, sp, fp);
++
++      switch (rdata->rp_state) {
++      case RPORT_ST_PRLI:
++              fc_rport_enter_ready(rdata);
++              break;
++      default:
++              break;
+       }
++      goto drop;
++
++reject_len:
++      rjt_data.reason = ELS_RJT_PROT;
++      rjt_data.explan = ELS_EXPL_INV_LEN;
++reject:
++      lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
++drop:
+       fc_frame_free(rx_fp);
+ }
diff --git a/fcst/linux-patches/02-fc4 b/fcst/linux-patches/02-fc4
new file mode 100644 (file)
index 0000000..1f3f1ae
--- /dev/null
@@ -0,0 +1,487 @@
+libfc: add hook for FC-4 provider registration
+
+Allow FC-4 provider modules to hook into libfc, mostly for targets.
+This should allow any FC-4 module to handle PRLI requests and maintain
+process-association states.
+
+Each provider registers its ops with libfc and then will be called for
+any incoming PRLI for that FC-4 type on any instance.   The provider
+can decide whether to handle that particular instance using any method
+it likes, such as ACLs or other configuration information.
+
+A count is kept of the number of successful PRLIs from the remote port.
+Providers are called back with an implicit PRLO when the remote port
+is about to be deleted or has been reset.
+
+fc_lport_recv_req() now sends incoming FC-4 requests to FC-4 providers,
+and there is a built-in provider always registered for handling
+incoming ELS requests.
+
+The call to provider recv() routines uses rcu_read_lock()
+so that providers aren't removed during the call.  That lock is very
+cheap and shouldn't affect any performance on ELS requests.
+Providers can rely on the RCU lock to protect a session lookup as well.
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ drivers/scsi/libfc/fc_libfc.c |   60 ++++++++++++++++++
+ drivers/scsi/libfc/fc_libfc.h |   11 +++
+ drivers/scsi/libfc/fc_lport.c |   63 ++++++++++++++++---
+ drivers/scsi/libfc/fc_rport.c |  133 +++++++++++++++++++++++++++++++++--------
+ include/scsi/libfc.h          |   26 ++++++++
+ 5 files changed, 255 insertions(+), 38 deletions(-)
+
+
+---
+diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
+index 39f4b6a..ce0de44 100644
+--- a/drivers/scsi/libfc/fc_libfc.c
++++ b/drivers/scsi/libfc/fc_libfc.c
+@@ -34,6 +34,23 @@ unsigned int fc_debug_logging;
+ module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
+ MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
++DEFINE_MUTEX(fc_prov_mutex);
++
++/*
++ * Providers which primarily send requests and PRLIs.
++ */
++struct fc4_prov *fc_active_prov[FC_FC4_PROV_SIZE] = {
++      [0] = &fc_rport_t0_prov,
++      [FC_TYPE_FCP] = &fc_rport_fcp_init,
++};
++
++/*
++ * Providers which receive requests.
++ */
++struct fc4_prov *fc_passive_prov[FC_FC4_PROV_SIZE] = {
++      [FC_TYPE_ELS] = &fc_lport_els_prov,
++};
++
+ /**
+  * libfc_init() - Initialize libfc.ko
+  */
+@@ -132,3 +149,46 @@ u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
+       }
+       return copy_len;
+ }
++
++/**
++ * fc_fc4_register_provider() - register FC-4 upper-level provider.
++ * @type: FC-4 type, such as FC_TYPE_FCP
++ * @prov: structure describing provider including ops vector.
++ *
++ * Returns 0 on success, negative error otherwise.
++ */
++int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *prov)
++{
++      struct fc4_prov **prov_entry;
++      int ret = 0;
++
++      if (type >= FC_FC4_PROV_SIZE)
++              return -EINVAL;
++      mutex_lock(&fc_prov_mutex);
++      prov_entry = (prov->recv ? fc_passive_prov : fc_active_prov) + type;
++      if (*prov_entry)
++              ret = -EBUSY;
++      else
++              *prov_entry = prov;
++      mutex_unlock(&fc_prov_mutex);
++      return ret;
++}
++EXPORT_SYMBOL(fc_fc4_register_provider);
++
++/**
++ * fc_fc4_deregister_provider() - deregister FC-4 upper-level provider.
++ * @type: FC-4 type, such as FC_TYPE_FCP
++ * @prov: structure describing provider including ops vector.
++ */
++void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov)
++{
++      BUG_ON(type >= FC_FC4_PROV_SIZE);
++      mutex_lock(&fc_prov_mutex);
++      if (prov->recv)
++              rcu_assign_pointer(fc_passive_prov[type], NULL);
++      else
++              rcu_assign_pointer(fc_active_prov[type], NULL);
++      mutex_unlock(&fc_prov_mutex);
++      synchronize_rcu();
++}
++EXPORT_SYMBOL(fc_fc4_deregister_provider);
+diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
+index 741fd5c..c3d740c 100644
+--- a/drivers/scsi/libfc/fc_libfc.h
++++ b/drivers/scsi/libfc/fc_libfc.h
+@@ -82,6 +82,17 @@ extern unsigned int fc_debug_logging;
+                               (lport)->host->host_no, ##args))
+ /*
++ * FC-4 Providers.
++ */
++extern struct fc4_prov *fc_active_prov[];     /* providers without recv */
++extern struct fc4_prov *fc_passive_prov[];    /* providers with recv */
++extern struct mutex fc_prov_mutex;            /* lock over table changes */
++
++extern struct fc4_prov fc_rport_t0_prov;      /* type 0 provider */
++extern struct fc4_prov fc_lport_els_prov;     /* ELS provider */
++extern struct fc4_prov fc_rport_fcp_init;     /* FCP initiator provider */
++
++/*
+  * Set up direct-data placement for this I/O request
+  */
+ void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid);
+diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
+index 7ec8ce7..35149f0 100644
+--- a/drivers/scsi/libfc/fc_lport.c
++++ b/drivers/scsi/libfc/fc_lport.c
+@@ -847,7 +847,7 @@ out:
+ }
+ /**
+- * fc_lport_recv_req() - The generic lport request handler
++ * fc_lport_recv_els_req() - The generic lport ELS request handler
+  * @lport: The local port that received the request
+  * @sp:          The sequence the request is on
+  * @fp:          The request frame
+@@ -858,8 +858,8 @@ out:
+  * Locking Note: This function should not be called with the lport
+  *             lock held becuase it will grab the lock.
+  */
+-static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
+-                            struct fc_frame *fp)
++static void fc_lport_recv_els_req(struct fc_lport *lport, struct fc_seq *sp,
++                                struct fc_frame *fp)
+ {
+       struct fc_frame_header *fh = fc_frame_header_get(fp);
+       void (*recv) (struct fc_seq *, struct fc_frame *, struct fc_lport *);
+@@ -873,8 +873,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
+        */
+       if (!lport->link_up)
+               fc_frame_free(fp);
+-      else if (fh->fh_type == FC_TYPE_ELS &&
+-               fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
++      else {
+               /*
+                * Check opcode.
+                */
+@@ -903,17 +902,59 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
+               }
+               recv(sp, fp, lport);
+-      } else {
+-              FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n",
+-                           fr_eof(fp));
+-              fc_frame_free(fp);
+       }
+       mutex_unlock(&lport->lp_mutex);
++      lport->tt.exch_done(sp);
++}
++
++static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len,
++                           const struct fc_els_spp *spp_in,
++                           struct fc_els_spp *spp_out)
++{
++      return FC_SPP_RESP_INVL;
++}
++
++struct fc4_prov fc_lport_els_prov = {
++      .prli = fc_lport_els_prli,
++      .recv = fc_lport_recv_els_req,
++};
++
++/**
++ * fc_lport_recv_req() - The generic lport request handler
++ * @lport: The lport that received the request
++ * @sp: The sequence the request is on
++ * @fp: The frame the request is in
++ *
++ * Locking Note: This function should not be called with the lport
++ *             lock held becuase it may grab the lock.
++ */
++static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
++                            struct fc_frame *fp)
++{
++      struct fc_frame_header *fh = fc_frame_header_get(fp);
++      struct fc4_prov *prov;
+       /*
+-       *  The common exch_done for all request may not be good
+-       *  if any request requires longer hold on exhange. XXX
++       * Use RCU read lock and module_lock to be sure module doesn't
++       * deregister and get unloaded while we're calling it.
++       * try_module_get() is inlined and accepts a NULL parameter.
++       * Only ELSes and FCP target ops should come through here.
++       * The locking is unfortunate, and a better scheme is being sought.
+        */
++      rcu_read_lock();
++      if (fh->fh_type >= FC_FC4_PROV_SIZE)
++              goto drop;
++      prov = rcu_dereference(fc_passive_prov[fh->fh_type]);
++      if (!prov || !try_module_get(prov->module))
++              goto drop;
++      rcu_read_unlock();
++      prov->recv(lport, sp, fp);
++      module_put(prov->module);
++      return;
++drop:
++      rcu_read_unlock();
++      FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type);
++      fc_frame_free(fp);
+       lport->tt.exch_done(sp);
+ }
+diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
+index 09ec635..3713060 100644
+--- a/drivers/scsi/libfc/fc_rport.c
++++ b/drivers/scsi/libfc/fc_rport.c
+@@ -245,6 +245,8 @@ static void fc_rport_work(struct work_struct *work)
+       struct fc_rport_operations *rport_ops;
+       struct fc_rport_identifiers ids;
+       struct fc_rport *rport;
++      struct fc4_prov *prov;
++      u8 type;
+       int restart = 0;
+       mutex_lock(&rdata->rp_mutex);
+@@ -294,6 +296,15 @@ static void fc_rport_work(struct work_struct *work)
+       case RPORT_EV_FAILED:
+       case RPORT_EV_LOGO:
+       case RPORT_EV_STOP:
++              if (rdata->prli_count) {
++                      mutex_lock(&fc_prov_mutex);
++                      for (type = 1; type < FC_FC4_PROV_SIZE; type++) {
++                              prov = fc_passive_prov[type];
++                              if (prov && prov->prlo)
++                                      prov->prlo(rdata);
++                      }
++                      mutex_unlock(&fc_prov_mutex);
++              }
+               port_id = rdata->ids.port_id;
+               mutex_unlock(&rdata->rp_mutex);
+@@ -1433,6 +1444,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
+       struct fc_exch *ep;
+       struct fc_frame *fp;
+       struct fc_frame_header *fh;
++      struct fc4_prov *prov;
+       struct {
+               struct fc_els_prli prli;
+               struct fc_els_spp spp;
+@@ -1442,10 +1454,9 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
+       unsigned int len;
+       unsigned int plen;
+       enum fc_els_spp_resp resp;
++      enum fc_els_spp_resp passive;
+       struct fc_seq_els_data rjt_data;
+       u32 f_ctl;
+-      u32 fcp_parm;
+-      u32 roles = FC_RPORT_ROLE_UNKNOWN;
+       rjt_data.fp = NULL;
+       fh = fc_frame_header_get(rx_fp);
+@@ -1484,46 +1495,41 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
+       pp->prli.prli_len = htons(len);
+       len -= sizeof(struct fc_els_prli);
+-      /* reinitialize remote port roles */
+-      rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+-
+       /*
+        * Go through all the service parameter pages and build
+        * response.  If plen indicates longer SPP than standard,
+        * use that.  The entire response has been pre-cleared above.
+        */
+       spp = &pp->spp;
++      mutex_lock(&fc_prov_mutex);
+       while (len >= plen) {
+               spp->spp_type = rspp->spp_type;
+               spp->spp_type_ext = rspp->spp_type_ext;
+-              spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
+-              resp = FC_SPP_RESP_ACK;
+-
+-              switch (rspp->spp_type) {
+-              case 0: /* common to all FC-4 types */
+-                      break;
+-              case FC_TYPE_FCP:
+-                      fcp_parm = ntohl(rspp->spp_params);
+-                      if (fcp_parm & FCP_SPPF_RETRY)
+-                              rdata->flags |= FC_RP_FLAGS_RETRY;
+-                      rdata->supported_classes = FC_COS_CLASS3;
+-                      if (fcp_parm & FCP_SPPF_INIT_FCN)
+-                              roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+-                      if (fcp_parm & FCP_SPPF_TARG_FCN)
+-                              roles |= FC_RPORT_ROLE_FCP_TARGET;
+-                      rdata->ids.roles = roles;
+-
+-                      spp->spp_params = htonl(lport->service_params);
+-                      break;
+-              default:
+-                      resp = FC_SPP_RESP_INVL;
+-                      break;
++              resp = 0;
++
++              if (rspp->spp_type < FC_FC4_PROV_SIZE) {
++                      prov = fc_active_prov[rspp->spp_type];
++                      if (prov)
++                              resp = prov->prli(rdata, plen, rspp, spp);
++                      prov = fc_passive_prov[rspp->spp_type];
++                      if (prov) {
++                              passive = prov->prli(rdata, plen, rspp, spp);
++                              if (!resp || passive == FC_SPP_RESP_ACK)
++                                      resp = passive;
++                      }
++              }
++              if (!resp) {
++                      if (spp->spp_flags & FC_SPP_EST_IMG_PAIR)
++                              resp |= FC_SPP_RESP_CONF;
++                      else
++                              resp |= FC_SPP_RESP_INVL;
+               }
+               spp->spp_flags |= resp;
+               len -= plen;
+               rspp = (struct fc_els_spp *)((char *)rspp + plen);
+               spp = (struct fc_els_spp *)((char *)spp + plen);
+       }
++      mutex_unlock(&fc_prov_mutex);
+       /*
+        * Send LS_ACC.  If this fails, the originator should retry.
+@@ -1668,6 +1674,79 @@ int fc_rport_init(struct fc_lport *lport)
+ EXPORT_SYMBOL(fc_rport_init);
+ /**
++ * fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator.
++ * @rdata: remote port private
++ * @spp_len: service parameter page length
++ * @rspp: received service parameter page
++ * @spp: response service parameter page
++ *
++ * Returns the value for the response code to be placed in spp_flags;
++ * Returns 0 if not an initiator.
++ */
++static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len,
++                           const struct fc_els_spp *rspp,
++                           struct fc_els_spp *spp)
++{
++      struct fc_lport *lport = rdata->local_port;
++      u32 fcp_parm;
++
++      fcp_parm = ntohl(rspp->spp_params);
++      rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
++      if (fcp_parm & FCP_SPPF_INIT_FCN)
++              rdata->ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
++      if (fcp_parm & FCP_SPPF_TARG_FCN)
++              rdata->ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
++      if (fcp_parm & FCP_SPPF_RETRY)
++              rdata->flags |= FC_RP_FLAGS_RETRY;
++      rdata->supported_classes = FC_COS_CLASS3;
++
++      if (!(lport->service_params & FC_RPORT_ROLE_FCP_INITIATOR))
++              return 0;
++
++      spp->spp_flags |= rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
++
++      /*
++       * OR in our service parameters with other providers (target), if any.
++       */
++      fcp_parm = ntohl(spp->spp_params);
++      spp->spp_params = htonl(fcp_parm | lport->service_params);
++      return FC_SPP_RESP_ACK;
++}
++
++/*
++ * FC-4 provider ops for FCP initiator.
++ */
++struct fc4_prov fc_rport_fcp_init = {
++      .prli = fc_rport_fcp_prli,
++};
++
++/**
++ * fc_rport_t0_prli() - Handle incoming PRLI parameters for type 0
++ * @rdata: remote port private
++ * @spp_len: service parameter page length
++ * @rspp: received service parameter page
++ * @spp: response service parameter page
++ */
++static int fc_rport_t0_prli(struct fc_rport_priv *rdata, u32 spp_len,
++                          const struct fc_els_spp *rspp,
++                          struct fc_els_spp *spp)
++{
++      if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR)
++              return FC_SPP_RESP_INVL;
++      return FC_SPP_RESP_ACK;
++}
++
++/*
++ * FC-4 provider ops for type 0 service parameters.
++ *
++ * This handles the special case of type 0 which is always successful
++ * but doesn't do anything otherwise.
++ */
++struct fc4_prov fc_rport_t0_prov = {
++      .prli = fc_rport_t0_prli,
++};
++
++/**
+  * fc_setup_rport() - Initialize the rport_event_queue
+  */
+ int fc_setup_rport()
+diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
+index 4b912ee..9403d4e 100644
+--- a/include/scsi/libfc.h
++++ b/include/scsi/libfc.h
+@@ -35,6 +35,8 @@
+ #include <scsi/fc_frame.h>
++#define       FC_FC4_PROV_SIZE        (FC_TYPE_FCP + 1)       /* size of tables */
++
+ /*
+  * libfc error codes
+  */
+@@ -190,6 +192,7 @@ struct fc_rport_libfc_priv {
+  * @rp_mutex:       The mutex that protects the remote port
+  * @retry_work:     Handle for retries
+  * @event_callback: Callback when READY, FAILED or LOGO states complete
++ * @prli_count:     Count of open PRLI sessions in providers
+  */
+ struct fc_rport_priv {
+       struct fc_lport             *local_port;
+@@ -211,6 +214,7 @@ struct fc_rport_priv {
+       struct list_head            peers;
+       struct work_struct          event_work;
+       u32                         supported_classes;
++      u16                         prli_count;
+ };
+ /**
+@@ -850,6 +854,28 @@ struct fc_lport {
+       struct delayed_work            retry_work;
+ };
++/**
++ * struct fc4_prov - FC-4 provider registration
++ * @prli:             Handler for incoming PRLI
++ * @prlo:             Handler for session reset
++ * @recv:             Handler for incoming request
++ * @module:           Pointer to module.  May be NULL.
++ */
++struct fc4_prov {
++      int (*prli)(struct fc_rport_priv *, u32 spp_len,
++                  const struct fc_els_spp *spp_in,
++                  struct fc_els_spp *spp_out);
++      void (*prlo)(struct fc_rport_priv *);
++      void (*recv)(struct fc_lport *, struct fc_seq *, struct fc_frame *);
++      struct module *module;
++};
++
++/*
++ * Register FC-4 provider with libfc.
++ */
++int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *);
++void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *);
++
+ /*
+  * FC_LPORT HELPER FUNCTIONS
+  *****************************/
diff --git a/fcst/linux-patches/03-seq-init b/fcst/linux-patches/03-seq-init
new file mode 100644 (file)
index 0000000..86cf85c
--- /dev/null
@@ -0,0 +1,31 @@
+libfc: fix sequence-initiative WARN in fc_seq_start_next
+
+The target code was getting a warning that sequence initiative
+wasn't held when starting the response sequence after sending
+the data sequence.
+
+The bug was that sequence initiative was cleared due to the
+END_SEQ flag being on.  The intent may have been to check LAST_SEQ.
+Change just to check SEQ_INIT.
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ drivers/scsi/libfc/fc_exch.c |    2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+
+---
+diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
+index 7f43647..1ad1ded 100644
+--- a/drivers/scsi/libfc/fc_exch.c
++++ b/drivers/scsi/libfc/fc_exch.c
+@@ -488,7 +488,7 @@ static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp,
+        */
+       spin_lock_bh(&ep->ex_lock);
+       ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ;   /* not first seq */
+-      if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT))
++      if (f_ctl & FC_FC_SEQ_INIT)
+               ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+       spin_unlock_bh(&ep->ex_lock);
+       return error;
diff --git a/fcst/linux-patches/04-seq-set-resp b/fcst/linux-patches/04-seq-set-resp
new file mode 100644 (file)
index 0000000..22abfca
--- /dev/null
@@ -0,0 +1,84 @@
+libfc: add method for setting handler for incoming exchange
+
+Add a method for setting handler for incoming exchange.
+For multi-sequence exchanges, this allows the target driver
+to add a response handler for handling subsequent sequences,
+and exchange manager resets.
+
+The new function is called fc_seq_set_resp().
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ drivers/scsi/libfc/fc_exch.c |   19 +++++++++++++++++++
+ include/scsi/libfc.h         |   11 ++++++++++-
+ 2 files changed, 29 insertions(+), 1 deletions(-)
+
+
+---
+diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
+index 1ad1ded..aee3c59 100644
+--- a/drivers/scsi/libfc/fc_exch.c
++++ b/drivers/scsi/libfc/fc_exch.c
+@@ -545,6 +545,22 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
+       return sp;
+ }
++/*
++ * Set the response handler for the exchange associated with a sequence.
++ */
++static void fc_seq_set_resp(struct fc_seq *sp,
++                          void (*resp)(struct fc_seq *, struct fc_frame *,
++                                       void *),
++                          void *arg)
++{
++      struct fc_exch *ep = fc_seq_exch(sp);
++
++      spin_lock_bh(&ep->ex_lock);
++      ep->resp = resp;
++      ep->arg = arg;
++      spin_unlock_bh(&ep->ex_lock);
++}
++
+ /**
+  * fc_seq_exch_abort() - Abort an exchange and sequence
+  * @req_sp:   The sequence to be aborted
+@@ -2280,6 +2296,9 @@ int fc_exch_init(struct fc_lport *lport)
+       if (!lport->tt.seq_start_next)
+               lport->tt.seq_start_next = fc_seq_start_next;
++      if (!lport->tt.seq_set_resp)
++              lport->tt.seq_set_resp = fc_seq_set_resp;
++
+       if (!lport->tt.exch_seq_send)
+               lport->tt.exch_seq_send = fc_exch_seq_send;
+diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
+index 9403d4e..87204da 100644
+--- a/include/scsi/libfc.h
++++ b/include/scsi/libfc.h
+@@ -566,6 +566,16 @@ struct libfc_function_template {
+       struct fc_seq *(*seq_start_next)(struct fc_seq *);
+       /*
++       * Set a response handler for the exchange of the sequence.
++       *
++       * STATUS: OPTIONAL
++       */
++      void (*seq_set_resp)(struct fc_seq *sp,
++                           void (*resp)(struct fc_seq *, struct fc_frame *,
++                                        void *),
++                           void *arg);
++
++      /*
+        * Reset an exchange manager, completing all sequences and exchanges.
+        * If s_id is non-zero, reset only exchanges originating from that FID.
+        * If d_id is non-zero, reset only exchanges sending to that FID.
+@@ -1058,7 +1068,6 @@ struct fc_seq *fc_elsct_send(struct fc_lport *, u32 did,
+ void fc_lport_flogi_resp(struct fc_seq *, struct fc_frame *, void *);
+ void fc_lport_logo_resp(struct fc_seq *, struct fc_frame *, void *);
+-
+ /*
+  * EXCHANGE MANAGER LAYER
+  *****************************/
diff --git a/fcst/linux-patches/05-lport-hook b/fcst/linux-patches/05-lport-hook
new file mode 100644 (file)
index 0000000..0b59bc3
--- /dev/null
@@ -0,0 +1,36 @@
+libfc: add local port hook for provider session lookup
+
+The target provider needs a per-instance lookup table
+or other way to lookup sessions quickly without going through
+a linear list or serializing too much.
+
+Add a simple void * array indexed by FC-4 type to the fc_lport.
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ include/scsi/libfc.h |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+
+---
+diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
+index 87204da..6817fe3 100644
+--- a/include/scsi/libfc.h
++++ b/include/scsi/libfc.h
+@@ -810,6 +810,7 @@ struct fc_disc {
+  * @lp_mutex:              Mutex to protect the local port
+  * @list:                  Handle for list of local ports
+  * @retry_work:            Handle to local port for delayed retry context
++ * @prov:                Pointers available for use by passive FC-4 providers
+  */
+ struct fc_lport {
+       /* Associations */
+@@ -862,6 +863,7 @@ struct fc_lport {
+       struct mutex                   lp_mutex;
+       struct list_head               list;
+       struct delayed_work            retry_work;
++      void                           *prov[FC_FC4_PROV_SIZE];
+ };
+ /**
diff --git a/fcst/linux-patches/06-lport-notify b/fcst/linux-patches/06-lport-notify
new file mode 100644 (file)
index 0000000..015c12e
--- /dev/null
@@ -0,0 +1,167 @@
+libfc: add hook to notify providers of local port changes
+
+When an SCST provider is registered, it needs to know what
+local ports are available for configuration as targets.
+
+Add a notifier chain that is invoked when any local port
+that is added or deleted.
+
+Maintain a global list of local ports and add an
+interator function that calls a given function for
+every existing local port.  This is used when first
+loading a provider.
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ drivers/scsi/libfc/fc_libfc.c |   41 +++++++++++++++++++++++++++++++++++++++++
+ drivers/scsi/libfc/fc_libfc.h |    2 ++
+ drivers/scsi/libfc/fc_lport.c |    2 ++
+ include/scsi/libfc.h          |   14 +++++++++++++-
+ 4 files changed, 58 insertions(+), 1 deletions(-)
+
+
+---
+diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
+index ce0de44..abd108a 100644
+--- a/drivers/scsi/libfc/fc_libfc.c
++++ b/drivers/scsi/libfc/fc_libfc.c
+@@ -35,6 +35,10 @@ module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
+ MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+ DEFINE_MUTEX(fc_prov_mutex);
++static LIST_HEAD(fc_local_ports);
++struct blocking_notifier_head fc_lport_notifier_head =
++              BLOCKING_NOTIFIER_INIT(fc_lport_notifier_head);
++EXPORT_SYMBOL(fc_lport_notifier_head);
+ /*
+  * Providers which primarily send requests and PRLIs.
+@@ -150,6 +154,17 @@ u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
+       return copy_len;
+ }
++void fc_lport_iterate(void (*notify)(struct fc_lport *, void *), void *arg)
++{
++      struct fc_lport *lport;
++
++      mutex_lock(&fc_prov_mutex);
++      list_for_each_entry(lport, &fc_local_ports, lport_list)
++              notify(lport, arg);
++      mutex_unlock(&fc_prov_mutex);
++}
++EXPORT_SYMBOL(fc_lport_iterate);
++
+ /**
+  * fc_fc4_register_provider() - register FC-4 upper-level provider.
+  * @type: FC-4 type, such as FC_TYPE_FCP
+@@ -192,3 +207,29 @@ void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov)
+       synchronize_rcu();
+ }
+ EXPORT_SYMBOL(fc_fc4_deregister_provider);
++
++/**
++ * fc_fc4_add_lport() - add new local port to list and run notifiers.
++ * @lport:  The new local port.
++ */
++void fc_fc4_add_lport(struct fc_lport *lport)
++{
++      mutex_lock(&fc_prov_mutex);
++      list_add_tail(&lport->lport_list, &fc_local_ports);
++      blocking_notifier_call_chain(&fc_lport_notifier_head,
++                                   FC_LPORT_EV_ADD, lport);
++      mutex_unlock(&fc_prov_mutex);
++}
++
++/**
++ * fc_fc4_del_lport() - remove local port from list and run notifiers.
++ * @lport:  The new local port.
++ */
++void fc_fc4_del_lport(struct fc_lport *lport)
++{
++      mutex_lock(&fc_prov_mutex);
++      list_del(&lport->lport_list);
++      blocking_notifier_call_chain(&fc_lport_notifier_head,
++                                   FC_LPORT_EV_DEL, lport);
++      mutex_unlock(&fc_prov_mutex);
++}
+diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
+index c3d740c..1bde8e2 100644
+--- a/drivers/scsi/libfc/fc_libfc.h
++++ b/drivers/scsi/libfc/fc_libfc.h
+@@ -111,6 +111,8 @@ void fc_destroy_fcp(void);
+  * Internal libfc functions
+  */
+ const char *fc_els_resp_type(struct fc_frame *);
++extern void fc_fc4_add_lport(struct fc_lport *);
++extern void fc_fc4_del_lport(struct fc_lport *);
+ /*
+  * Copies a buffer into an sg list
+diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
+index 35149f0..507b6c5 100644
+--- a/drivers/scsi/libfc/fc_lport.c
++++ b/drivers/scsi/libfc/fc_lport.c
+@@ -654,6 +654,7 @@ int fc_lport_destroy(struct fc_lport *lport)
+       lport->tt.fcp_abort_io(lport);
+       lport->tt.disc_stop_final(lport);
+       lport->tt.exch_mgr_reset(lport, 0, 0);
++      fc_fc4_del_lport(lport);
+       return 0;
+ }
+ EXPORT_SYMBOL(fc_lport_destroy);
+@@ -1638,6 +1639,7 @@ int fc_lport_init(struct fc_lport *lport)
+               fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT;
+       if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
+               fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
++      fc_fc4_add_lport(lport);
+       return 0;
+ }
+diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
+index 6817fe3..12fe787 100644
+--- a/include/scsi/libfc.h
++++ b/include/scsi/libfc.h
+@@ -770,6 +770,15 @@ struct fc_disc {
+                             enum fc_disc_event);
+ };
++/*
++ * Local port notifier and events.
++ */
++extern struct blocking_notifier_head fc_lport_notifier_head;
++enum fc_lport_event {
++      FC_LPORT_EV_ADD,
++      FC_LPORT_EV_DEL,
++};
++
+ /**
+  * struct fc_lport - Local port
+  * @host:                  The SCSI host associated with a local port
+@@ -808,8 +817,9 @@ struct fc_disc {
+  * @lso_max:               The maximum large offload send size
+  * @fcts:                  FC-4 type mask
+  * @lp_mutex:              Mutex to protect the local port
+- * @list:                  Handle for list of local ports
++ * @list:                  Linkage on list of vport peers
+  * @retry_work:            Handle to local port for delayed retry context
++ * @lport_list:            Linkage on module-wide list of local ports
+  * @prov:                Pointers available for use by passive FC-4 providers
+  */
+ struct fc_lport {
+@@ -863,6 +873,7 @@ struct fc_lport {
+       struct mutex                   lp_mutex;
+       struct list_head               list;
+       struct delayed_work            retry_work;
++      struct list_head               lport_list;
+       void                           *prov[FC_FC4_PROV_SIZE];
+ };
+@@ -1026,6 +1037,7 @@ int fc_set_mfs(struct fc_lport *, u32 mfs);
+ struct fc_lport *libfc_vport_create(struct fc_vport *, int privsize);
+ struct fc_lport *fc_vport_id_lookup(struct fc_lport *, u32 port_id);
+ int fc_lport_bsg_request(struct fc_bsg_job *);
++void fc_lport_iterate(void (*func)(struct fc_lport *, void *), void *);
+ /*
+  * REMOTE PORT LAYER
diff --git a/fcst/linux-patches/07-pta-mask b/fcst/linux-patches/07-pta-mask
new file mode 100644 (file)
index 0000000..2f496d9
--- /dev/null
@@ -0,0 +1,25 @@
+libfc: add definition for task attribute mask
+
+The FCP command header definition should define a mask for
+the task attribute field.  This adds that #define.
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ include/scsi/fc/fc_fcp.h |    1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+
+---
+diff --git a/include/scsi/fc/fc_fcp.h b/include/scsi/fc/fc_fcp.h
+index 747e2c7..8e9b222 100644
+--- a/include/scsi/fc/fc_fcp.h
++++ b/include/scsi/fc/fc_fcp.h
+@@ -76,6 +76,7 @@ struct fcp_cmnd32 {
+ #define       FCP_PTA_HEADQ       1   /* head of queue task attribute */
+ #define       FCP_PTA_ORDERED     2   /* ordered task attribute */
+ #define       FCP_PTA_ACA         4   /* auto. contigent allegiance */
++#define       FCP_PTA_MASK        7   /* mask for task attribute field */
+ #define       FCP_PRI_SHIFT       3   /* priority field starts in bit 3 */
+ #define       FCP_PRI_RESVD_MASK  0x80        /* reserved bits in priority field */
diff --git a/fcst/linux-patches/08-ptp-ref b/fcst/linux-patches/08-ptp-ref
new file mode 100644 (file)
index 0000000..cf23c6b
--- /dev/null
@@ -0,0 +1,49 @@
+libfc: fix oops in point-to-point mode
+
+In point-to-point mode, if the PLOGI to the remote port times
+out, it can get deleted by the remote port module.  Since there's
+no reference by the local port, lport->ptp_data points to a freed
+rport, and when the local port is reset and tries to logout again,
+an oops occurs in mutex_lock_nested().
+
+Hold a reference count on the point-to-point rdata.
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ drivers/scsi/libfc/fc_lport.c |   11 +++++++++--
+ 1 files changed, 9 insertions(+), 2 deletions(-)
+
+
+---
+diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
+index 507b6c5..a728e99 100644
+--- a/drivers/scsi/libfc/fc_lport.c
++++ b/drivers/scsi/libfc/fc_lport.c
+@@ -227,9 +227,12 @@ static void fc_lport_ptp_setup(struct fc_lport *lport,
+                              u64 remote_wwnn)
+ {
+       mutex_lock(&lport->disc.disc_mutex);
+-      if (lport->ptp_rdata)
++      if (lport->ptp_rdata) {
+               lport->tt.rport_logoff(lport->ptp_rdata);
++              kref_put(&lport->ptp_rdata->kref, lport->tt.rport_destroy);
++      }
+       lport->ptp_rdata = lport->tt.rport_create(lport, remote_fid);
++      kref_get(&lport->ptp_rdata->kref);
+       lport->ptp_rdata->ids.port_name = remote_wwpn;
+       lport->ptp_rdata->ids.node_name = remote_wwnn;
+       mutex_unlock(&lport->disc.disc_mutex);
+@@ -988,7 +991,11 @@ static void fc_lport_reset_locked(struct fc_lport *lport)
+       if (lport->dns_rdata)
+               lport->tt.rport_logoff(lport->dns_rdata);
+-      lport->ptp_rdata = NULL;
++      if (lport->ptp_rdata) {
++              lport->tt.rport_logoff(lport->ptp_rdata);
++              kref_put(&lport->ptp_rdata->kref, lport->tt.rport_destroy);
++              lport->ptp_rdata = NULL;
++      }
+       lport->tt.disc_stop(lport);
diff --git a/fcst/linux-patches/09-ptp-type b/fcst/linux-patches/09-ptp-type
new file mode 100644 (file)
index 0000000..07d783f
--- /dev/null
@@ -0,0 +1,30 @@
+fcoe: call fcoe_ctlr_els_send even for ELS responses
+
+In point-to-point mode, the destination MAC address for
+the FLOGI response was zero because the LS_ACC for the FLOGI
+wasn't getting intercepted by FIP.
+
+Change to call fcoe_ctlr_els_send when sending any ELS,
+not just requests.
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ drivers/scsi/fcoe/fcoe.c |    2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+
+---
+diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
+index 2f47ae7..2f3b73c 100644
+--- a/drivers/scsi/fcoe/fcoe.c
++++ b/drivers/scsi/fcoe/fcoe.c
+@@ -1443,7 +1443,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
+               return 0;
+       }
+-      if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
++      if (unlikely(fh->fh_type == FC_TYPE_ELS) &&
+           fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb))
+               return 0;
diff --git a/fcst/linux-patches/10-non-fip b/fcst/linux-patches/10-non-fip
new file mode 100644 (file)
index 0000000..0501ad4
--- /dev/null
@@ -0,0 +1,33 @@
+libfcoe: fix debug message entering non-FIP mode
+
+The debug message that indicated we are using non-FIP mode was
+being printed only if we were already in non-FIP mode.
+Also changed the message text to make it more clear the mode
+is being set, not that the message is indicating how FLOGI
+was received.
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ drivers/scsi/fcoe/libfcoe.c |    6 +++---
+ 1 files changed, 3 insertions(+), 3 deletions(-)
+
+
+---
+diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
+index 511cb6b..b2af53f 100644
+--- a/drivers/scsi/fcoe/libfcoe.c
++++ b/drivers/scsi/fcoe/libfcoe.c
+@@ -1333,9 +1333,9 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
+               if (fip->state == FIP_ST_AUTO || fip->state == FIP_ST_NON_FIP) {
+                       memcpy(fip->dest_addr, sa, ETH_ALEN);
+                       fip->map_dest = 0;
+-                      if (fip->state == FIP_ST_NON_FIP)
+-                              LIBFCOE_FIP_DBG(fip, "received FLOGI REQ, "
+-                                              "using non-FIP mode\n");
++                      if (fip->state == FIP_ST_AUTO)
++                              LIBFCOE_FIP_DBG(fip, "received non-FIP FLOGI. "
++                                              "Setting non-FIP mode\n");
+                       fip->state = FIP_ST_NON_FIP;
+               }
+               spin_unlock_bh(&fip->lock);
diff --git a/fcst/linux-patches/11-ptp-flogi-recv b/fcst/linux-patches/11-ptp-flogi-recv
new file mode 100644 (file)
index 0000000..d6b192a
--- /dev/null
@@ -0,0 +1,63 @@
+fcoe: save gateway address when receiving FLOGI request
+
+In point-to-point mode, we need to save the source MAC
+from received FLOGI requests to use as the destination MAC
+for all outgoing frames.  We stopped doing that at some point.
+
+Use the lport_set_port_id method to catch incoming FLOGI frames
+and pass them to fcoe_ctlr_recv_flogi() so it can save the source MAC.
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ drivers/scsi/fcoe/fcoe.c |   24 ++++++++++++++++++++++++
+ 1 files changed, 24 insertions(+), 0 deletions(-)
+
+
+---
+diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
+index 2f3b73c..546e69a 100644
+--- a/drivers/scsi/fcoe/fcoe.c
++++ b/drivers/scsi/fcoe/fcoe.c
+@@ -145,6 +145,7 @@ static int fcoe_vport_destroy(struct fc_vport *);
+ static int fcoe_vport_create(struct fc_vport *, bool disabled);
+ static int fcoe_vport_disable(struct fc_vport *, bool disable);
+ static void fcoe_set_vport_symbolic_name(struct fc_vport *);
++static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *);
+ static struct libfc_function_template fcoe_libfc_fcn_templ = {
+       .frame_send = fcoe_xmit,
+@@ -152,6 +153,7 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = {
+       .ddp_done = fcoe_ddp_done,
+       .elsct_send = fcoe_elsct_send,
+       .get_lesb = fcoe_get_lesb,
++      .lport_set_port_id = fcoe_set_port_id,
+ };
+ struct fc_function_template fcoe_transport_function = {
+@@ -2630,3 +2632,25 @@ static void fcoe_get_lesb(struct fc_lport *lport,
+       lesb->lesb_miss_fka = htonl(mdac);
+       lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors);
+ }
++
++/**
++ * fcoe_set_port_id() - Callback from libfc when Port_ID is set.
++ * @lport: the local port
++ * @port_id: the port ID
++ * @fp: the received frame, if any, that caused the port_id to be set.
++ *
++ * This routine handles the case where we received a FLOGI and are
++ * entering point-to-point mode.  We need to call fcoe_ctlr_recv_flogi()
++ * so it can set the non-mapped mode and gateway address.
++ *
++ * The FLOGI LS_ACC is handled by fcoe_flogi_resp().
++ */
++static void fcoe_set_port_id(struct fc_lport *lport,
++                           u32 port_id, struct fc_frame *fp)
++{
++      struct fcoe_port *port = lport_priv(lport);
++      struct fcoe_interface *fcoe = port->fcoe;
++
++      if (fp && fc_frame_payload_op(fp) == ELS_FLOGI)
++              fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp);
++}
diff --git a/fcst/linux-patches/12-ptp-npiv b/fcst/linux-patches/12-ptp-npiv
new file mode 100644 (file)
index 0000000..5664ab8
--- /dev/null
@@ -0,0 +1,30 @@
+libfc: recognize incoming FLOGI for point-to-point mode
+
+When receiving a FLOGI request from a point-to-point peer,
+the D_ID of 0xfffffe was not recognized as belonging to one
+of the lports, so it was dropped.
+
+Change fc_vport_id_lookup() to treat d_id 0xfffffe as a match.
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ drivers/scsi/libfc/fc_npiv.c |    3 +++
+ 1 files changed, 3 insertions(+), 0 deletions(-)
+
+
+---
+diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c
+index c68f6c7..45b6f1e 100644
+--- a/drivers/scsi/libfc/fc_npiv.c
++++ b/drivers/scsi/libfc/fc_npiv.c
+@@ -72,6 +72,9 @@ struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id)
+       if (fc_host_port_id(n_port->host) == port_id)
+               return n_port;
++      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 (fc_host_port_id(vn_port->host) == port_id) {
diff --git a/fcst/linux-patches/13-ptp-acc b/fcst/linux-patches/13-ptp-acc
new file mode 100644 (file)
index 0000000..8686e55
--- /dev/null
@@ -0,0 +1,32 @@
+libfc: point-to-point: send FLOGI LS_ACC to assigned D_DID
+
+The method we've been using for point-to-point mode requires
+that the LS_ACC for the FLOGI uses the D_ID and S_ID assigned
+to the remote port and local port, not those in the exchange.
+
+This is not the correct method, but for now, it's what works
+with the old target, as well as with new targets based on libfc.
+
+This patch changes the addresses used accordingly.
+
+Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
+
+---
+ drivers/scsi/libfc/fc_lport.c |    2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+
+---
+diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
+index a728e99..a2a49a2 100644
+--- a/drivers/scsi/libfc/fc_lport.c
++++ b/drivers/scsi/libfc/fc_lport.c
+@@ -835,7 +835,7 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
+                */
+               f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+               ep = fc_seq_exch(sp);
+-              fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
++              fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, remote_fid, local_fid,
+                              FC_TYPE_ELS, f_ctl, 0);
+               lport->tt.seq_send(lport, sp, fp);
diff --git a/fcst/linux-patches/series-2.6.33-rc8 b/fcst/linux-patches/series-2.6.33-rc8
new file mode 100644 (file)
index 0000000..b8b3733
--- /dev/null
@@ -0,0 +1,14 @@
+# This series applies on GIT commit ec7b3b3aaf3b3b208140ac83dc19a754eebf648f
+01-prli-clean
+02-fc4
+03-seq-init
+04-seq-set-resp
+05-lport-hook
+06-lport-notify
+07-pta-mask
+08-ptp-ref
+09-ptp-type
+10-non-fip
+11-ptp-flogi-recv
+12-ptp-npiv
+13-ptp-acc