[SCSI] target: Add LIO target core v4.0.0-rc6
Nicholas Bellinger [Fri, 17 Dec 2010 19:11:26 +0000 (11:11 -0800)]
LIO target is a full featured in-kernel target framework with the
following feature set:

High-performance, non-blocking, multithreaded architecture with SIMD
support.

Advanced SCSI feature set:

    * Persistent Reservations (PRs)
    * Asymmetric Logical Unit Assignment (ALUA)
    * Protocol and intra-nexus multiplexing, load-balancing and failover (MC/S)
    * Full Error Recovery (ERL=0,1,2)
    * Active/active task migration and session continuation (ERL=2)
    * Thin LUN provisioning (UNMAP and WRITE_SAMExx)

Multiprotocol target plugins

Storage media independence:

    * Virtualization of all storage media; transparent mapping of IO to LUNs
    * No hard limits on number of LUNs per Target; maximum LUN size ~750 TB
    * Backstores: SATA, SAS, SCSI, BluRay, DVD, FLASH, USB, ramdisk, etc.

Standards compliance:

    * Full compliance with IETF (RFC 3720)
    * Full implementation of SPC-4 PRs and ALUA

Significant code cleanups done by Christoph Hellwig.

[jejb: fix up for new block bdev exclusive interface. Minor fixes from
 Randy Dunlap and Dan Carpenter.]
Signed-off-by: Nicholas A. Bellinger <nab@linux-iscsi.org>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>

44 files changed:
Documentation/target/tcm_mod_builder.py [new file with mode: 0755]
Documentation/target/tcm_mod_builder.txt [new file with mode: 0644]
drivers/Kconfig
drivers/Makefile
drivers/target/Kconfig [new file with mode: 0644]
drivers/target/Makefile [new file with mode: 0644]
drivers/target/target_core_alua.c [new file with mode: 0644]
drivers/target/target_core_alua.h [new file with mode: 0644]
drivers/target/target_core_cdb.c [new file with mode: 0644]
drivers/target/target_core_configfs.c [new file with mode: 0644]
drivers/target/target_core_device.c [new file with mode: 0644]
drivers/target/target_core_fabric_configfs.c [new file with mode: 0644]
drivers/target/target_core_fabric_lib.c [new file with mode: 0644]
drivers/target/target_core_file.c [new file with mode: 0644]
drivers/target/target_core_file.h [new file with mode: 0644]
drivers/target/target_core_hba.c [new file with mode: 0644]
drivers/target/target_core_hba.h [new file with mode: 0644]
drivers/target/target_core_iblock.c [new file with mode: 0644]
drivers/target/target_core_iblock.h [new file with mode: 0644]
drivers/target/target_core_mib.c [new file with mode: 0644]
drivers/target/target_core_mib.h [new file with mode: 0644]
drivers/target/target_core_pr.c [new file with mode: 0644]
drivers/target/target_core_pr.h [new file with mode: 0644]
drivers/target/target_core_pscsi.c [new file with mode: 0644]
drivers/target/target_core_pscsi.h [new file with mode: 0644]
drivers/target/target_core_rd.c [new file with mode: 0644]
drivers/target/target_core_rd.h [new file with mode: 0644]
drivers/target/target_core_scdb.c [new file with mode: 0644]
drivers/target/target_core_scdb.h [new file with mode: 0644]
drivers/target/target_core_tmr.c [new file with mode: 0644]
drivers/target/target_core_tpg.c [new file with mode: 0644]
drivers/target/target_core_transport.c [new file with mode: 0644]
drivers/target/target_core_ua.c [new file with mode: 0644]
drivers/target/target_core_ua.h [new file with mode: 0644]
include/target/configfs_macros.h [new file with mode: 0644]
include/target/target_core_base.h [new file with mode: 0644]
include/target/target_core_configfs.h [new file with mode: 0644]
include/target/target_core_device.h [new file with mode: 0644]
include/target/target_core_fabric_configfs.h [new file with mode: 0644]
include/target/target_core_fabric_lib.h [new file with mode: 0644]
include/target/target_core_fabric_ops.h [new file with mode: 0644]
include/target/target_core_tmr.h [new file with mode: 0644]
include/target/target_core_tpg.h [new file with mode: 0644]
include/target/target_core_transport.h [new file with mode: 0644]

diff --git a/Documentation/target/tcm_mod_builder.py b/Documentation/target/tcm_mod_builder.py
new file mode 100755 (executable)
index 0000000..dbeb8a0
--- /dev/null
@@ -0,0 +1,1094 @@
+#!/usr/bin/python
+# The TCM v4 multi-protocol fabric module generation script for drivers/target/$NEW_MOD
+#
+# Copyright (c) 2010 Rising Tide Systems
+# Copyright (c) 2010 Linux-iSCSI.org
+#
+# Author: nab@kernel.org
+#
+import os, sys
+import subprocess as sub
+import string
+import re
+import optparse
+
+tcm_dir = ""
+
+fabric_ops = []
+fabric_mod_dir = ""
+fabric_mod_port = ""
+fabric_mod_init_port = ""
+
+def tcm_mod_err(msg):
+       print msg
+       sys.exit(1)
+
+def tcm_mod_create_module_subdir(fabric_mod_dir_var):
+
+       if os.path.isdir(fabric_mod_dir_var) == True:
+               return 1
+
+       print "Creating fabric_mod_dir: " + fabric_mod_dir_var
+       ret = os.mkdir(fabric_mod_dir_var)
+       if ret:
+               tcm_mod_err("Unable to mkdir " + fabric_mod_dir_var)
+
+       return
+
+def tcm_mod_build_FC_include(fabric_mod_dir_var, fabric_mod_name):
+       global fabric_mod_port
+       global fabric_mod_init_port
+       buf = ""
+
+       f = fabric_mod_dir_var + "/" + fabric_mod_name + "_base.h"
+       print "Writing file: " + f
+
+       p = open(f, 'w');
+       if not p:
+               tcm_mod_err("Unable to open file: " + f)
+
+       buf = "#define " + fabric_mod_name.upper() + "_VERSION  \"v0.1\"\n"
+       buf += "#define " + fabric_mod_name.upper() + "_NAMELEN 32\n"
+       buf += "\n"
+       buf += "struct " + fabric_mod_name + "_nacl {\n"
+       buf += "        /* Binary World Wide unique Port Name for FC Initiator Nport */\n"
+       buf += "        u64 nport_wwpn;\n"
+       buf += "        /* ASCII formatted WWPN for FC Initiator Nport */\n"
+       buf += "        char nport_name[" + fabric_mod_name.upper() + "_NAMELEN];\n"
+       buf += "        /* Returned by " + fabric_mod_name + "_make_nodeacl() */\n"
+       buf += "        struct se_node_acl se_node_acl;\n"
+       buf += "};\n"
+       buf += "\n"
+       buf += "struct " + fabric_mod_name + "_tpg {\n"
+       buf += "        /* FC lport target portal group tag for TCM */\n"
+       buf += "        u16 lport_tpgt;\n"
+       buf += "        /* Pointer back to " + fabric_mod_name + "_lport */\n"
+       buf += "        struct " + fabric_mod_name + "_lport *lport;\n"
+       buf += "        /* Returned by " + fabric_mod_name + "_make_tpg() */\n"
+       buf += "        struct se_portal_group se_tpg;\n"
+       buf += "};\n"
+       buf += "\n"
+       buf += "struct " + fabric_mod_name + "_lport {\n"
+       buf += "        /* SCSI protocol the lport is providing */\n"
+       buf += "        u8 lport_proto_id;\n"
+       buf += "        /* Binary World Wide unique Port Name for FC Target Lport */\n"
+       buf += "        u64 lport_wwpn;\n"
+       buf += "        /* ASCII formatted WWPN for FC Target Lport */\n"
+       buf += "        char lport_name[" + fabric_mod_name.upper() + "_NAMELEN];\n"
+       buf += "        /* Returned by " + fabric_mod_name + "_make_lport() */\n"
+       buf += "        struct se_wwn lport_wwn;\n"
+       buf += "};\n"
+
+       ret = p.write(buf)
+       if ret:
+               tcm_mod_err("Unable to write f: " + f)
+
+       p.close()
+
+       fabric_mod_port = "lport"
+       fabric_mod_init_port = "nport"
+
+       return
+
+def tcm_mod_build_SAS_include(fabric_mod_dir_var, fabric_mod_name):
+       global fabric_mod_port
+       global fabric_mod_init_port
+       buf = ""
+
+       f = fabric_mod_dir_var + "/" + fabric_mod_name + "_base.h"
+       print "Writing file: " + f
+
+       p = open(f, 'w');
+       if not p:
+               tcm_mod_err("Unable to open file: " + f)
+
+       buf = "#define " + fabric_mod_name.upper() + "_VERSION  \"v0.1\"\n"
+       buf += "#define " + fabric_mod_name.upper() + "_NAMELEN 32\n"
+       buf += "\n"
+       buf += "struct " + fabric_mod_name + "_nacl {\n"
+       buf += "        /* Binary World Wide unique Port Name for SAS Initiator port */\n"
+       buf += "        u64 iport_wwpn;\n"
+       buf += "        /* ASCII formatted WWPN for Sas Initiator port */\n"
+       buf += "        char iport_name[" + fabric_mod_name.upper() + "_NAMELEN];\n"
+       buf += "        /* Returned by " + fabric_mod_name + "_make_nodeacl() */\n"
+       buf += "        struct se_node_acl se_node_acl;\n"
+       buf += "};\n\n"
+       buf += "struct " + fabric_mod_name + "_tpg {\n"
+       buf += "        /* SAS port target portal group tag for TCM */\n"
+       buf += "        u16 tport_tpgt;\n"
+       buf += "        /* Pointer back to " + fabric_mod_name + "_tport */\n"
+       buf += "        struct " + fabric_mod_name + "_tport *tport;\n"
+       buf += "        /* Returned by " + fabric_mod_name + "_make_tpg() */\n"
+       buf += "        struct se_portal_group se_tpg;\n"
+       buf += "};\n\n"
+       buf += "struct " + fabric_mod_name + "_tport {\n"
+       buf += "        /* SCSI protocol the tport is providing */\n"
+       buf += "        u8 tport_proto_id;\n"
+       buf += "        /* Binary World Wide unique Port Name for SAS Target port */\n"
+       buf += "        u64 tport_wwpn;\n"
+       buf += "        /* ASCII formatted WWPN for SAS Target port */\n"
+       buf += "        char tport_name[" + fabric_mod_name.upper() + "_NAMELEN];\n"
+       buf += "        /* Returned by " + fabric_mod_name + "_make_tport() */\n"
+       buf += "        struct se_wwn tport_wwn;\n"
+       buf += "};\n"
+
+       ret = p.write(buf)
+       if ret:
+               tcm_mod_err("Unable to write f: " + f)
+
+       p.close()
+
+       fabric_mod_port = "tport"
+       fabric_mod_init_port = "iport"
+
+       return
+
+def tcm_mod_build_iSCSI_include(fabric_mod_dir_var, fabric_mod_name):
+       global fabric_mod_port
+       global fabric_mod_init_port
+       buf = ""
+
+       f = fabric_mod_dir_var + "/" + fabric_mod_name + "_base.h"
+       print "Writing file: " + f
+
+       p = open(f, 'w');
+       if not p:
+               tcm_mod_err("Unable to open file: " + f)
+
+       buf = "#define " + fabric_mod_name.upper() + "_VERSION  \"v0.1\"\n"
+       buf += "#define " + fabric_mod_name.upper() + "_NAMELEN 32\n"
+       buf += "\n"
+       buf += "struct " + fabric_mod_name + "_nacl {\n"
+       buf += "        /* ASCII formatted InitiatorName */\n"
+       buf += "        char iport_name[" + fabric_mod_name.upper() + "_NAMELEN];\n"
+       buf += "        /* Returned by " + fabric_mod_name + "_make_nodeacl() */\n"
+       buf += "        struct se_node_acl se_node_acl;\n"
+       buf += "};\n\n"
+       buf += "struct " + fabric_mod_name + "_tpg {\n"
+       buf += "        /* iSCSI target portal group tag for TCM */\n"
+       buf += "        u16 tport_tpgt;\n"
+       buf += "        /* Pointer back to " + fabric_mod_name + "_tport */\n"
+       buf += "        struct " + fabric_mod_name + "_tport *tport;\n"
+       buf += "        /* Returned by " + fabric_mod_name + "_make_tpg() */\n"
+       buf += "        struct se_portal_group se_tpg;\n"
+       buf += "};\n\n"
+       buf += "struct " + fabric_mod_name + "_tport {\n"
+       buf += "        /* SCSI protocol the tport is providing */\n"
+       buf += "        u8 tport_proto_id;\n"
+       buf += "        /* ASCII formatted TargetName for IQN */\n"
+       buf += "        char tport_name[" + fabric_mod_name.upper() + "_NAMELEN];\n"
+       buf += "        /* Returned by " + fabric_mod_name + "_make_tport() */\n"
+       buf += "        struct se_wwn tport_wwn;\n"
+       buf += "};\n"
+
+       ret = p.write(buf)
+       if ret:
+               tcm_mod_err("Unable to write f: " + f)
+
+       p.close()
+
+       fabric_mod_port = "tport"
+       fabric_mod_init_port = "iport"
+
+       return
+
+def tcm_mod_build_base_includes(proto_ident, fabric_mod_dir_val, fabric_mod_name):
+
+       if proto_ident == "FC":
+               tcm_mod_build_FC_include(fabric_mod_dir_val, fabric_mod_name)
+       elif proto_ident == "SAS":
+               tcm_mod_build_SAS_include(fabric_mod_dir_val, fabric_mod_name)
+       elif proto_ident == "iSCSI":
+               tcm_mod_build_iSCSI_include(fabric_mod_dir_val, fabric_mod_name)
+       else:
+               print "Unsupported proto_ident: " + proto_ident
+               sys.exit(1)
+
+       return
+
+def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name):
+       buf = ""
+
+       f = fabric_mod_dir_var + "/" + fabric_mod_name + "_configfs.c"
+       print "Writing file: " + f
+
+        p = open(f, 'w');
+        if not p:
+                tcm_mod_err("Unable to open file: " + f)
+
+       buf = "#include <linux/module.h>\n"
+       buf += "#include <linux/moduleparam.h>\n"
+       buf += "#include <linux/version.h>\n"
+       buf += "#include <generated/utsrelease.h>\n"
+       buf += "#include <linux/utsname.h>\n"
+       buf += "#include <linux/init.h>\n"
+       buf += "#include <linux/slab.h>\n"
+       buf += "#include <linux/kthread.h>\n"
+       buf += "#include <linux/types.h>\n"
+       buf += "#include <linux/string.h>\n"
+       buf += "#include <linux/configfs.h>\n"
+       buf += "#include <linux/ctype.h>\n"
+       buf += "#include <asm/unaligned.h>\n\n"
+       buf += "#include <target/target_core_base.h>\n"
+       buf += "#include <target/target_core_transport.h>\n"
+       buf += "#include <target/target_core_fabric_ops.h>\n"
+       buf += "#include <target/target_core_fabric_configfs.h>\n"
+       buf += "#include <target/target_core_fabric_lib.h>\n"
+       buf += "#include <target/target_core_device.h>\n"
+       buf += "#include <target/target_core_tpg.h>\n"
+       buf += "#include <target/target_core_configfs.h>\n"
+       buf += "#include <target/target_core_base.h>\n"
+       buf += "#include <target/configfs_macros.h>\n\n"
+       buf += "#include <" + fabric_mod_name + "_base.h>\n"
+       buf += "#include <" + fabric_mod_name + "_fabric.h>\n\n"
+
+       buf += "/* Local pointer to allocated TCM configfs fabric module */\n"
+       buf += "struct target_fabric_configfs *" + fabric_mod_name + "_fabric_configfs;\n\n"
+
+       buf += "static struct se_node_acl *" + fabric_mod_name + "_make_nodeacl(\n"
+       buf += "        struct se_portal_group *se_tpg,\n"
+       buf += "        struct config_group *group,\n"
+       buf += "        const char *name)\n"
+       buf += "{\n"
+       buf += "        struct se_node_acl *se_nacl, *se_nacl_new;\n"
+       buf += "        struct " + fabric_mod_name + "_nacl *nacl;\n"
+
+       if proto_ident == "FC" or proto_ident == "SAS":
+               buf += "        u64 wwpn = 0;\n"
+
+       buf += "        u32 nexus_depth;\n\n"
+       buf += "        /* " + fabric_mod_name + "_parse_wwn(name, &wwpn, 1) < 0)\n"
+       buf += "                return ERR_PTR(-EINVAL); */\n"
+       buf += "        se_nacl_new = " + fabric_mod_name + "_alloc_fabric_acl(se_tpg);\n"
+       buf += "        if (!(se_nacl_new))\n"
+       buf += "                return ERR_PTR(-ENOMEM);\n"
+       buf += "//#warning FIXME: Hardcoded nexus depth in " + fabric_mod_name + "_make_nodeacl()\n"
+       buf += "        nexus_depth = 1;\n"
+       buf += "        /*\n"
+       buf += "         * se_nacl_new may be released by core_tpg_add_initiator_node_acl()\n"
+       buf += "         * when converting a NodeACL from demo mode -> explict\n"
+       buf += "         */\n"
+       buf += "        se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,\n"
+       buf += "                                name, nexus_depth);\n"
+       buf += "        if (IS_ERR(se_nacl)) {\n"
+       buf += "                " + fabric_mod_name + "_release_fabric_acl(se_tpg, se_nacl_new);\n"
+       buf += "                return se_nacl;\n"
+       buf += "        }\n"
+       buf += "        /*\n"
+       buf += "         * Locate our struct " + fabric_mod_name + "_nacl and set the FC Nport WWPN\n"
+       buf += "         */\n"
+       buf += "        nacl = container_of(se_nacl, struct " + fabric_mod_name + "_nacl, se_node_acl);\n"
+
+       if proto_ident == "FC" or proto_ident == "SAS":
+               buf += "        nacl->" + fabric_mod_init_port + "_wwpn = wwpn;\n"
+
+       buf += "        /* " + fabric_mod_name + "_format_wwn(&nacl->" + fabric_mod_init_port + "_name[0], " + fabric_mod_name.upper() + "_NAMELEN, wwpn); */\n\n"
+       buf += "        return se_nacl;\n"
+       buf += "}\n\n"
+       buf += "static void " + fabric_mod_name + "_drop_nodeacl(struct se_node_acl *se_acl)\n"
+       buf += "{\n"
+       buf += "        struct " + fabric_mod_name + "_nacl *nacl = container_of(se_acl,\n"
+       buf += "                                struct " + fabric_mod_name + "_nacl, se_node_acl);\n"
+       buf += "        kfree(nacl);\n"
+       buf += "}\n\n"
+
+       buf += "static struct se_portal_group *" + fabric_mod_name + "_make_tpg(\n"
+       buf += "        struct se_wwn *wwn,\n"
+       buf += "        struct config_group *group,\n"
+       buf += "        const char *name)\n"
+       buf += "{\n"
+       buf += "        struct " + fabric_mod_name + "_" + fabric_mod_port + "*" + fabric_mod_port + " = container_of(wwn,\n"
+       buf += "                        struct " + fabric_mod_name + "_" + fabric_mod_port + ", " + fabric_mod_port + "_wwn);\n\n"
+       buf += "        struct " + fabric_mod_name + "_tpg *tpg;\n"
+       buf += "        unsigned long tpgt;\n"
+       buf += "        int ret;\n\n"
+       buf += "        if (strstr(name, \"tpgt_\") != name)\n"
+       buf += "                return ERR_PTR(-EINVAL);\n"
+       buf += "        if (strict_strtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)\n"
+       buf += "                return ERR_PTR(-EINVAL);\n\n"
+       buf += "        tpg = kzalloc(sizeof(struct " + fabric_mod_name + "_tpg), GFP_KERNEL);\n"
+       buf += "        if (!(tpg)) {\n"
+       buf += "                printk(KERN_ERR \"Unable to allocate struct " + fabric_mod_name + "_tpg\");\n"
+       buf += "                return ERR_PTR(-ENOMEM);\n"
+       buf += "        }\n"
+       buf += "        tpg->" + fabric_mod_port + " = " + fabric_mod_port + ";\n"
+       buf += "        tpg->" + fabric_mod_port + "_tpgt = tpgt;\n\n"
+       buf += "        ret = core_tpg_register(&" + fabric_mod_name + "_fabric_configfs->tf_ops, wwn,\n"
+       buf += "                                &tpg->se_tpg, (void *)tpg,\n"
+       buf += "                                TRANSPORT_TPG_TYPE_NORMAL);\n"
+       buf += "        if (ret < 0) {\n"
+       buf += "                kfree(tpg);\n"
+       buf += "                return NULL;\n"
+       buf += "        }\n"
+       buf += "        return &tpg->se_tpg;\n"
+       buf += "}\n\n"
+       buf += "static void " + fabric_mod_name + "_drop_tpg(struct se_portal_group *se_tpg)\n"
+       buf += "{\n"
+       buf += "        struct " + fabric_mod_name + "_tpg *tpg = container_of(se_tpg,\n"
+       buf += "                                struct " + fabric_mod_name + "_tpg, se_tpg);\n\n"
+       buf += "        core_tpg_deregister(se_tpg);\n"
+       buf += "        kfree(tpg);\n"
+       buf += "}\n\n"
+
+       buf += "static struct se_wwn *" + fabric_mod_name + "_make_" + fabric_mod_port + "(\n"
+       buf += "        struct target_fabric_configfs *tf,\n"
+       buf += "        struct config_group *group,\n"
+       buf += "        const char *name)\n"
+       buf += "{\n"
+       buf += "        struct " + fabric_mod_name + "_" + fabric_mod_port + " *" + fabric_mod_port + ";\n"
+
+       if proto_ident == "FC" or proto_ident == "SAS":
+               buf += "        u64 wwpn = 0;\n\n"
+
+       buf += "        /* if (" + fabric_mod_name + "_parse_wwn(name, &wwpn, 1) < 0)\n"
+       buf += "                return ERR_PTR(-EINVAL); */\n\n"
+       buf += "        " + fabric_mod_port + " = kzalloc(sizeof(struct " + fabric_mod_name + "_" + fabric_mod_port + "), GFP_KERNEL);\n"
+       buf += "        if (!(" + fabric_mod_port + ")) {\n"
+       buf += "                printk(KERN_ERR \"Unable to allocate struct " + fabric_mod_name + "_" + fabric_mod_port + "\");\n"
+       buf += "                return ERR_PTR(-ENOMEM);\n"
+       buf += "        }\n"
+
+       if proto_ident == "FC" or proto_ident == "SAS":
+               buf += "        " + fabric_mod_port + "->" + fabric_mod_port + "_wwpn = wwpn;\n"
+
+       buf += "        /* " + fabric_mod_name + "_format_wwn(&" + fabric_mod_port + "->" + fabric_mod_port + "_name[0], " + fabric_mod_name.upper() + "__NAMELEN, wwpn); */\n\n"
+       buf += "        return &" + fabric_mod_port + "->" + fabric_mod_port + "_wwn;\n"
+       buf += "}\n\n"
+       buf += "static void " + fabric_mod_name + "_drop_" + fabric_mod_port + "(struct se_wwn *wwn)\n"
+       buf += "{\n"
+       buf += "        struct " + fabric_mod_name + "_" + fabric_mod_port + " *" + fabric_mod_port + " = container_of(wwn,\n"
+       buf += "                                struct " + fabric_mod_name + "_" + fabric_mod_port + ", " + fabric_mod_port + "_wwn);\n"
+       buf += "        kfree(" + fabric_mod_port + ");\n"
+       buf += "}\n\n"
+       buf += "static ssize_t " + fabric_mod_name + "_wwn_show_attr_version(\n"
+       buf += "        struct target_fabric_configfs *tf,\n"
+       buf += "        char *page)\n"
+       buf += "{\n"
+       buf += "        return sprintf(page, \"" + fabric_mod_name.upper() + " fabric module %s on %s/%s\"\n"
+       buf += "                \"on \"UTS_RELEASE\"\\n\", " + fabric_mod_name.upper() + "_VERSION, utsname()->sysname,\n"
+       buf += "                utsname()->machine);\n"
+       buf += "}\n\n"
+       buf += "TF_WWN_ATTR_RO(" + fabric_mod_name + ", version);\n\n"
+       buf += "static struct configfs_attribute *" + fabric_mod_name + "_wwn_attrs[] = {\n"
+       buf += "        &" + fabric_mod_name + "_wwn_version.attr,\n"
+       buf += "        NULL,\n"
+       buf += "};\n\n"
+
+       buf += "static struct target_core_fabric_ops " + fabric_mod_name + "_ops = {\n"
+       buf += "        .get_fabric_name                = " + fabric_mod_name + "_get_fabric_name,\n"
+       buf += "        .get_fabric_proto_ident         = " + fabric_mod_name + "_get_fabric_proto_ident,\n"
+       buf += "        .tpg_get_wwn                    = " + fabric_mod_name + "_get_fabric_wwn,\n"
+       buf += "        .tpg_get_tag                    = " + fabric_mod_name + "_get_tag,\n"
+       buf += "        .tpg_get_default_depth          = " + fabric_mod_name + "_get_default_depth,\n"
+       buf += "        .tpg_get_pr_transport_id        = " + fabric_mod_name + "_get_pr_transport_id,\n"
+       buf += "        .tpg_get_pr_transport_id_len    = " + fabric_mod_name + "_get_pr_transport_id_len,\n"
+       buf += "        .tpg_parse_pr_out_transport_id  = " + fabric_mod_name + "_parse_pr_out_transport_id,\n"
+       buf += "        .tpg_check_demo_mode            = " + fabric_mod_name + "_check_false,\n"
+       buf += "        .tpg_check_demo_mode_cache      = " + fabric_mod_name + "_check_true,\n"
+       buf += "        .tpg_check_demo_mode_write_protect = " + fabric_mod_name + "_check_true,\n"
+       buf += "        .tpg_check_prod_mode_write_protect = " + fabric_mod_name + "_check_false,\n"
+       buf += "        .tpg_alloc_fabric_acl           = " + fabric_mod_name + "_alloc_fabric_acl,\n"
+       buf += "        .tpg_release_fabric_acl         = " + fabric_mod_name + "_release_fabric_acl,\n"
+       buf += "        .tpg_get_inst_index             = " + fabric_mod_name + "_tpg_get_inst_index,\n"
+       buf += "        .release_cmd_to_pool            = " + fabric_mod_name + "_release_cmd,\n"
+       buf += "        .release_cmd_direct             = " + fabric_mod_name + "_release_cmd,\n"
+       buf += "        .shutdown_session               = " + fabric_mod_name + "_shutdown_session,\n"
+       buf += "        .close_session                  = " + fabric_mod_name + "_close_session,\n"
+       buf += "        .stop_session                   = " + fabric_mod_name + "_stop_session,\n"
+       buf += "        .fall_back_to_erl0              = " + fabric_mod_name + "_reset_nexus,\n"
+       buf += "        .sess_logged_in                 = " + fabric_mod_name + "_sess_logged_in,\n"
+       buf += "        .sess_get_index                 = " + fabric_mod_name + "_sess_get_index,\n"
+       buf += "        .sess_get_initiator_sid         = NULL,\n"
+       buf += "        .write_pending                  = " + fabric_mod_name + "_write_pending,\n"
+       buf += "        .write_pending_status           = " + fabric_mod_name + "_write_pending_status,\n"
+       buf += "        .set_default_node_attributes    = " + fabric_mod_name + "_set_default_node_attrs,\n"
+       buf += "        .get_task_tag                   = " + fabric_mod_name + "_get_task_tag,\n"
+       buf += "        .get_cmd_state                  = " + fabric_mod_name + "_get_cmd_state,\n"
+       buf += "        .new_cmd_failure                = " + fabric_mod_name + "_new_cmd_failure,\n"
+       buf += "        .queue_data_in                  = " + fabric_mod_name + "_queue_data_in,\n"
+       buf += "        .queue_status                   = " + fabric_mod_name + "_queue_status,\n"
+       buf += "        .queue_tm_rsp                   = " + fabric_mod_name + "_queue_tm_rsp,\n"
+       buf += "        .get_fabric_sense_len           = " + fabric_mod_name + "_get_fabric_sense_len,\n"
+       buf += "        .set_fabric_sense_len           = " + fabric_mod_name + "_set_fabric_sense_len,\n"
+       buf += "        .is_state_remove                = " + fabric_mod_name + "_is_state_remove,\n"
+       buf += "        .pack_lun                       = " + fabric_mod_name + "_pack_lun,\n"
+       buf += "        /*\n"
+       buf += "         * Setup function pointers for generic logic in target_core_fabric_configfs.c\n"
+       buf += "         */\n"
+       buf += "        .fabric_make_wwn                = " + fabric_mod_name + "_make_" + fabric_mod_port + ",\n"
+       buf += "        .fabric_drop_wwn                = " + fabric_mod_name + "_drop_" + fabric_mod_port + ",\n"
+       buf += "        .fabric_make_tpg                = " + fabric_mod_name + "_make_tpg,\n"
+       buf += "        .fabric_drop_tpg                = " + fabric_mod_name + "_drop_tpg,\n"
+       buf += "        .fabric_post_link               = NULL,\n"
+       buf += "        .fabric_pre_unlink              = NULL,\n"
+       buf += "        .fabric_make_np                 = NULL,\n"
+       buf += "        .fabric_drop_np                 = NULL,\n"
+       buf += "        .fabric_make_nodeacl            = " + fabric_mod_name + "_make_nodeacl,\n"
+       buf += "        .fabric_drop_nodeacl            = " + fabric_mod_name + "_drop_nodeacl,\n"
+       buf += "};\n\n"
+
+       buf += "static int " + fabric_mod_name + "_register_configfs(void)\n"
+       buf += "{\n"
+       buf += "        struct target_fabric_configfs *fabric;\n"
+       buf += "        int ret;\n\n"
+       buf += "        printk(KERN_INFO \"" + fabric_mod_name.upper() + " fabric module %s on %s/%s\"\n"
+       buf += "                \" on \"UTS_RELEASE\"\\n\"," + fabric_mod_name.upper() + "_VERSION, utsname()->sysname,\n"
+       buf += "                utsname()->machine);\n"
+       buf += "        /*\n"
+       buf += "         * Register the top level struct config_item_type with TCM core\n"
+       buf += "         */\n"
+       buf += "        fabric = target_fabric_configfs_init(THIS_MODULE, \"" + fabric_mod_name[4:] + "\");\n"
+       buf += "        if (!(fabric)) {\n"
+       buf += "                printk(KERN_ERR \"target_fabric_configfs_init() failed\\n\");\n"
+       buf += "                return -ENOMEM;\n"
+       buf += "        }\n"
+       buf += "        /*\n"
+       buf += "         * Setup fabric->tf_ops from our local " + fabric_mod_name + "_ops\n"
+       buf += "         */\n"
+       buf += "        fabric->tf_ops = " + fabric_mod_name + "_ops;\n"
+       buf += "        /*\n"
+       buf += "         * Setup default attribute lists for various fabric->tf_cit_tmpl\n"
+       buf += "         */\n"
+       buf += "        TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = " + fabric_mod_name + "_wwn_attrs;\n"
+       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = NULL;\n"
+       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;\n"
+       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;\n"
+       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;\n"
+       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;\n"
+       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;\n"
+       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;\n"
+       buf += "        TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;\n"
+       buf += "        /*\n"
+       buf += "         * Register the fabric for use within TCM\n"
+       buf += "         */\n"
+       buf += "        ret = target_fabric_configfs_register(fabric);\n"
+       buf += "        if (ret < 0) {\n"
+       buf += "                printk(KERN_ERR \"target_fabric_configfs_register() failed\"\n"
+       buf += "                                \" for " + fabric_mod_name.upper() + "\\n\");\n"
+       buf += "                return ret;\n"
+       buf += "        }\n"
+       buf += "        /*\n"
+       buf += "         * Setup our local pointer to *fabric\n"
+       buf += "         */\n"
+       buf += "        " + fabric_mod_name + "_fabric_configfs = fabric;\n"
+       buf += "        printk(KERN_INFO \"" +  fabric_mod_name.upper() + "[0] - Set fabric -> " + fabric_mod_name + "_fabric_configfs\\n\");\n"
+       buf += "        return 0;\n"
+       buf += "};\n\n"
+       buf += "static void " + fabric_mod_name + "_deregister_configfs(void)\n"
+       buf += "{\n"
+       buf += "        if (!(" + fabric_mod_name + "_fabric_configfs))\n"
+       buf += "                return;\n\n"
+       buf += "        target_fabric_configfs_deregister(" + fabric_mod_name + "_fabric_configfs);\n"
+       buf += "        " + fabric_mod_name + "_fabric_configfs = NULL;\n"
+       buf += "        printk(KERN_INFO \"" +  fabric_mod_name.upper() + "[0] - Cleared " + fabric_mod_name + "_fabric_configfs\\n\");\n"
+       buf += "};\n\n"
+
+       buf += "static int __init " + fabric_mod_name + "_init(void)\n"
+       buf += "{\n"
+       buf += "        int ret;\n\n"
+       buf += "        ret = " + fabric_mod_name + "_register_configfs();\n"
+       buf += "        if (ret < 0)\n"
+       buf += "                return ret;\n\n"
+       buf += "        return 0;\n"
+       buf += "};\n\n"
+       buf += "static void " + fabric_mod_name + "_exit(void)\n"
+       buf += "{\n"
+       buf += "        " + fabric_mod_name + "_deregister_configfs();\n"
+       buf += "};\n\n"
+
+       buf += "#ifdef MODULE\n"
+       buf += "MODULE_DESCRIPTION(\"" + fabric_mod_name.upper() + " series fabric driver\");\n"
+       buf += "MODULE_LICENSE(\"GPL\");\n"
+       buf += "module_init(" + fabric_mod_name + "_init);\n"
+       buf += "module_exit(" + fabric_mod_name + "_exit);\n"
+       buf += "#endif\n"
+
+       ret = p.write(buf)
+       if ret:
+               tcm_mod_err("Unable to write f: " + f)
+
+       p.close()
+
+       return
+
+def tcm_mod_scan_fabric_ops(tcm_dir):
+
+       fabric_ops_api = tcm_dir + "include/target/target_core_fabric_ops.h"
+
+       print "Using tcm_mod_scan_fabric_ops: " + fabric_ops_api
+       process_fo = 0;
+
+       p = open(fabric_ops_api, 'r')
+
+       line = p.readline()
+       while line:
+               if process_fo == 0 and re.search('struct target_core_fabric_ops {', line):
+                       line = p.readline()
+                       continue
+
+               if process_fo == 0:
+                       process_fo = 1;
+                       line = p.readline()
+                       # Search for function pointer
+                       if not re.search('\(\*', line):
+                               continue
+
+                       fabric_ops.append(line.rstrip())
+                       continue
+
+               line = p.readline()
+               # Search for function pointer
+               if not re.search('\(\*', line):
+                       continue
+
+               fabric_ops.append(line.rstrip())
+
+       p.close()
+       return
+
+def tcm_mod_dump_fabric_ops(proto_ident, fabric_mod_dir_var, fabric_mod_name):
+       buf = ""
+       bufi = ""
+
+       f = fabric_mod_dir_var + "/" + fabric_mod_name + "_fabric.c"
+       print "Writing file: " + f
+
+       p = open(f, 'w')
+       if not p:
+               tcm_mod_err("Unable to open file: " + f)
+
+       fi = fabric_mod_dir_var + "/" + fabric_mod_name + "_fabric.h"
+       print "Writing file: " + fi
+
+       pi = open(fi, 'w')
+       if not pi:
+               tcm_mod_err("Unable to open file: " + fi)
+
+       buf = "#include <linux/slab.h>\n"
+       buf += "#include <linux/kthread.h>\n"
+       buf += "#include <linux/types.h>\n"
+       buf += "#include <linux/list.h>\n"
+       buf += "#include <linux/types.h>\n"
+       buf += "#include <linux/string.h>\n"
+       buf += "#include <linux/ctype.h>\n"
+       buf += "#include <asm/unaligned.h>\n"
+       buf += "#include <scsi/scsi.h>\n"
+       buf += "#include <scsi/scsi_host.h>\n"
+       buf += "#include <scsi/scsi_device.h>\n"
+       buf += "#include <scsi/scsi_cmnd.h>\n"
+       buf += "#include <scsi/libfc.h>\n\n"
+       buf += "#include <target/target_core_base.h>\n"
+       buf += "#include <target/target_core_transport.h>\n"
+       buf += "#include <target/target_core_fabric_ops.h>\n"
+       buf += "#include <target/target_core_fabric_lib.h>\n"
+       buf += "#include <target/target_core_device.h>\n"
+       buf += "#include <target/target_core_tpg.h>\n"
+       buf += "#include <target/target_core_configfs.h>\n"
+       buf += "#include <" + fabric_mod_name + "_base.h>\n"
+       buf += "#include <" + fabric_mod_name + "_fabric.h>\n\n"
+
+       buf += "int " + fabric_mod_name + "_check_true(struct se_portal_group *se_tpg)\n"
+       buf += "{\n"
+       buf += "        return 1;\n"
+       buf += "}\n\n"
+       bufi += "int " + fabric_mod_name + "_check_true(struct se_portal_group *);\n"
+
+       buf += "int " + fabric_mod_name + "_check_false(struct se_portal_group *se_tpg)\n"
+       buf += "{\n"
+       buf += "        return 0;\n"
+       buf += "}\n\n"
+       bufi += "int " + fabric_mod_name + "_check_false(struct se_portal_group *);\n"
+
+       total_fabric_ops = len(fabric_ops)
+       i = 0
+
+       while i < total_fabric_ops:
+               fo = fabric_ops[i]
+               i += 1
+#              print "fabric_ops: " + fo
+
+               if re.search('get_fabric_name', fo):
+                       buf += "char *" + fabric_mod_name + "_get_fabric_name(void)\n"
+                       buf += "{\n"
+                       buf += "        return \"" + fabric_mod_name[4:] + "\";\n"
+                       buf += "}\n\n"
+                       bufi += "char *" + fabric_mod_name + "_get_fabric_name(void);\n"
+                       continue
+
+               if re.search('get_fabric_proto_ident', fo):
+                       buf += "u8 " + fabric_mod_name + "_get_fabric_proto_ident(struct se_portal_group *se_tpg)\n"
+                       buf += "{\n"
+                       buf += "        struct " + fabric_mod_name + "_tpg *tpg = container_of(se_tpg,\n"
+                       buf += "                                struct " + fabric_mod_name + "_tpg, se_tpg);\n"
+                       buf += "        struct " + fabric_mod_name + "_" + fabric_mod_port + " *" + fabric_mod_port + " = tpg->" + fabric_mod_port + ";\n"
+                       buf += "        u8 proto_id;\n\n"
+                       buf += "        switch (" + fabric_mod_port + "->" + fabric_mod_port + "_proto_id) {\n"
+                       if proto_ident == "FC":
+                               buf += "        case SCSI_PROTOCOL_FCP:\n"
+                               buf += "        default:\n"
+                               buf += "                proto_id = fc_get_fabric_proto_ident(se_tpg);\n"
+                               buf += "                break;\n"
+                       elif proto_ident == "SAS":
+                               buf += "        case SCSI_PROTOCOL_SAS:\n"
+                               buf += "        default:\n"
+                               buf += "                proto_id = sas_get_fabric_proto_ident(se_tpg);\n"
+                               buf += "                break;\n"
+                       elif proto_ident == "iSCSI":
+                               buf += "        case SCSI_PROTOCOL_ISCSI:\n"
+                               buf += "        default:\n"
+                               buf += "                proto_id = iscsi_get_fabric_proto_ident(se_tpg);\n"
+                               buf += "                break;\n"
+
+                       buf += "        }\n\n"
+                       buf += "        return proto_id;\n"
+                       buf += "}\n\n"
+                       bufi += "u8 " + fabric_mod_name + "_get_fabric_proto_ident(struct se_portal_group *);\n"
+
+               if re.search('get_wwn', fo):
+                       buf += "char *" + fabric_mod_name + "_get_fabric_wwn(struct se_portal_group *se_tpg)\n"
+                       buf += "{\n"
+                       buf += "        struct " + fabric_mod_name + "_tpg *tpg = container_of(se_tpg,\n"
+                       buf += "                                struct " + fabric_mod_name + "_tpg, se_tpg);\n"
+                       buf += "        struct " + fabric_mod_name + "_" + fabric_mod_port + " *" + fabric_mod_port + " = tpg->" + fabric_mod_port + ";\n\n"
+                       buf += "        return &" + fabric_mod_port + "->" + fabric_mod_port + "_name[0];\n"
+                       buf += "}\n\n"
+                       bufi += "char *" + fabric_mod_name + "_get_fabric_wwn(struct se_portal_group *);\n"
+
+               if re.search('get_tag', fo):
+                       buf += "u16 " + fabric_mod_name + "_get_tag(struct se_portal_group *se_tpg)\n"
+                       buf += "{\n"
+                       buf += "        struct " + fabric_mod_name + "_tpg *tpg = container_of(se_tpg,\n"
+                       buf += "                                struct " + fabric_mod_name + "_tpg, se_tpg);\n"
+                       buf += "        return tpg->" + fabric_mod_port + "_tpgt;\n"
+                       buf += "}\n\n"
+                       bufi += "u16 " + fabric_mod_name + "_get_tag(struct se_portal_group *);\n"
+
+               if re.search('get_default_depth', fo):
+                       buf += "u32 " + fabric_mod_name + "_get_default_depth(struct se_portal_group *se_tpg)\n"
+                       buf += "{\n"
+                       buf += "        return 1;\n"
+                       buf += "}\n\n"
+                       bufi += "u32 " + fabric_mod_name + "_get_default_depth(struct se_portal_group *);\n"
+
+               if re.search('get_pr_transport_id\)\(', fo):
+                       buf += "u32 " + fabric_mod_name + "_get_pr_transport_id(\n"
+                       buf += "        struct se_portal_group *se_tpg,\n"
+                       buf += "        struct se_node_acl *se_nacl,\n"
+                       buf += "        struct t10_pr_registration *pr_reg,\n"
+                       buf += "        int *format_code,\n"
+                       buf += "        unsigned char *buf)\n"
+                       buf += "{\n"
+                       buf += "        struct " + fabric_mod_name + "_tpg *tpg = container_of(se_tpg,\n"
+                       buf += "                                struct " + fabric_mod_name + "_tpg, se_tpg);\n"
+                       buf += "        struct " + fabric_mod_name + "_" + fabric_mod_port + " *" + fabric_mod_port + " = tpg->" + fabric_mod_port + ";\n"
+                       buf += "        int ret = 0;\n\n"
+                       buf += "        switch (" + fabric_mod_port + "->" + fabric_mod_port + "_proto_id) {\n"
+                       if proto_ident == "FC":
+                               buf += "        case SCSI_PROTOCOL_FCP:\n"
+                               buf += "        default:\n"
+                               buf += "                ret = fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg,\n"
+                               buf += "                                        format_code, buf);\n"
+                               buf += "                break;\n"
+                       elif proto_ident == "SAS":
+                               buf += "        case SCSI_PROTOCOL_SAS:\n"
+                               buf += "        default:\n"
+                               buf += "                ret = sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,\n"
+                               buf += "                                        format_code, buf);\n"
+                               buf += "                break;\n"
+                       elif proto_ident == "iSCSI":
+                               buf += "        case SCSI_PROTOCOL_ISCSI:\n"
+                               buf += "        default:\n"
+                               buf += "                ret = iscsi_get_pr_transport_id(se_tpg, se_nacl, pr_reg,\n"
+                               buf += "                                        format_code, buf);\n"
+                               buf += "                break;\n"
+
+                       buf += "        }\n\n"
+                       buf += "        return ret;\n"
+                       buf += "}\n\n"
+                       bufi += "u32 " + fabric_mod_name + "_get_pr_transport_id(struct se_portal_group *,\n"
+                       bufi += "                       struct se_node_acl *, struct t10_pr_registration *,\n"
+                       bufi += "                       int *, unsigned char *);\n"
+
+               if re.search('get_pr_transport_id_len\)\(', fo):
+                       buf += "u32 " + fabric_mod_name + "_get_pr_transport_id_len(\n"
+                       buf += "        struct se_portal_group *se_tpg,\n"
+                       buf += "        struct se_node_acl *se_nacl,\n"
+                       buf += "        struct t10_pr_registration *pr_reg,\n"
+                       buf += "        int *format_code)\n"
+                       buf += "{\n"
+                       buf += "        struct " + fabric_mod_name + "_tpg *tpg = container_of(se_tpg,\n"
+                       buf += "                                struct " + fabric_mod_name + "_tpg, se_tpg);\n"
+                       buf += "        struct " + fabric_mod_name + "_" + fabric_mod_port + " *" + fabric_mod_port + " = tpg->" + fabric_mod_port + ";\n"
+                       buf += "        int ret = 0;\n\n"
+                       buf += "        switch (" + fabric_mod_port + "->" + fabric_mod_port + "_proto_id) {\n"
+                       if proto_ident == "FC":
+                               buf += "        case SCSI_PROTOCOL_FCP:\n"
+                               buf += "        default:\n"
+                               buf += "                ret = fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,\n"
+                               buf += "                                        format_code);\n"
+                               buf += "                break;\n"
+                       elif proto_ident == "SAS":
+                               buf += "        case SCSI_PROTOCOL_SAS:\n"
+                               buf += "        default:\n"
+                               buf += "                ret = sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,\n"
+                               buf += "                                        format_code);\n"
+                               buf += "                break;\n"
+                       elif proto_ident == "iSCSI":
+                               buf += "        case SCSI_PROTOCOL_ISCSI:\n"
+                               buf += "        default:\n"
+                               buf += "                ret = iscsi_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,\n"
+                               buf += "                                        format_code);\n"
+                               buf += "                break;\n"
+
+
+                       buf += "        }\n\n"
+                       buf += "        return ret;\n"
+                       buf += "}\n\n"
+                       bufi += "u32 " + fabric_mod_name + "_get_pr_transport_id_len(struct se_portal_group *,\n"
+                       bufi += "                       struct se_node_acl *, struct t10_pr_registration *,\n"
+                       bufi += "                       int *);\n"
+
+               if re.search('parse_pr_out_transport_id\)\(', fo):
+                       buf += "char *" + fabric_mod_name + "_parse_pr_out_transport_id(\n"
+                       buf += "        struct se_portal_group *se_tpg,\n"
+                       buf += "        const char *buf,\n"
+                       buf += "        u32 *out_tid_len,\n"
+                       buf += "        char **port_nexus_ptr)\n"
+                       buf += "{\n"
+                       buf += "        struct " + fabric_mod_name + "_tpg *tpg = container_of(se_tpg,\n"
+                       buf += "                                struct " + fabric_mod_name + "_tpg, se_tpg);\n"
+                       buf += "        struct " + fabric_mod_name + "_" + fabric_mod_port + " *" + fabric_mod_port + " = tpg->" + fabric_mod_port + ";\n"
+                       buf += "        char *tid = NULL;\n\n"
+                       buf += "        switch (" + fabric_mod_port + "->" + fabric_mod_port + "_proto_id) {\n"
+                       if proto_ident == "FC":
+                               buf += "        case SCSI_PROTOCOL_FCP:\n"
+                               buf += "        default:\n"
+                               buf += "                tid = fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,\n"
+                               buf += "                                        port_nexus_ptr);\n"
+                       elif proto_ident == "SAS":
+                               buf += "        case SCSI_PROTOCOL_SAS:\n"
+                               buf += "        default:\n"
+                               buf += "                tid = sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,\n"
+                               buf += "                                        port_nexus_ptr);\n"
+                       elif proto_ident == "iSCSI":
+                               buf += "        case SCSI_PROTOCOL_ISCSI:\n"
+                               buf += "        default:\n"
+                               buf += "                tid = iscsi_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,\n"
+                               buf += "                                        port_nexus_ptr);\n"
+
+                       buf += "        }\n\n"
+                       buf += "        return tid;\n"
+                       buf += "}\n\n"
+                       bufi += "char *" + fabric_mod_name + "_parse_pr_out_transport_id(struct se_portal_group *,\n"
+                       bufi += "                       const char *, u32 *, char **);\n"
+
+               if re.search('alloc_fabric_acl\)\(', fo):
+                       buf += "struct se_node_acl *" + fabric_mod_name + "_alloc_fabric_acl(struct se_portal_group *se_tpg)\n"
+                       buf += "{\n"
+                       buf += "        struct " + fabric_mod_name + "_nacl *nacl;\n\n"
+                       buf += "        nacl = kzalloc(sizeof(struct " + fabric_mod_name + "_nacl), GFP_KERNEL);\n"
+                       buf += "        if (!(nacl)) {\n"
+                       buf += "                printk(KERN_ERR \"Unable to alocate struct " + fabric_mod_name + "_nacl\\n\");\n"
+                       buf += "                return NULL;\n"
+                       buf += "        }\n\n"
+                       buf += "        return &nacl->se_node_acl;\n"
+                       buf += "}\n\n"
+                       bufi += "struct se_node_acl *" + fabric_mod_name + "_alloc_fabric_acl(struct se_portal_group *);\n"
+
+               if re.search('release_fabric_acl\)\(', fo):
+                       buf += "void " + fabric_mod_name + "_release_fabric_acl(\n"
+                       buf += "        struct se_portal_group *se_tpg,\n"
+                       buf += "        struct se_node_acl *se_nacl)\n"
+                       buf += "{\n"
+                       buf += "        struct " + fabric_mod_name + "_nacl *nacl = container_of(se_nacl,\n"
+                       buf += "                        struct " + fabric_mod_name + "_nacl, se_node_acl);\n"
+                       buf += "        kfree(nacl);\n"
+                       buf += "}\n\n"
+                       bufi += "void " + fabric_mod_name + "_release_fabric_acl(struct se_portal_group *,\n"
+                       bufi += "                       struct se_node_acl *);\n"
+
+               if re.search('tpg_get_inst_index\)\(', fo):
+                       buf += "u32 " + fabric_mod_name + "_tpg_get_inst_index(struct se_portal_group *se_tpg)\n"
+                       buf += "{\n"
+                       buf += "        return 1;\n"
+                       buf += "}\n\n"
+                       bufi += "u32 " + fabric_mod_name + "_tpg_get_inst_index(struct se_portal_group *);\n"
+
+               if re.search('release_cmd_to_pool', fo):
+                       buf += "void " + fabric_mod_name + "_release_cmd(struct se_cmd *se_cmd)\n"
+                       buf += "{\n"
+                       buf += "        return;\n"
+                       buf += "}\n\n"
+                       bufi += "void " + fabric_mod_name + "_release_cmd(struct se_cmd *);\n"
+
+               if re.search('shutdown_session\)\(', fo):
+                       buf += "int " + fabric_mod_name + "_shutdown_session(struct se_session *se_sess)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "int " + fabric_mod_name + "_shutdown_session(struct se_session *);\n"
+
+               if re.search('close_session\)\(', fo):
+                       buf += "void " + fabric_mod_name + "_close_session(struct se_session *se_sess)\n"
+                       buf += "{\n"
+                       buf += "        return;\n"
+                       buf += "}\n\n"
+                       bufi += "void " + fabric_mod_name + "_close_session(struct se_session *);\n"
+
+               if re.search('stop_session\)\(', fo):
+                       buf += "void " + fabric_mod_name + "_stop_session(struct se_session *se_sess, int sess_sleep , int conn_sleep)\n"
+                       buf += "{\n"
+                       buf += "        return;\n"
+                       buf += "}\n\n"
+                       bufi += "void " + fabric_mod_name + "_stop_session(struct se_session *, int, int);\n"
+
+               if re.search('fall_back_to_erl0\)\(', fo):
+                       buf += "void " + fabric_mod_name + "_reset_nexus(struct se_session *se_sess)\n"
+                       buf += "{\n"
+                       buf += "        return;\n"
+                       buf += "}\n\n"
+                       bufi += "void " + fabric_mod_name + "_reset_nexus(struct se_session *);\n"
+
+               if re.search('sess_logged_in\)\(', fo):
+                       buf += "int " + fabric_mod_name + "_sess_logged_in(struct se_session *se_sess)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "int " + fabric_mod_name + "_sess_logged_in(struct se_session *);\n"
+
+               if re.search('sess_get_index\)\(', fo):
+                       buf += "u32 " + fabric_mod_name + "_sess_get_index(struct se_session *se_sess)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "u32 " + fabric_mod_name + "_sess_get_index(struct se_session *);\n"
+
+               if re.search('write_pending\)\(', fo):
+                       buf += "int " + fabric_mod_name + "_write_pending(struct se_cmd *se_cmd)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "int " + fabric_mod_name + "_write_pending(struct se_cmd *);\n"
+
+               if re.search('write_pending_status\)\(', fo):
+                       buf += "int " + fabric_mod_name + "_write_pending_status(struct se_cmd *se_cmd)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "int " + fabric_mod_name + "_write_pending_status(struct se_cmd *);\n"
+
+               if re.search('set_default_node_attributes\)\(', fo):
+                       buf += "void " + fabric_mod_name + "_set_default_node_attrs(struct se_node_acl *nacl)\n"
+                       buf += "{\n"
+                       buf += "        return;\n"
+                       buf += "}\n\n"
+                       bufi += "void " + fabric_mod_name + "_set_default_node_attrs(struct se_node_acl *);\n"
+
+               if re.search('get_task_tag\)\(', fo):
+                       buf += "u32 " + fabric_mod_name + "_get_task_tag(struct se_cmd *se_cmd)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "u32 " + fabric_mod_name + "_get_task_tag(struct se_cmd *);\n"
+
+               if re.search('get_cmd_state\)\(', fo):
+                       buf += "int " + fabric_mod_name + "_get_cmd_state(struct se_cmd *se_cmd)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "int " + fabric_mod_name + "_get_cmd_state(struct se_cmd *);\n"
+
+               if re.search('new_cmd_failure\)\(', fo):
+                       buf += "void " + fabric_mod_name + "_new_cmd_failure(struct se_cmd *se_cmd)\n"
+                       buf += "{\n"
+                       buf += "        return;\n"
+                       buf += "}\n\n"
+                       bufi += "void " + fabric_mod_name + "_new_cmd_failure(struct se_cmd *);\n"
+
+               if re.search('queue_data_in\)\(', fo):
+                       buf += "int " + fabric_mod_name + "_queue_data_in(struct se_cmd *se_cmd)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "int " + fabric_mod_name + "_queue_data_in(struct se_cmd *);\n"
+
+               if re.search('queue_status\)\(', fo):
+                       buf += "int " + fabric_mod_name + "_queue_status(struct se_cmd *se_cmd)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "int " + fabric_mod_name + "_queue_status(struct se_cmd *);\n"
+
+               if re.search('queue_tm_rsp\)\(', fo):
+                       buf += "int " + fabric_mod_name + "_queue_tm_rsp(struct se_cmd *se_cmd)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "int " + fabric_mod_name + "_queue_tm_rsp(struct se_cmd *);\n"
+
+               if re.search('get_fabric_sense_len\)\(', fo):
+                       buf += "u16 " + fabric_mod_name + "_get_fabric_sense_len(void)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "u16 " + fabric_mod_name + "_get_fabric_sense_len(void);\n"
+
+               if re.search('set_fabric_sense_len\)\(', fo):
+                       buf += "u16 " + fabric_mod_name + "_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "u16 " + fabric_mod_name + "_set_fabric_sense_len(struct se_cmd *, u32);\n"
+
+               if re.search('is_state_remove\)\(', fo):
+                       buf += "int " + fabric_mod_name + "_is_state_remove(struct se_cmd *se_cmd)\n"
+                       buf += "{\n"
+                       buf += "        return 0;\n"
+                       buf += "}\n\n"
+                       bufi += "int " + fabric_mod_name + "_is_state_remove(struct se_cmd *);\n"
+
+               if re.search('pack_lun\)\(', fo):
+                       buf += "u64 " + fabric_mod_name + "_pack_lun(unsigned int lun)\n"
+                       buf += "{\n"
+                       buf += "        WARN_ON(lun >= 256);\n"
+                       buf += "        /* Caller wants this byte-swapped */\n"
+                       buf += "        return cpu_to_le64((lun & 0xff) << 8);\n"
+                       buf += "}\n\n"
+                       bufi += "u64 " + fabric_mod_name + "_pack_lun(unsigned int);\n"
+
+
+       ret = p.write(buf)
+       if ret:
+               tcm_mod_err("Unable to write f: " + f)
+
+       p.close()
+
+       ret = pi.write(bufi)
+       if ret:
+               tcm_mod_err("Unable to write fi: " + fi)
+
+       pi.close()
+       return
+
+def tcm_mod_build_kbuild(fabric_mod_dir_var, fabric_mod_name):
+
+       buf = ""
+       f = fabric_mod_dir_var + "/Kbuild"
+       print "Writing file: " + f
+
+       p = open(f, 'w')
+       if not p:
+               tcm_mod_err("Unable to open file: " + f)
+
+       buf = "EXTRA_CFLAGS += -I$(srctree)/drivers/target/ -I$(srctree)/include/ -I$(srctree)/drivers/scsi/ -I$(srctree)/include/scsi/ -I$(srctree)/drivers/target/" + fabric_mod_name + "\n\n"
+       buf += fabric_mod_name + "-objs                 := " + fabric_mod_name + "_fabric.o \\\n"
+       buf += "                                           " + fabric_mod_name + "_configfs.o\n"
+       buf += "obj-$(CONFIG_" + fabric_mod_name.upper() + ")           += " + fabric_mod_name + ".o\n"
+
+       ret = p.write(buf)
+       if ret:
+               tcm_mod_err("Unable to write f: " + f)
+
+       p.close()
+       return
+
+def tcm_mod_build_kconfig(fabric_mod_dir_var, fabric_mod_name):
+
+       buf = ""
+       f = fabric_mod_dir_var + "/Kconfig"
+       print "Writing file: " + f
+
+       p = open(f, 'w')
+       if not p:
+               tcm_mod_err("Unable to open file: " + f)
+
+       buf = "config " + fabric_mod_name.upper() + "\n"
+       buf += "        tristate \"" + fabric_mod_name.upper() + " fabric module\"\n"
+       buf += "        depends on TARGET_CORE && CONFIGFS_FS\n"
+       buf += "        default n\n"
+       buf += "        ---help---\n"
+       buf += "        Say Y here to enable the " + fabric_mod_name.upper() + " fabric module\n"
+
+       ret = p.write(buf)
+       if ret:
+               tcm_mod_err("Unable to write f: " + f)
+
+       p.close()
+       return
+
+def tcm_mod_add_kbuild(tcm_dir, fabric_mod_name):
+       buf = "obj-$(CONFIG_" + fabric_mod_name.upper() + ")    += " + fabric_mod_name.lower() + "/\n"
+       kbuild = tcm_dir + "/drivers/target/Kbuild"
+
+       f = open(kbuild, 'a')
+       f.write(buf)
+       f.close()
+       return
+
+def tcm_mod_add_kconfig(tcm_dir, fabric_mod_name):
+       buf = "source \"drivers/target/" + fabric_mod_name.lower() + "/Kconfig\"\n"
+       kconfig = tcm_dir + "/drivers/target/Kconfig"
+
+       f = open(kconfig, 'a')
+       f.write(buf)
+       f.close()
+       return
+
+def main(modname, proto_ident):
+#      proto_ident = "FC"
+#      proto_ident = "SAS"
+#      proto_ident = "iSCSI"
+
+       tcm_dir = os.getcwd();
+       tcm_dir += "/../../"
+       print "tcm_dir: " + tcm_dir
+       fabric_mod_name = modname
+       fabric_mod_dir = tcm_dir + "drivers/target/" + fabric_mod_name
+       print "Set fabric_mod_name: " + fabric_mod_name
+       print "Set fabric_mod_dir: " + fabric_mod_dir
+       print "Using proto_ident: " + proto_ident
+
+       if proto_ident != "FC" and proto_ident != "SAS" and proto_ident != "iSCSI":
+               print "Unsupported proto_ident: " + proto_ident
+               sys.exit(1)
+
+       ret = tcm_mod_create_module_subdir(fabric_mod_dir)
+       if ret:
+               print "tcm_mod_create_module_subdir() failed because module already exists!"
+               sys.exit(1)
+
+       tcm_mod_build_base_includes(proto_ident, fabric_mod_dir, fabric_mod_name)
+       tcm_mod_scan_fabric_ops(tcm_dir)
+       tcm_mod_dump_fabric_ops(proto_ident, fabric_mod_dir, fabric_mod_name)
+       tcm_mod_build_configfs(proto_ident, fabric_mod_dir, fabric_mod_name)
+       tcm_mod_build_kbuild(fabric_mod_dir, fabric_mod_name)
+       tcm_mod_build_kconfig(fabric_mod_dir, fabric_mod_name)
+
+       input = raw_input("Would you like to add " + fabric_mod_name + "to drivers/target/Kbuild..? [yes,no]: ")
+       if input == "yes" or input == "y":
+               tcm_mod_add_kbuild(tcm_dir, fabric_mod_name)
+
+       input = raw_input("Would you like to add " + fabric_mod_name + "to drivers/target/Kconfig..? [yes,no]: ")
+       if input == "yes" or input == "y":
+               tcm_mod_add_kconfig(tcm_dir, fabric_mod_name)
+
+       return
+
+parser = optparse.OptionParser()
+parser.add_option('-m', '--modulename', help='Module name', dest='modname',
+               action='store', nargs=1, type='string')
+parser.add_option('-p', '--protoident', help='Protocol Ident', dest='protoident',
+               action='store', nargs=1, type='string')
+
+(opts, args) = parser.parse_args()
+
+mandatories = ['modname', 'protoident']
+for m in mandatories:
+       if not opts.__dict__[m]:
+               print "mandatory option is missing\n"
+               parser.print_help()
+               exit(-1)
+
+if __name__ == "__main__":
+
+       main(str(opts.modname), opts.protoident)
diff --git a/Documentation/target/tcm_mod_builder.txt b/Documentation/target/tcm_mod_builder.txt
new file mode 100644 (file)
index 0000000..84533d8
--- /dev/null
@@ -0,0 +1,145 @@
+>>>>>>>>>> The TCM v4 fabric module script generator <<<<<<<<<<
+
+Greetings all,
+
+This document is intended to be a mini-HOWTO for using the tcm_mod_builder.py
+script to generate a brand new functional TCM v4 fabric .ko module of your very own,
+that once built can be immediately be loaded to start access the new TCM/ConfigFS
+fabric skeleton, by simply using:
+
+       modprobe $TCM_NEW_MOD
+       mkdir -p /sys/kernel/config/target/$TCM_NEW_MOD
+
+This script will create a new drivers/target/$TCM_NEW_MOD/, and will do the following
+
+       *) Generate new API callers for drivers/target/target_core_fabric_configs.c logic
+          ->make_nodeacl(), ->drop_nodeacl(), ->make_tpg(), ->drop_tpg()
+          ->make_wwn(), ->drop_wwn().  These are created into $TCM_NEW_MOD/$TCM_NEW_MOD_configfs.c
+       *) Generate basic infrastructure for loading/unloading LKMs and TCM/ConfigFS fabric module
+          using a skeleton struct target_core_fabric_ops API template.
+       *) Based on user defined T10 Proto_Ident for the new fabric module being built,
+          the TransportID / Initiator and Target WWPN related handlers for
+          SPC-3 persistent reservation are automatically generated in $TCM_NEW_MOD/$TCM_NEW_MOD_fabric.c
+          using drivers/target/target_core_fabric_lib.c logic.
+       *) NOP API calls for all other Data I/O path and fabric dependent attribute logic
+          in $TCM_NEW_MOD/$TCM_NEW_MOD_fabric.c
+
+tcm_mod_builder.py depends upon the mandatory '-p $PROTO_IDENT' and '-m
+$FABRIC_MOD_name' parameters, and actually running the script looks like:
+
+target:/mnt/sdb/lio-core-2.6.git/Documentation/target# python tcm_mod_builder.py -p iSCSI -m tcm_nab5000
+tcm_dir: /mnt/sdb/lio-core-2.6.git/Documentation/target/../../
+Set fabric_mod_name: tcm_nab5000
+Set fabric_mod_dir:
+/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000
+Using proto_ident: iSCSI
+Creating fabric_mod_dir:
+/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000
+Writing file:
+/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000/tcm_nab5000_base.h
+Using tcm_mod_scan_fabric_ops:
+/mnt/sdb/lio-core-2.6.git/Documentation/target/../../include/target/target_core_fabric_ops.h
+Writing file:
+/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000/tcm_nab5000_fabric.c
+Writing file:
+/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000/tcm_nab5000_fabric.h
+Writing file:
+/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000/tcm_nab5000_configfs.c
+Writing file:
+/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000/Kbuild
+Writing file:
+/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000/Kconfig
+Would you like to add tcm_nab5000to drivers/target/Kbuild..? [yes,no]: yes
+Would you like to add tcm_nab5000to drivers/target/Kconfig..? [yes,no]: yes
+
+At the end of tcm_mod_builder.py. the script will ask to add the following
+line to drivers/target/Kbuild:
+
+       obj-$(CONFIG_TCM_NAB5000)       += tcm_nab5000/
+
+and the same for drivers/target/Kconfig:
+
+       source "drivers/target/tcm_nab5000/Kconfig"
+
+*) Run 'make menuconfig' and select the new CONFIG_TCM_NAB5000 item:
+
+       <M>   TCM_NAB5000 fabric module
+
+*) Build using 'make modules', once completed you will have:
+
+target:/mnt/sdb/lio-core-2.6.git# ls -la drivers/target/tcm_nab5000/
+total 1348
+drwxr-xr-x 2 root root   4096 2010-10-05 03:23 .
+drwxr-xr-x 9 root root   4096 2010-10-05 03:22 ..
+-rw-r--r-- 1 root root    282 2010-10-05 03:22 Kbuild
+-rw-r--r-- 1 root root    171 2010-10-05 03:22 Kconfig
+-rw-r--r-- 1 root root     49 2010-10-05 03:23 modules.order
+-rw-r--r-- 1 root root    738 2010-10-05 03:22 tcm_nab5000_base.h
+-rw-r--r-- 1 root root   9096 2010-10-05 03:22 tcm_nab5000_configfs.c
+-rw-r--r-- 1 root root 191200 2010-10-05 03:23 tcm_nab5000_configfs.o
+-rw-r--r-- 1 root root  40504 2010-10-05 03:23 .tcm_nab5000_configfs.o.cmd
+-rw-r--r-- 1 root root   5414 2010-10-05 03:22 tcm_nab5000_fabric.c
+-rw-r--r-- 1 root root   2016 2010-10-05 03:22 tcm_nab5000_fabric.h
+-rw-r--r-- 1 root root 190932 2010-10-05 03:23 tcm_nab5000_fabric.o
+-rw-r--r-- 1 root root  40713 2010-10-05 03:23 .tcm_nab5000_fabric.o.cmd
+-rw-r--r-- 1 root root 401861 2010-10-05 03:23 tcm_nab5000.ko
+-rw-r--r-- 1 root root    265 2010-10-05 03:23 .tcm_nab5000.ko.cmd
+-rw-r--r-- 1 root root    459 2010-10-05 03:23 tcm_nab5000.mod.c
+-rw-r--r-- 1 root root  23896 2010-10-05 03:23 tcm_nab5000.mod.o
+-rw-r--r-- 1 root root  22655 2010-10-05 03:23 .tcm_nab5000.mod.o.cmd
+-rw-r--r-- 1 root root 379022 2010-10-05 03:23 tcm_nab5000.o
+-rw-r--r-- 1 root root    211 2010-10-05 03:23 .tcm_nab5000.o.cmd
+
+*) Load the new module, create a lun_0 configfs group, and add new TCM Core
+   IBLOCK backstore symlink to port:
+
+target:/mnt/sdb/lio-core-2.6.git# insmod drivers/target/tcm_nab5000.ko
+target:/mnt/sdb/lio-core-2.6.git# mkdir -p /sys/kernel/config/target/nab5000/iqn.foo/tpgt_1/lun/lun_0
+target:/mnt/sdb/lio-core-2.6.git# cd /sys/kernel/config/target/nab5000/iqn.foo/tpgt_1/lun/lun_0/
+target:/sys/kernel/config/target/nab5000/iqn.foo/tpgt_1/lun/lun_0# ln -s /sys/kernel/config/target/core/iblock_0/lvm_test0 nab5000_port
+
+target:/sys/kernel/config/target/nab5000/iqn.foo/tpgt_1/lun/lun_0# cd -
+target:/mnt/sdb/lio-core-2.6.git# tree /sys/kernel/config/target/nab5000/
+/sys/kernel/config/target/nab5000/
+|-- discovery_auth
+|-- iqn.foo
+|   `-- tpgt_1
+|       |-- acls
+|       |-- attrib
+|       |-- lun
+|       |   `-- lun_0
+|       |       |-- alua_tg_pt_gp
+|       |       |-- alua_tg_pt_offline
+|       |       |-- alua_tg_pt_status
+|       |       |-- alua_tg_pt_write_md
+|      |       `-- nab5000_port -> ../../../../../../target/core/iblock_0/lvm_test0
+|       |-- np
+|       `-- param
+`-- version
+
+target:/mnt/sdb/lio-core-2.6.git# lsmod
+Module                  Size  Used by
+tcm_nab5000             3935  4
+iscsi_target_mod      193211  0
+target_core_stgt        8090  0
+target_core_pscsi      11122  1
+target_core_file        9172  2
+target_core_iblock      9280  1
+target_core_mod       228575  31
+tcm_nab5000,iscsi_target_mod,target_core_stgt,target_core_pscsi,target_core_file,target_core_iblock
+libfc                  73681  0
+scsi_debug             56265  0
+scsi_tgt                8666  1 target_core_stgt
+configfs               20644  2 target_core_mod
+
+----------------------------------------------------------------------
+
+Future TODO items:
+
+       *) Add more T10 proto_idents
+       *) Make tcm_mod_dump_fabric_ops() smarter and generate function pointer
+          defs directly from include/target/target_core_fabric_ops.h:struct target_core_fabric_ops
+          structure members.
+
+October 5th, 2010
+Nicholas A. Bellinger <nab@linux-iscsi.org>
index dd0a5b5..9bfb71f 100644 (file)
@@ -26,6 +26,8 @@ source "drivers/ata/Kconfig"
 
 source "drivers/md/Kconfig"
 
+source "drivers/target/Kconfig"
+
 source "drivers/message/fusion/Kconfig"
 
 source "drivers/firewire/Kconfig"
index ef51324..7eb35f4 100644 (file)
@@ -46,6 +46,7 @@ obj-y                         += macintosh/
 obj-$(CONFIG_IDE)              += ide/
 obj-$(CONFIG_SCSI)             += scsi/
 obj-$(CONFIG_ATA)              += ata/
+obj-$(CONFIG_TARGET_CORE)      += target/
 obj-$(CONFIG_MTD)              += mtd/
 obj-$(CONFIG_SPI)              += spi/
 obj-y                          += net/
diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig
new file mode 100644 (file)
index 0000000..2fac3be
--- /dev/null
@@ -0,0 +1,32 @@
+
+menuconfig TARGET_CORE
+       tristate "Generic Target Core Mod (TCM) and ConfigFS Infrastructure"
+       depends on SCSI && BLOCK
+       select CONFIGFS_FS
+       default n
+       help
+       Say Y or M here to enable the TCM Storage Engine and ConfigFS enabled
+       control path for target_core_mod.  This includes built-in TCM RAMDISK
+       subsystem logic for virtual LUN 0 access
+
+if TARGET_CORE
+
+config TCM_IBLOCK
+       tristate "TCM/IBLOCK Subsystem Plugin for Linux/BLOCK"
+       help
+       Say Y here to enable the TCM/IBLOCK subsystem plugin for non-buffered
+       access to Linux/Block devices using BIO
+
+config TCM_FILEIO
+       tristate "TCM/FILEIO Subsystem Plugin for Linux/VFS"
+       help
+       Say Y here to enable the TCM/FILEIO subsystem plugin for buffered
+       access to Linux/VFS struct file or struct block_device
+
+config TCM_PSCSI
+       tristate "TCM/pSCSI Subsystem Plugin for Linux/SCSI"
+       help
+       Say Y here to enable the TCM/pSCSI subsystem plugin for non-buffered
+       passthrough access to Linux/SCSI device
+
+endif
diff --git a/drivers/target/Makefile b/drivers/target/Makefile
new file mode 100644 (file)
index 0000000..5cfd708
--- /dev/null
@@ -0,0 +1,24 @@
+EXTRA_CFLAGS += -I$(srctree)/drivers/target/ -I$(srctree)/drivers/scsi/
+
+target_core_mod-y              := target_core_configfs.o \
+                                  target_core_device.o \
+                                  target_core_fabric_configfs.o \
+                                  target_core_fabric_lib.o \
+                                  target_core_hba.o \
+                                  target_core_pr.o \
+                                  target_core_alua.o \
+                                  target_core_scdb.o \
+                                  target_core_tmr.o \
+                                  target_core_tpg.o \
+                                  target_core_transport.o \
+                                  target_core_cdb.o \
+                                  target_core_ua.o \
+                                  target_core_rd.o \
+                                  target_core_mib.o
+
+obj-$(CONFIG_TARGET_CORE)      += target_core_mod.o
+
+# Subsystem modules
+obj-$(CONFIG_TCM_IBLOCK)       += target_core_iblock.o
+obj-$(CONFIG_TCM_FILEIO)       += target_core_file.o
+obj-$(CONFIG_TCM_PSCSI)                += target_core_pscsi.o
diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c
new file mode 100644 (file)
index 0000000..2c5fcfe
--- /dev/null
@@ -0,0 +1,1991 @@
+/*******************************************************************************
+ * Filename:  target_core_alua.c
+ *
+ * This file contains SPC-3 compliant asymmetric logical unit assigntment (ALUA)
+ *
+ * Copyright (c) 2009-2010 Rising Tide Systems
+ * Copyright (c) 2009-2010 Linux-iSCSI.org
+ *
+ * Nicholas A. Bellinger <nab@kernel.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/version.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/configfs.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_device.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+#include <target/target_core_configfs.h>
+
+#include "target_core_alua.h"
+#include "target_core_hba.h"
+#include "target_core_ua.h"
+
+static int core_alua_check_transition(int state, int *primary);
+static int core_alua_set_tg_pt_secondary_state(
+               struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem,
+               struct se_port *port, int explict, int offline);
+
+/*
+ * REPORT_TARGET_PORT_GROUPS
+ *
+ * See spc4r17 section 6.27
+ */
+int core_emulate_report_target_port_groups(struct se_cmd *cmd)
+{
+       struct se_subsystem_dev *su_dev = SE_DEV(cmd)->se_sub_dev;
+       struct se_port *port;
+       struct t10_alua_tg_pt_gp *tg_pt_gp;
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;
+       unsigned char *buf = (unsigned char *)T_TASK(cmd)->t_task_buf;
+       u32 rd_len = 0, off = 4; /* Skip over RESERVED area to first
+                                   Target port group descriptor */
+
+       spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+       list_for_each_entry(tg_pt_gp, &T10_ALUA(su_dev)->tg_pt_gps_list,
+                       tg_pt_gp_list) {
+               /*
+                * PREF: Preferred target port bit, determine if this
+                * bit should be set for port group.
+                */
+               if (tg_pt_gp->tg_pt_gp_pref)
+                       buf[off] = 0x80;
+               /*
+                * Set the ASYMMETRIC ACCESS State
+                */
+               buf[off++] |= (atomic_read(
+                       &tg_pt_gp->tg_pt_gp_alua_access_state) & 0xff);
+               /*
+                * Set supported ASYMMETRIC ACCESS State bits
+                */
+               buf[off] = 0x80; /* T_SUP */
+               buf[off] |= 0x40; /* O_SUP */
+               buf[off] |= 0x8; /* U_SUP */
+               buf[off] |= 0x4; /* S_SUP */
+               buf[off] |= 0x2; /* AN_SUP */
+               buf[off++] |= 0x1; /* AO_SUP */
+               /*
+                * TARGET PORT GROUP
+                */
+               buf[off++] = ((tg_pt_gp->tg_pt_gp_id >> 8) & 0xff);
+               buf[off++] = (tg_pt_gp->tg_pt_gp_id & 0xff);
+
+               off++; /* Skip over Reserved */
+               /*
+                * STATUS CODE
+                */
+               buf[off++] = (tg_pt_gp->tg_pt_gp_alua_access_status & 0xff);
+               /*
+                * Vendor Specific field
+                */
+               buf[off++] = 0x00;
+               /*
+                * TARGET PORT COUNT
+                */
+               buf[off++] = (tg_pt_gp->tg_pt_gp_members & 0xff);
+               rd_len += 8;
+
+               spin_lock(&tg_pt_gp->tg_pt_gp_lock);
+               list_for_each_entry(tg_pt_gp_mem, &tg_pt_gp->tg_pt_gp_mem_list,
+                               tg_pt_gp_mem_list) {
+                       port = tg_pt_gp_mem->tg_pt;
+                       /*
+                        * Start Target Port descriptor format
+                        *
+                        * See spc4r17 section 6.2.7 Table 247
+                        */
+                       off += 2; /* Skip over Obsolete */
+                       /*
+                        * Set RELATIVE TARGET PORT IDENTIFIER
+                        */
+                       buf[off++] = ((port->sep_rtpi >> 8) & 0xff);
+                       buf[off++] = (port->sep_rtpi & 0xff);
+                       rd_len += 4;
+               }
+               spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
+       }
+       spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+       /*
+        * Set the RETURN DATA LENGTH set in the header of the DataIN Payload
+        */
+       buf[0] = ((rd_len >> 24) & 0xff);
+       buf[1] = ((rd_len >> 16) & 0xff);
+       buf[2] = ((rd_len >> 8) & 0xff);
+       buf[3] = (rd_len & 0xff);
+
+       return 0;
+}
+
+/*
+ * SET_TARGET_PORT_GROUPS for explict ALUA operation.
+ *
+ * See spc4r17 section 6.35
+ */
+int core_emulate_set_target_port_groups(struct se_cmd *cmd)
+{
+       struct se_device *dev = SE_DEV(cmd);
+       struct se_subsystem_dev *su_dev = SE_DEV(cmd)->se_sub_dev;
+       struct se_port *port, *l_port = SE_LUN(cmd)->lun_sep;
+       struct se_node_acl *nacl = SE_SESS(cmd)->se_node_acl;
+       struct t10_alua_tg_pt_gp *tg_pt_gp = NULL, *l_tg_pt_gp;
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem, *l_tg_pt_gp_mem;
+       unsigned char *buf = (unsigned char *)T_TASK(cmd)->t_task_buf;
+       unsigned char *ptr = &buf[4]; /* Skip over RESERVED area in header */
+       u32 len = 4; /* Skip over RESERVED area in header */
+       int alua_access_state, primary = 0, rc;
+       u16 tg_pt_id, rtpi;
+
+       if (!(l_port))
+               return PYX_TRANSPORT_LU_COMM_FAILURE;
+       /*
+        * Determine if explict ALUA via SET_TARGET_PORT_GROUPS is allowed
+        * for the local tg_pt_gp.
+        */
+       l_tg_pt_gp_mem = l_port->sep_alua_tg_pt_gp_mem;
+       if (!(l_tg_pt_gp_mem)) {
+               printk(KERN_ERR "Unable to access l_port->sep_alua_tg_pt_gp_mem\n");
+               return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+       }
+       spin_lock(&l_tg_pt_gp_mem->tg_pt_gp_mem_lock);
+       l_tg_pt_gp = l_tg_pt_gp_mem->tg_pt_gp;
+       if (!(l_tg_pt_gp)) {
+               spin_unlock(&l_tg_pt_gp_mem->tg_pt_gp_mem_lock);
+               printk(KERN_ERR "Unable to access *l_tg_pt_gp_mem->tg_pt_gp\n");
+               return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+       }
+       rc = (l_tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICT_ALUA);
+       spin_unlock(&l_tg_pt_gp_mem->tg_pt_gp_mem_lock);
+
+       if (!(rc)) {
+               printk(KERN_INFO "Unable to process SET_TARGET_PORT_GROUPS"
+                               " while TPGS_EXPLICT_ALUA is disabled\n");
+               return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+       }
+
+       while (len < cmd->data_length) {
+               alua_access_state = (ptr[0] & 0x0f);
+               /*
+                * Check the received ALUA access state, and determine if
+                * the state is a primary or secondary target port asymmetric
+                * access state.
+                */
+               rc = core_alua_check_transition(alua_access_state, &primary);
+               if (rc != 0) {
+                       /*
+                        * If the SET TARGET PORT GROUPS attempts to establish
+                        * an invalid combination of target port asymmetric
+                        * access states or attempts to establish an
+                        * unsupported target port asymmetric access state,
+                        * then the command shall be terminated with CHECK
+                        * CONDITION status, with the sense key set to ILLEGAL
+                        * REQUEST, and the additional sense code set to INVALID
+                        * FIELD IN PARAMETER LIST.
+                        */
+                       return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+               }
+               rc = -1;
+               /*
+                * If the ASYMMETRIC ACCESS STATE field (see table 267)
+                * specifies a primary target port asymmetric access state,
+                * then the TARGET PORT GROUP OR TARGET PORT field specifies
+                * a primary target port group for which the primary target
+                * port asymmetric access state shall be changed. If the
+                * ASYMMETRIC ACCESS STATE field specifies a secondary target
+                * port asymmetric access state, then the TARGET PORT GROUP OR
+                * TARGET PORT field specifies the relative target port
+                * identifier (see 3.1.120) of the target port for which the
+                * secondary target port asymmetric access state shall be
+                * changed.
+                */
+               if (primary) {
+                       tg_pt_id = ((ptr[2] << 8) & 0xff);
+                       tg_pt_id |= (ptr[3] & 0xff);
+                       /*
+                        * Locate the matching target port group ID from
+                        * the global tg_pt_gp list
+                        */
+                       spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+                       list_for_each_entry(tg_pt_gp,
+                                       &T10_ALUA(su_dev)->tg_pt_gps_list,
+                                       tg_pt_gp_list) {
+                               if (!(tg_pt_gp->tg_pt_gp_valid_id))
+                                       continue;
+
+                               if (tg_pt_id != tg_pt_gp->tg_pt_gp_id)
+                                       continue;
+
+                               atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt);
+                               smp_mb__after_atomic_inc();
+                               spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+
+                               rc = core_alua_do_port_transition(tg_pt_gp,
+                                               dev, l_port, nacl,
+                                               alua_access_state, 1);
+
+                               spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+                               atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt);
+                               smp_mb__after_atomic_dec();
+                               break;
+                       }
+                       spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+                       /*
+                        * If not matching target port group ID can be located
+                        * throw an exception with ASCQ: INVALID_PARAMETER_LIST
+                        */
+                       if (rc != 0)
+                               return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+               } else {
+                       /*
+                        * Extact the RELATIVE TARGET PORT IDENTIFIER to identify
+                        * the Target Port in question for the the incoming
+                        * SET_TARGET_PORT_GROUPS op.
+                        */
+                       rtpi = ((ptr[2] << 8) & 0xff);
+                       rtpi |= (ptr[3] & 0xff);
+                       /*
+                        * Locate the matching relative target port identifer
+                        * for the struct se_device storage object.
+                        */
+                       spin_lock(&dev->se_port_lock);
+                       list_for_each_entry(port, &dev->dev_sep_list,
+                                                       sep_list) {
+                               if (port->sep_rtpi != rtpi)
+                                       continue;
+
+                               tg_pt_gp_mem = port->sep_alua_tg_pt_gp_mem;
+                               spin_unlock(&dev->se_port_lock);
+
+                               rc = core_alua_set_tg_pt_secondary_state(
+                                               tg_pt_gp_mem, port, 1, 1);
+
+                               spin_lock(&dev->se_port_lock);
+                               break;
+                       }
+                       spin_unlock(&dev->se_port_lock);
+                       /*
+                        * If not matching relative target port identifier can
+                        * be located, throw an exception with ASCQ:
+                        * INVALID_PARAMETER_LIST
+                        */
+                       if (rc != 0)
+                               return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+               }
+
+               ptr += 4;
+               len += 4;
+       }
+
+       return 0;
+}
+
+static inline int core_alua_state_nonoptimized(
+       struct se_cmd *cmd,
+       unsigned char *cdb,
+       int nonop_delay_msecs,
+       u8 *alua_ascq)
+{
+       /*
+        * Set SCF_ALUA_NON_OPTIMIZED here, this value will be checked
+        * later to determine if processing of this cmd needs to be
+        * temporarily delayed for the Active/NonOptimized primary access state.
+        */
+       cmd->se_cmd_flags |= SCF_ALUA_NON_OPTIMIZED;
+       cmd->alua_nonop_delay = nonop_delay_msecs;
+       return 0;
+}
+
+static inline int core_alua_state_standby(
+       struct se_cmd *cmd,
+       unsigned char *cdb,
+       u8 *alua_ascq)
+{
+       /*
+        * Allowed CDBs for ALUA_ACCESS_STATE_STANDBY as defined by
+        * spc4r17 section 5.9.2.4.4
+        */
+       switch (cdb[0]) {
+       case INQUIRY:
+       case LOG_SELECT:
+       case LOG_SENSE:
+       case MODE_SELECT:
+       case MODE_SENSE:
+       case REPORT_LUNS:
+       case RECEIVE_DIAGNOSTIC:
+       case SEND_DIAGNOSTIC:
+       case MAINTENANCE_IN:
+               switch (cdb[1]) {
+               case MI_REPORT_TARGET_PGS:
+                       return 0;
+               default:
+                       *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY;
+                       return 1;
+               }
+       case MAINTENANCE_OUT:
+               switch (cdb[1]) {
+               case MO_SET_TARGET_PGS:
+                       return 0;
+               default:
+                       *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY;
+                       return 1;
+               }
+       case REQUEST_SENSE:
+       case PERSISTENT_RESERVE_IN:
+       case PERSISTENT_RESERVE_OUT:
+       case READ_BUFFER:
+       case WRITE_BUFFER:
+               return 0;
+       default:
+               *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY;
+               return 1;
+       }
+
+       return 0;
+}
+
+static inline int core_alua_state_unavailable(
+       struct se_cmd *cmd,
+       unsigned char *cdb,
+       u8 *alua_ascq)
+{
+       /*
+        * Allowed CDBs for ALUA_ACCESS_STATE_UNAVAILABLE as defined by
+        * spc4r17 section 5.9.2.4.5
+        */
+       switch (cdb[0]) {
+       case INQUIRY:
+       case REPORT_LUNS:
+       case MAINTENANCE_IN:
+               switch (cdb[1]) {
+               case MI_REPORT_TARGET_PGS:
+                       return 0;
+               default:
+                       *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
+                       return 1;
+               }
+       case MAINTENANCE_OUT:
+               switch (cdb[1]) {
+               case MO_SET_TARGET_PGS:
+                       return 0;
+               default:
+                       *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
+                       return 1;
+               }
+       case REQUEST_SENSE:
+       case READ_BUFFER:
+       case WRITE_BUFFER:
+               return 0;
+       default:
+               *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
+               return 1;
+       }
+
+       return 0;
+}
+
+static inline int core_alua_state_transition(
+       struct se_cmd *cmd,
+       unsigned char *cdb,
+       u8 *alua_ascq)
+{
+       /*
+        * Allowed CDBs for ALUA_ACCESS_STATE_TRANSITIO as defined by
+        * spc4r17 section 5.9.2.5
+        */
+       switch (cdb[0]) {
+       case INQUIRY:
+       case REPORT_LUNS:
+       case MAINTENANCE_IN:
+               switch (cdb[1]) {
+               case MI_REPORT_TARGET_PGS:
+                       return 0;
+               default:
+                       *alua_ascq = ASCQ_04H_ALUA_STATE_TRANSITION;
+                       return 1;
+               }
+       case REQUEST_SENSE:
+       case READ_BUFFER:
+       case WRITE_BUFFER:
+               return 0;
+       default:
+               *alua_ascq = ASCQ_04H_ALUA_STATE_TRANSITION;
+               return 1;
+       }
+
+       return 0;
+}
+
+/*
+ * Used for alua_type SPC_ALUA_PASSTHROUGH and SPC2_ALUA_DISABLED
+ * in transport_cmd_sequencer().  This function is assigned to
+ * struct t10_alua *->state_check() in core_setup_alua()
+ */
+static int core_alua_state_check_nop(
+       struct se_cmd *cmd,
+       unsigned char *cdb,
+       u8 *alua_ascq)
+{
+       return 0;
+}
+
+/*
+ * Used for alua_type SPC3_ALUA_EMULATED in transport_cmd_sequencer().
+ * This function is assigned to struct t10_alua *->state_check() in
+ * core_setup_alua()
+ *
+ * Also, this function can return three different return codes to
+ * signal transport_generic_cmd_sequencer()
+ *
+ * return 1: Is used to signal LUN not accecsable, and check condition/not ready
+ * return 0: Used to signal success
+ * reutrn -1: Used to signal failure, and invalid cdb field
+ */
+static int core_alua_state_check(
+       struct se_cmd *cmd,
+       unsigned char *cdb,
+       u8 *alua_ascq)
+{
+       struct se_lun *lun = SE_LUN(cmd);
+       struct se_port *port = lun->lun_sep;
+       struct t10_alua_tg_pt_gp *tg_pt_gp;
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;
+       int out_alua_state, nonop_delay_msecs;
+
+       if (!(port))
+               return 0;
+       /*
+        * First, check for a struct se_port specific secondary ALUA target port
+        * access state: OFFLINE
+        */
+       if (atomic_read(&port->sep_tg_pt_secondary_offline)) {
+               *alua_ascq = ASCQ_04H_ALUA_OFFLINE;
+               printk(KERN_INFO "ALUA: Got secondary offline status for local"
+                               " target port\n");
+               *alua_ascq = ASCQ_04H_ALUA_OFFLINE;
+               return 1;
+       }
+        /*
+        * Second, obtain the struct t10_alua_tg_pt_gp_member pointer to the
+        * ALUA target port group, to obtain current ALUA access state.
+        * Otherwise look for the underlying struct se_device association with
+        * a ALUA logical unit group.
+        */
+       tg_pt_gp_mem = port->sep_alua_tg_pt_gp_mem;
+       spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+       tg_pt_gp = tg_pt_gp_mem->tg_pt_gp;
+       out_alua_state = atomic_read(&tg_pt_gp->tg_pt_gp_alua_access_state);
+       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.
+        * For the Optimized ALUA access state case, we want to process the
+        * incoming fabric cmd ASAP..
+        */
+       if (out_alua_state == ALUA_ACCESS_STATE_ACTIVE_OPTMIZED)
+               return 0;
+
+       switch (out_alua_state) {
+       case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED:
+               return core_alua_state_nonoptimized(cmd, cdb,
+                                       nonop_delay_msecs, alua_ascq);
+       case ALUA_ACCESS_STATE_STANDBY:
+               return core_alua_state_standby(cmd, cdb, alua_ascq);
+       case ALUA_ACCESS_STATE_UNAVAILABLE:
+               return core_alua_state_unavailable(cmd, cdb, alua_ascq);
+       case ALUA_ACCESS_STATE_TRANSITION:
+               return core_alua_state_transition(cmd, cdb, alua_ascq);
+       /*
+        * OFFLINE is a secondary ALUA target port group access state, that is
+        * handled above with struct se_port->sep_tg_pt_secondary_offline=1
+        */
+       case ALUA_ACCESS_STATE_OFFLINE:
+       default:
+               printk(KERN_ERR "Unknown ALUA access state: 0x%02x\n",
+                               out_alua_state);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * Check implict and explict ALUA state change request.
+ */
+static int core_alua_check_transition(int state, int *primary)
+{
+       switch (state) {
+       case ALUA_ACCESS_STATE_ACTIVE_OPTMIZED:
+       case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED:
+       case ALUA_ACCESS_STATE_STANDBY:
+       case ALUA_ACCESS_STATE_UNAVAILABLE:
+               /*
+                * OPTIMIZED, NON-OPTIMIZED, STANDBY and UNAVAILABLE are
+                * defined as primary target port asymmetric access states.
+                */
+               *primary = 1;
+               break;
+       case ALUA_ACCESS_STATE_OFFLINE:
+               /*
+                * OFFLINE state is defined as a secondary target port
+                * asymmetric access state.
+                */
+               *primary = 0;
+               break;
+       default:
+               printk(KERN_ERR "Unknown ALUA access state: 0x%02x\n", state);
+               return -1;
+       }
+
+       return 0;
+}
+
+static char *core_alua_dump_state(int state)
+{
+       switch (state) {
+       case ALUA_ACCESS_STATE_ACTIVE_OPTMIZED:
+               return "Active/Optimized";
+       case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED:
+               return "Active/NonOptimized";
+       case ALUA_ACCESS_STATE_STANDBY:
+               return "Standby";
+       case ALUA_ACCESS_STATE_UNAVAILABLE:
+               return "Unavailable";
+       case ALUA_ACCESS_STATE_OFFLINE:
+               return "Offline";
+       default:
+               return "Unknown";
+       }
+
+       return NULL;
+}
+
+char *core_alua_dump_status(int status)
+{
+       switch (status) {
+       case ALUA_STATUS_NONE:
+               return "None";
+       case ALUA_STATUS_ALTERED_BY_EXPLICT_STPG:
+               return "Altered by Explict STPG";
+       case ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA:
+               return "Altered by Implict ALUA";
+       default:
+               return "Unknown";
+       }
+
+       return NULL;
+}
+
+/*
+ * Used by fabric modules to determine when we need to delay processing
+ * for the Active/NonOptimized paths..
+ */
+int core_alua_check_nonop_delay(
+       struct se_cmd *cmd)
+{
+       if (!(cmd->se_cmd_flags & SCF_ALUA_NON_OPTIMIZED))
+               return 0;
+       if (in_interrupt())
+               return 0;
+       /*
+        * The ALUA Active/NonOptimized access state delay can be disabled
+        * in via configfs with a value of zero
+        */
+       if (!(cmd->alua_nonop_delay))
+               return 0;
+       /*
+        * struct se_cmd->alua_nonop_delay gets set by a target port group
+        * defined interval in core_alua_state_nonoptimized()
+        */
+       msleep_interruptible(cmd->alua_nonop_delay);
+       return 0;
+}
+EXPORT_SYMBOL(core_alua_check_nonop_delay);
+
+/*
+ * Called with tg_pt_gp->tg_pt_gp_md_mutex or tg_pt_gp_mem->sep_tg_pt_md_mutex
+ *
+ */
+static int core_alua_write_tpg_metadata(
+       const char *path,
+       unsigned char *md_buf,
+       u32 md_buf_len)
+{
+       mm_segment_t old_fs;
+       struct file *file;
+       struct iovec iov[1];
+       int flags = O_RDWR | O_CREAT | O_TRUNC, ret;
+
+       memset(iov, 0, sizeof(struct iovec));
+
+       file = filp_open(path, flags, 0600);
+       if (IS_ERR(file) || !file || !file->f_dentry) {
+               printk(KERN_ERR "filp_open(%s) for ALUA metadata failed\n",
+                       path);
+               return -ENODEV;
+       }
+
+       iov[0].iov_base = &md_buf[0];
+       iov[0].iov_len = md_buf_len;
+
+       old_fs = get_fs();
+       set_fs(get_ds());
+       ret = vfs_writev(file, &iov[0], 1, &file->f_pos);
+       set_fs(old_fs);
+
+       if (ret < 0) {
+               printk(KERN_ERR "Error writing ALUA metadata file: %s\n", path);
+               filp_close(file, NULL);
+               return -EIO;
+       }
+       filp_close(file, NULL);
+
+       return 0;
+}
+
+/*
+ * Called with tg_pt_gp->tg_pt_gp_md_mutex held
+ */
+static int core_alua_update_tpg_primary_metadata(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       int primary_state,
+       unsigned char *md_buf)
+{
+       struct se_subsystem_dev *su_dev = tg_pt_gp->tg_pt_gp_su_dev;
+       struct t10_wwn *wwn = &su_dev->t10_wwn;
+       char path[ALUA_METADATA_PATH_LEN];
+       int len;
+
+       memset(path, 0, ALUA_METADATA_PATH_LEN);
+
+       len = snprintf(md_buf, tg_pt_gp->tg_pt_gp_md_buf_len,
+                       "tg_pt_gp_id=%hu\n"
+                       "alua_access_state=0x%02x\n"
+                       "alua_access_status=0x%02x\n",
+                       tg_pt_gp->tg_pt_gp_id, primary_state,
+                       tg_pt_gp->tg_pt_gp_alua_access_status);
+
+       snprintf(path, ALUA_METADATA_PATH_LEN,
+               "/var/target/alua/tpgs_%s/%s", &wwn->unit_serial[0],
+               config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item));
+
+       return core_alua_write_tpg_metadata(path, md_buf, len);
+}
+
+static int core_alua_do_transition_tg_pt(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       struct se_port *l_port,
+       struct se_node_acl *nacl,
+       unsigned char *md_buf,
+       int new_state,
+       int explict)
+{
+       struct se_dev_entry *se_deve;
+       struct se_lun_acl *lacl;
+       struct se_port *port;
+       struct t10_alua_tg_pt_gp_member *mem;
+       int old_state = 0;
+       /*
+        * Save the old primary ALUA access state, and set the current state
+        * to ALUA_ACCESS_STATE_TRANSITION.
+        */
+       old_state = atomic_read(&tg_pt_gp->tg_pt_gp_alua_access_state);
+       atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state,
+                       ALUA_ACCESS_STATE_TRANSITION);
+       tg_pt_gp->tg_pt_gp_alua_access_status = (explict) ?
+                               ALUA_STATUS_ALTERED_BY_EXPLICT_STPG :
+                               ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA;
+       /*
+        * Check for the optional ALUA primary state transition delay
+        */
+       if (tg_pt_gp->tg_pt_gp_trans_delay_msecs != 0)
+               msleep_interruptible(tg_pt_gp->tg_pt_gp_trans_delay_msecs);
+
+       spin_lock(&tg_pt_gp->tg_pt_gp_lock);
+       list_for_each_entry(mem, &tg_pt_gp->tg_pt_gp_mem_list,
+                               tg_pt_gp_mem_list) {
+               port = mem->tg_pt;
+               /*
+                * After an implicit target port asymmetric access state
+                * change, a device server shall establish a unit attention
+                * condition for the initiator port associated with every I_T
+                * nexus with the additional sense code set to ASYMMETRIC
+                * ACCESS STATE CHAGED.
+                *
+                * After an explicit target port asymmetric access state
+                * change, a device server shall establish a unit attention
+                * condition with the additional sense code set to ASYMMETRIC
+                * ACCESS STATE CHANGED for the initiator port associated with
+                * every I_T nexus other than the I_T nexus on which the SET
+                * TARGET PORT GROUPS command
+                */
+               atomic_inc(&mem->tg_pt_gp_mem_ref_cnt);
+               smp_mb__after_atomic_inc();
+               spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
+
+               spin_lock_bh(&port->sep_alua_lock);
+               list_for_each_entry(se_deve, &port->sep_alua_list,
+                                       alua_port_list) {
+                       lacl = se_deve->se_lun_acl;
+                       /*
+                        * se_deve->se_lun_acl pointer may be NULL for a
+                        * entry created without explict Node+MappedLUN ACLs
+                        */
+                       if (!(lacl))
+                               continue;
+
+                       if (explict &&
+                          (nacl != NULL) && (nacl == lacl->se_lun_nacl) &&
+                          (l_port != NULL) && (l_port == port))
+                               continue;
+
+                       core_scsi3_ua_allocate(lacl->se_lun_nacl,
+                               se_deve->mapped_lun, 0x2A,
+                               ASCQ_2AH_ASYMMETRIC_ACCESS_STATE_CHANGED);
+               }
+               spin_unlock_bh(&port->sep_alua_lock);
+
+               spin_lock(&tg_pt_gp->tg_pt_gp_lock);
+               atomic_dec(&mem->tg_pt_gp_mem_ref_cnt);
+               smp_mb__after_atomic_dec();
+       }
+       spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
+       /*
+        * Update the ALUA metadata buf that has been allocated in
+        * core_alua_do_port_transition(), this metadata will be written
+        * to struct file.
+        *
+        * Note that there is the case where we do not want to update the
+        * metadata when the saved metadata is being parsed in userspace
+        * when setting the existing port access state and access status.
+        *
+        * Also note that the failure to write out the ALUA metadata to
+        * struct file does NOT affect the actual ALUA transition.
+        */
+       if (tg_pt_gp->tg_pt_gp_write_metadata) {
+               mutex_lock(&tg_pt_gp->tg_pt_gp_md_mutex);
+               core_alua_update_tpg_primary_metadata(tg_pt_gp,
+                                       new_state, md_buf);
+               mutex_unlock(&tg_pt_gp->tg_pt_gp_md_mutex);
+       }
+       /*
+        * Set the current primary ALUA access state to the requested new state
+        */
+       atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state, new_state);
+
+       printk(KERN_INFO "Successful %s ALUA transition TG PT Group: %s ID: %hu"
+               " from primary access state %s to %s\n", (explict) ? "explict" :
+               "implict", config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item),
+               tg_pt_gp->tg_pt_gp_id, core_alua_dump_state(old_state),
+               core_alua_dump_state(new_state));
+
+       return 0;
+}
+
+int core_alua_do_port_transition(
+       struct t10_alua_tg_pt_gp *l_tg_pt_gp,
+       struct se_device *l_dev,
+       struct se_port *l_port,
+       struct se_node_acl *l_nacl,
+       int new_state,
+       int explict)
+{
+       struct se_device *dev;
+       struct se_port *port;
+       struct se_subsystem_dev *su_dev;
+       struct se_node_acl *nacl;
+       struct t10_alua_lu_gp *lu_gp;
+       struct t10_alua_lu_gp_member *lu_gp_mem, *local_lu_gp_mem;
+       struct t10_alua_tg_pt_gp *tg_pt_gp;
+       unsigned char *md_buf;
+       int primary;
+
+       if (core_alua_check_transition(new_state, &primary) != 0)
+               return -EINVAL;
+
+       md_buf = kzalloc(l_tg_pt_gp->tg_pt_gp_md_buf_len, GFP_KERNEL);
+       if (!(md_buf)) {
+               printk("Unable to allocate buf for ALUA metadata\n");
+               return -ENOMEM;
+       }
+
+       local_lu_gp_mem = l_dev->dev_alua_lu_gp_mem;
+       spin_lock(&local_lu_gp_mem->lu_gp_mem_lock);
+       lu_gp = local_lu_gp_mem->lu_gp;
+       atomic_inc(&lu_gp->lu_gp_ref_cnt);
+       smp_mb__after_atomic_inc();
+       spin_unlock(&local_lu_gp_mem->lu_gp_mem_lock);
+       /*
+        * For storage objects that are members of the 'default_lu_gp',
+        * we only do transition on the passed *l_tp_pt_gp, and not
+        * on all of the matching target port groups IDs in default_lu_gp.
+        */
+       if (!(lu_gp->lu_gp_id)) {
+               /*
+                * core_alua_do_transition_tg_pt() will always return
+                * success.
+                */
+               core_alua_do_transition_tg_pt(l_tg_pt_gp, l_port, l_nacl,
+                                       md_buf, new_state, explict);
+               atomic_dec(&lu_gp->lu_gp_ref_cnt);
+               smp_mb__after_atomic_dec();
+               kfree(md_buf);
+               return 0;
+       }
+       /*
+        * For all other LU groups aside from 'default_lu_gp', walk all of
+        * the associated storage objects looking for a matching target port
+        * group ID from the local target port group.
+        */
+       spin_lock(&lu_gp->lu_gp_lock);
+       list_for_each_entry(lu_gp_mem, &lu_gp->lu_gp_mem_list,
+                               lu_gp_mem_list) {
+
+               dev = lu_gp_mem->lu_gp_mem_dev;
+               su_dev = dev->se_sub_dev;
+               atomic_inc(&lu_gp_mem->lu_gp_mem_ref_cnt);
+               smp_mb__after_atomic_inc();
+               spin_unlock(&lu_gp->lu_gp_lock);
+
+               spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+               list_for_each_entry(tg_pt_gp,
+                               &T10_ALUA(su_dev)->tg_pt_gps_list,
+                               tg_pt_gp_list) {
+
+                       if (!(tg_pt_gp->tg_pt_gp_valid_id))
+                               continue;
+                       /*
+                        * If the target behavior port asymmetric access state
+                        * is changed for any target port group accessiable via
+                        * a logical unit within a LU group, the target port
+                        * behavior group asymmetric access states for the same
+                        * target port group accessible via other logical units
+                        * in that LU group will also change.
+                        */
+                       if (l_tg_pt_gp->tg_pt_gp_id != tg_pt_gp->tg_pt_gp_id)
+                               continue;
+
+                       if (l_tg_pt_gp == tg_pt_gp) {
+                               port = l_port;
+                               nacl = l_nacl;
+                       } else {
+                               port = NULL;
+                               nacl = NULL;
+                       }
+                       atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt);
+                       smp_mb__after_atomic_inc();
+                       spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+                       /*
+                        * core_alua_do_transition_tg_pt() will always return
+                        * success.
+                        */
+                       core_alua_do_transition_tg_pt(tg_pt_gp, port,
+                                       nacl, md_buf, new_state, explict);
+
+                       spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+                       atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt);
+                       smp_mb__after_atomic_dec();
+               }
+               spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+
+               spin_lock(&lu_gp->lu_gp_lock);
+               atomic_dec(&lu_gp_mem->lu_gp_mem_ref_cnt);
+               smp_mb__after_atomic_dec();
+       }
+       spin_unlock(&lu_gp->lu_gp_lock);
+
+       printk(KERN_INFO "Successfully processed LU Group: %s all ALUA TG PT"
+               " Group IDs: %hu %s transition to primary state: %s\n",
+               config_item_name(&lu_gp->lu_gp_group.cg_item),
+               l_tg_pt_gp->tg_pt_gp_id, (explict) ? "explict" : "implict",
+               core_alua_dump_state(new_state));
+
+       atomic_dec(&lu_gp->lu_gp_ref_cnt);
+       smp_mb__after_atomic_dec();
+       kfree(md_buf);
+       return 0;
+}
+
+/*
+ * Called with tg_pt_gp_mem->sep_tg_pt_md_mutex held
+ */
+static int core_alua_update_tpg_secondary_metadata(
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem,
+       struct se_port *port,
+       unsigned char *md_buf,
+       u32 md_buf_len)
+{
+       struct se_portal_group *se_tpg = port->sep_tpg;
+       char path[ALUA_METADATA_PATH_LEN], wwn[ALUA_SECONDARY_METADATA_WWN_LEN];
+       int len;
+
+       memset(path, 0, ALUA_METADATA_PATH_LEN);
+       memset(wwn, 0, ALUA_SECONDARY_METADATA_WWN_LEN);
+
+       len = snprintf(wwn, ALUA_SECONDARY_METADATA_WWN_LEN, "%s",
+                       TPG_TFO(se_tpg)->tpg_get_wwn(se_tpg));
+
+       if (TPG_TFO(se_tpg)->tpg_get_tag != NULL)
+               snprintf(wwn+len, ALUA_SECONDARY_METADATA_WWN_LEN-len, "+%hu",
+                               TPG_TFO(se_tpg)->tpg_get_tag(se_tpg));
+
+       len = snprintf(md_buf, md_buf_len, "alua_tg_pt_offline=%d\n"
+                       "alua_tg_pt_status=0x%02x\n",
+                       atomic_read(&port->sep_tg_pt_secondary_offline),
+                       port->sep_tg_pt_secondary_stat);
+
+       snprintf(path, ALUA_METADATA_PATH_LEN, "/var/target/alua/%s/%s/lun_%u",
+                       TPG_TFO(se_tpg)->get_fabric_name(), wwn,
+                       port->sep_lun->unpacked_lun);
+
+       return core_alua_write_tpg_metadata(path, md_buf, len);
+}
+
+static int core_alua_set_tg_pt_secondary_state(
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem,
+       struct se_port *port,
+       int explict,
+       int offline)
+{
+       struct t10_alua_tg_pt_gp *tg_pt_gp;
+       unsigned char *md_buf;
+       u32 md_buf_len;
+       int trans_delay_msecs;
+
+       spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+       tg_pt_gp = tg_pt_gp_mem->tg_pt_gp;
+       if (!(tg_pt_gp)) {
+               spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+               printk(KERN_ERR "Unable to complete secondary state"
+                               " transition\n");
+               return -1;
+       }
+       trans_delay_msecs = tg_pt_gp->tg_pt_gp_trans_delay_msecs;
+       /*
+        * Set the secondary ALUA target port access state to OFFLINE
+        * or release the previously secondary state for struct se_port
+        */
+       if (offline)
+               atomic_set(&port->sep_tg_pt_secondary_offline, 1);
+       else
+               atomic_set(&port->sep_tg_pt_secondary_offline, 0);
+
+       md_buf_len = tg_pt_gp->tg_pt_gp_md_buf_len;
+       port->sep_tg_pt_secondary_stat = (explict) ?
+                       ALUA_STATUS_ALTERED_BY_EXPLICT_STPG :
+                       ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA;
+
+       printk(KERN_INFO "Successful %s ALUA transition TG PT Group: %s ID: %hu"
+               " to secondary access state: %s\n", (explict) ? "explict" :
+               "implict", config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item),
+               tg_pt_gp->tg_pt_gp_id, (offline) ? "OFFLINE" : "ONLINE");
+
+       spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+       /*
+        * Do the optional transition delay after we set the secondary
+        * ALUA access state.
+        */
+       if (trans_delay_msecs != 0)
+               msleep_interruptible(trans_delay_msecs);
+       /*
+        * See if we need to update the ALUA fabric port metadata for
+        * secondary state and status
+        */
+       if (port->sep_tg_pt_secondary_write_md) {
+               md_buf = kzalloc(md_buf_len, GFP_KERNEL);
+               if (!(md_buf)) {
+                       printk(KERN_ERR "Unable to allocate md_buf for"
+                               " secondary ALUA access metadata\n");
+                       return -1;
+               }
+               mutex_lock(&port->sep_tg_pt_md_mutex);
+               core_alua_update_tpg_secondary_metadata(tg_pt_gp_mem, port,
+                               md_buf, md_buf_len);
+               mutex_unlock(&port->sep_tg_pt_md_mutex);
+
+               kfree(md_buf);
+       }
+
+       return 0;
+}
+
+struct t10_alua_lu_gp *
+core_alua_allocate_lu_gp(const char *name, int def_group)
+{
+       struct t10_alua_lu_gp *lu_gp;
+
+       lu_gp = kmem_cache_zalloc(t10_alua_lu_gp_cache, GFP_KERNEL);
+       if (!(lu_gp)) {
+               printk(KERN_ERR "Unable to allocate struct t10_alua_lu_gp\n");
+               return ERR_PTR(-ENOMEM);;
+       }
+       INIT_LIST_HEAD(&lu_gp->lu_gp_list);
+       INIT_LIST_HEAD(&lu_gp->lu_gp_mem_list);
+       spin_lock_init(&lu_gp->lu_gp_lock);
+       atomic_set(&lu_gp->lu_gp_ref_cnt, 0);
+
+       if (def_group) {
+               lu_gp->lu_gp_id = se_global->alua_lu_gps_counter++;;
+               lu_gp->lu_gp_valid_id = 1;
+               se_global->alua_lu_gps_count++;
+       }
+
+       return lu_gp;
+}
+
+int core_alua_set_lu_gp_id(struct t10_alua_lu_gp *lu_gp, u16 lu_gp_id)
+{
+       struct t10_alua_lu_gp *lu_gp_tmp;
+       u16 lu_gp_id_tmp;
+       /*
+        * The lu_gp->lu_gp_id may only be set once..
+        */
+       if (lu_gp->lu_gp_valid_id) {
+               printk(KERN_WARNING "ALUA LU Group already has a valid ID,"
+                       " ignoring request\n");
+               return -1;
+       }
+
+       spin_lock(&se_global->lu_gps_lock);
+       if (se_global->alua_lu_gps_count == 0x0000ffff) {
+               printk(KERN_ERR "Maximum ALUA se_global->alua_lu_gps_count:"
+                               " 0x0000ffff reached\n");
+               spin_unlock(&se_global->lu_gps_lock);
+               kmem_cache_free(t10_alua_lu_gp_cache, lu_gp);
+               return -1;
+       }
+again:
+       lu_gp_id_tmp = (lu_gp_id != 0) ? lu_gp_id :
+                               se_global->alua_lu_gps_counter++;
+
+       list_for_each_entry(lu_gp_tmp, &se_global->g_lu_gps_list, lu_gp_list) {
+               if (lu_gp_tmp->lu_gp_id == lu_gp_id_tmp) {
+                       if (!(lu_gp_id))
+                               goto again;
+
+                       printk(KERN_WARNING "ALUA Logical Unit Group ID: %hu"
+                               " already exists, ignoring request\n",
+                               lu_gp_id);
+                       spin_unlock(&se_global->lu_gps_lock);
+                       return -1;
+               }
+       }
+
+       lu_gp->lu_gp_id = lu_gp_id_tmp;
+       lu_gp->lu_gp_valid_id = 1;
+       list_add_tail(&lu_gp->lu_gp_list, &se_global->g_lu_gps_list);
+       se_global->alua_lu_gps_count++;
+       spin_unlock(&se_global->lu_gps_lock);
+
+       return 0;
+}
+
+static struct t10_alua_lu_gp_member *
+core_alua_allocate_lu_gp_mem(struct se_device *dev)
+{
+       struct t10_alua_lu_gp_member *lu_gp_mem;
+
+       lu_gp_mem = kmem_cache_zalloc(t10_alua_lu_gp_mem_cache, GFP_KERNEL);
+       if (!(lu_gp_mem)) {
+               printk(KERN_ERR "Unable to allocate struct t10_alua_lu_gp_member\n");
+               return ERR_PTR(-ENOMEM);
+       }
+       INIT_LIST_HEAD(&lu_gp_mem->lu_gp_mem_list);
+       spin_lock_init(&lu_gp_mem->lu_gp_mem_lock);
+       atomic_set(&lu_gp_mem->lu_gp_mem_ref_cnt, 0);
+
+       lu_gp_mem->lu_gp_mem_dev = dev;
+       dev->dev_alua_lu_gp_mem = lu_gp_mem;
+
+       return lu_gp_mem;
+}
+
+void core_alua_free_lu_gp(struct t10_alua_lu_gp *lu_gp)
+{
+       struct t10_alua_lu_gp_member *lu_gp_mem, *lu_gp_mem_tmp;
+       /*
+        * Once we have reached this point, config_item_put() has
+        * already been called from target_core_alua_drop_lu_gp().
+        *
+        * Here, we remove the *lu_gp from the global list so that
+        * no associations can be made while we are releasing
+        * struct t10_alua_lu_gp.
+        */
+       spin_lock(&se_global->lu_gps_lock);
+       atomic_set(&lu_gp->lu_gp_shutdown, 1);
+       list_del(&lu_gp->lu_gp_list);
+       se_global->alua_lu_gps_count--;
+       spin_unlock(&se_global->lu_gps_lock);
+       /*
+        * Allow struct t10_alua_lu_gp * referenced by core_alua_get_lu_gp_by_name()
+        * in target_core_configfs.c:target_core_store_alua_lu_gp() to be
+        * released with core_alua_put_lu_gp_from_name()
+        */
+       while (atomic_read(&lu_gp->lu_gp_ref_cnt))
+               cpu_relax();
+       /*
+        * Release reference to struct t10_alua_lu_gp * from all associated
+        * struct se_device.
+        */
+       spin_lock(&lu_gp->lu_gp_lock);
+       list_for_each_entry_safe(lu_gp_mem, lu_gp_mem_tmp,
+                               &lu_gp->lu_gp_mem_list, lu_gp_mem_list) {
+               if (lu_gp_mem->lu_gp_assoc) {
+                       list_del(&lu_gp_mem->lu_gp_mem_list);
+                       lu_gp->lu_gp_members--;
+                       lu_gp_mem->lu_gp_assoc = 0;
+               }
+               spin_unlock(&lu_gp->lu_gp_lock);
+               /*
+                *
+                * lu_gp_mem is assoicated 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().
+                *
+                * If the passed lu_gp does NOT match the default_lu_gp, assume
+                * we want to re-assocate a given lu_gp_mem with default_lu_gp.
+                */
+               spin_lock(&lu_gp_mem->lu_gp_mem_lock);
+               if (lu_gp != se_global->default_lu_gp)
+                       __core_alua_attach_lu_gp_mem(lu_gp_mem,
+                                       se_global->default_lu_gp);
+               else
+                       lu_gp_mem->lu_gp = NULL;
+               spin_unlock(&lu_gp_mem->lu_gp_mem_lock);
+
+               spin_lock(&lu_gp->lu_gp_lock);
+       }
+       spin_unlock(&lu_gp->lu_gp_lock);
+
+       kmem_cache_free(t10_alua_lu_gp_cache, lu_gp);
+}
+
+void core_alua_free_lu_gp_mem(struct se_device *dev)
+{
+       struct se_subsystem_dev *su_dev = dev->se_sub_dev;
+       struct t10_alua *alua = T10_ALUA(su_dev);
+       struct t10_alua_lu_gp *lu_gp;
+       struct t10_alua_lu_gp_member *lu_gp_mem;
+
+       if (alua->alua_type != SPC3_ALUA_EMULATED)
+               return;
+
+       lu_gp_mem = dev->dev_alua_lu_gp_mem;
+       if (!(lu_gp_mem))
+               return;
+
+       while (atomic_read(&lu_gp_mem->lu_gp_mem_ref_cnt))
+               cpu_relax();
+
+       spin_lock(&lu_gp_mem->lu_gp_mem_lock);
+       lu_gp = lu_gp_mem->lu_gp;
+       if ((lu_gp)) {
+               spin_lock(&lu_gp->lu_gp_lock);
+               if (lu_gp_mem->lu_gp_assoc) {
+                       list_del(&lu_gp_mem->lu_gp_mem_list);
+                       lu_gp->lu_gp_members--;
+                       lu_gp_mem->lu_gp_assoc = 0;
+               }
+               spin_unlock(&lu_gp->lu_gp_lock);
+               lu_gp_mem->lu_gp = NULL;
+       }
+       spin_unlock(&lu_gp_mem->lu_gp_mem_lock);
+
+       kmem_cache_free(t10_alua_lu_gp_mem_cache, lu_gp_mem);
+}
+
+struct t10_alua_lu_gp *core_alua_get_lu_gp_by_name(const char *name)
+{
+       struct t10_alua_lu_gp *lu_gp;
+       struct config_item *ci;
+
+       spin_lock(&se_global->lu_gps_lock);
+       list_for_each_entry(lu_gp, &se_global->g_lu_gps_list, lu_gp_list) {
+               if (!(lu_gp->lu_gp_valid_id))
+                       continue;
+               ci = &lu_gp->lu_gp_group.cg_item;
+               if (!(strcmp(config_item_name(ci), name))) {
+                       atomic_inc(&lu_gp->lu_gp_ref_cnt);
+                       spin_unlock(&se_global->lu_gps_lock);
+                       return lu_gp;
+               }
+       }
+       spin_unlock(&se_global->lu_gps_lock);
+
+       return NULL;
+}
+
+void core_alua_put_lu_gp_from_name(struct t10_alua_lu_gp *lu_gp)
+{
+       spin_lock(&se_global->lu_gps_lock);
+       atomic_dec(&lu_gp->lu_gp_ref_cnt);
+       spin_unlock(&se_global->lu_gps_lock);
+}
+
+/*
+ * Called with struct t10_alua_lu_gp_member->lu_gp_mem_lock
+ */
+void __core_alua_attach_lu_gp_mem(
+       struct t10_alua_lu_gp_member *lu_gp_mem,
+       struct t10_alua_lu_gp *lu_gp)
+{
+       spin_lock(&lu_gp->lu_gp_lock);
+       lu_gp_mem->lu_gp = lu_gp;
+       lu_gp_mem->lu_gp_assoc = 1;
+       list_add_tail(&lu_gp_mem->lu_gp_mem_list, &lu_gp->lu_gp_mem_list);
+       lu_gp->lu_gp_members++;
+       spin_unlock(&lu_gp->lu_gp_lock);
+}
+
+/*
+ * Called with struct t10_alua_lu_gp_member->lu_gp_mem_lock
+ */
+void __core_alua_drop_lu_gp_mem(
+       struct t10_alua_lu_gp_member *lu_gp_mem,
+       struct t10_alua_lu_gp *lu_gp)
+{
+       spin_lock(&lu_gp->lu_gp_lock);
+       list_del(&lu_gp_mem->lu_gp_mem_list);
+       lu_gp_mem->lu_gp = NULL;
+       lu_gp_mem->lu_gp_assoc = 0;
+       lu_gp->lu_gp_members--;
+       spin_unlock(&lu_gp->lu_gp_lock);
+}
+
+struct t10_alua_tg_pt_gp *core_alua_allocate_tg_pt_gp(
+       struct se_subsystem_dev *su_dev,
+       const char *name,
+       int def_group)
+{
+       struct t10_alua_tg_pt_gp *tg_pt_gp;
+
+       tg_pt_gp = kmem_cache_zalloc(t10_alua_tg_pt_gp_cache, GFP_KERNEL);
+       if (!(tg_pt_gp)) {
+               printk(KERN_ERR "Unable to allocate struct t10_alua_tg_pt_gp\n");
+               return NULL;
+       }
+       INIT_LIST_HEAD(&tg_pt_gp->tg_pt_gp_list);
+       INIT_LIST_HEAD(&tg_pt_gp->tg_pt_gp_mem_list);
+       mutex_init(&tg_pt_gp->tg_pt_gp_md_mutex);
+       spin_lock_init(&tg_pt_gp->tg_pt_gp_lock);
+       atomic_set(&tg_pt_gp->tg_pt_gp_ref_cnt, 0);
+       tg_pt_gp->tg_pt_gp_su_dev = su_dev;
+       tg_pt_gp->tg_pt_gp_md_buf_len = ALUA_MD_BUF_LEN;
+       atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state,
+               ALUA_ACCESS_STATE_ACTIVE_OPTMIZED);
+       /*
+        * Enable both explict and implict ALUA support by default
+        */
+       tg_pt_gp->tg_pt_gp_alua_access_type =
+                       TPGS_EXPLICT_ALUA | TPGS_IMPLICT_ALUA;
+       /*
+        * Set the default Active/NonOptimized Delay in milliseconds
+        */
+       tg_pt_gp->tg_pt_gp_nonop_delay_msecs = ALUA_DEFAULT_NONOP_DELAY_MSECS;
+       tg_pt_gp->tg_pt_gp_trans_delay_msecs = ALUA_DEFAULT_TRANS_DELAY_MSECS;
+
+       if (def_group) {
+               spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+               tg_pt_gp->tg_pt_gp_id =
+                               T10_ALUA(su_dev)->alua_tg_pt_gps_counter++;
+               tg_pt_gp->tg_pt_gp_valid_id = 1;
+               T10_ALUA(su_dev)->alua_tg_pt_gps_count++;
+               list_add_tail(&tg_pt_gp->tg_pt_gp_list,
+                             &T10_ALUA(su_dev)->tg_pt_gps_list);
+               spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+       }
+
+       return tg_pt_gp;
+}
+
+int core_alua_set_tg_pt_gp_id(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       u16 tg_pt_gp_id)
+{
+       struct se_subsystem_dev *su_dev = tg_pt_gp->tg_pt_gp_su_dev;
+       struct t10_alua_tg_pt_gp *tg_pt_gp_tmp;
+       u16 tg_pt_gp_id_tmp;
+       /*
+        * The tg_pt_gp->tg_pt_gp_id may only be set once..
+        */
+       if (tg_pt_gp->tg_pt_gp_valid_id) {
+               printk(KERN_WARNING "ALUA TG PT Group already has a valid ID,"
+                       " ignoring request\n");
+               return -1;
+       }
+
+       spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+       if (T10_ALUA(su_dev)->alua_tg_pt_gps_count == 0x0000ffff) {
+               printk(KERN_ERR "Maximum ALUA alua_tg_pt_gps_count:"
+                       " 0x0000ffff reached\n");
+               spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+               kmem_cache_free(t10_alua_tg_pt_gp_cache, tg_pt_gp);
+               return -1;
+       }
+again:
+       tg_pt_gp_id_tmp = (tg_pt_gp_id != 0) ? tg_pt_gp_id :
+                       T10_ALUA(su_dev)->alua_tg_pt_gps_counter++;
+
+       list_for_each_entry(tg_pt_gp_tmp, &T10_ALUA(su_dev)->tg_pt_gps_list,
+                       tg_pt_gp_list) {
+               if (tg_pt_gp_tmp->tg_pt_gp_id == tg_pt_gp_id_tmp) {
+                       if (!(tg_pt_gp_id))
+                               goto again;
+
+                       printk(KERN_ERR "ALUA Target Port Group ID: %hu already"
+                               " exists, ignoring request\n", tg_pt_gp_id);
+                       spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+                       return -1;
+               }
+       }
+
+       tg_pt_gp->tg_pt_gp_id = tg_pt_gp_id_tmp;
+       tg_pt_gp->tg_pt_gp_valid_id = 1;
+       list_add_tail(&tg_pt_gp->tg_pt_gp_list,
+                       &T10_ALUA(su_dev)->tg_pt_gps_list);
+       T10_ALUA(su_dev)->alua_tg_pt_gps_count++;
+       spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+
+       return 0;
+}
+
+struct t10_alua_tg_pt_gp_member *core_alua_allocate_tg_pt_gp_mem(
+       struct se_port *port)
+{
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;
+
+       tg_pt_gp_mem = kmem_cache_zalloc(t10_alua_tg_pt_gp_mem_cache,
+                               GFP_KERNEL);
+       if (!(tg_pt_gp_mem)) {
+               printk(KERN_ERR "Unable to allocate struct t10_alua_tg_pt_gp_member\n");
+               return ERR_PTR(-ENOMEM);
+       }
+       INIT_LIST_HEAD(&tg_pt_gp_mem->tg_pt_gp_mem_list);
+       spin_lock_init(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+       atomic_set(&tg_pt_gp_mem->tg_pt_gp_mem_ref_cnt, 0);
+
+       tg_pt_gp_mem->tg_pt = port;
+       port->sep_alua_tg_pt_gp_mem = tg_pt_gp_mem;
+       atomic_set(&port->sep_tg_pt_gp_active, 1);
+
+       return tg_pt_gp_mem;
+}
+
+void core_alua_free_tg_pt_gp(
+       struct t10_alua_tg_pt_gp *tg_pt_gp)
+{
+       struct se_subsystem_dev *su_dev = tg_pt_gp->tg_pt_gp_su_dev;
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem, *tg_pt_gp_mem_tmp;
+       /*
+        * Once we have reached this point, config_item_put() has already
+        * been called from target_core_alua_drop_tg_pt_gp().
+        *
+        * Here we remove *tg_pt_gp from the global list so that
+        * no assications *OR* explict ALUA via SET_TARGET_PORT_GROUPS
+        * can be made while we are releasing struct t10_alua_tg_pt_gp.
+        */
+       spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+       list_del(&tg_pt_gp->tg_pt_gp_list);
+       T10_ALUA(su_dev)->alua_tg_pt_gps_counter--;
+       spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+       /*
+        * Allow a struct t10_alua_tg_pt_gp_member * referenced by
+        * core_alua_get_tg_pt_gp_by_name() in
+        * target_core_configfs.c:target_core_store_alua_tg_pt_gp()
+        * to be released with core_alua_put_tg_pt_gp_from_name().
+        */
+       while (atomic_read(&tg_pt_gp->tg_pt_gp_ref_cnt))
+               cpu_relax();
+       /*
+        * Release reference to struct t10_alua_tg_pt_gp from all associated
+        * struct se_port.
+        */
+       spin_lock(&tg_pt_gp->tg_pt_gp_lock);
+       list_for_each_entry_safe(tg_pt_gp_mem, tg_pt_gp_mem_tmp,
+                       &tg_pt_gp->tg_pt_gp_mem_list, tg_pt_gp_mem_list) {
+               if (tg_pt_gp_mem->tg_pt_gp_assoc) {
+                       list_del(&tg_pt_gp_mem->tg_pt_gp_mem_list);
+                       tg_pt_gp->tg_pt_gp_members--;
+                       tg_pt_gp_mem->tg_pt_gp_assoc = 0;
+               }
+               spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
+               /*
+                * tg_pt_gp_mem is assoicated with a single
+                * se_port->sep_alua_tg_pt_gp_mem, and is released via
+                * core_alua_free_tg_pt_gp_mem().
+                *
+                * If the passed tg_pt_gp does NOT match the default_tg_pt_gp,
+                * assume we want to re-assocate a given tg_pt_gp_mem with
+                * default_tg_pt_gp.
+                */
+               spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+               if (tg_pt_gp != T10_ALUA(su_dev)->default_tg_pt_gp) {
+                       __core_alua_attach_tg_pt_gp_mem(tg_pt_gp_mem,
+                                       T10_ALUA(su_dev)->default_tg_pt_gp);
+               } else
+                       tg_pt_gp_mem->tg_pt_gp = NULL;
+               spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+
+               spin_lock(&tg_pt_gp->tg_pt_gp_lock);
+       }
+       spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
+
+       kmem_cache_free(t10_alua_tg_pt_gp_cache, tg_pt_gp);
+}
+
+void core_alua_free_tg_pt_gp_mem(struct se_port *port)
+{
+       struct se_subsystem_dev *su_dev = port->sep_lun->lun_se_dev->se_sub_dev;
+       struct t10_alua *alua = T10_ALUA(su_dev);
+       struct t10_alua_tg_pt_gp *tg_pt_gp;
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;
+
+       if (alua->alua_type != SPC3_ALUA_EMULATED)
+               return;
+
+       tg_pt_gp_mem = port->sep_alua_tg_pt_gp_mem;
+       if (!(tg_pt_gp_mem))
+               return;
+
+       while (atomic_read(&tg_pt_gp_mem->tg_pt_gp_mem_ref_cnt))
+               cpu_relax();
+
+       spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+       tg_pt_gp = tg_pt_gp_mem->tg_pt_gp;
+       if ((tg_pt_gp)) {
+               spin_lock(&tg_pt_gp->tg_pt_gp_lock);
+               if (tg_pt_gp_mem->tg_pt_gp_assoc) {
+                       list_del(&tg_pt_gp_mem->tg_pt_gp_mem_list);
+                       tg_pt_gp->tg_pt_gp_members--;
+                       tg_pt_gp_mem->tg_pt_gp_assoc = 0;
+               }
+               spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
+               tg_pt_gp_mem->tg_pt_gp = NULL;
+       }
+       spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+
+       kmem_cache_free(t10_alua_tg_pt_gp_mem_cache, tg_pt_gp_mem);
+}
+
+static struct t10_alua_tg_pt_gp *core_alua_get_tg_pt_gp_by_name(
+       struct se_subsystem_dev *su_dev,
+       const char *name)
+{
+       struct t10_alua_tg_pt_gp *tg_pt_gp;
+       struct config_item *ci;
+
+       spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+       list_for_each_entry(tg_pt_gp, &T10_ALUA(su_dev)->tg_pt_gps_list,
+                       tg_pt_gp_list) {
+               if (!(tg_pt_gp->tg_pt_gp_valid_id))
+                       continue;
+               ci = &tg_pt_gp->tg_pt_gp_group.cg_item;
+               if (!(strcmp(config_item_name(ci), name))) {
+                       atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt);
+                       spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+                       return tg_pt_gp;
+               }
+       }
+       spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+
+       return NULL;
+}
+
+static void core_alua_put_tg_pt_gp_from_name(
+       struct t10_alua_tg_pt_gp *tg_pt_gp)
+{
+       struct se_subsystem_dev *su_dev = tg_pt_gp->tg_pt_gp_su_dev;
+
+       spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+       atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt);
+       spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+}
+
+/*
+ * Called with struct t10_alua_tg_pt_gp_member->tg_pt_gp_mem_lock held
+ */
+void __core_alua_attach_tg_pt_gp_mem(
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem,
+       struct t10_alua_tg_pt_gp *tg_pt_gp)
+{
+       spin_lock(&tg_pt_gp->tg_pt_gp_lock);
+       tg_pt_gp_mem->tg_pt_gp = tg_pt_gp;
+       tg_pt_gp_mem->tg_pt_gp_assoc = 1;
+       list_add_tail(&tg_pt_gp_mem->tg_pt_gp_mem_list,
+                       &tg_pt_gp->tg_pt_gp_mem_list);
+       tg_pt_gp->tg_pt_gp_members++;
+       spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
+}
+
+/*
+ * Called with struct t10_alua_tg_pt_gp_member->tg_pt_gp_mem_lock held
+ */
+static void __core_alua_drop_tg_pt_gp_mem(
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem,
+       struct t10_alua_tg_pt_gp *tg_pt_gp)
+{
+       spin_lock(&tg_pt_gp->tg_pt_gp_lock);
+       list_del(&tg_pt_gp_mem->tg_pt_gp_mem_list);
+       tg_pt_gp_mem->tg_pt_gp = NULL;
+       tg_pt_gp_mem->tg_pt_gp_assoc = 0;
+       tg_pt_gp->tg_pt_gp_members--;
+       spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
+}
+
+ssize_t core_alua_show_tg_pt_gp_info(struct se_port *port, char *page)
+{
+       struct se_subsystem_dev *su_dev = port->sep_lun->lun_se_dev->se_sub_dev;
+       struct config_item *tg_pt_ci;
+       struct t10_alua *alua = T10_ALUA(su_dev);
+       struct t10_alua_tg_pt_gp *tg_pt_gp;
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;
+       ssize_t len = 0;
+
+       if (alua->alua_type != SPC3_ALUA_EMULATED)
+               return len;
+
+       tg_pt_gp_mem = port->sep_alua_tg_pt_gp_mem;
+       if (!(tg_pt_gp_mem))
+               return len;
+
+       spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+       tg_pt_gp = tg_pt_gp_mem->tg_pt_gp;
+       if ((tg_pt_gp)) {
+               tg_pt_ci = &tg_pt_gp->tg_pt_gp_group.cg_item;
+               len += sprintf(page, "TG Port Alias: %s\nTG Port Group ID:"
+                       " %hu\nTG Port Primary Access State: %s\nTG Port "
+                       "Primary Access Status: %s\nTG Port Secondary Access"
+                       " State: %s\nTG Port Secondary Access Status: %s\n",
+                       config_item_name(tg_pt_ci), tg_pt_gp->tg_pt_gp_id,
+                       core_alua_dump_state(atomic_read(
+                                       &tg_pt_gp->tg_pt_gp_alua_access_state)),
+                       core_alua_dump_status(
+                               tg_pt_gp->tg_pt_gp_alua_access_status),
+                       (atomic_read(&port->sep_tg_pt_secondary_offline)) ?
+                       "Offline" : "None",
+                       core_alua_dump_status(port->sep_tg_pt_secondary_stat));
+       }
+       spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+
+       return len;
+}
+
+ssize_t core_alua_store_tg_pt_gp_info(
+       struct se_port *port,
+       const char *page,
+       size_t count)
+{
+       struct se_portal_group *tpg;
+       struct se_lun *lun;
+       struct se_subsystem_dev *su_dev = port->sep_lun->lun_se_dev->se_sub_dev;
+       struct t10_alua_tg_pt_gp *tg_pt_gp = NULL, *tg_pt_gp_new = NULL;
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;
+       unsigned char buf[TG_PT_GROUP_NAME_BUF];
+       int move = 0;
+
+       tpg = port->sep_tpg;
+       lun = port->sep_lun;
+
+       if (T10_ALUA(su_dev)->alua_type != SPC3_ALUA_EMULATED) {
+               printk(KERN_WARNING "SPC3_ALUA_EMULATED not enabled for"
+                       " %s/tpgt_%hu/%s\n", TPG_TFO(tpg)->tpg_get_wwn(tpg),
+                       TPG_TFO(tpg)->tpg_get_tag(tpg),
+                       config_item_name(&lun->lun_group.cg_item));
+               return -EINVAL;
+       }
+
+       if (count > TG_PT_GROUP_NAME_BUF) {
+               printk(KERN_ERR "ALUA Target Port Group alias too large!\n");
+               return -EINVAL;
+       }
+       memset(buf, 0, TG_PT_GROUP_NAME_BUF);
+       memcpy(buf, page, count);
+       /*
+        * Any ALUA target port group alias besides "NULL" means we will be
+        * making a new group association.
+        */
+       if (strcmp(strstrip(buf), "NULL")) {
+               /*
+                * core_alua_get_tg_pt_gp_by_name() will increment reference to
+                * struct t10_alua_tg_pt_gp.  This reference is released with
+                * core_alua_put_tg_pt_gp_from_name() below.
+                */
+               tg_pt_gp_new = core_alua_get_tg_pt_gp_by_name(su_dev,
+                                       strstrip(buf));
+               if (!(tg_pt_gp_new))
+                       return -ENODEV;
+       }
+       tg_pt_gp_mem = port->sep_alua_tg_pt_gp_mem;
+       if (!(tg_pt_gp_mem)) {
+               if (tg_pt_gp_new)
+                       core_alua_put_tg_pt_gp_from_name(tg_pt_gp_new);
+               printk(KERN_ERR "NULL struct se_port->sep_alua_tg_pt_gp_mem pointer\n");
+               return -EINVAL;
+       }
+
+       spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+       tg_pt_gp = tg_pt_gp_mem->tg_pt_gp;
+       if ((tg_pt_gp)) {
+               /*
+                * Clearing an existing tg_pt_gp association, and replacing
+                * with the default_tg_pt_gp.
+                */
+               if (!(tg_pt_gp_new)) {
+                       printk(KERN_INFO "Target_Core_ConfigFS: Moving"
+                               " %s/tpgt_%hu/%s from ALUA Target Port Group:"
+                               " alua/%s, ID: %hu back to"
+                               " default_tg_pt_gp\n",
+                               TPG_TFO(tpg)->tpg_get_wwn(tpg),
+                               TPG_TFO(tpg)->tpg_get_tag(tpg),
+                               config_item_name(&lun->lun_group.cg_item),
+                               config_item_name(
+                                       &tg_pt_gp->tg_pt_gp_group.cg_item),
+                               tg_pt_gp->tg_pt_gp_id);
+
+                       __core_alua_drop_tg_pt_gp_mem(tg_pt_gp_mem, tg_pt_gp);
+                       __core_alua_attach_tg_pt_gp_mem(tg_pt_gp_mem,
+                                       T10_ALUA(su_dev)->default_tg_pt_gp);
+                       spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+
+                       return count;
+               }
+               /*
+                * Removing existing association of tg_pt_gp_mem with tg_pt_gp
+                */
+               __core_alua_drop_tg_pt_gp_mem(tg_pt_gp_mem, tg_pt_gp);
+               move = 1;
+       }
+       /*
+        * Associate tg_pt_gp_mem with tg_pt_gp_new.
+        */
+       __core_alua_attach_tg_pt_gp_mem(tg_pt_gp_mem, tg_pt_gp_new);
+       spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+       printk(KERN_INFO "Target_Core_ConfigFS: %s %s/tpgt_%hu/%s to ALUA"
+               " Target Port Group: alua/%s, ID: %hu\n", (move) ?
+               "Moving" : "Adding", TPG_TFO(tpg)->tpg_get_wwn(tpg),
+               TPG_TFO(tpg)->tpg_get_tag(tpg),
+               config_item_name(&lun->lun_group.cg_item),
+               config_item_name(&tg_pt_gp_new->tg_pt_gp_group.cg_item),
+               tg_pt_gp_new->tg_pt_gp_id);
+
+       core_alua_put_tg_pt_gp_from_name(tg_pt_gp_new);
+       return count;
+}
+
+ssize_t core_alua_show_access_type(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       if ((tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICT_ALUA) &&
+           (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICT_ALUA))
+               return sprintf(page, "Implict and Explict\n");
+       else if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICT_ALUA)
+               return sprintf(page, "Implict\n");
+       else if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICT_ALUA)
+               return sprintf(page, "Explict\n");
+       else
+               return sprintf(page, "None\n");
+}
+
+ssize_t core_alua_store_access_type(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       const char *page,
+       size_t count)
+{
+       unsigned long tmp;
+       int ret;
+
+       ret = strict_strtoul(page, 0, &tmp);
+       if (ret < 0) {
+               printk(KERN_ERR "Unable to extract alua_access_type\n");
+               return -EINVAL;
+       }
+       if ((tmp != 0) && (tmp != 1) && (tmp != 2) && (tmp != 3)) {
+               printk(KERN_ERR "Illegal value for alua_access_type:"
+                               " %lu\n", tmp);
+               return -EINVAL;
+       }
+       if (tmp == 3)
+               tg_pt_gp->tg_pt_gp_alua_access_type =
+                       TPGS_IMPLICT_ALUA | TPGS_EXPLICT_ALUA;
+       else if (tmp == 2)
+               tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_EXPLICT_ALUA;
+       else if (tmp == 1)
+               tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_IMPLICT_ALUA;
+       else
+               tg_pt_gp->tg_pt_gp_alua_access_type = 0;
+
+       return count;
+}
+
+ssize_t core_alua_show_nonop_delay_msecs(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       return sprintf(page, "%d\n", tg_pt_gp->tg_pt_gp_nonop_delay_msecs);
+}
+
+ssize_t core_alua_store_nonop_delay_msecs(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       const char *page,
+       size_t count)
+{
+       unsigned long tmp;
+       int ret;
+
+       ret = strict_strtoul(page, 0, &tmp);
+       if (ret < 0) {
+               printk(KERN_ERR "Unable to extract nonop_delay_msecs\n");
+               return -EINVAL;
+       }
+       if (tmp > ALUA_MAX_NONOP_DELAY_MSECS) {
+               printk(KERN_ERR "Passed nonop_delay_msecs: %lu, exceeds"
+                       " ALUA_MAX_NONOP_DELAY_MSECS: %d\n", tmp,
+                       ALUA_MAX_NONOP_DELAY_MSECS);
+               return -EINVAL;
+       }
+       tg_pt_gp->tg_pt_gp_nonop_delay_msecs = (int)tmp;
+
+       return count;
+}
+
+ssize_t core_alua_show_trans_delay_msecs(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       return sprintf(page, "%d\n", tg_pt_gp->tg_pt_gp_trans_delay_msecs);
+}
+
+ssize_t core_alua_store_trans_delay_msecs(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       const char *page,
+       size_t count)
+{
+       unsigned long tmp;
+       int ret;
+
+       ret = strict_strtoul(page, 0, &tmp);
+       if (ret < 0) {
+               printk(KERN_ERR "Unable to extract trans_delay_msecs\n");
+               return -EINVAL;
+       }
+       if (tmp > ALUA_MAX_TRANS_DELAY_MSECS) {
+               printk(KERN_ERR "Passed trans_delay_msecs: %lu, exceeds"
+                       " ALUA_MAX_TRANS_DELAY_MSECS: %d\n", tmp,
+                       ALUA_MAX_TRANS_DELAY_MSECS);
+               return -EINVAL;
+       }
+       tg_pt_gp->tg_pt_gp_trans_delay_msecs = (int)tmp;
+
+       return count;
+}
+
+ssize_t core_alua_show_preferred_bit(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       return sprintf(page, "%d\n", tg_pt_gp->tg_pt_gp_pref);
+}
+
+ssize_t core_alua_store_preferred_bit(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       const char *page,
+       size_t count)
+{
+       unsigned long tmp;
+       int ret;
+
+       ret = strict_strtoul(page, 0, &tmp);
+       if (ret < 0) {
+               printk(KERN_ERR "Unable to extract preferred ALUA value\n");
+               return -EINVAL;
+       }
+       if ((tmp != 0) && (tmp != 1)) {
+               printk(KERN_ERR "Illegal value for preferred ALUA: %lu\n", tmp);
+               return -EINVAL;
+       }
+       tg_pt_gp->tg_pt_gp_pref = (int)tmp;
+
+       return count;
+}
+
+ssize_t core_alua_show_offline_bit(struct se_lun *lun, char *page)
+{
+       if (!(lun->lun_sep))
+               return -ENODEV;
+
+       return sprintf(page, "%d\n",
+               atomic_read(&lun->lun_sep->sep_tg_pt_secondary_offline));
+}
+
+ssize_t core_alua_store_offline_bit(
+       struct se_lun *lun,
+       const char *page,
+       size_t count)
+{
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;
+       unsigned long tmp;
+       int ret;
+
+       if (!(lun->lun_sep))
+               return -ENODEV;
+
+       ret = strict_strtoul(page, 0, &tmp);
+       if (ret < 0) {
+               printk(KERN_ERR "Unable to extract alua_tg_pt_offline value\n");
+               return -EINVAL;
+       }
+       if ((tmp != 0) && (tmp != 1)) {
+               printk(KERN_ERR "Illegal value for alua_tg_pt_offline: %lu\n",
+                               tmp);
+               return -EINVAL;
+       }
+       tg_pt_gp_mem = lun->lun_sep->sep_alua_tg_pt_gp_mem;
+       if (!(tg_pt_gp_mem)) {
+               printk(KERN_ERR "Unable to locate *tg_pt_gp_mem\n");
+               return -EINVAL;
+       }
+
+       ret = core_alua_set_tg_pt_secondary_state(tg_pt_gp_mem,
+                       lun->lun_sep, 0, (int)tmp);
+       if (ret < 0)
+               return -EINVAL;
+
+       return count;
+}
+
+ssize_t core_alua_show_secondary_status(
+       struct se_lun *lun,
+       char *page)
+{
+       return sprintf(page, "%d\n", lun->lun_sep->sep_tg_pt_secondary_stat);
+}
+
+ssize_t core_alua_store_secondary_status(
+       struct se_lun *lun,
+       const char *page,
+       size_t count)
+{
+       unsigned long tmp;
+       int ret;
+
+       ret = strict_strtoul(page, 0, &tmp);
+       if (ret < 0) {
+               printk(KERN_ERR "Unable to extract alua_tg_pt_status\n");
+               return -EINVAL;
+       }
+       if ((tmp != ALUA_STATUS_NONE) &&
+           (tmp != ALUA_STATUS_ALTERED_BY_EXPLICT_STPG) &&
+           (tmp != ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA)) {
+               printk(KERN_ERR "Illegal value for alua_tg_pt_status: %lu\n",
+                               tmp);
+               return -EINVAL;
+       }
+       lun->lun_sep->sep_tg_pt_secondary_stat = (int)tmp;
+
+       return count;
+}
+
+ssize_t core_alua_show_secondary_write_metadata(
+       struct se_lun *lun,
+       char *page)
+{
+       return sprintf(page, "%d\n",
+                       lun->lun_sep->sep_tg_pt_secondary_write_md);
+}
+
+ssize_t core_alua_store_secondary_write_metadata(
+       struct se_lun *lun,
+       const char *page,
+       size_t count)
+{
+       unsigned long tmp;
+       int ret;
+
+       ret = strict_strtoul(page, 0, &tmp);
+       if (ret < 0) {
+               printk(KERN_ERR "Unable to extract alua_tg_pt_write_md\n");
+               return -EINVAL;
+       }
+       if ((tmp != 0) && (tmp != 1)) {
+               printk(KERN_ERR "Illegal value for alua_tg_pt_write_md:"
+                               " %lu\n", tmp);
+               return -EINVAL;
+       }
+       lun->lun_sep->sep_tg_pt_secondary_write_md = (int)tmp;
+
+       return count;
+}
+
+int core_setup_alua(struct se_device *dev, int force_pt)
+{
+       struct se_subsystem_dev *su_dev = dev->se_sub_dev;
+       struct t10_alua *alua = T10_ALUA(su_dev);
+       struct t10_alua_lu_gp_member *lu_gp_mem;
+       /*
+        * If this device is from Target_Core_Mod/pSCSI, use the ALUA logic
+        * of the Underlying SCSI hardware.  In Linux/SCSI terms, this can
+        * cause a problem because libata and some SATA RAID HBAs appear
+        * under Linux/SCSI, but emulate SCSI logic themselves.
+        */
+       if (((TRANSPORT(dev)->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) &&
+           !(DEV_ATTRIB(dev)->emulate_alua)) || force_pt) {
+               alua->alua_type = SPC_ALUA_PASSTHROUGH;
+               alua->alua_state_check = &core_alua_state_check_nop;
+               printk(KERN_INFO "%s: Using SPC_ALUA_PASSTHROUGH, no ALUA"
+                       " emulation\n", TRANSPORT(dev)->name);
+               return 0;
+       }
+       /*
+        * If SPC-3 or above is reported by real or emulated struct se_device,
+        * use emulated ALUA.
+        */
+       if (TRANSPORT(dev)->get_device_rev(dev) >= SCSI_3) {
+               printk(KERN_INFO "%s: Enabling ALUA Emulation for SPC-3"
+                       " device\n", TRANSPORT(dev)->name);
+               /*
+                * Assoicate this struct se_device with the default ALUA
+                * LUN Group.
+                */
+               lu_gp_mem = core_alua_allocate_lu_gp_mem(dev);
+               if (IS_ERR(lu_gp_mem) || !lu_gp_mem)
+                       return -1;
+
+               alua->alua_type = SPC3_ALUA_EMULATED;
+               alua->alua_state_check = &core_alua_state_check;
+               spin_lock(&lu_gp_mem->lu_gp_mem_lock);
+               __core_alua_attach_lu_gp_mem(lu_gp_mem,
+                               se_global->default_lu_gp);
+               spin_unlock(&lu_gp_mem->lu_gp_mem_lock);
+
+               printk(KERN_INFO "%s: Adding to default ALUA LU Group:"
+                       " core/alua/lu_gps/default_lu_gp\n",
+                       TRANSPORT(dev)->name);
+       } else {
+               alua->alua_type = SPC2_ALUA_DISABLED;
+               alua->alua_state_check = &core_alua_state_check_nop;
+               printk(KERN_INFO "%s: Disabling ALUA Emulation for SPC-2"
+                       " device\n", TRANSPORT(dev)->name);
+       }
+
+       return 0;
+}
diff --git a/drivers/target/target_core_alua.h b/drivers/target/target_core_alua.h
new file mode 100644 (file)
index 0000000..c86f97a
--- /dev/null
@@ -0,0 +1,126 @@
+#ifndef TARGET_CORE_ALUA_H
+#define TARGET_CORE_ALUA_H
+
+/*
+ * INQUIRY response data, TPGS Field
+ *
+ * from spc4r17 section 6.4.2 Table 135
+ */
+#define TPGS_NO_ALUA                           0x00
+#define TPGS_IMPLICT_ALUA                      0x10
+#define TPGS_EXPLICT_ALUA                      0x20
+
+/*
+ * ASYMMETRIC ACCESS STATE field
+ *
+ * from spc4r17 section 6.27 Table 245
+ */
+#define ALUA_ACCESS_STATE_ACTIVE_OPTMIZED      0x0
+#define ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED 0x1
+#define ALUA_ACCESS_STATE_STANDBY              0x2
+#define ALUA_ACCESS_STATE_UNAVAILABLE          0x3
+#define ALUA_ACCESS_STATE_OFFLINE              0xe
+#define ALUA_ACCESS_STATE_TRANSITION           0xf
+
+/*
+ * REPORT_TARGET_PORT_GROUP STATUS CODE
+ *
+ * from spc4r17 section 6.27 Table 246
+ */
+#define ALUA_STATUS_NONE                               0x00
+#define ALUA_STATUS_ALTERED_BY_EXPLICT_STPG            0x01
+#define ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA            0x02
+
+/*
+ * From spc4r17, Table D.1: ASC and ASCQ Assignement
+ */
+#define ASCQ_04H_ALUA_STATE_TRANSITION                 0x0a
+#define ASCQ_04H_ALUA_TG_PT_STANDBY                    0x0b
+#define ASCQ_04H_ALUA_TG_PT_UNAVAILABLE                        0x0c
+#define ASCQ_04H_ALUA_OFFLINE                          0x12
+
+/*
+ * Used as the default for Active/NonOptimized delay (in milliseconds)
+ * This can also be changed via configfs on a per target port group basis..
+ */
+#define ALUA_DEFAULT_NONOP_DELAY_MSECS                 100
+#define ALUA_MAX_NONOP_DELAY_MSECS                     10000 /* 10 seconds */
+/*
+ * Used for implict and explict ALUA transitional delay, that is disabled
+ * by default, and is intended to be used for debugging client side ALUA code.
+ */
+#define ALUA_DEFAULT_TRANS_DELAY_MSECS                 0
+#define ALUA_MAX_TRANS_DELAY_MSECS                     30000 /* 30 seconds */
+/*
+ * Used by core_alua_update_tpg_primary_metadata() and
+ * core_alua_update_tpg_secondary_metadata()
+ */
+#define ALUA_METADATA_PATH_LEN                         512
+/*
+ * Used by core_alua_update_tpg_secondary_metadata()
+ */
+#define ALUA_SECONDARY_METADATA_WWN_LEN                        256
+
+extern struct kmem_cache *t10_alua_lu_gp_cache;
+extern struct kmem_cache *t10_alua_lu_gp_mem_cache;
+extern struct kmem_cache *t10_alua_tg_pt_gp_cache;
+extern struct kmem_cache *t10_alua_tg_pt_gp_mem_cache;
+
+extern int core_emulate_report_target_port_groups(struct se_cmd *);
+extern int core_emulate_set_target_port_groups(struct se_cmd *);
+extern int core_alua_check_nonop_delay(struct se_cmd *);
+extern int core_alua_do_port_transition(struct t10_alua_tg_pt_gp *,
+                               struct se_device *, struct se_port *,
+                               struct se_node_acl *, int, int);
+extern char *core_alua_dump_status(int);
+extern struct t10_alua_lu_gp *core_alua_allocate_lu_gp(const char *, int);
+extern int core_alua_set_lu_gp_id(struct t10_alua_lu_gp *, u16);
+extern void core_alua_free_lu_gp(struct t10_alua_lu_gp *);
+extern void core_alua_free_lu_gp_mem(struct se_device *);
+extern struct t10_alua_lu_gp *core_alua_get_lu_gp_by_name(const char *);
+extern void core_alua_put_lu_gp_from_name(struct t10_alua_lu_gp *);
+extern void __core_alua_attach_lu_gp_mem(struct t10_alua_lu_gp_member *,
+                                       struct t10_alua_lu_gp *);
+extern void __core_alua_drop_lu_gp_mem(struct t10_alua_lu_gp_member *,
+                                       struct t10_alua_lu_gp *);
+extern void core_alua_drop_lu_gp_dev(struct se_device *);
+extern struct t10_alua_tg_pt_gp *core_alua_allocate_tg_pt_gp(
+                       struct se_subsystem_dev *, const char *, int);
+extern int core_alua_set_tg_pt_gp_id(struct t10_alua_tg_pt_gp *, u16);
+extern struct t10_alua_tg_pt_gp_member *core_alua_allocate_tg_pt_gp_mem(
+                                       struct se_port *);
+extern void core_alua_free_tg_pt_gp(struct t10_alua_tg_pt_gp *);
+extern void core_alua_free_tg_pt_gp_mem(struct se_port *);
+extern void __core_alua_attach_tg_pt_gp_mem(struct t10_alua_tg_pt_gp_member *,
+                                       struct t10_alua_tg_pt_gp *);
+extern ssize_t core_alua_show_tg_pt_gp_info(struct se_port *, char *);
+extern ssize_t core_alua_store_tg_pt_gp_info(struct se_port *, const char *,
+                                               size_t);
+extern ssize_t core_alua_show_access_type(struct t10_alua_tg_pt_gp *, char *);
+extern ssize_t core_alua_store_access_type(struct t10_alua_tg_pt_gp *,
+                                       const char *, size_t);
+extern ssize_t core_alua_show_nonop_delay_msecs(struct t10_alua_tg_pt_gp *,
+                                               char *);
+extern ssize_t core_alua_store_nonop_delay_msecs(struct t10_alua_tg_pt_gp *,
+                                       const char *, size_t);
+extern ssize_t core_alua_show_trans_delay_msecs(struct t10_alua_tg_pt_gp *,
+                                       char *);
+extern ssize_t core_alua_store_trans_delay_msecs(struct t10_alua_tg_pt_gp *,
+                                       const char *, size_t);
+extern ssize_t core_alua_show_preferred_bit(struct t10_alua_tg_pt_gp *,
+                                       char *);
+extern ssize_t core_alua_store_preferred_bit(struct t10_alua_tg_pt_gp *,
+                                       const char *, size_t);
+extern ssize_t core_alua_show_offline_bit(struct se_lun *, char *);
+extern ssize_t core_alua_store_offline_bit(struct se_lun *, const char *,
+                                       size_t);
+extern ssize_t core_alua_show_secondary_status(struct se_lun *, char *);
+extern ssize_t core_alua_store_secondary_status(struct se_lun *,
+                                       const char *, size_t);
+extern ssize_t core_alua_show_secondary_write_metadata(struct se_lun *,
+                                       char *);
+extern ssize_t core_alua_store_secondary_write_metadata(struct se_lun *,
+                                       const char *, size_t);
+extern int core_setup_alua(struct se_device *, int);
+
+#endif /* TARGET_CORE_ALUA_H */
diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c
new file mode 100644 (file)
index 0000000..366080b
--- /dev/null
@@ -0,0 +1,1131 @@
+/*
+ * CDB emulation for non-READ/WRITE commands.
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005 PyX Technologies, Inc.
+ * Copyright (c) 2005, 2006, 2007 SBE, Inc.
+ * Copyright (c) 2007-2010 Rising Tide Systems
+ * Copyright (c) 2008-2010 Linux-iSCSI.org
+ *
+ * Nicholas A. Bellinger <nab@kernel.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 <asm/unaligned.h>
+#include <scsi/scsi.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+#include "target_core_ua.h"
+
+static void
+target_fill_alua_data(struct se_port *port, unsigned char *buf)
+{
+       struct t10_alua_tg_pt_gp *tg_pt_gp;
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;
+
+       /*
+        * Set SCCS for MAINTENANCE_IN + REPORT_TARGET_PORT_GROUPS.
+        */
+       buf[5]  = 0x80;
+
+       /*
+        * Set TPGS field for explict and/or implict ALUA access type
+        * and opteration.
+        *
+        * See spc4r17 section 6.4.2 Table 135
+        */
+       if (!port)
+               return;
+       tg_pt_gp_mem = port->sep_alua_tg_pt_gp_mem;
+       if (!tg_pt_gp_mem)
+               return;
+
+       spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+       tg_pt_gp = tg_pt_gp_mem->tg_pt_gp;
+       if (tg_pt_gp)
+               buf[5] |= tg_pt_gp->tg_pt_gp_alua_access_type;
+       spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+}
+
+static int
+target_emulate_inquiry_std(struct se_cmd *cmd)
+{
+       struct se_lun *lun = SE_LUN(cmd);
+       struct se_device *dev = SE_DEV(cmd);
+       unsigned char *buf = cmd->t_task->t_task_buf;
+
+       /*
+        * Make sure we at least have 6 bytes of INQUIRY response
+        * payload going back for EVPD=0
+        */
+       if (cmd->data_length < 6) {
+               printk(KERN_ERR "SCSI Inquiry payload length: %u"
+                       " too small for EVPD=0\n", cmd->data_length);
+               return -1;
+       }
+
+       buf[0] = dev->transport->get_device_type(dev);
+       if (buf[0] == TYPE_TAPE)
+               buf[1] = 0x80;
+       buf[2] = dev->transport->get_device_rev(dev);
+
+       /*
+        * Enable SCCS and TPGS fields for Emulated ALUA
+        */
+       if (T10_ALUA(dev->se_sub_dev)->alua_type == SPC3_ALUA_EMULATED)
+               target_fill_alua_data(lun->lun_sep, buf);
+
+       if (cmd->data_length < 8) {
+               buf[4] = 1; /* Set additional length to 1 */
+               return 0;
+       }
+
+       buf[7] = 0x32; /* Sync=1 and CmdQue=1 */
+
+       /*
+        * Do not include vendor, product, reversion info in INQUIRY
+        * response payload for cdbs with a small allocation length.
+        */
+       if (cmd->data_length < 36) {
+               buf[4] = 3; /* Set additional length to 3 */
+               return 0;
+       }
+
+       snprintf((unsigned char *)&buf[8], 8, "LIO-ORG");
+       snprintf((unsigned char *)&buf[16], 16, "%s",
+                &DEV_T10_WWN(dev)->model[0]);
+       snprintf((unsigned char *)&buf[32], 4, "%s",
+                &DEV_T10_WWN(dev)->revision[0]);
+       buf[4] = 31; /* Set additional length to 31 */
+       return 0;
+}
+
+/* supported vital product data pages */
+static int
+target_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf)
+{
+       buf[1] = 0x00;
+       if (cmd->data_length < 8)
+               return 0;
+
+       buf[4] = 0x0;
+       /*
+        * Only report the INQUIRY EVPD=1 pages after a valid NAA
+        * Registered Extended LUN WWN has been set via ConfigFS
+        * during device creation/restart.
+        */
+       if (SE_DEV(cmd)->se_sub_dev->su_dev_flags &
+                       SDF_EMULATED_VPD_UNIT_SERIAL) {
+               buf[3] = 3;
+               buf[5] = 0x80;
+               buf[6] = 0x83;
+               buf[7] = 0x86;
+       }
+
+       return 0;
+}
+
+/* unit serial number */
+static int
+target_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf)
+{
+       struct se_device *dev = SE_DEV(cmd);
+       u16 len = 0;
+
+       buf[1] = 0x80;
+       if (dev->se_sub_dev->su_dev_flags &
+                       SDF_EMULATED_VPD_UNIT_SERIAL) {
+               u32 unit_serial_len;
+
+               unit_serial_len =
+                       strlen(&DEV_T10_WWN(dev)->unit_serial[0]);
+               unit_serial_len++; /* For NULL Terminator */
+
+               if (((len + 4) + unit_serial_len) > cmd->data_length) {
+                       len += unit_serial_len;
+                       buf[2] = ((len >> 8) & 0xff);
+                       buf[3] = (len & 0xff);
+                       return 0;
+               }
+               len += sprintf((unsigned char *)&buf[4], "%s",
+                       &DEV_T10_WWN(dev)->unit_serial[0]);
+               len++; /* Extra Byte for NULL Terminator */
+               buf[3] = len;
+       }
+       return 0;
+}
+
+/*
+ * Device identification VPD, for a complete list of
+ * DESIGNATOR TYPEs see spc4r17 Table 459.
+ */
+static int
+target_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf)
+{
+       struct se_device *dev = SE_DEV(cmd);
+       struct se_lun *lun = SE_LUN(cmd);
+       struct se_port *port = NULL;
+       struct se_portal_group *tpg = NULL;
+       struct t10_alua_lu_gp_member *lu_gp_mem;
+       struct t10_alua_tg_pt_gp *tg_pt_gp;
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;
+       unsigned char binary, binary_new;
+       unsigned char *prod = &DEV_T10_WWN(dev)->model[0];
+       u32 prod_len;
+       u32 unit_serial_len, off = 0;
+       int i;
+       u16 len = 0, id_len;
+
+       buf[1] = 0x83;
+       off = 4;
+
+       /*
+        * NAA IEEE Registered Extended Assigned designator format, see
+        * spc4r17 section 7.7.3.6.5
+        *
+        * We depend upon a target_core_mod/ConfigFS provided
+        * /sys/kernel/config/target/core/$HBA/$DEV/wwn/vpd_unit_serial
+        * value in order to return the NAA id.
+        */
+       if (!(dev->se_sub_dev->su_dev_flags & SDF_EMULATED_VPD_UNIT_SERIAL))
+               goto check_t10_vend_desc;
+
+       if (off + 20 > cmd->data_length)
+               goto check_t10_vend_desc;
+
+       /* CODE SET == Binary */
+       buf[off++] = 0x1;
+
+       /* Set ASSOICATION == addressed logical unit: 0)b */
+       buf[off] = 0x00;
+
+       /* Identifier/Designator type == NAA identifier */
+       buf[off++] = 0x3;
+       off++;
+
+       /* Identifier/Designator length */
+       buf[off++] = 0x10;
+
+       /*
+        * Start NAA IEEE Registered Extended Identifier/Designator
+        */
+       buf[off++] = (0x6 << 4);
+
+       /*
+        * Use OpenFabrics IEEE Company ID: 00 14 05
+        */
+       buf[off++] = 0x01;
+       buf[off++] = 0x40;
+       buf[off] = (0x5 << 4);
+
+       /*
+        * Return ConfigFS Unit Serial Number information for
+        * VENDOR_SPECIFIC_IDENTIFIER and
+        * VENDOR_SPECIFIC_IDENTIFIER_EXTENTION
+        */
+       binary = transport_asciihex_to_binaryhex(
+                               &DEV_T10_WWN(dev)->unit_serial[0]);
+       buf[off++] |= (binary & 0xf0) >> 4;
+       for (i = 0; i < 24; i += 2) {
+               binary_new = transport_asciihex_to_binaryhex(
+                       &DEV_T10_WWN(dev)->unit_serial[i+2]);
+               buf[off] = (binary & 0x0f) << 4;
+               buf[off++] |= (binary_new & 0xf0) >> 4;
+               binary = binary_new;
+       }
+       len = 20;
+       off = (len + 4);
+
+check_t10_vend_desc:
+       /*
+        * T10 Vendor Identifier Page, see spc4r17 section 7.7.3.4
+        */
+       id_len = 8; /* For Vendor field */
+       prod_len = 4; /* For VPD Header */
+       prod_len += 8; /* For Vendor field */
+       prod_len += strlen(prod);
+       prod_len++; /* For : */
+
+       if (dev->se_sub_dev->su_dev_flags &
+                       SDF_EMULATED_VPD_UNIT_SERIAL) {
+               unit_serial_len =
+                       strlen(&DEV_T10_WWN(dev)->unit_serial[0]);
+               unit_serial_len++; /* For NULL Terminator */
+
+               if ((len + (id_len + 4) +
+                   (prod_len + unit_serial_len)) >
+                               cmd->data_length) {
+                       len += (prod_len + unit_serial_len);
+                       goto check_port;
+               }
+               id_len += sprintf((unsigned char *)&buf[off+12],
+                               "%s:%s", prod,
+                               &DEV_T10_WWN(dev)->unit_serial[0]);
+       }
+       buf[off] = 0x2; /* ASCII */
+       buf[off+1] = 0x1; /* T10 Vendor ID */
+       buf[off+2] = 0x0;
+       memcpy((unsigned char *)&buf[off+4], "LIO-ORG", 8);
+       /* Extra Byte for NULL Terminator */
+       id_len++;
+       /* Identifier Length */
+       buf[off+3] = id_len;
+       /* Header size for Designation descriptor */
+       len += (id_len + 4);
+       off += (id_len + 4);
+       /*
+        * struct se_port is only set for INQUIRY VPD=1 through $FABRIC_MOD
+        */
+check_port:
+       port = lun->lun_sep;
+       if (port) {
+               struct t10_alua_lu_gp *lu_gp;
+               u32 padding, scsi_name_len;
+               u16 lu_gp_id = 0;
+               u16 tg_pt_gp_id = 0;
+               u16 tpgt;
+
+               tpg = port->sep_tpg;
+               /*
+                * Relative target port identifer, see spc4r17
+                * section 7.7.3.7
+                *
+                * Get the PROTOCOL IDENTIFIER as defined by spc4r17
+                * section 7.5.1 Table 362
+                */
+               if (((len + 4) + 8) > cmd->data_length) {
+                       len += 8;
+                       goto check_tpgi;
+               }
+               buf[off] =
+                       (TPG_TFO(tpg)->get_fabric_proto_ident(tpg) << 4);
+               buf[off++] |= 0x1; /* CODE SET == Binary */
+               buf[off] = 0x80; /* Set PIV=1 */
+               /* Set ASSOICATION == target port: 01b */
+               buf[off] |= 0x10;
+               /* DESIGNATOR TYPE == Relative target port identifer */
+               buf[off++] |= 0x4;
+               off++; /* Skip over Reserved */
+               buf[off++] = 4; /* DESIGNATOR LENGTH */
+               /* Skip over Obsolete field in RTPI payload
+                * in Table 472 */
+               off += 2;
+               buf[off++] = ((port->sep_rtpi >> 8) & 0xff);
+               buf[off++] = (port->sep_rtpi & 0xff);
+               len += 8; /* Header size + Designation descriptor */
+               /*
+                * Target port group identifier, see spc4r17
+                * section 7.7.3.8
+                *
+                * Get the PROTOCOL IDENTIFIER as defined by spc4r17
+                * section 7.5.1 Table 362
+                */
+check_tpgi:
+               if (T10_ALUA(dev->se_sub_dev)->alua_type !=
+                               SPC3_ALUA_EMULATED)
+                       goto check_scsi_name;
+
+               if (((len + 4) + 8) > cmd->data_length) {
+                       len += 8;
+                       goto check_lu_gp;
+               }
+               tg_pt_gp_mem = port->sep_alua_tg_pt_gp_mem;
+               if (!tg_pt_gp_mem)
+                       goto check_lu_gp;
+
+               spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+               tg_pt_gp = tg_pt_gp_mem->tg_pt_gp;
+               if (!(tg_pt_gp)) {
+                       spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+                       goto check_lu_gp;
+               }
+               tg_pt_gp_id = tg_pt_gp->tg_pt_gp_id;
+               spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+
+               buf[off] =
+                       (TPG_TFO(tpg)->get_fabric_proto_ident(tpg) << 4);
+               buf[off++] |= 0x1; /* CODE SET == Binary */
+               buf[off] = 0x80; /* Set PIV=1 */
+               /* Set ASSOICATION == target port: 01b */
+               buf[off] |= 0x10;
+               /* DESIGNATOR TYPE == Target port group identifier */
+               buf[off++] |= 0x5;
+               off++; /* Skip over Reserved */
+               buf[off++] = 4; /* DESIGNATOR LENGTH */
+               off += 2; /* Skip over Reserved Field */
+               buf[off++] = ((tg_pt_gp_id >> 8) & 0xff);
+               buf[off++] = (tg_pt_gp_id & 0xff);
+               len += 8; /* Header size + Designation descriptor */
+               /*
+                * Logical Unit Group identifier, see spc4r17
+                * section 7.7.3.8
+                */
+check_lu_gp:
+               if (((len + 4) + 8) > cmd->data_length) {
+                       len += 8;
+                       goto check_scsi_name;
+               }
+               lu_gp_mem = dev->dev_alua_lu_gp_mem;
+               if (!(lu_gp_mem))
+                       goto check_scsi_name;
+
+               spin_lock(&lu_gp_mem->lu_gp_mem_lock);
+               lu_gp = lu_gp_mem->lu_gp;
+               if (!(lu_gp)) {
+                       spin_unlock(&lu_gp_mem->lu_gp_mem_lock);
+                       goto check_scsi_name;
+               }
+               lu_gp_id = lu_gp->lu_gp_id;
+               spin_unlock(&lu_gp_mem->lu_gp_mem_lock);
+
+               buf[off++] |= 0x1; /* CODE SET == Binary */
+               /* DESIGNATOR TYPE == Logical Unit Group identifier */
+               buf[off++] |= 0x6;
+               off++; /* Skip over Reserved */
+               buf[off++] = 4; /* DESIGNATOR LENGTH */
+               off += 2; /* Skip over Reserved Field */
+               buf[off++] = ((lu_gp_id >> 8) & 0xff);
+               buf[off++] = (lu_gp_id & 0xff);
+               len += 8; /* Header size + Designation descriptor */
+               /*
+                * SCSI name string designator, see spc4r17
+                * section 7.7.3.11
+                *
+                * Get the PROTOCOL IDENTIFIER as defined by spc4r17
+                * section 7.5.1 Table 362
+                */
+check_scsi_name:
+               scsi_name_len = strlen(TPG_TFO(tpg)->tpg_get_wwn(tpg));
+               /* UTF-8 ",t,0x<16-bit TPGT>" + NULL Terminator */
+               scsi_name_len += 10;
+               /* Check for 4-byte padding */
+               padding = ((-scsi_name_len) & 3);
+               if (padding != 0)
+                       scsi_name_len += padding;
+               /* Header size + Designation descriptor */
+               scsi_name_len += 4;
+
+               if (((len + 4) + scsi_name_len) > cmd->data_length) {
+                       len += scsi_name_len;
+                       goto set_len;
+               }
+               buf[off] =
+                       (TPG_TFO(tpg)->get_fabric_proto_ident(tpg) << 4);
+               buf[off++] |= 0x3; /* CODE SET == UTF-8 */
+               buf[off] = 0x80; /* Set PIV=1 */
+               /* Set ASSOICATION == target port: 01b */
+               buf[off] |= 0x10;
+               /* DESIGNATOR TYPE == SCSI name string */
+               buf[off++] |= 0x8;
+               off += 2; /* Skip over Reserved and length */
+               /*
+                * SCSI name string identifer containing, $FABRIC_MOD
+                * dependent information.  For LIO-Target and iSCSI
+                * Target Port, this means "<iSCSI name>,t,0x<TPGT> in
+                * UTF-8 encoding.
+                */
+               tpgt = TPG_TFO(tpg)->tpg_get_tag(tpg);
+               scsi_name_len = sprintf(&buf[off], "%s,t,0x%04x",
+                                       TPG_TFO(tpg)->tpg_get_wwn(tpg), tpgt);
+               scsi_name_len += 1 /* Include  NULL terminator */;
+               /*
+                * The null-terminated, null-padded (see 4.4.2) SCSI
+                * NAME STRING field contains a UTF-8 format string.
+                * The number of bytes in the SCSI NAME STRING field
+                * (i.e., the value in the DESIGNATOR LENGTH field)
+                * shall be no larger than 256 and shall be a multiple
+                * of four.
+                */
+               if (padding)
+                       scsi_name_len += padding;
+
+               buf[off-1] = scsi_name_len;
+               off += scsi_name_len;
+               /* Header size + Designation descriptor */
+               len += (scsi_name_len + 4);
+       }
+set_len:
+       buf[2] = ((len >> 8) & 0xff);
+       buf[3] = (len & 0xff); /* Page Length for VPD 0x83 */
+       return 0;
+}
+
+/* Extended INQUIRY Data VPD Page */
+static int
+target_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf)
+{
+       if (cmd->data_length < 60)
+               return 0;
+
+       buf[1] = 0x86;
+       buf[2] = 0x3c;
+       /* Set HEADSUP, ORDSUP, SIMPSUP */
+       buf[5] = 0x07;
+
+       /* If WriteCache emulation is enabled, set V_SUP */
+       if (DEV_ATTRIB(SE_DEV(cmd))->emulate_write_cache > 0)
+               buf[6] = 0x01;
+       return 0;
+}
+
+/* Block Limits VPD page */
+static int
+target_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
+{
+       struct se_device *dev = SE_DEV(cmd);
+       int have_tp = 0;
+
+       /*
+        * Following sbc3r22 section 6.5.3 Block Limits VPD page, when
+        * emulate_tpu=1 or emulate_tpws=1 we will be expect a
+        * different page length for Thin Provisioning.
+        */
+       if (DEV_ATTRIB(dev)->emulate_tpu || DEV_ATTRIB(dev)->emulate_tpws)
+               have_tp = 1;
+
+       if (cmd->data_length < (0x10 + 4)) {
+               printk(KERN_INFO "Received data_length: %u"
+                       " too small for EVPD 0xb0\n",
+                       cmd->data_length);
+               return -1;
+       }
+
+       if (have_tp && cmd->data_length < (0x3c + 4)) {
+               printk(KERN_INFO "Received data_length: %u"
+                       " too small for TPE=1 EVPD 0xb0\n",
+                       cmd->data_length);
+               have_tp = 0;
+       }
+
+       buf[0] = dev->transport->get_device_type(dev);
+       buf[1] = 0xb0;
+       buf[3] = have_tp ? 0x3c : 0x10;
+
+       /*
+        * Set OPTIMAL TRANSFER LENGTH GRANULARITY
+        */
+       put_unaligned_be16(1, &buf[6]);
+
+       /*
+        * Set MAXIMUM TRANSFER LENGTH
+        */
+       put_unaligned_be32(DEV_ATTRIB(dev)->max_sectors, &buf[8]);
+
+       /*
+        * Set OPTIMAL TRANSFER LENGTH
+        */
+       put_unaligned_be32(DEV_ATTRIB(dev)->optimal_sectors, &buf[12]);
+
+       /*
+        * Exit now if we don't support TP or the initiator sent a too
+        * short buffer.
+        */
+       if (!have_tp || cmd->data_length < (0x3c + 4))
+               return 0;
+
+       /*
+        * Set MAXIMUM UNMAP LBA COUNT
+        */
+       put_unaligned_be32(DEV_ATTRIB(dev)->max_unmap_lba_count, &buf[20]);
+
+       /*
+        * Set MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT
+        */
+       put_unaligned_be32(DEV_ATTRIB(dev)->max_unmap_block_desc_count,
+                          &buf[24]);
+
+       /*
+        * Set OPTIMAL UNMAP GRANULARITY
+        */
+       put_unaligned_be32(DEV_ATTRIB(dev)->unmap_granularity, &buf[28]);
+
+       /*
+        * UNMAP GRANULARITY ALIGNMENT
+        */
+       put_unaligned_be32(DEV_ATTRIB(dev)->unmap_granularity_alignment,
+                          &buf[32]);
+       if (DEV_ATTRIB(dev)->unmap_granularity_alignment != 0)
+               buf[32] |= 0x80; /* Set the UGAVALID bit */
+
+       return 0;
+}
+
+/* Thin Provisioning VPD */
+static int
+target_emulate_evpd_b2(struct se_cmd *cmd, unsigned char *buf)
+{
+       struct se_device *dev = SE_DEV(cmd);
+
+       /*
+        * From sbc3r22 section 6.5.4 Thin Provisioning VPD page:
+        *
+        * The PAGE LENGTH field is defined in SPC-4. If the DP bit is set to
+        * zero, then the page length shall be set to 0004h.  If the DP bit
+        * is set to one, then the page length shall be set to the value
+        * defined in table 162.
+        */
+       buf[0] = dev->transport->get_device_type(dev);
+       buf[1] = 0xb2;
+
+       /*
+        * Set Hardcoded length mentioned above for DP=0
+        */
+       put_unaligned_be16(0x0004, &buf[2]);
+
+       /*
+        * The THRESHOLD EXPONENT field indicates the threshold set size in
+        * LBAs as a power of 2 (i.e., the threshold set size is equal to
+        * 2(threshold exponent)).
+        *
+        * Note that this is currently set to 0x00 as mkp says it will be
+        * changing again.  We can enable this once it has settled in T10
+        * and is actually used by Linux/SCSI ML code.
+        */
+       buf[4] = 0x00;
+
+       /*
+        * A TPU bit set to one indicates that the device server supports
+        * the UNMAP command (see 5.25). A TPU bit set to zero indicates
+        * that the device server does not support the UNMAP command.
+        */
+       if (DEV_ATTRIB(dev)->emulate_tpu != 0)
+               buf[5] = 0x80;
+
+       /*
+        * A TPWS bit set to one indicates that the device server supports
+        * the use of the WRITE SAME (16) command (see 5.42) to unmap LBAs.
+        * A TPWS bit set to zero indicates that the device server does not
+        * support the use of the WRITE SAME (16) command to unmap LBAs.
+        */
+       if (DEV_ATTRIB(dev)->emulate_tpws != 0)
+               buf[5] |= 0x40;
+
+       return 0;
+}
+
+static int
+target_emulate_inquiry(struct se_cmd *cmd)
+{
+       struct se_device *dev = SE_DEV(cmd);
+       unsigned char *buf = cmd->t_task->t_task_buf;
+       unsigned char *cdb = cmd->t_task->t_task_cdb;
+
+       if (!(cdb[1] & 0x1))
+               return target_emulate_inquiry_std(cmd);
+
+       /*
+        * Make sure we at least have 4 bytes of INQUIRY response
+        * payload for 0x00 going back for EVPD=1.  Note that 0x80
+        * and 0x83 will check for enough payload data length and
+        * jump to set_len: label when there is not enough inquiry EVPD
+        * payload length left for the next outgoing EVPD metadata
+        */
+       if (cmd->data_length < 4) {
+               printk(KERN_ERR "SCSI Inquiry payload length: %u"
+                       " too small for EVPD=1\n", cmd->data_length);
+               return -1;
+       }
+       buf[0] = dev->transport->get_device_type(dev);
+
+       switch (cdb[2]) {
+       case 0x00:
+               return target_emulate_evpd_00(cmd, buf);
+       case 0x80:
+               return target_emulate_evpd_80(cmd, buf);
+       case 0x83:
+               return target_emulate_evpd_83(cmd, buf);
+       case 0x86:
+               return target_emulate_evpd_86(cmd, buf);
+       case 0xb0:
+               return target_emulate_evpd_b0(cmd, buf);
+       case 0xb2:
+               return target_emulate_evpd_b2(cmd, buf);
+       default:
+               printk(KERN_ERR "Unknown VPD Code: 0x%02x\n", cdb[2]);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+target_emulate_readcapacity(struct se_cmd *cmd)
+{
+       struct se_device *dev = SE_DEV(cmd);
+       unsigned char *buf = cmd->t_task->t_task_buf;
+       u32 blocks = dev->transport->get_blocks(dev);
+
+       buf[0] = (blocks >> 24) & 0xff;
+       buf[1] = (blocks >> 16) & 0xff;
+       buf[2] = (blocks >> 8) & 0xff;
+       buf[3] = blocks & 0xff;
+       buf[4] = (DEV_ATTRIB(dev)->block_size >> 24) & 0xff;
+       buf[5] = (DEV_ATTRIB(dev)->block_size >> 16) & 0xff;
+       buf[6] = (DEV_ATTRIB(dev)->block_size >> 8) & 0xff;
+       buf[7] = DEV_ATTRIB(dev)->block_size & 0xff;
+       /*
+        * Set max 32-bit blocks to signal SERVICE ACTION READ_CAPACITY_16
+       */
+       if (DEV_ATTRIB(dev)->emulate_tpu || DEV_ATTRIB(dev)->emulate_tpws)
+               put_unaligned_be32(0xFFFFFFFF, &buf[0]);
+
+       return 0;
+}
+
+static int
+target_emulate_readcapacity_16(struct se_cmd *cmd)
+{
+       struct se_device *dev = SE_DEV(cmd);
+       unsigned char *buf = cmd->t_task->t_task_buf;
+       unsigned long long blocks = dev->transport->get_blocks(dev);
+
+       buf[0] = (blocks >> 56) & 0xff;
+       buf[1] = (blocks >> 48) & 0xff;
+       buf[2] = (blocks >> 40) & 0xff;
+       buf[3] = (blocks >> 32) & 0xff;
+       buf[4] = (blocks >> 24) & 0xff;
+       buf[5] = (blocks >> 16) & 0xff;
+       buf[6] = (blocks >> 8) & 0xff;
+       buf[7] = blocks & 0xff;
+       buf[8] = (DEV_ATTRIB(dev)->block_size >> 24) & 0xff;
+       buf[9] = (DEV_ATTRIB(dev)->block_size >> 16) & 0xff;
+       buf[10] = (DEV_ATTRIB(dev)->block_size >> 8) & 0xff;
+       buf[11] = DEV_ATTRIB(dev)->block_size & 0xff;
+       /*
+        * Set Thin Provisioning Enable bit following sbc3r22 in section
+        * READ CAPACITY (16) byte 14 if emulate_tpu or emulate_tpws is enabled.
+        */
+       if (DEV_ATTRIB(dev)->emulate_tpu || DEV_ATTRIB(dev)->emulate_tpws)
+               buf[14] = 0x80;
+
+       return 0;
+}
+
+static int
+target_modesense_rwrecovery(unsigned char *p)
+{
+       p[0] = 0x01;
+       p[1] = 0x0a;
+
+       return 12;
+}
+
+static int
+target_modesense_control(struct se_device *dev, unsigned char *p)
+{
+       p[0] = 0x0a;
+       p[1] = 0x0a;
+       p[2] = 2;
+       /*
+        * From spc4r17, section 7.4.6 Control mode Page
+        *
+        * Unit Attention interlocks control (UN_INTLCK_CTRL) to code 00b
+        *
+        * 00b: The logical unit shall clear any unit attention condition
+        * reported in the same I_T_L_Q nexus transaction as a CHECK CONDITION
+        * status and shall not establish a unit attention condition when a com-
+        * mand is completed with BUSY, TASK SET FULL, or RESERVATION CONFLICT
+        * status.
+        *
+        * 10b: The logical unit shall not clear any unit attention condition
+        * reported in the same I_T_L_Q nexus transaction as a CHECK CONDITION
+        * status and shall not establish a unit attention condition when
+        * a command is completed with BUSY, TASK SET FULL, or RESERVATION
+        * CONFLICT status.
+        *
+        * 11b a The logical unit shall not clear any unit attention condition
+        * reported in the same I_T_L_Q nexus transaction as a CHECK CONDITION
+        * status and shall establish a unit attention condition for the
+        * initiator port associated with the I_T nexus on which the BUSY,
+        * TASK SET FULL, or RESERVATION CONFLICT status is being returned.
+        * Depending on the status, the additional sense code shall be set to
+        * PREVIOUS BUSY STATUS, PREVIOUS TASK SET FULL STATUS, or PREVIOUS
+        * RESERVATION CONFLICT STATUS. Until it is cleared by a REQUEST SENSE
+        * command, a unit attention condition shall be established only once
+        * for a BUSY, TASK SET FULL, or RESERVATION CONFLICT status regardless
+        * to the number of commands completed with one of those status codes.
+        */
+       p[4] = (DEV_ATTRIB(dev)->emulate_ua_intlck_ctrl == 2) ? 0x30 :
+              (DEV_ATTRIB(dev)->emulate_ua_intlck_ctrl == 1) ? 0x20 : 0x00;
+       /*
+        * From spc4r17, section 7.4.6 Control mode Page
+        *
+        * Task Aborted Status (TAS) bit set to zero.
+        *
+        * A task aborted status (TAS) bit set to zero specifies that aborted
+        * tasks shall be terminated by the device server without any response
+        * to the application client. A TAS bit set to one specifies that tasks
+        * aborted by the actions of an I_T nexus other than the I_T nexus on
+        * which the command was received shall be completed with TASK ABORTED
+        * status (see SAM-4).
+        */
+       p[5] = (DEV_ATTRIB(dev)->emulate_tas) ? 0x40 : 0x00;
+       p[8] = 0xff;
+       p[9] = 0xff;
+       p[11] = 30;
+
+       return 12;
+}
+
+static int
+target_modesense_caching(struct se_device *dev, unsigned char *p)
+{
+       p[0] = 0x08;
+       p[1] = 0x12;
+       if (DEV_ATTRIB(dev)->emulate_write_cache > 0)
+               p[2] = 0x04; /* Write Cache Enable */
+       p[12] = 0x20; /* Disabled Read Ahead */
+
+       return 20;
+}
+
+static void
+target_modesense_write_protect(unsigned char *buf, int type)
+{
+       /*
+        * I believe that the WP bit (bit 7) in the mode header is the same for
+        * all device types..
+        */
+       switch (type) {
+       case TYPE_DISK:
+       case TYPE_TAPE:
+       default:
+               buf[0] |= 0x80; /* WP bit */
+               break;
+       }
+}
+
+static void
+target_modesense_dpofua(unsigned char *buf, int type)
+{
+       switch (type) {
+       case TYPE_DISK:
+               buf[0] |= 0x10; /* DPOFUA bit */
+               break;
+       default:
+               break;
+       }
+}
+
+static int
+target_emulate_modesense(struct se_cmd *cmd, int ten)
+{
+       struct se_device *dev = SE_DEV(cmd);
+       char *cdb = cmd->t_task->t_task_cdb;
+       unsigned char *rbuf = cmd->t_task->t_task_buf;
+       int type = dev->transport->get_device_type(dev);
+       int offset = (ten) ? 8 : 4;
+       int length = 0;
+       unsigned char buf[SE_MODE_PAGE_BUF];
+
+       memset(buf, 0, SE_MODE_PAGE_BUF);
+
+       switch (cdb[2] & 0x3f) {
+       case 0x01:
+               length = target_modesense_rwrecovery(&buf[offset]);
+               break;
+       case 0x08:
+               length = target_modesense_caching(dev, &buf[offset]);
+               break;
+       case 0x0a:
+               length = target_modesense_control(dev, &buf[offset]);
+               break;
+       case 0x3f:
+               length = target_modesense_rwrecovery(&buf[offset]);
+               length += target_modesense_caching(dev, &buf[offset+length]);
+               length += target_modesense_control(dev, &buf[offset+length]);
+               break;
+       default:
+               printk(KERN_ERR "Got Unknown Mode Page: 0x%02x\n",
+                               cdb[2] & 0x3f);
+               return PYX_TRANSPORT_UNKNOWN_MODE_PAGE;
+       }
+       offset += length;
+
+       if (ten) {
+               offset -= 2;
+               buf[0] = (offset >> 8) & 0xff;
+               buf[1] = offset & 0xff;
+
+               if ((SE_LUN(cmd)->lun_access & TRANSPORT_LUNFLAGS_READ_ONLY) ||
+                   (cmd->se_deve &&
+                   (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY)))
+                       target_modesense_write_protect(&buf[3], type);
+
+               if ((DEV_ATTRIB(dev)->emulate_write_cache > 0) &&
+                   (DEV_ATTRIB(dev)->emulate_fua_write > 0))
+                       target_modesense_dpofua(&buf[3], type);
+
+               if ((offset + 2) > cmd->data_length)
+                       offset = cmd->data_length;
+
+       } else {
+               offset -= 1;
+               buf[0] = offset & 0xff;
+
+               if ((SE_LUN(cmd)->lun_access & TRANSPORT_LUNFLAGS_READ_ONLY) ||
+                   (cmd->se_deve &&
+                   (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY)))
+                       target_modesense_write_protect(&buf[2], type);
+
+               if ((DEV_ATTRIB(dev)->emulate_write_cache > 0) &&
+                   (DEV_ATTRIB(dev)->emulate_fua_write > 0))
+                       target_modesense_dpofua(&buf[2], type);
+
+               if ((offset + 1) > cmd->data_length)
+                       offset = cmd->data_length;
+       }
+       memcpy(rbuf, buf, offset);
+
+       return 0;
+}
+
+static int
+target_emulate_request_sense(struct se_cmd *cmd)
+{
+       unsigned char *cdb = cmd->t_task->t_task_cdb;
+       unsigned char *buf = cmd->t_task->t_task_buf;
+       u8 ua_asc = 0, ua_ascq = 0;
+
+       if (cdb[1] & 0x01) {
+               printk(KERN_ERR "REQUEST_SENSE description emulation not"
+                       " supported\n");
+               return PYX_TRANSPORT_INVALID_CDB_FIELD;
+       }
+       if (!(core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq))) {
+               /*
+                * CURRENT ERROR, UNIT ATTENTION
+                */
+               buf[0] = 0x70;
+               buf[SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION;
+               /*
+                * Make sure request data length is enough for additional
+                * sense data.
+                */
+               if (cmd->data_length <= 18) {
+                       buf[7] = 0x00;
+                       return 0;
+               }
+               /*
+                * The Additional Sense Code (ASC) from the UNIT ATTENTION
+                */
+               buf[SPC_ASC_KEY_OFFSET] = ua_asc;
+               buf[SPC_ASCQ_KEY_OFFSET] = ua_ascq;
+               buf[7] = 0x0A;
+       } else {
+               /*
+                * CURRENT ERROR, NO SENSE
+                */
+               buf[0] = 0x70;
+               buf[SPC_SENSE_KEY_OFFSET] = NO_SENSE;
+               /*
+                * Make sure request data length is enough for additional
+                * sense data.
+                */
+               if (cmd->data_length <= 18) {
+                       buf[7] = 0x00;
+                       return 0;
+               }
+               /*
+                * NO ADDITIONAL SENSE INFORMATION
+                */
+               buf[SPC_ASC_KEY_OFFSET] = 0x00;
+               buf[7] = 0x0A;
+       }
+
+       return 0;
+}
+
+/*
+ * Used for TCM/IBLOCK and TCM/FILEIO for block/blk-lib.c level discard support.
+ * Note this is not used for TCM/pSCSI passthrough
+ */
+static int
+target_emulate_unmap(struct se_task *task)
+{
+       struct se_cmd *cmd = TASK_CMD(task);
+       struct se_device *dev = SE_DEV(cmd);
+       unsigned char *buf = cmd->t_task->t_task_buf, *ptr = NULL;
+       unsigned char *cdb = &cmd->t_task->t_task_cdb[0];
+       sector_t lba;
+       unsigned int size = cmd->data_length, range;
+       int ret, offset;
+       unsigned short dl, bd_dl;
+
+       /* First UNMAP block descriptor starts at 8 byte offset */
+       offset = 8;
+       size -= 8;
+       dl = get_unaligned_be16(&cdb[0]);
+       bd_dl = get_unaligned_be16(&cdb[2]);
+       ptr = &buf[offset];
+       printk(KERN_INFO "UNMAP: Sub: %s Using dl: %hu bd_dl: %hu size: %hu"
+               " ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr);
+
+       while (size) {
+               lba = get_unaligned_be64(&ptr[0]);
+               range = get_unaligned_be32(&ptr[8]);
+               printk(KERN_INFO "UNMAP: Using lba: %llu and range: %u\n",
+                                (unsigned long long)lba, range);
+
+               ret = dev->transport->do_discard(dev, lba, range);
+               if (ret < 0) {
+                       printk(KERN_ERR "blkdev_issue_discard() failed: %d\n",
+                                       ret);
+                       return -1;
+               }
+
+               ptr += 16;
+               size -= 16;
+       }
+
+       task->task_scsi_status = GOOD;
+       transport_complete_task(task, 1);
+       return 0;
+}
+
+/*
+ * Used for TCM/IBLOCK and TCM/FILEIO for block/blk-lib.c level discard support.
+ * Note this is not used for TCM/pSCSI passthrough
+ */
+static int
+target_emulate_write_same(struct se_task *task)
+{
+       struct se_cmd *cmd = TASK_CMD(task);
+       struct se_device *dev = SE_DEV(cmd);
+       sector_t lba = cmd->t_task->t_task_lba;
+       unsigned int range;
+       int ret;
+
+       range = (cmd->data_length / DEV_ATTRIB(dev)->block_size);
+
+       printk(KERN_INFO "WRITE_SAME UNMAP: LBA: %llu Range: %u\n",
+                        (unsigned long long)lba, range);
+
+       ret = dev->transport->do_discard(dev, lba, range);
+       if (ret < 0) {
+               printk(KERN_INFO "blkdev_issue_discard() failed for WRITE_SAME\n");
+               return -1;
+       }
+
+       task->task_scsi_status = GOOD;
+       transport_complete_task(task, 1);
+       return 0;
+}
+
+int
+transport_emulate_control_cdb(struct se_task *task)
+{
+       struct se_cmd *cmd = TASK_CMD(task);
+       struct se_device *dev = SE_DEV(cmd);
+       unsigned short service_action;
+       int ret = 0;
+
+       switch (cmd->t_task->t_task_cdb[0]) {
+       case INQUIRY:
+               ret = target_emulate_inquiry(cmd);
+               break;
+       case READ_CAPACITY:
+               ret = target_emulate_readcapacity(cmd);
+               break;
+       case MODE_SENSE:
+               ret = target_emulate_modesense(cmd, 0);
+               break;
+       case MODE_SENSE_10:
+               ret = target_emulate_modesense(cmd, 1);
+               break;
+       case SERVICE_ACTION_IN:
+               switch (cmd->t_task->t_task_cdb[1] & 0x1f) {
+               case SAI_READ_CAPACITY_16:
+                       ret = target_emulate_readcapacity_16(cmd);
+                       break;
+               default:
+                       printk(KERN_ERR "Unsupported SA: 0x%02x\n",
+                               cmd->t_task->t_task_cdb[1] & 0x1f);
+                       return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+               }
+               break;
+       case REQUEST_SENSE:
+               ret = target_emulate_request_sense(cmd);
+               break;
+       case UNMAP:
+               if (!dev->transport->do_discard) {
+                       printk(KERN_ERR "UNMAP emulation not supported for: %s\n",
+                                       dev->transport->name);
+                       return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+               }
+               ret = target_emulate_unmap(task);
+               break;
+       case WRITE_SAME_16:
+               if (!dev->transport->do_discard) {
+                       printk(KERN_ERR "WRITE_SAME_16 emulation not supported"
+                                       " for: %s\n", dev->transport->name);
+                       return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+               }
+               ret = target_emulate_write_same(task);
+               break;
+       case VARIABLE_LENGTH_CMD:
+               service_action =
+                       get_unaligned_be16(&cmd->t_task->t_task_cdb[8]);
+               switch (service_action) {
+               case WRITE_SAME_32:
+                       if (!dev->transport->do_discard) {
+                               printk(KERN_ERR "WRITE_SAME_32 SA emulation not"
+                                       " supported for: %s\n",
+                                       dev->transport->name);
+                               return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+                       }
+                       ret = target_emulate_write_same(task);
+                       break;
+               default:
+                       printk(KERN_ERR "Unsupported VARIABLE_LENGTH_CMD SA:"
+                                       " 0x%02x\n", service_action);
+                       break;
+               }
+               break;
+       case SYNCHRONIZE_CACHE:
+       case 0x91: /* SYNCHRONIZE_CACHE_16: */
+               if (!dev->transport->do_sync_cache) {
+                       printk(KERN_ERR
+                               "SYNCHRONIZE_CACHE emulation not supported"
+                               " for: %s\n", dev->transport->name);
+                       return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+               }
+               dev->transport->do_sync_cache(task);
+               break;
+       case ALLOW_MEDIUM_REMOVAL:
+       case ERASE:
+       case REZERO_UNIT:
+       case SEEK_10:
+       case SPACE:
+       case START_STOP:
+       case TEST_UNIT_READY:
+       case VERIFY:
+       case WRITE_FILEMARKS:
+               break;
+       default:
+               printk(KERN_ERR "Unsupported SCSI Opcode: 0x%02x for %s\n",
+                       cmd->t_task->t_task_cdb[0], dev->transport->name);
+               return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+       }
+
+       if (ret < 0)
+               return ret;
+       task->task_scsi_status = GOOD;
+       transport_complete_task(task, 1);
+
+       return PYX_TRANSPORT_SENT_TO_TRANSPORT;
+}
diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c
new file mode 100644 (file)
index 0000000..2764510
--- /dev/null
@@ -0,0 +1,3225 @@
+/*******************************************************************************
+ * Filename:  target_core_configfs.c
+ *
+ * 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
+ *
+ * Nicholas A. Bellinger <nab@kernel.org>
+ *
+ * based on configfs Copyright (C) 2005 Oracle.  All rights reserved.
+ *
+ * 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/version.h>
+#include <generated/utsrelease.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/unistd.h>
+#include <linux/string.h>
+#include <linux/parser.h>
+#include <linux/syscalls.h>
+#include <linux/configfs.h>
+#include <linux/proc_fs.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_device.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_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "target_core_alua.h"
+#include "target_core_hba.h"
+#include "target_core_pr.h"
+#include "target_core_rd.h"
+
+static struct list_head g_tf_list;
+static struct mutex g_tf_lock;
+
+struct target_core_configfs_attribute {
+       struct configfs_attribute attr;
+       ssize_t (*show)(void *, char *);
+       ssize_t (*store)(void *, const char *, size_t);
+};
+
+static inline struct se_hba *
+item_to_hba(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct se_hba, hba_group);
+}
+
+/*
+ * Attributes for /sys/kernel/config/target/
+ */
+static ssize_t target_core_attr_show(struct config_item *item,
+                                     struct configfs_attribute *attr,
+                                     char *page)
+{
+       return sprintf(page, "Target Engine Core ConfigFS Infrastructure %s"
+               " on %s/%s on "UTS_RELEASE"\n", TARGET_CORE_CONFIGFS_VERSION,
+               utsname()->sysname, utsname()->machine);
+}
+
+static struct configfs_item_operations target_core_fabric_item_ops = {
+       .show_attribute = target_core_attr_show,
+};
+
+static struct configfs_attribute target_core_item_attr_version = {
+       .ca_owner       = THIS_MODULE,
+       .ca_name        = "version",
+       .ca_mode        = S_IRUGO,
+};
+
+static struct target_fabric_configfs *target_core_get_fabric(
+       const char *name)
+{
+       struct target_fabric_configfs *tf;
+
+       if (!(name))
+               return NULL;
+
+       mutex_lock(&g_tf_lock);
+       list_for_each_entry(tf, &g_tf_list, tf_list) {
+               if (!(strcmp(tf->tf_name, name))) {
+                       atomic_inc(&tf->tf_access_cnt);
+                       mutex_unlock(&g_tf_lock);
+                       return tf;
+               }
+       }
+       mutex_unlock(&g_tf_lock);
+
+       return NULL;
+}
+
+/*
+ * Called from struct target_core_group_ops->make_group()
+ */
+static struct config_group *target_core_register_fabric(
+       struct config_group *group,
+       const char *name)
+{
+       struct target_fabric_configfs *tf;
+       int ret;
+
+       printk(KERN_INFO "Target_Core_ConfigFS: REGISTER -> group: %p name:"
+                       " %s\n", group, name);
+       /*
+        * Ensure that TCM subsystem plugins are loaded at this point for
+        * using the RAMDISK_DR virtual LUN 0 and all other struct se_port
+        * LUN symlinks.
+        */
+       if (transport_subsystem_check_init() < 0)
+               return ERR_PTR(-EINVAL);
+
+       /*
+        * Below are some hardcoded request_module() calls to automatically
+        * local fabric modules when the following is called:
+        *
+        * mkdir -p /sys/kernel/config/target/$MODULE_NAME
+        *
+        * Note that this does not limit which TCM fabric module can be
+        * registered, but simply provids auto loading logic for modules with
+        * mkdir(2) system calls with known TCM fabric modules.
+        */
+       if (!(strncmp(name, "iscsi", 5))) {
+               /*
+                * Automatically load the LIO Target fabric module when the
+                * following is called:
+                *
+                * mkdir -p $CONFIGFS/target/iscsi
+                */
+               ret = request_module("iscsi_target_mod");
+               if (ret < 0) {
+                       printk(KERN_ERR "request_module() failed for"
+                               " iscsi_target_mod.ko: %d\n", ret);
+                       return ERR_PTR(-EINVAL);
+               }
+       } else if (!(strncmp(name, "loopback", 8))) {
+               /*
+                * Automatically load the tcm_loop fabric module when the
+                * following is called:
+                *
+                * mkdir -p $CONFIGFS/target/loopback
+                */
+               ret = request_module("tcm_loop");
+               if (ret < 0) {
+                       printk(KERN_ERR "request_module() failed for"
+                               " tcm_loop.ko: %d\n", ret);
+                       return ERR_PTR(-EINVAL);
+               }
+       }
+
+       tf = target_core_get_fabric(name);
+       if (!(tf)) {
+               printk(KERN_ERR "target_core_get_fabric() failed for %s\n",
+                       name);
+               return ERR_PTR(-EINVAL);
+       }
+       printk(KERN_INFO "Target_Core_ConfigFS: REGISTER -> Located fabric:"
+                       " %s\n", tf->tf_name);
+       /*
+        * On a successful target_core_get_fabric() look, the returned
+        * struct target_fabric_configfs *tf will contain a usage reference.
+        */
+       printk(KERN_INFO "Target_Core_ConfigFS: REGISTER tfc_wwn_cit -> %p\n",
+                       &TF_CIT_TMPL(tf)->tfc_wwn_cit);
+
+       tf->tf_group.default_groups = tf->tf_default_groups;
+       tf->tf_group.default_groups[0] = &tf->tf_disc_group;
+       tf->tf_group.default_groups[1] = NULL;
+
+       config_group_init_type_name(&tf->tf_group, name,
+                       &TF_CIT_TMPL(tf)->tfc_wwn_cit);
+       config_group_init_type_name(&tf->tf_disc_group, "discovery_auth",
+                       &TF_CIT_TMPL(tf)->tfc_discovery_cit);
+
+       printk(KERN_INFO "Target_Core_ConfigFS: REGISTER -> Allocated Fabric:"
+                       " %s\n", tf->tf_group.cg_item.ci_name);
+       /*
+        * Setup tf_ops.tf_subsys pointer for usage with configfs_depend_item()
+        */
+       tf->tf_ops.tf_subsys = tf->tf_subsys;
+       tf->tf_fabric = &tf->tf_group.cg_item;
+       printk(KERN_INFO "Target_Core_ConfigFS: REGISTER -> Set tf->tf_fabric"
+                       " for %s\n", name);
+
+       return &tf->tf_group;
+}
+
+/*
+ * Called from struct target_core_group_ops->drop_item()
+ */
+static void target_core_deregister_fabric(
+       struct config_group *group,
+       struct config_item *item)
+{
+       struct target_fabric_configfs *tf = container_of(
+               to_config_group(item), struct target_fabric_configfs, tf_group);
+       struct config_group *tf_group;
+       struct config_item *df_item;
+       int i;
+
+       printk(KERN_INFO "Target_Core_ConfigFS: DEREGISTER -> Looking up %s in"
+               " tf list\n", config_item_name(item));
+
+       printk(KERN_INFO "Target_Core_ConfigFS: DEREGISTER -> located fabric:"
+                       " %s\n", tf->tf_name);
+       atomic_dec(&tf->tf_access_cnt);
+
+       printk(KERN_INFO "Target_Core_ConfigFS: DEREGISTER -> Releasing"
+                       " tf->tf_fabric for %s\n", tf->tf_name);
+       tf->tf_fabric = NULL;
+
+       printk(KERN_INFO "Target_Core_ConfigFS: DEREGISTER -> Releasing ci"
+                       " %s\n", config_item_name(item));
+
+       tf_group = &tf->tf_group;
+       for (i = 0; tf_group->default_groups[i]; i++) {
+               df_item = &tf_group->default_groups[i]->cg_item;
+               tf_group->default_groups[i] = NULL;
+               config_item_put(df_item);
+       }
+       config_item_put(item);
+}
+
+static struct configfs_group_operations target_core_fabric_group_ops = {
+       .make_group     = &target_core_register_fabric,
+       .drop_item      = &target_core_deregister_fabric,
+};
+
+/*
+ * All item attributes appearing in /sys/kernel/target/ appear here.
+ */
+static struct configfs_attribute *target_core_fabric_item_attrs[] = {
+       &target_core_item_attr_version,
+       NULL,
+};
+
+/*
+ * Provides Fabrics Groups and Item Attributes for /sys/kernel/config/target/
+ */
+static struct config_item_type target_core_fabrics_item = {
+       .ct_item_ops    = &target_core_fabric_item_ops,
+       .ct_group_ops   = &target_core_fabric_group_ops,
+       .ct_attrs       = target_core_fabric_item_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct configfs_subsystem target_core_fabrics = {
+       .su_group = {
+               .cg_item = {
+                       .ci_namebuf = "target",
+                       .ci_type = &target_core_fabrics_item,
+               },
+       },
+};
+
+static struct configfs_subsystem *target_core_subsystem[] = {
+       &target_core_fabrics,
+       NULL,
+};
+
+/*##############################################################################
+// Start functions called by external Target Fabrics Modules
+//############################################################################*/
+
+/*
+ * First function called by fabric modules to:
+ *
+ * 1) Allocate a struct target_fabric_configfs and save the *fabric_cit pointer.
+ * 2) Add struct target_fabric_configfs to g_tf_list
+ * 3) Return struct target_fabric_configfs to fabric module to be passed
+ *    into target_fabric_configfs_register().
+ */
+struct target_fabric_configfs *target_fabric_configfs_init(
+       struct module *fabric_mod,
+       const char *name)
+{
+       struct target_fabric_configfs *tf;
+
+       if (!(fabric_mod)) {
+               printk(KERN_ERR "Missing struct module *fabric_mod pointer\n");
+               return NULL;
+       }
+       if (!(name)) {
+               printk(KERN_ERR "Unable to locate passed fabric name\n");
+               return NULL;
+       }
+       if (strlen(name) > TARGET_FABRIC_NAME_SIZE) {
+               printk(KERN_ERR "Passed name: %s exceeds TARGET_FABRIC"
+                       "_NAME_SIZE\n", name);
+               return NULL;
+       }
+
+       tf = kzalloc(sizeof(struct target_fabric_configfs), GFP_KERNEL);
+       if (!(tf))
+               return ERR_PTR(-ENOMEM);
+
+       INIT_LIST_HEAD(&tf->tf_list);
+       atomic_set(&tf->tf_access_cnt, 0);
+       /*
+        * Setup the default generic struct config_item_type's (cits) in
+        * struct target_fabric_configfs->tf_cit_tmpl
+        */
+       tf->tf_module = fabric_mod;
+       target_fabric_setup_cits(tf);
+
+       tf->tf_subsys = target_core_subsystem[0];
+       snprintf(tf->tf_name, TARGET_FABRIC_NAME_SIZE, "%s", name);
+
+       mutex_lock(&g_tf_lock);
+       list_add_tail(&tf->tf_list, &g_tf_list);
+       mutex_unlock(&g_tf_lock);
+
+       printk(KERN_INFO "<<<<<<<<<<<<<<<<<<<<<< BEGIN FABRIC API >>>>>>>>"
+                       ">>>>>>>>>>>>>>\n");
+       printk(KERN_INFO "Initialized struct target_fabric_configfs: %p for"
+                       " %s\n", tf, tf->tf_name);
+       return tf;
+}
+EXPORT_SYMBOL(target_fabric_configfs_init);
+
+/*
+ * Called by fabric plugins after FAILED target_fabric_configfs_register() call.
+ */
+void target_fabric_configfs_free(
+       struct target_fabric_configfs *tf)
+{
+       mutex_lock(&g_tf_lock);
+       list_del(&tf->tf_list);
+       mutex_unlock(&g_tf_lock);
+
+       kfree(tf);
+}
+EXPORT_SYMBOL(target_fabric_configfs_free);
+
+/*
+ * Perform a sanity check of the passed tf->tf_ops before completing
+ * TCM fabric module registration.
+ */
+static int target_fabric_tf_ops_check(
+       struct target_fabric_configfs *tf)
+{
+       struct target_core_fabric_ops *tfo = &tf->tf_ops;
+
+       if (!(tfo->get_fabric_name)) {
+               printk(KERN_ERR "Missing tfo->get_fabric_name()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->get_fabric_proto_ident)) {
+               printk(KERN_ERR "Missing tfo->get_fabric_proto_ident()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->tpg_get_wwn)) {
+               printk(KERN_ERR "Missing tfo->tpg_get_wwn()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->tpg_get_tag)) {
+               printk(KERN_ERR "Missing tfo->tpg_get_tag()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->tpg_get_default_depth)) {
+               printk(KERN_ERR "Missing tfo->tpg_get_default_depth()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->tpg_get_pr_transport_id)) {
+               printk(KERN_ERR "Missing tfo->tpg_get_pr_transport_id()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->tpg_get_pr_transport_id_len)) {
+               printk(KERN_ERR "Missing tfo->tpg_get_pr_transport_id_len()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->tpg_check_demo_mode)) {
+               printk(KERN_ERR "Missing tfo->tpg_check_demo_mode()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->tpg_check_demo_mode_cache)) {
+               printk(KERN_ERR "Missing tfo->tpg_check_demo_mode_cache()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->tpg_check_demo_mode_write_protect)) {
+               printk(KERN_ERR "Missing tfo->tpg_check_demo_mode_write_protect()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->tpg_check_prod_mode_write_protect)) {
+               printk(KERN_ERR "Missing tfo->tpg_check_prod_mode_write_protect()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->tpg_alloc_fabric_acl)) {
+               printk(KERN_ERR "Missing tfo->tpg_alloc_fabric_acl()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->tpg_release_fabric_acl)) {
+               printk(KERN_ERR "Missing tfo->tpg_release_fabric_acl()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->tpg_get_inst_index)) {
+               printk(KERN_ERR "Missing tfo->tpg_get_inst_index()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->release_cmd_to_pool)) {
+               printk(KERN_ERR "Missing tfo->release_cmd_to_pool()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->release_cmd_direct)) {
+               printk(KERN_ERR "Missing tfo->release_cmd_direct()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->shutdown_session)) {
+               printk(KERN_ERR "Missing tfo->shutdown_session()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->close_session)) {
+               printk(KERN_ERR "Missing tfo->close_session()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->stop_session)) {
+               printk(KERN_ERR "Missing tfo->stop_session()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->fall_back_to_erl0)) {
+               printk(KERN_ERR "Missing tfo->fall_back_to_erl0()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->sess_logged_in)) {
+               printk(KERN_ERR "Missing tfo->sess_logged_in()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->sess_get_index)) {
+               printk(KERN_ERR "Missing tfo->sess_get_index()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->write_pending)) {
+               printk(KERN_ERR "Missing tfo->write_pending()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->write_pending_status)) {
+               printk(KERN_ERR "Missing tfo->write_pending_status()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->set_default_node_attributes)) {
+               printk(KERN_ERR "Missing tfo->set_default_node_attributes()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->get_task_tag)) {
+               printk(KERN_ERR "Missing tfo->get_task_tag()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->get_cmd_state)) {
+               printk(KERN_ERR "Missing tfo->get_cmd_state()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->new_cmd_failure)) {
+               printk(KERN_ERR "Missing tfo->new_cmd_failure()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->queue_data_in)) {
+               printk(KERN_ERR "Missing tfo->queue_data_in()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->queue_status)) {
+               printk(KERN_ERR "Missing tfo->queue_status()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->queue_tm_rsp)) {
+               printk(KERN_ERR "Missing tfo->queue_tm_rsp()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->set_fabric_sense_len)) {
+               printk(KERN_ERR "Missing tfo->set_fabric_sense_len()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->get_fabric_sense_len)) {
+               printk(KERN_ERR "Missing tfo->get_fabric_sense_len()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->is_state_remove)) {
+               printk(KERN_ERR "Missing tfo->is_state_remove()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->pack_lun)) {
+               printk(KERN_ERR "Missing tfo->pack_lun()\n");
+               return -EINVAL;
+       }
+       /*
+        * We at least require tfo->fabric_make_wwn(), tfo->fabric_drop_wwn()
+        * tfo->fabric_make_tpg() and tfo->fabric_drop_tpg() in
+        * target_core_fabric_configfs.c WWN+TPG group context code.
+        */
+       if (!(tfo->fabric_make_wwn)) {
+               printk(KERN_ERR "Missing tfo->fabric_make_wwn()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->fabric_drop_wwn)) {
+               printk(KERN_ERR "Missing tfo->fabric_drop_wwn()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->fabric_make_tpg)) {
+               printk(KERN_ERR "Missing tfo->fabric_make_tpg()\n");
+               return -EINVAL;
+       }
+       if (!(tfo->fabric_drop_tpg)) {
+               printk(KERN_ERR "Missing tfo->fabric_drop_tpg()\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Called 2nd from fabric module with returned parameter of
+ * struct target_fabric_configfs * from target_fabric_configfs_init().
+ *
+ * Upon a successful registration, the new fabric's struct config_item is
+ * return.  Also, a pointer to this struct is set in the passed
+ * struct target_fabric_configfs.
+ */
+int target_fabric_configfs_register(
+       struct target_fabric_configfs *tf)
+{
+       struct config_group *su_group;
+       int ret;
+
+       if (!(tf)) {
+               printk(KERN_ERR "Unable to locate target_fabric_configfs"
+                       " pointer\n");
+               return -EINVAL;
+       }
+       if (!(tf->tf_subsys)) {
+               printk(KERN_ERR "Unable to target struct config_subsystem"
+                       " pointer\n");
+               return -EINVAL;
+       }
+       su_group = &tf->tf_subsys->su_group;
+       if (!(su_group)) {
+               printk(KERN_ERR "Unable to locate target struct config_group"
+                       " pointer\n");
+               return -EINVAL;
+       }
+       ret = target_fabric_tf_ops_check(tf);
+       if (ret < 0)
+               return ret;
+
+       printk(KERN_INFO "<<<<<<<<<<<<<<<<<<<<<< END FABRIC API >>>>>>>>>>>>"
+               ">>>>>>>>>>\n");
+       return 0;
+}
+EXPORT_SYMBOL(target_fabric_configfs_register);
+
+void target_fabric_configfs_deregister(
+       struct target_fabric_configfs *tf)
+{
+       struct config_group *su_group;
+       struct configfs_subsystem *su;
+
+       if (!(tf)) {
+               printk(KERN_ERR "Unable to locate passed target_fabric_"
+                       "configfs\n");
+               return;
+       }
+       su = tf->tf_subsys;
+       if (!(su)) {
+               printk(KERN_ERR "Unable to locate passed tf->tf_subsys"
+                       " pointer\n");
+               return;
+       }
+       su_group = &tf->tf_subsys->su_group;
+       if (!(su_group)) {
+               printk(KERN_ERR "Unable to locate target struct config_group"
+                       " pointer\n");
+               return;
+       }
+
+       printk(KERN_INFO "<<<<<<<<<<<<<<<<<<<<<< BEGIN FABRIC API >>>>>>>>>>"
+                       ">>>>>>>>>>>>\n");
+       mutex_lock(&g_tf_lock);
+       if (atomic_read(&tf->tf_access_cnt)) {
+               mutex_unlock(&g_tf_lock);
+               printk(KERN_ERR "Non zero tf->tf_access_cnt for fabric %s\n",
+                       tf->tf_name);
+               BUG();
+       }
+       list_del(&tf->tf_list);
+       mutex_unlock(&g_tf_lock);
+
+       printk(KERN_INFO "Target_Core_ConfigFS: DEREGISTER -> Releasing tf:"
+                       " %s\n", tf->tf_name);
+       tf->tf_module = NULL;
+       tf->tf_subsys = NULL;
+       kfree(tf);
+
+       printk("<<<<<<<<<<<<<<<<<<<<<< END FABRIC API >>>>>>>>>>>>>>>>>"
+                       ">>>>>\n");
+       return;
+}
+EXPORT_SYMBOL(target_fabric_configfs_deregister);
+
+/*##############################################################################
+// Stop functions called by external Target Fabrics Modules
+//############################################################################*/
+
+/* Start functions for struct config_item_type target_core_dev_attrib_cit */
+
+#define DEF_DEV_ATTRIB_SHOW(_name)                                     \
+static ssize_t target_core_dev_show_attr_##_name(                      \
+       struct se_dev_attrib *da,                                       \
+       char *page)                                                     \
+{                                                                      \
+       struct se_device *dev;                                          \
+       struct se_subsystem_dev *se_dev = da->da_sub_dev;                       \
+       ssize_t rb;                                                     \
+                                                                       \
+       spin_lock(&se_dev->se_dev_lock);                                \
+       dev = se_dev->se_dev_ptr;                                       \
+       if (!(dev)) {                                                   \
+               spin_unlock(&se_dev->se_dev_lock);                      \
+               return -ENODEV;                                         \
+       }                                                               \
+       rb = snprintf(page, PAGE_SIZE, "%u\n", (u32)DEV_ATTRIB(dev)->_name); \
+       spin_unlock(&se_dev->se_dev_lock);                              \
+                                                                       \
+       return rb;                                                      \
+}
+
+#define DEF_DEV_ATTRIB_STORE(_name)                                    \
+static ssize_t target_core_dev_store_attr_##_name(                     \
+       struct se_dev_attrib *da,                                       \
+       const char *page,                                               \
+       size_t count)                                                   \
+{                                                                      \
+       struct se_device *dev;                                          \
+       struct se_subsystem_dev *se_dev = da->da_sub_dev;                       \
+       unsigned long val;                                              \
+       int ret;                                                        \
+                                                                       \
+       spin_lock(&se_dev->se_dev_lock);                                \
+       dev = se_dev->se_dev_ptr;                                       \
+       if (!(dev)) {                                                   \
+               spin_unlock(&se_dev->se_dev_lock);                      \
+               return -ENODEV;                                         \
+       }                                                               \
+       ret = strict_strtoul(page, 0, &val);                            \
+       if (ret < 0) {                                                  \
+               spin_unlock(&se_dev->se_dev_lock);                      \
+               printk(KERN_ERR "strict_strtoul() failed with"          \
+                       " ret: %d\n", ret);                             \
+               return -EINVAL;                                         \
+       }                                                               \
+       ret = se_dev_set_##_name(dev, (u32)val);                        \
+       spin_unlock(&se_dev->se_dev_lock);                              \
+                                                                       \
+       return (!ret) ? count : -EINVAL;                                \
+}
+
+#define DEF_DEV_ATTRIB(_name)                                          \
+DEF_DEV_ATTRIB_SHOW(_name);                                            \
+DEF_DEV_ATTRIB_STORE(_name);
+
+#define DEF_DEV_ATTRIB_RO(_name)                                       \
+DEF_DEV_ATTRIB_SHOW(_name);
+
+CONFIGFS_EATTR_STRUCT(target_core_dev_attrib, se_dev_attrib);
+#define SE_DEV_ATTR(_name, _mode)                                      \
+static struct target_core_dev_attrib_attribute                         \
+                       target_core_dev_attrib_##_name =                \
+               __CONFIGFS_EATTR(_name, _mode,                          \
+               target_core_dev_show_attr_##_name,                      \
+               target_core_dev_store_attr_##_name);
+
+#define SE_DEV_ATTR_RO(_name);                                         \
+static struct target_core_dev_attrib_attribute                         \
+                       target_core_dev_attrib_##_name =                \
+       __CONFIGFS_EATTR_RO(_name,                                      \
+       target_core_dev_show_attr_##_name);
+
+DEF_DEV_ATTRIB(emulate_dpo);
+SE_DEV_ATTR(emulate_dpo, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(emulate_fua_write);
+SE_DEV_ATTR(emulate_fua_write, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(emulate_fua_read);
+SE_DEV_ATTR(emulate_fua_read, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(emulate_write_cache);
+SE_DEV_ATTR(emulate_write_cache, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(emulate_ua_intlck_ctrl);
+SE_DEV_ATTR(emulate_ua_intlck_ctrl, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(emulate_tas);
+SE_DEV_ATTR(emulate_tas, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(emulate_tpu);
+SE_DEV_ATTR(emulate_tpu, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(emulate_tpws);
+SE_DEV_ATTR(emulate_tpws, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(enforce_pr_isids);
+SE_DEV_ATTR(enforce_pr_isids, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB_RO(hw_block_size);
+SE_DEV_ATTR_RO(hw_block_size);
+
+DEF_DEV_ATTRIB(block_size);
+SE_DEV_ATTR(block_size, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB_RO(hw_max_sectors);
+SE_DEV_ATTR_RO(hw_max_sectors);
+
+DEF_DEV_ATTRIB(max_sectors);
+SE_DEV_ATTR(max_sectors, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(optimal_sectors);
+SE_DEV_ATTR(optimal_sectors, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB_RO(hw_queue_depth);
+SE_DEV_ATTR_RO(hw_queue_depth);
+
+DEF_DEV_ATTRIB(queue_depth);
+SE_DEV_ATTR(queue_depth, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(task_timeout);
+SE_DEV_ATTR(task_timeout, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(max_unmap_lba_count);
+SE_DEV_ATTR(max_unmap_lba_count, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(max_unmap_block_desc_count);
+SE_DEV_ATTR(max_unmap_block_desc_count, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(unmap_granularity);
+SE_DEV_ATTR(unmap_granularity, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(unmap_granularity_alignment);
+SE_DEV_ATTR(unmap_granularity_alignment, S_IRUGO | S_IWUSR);
+
+CONFIGFS_EATTR_OPS(target_core_dev_attrib, se_dev_attrib, da_group);
+
+static struct configfs_attribute *target_core_dev_attrib_attrs[] = {
+       &target_core_dev_attrib_emulate_dpo.attr,
+       &target_core_dev_attrib_emulate_fua_write.attr,
+       &target_core_dev_attrib_emulate_fua_read.attr,
+       &target_core_dev_attrib_emulate_write_cache.attr,
+       &target_core_dev_attrib_emulate_ua_intlck_ctrl.attr,
+       &target_core_dev_attrib_emulate_tas.attr,
+       &target_core_dev_attrib_emulate_tpu.attr,
+       &target_core_dev_attrib_emulate_tpws.attr,
+       &target_core_dev_attrib_enforce_pr_isids.attr,
+       &target_core_dev_attrib_hw_block_size.attr,
+       &target_core_dev_attrib_block_size.attr,
+       &target_core_dev_attrib_hw_max_sectors.attr,
+       &target_core_dev_attrib_max_sectors.attr,
+       &target_core_dev_attrib_optimal_sectors.attr,
+       &target_core_dev_attrib_hw_queue_depth.attr,
+       &target_core_dev_attrib_queue_depth.attr,
+       &target_core_dev_attrib_task_timeout.attr,
+       &target_core_dev_attrib_max_unmap_lba_count.attr,
+       &target_core_dev_attrib_max_unmap_block_desc_count.attr,
+       &target_core_dev_attrib_unmap_granularity.attr,
+       &target_core_dev_attrib_unmap_granularity_alignment.attr,
+       NULL,
+};
+
+static struct configfs_item_operations target_core_dev_attrib_ops = {
+       .show_attribute         = target_core_dev_attrib_attr_show,
+       .store_attribute        = target_core_dev_attrib_attr_store,
+};
+
+static struct config_item_type target_core_dev_attrib_cit = {
+       .ct_item_ops            = &target_core_dev_attrib_ops,
+       .ct_attrs               = target_core_dev_attrib_attrs,
+       .ct_owner               = THIS_MODULE,
+};
+
+/* End functions for struct config_item_type target_core_dev_attrib_cit */
+
+/*  Start functions for struct config_item_type target_core_dev_wwn_cit */
+
+CONFIGFS_EATTR_STRUCT(target_core_dev_wwn, t10_wwn);
+#define SE_DEV_WWN_ATTR(_name, _mode)                                  \
+static struct target_core_dev_wwn_attribute target_core_dev_wwn_##_name = \
+               __CONFIGFS_EATTR(_name, _mode,                          \
+               target_core_dev_wwn_show_attr_##_name,                  \
+               target_core_dev_wwn_store_attr_##_name);
+
+#define SE_DEV_WWN_ATTR_RO(_name);                                     \
+do {                                                                   \
+       static struct target_core_dev_wwn_attribute                     \
+                       target_core_dev_wwn_##_name =                   \
+               __CONFIGFS_EATTR_RO(_name,                              \
+               target_core_dev_wwn_show_attr_##_name);                 \
+} while (0);
+
+/*
+ * VPD page 0x80 Unit serial
+ */
+static ssize_t target_core_dev_wwn_show_attr_vpd_unit_serial(
+       struct t10_wwn *t10_wwn,
+       char *page)
+{
+       struct se_subsystem_dev *se_dev = t10_wwn->t10_sub_dev;
+       struct se_device *dev;
+
+       dev = se_dev->se_dev_ptr;
+       if (!(dev))
+               return -ENODEV;
+
+       return sprintf(page, "T10 VPD Unit Serial Number: %s\n",
+               &t10_wwn->unit_serial[0]);
+}
+
+static ssize_t target_core_dev_wwn_store_attr_vpd_unit_serial(
+       struct t10_wwn *t10_wwn,
+       const char *page,
+       size_t count)
+{
+       struct se_subsystem_dev *su_dev = t10_wwn->t10_sub_dev;
+       struct se_device *dev;
+       unsigned char buf[INQUIRY_VPD_SERIAL_LEN];
+
+       /*
+        * If Linux/SCSI subsystem_api_t plugin got a VPD Unit Serial
+        * from the struct scsi_device level firmware, do not allow
+        * VPD Unit Serial to be emulated.
+        *
+        * Note this struct scsi_device could also be emulating VPD
+        * information from its drivers/scsi LLD.  But for now we assume
+        * it is doing 'the right thing' wrt a world wide unique
+        * VPD Unit Serial Number that OS dependent multipath can depend on.
+        */
+       if (su_dev->su_dev_flags & SDF_FIRMWARE_VPD_UNIT_SERIAL) {
+               printk(KERN_ERR "Underlying SCSI device firmware provided VPD"
+                       " Unit Serial, ignoring request\n");
+               return -EOPNOTSUPP;
+       }
+
+       if ((strlen(page) + 1) > INQUIRY_VPD_SERIAL_LEN) {
+               printk(KERN_ERR "Emulated VPD Unit Serial exceeds"
+               " INQUIRY_VPD_SERIAL_LEN: %d\n", INQUIRY_VPD_SERIAL_LEN);
+               return -EOVERFLOW;
+       }
+       /*
+        * Check to see if any active $FABRIC_MOD exports exist.  If they
+        * do exist, fail here as changing this information on the fly
+        * (underneath the initiator side OS dependent multipath code)
+        * could cause negative effects.
+        */
+       dev = su_dev->se_dev_ptr;
+       if ((dev)) {
+               if (atomic_read(&dev->dev_export_obj.obj_access_count)) {
+                       printk(KERN_ERR "Unable to set VPD Unit Serial while"
+                               " active %d $FABRIC_MOD exports exist\n",
+                               atomic_read(&dev->dev_export_obj.obj_access_count));
+                       return -EINVAL;
+               }
+       }
+       /*
+        * This currently assumes ASCII encoding for emulated VPD Unit Serial.
+        *
+        * Also, strip any newline added from the userspace
+        * echo $UUID > $TARGET/$HBA/$STORAGE_OBJECT/wwn/vpd_unit_serial
+        */
+       memset(buf, 0, INQUIRY_VPD_SERIAL_LEN);
+       snprintf(buf, INQUIRY_VPD_SERIAL_LEN, "%s", page);
+       snprintf(su_dev->t10_wwn.unit_serial, INQUIRY_VPD_SERIAL_LEN,
+                       "%s", strstrip(buf));
+       su_dev->su_dev_flags |= SDF_EMULATED_VPD_UNIT_SERIAL;
+
+       printk(KERN_INFO "Target_Core_ConfigFS: Set emulated VPD Unit Serial:"
+                       " %s\n", su_dev->t10_wwn.unit_serial);
+
+       return count;
+}
+
+SE_DEV_WWN_ATTR(vpd_unit_serial, S_IRUGO | S_IWUSR);
+
+/*
+ * VPD page 0x83 Protocol Identifier
+ */
+static ssize_t target_core_dev_wwn_show_attr_vpd_protocol_identifier(
+       struct t10_wwn *t10_wwn,
+       char *page)
+{
+       struct se_subsystem_dev *se_dev = t10_wwn->t10_sub_dev;
+       struct se_device *dev;
+       struct t10_vpd *vpd;
+       unsigned char buf[VPD_TMP_BUF_SIZE];
+       ssize_t len = 0;
+
+       dev = se_dev->se_dev_ptr;
+       if (!(dev))
+               return -ENODEV;
+
+       memset(buf, 0, VPD_TMP_BUF_SIZE);
+
+       spin_lock(&t10_wwn->t10_vpd_lock);
+       list_for_each_entry(vpd, &t10_wwn->t10_vpd_list, vpd_list) {
+               if (!(vpd->protocol_identifier_set))
+                       continue;
+
+               transport_dump_vpd_proto_id(vpd, buf, VPD_TMP_BUF_SIZE);
+
+               if ((len + strlen(buf) > PAGE_SIZE))
+                       break;
+
+               len += sprintf(page+len, "%s", buf);
+       }
+       spin_unlock(&t10_wwn->t10_vpd_lock);
+
+       return len;
+}
+
+static ssize_t target_core_dev_wwn_store_attr_vpd_protocol_identifier(
+       struct t10_wwn *t10_wwn,
+       const char *page,
+       size_t count)
+{
+       return -ENOSYS;
+}
+
+SE_DEV_WWN_ATTR(vpd_protocol_identifier, S_IRUGO | S_IWUSR);
+
+/*
+ * Generic wrapper for dumping VPD identifiers by association.
+ */
+#define DEF_DEV_WWN_ASSOC_SHOW(_name, _assoc)                          \
+static ssize_t target_core_dev_wwn_show_attr_##_name(                  \
+       struct t10_wwn *t10_wwn,                                        \
+       char *page)                                                     \
+{                                                                      \
+       struct se_subsystem_dev *se_dev = t10_wwn->t10_sub_dev;         \
+       struct se_device *dev;                                          \
+       struct t10_vpd *vpd;                                                    \
+       unsigned char buf[VPD_TMP_BUF_SIZE];                            \
+       ssize_t len = 0;                                                \
+                                                                       \
+       dev = se_dev->se_dev_ptr;                                       \
+       if (!(dev))                                                     \
+               return -ENODEV;                                         \
+                                                                       \
+       spin_lock(&t10_wwn->t10_vpd_lock);                              \
+       list_for_each_entry(vpd, &t10_wwn->t10_vpd_list, vpd_list) {    \
+               if (vpd->association != _assoc)                         \
+                       continue;                                       \
+                                                                       \
+               memset(buf, 0, VPD_TMP_BUF_SIZE);                       \
+               transport_dump_vpd_assoc(vpd, buf, VPD_TMP_BUF_SIZE);   \
+               if ((len + strlen(buf) > PAGE_SIZE))                    \
+                       break;                                          \
+               len += sprintf(page+len, "%s", buf);                    \
+                                                                       \
+               memset(buf, 0, VPD_TMP_BUF_SIZE);                       \
+               transport_dump_vpd_ident_type(vpd, buf, VPD_TMP_BUF_SIZE); \
+               if ((len + strlen(buf) > PAGE_SIZE))                    \
+                       break;                                          \
+               len += sprintf(page+len, "%s", buf);                    \
+                                                                       \
+               memset(buf, 0, VPD_TMP_BUF_SIZE);                       \
+               transport_dump_vpd_ident(vpd, buf, VPD_TMP_BUF_SIZE); \
+               if ((len + strlen(buf) > PAGE_SIZE))                    \
+                       break;                                          \
+               len += sprintf(page+len, "%s", buf);                    \
+       }                                                               \
+       spin_unlock(&t10_wwn->t10_vpd_lock);                            \
+                                                                       \
+       return len;                                                     \
+}
+
+/*
+ * VPD page 0x83 Assoication: Logical Unit
+ */
+DEF_DEV_WWN_ASSOC_SHOW(vpd_assoc_logical_unit, 0x00);
+
+static ssize_t target_core_dev_wwn_store_attr_vpd_assoc_logical_unit(
+       struct t10_wwn *t10_wwn,
+       const char *page,
+       size_t count)
+{
+       return -ENOSYS;
+}
+
+SE_DEV_WWN_ATTR(vpd_assoc_logical_unit, S_IRUGO | S_IWUSR);
+
+/*
+ * VPD page 0x83 Association: Target Port
+ */
+DEF_DEV_WWN_ASSOC_SHOW(vpd_assoc_target_port, 0x10);
+
+static ssize_t target_core_dev_wwn_store_attr_vpd_assoc_target_port(
+       struct t10_wwn *t10_wwn,
+       const char *page,
+       size_t count)
+{
+       return -ENOSYS;
+}
+
+SE_DEV_WWN_ATTR(vpd_assoc_target_port, S_IRUGO | S_IWUSR);
+
+/*
+ * VPD page 0x83 Association: SCSI Target Device
+ */
+DEF_DEV_WWN_ASSOC_SHOW(vpd_assoc_scsi_target_device, 0x20);
+
+static ssize_t target_core_dev_wwn_store_attr_vpd_assoc_scsi_target_device(
+       struct t10_wwn *t10_wwn,
+       const char *page,
+       size_t count)
+{
+       return -ENOSYS;
+}
+
+SE_DEV_WWN_ATTR(vpd_assoc_scsi_target_device, S_IRUGO | S_IWUSR);
+
+CONFIGFS_EATTR_OPS(target_core_dev_wwn, t10_wwn, t10_wwn_group);
+
+static struct configfs_attribute *target_core_dev_wwn_attrs[] = {
+       &target_core_dev_wwn_vpd_unit_serial.attr,
+       &target_core_dev_wwn_vpd_protocol_identifier.attr,
+       &target_core_dev_wwn_vpd_assoc_logical_unit.attr,
+       &target_core_dev_wwn_vpd_assoc_target_port.attr,
+       &target_core_dev_wwn_vpd_assoc_scsi_target_device.attr,
+       NULL,
+};
+
+static struct configfs_item_operations target_core_dev_wwn_ops = {
+       .show_attribute         = target_core_dev_wwn_attr_show,
+       .store_attribute        = target_core_dev_wwn_attr_store,
+};
+
+static struct config_item_type target_core_dev_wwn_cit = {
+       .ct_item_ops            = &target_core_dev_wwn_ops,
+       .ct_attrs               = target_core_dev_wwn_attrs,
+       .ct_owner               = THIS_MODULE,
+};
+
+/*  End functions for struct config_item_type target_core_dev_wwn_cit */
+
+/*  Start functions for struct config_item_type target_core_dev_pr_cit */
+
+CONFIGFS_EATTR_STRUCT(target_core_dev_pr, se_subsystem_dev);
+#define SE_DEV_PR_ATTR(_name, _mode)                                   \
+static struct target_core_dev_pr_attribute target_core_dev_pr_##_name = \
+       __CONFIGFS_EATTR(_name, _mode,                                  \
+       target_core_dev_pr_show_attr_##_name,                           \
+       target_core_dev_pr_store_attr_##_name);
+
+#define SE_DEV_PR_ATTR_RO(_name);                                      \
+static struct target_core_dev_pr_attribute target_core_dev_pr_##_name =        \
+       __CONFIGFS_EATTR_RO(_name,                                      \
+       target_core_dev_pr_show_attr_##_name);
+
+/*
+ * res_holder
+ */
+static ssize_t target_core_dev_pr_show_spc3_res(
+       struct se_device *dev,
+       char *page,
+       ssize_t *len)
+{
+       struct se_node_acl *se_nacl;
+       struct t10_pr_registration *pr_reg;
+       char i_buf[PR_REG_ISID_ID_LEN];
+       int prf_isid;
+
+       memset(i_buf, 0, PR_REG_ISID_ID_LEN);
+
+       spin_lock(&dev->dev_reservation_lock);
+       pr_reg = dev->dev_pr_res_holder;
+       if (!(pr_reg)) {
+               *len += sprintf(page + *len, "No SPC-3 Reservation holder\n");
+               spin_unlock(&dev->dev_reservation_lock);
+               return *len;
+       }
+       se_nacl = pr_reg->pr_reg_nacl;
+       prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
+                               PR_REG_ISID_ID_LEN);
+
+       *len += sprintf(page + *len, "SPC-3 Reservation: %s Initiator: %s%s\n",
+               TPG_TFO(se_nacl->se_tpg)->get_fabric_name(),
+               se_nacl->initiatorname, (prf_isid) ? &i_buf[0] : "");
+       spin_unlock(&dev->dev_reservation_lock);
+
+       return *len;
+}
+
+static ssize_t target_core_dev_pr_show_spc2_res(
+       struct se_device *dev,
+       char *page,
+       ssize_t *len)
+{
+       struct se_node_acl *se_nacl;
+
+       spin_lock(&dev->dev_reservation_lock);
+       se_nacl = dev->dev_reserved_node_acl;
+       if (!(se_nacl)) {
+               *len += sprintf(page + *len, "No SPC-2 Reservation holder\n");
+               spin_unlock(&dev->dev_reservation_lock);
+               return *len;
+       }
+       *len += sprintf(page + *len, "SPC-2 Reservation: %s Initiator: %s\n",
+               TPG_TFO(se_nacl->se_tpg)->get_fabric_name(),
+               se_nacl->initiatorname);
+       spin_unlock(&dev->dev_reservation_lock);
+
+       return *len;
+}
+
+static ssize_t target_core_dev_pr_show_attr_res_holder(
+       struct se_subsystem_dev *su_dev,
+       char *page)
+{
+       ssize_t len = 0;
+
+       if (!(su_dev->se_dev_ptr))
+               return -ENODEV;
+
+       switch (T10_RES(su_dev)->res_type) {
+       case SPC3_PERSISTENT_RESERVATIONS:
+               target_core_dev_pr_show_spc3_res(su_dev->se_dev_ptr,
+                               page, &len);
+               break;
+       case SPC2_RESERVATIONS:
+               target_core_dev_pr_show_spc2_res(su_dev->se_dev_ptr,
+                               page, &len);
+               break;
+       case SPC_PASSTHROUGH:
+               len += sprintf(page+len, "Passthrough\n");
+               break;
+       default:
+               len += sprintf(page+len, "Unknown\n");
+               break;
+       }
+
+       return len;
+}
+
+SE_DEV_PR_ATTR_RO(res_holder);
+
+/*
+ * res_pr_all_tgt_pts
+ */
+static ssize_t target_core_dev_pr_show_attr_res_pr_all_tgt_pts(
+       struct se_subsystem_dev *su_dev,
+       char *page)
+{
+       struct se_device *dev;
+       struct t10_pr_registration *pr_reg;
+       ssize_t len = 0;
+
+       dev = su_dev->se_dev_ptr;
+       if (!(dev))
+               return -ENODEV;
+
+       if (T10_RES(su_dev)->res_type != SPC3_PERSISTENT_RESERVATIONS)
+               return len;
+
+       spin_lock(&dev->dev_reservation_lock);
+       pr_reg = dev->dev_pr_res_holder;
+       if (!(pr_reg)) {
+               len = sprintf(page, "No SPC-3 Reservation holder\n");
+               spin_unlock(&dev->dev_reservation_lock);
+               return len;
+       }
+       /*
+        * See All Target Ports (ALL_TG_PT) bit in spcr17, section 6.14.3
+        * Basic PERSISTENT RESERVER OUT parameter list, page 290
+        */
+       if (pr_reg->pr_reg_all_tg_pt)
+               len = sprintf(page, "SPC-3 Reservation: All Target"
+                       " Ports registration\n");
+       else
+               len = sprintf(page, "SPC-3 Reservation: Single"
+                       " Target Port registration\n");
+       spin_unlock(&dev->dev_reservation_lock);
+
+       return len;
+}
+
+SE_DEV_PR_ATTR_RO(res_pr_all_tgt_pts);
+
+/*
+ * res_pr_generation
+ */
+static ssize_t target_core_dev_pr_show_attr_res_pr_generation(
+       struct se_subsystem_dev *su_dev,
+       char *page)
+{
+       if (!(su_dev->se_dev_ptr))
+               return -ENODEV;
+
+       if (T10_RES(su_dev)->res_type != SPC3_PERSISTENT_RESERVATIONS)
+               return 0;
+
+       return sprintf(page, "0x%08x\n", T10_RES(su_dev)->pr_generation);
+}
+
+SE_DEV_PR_ATTR_RO(res_pr_generation);
+
+/*
+ * res_pr_holder_tg_port
+ */
+static ssize_t target_core_dev_pr_show_attr_res_pr_holder_tg_port(
+       struct se_subsystem_dev *su_dev,
+       char *page)
+{
+       struct se_device *dev;
+       struct se_node_acl *se_nacl;
+       struct se_lun *lun;
+       struct se_portal_group *se_tpg;
+       struct t10_pr_registration *pr_reg;
+       struct target_core_fabric_ops *tfo;
+       ssize_t len = 0;
+
+       dev = su_dev->se_dev_ptr;
+       if (!(dev))
+               return -ENODEV;
+
+       if (T10_RES(su_dev)->res_type != SPC3_PERSISTENT_RESERVATIONS)
+               return len;
+
+       spin_lock(&dev->dev_reservation_lock);
+       pr_reg = dev->dev_pr_res_holder;
+       if (!(pr_reg)) {
+               len = sprintf(page, "No SPC-3 Reservation holder\n");
+               spin_unlock(&dev->dev_reservation_lock);
+               return len;
+       }
+       se_nacl = pr_reg->pr_reg_nacl;
+       se_tpg = se_nacl->se_tpg;
+       lun = pr_reg->pr_reg_tg_pt_lun;
+       tfo = TPG_TFO(se_tpg);
+
+       len += sprintf(page+len, "SPC-3 Reservation: %s"
+               " Target Node Endpoint: %s\n", tfo->get_fabric_name(),
+               tfo->tpg_get_wwn(se_tpg));
+       len += sprintf(page+len, "SPC-3 Reservation: Relative Port"
+               " Identifer Tag: %hu %s Portal Group Tag: %hu"
+               " %s Logical Unit: %u\n", lun->lun_sep->sep_rtpi,
+               tfo->get_fabric_name(), tfo->tpg_get_tag(se_tpg),
+               tfo->get_fabric_name(), lun->unpacked_lun);
+       spin_unlock(&dev->dev_reservation_lock);
+
+       return len;
+}
+
+SE_DEV_PR_ATTR_RO(res_pr_holder_tg_port);
+
+/*
+ * res_pr_registered_i_pts
+ */
+static ssize_t target_core_dev_pr_show_attr_res_pr_registered_i_pts(
+       struct se_subsystem_dev *su_dev,
+       char *page)
+{
+       struct target_core_fabric_ops *tfo;
+       struct t10_pr_registration *pr_reg;
+       unsigned char buf[384];
+       char i_buf[PR_REG_ISID_ID_LEN];
+       ssize_t len = 0;
+       int reg_count = 0, prf_isid;
+
+       if (!(su_dev->se_dev_ptr))
+               return -ENODEV;
+
+       if (T10_RES(su_dev)->res_type != SPC3_PERSISTENT_RESERVATIONS)
+               return len;
+
+       len += sprintf(page+len, "SPC-3 PR Registrations:\n");
+
+       spin_lock(&T10_RES(su_dev)->registration_lock);
+       list_for_each_entry(pr_reg, &T10_RES(su_dev)->registration_list,
+                       pr_reg_list) {
+
+               memset(buf, 0, 384);
+               memset(i_buf, 0, PR_REG_ISID_ID_LEN);
+               tfo = pr_reg->pr_reg_nacl->se_tpg->se_tpg_tfo;
+               prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
+                                       PR_REG_ISID_ID_LEN);
+               sprintf(buf, "%s Node: %s%s Key: 0x%016Lx PRgen: 0x%08x\n",
+                       tfo->get_fabric_name(),
+                       pr_reg->pr_reg_nacl->initiatorname, (prf_isid) ?
+                       &i_buf[0] : "", pr_reg->pr_res_key,
+                       pr_reg->pr_res_generation);
+
+               if ((len + strlen(buf) > PAGE_SIZE))
+                       break;
+
+               len += sprintf(page+len, "%s", buf);
+               reg_count++;
+       }
+       spin_unlock(&T10_RES(su_dev)->registration_lock);
+
+       if (!(reg_count))
+               len += sprintf(page+len, "None\n");
+
+       return len;
+}
+
+SE_DEV_PR_ATTR_RO(res_pr_registered_i_pts);
+
+/*
+ * res_pr_type
+ */
+static ssize_t target_core_dev_pr_show_attr_res_pr_type(
+       struct se_subsystem_dev *su_dev,
+       char *page)
+{
+       struct se_device *dev;
+       struct t10_pr_registration *pr_reg;
+       ssize_t len = 0;
+
+       dev = su_dev->se_dev_ptr;
+       if (!(dev))
+               return -ENODEV;
+
+       if (T10_RES(su_dev)->res_type != SPC3_PERSISTENT_RESERVATIONS)
+               return len;
+
+       spin_lock(&dev->dev_reservation_lock);
+       pr_reg = dev->dev_pr_res_holder;
+       if (!(pr_reg)) {
+               len = sprintf(page, "No SPC-3 Reservation holder\n");
+               spin_unlock(&dev->dev_reservation_lock);
+               return len;
+       }
+       len = sprintf(page, "SPC-3 Reservation Type: %s\n",
+               core_scsi3_pr_dump_type(pr_reg->pr_res_type));
+       spin_unlock(&dev->dev_reservation_lock);
+
+       return len;
+}
+
+SE_DEV_PR_ATTR_RO(res_pr_type);
+
+/*
+ * res_type
+ */
+static ssize_t target_core_dev_pr_show_attr_res_type(
+       struct se_subsystem_dev *su_dev,
+       char *page)
+{
+       ssize_t len = 0;
+
+       if (!(su_dev->se_dev_ptr))
+               return -ENODEV;
+
+       switch (T10_RES(su_dev)->res_type) {
+       case SPC3_PERSISTENT_RESERVATIONS:
+               len = sprintf(page, "SPC3_PERSISTENT_RESERVATIONS\n");
+               break;
+       case SPC2_RESERVATIONS:
+               len = sprintf(page, "SPC2_RESERVATIONS\n");
+               break;
+       case SPC_PASSTHROUGH:
+               len = sprintf(page, "SPC_PASSTHROUGH\n");
+               break;
+       default:
+               len = sprintf(page, "UNKNOWN\n");
+               break;
+       }
+
+       return len;
+}
+
+SE_DEV_PR_ATTR_RO(res_type);
+
+/*
+ * res_aptpl_active
+ */
+
+static ssize_t target_core_dev_pr_show_attr_res_aptpl_active(
+       struct se_subsystem_dev *su_dev,
+       char *page)
+{
+       if (!(su_dev->se_dev_ptr))
+               return -ENODEV;
+
+       if (T10_RES(su_dev)->res_type != SPC3_PERSISTENT_RESERVATIONS)
+               return 0;
+
+       return sprintf(page, "APTPL Bit Status: %s\n",
+               (T10_RES(su_dev)->pr_aptpl_active) ? "Activated" : "Disabled");
+}
+
+SE_DEV_PR_ATTR_RO(res_aptpl_active);
+
+/*
+ * res_aptpl_metadata
+ */
+static ssize_t target_core_dev_pr_show_attr_res_aptpl_metadata(
+       struct se_subsystem_dev *su_dev,
+       char *page)
+{
+       if (!(su_dev->se_dev_ptr))
+               return -ENODEV;
+
+       if (T10_RES(su_dev)->res_type != SPC3_PERSISTENT_RESERVATIONS)
+               return 0;
+
+       return sprintf(page, "Ready to process PR APTPL metadata..\n");
+}
+
+enum {
+       Opt_initiator_fabric, Opt_initiator_node, Opt_initiator_sid,
+       Opt_sa_res_key, Opt_res_holder, Opt_res_type, Opt_res_scope,
+       Opt_res_all_tg_pt, Opt_mapped_lun, Opt_target_fabric,
+       Opt_target_node, Opt_tpgt, Opt_port_rtpi, Opt_target_lun, Opt_err
+};
+
+static match_table_t tokens = {
+       {Opt_initiator_fabric, "initiator_fabric=%s"},
+       {Opt_initiator_node, "initiator_node=%s"},
+       {Opt_initiator_sid, "initiator_sid=%s"},
+       {Opt_sa_res_key, "sa_res_key=%s"},
+       {Opt_res_holder, "res_holder=%d"},
+       {Opt_res_type, "res_type=%d"},
+       {Opt_res_scope, "res_scope=%d"},
+       {Opt_res_all_tg_pt, "res_all_tg_pt=%d"},
+       {Opt_mapped_lun, "mapped_lun=%d"},
+       {Opt_target_fabric, "target_fabric=%s"},
+       {Opt_target_node, "target_node=%s"},
+       {Opt_tpgt, "tpgt=%d"},
+       {Opt_port_rtpi, "port_rtpi=%d"},
+       {Opt_target_lun, "target_lun=%d"},
+       {Opt_err, NULL}
+};
+
+static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
+       struct se_subsystem_dev *su_dev,
+       const char *page,
+       size_t count)
+{
+       struct se_device *dev;
+       unsigned char *i_fabric, *t_fabric, *i_port = NULL, *t_port = NULL;
+       unsigned char *isid = NULL;
+       char *orig, *ptr, *arg_p, *opts;
+       substring_t args[MAX_OPT_ARGS];
+       unsigned long long tmp_ll;
+       u64 sa_res_key = 0;
+       u32 mapped_lun = 0, target_lun = 0;
+       int ret = -1, res_holder = 0, all_tg_pt = 0, arg, token;
+       u16 port_rpti = 0, tpgt = 0;
+       u8 type = 0, scope;
+
+       dev = su_dev->se_dev_ptr;
+       if (!(dev))
+               return -ENODEV;
+
+       if (T10_RES(su_dev)->res_type != SPC3_PERSISTENT_RESERVATIONS)
+               return 0;
+
+       if (atomic_read(&dev->dev_export_obj.obj_access_count)) {
+               printk(KERN_INFO "Unable to process APTPL metadata while"
+                       " active fabric exports exist\n");
+               return -EINVAL;
+       }
+
+       opts = kstrdup(page, GFP_KERNEL);
+       if (!opts)
+               return -ENOMEM;
+
+       orig = opts;
+       while ((ptr = strsep(&opts, ",")) != NULL) {
+               if (!*ptr)
+                       continue;
+
+               token = match_token(ptr, tokens, args);
+               switch (token) {
+               case Opt_initiator_fabric:
+                       i_fabric = match_strdup(&args[0]);
+                       break;
+               case Opt_initiator_node:
+                       i_port = match_strdup(&args[0]);
+                       if (strlen(i_port) > PR_APTPL_MAX_IPORT_LEN) {
+                               printk(KERN_ERR "APTPL metadata initiator_node="
+                                       " exceeds PR_APTPL_MAX_IPORT_LEN: %d\n",
+                                       PR_APTPL_MAX_IPORT_LEN);
+                               ret = -EINVAL;
+                               break;
+                       }
+                       break;
+               case Opt_initiator_sid:
+                       isid = match_strdup(&args[0]);
+                       if (strlen(isid) > PR_REG_ISID_LEN) {
+                               printk(KERN_ERR "APTPL metadata initiator_isid"
+                                       "= exceeds PR_REG_ISID_LEN: %d\n",
+                                       PR_REG_ISID_LEN);
+                               ret = -EINVAL;
+                               break;
+                       }
+                       break;
+               case Opt_sa_res_key:
+                       arg_p = match_strdup(&args[0]);
+                       ret = strict_strtoull(arg_p, 0, &tmp_ll);
+                       if (ret < 0) {
+                               printk(KERN_ERR "strict_strtoull() failed for"
+                                       " sa_res_key=\n");
+                               goto out;
+                       }
+                       sa_res_key = (u64)tmp_ll;
+                       break;
+               /*
+                * PR APTPL Metadata for Reservation
+                */
+               case Opt_res_holder:
+                       match_int(args, &arg);
+                       res_holder = arg;
+                       break;
+               case Opt_res_type:
+                       match_int(args, &arg);
+                       type = (u8)arg;
+                       break;
+               case Opt_res_scope:
+                       match_int(args, &arg);
+                       scope = (u8)arg;
+                       break;
+               case Opt_res_all_tg_pt:
+                       match_int(args, &arg);
+                       all_tg_pt = (int)arg;
+                       break;
+               case Opt_mapped_lun:
+                       match_int(args, &arg);
+                       mapped_lun = (u32)arg;
+                       break;
+               /*
+                * PR APTPL Metadata for Target Port
+                */
+               case Opt_target_fabric:
+                       t_fabric = match_strdup(&args[0]);
+                       break;
+               case Opt_target_node:
+                       t_port = match_strdup(&args[0]);
+                       if (strlen(t_port) > PR_APTPL_MAX_TPORT_LEN) {
+                               printk(KERN_ERR "APTPL metadata target_node="
+                                       " exceeds PR_APTPL_MAX_TPORT_LEN: %d\n",
+                                       PR_APTPL_MAX_TPORT_LEN);
+                               ret = -EINVAL;
+                               break;
+                       }
+                       break;
+               case Opt_tpgt:
+                       match_int(args, &arg);
+                       tpgt = (u16)arg;
+                       break;
+               case Opt_port_rtpi:
+                       match_int(args, &arg);
+                       port_rpti = (u16)arg;
+                       break;
+               case Opt_target_lun:
+                       match_int(args, &arg);
+                       target_lun = (u32)arg;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       if (!(i_port) || !(t_port) || !(sa_res_key)) {
+               printk(KERN_ERR "Illegal parameters for APTPL registration\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (res_holder && !(type)) {
+               printk(KERN_ERR "Illegal PR type: 0x%02x for reservation"
+                               " holder\n", type);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = core_scsi3_alloc_aptpl_registration(T10_RES(su_dev), sa_res_key,
+                       i_port, isid, mapped_lun, t_port, tpgt, target_lun,
+                       res_holder, all_tg_pt, type);
+out:
+       kfree(orig);
+       return (ret == 0) ? count : ret;
+}
+
+SE_DEV_PR_ATTR(res_aptpl_metadata, S_IRUGO | S_IWUSR);
+
+CONFIGFS_EATTR_OPS(target_core_dev_pr, se_subsystem_dev, se_dev_pr_group);
+
+static struct configfs_attribute *target_core_dev_pr_attrs[] = {
+       &target_core_dev_pr_res_holder.attr,
+       &target_core_dev_pr_res_pr_all_tgt_pts.attr,
+       &target_core_dev_pr_res_pr_generation.attr,
+       &target_core_dev_pr_res_pr_holder_tg_port.attr,
+       &target_core_dev_pr_res_pr_registered_i_pts.attr,
+       &target_core_dev_pr_res_pr_type.attr,
+       &target_core_dev_pr_res_type.attr,
+       &target_core_dev_pr_res_aptpl_active.attr,
+       &target_core_dev_pr_res_aptpl_metadata.attr,
+       NULL,
+};
+
+static struct configfs_item_operations target_core_dev_pr_ops = {
+       .show_attribute         = target_core_dev_pr_attr_show,
+       .store_attribute        = target_core_dev_pr_attr_store,
+};
+
+static struct config_item_type target_core_dev_pr_cit = {
+       .ct_item_ops            = &target_core_dev_pr_ops,
+       .ct_attrs               = target_core_dev_pr_attrs,
+       .ct_owner               = THIS_MODULE,
+};
+
+/*  End functions for struct config_item_type target_core_dev_pr_cit */
+
+/*  Start functions for struct config_item_type target_core_dev_cit */
+
+static ssize_t target_core_show_dev_info(void *p, char *page)
+{
+       struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p;
+       struct se_hba *hba = se_dev->se_dev_hba;
+       struct se_subsystem_api *t = hba->transport;
+       int bl = 0;
+       ssize_t read_bytes = 0;
+
+       if (!(se_dev->se_dev_ptr))
+               return -ENODEV;
+
+       transport_dump_dev_state(se_dev->se_dev_ptr, page, &bl);
+       read_bytes += bl;
+       read_bytes += t->show_configfs_dev_params(hba, se_dev, page+read_bytes);
+       return read_bytes;
+}
+
+static struct target_core_configfs_attribute target_core_attr_dev_info = {
+       .attr   = { .ca_owner = THIS_MODULE,
+                   .ca_name = "info",
+                   .ca_mode = S_IRUGO },
+       .show   = target_core_show_dev_info,
+       .store  = NULL,
+};
+
+static ssize_t target_core_store_dev_control(
+       void *p,
+       const char *page,
+       size_t count)
+{
+       struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p;
+       struct se_hba *hba = se_dev->se_dev_hba;
+       struct se_subsystem_api *t = hba->transport;
+
+       if (!(se_dev->se_dev_su_ptr)) {
+               printk(KERN_ERR "Unable to locate struct se_subsystem_dev>se"
+                               "_dev_su_ptr\n");
+               return -EINVAL;
+       }
+
+       return t->set_configfs_dev_params(hba, se_dev, page, count);
+}
+
+static struct target_core_configfs_attribute target_core_attr_dev_control = {
+       .attr   = { .ca_owner = THIS_MODULE,
+                   .ca_name = "control",
+                   .ca_mode = S_IWUSR },
+       .show   = NULL,
+       .store  = target_core_store_dev_control,
+};
+
+static ssize_t target_core_show_dev_alias(void *p, char *page)
+{
+       struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p;
+
+       if (!(se_dev->su_dev_flags & SDF_USING_ALIAS))
+               return 0;
+
+       return snprintf(page, PAGE_SIZE, "%s\n", se_dev->se_dev_alias);
+}
+
+static ssize_t target_core_store_dev_alias(
+       void *p,
+       const char *page,
+       size_t count)
+{
+       struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p;
+       struct se_hba *hba = se_dev->se_dev_hba;
+       ssize_t read_bytes;
+
+       if (count > (SE_DEV_ALIAS_LEN-1)) {
+               printk(KERN_ERR "alias count: %d exceeds"
+                       " SE_DEV_ALIAS_LEN-1: %u\n", (int)count,
+                       SE_DEV_ALIAS_LEN-1);
+               return -EINVAL;
+       }
+
+       se_dev->su_dev_flags |= SDF_USING_ALIAS;
+       read_bytes = snprintf(&se_dev->se_dev_alias[0], SE_DEV_ALIAS_LEN,
+                       "%s", page);
+
+       printk(KERN_INFO "Target_Core_ConfigFS: %s/%s set alias: %s\n",
+               config_item_name(&hba->hba_group.cg_item),
+               config_item_name(&se_dev->se_dev_group.cg_item),
+               se_dev->se_dev_alias);
+
+       return read_bytes;
+}
+
+static struct target_core_configfs_attribute target_core_attr_dev_alias = {
+       .attr   = { .ca_owner = THIS_MODULE,
+                   .ca_name = "alias",
+                   .ca_mode =  S_IRUGO | S_IWUSR },
+       .show   = target_core_show_dev_alias,
+       .store  = target_core_store_dev_alias,
+};
+
+static ssize_t target_core_show_dev_udev_path(void *p, char *page)
+{
+       struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p;
+
+       if (!(se_dev->su_dev_flags & SDF_USING_UDEV_PATH))
+               return 0;
+
+       return snprintf(page, PAGE_SIZE, "%s\n", se_dev->se_dev_udev_path);
+}
+
+static ssize_t target_core_store_dev_udev_path(
+       void *p,
+       const char *page,
+       size_t count)
+{
+       struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p;
+       struct se_hba *hba = se_dev->se_dev_hba;
+       ssize_t read_bytes;
+
+       if (count > (SE_UDEV_PATH_LEN-1)) {
+               printk(KERN_ERR "udev_path count: %d exceeds"
+                       " SE_UDEV_PATH_LEN-1: %u\n", (int)count,
+                       SE_UDEV_PATH_LEN-1);
+               return -EINVAL;
+       }
+
+       se_dev->su_dev_flags |= SDF_USING_UDEV_PATH;
+       read_bytes = snprintf(&se_dev->se_dev_udev_path[0], SE_UDEV_PATH_LEN,
+                       "%s", page);
+
+       printk(KERN_INFO "Target_Core_ConfigFS: %s/%s set udev_path: %s\n",
+               config_item_name(&hba->hba_group.cg_item),
+               config_item_name(&se_dev->se_dev_group.cg_item),
+               se_dev->se_dev_udev_path);
+
+       return read_bytes;
+}
+
+static struct target_core_configfs_attribute target_core_attr_dev_udev_path = {
+       .attr   = { .ca_owner = THIS_MODULE,
+                   .ca_name = "udev_path",
+                   .ca_mode =  S_IRUGO | S_IWUSR },
+       .show   = target_core_show_dev_udev_path,
+       .store  = target_core_store_dev_udev_path,
+};
+
+static ssize_t target_core_store_dev_enable(
+       void *p,
+       const char *page,
+       size_t count)
+{
+       struct se_subsystem_dev *se_dev = (struct se_subsystem_dev *)p;
+       struct se_device *dev;
+       struct se_hba *hba = se_dev->se_dev_hba;
+       struct se_subsystem_api *t = hba->transport;
+       char *ptr;
+
+       ptr = strstr(page, "1");
+       if (!(ptr)) {
+               printk(KERN_ERR "For dev_enable ops, only valid value"
+                               " is \"1\"\n");
+               return -EINVAL;
+       }
+       if ((se_dev->se_dev_ptr)) {
+               printk(KERN_ERR "se_dev->se_dev_ptr already set for storage"
+                               " object\n");
+               return -EEXIST;
+       }
+
+       if (t->check_configfs_dev_params(hba, se_dev) < 0)
+               return -EINVAL;
+
+       dev = t->create_virtdevice(hba, se_dev, se_dev->se_dev_su_ptr);
+       if (!(dev) || IS_ERR(dev))
+               return -EINVAL;
+
+       se_dev->se_dev_ptr = dev;
+       printk(KERN_INFO "Target_Core_ConfigFS: Registered se_dev->se_dev_ptr:"
+               " %p\n", se_dev->se_dev_ptr);
+
+       return count;
+}
+
+static struct target_core_configfs_attribute target_core_attr_dev_enable = {
+       .attr   = { .ca_owner = THIS_MODULE,
+                   .ca_name = "enable",
+                   .ca_mode = S_IWUSR },
+       .show   = NULL,
+       .store  = target_core_store_dev_enable,
+};
+
+static ssize_t target_core_show_alua_lu_gp(void *p, char *page)
+{
+       struct se_device *dev;
+       struct se_subsystem_dev *su_dev = (struct se_subsystem_dev *)p;
+       struct config_item *lu_ci;
+       struct t10_alua_lu_gp *lu_gp;
+       struct t10_alua_lu_gp_member *lu_gp_mem;
+       ssize_t len = 0;
+
+       dev = su_dev->se_dev_ptr;
+       if (!(dev))
+               return -ENODEV;
+
+       if (T10_ALUA(su_dev)->alua_type != SPC3_ALUA_EMULATED)
+               return len;
+
+       lu_gp_mem = dev->dev_alua_lu_gp_mem;
+       if (!(lu_gp_mem)) {
+               printk(KERN_ERR "NULL struct se_device->dev_alua_lu_gp_mem"
+                               " pointer\n");
+               return -EINVAL;
+       }
+
+       spin_lock(&lu_gp_mem->lu_gp_mem_lock);
+       lu_gp = lu_gp_mem->lu_gp;
+       if ((lu_gp)) {
+               lu_ci = &lu_gp->lu_gp_group.cg_item;
+               len += sprintf(page, "LU Group Alias: %s\nLU Group ID: %hu\n",
+                       config_item_name(lu_ci), lu_gp->lu_gp_id);
+       }
+       spin_unlock(&lu_gp_mem->lu_gp_mem_lock);
+
+       return len;
+}
+
+static ssize_t target_core_store_alua_lu_gp(
+       void *p,
+       const char *page,
+       size_t count)
+{
+       struct se_device *dev;
+       struct se_subsystem_dev *su_dev = (struct se_subsystem_dev *)p;
+       struct se_hba *hba = su_dev->se_dev_hba;
+       struct t10_alua_lu_gp *lu_gp = NULL, *lu_gp_new = NULL;
+       struct t10_alua_lu_gp_member *lu_gp_mem;
+       unsigned char buf[LU_GROUP_NAME_BUF];
+       int move = 0;
+
+       dev = su_dev->se_dev_ptr;
+       if (!(dev))
+               return -ENODEV;
+
+       if (T10_ALUA(su_dev)->alua_type != SPC3_ALUA_EMULATED) {
+               printk(KERN_WARNING "SPC3_ALUA_EMULATED not enabled for %s/%s\n",
+                       config_item_name(&hba->hba_group.cg_item),
+                       config_item_name(&su_dev->se_dev_group.cg_item));
+               return -EINVAL;
+       }
+       if (count > LU_GROUP_NAME_BUF) {
+               printk(KERN_ERR "ALUA LU Group Alias too large!\n");
+               return -EINVAL;
+       }
+       memset(buf, 0, LU_GROUP_NAME_BUF);
+       memcpy(buf, page, count);
+       /*
+        * Any ALUA logical unit alias besides "NULL" means we will be
+        * making a new group association.
+        */
+       if (strcmp(strstrip(buf), "NULL")) {
+               /*
+                * core_alua_get_lu_gp_by_name() will increment reference to
+                * struct t10_alua_lu_gp.  This reference is released with
+                * core_alua_get_lu_gp_by_name below().
+                */
+               lu_gp_new = core_alua_get_lu_gp_by_name(strstrip(buf));
+               if (!(lu_gp_new))
+                       return -ENODEV;
+       }
+       lu_gp_mem = dev->dev_alua_lu_gp_mem;
+       if (!(lu_gp_mem)) {
+               if (lu_gp_new)
+                       core_alua_put_lu_gp_from_name(lu_gp_new);
+               printk(KERN_ERR "NULL struct se_device->dev_alua_lu_gp_mem"
+                               " pointer\n");
+               return -EINVAL;
+       }
+
+       spin_lock(&lu_gp_mem->lu_gp_mem_lock);
+       lu_gp = lu_gp_mem->lu_gp;
+       if ((lu_gp)) {
+               /*
+                * Clearing an existing lu_gp association, and replacing
+                * with NULL
+                */
+               if (!(lu_gp_new)) {
+                       printk(KERN_INFO "Target_Core_ConfigFS: Releasing %s/%s"
+                               " from ALUA LU Group: core/alua/lu_gps/%s, ID:"
+                               " %hu\n",
+                               config_item_name(&hba->hba_group.cg_item),
+                               config_item_name(&su_dev->se_dev_group.cg_item),
+                               config_item_name(&lu_gp->lu_gp_group.cg_item),
+                               lu_gp->lu_gp_id);
+
+                       __core_alua_drop_lu_gp_mem(lu_gp_mem, lu_gp);
+                       spin_unlock(&lu_gp_mem->lu_gp_mem_lock);
+
+                       return count;
+               }
+               /*
+                * Removing existing association of lu_gp_mem with lu_gp
+                */
+               __core_alua_drop_lu_gp_mem(lu_gp_mem, lu_gp);
+               move = 1;
+       }
+       /*
+        * Associate lu_gp_mem with lu_gp_new.
+        */
+       __core_alua_attach_lu_gp_mem(lu_gp_mem, lu_gp_new);
+       spin_unlock(&lu_gp_mem->lu_gp_mem_lock);
+
+       printk(KERN_INFO "Target_Core_ConfigFS: %s %s/%s to ALUA LU Group:"
+               " core/alua/lu_gps/%s, ID: %hu\n",
+               (move) ? "Moving" : "Adding",
+               config_item_name(&hba->hba_group.cg_item),
+               config_item_name(&su_dev->se_dev_group.cg_item),
+               config_item_name(&lu_gp_new->lu_gp_group.cg_item),
+               lu_gp_new->lu_gp_id);
+
+       core_alua_put_lu_gp_from_name(lu_gp_new);
+       return count;
+}
+
+static struct target_core_configfs_attribute target_core_attr_dev_alua_lu_gp = {
+       .attr   = { .ca_owner = THIS_MODULE,
+                   .ca_name = "alua_lu_gp",
+                   .ca_mode = S_IRUGO | S_IWUSR },
+       .show   = target_core_show_alua_lu_gp,
+       .store  = target_core_store_alua_lu_gp,
+};
+
+static struct configfs_attribute *lio_core_dev_attrs[] = {
+       &target_core_attr_dev_info.attr,
+       &target_core_attr_dev_control.attr,
+       &target_core_attr_dev_alias.attr,
+       &target_core_attr_dev_udev_path.attr,
+       &target_core_attr_dev_enable.attr,
+       &target_core_attr_dev_alua_lu_gp.attr,
+       NULL,
+};
+
+static void target_core_dev_release(struct config_item *item)
+{
+       struct se_subsystem_dev *se_dev = container_of(to_config_group(item),
+                               struct se_subsystem_dev, se_dev_group);
+       struct config_group *dev_cg;
+
+       if (!(se_dev))
+               return;
+
+       dev_cg = &se_dev->se_dev_group;
+       kfree(dev_cg->default_groups);
+}
+
+static ssize_t target_core_dev_show(struct config_item *item,
+                                    struct configfs_attribute *attr,
+                                    char *page)
+{
+       struct se_subsystem_dev *se_dev = container_of(
+                       to_config_group(item), struct se_subsystem_dev,
+                       se_dev_group);
+       struct target_core_configfs_attribute *tc_attr = container_of(
+                       attr, struct target_core_configfs_attribute, attr);
+
+       if (!(tc_attr->show))
+               return -EINVAL;
+
+       return tc_attr->show((void *)se_dev, page);
+}
+
+static ssize_t target_core_dev_store(struct config_item *item,
+                                     struct configfs_attribute *attr,
+                                     const char *page, size_t count)
+{
+       struct se_subsystem_dev *se_dev = container_of(
+                       to_config_group(item), struct se_subsystem_dev,
+                       se_dev_group);
+       struct target_core_configfs_attribute *tc_attr = container_of(
+                       attr, struct target_core_configfs_attribute, attr);
+
+       if (!(tc_attr->store))
+               return -EINVAL;
+
+       return tc_attr->store((void *)se_dev, page, count);
+}
+
+static struct configfs_item_operations target_core_dev_item_ops = {
+       .release                = target_core_dev_release,
+       .show_attribute         = target_core_dev_show,
+       .store_attribute        = target_core_dev_store,
+};
+
+static struct config_item_type target_core_dev_cit = {
+       .ct_item_ops            = &target_core_dev_item_ops,
+       .ct_attrs               = lio_core_dev_attrs,
+       .ct_owner               = THIS_MODULE,
+};
+
+/* End functions for struct config_item_type target_core_dev_cit */
+
+/* Start functions for struct config_item_type target_core_alua_lu_gp_cit */
+
+CONFIGFS_EATTR_STRUCT(target_core_alua_lu_gp, t10_alua_lu_gp);
+#define SE_DEV_ALUA_LU_ATTR(_name, _mode)                              \
+static struct target_core_alua_lu_gp_attribute                         \
+                       target_core_alua_lu_gp_##_name =                \
+       __CONFIGFS_EATTR(_name, _mode,                                  \
+       target_core_alua_lu_gp_show_attr_##_name,                       \
+       target_core_alua_lu_gp_store_attr_##_name);
+
+#define SE_DEV_ALUA_LU_ATTR_RO(_name)                                  \
+static struct target_core_alua_lu_gp_attribute                         \
+                       target_core_alua_lu_gp_##_name =                \
+       __CONFIGFS_EATTR_RO(_name,                                      \
+       target_core_alua_lu_gp_show_attr_##_name);
+
+/*
+ * lu_gp_id
+ */
+static ssize_t target_core_alua_lu_gp_show_attr_lu_gp_id(
+       struct t10_alua_lu_gp *lu_gp,
+       char *page)
+{
+       if (!(lu_gp->lu_gp_valid_id))
+               return 0;
+
+       return sprintf(page, "%hu\n", lu_gp->lu_gp_id);
+}
+
+static ssize_t target_core_alua_lu_gp_store_attr_lu_gp_id(
+       struct t10_alua_lu_gp *lu_gp,
+       const char *page,
+       size_t count)
+{
+       struct config_group *alua_lu_gp_cg = &lu_gp->lu_gp_group;
+       unsigned long lu_gp_id;
+       int ret;
+
+       ret = strict_strtoul(page, 0, &lu_gp_id);
+       if (ret < 0) {
+               printk(KERN_ERR "strict_strtoul() returned %d for"
+                       " lu_gp_id\n", ret);
+               return -EINVAL;
+       }
+       if (lu_gp_id > 0x0000ffff) {
+               printk(KERN_ERR "ALUA lu_gp_id: %lu exceeds maximum:"
+                       " 0x0000ffff\n", lu_gp_id);
+               return -EINVAL;
+       }
+
+       ret = core_alua_set_lu_gp_id(lu_gp, (u16)lu_gp_id);
+       if (ret < 0)
+               return -EINVAL;
+
+       printk(KERN_INFO "Target_Core_ConfigFS: Set ALUA Logical Unit"
+               " Group: core/alua/lu_gps/%s to ID: %hu\n",
+               config_item_name(&alua_lu_gp_cg->cg_item),
+               lu_gp->lu_gp_id);
+
+       return count;
+}
+
+SE_DEV_ALUA_LU_ATTR(lu_gp_id, S_IRUGO | S_IWUSR);
+
+/*
+ * members
+ */
+static ssize_t target_core_alua_lu_gp_show_attr_members(
+       struct t10_alua_lu_gp *lu_gp,
+       char *page)
+{
+       struct se_device *dev;
+       struct se_hba *hba;
+       struct se_subsystem_dev *su_dev;
+       struct t10_alua_lu_gp_member *lu_gp_mem;
+       ssize_t len = 0, cur_len;
+       unsigned char buf[LU_GROUP_NAME_BUF];
+
+       memset(buf, 0, LU_GROUP_NAME_BUF);
+
+       spin_lock(&lu_gp->lu_gp_lock);
+       list_for_each_entry(lu_gp_mem, &lu_gp->lu_gp_mem_list, lu_gp_mem_list) {
+               dev = lu_gp_mem->lu_gp_mem_dev;
+               su_dev = dev->se_sub_dev;
+               hba = su_dev->se_dev_hba;
+
+               cur_len = snprintf(buf, LU_GROUP_NAME_BUF, "%s/%s\n",
+                       config_item_name(&hba->hba_group.cg_item),
+                       config_item_name(&su_dev->se_dev_group.cg_item));
+               cur_len++; /* Extra byte for NULL terminator */
+
+               if ((cur_len + len) > PAGE_SIZE) {
+                       printk(KERN_WARNING "Ran out of lu_gp_show_attr"
+                               "_members buffer\n");
+                       break;
+               }
+               memcpy(page+len, buf, cur_len);
+               len += cur_len;
+       }
+       spin_unlock(&lu_gp->lu_gp_lock);
+
+       return len;
+}
+
+SE_DEV_ALUA_LU_ATTR_RO(members);
+
+CONFIGFS_EATTR_OPS(target_core_alua_lu_gp, t10_alua_lu_gp, lu_gp_group);
+
+static struct configfs_attribute *target_core_alua_lu_gp_attrs[] = {
+       &target_core_alua_lu_gp_lu_gp_id.attr,
+       &target_core_alua_lu_gp_members.attr,
+       NULL,
+};
+
+static struct configfs_item_operations target_core_alua_lu_gp_ops = {
+       .show_attribute         = target_core_alua_lu_gp_attr_show,
+       .store_attribute        = target_core_alua_lu_gp_attr_store,
+};
+
+static struct config_item_type target_core_alua_lu_gp_cit = {
+       .ct_item_ops            = &target_core_alua_lu_gp_ops,
+       .ct_attrs               = target_core_alua_lu_gp_attrs,
+       .ct_owner               = THIS_MODULE,
+};
+
+/* End functions for struct config_item_type target_core_alua_lu_gp_cit */
+
+/* Start functions for struct config_item_type target_core_alua_lu_gps_cit */
+
+static struct config_group *target_core_alua_create_lu_gp(
+       struct config_group *group,
+       const char *name)
+{
+       struct t10_alua_lu_gp *lu_gp;
+       struct config_group *alua_lu_gp_cg = NULL;
+       struct config_item *alua_lu_gp_ci = NULL;
+
+       lu_gp = core_alua_allocate_lu_gp(name, 0);
+       if (IS_ERR(lu_gp))
+               return NULL;
+
+       alua_lu_gp_cg = &lu_gp->lu_gp_group;
+       alua_lu_gp_ci = &alua_lu_gp_cg->cg_item;
+
+       config_group_init_type_name(alua_lu_gp_cg, name,
+                       &target_core_alua_lu_gp_cit);
+
+       printk(KERN_INFO "Target_Core_ConfigFS: Allocated ALUA Logical Unit"
+               " Group: core/alua/lu_gps/%s\n",
+               config_item_name(alua_lu_gp_ci));
+
+       return alua_lu_gp_cg;
+
+}
+
+static void target_core_alua_drop_lu_gp(
+       struct config_group *group,
+       struct config_item *item)
+{
+       struct t10_alua_lu_gp *lu_gp = container_of(to_config_group(item),
+                       struct t10_alua_lu_gp, lu_gp_group);
+
+       printk(KERN_INFO "Target_Core_ConfigFS: Releasing ALUA Logical Unit"
+               " Group: core/alua/lu_gps/%s, ID: %hu\n",
+               config_item_name(item), lu_gp->lu_gp_id);
+
+       config_item_put(item);
+       core_alua_free_lu_gp(lu_gp);
+}
+
+static struct configfs_group_operations target_core_alua_lu_gps_group_ops = {
+       .make_group             = &target_core_alua_create_lu_gp,
+       .drop_item              = &target_core_alua_drop_lu_gp,
+};
+
+static struct config_item_type target_core_alua_lu_gps_cit = {
+       .ct_item_ops            = NULL,
+       .ct_group_ops           = &target_core_alua_lu_gps_group_ops,
+       .ct_owner               = THIS_MODULE,
+};
+
+/* End functions for struct config_item_type target_core_alua_lu_gps_cit */
+
+/* Start functions for struct config_item_type target_core_alua_tg_pt_gp_cit */
+
+CONFIGFS_EATTR_STRUCT(target_core_alua_tg_pt_gp, t10_alua_tg_pt_gp);
+#define SE_DEV_ALUA_TG_PT_ATTR(_name, _mode)                           \
+static struct target_core_alua_tg_pt_gp_attribute                      \
+                       target_core_alua_tg_pt_gp_##_name =             \
+       __CONFIGFS_EATTR(_name, _mode,                                  \
+       target_core_alua_tg_pt_gp_show_attr_##_name,                    \
+       target_core_alua_tg_pt_gp_store_attr_##_name);
+
+#define SE_DEV_ALUA_TG_PT_ATTR_RO(_name)                               \
+static struct target_core_alua_tg_pt_gp_attribute                      \
+                       target_core_alua_tg_pt_gp_##_name =             \
+       __CONFIGFS_EATTR_RO(_name,                                      \
+       target_core_alua_tg_pt_gp_show_attr_##_name);
+
+/*
+ * alua_access_state
+ */
+static ssize_t target_core_alua_tg_pt_gp_show_attr_alua_access_state(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       return sprintf(page, "%d\n",
+               atomic_read(&tg_pt_gp->tg_pt_gp_alua_access_state));
+}
+
+static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_access_state(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       const char *page,
+       size_t count)
+{
+       struct se_subsystem_dev *su_dev = tg_pt_gp->tg_pt_gp_su_dev;
+       unsigned long tmp;
+       int new_state, ret;
+
+       if (!(tg_pt_gp->tg_pt_gp_valid_id)) {
+               printk(KERN_ERR "Unable to do implict ALUA on non valid"
+                       " tg_pt_gp ID: %hu\n", tg_pt_gp->tg_pt_gp_valid_id);
+               return -EINVAL;
+       }
+
+       ret = strict_strtoul(page, 0, &tmp);
+       if (ret < 0) {
+               printk("Unable to extract new ALUA access state from"
+                               " %s\n", page);
+               return -EINVAL;
+       }
+       new_state = (int)tmp;
+
+       if (!(tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICT_ALUA)) {
+               printk(KERN_ERR "Unable to process implict configfs ALUA"
+                       " transition while TPGS_IMPLICT_ALUA is diabled\n");
+               return -EINVAL;
+       }
+
+       ret = core_alua_do_port_transition(tg_pt_gp, su_dev->se_dev_ptr,
+                                       NULL, NULL, new_state, 0);
+       return (!ret) ? count : -EINVAL;
+}
+
+SE_DEV_ALUA_TG_PT_ATTR(alua_access_state, S_IRUGO | S_IWUSR);
+
+/*
+ * alua_access_status
+ */
+static ssize_t target_core_alua_tg_pt_gp_show_attr_alua_access_status(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       return sprintf(page, "%s\n",
+               core_alua_dump_status(tg_pt_gp->tg_pt_gp_alua_access_status));
+}
+
+static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_access_status(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       const char *page,
+       size_t count)
+{
+       unsigned long tmp;
+       int new_status, ret;
+
+       if (!(tg_pt_gp->tg_pt_gp_valid_id)) {
+               printk(KERN_ERR "Unable to do set ALUA access status on non"
+                       " valid tg_pt_gp ID: %hu\n",
+                       tg_pt_gp->tg_pt_gp_valid_id);
+               return -EINVAL;
+       }
+
+       ret = strict_strtoul(page, 0, &tmp);
+       if (ret < 0) {
+               printk(KERN_ERR "Unable to extract new ALUA access status"
+                               " from %s\n", page);
+               return -EINVAL;
+       }
+       new_status = (int)tmp;
+
+       if ((new_status != ALUA_STATUS_NONE) &&
+           (new_status != ALUA_STATUS_ALTERED_BY_EXPLICT_STPG) &&
+           (new_status != ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA)) {
+               printk(KERN_ERR "Illegal ALUA access status: 0x%02x\n",
+                               new_status);
+               return -EINVAL;
+       }
+
+       tg_pt_gp->tg_pt_gp_alua_access_status = new_status;
+       return count;
+}
+
+SE_DEV_ALUA_TG_PT_ATTR(alua_access_status, S_IRUGO | S_IWUSR);
+
+/*
+ * alua_access_type
+ */
+static ssize_t target_core_alua_tg_pt_gp_show_attr_alua_access_type(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       return core_alua_show_access_type(tg_pt_gp, page);
+}
+
+static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_access_type(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       const char *page,
+       size_t count)
+{
+       return core_alua_store_access_type(tg_pt_gp, page, count);
+}
+
+SE_DEV_ALUA_TG_PT_ATTR(alua_access_type, S_IRUGO | S_IWUSR);
+
+/*
+ * alua_write_metadata
+ */
+static ssize_t target_core_alua_tg_pt_gp_show_attr_alua_write_metadata(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       return sprintf(page, "%d\n", tg_pt_gp->tg_pt_gp_write_metadata);
+}
+
+static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_write_metadata(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       const char *page,
+       size_t count)
+{
+       unsigned long tmp;
+       int ret;
+
+       ret = strict_strtoul(page, 0, &tmp);
+       if (ret < 0) {
+               printk(KERN_ERR "Unable to extract alua_write_metadata\n");
+               return -EINVAL;
+       }
+
+       if ((tmp != 0) && (tmp != 1)) {
+               printk(KERN_ERR "Illegal value for alua_write_metadata:"
+                       " %lu\n", tmp);
+               return -EINVAL;
+       }
+       tg_pt_gp->tg_pt_gp_write_metadata = (int)tmp;
+
+       return count;
+}
+
+SE_DEV_ALUA_TG_PT_ATTR(alua_write_metadata, S_IRUGO | S_IWUSR);
+
+
+
+/*
+ * nonop_delay_msecs
+ */
+static ssize_t target_core_alua_tg_pt_gp_show_attr_nonop_delay_msecs(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       return core_alua_show_nonop_delay_msecs(tg_pt_gp, page);
+
+}
+
+static ssize_t target_core_alua_tg_pt_gp_store_attr_nonop_delay_msecs(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       const char *page,
+       size_t count)
+{
+       return core_alua_store_nonop_delay_msecs(tg_pt_gp, page, count);
+}
+
+SE_DEV_ALUA_TG_PT_ATTR(nonop_delay_msecs, S_IRUGO | S_IWUSR);
+
+/*
+ * trans_delay_msecs
+ */
+static ssize_t target_core_alua_tg_pt_gp_show_attr_trans_delay_msecs(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       return core_alua_show_trans_delay_msecs(tg_pt_gp, page);
+}
+
+static ssize_t target_core_alua_tg_pt_gp_store_attr_trans_delay_msecs(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       const char *page,
+       size_t count)
+{
+       return core_alua_store_trans_delay_msecs(tg_pt_gp, page, count);
+}
+
+SE_DEV_ALUA_TG_PT_ATTR(trans_delay_msecs, S_IRUGO | S_IWUSR);
+
+/*
+ * preferred
+ */
+
+static ssize_t target_core_alua_tg_pt_gp_show_attr_preferred(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       return core_alua_show_preferred_bit(tg_pt_gp, page);
+}
+
+static ssize_t target_core_alua_tg_pt_gp_store_attr_preferred(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       const char *page,
+       size_t count)
+{
+       return core_alua_store_preferred_bit(tg_pt_gp, page, count);
+}
+
+SE_DEV_ALUA_TG_PT_ATTR(preferred, S_IRUGO | S_IWUSR);
+
+/*
+ * tg_pt_gp_id
+ */
+static ssize_t target_core_alua_tg_pt_gp_show_attr_tg_pt_gp_id(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       if (!(tg_pt_gp->tg_pt_gp_valid_id))
+               return 0;
+
+       return sprintf(page, "%hu\n", tg_pt_gp->tg_pt_gp_id);
+}
+
+static ssize_t target_core_alua_tg_pt_gp_store_attr_tg_pt_gp_id(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       const char *page,
+       size_t count)
+{
+       struct config_group *alua_tg_pt_gp_cg = &tg_pt_gp->tg_pt_gp_group;
+       unsigned long tg_pt_gp_id;
+       int ret;
+
+       ret = strict_strtoul(page, 0, &tg_pt_gp_id);
+       if (ret < 0) {
+               printk(KERN_ERR "strict_strtoul() returned %d for"
+                       " tg_pt_gp_id\n", ret);
+               return -EINVAL;
+       }
+       if (tg_pt_gp_id > 0x0000ffff) {
+               printk(KERN_ERR "ALUA tg_pt_gp_id: %lu exceeds maximum:"
+                       " 0x0000ffff\n", tg_pt_gp_id);
+               return -EINVAL;
+       }
+
+       ret = core_alua_set_tg_pt_gp_id(tg_pt_gp, (u16)tg_pt_gp_id);
+       if (ret < 0)
+               return -EINVAL;
+
+       printk(KERN_INFO "Target_Core_ConfigFS: Set ALUA Target Port Group: "
+               "core/alua/tg_pt_gps/%s to ID: %hu\n",
+               config_item_name(&alua_tg_pt_gp_cg->cg_item),
+               tg_pt_gp->tg_pt_gp_id);
+
+       return count;
+}
+
+SE_DEV_ALUA_TG_PT_ATTR(tg_pt_gp_id, S_IRUGO | S_IWUSR);
+
+/*
+ * members
+ */
+static ssize_t target_core_alua_tg_pt_gp_show_attr_members(
+       struct t10_alua_tg_pt_gp *tg_pt_gp,
+       char *page)
+{
+       struct se_port *port;
+       struct se_portal_group *tpg;
+       struct se_lun *lun;
+       struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;
+       ssize_t len = 0, cur_len;
+       unsigned char buf[TG_PT_GROUP_NAME_BUF];
+
+       memset(buf, 0, TG_PT_GROUP_NAME_BUF);
+
+       spin_lock(&tg_pt_gp->tg_pt_gp_lock);
+       list_for_each_entry(tg_pt_gp_mem, &tg_pt_gp->tg_pt_gp_mem_list,
+                       tg_pt_gp_mem_list) {
+               port = tg_pt_gp_mem->tg_pt;
+               tpg = port->sep_tpg;
+               lun = port->sep_lun;
+
+               cur_len = snprintf(buf, TG_PT_GROUP_NAME_BUF, "%s/%s/tpgt_%hu"
+                       "/%s\n", TPG_TFO(tpg)->get_fabric_name(),
+                       TPG_TFO(tpg)->tpg_get_wwn(tpg),
+                       TPG_TFO(tpg)->tpg_get_tag(tpg),
+                       config_item_name(&lun->lun_group.cg_item));
+               cur_len++; /* Extra byte for NULL terminator */
+
+               if ((cur_len + len) > PAGE_SIZE) {
+                       printk(KERN_WARNING "Ran out of lu_gp_show_attr"
+                               "_members buffer\n");
+                       break;
+               }
+               memcpy(page+len, buf, cur_len);
+               len += cur_len;
+       }
+       spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
+
+       return len;
+}
+
+SE_DEV_ALUA_TG_PT_ATTR_RO(members);
+
+CONFIGFS_EATTR_OPS(target_core_alua_tg_pt_gp, t10_alua_tg_pt_gp,
+                       tg_pt_gp_group);
+
+static struct configfs_attribute *target_core_alua_tg_pt_gp_attrs[] = {
+       &target_core_alua_tg_pt_gp_alua_access_state.attr,
+       &target_core_alua_tg_pt_gp_alua_access_status.attr,
+       &target_core_alua_tg_pt_gp_alua_access_type.attr,
+       &target_core_alua_tg_pt_gp_alua_write_metadata.attr,
+       &target_core_alua_tg_pt_gp_nonop_delay_msecs.attr,
+       &target_core_alua_tg_pt_gp_trans_delay_msecs.attr,
+       &target_core_alua_tg_pt_gp_preferred.attr,
+       &target_core_alua_tg_pt_gp_tg_pt_gp_id.attr,
+       &target_core_alua_tg_pt_gp_members.attr,
+       NULL,
+};
+
+static struct configfs_item_operations target_core_alua_tg_pt_gp_ops = {
+       .show_attribute         = target_core_alua_tg_pt_gp_attr_show,
+       .store_attribute        = target_core_alua_tg_pt_gp_attr_store,
+};
+
+static struct config_item_type target_core_alua_tg_pt_gp_cit = {
+       .ct_item_ops            = &target_core_alua_tg_pt_gp_ops,
+       .ct_attrs               = target_core_alua_tg_pt_gp_attrs,
+       .ct_owner               = THIS_MODULE,
+};
+
+/* End functions for struct config_item_type target_core_alua_tg_pt_gp_cit */
+
+/* Start functions for struct config_item_type target_core_alua_tg_pt_gps_cit */
+
+static struct config_group *target_core_alua_create_tg_pt_gp(
+       struct config_group *group,
+       const char *name)
+{
+       struct t10_alua *alua = container_of(group, struct t10_alua,
+                                       alua_tg_pt_gps_group);
+       struct t10_alua_tg_pt_gp *tg_pt_gp;
+       struct se_subsystem_dev *su_dev = alua->t10_sub_dev;
+       struct config_group *alua_tg_pt_gp_cg = NULL;
+       struct config_item *alua_tg_pt_gp_ci = NULL;
+
+       tg_pt_gp = core_alua_allocate_tg_pt_gp(su_dev, name, 0);
+       if (!(tg_pt_gp))
+               return NULL;
+
+       alua_tg_pt_gp_cg = &tg_pt_gp->tg_pt_gp_group;
+       alua_tg_pt_gp_ci = &alua_tg_pt_gp_cg->cg_item;
+
+       config_group_init_type_name(alua_tg_pt_gp_cg, name,
+                       &target_core_alua_tg_pt_gp_cit);
+
+       printk(KERN_INFO "Target_Core_ConfigFS: Allocated ALUA Target Port"
+               " Group: alua/tg_pt_gps/%s\n",
+               config_item_name(alua_tg_pt_gp_ci));
+
+       return alua_tg_pt_gp_cg;
+}
+
+static void target_core_alua_drop_tg_pt_gp(
+       struct config_group *group,
+       struct config_item *item)
+{
+       struct t10_alua_tg_pt_gp *tg_pt_gp = container_of(to_config_group(item),
+                       struct t10_alua_tg_pt_gp, tg_pt_gp_group);
+
+       printk(KERN_INFO "Target_Core_ConfigFS: Releasing ALUA Target Port"
+               " Group: alua/tg_pt_gps/%s, ID: %hu\n",
+               config_item_name(item), tg_pt_gp->tg_pt_gp_id);
+
+       config_item_put(item);
+       core_alua_free_tg_pt_gp(tg_pt_gp);
+}
+
+static struct configfs_group_operations target_core_alua_tg_pt_gps_group_ops = {
+       .make_group             = &target_core_alua_create_tg_pt_gp,
+       .drop_item              = &target_core_alua_drop_tg_pt_gp,
+};
+
+static struct config_item_type target_core_alua_tg_pt_gps_cit = {
+       .ct_group_ops           = &target_core_alua_tg_pt_gps_group_ops,
+       .ct_owner               = THIS_MODULE,
+};
+
+/* End functions for struct config_item_type target_core_alua_tg_pt_gps_cit */
+
+/* Start functions for struct config_item_type target_core_alua_cit */
+
+/*
+ * target_core_alua_cit is a ConfigFS group that lives under
+ * /sys/kernel/config/target/core/alua.  There are default groups
+ * core/alua/lu_gps and core/alua/tg_pt_gps that are attached to
+ * target_core_alua_cit in target_core_init_configfs() below.
+ */
+static struct config_item_type target_core_alua_cit = {
+       .ct_item_ops            = NULL,
+       .ct_attrs               = NULL,
+       .ct_owner               = THIS_MODULE,
+};
+
+/* End functions for struct config_item_type target_core_alua_cit */
+
+/* Start functions for struct config_item_type target_core_hba_cit */
+
+static struct config_group *target_core_make_subdev(
+       struct config_group *group,
+       const char *name)
+{
+       struct t10_alua_tg_pt_gp *tg_pt_gp;
+       struct se_subsystem_dev *se_dev;
+       struct se_subsystem_api *t;
+       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;
+
+       if (mutex_lock_interruptible(&hba->hba_access_mutex))
+               return NULL;
+
+       /*
+        * Locate the struct se_subsystem_api from parent's struct se_hba.
+        */
+       t = hba->transport;
+
+       se_dev = kzalloc(sizeof(struct se_subsystem_dev), GFP_KERNEL);
+       if (!se_dev) {
+               printk(KERN_ERR "Unable to allocate memory for"
+                               " struct se_subsystem_dev\n");
+               goto unlock;
+       }
+       INIT_LIST_HEAD(&se_dev->g_se_dev_list);
+       INIT_LIST_HEAD(&se_dev->t10_wwn.t10_vpd_list);
+       spin_lock_init(&se_dev->t10_wwn.t10_vpd_lock);
+       INIT_LIST_HEAD(&se_dev->t10_reservation.registration_list);
+       INIT_LIST_HEAD(&se_dev->t10_reservation.aptpl_reg_list);
+       spin_lock_init(&se_dev->t10_reservation.registration_lock);
+       spin_lock_init(&se_dev->t10_reservation.aptpl_reg_lock);
+       INIT_LIST_HEAD(&se_dev->t10_alua.tg_pt_gps_list);
+       spin_lock_init(&se_dev->t10_alua.tg_pt_gps_lock);
+       spin_lock_init(&se_dev->se_dev_lock);
+       se_dev->t10_reservation.pr_aptpl_buf_len = PR_APTPL_BUF_LEN;
+       se_dev->t10_wwn.t10_sub_dev = se_dev;
+       se_dev->t10_alua.t10_sub_dev = se_dev;
+       se_dev->se_dev_attrib.da_sub_dev = se_dev;
+
+       se_dev->se_dev_hba = hba;
+       dev_cg = &se_dev->se_dev_group;
+
+       dev_cg->default_groups = kzalloc(sizeof(struct config_group) * 6,
+                       GFP_KERNEL);
+       if (!(dev_cg->default_groups))
+               goto out;
+       /*
+        * Set se_dev_su_ptr from struct se_subsystem_api returned void ptr
+        * for ->allocate_virtdevice()
+        *
+        * se_dev->se_dev_ptr will be set after ->create_virtdev()
+        * has been called successfully in the next level up in the
+        * configfs tree for device object's struct config_group.
+        */
+       se_dev->se_dev_su_ptr = t->allocate_virtdevice(hba, name);
+       if (!(se_dev->se_dev_su_ptr)) {
+               printk(KERN_ERR "Unable to locate subsystem dependent pointer"
+                       " from allocate_virtdevice()\n");
+               goto out;
+       }
+       spin_lock(&se_global->g_device_lock);
+       list_add_tail(&se_dev->g_se_dev_list, &se_global->g_se_dev_list);
+       spin_unlock(&se_global->g_device_lock);
+
+       config_group_init_type_name(&se_dev->se_dev_group, name,
+                       &target_core_dev_cit);
+       config_group_init_type_name(&se_dev->se_dev_attrib.da_group, "attrib",
+                       &target_core_dev_attrib_cit);
+       config_group_init_type_name(&se_dev->se_dev_pr_group, "pr",
+                       &target_core_dev_pr_cit);
+       config_group_init_type_name(&se_dev->t10_wwn.t10_wwn_group, "wwn",
+                       &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);
+       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;
+       /*
+        * Add core/$HBA/$DEV/alua/tg_pt_gps/default_tg_pt_gp
+        */
+       tg_pt_gp = core_alua_allocate_tg_pt_gp(se_dev, "default_tg_pt_gp", 1);
+       if (!(tg_pt_gp))
+               goto out;
+
+       tg_pt_gp_cg = &T10_ALUA(se_dev)->alua_tg_pt_gps_group;
+       tg_pt_gp_cg->default_groups = kzalloc(sizeof(struct config_group) * 2,
+                               GFP_KERNEL);
+       if (!(tg_pt_gp_cg->default_groups)) {
+               printk(KERN_ERR "Unable to allocate tg_pt_gp_cg->"
+                               "default_groups\n");
+               goto out;
+       }
+
+       config_group_init_type_name(&tg_pt_gp->tg_pt_gp_group,
+                       "default_tg_pt_gp", &target_core_alua_tg_pt_gp_cit);
+       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;
+
+       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);
+
+       mutex_unlock(&hba->hba_access_mutex);
+       return &se_dev->se_dev_group;
+out:
+       if (T10_ALUA(se_dev)->default_tg_pt_gp) {
+               core_alua_free_tg_pt_gp(T10_ALUA(se_dev)->default_tg_pt_gp);
+               T10_ALUA(se_dev)->default_tg_pt_gp = NULL;
+       }
+       if (tg_pt_gp_cg)
+               kfree(tg_pt_gp_cg->default_groups);
+       if (dev_cg)
+               kfree(dev_cg->default_groups);
+       if (se_dev->se_dev_su_ptr)
+               t->free_device(se_dev->se_dev_su_ptr);
+       kfree(se_dev);
+unlock:
+       mutex_unlock(&hba->hba_access_mutex);
+       return NULL;
+}
+
+static void target_core_drop_subdev(
+       struct config_group *group,
+       struct config_item *item)
+{
+       struct se_subsystem_dev *se_dev = container_of(to_config_group(item),
+                               struct se_subsystem_dev, se_dev_group);
+       struct se_hba *hba;
+       struct se_subsystem_api *t;
+       struct config_item *df_item;
+       struct config_group *dev_cg, *tg_pt_gp_cg;
+       int i, ret;
+
+       hba = item_to_hba(&se_dev->se_dev_hba->hba_group.cg_item);
+
+       if (mutex_lock_interruptible(&hba->hba_access_mutex))
+               goto out;
+
+       t = hba->transport;
+
+       spin_lock(&se_global->g_device_lock);
+       list_del(&se_dev->g_se_dev_list);
+       spin_unlock(&se_global->g_device_lock);
+
+       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;
+               tg_pt_gp_cg->default_groups[i] = NULL;
+               config_item_put(df_item);
+       }
+       kfree(tg_pt_gp_cg->default_groups);
+       core_alua_free_tg_pt_gp(T10_ALUA(se_dev)->default_tg_pt_gp);
+       T10_ALUA(se_dev)->default_tg_pt_gp = NULL;
+
+       dev_cg = &se_dev->se_dev_group;
+       for (i = 0; dev_cg->default_groups[i]; i++) {
+               df_item = &dev_cg->default_groups[i]->cg_item;
+               dev_cg->default_groups[i] = NULL;
+               config_item_put(df_item);
+       }
+
+       config_item_put(item);
+       /*
+        * This pointer will set when the storage is enabled with:
+        * `echo 1 > $CONFIGFS/core/$HBA/$DEV/dev_enable`
+        */
+       if (se_dev->se_dev_ptr) {
+               printk(KERN_INFO "Target_Core_ConfigFS: Calling se_free_"
+                       "virtual_device() for se_dev_ptr: %p\n",
+                               se_dev->se_dev_ptr);
+
+               ret = se_free_virtual_device(se_dev->se_dev_ptr, hba);
+               if (ret < 0)
+                       goto hba_out;
+       } else {
+               /*
+                * Release struct se_subsystem_dev->se_dev_su_ptr..
+                */
+               printk(KERN_INFO "Target_Core_ConfigFS: Calling t->free_"
+                       "device() for se_dev_su_ptr: %p\n",
+                       se_dev->se_dev_su_ptr);
+
+               t->free_device(se_dev->se_dev_su_ptr);
+       }
+
+       printk(KERN_INFO "Target_Core_ConfigFS: Deallocating se_subsystem"
+               "_dev_t: %p\n", se_dev);
+
+hba_out:
+       mutex_unlock(&hba->hba_access_mutex);
+out:
+       kfree(se_dev);
+}
+
+static struct configfs_group_operations target_core_hba_group_ops = {
+       .make_group             = target_core_make_subdev,
+       .drop_item              = target_core_drop_subdev,
+};
+
+CONFIGFS_EATTR_STRUCT(target_core_hba, se_hba);
+#define SE_HBA_ATTR(_name, _mode)                              \
+static struct target_core_hba_attribute                                \
+               target_core_hba_##_name =                       \
+               __CONFIGFS_EATTR(_name, _mode,                  \
+               target_core_hba_show_attr_##_name,              \
+               target_core_hba_store_attr_##_name);
+
+#define SE_HBA_ATTR_RO(_name)                                  \
+static struct target_core_hba_attribute                                \
+               target_core_hba_##_name =                       \
+               __CONFIGFS_EATTR_RO(_name,                      \
+               target_core_hba_show_attr_##_name);
+
+static ssize_t target_core_hba_show_attr_hba_info(
+       struct se_hba *hba,
+       char *page)
+{
+       return sprintf(page, "HBA Index: %d plugin: %s version: %s\n",
+                       hba->hba_id, hba->transport->name,
+                       TARGET_CORE_CONFIGFS_VERSION);
+}
+
+SE_HBA_ATTR_RO(hba_info);
+
+static ssize_t target_core_hba_show_attr_hba_mode(struct se_hba *hba,
+                               char *page)
+{
+       int hba_mode = 0;
+
+       if (hba->hba_flags & HBA_FLAGS_PSCSI_MODE)
+               hba_mode = 1;
+
+       return sprintf(page, "%d\n", hba_mode);
+}
+
+static ssize_t target_core_hba_store_attr_hba_mode(struct se_hba *hba,
+                               const char *page, size_t count)
+{
+       struct se_subsystem_api *transport = hba->transport;
+       unsigned long mode_flag;
+       int ret;
+
+       if (transport->pmode_enable_hba == NULL)
+               return -EINVAL;
+
+       ret = strict_strtoul(page, 0, &mode_flag);
+       if (ret < 0) {
+               printk(KERN_ERR "Unable to extract hba mode flag: %d\n", ret);
+               return -EINVAL;
+       }
+
+       spin_lock(&hba->device_lock);
+       if (!(list_empty(&hba->hba_dev_list))) {
+               printk(KERN_ERR "Unable to set hba_mode with active devices\n");
+               spin_unlock(&hba->device_lock);
+               return -EINVAL;
+       }
+       spin_unlock(&hba->device_lock);
+
+       ret = transport->pmode_enable_hba(hba, mode_flag);
+       if (ret < 0)
+               return -EINVAL;
+       if (ret > 0)
+               hba->hba_flags |= HBA_FLAGS_PSCSI_MODE;
+       else if (ret == 0)
+               hba->hba_flags &= ~HBA_FLAGS_PSCSI_MODE;
+
+       return count;
+}
+
+SE_HBA_ATTR(hba_mode, S_IRUGO | S_IWUSR);
+
+CONFIGFS_EATTR_OPS(target_core_hba, se_hba, hba_group);
+
+static struct configfs_attribute *target_core_hba_attrs[] = {
+       &target_core_hba_hba_info.attr,
+       &target_core_hba_hba_mode.attr,
+       NULL,
+};
+
+static struct configfs_item_operations target_core_hba_item_ops = {
+       .show_attribute         = target_core_hba_attr_show,
+       .store_attribute        = target_core_hba_attr_store,
+};
+
+static struct config_item_type target_core_hba_cit = {
+       .ct_item_ops            = &target_core_hba_item_ops,
+       .ct_group_ops           = &target_core_hba_group_ops,
+       .ct_attrs               = target_core_hba_attrs,
+       .ct_owner               = THIS_MODULE,
+};
+
+static struct config_group *target_core_call_addhbatotarget(
+       struct config_group *group,
+       const char *name)
+{
+       char *se_plugin_str, *str, *str2;
+       struct se_hba *hba;
+       char buf[TARGET_CORE_NAME_MAX_LEN];
+       unsigned long plugin_dep_id = 0;
+       int ret;
+
+       memset(buf, 0, TARGET_CORE_NAME_MAX_LEN);
+       if (strlen(name) > TARGET_CORE_NAME_MAX_LEN) {
+               printk(KERN_ERR "Passed *name strlen(): %d exceeds"
+                       " TARGET_CORE_NAME_MAX_LEN: %d\n", (int)strlen(name),
+                       TARGET_CORE_NAME_MAX_LEN);
+               return ERR_PTR(-ENAMETOOLONG);
+       }
+       snprintf(buf, TARGET_CORE_NAME_MAX_LEN, "%s", name);
+
+       str = strstr(buf, "_");
+       if (!(str)) {
+               printk(KERN_ERR "Unable to locate \"_\" for $SUBSYSTEM_PLUGIN_$HOST_ID\n");
+               return ERR_PTR(-EINVAL);
+       }
+       se_plugin_str = buf;
+       /*
+        * Special case for subsystem plugins that have "_" in their names.
+        * Namely rd_direct and rd_mcp..
+        */
+       str2 = strstr(str+1, "_");
+       if ((str2)) {
+               *str2 = '\0'; /* Terminate for *se_plugin_str */
+               str2++; /* Skip to start of plugin dependent ID */