diff --git a/fcst/Kconfig b/fcst/Kconfig new file mode 100644 index 000000000..4c8890ad4 --- /dev/null +++ b/fcst/Kconfig @@ -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 index 000000000..72bd2b879 --- /dev/null +++ b/fcst/Makefile @@ -0,0 +1,106 @@ +# +# FCST: libfc SCSI target makefile +# Based on ../mvsas_tgt/Makefile +# +# Copyright (C) 2006 - 2008 Jacky Feng +# +# 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 index 000000000..a9c003efc --- /dev/null +++ b/fcst/Makefile_in-tree @@ -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 index 000000000..712827e5c --- /dev/null +++ b/fcst/README @@ -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 index 000000000..e80b628b1 --- /dev/null +++ b/fcst/fcst.h @@ -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 index 000000000..de5806b2b --- /dev/null +++ b/fcst/ft_cmd.c @@ -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 +#include +#include +#include +#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(¶ms, 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, ¶ms); + 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 index 000000000..fe550aaa5 --- /dev/null +++ b/fcst/ft_io.c @@ -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 +#include +#include +#include +#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 index 000000000..f02925b99 --- /dev/null +++ b/fcst/ft_scst.c @@ -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 +#include +#include +#include "fcst.h" + +MODULE_AUTHOR("Joe Eykholt "); +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 index 000000000..5580dc0ad --- /dev/null +++ b/fcst/ft_sess.c @@ -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 +#include +#include +#include +#include +#include +#include +#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 index 000000000..a57dc6981 --- /dev/null +++ b/fcst/linux-patches/01-prli-clean @@ -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 + +--- + 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 index 000000000..1f3f1ae95 --- /dev/null +++ b/fcst/linux-patches/02-fc4 @@ -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 + +--- + 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 + ++#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 index 000000000..86cf85ce5 --- /dev/null +++ b/fcst/linux-patches/03-seq-init @@ -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 + +--- + 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 index 000000000..22abfca34 --- /dev/null +++ b/fcst/linux-patches/04-seq-set-resp @@ -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 + +--- + 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 index 000000000..0b59bc34f --- /dev/null +++ b/fcst/linux-patches/05-lport-hook @@ -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 + +--- + 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 index 000000000..015c12e5f --- /dev/null +++ b/fcst/linux-patches/06-lport-notify @@ -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 + +--- + 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 index 000000000..2f496d9e7 --- /dev/null +++ b/fcst/linux-patches/07-pta-mask @@ -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 + +--- + 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 index 000000000..cf23c6b82 --- /dev/null +++ b/fcst/linux-patches/08-ptp-ref @@ -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 + +--- + 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 index 000000000..07d783f6a --- /dev/null +++ b/fcst/linux-patches/09-ptp-type @@ -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 + +--- + 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 index 000000000..0501ad42b --- /dev/null +++ b/fcst/linux-patches/10-non-fip @@ -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 + +--- + 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 index 000000000..d6b192a1c --- /dev/null +++ b/fcst/linux-patches/11-ptp-flogi-recv @@ -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 + +--- + 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 index 000000000..5664ab8da --- /dev/null +++ b/fcst/linux-patches/12-ptp-npiv @@ -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 + +--- + 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 index 000000000..8686e5563 --- /dev/null +++ b/fcst/linux-patches/13-ptp-acc @@ -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 + +--- + 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 index 000000000..b8b37331c --- /dev/null +++ b/fcst/linux-patches/series-2.6.33-rc8 @@ -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