summaryrefslogtreecommitdiff
path: root/drivers/target
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2011-04-26 10:22:15 +0200
committerJiri Kosina <jkosina@suse.cz>2011-04-26 10:22:59 +0200
commit07f9479a40cc778bc1462ada11f95b01360ae4ff (patch)
tree0676cf38df3844004bb3ebfd99dfa67a4a8998f5 /drivers/target
parent9d5e6bdb3013acfb311ab407eeca0b6a6a3dedbf (diff)
parentcd2e49e90f1cae7726c9a2c54488d881d7f1cd1c (diff)
Merge branch 'master' into for-next
Fast-forwarded to current state of Linus' tree as there are patches to be applied for files that didn't exist on the old branch.
Diffstat (limited to 'drivers/target')
-rw-r--r--drivers/target/Kconfig2
-rw-r--r--drivers/target/Makefile7
-rw-r--r--drivers/target/loopback/Kconfig11
-rw-r--r--drivers/target/loopback/Makefile1
-rw-r--r--drivers/target/loopback/tcm_loop.c1579
-rw-r--r--drivers/target/loopback/tcm_loop.h77
-rw-r--r--drivers/target/target_core_alua.c10
-rw-r--r--drivers/target/target_core_configfs.c117
-rw-r--r--drivers/target/target_core_device.c42
-rw-r--r--drivers/target/target_core_fabric_configfs.c209
-rw-r--r--drivers/target/target_core_fabric_lib.c3
-rw-r--r--drivers/target/target_core_file.c30
-rw-r--r--drivers/target/target_core_hba.c15
-rw-r--r--drivers/target/target_core_iblock.c35
-rw-r--r--drivers/target/target_core_pr.c6
-rw-r--r--drivers/target/target_core_pscsi.c22
-rw-r--r--drivers/target/target_core_rd.c15
-rw-r--r--drivers/target/target_core_rd.h2
-rw-r--r--drivers/target/target_core_stat.c1810
-rw-r--r--drivers/target/target_core_stat.h8
-rw-r--r--drivers/target/target_core_transport.c51
-rw-r--r--drivers/target/target_core_ua.c4
22 files changed, 3904 insertions, 152 deletions
diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig
index 2fac3be209ac..9ef2dbbfa62b 100644
--- a/drivers/target/Kconfig
+++ b/drivers/target/Kconfig
@@ -29,4 +29,6 @@ config TCM_PSCSI
Say Y here to enable the TCM/pSCSI subsystem plugin for non-buffered
passthrough access to Linux/SCSI device
+source "drivers/target/loopback/Kconfig"
+
endif
diff --git a/drivers/target/Makefile b/drivers/target/Makefile
index 973bb190ef57..1178bbfc68fe 100644
--- a/drivers/target/Makefile
+++ b/drivers/target/Makefile
@@ -1,4 +1,3 @@
-EXTRA_CFLAGS += -I$(srctree)/drivers/target/ -I$(srctree)/drivers/scsi/
target_core_mod-y := target_core_configfs.o \
target_core_device.o \
@@ -13,7 +12,8 @@ target_core_mod-y := target_core_configfs.o \
target_core_transport.o \
target_core_cdb.o \
target_core_ua.o \
- target_core_rd.o
+ target_core_rd.o \
+ target_core_stat.o
obj-$(CONFIG_TARGET_CORE) += target_core_mod.o
@@ -21,3 +21,6 @@ obj-$(CONFIG_TARGET_CORE) += target_core_mod.o
obj-$(CONFIG_TCM_IBLOCK) += target_core_iblock.o
obj-$(CONFIG_TCM_FILEIO) += target_core_file.o
obj-$(CONFIG_TCM_PSCSI) += target_core_pscsi.o
+
+# Fabric modules
+obj-$(CONFIG_LOOPBACK_TARGET) += loopback/
diff --git a/drivers/target/loopback/Kconfig b/drivers/target/loopback/Kconfig
new file mode 100644
index 000000000000..57dcbc2d711b
--- /dev/null
+++ b/drivers/target/loopback/Kconfig
@@ -0,0 +1,11 @@
+config LOOPBACK_TARGET
+ tristate "TCM Virtual SAS target and Linux/SCSI LDD fabric loopback module"
+ help
+ Say Y here to enable the TCM Virtual SAS target and Linux/SCSI LLD
+ fabric loopback module.
+
+config LOOPBACK_TARGET_CDB_DEBUG
+ bool "TCM loopback fabric module CDB debug code"
+ depends on LOOPBACK_TARGET
+ help
+ Say Y here to enable the TCM loopback fabric module CDB debug code
diff --git a/drivers/target/loopback/Makefile b/drivers/target/loopback/Makefile
new file mode 100644
index 000000000000..6abebdf95659
--- /dev/null
+++ b/drivers/target/loopback/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_LOOPBACK_TARGET) += tcm_loop.o
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c
new file mode 100644
index 000000000000..aed4e464d31c
--- /dev/null
+++ b/drivers/target/loopback/tcm_loop.c
@@ -0,0 +1,1579 @@
+/*******************************************************************************
+ *
+ * This file contains the Linux/SCSI LLD virtual SCSI initiator driver
+ * for emulated SAS initiator ports
+ *
+ * © Copyright 2011 RisingTide Systems LLC.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@risingtidesystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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.
+ ****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/configfs.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libsas.h> /* For TASK_ATTR_* */
+
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_fabric_lib.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_device.h>
+#include <target/target_core_tpg.h>
+#include <target/target_core_tmr.h>
+
+#include "tcm_loop.h"
+
+#define to_tcm_loop_hba(hba) container_of(hba, struct tcm_loop_hba, dev)
+
+/* Local pointer to allocated TCM configfs fabric module */
+static struct target_fabric_configfs *tcm_loop_fabric_configfs;
+
+static struct kmem_cache *tcm_loop_cmd_cache;
+
+static int tcm_loop_hba_no_cnt;
+
+/*
+ * Allocate a tcm_loop cmd descriptor from target_core_mod code
+ *
+ * Can be called from interrupt context in tcm_loop_queuecommand() below
+ */
+static struct se_cmd *tcm_loop_allocate_core_cmd(
+ struct tcm_loop_hba *tl_hba,
+ struct se_portal_group *se_tpg,
+ struct scsi_cmnd *sc)
+{
+ struct se_cmd *se_cmd;
+ struct se_session *se_sess;
+ struct tcm_loop_nexus *tl_nexus = tl_hba->tl_nexus;
+ struct tcm_loop_cmd *tl_cmd;
+ int sam_task_attr;
+
+ if (!tl_nexus) {
+ scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus"
+ " does not exist\n");
+ set_host_byte(sc, DID_ERROR);
+ return NULL;
+ }
+ se_sess = tl_nexus->se_sess;
+
+ tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_ATOMIC);
+ if (!tl_cmd) {
+ printk(KERN_ERR "Unable to allocate struct tcm_loop_cmd\n");
+ set_host_byte(sc, DID_ERROR);
+ return NULL;
+ }
+ se_cmd = &tl_cmd->tl_se_cmd;
+ /*
+ * Save the pointer to struct scsi_cmnd *sc
+ */
+ tl_cmd->sc = sc;
+ /*
+ * Locate the SAM Task Attr from struct scsi_cmnd *
+ */
+ if (sc->device->tagged_supported) {
+ switch (sc->tag) {
+ case HEAD_OF_QUEUE_TAG:
+ sam_task_attr = TASK_ATTR_HOQ;
+ break;
+ case ORDERED_QUEUE_TAG:
+ sam_task_attr = TASK_ATTR_ORDERED;
+ break;
+ default:
+ sam_task_attr = TASK_ATTR_SIMPLE;
+ break;
+ }
+ } else
+ sam_task_attr = TASK_ATTR_SIMPLE;
+
+ /*
+ * Initialize struct se_cmd descriptor from target_core_mod infrastructure
+ */
+ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess,
+ scsi_bufflen(sc), sc->sc_data_direction, sam_task_attr,
+ &tl_cmd->tl_sense_buf[0]);
+
+ /*
+ * Signal BIDI usage with T_TASK(cmd)->t_tasks_bidi
+ */
+ if (scsi_bidi_cmnd(sc))
+ T_TASK(se_cmd)->t_tasks_bidi = 1;
+ /*
+ * Locate the struct se_lun pointer and attach it to struct se_cmd
+ */
+ if (transport_get_lun_for_cmd(se_cmd, NULL, tl_cmd->sc->device->lun) < 0) {
+ kmem_cache_free(tcm_loop_cmd_cache, tl_cmd);
+ set_host_byte(sc, DID_NO_CONNECT);
+ return NULL;
+ }
+
+ transport_device_setup_cmd(se_cmd);
+ return se_cmd;
+}
+
+/*
+ * Called by struct target_core_fabric_ops->new_cmd_map()
+ *
+ * Always called in process context. A non zero return value
+ * here will signal to handle an exception based on the return code.
+ */
+static int tcm_loop_new_cmd_map(struct se_cmd *se_cmd)
+{
+ struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
+ struct tcm_loop_cmd, tl_se_cmd);
+ struct scsi_cmnd *sc = tl_cmd->sc;
+ void *mem_ptr, *mem_bidi_ptr = NULL;
+ u32 sg_no_bidi = 0;
+ int ret;
+ /*
+ * Allocate the necessary tasks to complete the received CDB+data
+ */
+ ret = transport_generic_allocate_tasks(se_cmd, tl_cmd->sc->cmnd);
+ if (ret == -1) {
+ /* Out of Resources */
+ return PYX_TRANSPORT_LU_COMM_FAILURE;
+ } else if (ret == -2) {
+ /*
+ * Handle case for SAM_STAT_RESERVATION_CONFLICT
+ */
+ if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT)
+ return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ /*
+ * Otherwise, return SAM_STAT_CHECK_CONDITION and return
+ * sense data.
+ */
+ return PYX_TRANSPORT_USE_SENSE_REASON;
+ }
+ /*
+ * Setup the struct scatterlist memory from the received
+ * struct scsi_cmnd.
+ */
+ if (scsi_sg_count(sc)) {
+ se_cmd->se_cmd_flags |= SCF_PASSTHROUGH_SG_TO_MEM;
+ mem_ptr = (void *)scsi_sglist(sc);
+ /*
+ * For BIDI commands, pass in the extra READ buffer
+ * to transport_generic_map_mem_to_cmd() below..
+ */
+ if (T_TASK(se_cmd)->t_tasks_bidi) {
+ struct scsi_data_buffer *sdb = scsi_in(sc);
+
+ mem_bidi_ptr = (void *)sdb->table.sgl;
+ sg_no_bidi = sdb->table.nents;
+ }
+ } else {
+ /*
+ * Used for DMA_NONE
+ */
+ mem_ptr = NULL;
+ }
+ /*
+ * Map the SG memory into struct se_mem->page linked list using the same
+ * physical memory at sg->page_link.
+ */
+ ret = transport_generic_map_mem_to_cmd(se_cmd, mem_ptr,
+ scsi_sg_count(sc), mem_bidi_ptr, sg_no_bidi);
+ if (ret < 0)
+ return PYX_TRANSPORT_LU_COMM_FAILURE;
+
+ return 0;
+}
+
+/*
+ * Called from struct target_core_fabric_ops->check_stop_free()
+ */
+static void tcm_loop_check_stop_free(struct se_cmd *se_cmd)
+{
+ /*
+ * Do not release struct se_cmd's containing a valid TMR
+ * pointer. These will be released directly in tcm_loop_device_reset()
+ * with transport_generic_free_cmd().
+ */
+ if (se_cmd->se_tmr_req)
+ return;
+ /*
+ * Release the struct se_cmd, which will make a callback to release
+ * struct tcm_loop_cmd * in tcm_loop_deallocate_core_cmd()
+ */
+ transport_generic_free_cmd(se_cmd, 0, 1, 0);
+}
+
+/*
+ * Called from struct target_core_fabric_ops->release_cmd_to_pool()
+ */
+static void tcm_loop_deallocate_core_cmd(struct se_cmd *se_cmd)
+{
+ struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
+ struct tcm_loop_cmd, tl_se_cmd);
+
+ kmem_cache_free(tcm_loop_cmd_cache, tl_cmd);
+}
+
+static int tcm_loop_proc_info(struct Scsi_Host *host, char *buffer,
+ char **start, off_t offset,
+ int length, int inout)
+{
+ return sprintf(buffer, "tcm_loop_proc_info()\n");
+}
+
+static int tcm_loop_driver_probe(struct device *);
+static int tcm_loop_driver_remove(struct device *);
+
+static int pseudo_lld_bus_match(struct device *dev,
+ struct device_driver *dev_driver)
+{
+ return 1;
+}
+
+static struct bus_type tcm_loop_lld_bus = {
+ .name = "tcm_loop_bus",
+ .match = pseudo_lld_bus_match,
+ .probe = tcm_loop_driver_probe,
+ .remove = tcm_loop_driver_remove,
+};
+
+static struct device_driver tcm_loop_driverfs = {
+ .name = "tcm_loop",
+ .bus = &tcm_loop_lld_bus,
+};
+/*
+ * Used with root_device_register() in tcm_loop_alloc_core_bus() below
+ */
+struct device *tcm_loop_primary;
+
+/*
+ * Copied from drivers/scsi/libfc/fc_fcp.c:fc_change_queue_depth() and
+ * drivers/scsi/libiscsi.c:iscsi_change_queue_depth()
+ */
+static int tcm_loop_change_queue_depth(
+ struct scsi_device *sdev,
+ int depth,
+ int reason)
+{
+ switch (reason) {
+ case SCSI_QDEPTH_DEFAULT:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ break;
+ case SCSI_QDEPTH_QFULL:
+ scsi_track_queue_full(sdev, depth);
+ break;
+ case SCSI_QDEPTH_RAMP_UP:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return sdev->queue_depth;
+}
+
+/*
+ * Main entry point from struct scsi_host_template for incoming SCSI CDB+Data
+ * from Linux/SCSI subsystem for SCSI low level device drivers (LLDs)
+ */
+static int tcm_loop_queuecommand(
+ struct Scsi_Host *sh,
+ struct scsi_cmnd *sc)
+{
+ struct se_cmd *se_cmd;
+ struct se_portal_group *se_tpg;
+ struct tcm_loop_hba *tl_hba;
+ struct tcm_loop_tpg *tl_tpg;
+
+ TL_CDB_DEBUG("tcm_loop_queuecommand() %d:%d:%d:%d got CDB: 0x%02x"
+ " scsi_buf_len: %u\n", sc->device->host->host_no,
+ sc->device->id, sc->device->channel, sc->device->lun,
+ sc->cmnd[0], scsi_bufflen(sc));
+ /*
+ * Locate the tcm_loop_hba_t pointer
+ */
+ tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
+ tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
+ se_tpg = &tl_tpg->tl_se_tpg;
+ /*
+ * Determine the SAM Task Attribute and allocate tl_cmd and
+ * tl_cmd->tl_se_cmd from TCM infrastructure
+ */
+ se_cmd = tcm_loop_allocate_core_cmd(tl_hba, se_tpg, sc);
+ if (!se_cmd) {
+ sc->scsi_done(sc);
+ return 0;
+ }
+ /*
+ * Queue up the newly allocated to be processed in TCM thread context.
+ */
+ transport_generic_handle_cdb_map(se_cmd);
+ return 0;
+}
+
+/*
+ * Called from SCSI EH process context to issue a LUN_RESET TMR
+ * to struct scsi_device
+ */
+static int tcm_loop_device_reset(struct scsi_cmnd *sc)
+{
+ struct se_cmd *se_cmd = NULL;
+ struct se_portal_group *se_tpg;
+ struct se_session *se_sess;
+ struct tcm_loop_cmd *tl_cmd = NULL;
+ struct tcm_loop_hba *tl_hba;
+ struct tcm_loop_nexus *tl_nexus;
+ struct tcm_loop_tmr *tl_tmr = NULL;
+ struct tcm_loop_tpg *tl_tpg;
+ int ret = FAILED;
+ /*
+ * Locate the tcm_loop_hba_t pointer
+ */
+ tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
+ /*
+ * Locate the tl_nexus and se_sess pointers
+ */
+ tl_nexus = tl_hba->tl_nexus;
+ if (!tl_nexus) {
+ printk(KERN_ERR "Unable to perform device reset without"
+ " active I_T Nexus\n");
+ return FAILED;
+ }
+ se_sess = tl_nexus->se_sess;
+ /*
+ * Locate the tl_tpg and se_tpg pointers from TargetID in sc->device->id
+ */
+ tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
+ se_tpg = &tl_tpg->tl_se_tpg;
+
+ tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_KERNEL);
+ if (!tl_cmd) {
+ printk(KERN_ERR "Unable to allocate memory for tl_cmd\n");
+ return FAILED;
+ }
+
+ tl_tmr = kzalloc(sizeof(struct tcm_loop_tmr), GFP_KERNEL);
+ if (!tl_tmr) {
+ printk(KERN_ERR "Unable to allocate memory for tl_tmr\n");
+ goto release;
+ }
+ init_waitqueue_head(&tl_tmr->tl_tmr_wait);
+
+ se_cmd = &tl_cmd->tl_se_cmd;
+ /*
+ * Initialize struct se_cmd descriptor from target_core_mod infrastructure
+ */
+ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, 0,
+ DMA_NONE, TASK_ATTR_SIMPLE,
+ &tl_cmd->tl_sense_buf[0]);
+ /*
+ * Allocate the LUN_RESET TMR
+ */
+ se_cmd->se_tmr_req = core_tmr_alloc_req(se_cmd, (void *)tl_tmr,
+ TMR_LUN_RESET);
+ if (!se_cmd->se_tmr_req)
+ goto release;
+ /*
+ * Locate the underlying TCM struct se_lun from sc->device->lun
+ */
+ if (transport_get_lun_for_tmr(se_cmd, sc->device->lun) < 0)
+ goto release;
+ /*
+ * Queue the TMR to TCM Core and sleep waiting for tcm_loop_queue_tm_rsp()
+ * to wake us up.
+ */
+ transport_generic_handle_tmr(se_cmd);
+ wait_event(tl_tmr->tl_tmr_wait, atomic_read(&tl_tmr->tmr_complete));
+ /*
+ * The TMR LUN_RESET has completed, check the response status and
+ * then release allocations.
+ */
+ ret = (se_cmd->se_tmr_req->response == TMR_FUNCTION_COMPLETE) ?
+ SUCCESS : FAILED;
+release:
+ if (se_cmd)
+ transport_generic_free_cmd(se_cmd, 1, 1, 0);
+ else
+ kmem_cache_free(tcm_loop_cmd_cache, tl_cmd);
+ kfree(tl_tmr);
+ return ret;
+}
+
+static int tcm_loop_slave_alloc(struct scsi_device *sd)
+{
+ set_bit(QUEUE_FLAG_BIDI, &sd->request_queue->queue_flags);
+ return 0;
+}
+
+static int tcm_loop_slave_configure(struct scsi_device *sd)
+{
+ return 0;
+}
+
+static struct scsi_host_template tcm_loop_driver_template = {
+ .proc_info = tcm_loop_proc_info,
+ .proc_name = "tcm_loopback",
+ .name = "TCM_Loopback",
+ .queuecommand = tcm_loop_queuecommand,
+ .change_queue_depth = tcm_loop_change_queue_depth,
+ .eh_device_reset_handler = tcm_loop_device_reset,
+ .can_queue = TL_SCSI_CAN_QUEUE,
+ .this_id = -1,
+ .sg_tablesize = TL_SCSI_SG_TABLESIZE,
+ .cmd_per_lun = TL_SCSI_CMD_PER_LUN,
+ .max_sectors = TL_SCSI_MAX_SECTORS,
+ .use_clustering = DISABLE_CLUSTERING,
+ .slave_alloc = tcm_loop_slave_alloc,
+ .slave_configure = tcm_loop_slave_configure,
+ .module = THIS_MODULE,
+};
+
+static int tcm_loop_driver_probe(struct device *dev)
+{
+ struct tcm_loop_hba *tl_hba;
+ struct Scsi_Host *sh;
+ int error;
+
+ tl_hba = to_tcm_loop_hba(dev);
+
+ sh = scsi_host_alloc(&tcm_loop_driver_template,
+ sizeof(struct tcm_loop_hba));
+ if (!sh) {
+ printk(KERN_ERR "Unable to allocate struct scsi_host\n");
+ return -ENODEV;
+ }
+ tl_hba->sh = sh;
+
+ /*
+ * Assign the struct tcm_loop_hba pointer to struct Scsi_Host->hostdata
+ */
+ *((struct tcm_loop_hba **)sh->hostdata) = tl_hba;
+ /*
+ * Setup single ID, Channel and LUN for now..
+ */
+ sh->max_id = 2;
+ sh->max_lun = 0;
+ sh->max_channel = 0;
+ sh->max_cmd_len = TL_SCSI_MAX_CMD_LEN;
+
+ error = scsi_add_host(sh, &tl_hba->dev);
+ if (error) {
+ printk(KERN_ERR "%s: scsi_add_host failed\n", __func__);
+ scsi_host_put(sh);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int tcm_loop_driver_remove(struct device *dev)
+{
+ struct tcm_loop_hba *tl_hba;
+ struct Scsi_Host *sh;
+
+ tl_hba = to_tcm_loop_hba(dev);
+ sh = tl_hba->sh;
+
+ scsi_remove_host(sh);
+ scsi_host_put(sh);
+ return 0;
+}
+
+static void tcm_loop_release_adapter(struct device *dev)
+{
+ struct tcm_loop_hba *tl_hba = to_tcm_loop_hba(dev);
+
+ kfree(tl_hba);
+}
+
+/*
+ * Called from tcm_loop_make_scsi_hba() in tcm_loop_configfs.c
+ */
+static int tcm_loop_setup_hba_bus(struct tcm_loop_hba *tl_hba, int tcm_loop_host_id)
+{
+ int ret;
+
+ tl_hba->dev.bus = &tcm_loop_lld_bus;
+ tl_hba->dev.parent = tcm_loop_primary;
+ tl_hba->dev.release = &tcm_loop_release_adapter;
+ dev_set_name(&tl_hba->dev, "tcm_loop_adapter_%d", tcm_loop_host_id);
+
+ ret = device_register(&tl_hba->dev);
+ if (ret) {
+ printk(KERN_ERR "device_register() failed for"
+ " tl_hba->dev: %d\n", ret);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * Called from tcm_loop_fabric_init() in tcl_loop_fabric.c to load the emulated
+ * tcm_loop SCSI bus.
+ */
+static int tcm_loop_alloc_core_bus(void)
+{
+ int ret;
+
+ tcm_loop_primary = root_device_register("tcm_loop_0");
+ if (IS_ERR(tcm_loop_primary)) {
+ printk(KERN_ERR "Unable to allocate tcm_loop_primary\n");
+ return PTR_ERR(tcm_loop_primary);
+ }
+
+ ret = bus_register(&tcm_loop_lld_bus);
+ if (ret) {
+ printk(KERN_ERR "bus_register() failed for tcm_loop_lld_bus\n");
+ goto dev_unreg;
+ }
+
+ ret = driver_register(&tcm_loop_driverfs);
+ if (ret) {
+ printk(KERN_ERR "driver_register() failed for"
+ "tcm_loop_driverfs\n");
+ goto bus_unreg;
+ }
+
+ printk(KERN_INFO "Initialized TCM Loop Core Bus\n");
+ return ret;
+
+bus_unreg:
+ bus_unregister(&tcm_loop_lld_bus);
+dev_unreg:
+ root_device_unregister(tcm_loop_primary);
+ return ret;
+}
+
+static void tcm_loop_release_core_bus(void)
+{
+ driver_unregister(&tcm_loop_driverfs);
+ bus_unregister(&tcm_loop_lld_bus);
+ root_device_unregister(tcm_loop_primary);
+
+ printk(KERN_INFO "Releasing TCM Loop Core BUS\n");
+}
+
+static char *tcm_loop_get_fabric_name(void)
+{
+ return "loopback";
+}
+
+static u8 tcm_loop_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+ struct tcm_loop_tpg *tl_tpg =
+ (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr;
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+ /*
+ * tl_proto_id is set at tcm_loop_configfs.c:tcm_loop_make_scsi_hba()
+ * time based on the protocol dependent prefix of the passed configfs group.
+ *
+ * Based upon tl_proto_id, TCM_Loop emulates the requested fabric
+ * ProtocolID using target_core_fabric_lib.c symbols.
+ */
+ switch (tl_hba->tl_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_get_fabric_proto_ident(se_tpg);
+ case SCSI_PROTOCOL_FCP:
+ return fc_get_fabric_proto_ident(se_tpg);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_get_fabric_proto_ident(se_tpg);
+ default:
+ printk(KERN_ERR "Unknown tl_proto_id: 0x%02x, using"
+ " SAS emulation\n", tl_hba->tl_proto_id);
+ break;
+ }
+
+ return sas_get_fabric_proto_ident(se_tpg);
+}
+
+static char *tcm_loop_get_endpoint_wwn(struct se_portal_group *se_tpg)
+{
+ struct tcm_loop_tpg *tl_tpg =
+ (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr;
+ /*
+ * Return the passed NAA identifier for the SAS Target Port
+ */
+ return &tl_tpg->tl_hba->tl_wwn_address[0];
+}
+
+static u16 tcm_loop_get_tag(struct se_portal_group *se_tpg)
+{
+ struct tcm_loop_tpg *tl_tpg =
+ (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr;
+ /*
+ * This Tag is used when forming SCSI Name identifier in EVPD=1 0x83
+ * to represent the SCSI Target Port.
+ */
+ return tl_tpg->tl_tpgt;
+}
+
+static u32 tcm_loop_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static u32 tcm_loop_get_pr_transport_id(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code,
+ unsigned char *buf)
+{
+ struct tcm_loop_tpg *tl_tpg =
+ (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr;
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+
+ switch (tl_hba->tl_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ case SCSI_PROTOCOL_FCP:
+ return fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ default:
+ printk(KERN_ERR "Unknown tl_proto_id: 0x%02x, using"
+ " SAS emulation\n", tl_hba->tl_proto_id);
+ break;
+ }
+
+ return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+}
+
+static u32 tcm_loop_get_pr_transport_id_len(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code)
+{
+ struct tcm_loop_tpg *tl_tpg =
+ (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr;
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+
+ switch (tl_hba->tl_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ case SCSI_PROTOCOL_FCP:
+ return fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ default:
+ printk(KERN_ERR "Unknown tl_proto_id: 0x%02x, using"
+ " SAS emulation\n", tl_hba->tl_proto_id);
+ break;
+ }
+
+ return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+}
+
+/*
+ * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above
+ * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations.
+ */
+static char *tcm_loop_parse_pr_out_transport_id(
+ struct se_portal_group *se_tpg,
+ const char *buf,
+ u32 *out_tid_len,
+ char **port_nexus_ptr)
+{
+ struct tcm_loop_tpg *tl_tpg =
+ (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr;
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+
+ switch (tl_hba->tl_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ case SCSI_PROTOCOL_FCP:
+ return fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ default:
+ printk(KERN_ERR "Unknown tl_proto_id: 0x%02x, using"
+ " SAS emulation\n", tl_hba->tl_proto_id);
+ break;
+ }
+
+ return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+}
+
+/*
+ * Returning (1) here allows for target_core_mod struct se_node_acl to be generated
+ * based upon the incoming fabric dependent SCSI Initiator Port
+ */
+static int tcm_loop_check_demo_mode(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int tcm_loop_check_demo_mode_cache(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+/*
+ * Allow I_T Nexus full READ-WRITE access without explict Initiator Node ACLs for
+ * local virtual Linux/SCSI LLD passthrough into VM hypervisor guest
+ */
+static int tcm_loop_check_demo_mode_write_protect(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+/*
+ * Because TCM_Loop does not use explict ACLs and MappedLUNs, this will
+ * never be called for TCM_Loop by target_core_fabric_configfs.c code.
+ * It has been added here as a nop for target_fabric_tf_ops_check()
+ */
+static int tcm_loop_check_prod_mode_write_protect(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+static struct se_node_acl *tcm_loop_tpg_alloc_fabric_acl(
+ struct se_portal_group *se_tpg)
+{
+ struct tcm_loop_nacl *tl_nacl;
+
+ tl_nacl = kzalloc(sizeof(struct tcm_loop_nacl), GFP_KERNEL);
+ if (!tl_nacl) {
+ printk(KERN_ERR "Unable to allocate struct tcm_loop_nacl\n");
+ return NULL;
+ }
+
+ return &tl_nacl->se_node_acl;
+}
+
+static void tcm_loop_tpg_release_fabric_acl(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl)
+{
+ struct tcm_loop_nacl *tl_nacl = container_of(se_nacl,
+ struct tcm_loop_nacl, se_node_acl);
+
+ kfree(tl_nacl);
+}
+
+static u32 tcm_loop_get_inst_index(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static void tcm_loop_new_cmd_failure(struct se_cmd *se_cmd)
+{
+ /*
+ * Since TCM_loop is already passing struct scatterlist data from
+ * struct scsi_cmnd, no more Linux/SCSI failure dependent state need
+ * to be handled here.
+ */
+ return;
+}
+
+static int tcm_loop_is_state_remove(struct se_cmd *se_cmd)
+{
+ /*
+ * Assume struct scsi_cmnd is not in remove state..
+ */
+ return 0;
+}
+
+static int tcm_loop_sess_logged_in(struct se_session *se_sess)
+{
+ /*
+ * Assume that TL Nexus is always active
+ */
+ return 1;
+}
+
+static u32 tcm_loop_sess_get_index(struct se_session *se_sess)
+{
+ return 1;
+}
+
+static void tcm_loop_set_default_node_attributes(struct se_node_acl *se_acl)
+{
+ return;
+}
+
+static u32 tcm_loop_get_task_tag(struct se_cmd *se_cmd)
+{
+ return 1;
+}
+
+static int tcm_loop_get_cmd_state(struct se_cmd *se_cmd)
+{
+ struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
+ struct tcm_loop_cmd, tl_se_cmd);
+
+ return tl_cmd->sc_cmd_state;
+}
+
+static int tcm_loop_shutdown_session(struct se_session *se_sess)
+{
+ return 0;
+}
+
+static void tcm_loop_close_session(struct se_session *se_sess)
+{
+ return;
+};
+
+static void tcm_loop_stop_session(
+ struct se_session *se_sess,
+ int sess_sleep,
+ int conn_sleep)
+{
+ return;
+}
+
+static void tcm_loop_fall_back_to_erl0(struct se_session *se_sess)
+{
+ return;
+}
+
+static int tcm_loop_write_pending(struct se_cmd *se_cmd)
+{
+ /*
+ * Since Linux/SCSI has already sent down a struct scsi_cmnd
+ * sc->sc_data_direction of DMA_TO_DEVICE with struct scatterlist array
+ * memory, and memory has already been mapped to struct se_cmd->t_mem_list
+ * format with transport_generic_map_mem_to_cmd().
+ *
+ * We now tell TCM to add this WRITE CDB directly into the TCM storage
+ * object execution queue.
+ */
+ transport_generic_process_write(se_cmd);
+ return 0;
+}
+
+static int tcm_loop_write_pending_status(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static int tcm_loop_queue_data_in(struct se_cmd *se_cmd)
+{
+ struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
+ struct tcm_loop_cmd, tl_se_cmd);
+ struct scsi_cmnd *sc = tl_cmd->sc;
+
+ TL_CDB_DEBUG("tcm_loop_queue_data_in() called for scsi_cmnd: %p"
+ " cdb: 0x%02x\n", sc, sc->cmnd[0]);
+
+ sc->result = SAM_STAT_GOOD;
+ set_host_byte(sc, DID_OK);
+ sc->scsi_done(sc);
+ return 0;
+}
+
+static int tcm_loop_queue_status(struct se_cmd *se_cmd)
+{
+ struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
+ struct tcm_loop_cmd, tl_se_cmd);
+ struct scsi_cmnd *sc = tl_cmd->sc;
+
+ TL_CDB_DEBUG("tcm_loop_queue_status() called for scsi_cmnd: %p"
+ " cdb: 0x%02x\n", sc, sc->cmnd[0]);
+
+ if (se_cmd->sense_buffer &&
+ ((se_cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ||
+ (se_cmd->se_cmd_flags & SCF_EMULATED_TASK_SENSE))) {
+
+ memcpy((void *)sc->sense_buffer, (void *)se_cmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE);
+ sc->result = SAM_STAT_CHECK_CONDITION;
+ set_driver_byte(sc, DRIVER_SENSE);
+ } else
+ sc->result = se_cmd->scsi_status;
+
+ set_host_byte(sc, DID_OK);
+ sc->scsi_done(sc);
+ return 0;
+}
+
+static int tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+ struct se_tmr_req *se_tmr = se_cmd->se_tmr_req;
+ struct tcm_loop_tmr *tl_tmr = se_tmr->fabric_tmr_ptr;
+ /*
+ * The SCSI EH thread will be sleeping on se_tmr->tl_tmr_wait, go ahead
+ * and wake up the wait_queue_head_t in tcm_loop_device_reset()
+ */
+ atomic_set(&tl_tmr->tmr_complete, 1);
+ wake_up(&tl_tmr->tl_tmr_wait);
+ return 0;
+}
+
+static u16 tcm_loop_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length)
+{
+ return 0;
+}
+
+static u16 tcm_loop_get_fabric_sense_len(void)
+{
+ return 0;
+}
+
+static u64 tcm_loop_pack_lun(unsigned int lun)
+{
+ u64 result;
+
+ /* LSB of lun into byte 1 big-endian */
+ result = ((lun & 0xff) << 8);
+ /* use flat space addressing method */
+ result |= 0x40 | ((lun >> 8) & 0x3f);
+
+ return cpu_to_le64(result);
+}
+
+static char *tcm_loop_dump_proto_id(struct tcm_loop_hba *tl_hba)
+{
+ switch (tl_hba->tl_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return "SAS";
+ case SCSI_PROTOCOL_FCP:
+ return "FCP";
+ case SCSI_PROTOCOL_ISCSI:
+ return "iSCSI";
+ default:
+ break;
+ }
+
+ return "Unknown";
+}
+
+/* Start items for tcm_loop_port_cit */
+
+static int tcm_loop_port_link(
+ struct se_portal_group *se_tpg,
+ struct se_lun *lun)
+{
+ struct tcm_loop_tpg *tl_tpg = container_of(se_tpg,
+ struct tcm_loop_tpg, tl_se_tpg);
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+
+ atomic_inc(&tl_tpg->tl_tpg_port_count);
+ smp_mb__after_atomic_inc();
+ /*
+ * Add Linux/SCSI struct scsi_device by HCTL
+ */
+ scsi_add_device(tl_hba->sh, 0, tl_tpg->tl_tpgt, lun->unpacked_lun);
+
+ printk(KERN_INFO "TCM_Loop_ConfigFS: Port Link Successful\n");
+ return 0;
+}
+
+static void tcm_loop_port_unlink(
+ struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct scsi_device *sd;
+ struct tcm_loop_hba *tl_hba;
+ struct tcm_loop_tpg *tl_tpg;
+
+ tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, tl_se_tpg);
+ tl_hba = tl_tpg->tl_hba;
+
+ sd = scsi_device_lookup(tl_hba->sh, 0, tl_tpg->tl_tpgt,
+ se_lun->unpacked_lun);
+ if (!sd) {
+ printk(KERN_ERR "Unable to locate struct scsi_device for %d:%d:"
+ "%d\n", 0, tl_tpg->tl_tpgt, se_lun->unpacked_lun);
+ return;
+ }
+ /*
+ * Remove Linux/SCSI struct scsi_device by HCTL
+ */
+ scsi_remove_device(sd);
+ scsi_device_put(sd);
+
+ atomic_dec(&tl_tpg->tl_tpg_port_count);
+ smp_mb__after_atomic_dec();
+
+ printk(KERN_INFO "TCM_Loop_ConfigFS: Port Unlink Successful\n");
+}
+
+/* End items for tcm_loop_port_cit */
+
+/* Start items for tcm_loop_nexus_cit */
+
+static int tcm_loop_make_nexus(
+ struct tcm_loop_tpg *tl_tpg,
+ const char *name)
+{
+ struct se_portal_group *se_tpg;
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+ struct tcm_loop_nexus *tl_nexus;
+
+ if (tl_tpg->tl_hba->tl_nexus) {
+ printk(KERN_INFO "tl_tpg->tl_hba->tl_nexus already exists\n");
+ return -EEXIST;
+ }
+ se_tpg = &tl_tpg->tl_se_tpg;
+
+ tl_nexus = kzalloc(sizeof(struct tcm_loop_nexus), GFP_KERNEL);
+ if (!tl_nexus) {
+ printk(KERN_ERR "Unable to allocate struct tcm_loop_nexus\n");
+ return -ENOMEM;
+ }
+ /*
+ * Initialize the struct se_session pointer
+ */
+ tl_nexus->se_sess = transport_init_session();
+ if (!tl_nexus->se_sess)
+ goto out;
+ /*
+ * Since we are running in 'demo mode' this call with generate a
+ * struct se_node_acl for the tcm_loop struct se_portal_group with the SCSI
+ * Initiator port name of the passed configfs group 'name'.
+ */
+ tl_nexus->se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
+ se_tpg, (unsigned char *)name);
+ if (!tl_nexus->se_sess->se_node_acl) {
+ transport_free_session(tl_nexus->se_sess);
+ goto out;
+ }
+ /*
+ * Now, register the SAS I_T Nexus as active with the call to
+ * transport_register_session()
+ */
+ __transport_register_session(se_tpg, tl_nexus->se_sess->se_node_acl,
+ tl_nexus->se_sess, (void *)tl_nexus);
+ tl_tpg->tl_hba->tl_nexus = tl_nexus;
+ printk(KERN_INFO "TCM_Loop_ConfigFS: Established I_T Nexus to emulated"
+ " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba),
+ name);
+ return 0;
+
+out:
+ kfree(tl_nexus);
+ return -ENOMEM;
+}
+
+static int tcm_loop_drop_nexus(
+ struct tcm_loop_tpg *tpg)
+{
+ struct se_session *se_sess;
+ struct tcm_loop_nexus *tl_nexus;
+ struct tcm_loop_hba *tl_hba = tpg->tl_hba;
+
+ tl_nexus = tpg->tl_hba->tl_nexus;
+ if (!tl_nexus)
+ return -ENODEV;
+
+ se_sess = tl_nexus->se_sess;
+ if (!se_sess)
+ return -ENODEV;
+
+ if (atomic_read(&tpg->tl_tpg_port_count)) {
+ printk(KERN_ERR "Unable to remove TCM_Loop I_T Nexus with"
+ " active TPG port count: %d\n",
+ atomic_read(&tpg->tl_tpg_port_count));
+ return -EPERM;
+ }
+
+ printk(KERN_INFO "TCM_Loop_ConfigFS: Removing I_T Nexus to emulated"
+ " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba),
+ tl_nexus->se_sess->se_node_acl->initiatorname);
+ /*
+ * Release the SCSI I_T Nexus to the emulated SAS Target Port
+ */
+ transport_deregister_session(tl_nexus->se_sess);
+ tpg->tl_hba->tl_nexus = NULL;
+ kfree(tl_nexus);
+ return 0;
+}
+
+/* End items for tcm_loop_nexus_cit */
+
+static ssize_t tcm_loop_tpg_show_nexus(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct tcm_loop_tpg *tl_tpg = container_of(se_tpg,
+ struct tcm_loop_tpg, tl_se_tpg);
+ struct tcm_loop_nexus *tl_nexus;
+ ssize_t ret;
+
+ tl_nexus = tl_tpg->tl_hba->tl_nexus;
+ if (!tl_nexus)
+ return -ENODEV;
+
+ ret = snprintf(page, PAGE_SIZE, "%s\n",
+ tl_nexus->se_sess->se_node_acl->initiatorname);
+
+ return ret;
+}
+
+static ssize_t tcm_loop_tpg_store_nexus(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct tcm_loop_tpg *tl_tpg = container_of(se_tpg,
+ struct tcm_loop_tpg, tl_se_tpg);
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+ unsigned char i_port[TL_WWN_ADDR_LEN], *ptr, *port_ptr;
+ int ret;
+ /*
+ * Shutdown the active I_T nexus if 'NULL' is passed..
+ */
+ if (!strncmp(page, "NULL", 4)) {
+ ret = tcm_loop_drop_nexus(tl_tpg);
+ return (!ret) ? count : ret;
+ }
+ /*
+ * Otherwise make sure the passed virtual Initiator port WWN matches
+ * the fabric protocol_id set in tcm_loop_make_scsi_hba(), and call
+ * tcm_loop_make_nexus()
+ */
+ if (strlen(page) > TL_WWN_ADDR_LEN) {
+ printk(KERN_ERR "Emulated NAA Sas Address: %s, exceeds"
+ " max: %d\n", page, TL_WWN_ADDR_LEN);
+ return -EINVAL;
+ }
+ snprintf(&i_port[0], TL_WWN_ADDR_LEN, "%s", page);
+
+ ptr = strstr(i_port, "naa.");
+ if (ptr) {
+ if (tl_hba->tl_proto_id != SCSI_PROTOCOL_SAS) {
+ printk(KERN_ERR "Passed SAS Initiator Port %s does not"
+ " match target port protoid: %s\n", i_port,
+ tcm_loop_dump_proto_id(tl_hba));
+ return -EINVAL;
+ }
+ port_ptr = &i_port[0];
+ goto check_newline;
+ }
+ ptr = strstr(i_port, "fc.");
+ if (ptr) {
+ if (tl_hba->tl_proto_id != SCSI_PROTOCOL_FCP) {
+ printk(KERN_ERR "Passed FCP Initiator Port %s does not"
+ " match target port protoid: %s\n", i_port,
+ tcm_loop_dump_proto_id(tl_hba));
+ return -EINVAL;
+ }
+ port_ptr = &i_port[3]; /* Skip over "fc." */
+ goto check_newline;
+ }
+ ptr = strstr(i_port, "iqn.");
+ if (ptr) {
+ if (tl_hba->tl_proto_id != SCSI_PROTOCOL_ISCSI) {
+ printk(KERN_ERR "Passed iSCSI Initiator Port %s does not"
+ " match target port protoid: %s\n", i_port,
+ tcm_loop_dump_proto_id(tl_hba));
+ return -EINVAL;
+ }
+ port_ptr = &i_port[0];
+ goto check_newline;
+ }
+ printk(KERN_ERR "Unable to locate prefix for emulated Initiator Port:"
+ " %s\n", i_port);
+ return -EINVAL;
+ /*
+ * Clear any trailing newline for the NAA WWN
+ */
+check_newline:
+ if (i_port[strlen(i_port)-1] == '\n')
+ i_port[strlen(i_port)-1] = '\0';
+
+ ret = tcm_loop_make_nexus(tl_tpg, port_ptr);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+TF_TPG_BASE_ATTR(tcm_loop, nexus, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *tcm_loop_tpg_attrs[] = {
+ &tcm_loop_tpg_nexus.attr,
+ NULL,
+};
+
+/* Start items for tcm_loop_naa_cit */
+
+struct se_portal_group *tcm_loop_make_naa_tpg(
+ struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct tcm_loop_hba *tl_hba = container_of(wwn,
+ struct tcm_loop_hba, tl_hba_wwn);
+ struct tcm_loop_tpg *tl_tpg;
+ char *tpgt_str, *end_ptr;
+ int ret;
+ unsigned short int tpgt;
+
+ tpgt_str = strstr(name, "tpgt_");
+ if (!tpgt_str) {
+ printk(KERN_ERR "Unable to locate \"tpgt_#\" directory"
+ " group\n");
+ return ERR_PTR(-EINVAL);
+ }
+ tpgt_str += 5; /* Skip ahead of "tpgt_" */
+ tpgt = (unsigned short int) simple_strtoul(tpgt_str, &end_ptr, 0);
+
+ if (tpgt > TL_TPGS_PER_HBA) {
+ printk(KERN_ERR "Passed tpgt: %hu exceeds TL_TPGS_PER_HBA:"
+ " %u\n", tpgt, TL_TPGS_PER_HBA);
+ return ERR_PTR(-EINVAL);
+ }
+ tl_tpg = &tl_hba->tl_hba_tpgs[tpgt];
+ tl_tpg->tl_hba = tl_hba;
+ tl_tpg->tl_tpgt = tpgt;
+ /*
+ * Register the tl_tpg as a emulated SAS TCM Target Endpoint
+ */
+ ret = core_tpg_register(&tcm_loop_fabric_configfs->tf_ops,
+ wwn, &tl_tpg->tl_se_tpg, (void *)tl_tpg,
+ TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0)
+ return ERR_PTR(-ENOMEM);
+
+ printk(KERN_INFO "TCM_Loop_ConfigFS: Allocated Emulated %s"
+ " Target Port %s,t,0x%04x\n", tcm_loop_dump_proto_id(tl_hba),
+ config_item_name(&wwn->wwn_group.cg_item), tpgt);
+
+ return &tl_tpg->tl_se_tpg;
+}
+
+void tcm_loop_drop_naa_tpg(
+ struct se_portal_group *se_tpg)
+{
+ struct se_wwn *wwn = se_tpg->se_tpg_wwn;
+ struct tcm_loop_tpg *tl_tpg = container_of(se_tpg,
+ struct tcm_loop_tpg, tl_se_tpg);
+ struct tcm_loop_hba *tl_hba;
+ unsigned short tpgt;
+
+ tl_hba = tl_tpg->tl_hba;
+ tpgt = tl_tpg->tl_tpgt;
+ /*
+ * Release the I_T Nexus for the Virtual SAS link if present
+ */
+ tcm_loop_drop_nexus(tl_tpg);
+ /*
+ * Deregister the tl_tpg as a emulated SAS TCM Target Endpoint
+ */
+ core_tpg_deregister(se_tpg);
+
+ printk(KERN_INFO "TCM_Loop_ConfigFS: Deallocated Emulated %s"
+ " Target Port %s,t,0x%04x\n", tcm_loop_dump_proto_id(tl_hba),
+ config_item_name(&wwn->wwn_group.cg_item), tpgt);
+}
+
+/* End items for tcm_loop_naa_cit */
+
+/* Start items for tcm_loop_cit */
+
+struct se_wwn *tcm_loop_make_scsi_hba(
+ struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct tcm_loop_hba *tl_hba;
+ struct Scsi_Host *sh;
+ char *ptr;
+ int ret, off = 0;
+
+ tl_hba = kzalloc(sizeof(struct tcm_loop_hba), GFP_KERNEL);
+ if (!tl_hba) {
+ printk(KERN_ERR "Unable to allocate struct tcm_loop_hba\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ /*
+ * Determine the emulated Protocol Identifier and Target Port Name
+ * based on the incoming configfs directory name.
+ */
+ ptr = strstr(name, "naa.");
+ if (ptr) {
+ tl_hba->tl_proto_id = SCSI_PROTOCOL_SAS;
+ goto check_len;
+ }
+ ptr = strstr(name, "fc.");
+ if (ptr) {
+ tl_hba->tl_proto_id = SCSI_PROTOCOL_FCP;
+ off = 3; /* Skip over "fc." */
+ goto check_len;
+ }
+ ptr = strstr(name, "iqn.");
+ if (ptr) {
+ tl_hba->tl_proto_id = SCSI_PROTOCOL_ISCSI;
+ goto check_len;
+ }
+
+ printk(KERN_ERR "Unable to locate prefix for emulated Target Port:"
+ " %s\n", name);
+ return ERR_PTR(-EINVAL);
+
+check_len:
+ if (strlen(name) > TL_WWN_ADDR_LEN) {
+ printk(KERN_ERR "Emulated NAA %s Address: %s, exceeds"
+ " max: %d\n", name, tcm_loop_dump_proto_id(tl_hba),
+ TL_WWN_ADDR_LEN);
+ kfree(tl_hba);
+ return ERR_PTR(-EINVAL);
+ }
+ snprintf(&tl_hba->tl_wwn_address[0], TL_WWN_ADDR_LEN, "%s", &name[off]);
+
+ /*
+ * Call device_register(tl_hba->dev) to register the emulated
+ * Linux/SCSI LLD of type struct Scsi_Host at tl_hba->sh after
+ * device_register() callbacks in tcm_loop_driver_probe()
+ */
+ ret = tcm_loop_setup_hba_bus(tl_hba, tcm_loop_hba_no_cnt);
+ if (ret)
+ goto out;
+
+ sh = tl_hba->sh;
+ tcm_loop_hba_no_cnt++;
+ printk(KERN_INFO "TCM_Loop_ConfigFS: Allocated emulated Target"
+ " %s Address: %s at Linux/SCSI Host ID: %d\n",
+ tcm_loop_dump_proto_id(tl_hba), name, sh->host_no);
+
+ return &tl_hba->tl_hba_wwn;
+out:
+ kfree(tl_hba);
+ return ERR_PTR(ret);
+}
+
+void tcm_loop_drop_scsi_hba(
+ struct se_wwn *wwn)
+{
+ struct tcm_loop_hba *tl_hba = container_of(wwn,
+ struct tcm_loop_hba, tl_hba_wwn);
+ int host_no = tl_hba->sh->host_no;
+ /*
+ * Call device_unregister() on the original tl_hba->dev.
+ * tcm_loop_fabric_scsi.c:tcm_loop_release_adapter() will
+ * release *tl_hba;
+ */
+ device_unregister(&tl_hba->dev);
+
+ printk(KERN_INFO "TCM_Loop_ConfigFS: Deallocated emulated Target"
+ " SAS Address: %s at Linux/SCSI Host ID: %d\n",
+ config_item_name(&wwn->wwn_group.cg_item), host_no);
+}
+
+/* Start items for tcm_loop_cit */
+static ssize_t tcm_loop_wwn_show_attr_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "TCM Loopback Fabric module %s\n", TCM_LOOP_VERSION);
+}
+
+TF_WWN_ATTR_RO(tcm_loop, version);
+
+static struct configfs_attribute *tcm_loop_wwn_attrs[] = {
+ &tcm_loop_wwn_version.attr,
+ NULL,
+};
+
+/* End items for tcm_loop_cit */
+
+static int tcm_loop_register_configfs(void)
+{
+ struct target_fabric_configfs *fabric;
+ struct config_group *tf_cg;
+ int ret;
+ /*
+ * Set the TCM Loop HBA counter to zero
+ */
+ tcm_loop_hba_no_cnt = 0;
+ /*
+ * Register the top level struct config_item_type with TCM core
+ */
+ fabric = target_fabric_configfs_init(THIS_MODULE, "loopback");
+ if (!fabric) {
+ printk(KERN_ERR "tcm_loop_register_configfs() failed!\n");
+ return -1;
+ }
+ /*
+ * Setup the fabric API of function pointers used by target_core_mod
+ */
+ fabric->tf_ops.get_fabric_name = &tcm_loop_get_fabric_name;
+ fabric->tf_ops.get_fabric_proto_ident = &tcm_loop_get_fabric_proto_ident;
+ fabric->tf_ops.tpg_get_wwn = &tcm_loop_get_endpoint_wwn;
+ fabric->tf_ops.tpg_get_tag = &tcm_loop_get_tag;
+ fabric->tf_ops.tpg_get_default_depth = &tcm_loop_get_default_depth;
+ fabric->tf_ops.tpg_get_pr_transport_id = &tcm_loop_get_pr_transport_id;
+ fabric->tf_ops.tpg_get_pr_transport_id_len =
+ &tcm_loop_get_pr_transport_id_len;
+ fabric->tf_ops.tpg_parse_pr_out_transport_id =
+ &tcm_loop_parse_pr_out_transport_id;
+ fabric->tf_ops.tpg_check_demo_mode = &tcm_loop_check_demo_mode;
+ fabric->tf_ops.tpg_check_demo_mode_cache =
+ &tcm_loop_check_demo_mode_cache;
+ fabric->tf_ops.tpg_check_demo_mode_write_protect =
+ &tcm_loop_check_demo_mode_write_protect;
+ fabric->tf_ops.tpg_check_prod_mode_write_protect =
+ &tcm_loop_check_prod_mode_write_protect;
+ /*
+ * The TCM loopback fabric module runs in demo-mode to a local
+ * virtual SCSI device, so fabric dependent initator ACLs are
+ * not required.
+ */
+ fabric->tf_ops.tpg_alloc_fabric_acl = &tcm_loop_tpg_alloc_fabric_acl;
+ fabric->tf_ops.tpg_release_fabric_acl =
+ &tcm_loop_tpg_release_fabric_acl;
+ fabric->tf_ops.tpg_get_inst_index = &tcm_loop_get_inst_index;
+ /*
+ * Since tcm_loop is mapping physical memory from Linux/SCSI
+ * struct scatterlist arrays for each struct scsi_cmnd I/O,
+ * we do not need TCM to allocate a iovec array for
+ * virtual memory address mappings
+ */
+ fabric->tf_ops.alloc_cmd_iovecs = NULL;
+ /*
+ * Used for setting up remaining TCM resources in process context
+ */
+ fabric->tf_ops.new_cmd_map = &tcm_loop_new_cmd_map;
+ fabric->tf_ops.check_stop_free = &tcm_loop_check_stop_free;
+ fabric->tf_ops.release_cmd_to_pool = &tcm_loop_deallocate_core_cmd;
+ fabric->tf_ops.release_cmd_direct = &tcm_loop_deallocate_core_cmd;
+ fabric->tf_ops.shutdown_session = &tcm_loop_shutdown_session;
+ fabric->tf_ops.close_session = &tcm_loop_close_session;
+ fabric->tf_ops.stop_session = &tcm_loop_stop_session;
+ fabric->tf_ops.fall_back_to_erl0 = &tcm_loop_fall_back_to_erl0;
+ fabric->tf_ops.sess_logged_in = &tcm_loop_sess_logged_in;
+ fabric->tf_ops.sess_get_index = &tcm_loop_sess_get_index;
+ fabric->tf_ops.sess_get_initiator_sid = NULL;
+ fabric->tf_ops.write_pending = &tcm_loop_write_pending;
+ fabric->tf_ops.write_pending_status = &tcm_loop_write_pending_status;
+ /*
+ * Not used for TCM loopback
+ */
+ fabric->tf_ops.set_default_node_attributes =
+ &tcm_loop_set_default_node_attributes;
+ fabric->tf_ops.get_task_tag = &tcm_loop_get_task_tag;
+ fabric->tf_ops.get_cmd_state = &tcm_loop_get_cmd_state;
+ fabric->tf_ops.new_cmd_failure = &tcm_loop_new_cmd_failure;
+ fabric->tf_ops.queue_data_in = &tcm_loop_queue_data_in;
+ fabric->tf_ops.queue_status = &tcm_loop_queue_status;
+ fabric->tf_ops.queue_tm_rsp = &tcm_loop_queue_tm_rsp;
+ fabric->tf_ops.set_fabric_sense_len = &tcm_loop_set_fabric_sense_len;
+ fabric->tf_ops.get_fabric_sense_len = &tcm_loop_get_fabric_sense_len;
+ fabric->tf_ops.is_state_remove = &tcm_loop_is_state_remove;
+ fabric->tf_ops.pack_lun = &tcm_loop_pack_lun;
+
+ tf_cg = &fabric->tf_group;
+ /*
+ * Setup function pointers for generic logic in target_core_fabric_configfs.c
+ */
+ fabric->tf_ops.fabric_make_wwn = &tcm_loop_make_scsi_hba;
+ fabric->tf_ops.fabric_drop_wwn = &tcm_loop_drop_scsi_hba;
+ fabric->tf_ops.fabric_make_tpg = &tcm_loop_make_naa_tpg;
+ fabric->tf_ops.fabric_drop_tpg = &tcm_loop_drop_naa_tpg;
+ /*
+ * fabric_post_link() and fabric_pre_unlink() are used for
+ * registration and release of TCM Loop Virtual SCSI LUNs.
+ */
+ fabric->tf_ops.fabric_post_link = &tcm_loop_port_link;
+ fabric->tf_ops.fabric_pre_unlink = &tcm_loop_port_unlink;
+ fabric->tf_ops.fabric_make_np = NULL;
+ fabric->tf_ops.fabric_drop_np = NULL;
+ /*
+ * Setup default attribute lists for various fabric->tf_cit_tmpl
+ */
+ TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+ /*
+ * Once fabric->tf_ops has been setup, now register the fabric for
+ * use within TCM
+ */
+ ret = target_fabric_configfs_register(fabric);
+ if (ret < 0) {
+ printk(KERN_ERR "target_fabric_configfs_register() for"
+ " TCM_Loop failed!\n");
+ target_fabric_configfs_free(fabric);
+ return -1;
+ }
+ /*
+ * Setup our local pointer to *fabric.
+ */
+ tcm_loop_fabric_configfs = fabric;
+ printk(KERN_INFO "TCM_LOOP[0] - Set fabric ->"
+ " tcm_loop_fabric_configfs\n");
+ return 0;
+}
+
+static void tcm_loop_deregister_configfs(void)
+{
+ if (!tcm_loop_fabric_configfs)
+ return;
+
+ target_fabric_configfs_deregister(tcm_loop_fabric_configfs);
+ tcm_loop_fabric_configfs = NULL;
+ printk(KERN_INFO "TCM_LOOP[0] - Cleared"
+ " tcm_loop_fabric_configfs\n");
+}
+
+static int __init tcm_loop_fabric_init(void)
+{
+ int ret;
+
+ tcm_loop_cmd_cache = kmem_cache_create("tcm_loop_cmd_cache",
+ sizeof(struct tcm_loop_cmd),
+ __alignof__(struct tcm_loop_cmd),
+ 0, NULL);
+ if (!tcm_loop_cmd_cache) {
+ printk(KERN_ERR "kmem_cache_create() for"
+ " tcm_loop_cmd_cache failed\n");
+ return -ENOMEM;
+ }
+
+ ret = tcm_loop_alloc_core_bus();
+ if (ret)
+ return ret;
+
+ ret = tcm_loop_register_configfs();
+ if (ret) {
+ tcm_loop_release_core_bus();
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit tcm_loop_fabric_exit(void)
+{
+ tcm_loop_deregister_configfs();
+ tcm_loop_release_core_bus();
+ kmem_cache_destroy(tcm_loop_cmd_cache);
+}
+
+MODULE_DESCRIPTION("TCM loopback virtual Linux/SCSI fabric module");
+MODULE_AUTHOR("Nicholas A. Bellinger <nab@risingtidesystems.com>");
+MODULE_LICENSE("GPL");
+module_init(tcm_loop_fabric_init);
+module_exit(tcm_loop_fabric_exit);
diff --git a/drivers/target/loopback/tcm_loop.h b/drivers/target/loopback/tcm_loop.h
new file mode 100644
index 000000000000..7e9f7ab45548
--- /dev/null
+++ b/drivers/target/loopback/tcm_loop.h
@@ -0,0 +1,77 @@
+#define TCM_LOOP_VERSION "v2.1-rc1"
+#define TL_WWN_ADDR_LEN 256
+#define TL_TPGS_PER_HBA 32
+/*
+ * Defaults for struct scsi_host_template tcm_loop_driver_template
+ *
+ * We use large can_queue and cmd_per_lun here and let TCM enforce
+ * the underlying se_device_t->queue_depth.
+ */
+#define TL_SCSI_CAN_QUEUE 1024
+#define TL_SCSI_CMD_PER_LUN 1024
+#define TL_SCSI_MAX_SECTORS 1024
+#define TL_SCSI_SG_TABLESIZE 256
+/*
+ * Used in tcm_loop_driver_probe() for struct Scsi_Host->max_cmd_len
+ */
+#define TL_SCSI_MAX_CMD_LEN 32
+
+#ifdef CONFIG_LOOPBACK_TARGET_CDB_DEBUG
+# define TL_CDB_DEBUG(x...) printk(KERN_INFO x)
+#else
+# define TL_CDB_DEBUG(x...)
+#endif
+
+struct tcm_loop_cmd {
+ /* State of Linux/SCSI CDB+Data descriptor */
+ u32 sc_cmd_state;
+ /* Pointer to the CDB+Data descriptor from Linux/SCSI subsystem */
+ struct scsi_cmnd *sc;
+ struct list_head *tl_cmd_list;
+ /* The TCM I/O descriptor that is accessed via container_of() */
+ struct se_cmd tl_se_cmd;
+ /* Sense buffer that will be mapped into outgoing status */
+ unsigned char tl_sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+struct tcm_loop_tmr {
+ atomic_t tmr_complete;
+ wait_queue_head_t tl_tmr_wait;
+};
+
+struct tcm_loop_nexus {
+ int it_nexus_active;
+ /*
+ * Pointer to Linux/SCSI HBA from linux/include/scsi_host.h
+ */
+ struct scsi_host *sh;
+ /*
+ * Pointer to TCM session for I_T Nexus
+ */
+ struct se_session *se_sess;
+};
+
+struct tcm_loop_nacl {
+ struct se_node_acl se_node_acl;
+};
+
+struct tcm_loop_tpg {
+ unsigned short tl_tpgt;
+ atomic_t tl_tpg_port_count;
+ struct se_portal_group tl_se_tpg;
+ struct tcm_loop_hba *tl_hba;
+};
+
+struct tcm_loop_hba {
+ u8 tl_proto_id;
+ unsigned char tl_wwn_address[TL_WWN_ADDR_LEN];
+ struct se_hba_s *se_hba;
+ struct se_lun *tl_hba_lun;
+ struct se_port *tl_hba_lun_sep;
+ struct se_device_s *se_dev_hba_ptr;
+ struct tcm_loop_nexus *tl_nexus;
+ struct device dev;
+ struct Scsi_Host *sh;
+ struct tcm_loop_tpg tl_hba_tpgs[TL_TPGS_PER_HBA];
+ struct se_wwn tl_hba_wwn;
+};
diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c
index e7dddafe73f6..47abb42d9c36 100644
--- a/drivers/target/target_core_alua.c
+++ b/drivers/target/target_core_alua.c
@@ -496,8 +496,8 @@ static int core_alua_state_check(
nonop_delay_msecs = tg_pt_gp->tg_pt_gp_nonop_delay_msecs;
spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
/*
- * Process ALUA_ACCESS_STATE_ACTIVE_OPTMIZED in a seperate conditional
- * statement so the complier knows explictly to check this case first.
+ * Process ALUA_ACCESS_STATE_ACTIVE_OPTMIZED in a separate conditional
+ * statement so the compiler knows explicitly to check this case first.
* For the Optimized ALUA access state case, we want to process the
* incoming fabric cmd ASAP..
*/
@@ -1157,7 +1157,7 @@ void core_alua_free_lu_gp(struct t10_alua_lu_gp *lu_gp)
spin_unlock(&lu_gp->lu_gp_lock);
/*
*
- * lu_gp_mem is assoicated with a single
+ * lu_gp_mem is associated with a single
* struct se_device->dev_alua_lu_gp_mem, and is released when
* struct se_device is released via core_alua_free_lu_gp_mem().
*
@@ -1429,7 +1429,7 @@ void core_alua_free_tg_pt_gp(
}
spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
/*
- * tg_pt_gp_mem is assoicated with a single
+ * tg_pt_gp_mem is associated with a single
* se_port->sep_alua_tg_pt_gp_mem, and is released via
* core_alua_free_tg_pt_gp_mem().
*
@@ -1963,7 +1963,7 @@ int core_setup_alua(struct se_device *dev, int force_pt)
printk(KERN_INFO "%s: Enabling ALUA Emulation for SPC-3"
" device\n", TRANSPORT(dev)->name);
/*
- * Assoicate this struct se_device with the default ALUA
+ * Associate this struct se_device with the default ALUA
* LUN Group.
*/
lu_gp_mem = core_alua_allocate_lu_gp_mem(dev);
diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c
index caf8dc18ee0a..a5f44a6e6e1d 100644
--- a/drivers/target/target_core_configfs.c
+++ b/drivers/target/target_core_configfs.c
@@ -3,8 +3,8 @@
*
* This file contains ConfigFS logic for the Generic Target Engine project.
*
- * Copyright (c) 2008-2010 Rising Tide Systems
- * Copyright (c) 2008-2010 Linux-iSCSI.org
+ * Copyright (c) 2008-2011 Rising Tide Systems
+ * Copyright (c) 2008-2011 Linux-iSCSI.org
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -50,6 +50,7 @@
#include "target_core_hba.h"
#include "target_core_pr.h"
#include "target_core_rd.h"
+#include "target_core_stat.h"
static struct list_head g_tf_list;
static struct mutex g_tf_lock;
@@ -1451,8 +1452,8 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
size_t count)
{
struct se_device *dev;
- unsigned char *i_fabric, *t_fabric, *i_port = NULL, *t_port = NULL;
- unsigned char *isid = NULL;
+ unsigned char *i_fabric = NULL, *i_port = NULL, *isid = NULL;
+ unsigned char *t_fabric = NULL, *t_port = NULL;
char *orig, *ptr, *arg_p, *opts;
substring_t args[MAX_OPT_ARGS];
unsigned long long tmp_ll;
@@ -1488,9 +1489,17 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
switch (token) {
case Opt_initiator_fabric:
i_fabric = match_strdup(&args[0]);
+ if (!i_fabric) {
+ ret = -ENOMEM;
+ goto out;
+ }
break;
case Opt_initiator_node:
i_port = match_strdup(&args[0]);
+ if (!i_port) {
+ ret = -ENOMEM;
+ goto out;
+ }
if (strlen(i_port) > PR_APTPL_MAX_IPORT_LEN) {
printk(KERN_ERR "APTPL metadata initiator_node="
" exceeds PR_APTPL_MAX_IPORT_LEN: %d\n",
@@ -1501,6 +1510,10 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
break;
case Opt_initiator_sid:
isid = match_strdup(&args[0]);
+ if (!isid) {
+ ret = -ENOMEM;
+ goto out;
+ }
if (strlen(isid) > PR_REG_ISID_LEN) {
printk(KERN_ERR "APTPL metadata initiator_isid"
"= exceeds PR_REG_ISID_LEN: %d\n",
@@ -1511,6 +1524,10 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
break;
case Opt_sa_res_key:
arg_p = match_strdup(&args[0]);
+ if (!arg_p) {
+ ret = -ENOMEM;
+ goto out;
+ }
ret = strict_strtoull(arg_p, 0, &tmp_ll);
if (ret < 0) {
printk(KERN_ERR "strict_strtoull() failed for"
@@ -1547,9 +1564,17 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
*/
case Opt_target_fabric:
t_fabric = match_strdup(&args[0]);
+ if (!t_fabric) {
+ ret = -ENOMEM;
+ goto out;
+ }
break;
case Opt_target_node:
t_port = match_strdup(&args[0]);
+ if (!t_port) {
+ ret = -ENOMEM;
+ goto out;
+ }
if (strlen(t_port) > PR_APTPL_MAX_TPORT_LEN) {
printk(KERN_ERR "APTPL metadata target_node="
" exceeds PR_APTPL_MAX_TPORT_LEN: %d\n",
@@ -1592,6 +1617,11 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
i_port, isid, mapped_lun, t_port, tpgt, target_lun,
res_holder, all_tg_pt, type);
out:
+ kfree(i_fabric);
+ kfree(i_port);
+ kfree(isid);
+ kfree(t_fabric);
+ kfree(t_port);
kfree(orig);
return (ret == 0) ? count : ret;
}
@@ -1798,7 +1828,9 @@ static ssize_t target_core_store_dev_enable(
return -EINVAL;
dev = t->create_virtdevice(hba, se_dev, se_dev->se_dev_su_ptr);
- if (!(dev) || IS_ERR(dev))
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+ else if (!dev)
return -EINVAL;
se_dev->se_dev_ptr = dev;
@@ -2678,6 +2710,34 @@ static struct config_item_type target_core_alua_cit = {
/* End functions for struct config_item_type target_core_alua_cit */
+/* Start functions for struct config_item_type target_core_stat_cit */
+
+static struct config_group *target_core_stat_mkdir(
+ struct config_group *group,
+ const char *name)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static void target_core_stat_rmdir(
+ struct config_group *group,
+ struct config_item *item)
+{
+ return;
+}
+
+static struct configfs_group_operations target_core_stat_group_ops = {
+ .make_group = &target_core_stat_mkdir,
+ .drop_item = &target_core_stat_rmdir,
+};
+
+static struct config_item_type target_core_stat_cit = {
+ .ct_group_ops = &target_core_stat_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* End functions for struct config_item_type target_core_stat_cit */
+
/* Start functions for struct config_item_type target_core_hba_cit */
static struct config_group *target_core_make_subdev(
@@ -2690,10 +2750,12 @@ static struct config_group *target_core_make_subdev(
struct config_item *hba_ci = &group->cg_item;
struct se_hba *hba = item_to_hba(hba_ci);
struct config_group *dev_cg = NULL, *tg_pt_gp_cg = NULL;
+ struct config_group *dev_stat_grp = NULL;
+ int errno = -ENOMEM, ret;
- if (mutex_lock_interruptible(&hba->hba_access_mutex))
- return NULL;
-
+ ret = mutex_lock_interruptible(&hba->hba_access_mutex);
+ if (ret)
+ return ERR_PTR(ret);
/*
* Locate the struct se_subsystem_api from parent's struct se_hba.
*/
@@ -2723,7 +2785,7 @@ static struct config_group *target_core_make_subdev(
se_dev->se_dev_hba = hba;
dev_cg = &se_dev->se_dev_group;
- dev_cg->default_groups = kzalloc(sizeof(struct config_group) * 6,
+ dev_cg->default_groups = kzalloc(sizeof(struct config_group) * 7,
GFP_KERNEL);
if (!(dev_cg->default_groups))
goto out;
@@ -2755,13 +2817,17 @@ static struct config_group *target_core_make_subdev(
&target_core_dev_wwn_cit);
config_group_init_type_name(&se_dev->t10_alua.alua_tg_pt_gps_group,
"alua", &target_core_alua_tg_pt_gps_cit);
+ config_group_init_type_name(&se_dev->dev_stat_grps.stat_group,
+ "statistics", &target_core_stat_cit);
+
dev_cg->default_groups[0] = &se_dev->se_dev_attrib.da_group;
dev_cg->default_groups[1] = &se_dev->se_dev_pr_group;
dev_cg->default_groups[2] = &se_dev->t10_wwn.t10_wwn_group;
dev_cg->default_groups[3] = &se_dev->t10_alua.alua_tg_pt_gps_group;
- dev_cg->default_groups[4] = NULL;
+ dev_cg->default_groups[4] = &se_dev->dev_stat_grps.stat_group;
+ dev_cg->default_groups[5] = NULL;
/*
- * Add core/$HBA/$DEV/alua/tg_pt_gps/default_tg_pt_gp
+ * Add core/$HBA/$DEV/alua/default_tg_pt_gp
*/
tg_pt_gp = core_alua_allocate_tg_pt_gp(se_dev, "default_tg_pt_gp", 1);
if (!(tg_pt_gp))
@@ -2781,6 +2847,17 @@ static struct config_group *target_core_make_subdev(
tg_pt_gp_cg->default_groups[0] = &tg_pt_gp->tg_pt_gp_group;
tg_pt_gp_cg->default_groups[1] = NULL;
T10_ALUA(se_dev)->default_tg_pt_gp = tg_pt_gp;
+ /*
+ * Add core/$HBA/$DEV/statistics/ default groups
+ */
+ dev_stat_grp = &DEV_STAT_GRP(se_dev)->stat_group;
+ dev_stat_grp->default_groups = kzalloc(sizeof(struct config_group) * 4,
+ GFP_KERNEL);
+ if (!dev_stat_grp->default_groups) {
+ printk(KERN_ERR "Unable to allocate dev_stat_grp->default_groups\n");
+ goto out;
+ }
+ target_stat_setup_dev_default_groups(se_dev);
printk(KERN_INFO "Target_Core_ConfigFS: Allocated struct se_subsystem_dev:"
" %p se_dev_su_ptr: %p\n", se_dev, se_dev->se_dev_su_ptr);
@@ -2792,6 +2869,8 @@ out:
core_alua_free_tg_pt_gp(T10_ALUA(se_dev)->default_tg_pt_gp);
T10_ALUA(se_dev)->default_tg_pt_gp = NULL;
}
+ if (dev_stat_grp)
+ kfree(dev_stat_grp->default_groups);
if (tg_pt_gp_cg)
kfree(tg_pt_gp_cg->default_groups);
if (dev_cg)
@@ -2801,7 +2880,7 @@ out:
kfree(se_dev);
unlock:
mutex_unlock(&hba->hba_access_mutex);
- return NULL;
+ return ERR_PTR(errno);
}
static void target_core_drop_subdev(
@@ -2813,7 +2892,7 @@ static void target_core_drop_subdev(
struct se_hba *hba;
struct se_subsystem_api *t;
struct config_item *df_item;
- struct config_group *dev_cg, *tg_pt_gp_cg;
+ struct config_group *dev_cg, *tg_pt_gp_cg, *dev_stat_grp;
int i;
hba = item_to_hba(&se_dev->se_dev_hba->hba_group.cg_item);
@@ -2825,6 +2904,14 @@ static void target_core_drop_subdev(
list_del(&se_dev->g_se_dev_list);
spin_unlock(&se_global->g_device_lock);
+ dev_stat_grp = &DEV_STAT_GRP(se_dev)->stat_group;
+ for (i = 0; dev_stat_grp->default_groups[i]; i++) {
+ df_item = &dev_stat_grp->default_groups[i]->cg_item;
+ dev_stat_grp->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+ kfree(dev_stat_grp->default_groups);
+
tg_pt_gp_cg = &T10_ALUA(se_dev)->alua_tg_pt_gps_group;
for (i = 0; tg_pt_gp_cg->default_groups[i]; i++) {
df_item = &tg_pt_gp_cg->default_groups[i]->cg_item;
@@ -3044,7 +3131,7 @@ static struct config_item_type target_core_cit = {
/* Stop functions for struct config_item_type target_core_hba_cit */
-static int target_core_init_configfs(void)
+static int __init target_core_init_configfs(void)
{
struct config_group *target_cg, *hba_cg = NULL, *alua_cg = NULL;
struct config_group *lu_gp_cg = NULL;
@@ -3176,7 +3263,7 @@ out_global:
return -1;
}
-static void target_core_exit_configfs(void)
+static void __exit target_core_exit_configfs(void)
{
struct configfs_subsystem *subsys;
struct config_group *hba_cg, *alua_cg, *lu_gp_cg;
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index 350ed401544e..d25e20829012 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -371,7 +371,7 @@ int core_update_device_list_for_node(
if (!(enable)) {
/*
* deve->se_lun_acl will be NULL for demo-mode created LUNs
- * that have not been explictly concerted to MappedLUNs ->
+ * that have not been explicitly concerted to MappedLUNs ->
* struct se_lun_acl, but we remove deve->alua_port_list from
* port->sep_alua_list. This also means that active UAs and
* NodeACL context specific PR metadata for demo-mode
@@ -589,6 +589,7 @@ static void core_export_port(
* Called with struct se_device->se_port_lock spinlock held.
*/
static void core_release_port(struct se_device *dev, struct se_port *port)
+ __releases(&dev->se_port_lock) __acquires(&dev->se_port_lock)
{
/*
* Wait for any port reference for PR ALL_TG_PT=1 operation
@@ -779,49 +780,14 @@ void se_release_vpd_for_dev(struct se_device *dev)
return;
}
-/*
- * Called with struct se_hba->device_lock held.
- */
-void se_clear_dev_ports(struct se_device *dev)
-{
- struct se_hba *hba = dev->se_hba;
- struct se_lun *lun;
- struct se_portal_group *tpg;
- struct se_port *sep, *sep_tmp;
-
- spin_lock(&dev->se_port_lock);
- list_for_each_entry_safe(sep, sep_tmp, &dev->dev_sep_list, sep_list) {
- spin_unlock(&dev->se_port_lock);
- spin_unlock(&hba->device_lock);
-
- lun = sep->sep_lun;
- tpg = sep->sep_tpg;
- spin_lock(&lun->lun_sep_lock);
- if (lun->lun_se_dev == NULL) {
- spin_unlock(&lun->lun_sep_lock);
- continue;
- }
- spin_unlock(&lun->lun_sep_lock);
-
- core_dev_del_lun(tpg, lun->unpacked_lun);
-
- spin_lock(&hba->device_lock);
- spin_lock(&dev->se_port_lock);
- }
- spin_unlock(&dev->se_port_lock);
-
- return;
-}
-
/* se_free_virtual_device():
*
* Used for IBLOCK, RAMDISK, and FILEIO Transport Drivers.
*/
int se_free_virtual_device(struct se_device *dev, struct se_hba *hba)
{
- spin_lock(&hba->device_lock);
- se_clear_dev_ports(dev);
- spin_unlock(&hba->device_lock);
+ if (!list_empty(&dev->dev_sep_list))
+ dump_stack();
core_alua_free_lu_gp_mem(dev);
se_release_device_for_hba(dev);
diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c
index b65d1c8e7740..07ab5a3bb8e8 100644
--- a/drivers/target/target_core_fabric_configfs.c
+++ b/drivers/target/target_core_fabric_configfs.c
@@ -4,10 +4,10 @@
* This file contains generic fabric module configfs infrastructure for
* TCM v4.x code
*
- * Copyright (c) 2010 Rising Tide Systems
- * Copyright (c) 2010 Linux-iSCSI.org
+ * Copyright (c) 2010,2011 Rising Tide Systems
+ * Copyright (c) 2010,2011 Linux-iSCSI.org
*
- * Copyright (c) 2010 Nicholas A. Bellinger <nab@linux-iscsi.org>
+ * Copyright (c) Nicholas A. Bellinger <nab@linux-iscsi.org>
*
* 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
@@ -48,6 +48,7 @@
#include "target_core_alua.h"
#include "target_core_hba.h"
#include "target_core_pr.h"
+#include "target_core_stat.h"
#define TF_CIT_SETUP(_name, _item_ops, _group_ops, _attrs) \
static void target_fabric_setup_##_name##_cit(struct target_fabric_configfs *tf) \
@@ -241,6 +242,32 @@ TF_CIT_SETUP(tpg_mappedlun, &target_fabric_mappedlun_item_ops, NULL,
/* End of tfc_tpg_mappedlun_cit */
+/* Start of tfc_tpg_mappedlun_port_cit */
+
+static struct config_group *target_core_mappedlun_stat_mkdir(
+ struct config_group *group,
+ const char *name)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static void target_core_mappedlun_stat_rmdir(
+ struct config_group *group,
+ struct config_item *item)
+{
+ return;
+}
+
+static struct configfs_group_operations target_fabric_mappedlun_stat_group_ops = {
+ .make_group = target_core_mappedlun_stat_mkdir,
+ .drop_item = target_core_mappedlun_stat_rmdir,
+};
+
+TF_CIT_SETUP(tpg_mappedlun_stat, NULL, &target_fabric_mappedlun_stat_group_ops,
+ NULL);
+
+/* End of tfc_tpg_mappedlun_port_cit */
+
/* Start of tfc_tpg_nacl_attrib_cit */
CONFIGFS_EATTR_OPS(target_fabric_nacl_attrib, se_node_acl, acl_attrib_group);
@@ -294,6 +321,7 @@ static struct config_group *target_fabric_make_mappedlun(
struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf;
struct se_lun_acl *lacl;
struct config_item *acl_ci;
+ struct config_group *lacl_cg = NULL, *ml_stat_grp = NULL;
char *buf;
unsigned long mapped_lun;
int ret = 0;
@@ -330,15 +358,42 @@ static struct config_group *target_fabric_make_mappedlun(
lacl = core_dev_init_initiator_node_lun_acl(se_tpg, mapped_lun,
config_item_name(acl_ci), &ret);
- if (!(lacl))
+ if (!(lacl)) {
+ ret = -EINVAL;
goto out;
+ }
+
+ lacl_cg = &lacl->se_lun_group;
+ lacl_cg->default_groups = kzalloc(sizeof(struct config_group) * 2,
+ GFP_KERNEL);
+ if (!lacl_cg->default_groups) {
+ printk(KERN_ERR "Unable to allocate lacl_cg->default_groups\n");
+ ret = -ENOMEM;
+ goto out;
+ }
config_group_init_type_name(&lacl->se_lun_group, name,
&TF_CIT_TMPL(tf)->tfc_tpg_mappedlun_cit);
+ config_group_init_type_name(&lacl->ml_stat_grps.stat_group,
+ "statistics", &TF_CIT_TMPL(tf)->tfc_tpg_mappedlun_stat_cit);
+ lacl_cg->default_groups[0] = &lacl->ml_stat_grps.stat_group;
+ lacl_cg->default_groups[1] = NULL;
+
+ ml_stat_grp = &ML_STAT_GRPS(lacl)->stat_group;
+ ml_stat_grp->default_groups = kzalloc(sizeof(struct config_group) * 3,
+ GFP_KERNEL);
+ if (!ml_stat_grp->default_groups) {
+ printk(KERN_ERR "Unable to allocate ml_stat_grp->default_groups\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ target_stat_setup_mappedlun_default_groups(lacl);
kfree(buf);
return &lacl->se_lun_group;
out:
+ if (lacl_cg)
+ kfree(lacl_cg->default_groups);
kfree(buf);
return ERR_PTR(ret);
}
@@ -347,6 +402,28 @@ static void target_fabric_drop_mappedlun(
struct config_group *group,
struct config_item *item)
{
+ struct se_lun_acl *lacl = container_of(to_config_group(item),
+ struct se_lun_acl, se_lun_group);
+ struct config_item *df_item;
+ struct config_group *lacl_cg = NULL, *ml_stat_grp = NULL;
+ int i;
+
+ ml_stat_grp = &ML_STAT_GRPS(lacl)->stat_group;
+ for (i = 0; ml_stat_grp->default_groups[i]; i++) {
+ df_item = &ml_stat_grp->default_groups[i]->cg_item;
+ ml_stat_grp->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+ kfree(ml_stat_grp->default_groups);
+
+ lacl_cg = &lacl->se_lun_group;
+ for (i = 0; lacl_cg->default_groups[i]; i++) {
+ df_item = &lacl_cg->default_groups[i]->cg_item;
+ lacl_cg->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+ kfree(lacl_cg->default_groups);
+
config_item_put(item);
}
@@ -376,6 +453,15 @@ TF_CIT_SETUP(tpg_nacl_base, &target_fabric_nacl_base_item_ops,
/* End of tfc_tpg_nacl_base_cit */
+/* Start of tfc_node_fabric_stats_cit */
+/*
+ * This is used as a placeholder for struct se_node_acl->acl_fabric_stat_group
+ * to allow fabrics access to ->acl_fabric_stat_group->default_groups[]
+ */
+TF_CIT_SETUP(tpg_nacl_stat, NULL, NULL, NULL);
+
+/* End of tfc_wwn_fabric_stats_cit */
+
/* Start of tfc_tpg_nacl_cit */
static struct config_group *target_fabric_make_nodeacl(
@@ -402,7 +488,8 @@ static struct config_group *target_fabric_make_nodeacl(
nacl_cg->default_groups[0] = &se_nacl->acl_attrib_group;
nacl_cg->default_groups[1] = &se_nacl->acl_auth_group;
nacl_cg->default_groups[2] = &se_nacl->acl_param_group;
- nacl_cg->default_groups[3] = NULL;
+ nacl_cg->default_groups[3] = &se_nacl->acl_fabric_stat_group;
+ nacl_cg->default_groups[4] = NULL;
config_group_init_type_name(&se_nacl->acl_group, name,
&TF_CIT_TMPL(tf)->tfc_tpg_nacl_base_cit);
@@ -412,6 +499,9 @@ static struct config_group *target_fabric_make_nodeacl(
&TF_CIT_TMPL(tf)->tfc_tpg_nacl_auth_cit);
config_group_init_type_name(&se_nacl->acl_param_group, "param",
&TF_CIT_TMPL(tf)->tfc_tpg_nacl_param_cit);
+ config_group_init_type_name(&se_nacl->acl_fabric_stat_group,
+ "fabric_statistics",
+ &TF_CIT_TMPL(tf)->tfc_tpg_nacl_stat_cit);
return &se_nacl->acl_group;
}
@@ -758,6 +848,31 @@ TF_CIT_SETUP(tpg_port, &target_fabric_port_item_ops, NULL, target_fabric_port_at
/* End of tfc_tpg_port_cit */
+/* Start of tfc_tpg_port_stat_cit */
+
+static struct config_group *target_core_port_stat_mkdir(
+ struct config_group *group,
+ const char *name)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static void target_core_port_stat_rmdir(
+ struct config_group *group,
+ struct config_item *item)
+{
+ return;
+}
+
+static struct configfs_group_operations target_fabric_port_stat_group_ops = {
+ .make_group = target_core_port_stat_mkdir,
+ .drop_item = target_core_port_stat_rmdir,
+};
+
+TF_CIT_SETUP(tpg_port_stat, NULL, &target_fabric_port_stat_group_ops, NULL);
+
+/* End of tfc_tpg_port_stat_cit */
+
/* Start of tfc_tpg_lun_cit */
static struct config_group *target_fabric_make_lun(
@@ -768,7 +883,9 @@ static struct config_group *target_fabric_make_lun(
struct se_portal_group *se_tpg = container_of(group,
struct se_portal_group, tpg_lun_group);
struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf;
+ struct config_group *lun_cg = NULL, *port_stat_grp = NULL;
unsigned long unpacked_lun;
+ int errno;
if (strstr(name, "lun_") != name) {
printk(KERN_ERR "Unable to locate \'_\" in"
@@ -782,16 +899,64 @@ static struct config_group *target_fabric_make_lun(
if (!(lun))
return ERR_PTR(-EINVAL);
+ lun_cg = &lun->lun_group;
+ lun_cg->default_groups = kzalloc(sizeof(struct config_group) * 2,
+ GFP_KERNEL);
+ if (!lun_cg->default_groups) {
+ printk(KERN_ERR "Unable to allocate lun_cg->default_groups\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
config_group_init_type_name(&lun->lun_group, name,
&TF_CIT_TMPL(tf)->tfc_tpg_port_cit);
+ config_group_init_type_name(&lun->port_stat_grps.stat_group,
+ "statistics", &TF_CIT_TMPL(tf)->tfc_tpg_port_stat_cit);
+ lun_cg->default_groups[0] = &lun->port_stat_grps.stat_group;
+ lun_cg->default_groups[1] = NULL;
+
+ port_stat_grp = &PORT_STAT_GRP(lun)->stat_group;
+ port_stat_grp->default_groups = kzalloc(sizeof(struct config_group) * 3,
+ GFP_KERNEL);
+ if (!port_stat_grp->default_groups) {
+ printk(KERN_ERR "Unable to allocate port_stat_grp->default_groups\n");
+ errno = -ENOMEM;
+ goto out;
+ }
+ target_stat_setup_port_default_groups(lun);
return &lun->lun_group;
+out:
+ if (lun_cg)
+ kfree(lun_cg->default_groups);
+ return ERR_PTR(errno);
}
static void target_fabric_drop_lun(
struct config_group *group,
struct config_item *item)
{
+ struct se_lun *lun = container_of(to_config_group(item),
+ struct se_lun, lun_group);
+ struct config_item *df_item;
+ struct config_group *lun_cg, *port_stat_grp;
+ int i;
+
+ port_stat_grp = &PORT_STAT_GRP(lun)->stat_group;
+ for (i = 0; port_stat_grp->default_groups[i]; i++) {
+ df_item = &port_stat_grp->default_groups[i]->cg_item;
+ port_stat_grp->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+ kfree(port_stat_grp->default_groups);
+
+ lun_cg = &lun->lun_group;
+ for (i = 0; lun_cg->default_groups[i]; i++) {
+ df_item = &lun_cg->default_groups[i]->cg_item;
+ lun_cg->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+ kfree(lun_cg->default_groups);
+
config_item_put(item);
}
@@ -946,6 +1111,15 @@ TF_CIT_SETUP(tpg, &target_fabric_tpg_item_ops, &target_fabric_tpg_group_ops,
/* End of tfc_tpg_cit */
+/* Start of tfc_wwn_fabric_stats_cit */
+/*
+ * This is used as a placeholder for struct se_wwn->fabric_stat_group
+ * to allow fabrics access to ->fabric_stat_group->default_groups[]
+ */
+TF_CIT_SETUP(wwn_fabric_stats, NULL, NULL, NULL);
+
+/* End of tfc_wwn_fabric_stats_cit */
+
/* Start of tfc_wwn_cit */
static struct config_group *target_fabric_make_wwn(
@@ -966,8 +1140,17 @@ static struct config_group *target_fabric_make_wwn(
return ERR_PTR(-EINVAL);
wwn->wwn_tf = tf;
+ /*
+ * Setup default groups from pre-allocated wwn->wwn_default_groups
+ */
+ wwn->wwn_group.default_groups = wwn->wwn_default_groups;
+ wwn->wwn_group.default_groups[0] = &wwn->fabric_stat_group;
+ wwn->wwn_group.default_groups[1] = NULL;
+
config_group_init_type_name(&wwn->wwn_group, name,
&TF_CIT_TMPL(tf)->tfc_tpg_cit);
+ config_group_init_type_name(&wwn->fabric_stat_group, "fabric_statistics",
+ &TF_CIT_TMPL(tf)->tfc_wwn_fabric_stats_cit);
return &wwn->wwn_group;
}
@@ -976,6 +1159,18 @@ static void target_fabric_drop_wwn(
struct config_group *group,
struct config_item *item)
{
+ struct se_wwn *wwn = container_of(to_config_group(item),
+ struct se_wwn, wwn_group);
+ struct config_item *df_item;
+ struct config_group *cg = &wwn->wwn_group;
+ int i;
+
+ for (i = 0; cg->default_groups[i]; i++) {
+ df_item = &cg->default_groups[i]->cg_item;
+ cg->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+
config_item_put(item);
}
@@ -1015,9 +1210,11 @@ int target_fabric_setup_cits(struct target_fabric_configfs *tf)
{
target_fabric_setup_discovery_cit(tf);
target_fabric_setup_wwn_cit(tf);
+ target_fabric_setup_wwn_fabric_stats_cit(tf);
target_fabric_setup_tpg_cit(tf);
target_fabric_setup_tpg_base_cit(tf);
target_fabric_setup_tpg_port_cit(tf);
+ target_fabric_setup_tpg_port_stat_cit(tf);
target_fabric_setup_tpg_lun_cit(tf);
target_fabric_setup_tpg_np_cit(tf);
target_fabric_setup_tpg_np_base_cit(tf);
@@ -1028,7 +1225,9 @@ int target_fabric_setup_cits(struct target_fabric_configfs *tf)
target_fabric_setup_tpg_nacl_attrib_cit(tf);
target_fabric_setup_tpg_nacl_auth_cit(tf);
target_fabric_setup_tpg_nacl_param_cit(tf);
+ target_fabric_setup_tpg_nacl_stat_cit(tf);
target_fabric_setup_tpg_mappedlun_cit(tf);
+ target_fabric_setup_tpg_mappedlun_stat_cit(tf);
return 0;
}
diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c
index a3c695adabec..1e193f324895 100644
--- a/drivers/target/target_core_fabric_lib.c
+++ b/drivers/target/target_core_fabric_lib.c
@@ -34,6 +34,7 @@
#include <target/target_core_base.h>
#include <target/target_core_device.h>
#include <target/target_core_transport.h>
+#include <target/target_core_fabric_lib.h>
#include <target/target_core_fabric_ops.h>
#include <target/target_core_configfs.h>
@@ -432,7 +433,7 @@ char *iscsi_parse_pr_out_transport_id(
/*
* Go ahead and do the lower case conversion of the received
* 12 ASCII characters representing the ISID in the TransportID
- * for comparision against the running iSCSI session's ISID from
+ * for comparison against the running iSCSI session's ISID from
* iscsi_target.c:lio_sess_get_initiator_sid()
*/
for (i = 0; i < 12; i++) {
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index 190ca8ac2498..150c4305f385 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -134,7 +134,7 @@ static struct se_device *fd_create_virtdevice(
mm_segment_t old_fs;
struct file *file;
struct inode *inode = NULL;
- int dev_flags = 0, flags;
+ int dev_flags = 0, flags, ret = -EINVAL;
memset(&dev_limits, 0, sizeof(struct se_dev_limits));
@@ -146,6 +146,7 @@ static struct se_device *fd_create_virtdevice(
if (IS_ERR(dev_p)) {
printk(KERN_ERR "getname(%s) failed: %lu\n",
fd_dev->fd_dev_name, IS_ERR(dev_p));
+ ret = PTR_ERR(dev_p);
goto fail;
}
#if 0
@@ -158,15 +159,19 @@ static struct se_device *fd_create_virtdevice(
#endif
/* flags |= O_DIRECT; */
/*
- * If fd_buffered_io=1 has not been set explictly (the default),
+ * If fd_buffered_io=1 has not been set explicitly (the default),
* use O_SYNC to force FILEIO writes to disk.
*/
if (!(fd_dev->fbd_flags & FDBD_USE_BUFFERED_IO))
flags |= O_SYNC;
file = filp_open(dev_p, flags, 0600);
-
- if (IS_ERR(file) || !file || !file->f_dentry) {
+ if (IS_ERR(file)) {
+ printk(KERN_ERR "filp_open(%s) failed\n", dev_p);
+ ret = PTR_ERR(file);
+ goto fail;
+ }
+ if (!file || !file->f_dentry) {
printk(KERN_ERR "filp_open(%s) failed\n", dev_p);
goto fail;
}
@@ -241,7 +246,7 @@ fail:
fd_dev->fd_file = NULL;
}
putname(dev_p);
- return NULL;
+ return ERR_PTR(ret);
}
/* fd_free_device(): (Part of se_subsystem_api_t template)
@@ -509,7 +514,7 @@ enum {
static match_table_t tokens = {
{Opt_fd_dev_name, "fd_dev_name=%s"},
{Opt_fd_dev_size, "fd_dev_size=%s"},
- {Opt_fd_buffered_io, "fd_buffered_id=%d"},
+ {Opt_fd_buffered_io, "fd_buffered_io=%d"},
{Opt_err, NULL}
};
@@ -536,15 +541,26 @@ static ssize_t fd_set_configfs_dev_params(
token = match_token(ptr, tokens, args);
switch (token) {
case Opt_fd_dev_name:
+ arg_p = match_strdup(&args[0]);
+ if (!arg_p) {
+ ret = -ENOMEM;
+ break;
+ }
snprintf(fd_dev->fd_dev_name, FD_MAX_DEV_NAME,
- "%s", match_strdup(&args[0]));
+ "%s", arg_p);
+ kfree(arg_p);
printk(KERN_INFO "FILEIO: Referencing Path: %s\n",
fd_dev->fd_dev_name);
fd_dev->fbd_flags |= FBDF_HAS_PATH;
break;
case Opt_fd_dev_size:
arg_p = match_strdup(&args[0]);
+ if (!arg_p) {
+ ret = -ENOMEM;
+ break;
+ }
ret = strict_strtoull(arg_p, 0, &fd_dev->fd_dev_size);
+ kfree(arg_p);
if (ret < 0) {
printk(KERN_ERR "strict_strtoull() failed for"
" fd_dev_size=\n");
diff --git a/drivers/target/target_core_hba.c b/drivers/target/target_core_hba.c
index 6ec51cbc018e..0b8f8da89019 100644
--- a/drivers/target/target_core_hba.c
+++ b/drivers/target/target_core_hba.c
@@ -151,19 +151,8 @@ out_free_hba:
int
core_delete_hba(struct se_hba *hba)
{
- struct se_device *dev, *dev_tmp;
-
- spin_lock(&hba->device_lock);
- list_for_each_entry_safe(dev, dev_tmp, &hba->hba_dev_list, dev_list) {
-
- se_clear_dev_ports(dev);
- spin_unlock(&hba->device_lock);
-
- se_release_device_for_hba(dev);
-
- spin_lock(&hba->device_lock);
- }
- spin_unlock(&hba->device_lock);
+ if (!list_empty(&hba->hba_dev_list))
+ dump_stack();
hba->transport->detach_hba(hba);
diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
index 3df570db0e4f..86639004af9e 100644
--- a/drivers/target/target_core_iblock.c
+++ b/drivers/target/target_core_iblock.c
@@ -129,10 +129,11 @@ static struct se_device *iblock_create_virtdevice(
struct request_queue *q;
struct queue_limits *limits;
u32 dev_flags = 0;
+ int ret = -EINVAL;
if (!(ib_dev)) {
printk(KERN_ERR "Unable to locate struct iblock_dev parameter\n");
- return 0;
+ return ERR_PTR(ret);
}
memset(&dev_limits, 0, sizeof(struct se_dev_limits));
/*
@@ -141,7 +142,7 @@ static struct se_device *iblock_create_virtdevice(
ib_dev->ibd_bio_set = bioset_create(32, 64);
if (!(ib_dev->ibd_bio_set)) {
printk(KERN_ERR "IBLOCK: Unable to create bioset()\n");
- return 0;
+ return ERR_PTR(-ENOMEM);
}
printk(KERN_INFO "IBLOCK: Created bio_set()\n");
/*
@@ -153,8 +154,10 @@ static struct se_device *iblock_create_virtdevice(
bd = blkdev_get_by_path(ib_dev->ibd_udev_path,
FMODE_WRITE|FMODE_READ|FMODE_EXCL, ib_dev);
- if (IS_ERR(bd))
+ if (IS_ERR(bd)) {
+ ret = PTR_ERR(bd);
goto failed;
+ }
/*
* Setup the local scope queue_limits from struct request_queue->limits
* to pass into transport_add_device_to_core_hba() as struct se_dev_limits.
@@ -184,9 +187,7 @@ static struct se_device *iblock_create_virtdevice(
* the QUEUE_FLAG_DISCARD bit for UNMAP/WRITE_SAME in SCSI + TRIM
* in ATA and we need to set TPE=1
*/
- if (blk_queue_discard(bdev_get_queue(bd))) {
- struct request_queue *q = bdev_get_queue(bd);
-
+ if (blk_queue_discard(q)) {
DEV_ATTRIB(dev)->max_unmap_lba_count =
q->limits.max_discard_sectors;
/*
@@ -212,7 +213,7 @@ failed:
ib_dev->ibd_bd = NULL;
ib_dev->ibd_major = 0;
ib_dev->ibd_minor = 0;
- return NULL;
+ return ERR_PTR(ret);
}
static void iblock_free_device(void *p)
@@ -391,9 +392,8 @@ static int iblock_do_task(struct se_task *task)
{
struct se_device *dev = task->task_se_cmd->se_dev;
struct iblock_req *req = IBLOCK_REQ(task);
- struct iblock_dev *ibd = (struct iblock_dev *)req->ib_dev;
- struct request_queue *q = bdev_get_queue(ibd->ibd_bd);
struct bio *bio = req->ib_bio, *nbio = NULL;
+ struct blk_plug plug;
int rw;
if (task->task_data_direction == DMA_TO_DEVICE) {
@@ -411,6 +411,7 @@ static int iblock_do_task(struct se_task *task)
rw = READ;
}
+ blk_start_plug(&plug);
while (bio) {
nbio = bio->bi_next;
bio->bi_next = NULL;
@@ -420,9 +421,8 @@ static int iblock_do_task(struct se_task *task)
submit_bio(rw, bio);
bio = nbio;
}
+ blk_finish_plug(&plug);
- if (q->unplug_fn)
- q->unplug_fn(q);
return PYX_TRANSPORT_SENT_TO_TRANSPORT;
}
@@ -468,7 +468,7 @@ static ssize_t iblock_set_configfs_dev_params(struct se_hba *hba,
const char *page, ssize_t count)
{
struct iblock_dev *ib_dev = se_dev->se_dev_su_ptr;
- char *orig, *ptr, *opts;
+ char *orig, *ptr, *arg_p, *opts;
substring_t args[MAX_OPT_ARGS];
int ret = 0, arg, token;
@@ -491,9 +491,14 @@ static ssize_t iblock_set_configfs_dev_params(struct se_hba *hba,
ret = -EEXIST;
goto out;
}
-
- ret = snprintf(ib_dev->ibd_udev_path, SE_UDEV_PATH_LEN,
- "%s", match_strdup(&args[0]));
+ arg_p = match_strdup(&args[0]);
+ if (!arg_p) {
+ ret = -ENOMEM;
+ break;
+ }
+ snprintf(ib_dev->ibd_udev_path, SE_UDEV_PATH_LEN,
+ "%s", arg_p);
+ kfree(arg_p);
printk(KERN_INFO "IBLOCK: Referencing UDEV path: %s\n",
ib_dev->ibd_udev_path);
ib_dev->ibd_flags |= IBDF_HAS_UDEV_PATH;
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 2521f75362c3..a79f518ca6e2 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -478,7 +478,7 @@ static int core_scsi3_pr_seq_non_holder(
break;
}
/*
- * Case where the CDB is explictly allowed in the above switch
+ * Case where the CDB is explicitly allowed in the above switch
* statement.
*/
if (!(ret) && !(other_cdb)) {
@@ -3735,7 +3735,7 @@ static int core_scsi3_emulate_pr_out(struct se_cmd *cmd, unsigned char *cdb)
return PYX_TRANSPORT_LU_COMM_FAILURE;
if (cmd->data_length < 24) {
- printk(KERN_WARNING "SPC-PR: Recieved PR OUT parameter list"
+ printk(KERN_WARNING "SPC-PR: Received PR OUT parameter list"
" length too small: %u\n", cmd->data_length);
return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
}
@@ -3778,7 +3778,7 @@ static int core_scsi3_emulate_pr_out(struct se_cmd *cmd, unsigned char *cdb)
*/
if (!(spec_i_pt) && ((cdb[1] & 0x1f) != PRO_REGISTER_AND_MOVE) &&
(cmd->data_length != 24)) {
- printk(KERN_WARNING "SPC-PR: Recieved PR OUT illegal parameter"
+ printk(KERN_WARNING "SPC-PR: Received PR OUT illegal parameter"
" list length: %u\n", cmd->data_length);
return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
}
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
index 5a9d2ba4b609..7ff6a35f26ac 100644
--- a/drivers/target/target_core_pscsi.c
+++ b/drivers/target/target_core_pscsi.c
@@ -441,6 +441,7 @@ static struct se_device *pscsi_create_type_disk(
struct pscsi_dev_virt *pdv,
struct se_subsystem_dev *se_dev,
struct se_hba *hba)
+ __releases(sh->host_lock)
{
struct se_device *dev;
struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)pdv->pdv_se_hba->hba_ptr;
@@ -488,6 +489,7 @@ static struct se_device *pscsi_create_type_rom(
struct pscsi_dev_virt *pdv,
struct se_subsystem_dev *se_dev,
struct se_hba *hba)
+ __releases(sh->host_lock)
{
struct se_device *dev;
struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)pdv->pdv_se_hba->hba_ptr;
@@ -522,6 +524,7 @@ static struct se_device *pscsi_create_type_other(
struct pscsi_dev_virt *pdv,
struct se_subsystem_dev *se_dev,
struct se_hba *hba)
+ __releases(sh->host_lock)
{
struct se_device *dev;
struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)pdv->pdv_se_hba->hba_ptr;
@@ -555,7 +558,7 @@ static struct se_device *pscsi_create_virtdevice(
if (!(pdv)) {
printk(KERN_ERR "Unable to locate struct pscsi_dev_virt"
" parameter\n");
- return NULL;
+ return ERR_PTR(-EINVAL);
}
/*
* If not running in PHV_LLD_SCSI_HOST_NO mode, locate the
@@ -565,7 +568,7 @@ static struct se_device *pscsi_create_virtdevice(
if (phv->phv_mode == PHV_LLD_SCSI_HOST_NO) {
printk(KERN_ERR "pSCSI: Unable to locate struct"
" Scsi_Host for PHV_LLD_SCSI_HOST_NO\n");
- return NULL;
+ return ERR_PTR(-ENODEV);
}
/*
* For the newer PHV_VIRUTAL_HOST_ID struct scsi_device
@@ -574,7 +577,7 @@ static struct se_device *pscsi_create_virtdevice(
if (!(se_dev->su_dev_flags & SDF_USING_UDEV_PATH)) {
printk(KERN_ERR "pSCSI: udev_path attribute has not"
" been set before ENABLE=1\n");
- return NULL;
+ return ERR_PTR(-EINVAL);
}
/*
* If no scsi_host_id= was passed for PHV_VIRUTAL_HOST_ID,
@@ -587,12 +590,12 @@ static struct se_device *pscsi_create_virtdevice(
printk(KERN_ERR "pSCSI: Unable to set hba_mode"
" with active devices\n");
spin_unlock(&hba->device_lock);
- return NULL;
+ return ERR_PTR(-EEXIST);
}
spin_unlock(&hba->device_lock);
if (pscsi_pmode_enable_hba(hba, 1) != 1)
- return NULL;
+ return ERR_PTR(-ENODEV);
legacy_mode_enable = 1;
hba->hba_flags |= HBA_FLAGS_PSCSI_MODE;
@@ -602,14 +605,14 @@ static struct se_device *pscsi_create_virtdevice(
if (!(sh)) {
printk(KERN_ERR "pSCSI: Unable to locate"
" pdv_host_id: %d\n", pdv->pdv_host_id);
- return NULL;
+ return ERR_PTR(-ENODEV);
}
}
} else {
if (phv->phv_mode == PHV_VIRUTAL_HOST_ID) {
printk(KERN_ERR "pSCSI: PHV_VIRUTAL_HOST_ID set while"
" struct Scsi_Host exists\n");
- return NULL;
+ return ERR_PTR(-EEXIST);
}
}
@@ -644,7 +647,7 @@ static struct se_device *pscsi_create_virtdevice(
hba->hba_flags &= ~HBA_FLAGS_PSCSI_MODE;
}
pdv->pdv_sd = NULL;
- return NULL;
+ return ERR_PTR(-ENODEV);
}
return dev;
}
@@ -660,7 +663,7 @@ static struct se_device *pscsi_create_virtdevice(
hba->hba_flags &= ~HBA_FLAGS_PSCSI_MODE;
}
- return NULL;
+ return ERR_PTR(-ENODEV);
}
/* pscsi_free_device(): (Part of se_subsystem_api_t template)
@@ -816,6 +819,7 @@ pscsi_alloc_task(struct se_cmd *cmd)
if (!(pt->pscsi_cdb)) {
printk(KERN_ERR "pSCSI: Unable to allocate extended"
" pt->pscsi_cdb\n");
+ kfree(pt);
return NULL;
}
} else
diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c
index 8dc6d74c1d40..7837dd365a9d 100644
--- a/drivers/target/target_core_rd.c
+++ b/drivers/target/target_core_rd.c
@@ -150,7 +150,7 @@ static int rd_build_device_space(struct rd_dev *rd_dev)
if (rd_dev->rd_page_count <= 0) {
printk(KERN_ERR "Illegal page count: %u for Ramdisk device\n",
rd_dev->rd_page_count);
- return -1;
+ return -EINVAL;
}
total_sg_needed = rd_dev->rd_page_count;
@@ -160,7 +160,7 @@ static int rd_build_device_space(struct rd_dev *rd_dev)
if (!(sg_table)) {
printk(KERN_ERR "Unable to allocate memory for Ramdisk"
" scatterlist tables\n");
- return -1;
+ return -ENOMEM;
}
rd_dev->sg_table_array = sg_table;
@@ -175,7 +175,7 @@ static int rd_build_device_space(struct rd_dev *rd_dev)
if (!(sg)) {
printk(KERN_ERR "Unable to allocate scatterlist array"
" for struct rd_dev\n");
- return -1;
+ return -ENOMEM;
}
sg_init_table((struct scatterlist *)&sg[0], sg_per_table);
@@ -191,7 +191,7 @@ static int rd_build_device_space(struct rd_dev *rd_dev)
if (!(pg)) {
printk(KERN_ERR "Unable to allocate scatterlist"
" pages for struct rd_dev_sg_table\n");
- return -1;
+ return -ENOMEM;
}
sg_assign_page(&sg[j], pg);
sg[j].length = PAGE_SIZE;
@@ -253,12 +253,13 @@ static struct se_device *rd_create_virtdevice(
struct se_dev_limits dev_limits;
struct rd_dev *rd_dev = p;
struct rd_host *rd_host = hba->hba_ptr;
- int dev_flags = 0;
+ int dev_flags = 0, ret;
char prod[16], rev[4];
memset(&dev_limits, 0, sizeof(struct se_dev_limits));
- if (rd_build_device_space(rd_dev) < 0)
+ ret = rd_build_device_space(rd_dev);
+ if (ret < 0)
goto fail;
snprintf(prod, 16, "RAMDISK-%s", (rd_dev->rd_direct) ? "DR" : "MCP");
@@ -292,7 +293,7 @@ static struct se_device *rd_create_virtdevice(
fail:
rd_release_device_space(rd_dev);
- return NULL;
+ return ERR_PTR(ret);
}
static struct se_device *rd_DIRECT_create_virtdevice(
diff --git a/drivers/target/target_core_rd.h b/drivers/target/target_core_rd.h
index 13badfbaf9c0..3ea19e29d8ec 100644
--- a/drivers/target/target_core_rd.h
+++ b/drivers/target/target_core_rd.h
@@ -14,8 +14,6 @@
#define RD_BLOCKSIZE 512
#define RD_MAX_SECTORS 1024
-extern struct kmem_cache *se_mem_cache;
-
/* Used in target_core_init_configfs() for virtual LUN 0 access */
int __init rd_module_init(void);
void rd_module_exit(void);
diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c
new file mode 100644
index 000000000000..5e3a067a7475
--- /dev/null
+++ b/drivers/target/target_core_stat.c
@@ -0,0 +1,1810 @@
+/*******************************************************************************
+ * Filename: target_core_stat.c
+ *
+ * Copyright (c) 2011 Rising Tide Systems
+ * Copyright (c) 2011 Linux-iSCSI.org
+ *
+ * Modern ConfigFS group context specific statistics based on original
+ * target_core_mib.c code
+ *
+ * Copyright (c) 2006-2007 SBE, Inc. All Rights Reserved.
+ *
+ * Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/version.h>
+#include <generated/utsrelease.h>
+#include <linux/utsname.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/blkdev.h>
+#include <linux/configfs.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "target_core_hba.h"
+
+#ifndef INITIAL_JIFFIES
+#define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ))
+#endif
+
+#define NONE "None"
+#define ISPRINT(a) ((a >= ' ') && (a <= '~'))
+
+#define SCSI_LU_INDEX 1
+#define LU_COUNT 1
+
+/*
+ * SCSI Device Table
+ */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_dev, se_dev_stat_grps);
+#define DEV_STAT_SCSI_DEV_ATTR(_name, _mode) \
+static struct target_stat_scsi_dev_attribute \
+ target_stat_scsi_dev_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_dev_show_attr_##_name, \
+ target_stat_scsi_dev_store_attr_##_name);
+
+#define DEV_STAT_SCSI_DEV_ATTR_RO(_name) \
+static struct target_stat_scsi_dev_attribute \
+ target_stat_scsi_dev_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_dev_show_attr_##_name);
+
+static ssize_t target_stat_scsi_dev_show_attr_inst(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_hba *hba = se_subdev->se_dev_hba;
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index);
+}
+DEV_STAT_SCSI_DEV_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_dev_show_attr_indx(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index);
+}
+DEV_STAT_SCSI_DEV_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_dev_show_attr_role(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ return snprintf(page, PAGE_SIZE, "Target\n");
+}
+DEV_STAT_SCSI_DEV_ATTR_RO(role);
+
+static ssize_t target_stat_scsi_dev_show_attr_ports(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", dev->dev_port_count);
+}
+DEV_STAT_SCSI_DEV_ATTR_RO(ports);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_dev, se_dev_stat_grps, scsi_dev_group);
+
+static struct configfs_attribute *target_stat_scsi_dev_attrs[] = {
+ &target_stat_scsi_dev_inst.attr,
+ &target_stat_scsi_dev_indx.attr,
+ &target_stat_scsi_dev_role.attr,
+ &target_stat_scsi_dev_ports.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_dev_attrib_ops = {
+ .show_attribute = target_stat_scsi_dev_attr_show,
+ .store_attribute = target_stat_scsi_dev_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_dev_cit = {
+ .ct_item_ops = &target_stat_scsi_dev_attrib_ops,
+ .ct_attrs = target_stat_scsi_dev_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * SCSI Target Device Table
+ */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_tgt_dev, se_dev_stat_grps);
+#define DEV_STAT_SCSI_TGT_DEV_ATTR(_name, _mode) \
+static struct target_stat_scsi_tgt_dev_attribute \
+ target_stat_scsi_tgt_dev_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_tgt_dev_show_attr_##_name, \
+ target_stat_scsi_tgt_dev_store_attr_##_name);
+
+#define DEV_STAT_SCSI_TGT_DEV_ATTR_RO(_name) \
+static struct target_stat_scsi_tgt_dev_attribute \
+ target_stat_scsi_tgt_dev_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_tgt_dev_show_attr_##_name);
+
+static ssize_t target_stat_scsi_tgt_dev_show_attr_inst(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_hba *hba = se_subdev->se_dev_hba;
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index);
+}
+DEV_STAT_SCSI_TGT_DEV_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_tgt_dev_show_attr_indx(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index);
+}
+DEV_STAT_SCSI_TGT_DEV_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_tgt_dev_show_attr_num_lus(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", LU_COUNT);
+}
+DEV_STAT_SCSI_TGT_DEV_ATTR_RO(num_lus);
+
+static ssize_t target_stat_scsi_tgt_dev_show_attr_status(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+ char status[16];
+
+ if (!dev)
+ return -ENODEV;
+
+ switch (dev->dev_status) {
+ case TRANSPORT_DEVICE_ACTIVATED:
+ strcpy(status, "activated");
+ break;
+ case TRANSPORT_DEVICE_DEACTIVATED:
+ strcpy(status, "deactivated");
+ break;
+ case TRANSPORT_DEVICE_SHUTDOWN:
+ strcpy(status, "shutdown");
+ break;
+ case TRANSPORT_DEVICE_OFFLINE_ACTIVATED:
+ case TRANSPORT_DEVICE_OFFLINE_DEACTIVATED:
+ strcpy(status, "offline");
+ break;
+ default:
+ sprintf(status, "unknown(%d)", dev->dev_status);
+ break;
+ }
+
+ return snprintf(page, PAGE_SIZE, "%s\n", status);
+}
+DEV_STAT_SCSI_TGT_DEV_ATTR_RO(status);
+
+static ssize_t target_stat_scsi_tgt_dev_show_attr_non_access_lus(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+ int non_accessible_lus;
+
+ if (!dev)
+ return -ENODEV;
+
+ switch (dev->dev_status) {
+ case TRANSPORT_DEVICE_ACTIVATED:
+ non_accessible_lus = 0;
+ break;
+ case TRANSPORT_DEVICE_DEACTIVATED:
+ case TRANSPORT_DEVICE_SHUTDOWN:
+ case TRANSPORT_DEVICE_OFFLINE_ACTIVATED:
+ case TRANSPORT_DEVICE_OFFLINE_DEACTIVATED:
+ default:
+ non_accessible_lus = 1;
+ break;
+ }
+
+ return snprintf(page, PAGE_SIZE, "%u\n", non_accessible_lus);
+}
+DEV_STAT_SCSI_TGT_DEV_ATTR_RO(non_access_lus);
+
+static ssize_t target_stat_scsi_tgt_dev_show_attr_resets(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", dev->num_resets);
+}
+DEV_STAT_SCSI_TGT_DEV_ATTR_RO(resets);
+
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_tgt_dev, se_dev_stat_grps, scsi_tgt_dev_group);
+
+static struct configfs_attribute *target_stat_scsi_tgt_dev_attrs[] = {
+ &target_stat_scsi_tgt_dev_inst.attr,
+ &target_stat_scsi_tgt_dev_indx.attr,
+ &target_stat_scsi_tgt_dev_num_lus.attr,
+ &target_stat_scsi_tgt_dev_status.attr,
+ &target_stat_scsi_tgt_dev_non_access_lus.attr,
+ &target_stat_scsi_tgt_dev_resets.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_tgt_dev_attrib_ops = {
+ .show_attribute = target_stat_scsi_tgt_dev_attr_show,
+ .store_attribute = target_stat_scsi_tgt_dev_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_tgt_dev_cit = {
+ .ct_item_ops = &target_stat_scsi_tgt_dev_attrib_ops,
+ .ct_attrs = target_stat_scsi_tgt_dev_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * SCSI Logical Unit Table
+ */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_lu, se_dev_stat_grps);
+#define DEV_STAT_SCSI_LU_ATTR(_name, _mode) \
+static struct target_stat_scsi_lu_attribute target_stat_scsi_lu_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_lu_show_attr_##_name, \
+ target_stat_scsi_lu_store_attr_##_name);
+
+#define DEV_STAT_SCSI_LU_ATTR_RO(_name) \
+static struct target_stat_scsi_lu_attribute target_stat_scsi_lu_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_lu_show_attr_##_name);
+
+static ssize_t target_stat_scsi_lu_show_attr_inst(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_hba *hba = se_subdev->se_dev_hba;
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_lu_show_attr_dev(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(dev);
+
+static ssize_t target_stat_scsi_lu_show_attr_indx(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", SCSI_LU_INDEX);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_lu_show_attr_lun(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+ /* FIXME: scsiLuDefaultLun */
+ return snprintf(page, PAGE_SIZE, "%llu\n", (unsigned long long)0);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(lun);
+
+static ssize_t target_stat_scsi_lu_show_attr_lu_name(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+ /* scsiLuWwnName */
+ return snprintf(page, PAGE_SIZE, "%s\n",
+ (strlen(DEV_T10_WWN(dev)->unit_serial)) ?
+ (char *)&DEV_T10_WWN(dev)->unit_serial[0] : "None");
+}
+DEV_STAT_SCSI_LU_ATTR_RO(lu_name);
+
+static ssize_t target_stat_scsi_lu_show_attr_vend(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+ int j;
+ char str[28];
+
+ if (!dev)
+ return -ENODEV;
+ /* scsiLuVendorId */
+ memcpy(&str[0], (void *)DEV_T10_WWN(dev), 28);
+ for (j = 0; j < 8; j++)
+ str[j] = ISPRINT(DEV_T10_WWN(dev)->vendor[j]) ?
+ DEV_T10_WWN(dev)->vendor[j] : 0x20;
+ str[8] = 0;
+ return snprintf(page, PAGE_SIZE, "%s\n", str);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(vend);
+
+static ssize_t target_stat_scsi_lu_show_attr_prod(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+ int j;
+ char str[28];
+
+ if (!dev)
+ return -ENODEV;
+
+ /* scsiLuProductId */
+ memcpy(&str[0], (void *)DEV_T10_WWN(dev), 28);
+ for (j = 0; j < 16; j++)
+ str[j] = ISPRINT(DEV_T10_WWN(dev)->model[j]) ?
+ DEV_T10_WWN(dev)->model[j] : 0x20;
+ str[16] = 0;
+ return snprintf(page, PAGE_SIZE, "%s\n", str);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(prod);
+
+static ssize_t target_stat_scsi_lu_show_attr_rev(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+ int j;
+ char str[28];
+
+ if (!dev)
+ return -ENODEV;
+
+ /* scsiLuRevisionId */
+ memcpy(&str[0], (void *)DEV_T10_WWN(dev), 28);
+ for (j = 0; j < 4; j++)
+ str[j] = ISPRINT(DEV_T10_WWN(dev)->revision[j]) ?
+ DEV_T10_WWN(dev)->revision[j] : 0x20;
+ str[4] = 0;
+ return snprintf(page, PAGE_SIZE, "%s\n", str);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(rev);
+
+static ssize_t target_stat_scsi_lu_show_attr_dev_type(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ /* scsiLuPeripheralType */
+ return snprintf(page, PAGE_SIZE, "%u\n",
+ TRANSPORT(dev)->get_device_type(dev));
+}
+DEV_STAT_SCSI_LU_ATTR_RO(dev_type);
+
+static ssize_t target_stat_scsi_lu_show_attr_status(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ /* scsiLuStatus */
+ return snprintf(page, PAGE_SIZE, "%s\n",
+ (dev->dev_status == TRANSPORT_DEVICE_ACTIVATED) ?
+ "available" : "notavailable");
+}
+DEV_STAT_SCSI_LU_ATTR_RO(status);
+
+static ssize_t target_stat_scsi_lu_show_attr_state_bit(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ /* scsiLuState */
+ return snprintf(page, PAGE_SIZE, "exposed\n");
+}
+DEV_STAT_SCSI_LU_ATTR_RO(state_bit);
+
+static ssize_t target_stat_scsi_lu_show_attr_num_cmds(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ /* scsiLuNumCommands */
+ return snprintf(page, PAGE_SIZE, "%llu\n",
+ (unsigned long long)dev->num_cmds);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(num_cmds);
+
+static ssize_t target_stat_scsi_lu_show_attr_read_mbytes(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ /* scsiLuReadMegaBytes */
+ return snprintf(page, PAGE_SIZE, "%u\n", (u32)(dev->read_bytes >> 20));
+}
+DEV_STAT_SCSI_LU_ATTR_RO(read_mbytes);
+
+static ssize_t target_stat_scsi_lu_show_attr_write_mbytes(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ /* scsiLuWrittenMegaBytes */
+ return snprintf(page, PAGE_SIZE, "%u\n", (u32)(dev->write_bytes >> 20));
+}
+DEV_STAT_SCSI_LU_ATTR_RO(write_mbytes);
+
+static ssize_t target_stat_scsi_lu_show_attr_resets(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ /* scsiLuInResets */
+ return snprintf(page, PAGE_SIZE, "%u\n", dev->num_resets);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(resets);
+
+static ssize_t target_stat_scsi_lu_show_attr_full_stat(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ /* FIXME: scsiLuOutTaskSetFullStatus */
+ return snprintf(page, PAGE_SIZE, "%u\n", 0);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(full_stat);
+
+static ssize_t target_stat_scsi_lu_show_attr_hs_num_cmds(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ /* FIXME: scsiLuHSInCommands */
+ return snprintf(page, PAGE_SIZE, "%u\n", 0);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(hs_num_cmds);
+
+static ssize_t target_stat_scsi_lu_show_attr_creation_time(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_subsystem_dev *se_subdev = container_of(sgrps,
+ struct se_subsystem_dev, dev_stat_grps);
+ struct se_device *dev = se_subdev->se_dev_ptr;
+
+ if (!dev)
+ return -ENODEV;
+
+ /* scsiLuCreationTime */
+ return snprintf(page, PAGE_SIZE, "%u\n", (u32)(((u32)dev->creation_time -
+ INITIAL_JIFFIES) * 100 / HZ));
+}
+DEV_STAT_SCSI_LU_ATTR_RO(creation_time);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_lu, se_dev_stat_grps, scsi_lu_group);
+
+static struct configfs_attribute *target_stat_scsi_lu_attrs[] = {
+ &target_stat_scsi_lu_inst.attr,
+ &target_stat_scsi_lu_dev.attr,
+ &target_stat_scsi_lu_indx.attr,
+ &target_stat_scsi_lu_lun.attr,
+ &target_stat_scsi_lu_lu_name.attr,
+ &target_stat_scsi_lu_vend.attr,
+ &target_stat_scsi_lu_prod.attr,
+ &target_stat_scsi_lu_rev.attr,
+ &target_stat_scsi_lu_dev_type.attr,
+ &target_stat_scsi_lu_status.attr,
+ &target_stat_scsi_lu_state_bit.attr,
+ &target_stat_scsi_lu_num_cmds.attr,
+ &target_stat_scsi_lu_read_mbytes.attr,
+ &target_stat_scsi_lu_write_mbytes.attr,
+ &target_stat_scsi_lu_resets.attr,
+ &target_stat_scsi_lu_full_stat.attr,
+ &target_stat_scsi_lu_hs_num_cmds.attr,
+ &target_stat_scsi_lu_creation_time.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_lu_attrib_ops = {
+ .show_attribute = target_stat_scsi_lu_attr_show,
+ .store_attribute = target_stat_scsi_lu_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_lu_cit = {
+ .ct_item_ops = &target_stat_scsi_lu_attrib_ops,
+ .ct_attrs = target_stat_scsi_lu_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Called from target_core_configfs.c:target_core_make_subdev() to setup
+ * the target statistics groups + configfs CITs located in target_core_stat.c
+ */
+void target_stat_setup_dev_default_groups(struct se_subsystem_dev *se_subdev)
+{
+ struct config_group *dev_stat_grp = &DEV_STAT_GRP(se_subdev)->stat_group;
+
+ config_group_init_type_name(&DEV_STAT_GRP(se_subdev)->scsi_dev_group,
+ "scsi_dev", &target_stat_scsi_dev_cit);
+ config_group_init_type_name(&DEV_STAT_GRP(se_subdev)->scsi_tgt_dev_group,
+ "scsi_tgt_dev", &target_stat_scsi_tgt_dev_cit);
+ config_group_init_type_name(&DEV_STAT_GRP(se_subdev)->scsi_lu_group,
+ "scsi_lu", &target_stat_scsi_lu_cit);
+
+ dev_stat_grp->default_groups[0] = &DEV_STAT_GRP(se_subdev)->scsi_dev_group;
+ dev_stat_grp->default_groups[1] = &DEV_STAT_GRP(se_subdev)->scsi_tgt_dev_group;
+ dev_stat_grp->default_groups[2] = &DEV_STAT_GRP(se_subdev)->scsi_lu_group;
+ dev_stat_grp->default_groups[3] = NULL;
+}
+
+/*
+ * SCSI Port Table
+ */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_port, se_port_stat_grps);
+#define DEV_STAT_SCSI_PORT_ATTR(_name, _mode) \
+static struct target_stat_scsi_port_attribute \
+ target_stat_scsi_port_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_port_show_attr_##_name, \
+ target_stat_scsi_port_store_attr_##_name);
+
+#define DEV_STAT_SCSI_PORT_ATTR_RO(_name) \
+static struct target_stat_scsi_port_attribute \
+ target_stat_scsi_port_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_port_show_attr_##_name);
+
+static ssize_t target_stat_scsi_port_show_attr_inst(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_device *dev = lun->lun_se_dev;
+ struct se_hba *hba;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ hba = dev->se_hba;
+ ret = snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_PORT_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_port_show_attr_dev(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_device *dev = lun->lun_se_dev;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_PORT_ATTR_RO(dev);
+
+static ssize_t target_stat_scsi_port_show_attr_indx(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%u\n", sep->sep_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_PORT_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_port_show_attr_role(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_device *dev = lun->lun_se_dev;
+ struct se_port *sep;
+ ssize_t ret;
+
+ if (!dev)
+ return -ENODEV;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%s%u\n", "Device", dev->dev_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_PORT_ATTR_RO(role);
+
+static ssize_t target_stat_scsi_port_show_attr_busy_count(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ /* FIXME: scsiPortBusyStatuses */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", 0);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_PORT_ATTR_RO(busy_count);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_port, se_port_stat_grps, scsi_port_group);
+
+static struct configfs_attribute *target_stat_scsi_port_attrs[] = {
+ &target_stat_scsi_port_inst.attr,
+ &target_stat_scsi_port_dev.attr,
+ &target_stat_scsi_port_indx.attr,
+ &target_stat_scsi_port_role.attr,
+ &target_stat_scsi_port_busy_count.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_port_attrib_ops = {
+ .show_attribute = target_stat_scsi_port_attr_show,
+ .store_attribute = target_stat_scsi_port_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_port_cit = {
+ .ct_item_ops = &target_stat_scsi_port_attrib_ops,
+ .ct_attrs = target_stat_scsi_port_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * SCSI Target Port Table
+ */
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_tgt_port, se_port_stat_grps);
+#define DEV_STAT_SCSI_TGT_PORT_ATTR(_name, _mode) \
+static struct target_stat_scsi_tgt_port_attribute \
+ target_stat_scsi_tgt_port_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_tgt_port_show_attr_##_name, \
+ target_stat_scsi_tgt_port_store_attr_##_name);
+
+#define DEV_STAT_SCSI_TGT_PORT_ATTR_RO(_name) \
+static struct target_stat_scsi_tgt_port_attribute \
+ target_stat_scsi_tgt_port_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_tgt_port_show_attr_##_name);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_inst(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_device *dev = lun->lun_se_dev;
+ struct se_port *sep;
+ struct se_hba *hba;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ hba = dev->se_hba;
+ ret = snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_dev(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_device *dev = lun->lun_se_dev;
+ struct se_port *sep;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(dev);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_indx(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%u\n", sep->sep_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_name(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+
+ ret = snprintf(page, PAGE_SIZE, "%sPort#%u\n",
+ TPG_TFO(tpg)->get_fabric_name(), sep->sep_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(name);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_port_index(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+
+ ret = snprintf(page, PAGE_SIZE, "%s%s%d\n",
+ TPG_TFO(tpg)->tpg_get_wwn(tpg), "+t+",
+ TPG_TFO(tpg)->tpg_get_tag(tpg));
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(port_index);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_in_cmds(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+
+ ret = snprintf(page, PAGE_SIZE, "%llu\n", sep->sep_stats.cmd_pdus);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(in_cmds);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_write_mbytes(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ (u32)(sep->sep_stats.rx_data_octets >> 20));
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(write_mbytes);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_read_mbytes(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ (u32)(sep->sep_stats.tx_data_octets >> 20));
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(read_mbytes);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_hs_in_cmds(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+
+ /* FIXME: scsiTgtPortHsInCommands */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", 0);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(hs_in_cmds);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_tgt_port, se_port_stat_grps,
+ scsi_tgt_port_group);
+
+static struct configfs_attribute *target_stat_scsi_tgt_port_attrs[] = {
+ &target_stat_scsi_tgt_port_inst.attr,
+ &target_stat_scsi_tgt_port_dev.attr,
+ &target_stat_scsi_tgt_port_indx.attr,
+ &target_stat_scsi_tgt_port_name.attr,
+ &target_stat_scsi_tgt_port_port_index.attr,
+ &target_stat_scsi_tgt_port_in_cmds.attr,
+ &target_stat_scsi_tgt_port_write_mbytes.attr,
+ &target_stat_scsi_tgt_port_read_mbytes.attr,
+ &target_stat_scsi_tgt_port_hs_in_cmds.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_tgt_port_attrib_ops = {
+ .show_attribute = target_stat_scsi_tgt_port_attr_show,
+ .store_attribute = target_stat_scsi_tgt_port_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_tgt_port_cit = {
+ .ct_item_ops = &target_stat_scsi_tgt_port_attrib_ops,
+ .ct_attrs = target_stat_scsi_tgt_port_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * SCSI Transport Table
+o */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_transport, se_port_stat_grps);
+#define DEV_STAT_SCSI_TRANSPORT_ATTR(_name, _mode) \
+static struct target_stat_scsi_transport_attribute \
+ target_stat_scsi_transport_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_transport_show_attr_##_name, \
+ target_stat_scsi_transport_store_attr_##_name);
+
+#define DEV_STAT_SCSI_TRANSPORT_ATTR_RO(_name) \
+static struct target_stat_scsi_transport_attribute \
+ target_stat_scsi_transport_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_transport_show_attr_##_name);
+
+static ssize_t target_stat_scsi_transport_show_attr_inst(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_device *dev = lun->lun_se_dev;
+ struct se_port *sep;
+ struct se_hba *hba;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+
+ hba = dev->se_hba;
+ ret = snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TRANSPORT_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_transport_show_attr_device(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+ /* scsiTransportType */
+ ret = snprintf(page, PAGE_SIZE, "scsiTransport%s\n",
+ TPG_TFO(tpg)->get_fabric_name());
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TRANSPORT_ATTR_RO(device);
+
+static ssize_t target_stat_scsi_transport_show_attr_indx(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ TPG_TFO(tpg)->tpg_get_inst_index(tpg));
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TRANSPORT_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_transport_show_attr_dev_name(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_device *dev = lun->lun_se_dev;
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ struct t10_wwn *wwn;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+ wwn = DEV_T10_WWN(dev);
+ /* scsiTransportDevName */
+ ret = snprintf(page, PAGE_SIZE, "%s+%s\n",
+ TPG_TFO(tpg)->tpg_get_wwn(tpg),
+ (strlen(wwn->unit_serial)) ? wwn->unit_serial :
+ wwn->vendor);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TRANSPORT_ATTR_RO(dev_name);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_transport, se_port_stat_grps,
+ scsi_transport_group);
+
+static struct configfs_attribute *target_stat_scsi_transport_attrs[] = {
+ &target_stat_scsi_transport_inst.attr,
+ &target_stat_scsi_transport_device.attr,
+ &target_stat_scsi_transport_indx.attr,
+ &target_stat_scsi_transport_dev_name.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_transport_attrib_ops = {
+ .show_attribute = target_stat_scsi_transport_attr_show,
+ .store_attribute = target_stat_scsi_transport_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_transport_cit = {
+ .ct_item_ops = &target_stat_scsi_transport_attrib_ops,
+ .ct_attrs = target_stat_scsi_transport_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Called from target_core_fabric_configfs.c:target_fabric_make_lun() to setup
+ * the target port statistics groups + configfs CITs located in target_core_stat.c
+ */
+void target_stat_setup_port_default_groups(struct se_lun *lun)
+{
+ struct config_group *port_stat_grp = &PORT_STAT_GRP(lun)->stat_group;
+
+ config_group_init_type_name(&PORT_STAT_GRP(lun)->scsi_port_group,
+ "scsi_port", &target_stat_scsi_port_cit);
+ config_group_init_type_name(&PORT_STAT_GRP(lun)->scsi_tgt_port_group,
+ "scsi_tgt_port", &target_stat_scsi_tgt_port_cit);
+ config_group_init_type_name(&PORT_STAT_GRP(lun)->scsi_transport_group,
+ "scsi_transport", &target_stat_scsi_transport_cit);
+
+ port_stat_grp->default_groups[0] = &PORT_STAT_GRP(lun)->scsi_port_group;
+ port_stat_grp->default_groups[1] = &PORT_STAT_GRP(lun)->scsi_tgt_port_group;
+ port_stat_grp->default_groups[2] = &PORT_STAT_GRP(lun)->scsi_transport_group;
+ port_stat_grp->default_groups[3] = NULL;
+}
+
+/*
+ * SCSI Authorized Initiator Table
+ */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_auth_intr, se_ml_stat_grps);
+#define DEV_STAT_SCSI_AUTH_INTR_ATTR(_name, _mode) \
+static struct target_stat_scsi_auth_intr_attribute \
+ target_stat_scsi_auth_intr_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_auth_intr_show_attr_##_name, \
+ target_stat_scsi_auth_intr_store_attr_##_name);
+
+#define DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(_name) \
+static struct target_stat_scsi_auth_intr_attribute \
+ target_stat_scsi_auth_intr_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_auth_intr_show_attr_##_name);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_inst(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ tpg = nacl->se_tpg;
+ /* scsiInstIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ TPG_TFO(tpg)->tpg_get_inst_index(tpg));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_dev(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ struct se_lun *lun;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ tpg = nacl->se_tpg;
+ lun = deve->se_lun;
+ /* scsiDeviceIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", lun->lun_se_dev->dev_index);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(dev);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_port(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ tpg = nacl->se_tpg;
+ /* scsiAuthIntrTgtPortIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", TPG_TFO(tpg)->tpg_get_tag(tpg));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(port);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_indx(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", nacl->acl_index);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_dev_or_port(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrDevOrPort */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", 1);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(dev_or_port);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_intr_name(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrName */
+ ret = snprintf(page, PAGE_SIZE, "%s\n", nacl->initiatorname);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(intr_name);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_map_indx(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* FIXME: scsiAuthIntrLunMapIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", 0);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(map_indx);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_att_count(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrAttachedTimes */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", deve->attach_count);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(att_count);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_num_cmds(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrOutCommands */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", deve->total_cmds);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(num_cmds);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_read_mbytes(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrReadMegaBytes */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", (u32)(deve->read_bytes >> 20));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(read_mbytes);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_write_mbytes(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrWrittenMegaBytes */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", (u32)(deve->write_bytes >> 20));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(write_mbytes);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_hs_num_cmds(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* FIXME: scsiAuthIntrHSOutCommands */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", 0);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(hs_num_cmds);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_creation_time(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrLastCreation */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", (u32)(((u32)deve->creation_time -
+ INITIAL_JIFFIES) * 100 / HZ));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(creation_time);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_row_status(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* FIXME: scsiAuthIntrRowStatus */
+ ret = snprintf(page, PAGE_SIZE, "Ready\n");
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(row_status);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_auth_intr, se_ml_stat_grps,
+ scsi_auth_intr_group);
+
+static struct configfs_attribute *target_stat_scsi_auth_intr_attrs[] = {
+ &target_stat_scsi_auth_intr_inst.attr,
+ &target_stat_scsi_auth_intr_dev.attr,
+ &target_stat_scsi_auth_intr_port.attr,
+ &target_stat_scsi_auth_intr_indx.attr,
+ &target_stat_scsi_auth_intr_dev_or_port.attr,
+ &target_stat_scsi_auth_intr_intr_name.attr,
+ &target_stat_scsi_auth_intr_map_indx.attr,
+ &target_stat_scsi_auth_intr_att_count.attr,
+ &target_stat_scsi_auth_intr_num_cmds.attr,
+ &target_stat_scsi_auth_intr_read_mbytes.attr,
+ &target_stat_scsi_auth_intr_write_mbytes.attr,
+ &target_stat_scsi_auth_intr_hs_num_cmds.attr,
+ &target_stat_scsi_auth_intr_creation_time.attr,
+ &target_stat_scsi_auth_intr_row_status.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_auth_intr_attrib_ops = {
+ .show_attribute = target_stat_scsi_auth_intr_attr_show,
+ .store_attribute = target_stat_scsi_auth_intr_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_auth_intr_cit = {
+ .ct_item_ops = &target_stat_scsi_auth_intr_attrib_ops,
+ .ct_attrs = target_stat_scsi_auth_intr_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * SCSI Attached Initiator Port Table
+ */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_att_intr_port, se_ml_stat_grps);
+#define DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR(_name, _mode) \
+static struct target_stat_scsi_att_intr_port_attribute \
+ target_stat_scsi_att_intr_port_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_att_intr_port_show_attr_##_name, \
+ target_stat_scsi_att_intr_port_store_attr_##_name);
+
+#define DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(_name) \
+static struct target_stat_scsi_att_intr_port_attribute \
+ target_stat_scsi_att_intr_port_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_att_intr_port_show_attr_##_name);
+
+static ssize_t target_stat_scsi_att_intr_port_show_attr_inst(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ tpg = nacl->se_tpg;
+ /* scsiInstIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ TPG_TFO(tpg)->tpg_get_inst_index(tpg));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_att_intr_port_show_attr_dev(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ struct se_lun *lun;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ tpg = nacl->se_tpg;
+ lun = deve->se_lun;
+ /* scsiDeviceIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", lun->lun_se_dev->dev_index);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(dev);
+
+static ssize_t target_stat_scsi_att_intr_port_show_attr_port(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ tpg = nacl->se_tpg;
+ /* scsiPortIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", TPG_TFO(tpg)->tpg_get_tag(tpg));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(port);
+
+static ssize_t target_stat_scsi_att_intr_port_show_attr_indx(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_session *se_sess;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->nacl_sess_lock);
+ se_sess = nacl->nacl_sess;
+ if (!se_sess) {
+ spin_unlock_irq(&nacl->nacl_sess_lock);
+ return -ENODEV;
+ }
+
+ tpg = nacl->se_tpg;
+ /* scsiAttIntrPortIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ TPG_TFO(tpg)->sess_get_index(se_sess));
+ spin_unlock_irq(&nacl->nacl_sess_lock);
+ return ret;
+}
+DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_att_intr_port_show_attr_port_auth_indx(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = &nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAttIntrPortAuthIntrIdx */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", nacl->acl_index);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(port_auth_indx);
+
+static ssize_t target_stat_scsi_att_intr_port_show_attr_port_ident(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_session *se_sess;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+ unsigned char buf[64];
+
+ spin_lock_irq(&nacl->nacl_sess_lock);
+ se_sess = nacl->nacl_sess;
+ if (!se_sess) {
+ spin_unlock_irq(&nacl->nacl_sess_lock);
+ return -ENODEV;
+ }
+
+ tpg = nacl->se_tpg;
+ /* scsiAttIntrPortName+scsiAttIntrPortIdentifier */
+ memset(buf, 0, 64);
+ if (TPG_TFO(tpg)->sess_get_initiator_sid != NULL)
+ TPG_TFO(tpg)->sess_get_initiator_sid(se_sess,
+ (unsigned char *)&buf[0], 64);
+
+ ret = snprintf(page, PAGE_SIZE, "%s+i+%s\n", nacl->initiatorname, buf);
+ spin_unlock_irq(&nacl->nacl_sess_lock);
+ return ret;
+}
+DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(port_ident);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_att_intr_port, se_ml_stat_grps,
+ scsi_att_intr_port_group);
+
+static struct configfs_attribute *target_stat_scsi_ath_intr_port_attrs[] = {
+ &target_stat_scsi_att_intr_port_inst.attr,
+ &target_stat_scsi_att_intr_port_dev.attr,
+ &target_stat_scsi_att_intr_port_port.attr,
+ &target_stat_scsi_att_intr_port_indx.attr,
+ &target_stat_scsi_att_intr_port_port_auth_indx.attr,
+ &target_stat_scsi_att_intr_port_port_ident.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_att_intr_port_attrib_ops = {
+ .show_attribute = target_stat_scsi_att_intr_port_attr_show,
+ .store_attribute = target_stat_scsi_att_intr_port_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_att_intr_port_cit = {
+ .ct_item_ops = &target_stat_scsi_att_intr_port_attrib_ops,
+ .ct_attrs = target_stat_scsi_ath_intr_port_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Called from target_core_fabric_configfs.c:target_fabric_make_mappedlun() to setup
+ * the target MappedLUN statistics groups + configfs CITs located in target_core_stat.c
+ */
+void target_stat_setup_mappedlun_default_groups(struct se_lun_acl *lacl)
+{
+ struct config_group *ml_stat_grp = &ML_STAT_GRPS(lacl)->stat_group;
+
+ config_group_init_type_name(&ML_STAT_GRPS(lacl)->scsi_auth_intr_group,
+ "scsi_auth_intr", &target_stat_scsi_auth_intr_cit);
+ config_group_init_type_name(&ML_STAT_GRPS(lacl)->scsi_att_intr_port_group,
+ "scsi_att_intr_port", &target_stat_scsi_att_intr_port_cit);
+
+ ml_stat_grp->default_groups[0] = &ML_STAT_GRPS(lacl)->scsi_auth_intr_group;
+ ml_stat_grp->default_groups[1] = &ML_STAT_GRPS(lacl)->scsi_att_intr_port_group;
+ ml_stat_grp->default_groups[2] = NULL;
+}
diff --git a/drivers/target/target_core_stat.h b/drivers/target/target_core_stat.h
new file mode 100644
index 000000000000..86c252f9ea47
--- /dev/null
+++ b/drivers/target/target_core_stat.h
@@ -0,0 +1,8 @@
+#ifndef TARGET_CORE_STAT_H
+#define TARGET_CORE_STAT_H
+
+extern void target_stat_setup_dev_default_groups(struct se_subsystem_dev *);
+extern void target_stat_setup_port_default_groups(struct se_lun *);
+extern void target_stat_setup_mappedlun_default_groups(struct se_lun_acl *);
+
+#endif /*** TARGET_CORE_STAT_H ***/
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 39ac190080e2..b9d3501bdd91 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -227,8 +227,6 @@ static void transport_remove_cmd_from_queue(struct se_cmd *cmd,
static int transport_set_sense_codes(struct se_cmd *cmd, u8 asc, u8 ascq);
static void transport_stop_all_task_timers(struct se_cmd *cmd);
-int transport_emulate_control_cdb(struct se_task *task);
-
int init_se_global(void)
{
struct se_global *global;
@@ -721,7 +719,7 @@ static int transport_cmd_check_stop(
cmd->se_lun = NULL;
/*
* Some fabric modules like tcm_loop can release
- * their internally allocated I/O refrence now and
+ * their internally allocated I/O reference now and
* struct se_cmd now.
*/
if (CMD_TFO(cmd)->check_stop_free != NULL) {
@@ -1622,7 +1620,7 @@ struct se_device *transport_add_device_to_core_hba(
const char *inquiry_prod,
const char *inquiry_rev)
{
- int ret = 0, force_pt;
+ int force_pt;
struct se_device *dev;
dev = kzalloc(sizeof(struct se_device), GFP_KERNEL);
@@ -1739,9 +1737,8 @@ struct se_device *transport_add_device_to_core_hba(
}
scsi_dump_inquiry(dev);
+ return dev;
out:
- if (!ret)
- return dev;
kthread_stop(dev->process_thread);
spin_lock(&hba->device_lock);
@@ -2032,7 +2029,7 @@ int transport_generic_handle_data(
* If the received CDB has aleady been ABORTED by the generic
* target engine, we now call transport_check_aborted_status()
* to queue any delated TASK_ABORTED status for the received CDB to the
- * fabric module as we are expecting no futher incoming DATA OUT
+ * fabric module as we are expecting no further incoming DATA OUT
* sequences at this point.
*/
if (transport_check_aborted_status(cmd, 1) != 0)
@@ -2504,7 +2501,7 @@ static inline int transport_execute_task_attr(struct se_cmd *cmd)
if (SE_DEV(cmd)->dev_task_attr_type != SAM_TASK_ATTR_EMULATED)
return 1;
/*
- * Check for the existance of HEAD_OF_QUEUE, and if true return 1
+ * Check for the existence of HEAD_OF_QUEUE, and if true return 1
* to allow the passed struct se_cmd list of tasks to the front of the list.
*/
if (cmd->sam_task_attr == TASK_ATTR_HOQ) {
@@ -2550,7 +2547,7 @@ static inline int transport_execute_task_attr(struct se_cmd *cmd)
if (atomic_read(&SE_DEV(cmd)->dev_ordered_sync) != 0) {
/*
* Otherwise, add cmd w/ tasks to delayed cmd queue that
- * will be drained upon competion of HEAD_OF_QUEUE task.
+ * will be drained upon completion of HEAD_OF_QUEUE task.
*/
spin_lock(&SE_DEV(cmd)->delayed_cmd_lock);
cmd->se_cmd_flags |= SCF_DELAYED_CMD_FROM_SAM_ATTR;
@@ -2592,7 +2589,7 @@ static int transport_execute_tasks(struct se_cmd *cmd)
}
/*
* Call transport_cmd_check_stop() to see if a fabric exception
- * has occured that prevents execution.
+ * has occurred that prevents execution.
*/
if (!(transport_cmd_check_stop(cmd, 0, TRANSPORT_PROCESSING))) {
/*
@@ -3112,7 +3109,7 @@ static int transport_generic_cmd_sequencer(
if (ret != 0) {
cmd->transport_wait_for_tasks = &transport_nop_wait_for_tasks;
/*
- * Set SCSI additional sense code (ASC) to 'LUN Not Accessable';
+ * Set SCSI additional sense code (ASC) to 'LUN Not Accessible';
* The ALUA additional sense code qualifier (ASCQ) is determined
* by the ALUA primary or secondary access state..
*/
@@ -3870,7 +3867,7 @@ static void transport_generic_complete_ok(struct se_cmd *cmd)
}
}
/*
- * Check for a callback, used by amoungst other things
+ * Check for a callback, used by amongst other things
* XDWRITE_READ_10 emulation.
*/
if (cmd->transport_complete_callback)
@@ -4359,11 +4356,9 @@ transport_generic_get_mem(struct se_cmd *cmd, u32 length, u32 dma_size)
printk(KERN_ERR "Unable to allocate struct se_mem\n");
goto out;
}
- INIT_LIST_HEAD(&se_mem->se_list);
- se_mem->se_len = (length > dma_size) ? dma_size : length;
/* #warning FIXME Allocate contigous pages for struct se_mem elements */
- se_mem->se_page = (struct page *) alloc_pages(GFP_KERNEL, 0);
+ se_mem->se_page = alloc_pages(GFP_KERNEL, 0);
if (!(se_mem->se_page)) {
printk(KERN_ERR "alloc_pages() failed\n");
goto out;
@@ -4374,6 +4369,8 @@ transport_generic_get_mem(struct se_cmd *cmd, u32 length, u32 dma_size)
printk(KERN_ERR "kmap_atomic() failed\n");
goto out;
}
+ INIT_LIST_HEAD(&se_mem->se_list);
+ se_mem->se_len = (length > dma_size) ? dma_size : length;
memset(buf, 0, se_mem->se_len);
kunmap_atomic(buf, KM_IRQ0);
@@ -4392,10 +4389,13 @@ transport_generic_get_mem(struct se_cmd *cmd, u32 length, u32 dma_size)
return 0;
out:
+ if (se_mem)
+ __free_pages(se_mem->se_page, 0);
+ kmem_cache_free(se_mem_cache, se_mem);
return -1;
}
-extern u32 transport_calc_sg_num(
+u32 transport_calc_sg_num(
struct se_task *task,
struct se_mem *in_se_mem,
u32 task_offset)
@@ -5834,31 +5834,26 @@ int transport_generic_do_tmr(struct se_cmd *cmd)
int ret;
switch (tmr->function) {
- case ABORT_TASK:
+ case TMR_ABORT_TASK:
ref_cmd = tmr->ref_cmd;
tmr->response = TMR_FUNCTION_REJECTED;
break;
- case ABORT_TASK_SET:
- case CLEAR_ACA:
- case CLEAR_TASK_SET:
+ case TMR_ABORT_TASK_SET:
+ case TMR_CLEAR_ACA:
+ case TMR_CLEAR_TASK_SET:
tmr->response = TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED;
break;
- case LUN_RESET:
+ case TMR_LUN_RESET:
ret = core_tmr_lun_reset(dev, tmr, NULL, NULL);
tmr->response = (!ret) ? TMR_FUNCTION_COMPLETE :
TMR_FUNCTION_REJECTED;
break;
-#if 0
- case TARGET_WARM_RESET:
- transport_generic_host_reset(dev->se_hba);
+ case TMR_TARGET_WARM_RESET:
tmr->response = TMR_FUNCTION_REJECTED;
break;
- case TARGET_COLD_RESET:
- transport_generic_host_reset(dev->se_hba);
- transport_generic_cold_reset(dev->se_hba);
+ case TMR_TARGET_COLD_RESET:
tmr->response = TMR_FUNCTION_REJECTED;
break;
-#endif
default:
printk(KERN_ERR "Uknown TMR function: 0x%02x.\n",
tmr->function);
diff --git a/drivers/target/target_core_ua.c b/drivers/target/target_core_ua.c
index a2ef346087e8..df355176a377 100644
--- a/drivers/target/target_core_ua.c
+++ b/drivers/target/target_core_ua.c
@@ -247,7 +247,7 @@ void core_scsi3_ua_for_check_condition(
}
/*
* Otherwise for the default 00b, release the UNIT ATTENTION
- * condition. Return the ASC/ASCQ of the higest priority UA
+ * condition. Return the ASC/ASCQ of the highest priority UA
* (head of the list) in the outgoing CHECK_CONDITION + sense.
*/
if (head) {
@@ -304,7 +304,7 @@ int core_scsi3_ua_clear_for_request_sense(
* matching struct se_lun.
*
* Once the returning ASC/ASCQ values are set, we go ahead and
- * release all of the Unit Attention conditions for the assoicated
+ * release all of the Unit Attention conditions for the associated
* struct se_lun.
*/
spin_lock(&deve->ua_lock);