diff --git a/Makefile b/Makefile index 92ce912cd..44b6304dd 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ QLA_ISP_DIR=qla_isp LSI_DIR=mpt USR_DIR=usr/fileio SRP_DIR=srpt +SCST_LOCAL_DIR=scst_local ISCSI_DIR=iscsi-scst #ISCSI_DISTDIR=../../../iscsi_scst_inst @@ -77,6 +78,10 @@ help: @echo " srpt_install : srp target: install" @echo " srpt_uninstall : srp target: uninstall" @echo "" + @echo " scst_local : make scst_local target" + @echo " scst_local_install : scst_local target: install" + @echo " scst_local_uninstall : scst_local target: uninstall" + @echo "" @echo " usr : make user space fileio_tgt target" @echo " usr_clean : usr target: clean " @echo " usr_extraclean : usr target: clean + clean dependencies" @@ -99,6 +104,7 @@ all: # @if [ -d $(SRP_DIR) ]; then cd $(SRP_DIR) && $(MAKE) $@; fi @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi + @if [ -d $(SCST_LOCAL_DIR) ]; then cd $(SCST_LOCAL_DIR) && $(MAKE) $@; fi install: cd $(SCST_DIR) && $(MAKE) $@ @@ -108,6 +114,7 @@ install: # @if [ -d $(SRP_DIR) ]; then cd $(SRP_DIR) && $(MAKE) $@; fi @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) DISTDIR=$(ISCSI_DISTDIR) $@; fi @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi + @if [ -d $(SCST_LOCAL_DIR) ]; then cd $(SCST_LOCAL_DIR) && $(MAKE) $@; fi uninstall: cd $(SCST_DIR) && $(MAKE) $@ @@ -117,6 +124,7 @@ uninstall: @if [ -d $(SRP_DIR) ]; then cd $(SRP_DIR) && $(MAKE) $@; fi @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi + @if [ -d $(SCST_LOCAL_DIR) ]; then cd $(SCST_LOCAL_DIR) && $(MAKE) $@; fi clean: cd $(SCST_DIR) && $(MAKE) $@ @@ -127,6 +135,7 @@ clean: @if [ -d $(SRP_DIR) ]; then cd $(SRP_DIR) && $(MAKE) $@; fi @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi + @if [ -d $(SCST_LOCAL_DIR) ]; then cd $(SCST_LOCAL_DIR) && $(MAKE) $@; fi extraclean: cd $(SCST_DIR) && $(MAKE) $@ @@ -137,6 +146,7 @@ extraclean: @if [ -d $(SRP_DIR) ]; then cd $(SRP_DIR) && $(MAKE) $@; fi @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi + @if [ -d $(SCST_LOCAL_DIR) ]; then cd $(SCST_LOCAL_DIR) && $(MAKE) $@; fi scst: cd $(SCST_DIR) && $(MAKE) all @@ -239,12 +249,27 @@ srpt_install: srpt_uninstall: cd $(SRP_DIR) && $(MAKE) uninstall -srpt_clean: +srpt_clean: cd $(SRP_DIR) && $(MAKE) clean srpt_extraclean: cd $(SRP_DIR) && $(MAKE) extraclean +scst_local: + cd $(SCST_LOCAL_DIR) && $(MAKE) all + +scst_local_install: + cd $(SCST_LOCAL_DIR) && $(MAKE) install + +scst_local_uninstall: + cd $(SCST_LOCAL_DIR) && $(MAKE) uninstall + +scst_local_clean: + cd $(SCST_LOCAL_DIR) && $(MAKE) clean + +scst_local_extraclean: + cd $(SCST_LOCAL_DIR) && $(MAKE) extraclean + usr: cd $(USR_DIR) && $(MAKE) @@ -301,4 +326,5 @@ release2debug: scstadm scstadm_install scstadm_uninstall scstadm_clean scstadm_extraclean \ srpt srpt_install srpt_uninstall srpt_clean srpt_extraclean \ usr usr_install usr_uninstall usr_clean usr_extraclean \ + scst_local scst_local_install scst_local_uninstall scst_local_clean scst_local_extraclean \ debug2perf, debug2release, perf2debug, release2debug diff --git a/scripts/generate-kernel-patch b/scripts/generate-kernel-patch index ec5fcef9b..6eb222a30 100755 --- a/scripts/generate-kernel-patch +++ b/scripts/generate-kernel-patch @@ -24,10 +24,11 @@ ######################## function usage { - echo "Usage: $0 [-q] [-s] [-m] , where: " + echo "Usage: $0 [-q] [-s] [-m] [-l] , where: " echo " -q - add qla2x00t driver" echo " -s - exclude srpt driver" echo " -m - add mpt target driver" + echo " -l - add scst_local target driver" } # Convert an existing patch. @@ -83,17 +84,19 @@ EOF qla2x00t="false" srpt="true" mpt_scst="false" +scst_local="false" -if [ ! -e scst -o ! -e iscsi-scst -o ! -e srpt ]; then +if [ ! -e scst -o ! -e iscsi-scst -o ! -e srpt -o ! -e scst_local ]; then echo "Please run this script from inside the SCST subversion source tree." exit 1 fi -set -- $(/usr/bin/getopt hmqs "$@") +set -- $(/usr/bin/getopt hlmqs "$@") while [ "$1" != "${1#-}" ] do case "$1" in '-h') usage; exit 1;; + '-l') scst_local="true"; shift;; '-m') mpt_scst="true"; shift;; '-q') qla2x00t="true"; shift;; '-s') srpt="false"; shift;; @@ -264,3 +267,15 @@ if [ "$mpt_scst" = "true" ]; then done fi + +# Directory drivers/scst/scst-local + +if [ "$scst_local" = "true" ]; then + + add_file "scst_local/in-tree/Kconfig" "drivers/scst/scst_local/Kconfig" + + add_file "scst_local/in-tree/Makefile" "drivers/scst/scst_local/Makefile" + + add_file "scst_local/scst_local.c" "drivers/scst/scst_local/scst_local.c" + +fi diff --git a/scst/kernel/in-tree/Kconfig.scst b/scst/kernel/in-tree/Kconfig.scst index 1eae1104e..7f45915b7 100644 --- a/scst/kernel/in-tree/Kconfig.scst +++ b/scst/kernel/in-tree/Kconfig.scst @@ -251,5 +251,6 @@ config SCST_MEASURE_LATENCY source "drivers/scst/iscsi-scst/Kconfig" source "drivers/scst/qla2xxx-target/Kconfig" source "drivers/scst/srpt/Kconfig" +source "drivers/scst/scst_local/Kconfig" endmenu diff --git a/scst/kernel/in-tree/Makefile.scst b/scst/kernel/in-tree/Makefile.scst index ef080e1ad..df0c8da52 100644 --- a/scst/kernel/in-tree/Makefile.scst +++ b/scst/kernel/in-tree/Makefile.scst @@ -7,5 +7,6 @@ scst-y += scst_proc.o scst-y += scst_mem.o scst-y += scst_debug.o -obj-$(CONFIG_SCST) += scst.o dev_handlers/ iscsi-scst/ qla2xxx-target/ srpt/ +obj-$(CONFIG_SCST) += scst.o dev_handlers/ iscsi-scst/ qla2xxx-target/ srpt/ \ + scst_local/ diff --git a/scst_local/Makefile b/scst_local/Makefile new file mode 100644 index 000000000..656c63ae9 --- /dev/null +++ b/scst_local/Makefile @@ -0,0 +1,68 @@ +# +# A Makefile for the scst-local ... +# + +#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) -I$(SCST_DIR) -Wextra -Wno-unused-parameter + +EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS + +#EXTRA_CFLAGS += -DCONFIG_SCST_TRACING +EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG + +ifeq ($(KVER),) + ifeq ($(KDIR),) + KDIR := /lib/modules/$(shell uname -r)/build + endif +else + KDIR := /lib/modules/$(KVER)/build +endif + +ifneq ($(KERNELRELEASE),) +obj-m := scst_local.o +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=m + +install: all scst_local.ko + $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ + modules_install + -depmod -a + +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)/scst_local.kp + -/sbin/depmod -a $(KVER) +endif + +clean: + @$(MAKE) -C $(KDIR) M=$(PWD) clean + @$(RM) tags Modules.symvers module.symvers Module.markers modules.order + +extraclean: clean + +.PHONY: all tgt install uninstall clean extraclean diff --git a/scst_local/README b/scst_local/README new file mode 100644 index 000000000..05a15d848 --- /dev/null +++ b/scst_local/README @@ -0,0 +1,89 @@ +SCST Debug ... +Richard Sharpe, 24-Sep-2008 + +This is the SCST Local driver. Its function is to allow you to access devices +that are exported via SCST directly on the same Linux system that they are +exported from. + +No assumptions are made in the code about the device types on the target, so +any device handlers that you load in SCST should be visible, including tapes +and so forth. + +To build, simply issue 'make' in the scst-local directory. + +Try 'modinfo scst_local.ko' for a listing of module parameters so far. + +Here is how I have used it so far: + +1. Load up scst: + + modprobe scst + modprobe scst_vdisk + +2. Create a virtual disk (or your own device handler): + + dd if=/dev/zero of=/some/path/vdisk1.img bs=16384 count=1000000 +# dd if=/dev/zero of=/some/path/vdisk2.img bs=16384 count=1000000 + echo "open vm_disk1 /some/path/vdisk1.img" > /proc/scsi_tgt/vdisk/vdisk +# echo "open vm_disk2 /some/path/vdisk2.img" > /proc/scsi_tgt/vdisk/vdisk + echo "add vm_disk1 0" > /proc/scsi_tgt/groups/Default/devices +# echo "add vm_disk2 1" > /proc/scsi_tgt/groups/Default/devices + +3. Load the scst_local driver: + + insmod scst_local.ko +# insmod scst_local.ko max_luns=8 options=1 # That options prints debug info + +4. Check what you have + + cat /proc/scsi/scsi + Attached devices: + Host: scsi0 Channel: 00 Id: 00 Lun: 00 + Vendor: ATA Model: ST9320320AS Rev: 0303 + Type: Direct-Access ANSI SCSI revision: 05 + Host: scsi4 Channel: 00 Id: 00 Lun: 00 + Vendor: TSSTcorp Model: CD/DVDW TS-L632D Rev: TO04 + Type: CD-ROM ANSI SCSI revision: 05 + Host: scsi7 Channel: 00 Id: 00 Lun: 00 + Vendor: SCST_FIO Model: vm_disk1 Rev: 101 + Type: Direct-Access ANSI SCSI revision: 04 + +5. Have fun. + +Some of this was coded while in Santa Clara, some in Bangalore, and some in +Hyderabad. Noe doubt some will be coded on the way back to Santa Clara. + +The code still has bugs, so if you encounter any, email me the fixes at: + + realrichardsharpe@gmail.com + +I am thinking of renaming this to something more interesting. + +6. Change log + +V0.1 24-Sep-2008 (Hyderabad) Initial coding, pretty chatty and messy, + but worked. + +V0.2 25-Sep-2008 (Hong Kong) Cleaned up the code a lot, reduced the log + chatter, fixed a bug where multiple LUNs did not + work. Also, added logging control. Tested with + five virtual disks. They all came up as /dev/sdb + through /dev/sdf and I could dd to them. Also + fixed a bug preventing multiple adapters. + +V0.3 26-Sep-2008 (Santa Clara) Added back a copyright plus cleaned up some + unused functions and structures. + +V0.4 5-Oct-2008 (Santa Clara) Changed name to scst_local as suggested, cleaned + up some unused variables (made them used) and + change allocation to a kmem_cache pool. + +V0.5 5-Oct-2008 (Santa Clara) Added mgmt commands to handle dev reset and + aborts. Not sure if aborts works. Also corrected + the version info and renamed readme to README. + +V0.6 7-Oct-2008 (Santa Clara) Removed some redundant code and made some + changes suggested by Vladislav. + +V0.7 11-Oct-2008 (Santa Clara) Moved into the scst tree. Cleaned up some + unused functions, used TRACE macros etc. diff --git a/scst_local/in-tree/Kconfig b/scst_local/in-tree/Kconfig new file mode 100644 index 000000000..09bf76da1 --- /dev/null +++ b/scst_local/in-tree/Kconfig @@ -0,0 +1,10 @@ +config SCST_LOCAL + tristate "SCST Local driver" + depends on SCST + ---help--- + This module provides a LLD SCSI driver that connects to + the SCST target mode subsystem in a loop-back manner. + It allows you to test target-mode device-handlers locally. + You will need the SCST subsystem as well. + + If unsure whether you really want or need this, say N. diff --git a/scst_local/in-tree/Makefile b/scst_local/in-tree/Makefile new file mode 100644 index 000000000..eeebd2967 --- /dev/null +++ b/scst_local/in-tree/Makefile @@ -0,0 +1,10 @@ +SCST_INC_DIR := include/scst +SCST_DIR := drivers/scst +EXTRA_CFLAGS += -I$(SCST_INC_DIR) -I$(SCST_DIR) + +#EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS +#EXTRA_CFLAGS += -DCONFIG_SCST_TRACING +#EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG + +obj-$(CONFIG_SCST_LOCAL) += scst_local.o + diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c new file mode 100644 index 000000000..d7ab5f8a1 --- /dev/null +++ b/scst_local/scst_local.c @@ -0,0 +1,1023 @@ +/* + * Copyright (C) 2008 Richard Sharpe + * Copyright (C) 1992 Eric Youngdale + * + * Simulate a host adapter and an SCST target adapter back to back + * + * Based on the scsi_debug.c driver originally by Eric Youngdale and + * others, including D Gilbert et al + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* SCST includes ... */ +#include +#include + +#define LOG_PREFIX "scst_local" + +#include + +#ifdef CONFIG_SCST_DEBUG +#define SCST_LOCAL_DEFAULT_LOG_FLAGS (TRACE_FUNCTION | TRACE_PID | \ + TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MGMT_MINOR | \ + TRACE_MGMT_DEBUG | TRACE_MINOR | TRACE_SPECIAL) +#else +# ifdef CONFIG_SCST_TRACING +#define SCST_LOCAL_DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | \ + TRACE_MINOR | TRACE_SPECIAL) +# endif +#endif + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) +#define trace_flag scst_local_trace_flag +unsigned long scst_local_trace_flag = SCST_LOCAL_DEFAULT_LOG_FLAGS; +#endif + +#define TRUE 1 +#define FALSE 0 + +/* + * Some definitions needed by the scst portion + */ +static void scst_local_remove_adapter(void); +static int scst_local_add_adapter(void); + +#define SCST_LOCAL_VERSION "0.7" +static const char *scst_local_version_date = "20081011"; + +#define SCST_LOCAL_SG_TABLESIZE 256 + +/* + * Target structures that are shared between the two pieces + * This will have to change if we have more than one target + */ +static struct scst_tgt_template scst_local_targ_tmpl; + +/* + * Some max values + */ +#define DEF_NUM_HOST 2 +#define DEF_NUM_TGTS 1 +#define DEF_MAX_LUNS 256 +#define SCST_LOCAL_CANQUEUE 1 +/*#define SCST_LOCAL_CANQUEUE 255*/ + +/* + * These following defines are the SCSI Host LLD (the initiator). + * SCST Target Driver is below + */ + +static int scst_local_add_host = DEF_NUM_HOST; +static int scst_local_num_tgts = DEF_NUM_TGTS; +static int scst_local_max_luns = DEF_MAX_LUNS; + +static int num_aborts; +static int num_dev_resets; +static int num_target_resets; + +/* + * Each host has a target it is pointing to ... this will allow multiple + * hosts pointing to the one target. + */ +struct scst_local_host_info { + struct list_head host_list; + struct Scsi_Host *shost; + struct scst_tgt *target; + struct scst_session *session; + struct device dev; +}; + +#define to_scst_lcl_host(d) \ + container_of(d, struct scst_local_host_info, dev) + +/* + * Maintains data that is needed during command processing ... + */ +struct scst_local_tgt_specific { + struct scsi_cmnd *cmnd; + void (*done)(struct scsi_cmnd *); +}; + +/* + * We use a pool of objects maintaind by the kernel so that it is less + * likely to have to allocate them when we are in the data path. + */ +static struct kmem_cache *tgt_specific_pool; + +static LIST_HEAD(scst_local_host_list); +static DEFINE_SPINLOCK(scst_local_host_list_lock); + +static char scst_local_proc_name[] = "scst_ini_targ_debug"; + +static struct bus_type scst_fake_lld_bus; +static struct device scst_fake_primary; + +static struct device_driver scst_local_driverfs_driver = { + .name = scst_local_proc_name, + .bus = &scst_fake_lld_bus, +}; + +module_param_named(add_host, scst_local_add_host, int, S_IRUGO | S_IWUSR); +module_param_named(num_tgts, scst_local_num_tgts, int, S_IRUGO | S_IWUSR); +module_param_named(max_luns, scst_local_max_luns, int, S_IRUGO | S_IWUSR); + +MODULE_AUTHOR("Richard Sharpe + ideas from SCSI_DEBUG"); +MODULE_DESCRIPTION("SCSI+SCST local adapter driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(SCST_LOCAL_VERSION); + +MODULE_PARM_DESC(add_host, "0..127 hosts can be created (def=1)"); +MODULE_PARM_DESC(num_tgts, "mumber of targets per host (def=1)"); +MODULE_PARM_DESC(max_luns, "number of luns per target (def=1)"); + +static int scst_local_target_register(void); + +static int scst_local_proc_info(struct Scsi_Host *host, char *buffer, + char **start, off_t offset, int length, + int inout) +{ + int len, pos, begin; + + TRACE_ENTRY(); + + if (inout == 1) + return -EACCES; + + begin = 0; + pos = len = sprintf(buffer, "scst_local adapter driver, version " + "%s [%s]\n" + "num_tgts=%d, Aborts=%d, Device Resets=%d, " + "Target Resets=%d\n", + SCST_LOCAL_VERSION, scst_local_version_date, + scst_local_num_tgts, num_aborts, num_dev_resets, + num_target_resets); + if (pos < offset) { + len = 0; + begin = pos; + } + if (start) + *start = buffer + (offset - begin); + len -= (offset - begin); + if (len > length) + len = length; + + TRACE_EXIT_RES(len); + return len; +} + +static ssize_t scst_local_add_host_show(struct device_driver *ddp, char *buf) +{ + int len = 0; + struct scst_local_host_info *scst_lcl_host, *tmp; + + TRACE_ENTRY(); + + list_for_each_entry_safe(scst_lcl_host, tmp, &scst_local_host_list, + host_list) { + len += scnprintf(&buf[len], PAGE_SIZE - len, " Initiator: %s\n", + scst_lcl_host->session->initiator_name); + } + + TRACE_EXIT_RES(len); + return len; +} + +static ssize_t scst_local_add_host_store(struct device_driver *ddp, + const char *buf, size_t count) +{ + int delta_hosts; + + TRACE_ENTRY(); + + if (sscanf(buf, "%d", &delta_hosts) != 2) + return -EINVAL; + if (delta_hosts > 0) { + do { + scst_local_add_adapter(); + } while (--delta_hosts); + } else if (delta_hosts < 0) { + do { + scst_local_remove_adapter(); + } while (++delta_hosts); + } + + TRACE_EXIT_RES(count); + return count; +} + +DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, scst_local_add_host_show, + scst_local_add_host_store); + +static int do_create_driverfs_files(void) +{ + int ret; + + TRACE_ENTRY(); + + ret = driver_create_file(&scst_local_driverfs_driver, + &driver_attr_add_host); + + TRACE_EXIT_RES(ret); + return ret; +} + +static void do_remove_driverfs_files(void) +{ + driver_remove_file(&scst_local_driverfs_driver, + &driver_attr_add_host); +} + +static char scst_local_info_buf[256]; + +static const char *scst_local_info(struct Scsi_Host *shp) +{ + TRACE_ENTRY(); + + sprintf(scst_local_info_buf, "scst_local, version %s [%s], " + "Aborts: %d, Device Resets: %d, Target Resets: %d", + SCST_LOCAL_VERSION, scst_local_version_date, + num_aborts, num_dev_resets, num_target_resets); + + TRACE_EXIT(); + return scst_local_info_buf; +} + +/* +static int scst_local_ioctl(struct scsi_device *dev, int cmd, void __user *arg) +{ + TRACE_ENTRY(); + + if (scst_local_opt_noise & SCST_LOCAL_OPT_LLD_NOISE) + printk(KERN_INFO "scst_local: ioctl: cmd=0x%x\n", cmd); + return -EINVAL; + + TRACE_EXIT(); +} +*/ + +static int scst_local_abort(struct scsi_cmnd *SCpnt) +{ + struct scst_local_host_info *scst_lcl_host; + int ret = 0; + DECLARE_COMPLETION_ONSTACK(dev_reset_completion); + + TRACE_ENTRY(); + + scst_lcl_host = to_scst_lcl_host(scsi_get_device(SCpnt->device->host)); + + ret = scst_rx_mgmt_fn_tag(scst_lcl_host->session, + SCST_LUN_RESET, SCpnt->tag, FALSE, + &dev_reset_completion); + + wait_for_completion_interruptible(&dev_reset_completion); + + ++num_aborts; + + TRACE_EXIT_RES(ret); + return ret; +} + +/* + * We issue a mgmt function. We should pass a structure to the function + * that contains our private data, so we can retrieve the status from the + * done routine ... TODO + */ +static int scst_local_device_reset(struct scsi_cmnd *SCpnt) +{ + struct scst_local_host_info *scst_lcl_host; + int lun; + int ret = 0; + DECLARE_COMPLETION_ONSTACK(dev_reset_completion); + + TRACE_ENTRY(); + + scst_lcl_host = to_scst_lcl_host(scsi_get_device(SCpnt->device->host)); + + lun = SCpnt->device->lun; + lun = (lun & 0xFF) << 8 | ((lun & 0xFF00) >> 8); /* FIXME: LE only */ + + ret = scst_rx_mgmt_fn_lun(scst_lcl_host->session, + SCST_LUN_RESET, + (const uint8_t *)&lun, + sizeof(lun), FALSE, + &dev_reset_completion); + + /* + * Now wait for the completion ... + */ + wait_for_completion_interruptible(&dev_reset_completion); + + ++num_dev_resets; + + TRACE_EXIT_RES(ret); + return ret; +} + +static int scst_local_target_reset(struct scsi_cmnd *SCpnt) +{ + struct scst_local_host_info *scst_lcl_host; + int lun; + int ret = 0; + DECLARE_COMPLETION_ONSTACK(dev_reset_completion); + + TRACE_ENTRY(); + + scst_lcl_host = to_scst_lcl_host(scsi_get_device(SCpnt->device->host)); + + lun = SCpnt->device->lun; + lun = (lun & 0xFF) << 8 | ((lun & 0xFF00) >> 8); /* FIXME: LE only */ + + ret = scst_rx_mgmt_fn_lun(scst_lcl_host->session, + SCST_TARGET_RESET, + (const uint8_t *)&lun, + sizeof(lun), FALSE, + &dev_reset_completion); + + /* + * Now wait for the completion ... + */ + wait_for_completion_interruptible(&dev_reset_completion); + + ++num_target_resets; + + TRACE_EXIT_RES(ret); + return ret; +} + +static void copy_sense(struct scsi_cmnd *cmnd, struct scst_cmd *scst_cmnd) +{ + int scst_cmnd_sense_len = scst_cmd_get_sense_buffer_len(scst_cmnd); + + TRACE_ENTRY(); + + scst_cmnd_sense_len = (SCSI_SENSE_BUFFERSIZE < scst_cmnd_sense_len ? + SCSI_SENSE_BUFFERSIZE : scst_cmnd_sense_len); + memcpy(cmnd->sense_buffer, scst_cmd_get_sense_buffer(scst_cmnd), + scst_cmnd_sense_len); + + TRACE_BUFFER("Sense set", cmnd->sense_buffer, scst_cmnd_sense_len); + + TRACE_EXIT(); + return; +} + +/* + * Utility function to handle processing of done and allow + * easy insertion of error injection if desired + */ +static int scst_local_send_resp(struct scsi_cmnd *cmnd, + struct scst_cmd *scst_cmnd, + void (*done)(struct scsi_cmnd *), int scsi_result) +{ + int ret = 0; + + TRACE_ENTRY(); + + if (cmnd && scst_cmnd) { + /* Simulate autosense by this driver */ + if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xFF)) + copy_sense(cmnd, scst_cmnd); + } + + if (cmnd) + cmnd->result = scsi_result; + if (done) + done(cmnd); + + TRACE_EXIT_RES(ret); + return ret; +} + +/* + * This does the heavy lifting ... we pass all the commands on to the + * target driver and have it do its magic ... + */ +static int scst_local_queuecommand(struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)) +{ + struct scst_local_tgt_specific *tgt_specific = NULL; + struct scst_local_host_info *scst_lcl_host; + int target = SCpnt->device->id; + int lun; + struct scst_cmd *scst_cmd = NULL; + scst_data_direction dir; + + TRACE_ENTRY(); + + TRACE_DBG("targ: %d, init id %d, lun %d, cmd: 0X%02X\n", + target, SCpnt->device->host->hostt->this_id, SCpnt->device->lun, + SCpnt->cmnd[0]); + + scst_lcl_host = to_scst_lcl_host(scsi_get_device(SCpnt->device->host)); + + scsi_set_resid(SCpnt, 0); + + if (target == SCpnt->device->host->hostt->this_id) { + printk(KERN_ERR "%s: initiator's id used as target\n", + __func__); + return scst_local_send_resp(SCpnt, NULL, done, + DID_NO_CONNECT << 16); + } + + tgt_specific = kmem_cache_alloc(tgt_specific_pool, GFP_ATOMIC); + if (!tgt_specific) { + printk(KERN_ERR "%s out of memory at line %d\n", + __func__, __LINE__); + return -ENOMEM; + } + tgt_specific->cmnd = SCpnt; + tgt_specific->done = done; + + /* + * Tell the target that we have a command ... but first we need + * to get the LUN into a format that SCST understand + */ + lun = SCpnt->device->lun; + lun = (lun & 0xFF) << 8 | ((lun & 0xFF00) >> 8); /* FIXME: LE only */ + scst_cmd = scst_rx_cmd(scst_lcl_host->session, + (const uint8_t *)&lun, + sizeof(lun), SCpnt->cmnd, + SCpnt->cmd_len, TRUE); + if (!scst_cmd) { + printk(KERN_ERR "%s out of memory at line %d\n", + __func__, __LINE__); + return -ENOMEM; + } + + scst_cmd_set_tgt_priv(scst_cmd, tgt_specific); + scst_cmd_set_tag(scst_cmd, SCpnt->tag); + switch (scsi_get_tag_type(SCpnt->device)) { + case MSG_SIMPLE_TAG: + scst_cmd->queue_type = SCST_CMD_QUEUE_SIMPLE; + break; + case MSG_HEAD_TAG: + scst_cmd->queue_type = SCST_CMD_QUEUE_HEAD_OF_QUEUE; + break; + case MSG_ORDERED_TAG: + scst_cmd->queue_type = SCST_CMD_QUEUE_ORDERED; + break; + case SCSI_NO_TAG: + default: + scst_cmd->queue_type = SCST_CMD_QUEUE_UNTAGGED; + break; + } + + /* + * We get given data buffers by the SML + */ + scst_cmd_set_data_buff_alloced(scst_cmd); + + dir = SCST_DATA_NONE; + switch (SCpnt->sc_data_direction) { + case DMA_TO_DEVICE: + dir = SCST_DATA_WRITE; + break; + case DMA_FROM_DEVICE: + dir = SCST_DATA_READ; + break; + case DMA_BIDIRECTIONAL: + printk(KERN_ERR "%s: DMA_BIDIRECTIONAL not allowed!\n", + __func__); + return scst_local_send_resp(SCpnt, NULL, done, + DID_ERROR << 16); + dir = SCST_DATA_UNKNOWN; + break; + case DMA_NONE: + default: + dir = SCST_DATA_NONE; + break; + } + scst_cmd_set_expected(scst_cmd, dir, scsi_bufflen(SCpnt)); + + /* + * Set the SGL things directly ... + */ + scst_cmd->sg_cnt = scsi_sg_count(SCpnt); + scst_cmd->sg = scsi_sglist(SCpnt); + + /* + * Unfortunately, we called with IRQs disabled, so have no choice, + * except pass to the thread context. + */ + scst_cmd_init_done(scst_cmd, SCST_CONTEXT_THREAD); + + /* + * We are done here I think. Other callbacks move us forward. + */ + TRACE_EXIT(); + return 0; +} + +static void scst_local_release_adapter(struct device *dev) +{ + struct scst_local_host_info *scst_lcl_host; + + TRACE_ENTRY(); + scst_lcl_host = to_scst_lcl_host(dev); + if (scst_lcl_host) { + scst_unregister_session(scst_lcl_host->session, TRUE, NULL); + scst_unregister(scst_lcl_host->target); + kfree(scst_lcl_host); + } + + TRACE_EXIT(); +} + +/* + * Add an adapter on the host side ... We add the target before we add + * the host (initiator) so that we don't get any requests before we are + * ready for them. + * + * I want to convert this so we can map many hosts to a smaller number of + * targets to support the simulation of multiple initiators. + */ +static int scst_local_add_adapter(void) +{ + int error = 0; + struct scst_local_host_info *scst_lcl_host; + char name[32]; + + TRACE_ENTRY(); + + scst_lcl_host = kzalloc(sizeof(struct scst_local_host_info), + GFP_KERNEL); + if (NULL == scst_lcl_host) { + printk(KERN_ERR "%s out of memory at line %d\n", + __func__, __LINE__); + return -ENOMEM; + } + + spin_lock(&scst_local_host_list_lock); + list_add_tail(&scst_lcl_host->host_list, &scst_local_host_list); + spin_unlock(&scst_local_host_list_lock); + + /* + * Register a target with SCST and add a session + */ + sprintf(name, "scst_lcl_targ_%d", scst_local_add_host); + scst_lcl_host->target = scst_register(&scst_local_targ_tmpl, name); + if (!scst_lcl_host) { + printk(KERN_WARNING "scst_register_target failed:\n"); + error = -1; + goto cleanup; + } + + sprintf(name, "scst_lcl_host_%d", scst_local_add_host); + scst_lcl_host->session = scst_register_session(scst_lcl_host->target, + TRUE, name, NULL, NULL); + if (!scst_lcl_host->session) { + printk(KERN_WARNING "scst_register_session failed:\n"); + error = -1; + goto unregister_target; + } + + scst_lcl_host->dev.bus = &scst_fake_lld_bus; + scst_lcl_host->dev.parent = &scst_fake_primary; + scst_lcl_host->dev.release = &scst_local_release_adapter; + sprintf(scst_lcl_host->dev.bus_id, "scst_adp_%d", scst_local_add_host); + + error = device_register(&scst_lcl_host->dev); + if (error) + goto unregister_session; + + scst_local_add_host++; /* keep count of what we have added */ + + TRACE_EXIT(); + return error; + +unregister_session: + scst_unregister_session(scst_lcl_host->session, TRUE, NULL); +unregister_target: + scst_unregister(scst_lcl_host->target); +cleanup: + kfree(scst_lcl_host); + TRACE_EXIT(); + return error; +} + +/* + * Remove an adapter ... + */ +static void scst_local_remove_adapter(void) +{ + struct scst_local_host_info *scst_lcl_host = NULL; + + TRACE_ENTRY(); + + spin_lock(&scst_local_host_list_lock); + if (!list_empty(&scst_local_host_list)) { + scst_lcl_host = list_entry(scst_local_host_list.prev, + struct scst_local_host_info, + host_list); + list_del(&scst_lcl_host->host_list); + } + spin_unlock(&scst_local_host_list_lock); + + if (!scst_lcl_host) + return; + + device_unregister(&scst_lcl_host->dev); + + --scst_local_add_host; + + TRACE_EXIT(); +} + +static struct scsi_host_template scst_lcl_ini_driver_template = { + .proc_info = scst_local_proc_info, + .proc_name = scst_local_proc_name, + .name = SCST_LOCAL_NAME, + .info = scst_local_info, +/* .slave_alloc = scst_local_slave_alloc, */ +/* .ioctl = scst_local_ioctl, */ + .queuecommand = scst_local_queuecommand, + .eh_abort_handler = scst_local_abort, + .eh_device_reset_handler = scst_local_device_reset, + .eh_target_reset_handler = scst_local_target_reset, + .can_queue = SCST_LOCAL_CANQUEUE, + .this_id = 7, /*???*/ + .sg_tablesize = SCST_LOCAL_SG_TABLESIZE, + .cmd_per_lun = 16, + .max_sectors = 0xffff, + .use_clustering = DISABLE_CLUSTERING, + .module = THIS_MODULE, +}; + +static void scst_fake_0_release(struct device *dev) +{ + TRACE_ENTRY(); + + TRACE_EXIT(); +} + +static struct device scst_fake_primary = { + .bus_id = "scst_fake_0", + .release = scst_fake_0_release, +}; + +static int __init scst_local_init(void) +{ + int ret, k, adapters; + + TRACE_ENTRY(); + + TRACE_DBG("Adapters: %d\n", scst_local_add_host); + + /* + * Allocate a pool of structures for tgt_specific structures + */ + tgt_specific_pool = kmem_cache_create("scst_tgt_specific", + sizeof(struct scst_local_tgt_specific), + 0, SCST_SLAB_FLAGS, NULL); + + if (!tgt_specific_pool) { + printk(KERN_WARNING "%s: out of memory for " + "tgt_specific structs", + __func__); + return -ENOMEM; + } + + ret = device_register(&scst_fake_primary); + if (ret < 0) { + printk(KERN_WARNING "%s: device_register error: %d\n", + __func__, ret); + goto free_stuff; + } + ret = bus_register(&scst_fake_lld_bus); + if (ret < 0) { + printk(KERN_WARNING "%s: bus_register error: %d\n", + __func__, ret); + goto dev_unreg; + } + ret = driver_register(&scst_local_driverfs_driver); + if (ret < 0) { + printk(KERN_WARNING "%s: driver_register error: %d\n", + __func__, ret); + goto bus_unreg; + } + ret = do_create_driverfs_files(); + if (ret < 0) { + printk(KERN_WARNING "%s: create_files error: %d\n", + __func__, ret); + goto driver_unregister; + } + + + /* + * register the target driver and then create a host. This makes sure + * that we see any targets that are there. Gotta figure out how to + * tell the system that there are new targets when SCST creates them. + */ + + ret = scst_local_target_register(); + if (ret < 0) { + printk(KERN_WARNING "%s: unable to register targ griver: %d\n", + __func__, ret); + goto del_files; + } + + /* + * Add adapters ... + */ + adapters = scst_local_add_host; + scst_local_add_host = 0; + for (k = 0; k < adapters; k++) { + if (scst_local_add_adapter()) { + printk(KERN_ERR "%s: " + "scst_local_add_adapter failed: %d\n", + __func__, k); + break; + } + } + + TRACE_EXIT(); + return 0; + +del_files: + do_remove_driverfs_files(); +driver_unregister: + driver_unregister(&scst_local_driverfs_driver); +bus_unreg: + bus_unregister(&scst_fake_lld_bus); +dev_unreg: + device_unregister(&scst_fake_primary); +free_stuff: + + TRACE_EXIT_RES(ret); + return ret; +} + +static void __exit scst_local_exit(void) +{ + int k = scst_local_add_host; + + TRACE_ENTRY(); + + for (; k; k--) { + printk(KERN_INFO "removing adapter in %s\n", __func__); + scst_local_remove_adapter(); + } + do_remove_driverfs_files(); + driver_unregister(&scst_local_driverfs_driver); + bus_unregister(&scst_fake_lld_bus); + device_unregister(&scst_fake_primary); + + /* + * Now unregister the target template + */ + scst_unregister_target_template(&scst_local_targ_tmpl); + + /* + * Free the pool we allocated + */ + if (tgt_specific_pool) + kmem_cache_destroy(tgt_specific_pool); + + TRACE_EXIT(); +} + +device_initcall(scst_local_init); +module_exit(scst_local_exit); + +/* + * Fake LLD Bus and functions + */ + +static int scst_fake_lld_driver_probe(struct device *dev) +{ + int ret = 0; + struct scst_local_host_info *scst_lcl_host; + struct Scsi_Host *hpnt; + + TRACE_ENTRY(); + + scst_lcl_host = to_scst_lcl_host(dev); + + hpnt = scsi_host_alloc(&scst_lcl_ini_driver_template, + sizeof(scst_lcl_host)); + if (NULL == hpnt) { + printk(KERN_ERR "%s: scsi_register failed\n", __func__); + ret = -ENODEV; + return ret; + } + + scst_lcl_host->shost = hpnt; + + /* + * We are going to have to register with SCST here I think + * and fill in some of these from that info? + */ + + *((struct scst_local_host_info **)hpnt->hostdata) = scst_lcl_host; + if ((hpnt->this_id >= 0) && (scst_local_num_tgts > hpnt->this_id)) + hpnt->max_id = scst_local_num_tgts + 1; + else + hpnt->max_id = scst_local_num_tgts; + hpnt->max_lun = scst_local_max_luns - 1; + + ret = scsi_add_host(hpnt, &scst_lcl_host->dev); + if (ret) { + printk(KERN_ERR "%s: scsi_add_host failed\n", __func__); + ret = -ENODEV; + scsi_host_put(hpnt); + } else + scsi_scan_host(hpnt); + + TRACE_EXIT_RES(ret); + return ret; +} + +static int scst_fake_lld_driver_remove(struct device *dev) +{ + struct scst_local_host_info *scst_lcl_host; + + TRACE_ENTRY(); + + scst_lcl_host = to_scst_lcl_host(dev); + + if (!scst_lcl_host) { + printk(KERN_ERR "%s: Unable to locate host info\n", + __func__); + return -ENODEV; + } + + scsi_remove_host(scst_lcl_host->shost); + + scsi_host_put(scst_lcl_host->shost); + + TRACE_EXIT(); + return 0; +} + +static int scst_fake_lld_bus_match(struct device dev, + struct device_driver *dev_driver) +{ + TRACE_ENTRY(); + + TRACE_EXIT(); + return 1; +} + +static struct bus_type scst_fake_lld_bus = { + .name = "scst_fake_bus", + .match = scst_fake_lld_bus_match, + .probe = scst_fake_lld_driver_probe, + .remove = scst_fake_lld_driver_remove, +}; + +/* + * SCST Target driver from here ... there are some forward declarations + * above + */ + +static int scst_local_targ_detect(struct scst_tgt_template *tgt_template) +{ + int adapter_count; + + TRACE_ENTRY(); + + /* + * Register the adapter(s) + */ + + adapter_count = scst_local_add_host; + + TRACE_EXIT_RES(adapter_count); + return adapter_count; +}; + +static int scst_local_targ_release(struct scst_tgt *tgt) +{ + TRACE_ENTRY(); + + TRACE_EXIT(); + return 0; +} + +static int scst_local_targ_xmit_response(struct scst_cmd *scst_cmd) +{ + struct scst_local_tgt_specific *tgt_specific; + + TRACE_ENTRY(); + + if (unlikely(scst_cmd_aborted(scst_cmd))) { + scst_set_delivery_status(scst_cmd, SCST_CMD_DELIVERY_ABORTED); + scst_tgt_cmd_done(scst_cmd); + printk(KERN_INFO "%s aborted command handled\n", __func__); + return SCST_TGT_RES_SUCCESS; + } + + tgt_specific = scst_cmd_get_tgt_priv(scst_cmd); + + /* + * This might have to change to use the two status flags + */ + if (scst_cmd_get_is_send_status(scst_cmd)) { + (void)scst_local_send_resp(tgt_specific->cmnd, scst_cmd, + tgt_specific->done, + scst_cmd_get_status(scst_cmd)); + } + + /* + * Now tell SCST that the command is done ... + */ + scst_tgt_cmd_done(scst_cmd); + + TRACE_EXIT(); + + return SCST_TGT_RES_SUCCESS; +} + +static void scst_local_targ_on_free_cmd(struct scst_cmd *scst_cmd) +{ + struct scst_local_tgt_specific *tgt_specific; + + TRACE_ENTRY(); + + tgt_specific = scst_cmd_get_tgt_priv(scst_cmd); + kmem_cache_free(tgt_specific_pool, tgt_specific); + + TRACE_EXIT(); + return; +} + +static void scst_local_targ_task_mgmt_done(struct scst_mgmt_cmd *mgmt_cmd) +{ + struct completion *tgt_specific; + + TRACE_ENTRY(); + + tgt_specific = (struct completion *) + scst_mgmt_cmd_get_tgt_priv(mgmt_cmd); + + if (tgt_specific) + complete(tgt_specific); + + TRACE_EXIT(); + return; +} + +static struct scst_tgt_template scst_local_targ_tmpl = { + .sg_tablesize = SCST_LOCAL_SG_TABLESIZE, + .name = "scst_local_tgt", + .unchecked_isa_dma = 0, + .use_clustering = 0, + .xmit_response_atomic = 1, + .detect = scst_local_targ_detect, + .release = scst_local_targ_release, + .xmit_response = scst_local_targ_xmit_response, + .on_free_cmd = scst_local_targ_on_free_cmd, + .task_mgmt_fn_done = scst_local_targ_task_mgmt_done, +}; + +/* + * Register the target driver ... to get things going + */ +static int scst_local_target_register(void) +{ + int ret; + + TRACE_ENTRY(); + + ret = scst_register_target_template(&scst_local_targ_tmpl); + if (ret < 0) { + printk(KERN_WARNING "scst_register_target_template " + "failed: %d\n", + ret); + goto error; + } + + TRACE_EXIT(); + return 0; + +error: + TRACE_EXIT_RES(ret); + return ret; +} +