Greetings all, This patch adds the first pieces of a v3.x subsystem plugin that interacts with Linux/SCSI to create a virtual target mode struct Scsi_Host, and using userspace backstores from the existing STGT codebase. So far, creating and removing STGT virtual target HBAs in through ConfigFS is working and status appearing under /sys/bus/stgt_bus and /sys/class/scsi_host/host#/ Most of the code required for actual STGT struct scsi_cmnd is in place (but not-tested yet), and the next main step is hooking up stgt_create_virtdevice() in drivers/target/target_core_stgt.c to assoicate the userspace backstore attached to the Target_Core_Mod/STGT virtual SCSI target mode HBA. The long term goal to allow all kernel level fabric modules to take advantage of the upstream STGT code in drivers/scsi/scsi_tgt_[if,lib].c. Here is what the running code looks like: *) tree /sys/kernel/config/target/core/ /sys/kernel/config/target/core/ |-- alua | |-- lu_gps | | `-- default_lu_gp | | |-- alua_access_state | | |-- lu_gp_id | | `-- members | `-- tg_pt_gps | `-- default_tg_pt_gp | |-- alua_access_state | |-- members | `-- tg_pt_gp_id |-- stgt_0 | `-- hba_info |-- stgt_1 | `-- hba_info |-- stgt_2 | `-- hba_info `-- stgt_3 `-- hba_info *) tree /sys/bus/stgt_bus/ /sys/bus/stgt_bus/ |-- devices | |-- stgt_adapter0 -> ../../../devices/stgt_primary_0/stgt_adapter0 | |-- stgt_adapter1 -> ../../../devices/stgt_primary_0/stgt_adapter1 | |-- stgt_adapter2 -> ../../../devices/stgt_primary_0/stgt_adapter2 | `-- stgt_adapter3 -> ../../../devices/stgt_primary_0/stgt_adapter3 |-- drivers | `-- stgt_tcm | |-- bind | |-- stgt_adapter0 -> ../../../../devices/stgt_primary_0/stgt_adapter0 | |-- stgt_adapter1 -> ../../../../devices/stgt_primary_0/stgt_adapter1 | |-- stgt_adapter2 -> ../../../../devices/stgt_primary_0/stgt_adapter2 | |-- stgt_adapter3 -> ../../../../devices/stgt_primary_0/stgt_adapter3 | |-- uevent | `-- unbind |-- drivers_autoprobe |-- drivers_probe `-- uevent *) Output in /sys/class/scsi_host/host$LINUX_HOST_ID for each stgt_tcm HBA target mode driver target# cat /sys/class/scsi_host/host30/active_mode Target target# cat /sys/class/scsi_host/host30/supported_mode Target target# cat /sys/class/scsi_host/host30/proc_name stgt_tcm 11 directories, 6 files *) lsmod lsmod Module Size Used by target_core_mod 194128 8 scsi_transport_iscsi 26808 0 scsi_tgt 11008 1 target_core_mod configfs 21680 2 target_core_mod pcnet32 29412 0 mii 4212 1 pcnet32 *) kernel ring buffer TARGET_CORE[0]: Loading Generic Kernel Storage Engine: v3.0.0 on Linux/i686 on 2.6.29 TARGET_CORE[0]: Initialized ConfigFS Fabric Infrastructure: v1.0.0 on Linux/i686 on 2.6.29 SE_PC[0] - Registered Plugin Class: TRANSPORT PLUGIN_TRANSPORT[1] - pscsi registered PLUGIN_TRANSPORT[2] - stgt registered CORE_STGT[0]: Bus Initalization complete PLUGIN_TRANSPORT[4] - iblock registered PLUGIN_TRANSPORT[5] - rd_dr registered PLUGIN_TRANSPORT[6] - rd_mcp registered PLUGIN_TRANSPORT[7] - fileio registered SE_PC[1] - Registered Plugin Class: OBJ PLUGIN_OBJ[1] - dev registered Target_Core_ConfigFS: Located se_plugin: dfbc0850 plugin_name: stgt hba_type: 2 plugin_dep_id: 0 scsi27 : stgt_tcm CORE_HBA[0] - Linux-iSCSI.org STGT HBA Driver v1.0 on Generic Target Core Stack v3.0 CORE_HBA[0] - stgt_tcm CORE_HBA[0] - Attached STGT HBA to Generic Target Core with TCQ Depth: 1 MaxSectors: 1024 CORE_HBA[0] - Attached HBA to Generic Target Core Target_Core_ConfigFS: Located se_plugin: dfbc0850 plugin_name: stgt hba_type: 2 plugin_dep_id: 1 scsi28 : stgt_tcm CORE_HBA[1] - Linux-iSCSI.org STGT HBA Driver v1.0 on Generic Target Core Stack v3.0 CORE_HBA[1] - stgt_tcm CORE_HBA[1] - Attached STGT HBA to Generic Target Core with TCQ Depth: 1 MaxSectors: 1024 CORE_HBA[1] - Attached HBA to Generic Target Core Target_Core_ConfigFS: Located se_plugin: dfbc0850 plugin_name: stgt hba_type: 2 plugin_dep_id: 2 scsi29 : stgt_tcm CORE_HBA[2] - Linux-iSCSI.org STGT HBA Driver v1.0 on Generic Target Core Stack v3.0 CORE_HBA[2] - stgt_tcm CORE_HBA[2] - Attached STGT HBA to Generic Target Core with TCQ Depth: 1 MaxSectors: 1024 CORE_HBA[2] - Attached HBA to Generic Target Core Target_Core_ConfigFS: Located se_plugin: dfbc0850 plugin_name: stgt hba_type: 2 plugin_dep_id: 3 scsi30 : stgt_tcm CORE_HBA[3] - Linux-iSCSI.org STGT HBA Driver v1.0 on Generic Target Core Stack v3.0 CORE_HBA[3] - stgt_tcm CORE_HBA[3] - Attached STGT HBA to Generic Target Core with TCQ Depth: 1 MaxSectors: 1024 CORE_HBA[3] - Attached HBA to Generic Target Core These patches are made against lio-core-2.6.git/master and tested on v2.6.29 x86 32-bit HVM. The lio-core-2.6.git tree can be found at: http://git.kernel.org/?p=linux/kernel/git/nab/lio-core-2.6.git;a=summary Signed-off-by: Nicholas A. Bellinger <nab@xxxxxxxxxxxxxxx> --- drivers/target/MCONFIG_TARGET | 1 + drivers/target/Makefile | 4 + drivers/target/target_core_stgt.c | 916 +++++++++++++++++++++++++ drivers/target/target_core_transport.c | 7 + include/target/target_core_base.h | 2 +- include/target/target_core_stgt.h | 200 ++++++ include/target/target_core_transport_plugin.h | 7 + 7 files changed, 1136 insertions(+), 1 deletions(-) create mode 100644 drivers/target/target_core_stgt.c create mode 100644 include/target/target_core_stgt.h diff --git a/drivers/target/MCONFIG_TARGET b/drivers/target/MCONFIG_TARGET index 77378be..8023deb 100644 --- a/drivers/target/MCONFIG_TARGET +++ b/drivers/target/MCONFIG_TARGET @@ -9,6 +9,7 @@ PYX_ISCSI_VENDOR ?="Linux-iSCSI.org" # Transport Plugins and Devices. # LINUX_PARALLEL_SCSI ?= 1 +LINUX_STGT ?= 1 LINUX_SCSI_MEDIA_ROM ?= 1 LINUX_PARALLEL_ATA ?= 0 LINUX_IBLOCK ?= 1 diff --git a/drivers/target/Makefile b/drivers/target/Makefile index 63c1e3b..47fef07 100644 --- a/drivers/target/Makefile +++ b/drivers/target/Makefile @@ -51,6 +51,10 @@ ifeq ($(LINUX_PARALLEL_SCSI), 1) target_core_mod-objs += target_core_pscsi.o EXTRA_CFLAGS += -DPARALLEL_SCSI endif +ifeq ($(LINUX_STGT), 1) +target_core_mod-objs += target_core_stgt.o +EXTRA_CFLAGS += -DSTGT_PLUGIN +endif ifeq ($(LINUX_RAMDISK), 1) target_core_mod-objs += target_core_rd.o EXTRA_CFLAGS += -DPYX_RAMDISK diff --git a/drivers/target/target_core_stgt.c b/drivers/target/target_core_stgt.c new file mode 100644 index 0000000..6a72503 --- /dev/null +++ b/drivers/target/target_core_stgt.c @@ -0,0 +1,916 @@ +/******************************************************************************* + * Filename: target_core_stgt.c + * + * This file contains the generic target mode <-> Linux SCSI subsystem plugin. + * + * Copyright (c) 2009 Rising Tide Systems, Inc. + * Copyright (c) 2009 Linux-iSCSI.org + * + * Nicholas A. Bellinger <nab@xxxxxxxxxx> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ******************************************************************************/ + + +#define TARGET_CORE_STGT_C + +#include <linux/version.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/blkdev.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/smp_lock.h> +#include <linux/genhd.h> +#include <linux/cdrom.h> +#include <linux/file.h> +#include <scsi/scsi.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_tgt.h> +#include <sd.h> +#include <sr.h> + +#include <target/target_core_base.h> +#include <target/target_core_device.h> +#include <target/target_core_transport.h> +#include <target/target_core_stgt.h> +#include <target/target_core_plugin.h> +#include <target/target_core_seobj.h> +#include <target/target_core_transport_plugin.h> + +#undef TARGET_CORE_STGT_C + +#define to_stgt_hba(d) container_of(d, struct stgt_hba_s, dev) + +static int stgt_host_no_cnt = 0; + +#define ISPRINT(a) ((a >= ' ') && (a <= '~')) + +static int pseudo_lld_bus_match(struct device *dev, + struct device_driver *dev_driver) +{ + return 1; +} + +static int stgt_lld_probe(struct device *); +static int stgt_lld_remove(struct device *); + +static struct bus_type stgt_lld_bus = { + .name = "stgt_bus", + .match = pseudo_lld_bus_match, + .probe = stgt_lld_probe, + .remove = stgt_lld_remove, +}; + +static struct device_driver stgt_driverfs_driver = { + .name = STGT_NAME, + .bus = &stgt_lld_bus, +}; + +static void stgt_primary_release(struct device *dev) +{ + return; +} + +static struct device stgt_primary = { + .init_name = "stgt_primary_0", + .release = stgt_primary_release, +}; + +static struct scsi_host_template stgt_driver_template = { + .name = STGT_NAME, + .module = THIS_MODULE, + .can_queue = 1, + .sg_tablesize = SG_ALL, + .use_clustering = DISABLE_CLUSTERING, + .max_sectors = SCSI_DEFAULT_MAX_SECTORS, + .transfer_response = stgt_transfer_response, + .eh_abort_handler = NULL, + .shost_attrs = NULL, + .proc_name = STGT_NAME, + .supported_mode = MODE_TARGET, +}; + +static void stgt_release_adapter(struct device * dev) +{ + stgt_hba_t *stgt_hba; + + stgt_hba = to_stgt_hba(dev); + kfree(stgt_hba); +} + +int stgt_plugin_init(void) +{ + int ret; + + ret = device_register(&stgt_primary); + if (ret) { + printk(KERN_ERR "device_register() failed for stgt_primary\n"); + return ret; + } + + ret = bus_register(&stgt_lld_bus); + if (ret) { + printk(KERN_ERR "bus_register() failed for stgt_ldd_bus\n"); + goto dev_unreg; + } + + ret = driver_register(&stgt_driverfs_driver); + if (ret) { + printk(KERN_ERR "driver_register() failed for" + " stgt_driverfs_driver\n"); + goto bus_unreg; + } + + printk(KERN_INFO "CORE_STGT[0]: Bus Initalization complete\n"); + return 0; + +bus_unreg: + bus_unregister(&stgt_lld_bus); +dev_unreg: + device_unregister(&stgt_primary); + return ret; +} + +void stgt_plugin_free(void) +{ + driver_unregister(&stgt_driverfs_driver); + bus_unregister(&stgt_lld_bus); + device_unregister(&stgt_primary); + + printk(KERN_INFO "CORE_STGT[0]: Bus release complete\n"); +} + +/* stgt_attach_hba(): + * + */ +int stgt_attach_hba(se_hba_t *hba, u32 host_id) +{ + stgt_hba_t *stgt_hba; + int err; + + stgt_hba = kzalloc(sizeof(stgt_hba_t), GFP_KERNEL); + if (!(stgt_hba)) { + printk("Unable to allocate stgt_hba_t\n"); + return -ENOMEM; + } + stgt_hba->se_hba = hba; + + stgt_hba->dev.bus = &stgt_lld_bus; + stgt_hba->dev.parent = &stgt_primary; + stgt_hba->dev.release = &stgt_release_adapter; + dev_set_name(&stgt_hba->dev, "stgt_adapter%d", stgt_host_no_cnt); + + err = device_register(&stgt_hba->dev); + if (err) { + printk(KERN_ERR "device_register() for stgt_hba failed: %d\n", err); + return err; + } + stgt_host_no_cnt++; + + return 0; +} + + +static int stgt_lld_probe(struct device *dev) +{ + se_hba_t *hba; + stgt_hba_t *stgt_hba; + struct Scsi_Host *sh; + int hba_depth, max_sectors, err; + + stgt_hba = to_stgt_hba(dev); + + sh = scsi_host_alloc(&stgt_driver_template, sizeof(stgt_hba)); + if (!(sh)) { + printk(KERN_ERR "scsi_host_alloc() failed\n"); + return -ENOMEM; + } + hba = stgt_hba->se_hba; + stgt_hba->scsi_host = sh; + + sh->max_id = 10; + sh->max_lun = 10; + + /* + * Assign the stgt_hba_t pointer to struct Scsi_Host->hostdata.. + */ + *(stgt_hba_t **)&sh->hostdata = stgt_hba; + + err = scsi_add_host(sh, &stgt_hba->dev); + if (err) { + printk(KERN_ERR "scsi_add_host() failed with err: %d\n", err); + return err; + } + + max_sectors = sh->max_sectors; + /* + * Usually the SCSI LLD will use the hostt->can_queue value to define + * its HBA TCQ depth. Some other drivers (like 2.6 megaraid) don't set + * this at all and set sh->can_queue at runtime. + */ + hba_depth = (sh->hostt->can_queue > sh->can_queue) ? + sh->hostt->can_queue : sh->can_queue; + atomic_set(&hba->left_queue_depth, hba_depth); + atomic_set(&hba->max_queue_depth, hba_depth); + + hba->hba_ptr = (void *) sh; + hba->transport = &stgt_template; + + printk(KERN_INFO "CORE_HBA[%d] - %s STGT HBA Driver %s on" + " Generic Target Core Stack %s\n", hba->hba_id, + PYX_ISCSI_VENDOR, STGT_VERSION, TARGET_CORE_MOD_VERSION); + printk(KERN_INFO "CORE_HBA[%d] - %s\n", hba->hba_id, (sh->hostt->name) ? + (sh->hostt->name) : "Unknown"); + printk(KERN_INFO "CORE_HBA[%d] - Attached STGT HBA to Generic" + " Target Core with TCQ Depth: %d MaxSectors: %hu\n", + hba->hba_id, atomic_read(&hba->max_queue_depth), max_sectors); + + return 0; +} + +static int stgt_lld_remove(struct device *dev) +{ + stgt_hba_t *stgt_hba; + struct Scsi_Host *sh; + + stgt_hba = to_stgt_hba(dev); + sh = stgt_hba->scsi_host; + + scsi_remove_host(sh); + scsi_host_put(sh); + + return 0; +} + +/* stgt_detach_hba(): (Part of se_subsystem_api_t template) + * + * + */ +int stgt_detach_hba(se_hba_t *hba) +{ + struct Scsi_Host *scsi_host = (struct Scsi_Host *) hba->hba_ptr; + stgt_hba_t *stgt_hba = *(stgt_hba_t **)shost_priv(scsi_host); + + printk(KERN_INFO "CORE_HBA[%d] - Detached STGT HBA: %s from" + " Generic Target Core\n", hba->hba_id, + (scsi_host->hostt->name) ? (scsi_host->hostt->name) : + "Unknown"); + + device_unregister(&stgt_hba->dev); + hba->hba_ptr = NULL; + + return 0; +} + +void *stgt_allocate_virtdevice(se_hba_t *hba, const char *name) +{ + stgt_dev_virt_t *sdv; + + sdv = kzalloc(sizeof(stgt_dev_virt_t), GFP_KERNEL); + if (!(sdv)) { + printk(KERN_ERR "Unable to allocate memory for stgt_dev_virt_t\n"); + return NULL; + } + sdv->sdv_se_hba = hba; + + printk(KERN_INFO "STGT: Allocated sdv: %p for %s\n", sdv, name); + return (void *)sdv; +} + +#warning FIXME: implement stgt_create_virtdevice() +se_device_t *stgt_create_virtdevice( + se_hba_t *hba, + se_subsystem_dev_t *se_dev, + void *p) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *)p; + struct Scsi_Host *sh = (struct Scsi_Host *) hba->hba_ptr; + + if (!(sdv)) { + printk(KERN_ERR "Unable to locate stgt_dev_virt_t" + " parameter\n"); + return NULL; + } + + printk(KERN_ERR "Unable to locate %d:%d:%d:%d\n", sh->host_no, + sdv->sdv_channel_id, sdv->sdv_target_id, sdv->sdv_lun_id); + + return NULL; +} + +/* stgt_activate_device(): (Part of se_subsystem_api_t template) + * + * + */ +int stgt_activate_device(se_device_t *dev) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *) dev->dev_ptr; + struct scsi_device *sd = (struct scsi_device *) sdv->sdv_sd; + struct Scsi_Host *sh = sd->host; + + printk(KERN_INFO "CORE_STGT[%d] - Activating %s Device with TCQ: %d at" + " SCSI Location (Channel/Target/LUN) %d/%d/%d\n", sh->host_no, + (sdv->sdv_legacy) ? "Legacy" : "REQ", sd->queue_depth, + sd->channel, sd->id, sd->lun); + + return 0; +} + +/* stgt_deactivate_device(): (Part of se_subsystem_api_t template) + * + * + */ +void stgt_deactivate_device(se_device_t *dev) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *) dev->dev_ptr; + struct scsi_device *sd = (struct scsi_device *) sdv->sdv_sd; + struct Scsi_Host *sh = sd->host; + + printk(KERN_INFO "CORE_STGT[%d] - Deactivating %s Device with TCQ: %d" + " at SCSI Location (Channel/Target/LUN) %d/%d/%d\n", + sh->host_no, (sdv->sdv_legacy) ? "Legacy" : "REQ", + sd->queue_depth, sd->channel, sd->id, sd->lun); +} + +/* stgt_free_device(): (Part of se_subsystem_api_t template) + * + * + */ +void stgt_free_device(void *p) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *) p; + struct scsi_device *sd = (struct scsi_device *) sdv->sdv_sd; + + if (sdv->sdv_bd) + sdv->sdv_bd = NULL; + + if (sd) { + if ((sd->type == TYPE_DISK) || (sd->type == TYPE_ROM)) + scsi_device_put(sd); + + sdv->sdv_sd = NULL; + } + + kfree(sdv); +} + +/* pscsi_transport_complete(): + * + * + */ +int stgt_transport_complete(se_task_t *task) +{ + stgt_plugin_task_t *st = (stgt_plugin_task_t *) task->transport_req; + int result; + + result = st->stgt_result; + if (status_byte(result) & CHECK_CONDITION) + return 1; + + return 0; +} + +/* stgt_allocate_request(): (Part of se_subsystem_api_t template) + * + * + */ +void *stgt_allocate_request( + se_task_t *task, + se_device_t *dev) +{ + stgt_plugin_task_t *st; + + st = kzalloc(sizeof(stgt_plugin_task_t), GFP_KERNEL); + if (!(st)) { + printk(KERN_ERR "Unable to allocate stgt_plugin_task_t\n"); + return NULL; + } + + return st; +} + +/* stgt_do_task(): (Part of se_subsystem_api_t template) + * + * + */ +int stgt_do_task(se_task_t *task) +{ + stgt_plugin_task_t *st = (stgt_plugin_task_t *) task->transport_req; + struct Scsi_Host *sh = task->se_dev->se_hba->hba_ptr; + struct scsi_cmnd *sc; + int tag = MSG_SIMPLE_TAG; + + sc = scsi_host_get_command(sh, st->stgt_direction, GFP_KERNEL); + if (!sc) { + printk(KERN_ERR "Unable to allocate memory for struct" + " scsi_cmnd\n"); + return PYX_TRANSPORT_LOGICAL_UNIT_COMMUNICATION_FAILURE; + } + + memcpy(sc->cmnd, st->stgt_cdb, MAX_COMMAND_SIZE); + sc->sdb.length = task->task_size; + sc->sdb.table.sgl = task->task_sg; + sc->tag = tag; + + BUG(); +#warning FIXME: Get struct scsi_lun for scsi_tgt_queue_command() +#if 0 + err = scsi_tgt_queue_command(sc, itn_id, (struct scsi_lun *)&cmd->lun, + cmd->tag); + if (err) { + printk(KERN_INFO "scsi_tgt_queue_command() failed for sc:" + " %p\n", sc); + scsi_host_put_command(sh, sc); + } +#endif + return PYX_TRANSPORT_SENT_TO_TRANSPORT; +} + +/* stgt_free_task(): (Part of se_subsystem_api_t template) + * + * + */ +void stgt_free_task(se_task_t *task) +{ + stgt_plugin_task_t *st = (stgt_plugin_task_t *)task->transport_req; + + kfree(st); +} + +ssize_t stgt_set_configfs_dev_params(se_hba_t *hba, + se_subsystem_dev_t *se_dev, + const char *page, + ssize_t count) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *) se_dev->se_dev_su_ptr; + struct Scsi_Host *sh = (struct Scsi_Host *) hba->hba_ptr; + char *buf, *cur, *ptr, *ptr2; + int params = 0, ret; + + buf = kzalloc(count, GFP_KERNEL); + if (!(buf)) { + printk(KERN_ERR "Unable to allocate memory for temporary" + " buffer\n"); + return -ENOMEM; + } + memcpy(buf, page, count); + cur = buf; + + while (cur) { + ptr = strstr(cur, "="); + if (!(ptr)) + goto out; + + *ptr = '\0'; + ptr++; + + ptr2 = strstr(cur, "scsi_channel_id"); + if ((ptr2)) { + transport_check_dev_params_delim(ptr, &cur); + ret = strict_strtoul(ptr, 0, + (unsigned long *)&sdv->sdv_channel_id); + if (ret < 0) { + printk(KERN_ERR "strict_strtoul() failed for" + " scsi_channel_id=\n"); + break; + } + printk(KERN_INFO "STGT[%d]: Referencing SCSI Channel" + " ID: %d\n", sh->host_no, sdv->sdv_channel_id); + sdv->sdv_flags |= PDF_HAS_CHANNEL_ID; + params++; + continue; + } + ptr2 = strstr(cur, "scsi_target_id"); + if ((ptr2)) { + transport_check_dev_params_delim(ptr, &cur); + ret = strict_strtoul(ptr, 0, + (unsigned long *)&sdv->sdv_target_id); + if (ret < 0) { + printk("strict_strtoul() failed for" + " strict_strtoul()\n"); + break; + } + printk(KERN_INFO "STGT[%d]: Referencing SCSI Target" + " ID: %d\n", sh->host_no, sdv->sdv_target_id); + sdv->sdv_flags |= PDF_HAS_TARGET_ID; + params++; + continue; + } + ptr2 = strstr(cur, "scsi_lun_id"); + if ((ptr2)) { + transport_check_dev_params_delim(ptr, &cur); + ret = strict_strtoul(ptr, 0, + (unsigned long *)&sdv->sdv_lun_id); + if (ret < 0) { + printk("strict_strtoul() failed for" + " scsi_lun_id=\n"); + break; + } + printk(KERN_INFO "STGT[%d]: Referencing SCSI LUN ID:" + " %d\n", sh->host_no, sdv->sdv_lun_id); + sdv->sdv_flags |= PDF_HAS_LUN_ID; + params++; + } else + cur = NULL; + } + +out: + kfree(buf); + return (params) ? count : -EINVAL; +} + +ssize_t stgt_check_configfs_dev_params( + se_hba_t *hba, + se_subsystem_dev_t *se_dev) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *) se_dev->se_dev_su_ptr; + + if (!(sdv->sdv_flags & PDF_HAS_CHANNEL_ID) || + !(sdv->sdv_flags & PDF_HAS_TARGET_ID) || + !(sdv->sdv_flags & PDF_HAS_TARGET_ID)) { + printk(KERN_ERR "Missing scsi_channel_id=, scsi_target_id= and" + " scsi_lun_id= parameters\n"); + return -1; + } + + return 0; +} + +ssize_t stgt_show_configfs_dev_params( + se_hba_t *hba, + se_subsystem_dev_t *se_dev, + char *page) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *) se_dev->se_dev_su_ptr; + int bl = 0; + + __stgt_get_dev_info(sdv, page, &bl); + return (ssize_t)bl; +} + +void stgt_get_plugin_info(void *p, char *b, int *bl) +{ + *bl += sprintf(b + *bl, "%s STGT <-> Target_Core_Mod Plugin %s\n", + PYX_ISCSI_VENDOR, STGT_VERSION); +} + +void stgt_get_hba_info(se_hba_t *hba, char *b, int *bl) +{ + struct Scsi_Host *sh = (struct Scsi_Host *) hba->hba_ptr; + + *bl += sprintf(b + *bl, "Core Host ID: %u SCSI Host ID: %u\n", + hba->hba_id, sh->host_no); + *bl += sprintf(b + *bl, " SCSI HBA: %s <local>\n", + (sh->hostt->name) ? (sh->hostt->name) : "Unknown"); +} + +void stgt_get_dev_info(se_device_t *dev, char *b, int *bl) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *) dev->dev_ptr; + + __stgt_get_dev_info(sdv, b, bl); +} + +void __stgt_get_dev_info(stgt_dev_virt_t *sdv, char *b, int *bl) +{ + int i; + struct scsi_device *sd = (struct scsi_device *) sdv->sdv_sd; + + *bl += sprintf(b + *bl, "STGT SCSI Device Bus Location:" + " Channel ID: %d Target ID: %d LUN: %d\n", + sdv->sdv_channel_id, sdv->sdv_target_id, sdv->sdv_lun_id); + + if (sd) { + *bl += sprintf(b + *bl, " "); + *bl += sprintf(b + *bl, "Vendor: "); + for (i = 0; i < 8; i++) { + if (ISPRINT(sd->vendor[i])) /* printable character? */ + *bl += sprintf(b + *bl, "%c", sd->vendor[i]); + else + *bl += sprintf(b + *bl, " "); + } + *bl += sprintf(b + *bl, " Model: "); + for (i = 0; i < 16; i++) { + if (ISPRINT(sd->model[i])) /* printable character ? */ + *bl += sprintf(b + *bl, "%c", sd->model[i]); + else + *bl += sprintf(b + *bl, " "); + } + *bl += sprintf(b + *bl, " Rev: "); + for (i = 0; i < 4; i++) { + if (ISPRINT(sd->rev[i])) /* printable character ? */ + *bl += sprintf(b + *bl, "%c", sd->rev[i]); + else + *bl += sprintf(b + *bl, " "); + } + + if (sd->type == TYPE_DISK) { + struct scsi_disk *sdisk = + dev_get_drvdata(&sd->sdev_gendev); + struct gendisk *disk = (struct gendisk *) sdisk->disk; + struct block_device *bdev = bdget(MKDEV(disk->major, + disk->first_minor)); + + bdev->bd_disk = disk; + *bl += sprintf(b + *bl, " %s\n", (!bdev->bd_holder) ? + "" : (bdev->bd_holder == + (struct scsi_device *)sd) ? + "CLAIMED: PSCSI" : "CLAIMED: OS"); + } else + *bl += sprintf(b + *bl, "\n"); + } + + return; +} + +/* stgt_map_task_SG(): + * + * + */ +int stgt_map_task_SG(se_task_t *task) +{ + return 0; +} + +/* stgt_map_task_non_SG(): + * + * + */ +int stgt_map_task_non_SG(se_task_t *task) +{ + return 0; +} + +/* stgt_CDB_inquiry(): + * + * + */ +int stgt_CDB_inquiry(se_task_t *task, u32 size) +{ + stgt_plugin_task_t *st = (stgt_plugin_task_t *) task->transport_req; + + st->stgt_direction = DMA_FROM_DEVICE; + return stgt_map_task_non_SG(task); +} + +int stgt_CDB_none(se_task_t *task, u32 size) +{ + stgt_plugin_task_t *st = (stgt_plugin_task_t *) task->transport_req; + + st->stgt_direction = DMA_NONE; + return 0; +} + +/* stgt_CDB_read_non_SG(): + * + * + */ +int stgt_CDB_read_non_SG(se_task_t *task, u32 size) +{ + stgt_plugin_task_t *pt = (stgt_plugin_task_t *) task->transport_req; + + pt->stgt_direction = DMA_FROM_DEVICE; + return stgt_map_task_non_SG(task); +} + +/* stgt_CDB_read_SG(): + * + * + */ +int stgt_CDB_read_SG(se_task_t *task, u32 size) +{ + stgt_plugin_task_t *pt = (stgt_plugin_task_t *) task->transport_req; + + pt->stgt_direction = DMA_FROM_DEVICE; + + if (stgt_map_task_SG(task) < 0) + return PYX_TRANSPORT_LOGICAL_UNIT_COMMUNICATION_FAILURE; + + return task->task_sg_num; +} + +/* stgt_CDB_write_non_SG(): + * + * + */ +int stgt_CDB_write_non_SG(se_task_t *task, u32 size) +{ + stgt_plugin_task_t *pt = (stgt_plugin_task_t *) task->transport_req; + + pt->stgt_direction = DMA_TO_DEVICE; + return stgt_map_task_non_SG(task); +} + +/* stgt_CDB_write_SG(): + * + * + */ +int stgt_CDB_write_SG(se_task_t *task, u32 size) +{ + stgt_plugin_task_t *st = (stgt_plugin_task_t *) task->transport_req; + + st->stgt_direction = DMA_TO_DEVICE; + + if (stgt_map_task_SG(task) < 0) + return PYX_TRANSPORT_LOGICAL_UNIT_COMMUNICATION_FAILURE; + + return task->task_sg_num; +} + +/* stgt_check_lba(): + * + * + */ +int stgt_check_lba(unsigned long long lba, se_device_t *dev) +{ + return 0; +} + +/* stgt_check_for_SG(): + * + * + */ +int stgt_check_for_SG(se_task_t *task) +{ + return task->task_sg_num; +} + +/* stgt_get_cdb(): + * + * + */ +unsigned char *stgt_get_cdb(se_task_t *task) +{ + stgt_plugin_task_t *pt = (stgt_plugin_task_t *) task->transport_req; + + return pt->stgt_cdb; +} + +/* stgt_get_sense_buffer(): + * + * + */ +unsigned char *stgt_get_sense_buffer(se_task_t *task) +{ + stgt_plugin_task_t *pt = (stgt_plugin_task_t *) task->transport_req; + + return (unsigned char *)&pt->stgt_sense[0]; +} + +/* stgt_get_blocksize(): + * + * + */ +u32 stgt_get_blocksize(se_device_t *dev) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *) dev->dev_ptr; + struct scsi_device *sd = (struct scsi_device *) sdv->sdv_sd; + + return sd->sector_size; +} + +/* stgt_get_device_rev(): + * + * + */ +u32 stgt_get_device_rev(se_device_t *dev) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *) dev->dev_ptr; + struct scsi_device *sd = (struct scsi_device *) sdv->sdv_sd; + + return (sd->scsi_level - 1) ? sd->scsi_level - 1 : 1; +} + +/* stgt_get_device_type(): + * + * + */ +u32 stgt_get_device_type(se_device_t *dev) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *) dev->dev_ptr; + struct scsi_device *sd = (struct scsi_device *) sdv->sdv_sd; + + return sd->type; +} + +/* stgt_get_dma_length(): + * + * + */ +u32 stgt_get_dma_length(u32 task_size, se_device_t *dev) +{ + return PAGE_SIZE; +} + +/* stgt_get_max_sectors(): + * + * + */ +u32 stgt_get_max_sectors(se_device_t *dev) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *) dev->dev_ptr; + struct scsi_device *sd = (struct scsi_device *) sdv->sdv_sd; + return (sd->host->max_sectors > sd->request_queue->max_sectors) ? + sd->request_queue->max_sectors : sd->host->max_sectors; +} + +/* stgt_get_queue_depth(): + * + * + */ +u32 stgt_get_queue_depth(se_device_t *dev) +{ + stgt_dev_virt_t *sdv = (stgt_dev_virt_t *) dev->dev_ptr; + struct scsi_device *sd = (struct scsi_device *) sdv->sdv_sd; + + return sd->queue_depth; +} + +void stgt_shutdown_hba(se_hba_t *hba) +{ + return; +} + +/* stgt_handle_SAM_STATUS_failures(): + * + * + */ +static inline void stgt_process_SAM_status( + se_task_t *task, + stgt_plugin_task_t *st) +{ + task->task_scsi_status = status_byte(st->stgt_result); + if ((task->task_scsi_status)) { + task->task_scsi_status <<= 1; + printk(KERN_INFO "PSCSI Status Byte exception at task: %p CDB:" + " 0x%02x Result: 0x%08x\n", task, st->stgt_cdb[0], + st->stgt_result); + } + + switch (host_byte(st->stgt_result)) { + case DID_OK: + transport_complete_task(task, (!task->task_scsi_status)); + break; + default: + printk(KERN_INFO "PSCSI Host Byte exception at task: %p CDB:" + " 0x%02x Result: 0x%08x\n", task, st->stgt_cdb[0], + st->stgt_result); + task->task_scsi_status = SAM_STAT_CHECK_CONDITION; + task->task_error_status = PYX_TRANSPORT_UNKNOWN_SAM_OPCODE; + TASK_CMD(task)->transport_error_status = + PYX_TRANSPORT_UNKNOWN_SAM_OPCODE; + transport_complete_task(task, 0); + break; + } + + return; +} + +/* + * Use for struct scsi_host_template->transfer_response() function pointer + * that is called from STGT in drivers/scsi/scsi_tgt_lib.c: + * scsi_tgt_transfer_response() + */ +int stgt_transfer_response(struct scsi_cmnd *sc, + void (*done)(struct scsi_cmnd *)) +{ + se_task_t *task = (se_task_t *)sc->SCp.ptr; + stgt_plugin_task_t *st; + + if (!task) { + printk("se_task_t is NULL!\n"); + BUG(); + } + st = (stgt_plugin_task_t *)task->transport_req; + if (!st) { + printk("stgt_plugin_task_t is NULL!\n"); + BUG(); + } + st->stgt_result = sc->request->errors; + st->stgt_resid = sc->request->data_len; + +#warning FIXME: Sense for STGT struct scsi_cmnd usage.. +#if 0 + memcpy(st->stgt_sense, sense, SCSI_SENSE_BUFFERSIZE); +#endif + stgt_process_SAM_status(task, st); + done(sc); + return 0; +} diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 4faaf12..ec130a6 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -516,6 +516,13 @@ void transport_load_plugins(void) pscsi_template.name, PLUGIN_TYPE_TRANSPORT, pscsi_template.get_plugin_info, NULL, NULL, &ret); #endif +#ifdef STGT_PLUGIN + plugin_register((void *)&stgt_template, stgt_template.type, + stgt_template.name, PLUGIN_TYPE_TRANSPORT, + stgt_template.get_plugin_info, + stgt_template.plugin_init, + stgt_template.plugin_free, &ret); +#endif #ifdef PYX_IBLOCK plugin_register((void *)&iblock_template, iblock_template.type, iblock_template.name, PLUGIN_TYPE_TRANSPORT, diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 0151915..182270c 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -150,7 +150,7 @@ /* se_device_t->type */ #define PSCSI 1 -#define SSCSI 2 +#define STGT 2 #define PATA 3 #define IBLOCK 4 #define RAMDISK_DR 5 diff --git a/include/target/target_core_stgt.h b/include/target/target_core_stgt.h new file mode 100644 index 0000000..89a4f37 --- /dev/null +++ b/include/target/target_core_stgt.h @@ -0,0 +1,200 @@ +/******************************************************************************* + * Filename: target_core_stgt.h + * + * This file contains the generic target mode <-> Linux STGT subsystem plugin. + * specific definitions and prototypes. + * + * Copyright (c) 2009 Rising Tide Systems, Inc. + * Copyright (c) 2009 Linux-iSCSI.org + * + * Nicholas A. Bellinger <nab@xxxxxxxxxx> + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ******************************************************************************/ + + +#ifndef TARGET_CORE_STGT_H +#define TARGET_CORE_STGT_H + +#define STGT_VERSION "v1.0" +#define STGT_NAME "stgt_tcm" + +/* used in pscsi_add_device_to_list() */ +#define STGT_DEFAULT_QUEUEDEPTH 1 + +#define PS_RETRY 5 +#define PS_TIMEOUT_DISK (15*HZ) +#define PS_TIMEOUT_OTHER (500*HZ) + +extern se_global_t *se_global; +extern struct block_device *linux_blockdevice_claim(int, int, void *); +extern int linux_blockdevice_release(int, int, struct block_device *); +extern int linux_blockdevice_check(int, int); + +extern int stgt_CDB_inquiry(se_task_t *, u32); +extern int stgt_CDB_none(se_task_t *, u32); +extern int stgt_CDB_read_non_SG(se_task_t *, u32); +extern int stgt_CDB_read_SG(se_task_t *, u32); +extern int stgt_CDB_write_non_SG(se_task_t *, u32); +extern int stgt_CDB_write_SG(se_task_t *, u32); + +#ifndef STGT_INCLUDE_STRUCTS +extern int stgt_plugin_init(void); +extern void stgt_plugin_free(void); +extern int stgt_attach_hba(se_hba_t *, u32); +extern int stgt_detach_hba(se_hba_t *); +#if 0 +extern int pscsi_claim_phydevice(se_hba_t *, se_device_t *); +extern int pscsi_release_phydevice(se_device_t *); +#endif +extern void *stgt_allocate_virtdevice(se_hba_t *, const char *); +extern se_device_t *stgt_create_virtdevice(se_hba_t *, se_subsystem_dev_t *, + void *); +extern int stgt_activate_device(se_device_t *); +extern void stgt_deactivate_device(se_device_t *); +extern void stgt_free_device(void *); +extern int stgt_transport_complete(se_task_t *); +extern void *stgt_allocate_request(se_task_t *, se_device_t *); +extern int stgt_do_task(se_task_t *); +extern void stgt_free_task(se_task_t *); +extern ssize_t stgt_set_configfs_dev_params(se_hba_t *, se_subsystem_dev_t *, + const char *, ssize_t); +extern ssize_t stgt_check_configfs_dev_params(se_hba_t *, + se_subsystem_dev_t *); +extern ssize_t stgt_show_configfs_dev_params(se_hba_t *, se_subsystem_dev_t *, + char *); +#if 0 +extern se_device_t *scsi_create_virtdevice_from_fd(se_subsystem_dev_t *, + const char *); +#endif +extern void stgt_get_plugin_info(void *, char *, int *); +extern void stgt_get_hba_info(se_hba_t *, char *, int *); +extern void stgt_get_dev_info(se_device_t *, char *, int *); +extern int stgt_check_lba(unsigned long long, se_device_t *); +extern int stgt_check_for_SG(se_task_t *); +extern unsigned char *stgt_get_cdb(se_task_t *); +extern unsigned char *stgt_get_sense_buffer(se_task_t *); +extern u32 stgt_get_blocksize(se_device_t *); +extern u32 stgt_get_device_rev(se_device_t *); +extern u32 stgt_get_device_type(se_device_t *); +extern u32 stgt_get_dma_length(u32, se_device_t *); +extern u32 stgt_get_max_sectors(se_device_t *); +extern u32 stgt_get_queue_depth(se_device_t *); +extern void stgt_shutdown_hba(struct se_hba_s *); +extern void stgt_req_done(struct request *, int); +extern int stgt_transfer_response(struct scsi_cmnd *, + void (*done)(struct scsi_cmnd *)); +#endif + +#include <linux/device.h> +#include <scsi/scsi_driver.h> +#include <scsi/scsi_device.h> +#include <linux/kref.h> +#include <linux/kobject.h> + +typedef struct stgt_plugin_task_s { + unsigned char stgt_cdb[SCSI_CDB_SIZE]; + unsigned char stgt_sense[SCSI_SENSE_BUFFERSIZE]; + int stgt_direction; + int stgt_result; + u32 stgt_resid; + struct scsi_cmnd *stgt_cmd; +} stgt_plugin_task_t; + +#define PDF_HAS_CHANNEL_ID 0x01 +#define PDF_HAS_TARGET_ID 0x02 +#define PDF_HAS_LUN_ID 0x04 +#define PDF_HAS_VPD_UNIT_SERIAL 0x08 +#define PDF_HAS_VPD_DEV_IDENT 0x10 + +typedef struct stgt_dev_virt_s { + int sdv_flags; + int sdv_legacy; /* Use scsi_execute_async() from HTCL */ + int sdv_channel_id; + int sdv_target_id; + int sdv_lun_id; + struct block_device *sdv_bd; /* Temporary for v2.6.28 */ + struct scsi_device *sdv_sd; + struct se_hba_s *sdv_se_hba; +} stgt_dev_virt_t; + +typedef struct stgt_hba_s { + struct device dev; + struct se_hba_s *se_hba; + struct Scsi_Host *scsi_host; +} stgt_hba_t; + +extern void __stgt_get_dev_info(stgt_dev_virt_t *, char *, int *); + +/* + * We use the generic command sequencer, so we must setup + * se_subsystem_spc_t. + */ +#ifndef STGT_INCLUDE_STRUCTS + +se_subsystem_spc_t stgt_template_spc = { + .inquiry = stgt_CDB_inquiry, + .none = stgt_CDB_none, + .read_non_SG = stgt_CDB_read_non_SG, + .read_SG = stgt_CDB_read_SG, + .write_non_SG = stgt_CDB_write_non_SG, + .write_SG = stgt_CDB_write_SG, +}; + +se_subsystem_api_t stgt_template = { + .name = "stgt", \ + .type = STGT, \ + .transport_type = TRANSPORT_PLUGIN_VHBA_PDEV, \ + .attach_hba = stgt_attach_hba, \ + .detach_hba = stgt_detach_hba, \ + .activate_device = stgt_activate_device, \ + .deactivate_device = stgt_deactivate_device, \ + .claim_phydevice = NULL, \ + .allocate_virtdevice = stgt_allocate_virtdevice, \ + .create_virtdevice = stgt_create_virtdevice, \ + .free_device = stgt_free_device, \ + .release_phydevice = NULL, \ + .transport_complete = stgt_transport_complete, \ + .allocate_request = stgt_allocate_request, \ + .do_task = stgt_do_task, \ + .free_task = stgt_free_task, \ + .check_configfs_dev_params = stgt_check_configfs_dev_params, \ + .set_configfs_dev_params = stgt_set_configfs_dev_params, \ + .show_configfs_dev_params = stgt_show_configfs_dev_params, \ + .create_virtdevice_from_fd = NULL, \ + .plugin_init = stgt_plugin_init, \ + .plugin_free = stgt_plugin_free, \ + .get_plugin_info = stgt_get_plugin_info, \ + .get_hba_info = stgt_get_hba_info, \ + .get_dev_info = stgt_get_dev_info, \ + .check_lba = stgt_check_lba, \ + .check_for_SG = stgt_check_for_SG, \ + .get_cdb = stgt_get_cdb, \ + .get_sense_buffer = stgt_get_sense_buffer, \ + .get_blocksize = stgt_get_blocksize, \ + .get_device_rev = stgt_get_device_rev, \ + .get_device_type = stgt_get_device_type, \ + .get_dma_length = stgt_get_dma_length, \ + .get_max_sectors = stgt_get_max_sectors, \ + .get_queue_depth = stgt_get_queue_depth, \ + .shutdown_hba = stgt_shutdown_hba, \ + .write_pending = NULL, \ + .spc = &stgt_template_spc, \ +}; + +#endif + +#endif /*** TARGET_CORE_STGT_H ***/ diff --git a/include/target/target_core_transport_plugin.h b/include/target/target_core_transport_plugin.h index 118ee3a..7e46360 100644 --- a/include/target/target_core_transport_plugin.h +++ b/include/target/target_core_transport_plugin.h @@ -35,6 +35,13 @@ extern se_subsystem_api_t pscsi_template; #endif /* PARALLEL_SCSI */ +#ifdef STGT_PLUGIN +#define STGT_INCLUDE_STRUCTS +#include <target/target_core_stgt.h> +#undef STGT_INCLUDE_STRUCTS +extern se_subsystem_api_t stgt_template; +#endif /* STGT_PLUGIN */ + #ifdef PYX_IBLOCK #define IBLOCK_INCLUDE_STRUCTS #include <target/target_core_iblock.h> -- 1.5.4.1 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html