aboutsummaryrefslogtreecommitdiff
path: root/drivers/target
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/target')
-rw-r--r--drivers/target/Kconfig7
-rw-r--r--drivers/target/Makefile15
-rw-r--r--drivers/target/iscsi/Kconfig9
-rw-r--r--drivers/target/iscsi/Makefile21
-rw-r--r--drivers/target/iscsi/iscsi_target.c4786
-rw-r--r--drivers/target/iscsi/iscsi_target.h50
-rw-r--r--drivers/target/iscsi/iscsi_target_auth.c502
-rw-r--r--drivers/target/iscsi/iscsi_target_auth.h32
-rw-r--r--drivers/target/iscsi/iscsi_target_configfs.c2074
-rw-r--r--drivers/target/iscsi/iscsi_target_configfs.h7
-rw-r--r--drivers/target/iscsi/iscsi_target_core.h884
-rw-r--r--drivers/target/iscsi/iscsi_target_datain_values.c526
-rw-r--r--drivers/target/iscsi/iscsi_target_datain_values.h12
-rw-r--r--drivers/target/iscsi/iscsi_target_device.c66
-rw-r--r--drivers/target/iscsi/iscsi_target_device.h7
-rw-r--r--drivers/target/iscsi/iscsi_target_erl0.c1013
-rw-r--r--drivers/target/iscsi/iscsi_target_erl0.h15
-rw-r--r--drivers/target/iscsi/iscsi_target_erl1.c1294
-rw-r--r--drivers/target/iscsi/iscsi_target_erl1.h26
-rw-r--r--drivers/target/iscsi/iscsi_target_erl2.c436
-rw-r--r--drivers/target/iscsi/iscsi_target_erl2.h18
-rw-r--r--drivers/target/iscsi/iscsi_target_login.c1457
-rw-r--r--drivers/target/iscsi/iscsi_target_login.h21
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.c1254
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.h20
-rw-r--r--drivers/target/iscsi/iscsi_target_nodeattrib.c261
-rw-r--r--drivers/target/iscsi/iscsi_target_nodeattrib.h15
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.c1982
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.h290
-rw-r--r--drivers/target/iscsi/iscsi_target_seq_pdu_list.c700
-rw-r--r--drivers/target/iscsi/iscsi_target_seq_pdu_list.h86
-rw-r--r--drivers/target/iscsi/iscsi_target_stat.c949
-rw-r--r--drivers/target/iscsi/iscsi_target_stat.h64
-rw-r--r--drivers/target/iscsi/iscsi_target_tmr.c849
-rw-r--r--drivers/target/iscsi/iscsi_target_tmr.h14
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.c881
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.h43
-rw-r--r--drivers/target/iscsi/iscsi_target_tq.c513
-rw-r--r--drivers/target/iscsi/iscsi_target_tq.h84
-rw-r--r--drivers/target/iscsi/iscsi_target_transport.c55
-rw-r--r--drivers/target/iscsi/iscsi_target_util.c1510
-rw-r--r--drivers/target/iscsi/iscsi_target_util.h62
-rw-r--r--drivers/target/loopback/Kconfig5
-rw-r--r--drivers/target/loopback/Makefile1
-rw-r--r--drivers/target/loopback/tcm_loop.c1611
-rw-r--r--drivers/target/loopback/tcm_loop.h67
-rw-r--r--drivers/target/sbp/Kconfig11
-rw-r--r--drivers/target/sbp/Makefile1
-rw-r--r--drivers/target/sbp/sbp_target.c2614
-rw-r--r--drivers/target/sbp/sbp_target.h251
-rw-r--r--drivers/target/target_core_alua.c1513
-rw-r--r--drivers/target/target_core_alua.h57
-rw-r--r--drivers/target/target_core_cdb.c1131
-rw-r--r--drivers/target/target_core_configfs.c1752
-rw-r--r--drivers/target/target_core_device.c1830
-rw-r--r--drivers/target/target_core_fabric_configfs.c437
-rw-r--r--drivers/target/target_core_fabric_lib.c60
-rw-r--r--drivers/target/target_core_file.c962
-rw-r--r--drivers/target/target_core_file.h28
-rw-r--r--drivers/target/target_core_hba.c67
-rw-r--r--drivers/target/target_core_hba.h7
-rw-r--r--drivers/target/target_core_iblock.c995
-rw-r--r--drivers/target/target_core_iblock.h21
-rw-r--r--drivers/target/target_core_internal.h115
-rw-r--r--drivers/target/target_core_pr.c2379
-rw-r--r--drivers/target/target_core_pr.h20
-rw-r--r--drivers/target/target_core_pscsi.c1021
-rw-r--r--drivers/target/target_core_pscsi.h10
-rw-r--r--drivers/target/target_core_rd.c1134
-rw-r--r--drivers/target/target_core_rd.h34
-rw-r--r--drivers/target/target_core_sbc.c1336
-rw-r--r--drivers/target/target_core_scdb.c105
-rw-r--r--drivers/target/target_core_scdb.h10
-rw-r--r--drivers/target/target_core_spc.c1445
-rw-r--r--drivers/target/target_core_stat.c1635
-rw-r--r--drivers/target/target_core_tmr.c477
-rw-r--r--drivers/target/target_core_tpg.c497
-rw-r--r--drivers/target/target_core_transport.c6570
-rw-r--r--drivers/target/target_core_ua.c102
-rw-r--r--drivers/target/target_core_ua.h4
-rw-r--r--drivers/target/target_core_xcopy.c1081
-rw-r--r--drivers/target/target_core_xcopy.h62
-rw-r--r--drivers/target/tcm_fc/Kconfig5
-rw-r--r--drivers/target/tcm_fc/Makefile6
-rw-r--r--drivers/target/tcm_fc/tcm_fc.h183
-rw-r--r--drivers/target/tcm_fc/tfc_cmd.c585
-rw-r--r--drivers/target/tcm_fc/tfc_conf.c642
-rw-r--r--drivers/target/tcm_fc/tfc_io.c379
-rw-r--r--drivers/target/tcm_fc/tfc_sess.c504
89 files changed, 43953 insertions, 12719 deletions
diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig
index 2fac3be209a..dc2d84ac5a0 100644
--- a/drivers/target/Kconfig
+++ b/drivers/target/Kconfig
@@ -3,6 +3,7 @@ menuconfig TARGET_CORE
tristate "Generic Target Core Mod (TCM) and ConfigFS Infrastructure"
depends on SCSI && BLOCK
select CONFIGFS_FS
+ select CRC_T10DIF
default n
help
Say Y or M here to enable the TCM Storage Engine and ConfigFS enabled
@@ -13,6 +14,7 @@ if TARGET_CORE
config TCM_IBLOCK
tristate "TCM/IBLOCK Subsystem Plugin for Linux/BLOCK"
+ select BLK_DEV_INTEGRITY
help
Say Y here to enable the TCM/IBLOCK subsystem plugin for non-buffered
access to Linux/Block devices using BIO
@@ -29,4 +31,9 @@ config TCM_PSCSI
Say Y here to enable the TCM/pSCSI subsystem plugin for non-buffered
passthrough access to Linux/SCSI device
+source "drivers/target/loopback/Kconfig"
+source "drivers/target/tcm_fc/Kconfig"
+source "drivers/target/iscsi/Kconfig"
+source "drivers/target/sbp/Kconfig"
+
endif
diff --git a/drivers/target/Makefile b/drivers/target/Makefile
index 973bb190ef5..85b012d2f89 100644
--- a/drivers/target/Makefile
+++ b/drivers/target/Makefile
@@ -1,4 +1,3 @@
-EXTRA_CFLAGS += -I$(srctree)/drivers/target/ -I$(srctree)/drivers/scsi/
target_core_mod-y := target_core_configfs.o \
target_core_device.o \
@@ -7,13 +6,15 @@ target_core_mod-y := target_core_configfs.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_sbc.o \
+ target_core_spc.o \
target_core_ua.o \
- target_core_rd.o
+ target_core_rd.o \
+ target_core_stat.o \
+ target_core_xcopy.o
obj-$(CONFIG_TARGET_CORE) += target_core_mod.o
@@ -21,3 +22,9 @@ obj-$(CONFIG_TARGET_CORE) += target_core_mod.o
obj-$(CONFIG_TCM_IBLOCK) += target_core_iblock.o
obj-$(CONFIG_TCM_FILEIO) += target_core_file.o
obj-$(CONFIG_TCM_PSCSI) += target_core_pscsi.o
+
+# Fabric modules
+obj-$(CONFIG_LOOPBACK_TARGET) += loopback/
+obj-$(CONFIG_TCM_FC) += tcm_fc/
+obj-$(CONFIG_ISCSI_TARGET) += iscsi/
+obj-$(CONFIG_SBP_TARGET) += sbp/
diff --git a/drivers/target/iscsi/Kconfig b/drivers/target/iscsi/Kconfig
new file mode 100644
index 00000000000..8345fb457a4
--- /dev/null
+++ b/drivers/target/iscsi/Kconfig
@@ -0,0 +1,9 @@
+config ISCSI_TARGET
+ tristate "Linux-iSCSI.org iSCSI Target Mode Stack"
+ depends on NET
+ select CRYPTO
+ select CRYPTO_CRC32C
+ select CRYPTO_CRC32C_INTEL if X86
+ help
+ Say M here to enable the ConfigFS enabled Linux-iSCSI.org iSCSI
+ Target Mode Stack.
diff --git a/drivers/target/iscsi/Makefile b/drivers/target/iscsi/Makefile
new file mode 100644
index 00000000000..13a92403fe3
--- /dev/null
+++ b/drivers/target/iscsi/Makefile
@@ -0,0 +1,21 @@
+iscsi_target_mod-y += iscsi_target_parameters.o \
+ iscsi_target_seq_pdu_list.o \
+ iscsi_target_tq.o \
+ iscsi_target_auth.o \
+ iscsi_target_datain_values.o \
+ iscsi_target_device.o \
+ iscsi_target_erl0.o \
+ iscsi_target_erl1.o \
+ iscsi_target_erl2.o \
+ iscsi_target_login.o \
+ iscsi_target_nego.o \
+ iscsi_target_nodeattrib.o \
+ iscsi_target_tmr.o \
+ iscsi_target_tpg.o \
+ iscsi_target_util.o \
+ iscsi_target.o \
+ iscsi_target_configfs.o \
+ iscsi_target_stat.o \
+ iscsi_target_transport.o
+
+obj-$(CONFIG_ISCSI_TARGET) += iscsi_target_mod.o
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
new file mode 100644
index 00000000000..1f4c794f5fc
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -0,0 +1,4786 @@
+/*******************************************************************************
+ * This file contains main functions related to the iSCSI Target Core Driver.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/string.h>
+#include <linux/kthread.h>
+#include <linux/crypto.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/idr.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi_device.h>
+#include <scsi/iscsi_proto.h>
+#include <scsi/scsi_tcq.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_parameters.h"
+#include "iscsi_target_seq_pdu_list.h"
+#include "iscsi_target_tq.h"
+#include "iscsi_target_configfs.h"
+#include "iscsi_target_datain_values.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target_login.h"
+#include "iscsi_target_tmr.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_stat.h"
+
+#include <target/iscsi/iscsi_transport.h>
+
+static LIST_HEAD(g_tiqn_list);
+static LIST_HEAD(g_np_list);
+static DEFINE_SPINLOCK(tiqn_lock);
+static DEFINE_MUTEX(np_lock);
+
+static struct idr tiqn_idr;
+struct idr sess_idr;
+struct mutex auth_id_lock;
+spinlock_t sess_idr_lock;
+
+struct iscsit_global *iscsit_global;
+
+struct kmem_cache *lio_qr_cache;
+struct kmem_cache *lio_dr_cache;
+struct kmem_cache *lio_ooo_cache;
+struct kmem_cache *lio_r2t_cache;
+
+static int iscsit_handle_immediate_data(struct iscsi_cmd *,
+ struct iscsi_scsi_req *, u32);
+
+struct iscsi_tiqn *iscsit_get_tiqn_for_login(unsigned char *buf)
+{
+ struct iscsi_tiqn *tiqn = NULL;
+
+ spin_lock(&tiqn_lock);
+ list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) {
+ if (!strcmp(tiqn->tiqn, buf)) {
+
+ spin_lock(&tiqn->tiqn_state_lock);
+ if (tiqn->tiqn_state == TIQN_STATE_ACTIVE) {
+ tiqn->tiqn_access_count++;
+ spin_unlock(&tiqn->tiqn_state_lock);
+ spin_unlock(&tiqn_lock);
+ return tiqn;
+ }
+ spin_unlock(&tiqn->tiqn_state_lock);
+ }
+ }
+ spin_unlock(&tiqn_lock);
+
+ return NULL;
+}
+
+static int iscsit_set_tiqn_shutdown(struct iscsi_tiqn *tiqn)
+{
+ spin_lock(&tiqn->tiqn_state_lock);
+ if (tiqn->tiqn_state == TIQN_STATE_ACTIVE) {
+ tiqn->tiqn_state = TIQN_STATE_SHUTDOWN;
+ spin_unlock(&tiqn->tiqn_state_lock);
+ return 0;
+ }
+ spin_unlock(&tiqn->tiqn_state_lock);
+
+ return -1;
+}
+
+void iscsit_put_tiqn_for_login(struct iscsi_tiqn *tiqn)
+{
+ spin_lock(&tiqn->tiqn_state_lock);
+ tiqn->tiqn_access_count--;
+ spin_unlock(&tiqn->tiqn_state_lock);
+}
+
+/*
+ * Note that IQN formatting is expected to be done in userspace, and
+ * no explict IQN format checks are done here.
+ */
+struct iscsi_tiqn *iscsit_add_tiqn(unsigned char *buf)
+{
+ struct iscsi_tiqn *tiqn = NULL;
+ int ret;
+
+ if (strlen(buf) >= ISCSI_IQN_LEN) {
+ pr_err("Target IQN exceeds %d bytes\n",
+ ISCSI_IQN_LEN);
+ return ERR_PTR(-EINVAL);
+ }
+
+ tiqn = kzalloc(sizeof(struct iscsi_tiqn), GFP_KERNEL);
+ if (!tiqn) {
+ pr_err("Unable to allocate struct iscsi_tiqn\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sprintf(tiqn->tiqn, "%s", buf);
+ INIT_LIST_HEAD(&tiqn->tiqn_list);
+ INIT_LIST_HEAD(&tiqn->tiqn_tpg_list);
+ spin_lock_init(&tiqn->tiqn_state_lock);
+ spin_lock_init(&tiqn->tiqn_tpg_lock);
+ spin_lock_init(&tiqn->sess_err_stats.lock);
+ spin_lock_init(&tiqn->login_stats.lock);
+ spin_lock_init(&tiqn->logout_stats.lock);
+
+ tiqn->tiqn_state = TIQN_STATE_ACTIVE;
+
+ idr_preload(GFP_KERNEL);
+ spin_lock(&tiqn_lock);
+
+ ret = idr_alloc(&tiqn_idr, NULL, 0, 0, GFP_NOWAIT);
+ if (ret < 0) {
+ pr_err("idr_alloc() failed for tiqn->tiqn_index\n");
+ spin_unlock(&tiqn_lock);
+ idr_preload_end();
+ kfree(tiqn);
+ return ERR_PTR(ret);
+ }
+ tiqn->tiqn_index = ret;
+ list_add_tail(&tiqn->tiqn_list, &g_tiqn_list);
+
+ spin_unlock(&tiqn_lock);
+ idr_preload_end();
+
+ pr_debug("CORE[0] - Added iSCSI Target IQN: %s\n", tiqn->tiqn);
+
+ return tiqn;
+
+}
+
+static void iscsit_wait_for_tiqn(struct iscsi_tiqn *tiqn)
+{
+ /*
+ * Wait for accesses to said struct iscsi_tiqn to end.
+ */
+ spin_lock(&tiqn->tiqn_state_lock);
+ while (tiqn->tiqn_access_count != 0) {
+ spin_unlock(&tiqn->tiqn_state_lock);
+ msleep(10);
+ spin_lock(&tiqn->tiqn_state_lock);
+ }
+ spin_unlock(&tiqn->tiqn_state_lock);
+}
+
+void iscsit_del_tiqn(struct iscsi_tiqn *tiqn)
+{
+ /*
+ * iscsit_set_tiqn_shutdown sets tiqn->tiqn_state = TIQN_STATE_SHUTDOWN
+ * while holding tiqn->tiqn_state_lock. This means that all subsequent
+ * attempts to access this struct iscsi_tiqn will fail from both transport
+ * fabric and control code paths.
+ */
+ if (iscsit_set_tiqn_shutdown(tiqn) < 0) {
+ pr_err("iscsit_set_tiqn_shutdown() failed\n");
+ return;
+ }
+
+ iscsit_wait_for_tiqn(tiqn);
+
+ spin_lock(&tiqn_lock);
+ list_del(&tiqn->tiqn_list);
+ idr_remove(&tiqn_idr, tiqn->tiqn_index);
+ spin_unlock(&tiqn_lock);
+
+ pr_debug("CORE[0] - Deleted iSCSI Target IQN: %s\n",
+ tiqn->tiqn);
+ kfree(tiqn);
+}
+
+int iscsit_access_np(struct iscsi_np *np, struct iscsi_portal_group *tpg)
+{
+ int ret;
+ /*
+ * Determine if the network portal is accepting storage traffic.
+ */
+ spin_lock_bh(&np->np_thread_lock);
+ if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
+ spin_unlock_bh(&np->np_thread_lock);
+ return -1;
+ }
+ spin_unlock_bh(&np->np_thread_lock);
+ /*
+ * Determine if the portal group is accepting storage traffic.
+ */
+ spin_lock_bh(&tpg->tpg_state_lock);
+ if (tpg->tpg_state != TPG_STATE_ACTIVE) {
+ spin_unlock_bh(&tpg->tpg_state_lock);
+ return -1;
+ }
+ spin_unlock_bh(&tpg->tpg_state_lock);
+
+ /*
+ * Here we serialize access across the TIQN+TPG Tuple.
+ */
+ ret = down_interruptible(&tpg->np_login_sem);
+ if ((ret != 0) || signal_pending(current))
+ return -1;
+
+ spin_lock_bh(&tpg->tpg_state_lock);
+ if (tpg->tpg_state != TPG_STATE_ACTIVE) {
+ spin_unlock_bh(&tpg->tpg_state_lock);
+ up(&tpg->np_login_sem);
+ return -1;
+ }
+ spin_unlock_bh(&tpg->tpg_state_lock);
+
+ return 0;
+}
+
+void iscsit_login_kref_put(struct kref *kref)
+{
+ struct iscsi_tpg_np *tpg_np = container_of(kref,
+ struct iscsi_tpg_np, tpg_np_kref);
+
+ complete(&tpg_np->tpg_np_comp);
+}
+
+int iscsit_deaccess_np(struct iscsi_np *np, struct iscsi_portal_group *tpg,
+ struct iscsi_tpg_np *tpg_np)
+{
+ struct iscsi_tiqn *tiqn = tpg->tpg_tiqn;
+
+ up(&tpg->np_login_sem);
+
+ if (tpg_np)
+ kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
+
+ if (tiqn)
+ iscsit_put_tiqn_for_login(tiqn);
+
+ return 0;
+}
+
+bool iscsit_check_np_match(
+ struct __kernel_sockaddr_storage *sockaddr,
+ struct iscsi_np *np,
+ int network_transport)
+{
+ struct sockaddr_in *sock_in, *sock_in_e;
+ struct sockaddr_in6 *sock_in6, *sock_in6_e;
+ bool ip_match = false;
+ u16 port;
+
+ if (sockaddr->ss_family == AF_INET6) {
+ sock_in6 = (struct sockaddr_in6 *)sockaddr;
+ sock_in6_e = (struct sockaddr_in6 *)&np->np_sockaddr;
+
+ if (!memcmp(&sock_in6->sin6_addr.in6_u,
+ &sock_in6_e->sin6_addr.in6_u,
+ sizeof(struct in6_addr)))
+ ip_match = true;
+
+ port = ntohs(sock_in6->sin6_port);
+ } else {
+ sock_in = (struct sockaddr_in *)sockaddr;
+ sock_in_e = (struct sockaddr_in *)&np->np_sockaddr;
+
+ if (sock_in->sin_addr.s_addr == sock_in_e->sin_addr.s_addr)
+ ip_match = true;
+
+ port = ntohs(sock_in->sin_port);
+ }
+
+ if (ip_match && (np->np_port == port) &&
+ (np->np_network_transport == network_transport))
+ return true;
+
+ return false;
+}
+
+/*
+ * Called with mutex np_lock held
+ */
+static struct iscsi_np *iscsit_get_np(
+ struct __kernel_sockaddr_storage *sockaddr,
+ int network_transport)
+{
+ struct iscsi_np *np;
+ bool match;
+
+ list_for_each_entry(np, &g_np_list, np_list) {
+ spin_lock_bh(&np->np_thread_lock);
+ if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
+ spin_unlock_bh(&np->np_thread_lock);
+ continue;
+ }
+
+ match = iscsit_check_np_match(sockaddr, np, network_transport);
+ if (match) {
+ /*
+ * Increment the np_exports reference count now to
+ * prevent iscsit_del_np() below from being called
+ * while iscsi_tpg_add_network_portal() is called.
+ */
+ np->np_exports++;
+ spin_unlock_bh(&np->np_thread_lock);
+ return np;
+ }
+ spin_unlock_bh(&np->np_thread_lock);
+ }
+
+ return NULL;
+}
+
+struct iscsi_np *iscsit_add_np(
+ struct __kernel_sockaddr_storage *sockaddr,
+ char *ip_str,
+ int network_transport)
+{
+ struct sockaddr_in *sock_in;
+ struct sockaddr_in6 *sock_in6;
+ struct iscsi_np *np;
+ int ret;
+
+ mutex_lock(&np_lock);
+
+ /*
+ * Locate the existing struct iscsi_np if already active..
+ */
+ np = iscsit_get_np(sockaddr, network_transport);
+ if (np) {
+ mutex_unlock(&np_lock);
+ return np;
+ }
+
+ np = kzalloc(sizeof(struct iscsi_np), GFP_KERNEL);
+ if (!np) {
+ pr_err("Unable to allocate memory for struct iscsi_np\n");
+ mutex_unlock(&np_lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ np->np_flags |= NPF_IP_NETWORK;
+ if (sockaddr->ss_family == AF_INET6) {
+ sock_in6 = (struct sockaddr_in6 *)sockaddr;
+ snprintf(np->np_ip, IPV6_ADDRESS_SPACE, "%s", ip_str);
+ np->np_port = ntohs(sock_in6->sin6_port);
+ } else {
+ sock_in = (struct sockaddr_in *)sockaddr;
+ sprintf(np->np_ip, "%s", ip_str);
+ np->np_port = ntohs(sock_in->sin_port);
+ }
+
+ np->np_network_transport = network_transport;
+ spin_lock_init(&np->np_thread_lock);
+ init_completion(&np->np_restart_comp);
+ INIT_LIST_HEAD(&np->np_list);
+
+ ret = iscsi_target_setup_login_socket(np, sockaddr);
+ if (ret != 0) {
+ kfree(np);
+ mutex_unlock(&np_lock);
+ return ERR_PTR(ret);
+ }
+
+ np->np_thread = kthread_run(iscsi_target_login_thread, np, "iscsi_np");
+ if (IS_ERR(np->np_thread)) {
+ pr_err("Unable to create kthread: iscsi_np\n");
+ ret = PTR_ERR(np->np_thread);
+ kfree(np);
+ mutex_unlock(&np_lock);
+ return ERR_PTR(ret);
+ }
+ /*
+ * Increment the np_exports reference count now to prevent
+ * iscsit_del_np() below from being run while a new call to
+ * iscsi_tpg_add_network_portal() for a matching iscsi_np is
+ * active. We don't need to hold np->np_thread_lock at this
+ * point because iscsi_np has not been added to g_np_list yet.
+ */
+ np->np_exports = 1;
+ np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
+
+ list_add_tail(&np->np_list, &g_np_list);
+ mutex_unlock(&np_lock);
+
+ pr_debug("CORE[0] - Added Network Portal: %s:%hu on %s\n",
+ np->np_ip, np->np_port, np->np_transport->name);
+
+ return np;
+}
+
+int iscsit_reset_np_thread(
+ struct iscsi_np *np,
+ struct iscsi_tpg_np *tpg_np,
+ struct iscsi_portal_group *tpg,
+ bool shutdown)
+{
+ spin_lock_bh(&np->np_thread_lock);
+ if (np->np_thread_state == ISCSI_NP_THREAD_INACTIVE) {
+ spin_unlock_bh(&np->np_thread_lock);
+ return 0;
+ }
+ np->np_thread_state = ISCSI_NP_THREAD_RESET;
+
+ if (np->np_thread) {
+ spin_unlock_bh(&np->np_thread_lock);
+ send_sig(SIGINT, np->np_thread, 1);
+ wait_for_completion(&np->np_restart_comp);
+ spin_lock_bh(&np->np_thread_lock);
+ }
+ spin_unlock_bh(&np->np_thread_lock);
+
+ if (tpg_np && shutdown) {
+ kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
+
+ wait_for_completion(&tpg_np->tpg_np_comp);
+ }
+
+ return 0;
+}
+
+static void iscsit_free_np(struct iscsi_np *np)
+{
+ if (np->np_socket)
+ sock_release(np->np_socket);
+}
+
+int iscsit_del_np(struct iscsi_np *np)
+{
+ spin_lock_bh(&np->np_thread_lock);
+ np->np_exports--;
+ if (np->np_exports) {
+ np->enabled = true;
+ spin_unlock_bh(&np->np_thread_lock);
+ return 0;
+ }
+ np->np_thread_state = ISCSI_NP_THREAD_SHUTDOWN;
+ spin_unlock_bh(&np->np_thread_lock);
+
+ if (np->np_thread) {
+ /*
+ * We need to send the signal to wakeup Linux/Net
+ * which may be sleeping in sock_accept()..
+ */
+ send_sig(SIGINT, np->np_thread, 1);
+ kthread_stop(np->np_thread);
+ np->np_thread = NULL;
+ }
+
+ np->np_transport->iscsit_free_np(np);
+
+ mutex_lock(&np_lock);
+ list_del(&np->np_list);
+ mutex_unlock(&np_lock);
+
+ pr_debug("CORE[0] - Removed Network Portal: %s:%hu on %s\n",
+ np->np_ip, np->np_port, np->np_transport->name);
+
+ iscsit_put_transport(np->np_transport);
+ kfree(np);
+ return 0;
+}
+
+static int iscsit_immediate_queue(struct iscsi_conn *, struct iscsi_cmd *, int);
+static int iscsit_response_queue(struct iscsi_conn *, struct iscsi_cmd *, int);
+
+static int iscsit_queue_rsp(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
+{
+ iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
+ return 0;
+}
+
+static void iscsit_aborted_task(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
+{
+ bool scsi_cmd = (cmd->iscsi_opcode == ISCSI_OP_SCSI_CMD);
+
+ spin_lock_bh(&conn->cmd_lock);
+ if (!list_empty(&cmd->i_conn_node))
+ list_del_init(&cmd->i_conn_node);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ __iscsit_free_cmd(cmd, scsi_cmd, true);
+}
+
+static enum target_prot_op iscsit_get_sup_prot_ops(struct iscsi_conn *conn)
+{
+ return TARGET_PROT_NORMAL;
+}
+
+static struct iscsit_transport iscsi_target_transport = {
+ .name = "iSCSI/TCP",
+ .transport_type = ISCSI_TCP,
+ .owner = NULL,
+ .iscsit_setup_np = iscsit_setup_np,
+ .iscsit_accept_np = iscsit_accept_np,
+ .iscsit_free_np = iscsit_free_np,
+ .iscsit_get_login_rx = iscsit_get_login_rx,
+ .iscsit_put_login_tx = iscsit_put_login_tx,
+ .iscsit_get_dataout = iscsit_build_r2ts_for_cmd,
+ .iscsit_immediate_queue = iscsit_immediate_queue,
+ .iscsit_response_queue = iscsit_response_queue,
+ .iscsit_queue_data_in = iscsit_queue_rsp,
+ .iscsit_queue_status = iscsit_queue_rsp,
+ .iscsit_aborted_task = iscsit_aborted_task,
+ .iscsit_get_sup_prot_ops = iscsit_get_sup_prot_ops,
+};
+
+static int __init iscsi_target_init_module(void)
+{
+ int ret = 0;
+
+ pr_debug("iSCSI-Target "ISCSIT_VERSION"\n");
+
+ iscsit_global = kzalloc(sizeof(struct iscsit_global), GFP_KERNEL);
+ if (!iscsit_global) {
+ pr_err("Unable to allocate memory for iscsit_global\n");
+ return -1;
+ }
+ mutex_init(&auth_id_lock);
+ spin_lock_init(&sess_idr_lock);
+ idr_init(&tiqn_idr);
+ idr_init(&sess_idr);
+
+ ret = iscsi_target_register_configfs();
+ if (ret < 0)
+ goto out;
+
+ ret = iscsi_thread_set_init();
+ if (ret < 0)
+ goto configfs_out;
+
+ if (iscsi_allocate_thread_sets(TARGET_THREAD_SET_COUNT) !=
+ TARGET_THREAD_SET_COUNT) {
+ pr_err("iscsi_allocate_thread_sets() returned"
+ " unexpected value!\n");
+ goto ts_out1;
+ }
+
+ lio_qr_cache = kmem_cache_create("lio_qr_cache",
+ sizeof(struct iscsi_queue_req),
+ __alignof__(struct iscsi_queue_req), 0, NULL);
+ if (!lio_qr_cache) {
+ pr_err("nable to kmem_cache_create() for"
+ " lio_qr_cache\n");
+ goto ts_out2;
+ }
+
+ lio_dr_cache = kmem_cache_create("lio_dr_cache",
+ sizeof(struct iscsi_datain_req),
+ __alignof__(struct iscsi_datain_req), 0, NULL);
+ if (!lio_dr_cache) {
+ pr_err("Unable to kmem_cache_create() for"
+ " lio_dr_cache\n");
+ goto qr_out;
+ }
+
+ lio_ooo_cache = kmem_cache_create("lio_ooo_cache",
+ sizeof(struct iscsi_ooo_cmdsn),
+ __alignof__(struct iscsi_ooo_cmdsn), 0, NULL);
+ if (!lio_ooo_cache) {
+ pr_err("Unable to kmem_cache_create() for"
+ " lio_ooo_cache\n");
+ goto dr_out;
+ }
+
+ lio_r2t_cache = kmem_cache_create("lio_r2t_cache",
+ sizeof(struct iscsi_r2t), __alignof__(struct iscsi_r2t),
+ 0, NULL);
+ if (!lio_r2t_cache) {
+ pr_err("Unable to kmem_cache_create() for"
+ " lio_r2t_cache\n");
+ goto ooo_out;
+ }
+
+ iscsit_register_transport(&iscsi_target_transport);
+
+ if (iscsit_load_discovery_tpg() < 0)
+ goto r2t_out;
+
+ return ret;
+r2t_out:
+ kmem_cache_destroy(lio_r2t_cache);
+ooo_out:
+ kmem_cache_destroy(lio_ooo_cache);
+dr_out:
+ kmem_cache_destroy(lio_dr_cache);
+qr_out:
+ kmem_cache_destroy(lio_qr_cache);
+ts_out2:
+ iscsi_deallocate_thread_sets();
+ts_out1:
+ iscsi_thread_set_free();
+configfs_out:
+ iscsi_target_deregister_configfs();
+out:
+ kfree(iscsit_global);
+ return -ENOMEM;
+}
+
+static void __exit iscsi_target_cleanup_module(void)
+{
+ iscsi_deallocate_thread_sets();
+ iscsi_thread_set_free();
+ iscsit_release_discovery_tpg();
+ iscsit_unregister_transport(&iscsi_target_transport);
+ kmem_cache_destroy(lio_qr_cache);
+ kmem_cache_destroy(lio_dr_cache);
+ kmem_cache_destroy(lio_ooo_cache);
+ kmem_cache_destroy(lio_r2t_cache);
+
+ iscsi_target_deregister_configfs();
+
+ kfree(iscsit_global);
+}
+
+static int iscsit_add_reject(
+ struct iscsi_conn *conn,
+ u8 reason,
+ unsigned char *buf)
+{
+ struct iscsi_cmd *cmd;
+
+ cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
+ if (!cmd)
+ return -1;
+
+ cmd->iscsi_opcode = ISCSI_OP_REJECT;
+ cmd->reject_reason = reason;
+
+ cmd->buf_ptr = kmemdup(buf, ISCSI_HDR_LEN, GFP_KERNEL);
+ if (!cmd->buf_ptr) {
+ pr_err("Unable to allocate memory for cmd->buf_ptr\n");
+ iscsit_free_cmd(cmd, false);
+ return -1;
+ }
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ cmd->i_state = ISTATE_SEND_REJECT;
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+
+ return -1;
+}
+
+static int iscsit_add_reject_from_cmd(
+ struct iscsi_cmd *cmd,
+ u8 reason,
+ bool add_to_conn,
+ unsigned char *buf)
+{
+ struct iscsi_conn *conn;
+
+ if (!cmd->conn) {
+ pr_err("cmd->conn is NULL for ITT: 0x%08x\n",
+ cmd->init_task_tag);
+ return -1;
+ }
+ conn = cmd->conn;
+
+ cmd->iscsi_opcode = ISCSI_OP_REJECT;
+ cmd->reject_reason = reason;
+
+ cmd->buf_ptr = kmemdup(buf, ISCSI_HDR_LEN, GFP_KERNEL);
+ if (!cmd->buf_ptr) {
+ pr_err("Unable to allocate memory for cmd->buf_ptr\n");
+ iscsit_free_cmd(cmd, false);
+ return -1;
+ }
+
+ if (add_to_conn) {
+ spin_lock_bh(&conn->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
+ spin_unlock_bh(&conn->cmd_lock);
+ }
+
+ cmd->i_state = ISTATE_SEND_REJECT;
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+ /*
+ * Perform the kref_put now if se_cmd has already been setup by
+ * scsit_setup_scsi_cmd()
+ */
+ if (cmd->se_cmd.se_tfo != NULL) {
+ pr_debug("iscsi reject: calling target_put_sess_cmd >>>>>>\n");
+ target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
+ }
+ return -1;
+}
+
+static int iscsit_add_reject_cmd(struct iscsi_cmd *cmd, u8 reason,
+ unsigned char *buf)
+{
+ return iscsit_add_reject_from_cmd(cmd, reason, true, buf);
+}
+
+int iscsit_reject_cmd(struct iscsi_cmd *cmd, u8 reason, unsigned char *buf)
+{
+ return iscsit_add_reject_from_cmd(cmd, reason, false, buf);
+}
+
+/*
+ * Map some portion of the allocated scatterlist to an iovec, suitable for
+ * kernel sockets to copy data in/out.
+ */
+static int iscsit_map_iovec(
+ struct iscsi_cmd *cmd,
+ struct kvec *iov,
+ u32 data_offset,
+ u32 data_length)
+{
+ u32 i = 0;
+ struct scatterlist *sg;
+ unsigned int page_off;
+
+ /*
+ * We know each entry in t_data_sg contains a page.
+ */
+ sg = &cmd->se_cmd.t_data_sg[data_offset / PAGE_SIZE];
+ page_off = (data_offset % PAGE_SIZE);
+
+ cmd->first_data_sg = sg;
+ cmd->first_data_sg_off = page_off;
+
+ while (data_length) {
+ u32 cur_len = min_t(u32, data_length, sg->length - page_off);
+
+ iov[i].iov_base = kmap(sg_page(sg)) + sg->offset + page_off;
+ iov[i].iov_len = cur_len;
+
+ data_length -= cur_len;
+ page_off = 0;
+ sg = sg_next(sg);
+ i++;
+ }
+
+ cmd->kmapped_nents = i;
+
+ return i;
+}
+
+static void iscsit_unmap_iovec(struct iscsi_cmd *cmd)
+{
+ u32 i;
+ struct scatterlist *sg;
+
+ sg = cmd->first_data_sg;
+
+ for (i = 0; i < cmd->kmapped_nents; i++)
+ kunmap(sg_page(&sg[i]));
+}
+
+static void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn)
+{
+ LIST_HEAD(ack_list);
+ struct iscsi_cmd *cmd, *cmd_p;
+
+ conn->exp_statsn = exp_statsn;
+
+ if (conn->sess->sess_ops->RDMAExtensions)
+ return;
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_for_each_entry_safe(cmd, cmd_p, &conn->conn_cmd_list, i_conn_node) {
+ spin_lock(&cmd->istate_lock);
+ if ((cmd->i_state == ISTATE_SENT_STATUS) &&
+ iscsi_sna_lt(cmd->stat_sn, exp_statsn)) {
+ cmd->i_state = ISTATE_REMOVE;
+ spin_unlock(&cmd->istate_lock);
+ list_move_tail(&cmd->i_conn_node, &ack_list);
+ continue;
+ }
+ spin_unlock(&cmd->istate_lock);
+ }
+ spin_unlock_bh(&conn->cmd_lock);
+
+ list_for_each_entry_safe(cmd, cmd_p, &ack_list, i_conn_node) {
+ list_del_init(&cmd->i_conn_node);
+ iscsit_free_cmd(cmd, false);
+ }
+}
+
+static int iscsit_allocate_iovecs(struct iscsi_cmd *cmd)
+{
+ u32 iov_count = max(1UL, DIV_ROUND_UP(cmd->se_cmd.data_length, PAGE_SIZE));
+
+ iov_count += ISCSI_IOV_DATA_BUFFER;
+
+ cmd->iov_data = kzalloc(iov_count * sizeof(struct kvec), GFP_KERNEL);
+ if (!cmd->iov_data) {
+ pr_err("Unable to allocate cmd->iov_data\n");
+ return -ENOMEM;
+ }
+
+ cmd->orig_iov_data_count = iov_count;
+ return 0;
+}
+
+int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ int data_direction, payload_length;
+ struct iscsi_scsi_req *hdr;
+ int iscsi_task_attr;
+ int sam_task_attr;
+
+ atomic_long_inc(&conn->sess->cmd_pdus);
+
+ hdr = (struct iscsi_scsi_req *) buf;
+ payload_length = ntoh24(hdr->dlength);
+
+ /* FIXME; Add checks for AdditionalHeaderSegment */
+
+ if (!(hdr->flags & ISCSI_FLAG_CMD_WRITE) &&
+ !(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {
+ pr_err("ISCSI_FLAG_CMD_WRITE & ISCSI_FLAG_CMD_FINAL"
+ " not set. Bad iSCSI Initiator.\n");
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
+ }
+
+ if (((hdr->flags & ISCSI_FLAG_CMD_READ) ||
+ (hdr->flags & ISCSI_FLAG_CMD_WRITE)) && !hdr->data_length) {
+ /*
+ * From RFC-3720 Section 10.3.1:
+ *
+ * "Either or both of R and W MAY be 1 when either the
+ * Expected Data Transfer Length and/or Bidirectional Read
+ * Expected Data Transfer Length are 0"
+ *
+ * For this case, go ahead and clear the unnecssary bits
+ * to avoid any confusion with ->data_direction.
+ */
+ hdr->flags &= ~ISCSI_FLAG_CMD_READ;
+ hdr->flags &= ~ISCSI_FLAG_CMD_WRITE;
+
+ pr_warn("ISCSI_FLAG_CMD_READ or ISCSI_FLAG_CMD_WRITE"
+ " set when Expected Data Transfer Length is 0 for"
+ " CDB: 0x%02x, Fixing up flags\n", hdr->cdb[0]);
+ }
+
+ if (!(hdr->flags & ISCSI_FLAG_CMD_READ) &&
+ !(hdr->flags & ISCSI_FLAG_CMD_WRITE) && (hdr->data_length != 0)) {
+ pr_err("ISCSI_FLAG_CMD_READ and/or ISCSI_FLAG_CMD_WRITE"
+ " MUST be set if Expected Data Transfer Length is not 0."
+ " Bad iSCSI Initiator\n");
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
+ }
+
+ if ((hdr->flags & ISCSI_FLAG_CMD_READ) &&
+ (hdr->flags & ISCSI_FLAG_CMD_WRITE)) {
+ pr_err("Bidirectional operations not supported!\n");
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
+ }
+
+ if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
+ pr_err("Illegally set Immediate Bit in iSCSI Initiator"
+ " Scsi Command PDU.\n");
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
+ }
+
+ if (payload_length && !conn->sess->sess_ops->ImmediateData) {
+ pr_err("ImmediateData=No but DataSegmentLength=%u,"
+ " protocol error.\n", payload_length);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
+ }
+
+ if ((be32_to_cpu(hdr->data_length) == payload_length) &&
+ (!(hdr->flags & ISCSI_FLAG_CMD_FINAL))) {
+ pr_err("Expected Data Transfer Length and Length of"
+ " Immediate Data are the same, but ISCSI_FLAG_CMD_FINAL"
+ " bit is not set protocol error\n");
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
+ }
+
+ if (payload_length > be32_to_cpu(hdr->data_length)) {
+ pr_err("DataSegmentLength: %u is greater than"
+ " EDTL: %u, protocol error.\n", payload_length,
+ hdr->data_length);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
+ }
+
+ if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
+ pr_err("DataSegmentLength: %u is greater than"
+ " MaxXmitDataSegmentLength: %u, protocol error.\n",
+ payload_length, conn->conn_ops->MaxXmitDataSegmentLength);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
+ }
+
+ if (payload_length > conn->sess->sess_ops->FirstBurstLength) {
+ pr_err("DataSegmentLength: %u is greater than"
+ " FirstBurstLength: %u, protocol error.\n",
+ payload_length, conn->sess->sess_ops->FirstBurstLength);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
+ }
+
+ data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE :
+ (hdr->flags & ISCSI_FLAG_CMD_READ) ? DMA_FROM_DEVICE :
+ DMA_NONE;
+
+ cmd->data_direction = data_direction;
+ iscsi_task_attr = hdr->flags & ISCSI_FLAG_CMD_ATTR_MASK;
+ /*
+ * Figure out the SAM Task Attribute for the incoming SCSI CDB
+ */
+ if ((iscsi_task_attr == ISCSI_ATTR_UNTAGGED) ||
+ (iscsi_task_attr == ISCSI_ATTR_SIMPLE))
+ sam_task_attr = MSG_SIMPLE_TAG;
+ else if (iscsi_task_attr == ISCSI_ATTR_ORDERED)
+ sam_task_attr = MSG_ORDERED_TAG;
+ else if (iscsi_task_attr == ISCSI_ATTR_HEAD_OF_QUEUE)
+ sam_task_attr = MSG_HEAD_TAG;
+ else if (iscsi_task_attr == ISCSI_ATTR_ACA)
+ sam_task_attr = MSG_ACA_TAG;
+ else {
+ pr_debug("Unknown iSCSI Task Attribute: 0x%02x, using"
+ " MSG_SIMPLE_TAG\n", iscsi_task_attr);
+ sam_task_attr = MSG_SIMPLE_TAG;
+ }
+
+ cmd->iscsi_opcode = ISCSI_OP_SCSI_CMD;
+ cmd->i_state = ISTATE_NEW_CMD;
+ cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
+ cmd->immediate_data = (payload_length) ? 1 : 0;
+ cmd->unsolicited_data = ((!(hdr->flags & ISCSI_FLAG_CMD_FINAL) &&
+ (hdr->flags & ISCSI_FLAG_CMD_WRITE)) ? 1 : 0);
+ if (cmd->unsolicited_data)
+ cmd->cmd_flags |= ICF_NON_IMMEDIATE_UNSOLICITED_DATA;
+
+ conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
+ if (hdr->flags & ISCSI_FLAG_CMD_READ) {
+ spin_lock_bh(&conn->sess->ttt_lock);
+ cmd->targ_xfer_tag = conn->sess->targ_xfer_tag++;
+ if (cmd->targ_xfer_tag == 0xFFFFFFFF)
+ cmd->targ_xfer_tag = conn->sess->targ_xfer_tag++;
+ spin_unlock_bh(&conn->sess->ttt_lock);
+ } else if (hdr->flags & ISCSI_FLAG_CMD_WRITE)
+ cmd->targ_xfer_tag = 0xFFFFFFFF;
+ cmd->cmd_sn = be32_to_cpu(hdr->cmdsn);
+ cmd->exp_stat_sn = be32_to_cpu(hdr->exp_statsn);
+ cmd->first_burst_len = payload_length;
+
+ if (!conn->sess->sess_ops->RDMAExtensions &&
+ cmd->data_direction == DMA_FROM_DEVICE) {
+ struct iscsi_datain_req *dr;
+
+ dr = iscsit_allocate_datain_req();
+ if (!dr)
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
+
+ iscsit_attach_datain_req(cmd, dr);
+ }
+
+ /*
+ * Initialize struct se_cmd descriptor from target_core_mod infrastructure
+ */
+ transport_init_se_cmd(&cmd->se_cmd, &lio_target_fabric_configfs->tf_ops,
+ conn->sess->se_sess, be32_to_cpu(hdr->data_length),
+ cmd->data_direction, sam_task_attr,
+ cmd->sense_buffer + 2);
+
+ pr_debug("Got SCSI Command, ITT: 0x%08x, CmdSN: 0x%08x,"
+ " ExpXferLen: %u, Length: %u, CID: %hu\n", hdr->itt,
+ hdr->cmdsn, be32_to_cpu(hdr->data_length), payload_length,
+ conn->cid);
+
+ target_get_sess_cmd(conn->sess->se_sess, &cmd->se_cmd, true);
+
+ cmd->sense_reason = transport_lookup_cmd_lun(&cmd->se_cmd,
+ scsilun_to_int(&hdr->lun));
+ if (cmd->sense_reason)
+ goto attach_cmd;
+
+ cmd->sense_reason = target_setup_cmd_from_cdb(&cmd->se_cmd, hdr->cdb);
+ if (cmd->sense_reason) {
+ if (cmd->sense_reason == TCM_OUT_OF_RESOURCES) {
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
+ }
+
+ goto attach_cmd;
+ }
+
+ if (iscsit_build_pdu_and_seq_lists(cmd, payload_length) < 0) {
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
+ }
+
+attach_cmd:
+ spin_lock_bh(&conn->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
+ spin_unlock_bh(&conn->cmd_lock);
+ /*
+ * Check if we need to delay processing because of ALUA
+ * Active/NonOptimized primary access state..
+ */
+ core_alua_check_nonop_delay(&cmd->se_cmd);
+
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_setup_scsi_cmd);
+
+void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *cmd)
+{
+ iscsit_set_dataout_sequence_values(cmd);
+
+ spin_lock_bh(&cmd->dataout_timeout_lock);
+ iscsit_start_dataout_timer(cmd, cmd->conn);
+ spin_unlock_bh(&cmd->dataout_timeout_lock);
+}
+EXPORT_SYMBOL(iscsit_set_unsoliticed_dataout);
+
+int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ struct iscsi_scsi_req *hdr)
+{
+ int cmdsn_ret = 0;
+ /*
+ * Check the CmdSN against ExpCmdSN/MaxCmdSN here if
+ * the Immediate Bit is not set, and no Immediate
+ * Data is attached.
+ *
+ * A PDU/CmdSN carrying Immediate Data can only
+ * be processed after the DataCRC has passed.
+ * If the DataCRC fails, the CmdSN MUST NOT
+ * be acknowledged. (See below)
+ */
+ if (!cmd->immediate_data) {
+ cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
+ (unsigned char *)hdr, hdr->cmdsn);
+ if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+ return -1;
+ else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
+ target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
+ return 0;
+ }
+ }
+
+ iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
+
+ /*
+ * If no Immediate Data is attached, it's OK to return now.
+ */
+ if (!cmd->immediate_data) {
+ if (!cmd->sense_reason && cmd->unsolicited_data)
+ iscsit_set_unsoliticed_dataout(cmd);
+ if (!cmd->sense_reason)
+ return 0;
+
+ target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
+ return 0;
+ }
+
+ /*
+ * Early CHECK_CONDITIONs with ImmediateData never make it to command
+ * execution. These exceptions are processed in CmdSN order using
+ * iscsit_check_received_cmdsn() in iscsit_get_immediate_data() below.
+ */
+ if (cmd->sense_reason) {
+ if (cmd->reject_reason)
+ return 0;
+
+ return 1;
+ }
+ /*
+ * Call directly into transport_generic_new_cmd() to perform
+ * the backend memory allocation.
+ */
+ cmd->sense_reason = transport_generic_new_cmd(&cmd->se_cmd);
+ if (cmd->sense_reason)
+ return 1;
+
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_process_scsi_cmd);
+
+static int
+iscsit_get_immediate_data(struct iscsi_cmd *cmd, struct iscsi_scsi_req *hdr,
+ bool dump_payload)
+{
+ struct iscsi_conn *conn = cmd->conn;
+ int cmdsn_ret = 0, immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION;
+ /*
+ * Special case for Unsupported SAM WRITE Opcodes and ImmediateData=Yes.
+ */
+ if (dump_payload)
+ goto after_immediate_data;
+
+ immed_ret = iscsit_handle_immediate_data(cmd, hdr,
+ cmd->first_burst_len);
+after_immediate_data:
+ if (immed_ret == IMMEDIATE_DATA_NORMAL_OPERATION) {
+ /*
+ * A PDU/CmdSN carrying Immediate Data passed
+ * DataCRC, check against ExpCmdSN/MaxCmdSN if
+ * Immediate Bit is not set.
+ */
+ cmdsn_ret = iscsit_sequence_cmd(cmd->conn, cmd,
+ (unsigned char *)hdr, hdr->cmdsn);
+ if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+ return -1;
+
+ if (cmd->sense_reason || cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
+ int rc;
+
+ rc = iscsit_dump_data_payload(cmd->conn,
+ cmd->first_burst_len, 1);
+ target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
+ return rc;
+ } else if (cmd->unsolicited_data)
+ iscsit_set_unsoliticed_dataout(cmd);
+
+ } else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) {
+ /*
+ * Immediate Data failed DataCRC and ERL>=1,
+ * silently drop this PDU and let the initiator
+ * plug the CmdSN gap.
+ *
+ * FIXME: Send Unsolicited NOPIN with reserved
+ * TTT here to help the initiator figure out
+ * the missing CmdSN, although they should be
+ * intelligent enough to determine the missing
+ * CmdSN and issue a retry to plug the sequence.
+ */
+ cmd->i_state = ISTATE_REMOVE;
+ iscsit_add_cmd_to_immediate_queue(cmd, cmd->conn, cmd->i_state);
+ } else /* immed_ret == IMMEDIATE_DATA_CANNOT_RECOVER */
+ return -1;
+
+ return 0;
+}
+
+static int
+iscsit_handle_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)buf;
+ int rc, immed_data;
+ bool dump_payload = false;
+
+ rc = iscsit_setup_scsi_cmd(conn, cmd, buf);
+ if (rc < 0)
+ return 0;
+ /*
+ * Allocation iovecs needed for struct socket operations for
+ * traditional iSCSI block I/O.
+ */
+ if (iscsit_allocate_iovecs(cmd) < 0) {
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
+ }
+ immed_data = cmd->immediate_data;
+
+ rc = iscsit_process_scsi_cmd(conn, cmd, hdr);
+ if (rc < 0)
+ return rc;
+ else if (rc > 0)
+ dump_payload = true;
+
+ if (!immed_data)
+ return 0;
+
+ return iscsit_get_immediate_data(cmd, hdr, dump_payload);
+}
+
+static u32 iscsit_do_crypto_hash_sg(
+ struct hash_desc *hash,
+ struct iscsi_cmd *cmd,
+ u32 data_offset,
+ u32 data_length,
+ u32 padding,
+ u8 *pad_bytes)
+{
+ u32 data_crc;
+ u32 i;
+ struct scatterlist *sg;
+ unsigned int page_off;
+
+ crypto_hash_init(hash);
+
+ sg = cmd->first_data_sg;
+ page_off = cmd->first_data_sg_off;
+
+ i = 0;
+ while (data_length) {
+ u32 cur_len = min_t(u32, data_length, (sg[i].length - page_off));
+
+ crypto_hash_update(hash, &sg[i], cur_len);
+
+ data_length -= cur_len;
+ page_off = 0;
+ i++;
+ }
+
+ if (padding) {
+ struct scatterlist pad_sg;
+
+ sg_init_one(&pad_sg, pad_bytes, padding);
+ crypto_hash_update(hash, &pad_sg, padding);
+ }
+ crypto_hash_final(hash, (u8 *) &data_crc);
+
+ return data_crc;
+}
+
+static void iscsit_do_crypto_hash_buf(
+ struct hash_desc *hash,
+ const void *buf,
+ u32 payload_length,
+ u32 padding,
+ u8 *pad_bytes,
+ u8 *data_crc)
+{
+ struct scatterlist sg;
+
+ crypto_hash_init(hash);
+
+ sg_init_one(&sg, buf, payload_length);
+ crypto_hash_update(hash, &sg, payload_length);
+
+ if (padding) {
+ sg_init_one(&sg, pad_bytes, padding);
+ crypto_hash_update(hash, &sg, padding);
+ }
+ crypto_hash_final(hash, data_crc);
+}
+
+int
+iscsit_check_dataout_hdr(struct iscsi_conn *conn, unsigned char *buf,
+ struct iscsi_cmd **out_cmd)
+{
+ struct iscsi_data *hdr = (struct iscsi_data *)buf;
+ struct iscsi_cmd *cmd = NULL;
+ struct se_cmd *se_cmd;
+ u32 payload_length = ntoh24(hdr->dlength);
+ int rc;
+
+ if (!payload_length) {
+ pr_warn("DataOUT payload is ZERO, ignoring.\n");
+ return 0;
+ }
+
+ /* iSCSI write */
+ atomic_long_add(payload_length, &conn->sess->rx_data_octets);
+
+ if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
+ pr_err("DataSegmentLength: %u is greater than"
+ " MaxXmitDataSegmentLength: %u\n", payload_length,
+ conn->conn_ops->MaxXmitDataSegmentLength);
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ buf);
+ }
+
+ cmd = iscsit_find_cmd_from_itt_or_dump(conn, hdr->itt,
+ payload_length);
+ if (!cmd)
+ return 0;
+
+ pr_debug("Got DataOut ITT: 0x%08x, TTT: 0x%08x,"
+ " DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n",
+ hdr->itt, hdr->ttt, hdr->datasn, ntohl(hdr->offset),
+ payload_length, conn->cid);
+
+ if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) {
+ pr_err("Command ITT: 0x%08x received DataOUT after"
+ " last DataOUT received, dumping payload\n",
+ cmd->init_task_tag);
+ return iscsit_dump_data_payload(conn, payload_length, 1);
+ }
+
+ if (cmd->data_direction != DMA_TO_DEVICE) {
+ pr_err("Command ITT: 0x%08x received DataOUT for a"
+ " NON-WRITE command.\n", cmd->init_task_tag);
+ return iscsit_dump_data_payload(conn, payload_length, 1);
+ }
+ se_cmd = &cmd->se_cmd;
+ iscsit_mod_dataout_timer(cmd);
+
+ if ((be32_to_cpu(hdr->offset) + payload_length) > cmd->se_cmd.data_length) {
+ pr_err("DataOut Offset: %u, Length %u greater than"
+ " iSCSI Command EDTL %u, protocol error.\n",
+ hdr->offset, payload_length, cmd->se_cmd.data_length);
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_INVALID, buf);
+ }
+
+ if (cmd->unsolicited_data) {
+ int dump_unsolicited_data = 0;
+
+ if (conn->sess->sess_ops->InitialR2T) {
+ pr_err("Received unexpected unsolicited data"
+ " while InitialR2T=Yes, protocol error.\n");
+ transport_send_check_condition_and_sense(&cmd->se_cmd,
+ TCM_UNEXPECTED_UNSOLICITED_DATA, 0);
+ return -1;
+ }
+ /*
+ * Special case for dealing with Unsolicited DataOUT
+ * and Unsupported SAM WRITE Opcodes and SE resource allocation
+ * failures;
+ */
+
+ /* Something's amiss if we're not in WRITE_PENDING state... */
+ WARN_ON(se_cmd->t_state != TRANSPORT_WRITE_PENDING);
+ if (!(se_cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE))
+ dump_unsolicited_data = 1;
+
+ if (dump_unsolicited_data) {
+ /*
+ * Check if a delayed TASK_ABORTED status needs to
+ * be sent now if the ISCSI_FLAG_CMD_FINAL has been
+ * received with the unsolicitied data out.
+ */
+ if (hdr->flags & ISCSI_FLAG_CMD_FINAL)
+ iscsit_stop_dataout_timer(cmd);
+
+ transport_check_aborted_status(se_cmd,
+ (hdr->flags & ISCSI_FLAG_CMD_FINAL));
+ return iscsit_dump_data_payload(conn, payload_length, 1);
+ }
+ } else {
+ /*
+ * For the normal solicited data path:
+ *
+ * Check for a delayed TASK_ABORTED status and dump any
+ * incoming data out payload if one exists. Also, when the
+ * ISCSI_FLAG_CMD_FINAL is set to denote the end of the current
+ * data out sequence, we decrement outstanding_r2ts. Once
+ * outstanding_r2ts reaches zero, go ahead and send the delayed
+ * TASK_ABORTED status.
+ */
+ if (se_cmd->transport_state & CMD_T_ABORTED) {
+ if (hdr->flags & ISCSI_FLAG_CMD_FINAL)
+ if (--cmd->outstanding_r2ts < 1) {
+ iscsit_stop_dataout_timer(cmd);
+ transport_check_aborted_status(
+ se_cmd, 1);
+ }
+
+ return iscsit_dump_data_payload(conn, payload_length, 1);
+ }
+ }
+ /*
+ * Preform DataSN, DataSequenceInOrder, DataPDUInOrder, and
+ * within-command recovery checks before receiving the payload.
+ */
+ rc = iscsit_check_pre_dataout(cmd, buf);
+ if (rc == DATAOUT_WITHIN_COMMAND_RECOVERY)
+ return 0;
+ else if (rc == DATAOUT_CANNOT_RECOVER)
+ return -1;
+
+ *out_cmd = cmd;
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_check_dataout_hdr);
+
+static int
+iscsit_get_dataout(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ struct iscsi_data *hdr)
+{
+ struct kvec *iov;
+ u32 checksum, iov_count = 0, padding = 0, rx_got = 0, rx_size = 0;
+ u32 payload_length = ntoh24(hdr->dlength);
+ int iov_ret, data_crc_failed = 0;
+
+ rx_size += payload_length;
+ iov = &cmd->iov_data[0];
+
+ iov_ret = iscsit_map_iovec(cmd, iov, be32_to_cpu(hdr->offset),
+ payload_length);
+ if (iov_ret < 0)
+ return -1;
+
+ iov_count += iov_ret;
+
+ padding = ((-payload_length) & 3);
+ if (padding != 0) {
+ iov[iov_count].iov_base = cmd->pad_bytes;
+ iov[iov_count++].iov_len = padding;
+ rx_size += padding;
+ pr_debug("Receiving %u padding bytes.\n", padding);
+ }
+
+ if (conn->conn_ops->DataDigest) {
+ iov[iov_count].iov_base = &checksum;
+ iov[iov_count++].iov_len = ISCSI_CRC_LEN;
+ rx_size += ISCSI_CRC_LEN;
+ }
+
+ rx_got = rx_data(conn, &cmd->iov_data[0], iov_count, rx_size);
+
+ iscsit_unmap_iovec(cmd);
+
+ if (rx_got != rx_size)
+ return -1;
+
+ if (conn->conn_ops->DataDigest) {
+ u32 data_crc;
+
+ data_crc = iscsit_do_crypto_hash_sg(&conn->conn_rx_hash, cmd,
+ be32_to_cpu(hdr->offset),
+ payload_length, padding,
+ cmd->pad_bytes);
+
+ if (checksum != data_crc) {
+ pr_err("ITT: 0x%08x, Offset: %u, Length: %u,"
+ " DataSN: 0x%08x, CRC32C DataDigest 0x%08x"
+ " does not match computed 0x%08x\n",
+ hdr->itt, hdr->offset, payload_length,
+ hdr->datasn, checksum, data_crc);
+ data_crc_failed = 1;
+ } else {
+ pr_debug("Got CRC32C DataDigest 0x%08x for"
+ " %u bytes of Data Out\n", checksum,
+ payload_length);
+ }
+ }
+
+ return data_crc_failed;
+}
+
+int
+iscsit_check_dataout_payload(struct iscsi_cmd *cmd, struct iscsi_data *hdr,
+ bool data_crc_failed)
+{
+ struct iscsi_conn *conn = cmd->conn;
+ int rc, ooo_cmdsn;
+ /*
+ * Increment post receive data and CRC values or perform
+ * within-command recovery.
+ */
+ rc = iscsit_check_post_dataout(cmd, (unsigned char *)hdr, data_crc_failed);
+ if ((rc == DATAOUT_NORMAL) || (rc == DATAOUT_WITHIN_COMMAND_RECOVERY))
+ return 0;
+ else if (rc == DATAOUT_SEND_R2T) {
+ iscsit_set_dataout_sequence_values(cmd);
+ conn->conn_transport->iscsit_get_dataout(conn, cmd, false);
+ } else if (rc == DATAOUT_SEND_TO_TRANSPORT) {
+ /*
+ * Handle extra special case for out of order
+ * Unsolicited Data Out.
+ */
+ spin_lock_bh(&cmd->istate_lock);
+ ooo_cmdsn = (cmd->cmd_flags & ICF_OOO_CMDSN);
+ cmd->cmd_flags |= ICF_GOT_LAST_DATAOUT;
+ cmd->i_state = ISTATE_RECEIVED_LAST_DATAOUT;
+ spin_unlock_bh(&cmd->istate_lock);
+
+ iscsit_stop_dataout_timer(cmd);
+ if (ooo_cmdsn)
+ return 0;
+ target_execute_cmd(&cmd->se_cmd);
+ return 0;
+ } else /* DATAOUT_CANNOT_RECOVER */
+ return -1;
+
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_check_dataout_payload);
+
+static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf)
+{
+ struct iscsi_cmd *cmd = NULL;
+ struct iscsi_data *hdr = (struct iscsi_data *)buf;
+ int rc;
+ bool data_crc_failed = false;
+
+ rc = iscsit_check_dataout_hdr(conn, buf, &cmd);
+ if (rc < 0)
+ return 0;
+ else if (!cmd)
+ return 0;
+
+ rc = iscsit_get_dataout(conn, cmd, hdr);
+ if (rc < 0)
+ return rc;
+ else if (rc > 0)
+ data_crc_failed = true;
+
+ return iscsit_check_dataout_payload(cmd, hdr, data_crc_failed);
+}
+
+int iscsit_setup_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ struct iscsi_nopout *hdr)
+{
+ u32 payload_length = ntoh24(hdr->dlength);
+
+ if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {
+ pr_err("NopOUT Flag's, Left Most Bit not set, protocol error.\n");
+ if (!cmd)
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
+
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
+ }
+
+ if (hdr->itt == RESERVED_ITT && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+ pr_err("NOPOUT ITT is reserved, but Immediate Bit is"
+ " not set, protocol error.\n");
+ if (!cmd)
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
+
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
+ }
+
+ if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
+ pr_err("NOPOUT Ping Data DataSegmentLength: %u is"
+ " greater than MaxXmitDataSegmentLength: %u, protocol"
+ " error.\n", payload_length,
+ conn->conn_ops->MaxXmitDataSegmentLength);
+ if (!cmd)
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
+
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
+ }
+
+ pr_debug("Got NOPOUT Ping %s ITT: 0x%08x, TTT: 0x%08x,"
+ " CmdSN: 0x%08x, ExpStatSN: 0x%08x, Length: %u\n",
+ hdr->itt == RESERVED_ITT ? "Response" : "Request",
+ hdr->itt, hdr->ttt, hdr->cmdsn, hdr->exp_statsn,
+ payload_length);
+ /*
+ * This is not a response to a Unsolicited NopIN, which means
+ * it can either be a NOPOUT ping request (with a valid ITT),
+ * or a NOPOUT not requesting a NOPIN (with a reserved ITT).
+ * Either way, make sure we allocate an struct iscsi_cmd, as both
+ * can contain ping data.
+ */
+ if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
+ cmd->iscsi_opcode = ISCSI_OP_NOOP_OUT;
+ cmd->i_state = ISTATE_SEND_NOPIN;
+ cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ?
+ 1 : 0);
+ conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
+ cmd->targ_xfer_tag = 0xFFFFFFFF;
+ cmd->cmd_sn = be32_to_cpu(hdr->cmdsn);
+ cmd->exp_stat_sn = be32_to_cpu(hdr->exp_statsn);
+ cmd->data_direction = DMA_NONE;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_setup_nop_out);
+
+int iscsit_process_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ struct iscsi_nopout *hdr)
+{
+ struct iscsi_cmd *cmd_p = NULL;
+ int cmdsn_ret = 0;
+ /*
+ * Initiator is expecting a NopIN ping reply..
+ */
+ if (hdr->itt != RESERVED_ITT) {
+ if (!cmd)
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
+
+ if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
+ iscsit_add_cmd_to_response_queue(cmd, conn,
+ cmd->i_state);
+ return 0;
+ }
+
+ cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
+ (unsigned char *)hdr, hdr->cmdsn);
+ if (cmdsn_ret == CMDSN_LOWER_THAN_EXP)
+ return 0;
+ if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+ return -1;
+
+ return 0;
+ }
+ /*
+ * This was a response to a unsolicited NOPIN ping.
+ */
+ if (hdr->ttt != cpu_to_be32(0xFFFFFFFF)) {
+ cmd_p = iscsit_find_cmd_from_ttt(conn, be32_to_cpu(hdr->ttt));
+ if (!cmd_p)
+ return -EINVAL;
+
+ iscsit_stop_nopin_response_timer(conn);
+
+ cmd_p->i_state = ISTATE_REMOVE;
+ iscsit_add_cmd_to_immediate_queue(cmd_p, conn, cmd_p->i_state);
+
+ iscsit_start_nopin_timer(conn);
+ return 0;
+ }
+ /*
+ * Otherwise, initiator is not expecting a NOPIN is response.
+ * Just ignore for now.
+ */
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_process_nop_out);
+
+static int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ unsigned char *ping_data = NULL;
+ struct iscsi_nopout *hdr = (struct iscsi_nopout *)buf;
+ struct kvec *iov = NULL;
+ u32 payload_length = ntoh24(hdr->dlength);
+ int ret;
+
+ ret = iscsit_setup_nop_out(conn, cmd, hdr);
+ if (ret < 0)
+ return 0;
+ /*
+ * Handle NOP-OUT payload for traditional iSCSI sockets
+ */
+ if (payload_length && hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
+ u32 checksum, data_crc, padding = 0;
+ int niov = 0, rx_got, rx_size = payload_length;
+
+ ping_data = kzalloc(payload_length + 1, GFP_KERNEL);
+ if (!ping_data) {
+ pr_err("Unable to allocate memory for"
+ " NOPOUT ping data.\n");
+ ret = -1;
+ goto out;
+ }
+
+ iov = &cmd->iov_misc[0];
+ iov[niov].iov_base = ping_data;
+ iov[niov++].iov_len = payload_length;
+
+ padding = ((-payload_length) & 3);
+ if (padding != 0) {
+ pr_debug("Receiving %u additional bytes"
+ " for padding.\n", padding);
+ iov[niov].iov_base = &cmd->pad_bytes;
+ iov[niov++].iov_len = padding;
+ rx_size += padding;
+ }
+ if (conn->conn_ops->DataDigest) {
+ iov[niov].iov_base = &checksum;
+ iov[niov++].iov_len = ISCSI_CRC_LEN;
+ rx_size += ISCSI_CRC_LEN;
+ }
+
+ rx_got = rx_data(conn, &cmd->iov_misc[0], niov, rx_size);
+ if (rx_got != rx_size) {
+ ret = -1;
+ goto out;
+ }
+
+ if (conn->conn_ops->DataDigest) {
+ iscsit_do_crypto_hash_buf(&conn->conn_rx_hash,
+ ping_data, payload_length,
+ padding, cmd->pad_bytes,
+ (u8 *)&data_crc);
+
+ if (checksum != data_crc) {
+ pr_err("Ping data CRC32C DataDigest"
+ " 0x%08x does not match computed 0x%08x\n",
+ checksum, data_crc);
+ if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+ pr_err("Unable to recover from"
+ " NOPOUT Ping DataCRC failure while in"
+ " ERL=0.\n");
+ ret = -1;
+ goto out;
+ } else {
+ /*
+ * Silently drop this PDU and let the
+ * initiator plug the CmdSN gap.
+ */
+ pr_debug("Dropping NOPOUT"
+ " Command CmdSN: 0x%08x due to"
+ " DataCRC error.\n", hdr->cmdsn);
+ ret = 0;
+ goto out;
+ }
+ } else {
+ pr_debug("Got CRC32C DataDigest"
+ " 0x%08x for %u bytes of ping data.\n",
+ checksum, payload_length);
+ }
+ }
+
+ ping_data[payload_length] = '\0';
+ /*
+ * Attach ping data to struct iscsi_cmd->buf_ptr.
+ */
+ cmd->buf_ptr = ping_data;
+ cmd->buf_ptr_size = payload_length;
+
+ pr_debug("Got %u bytes of NOPOUT ping"
+ " data.\n", payload_length);
+ pr_debug("Ping Data: \"%s\"\n", ping_data);
+ }
+
+ return iscsit_process_nop_out(conn, cmd, hdr);
+out:
+ if (cmd)
+ iscsit_free_cmd(cmd, false);
+
+ kfree(ping_data);
+ return ret;
+}
+
+int
+iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ struct se_tmr_req *se_tmr;
+ struct iscsi_tmr_req *tmr_req;
+ struct iscsi_tm *hdr;
+ int out_of_order_cmdsn = 0, ret;
+ bool sess_ref = false;
+ u8 function;
+
+ hdr = (struct iscsi_tm *) buf;
+ hdr->flags &= ~ISCSI_FLAG_CMD_FINAL;
+ function = hdr->flags;
+
+ pr_debug("Got Task Management Request ITT: 0x%08x, CmdSN:"
+ " 0x%08x, Function: 0x%02x, RefTaskTag: 0x%08x, RefCmdSN:"
+ " 0x%08x, CID: %hu\n", hdr->itt, hdr->cmdsn, function,
+ hdr->rtt, hdr->refcmdsn, conn->cid);
+
+ if ((function != ISCSI_TM_FUNC_ABORT_TASK) &&
+ ((function != ISCSI_TM_FUNC_TASK_REASSIGN) &&
+ hdr->rtt != RESERVED_ITT)) {
+ pr_err("RefTaskTag should be set to 0xFFFFFFFF.\n");
+ hdr->rtt = RESERVED_ITT;
+ }
+
+ if ((function == ISCSI_TM_FUNC_TASK_REASSIGN) &&
+ !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+ pr_err("Task Management Request TASK_REASSIGN not"
+ " issued as immediate command, bad iSCSI Initiator"
+ "implementation\n");
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
+ }
+ if ((function != ISCSI_TM_FUNC_ABORT_TASK) &&
+ be32_to_cpu(hdr->refcmdsn) != ISCSI_RESERVED_TAG)
+ hdr->refcmdsn = cpu_to_be32(ISCSI_RESERVED_TAG);
+
+ cmd->data_direction = DMA_NONE;
+
+ cmd->tmr_req = kzalloc(sizeof(struct iscsi_tmr_req), GFP_KERNEL);
+ if (!cmd->tmr_req) {
+ pr_err("Unable to allocate memory for"
+ " Task Management command!\n");
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+ buf);
+ }
+
+ /*
+ * TASK_REASSIGN for ERL=2 / connection stays inside of
+ * LIO-Target $FABRIC_MOD
+ */
+ if (function != ISCSI_TM_FUNC_TASK_REASSIGN) {
+
+ u8 tcm_function;
+ int ret;
+
+ transport_init_se_cmd(&cmd->se_cmd,
+ &lio_target_fabric_configfs->tf_ops,
+ conn->sess->se_sess, 0, DMA_NONE,
+ MSG_SIMPLE_TAG, cmd->sense_buffer + 2);
+
+ target_get_sess_cmd(conn->sess->se_sess, &cmd->se_cmd, true);
+ sess_ref = true;
+
+ switch (function) {
+ case ISCSI_TM_FUNC_ABORT_TASK:
+ tcm_function = TMR_ABORT_TASK;
+ break;
+ case ISCSI_TM_FUNC_ABORT_TASK_SET:
+ tcm_function = TMR_ABORT_TASK_SET;
+ break;
+ case ISCSI_TM_FUNC_CLEAR_ACA:
+ tcm_function = TMR_CLEAR_ACA;
+ break;
+ case ISCSI_TM_FUNC_CLEAR_TASK_SET:
+ tcm_function = TMR_CLEAR_TASK_SET;
+ break;
+ case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
+ tcm_function = TMR_LUN_RESET;
+ break;
+ case ISCSI_TM_FUNC_TARGET_WARM_RESET:
+ tcm_function = TMR_TARGET_WARM_RESET;
+ break;
+ case ISCSI_TM_FUNC_TARGET_COLD_RESET:
+ tcm_function = TMR_TARGET_COLD_RESET;
+ break;
+ default:
+ pr_err("Unknown iSCSI TMR Function:"
+ " 0x%02x\n", function);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
+ }
+
+ ret = core_tmr_alloc_req(&cmd->se_cmd, cmd->tmr_req,
+ tcm_function, GFP_KERNEL);
+ if (ret < 0)
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
+
+ cmd->tmr_req->se_tmr_req = cmd->se_cmd.se_tmr_req;
+ }
+
+ cmd->iscsi_opcode = ISCSI_OP_SCSI_TMFUNC;
+ cmd->i_state = ISTATE_SEND_TASKMGTRSP;
+ cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
+ cmd->init_task_tag = hdr->itt;
+ cmd->targ_xfer_tag = 0xFFFFFFFF;
+ cmd->cmd_sn = be32_to_cpu(hdr->cmdsn);
+ cmd->exp_stat_sn = be32_to_cpu(hdr->exp_statsn);
+ se_tmr = cmd->se_cmd.se_tmr_req;
+ tmr_req = cmd->tmr_req;
+ /*
+ * Locate the struct se_lun for all TMRs not related to ERL=2 TASK_REASSIGN
+ */
+ if (function != ISCSI_TM_FUNC_TASK_REASSIGN) {
+ ret = transport_lookup_tmr_lun(&cmd->se_cmd,
+ scsilun_to_int(&hdr->lun));
+ if (ret < 0) {
+ se_tmr->response = ISCSI_TMF_RSP_NO_LUN;
+ goto attach;
+ }
+ }
+
+ switch (function) {
+ case ISCSI_TM_FUNC_ABORT_TASK:
+ se_tmr->response = iscsit_tmr_abort_task(cmd, buf);
+ if (se_tmr->response)
+ goto attach;
+ break;
+ case ISCSI_TM_FUNC_ABORT_TASK_SET:
+ case ISCSI_TM_FUNC_CLEAR_ACA:
+ case ISCSI_TM_FUNC_CLEAR_TASK_SET:
+ case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
+ break;
+ case ISCSI_TM_FUNC_TARGET_WARM_RESET:
+ if (iscsit_tmr_task_warm_reset(conn, tmr_req, buf) < 0) {
+ se_tmr->response = ISCSI_TMF_RSP_AUTH_FAILED;
+ goto attach;
+ }
+ break;
+ case ISCSI_TM_FUNC_TARGET_COLD_RESET:
+ if (iscsit_tmr_task_cold_reset(conn, tmr_req, buf) < 0) {
+ se_tmr->response = ISCSI_TMF_RSP_AUTH_FAILED;
+ goto attach;
+ }
+ break;
+ case ISCSI_TM_FUNC_TASK_REASSIGN:
+ se_tmr->response = iscsit_tmr_task_reassign(cmd, buf);
+ /*
+ * Perform sanity checks on the ExpDataSN only if the
+ * TASK_REASSIGN was successful.
+ */
+ if (se_tmr->response)
+ break;
+
+ if (iscsit_check_task_reassign_expdatasn(tmr_req, conn) < 0)
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
+ break;
+ default:
+ pr_err("Unknown TMR function: 0x%02x, protocol"
+ " error.\n", function);
+ se_tmr->response = ISCSI_TMF_RSP_NOT_SUPPORTED;
+ goto attach;
+ }
+
+ if ((function != ISCSI_TM_FUNC_TASK_REASSIGN) &&
+ (se_tmr->response == ISCSI_TMF_RSP_COMPLETE))
+ se_tmr->call_transport = 1;
+attach:
+ spin_lock_bh(&conn->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+ int cmdsn_ret = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn);
+ if (cmdsn_ret == CMDSN_HIGHER_THAN_EXP)
+ out_of_order_cmdsn = 1;
+ else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP)
+ return 0;
+ else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+ return -1;
+ }
+ iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
+
+ if (out_of_order_cmdsn || !(hdr->opcode & ISCSI_OP_IMMEDIATE))
+ return 0;
+ /*
+ * Found the referenced task, send to transport for processing.
+ */
+ if (se_tmr->call_transport)
+ return transport_generic_handle_tmr(&cmd->se_cmd);
+
+ /*
+ * Could not find the referenced LUN, task, or Task Management
+ * command not authorized or supported. Change state and
+ * let the tx_thread send the response.
+ *
+ * For connection recovery, this is also the default action for
+ * TMR TASK_REASSIGN.
+ */
+ if (sess_ref) {
+ pr_debug("Handle TMR, using sess_ref=true check\n");
+ target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
+ }
+
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_handle_task_mgt_cmd);
+
+/* #warning FIXME: Support Text Command parameters besides SendTargets */
+int
+iscsit_setup_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ struct iscsi_text *hdr)
+{
+ u32 payload_length = ntoh24(hdr->dlength);
+
+ if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
+ pr_err("Unable to accept text parameter length: %u"
+ "greater than MaxXmitDataSegmentLength %u.\n",
+ payload_length, conn->conn_ops->MaxXmitDataSegmentLength);
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
+ }
+
+ if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL) ||
+ (hdr->flags & ISCSI_FLAG_TEXT_CONTINUE)) {
+ pr_err("Multi sequence text commands currently not supported\n");
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_CMD_NOT_SUPPORTED,
+ (unsigned char *)hdr);
+ }
+
+ pr_debug("Got Text Request: ITT: 0x%08x, CmdSN: 0x%08x,"
+ " ExpStatSN: 0x%08x, Length: %u\n", hdr->itt, hdr->cmdsn,
+ hdr->exp_statsn, payload_length);
+
+ cmd->iscsi_opcode = ISCSI_OP_TEXT;
+ cmd->i_state = ISTATE_SEND_TEXTRSP;
+ cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
+ conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
+ cmd->targ_xfer_tag = 0xFFFFFFFF;
+ cmd->cmd_sn = be32_to_cpu(hdr->cmdsn);
+ cmd->exp_stat_sn = be32_to_cpu(hdr->exp_statsn);
+ cmd->data_direction = DMA_NONE;
+
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_setup_text_cmd);
+
+int
+iscsit_process_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ struct iscsi_text *hdr)
+{
+ unsigned char *text_in = cmd->text_in_ptr, *text_ptr;
+ int cmdsn_ret;
+
+ if (!text_in) {
+ pr_err("Unable to locate text_in buffer for sendtargets"
+ " discovery\n");
+ goto reject;
+ }
+ if (strncmp("SendTargets", text_in, 11) != 0) {
+ pr_err("Received Text Data that is not"
+ " SendTargets, cannot continue.\n");
+ goto reject;
+ }
+ text_ptr = strchr(text_in, '=');
+ if (!text_ptr) {
+ pr_err("No \"=\" separator found in Text Data,"
+ " cannot continue.\n");
+ goto reject;
+ }
+ if (!strncmp("=All", text_ptr, 4)) {
+ cmd->cmd_flags |= IFC_SENDTARGETS_ALL;
+ } else if (!strncmp("=iqn.", text_ptr, 5) ||
+ !strncmp("=eui.", text_ptr, 5)) {
+ cmd->cmd_flags |= IFC_SENDTARGETS_SINGLE;
+ } else {
+ pr_err("Unable to locate valid SendTargets=%s value\n", text_ptr);
+ goto reject;
+ }
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
+
+ if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+ cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
+ (unsigned char *)hdr, hdr->cmdsn);
+ if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+ return -1;
+
+ return 0;
+ }
+
+ return iscsit_execute_cmd(cmd, 0);
+
+reject:
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
+}
+EXPORT_SYMBOL(iscsit_process_text_cmd);
+
+static int
+iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ struct iscsi_text *hdr = (struct iscsi_text *)buf;
+ char *text_in = NULL;
+ u32 payload_length = ntoh24(hdr->dlength);
+ int rx_size, rc;
+
+ rc = iscsit_setup_text_cmd(conn, cmd, hdr);
+ if (rc < 0)
+ return 0;
+
+ rx_size = payload_length;
+ if (payload_length) {
+ u32 checksum = 0, data_crc = 0;
+ u32 padding = 0, pad_bytes = 0;
+ int niov = 0, rx_got;
+ struct kvec iov[3];
+
+ text_in = kzalloc(payload_length, GFP_KERNEL);
+ if (!text_in) {
+ pr_err("Unable to allocate memory for"
+ " incoming text parameters\n");
+ goto reject;
+ }
+ cmd->text_in_ptr = text_in;
+
+ memset(iov, 0, 3 * sizeof(struct kvec));
+ iov[niov].iov_base = text_in;
+ iov[niov++].iov_len = payload_length;
+
+ padding = ((-payload_length) & 3);
+ if (padding != 0) {
+ iov[niov].iov_base = &pad_bytes;
+ iov[niov++].iov_len = padding;
+ rx_size += padding;
+ pr_debug("Receiving %u additional bytes"
+ " for padding.\n", padding);
+ }
+ if (conn->conn_ops->DataDigest) {
+ iov[niov].iov_base = &checksum;
+ iov[niov++].iov_len = ISCSI_CRC_LEN;
+ rx_size += ISCSI_CRC_LEN;
+ }
+
+ rx_got = rx_data(conn, &iov[0], niov, rx_size);
+ if (rx_got != rx_size)
+ goto reject;
+
+ if (conn->conn_ops->DataDigest) {
+ iscsit_do_crypto_hash_buf(&conn->conn_rx_hash,
+ text_in, payload_length,
+ padding, (u8 *)&pad_bytes,
+ (u8 *)&data_crc);
+
+ if (checksum != data_crc) {
+ pr_err("Text data CRC32C DataDigest"
+ " 0x%08x does not match computed"
+ " 0x%08x\n", checksum, data_crc);
+ if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+ pr_err("Unable to recover from"
+ " Text Data digest failure while in"
+ " ERL=0.\n");
+ goto reject;
+ } else {
+ /*
+ * Silently drop this PDU and let the
+ * initiator plug the CmdSN gap.
+ */
+ pr_debug("Dropping Text"
+ " Command CmdSN: 0x%08x due to"
+ " DataCRC error.\n", hdr->cmdsn);
+ kfree(text_in);
+ return 0;
+ }
+ } else {
+ pr_debug("Got CRC32C DataDigest"
+ " 0x%08x for %u bytes of text data.\n",
+ checksum, payload_length);
+ }
+ }
+ text_in[payload_length - 1] = '\0';
+ pr_debug("Successfully read %d bytes of text"
+ " data.\n", payload_length);
+ }
+
+ return iscsit_process_text_cmd(conn, cmd, hdr);
+
+reject:
+ kfree(cmd->text_in_ptr);
+ cmd->text_in_ptr = NULL;
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf);
+}
+EXPORT_SYMBOL(iscsit_handle_text_cmd);
+
+int iscsit_logout_closesession(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+ struct iscsi_conn *conn_p;
+ struct iscsi_session *sess = conn->sess;
+
+ pr_debug("Received logout request CLOSESESSION on CID: %hu"
+ " for SID: %u.\n", conn->cid, conn->sess->sid);
+
+ atomic_set(&sess->session_logout, 1);
+ atomic_set(&conn->conn_logout_remove, 1);
+ conn->conn_logout_reason = ISCSI_LOGOUT_REASON_CLOSE_SESSION;
+
+ iscsit_inc_conn_usage_count(conn);
+ iscsit_inc_session_usage_count(sess);
+
+ spin_lock_bh(&sess->conn_lock);
+ list_for_each_entry(conn_p, &sess->sess_conn_list, conn_list) {
+ if (conn_p->conn_state != TARG_CONN_STATE_LOGGED_IN)
+ continue;
+
+ pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
+ conn_p->conn_state = TARG_CONN_STATE_IN_LOGOUT;
+ }
+ spin_unlock_bh(&sess->conn_lock);
+
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+
+ return 0;
+}
+
+int iscsit_logout_closeconnection(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+ struct iscsi_conn *l_conn;
+ struct iscsi_session *sess = conn->sess;
+
+ pr_debug("Received logout request CLOSECONNECTION for CID:"
+ " %hu on CID: %hu.\n", cmd->logout_cid, conn->cid);
+
+ /*
+ * A Logout Request with a CLOSECONNECTION reason code for a CID
+ * can arrive on a connection with a differing CID.
+ */
+ if (conn->cid == cmd->logout_cid) {
+ spin_lock_bh(&conn->state_lock);
+ pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
+ conn->conn_state = TARG_CONN_STATE_IN_LOGOUT;
+
+ atomic_set(&conn->conn_logout_remove, 1);
+ conn->conn_logout_reason = ISCSI_LOGOUT_REASON_CLOSE_CONNECTION;
+ iscsit_inc_conn_usage_count(conn);
+
+ spin_unlock_bh(&conn->state_lock);
+ } else {
+ /*
+ * Handle all different cid CLOSECONNECTION requests in
+ * iscsit_logout_post_handler_diffcid() as to give enough
+ * time for any non immediate command's CmdSN to be
+ * acknowledged on the connection in question.
+ *
+ * Here we simply make sure the CID is still around.
+ */
+ l_conn = iscsit_get_conn_from_cid(sess,
+ cmd->logout_cid);
+ if (!l_conn) {
+ cmd->logout_response = ISCSI_LOGOUT_CID_NOT_FOUND;
+ iscsit_add_cmd_to_response_queue(cmd, conn,
+ cmd->i_state);
+ return 0;
+ }
+
+ iscsit_dec_conn_usage_count(l_conn);
+ }
+
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+
+ return 0;
+}
+
+int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+ struct iscsi_session *sess = conn->sess;
+
+ pr_debug("Received explicit REMOVECONNFORRECOVERY logout for"
+ " CID: %hu on CID: %hu.\n", cmd->logout_cid, conn->cid);
+
+ if (sess->sess_ops->ErrorRecoveryLevel != 2) {
+ pr_err("Received Logout Request REMOVECONNFORRECOVERY"
+ " while ERL!=2.\n");
+ cmd->logout_response = ISCSI_LOGOUT_RECOVERY_UNSUPPORTED;
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+ return 0;
+ }
+
+ if (conn->cid == cmd->logout_cid) {
+ pr_err("Received Logout Request REMOVECONNFORRECOVERY"
+ " with CID: %hu on CID: %hu, implementation error.\n",
+ cmd->logout_cid, conn->cid);
+ cmd->logout_response = ISCSI_LOGOUT_CLEANUP_FAILED;
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+ return 0;
+ }
+
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+
+ return 0;
+}
+
+int
+iscsit_handle_logout_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ int cmdsn_ret, logout_remove = 0;
+ u8 reason_code = 0;
+ struct iscsi_logout *hdr;
+ struct iscsi_tiqn *tiqn = iscsit_snmp_get_tiqn(conn);
+
+ hdr = (struct iscsi_logout *) buf;
+ reason_code = (hdr->flags & 0x7f);
+
+ if (tiqn) {
+ spin_lock(&tiqn->logout_stats.lock);
+ if (reason_code == ISCSI_LOGOUT_REASON_CLOSE_SESSION)
+ tiqn->logout_stats.normal_logouts++;
+ else
+ tiqn->logout_stats.abnormal_logouts++;
+ spin_unlock(&tiqn->logout_stats.lock);
+ }
+
+ pr_debug("Got Logout Request ITT: 0x%08x CmdSN: 0x%08x"
+ " ExpStatSN: 0x%08x Reason: 0x%02x CID: %hu on CID: %hu\n",
+ hdr->itt, hdr->cmdsn, hdr->exp_statsn, reason_code,
+ hdr->cid, conn->cid);
+
+ if (conn->conn_state != TARG_CONN_STATE_LOGGED_IN) {
+ pr_err("Received logout request on connection that"
+ " is not in logged in state, ignoring request.\n");
+ iscsit_free_cmd(cmd, false);
+ return 0;
+ }
+
+ cmd->iscsi_opcode = ISCSI_OP_LOGOUT;
+ cmd->i_state = ISTATE_SEND_LOGOUTRSP;
+ cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
+ conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
+ cmd->targ_xfer_tag = 0xFFFFFFFF;
+ cmd->cmd_sn = be32_to_cpu(hdr->cmdsn);
+ cmd->exp_stat_sn = be32_to_cpu(hdr->exp_statsn);
+ cmd->logout_cid = be16_to_cpu(hdr->cid);
+ cmd->logout_reason = reason_code;
+ cmd->data_direction = DMA_NONE;
+
+ /*
+ * We need to sleep in these cases (by returning 1) until the Logout
+ * Response gets sent in the tx thread.
+ */
+ if ((reason_code == ISCSI_LOGOUT_REASON_CLOSE_SESSION) ||
+ ((reason_code == ISCSI_LOGOUT_REASON_CLOSE_CONNECTION) &&
+ be16_to_cpu(hdr->cid) == conn->cid))
+ logout_remove = 1;
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ if (reason_code != ISCSI_LOGOUT_REASON_RECOVERY)
+ iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
+
+ /*
+ * Immediate commands are executed, well, immediately.
+ * Non-Immediate Logout Commands are executed in CmdSN order.
+ */
+ if (cmd->immediate_cmd) {
+ int ret = iscsit_execute_cmd(cmd, 0);
+
+ if (ret < 0)
+ return ret;
+ } else {
+ cmdsn_ret = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn);
+ if (cmdsn_ret == CMDSN_LOWER_THAN_EXP)
+ logout_remove = 0;
+ else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+ return -1;
+ }
+
+ return logout_remove;
+}
+EXPORT_SYMBOL(iscsit_handle_logout_cmd);
+
+static int iscsit_handle_snack(
+ struct iscsi_conn *conn,
+ unsigned char *buf)
+{
+ struct iscsi_snack *hdr;
+
+ hdr = (struct iscsi_snack *) buf;
+ hdr->flags &= ~ISCSI_FLAG_CMD_FINAL;
+
+ pr_debug("Got ISCSI_INIT_SNACK, ITT: 0x%08x, ExpStatSN:"
+ " 0x%08x, Type: 0x%02x, BegRun: 0x%08x, RunLength: 0x%08x,"
+ " CID: %hu\n", hdr->itt, hdr->exp_statsn, hdr->flags,
+ hdr->begrun, hdr->runlength, conn->cid);
+
+ if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+ pr_err("Initiator sent SNACK request while in"
+ " ErrorRecoveryLevel=0.\n");
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ buf);
+ }
+ /*
+ * SNACK_DATA and SNACK_R2T are both 0, so check which function to
+ * call from inside iscsi_send_recovery_datain_or_r2t().
+ */
+ switch (hdr->flags & ISCSI_FLAG_SNACK_TYPE_MASK) {
+ case 0:
+ return iscsit_handle_recovery_datain_or_r2t(conn, buf,
+ hdr->itt,
+ be32_to_cpu(hdr->ttt),
+ be32_to_cpu(hdr->begrun),
+ be32_to_cpu(hdr->runlength));
+ case ISCSI_FLAG_SNACK_TYPE_STATUS:
+ return iscsit_handle_status_snack(conn, hdr->itt,
+ be32_to_cpu(hdr->ttt),
+ be32_to_cpu(hdr->begrun), be32_to_cpu(hdr->runlength));
+ case ISCSI_FLAG_SNACK_TYPE_DATA_ACK:
+ return iscsit_handle_data_ack(conn, be32_to_cpu(hdr->ttt),
+ be32_to_cpu(hdr->begrun),
+ be32_to_cpu(hdr->runlength));
+ case ISCSI_FLAG_SNACK_TYPE_RDATA:
+ /* FIXME: Support R-Data SNACK */
+ pr_err("R-Data SNACK Not Supported.\n");
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ buf);
+ default:
+ pr_err("Unknown SNACK type 0x%02x, protocol"
+ " error.\n", hdr->flags & 0x0f);
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ buf);
+ }
+
+ return 0;
+}
+
+static void iscsit_rx_thread_wait_for_tcp(struct iscsi_conn *conn)
+{
+ if ((conn->sock->sk->sk_shutdown & SEND_SHUTDOWN) ||
+ (conn->sock->sk->sk_shutdown & RCV_SHUTDOWN)) {
+ wait_for_completion_interruptible_timeout(
+ &conn->rx_half_close_comp,
+ ISCSI_RX_THREAD_TCP_TIMEOUT * HZ);
+ }
+}
+
+static int iscsit_handle_immediate_data(
+ struct iscsi_cmd *cmd,
+ struct iscsi_scsi_req *hdr,
+ u32 length)
+{
+ int iov_ret, rx_got = 0, rx_size = 0;
+ u32 checksum, iov_count = 0, padding = 0;
+ struct iscsi_conn *conn = cmd->conn;
+ struct kvec *iov;
+
+ iov_ret = iscsit_map_iovec(cmd, cmd->iov_data, cmd->write_data_done, length);
+ if (iov_ret < 0)
+ return IMMEDIATE_DATA_CANNOT_RECOVER;
+
+ rx_size = length;
+ iov_count = iov_ret;
+ iov = &cmd->iov_data[0];
+
+ padding = ((-length) & 3);
+ if (padding != 0) {
+ iov[iov_count].iov_base = cmd->pad_bytes;
+ iov[iov_count++].iov_len = padding;
+ rx_size += padding;
+ }
+
+ if (conn->conn_ops->DataDigest) {
+ iov[iov_count].iov_base = &checksum;
+ iov[iov_count++].iov_len = ISCSI_CRC_LEN;
+ rx_size += ISCSI_CRC_LEN;
+ }
+
+ rx_got = rx_data(conn, &cmd->iov_data[0], iov_count, rx_size);
+
+ iscsit_unmap_iovec(cmd);
+
+ if (rx_got != rx_size) {
+ iscsit_rx_thread_wait_for_tcp(conn);
+ return IMMEDIATE_DATA_CANNOT_RECOVER;
+ }
+
+ if (conn->conn_ops->DataDigest) {
+ u32 data_crc;
+
+ data_crc = iscsit_do_crypto_hash_sg(&conn->conn_rx_hash, cmd,
+ cmd->write_data_done, length, padding,
+ cmd->pad_bytes);
+
+ if (checksum != data_crc) {
+ pr_err("ImmediateData CRC32C DataDigest 0x%08x"
+ " does not match computed 0x%08x\n", checksum,
+ data_crc);
+
+ if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+ pr_err("Unable to recover from"
+ " Immediate Data digest failure while"
+ " in ERL=0.\n");
+ iscsit_reject_cmd(cmd,
+ ISCSI_REASON_DATA_DIGEST_ERROR,
+ (unsigned char *)hdr);
+ return IMMEDIATE_DATA_CANNOT_RECOVER;
+ } else {
+ iscsit_reject_cmd(cmd,
+ ISCSI_REASON_DATA_DIGEST_ERROR,
+ (unsigned char *)hdr);
+ return IMMEDIATE_DATA_ERL1_CRC_FAILURE;
+ }
+ } else {
+ pr_debug("Got CRC32C DataDigest 0x%08x for"
+ " %u bytes of Immediate Data\n", checksum,
+ length);
+ }
+ }
+
+ cmd->write_data_done += length;
+
+ if (cmd->write_data_done == cmd->se_cmd.data_length) {
+ spin_lock_bh(&cmd->istate_lock);
+ cmd->cmd_flags |= ICF_GOT_LAST_DATAOUT;
+ cmd->i_state = ISTATE_RECEIVED_LAST_DATAOUT;
+ spin_unlock_bh(&cmd->istate_lock);
+ }
+
+ return IMMEDIATE_DATA_NORMAL_OPERATION;
+}
+
+/*
+ * Called with sess->conn_lock held.
+ */
+/* #warning iscsi_build_conn_drop_async_message() only sends out on connections
+ with active network interface */
+static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn)
+{
+ struct iscsi_cmd *cmd;
+ struct iscsi_conn *conn_p;
+ bool found = false;
+
+ /*
+ * Only send a Asynchronous Message on connections whos network
+ * interface is still functional.
+ */
+ list_for_each_entry(conn_p, &conn->sess->sess_conn_list, conn_list) {
+ if (conn_p->conn_state == TARG_CONN_STATE_LOGGED_IN) {
+ iscsit_inc_conn_usage_count(conn_p);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return;
+
+ cmd = iscsit_allocate_cmd(conn_p, TASK_RUNNING);
+ if (!cmd) {
+ iscsit_dec_conn_usage_count(conn_p);
+ return;
+ }
+
+ cmd->logout_cid = conn->cid;
+ cmd->iscsi_opcode = ISCSI_OP_ASYNC_EVENT;
+ cmd->i_state = ISTATE_SEND_ASYNCMSG;
+
+ spin_lock_bh(&conn_p->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn_p->conn_cmd_list);
+ spin_unlock_bh(&conn_p->cmd_lock);
+
+ iscsit_add_cmd_to_response_queue(cmd, conn_p, cmd->i_state);
+ iscsit_dec_conn_usage_count(conn_p);
+}
+
+static int iscsit_send_conn_drop_async_message(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_async *hdr;
+
+ cmd->tx_size = ISCSI_HDR_LEN;
+ cmd->iscsi_opcode = ISCSI_OP_ASYNC_EVENT;
+
+ hdr = (struct iscsi_async *) cmd->pdu;
+ hdr->opcode = ISCSI_OP_ASYNC_EVENT;
+ hdr->flags = ISCSI_FLAG_CMD_FINAL;
+ cmd->init_task_tag = RESERVED_ITT;
+ cmd->targ_xfer_tag = 0xFFFFFFFF;
+ put_unaligned_be64(0xFFFFFFFFFFFFFFFFULL, &hdr->rsvd4[0]);
+ cmd->stat_sn = conn->stat_sn++;
+ hdr->statsn = cpu_to_be32(cmd->stat_sn);
+ hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
+ hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
+ hdr->async_event = ISCSI_ASYNC_MSG_DROPPING_CONNECTION;
+ hdr->param1 = cpu_to_be16(cmd->logout_cid);
+ hdr->param2 = cpu_to_be16(conn->sess->sess_ops->DefaultTime2Wait);
+ hdr->param3 = cpu_to_be16(conn->sess->sess_ops->DefaultTime2Retain);
+
+ if (conn->conn_ops->HeaderDigest) {
+ u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, hdr,
+ ISCSI_HDR_LEN, 0, NULL, (u8 *)header_digest);
+
+ cmd->tx_size += ISCSI_CRC_LEN;
+ pr_debug("Attaching CRC32C HeaderDigest to"
+ " Async Message 0x%08x\n", *header_digest);
+ }
+
+ cmd->iov_misc[0].iov_base = cmd->pdu;
+ cmd->iov_misc[0].iov_len = cmd->tx_size;
+ cmd->iov_misc_count = 1;
+
+ pr_debug("Sending Connection Dropped Async Message StatSN:"
+ " 0x%08x, for CID: %hu on CID: %hu\n", cmd->stat_sn,
+ cmd->logout_cid, conn->cid);
+ return 0;
+}
+
+static void iscsit_tx_thread_wait_for_tcp(struct iscsi_conn *conn)
+{
+ if ((conn->sock->sk->sk_shutdown & SEND_SHUTDOWN) ||
+ (conn->sock->sk->sk_shutdown & RCV_SHUTDOWN)) {
+ wait_for_completion_interruptible_timeout(
+ &conn->tx_half_close_comp,
+ ISCSI_TX_THREAD_TCP_TIMEOUT * HZ);
+ }
+}
+
+static void
+iscsit_build_datain_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
+ struct iscsi_datain *datain, struct iscsi_data_rsp *hdr,
+ bool set_statsn)
+{
+ hdr->opcode = ISCSI_OP_SCSI_DATA_IN;
+ hdr->flags = datain->flags;
+ if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+ if (cmd->se_cmd.se_cmd_flags & SCF_OVERFLOW_BIT) {
+ hdr->flags |= ISCSI_FLAG_DATA_OVERFLOW;
+ hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count);
+ } else if (cmd->se_cmd.se_cmd_flags & SCF_UNDERFLOW_BIT) {
+ hdr->flags |= ISCSI_FLAG_DATA_UNDERFLOW;
+ hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count);
+ }
+ }
+ hton24(hdr->dlength, datain->length);
+ if (hdr->flags & ISCSI_FLAG_DATA_ACK)
+ int_to_scsilun(cmd->se_cmd.orig_fe_lun,
+ (struct scsi_lun *)&hdr->lun);
+ else
+ put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun);
+
+ hdr->itt = cmd->init_task_tag;
+
+ if (hdr->flags & ISCSI_FLAG_DATA_ACK)
+ hdr->ttt = cpu_to_be32(cmd->targ_xfer_tag);
+ else
+ hdr->ttt = cpu_to_be32(0xFFFFFFFF);
+ if (set_statsn)
+ hdr->statsn = cpu_to_be32(cmd->stat_sn);
+ else
+ hdr->statsn = cpu_to_be32(0xFFFFFFFF);
+
+ hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
+ hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
+ hdr->datasn = cpu_to_be32(datain->data_sn);
+ hdr->offset = cpu_to_be32(datain->offset);
+
+ pr_debug("Built DataIN ITT: 0x%08x, StatSN: 0x%08x,"
+ " DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n",
+ cmd->init_task_tag, ntohl(hdr->statsn), ntohl(hdr->datasn),
+ ntohl(hdr->offset), datain->length, conn->cid);
+}
+
+static int iscsit_send_datain(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+ struct iscsi_data_rsp *hdr = (struct iscsi_data_rsp *)&cmd->pdu[0];
+ struct iscsi_datain datain;
+ struct iscsi_datain_req *dr;
+ struct kvec *iov;
+ u32 iov_count = 0, tx_size = 0;
+ int eodr = 0, ret, iov_ret;
+ bool set_statsn = false;
+
+ memset(&datain, 0, sizeof(struct iscsi_datain));
+ dr = iscsit_get_datain_values(cmd, &datain);
+ if (!dr) {
+ pr_err("iscsit_get_datain_values failed for ITT: 0x%08x\n",
+ cmd->init_task_tag);
+ return -1;
+ }
+ /*
+ * Be paranoid and double check the logic for now.
+ */
+ if ((datain.offset + datain.length) > cmd->se_cmd.data_length) {
+ pr_err("Command ITT: 0x%08x, datain.offset: %u and"
+ " datain.length: %u exceeds cmd->data_length: %u\n",
+ cmd->init_task_tag, datain.offset, datain.length,
+ cmd->se_cmd.data_length);
+ return -1;
+ }
+
+ atomic_long_add(datain.length, &conn->sess->tx_data_octets);
+ /*
+ * Special case for successfully execution w/ both DATAIN
+ * and Sense Data.
+ */
+ if ((datain.flags & ISCSI_FLAG_DATA_STATUS) &&
+ (cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE))
+ datain.flags &= ~ISCSI_FLAG_DATA_STATUS;
+ else {
+ if ((dr->dr_complete == DATAIN_COMPLETE_NORMAL) ||
+ (dr->dr_complete == DATAIN_COMPLETE_CONNECTION_RECOVERY)) {
+ iscsit_increment_maxcmdsn(cmd, conn->sess);
+ cmd->stat_sn = conn->stat_sn++;
+ set_statsn = true;
+ } else if (dr->dr_complete ==
+ DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY)
+ set_statsn = true;
+ }
+
+ iscsit_build_datain_pdu(cmd, conn, &datain, hdr, set_statsn);
+
+ iov = &cmd->iov_data[0];
+ iov[iov_count].iov_base = cmd->pdu;
+ iov[iov_count++].iov_len = ISCSI_HDR_LEN;
+ tx_size += ISCSI_HDR_LEN;
+
+ if (conn->conn_ops->HeaderDigest) {
+ u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, cmd->pdu,
+ ISCSI_HDR_LEN, 0, NULL, (u8 *)header_digest);
+
+ iov[0].iov_len += ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+
+ pr_debug("Attaching CRC32 HeaderDigest"
+ " for DataIN PDU 0x%08x\n", *header_digest);
+ }
+
+ iov_ret = iscsit_map_iovec(cmd, &cmd->iov_data[1],
+ datain.offset, datain.length);
+ if (iov_ret < 0)
+ return -1;
+
+ iov_count += iov_ret;
+ tx_size += datain.length;
+
+ cmd->padding = ((-datain.length) & 3);
+ if (cmd->padding) {
+ iov[iov_count].iov_base = cmd->pad_bytes;
+ iov[iov_count++].iov_len = cmd->padding;
+ tx_size += cmd->padding;
+
+ pr_debug("Attaching %u padding bytes\n",
+ cmd->padding);
+ }
+ if (conn->conn_ops->DataDigest) {
+ cmd->data_crc = iscsit_do_crypto_hash_sg(&conn->conn_tx_hash, cmd,
+ datain.offset, datain.length, cmd->padding, cmd->pad_bytes);
+
+ iov[iov_count].iov_base = &cmd->data_crc;
+ iov[iov_count++].iov_len = ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+
+ pr_debug("Attached CRC32C DataDigest %d bytes, crc"
+ " 0x%08x\n", datain.length+cmd->padding, cmd->data_crc);
+ }
+
+ cmd->iov_data_count = iov_count;
+ cmd->tx_size = tx_size;
+
+ /* sendpage is preferred but can't insert markers */
+ if (!conn->conn_ops->IFMarker)
+ ret = iscsit_fe_sendpage_sg(cmd, conn);
+ else
+ ret = iscsit_send_tx_data(cmd, conn, 0);
+
+ iscsit_unmap_iovec(cmd);
+
+ if (ret < 0) {
+ iscsit_tx_thread_wait_for_tcp(conn);
+ return ret;
+ }
+
+ if (dr->dr_complete) {
+ eodr = (cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ?
+ 2 : 1;
+ iscsit_free_datain_req(cmd, dr);
+ }
+
+ return eodr;
+}
+
+int
+iscsit_build_logout_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
+ struct iscsi_logout_rsp *hdr)
+{
+ struct iscsi_conn *logout_conn = NULL;
+ struct iscsi_conn_recovery *cr = NULL;
+ struct iscsi_session *sess = conn->sess;
+ /*
+ * The actual shutting down of Sessions and/or Connections
+ * for CLOSESESSION and CLOSECONNECTION Logout Requests
+ * is done in scsi_logout_post_handler().
+ */
+ switch (cmd->logout_reason) {
+ case ISCSI_LOGOUT_REASON_CLOSE_SESSION:
+ pr_debug("iSCSI session logout successful, setting"
+ " logout response to ISCSI_LOGOUT_SUCCESS.\n");
+ cmd->logout_response = ISCSI_LOGOUT_SUCCESS;
+ break;
+ case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION:
+ if (cmd->logout_response == ISCSI_LOGOUT_CID_NOT_FOUND)
+ break;
+ /*
+ * For CLOSECONNECTION logout requests carrying
+ * a matching logout CID -> local CID, the reference
+ * for the local CID will have been incremented in
+ * iscsi_logout_closeconnection().
+ *
+ * For CLOSECONNECTION logout requests carrying
+ * a different CID than the connection it arrived
+ * on, the connection responding to cmd->logout_cid
+ * is stopped in iscsit_logout_post_handler_diffcid().
+ */
+
+ pr_debug("iSCSI CID: %hu logout on CID: %hu"
+ " successful.\n", cmd->logout_cid, conn->cid);
+ cmd->logout_response = ISCSI_LOGOUT_SUCCESS;
+ break;
+ case ISCSI_LOGOUT_REASON_RECOVERY:
+ if ((cmd->logout_response == ISCSI_LOGOUT_RECOVERY_UNSUPPORTED) ||
+ (cmd->logout_response == ISCSI_LOGOUT_CLEANUP_FAILED))
+ break;
+ /*
+ * If the connection is still active from our point of view
+ * force connection recovery to occur.
+ */
+ logout_conn = iscsit_get_conn_from_cid_rcfr(sess,
+ cmd->logout_cid);
+ if (logout_conn) {
+ iscsit_connection_reinstatement_rcfr(logout_conn);
+ iscsit_dec_conn_usage_count(logout_conn);
+ }
+
+ cr = iscsit_get_inactive_connection_recovery_entry(
+ conn->sess, cmd->logout_cid);
+ if (!cr) {
+ pr_err("Unable to locate CID: %hu for"
+ " REMOVECONNFORRECOVERY Logout Request.\n",
+ cmd->logout_cid);
+ cmd->logout_response = ISCSI_LOGOUT_CID_NOT_FOUND;
+ break;
+ }
+
+ iscsit_discard_cr_cmds_by_expstatsn(cr, cmd->exp_stat_sn);
+
+ pr_debug("iSCSI REMOVECONNFORRECOVERY logout"
+ " for recovery for CID: %hu on CID: %hu successful.\n",
+ cmd->logout_cid, conn->cid);
+ cmd->logout_response = ISCSI_LOGOUT_SUCCESS;
+ break;
+ default:
+ pr_err("Unknown cmd->logout_reason: 0x%02x\n",
+ cmd->logout_reason);
+ return -1;
+ }
+
+ hdr->opcode = ISCSI_OP_LOGOUT_RSP;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ hdr->response = cmd->logout_response;
+ hdr->itt = cmd->init_task_tag;
+ cmd->stat_sn = conn->stat_sn++;
+ hdr->statsn = cpu_to_be32(cmd->stat_sn);
+
+ iscsit_increment_maxcmdsn(cmd, conn->sess);
+ hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
+ hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
+
+ pr_debug("Built Logout Response ITT: 0x%08x StatSN:"
+ " 0x%08x Response: 0x%02x CID: %hu on CID: %hu\n",
+ cmd->init_task_tag, cmd->stat_sn, hdr->response,
+ cmd->logout_cid, conn->cid);
+
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_build_logout_rsp);
+
+static int
+iscsit_send_logout(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+ struct kvec *iov;
+ int niov = 0, tx_size, rc;
+
+ rc = iscsit_build_logout_rsp(cmd, conn,
+ (struct iscsi_logout_rsp *)&cmd->pdu[0]);
+ if (rc < 0)
+ return rc;
+
+ tx_size = ISCSI_HDR_LEN;
+ iov = &cmd->iov_misc[0];
+ iov[niov].iov_base = cmd->pdu;
+ iov[niov++].iov_len = ISCSI_HDR_LEN;
+
+ if (conn->conn_ops->HeaderDigest) {
+ u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, &cmd->pdu[0],
+ ISCSI_HDR_LEN, 0, NULL, (u8 *)header_digest);
+
+ iov[0].iov_len += ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+ pr_debug("Attaching CRC32C HeaderDigest to"
+ " Logout Response 0x%08x\n", *header_digest);
+ }
+ cmd->iov_misc_count = niov;
+ cmd->tx_size = tx_size;
+
+ return 0;
+}
+
+void
+iscsit_build_nopin_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
+ struct iscsi_nopin *hdr, bool nopout_response)
+{
+ hdr->opcode = ISCSI_OP_NOOP_IN;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ hton24(hdr->dlength, cmd->buf_ptr_size);
+ if (nopout_response)
+ put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun);
+ hdr->itt = cmd->init_task_tag;
+ hdr->ttt = cpu_to_be32(cmd->targ_xfer_tag);
+ cmd->stat_sn = (nopout_response) ? conn->stat_sn++ :
+ conn->stat_sn;
+ hdr->statsn = cpu_to_be32(cmd->stat_sn);
+
+ if (nopout_response)
+ iscsit_increment_maxcmdsn(cmd, conn->sess);
+
+ hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
+ hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
+
+ pr_debug("Built NOPIN %s Response ITT: 0x%08x, TTT: 0x%08x,"
+ " StatSN: 0x%08x, Length %u\n", (nopout_response) ?
+ "Solicitied" : "Unsolicitied", cmd->init_task_tag,
+ cmd->targ_xfer_tag, cmd->stat_sn, cmd->buf_ptr_size);
+}
+EXPORT_SYMBOL(iscsit_build_nopin_rsp);
+
+/*
+ * Unsolicited NOPIN, either requesting a response or not.
+ */
+static int iscsit_send_unsolicited_nopin(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn,
+ int want_response)
+{
+ struct iscsi_nopin *hdr = (struct iscsi_nopin *)&cmd->pdu[0];
+ int tx_size = ISCSI_HDR_LEN, ret;
+
+ iscsit_build_nopin_rsp(cmd, conn, hdr, false);
+
+ if (conn->conn_ops->HeaderDigest) {
+ u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, hdr,
+ ISCSI_HDR_LEN, 0, NULL, (u8 *)header_digest);
+
+ tx_size += ISCSI_CRC_LEN;
+ pr_debug("Attaching CRC32C HeaderDigest to"
+ " NopIN 0x%08x\n", *header_digest);
+ }
+
+ cmd->iov_misc[0].iov_base = cmd->pdu;
+ cmd->iov_misc[0].iov_len = tx_size;
+ cmd->iov_misc_count = 1;
+ cmd->tx_size = tx_size;
+
+ pr_debug("Sending Unsolicited NOPIN TTT: 0x%08x StatSN:"
+ " 0x%08x CID: %hu\n", hdr->ttt, cmd->stat_sn, conn->cid);
+
+ ret = iscsit_send_tx_data(cmd, conn, 1);
+ if (ret < 0) {
+ iscsit_tx_thread_wait_for_tcp(conn);
+ return ret;
+ }
+
+ spin_lock_bh(&cmd->istate_lock);
+ cmd->i_state = want_response ?
+ ISTATE_SENT_NOPIN_WANT_RESPONSE : ISTATE_SENT_STATUS;
+ spin_unlock_bh(&cmd->istate_lock);
+
+ return 0;
+}
+
+static int
+iscsit_send_nopin(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+ struct iscsi_nopin *hdr = (struct iscsi_nopin *)&cmd->pdu[0];
+ struct kvec *iov;
+ u32 padding = 0;
+ int niov = 0, tx_size;
+
+ iscsit_build_nopin_rsp(cmd, conn, hdr, true);
+
+ tx_size = ISCSI_HDR_LEN;
+ iov = &cmd->iov_misc[0];
+ iov[niov].iov_base = cmd->pdu;
+ iov[niov++].iov_len = ISCSI_HDR_LEN;
+
+ if (conn->conn_ops->HeaderDigest) {
+ u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, hdr,
+ ISCSI_HDR_LEN, 0, NULL, (u8 *)header_digest);
+
+ iov[0].iov_len += ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+ pr_debug("Attaching CRC32C HeaderDigest"
+ " to NopIn 0x%08x\n", *header_digest);
+ }
+
+ /*
+ * NOPOUT Ping Data is attached to struct iscsi_cmd->buf_ptr.
+ * NOPOUT DataSegmentLength is at struct iscsi_cmd->buf_ptr_size.
+ */
+ if (cmd->buf_ptr_size) {
+ iov[niov].iov_base = cmd->buf_ptr;
+ iov[niov++].iov_len = cmd->buf_ptr_size;
+ tx_size += cmd->buf_ptr_size;
+
+ pr_debug("Echoing back %u bytes of ping"
+ " data.\n", cmd->buf_ptr_size);
+
+ padding = ((-cmd->buf_ptr_size) & 3);
+ if (padding != 0) {
+ iov[niov].iov_base = &cmd->pad_bytes;
+ iov[niov++].iov_len = padding;
+ tx_size += padding;
+ pr_debug("Attaching %u additional"
+ " padding bytes.\n", padding);
+ }
+ if (conn->conn_ops->DataDigest) {
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+ cmd->buf_ptr, cmd->buf_ptr_size,
+ padding, (u8 *)&cmd->pad_bytes,
+ (u8 *)&cmd->data_crc);
+
+ iov[niov].iov_base = &cmd->data_crc;
+ iov[niov++].iov_len = ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+ pr_debug("Attached DataDigest for %u"
+ " bytes of ping data, CRC 0x%08x\n",
+ cmd->buf_ptr_size, cmd->data_crc);
+ }
+ }
+
+ cmd->iov_misc_count = niov;
+ cmd->tx_size = tx_size;
+
+ return 0;
+}
+
+static int iscsit_send_r2t(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn)
+{
+ int tx_size = 0;
+ struct iscsi_r2t *r2t;
+ struct iscsi_r2t_rsp *hdr;
+ int ret;
+
+ r2t = iscsit_get_r2t_from_list(cmd);
+ if (!r2t)
+ return -1;
+
+ hdr = (struct iscsi_r2t_rsp *) cmd->pdu;
+ memset(hdr, 0, ISCSI_HDR_LEN);
+ hdr->opcode = ISCSI_OP_R2T;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ int_to_scsilun(cmd->se_cmd.orig_fe_lun,
+ (struct scsi_lun *)&hdr->lun);
+ hdr->itt = cmd->init_task_tag;
+ spin_lock_bh(&conn->sess->ttt_lock);
+ r2t->targ_xfer_tag = conn->sess->targ_xfer_tag++;
+ if (r2t->targ_xfer_tag == 0xFFFFFFFF)
+ r2t->targ_xfer_tag = conn->sess->targ_xfer_tag++;
+ spin_unlock_bh(&conn->sess->ttt_lock);
+ hdr->ttt = cpu_to_be32(r2t->targ_xfer_tag);
+ hdr->statsn = cpu_to_be32(conn->stat_sn);
+ hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
+ hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
+ hdr->r2tsn = cpu_to_be32(r2t->r2t_sn);
+ hdr->data_offset = cpu_to_be32(r2t->offset);
+ hdr->data_length = cpu_to_be32(r2t->xfer_len);
+
+ cmd->iov_misc[0].iov_base = cmd->pdu;
+ cmd->iov_misc[0].iov_len = ISCSI_HDR_LEN;
+ tx_size += ISCSI_HDR_LEN;
+
+ if (conn->conn_ops->HeaderDigest) {
+ u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, hdr,
+ ISCSI_HDR_LEN, 0, NULL, (u8 *)header_digest);
+
+ cmd->iov_misc[0].iov_len += ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+ pr_debug("Attaching CRC32 HeaderDigest for R2T"
+ " PDU 0x%08x\n", *header_digest);
+ }
+
+ pr_debug("Built %sR2T, ITT: 0x%08x, TTT: 0x%08x, StatSN:"
+ " 0x%08x, R2TSN: 0x%08x, Offset: %u, DDTL: %u, CID: %hu\n",
+ (!r2t->recovery_r2t) ? "" : "Recovery ", cmd->init_task_tag,
+ r2t->targ_xfer_tag, ntohl(hdr->statsn), r2t->r2t_sn,
+ r2t->offset, r2t->xfer_len, conn->cid);
+
+ cmd->iov_misc_count = 1;
+ cmd->tx_size = tx_size;
+
+ spin_lock_bh(&cmd->r2t_lock);
+ r2t->sent_r2t = 1;
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ ret = iscsit_send_tx_data(cmd, conn, 1);
+ if (ret < 0) {
+ iscsit_tx_thread_wait_for_tcp(conn);
+ return ret;
+ }
+
+ spin_lock_bh(&cmd->dataout_timeout_lock);
+ iscsit_start_dataout_timer(cmd, conn);
+ spin_unlock_bh(&cmd->dataout_timeout_lock);
+
+ return 0;
+}
+
+/*
+ * @recovery: If called from iscsi_task_reassign_complete_write() for
+ * connection recovery.
+ */
+int iscsit_build_r2ts_for_cmd(
+ struct iscsi_conn *conn,
+ struct iscsi_cmd *cmd,
+ bool recovery)
+{
+ int first_r2t = 1;
+ u32 offset = 0, xfer_len = 0;
+
+ spin_lock_bh(&cmd->r2t_lock);
+ if (cmd->cmd_flags & ICF_SENT_LAST_R2T) {
+ spin_unlock_bh(&cmd->r2t_lock);
+ return 0;
+ }
+
+ if (conn->sess->sess_ops->DataSequenceInOrder &&
+ !recovery)
+ cmd->r2t_offset = max(cmd->r2t_offset, cmd->write_data_done);
+
+ while (cmd->outstanding_r2ts < conn->sess->sess_ops->MaxOutstandingR2T) {
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+ offset = cmd->r2t_offset;
+
+ if (first_r2t && recovery) {
+ int new_data_end = offset +
+ conn->sess->sess_ops->MaxBurstLength -
+ cmd->next_burst_len;
+
+ if (new_data_end > cmd->se_cmd.data_length)
+ xfer_len = cmd->se_cmd.data_length - offset;
+ else
+ xfer_len =
+ conn->sess->sess_ops->MaxBurstLength -
+ cmd->next_burst_len;
+ } else {
+ int new_data_end = offset +
+ conn->sess->sess_ops->MaxBurstLength;
+
+ if (new_data_end > cmd->se_cmd.data_length)
+ xfer_len = cmd->se_cmd.data_length - offset;
+ else
+ xfer_len = conn->sess->sess_ops->MaxBurstLength;
+ }
+ cmd->r2t_offset += xfer_len;
+
+ if (cmd->r2t_offset == cmd->se_cmd.data_length)
+ cmd->cmd_flags |= ICF_SENT_LAST_R2T;
+ } else {
+ struct iscsi_seq *seq;
+
+ seq = iscsit_get_seq_holder_for_r2t(cmd);
+ if (!seq) {
+ spin_unlock_bh(&cmd->r2t_lock);
+ return -1;
+ }
+
+ offset = seq->offset;
+ xfer_len = seq->xfer_len;
+
+ if (cmd->seq_send_order == cmd->seq_count)
+ cmd->cmd_flags |= ICF_SENT_LAST_R2T;
+ }
+ cmd->outstanding_r2ts++;
+ first_r2t = 0;
+
+ if (iscsit_add_r2t_to_list(cmd, offset, xfer_len, 0, 0) < 0) {
+ spin_unlock_bh(&cmd->r2t_lock);
+ return -1;
+ }
+
+ if (cmd->cmd_flags & ICF_SENT_LAST_R2T)
+ break;
+ }
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ return 0;
+}
+
+void iscsit_build_rsp_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
+ bool inc_stat_sn, struct iscsi_scsi_rsp *hdr)
+{
+ if (inc_stat_sn)
+ cmd->stat_sn = conn->stat_sn++;
+
+ atomic_long_inc(&conn->sess->rsp_pdus);
+
+ memset(hdr, 0, ISCSI_HDR_LEN);
+ hdr->opcode = ISCSI_OP_SCSI_CMD_RSP;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ if (cmd->se_cmd.se_cmd_flags & SCF_OVERFLOW_BIT) {
+ hdr->flags |= ISCSI_FLAG_CMD_OVERFLOW;
+ hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count);
+ } else if (cmd->se_cmd.se_cmd_flags & SCF_UNDERFLOW_BIT) {
+ hdr->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
+ hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count);
+ }
+ hdr->response = cmd->iscsi_response;
+ hdr->cmd_status = cmd->se_cmd.scsi_status;
+ hdr->itt = cmd->init_task_tag;
+ hdr->statsn = cpu_to_be32(cmd->stat_sn);
+
+ iscsit_increment_maxcmdsn(cmd, conn->sess);
+ hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
+ hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
+
+ pr_debug("Built SCSI Response, ITT: 0x%08x, StatSN: 0x%08x,"
+ " Response: 0x%02x, SAM Status: 0x%02x, CID: %hu\n",
+ cmd->init_task_tag, cmd->stat_sn, cmd->se_cmd.scsi_status,
+ cmd->se_cmd.scsi_status, conn->cid);
+}
+EXPORT_SYMBOL(iscsit_build_rsp_pdu);
+
+static int iscsit_send_response(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+ struct iscsi_scsi_rsp *hdr = (struct iscsi_scsi_rsp *)&cmd->pdu[0];
+ struct kvec *iov;
+ u32 padding = 0, tx_size = 0;
+ int iov_count = 0;
+ bool inc_stat_sn = (cmd->i_state == ISTATE_SEND_STATUS);
+
+ iscsit_build_rsp_pdu(cmd, conn, inc_stat_sn, hdr);
+
+ iov = &cmd->iov_misc[0];
+ iov[iov_count].iov_base = cmd->pdu;
+ iov[iov_count++].iov_len = ISCSI_HDR_LEN;
+ tx_size += ISCSI_HDR_LEN;
+
+ /*
+ * Attach SENSE DATA payload to iSCSI Response PDU
+ */
+ if (cmd->se_cmd.sense_buffer &&
+ ((cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ||
+ (cmd->se_cmd.se_cmd_flags & SCF_EMULATED_TASK_SENSE))) {
+ put_unaligned_be16(cmd->se_cmd.scsi_sense_length, cmd->sense_buffer);
+ cmd->se_cmd.scsi_sense_length += sizeof (__be16);
+
+ padding = -(cmd->se_cmd.scsi_sense_length) & 3;
+ hton24(hdr->dlength, (u32)cmd->se_cmd.scsi_sense_length);
+ iov[iov_count].iov_base = cmd->sense_buffer;
+ iov[iov_count++].iov_len =
+ (cmd->se_cmd.scsi_sense_length + padding);
+ tx_size += cmd->se_cmd.scsi_sense_length;
+
+ if (padding) {
+ memset(cmd->sense_buffer +
+ cmd->se_cmd.scsi_sense_length, 0, padding);
+ tx_size += padding;
+ pr_debug("Adding %u bytes of padding to"
+ " SENSE.\n", padding);
+ }
+
+ if (conn->conn_ops->DataDigest) {
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+ cmd->sense_buffer,
+ (cmd->se_cmd.scsi_sense_length + padding),
+ 0, NULL, (u8 *)&cmd->data_crc);
+
+ iov[iov_count].iov_base = &cmd->data_crc;
+ iov[iov_count++].iov_len = ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+
+ pr_debug("Attaching CRC32 DataDigest for"
+ " SENSE, %u bytes CRC 0x%08x\n",
+ (cmd->se_cmd.scsi_sense_length + padding),
+ cmd->data_crc);
+ }
+
+ pr_debug("Attaching SENSE DATA: %u bytes to iSCSI"
+ " Response PDU\n",
+ cmd->se_cmd.scsi_sense_length);
+ }
+
+ if (conn->conn_ops->HeaderDigest) {
+ u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, cmd->pdu,
+ ISCSI_HDR_LEN, 0, NULL, (u8 *)header_digest);
+
+ iov[0].iov_len += ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+ pr_debug("Attaching CRC32 HeaderDigest for Response"
+ " PDU 0x%08x\n", *header_digest);
+ }
+
+ cmd->iov_misc_count = iov_count;
+ cmd->tx_size = tx_size;
+
+ return 0;
+}
+
+static u8 iscsit_convert_tcm_tmr_rsp(struct se_tmr_req *se_tmr)
+{
+ switch (se_tmr->response) {
+ case TMR_FUNCTION_COMPLETE:
+ return ISCSI_TMF_RSP_COMPLETE;
+ case TMR_TASK_DOES_NOT_EXIST:
+ return ISCSI_TMF_RSP_NO_TASK;
+ case TMR_LUN_DOES_NOT_EXIST:
+ return ISCSI_TMF_RSP_NO_LUN;
+ case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED:
+ return ISCSI_TMF_RSP_NOT_SUPPORTED;
+ case TMR_FUNCTION_REJECTED:
+ default:
+ return ISCSI_TMF_RSP_REJECTED;
+ }
+}
+
+void
+iscsit_build_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
+ struct iscsi_tm_rsp *hdr)
+{
+ struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req;
+
+ hdr->opcode = ISCSI_OP_SCSI_TMFUNC_RSP;
+ hdr->flags = ISCSI_FLAG_CMD_FINAL;
+ hdr->response = iscsit_convert_tcm_tmr_rsp(se_tmr);
+ hdr->itt = cmd->init_task_tag;
+ cmd->stat_sn = conn->stat_sn++;
+ hdr->statsn = cpu_to_be32(cmd->stat_sn);
+
+ iscsit_increment_maxcmdsn(cmd, conn->sess);
+ hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
+ hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
+
+ pr_debug("Built Task Management Response ITT: 0x%08x,"
+ " StatSN: 0x%08x, Response: 0x%02x, CID: %hu\n",
+ cmd->init_task_tag, cmd->stat_sn, hdr->response, conn->cid);
+}
+EXPORT_SYMBOL(iscsit_build_task_mgt_rsp);
+
+static int
+iscsit_send_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+ struct iscsi_tm_rsp *hdr = (struct iscsi_tm_rsp *)&cmd->pdu[0];
+ u32 tx_size = 0;
+
+ iscsit_build_task_mgt_rsp(cmd, conn, hdr);
+
+ cmd->iov_misc[0].iov_base = cmd->pdu;
+ cmd->iov_misc[0].iov_len = ISCSI_HDR_LEN;
+ tx_size += ISCSI_HDR_LEN;
+
+ if (conn->conn_ops->HeaderDigest) {
+ u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, hdr,
+ ISCSI_HDR_LEN, 0, NULL, (u8 *)header_digest);
+
+ cmd->iov_misc[0].iov_len += ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+ pr_debug("Attaching CRC32 HeaderDigest for Task"
+ " Mgmt Response PDU 0x%08x\n", *header_digest);
+ }
+
+ cmd->iov_misc_count = 1;
+ cmd->tx_size = tx_size;
+
+ return 0;
+}
+
+static bool iscsit_check_inaddr_any(struct iscsi_np *np)
+{
+ bool ret = false;
+
+ if (np->np_sockaddr.ss_family == AF_INET6) {
+ const struct sockaddr_in6 sin6 = {
+ .sin6_addr = IN6ADDR_ANY_INIT };
+ struct sockaddr_in6 *sock_in6 =
+ (struct sockaddr_in6 *)&np->np_sockaddr;
+
+ if (!memcmp(sock_in6->sin6_addr.s6_addr,
+ sin6.sin6_addr.s6_addr, 16))
+ ret = true;
+ } else {
+ struct sockaddr_in * sock_in =
+ (struct sockaddr_in *)&np->np_sockaddr;
+
+ if (sock_in->sin_addr.s_addr == htonl(INADDR_ANY))
+ ret = true;
+ }
+
+ return ret;
+}
+
+#define SENDTARGETS_BUF_LIMIT 32768U
+
+static int
+iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
+ enum iscsit_transport_type network_transport)
+{
+ char *payload = NULL;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_portal_group *tpg;
+ struct iscsi_tiqn *tiqn;
+ struct iscsi_tpg_np *tpg_np;
+ int buffer_len, end_of_buf = 0, len = 0, payload_len = 0;
+ int target_name_printed;
+ unsigned char buf[ISCSI_IQN_LEN+12]; /* iqn + "TargetName=" + \0 */
+ unsigned char *text_in = cmd->text_in_ptr, *text_ptr = NULL;
+
+ buffer_len = max(conn->conn_ops->MaxRecvDataSegmentLength,
+ SENDTARGETS_BUF_LIMIT);
+
+ payload = kzalloc(buffer_len, GFP_KERNEL);
+ if (!payload) {
+ pr_err("Unable to allocate memory for sendtargets"
+ " response.\n");
+ return -ENOMEM;
+ }
+ /*
+ * Locate pointer to iqn./eui. string for IFC_SENDTARGETS_SINGLE
+ * explicit case..
+ */
+ if (cmd->cmd_flags & IFC_SENDTARGETS_SINGLE) {
+ text_ptr = strchr(text_in, '=');
+ if (!text_ptr) {
+ pr_err("Unable to locate '=' string in text_in:"
+ " %s\n", text_in);
+ kfree(payload);
+ return -EINVAL;
+ }
+ /*
+ * Skip over '=' character..
+ */
+ text_ptr += 1;
+ }
+
+ spin_lock(&tiqn_lock);
+ list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) {
+ if ((cmd->cmd_flags & IFC_SENDTARGETS_SINGLE) &&
+ strcmp(tiqn->tiqn, text_ptr)) {
+ continue;
+ }
+
+ target_name_printed = 0;
+
+ spin_lock(&tiqn->tiqn_tpg_lock);
+ list_for_each_entry(tpg, &tiqn->tiqn_tpg_list, tpg_list) {
+
+ /* If demo_mode_discovery=0 and generate_node_acls=0
+ * (demo mode dislabed) do not return
+ * TargetName+TargetAddress unless a NodeACL exists.
+ */
+
+ if ((tpg->tpg_attrib.generate_node_acls == 0) &&
+ (tpg->tpg_attrib.demo_mode_discovery == 0) &&
+ (!core_tpg_get_initiator_node_acl(&tpg->tpg_se_tpg,
+ cmd->conn->sess->sess_ops->InitiatorName))) {
+ continue;
+ }
+
+ spin_lock(&tpg->tpg_state_lock);
+ if ((tpg->tpg_state == TPG_STATE_FREE) ||
+ (tpg->tpg_state == TPG_STATE_INACTIVE)) {
+ spin_unlock(&tpg->tpg_state_lock);
+ continue;
+ }
+ spin_unlock(&tpg->tpg_state_lock);
+
+ spin_lock(&tpg->tpg_np_lock);
+ list_for_each_entry(tpg_np, &tpg->tpg_gnp_list,
+ tpg_np_list) {
+ struct iscsi_np *np = tpg_np->tpg_np;
+ bool inaddr_any = iscsit_check_inaddr_any(np);
+
+ if (np->np_network_transport != network_transport)
+ continue;
+
+ if (!target_name_printed) {
+ len = sprintf(buf, "TargetName=%s",
+ tiqn->tiqn);
+ len += 1;
+
+ if ((len + payload_len) > buffer_len) {
+ spin_unlock(&tpg->tpg_np_lock);
+ spin_unlock(&tiqn->tiqn_tpg_lock);
+ end_of_buf = 1;
+ goto eob;
+ }
+ memcpy(payload + payload_len, buf, len);
+ payload_len += len;
+ target_name_printed = 1;
+ }
+
+ len = sprintf(buf, "TargetAddress="
+ "%s:%hu,%hu",
+ inaddr_any ? conn->local_ip : np->np_ip,
+ inaddr_any ? conn->local_port : np->np_port,
+ tpg->tpgt);
+ len += 1;
+
+ if ((len + payload_len) > buffer_len) {
+ spin_unlock(&tpg->tpg_np_lock);
+ spin_unlock(&tiqn->tiqn_tpg_lock);
+ end_of_buf = 1;
+ goto eob;
+ }
+ memcpy(payload + payload_len, buf, len);
+ payload_len += len;
+ }
+ spin_unlock(&tpg->tpg_np_lock);
+ }
+ spin_unlock(&tiqn->tiqn_tpg_lock);
+eob:
+ if (end_of_buf)
+ break;
+
+ if (cmd->cmd_flags & IFC_SENDTARGETS_SINGLE)
+ break;
+ }
+ spin_unlock(&tiqn_lock);
+
+ cmd->buf_ptr = payload;
+
+ return payload_len;
+}
+
+int
+iscsit_build_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
+ struct iscsi_text_rsp *hdr,
+ enum iscsit_transport_type network_transport)
+{
+ int text_length, padding;
+
+ text_length = iscsit_build_sendtargets_response(cmd, network_transport);
+ if (text_length < 0)
+ return text_length;
+
+ hdr->opcode = ISCSI_OP_TEXT_RSP;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ padding = ((-text_length) & 3);
+ hton24(hdr->dlength, text_length);
+ hdr->itt = cmd->init_task_tag;
+ hdr->ttt = cpu_to_be32(cmd->targ_xfer_tag);
+ cmd->stat_sn = conn->stat_sn++;
+ hdr->statsn = cpu_to_be32(cmd->stat_sn);
+
+ iscsit_increment_maxcmdsn(cmd, conn->sess);
+ hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
+ hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
+
+ pr_debug("Built Text Response: ITT: 0x%08x, StatSN: 0x%08x,"
+ " Length: %u, CID: %hu\n", cmd->init_task_tag, cmd->stat_sn,
+ text_length, conn->cid);
+
+ return text_length + padding;
+}
+EXPORT_SYMBOL(iscsit_build_text_rsp);
+
+/*
+ * FIXME: Add support for F_BIT and C_BIT when the length is longer than
+ * MaxRecvDataSegmentLength.
+ */
+static int iscsit_send_text_rsp(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_text_rsp *hdr = (struct iscsi_text_rsp *)cmd->pdu;
+ struct kvec *iov;
+ u32 tx_size = 0;
+ int text_length, iov_count = 0, rc;
+
+ rc = iscsit_build_text_rsp(cmd, conn, hdr, ISCSI_TCP);
+ if (rc < 0)
+ return rc;
+
+ text_length = rc;
+ iov = &cmd->iov_misc[0];
+ iov[iov_count].iov_base = cmd->pdu;
+ iov[iov_count++].iov_len = ISCSI_HDR_LEN;
+ iov[iov_count].iov_base = cmd->buf_ptr;
+ iov[iov_count++].iov_len = text_length;
+
+ tx_size += (ISCSI_HDR_LEN + text_length);
+
+ if (conn->conn_ops->HeaderDigest) {
+ u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, hdr,
+ ISCSI_HDR_LEN, 0, NULL, (u8 *)header_digest);
+
+ iov[0].iov_len += ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+ pr_debug("Attaching CRC32 HeaderDigest for"
+ " Text Response PDU 0x%08x\n", *header_digest);
+ }
+
+ if (conn->conn_ops->DataDigest) {
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
+ cmd->buf_ptr, text_length,
+ 0, NULL, (u8 *)&cmd->data_crc);
+
+ iov[iov_count].iov_base = &cmd->data_crc;
+ iov[iov_count++].iov_len = ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+
+ pr_debug("Attaching DataDigest for %u bytes of text"
+ " data, CRC 0x%08x\n", text_length,
+ cmd->data_crc);
+ }
+
+ cmd->iov_misc_count = iov_count;
+ cmd->tx_size = tx_size;
+
+ return 0;
+}
+
+void
+iscsit_build_reject(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
+ struct iscsi_reject *hdr)
+{
+ hdr->opcode = ISCSI_OP_REJECT;
+ hdr->reason = cmd->reject_reason;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ hton24(hdr->dlength, ISCSI_HDR_LEN);
+ hdr->ffffffff = cpu_to_be32(0xffffffff);
+ cmd->stat_sn = conn->stat_sn++;
+ hdr->statsn = cpu_to_be32(cmd->stat_sn);
+ hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
+ hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
+
+}
+EXPORT_SYMBOL(iscsit_build_reject);
+
+static int iscsit_send_reject(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_reject *hdr = (struct iscsi_reject *)&cmd->pdu[0];
+ struct kvec *iov;
+ u32 iov_count = 0, tx_size;
+
+ iscsit_build_reject(cmd, conn, hdr);
+
+ iov = &cmd->iov_misc[0];
+ iov[iov_count].iov_base = cmd->pdu;
+ iov[iov_count++].iov_len = ISCSI_HDR_LEN;
+ iov[iov_count].iov_base = cmd->buf_ptr;
+ iov[iov_count++].iov_len = ISCSI_HDR_LEN;
+
+ tx_size = (ISCSI_HDR_LEN + ISCSI_HDR_LEN);
+
+ if (conn->conn_ops->HeaderDigest) {
+ u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
+
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, hdr,
+ ISCSI_HDR_LEN, 0, NULL, (u8 *)header_digest);
+
+ iov[0].iov_len += ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+ pr_debug("Attaching CRC32 HeaderDigest for"
+ " REJECT PDU 0x%08x\n", *header_digest);
+ }
+
+ if (conn->conn_ops->DataDigest) {
+ iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, cmd->buf_ptr,
+ ISCSI_HDR_LEN, 0, NULL, (u8 *)&cmd->data_crc);
+
+ iov[iov_count].iov_base = &cmd->data_crc;
+ iov[iov_count++].iov_len = ISCSI_CRC_LEN;
+ tx_size += ISCSI_CRC_LEN;
+ pr_debug("Attaching CRC32 DataDigest for REJECT"
+ " PDU 0x%08x\n", cmd->data_crc);
+ }
+
+ cmd->iov_misc_count = iov_count;
+ cmd->tx_size = tx_size;
+
+ pr_debug("Built Reject PDU StatSN: 0x%08x, Reason: 0x%02x,"
+ " CID: %hu\n", ntohl(hdr->statsn), hdr->reason, conn->cid);
+
+ return 0;
+}
+
+void iscsit_thread_get_cpumask(struct iscsi_conn *conn)
+{
+ struct iscsi_thread_set *ts = conn->thread_set;
+ int ord, cpu;
+ /*
+ * thread_id is assigned from iscsit_global->ts_bitmap from
+ * within iscsi_thread_set.c:iscsi_allocate_thread_sets()
+ *
+ * Here we use thread_id to determine which CPU that this
+ * iSCSI connection's iscsi_thread_set will be scheduled to
+ * execute upon.
+ */
+ ord = ts->thread_id % cpumask_weight(cpu_online_mask);
+ for_each_online_cpu(cpu) {
+ if (ord-- == 0) {
+ cpumask_set_cpu(cpu, conn->conn_cpumask);
+ return;
+ }
+ }
+ /*
+ * This should never be reached..
+ */
+ dump_stack();
+ cpumask_setall(conn->conn_cpumask);
+}
+
+static inline void iscsit_thread_check_cpumask(
+ struct iscsi_conn *conn,
+ struct task_struct *p,
+ int mode)
+{
+ char buf[128];
+ /*
+ * mode == 1 signals iscsi_target_tx_thread() usage.
+ * mode == 0 signals iscsi_target_rx_thread() usage.
+ */
+ if (mode == 1) {
+ if (!conn->conn_tx_reset_cpumask)
+ return;
+ conn->conn_tx_reset_cpumask = 0;
+ } else {
+ if (!conn->conn_rx_reset_cpumask)
+ return;
+ conn->conn_rx_reset_cpumask = 0;
+ }
+ /*
+ * Update the CPU mask for this single kthread so that
+ * both TX and RX kthreads are scheduled to run on the
+ * same CPU.
+ */
+ memset(buf, 0, 128);
+ cpumask_scnprintf(buf, 128, conn->conn_cpumask);
+ set_cpus_allowed_ptr(p, conn->conn_cpumask);
+}
+
+static int
+iscsit_immediate_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state)
+{
+ int ret;
+
+ switch (state) {
+ case ISTATE_SEND_R2T:
+ ret = iscsit_send_r2t(cmd, conn);
+ if (ret < 0)
+ goto err;
+ break;
+ case ISTATE_REMOVE:
+ spin_lock_bh(&conn->cmd_lock);
+ list_del_init(&cmd->i_conn_node);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ iscsit_free_cmd(cmd, false);
+ break;
+ case ISTATE_SEND_NOPIN_WANT_RESPONSE:
+ iscsit_mod_nopin_response_timer(conn);
+ ret = iscsit_send_unsolicited_nopin(cmd, conn, 1);
+ if (ret < 0)
+ goto err;
+ break;
+ case ISTATE_SEND_NOPIN_NO_RESPONSE:
+ ret = iscsit_send_unsolicited_nopin(cmd, conn, 0);
+ if (ret < 0)
+ goto err;
+ break;
+ default:
+ pr_err("Unknown Opcode: 0x%02x ITT:"
+ " 0x%08x, i_state: %d on CID: %hu\n",
+ cmd->iscsi_opcode, cmd->init_task_tag, state,
+ conn->cid);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ return -1;
+}
+
+static int
+iscsit_handle_immediate_queue(struct iscsi_conn *conn)
+{
+ struct iscsit_transport *t = conn->conn_transport;
+ struct iscsi_queue_req *qr;
+ struct iscsi_cmd *cmd;
+ u8 state;
+ int ret;
+
+ while ((qr = iscsit_get_cmd_from_immediate_queue(conn))) {
+ atomic_set(&conn->check_immediate_queue, 0);
+ cmd = qr->cmd;
+ state = qr->state;
+ kmem_cache_free(lio_qr_cache, qr);
+
+ ret = t->iscsit_immediate_queue(conn, cmd, state);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+iscsit_response_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state)
+{
+ int ret;
+
+check_rsp_state:
+ switch (state) {
+ case ISTATE_SEND_DATAIN:
+ ret = iscsit_send_datain(cmd, conn);
+ if (ret < 0)
+ goto err;
+ else if (!ret)
+ /* more drs */
+ goto check_rsp_state;
+ else if (ret == 1) {
+ /* all done */
+ spin_lock_bh(&cmd->istate_lock);
+ cmd->i_state = ISTATE_SENT_STATUS;
+ spin_unlock_bh(&cmd->istate_lock);
+
+ if (atomic_read(&conn->check_immediate_queue))
+ return 1;
+
+ return 0;
+ } else if (ret == 2) {
+ /* Still must send status,
+ SCF_TRANSPORT_TASK_SENSE was set */
+ spin_lock_bh(&cmd->istate_lock);
+ cmd->i_state = ISTATE_SEND_STATUS;
+ spin_unlock_bh(&cmd->istate_lock);
+ state = ISTATE_SEND_STATUS;
+ goto check_rsp_state;
+ }
+
+ break;
+ case ISTATE_SEND_STATUS:
+ case ISTATE_SEND_STATUS_RECOVERY:
+ ret = iscsit_send_response(cmd, conn);
+ break;
+ case ISTATE_SEND_LOGOUTRSP:
+ ret = iscsit_send_logout(cmd, conn);
+ break;
+ case ISTATE_SEND_ASYNCMSG:
+ ret = iscsit_send_conn_drop_async_message(
+ cmd, conn);
+ break;
+ case ISTATE_SEND_NOPIN:
+ ret = iscsit_send_nopin(cmd, conn);
+ break;
+ case ISTATE_SEND_REJECT:
+ ret = iscsit_send_reject(cmd, conn);
+ break;
+ case ISTATE_SEND_TASKMGTRSP:
+ ret = iscsit_send_task_mgt_rsp(cmd, conn);
+ if (ret != 0)
+ break;
+ ret = iscsit_tmr_post_handler(cmd, conn);
+ if (ret != 0)
+ iscsit_fall_back_to_erl0(conn->sess);
+ break;
+ case ISTATE_SEND_TEXTRSP:
+ ret = iscsit_send_text_rsp(cmd, conn);
+ break;
+ default:
+ pr_err("Unknown Opcode: 0x%02x ITT:"
+ " 0x%08x, i_state: %d on CID: %hu\n",
+ cmd->iscsi_opcode, cmd->init_task_tag,
+ state, conn->cid);
+ goto err;
+ }
+ if (ret < 0)
+ goto err;
+
+ if (iscsit_send_tx_data(cmd, conn, 1) < 0) {
+ iscsit_tx_thread_wait_for_tcp(conn);
+ iscsit_unmap_iovec(cmd);
+ goto err;
+ }
+ iscsit_unmap_iovec(cmd);
+
+ switch (state) {
+ case ISTATE_SEND_LOGOUTRSP:
+ if (!iscsit_logout_post_handler(cmd, conn))
+ goto restart;
+ /* fall through */
+ case ISTATE_SEND_STATUS:
+ case ISTATE_SEND_ASYNCMSG:
+ case ISTATE_SEND_NOPIN:
+ case ISTATE_SEND_STATUS_RECOVERY:
+ case ISTATE_SEND_TEXTRSP:
+ case ISTATE_SEND_TASKMGTRSP:
+ case ISTATE_SEND_REJECT:
+ spin_lock_bh(&cmd->istate_lock);
+ cmd->i_state = ISTATE_SENT_STATUS;
+ spin_unlock_bh(&cmd->istate_lock);
+ break;
+ default:
+ pr_err("Unknown Opcode: 0x%02x ITT:"
+ " 0x%08x, i_state: %d on CID: %hu\n",
+ cmd->iscsi_opcode, cmd->init_task_tag,
+ cmd->i_state, conn->cid);
+ goto err;
+ }
+
+ if (atomic_read(&conn->check_immediate_queue))
+ return 1;
+
+ return 0;
+
+err:
+ return -1;
+restart:
+ return -EAGAIN;
+}
+
+static int iscsit_handle_response_queue(struct iscsi_conn *conn)
+{
+ struct iscsit_transport *t = conn->conn_transport;
+ struct iscsi_queue_req *qr;
+ struct iscsi_cmd *cmd;
+ u8 state;
+ int ret;
+
+ while ((qr = iscsit_get_cmd_from_response_queue(conn))) {
+ cmd = qr->cmd;
+ state = qr->state;
+ kmem_cache_free(lio_qr_cache, qr);
+
+ ret = t->iscsit_response_queue(conn, cmd, state);
+ if (ret == 1 || ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+int iscsi_target_tx_thread(void *arg)
+{
+ int ret = 0;
+ struct iscsi_conn *conn;
+ struct iscsi_thread_set *ts = arg;
+ /*
+ * Allow ourselves to be interrupted by SIGINT so that a
+ * connection recovery / failure event can be triggered externally.
+ */
+ allow_signal(SIGINT);
+
+restart:
+ conn = iscsi_tx_thread_pre_handler(ts);
+ if (!conn)
+ goto out;
+
+ ret = 0;
+
+ while (!kthread_should_stop()) {
+ /*
+ * Ensure that both TX and RX per connection kthreads
+ * are scheduled to run on the same CPU.
+ */
+ iscsit_thread_check_cpumask(conn, current, 1);
+
+ wait_event_interruptible(conn->queues_wq,
+ !iscsit_conn_all_queues_empty(conn) ||
+ ts->status == ISCSI_THREAD_SET_RESET);
+
+ if ((ts->status == ISCSI_THREAD_SET_RESET) ||
+ signal_pending(current))
+ goto transport_err;
+
+get_immediate:
+ ret = iscsit_handle_immediate_queue(conn);
+ if (ret < 0)
+ goto transport_err;
+
+ ret = iscsit_handle_response_queue(conn);
+ if (ret == 1)
+ goto get_immediate;
+ else if (ret == -EAGAIN)
+ goto restart;
+ else if (ret < 0)
+ goto transport_err;
+ }
+
+transport_err:
+ iscsit_take_action_for_connection_exit(conn);
+ goto restart;
+out:
+ return 0;
+}
+
+static int iscsi_target_rx_opcode(struct iscsi_conn *conn, unsigned char *buf)
+{
+ struct iscsi_hdr *hdr = (struct iscsi_hdr *)buf;
+ struct iscsi_cmd *cmd;
+ int ret = 0;
+
+ switch (hdr->opcode & ISCSI_OPCODE_MASK) {
+ case ISCSI_OP_SCSI_CMD:
+ cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
+ if (!cmd)
+ goto reject;
+
+ ret = iscsit_handle_scsi_cmd(conn, cmd, buf);
+ break;
+ case ISCSI_OP_SCSI_DATA_OUT:
+ ret = iscsit_handle_data_out(conn, buf);
+ break;
+ case ISCSI_OP_NOOP_OUT:
+ cmd = NULL;
+ if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
+ cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
+ if (!cmd)
+ goto reject;
+ }
+ ret = iscsit_handle_nop_out(conn, cmd, buf);
+ break;
+ case ISCSI_OP_SCSI_TMFUNC:
+ cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
+ if (!cmd)
+ goto reject;
+
+ ret = iscsit_handle_task_mgt_cmd(conn, cmd, buf);
+ break;
+ case ISCSI_OP_TEXT:
+ cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
+ if (!cmd)
+ goto reject;
+
+ ret = iscsit_handle_text_cmd(conn, cmd, buf);
+ break;
+ case ISCSI_OP_LOGOUT:
+ cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
+ if (!cmd)
+ goto reject;
+
+ ret = iscsit_handle_logout_cmd(conn, cmd, buf);
+ if (ret > 0)
+ wait_for_completion_timeout(&conn->conn_logout_comp,
+ SECONDS_FOR_LOGOUT_COMP * HZ);
+ break;
+ case ISCSI_OP_SNACK:
+ ret = iscsit_handle_snack(conn, buf);
+ break;
+ default:
+ pr_err("Got unknown iSCSI OpCode: 0x%02x\n", hdr->opcode);
+ if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+ pr_err("Cannot recover from unknown"
+ " opcode while ERL=0, closing iSCSI connection.\n");
+ return -1;
+ }
+ if (!conn->conn_ops->OFMarker) {
+ pr_err("Unable to recover from unknown"
+ " opcode while OFMarker=No, closing iSCSI"
+ " connection.\n");
+ return -1;
+ }
+ if (iscsit_recover_from_unknown_opcode(conn) < 0) {
+ pr_err("Unable to recover from unknown"
+ " opcode, closing iSCSI connection.\n");
+ return -1;
+ }
+ break;
+ }
+
+ return ret;
+reject:
+ return iscsit_add_reject(conn, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
+}
+
+int iscsi_target_rx_thread(void *arg)
+{
+ int ret;
+ u8 buffer[ISCSI_HDR_LEN], opcode;
+ u32 checksum = 0, digest = 0;
+ struct iscsi_conn *conn = NULL;
+ struct iscsi_thread_set *ts = arg;
+ struct kvec iov;
+ /*
+ * Allow ourselves to be interrupted by SIGINT so that a
+ * connection recovery / failure event can be triggered externally.
+ */
+ allow_signal(SIGINT);
+
+restart:
+ conn = iscsi_rx_thread_pre_handler(ts);
+ if (!conn)
+ goto out;
+
+ if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) {
+ struct completion comp;
+ int rc;
+
+ init_completion(&comp);
+ rc = wait_for_completion_interruptible(&comp);
+ if (rc < 0)
+ goto transport_err;
+
+ goto out;
+ }
+
+ while (!kthread_should_stop()) {
+ /*
+ * Ensure that both TX and RX per connection kthreads
+ * are scheduled to run on the same CPU.
+ */
+ iscsit_thread_check_cpumask(conn, current, 0);
+
+ memset(buffer, 0, ISCSI_HDR_LEN);
+ memset(&iov, 0, sizeof(struct kvec));
+
+ iov.iov_base = buffer;
+ iov.iov_len = ISCSI_HDR_LEN;
+
+ ret = rx_data(conn, &iov, 1, ISCSI_HDR_LEN);
+ if (ret != ISCSI_HDR_LEN) {
+ iscsit_rx_thread_wait_for_tcp(conn);
+ goto transport_err;
+ }
+
+ if (conn->conn_ops->HeaderDigest) {
+ iov.iov_base = &digest;
+ iov.iov_len = ISCSI_CRC_LEN;
+
+ ret = rx_data(conn, &iov, 1, ISCSI_CRC_LEN);
+ if (ret != ISCSI_CRC_LEN) {
+ iscsit_rx_thread_wait_for_tcp(conn);
+ goto transport_err;
+ }
+
+ iscsit_do_crypto_hash_buf(&conn->conn_rx_hash,
+ buffer, ISCSI_HDR_LEN,
+ 0, NULL, (u8 *)&checksum);
+
+ if (digest != checksum) {
+ pr_err("HeaderDigest CRC32C failed,"
+ " received 0x%08x, computed 0x%08x\n",
+ digest, checksum);
+ /*
+ * Set the PDU to 0xff so it will intentionally
+ * hit default in the switch below.
+ */
+ memset(buffer, 0xff, ISCSI_HDR_LEN);
+ atomic_long_inc(&conn->sess->conn_digest_errors);
+ } else {
+ pr_debug("Got HeaderDigest CRC32C"
+ " 0x%08x\n", checksum);
+ }
+ }
+
+ if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT)
+ goto transport_err;
+
+ opcode = buffer[0] & ISCSI_OPCODE_MASK;
+
+ if (conn->sess->sess_ops->SessionType &&
+ ((!(opcode & ISCSI_OP_TEXT)) ||
+ (!(opcode & ISCSI_OP_LOGOUT)))) {
+ pr_err("Received illegal iSCSI Opcode: 0x%02x"
+ " while in Discovery Session, rejecting.\n", opcode);
+ iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ buffer);
+ goto transport_err;
+ }
+
+ ret = iscsi_target_rx_opcode(conn, buffer);
+ if (ret < 0)
+ goto transport_err;
+ }
+
+transport_err:
+ if (!signal_pending(current))
+ atomic_set(&conn->transport_failed, 1);
+ iscsit_take_action_for_connection_exit(conn);
+ goto restart;
+out:
+ return 0;
+}
+
+static void iscsit_release_commands_from_conn(struct iscsi_conn *conn)
+{
+ struct iscsi_cmd *cmd = NULL, *cmd_tmp = NULL;
+ struct iscsi_session *sess = conn->sess;
+ /*
+ * We expect this function to only ever be called from either RX or TX
+ * thread context via iscsit_close_connection() once the other context
+ * has been reset -> returned sleeping pre-handler state.
+ */
+ spin_lock_bh(&conn->cmd_lock);
+ list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) {
+
+ list_del_init(&cmd->i_conn_node);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ iscsit_increment_maxcmdsn(cmd, sess);
+
+ iscsit_free_cmd(cmd, true);
+
+ spin_lock_bh(&conn->cmd_lock);
+ }
+ spin_unlock_bh(&conn->cmd_lock);
+}
+
+static void iscsit_stop_timers_for_cmds(
+ struct iscsi_conn *conn)
+{
+ struct iscsi_cmd *cmd;
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_for_each_entry(cmd, &conn->conn_cmd_list, i_conn_node) {
+ if (cmd->data_direction == DMA_TO_DEVICE)
+ iscsit_stop_dataout_timer(cmd);
+ }
+ spin_unlock_bh(&conn->cmd_lock);
+}
+
+int iscsit_close_connection(
+ struct iscsi_conn *conn)
+{
+ int conn_logout = (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT);
+ struct iscsi_session *sess = conn->sess;
+
+ pr_debug("Closing iSCSI connection CID %hu on SID:"
+ " %u\n", conn->cid, sess->sid);
+ /*
+ * Always up conn_logout_comp just in case the RX Thread is sleeping
+ * and the logout response never got sent because the connection
+ * failed.
+ */
+ complete(&conn->conn_logout_comp);
+
+ iscsi_release_thread_set(conn);
+
+ iscsit_stop_timers_for_cmds(conn);
+ iscsit_stop_nopin_response_timer(conn);
+ iscsit_stop_nopin_timer(conn);
+
+ if (conn->conn_transport->iscsit_wait_conn)
+ conn->conn_transport->iscsit_wait_conn(conn);
+
+ /*
+ * During Connection recovery drop unacknowledged out of order
+ * commands for this connection, and prepare the other commands
+ * for realligence.
+ *
+ * During normal operation clear the out of order commands (but
+ * do not free the struct iscsi_ooo_cmdsn's) and release all
+ * struct iscsi_cmds.
+ */
+ if (atomic_read(&conn->connection_recovery)) {
+ iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(conn);
+ iscsit_prepare_cmds_for_realligance(conn);
+ } else {
+ iscsit_clear_ooo_cmdsns_for_conn(conn);
+ iscsit_release_commands_from_conn(conn);
+ }
+ iscsit_free_queue_reqs_for_conn(conn);
+
+ /*
+ * Handle decrementing session or connection usage count if
+ * a logout response was not able to be sent because the
+ * connection failed. Fall back to Session Recovery here.
+ */
+ if (atomic_read(&conn->conn_logout_remove)) {
+ if (conn->conn_logout_reason == ISCSI_LOGOUT_REASON_CLOSE_SESSION) {
+ iscsit_dec_conn_usage_count(conn);
+ iscsit_dec_session_usage_count(sess);
+ }
+ if (conn->conn_logout_reason == ISCSI_LOGOUT_REASON_CLOSE_CONNECTION)
+ iscsit_dec_conn_usage_count(conn);
+
+ atomic_set(&conn->conn_logout_remove, 0);
+ atomic_set(&sess->session_reinstatement, 0);
+ atomic_set(&sess->session_fall_back_to_erl0, 1);
+ }
+
+ spin_lock_bh(&sess->conn_lock);
+ list_del(&conn->conn_list);
+
+ /*
+ * Attempt to let the Initiator know this connection failed by
+ * sending an Connection Dropped Async Message on another
+ * active connection.
+ */
+ if (atomic_read(&conn->connection_recovery))
+ iscsit_build_conn_drop_async_message(conn);
+
+ spin_unlock_bh(&sess->conn_lock);
+
+ /*
+ * If connection reinstatement is being performed on this connection,
+ * up the connection reinstatement semaphore that is being blocked on
+ * in iscsit_cause_connection_reinstatement().
+ */
+ spin_lock_bh(&conn->state_lock);
+ if (atomic_read(&conn->sleep_on_conn_wait_comp)) {
+ spin_unlock_bh(&conn->state_lock);
+ complete(&conn->conn_wait_comp);
+ wait_for_completion(&conn->conn_post_wait_comp);
+ spin_lock_bh(&conn->state_lock);
+ }
+
+ /*
+ * If connection reinstatement is being performed on this connection
+ * by receiving a REMOVECONNFORRECOVERY logout request, up the
+ * connection wait rcfr semaphore that is being blocked on
+ * an iscsit_connection_reinstatement_rcfr().
+ */
+ if (atomic_read(&conn->connection_wait_rcfr)) {
+ spin_unlock_bh(&conn->state_lock);
+ complete(&conn->conn_wait_rcfr_comp);
+ wait_for_completion(&conn->conn_post_wait_comp);
+ spin_lock_bh(&conn->state_lock);
+ }
+ atomic_set(&conn->connection_reinstatement, 1);
+ spin_unlock_bh(&conn->state_lock);
+
+ /*
+ * If any other processes are accessing this connection pointer we
+ * must wait until they have completed.
+ */
+ iscsit_check_conn_usage_count(conn);
+
+ if (conn->conn_rx_hash.tfm)
+ crypto_free_hash(conn->conn_rx_hash.tfm);
+ if (conn->conn_tx_hash.tfm)
+ crypto_free_hash(conn->conn_tx_hash.tfm);
+
+ if (conn->conn_cpumask)
+ free_cpumask_var(conn->conn_cpumask);
+
+ kfree(conn->conn_ops);
+ conn->conn_ops = NULL;
+
+ if (conn->sock)
+ sock_release(conn->sock);
+
+ if (conn->conn_transport->iscsit_free_conn)
+ conn->conn_transport->iscsit_free_conn(conn);
+
+ iscsit_put_transport(conn->conn_transport);
+
+ conn->thread_set = NULL;
+
+ pr_debug("Moving to TARG_CONN_STATE_FREE.\n");
+ conn->conn_state = TARG_CONN_STATE_FREE;
+ kfree(conn);
+
+ spin_lock_bh(&sess->conn_lock);
+ atomic_dec(&sess->nconn);
+ pr_debug("Decremented iSCSI connection count to %hu from node:"
+ " %s\n", atomic_read(&sess->nconn),
+ sess->sess_ops->InitiatorName);
+ /*
+ * Make sure that if one connection fails in an non ERL=2 iSCSI
+ * Session that they all fail.
+ */
+ if ((sess->sess_ops->ErrorRecoveryLevel != 2) && !conn_logout &&
+ !atomic_read(&sess->session_logout))
+ atomic_set(&sess->session_fall_back_to_erl0, 1);
+
+ /*
+ * If this was not the last connection in the session, and we are
+ * performing session reinstatement or falling back to ERL=0, call
+ * iscsit_stop_session() without sleeping to shutdown the other
+ * active connections.
+ */
+ if (atomic_read(&sess->nconn)) {
+ if (!atomic_read(&sess->session_reinstatement) &&
+ !atomic_read(&sess->session_fall_back_to_erl0)) {
+ spin_unlock_bh(&sess->conn_lock);
+ return 0;
+ }
+ if (!atomic_read(&sess->session_stop_active)) {
+ atomic_set(&sess->session_stop_active, 1);
+ spin_unlock_bh(&sess->conn_lock);
+ iscsit_stop_session(sess, 0, 0);
+ return 0;
+ }
+ spin_unlock_bh(&sess->conn_lock);
+ return 0;
+ }
+
+ /*
+ * If this was the last connection in the session and one of the
+ * following is occurring:
+ *
+ * Session Reinstatement is not being performed, and are falling back
+ * to ERL=0 call iscsit_close_session().
+ *
+ * Session Logout was requested. iscsit_close_session() will be called
+ * elsewhere.
+ *
+ * Session Continuation is not being performed, start the Time2Retain
+ * handler and check if sleep_on_sess_wait_sem is active.
+ */
+ if (!atomic_read(&sess->session_reinstatement) &&
+ atomic_read(&sess->session_fall_back_to_erl0)) {
+ spin_unlock_bh(&sess->conn_lock);
+ target_put_session(sess->se_sess);
+
+ return 0;
+ } else if (atomic_read(&sess->session_logout)) {
+ pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
+ sess->session_state = TARG_SESS_STATE_FREE;
+ spin_unlock_bh(&sess->conn_lock);
+
+ if (atomic_read(&sess->sleep_on_sess_wait_comp))
+ complete(&sess->session_wait_comp);
+
+ return 0;
+ } else {
+ pr_debug("Moving to TARG_SESS_STATE_FAILED.\n");
+ sess->session_state = TARG_SESS_STATE_FAILED;
+
+ if (!atomic_read(&sess->session_continuation)) {
+ spin_unlock_bh(&sess->conn_lock);
+ iscsit_start_time2retain_handler(sess);
+ } else
+ spin_unlock_bh(&sess->conn_lock);
+
+ if (atomic_read(&sess->sleep_on_sess_wait_comp))
+ complete(&sess->session_wait_comp);
+
+ return 0;
+ }
+ spin_unlock_bh(&sess->conn_lock);
+
+ return 0;
+}
+
+int iscsit_close_session(struct iscsi_session *sess)
+{
+ struct iscsi_portal_group *tpg = sess->tpg;
+ struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+
+ if (atomic_read(&sess->nconn)) {
+ pr_err("%d connection(s) still exist for iSCSI session"
+ " to %s\n", atomic_read(&sess->nconn),
+ sess->sess_ops->InitiatorName);
+ BUG();
+ }
+
+ spin_lock_bh(&se_tpg->session_lock);
+ atomic_set(&sess->session_logout, 1);
+ atomic_set(&sess->session_reinstatement, 1);
+ iscsit_stop_time2retain_timer(sess);
+ spin_unlock_bh(&se_tpg->session_lock);
+
+ /*
+ * transport_deregister_session_configfs() will clear the
+ * struct se_node_acl->nacl_sess pointer now as a iscsi_np process context
+ * can be setting it again with __transport_register_session() in
+ * iscsi_post_login_handler() again after the iscsit_stop_session()
+ * completes in iscsi_np context.
+ */
+ transport_deregister_session_configfs(sess->se_sess);
+
+ /*
+ * If any other processes are accessing this session pointer we must
+ * wait until they have completed. If we are in an interrupt (the
+ * time2retain handler) and contain and active session usage count we
+ * restart the timer and exit.
+ */
+ if (!in_interrupt()) {
+ if (iscsit_check_session_usage_count(sess) == 1)
+ iscsit_stop_session(sess, 1, 1);
+ } else {
+ if (iscsit_check_session_usage_count(sess) == 2) {
+ atomic_set(&sess->session_logout, 0);
+ iscsit_start_time2retain_handler(sess);
+ return 0;
+ }
+ }
+
+ transport_deregister_session(sess->se_sess);
+
+ if (sess->sess_ops->ErrorRecoveryLevel == 2)
+ iscsit_free_connection_recovery_entires(sess);
+
+ iscsit_free_all_ooo_cmdsns(sess);
+
+ spin_lock_bh(&se_tpg->session_lock);
+ pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
+ sess->session_state = TARG_SESS_STATE_FREE;
+ pr_debug("Released iSCSI session from node: %s\n",
+ sess->sess_ops->InitiatorName);
+ tpg->nsessions--;
+ if (tpg->tpg_tiqn)
+ tpg->tpg_tiqn->tiqn_nsessions--;
+
+ pr_debug("Decremented number of active iSCSI Sessions on"
+ " iSCSI TPG: %hu to %u\n", tpg->tpgt, tpg->nsessions);
+
+ spin_lock(&sess_idr_lock);
+ idr_remove(&sess_idr, sess->session_index);
+ spin_unlock(&sess_idr_lock);
+
+ kfree(sess->sess_ops);
+ sess->sess_ops = NULL;
+ spin_unlock_bh(&se_tpg->session_lock);
+
+ kfree(sess);
+ return 0;
+}
+
+static void iscsit_logout_post_handler_closesession(
+ struct iscsi_conn *conn)
+{
+ struct iscsi_session *sess = conn->sess;
+
+ iscsi_set_thread_clear(conn, ISCSI_CLEAR_TX_THREAD);
+ iscsi_set_thread_set_signal(conn, ISCSI_SIGNAL_TX_THREAD);
+
+ atomic_set(&conn->conn_logout_remove, 0);
+ complete(&conn->conn_logout_comp);
+
+ iscsit_dec_conn_usage_count(conn);
+ iscsit_stop_session(sess, 1, 1);
+ iscsit_dec_session_usage_count(sess);
+ target_put_session(sess->se_sess);
+}
+
+static void iscsit_logout_post_handler_samecid(
+ struct iscsi_conn *conn)
+{
+ iscsi_set_thread_clear(conn, ISCSI_CLEAR_TX_THREAD);
+ iscsi_set_thread_set_signal(conn, ISCSI_SIGNAL_TX_THREAD);
+
+ atomic_set(&conn->conn_logout_remove, 0);
+ complete(&conn->conn_logout_comp);
+
+ iscsit_cause_connection_reinstatement(conn, 1);
+ iscsit_dec_conn_usage_count(conn);
+}
+
+static void iscsit_logout_post_handler_diffcid(
+ struct iscsi_conn *conn,
+ u16 cid)
+{
+ struct iscsi_conn *l_conn;
+ struct iscsi_session *sess = conn->sess;
+
+ if (!sess)
+ return;
+
+ spin_lock_bh(&sess->conn_lock);
+ list_for_each_entry(l_conn, &sess->sess_conn_list, conn_list) {
+ if (l_conn->cid == cid) {
+ iscsit_inc_conn_usage_count(l_conn);
+ break;
+ }
+ }
+ spin_unlock_bh(&sess->conn_lock);
+
+ if (!l_conn)
+ return;
+
+ if (l_conn->sock)
+ l_conn->sock->ops->shutdown(l_conn->sock, RCV_SHUTDOWN);
+
+ spin_lock_bh(&l_conn->state_lock);
+ pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
+ l_conn->conn_state = TARG_CONN_STATE_IN_LOGOUT;
+ spin_unlock_bh(&l_conn->state_lock);
+
+ iscsit_cause_connection_reinstatement(l_conn, 1);
+ iscsit_dec_conn_usage_count(l_conn);
+}
+
+/*
+ * Return of 0 causes the TX thread to restart.
+ */
+int iscsit_logout_post_handler(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn)
+{
+ int ret = 0;
+
+ switch (cmd->logout_reason) {
+ case ISCSI_LOGOUT_REASON_CLOSE_SESSION:
+ switch (cmd->logout_response) {
+ case ISCSI_LOGOUT_SUCCESS:
+ case ISCSI_LOGOUT_CLEANUP_FAILED:
+ default:
+ iscsit_logout_post_handler_closesession(conn);
+ break;
+ }
+ ret = 0;
+ break;
+ case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION:
+ if (conn->cid == cmd->logout_cid) {
+ switch (cmd->logout_response) {
+ case ISCSI_LOGOUT_SUCCESS:
+ case ISCSI_LOGOUT_CLEANUP_FAILED:
+ default:
+ iscsit_logout_post_handler_samecid(conn);
+ break;
+ }
+ ret = 0;
+ } else {
+ switch (cmd->logout_response) {
+ case ISCSI_LOGOUT_SUCCESS:
+ iscsit_logout_post_handler_diffcid(conn,
+ cmd->logout_cid);
+ break;
+ case ISCSI_LOGOUT_CID_NOT_FOUND:
+ case ISCSI_LOGOUT_CLEANUP_FAILED:
+ default:
+ break;
+ }
+ ret = 1;
+ }
+ break;
+ case ISCSI_LOGOUT_REASON_RECOVERY:
+ switch (cmd->logout_response) {
+ case ISCSI_LOGOUT_SUCCESS:
+ case ISCSI_LOGOUT_CID_NOT_FOUND:
+ case ISCSI_LOGOUT_RECOVERY_UNSUPPORTED:
+ case ISCSI_LOGOUT_CLEANUP_FAILED:
+ default:
+ break;
+ }
+ ret = 1;
+ break;
+ default:
+ break;
+
+ }
+ return ret;
+}
+EXPORT_SYMBOL(iscsit_logout_post_handler);
+
+void iscsit_fail_session(struct iscsi_session *sess)
+{
+ struct iscsi_conn *conn;
+
+ spin_lock_bh(&sess->conn_lock);
+ list_for_each_entry(conn, &sess->sess_conn_list, conn_list) {
+ pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n");
+ conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT;
+ }
+ spin_unlock_bh(&sess->conn_lock);
+
+ pr_debug("Moving to TARG_SESS_STATE_FAILED.\n");
+ sess->session_state = TARG_SESS_STATE_FAILED;
+}
+
+int iscsit_free_session(struct iscsi_session *sess)
+{
+ u16 conn_count = atomic_read(&sess->nconn);
+ struct iscsi_conn *conn, *conn_tmp = NULL;
+ int is_last;
+
+ spin_lock_bh(&sess->conn_lock);
+ atomic_set(&sess->sleep_on_sess_wait_comp, 1);
+
+ list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list,
+ conn_list) {
+ if (conn_count == 0)
+ break;
+
+ if (list_is_last(&conn->conn_list, &sess->sess_conn_list)) {
+ is_last = 1;
+ } else {
+ iscsit_inc_conn_usage_count(conn_tmp);
+ is_last = 0;
+ }
+ iscsit_inc_conn_usage_count(conn);
+
+ spin_unlock_bh(&sess->conn_lock);
+ iscsit_cause_connection_reinstatement(conn, 1);
+ spin_lock_bh(&sess->conn_lock);
+
+ iscsit_dec_conn_usage_count(conn);
+ if (is_last == 0)
+ iscsit_dec_conn_usage_count(conn_tmp);
+
+ conn_count--;
+ }
+
+ if (atomic_read(&sess->nconn)) {
+ spin_unlock_bh(&sess->conn_lock);
+ wait_for_completion(&sess->session_wait_comp);
+ } else
+ spin_unlock_bh(&sess->conn_lock);
+
+ target_put_session(sess->se_sess);
+ return 0;
+}
+
+void iscsit_stop_session(
+ struct iscsi_session *sess,
+ int session_sleep,
+ int connection_sleep)
+{
+ u16 conn_count = atomic_read(&sess->nconn);
+ struct iscsi_conn *conn, *conn_tmp = NULL;
+ int is_last;
+
+ spin_lock_bh(&sess->conn_lock);
+ if (session_sleep)
+ atomic_set(&sess->sleep_on_sess_wait_comp, 1);
+
+ if (connection_sleep) {
+ list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list,
+ conn_list) {
+ if (conn_count == 0)
+ break;
+
+ if (list_is_last(&conn->conn_list, &sess->sess_conn_list)) {
+ is_last = 1;
+ } else {
+ iscsit_inc_conn_usage_count(conn_tmp);
+ is_last = 0;
+ }
+ iscsit_inc_conn_usage_count(conn);
+
+ spin_unlock_bh(&sess->conn_lock);
+ iscsit_cause_connection_reinstatement(conn, 1);
+ spin_lock_bh(&sess->conn_lock);
+
+ iscsit_dec_conn_usage_count(conn);
+ if (is_last == 0)
+ iscsit_dec_conn_usage_count(conn_tmp);
+ conn_count--;
+ }
+ } else {
+ list_for_each_entry(conn, &sess->sess_conn_list, conn_list)
+ iscsit_cause_connection_reinstatement(conn, 0);
+ }
+
+ if (session_sleep && atomic_read(&sess->nconn)) {
+ spin_unlock_bh(&sess->conn_lock);
+ wait_for_completion(&sess->session_wait_comp);
+ } else
+ spin_unlock_bh(&sess->conn_lock);
+}
+
+int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
+{
+ struct iscsi_session *sess;
+ struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+ struct se_session *se_sess, *se_sess_tmp;
+ int session_count = 0;
+
+ spin_lock_bh(&se_tpg->session_lock);
+ if (tpg->nsessions && !force) {
+ spin_unlock_bh(&se_tpg->session_lock);
+ return -1;
+ }
+
+ list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list,
+ sess_list) {
+ sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+
+ spin_lock(&sess->conn_lock);
+ if (atomic_read(&sess->session_fall_back_to_erl0) ||
+ atomic_read(&sess->session_logout) ||
+ (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
+ spin_unlock(&sess->conn_lock);
+ continue;
+ }
+ atomic_set(&sess->session_reinstatement, 1);
+ spin_unlock(&sess->conn_lock);
+ spin_unlock_bh(&se_tpg->session_lock);
+
+ iscsit_free_session(sess);
+ spin_lock_bh(&se_tpg->session_lock);
+
+ session_count++;
+ }
+ spin_unlock_bh(&se_tpg->session_lock);
+
+ pr_debug("Released %d iSCSI Session(s) from Target Portal"
+ " Group: %hu\n", session_count, tpg->tpgt);
+ return 0;
+}
+
+MODULE_DESCRIPTION("iSCSI-Target Driver for mainline target infrastructure");
+MODULE_VERSION("4.1.x");
+MODULE_AUTHOR("nab@Linux-iSCSI.org");
+MODULE_LICENSE("GPL");
+
+module_init(iscsi_target_init_module);
+module_exit(iscsi_target_cleanup_module);
diff --git a/drivers/target/iscsi/iscsi_target.h b/drivers/target/iscsi/iscsi_target.h
new file mode 100644
index 00000000000..e936d56fb52
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target.h
@@ -0,0 +1,50 @@
+#ifndef ISCSI_TARGET_H
+#define ISCSI_TARGET_H
+
+extern struct iscsi_tiqn *iscsit_get_tiqn_for_login(unsigned char *);
+extern struct iscsi_tiqn *iscsit_get_tiqn(unsigned char *, int);
+extern void iscsit_put_tiqn_for_login(struct iscsi_tiqn *);
+extern struct iscsi_tiqn *iscsit_add_tiqn(unsigned char *);
+extern void iscsit_del_tiqn(struct iscsi_tiqn *);
+extern int iscsit_access_np(struct iscsi_np *, struct iscsi_portal_group *);
+extern void iscsit_login_kref_put(struct kref *);
+extern int iscsit_deaccess_np(struct iscsi_np *, struct iscsi_portal_group *,
+ struct iscsi_tpg_np *);
+extern bool iscsit_check_np_match(struct __kernel_sockaddr_storage *,
+ struct iscsi_np *, int);
+extern struct iscsi_np *iscsit_add_np(struct __kernel_sockaddr_storage *,
+ char *, int);
+extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *,
+ struct iscsi_portal_group *, bool);
+extern int iscsit_del_np(struct iscsi_np *);
+extern int iscsit_reject_cmd(struct iscsi_cmd *cmd, u8, unsigned char *);
+extern void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *);
+extern int iscsit_logout_closesession(struct iscsi_cmd *, struct iscsi_conn *);
+extern int iscsit_logout_closeconnection(struct iscsi_cmd *, struct iscsi_conn *);
+extern int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *, struct iscsi_conn *);
+extern int iscsit_send_async_msg(struct iscsi_conn *, u16, u8, u8);
+extern int iscsit_build_r2ts_for_cmd(struct iscsi_conn *, struct iscsi_cmd *, bool recovery);
+extern void iscsit_thread_get_cpumask(struct iscsi_conn *);
+extern int iscsi_target_tx_thread(void *);
+extern int iscsi_target_rx_thread(void *);
+extern int iscsit_close_connection(struct iscsi_conn *);
+extern int iscsit_close_session(struct iscsi_session *);
+extern void iscsit_fail_session(struct iscsi_session *);
+extern int iscsit_free_session(struct iscsi_session *);
+extern void iscsit_stop_session(struct iscsi_session *, int, int);
+extern int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *, int);
+
+extern struct iscsit_global *iscsit_global;
+extern struct target_fabric_configfs *lio_target_fabric_configfs;
+
+extern struct kmem_cache *lio_dr_cache;
+extern struct kmem_cache *lio_ooo_cache;
+extern struct kmem_cache *lio_qr_cache;
+extern struct kmem_cache *lio_r2t_cache;
+
+extern struct idr sess_idr;
+extern struct mutex auth_id_lock;
+extern spinlock_t sess_idr_lock;
+
+
+#endif /*** ISCSI_TARGET_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c
new file mode 100644
index 00000000000..ab4915c0d93
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_auth.c
@@ -0,0 +1,502 @@
+/*******************************************************************************
+ * This file houses the main functions for the iSCSI CHAP support
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <linux/scatterlist.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_nego.h"
+#include "iscsi_target_auth.h"
+
+static int chap_string_to_hex(unsigned char *dst, unsigned char *src, int len)
+{
+ int j = DIV_ROUND_UP(len, 2), rc;
+
+ rc = hex2bin(dst, src, j);
+ if (rc < 0)
+ pr_debug("CHAP string contains non hex digit symbols\n");
+
+ dst[j] = '\0';
+ return j;
+}
+
+static void chap_binaryhex_to_asciihex(char *dst, char *src, int src_len)
+{
+ int i;
+
+ for (i = 0; i < src_len; i++) {
+ sprintf(&dst[i*2], "%02x", (int) src[i] & 0xff);
+ }
+}
+
+static void chap_gen_challenge(
+ struct iscsi_conn *conn,
+ int caller,
+ char *c_str,
+ unsigned int *c_len)
+{
+ unsigned char challenge_asciihex[CHAP_CHALLENGE_LENGTH * 2 + 1];
+ struct iscsi_chap *chap = conn->auth_protocol;
+
+ memset(challenge_asciihex, 0, CHAP_CHALLENGE_LENGTH * 2 + 1);
+
+ get_random_bytes(chap->challenge, CHAP_CHALLENGE_LENGTH);
+ chap_binaryhex_to_asciihex(challenge_asciihex, chap->challenge,
+ CHAP_CHALLENGE_LENGTH);
+ /*
+ * Set CHAP_C, and copy the generated challenge into c_str.
+ */
+ *c_len += sprintf(c_str + *c_len, "CHAP_C=0x%s", challenge_asciihex);
+ *c_len += 1;
+
+ pr_debug("[%s] Sending CHAP_C=0x%s\n\n", (caller) ? "server" : "client",
+ challenge_asciihex);
+}
+
+static int chap_check_algorithm(const char *a_str)
+{
+ char *tmp, *orig, *token;
+
+ tmp = kstrdup(a_str, GFP_KERNEL);
+ if (!tmp) {
+ pr_err("Memory allocation failed for CHAP_A temporary buffer\n");
+ return CHAP_DIGEST_UNKNOWN;
+ }
+ orig = tmp;
+
+ token = strsep(&tmp, "=");
+ if (!token)
+ goto out;
+
+ if (strcmp(token, "CHAP_A")) {
+ pr_err("Unable to locate CHAP_A key\n");
+ goto out;
+ }
+ while (token) {
+ token = strsep(&tmp, ",");
+ if (!token)
+ goto out;
+
+ if (!strncmp(token, "5", 1)) {
+ pr_debug("Selected MD5 Algorithm\n");
+ kfree(orig);
+ return CHAP_DIGEST_MD5;
+ }
+ }
+out:
+ kfree(orig);
+ return CHAP_DIGEST_UNKNOWN;
+}
+
+static struct iscsi_chap *chap_server_open(
+ struct iscsi_conn *conn,
+ struct iscsi_node_auth *auth,
+ const char *a_str,
+ char *aic_str,
+ unsigned int *aic_len)
+{
+ int ret;
+ struct iscsi_chap *chap;
+
+ if (!(auth->naf_flags & NAF_USERID_SET) ||
+ !(auth->naf_flags & NAF_PASSWORD_SET)) {
+ pr_err("CHAP user or password not set for"
+ " Initiator ACL\n");
+ return NULL;
+ }
+
+ conn->auth_protocol = kzalloc(sizeof(struct iscsi_chap), GFP_KERNEL);
+ if (!conn->auth_protocol)
+ return NULL;
+
+ chap = conn->auth_protocol;
+ ret = chap_check_algorithm(a_str);
+ switch (ret) {
+ case CHAP_DIGEST_MD5:
+ pr_debug("[server] Got CHAP_A=5\n");
+ /*
+ * Send back CHAP_A set to MD5.
+ */
+ *aic_len = sprintf(aic_str, "CHAP_A=5");
+ *aic_len += 1;
+ chap->digest_type = CHAP_DIGEST_MD5;
+ pr_debug("[server] Sending CHAP_A=%d\n", chap->digest_type);
+ break;
+ case CHAP_DIGEST_UNKNOWN:
+ default:
+ pr_err("Unsupported CHAP_A value\n");
+ return NULL;
+ }
+
+ /*
+ * Set Identifier.
+ */
+ chap->id = conn->tpg->tpg_chap_id++;
+ *aic_len += sprintf(aic_str + *aic_len, "CHAP_I=%d", chap->id);
+ *aic_len += 1;
+ pr_debug("[server] Sending CHAP_I=%d\n", chap->id);
+ /*
+ * Generate Challenge.
+ */
+ chap_gen_challenge(conn, 1, aic_str, aic_len);
+
+ return chap;
+}
+
+static void chap_close(struct iscsi_conn *conn)
+{
+ kfree(conn->auth_protocol);
+ conn->auth_protocol = NULL;
+}
+
+static int chap_server_compute_md5(
+ struct iscsi_conn *conn,
+ struct iscsi_node_auth *auth,
+ char *nr_in_ptr,
+ char *nr_out_ptr,
+ unsigned int *nr_out_len)
+{
+ unsigned long id;
+ unsigned char id_as_uchar;
+ unsigned char digest[MD5_SIGNATURE_SIZE];
+ unsigned char type, response[MD5_SIGNATURE_SIZE * 2 + 2];
+ unsigned char identifier[10], *challenge = NULL;
+ unsigned char *challenge_binhex = NULL;
+ unsigned char client_digest[MD5_SIGNATURE_SIZE];
+ unsigned char server_digest[MD5_SIGNATURE_SIZE];
+ unsigned char chap_n[MAX_CHAP_N_SIZE], chap_r[MAX_RESPONSE_LENGTH];
+ size_t compare_len;
+ struct iscsi_chap *chap = conn->auth_protocol;
+ struct crypto_hash *tfm;
+ struct hash_desc desc;
+ struct scatterlist sg;
+ int auth_ret = -1, ret, challenge_len;
+
+ memset(identifier, 0, 10);
+ memset(chap_n, 0, MAX_CHAP_N_SIZE);
+ memset(chap_r, 0, MAX_RESPONSE_LENGTH);
+ memset(digest, 0, MD5_SIGNATURE_SIZE);
+ memset(response, 0, MD5_SIGNATURE_SIZE * 2 + 2);
+ memset(client_digest, 0, MD5_SIGNATURE_SIZE);
+ memset(server_digest, 0, MD5_SIGNATURE_SIZE);
+
+ challenge = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL);
+ if (!challenge) {
+ pr_err("Unable to allocate challenge buffer\n");
+ goto out;
+ }
+
+ challenge_binhex = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL);
+ if (!challenge_binhex) {
+ pr_err("Unable to allocate challenge_binhex buffer\n");
+ goto out;
+ }
+ /*
+ * Extract CHAP_N.
+ */
+ if (extract_param(nr_in_ptr, "CHAP_N", MAX_CHAP_N_SIZE, chap_n,
+ &type) < 0) {
+ pr_err("Could not find CHAP_N.\n");
+ goto out;
+ }
+ if (type == HEX) {
+ pr_err("Could not find CHAP_N.\n");
+ goto out;
+ }
+
+ /* Include the terminating NULL in the compare */
+ compare_len = strlen(auth->userid) + 1;
+ if (strncmp(chap_n, auth->userid, compare_len) != 0) {
+ pr_err("CHAP_N values do not match!\n");
+ goto out;
+ }
+ pr_debug("[server] Got CHAP_N=%s\n", chap_n);
+ /*
+ * Extract CHAP_R.
+ */
+ if (extract_param(nr_in_ptr, "CHAP_R", MAX_RESPONSE_LENGTH, chap_r,
+ &type) < 0) {
+ pr_err("Could not find CHAP_R.\n");
+ goto out;
+ }
+ if (type != HEX) {
+ pr_err("Could not find CHAP_R.\n");
+ goto out;
+ }
+
+ pr_debug("[server] Got CHAP_R=%s\n", chap_r);
+ chap_string_to_hex(client_digest, chap_r, strlen(chap_r));
+
+ tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ pr_err("Unable to allocate struct crypto_hash\n");
+ goto out;
+ }
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ ret = crypto_hash_init(&desc);
+ if (ret < 0) {
+ pr_err("crypto_hash_init() failed\n");
+ crypto_free_hash(tfm);
+ goto out;
+ }
+
+ sg_init_one(&sg, &chap->id, 1);
+ ret = crypto_hash_update(&desc, &sg, 1);
+ if (ret < 0) {
+ pr_err("crypto_hash_update() failed for id\n");
+ crypto_free_hash(tfm);
+ goto out;
+ }
+
+ sg_init_one(&sg, &auth->password, strlen(auth->password));
+ ret = crypto_hash_update(&desc, &sg, strlen(auth->password));
+ if (ret < 0) {
+ pr_err("crypto_hash_update() failed for password\n");
+ crypto_free_hash(tfm);
+ goto out;
+ }
+
+ sg_init_one(&sg, chap->challenge, CHAP_CHALLENGE_LENGTH);
+ ret = crypto_hash_update(&desc, &sg, CHAP_CHALLENGE_LENGTH);
+ if (ret < 0) {
+ pr_err("crypto_hash_update() failed for challenge\n");
+ crypto_free_hash(tfm);
+ goto out;
+ }
+
+ ret = crypto_hash_final(&desc, server_digest);
+ if (ret < 0) {
+ pr_err("crypto_hash_final() failed for server digest\n");
+ crypto_free_hash(tfm);
+ goto out;
+ }
+ crypto_free_hash(tfm);
+
+ chap_binaryhex_to_asciihex(response, server_digest, MD5_SIGNATURE_SIZE);
+ pr_debug("[server] MD5 Server Digest: %s\n", response);
+
+ if (memcmp(server_digest, client_digest, MD5_SIGNATURE_SIZE) != 0) {
+ pr_debug("[server] MD5 Digests do not match!\n\n");
+ goto out;
+ } else
+ pr_debug("[server] MD5 Digests match, CHAP connetication"
+ " successful.\n\n");
+ /*
+ * One way authentication has succeeded, return now if mutual
+ * authentication is not enabled.
+ */
+ if (!auth->authenticate_target) {
+ kfree(challenge);
+ kfree(challenge_binhex);
+ return 0;
+ }
+ /*
+ * Get CHAP_I.
+ */
+ if (extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type) < 0) {
+ pr_err("Could not find CHAP_I.\n");
+ goto out;
+ }
+
+ if (type == HEX)
+ ret = kstrtoul(&identifier[2], 0, &id);
+ else
+ ret = kstrtoul(identifier, 0, &id);
+
+ if (ret < 0) {
+ pr_err("kstrtoul() failed for CHAP identifier: %d\n", ret);
+ goto out;
+ }
+ if (id > 255) {
+ pr_err("chap identifier: %lu greater than 255\n", id);
+ goto out;
+ }
+ /*
+ * RFC 1994 says Identifier is no more than octet (8 bits).
+ */
+ pr_debug("[server] Got CHAP_I=%lu\n", id);
+ /*
+ * Get CHAP_C.
+ */
+ if (extract_param(nr_in_ptr, "CHAP_C", CHAP_CHALLENGE_STR_LEN,
+ challenge, &type) < 0) {
+ pr_err("Could not find CHAP_C.\n");
+ goto out;
+ }
+
+ if (type != HEX) {
+ pr_err("Could not find CHAP_C.\n");
+ goto out;
+ }
+ pr_debug("[server] Got CHAP_C=%s\n", challenge);
+ challenge_len = chap_string_to_hex(challenge_binhex, challenge,
+ strlen(challenge));
+ if (!challenge_len) {
+ pr_err("Unable to convert incoming challenge\n");
+ goto out;
+ }
+ if (challenge_len > 1024) {
+ pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n");
+ goto out;
+ }
+ /*
+ * During mutual authentication, the CHAP_C generated by the
+ * initiator must not match the original CHAP_C generated by
+ * the target.
+ */
+ if (!memcmp(challenge_binhex, chap->challenge, CHAP_CHALLENGE_LENGTH)) {
+ pr_err("initiator CHAP_C matches target CHAP_C, failing"
+ " login attempt\n");
+ goto out;
+ }
+ /*
+ * Generate CHAP_N and CHAP_R for mutual authentication.
+ */
+ tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ pr_err("Unable to allocate struct crypto_hash\n");
+ goto out;
+ }
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ ret = crypto_hash_init(&desc);
+ if (ret < 0) {
+ pr_err("crypto_hash_init() failed\n");
+ crypto_free_hash(tfm);
+ goto out;
+ }
+
+ /* To handle both endiannesses */
+ id_as_uchar = id;
+ sg_init_one(&sg, &id_as_uchar, 1);
+ ret = crypto_hash_update(&desc, &sg, 1);
+ if (ret < 0) {
+ pr_err("crypto_hash_update() failed for id\n");
+ crypto_free_hash(tfm);
+ goto out;
+ }
+
+ sg_init_one(&sg, auth->password_mutual,
+ strlen(auth->password_mutual));
+ ret = crypto_hash_update(&desc, &sg, strlen(auth->password_mutual));
+ if (ret < 0) {
+ pr_err("crypto_hash_update() failed for"
+ " password_mutual\n");
+ crypto_free_hash(tfm);
+ goto out;
+ }
+ /*
+ * Convert received challenge to binary hex.
+ */
+ sg_init_one(&sg, challenge_binhex, challenge_len);
+ ret = crypto_hash_update(&desc, &sg, challenge_len);
+ if (ret < 0) {
+ pr_err("crypto_hash_update() failed for ma challenge\n");
+ crypto_free_hash(tfm);
+ goto out;
+ }
+
+ ret = crypto_hash_final(&desc, digest);
+ if (ret < 0) {
+ pr_err("crypto_hash_final() failed for ma digest\n");
+ crypto_free_hash(tfm);
+ goto out;
+ }
+ crypto_free_hash(tfm);
+ /*
+ * Generate CHAP_N and CHAP_R.
+ */
+ *nr_out_len = sprintf(nr_out_ptr, "CHAP_N=%s", auth->userid_mutual);
+ *nr_out_len += 1;
+ pr_debug("[server] Sending CHAP_N=%s\n", auth->userid_mutual);
+ /*
+ * Convert response from binary hex to ascii hext.
+ */
+ chap_binaryhex_to_asciihex(response, digest, MD5_SIGNATURE_SIZE);
+ *nr_out_len += sprintf(nr_out_ptr + *nr_out_len, "CHAP_R=0x%s",
+ response);
+ *nr_out_len += 1;
+ pr_debug("[server] Sending CHAP_R=0x%s\n", response);
+ auth_ret = 0;
+out:
+ kfree(challenge);
+ kfree(challenge_binhex);
+ return auth_ret;
+}
+
+static int chap_got_response(
+ struct iscsi_conn *conn,
+ struct iscsi_node_auth *auth,
+ char *nr_in_ptr,
+ char *nr_out_ptr,
+ unsigned int *nr_out_len)
+{
+ struct iscsi_chap *chap = conn->auth_protocol;
+
+ switch (chap->digest_type) {
+ case CHAP_DIGEST_MD5:
+ if (chap_server_compute_md5(conn, auth, nr_in_ptr,
+ nr_out_ptr, nr_out_len) < 0)
+ return -1;
+ return 0;
+ default:
+ pr_err("Unknown CHAP digest type %d!\n",
+ chap->digest_type);
+ return -1;
+ }
+}
+
+u32 chap_main_loop(
+ struct iscsi_conn *conn,
+ struct iscsi_node_auth *auth,
+ char *in_text,
+ char *out_text,
+ int *in_len,
+ int *out_len)
+{
+ struct iscsi_chap *chap = conn->auth_protocol;
+
+ if (!chap) {
+ chap = chap_server_open(conn, auth, in_text, out_text, out_len);
+ if (!chap)
+ return 2;
+ chap->chap_state = CHAP_STAGE_SERVER_AIC;
+ return 0;
+ } else if (chap->chap_state == CHAP_STAGE_SERVER_AIC) {
+ convert_null_to_semi(in_text, *in_len);
+ if (chap_got_response(conn, auth, in_text, out_text,
+ out_len) < 0) {
+ chap_close(conn);
+ return 2;
+ }
+ if (auth->authenticate_target)
+ chap->chap_state = CHAP_STAGE_SERVER_NR;
+ else
+ *out_len = 0;
+ chap_close(conn);
+ return 1;
+ }
+
+ return 2;
+}
diff --git a/drivers/target/iscsi/iscsi_target_auth.h b/drivers/target/iscsi/iscsi_target_auth.h
new file mode 100644
index 00000000000..d22f7b96a06
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_auth.h
@@ -0,0 +1,32 @@
+#ifndef _ISCSI_CHAP_H_
+#define _ISCSI_CHAP_H_
+
+#define CHAP_DIGEST_UNKNOWN 0
+#define CHAP_DIGEST_MD5 5
+#define CHAP_DIGEST_SHA 6
+
+#define CHAP_CHALLENGE_LENGTH 16
+#define CHAP_CHALLENGE_STR_LEN 4096
+#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 */
+#define MAX_CHAP_N_SIZE 512
+
+#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */
+
+#define CHAP_STAGE_CLIENT_A 1
+#define CHAP_STAGE_SERVER_AIC 2
+#define CHAP_STAGE_CLIENT_NR 3
+#define CHAP_STAGE_CLIENT_NRIC 4
+#define CHAP_STAGE_SERVER_NR 5
+
+extern u32 chap_main_loop(struct iscsi_conn *, struct iscsi_node_auth *, char *, char *,
+ int *, int *);
+
+struct iscsi_chap {
+ unsigned char digest_type;
+ unsigned char id;
+ unsigned char challenge[CHAP_CHALLENGE_LENGTH];
+ unsigned int authenticate_target;
+ unsigned int chap_state;
+} ____cacheline_aligned;
+
+#endif /*** _ISCSI_CHAP_H_ ***/
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c
new file mode 100644
index 00000000000..ae03f3e5de1
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_configfs.c
@@ -0,0 +1,2074 @@
+/*******************************************************************************
+ * This file contains the configfs implementation for iSCSI Target mode
+ * from the LIO-Target Project.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ****************************************************************************/
+
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/export.h>
+#include <linux/inet.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+#include <target/iscsi/iscsi_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_parameters.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_nodeattrib.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+#include "iscsi_target_stat.h"
+#include "iscsi_target_configfs.h"
+
+struct target_fabric_configfs *lio_target_fabric_configfs;
+
+struct lio_target_configfs_attribute {
+ struct configfs_attribute attr;
+ ssize_t (*show)(void *, char *);
+ ssize_t (*store)(void *, const char *, size_t);
+};
+
+/* Start items for lio_target_portal_cit */
+
+static ssize_t lio_target_np_show_sctp(
+ struct se_tpg_np *se_tpg_np,
+ char *page)
+{
+ struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np,
+ struct iscsi_tpg_np, se_tpg_np);
+ struct iscsi_tpg_np *tpg_np_sctp;
+ ssize_t rb;
+
+ tpg_np_sctp = iscsit_tpg_locate_child_np(tpg_np, ISCSI_SCTP_TCP);
+ if (tpg_np_sctp)
+ rb = sprintf(page, "1\n");
+ else
+ rb = sprintf(page, "0\n");
+
+ return rb;
+}
+
+static ssize_t lio_target_np_store_sctp(
+ struct se_tpg_np *se_tpg_np,
+ const char *page,
+ size_t count)
+{
+ struct iscsi_np *np;
+ struct iscsi_portal_group *tpg;
+ struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np,
+ struct iscsi_tpg_np, se_tpg_np);
+ struct iscsi_tpg_np *tpg_np_sctp = NULL;
+ u32 op;
+ int ret;
+
+ ret = kstrtou32(page, 0, &op);
+ if (ret)
+ return ret;
+ if ((op != 1) && (op != 0)) {
+ pr_err("Illegal value for tpg_enable: %u\n", op);
+ return -EINVAL;
+ }
+ np = tpg_np->tpg_np;
+ if (!np) {
+ pr_err("Unable to locate struct iscsi_np from"
+ " struct iscsi_tpg_np\n");
+ return -EINVAL;
+ }
+
+ tpg = tpg_np->tpg;
+ if (iscsit_get_tpg(tpg) < 0)
+ return -EINVAL;
+
+ if (op) {
+ /*
+ * Use existing np->np_sockaddr for SCTP network portal reference
+ */
+ tpg_np_sctp = iscsit_tpg_add_network_portal(tpg, &np->np_sockaddr,
+ np->np_ip, tpg_np, ISCSI_SCTP_TCP);
+ if (!tpg_np_sctp || IS_ERR(tpg_np_sctp))
+ goto out;
+ } else {
+ tpg_np_sctp = iscsit_tpg_locate_child_np(tpg_np, ISCSI_SCTP_TCP);
+ if (!tpg_np_sctp)
+ goto out;
+
+ ret = iscsit_tpg_del_network_portal(tpg, tpg_np_sctp);
+ if (ret < 0)
+ goto out;
+ }
+
+ iscsit_put_tpg(tpg);
+ return count;
+out:
+ iscsit_put_tpg(tpg);
+ return -EINVAL;
+}
+
+TF_NP_BASE_ATTR(lio_target, sctp, S_IRUGO | S_IWUSR);
+
+static ssize_t lio_target_np_show_iser(
+ struct se_tpg_np *se_tpg_np,
+ char *page)
+{
+ struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np,
+ struct iscsi_tpg_np, se_tpg_np);
+ struct iscsi_tpg_np *tpg_np_iser;
+ ssize_t rb;
+
+ tpg_np_iser = iscsit_tpg_locate_child_np(tpg_np, ISCSI_INFINIBAND);
+ if (tpg_np_iser)
+ rb = sprintf(page, "1\n");
+ else
+ rb = sprintf(page, "0\n");
+
+ return rb;
+}
+
+static ssize_t lio_target_np_store_iser(
+ struct se_tpg_np *se_tpg_np,
+ const char *page,
+ size_t count)
+{
+ struct iscsi_np *np;
+ struct iscsi_portal_group *tpg;
+ struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np,
+ struct iscsi_tpg_np, se_tpg_np);
+ struct iscsi_tpg_np *tpg_np_iser = NULL;
+ char *endptr;
+ u32 op;
+ int rc = 0;
+
+ op = simple_strtoul(page, &endptr, 0);
+ if ((op != 1) && (op != 0)) {
+ pr_err("Illegal value for tpg_enable: %u\n", op);
+ return -EINVAL;
+ }
+ np = tpg_np->tpg_np;
+ if (!np) {
+ pr_err("Unable to locate struct iscsi_np from"
+ " struct iscsi_tpg_np\n");
+ return -EINVAL;
+ }
+
+ tpg = tpg_np->tpg;
+ if (iscsit_get_tpg(tpg) < 0)
+ return -EINVAL;
+
+ if (op) {
+ rc = request_module("ib_isert");
+ if (rc != 0) {
+ pr_warn("Unable to request_module for ib_isert\n");
+ rc = 0;
+ }
+
+ tpg_np_iser = iscsit_tpg_add_network_portal(tpg, &np->np_sockaddr,
+ np->np_ip, tpg_np, ISCSI_INFINIBAND);
+ if (IS_ERR(tpg_np_iser)) {
+ rc = PTR_ERR(tpg_np_iser);
+ goto out;
+ }
+ } else {
+ tpg_np_iser = iscsit_tpg_locate_child_np(tpg_np, ISCSI_INFINIBAND);
+ if (tpg_np_iser) {
+ rc = iscsit_tpg_del_network_portal(tpg, tpg_np_iser);
+ if (rc < 0)
+ goto out;
+ }
+ }
+
+ iscsit_put_tpg(tpg);
+ return count;
+out:
+ iscsit_put_tpg(tpg);
+ return rc;
+}
+
+TF_NP_BASE_ATTR(lio_target, iser, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_portal_attrs[] = {
+ &lio_target_np_sctp.attr,
+ &lio_target_np_iser.attr,
+ NULL,
+};
+
+/* Stop items for lio_target_portal_cit */
+
+/* Start items for lio_target_np_cit */
+
+#define MAX_PORTAL_LEN 256
+
+static struct se_tpg_np *lio_target_call_addnptotpg(
+ struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
+{
+ struct iscsi_portal_group *tpg;
+ struct iscsi_tpg_np *tpg_np;
+ char *str, *str2, *ip_str, *port_str;
+ struct __kernel_sockaddr_storage sockaddr;
+ struct sockaddr_in *sock_in;
+ struct sockaddr_in6 *sock_in6;
+ unsigned long port;
+ int ret;
+ char buf[MAX_PORTAL_LEN + 1];
+
+ if (strlen(name) > MAX_PORTAL_LEN) {
+ pr_err("strlen(name): %d exceeds MAX_PORTAL_LEN: %d\n",
+ (int)strlen(name), MAX_PORTAL_LEN);
+ return ERR_PTR(-EOVERFLOW);
+ }
+ memset(buf, 0, MAX_PORTAL_LEN + 1);
+ snprintf(buf, MAX_PORTAL_LEN + 1, "%s", name);
+
+ memset(&sockaddr, 0, sizeof(struct __kernel_sockaddr_storage));
+
+ str = strstr(buf, "[");
+ if (str) {
+ const char *end;
+
+ str2 = strstr(str, "]");
+ if (!str2) {
+ pr_err("Unable to locate trailing \"]\""
+ " in IPv6 iSCSI network portal address\n");
+ return ERR_PTR(-EINVAL);
+ }
+ str++; /* Skip over leading "[" */
+ *str2 = '\0'; /* Terminate the IPv6 address */
+ str2++; /* Skip over the "]" */
+ port_str = strstr(str2, ":");
+ if (!port_str) {
+ pr_err("Unable to locate \":port\""
+ " in IPv6 iSCSI network portal address\n");
+ return ERR_PTR(-EINVAL);
+ }
+ *port_str = '\0'; /* Terminate string for IP */
+ port_str++; /* Skip over ":" */
+
+ ret = kstrtoul(port_str, 0, &port);
+ if (ret < 0) {
+ pr_err("kstrtoul() failed for port_str: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+ sock_in6 = (struct sockaddr_in6 *)&sockaddr;
+ sock_in6->sin6_family = AF_INET6;
+ sock_in6->sin6_port = htons((unsigned short)port);
+ ret = in6_pton(str, IPV6_ADDRESS_SPACE,
+ (void *)&sock_in6->sin6_addr.in6_u, -1, &end);
+ if (ret <= 0) {
+ pr_err("in6_pton returned: %d\n", ret);
+ return ERR_PTR(-EINVAL);
+ }
+ } else {
+ str = ip_str = &buf[0];
+ port_str = strstr(ip_str, ":");
+ if (!port_str) {
+ pr_err("Unable to locate \":port\""
+ " in IPv4 iSCSI network portal address\n");
+ return ERR_PTR(-EINVAL);
+ }
+ *port_str = '\0'; /* Terminate string for IP */
+ port_str++; /* Skip over ":" */
+
+ ret = kstrtoul(port_str, 0, &port);
+ if (ret < 0) {
+ pr_err("kstrtoul() failed for port_str: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+ sock_in = (struct sockaddr_in *)&sockaddr;
+ sock_in->sin_family = AF_INET;
+ sock_in->sin_port = htons((unsigned short)port);
+ sock_in->sin_addr.s_addr = in_aton(ip_str);
+ }
+ tpg = container_of(se_tpg, struct iscsi_portal_group, tpg_se_tpg);
+ ret = iscsit_get_tpg(tpg);
+ if (ret < 0)
+ return ERR_PTR(-EINVAL);
+
+ pr_debug("LIO_Target_ConfigFS: REGISTER -> %s TPGT: %hu"
+ " PORTAL: %s\n",
+ config_item_name(&se_tpg->se_tpg_wwn->wwn_group.cg_item),
+ tpg->tpgt, name);
+ /*
+ * Assume ISCSI_TCP by default. Other network portals for other
+ * iSCSI fabrics:
+ *
+ * Traditional iSCSI over SCTP (initial support)
+ * iSER/TCP (TODO, hardware available)
+ * iSER/SCTP (TODO, software emulation with osc-iwarp)
+ * iSER/IB (TODO, hardware available)
+ *
+ * can be enabled with attributes under
+ * sys/kernel/config/iscsi/$IQN/$TPG/np/$IP:$PORT/
+ *
+ */
+ tpg_np = iscsit_tpg_add_network_portal(tpg, &sockaddr, str, NULL,
+ ISCSI_TCP);
+ if (IS_ERR(tpg_np)) {
+ iscsit_put_tpg(tpg);
+ return ERR_CAST(tpg_np);
+ }
+ pr_debug("LIO_Target_ConfigFS: addnptotpg done!\n");
+
+ iscsit_put_tpg(tpg);
+ return &tpg_np->se_tpg_np;
+}
+
+static void lio_target_call_delnpfromtpg(
+ struct se_tpg_np *se_tpg_np)
+{
+ struct iscsi_portal_group *tpg;
+ struct iscsi_tpg_np *tpg_np;
+ struct se_portal_group *se_tpg;
+ int ret;
+
+ tpg_np = container_of(se_tpg_np, struct iscsi_tpg_np, se_tpg_np);
+ tpg = tpg_np->tpg;
+ ret = iscsit_get_tpg(tpg);
+ if (ret < 0)
+ return;
+
+ se_tpg = &tpg->tpg_se_tpg;
+ pr_debug("LIO_Target_ConfigFS: DEREGISTER -> %s TPGT: %hu"
+ " PORTAL: %s:%hu\n", config_item_name(&se_tpg->se_tpg_wwn->wwn_group.cg_item),
+ tpg->tpgt, tpg_np->tpg_np->np_ip, tpg_np->tpg_np->np_port);
+
+ ret = iscsit_tpg_del_network_portal(tpg, tpg_np);
+ if (ret < 0)
+ goto out;
+
+ pr_debug("LIO_Target_ConfigFS: delnpfromtpg done!\n");
+out:
+ iscsit_put_tpg(tpg);
+}
+
+/* End items for lio_target_np_cit */
+
+/* Start items for lio_target_nacl_attrib_cit */
+
+#define DEF_NACL_ATTRIB(name) \
+static ssize_t iscsi_nacl_attrib_show_##name( \
+ struct se_node_acl *se_nacl, \
+ char *page) \
+{ \
+ struct iscsi_node_acl *nacl = container_of(se_nacl, struct iscsi_node_acl, \
+ se_node_acl); \
+ \
+ return sprintf(page, "%u\n", nacl->node_attrib.name); \
+} \
+ \
+static ssize_t iscsi_nacl_attrib_store_##name( \
+ struct se_node_acl *se_nacl, \
+ const char *page, \
+ size_t count) \
+{ \
+ struct iscsi_node_acl *nacl = container_of(se_nacl, struct iscsi_node_acl, \
+ se_node_acl); \
+ u32 val; \
+ int ret; \
+ \
+ ret = kstrtou32(page, 0, &val); \
+ if (ret) \
+ return ret; \
+ ret = iscsit_na_##name(nacl, val); \
+ if (ret < 0) \
+ return ret; \
+ \
+ return count; \
+}
+
+#define NACL_ATTR(_name, _mode) TF_NACL_ATTRIB_ATTR(iscsi, _name, _mode);
+/*
+ * Define iscsi_node_attrib_s_dataout_timeout
+ */
+DEF_NACL_ATTRIB(dataout_timeout);
+NACL_ATTR(dataout_timeout, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_dataout_timeout_retries
+ */
+DEF_NACL_ATTRIB(dataout_timeout_retries);
+NACL_ATTR(dataout_timeout_retries, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_default_erl
+ */
+DEF_NACL_ATTRIB(default_erl);
+NACL_ATTR(default_erl, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_nopin_timeout
+ */
+DEF_NACL_ATTRIB(nopin_timeout);
+NACL_ATTR(nopin_timeout, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_nopin_response_timeout
+ */
+DEF_NACL_ATTRIB(nopin_response_timeout);
+NACL_ATTR(nopin_response_timeout, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_random_datain_pdu_offsets
+ */
+DEF_NACL_ATTRIB(random_datain_pdu_offsets);
+NACL_ATTR(random_datain_pdu_offsets, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_random_datain_seq_offsets
+ */
+DEF_NACL_ATTRIB(random_datain_seq_offsets);
+NACL_ATTR(random_datain_seq_offsets, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_node_attrib_s_random_r2t_offsets
+ */
+DEF_NACL_ATTRIB(random_r2t_offsets);
+NACL_ATTR(random_r2t_offsets, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_nacl_attrib_attrs[] = {
+ &iscsi_nacl_attrib_dataout_timeout.attr,
+ &iscsi_nacl_attrib_dataout_timeout_retries.attr,
+ &iscsi_nacl_attrib_default_erl.attr,
+ &iscsi_nacl_attrib_nopin_timeout.attr,
+ &iscsi_nacl_attrib_nopin_response_timeout.attr,
+ &iscsi_nacl_attrib_random_datain_pdu_offsets.attr,
+ &iscsi_nacl_attrib_random_datain_seq_offsets.attr,
+ &iscsi_nacl_attrib_random_r2t_offsets.attr,
+ NULL,
+};
+
+/* End items for lio_target_nacl_attrib_cit */
+
+/* Start items for lio_target_nacl_auth_cit */
+
+#define __DEF_NACL_AUTH_STR(prefix, name, flags) \
+static ssize_t __iscsi_##prefix##_show_##name( \
+ struct iscsi_node_acl *nacl, \
+ char *page) \
+{ \
+ struct iscsi_node_auth *auth = &nacl->node_auth; \
+ \
+ if (!capable(CAP_SYS_ADMIN)) \
+ return -EPERM; \
+ return snprintf(page, PAGE_SIZE, "%s\n", auth->name); \
+} \
+ \
+static ssize_t __iscsi_##prefix##_store_##name( \
+ struct iscsi_node_acl *nacl, \
+ const char *page, \
+ size_t count) \
+{ \
+ struct iscsi_node_auth *auth = &nacl->node_auth; \
+ \
+ if (!capable(CAP_SYS_ADMIN)) \
+ return -EPERM; \
+ if (count >= sizeof(auth->name)) \
+ return -EINVAL; \
+ snprintf(auth->name, sizeof(auth->name), "%s", page); \
+ if (!strncmp("NULL", auth->name, 4)) \
+ auth->naf_flags &= ~flags; \
+ else \
+ auth->naf_flags |= flags; \
+ \
+ if ((auth->naf_flags & NAF_USERID_IN_SET) && \
+ (auth->naf_flags & NAF_PASSWORD_IN_SET)) \
+ auth->authenticate_target = 1; \
+ else \
+ auth->authenticate_target = 0; \
+ \
+ return count; \
+}
+
+#define __DEF_NACL_AUTH_INT(prefix, name) \
+static ssize_t __iscsi_##prefix##_show_##name( \
+ struct iscsi_node_acl *nacl, \
+ char *page) \
+{ \
+ struct iscsi_node_auth *auth = &nacl->node_auth; \
+ \
+ if (!capable(CAP_SYS_ADMIN)) \
+ return -EPERM; \
+ \
+ return snprintf(page, PAGE_SIZE, "%d\n", auth->name); \
+}
+
+#define DEF_NACL_AUTH_STR(name, flags) \
+ __DEF_NACL_AUTH_STR(nacl_auth, name, flags) \
+static ssize_t iscsi_nacl_auth_show_##name( \
+ struct se_node_acl *nacl, \
+ char *page) \
+{ \
+ return __iscsi_nacl_auth_show_##name(container_of(nacl, \
+ struct iscsi_node_acl, se_node_acl), page); \
+} \
+static ssize_t iscsi_nacl_auth_store_##name( \
+ struct se_node_acl *nacl, \
+ const char *page, \
+ size_t count) \
+{ \
+ return __iscsi_nacl_auth_store_##name(container_of(nacl, \
+ struct iscsi_node_acl, se_node_acl), page, count); \
+}
+
+#define DEF_NACL_AUTH_INT(name) \
+ __DEF_NACL_AUTH_INT(nacl_auth, name) \
+static ssize_t iscsi_nacl_auth_show_##name( \
+ struct se_node_acl *nacl, \
+ char *page) \
+{ \
+ return __iscsi_nacl_auth_show_##name(container_of(nacl, \
+ struct iscsi_node_acl, se_node_acl), page); \
+}
+
+#define AUTH_ATTR(_name, _mode) TF_NACL_AUTH_ATTR(iscsi, _name, _mode);
+#define AUTH_ATTR_RO(_name) TF_NACL_AUTH_ATTR_RO(iscsi, _name);
+
+/*
+ * One-way authentication userid
+ */
+DEF_NACL_AUTH_STR(userid, NAF_USERID_SET);
+AUTH_ATTR(userid, S_IRUGO | S_IWUSR);
+/*
+ * One-way authentication password
+ */
+DEF_NACL_AUTH_STR(password, NAF_PASSWORD_SET);
+AUTH_ATTR(password, S_IRUGO | S_IWUSR);
+/*
+ * Enforce mutual authentication
+ */
+DEF_NACL_AUTH_INT(authenticate_target);
+AUTH_ATTR_RO(authenticate_target);
+/*
+ * Mutual authentication userid
+ */
+DEF_NACL_AUTH_STR(userid_mutual, NAF_USERID_IN_SET);
+AUTH_ATTR(userid_mutual, S_IRUGO | S_IWUSR);
+/*
+ * Mutual authentication password
+ */
+DEF_NACL_AUTH_STR(password_mutual, NAF_PASSWORD_IN_SET);
+AUTH_ATTR(password_mutual, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_nacl_auth_attrs[] = {
+ &iscsi_nacl_auth_userid.attr,
+ &iscsi_nacl_auth_password.attr,
+ &iscsi_nacl_auth_authenticate_target.attr,
+ &iscsi_nacl_auth_userid_mutual.attr,
+ &iscsi_nacl_auth_password_mutual.attr,
+ NULL,
+};
+
+/* End items for lio_target_nacl_auth_cit */
+
+/* Start items for lio_target_nacl_param_cit */
+
+#define DEF_NACL_PARAM(name) \
+static ssize_t iscsi_nacl_param_show_##name( \
+ struct se_node_acl *se_nacl, \
+ char *page) \
+{ \
+ struct iscsi_session *sess; \
+ struct se_session *se_sess; \
+ ssize_t rb; \
+ \
+ spin_lock_bh(&se_nacl->nacl_sess_lock); \
+ se_sess = se_nacl->nacl_sess; \
+ if (!se_sess) { \
+ rb = snprintf(page, PAGE_SIZE, \
+ "No Active iSCSI Session\n"); \
+ } else { \
+ sess = se_sess->fabric_sess_ptr; \
+ rb = snprintf(page, PAGE_SIZE, "%u\n", \
+ (u32)sess->sess_ops->name); \
+ } \
+ spin_unlock_bh(&se_nacl->nacl_sess_lock); \
+ \
+ return rb; \
+}
+
+#define NACL_PARAM_ATTR(_name) TF_NACL_PARAM_ATTR_RO(iscsi, _name);
+
+DEF_NACL_PARAM(MaxConnections);
+NACL_PARAM_ATTR(MaxConnections);
+
+DEF_NACL_PARAM(InitialR2T);
+NACL_PARAM_ATTR(InitialR2T);
+
+DEF_NACL_PARAM(ImmediateData);
+NACL_PARAM_ATTR(ImmediateData);
+
+DEF_NACL_PARAM(MaxBurstLength);
+NACL_PARAM_ATTR(MaxBurstLength);
+
+DEF_NACL_PARAM(FirstBurstLength);
+NACL_PARAM_ATTR(FirstBurstLength);
+
+DEF_NACL_PARAM(DefaultTime2Wait);
+NACL_PARAM_ATTR(DefaultTime2Wait);
+
+DEF_NACL_PARAM(DefaultTime2Retain);
+NACL_PARAM_ATTR(DefaultTime2Retain);
+
+DEF_NACL_PARAM(MaxOutstandingR2T);
+NACL_PARAM_ATTR(MaxOutstandingR2T);
+
+DEF_NACL_PARAM(DataPDUInOrder);
+NACL_PARAM_ATTR(DataPDUInOrder);
+
+DEF_NACL_PARAM(DataSequenceInOrder);
+NACL_PARAM_ATTR(DataSequenceInOrder);
+
+DEF_NACL_PARAM(ErrorRecoveryLevel);
+NACL_PARAM_ATTR(ErrorRecoveryLevel);
+
+static struct configfs_attribute *lio_target_nacl_param_attrs[] = {
+ &iscsi_nacl_param_MaxConnections.attr,
+ &iscsi_nacl_param_InitialR2T.attr,
+ &iscsi_nacl_param_ImmediateData.attr,
+ &iscsi_nacl_param_MaxBurstLength.attr,
+ &iscsi_nacl_param_FirstBurstLength.attr,
+ &iscsi_nacl_param_DefaultTime2Wait.attr,
+ &iscsi_nacl_param_DefaultTime2Retain.attr,
+ &iscsi_nacl_param_MaxOutstandingR2T.attr,
+ &iscsi_nacl_param_DataPDUInOrder.attr,
+ &iscsi_nacl_param_DataSequenceInOrder.attr,
+ &iscsi_nacl_param_ErrorRecoveryLevel.attr,
+ NULL,
+};
+
+/* End items for lio_target_nacl_param_cit */
+
+/* Start items for lio_target_acl_cit */
+
+static ssize_t lio_target_nacl_show_info(
+ struct se_node_acl *se_nacl,
+ char *page)
+{
+ struct iscsi_session *sess;
+ struct iscsi_conn *conn;
+ struct se_session *se_sess;
+ ssize_t rb = 0;
+
+ spin_lock_bh(&se_nacl->nacl_sess_lock);
+ se_sess = se_nacl->nacl_sess;
+ if (!se_sess) {
+ rb += sprintf(page+rb, "No active iSCSI Session for Initiator"
+ " Endpoint: %s\n", se_nacl->initiatorname);
+ } else {
+ sess = se_sess->fabric_sess_ptr;
+
+ if (sess->sess_ops->InitiatorName)
+ rb += sprintf(page+rb, "InitiatorName: %s\n",
+ sess->sess_ops->InitiatorName);
+ if (sess->sess_ops->InitiatorAlias)
+ rb += sprintf(page+rb, "InitiatorAlias: %s\n",
+ sess->sess_ops->InitiatorAlias);
+
+ rb += sprintf(page+rb, "LIO Session ID: %u "
+ "ISID: 0x%02x %02x %02x %02x %02x %02x "
+ "TSIH: %hu ", sess->sid,
+ sess->isid[0], sess->isid[1], sess->isid[2],
+ sess->isid[3], sess->isid[4], sess->isid[5],
+ sess->tsih);
+ rb += sprintf(page+rb, "SessionType: %s\n",
+ (sess->sess_ops->SessionType) ?
+ "Discovery" : "Normal");
+ rb += sprintf(page+rb, "Session State: ");
+ switch (sess->session_state) {
+ case TARG_SESS_STATE_FREE:
+ rb += sprintf(page+rb, "TARG_SESS_FREE\n");
+ break;
+ case TARG_SESS_STATE_ACTIVE:
+ rb += sprintf(page+rb, "TARG_SESS_STATE_ACTIVE\n");
+ break;
+ case TARG_SESS_STATE_LOGGED_IN:
+ rb += sprintf(page+rb, "TARG_SESS_STATE_LOGGED_IN\n");
+ break;
+ case TARG_SESS_STATE_FAILED:
+ rb += sprintf(page+rb, "TARG_SESS_STATE_FAILED\n");
+ break;
+ case TARG_SESS_STATE_IN_CONTINUE:
+ rb += sprintf(page+rb, "TARG_SESS_STATE_IN_CONTINUE\n");
+ break;
+ default:
+ rb += sprintf(page+rb, "ERROR: Unknown Session"
+ " State!\n");
+ break;
+ }
+
+ rb += sprintf(page+rb, "---------------------[iSCSI Session"
+ " Values]-----------------------\n");
+ rb += sprintf(page+rb, " CmdSN/WR : CmdSN/WC : ExpCmdSN"
+ " : MaxCmdSN : ITT : TTT\n");
+ rb += sprintf(page+rb, " 0x%08x 0x%08x 0x%08x 0x%08x"
+ " 0x%08x 0x%08x\n",
+ sess->cmdsn_window,
+ (sess->max_cmd_sn - sess->exp_cmd_sn) + 1,
+ sess->exp_cmd_sn, sess->max_cmd_sn,
+ sess->init_task_tag, sess->targ_xfer_tag);
+ rb += sprintf(page+rb, "----------------------[iSCSI"
+ " Connections]-------------------------\n");
+
+ spin_lock(&sess->conn_lock);
+ list_for_each_entry(conn, &sess->sess_conn_list, conn_list) {
+ rb += sprintf(page+rb, "CID: %hu Connection"
+ " State: ", conn->cid);
+ switch (conn->conn_state) {
+ case TARG_CONN_STATE_FREE:
+ rb += sprintf(page+rb,
+ "TARG_CONN_STATE_FREE\n");
+ break;
+ case TARG_CONN_STATE_XPT_UP:
+ rb += sprintf(page+rb,
+ "TARG_CONN_STATE_XPT_UP\n");
+ break;
+ case TARG_CONN_STATE_IN_LOGIN:
+ rb += sprintf(page+rb,
+ "TARG_CONN_STATE_IN_LOGIN\n");
+ break;
+ case TARG_CONN_STATE_LOGGED_IN:
+ rb += sprintf(page+rb,
+ "TARG_CONN_STATE_LOGGED_IN\n");
+ break;
+ case TARG_CONN_STATE_IN_LOGOUT:
+ rb += sprintf(page+rb,
+ "TARG_CONN_STATE_IN_LOGOUT\n");
+ break;
+ case TARG_CONN_STATE_LOGOUT_REQUESTED:
+ rb += sprintf(page+rb,
+ "TARG_CONN_STATE_LOGOUT_REQUESTED\n");
+ break;
+ case TARG_CONN_STATE_CLEANUP_WAIT:
+ rb += sprintf(page+rb,
+ "TARG_CONN_STATE_CLEANUP_WAIT\n");
+ break;
+ default:
+ rb += sprintf(page+rb,
+ "ERROR: Unknown Connection State!\n");
+ break;
+ }
+
+ rb += sprintf(page+rb, " Address %s %s", conn->login_ip,
+ (conn->network_transport == ISCSI_TCP) ?
+ "TCP" : "SCTP");
+ rb += sprintf(page+rb, " StatSN: 0x%08x\n",
+ conn->stat_sn);
+ }
+ spin_unlock(&sess->conn_lock);
+ }
+ spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+ return rb;
+}
+
+TF_NACL_BASE_ATTR_RO(lio_target, info);
+
+static ssize_t lio_target_nacl_show_cmdsn_depth(
+ struct se_node_acl *se_nacl,
+ char *page)
+{
+ return sprintf(page, "%u\n", se_nacl->queue_depth);
+}
+
+static ssize_t lio_target_nacl_store_cmdsn_depth(
+ struct se_node_acl *se_nacl,
+ const char *page,
+ size_t count)
+{
+ struct se_portal_group *se_tpg = se_nacl->se_tpg;
+ struct iscsi_portal_group *tpg = container_of(se_tpg,
+ struct iscsi_portal_group, tpg_se_tpg);
+ struct config_item *acl_ci, *tpg_ci, *wwn_ci;
+ u32 cmdsn_depth = 0;
+ int ret;
+
+ ret = kstrtou32(page, 0, &cmdsn_depth);
+ if (ret)
+ return ret;
+ if (cmdsn_depth > TA_DEFAULT_CMDSN_DEPTH_MAX) {
+ pr_err("Passed cmdsn_depth: %u exceeds"
+ " TA_DEFAULT_CMDSN_DEPTH_MAX: %u\n", cmdsn_depth,
+ TA_DEFAULT_CMDSN_DEPTH_MAX);
+ return -EINVAL;
+ }
+ acl_ci = &se_nacl->acl_group.cg_item;
+ if (!acl_ci) {
+ pr_err("Unable to locatel acl_ci\n");
+ return -EINVAL;
+ }
+ tpg_ci = &acl_ci->ci_parent->ci_group->cg_item;
+ if (!tpg_ci) {
+ pr_err("Unable to locate tpg_ci\n");
+ return -EINVAL;
+ }
+ wwn_ci = &tpg_ci->ci_group->cg_item;
+ if (!wwn_ci) {
+ pr_err("Unable to locate config_item wwn_ci\n");
+ return -EINVAL;
+ }
+
+ if (iscsit_get_tpg(tpg) < 0)
+ return -EINVAL;
+ /*
+ * iscsit_tpg_set_initiator_node_queue_depth() assumes force=1
+ */
+ ret = iscsit_tpg_set_initiator_node_queue_depth(tpg,
+ config_item_name(acl_ci), cmdsn_depth, 1);
+
+ pr_debug("LIO_Target_ConfigFS: %s/%s Set CmdSN Window: %u for"
+ "InitiatorName: %s\n", config_item_name(wwn_ci),
+ config_item_name(tpg_ci), cmdsn_depth,
+ config_item_name(acl_ci));
+
+ iscsit_put_tpg(tpg);
+ return (!ret) ? count : (ssize_t)ret;
+}
+
+TF_NACL_BASE_ATTR(lio_target, cmdsn_depth, S_IRUGO | S_IWUSR);
+
+static ssize_t lio_target_nacl_show_tag(
+ struct se_node_acl *se_nacl,
+ char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%s", se_nacl->acl_tag);
+}
+
+static ssize_t lio_target_nacl_store_tag(
+ struct se_node_acl *se_nacl,
+ const char *page,
+ size_t count)
+{
+ int ret;
+
+ ret = core_tpg_set_initiator_node_tag(se_nacl->se_tpg, se_nacl, page);
+
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+TF_NACL_BASE_ATTR(lio_target, tag, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_initiator_attrs[] = {
+ &lio_target_nacl_info.attr,
+ &lio_target_nacl_cmdsn_depth.attr,
+ &lio_target_nacl_tag.attr,
+ NULL,
+};
+
+static struct se_node_acl *lio_tpg_alloc_fabric_acl(
+ struct se_portal_group *se_tpg)
+{
+ struct iscsi_node_acl *acl;
+
+ acl = kzalloc(sizeof(struct iscsi_node_acl), GFP_KERNEL);
+ if (!acl) {
+ pr_err("Unable to allocate memory for struct iscsi_node_acl\n");
+ return NULL;
+ }
+
+ return &acl->se_node_acl;
+}
+
+static struct se_node_acl *lio_target_make_nodeacl(
+ struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
+{
+ struct config_group *stats_cg;
+ struct iscsi_node_acl *acl;
+ struct se_node_acl *se_nacl_new, *se_nacl;
+ struct iscsi_portal_group *tpg = container_of(se_tpg,
+ struct iscsi_portal_group, tpg_se_tpg);
+ u32 cmdsn_depth;
+
+ se_nacl_new = lio_tpg_alloc_fabric_acl(se_tpg);
+ if (!se_nacl_new)
+ return ERR_PTR(-ENOMEM);
+
+ cmdsn_depth = tpg->tpg_attrib.default_cmdsn_depth;
+ /*
+ * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+ * when converting a NdoeACL from demo mode -> explict
+ */
+ se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+ name, cmdsn_depth);
+ if (IS_ERR(se_nacl))
+ return se_nacl;
+
+ acl = container_of(se_nacl, struct iscsi_node_acl, se_node_acl);
+ stats_cg = &se_nacl->acl_fabric_stat_group;
+
+ stats_cg->default_groups = kmalloc(sizeof(struct config_group *) * 2,
+ GFP_KERNEL);
+ if (!stats_cg->default_groups) {
+ pr_err("Unable to allocate memory for"
+ " stats_cg->default_groups\n");
+ core_tpg_del_initiator_node_acl(se_tpg, se_nacl, 1);
+ kfree(acl);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ stats_cg->default_groups[0] = &acl->node_stat_grps.iscsi_sess_stats_group;
+ stats_cg->default_groups[1] = NULL;
+ config_group_init_type_name(&acl->node_stat_grps.iscsi_sess_stats_group,
+ "iscsi_sess_stats", &iscsi_stat_sess_cit);
+
+ return se_nacl;
+}
+
+static void lio_target_drop_nodeacl(
+ struct se_node_acl *se_nacl)
+{
+ struct se_portal_group *se_tpg = se_nacl->se_tpg;
+ struct iscsi_node_acl *acl = container_of(se_nacl,
+ struct iscsi_node_acl, se_node_acl);
+ struct config_item *df_item;
+ struct config_group *stats_cg;
+ int i;
+
+ stats_cg = &acl->se_node_acl.acl_fabric_stat_group;
+ for (i = 0; stats_cg->default_groups[i]; i++) {
+ df_item = &stats_cg->default_groups[i]->cg_item;
+ stats_cg->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+ kfree(stats_cg->default_groups);
+
+ core_tpg_del_initiator_node_acl(se_tpg, se_nacl, 1);
+ kfree(acl);
+}
+
+/* End items for lio_target_acl_cit */
+
+/* Start items for lio_target_tpg_attrib_cit */
+
+#define DEF_TPG_ATTRIB(name) \
+ \
+static ssize_t iscsi_tpg_attrib_show_##name( \
+ struct se_portal_group *se_tpg, \
+ char *page) \
+{ \
+ struct iscsi_portal_group *tpg = container_of(se_tpg, \
+ struct iscsi_portal_group, tpg_se_tpg); \
+ ssize_t rb; \
+ \
+ if (iscsit_get_tpg(tpg) < 0) \
+ return -EINVAL; \
+ \
+ rb = sprintf(page, "%u\n", tpg->tpg_attrib.name); \
+ iscsit_put_tpg(tpg); \
+ return rb; \
+} \
+ \
+static ssize_t iscsi_tpg_attrib_store_##name( \
+ struct se_portal_group *se_tpg, \
+ const char *page, \
+ size_t count) \
+{ \
+ struct iscsi_portal_group *tpg = container_of(se_tpg, \
+ struct iscsi_portal_group, tpg_se_tpg); \
+ u32 val; \
+ int ret; \
+ \
+ if (iscsit_get_tpg(tpg) < 0) \
+ return -EINVAL; \
+ \
+ ret = kstrtou32(page, 0, &val); \
+ if (ret) \
+ goto out; \
+ ret = iscsit_ta_##name(tpg, val); \
+ if (ret < 0) \
+ goto out; \
+ \
+ iscsit_put_tpg(tpg); \
+ return count; \
+out: \
+ iscsit_put_tpg(tpg); \
+ return ret; \
+}
+
+#define TPG_ATTR(_name, _mode) TF_TPG_ATTRIB_ATTR(iscsi, _name, _mode);
+
+/*
+ * Define iscsi_tpg_attrib_s_authentication
+ */
+DEF_TPG_ATTRIB(authentication);
+TPG_ATTR(authentication, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_login_timeout
+ */
+DEF_TPG_ATTRIB(login_timeout);
+TPG_ATTR(login_timeout, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_netif_timeout
+ */
+DEF_TPG_ATTRIB(netif_timeout);
+TPG_ATTR(netif_timeout, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_generate_node_acls
+ */
+DEF_TPG_ATTRIB(generate_node_acls);
+TPG_ATTR(generate_node_acls, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_default_cmdsn_depth
+ */
+DEF_TPG_ATTRIB(default_cmdsn_depth);
+TPG_ATTR(default_cmdsn_depth, S_IRUGO | S_IWUSR);
+/*
+ Define iscsi_tpg_attrib_s_cache_dynamic_acls
+ */
+DEF_TPG_ATTRIB(cache_dynamic_acls);
+TPG_ATTR(cache_dynamic_acls, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_demo_mode_write_protect
+ */
+DEF_TPG_ATTRIB(demo_mode_write_protect);
+TPG_ATTR(demo_mode_write_protect, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_prod_mode_write_protect
+ */
+DEF_TPG_ATTRIB(prod_mode_write_protect);
+TPG_ATTR(prod_mode_write_protect, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_demo_mode_discovery,
+ */
+DEF_TPG_ATTRIB(demo_mode_discovery);
+TPG_ATTR(demo_mode_discovery, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_default_erl
+ */
+DEF_TPG_ATTRIB(default_erl);
+TPG_ATTR(default_erl, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_t10_pi
+ */
+DEF_TPG_ATTRIB(t10_pi);
+TPG_ATTR(t10_pi, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = {
+ &iscsi_tpg_attrib_authentication.attr,
+ &iscsi_tpg_attrib_login_timeout.attr,
+ &iscsi_tpg_attrib_netif_timeout.attr,
+ &iscsi_tpg_attrib_generate_node_acls.attr,
+ &iscsi_tpg_attrib_default_cmdsn_depth.attr,
+ &iscsi_tpg_attrib_cache_dynamic_acls.attr,
+ &iscsi_tpg_attrib_demo_mode_write_protect.attr,
+ &iscsi_tpg_attrib_prod_mode_write_protect.attr,
+ &iscsi_tpg_attrib_demo_mode_discovery.attr,
+ &iscsi_tpg_attrib_default_erl.attr,
+ &iscsi_tpg_attrib_t10_pi.attr,
+ NULL,
+};
+
+/* End items for lio_target_tpg_attrib_cit */
+
+/* Start items for lio_target_tpg_auth_cit */
+
+#define __DEF_TPG_AUTH_STR(prefix, name, flags) \
+static ssize_t __iscsi_##prefix##_show_##name( \
+ struct se_portal_group *se_tpg, \
+ char *page) \
+{ \
+ struct iscsi_portal_group *tpg = container_of(se_tpg, \
+ struct iscsi_portal_group, tpg_se_tpg); \
+ struct iscsi_node_auth *auth = &tpg->tpg_demo_auth; \
+ \
+ if (!capable(CAP_SYS_ADMIN)) \
+ return -EPERM; \
+ \
+ return snprintf(page, PAGE_SIZE, "%s\n", auth->name); \
+} \
+ \
+static ssize_t __iscsi_##prefix##_store_##name( \
+ struct se_portal_group *se_tpg, \
+ const char *page, \
+ size_t count) \
+{ \
+ struct iscsi_portal_group *tpg = container_of(se_tpg, \
+ struct iscsi_portal_group, tpg_se_tpg); \
+ struct iscsi_node_auth *auth = &tpg->tpg_demo_auth; \
+ \
+ if (!capable(CAP_SYS_ADMIN)) \
+ return -EPERM; \
+ \
+ snprintf(auth->name, sizeof(auth->name), "%s", page); \
+ if (!(strncmp("NULL", auth->name, 4))) \
+ auth->naf_flags &= ~flags; \
+ else \
+ auth->naf_flags |= flags; \
+ \
+ if ((auth->naf_flags & NAF_USERID_IN_SET) && \
+ (auth->naf_flags & NAF_PASSWORD_IN_SET)) \
+ auth->authenticate_target = 1; \
+ else \
+ auth->authenticate_target = 0; \
+ \
+ return count; \
+}
+
+#define __DEF_TPG_AUTH_INT(prefix, name) \
+static ssize_t __iscsi_##prefix##_show_##name( \
+ struct se_portal_group *se_tpg, \
+ char *page) \
+{ \
+ struct iscsi_portal_group *tpg = container_of(se_tpg, \
+ struct iscsi_portal_group, tpg_se_tpg); \
+ struct iscsi_node_auth *auth = &tpg->tpg_demo_auth; \
+ \
+ if (!capable(CAP_SYS_ADMIN)) \
+ return -EPERM; \
+ \
+ return snprintf(page, PAGE_SIZE, "%d\n", auth->name); \
+}
+
+#define DEF_TPG_AUTH_STR(name, flags) \
+ __DEF_TPG_AUTH_STR(tpg_auth, name, flags) \
+static ssize_t iscsi_tpg_auth_show_##name( \
+ struct se_portal_group *se_tpg, \
+ char *page) \
+{ \
+ return __iscsi_tpg_auth_show_##name(se_tpg, page); \
+} \
+ \
+static ssize_t iscsi_tpg_auth_store_##name( \
+ struct se_portal_group *se_tpg, \
+ const char *page, \
+ size_t count) \
+{ \
+ return __iscsi_tpg_auth_store_##name(se_tpg, page, count); \
+}
+
+#define DEF_TPG_AUTH_INT(name) \
+ __DEF_TPG_AUTH_INT(tpg_auth, name) \
+static ssize_t iscsi_tpg_auth_show_##name( \
+ struct se_portal_group *se_tpg, \
+ char *page) \
+{ \
+ return __iscsi_tpg_auth_show_##name(se_tpg, page); \
+}
+
+#define TPG_AUTH_ATTR(_name, _mode) TF_TPG_AUTH_ATTR(iscsi, _name, _mode);
+#define TPG_AUTH_ATTR_RO(_name) TF_TPG_AUTH_ATTR_RO(iscsi, _name);
+
+/*
+ * * One-way authentication userid
+ * */
+DEF_TPG_AUTH_STR(userid, NAF_USERID_SET);
+TPG_AUTH_ATTR(userid, S_IRUGO | S_IWUSR);
+/*
+ * * One-way authentication password
+ * */
+DEF_TPG_AUTH_STR(password, NAF_PASSWORD_SET);
+TPG_AUTH_ATTR(password, S_IRUGO | S_IWUSR);
+/*
+ * * Enforce mutual authentication
+ * */
+DEF_TPG_AUTH_INT(authenticate_target);
+TPG_AUTH_ATTR_RO(authenticate_target);
+/*
+ * * Mutual authentication userid
+ * */
+DEF_TPG_AUTH_STR(userid_mutual, NAF_USERID_IN_SET);
+TPG_AUTH_ATTR(userid_mutual, S_IRUGO | S_IWUSR);
+/*
+ * * Mutual authentication password
+ * */
+DEF_TPG_AUTH_STR(password_mutual, NAF_PASSWORD_IN_SET);
+TPG_AUTH_ATTR(password_mutual, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_tpg_auth_attrs[] = {
+ &iscsi_tpg_auth_userid.attr,
+ &iscsi_tpg_auth_password.attr,
+ &iscsi_tpg_auth_authenticate_target.attr,
+ &iscsi_tpg_auth_userid_mutual.attr,
+ &iscsi_tpg_auth_password_mutual.attr,
+ NULL,
+};
+
+/* End items for lio_target_tpg_auth_cit */
+
+/* Start items for lio_target_tpg_param_cit */
+
+#define DEF_TPG_PARAM(name) \
+static ssize_t iscsi_tpg_param_show_##name( \
+ struct se_portal_group *se_tpg, \
+ char *page) \
+{ \
+ struct iscsi_portal_group *tpg = container_of(se_tpg, \
+ struct iscsi_portal_group, tpg_se_tpg); \
+ struct iscsi_param *param; \
+ ssize_t rb; \
+ \
+ if (iscsit_get_tpg(tpg) < 0) \
+ return -EINVAL; \
+ \
+ param = iscsi_find_param_from_key(__stringify(name), \
+ tpg->param_list); \
+ if (!param) { \
+ iscsit_put_tpg(tpg); \
+ return -EINVAL; \
+ } \
+ rb = snprintf(page, PAGE_SIZE, "%s\n", param->value); \
+ \
+ iscsit_put_tpg(tpg); \
+ return rb; \
+} \
+static ssize_t iscsi_tpg_param_store_##name( \
+ struct se_portal_group *se_tpg, \
+ const char *page, \
+ size_t count) \
+{ \
+ struct iscsi_portal_group *tpg = container_of(se_tpg, \
+ struct iscsi_portal_group, tpg_se_tpg); \
+ char *buf; \
+ int ret, len; \
+ \
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL); \
+ if (!buf) \
+ return -ENOMEM; \
+ len = snprintf(buf, PAGE_SIZE, "%s=%s", __stringify(name), page); \
+ if (isspace(buf[len-1])) \
+ buf[len-1] = '\0'; /* Kill newline */ \
+ \
+ if (iscsit_get_tpg(tpg) < 0) { \
+ kfree(buf); \
+ return -EINVAL; \
+ } \
+ \
+ ret = iscsi_change_param_value(buf, tpg->param_list, 1); \
+ if (ret < 0) \
+ goto out; \
+ \
+ kfree(buf); \
+ iscsit_put_tpg(tpg); \
+ return count; \
+out: \
+ kfree(buf); \
+ iscsit_put_tpg(tpg); \
+ return -EINVAL; \
+}
+
+#define TPG_PARAM_ATTR(_name, _mode) TF_TPG_PARAM_ATTR(iscsi, _name, _mode);
+
+DEF_TPG_PARAM(AuthMethod);
+TPG_PARAM_ATTR(AuthMethod, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(HeaderDigest);
+TPG_PARAM_ATTR(HeaderDigest, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(DataDigest);
+TPG_PARAM_ATTR(DataDigest, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(MaxConnections);
+TPG_PARAM_ATTR(MaxConnections, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(TargetAlias);
+TPG_PARAM_ATTR(TargetAlias, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(InitialR2T);
+TPG_PARAM_ATTR(InitialR2T, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(ImmediateData);
+TPG_PARAM_ATTR(ImmediateData, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(MaxRecvDataSegmentLength);
+TPG_PARAM_ATTR(MaxRecvDataSegmentLength, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(MaxXmitDataSegmentLength);
+TPG_PARAM_ATTR(MaxXmitDataSegmentLength, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(MaxBurstLength);
+TPG_PARAM_ATTR(MaxBurstLength, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(FirstBurstLength);
+TPG_PARAM_ATTR(FirstBurstLength, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(DefaultTime2Wait);
+TPG_PARAM_ATTR(DefaultTime2Wait, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(DefaultTime2Retain);
+TPG_PARAM_ATTR(DefaultTime2Retain, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(MaxOutstandingR2T);
+TPG_PARAM_ATTR(MaxOutstandingR2T, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(DataPDUInOrder);
+TPG_PARAM_ATTR(DataPDUInOrder, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(DataSequenceInOrder);
+TPG_PARAM_ATTR(DataSequenceInOrder, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(ErrorRecoveryLevel);
+TPG_PARAM_ATTR(ErrorRecoveryLevel, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(IFMarker);
+TPG_PARAM_ATTR(IFMarker, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(OFMarker);
+TPG_PARAM_ATTR(OFMarker, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(IFMarkInt);
+TPG_PARAM_ATTR(IFMarkInt, S_IRUGO | S_IWUSR);
+
+DEF_TPG_PARAM(OFMarkInt);
+TPG_PARAM_ATTR(OFMarkInt, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_tpg_param_attrs[] = {
+ &iscsi_tpg_param_AuthMethod.attr,
+ &iscsi_tpg_param_HeaderDigest.attr,
+ &iscsi_tpg_param_DataDigest.attr,
+ &iscsi_tpg_param_MaxConnections.attr,
+ &iscsi_tpg_param_TargetAlias.attr,
+ &iscsi_tpg_param_InitialR2T.attr,
+ &iscsi_tpg_param_ImmediateData.attr,
+ &iscsi_tpg_param_MaxRecvDataSegmentLength.attr,
+ &iscsi_tpg_param_MaxXmitDataSegmentLength.attr,
+ &iscsi_tpg_param_MaxBurstLength.attr,
+ &iscsi_tpg_param_FirstBurstLength.attr,
+ &iscsi_tpg_param_DefaultTime2Wait.attr,
+ &iscsi_tpg_param_DefaultTime2Retain.attr,
+ &iscsi_tpg_param_MaxOutstandingR2T.attr,
+ &iscsi_tpg_param_DataPDUInOrder.attr,
+ &iscsi_tpg_param_DataSequenceInOrder.attr,
+ &iscsi_tpg_param_ErrorRecoveryLevel.attr,
+ &iscsi_tpg_param_IFMarker.attr,
+ &iscsi_tpg_param_OFMarker.attr,
+ &iscsi_tpg_param_IFMarkInt.attr,
+ &iscsi_tpg_param_OFMarkInt.attr,
+ NULL,
+};
+
+/* End items for lio_target_tpg_param_cit */
+
+/* Start items for lio_target_tpg_cit */
+
+static ssize_t lio_target_tpg_show_enable(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct iscsi_portal_group *tpg = container_of(se_tpg,
+ struct iscsi_portal_group, tpg_se_tpg);
+ ssize_t len;
+
+ spin_lock(&tpg->tpg_state_lock);
+ len = sprintf(page, "%d\n",
+ (tpg->tpg_state == TPG_STATE_ACTIVE) ? 1 : 0);
+ spin_unlock(&tpg->tpg_state_lock);
+
+ return len;
+}
+
+static ssize_t lio_target_tpg_store_enable(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct iscsi_portal_group *tpg = container_of(se_tpg,
+ struct iscsi_portal_group, tpg_se_tpg);
+ u32 op;
+ int ret;
+
+ ret = kstrtou32(page, 0, &op);
+ if (ret)
+ return ret;
+ if ((op != 1) && (op != 0)) {
+ pr_err("Illegal value for tpg_enable: %u\n", op);
+ return -EINVAL;
+ }
+
+ ret = iscsit_get_tpg(tpg);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (op) {
+ ret = iscsit_tpg_enable_portal_group(tpg);
+ if (ret < 0)
+ goto out;
+ } else {
+ /*
+ * iscsit_tpg_disable_portal_group() assumes force=1
+ */
+ ret = iscsit_tpg_disable_portal_group(tpg, 1);
+ if (ret < 0)
+ goto out;
+ }
+
+ iscsit_put_tpg(tpg);
+ return count;
+out:
+ iscsit_put_tpg(tpg);
+ return -EINVAL;
+}
+
+TF_TPG_BASE_ATTR(lio_target, enable, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_tpg_attrs[] = {
+ &lio_target_tpg_enable.attr,
+ NULL,
+};
+
+/* End items for lio_target_tpg_cit */
+
+/* Start items for lio_target_tiqn_cit */
+
+static struct se_portal_group *lio_target_tiqn_addtpg(
+ struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct iscsi_portal_group *tpg;
+ struct iscsi_tiqn *tiqn;
+ char *tpgt_str;
+ int ret;
+ u16 tpgt;
+
+ tiqn = container_of(wwn, struct iscsi_tiqn, tiqn_wwn);
+ /*
+ * Only tpgt_# directory groups can be created below
+ * target/iscsi/iqn.superturodiskarry/
+ */
+ tpgt_str = strstr(name, "tpgt_");
+ if (!tpgt_str) {
+ pr_err("Unable to locate \"tpgt_#\" directory"
+ " group\n");
+ return NULL;
+ }
+ tpgt_str += 5; /* Skip ahead of "tpgt_" */
+ ret = kstrtou16(tpgt_str, 0, &tpgt);
+ if (ret)
+ return NULL;
+
+ tpg = iscsit_alloc_portal_group(tiqn, tpgt);
+ if (!tpg)
+ return NULL;
+
+ ret = core_tpg_register(
+ &lio_target_fabric_configfs->tf_ops,
+ wwn, &tpg->tpg_se_tpg, tpg,
+ TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0)
+ return NULL;
+
+ ret = iscsit_tpg_add_portal_group(tiqn, tpg);
+ if (ret != 0)
+ goto out;
+
+ pr_debug("LIO_Target_ConfigFS: REGISTER -> %s\n", tiqn->tiqn);
+ pr_debug("LIO_Target_ConfigFS: REGISTER -> Allocated TPG: %s\n",
+ name);
+ return &tpg->tpg_se_tpg;
+out:
+ core_tpg_deregister(&tpg->tpg_se_tpg);
+ kfree(tpg);
+ return NULL;
+}
+
+static void lio_target_tiqn_deltpg(struct se_portal_group *se_tpg)
+{
+ struct iscsi_portal_group *tpg;
+ struct iscsi_tiqn *tiqn;
+
+ tpg = container_of(se_tpg, struct iscsi_portal_group, tpg_se_tpg);
+ tiqn = tpg->tpg_tiqn;
+ /*
+ * iscsit_tpg_del_portal_group() assumes force=1
+ */
+ pr_debug("LIO_Target_ConfigFS: DEREGISTER -> Releasing TPG\n");
+ iscsit_tpg_del_portal_group(tiqn, tpg, 1);
+}
+
+/* End items for lio_target_tiqn_cit */
+
+/* Start LIO-Target TIQN struct contig_item lio_target_cit */
+
+static ssize_t lio_target_wwn_show_attr_lio_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "Datera Inc. iSCSI Target "ISCSIT_VERSION"\n");
+}
+
+TF_WWN_ATTR_RO(lio_target, lio_version);
+
+static struct configfs_attribute *lio_target_wwn_attrs[] = {
+ &lio_target_wwn_lio_version.attr,
+ NULL,
+};
+
+static struct se_wwn *lio_target_call_coreaddtiqn(
+ struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct config_group *stats_cg;
+ struct iscsi_tiqn *tiqn;
+
+ tiqn = iscsit_add_tiqn((unsigned char *)name);
+ if (IS_ERR(tiqn))
+ return ERR_CAST(tiqn);
+ /*
+ * Setup struct iscsi_wwn_stat_grps for se_wwn->fabric_stat_group.
+ */
+ stats_cg = &tiqn->tiqn_wwn.fabric_stat_group;
+
+ stats_cg->default_groups = kmalloc(sizeof(struct config_group *) * 6,
+ GFP_KERNEL);
+ if (!stats_cg->default_groups) {
+ pr_err("Unable to allocate memory for"
+ " stats_cg->default_groups\n");
+ iscsit_del_tiqn(tiqn);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ stats_cg->default_groups[0] = &tiqn->tiqn_stat_grps.iscsi_instance_group;
+ stats_cg->default_groups[1] = &tiqn->tiqn_stat_grps.iscsi_sess_err_group;
+ stats_cg->default_groups[2] = &tiqn->tiqn_stat_grps.iscsi_tgt_attr_group;
+ stats_cg->default_groups[3] = &tiqn->tiqn_stat_grps.iscsi_login_stats_group;
+ stats_cg->default_groups[4] = &tiqn->tiqn_stat_grps.iscsi_logout_stats_group;
+ stats_cg->default_groups[5] = NULL;
+ config_group_init_type_name(&tiqn->tiqn_stat_grps.iscsi_instance_group,
+ "iscsi_instance", &iscsi_stat_instance_cit);
+ config_group_init_type_name(&tiqn->tiqn_stat_grps.iscsi_sess_err_group,
+ "iscsi_sess_err", &iscsi_stat_sess_err_cit);
+ config_group_init_type_name(&tiqn->tiqn_stat_grps.iscsi_tgt_attr_group,
+ "iscsi_tgt_attr", &iscsi_stat_tgt_attr_cit);
+ config_group_init_type_name(&tiqn->tiqn_stat_grps.iscsi_login_stats_group,
+ "iscsi_login_stats", &iscsi_stat_login_cit);
+ config_group_init_type_name(&tiqn->tiqn_stat_grps.iscsi_logout_stats_group,
+ "iscsi_logout_stats", &iscsi_stat_logout_cit);
+
+ pr_debug("LIO_Target_ConfigFS: REGISTER -> %s\n", tiqn->tiqn);
+ pr_debug("LIO_Target_ConfigFS: REGISTER -> Allocated Node:"
+ " %s\n", name);
+ return &tiqn->tiqn_wwn;
+}
+
+static void lio_target_call_coredeltiqn(
+ struct se_wwn *wwn)
+{
+ struct iscsi_tiqn *tiqn = container_of(wwn, struct iscsi_tiqn, tiqn_wwn);
+ struct config_item *df_item;
+ struct config_group *stats_cg;
+ int i;
+
+ stats_cg = &tiqn->tiqn_wwn.fabric_stat_group;
+ for (i = 0; stats_cg->default_groups[i]; i++) {
+ df_item = &stats_cg->default_groups[i]->cg_item;
+ stats_cg->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+ kfree(stats_cg->default_groups);
+
+ pr_debug("LIO_Target_ConfigFS: DEREGISTER -> %s\n",
+ tiqn->tiqn);
+ iscsit_del_tiqn(tiqn);
+}
+
+/* End LIO-Target TIQN struct contig_lio_target_cit */
+
+/* Start lio_target_discovery_auth_cit */
+
+#define DEF_DISC_AUTH_STR(name, flags) \
+ __DEF_NACL_AUTH_STR(disc, name, flags) \
+static ssize_t iscsi_disc_show_##name( \
+ struct target_fabric_configfs *tf, \
+ char *page) \
+{ \
+ return __iscsi_disc_show_##name(&iscsit_global->discovery_acl, \
+ page); \
+} \
+static ssize_t iscsi_disc_store_##name( \
+ struct target_fabric_configfs *tf, \
+ const char *page, \
+ size_t count) \
+{ \
+ return __iscsi_disc_store_##name(&iscsit_global->discovery_acl, \
+ page, count); \
+}
+
+#define DEF_DISC_AUTH_INT(name) \
+ __DEF_NACL_AUTH_INT(disc, name) \
+static ssize_t iscsi_disc_show_##name( \
+ struct target_fabric_configfs *tf, \
+ char *page) \
+{ \
+ return __iscsi_disc_show_##name(&iscsit_global->discovery_acl, \
+ page); \
+}
+
+#define DISC_AUTH_ATTR(_name, _mode) TF_DISC_ATTR(iscsi, _name, _mode)
+#define DISC_AUTH_ATTR_RO(_name) TF_DISC_ATTR_RO(iscsi, _name)
+
+/*
+ * One-way authentication userid
+ */
+DEF_DISC_AUTH_STR(userid, NAF_USERID_SET);
+DISC_AUTH_ATTR(userid, S_IRUGO | S_IWUSR);
+/*
+ * One-way authentication password
+ */
+DEF_DISC_AUTH_STR(password, NAF_PASSWORD_SET);
+DISC_AUTH_ATTR(password, S_IRUGO | S_IWUSR);
+/*
+ * Enforce mutual authentication
+ */
+DEF_DISC_AUTH_INT(authenticate_target);
+DISC_AUTH_ATTR_RO(authenticate_target);
+/*
+ * Mutual authentication userid
+ */
+DEF_DISC_AUTH_STR(userid_mutual, NAF_USERID_IN_SET);
+DISC_AUTH_ATTR(userid_mutual, S_IRUGO | S_IWUSR);
+/*
+ * Mutual authentication password
+ */
+DEF_DISC_AUTH_STR(password_mutual, NAF_PASSWORD_IN_SET);
+DISC_AUTH_ATTR(password_mutual, S_IRUGO | S_IWUSR);
+
+/*
+ * enforce_discovery_auth
+ */
+static ssize_t iscsi_disc_show_enforce_discovery_auth(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ struct iscsi_node_auth *discovery_auth = &iscsit_global->discovery_acl.node_auth;
+
+ return sprintf(page, "%d\n", discovery_auth->enforce_discovery_auth);
+}
+
+static ssize_t iscsi_disc_store_enforce_discovery_auth(
+ struct target_fabric_configfs *tf,
+ const char *page,
+ size_t count)
+{
+ struct iscsi_param *param;
+ struct iscsi_portal_group *discovery_tpg = iscsit_global->discovery_tpg;
+ u32 op;
+ int err;
+
+ err = kstrtou32(page, 0, &op);
+ if (err)
+ return -EINVAL;
+ if ((op != 1) && (op != 0)) {
+ pr_err("Illegal value for enforce_discovery_auth:"
+ " %u\n", op);
+ return -EINVAL;
+ }
+
+ if (!discovery_tpg) {
+ pr_err("iscsit_global->discovery_tpg is NULL\n");
+ return -EINVAL;
+ }
+
+ param = iscsi_find_param_from_key(AUTHMETHOD,
+ discovery_tpg->param_list);
+ if (!param)
+ return -EINVAL;
+
+ if (op) {
+ /*
+ * Reset the AuthMethod key to CHAP.
+ */
+ if (iscsi_update_param_value(param, CHAP) < 0)
+ return -EINVAL;
+
+ discovery_tpg->tpg_attrib.authentication = 1;
+ iscsit_global->discovery_acl.node_auth.enforce_discovery_auth = 1;
+ pr_debug("LIO-CORE[0] Successfully enabled"
+ " authentication enforcement for iSCSI"
+ " Discovery TPG\n");
+ } else {
+ /*
+ * Reset the AuthMethod key to CHAP,None
+ */
+ if (iscsi_update_param_value(param, "CHAP,None") < 0)
+ return -EINVAL;
+
+ discovery_tpg->tpg_attrib.authentication = 0;
+ iscsit_global->discovery_acl.node_auth.enforce_discovery_auth = 0;
+ pr_debug("LIO-CORE[0] Successfully disabled"
+ " authentication enforcement for iSCSI"
+ " Discovery TPG\n");
+ }
+
+ return count;
+}
+
+DISC_AUTH_ATTR(enforce_discovery_auth, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_discovery_auth_attrs[] = {
+ &iscsi_disc_userid.attr,
+ &iscsi_disc_password.attr,
+ &iscsi_disc_authenticate_target.attr,
+ &iscsi_disc_userid_mutual.attr,
+ &iscsi_disc_password_mutual.attr,
+ &iscsi_disc_enforce_discovery_auth.attr,
+ NULL,
+};
+
+/* End lio_target_discovery_auth_cit */
+
+/* Start functions for target_core_fabric_ops */
+
+static char *iscsi_get_fabric_name(void)
+{
+ return "iSCSI";
+}
+
+static u32 iscsi_get_task_tag(struct se_cmd *se_cmd)
+{
+ struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+ /* only used for printks or comparism with ->ref_task_tag */
+ return (__force u32)cmd->init_task_tag;
+}
+
+static int iscsi_get_cmd_state(struct se_cmd *se_cmd)
+{
+ struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+ return cmd->i_state;
+}
+
+static u32 lio_sess_get_index(struct se_session *se_sess)
+{
+ struct iscsi_session *sess = se_sess->fabric_sess_ptr;
+
+ return sess->session_index;
+}
+
+static u32 lio_sess_get_initiator_sid(
+ struct se_session *se_sess,
+ unsigned char *buf,
+ u32 size)
+{
+ struct iscsi_session *sess = se_sess->fabric_sess_ptr;
+ /*
+ * iSCSI Initiator Session Identifier from RFC-3720.
+ */
+ return snprintf(buf, size, "%02x%02x%02x%02x%02x%02x",
+ sess->isid[0], sess->isid[1], sess->isid[2],
+ sess->isid[3], sess->isid[4], sess->isid[5]);
+}
+
+static int lio_queue_data_in(struct se_cmd *se_cmd)
+{
+ struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+ cmd->i_state = ISTATE_SEND_DATAIN;
+ cmd->conn->conn_transport->iscsit_queue_data_in(cmd->conn, cmd);
+
+ return 0;
+}
+
+static int lio_write_pending(struct se_cmd *se_cmd)
+{
+ struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+ struct iscsi_conn *conn = cmd->conn;
+
+ if (!cmd->immediate_data && !cmd->unsolicited_data)
+ return conn->conn_transport->iscsit_get_dataout(conn, cmd, false);
+
+ return 0;
+}
+
+static int lio_write_pending_status(struct se_cmd *se_cmd)
+{
+ struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+ int ret;
+
+ spin_lock_bh(&cmd->istate_lock);
+ ret = !(cmd->cmd_flags & ICF_GOT_LAST_DATAOUT);
+ spin_unlock_bh(&cmd->istate_lock);
+
+ return ret;
+}
+
+static int lio_queue_status(struct se_cmd *se_cmd)
+{
+ struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+ cmd->i_state = ISTATE_SEND_STATUS;
+
+ if (cmd->se_cmd.scsi_status || cmd->sense_reason) {
+ iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
+ return 0;
+ }
+ cmd->conn->conn_transport->iscsit_queue_status(cmd->conn, cmd);
+
+ return 0;
+}
+
+static void lio_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+ struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+ cmd->i_state = ISTATE_SEND_TASKMGTRSP;
+ iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
+}
+
+static void lio_aborted_task(struct se_cmd *se_cmd)
+{
+ struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+ cmd->conn->conn_transport->iscsit_aborted_task(cmd->conn, cmd);
+}
+
+static char *lio_tpg_get_endpoint_wwn(struct se_portal_group *se_tpg)
+{
+ struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ return &tpg->tpg_tiqn->tiqn[0];
+}
+
+static u16 lio_tpg_get_tag(struct se_portal_group *se_tpg)
+{
+ struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ return tpg->tpgt;
+}
+
+static u32 lio_tpg_get_default_depth(struct se_portal_group *se_tpg)
+{
+ struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ return tpg->tpg_attrib.default_cmdsn_depth;
+}
+
+static int lio_tpg_check_demo_mode(struct se_portal_group *se_tpg)
+{
+ struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ return tpg->tpg_attrib.generate_node_acls;
+}
+
+static int lio_tpg_check_demo_mode_cache(struct se_portal_group *se_tpg)
+{
+ struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ return tpg->tpg_attrib.cache_dynamic_acls;
+}
+
+static int lio_tpg_check_demo_mode_write_protect(
+ struct se_portal_group *se_tpg)
+{
+ struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ return tpg->tpg_attrib.demo_mode_write_protect;
+}
+
+static int lio_tpg_check_prod_mode_write_protect(
+ struct se_portal_group *se_tpg)
+{
+ struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ return tpg->tpg_attrib.prod_mode_write_protect;
+}
+
+static void lio_tpg_release_fabric_acl(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_acl)
+{
+ struct iscsi_node_acl *acl = container_of(se_acl,
+ struct iscsi_node_acl, se_node_acl);
+ kfree(acl);
+}
+
+/*
+ * Called with spin_lock_bh(struct se_portal_group->session_lock) held..
+ *
+ * Also, this function calls iscsit_inc_session_usage_count() on the
+ * struct iscsi_session in question.
+ */
+static int lio_tpg_shutdown_session(struct se_session *se_sess)
+{
+ struct iscsi_session *sess = se_sess->fabric_sess_ptr;
+
+ spin_lock(&sess->conn_lock);
+ if (atomic_read(&sess->session_fall_back_to_erl0) ||
+ atomic_read(&sess->session_logout) ||
+ (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
+ spin_unlock(&sess->conn_lock);
+ return 0;
+ }
+ atomic_set(&sess->session_reinstatement, 1);
+ spin_unlock(&sess->conn_lock);
+
+ iscsit_stop_time2retain_timer(sess);
+ iscsit_stop_session(sess, 1, 1);
+
+ return 1;
+}
+
+/*
+ * Calls iscsit_dec_session_usage_count() as inverse of
+ * lio_tpg_shutdown_session()
+ */
+static void lio_tpg_close_session(struct se_session *se_sess)
+{
+ struct iscsi_session *sess = se_sess->fabric_sess_ptr;
+ /*
+ * If the iSCSI Session for the iSCSI Initiator Node exists,
+ * forcefully shutdown the iSCSI NEXUS.
+ */
+ iscsit_close_session(sess);
+}
+
+static u32 lio_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+ struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ return tpg->tpg_tiqn->tiqn_index;
+}
+
+static void lio_set_default_node_attributes(struct se_node_acl *se_acl)
+{
+ struct iscsi_node_acl *acl = container_of(se_acl, struct iscsi_node_acl,
+ se_node_acl);
+ struct se_portal_group *se_tpg = se_acl->se_tpg;
+ struct iscsi_portal_group *tpg = container_of(se_tpg,
+ struct iscsi_portal_group, tpg_se_tpg);
+
+ acl->node_attrib.nacl = acl;
+ iscsit_set_default_node_attribues(acl, tpg);
+}
+
+static int lio_check_stop_free(struct se_cmd *se_cmd)
+{
+ return target_put_sess_cmd(se_cmd->se_sess, se_cmd);
+}
+
+static void lio_release_cmd(struct se_cmd *se_cmd)
+{
+ struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
+
+ pr_debug("Entering lio_release_cmd for se_cmd: %p\n", se_cmd);
+ iscsit_release_cmd(cmd);
+}
+
+/* End functions for target_core_fabric_ops */
+
+int iscsi_target_register_configfs(void)
+{
+ struct target_fabric_configfs *fabric;
+ int ret;
+
+ lio_target_fabric_configfs = NULL;
+ fabric = target_fabric_configfs_init(THIS_MODULE, "iscsi");
+ if (IS_ERR(fabric)) {
+ pr_err("target_fabric_configfs_init() for"
+ " LIO-Target failed!\n");
+ return PTR_ERR(fabric);
+ }
+ /*
+ * Setup the fabric API of function pointers used by target_core_mod..
+ */
+ fabric->tf_ops.get_fabric_name = &iscsi_get_fabric_name;
+ fabric->tf_ops.get_fabric_proto_ident = &iscsi_get_fabric_proto_ident;
+ fabric->tf_ops.tpg_get_wwn = &lio_tpg_get_endpoint_wwn;
+ fabric->tf_ops.tpg_get_tag = &lio_tpg_get_tag;
+ fabric->tf_ops.tpg_get_default_depth = &lio_tpg_get_default_depth;
+ fabric->tf_ops.tpg_get_pr_transport_id = &iscsi_get_pr_transport_id;
+ fabric->tf_ops.tpg_get_pr_transport_id_len =
+ &iscsi_get_pr_transport_id_len;
+ fabric->tf_ops.tpg_parse_pr_out_transport_id =
+ &iscsi_parse_pr_out_transport_id;
+ fabric->tf_ops.tpg_check_demo_mode = &lio_tpg_check_demo_mode;
+ fabric->tf_ops.tpg_check_demo_mode_cache =
+ &lio_tpg_check_demo_mode_cache;
+ fabric->tf_ops.tpg_check_demo_mode_write_protect =
+ &lio_tpg_check_demo_mode_write_protect;
+ fabric->tf_ops.tpg_check_prod_mode_write_protect =
+ &lio_tpg_check_prod_mode_write_protect;
+ fabric->tf_ops.tpg_alloc_fabric_acl = &lio_tpg_alloc_fabric_acl;
+ fabric->tf_ops.tpg_release_fabric_acl = &lio_tpg_release_fabric_acl;
+ fabric->tf_ops.tpg_get_inst_index = &lio_tpg_get_inst_index;
+ fabric->tf_ops.check_stop_free = &lio_check_stop_free,
+ fabric->tf_ops.release_cmd = &lio_release_cmd;
+ fabric->tf_ops.shutdown_session = &lio_tpg_shutdown_session;
+ fabric->tf_ops.close_session = &lio_tpg_close_session;
+ fabric->tf_ops.sess_get_index = &lio_sess_get_index;
+ fabric->tf_ops.sess_get_initiator_sid = &lio_sess_get_initiator_sid;
+ fabric->tf_ops.write_pending = &lio_write_pending;
+ fabric->tf_ops.write_pending_status = &lio_write_pending_status;
+ fabric->tf_ops.set_default_node_attributes =
+ &lio_set_default_node_attributes;
+ fabric->tf_ops.get_task_tag = &iscsi_get_task_tag;
+ fabric->tf_ops.get_cmd_state = &iscsi_get_cmd_state;
+ fabric->tf_ops.queue_data_in = &lio_queue_data_in;
+ fabric->tf_ops.queue_status = &lio_queue_status;
+ fabric->tf_ops.queue_tm_rsp = &lio_queue_tm_rsp;
+ fabric->tf_ops.aborted_task = &lio_aborted_task;
+ /*
+ * Setup function pointers for generic logic in target_core_fabric_configfs.c
+ */
+ fabric->tf_ops.fabric_make_wwn = &lio_target_call_coreaddtiqn;
+ fabric->tf_ops.fabric_drop_wwn = &lio_target_call_coredeltiqn;
+ fabric->tf_ops.fabric_make_tpg = &lio_target_tiqn_addtpg;
+ fabric->tf_ops.fabric_drop_tpg = &lio_target_tiqn_deltpg;
+ fabric->tf_ops.fabric_post_link = NULL;
+ fabric->tf_ops.fabric_pre_unlink = NULL;
+ fabric->tf_ops.fabric_make_np = &lio_target_call_addnptotpg;
+ fabric->tf_ops.fabric_drop_np = &lio_target_call_delnpfromtpg;
+ fabric->tf_ops.fabric_make_nodeacl = &lio_target_make_nodeacl;
+ fabric->tf_ops.fabric_drop_nodeacl = &lio_target_drop_nodeacl;
+ /*
+ * Setup default attribute lists for various fabric->tf_cit_tmpl
+ * sturct config_item_type's
+ */
+ fabric->tf_cit_tmpl.tfc_discovery_cit.ct_attrs = lio_target_discovery_auth_attrs;
+ fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = lio_target_wwn_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = lio_target_tpg_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = lio_target_tpg_attrib_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_auth_cit.ct_attrs = lio_target_tpg_auth_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = lio_target_tpg_param_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = lio_target_portal_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = lio_target_initiator_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = lio_target_nacl_attrib_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = lio_target_nacl_auth_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = lio_target_nacl_param_attrs;
+
+ ret = target_fabric_configfs_register(fabric);
+ if (ret < 0) {
+ pr_err("target_fabric_configfs_register() for"
+ " LIO-Target failed!\n");
+ target_fabric_configfs_free(fabric);
+ return ret;
+ }
+
+ lio_target_fabric_configfs = fabric;
+ pr_debug("LIO_TARGET[0] - Set fabric ->"
+ " lio_target_fabric_configfs\n");
+ return 0;
+}
+
+
+void iscsi_target_deregister_configfs(void)
+{
+ if (!lio_target_fabric_configfs)
+ return;
+ /*
+ * Shutdown discovery sessions and disable discovery TPG
+ */
+ if (iscsit_global->discovery_tpg)
+ iscsit_tpg_disable_portal_group(iscsit_global->discovery_tpg, 1);
+
+ target_fabric_configfs_deregister(lio_target_fabric_configfs);
+ lio_target_fabric_configfs = NULL;
+ pr_debug("LIO_TARGET[0] - Cleared"
+ " lio_target_fabric_configfs\n");
+}
diff --git a/drivers/target/iscsi/iscsi_target_configfs.h b/drivers/target/iscsi/iscsi_target_configfs.h
new file mode 100644
index 00000000000..8cd5a63c4ed
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_configfs.h
@@ -0,0 +1,7 @@
+#ifndef ISCSI_TARGET_CONFIGFS_H
+#define ISCSI_TARGET_CONFIGFS_H
+
+extern int iscsi_target_register_configfs(void);
+extern void iscsi_target_deregister_configfs(void);
+
+#endif /* ISCSI_TARGET_CONFIGFS_H */
diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h
new file mode 100644
index 00000000000..302eb3b7871
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_core.h
@@ -0,0 +1,884 @@
+#ifndef ISCSI_TARGET_CORE_H
+#define ISCSI_TARGET_CORE_H
+
+#include <linux/in.h>
+#include <linux/configfs.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+
+#define ISCSIT_VERSION "v4.1.0"
+#define ISCSI_MAX_DATASN_MISSING_COUNT 16
+#define ISCSI_TX_THREAD_TCP_TIMEOUT 2
+#define ISCSI_RX_THREAD_TCP_TIMEOUT 2
+#define SECONDS_FOR_ASYNC_LOGOUT 10
+#define SECONDS_FOR_ASYNC_TEXT 10
+#define SECONDS_FOR_LOGOUT_COMP 15
+#define WHITE_SPACE " \t\v\f\n\r"
+#define ISCSIT_MIN_TAGS 16
+#define ISCSIT_EXTRA_TAGS 8
+#define ISCSIT_TCP_BACKLOG 256
+
+/* struct iscsi_node_attrib sanity values */
+#define NA_DATAOUT_TIMEOUT 3
+#define NA_DATAOUT_TIMEOUT_MAX 60
+#define NA_DATAOUT_TIMEOUT_MIX 2
+#define NA_DATAOUT_TIMEOUT_RETRIES 5
+#define NA_DATAOUT_TIMEOUT_RETRIES_MAX 15
+#define NA_DATAOUT_TIMEOUT_RETRIES_MIN 1
+#define NA_NOPIN_TIMEOUT 15
+#define NA_NOPIN_TIMEOUT_MAX 60
+#define NA_NOPIN_TIMEOUT_MIN 3
+#define NA_NOPIN_RESPONSE_TIMEOUT 30
+#define NA_NOPIN_RESPONSE_TIMEOUT_MAX 60
+#define NA_NOPIN_RESPONSE_TIMEOUT_MIN 3
+#define NA_RANDOM_DATAIN_PDU_OFFSETS 0
+#define NA_RANDOM_DATAIN_SEQ_OFFSETS 0
+#define NA_RANDOM_R2T_OFFSETS 0
+
+/* struct iscsi_tpg_attrib sanity values */
+#define TA_AUTHENTICATION 1
+#define TA_LOGIN_TIMEOUT 15
+#define TA_LOGIN_TIMEOUT_MAX 30
+#define TA_LOGIN_TIMEOUT_MIN 5
+#define TA_NETIF_TIMEOUT 2
+#define TA_NETIF_TIMEOUT_MAX 15
+#define TA_NETIF_TIMEOUT_MIN 2
+#define TA_GENERATE_NODE_ACLS 0
+#define TA_DEFAULT_CMDSN_DEPTH 64
+#define TA_DEFAULT_CMDSN_DEPTH_MAX 512
+#define TA_DEFAULT_CMDSN_DEPTH_MIN 1
+#define TA_CACHE_DYNAMIC_ACLS 0
+/* Enabled by default in demo mode (generic_node_acls=1) */
+#define TA_DEMO_MODE_WRITE_PROTECT 1
+/* Disabled by default in production mode w/ explict ACLs */
+#define TA_PROD_MODE_WRITE_PROTECT 0
+#define TA_DEMO_MODE_DISCOVERY 1
+#define TA_DEFAULT_ERL 0
+#define TA_CACHE_CORE_NPS 0
+/* T10 protection information disabled by default */
+#define TA_DEFAULT_T10_PI 0
+
+#define ISCSI_IOV_DATA_BUFFER 5
+
+enum iscsit_transport_type {
+ ISCSI_TCP = 0,
+ ISCSI_SCTP_TCP = 1,
+ ISCSI_SCTP_UDP = 2,
+ ISCSI_IWARP_TCP = 3,
+ ISCSI_IWARP_SCTP = 4,
+ ISCSI_INFINIBAND = 5,
+};
+
+/* RFC-3720 7.1.4 Standard Connection State Diagram for a Target */
+enum target_conn_state_table {
+ TARG_CONN_STATE_FREE = 0x1,
+ TARG_CONN_STATE_XPT_UP = 0x3,
+ TARG_CONN_STATE_IN_LOGIN = 0x4,
+ TARG_CONN_STATE_LOGGED_IN = 0x5,
+ TARG_CONN_STATE_IN_LOGOUT = 0x6,
+ TARG_CONN_STATE_LOGOUT_REQUESTED = 0x7,
+ TARG_CONN_STATE_CLEANUP_WAIT = 0x8,
+};
+
+/* RFC-3720 7.3.2 Session State Diagram for a Target */
+enum target_sess_state_table {
+ TARG_SESS_STATE_FREE = 0x1,
+ TARG_SESS_STATE_ACTIVE = 0x2,
+ TARG_SESS_STATE_LOGGED_IN = 0x3,
+ TARG_SESS_STATE_FAILED = 0x4,
+ TARG_SESS_STATE_IN_CONTINUE = 0x5,
+};
+
+/* struct iscsi_data_count->type */
+enum data_count_type {
+ ISCSI_RX_DATA = 1,
+ ISCSI_TX_DATA = 2,
+};
+
+/* struct iscsi_datain_req->dr_complete */
+enum datain_req_comp_table {
+ DATAIN_COMPLETE_NORMAL = 1,
+ DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY = 2,
+ DATAIN_COMPLETE_CONNECTION_RECOVERY = 3,
+};
+
+/* struct iscsi_datain_req->recovery */
+enum datain_req_rec_table {
+ DATAIN_WITHIN_COMMAND_RECOVERY = 1,
+ DATAIN_CONNECTION_RECOVERY = 2,
+};
+
+/* struct iscsi_portal_group->state */
+enum tpg_state_table {
+ TPG_STATE_FREE = 0,
+ TPG_STATE_ACTIVE = 1,
+ TPG_STATE_INACTIVE = 2,
+ TPG_STATE_COLD_RESET = 3,
+};
+
+/* struct iscsi_tiqn->tiqn_state */
+enum tiqn_state_table {
+ TIQN_STATE_ACTIVE = 1,
+ TIQN_STATE_SHUTDOWN = 2,
+};
+
+/* struct iscsi_cmd->cmd_flags */
+enum cmd_flags_table {
+ ICF_GOT_LAST_DATAOUT = 0x00000001,
+ ICF_GOT_DATACK_SNACK = 0x00000002,
+ ICF_NON_IMMEDIATE_UNSOLICITED_DATA = 0x00000004,
+ ICF_SENT_LAST_R2T = 0x00000008,
+ ICF_WITHIN_COMMAND_RECOVERY = 0x00000010,
+ ICF_CONTIG_MEMORY = 0x00000020,
+ ICF_ATTACHED_TO_RQUEUE = 0x00000040,
+ ICF_OOO_CMDSN = 0x00000080,
+ IFC_SENDTARGETS_ALL = 0x00000100,
+ IFC_SENDTARGETS_SINGLE = 0x00000200,
+};
+
+/* struct iscsi_cmd->i_state */
+enum cmd_i_state_table {
+ ISTATE_NO_STATE = 0,
+ ISTATE_NEW_CMD = 1,
+ ISTATE_DEFERRED_CMD = 2,
+ ISTATE_UNSOLICITED_DATA = 3,
+ ISTATE_RECEIVE_DATAOUT = 4,
+ ISTATE_RECEIVE_DATAOUT_RECOVERY = 5,
+ ISTATE_RECEIVED_LAST_DATAOUT = 6,
+ ISTATE_WITHIN_DATAOUT_RECOVERY = 7,
+ ISTATE_IN_CONNECTION_RECOVERY = 8,
+ ISTATE_RECEIVED_TASKMGT = 9,
+ ISTATE_SEND_ASYNCMSG = 10,
+ ISTATE_SENT_ASYNCMSG = 11,
+ ISTATE_SEND_DATAIN = 12,
+ ISTATE_SEND_LAST_DATAIN = 13,
+ ISTATE_SENT_LAST_DATAIN = 14,
+ ISTATE_SEND_LOGOUTRSP = 15,
+ ISTATE_SENT_LOGOUTRSP = 16,
+ ISTATE_SEND_NOPIN = 17,
+ ISTATE_SENT_NOPIN = 18,
+ ISTATE_SEND_REJECT = 19,
+ ISTATE_SENT_REJECT = 20,
+ ISTATE_SEND_R2T = 21,
+ ISTATE_SENT_R2T = 22,
+ ISTATE_SEND_R2T_RECOVERY = 23,
+ ISTATE_SENT_R2T_RECOVERY = 24,
+ ISTATE_SEND_LAST_R2T = 25,
+ ISTATE_SENT_LAST_R2T = 26,
+ ISTATE_SEND_LAST_R2T_RECOVERY = 27,
+ ISTATE_SENT_LAST_R2T_RECOVERY = 28,
+ ISTATE_SEND_STATUS = 29,
+ ISTATE_SEND_STATUS_BROKEN_PC = 30,
+ ISTATE_SENT_STATUS = 31,
+ ISTATE_SEND_STATUS_RECOVERY = 32,
+ ISTATE_SENT_STATUS_RECOVERY = 33,
+ ISTATE_SEND_TASKMGTRSP = 34,
+ ISTATE_SENT_TASKMGTRSP = 35,
+ ISTATE_SEND_TEXTRSP = 36,
+ ISTATE_SENT_TEXTRSP = 37,
+ ISTATE_SEND_NOPIN_WANT_RESPONSE = 38,
+ ISTATE_SENT_NOPIN_WANT_RESPONSE = 39,
+ ISTATE_SEND_NOPIN_NO_RESPONSE = 40,
+ ISTATE_REMOVE = 41,
+ ISTATE_FREE = 42,
+};
+
+/* Used for iscsi_recover_cmdsn() return values */
+enum recover_cmdsn_ret_table {
+ CMDSN_ERROR_CANNOT_RECOVER = -1,
+ CMDSN_NORMAL_OPERATION = 0,
+ CMDSN_LOWER_THAN_EXP = 1,
+ CMDSN_HIGHER_THAN_EXP = 2,
+ CMDSN_MAXCMDSN_OVERRUN = 3,
+};
+
+/* Used for iscsi_handle_immediate_data() return values */
+enum immedate_data_ret_table {
+ IMMEDIATE_DATA_CANNOT_RECOVER = -1,
+ IMMEDIATE_DATA_NORMAL_OPERATION = 0,
+ IMMEDIATE_DATA_ERL1_CRC_FAILURE = 1,
+};
+
+/* Used for iscsi_decide_dataout_action() return values */
+enum dataout_action_ret_table {
+ DATAOUT_CANNOT_RECOVER = -1,
+ DATAOUT_NORMAL = 0,
+ DATAOUT_SEND_R2T = 1,
+ DATAOUT_SEND_TO_TRANSPORT = 2,
+ DATAOUT_WITHIN_COMMAND_RECOVERY = 3,
+};
+
+/* Used for struct iscsi_node_auth->naf_flags */
+enum naf_flags_table {
+ NAF_USERID_SET = 0x01,
+ NAF_PASSWORD_SET = 0x02,
+ NAF_USERID_IN_SET = 0x04,
+ NAF_PASSWORD_IN_SET = 0x08,
+};
+
+/* Used by various struct timer_list to manage iSCSI specific state */
+enum iscsi_timer_flags_table {
+ ISCSI_TF_RUNNING = 0x01,
+ ISCSI_TF_STOP = 0x02,
+ ISCSI_TF_EXPIRED = 0x04,
+};
+
+/* Used for struct iscsi_np->np_flags */
+enum np_flags_table {
+ NPF_IP_NETWORK = 0x00,
+};
+
+/* Used for struct iscsi_np->np_thread_state */
+enum np_thread_state_table {
+ ISCSI_NP_THREAD_ACTIVE = 1,
+ ISCSI_NP_THREAD_INACTIVE = 2,
+ ISCSI_NP_THREAD_RESET = 3,
+ ISCSI_NP_THREAD_SHUTDOWN = 4,
+ ISCSI_NP_THREAD_EXIT = 5,
+};
+
+struct iscsi_conn_ops {
+ u8 HeaderDigest; /* [0,1] == [None,CRC32C] */
+ u8 DataDigest; /* [0,1] == [None,CRC32C] */
+ u32 MaxRecvDataSegmentLength; /* [512..2**24-1] */
+ u32 MaxXmitDataSegmentLength; /* [512..2**24-1] */
+ u8 OFMarker; /* [0,1] == [No,Yes] */
+ u8 IFMarker; /* [0,1] == [No,Yes] */
+ u32 OFMarkInt; /* [1..65535] */
+ u32 IFMarkInt; /* [1..65535] */
+ /*
+ * iSER specific connection parameters
+ */
+ u32 InitiatorRecvDataSegmentLength; /* [512..2**24-1] */
+ u32 TargetRecvDataSegmentLength; /* [512..2**24-1] */
+};
+
+struct iscsi_sess_ops {
+ char InitiatorName[224];
+ char InitiatorAlias[256];
+ char TargetName[224];
+ char TargetAlias[256];
+ char TargetAddress[256];
+ u16 TargetPortalGroupTag; /* [0..65535] */
+ u16 MaxConnections; /* [1..65535] */
+ u8 InitialR2T; /* [0,1] == [No,Yes] */
+ u8 ImmediateData; /* [0,1] == [No,Yes] */
+ u32 MaxBurstLength; /* [512..2**24-1] */
+ u32 FirstBurstLength; /* [512..2**24-1] */
+ u16 DefaultTime2Wait; /* [0..3600] */
+ u16 DefaultTime2Retain; /* [0..3600] */
+ u16 MaxOutstandingR2T; /* [1..65535] */
+ u8 DataPDUInOrder; /* [0,1] == [No,Yes] */
+ u8 DataSequenceInOrder; /* [0,1] == [No,Yes] */
+ u8 ErrorRecoveryLevel; /* [0..2] */
+ u8 SessionType; /* [0,1] == [Normal,Discovery]*/
+ /*
+ * iSER specific session parameters
+ */
+ u8 RDMAExtensions; /* [0,1] == [No,Yes] */
+};
+
+struct iscsi_queue_req {
+ int state;
+ struct iscsi_cmd *cmd;
+ struct list_head qr_list;
+};
+
+struct iscsi_data_count {
+ int data_length;
+ int sync_and_steering;
+ enum data_count_type type;
+ u32 iov_count;
+ u32 ss_iov_count;
+ u32 ss_marker_count;
+ struct kvec *iov;
+};
+
+struct iscsi_param_list {
+ bool iser;
+ struct list_head param_list;
+ struct list_head extra_response_list;
+};
+
+struct iscsi_datain_req {
+ enum datain_req_comp_table dr_complete;
+ int generate_recovery_values;
+ enum datain_req_rec_table recovery;
+ u32 begrun;
+ u32 runlength;
+ u32 data_length;
+ u32 data_offset;
+ u32 data_sn;
+ u32 next_burst_len;
+ u32 read_data_done;
+ u32 seq_send_order;
+ struct list_head cmd_datain_node;
+} ____cacheline_aligned;
+
+struct iscsi_ooo_cmdsn {
+ u16 cid;
+ u32 batch_count;
+ u32 cmdsn;
+ u32 exp_cmdsn;
+ struct iscsi_cmd *cmd;
+ struct list_head ooo_list;
+} ____cacheline_aligned;
+
+struct iscsi_datain {
+ u8 flags;
+ u32 data_sn;
+ u32 length;
+ u32 offset;
+} ____cacheline_aligned;
+
+struct iscsi_r2t {
+ int seq_complete;
+ int recovery_r2t;
+ int sent_r2t;
+ u32 r2t_sn;
+ u32 offset;
+ u32 targ_xfer_tag;
+ u32 xfer_len;
+ struct list_head r2t_list;
+} ____cacheline_aligned;
+
+struct iscsi_cmd {
+ enum iscsi_timer_flags_table dataout_timer_flags;
+ /* DataOUT timeout retries */
+ u8 dataout_timeout_retries;
+ /* Within command recovery count */
+ u8 error_recovery_count;
+ /* iSCSI dependent state for out or order CmdSNs */
+ enum cmd_i_state_table deferred_i_state;
+ /* iSCSI dependent state */
+ enum cmd_i_state_table i_state;
+ /* Command is an immediate command (ISCSI_OP_IMMEDIATE set) */
+ u8 immediate_cmd;
+ /* Immediate data present */
+ u8 immediate_data;
+ /* iSCSI Opcode */
+ u8 iscsi_opcode;
+ /* iSCSI Response Code */
+ u8 iscsi_response;
+ /* Logout reason when iscsi_opcode == ISCSI_INIT_LOGOUT_CMND */
+ u8 logout_reason;
+ /* Logout response code when iscsi_opcode == ISCSI_INIT_LOGOUT_CMND */
+ u8 logout_response;
+ /* MaxCmdSN has been incremented */
+ u8 maxcmdsn_inc;
+ /* Immediate Unsolicited Dataout */
+ u8 unsolicited_data;
+ /* Reject reason code */
+ u8 reject_reason;
+ /* CID contained in logout PDU when opcode == ISCSI_INIT_LOGOUT_CMND */
+ u16 logout_cid;
+ /* Command flags */
+ enum cmd_flags_table cmd_flags;
+ /* Initiator Task Tag assigned from Initiator */
+ itt_t init_task_tag;
+ /* Target Transfer Tag assigned from Target */
+ u32 targ_xfer_tag;
+ /* CmdSN assigned from Initiator */
+ u32 cmd_sn;
+ /* ExpStatSN assigned from Initiator */
+ u32 exp_stat_sn;
+ /* StatSN assigned to this ITT */
+ u32 stat_sn;
+ /* DataSN Counter */
+ u32 data_sn;
+ /* R2TSN Counter */
+ u32 r2t_sn;
+ /* Last DataSN acknowledged via DataAck SNACK */
+ u32 acked_data_sn;
+ /* Used for echoing NOPOUT ping data */
+ u32 buf_ptr_size;
+ /* Used to store DataDigest */
+ u32 data_crc;
+ /* Counter for MaxOutstandingR2T */
+ u32 outstanding_r2ts;
+ /* Next R2T Offset when DataSequenceInOrder=Yes */
+ u32 r2t_offset;
+ /* Iovec current and orig count for iscsi_cmd->iov_data */
+ u32 iov_data_count;
+ u32 orig_iov_data_count;
+ /* Number of miscellaneous iovecs used for IP stack calls */
+ u32 iov_misc_count;
+ /* Number of struct iscsi_pdu in struct iscsi_cmd->pdu_list */
+ u32 pdu_count;
+ /* Next struct iscsi_pdu to send in struct iscsi_cmd->pdu_list */
+ u32 pdu_send_order;
+ /* Current struct iscsi_pdu in struct iscsi_cmd->pdu_list */
+ u32 pdu_start;
+ /* Next struct iscsi_seq to send in struct iscsi_cmd->seq_list */
+ u32 seq_send_order;
+ /* Number of struct iscsi_seq in struct iscsi_cmd->seq_list */
+ u32 seq_count;
+ /* Current struct iscsi_seq in struct iscsi_cmd->seq_list */
+ u32 seq_no;
+ /* Lowest offset in current DataOUT sequence */
+ u32 seq_start_offset;
+ /* Highest offset in current DataOUT sequence */
+ u32 seq_end_offset;
+ /* Total size in bytes received so far of READ data */
+ u32 read_data_done;
+ /* Total size in bytes received so far of WRITE data */
+ u32 write_data_done;
+ /* Counter for FirstBurstLength key */
+ u32 first_burst_len;
+ /* Counter for MaxBurstLength key */
+ u32 next_burst_len;
+ /* Transfer size used for IP stack calls */
+ u32 tx_size;
+ /* Buffer used for various purposes */
+ void *buf_ptr;
+ /* Used by SendTargets=[iqn.,eui.] discovery */
+ void *text_in_ptr;
+ /* See include/linux/dma-mapping.h */
+ enum dma_data_direction data_direction;
+ /* iSCSI PDU Header + CRC */
+ unsigned char pdu[ISCSI_HDR_LEN + ISCSI_CRC_LEN];
+ /* Number of times struct iscsi_cmd is present in immediate queue */
+ atomic_t immed_queue_count;
+ atomic_t response_queue_count;
+ spinlock_t datain_lock;
+ spinlock_t dataout_timeout_lock;
+ /* spinlock for protecting struct iscsi_cmd->i_state */
+ spinlock_t istate_lock;
+ /* spinlock for adding within command recovery entries */
+ spinlock_t error_lock;
+ /* spinlock for adding R2Ts */
+ spinlock_t r2t_lock;
+ /* DataIN List */
+ struct list_head datain_list;
+ /* R2T List */
+ struct list_head cmd_r2t_list;
+ /* Timer for DataOUT */
+ struct timer_list dataout_timer;
+ /* Iovecs for SCSI data payload RX/TX w/ kernel level sockets */
+ struct kvec *iov_data;
+ /* Iovecs for miscellaneous purposes */
+#define ISCSI_MISC_IOVECS 5
+ struct kvec iov_misc[ISCSI_MISC_IOVECS];
+ /* Array of struct iscsi_pdu used for DataPDUInOrder=No */
+ struct iscsi_pdu *pdu_list;
+ /* Current struct iscsi_pdu used for DataPDUInOrder=No */
+ struct iscsi_pdu *pdu_ptr;
+ /* Array of struct iscsi_seq used for DataSequenceInOrder=No */
+ struct iscsi_seq *seq_list;
+ /* Current struct iscsi_seq used for DataSequenceInOrder=No */
+ struct iscsi_seq *seq_ptr;
+ /* TMR Request when iscsi_opcode == ISCSI_OP_SCSI_TMFUNC */
+ struct iscsi_tmr_req *tmr_req;
+ /* Connection this command is alligient to */
+ struct iscsi_conn *conn;
+ /* Pointer to connection recovery entry */
+ struct iscsi_conn_recovery *cr;
+ /* Session the command is part of, used for connection recovery */
+ struct iscsi_session *sess;
+ /* list_head for connection list */
+ struct list_head i_conn_node;
+ /* The TCM I/O descriptor that is accessed via container_of() */
+ struct se_cmd se_cmd;
+ /* Sense buffer that will be mapped into outgoing status */
+#define ISCSI_SENSE_BUFFER_LEN (TRANSPORT_SENSE_BUFFER + 2)
+ unsigned char sense_buffer[ISCSI_SENSE_BUFFER_LEN];
+
+ u32 padding;
+ u8 pad_bytes[4];
+
+ struct scatterlist *first_data_sg;
+ u32 first_data_sg_off;
+ u32 kmapped_nents;
+ sense_reason_t sense_reason;
+} ____cacheline_aligned;
+
+struct iscsi_tmr_req {
+ bool task_reassign:1;
+ u32 exp_data_sn;
+ struct iscsi_cmd *ref_cmd;
+ struct iscsi_conn_recovery *conn_recovery;
+ struct se_tmr_req *se_tmr_req;
+};
+
+struct iscsi_conn {
+ wait_queue_head_t queues_wq;
+ /* Authentication Successful for this connection */
+ u8 auth_complete;
+ /* State connection is currently in */
+ u8 conn_state;
+ u8 conn_logout_reason;
+ u8 network_transport;
+ enum iscsi_timer_flags_table nopin_timer_flags;
+ enum iscsi_timer_flags_table nopin_response_timer_flags;
+ /* Used to know what thread encountered a transport failure */
+ u8 which_thread;
+ /* connection id assigned by the Initiator */
+ u16 cid;
+ /* Remote TCP Port */
+ u16 login_port;
+ u16 local_port;
+ int net_size;
+ int login_family;
+ u32 auth_id;
+ u32 conn_flags;
+ /* Used for iscsi_tx_login_rsp() */
+ itt_t login_itt;
+ u32 exp_statsn;
+ /* Per connection status sequence number */
+ u32 stat_sn;
+ /* IFMarkInt's Current Value */
+ u32 if_marker;
+ /* OFMarkInt's Current Value */
+ u32 of_marker;
+ /* Used for calculating OFMarker offset to next PDU */
+ u32 of_marker_offset;
+#define IPV6_ADDRESS_SPACE 48
+ unsigned char login_ip[IPV6_ADDRESS_SPACE];
+ unsigned char local_ip[IPV6_ADDRESS_SPACE];
+ int conn_usage_count;
+ int conn_waiting_on_uc;
+ atomic_t check_immediate_queue;
+ atomic_t conn_logout_remove;
+ atomic_t connection_exit;
+ atomic_t connection_recovery;
+ atomic_t connection_reinstatement;
+ atomic_t connection_wait_rcfr;
+ atomic_t sleep_on_conn_wait_comp;
+ atomic_t transport_failed;
+ struct completion conn_post_wait_comp;
+ struct completion conn_wait_comp;
+ struct completion conn_wait_rcfr_comp;
+ struct completion conn_waiting_on_uc_comp;
+ struct completion conn_logout_comp;
+ struct completion tx_half_close_comp;
+ struct completion rx_half_close_comp;
+ /* socket used by this connection */
+ struct socket *sock;
+ void (*orig_data_ready)(struct sock *);
+ void (*orig_state_change)(struct sock *);
+#define LOGIN_FLAGS_READ_ACTIVE 1
+#define LOGIN_FLAGS_CLOSED 2
+#define LOGIN_FLAGS_READY 4
+ unsigned long login_flags;
+ struct delayed_work login_work;
+ struct delayed_work login_cleanup_work;
+ struct iscsi_login *login;
+ struct timer_list nopin_timer;
+ struct timer_list nopin_response_timer;
+ struct timer_list transport_timer;
+ struct task_struct *login_kworker;
+ /* Spinlock used for add/deleting cmd's from conn_cmd_list */
+ spinlock_t cmd_lock;
+ spinlock_t conn_usage_lock;
+ spinlock_t immed_queue_lock;
+ spinlock_t nopin_timer_lock;
+ spinlock_t response_queue_lock;
+ spinlock_t state_lock;
+ /* libcrypto RX and TX contexts for crc32c */
+ struct hash_desc conn_rx_hash;
+ struct hash_desc conn_tx_hash;
+ /* Used for scheduling TX and RX connection kthreads */
+ cpumask_var_t conn_cpumask;
+ unsigned int conn_rx_reset_cpumask:1;
+ unsigned int conn_tx_reset_cpumask:1;
+ /* list_head of struct iscsi_cmd for this connection */
+ struct list_head conn_cmd_list;
+ struct list_head immed_queue_list;
+ struct list_head response_queue_list;
+ struct iscsi_conn_ops *conn_ops;
+ struct iscsi_login *conn_login;
+ struct iscsit_transport *conn_transport;
+ struct iscsi_param_list *param_list;
+ /* Used for per connection auth state machine */
+ void *auth_protocol;
+ void *context;
+ struct iscsi_login_thread_s *login_thread;
+ struct iscsi_portal_group *tpg;
+ struct iscsi_tpg_np *tpg_np;
+ /* Pointer to parent session */
+ struct iscsi_session *sess;
+ /* Pointer to thread_set in use for this conn's threads */
+ struct iscsi_thread_set *thread_set;
+ /* list_head for session connection list */
+ struct list_head conn_list;
+} ____cacheline_aligned;
+
+struct iscsi_conn_recovery {
+ u16 cid;
+ u32 cmd_count;
+ u32 maxrecvdatasegmentlength;
+ u32 maxxmitdatasegmentlength;
+ int ready_for_reallegiance;
+ struct list_head conn_recovery_cmd_list;
+ spinlock_t conn_recovery_cmd_lock;
+ struct timer_list time2retain_timer;
+ struct iscsi_session *sess;
+ struct list_head cr_list;
+} ____cacheline_aligned;
+
+struct iscsi_session {
+ u8 initiator_vendor;
+ u8 isid[6];
+ enum iscsi_timer_flags_table time2retain_timer_flags;
+ u8 version_active;
+ u16 cid_called;
+ u16 conn_recovery_count;
+ u16 tsih;
+ /* state session is currently in */
+ u32 session_state;
+ /* session wide counter: initiator assigned task tag */
+ itt_t init_task_tag;
+ /* session wide counter: target assigned task tag */
+ u32 targ_xfer_tag;
+ u32 cmdsn_window;
+
+ /* protects cmdsn values */
+ struct mutex cmdsn_mutex;
+ /* session wide counter: expected command sequence number */
+ u32 exp_cmd_sn;
+ /* session wide counter: maximum allowed command sequence number */
+ u32 max_cmd_sn;
+ struct list_head sess_ooo_cmdsn_list;
+
+ /* LIO specific session ID */
+ u32 sid;
+ char auth_type[8];
+ /* unique within the target */
+ int session_index;
+ /* Used for session reference counting */
+ int session_usage_count;
+ int session_waiting_on_uc;
+ atomic_long_t cmd_pdus;
+ atomic_long_t rsp_pdus;
+ atomic_long_t tx_data_octets;
+ atomic_long_t rx_data_octets;
+ atomic_long_t conn_digest_errors;
+ atomic_long_t conn_timeout_errors;
+ u64 creation_time;
+ /* Number of active connections */
+ atomic_t nconn;
+ atomic_t session_continuation;
+ atomic_t session_fall_back_to_erl0;
+ atomic_t session_logout;
+ atomic_t session_reinstatement;
+ atomic_t session_stop_active;
+ atomic_t sleep_on_sess_wait_comp;
+ /* connection list */
+ struct list_head sess_conn_list;
+ struct list_head cr_active_list;
+ struct list_head cr_inactive_list;
+ spinlock_t conn_lock;
+ spinlock_t cr_a_lock;
+ spinlock_t cr_i_lock;
+ spinlock_t session_usage_lock;
+ spinlock_t ttt_lock;
+ struct completion async_msg_comp;
+ struct completion reinstatement_comp;
+ struct completion session_wait_comp;
+ struct completion session_waiting_on_uc_comp;
+ struct timer_list time2retain_timer;
+ struct iscsi_sess_ops *sess_ops;
+ struct se_session *se_sess;
+ struct iscsi_portal_group *tpg;
+} ____cacheline_aligned;
+
+struct iscsi_login {
+ u8 auth_complete;
+ u8 checked_for_existing;
+ u8 current_stage;
+ u8 leading_connection;
+ u8 first_request;
+ u8 version_min;
+ u8 version_max;
+ u8 login_complete;
+ u8 login_failed;
+ bool zero_tsih;
+ char isid[6];
+ u32 cmd_sn;
+ itt_t init_task_tag;
+ u32 initial_exp_statsn;
+ u32 rsp_length;
+ u16 cid;
+ u16 tsih;
+ char req[ISCSI_HDR_LEN];
+ char rsp[ISCSI_HDR_LEN];
+ char *req_buf;
+ char *rsp_buf;
+ struct iscsi_conn *conn;
+ struct iscsi_np *np;
+} ____cacheline_aligned;
+
+struct iscsi_node_attrib {
+ u32 dataout_timeout;
+ u32 dataout_timeout_retries;
+ u32 default_erl;
+ u32 nopin_timeout;
+ u32 nopin_response_timeout;
+ u32 random_datain_pdu_offsets;
+ u32 random_datain_seq_offsets;
+ u32 random_r2t_offsets;
+ u32 tmr_cold_reset;
+ u32 tmr_warm_reset;
+ struct iscsi_node_acl *nacl;
+};
+
+struct se_dev_entry_s;
+
+struct iscsi_node_auth {
+ enum naf_flags_table naf_flags;
+ int authenticate_target;
+ /* Used for iscsit_global->discovery_auth,
+ * set to zero (auth disabled) by default */
+ int enforce_discovery_auth;
+#define MAX_USER_LEN 256
+#define MAX_PASS_LEN 256
+ char userid[MAX_USER_LEN];
+ char password[MAX_PASS_LEN];
+ char userid_mutual[MAX_USER_LEN];
+ char password_mutual[MAX_PASS_LEN];
+};
+
+#include "iscsi_target_stat.h"
+
+struct iscsi_node_stat_grps {
+ struct config_group iscsi_sess_stats_group;
+ struct config_group iscsi_conn_stats_group;
+};
+
+struct iscsi_node_acl {
+ struct iscsi_node_attrib node_attrib;
+ struct iscsi_node_auth node_auth;
+ struct iscsi_node_stat_grps node_stat_grps;
+ struct se_node_acl se_node_acl;
+};
+
+struct iscsi_tpg_attrib {
+ u32 authentication;
+ u32 login_timeout;
+ u32 netif_timeout;
+ u32 generate_node_acls;
+ u32 cache_dynamic_acls;
+ u32 default_cmdsn_depth;
+ u32 demo_mode_write_protect;
+ u32 prod_mode_write_protect;
+ u32 demo_mode_discovery;
+ u32 default_erl;
+ u8 t10_pi;
+ struct iscsi_portal_group *tpg;
+};
+
+struct iscsi_np {
+ int np_network_transport;
+ int np_ip_proto;
+ int np_sock_type;
+ enum np_thread_state_table np_thread_state;
+ bool enabled;
+ enum iscsi_timer_flags_table np_login_timer_flags;
+ u32 np_exports;
+ enum np_flags_table np_flags;
+ unsigned char np_ip[IPV6_ADDRESS_SPACE];
+ u16 np_port;
+ spinlock_t np_thread_lock;
+ struct completion np_restart_comp;
+ struct socket *np_socket;
+ struct __kernel_sockaddr_storage np_sockaddr;
+ struct task_struct *np_thread;
+ struct timer_list np_login_timer;
+ void *np_context;
+ struct iscsit_transport *np_transport;
+ struct list_head np_list;
+ struct iscsi_tpg_np *tpg_np;
+} ____cacheline_aligned;
+
+struct iscsi_tpg_np {
+ struct iscsi_np *tpg_np;
+ struct iscsi_portal_group *tpg;
+ struct iscsi_tpg_np *tpg_np_parent;
+ struct list_head tpg_np_list;
+ struct list_head tpg_np_child_list;
+ struct list_head tpg_np_parent_list;
+ struct se_tpg_np se_tpg_np;
+ spinlock_t tpg_np_parent_lock;
+ struct completion tpg_np_comp;
+ struct kref tpg_np_kref;
+};
+
+struct iscsi_portal_group {
+ unsigned char tpg_chap_id;
+ /* TPG State */
+ enum tpg_state_table tpg_state;
+ /* Target Portal Group Tag */
+ u16 tpgt;
+ /* Id assigned to target sessions */
+ u16 ntsih;
+ /* Number of active sessions */
+ u32 nsessions;
+ /* Number of Network Portals available for this TPG */
+ u32 num_tpg_nps;
+ /* Per TPG LIO specific session ID. */
+ u32 sid;
+ /* Spinlock for adding/removing Network Portals */
+ spinlock_t tpg_np_lock;
+ spinlock_t tpg_state_lock;
+ struct se_portal_group tpg_se_tpg;
+ struct mutex tpg_access_lock;
+ struct semaphore np_login_sem;
+ struct iscsi_tpg_attrib tpg_attrib;
+ struct iscsi_node_auth tpg_demo_auth;
+ /* Pointer to default list of iSCSI parameters for TPG */
+ struct iscsi_param_list *param_list;
+ struct iscsi_tiqn *tpg_tiqn;
+ struct list_head tpg_gnp_list;
+ struct list_head tpg_list;
+} ____cacheline_aligned;
+
+struct iscsi_wwn_stat_grps {
+ struct config_group iscsi_stat_group;
+ struct config_group iscsi_instance_group;
+ struct config_group iscsi_sess_err_group;
+ struct config_group iscsi_tgt_attr_group;
+ struct config_group iscsi_login_stats_group;
+ struct config_group iscsi_logout_stats_group;
+};
+
+struct iscsi_tiqn {
+#define ISCSI_IQN_LEN 224
+ unsigned char tiqn[ISCSI_IQN_LEN];
+ enum tiqn_state_table tiqn_state;
+ int tiqn_access_count;
+ u32 tiqn_active_tpgs;
+ u32 tiqn_ntpgs;
+ u32 tiqn_num_tpg_nps;
+ u32 tiqn_nsessions;
+ struct list_head tiqn_list;
+ struct list_head tiqn_tpg_list;
+ spinlock_t tiqn_state_lock;
+ spinlock_t tiqn_tpg_lock;
+ struct se_wwn tiqn_wwn;
+ struct iscsi_wwn_stat_grps tiqn_stat_grps;
+ int tiqn_index;
+ struct iscsi_sess_err_stats sess_err_stats;
+ struct iscsi_login_stats login_stats;
+ struct iscsi_logout_stats logout_stats;
+} ____cacheline_aligned;
+
+struct iscsit_global {
+ /* In core shutdown */
+ u32 in_shutdown;
+ u32 active_ts;
+ /* Unique identifier used for the authentication daemon */
+ u32 auth_id;
+ u32 inactive_ts;
+ /* Thread Set bitmap count */
+ int ts_bitmap_count;
+ /* Thread Set bitmap pointer */
+ unsigned long *ts_bitmap;
+ /* Used for iSCSI discovery session authentication */
+ struct iscsi_node_acl discovery_acl;
+ struct iscsi_portal_group *discovery_tpg;
+};
+
+#endif /* ISCSI_TARGET_CORE_H */
diff --git a/drivers/target/iscsi/iscsi_target_datain_values.c b/drivers/target/iscsi/iscsi_target_datain_values.c
new file mode 100644
index 00000000000..e93d5a7a3f8
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_datain_values.c
@@ -0,0 +1,526 @@
+/*******************************************************************************
+ * This file contains the iSCSI Target DataIN value generation functions.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <scsi/iscsi_proto.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_seq_pdu_list.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+#include "iscsi_target_datain_values.h"
+
+struct iscsi_datain_req *iscsit_allocate_datain_req(void)
+{
+ struct iscsi_datain_req *dr;
+
+ dr = kmem_cache_zalloc(lio_dr_cache, GFP_ATOMIC);
+ if (!dr) {
+ pr_err("Unable to allocate memory for"
+ " struct iscsi_datain_req\n");
+ return NULL;
+ }
+ INIT_LIST_HEAD(&dr->cmd_datain_node);
+
+ return dr;
+}
+
+void iscsit_attach_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr)
+{
+ spin_lock(&cmd->datain_lock);
+ list_add_tail(&dr->cmd_datain_node, &cmd->datain_list);
+ spin_unlock(&cmd->datain_lock);
+}
+
+void iscsit_free_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr)
+{
+ spin_lock(&cmd->datain_lock);
+ list_del(&dr->cmd_datain_node);
+ spin_unlock(&cmd->datain_lock);
+
+ kmem_cache_free(lio_dr_cache, dr);
+}
+
+void iscsit_free_all_datain_reqs(struct iscsi_cmd *cmd)
+{
+ struct iscsi_datain_req *dr, *dr_tmp;
+
+ spin_lock(&cmd->datain_lock);
+ list_for_each_entry_safe(dr, dr_tmp, &cmd->datain_list, cmd_datain_node) {
+ list_del(&dr->cmd_datain_node);
+ kmem_cache_free(lio_dr_cache, dr);
+ }
+ spin_unlock(&cmd->datain_lock);
+}
+
+struct iscsi_datain_req *iscsit_get_datain_req(struct iscsi_cmd *cmd)
+{
+ if (list_empty(&cmd->datain_list)) {
+ pr_err("cmd->datain_list is empty for ITT:"
+ " 0x%08x\n", cmd->init_task_tag);
+ return NULL;
+ }
+
+ return list_first_entry(&cmd->datain_list, struct iscsi_datain_req,
+ cmd_datain_node);
+}
+
+/*
+ * For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=Yes.
+ */
+static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_yes(
+ struct iscsi_cmd *cmd,
+ struct iscsi_datain *datain)
+{
+ u32 next_burst_len, read_data_done, read_data_left;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_datain_req *dr;
+
+ dr = iscsit_get_datain_req(cmd);
+ if (!dr)
+ return NULL;
+
+ if (dr->recovery && dr->generate_recovery_values) {
+ if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(
+ cmd, dr) < 0)
+ return NULL;
+
+ dr->generate_recovery_values = 0;
+ }
+
+ next_burst_len = (!dr->recovery) ?
+ cmd->next_burst_len : dr->next_burst_len;
+ read_data_done = (!dr->recovery) ?
+ cmd->read_data_done : dr->read_data_done;
+
+ read_data_left = (cmd->se_cmd.data_length - read_data_done);
+ if (!read_data_left) {
+ pr_err("ITT: 0x%08x read_data_left is zero!\n",
+ cmd->init_task_tag);
+ return NULL;
+ }
+
+ if ((read_data_left <= conn->conn_ops->MaxRecvDataSegmentLength) &&
+ (read_data_left <= (conn->sess->sess_ops->MaxBurstLength -
+ next_burst_len))) {
+ datain->length = read_data_left;
+
+ datain->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);
+ if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+ datain->flags |= ISCSI_FLAG_DATA_ACK;
+ } else {
+ if ((next_burst_len +
+ conn->conn_ops->MaxRecvDataSegmentLength) <
+ conn->sess->sess_ops->MaxBurstLength) {
+ datain->length =
+ conn->conn_ops->MaxRecvDataSegmentLength;
+ next_burst_len += datain->length;
+ } else {
+ datain->length = (conn->sess->sess_ops->MaxBurstLength -
+ next_burst_len);
+ next_burst_len = 0;
+
+ datain->flags |= ISCSI_FLAG_CMD_FINAL;
+ if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+ datain->flags |= ISCSI_FLAG_DATA_ACK;
+ }
+ }
+
+ datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
+ datain->offset = read_data_done;
+
+ if (!dr->recovery) {
+ cmd->next_burst_len = next_burst_len;
+ cmd->read_data_done += datain->length;
+ } else {
+ dr->next_burst_len = next_burst_len;
+ dr->read_data_done += datain->length;
+ }
+
+ if (!dr->recovery) {
+ if (datain->flags & ISCSI_FLAG_DATA_STATUS)
+ dr->dr_complete = DATAIN_COMPLETE_NORMAL;
+
+ return dr;
+ }
+
+ if (!dr->runlength) {
+ if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
+ dr->dr_complete =
+ (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+ DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+ DATAIN_COMPLETE_CONNECTION_RECOVERY;
+ }
+ } else {
+ if ((dr->begrun + dr->runlength) == dr->data_sn) {
+ dr->dr_complete =
+ (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+ DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+ DATAIN_COMPLETE_CONNECTION_RECOVERY;
+ }
+ }
+
+ return dr;
+}
+
+/*
+ * For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=Yes.
+ */
+static struct iscsi_datain_req *iscsit_set_datain_values_no_and_yes(
+ struct iscsi_cmd *cmd,
+ struct iscsi_datain *datain)
+{
+ u32 offset, read_data_done, read_data_left, seq_send_order;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_datain_req *dr;
+ struct iscsi_seq *seq;
+
+ dr = iscsit_get_datain_req(cmd);
+ if (!dr)
+ return NULL;
+
+ if (dr->recovery && dr->generate_recovery_values) {
+ if (iscsit_create_recovery_datain_values_datasequenceinorder_no(
+ cmd, dr) < 0)
+ return NULL;
+
+ dr->generate_recovery_values = 0;
+ }
+
+ read_data_done = (!dr->recovery) ?
+ cmd->read_data_done : dr->read_data_done;
+ seq_send_order = (!dr->recovery) ?
+ cmd->seq_send_order : dr->seq_send_order;
+
+ read_data_left = (cmd->se_cmd.data_length - read_data_done);
+ if (!read_data_left) {
+ pr_err("ITT: 0x%08x read_data_left is zero!\n",
+ cmd->init_task_tag);
+ return NULL;
+ }
+
+ seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);
+ if (!seq)
+ return NULL;
+
+ seq->sent = 1;
+
+ if (!dr->recovery && !seq->next_burst_len)
+ seq->first_datasn = cmd->data_sn;
+
+ offset = (seq->offset + seq->next_burst_len);
+
+ if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >=
+ cmd->se_cmd.data_length) {
+ datain->length = (cmd->se_cmd.data_length - offset);
+ datain->offset = offset;
+
+ datain->flags |= ISCSI_FLAG_CMD_FINAL;
+ if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+ datain->flags |= ISCSI_FLAG_DATA_ACK;
+
+ seq->next_burst_len = 0;
+ seq_send_order++;
+ } else {
+ if ((seq->next_burst_len +
+ conn->conn_ops->MaxRecvDataSegmentLength) <
+ conn->sess->sess_ops->MaxBurstLength) {
+ datain->length =
+ conn->conn_ops->MaxRecvDataSegmentLength;
+ datain->offset = (seq->offset + seq->next_burst_len);
+
+ seq->next_burst_len += datain->length;
+ } else {
+ datain->length = (conn->sess->sess_ops->MaxBurstLength -
+ seq->next_burst_len);
+ datain->offset = (seq->offset + seq->next_burst_len);
+
+ datain->flags |= ISCSI_FLAG_CMD_FINAL;
+ if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+ datain->flags |= ISCSI_FLAG_DATA_ACK;
+
+ seq->next_burst_len = 0;
+ seq_send_order++;
+ }
+ }
+
+ if ((read_data_done + datain->length) == cmd->se_cmd.data_length)
+ datain->flags |= ISCSI_FLAG_DATA_STATUS;
+
+ datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
+ if (!dr->recovery) {
+ cmd->seq_send_order = seq_send_order;
+ cmd->read_data_done += datain->length;
+ } else {
+ dr->seq_send_order = seq_send_order;
+ dr->read_data_done += datain->length;
+ }
+
+ if (!dr->recovery) {
+ if (datain->flags & ISCSI_FLAG_CMD_FINAL)
+ seq->last_datasn = datain->data_sn;
+ if (datain->flags & ISCSI_FLAG_DATA_STATUS)
+ dr->dr_complete = DATAIN_COMPLETE_NORMAL;
+
+ return dr;
+ }
+
+ if (!dr->runlength) {
+ if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
+ dr->dr_complete =
+ (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+ DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+ DATAIN_COMPLETE_CONNECTION_RECOVERY;
+ }
+ } else {
+ if ((dr->begrun + dr->runlength) == dr->data_sn) {
+ dr->dr_complete =
+ (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+ DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+ DATAIN_COMPLETE_CONNECTION_RECOVERY;
+ }
+ }
+
+ return dr;
+}
+
+/*
+ * For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=No.
+ */
+static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_no(
+ struct iscsi_cmd *cmd,
+ struct iscsi_datain *datain)
+{
+ u32 next_burst_len, read_data_done, read_data_left;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_datain_req *dr;
+ struct iscsi_pdu *pdu;
+
+ dr = iscsit_get_datain_req(cmd);
+ if (!dr)
+ return NULL;
+
+ if (dr->recovery && dr->generate_recovery_values) {
+ if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(
+ cmd, dr) < 0)
+ return NULL;
+
+ dr->generate_recovery_values = 0;
+ }
+
+ next_burst_len = (!dr->recovery) ?
+ cmd->next_burst_len : dr->next_burst_len;
+ read_data_done = (!dr->recovery) ?
+ cmd->read_data_done : dr->read_data_done;
+
+ read_data_left = (cmd->se_cmd.data_length - read_data_done);
+ if (!read_data_left) {
+ pr_err("ITT: 0x%08x read_data_left is zero!\n",
+ cmd->init_task_tag);
+ return dr;
+ }
+
+ pdu = iscsit_get_pdu_holder_for_seq(cmd, NULL);
+ if (!pdu)
+ return dr;
+
+ if ((read_data_done + pdu->length) == cmd->se_cmd.data_length) {
+ pdu->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);
+ if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+ pdu->flags |= ISCSI_FLAG_DATA_ACK;
+
+ next_burst_len = 0;
+ } else {
+ if ((next_burst_len + conn->conn_ops->MaxRecvDataSegmentLength) <
+ conn->sess->sess_ops->MaxBurstLength)
+ next_burst_len += pdu->length;
+ else {
+ pdu->flags |= ISCSI_FLAG_CMD_FINAL;
+ if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+ pdu->flags |= ISCSI_FLAG_DATA_ACK;
+
+ next_burst_len = 0;
+ }
+ }
+
+ pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
+ if (!dr->recovery) {
+ cmd->next_burst_len = next_burst_len;
+ cmd->read_data_done += pdu->length;
+ } else {
+ dr->next_burst_len = next_burst_len;
+ dr->read_data_done += pdu->length;
+ }
+
+ datain->flags = pdu->flags;
+ datain->length = pdu->length;
+ datain->offset = pdu->offset;
+ datain->data_sn = pdu->data_sn;
+
+ if (!dr->recovery) {
+ if (datain->flags & ISCSI_FLAG_DATA_STATUS)
+ dr->dr_complete = DATAIN_COMPLETE_NORMAL;
+
+ return dr;
+ }
+
+ if (!dr->runlength) {
+ if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
+ dr->dr_complete =
+ (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+ DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+ DATAIN_COMPLETE_CONNECTION_RECOVERY;
+ }
+ } else {
+ if ((dr->begrun + dr->runlength) == dr->data_sn) {
+ dr->dr_complete =
+ (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+ DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+ DATAIN_COMPLETE_CONNECTION_RECOVERY;
+ }
+ }
+
+ return dr;
+}
+
+/*
+ * For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=No.
+ */
+static struct iscsi_datain_req *iscsit_set_datain_values_no_and_no(
+ struct iscsi_cmd *cmd,
+ struct iscsi_datain *datain)
+{
+ u32 read_data_done, read_data_left, seq_send_order;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_datain_req *dr;
+ struct iscsi_pdu *pdu;
+ struct iscsi_seq *seq = NULL;
+
+ dr = iscsit_get_datain_req(cmd);
+ if (!dr)
+ return NULL;
+
+ if (dr->recovery && dr->generate_recovery_values) {
+ if (iscsit_create_recovery_datain_values_datasequenceinorder_no(
+ cmd, dr) < 0)
+ return NULL;
+
+ dr->generate_recovery_values = 0;
+ }
+
+ read_data_done = (!dr->recovery) ?
+ cmd->read_data_done : dr->read_data_done;
+ seq_send_order = (!dr->recovery) ?
+ cmd->seq_send_order : dr->seq_send_order;
+
+ read_data_left = (cmd->se_cmd.data_length - read_data_done);
+ if (!read_data_left) {
+ pr_err("ITT: 0x%08x read_data_left is zero!\n",
+ cmd->init_task_tag);
+ return NULL;
+ }
+
+ seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);
+ if (!seq)
+ return NULL;
+
+ seq->sent = 1;
+
+ if (!dr->recovery && !seq->next_burst_len)
+ seq->first_datasn = cmd->data_sn;
+
+ pdu = iscsit_get_pdu_holder_for_seq(cmd, seq);
+ if (!pdu)
+ return NULL;
+
+ if (seq->pdu_send_order == seq->pdu_count) {
+ pdu->flags |= ISCSI_FLAG_CMD_FINAL;
+ if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
+ pdu->flags |= ISCSI_FLAG_DATA_ACK;
+
+ seq->next_burst_len = 0;
+ seq_send_order++;
+ } else
+ seq->next_burst_len += pdu->length;
+
+ if ((read_data_done + pdu->length) == cmd->se_cmd.data_length)
+ pdu->flags |= ISCSI_FLAG_DATA_STATUS;
+
+ pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
+ if (!dr->recovery) {
+ cmd->seq_send_order = seq_send_order;
+ cmd->read_data_done += pdu->length;
+ } else {
+ dr->seq_send_order = seq_send_order;
+ dr->read_data_done += pdu->length;
+ }
+
+ datain->flags = pdu->flags;
+ datain->length = pdu->length;
+ datain->offset = pdu->offset;
+ datain->data_sn = pdu->data_sn;
+
+ if (!dr->recovery) {
+ if (datain->flags & ISCSI_FLAG_CMD_FINAL)
+ seq->last_datasn = datain->data_sn;
+ if (datain->flags & ISCSI_FLAG_DATA_STATUS)
+ dr->dr_complete = DATAIN_COMPLETE_NORMAL;
+
+ return dr;
+ }
+
+ if (!dr->runlength) {
+ if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
+ dr->dr_complete =
+ (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+ DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+ DATAIN_COMPLETE_CONNECTION_RECOVERY;
+ }
+ } else {
+ if ((dr->begrun + dr->runlength) == dr->data_sn) {
+ dr->dr_complete =
+ (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
+ DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
+ DATAIN_COMPLETE_CONNECTION_RECOVERY;
+ }
+ }
+
+ return dr;
+}
+
+struct iscsi_datain_req *iscsit_get_datain_values(
+ struct iscsi_cmd *cmd,
+ struct iscsi_datain *datain)
+{
+ struct iscsi_conn *conn = cmd->conn;
+
+ if (conn->sess->sess_ops->DataSequenceInOrder &&
+ conn->sess->sess_ops->DataPDUInOrder)
+ return iscsit_set_datain_values_yes_and_yes(cmd, datain);
+ else if (!conn->sess->sess_ops->DataSequenceInOrder &&
+ conn->sess->sess_ops->DataPDUInOrder)
+ return iscsit_set_datain_values_no_and_yes(cmd, datain);
+ else if (conn->sess->sess_ops->DataSequenceInOrder &&
+ !conn->sess->sess_ops->DataPDUInOrder)
+ return iscsit_set_datain_values_yes_and_no(cmd, datain);
+ else if (!conn->sess->sess_ops->DataSequenceInOrder &&
+ !conn->sess->sess_ops->DataPDUInOrder)
+ return iscsit_set_datain_values_no_and_no(cmd, datain);
+
+ return NULL;
+}
diff --git a/drivers/target/iscsi/iscsi_target_datain_values.h b/drivers/target/iscsi/iscsi_target_datain_values.h
new file mode 100644
index 00000000000..646429ac5a0
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_datain_values.h
@@ -0,0 +1,12 @@
+#ifndef ISCSI_TARGET_DATAIN_VALUES_H
+#define ISCSI_TARGET_DATAIN_VALUES_H
+
+extern struct iscsi_datain_req *iscsit_allocate_datain_req(void);
+extern void iscsit_attach_datain_req(struct iscsi_cmd *, struct iscsi_datain_req *);
+extern void iscsit_free_datain_req(struct iscsi_cmd *, struct iscsi_datain_req *);
+extern void iscsit_free_all_datain_reqs(struct iscsi_cmd *);
+extern struct iscsi_datain_req *iscsit_get_datain_req(struct iscsi_cmd *);
+extern struct iscsi_datain_req *iscsit_get_datain_values(struct iscsi_cmd *,
+ struct iscsi_datain *);
+
+#endif /*** ISCSI_TARGET_DATAIN_VALUES_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_device.c b/drivers/target/iscsi/iscsi_target_device.c
new file mode 100644
index 00000000000..7087c736daa
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_device.c
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * This file contains the iSCSI Virtual Device and Disk Transport
+ * agnostic related functions.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <scsi/scsi_device.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+
+void iscsit_determine_maxcmdsn(struct iscsi_session *sess)
+{
+ struct se_node_acl *se_nacl;
+
+ /*
+ * This is a discovery session, the single queue slot was already
+ * assigned in iscsi_login_zero_tsih(). Since only Logout and
+ * Text Opcodes are allowed during discovery we do not have to worry
+ * about the HBA's queue depth here.
+ */
+ if (sess->sess_ops->SessionType)
+ return;
+
+ se_nacl = sess->se_sess->se_node_acl;
+
+ /*
+ * This is a normal session, set the Session's CmdSN window to the
+ * struct se_node_acl->queue_depth. The value in struct se_node_acl->queue_depth
+ * has already been validated as a legal value in
+ * core_set_queue_depth_for_node().
+ */
+ sess->cmdsn_window = se_nacl->queue_depth;
+ sess->max_cmd_sn = (sess->max_cmd_sn + se_nacl->queue_depth) - 1;
+}
+
+void iscsit_increment_maxcmdsn(struct iscsi_cmd *cmd, struct iscsi_session *sess)
+{
+ if (cmd->immediate_cmd || cmd->maxcmdsn_inc)
+ return;
+
+ cmd->maxcmdsn_inc = 1;
+
+ mutex_lock(&sess->cmdsn_mutex);
+ sess->max_cmd_sn += 1;
+ pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn);
+ mutex_unlock(&sess->cmdsn_mutex);
+}
+EXPORT_SYMBOL(iscsit_increment_maxcmdsn);
diff --git a/drivers/target/iscsi/iscsi_target_device.h b/drivers/target/iscsi/iscsi_target_device.h
new file mode 100644
index 00000000000..a0e2df9e809
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_device.h
@@ -0,0 +1,7 @@
+#ifndef ISCSI_TARGET_DEVICE_H
+#define ISCSI_TARGET_DEVICE_H
+
+extern void iscsit_determine_maxcmdsn(struct iscsi_session *);
+extern void iscsit_increment_maxcmdsn(struct iscsi_cmd *, struct iscsi_session *);
+
+#endif /* ISCSI_TARGET_DEVICE_H */
diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c
new file mode 100644
index 00000000000..0d1e6ee3e99
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_erl0.c
@@ -0,0 +1,1013 @@
+/******************************************************************************
+ * This file contains error recovery level zero functions used by
+ * the iSCSI Target driver.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_seq_pdu_list.h"
+#include "iscsi_target_tq.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+
+/*
+ * Used to set values in struct iscsi_cmd that iscsit_dataout_check_sequence()
+ * checks against to determine a PDU's Offset+Length is within the current
+ * DataOUT Sequence. Used for DataSequenceInOrder=Yes only.
+ */
+void iscsit_set_dataout_sequence_values(
+ struct iscsi_cmd *cmd)
+{
+ struct iscsi_conn *conn = cmd->conn;
+ /*
+ * Still set seq_start_offset and seq_end_offset for Unsolicited
+ * DataOUT, even if DataSequenceInOrder=No.
+ */
+ if (cmd->unsolicited_data) {
+ cmd->seq_start_offset = cmd->write_data_done;
+ cmd->seq_end_offset = (cmd->write_data_done +
+ ((cmd->se_cmd.data_length >
+ conn->sess->sess_ops->FirstBurstLength) ?
+ conn->sess->sess_ops->FirstBurstLength : cmd->se_cmd.data_length));
+ return;
+ }
+
+ if (!conn->sess->sess_ops->DataSequenceInOrder)
+ return;
+
+ if (!cmd->seq_start_offset && !cmd->seq_end_offset) {
+ cmd->seq_start_offset = cmd->write_data_done;
+ cmd->seq_end_offset = (cmd->se_cmd.data_length >
+ conn->sess->sess_ops->MaxBurstLength) ?
+ (cmd->write_data_done +
+ conn->sess->sess_ops->MaxBurstLength) : cmd->se_cmd.data_length;
+ } else {
+ cmd->seq_start_offset = cmd->seq_end_offset;
+ cmd->seq_end_offset = ((cmd->seq_end_offset +
+ conn->sess->sess_ops->MaxBurstLength) >=
+ cmd->se_cmd.data_length) ? cmd->se_cmd.data_length :
+ (cmd->seq_end_offset +
+ conn->sess->sess_ops->MaxBurstLength);
+ }
+}
+
+static int iscsit_dataout_within_command_recovery_check(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_data *hdr = (struct iscsi_data *) buf;
+ u32 payload_length = ntoh24(hdr->dlength);
+
+ /*
+ * We do the within-command recovery checks here as it is
+ * the first function called in iscsi_check_pre_dataout().
+ * Basically, if we are in within-command recovery and
+ * the PDU does not contain the offset the sequence needs,
+ * dump the payload.
+ *
+ * This only applies to DataPDUInOrder=Yes, for
+ * DataPDUInOrder=No we only re-request the failed PDU
+ * and check that all PDUs in a sequence are received
+ * upon end of sequence.
+ */
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+ if ((cmd->cmd_flags & ICF_WITHIN_COMMAND_RECOVERY) &&
+ cmd->write_data_done != be32_to_cpu(hdr->offset))
+ goto dump;
+
+ cmd->cmd_flags &= ~ICF_WITHIN_COMMAND_RECOVERY;
+ } else {
+ struct iscsi_seq *seq;
+
+ seq = iscsit_get_seq_holder(cmd, be32_to_cpu(hdr->offset),
+ payload_length);
+ if (!seq)
+ return DATAOUT_CANNOT_RECOVER;
+ /*
+ * Set the struct iscsi_seq pointer to reuse later.
+ */
+ cmd->seq_ptr = seq;
+
+ if (conn->sess->sess_ops->DataPDUInOrder) {
+ if (seq->status ==
+ DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY &&
+ (seq->offset != be32_to_cpu(hdr->offset) ||
+ seq->data_sn != be32_to_cpu(hdr->datasn)))
+ goto dump;
+ } else {
+ if (seq->status ==
+ DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY &&
+ seq->data_sn != be32_to_cpu(hdr->datasn))
+ goto dump;
+ }
+
+ if (seq->status == DATAOUT_SEQUENCE_COMPLETE)
+ goto dump;
+
+ if (seq->status != DATAOUT_SEQUENCE_COMPLETE)
+ seq->status = 0;
+ }
+
+ return DATAOUT_NORMAL;
+
+dump:
+ pr_err("Dumping DataOUT PDU Offset: %u Length: %d DataSN:"
+ " 0x%08x\n", hdr->offset, payload_length, hdr->datasn);
+ return iscsit_dump_data_payload(conn, payload_length, 1);
+}
+
+static int iscsit_dataout_check_unsolicited_sequence(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ u32 first_burst_len;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_data *hdr = (struct iscsi_data *) buf;
+ u32 payload_length = ntoh24(hdr->dlength);
+
+
+ if ((be32_to_cpu(hdr->offset) < cmd->seq_start_offset) ||
+ ((be32_to_cpu(hdr->offset) + payload_length) > cmd->seq_end_offset)) {
+ pr_err("Command ITT: 0x%08x with Offset: %u,"
+ " Length: %u outside of Unsolicited Sequence %u:%u while"
+ " DataSequenceInOrder=Yes.\n", cmd->init_task_tag,
+ be32_to_cpu(hdr->offset), payload_length, cmd->seq_start_offset,
+ cmd->seq_end_offset);
+ return DATAOUT_CANNOT_RECOVER;
+ }
+
+ first_burst_len = (cmd->first_burst_len + payload_length);
+
+ if (first_burst_len > conn->sess->sess_ops->FirstBurstLength) {
+ pr_err("Total %u bytes exceeds FirstBurstLength: %u"
+ " for this Unsolicited DataOut Burst.\n",
+ first_burst_len, conn->sess->sess_ops->FirstBurstLength);
+ transport_send_check_condition_and_sense(&cmd->se_cmd,
+ TCM_INCORRECT_AMOUNT_OF_DATA, 0);
+ return DATAOUT_CANNOT_RECOVER;
+ }
+
+ /*
+ * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity
+ * checks for the current Unsolicited DataOUT Sequence.
+ */
+ if (hdr->flags & ISCSI_FLAG_CMD_FINAL) {
+ /*
+ * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of
+ * sequence checks are handled in
+ * iscsit_dataout_datapduinorder_no_fbit().
+ */
+ if (!conn->sess->sess_ops->DataPDUInOrder)
+ goto out;
+
+ if ((first_burst_len != cmd->se_cmd.data_length) &&
+ (first_burst_len != conn->sess->sess_ops->FirstBurstLength)) {
+ pr_err("Unsolicited non-immediate data"
+ " received %u does not equal FirstBurstLength: %u, and"
+ " does not equal ExpXferLen %u.\n", first_burst_len,
+ conn->sess->sess_ops->FirstBurstLength,
+ cmd->se_cmd.data_length);
+ transport_send_check_condition_and_sense(&cmd->se_cmd,
+ TCM_INCORRECT_AMOUNT_OF_DATA, 0);
+ return DATAOUT_CANNOT_RECOVER;
+ }
+ } else {
+ if (first_burst_len == conn->sess->sess_ops->FirstBurstLength) {
+ pr_err("Command ITT: 0x%08x reached"
+ " FirstBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol"
+ " error.\n", cmd->init_task_tag,
+ conn->sess->sess_ops->FirstBurstLength);
+ return DATAOUT_CANNOT_RECOVER;
+ }
+ if (first_burst_len == cmd->se_cmd.data_length) {
+ pr_err("Command ITT: 0x%08x reached"
+ " ExpXferLen: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol"
+ " error.\n", cmd->init_task_tag, cmd->se_cmd.data_length);
+ return DATAOUT_CANNOT_RECOVER;
+ }
+ }
+
+out:
+ return DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_check_sequence(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ u32 next_burst_len;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_seq *seq = NULL;
+ struct iscsi_data *hdr = (struct iscsi_data *) buf;
+ u32 payload_length = ntoh24(hdr->dlength);
+
+ /*
+ * For DataSequenceInOrder=Yes: Check that the offset and offset+length
+ * is within range as defined by iscsi_set_dataout_sequence_values().
+ *
+ * For DataSequenceInOrder=No: Check that an struct iscsi_seq exists for
+ * offset+length tuple.
+ */
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+ /*
+ * Due to possibility of recovery DataOUT sent by the initiator
+ * fullfilling an Recovery R2T, it's best to just dump the
+ * payload here, instead of erroring out.
+ */
+ if ((be32_to_cpu(hdr->offset) < cmd->seq_start_offset) ||
+ ((be32_to_cpu(hdr->offset) + payload_length) > cmd->seq_end_offset)) {
+ pr_err("Command ITT: 0x%08x with Offset: %u,"
+ " Length: %u outside of Sequence %u:%u while"
+ " DataSequenceInOrder=Yes.\n", cmd->init_task_tag,
+ be32_to_cpu(hdr->offset), payload_length, cmd->seq_start_offset,
+ cmd->seq_end_offset);
+
+ if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
+ return DATAOUT_CANNOT_RECOVER;
+ return DATAOUT_WITHIN_COMMAND_RECOVERY;
+ }
+
+ next_burst_len = (cmd->next_burst_len + payload_length);
+ } else {
+ seq = iscsit_get_seq_holder(cmd, be32_to_cpu(hdr->offset),
+ payload_length);
+ if (!seq)
+ return DATAOUT_CANNOT_RECOVER;
+ /*
+ * Set the struct iscsi_seq pointer to reuse later.
+ */
+ cmd->seq_ptr = seq;
+
+ if (seq->status == DATAOUT_SEQUENCE_COMPLETE) {
+ if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
+ return DATAOUT_CANNOT_RECOVER;
+ return DATAOUT_WITHIN_COMMAND_RECOVERY;
+ }
+
+ next_burst_len = (seq->next_burst_len + payload_length);
+ }
+
+ if (next_burst_len > conn->sess->sess_ops->MaxBurstLength) {
+ pr_err("Command ITT: 0x%08x, NextBurstLength: %u and"
+ " Length: %u exceeds MaxBurstLength: %u. protocol"
+ " error.\n", cmd->init_task_tag,
+ (next_burst_len - payload_length),
+ payload_length, conn->sess->sess_ops->MaxBurstLength);
+ return DATAOUT_CANNOT_RECOVER;
+ }
+
+ /*
+ * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity
+ * checks for the current DataOUT Sequence.
+ */
+ if (hdr->flags & ISCSI_FLAG_CMD_FINAL) {
+ /*
+ * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of
+ * sequence checks are handled in
+ * iscsit_dataout_datapduinorder_no_fbit().
+ */
+ if (!conn->sess->sess_ops->DataPDUInOrder)
+ goto out;
+
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+ if ((next_burst_len <
+ conn->sess->sess_ops->MaxBurstLength) &&
+ ((cmd->write_data_done + payload_length) <
+ cmd->se_cmd.data_length)) {
+ pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL"
+ " before end of DataOUT sequence, protocol"
+ " error.\n", cmd->init_task_tag);
+ return DATAOUT_CANNOT_RECOVER;
+ }
+ } else {
+ if (next_burst_len < seq->xfer_len) {
+ pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL"
+ " before end of DataOUT sequence, protocol"
+ " error.\n", cmd->init_task_tag);
+ return DATAOUT_CANNOT_RECOVER;
+ }
+ }
+ } else {
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+ if (next_burst_len ==
+ conn->sess->sess_ops->MaxBurstLength) {
+ pr_err("Command ITT: 0x%08x reached"
+ " MaxBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is"
+ " not set, protocol error.", cmd->init_task_tag,
+ conn->sess->sess_ops->MaxBurstLength);
+ return DATAOUT_CANNOT_RECOVER;
+ }
+ if ((cmd->write_data_done + payload_length) ==
+ cmd->se_cmd.data_length) {
+ pr_err("Command ITT: 0x%08x reached"
+ " last DataOUT PDU in sequence but ISCSI_FLAG_"
+ "CMD_FINAL is not set, protocol error.\n",
+ cmd->init_task_tag);
+ return DATAOUT_CANNOT_RECOVER;
+ }
+ } else {
+ if (next_burst_len == seq->xfer_len) {
+ pr_err("Command ITT: 0x%08x reached"
+ " last DataOUT PDU in sequence but ISCSI_FLAG_"
+ "CMD_FINAL is not set, protocol error.\n",
+ cmd->init_task_tag);
+ return DATAOUT_CANNOT_RECOVER;
+ }
+ }
+ }
+
+out:
+ return DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_check_datasn(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ int dump = 0, recovery = 0;
+ u32 data_sn = 0;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_data *hdr = (struct iscsi_data *) buf;
+ u32 payload_length = ntoh24(hdr->dlength);
+
+ /*
+ * Considering the target has no method of re-requesting DataOUT
+ * by DataSN, if we receieve a greater DataSN than expected we
+ * assume the functions for DataPDUInOrder=[Yes,No] below will
+ * handle it.
+ *
+ * If the DataSN is less than expected, dump the payload.
+ */
+ if (conn->sess->sess_ops->DataSequenceInOrder)
+ data_sn = cmd->data_sn;
+ else {
+ struct iscsi_seq *seq = cmd->seq_ptr;
+ data_sn = seq->data_sn;
+ }
+
+ if (be32_to_cpu(hdr->datasn) > data_sn) {
+ pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x"
+ " higher than expected 0x%08x.\n", cmd->init_task_tag,
+ be32_to_cpu(hdr->datasn), data_sn);
+ recovery = 1;
+ goto recover;
+ } else if (be32_to_cpu(hdr->datasn) < data_sn) {
+ pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x"
+ " lower than expected 0x%08x, discarding payload.\n",
+ cmd->init_task_tag, be32_to_cpu(hdr->datasn), data_sn);
+ dump = 1;
+ goto dump;
+ }
+
+ return DATAOUT_NORMAL;
+
+recover:
+ if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+ pr_err("Unable to perform within-command recovery"
+ " while ERL=0.\n");
+ return DATAOUT_CANNOT_RECOVER;
+ }
+dump:
+ if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
+ return DATAOUT_CANNOT_RECOVER;
+
+ return (recovery || dump) ? DATAOUT_WITHIN_COMMAND_RECOVERY :
+ DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_pre_datapduinorder_yes(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ int dump = 0, recovery = 0;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_data *hdr = (struct iscsi_data *) buf;
+ u32 payload_length = ntoh24(hdr->dlength);
+
+ /*
+ * For DataSequenceInOrder=Yes: If the offset is greater than the global
+ * DataPDUInOrder=Yes offset counter in struct iscsi_cmd a protcol error has
+ * occurred and fail the connection.
+ *
+ * For DataSequenceInOrder=No: If the offset is greater than the per
+ * sequence DataPDUInOrder=Yes offset counter in struct iscsi_seq a protocol
+ * error has occurred and fail the connection.
+ */
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+ if (be32_to_cpu(hdr->offset) != cmd->write_data_done) {
+ pr_err("Command ITT: 0x%08x, received offset"
+ " %u different than expected %u.\n", cmd->init_task_tag,
+ be32_to_cpu(hdr->offset), cmd->write_data_done);
+ recovery = 1;
+ goto recover;
+ }
+ } else {
+ struct iscsi_seq *seq = cmd->seq_ptr;
+
+ if (be32_to_cpu(hdr->offset) > seq->offset) {
+ pr_err("Command ITT: 0x%08x, received offset"
+ " %u greater than expected %u.\n", cmd->init_task_tag,
+ be32_to_cpu(hdr->offset), seq->offset);
+ recovery = 1;
+ goto recover;
+ } else if (be32_to_cpu(hdr->offset) < seq->offset) {
+ pr_err("Command ITT: 0x%08x, received offset"
+ " %u less than expected %u, discarding payload.\n",
+ cmd->init_task_tag, be32_to_cpu(hdr->offset),
+ seq->offset);
+ dump = 1;
+ goto dump;
+ }
+ }
+
+ return DATAOUT_NORMAL;
+
+recover:
+ if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+ pr_err("Unable to perform within-command recovery"
+ " while ERL=0.\n");
+ return DATAOUT_CANNOT_RECOVER;
+ }
+dump:
+ if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
+ return DATAOUT_CANNOT_RECOVER;
+
+ return (recovery) ? iscsit_recover_dataout_sequence(cmd,
+ be32_to_cpu(hdr->offset), payload_length) :
+ (dump) ? DATAOUT_WITHIN_COMMAND_RECOVERY : DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_pre_datapduinorder_no(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ struct iscsi_pdu *pdu;
+ struct iscsi_data *hdr = (struct iscsi_data *) buf;
+ u32 payload_length = ntoh24(hdr->dlength);
+
+ pdu = iscsit_get_pdu_holder(cmd, be32_to_cpu(hdr->offset),
+ payload_length);
+ if (!pdu)
+ return DATAOUT_CANNOT_RECOVER;
+
+ cmd->pdu_ptr = pdu;
+
+ switch (pdu->status) {
+ case ISCSI_PDU_NOT_RECEIVED:
+ case ISCSI_PDU_CRC_FAILED:
+ case ISCSI_PDU_TIMED_OUT:
+ break;
+ case ISCSI_PDU_RECEIVED_OK:
+ pr_err("Command ITT: 0x%08x received already gotten"
+ " Offset: %u, Length: %u\n", cmd->init_task_tag,
+ be32_to_cpu(hdr->offset), payload_length);
+ return iscsit_dump_data_payload(cmd->conn, payload_length, 1);
+ default:
+ return DATAOUT_CANNOT_RECOVER;
+ }
+
+ return DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_update_r2t(struct iscsi_cmd *cmd, u32 offset, u32 length)
+{
+ struct iscsi_r2t *r2t;
+
+ if (cmd->unsolicited_data)
+ return 0;
+
+ r2t = iscsit_get_r2t_for_eos(cmd, offset, length);
+ if (!r2t)
+ return -1;
+
+ spin_lock_bh(&cmd->r2t_lock);
+ r2t->seq_complete = 1;
+ cmd->outstanding_r2ts--;
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ return 0;
+}
+
+static int iscsit_dataout_update_datapduinorder_no(
+ struct iscsi_cmd *cmd,
+ u32 data_sn,
+ int f_bit)
+{
+ int ret = 0;
+ struct iscsi_pdu *pdu = cmd->pdu_ptr;
+
+ pdu->data_sn = data_sn;
+
+ switch (pdu->status) {
+ case ISCSI_PDU_NOT_RECEIVED:
+ pdu->status = ISCSI_PDU_RECEIVED_OK;
+ break;
+ case ISCSI_PDU_CRC_FAILED:
+ pdu->status = ISCSI_PDU_RECEIVED_OK;
+ break;
+ case ISCSI_PDU_TIMED_OUT:
+ pdu->status = ISCSI_PDU_RECEIVED_OK;
+ break;
+ default:
+ return DATAOUT_CANNOT_RECOVER;
+ }
+
+ if (f_bit) {
+ ret = iscsit_dataout_datapduinorder_no_fbit(cmd, pdu);
+ if (ret == DATAOUT_CANNOT_RECOVER)
+ return ret;
+ }
+
+ return DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_post_crc_passed(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ int ret, send_r2t = 0;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_seq *seq = NULL;
+ struct iscsi_data *hdr = (struct iscsi_data *) buf;
+ u32 payload_length = ntoh24(hdr->dlength);
+
+ if (cmd->unsolicited_data) {
+ if ((cmd->first_burst_len + payload_length) ==
+ conn->sess->sess_ops->FirstBurstLength) {
+ if (iscsit_dataout_update_r2t(cmd, be32_to_cpu(hdr->offset),
+ payload_length) < 0)
+ return DATAOUT_CANNOT_RECOVER;
+ send_r2t = 1;
+ }
+
+ if (!conn->sess->sess_ops->DataPDUInOrder) {
+ ret = iscsit_dataout_update_datapduinorder_no(cmd,
+ be32_to_cpu(hdr->datasn),
+ (hdr->flags & ISCSI_FLAG_CMD_FINAL));
+ if (ret == DATAOUT_CANNOT_RECOVER)
+ return ret;
+ }
+
+ cmd->first_burst_len += payload_length;
+
+ if (conn->sess->sess_ops->DataSequenceInOrder)
+ cmd->data_sn++;
+ else {
+ seq = cmd->seq_ptr;
+ seq->data_sn++;
+ seq->offset += payload_length;
+ }
+
+ if (send_r2t) {
+ if (seq)
+ seq->status = DATAOUT_SEQUENCE_COMPLETE;
+ cmd->first_burst_len = 0;
+ cmd->unsolicited_data = 0;
+ }
+ } else {
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+ if ((cmd->next_burst_len + payload_length) ==
+ conn->sess->sess_ops->MaxBurstLength) {
+ if (iscsit_dataout_update_r2t(cmd,
+ be32_to_cpu(hdr->offset),
+ payload_length) < 0)
+ return DATAOUT_CANNOT_RECOVER;
+ send_r2t = 1;
+ }
+
+ if (!conn->sess->sess_ops->DataPDUInOrder) {
+ ret = iscsit_dataout_update_datapduinorder_no(
+ cmd, be32_to_cpu(hdr->datasn),
+ (hdr->flags & ISCSI_FLAG_CMD_FINAL));
+ if (ret == DATAOUT_CANNOT_RECOVER)
+ return ret;
+ }
+
+ cmd->next_burst_len += payload_length;
+ cmd->data_sn++;
+
+ if (send_r2t)
+ cmd->next_burst_len = 0;
+ } else {
+ seq = cmd->seq_ptr;
+
+ if ((seq->next_burst_len + payload_length) ==
+ seq->xfer_len) {
+ if (iscsit_dataout_update_r2t(cmd,
+ be32_to_cpu(hdr->offset),
+ payload_length) < 0)
+ return DATAOUT_CANNOT_RECOVER;
+ send_r2t = 1;
+ }
+
+ if (!conn->sess->sess_ops->DataPDUInOrder) {
+ ret = iscsit_dataout_update_datapduinorder_no(
+ cmd, be32_to_cpu(hdr->datasn),
+ (hdr->flags & ISCSI_FLAG_CMD_FINAL));
+ if (ret == DATAOUT_CANNOT_RECOVER)
+ return ret;
+ }
+
+ seq->data_sn++;
+ seq->offset += payload_length;
+ seq->next_burst_len += payload_length;
+
+ if (send_r2t) {
+ seq->next_burst_len = 0;
+ seq->status = DATAOUT_SEQUENCE_COMPLETE;
+ }
+ }
+ }
+
+ if (send_r2t && conn->sess->sess_ops->DataSequenceInOrder)
+ cmd->data_sn = 0;
+
+ cmd->write_data_done += payload_length;
+
+ if (cmd->write_data_done == cmd->se_cmd.data_length)
+ return DATAOUT_SEND_TO_TRANSPORT;
+ else if (send_r2t)
+ return DATAOUT_SEND_R2T;
+ else
+ return DATAOUT_NORMAL;
+}
+
+static int iscsit_dataout_post_crc_failed(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_pdu *pdu;
+ struct iscsi_data *hdr = (struct iscsi_data *) buf;
+ u32 payload_length = ntoh24(hdr->dlength);
+
+ if (conn->sess->sess_ops->DataPDUInOrder)
+ goto recover;
+ /*
+ * The rest of this function is only called when DataPDUInOrder=No.
+ */
+ pdu = cmd->pdu_ptr;
+
+ switch (pdu->status) {
+ case ISCSI_PDU_NOT_RECEIVED:
+ pdu->status = ISCSI_PDU_CRC_FAILED;
+ break;
+ case ISCSI_PDU_CRC_FAILED:
+ break;
+ case ISCSI_PDU_TIMED_OUT:
+ pdu->status = ISCSI_PDU_CRC_FAILED;
+ break;
+ default:
+ return DATAOUT_CANNOT_RECOVER;
+ }
+
+recover:
+ return iscsit_recover_dataout_sequence(cmd, be32_to_cpu(hdr->offset),
+ payload_length);
+}
+
+/*
+ * Called from iscsit_handle_data_out() before DataOUT Payload is received
+ * and CRC computed.
+ */
+int iscsit_check_pre_dataout(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ int ret;
+ struct iscsi_conn *conn = cmd->conn;
+
+ ret = iscsit_dataout_within_command_recovery_check(cmd, buf);
+ if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
+ (ret == DATAOUT_CANNOT_RECOVER))
+ return ret;
+
+ ret = iscsit_dataout_check_datasn(cmd, buf);
+ if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
+ (ret == DATAOUT_CANNOT_RECOVER))
+ return ret;
+
+ if (cmd->unsolicited_data) {
+ ret = iscsit_dataout_check_unsolicited_sequence(cmd, buf);
+ if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
+ (ret == DATAOUT_CANNOT_RECOVER))
+ return ret;
+ } else {
+ ret = iscsit_dataout_check_sequence(cmd, buf);
+ if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
+ (ret == DATAOUT_CANNOT_RECOVER))
+ return ret;
+ }
+
+ return (conn->sess->sess_ops->DataPDUInOrder) ?
+ iscsit_dataout_pre_datapduinorder_yes(cmd, buf) :
+ iscsit_dataout_pre_datapduinorder_no(cmd, buf);
+}
+
+/*
+ * Called from iscsit_handle_data_out() after DataOUT Payload is received
+ * and CRC computed.
+ */
+int iscsit_check_post_dataout(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf,
+ u8 data_crc_failed)
+{
+ struct iscsi_conn *conn = cmd->conn;
+
+ cmd->dataout_timeout_retries = 0;
+
+ if (!data_crc_failed)
+ return iscsit_dataout_post_crc_passed(cmd, buf);
+ else {
+ if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
+ pr_err("Unable to recover from DataOUT CRC"
+ " failure while ERL=0, closing session.\n");
+ iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR,
+ buf);
+ return DATAOUT_CANNOT_RECOVER;
+ }
+
+ iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR, buf);
+ return iscsit_dataout_post_crc_failed(cmd, buf);
+ }
+}
+
+static void iscsit_handle_time2retain_timeout(unsigned long data)
+{
+ struct iscsi_session *sess = (struct iscsi_session *) data;
+ struct iscsi_portal_group *tpg = sess->tpg;
+ struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+
+ spin_lock_bh(&se_tpg->session_lock);
+ if (sess->time2retain_timer_flags & ISCSI_TF_STOP) {
+ spin_unlock_bh(&se_tpg->session_lock);
+ return;
+ }
+ if (atomic_read(&sess->session_reinstatement)) {
+ pr_err("Exiting Time2Retain handler because"
+ " session_reinstatement=1\n");
+ spin_unlock_bh(&se_tpg->session_lock);
+ return;
+ }
+ sess->time2retain_timer_flags |= ISCSI_TF_EXPIRED;
+
+ pr_err("Time2Retain timer expired for SID: %u, cleaning up"
+ " iSCSI session.\n", sess->sid);
+ {
+ struct iscsi_tiqn *tiqn = tpg->tpg_tiqn;
+
+ if (tiqn) {
+ spin_lock(&tiqn->sess_err_stats.lock);
+ strcpy(tiqn->sess_err_stats.last_sess_fail_rem_name,
+ (void *)sess->sess_ops->InitiatorName);
+ tiqn->sess_err_stats.last_sess_failure_type =
+ ISCSI_SESS_ERR_CXN_TIMEOUT;
+ tiqn->sess_err_stats.cxn_timeout_errors++;
+ atomic_long_inc(&sess->conn_timeout_errors);
+ spin_unlock(&tiqn->sess_err_stats.lock);
+ }
+ }
+
+ spin_unlock_bh(&se_tpg->session_lock);
+ target_put_session(sess->se_sess);
+}
+
+void iscsit_start_time2retain_handler(struct iscsi_session *sess)
+{
+ int tpg_active;
+ /*
+ * Only start Time2Retain timer when the associated TPG is still in
+ * an ACTIVE (eg: not disabled or shutdown) state.
+ */
+ spin_lock(&sess->tpg->tpg_state_lock);
+ tpg_active = (sess->tpg->tpg_state == TPG_STATE_ACTIVE);
+ spin_unlock(&sess->tpg->tpg_state_lock);
+
+ if (!tpg_active)
+ return;
+
+ if (sess->time2retain_timer_flags & ISCSI_TF_RUNNING)
+ return;
+
+ pr_debug("Starting Time2Retain timer for %u seconds on"
+ " SID: %u\n", sess->sess_ops->DefaultTime2Retain, sess->sid);
+
+ init_timer(&sess->time2retain_timer);
+ sess->time2retain_timer.expires =
+ (get_jiffies_64() + sess->sess_ops->DefaultTime2Retain * HZ);
+ sess->time2retain_timer.data = (unsigned long)sess;
+ sess->time2retain_timer.function = iscsit_handle_time2retain_timeout;
+ sess->time2retain_timer_flags &= ~ISCSI_TF_STOP;
+ sess->time2retain_timer_flags |= ISCSI_TF_RUNNING;
+ add_timer(&sess->time2retain_timer);
+}
+
+/*
+ * Called with spin_lock_bh(&struct se_portal_group->session_lock) held
+ */
+int iscsit_stop_time2retain_timer(struct iscsi_session *sess)
+{
+ struct iscsi_portal_group *tpg = sess->tpg;
+ struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+
+ if (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)
+ return -1;
+
+ if (!(sess->time2retain_timer_flags & ISCSI_TF_RUNNING))
+ return 0;
+
+ sess->time2retain_timer_flags |= ISCSI_TF_STOP;
+ spin_unlock(&se_tpg->session_lock);
+
+ del_timer_sync(&sess->time2retain_timer);
+
+ spin_lock(&se_tpg->session_lock);
+ sess->time2retain_timer_flags &= ~ISCSI_TF_RUNNING;
+ pr_debug("Stopped Time2Retain Timer for SID: %u\n",
+ sess->sid);
+ return 0;
+}
+
+void iscsit_connection_reinstatement_rcfr(struct iscsi_conn *conn)
+{
+ spin_lock_bh(&conn->state_lock);
+ if (atomic_read(&conn->connection_exit)) {
+ spin_unlock_bh(&conn->state_lock);
+ goto sleep;
+ }
+
+ if (atomic_read(&conn->transport_failed)) {
+ spin_unlock_bh(&conn->state_lock);
+ goto sleep;
+ }
+ spin_unlock_bh(&conn->state_lock);
+
+ iscsi_thread_set_force_reinstatement(conn);
+
+sleep:
+ wait_for_completion(&conn->conn_wait_rcfr_comp);
+ complete(&conn->conn_post_wait_comp);
+}
+
+void iscsit_cause_connection_reinstatement(struct iscsi_conn *conn, int sleep)
+{
+ spin_lock_bh(&conn->state_lock);
+ if (atomic_read(&conn->connection_exit)) {
+ spin_unlock_bh(&conn->state_lock);
+ return;
+ }
+
+ if (atomic_read(&conn->transport_failed)) {
+ spin_unlock_bh(&conn->state_lock);
+ return;
+ }
+
+ if (atomic_read(&conn->connection_reinstatement)) {
+ spin_unlock_bh(&conn->state_lock);
+ return;
+ }
+
+ if (iscsi_thread_set_force_reinstatement(conn) < 0) {
+ spin_unlock_bh(&conn->state_lock);
+ return;
+ }
+
+ atomic_set(&conn->connection_reinstatement, 1);
+ if (!sleep) {
+ spin_unlock_bh(&conn->state_lock);
+ return;
+ }
+
+ atomic_set(&conn->sleep_on_conn_wait_comp, 1);
+ spin_unlock_bh(&conn->state_lock);
+
+ wait_for_completion(&conn->conn_wait_comp);
+ complete(&conn->conn_post_wait_comp);
+}
+EXPORT_SYMBOL(iscsit_cause_connection_reinstatement);
+
+void iscsit_fall_back_to_erl0(struct iscsi_session *sess)
+{
+ pr_debug("Falling back to ErrorRecoveryLevel=0 for SID:"
+ " %u\n", sess->sid);
+
+ atomic_set(&sess->session_fall_back_to_erl0, 1);
+}
+
+static void iscsit_handle_connection_cleanup(struct iscsi_conn *conn)
+{
+ struct iscsi_session *sess = conn->sess;
+
+ if ((sess->sess_ops->ErrorRecoveryLevel == 2) &&
+ !atomic_read(&sess->session_reinstatement) &&
+ !atomic_read(&sess->session_fall_back_to_erl0))
+ iscsit_connection_recovery_transport_reset(conn);
+ else {
+ pr_debug("Performing cleanup for failed iSCSI"
+ " Connection ID: %hu from %s\n", conn->cid,
+ sess->sess_ops->InitiatorName);
+ iscsit_close_connection(conn);
+ }
+}
+
+void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn)
+{
+ spin_lock_bh(&conn->state_lock);
+ if (atomic_read(&conn->connection_exit)) {
+ spin_unlock_bh(&conn->state_lock);
+ return;
+ }
+ atomic_set(&conn->connection_exit, 1);
+
+ if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) {
+ spin_unlock_bh(&conn->state_lock);
+ iscsit_close_connection(conn);
+ return;
+ }
+
+ if (conn->conn_state == TARG_CONN_STATE_CLEANUP_WAIT) {
+ spin_unlock_bh(&conn->state_lock);
+ return;
+ }
+
+ pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n");
+ conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT;
+ spin_unlock_bh(&conn->state_lock);
+
+ iscsit_handle_connection_cleanup(conn);
+}
+
+/*
+ * This is the simple function that makes the magic of
+ * sync and steering happen in the follow paradoxical order:
+ *
+ * 0) Receive conn->of_marker (bytes left until next OFMarker)
+ * bytes into an offload buffer. When we pass the exact number
+ * of bytes in conn->of_marker, iscsit_dump_data_payload() and hence
+ * rx_data() will automatically receive the identical u32 marker
+ * values and store it in conn->of_marker_offset;
+ * 1) Now conn->of_marker_offset will contain the offset to the start
+ * of the next iSCSI PDU. Dump these remaining bytes into another
+ * offload buffer.
+ * 2) We are done!
+ * Next byte in the TCP stream will contain the next iSCSI PDU!
+ * Cool Huh?!
+ */
+int iscsit_recover_from_unknown_opcode(struct iscsi_conn *conn)
+{
+ /*
+ * Make sure the remaining bytes to next maker is a sane value.
+ */
+ if (conn->of_marker > (conn->conn_ops->OFMarkInt * 4)) {
+ pr_err("Remaining bytes to OFMarker: %u exceeds"
+ " OFMarkInt bytes: %u.\n", conn->of_marker,
+ conn->conn_ops->OFMarkInt * 4);
+ return -1;
+ }
+
+ pr_debug("Advancing %u bytes in TCP stream to get to the"
+ " next OFMarker.\n", conn->of_marker);
+
+ if (iscsit_dump_data_payload(conn, conn->of_marker, 0) < 0)
+ return -1;
+
+ /*
+ * Make sure the offset marker we retrived is a valid value.
+ */
+ if (conn->of_marker_offset > (ISCSI_HDR_LEN + (ISCSI_CRC_LEN * 2) +
+ conn->conn_ops->MaxRecvDataSegmentLength)) {
+ pr_err("OfMarker offset value: %u exceeds limit.\n",
+ conn->of_marker_offset);
+ return -1;
+ }
+
+ pr_debug("Discarding %u bytes of TCP stream to get to the"
+ " next iSCSI Opcode.\n", conn->of_marker_offset);
+
+ if (iscsit_dump_data_payload(conn, conn->of_marker_offset, 0) < 0)
+ return -1;
+
+ return 0;
+}
diff --git a/drivers/target/iscsi/iscsi_target_erl0.h b/drivers/target/iscsi/iscsi_target_erl0.h
new file mode 100644
index 00000000000..21acc9a0637
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_erl0.h
@@ -0,0 +1,15 @@
+#ifndef ISCSI_TARGET_ERL0_H
+#define ISCSI_TARGET_ERL0_H
+
+extern void iscsit_set_dataout_sequence_values(struct iscsi_cmd *);
+extern int iscsit_check_pre_dataout(struct iscsi_cmd *, unsigned char *);
+extern int iscsit_check_post_dataout(struct iscsi_cmd *, unsigned char *, u8);
+extern void iscsit_start_time2retain_handler(struct iscsi_session *);
+extern int iscsit_stop_time2retain_timer(struct iscsi_session *);
+extern void iscsit_connection_reinstatement_rcfr(struct iscsi_conn *);
+extern void iscsit_cause_connection_reinstatement(struct iscsi_conn *, int);
+extern void iscsit_fall_back_to_erl0(struct iscsi_session *);
+extern void iscsit_take_action_for_connection_exit(struct iscsi_conn *);
+extern int iscsit_recover_from_unknown_opcode(struct iscsi_conn *);
+
+#endif /*** ISCSI_TARGET_ERL0_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c
new file mode 100644
index 00000000000..cda4d80cfae
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_erl1.c
@@ -0,0 +1,1294 @@
+/*******************************************************************************
+ * This file contains error recovery level one used by the iSCSI Target driver.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/list.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/iscsi/iscsi_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_seq_pdu_list.h"
+#include "iscsi_target_datain_values.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target.h"
+
+#define OFFLOAD_BUF_SIZE 32768
+
+/*
+ * Used to dump excess datain payload for certain error recovery
+ * situations. Receive in OFFLOAD_BUF_SIZE max of datain per rx_data().
+ *
+ * dump_padding_digest denotes if padding and data digests need
+ * to be dumped.
+ */
+int iscsit_dump_data_payload(
+ struct iscsi_conn *conn,
+ u32 buf_len,
+ int dump_padding_digest)
+{
+ char *buf, pad_bytes[4];
+ int ret = DATAOUT_WITHIN_COMMAND_RECOVERY, rx_got;
+ u32 length, padding, offset = 0, size;
+ struct kvec iov;
+
+ if (conn->sess->sess_ops->RDMAExtensions)
+ return 0;
+
+ length = (buf_len > OFFLOAD_BUF_SIZE) ? OFFLOAD_BUF_SIZE : buf_len;
+
+ buf = kzalloc(length, GFP_ATOMIC);
+ if (!buf) {
+ pr_err("Unable to allocate %u bytes for offload"
+ " buffer.\n", length);
+ return -1;
+ }
+ memset(&iov, 0, sizeof(struct kvec));
+
+ while (offset < buf_len) {
+ size = ((offset + length) > buf_len) ?
+ (buf_len - offset) : length;
+
+ iov.iov_len = size;
+ iov.iov_base = buf;
+
+ rx_got = rx_data(conn, &iov, 1, size);
+ if (rx_got != size) {
+ ret = DATAOUT_CANNOT_RECOVER;
+ goto out;
+ }
+
+ offset += size;
+ }
+
+ if (!dump_padding_digest)
+ goto out;
+
+ padding = ((-buf_len) & 3);
+ if (padding != 0) {
+ iov.iov_len = padding;
+ iov.iov_base = pad_bytes;
+
+ rx_got = rx_data(conn, &iov, 1, padding);
+ if (rx_got != padding) {
+ ret = DATAOUT_CANNOT_RECOVER;
+ goto out;
+ }
+ }
+
+ if (conn->conn_ops->DataDigest) {
+ u32 data_crc;
+
+ iov.iov_len = ISCSI_CRC_LEN;
+ iov.iov_base = &data_crc;
+
+ rx_got = rx_data(conn, &iov, 1, ISCSI_CRC_LEN);
+ if (rx_got != ISCSI_CRC_LEN) {
+ ret = DATAOUT_CANNOT_RECOVER;
+ goto out;
+ }
+ }
+
+out:
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * Used for retransmitting R2Ts from a R2T SNACK request.
+ */
+static int iscsit_send_recovery_r2t_for_snack(
+ struct iscsi_cmd *cmd,
+ struct iscsi_r2t *r2t)
+{
+ /*
+ * If the struct iscsi_r2t has not been sent yet, we can safely
+ * ignore retransmission
+ * of the R2TSN in question.
+ */
+ spin_lock_bh(&cmd->r2t_lock);
+ if (!r2t->sent_r2t) {
+ spin_unlock_bh(&cmd->r2t_lock);
+ return 0;
+ }
+ r2t->sent_r2t = 0;
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ iscsit_add_cmd_to_immediate_queue(cmd, cmd->conn, ISTATE_SEND_R2T);
+
+ return 0;
+}
+
+static int iscsit_handle_r2t_snack(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf,
+ u32 begrun,
+ u32 runlength)
+{
+ u32 last_r2tsn;
+ struct iscsi_r2t *r2t;
+
+ /*
+ * Make sure the initiator is not requesting retransmission
+ * of R2TSNs already acknowledged by a TMR TASK_REASSIGN.
+ */
+ if ((cmd->cmd_flags & ICF_GOT_DATACK_SNACK) &&
+ (begrun <= cmd->acked_data_sn)) {
+ pr_err("ITT: 0x%08x, R2T SNACK requesting"
+ " retransmission of R2TSN: 0x%08x to 0x%08x but already"
+ " acked to R2TSN: 0x%08x by TMR TASK_REASSIGN,"
+ " protocol error.\n", cmd->init_task_tag, begrun,
+ (begrun + runlength), cmd->acked_data_sn);
+
+ return iscsit_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
+ }
+
+ if (runlength) {
+ if ((begrun + runlength) > cmd->r2t_sn) {
+ pr_err("Command ITT: 0x%08x received R2T SNACK"
+ " with BegRun: 0x%08x, RunLength: 0x%08x, exceeds"
+ " current R2TSN: 0x%08x, protocol error.\n",
+ cmd->init_task_tag, begrun, runlength, cmd->r2t_sn);
+ return iscsit_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
+ }
+ last_r2tsn = (begrun + runlength);
+ } else
+ last_r2tsn = cmd->r2t_sn;
+
+ while (begrun < last_r2tsn) {
+ r2t = iscsit_get_holder_for_r2tsn(cmd, begrun);
+ if (!r2t)
+ return -1;
+ if (iscsit_send_recovery_r2t_for_snack(cmd, r2t) < 0)
+ return -1;
+
+ begrun++;
+ }
+
+ return 0;
+}
+
+/*
+ * Generates Offsets and NextBurstLength based on Begrun and Runlength
+ * carried in a Data SNACK or ExpDataSN in TMR TASK_REASSIGN.
+ *
+ * For DataSequenceInOrder=Yes and DataPDUInOrder=[Yes,No] only.
+ *
+ * FIXME: How is this handled for a RData SNACK?
+ */
+int iscsit_create_recovery_datain_values_datasequenceinorder_yes(
+ struct iscsi_cmd *cmd,
+ struct iscsi_datain_req *dr)
+{
+ u32 data_sn = 0, data_sn_count = 0;
+ u32 pdu_start = 0, seq_no = 0;
+ u32 begrun = dr->begrun;
+ struct iscsi_conn *conn = cmd->conn;
+
+ while (begrun > data_sn++) {
+ data_sn_count++;
+ if ((dr->next_burst_len +
+ conn->conn_ops->MaxRecvDataSegmentLength) <
+ conn->sess->sess_ops->MaxBurstLength) {
+ dr->read_data_done +=
+ conn->conn_ops->MaxRecvDataSegmentLength;
+ dr->next_burst_len +=
+ conn->conn_ops->MaxRecvDataSegmentLength;
+ } else {
+ dr->read_data_done +=
+ (conn->sess->sess_ops->MaxBurstLength -
+ dr->next_burst_len);
+ dr->next_burst_len = 0;
+ pdu_start += data_sn_count;
+ data_sn_count = 0;
+ seq_no++;
+ }
+ }
+
+ if (!conn->sess->sess_ops->DataPDUInOrder) {
+ cmd->seq_no = seq_no;
+ cmd->pdu_start = pdu_start;
+ cmd->pdu_send_order = data_sn_count;
+ }
+
+ return 0;
+}
+
+/*
+ * Generates Offsets and NextBurstLength based on Begrun and Runlength
+ * carried in a Data SNACK or ExpDataSN in TMR TASK_REASSIGN.
+ *
+ * For DataSequenceInOrder=No and DataPDUInOrder=[Yes,No] only.
+ *
+ * FIXME: How is this handled for a RData SNACK?
+ */
+int iscsit_create_recovery_datain_values_datasequenceinorder_no(
+ struct iscsi_cmd *cmd,
+ struct iscsi_datain_req *dr)
+{
+ int found_seq = 0, i;
+ u32 data_sn, read_data_done = 0, seq_send_order = 0;
+ u32 begrun = dr->begrun;
+ u32 runlength = dr->runlength;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_seq *first_seq = NULL, *seq = NULL;
+
+ if (!cmd->seq_list) {
+ pr_err("struct iscsi_cmd->seq_list is NULL!\n");
+ return -1;
+ }
+
+ /*
+ * Calculate read_data_done for all sequences containing a
+ * first_datasn and last_datasn less than the BegRun.
+ *
+ * Locate the struct iscsi_seq the BegRun lies within and calculate
+ * NextBurstLenghth up to the DataSN based on MaxRecvDataSegmentLength.
+ *
+ * Also use struct iscsi_seq->seq_send_order to determine where to start.
+ */
+ for (i = 0; i < cmd->seq_count; i++) {
+ seq = &cmd->seq_list[i];
+
+ if (!seq->seq_send_order)
+ first_seq = seq;
+
+ /*
+ * No data has been transferred for this DataIN sequence, so the
+ * seq->first_datasn and seq->last_datasn have not been set.
+ */
+ if (!seq->sent) {
+ pr_err("Ignoring non-sent sequence 0x%08x ->"
+ " 0x%08x\n\n", seq->first_datasn,
+ seq->last_datasn);
+ continue;
+ }
+
+ /*
+ * This DataIN sequence is precedes the received BegRun, add the
+ * total xfer_len of the sequence to read_data_done and reset
+ * seq->pdu_send_order.
+ */
+ if ((seq->first_datasn < begrun) &&
+ (seq->last_datasn < begrun)) {
+ pr_err("Pre BegRun sequence 0x%08x ->"
+ " 0x%08x\n", seq->first_datasn,
+ seq->last_datasn);
+
+ read_data_done += cmd->seq_list[i].xfer_len;
+ seq->next_burst_len = seq->pdu_send_order = 0;
+ continue;
+ }
+
+ /*
+ * The BegRun lies within this DataIN sequence.
+ */
+ if ((seq->first_datasn <= begrun) &&
+ (seq->last_datasn >= begrun)) {
+ pr_err("Found sequence begrun: 0x%08x in"
+ " 0x%08x -> 0x%08x\n", begrun,
+ seq->first_datasn, seq->last_datasn);
+
+ seq_send_order = seq->seq_send_order;
+ data_sn = seq->first_datasn;
+ seq->next_burst_len = seq->pdu_send_order = 0;
+ found_seq = 1;
+
+ /*
+ * For DataPDUInOrder=Yes, while the first DataSN of
+ * the sequence is less than the received BegRun, add
+ * the MaxRecvDataSegmentLength to read_data_done and
+ * to the sequence's next_burst_len;
+ *
+ * For DataPDUInOrder=No, while the first DataSN of the
+ * sequence is less than the received BegRun, find the
+ * struct iscsi_pdu of the DataSN in question and add the
+ * MaxRecvDataSegmentLength to read_data_done and to the
+ * sequence's next_burst_len;
+ */
+ if (conn->sess->sess_ops->DataPDUInOrder) {
+ while (data_sn < begrun) {
+ seq->pdu_send_order++;
+ read_data_done +=
+ conn->conn_ops->MaxRecvDataSegmentLength;
+ seq->next_burst_len +=
+ conn->conn_ops->MaxRecvDataSegmentLength;
+ data_sn++;
+ }
+ } else {
+ int j;
+ struct iscsi_pdu *pdu;
+
+ while (data_sn < begrun) {
+ seq->pdu_send_order++;
+
+ for (j = 0; j < seq->pdu_count; j++) {
+ pdu = &cmd->pdu_list[
+ seq->pdu_start + j];
+ if (pdu->data_sn == data_sn) {
+ read_data_done +=
+ pdu->length;
+ seq->next_burst_len +=
+ pdu->length;
+ }
+ }
+ data_sn++;
+ }
+ }
+ continue;
+ }
+
+ /*
+ * This DataIN sequence is larger than the received BegRun,
+ * reset seq->pdu_send_order and continue.
+ */
+ if ((seq->first_datasn > begrun) ||
+ (seq->last_datasn > begrun)) {
+ pr_err("Post BegRun sequence 0x%08x -> 0x%08x\n",
+ seq->first_datasn, seq->last_datasn);
+
+ seq->next_burst_len = seq->pdu_send_order = 0;
+ continue;
+ }
+ }
+
+ if (!found_seq) {
+ if (!begrun) {
+ if (!first_seq) {
+ pr_err("ITT: 0x%08x, Begrun: 0x%08x"
+ " but first_seq is NULL\n",
+ cmd->init_task_tag, begrun);
+ return -1;
+ }
+ seq_send_order = first_seq->seq_send_order;
+ seq->next_burst_len = seq->pdu_send_order = 0;
+ goto done;
+ }
+
+ pr_err("Unable to locate struct iscsi_seq for ITT: 0x%08x,"
+ " BegRun: 0x%08x, RunLength: 0x%08x while"
+ " DataSequenceInOrder=No and DataPDUInOrder=%s.\n",
+ cmd->init_task_tag, begrun, runlength,
+ (conn->sess->sess_ops->DataPDUInOrder) ? "Yes" : "No");
+ return -1;
+ }
+
+done:
+ dr->read_data_done = read_data_done;
+ dr->seq_send_order = seq_send_order;
+
+ return 0;
+}
+
+static int iscsit_handle_recovery_datain(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf,
+ u32 begrun,
+ u32 runlength)
+{
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_datain_req *dr;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+
+ if (!(se_cmd->transport_state & CMD_T_COMPLETE)) {
+ pr_err("Ignoring ITT: 0x%08x Data SNACK\n",
+ cmd->init_task_tag);
+ return 0;
+ }
+
+ /*
+ * Make sure the initiator is not requesting retransmission
+ * of DataSNs already acknowledged by a Data ACK SNACK.
+ */
+ if ((cmd->cmd_flags & ICF_GOT_DATACK_SNACK) &&
+ (begrun <= cmd->acked_data_sn)) {
+ pr_err("ITT: 0x%08x, Data SNACK requesting"
+ " retransmission of DataSN: 0x%08x to 0x%08x but"
+ " already acked to DataSN: 0x%08x by Data ACK SNACK,"
+ " protocol error.\n", cmd->init_task_tag, begrun,
+ (begrun + runlength), cmd->acked_data_sn);
+
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf);
+ }
+
+ /*
+ * Make sure BegRun and RunLength in the Data SNACK are sane.
+ * Note: (cmd->data_sn - 1) will carry the maximum DataSN sent.
+ */
+ if ((begrun + runlength) > (cmd->data_sn - 1)) {
+ pr_err("Initiator requesting BegRun: 0x%08x, RunLength"
+ ": 0x%08x greater than maximum DataSN: 0x%08x.\n",
+ begrun, runlength, (cmd->data_sn - 1));
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_INVALID,
+ buf);
+ }
+
+ dr = iscsit_allocate_datain_req();
+ if (!dr)
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+ buf);
+
+ dr->data_sn = dr->begrun = begrun;
+ dr->runlength = runlength;
+ dr->generate_recovery_values = 1;
+ dr->recovery = DATAIN_WITHIN_COMMAND_RECOVERY;
+
+ iscsit_attach_datain_req(cmd, dr);
+
+ cmd->i_state = ISTATE_SEND_DATAIN;
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+
+ return 0;
+}
+
+int iscsit_handle_recovery_datain_or_r2t(
+ struct iscsi_conn *conn,
+ unsigned char *buf,
+ itt_t init_task_tag,
+ u32 targ_xfer_tag,
+ u32 begrun,
+ u32 runlength)
+{
+ struct iscsi_cmd *cmd;
+
+ cmd = iscsit_find_cmd_from_itt(conn, init_task_tag);
+ if (!cmd)
+ return 0;
+
+ /*
+ * FIXME: This will not work for bidi commands.
+ */
+ switch (cmd->data_direction) {
+ case DMA_TO_DEVICE:
+ return iscsit_handle_r2t_snack(cmd, buf, begrun, runlength);
+ case DMA_FROM_DEVICE:
+ return iscsit_handle_recovery_datain(cmd, buf, begrun,
+ runlength);
+ default:
+ pr_err("Unknown cmd->data_direction: 0x%02x\n",
+ cmd->data_direction);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* #warning FIXME: Status SNACK needs to be dependent on OPCODE!!! */
+int iscsit_handle_status_snack(
+ struct iscsi_conn *conn,
+ itt_t init_task_tag,
+ u32 targ_xfer_tag,
+ u32 begrun,
+ u32 runlength)
+{
+ struct iscsi_cmd *cmd = NULL;
+ u32 last_statsn;
+ int found_cmd;
+
+ if (!begrun) {
+ begrun = conn->exp_statsn;
+ } else if (conn->exp_statsn > begrun) {
+ pr_err("Got Status SNACK Begrun: 0x%08x, RunLength:"
+ " 0x%08x but already got ExpStatSN: 0x%08x on CID:"
+ " %hu.\n", begrun, runlength, conn->exp_statsn,
+ conn->cid);
+ return 0;
+ }
+
+ last_statsn = (!runlength) ? conn->stat_sn : (begrun + runlength);
+
+ while (begrun < last_statsn) {
+ found_cmd = 0;
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_for_each_entry(cmd, &conn->conn_cmd_list, i_conn_node) {
+ if (cmd->stat_sn == begrun) {
+ found_cmd = 1;
+ break;
+ }
+ }
+ spin_unlock_bh(&conn->cmd_lock);
+
+ if (!found_cmd) {
+ pr_err("Unable to find StatSN: 0x%08x for"
+ " a Status SNACK, assuming this was a"
+ " protactic SNACK for an untransmitted"
+ " StatSN, ignoring.\n", begrun);
+ begrun++;
+ continue;
+ }
+
+ spin_lock_bh(&cmd->istate_lock);
+ if (cmd->i_state == ISTATE_SEND_DATAIN) {
+ spin_unlock_bh(&cmd->istate_lock);
+ pr_err("Ignoring Status SNACK for BegRun:"
+ " 0x%08x, RunLength: 0x%08x, assuming this was"
+ " a protactic SNACK for an untransmitted"
+ " StatSN\n", begrun, runlength);
+ begrun++;
+ continue;
+ }
+ spin_unlock_bh(&cmd->istate_lock);
+
+ cmd->i_state = ISTATE_SEND_STATUS_RECOVERY;
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+ begrun++;
+ }
+
+ return 0;
+}
+
+int iscsit_handle_data_ack(
+ struct iscsi_conn *conn,
+ u32 targ_xfer_tag,
+ u32 begrun,
+ u32 runlength)
+{
+ struct iscsi_cmd *cmd = NULL;
+
+ cmd = iscsit_find_cmd_from_ttt(conn, targ_xfer_tag);
+ if (!cmd) {
+ pr_err("Data ACK SNACK for TTT: 0x%08x is"
+ " invalid.\n", targ_xfer_tag);
+ return -1;
+ }
+
+ if (begrun <= cmd->acked_data_sn) {
+ pr_err("ITT: 0x%08x Data ACK SNACK BegRUN: 0x%08x is"
+ " less than the already acked DataSN: 0x%08x.\n",
+ cmd->init_task_tag, begrun, cmd->acked_data_sn);
+ return -1;
+ }
+
+ /*
+ * For Data ACK SNACK, BegRun is the next expected DataSN.
+ * (see iSCSI v19: 10.16.6)
+ */
+ cmd->cmd_flags |= ICF_GOT_DATACK_SNACK;
+ cmd->acked_data_sn = (begrun - 1);
+
+ pr_debug("Received Data ACK SNACK for ITT: 0x%08x,"
+ " updated acked DataSN to 0x%08x.\n",
+ cmd->init_task_tag, cmd->acked_data_sn);
+
+ return 0;
+}
+
+static int iscsit_send_recovery_r2t(
+ struct iscsi_cmd *cmd,
+ u32 offset,
+ u32 xfer_len)
+{
+ int ret;
+
+ spin_lock_bh(&cmd->r2t_lock);
+ ret = iscsit_add_r2t_to_list(cmd, offset, xfer_len, 1, 0);
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ return ret;
+}
+
+int iscsit_dataout_datapduinorder_no_fbit(
+ struct iscsi_cmd *cmd,
+ struct iscsi_pdu *pdu)
+{
+ int i, send_recovery_r2t = 0, recovery = 0;
+ u32 length = 0, offset = 0, pdu_count = 0, xfer_len = 0;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_pdu *first_pdu = NULL;
+
+ /*
+ * Get an struct iscsi_pdu pointer to the first PDU, and total PDU count
+ * of the DataOUT sequence.
+ */
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+ for (i = 0; i < cmd->pdu_count; i++) {
+ if (cmd->pdu_list[i].seq_no == pdu->seq_no) {
+ if (!first_pdu)
+ first_pdu = &cmd->pdu_list[i];
+ xfer_len += cmd->pdu_list[i].length;
+ pdu_count++;
+ } else if (pdu_count)
+ break;
+ }
+ } else {
+ struct iscsi_seq *seq = cmd->seq_ptr;
+
+ first_pdu = &cmd->pdu_list[seq->pdu_start];
+ pdu_count = seq->pdu_count;
+ }
+
+ if (!first_pdu || !pdu_count)
+ return DATAOUT_CANNOT_RECOVER;
+
+ /*
+ * Loop through the ending DataOUT Sequence checking each struct iscsi_pdu.
+ * The following ugly logic does batching of not received PDUs.
+ */
+ for (i = 0; i < pdu_count; i++) {
+ if (first_pdu[i].status == ISCSI_PDU_RECEIVED_OK) {
+ if (!send_recovery_r2t)
+ continue;
+
+ if (iscsit_send_recovery_r2t(cmd, offset, length) < 0)
+ return DATAOUT_CANNOT_RECOVER;
+
+ send_recovery_r2t = length = offset = 0;
+ continue;
+ }
+ /*
+ * Set recovery = 1 for any missing, CRC failed, or timed
+ * out PDUs to let the DataOUT logic know that this sequence
+ * has not been completed yet.
+ *
+ * Also, only send a Recovery R2T for ISCSI_PDU_NOT_RECEIVED.
+ * We assume if the PDU either failed CRC or timed out
+ * that a Recovery R2T has already been sent.
+ */
+ recovery = 1;
+
+ if (first_pdu[i].status != ISCSI_PDU_NOT_RECEIVED)
+ continue;
+
+ if (!offset)
+ offset = first_pdu[i].offset;
+ length += first_pdu[i].length;
+
+ send_recovery_r2t = 1;
+ }
+
+ if (send_recovery_r2t)
+ if (iscsit_send_recovery_r2t(cmd, offset, length) < 0)
+ return DATAOUT_CANNOT_RECOVER;
+
+ return (!recovery) ? DATAOUT_NORMAL : DATAOUT_WITHIN_COMMAND_RECOVERY;
+}
+
+static int iscsit_recalculate_dataout_values(
+ struct iscsi_cmd *cmd,
+ u32 pdu_offset,
+ u32 pdu_length,
+ u32 *r2t_offset,
+ u32 *r2t_length)
+{
+ int i;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_pdu *pdu = NULL;
+
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+ cmd->data_sn = 0;
+
+ if (conn->sess->sess_ops->DataPDUInOrder) {
+ *r2t_offset = cmd->write_data_done;
+ *r2t_length = (cmd->seq_end_offset -
+ cmd->write_data_done);
+ return 0;
+ }
+
+ *r2t_offset = cmd->seq_start_offset;
+ *r2t_length = (cmd->seq_end_offset - cmd->seq_start_offset);
+
+ for (i = 0; i < cmd->pdu_count; i++) {
+ pdu = &cmd->pdu_list[i];
+
+ if (pdu->status != ISCSI_PDU_RECEIVED_OK)
+ continue;
+
+ if ((pdu->offset >= cmd->seq_start_offset) &&
+ ((pdu->offset + pdu->length) <=
+ cmd->seq_end_offset)) {
+ if (!cmd->unsolicited_data)
+ cmd->next_burst_len -= pdu->length;
+ else
+ cmd->first_burst_len -= pdu->length;
+
+ cmd->write_data_done -= pdu->length;
+ pdu->status = ISCSI_PDU_NOT_RECEIVED;
+ }
+ }
+ } else {
+ struct iscsi_seq *seq = NULL;
+
+ seq = iscsit_get_seq_holder(cmd, pdu_offset, pdu_length);
+ if (!seq)
+ return -1;
+
+ *r2t_offset = seq->orig_offset;
+ *r2t_length = seq->xfer_len;
+
+ cmd->write_data_done -= (seq->offset - seq->orig_offset);
+ if (cmd->immediate_data)
+ cmd->first_burst_len = cmd->write_data_done;
+
+ seq->data_sn = 0;
+ seq->offset = seq->orig_offset;
+ seq->next_burst_len = 0;
+ seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY;
+
+ if (conn->sess->sess_ops->DataPDUInOrder)
+ return 0;
+
+ for (i = 0; i < seq->pdu_count; i++) {
+ pdu = &cmd->pdu_list[i+seq->pdu_start];
+
+ if (pdu->status != ISCSI_PDU_RECEIVED_OK)
+ continue;
+
+ pdu->status = ISCSI_PDU_NOT_RECEIVED;
+ }
+ }
+
+ return 0;
+}
+
+int iscsit_recover_dataout_sequence(
+ struct iscsi_cmd *cmd,
+ u32 pdu_offset,
+ u32 pdu_length)
+{
+ u32 r2t_length = 0, r2t_offset = 0;
+
+ spin_lock_bh(&cmd->istate_lock);
+ cmd->cmd_flags |= ICF_WITHIN_COMMAND_RECOVERY;
+ spin_unlock_bh(&cmd->istate_lock);
+
+ if (iscsit_recalculate_dataout_values(cmd, pdu_offset, pdu_length,
+ &r2t_offset, &r2t_length) < 0)
+ return DATAOUT_CANNOT_RECOVER;
+
+ iscsit_send_recovery_r2t(cmd, r2t_offset, r2t_length);
+
+ return DATAOUT_WITHIN_COMMAND_RECOVERY;
+}
+
+static struct iscsi_ooo_cmdsn *iscsit_allocate_ooo_cmdsn(void)
+{
+ struct iscsi_ooo_cmdsn *ooo_cmdsn = NULL;
+
+ ooo_cmdsn = kmem_cache_zalloc(lio_ooo_cache, GFP_ATOMIC);
+ if (!ooo_cmdsn) {
+ pr_err("Unable to allocate memory for"
+ " struct iscsi_ooo_cmdsn.\n");
+ return NULL;
+ }
+ INIT_LIST_HEAD(&ooo_cmdsn->ooo_list);
+
+ return ooo_cmdsn;
+}
+
+/*
+ * Called with sess->cmdsn_mutex held.
+ */
+static int iscsit_attach_ooo_cmdsn(
+ struct iscsi_session *sess,
+ struct iscsi_ooo_cmdsn *ooo_cmdsn)
+{
+ struct iscsi_ooo_cmdsn *ooo_tail, *ooo_tmp;
+ /*
+ * We attach the struct iscsi_ooo_cmdsn entry to the out of order
+ * list in increasing CmdSN order.
+ * This allows iscsi_execute_ooo_cmdsns() to detect any
+ * additional CmdSN holes while performing delayed execution.
+ */
+ if (list_empty(&sess->sess_ooo_cmdsn_list))
+ list_add_tail(&ooo_cmdsn->ooo_list,
+ &sess->sess_ooo_cmdsn_list);
+ else {
+ ooo_tail = list_entry(sess->sess_ooo_cmdsn_list.prev,
+ typeof(*ooo_tail), ooo_list);
+ /*
+ * CmdSN is greater than the tail of the list.
+ */
+ if (iscsi_sna_lt(ooo_tail->cmdsn, ooo_cmdsn->cmdsn))
+ list_add_tail(&ooo_cmdsn->ooo_list,
+ &sess->sess_ooo_cmdsn_list);
+ else {
+ /*
+ * CmdSN is either lower than the head, or somewhere
+ * in the middle.
+ */
+ list_for_each_entry(ooo_tmp, &sess->sess_ooo_cmdsn_list,
+ ooo_list) {
+ if (iscsi_sna_lt(ooo_tmp->cmdsn, ooo_cmdsn->cmdsn))
+ continue;
+
+ /* Insert before this entry */
+ list_add(&ooo_cmdsn->ooo_list,
+ ooo_tmp->ooo_list.prev);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Removes an struct iscsi_ooo_cmdsn from a session's list,
+ * called with struct iscsi_session->cmdsn_mutex held.
+ */
+void iscsit_remove_ooo_cmdsn(
+ struct iscsi_session *sess,
+ struct iscsi_ooo_cmdsn *ooo_cmdsn)
+{
+ list_del(&ooo_cmdsn->ooo_list);
+ kmem_cache_free(lio_ooo_cache, ooo_cmdsn);
+}
+
+void iscsit_clear_ooo_cmdsns_for_conn(struct iscsi_conn *conn)
+{
+ struct iscsi_ooo_cmdsn *ooo_cmdsn;
+ struct iscsi_session *sess = conn->sess;
+
+ mutex_lock(&sess->cmdsn_mutex);
+ list_for_each_entry(ooo_cmdsn, &sess->sess_ooo_cmdsn_list, ooo_list) {
+ if (ooo_cmdsn->cid != conn->cid)
+ continue;
+
+ ooo_cmdsn->cmd = NULL;
+ }
+ mutex_unlock(&sess->cmdsn_mutex);
+}
+
+/*
+ * Called with sess->cmdsn_mutex held.
+ */
+int iscsit_execute_ooo_cmdsns(struct iscsi_session *sess)
+{
+ int ooo_count = 0;
+ struct iscsi_cmd *cmd = NULL;
+ struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp;
+
+ list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp,
+ &sess->sess_ooo_cmdsn_list, ooo_list) {
+ if (ooo_cmdsn->cmdsn != sess->exp_cmd_sn)
+ continue;
+
+ if (!ooo_cmdsn->cmd) {
+ sess->exp_cmd_sn++;
+ iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn);
+ continue;
+ }
+
+ cmd = ooo_cmdsn->cmd;
+ cmd->i_state = cmd->deferred_i_state;
+ ooo_count++;
+ sess->exp_cmd_sn++;
+ pr_debug("Executing out of order CmdSN: 0x%08x,"
+ " incremented ExpCmdSN to 0x%08x.\n",
+ cmd->cmd_sn, sess->exp_cmd_sn);
+
+ iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn);
+
+ if (iscsit_execute_cmd(cmd, 1) < 0)
+ return -1;
+
+ continue;
+ }
+
+ return ooo_count;
+}
+
+/*
+ * Called either:
+ *
+ * 1. With sess->cmdsn_mutex held from iscsi_execute_ooo_cmdsns()
+ * or iscsi_check_received_cmdsn().
+ * 2. With no locks held directly from iscsi_handle_XXX_pdu() functions
+ * for immediate commands.
+ */
+int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo)
+{
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct iscsi_conn *conn = cmd->conn;
+ int lr = 0;
+
+ spin_lock_bh(&cmd->istate_lock);
+ if (ooo)
+ cmd->cmd_flags &= ~ICF_OOO_CMDSN;
+
+ switch (cmd->iscsi_opcode) {
+ case ISCSI_OP_SCSI_CMD:
+ /*
+ * Go ahead and send the CHECK_CONDITION status for
+ * any SCSI CDB exceptions that may have occurred.
+ */
+ if (cmd->sense_reason) {
+ if (cmd->sense_reason == TCM_RESERVATION_CONFLICT) {
+ cmd->i_state = ISTATE_SEND_STATUS;
+ spin_unlock_bh(&cmd->istate_lock);
+ iscsit_add_cmd_to_response_queue(cmd, cmd->conn,
+ cmd->i_state);
+ return 0;
+ }
+ spin_unlock_bh(&cmd->istate_lock);
+ /*
+ * Determine if delayed TASK_ABORTED status for WRITEs
+ * should be sent now if no unsolicited data out
+ * payloads are expected, or if the delayed status
+ * should be sent after unsolicited data out with
+ * ISCSI_FLAG_CMD_FINAL set in iscsi_handle_data_out()
+ */
+ if (transport_check_aborted_status(se_cmd,
+ (cmd->unsolicited_data == 0)) != 0)
+ return 0;
+ /*
+ * Otherwise send CHECK_CONDITION and sense for
+ * exception
+ */
+ return transport_send_check_condition_and_sense(se_cmd,
+ cmd->sense_reason, 0);
+ }
+ /*
+ * Special case for delayed CmdSN with Immediate
+ * Data and/or Unsolicited Data Out attached.
+ */
+ if (cmd->immediate_data) {
+ if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) {
+ spin_unlock_bh(&cmd->istate_lock);
+ target_execute_cmd(&cmd->se_cmd);
+ return 0;
+ }
+ spin_unlock_bh(&cmd->istate_lock);
+
+ if (!(cmd->cmd_flags &
+ ICF_NON_IMMEDIATE_UNSOLICITED_DATA)) {
+ /*
+ * Send the delayed TASK_ABORTED status for
+ * WRITEs if no more unsolicitied data is
+ * expected.
+ */
+ if (transport_check_aborted_status(se_cmd, 1)
+ != 0)
+ return 0;
+
+ iscsit_set_dataout_sequence_values(cmd);
+ conn->conn_transport->iscsit_get_dataout(conn, cmd, false);
+ }
+ return 0;
+ }
+ /*
+ * The default handler.
+ */
+ spin_unlock_bh(&cmd->istate_lock);
+
+ if ((cmd->data_direction == DMA_TO_DEVICE) &&
+ !(cmd->cmd_flags & ICF_NON_IMMEDIATE_UNSOLICITED_DATA)) {
+ /*
+ * Send the delayed TASK_ABORTED status for WRITEs if
+ * no more nsolicitied data is expected.
+ */
+ if (transport_check_aborted_status(se_cmd, 1) != 0)
+ return 0;
+
+ iscsit_set_unsoliticed_dataout(cmd);
+ }
+ return transport_handle_cdb_direct(&cmd->se_cmd);
+
+ case ISCSI_OP_NOOP_OUT:
+ case ISCSI_OP_TEXT:
+ spin_unlock_bh(&cmd->istate_lock);
+ iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
+ break;
+ case ISCSI_OP_SCSI_TMFUNC:
+ if (cmd->se_cmd.se_tmr_req->response) {
+ spin_unlock_bh(&cmd->istate_lock);
+ iscsit_add_cmd_to_response_queue(cmd, cmd->conn,
+ cmd->i_state);
+ return 0;
+ }
+ spin_unlock_bh(&cmd->istate_lock);
+
+ return transport_generic_handle_tmr(&cmd->se_cmd);
+ case ISCSI_OP_LOGOUT:
+ spin_unlock_bh(&cmd->istate_lock);
+ switch (cmd->logout_reason) {
+ case ISCSI_LOGOUT_REASON_CLOSE_SESSION:
+ lr = iscsit_logout_closesession(cmd, cmd->conn);
+ break;
+ case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION:
+ lr = iscsit_logout_closeconnection(cmd, cmd->conn);
+ break;
+ case ISCSI_LOGOUT_REASON_RECOVERY:
+ lr = iscsit_logout_removeconnforrecovery(cmd, cmd->conn);
+ break;
+ default:
+ pr_err("Unknown iSCSI Logout Request Code:"
+ " 0x%02x\n", cmd->logout_reason);
+ return -1;
+ }
+
+ return lr;
+ default:
+ spin_unlock_bh(&cmd->istate_lock);
+ pr_err("Cannot perform out of order execution for"
+ " unknown iSCSI Opcode: 0x%02x\n", cmd->iscsi_opcode);
+ return -1;
+ }
+
+ return 0;
+}
+
+void iscsit_free_all_ooo_cmdsns(struct iscsi_session *sess)
+{
+ struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp;
+
+ mutex_lock(&sess->cmdsn_mutex);
+ list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp,
+ &sess->sess_ooo_cmdsn_list, ooo_list) {
+
+ list_del(&ooo_cmdsn->ooo_list);
+ kmem_cache_free(lio_ooo_cache, ooo_cmdsn);
+ }
+ mutex_unlock(&sess->cmdsn_mutex);
+}
+
+int iscsit_handle_ooo_cmdsn(
+ struct iscsi_session *sess,
+ struct iscsi_cmd *cmd,
+ u32 cmdsn)
+{
+ int batch = 0;
+ struct iscsi_ooo_cmdsn *ooo_cmdsn = NULL, *ooo_tail = NULL;
+
+ cmd->deferred_i_state = cmd->i_state;
+ cmd->i_state = ISTATE_DEFERRED_CMD;
+ cmd->cmd_flags |= ICF_OOO_CMDSN;
+
+ if (list_empty(&sess->sess_ooo_cmdsn_list))
+ batch = 1;
+ else {
+ ooo_tail = list_entry(sess->sess_ooo_cmdsn_list.prev,
+ typeof(*ooo_tail), ooo_list);
+ if (ooo_tail->cmdsn != (cmdsn - 1))
+ batch = 1;
+ }
+
+ ooo_cmdsn = iscsit_allocate_ooo_cmdsn();
+ if (!ooo_cmdsn)
+ return -ENOMEM;
+
+ ooo_cmdsn->cmd = cmd;
+ ooo_cmdsn->batch_count = (batch) ?
+ (cmdsn - sess->exp_cmd_sn) : 1;
+ ooo_cmdsn->cid = cmd->conn->cid;
+ ooo_cmdsn->exp_cmdsn = sess->exp_cmd_sn;
+ ooo_cmdsn->cmdsn = cmdsn;
+
+ if (iscsit_attach_ooo_cmdsn(sess, ooo_cmdsn) < 0) {
+ kmem_cache_free(lio_ooo_cache, ooo_cmdsn);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int iscsit_set_dataout_timeout_values(
+ struct iscsi_cmd *cmd,
+ u32 *offset,
+ u32 *length)
+{
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_r2t *r2t;
+
+ if (cmd->unsolicited_data) {
+ *offset = 0;
+ *length = (conn->sess->sess_ops->FirstBurstLength >
+ cmd->se_cmd.data_length) ?
+ cmd->se_cmd.data_length :
+ conn->sess->sess_ops->FirstBurstLength;
+ return 0;
+ }
+
+ spin_lock_bh(&cmd->r2t_lock);
+ if (list_empty(&cmd->cmd_r2t_list)) {
+ pr_err("cmd->cmd_r2t_list is empty!\n");
+ spin_unlock_bh(&cmd->r2t_lock);
+ return -1;
+ }
+
+ list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) {
+ if (r2t->sent_r2t && !r2t->recovery_r2t && !r2t->seq_complete) {
+ *offset = r2t->offset;
+ *length = r2t->xfer_len;
+ spin_unlock_bh(&cmd->r2t_lock);
+ return 0;
+ }
+ }
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ pr_err("Unable to locate any incomplete DataOUT"
+ " sequences for ITT: 0x%08x.\n", cmd->init_task_tag);
+
+ return -1;
+}
+
+/*
+ * NOTE: Called from interrupt (timer) context.
+ */
+static void iscsit_handle_dataout_timeout(unsigned long data)
+{
+ u32 pdu_length = 0, pdu_offset = 0;
+ u32 r2t_length = 0, r2t_offset = 0;
+ struct iscsi_cmd *cmd = (struct iscsi_cmd *) data;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_session *sess = NULL;
+ struct iscsi_node_attrib *na;
+
+ iscsit_inc_conn_usage_count(conn);
+
+ spin_lock_bh(&cmd->dataout_timeout_lock);
+ if (cmd->dataout_timer_flags & ISCSI_TF_STOP) {
+ spin_unlock_bh(&cmd->dataout_timeout_lock);
+ iscsit_dec_conn_usage_count(conn);
+ return;
+ }
+ cmd->dataout_timer_flags &= ~ISCSI_TF_RUNNING;
+ sess = conn->sess;
+ na = iscsit_tpg_get_node_attrib(sess);
+
+ if (!sess->sess_ops->ErrorRecoveryLevel) {
+ pr_debug("Unable to recover from DataOut timeout while"
+ " in ERL=0.\n");
+ goto failure;
+ }
+
+ if (++cmd->dataout_timeout_retries == na->dataout_timeout_retries) {
+ pr_debug("Command ITT: 0x%08x exceeded max retries"
+ " for DataOUT timeout %u, closing iSCSI connection.\n",
+ cmd->init_task_tag, na->dataout_timeout_retries);
+ goto failure;
+ }
+
+ cmd->cmd_flags |= ICF_WITHIN_COMMAND_RECOVERY;
+
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+ if (conn->sess->sess_ops->DataPDUInOrder) {
+ pdu_offset = cmd->write_data_done;
+ if ((pdu_offset + (conn->sess->sess_ops->MaxBurstLength -
+ cmd->next_burst_len)) > cmd->se_cmd.data_length)
+ pdu_length = (cmd->se_cmd.data_length -
+ cmd->write_data_done);
+ else
+ pdu_length = (conn->sess->sess_ops->MaxBurstLength -
+ cmd->next_burst_len);
+ } else {
+ pdu_offset = cmd->seq_start_offset;
+ pdu_length = (cmd->seq_end_offset -
+ cmd->seq_start_offset);
+ }
+ } else {
+ if (iscsit_set_dataout_timeout_values(cmd, &pdu_offset,
+ &pdu_length) < 0)
+ goto failure;
+ }
+
+ if (iscsit_recalculate_dataout_values(cmd, pdu_offset, pdu_length,
+ &r2t_offset, &r2t_length) < 0)
+ goto failure;
+
+ pr_debug("Command ITT: 0x%08x timed out waiting for"
+ " completion of %sDataOUT Sequence Offset: %u, Length: %u\n",
+ cmd->init_task_tag, (cmd->unsolicited_data) ? "Unsolicited " :
+ "", r2t_offset, r2t_length);
+
+ if (iscsit_send_recovery_r2t(cmd, r2t_offset, r2t_length) < 0)
+ goto failure;
+
+ iscsit_start_dataout_timer(cmd, conn);
+ spin_unlock_bh(&cmd->dataout_timeout_lock);
+ iscsit_dec_conn_usage_count(conn);
+
+ return;
+
+failure:
+ spin_unlock_bh(&cmd->dataout_timeout_lock);
+ iscsit_cause_connection_reinstatement(conn, 0);
+ iscsit_dec_conn_usage_count(conn);
+}
+
+void iscsit_mod_dataout_timer(struct iscsi_cmd *cmd)
+{
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_session *sess = conn->sess;
+ struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+
+ spin_lock_bh(&cmd->dataout_timeout_lock);
+ if (!(cmd->dataout_timer_flags & ISCSI_TF_RUNNING)) {
+ spin_unlock_bh(&cmd->dataout_timeout_lock);
+ return;
+ }
+
+ mod_timer(&cmd->dataout_timer,
+ (get_jiffies_64() + na->dataout_timeout * HZ));
+ pr_debug("Updated DataOUT timer for ITT: 0x%08x",
+ cmd->init_task_tag);
+ spin_unlock_bh(&cmd->dataout_timeout_lock);
+}
+
+/*
+ * Called with cmd->dataout_timeout_lock held.
+ */
+void iscsit_start_dataout_timer(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_session *sess = conn->sess;
+ struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+
+ if (cmd->dataout_timer_flags & ISCSI_TF_RUNNING)
+ return;
+
+ pr_debug("Starting DataOUT timer for ITT: 0x%08x on"
+ " CID: %hu.\n", cmd->init_task_tag, conn->cid);
+
+ init_timer(&cmd->dataout_timer);
+ cmd->dataout_timer.expires = (get_jiffies_64() + na->dataout_timeout * HZ);
+ cmd->dataout_timer.data = (unsigned long)cmd;
+ cmd->dataout_timer.function = iscsit_handle_dataout_timeout;
+ cmd->dataout_timer_flags &= ~ISCSI_TF_STOP;
+ cmd->dataout_timer_flags |= ISCSI_TF_RUNNING;
+ add_timer(&cmd->dataout_timer);
+}
+
+void iscsit_stop_dataout_timer(struct iscsi_cmd *cmd)
+{
+ spin_lock_bh(&cmd->dataout_timeout_lock);
+ if (!(cmd->dataout_timer_flags & ISCSI_TF_RUNNING)) {
+ spin_unlock_bh(&cmd->dataout_timeout_lock);
+ return;
+ }
+ cmd->dataout_timer_flags |= ISCSI_TF_STOP;
+ spin_unlock_bh(&cmd->dataout_timeout_lock);
+
+ del_timer_sync(&cmd->dataout_timer);
+
+ spin_lock_bh(&cmd->dataout_timeout_lock);
+ cmd->dataout_timer_flags &= ~ISCSI_TF_RUNNING;
+ pr_debug("Stopped DataOUT Timer for ITT: 0x%08x\n",
+ cmd->init_task_tag);
+ spin_unlock_bh(&cmd->dataout_timeout_lock);
+}
+EXPORT_SYMBOL(iscsit_stop_dataout_timer);
diff --git a/drivers/target/iscsi/iscsi_target_erl1.h b/drivers/target/iscsi/iscsi_target_erl1.h
new file mode 100644
index 00000000000..2a3ebf118a3
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_erl1.h
@@ -0,0 +1,26 @@
+#ifndef ISCSI_TARGET_ERL1_H
+#define ISCSI_TARGET_ERL1_H
+
+extern int iscsit_dump_data_payload(struct iscsi_conn *, u32, int);
+extern int iscsit_create_recovery_datain_values_datasequenceinorder_yes(
+ struct iscsi_cmd *, struct iscsi_datain_req *);
+extern int iscsit_create_recovery_datain_values_datasequenceinorder_no(
+ struct iscsi_cmd *, struct iscsi_datain_req *);
+extern int iscsit_handle_recovery_datain_or_r2t(struct iscsi_conn *, unsigned char *,
+ itt_t, u32, u32, u32);
+extern int iscsit_handle_status_snack(struct iscsi_conn *, itt_t, u32,
+ u32, u32);
+extern int iscsit_handle_data_ack(struct iscsi_conn *, u32, u32, u32);
+extern int iscsit_dataout_datapduinorder_no_fbit(struct iscsi_cmd *, struct iscsi_pdu *);
+extern int iscsit_recover_dataout_sequence(struct iscsi_cmd *, u32, u32);
+extern void iscsit_clear_ooo_cmdsns_for_conn(struct iscsi_conn *);
+extern void iscsit_free_all_ooo_cmdsns(struct iscsi_session *);
+extern int iscsit_execute_ooo_cmdsns(struct iscsi_session *);
+extern int iscsit_execute_cmd(struct iscsi_cmd *, int);
+extern int iscsit_handle_ooo_cmdsn(struct iscsi_session *, struct iscsi_cmd *, u32);
+extern void iscsit_remove_ooo_cmdsn(struct iscsi_session *, struct iscsi_ooo_cmdsn *);
+extern void iscsit_mod_dataout_timer(struct iscsi_cmd *);
+extern void iscsit_start_dataout_timer(struct iscsi_cmd *, struct iscsi_conn *);
+extern void iscsit_stop_dataout_timer(struct iscsi_cmd *);
+
+#endif /* ISCSI_TARGET_ERL1_H */
diff --git a/drivers/target/iscsi/iscsi_target_erl2.c b/drivers/target/iscsi/iscsi_target_erl2.c
new file mode 100644
index 00000000000..4ca8fd2a70d
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_erl2.c
@@ -0,0 +1,436 @@
+/*******************************************************************************
+ * This file contains error recovery level two functions used by
+ * the iSCSI Target driver.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_datain_values.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target.h"
+
+/*
+ * FIXME: Does RData SNACK apply here as well?
+ */
+void iscsit_create_conn_recovery_datain_values(
+ struct iscsi_cmd *cmd,
+ __be32 exp_data_sn)
+{
+ u32 data_sn = 0;
+ struct iscsi_conn *conn = cmd->conn;
+
+ cmd->next_burst_len = 0;
+ cmd->read_data_done = 0;
+
+ while (be32_to_cpu(exp_data_sn) > data_sn) {
+ if ((cmd->next_burst_len +
+ conn->conn_ops->MaxRecvDataSegmentLength) <
+ conn->sess->sess_ops->MaxBurstLength) {
+ cmd->read_data_done +=
+ conn->conn_ops->MaxRecvDataSegmentLength;
+ cmd->next_burst_len +=
+ conn->conn_ops->MaxRecvDataSegmentLength;
+ } else {
+ cmd->read_data_done +=
+ (conn->sess->sess_ops->MaxBurstLength -
+ cmd->next_burst_len);
+ cmd->next_burst_len = 0;
+ }
+ data_sn++;
+ }
+}
+
+void iscsit_create_conn_recovery_dataout_values(
+ struct iscsi_cmd *cmd)
+{
+ u32 write_data_done = 0;
+ struct iscsi_conn *conn = cmd->conn;
+
+ cmd->data_sn = 0;
+ cmd->next_burst_len = 0;
+
+ while (cmd->write_data_done > write_data_done) {
+ if ((write_data_done + conn->sess->sess_ops->MaxBurstLength) <=
+ cmd->write_data_done)
+ write_data_done += conn->sess->sess_ops->MaxBurstLength;
+ else
+ break;
+ }
+
+ cmd->write_data_done = write_data_done;
+}
+
+static int iscsit_attach_active_connection_recovery_entry(
+ struct iscsi_session *sess,
+ struct iscsi_conn_recovery *cr)
+{
+ spin_lock(&sess->cr_a_lock);
+ list_add_tail(&cr->cr_list, &sess->cr_active_list);
+ spin_unlock(&sess->cr_a_lock);
+
+ return 0;
+}
+
+static int iscsit_attach_inactive_connection_recovery_entry(
+ struct iscsi_session *sess,
+ struct iscsi_conn_recovery *cr)
+{
+ spin_lock(&sess->cr_i_lock);
+ list_add_tail(&cr->cr_list, &sess->cr_inactive_list);
+
+ sess->conn_recovery_count++;
+ pr_debug("Incremented connection recovery count to %u for"
+ " SID: %u\n", sess->conn_recovery_count, sess->sid);
+ spin_unlock(&sess->cr_i_lock);
+
+ return 0;
+}
+
+struct iscsi_conn_recovery *iscsit_get_inactive_connection_recovery_entry(
+ struct iscsi_session *sess,
+ u16 cid)
+{
+ struct iscsi_conn_recovery *cr;
+
+ spin_lock(&sess->cr_i_lock);
+ list_for_each_entry(cr, &sess->cr_inactive_list, cr_list) {
+ if (cr->cid == cid) {
+ spin_unlock(&sess->cr_i_lock);
+ return cr;
+ }
+ }
+ spin_unlock(&sess->cr_i_lock);
+
+ return NULL;
+}
+
+void iscsit_free_connection_recovery_entires(struct iscsi_session *sess)
+{
+ struct iscsi_cmd *cmd, *cmd_tmp;
+ struct iscsi_conn_recovery *cr, *cr_tmp;
+
+ spin_lock(&sess->cr_a_lock);
+ list_for_each_entry_safe(cr, cr_tmp, &sess->cr_active_list, cr_list) {
+ list_del(&cr->cr_list);
+ spin_unlock(&sess->cr_a_lock);
+
+ spin_lock(&cr->conn_recovery_cmd_lock);
+ list_for_each_entry_safe(cmd, cmd_tmp,
+ &cr->conn_recovery_cmd_list, i_conn_node) {
+
+ list_del_init(&cmd->i_conn_node);
+ cmd->conn = NULL;
+ spin_unlock(&cr->conn_recovery_cmd_lock);
+ iscsit_free_cmd(cmd, true);
+ spin_lock(&cr->conn_recovery_cmd_lock);
+ }
+ spin_unlock(&cr->conn_recovery_cmd_lock);
+ spin_lock(&sess->cr_a_lock);
+
+ kfree(cr);
+ }
+ spin_unlock(&sess->cr_a_lock);
+
+ spin_lock(&sess->cr_i_lock);
+ list_for_each_entry_safe(cr, cr_tmp, &sess->cr_inactive_list, cr_list) {
+ list_del(&cr->cr_list);
+ spin_unlock(&sess->cr_i_lock);
+
+ spin_lock(&cr->conn_recovery_cmd_lock);
+ list_for_each_entry_safe(cmd, cmd_tmp,
+ &cr->conn_recovery_cmd_list, i_conn_node) {
+
+ list_del_init(&cmd->i_conn_node);
+ cmd->conn = NULL;
+ spin_unlock(&cr->conn_recovery_cmd_lock);
+ iscsit_free_cmd(cmd, true);
+ spin_lock(&cr->conn_recovery_cmd_lock);
+ }
+ spin_unlock(&cr->conn_recovery_cmd_lock);
+ spin_lock(&sess->cr_i_lock);
+
+ kfree(cr);
+ }
+ spin_unlock(&sess->cr_i_lock);
+}
+
+int iscsit_remove_active_connection_recovery_entry(
+ struct iscsi_conn_recovery *cr,
+ struct iscsi_session *sess)
+{
+ spin_lock(&sess->cr_a_lock);
+ list_del(&cr->cr_list);
+
+ sess->conn_recovery_count--;
+ pr_debug("Decremented connection recovery count to %u for"
+ " SID: %u\n", sess->conn_recovery_count, sess->sid);
+ spin_unlock(&sess->cr_a_lock);
+
+ kfree(cr);
+
+ return 0;
+}
+
+static void iscsit_remove_inactive_connection_recovery_entry(
+ struct iscsi_conn_recovery *cr,
+ struct iscsi_session *sess)
+{
+ spin_lock(&sess->cr_i_lock);
+ list_del(&cr->cr_list);
+ spin_unlock(&sess->cr_i_lock);
+}
+
+/*
+ * Called with cr->conn_recovery_cmd_lock help.
+ */
+int iscsit_remove_cmd_from_connection_recovery(
+ struct iscsi_cmd *cmd,
+ struct iscsi_session *sess)
+{
+ struct iscsi_conn_recovery *cr;
+
+ if (!cmd->cr) {
+ pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x"
+ " is NULL!\n", cmd->init_task_tag);
+ BUG();
+ }
+ cr = cmd->cr;
+
+ list_del_init(&cmd->i_conn_node);
+ return --cr->cmd_count;
+}
+
+void iscsit_discard_cr_cmds_by_expstatsn(
+ struct iscsi_conn_recovery *cr,
+ u32 exp_statsn)
+{
+ u32 dropped_count = 0;
+ struct iscsi_cmd *cmd, *cmd_tmp;
+ struct iscsi_session *sess = cr->sess;
+
+ spin_lock(&cr->conn_recovery_cmd_lock);
+ list_for_each_entry_safe(cmd, cmd_tmp,
+ &cr->conn_recovery_cmd_list, i_conn_node) {
+
+ if (((cmd->deferred_i_state != ISTATE_SENT_STATUS) &&
+ (cmd->deferred_i_state != ISTATE_REMOVE)) ||
+ (cmd->stat_sn >= exp_statsn)) {
+ continue;
+ }
+
+ dropped_count++;
+ pr_debug("Dropping Acknowledged ITT: 0x%08x, StatSN:"
+ " 0x%08x, CID: %hu.\n", cmd->init_task_tag,
+ cmd->stat_sn, cr->cid);
+
+ iscsit_remove_cmd_from_connection_recovery(cmd, sess);
+
+ spin_unlock(&cr->conn_recovery_cmd_lock);
+ iscsit_free_cmd(cmd, true);
+ spin_lock(&cr->conn_recovery_cmd_lock);
+ }
+ spin_unlock(&cr->conn_recovery_cmd_lock);
+
+ pr_debug("Dropped %u total acknowledged commands on"
+ " CID: %hu less than old ExpStatSN: 0x%08x\n",
+ dropped_count, cr->cid, exp_statsn);
+
+ if (!cr->cmd_count) {
+ pr_debug("No commands to be reassigned for failed"
+ " connection CID: %hu on SID: %u\n",
+ cr->cid, sess->sid);
+ iscsit_remove_inactive_connection_recovery_entry(cr, sess);
+ iscsit_attach_active_connection_recovery_entry(sess, cr);
+ pr_debug("iSCSI connection recovery successful for CID:"
+ " %hu on SID: %u\n", cr->cid, sess->sid);
+ iscsit_remove_active_connection_recovery_entry(cr, sess);
+ } else {
+ iscsit_remove_inactive_connection_recovery_entry(cr, sess);
+ iscsit_attach_active_connection_recovery_entry(sess, cr);
+ }
+}
+
+int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *conn)
+{
+ u32 dropped_count = 0;
+ struct iscsi_cmd *cmd, *cmd_tmp;
+ struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp;
+ struct iscsi_session *sess = conn->sess;
+
+ mutex_lock(&sess->cmdsn_mutex);
+ list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp,
+ &sess->sess_ooo_cmdsn_list, ooo_list) {
+
+ if (ooo_cmdsn->cid != conn->cid)
+ continue;
+
+ dropped_count++;
+ pr_debug("Dropping unacknowledged CmdSN:"
+ " 0x%08x during connection recovery on CID: %hu\n",
+ ooo_cmdsn->cmdsn, conn->cid);
+ iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn);
+ }
+ mutex_unlock(&sess->cmdsn_mutex);
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) {
+ if (!(cmd->cmd_flags & ICF_OOO_CMDSN))
+ continue;
+
+ list_del_init(&cmd->i_conn_node);
+
+ spin_unlock_bh(&conn->cmd_lock);
+ iscsit_free_cmd(cmd, true);
+ spin_lock_bh(&conn->cmd_lock);
+ }
+ spin_unlock_bh(&conn->cmd_lock);
+
+ pr_debug("Dropped %u total unacknowledged commands on CID:"
+ " %hu for ExpCmdSN: 0x%08x.\n", dropped_count, conn->cid,
+ sess->exp_cmd_sn);
+ return 0;
+}
+
+int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn)
+{
+ u32 cmd_count = 0;
+ struct iscsi_cmd *cmd, *cmd_tmp;
+ struct iscsi_conn_recovery *cr;
+
+ /*
+ * Allocate an struct iscsi_conn_recovery for this connection.
+ * Each struct iscsi_cmd contains an struct iscsi_conn_recovery pointer
+ * (struct iscsi_cmd->cr) so we need to allocate this before preparing the
+ * connection's command list for connection recovery.
+ */
+ cr = kzalloc(sizeof(struct iscsi_conn_recovery), GFP_KERNEL);
+ if (!cr) {
+ pr_err("Unable to allocate memory for"
+ " struct iscsi_conn_recovery.\n");
+ return -1;
+ }
+ INIT_LIST_HEAD(&cr->cr_list);
+ INIT_LIST_HEAD(&cr->conn_recovery_cmd_list);
+ spin_lock_init(&cr->conn_recovery_cmd_lock);
+ /*
+ * Only perform connection recovery on ISCSI_OP_SCSI_CMD or
+ * ISCSI_OP_NOOP_OUT opcodes. For all other opcodes call
+ * list_del_init(&cmd->i_conn_node); to release the command to the
+ * session pool and remove it from the connection's list.
+ *
+ * Also stop the DataOUT timer, which will be restarted after
+ * sending the TMR response.
+ */
+ spin_lock_bh(&conn->cmd_lock);
+ list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) {
+
+ if ((cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) &&
+ (cmd->iscsi_opcode != ISCSI_OP_NOOP_OUT)) {
+ pr_debug("Not performing realligence on"
+ " Opcode: 0x%02x, ITT: 0x%08x, CmdSN: 0x%08x,"
+ " CID: %hu\n", cmd->iscsi_opcode,
+ cmd->init_task_tag, cmd->cmd_sn, conn->cid);
+
+ list_del_init(&cmd->i_conn_node);
+ spin_unlock_bh(&conn->cmd_lock);
+ iscsit_free_cmd(cmd, true);
+ spin_lock_bh(&conn->cmd_lock);
+ continue;
+ }
+
+ /*
+ * Special case where commands greater than or equal to
+ * the session's ExpCmdSN are attached to the connection
+ * list but not to the out of order CmdSN list. The one
+ * obvious case is when a command with immediate data
+ * attached must only check the CmdSN against ExpCmdSN
+ * after the data is received. The special case below
+ * is when the connection fails before data is received,
+ * but also may apply to other PDUs, so it has been
+ * made generic here.
+ */
+ if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd &&
+ iscsi_sna_gte(cmd->cmd_sn, conn->sess->exp_cmd_sn)) {
+ list_del_init(&cmd->i_conn_node);
+ spin_unlock_bh(&conn->cmd_lock);
+ iscsit_free_cmd(cmd, true);
+ spin_lock_bh(&conn->cmd_lock);
+ continue;
+ }
+
+ cmd_count++;
+ pr_debug("Preparing Opcode: 0x%02x, ITT: 0x%08x,"
+ " CmdSN: 0x%08x, StatSN: 0x%08x, CID: %hu for"
+ " realligence.\n", cmd->iscsi_opcode,
+ cmd->init_task_tag, cmd->cmd_sn, cmd->stat_sn,
+ conn->cid);
+
+ cmd->deferred_i_state = cmd->i_state;
+ cmd->i_state = ISTATE_IN_CONNECTION_RECOVERY;
+
+ if (cmd->data_direction == DMA_TO_DEVICE)
+ iscsit_stop_dataout_timer(cmd);
+
+ cmd->sess = conn->sess;
+
+ list_del_init(&cmd->i_conn_node);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ iscsit_free_all_datain_reqs(cmd);
+
+ transport_wait_for_tasks(&cmd->se_cmd);
+ /*
+ * Add the struct iscsi_cmd to the connection recovery cmd list
+ */
+ spin_lock(&cr->conn_recovery_cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &cr->conn_recovery_cmd_list);
+ spin_unlock(&cr->conn_recovery_cmd_lock);
+
+ spin_lock_bh(&conn->cmd_lock);
+ cmd->cr = cr;
+ cmd->conn = NULL;
+ }
+ spin_unlock_bh(&conn->cmd_lock);
+ /*
+ * Fill in the various values in the preallocated struct iscsi_conn_recovery.
+ */
+ cr->cid = conn->cid;
+ cr->cmd_count = cmd_count;
+ cr->maxrecvdatasegmentlength = conn->conn_ops->MaxRecvDataSegmentLength;
+ cr->maxxmitdatasegmentlength = conn->conn_ops->MaxXmitDataSegmentLength;
+ cr->sess = conn->sess;
+
+ iscsit_attach_inactive_connection_recovery_entry(conn->sess, cr);
+
+ return 0;
+}
+
+int iscsit_connection_recovery_transport_reset(struct iscsi_conn *conn)
+{
+ atomic_set(&conn->connection_recovery, 1);
+
+ if (iscsit_close_connection(conn) < 0)
+ return -1;
+
+ return 0;
+}
diff --git a/drivers/target/iscsi/iscsi_target_erl2.h b/drivers/target/iscsi/iscsi_target_erl2.h
new file mode 100644
index 00000000000..63f2501f3fe
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_erl2.h
@@ -0,0 +1,18 @@
+#ifndef ISCSI_TARGET_ERL2_H
+#define ISCSI_TARGET_ERL2_H
+
+extern void iscsit_create_conn_recovery_datain_values(struct iscsi_cmd *, __be32);
+extern void iscsit_create_conn_recovery_dataout_values(struct iscsi_cmd *);
+extern struct iscsi_conn_recovery *iscsit_get_inactive_connection_recovery_entry(
+ struct iscsi_session *, u16);
+extern void iscsit_free_connection_recovery_entires(struct iscsi_session *);
+extern int iscsit_remove_active_connection_recovery_entry(
+ struct iscsi_conn_recovery *, struct iscsi_session *);
+extern int iscsit_remove_cmd_from_connection_recovery(struct iscsi_cmd *,
+ struct iscsi_session *);
+extern void iscsit_discard_cr_cmds_by_expstatsn(struct iscsi_conn_recovery *, u32);
+extern int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *);
+extern int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *);
+extern int iscsit_connection_recovery_transport_reset(struct iscsi_conn *);
+
+#endif /*** ISCSI_TARGET_ERL2_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
new file mode 100644
index 00000000000..5e71ac60941
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -0,0 +1,1457 @@
+/*******************************************************************************
+ * This file contains the login functions used by the iSCSI Target driver.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/string.h>
+#include <linux/kthread.h>
+#include <linux/crypto.h>
+#include <linux/idr.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_tq.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_nego.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target_login.h"
+#include "iscsi_target_stat.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+#include "iscsi_target_parameters.h"
+
+#include <target/iscsi/iscsi_transport.h>
+
+static struct iscsi_login *iscsi_login_init_conn(struct iscsi_conn *conn)
+{
+ struct iscsi_login *login;
+
+ login = kzalloc(sizeof(struct iscsi_login), GFP_KERNEL);
+ if (!login) {
+ pr_err("Unable to allocate memory for struct iscsi_login.\n");
+ return NULL;
+ }
+ conn->login = login;
+ login->conn = conn;
+ login->first_request = 1;
+
+ login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL);
+ if (!login->req_buf) {
+ pr_err("Unable to allocate memory for response buffer.\n");
+ goto out_login;
+ }
+
+ login->rsp_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL);
+ if (!login->rsp_buf) {
+ pr_err("Unable to allocate memory for request buffer.\n");
+ goto out_req_buf;
+ }
+
+ conn->conn_ops = kzalloc(sizeof(struct iscsi_conn_ops), GFP_KERNEL);
+ if (!conn->conn_ops) {
+ pr_err("Unable to allocate memory for"
+ " struct iscsi_conn_ops.\n");
+ goto out_rsp_buf;
+ }
+
+ init_waitqueue_head(&conn->queues_wq);
+ INIT_LIST_HEAD(&conn->conn_list);
+ INIT_LIST_HEAD(&conn->conn_cmd_list);
+ INIT_LIST_HEAD(&conn->immed_queue_list);
+ INIT_LIST_HEAD(&conn->response_queue_list);
+ init_completion(&conn->conn_post_wait_comp);
+ init_completion(&conn->conn_wait_comp);
+ init_completion(&conn->conn_wait_rcfr_comp);
+ init_completion(&conn->conn_waiting_on_uc_comp);
+ init_completion(&conn->conn_logout_comp);
+ init_completion(&conn->rx_half_close_comp);
+ init_completion(&conn->tx_half_close_comp);
+ spin_lock_init(&conn->cmd_lock);
+ spin_lock_init(&conn->conn_usage_lock);
+ spin_lock_init(&conn->immed_queue_lock);
+ spin_lock_init(&conn->nopin_timer_lock);
+ spin_lock_init(&conn->response_queue_lock);
+ spin_lock_init(&conn->state_lock);
+
+ if (!zalloc_cpumask_var(&conn->conn_cpumask, GFP_KERNEL)) {
+ pr_err("Unable to allocate conn->conn_cpumask\n");
+ goto out_conn_ops;
+ }
+ conn->conn_login = login;
+
+ return login;
+
+out_conn_ops:
+ kfree(conn->conn_ops);
+out_rsp_buf:
+ kfree(login->rsp_buf);
+out_req_buf:
+ kfree(login->req_buf);
+out_login:
+ kfree(login);
+ return NULL;
+}
+
+/*
+ * Used by iscsi_target_nego.c:iscsi_target_locate_portal() to setup
+ * per struct iscsi_conn libcrypto contexts for crc32c and crc32-intel
+ */
+int iscsi_login_setup_crypto(struct iscsi_conn *conn)
+{
+ /*
+ * Setup slicing by CRC32C algorithm for RX and TX libcrypto contexts
+ * which will default to crc32c_intel.ko for cpu_has_xmm4_2, or fallback
+ * to software 1x8 byte slicing from crc32c.ko
+ */
+ conn->conn_rx_hash.flags = 0;
+ conn->conn_rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(conn->conn_rx_hash.tfm)) {
+ pr_err("crypto_alloc_hash() failed for conn_rx_tfm\n");
+ return -ENOMEM;
+ }
+
+ conn->conn_tx_hash.flags = 0;
+ conn->conn_tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(conn->conn_tx_hash.tfm)) {
+ pr_err("crypto_alloc_hash() failed for conn_tx_tfm\n");
+ crypto_free_hash(conn->conn_rx_hash.tfm);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int iscsi_login_check_initiator_version(
+ struct iscsi_conn *conn,
+ u8 version_max,
+ u8 version_min)
+{
+ if ((version_max != 0x00) || (version_min != 0x00)) {
+ pr_err("Unsupported iSCSI IETF Pre-RFC Revision,"
+ " version Min/Max 0x%02x/0x%02x, rejecting login.\n",
+ version_min, version_max);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_NO_VERSION);
+ return -1;
+ }
+
+ return 0;
+}
+
+int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
+{
+ int sessiontype;
+ struct iscsi_param *initiatorname_param = NULL, *sessiontype_param = NULL;
+ struct iscsi_portal_group *tpg = conn->tpg;
+ struct iscsi_session *sess = NULL, *sess_p = NULL;
+ struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+ struct se_session *se_sess, *se_sess_tmp;
+
+ initiatorname_param = iscsi_find_param_from_key(
+ INITIATORNAME, conn->param_list);
+ sessiontype_param = iscsi_find_param_from_key(
+ SESSIONTYPE, conn->param_list);
+ if (!initiatorname_param || !sessiontype_param) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_MISSING_FIELDS);
+ return -1;
+ }
+
+ sessiontype = (strncmp(sessiontype_param->value, NORMAL, 6)) ? 1 : 0;
+
+ spin_lock_bh(&se_tpg->session_lock);
+ list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list,
+ sess_list) {
+
+ sess_p = se_sess->fabric_sess_ptr;
+ spin_lock(&sess_p->conn_lock);
+ if (atomic_read(&sess_p->session_fall_back_to_erl0) ||
+ atomic_read(&sess_p->session_logout) ||
+ (sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
+ spin_unlock(&sess_p->conn_lock);
+ continue;
+ }
+ if (!memcmp(sess_p->isid, conn->sess->isid, 6) &&
+ (!strcmp(sess_p->sess_ops->InitiatorName,
+ initiatorname_param->value) &&
+ (sess_p->sess_ops->SessionType == sessiontype))) {
+ atomic_set(&sess_p->session_reinstatement, 1);
+ spin_unlock(&sess_p->conn_lock);
+ iscsit_inc_session_usage_count(sess_p);
+ iscsit_stop_time2retain_timer(sess_p);
+ sess = sess_p;
+ break;
+ }
+ spin_unlock(&sess_p->conn_lock);
+ }
+ spin_unlock_bh(&se_tpg->session_lock);
+ /*
+ * If the Time2Retain handler has expired, the session is already gone.
+ */
+ if (!sess)
+ return 0;
+
+ pr_debug("%s iSCSI Session SID %u is still active for %s,"
+ " preforming session reinstatement.\n", (sessiontype) ?
+ "Discovery" : "Normal", sess->sid,
+ sess->sess_ops->InitiatorName);
+
+ spin_lock_bh(&sess->conn_lock);
+ if (sess->session_state == TARG_SESS_STATE_FAILED) {
+ spin_unlock_bh(&sess->conn_lock);
+ iscsit_dec_session_usage_count(sess);
+ target_put_session(sess->se_sess);
+ return 0;
+ }
+ spin_unlock_bh(&sess->conn_lock);
+
+ iscsit_stop_session(sess, 1, 1);
+ iscsit_dec_session_usage_count(sess);
+
+ target_put_session(sess->se_sess);
+ return 0;
+}
+
+static void iscsi_login_set_conn_values(
+ struct iscsi_session *sess,
+ struct iscsi_conn *conn,
+ __be16 cid)
+{
+ conn->sess = sess;
+ conn->cid = be16_to_cpu(cid);
+ /*
+ * Generate a random Status sequence number (statsn) for the new
+ * iSCSI connection.
+ */
+ get_random_bytes(&conn->stat_sn, sizeof(u32));
+
+ mutex_lock(&auth_id_lock);
+ conn->auth_id = iscsit_global->auth_id++;
+ mutex_unlock(&auth_id_lock);
+}
+
+static __printf(2, 3) int iscsi_change_param_sprintf(
+ struct iscsi_conn *conn,
+ const char *fmt, ...)
+{
+ va_list args;
+ unsigned char buf[64];
+
+ memset(buf, 0, sizeof buf);
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof buf, fmt, args);
+ va_end(args);
+
+ if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * This is the leading connection of a new session,
+ * or session reinstatement.
+ */
+static int iscsi_login_zero_tsih_s1(
+ struct iscsi_conn *conn,
+ unsigned char *buf)
+{
+ struct iscsi_session *sess = NULL;
+ struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf;
+ enum target_prot_op sup_pro_ops;
+ int ret;
+
+ sess = kzalloc(sizeof(struct iscsi_session), GFP_KERNEL);
+ if (!sess) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ pr_err("Could not allocate memory for session\n");
+ return -ENOMEM;
+ }
+
+ iscsi_login_set_conn_values(sess, conn, pdu->cid);
+ sess->init_task_tag = pdu->itt;
+ memcpy(&sess->isid, pdu->isid, 6);
+ sess->exp_cmd_sn = be32_to_cpu(pdu->cmdsn);
+ INIT_LIST_HEAD(&sess->sess_conn_list);
+ INIT_LIST_HEAD(&sess->sess_ooo_cmdsn_list);
+ INIT_LIST_HEAD(&sess->cr_active_list);
+ INIT_LIST_HEAD(&sess->cr_inactive_list);
+ init_completion(&sess->async_msg_comp);
+ init_completion(&sess->reinstatement_comp);
+ init_completion(&sess->session_wait_comp);
+ init_completion(&sess->session_waiting_on_uc_comp);
+ mutex_init(&sess->cmdsn_mutex);
+ spin_lock_init(&sess->conn_lock);
+ spin_lock_init(&sess->cr_a_lock);
+ spin_lock_init(&sess->cr_i_lock);
+ spin_lock_init(&sess->session_usage_lock);
+ spin_lock_init(&sess->ttt_lock);
+
+ idr_preload(GFP_KERNEL);
+ spin_lock_bh(&sess_idr_lock);
+ ret = idr_alloc(&sess_idr, NULL, 0, 0, GFP_NOWAIT);
+ if (ret >= 0)
+ sess->session_index = ret;
+ spin_unlock_bh(&sess_idr_lock);
+ idr_preload_end();
+
+ if (ret < 0) {
+ pr_err("idr_alloc() for sess_idr failed\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ kfree(sess);
+ return -ENOMEM;
+ }
+
+ sess->creation_time = get_jiffies_64();
+ /*
+ * The FFP CmdSN window values will be allocated from the TPG's
+ * Initiator Node's ACL once the login has been successfully completed.
+ */
+ sess->max_cmd_sn = be32_to_cpu(pdu->cmdsn);
+
+ sess->sess_ops = kzalloc(sizeof(struct iscsi_sess_ops), GFP_KERNEL);
+ if (!sess->sess_ops) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ pr_err("Unable to allocate memory for"
+ " struct iscsi_sess_ops.\n");
+ kfree(sess);
+ return -ENOMEM;
+ }
+ sup_pro_ops = conn->conn_transport->iscsit_get_sup_prot_ops(conn);
+
+ sess->se_sess = transport_init_session(sup_pro_ops);
+ if (IS_ERR(sess->se_sess)) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ kfree(sess);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int iscsi_login_zero_tsih_s2(
+ struct iscsi_conn *conn)
+{
+ struct iscsi_node_attrib *na;
+ struct iscsi_session *sess = conn->sess;
+ bool iser = false;
+
+ sess->tpg = conn->tpg;
+
+ /*
+ * Assign a new TPG Session Handle. Note this is protected with
+ * struct iscsi_portal_group->np_login_sem from iscsit_access_np().
+ */
+ sess->tsih = ++sess->tpg->ntsih;
+ if (!sess->tsih)
+ sess->tsih = ++sess->tpg->ntsih;
+
+ /*
+ * Create the default params from user defined values..
+ */
+ if (iscsi_copy_param_list(&conn->param_list,
+ conn->tpg->param_list, 1) < 0) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ return -1;
+ }
+
+ if (conn->conn_transport->transport_type == ISCSI_INFINIBAND)
+ iser = true;
+
+ iscsi_set_keys_to_negotiate(conn->param_list, iser);
+
+ if (sess->sess_ops->SessionType)
+ return iscsi_set_keys_irrelevant_for_discovery(
+ conn->param_list);
+
+ na = iscsit_tpg_get_node_attrib(sess);
+
+ /*
+ * Need to send TargetPortalGroupTag back in first login response
+ * on any iSCSI connection where the Initiator provides TargetName.
+ * See 5.3.1. Login Phase Start
+ *
+ * In our case, we have already located the struct iscsi_tiqn at this point.
+ */
+ if (iscsi_change_param_sprintf(conn, "TargetPortalGroupTag=%hu", sess->tpg->tpgt))
+ return -1;
+
+ /*
+ * Workaround for Initiators that have broken connection recovery logic.
+ *
+ * "We would really like to get rid of this." Linux-iSCSI.org team
+ */
+ if (iscsi_change_param_sprintf(conn, "ErrorRecoveryLevel=%d", na->default_erl))
+ return -1;
+
+ if (iscsi_login_disable_FIM_keys(conn->param_list, conn) < 0)
+ return -1;
+ /*
+ * Set RDMAExtensions=Yes by default for iSER enabled network portals
+ */
+ if (iser) {
+ struct iscsi_param *param;
+ unsigned long mrdsl, off;
+ int rc;
+
+ if (iscsi_change_param_sprintf(conn, "RDMAExtensions=Yes"))
+ return -1;
+
+ /*
+ * Make MaxRecvDataSegmentLength PAGE_SIZE aligned for
+ * Immediate Data + Unsolicitied Data-OUT if necessary..
+ */
+ param = iscsi_find_param_from_key("MaxRecvDataSegmentLength",
+ conn->param_list);
+ if (!param) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ return -1;
+ }
+ rc = kstrtoul(param->value, 0, &mrdsl);
+ if (rc < 0) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ return -1;
+ }
+ off = mrdsl % PAGE_SIZE;
+ if (!off)
+ goto check_prot;
+
+ if (mrdsl < PAGE_SIZE)
+ mrdsl = PAGE_SIZE;
+ else
+ mrdsl -= off;
+
+ pr_warn("Aligning ISER MaxRecvDataSegmentLength: %lu down"
+ " to PAGE_SIZE\n", mrdsl);
+
+ if (iscsi_change_param_sprintf(conn, "MaxRecvDataSegmentLength=%lu\n", mrdsl))
+ return -1;
+ /*
+ * ISER currently requires that ImmediateData + Unsolicited
+ * Data be disabled when protection / signature MRs are enabled.
+ */
+check_prot:
+ if (sess->se_sess->sup_prot_ops &
+ (TARGET_PROT_DOUT_STRIP | TARGET_PROT_DOUT_PASS |
+ TARGET_PROT_DOUT_INSERT)) {
+
+ if (iscsi_change_param_sprintf(conn, "ImmediateData=No"))
+ return -1;
+
+ if (iscsi_change_param_sprintf(conn, "InitialR2T=Yes"))
+ return -1;
+
+ pr_debug("Forcing ImmediateData=No + InitialR2T=Yes for"
+ " T10-PI enabled ISER session\n");
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Remove PSTATE_NEGOTIATE for the four FIM related keys.
+ * The Initiator node will be able to enable FIM by proposing them itself.
+ */
+int iscsi_login_disable_FIM_keys(
+ struct iscsi_param_list *param_list,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_param *param;
+
+ param = iscsi_find_param_from_key("OFMarker", param_list);
+ if (!param) {
+ pr_err("iscsi_find_param_from_key() for"
+ " OFMarker failed\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ return -1;
+ }
+ param->state &= ~PSTATE_NEGOTIATE;
+
+ param = iscsi_find_param_from_key("OFMarkInt", param_list);
+ if (!param) {
+ pr_err("iscsi_find_param_from_key() for"
+ " IFMarker failed\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ return -1;
+ }
+ param->state &= ~PSTATE_NEGOTIATE;
+
+ param = iscsi_find_param_from_key("IFMarker", param_list);
+ if (!param) {
+ pr_err("iscsi_find_param_from_key() for"
+ " IFMarker failed\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ return -1;
+ }
+ param->state &= ~PSTATE_NEGOTIATE;
+
+ param = iscsi_find_param_from_key("IFMarkInt", param_list);
+ if (!param) {
+ pr_err("iscsi_find_param_from_key() for"
+ " IFMarker failed\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ return -1;
+ }
+ param->state &= ~PSTATE_NEGOTIATE;
+
+ return 0;
+}
+
+static int iscsi_login_non_zero_tsih_s1(
+ struct iscsi_conn *conn,
+ unsigned char *buf)
+{
+ struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf;
+
+ iscsi_login_set_conn_values(NULL, conn, pdu->cid);
+ return 0;
+}
+
+/*
+ * Add a new connection to an existing session.
+ */
+static int iscsi_login_non_zero_tsih_s2(
+ struct iscsi_conn *conn,
+ unsigned char *buf)
+{
+ struct iscsi_portal_group *tpg = conn->tpg;
+ struct iscsi_session *sess = NULL, *sess_p = NULL;
+ struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+ struct se_session *se_sess, *se_sess_tmp;
+ struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf;
+ bool iser = false;
+
+ spin_lock_bh(&se_tpg->session_lock);
+ list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list,
+ sess_list) {
+
+ sess_p = (struct iscsi_session *)se_sess->fabric_sess_ptr;
+ if (atomic_read(&sess_p->session_fall_back_to_erl0) ||
+ atomic_read(&sess_p->session_logout) ||
+ (sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED))
+ continue;
+ if (!memcmp(sess_p->isid, pdu->isid, 6) &&
+ (sess_p->tsih == be16_to_cpu(pdu->tsih))) {
+ iscsit_inc_session_usage_count(sess_p);
+ iscsit_stop_time2retain_timer(sess_p);
+ sess = sess_p;
+ break;
+ }
+ }
+ spin_unlock_bh(&se_tpg->session_lock);
+
+ /*
+ * If the Time2Retain handler has expired, the session is already gone.
+ */
+ if (!sess) {
+ pr_err("Initiator attempting to add a connection to"
+ " a non-existent session, rejecting iSCSI Login.\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_NO_SESSION);
+ return -1;
+ }
+
+ /*
+ * Stop the Time2Retain timer if this is a failed session, we restart
+ * the timer if the login is not successful.
+ */
+ spin_lock_bh(&sess->conn_lock);
+ if (sess->session_state == TARG_SESS_STATE_FAILED)
+ atomic_set(&sess->session_continuation, 1);
+ spin_unlock_bh(&sess->conn_lock);
+
+ iscsi_login_set_conn_values(sess, conn, pdu->cid);
+
+ if (iscsi_copy_param_list(&conn->param_list,
+ conn->tpg->param_list, 0) < 0) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ return -1;
+ }
+
+ if (conn->conn_transport->transport_type == ISCSI_INFINIBAND)
+ iser = true;
+
+ iscsi_set_keys_to_negotiate(conn->param_list, iser);
+ /*
+ * Need to send TargetPortalGroupTag back in first login response
+ * on any iSCSI connection where the Initiator provides TargetName.
+ * See 5.3.1. Login Phase Start
+ *
+ * In our case, we have already located the struct iscsi_tiqn at this point.
+ */
+ if (iscsi_change_param_sprintf(conn, "TargetPortalGroupTag=%hu", sess->tpg->tpgt))
+ return -1;
+
+ return iscsi_login_disable_FIM_keys(conn->param_list, conn);
+}
+
+int iscsi_login_post_auth_non_zero_tsih(
+ struct iscsi_conn *conn,
+ u16 cid,
+ u32 exp_statsn)
+{
+ struct iscsi_conn *conn_ptr = NULL;
+ struct iscsi_conn_recovery *cr = NULL;
+ struct iscsi_session *sess = conn->sess;
+
+ /*
+ * By following item 5 in the login table, if we have found
+ * an existing ISID and a valid/existing TSIH and an existing
+ * CID we do connection reinstatement. Currently we dont not
+ * support it so we send back an non-zero status class to the
+ * initiator and release the new connection.
+ */
+ conn_ptr = iscsit_get_conn_from_cid_rcfr(sess, cid);
+ if (conn_ptr) {
+ pr_err("Connection exists with CID %hu for %s,"
+ " performing connection reinstatement.\n",
+ conn_ptr->cid, sess->sess_ops->InitiatorName);
+
+ iscsit_connection_reinstatement_rcfr(conn_ptr);
+ iscsit_dec_conn_usage_count(conn_ptr);
+ }
+
+ /*
+ * Check for any connection recovery entires containing CID.
+ * We use the original ExpStatSN sent in the first login request
+ * to acknowledge commands for the failed connection.
+ *
+ * Also note that an explict logout may have already been sent,
+ * but the response may not be sent due to additional connection
+ * loss.
+ */
+ if (sess->sess_ops->ErrorRecoveryLevel == 2) {
+ cr = iscsit_get_inactive_connection_recovery_entry(
+ sess, cid);
+ if (cr) {
+ pr_debug("Performing implicit logout"
+ " for connection recovery on CID: %hu\n",
+ conn->cid);
+ iscsit_discard_cr_cmds_by_expstatsn(cr, exp_statsn);
+ }
+ }
+
+ /*
+ * Else we follow item 4 from the login table in that we have
+ * found an existing ISID and a valid/existing TSIH and a new
+ * CID we go ahead and continue to add a new connection to the
+ * session.
+ */
+ pr_debug("Adding CID %hu to existing session for %s.\n",
+ cid, sess->sess_ops->InitiatorName);
+
+ if ((atomic_read(&sess->nconn) + 1) > sess->sess_ops->MaxConnections) {
+ pr_err("Adding additional connection to this session"
+ " would exceed MaxConnections %d, login failed.\n",
+ sess->sess_ops->MaxConnections);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_ISID_ERROR);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void iscsi_post_login_start_timers(struct iscsi_conn *conn)
+{
+ struct iscsi_session *sess = conn->sess;
+ /*
+ * FIXME: Unsolicitied NopIN support for ISER
+ */
+ if (conn->conn_transport->transport_type == ISCSI_INFINIBAND)
+ return;
+
+ if (!sess->sess_ops->SessionType)
+ iscsit_start_nopin_timer(conn);
+}
+
+int iscsi_post_login_handler(
+ struct iscsi_np *np,
+ struct iscsi_conn *conn,
+ u8 zero_tsih)
+{
+ int stop_timer = 0;
+ struct iscsi_session *sess = conn->sess;
+ struct se_session *se_sess = sess->se_sess;
+ struct iscsi_portal_group *tpg = sess->tpg;
+ struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
+ struct iscsi_thread_set *ts;
+
+ iscsit_inc_conn_usage_count(conn);
+
+ iscsit_collect_login_stats(conn, ISCSI_STATUS_CLS_SUCCESS,
+ ISCSI_LOGIN_STATUS_ACCEPT);
+
+ pr_debug("Moving to TARG_CONN_STATE_LOGGED_IN.\n");
+ conn->conn_state = TARG_CONN_STATE_LOGGED_IN;
+
+ iscsi_set_connection_parameters(conn->conn_ops, conn->param_list);
+ iscsit_set_sync_and_steering_values(conn);
+ /*
+ * SCSI Initiator -> SCSI Target Port Mapping
+ */
+ ts = iscsi_get_thread_set();
+ if (!zero_tsih) {
+ iscsi_set_session_parameters(sess->sess_ops,
+ conn->param_list, 0);
+ iscsi_release_param_list(conn->param_list);
+ conn->param_list = NULL;
+
+ spin_lock_bh(&sess->conn_lock);
+ atomic_set(&sess->session_continuation, 0);
+ if (sess->session_state == TARG_SESS_STATE_FAILED) {
+ pr_debug("Moving to"
+ " TARG_SESS_STATE_LOGGED_IN.\n");
+ sess->session_state = TARG_SESS_STATE_LOGGED_IN;
+ stop_timer = 1;
+ }
+
+ pr_debug("iSCSI Login successful on CID: %hu from %s to"
+ " %s:%hu,%hu\n", conn->cid, conn->login_ip,
+ conn->local_ip, conn->local_port, tpg->tpgt);
+
+ list_add_tail(&conn->conn_list, &sess->sess_conn_list);
+ atomic_inc(&sess->nconn);
+ pr_debug("Incremented iSCSI Connection count to %hu"
+ " from node: %s\n", atomic_read(&sess->nconn),
+ sess->sess_ops->InitiatorName);
+ spin_unlock_bh(&sess->conn_lock);
+
+ iscsi_post_login_start_timers(conn);
+
+ iscsi_activate_thread_set(conn, ts);
+ /*
+ * Determine CPU mask to ensure connection's RX and TX kthreads
+ * are scheduled on the same CPU.
+ */
+ iscsit_thread_get_cpumask(conn);
+ conn->conn_rx_reset_cpumask = 1;
+ conn->conn_tx_reset_cpumask = 1;
+
+ iscsit_dec_conn_usage_count(conn);
+ if (stop_timer) {
+ spin_lock_bh(&se_tpg->session_lock);
+ iscsit_stop_time2retain_timer(sess);
+ spin_unlock_bh(&se_tpg->session_lock);
+ }
+ iscsit_dec_session_usage_count(sess);
+ return 0;
+ }
+
+ iscsi_set_session_parameters(sess->sess_ops, conn->param_list, 1);
+ iscsi_release_param_list(conn->param_list);
+ conn->param_list = NULL;
+
+ iscsit_determine_maxcmdsn(sess);
+
+ spin_lock_bh(&se_tpg->session_lock);
+ __transport_register_session(&sess->tpg->tpg_se_tpg,
+ se_sess->se_node_acl, se_sess, sess);
+ pr_debug("Moving to TARG_SESS_STATE_LOGGED_IN.\n");
+ sess->session_state = TARG_SESS_STATE_LOGGED_IN;
+
+ pr_debug("iSCSI Login successful on CID: %hu from %s to %s:%hu,%hu\n",
+ conn->cid, conn->login_ip, conn->local_ip, conn->local_port,
+ tpg->tpgt);
+
+ spin_lock_bh(&sess->conn_lock);
+ list_add_tail(&conn->conn_list, &sess->sess_conn_list);
+ atomic_inc(&sess->nconn);
+ pr_debug("Incremented iSCSI Connection count to %hu from node:"
+ " %s\n", atomic_read(&sess->nconn),
+ sess->sess_ops->InitiatorName);
+ spin_unlock_bh(&sess->conn_lock);
+
+ sess->sid = tpg->sid++;
+ if (!sess->sid)
+ sess->sid = tpg->sid++;
+ pr_debug("Established iSCSI session from node: %s\n",
+ sess->sess_ops->InitiatorName);
+
+ tpg->nsessions++;
+ if (tpg->tpg_tiqn)
+ tpg->tpg_tiqn->tiqn_nsessions++;
+
+ pr_debug("Incremented number of active iSCSI sessions to %u on"
+ " iSCSI Target Portal Group: %hu\n", tpg->nsessions, tpg->tpgt);
+ spin_unlock_bh(&se_tpg->session_lock);
+
+ iscsi_post_login_start_timers(conn);
+ iscsi_activate_thread_set(conn, ts);
+ /*
+ * Determine CPU mask to ensure connection's RX and TX kthreads
+ * are scheduled on the same CPU.
+ */
+ iscsit_thread_get_cpumask(conn);
+ conn->conn_rx_reset_cpumask = 1;
+ conn->conn_tx_reset_cpumask = 1;
+
+ iscsit_dec_conn_usage_count(conn);
+
+ return 0;
+}
+
+static void iscsi_handle_login_thread_timeout(unsigned long data)
+{
+ struct iscsi_np *np = (struct iscsi_np *) data;
+
+ spin_lock_bh(&np->np_thread_lock);
+ pr_err("iSCSI Login timeout on Network Portal %s:%hu\n",
+ np->np_ip, np->np_port);
+
+ if (np->np_login_timer_flags & ISCSI_TF_STOP) {
+ spin_unlock_bh(&np->np_thread_lock);
+ return;
+ }
+
+ if (np->np_thread)
+ send_sig(SIGINT, np->np_thread, 1);
+
+ np->np_login_timer_flags &= ~ISCSI_TF_RUNNING;
+ spin_unlock_bh(&np->np_thread_lock);
+}
+
+static void iscsi_start_login_thread_timer(struct iscsi_np *np)
+{
+ /*
+ * This used the TA_LOGIN_TIMEOUT constant because at this
+ * point we do not have access to ISCSI_TPG_ATTRIB(tpg)->login_timeout
+ */
+ spin_lock_bh(&np->np_thread_lock);
+ init_timer(&np->np_login_timer);
+ np->np_login_timer.expires = (get_jiffies_64() + TA_LOGIN_TIMEOUT * HZ);
+ np->np_login_timer.data = (unsigned long)np;
+ np->np_login_timer.function = iscsi_handle_login_thread_timeout;
+ np->np_login_timer_flags &= ~ISCSI_TF_STOP;
+ np->np_login_timer_flags |= ISCSI_TF_RUNNING;
+ add_timer(&np->np_login_timer);
+
+ pr_debug("Added timeout timer to iSCSI login request for"
+ " %u seconds.\n", TA_LOGIN_TIMEOUT);
+ spin_unlock_bh(&np->np_thread_lock);
+}
+
+static void iscsi_stop_login_thread_timer(struct iscsi_np *np)
+{
+ spin_lock_bh(&np->np_thread_lock);
+ if (!(np->np_login_timer_flags & ISCSI_TF_RUNNING)) {
+ spin_unlock_bh(&np->np_thread_lock);
+ return;
+ }
+ np->np_login_timer_flags |= ISCSI_TF_STOP;
+ spin_unlock_bh(&np->np_thread_lock);
+
+ del_timer_sync(&np->np_login_timer);
+
+ spin_lock_bh(&np->np_thread_lock);
+ np->np_login_timer_flags &= ~ISCSI_TF_RUNNING;
+ spin_unlock_bh(&np->np_thread_lock);
+}
+
+int iscsit_setup_np(
+ struct iscsi_np *np,
+ struct __kernel_sockaddr_storage *sockaddr)
+{
+ struct socket *sock = NULL;
+ int backlog = ISCSIT_TCP_BACKLOG, ret, opt = 0, len;
+
+ switch (np->np_network_transport) {
+ case ISCSI_TCP:
+ np->np_ip_proto = IPPROTO_TCP;
+ np->np_sock_type = SOCK_STREAM;
+ break;
+ case ISCSI_SCTP_TCP:
+ np->np_ip_proto = IPPROTO_SCTP;
+ np->np_sock_type = SOCK_STREAM;
+ break;
+ case ISCSI_SCTP_UDP:
+ np->np_ip_proto = IPPROTO_SCTP;
+ np->np_sock_type = SOCK_SEQPACKET;
+ break;
+ default:
+ pr_err("Unsupported network_transport: %d\n",
+ np->np_network_transport);
+ return -EINVAL;
+ }
+
+ np->np_ip_proto = IPPROTO_TCP;
+ np->np_sock_type = SOCK_STREAM;
+
+ ret = sock_create(sockaddr->ss_family, np->np_sock_type,
+ np->np_ip_proto, &sock);
+ if (ret < 0) {
+ pr_err("sock_create() failed.\n");
+ return ret;
+ }
+ np->np_socket = sock;
+ /*
+ * Setup the np->np_sockaddr from the passed sockaddr setup
+ * in iscsi_target_configfs.c code..
+ */
+ memcpy(&np->np_sockaddr, sockaddr,
+ sizeof(struct __kernel_sockaddr_storage));
+
+ if (sockaddr->ss_family == AF_INET6)
+ len = sizeof(struct sockaddr_in6);
+ else
+ len = sizeof(struct sockaddr_in);
+ /*
+ * Set SO_REUSEADDR, and disable Nagel Algorithm with TCP_NODELAY.
+ */
+ /* FIXME: Someone please explain why this is endian-safe */
+ opt = 1;
+ if (np->np_network_transport == ISCSI_TCP) {
+ ret = kernel_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&opt, sizeof(opt));
+ if (ret < 0) {
+ pr_err("kernel_setsockopt() for TCP_NODELAY"
+ " failed: %d\n", ret);
+ goto fail;
+ }
+ }
+
+ /* FIXME: Someone please explain why this is endian-safe */
+ ret = kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&opt, sizeof(opt));
+ if (ret < 0) {
+ pr_err("kernel_setsockopt() for SO_REUSEADDR"
+ " failed\n");
+ goto fail;
+ }
+
+ ret = kernel_setsockopt(sock, IPPROTO_IP, IP_FREEBIND,
+ (char *)&opt, sizeof(opt));
+ if (ret < 0) {
+ pr_err("kernel_setsockopt() for IP_FREEBIND"
+ " failed\n");
+ goto fail;
+ }
+
+ ret = kernel_bind(sock, (struct sockaddr *)&np->np_sockaddr, len);
+ if (ret < 0) {
+ pr_err("kernel_bind() failed: %d\n", ret);
+ goto fail;
+ }
+
+ ret = kernel_listen(sock, backlog);
+ if (ret != 0) {
+ pr_err("kernel_listen() failed: %d\n", ret);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ np->np_socket = NULL;
+ if (sock)
+ sock_release(sock);
+ return ret;
+}
+
+int iscsi_target_setup_login_socket(
+ struct iscsi_np *np,
+ struct __kernel_sockaddr_storage *sockaddr)
+{
+ struct iscsit_transport *t;
+ int rc;
+
+ t = iscsit_get_transport(np->np_network_transport);
+ if (!t)
+ return -EINVAL;
+
+ rc = t->iscsit_setup_np(np, sockaddr);
+ if (rc < 0) {
+ iscsit_put_transport(t);
+ return rc;
+ }
+
+ np->np_transport = t;
+ np->enabled = true;
+ return 0;
+}
+
+int iscsit_accept_np(struct iscsi_np *np, struct iscsi_conn *conn)
+{
+ struct socket *new_sock, *sock = np->np_socket;
+ struct sockaddr_in sock_in;
+ struct sockaddr_in6 sock_in6;
+ int rc, err;
+
+ rc = kernel_accept(sock, &new_sock, 0);
+ if (rc < 0)
+ return rc;
+
+ conn->sock = new_sock;
+ conn->login_family = np->np_sockaddr.ss_family;
+
+ if (np->np_sockaddr.ss_family == AF_INET6) {
+ memset(&sock_in6, 0, sizeof(struct sockaddr_in6));
+
+ rc = conn->sock->ops->getname(conn->sock,
+ (struct sockaddr *)&sock_in6, &err, 1);
+ if (!rc) {
+ if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr))
+ snprintf(conn->login_ip, sizeof(conn->login_ip), "[%pI6c]",
+ &sock_in6.sin6_addr.in6_u);
+ else
+ snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI4",
+ &sock_in6.sin6_addr.s6_addr32[3]);
+ conn->login_port = ntohs(sock_in6.sin6_port);
+ }
+
+ rc = conn->sock->ops->getname(conn->sock,
+ (struct sockaddr *)&sock_in6, &err, 0);
+ if (!rc) {
+ if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr))
+ snprintf(conn->local_ip, sizeof(conn->local_ip), "[%pI6c]",
+ &sock_in6.sin6_addr.in6_u);
+ else
+ snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI4",
+ &sock_in6.sin6_addr.s6_addr32[3]);
+ conn->local_port = ntohs(sock_in6.sin6_port);
+ }
+ } else {
+ memset(&sock_in, 0, sizeof(struct sockaddr_in));
+
+ rc = conn->sock->ops->getname(conn->sock,
+ (struct sockaddr *)&sock_in, &err, 1);
+ if (!rc) {
+ sprintf(conn->login_ip, "%pI4",
+ &sock_in.sin_addr.s_addr);
+ conn->login_port = ntohs(sock_in.sin_port);
+ }
+
+ rc = conn->sock->ops->getname(conn->sock,
+ (struct sockaddr *)&sock_in, &err, 0);
+ if (!rc) {
+ sprintf(conn->local_ip, "%pI4",
+ &sock_in.sin_addr.s_addr);
+ conn->local_port = ntohs(sock_in.sin_port);
+ }
+ }
+
+ return 0;
+}
+
+int iscsit_get_login_rx(struct iscsi_conn *conn, struct iscsi_login *login)
+{
+ struct iscsi_login_req *login_req;
+ u32 padding = 0, payload_length;
+
+ if (iscsi_login_rx_data(conn, login->req, ISCSI_HDR_LEN) < 0)
+ return -1;
+
+ login_req = (struct iscsi_login_req *)login->req;
+ payload_length = ntoh24(login_req->dlength);
+ padding = ((-payload_length) & 3);
+
+ pr_debug("Got Login Command, Flags 0x%02x, ITT: 0x%08x,"
+ " CmdSN: 0x%08x, ExpStatSN: 0x%08x, CID: %hu, Length: %u\n",
+ login_req->flags, login_req->itt, login_req->cmdsn,
+ login_req->exp_statsn, login_req->cid, payload_length);
+ /*
+ * Setup the initial iscsi_login values from the leading
+ * login request PDU.
+ */
+ if (login->first_request) {
+ login_req = (struct iscsi_login_req *)login->req;
+ login->leading_connection = (!login_req->tsih) ? 1 : 0;
+ login->current_stage = ISCSI_LOGIN_CURRENT_STAGE(login_req->flags);
+ login->version_min = login_req->min_version;
+ login->version_max = login_req->max_version;
+ memcpy(login->isid, login_req->isid, 6);
+ login->cmd_sn = be32_to_cpu(login_req->cmdsn);
+ login->init_task_tag = login_req->itt;
+ login->initial_exp_statsn = be32_to_cpu(login_req->exp_statsn);
+ login->cid = be16_to_cpu(login_req->cid);
+ login->tsih = be16_to_cpu(login_req->tsih);
+ }
+
+ if (iscsi_target_check_login_request(conn, login) < 0)
+ return -1;
+
+ memset(login->req_buf, 0, MAX_KEY_VALUE_PAIRS);
+ if (iscsi_login_rx_data(conn, login->req_buf,
+ payload_length + padding) < 0)
+ return -1;
+
+ return 0;
+}
+
+int iscsit_put_login_tx(struct iscsi_conn *conn, struct iscsi_login *login,
+ u32 length)
+{
+ if (iscsi_login_tx_data(conn, login->rsp, login->rsp_buf, length) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int
+iscsit_conn_set_transport(struct iscsi_conn *conn, struct iscsit_transport *t)
+{
+ int rc;
+
+ if (!t->owner) {
+ conn->conn_transport = t;
+ return 0;
+ }
+
+ rc = try_module_get(t->owner);
+ if (!rc) {
+ pr_err("try_module_get() failed for %s\n", t->name);
+ return -EINVAL;
+ }
+
+ conn->conn_transport = t;
+ return 0;
+}
+
+void iscsi_target_login_sess_out(struct iscsi_conn *conn,
+ struct iscsi_np *np, bool zero_tsih, bool new_sess)
+{
+ if (!new_sess)
+ goto old_sess_out;
+
+ pr_err("iSCSI Login negotiation failed.\n");
+ iscsit_collect_login_stats(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ if (!zero_tsih || !conn->sess)
+ goto old_sess_out;
+ if (conn->sess->se_sess)
+ transport_free_session(conn->sess->se_sess);
+ if (conn->sess->session_index != 0) {
+ spin_lock_bh(&sess_idr_lock);
+ idr_remove(&sess_idr, conn->sess->session_index);
+ spin_unlock_bh(&sess_idr_lock);
+ }
+ kfree(conn->sess->sess_ops);
+ kfree(conn->sess);
+
+old_sess_out:
+ iscsi_stop_login_thread_timer(np);
+ /*
+ * If login negotiation fails check if the Time2Retain timer
+ * needs to be restarted.
+ */
+ if (!zero_tsih && conn->sess) {
+ spin_lock_bh(&conn->sess->conn_lock);
+ if (conn->sess->session_state == TARG_SESS_STATE_FAILED) {
+ struct se_portal_group *se_tpg =
+ &conn->tpg->tpg_se_tpg;
+
+ atomic_set(&conn->sess->session_continuation, 0);
+ spin_unlock_bh(&conn->sess->conn_lock);
+ spin_lock_bh(&se_tpg->session_lock);
+ iscsit_start_time2retain_handler(conn->sess);
+ spin_unlock_bh(&se_tpg->session_lock);
+ } else
+ spin_unlock_bh(&conn->sess->conn_lock);
+ iscsit_dec_session_usage_count(conn->sess);
+ }
+
+ if (!IS_ERR(conn->conn_rx_hash.tfm))
+ crypto_free_hash(conn->conn_rx_hash.tfm);
+ if (!IS_ERR(conn->conn_tx_hash.tfm))
+ crypto_free_hash(conn->conn_tx_hash.tfm);
+
+ if (conn->conn_cpumask)
+ free_cpumask_var(conn->conn_cpumask);
+
+ kfree(conn->conn_ops);
+
+ if (conn->param_list) {
+ iscsi_release_param_list(conn->param_list);
+ conn->param_list = NULL;
+ }
+ iscsi_target_nego_release(conn);
+
+ if (conn->sock) {
+ sock_release(conn->sock);
+ conn->sock = NULL;
+ }
+
+ if (conn->conn_transport->iscsit_free_conn)
+ conn->conn_transport->iscsit_free_conn(conn);
+
+ iscsit_put_transport(conn->conn_transport);
+ kfree(conn);
+}
+
+static int __iscsi_target_login_thread(struct iscsi_np *np)
+{
+ u8 *buffer, zero_tsih = 0;
+ int ret = 0, rc;
+ struct iscsi_conn *conn = NULL;
+ struct iscsi_login *login;
+ struct iscsi_portal_group *tpg = NULL;
+ struct iscsi_login_req *pdu;
+ struct iscsi_tpg_np *tpg_np;
+ bool new_sess = false;
+
+ flush_signals(current);
+
+ spin_lock_bh(&np->np_thread_lock);
+ if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
+ np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
+ complete(&np->np_restart_comp);
+ } else if (np->np_thread_state == ISCSI_NP_THREAD_SHUTDOWN) {
+ spin_unlock_bh(&np->np_thread_lock);
+ goto exit;
+ } else {
+ np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
+ }
+ spin_unlock_bh(&np->np_thread_lock);
+
+ conn = kzalloc(sizeof(struct iscsi_conn), GFP_KERNEL);
+ if (!conn) {
+ pr_err("Could not allocate memory for"
+ " new connection\n");
+ /* Get another socket */
+ return 1;
+ }
+ pr_debug("Moving to TARG_CONN_STATE_FREE.\n");
+ conn->conn_state = TARG_CONN_STATE_FREE;
+
+ if (iscsit_conn_set_transport(conn, np->np_transport) < 0) {
+ kfree(conn);
+ return 1;
+ }
+
+ rc = np->np_transport->iscsit_accept_np(np, conn);
+ if (rc == -ENOSYS) {
+ complete(&np->np_restart_comp);
+ iscsit_put_transport(conn->conn_transport);
+ kfree(conn);
+ conn = NULL;
+ goto exit;
+ } else if (rc < 0) {
+ spin_lock_bh(&np->np_thread_lock);
+ if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
+ spin_unlock_bh(&np->np_thread_lock);
+ complete(&np->np_restart_comp);
+ iscsit_put_transport(conn->conn_transport);
+ kfree(conn);
+ conn = NULL;
+ if (ret == -ENODEV)
+ goto out;
+ /* Get another socket */
+ return 1;
+ }
+ spin_unlock_bh(&np->np_thread_lock);
+ iscsit_put_transport(conn->conn_transport);
+ kfree(conn);
+ conn = NULL;
+ goto out;
+ }
+ /*
+ * Perform the remaining iSCSI connection initialization items..
+ */
+ login = iscsi_login_init_conn(conn);
+ if (!login) {
+ goto new_sess_out;
+ }
+
+ iscsi_start_login_thread_timer(np);
+
+ pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n");
+ conn->conn_state = TARG_CONN_STATE_XPT_UP;
+ /*
+ * This will process the first login request + payload..
+ */
+ rc = np->np_transport->iscsit_get_login_rx(conn, login);
+ if (rc == 1)
+ return 1;
+ else if (rc < 0)
+ goto new_sess_out;
+
+ buffer = &login->req[0];
+ pdu = (struct iscsi_login_req *)buffer;
+ /*
+ * Used by iscsit_tx_login_rsp() for Login Resonses PDUs
+ * when Status-Class != 0.
+ */
+ conn->login_itt = pdu->itt;
+
+ spin_lock_bh(&np->np_thread_lock);
+ if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
+ spin_unlock_bh(&np->np_thread_lock);
+ pr_err("iSCSI Network Portal on %s:%hu currently not"
+ " active.\n", np->np_ip, np->np_port);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
+ goto new_sess_out;
+ }
+ spin_unlock_bh(&np->np_thread_lock);
+
+ conn->network_transport = np->np_network_transport;
+
+ pr_debug("Received iSCSI login request from %s on %s Network"
+ " Portal %s:%hu\n", conn->login_ip, np->np_transport->name,
+ conn->local_ip, conn->local_port);
+
+ pr_debug("Moving to TARG_CONN_STATE_IN_LOGIN.\n");
+ conn->conn_state = TARG_CONN_STATE_IN_LOGIN;
+
+ if (iscsi_login_check_initiator_version(conn, pdu->max_version,
+ pdu->min_version) < 0)
+ goto new_sess_out;
+
+ zero_tsih = (pdu->tsih == 0x0000);
+ if (zero_tsih) {
+ /*
+ * This is the leading connection of a new session.
+ * We wait until after authentication to check for
+ * session reinstatement.
+ */
+ if (iscsi_login_zero_tsih_s1(conn, buffer) < 0)
+ goto new_sess_out;
+ } else {
+ /*
+ * Add a new connection to an existing session.
+ * We check for a non-existant session in
+ * iscsi_login_non_zero_tsih_s2() below based
+ * on ISID/TSIH, but wait until after authentication
+ * to check for connection reinstatement, etc.
+ */
+ if (iscsi_login_non_zero_tsih_s1(conn, buffer) < 0)
+ goto new_sess_out;
+ }
+ /*
+ * SessionType: Discovery
+ *
+ * Locates Default Portal
+ *
+ * SessionType: Normal
+ *
+ * Locates Target Portal from NP -> Target IQN
+ */
+ rc = iscsi_target_locate_portal(np, conn, login);
+ if (rc < 0) {
+ tpg = conn->tpg;
+ goto new_sess_out;
+ }
+ login->zero_tsih = zero_tsih;
+
+ tpg = conn->tpg;
+ if (!tpg) {
+ pr_err("Unable to locate struct iscsi_conn->tpg\n");
+ goto new_sess_out;
+ }
+
+ if (zero_tsih) {
+ if (iscsi_login_zero_tsih_s2(conn) < 0)
+ goto new_sess_out;
+ } else {
+ if (iscsi_login_non_zero_tsih_s2(conn, buffer) < 0)
+ goto old_sess_out;
+ }
+
+ ret = iscsi_target_start_negotiation(login, conn);
+ if (ret < 0)
+ goto new_sess_out;
+
+ if (!conn->sess) {
+ pr_err("struct iscsi_conn session pointer is NULL!\n");
+ goto new_sess_out;
+ }
+
+ iscsi_stop_login_thread_timer(np);
+
+ if (signal_pending(current))
+ goto new_sess_out;
+
+ if (ret == 1) {
+ tpg_np = conn->tpg_np;
+
+ ret = iscsi_post_login_handler(np, conn, zero_tsih);
+ if (ret < 0)
+ goto new_sess_out;
+
+ iscsit_deaccess_np(np, tpg, tpg_np);
+ }
+
+ tpg = NULL;
+ tpg_np = NULL;
+ /* Get another socket */
+ return 1;
+
+new_sess_out:
+ new_sess = true;
+old_sess_out:
+ tpg_np = conn->tpg_np;
+ iscsi_target_login_sess_out(conn, np, zero_tsih, new_sess);
+ new_sess = false;
+
+ if (tpg) {
+ iscsit_deaccess_np(np, tpg, tpg_np);
+ tpg = NULL;
+ tpg_np = NULL;
+ }
+
+out:
+ return 1;
+
+exit:
+ iscsi_stop_login_thread_timer(np);
+ spin_lock_bh(&np->np_thread_lock);
+ np->np_thread_state = ISCSI_NP_THREAD_EXIT;
+ spin_unlock_bh(&np->np_thread_lock);
+
+ return 0;
+}
+
+int iscsi_target_login_thread(void *arg)
+{
+ struct iscsi_np *np = arg;
+ int ret;
+
+ allow_signal(SIGINT);
+
+ while (1) {
+ ret = __iscsi_target_login_thread(np);
+ /*
+ * We break and exit here unless another sock_accept() call
+ * is expected.
+ */
+ if (ret != 1)
+ break;
+ }
+
+ return 0;
+}
diff --git a/drivers/target/iscsi/iscsi_target_login.h b/drivers/target/iscsi/iscsi_target_login.h
new file mode 100644
index 00000000000..29d098324b7
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_login.h
@@ -0,0 +1,21 @@
+#ifndef ISCSI_TARGET_LOGIN_H
+#define ISCSI_TARGET_LOGIN_H
+
+extern int iscsi_login_setup_crypto(struct iscsi_conn *);
+extern int iscsi_check_for_session_reinstatement(struct iscsi_conn *);
+extern int iscsi_login_post_auth_non_zero_tsih(struct iscsi_conn *, u16, u32);
+extern int iscsit_setup_np(struct iscsi_np *,
+ struct __kernel_sockaddr_storage *);
+extern int iscsi_target_setup_login_socket(struct iscsi_np *,
+ struct __kernel_sockaddr_storage *);
+extern int iscsit_accept_np(struct iscsi_np *, struct iscsi_conn *);
+extern int iscsit_get_login_rx(struct iscsi_conn *, struct iscsi_login *);
+extern int iscsit_put_login_tx(struct iscsi_conn *, struct iscsi_login *, u32);
+extern void iscsit_free_conn(struct iscsi_np *, struct iscsi_conn *);
+extern int iscsi_post_login_handler(struct iscsi_np *, struct iscsi_conn *, u8);
+extern void iscsi_target_login_sess_out(struct iscsi_conn *, struct iscsi_np *,
+ bool, bool);
+extern int iscsi_target_login_thread(void *);
+extern int iscsi_login_disable_FIM_keys(struct iscsi_param_list *, struct iscsi_conn *);
+
+#endif /*** ISCSI_TARGET_LOGIN_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
new file mode 100644
index 00000000000..62a095f36bf
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -0,0 +1,1254 @@
+/*******************************************************************************
+ * This file contains main functions related to iSCSI Parameter negotiation.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/ctype.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/iscsi/iscsi_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_parameters.h"
+#include "iscsi_target_login.h"
+#include "iscsi_target_nego.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+#include "iscsi_target_auth.h"
+
+#define MAX_LOGIN_PDUS 7
+#define TEXT_LEN 4096
+
+void convert_null_to_semi(char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (buf[i] == '\0')
+ buf[i] = ';';
+}
+
+static int strlen_semi(char *buf)
+{
+ int i = 0;
+
+ while (buf[i] != '\0') {
+ if (buf[i] == ';')
+ return i;
+ i++;
+ }
+
+ return -1;
+}
+
+int extract_param(
+ const char *in_buf,
+ const char *pattern,
+ unsigned int max_length,
+ char *out_buf,
+ unsigned char *type)
+{
+ char *ptr;
+ int len;
+
+ if (!in_buf || !pattern || !out_buf || !type)
+ return -1;
+
+ ptr = strstr(in_buf, pattern);
+ if (!ptr)
+ return -1;
+
+ ptr = strstr(ptr, "=");
+ if (!ptr)
+ return -1;
+
+ ptr += 1;
+ if (*ptr == '0' && (*(ptr+1) == 'x' || *(ptr+1) == 'X')) {
+ ptr += 2; /* skip 0x */
+ *type = HEX;
+ } else
+ *type = DECIMAL;
+
+ len = strlen_semi(ptr);
+ if (len < 0)
+ return -1;
+
+ if (len >= max_length) {
+ pr_err("Length of input: %d exceeds max_length:"
+ " %d\n", len, max_length);
+ return -1;
+ }
+ memcpy(out_buf, ptr, len);
+ out_buf[len] = '\0';
+
+ return 0;
+}
+
+static u32 iscsi_handle_authentication(
+ struct iscsi_conn *conn,
+ char *in_buf,
+ char *out_buf,
+ int in_length,
+ int *out_length,
+ unsigned char *authtype)
+{
+ struct iscsi_session *sess = conn->sess;
+ struct iscsi_node_auth *auth;
+ struct iscsi_node_acl *iscsi_nacl;
+ struct iscsi_portal_group *iscsi_tpg;
+ struct se_node_acl *se_nacl;
+
+ if (!sess->sess_ops->SessionType) {
+ /*
+ * For SessionType=Normal
+ */
+ se_nacl = conn->sess->se_sess->se_node_acl;
+ if (!se_nacl) {
+ pr_err("Unable to locate struct se_node_acl for"
+ " CHAP auth\n");
+ return -1;
+ }
+ iscsi_nacl = container_of(se_nacl, struct iscsi_node_acl,
+ se_node_acl);
+ if (!iscsi_nacl) {
+ pr_err("Unable to locate struct iscsi_node_acl for"
+ " CHAP auth\n");
+ return -1;
+ }
+
+ if (se_nacl->dynamic_node_acl) {
+ iscsi_tpg = container_of(se_nacl->se_tpg,
+ struct iscsi_portal_group, tpg_se_tpg);
+
+ auth = &iscsi_tpg->tpg_demo_auth;
+ } else {
+ iscsi_nacl = container_of(se_nacl, struct iscsi_node_acl,
+ se_node_acl);
+
+ auth = &iscsi_nacl->node_auth;
+ }
+ } else {
+ /*
+ * For SessionType=Discovery
+ */
+ auth = &iscsit_global->discovery_acl.node_auth;
+ }
+
+ if (strstr("CHAP", authtype))
+ strcpy(conn->sess->auth_type, "CHAP");
+ else
+ strcpy(conn->sess->auth_type, NONE);
+
+ if (strstr("None", authtype))
+ return 1;
+#ifdef CANSRP
+ else if (strstr("SRP", authtype))
+ return srp_main_loop(conn, auth, in_buf, out_buf,
+ &in_length, out_length);
+#endif
+ else if (strstr("CHAP", authtype))
+ return chap_main_loop(conn, auth, in_buf, out_buf,
+ &in_length, out_length);
+ else if (strstr("SPKM1", authtype))
+ return 2;
+ else if (strstr("SPKM2", authtype))
+ return 2;
+ else if (strstr("KRB5", authtype))
+ return 2;
+ else
+ return 2;
+}
+
+static void iscsi_remove_failed_auth_entry(struct iscsi_conn *conn)
+{
+ kfree(conn->auth_protocol);
+}
+
+int iscsi_target_check_login_request(
+ struct iscsi_conn *conn,
+ struct iscsi_login *login)
+{
+ int req_csg, req_nsg;
+ u32 payload_length;
+ struct iscsi_login_req *login_req;
+
+ login_req = (struct iscsi_login_req *) login->req;
+ payload_length = ntoh24(login_req->dlength);
+
+ switch (login_req->opcode & ISCSI_OPCODE_MASK) {
+ case ISCSI_OP_LOGIN:
+ break;
+ default:
+ pr_err("Received unknown opcode 0x%02x.\n",
+ login_req->opcode & ISCSI_OPCODE_MASK);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ return -1;
+ }
+
+ if ((login_req->flags & ISCSI_FLAG_LOGIN_CONTINUE) &&
+ (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) {
+ pr_err("Login request has both ISCSI_FLAG_LOGIN_CONTINUE"
+ " and ISCSI_FLAG_LOGIN_TRANSIT set, protocol error.\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ return -1;
+ }
+
+ req_csg = ISCSI_LOGIN_CURRENT_STAGE(login_req->flags);
+ req_nsg = ISCSI_LOGIN_NEXT_STAGE(login_req->flags);
+
+ if (req_csg != login->current_stage) {
+ pr_err("Initiator unexpectedly changed login stage"
+ " from %d to %d, login failed.\n", login->current_stage,
+ req_csg);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ return -1;
+ }
+
+ if ((req_nsg == 2) || (req_csg >= 2) ||
+ ((login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT) &&
+ (req_nsg <= req_csg))) {
+ pr_err("Illegal login_req->flags Combination, CSG: %d,"
+ " NSG: %d, ISCSI_FLAG_LOGIN_TRANSIT: %d.\n", req_csg,
+ req_nsg, (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT));
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ return -1;
+ }
+
+ if ((login_req->max_version != login->version_max) ||
+ (login_req->min_version != login->version_min)) {
+ pr_err("Login request changed Version Max/Nin"
+ " unexpectedly to 0x%02x/0x%02x, protocol error\n",
+ login_req->max_version, login_req->min_version);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ return -1;
+ }
+
+ if (memcmp(login_req->isid, login->isid, 6) != 0) {
+ pr_err("Login request changed ISID unexpectedly,"
+ " protocol error.\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ return -1;
+ }
+
+ if (login_req->itt != login->init_task_tag) {
+ pr_err("Login request changed ITT unexpectedly to"
+ " 0x%08x, protocol error.\n", login_req->itt);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ return -1;
+ }
+
+ if (payload_length > MAX_KEY_VALUE_PAIRS) {
+ pr_err("Login request payload exceeds default"
+ " MaxRecvDataSegmentLength: %u, protocol error.\n",
+ MAX_KEY_VALUE_PAIRS);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int iscsi_target_check_first_request(
+ struct iscsi_conn *conn,
+ struct iscsi_login *login)
+{
+ struct iscsi_param *param = NULL;
+ struct se_node_acl *se_nacl;
+
+ login->first_request = 0;
+
+ list_for_each_entry(param, &conn->param_list->param_list, p_list) {
+ if (!strncmp(param->name, SESSIONTYPE, 11)) {
+ if (!IS_PSTATE_ACCEPTOR(param)) {
+ pr_err("SessionType key not received"
+ " in first login request.\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_MISSING_FIELDS);
+ return -1;
+ }
+ if (!strncmp(param->value, DISCOVERY, 9))
+ return 0;
+ }
+
+ if (!strncmp(param->name, INITIATORNAME, 13)) {
+ if (!IS_PSTATE_ACCEPTOR(param)) {
+ if (!login->leading_connection)
+ continue;
+
+ pr_err("InitiatorName key not received"
+ " in first login request.\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_MISSING_FIELDS);
+ return -1;
+ }
+
+ /*
+ * For non-leading connections, double check that the
+ * received InitiatorName matches the existing session's
+ * struct iscsi_node_acl.
+ */
+ if (!login->leading_connection) {
+ se_nacl = conn->sess->se_sess->se_node_acl;
+ if (!se_nacl) {
+ pr_err("Unable to locate"
+ " struct se_node_acl\n");
+ iscsit_tx_login_rsp(conn,
+ ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_TGT_NOT_FOUND);
+ return -1;
+ }
+
+ if (strcmp(param->value,
+ se_nacl->initiatorname)) {
+ pr_err("Incorrect"
+ " InitiatorName: %s for this"
+ " iSCSI Initiator Node.\n",
+ param->value);
+ iscsit_tx_login_rsp(conn,
+ ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_TGT_NOT_FOUND);
+ return -1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_login *login)
+{
+ u32 padding = 0;
+ struct iscsi_session *sess = conn->sess;
+ struct iscsi_login_rsp *login_rsp;
+
+ login_rsp = (struct iscsi_login_rsp *) login->rsp;
+
+ login_rsp->opcode = ISCSI_OP_LOGIN_RSP;
+ hton24(login_rsp->dlength, login->rsp_length);
+ memcpy(login_rsp->isid, login->isid, 6);
+ login_rsp->tsih = cpu_to_be16(login->tsih);
+ login_rsp->itt = login->init_task_tag;
+ login_rsp->statsn = cpu_to_be32(conn->stat_sn++);
+ login_rsp->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
+ login_rsp->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
+
+ pr_debug("Sending Login Response, Flags: 0x%02x, ITT: 0x%08x,"
+ " ExpCmdSN; 0x%08x, MaxCmdSN: 0x%08x, StatSN: 0x%08x, Length:"
+ " %u\n", login_rsp->flags, (__force u32)login_rsp->itt,
+ ntohl(login_rsp->exp_cmdsn), ntohl(login_rsp->max_cmdsn),
+ ntohl(login_rsp->statsn), login->rsp_length);
+
+ padding = ((-login->rsp_length) & 3);
+
+ if (conn->conn_transport->iscsit_put_login_tx(conn, login,
+ login->rsp_length + padding) < 0)
+ return -1;
+
+ login->rsp_length = 0;
+ mutex_lock(&sess->cmdsn_mutex);
+ login_rsp->exp_cmdsn = cpu_to_be32(sess->exp_cmd_sn);
+ login_rsp->max_cmdsn = cpu_to_be32(sess->max_cmd_sn);
+ mutex_unlock(&sess->cmdsn_mutex);
+
+ return 0;
+}
+
+static void iscsi_target_sk_data_ready(struct sock *sk)
+{
+ struct iscsi_conn *conn = sk->sk_user_data;
+ bool rc;
+
+ pr_debug("Entering iscsi_target_sk_data_ready: conn: %p\n", conn);
+
+ write_lock_bh(&sk->sk_callback_lock);
+ if (!sk->sk_user_data) {
+ write_unlock_bh(&sk->sk_callback_lock);
+ return;
+ }
+ if (!test_bit(LOGIN_FLAGS_READY, &conn->login_flags)) {
+ write_unlock_bh(&sk->sk_callback_lock);
+ pr_debug("Got LOGIN_FLAGS_READY=0, conn: %p >>>>\n", conn);
+ return;
+ }
+ if (test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
+ write_unlock_bh(&sk->sk_callback_lock);
+ pr_debug("Got LOGIN_FLAGS_CLOSED=1, conn: %p >>>>\n", conn);
+ return;
+ }
+ if (test_and_set_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) {
+ write_unlock_bh(&sk->sk_callback_lock);
+ pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1, conn: %p >>>>\n", conn);
+ return;
+ }
+
+ rc = schedule_delayed_work(&conn->login_work, 0);
+ if (!rc) {
+ pr_debug("iscsi_target_sk_data_ready, schedule_delayed_work"
+ " got false\n");
+ }
+ write_unlock_bh(&sk->sk_callback_lock);
+}
+
+static void iscsi_target_sk_state_change(struct sock *);
+
+static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn)
+{
+ struct sock *sk;
+
+ if (!conn->sock)
+ return;
+
+ sk = conn->sock->sk;
+ pr_debug("Entering iscsi_target_set_sock_callbacks: conn: %p\n", conn);
+
+ write_lock_bh(&sk->sk_callback_lock);
+ sk->sk_user_data = conn;
+ conn->orig_data_ready = sk->sk_data_ready;
+ conn->orig_state_change = sk->sk_state_change;
+ sk->sk_data_ready = iscsi_target_sk_data_ready;
+ sk->sk_state_change = iscsi_target_sk_state_change;
+ write_unlock_bh(&sk->sk_callback_lock);
+
+ sk->sk_sndtimeo = TA_LOGIN_TIMEOUT * HZ;
+ sk->sk_rcvtimeo = TA_LOGIN_TIMEOUT * HZ;
+}
+
+static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn)
+{
+ struct sock *sk;
+
+ if (!conn->sock)
+ return;
+
+ sk = conn->sock->sk;
+ pr_debug("Entering iscsi_target_restore_sock_callbacks: conn: %p\n", conn);
+
+ write_lock_bh(&sk->sk_callback_lock);
+ if (!sk->sk_user_data) {
+ write_unlock_bh(&sk->sk_callback_lock);
+ return;
+ }
+ sk->sk_user_data = NULL;
+ sk->sk_data_ready = conn->orig_data_ready;
+ sk->sk_state_change = conn->orig_state_change;
+ write_unlock_bh(&sk->sk_callback_lock);
+
+ sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
+ sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
+}
+
+static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *);
+
+static bool iscsi_target_sk_state_check(struct sock *sk)
+{
+ if (sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) {
+ pr_debug("iscsi_target_sk_state_check: TCP_CLOSE_WAIT|TCP_CLOSE,"
+ "returning FALSE\n");
+ return false;
+ }
+ return true;
+}
+
+static void iscsi_target_login_drop(struct iscsi_conn *conn, struct iscsi_login *login)
+{
+ struct iscsi_np *np = login->np;
+ bool zero_tsih = login->zero_tsih;
+
+ iscsi_remove_failed_auth_entry(conn);
+ iscsi_target_nego_release(conn);
+ iscsi_target_login_sess_out(conn, np, zero_tsih, true);
+}
+
+static void iscsi_target_login_timeout(unsigned long data)
+{
+ struct iscsi_conn *conn = (struct iscsi_conn *)data;
+
+ pr_debug("Entering iscsi_target_login_timeout >>>>>>>>>>>>>>>>>>>\n");
+
+ if (conn->login_kworker) {
+ pr_debug("Sending SIGINT to conn->login_kworker %s/%d\n",
+ conn->login_kworker->comm, conn->login_kworker->pid);
+ send_sig(SIGINT, conn->login_kworker, 1);
+ }
+}
+
+static void iscsi_target_do_login_rx(struct work_struct *work)
+{
+ struct iscsi_conn *conn = container_of(work,
+ struct iscsi_conn, login_work.work);
+ struct iscsi_login *login = conn->login;
+ struct iscsi_np *np = login->np;
+ struct iscsi_portal_group *tpg = conn->tpg;
+ struct iscsi_tpg_np *tpg_np = conn->tpg_np;
+ struct timer_list login_timer;
+ int rc, zero_tsih = login->zero_tsih;
+ bool state;
+
+ pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
+ conn, current->comm, current->pid);
+
+ spin_lock(&tpg->tpg_state_lock);
+ state = (tpg->tpg_state == TPG_STATE_ACTIVE);
+ spin_unlock(&tpg->tpg_state_lock);
+
+ if (!state) {
+ pr_debug("iscsi_target_do_login_rx: tpg_state != TPG_STATE_ACTIVE\n");
+ iscsi_target_restore_sock_callbacks(conn);
+ iscsi_target_login_drop(conn, login);
+ iscsit_deaccess_np(np, tpg, tpg_np);
+ return;
+ }
+
+ if (conn->sock) {
+ struct sock *sk = conn->sock->sk;
+
+ read_lock_bh(&sk->sk_callback_lock);
+ state = iscsi_target_sk_state_check(sk);
+ read_unlock_bh(&sk->sk_callback_lock);
+
+ if (!state) {
+ pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
+ iscsi_target_restore_sock_callbacks(conn);
+ iscsi_target_login_drop(conn, login);
+ iscsit_deaccess_np(np, tpg, tpg_np);
+ return;
+ }
+ }
+
+ conn->login_kworker = current;
+ allow_signal(SIGINT);
+
+ init_timer(&login_timer);
+ login_timer.expires = (get_jiffies_64() + TA_LOGIN_TIMEOUT * HZ);
+ login_timer.data = (unsigned long)conn;
+ login_timer.function = iscsi_target_login_timeout;
+ add_timer(&login_timer);
+ pr_debug("Starting login_timer for %s/%d\n", current->comm, current->pid);
+
+ rc = conn->conn_transport->iscsit_get_login_rx(conn, login);
+ del_timer_sync(&login_timer);
+ flush_signals(current);
+ conn->login_kworker = NULL;
+
+ if (rc < 0) {
+ iscsi_target_restore_sock_callbacks(conn);
+ iscsi_target_login_drop(conn, login);
+ iscsit_deaccess_np(np, tpg, tpg_np);
+ return;
+ }
+
+ pr_debug("iscsi_target_do_login_rx after rx_login_io, %p, %s:%d\n",
+ conn, current->comm, current->pid);
+
+ rc = iscsi_target_do_login(conn, login);
+ if (rc < 0) {
+ iscsi_target_restore_sock_callbacks(conn);
+ iscsi_target_login_drop(conn, login);
+ iscsit_deaccess_np(np, tpg, tpg_np);
+ } else if (!rc) {
+ if (conn->sock) {
+ struct sock *sk = conn->sock->sk;
+
+ write_lock_bh(&sk->sk_callback_lock);
+ clear_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags);
+ write_unlock_bh(&sk->sk_callback_lock);
+ }
+ } else if (rc == 1) {
+ iscsi_target_nego_release(conn);
+ iscsi_post_login_handler(np, conn, zero_tsih);
+ iscsit_deaccess_np(np, tpg, tpg_np);
+ }
+}
+
+static void iscsi_target_do_cleanup(struct work_struct *work)
+{
+ struct iscsi_conn *conn = container_of(work,
+ struct iscsi_conn, login_cleanup_work.work);
+ struct sock *sk = conn->sock->sk;
+ struct iscsi_login *login = conn->login;
+ struct iscsi_np *np = login->np;
+ struct iscsi_portal_group *tpg = conn->tpg;
+ struct iscsi_tpg_np *tpg_np = conn->tpg_np;
+
+ pr_debug("Entering iscsi_target_do_cleanup\n");
+
+ cancel_delayed_work_sync(&conn->login_work);
+ conn->orig_state_change(sk);
+
+ iscsi_target_restore_sock_callbacks(conn);
+ iscsi_target_login_drop(conn, login);
+ iscsit_deaccess_np(np, tpg, tpg_np);
+
+ pr_debug("iscsi_target_do_cleanup done()\n");
+}
+
+static void iscsi_target_sk_state_change(struct sock *sk)
+{
+ struct iscsi_conn *conn;
+ void (*orig_state_change)(struct sock *);
+ bool state;
+
+ pr_debug("Entering iscsi_target_sk_state_change\n");
+
+ write_lock_bh(&sk->sk_callback_lock);
+ conn = sk->sk_user_data;
+ if (!conn) {
+ write_unlock_bh(&sk->sk_callback_lock);
+ return;
+ }
+ orig_state_change = conn->orig_state_change;
+
+ if (!test_bit(LOGIN_FLAGS_READY, &conn->login_flags)) {
+ pr_debug("Got LOGIN_FLAGS_READY=0 sk_state_change conn: %p\n",
+ conn);
+ write_unlock_bh(&sk->sk_callback_lock);
+ orig_state_change(sk);
+ return;
+ }
+ if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) {
+ pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1 sk_state_change"
+ " conn: %p\n", conn);
+ write_unlock_bh(&sk->sk_callback_lock);
+ orig_state_change(sk);
+ return;
+ }
+ if (test_and_set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
+ pr_debug("Got LOGIN_FLAGS_CLOSED=1 sk_state_change conn: %p\n",
+ conn);
+ write_unlock_bh(&sk->sk_callback_lock);
+ orig_state_change(sk);
+ return;
+ }
+
+ state = iscsi_target_sk_state_check(sk);
+ write_unlock_bh(&sk->sk_callback_lock);
+
+ pr_debug("iscsi_target_sk_state_change: state: %d\n", state);
+
+ if (!state) {
+ pr_debug("iscsi_target_sk_state_change got failed state\n");
+ schedule_delayed_work(&conn->login_cleanup_work, 0);
+ return;
+ }
+ orig_state_change(sk);
+}
+
+/*
+ * NOTE: We check for existing sessions or connections AFTER the initiator
+ * has been successfully authenticated in order to protect against faked
+ * ISID/TSIH combinations.
+ */
+static int iscsi_target_check_for_existing_instances(
+ struct iscsi_conn *conn,
+ struct iscsi_login *login)
+{
+ if (login->checked_for_existing)
+ return 0;
+
+ login->checked_for_existing = 1;
+
+ if (!login->tsih)
+ return iscsi_check_for_session_reinstatement(conn);
+ else
+ return iscsi_login_post_auth_non_zero_tsih(conn, login->cid,
+ login->initial_exp_statsn);
+}
+
+static int iscsi_target_do_authentication(
+ struct iscsi_conn *conn,
+ struct iscsi_login *login)
+{
+ int authret;
+ u32 payload_length;
+ struct iscsi_param *param;
+ struct iscsi_login_req *login_req;
+ struct iscsi_login_rsp *login_rsp;
+
+ login_req = (struct iscsi_login_req *) login->req;
+ login_rsp = (struct iscsi_login_rsp *) login->rsp;
+ payload_length = ntoh24(login_req->dlength);
+
+ param = iscsi_find_param_from_key(AUTHMETHOD, conn->param_list);
+ if (!param)
+ return -1;
+
+ authret = iscsi_handle_authentication(
+ conn,
+ login->req_buf,
+ login->rsp_buf,
+ payload_length,
+ &login->rsp_length,
+ param->value);
+ switch (authret) {
+ case 0:
+ pr_debug("Received OK response"
+ " from LIO Authentication, continuing.\n");
+ break;
+ case 1:
+ pr_debug("iSCSI security negotiation"
+ " completed successfully.\n");
+ login->auth_complete = 1;
+ if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) &&
+ (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) {
+ login_rsp->flags |= (ISCSI_FLAG_LOGIN_NEXT_STAGE1 |
+ ISCSI_FLAG_LOGIN_TRANSIT);
+ login->current_stage = 1;
+ }
+ return iscsi_target_check_for_existing_instances(
+ conn, login);
+ case 2:
+ pr_err("Security negotiation"
+ " failed.\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_AUTH_FAILED);
+ return -1;
+ default:
+ pr_err("Received unknown error %d from LIO"
+ " Authentication\n", authret);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_TARGET_ERROR);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int iscsi_target_handle_csg_zero(
+ struct iscsi_conn *conn,
+ struct iscsi_login *login)
+{
+ int ret;
+ u32 payload_length;
+ struct iscsi_param *param;
+ struct iscsi_login_req *login_req;
+ struct iscsi_login_rsp *login_rsp;
+
+ login_req = (struct iscsi_login_req *) login->req;
+ login_rsp = (struct iscsi_login_rsp *) login->rsp;
+ payload_length = ntoh24(login_req->dlength);
+
+ param = iscsi_find_param_from_key(AUTHMETHOD, conn->param_list);
+ if (!param)
+ return -1;
+
+ ret = iscsi_decode_text_input(
+ PHASE_SECURITY|PHASE_DECLARATIVE,
+ SENDER_INITIATOR|SENDER_RECEIVER,
+ login->req_buf,
+ payload_length,
+ conn);
+ if (ret < 0)
+ return -1;
+
+ if (ret > 0) {
+ if (login->auth_complete) {
+ pr_err("Initiator has already been"
+ " successfully authenticated, but is still"
+ " sending %s keys.\n", param->value);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ return -1;
+ }
+
+ goto do_auth;
+ } else if (!payload_length) {
+ pr_err("Initiator sent zero length security payload,"
+ " login failed\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_AUTH_FAILED);
+ return -1;
+ }
+
+ if (login->first_request)
+ if (iscsi_target_check_first_request(conn, login) < 0)
+ return -1;
+
+ ret = iscsi_encode_text_output(
+ PHASE_SECURITY|PHASE_DECLARATIVE,
+ SENDER_TARGET,
+ login->rsp_buf,
+ &login->rsp_length,
+ conn->param_list);
+ if (ret < 0)
+ return -1;
+
+ if (!iscsi_check_negotiated_keys(conn->param_list)) {
+ if (conn->tpg->tpg_attrib.authentication &&
+ !strncmp(param->value, NONE, 4)) {
+ pr_err("Initiator sent AuthMethod=None but"
+ " Target is enforcing iSCSI Authentication,"
+ " login failed.\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_AUTH_FAILED);
+ return -1;
+ }
+
+ if (conn->tpg->tpg_attrib.authentication &&
+ !login->auth_complete)
+ return 0;
+
+ if (strncmp(param->value, NONE, 4) && !login->auth_complete)
+ return 0;
+
+ if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) &&
+ (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) {
+ login_rsp->flags |= ISCSI_FLAG_LOGIN_NEXT_STAGE1 |
+ ISCSI_FLAG_LOGIN_TRANSIT;
+ login->current_stage = 1;
+ }
+ }
+
+ return 0;
+do_auth:
+ return iscsi_target_do_authentication(conn, login);
+}
+
+static int iscsi_target_handle_csg_one(struct iscsi_conn *conn, struct iscsi_login *login)
+{
+ int ret;
+ u32 payload_length;
+ struct iscsi_login_req *login_req;
+ struct iscsi_login_rsp *login_rsp;
+
+ login_req = (struct iscsi_login_req *) login->req;
+ login_rsp = (struct iscsi_login_rsp *) login->rsp;
+ payload_length = ntoh24(login_req->dlength);
+
+ ret = iscsi_decode_text_input(
+ PHASE_OPERATIONAL|PHASE_DECLARATIVE,
+ SENDER_INITIATOR|SENDER_RECEIVER,
+ login->req_buf,
+ payload_length,
+ conn);
+ if (ret < 0) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ return -1;
+ }
+
+ if (login->first_request)
+ if (iscsi_target_check_first_request(conn, login) < 0)
+ return -1;
+
+ if (iscsi_target_check_for_existing_instances(conn, login) < 0)
+ return -1;
+
+ ret = iscsi_encode_text_output(
+ PHASE_OPERATIONAL|PHASE_DECLARATIVE,
+ SENDER_TARGET,
+ login->rsp_buf,
+ &login->rsp_length,
+ conn->param_list);
+ if (ret < 0) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_INIT_ERR);
+ return -1;
+ }
+
+ if (!login->auth_complete &&
+ conn->tpg->tpg_attrib.authentication) {
+ pr_err("Initiator is requesting CSG: 1, has not been"
+ " successfully authenticated, and the Target is"
+ " enforcing iSCSI Authentication, login failed.\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_AUTH_FAILED);
+ return -1;
+ }
+
+ if (!iscsi_check_negotiated_keys(conn->param_list))
+ if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE3) &&
+ (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT))
+ login_rsp->flags |= ISCSI_FLAG_LOGIN_NEXT_STAGE3 |
+ ISCSI_FLAG_LOGIN_TRANSIT;
+
+ return 0;
+}
+
+static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *login)
+{
+ int pdu_count = 0;
+ struct iscsi_login_req *login_req;
+ struct iscsi_login_rsp *login_rsp;
+
+ login_req = (struct iscsi_login_req *) login->req;
+ login_rsp = (struct iscsi_login_rsp *) login->rsp;
+
+ while (1) {
+ if (++pdu_count > MAX_LOGIN_PDUS) {
+ pr_err("MAX_LOGIN_PDUS count reached.\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_TARGET_ERROR);
+ return -1;
+ }
+
+ switch (ISCSI_LOGIN_CURRENT_STAGE(login_req->flags)) {
+ case 0:
+ login_rsp->flags &= ~ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK;
+ if (iscsi_target_handle_csg_zero(conn, login) < 0)
+ return -1;
+ break;
+ case 1:
+ login_rsp->flags |= ISCSI_FLAG_LOGIN_CURRENT_STAGE1;
+ if (iscsi_target_handle_csg_one(conn, login) < 0)
+ return -1;
+ if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
+ login->tsih = conn->sess->tsih;
+ login->login_complete = 1;
+ iscsi_target_restore_sock_callbacks(conn);
+ if (iscsi_target_do_tx_login_io(conn,
+ login) < 0)
+ return -1;
+ return 1;
+ }
+ break;
+ default:
+ pr_err("Illegal CSG: %d received from"
+ " Initiator, protocol error.\n",
+ ISCSI_LOGIN_CURRENT_STAGE(login_req->flags));
+ break;
+ }
+
+ if (iscsi_target_do_tx_login_io(conn, login) < 0)
+ return -1;
+
+ if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
+ login_rsp->flags &= ~ISCSI_FLAG_LOGIN_TRANSIT;
+ login_rsp->flags &= ~ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK;
+ }
+ break;
+ }
+
+ if (conn->sock) {
+ struct sock *sk = conn->sock->sk;
+ bool state;
+
+ read_lock_bh(&sk->sk_callback_lock);
+ state = iscsi_target_sk_state_check(sk);
+ read_unlock_bh(&sk->sk_callback_lock);
+
+ if (!state) {
+ pr_debug("iscsi_target_do_login() failed state for"
+ " conn: %p\n", conn);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void iscsi_initiatorname_tolower(
+ char *param_buf)
+{
+ char *c;
+ u32 iqn_size = strlen(param_buf), i;
+
+ for (i = 0; i < iqn_size; i++) {
+ c = &param_buf[i];
+ if (!isupper(*c))
+ continue;
+
+ *c = tolower(*c);
+ }
+}
+
+/*
+ * Processes the first Login Request..
+ */
+int iscsi_target_locate_portal(
+ struct iscsi_np *np,
+ struct iscsi_conn *conn,
+ struct iscsi_login *login)
+{
+ char *i_buf = NULL, *s_buf = NULL, *t_buf = NULL;
+ char *tmpbuf, *start = NULL, *end = NULL, *key, *value;
+ struct iscsi_session *sess = conn->sess;
+ struct iscsi_tiqn *tiqn;
+ struct iscsi_tpg_np *tpg_np = NULL;
+ struct iscsi_login_req *login_req;
+ struct se_node_acl *se_nacl;
+ u32 payload_length, queue_depth = 0;
+ int sessiontype = 0, ret = 0, tag_num, tag_size;
+
+ INIT_DELAYED_WORK(&conn->login_work, iscsi_target_do_login_rx);
+ INIT_DELAYED_WORK(&conn->login_cleanup_work, iscsi_target_do_cleanup);
+ iscsi_target_set_sock_callbacks(conn);
+
+ login->np = np;
+
+ login_req = (struct iscsi_login_req *) login->req;
+ payload_length = ntoh24(login_req->dlength);
+
+ tmpbuf = kzalloc(payload_length + 1, GFP_KERNEL);
+ if (!tmpbuf) {
+ pr_err("Unable to allocate memory for tmpbuf.\n");
+ return -1;
+ }
+
+ memcpy(tmpbuf, login->req_buf, payload_length);
+ tmpbuf[payload_length] = '\0';
+ start = tmpbuf;
+ end = (start + payload_length);
+
+ /*
+ * Locate the initial keys expected from the Initiator node in
+ * the first login request in order to progress with the login phase.
+ */
+ while (start < end) {
+ if (iscsi_extract_key_value(start, &key, &value) < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ if (!strncmp(key, "InitiatorName", 13))
+ i_buf = value;
+ else if (!strncmp(key, "SessionType", 11))
+ s_buf = value;
+ else if (!strncmp(key, "TargetName", 10))
+ t_buf = value;
+
+ start += strlen(key) + strlen(value) + 2;
+ }
+ /*
+ * See 5.3. Login Phase.
+ */
+ if (!i_buf) {
+ pr_err("InitiatorName key not received"
+ " in first login request.\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_MISSING_FIELDS);
+ ret = -1;
+ goto out;
+ }
+ /*
+ * Convert the incoming InitiatorName to lowercase following
+ * RFC-3720 3.2.6.1. section c) that says that iSCSI IQNs
+ * are NOT case sensitive.
+ */
+ iscsi_initiatorname_tolower(i_buf);
+
+ if (!s_buf) {
+ if (!login->leading_connection)
+ goto get_target;
+
+ pr_err("SessionType key not received"
+ " in first login request.\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_MISSING_FIELDS);
+ ret = -1;
+ goto out;
+ }
+
+ /*
+ * Use default portal group for discovery sessions.
+ */
+ sessiontype = strncmp(s_buf, DISCOVERY, 9);
+ if (!sessiontype) {
+ conn->tpg = iscsit_global->discovery_tpg;
+ if (!login->leading_connection)
+ goto get_target;
+
+ sess->sess_ops->SessionType = 1;
+ /*
+ * Setup crc32c modules from libcrypto
+ */
+ if (iscsi_login_setup_crypto(conn) < 0) {
+ pr_err("iscsi_login_setup_crypto() failed\n");
+ ret = -1;
+ goto out;
+ }
+ /*
+ * Serialize access across the discovery struct iscsi_portal_group to
+ * process login attempt.
+ */
+ if (iscsit_access_np(np, conn->tpg) < 0) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
+ ret = -1;
+ goto out;
+ }
+ ret = 0;
+ goto alloc_tags;
+ }
+
+get_target:
+ if (!t_buf) {
+ pr_err("TargetName key not received"
+ " in first login request while"
+ " SessionType=Normal.\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_MISSING_FIELDS);
+ ret = -1;
+ goto out;
+ }
+
+ /*
+ * Locate Target IQN from Storage Node.
+ */
+ tiqn = iscsit_get_tiqn_for_login(t_buf);
+ if (!tiqn) {
+ pr_err("Unable to locate Target IQN: %s in"
+ " Storage Node\n", t_buf);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
+ ret = -1;
+ goto out;
+ }
+ pr_debug("Located Storage Object: %s\n", tiqn->tiqn);
+
+ /*
+ * Locate Target Portal Group from Storage Node.
+ */
+ conn->tpg = iscsit_get_tpg_from_np(tiqn, np, &tpg_np);
+ if (!conn->tpg) {
+ pr_err("Unable to locate Target Portal Group"
+ " on %s\n", tiqn->tiqn);
+ iscsit_put_tiqn_for_login(tiqn);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
+ ret = -1;
+ goto out;
+ }
+ conn->tpg_np = tpg_np;
+ pr_debug("Located Portal Group Object: %hu\n", conn->tpg->tpgt);
+ /*
+ * Setup crc32c modules from libcrypto
+ */
+ if (iscsi_login_setup_crypto(conn) < 0) {
+ pr_err("iscsi_login_setup_crypto() failed\n");
+ kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
+ iscsit_put_tiqn_for_login(tiqn);
+ conn->tpg = NULL;
+ ret = -1;
+ goto out;
+ }
+ /*
+ * Serialize access across the struct iscsi_portal_group to
+ * process login attempt.
+ */
+ if (iscsit_access_np(np, conn->tpg) < 0) {
+ kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
+ iscsit_put_tiqn_for_login(tiqn);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
+ conn->tpg = NULL;
+ ret = -1;
+ goto out;
+ }
+
+ /*
+ * conn->sess->node_acl will be set when the referenced
+ * struct iscsi_session is located from received ISID+TSIH in
+ * iscsi_login_non_zero_tsih_s2().
+ */
+ if (!login->leading_connection) {
+ ret = 0;
+ goto out;
+ }
+
+ /*
+ * This value is required in iscsi_login_zero_tsih_s2()
+ */
+ sess->sess_ops->SessionType = 0;
+
+ /*
+ * Locate incoming Initiator IQN reference from Storage Node.
+ */
+ sess->se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
+ &conn->tpg->tpg_se_tpg, i_buf);
+ if (!sess->se_sess->se_node_acl) {
+ pr_err("iSCSI Initiator Node: %s is not authorized to"
+ " access iSCSI target portal group: %hu.\n",
+ i_buf, conn->tpg->tpgt);
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_TGT_FORBIDDEN);
+ ret = -1;
+ goto out;
+ }
+ se_nacl = sess->se_sess->se_node_acl;
+ queue_depth = se_nacl->queue_depth;
+ /*
+ * Setup pre-allocated tags based upon allowed per NodeACL CmdSN
+ * depth for non immediate commands, plus extra tags for immediate
+ * commands.
+ *
+ * Also enforce a ISCSIT_MIN_TAGS to prevent unnecessary contention
+ * in per-cpu-ida tag allocation logic + small queue_depth.
+ */
+alloc_tags:
+ tag_num = max_t(u32, ISCSIT_MIN_TAGS, queue_depth);
+ tag_num = (tag_num * 2) + ISCSIT_EXTRA_TAGS;
+ tag_size = sizeof(struct iscsi_cmd) + conn->conn_transport->priv_size;
+
+ ret = transport_alloc_session_tags(sess->se_sess, tag_num, tag_size);
+ if (ret < 0) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ ret = -1;
+ }
+out:
+ kfree(tmpbuf);
+ return ret;
+}
+
+int iscsi_target_start_negotiation(
+ struct iscsi_login *login,
+ struct iscsi_conn *conn)
+{
+ int ret;
+
+ ret = iscsi_target_do_login(conn, login);
+ if (!ret) {
+ if (conn->sock) {
+ struct sock *sk = conn->sock->sk;
+
+ write_lock_bh(&sk->sk_callback_lock);
+ set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
+ write_unlock_bh(&sk->sk_callback_lock);
+ }
+ } else if (ret < 0) {
+ cancel_delayed_work_sync(&conn->login_work);
+ cancel_delayed_work_sync(&conn->login_cleanup_work);
+ iscsi_target_restore_sock_callbacks(conn);
+ iscsi_remove_failed_auth_entry(conn);
+ }
+ if (ret != 0)
+ iscsi_target_nego_release(conn);
+
+ return ret;
+}
+
+void iscsi_target_nego_release(struct iscsi_conn *conn)
+{
+ struct iscsi_login *login = conn->conn_login;
+
+ if (!login)
+ return;
+
+ kfree(login->req_buf);
+ kfree(login->rsp_buf);
+ kfree(login);
+
+ conn->conn_login = NULL;
+}
diff --git a/drivers/target/iscsi/iscsi_target_nego.h b/drivers/target/iscsi/iscsi_target_nego.h
new file mode 100644
index 00000000000..f021cbd330e
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_nego.h
@@ -0,0 +1,20 @@
+#ifndef ISCSI_TARGET_NEGO_H
+#define ISCSI_TARGET_NEGO_H
+
+#define DECIMAL 0
+#define HEX 1
+
+extern void convert_null_to_semi(char *, int);
+extern int extract_param(const char *, const char *, unsigned int, char *,
+ unsigned char *);
+extern int iscsi_target_check_login_request(struct iscsi_conn *,
+ struct iscsi_login *);
+extern int iscsi_target_get_initial_payload(struct iscsi_conn *,
+ struct iscsi_login *);
+extern int iscsi_target_locate_portal(struct iscsi_np *, struct iscsi_conn *,
+ struct iscsi_login *);
+extern int iscsi_target_start_negotiation(
+ struct iscsi_login *, struct iscsi_conn *);
+extern void iscsi_target_nego_release(struct iscsi_conn *);
+
+#endif /* ISCSI_TARGET_NEGO_H */
diff --git a/drivers/target/iscsi/iscsi_target_nodeattrib.c b/drivers/target/iscsi/iscsi_target_nodeattrib.c
new file mode 100644
index 00000000000..16454a922e2
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_nodeattrib.c
@@ -0,0 +1,261 @@
+/*******************************************************************************
+ * This file contains the main functions related to Initiator Node Attributes.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <target/target_core_base.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target_nodeattrib.h"
+
+static inline char *iscsit_na_get_initiatorname(
+ struct iscsi_node_acl *nacl)
+{
+ struct se_node_acl *se_nacl = &nacl->se_node_acl;
+
+ return &se_nacl->initiatorname[0];
+}
+
+void iscsit_set_default_node_attribues(
+ struct iscsi_node_acl *acl,
+ struct iscsi_portal_group *tpg)
+{
+ struct iscsi_node_attrib *a = &acl->node_attrib;
+
+ a->dataout_timeout = NA_DATAOUT_TIMEOUT;
+ a->dataout_timeout_retries = NA_DATAOUT_TIMEOUT_RETRIES;
+ a->nopin_timeout = NA_NOPIN_TIMEOUT;
+ a->nopin_response_timeout = NA_NOPIN_RESPONSE_TIMEOUT;
+ a->random_datain_pdu_offsets = NA_RANDOM_DATAIN_PDU_OFFSETS;
+ a->random_datain_seq_offsets = NA_RANDOM_DATAIN_SEQ_OFFSETS;
+ a->random_r2t_offsets = NA_RANDOM_R2T_OFFSETS;
+ a->default_erl = tpg->tpg_attrib.default_erl;
+}
+
+int iscsit_na_dataout_timeout(
+ struct iscsi_node_acl *acl,
+ u32 dataout_timeout)
+{
+ struct iscsi_node_attrib *a = &acl->node_attrib;
+
+ if (dataout_timeout > NA_DATAOUT_TIMEOUT_MAX) {
+ pr_err("Requested DataOut Timeout %u larger than"
+ " maximum %u\n", dataout_timeout,
+ NA_DATAOUT_TIMEOUT_MAX);
+ return -EINVAL;
+ } else if (dataout_timeout < NA_DATAOUT_TIMEOUT_MIX) {
+ pr_err("Requested DataOut Timeout %u smaller than"
+ " minimum %u\n", dataout_timeout,
+ NA_DATAOUT_TIMEOUT_MIX);
+ return -EINVAL;
+ }
+
+ a->dataout_timeout = dataout_timeout;
+ pr_debug("Set DataOut Timeout to %u for Initiator Node"
+ " %s\n", a->dataout_timeout, iscsit_na_get_initiatorname(acl));
+
+ return 0;
+}
+
+int iscsit_na_dataout_timeout_retries(
+ struct iscsi_node_acl *acl,
+ u32 dataout_timeout_retries)
+{
+ struct iscsi_node_attrib *a = &acl->node_attrib;
+
+ if (dataout_timeout_retries > NA_DATAOUT_TIMEOUT_RETRIES_MAX) {
+ pr_err("Requested DataOut Timeout Retries %u larger"
+ " than maximum %u", dataout_timeout_retries,
+ NA_DATAOUT_TIMEOUT_RETRIES_MAX);
+ return -EINVAL;
+ } else if (dataout_timeout_retries < NA_DATAOUT_TIMEOUT_RETRIES_MIN) {
+ pr_err("Requested DataOut Timeout Retries %u smaller"
+ " than minimum %u", dataout_timeout_retries,
+ NA_DATAOUT_TIMEOUT_RETRIES_MIN);
+ return -EINVAL;
+ }
+
+ a->dataout_timeout_retries = dataout_timeout_retries;
+ pr_debug("Set DataOut Timeout Retries to %u for"
+ " Initiator Node %s\n", a->dataout_timeout_retries,
+ iscsit_na_get_initiatorname(acl));
+
+ return 0;
+}
+
+int iscsit_na_nopin_timeout(
+ struct iscsi_node_acl *acl,
+ u32 nopin_timeout)
+{
+ struct iscsi_node_attrib *a = &acl->node_attrib;
+ struct iscsi_session *sess;
+ struct iscsi_conn *conn;
+ struct se_node_acl *se_nacl = &a->nacl->se_node_acl;
+ struct se_session *se_sess;
+ u32 orig_nopin_timeout = a->nopin_timeout;
+
+ if (nopin_timeout > NA_NOPIN_TIMEOUT_MAX) {
+ pr_err("Requested NopIn Timeout %u larger than maximum"
+ " %u\n", nopin_timeout, NA_NOPIN_TIMEOUT_MAX);
+ return -EINVAL;
+ } else if ((nopin_timeout < NA_NOPIN_TIMEOUT_MIN) &&
+ (nopin_timeout != 0)) {
+ pr_err("Requested NopIn Timeout %u smaller than"
+ " minimum %u and not 0\n", nopin_timeout,
+ NA_NOPIN_TIMEOUT_MIN);
+ return -EINVAL;
+ }
+
+ a->nopin_timeout = nopin_timeout;
+ pr_debug("Set NopIn Timeout to %u for Initiator"
+ " Node %s\n", a->nopin_timeout,
+ iscsit_na_get_initiatorname(acl));
+ /*
+ * Reenable disabled nopin_timeout timer for all iSCSI connections.
+ */
+ if (!orig_nopin_timeout) {
+ spin_lock_bh(&se_nacl->nacl_sess_lock);
+ se_sess = se_nacl->nacl_sess;
+ if (se_sess) {
+ sess = se_sess->fabric_sess_ptr;
+
+ spin_lock(&sess->conn_lock);
+ list_for_each_entry(conn, &sess->sess_conn_list,
+ conn_list) {
+ if (conn->conn_state !=
+ TARG_CONN_STATE_LOGGED_IN)
+ continue;
+
+ spin_lock(&conn->nopin_timer_lock);
+ __iscsit_start_nopin_timer(conn);
+ spin_unlock(&conn->nopin_timer_lock);
+ }
+ spin_unlock(&sess->conn_lock);
+ }
+ spin_unlock_bh(&se_nacl->nacl_sess_lock);
+ }
+
+ return 0;
+}
+
+int iscsit_na_nopin_response_timeout(
+ struct iscsi_node_acl *acl,
+ u32 nopin_response_timeout)
+{
+ struct iscsi_node_attrib *a = &acl->node_attrib;
+
+ if (nopin_response_timeout > NA_NOPIN_RESPONSE_TIMEOUT_MAX) {
+ pr_err("Requested NopIn Response Timeout %u larger"
+ " than maximum %u\n", nopin_response_timeout,
+ NA_NOPIN_RESPONSE_TIMEOUT_MAX);
+ return -EINVAL;
+ } else if (nopin_response_timeout < NA_NOPIN_RESPONSE_TIMEOUT_MIN) {
+ pr_err("Requested NopIn Response Timeout %u smaller"
+ " than minimum %u\n", nopin_response_timeout,
+ NA_NOPIN_RESPONSE_TIMEOUT_MIN);
+ return -EINVAL;
+ }
+
+ a->nopin_response_timeout = nopin_response_timeout;
+ pr_debug("Set NopIn Response Timeout to %u for"
+ " Initiator Node %s\n", a->nopin_timeout,
+ iscsit_na_get_initiatorname(acl));
+
+ return 0;
+}
+
+int iscsit_na_random_datain_pdu_offsets(
+ struct iscsi_node_acl *acl,
+ u32 random_datain_pdu_offsets)
+{
+ struct iscsi_node_attrib *a = &acl->node_attrib;
+
+ if (random_datain_pdu_offsets != 0 && random_datain_pdu_offsets != 1) {
+ pr_err("Requested Random DataIN PDU Offsets: %u not"
+ " 0 or 1\n", random_datain_pdu_offsets);
+ return -EINVAL;
+ }
+
+ a->random_datain_pdu_offsets = random_datain_pdu_offsets;
+ pr_debug("Set Random DataIN PDU Offsets to %u for"
+ " Initiator Node %s\n", a->random_datain_pdu_offsets,
+ iscsit_na_get_initiatorname(acl));
+
+ return 0;
+}
+
+int iscsit_na_random_datain_seq_offsets(
+ struct iscsi_node_acl *acl,
+ u32 random_datain_seq_offsets)
+{
+ struct iscsi_node_attrib *a = &acl->node_attrib;
+
+ if (random_datain_seq_offsets != 0 && random_datain_seq_offsets != 1) {
+ pr_err("Requested Random DataIN Sequence Offsets: %u"
+ " not 0 or 1\n", random_datain_seq_offsets);
+ return -EINVAL;
+ }
+
+ a->random_datain_seq_offsets = random_datain_seq_offsets;
+ pr_debug("Set Random DataIN Sequence Offsets to %u for"
+ " Initiator Node %s\n", a->random_datain_seq_offsets,
+ iscsit_na_get_initiatorname(acl));
+
+ return 0;
+}
+
+int iscsit_na_random_r2t_offsets(
+ struct iscsi_node_acl *acl,
+ u32 random_r2t_offsets)
+{
+ struct iscsi_node_attrib *a = &acl->node_attrib;
+
+ if (random_r2t_offsets != 0 && random_r2t_offsets != 1) {
+ pr_err("Requested Random R2T Offsets: %u not"
+ " 0 or 1\n", random_r2t_offsets);
+ return -EINVAL;
+ }
+
+ a->random_r2t_offsets = random_r2t_offsets;
+ pr_debug("Set Random R2T Offsets to %u for"
+ " Initiator Node %s\n", a->random_r2t_offsets,
+ iscsit_na_get_initiatorname(acl));
+
+ return 0;
+}
+
+int iscsit_na_default_erl(
+ struct iscsi_node_acl *acl,
+ u32 default_erl)
+{
+ struct iscsi_node_attrib *a = &acl->node_attrib;
+
+ if (default_erl != 0 && default_erl != 1 && default_erl != 2) {
+ pr_err("Requested default ERL: %u not 0, 1, or 2\n",
+ default_erl);
+ return -EINVAL;
+ }
+
+ a->default_erl = default_erl;
+ pr_debug("Set use ERL0 flag to %u for Initiator"
+ " Node %s\n", a->default_erl,
+ iscsit_na_get_initiatorname(acl));
+
+ return 0;
+}
diff --git a/drivers/target/iscsi/iscsi_target_nodeattrib.h b/drivers/target/iscsi/iscsi_target_nodeattrib.h
new file mode 100644
index 00000000000..0c69a46a62e
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_nodeattrib.h
@@ -0,0 +1,15 @@
+#ifndef ISCSI_TARGET_NODEATTRIB_H
+#define ISCSI_TARGET_NODEATTRIB_H
+
+extern void iscsit_set_default_node_attribues(struct iscsi_node_acl *,
+ struct iscsi_portal_group *);
+extern int iscsit_na_dataout_timeout(struct iscsi_node_acl *, u32);
+extern int iscsit_na_dataout_timeout_retries(struct iscsi_node_acl *, u32);
+extern int iscsit_na_nopin_timeout(struct iscsi_node_acl *, u32);
+extern int iscsit_na_nopin_response_timeout(struct iscsi_node_acl *, u32);
+extern int iscsit_na_random_datain_pdu_offsets(struct iscsi_node_acl *, u32);
+extern int iscsit_na_random_datain_seq_offsets(struct iscsi_node_acl *, u32);
+extern int iscsit_na_random_r2t_offsets(struct iscsi_node_acl *, u32);
+extern int iscsit_na_default_erl(struct iscsi_node_acl *, u32);
+
+#endif /* ISCSI_TARGET_NODEATTRIB_H */
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c
new file mode 100644
index 00000000000..02f9de26f38
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_parameters.c
@@ -0,0 +1,1982 @@
+/*******************************************************************************
+ * This file contains main functions related to iSCSI Parameter negotiation.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/slab.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target_parameters.h"
+
+int iscsi_login_rx_data(
+ struct iscsi_conn *conn,
+ char *buf,
+ int length)
+{
+ int rx_got;
+ struct kvec iov;
+
+ memset(&iov, 0, sizeof(struct kvec));
+ iov.iov_len = length;
+ iov.iov_base = buf;
+
+ /*
+ * Initial Marker-less Interval.
+ * Add the values regardless of IFMarker/OFMarker, considering
+ * it may not be negoitated yet.
+ */
+ conn->of_marker += length;
+
+ rx_got = rx_data(conn, &iov, 1, length);
+ if (rx_got != length) {
+ pr_err("rx_data returned %d, expecting %d.\n",
+ rx_got, length);
+ return -1;
+ }
+
+ return 0 ;
+}
+
+int iscsi_login_tx_data(
+ struct iscsi_conn *conn,
+ char *pdu_buf,
+ char *text_buf,
+ int text_length)
+{
+ int length, tx_sent, iov_cnt = 1;
+ struct kvec iov[2];
+
+ length = (ISCSI_HDR_LEN + text_length);
+
+ memset(&iov[0], 0, 2 * sizeof(struct kvec));
+ iov[0].iov_len = ISCSI_HDR_LEN;
+ iov[0].iov_base = pdu_buf;
+
+ if (text_buf && text_length) {
+ iov[1].iov_len = text_length;
+ iov[1].iov_base = text_buf;
+ iov_cnt++;
+ }
+
+ /*
+ * Initial Marker-less Interval.
+ * Add the values regardless of IFMarker/OFMarker, considering
+ * it may not be negoitated yet.
+ */
+ conn->if_marker += length;
+
+ tx_sent = tx_data(conn, &iov[0], iov_cnt, length);
+ if (tx_sent != length) {
+ pr_err("tx_data returned %d, expecting %d.\n",
+ tx_sent, length);
+ return -1;
+ }
+
+ return 0;
+}
+
+void iscsi_dump_conn_ops(struct iscsi_conn_ops *conn_ops)
+{
+ pr_debug("HeaderDigest: %s\n", (conn_ops->HeaderDigest) ?
+ "CRC32C" : "None");
+ pr_debug("DataDigest: %s\n", (conn_ops->DataDigest) ?
+ "CRC32C" : "None");
+ pr_debug("MaxRecvDataSegmentLength: %u\n",
+ conn_ops->MaxRecvDataSegmentLength);
+ pr_debug("OFMarker: %s\n", (conn_ops->OFMarker) ? "Yes" : "No");
+ pr_debug("IFMarker: %s\n", (conn_ops->IFMarker) ? "Yes" : "No");
+ if (conn_ops->OFMarker)
+ pr_debug("OFMarkInt: %u\n", conn_ops->OFMarkInt);
+ if (conn_ops->IFMarker)
+ pr_debug("IFMarkInt: %u\n", conn_ops->IFMarkInt);
+}
+
+void iscsi_dump_sess_ops(struct iscsi_sess_ops *sess_ops)
+{
+ pr_debug("InitiatorName: %s\n", sess_ops->InitiatorName);
+ pr_debug("InitiatorAlias: %s\n", sess_ops->InitiatorAlias);
+ pr_debug("TargetName: %s\n", sess_ops->TargetName);
+ pr_debug("TargetAlias: %s\n", sess_ops->TargetAlias);
+ pr_debug("TargetPortalGroupTag: %hu\n",
+ sess_ops->TargetPortalGroupTag);
+ pr_debug("MaxConnections: %hu\n", sess_ops->MaxConnections);
+ pr_debug("InitialR2T: %s\n",
+ (sess_ops->InitialR2T) ? "Yes" : "No");
+ pr_debug("ImmediateData: %s\n", (sess_ops->ImmediateData) ?
+ "Yes" : "No");
+ pr_debug("MaxBurstLength: %u\n", sess_ops->MaxBurstLength);
+ pr_debug("FirstBurstLength: %u\n", sess_ops->FirstBurstLength);
+ pr_debug("DefaultTime2Wait: %hu\n", sess_ops->DefaultTime2Wait);
+ pr_debug("DefaultTime2Retain: %hu\n",
+ sess_ops->DefaultTime2Retain);
+ pr_debug("MaxOutstandingR2T: %hu\n",
+ sess_ops->MaxOutstandingR2T);
+ pr_debug("DataPDUInOrder: %s\n",
+ (sess_ops->DataPDUInOrder) ? "Yes" : "No");
+ pr_debug("DataSequenceInOrder: %s\n",
+ (sess_ops->DataSequenceInOrder) ? "Yes" : "No");
+ pr_debug("ErrorRecoveryLevel: %hu\n",
+ sess_ops->ErrorRecoveryLevel);
+ pr_debug("SessionType: %s\n", (sess_ops->SessionType) ?
+ "Discovery" : "Normal");
+}
+
+void iscsi_print_params(struct iscsi_param_list *param_list)
+{
+ struct iscsi_param *param;
+
+ list_for_each_entry(param, &param_list->param_list, p_list)
+ pr_debug("%s: %s\n", param->name, param->value);
+}
+
+static struct iscsi_param *iscsi_set_default_param(struct iscsi_param_list *param_list,
+ char *name, char *value, u8 phase, u8 scope, u8 sender,
+ u16 type_range, u8 use)
+{
+ struct iscsi_param *param = NULL;
+
+ param = kzalloc(sizeof(struct iscsi_param), GFP_KERNEL);
+ if (!param) {
+ pr_err("Unable to allocate memory for parameter.\n");
+ goto out;
+ }
+ INIT_LIST_HEAD(&param->p_list);
+
+ param->name = kstrdup(name, GFP_KERNEL);
+ if (!param->name) {
+ pr_err("Unable to allocate memory for parameter name.\n");
+ goto out;
+ }
+
+ param->value = kstrdup(value, GFP_KERNEL);
+ if (!param->value) {
+ pr_err("Unable to allocate memory for parameter value.\n");
+ goto out;
+ }
+
+ param->phase = phase;
+ param->scope = scope;
+ param->sender = sender;
+ param->use = use;
+ param->type_range = type_range;
+
+ switch (param->type_range) {
+ case TYPERANGE_BOOL_AND:
+ param->type = TYPE_BOOL_AND;
+ break;
+ case TYPERANGE_BOOL_OR:
+ param->type = TYPE_BOOL_OR;
+ break;
+ case TYPERANGE_0_TO_2:
+ case TYPERANGE_0_TO_3600:
+ case TYPERANGE_0_TO_32767:
+ case TYPERANGE_0_TO_65535:
+ case TYPERANGE_1_TO_65535:
+ case TYPERANGE_2_TO_3600:
+ case TYPERANGE_512_TO_16777215:
+ param->type = TYPE_NUMBER;
+ break;
+ case TYPERANGE_AUTH:
+ case TYPERANGE_DIGEST:
+ param->type = TYPE_VALUE_LIST | TYPE_STRING;
+ break;
+ case TYPERANGE_MARKINT:
+ param->type = TYPE_NUMBER_RANGE;
+ param->type_range |= TYPERANGE_1_TO_65535;
+ break;
+ case TYPERANGE_ISCSINAME:
+ case TYPERANGE_SESSIONTYPE:
+ case TYPERANGE_TARGETADDRESS:
+ case TYPERANGE_UTF8:
+ param->type = TYPE_STRING;
+ break;
+ default:
+ pr_err("Unknown type_range 0x%02x\n",
+ param->type_range);
+ goto out;
+ }
+ list_add_tail(&param->p_list, &param_list->param_list);
+
+ return param;
+out:
+ if (param) {
+ kfree(param->value);
+ kfree(param->name);
+ kfree(param);
+ }
+
+ return NULL;
+}
+
+/* #warning Add extension keys */
+int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr)
+{
+ struct iscsi_param *param = NULL;
+ struct iscsi_param_list *pl;
+
+ pl = kzalloc(sizeof(struct iscsi_param_list), GFP_KERNEL);
+ if (!pl) {
+ pr_err("Unable to allocate memory for"
+ " struct iscsi_param_list.\n");
+ return -1 ;
+ }
+ INIT_LIST_HEAD(&pl->param_list);
+ INIT_LIST_HEAD(&pl->extra_response_list);
+
+ /*
+ * The format for setting the initial parameter definitions are:
+ *
+ * Parameter name:
+ * Initial value:
+ * Allowable phase:
+ * Scope:
+ * Allowable senders:
+ * Typerange:
+ * Use:
+ */
+ param = iscsi_set_default_param(pl, AUTHMETHOD, INITIAL_AUTHMETHOD,
+ PHASE_SECURITY, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+ TYPERANGE_AUTH, USE_INITIAL_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, HEADERDIGEST, INITIAL_HEADERDIGEST,
+ PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+ TYPERANGE_DIGEST, USE_INITIAL_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, DATADIGEST, INITIAL_DATADIGEST,
+ PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+ TYPERANGE_DIGEST, USE_INITIAL_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, MAXCONNECTIONS,
+ INITIAL_MAXCONNECTIONS, PHASE_OPERATIONAL,
+ SCOPE_SESSION_WIDE, SENDER_BOTH,
+ TYPERANGE_1_TO_65535, USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, SENDTARGETS, INITIAL_SENDTARGETS,
+ PHASE_FFP0, SCOPE_SESSION_WIDE, SENDER_INITIATOR,
+ TYPERANGE_UTF8, 0);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, TARGETNAME, INITIAL_TARGETNAME,
+ PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_BOTH,
+ TYPERANGE_ISCSINAME, USE_ALL);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, INITIATORNAME,
+ INITIAL_INITIATORNAME, PHASE_DECLARATIVE,
+ SCOPE_SESSION_WIDE, SENDER_INITIATOR,
+ TYPERANGE_ISCSINAME, USE_INITIAL_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, TARGETALIAS, INITIAL_TARGETALIAS,
+ PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_TARGET,
+ TYPERANGE_UTF8, USE_ALL);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, INITIATORALIAS,
+ INITIAL_INITIATORALIAS, PHASE_DECLARATIVE,
+ SCOPE_SESSION_WIDE, SENDER_INITIATOR, TYPERANGE_UTF8,
+ USE_ALL);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, TARGETADDRESS,
+ INITIAL_TARGETADDRESS, PHASE_DECLARATIVE,
+ SCOPE_SESSION_WIDE, SENDER_TARGET,
+ TYPERANGE_TARGETADDRESS, USE_ALL);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, TARGETPORTALGROUPTAG,
+ INITIAL_TARGETPORTALGROUPTAG,
+ PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_TARGET,
+ TYPERANGE_0_TO_65535, USE_INITIAL_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, INITIALR2T, INITIAL_INITIALR2T,
+ PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+ TYPERANGE_BOOL_OR, USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, IMMEDIATEDATA,
+ INITIAL_IMMEDIATEDATA, PHASE_OPERATIONAL,
+ SCOPE_SESSION_WIDE, SENDER_BOTH, TYPERANGE_BOOL_AND,
+ USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, MAXXMITDATASEGMENTLENGTH,
+ INITIAL_MAXXMITDATASEGMENTLENGTH,
+ PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+ TYPERANGE_512_TO_16777215, USE_ALL);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, MAXRECVDATASEGMENTLENGTH,
+ INITIAL_MAXRECVDATASEGMENTLENGTH,
+ PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+ TYPERANGE_512_TO_16777215, USE_ALL);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, MAXBURSTLENGTH,
+ INITIAL_MAXBURSTLENGTH, PHASE_OPERATIONAL,
+ SCOPE_SESSION_WIDE, SENDER_BOTH,
+ TYPERANGE_512_TO_16777215, USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, FIRSTBURSTLENGTH,
+ INITIAL_FIRSTBURSTLENGTH,
+ PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+ TYPERANGE_512_TO_16777215, USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, DEFAULTTIME2WAIT,
+ INITIAL_DEFAULTTIME2WAIT,
+ PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+ TYPERANGE_0_TO_3600, USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, DEFAULTTIME2RETAIN,
+ INITIAL_DEFAULTTIME2RETAIN,
+ PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+ TYPERANGE_0_TO_3600, USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, MAXOUTSTANDINGR2T,
+ INITIAL_MAXOUTSTANDINGR2T,
+ PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+ TYPERANGE_1_TO_65535, USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, DATAPDUINORDER,
+ INITIAL_DATAPDUINORDER, PHASE_OPERATIONAL,
+ SCOPE_SESSION_WIDE, SENDER_BOTH, TYPERANGE_BOOL_OR,
+ USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, DATASEQUENCEINORDER,
+ INITIAL_DATASEQUENCEINORDER,
+ PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+ TYPERANGE_BOOL_OR, USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, ERRORRECOVERYLEVEL,
+ INITIAL_ERRORRECOVERYLEVEL,
+ PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+ TYPERANGE_0_TO_2, USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, SESSIONTYPE, INITIAL_SESSIONTYPE,
+ PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_INITIATOR,
+ TYPERANGE_SESSIONTYPE, USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, IFMARKER, INITIAL_IFMARKER,
+ PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+ TYPERANGE_BOOL_AND, USE_INITIAL_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, OFMARKER, INITIAL_OFMARKER,
+ PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+ TYPERANGE_BOOL_AND, USE_INITIAL_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, IFMARKINT, INITIAL_IFMARKINT,
+ PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+ TYPERANGE_MARKINT, USE_INITIAL_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, OFMARKINT, INITIAL_OFMARKINT,
+ PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+ TYPERANGE_MARKINT, USE_INITIAL_ONLY);
+ if (!param)
+ goto out;
+ /*
+ * Extra parameters for ISER from RFC-5046
+ */
+ param = iscsi_set_default_param(pl, RDMAEXTENSIONS, INITIAL_RDMAEXTENSIONS,
+ PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
+ TYPERANGE_BOOL_AND, USE_LEADING_ONLY);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, INITIATORRECVDATASEGMENTLENGTH,
+ INITIAL_INITIATORRECVDATASEGMENTLENGTH,
+ PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+ TYPERANGE_512_TO_16777215, USE_ALL);
+ if (!param)
+ goto out;
+
+ param = iscsi_set_default_param(pl, TARGETRECVDATASEGMENTLENGTH,
+ INITIAL_TARGETRECVDATASEGMENTLENGTH,
+ PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
+ TYPERANGE_512_TO_16777215, USE_ALL);
+ if (!param)
+ goto out;
+
+ *param_list_ptr = pl;
+ return 0;
+out:
+ iscsi_release_param_list(pl);
+ return -1;
+}
+
+int iscsi_set_keys_to_negotiate(
+ struct iscsi_param_list *param_list,
+ bool iser)
+{
+ struct iscsi_param *param;
+
+ param_list->iser = iser;
+
+ list_for_each_entry(param, &param_list->param_list, p_list) {
+ param->state = 0;
+ if (!strcmp(param->name, AUTHMETHOD)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, HEADERDIGEST)) {
+ if (!iser)
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, DATADIGEST)) {
+ if (!iser)
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, MAXCONNECTIONS)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, TARGETNAME)) {
+ continue;
+ } else if (!strcmp(param->name, INITIATORNAME)) {
+ continue;
+ } else if (!strcmp(param->name, TARGETALIAS)) {
+ if (param->value)
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, INITIATORALIAS)) {
+ continue;
+ } else if (!strcmp(param->name, TARGETPORTALGROUPTAG)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, INITIALR2T)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, IMMEDIATEDATA)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) {
+ if (!iser)
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) {
+ continue;
+ } else if (!strcmp(param->name, MAXBURSTLENGTH)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, FIRSTBURSTLENGTH)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, DEFAULTTIME2WAIT)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, DEFAULTTIME2RETAIN)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, MAXOUTSTANDINGR2T)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, DATAPDUINORDER)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, DATASEQUENCEINORDER)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, ERRORRECOVERYLEVEL)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, SESSIONTYPE)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, IFMARKER)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, OFMARKER)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, IFMARKINT)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, OFMARKINT)) {
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, RDMAEXTENSIONS)) {
+ if (iser)
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH)) {
+ if (iser)
+ SET_PSTATE_NEGOTIATE(param);
+ } else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH)) {
+ if (iser)
+ SET_PSTATE_NEGOTIATE(param);
+ }
+ }
+
+ return 0;
+}
+
+int iscsi_set_keys_irrelevant_for_discovery(
+ struct iscsi_param_list *param_list)
+{
+ struct iscsi_param *param;
+
+ list_for_each_entry(param, &param_list->param_list, p_list) {
+ if (!strcmp(param->name, MAXCONNECTIONS))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, INITIALR2T))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, IMMEDIATEDATA))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, MAXBURSTLENGTH))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, FIRSTBURSTLENGTH))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, MAXOUTSTANDINGR2T))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, DATAPDUINORDER))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, DATASEQUENCEINORDER))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, ERRORRECOVERYLEVEL))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, DEFAULTTIME2WAIT))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, DEFAULTTIME2RETAIN))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, IFMARKER))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, OFMARKER))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, IFMARKINT))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, OFMARKINT))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, RDMAEXTENSIONS))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH))
+ param->state &= ~PSTATE_NEGOTIATE;
+ else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH))
+ param->state &= ~PSTATE_NEGOTIATE;
+ }
+
+ return 0;
+}
+
+int iscsi_copy_param_list(
+ struct iscsi_param_list **dst_param_list,
+ struct iscsi_param_list *src_param_list,
+ int leading)
+{
+ struct iscsi_param *param = NULL;
+ struct iscsi_param *new_param = NULL;
+ struct iscsi_param_list *param_list = NULL;
+
+ param_list = kzalloc(sizeof(struct iscsi_param_list), GFP_KERNEL);
+ if (!param_list) {
+ pr_err("Unable to allocate memory for struct iscsi_param_list.\n");
+ goto err_out;
+ }
+ INIT_LIST_HEAD(&param_list->param_list);
+ INIT_LIST_HEAD(&param_list->extra_response_list);
+
+ list_for_each_entry(param, &src_param_list->param_list, p_list) {
+ if (!leading && (param->scope & SCOPE_SESSION_WIDE)) {
+ if ((strcmp(param->name, "TargetName") != 0) &&
+ (strcmp(param->name, "InitiatorName") != 0) &&
+ (strcmp(param->name, "TargetPortalGroupTag") != 0))
+ continue;
+ }
+
+ new_param = kzalloc(sizeof(struct iscsi_param), GFP_KERNEL);
+ if (!new_param) {
+ pr_err("Unable to allocate memory for struct iscsi_param.\n");
+ goto err_out;
+ }
+
+ new_param->name = kstrdup(param->name, GFP_KERNEL);
+ new_param->value = kstrdup(param->value, GFP_KERNEL);
+ if (!new_param->value || !new_param->name) {
+ kfree(new_param->value);
+ kfree(new_param->name);
+ kfree(new_param);
+ pr_err("Unable to allocate memory for parameter name/value.\n");
+ goto err_out;
+ }
+
+ new_param->set_param = param->set_param;
+ new_param->phase = param->phase;
+ new_param->scope = param->scope;
+ new_param->sender = param->sender;
+ new_param->type = param->type;
+ new_param->use = param->use;
+ new_param->type_range = param->type_range;
+
+ list_add_tail(&new_param->p_list, &param_list->param_list);
+ }
+
+ if (!list_empty(&param_list->param_list)) {
+ *dst_param_list = param_list;
+ } else {
+ pr_err("No parameters allocated.\n");
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ iscsi_release_param_list(param_list);
+ return -1;
+}
+
+static void iscsi_release_extra_responses(struct iscsi_param_list *param_list)
+{
+ struct iscsi_extra_response *er, *er_tmp;
+
+ list_for_each_entry_safe(er, er_tmp, &param_list->extra_response_list,
+ er_list) {
+ list_del(&er->er_list);
+ kfree(er);
+ }
+}
+
+void iscsi_release_param_list(struct iscsi_param_list *param_list)
+{
+ struct iscsi_param *param, *param_tmp;
+
+ list_for_each_entry_safe(param, param_tmp, &param_list->param_list,
+ p_list) {
+ list_del(&param->p_list);
+
+ kfree(param->name);
+ kfree(param->value);
+ kfree(param);
+ }
+
+ iscsi_release_extra_responses(param_list);
+
+ kfree(param_list);
+}
+
+struct iscsi_param *iscsi_find_param_from_key(
+ char *key,
+ struct iscsi_param_list *param_list)
+{
+ struct iscsi_param *param;
+
+ if (!key || !param_list) {
+ pr_err("Key or parameter list pointer is NULL.\n");
+ return NULL;
+ }
+
+ list_for_each_entry(param, &param_list->param_list, p_list) {
+ if (!strcmp(key, param->name))
+ return param;
+ }
+
+ pr_err("Unable to locate key \"%s\".\n", key);
+ return NULL;
+}
+
+int iscsi_extract_key_value(char *textbuf, char **key, char **value)
+{
+ *value = strchr(textbuf, '=');
+ if (!*value) {
+ pr_err("Unable to locate \"=\" separator for key,"
+ " ignoring request.\n");
+ return -1;
+ }
+
+ *key = textbuf;
+ **value = '\0';
+ *value = *value + 1;
+
+ return 0;
+}
+
+int iscsi_update_param_value(struct iscsi_param *param, char *value)
+{
+ kfree(param->value);
+
+ param->value = kstrdup(value, GFP_KERNEL);
+ if (!param->value) {
+ pr_err("Unable to allocate memory for value.\n");
+ return -ENOMEM;
+ }
+
+ pr_debug("iSCSI Parameter updated to %s=%s\n",
+ param->name, param->value);
+ return 0;
+}
+
+static int iscsi_add_notunderstood_response(
+ char *key,
+ char *value,
+ struct iscsi_param_list *param_list)
+{
+ struct iscsi_extra_response *extra_response;
+
+ if (strlen(value) > VALUE_MAXLEN) {
+ pr_err("Value for notunderstood key \"%s\" exceeds %d,"
+ " protocol error.\n", key, VALUE_MAXLEN);
+ return -1;
+ }
+
+ extra_response = kzalloc(sizeof(struct iscsi_extra_response), GFP_KERNEL);
+ if (!extra_response) {
+ pr_err("Unable to allocate memory for"
+ " struct iscsi_extra_response.\n");
+ return -1;
+ }
+ INIT_LIST_HEAD(&extra_response->er_list);
+
+ strlcpy(extra_response->key, key, sizeof(extra_response->key));
+ strlcpy(extra_response->value, NOTUNDERSTOOD,
+ sizeof(extra_response->value));
+
+ list_add_tail(&extra_response->er_list,
+ &param_list->extra_response_list);
+ return 0;
+}
+
+static int iscsi_check_for_auth_key(char *key)
+{
+ /*
+ * RFC 1994
+ */
+ if (!strcmp(key, "CHAP_A") || !strcmp(key, "CHAP_I") ||
+ !strcmp(key, "CHAP_C") || !strcmp(key, "CHAP_N") ||
+ !strcmp(key, "CHAP_R"))
+ return 1;
+
+ /*
+ * RFC 2945
+ */
+ if (!strcmp(key, "SRP_U") || !strcmp(key, "SRP_N") ||
+ !strcmp(key, "SRP_g") || !strcmp(key, "SRP_s") ||
+ !strcmp(key, "SRP_A") || !strcmp(key, "SRP_B") ||
+ !strcmp(key, "SRP_M") || !strcmp(key, "SRP_HM"))
+ return 1;
+
+ return 0;
+}
+
+static void iscsi_check_proposer_for_optional_reply(struct iscsi_param *param)
+{
+ if (IS_TYPE_BOOL_AND(param)) {
+ if (!strcmp(param->value, NO))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ } else if (IS_TYPE_BOOL_OR(param)) {
+ if (!strcmp(param->value, YES))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ /*
+ * Required for gPXE iSCSI boot client
+ */
+ if (!strcmp(param->name, IMMEDIATEDATA))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ } else if (IS_TYPE_NUMBER(param)) {
+ if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ /*
+ * The GlobalSAN iSCSI Initiator for MacOSX does
+ * not respond to MaxBurstLength, FirstBurstLength,
+ * DefaultTime2Wait or DefaultTime2Retain parameter keys.
+ * So, we set them to 'reply optional' here, and assume the
+ * the defaults from iscsi_parameters.h if the initiator
+ * is not RFC compliant and the keys are not negotiated.
+ */
+ if (!strcmp(param->name, MAXBURSTLENGTH))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ if (!strcmp(param->name, FIRSTBURSTLENGTH))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ if (!strcmp(param->name, DEFAULTTIME2WAIT))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ if (!strcmp(param->name, DEFAULTTIME2RETAIN))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ /*
+ * Required for gPXE iSCSI boot client
+ */
+ if (!strcmp(param->name, MAXCONNECTIONS))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ } else if (IS_PHASE_DECLARATIVE(param))
+ SET_PSTATE_REPLY_OPTIONAL(param);
+}
+
+static int iscsi_check_boolean_value(struct iscsi_param *param, char *value)
+{
+ if (strcmp(value, YES) && strcmp(value, NO)) {
+ pr_err("Illegal value for \"%s\", must be either"
+ " \"%s\" or \"%s\".\n", param->name, YES, NO);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int iscsi_check_numerical_value(struct iscsi_param *param, char *value_ptr)
+{
+ char *tmpptr;
+ int value = 0;
+
+ value = simple_strtoul(value_ptr, &tmpptr, 0);
+
+ if (IS_TYPERANGE_0_TO_2(param)) {
+ if ((value < 0) || (value > 2)) {
+ pr_err("Illegal value for \"%s\", must be"
+ " between 0 and 2.\n", param->name);
+ return -1;
+ }
+ return 0;
+ }
+ if (IS_TYPERANGE_0_TO_3600(param)) {
+ if ((value < 0) || (value > 3600)) {
+ pr_err("Illegal value for \"%s\", must be"
+ " between 0 and 3600.\n", param->name);
+ return -1;
+ }
+ return 0;
+ }
+ if (IS_TYPERANGE_0_TO_32767(param)) {
+ if ((value < 0) || (value > 32767)) {
+ pr_err("Illegal value for \"%s\", must be"
+ " between 0 and 32767.\n", param->name);
+ return -1;
+ }
+ return 0;
+ }
+ if (IS_TYPERANGE_0_TO_65535(param)) {
+ if ((value < 0) || (value > 65535)) {
+ pr_err("Illegal value for \"%s\", must be"
+ " between 0 and 65535.\n", param->name);
+ return -1;
+ }
+ return 0;
+ }
+ if (IS_TYPERANGE_1_TO_65535(param)) {
+ if ((value < 1) || (value > 65535)) {
+ pr_err("Illegal value for \"%s\", must be"
+ " between 1 and 65535.\n", param->name);
+ return -1;
+ }
+ return 0;
+ }
+ if (IS_TYPERANGE_2_TO_3600(param)) {
+ if ((value < 2) || (value > 3600)) {
+ pr_err("Illegal value for \"%s\", must be"
+ " between 2 and 3600.\n", param->name);
+ return -1;
+ }
+ return 0;
+ }
+ if (IS_TYPERANGE_512_TO_16777215(param)) {
+ if ((value < 512) || (value > 16777215)) {
+ pr_err("Illegal value for \"%s\", must be"
+ " between 512 and 16777215.\n", param->name);
+ return -1;
+ }
+ return 0;
+ }
+
+ return 0;
+}
+
+static int iscsi_check_numerical_range_value(struct iscsi_param *param, char *value)
+{
+ char *left_val_ptr = NULL, *right_val_ptr = NULL;
+ char *tilde_ptr = NULL;
+ u32 left_val, right_val, local_left_val;
+
+ if (strcmp(param->name, IFMARKINT) &&
+ strcmp(param->name, OFMARKINT)) {
+ pr_err("Only parameters \"%s\" or \"%s\" may contain a"
+ " numerical range value.\n", IFMARKINT, OFMARKINT);
+ return -1;
+ }
+
+ if (IS_PSTATE_PROPOSER(param))
+ return 0;
+
+ tilde_ptr = strchr(value, '~');
+ if (!tilde_ptr) {
+ pr_err("Unable to locate numerical range indicator"
+ " \"~\" for \"%s\".\n", param->name);
+ return -1;
+ }
+ *tilde_ptr = '\0';
+
+ left_val_ptr = value;
+ right_val_ptr = value + strlen(left_val_ptr) + 1;
+
+ if (iscsi_check_numerical_value(param, left_val_ptr) < 0)
+ return -1;
+ if (iscsi_check_numerical_value(param, right_val_ptr) < 0)
+ return -1;
+
+ left_val = simple_strtoul(left_val_ptr, NULL, 0);
+ right_val = simple_strtoul(right_val_ptr, NULL, 0);
+ *tilde_ptr = '~';
+
+ if (right_val < left_val) {
+ pr_err("Numerical range for parameter \"%s\" contains"
+ " a right value which is less than the left.\n",
+ param->name);
+ return -1;
+ }
+
+ /*
+ * For now, enforce reasonable defaults for [I,O]FMarkInt.
+ */
+ tilde_ptr = strchr(param->value, '~');
+ if (!tilde_ptr) {
+ pr_err("Unable to locate numerical range indicator"
+ " \"~\" for \"%s\".\n", param->name);
+ return -1;
+ }
+ *tilde_ptr = '\0';
+
+ left_val_ptr = param->value;
+ right_val_ptr = param->value + strlen(left_val_ptr) + 1;
+
+ local_left_val = simple_strtoul(left_val_ptr, NULL, 0);
+ *tilde_ptr = '~';
+
+ if (param->set_param) {
+ if ((left_val < local_left_val) ||
+ (right_val < local_left_val)) {
+ pr_err("Passed value range \"%u~%u\" is below"
+ " minimum left value \"%u\" for key \"%s\","
+ " rejecting.\n", left_val, right_val,
+ local_left_val, param->name);
+ return -1;
+ }
+ } else {
+ if ((left_val < local_left_val) &&
+ (right_val < local_left_val)) {
+ pr_err("Received value range \"%u~%u\" is"
+ " below minimum left value \"%u\" for key"
+ " \"%s\", rejecting.\n", left_val, right_val,
+ local_left_val, param->name);
+ SET_PSTATE_REJECT(param);
+ if (iscsi_update_param_value(param, REJECT) < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int iscsi_check_string_or_list_value(struct iscsi_param *param, char *value)
+{
+ if (IS_PSTATE_PROPOSER(param))
+ return 0;
+
+ if (IS_TYPERANGE_AUTH_PARAM(param)) {
+ if (strcmp(value, KRB5) && strcmp(value, SPKM1) &&
+ strcmp(value, SPKM2) && strcmp(value, SRP) &&
+ strcmp(value, CHAP) && strcmp(value, NONE)) {
+ pr_err("Illegal value for \"%s\", must be"
+ " \"%s\", \"%s\", \"%s\", \"%s\", \"%s\""
+ " or \"%s\".\n", param->name, KRB5,
+ SPKM1, SPKM2, SRP, CHAP, NONE);
+ return -1;
+ }
+ }
+ if (IS_TYPERANGE_DIGEST_PARAM(param)) {
+ if (strcmp(value, CRC32C) && strcmp(value, NONE)) {
+ pr_err("Illegal value for \"%s\", must be"
+ " \"%s\" or \"%s\".\n", param->name,
+ CRC32C, NONE);
+ return -1;
+ }
+ }
+ if (IS_TYPERANGE_SESSIONTYPE(param)) {
+ if (strcmp(value, DISCOVERY) && strcmp(value, NORMAL)) {
+ pr_err("Illegal value for \"%s\", must be"
+ " \"%s\" or \"%s\".\n", param->name,
+ DISCOVERY, NORMAL);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This function is used to pick a value range number, currently just
+ * returns the lesser of both right values.
+ */
+static char *iscsi_get_value_from_number_range(
+ struct iscsi_param *param,
+ char *value)
+{
+ char *end_ptr, *tilde_ptr1 = NULL, *tilde_ptr2 = NULL;
+ u32 acceptor_right_value, proposer_right_value;
+
+ tilde_ptr1 = strchr(value, '~');
+ if (!tilde_ptr1)
+ return NULL;
+ *tilde_ptr1++ = '\0';
+ proposer_right_value = simple_strtoul(tilde_ptr1, &end_ptr, 0);
+
+ tilde_ptr2 = strchr(param->value, '~');
+ if (!tilde_ptr2)
+ return NULL;
+ *tilde_ptr2++ = '\0';
+ acceptor_right_value = simple_strtoul(tilde_ptr2, &end_ptr, 0);
+
+ return (acceptor_right_value >= proposer_right_value) ?
+ tilde_ptr1 : tilde_ptr2;
+}
+
+static char *iscsi_check_valuelist_for_support(
+ struct iscsi_param *param,
+ char *value)
+{
+ char *tmp1 = NULL, *tmp2 = NULL;
+ char *acceptor_values = NULL, *proposer_values = NULL;
+
+ acceptor_values = param->value;
+ proposer_values = value;
+
+ do {
+ if (!proposer_values)
+ return NULL;
+ tmp1 = strchr(proposer_values, ',');
+ if (tmp1)
+ *tmp1 = '\0';
+ acceptor_values = param->value;
+ do {
+ if (!acceptor_values) {
+ if (tmp1)
+ *tmp1 = ',';
+ return NULL;
+ }
+ tmp2 = strchr(acceptor_values, ',');
+ if (tmp2)
+ *tmp2 = '\0';
+ if (!strcmp(acceptor_values, proposer_values)) {
+ if (tmp2)
+ *tmp2 = ',';
+ goto out;
+ }
+ if (tmp2)
+ *tmp2++ = ',';
+
+ acceptor_values = tmp2;
+ } while (acceptor_values);
+ if (tmp1)
+ *tmp1++ = ',';
+ proposer_values = tmp1;
+ } while (proposer_values);
+
+out:
+ return proposer_values;
+}
+
+static int iscsi_check_acceptor_state(struct iscsi_param *param, char *value,
+ struct iscsi_conn *conn)
+{
+ u8 acceptor_boolean_value = 0, proposer_boolean_value = 0;
+ char *negoitated_value = NULL;
+
+ if (IS_PSTATE_ACCEPTOR(param)) {
+ pr_err("Received key \"%s\" twice, protocol error.\n",
+ param->name);
+ return -1;
+ }
+
+ if (IS_PSTATE_REJECT(param))
+ return 0;
+
+ if (IS_TYPE_BOOL_AND(param)) {
+ if (!strcmp(value, YES))
+ proposer_boolean_value = 1;
+ if (!strcmp(param->value, YES))
+ acceptor_boolean_value = 1;
+ if (acceptor_boolean_value && proposer_boolean_value)
+ do {} while (0);
+ else {
+ if (iscsi_update_param_value(param, NO) < 0)
+ return -1;
+ if (!proposer_boolean_value)
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ }
+ } else if (IS_TYPE_BOOL_OR(param)) {
+ if (!strcmp(value, YES))
+ proposer_boolean_value = 1;
+ if (!strcmp(param->value, YES))
+ acceptor_boolean_value = 1;
+ if (acceptor_boolean_value || proposer_boolean_value) {
+ if (iscsi_update_param_value(param, YES) < 0)
+ return -1;
+ if (proposer_boolean_value)
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ }
+ } else if (IS_TYPE_NUMBER(param)) {
+ char *tmpptr, buf[11];
+ u32 acceptor_value = simple_strtoul(param->value, &tmpptr, 0);
+ u32 proposer_value = simple_strtoul(value, &tmpptr, 0);
+
+ memset(buf, 0, sizeof(buf));
+
+ if (!strcmp(param->name, MAXCONNECTIONS) ||
+ !strcmp(param->name, MAXBURSTLENGTH) ||
+ !strcmp(param->name, FIRSTBURSTLENGTH) ||
+ !strcmp(param->name, MAXOUTSTANDINGR2T) ||
+ !strcmp(param->name, DEFAULTTIME2RETAIN) ||
+ !strcmp(param->name, ERRORRECOVERYLEVEL)) {
+ if (proposer_value > acceptor_value) {
+ sprintf(buf, "%u", acceptor_value);
+ if (iscsi_update_param_value(param,
+ &buf[0]) < 0)
+ return -1;
+ } else {
+ if (iscsi_update_param_value(param, value) < 0)
+ return -1;
+ }
+ } else if (!strcmp(param->name, DEFAULTTIME2WAIT)) {
+ if (acceptor_value > proposer_value) {
+ sprintf(buf, "%u", acceptor_value);
+ if (iscsi_update_param_value(param,
+ &buf[0]) < 0)
+ return -1;
+ } else {
+ if (iscsi_update_param_value(param, value) < 0)
+ return -1;
+ }
+ } else {
+ if (iscsi_update_param_value(param, value) < 0)
+ return -1;
+ }
+
+ if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) {
+ struct iscsi_param *param_mxdsl;
+ unsigned long long tmp;
+ int rc;
+
+ rc = kstrtoull(param->value, 0, &tmp);
+ if (rc < 0)
+ return -1;
+
+ conn->conn_ops->MaxRecvDataSegmentLength = tmp;
+ pr_debug("Saving op->MaxRecvDataSegmentLength from"
+ " original initiator received value: %u\n",
+ conn->conn_ops->MaxRecvDataSegmentLength);
+
+ param_mxdsl = iscsi_find_param_from_key(
+ MAXXMITDATASEGMENTLENGTH,
+ conn->param_list);
+ if (!param_mxdsl)
+ return -1;
+
+ rc = iscsi_update_param_value(param,
+ param_mxdsl->value);
+ if (rc < 0)
+ return -1;
+
+ pr_debug("Updated %s to target MXDSL value: %s\n",
+ param->name, param->value);
+ }
+
+ } else if (IS_TYPE_NUMBER_RANGE(param)) {
+ negoitated_value = iscsi_get_value_from_number_range(
+ param, value);
+ if (!negoitated_value)
+ return -1;
+ if (iscsi_update_param_value(param, negoitated_value) < 0)
+ return -1;
+ } else if (IS_TYPE_VALUE_LIST(param)) {
+ negoitated_value = iscsi_check_valuelist_for_support(
+ param, value);
+ if (!negoitated_value) {
+ pr_err("Proposer's value list \"%s\" contains"
+ " no valid values from Acceptor's value list"
+ " \"%s\".\n", value, param->value);
+ return -1;
+ }
+ if (iscsi_update_param_value(param, negoitated_value) < 0)
+ return -1;
+ } else if (IS_PHASE_DECLARATIVE(param)) {
+ if (iscsi_update_param_value(param, value) < 0)
+ return -1;
+ SET_PSTATE_REPLY_OPTIONAL(param);
+ }
+
+ return 0;
+}
+
+static int iscsi_check_proposer_state(struct iscsi_param *param, char *value)
+{
+ if (IS_PSTATE_RESPONSE_GOT(param)) {
+ pr_err("Received key \"%s\" twice, protocol error.\n",
+ param->name);
+ return -1;
+ }
+
+ if (IS_TYPE_NUMBER_RANGE(param)) {
+ u32 left_val = 0, right_val = 0, recieved_value = 0;
+ char *left_val_ptr = NULL, *right_val_ptr = NULL;
+ char *tilde_ptr = NULL;
+
+ if (!strcmp(value, IRRELEVANT) || !strcmp(value, REJECT)) {
+ if (iscsi_update_param_value(param, value) < 0)
+ return -1;
+ return 0;
+ }
+
+ tilde_ptr = strchr(value, '~');
+ if (tilde_ptr) {
+ pr_err("Illegal \"~\" in response for \"%s\".\n",
+ param->name);
+ return -1;
+ }
+ tilde_ptr = strchr(param->value, '~');
+ if (!tilde_ptr) {
+ pr_err("Unable to locate numerical range"
+ " indicator \"~\" for \"%s\".\n", param->name);
+ return -1;
+ }
+ *tilde_ptr = '\0';
+
+ left_val_ptr = param->value;
+ right_val_ptr = param->value + strlen(left_val_ptr) + 1;
+ left_val = simple_strtoul(left_val_ptr, NULL, 0);
+ right_val = simple_strtoul(right_val_ptr, NULL, 0);
+ recieved_value = simple_strtoul(value, NULL, 0);
+
+ *tilde_ptr = '~';
+
+ if ((recieved_value < left_val) ||
+ (recieved_value > right_val)) {
+ pr_err("Illegal response \"%s=%u\", value must"
+ " be between %u and %u.\n", param->name,
+ recieved_value, left_val, right_val);
+ return -1;
+ }
+ } else if (IS_TYPE_VALUE_LIST(param)) {
+ char *comma_ptr = NULL, *tmp_ptr = NULL;
+
+ comma_ptr = strchr(value, ',');
+ if (comma_ptr) {
+ pr_err("Illegal \",\" in response for \"%s\".\n",
+ param->name);
+ return -1;
+ }
+
+ tmp_ptr = iscsi_check_valuelist_for_support(param, value);
+ if (!tmp_ptr)
+ return -1;
+ }
+
+ if (iscsi_update_param_value(param, value) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int iscsi_check_value(struct iscsi_param *param, char *value)
+{
+ char *comma_ptr = NULL;
+
+ if (!strcmp(value, REJECT)) {
+ if (!strcmp(param->name, IFMARKINT) ||
+ !strcmp(param->name, OFMARKINT)) {
+ /*
+ * Reject is not fatal for [I,O]FMarkInt, and causes
+ * [I,O]FMarker to be reset to No. (See iSCSI v20 A.3.2)
+ */
+ SET_PSTATE_REJECT(param);
+ return 0;
+ }
+ pr_err("Received %s=%s\n", param->name, value);
+ return -1;
+ }
+ if (!strcmp(value, IRRELEVANT)) {
+ pr_debug("Received %s=%s\n", param->name, value);
+ SET_PSTATE_IRRELEVANT(param);
+ return 0;
+ }
+ if (!strcmp(value, NOTUNDERSTOOD)) {
+ if (!IS_PSTATE_PROPOSER(param)) {
+ pr_err("Received illegal offer %s=%s\n",
+ param->name, value);
+ return -1;
+ }
+
+/* #warning FIXME: Add check for X-ExtensionKey here */
+ pr_err("Standard iSCSI key \"%s\" cannot be answered"
+ " with \"%s\", protocol error.\n", param->name, value);
+ return -1;
+ }
+
+ do {
+ comma_ptr = NULL;
+ comma_ptr = strchr(value, ',');
+
+ if (comma_ptr && !IS_TYPE_VALUE_LIST(param)) {
+ pr_err("Detected value separator \",\", but"
+ " key \"%s\" does not allow a value list,"
+ " protocol error.\n", param->name);
+ return -1;
+ }
+ if (comma_ptr)
+ *comma_ptr = '\0';
+
+ if (strlen(value) > VALUE_MAXLEN) {
+ pr_err("Value for key \"%s\" exceeds %d,"
+ " protocol error.\n", param->name,
+ VALUE_MAXLEN);
+ return -1;
+ }
+
+ if (IS_TYPE_BOOL_AND(param) || IS_TYPE_BOOL_OR(param)) {
+ if (iscsi_check_boolean_value(param, value) < 0)
+ return -1;
+ } else if (IS_TYPE_NUMBER(param)) {
+ if (iscsi_check_numerical_value(param, value) < 0)
+ return -1;
+ } else if (IS_TYPE_NUMBER_RANGE(param)) {
+ if (iscsi_check_numerical_range_value(param, value) < 0)
+ return -1;
+ } else if (IS_TYPE_STRING(param) || IS_TYPE_VALUE_LIST(param)) {
+ if (iscsi_check_string_or_list_value(param, value) < 0)
+ return -1;
+ } else {
+ pr_err("Huh? 0x%02x\n", param->type);
+ return -1;
+ }
+
+ if (comma_ptr)
+ *comma_ptr++ = ',';
+
+ value = comma_ptr;
+ } while (value);
+
+ return 0;
+}
+
+static struct iscsi_param *__iscsi_check_key(
+ char *key,
+ int sender,
+ struct iscsi_param_list *param_list)
+{
+ struct iscsi_param *param;
+
+ if (strlen(key) > KEY_MAXLEN) {
+ pr_err("Length of key name \"%s\" exceeds %d.\n",
+ key, KEY_MAXLEN);
+ return NULL;
+ }
+
+ param = iscsi_find_param_from_key(key, param_list);
+ if (!param)
+ return NULL;
+
+ if ((sender & SENDER_INITIATOR) && !IS_SENDER_INITIATOR(param)) {
+ pr_err("Key \"%s\" may not be sent to %s,"
+ " protocol error.\n", param->name,
+ (sender & SENDER_RECEIVER) ? "target" : "initiator");
+ return NULL;
+ }
+
+ if ((sender & SENDER_TARGET) && !IS_SENDER_TARGET(param)) {
+ pr_err("Key \"%s\" may not be sent to %s,"
+ " protocol error.\n", param->name,
+ (sender & SENDER_RECEIVER) ? "initiator" : "target");
+ return NULL;
+ }
+
+ return param;
+}
+
+static struct iscsi_param *iscsi_check_key(
+ char *key,
+ int phase,
+ int sender,
+ struct iscsi_param_list *param_list)
+{
+ struct iscsi_param *param;
+ /*
+ * Key name length must not exceed 63 bytes. (See iSCSI v20 5.1)
+ */
+ if (strlen(key) > KEY_MAXLEN) {
+ pr_err("Length of key name \"%s\" exceeds %d.\n",
+ key, KEY_MAXLEN);
+ return NULL;
+ }
+
+ param = iscsi_find_param_from_key(key, param_list);
+ if (!param)
+ return NULL;
+
+ if ((sender & SENDER_INITIATOR) && !IS_SENDER_INITIATOR(param)) {
+ pr_err("Key \"%s\" may not be sent to %s,"
+ " protocol error.\n", param->name,
+ (sender & SENDER_RECEIVER) ? "target" : "initiator");
+ return NULL;
+ }
+ if ((sender & SENDER_TARGET) && !IS_SENDER_TARGET(param)) {
+ pr_err("Key \"%s\" may not be sent to %s,"
+ " protocol error.\n", param->name,
+ (sender & SENDER_RECEIVER) ? "initiator" : "target");
+ return NULL;
+ }
+
+ if (IS_PSTATE_ACCEPTOR(param)) {
+ pr_err("Key \"%s\" received twice, protocol error.\n",
+ key);
+ return NULL;
+ }
+
+ if (!phase)
+ return param;
+
+ if (!(param->phase & phase)) {
+ pr_err("Key \"%s\" may not be negotiated during ",
+ param->name);
+ switch (phase) {
+ case PHASE_SECURITY:
+ pr_debug("Security phase.\n");
+ break;
+ case PHASE_OPERATIONAL:
+ pr_debug("Operational phase.\n");
+ break;
+ default:
+ pr_debug("Unknown phase.\n");
+ }
+ return NULL;
+ }
+
+ return param;
+}
+
+static int iscsi_enforce_integrity_rules(
+ u8 phase,
+ struct iscsi_param_list *param_list)
+{
+ char *tmpptr;
+ u8 DataSequenceInOrder = 0;
+ u8 ErrorRecoveryLevel = 0, SessionType = 0;
+ u8 IFMarker = 0, OFMarker = 0;
+ u8 IFMarkInt_Reject = 1, OFMarkInt_Reject = 1;
+ u32 FirstBurstLength = 0, MaxBurstLength = 0;
+ struct iscsi_param *param = NULL;
+
+ list_for_each_entry(param, &param_list->param_list, p_list) {
+ if (!(param->phase & phase))
+ continue;
+ if (!strcmp(param->name, SESSIONTYPE))
+ if (!strcmp(param->value, NORMAL))
+ SessionType = 1;
+ if (!strcmp(param->name, ERRORRECOVERYLEVEL))
+ ErrorRecoveryLevel = simple_strtoul(param->value,
+ &tmpptr, 0);
+ if (!strcmp(param->name, DATASEQUENCEINORDER))
+ if (!strcmp(param->value, YES))
+ DataSequenceInOrder = 1;
+ if (!strcmp(param->name, MAXBURSTLENGTH))
+ MaxBurstLength = simple_strtoul(param->value,
+ &tmpptr, 0);
+ if (!strcmp(param->name, IFMARKER))
+ if (!strcmp(param->value, YES))
+ IFMarker = 1;
+ if (!strcmp(param->name, OFMARKER))
+ if (!strcmp(param->value, YES))
+ OFMarker = 1;
+ if (!strcmp(param->name, IFMARKINT))
+ if (!strcmp(param->value, REJECT))
+ IFMarkInt_Reject = 1;
+ if (!strcmp(param->name, OFMARKINT))
+ if (!strcmp(param->value, REJECT))
+ OFMarkInt_Reject = 1;
+ }
+
+ list_for_each_entry(param, &param_list->param_list, p_list) {
+ if (!(param->phase & phase))
+ continue;
+ if (!SessionType && (!IS_PSTATE_ACCEPTOR(param) &&
+ (strcmp(param->name, IFMARKER) &&
+ strcmp(param->name, OFMARKER) &&
+ strcmp(param->name, IFMARKINT) &&
+ strcmp(param->name, OFMARKINT))))
+ continue;
+ if (!strcmp(param->name, MAXOUTSTANDINGR2T) &&
+ DataSequenceInOrder && (ErrorRecoveryLevel > 0)) {
+ if (strcmp(param->value, "1")) {
+ if (iscsi_update_param_value(param, "1") < 0)
+ return -1;
+ pr_debug("Reset \"%s\" to \"%s\".\n",
+ param->name, param->value);
+ }
+ }
+ if (!strcmp(param->name, MAXCONNECTIONS) && !SessionType) {
+ if (strcmp(param->value, "1")) {
+ if (iscsi_update_param_value(param, "1") < 0)
+ return -1;
+ pr_debug("Reset \"%s\" to \"%s\".\n",
+ param->name, param->value);
+ }
+ }
+ if (!strcmp(param->name, FIRSTBURSTLENGTH)) {
+ FirstBurstLength = simple_strtoul(param->value,
+ &tmpptr, 0);
+ if (FirstBurstLength > MaxBurstLength) {
+ char tmpbuf[11];
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ sprintf(tmpbuf, "%u", MaxBurstLength);
+ if (iscsi_update_param_value(param, tmpbuf))
+ return -1;
+ pr_debug("Reset \"%s\" to \"%s\".\n",
+ param->name, param->value);
+ }
+ }
+ if (!strcmp(param->name, IFMARKER) && IFMarkInt_Reject) {
+ if (iscsi_update_param_value(param, NO) < 0)
+ return -1;
+ IFMarker = 0;
+ pr_debug("Reset \"%s\" to \"%s\".\n",
+ param->name, param->value);
+ }
+ if (!strcmp(param->name, OFMARKER) && OFMarkInt_Reject) {
+ if (iscsi_update_param_value(param, NO) < 0)
+ return -1;
+ OFMarker = 0;
+ pr_debug("Reset \"%s\" to \"%s\".\n",
+ param->name, param->value);
+ }
+ if (!strcmp(param->name, IFMARKINT) && !IFMarker) {
+ if (!strcmp(param->value, REJECT))
+ continue;
+ param->state &= ~PSTATE_NEGOTIATE;
+ if (iscsi_update_param_value(param, IRRELEVANT) < 0)
+ return -1;
+ pr_debug("Reset \"%s\" to \"%s\".\n",
+ param->name, param->value);
+ }
+ if (!strcmp(param->name, OFMARKINT) && !OFMarker) {
+ if (!strcmp(param->value, REJECT))
+ continue;
+ param->state &= ~PSTATE_NEGOTIATE;
+ if (iscsi_update_param_value(param, IRRELEVANT) < 0)
+ return -1;
+ pr_debug("Reset \"%s\" to \"%s\".\n",
+ param->name, param->value);
+ }
+ }
+
+ return 0;
+}
+
+int iscsi_decode_text_input(
+ u8 phase,
+ u8 sender,
+ char *textbuf,
+ u32 length,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_param_list *param_list = conn->param_list;
+ char *tmpbuf, *start = NULL, *end = NULL;
+
+ tmpbuf = kzalloc(length + 1, GFP_KERNEL);
+ if (!tmpbuf) {
+ pr_err("Unable to allocate %u + 1 bytes for tmpbuf.\n", length);
+ return -1;
+ }
+
+ memcpy(tmpbuf, textbuf, length);
+ tmpbuf[length] = '\0';
+ start = tmpbuf;
+ end = (start + length);
+
+ while (start < end) {
+ char *key, *value;
+ struct iscsi_param *param;
+
+ if (iscsi_extract_key_value(start, &key, &value) < 0) {
+ kfree(tmpbuf);
+ return -1;
+ }
+
+ pr_debug("Got key: %s=%s\n", key, value);
+
+ if (phase & PHASE_SECURITY) {
+ if (iscsi_check_for_auth_key(key) > 0) {
+ kfree(tmpbuf);
+ return 1;
+ }
+ }
+
+ param = iscsi_check_key(key, phase, sender, param_list);
+ if (!param) {
+ if (iscsi_add_notunderstood_response(key,
+ value, param_list) < 0) {
+ kfree(tmpbuf);
+ return -1;
+ }
+ start += strlen(key) + strlen(value) + 2;
+ continue;
+ }
+ if (iscsi_check_value(param, value) < 0) {
+ kfree(tmpbuf);
+ return -1;
+ }
+
+ start += strlen(key) + strlen(value) + 2;
+
+ if (IS_PSTATE_PROPOSER(param)) {
+ if (iscsi_check_proposer_state(param, value) < 0) {
+ kfree(tmpbuf);
+ return -1;
+ }
+ SET_PSTATE_RESPONSE_GOT(param);
+ } else {
+ if (iscsi_check_acceptor_state(param, value, conn) < 0) {
+ kfree(tmpbuf);
+ return -1;
+ }
+ SET_PSTATE_ACCEPTOR(param);
+ }
+ }
+
+ kfree(tmpbuf);
+ return 0;
+}
+
+int iscsi_encode_text_output(
+ u8 phase,
+ u8 sender,
+ char *textbuf,
+ u32 *length,
+ struct iscsi_param_list *param_list)
+{
+ char *output_buf = NULL;
+ struct iscsi_extra_response *er;
+ struct iscsi_param *param;
+
+ output_buf = textbuf + *length;
+
+ if (iscsi_enforce_integrity_rules(phase, param_list) < 0)
+ return -1;
+
+ list_for_each_entry(param, &param_list->param_list, p_list) {
+ if (!(param->sender & sender))
+ continue;
+ if (IS_PSTATE_ACCEPTOR(param) &&
+ !IS_PSTATE_RESPONSE_SENT(param) &&
+ !IS_PSTATE_REPLY_OPTIONAL(param) &&
+ (param->phase & phase)) {
+ *length += sprintf(output_buf, "%s=%s",
+ param->name, param->value);
+ *length += 1;
+ output_buf = textbuf + *length;
+ SET_PSTATE_RESPONSE_SENT(param);
+ pr_debug("Sending key: %s=%s\n",
+ param->name, param->value);
+ continue;
+ }
+ if (IS_PSTATE_NEGOTIATE(param) &&
+ !IS_PSTATE_ACCEPTOR(param) &&
+ !IS_PSTATE_PROPOSER(param) &&
+ (param->phase & phase)) {
+ *length += sprintf(output_buf, "%s=%s",
+ param->name, param->value);
+ *length += 1;
+ output_buf = textbuf + *length;
+ SET_PSTATE_PROPOSER(param);
+ iscsi_check_proposer_for_optional_reply(param);
+ pr_debug("Sending key: %s=%s\n",
+ param->name, param->value);
+ }
+ }
+
+ list_for_each_entry(er, &param_list->extra_response_list, er_list) {
+ *length += sprintf(output_buf, "%s=%s", er->key, er->value);
+ *length += 1;
+ output_buf = textbuf + *length;
+ pr_debug("Sending key: %s=%s\n", er->key, er->value);
+ }
+ iscsi_release_extra_responses(param_list);
+
+ return 0;
+}
+
+int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list)
+{
+ int ret = 0;
+ struct iscsi_param *param;
+
+ list_for_each_entry(param, &param_list->param_list, p_list) {
+ if (IS_PSTATE_NEGOTIATE(param) &&
+ IS_PSTATE_PROPOSER(param) &&
+ !IS_PSTATE_RESPONSE_GOT(param) &&
+ !IS_PSTATE_REPLY_OPTIONAL(param) &&
+ !IS_PHASE_DECLARATIVE(param)) {
+ pr_err("No response for proposed key \"%s\".\n",
+ param->name);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+int iscsi_change_param_value(
+ char *keyvalue,
+ struct iscsi_param_list *param_list,
+ int check_key)
+{
+ char *key = NULL, *value = NULL;
+ struct iscsi_param *param;
+ int sender = 0;
+
+ if (iscsi_extract_key_value(keyvalue, &key, &value) < 0)
+ return -1;
+
+ if (!check_key) {
+ param = __iscsi_check_key(keyvalue, sender, param_list);
+ if (!param)
+ return -1;
+ } else {
+ param = iscsi_check_key(keyvalue, 0, sender, param_list);
+ if (!param)
+ return -1;
+
+ param->set_param = 1;
+ if (iscsi_check_value(param, value) < 0) {
+ param->set_param = 0;
+ return -1;
+ }
+ param->set_param = 0;
+ }
+
+ if (iscsi_update_param_value(param, value) < 0)
+ return -1;
+
+ return 0;
+}
+
+void iscsi_set_connection_parameters(
+ struct iscsi_conn_ops *ops,
+ struct iscsi_param_list *param_list)
+{
+ char *tmpptr;
+ struct iscsi_param *param;
+
+ pr_debug("---------------------------------------------------"
+ "---------------\n");
+ list_for_each_entry(param, &param_list->param_list, p_list) {
+ /*
+ * Special case to set MAXXMITDATASEGMENTLENGTH from the
+ * target requested MaxRecvDataSegmentLength, even though
+ * this key is not sent over the wire.
+ */
+ if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) {
+ ops->MaxXmitDataSegmentLength =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("MaxXmitDataSegmentLength: %s\n",
+ param->value);
+ }
+
+ if (!IS_PSTATE_ACCEPTOR(param) && !IS_PSTATE_PROPOSER(param))
+ continue;
+ if (!strcmp(param->name, AUTHMETHOD)) {
+ pr_debug("AuthMethod: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, HEADERDIGEST)) {
+ ops->HeaderDigest = !strcmp(param->value, CRC32C);
+ pr_debug("HeaderDigest: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, DATADIGEST)) {
+ ops->DataDigest = !strcmp(param->value, CRC32C);
+ pr_debug("DataDigest: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) {
+ /*
+ * At this point iscsi_check_acceptor_state() will have
+ * set ops->MaxRecvDataSegmentLength from the original
+ * initiator provided value.
+ */
+ pr_debug("MaxRecvDataSegmentLength: %u\n",
+ ops->MaxRecvDataSegmentLength);
+ } else if (!strcmp(param->name, OFMARKER)) {
+ ops->OFMarker = !strcmp(param->value, YES);
+ pr_debug("OFMarker: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, IFMARKER)) {
+ ops->IFMarker = !strcmp(param->value, YES);
+ pr_debug("IFMarker: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, OFMARKINT)) {
+ ops->OFMarkInt =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("OFMarkInt: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, IFMARKINT)) {
+ ops->IFMarkInt =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("IFMarkInt: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH)) {
+ ops->InitiatorRecvDataSegmentLength =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("InitiatorRecvDataSegmentLength: %s\n",
+ param->value);
+ ops->MaxRecvDataSegmentLength =
+ ops->InitiatorRecvDataSegmentLength;
+ pr_debug("Set MRDSL from InitiatorRecvDataSegmentLength\n");
+ } else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH)) {
+ ops->TargetRecvDataSegmentLength =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("TargetRecvDataSegmentLength: %s\n",
+ param->value);
+ ops->MaxXmitDataSegmentLength =
+ ops->TargetRecvDataSegmentLength;
+ pr_debug("Set MXDSL from TargetRecvDataSegmentLength\n");
+ }
+ }
+ pr_debug("----------------------------------------------------"
+ "--------------\n");
+}
+
+void iscsi_set_session_parameters(
+ struct iscsi_sess_ops *ops,
+ struct iscsi_param_list *param_list,
+ int leading)
+{
+ char *tmpptr;
+ struct iscsi_param *param;
+
+ pr_debug("----------------------------------------------------"
+ "--------------\n");
+ list_for_each_entry(param, &param_list->param_list, p_list) {
+ if (!IS_PSTATE_ACCEPTOR(param) && !IS_PSTATE_PROPOSER(param))
+ continue;
+ if (!strcmp(param->name, INITIATORNAME)) {
+ if (!param->value)
+ continue;
+ if (leading)
+ snprintf(ops->InitiatorName,
+ sizeof(ops->InitiatorName),
+ "%s", param->value);
+ pr_debug("InitiatorName: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, INITIATORALIAS)) {
+ if (!param->value)
+ continue;
+ snprintf(ops->InitiatorAlias,
+ sizeof(ops->InitiatorAlias),
+ "%s", param->value);
+ pr_debug("InitiatorAlias: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, TARGETNAME)) {
+ if (!param->value)
+ continue;
+ if (leading)
+ snprintf(ops->TargetName,
+ sizeof(ops->TargetName),
+ "%s", param->value);
+ pr_debug("TargetName: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, TARGETALIAS)) {
+ if (!param->value)
+ continue;
+ snprintf(ops->TargetAlias, sizeof(ops->TargetAlias),
+ "%s", param->value);
+ pr_debug("TargetAlias: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, TARGETPORTALGROUPTAG)) {
+ ops->TargetPortalGroupTag =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("TargetPortalGroupTag: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, MAXCONNECTIONS)) {
+ ops->MaxConnections =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("MaxConnections: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, INITIALR2T)) {
+ ops->InitialR2T = !strcmp(param->value, YES);
+ pr_debug("InitialR2T: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, IMMEDIATEDATA)) {
+ ops->ImmediateData = !strcmp(param->value, YES);
+ pr_debug("ImmediateData: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, MAXBURSTLENGTH)) {
+ ops->MaxBurstLength =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("MaxBurstLength: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, FIRSTBURSTLENGTH)) {
+ ops->FirstBurstLength =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("FirstBurstLength: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, DEFAULTTIME2WAIT)) {
+ ops->DefaultTime2Wait =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("DefaultTime2Wait: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, DEFAULTTIME2RETAIN)) {
+ ops->DefaultTime2Retain =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("DefaultTime2Retain: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, MAXOUTSTANDINGR2T)) {
+ ops->MaxOutstandingR2T =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("MaxOutstandingR2T: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, DATAPDUINORDER)) {
+ ops->DataPDUInOrder = !strcmp(param->value, YES);
+ pr_debug("DataPDUInOrder: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, DATASEQUENCEINORDER)) {
+ ops->DataSequenceInOrder = !strcmp(param->value, YES);
+ pr_debug("DataSequenceInOrder: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, ERRORRECOVERYLEVEL)) {
+ ops->ErrorRecoveryLevel =
+ simple_strtoul(param->value, &tmpptr, 0);
+ pr_debug("ErrorRecoveryLevel: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, SESSIONTYPE)) {
+ ops->SessionType = !strcmp(param->value, DISCOVERY);
+ pr_debug("SessionType: %s\n",
+ param->value);
+ } else if (!strcmp(param->name, RDMAEXTENSIONS)) {
+ ops->RDMAExtensions = !strcmp(param->value, YES);
+ pr_debug("RDMAExtensions: %s\n",
+ param->value);
+ }
+ }
+ pr_debug("----------------------------------------------------"
+ "--------------\n");
+
+}
diff --git a/drivers/target/iscsi/iscsi_target_parameters.h b/drivers/target/iscsi/iscsi_target_parameters.h
new file mode 100644
index 00000000000..a47046a752a
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_parameters.h
@@ -0,0 +1,290 @@
+#ifndef ISCSI_PARAMETERS_H
+#define ISCSI_PARAMETERS_H
+
+#include <scsi/iscsi_proto.h>
+
+struct iscsi_extra_response {
+ char key[KEY_MAXLEN];
+ char value[32];
+ struct list_head er_list;
+} ____cacheline_aligned;
+
+struct iscsi_param {
+ char *name;
+ char *value;
+ u8 set_param;
+ u8 phase;
+ u8 scope;
+ u8 sender;
+ u8 type;
+ u8 use;
+ u16 type_range;
+ u32 state;
+ struct list_head p_list;
+} ____cacheline_aligned;
+
+extern int iscsi_login_rx_data(struct iscsi_conn *, char *, int);
+extern int iscsi_login_tx_data(struct iscsi_conn *, char *, char *, int);
+extern void iscsi_dump_conn_ops(struct iscsi_conn_ops *);
+extern void iscsi_dump_sess_ops(struct iscsi_sess_ops *);
+extern void iscsi_print_params(struct iscsi_param_list *);
+extern int iscsi_create_default_params(struct iscsi_param_list **);
+extern int iscsi_set_keys_to_negotiate(struct iscsi_param_list *, bool);
+extern int iscsi_set_keys_irrelevant_for_discovery(struct iscsi_param_list *);
+extern int iscsi_copy_param_list(struct iscsi_param_list **,
+ struct iscsi_param_list *, int);
+extern int iscsi_change_param_value(char *, struct iscsi_param_list *, int);
+extern void iscsi_release_param_list(struct iscsi_param_list *);
+extern struct iscsi_param *iscsi_find_param_from_key(char *, struct iscsi_param_list *);
+extern int iscsi_extract_key_value(char *, char **, char **);
+extern int iscsi_update_param_value(struct iscsi_param *, char *);
+extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsi_conn *);
+extern int iscsi_encode_text_output(u8, u8, char *, u32 *,
+ struct iscsi_param_list *);
+extern int iscsi_check_negotiated_keys(struct iscsi_param_list *);
+extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *,
+ struct iscsi_param_list *);
+extern void iscsi_set_session_parameters(struct iscsi_sess_ops *,
+ struct iscsi_param_list *, int);
+
+#define YES "Yes"
+#define NO "No"
+#define ALL "All"
+#define IRRELEVANT "Irrelevant"
+#define NONE "None"
+#define NOTUNDERSTOOD "NotUnderstood"
+#define REJECT "Reject"
+
+/*
+ * The Parameter Names.
+ */
+#define AUTHMETHOD "AuthMethod"
+#define HEADERDIGEST "HeaderDigest"
+#define DATADIGEST "DataDigest"
+#define MAXCONNECTIONS "MaxConnections"
+#define SENDTARGETS "SendTargets"
+#define TARGETNAME "TargetName"
+#define INITIATORNAME "InitiatorName"
+#define TARGETALIAS "TargetAlias"
+#define INITIATORALIAS "InitiatorAlias"
+#define TARGETADDRESS "TargetAddress"
+#define TARGETPORTALGROUPTAG "TargetPortalGroupTag"
+#define INITIALR2T "InitialR2T"
+#define IMMEDIATEDATA "ImmediateData"
+#define MAXRECVDATASEGMENTLENGTH "MaxRecvDataSegmentLength"
+#define MAXXMITDATASEGMENTLENGTH "MaxXmitDataSegmentLength"
+#define MAXBURSTLENGTH "MaxBurstLength"
+#define FIRSTBURSTLENGTH "FirstBurstLength"
+#define DEFAULTTIME2WAIT "DefaultTime2Wait"
+#define DEFAULTTIME2RETAIN "DefaultTime2Retain"
+#define MAXOUTSTANDINGR2T "MaxOutstandingR2T"
+#define DATAPDUINORDER "DataPDUInOrder"
+#define DATASEQUENCEINORDER "DataSequenceInOrder"
+#define ERRORRECOVERYLEVEL "ErrorRecoveryLevel"
+#define SESSIONTYPE "SessionType"
+#define IFMARKER "IFMarker"
+#define OFMARKER "OFMarker"
+#define IFMARKINT "IFMarkInt"
+#define OFMARKINT "OFMarkInt"
+#define X_EXTENSIONKEY "X-com.sbei.version"
+#define X_EXTENSIONKEY_CISCO_NEW "X-com.cisco.protocol"
+#define X_EXTENSIONKEY_CISCO_OLD "X-com.cisco.iscsi.draft"
+
+/*
+ * Parameter names of iSCSI Extentions for RDMA (iSER). See RFC-5046
+ */
+#define RDMAEXTENSIONS "RDMAExtensions"
+#define INITIATORRECVDATASEGMENTLENGTH "InitiatorRecvDataSegmentLength"
+#define TARGETRECVDATASEGMENTLENGTH "TargetRecvDataSegmentLength"
+
+/*
+ * For AuthMethod.
+ */
+#define KRB5 "KRB5"
+#define SPKM1 "SPKM1"
+#define SPKM2 "SPKM2"
+#define SRP "SRP"
+#define CHAP "CHAP"
+
+/*
+ * Initial values for Parameter Negotiation.
+ */
+#define INITIAL_AUTHMETHOD CHAP
+#define INITIAL_HEADERDIGEST "CRC32C,None"
+#define INITIAL_DATADIGEST "CRC32C,None"
+#define INITIAL_MAXCONNECTIONS "1"
+#define INITIAL_SENDTARGETS ALL
+#define INITIAL_TARGETNAME "LIO.Target"
+#define INITIAL_INITIATORNAME "LIO.Initiator"
+#define INITIAL_TARGETALIAS "LIO Target"
+#define INITIAL_INITIATORALIAS "LIO Initiator"
+#define INITIAL_TARGETADDRESS "0.0.0.0:0000,0"
+#define INITIAL_TARGETPORTALGROUPTAG "1"
+#define INITIAL_INITIALR2T YES
+#define INITIAL_IMMEDIATEDATA YES
+#define INITIAL_MAXRECVDATASEGMENTLENGTH "8192"
+/*
+ * Match outgoing MXDSL default to incoming Open-iSCSI default
+ */
+#define INITIAL_MAXXMITDATASEGMENTLENGTH "262144"
+#define INITIAL_MAXBURSTLENGTH "262144"
+#define INITIAL_FIRSTBURSTLENGTH "65536"
+#define INITIAL_DEFAULTTIME2WAIT "2"
+#define INITIAL_DEFAULTTIME2RETAIN "20"
+#define INITIAL_MAXOUTSTANDINGR2T "1"
+#define INITIAL_DATAPDUINORDER YES
+#define INITIAL_DATASEQUENCEINORDER YES
+#define INITIAL_ERRORRECOVERYLEVEL "0"
+#define INITIAL_SESSIONTYPE NORMAL
+#define INITIAL_IFMARKER NO
+#define INITIAL_OFMARKER NO
+#define INITIAL_IFMARKINT "2048~65535"
+#define INITIAL_OFMARKINT "2048~65535"
+
+/*
+ * Initial values for iSER parameters following RFC-5046 Section 6
+ */
+#define INITIAL_RDMAEXTENSIONS NO
+#define INITIAL_INITIATORRECVDATASEGMENTLENGTH "262144"
+#define INITIAL_TARGETRECVDATASEGMENTLENGTH "8192"
+
+/*
+ * For [Header,Data]Digests.
+ */
+#define CRC32C "CRC32C"
+
+/*
+ * For SessionType.
+ */
+#define DISCOVERY "Discovery"
+#define NORMAL "Normal"
+
+/*
+ * struct iscsi_param->use
+ */
+#define USE_LEADING_ONLY 0x01
+#define USE_INITIAL_ONLY 0x02
+#define USE_ALL 0x04
+
+#define IS_USE_LEADING_ONLY(p) ((p)->use & USE_LEADING_ONLY)
+#define IS_USE_INITIAL_ONLY(p) ((p)->use & USE_INITIAL_ONLY)
+#define IS_USE_ALL(p) ((p)->use & USE_ALL)
+
+#define SET_USE_INITIAL_ONLY(p) ((p)->use |= USE_INITIAL_ONLY)
+
+/*
+ * struct iscsi_param->sender
+ */
+#define SENDER_INITIATOR 0x01
+#define SENDER_TARGET 0x02
+#define SENDER_BOTH 0x03
+/* Used in iscsi_check_key() */
+#define SENDER_RECEIVER 0x04
+
+#define IS_SENDER_INITIATOR(p) ((p)->sender & SENDER_INITIATOR)
+#define IS_SENDER_TARGET(p) ((p)->sender & SENDER_TARGET)
+#define IS_SENDER_BOTH(p) ((p)->sender & SENDER_BOTH)
+
+/*
+ * struct iscsi_param->scope
+ */
+#define SCOPE_CONNECTION_ONLY 0x01
+#define SCOPE_SESSION_WIDE 0x02
+
+#define IS_SCOPE_CONNECTION_ONLY(p) ((p)->scope & SCOPE_CONNECTION_ONLY)
+#define IS_SCOPE_SESSION_WIDE(p) ((p)->scope & SCOPE_SESSION_WIDE)
+
+/*
+ * struct iscsi_param->phase
+ */
+#define PHASE_SECURITY 0x01
+#define PHASE_OPERATIONAL 0x02
+#define PHASE_DECLARATIVE 0x04
+#define PHASE_FFP0 0x08
+
+#define IS_PHASE_SECURITY(p) ((p)->phase & PHASE_SECURITY)
+#define IS_PHASE_OPERATIONAL(p) ((p)->phase & PHASE_OPERATIONAL)
+#define IS_PHASE_DECLARATIVE(p) ((p)->phase & PHASE_DECLARATIVE)
+#define IS_PHASE_FFP0(p) ((p)->phase & PHASE_FFP0)
+
+/*
+ * struct iscsi_param->type
+ */
+#define TYPE_BOOL_AND 0x01
+#define TYPE_BOOL_OR 0x02
+#define TYPE_NUMBER 0x04
+#define TYPE_NUMBER_RANGE 0x08
+#define TYPE_STRING 0x10
+#define TYPE_VALUE_LIST 0x20
+
+#define IS_TYPE_BOOL_AND(p) ((p)->type & TYPE_BOOL_AND)
+#define IS_TYPE_BOOL_OR(p) ((p)->type & TYPE_BOOL_OR)
+#define IS_TYPE_NUMBER(p) ((p)->type & TYPE_NUMBER)
+#define IS_TYPE_NUMBER_RANGE(p) ((p)->type & TYPE_NUMBER_RANGE)
+#define IS_TYPE_STRING(p) ((p)->type & TYPE_STRING)
+#define IS_TYPE_VALUE_LIST(p) ((p)->type & TYPE_VALUE_LIST)
+
+/*
+ * struct iscsi_param->type_range
+ */
+#define TYPERANGE_BOOL_AND 0x0001
+#define TYPERANGE_BOOL_OR 0x0002
+#define TYPERANGE_0_TO_2 0x0004
+#define TYPERANGE_0_TO_3600 0x0008
+#define TYPERANGE_0_TO_32767 0x0010
+#define TYPERANGE_0_TO_65535 0x0020
+#define TYPERANGE_1_TO_65535 0x0040
+#define TYPERANGE_2_TO_3600 0x0080
+#define TYPERANGE_512_TO_16777215 0x0100
+#define TYPERANGE_AUTH 0x0200
+#define TYPERANGE_DIGEST 0x0400
+#define TYPERANGE_ISCSINAME 0x0800
+#define TYPERANGE_MARKINT 0x1000
+#define TYPERANGE_SESSIONTYPE 0x2000
+#define TYPERANGE_TARGETADDRESS 0x4000
+#define TYPERANGE_UTF8 0x8000
+
+#define IS_TYPERANGE_0_TO_2(p) ((p)->type_range & TYPERANGE_0_TO_2)
+#define IS_TYPERANGE_0_TO_3600(p) ((p)->type_range & TYPERANGE_0_TO_3600)
+#define IS_TYPERANGE_0_TO_32767(p) ((p)->type_range & TYPERANGE_0_TO_32767)
+#define IS_TYPERANGE_0_TO_65535(p) ((p)->type_range & TYPERANGE_0_TO_65535)
+#define IS_TYPERANGE_1_TO_65535(p) ((p)->type_range & TYPERANGE_1_TO_65535)
+#define IS_TYPERANGE_2_TO_3600(p) ((p)->type_range & TYPERANGE_2_TO_3600)
+#define IS_TYPERANGE_512_TO_16777215(p) ((p)->type_range & \
+ TYPERANGE_512_TO_16777215)
+#define IS_TYPERANGE_AUTH_PARAM(p) ((p)->type_range & TYPERANGE_AUTH)
+#define IS_TYPERANGE_DIGEST_PARAM(p) ((p)->type_range & TYPERANGE_DIGEST)
+#define IS_TYPERANGE_SESSIONTYPE(p) ((p)->type_range & \
+ TYPERANGE_SESSIONTYPE)
+
+/*
+ * struct iscsi_param->state
+ */
+#define PSTATE_ACCEPTOR 0x01
+#define PSTATE_NEGOTIATE 0x02
+#define PSTATE_PROPOSER 0x04
+#define PSTATE_IRRELEVANT 0x08
+#define PSTATE_REJECT 0x10
+#define PSTATE_REPLY_OPTIONAL 0x20
+#define PSTATE_RESPONSE_GOT 0x40
+#define PSTATE_RESPONSE_SENT 0x80
+
+#define IS_PSTATE_ACCEPTOR(p) ((p)->state & PSTATE_ACCEPTOR)
+#define IS_PSTATE_NEGOTIATE(p) ((p)->state & PSTATE_NEGOTIATE)
+#define IS_PSTATE_PROPOSER(p) ((p)->state & PSTATE_PROPOSER)
+#define IS_PSTATE_IRRELEVANT(p) ((p)->state & PSTATE_IRRELEVANT)
+#define IS_PSTATE_REJECT(p) ((p)->state & PSTATE_REJECT)
+#define IS_PSTATE_REPLY_OPTIONAL(p) ((p)->state & PSTATE_REPLY_OPTIONAL)
+#define IS_PSTATE_RESPONSE_GOT(p) ((p)->state & PSTATE_RESPONSE_GOT)
+#define IS_PSTATE_RESPONSE_SENT(p) ((p)->state & PSTATE_RESPONSE_SENT)
+
+#define SET_PSTATE_ACCEPTOR(p) ((p)->state |= PSTATE_ACCEPTOR)
+#define SET_PSTATE_NEGOTIATE(p) ((p)->state |= PSTATE_NEGOTIATE)
+#define SET_PSTATE_PROPOSER(p) ((p)->state |= PSTATE_PROPOSER)
+#define SET_PSTATE_IRRELEVANT(p) ((p)->state |= PSTATE_IRRELEVANT)
+#define SET_PSTATE_REJECT(p) ((p)->state |= PSTATE_REJECT)
+#define SET_PSTATE_REPLY_OPTIONAL(p) ((p)->state |= PSTATE_REPLY_OPTIONAL)
+#define SET_PSTATE_RESPONSE_GOT(p) ((p)->state |= PSTATE_RESPONSE_GOT)
+#define SET_PSTATE_RESPONSE_SENT(p) ((p)->state |= PSTATE_RESPONSE_SENT)
+
+#endif /* ISCSI_PARAMETERS_H */
diff --git a/drivers/target/iscsi/iscsi_target_seq_pdu_list.c b/drivers/target/iscsi/iscsi_target_seq_pdu_list.c
new file mode 100644
index 00000000000..ca41b583f2f
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_seq_pdu_list.c
@@ -0,0 +1,700 @@
+/*******************************************************************************
+ * This file contains main functions related to iSCSI DataSequenceInOrder=No
+ * and DataPDUInOrder=No.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/slab.h>
+#include <linux/random.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_seq_pdu_list.h"
+
+#define OFFLOAD_BUF_SIZE 32768
+
+#ifdef DEBUG
+static void iscsit_dump_seq_list(struct iscsi_cmd *cmd)
+{
+ int i;
+ struct iscsi_seq *seq;
+
+ pr_debug("Dumping Sequence List for ITT: 0x%08x:\n",
+ cmd->init_task_tag);
+
+ for (i = 0; i < cmd->seq_count; i++) {
+ seq = &cmd->seq_list[i];
+ pr_debug("i: %d, pdu_start: %d, pdu_count: %d,"
+ " offset: %d, xfer_len: %d, seq_send_order: %d,"
+ " seq_no: %d\n", i, seq->pdu_start, seq->pdu_count,
+ seq->offset, seq->xfer_len, seq->seq_send_order,
+ seq->seq_no);
+ }
+}
+
+static void iscsit_dump_pdu_list(struct iscsi_cmd *cmd)
+{
+ int i;
+ struct iscsi_pdu *pdu;
+
+ pr_debug("Dumping PDU List for ITT: 0x%08x:\n",
+ cmd->init_task_tag);
+
+ for (i = 0; i < cmd->pdu_count; i++) {
+ pdu = &cmd->pdu_list[i];
+ pr_debug("i: %d, offset: %d, length: %d,"
+ " pdu_send_order: %d, seq_no: %d\n", i, pdu->offset,
+ pdu->length, pdu->pdu_send_order, pdu->seq_no);
+ }
+}
+#else
+static void iscsit_dump_seq_list(struct iscsi_cmd *cmd) {}
+static void iscsit_dump_pdu_list(struct iscsi_cmd *cmd) {}
+#endif
+
+static void iscsit_ordered_seq_lists(
+ struct iscsi_cmd *cmd,
+ u8 type)
+{
+ u32 i, seq_count = 0;
+
+ for (i = 0; i < cmd->seq_count; i++) {
+ if (cmd->seq_list[i].type != SEQTYPE_NORMAL)
+ continue;
+ cmd->seq_list[i].seq_send_order = seq_count++;
+ }
+}
+
+static void iscsit_ordered_pdu_lists(
+ struct iscsi_cmd *cmd,
+ u8 type)
+{
+ u32 i, pdu_send_order = 0, seq_no = 0;
+
+ for (i = 0; i < cmd->pdu_count; i++) {
+redo:
+ if (cmd->pdu_list[i].seq_no == seq_no) {
+ cmd->pdu_list[i].pdu_send_order = pdu_send_order++;
+ continue;
+ }
+ seq_no++;
+ pdu_send_order = 0;
+ goto redo;
+ }
+}
+
+/*
+ * Generate count random values into array.
+ * Use 0x80000000 to mark generates valued in array[].
+ */
+static void iscsit_create_random_array(u32 *array, u32 count)
+{
+ int i, j, k;
+
+ if (count == 1) {
+ array[0] = 0;
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+redo:
+ get_random_bytes(&j, sizeof(u32));
+ j = (1 + (int) (9999 + 1) - j) % count;
+ for (k = 0; k < i + 1; k++) {
+ j |= 0x80000000;
+ if ((array[k] & 0x80000000) && (array[k] == j))
+ goto redo;
+ }
+ array[i] = j;
+ }
+
+ for (i = 0; i < count; i++)
+ array[i] &= ~0x80000000;
+}
+
+static int iscsit_randomize_pdu_lists(
+ struct iscsi_cmd *cmd,
+ u8 type)
+{
+ int i = 0;
+ u32 *array, pdu_count, seq_count = 0, seq_no = 0, seq_offset = 0;
+
+ for (pdu_count = 0; pdu_count < cmd->pdu_count; pdu_count++) {
+redo:
+ if (cmd->pdu_list[pdu_count].seq_no == seq_no) {
+ seq_count++;
+ continue;
+ }
+ array = kcalloc(seq_count, sizeof(u32), GFP_KERNEL);
+ if (!array) {
+ pr_err("Unable to allocate memory"
+ " for random array.\n");
+ return -ENOMEM;
+ }
+ iscsit_create_random_array(array, seq_count);
+
+ for (i = 0; i < seq_count; i++)
+ cmd->pdu_list[seq_offset+i].pdu_send_order = array[i];
+
+ kfree(array);
+
+ seq_offset += seq_count;
+ seq_count = 0;
+ seq_no++;
+ goto redo;
+ }
+
+ if (seq_count) {
+ array = kcalloc(seq_count, sizeof(u32), GFP_KERNEL);
+ if (!array) {
+ pr_err("Unable to allocate memory for"
+ " random array.\n");
+ return -ENOMEM;
+ }
+ iscsit_create_random_array(array, seq_count);
+
+ for (i = 0; i < seq_count; i++)
+ cmd->pdu_list[seq_offset+i].pdu_send_order = array[i];
+
+ kfree(array);
+ }
+
+ return 0;
+}
+
+static int iscsit_randomize_seq_lists(
+ struct iscsi_cmd *cmd,
+ u8 type)
+{
+ int i, j = 0;
+ u32 *array, seq_count = cmd->seq_count;
+
+ if ((type == PDULIST_IMMEDIATE) || (type == PDULIST_UNSOLICITED))
+ seq_count--;
+ else if (type == PDULIST_IMMEDIATE_AND_UNSOLICITED)
+ seq_count -= 2;
+
+ if (!seq_count)
+ return 0;
+
+ array = kcalloc(seq_count, sizeof(u32), GFP_KERNEL);
+ if (!array) {
+ pr_err("Unable to allocate memory for random array.\n");
+ return -ENOMEM;
+ }
+ iscsit_create_random_array(array, seq_count);
+
+ for (i = 0; i < cmd->seq_count; i++) {
+ if (cmd->seq_list[i].type != SEQTYPE_NORMAL)
+ continue;
+ cmd->seq_list[i].seq_send_order = array[j++];
+ }
+
+ kfree(array);
+ return 0;
+}
+
+static void iscsit_determine_counts_for_list(
+ struct iscsi_cmd *cmd,
+ struct iscsi_build_list *bl,
+ u32 *seq_count,
+ u32 *pdu_count)
+{
+ int check_immediate = 0;
+ u32 burstlength = 0, offset = 0;
+ u32 unsolicited_data_length = 0;
+ u32 mdsl;
+ struct iscsi_conn *conn = cmd->conn;
+
+ if (cmd->se_cmd.data_direction == DMA_TO_DEVICE)
+ mdsl = cmd->conn->conn_ops->MaxXmitDataSegmentLength;
+ else
+ mdsl = cmd->conn->conn_ops->MaxRecvDataSegmentLength;
+
+ if ((bl->type == PDULIST_IMMEDIATE) ||
+ (bl->type == PDULIST_IMMEDIATE_AND_UNSOLICITED))
+ check_immediate = 1;
+
+ if ((bl->type == PDULIST_UNSOLICITED) ||
+ (bl->type == PDULIST_IMMEDIATE_AND_UNSOLICITED))
+ unsolicited_data_length = min(cmd->se_cmd.data_length,
+ conn->sess->sess_ops->FirstBurstLength);
+
+ while (offset < cmd->se_cmd.data_length) {
+ *pdu_count += 1;
+
+ if (check_immediate) {
+ check_immediate = 0;
+ offset += bl->immediate_data_length;
+ *seq_count += 1;
+ if (unsolicited_data_length)
+ unsolicited_data_length -=
+ bl->immediate_data_length;
+ continue;
+ }
+ if (unsolicited_data_length > 0) {
+ if ((offset + mdsl) >= cmd->se_cmd.data_length) {
+ unsolicited_data_length -=
+ (cmd->se_cmd.data_length - offset);
+ offset += (cmd->se_cmd.data_length - offset);
+ continue;
+ }
+ if ((offset + mdsl)
+ >= conn->sess->sess_ops->FirstBurstLength) {
+ unsolicited_data_length -=
+ (conn->sess->sess_ops->FirstBurstLength -
+ offset);
+ offset += (conn->sess->sess_ops->FirstBurstLength -
+ offset);
+ burstlength = 0;
+ *seq_count += 1;
+ continue;
+ }
+
+ offset += mdsl;
+ unsolicited_data_length -= mdsl;
+ continue;
+ }
+ if ((offset + mdsl) >= cmd->se_cmd.data_length) {
+ offset += (cmd->se_cmd.data_length - offset);
+ continue;
+ }
+ if ((burstlength + mdsl) >=
+ conn->sess->sess_ops->MaxBurstLength) {
+ offset += (conn->sess->sess_ops->MaxBurstLength -
+ burstlength);
+ burstlength = 0;
+ *seq_count += 1;
+ continue;
+ }
+
+ burstlength += mdsl;
+ offset += mdsl;
+ }
+}
+
+
+/*
+ * Builds PDU and/or Sequence list, called while DataSequenceInOrder=No
+ * or DataPDUInOrder=No.
+ */
+static int iscsit_do_build_pdu_and_seq_lists(
+ struct iscsi_cmd *cmd,
+ struct iscsi_build_list *bl)
+{
+ int check_immediate = 0, datapduinorder, datasequenceinorder;
+ u32 burstlength = 0, offset = 0, i = 0, mdsl;
+ u32 pdu_count = 0, seq_no = 0, unsolicited_data_length = 0;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_pdu *pdu = cmd->pdu_list;
+ struct iscsi_seq *seq = cmd->seq_list;
+
+ if (cmd->se_cmd.data_direction == DMA_TO_DEVICE)
+ mdsl = cmd->conn->conn_ops->MaxXmitDataSegmentLength;
+ else
+ mdsl = cmd->conn->conn_ops->MaxRecvDataSegmentLength;
+
+ datapduinorder = conn->sess->sess_ops->DataPDUInOrder;
+ datasequenceinorder = conn->sess->sess_ops->DataSequenceInOrder;
+
+ if ((bl->type == PDULIST_IMMEDIATE) ||
+ (bl->type == PDULIST_IMMEDIATE_AND_UNSOLICITED))
+ check_immediate = 1;
+
+ if ((bl->type == PDULIST_UNSOLICITED) ||
+ (bl->type == PDULIST_IMMEDIATE_AND_UNSOLICITED))
+ unsolicited_data_length = min(cmd->se_cmd.data_length,
+ conn->sess->sess_ops->FirstBurstLength);
+
+ while (offset < cmd->se_cmd.data_length) {
+ pdu_count++;
+ if (!datapduinorder) {
+ pdu[i].offset = offset;
+ pdu[i].seq_no = seq_no;
+ }
+ if (!datasequenceinorder && (pdu_count == 1)) {
+ seq[seq_no].pdu_start = i;
+ seq[seq_no].seq_no = seq_no;
+ seq[seq_no].offset = offset;
+ seq[seq_no].orig_offset = offset;
+ }
+
+ if (check_immediate) {
+ check_immediate = 0;
+ if (!datapduinorder) {
+ pdu[i].type = PDUTYPE_IMMEDIATE;
+ pdu[i++].length = bl->immediate_data_length;
+ }
+ if (!datasequenceinorder) {
+ seq[seq_no].type = SEQTYPE_IMMEDIATE;
+ seq[seq_no].pdu_count = 1;
+ seq[seq_no].xfer_len =
+ bl->immediate_data_length;
+ }
+ offset += bl->immediate_data_length;
+ pdu_count = 0;
+ seq_no++;
+ if (unsolicited_data_length)
+ unsolicited_data_length -=
+ bl->immediate_data_length;
+ continue;
+ }
+ if (unsolicited_data_length > 0) {
+ if ((offset + mdsl) >= cmd->se_cmd.data_length) {
+ if (!datapduinorder) {
+ pdu[i].type = PDUTYPE_UNSOLICITED;
+ pdu[i].length =
+ (cmd->se_cmd.data_length - offset);
+ }
+ if (!datasequenceinorder) {
+ seq[seq_no].type = SEQTYPE_UNSOLICITED;
+ seq[seq_no].pdu_count = pdu_count;
+ seq[seq_no].xfer_len = (burstlength +
+ (cmd->se_cmd.data_length - offset));
+ }
+ unsolicited_data_length -=
+ (cmd->se_cmd.data_length - offset);
+ offset += (cmd->se_cmd.data_length - offset);
+ continue;
+ }
+ if ((offset + mdsl) >=
+ conn->sess->sess_ops->FirstBurstLength) {
+ if (!datapduinorder) {
+ pdu[i].type = PDUTYPE_UNSOLICITED;
+ pdu[i++].length =
+ (conn->sess->sess_ops->FirstBurstLength -
+ offset);
+ }
+ if (!datasequenceinorder) {
+ seq[seq_no].type = SEQTYPE_UNSOLICITED;
+ seq[seq_no].pdu_count = pdu_count;
+ seq[seq_no].xfer_len = (burstlength +
+ (conn->sess->sess_ops->FirstBurstLength -
+ offset));
+ }
+ unsolicited_data_length -=
+ (conn->sess->sess_ops->FirstBurstLength -
+ offset);
+ offset += (conn->sess->sess_ops->FirstBurstLength -
+ offset);
+ burstlength = 0;
+ pdu_count = 0;
+ seq_no++;
+ continue;
+ }
+
+ if (!datapduinorder) {
+ pdu[i].type = PDUTYPE_UNSOLICITED;
+ pdu[i++].length = mdsl;
+ }
+ burstlength += mdsl;
+ offset += mdsl;
+ unsolicited_data_length -= mdsl;
+ continue;
+ }
+ if ((offset + mdsl) >= cmd->se_cmd.data_length) {
+ if (!datapduinorder) {
+ pdu[i].type = PDUTYPE_NORMAL;
+ pdu[i].length = (cmd->se_cmd.data_length - offset);
+ }
+ if (!datasequenceinorder) {
+ seq[seq_no].type = SEQTYPE_NORMAL;
+ seq[seq_no].pdu_count = pdu_count;
+ seq[seq_no].xfer_len = (burstlength +
+ (cmd->se_cmd.data_length - offset));
+ }
+ offset += (cmd->se_cmd.data_length - offset);
+ continue;
+ }
+ if ((burstlength + mdsl) >=
+ conn->sess->sess_ops->MaxBurstLength) {
+ if (!datapduinorder) {
+ pdu[i].type = PDUTYPE_NORMAL;
+ pdu[i++].length =
+ (conn->sess->sess_ops->MaxBurstLength -
+ burstlength);
+ }
+ if (!datasequenceinorder) {
+ seq[seq_no].type = SEQTYPE_NORMAL;
+ seq[seq_no].pdu_count = pdu_count;
+ seq[seq_no].xfer_len = (burstlength +
+ (conn->sess->sess_ops->MaxBurstLength -
+ burstlength));
+ }
+ offset += (conn->sess->sess_ops->MaxBurstLength -
+ burstlength);
+ burstlength = 0;
+ pdu_count = 0;
+ seq_no++;
+ continue;
+ }
+
+ if (!datapduinorder) {
+ pdu[i].type = PDUTYPE_NORMAL;
+ pdu[i++].length = mdsl;
+ }
+ burstlength += mdsl;
+ offset += mdsl;
+ }
+
+ if (!datasequenceinorder) {
+ if (bl->data_direction & ISCSI_PDU_WRITE) {
+ if (bl->randomize & RANDOM_R2T_OFFSETS) {
+ if (iscsit_randomize_seq_lists(cmd, bl->type)
+ < 0)
+ return -1;
+ } else
+ iscsit_ordered_seq_lists(cmd, bl->type);
+ } else if (bl->data_direction & ISCSI_PDU_READ) {
+ if (bl->randomize & RANDOM_DATAIN_SEQ_OFFSETS) {
+ if (iscsit_randomize_seq_lists(cmd, bl->type)
+ < 0)
+ return -1;
+ } else
+ iscsit_ordered_seq_lists(cmd, bl->type);
+ }
+
+ iscsit_dump_seq_list(cmd);
+ }
+ if (!datapduinorder) {
+ if (bl->data_direction & ISCSI_PDU_WRITE) {
+ if (bl->randomize & RANDOM_DATAOUT_PDU_OFFSETS) {
+ if (iscsit_randomize_pdu_lists(cmd, bl->type)
+ < 0)
+ return -1;
+ } else
+ iscsit_ordered_pdu_lists(cmd, bl->type);
+ } else if (bl->data_direction & ISCSI_PDU_READ) {
+ if (bl->randomize & RANDOM_DATAIN_PDU_OFFSETS) {
+ if (iscsit_randomize_pdu_lists(cmd, bl->type)
+ < 0)
+ return -1;
+ } else
+ iscsit_ordered_pdu_lists(cmd, bl->type);
+ }
+
+ iscsit_dump_pdu_list(cmd);
+ }
+
+ return 0;
+}
+
+int iscsit_build_pdu_and_seq_lists(
+ struct iscsi_cmd *cmd,
+ u32 immediate_data_length)
+{
+ struct iscsi_build_list bl;
+ u32 pdu_count = 0, seq_count = 1;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_pdu *pdu = NULL;
+ struct iscsi_seq *seq = NULL;
+
+ struct iscsi_session *sess = conn->sess;
+ struct iscsi_node_attrib *na;
+
+ /*
+ * Do nothing if no OOO shenanigans
+ */
+ if (sess->sess_ops->DataSequenceInOrder &&
+ sess->sess_ops->DataPDUInOrder)
+ return 0;
+
+ if (cmd->data_direction == DMA_NONE)
+ return 0;
+
+ na = iscsit_tpg_get_node_attrib(sess);
+ memset(&bl, 0, sizeof(struct iscsi_build_list));
+
+ if (cmd->data_direction == DMA_FROM_DEVICE) {
+ bl.data_direction = ISCSI_PDU_READ;
+ bl.type = PDULIST_NORMAL;
+ if (na->random_datain_pdu_offsets)
+ bl.randomize |= RANDOM_DATAIN_PDU_OFFSETS;
+ if (na->random_datain_seq_offsets)
+ bl.randomize |= RANDOM_DATAIN_SEQ_OFFSETS;
+ } else {
+ bl.data_direction = ISCSI_PDU_WRITE;
+ bl.immediate_data_length = immediate_data_length;
+ if (na->random_r2t_offsets)
+ bl.randomize |= RANDOM_R2T_OFFSETS;
+
+ if (!cmd->immediate_data && !cmd->unsolicited_data)
+ bl.type = PDULIST_NORMAL;
+ else if (cmd->immediate_data && !cmd->unsolicited_data)
+ bl.type = PDULIST_IMMEDIATE;
+ else if (!cmd->immediate_data && cmd->unsolicited_data)
+ bl.type = PDULIST_UNSOLICITED;
+ else if (cmd->immediate_data && cmd->unsolicited_data)
+ bl.type = PDULIST_IMMEDIATE_AND_UNSOLICITED;
+ }
+
+ iscsit_determine_counts_for_list(cmd, &bl, &seq_count, &pdu_count);
+
+ if (!conn->sess->sess_ops->DataSequenceInOrder) {
+ seq = kcalloc(seq_count, sizeof(struct iscsi_seq), GFP_ATOMIC);
+ if (!seq) {
+ pr_err("Unable to allocate struct iscsi_seq list\n");
+ return -ENOMEM;
+ }
+ cmd->seq_list = seq;
+ cmd->seq_count = seq_count;
+ }
+
+ if (!conn->sess->sess_ops->DataPDUInOrder) {
+ pdu = kcalloc(pdu_count, sizeof(struct iscsi_pdu), GFP_ATOMIC);
+ if (!pdu) {
+ pr_err("Unable to allocate struct iscsi_pdu list.\n");
+ kfree(seq);
+ return -ENOMEM;
+ }
+ cmd->pdu_list = pdu;
+ cmd->pdu_count = pdu_count;
+ }
+
+ return iscsit_do_build_pdu_and_seq_lists(cmd, &bl);
+}
+
+struct iscsi_pdu *iscsit_get_pdu_holder(
+ struct iscsi_cmd *cmd,
+ u32 offset,
+ u32 length)
+{
+ u32 i;
+ struct iscsi_pdu *pdu = NULL;
+
+ if (!cmd->pdu_list) {
+ pr_err("struct iscsi_cmd->pdu_list is NULL!\n");
+ return NULL;
+ }
+
+ pdu = &cmd->pdu_list[0];
+
+ for (i = 0; i < cmd->pdu_count; i++)
+ if ((pdu[i].offset == offset) && (pdu[i].length == length))
+ return &pdu[i];
+
+ pr_err("Unable to locate PDU holder for ITT: 0x%08x, Offset:"
+ " %u, Length: %u\n", cmd->init_task_tag, offset, length);
+ return NULL;
+}
+
+struct iscsi_pdu *iscsit_get_pdu_holder_for_seq(
+ struct iscsi_cmd *cmd,
+ struct iscsi_seq *seq)
+{
+ u32 i;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_pdu *pdu = NULL;
+
+ if (!cmd->pdu_list) {
+ pr_err("struct iscsi_cmd->pdu_list is NULL!\n");
+ return NULL;
+ }
+
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+redo:
+ pdu = &cmd->pdu_list[cmd->pdu_start];
+
+ for (i = 0; pdu[i].seq_no != cmd->seq_no; i++) {
+ pr_debug("pdu[i].seq_no: %d, pdu[i].pdu"
+ "_send_order: %d, pdu[i].offset: %d,"
+ " pdu[i].length: %d\n", pdu[i].seq_no,
+ pdu[i].pdu_send_order, pdu[i].offset,
+ pdu[i].length);
+
+ if (pdu[i].pdu_send_order == cmd->pdu_send_order) {
+ cmd->pdu_send_order++;
+ return &pdu[i];
+ }
+ }
+
+ cmd->pdu_start += cmd->pdu_send_order;
+ cmd->pdu_send_order = 0;
+ cmd->seq_no++;
+
+ if (cmd->pdu_start < cmd->pdu_count)
+ goto redo;
+
+ pr_err("Command ITT: 0x%08x unable to locate"
+ " struct iscsi_pdu for cmd->pdu_send_order: %u.\n",
+ cmd->init_task_tag, cmd->pdu_send_order);
+ return NULL;
+ } else {
+ if (!seq) {
+ pr_err("struct iscsi_seq is NULL!\n");
+ return NULL;
+ }
+
+ pr_debug("seq->pdu_start: %d, seq->pdu_count: %d,"
+ " seq->seq_no: %d\n", seq->pdu_start, seq->pdu_count,
+ seq->seq_no);
+
+ pdu = &cmd->pdu_list[seq->pdu_start];
+
+ if (seq->pdu_send_order == seq->pdu_count) {
+ pr_err("Command ITT: 0x%08x seq->pdu_send"
+ "_order: %u equals seq->pdu_count: %u\n",
+ cmd->init_task_tag, seq->pdu_send_order,
+ seq->pdu_count);
+ return NULL;
+ }
+
+ for (i = 0; i < seq->pdu_count; i++) {
+ if (pdu[i].pdu_send_order == seq->pdu_send_order) {
+ seq->pdu_send_order++;
+ return &pdu[i];
+ }
+ }
+
+ pr_err("Command ITT: 0x%08x unable to locate iscsi"
+ "_pdu_t for seq->pdu_send_order: %u.\n",
+ cmd->init_task_tag, seq->pdu_send_order);
+ return NULL;
+ }
+
+ return NULL;
+}
+
+struct iscsi_seq *iscsit_get_seq_holder(
+ struct iscsi_cmd *cmd,
+ u32 offset,
+ u32 length)
+{
+ u32 i;
+
+ if (!cmd->seq_list) {
+ pr_err("struct iscsi_cmd->seq_list is NULL!\n");
+ return NULL;
+ }
+
+ for (i = 0; i < cmd->seq_count; i++) {
+ pr_debug("seq_list[i].orig_offset: %d, seq_list[i]."
+ "xfer_len: %d, seq_list[i].seq_no %u\n",
+ cmd->seq_list[i].orig_offset, cmd->seq_list[i].xfer_len,
+ cmd->seq_list[i].seq_no);
+
+ if ((cmd->seq_list[i].orig_offset +
+ cmd->seq_list[i].xfer_len) >=
+ (offset + length))
+ return &cmd->seq_list[i];
+ }
+
+ pr_err("Unable to locate Sequence holder for ITT: 0x%08x,"
+ " Offset: %u, Length: %u\n", cmd->init_task_tag, offset,
+ length);
+ return NULL;
+}
diff --git a/drivers/target/iscsi/iscsi_target_seq_pdu_list.h b/drivers/target/iscsi/iscsi_target_seq_pdu_list.h
new file mode 100644
index 00000000000..d5b153751a8
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_seq_pdu_list.h
@@ -0,0 +1,86 @@
+#ifndef ISCSI_SEQ_AND_PDU_LIST_H
+#define ISCSI_SEQ_AND_PDU_LIST_H
+
+/* struct iscsi_pdu->status */
+#define DATAOUT_PDU_SENT 1
+
+/* struct iscsi_seq->type */
+#define SEQTYPE_IMMEDIATE 1
+#define SEQTYPE_UNSOLICITED 2
+#define SEQTYPE_NORMAL 3
+
+/* struct iscsi_seq->status */
+#define DATAOUT_SEQUENCE_GOT_R2T 1
+#define DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY 2
+#define DATAOUT_SEQUENCE_COMPLETE 3
+
+/* iscsi_determine_counts_for_list() type */
+#define PDULIST_NORMAL 1
+#define PDULIST_IMMEDIATE 2
+#define PDULIST_UNSOLICITED 3
+#define PDULIST_IMMEDIATE_AND_UNSOLICITED 4
+
+/* struct iscsi_pdu->type */
+#define PDUTYPE_IMMEDIATE 1
+#define PDUTYPE_UNSOLICITED 2
+#define PDUTYPE_NORMAL 3
+
+/* struct iscsi_pdu->status */
+#define ISCSI_PDU_NOT_RECEIVED 0
+#define ISCSI_PDU_RECEIVED_OK 1
+#define ISCSI_PDU_CRC_FAILED 2
+#define ISCSI_PDU_TIMED_OUT 3
+
+/* struct iscsi_build_list->randomize */
+#define RANDOM_DATAIN_PDU_OFFSETS 0x01
+#define RANDOM_DATAIN_SEQ_OFFSETS 0x02
+#define RANDOM_DATAOUT_PDU_OFFSETS 0x04
+#define RANDOM_R2T_OFFSETS 0x08
+
+/* struct iscsi_build_list->data_direction */
+#define ISCSI_PDU_READ 0x01
+#define ISCSI_PDU_WRITE 0x02
+
+struct iscsi_build_list {
+ int data_direction;
+ int randomize;
+ int type;
+ int immediate_data_length;
+};
+
+struct iscsi_pdu {
+ int status;
+ int type;
+ u8 flags;
+ u32 data_sn;
+ u32 length;
+ u32 offset;
+ u32 pdu_send_order;
+ u32 seq_no;
+} ____cacheline_aligned;
+
+struct iscsi_seq {
+ int sent;
+ int status;
+ int type;
+ u32 data_sn;
+ u32 first_datasn;
+ u32 last_datasn;
+ u32 next_burst_len;
+ u32 pdu_start;
+ u32 pdu_count;
+ u32 offset;
+ u32 orig_offset;
+ u32 pdu_send_order;
+ u32 r2t_sn;
+ u32 seq_send_order;
+ u32 seq_no;
+ u32 xfer_len;
+} ____cacheline_aligned;
+
+extern int iscsit_build_pdu_and_seq_lists(struct iscsi_cmd *, u32);
+extern struct iscsi_pdu *iscsit_get_pdu_holder(struct iscsi_cmd *, u32, u32);
+extern struct iscsi_pdu *iscsit_get_pdu_holder_for_seq(struct iscsi_cmd *, struct iscsi_seq *);
+extern struct iscsi_seq *iscsit_get_seq_holder(struct iscsi_cmd *, u32, u32);
+
+#endif /* ISCSI_SEQ_AND_PDU_LIST_H */
diff --git a/drivers/target/iscsi/iscsi_target_stat.c b/drivers/target/iscsi/iscsi_target_stat.c
new file mode 100644
index 00000000000..10339551030
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_stat.c
@@ -0,0 +1,949 @@
+/*******************************************************************************
+ * Modern ConfigFS group context specific iSCSI statistics based on original
+ * iscsi_target_mib.c code
+ *
+ * Copyright (c) 2011-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/configfs.h>
+#include <linux/export.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/configfs_macros.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_parameters.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target_stat.h"
+
+#ifndef INITIAL_JIFFIES
+#define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ))
+#endif
+
+/* Instance Attributes Table */
+#define ISCSI_INST_NUM_NODES 1
+#define ISCSI_INST_DESCR "Storage Engine Target"
+#define ISCSI_INST_LAST_FAILURE_TYPE 0
+#define ISCSI_DISCONTINUITY_TIME 0
+
+#define ISCSI_NODE_INDEX 1
+
+#define ISPRINT(a) ((a >= ' ') && (a <= '~'))
+
+/****************************************************************************
+ * iSCSI MIB Tables
+ ****************************************************************************/
+/*
+ * Instance Attributes Table
+ */
+CONFIGFS_EATTR_STRUCT(iscsi_stat_instance, iscsi_wwn_stat_grps);
+#define ISCSI_STAT_INSTANCE_ATTR(_name, _mode) \
+static struct iscsi_stat_instance_attribute \
+ iscsi_stat_instance_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ iscsi_stat_instance_show_attr_##_name, \
+ iscsi_stat_instance_store_attr_##_name);
+
+#define ISCSI_STAT_INSTANCE_ATTR_RO(_name) \
+static struct iscsi_stat_instance_attribute \
+ iscsi_stat_instance_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ iscsi_stat_instance_show_attr_##_name);
+
+static ssize_t iscsi_stat_instance_show_attr_inst(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(inst);
+
+static ssize_t iscsi_stat_instance_show_attr_min_ver(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_DRAFT20_VERSION);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(min_ver);
+
+static ssize_t iscsi_stat_instance_show_attr_max_ver(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_DRAFT20_VERSION);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(max_ver);
+
+static ssize_t iscsi_stat_instance_show_attr_portals(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_num_tpg_nps);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(portals);
+
+static ssize_t iscsi_stat_instance_show_attr_nodes(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_INST_NUM_NODES);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(nodes);
+
+static ssize_t iscsi_stat_instance_show_attr_sessions(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_nsessions);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(sessions);
+
+static ssize_t iscsi_stat_instance_show_attr_fail_sess(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats;
+ u32 sess_err_count;
+
+ spin_lock_bh(&sess_err->lock);
+ sess_err_count = (sess_err->digest_errors +
+ sess_err->cxn_timeout_errors +
+ sess_err->pdu_format_errors);
+ spin_unlock_bh(&sess_err->lock);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", sess_err_count);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(fail_sess);
+
+static ssize_t iscsi_stat_instance_show_attr_fail_type(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats;
+
+ return snprintf(page, PAGE_SIZE, "%u\n",
+ sess_err->last_sess_failure_type);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(fail_type);
+
+static ssize_t iscsi_stat_instance_show_attr_fail_rem_name(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats;
+
+ return snprintf(page, PAGE_SIZE, "%s\n",
+ sess_err->last_sess_fail_rem_name[0] ?
+ sess_err->last_sess_fail_rem_name : NONE);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(fail_rem_name);
+
+static ssize_t iscsi_stat_instance_show_attr_disc_time(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_DISCONTINUITY_TIME);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(disc_time);
+
+static ssize_t iscsi_stat_instance_show_attr_description(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%s\n", ISCSI_INST_DESCR);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(description);
+
+static ssize_t iscsi_stat_instance_show_attr_vendor(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "Datera, Inc. iSCSI-Target\n");
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(vendor);
+
+static ssize_t iscsi_stat_instance_show_attr_version(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%s\n", ISCSIT_VERSION);
+}
+ISCSI_STAT_INSTANCE_ATTR_RO(version);
+
+CONFIGFS_EATTR_OPS(iscsi_stat_instance, iscsi_wwn_stat_grps,
+ iscsi_instance_group);
+
+static struct configfs_attribute *iscsi_stat_instance_attrs[] = {
+ &iscsi_stat_instance_inst.attr,
+ &iscsi_stat_instance_min_ver.attr,
+ &iscsi_stat_instance_max_ver.attr,
+ &iscsi_stat_instance_portals.attr,
+ &iscsi_stat_instance_nodes.attr,
+ &iscsi_stat_instance_sessions.attr,
+ &iscsi_stat_instance_fail_sess.attr,
+ &iscsi_stat_instance_fail_type.attr,
+ &iscsi_stat_instance_fail_rem_name.attr,
+ &iscsi_stat_instance_disc_time.attr,
+ &iscsi_stat_instance_description.attr,
+ &iscsi_stat_instance_vendor.attr,
+ &iscsi_stat_instance_version.attr,
+ NULL,
+};
+
+static struct configfs_item_operations iscsi_stat_instance_item_ops = {
+ .show_attribute = iscsi_stat_instance_attr_show,
+ .store_attribute = iscsi_stat_instance_attr_store,
+};
+
+struct config_item_type iscsi_stat_instance_cit = {
+ .ct_item_ops = &iscsi_stat_instance_item_ops,
+ .ct_attrs = iscsi_stat_instance_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Instance Session Failure Stats Table
+ */
+CONFIGFS_EATTR_STRUCT(iscsi_stat_sess_err, iscsi_wwn_stat_grps);
+#define ISCSI_STAT_SESS_ERR_ATTR(_name, _mode) \
+static struct iscsi_stat_sess_err_attribute \
+ iscsi_stat_sess_err_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ iscsi_stat_sess_err_show_attr_##_name, \
+ iscsi_stat_sess_err_store_attr_##_name);
+
+#define ISCSI_STAT_SESS_ERR_ATTR_RO(_name) \
+static struct iscsi_stat_sess_err_attribute \
+ iscsi_stat_sess_err_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ iscsi_stat_sess_err_show_attr_##_name);
+
+static ssize_t iscsi_stat_sess_err_show_attr_inst(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index);
+}
+ISCSI_STAT_SESS_ERR_ATTR_RO(inst);
+
+static ssize_t iscsi_stat_sess_err_show_attr_digest_errors(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", sess_err->digest_errors);
+}
+ISCSI_STAT_SESS_ERR_ATTR_RO(digest_errors);
+
+static ssize_t iscsi_stat_sess_err_show_attr_cxn_errors(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", sess_err->cxn_timeout_errors);
+}
+ISCSI_STAT_SESS_ERR_ATTR_RO(cxn_errors);
+
+static ssize_t iscsi_stat_sess_err_show_attr_format_errors(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", sess_err->pdu_format_errors);
+}
+ISCSI_STAT_SESS_ERR_ATTR_RO(format_errors);
+
+CONFIGFS_EATTR_OPS(iscsi_stat_sess_err, iscsi_wwn_stat_grps,
+ iscsi_sess_err_group);
+
+static struct configfs_attribute *iscsi_stat_sess_err_attrs[] = {
+ &iscsi_stat_sess_err_inst.attr,
+ &iscsi_stat_sess_err_digest_errors.attr,
+ &iscsi_stat_sess_err_cxn_errors.attr,
+ &iscsi_stat_sess_err_format_errors.attr,
+ NULL,
+};
+
+static struct configfs_item_operations iscsi_stat_sess_err_item_ops = {
+ .show_attribute = iscsi_stat_sess_err_attr_show,
+ .store_attribute = iscsi_stat_sess_err_attr_store,
+};
+
+struct config_item_type iscsi_stat_sess_err_cit = {
+ .ct_item_ops = &iscsi_stat_sess_err_item_ops,
+ .ct_attrs = iscsi_stat_sess_err_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Target Attributes Table
+ */
+CONFIGFS_EATTR_STRUCT(iscsi_stat_tgt_attr, iscsi_wwn_stat_grps);
+#define ISCSI_STAT_TGT_ATTR(_name, _mode) \
+static struct iscsi_stat_tgt_attr_attribute \
+ iscsi_stat_tgt_attr_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ iscsi_stat_tgt-attr_show_attr_##_name, \
+ iscsi_stat_tgt_attr_store_attr_##_name);
+
+#define ISCSI_STAT_TGT_ATTR_RO(_name) \
+static struct iscsi_stat_tgt_attr_attribute \
+ iscsi_stat_tgt_attr_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ iscsi_stat_tgt_attr_show_attr_##_name);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_inst(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index);
+}
+ISCSI_STAT_TGT_ATTR_RO(inst);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_indx(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_NODE_INDEX);
+}
+ISCSI_STAT_TGT_ATTR_RO(indx);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_login_fails(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_login_stats *lstat = &tiqn->login_stats;
+ u32 fail_count;
+
+ spin_lock(&lstat->lock);
+ fail_count = (lstat->redirects + lstat->authorize_fails +
+ lstat->authenticate_fails + lstat->negotiate_fails +
+ lstat->other_fails);
+ spin_unlock(&lstat->lock);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", fail_count);
+}
+ISCSI_STAT_TGT_ATTR_RO(login_fails);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_last_fail_time(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_login_stats *lstat = &tiqn->login_stats;
+ u32 last_fail_time;
+
+ spin_lock(&lstat->lock);
+ last_fail_time = lstat->last_fail_time ?
+ (u32)(((u32)lstat->last_fail_time -
+ INITIAL_JIFFIES) * 100 / HZ) : 0;
+ spin_unlock(&lstat->lock);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", last_fail_time);
+}
+ISCSI_STAT_TGT_ATTR_RO(last_fail_time);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_last_fail_type(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_login_stats *lstat = &tiqn->login_stats;
+ u32 last_fail_type;
+
+ spin_lock(&lstat->lock);
+ last_fail_type = lstat->last_fail_type;
+ spin_unlock(&lstat->lock);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", last_fail_type);
+}
+ISCSI_STAT_TGT_ATTR_RO(last_fail_type);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_name(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_login_stats *lstat = &tiqn->login_stats;
+ unsigned char buf[224];
+
+ spin_lock(&lstat->lock);
+ snprintf(buf, 224, "%s", lstat->last_intr_fail_name[0] ?
+ lstat->last_intr_fail_name : NONE);
+ spin_unlock(&lstat->lock);
+
+ return snprintf(page, PAGE_SIZE, "%s\n", buf);
+}
+ISCSI_STAT_TGT_ATTR_RO(fail_intr_name);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_addr_type(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_login_stats *lstat = &tiqn->login_stats;
+ int ret;
+
+ spin_lock(&lstat->lock);
+ if (lstat->last_intr_fail_ip_family == AF_INET6)
+ ret = snprintf(page, PAGE_SIZE, "ipv6\n");
+ else
+ ret = snprintf(page, PAGE_SIZE, "ipv4\n");
+ spin_unlock(&lstat->lock);
+
+ return ret;
+}
+ISCSI_STAT_TGT_ATTR_RO(fail_intr_addr_type);
+
+static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_addr(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_login_stats *lstat = &tiqn->login_stats;
+ int ret;
+
+ spin_lock(&lstat->lock);
+ ret = snprintf(page, PAGE_SIZE, "%s\n", lstat->last_intr_fail_ip_addr);
+ spin_unlock(&lstat->lock);
+
+ return ret;
+}
+ISCSI_STAT_TGT_ATTR_RO(fail_intr_addr);
+
+CONFIGFS_EATTR_OPS(iscsi_stat_tgt_attr, iscsi_wwn_stat_grps,
+ iscsi_tgt_attr_group);
+
+static struct configfs_attribute *iscsi_stat_tgt_attr_attrs[] = {
+ &iscsi_stat_tgt_attr_inst.attr,
+ &iscsi_stat_tgt_attr_indx.attr,
+ &iscsi_stat_tgt_attr_login_fails.attr,
+ &iscsi_stat_tgt_attr_last_fail_time.attr,
+ &iscsi_stat_tgt_attr_last_fail_type.attr,
+ &iscsi_stat_tgt_attr_fail_intr_name.attr,
+ &iscsi_stat_tgt_attr_fail_intr_addr_type.attr,
+ &iscsi_stat_tgt_attr_fail_intr_addr.attr,
+ NULL,
+};
+
+static struct configfs_item_operations iscsi_stat_tgt_attr_item_ops = {
+ .show_attribute = iscsi_stat_tgt_attr_attr_show,
+ .store_attribute = iscsi_stat_tgt_attr_attr_store,
+};
+
+struct config_item_type iscsi_stat_tgt_attr_cit = {
+ .ct_item_ops = &iscsi_stat_tgt_attr_item_ops,
+ .ct_attrs = iscsi_stat_tgt_attr_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Target Login Stats Table
+ */
+CONFIGFS_EATTR_STRUCT(iscsi_stat_login, iscsi_wwn_stat_grps);
+#define ISCSI_STAT_LOGIN(_name, _mode) \
+static struct iscsi_stat_login_attribute \
+ iscsi_stat_login_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ iscsi_stat_login_show_attr_##_name, \
+ iscsi_stat_login_store_attr_##_name);
+
+#define ISCSI_STAT_LOGIN_RO(_name) \
+static struct iscsi_stat_login_attribute \
+ iscsi_stat_login_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ iscsi_stat_login_show_attr_##_name);
+
+static ssize_t iscsi_stat_login_show_attr_inst(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index);
+}
+ISCSI_STAT_LOGIN_RO(inst);
+
+static ssize_t iscsi_stat_login_show_attr_indx(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_NODE_INDEX);
+}
+ISCSI_STAT_LOGIN_RO(indx);
+
+static ssize_t iscsi_stat_login_show_attr_accepts(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_login_stats *lstat = &tiqn->login_stats;
+ ssize_t ret;
+
+ spin_lock(&lstat->lock);
+ ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->accepts);
+ spin_unlock(&lstat->lock);
+
+ return ret;
+}
+ISCSI_STAT_LOGIN_RO(accepts);
+
+static ssize_t iscsi_stat_login_show_attr_other_fails(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_login_stats *lstat = &tiqn->login_stats;
+ ssize_t ret;
+
+ spin_lock(&lstat->lock);
+ ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->other_fails);
+ spin_unlock(&lstat->lock);
+
+ return ret;
+}
+ISCSI_STAT_LOGIN_RO(other_fails);
+
+static ssize_t iscsi_stat_login_show_attr_redirects(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_login_stats *lstat = &tiqn->login_stats;
+ ssize_t ret;
+
+ spin_lock(&lstat->lock);
+ ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->redirects);
+ spin_unlock(&lstat->lock);
+
+ return ret;
+}
+ISCSI_STAT_LOGIN_RO(redirects);
+
+static ssize_t iscsi_stat_login_show_attr_authorize_fails(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_login_stats *lstat = &tiqn->login_stats;
+ ssize_t ret;
+
+ spin_lock(&lstat->lock);
+ ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->authorize_fails);
+ spin_unlock(&lstat->lock);
+
+ return ret;
+}
+ISCSI_STAT_LOGIN_RO(authorize_fails);
+
+static ssize_t iscsi_stat_login_show_attr_authenticate_fails(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_login_stats *lstat = &tiqn->login_stats;
+ ssize_t ret;
+
+ spin_lock(&lstat->lock);
+ ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->authenticate_fails);
+ spin_unlock(&lstat->lock);
+
+ return ret;
+}
+ISCSI_STAT_LOGIN_RO(authenticate_fails);
+
+static ssize_t iscsi_stat_login_show_attr_negotiate_fails(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_login_stats *lstat = &tiqn->login_stats;
+ ssize_t ret;
+
+ spin_lock(&lstat->lock);
+ ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->negotiate_fails);
+ spin_unlock(&lstat->lock);
+
+ return ret;
+}
+ISCSI_STAT_LOGIN_RO(negotiate_fails);
+
+CONFIGFS_EATTR_OPS(iscsi_stat_login, iscsi_wwn_stat_grps,
+ iscsi_login_stats_group);
+
+static struct configfs_attribute *iscsi_stat_login_stats_attrs[] = {
+ &iscsi_stat_login_inst.attr,
+ &iscsi_stat_login_indx.attr,
+ &iscsi_stat_login_accepts.attr,
+ &iscsi_stat_login_other_fails.attr,
+ &iscsi_stat_login_redirects.attr,
+ &iscsi_stat_login_authorize_fails.attr,
+ &iscsi_stat_login_authenticate_fails.attr,
+ &iscsi_stat_login_negotiate_fails.attr,
+ NULL,
+};
+
+static struct configfs_item_operations iscsi_stat_login_stats_item_ops = {
+ .show_attribute = iscsi_stat_login_attr_show,
+ .store_attribute = iscsi_stat_login_attr_store,
+};
+
+struct config_item_type iscsi_stat_login_cit = {
+ .ct_item_ops = &iscsi_stat_login_stats_item_ops,
+ .ct_attrs = iscsi_stat_login_stats_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Target Logout Stats Table
+ */
+
+CONFIGFS_EATTR_STRUCT(iscsi_stat_logout, iscsi_wwn_stat_grps);
+#define ISCSI_STAT_LOGOUT(_name, _mode) \
+static struct iscsi_stat_logout_attribute \
+ iscsi_stat_logout_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ iscsi_stat_logout_show_attr_##_name, \
+ iscsi_stat_logout_store_attr_##_name);
+
+#define ISCSI_STAT_LOGOUT_RO(_name) \
+static struct iscsi_stat_logout_attribute \
+ iscsi_stat_logout_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ iscsi_stat_logout_show_attr_##_name);
+
+static ssize_t iscsi_stat_logout_show_attr_inst(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index);
+}
+ISCSI_STAT_LOGOUT_RO(inst);
+
+static ssize_t iscsi_stat_logout_show_attr_indx(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_NODE_INDEX);
+}
+ISCSI_STAT_LOGOUT_RO(indx);
+
+static ssize_t iscsi_stat_logout_show_attr_normal_logouts(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_logout_stats *lstats = &tiqn->logout_stats;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", lstats->normal_logouts);
+}
+ISCSI_STAT_LOGOUT_RO(normal_logouts);
+
+static ssize_t iscsi_stat_logout_show_attr_abnormal_logouts(
+ struct iscsi_wwn_stat_grps *igrps, char *page)
+{
+ struct iscsi_tiqn *tiqn = container_of(igrps,
+ struct iscsi_tiqn, tiqn_stat_grps);
+ struct iscsi_logout_stats *lstats = &tiqn->logout_stats;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", lstats->abnormal_logouts);
+}
+ISCSI_STAT_LOGOUT_RO(abnormal_logouts);
+
+CONFIGFS_EATTR_OPS(iscsi_stat_logout, iscsi_wwn_stat_grps,
+ iscsi_logout_stats_group);
+
+static struct configfs_attribute *iscsi_stat_logout_stats_attrs[] = {
+ &iscsi_stat_logout_inst.attr,
+ &iscsi_stat_logout_indx.attr,
+ &iscsi_stat_logout_normal_logouts.attr,
+ &iscsi_stat_logout_abnormal_logouts.attr,
+ NULL,
+};
+
+static struct configfs_item_operations iscsi_stat_logout_stats_item_ops = {
+ .show_attribute = iscsi_stat_logout_attr_show,
+ .store_attribute = iscsi_stat_logout_attr_store,
+};
+
+struct config_item_type iscsi_stat_logout_cit = {
+ .ct_item_ops = &iscsi_stat_logout_stats_item_ops,
+ .ct_attrs = iscsi_stat_logout_stats_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Session Stats Table
+ */
+
+CONFIGFS_EATTR_STRUCT(iscsi_stat_sess, iscsi_node_stat_grps);
+#define ISCSI_STAT_SESS(_name, _mode) \
+static struct iscsi_stat_sess_attribute \
+ iscsi_stat_sess_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ iscsi_stat_sess_show_attr_##_name, \
+ iscsi_stat_sess_store_attr_##_name);
+
+#define ISCSI_STAT_SESS_RO(_name) \
+static struct iscsi_stat_sess_attribute \
+ iscsi_stat_sess_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ iscsi_stat_sess_show_attr_##_name);
+
+static ssize_t iscsi_stat_sess_show_attr_inst(
+ struct iscsi_node_stat_grps *igrps, char *page)
+{
+ struct iscsi_node_acl *acl = container_of(igrps,
+ struct iscsi_node_acl, node_stat_grps);
+ struct se_wwn *wwn = acl->se_node_acl.se_tpg->se_tpg_wwn;
+ struct iscsi_tiqn *tiqn = container_of(wwn,
+ struct iscsi_tiqn, tiqn_wwn);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index);
+}
+ISCSI_STAT_SESS_RO(inst);
+
+static ssize_t iscsi_stat_sess_show_attr_node(
+ struct iscsi_node_stat_grps *igrps, char *page)
+{
+ struct iscsi_node_acl *acl = container_of(igrps,
+ struct iscsi_node_acl, node_stat_grps);
+ struct se_node_acl *se_nacl = &acl->se_node_acl;
+ struct iscsi_session *sess;
+ struct se_session *se_sess;
+ ssize_t ret = 0;
+
+ spin_lock_bh(&se_nacl->nacl_sess_lock);
+ se_sess = se_nacl->nacl_sess;
+ if (se_sess) {
+ sess = se_sess->fabric_sess_ptr;
+ if (sess)
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ sess->sess_ops->SessionType ? 0 : ISCSI_NODE_INDEX);
+ }
+ spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+ return ret;
+}
+ISCSI_STAT_SESS_RO(node);
+
+static ssize_t iscsi_stat_sess_show_attr_indx(
+ struct iscsi_node_stat_grps *igrps, char *page)
+{
+ struct iscsi_node_acl *acl = container_of(igrps,
+ struct iscsi_node_acl, node_stat_grps);
+ struct se_node_acl *se_nacl = &acl->se_node_acl;
+ struct iscsi_session *sess;
+ struct se_session *se_sess;
+ ssize_t ret = 0;
+
+ spin_lock_bh(&se_nacl->nacl_sess_lock);
+ se_sess = se_nacl->nacl_sess;
+ if (se_sess) {
+ sess = se_sess->fabric_sess_ptr;
+ if (sess)
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ sess->session_index);
+ }
+ spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+ return ret;
+}
+ISCSI_STAT_SESS_RO(indx);
+
+static ssize_t iscsi_stat_sess_show_attr_cmd_pdus(
+ struct iscsi_node_stat_grps *igrps, char *page)
+{
+ struct iscsi_node_acl *acl = container_of(igrps,
+ struct iscsi_node_acl, node_stat_grps);
+ struct se_node_acl *se_nacl = &acl->se_node_acl;
+ struct iscsi_session *sess;
+ struct se_session *se_sess;
+ ssize_t ret = 0;
+
+ spin_lock_bh(&se_nacl->nacl_sess_lock);
+ se_sess = se_nacl->nacl_sess;
+ if (se_sess) {
+ sess = se_sess->fabric_sess_ptr;
+ if (sess)
+ ret = snprintf(page, PAGE_SIZE, "%lu\n",
+ atomic_long_read(&sess->cmd_pdus));
+ }
+ spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+ return ret;
+}
+ISCSI_STAT_SESS_RO(cmd_pdus);
+
+static ssize_t iscsi_stat_sess_show_attr_rsp_pdus(
+ struct iscsi_node_stat_grps *igrps, char *page)
+{
+ struct iscsi_node_acl *acl = container_of(igrps,
+ struct iscsi_node_acl, node_stat_grps);
+ struct se_node_acl *se_nacl = &acl->se_node_acl;
+ struct iscsi_session *sess;
+ struct se_session *se_sess;
+ ssize_t ret = 0;
+
+ spin_lock_bh(&se_nacl->nacl_sess_lock);
+ se_sess = se_nacl->nacl_sess;
+ if (se_sess) {
+ sess = se_sess->fabric_sess_ptr;
+ if (sess)
+ ret = snprintf(page, PAGE_SIZE, "%lu\n",
+ atomic_long_read(&sess->rsp_pdus));
+ }
+ spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+ return ret;
+}
+ISCSI_STAT_SESS_RO(rsp_pdus);
+
+static ssize_t iscsi_stat_sess_show_attr_txdata_octs(
+ struct iscsi_node_stat_grps *igrps, char *page)
+{
+ struct iscsi_node_acl *acl = container_of(igrps,
+ struct iscsi_node_acl, node_stat_grps);
+ struct se_node_acl *se_nacl = &acl->se_node_acl;
+ struct iscsi_session *sess;
+ struct se_session *se_sess;
+ ssize_t ret = 0;
+
+ spin_lock_bh(&se_nacl->nacl_sess_lock);
+ se_sess = se_nacl->nacl_sess;
+ if (se_sess) {
+ sess = se_sess->fabric_sess_ptr;
+ if (sess)
+ ret = snprintf(page, PAGE_SIZE, "%lu\n",
+ atomic_long_read(&sess->tx_data_octets));
+ }
+ spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+ return ret;
+}
+ISCSI_STAT_SESS_RO(txdata_octs);
+
+static ssize_t iscsi_stat_sess_show_attr_rxdata_octs(
+ struct iscsi_node_stat_grps *igrps, char *page)
+{
+ struct iscsi_node_acl *acl = container_of(igrps,
+ struct iscsi_node_acl, node_stat_grps);
+ struct se_node_acl *se_nacl = &acl->se_node_acl;
+ struct iscsi_session *sess;
+ struct se_session *se_sess;
+ ssize_t ret = 0;
+
+ spin_lock_bh(&se_nacl->nacl_sess_lock);
+ se_sess = se_nacl->nacl_sess;
+ if (se_sess) {
+ sess = se_sess->fabric_sess_ptr;
+ if (sess)
+ ret = snprintf(page, PAGE_SIZE, "%lu\n",
+ atomic_long_read(&sess->rx_data_octets));
+ }
+ spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+ return ret;
+}
+ISCSI_STAT_SESS_RO(rxdata_octs);
+
+static ssize_t iscsi_stat_sess_show_attr_conn_digest_errors(
+ struct iscsi_node_stat_grps *igrps, char *page)
+{
+ struct iscsi_node_acl *acl = container_of(igrps,
+ struct iscsi_node_acl, node_stat_grps);
+ struct se_node_acl *se_nacl = &acl->se_node_acl;
+ struct iscsi_session *sess;
+ struct se_session *se_sess;
+ ssize_t ret = 0;
+
+ spin_lock_bh(&se_nacl->nacl_sess_lock);
+ se_sess = se_nacl->nacl_sess;
+ if (se_sess) {
+ sess = se_sess->fabric_sess_ptr;
+ if (sess)
+ ret = snprintf(page, PAGE_SIZE, "%lu\n",
+ atomic_long_read(&sess->conn_digest_errors));
+ }
+ spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+ return ret;
+}
+ISCSI_STAT_SESS_RO(conn_digest_errors);
+
+static ssize_t iscsi_stat_sess_show_attr_conn_timeout_errors(
+ struct iscsi_node_stat_grps *igrps, char *page)
+{
+ struct iscsi_node_acl *acl = container_of(igrps,
+ struct iscsi_node_acl, node_stat_grps);
+ struct se_node_acl *se_nacl = &acl->se_node_acl;
+ struct iscsi_session *sess;
+ struct se_session *se_sess;
+ ssize_t ret = 0;
+
+ spin_lock_bh(&se_nacl->nacl_sess_lock);
+ se_sess = se_nacl->nacl_sess;
+ if (se_sess) {
+ sess = se_sess->fabric_sess_ptr;
+ if (sess)
+ ret = snprintf(page, PAGE_SIZE, "%lu\n",
+ atomic_long_read(&sess->conn_timeout_errors));
+ }
+ spin_unlock_bh(&se_nacl->nacl_sess_lock);
+
+ return ret;
+}
+ISCSI_STAT_SESS_RO(conn_timeout_errors);
+
+CONFIGFS_EATTR_OPS(iscsi_stat_sess, iscsi_node_stat_grps,
+ iscsi_sess_stats_group);
+
+static struct configfs_attribute *iscsi_stat_sess_stats_attrs[] = {
+ &iscsi_stat_sess_inst.attr,
+ &iscsi_stat_sess_node.attr,
+ &iscsi_stat_sess_indx.attr,
+ &iscsi_stat_sess_cmd_pdus.attr,
+ &iscsi_stat_sess_rsp_pdus.attr,
+ &iscsi_stat_sess_txdata_octs.attr,
+ &iscsi_stat_sess_rxdata_octs.attr,
+ &iscsi_stat_sess_conn_digest_errors.attr,
+ &iscsi_stat_sess_conn_timeout_errors.attr,
+ NULL,
+};
+
+static struct configfs_item_operations iscsi_stat_sess_stats_item_ops = {
+ .show_attribute = iscsi_stat_sess_attr_show,
+ .store_attribute = iscsi_stat_sess_attr_store,
+};
+
+struct config_item_type iscsi_stat_sess_cit = {
+ .ct_item_ops = &iscsi_stat_sess_stats_item_ops,
+ .ct_attrs = iscsi_stat_sess_stats_attrs,
+ .ct_owner = THIS_MODULE,
+};
diff --git a/drivers/target/iscsi/iscsi_target_stat.h b/drivers/target/iscsi/iscsi_target_stat.h
new file mode 100644
index 00000000000..3ff76b4faad
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_stat.h
@@ -0,0 +1,64 @@
+#ifndef ISCSI_TARGET_STAT_H
+#define ISCSI_TARGET_STAT_H
+
+/*
+ * For struct iscsi_tiqn->tiqn_wwn default groups
+ */
+extern struct config_item_type iscsi_stat_instance_cit;
+extern struct config_item_type iscsi_stat_sess_err_cit;
+extern struct config_item_type iscsi_stat_tgt_attr_cit;
+extern struct config_item_type iscsi_stat_login_cit;
+extern struct config_item_type iscsi_stat_logout_cit;
+
+/*
+ * For struct iscsi_session->se_sess default groups
+ */
+extern struct config_item_type iscsi_stat_sess_cit;
+
+/* iSCSI session error types */
+#define ISCSI_SESS_ERR_UNKNOWN 0
+#define ISCSI_SESS_ERR_DIGEST 1
+#define ISCSI_SESS_ERR_CXN_TIMEOUT 2
+#define ISCSI_SESS_ERR_PDU_FORMAT 3
+
+/* iSCSI session error stats */
+struct iscsi_sess_err_stats {
+ spinlock_t lock;
+ u32 digest_errors;
+ u32 cxn_timeout_errors;
+ u32 pdu_format_errors;
+ u32 last_sess_failure_type;
+ char last_sess_fail_rem_name[224];
+} ____cacheline_aligned;
+
+/* iSCSI login failure types (sub oids) */
+#define ISCSI_LOGIN_FAIL_OTHER 2
+#define ISCSI_LOGIN_FAIL_REDIRECT 3
+#define ISCSI_LOGIN_FAIL_AUTHORIZE 4
+#define ISCSI_LOGIN_FAIL_AUTHENTICATE 5
+#define ISCSI_LOGIN_FAIL_NEGOTIATE 6
+
+/* iSCSI login stats */
+struct iscsi_login_stats {
+ spinlock_t lock;
+ u32 accepts;
+ u32 other_fails;
+ u32 redirects;
+ u32 authorize_fails;
+ u32 authenticate_fails;
+ u32 negotiate_fails; /* used for notifications */
+ u64 last_fail_time; /* time stamp (jiffies) */
+ u32 last_fail_type;
+ int last_intr_fail_ip_family;
+ unsigned char last_intr_fail_ip_addr[IPV6_ADDRESS_SPACE];
+ char last_intr_fail_name[224];
+} ____cacheline_aligned;
+
+/* iSCSI logout stats */
+struct iscsi_logout_stats {
+ spinlock_t lock;
+ u32 normal_logouts;
+ u32 abnormal_logouts;
+} ____cacheline_aligned;
+
+#endif /*** ISCSI_TARGET_STAT_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_tmr.c b/drivers/target/iscsi/iscsi_target_tmr.c
new file mode 100644
index 00000000000..78404b1cc0b
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_tmr.c
@@ -0,0 +1,849 @@
+/*******************************************************************************
+ * This file contains the iSCSI Target specific Task Management functions.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <asm/unaligned.h>
+#include <scsi/scsi_device.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/iscsi/iscsi_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_seq_pdu_list.h"
+#include "iscsi_target_datain_values.h"
+#include "iscsi_target_device.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target_tmr.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+
+u8 iscsit_tmr_abort_task(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ struct iscsi_cmd *ref_cmd;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_tmr_req *tmr_req = cmd->tmr_req;
+ struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req;
+ struct iscsi_tm *hdr = (struct iscsi_tm *) buf;
+
+ ref_cmd = iscsit_find_cmd_from_itt(conn, hdr->rtt);
+ if (!ref_cmd) {
+ pr_err("Unable to locate RefTaskTag: 0x%08x on CID:"
+ " %hu.\n", hdr->rtt, conn->cid);
+ return (iscsi_sna_gte(be32_to_cpu(hdr->refcmdsn), conn->sess->exp_cmd_sn) &&
+ iscsi_sna_lte(be32_to_cpu(hdr->refcmdsn), conn->sess->max_cmd_sn)) ?
+ ISCSI_TMF_RSP_COMPLETE : ISCSI_TMF_RSP_NO_TASK;
+ }
+ if (ref_cmd->cmd_sn != be32_to_cpu(hdr->refcmdsn)) {
+ pr_err("RefCmdSN 0x%08x does not equal"
+ " task's CmdSN 0x%08x. Rejecting ABORT_TASK.\n",
+ hdr->refcmdsn, ref_cmd->cmd_sn);
+ return ISCSI_TMF_RSP_REJECTED;
+ }
+
+ se_tmr->ref_task_tag = (__force u32)hdr->rtt;
+ tmr_req->ref_cmd = ref_cmd;
+ tmr_req->exp_data_sn = be32_to_cpu(hdr->exp_datasn);
+
+ return ISCSI_TMF_RSP_COMPLETE;
+}
+
+/*
+ * Called from iscsit_handle_task_mgt_cmd().
+ */
+int iscsit_tmr_task_warm_reset(
+ struct iscsi_conn *conn,
+ struct iscsi_tmr_req *tmr_req,
+ unsigned char *buf)
+{
+ struct iscsi_session *sess = conn->sess;
+ struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+
+ if (!na->tmr_warm_reset) {
+ pr_err("TMR Opcode TARGET_WARM_RESET authorization"
+ " failed for Initiator Node: %s\n",
+ sess->se_sess->se_node_acl->initiatorname);
+ return -1;
+ }
+ /*
+ * Do the real work in transport_generic_do_tmr().
+ */
+ return 0;
+}
+
+int iscsit_tmr_task_cold_reset(
+ struct iscsi_conn *conn,
+ struct iscsi_tmr_req *tmr_req,
+ unsigned char *buf)
+{
+ struct iscsi_session *sess = conn->sess;
+ struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+
+ if (!na->tmr_cold_reset) {
+ pr_err("TMR Opcode TARGET_COLD_RESET authorization"
+ " failed for Initiator Node: %s\n",
+ sess->se_sess->se_node_acl->initiatorname);
+ return -1;
+ }
+ /*
+ * Do the real work in transport_generic_do_tmr().
+ */
+ return 0;
+}
+
+u8 iscsit_tmr_task_reassign(
+ struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ struct iscsi_cmd *ref_cmd = NULL;
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_conn_recovery *cr = NULL;
+ struct iscsi_tmr_req *tmr_req = cmd->tmr_req;
+ struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req;
+ struct iscsi_tm *hdr = (struct iscsi_tm *) buf;
+ int ret, ref_lun;
+
+ pr_debug("Got TASK_REASSIGN TMR ITT: 0x%08x,"
+ " RefTaskTag: 0x%08x, ExpDataSN: 0x%08x, CID: %hu\n",
+ hdr->itt, hdr->rtt, hdr->exp_datasn, conn->cid);
+
+ if (conn->sess->sess_ops->ErrorRecoveryLevel != 2) {
+ pr_err("TMR TASK_REASSIGN not supported in ERL<2,"
+ " ignoring request.\n");
+ return ISCSI_TMF_RSP_NOT_SUPPORTED;
+ }
+
+ ret = iscsit_find_cmd_for_recovery(conn->sess, &ref_cmd, &cr, hdr->rtt);
+ if (ret == -2) {
+ pr_err("Command ITT: 0x%08x is still alligent to CID:"
+ " %hu\n", ref_cmd->init_task_tag, cr->cid);
+ return ISCSI_TMF_RSP_TASK_ALLEGIANT;
+ } else if (ret == -1) {
+ pr_err("Unable to locate RefTaskTag: 0x%08x in"
+ " connection recovery command list.\n", hdr->rtt);
+ return ISCSI_TMF_RSP_NO_TASK;
+ }
+ /*
+ * Temporary check to prevent connection recovery for
+ * connections with a differing Max*DataSegmentLength.
+ */
+ if (cr->maxrecvdatasegmentlength !=
+ conn->conn_ops->MaxRecvDataSegmentLength) {
+ pr_err("Unable to perform connection recovery for"
+ " differing MaxRecvDataSegmentLength, rejecting"
+ " TMR TASK_REASSIGN.\n");
+ return ISCSI_TMF_RSP_REJECTED;
+ }
+ if (cr->maxxmitdatasegmentlength !=
+ conn->conn_ops->MaxXmitDataSegmentLength) {
+ pr_err("Unable to perform connection recovery for"
+ " differing MaxXmitDataSegmentLength, rejecting"
+ " TMR TASK_REASSIGN.\n");
+ return ISCSI_TMF_RSP_REJECTED;
+ }
+
+ ref_lun = scsilun_to_int(&hdr->lun);
+ if (ref_lun != ref_cmd->se_cmd.orig_fe_lun) {
+ pr_err("Unable to perform connection recovery for"
+ " differing ref_lun: %d ref_cmd orig_fe_lun: %u\n",
+ ref_lun, ref_cmd->se_cmd.orig_fe_lun);
+ return ISCSI_TMF_RSP_REJECTED;
+ }
+
+ se_tmr->ref_task_tag = (__force u32)hdr->rtt;
+ tmr_req->ref_cmd = ref_cmd;
+ tmr_req->exp_data_sn = be32_to_cpu(hdr->exp_datasn);
+ tmr_req->conn_recovery = cr;
+ tmr_req->task_reassign = 1;
+ /*
+ * Command can now be reassigned to a new connection.
+ * The task management response must be sent before the
+ * reassignment actually happens. See iscsi_tmr_post_handler().
+ */
+ return ISCSI_TMF_RSP_COMPLETE;
+}
+
+static void iscsit_task_reassign_remove_cmd(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn_recovery *cr,
+ struct iscsi_session *sess)
+{
+ int ret;
+
+ spin_lock(&cr->conn_recovery_cmd_lock);
+ ret = iscsit_remove_cmd_from_connection_recovery(cmd, sess);
+ spin_unlock(&cr->conn_recovery_cmd_lock);
+ if (!ret) {
+ pr_debug("iSCSI connection recovery successful for CID:"
+ " %hu on SID: %u\n", cr->cid, sess->sid);
+ iscsit_remove_active_connection_recovery_entry(cr, sess);
+ }
+}
+
+static int iscsit_task_reassign_complete_nop_out(
+ struct iscsi_tmr_req *tmr_req,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_cmd *cmd = tmr_req->ref_cmd;
+ struct iscsi_conn_recovery *cr;
+
+ if (!cmd->cr) {
+ pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x"
+ " is NULL!\n", cmd->init_task_tag);
+ return -1;
+ }
+ cr = cmd->cr;
+
+ /*
+ * Reset the StatSN so a new one for this commands new connection
+ * will be assigned.
+ * Reset the ExpStatSN as well so we may receive Status SNACKs.
+ */
+ cmd->stat_sn = cmd->exp_stat_sn = 0;
+
+ iscsit_task_reassign_remove_cmd(cmd, cr, conn->sess);
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ cmd->i_state = ISTATE_SEND_NOPIN;
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+ return 0;
+}
+
+static int iscsit_task_reassign_complete_write(
+ struct iscsi_cmd *cmd,
+ struct iscsi_tmr_req *tmr_req)
+{
+ int no_build_r2ts = 0;
+ u32 length = 0, offset = 0;
+ struct iscsi_conn *conn = cmd->conn;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ /*
+ * The Initiator must not send a R2T SNACK with a Begrun less than
+ * the TMR TASK_REASSIGN's ExpDataSN.
+ */
+ if (!tmr_req->exp_data_sn) {
+ cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK;
+ cmd->acked_data_sn = 0;
+ } else {
+ cmd->cmd_flags |= ICF_GOT_DATACK_SNACK;
+ cmd->acked_data_sn = (tmr_req->exp_data_sn - 1);
+ }
+
+ /*
+ * The TMR TASK_REASSIGN's ExpDataSN contains the next R2TSN the
+ * Initiator is expecting. The Target controls all WRITE operations
+ * so if we have received all DataOUT we can safety ignore Initiator.
+ */
+ if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) {
+ if (!(cmd->se_cmd.transport_state & CMD_T_SENT)) {
+ pr_debug("WRITE ITT: 0x%08x: t_state: %d"
+ " never sent to transport\n",
+ cmd->init_task_tag, cmd->se_cmd.t_state);
+ target_execute_cmd(se_cmd);
+ return 0;
+ }
+
+ cmd->i_state = ISTATE_SEND_STATUS;
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+ return 0;
+ }
+
+ /*
+ * Special case to deal with DataSequenceInOrder=No and Non-Immeidate
+ * Unsolicited DataOut.
+ */
+ if (cmd->unsolicited_data) {
+ cmd->unsolicited_data = 0;
+
+ offset = cmd->next_burst_len = cmd->write_data_done;
+
+ if ((conn->sess->sess_ops->FirstBurstLength - offset) >=
+ cmd->se_cmd.data_length) {
+ no_build_r2ts = 1;
+ length = (cmd->se_cmd.data_length - offset);
+ } else
+ length = (conn->sess->sess_ops->FirstBurstLength - offset);
+
+ spin_lock_bh(&cmd->r2t_lock);
+ if (iscsit_add_r2t_to_list(cmd, offset, length, 0, 0) < 0) {
+ spin_unlock_bh(&cmd->r2t_lock);
+ return -1;
+ }
+ cmd->outstanding_r2ts++;
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ if (no_build_r2ts)
+ return 0;
+ }
+ /*
+ * iscsit_build_r2ts_for_cmd() can handle the rest from here.
+ */
+ return conn->conn_transport->iscsit_get_dataout(conn, cmd, true);
+}
+
+static int iscsit_task_reassign_complete_read(
+ struct iscsi_cmd *cmd,
+ struct iscsi_tmr_req *tmr_req)
+{
+ struct iscsi_conn *conn = cmd->conn;
+ struct iscsi_datain_req *dr;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ /*
+ * The Initiator must not send a Data SNACK with a BegRun less than
+ * the TMR TASK_REASSIGN's ExpDataSN.
+ */
+ if (!tmr_req->exp_data_sn) {
+ cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK;
+ cmd->acked_data_sn = 0;
+ } else {
+ cmd->cmd_flags |= ICF_GOT_DATACK_SNACK;
+ cmd->acked_data_sn = (tmr_req->exp_data_sn - 1);
+ }
+
+ if (!(cmd->se_cmd.transport_state & CMD_T_SENT)) {
+ pr_debug("READ ITT: 0x%08x: t_state: %d never sent to"
+ " transport\n", cmd->init_task_tag,
+ cmd->se_cmd.t_state);
+ transport_handle_cdb_direct(se_cmd);
+ return 0;
+ }
+
+ if (!(se_cmd->transport_state & CMD_T_COMPLETE)) {
+ pr_err("READ ITT: 0x%08x: t_state: %d, never returned"
+ " from transport\n", cmd->init_task_tag,
+ cmd->se_cmd.t_state);
+ return -1;
+ }
+
+ dr = iscsit_allocate_datain_req();
+ if (!dr)
+ return -1;
+ /*
+ * The TMR TASK_REASSIGN's ExpDataSN contains the next DataSN the
+ * Initiator is expecting.
+ */
+ dr->data_sn = dr->begrun = tmr_req->exp_data_sn;
+ dr->runlength = 0;
+ dr->generate_recovery_values = 1;
+ dr->recovery = DATAIN_CONNECTION_RECOVERY;
+
+ iscsit_attach_datain_req(cmd, dr);
+
+ cmd->i_state = ISTATE_SEND_DATAIN;
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+ return 0;
+}
+
+static int iscsit_task_reassign_complete_none(
+ struct iscsi_cmd *cmd,
+ struct iscsi_tmr_req *tmr_req)
+{
+ struct iscsi_conn *conn = cmd->conn;
+
+ cmd->i_state = ISTATE_SEND_STATUS;
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+ return 0;
+}
+
+static int iscsit_task_reassign_complete_scsi_cmnd(
+ struct iscsi_tmr_req *tmr_req,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_cmd *cmd = tmr_req->ref_cmd;
+ struct iscsi_conn_recovery *cr;
+
+ if (!cmd->cr) {
+ pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x"
+ " is NULL!\n", cmd->init_task_tag);
+ return -1;
+ }
+ cr = cmd->cr;
+
+ /*
+ * Reset the StatSN so a new one for this commands new connection
+ * will be assigned.
+ * Reset the ExpStatSN as well so we may receive Status SNACKs.
+ */
+ cmd->stat_sn = cmd->exp_stat_sn = 0;
+
+ iscsit_task_reassign_remove_cmd(cmd, cr, conn->sess);
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ if (cmd->se_cmd.se_cmd_flags & SCF_SENT_CHECK_CONDITION) {
+ cmd->i_state = ISTATE_SEND_STATUS;
+ iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+ return 0;
+ }
+
+ switch (cmd->data_direction) {
+ case DMA_TO_DEVICE:
+ return iscsit_task_reassign_complete_write(cmd, tmr_req);
+ case DMA_FROM_DEVICE:
+ return iscsit_task_reassign_complete_read(cmd, tmr_req);
+ case DMA_NONE:
+ return iscsit_task_reassign_complete_none(cmd, tmr_req);
+ default:
+ pr_err("Unknown cmd->data_direction: 0x%02x\n",
+ cmd->data_direction);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int iscsit_task_reassign_complete(
+ struct iscsi_tmr_req *tmr_req,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_cmd *cmd;
+ int ret = 0;
+
+ if (!tmr_req->ref_cmd) {
+ pr_err("TMR Request is missing a RefCmd struct iscsi_cmd.\n");
+ return -1;
+ }
+ cmd = tmr_req->ref_cmd;
+
+ cmd->conn = conn;
+
+ switch (cmd->iscsi_opcode) {
+ case ISCSI_OP_NOOP_OUT:
+ ret = iscsit_task_reassign_complete_nop_out(tmr_req, conn);
+ break;
+ case ISCSI_OP_SCSI_CMD:
+ ret = iscsit_task_reassign_complete_scsi_cmnd(tmr_req, conn);
+ break;
+ default:
+ pr_err("Illegal iSCSI Opcode 0x%02x during"
+ " command realligence\n", cmd->iscsi_opcode);
+ return -1;
+ }
+
+ if (ret != 0)
+ return ret;
+
+ pr_debug("Completed connection realligence for Opcode: 0x%02x,"
+ " ITT: 0x%08x to CID: %hu.\n", cmd->iscsi_opcode,
+ cmd->init_task_tag, conn->cid);
+
+ return 0;
+}
+
+/*
+ * Handles special after-the-fact actions related to TMRs.
+ * Right now the only one that its really needed for is
+ * connection recovery releated TASK_REASSIGN.
+ */
+int iscsit_tmr_post_handler(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+ struct iscsi_tmr_req *tmr_req = cmd->tmr_req;
+ struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req;
+
+ if (tmr_req->task_reassign &&
+ (se_tmr->response == ISCSI_TMF_RSP_COMPLETE))
+ return iscsit_task_reassign_complete(tmr_req, conn);
+
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_tmr_post_handler);
+
+/*
+ * Nothing to do here, but leave it for good measure. :-)
+ */
+static int iscsit_task_reassign_prepare_read(
+ struct iscsi_tmr_req *tmr_req,
+ struct iscsi_conn *conn)
+{
+ return 0;
+}
+
+static void iscsit_task_reassign_prepare_unsolicited_dataout(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn)
+{
+ int i, j;
+ struct iscsi_pdu *pdu = NULL;
+ struct iscsi_seq *seq = NULL;
+
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+ cmd->data_sn = 0;
+
+ if (cmd->immediate_data)
+ cmd->r2t_offset += (cmd->first_burst_len -
+ cmd->seq_start_offset);
+
+ if (conn->sess->sess_ops->DataPDUInOrder) {
+ cmd->write_data_done -= (cmd->immediate_data) ?
+ (cmd->first_burst_len -
+ cmd->seq_start_offset) :
+ cmd->first_burst_len;
+ cmd->first_burst_len = 0;
+ return;
+ }
+
+ for (i = 0; i < cmd->pdu_count; i++) {
+ pdu = &cmd->pdu_list[i];
+
+ if (pdu->status != ISCSI_PDU_RECEIVED_OK)
+ continue;
+
+ if ((pdu->offset >= cmd->seq_start_offset) &&
+ ((pdu->offset + pdu->length) <=
+ cmd->seq_end_offset)) {
+ cmd->first_burst_len -= pdu->length;
+ cmd->write_data_done -= pdu->length;
+ pdu->status = ISCSI_PDU_NOT_RECEIVED;
+ }
+ }
+ } else {
+ for (i = 0; i < cmd->seq_count; i++) {
+ seq = &cmd->seq_list[i];
+
+ if (seq->type != SEQTYPE_UNSOLICITED)
+ continue;
+
+ cmd->write_data_done -=
+ (seq->offset - seq->orig_offset);
+ cmd->first_burst_len = 0;
+ seq->data_sn = 0;
+ seq->offset = seq->orig_offset;
+ seq->next_burst_len = 0;
+ seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY;
+
+ if (conn->sess->sess_ops->DataPDUInOrder)
+ continue;
+
+ for (j = 0; j < seq->pdu_count; j++) {
+ pdu = &cmd->pdu_list[j+seq->pdu_start];
+
+ if (pdu->status != ISCSI_PDU_RECEIVED_OK)
+ continue;
+
+ pdu->status = ISCSI_PDU_NOT_RECEIVED;
+ }
+ }
+ }
+}
+
+static int iscsit_task_reassign_prepare_write(
+ struct iscsi_tmr_req *tmr_req,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_cmd *cmd = tmr_req->ref_cmd;
+ struct iscsi_pdu *pdu = NULL;
+ struct iscsi_r2t *r2t = NULL, *r2t_tmp;
+ int first_incomplete_r2t = 1, i = 0;
+
+ /*
+ * The command was in the process of receiving Unsolicited DataOUT when
+ * the connection failed.
+ */
+ if (cmd->unsolicited_data)
+ iscsit_task_reassign_prepare_unsolicited_dataout(cmd, conn);
+
+ /*
+ * The Initiator is requesting R2Ts starting from zero, skip
+ * checking acknowledged R2Ts and start checking struct iscsi_r2ts
+ * greater than zero.
+ */
+ if (!tmr_req->exp_data_sn)
+ goto drop_unacknowledged_r2ts;
+
+ /*
+ * We now check that the PDUs in DataOUT sequences below
+ * the TMR TASK_REASSIGN ExpDataSN (R2TSN the Initiator is
+ * expecting next) have all the DataOUT they require to complete
+ * the DataOUT sequence. First scan from R2TSN 0 to TMR
+ * TASK_REASSIGN ExpDataSN-1.
+ *
+ * If we have not received all DataOUT in question, we must
+ * make sure to make the appropriate changes to values in
+ * struct iscsi_cmd (and elsewhere depending on session parameters)
+ * so iscsit_build_r2ts_for_cmd() in iscsit_task_reassign_complete_write()
+ * will resend a new R2T for the DataOUT sequences in question.
+ */
+ spin_lock_bh(&cmd->r2t_lock);
+ if (list_empty(&cmd->cmd_r2t_list)) {
+ spin_unlock_bh(&cmd->r2t_lock);
+ return -1;
+ }
+
+ list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) {
+
+ if (r2t->r2t_sn >= tmr_req->exp_data_sn)
+ continue;
+ /*
+ * Safely ignore Recovery R2Ts and R2Ts that have completed
+ * DataOUT sequences.
+ */
+ if (r2t->seq_complete)
+ continue;
+
+ if (r2t->recovery_r2t)
+ continue;
+
+ /*
+ * DataSequenceInOrder=Yes:
+ *
+ * Taking into account the iSCSI implementation requirement of
+ * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and
+ * DataSequenceInOrder=Yes, we must take into consideration
+ * the following:
+ *
+ * DataSequenceInOrder=No:
+ *
+ * Taking into account that the Initiator controls the (possibly
+ * random) PDU Order in (possibly random) Sequence Order of
+ * DataOUT the target requests with R2Ts, we must take into
+ * consideration the following:
+ *
+ * DataPDUInOrder=Yes for DataSequenceInOrder=[Yes,No]:
+ *
+ * While processing non-complete R2T DataOUT sequence requests
+ * the Target will re-request only the total sequence length
+ * minus current received offset. This is because we must
+ * assume the initiator will continue sending DataOUT from the
+ * last PDU before the connection failed.
+ *
+ * DataPDUInOrder=No for DataSequenceInOrder=[Yes,No]:
+ *
+ * While processing non-complete R2T DataOUT sequence requests
+ * the Target will re-request the entire DataOUT sequence if
+ * any single PDU is missing from the sequence. This is because
+ * we have no logical method to determine the next PDU offset,
+ * and we must assume the Initiator will be sending any random
+ * PDU offset in the current sequence after TASK_REASSIGN
+ * has completed.
+ */
+ if (conn->sess->sess_ops->DataSequenceInOrder) {
+ if (!first_incomplete_r2t) {
+ cmd->r2t_offset -= r2t->xfer_len;
+ goto next;
+ }
+
+ if (conn->sess->sess_ops->DataPDUInOrder) {
+ cmd->data_sn = 0;
+ cmd->r2t_offset -= (r2t->xfer_len -
+ cmd->next_burst_len);
+ first_incomplete_r2t = 0;
+ goto next;
+ }
+
+ cmd->data_sn = 0;
+ cmd->r2t_offset -= r2t->xfer_len;
+
+ for (i = 0; i < cmd->pdu_count; i++) {
+ pdu = &cmd->pdu_list[i];
+
+ if (pdu->status != ISCSI_PDU_RECEIVED_OK)
+ continue;
+
+ if ((pdu->offset >= r2t->offset) &&
+ (pdu->offset < (r2t->offset +
+ r2t->xfer_len))) {
+ cmd->next_burst_len -= pdu->length;
+ cmd->write_data_done -= pdu->length;
+ pdu->status = ISCSI_PDU_NOT_RECEIVED;
+ }
+ }
+
+ first_incomplete_r2t = 0;
+ } else {
+ struct iscsi_seq *seq;
+
+ seq = iscsit_get_seq_holder(cmd, r2t->offset,
+ r2t->xfer_len);
+ if (!seq) {
+ spin_unlock_bh(&cmd->r2t_lock);
+ return -1;
+ }
+
+ cmd->write_data_done -=
+ (seq->offset - seq->orig_offset);
+ seq->data_sn = 0;
+ seq->offset = seq->orig_offset;
+ seq->next_burst_len = 0;
+ seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY;
+
+ cmd->seq_send_order--;
+
+ if (conn->sess->sess_ops->DataPDUInOrder)
+ goto next;
+
+ for (i = 0; i < seq->pdu_count; i++) {
+ pdu = &cmd->pdu_list[i+seq->pdu_start];
+
+ if (pdu->status != ISCSI_PDU_RECEIVED_OK)
+ continue;
+
+ pdu->status = ISCSI_PDU_NOT_RECEIVED;
+ }
+ }
+
+next:
+ cmd->outstanding_r2ts--;
+ }
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ /*
+ * We now drop all unacknowledged R2Ts, ie: ExpDataSN from TMR
+ * TASK_REASSIGN to the last R2T in the list.. We are also careful
+ * to check that the Initiator is not requesting R2Ts for DataOUT
+ * sequences it has already completed.
+ *
+ * Free each R2T in question and adjust values in struct iscsi_cmd
+ * accordingly so iscsit_build_r2ts_for_cmd() do the rest of
+ * the work after the TMR TASK_REASSIGN Response is sent.
+ */
+drop_unacknowledged_r2ts:
+
+ cmd->cmd_flags &= ~ICF_SENT_LAST_R2T;
+ cmd->r2t_sn = tmr_req->exp_data_sn;
+
+ spin_lock_bh(&cmd->r2t_lock);
+ list_for_each_entry_safe(r2t, r2t_tmp, &cmd->cmd_r2t_list, r2t_list) {
+ /*
+ * Skip up to the R2T Sequence number provided by the
+ * iSCSI TASK_REASSIGN TMR
+ */
+ if (r2t->r2t_sn < tmr_req->exp_data_sn)
+ continue;
+
+ if (r2t->seq_complete) {
+ pr_err("Initiator is requesting R2Ts from"
+ " R2TSN: 0x%08x, but R2TSN: 0x%08x, Offset: %u,"
+ " Length: %u is already complete."
+ " BAD INITIATOR ERL=2 IMPLEMENTATION!\n",
+ tmr_req->exp_data_sn, r2t->r2t_sn,
+ r2t->offset, r2t->xfer_len);
+ spin_unlock_bh(&cmd->r2t_lock);
+ return -1;
+ }
+
+ if (r2t->recovery_r2t) {
+ iscsit_free_r2t(r2t, cmd);
+ continue;
+ }
+
+ /* DataSequenceInOrder=Yes:
+ *
+ * Taking into account the iSCSI implementation requirement of
+ * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and
+ * DataSequenceInOrder=Yes, it's safe to subtract the R2Ts
+ * entire transfer length from the commands R2T offset marker.
+ *
+ * DataSequenceInOrder=No:
+ *
+ * We subtract the difference from struct iscsi_seq between the
+ * current offset and original offset from cmd->write_data_done
+ * for account for DataOUT PDUs already received. Then reset
+ * the current offset to the original and zero out the current
+ * burst length, to make sure we re-request the entire DataOUT
+ * sequence.
+ */
+ if (conn->sess->sess_ops->DataSequenceInOrder)
+ cmd->r2t_offset -= r2t->xfer_len;
+ else
+ cmd->seq_send_order--;
+
+ cmd->outstanding_r2ts--;
+ iscsit_free_r2t(r2t, cmd);
+ }
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ return 0;
+}
+
+/*
+ * Performs sanity checks TMR TASK_REASSIGN's ExpDataSN for
+ * a given struct iscsi_cmd.
+ */
+int iscsit_check_task_reassign_expdatasn(
+ struct iscsi_tmr_req *tmr_req,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_cmd *ref_cmd = tmr_req->ref_cmd;
+
+ if (ref_cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD)
+ return 0;
+
+ if (ref_cmd->se_cmd.se_cmd_flags & SCF_SENT_CHECK_CONDITION)
+ return 0;
+
+ if (ref_cmd->data_direction == DMA_NONE)
+ return 0;
+
+ /*
+ * For READs the TMR TASK_REASSIGNs ExpDataSN contains the next DataSN
+ * of DataIN the Initiator is expecting.
+ *
+ * Also check that the Initiator is not re-requesting DataIN that has
+ * already been acknowledged with a DataAck SNACK.
+ */
+ if (ref_cmd->data_direction == DMA_FROM_DEVICE) {
+ if (tmr_req->exp_data_sn > ref_cmd->data_sn) {
+ pr_err("Received ExpDataSN: 0x%08x for READ"
+ " in TMR TASK_REASSIGN greater than command's"
+ " DataSN: 0x%08x.\n", tmr_req->exp_data_sn,
+ ref_cmd->data_sn);
+ return -1;
+ }
+ if ((ref_cmd->cmd_flags & ICF_GOT_DATACK_SNACK) &&
+ (tmr_req->exp_data_sn <= ref_cmd->acked_data_sn)) {
+ pr_err("Received ExpDataSN: 0x%08x for READ"
+ " in TMR TASK_REASSIGN for previously"
+ " acknowledged DataIN: 0x%08x,"
+ " protocol error\n", tmr_req->exp_data_sn,
+ ref_cmd->acked_data_sn);
+ return -1;
+ }
+ return iscsit_task_reassign_prepare_read(tmr_req, conn);
+ }
+
+ /*
+ * For WRITEs the TMR TASK_REASSIGNs ExpDataSN contains the next R2TSN
+ * for R2Ts the Initiator is expecting.
+ *
+ * Do the magic in iscsit_task_reassign_prepare_write().
+ */
+ if (ref_cmd->data_direction == DMA_TO_DEVICE) {
+ if (tmr_req->exp_data_sn > ref_cmd->r2t_sn) {
+ pr_err("Received ExpDataSN: 0x%08x for WRITE"
+ " in TMR TASK_REASSIGN greater than command's"
+ " R2TSN: 0x%08x.\n", tmr_req->exp_data_sn,
+ ref_cmd->r2t_sn);
+ return -1;
+ }
+ return iscsit_task_reassign_prepare_write(tmr_req, conn);
+ }
+
+ pr_err("Unknown iSCSI data_direction: 0x%02x\n",
+ ref_cmd->data_direction);
+
+ return -1;
+}
diff --git a/drivers/target/iscsi/iscsi_target_tmr.h b/drivers/target/iscsi/iscsi_target_tmr.h
new file mode 100644
index 00000000000..142e992cb09
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_tmr.h
@@ -0,0 +1,14 @@
+#ifndef ISCSI_TARGET_TMR_H
+#define ISCSI_TARGET_TMR_H
+
+extern u8 iscsit_tmr_abort_task(struct iscsi_cmd *, unsigned char *);
+extern int iscsit_tmr_task_warm_reset(struct iscsi_conn *, struct iscsi_tmr_req *,
+ unsigned char *);
+extern int iscsit_tmr_task_cold_reset(struct iscsi_conn *, struct iscsi_tmr_req *,
+ unsigned char *);
+extern u8 iscsit_tmr_task_reassign(struct iscsi_cmd *, unsigned char *);
+extern int iscsit_tmr_post_handler(struct iscsi_cmd *, struct iscsi_conn *);
+extern int iscsit_check_task_reassign_expdatasn(struct iscsi_tmr_req *,
+ struct iscsi_conn *);
+
+#endif /* ISCSI_TARGET_TMR_H */
diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c
new file mode 100644
index 00000000000..c3cb5c15efd
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_tpg.c
@@ -0,0 +1,881 @@
+/*******************************************************************************
+ * This file contains iSCSI Target Portal Group related functions.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_login.h"
+#include "iscsi_target_nodeattrib.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+#include "iscsi_target_parameters.h"
+
+#include <target/iscsi/iscsi_transport.h>
+
+struct iscsi_portal_group *iscsit_alloc_portal_group(struct iscsi_tiqn *tiqn, u16 tpgt)
+{
+ struct iscsi_portal_group *tpg;
+
+ tpg = kzalloc(sizeof(struct iscsi_portal_group), GFP_KERNEL);
+ if (!tpg) {
+ pr_err("Unable to allocate struct iscsi_portal_group\n");
+ return NULL;
+ }
+
+ tpg->tpgt = tpgt;
+ tpg->tpg_state = TPG_STATE_FREE;
+ tpg->tpg_tiqn = tiqn;
+ INIT_LIST_HEAD(&tpg->tpg_gnp_list);
+ INIT_LIST_HEAD(&tpg->tpg_list);
+ mutex_init(&tpg->tpg_access_lock);
+ sema_init(&tpg->np_login_sem, 1);
+ spin_lock_init(&tpg->tpg_state_lock);
+ spin_lock_init(&tpg->tpg_np_lock);
+
+ return tpg;
+}
+
+static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *);
+
+int iscsit_load_discovery_tpg(void)
+{
+ struct iscsi_param *param;
+ struct iscsi_portal_group *tpg;
+ int ret;
+
+ tpg = iscsit_alloc_portal_group(NULL, 1);
+ if (!tpg) {
+ pr_err("Unable to allocate struct iscsi_portal_group\n");
+ return -1;
+ }
+
+ ret = core_tpg_register(
+ &lio_target_fabric_configfs->tf_ops,
+ NULL, &tpg->tpg_se_tpg, tpg,
+ TRANSPORT_TPG_TYPE_DISCOVERY);
+ if (ret < 0) {
+ kfree(tpg);
+ return -1;
+ }
+
+ tpg->sid = 1; /* First Assigned LIO Session ID */
+ iscsit_set_default_tpg_attribs(tpg);
+
+ if (iscsi_create_default_params(&tpg->param_list) < 0)
+ goto out;
+ /*
+ * By default we disable authentication for discovery sessions,
+ * this can be changed with:
+ *
+ * /sys/kernel/config/target/iscsi/discovery_auth/enforce_discovery_auth
+ */
+ param = iscsi_find_param_from_key(AUTHMETHOD, tpg->param_list);
+ if (!param)
+ goto out;
+
+ if (iscsi_update_param_value(param, "CHAP,None") < 0)
+ goto out;
+
+ tpg->tpg_attrib.authentication = 0;
+
+ spin_lock(&tpg->tpg_state_lock);
+ tpg->tpg_state = TPG_STATE_ACTIVE;
+ spin_unlock(&tpg->tpg_state_lock);
+
+ iscsit_global->discovery_tpg = tpg;
+ pr_debug("CORE[0] - Allocated Discovery TPG\n");
+
+ return 0;
+out:
+ if (tpg->sid == 1)
+ core_tpg_deregister(&tpg->tpg_se_tpg);
+ kfree(tpg);
+ return -1;
+}
+
+void iscsit_release_discovery_tpg(void)
+{
+ struct iscsi_portal_group *tpg = iscsit_global->discovery_tpg;
+
+ if (!tpg)
+ return;
+
+ core_tpg_deregister(&tpg->tpg_se_tpg);
+
+ kfree(tpg);
+ iscsit_global->discovery_tpg = NULL;
+}
+
+struct iscsi_portal_group *iscsit_get_tpg_from_np(
+ struct iscsi_tiqn *tiqn,
+ struct iscsi_np *np,
+ struct iscsi_tpg_np **tpg_np_out)
+{
+ struct iscsi_portal_group *tpg = NULL;
+ struct iscsi_tpg_np *tpg_np;
+
+ spin_lock(&tiqn->tiqn_tpg_lock);
+ list_for_each_entry(tpg, &tiqn->tiqn_tpg_list, tpg_list) {
+
+ spin_lock(&tpg->tpg_state_lock);
+ if (tpg->tpg_state != TPG_STATE_ACTIVE) {
+ spin_unlock(&tpg->tpg_state_lock);
+ continue;
+ }
+ spin_unlock(&tpg->tpg_state_lock);
+
+ spin_lock(&tpg->tpg_np_lock);
+ list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) {
+ if (tpg_np->tpg_np == np) {
+ *tpg_np_out = tpg_np;
+ kref_get(&tpg_np->tpg_np_kref);
+ spin_unlock(&tpg->tpg_np_lock);
+ spin_unlock(&tiqn->tiqn_tpg_lock);
+ return tpg;
+ }
+ }
+ spin_unlock(&tpg->tpg_np_lock);
+ }
+ spin_unlock(&tiqn->tiqn_tpg_lock);
+
+ return NULL;
+}
+
+int iscsit_get_tpg(
+ struct iscsi_portal_group *tpg)
+{
+ int ret;
+
+ ret = mutex_lock_interruptible(&tpg->tpg_access_lock);
+ return ((ret != 0) || signal_pending(current)) ? -1 : 0;
+}
+
+void iscsit_put_tpg(struct iscsi_portal_group *tpg)
+{
+ mutex_unlock(&tpg->tpg_access_lock);
+}
+
+static void iscsit_clear_tpg_np_login_thread(
+ struct iscsi_tpg_np *tpg_np,
+ struct iscsi_portal_group *tpg,
+ bool shutdown)
+{
+ if (!tpg_np->tpg_np) {
+ pr_err("struct iscsi_tpg_np->tpg_np is NULL!\n");
+ return;
+ }
+
+ if (shutdown)
+ tpg_np->tpg_np->enabled = false;
+ iscsit_reset_np_thread(tpg_np->tpg_np, tpg_np, tpg, shutdown);
+}
+
+static void iscsit_clear_tpg_np_login_threads(
+ struct iscsi_portal_group *tpg,
+ bool shutdown)
+{
+ struct iscsi_tpg_np *tpg_np;
+
+ spin_lock(&tpg->tpg_np_lock);
+ list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) {
+ if (!tpg_np->tpg_np) {
+ pr_err("struct iscsi_tpg_np->tpg_np is NULL!\n");
+ continue;
+ }
+ spin_unlock(&tpg->tpg_np_lock);
+ iscsit_clear_tpg_np_login_thread(tpg_np, tpg, shutdown);
+ spin_lock(&tpg->tpg_np_lock);
+ }
+ spin_unlock(&tpg->tpg_np_lock);
+}
+
+void iscsit_tpg_dump_params(struct iscsi_portal_group *tpg)
+{
+ iscsi_print_params(tpg->param_list);
+}
+
+static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ a->authentication = TA_AUTHENTICATION;
+ a->login_timeout = TA_LOGIN_TIMEOUT;
+ a->netif_timeout = TA_NETIF_TIMEOUT;
+ a->default_cmdsn_depth = TA_DEFAULT_CMDSN_DEPTH;
+ a->generate_node_acls = TA_GENERATE_NODE_ACLS;
+ a->cache_dynamic_acls = TA_CACHE_DYNAMIC_ACLS;
+ a->demo_mode_write_protect = TA_DEMO_MODE_WRITE_PROTECT;
+ a->prod_mode_write_protect = TA_PROD_MODE_WRITE_PROTECT;
+ a->demo_mode_discovery = TA_DEMO_MODE_DISCOVERY;
+ a->default_erl = TA_DEFAULT_ERL;
+ a->t10_pi = TA_DEFAULT_T10_PI;
+}
+
+int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_group *tpg)
+{
+ if (tpg->tpg_state != TPG_STATE_FREE) {
+ pr_err("Unable to add iSCSI Target Portal Group: %d"
+ " while not in TPG_STATE_FREE state.\n", tpg->tpgt);
+ return -EEXIST;
+ }
+ iscsit_set_default_tpg_attribs(tpg);
+
+ if (iscsi_create_default_params(&tpg->param_list) < 0)
+ goto err_out;
+
+ tpg->tpg_attrib.tpg = tpg;
+
+ spin_lock(&tpg->tpg_state_lock);
+ tpg->tpg_state = TPG_STATE_INACTIVE;
+ spin_unlock(&tpg->tpg_state_lock);
+
+ spin_lock(&tiqn->tiqn_tpg_lock);
+ list_add_tail(&tpg->tpg_list, &tiqn->tiqn_tpg_list);
+ tiqn->tiqn_ntpgs++;
+ pr_debug("CORE[%s]_TPG[%hu] - Added iSCSI Target Portal Group\n",
+ tiqn->tiqn, tpg->tpgt);
+ spin_unlock(&tiqn->tiqn_tpg_lock);
+
+ return 0;
+err_out:
+ if (tpg->param_list) {
+ iscsi_release_param_list(tpg->param_list);
+ tpg->param_list = NULL;
+ }
+ kfree(tpg);
+ return -ENOMEM;
+}
+
+int iscsit_tpg_del_portal_group(
+ struct iscsi_tiqn *tiqn,
+ struct iscsi_portal_group *tpg,
+ int force)
+{
+ u8 old_state = tpg->tpg_state;
+
+ spin_lock(&tpg->tpg_state_lock);
+ tpg->tpg_state = TPG_STATE_INACTIVE;
+ spin_unlock(&tpg->tpg_state_lock);
+
+ if (iscsit_release_sessions_for_tpg(tpg, force) < 0) {
+ pr_err("Unable to delete iSCSI Target Portal Group:"
+ " %hu while active sessions exist, and force=0\n",
+ tpg->tpgt);
+ tpg->tpg_state = old_state;
+ return -EPERM;
+ }
+
+ core_tpg_clear_object_luns(&tpg->tpg_se_tpg);
+
+ if (tpg->param_list) {
+ iscsi_release_param_list(tpg->param_list);
+ tpg->param_list = NULL;
+ }
+
+ core_tpg_deregister(&tpg->tpg_se_tpg);
+
+ spin_lock(&tpg->tpg_state_lock);
+ tpg->tpg_state = TPG_STATE_FREE;
+ spin_unlock(&tpg->tpg_state_lock);
+
+ spin_lock(&tiqn->tiqn_tpg_lock);
+ tiqn->tiqn_ntpgs--;
+ list_del(&tpg->tpg_list);
+ spin_unlock(&tiqn->tiqn_tpg_lock);
+
+ pr_debug("CORE[%s]_TPG[%hu] - Deleted iSCSI Target Portal Group\n",
+ tiqn->tiqn, tpg->tpgt);
+
+ kfree(tpg);
+ return 0;
+}
+
+int iscsit_tpg_enable_portal_group(struct iscsi_portal_group *tpg)
+{
+ struct iscsi_param *param;
+ struct iscsi_tiqn *tiqn = tpg->tpg_tiqn;
+ int ret;
+
+ spin_lock(&tpg->tpg_state_lock);
+ if (tpg->tpg_state == TPG_STATE_ACTIVE) {
+ pr_err("iSCSI target portal group: %hu is already"
+ " active, ignoring request.\n", tpg->tpgt);
+ spin_unlock(&tpg->tpg_state_lock);
+ return -EINVAL;
+ }
+ /*
+ * Make sure that AuthMethod does not contain None as an option
+ * unless explictly disabled. Set the default to CHAP if authentication
+ * is enforced (as per default), and remove the NONE option.
+ */
+ param = iscsi_find_param_from_key(AUTHMETHOD, tpg->param_list);
+ if (!param) {
+ spin_unlock(&tpg->tpg_state_lock);
+ return -EINVAL;
+ }
+
+ if (tpg->tpg_attrib.authentication) {
+ if (!strcmp(param->value, NONE)) {
+ ret = iscsi_update_param_value(param, CHAP);
+ if (ret)
+ goto err;
+ }
+
+ ret = iscsit_ta_authentication(tpg, 1);
+ if (ret < 0)
+ goto err;
+ }
+
+ tpg->tpg_state = TPG_STATE_ACTIVE;
+ spin_unlock(&tpg->tpg_state_lock);
+
+ spin_lock(&tiqn->tiqn_tpg_lock);
+ tiqn->tiqn_active_tpgs++;
+ pr_debug("iSCSI_TPG[%hu] - Enabled iSCSI Target Portal Group\n",
+ tpg->tpgt);
+ spin_unlock(&tiqn->tiqn_tpg_lock);
+
+ return 0;
+
+err:
+ spin_unlock(&tpg->tpg_state_lock);
+ return ret;
+}
+
+int iscsit_tpg_disable_portal_group(struct iscsi_portal_group *tpg, int force)
+{
+ struct iscsi_tiqn *tiqn;
+ u8 old_state = tpg->tpg_state;
+
+ spin_lock(&tpg->tpg_state_lock);
+ if (tpg->tpg_state == TPG_STATE_INACTIVE) {
+ pr_err("iSCSI Target Portal Group: %hu is already"
+ " inactive, ignoring request.\n", tpg->tpgt);
+ spin_unlock(&tpg->tpg_state_lock);
+ return -EINVAL;
+ }
+ tpg->tpg_state = TPG_STATE_INACTIVE;
+ spin_unlock(&tpg->tpg_state_lock);
+
+ iscsit_clear_tpg_np_login_threads(tpg, false);
+
+ if (iscsit_release_sessions_for_tpg(tpg, force) < 0) {
+ spin_lock(&tpg->tpg_state_lock);
+ tpg->tpg_state = old_state;
+ spin_unlock(&tpg->tpg_state_lock);
+ pr_err("Unable to disable iSCSI Target Portal Group:"
+ " %hu while active sessions exist, and force=0\n",
+ tpg->tpgt);
+ return -EPERM;
+ }
+
+ tiqn = tpg->tpg_tiqn;
+ if (!tiqn || (tpg == iscsit_global->discovery_tpg))
+ return 0;
+
+ spin_lock(&tiqn->tiqn_tpg_lock);
+ tiqn->tiqn_active_tpgs--;
+ pr_debug("iSCSI_TPG[%hu] - Disabled iSCSI Target Portal Group\n",
+ tpg->tpgt);
+ spin_unlock(&tiqn->tiqn_tpg_lock);
+
+ return 0;
+}
+
+struct iscsi_node_attrib *iscsit_tpg_get_node_attrib(
+ struct iscsi_session *sess)
+{
+ struct se_session *se_sess = sess->se_sess;
+ struct se_node_acl *se_nacl = se_sess->se_node_acl;
+ struct iscsi_node_acl *acl = container_of(se_nacl, struct iscsi_node_acl,
+ se_node_acl);
+
+ return &acl->node_attrib;
+}
+
+struct iscsi_tpg_np *iscsit_tpg_locate_child_np(
+ struct iscsi_tpg_np *tpg_np,
+ int network_transport)
+{
+ struct iscsi_tpg_np *tpg_np_child, *tpg_np_child_tmp;
+
+ spin_lock(&tpg_np->tpg_np_parent_lock);
+ list_for_each_entry_safe(tpg_np_child, tpg_np_child_tmp,
+ &tpg_np->tpg_np_parent_list, tpg_np_child_list) {
+ if (tpg_np_child->tpg_np->np_network_transport ==
+ network_transport) {
+ spin_unlock(&tpg_np->tpg_np_parent_lock);
+ return tpg_np_child;
+ }
+ }
+ spin_unlock(&tpg_np->tpg_np_parent_lock);
+
+ return NULL;
+}
+
+static bool iscsit_tpg_check_network_portal(
+ struct iscsi_tiqn *tiqn,
+ struct __kernel_sockaddr_storage *sockaddr,
+ int network_transport)
+{
+ struct iscsi_portal_group *tpg;
+ struct iscsi_tpg_np *tpg_np;
+ struct iscsi_np *np;
+ bool match = false;
+
+ spin_lock(&tiqn->tiqn_tpg_lock);
+ list_for_each_entry(tpg, &tiqn->tiqn_tpg_list, tpg_list) {
+
+ spin_lock(&tpg->tpg_np_lock);
+ list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) {
+ np = tpg_np->tpg_np;
+
+ match = iscsit_check_np_match(sockaddr, np,
+ network_transport);
+ if (match)
+ break;
+ }
+ spin_unlock(&tpg->tpg_np_lock);
+ }
+ spin_unlock(&tiqn->tiqn_tpg_lock);
+
+ return match;
+}
+
+struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
+ struct iscsi_portal_group *tpg,
+ struct __kernel_sockaddr_storage *sockaddr,
+ char *ip_str,
+ struct iscsi_tpg_np *tpg_np_parent,
+ int network_transport)
+{
+ struct iscsi_np *np;
+ struct iscsi_tpg_np *tpg_np;
+
+ if (!tpg_np_parent) {
+ if (iscsit_tpg_check_network_portal(tpg->tpg_tiqn, sockaddr,
+ network_transport)) {
+ pr_err("Network Portal: %s already exists on a"
+ " different TPG on %s\n", ip_str,
+ tpg->tpg_tiqn->tiqn);
+ return ERR_PTR(-EEXIST);
+ }
+ }
+
+ tpg_np = kzalloc(sizeof(struct iscsi_tpg_np), GFP_KERNEL);
+ if (!tpg_np) {
+ pr_err("Unable to allocate memory for"
+ " struct iscsi_tpg_np.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ np = iscsit_add_np(sockaddr, ip_str, network_transport);
+ if (IS_ERR(np)) {
+ kfree(tpg_np);
+ return ERR_CAST(np);
+ }
+
+ INIT_LIST_HEAD(&tpg_np->tpg_np_list);
+ INIT_LIST_HEAD(&tpg_np->tpg_np_child_list);
+ INIT_LIST_HEAD(&tpg_np->tpg_np_parent_list);
+ spin_lock_init(&tpg_np->tpg_np_parent_lock);
+ init_completion(&tpg_np->tpg_np_comp);
+ kref_init(&tpg_np->tpg_np_kref);
+ tpg_np->tpg_np = np;
+ np->tpg_np = tpg_np;
+ tpg_np->tpg = tpg;
+
+ spin_lock(&tpg->tpg_np_lock);
+ list_add_tail(&tpg_np->tpg_np_list, &tpg->tpg_gnp_list);
+ tpg->num_tpg_nps++;
+ if (tpg->tpg_tiqn)
+ tpg->tpg_tiqn->tiqn_num_tpg_nps++;
+ spin_unlock(&tpg->tpg_np_lock);
+
+ if (tpg_np_parent) {
+ tpg_np->tpg_np_parent = tpg_np_parent;
+ spin_lock(&tpg_np_parent->tpg_np_parent_lock);
+ list_add_tail(&tpg_np->tpg_np_child_list,
+ &tpg_np_parent->tpg_np_parent_list);
+ spin_unlock(&tpg_np_parent->tpg_np_parent_lock);
+ }
+
+ pr_debug("CORE[%s] - Added Network Portal: %s:%hu,%hu on %s\n",
+ tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt,
+ np->np_transport->name);
+
+ return tpg_np;
+}
+
+static int iscsit_tpg_release_np(
+ struct iscsi_tpg_np *tpg_np,
+ struct iscsi_portal_group *tpg,
+ struct iscsi_np *np)
+{
+ iscsit_clear_tpg_np_login_thread(tpg_np, tpg, true);
+
+ pr_debug("CORE[%s] - Removed Network Portal: %s:%hu,%hu on %s\n",
+ tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt,
+ np->np_transport->name);
+
+ tpg_np->tpg_np = NULL;
+ tpg_np->tpg = NULL;
+ kfree(tpg_np);
+ /*
+ * iscsit_del_np() will shutdown struct iscsi_np when last TPG reference is released.
+ */
+ return iscsit_del_np(np);
+}
+
+int iscsit_tpg_del_network_portal(
+ struct iscsi_portal_group *tpg,
+ struct iscsi_tpg_np *tpg_np)
+{
+ struct iscsi_np *np;
+ struct iscsi_tpg_np *tpg_np_child, *tpg_np_child_tmp;
+ int ret = 0;
+
+ np = tpg_np->tpg_np;
+ if (!np) {
+ pr_err("Unable to locate struct iscsi_np from"
+ " struct iscsi_tpg_np\n");
+ return -EINVAL;
+ }
+
+ if (!tpg_np->tpg_np_parent) {
+ /*
+ * We are the parent tpg network portal. Release all of the
+ * child tpg_np's (eg: the non ISCSI_TCP ones) on our parent
+ * list first.
+ */
+ list_for_each_entry_safe(tpg_np_child, tpg_np_child_tmp,
+ &tpg_np->tpg_np_parent_list,
+ tpg_np_child_list) {
+ ret = iscsit_tpg_del_network_portal(tpg, tpg_np_child);
+ if (ret < 0)
+ pr_err("iscsit_tpg_del_network_portal()"
+ " failed: %d\n", ret);
+ }
+ } else {
+ /*
+ * We are not the parent ISCSI_TCP tpg network portal. Release
+ * our own network portals from the child list.
+ */
+ spin_lock(&tpg_np->tpg_np_parent->tpg_np_parent_lock);
+ list_del(&tpg_np->tpg_np_child_list);
+ spin_unlock(&tpg_np->tpg_np_parent->tpg_np_parent_lock);
+ }
+
+ spin_lock(&tpg->tpg_np_lock);
+ list_del(&tpg_np->tpg_np_list);
+ tpg->num_tpg_nps--;
+ if (tpg->tpg_tiqn)
+ tpg->tpg_tiqn->tiqn_num_tpg_nps--;
+ spin_unlock(&tpg->tpg_np_lock);
+
+ return iscsit_tpg_release_np(tpg_np, tpg, np);
+}
+
+int iscsit_tpg_set_initiator_node_queue_depth(
+ struct iscsi_portal_group *tpg,
+ unsigned char *initiatorname,
+ u32 queue_depth,
+ int force)
+{
+ return core_tpg_set_initiator_node_queue_depth(&tpg->tpg_se_tpg,
+ initiatorname, queue_depth, force);
+}
+
+int iscsit_ta_authentication(struct iscsi_portal_group *tpg, u32 authentication)
+{
+ unsigned char buf1[256], buf2[256], *none = NULL;
+ int len;
+ struct iscsi_param *param;
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if ((authentication != 1) && (authentication != 0)) {
+ pr_err("Illegal value for authentication parameter:"
+ " %u, ignoring request.\n", authentication);
+ return -EINVAL;
+ }
+
+ memset(buf1, 0, sizeof(buf1));
+ memset(buf2, 0, sizeof(buf2));
+
+ param = iscsi_find_param_from_key(AUTHMETHOD, tpg->param_list);
+ if (!param)
+ return -EINVAL;
+
+ if (authentication) {
+ snprintf(buf1, sizeof(buf1), "%s", param->value);
+ none = strstr(buf1, NONE);
+ if (!none)
+ goto out;
+ if (!strncmp(none + 4, ",", 1)) {
+ if (!strcmp(buf1, none))
+ sprintf(buf2, "%s", none+5);
+ else {
+ none--;
+ *none = '\0';
+ len = sprintf(buf2, "%s", buf1);
+ none += 5;
+ sprintf(buf2 + len, "%s", none);
+ }
+ } else {
+ none--;
+ *none = '\0';
+ sprintf(buf2, "%s", buf1);
+ }
+ if (iscsi_update_param_value(param, buf2) < 0)
+ return -EINVAL;
+ } else {
+ snprintf(buf1, sizeof(buf1), "%s", param->value);
+ none = strstr(buf1, NONE);
+ if (none)
+ goto out;
+ strncat(buf1, ",", strlen(","));
+ strncat(buf1, NONE, strlen(NONE));
+ if (iscsi_update_param_value(param, buf1) < 0)
+ return -EINVAL;
+ }
+
+out:
+ a->authentication = authentication;
+ pr_debug("%s iSCSI Authentication Methods for TPG: %hu.\n",
+ a->authentication ? "Enforcing" : "Disabling", tpg->tpgt);
+
+ return 0;
+}
+
+int iscsit_ta_login_timeout(
+ struct iscsi_portal_group *tpg,
+ u32 login_timeout)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if (login_timeout > TA_LOGIN_TIMEOUT_MAX) {
+ pr_err("Requested Login Timeout %u larger than maximum"
+ " %u\n", login_timeout, TA_LOGIN_TIMEOUT_MAX);
+ return -EINVAL;
+ } else if (login_timeout < TA_LOGIN_TIMEOUT_MIN) {
+ pr_err("Requested Logout Timeout %u smaller than"
+ " minimum %u\n", login_timeout, TA_LOGIN_TIMEOUT_MIN);
+ return -EINVAL;
+ }
+
+ a->login_timeout = login_timeout;
+ pr_debug("Set Logout Timeout to %u for Target Portal Group"
+ " %hu\n", a->login_timeout, tpg->tpgt);
+
+ return 0;
+}
+
+int iscsit_ta_netif_timeout(
+ struct iscsi_portal_group *tpg,
+ u32 netif_timeout)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if (netif_timeout > TA_NETIF_TIMEOUT_MAX) {
+ pr_err("Requested Network Interface Timeout %u larger"
+ " than maximum %u\n", netif_timeout,
+ TA_NETIF_TIMEOUT_MAX);
+ return -EINVAL;
+ } else if (netif_timeout < TA_NETIF_TIMEOUT_MIN) {
+ pr_err("Requested Network Interface Timeout %u smaller"
+ " than minimum %u\n", netif_timeout,
+ TA_NETIF_TIMEOUT_MIN);
+ return -EINVAL;
+ }
+
+ a->netif_timeout = netif_timeout;
+ pr_debug("Set Network Interface Timeout to %u for"
+ " Target Portal Group %hu\n", a->netif_timeout, tpg->tpgt);
+
+ return 0;
+}
+
+int iscsit_ta_generate_node_acls(
+ struct iscsi_portal_group *tpg,
+ u32 flag)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if ((flag != 0) && (flag != 1)) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
+ }
+
+ a->generate_node_acls = flag;
+ pr_debug("iSCSI_TPG[%hu] - Generate Initiator Portal Group ACLs: %s\n",
+ tpg->tpgt, (a->generate_node_acls) ? "Enabled" : "Disabled");
+
+ if (flag == 1 && a->cache_dynamic_acls == 0) {
+ pr_debug("Explicitly setting cache_dynamic_acls=1 when "
+ "generate_node_acls=1\n");
+ a->cache_dynamic_acls = 1;
+ }
+
+ return 0;
+}
+
+int iscsit_ta_default_cmdsn_depth(
+ struct iscsi_portal_group *tpg,
+ u32 tcq_depth)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if (tcq_depth > TA_DEFAULT_CMDSN_DEPTH_MAX) {
+ pr_err("Requested Default Queue Depth: %u larger"
+ " than maximum %u\n", tcq_depth,
+ TA_DEFAULT_CMDSN_DEPTH_MAX);
+ return -EINVAL;
+ } else if (tcq_depth < TA_DEFAULT_CMDSN_DEPTH_MIN) {
+ pr_err("Requested Default Queue Depth: %u smaller"
+ " than minimum %u\n", tcq_depth,
+ TA_DEFAULT_CMDSN_DEPTH_MIN);
+ return -EINVAL;
+ }
+
+ a->default_cmdsn_depth = tcq_depth;
+ pr_debug("iSCSI_TPG[%hu] - Set Default CmdSN TCQ Depth to %u\n",
+ tpg->tpgt, a->default_cmdsn_depth);
+
+ return 0;
+}
+
+int iscsit_ta_cache_dynamic_acls(
+ struct iscsi_portal_group *tpg,
+ u32 flag)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if ((flag != 0) && (flag != 1)) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
+ }
+
+ if (a->generate_node_acls == 1 && flag == 0) {
+ pr_debug("Skipping cache_dynamic_acls=0 when"
+ " generate_node_acls=1\n");
+ return 0;
+ }
+
+ a->cache_dynamic_acls = flag;
+ pr_debug("iSCSI_TPG[%hu] - Cache Dynamic Initiator Portal Group"
+ " ACLs %s\n", tpg->tpgt, (a->cache_dynamic_acls) ?
+ "Enabled" : "Disabled");
+
+ return 0;
+}
+
+int iscsit_ta_demo_mode_write_protect(
+ struct iscsi_portal_group *tpg,
+ u32 flag)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if ((flag != 0) && (flag != 1)) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
+ }
+
+ a->demo_mode_write_protect = flag;
+ pr_debug("iSCSI_TPG[%hu] - Demo Mode Write Protect bit: %s\n",
+ tpg->tpgt, (a->demo_mode_write_protect) ? "ON" : "OFF");
+
+ return 0;
+}
+
+int iscsit_ta_prod_mode_write_protect(
+ struct iscsi_portal_group *tpg,
+ u32 flag)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if ((flag != 0) && (flag != 1)) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
+ }
+
+ a->prod_mode_write_protect = flag;
+ pr_debug("iSCSI_TPG[%hu] - Production Mode Write Protect bit:"
+ " %s\n", tpg->tpgt, (a->prod_mode_write_protect) ?
+ "ON" : "OFF");
+
+ return 0;
+}
+
+int iscsit_ta_demo_mode_discovery(
+ struct iscsi_portal_group *tpg,
+ u32 flag)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if ((flag != 0) && (flag != 1)) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
+ }
+
+ a->demo_mode_discovery = flag;
+ pr_debug("iSCSI_TPG[%hu] - Demo Mode Discovery bit:"
+ " %s\n", tpg->tpgt, (a->demo_mode_discovery) ?
+ "ON" : "OFF");
+
+ return 0;
+}
+
+int iscsit_ta_default_erl(
+ struct iscsi_portal_group *tpg,
+ u32 default_erl)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if ((default_erl != 0) && (default_erl != 1) && (default_erl != 2)) {
+ pr_err("Illegal value for default_erl: %u\n", default_erl);
+ return -EINVAL;
+ }
+
+ a->default_erl = default_erl;
+ pr_debug("iSCSI_TPG[%hu] - DefaultERL: %u\n", tpg->tpgt, a->default_erl);
+
+ return 0;
+}
+
+int iscsit_ta_t10_pi(
+ struct iscsi_portal_group *tpg,
+ u32 flag)
+{
+ struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+ if ((flag != 0) && (flag != 1)) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
+ }
+
+ a->t10_pi = flag;
+ pr_debug("iSCSI_TPG[%hu] - T10 Protection information bit:"
+ " %s\n", tpg->tpgt, (a->t10_pi) ?
+ "ON" : "OFF");
+
+ return 0;
+}
diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h
new file mode 100644
index 00000000000..e7265337bc4
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_tpg.h
@@ -0,0 +1,43 @@
+#ifndef ISCSI_TARGET_TPG_H
+#define ISCSI_TARGET_TPG_H
+
+extern struct iscsi_portal_group *iscsit_alloc_portal_group(struct iscsi_tiqn *, u16);
+extern int iscsit_load_discovery_tpg(void);
+extern void iscsit_release_discovery_tpg(void);
+extern struct iscsi_portal_group *iscsit_get_tpg_from_np(struct iscsi_tiqn *,
+ struct iscsi_np *, struct iscsi_tpg_np **);
+extern int iscsit_get_tpg(struct iscsi_portal_group *);
+extern void iscsit_put_tpg(struct iscsi_portal_group *);
+extern void iscsit_tpg_dump_params(struct iscsi_portal_group *);
+extern int iscsit_tpg_add_portal_group(struct iscsi_tiqn *, struct iscsi_portal_group *);
+extern int iscsit_tpg_del_portal_group(struct iscsi_tiqn *, struct iscsi_portal_group *,
+ int);
+extern int iscsit_tpg_enable_portal_group(struct iscsi_portal_group *);
+extern int iscsit_tpg_disable_portal_group(struct iscsi_portal_group *, int);
+extern struct iscsi_node_acl *iscsit_tpg_add_initiator_node_acl(
+ struct iscsi_portal_group *, const char *, u32);
+extern void iscsit_tpg_del_initiator_node_acl(struct iscsi_portal_group *,
+ struct se_node_acl *);
+extern struct iscsi_node_attrib *iscsit_tpg_get_node_attrib(struct iscsi_session *);
+extern void iscsit_tpg_del_external_nps(struct iscsi_tpg_np *);
+extern struct iscsi_tpg_np *iscsit_tpg_locate_child_np(struct iscsi_tpg_np *, int);
+extern struct iscsi_tpg_np *iscsit_tpg_add_network_portal(struct iscsi_portal_group *,
+ struct __kernel_sockaddr_storage *, char *, struct iscsi_tpg_np *,
+ int);
+extern int iscsit_tpg_del_network_portal(struct iscsi_portal_group *,
+ struct iscsi_tpg_np *);
+extern int iscsit_tpg_set_initiator_node_queue_depth(struct iscsi_portal_group *,
+ unsigned char *, u32, int);
+extern int iscsit_ta_authentication(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_login_timeout(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_netif_timeout(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_generate_node_acls(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_default_cmdsn_depth(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_cache_dynamic_acls(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_demo_mode_write_protect(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_prod_mode_write_protect(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_demo_mode_discovery(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_default_erl(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_t10_pi(struct iscsi_portal_group *, u32);
+
+#endif /* ISCSI_TARGET_TPG_H */
diff --git a/drivers/target/iscsi/iscsi_target_tq.c b/drivers/target/iscsi/iscsi_target_tq.c
new file mode 100644
index 00000000000..601e9cc61e9
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_tq.c
@@ -0,0 +1,513 @@
+/*******************************************************************************
+ * This file contains the iSCSI Login Thread and Thread Queue functions.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/bitmap.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_tq.h"
+#include "iscsi_target.h"
+
+static LIST_HEAD(active_ts_list);
+static LIST_HEAD(inactive_ts_list);
+static DEFINE_SPINLOCK(active_ts_lock);
+static DEFINE_SPINLOCK(inactive_ts_lock);
+static DEFINE_SPINLOCK(ts_bitmap_lock);
+
+static void iscsi_add_ts_to_active_list(struct iscsi_thread_set *ts)
+{
+ spin_lock(&active_ts_lock);
+ list_add_tail(&ts->ts_list, &active_ts_list);
+ iscsit_global->active_ts++;
+ spin_unlock(&active_ts_lock);
+}
+
+static void iscsi_add_ts_to_inactive_list(struct iscsi_thread_set *ts)
+{
+ spin_lock(&inactive_ts_lock);
+ list_add_tail(&ts->ts_list, &inactive_ts_list);
+ iscsit_global->inactive_ts++;
+ spin_unlock(&inactive_ts_lock);
+}
+
+static void iscsi_del_ts_from_active_list(struct iscsi_thread_set *ts)
+{
+ spin_lock(&active_ts_lock);
+ list_del(&ts->ts_list);
+ iscsit_global->active_ts--;
+ spin_unlock(&active_ts_lock);
+}
+
+static struct iscsi_thread_set *iscsi_get_ts_from_inactive_list(void)
+{
+ struct iscsi_thread_set *ts;
+
+ spin_lock(&inactive_ts_lock);
+ if (list_empty(&inactive_ts_list)) {
+ spin_unlock(&inactive_ts_lock);
+ return NULL;
+ }
+
+ ts = list_first_entry(&inactive_ts_list, struct iscsi_thread_set, ts_list);
+
+ list_del(&ts->ts_list);
+ iscsit_global->inactive_ts--;
+ spin_unlock(&inactive_ts_lock);
+
+ return ts;
+}
+
+int iscsi_allocate_thread_sets(u32 thread_pair_count)
+{
+ int allocated_thread_pair_count = 0, i, thread_id;
+ struct iscsi_thread_set *ts = NULL;
+
+ for (i = 0; i < thread_pair_count; i++) {
+ ts = kzalloc(sizeof(struct iscsi_thread_set), GFP_KERNEL);
+ if (!ts) {
+ pr_err("Unable to allocate memory for"
+ " thread set.\n");
+ return allocated_thread_pair_count;
+ }
+ /*
+ * Locate the next available regision in the thread_set_bitmap
+ */
+ spin_lock(&ts_bitmap_lock);
+ thread_id = bitmap_find_free_region(iscsit_global->ts_bitmap,
+ iscsit_global->ts_bitmap_count, get_order(1));
+ spin_unlock(&ts_bitmap_lock);
+ if (thread_id < 0) {
+ pr_err("bitmap_find_free_region() failed for"
+ " thread_set_bitmap\n");
+ kfree(ts);
+ return allocated_thread_pair_count;
+ }
+
+ ts->thread_id = thread_id;
+ ts->status = ISCSI_THREAD_SET_FREE;
+ INIT_LIST_HEAD(&ts->ts_list);
+ spin_lock_init(&ts->ts_state_lock);
+ init_completion(&ts->rx_restart_comp);
+ init_completion(&ts->tx_restart_comp);
+ init_completion(&ts->rx_start_comp);
+ init_completion(&ts->tx_start_comp);
+ sema_init(&ts->ts_activate_sem, 0);
+
+ ts->create_threads = 1;
+ ts->tx_thread = kthread_run(iscsi_target_tx_thread, ts, "%s",
+ ISCSI_TX_THREAD_NAME);
+ if (IS_ERR(ts->tx_thread)) {
+ dump_stack();
+ pr_err("Unable to start iscsi_target_tx_thread\n");
+ break;
+ }
+
+ ts->rx_thread = kthread_run(iscsi_target_rx_thread, ts, "%s",
+ ISCSI_RX_THREAD_NAME);
+ if (IS_ERR(ts->rx_thread)) {
+ kthread_stop(ts->tx_thread);
+ pr_err("Unable to start iscsi_target_rx_thread\n");
+ break;
+ }
+ ts->create_threads = 0;
+
+ iscsi_add_ts_to_inactive_list(ts);
+ allocated_thread_pair_count++;
+ }
+
+ pr_debug("Spawned %d thread set(s) (%d total threads).\n",
+ allocated_thread_pair_count, allocated_thread_pair_count * 2);
+ return allocated_thread_pair_count;
+}
+
+static void iscsi_deallocate_thread_one(struct iscsi_thread_set *ts)
+{
+ spin_lock_bh(&ts->ts_state_lock);
+ ts->status = ISCSI_THREAD_SET_DIE;
+
+ if (ts->rx_thread) {
+ complete(&ts->rx_start_comp);
+ spin_unlock_bh(&ts->ts_state_lock);
+ kthread_stop(ts->rx_thread);
+ spin_lock_bh(&ts->ts_state_lock);
+ }
+ if (ts->tx_thread) {
+ complete(&ts->tx_start_comp);
+ spin_unlock_bh(&ts->ts_state_lock);
+ kthread_stop(ts->tx_thread);
+ spin_lock_bh(&ts->ts_state_lock);
+ }
+ spin_unlock_bh(&ts->ts_state_lock);
+ /*
+ * Release this thread_id in the thread_set_bitmap
+ */
+ spin_lock(&ts_bitmap_lock);
+ bitmap_release_region(iscsit_global->ts_bitmap,
+ ts->thread_id, get_order(1));
+ spin_unlock(&ts_bitmap_lock);
+
+ kfree(ts);
+}
+
+void iscsi_deallocate_thread_sets(void)
+{
+ struct iscsi_thread_set *ts = NULL;
+ u32 released_count = 0;
+
+ while ((ts = iscsi_get_ts_from_inactive_list())) {
+
+ iscsi_deallocate_thread_one(ts);
+ released_count++;
+ }
+
+ if (released_count)
+ pr_debug("Stopped %d thread set(s) (%d total threads)."
+ "\n", released_count, released_count * 2);
+}
+
+static void iscsi_deallocate_extra_thread_sets(void)
+{
+ u32 orig_count, released_count = 0;
+ struct iscsi_thread_set *ts = NULL;
+
+ orig_count = TARGET_THREAD_SET_COUNT;
+
+ while ((iscsit_global->inactive_ts + 1) > orig_count) {
+ ts = iscsi_get_ts_from_inactive_list();
+ if (!ts)
+ break;
+
+ iscsi_deallocate_thread_one(ts);
+ released_count++;
+ }
+
+ if (released_count)
+ pr_debug("Stopped %d thread set(s) (%d total threads)."
+ "\n", released_count, released_count * 2);
+}
+
+void iscsi_activate_thread_set(struct iscsi_conn *conn, struct iscsi_thread_set *ts)
+{
+ iscsi_add_ts_to_active_list(ts);
+
+ spin_lock_bh(&ts->ts_state_lock);
+ conn->thread_set = ts;
+ ts->conn = conn;
+ ts->status = ISCSI_THREAD_SET_ACTIVE;
+ spin_unlock_bh(&ts->ts_state_lock);
+
+ complete(&ts->rx_start_comp);
+ complete(&ts->tx_start_comp);
+
+ down(&ts->ts_activate_sem);
+}
+
+struct iscsi_thread_set *iscsi_get_thread_set(void)
+{
+ struct iscsi_thread_set *ts;
+
+get_set:
+ ts = iscsi_get_ts_from_inactive_list();
+ if (!ts) {
+ iscsi_allocate_thread_sets(1);
+ goto get_set;
+ }
+
+ ts->delay_inactive = 1;
+ ts->signal_sent = 0;
+ ts->thread_count = 2;
+ init_completion(&ts->rx_restart_comp);
+ init_completion(&ts->tx_restart_comp);
+ sema_init(&ts->ts_activate_sem, 0);
+
+ return ts;
+}
+
+void iscsi_set_thread_clear(struct iscsi_conn *conn, u8 thread_clear)
+{
+ struct iscsi_thread_set *ts = NULL;
+
+ if (!conn->thread_set) {
+ pr_err("struct iscsi_conn->thread_set is NULL\n");
+ return;
+ }
+ ts = conn->thread_set;
+
+ spin_lock_bh(&ts->ts_state_lock);
+ ts->thread_clear &= ~thread_clear;
+
+ if ((thread_clear & ISCSI_CLEAR_RX_THREAD) &&
+ (ts->blocked_threads & ISCSI_BLOCK_RX_THREAD))
+ complete(&ts->rx_restart_comp);
+ else if ((thread_clear & ISCSI_CLEAR_TX_THREAD) &&
+ (ts->blocked_threads & ISCSI_BLOCK_TX_THREAD))
+ complete(&ts->tx_restart_comp);
+ spin_unlock_bh(&ts->ts_state_lock);
+}
+
+void iscsi_set_thread_set_signal(struct iscsi_conn *conn, u8 signal_sent)
+{
+ struct iscsi_thread_set *ts = NULL;
+
+ if (!conn->thread_set) {
+ pr_err("struct iscsi_conn->thread_set is NULL\n");
+ return;
+ }
+ ts = conn->thread_set;
+
+ spin_lock_bh(&ts->ts_state_lock);
+ ts->signal_sent |= signal_sent;
+ spin_unlock_bh(&ts->ts_state_lock);
+}
+
+int iscsi_release_thread_set(struct iscsi_conn *conn)
+{
+ int thread_called = 0;
+ struct iscsi_thread_set *ts = NULL;
+
+ if (!conn || !conn->thread_set) {
+ pr_err("connection or thread set pointer is NULL\n");
+ BUG();
+ }
+ ts = conn->thread_set;
+
+ spin_lock_bh(&ts->ts_state_lock);
+ ts->status = ISCSI_THREAD_SET_RESET;
+
+ if (!strncmp(current->comm, ISCSI_RX_THREAD_NAME,
+ strlen(ISCSI_RX_THREAD_NAME)))
+ thread_called = ISCSI_RX_THREAD;
+ else if (!strncmp(current->comm, ISCSI_TX_THREAD_NAME,
+ strlen(ISCSI_TX_THREAD_NAME)))
+ thread_called = ISCSI_TX_THREAD;
+
+ if (ts->rx_thread && (thread_called == ISCSI_TX_THREAD) &&
+ (ts->thread_clear & ISCSI_CLEAR_RX_THREAD)) {
+
+ if (!(ts->signal_sent & ISCSI_SIGNAL_RX_THREAD)) {
+ send_sig(SIGINT, ts->rx_thread, 1);
+ ts->signal_sent |= ISCSI_SIGNAL_RX_THREAD;
+ }
+ ts->blocked_threads |= ISCSI_BLOCK_RX_THREAD;
+ spin_unlock_bh(&ts->ts_state_lock);
+ wait_for_completion(&ts->rx_restart_comp);
+ spin_lock_bh(&ts->ts_state_lock);
+ ts->blocked_threads &= ~ISCSI_BLOCK_RX_THREAD;
+ }
+ if (ts->tx_thread && (thread_called == ISCSI_RX_THREAD) &&
+ (ts->thread_clear & ISCSI_CLEAR_TX_THREAD)) {
+
+ if (!(ts->signal_sent & ISCSI_SIGNAL_TX_THREAD)) {
+ send_sig(SIGINT, ts->tx_thread, 1);
+ ts->signal_sent |= ISCSI_SIGNAL_TX_THREAD;
+ }
+ ts->blocked_threads |= ISCSI_BLOCK_TX_THREAD;
+ spin_unlock_bh(&ts->ts_state_lock);
+ wait_for_completion(&ts->tx_restart_comp);
+ spin_lock_bh(&ts->ts_state_lock);
+ ts->blocked_threads &= ~ISCSI_BLOCK_TX_THREAD;
+ }
+
+ ts->conn = NULL;
+ ts->status = ISCSI_THREAD_SET_FREE;
+ spin_unlock_bh(&ts->ts_state_lock);
+
+ return 0;
+}
+
+int iscsi_thread_set_force_reinstatement(struct iscsi_conn *conn)
+{
+ struct iscsi_thread_set *ts;
+
+ if (!conn->thread_set)
+ return -1;
+ ts = conn->thread_set;
+
+ spin_lock_bh(&ts->ts_state_lock);
+ if (ts->status != ISCSI_THREAD_SET_ACTIVE) {
+ spin_unlock_bh(&ts->ts_state_lock);
+ return -1;
+ }
+
+ if (ts->tx_thread && (!(ts->signal_sent & ISCSI_SIGNAL_TX_THREAD))) {
+ send_sig(SIGINT, ts->tx_thread, 1);
+ ts->signal_sent |= ISCSI_SIGNAL_TX_THREAD;
+ }
+ if (ts->rx_thread && (!(ts->signal_sent & ISCSI_SIGNAL_RX_THREAD))) {
+ send_sig(SIGINT, ts->rx_thread, 1);
+ ts->signal_sent |= ISCSI_SIGNAL_RX_THREAD;
+ }
+ spin_unlock_bh(&ts->ts_state_lock);
+
+ return 0;
+}
+
+static void iscsi_check_to_add_additional_sets(void)
+{
+ int thread_sets_add;
+
+ spin_lock(&inactive_ts_lock);
+ thread_sets_add = iscsit_global->inactive_ts;
+ spin_unlock(&inactive_ts_lock);
+ if (thread_sets_add == 1)
+ iscsi_allocate_thread_sets(1);
+}
+
+static int iscsi_signal_thread_pre_handler(struct iscsi_thread_set *ts)
+{
+ spin_lock_bh(&ts->ts_state_lock);
+ if (ts->status == ISCSI_THREAD_SET_DIE || kthread_should_stop() ||
+ signal_pending(current)) {
+ spin_unlock_bh(&ts->ts_state_lock);
+ return -1;
+ }
+ spin_unlock_bh(&ts->ts_state_lock);
+
+ return 0;
+}
+
+struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *ts)
+{
+ int ret;
+
+ spin_lock_bh(&ts->ts_state_lock);
+ if (ts->create_threads) {
+ spin_unlock_bh(&ts->ts_state_lock);
+ goto sleep;
+ }
+
+ if (ts->status != ISCSI_THREAD_SET_DIE)
+ flush_signals(current);
+
+ if (ts->delay_inactive && (--ts->thread_count == 0)) {
+ spin_unlock_bh(&ts->ts_state_lock);
+ iscsi_del_ts_from_active_list(ts);
+
+ if (!iscsit_global->in_shutdown)
+ iscsi_deallocate_extra_thread_sets();
+
+ iscsi_add_ts_to_inactive_list(ts);
+ spin_lock_bh(&ts->ts_state_lock);
+ }
+
+ if ((ts->status == ISCSI_THREAD_SET_RESET) &&
+ (ts->thread_clear & ISCSI_CLEAR_RX_THREAD))
+ complete(&ts->rx_restart_comp);
+
+ ts->thread_clear &= ~ISCSI_CLEAR_RX_THREAD;
+ spin_unlock_bh(&ts->ts_state_lock);
+sleep:
+ ret = wait_for_completion_interruptible(&ts->rx_start_comp);
+ if (ret != 0)
+ return NULL;
+
+ if (iscsi_signal_thread_pre_handler(ts) < 0)
+ return NULL;
+
+ iscsi_check_to_add_additional_sets();
+
+ spin_lock_bh(&ts->ts_state_lock);
+ if (!ts->conn) {
+ pr_err("struct iscsi_thread_set->conn is NULL for"
+ " RX thread_id: %s/%d\n", current->comm, current->pid);
+ spin_unlock_bh(&ts->ts_state_lock);
+ return NULL;
+ }
+ ts->thread_clear |= ISCSI_CLEAR_RX_THREAD;
+ spin_unlock_bh(&ts->ts_state_lock);
+
+ up(&ts->ts_activate_sem);
+
+ return ts->conn;
+}
+
+struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *ts)
+{
+ int ret;
+
+ spin_lock_bh(&ts->ts_state_lock);
+ if (ts->create_threads) {
+ spin_unlock_bh(&ts->ts_state_lock);
+ goto sleep;
+ }
+
+ if (ts->status != ISCSI_THREAD_SET_DIE)
+ flush_signals(current);
+
+ if (ts->delay_inactive && (--ts->thread_count == 0)) {
+ spin_unlock_bh(&ts->ts_state_lock);
+ iscsi_del_ts_from_active_list(ts);
+
+ if (!iscsit_global->in_shutdown)
+ iscsi_deallocate_extra_thread_sets();
+
+ iscsi_add_ts_to_inactive_list(ts);
+ spin_lock_bh(&ts->ts_state_lock);
+ }
+ if ((ts->status == ISCSI_THREAD_SET_RESET) &&
+ (ts->thread_clear & ISCSI_CLEAR_TX_THREAD))
+ complete(&ts->tx_restart_comp);
+
+ ts->thread_clear &= ~ISCSI_CLEAR_TX_THREAD;
+ spin_unlock_bh(&ts->ts_state_lock);
+sleep:
+ ret = wait_for_completion_interruptible(&ts->tx_start_comp);
+ if (ret != 0)
+ return NULL;
+
+ if (iscsi_signal_thread_pre_handler(ts) < 0)
+ return NULL;
+
+ iscsi_check_to_add_additional_sets();
+
+ spin_lock_bh(&ts->ts_state_lock);
+ if (!ts->conn) {
+ pr_err("struct iscsi_thread_set->conn is NULL for"
+ " TX thread_id: %s/%d\n", current->comm, current->pid);
+ spin_unlock_bh(&ts->ts_state_lock);
+ return NULL;
+ }
+ ts->thread_clear |= ISCSI_CLEAR_TX_THREAD;
+ spin_unlock_bh(&ts->ts_state_lock);
+
+ up(&ts->ts_activate_sem);
+
+ return ts->conn;
+}
+
+int iscsi_thread_set_init(void)
+{
+ int size;
+
+ iscsit_global->ts_bitmap_count = ISCSI_TS_BITMAP_BITS;
+
+ size = BITS_TO_LONGS(iscsit_global->ts_bitmap_count) * sizeof(long);
+ iscsit_global->ts_bitmap = kzalloc(size, GFP_KERNEL);
+ if (!iscsit_global->ts_bitmap) {
+ pr_err("Unable to allocate iscsit_global->ts_bitmap\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void iscsi_thread_set_free(void)
+{
+ kfree(iscsit_global->ts_bitmap);
+}
diff --git a/drivers/target/iscsi/iscsi_target_tq.h b/drivers/target/iscsi/iscsi_target_tq.h
new file mode 100644
index 00000000000..cc1eede5ab3
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_tq.h
@@ -0,0 +1,84 @@
+#ifndef ISCSI_THREAD_QUEUE_H
+#define ISCSI_THREAD_QUEUE_H
+
+/*
+ * Defines for thread sets.
+ */
+extern int iscsi_thread_set_force_reinstatement(struct iscsi_conn *);
+extern int iscsi_allocate_thread_sets(u32);
+extern void iscsi_deallocate_thread_sets(void);
+extern void iscsi_activate_thread_set(struct iscsi_conn *, struct iscsi_thread_set *);
+extern struct iscsi_thread_set *iscsi_get_thread_set(void);
+extern void iscsi_set_thread_clear(struct iscsi_conn *, u8);
+extern void iscsi_set_thread_set_signal(struct iscsi_conn *, u8);
+extern int iscsi_release_thread_set(struct iscsi_conn *);
+extern struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *);
+extern struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *);
+extern int iscsi_thread_set_init(void);
+extern void iscsi_thread_set_free(void);
+
+extern int iscsi_target_tx_thread(void *);
+extern int iscsi_target_rx_thread(void *);
+
+#define TARGET_THREAD_SET_COUNT 4
+
+#define ISCSI_RX_THREAD 1
+#define ISCSI_TX_THREAD 2
+#define ISCSI_RX_THREAD_NAME "iscsi_trx"
+#define ISCSI_TX_THREAD_NAME "iscsi_ttx"
+#define ISCSI_BLOCK_RX_THREAD 0x1
+#define ISCSI_BLOCK_TX_THREAD 0x2
+#define ISCSI_CLEAR_RX_THREAD 0x1
+#define ISCSI_CLEAR_TX_THREAD 0x2
+#define ISCSI_SIGNAL_RX_THREAD 0x1
+#define ISCSI_SIGNAL_TX_THREAD 0x2
+
+/* struct iscsi_thread_set->status */
+#define ISCSI_THREAD_SET_FREE 1
+#define ISCSI_THREAD_SET_ACTIVE 2
+#define ISCSI_THREAD_SET_DIE 3
+#define ISCSI_THREAD_SET_RESET 4
+#define ISCSI_THREAD_SET_DEALLOCATE_THREADS 5
+
+/* By default allow a maximum of 32K iSCSI connections */
+#define ISCSI_TS_BITMAP_BITS 32768
+
+struct iscsi_thread_set {
+ /* flags used for blocking and restarting sets */
+ int blocked_threads;
+ /* flag for creating threads */
+ int create_threads;
+ /* flag for delaying readding to inactive list */
+ int delay_inactive;
+ /* status for thread set */
+ int status;
+ /* which threads have had signals sent */
+ int signal_sent;
+ /* flag for which threads exited first */
+ int thread_clear;
+ /* Active threads in the thread set */
+ int thread_count;
+ /* Unique thread ID */
+ u32 thread_id;
+ /* pointer to connection if set is active */
+ struct iscsi_conn *conn;
+ /* used for controlling ts state accesses */
+ spinlock_t ts_state_lock;
+ /* used for restarting thread queue */
+ struct completion rx_restart_comp;
+ /* used for restarting thread queue */
+ struct completion tx_restart_comp;
+ /* used for normal unused blocking */
+ struct completion rx_start_comp;
+ /* used for normal unused blocking */
+ struct completion tx_start_comp;
+ /* OS descriptor for rx thread */
+ struct task_struct *rx_thread;
+ /* OS descriptor for tx thread */
+ struct task_struct *tx_thread;
+ /* struct iscsi_thread_set in list list head*/
+ struct list_head ts_list;
+ struct semaphore ts_activate_sem;
+};
+
+#endif /*** ISCSI_THREAD_QUEUE_H ***/
diff --git a/drivers/target/iscsi/iscsi_target_transport.c b/drivers/target/iscsi/iscsi_target_transport.c
new file mode 100644
index 00000000000..882728fac30
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_transport.c
@@ -0,0 +1,55 @@
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <target/iscsi/iscsi_transport.h>
+
+static LIST_HEAD(g_transport_list);
+static DEFINE_MUTEX(transport_mutex);
+
+struct iscsit_transport *iscsit_get_transport(int type)
+{
+ struct iscsit_transport *t;
+
+ mutex_lock(&transport_mutex);
+ list_for_each_entry(t, &g_transport_list, t_node) {
+ if (t->transport_type == type) {
+ if (t->owner && !try_module_get(t->owner)) {
+ t = NULL;
+ }
+ mutex_unlock(&transport_mutex);
+ return t;
+ }
+ }
+ mutex_unlock(&transport_mutex);
+
+ return NULL;
+}
+
+void iscsit_put_transport(struct iscsit_transport *t)
+{
+ if (t->owner)
+ module_put(t->owner);
+}
+
+int iscsit_register_transport(struct iscsit_transport *t)
+{
+ INIT_LIST_HEAD(&t->t_node);
+
+ mutex_lock(&transport_mutex);
+ list_add_tail(&t->t_node, &g_transport_list);
+ mutex_unlock(&transport_mutex);
+
+ pr_debug("Registered iSCSI transport: %s\n", t->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_register_transport);
+
+void iscsit_unregister_transport(struct iscsit_transport *t)
+{
+ mutex_lock(&transport_mutex);
+ list_del(&t->t_node);
+ mutex_unlock(&transport_mutex);
+
+ pr_debug("Unregistered iSCSI transport: %s\n", t->name);
+}
+EXPORT_SYMBOL(iscsit_unregister_transport);
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c
new file mode 100644
index 00000000000..fd90b28f1d9
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_util.c
@@ -0,0 +1,1510 @@
+/*******************************************************************************
+ * This file contains the iSCSI Target specific utility functions.
+ *
+ * (c) Copyright 2007-2013 Datera, Inc.
+ *
+ * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+#include <linux/list.h>
+#include <linux/percpu_ida.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/iscsi_proto.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/iscsi/iscsi_transport.h>
+
+#include "iscsi_target_core.h"
+#include "iscsi_target_parameters.h"
+#include "iscsi_target_seq_pdu_list.h"
+#include "iscsi_target_datain_values.h"
+#include "iscsi_target_erl0.h"
+#include "iscsi_target_erl1.h"
+#include "iscsi_target_erl2.h"
+#include "iscsi_target_tpg.h"
+#include "iscsi_target_tq.h"
+#include "iscsi_target_util.h"
+#include "iscsi_target.h"
+
+#define PRINT_BUFF(buff, len) \
+{ \
+ int zzz; \
+ \
+ pr_debug("%d:\n", __LINE__); \
+ for (zzz = 0; zzz < len; zzz++) { \
+ if (zzz % 16 == 0) { \
+ if (zzz) \
+ pr_debug("\n"); \
+ pr_debug("%4i: ", zzz); \
+ } \
+ pr_debug("%02x ", (unsigned char) (buff)[zzz]); \
+ } \
+ if ((len + 1) % 16) \
+ pr_debug("\n"); \
+}
+
+extern struct list_head g_tiqn_list;
+extern spinlock_t tiqn_lock;
+
+/*
+ * Called with cmd->r2t_lock held.
+ */
+int iscsit_add_r2t_to_list(
+ struct iscsi_cmd *cmd,
+ u32 offset,
+ u32 xfer_len,
+ int recovery,
+ u32 r2t_sn)
+{
+ struct iscsi_r2t *r2t;
+
+ r2t = kmem_cache_zalloc(lio_r2t_cache, GFP_ATOMIC);
+ if (!r2t) {
+ pr_err("Unable to allocate memory for struct iscsi_r2t.\n");
+ return -1;
+ }
+ INIT_LIST_HEAD(&r2t->r2t_list);
+
+ r2t->recovery_r2t = recovery;
+ r2t->r2t_sn = (!r2t_sn) ? cmd->r2t_sn++ : r2t_sn;
+ r2t->offset = offset;
+ r2t->xfer_len = xfer_len;
+ list_add_tail(&r2t->r2t_list, &cmd->cmd_r2t_list);
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ iscsit_add_cmd_to_immediate_queue(cmd, cmd->conn, ISTATE_SEND_R2T);
+
+ spin_lock_bh(&cmd->r2t_lock);
+ return 0;
+}
+
+struct iscsi_r2t *iscsit_get_r2t_for_eos(
+ struct iscsi_cmd *cmd,
+ u32 offset,
+ u32 length)
+{
+ struct iscsi_r2t *r2t;
+
+ spin_lock_bh(&cmd->r2t_lock);
+ list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) {
+ if ((r2t->offset <= offset) &&
+ (r2t->offset + r2t->xfer_len) >= (offset + length)) {
+ spin_unlock_bh(&cmd->r2t_lock);
+ return r2t;
+ }
+ }
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ pr_err("Unable to locate R2T for Offset: %u, Length:"
+ " %u\n", offset, length);
+ return NULL;
+}
+
+struct iscsi_r2t *iscsit_get_r2t_from_list(struct iscsi_cmd *cmd)
+{
+ struct iscsi_r2t *r2t;
+
+ spin_lock_bh(&cmd->r2t_lock);
+ list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) {
+ if (!r2t->sent_r2t) {
+ spin_unlock_bh(&cmd->r2t_lock);
+ return r2t;
+ }
+ }
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ pr_err("Unable to locate next R2T to send for ITT:"
+ " 0x%08x.\n", cmd->init_task_tag);
+ return NULL;
+}
+
+/*
+ * Called with cmd->r2t_lock held.
+ */
+void iscsit_free_r2t(struct iscsi_r2t *r2t, struct iscsi_cmd *cmd)
+{
+ list_del(&r2t->r2t_list);
+ kmem_cache_free(lio_r2t_cache, r2t);
+}
+
+void iscsit_free_r2ts_from_list(struct iscsi_cmd *cmd)
+{
+ struct iscsi_r2t *r2t, *r2t_tmp;
+
+ spin_lock_bh(&cmd->r2t_lock);
+ list_for_each_entry_safe(r2t, r2t_tmp, &cmd->cmd_r2t_list, r2t_list)
+ iscsit_free_r2t(r2t, cmd);
+ spin_unlock_bh(&cmd->r2t_lock);
+}
+
+/*
+ * May be called from software interrupt (timer) context for allocating
+ * iSCSI NopINs.
+ */
+struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *conn, int state)
+{
+ struct iscsi_cmd *cmd;
+ struct se_session *se_sess = conn->sess->se_sess;
+ int size, tag;
+
+ tag = percpu_ida_alloc(&se_sess->sess_tag_pool, state);
+ if (tag < 0)
+ return NULL;
+
+ size = sizeof(struct iscsi_cmd) + conn->conn_transport->priv_size;
+ cmd = (struct iscsi_cmd *)(se_sess->sess_cmd_map + (tag * size));
+ memset(cmd, 0, size);
+
+ cmd->se_cmd.map_tag = tag;
+ cmd->conn = conn;
+ INIT_LIST_HEAD(&cmd->i_conn_node);
+ INIT_LIST_HEAD(&cmd->datain_list);
+ INIT_LIST_HEAD(&cmd->cmd_r2t_list);
+ spin_lock_init(&cmd->datain_lock);
+ spin_lock_init(&cmd->dataout_timeout_lock);
+ spin_lock_init(&cmd->istate_lock);
+ spin_lock_init(&cmd->error_lock);
+ spin_lock_init(&cmd->r2t_lock);
+
+ return cmd;
+}
+EXPORT_SYMBOL(iscsit_allocate_cmd);
+
+struct iscsi_seq *iscsit_get_seq_holder_for_datain(
+ struct iscsi_cmd *cmd,
+ u32 seq_send_order)
+{
+ u32 i;
+
+ for (i = 0; i < cmd->seq_count; i++)
+ if (cmd->seq_list[i].seq_send_order == seq_send_order)
+ return &cmd->seq_list[i];
+
+ return NULL;
+}
+
+struct iscsi_seq *iscsit_get_seq_holder_for_r2t(struct iscsi_cmd *cmd)
+{
+ u32 i;
+
+ if (!cmd->seq_list) {
+ pr_err("struct iscsi_cmd->seq_list is NULL!\n");
+ return NULL;
+ }
+
+ for (i = 0; i < cmd->seq_count; i++) {
+ if (cmd->seq_list[i].type != SEQTYPE_NORMAL)
+ continue;
+ if (cmd->seq_list[i].seq_send_order == cmd->seq_send_order) {
+ cmd->seq_send_order++;
+ return &cmd->seq_list[i];
+ }
+ }
+
+ return NULL;
+}
+
+struct iscsi_r2t *iscsit_get_holder_for_r2tsn(
+ struct iscsi_cmd *cmd,
+ u32 r2t_sn)
+{
+ struct iscsi_r2t *r2t;
+
+ spin_lock_bh(&cmd->r2t_lock);
+ list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) {
+ if (r2t->r2t_sn == r2t_sn) {
+ spin_unlock_bh(&cmd->r2t_lock);
+ return r2t;
+ }
+ }
+ spin_unlock_bh(&cmd->r2t_lock);
+
+ return NULL;
+}
+
+static inline int iscsit_check_received_cmdsn(struct iscsi_session *sess, u32 cmdsn)
+{
+ int ret;
+
+ /*
+ * This is the proper method of checking received CmdSN against
+ * ExpCmdSN and MaxCmdSN values, as well as accounting for out
+ * or order CmdSNs due to multiple connection sessions and/or
+ * CRC failures.
+ */
+ if (iscsi_sna_gt(cmdsn, sess->max_cmd_sn)) {
+ pr_err("Received CmdSN: 0x%08x is greater than"
+ " MaxCmdSN: 0x%08x, ignoring.\n", cmdsn,
+ sess->max_cmd_sn);
+ ret = CMDSN_MAXCMDSN_OVERRUN;
+
+ } else if (cmdsn == sess->exp_cmd_sn) {
+ sess->exp_cmd_sn++;
+ pr_debug("Received CmdSN matches ExpCmdSN,"
+ " incremented ExpCmdSN to: 0x%08x\n",
+ sess->exp_cmd_sn);
+ ret = CMDSN_NORMAL_OPERATION;
+
+ } else if (iscsi_sna_gt(cmdsn, sess->exp_cmd_sn)) {
+ pr_debug("Received CmdSN: 0x%08x is greater"
+ " than ExpCmdSN: 0x%08x, not acknowledging.\n",
+ cmdsn, sess->exp_cmd_sn);
+ ret = CMDSN_HIGHER_THAN_EXP;
+
+ } else {
+ pr_err("Received CmdSN: 0x%08x is less than"
+ " ExpCmdSN: 0x%08x, ignoring.\n", cmdsn,
+ sess->exp_cmd_sn);
+ ret = CMDSN_LOWER_THAN_EXP;
+ }
+
+ return ret;
+}
+
+/*
+ * Commands may be received out of order if MC/S is in use.
+ * Ensure they are executed in CmdSN order.
+ */
+int iscsit_sequence_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ unsigned char *buf, __be32 cmdsn)
+{
+ int ret, cmdsn_ret;
+ bool reject = false;
+ u8 reason = ISCSI_REASON_BOOKMARK_NO_RESOURCES;
+
+ mutex_lock(&conn->sess->cmdsn_mutex);
+
+ cmdsn_ret = iscsit_check_received_cmdsn(conn->sess, be32_to_cpu(cmdsn));
+ switch (cmdsn_ret) {
+ case CMDSN_NORMAL_OPERATION:
+ ret = iscsit_execute_cmd(cmd, 0);
+ if ((ret >= 0) && !list_empty(&conn->sess->sess_ooo_cmdsn_list))
+ iscsit_execute_ooo_cmdsns(conn->sess);
+ else if (ret < 0) {
+ reject = true;
+ ret = CMDSN_ERROR_CANNOT_RECOVER;
+ }
+ break;
+ case CMDSN_HIGHER_THAN_EXP:
+ ret = iscsit_handle_ooo_cmdsn(conn->sess, cmd, be32_to_cpu(cmdsn));
+ if (ret < 0) {
+ reject = true;
+ ret = CMDSN_ERROR_CANNOT_RECOVER;
+ break;
+ }
+ ret = CMDSN_HIGHER_THAN_EXP;
+ break;
+ case CMDSN_LOWER_THAN_EXP:
+ case CMDSN_MAXCMDSN_OVERRUN:
+ default:
+ cmd->i_state = ISTATE_REMOVE;
+ iscsit_add_cmd_to_immediate_queue(cmd, conn, cmd->i_state);
+ /*
+ * Existing callers for iscsit_sequence_cmd() will silently
+ * ignore commands with CMDSN_LOWER_THAN_EXP, so force this
+ * return for CMDSN_MAXCMDSN_OVERRUN as well..
+ */
+ ret = CMDSN_LOWER_THAN_EXP;
+ break;
+ }
+ mutex_unlock(&conn->sess->cmdsn_mutex);
+
+ if (reject)
+ iscsit_reject_cmd(cmd, reason, buf);
+
+ return ret;
+}
+EXPORT_SYMBOL(iscsit_sequence_cmd);
+
+int iscsit_check_unsolicited_dataout(struct iscsi_cmd *cmd, unsigned char *buf)
+{
+ struct iscsi_conn *conn = cmd->conn;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct iscsi_data *hdr = (struct iscsi_data *) buf;
+ u32 payload_length = ntoh24(hdr->dlength);
+
+ if (conn->sess->sess_ops->InitialR2T) {
+ pr_err("Received unexpected unsolicited data"
+ " while InitialR2T=Yes, protocol error.\n");
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_UNEXPECTED_UNSOLICITED_DATA, 0);
+ return -1;
+ }
+
+ if ((cmd->first_burst_len + payload_length) >
+ conn->sess->sess_ops->FirstBurstLength) {
+ pr_err("Total %u bytes exceeds FirstBurstLength: %u"
+ " for this Unsolicited DataOut Burst.\n",
+ (cmd->first_burst_len + payload_length),
+ conn->sess->sess_ops->FirstBurstLength);
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_INCORRECT_AMOUNT_OF_DATA, 0);
+ return -1;
+ }
+
+ if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL))
+ return 0;
+
+ if (((cmd->first_burst_len + payload_length) != cmd->se_cmd.data_length) &&
+ ((cmd->first_burst_len + payload_length) !=
+ conn->sess->sess_ops->FirstBurstLength)) {
+ pr_err("Unsolicited non-immediate data received %u"
+ " does not equal FirstBurstLength: %u, and does"
+ " not equal ExpXferLen %u.\n",
+ (cmd->first_burst_len + payload_length),
+ conn->sess->sess_ops->FirstBurstLength, cmd->se_cmd.data_length);
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_INCORRECT_AMOUNT_OF_DATA, 0);
+ return -1;
+ }
+ return 0;
+}
+
+struct iscsi_cmd *iscsit_find_cmd_from_itt(
+ struct iscsi_conn *conn,
+ itt_t init_task_tag)
+{
+ struct iscsi_cmd *cmd;
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_for_each_entry(cmd, &conn->conn_cmd_list, i_conn_node) {
+ if (cmd->init_task_tag == init_task_tag) {
+ spin_unlock_bh(&conn->cmd_lock);
+ return cmd;
+ }
+ }
+ spin_unlock_bh(&conn->cmd_lock);
+
+ pr_err("Unable to locate ITT: 0x%08x on CID: %hu",
+ init_task_tag, conn->cid);
+ return NULL;
+}
+
+struct iscsi_cmd *iscsit_find_cmd_from_itt_or_dump(
+ struct iscsi_conn *conn,
+ itt_t init_task_tag,
+ u32 length)
+{
+ struct iscsi_cmd *cmd;
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_for_each_entry(cmd, &conn->conn_cmd_list, i_conn_node) {
+ if (cmd->init_task_tag == init_task_tag) {
+ spin_unlock_bh(&conn->cmd_lock);
+ return cmd;
+ }
+ }
+ spin_unlock_bh(&conn->cmd_lock);
+
+ pr_err("Unable to locate ITT: 0x%08x on CID: %hu,"
+ " dumping payload\n", init_task_tag, conn->cid);
+ if (length)
+ iscsit_dump_data_payload(conn, length, 1);
+
+ return NULL;
+}
+
+struct iscsi_cmd *iscsit_find_cmd_from_ttt(
+ struct iscsi_conn *conn,
+ u32 targ_xfer_tag)
+{
+ struct iscsi_cmd *cmd = NULL;
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_for_each_entry(cmd, &conn->conn_cmd_list, i_conn_node) {
+ if (cmd->targ_xfer_tag == targ_xfer_tag) {
+ spin_unlock_bh(&conn->cmd_lock);
+ return cmd;
+ }
+ }
+ spin_unlock_bh(&conn->cmd_lock);
+
+ pr_err("Unable to locate TTT: 0x%08x on CID: %hu\n",
+ targ_xfer_tag, conn->cid);
+ return NULL;
+}
+
+int iscsit_find_cmd_for_recovery(
+ struct iscsi_session *sess,
+ struct iscsi_cmd **cmd_ptr,
+ struct iscsi_conn_recovery **cr_ptr,
+ itt_t init_task_tag)
+{
+ struct iscsi_cmd *cmd = NULL;
+ struct iscsi_conn_recovery *cr;
+ /*
+ * Scan through the inactive connection recovery list's command list.
+ * If init_task_tag matches the command is still alligent.
+ */
+ spin_lock(&sess->cr_i_lock);
+ list_for_each_entry(cr, &sess->cr_inactive_list, cr_list) {
+ spin_lock(&cr->conn_recovery_cmd_lock);
+ list_for_each_entry(cmd, &cr->conn_recovery_cmd_list, i_conn_node) {
+ if (cmd->init_task_tag == init_task_tag) {
+ spin_unlock(&cr->conn_recovery_cmd_lock);
+ spin_unlock(&sess->cr_i_lock);
+
+ *cr_ptr = cr;
+ *cmd_ptr = cmd;
+ return -2;
+ }
+ }
+ spin_unlock(&cr->conn_recovery_cmd_lock);
+ }
+ spin_unlock(&sess->cr_i_lock);
+ /*
+ * Scan through the active connection recovery list's command list.
+ * If init_task_tag matches the command is ready to be reassigned.
+ */
+ spin_lock(&sess->cr_a_lock);
+ list_for_each_entry(cr, &sess->cr_active_list, cr_list) {
+ spin_lock(&cr->conn_recovery_cmd_lock);
+ list_for_each_entry(cmd, &cr->conn_recovery_cmd_list, i_conn_node) {
+ if (cmd->init_task_tag == init_task_tag) {
+ spin_unlock(&cr->conn_recovery_cmd_lock);
+ spin_unlock(&sess->cr_a_lock);
+
+ *cr_ptr = cr;
+ *cmd_ptr = cmd;
+ return 0;
+ }
+ }
+ spin_unlock(&cr->conn_recovery_cmd_lock);
+ }
+ spin_unlock(&sess->cr_a_lock);
+
+ return -1;
+}
+
+void iscsit_add_cmd_to_immediate_queue(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn,
+ u8 state)
+{
+ struct iscsi_queue_req *qr;
+
+ qr = kmem_cache_zalloc(lio_qr_cache, GFP_ATOMIC);
+ if (!qr) {
+ pr_err("Unable to allocate memory for"
+ " struct iscsi_queue_req\n");
+ return;
+ }
+ INIT_LIST_HEAD(&qr->qr_list);
+ qr->cmd = cmd;
+ qr->state = state;
+
+ spin_lock_bh(&conn->immed_queue_lock);
+ list_add_tail(&qr->qr_list, &conn->immed_queue_list);
+ atomic_inc(&cmd->immed_queue_count);
+ atomic_set(&conn->check_immediate_queue, 1);
+ spin_unlock_bh(&conn->immed_queue_lock);
+
+ wake_up(&conn->queues_wq);
+}
+
+struct iscsi_queue_req *iscsit_get_cmd_from_immediate_queue(struct iscsi_conn *conn)
+{
+ struct iscsi_queue_req *qr;
+
+ spin_lock_bh(&conn->immed_queue_lock);
+ if (list_empty(&conn->immed_queue_list)) {
+ spin_unlock_bh(&conn->immed_queue_lock);
+ return NULL;
+ }
+ qr = list_first_entry(&conn->immed_queue_list,
+ struct iscsi_queue_req, qr_list);
+
+ list_del(&qr->qr_list);
+ if (qr->cmd)
+ atomic_dec(&qr->cmd->immed_queue_count);
+ spin_unlock_bh(&conn->immed_queue_lock);
+
+ return qr;
+}
+
+static void iscsit_remove_cmd_from_immediate_queue(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_queue_req *qr, *qr_tmp;
+
+ spin_lock_bh(&conn->immed_queue_lock);
+ if (!atomic_read(&cmd->immed_queue_count)) {
+ spin_unlock_bh(&conn->immed_queue_lock);
+ return;
+ }
+
+ list_for_each_entry_safe(qr, qr_tmp, &conn->immed_queue_list, qr_list) {
+ if (qr->cmd != cmd)
+ continue;
+
+ atomic_dec(&qr->cmd->immed_queue_count);
+ list_del(&qr->qr_list);
+ kmem_cache_free(lio_qr_cache, qr);
+ }
+ spin_unlock_bh(&conn->immed_queue_lock);
+
+ if (atomic_read(&cmd->immed_queue_count)) {
+ pr_err("ITT: 0x%08x immed_queue_count: %d\n",
+ cmd->init_task_tag,
+ atomic_read(&cmd->immed_queue_count));
+ }
+}
+
+void iscsit_add_cmd_to_response_queue(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn,
+ u8 state)
+{
+ struct iscsi_queue_req *qr;
+
+ qr = kmem_cache_zalloc(lio_qr_cache, GFP_ATOMIC);
+ if (!qr) {
+ pr_err("Unable to allocate memory for"
+ " struct iscsi_queue_req\n");
+ return;
+ }
+ INIT_LIST_HEAD(&qr->qr_list);
+ qr->cmd = cmd;
+ qr->state = state;
+
+ spin_lock_bh(&conn->response_queue_lock);
+ list_add_tail(&qr->qr_list, &conn->response_queue_list);
+ atomic_inc(&cmd->response_queue_count);
+ spin_unlock_bh(&conn->response_queue_lock);
+
+ wake_up(&conn->queues_wq);
+}
+
+struct iscsi_queue_req *iscsit_get_cmd_from_response_queue(struct iscsi_conn *conn)
+{
+ struct iscsi_queue_req *qr;
+
+ spin_lock_bh(&conn->response_queue_lock);
+ if (list_empty(&conn->response_queue_list)) {
+ spin_unlock_bh(&conn->response_queue_lock);
+ return NULL;
+ }
+
+ qr = list_first_entry(&conn->response_queue_list,
+ struct iscsi_queue_req, qr_list);
+
+ list_del(&qr->qr_list);
+ if (qr->cmd)
+ atomic_dec(&qr->cmd->response_queue_count);
+ spin_unlock_bh(&conn->response_queue_lock);
+
+ return qr;
+}
+
+static void iscsit_remove_cmd_from_response_queue(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn)
+{
+ struct iscsi_queue_req *qr, *qr_tmp;
+
+ spin_lock_bh(&conn->response_queue_lock);
+ if (!atomic_read(&cmd->response_queue_count)) {
+ spin_unlock_bh(&conn->response_queue_lock);
+ return;
+ }
+
+ list_for_each_entry_safe(qr, qr_tmp, &conn->response_queue_list,
+ qr_list) {
+ if (qr->cmd != cmd)
+ continue;
+
+ atomic_dec(&qr->cmd->response_queue_count);
+ list_del(&qr->qr_list);
+ kmem_cache_free(lio_qr_cache, qr);
+ }
+ spin_unlock_bh(&conn->response_queue_lock);
+
+ if (atomic_read(&cmd->response_queue_count)) {
+ pr_err("ITT: 0x%08x response_queue_count: %d\n",
+ cmd->init_task_tag,
+ atomic_read(&cmd->response_queue_count));
+ }
+}
+
+bool iscsit_conn_all_queues_empty(struct iscsi_conn *conn)
+{
+ bool empty;
+
+ spin_lock_bh(&conn->immed_queue_lock);
+ empty = list_empty(&conn->immed_queue_list);
+ spin_unlock_bh(&conn->immed_queue_lock);
+
+ if (!empty)
+ return empty;
+
+ spin_lock_bh(&conn->response_queue_lock);
+ empty = list_empty(&conn->response_queue_list);
+ spin_unlock_bh(&conn->response_queue_lock);
+
+ return empty;
+}
+
+void iscsit_free_queue_reqs_for_conn(struct iscsi_conn *conn)
+{
+ struct iscsi_queue_req *qr, *qr_tmp;
+
+ spin_lock_bh(&conn->immed_queue_lock);
+ list_for_each_entry_safe(qr, qr_tmp, &conn->immed_queue_list, qr_list) {
+ list_del(&qr->qr_list);
+ if (qr->cmd)
+ atomic_dec(&qr->cmd->immed_queue_count);
+
+ kmem_cache_free(lio_qr_cache, qr);
+ }
+ spin_unlock_bh(&conn->immed_queue_lock);
+
+ spin_lock_bh(&conn->response_queue_lock);
+ list_for_each_entry_safe(qr, qr_tmp, &conn->response_queue_list,
+ qr_list) {
+ list_del(&qr->qr_list);
+ if (qr->cmd)
+ atomic_dec(&qr->cmd->response_queue_count);
+
+ kmem_cache_free(lio_qr_cache, qr);
+ }
+ spin_unlock_bh(&conn->response_queue_lock);
+}
+
+void iscsit_release_cmd(struct iscsi_cmd *cmd)
+{
+ struct iscsi_session *sess;
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+
+ if (cmd->conn)
+ sess = cmd->conn->sess;
+ else
+ sess = cmd->sess;
+
+ BUG_ON(!sess || !sess->se_sess);
+
+ kfree(cmd->buf_ptr);
+ kfree(cmd->pdu_list);
+ kfree(cmd->seq_list);
+ kfree(cmd->tmr_req);
+ kfree(cmd->iov_data);
+ kfree(cmd->text_in_ptr);
+
+ percpu_ida_free(&sess->se_sess->sess_tag_pool, se_cmd->map_tag);
+}
+EXPORT_SYMBOL(iscsit_release_cmd);
+
+void __iscsit_free_cmd(struct iscsi_cmd *cmd, bool scsi_cmd,
+ bool check_queues)
+{
+ struct iscsi_conn *conn = cmd->conn;
+
+ if (scsi_cmd) {
+ if (cmd->data_direction == DMA_TO_DEVICE) {
+ iscsit_stop_dataout_timer(cmd);
+ iscsit_free_r2ts_from_list(cmd);
+ }
+ if (cmd->data_direction == DMA_FROM_DEVICE)
+ iscsit_free_all_datain_reqs(cmd);
+ }
+
+ if (conn && check_queues) {
+ iscsit_remove_cmd_from_immediate_queue(cmd, conn);
+ iscsit_remove_cmd_from_response_queue(cmd, conn);
+ }
+}
+
+void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown)
+{
+ struct se_cmd *se_cmd = NULL;
+ int rc;
+ /*
+ * Determine if a struct se_cmd is associated with
+ * this struct iscsi_cmd.
+ */
+ switch (cmd->iscsi_opcode) {
+ case ISCSI_OP_SCSI_CMD:
+ se_cmd = &cmd->se_cmd;
+ __iscsit_free_cmd(cmd, true, shutdown);
+ /*
+ * Fallthrough
+ */
+ case ISCSI_OP_SCSI_TMFUNC:
+ rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown);
+ if (!rc && shutdown && se_cmd && se_cmd->se_sess) {
+ __iscsit_free_cmd(cmd, true, shutdown);
+ target_put_sess_cmd(se_cmd->se_sess, se_cmd);
+ }
+ break;
+ case ISCSI_OP_REJECT:
+ /*
+ * Handle special case for REJECT when iscsi_add_reject*() has
+ * overwritten the original iscsi_opcode assignment, and the
+ * associated cmd->se_cmd needs to be released.
+ */
+ if (cmd->se_cmd.se_tfo != NULL) {
+ se_cmd = &cmd->se_cmd;
+ __iscsit_free_cmd(cmd, true, shutdown);
+
+ rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown);
+ if (!rc && shutdown && se_cmd->se_sess) {
+ __iscsit_free_cmd(cmd, true, shutdown);
+ target_put_sess_cmd(se_cmd->se_sess, se_cmd);
+ }
+ break;
+ }
+ /* Fall-through */
+ default:
+ __iscsit_free_cmd(cmd, false, shutdown);
+ iscsit_release_cmd(cmd);
+ break;
+ }
+}
+
+int iscsit_check_session_usage_count(struct iscsi_session *sess)
+{
+ spin_lock_bh(&sess->session_usage_lock);
+ if (sess->session_usage_count != 0) {
+ sess->session_waiting_on_uc = 1;
+ spin_unlock_bh(&sess->session_usage_lock);
+ if (in_interrupt())
+ return 2;
+
+ wait_for_completion(&sess->session_waiting_on_uc_comp);
+ return 1;
+ }
+ spin_unlock_bh(&sess->session_usage_lock);
+
+ return 0;
+}
+
+void iscsit_dec_session_usage_count(struct iscsi_session *sess)
+{
+ spin_lock_bh(&sess->session_usage_lock);
+ sess->session_usage_count--;
+
+ if (!sess->session_usage_count && sess->session_waiting_on_uc)
+ complete(&sess->session_waiting_on_uc_comp);
+
+ spin_unlock_bh(&sess->session_usage_lock);
+}
+
+void iscsit_inc_session_usage_count(struct iscsi_session *sess)
+{
+ spin_lock_bh(&sess->session_usage_lock);
+ sess->session_usage_count++;
+ spin_unlock_bh(&sess->session_usage_lock);
+}
+
+/*
+ * Setup conn->if_marker and conn->of_marker values based upon
+ * the initial marker-less interval. (see iSCSI v19 A.2)
+ */
+int iscsit_set_sync_and_steering_values(struct iscsi_conn *conn)
+{
+ int login_ifmarker_count = 0, login_ofmarker_count = 0, next_marker = 0;
+ /*
+ * IFMarkInt and OFMarkInt are negotiated as 32-bit words.
+ */
+ u32 IFMarkInt = (conn->conn_ops->IFMarkInt * 4);
+ u32 OFMarkInt = (conn->conn_ops->OFMarkInt * 4);
+
+ if (conn->conn_ops->OFMarker) {
+ /*
+ * Account for the first Login Command received not
+ * via iscsi_recv_msg().
+ */
+ conn->of_marker += ISCSI_HDR_LEN;
+ if (conn->of_marker <= OFMarkInt) {
+ conn->of_marker = (OFMarkInt - conn->of_marker);
+ } else {
+ login_ofmarker_count = (conn->of_marker / OFMarkInt);
+ next_marker = (OFMarkInt * (login_ofmarker_count + 1)) +
+ (login_ofmarker_count * MARKER_SIZE);
+ conn->of_marker = (next_marker - conn->of_marker);
+ }
+ conn->of_marker_offset = 0;
+ pr_debug("Setting OFMarker value to %u based on Initial"
+ " Markerless Interval.\n", conn->of_marker);
+ }
+
+ if (conn->conn_ops->IFMarker) {
+ if (conn->if_marker <= IFMarkInt) {
+ conn->if_marker = (IFMarkInt - conn->if_marker);
+ } else {
+ login_ifmarker_count = (conn->if_marker / IFMarkInt);
+ next_marker = (IFMarkInt * (login_ifmarker_count + 1)) +
+ (login_ifmarker_count * MARKER_SIZE);
+ conn->if_marker = (next_marker - conn->if_marker);
+ }
+ pr_debug("Setting IFMarker value to %u based on Initial"
+ " Markerless Interval.\n", conn->if_marker);
+ }
+
+ return 0;
+}
+
+struct iscsi_conn *iscsit_get_conn_from_cid(struct iscsi_session *sess, u16 cid)
+{
+ struct iscsi_conn *conn;
+
+ spin_lock_bh(&sess->conn_lock);
+ list_for_each_entry(conn, &sess->sess_conn_list, conn_list) {
+ if ((conn->cid == cid) &&
+ (conn->conn_state == TARG_CONN_STATE_LOGGED_IN)) {
+ iscsit_inc_conn_usage_count(conn);
+ spin_unlock_bh(&sess->conn_lock);
+ return conn;
+ }
+ }
+ spin_unlock_bh(&sess->conn_lock);
+
+ return NULL;
+}
+
+struct iscsi_conn *iscsit_get_conn_from_cid_rcfr(struct iscsi_session *sess, u16 cid)
+{
+ struct iscsi_conn *conn;
+
+ spin_lock_bh(&sess->conn_lock);
+ list_for_each_entry(conn, &sess->sess_conn_list, conn_list) {
+ if (conn->cid == cid) {
+ iscsit_inc_conn_usage_count(conn);
+ spin_lock(&conn->state_lock);
+ atomic_set(&conn->connection_wait_rcfr, 1);
+ spin_unlock(&conn->state_lock);
+ spin_unlock_bh(&sess->conn_lock);
+ return conn;
+ }
+ }
+ spin_unlock_bh(&sess->conn_lock);
+
+ return NULL;
+}
+
+void iscsit_check_conn_usage_count(struct iscsi_conn *conn)
+{
+ spin_lock_bh(&conn->conn_usage_lock);
+ if (conn->conn_usage_count != 0) {
+ conn->conn_waiting_on_uc = 1;
+ spin_unlock_bh(&conn->conn_usage_lock);
+
+ wait_for_completion(&conn->conn_waiting_on_uc_comp);
+ return;
+ }
+ spin_unlock_bh(&conn->conn_usage_lock);
+}
+
+void iscsit_dec_conn_usage_count(struct iscsi_conn *conn)
+{
+ spin_lock_bh(&conn->conn_usage_lock);
+ conn->conn_usage_count--;
+
+ if (!conn->conn_usage_count && conn->conn_waiting_on_uc)
+ complete(&conn->conn_waiting_on_uc_comp);
+
+ spin_unlock_bh(&conn->conn_usage_lock);
+}
+
+void iscsit_inc_conn_usage_count(struct iscsi_conn *conn)
+{
+ spin_lock_bh(&conn->conn_usage_lock);
+ conn->conn_usage_count++;
+ spin_unlock_bh(&conn->conn_usage_lock);
+}
+
+static int iscsit_add_nopin(struct iscsi_conn *conn, int want_response)
+{
+ u8 state;
+ struct iscsi_cmd *cmd;
+
+ cmd = iscsit_allocate_cmd(conn, TASK_RUNNING);
+ if (!cmd)
+ return -1;
+
+ cmd->iscsi_opcode = ISCSI_OP_NOOP_IN;
+ state = (want_response) ? ISTATE_SEND_NOPIN_WANT_RESPONSE :
+ ISTATE_SEND_NOPIN_NO_RESPONSE;
+ cmd->init_task_tag = RESERVED_ITT;
+ spin_lock_bh(&conn->sess->ttt_lock);
+ cmd->targ_xfer_tag = (want_response) ? conn->sess->targ_xfer_tag++ :
+ 0xFFFFFFFF;
+ if (want_response && (cmd->targ_xfer_tag == 0xFFFFFFFF))
+ cmd->targ_xfer_tag = conn->sess->targ_xfer_tag++;
+ spin_unlock_bh(&conn->sess->ttt_lock);
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ if (want_response)
+ iscsit_start_nopin_response_timer(conn);
+ iscsit_add_cmd_to_immediate_queue(cmd, conn, state);
+
+ return 0;
+}
+
+static void iscsit_handle_nopin_response_timeout(unsigned long data)
+{
+ struct iscsi_conn *conn = (struct iscsi_conn *) data;
+
+ iscsit_inc_conn_usage_count(conn);
+
+ spin_lock_bh(&conn->nopin_timer_lock);
+ if (conn->nopin_response_timer_flags & ISCSI_TF_STOP) {
+ spin_unlock_bh(&conn->nopin_timer_lock);
+ iscsit_dec_conn_usage_count(conn);
+ return;
+ }
+
+ pr_debug("Did not receive response to NOPIN on CID: %hu on"
+ " SID: %u, failing connection.\n", conn->cid,
+ conn->sess->sid);
+ conn->nopin_response_timer_flags &= ~ISCSI_TF_RUNNING;
+ spin_unlock_bh(&conn->nopin_timer_lock);
+
+ {
+ struct iscsi_portal_group *tpg = conn->sess->tpg;
+ struct iscsi_tiqn *tiqn = tpg->tpg_tiqn;
+
+ if (tiqn) {
+ spin_lock_bh(&tiqn->sess_err_stats.lock);
+ strcpy(tiqn->sess_err_stats.last_sess_fail_rem_name,
+ conn->sess->sess_ops->InitiatorName);
+ tiqn->sess_err_stats.last_sess_failure_type =
+ ISCSI_SESS_ERR_CXN_TIMEOUT;
+ tiqn->sess_err_stats.cxn_timeout_errors++;
+ atomic_long_inc(&conn->sess->conn_timeout_errors);
+ spin_unlock_bh(&tiqn->sess_err_stats.lock);
+ }
+ }
+
+ iscsit_cause_connection_reinstatement(conn, 0);
+ iscsit_dec_conn_usage_count(conn);
+}
+
+void iscsit_mod_nopin_response_timer(struct iscsi_conn *conn)
+{
+ struct iscsi_session *sess = conn->sess;
+ struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+
+ spin_lock_bh(&conn->nopin_timer_lock);
+ if (!(conn->nopin_response_timer_flags & ISCSI_TF_RUNNING)) {
+ spin_unlock_bh(&conn->nopin_timer_lock);
+ return;
+ }
+
+ mod_timer(&conn->nopin_response_timer,
+ (get_jiffies_64() + na->nopin_response_timeout * HZ));
+ spin_unlock_bh(&conn->nopin_timer_lock);
+}
+
+/*
+ * Called with conn->nopin_timer_lock held.
+ */
+void iscsit_start_nopin_response_timer(struct iscsi_conn *conn)
+{
+ struct iscsi_session *sess = conn->sess;
+ struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+
+ spin_lock_bh(&conn->nopin_timer_lock);
+ if (conn->nopin_response_timer_flags & ISCSI_TF_RUNNING) {
+ spin_unlock_bh(&conn->nopin_timer_lock);
+ return;
+ }
+
+ init_timer(&conn->nopin_response_timer);
+ conn->nopin_response_timer.expires =
+ (get_jiffies_64() + na->nopin_response_timeout * HZ);
+ conn->nopin_response_timer.data = (unsigned long)conn;
+ conn->nopin_response_timer.function = iscsit_handle_nopin_response_timeout;
+ conn->nopin_response_timer_flags &= ~ISCSI_TF_STOP;
+ conn->nopin_response_timer_flags |= ISCSI_TF_RUNNING;
+ add_timer(&conn->nopin_response_timer);
+
+ pr_debug("Started NOPIN Response Timer on CID: %d to %u"
+ " seconds\n", conn->cid, na->nopin_response_timeout);
+ spin_unlock_bh(&conn->nopin_timer_lock);
+}
+
+void iscsit_stop_nopin_response_timer(struct iscsi_conn *conn)
+{
+ spin_lock_bh(&conn->nopin_timer_lock);
+ if (!(conn->nopin_response_timer_flags & ISCSI_TF_RUNNING)) {
+ spin_unlock_bh(&conn->nopin_timer_lock);
+ return;
+ }
+ conn->nopin_response_timer_flags |= ISCSI_TF_STOP;
+ spin_unlock_bh(&conn->nopin_timer_lock);
+
+ del_timer_sync(&conn->nopin_response_timer);
+
+ spin_lock_bh(&conn->nopin_timer_lock);
+ conn->nopin_response_timer_flags &= ~ISCSI_TF_RUNNING;
+ spin_unlock_bh(&conn->nopin_timer_lock);
+}
+
+static void iscsit_handle_nopin_timeout(unsigned long data)
+{
+ struct iscsi_conn *conn = (struct iscsi_conn *) data;
+
+ iscsit_inc_conn_usage_count(conn);
+
+ spin_lock_bh(&conn->nopin_timer_lock);
+ if (conn->nopin_timer_flags & ISCSI_TF_STOP) {
+ spin_unlock_bh(&conn->nopin_timer_lock);
+ iscsit_dec_conn_usage_count(conn);
+ return;
+ }
+ conn->nopin_timer_flags &= ~ISCSI_TF_RUNNING;
+ spin_unlock_bh(&conn->nopin_timer_lock);
+
+ iscsit_add_nopin(conn, 1);
+ iscsit_dec_conn_usage_count(conn);
+}
+
+/*
+ * Called with conn->nopin_timer_lock held.
+ */
+void __iscsit_start_nopin_timer(struct iscsi_conn *conn)
+{
+ struct iscsi_session *sess = conn->sess;
+ struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+ /*
+ * NOPIN timeout is disabled.
+ */
+ if (!na->nopin_timeout)
+ return;
+
+ if (conn->nopin_timer_flags & ISCSI_TF_RUNNING)
+ return;
+
+ init_timer(&conn->nopin_timer);
+ conn->nopin_timer.expires = (get_jiffies_64() + na->nopin_timeout * HZ);
+ conn->nopin_timer.data = (unsigned long)conn;
+ conn->nopin_timer.function = iscsit_handle_nopin_timeout;
+ conn->nopin_timer_flags &= ~ISCSI_TF_STOP;
+ conn->nopin_timer_flags |= ISCSI_TF_RUNNING;
+ add_timer(&conn->nopin_timer);
+
+ pr_debug("Started NOPIN Timer on CID: %d at %u second"
+ " interval\n", conn->cid, na->nopin_timeout);
+}
+
+void iscsit_start_nopin_timer(struct iscsi_conn *conn)
+{
+ struct iscsi_session *sess = conn->sess;
+ struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess);
+ /*
+ * NOPIN timeout is disabled..
+ */
+ if (!na->nopin_timeout)
+ return;
+
+ spin_lock_bh(&conn->nopin_timer_lock);
+ if (conn->nopin_timer_flags & ISCSI_TF_RUNNING) {
+ spin_unlock_bh(&conn->nopin_timer_lock);
+ return;
+ }
+
+ init_timer(&conn->nopin_timer);
+ conn->nopin_timer.expires = (get_jiffies_64() + na->nopin_timeout * HZ);
+ conn->nopin_timer.data = (unsigned long)conn;
+ conn->nopin_timer.function = iscsit_handle_nopin_timeout;
+ conn->nopin_timer_flags &= ~ISCSI_TF_STOP;
+ conn->nopin_timer_flags |= ISCSI_TF_RUNNING;
+ add_timer(&conn->nopin_timer);
+
+ pr_debug("Started NOPIN Timer on CID: %d at %u second"
+ " interval\n", conn->cid, na->nopin_timeout);
+ spin_unlock_bh(&conn->nopin_timer_lock);
+}
+
+void iscsit_stop_nopin_timer(struct iscsi_conn *conn)
+{
+ spin_lock_bh(&conn->nopin_timer_lock);
+ if (!(conn->nopin_timer_flags & ISCSI_TF_RUNNING)) {
+ spin_unlock_bh(&conn->nopin_timer_lock);
+ return;
+ }
+ conn->nopin_timer_flags |= ISCSI_TF_STOP;
+ spin_unlock_bh(&conn->nopin_timer_lock);
+
+ del_timer_sync(&conn->nopin_timer);
+
+ spin_lock_bh(&conn->nopin_timer_lock);
+ conn->nopin_timer_flags &= ~ISCSI_TF_RUNNING;
+ spin_unlock_bh(&conn->nopin_timer_lock);
+}
+
+int iscsit_send_tx_data(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn,
+ int use_misc)
+{
+ int tx_sent, tx_size;
+ u32 iov_count;
+ struct kvec *iov;
+
+send_data:
+ tx_size = cmd->tx_size;
+
+ if (!use_misc) {
+ iov = &cmd->iov_data[0];
+ iov_count = cmd->iov_data_count;
+ } else {
+ iov = &cmd->iov_misc[0];
+ iov_count = cmd->iov_misc_count;
+ }
+
+ tx_sent = tx_data(conn, &iov[0], iov_count, tx_size);
+ if (tx_size != tx_sent) {
+ if (tx_sent == -EAGAIN) {
+ pr_err("tx_data() returned -EAGAIN\n");
+ goto send_data;
+ } else
+ return -1;
+ }
+ cmd->tx_size = 0;
+
+ return 0;
+}
+
+int iscsit_fe_sendpage_sg(
+ struct iscsi_cmd *cmd,
+ struct iscsi_conn *conn)
+{
+ struct scatterlist *sg = cmd->first_data_sg;
+ struct kvec iov;
+ u32 tx_hdr_size, data_len;
+ u32 offset = cmd->first_data_sg_off;
+ int tx_sent, iov_off;
+
+send_hdr:
+ tx_hdr_size = ISCSI_HDR_LEN;
+ if (conn->conn_ops->HeaderDigest)
+ tx_hdr_size += ISCSI_CRC_LEN;
+
+ iov.iov_base = cmd->pdu;
+ iov.iov_len = tx_hdr_size;
+
+ tx_sent = tx_data(conn, &iov, 1, tx_hdr_size);
+ if (tx_hdr_size != tx_sent) {
+ if (tx_sent == -EAGAIN) {
+ pr_err("tx_data() returned -EAGAIN\n");
+ goto send_hdr;
+ }
+ return -1;
+ }
+
+ data_len = cmd->tx_size - tx_hdr_size - cmd->padding;
+ /*
+ * Set iov_off used by padding and data digest tx_data() calls below
+ * in order to determine proper offset into cmd->iov_data[]
+ */
+ if (conn->conn_ops->DataDigest) {
+ data_len -= ISCSI_CRC_LEN;
+ if (cmd->padding)
+ iov_off = (cmd->iov_data_count - 2);
+ else
+ iov_off = (cmd->iov_data_count - 1);
+ } else {
+ iov_off = (cmd->iov_data_count - 1);
+ }
+ /*
+ * Perform sendpage() for each page in the scatterlist
+ */
+ while (data_len) {
+ u32 space = (sg->length - offset);
+ u32 sub_len = min_t(u32, data_len, space);
+send_pg:
+ tx_sent = conn->sock->ops->sendpage(conn->sock,
+ sg_page(sg), sg->offset + offset, sub_len, 0);
+ if (tx_sent != sub_len) {
+ if (tx_sent == -EAGAIN) {
+ pr_err("tcp_sendpage() returned"
+ " -EAGAIN\n");
+ goto send_pg;
+ }
+
+ pr_err("tcp_sendpage() failure: %d\n",
+ tx_sent);
+ return -1;
+ }
+
+ data_len -= sub_len;
+ offset = 0;
+ sg = sg_next(sg);
+ }
+
+send_padding:
+ if (cmd->padding) {
+ struct kvec *iov_p = &cmd->iov_data[iov_off++];
+
+ tx_sent = tx_data(conn, iov_p, 1, cmd->padding);
+ if (cmd->padding != tx_sent) {
+ if (tx_sent == -EAGAIN) {
+ pr_err("tx_data() returned -EAGAIN\n");
+ goto send_padding;
+ }
+ return -1;
+ }
+ }
+
+send_datacrc:
+ if (conn->conn_ops->DataDigest) {
+ struct kvec *iov_d = &cmd->iov_data[iov_off];
+
+ tx_sent = tx_data(conn, iov_d, 1, ISCSI_CRC_LEN);
+ if (ISCSI_CRC_LEN != tx_sent) {
+ if (tx_sent == -EAGAIN) {
+ pr_err("tx_data() returned -EAGAIN\n");
+ goto send_datacrc;
+ }
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This function is used for mainly sending a ISCSI_TARG_LOGIN_RSP PDU
+ * back to the Initiator when an expection condition occurs with the
+ * errors set in status_class and status_detail.
+ *
+ * Parameters: iSCSI Connection, Status Class, Status Detail.
+ * Returns: 0 on success, -1 on error.
+ */
+int iscsit_tx_login_rsp(struct iscsi_conn *conn, u8 status_class, u8 status_detail)
+{
+ struct iscsi_login_rsp *hdr;
+ struct iscsi_login *login = conn->conn_login;
+
+ login->login_failed = 1;
+ iscsit_collect_login_stats(conn, status_class, status_detail);
+
+ memset(&login->rsp[0], 0, ISCSI_HDR_LEN);
+
+ hdr = (struct iscsi_login_rsp *)&login->rsp[0];
+ hdr->opcode = ISCSI_OP_LOGIN_RSP;
+ hdr->status_class = status_class;
+ hdr->status_detail = status_detail;
+ hdr->itt = conn->login_itt;
+
+ return conn->conn_transport->iscsit_put_login_tx(conn, login, 0);
+}
+
+void iscsit_print_session_params(struct iscsi_session *sess)
+{
+ struct iscsi_conn *conn;
+
+ pr_debug("-----------------------------[Session Params for"
+ " SID: %u]-----------------------------\n", sess->sid);
+ spin_lock_bh(&sess->conn_lock);
+ list_for_each_entry(conn, &sess->sess_conn_list, conn_list)
+ iscsi_dump_conn_ops(conn->conn_ops);
+ spin_unlock_bh(&sess->conn_lock);
+
+ iscsi_dump_sess_ops(sess->sess_ops);
+}
+
+static int iscsit_do_rx_data(
+ struct iscsi_conn *conn,
+ struct iscsi_data_count *count)
+{
+ int data = count->data_length, rx_loop = 0, total_rx = 0, iov_len;
+ struct kvec *iov_p;
+ struct msghdr msg;
+
+ if (!conn || !conn->sock || !conn->conn_ops)
+ return -1;
+
+ memset(&msg, 0, sizeof(struct msghdr));
+
+ iov_p = count->iov;
+ iov_len = count->iov_count;
+
+ while (total_rx < data) {
+ rx_loop = kernel_recvmsg(conn->sock, &msg, iov_p, iov_len,
+ (data - total_rx), MSG_WAITALL);
+ if (rx_loop <= 0) {
+ pr_debug("rx_loop: %d total_rx: %d\n",
+ rx_loop, total_rx);
+ return rx_loop;
+ }
+ total_rx += rx_loop;
+ pr_debug("rx_loop: %d, total_rx: %d, data: %d\n",
+ rx_loop, total_rx, data);
+ }
+
+ return total_rx;
+}
+
+static int iscsit_do_tx_data(
+ struct iscsi_conn *conn,
+ struct iscsi_data_count *count)
+{
+ int data = count->data_length, total_tx = 0, tx_loop = 0, iov_len;
+ struct kvec *iov_p;
+ struct msghdr msg;
+
+ if (!conn || !conn->sock || !conn->conn_ops)
+ return -1;
+
+ if (data <= 0) {
+ pr_err("Data length is: %d\n", data);
+ return -1;
+ }
+
+ memset(&msg, 0, sizeof(struct msghdr));
+
+ iov_p = count->iov;
+ iov_len = count->iov_count;
+
+ while (total_tx < data) {
+ tx_loop = kernel_sendmsg(conn->sock, &msg, iov_p, iov_len,
+ (data - total_tx));
+ if (tx_loop <= 0) {
+ pr_debug("tx_loop: %d total_tx %d\n",
+ tx_loop, total_tx);
+ return tx_loop;
+ }
+ total_tx += tx_loop;
+ pr_debug("tx_loop: %d, total_tx: %d, data: %d\n",
+ tx_loop, total_tx, data);
+ }
+
+ return total_tx;
+}
+
+int rx_data(
+ struct iscsi_conn *conn,
+ struct kvec *iov,
+ int iov_count,
+ int data)
+{
+ struct iscsi_data_count c;
+
+ if (!conn || !conn->sock || !conn->conn_ops)
+ return -1;
+
+ memset(&c, 0, sizeof(struct iscsi_data_count));
+ c.iov = iov;
+ c.iov_count = iov_count;
+ c.data_length = data;
+ c.type = ISCSI_RX_DATA;
+
+ return iscsit_do_rx_data(conn, &c);
+}
+
+int tx_data(
+ struct iscsi_conn *conn,
+ struct kvec *iov,
+ int iov_count,
+ int data)
+{
+ struct iscsi_data_count c;
+
+ if (!conn || !conn->sock || !conn->conn_ops)
+ return -1;
+
+ memset(&c, 0, sizeof(struct iscsi_data_count));
+ c.iov = iov;
+ c.iov_count = iov_count;
+ c.data_length = data;
+ c.type = ISCSI_TX_DATA;
+
+ return iscsit_do_tx_data(conn, &c);
+}
+
+void iscsit_collect_login_stats(
+ struct iscsi_conn *conn,
+ u8 status_class,
+ u8 status_detail)
+{
+ struct iscsi_param *intrname = NULL;
+ struct iscsi_tiqn *tiqn;
+ struct iscsi_login_stats *ls;
+
+ tiqn = iscsit_snmp_get_tiqn(conn);
+ if (!tiqn)
+ return;
+
+ ls = &tiqn->login_stats;
+
+ spin_lock(&ls->lock);
+ if (!strcmp(conn->login_ip, ls->last_intr_fail_ip_addr) &&
+ ((get_jiffies_64() - ls->last_fail_time) < 10)) {
+ /* We already have the failure info for this login */
+ spin_unlock(&ls->lock);
+ return;
+ }
+
+ if (status_class == ISCSI_STATUS_CLS_SUCCESS)
+ ls->accepts++;
+ else if (status_class == ISCSI_STATUS_CLS_REDIRECT) {
+ ls->redirects++;
+ ls->last_fail_type = ISCSI_LOGIN_FAIL_REDIRECT;
+ } else if ((status_class == ISCSI_STATUS_CLS_INITIATOR_ERR) &&
+ (status_detail == ISCSI_LOGIN_STATUS_AUTH_FAILED)) {
+ ls->authenticate_fails++;
+ ls->last_fail_type = ISCSI_LOGIN_FAIL_AUTHENTICATE;
+ } else if ((status_class == ISCSI_STATUS_CLS_INITIATOR_ERR) &&
+ (status_detail == ISCSI_LOGIN_STATUS_TGT_FORBIDDEN)) {
+ ls->authorize_fails++;
+ ls->last_fail_type = ISCSI_LOGIN_FAIL_AUTHORIZE;
+ } else if ((status_class == ISCSI_STATUS_CLS_INITIATOR_ERR) &&
+ (status_detail == ISCSI_LOGIN_STATUS_INIT_ERR)) {
+ ls->negotiate_fails++;
+ ls->last_fail_type = ISCSI_LOGIN_FAIL_NEGOTIATE;
+ } else {
+ ls->other_fails++;
+ ls->last_fail_type = ISCSI_LOGIN_FAIL_OTHER;
+ }
+
+ /* Save initiator name, ip address and time, if it is a failed login */
+ if (status_class != ISCSI_STATUS_CLS_SUCCESS) {
+ if (conn->param_list)
+ intrname = iscsi_find_param_from_key(INITIATORNAME,
+ conn->param_list);
+ strcpy(ls->last_intr_fail_name,
+ (intrname ? intrname->value : "Unknown"));
+
+ ls->last_intr_fail_ip_family = conn->login_family;
+
+ snprintf(ls->last_intr_fail_ip_addr, IPV6_ADDRESS_SPACE,
+ "%s", conn->login_ip);
+ ls->last_fail_time = get_jiffies_64();
+ }
+
+ spin_unlock(&ls->lock);
+}
+
+struct iscsi_tiqn *iscsit_snmp_get_tiqn(struct iscsi_conn *conn)
+{
+ struct iscsi_portal_group *tpg;
+
+ if (!conn || !conn->sess)
+ return NULL;
+
+ tpg = conn->sess->tpg;
+ if (!tpg)
+ return NULL;
+
+ if (!tpg->tpg_tiqn)
+ return NULL;
+
+ return tpg->tpg_tiqn;
+}
diff --git a/drivers/target/iscsi/iscsi_target_util.h b/drivers/target/iscsi/iscsi_target_util.h
new file mode 100644
index 00000000000..a68508c4fec
--- /dev/null
+++ b/drivers/target/iscsi/iscsi_target_util.h
@@ -0,0 +1,62 @@
+#ifndef ISCSI_TARGET_UTIL_H
+#define ISCSI_TARGET_UTIL_H
+
+#define MARKER_SIZE 8
+
+extern int iscsit_add_r2t_to_list(struct iscsi_cmd *, u32, u32, int, u32);
+extern struct iscsi_r2t *iscsit_get_r2t_for_eos(struct iscsi_cmd *, u32, u32);
+extern struct iscsi_r2t *iscsit_get_r2t_from_list(struct iscsi_cmd *);
+extern void iscsit_free_r2t(struct iscsi_r2t *, struct iscsi_cmd *);
+extern void iscsit_free_r2ts_from_list(struct iscsi_cmd *);
+extern struct iscsi_cmd *iscsit_alloc_cmd(struct iscsi_conn *, gfp_t);
+extern struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *, int);
+extern struct iscsi_seq *iscsit_get_seq_holder_for_datain(struct iscsi_cmd *, u32);
+extern struct iscsi_seq *iscsit_get_seq_holder_for_r2t(struct iscsi_cmd *);
+extern struct iscsi_r2t *iscsit_get_holder_for_r2tsn(struct iscsi_cmd *, u32);
+extern int iscsit_sequence_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ unsigned char * ,__be32 cmdsn);
+extern int iscsit_check_unsolicited_dataout(struct iscsi_cmd *, unsigned char *);
+extern struct iscsi_cmd *iscsit_find_cmd_from_itt(struct iscsi_conn *, itt_t);
+extern struct iscsi_cmd *iscsit_find_cmd_from_itt_or_dump(struct iscsi_conn *,
+ itt_t, u32);
+extern struct iscsi_cmd *iscsit_find_cmd_from_ttt(struct iscsi_conn *, u32);
+extern int iscsit_find_cmd_for_recovery(struct iscsi_session *, struct iscsi_cmd **,
+ struct iscsi_conn_recovery **, itt_t);
+extern void iscsit_add_cmd_to_immediate_queue(struct iscsi_cmd *, struct iscsi_conn *, u8);
+extern struct iscsi_queue_req *iscsit_get_cmd_from_immediate_queue(struct iscsi_conn *);
+extern void iscsit_add_cmd_to_response_queue(struct iscsi_cmd *, struct iscsi_conn *, u8);
+extern struct iscsi_queue_req *iscsit_get_cmd_from_response_queue(struct iscsi_conn *);
+extern void iscsit_remove_cmd_from_tx_queues(struct iscsi_cmd *, struct iscsi_conn *);
+extern bool iscsit_conn_all_queues_empty(struct iscsi_conn *);
+extern void iscsit_free_queue_reqs_for_conn(struct iscsi_conn *);
+extern void iscsit_release_cmd(struct iscsi_cmd *);
+extern void __iscsit_free_cmd(struct iscsi_cmd *, bool, bool);
+extern void iscsit_free_cmd(struct iscsi_cmd *, bool);
+extern int iscsit_check_session_usage_count(struct iscsi_session *);
+extern void iscsit_dec_session_usage_count(struct iscsi_session *);
+extern void iscsit_inc_session_usage_count(struct iscsi_session *);
+extern int iscsit_set_sync_and_steering_values(struct iscsi_conn *);
+extern struct iscsi_conn *iscsit_get_conn_from_cid(struct iscsi_session *, u16);
+extern struct iscsi_conn *iscsit_get_conn_from_cid_rcfr(struct iscsi_session *, u16);
+extern void iscsit_check_conn_usage_count(struct iscsi_conn *);
+extern void iscsit_dec_conn_usage_count(struct iscsi_conn *);
+extern void iscsit_inc_conn_usage_count(struct iscsi_conn *);
+extern void iscsit_mod_nopin_response_timer(struct iscsi_conn *);
+extern void iscsit_start_nopin_response_timer(struct iscsi_conn *);
+extern void iscsit_stop_nopin_response_timer(struct iscsi_conn *);
+extern void __iscsit_start_nopin_timer(struct iscsi_conn *);
+extern void iscsit_start_nopin_timer(struct iscsi_conn *);
+extern void iscsit_stop_nopin_timer(struct iscsi_conn *);
+extern int iscsit_send_tx_data(struct iscsi_cmd *, struct iscsi_conn *, int);
+extern int iscsit_fe_sendpage_sg(struct iscsi_cmd *, struct iscsi_conn *);
+extern int iscsit_tx_login_rsp(struct iscsi_conn *, u8, u8);
+extern void iscsit_print_session_params(struct iscsi_session *);
+extern int iscsit_print_dev_to_proc(char *, char **, off_t, int);
+extern int iscsit_print_sessions_to_proc(char *, char **, off_t, int);
+extern int iscsit_print_tpg_to_proc(char *, char **, off_t, int);
+extern int rx_data(struct iscsi_conn *, struct kvec *, int, int);
+extern int tx_data(struct iscsi_conn *, struct kvec *, int, int);
+extern void iscsit_collect_login_stats(struct iscsi_conn *, u8, u8);
+extern struct iscsi_tiqn *iscsit_snmp_get_tiqn(struct iscsi_conn *);
+
+#endif /*** ISCSI_TARGET_UTIL_H ***/
diff --git a/drivers/target/loopback/Kconfig b/drivers/target/loopback/Kconfig
new file mode 100644
index 00000000000..abe8ecbcdf0
--- /dev/null
+++ b/drivers/target/loopback/Kconfig
@@ -0,0 +1,5 @@
+config LOOPBACK_TARGET
+ tristate "TCM Virtual SAS target and Linux/SCSI LDD fabric loopback module"
+ help
+ Say Y here to enable the TCM Virtual SAS target and Linux/SCSI LLD
+ fabric loopback module.
diff --git a/drivers/target/loopback/Makefile b/drivers/target/loopback/Makefile
new file mode 100644
index 00000000000..6abebdf9565
--- /dev/null
+++ b/drivers/target/loopback/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_LOOPBACK_TARGET) += tcm_loop.o
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c
new file mode 100644
index 00000000000..8c64b8776a9
--- /dev/null
+++ b/drivers/target/loopback/tcm_loop.c
@@ -0,0 +1,1611 @@
+/*******************************************************************************
+ *
+ * This file contains the Linux/SCSI LLD virtual SCSI initiator driver
+ * for emulated SAS initiator ports
+ *
+ * © Copyright 2011-2013 Datera, Inc.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Author: Nicholas A. Bellinger <nab@risingtidesystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/configfs.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+
+#include "tcm_loop.h"
+
+#define to_tcm_loop_hba(hba) container_of(hba, struct tcm_loop_hba, dev)
+
+/* Local pointer to allocated TCM configfs fabric module */
+static struct target_fabric_configfs *tcm_loop_fabric_configfs;
+
+static struct workqueue_struct *tcm_loop_workqueue;
+static struct kmem_cache *tcm_loop_cmd_cache;
+
+static int tcm_loop_hba_no_cnt;
+
+static int tcm_loop_queue_status(struct se_cmd *se_cmd);
+
+/*
+ * Called from struct target_core_fabric_ops->check_stop_free()
+ */
+static int tcm_loop_check_stop_free(struct se_cmd *se_cmd)
+{
+ /*
+ * Do not release struct se_cmd's containing a valid TMR
+ * pointer. These will be released directly in tcm_loop_device_reset()
+ * with transport_generic_free_cmd().
+ */
+ if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
+ return 0;
+ /*
+ * Release the struct se_cmd, which will make a callback to release
+ * struct tcm_loop_cmd * in tcm_loop_deallocate_core_cmd()
+ */
+ transport_generic_free_cmd(se_cmd, 0);
+ return 1;
+}
+
+static void tcm_loop_release_cmd(struct se_cmd *se_cmd)
+{
+ struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
+ struct tcm_loop_cmd, tl_se_cmd);
+
+ kmem_cache_free(tcm_loop_cmd_cache, tl_cmd);
+}
+
+static int tcm_loop_show_info(struct seq_file *m, struct Scsi_Host *host)
+{
+ seq_printf(m, "tcm_loop_proc_info()\n");
+ return 0;
+}
+
+static int tcm_loop_driver_probe(struct device *);
+static int tcm_loop_driver_remove(struct device *);
+
+static int pseudo_lld_bus_match(struct device *dev,
+ struct device_driver *dev_driver)
+{
+ return 1;
+}
+
+static struct bus_type tcm_loop_lld_bus = {
+ .name = "tcm_loop_bus",
+ .match = pseudo_lld_bus_match,
+ .probe = tcm_loop_driver_probe,
+ .remove = tcm_loop_driver_remove,
+};
+
+static struct device_driver tcm_loop_driverfs = {
+ .name = "tcm_loop",
+ .bus = &tcm_loop_lld_bus,
+};
+/*
+ * Used with root_device_register() in tcm_loop_alloc_core_bus() below
+ */
+struct device *tcm_loop_primary;
+
+/*
+ * Copied from drivers/scsi/libfc/fc_fcp.c:fc_change_queue_depth() and
+ * drivers/scsi/libiscsi.c:iscsi_change_queue_depth()
+ */
+static int tcm_loop_change_queue_depth(
+ struct scsi_device *sdev,
+ int depth,
+ int reason)
+{
+ switch (reason) {
+ case SCSI_QDEPTH_DEFAULT:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ break;
+ case SCSI_QDEPTH_QFULL:
+ scsi_track_queue_full(sdev, depth);
+ break;
+ case SCSI_QDEPTH_RAMP_UP:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return sdev->queue_depth;
+}
+
+static int tcm_loop_change_queue_type(struct scsi_device *sdev, int tag)
+{
+ if (sdev->tagged_supported) {
+ scsi_set_tag_type(sdev, tag);
+
+ if (tag)
+ scsi_activate_tcq(sdev, sdev->queue_depth);
+ else
+ scsi_deactivate_tcq(sdev, sdev->queue_depth);
+ } else
+ tag = 0;
+
+ return tag;
+}
+
+/*
+ * Locate the SAM Task Attr from struct scsi_cmnd *
+ */
+static int tcm_loop_sam_attr(struct scsi_cmnd *sc)
+{
+ if (sc->device->tagged_supported) {
+ switch (sc->tag) {
+ case HEAD_OF_QUEUE_TAG:
+ return MSG_HEAD_TAG;
+ case ORDERED_QUEUE_TAG:
+ return MSG_ORDERED_TAG;
+ default:
+ break;
+ }
+ }
+
+ return MSG_SIMPLE_TAG;
+}
+
+static void tcm_loop_submission_work(struct work_struct *work)
+{
+ struct tcm_loop_cmd *tl_cmd =
+ container_of(work, struct tcm_loop_cmd, work);
+ struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd;
+ struct scsi_cmnd *sc = tl_cmd->sc;
+ struct tcm_loop_nexus *tl_nexus;
+ struct tcm_loop_hba *tl_hba;
+ struct tcm_loop_tpg *tl_tpg;
+ struct scatterlist *sgl_bidi = NULL;
+ u32 sgl_bidi_count = 0, transfer_length;
+ int rc;
+
+ tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
+ tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
+
+ /*
+ * Ensure that this tl_tpg reference from the incoming sc->device->id
+ * has already been configured via tcm_loop_make_naa_tpg().
+ */
+ if (!tl_tpg->tl_hba) {
+ set_host_byte(sc, DID_NO_CONNECT);
+ goto out_done;
+ }
+ if (tl_tpg->tl_transport_status == TCM_TRANSPORT_OFFLINE) {
+ set_host_byte(sc, DID_TRANSPORT_DISRUPTED);
+ goto out_done;
+ }
+ tl_nexus = tl_hba->tl_nexus;
+ if (!tl_nexus) {
+ scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus"
+ " does not exist\n");
+ set_host_byte(sc, DID_ERROR);
+ goto out_done;
+ }
+ if (scsi_bidi_cmnd(sc)) {
+ struct scsi_data_buffer *sdb = scsi_in(sc);
+
+ sgl_bidi = sdb->table.sgl;
+ sgl_bidi_count = sdb->table.nents;
+ se_cmd->se_cmd_flags |= SCF_BIDI;
+
+ }
+
+ transfer_length = scsi_transfer_length(sc);
+ if (!scsi_prot_sg_count(sc) &&
+ scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) {
+ se_cmd->prot_pto = true;
+ /*
+ * loopback transport doesn't support
+ * WRITE_GENERATE, READ_STRIP protection
+ * information operations, go ahead unprotected.
+ */
+ transfer_length = scsi_bufflen(sc);
+ }
+
+ rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd,
+ &tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun,
+ transfer_length, tcm_loop_sam_attr(sc),
+ sc->sc_data_direction, 0,
+ scsi_sglist(sc), scsi_sg_count(sc),
+ sgl_bidi, sgl_bidi_count,
+ scsi_prot_sglist(sc), scsi_prot_sg_count(sc));
+ if (rc < 0) {
+ set_host_byte(sc, DID_NO_CONNECT);
+ goto out_done;
+ }
+ return;
+
+out_done:
+ kmem_cache_free(tcm_loop_cmd_cache, tl_cmd);
+ sc->scsi_done(sc);
+ return;
+}
+
+/*
+ * ->queuecommand can be and usually is called from interrupt context, so
+ * defer the actual submission to a workqueue.
+ */
+static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
+{
+ struct tcm_loop_cmd *tl_cmd;
+
+ pr_debug("tcm_loop_queuecommand() %d:%d:%d:%d got CDB: 0x%02x"
+ " scsi_buf_len: %u\n", sc->device->host->host_no,
+ sc->device->id, sc->device->channel, sc->device->lun,
+ sc->cmnd[0], scsi_bufflen(sc));
+
+ tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_ATOMIC);
+ if (!tl_cmd) {
+ pr_err("Unable to allocate struct tcm_loop_cmd\n");
+ set_host_byte(sc, DID_ERROR);
+ sc->scsi_done(sc);
+ return 0;
+ }
+
+ tl_cmd->sc = sc;
+ tl_cmd->sc_cmd_tag = sc->tag;
+ INIT_WORK(&tl_cmd->work, tcm_loop_submission_work);
+ queue_work(tcm_loop_workqueue, &tl_cmd->work);
+ return 0;
+}
+
+/*
+ * Called from SCSI EH process context to issue a LUN_RESET TMR
+ * to struct scsi_device
+ */
+static int tcm_loop_issue_tmr(struct tcm_loop_tpg *tl_tpg,
+ struct tcm_loop_nexus *tl_nexus,
+ int lun, int task, enum tcm_tmreq_table tmr)
+{
+ struct se_cmd *se_cmd = NULL;
+ struct se_session *se_sess;
+ struct se_portal_group *se_tpg;
+ struct tcm_loop_cmd *tl_cmd = NULL;
+ struct tcm_loop_tmr *tl_tmr = NULL;
+ int ret = TMR_FUNCTION_FAILED, rc;
+
+ tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_KERNEL);
+ if (!tl_cmd) {
+ pr_err("Unable to allocate memory for tl_cmd\n");
+ return ret;
+ }
+
+ tl_tmr = kzalloc(sizeof(struct tcm_loop_tmr), GFP_KERNEL);
+ if (!tl_tmr) {
+ pr_err("Unable to allocate memory for tl_tmr\n");
+ goto release;
+ }
+ init_waitqueue_head(&tl_tmr->tl_tmr_wait);
+
+ se_cmd = &tl_cmd->tl_se_cmd;
+ se_tpg = &tl_tpg->tl_se_tpg;
+ se_sess = tl_nexus->se_sess;
+ /*
+ * Initialize struct se_cmd descriptor from target_core_mod infrastructure
+ */
+ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, 0,
+ DMA_NONE, MSG_SIMPLE_TAG,
+ &tl_cmd->tl_sense_buf[0]);
+
+ rc = core_tmr_alloc_req(se_cmd, tl_tmr, tmr, GFP_KERNEL);
+ if (rc < 0)
+ goto release;
+
+ if (tmr == TMR_ABORT_TASK)
+ se_cmd->se_tmr_req->ref_task_tag = task;
+
+ /*
+ * Locate the underlying TCM struct se_lun
+ */
+ if (transport_lookup_tmr_lun(se_cmd, lun) < 0) {
+ ret = TMR_LUN_DOES_NOT_EXIST;
+ goto release;
+ }
+ /*
+ * Queue the TMR to TCM Core and sleep waiting for
+ * tcm_loop_queue_tm_rsp() to wake us up.
+ */
+ transport_generic_handle_tmr(se_cmd);
+ wait_event(tl_tmr->tl_tmr_wait, atomic_read(&tl_tmr->tmr_complete));
+ /*
+ * The TMR LUN_RESET has completed, check the response status and
+ * then release allocations.
+ */
+ ret = se_cmd->se_tmr_req->response;
+release:
+ if (se_cmd)
+ transport_generic_free_cmd(se_cmd, 1);
+ else
+ kmem_cache_free(tcm_loop_cmd_cache, tl_cmd);
+ kfree(tl_tmr);
+ return ret;
+}
+
+static int tcm_loop_abort_task(struct scsi_cmnd *sc)
+{
+ struct tcm_loop_hba *tl_hba;
+ struct tcm_loop_nexus *tl_nexus;
+ struct tcm_loop_tpg *tl_tpg;
+ int ret = FAILED;
+
+ /*
+ * Locate the tcm_loop_hba_t pointer
+ */
+ tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
+ /*
+ * Locate the tl_nexus and se_sess pointers
+ */
+ tl_nexus = tl_hba->tl_nexus;
+ if (!tl_nexus) {
+ pr_err("Unable to perform device reset without"
+ " active I_T Nexus\n");
+ return FAILED;
+ }
+
+ /*
+ * Locate the tl_tpg pointer from TargetID in sc->device->id
+ */
+ tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
+ ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun,
+ sc->tag, TMR_ABORT_TASK);
+ return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED;
+}
+
+/*
+ * Called from SCSI EH process context to issue a LUN_RESET TMR
+ * to struct scsi_device
+ */
+static int tcm_loop_device_reset(struct scsi_cmnd *sc)
+{
+ struct tcm_loop_hba *tl_hba;
+ struct tcm_loop_nexus *tl_nexus;
+ struct tcm_loop_tpg *tl_tpg;
+ int ret = FAILED;
+
+ /*
+ * Locate the tcm_loop_hba_t pointer
+ */
+ tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
+ /*
+ * Locate the tl_nexus and se_sess pointers
+ */
+ tl_nexus = tl_hba->tl_nexus;
+ if (!tl_nexus) {
+ pr_err("Unable to perform device reset without"
+ " active I_T Nexus\n");
+ return FAILED;
+ }
+ /*
+ * Locate the tl_tpg pointer from TargetID in sc->device->id
+ */
+ tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
+ ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun,
+ 0, TMR_LUN_RESET);
+ return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED;
+}
+
+static int tcm_loop_target_reset(struct scsi_cmnd *sc)
+{
+ struct tcm_loop_hba *tl_hba;
+ struct tcm_loop_tpg *tl_tpg;
+
+ /*
+ * Locate the tcm_loop_hba_t pointer
+ */
+ tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
+ if (!tl_hba) {
+ pr_err("Unable to perform device reset without"
+ " active I_T Nexus\n");
+ return FAILED;
+ }
+ /*
+ * Locate the tl_tpg pointer from TargetID in sc->device->id
+ */
+ tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
+ if (tl_tpg) {
+ tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE;
+ return SUCCESS;
+ }
+ return FAILED;
+}
+
+static int tcm_loop_slave_alloc(struct scsi_device *sd)
+{
+ set_bit(QUEUE_FLAG_BIDI, &sd->request_queue->queue_flags);
+ return 0;
+}
+
+static int tcm_loop_slave_configure(struct scsi_device *sd)
+{
+ if (sd->tagged_supported) {
+ scsi_activate_tcq(sd, sd->queue_depth);
+ scsi_adjust_queue_depth(sd, MSG_SIMPLE_TAG,
+ sd->host->cmd_per_lun);
+ } else {
+ scsi_adjust_queue_depth(sd, 0,
+ sd->host->cmd_per_lun);
+ }
+
+ return 0;
+}
+
+static struct scsi_host_template tcm_loop_driver_template = {
+ .show_info = tcm_loop_show_info,
+ .proc_name = "tcm_loopback",
+ .name = "TCM_Loopback",
+ .queuecommand = tcm_loop_queuecommand,
+ .change_queue_depth = tcm_loop_change_queue_depth,
+ .change_queue_type = tcm_loop_change_queue_type,
+ .eh_abort_handler = tcm_loop_abort_task,
+ .eh_device_reset_handler = tcm_loop_device_reset,
+ .eh_target_reset_handler = tcm_loop_target_reset,
+ .can_queue = 1024,
+ .this_id = -1,
+ .sg_tablesize = 256,
+ .cmd_per_lun = 1024,
+ .max_sectors = 0xFFFF,
+ .use_clustering = DISABLE_CLUSTERING,
+ .slave_alloc = tcm_loop_slave_alloc,
+ .slave_configure = tcm_loop_slave_configure,
+ .module = THIS_MODULE,
+};
+
+static int tcm_loop_driver_probe(struct device *dev)
+{
+ struct tcm_loop_hba *tl_hba;
+ struct Scsi_Host *sh;
+ int error, host_prot;
+
+ tl_hba = to_tcm_loop_hba(dev);
+
+ sh = scsi_host_alloc(&tcm_loop_driver_template,
+ sizeof(struct tcm_loop_hba));
+ if (!sh) {
+ pr_err("Unable to allocate struct scsi_host\n");
+ return -ENODEV;
+ }
+ tl_hba->sh = sh;
+
+ /*
+ * Assign the struct tcm_loop_hba pointer to struct Scsi_Host->hostdata
+ */
+ *((struct tcm_loop_hba **)sh->hostdata) = tl_hba;
+ /*
+ * Setup single ID, Channel and LUN for now..
+ */
+ sh->max_id = 2;
+ sh->max_lun = 0;
+ sh->max_channel = 0;
+ sh->max_cmd_len = TL_SCSI_MAX_CMD_LEN;
+
+ host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION |
+ SHOST_DIF_TYPE3_PROTECTION | SHOST_DIX_TYPE1_PROTECTION |
+ SHOST_DIX_TYPE2_PROTECTION | SHOST_DIX_TYPE3_PROTECTION;
+
+ scsi_host_set_prot(sh, host_prot);
+ scsi_host_set_guard(sh, SHOST_DIX_GUARD_CRC);
+
+ error = scsi_add_host(sh, &tl_hba->dev);
+ if (error) {
+ pr_err("%s: scsi_add_host failed\n", __func__);
+ scsi_host_put(sh);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int tcm_loop_driver_remove(struct device *dev)
+{
+ struct tcm_loop_hba *tl_hba;
+ struct Scsi_Host *sh;
+
+ tl_hba = to_tcm_loop_hba(dev);
+ sh = tl_hba->sh;
+
+ scsi_remove_host(sh);
+ scsi_host_put(sh);
+ return 0;
+}
+
+static void tcm_loop_release_adapter(struct device *dev)
+{
+ struct tcm_loop_hba *tl_hba = to_tcm_loop_hba(dev);
+
+ kfree(tl_hba);
+}
+
+/*
+ * Called from tcm_loop_make_scsi_hba() in tcm_loop_configfs.c
+ */
+static int tcm_loop_setup_hba_bus(struct tcm_loop_hba *tl_hba, int tcm_loop_host_id)
+{
+ int ret;
+
+ tl_hba->dev.bus = &tcm_loop_lld_bus;
+ tl_hba->dev.parent = tcm_loop_primary;
+ tl_hba->dev.release = &tcm_loop_release_adapter;
+ dev_set_name(&tl_hba->dev, "tcm_loop_adapter_%d", tcm_loop_host_id);
+
+ ret = device_register(&tl_hba->dev);
+ if (ret) {
+ pr_err("device_register() failed for"
+ " tl_hba->dev: %d\n", ret);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * Called from tcm_loop_fabric_init() in tcl_loop_fabric.c to load the emulated
+ * tcm_loop SCSI bus.
+ */
+static int tcm_loop_alloc_core_bus(void)
+{
+ int ret;
+
+ tcm_loop_primary = root_device_register("tcm_loop_0");
+ if (IS_ERR(tcm_loop_primary)) {
+ pr_err("Unable to allocate tcm_loop_primary\n");
+ return PTR_ERR(tcm_loop_primary);
+ }
+
+ ret = bus_register(&tcm_loop_lld_bus);
+ if (ret) {
+ pr_err("bus_register() failed for tcm_loop_lld_bus\n");
+ goto dev_unreg;
+ }
+
+ ret = driver_register(&tcm_loop_driverfs);
+ if (ret) {
+ pr_err("driver_register() failed for"
+ "tcm_loop_driverfs\n");
+ goto bus_unreg;
+ }
+
+ pr_debug("Initialized TCM Loop Core Bus\n");
+ return ret;
+
+bus_unreg:
+ bus_unregister(&tcm_loop_lld_bus);
+dev_unreg:
+ root_device_unregister(tcm_loop_primary);
+ return ret;
+}
+
+static void tcm_loop_release_core_bus(void)
+{
+ driver_unregister(&tcm_loop_driverfs);
+ bus_unregister(&tcm_loop_lld_bus);
+ root_device_unregister(tcm_loop_primary);
+
+ pr_debug("Releasing TCM Loop Core BUS\n");
+}
+
+static char *tcm_loop_get_fabric_name(void)
+{
+ return "loopback";
+}
+
+static u8 tcm_loop_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+ struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr;
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+ /*
+ * tl_proto_id is set at tcm_loop_configfs.c:tcm_loop_make_scsi_hba()
+ * time based on the protocol dependent prefix of the passed configfs group.
+ *
+ * Based upon tl_proto_id, TCM_Loop emulates the requested fabric
+ * ProtocolID using target_core_fabric_lib.c symbols.
+ */
+ switch (tl_hba->tl_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_get_fabric_proto_ident(se_tpg);
+ case SCSI_PROTOCOL_FCP:
+ return fc_get_fabric_proto_ident(se_tpg);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_get_fabric_proto_ident(se_tpg);
+ default:
+ pr_err("Unknown tl_proto_id: 0x%02x, using"
+ " SAS emulation\n", tl_hba->tl_proto_id);
+ break;
+ }
+
+ return sas_get_fabric_proto_ident(se_tpg);
+}
+
+static char *tcm_loop_get_endpoint_wwn(struct se_portal_group *se_tpg)
+{
+ struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr;
+ /*
+ * Return the passed NAA identifier for the SAS Target Port
+ */
+ return &tl_tpg->tl_hba->tl_wwn_address[0];
+}
+
+static u16 tcm_loop_get_tag(struct se_portal_group *se_tpg)
+{
+ struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr;
+ /*
+ * This Tag is used when forming SCSI Name identifier in EVPD=1 0x83
+ * to represent the SCSI Target Port.
+ */
+ return tl_tpg->tl_tpgt;
+}
+
+static u32 tcm_loop_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static u32 tcm_loop_get_pr_transport_id(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code,
+ unsigned char *buf)
+{
+ struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr;
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+
+ switch (tl_hba->tl_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ case SCSI_PROTOCOL_FCP:
+ return fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ default:
+ pr_err("Unknown tl_proto_id: 0x%02x, using"
+ " SAS emulation\n", tl_hba->tl_proto_id);
+ break;
+ }
+
+ return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+}
+
+static u32 tcm_loop_get_pr_transport_id_len(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code)
+{
+ struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr;
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+
+ switch (tl_hba->tl_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ case SCSI_PROTOCOL_FCP:
+ return fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ default:
+ pr_err("Unknown tl_proto_id: 0x%02x, using"
+ " SAS emulation\n", tl_hba->tl_proto_id);
+ break;
+ }
+
+ return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+}
+
+/*
+ * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above
+ * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations.
+ */
+static char *tcm_loop_parse_pr_out_transport_id(
+ struct se_portal_group *se_tpg,
+ const char *buf,
+ u32 *out_tid_len,
+ char **port_nexus_ptr)
+{
+ struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr;
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+
+ switch (tl_hba->tl_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ case SCSI_PROTOCOL_FCP:
+ return fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ default:
+ pr_err("Unknown tl_proto_id: 0x%02x, using"
+ " SAS emulation\n", tl_hba->tl_proto_id);
+ break;
+ }
+
+ return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+}
+
+/*
+ * Returning (1) here allows for target_core_mod struct se_node_acl to be generated
+ * based upon the incoming fabric dependent SCSI Initiator Port
+ */
+static int tcm_loop_check_demo_mode(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int tcm_loop_check_demo_mode_cache(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+/*
+ * Allow I_T Nexus full READ-WRITE access without explict Initiator Node ACLs for
+ * local virtual Linux/SCSI LLD passthrough into VM hypervisor guest
+ */
+static int tcm_loop_check_demo_mode_write_protect(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+/*
+ * Because TCM_Loop does not use explict ACLs and MappedLUNs, this will
+ * never be called for TCM_Loop by target_core_fabric_configfs.c code.
+ * It has been added here as a nop for target_fabric_tf_ops_check()
+ */
+static int tcm_loop_check_prod_mode_write_protect(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+static struct se_node_acl *tcm_loop_tpg_alloc_fabric_acl(
+ struct se_portal_group *se_tpg)
+{
+ struct tcm_loop_nacl *tl_nacl;
+
+ tl_nacl = kzalloc(sizeof(struct tcm_loop_nacl), GFP_KERNEL);
+ if (!tl_nacl) {
+ pr_err("Unable to allocate struct tcm_loop_nacl\n");
+ return NULL;
+ }
+
+ return &tl_nacl->se_node_acl;
+}
+
+static void tcm_loop_tpg_release_fabric_acl(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl)
+{
+ struct tcm_loop_nacl *tl_nacl = container_of(se_nacl,
+ struct tcm_loop_nacl, se_node_acl);
+
+ kfree(tl_nacl);
+}
+
+static u32 tcm_loop_get_inst_index(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static u32 tcm_loop_sess_get_index(struct se_session *se_sess)
+{
+ return 1;
+}
+
+static void tcm_loop_set_default_node_attributes(struct se_node_acl *se_acl)
+{
+ return;
+}
+
+static u32 tcm_loop_get_task_tag(struct se_cmd *se_cmd)
+{
+ struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
+ struct tcm_loop_cmd, tl_se_cmd);
+
+ return tl_cmd->sc_cmd_tag;
+}
+
+static int tcm_loop_get_cmd_state(struct se_cmd *se_cmd)
+{
+ struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
+ struct tcm_loop_cmd, tl_se_cmd);
+
+ return tl_cmd->sc_cmd_state;
+}
+
+static int tcm_loop_shutdown_session(struct se_session *se_sess)
+{
+ return 0;
+}
+
+static void tcm_loop_close_session(struct se_session *se_sess)
+{
+ return;
+};
+
+static int tcm_loop_write_pending(struct se_cmd *se_cmd)
+{
+ /*
+ * Since Linux/SCSI has already sent down a struct scsi_cmnd
+ * sc->sc_data_direction of DMA_TO_DEVICE with struct scatterlist array
+ * memory, and memory has already been mapped to struct se_cmd->t_mem_list
+ * format with transport_generic_map_mem_to_cmd().
+ *
+ * We now tell TCM to add this WRITE CDB directly into the TCM storage
+ * object execution queue.
+ */
+ target_execute_cmd(se_cmd);
+ return 0;
+}
+
+static int tcm_loop_write_pending_status(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static int tcm_loop_queue_data_in(struct se_cmd *se_cmd)
+{
+ struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
+ struct tcm_loop_cmd, tl_se_cmd);
+ struct scsi_cmnd *sc = tl_cmd->sc;
+
+ pr_debug("tcm_loop_queue_data_in() called for scsi_cmnd: %p"
+ " cdb: 0x%02x\n", sc, sc->cmnd[0]);
+
+ sc->result = SAM_STAT_GOOD;
+ set_host_byte(sc, DID_OK);
+ if ((se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) ||
+ (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT))
+ scsi_set_resid(sc, se_cmd->residual_count);
+ sc->scsi_done(sc);
+ return 0;
+}
+
+static int tcm_loop_queue_status(struct se_cmd *se_cmd)
+{
+ struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
+ struct tcm_loop_cmd, tl_se_cmd);
+ struct scsi_cmnd *sc = tl_cmd->sc;
+
+ pr_debug("tcm_loop_queue_status() called for scsi_cmnd: %p"
+ " cdb: 0x%02x\n", sc, sc->cmnd[0]);
+
+ if (se_cmd->sense_buffer &&
+ ((se_cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ||
+ (se_cmd->se_cmd_flags & SCF_EMULATED_TASK_SENSE))) {
+
+ memcpy(sc->sense_buffer, se_cmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE);
+ sc->result = SAM_STAT_CHECK_CONDITION;
+ set_driver_byte(sc, DRIVER_SENSE);
+ } else
+ sc->result = se_cmd->scsi_status;
+
+ set_host_byte(sc, DID_OK);
+ if ((se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) ||
+ (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT))
+ scsi_set_resid(sc, se_cmd->residual_count);
+ sc->scsi_done(sc);
+ return 0;
+}
+
+static void tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+ struct se_tmr_req *se_tmr = se_cmd->se_tmr_req;
+ struct tcm_loop_tmr *tl_tmr = se_tmr->fabric_tmr_ptr;
+ /*
+ * The SCSI EH thread will be sleeping on se_tmr->tl_tmr_wait, go ahead
+ * and wake up the wait_queue_head_t in tcm_loop_device_reset()
+ */
+ atomic_set(&tl_tmr->tmr_complete, 1);
+ wake_up(&tl_tmr->tl_tmr_wait);
+}
+
+static void tcm_loop_aborted_task(struct se_cmd *se_cmd)
+{
+ return;
+}
+
+static char *tcm_loop_dump_proto_id(struct tcm_loop_hba *tl_hba)
+{
+ switch (tl_hba->tl_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return "SAS";
+ case SCSI_PROTOCOL_FCP:
+ return "FCP";
+ case SCSI_PROTOCOL_ISCSI:
+ return "iSCSI";
+ default:
+ break;
+ }
+
+ return "Unknown";
+}
+
+/* Start items for tcm_loop_port_cit */
+
+static int tcm_loop_port_link(
+ struct se_portal_group *se_tpg,
+ struct se_lun *lun)
+{
+ struct tcm_loop_tpg *tl_tpg = container_of(se_tpg,
+ struct tcm_loop_tpg, tl_se_tpg);
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+
+ atomic_inc(&tl_tpg->tl_tpg_port_count);
+ smp_mb__after_atomic();
+ /*
+ * Add Linux/SCSI struct scsi_device by HCTL
+ */
+ scsi_add_device(tl_hba->sh, 0, tl_tpg->tl_tpgt, lun->unpacked_lun);
+
+ pr_debug("TCM_Loop_ConfigFS: Port Link Successful\n");
+ return 0;
+}
+
+static void tcm_loop_port_unlink(
+ struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct scsi_device *sd;
+ struct tcm_loop_hba *tl_hba;
+ struct tcm_loop_tpg *tl_tpg;
+
+ tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, tl_se_tpg);
+ tl_hba = tl_tpg->tl_hba;
+
+ sd = scsi_device_lookup(tl_hba->sh, 0, tl_tpg->tl_tpgt,
+ se_lun->unpacked_lun);
+ if (!sd) {
+ pr_err("Unable to locate struct scsi_device for %d:%d:"
+ "%d\n", 0, tl_tpg->tl_tpgt, se_lun->unpacked_lun);
+ return;
+ }
+ /*
+ * Remove Linux/SCSI struct scsi_device by HCTL
+ */
+ scsi_remove_device(sd);
+ scsi_device_put(sd);
+
+ atomic_dec(&tl_tpg->tl_tpg_port_count);
+ smp_mb__after_atomic();
+
+ pr_debug("TCM_Loop_ConfigFS: Port Unlink Successful\n");
+}
+
+/* End items for tcm_loop_port_cit */
+
+/* Start items for tcm_loop_nexus_cit */
+
+static int tcm_loop_make_nexus(
+ struct tcm_loop_tpg *tl_tpg,
+ const char *name)
+{
+ struct se_portal_group *se_tpg;
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+ struct tcm_loop_nexus *tl_nexus;
+ int ret = -ENOMEM;
+
+ if (tl_tpg->tl_hba->tl_nexus) {
+ pr_debug("tl_tpg->tl_hba->tl_nexus already exists\n");
+ return -EEXIST;
+ }
+ se_tpg = &tl_tpg->tl_se_tpg;
+
+ tl_nexus = kzalloc(sizeof(struct tcm_loop_nexus), GFP_KERNEL);
+ if (!tl_nexus) {
+ pr_err("Unable to allocate struct tcm_loop_nexus\n");
+ return -ENOMEM;
+ }
+ /*
+ * Initialize the struct se_session pointer
+ */
+ tl_nexus->se_sess = transport_init_session(TARGET_PROT_ALL);
+ if (IS_ERR(tl_nexus->se_sess)) {
+ ret = PTR_ERR(tl_nexus->se_sess);
+ goto out;
+ }
+ /*
+ * Since we are running in 'demo mode' this call with generate a
+ * struct se_node_acl for the tcm_loop struct se_portal_group with the SCSI
+ * Initiator port name of the passed configfs group 'name'.
+ */
+ tl_nexus->se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
+ se_tpg, (unsigned char *)name);
+ if (!tl_nexus->se_sess->se_node_acl) {
+ transport_free_session(tl_nexus->se_sess);
+ goto out;
+ }
+ /*
+ * Now, register the SAS I_T Nexus as active with the call to
+ * transport_register_session()
+ */
+ __transport_register_session(se_tpg, tl_nexus->se_sess->se_node_acl,
+ tl_nexus->se_sess, tl_nexus);
+ tl_tpg->tl_hba->tl_nexus = tl_nexus;
+ pr_debug("TCM_Loop_ConfigFS: Established I_T Nexus to emulated"
+ " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba),
+ name);
+ return 0;
+
+out:
+ kfree(tl_nexus);
+ return ret;
+}
+
+static int tcm_loop_drop_nexus(
+ struct tcm_loop_tpg *tpg)
+{
+ struct se_session *se_sess;
+ struct tcm_loop_nexus *tl_nexus;
+ struct tcm_loop_hba *tl_hba = tpg->tl_hba;
+
+ if (!tl_hba)
+ return -ENODEV;
+
+ tl_nexus = tl_hba->tl_nexus;
+ if (!tl_nexus)
+ return -ENODEV;
+
+ se_sess = tl_nexus->se_sess;
+ if (!se_sess)
+ return -ENODEV;
+
+ if (atomic_read(&tpg->tl_tpg_port_count)) {
+ pr_err("Unable to remove TCM_Loop I_T Nexus with"
+ " active TPG port count: %d\n",
+ atomic_read(&tpg->tl_tpg_port_count));
+ return -EPERM;
+ }
+
+ pr_debug("TCM_Loop_ConfigFS: Removing I_T Nexus to emulated"
+ " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba),
+ tl_nexus->se_sess->se_node_acl->initiatorname);
+ /*
+ * Release the SCSI I_T Nexus to the emulated SAS Target Port
+ */
+ transport_deregister_session(tl_nexus->se_sess);
+ tpg->tl_hba->tl_nexus = NULL;
+ kfree(tl_nexus);
+ return 0;
+}
+
+/* End items for tcm_loop_nexus_cit */
+
+static ssize_t tcm_loop_tpg_show_nexus(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct tcm_loop_tpg *tl_tpg = container_of(se_tpg,
+ struct tcm_loop_tpg, tl_se_tpg);
+ struct tcm_loop_nexus *tl_nexus;
+ ssize_t ret;
+
+ tl_nexus = tl_tpg->tl_hba->tl_nexus;
+ if (!tl_nexus)
+ return -ENODEV;
+
+ ret = snprintf(page, PAGE_SIZE, "%s\n",
+ tl_nexus->se_sess->se_node_acl->initiatorname);
+
+ return ret;
+}
+
+static ssize_t tcm_loop_tpg_store_nexus(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct tcm_loop_tpg *tl_tpg = container_of(se_tpg,
+ struct tcm_loop_tpg, tl_se_tpg);
+ struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba;
+ unsigned char i_port[TL_WWN_ADDR_LEN], *ptr, *port_ptr;
+ int ret;
+ /*
+ * Shutdown the active I_T nexus if 'NULL' is passed..
+ */
+ if (!strncmp(page, "NULL", 4)) {
+ ret = tcm_loop_drop_nexus(tl_tpg);
+ return (!ret) ? count : ret;
+ }
+ /*
+ * Otherwise make sure the passed virtual Initiator port WWN matches
+ * the fabric protocol_id set in tcm_loop_make_scsi_hba(), and call
+ * tcm_loop_make_nexus()
+ */
+ if (strlen(page) >= TL_WWN_ADDR_LEN) {
+ pr_err("Emulated NAA Sas Address: %s, exceeds"
+ " max: %d\n", page, TL_WWN_ADDR_LEN);
+ return -EINVAL;
+ }
+ snprintf(&i_port[0], TL_WWN_ADDR_LEN, "%s", page);
+
+ ptr = strstr(i_port, "naa.");
+ if (ptr) {
+ if (tl_hba->tl_proto_id != SCSI_PROTOCOL_SAS) {
+ pr_err("Passed SAS Initiator Port %s does not"
+ " match target port protoid: %s\n", i_port,
+ tcm_loop_dump_proto_id(tl_hba));
+ return -EINVAL;
+ }
+ port_ptr = &i_port[0];
+ goto check_newline;
+ }
+ ptr = strstr(i_port, "fc.");
+ if (ptr) {
+ if (tl_hba->tl_proto_id != SCSI_PROTOCOL_FCP) {
+ pr_err("Passed FCP Initiator Port %s does not"
+ " match target port protoid: %s\n", i_port,
+ tcm_loop_dump_proto_id(tl_hba));
+ return -EINVAL;
+ }
+ port_ptr = &i_port[3]; /* Skip over "fc." */
+ goto check_newline;
+ }
+ ptr = strstr(i_port, "iqn.");
+ if (ptr) {
+ if (tl_hba->tl_proto_id != SCSI_PROTOCOL_ISCSI) {
+ pr_err("Passed iSCSI Initiator Port %s does not"
+ " match target port protoid: %s\n", i_port,
+ tcm_loop_dump_proto_id(tl_hba));
+ return -EINVAL;
+ }
+ port_ptr = &i_port[0];
+ goto check_newline;
+ }
+ pr_err("Unable to locate prefix for emulated Initiator Port:"
+ " %s\n", i_port);
+ return -EINVAL;
+ /*
+ * Clear any trailing newline for the NAA WWN
+ */
+check_newline:
+ if (i_port[strlen(i_port)-1] == '\n')
+ i_port[strlen(i_port)-1] = '\0';
+
+ ret = tcm_loop_make_nexus(tl_tpg, port_ptr);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+TF_TPG_BASE_ATTR(tcm_loop, nexus, S_IRUGO | S_IWUSR);
+
+static ssize_t tcm_loop_tpg_show_transport_status(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct tcm_loop_tpg *tl_tpg = container_of(se_tpg,
+ struct tcm_loop_tpg, tl_se_tpg);
+ const char *status = NULL;
+ ssize_t ret = -EINVAL;
+
+ switch (tl_tpg->tl_transport_status) {
+ case TCM_TRANSPORT_ONLINE:
+ status = "online";
+ break;
+ case TCM_TRANSPORT_OFFLINE:
+ status = "offline";
+ break;
+ default:
+ break;
+ }
+
+ if (status)
+ ret = snprintf(page, PAGE_SIZE, "%s\n", status);
+
+ return ret;
+}
+
+static ssize_t tcm_loop_tpg_store_transport_status(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct tcm_loop_tpg *tl_tpg = container_of(se_tpg,
+ struct tcm_loop_tpg, tl_se_tpg);
+
+ if (!strncmp(page, "online", 6)) {
+ tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE;
+ return count;
+ }
+ if (!strncmp(page, "offline", 7)) {
+ tl_tpg->tl_transport_status = TCM_TRANSPORT_OFFLINE;
+ return count;
+ }
+ return -EINVAL;
+}
+
+TF_TPG_BASE_ATTR(tcm_loop, transport_status, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *tcm_loop_tpg_attrs[] = {
+ &tcm_loop_tpg_nexus.attr,
+ &tcm_loop_tpg_transport_status.attr,
+ NULL,
+};
+
+/* Start items for tcm_loop_naa_cit */
+
+static struct se_portal_group *tcm_loop_make_naa_tpg(
+ struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct tcm_loop_hba *tl_hba = container_of(wwn,
+ struct tcm_loop_hba, tl_hba_wwn);
+ struct tcm_loop_tpg *tl_tpg;
+ char *tpgt_str, *end_ptr;
+ int ret;
+ unsigned short int tpgt;
+
+ tpgt_str = strstr(name, "tpgt_");
+ if (!tpgt_str) {
+ pr_err("Unable to locate \"tpgt_#\" directory"
+ " group\n");
+ return ERR_PTR(-EINVAL);
+ }
+ tpgt_str += 5; /* Skip ahead of "tpgt_" */
+ tpgt = (unsigned short int) simple_strtoul(tpgt_str, &end_ptr, 0);
+
+ if (tpgt >= TL_TPGS_PER_HBA) {
+ pr_err("Passed tpgt: %hu exceeds TL_TPGS_PER_HBA:"
+ " %u\n", tpgt, TL_TPGS_PER_HBA);
+ return ERR_PTR(-EINVAL);
+ }
+ tl_tpg = &tl_hba->tl_hba_tpgs[tpgt];
+ tl_tpg->tl_hba = tl_hba;
+ tl_tpg->tl_tpgt = tpgt;
+ /*
+ * Register the tl_tpg as a emulated SAS TCM Target Endpoint
+ */
+ ret = core_tpg_register(&tcm_loop_fabric_configfs->tf_ops,
+ wwn, &tl_tpg->tl_se_tpg, tl_tpg,
+ TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0)
+ return ERR_PTR(-ENOMEM);
+
+ pr_debug("TCM_Loop_ConfigFS: Allocated Emulated %s"
+ " Target Port %s,t,0x%04x\n", tcm_loop_dump_proto_id(tl_hba),
+ config_item_name(&wwn->wwn_group.cg_item), tpgt);
+
+ return &tl_tpg->tl_se_tpg;
+}
+
+static void tcm_loop_drop_naa_tpg(
+ struct se_portal_group *se_tpg)
+{
+ struct se_wwn *wwn = se_tpg->se_tpg_wwn;
+ struct tcm_loop_tpg *tl_tpg = container_of(se_tpg,
+ struct tcm_loop_tpg, tl_se_tpg);
+ struct tcm_loop_hba *tl_hba;
+ unsigned short tpgt;
+
+ tl_hba = tl_tpg->tl_hba;
+ tpgt = tl_tpg->tl_tpgt;
+ /*
+ * Release the I_T Nexus for the Virtual SAS link if present
+ */
+ tcm_loop_drop_nexus(tl_tpg);
+ /*
+ * Deregister the tl_tpg as a emulated SAS TCM Target Endpoint
+ */
+ core_tpg_deregister(se_tpg);
+
+ tl_tpg->tl_hba = NULL;
+ tl_tpg->tl_tpgt = 0;
+
+ pr_debug("TCM_Loop_ConfigFS: Deallocated Emulated %s"
+ " Target Port %s,t,0x%04x\n", tcm_loop_dump_proto_id(tl_hba),
+ config_item_name(&wwn->wwn_group.cg_item), tpgt);
+}
+
+/* End items for tcm_loop_naa_cit */
+
+/* Start items for tcm_loop_cit */
+
+static struct se_wwn *tcm_loop_make_scsi_hba(
+ struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct tcm_loop_hba *tl_hba;
+ struct Scsi_Host *sh;
+ char *ptr;
+ int ret, off = 0;
+
+ tl_hba = kzalloc(sizeof(struct tcm_loop_hba), GFP_KERNEL);
+ if (!tl_hba) {
+ pr_err("Unable to allocate struct tcm_loop_hba\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ /*
+ * Determine the emulated Protocol Identifier and Target Port Name
+ * based on the incoming configfs directory name.
+ */
+ ptr = strstr(name, "naa.");
+ if (ptr) {
+ tl_hba->tl_proto_id = SCSI_PROTOCOL_SAS;
+ goto check_len;
+ }
+ ptr = strstr(name, "fc.");
+ if (ptr) {
+ tl_hba->tl_proto_id = SCSI_PROTOCOL_FCP;
+ off = 3; /* Skip over "fc." */
+ goto check_len;
+ }
+ ptr = strstr(name, "iqn.");
+ if (!ptr) {
+ pr_err("Unable to locate prefix for emulated Target "
+ "Port: %s\n", name);
+ ret = -EINVAL;
+ goto out;
+ }
+ tl_hba->tl_proto_id = SCSI_PROTOCOL_ISCSI;
+
+check_len:
+ if (strlen(name) >= TL_WWN_ADDR_LEN) {
+ pr_err("Emulated NAA %s Address: %s, exceeds"
+ " max: %d\n", name, tcm_loop_dump_proto_id(tl_hba),
+ TL_WWN_ADDR_LEN);
+ ret = -EINVAL;
+ goto out;
+ }
+ snprintf(&tl_hba->tl_wwn_address[0], TL_WWN_ADDR_LEN, "%s", &name[off]);
+
+ /*
+ * Call device_register(tl_hba->dev) to register the emulated
+ * Linux/SCSI LLD of type struct Scsi_Host at tl_hba->sh after
+ * device_register() callbacks in tcm_loop_driver_probe()
+ */
+ ret = tcm_loop_setup_hba_bus(tl_hba, tcm_loop_hba_no_cnt);
+ if (ret)
+ goto out;
+
+ sh = tl_hba->sh;
+ tcm_loop_hba_no_cnt++;
+ pr_debug("TCM_Loop_ConfigFS: Allocated emulated Target"
+ " %s Address: %s at Linux/SCSI Host ID: %d\n",
+ tcm_loop_dump_proto_id(tl_hba), name, sh->host_no);
+
+ return &tl_hba->tl_hba_wwn;
+out:
+ kfree(tl_hba);
+ return ERR_PTR(ret);
+}
+
+static void tcm_loop_drop_scsi_hba(
+ struct se_wwn *wwn)
+{
+ struct tcm_loop_hba *tl_hba = container_of(wwn,
+ struct tcm_loop_hba, tl_hba_wwn);
+
+ pr_debug("TCM_Loop_ConfigFS: Deallocating emulated Target"
+ " SAS Address: %s at Linux/SCSI Host ID: %d\n",
+ tl_hba->tl_wwn_address, tl_hba->sh->host_no);
+ /*
+ * Call device_unregister() on the original tl_hba->dev.
+ * tcm_loop_fabric_scsi.c:tcm_loop_release_adapter() will
+ * release *tl_hba;
+ */
+ device_unregister(&tl_hba->dev);
+}
+
+/* Start items for tcm_loop_cit */
+static ssize_t tcm_loop_wwn_show_attr_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "TCM Loopback Fabric module %s\n", TCM_LOOP_VERSION);
+}
+
+TF_WWN_ATTR_RO(tcm_loop, version);
+
+static struct configfs_attribute *tcm_loop_wwn_attrs[] = {
+ &tcm_loop_wwn_version.attr,
+ NULL,
+};
+
+/* End items for tcm_loop_cit */
+
+static int tcm_loop_register_configfs(void)
+{
+ struct target_fabric_configfs *fabric;
+ int ret;
+ /*
+ * Set the TCM Loop HBA counter to zero
+ */
+ tcm_loop_hba_no_cnt = 0;
+ /*
+ * Register the top level struct config_item_type with TCM core
+ */
+ fabric = target_fabric_configfs_init(THIS_MODULE, "loopback");
+ if (IS_ERR(fabric)) {
+ pr_err("tcm_loop_register_configfs() failed!\n");
+ return PTR_ERR(fabric);
+ }
+ /*
+ * Setup the fabric API of function pointers used by target_core_mod
+ */
+ fabric->tf_ops.get_fabric_name = &tcm_loop_get_fabric_name;
+ fabric->tf_ops.get_fabric_proto_ident = &tcm_loop_get_fabric_proto_ident;
+ fabric->tf_ops.tpg_get_wwn = &tcm_loop_get_endpoint_wwn;
+ fabric->tf_ops.tpg_get_tag = &tcm_loop_get_tag;
+ fabric->tf_ops.tpg_get_default_depth = &tcm_loop_get_default_depth;
+ fabric->tf_ops.tpg_get_pr_transport_id = &tcm_loop_get_pr_transport_id;
+ fabric->tf_ops.tpg_get_pr_transport_id_len =
+ &tcm_loop_get_pr_transport_id_len;
+ fabric->tf_ops.tpg_parse_pr_out_transport_id =
+ &tcm_loop_parse_pr_out_transport_id;
+ fabric->tf_ops.tpg_check_demo_mode = &tcm_loop_check_demo_mode;
+ fabric->tf_ops.tpg_check_demo_mode_cache =
+ &tcm_loop_check_demo_mode_cache;
+ fabric->tf_ops.tpg_check_demo_mode_write_protect =
+ &tcm_loop_check_demo_mode_write_protect;
+ fabric->tf_ops.tpg_check_prod_mode_write_protect =
+ &tcm_loop_check_prod_mode_write_protect;
+ /*
+ * The TCM loopback fabric module runs in demo-mode to a local
+ * virtual SCSI device, so fabric dependent initator ACLs are
+ * not required.
+ */
+ fabric->tf_ops.tpg_alloc_fabric_acl = &tcm_loop_tpg_alloc_fabric_acl;
+ fabric->tf_ops.tpg_release_fabric_acl =
+ &tcm_loop_tpg_release_fabric_acl;
+ fabric->tf_ops.tpg_get_inst_index = &tcm_loop_get_inst_index;
+ /*
+ * Used for setting up remaining TCM resources in process context
+ */
+ fabric->tf_ops.check_stop_free = &tcm_loop_check_stop_free;
+ fabric->tf_ops.release_cmd = &tcm_loop_release_cmd;
+ fabric->tf_ops.shutdown_session = &tcm_loop_shutdown_session;
+ fabric->tf_ops.close_session = &tcm_loop_close_session;
+ fabric->tf_ops.sess_get_index = &tcm_loop_sess_get_index;
+ fabric->tf_ops.sess_get_initiator_sid = NULL;
+ fabric->tf_ops.write_pending = &tcm_loop_write_pending;
+ fabric->tf_ops.write_pending_status = &tcm_loop_write_pending_status;
+ /*
+ * Not used for TCM loopback
+ */
+ fabric->tf_ops.set_default_node_attributes =
+ &tcm_loop_set_default_node_attributes;
+ fabric->tf_ops.get_task_tag = &tcm_loop_get_task_tag;
+ fabric->tf_ops.get_cmd_state = &tcm_loop_get_cmd_state;
+ fabric->tf_ops.queue_data_in = &tcm_loop_queue_data_in;
+ fabric->tf_ops.queue_status = &tcm_loop_queue_status;
+ fabric->tf_ops.queue_tm_rsp = &tcm_loop_queue_tm_rsp;
+ fabric->tf_ops.aborted_task = &tcm_loop_aborted_task;
+
+ /*
+ * Setup function pointers for generic logic in target_core_fabric_configfs.c
+ */
+ fabric->tf_ops.fabric_make_wwn = &tcm_loop_make_scsi_hba;
+ fabric->tf_ops.fabric_drop_wwn = &tcm_loop_drop_scsi_hba;
+ fabric->tf_ops.fabric_make_tpg = &tcm_loop_make_naa_tpg;
+ fabric->tf_ops.fabric_drop_tpg = &tcm_loop_drop_naa_tpg;
+ /*
+ * fabric_post_link() and fabric_pre_unlink() are used for
+ * registration and release of TCM Loop Virtual SCSI LUNs.
+ */
+ fabric->tf_ops.fabric_post_link = &tcm_loop_port_link;
+ fabric->tf_ops.fabric_pre_unlink = &tcm_loop_port_unlink;
+ fabric->tf_ops.fabric_make_np = NULL;
+ fabric->tf_ops.fabric_drop_np = NULL;
+ /*
+ * Setup default attribute lists for various fabric->tf_cit_tmpl
+ */
+ fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;
+ /*
+ * Once fabric->tf_ops has been setup, now register the fabric for
+ * use within TCM
+ */
+ ret = target_fabric_configfs_register(fabric);
+ if (ret < 0) {
+ pr_err("target_fabric_configfs_register() for"
+ " TCM_Loop failed!\n");
+ target_fabric_configfs_free(fabric);
+ return -1;
+ }
+ /*
+ * Setup our local pointer to *fabric.
+ */
+ tcm_loop_fabric_configfs = fabric;
+ pr_debug("TCM_LOOP[0] - Set fabric ->"
+ " tcm_loop_fabric_configfs\n");
+ return 0;
+}
+
+static void tcm_loop_deregister_configfs(void)
+{
+ if (!tcm_loop_fabric_configfs)
+ return;
+
+ target_fabric_configfs_deregister(tcm_loop_fabric_configfs);
+ tcm_loop_fabric_configfs = NULL;
+ pr_debug("TCM_LOOP[0] - Cleared"
+ " tcm_loop_fabric_configfs\n");
+}
+
+static int __init tcm_loop_fabric_init(void)
+{
+ int ret = -ENOMEM;
+
+ tcm_loop_workqueue = alloc_workqueue("tcm_loop", 0, 0);
+ if (!tcm_loop_workqueue)
+ goto out;
+
+ tcm_loop_cmd_cache = kmem_cache_create("tcm_loop_cmd_cache",
+ sizeof(struct tcm_loop_cmd),
+ __alignof__(struct tcm_loop_cmd),
+ 0, NULL);
+ if (!tcm_loop_cmd_cache) {
+ pr_debug("kmem_cache_create() for"
+ " tcm_loop_cmd_cache failed\n");
+ goto out_destroy_workqueue;
+ }
+
+ ret = tcm_loop_alloc_core_bus();
+ if (ret)
+ goto out_destroy_cache;
+
+ ret = tcm_loop_register_configfs();
+ if (ret)
+ goto out_release_core_bus;
+
+ return 0;
+
+out_release_core_bus:
+ tcm_loop_release_core_bus();
+out_destroy_cache:
+ kmem_cache_destroy(tcm_loop_cmd_cache);
+out_destroy_workqueue:
+ destroy_workqueue(tcm_loop_workqueue);
+out:
+ return ret;
+}
+
+static void __exit tcm_loop_fabric_exit(void)
+{
+ tcm_loop_deregister_configfs();
+ tcm_loop_release_core_bus();
+ kmem_cache_destroy(tcm_loop_cmd_cache);
+ destroy_workqueue(tcm_loop_workqueue);
+}
+
+MODULE_DESCRIPTION("TCM loopback virtual Linux/SCSI fabric module");
+MODULE_AUTHOR("Nicholas A. Bellinger <nab@risingtidesystems.com>");
+MODULE_LICENSE("GPL");
+module_init(tcm_loop_fabric_init);
+module_exit(tcm_loop_fabric_exit);
diff --git a/drivers/target/loopback/tcm_loop.h b/drivers/target/loopback/tcm_loop.h
new file mode 100644
index 00000000000..54c59d0b660
--- /dev/null
+++ b/drivers/target/loopback/tcm_loop.h
@@ -0,0 +1,67 @@
+#define TCM_LOOP_VERSION "v2.1-rc2"
+#define TL_WWN_ADDR_LEN 256
+#define TL_TPGS_PER_HBA 32
+
+/*
+ * Used in tcm_loop_driver_probe() for struct Scsi_Host->max_cmd_len
+ */
+#define TL_SCSI_MAX_CMD_LEN 32
+
+struct tcm_loop_cmd {
+ /* State of Linux/SCSI CDB+Data descriptor */
+ u32 sc_cmd_state;
+ /* Tagged command queueing */
+ u32 sc_cmd_tag;
+ /* Pointer to the CDB+Data descriptor from Linux/SCSI subsystem */
+ struct scsi_cmnd *sc;
+ /* The TCM I/O descriptor that is accessed via container_of() */
+ struct se_cmd tl_se_cmd;
+ struct work_struct work;
+ /* Sense buffer that will be mapped into outgoing status */
+ unsigned char tl_sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+struct tcm_loop_tmr {
+ atomic_t tmr_complete;
+ wait_queue_head_t tl_tmr_wait;
+};
+
+struct tcm_loop_nexus {
+ int it_nexus_active;
+ /*
+ * Pointer to Linux/SCSI HBA from linux/include/scsi_host.h
+ */
+ struct scsi_host *sh;
+ /*
+ * Pointer to TCM session for I_T Nexus
+ */
+ struct se_session *se_sess;
+};
+
+struct tcm_loop_nacl {
+ struct se_node_acl se_node_acl;
+};
+
+#define TCM_TRANSPORT_ONLINE 0
+#define TCM_TRANSPORT_OFFLINE 1
+
+struct tcm_loop_tpg {
+ unsigned short tl_tpgt;
+ unsigned short tl_transport_status;
+ atomic_t tl_tpg_port_count;
+ struct se_portal_group tl_se_tpg;
+ struct tcm_loop_hba *tl_hba;
+};
+
+struct tcm_loop_hba {
+ u8 tl_proto_id;
+ unsigned char tl_wwn_address[TL_WWN_ADDR_LEN];
+ struct se_hba_s *se_hba;
+ struct se_lun *tl_hba_lun;
+ struct se_port *tl_hba_lun_sep;
+ struct tcm_loop_nexus *tl_nexus;
+ struct device dev;
+ struct Scsi_Host *sh;
+ struct tcm_loop_tpg tl_hba_tpgs[TL_TPGS_PER_HBA];
+ struct se_wwn tl_hba_wwn;
+};
diff --git a/drivers/target/sbp/Kconfig b/drivers/target/sbp/Kconfig
new file mode 100644
index 00000000000..1614bc710d4
--- /dev/null
+++ b/drivers/target/sbp/Kconfig
@@ -0,0 +1,11 @@
+config SBP_TARGET
+ tristate "FireWire SBP-2 fabric module"
+ depends on FIREWIRE
+ help
+ Say Y or M here to enable SCSI target functionality over FireWire.
+ This enables you to expose SCSI devices to other nodes on the FireWire
+ bus, for example hard disks. Similar to FireWire Target Disk mode on
+ many Apple computers.
+
+ To compile this driver as a module, say M here: The module will be
+ called sbp-target.
diff --git a/drivers/target/sbp/Makefile b/drivers/target/sbp/Makefile
new file mode 100644
index 00000000000..27747ad054c
--- /dev/null
+++ b/drivers/target/sbp/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SBP_TARGET) += sbp_target.o
diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c
new file mode 100644
index 00000000000..e7e93727553
--- /dev/null
+++ b/drivers/target/sbp/sbp_target.c
@@ -0,0 +1,2614 @@
+/*
+ * SBP2 target driver (SCSI over IEEE1394 in target mode)
+ *
+ * Copyright (C) 2011 Chris Boot <bootc@bootc.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define KMSG_COMPONENT "sbp_target"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+#include <asm/unaligned.h>
+
+#include "sbp_target.h"
+
+/* Local pointer to allocated TCM configfs fabric module */
+static struct target_fabric_configfs *sbp_fabric_configfs;
+
+/* FireWire address region for management and command block address handlers */
+static const struct fw_address_region sbp_register_region = {
+ .start = CSR_REGISTER_BASE + 0x10000,
+ .end = 0x1000000000000ULL,
+};
+
+static const u32 sbp_unit_directory_template[] = {
+ 0x1200609e, /* unit_specifier_id: NCITS/T10 */
+ 0x13010483, /* unit_sw_version: 1155D Rev 4 */
+ 0x3800609e, /* command_set_specifier_id: NCITS/T10 */
+ 0x390104d8, /* command_set: SPC-2 */
+ 0x3b000000, /* command_set_revision: 0 */
+ 0x3c000001, /* firmware_revision: 1 */
+};
+
+#define SESSION_MAINTENANCE_INTERVAL HZ
+
+static atomic_t login_id = ATOMIC_INIT(0);
+
+static void session_maintenance_work(struct work_struct *);
+static int sbp_run_transaction(struct fw_card *, int, int, int, int,
+ unsigned long long, void *, size_t);
+
+static int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
+{
+ int ret;
+ __be32 high, low;
+
+ ret = sbp_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
+ &high, sizeof(high));
+ if (ret != RCODE_COMPLETE)
+ return ret;
+
+ ret = sbp_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
+ &low, sizeof(low));
+ if (ret != RCODE_COMPLETE)
+ return ret;
+
+ *guid = (u64)be32_to_cpu(high) << 32 | be32_to_cpu(low);
+
+ return RCODE_COMPLETE;
+}
+
+static struct sbp_session *sbp_session_find_by_guid(
+ struct sbp_tpg *tpg, u64 guid)
+{
+ struct se_session *se_sess;
+ struct sbp_session *sess, *found = NULL;
+
+ spin_lock_bh(&tpg->se_tpg.session_lock);
+ list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+ sess = se_sess->fabric_sess_ptr;
+ if (sess->guid == guid)
+ found = sess;
+ }
+ spin_unlock_bh(&tpg->se_tpg.session_lock);
+
+ return found;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_lun(
+ struct sbp_session *session, struct se_lun *lun)
+{
+ struct sbp_login_descriptor *login, *found = NULL;
+
+ spin_lock_bh(&session->lock);
+ list_for_each_entry(login, &session->login_list, link) {
+ if (login->lun == lun)
+ found = login;
+ }
+ spin_unlock_bh(&session->lock);
+
+ return found;
+}
+
+static int sbp_login_count_all_by_lun(
+ struct sbp_tpg *tpg,
+ struct se_lun *lun,
+ int exclusive)
+{
+ struct se_session *se_sess;
+ struct sbp_session *sess;
+ struct sbp_login_descriptor *login;
+ int count = 0;
+
+ spin_lock_bh(&tpg->se_tpg.session_lock);
+ list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+ sess = se_sess->fabric_sess_ptr;
+
+ spin_lock_bh(&sess->lock);
+ list_for_each_entry(login, &sess->login_list, link) {
+ if (login->lun != lun)
+ continue;
+
+ if (!exclusive || login->exclusive)
+ count++;
+ }
+ spin_unlock_bh(&sess->lock);
+ }
+ spin_unlock_bh(&tpg->se_tpg.session_lock);
+
+ return count;
+}
+
+static struct sbp_login_descriptor *sbp_login_find_by_id(
+ struct sbp_tpg *tpg, int login_id)
+{
+ struct se_session *se_sess;
+ struct sbp_session *sess;
+ struct sbp_login_descriptor *login, *found = NULL;
+
+ spin_lock_bh(&tpg->se_tpg.session_lock);
+ list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
+ sess = se_sess->fabric_sess_ptr;
+
+ spin_lock_bh(&sess->lock);
+ list_for_each_entry(login, &sess->login_list, link) {
+ if (login->login_id == login_id)
+ found = login;
+ }
+ spin_unlock_bh(&sess->lock);
+ }
+ spin_unlock_bh(&tpg->se_tpg.session_lock);
+
+ return found;
+}
+
+static struct se_lun *sbp_get_lun_from_tpg(struct sbp_tpg *tpg, int lun)
+{
+ struct se_portal_group *se_tpg = &tpg->se_tpg;
+ struct se_lun *se_lun;
+
+ if (lun >= TRANSPORT_MAX_LUNS_PER_TPG)
+ return ERR_PTR(-EINVAL);
+
+ spin_lock(&se_tpg->tpg_lun_lock);
+ se_lun = se_tpg->tpg_lun_list[lun];
+
+ if (se_lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE)
+ se_lun = ERR_PTR(-ENODEV);
+
+ spin_unlock(&se_tpg->tpg_lun_lock);
+
+ return se_lun;
+}
+
+static struct sbp_session *sbp_session_create(
+ struct sbp_tpg *tpg,
+ u64 guid)
+{
+ struct sbp_session *sess;
+ int ret;
+ char guid_str[17];
+ struct se_node_acl *se_nacl;
+
+ sess = kmalloc(sizeof(*sess), GFP_KERNEL);
+ if (!sess) {
+ pr_err("failed to allocate session descriptor\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sess->se_sess = transport_init_session(TARGET_PROT_NORMAL);
+ if (IS_ERR(sess->se_sess)) {
+ pr_err("failed to init se_session\n");
+
+ ret = PTR_ERR(sess->se_sess);
+ kfree(sess);
+ return ERR_PTR(ret);
+ }
+
+ snprintf(guid_str, sizeof(guid_str), "%016llx", guid);
+
+ se_nacl = core_tpg_check_initiator_node_acl(&tpg->se_tpg, guid_str);
+ if (!se_nacl) {
+ pr_warn("Node ACL not found for %s\n", guid_str);
+
+ transport_free_session(sess->se_sess);
+ kfree(sess);
+
+ return ERR_PTR(-EPERM);
+ }
+
+ sess->se_sess->se_node_acl = se_nacl;
+
+ spin_lock_init(&sess->lock);
+ INIT_LIST_HEAD(&sess->login_list);
+ INIT_DELAYED_WORK(&sess->maint_work, session_maintenance_work);
+
+ sess->guid = guid;
+
+ transport_register_session(&tpg->se_tpg, se_nacl, sess->se_sess, sess);
+
+ return sess;
+}
+
+static void sbp_session_release(struct sbp_session *sess, bool cancel_work)
+{
+ spin_lock_bh(&sess->lock);
+ if (!list_empty(&sess->login_list)) {
+ spin_unlock_bh(&sess->lock);
+ return;
+ }
+ spin_unlock_bh(&sess->lock);
+
+ if (cancel_work)
+ cancel_delayed_work_sync(&sess->maint_work);
+
+ transport_deregister_session_configfs(sess->se_sess);
+ transport_deregister_session(sess->se_sess);
+
+ if (sess->card)
+ fw_card_put(sess->card);
+
+ kfree(sess);
+}
+
+static void sbp_target_agent_unregister(struct sbp_target_agent *);
+
+static void sbp_login_release(struct sbp_login_descriptor *login,
+ bool cancel_work)
+{
+ struct sbp_session *sess = login->sess;
+
+ /* FIXME: abort/wait on tasks */
+
+ sbp_target_agent_unregister(login->tgt_agt);
+
+ if (sess) {
+ spin_lock_bh(&sess->lock);
+ list_del(&login->link);
+ spin_unlock_bh(&sess->lock);
+
+ sbp_session_release(sess, cancel_work);
+ }
+
+ kfree(login);
+}
+
+static struct sbp_target_agent *sbp_target_agent_register(
+ struct sbp_login_descriptor *);
+
+static void sbp_management_request_login(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ struct sbp_tport *tport = agent->tport;
+ struct sbp_tpg *tpg = tport->tpg;
+ struct se_lun *se_lun;
+ int ret;
+ u64 guid;
+ struct sbp_session *sess;
+ struct sbp_login_descriptor *login;
+ struct sbp_login_response_block *response;
+ int login_response_len;
+
+ se_lun = sbp_get_lun_from_tpg(tpg,
+ LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+ if (IS_ERR(se_lun)) {
+ pr_notice("login to unknown LUN: %d\n",
+ LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LUN_NOTSUPP));
+ return;
+ }
+
+ ret = read_peer_guid(&guid, req);
+ if (ret != RCODE_COMPLETE) {
+ pr_warn("failed to read peer GUID: %d\n", ret);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ return;
+ }
+
+ pr_notice("mgt_agent LOGIN to LUN %d from %016llx\n",
+ se_lun->unpacked_lun, guid);
+
+ sess = sbp_session_find_by_guid(tpg, guid);
+ if (sess) {
+ login = sbp_login_find_by_lun(sess, se_lun);
+ if (login) {
+ pr_notice("initiator already logged-in\n");
+
+ /*
+ * SBP-2 R4 says we should return access denied, but
+ * that can confuse initiators. Instead we need to
+ * treat this like a reconnect, but send the login
+ * response block like a fresh login.
+ *
+ * This is required particularly in the case of Apple
+ * devices booting off the FireWire target, where
+ * the firmware has an active login to the target. When
+ * the OS takes control of the session it issues its own
+ * LOGIN rather than a RECONNECT. To avoid the machine
+ * waiting until the reconnect_hold expires, we can skip
+ * the ACCESS_DENIED errors to speed things up.
+ */
+
+ goto already_logged_in;
+ }
+ }
+
+ /*
+ * check exclusive bit in login request
+ * reject with access_denied if any logins present
+ */
+ if (LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc)) &&
+ sbp_login_count_all_by_lun(tpg, se_lun, 0)) {
+ pr_warn("refusing exclusive login with other active logins\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ /*
+ * check exclusive bit in any existing login descriptor
+ * reject with access_denied if any exclusive logins present
+ */
+ if (sbp_login_count_all_by_lun(tpg, se_lun, 1)) {
+ pr_warn("refusing login while another exclusive login present\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ /*
+ * check we haven't exceeded the number of allowed logins
+ * reject with resources_unavailable if we have
+ */
+ if (sbp_login_count_all_by_lun(tpg, se_lun, 0) >=
+ tport->max_logins_per_lun) {
+ pr_warn("max number of logins reached\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ if (!sess) {
+ sess = sbp_session_create(tpg, guid);
+ if (IS_ERR(sess)) {
+ switch (PTR_ERR(sess)) {
+ case -EPERM:
+ ret = SBP_STATUS_ACCESS_DENIED;
+ break;
+ default:
+ ret = SBP_STATUS_RESOURCES_UNAVAIL;
+ break;
+ }
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(
+ STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(ret));
+ return;
+ }
+
+ sess->node_id = req->node_addr;
+ sess->card = fw_card_get(req->card);
+ sess->generation = req->generation;
+ sess->speed = req->speed;
+
+ schedule_delayed_work(&sess->maint_work,
+ SESSION_MAINTENANCE_INTERVAL);
+ }
+
+ /* only take the latest reconnect_hold into account */
+ sess->reconnect_hold = min(
+ 1 << LOGIN_ORB_RECONNECT(be32_to_cpu(req->orb.misc)),
+ tport->max_reconnect_timeout) - 1;
+
+ login = kmalloc(sizeof(*login), GFP_KERNEL);
+ if (!login) {
+ pr_err("failed to allocate login descriptor\n");
+
+ sbp_session_release(sess, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ login->sess = sess;
+ login->lun = se_lun;
+ login->status_fifo_addr = sbp2_pointer_to_addr(&req->orb.status_fifo);
+ login->exclusive = LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc));
+ login->login_id = atomic_inc_return(&login_id);
+
+ login->tgt_agt = sbp_target_agent_register(login);
+ if (IS_ERR(login->tgt_agt)) {
+ ret = PTR_ERR(login->tgt_agt);
+ pr_err("failed to map command block handler: %d\n", ret);
+
+ sbp_session_release(sess, true);
+ kfree(login);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ spin_lock_bh(&sess->lock);
+ list_add_tail(&login->link, &sess->login_list);
+ spin_unlock_bh(&sess->lock);
+
+already_logged_in:
+ response = kzalloc(sizeof(*response), GFP_KERNEL);
+ if (!response) {
+ pr_err("failed to allocate login response block\n");
+
+ sbp_login_release(login, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
+ return;
+ }
+
+ login_response_len = clamp_val(
+ LOGIN_ORB_RESPONSE_LENGTH(be32_to_cpu(req->orb.length)),
+ 12, sizeof(*response));
+ response->misc = cpu_to_be32(
+ ((login_response_len & 0xffff) << 16) |
+ (login->login_id & 0xffff));
+ response->reconnect_hold = cpu_to_be32(sess->reconnect_hold & 0xffff);
+ addr_to_sbp2_pointer(login->tgt_agt->handler.offset,
+ &response->command_block_agent);
+
+ ret = sbp_run_transaction(sess->card, TCODE_WRITE_BLOCK_REQUEST,
+ sess->node_id, sess->generation, sess->speed,
+ sbp2_pointer_to_addr(&req->orb.ptr2), response,
+ login_response_len);
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("failed to write login response block: %x\n", ret);
+
+ kfree(response);
+ sbp_login_release(login, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ return;
+ }
+
+ kfree(response);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static void sbp_management_request_query_logins(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ pr_notice("QUERY LOGINS not implemented\n");
+ /* FIXME: implement */
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+}
+
+static void sbp_management_request_reconnect(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ struct sbp_tport *tport = agent->tport;
+ struct sbp_tpg *tpg = tport->tpg;
+ int ret;
+ u64 guid;
+ struct sbp_login_descriptor *login;
+
+ ret = read_peer_guid(&guid, req);
+ if (ret != RCODE_COMPLETE) {
+ pr_warn("failed to read peer GUID: %d\n", ret);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ return;
+ }
+
+ pr_notice("mgt_agent RECONNECT from %016llx\n", guid);
+
+ login = sbp_login_find_by_id(tpg,
+ RECONNECT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc)));
+
+ if (!login) {
+ pr_err("mgt_agent RECONNECT unknown login ID\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ if (login->sess->guid != guid) {
+ pr_err("mgt_agent RECONNECT login GUID doesn't match\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ spin_lock_bh(&login->sess->lock);
+ if (login->sess->card)
+ fw_card_put(login->sess->card);
+
+ /* update the node details */
+ login->sess->generation = req->generation;
+ login->sess->node_id = req->node_addr;
+ login->sess->card = fw_card_get(req->card);
+ login->sess->speed = req->speed;
+ spin_unlock_bh(&login->sess->lock);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static void sbp_management_request_logout(
+ struct sbp_management_agent *agent, struct sbp_management_request *req,
+ int *status_data_size)
+{
+ struct sbp_tport *tport = agent->tport;
+ struct sbp_tpg *tpg = tport->tpg;
+ int id;
+ struct sbp_login_descriptor *login;
+
+ id = LOGOUT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc));
+
+ login = sbp_login_find_by_id(tpg, id);
+ if (!login) {
+ pr_warn("cannot find login: %d\n", id);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LOGIN_ID_UNKNOWN));
+ return;
+ }
+
+ pr_info("mgt_agent LOGOUT from LUN %d session %d\n",
+ login->lun->unpacked_lun, login->login_id);
+
+ if (req->node_addr != login->sess->node_id) {
+ pr_warn("logout from different node ID\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
+ return;
+ }
+
+ sbp_login_release(login, true);
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static void session_check_for_reset(struct sbp_session *sess)
+{
+ bool card_valid = false;
+
+ spin_lock_bh(&sess->lock);
+
+ if (sess->card) {
+ spin_lock_irq(&sess->card->lock);
+ card_valid = (sess->card->local_node != NULL);
+ spin_unlock_irq(&sess->card->lock);
+
+ if (!card_valid) {
+ fw_card_put(sess->card);
+ sess->card = NULL;
+ }
+ }
+
+ if (!card_valid || (sess->generation != sess->card->generation)) {
+ pr_info("Waiting for reconnect from node: %016llx\n",
+ sess->guid);
+
+ sess->node_id = -1;
+ sess->reconnect_expires = get_jiffies_64() +
+ ((sess->reconnect_hold + 1) * HZ);
+ }
+
+ spin_unlock_bh(&sess->lock);
+}
+
+static void session_reconnect_expired(struct sbp_session *sess)
+{
+ struct sbp_login_descriptor *login, *temp;
+ LIST_HEAD(login_list);
+
+ pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
+
+ spin_lock_bh(&sess->lock);
+ list_for_each_entry_safe(login, temp, &sess->login_list, link) {
+ login->sess = NULL;
+ list_move_tail(&login->link, &login_list);
+ }
+ spin_unlock_bh(&sess->lock);
+
+ list_for_each_entry_safe(login, temp, &login_list, link) {
+ list_del(&login->link);
+ sbp_login_release(login, false);
+ }
+
+ sbp_session_release(sess, false);
+}
+
+static void session_maintenance_work(struct work_struct *work)
+{
+ struct sbp_session *sess = container_of(work, struct sbp_session,
+ maint_work.work);
+
+ /* could be called while tearing down the session */
+ spin_lock_bh(&sess->lock);
+ if (list_empty(&sess->login_list)) {
+ spin_unlock_bh(&sess->lock);
+ return;
+ }
+ spin_unlock_bh(&sess->lock);
+
+ if (sess->node_id != -1) {
+ /* check for bus reset and make node_id invalid */
+ session_check_for_reset(sess);
+
+ schedule_delayed_work(&sess->maint_work,
+ SESSION_MAINTENANCE_INTERVAL);
+ } else if (!time_after64(get_jiffies_64(), sess->reconnect_expires)) {
+ /* still waiting for reconnect */
+ schedule_delayed_work(&sess->maint_work,
+ SESSION_MAINTENANCE_INTERVAL);
+ } else {
+ /* reconnect timeout has expired */
+ session_reconnect_expired(sess);
+ }
+}
+
+static int tgt_agent_rw_agent_state(struct fw_card *card, int tcode, void *data,
+ struct sbp_target_agent *agent)
+{
+ int state;
+
+ switch (tcode) {
+ case TCODE_READ_QUADLET_REQUEST:
+ pr_debug("tgt_agent AGENT_STATE READ\n");
+
+ spin_lock_bh(&agent->lock);
+ state = agent->state;
+ spin_unlock_bh(&agent->lock);
+
+ *(__be32 *)data = cpu_to_be32(state);
+
+ return RCODE_COMPLETE;
+
+ case TCODE_WRITE_QUADLET_REQUEST:
+ /* ignored */
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_agent_reset(struct fw_card *card, int tcode, void *data,
+ struct sbp_target_agent *agent)
+{
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ pr_debug("tgt_agent AGENT_RESET\n");
+ spin_lock_bh(&agent->lock);
+ agent->state = AGENT_STATE_RESET;
+ spin_unlock_bh(&agent->lock);
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_orb_pointer(struct fw_card *card, int tcode, void *data,
+ struct sbp_target_agent *agent)
+{
+ struct sbp2_pointer *ptr = data;
+
+ switch (tcode) {
+ case TCODE_WRITE_BLOCK_REQUEST:
+ spin_lock_bh(&agent->lock);
+ if (agent->state != AGENT_STATE_SUSPENDED &&
+ agent->state != AGENT_STATE_RESET) {
+ spin_unlock_bh(&agent->lock);
+ pr_notice("Ignoring ORB_POINTER write while active.\n");
+ return RCODE_CONFLICT_ERROR;
+ }
+ agent->state = AGENT_STATE_ACTIVE;
+ spin_unlock_bh(&agent->lock);
+
+ agent->orb_pointer = sbp2_pointer_to_addr(ptr);
+ agent->doorbell = false;
+
+ pr_debug("tgt_agent ORB_POINTER write: 0x%llx\n",
+ agent->orb_pointer);
+
+ queue_work(system_unbound_wq, &agent->work);
+
+ return RCODE_COMPLETE;
+
+ case TCODE_READ_BLOCK_REQUEST:
+ pr_debug("tgt_agent ORB_POINTER READ\n");
+ spin_lock_bh(&agent->lock);
+ addr_to_sbp2_pointer(agent->orb_pointer, ptr);
+ spin_unlock_bh(&agent->lock);
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_doorbell(struct fw_card *card, int tcode, void *data,
+ struct sbp_target_agent *agent)
+{
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ spin_lock_bh(&agent->lock);
+ if (agent->state != AGENT_STATE_SUSPENDED) {
+ spin_unlock_bh(&agent->lock);
+ pr_debug("Ignoring DOORBELL while active.\n");
+ return RCODE_CONFLICT_ERROR;
+ }
+ agent->state = AGENT_STATE_ACTIVE;
+ spin_unlock_bh(&agent->lock);
+
+ agent->doorbell = true;
+
+ pr_debug("tgt_agent DOORBELL\n");
+
+ queue_work(system_unbound_wq, &agent->work);
+
+ return RCODE_COMPLETE;
+
+ case TCODE_READ_QUADLET_REQUEST:
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static int tgt_agent_rw_unsolicited_status_enable(struct fw_card *card,
+ int tcode, void *data, struct sbp_target_agent *agent)
+{
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ pr_debug("tgt_agent UNSOLICITED_STATUS_ENABLE\n");
+ /* ignored as we don't send unsolicited status */
+ return RCODE_COMPLETE;
+
+ case TCODE_READ_QUADLET_REQUEST:
+ return RCODE_COMPLETE;
+
+ default:
+ return RCODE_TYPE_ERROR;
+ }
+}
+
+static void tgt_agent_rw(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source, int generation,
+ unsigned long long offset, void *data, size_t length,
+ void *callback_data)
+{
+ struct sbp_target_agent *agent = callback_data;
+ struct sbp_session *sess = agent->login->sess;
+ int sess_gen, sess_node, rcode;
+
+ spin_lock_bh(&sess->lock);
+ sess_gen = sess->generation;
+ sess_node = sess->node_id;
+ spin_unlock_bh(&sess->lock);
+
+ if (generation != sess_gen) {
+ pr_notice("ignoring request with wrong generation\n");
+ rcode = RCODE_TYPE_ERROR;
+ goto out;
+ }
+
+ if (source != sess_node) {
+ pr_notice("ignoring request from foreign node (%x != %x)\n",
+ source, sess_node);
+ rcode = RCODE_TYPE_ERROR;
+ goto out;
+ }
+
+ /* turn offset into the offset from the start of the block */
+ offset -= agent->handler.offset;
+
+ if (offset == 0x00 && length == 4) {
+ /* AGENT_STATE */
+ rcode = tgt_agent_rw_agent_state(card, tcode, data, agent);
+ } else if (offset == 0x04 && length == 4) {
+ /* AGENT_RESET */
+ rcode = tgt_agent_rw_agent_reset(card, tcode, data, agent);
+ } else if (offset == 0x08 && length == 8) {
+ /* ORB_POINTER */
+ rcode = tgt_agent_rw_orb_pointer(card, tcode, data, agent);
+ } else if (offset == 0x10 && length == 4) {
+ /* DOORBELL */
+ rcode = tgt_agent_rw_doorbell(card, tcode, data, agent);
+ } else if (offset == 0x14 && length == 4) {
+ /* UNSOLICITED_STATUS_ENABLE */
+ rcode = tgt_agent_rw_unsolicited_status_enable(card, tcode,
+ data, agent);
+ } else {
+ rcode = RCODE_ADDRESS_ERROR;
+ }
+
+out:
+ fw_send_response(card, request, rcode);
+}
+
+static void sbp_handle_command(struct sbp_target_request *);
+static int sbp_send_status(struct sbp_target_request *);
+static void sbp_free_request(struct sbp_target_request *);
+
+static void tgt_agent_process_work(struct work_struct *work)
+{
+ struct sbp_target_request *req =
+ container_of(work, struct sbp_target_request, work);
+
+ pr_debug("tgt_orb ptr:0x%llx next_ORB:0x%llx data_descriptor:0x%llx misc:0x%x\n",
+ req->orb_pointer,
+ sbp2_pointer_to_addr(&req->orb.next_orb),
+ sbp2_pointer_to_addr(&req->orb.data_descriptor),
+ be32_to_cpu(req->orb.misc));
+
+ if (req->orb_pointer >> 32)
+ pr_debug("ORB with high bits set\n");
+
+ switch (ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc))) {
+ case 0:/* Format specified by this standard */
+ sbp_handle_command(req);
+ return;
+ case 1: /* Reserved for future standardization */
+ case 2: /* Vendor-dependent */
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(
+ STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(
+ SBP_STATUS_REQ_TYPE_NOTSUPP));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ case 3: /* Dummy ORB */
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(
+ STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(
+ SBP_STATUS_DUMMY_ORB_COMPLETE));
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ default:
+ BUG();
+ }
+}
+
+/* used to double-check we haven't been issued an AGENT_RESET */
+static inline bool tgt_agent_check_active(struct sbp_target_agent *agent)
+{
+ bool active;
+
+ spin_lock_bh(&agent->lock);
+ active = (agent->state == AGENT_STATE_ACTIVE);
+ spin_unlock_bh(&agent->lock);
+
+ return active;
+}
+
+static void tgt_agent_fetch_work(struct work_struct *work)
+{
+ struct sbp_target_agent *agent =
+ container_of(work, struct sbp_target_agent, work);
+ struct sbp_session *sess = agent->login->sess;
+ struct sbp_target_request *req;
+ int ret;
+ bool doorbell = agent->doorbell;
+ u64 next_orb = agent->orb_pointer;
+
+ while (next_orb && tgt_agent_check_active(agent)) {
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req) {
+ spin_lock_bh(&agent->lock);
+ agent->state = AGENT_STATE_DEAD;
+ spin_unlock_bh(&agent->lock);
+ return;
+ }
+
+ req->login = agent->login;
+ req->orb_pointer = next_orb;
+
+ req->status.status = cpu_to_be32(STATUS_BLOCK_ORB_OFFSET_HIGH(
+ req->orb_pointer >> 32));
+ req->status.orb_low = cpu_to_be32(
+ req->orb_pointer & 0xfffffffc);
+
+ /* read in the ORB */
+ ret = sbp_run_transaction(sess->card, TCODE_READ_BLOCK_REQUEST,
+ sess->node_id, sess->generation, sess->speed,
+ req->orb_pointer, &req->orb, sizeof(req->orb));
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("tgt_orb fetch failed: %x\n", ret);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_SRC(
+ STATUS_SRC_ORB_FINISHED) |
+ STATUS_BLOCK_RESP(
+ STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(1) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(
+ SBP_STATUS_UNSPECIFIED_ERROR));
+ spin_lock_bh(&agent->lock);
+ agent->state = AGENT_STATE_DEAD;
+ spin_unlock_bh(&agent->lock);
+
+ sbp_send_status(req);
+ sbp_free_request(req);
+ return;
+ }
+
+ /* check the next_ORB field */
+ if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
+ next_orb = 0;
+ req->status.status |= cpu_to_be32(STATUS_BLOCK_SRC(
+ STATUS_SRC_ORB_FINISHED));
+ } else {
+ next_orb = sbp2_pointer_to_addr(&req->orb.next_orb);
+ req->status.status |= cpu_to_be32(STATUS_BLOCK_SRC(
+ STATUS_SRC_ORB_CONTINUING));
+ }
+
+ if (tgt_agent_check_active(agent) && !doorbell) {
+ INIT_WORK(&req->work, tgt_agent_process_work);
+ queue_work(system_unbound_wq, &req->work);
+ } else {
+ /* don't process this request, just check next_ORB */
+ sbp_free_request(req);
+ }
+
+ spin_lock_bh(&agent->lock);
+ doorbell = agent->doorbell = false;
+
+ /* check if we should carry on processing */
+ if (next_orb)
+ agent->orb_pointer = next_orb;
+ else
+ agent->state = AGENT_STATE_SUSPENDED;
+
+ spin_unlock_bh(&agent->lock);
+ };
+}
+
+static struct sbp_target_agent *sbp_target_agent_register(
+ struct sbp_login_descriptor *login)
+{
+ struct sbp_target_agent *agent;
+ int ret;
+
+ agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+ if (!agent)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock_init(&agent->lock);
+
+ agent->handler.length = 0x20;
+ agent->handler.address_callback = tgt_agent_rw;
+ agent->handler.callback_data = agent;
+
+ agent->login = login;
+ agent->state = AGENT_STATE_RESET;
+ INIT_WORK(&agent->work, tgt_agent_fetch_work);
+ agent->orb_pointer = 0;
+ agent->doorbell = false;
+
+ ret = fw_core_add_address_handler(&agent->handler,
+ &sbp_register_region);
+ if (ret < 0) {
+ kfree(agent);
+ return ERR_PTR(ret);
+ }
+
+ return agent;
+}
+
+static void sbp_target_agent_unregister(struct sbp_target_agent *agent)
+{
+ fw_core_remove_address_handler(&agent->handler);
+ cancel_work_sync(&agent->work);
+ kfree(agent);
+}
+
+/*
+ * Simple wrapper around fw_run_transaction that retries the transaction several
+ * times in case of failure, with an exponential backoff.
+ */
+static int sbp_run_transaction(struct fw_card *card, int tcode, int destination_id,
+ int generation, int speed, unsigned long long offset,
+ void *payload, size_t length)
+{
+ int attempt, ret, delay;
+
+ for (attempt = 1; attempt <= 5; attempt++) {
+ ret = fw_run_transaction(card, tcode, destination_id,
+ generation, speed, offset, payload, length);
+
+ switch (ret) {
+ case RCODE_COMPLETE:
+ case RCODE_TYPE_ERROR:
+ case RCODE_ADDRESS_ERROR:
+ case RCODE_GENERATION:
+ return ret;
+
+ default:
+ delay = 5 * attempt * attempt;
+ usleep_range(delay, delay * 2);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Wrapper around sbp_run_transaction that gets the card, destination,
+ * generation and speed out of the request's session.
+ */
+static int sbp_run_request_transaction(struct sbp_target_request *req,
+ int tcode, unsigned long long offset, void *payload,
+ size_t length)
+{
+ struct sbp_login_descriptor *login = req->login;
+ struct sbp_session *sess = login->sess;
+ struct fw_card *card;
+ int node_id, generation, speed, ret;
+
+ spin_lock_bh(&sess->lock);
+ card = fw_card_get(sess->card);
+ node_id = sess->node_id;
+ generation = sess->generation;
+ speed = sess->speed;
+ spin_unlock_bh(&sess->lock);
+
+ ret = sbp_run_transaction(card, tcode, node_id, generation, speed,
+ offset, payload, length);
+
+ fw_card_put(card);
+
+ return ret;
+}
+
+static int sbp_fetch_command(struct sbp_target_request *req)
+{
+ int ret, cmd_len, copy_len;
+
+ cmd_len = scsi_command_size(req->orb.command_block);
+
+ req->cmd_buf = kmalloc(cmd_len, GFP_KERNEL);
+ if (!req->cmd_buf)
+ return -ENOMEM;
+
+ memcpy(req->cmd_buf, req->orb.command_block,
+ min_t(int, cmd_len, sizeof(req->orb.command_block)));
+
+ if (cmd_len > sizeof(req->orb.command_block)) {
+ pr_debug("sbp_fetch_command: filling in long command\n");
+ copy_len = cmd_len - sizeof(req->orb.command_block);
+
+ ret = sbp_run_request_transaction(req,
+ TCODE_READ_BLOCK_REQUEST,
+ req->orb_pointer + sizeof(req->orb),
+ req->cmd_buf + sizeof(req->orb.command_block),
+ copy_len);
+ if (ret != RCODE_COMPLETE)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sbp_fetch_page_table(struct sbp_target_request *req)
+{
+ int pg_tbl_sz, ret;
+ struct sbp_page_table_entry *pg_tbl;
+
+ if (!CMDBLK_ORB_PG_TBL_PRESENT(be32_to_cpu(req->orb.misc)))
+ return 0;
+
+ pg_tbl_sz = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc)) *
+ sizeof(struct sbp_page_table_entry);
+
+ pg_tbl = kmalloc(pg_tbl_sz, GFP_KERNEL);
+ if (!pg_tbl)
+ return -ENOMEM;
+
+ ret = sbp_run_request_transaction(req, TCODE_READ_BLOCK_REQUEST,
+ sbp2_pointer_to_addr(&req->orb.data_descriptor),
+ pg_tbl, pg_tbl_sz);
+ if (ret != RCODE_COMPLETE) {
+ kfree(pg_tbl);
+ return -EIO;
+ }
+
+ req->pg_tbl = pg_tbl;
+ return 0;
+}
+
+static void sbp_calc_data_length_direction(struct sbp_target_request *req,
+ u32 *data_len, enum dma_data_direction *data_dir)
+{
+ int data_size, direction, idx;
+
+ data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+ direction = CMDBLK_ORB_DIRECTION(be32_to_cpu(req->orb.misc));
+
+ if (!data_size) {
+ *data_len = 0;
+ *data_dir = DMA_NONE;
+ return;
+ }
+
+ *data_dir = direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (req->pg_tbl) {
+ *data_len = 0;
+ for (idx = 0; idx < data_size; idx++) {
+ *data_len += be16_to_cpu(
+ req->pg_tbl[idx].segment_length);
+ }
+ } else {
+ *data_len = data_size;
+ }
+}
+
+static void sbp_handle_command(struct sbp_target_request *req)
+{
+ struct sbp_login_descriptor *login = req->login;
+ struct sbp_session *sess = login->sess;
+ int ret, unpacked_lun;
+ u32 data_length;
+ enum dma_data_direction data_dir;
+
+ ret = sbp_fetch_command(req);
+ if (ret) {
+ pr_debug("sbp_handle_command: fetch command failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = sbp_fetch_page_table(req);
+ if (ret) {
+ pr_debug("sbp_handle_command: fetch page table failed: %d\n",
+ ret);
+ goto err;
+ }
+
+ unpacked_lun = req->login->lun->unpacked_lun;
+ sbp_calc_data_length_direction(req, &data_length, &data_dir);
+
+ pr_debug("sbp_handle_command ORB:0x%llx unpacked_lun:%d data_len:%d data_dir:%d\n",
+ req->orb_pointer, unpacked_lun, data_length, data_dir);
+
+ if (target_submit_cmd(&req->se_cmd, sess->se_sess, req->cmd_buf,
+ req->sense_buf, unpacked_lun, data_length,
+ MSG_SIMPLE_TAG, data_dir, 0))
+ goto err;
+
+ return;
+
+err:
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ sbp_free_request(req);
+}
+
+/*
+ * DMA_TO_DEVICE = read from initiator (SCSI WRITE)
+ * DMA_FROM_DEVICE = write to initiator (SCSI READ)
+ */
+static int sbp_rw_data(struct sbp_target_request *req)
+{
+ struct sbp_session *sess = req->login->sess;
+ int tcode, sg_miter_flags, max_payload, pg_size, speed, node_id,
+ generation, num_pte, length, tfr_length,
+ rcode = RCODE_COMPLETE;
+ struct sbp_page_table_entry *pte;
+ unsigned long long offset;
+ struct fw_card *card;
+ struct sg_mapping_iter iter;
+
+ if (req->se_cmd.data_direction == DMA_FROM_DEVICE) {
+ tcode = TCODE_WRITE_BLOCK_REQUEST;
+ sg_miter_flags = SG_MITER_FROM_SG;
+ } else {
+ tcode = TCODE_READ_BLOCK_REQUEST;
+ sg_miter_flags = SG_MITER_TO_SG;
+ }
+
+ max_payload = 4 << CMDBLK_ORB_MAX_PAYLOAD(be32_to_cpu(req->orb.misc));
+ speed = CMDBLK_ORB_SPEED(be32_to_cpu(req->orb.misc));
+
+ pg_size = CMDBLK_ORB_PG_SIZE(be32_to_cpu(req->orb.misc));
+ if (pg_size) {
+ pr_err("sbp_run_transaction: page size ignored\n");
+ pg_size = 0x100 << pg_size;
+ }
+
+ spin_lock_bh(&sess->lock);
+ card = fw_card_get(sess->card);
+ node_id = sess->node_id;
+ generation = sess->generation;
+ spin_unlock_bh(&sess->lock);
+
+ if (req->pg_tbl) {
+ pte = req->pg_tbl;
+ num_pte = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
+
+ offset = 0;
+ length = 0;
+ } else {
+ pte = NULL;
+ num_pte = 0;
+
+ offset = sbp2_pointer_to_addr(&req->orb.data_descriptor);
+ length = req->se_cmd.data_length;
+ }
+
+ sg_miter_start(&iter, req->se_cmd.t_data_sg, req->se_cmd.t_data_nents,
+ sg_miter_flags);
+
+ while (length || num_pte) {
+ if (!length) {
+ offset = (u64)be16_to_cpu(pte->segment_base_hi) << 32 |
+ be32_to_cpu(pte->segment_base_lo);
+ length = be16_to_cpu(pte->segment_length);
+
+ pte++;
+ num_pte--;
+ }
+
+ sg_miter_next(&iter);
+
+ tfr_length = min3(length, max_payload, (int)iter.length);
+
+ /* FIXME: take page_size into account */
+
+ rcode = sbp_run_transaction(card, tcode, node_id,
+ generation, speed,
+ offset, iter.addr, tfr_length);
+
+ if (rcode != RCODE_COMPLETE)
+ break;
+
+ length -= tfr_length;
+ offset += tfr_length;
+ iter.consumed = tfr_length;
+ }
+
+ sg_miter_stop(&iter);
+ fw_card_put(card);
+
+ if (rcode == RCODE_COMPLETE) {
+ WARN_ON(length != 0);
+ return 0;
+ } else {
+ return -EIO;
+ }
+}
+
+static int sbp_send_status(struct sbp_target_request *req)
+{
+ int ret, length;
+ struct sbp_login_descriptor *login = req->login;
+
+ length = (((be32_to_cpu(req->status.status) >> 24) & 0x07) + 1) * 4;
+
+ ret = sbp_run_request_transaction(req, TCODE_WRITE_BLOCK_REQUEST,
+ login->status_fifo_addr, &req->status, length);
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("sbp_send_status: write failed: 0x%x\n", ret);
+ return -EIO;
+ }
+
+ pr_debug("sbp_send_status: status write complete for ORB: 0x%llx\n",
+ req->orb_pointer);
+
+ return 0;
+}
+
+static void sbp_sense_mangle(struct sbp_target_request *req)
+{
+ struct se_cmd *se_cmd = &req->se_cmd;
+ u8 *sense = req->sense_buf;
+ u8 *status = req->status.data;
+
+ WARN_ON(se_cmd->scsi_sense_length < 18);
+
+ switch (sense[0] & 0x7f) { /* sfmt */
+ case 0x70: /* current, fixed */
+ status[0] = 0 << 6;
+ break;
+ case 0x71: /* deferred, fixed */
+ status[0] = 1 << 6;
+ break;
+ case 0x72: /* current, descriptor */
+ case 0x73: /* deferred, descriptor */
+ default:
+ /*
+ * TODO: SBP-3 specifies what we should do with descriptor
+ * format sense data
+ */
+ pr_err("sbp_send_sense: unknown sense format: 0x%x\n",
+ sense[0]);
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQUEST_ABORTED));
+ return;
+ }
+
+ status[0] |= se_cmd->scsi_status & 0x3f;/* status */
+ status[1] =
+ (sense[0] & 0x80) | /* valid */
+ ((sense[2] & 0xe0) >> 1) | /* mark, eom, ili */
+ (sense[2] & 0x0f); /* sense_key */
+ status[2] = se_cmd->scsi_asc; /* sense_code */
+ status[3] = se_cmd->scsi_ascq; /* sense_qualifier */
+
+ /* information */
+ status[4] = sense[3];
+ status[5] = sense[4];
+ status[6] = sense[5];
+ status[7] = sense[6];
+
+ /* CDB-dependent */
+ status[8] = sense[8];
+ status[9] = sense[9];
+ status[10] = sense[10];
+ status[11] = sense[11];
+
+ /* fru */
+ status[12] = sense[14];
+
+ /* sense_key-dependent */
+ status[13] = sense[15];
+ status[14] = sense[16];
+ status[15] = sense[17];
+
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(5) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+}
+
+static int sbp_send_sense(struct sbp_target_request *req)
+{
+ struct se_cmd *se_cmd = &req->se_cmd;
+
+ if (se_cmd->scsi_sense_length) {
+ sbp_sense_mangle(req);
+ } else {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
+ }
+
+ return sbp_send_status(req);
+}
+
+static void sbp_free_request(struct sbp_target_request *req)
+{
+ kfree(req->pg_tbl);
+ kfree(req->cmd_buf);
+ kfree(req);
+}
+
+static void sbp_mgt_agent_process(struct work_struct *work)
+{
+ struct sbp_management_agent *agent =
+ container_of(work, struct sbp_management_agent, work);
+ struct sbp_management_request *req = agent->request;
+ int ret;
+ int status_data_len = 0;
+
+ /* fetch the ORB from the initiator */
+ ret = sbp_run_transaction(req->card, TCODE_READ_BLOCK_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ agent->orb_offset, &req->orb, sizeof(req->orb));
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("mgt_orb fetch failed: %x\n", ret);
+ goto out;
+ }
+
+ pr_debug("mgt_orb ptr1:0x%llx ptr2:0x%llx misc:0x%x len:0x%x status_fifo:0x%llx\n",
+ sbp2_pointer_to_addr(&req->orb.ptr1),
+ sbp2_pointer_to_addr(&req->orb.ptr2),
+ be32_to_cpu(req->orb.misc), be32_to_cpu(req->orb.length),
+ sbp2_pointer_to_addr(&req->orb.status_fifo));
+
+ if (!ORB_NOTIFY(be32_to_cpu(req->orb.misc)) ||
+ ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc)) != 0) {
+ pr_err("mgt_orb bad request\n");
+ goto out;
+ }
+
+ switch (MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc))) {
+ case MANAGEMENT_ORB_FUNCTION_LOGIN:
+ sbp_management_request_login(agent, req, &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS:
+ sbp_management_request_query_logins(agent, req,
+ &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_RECONNECT:
+ sbp_management_request_reconnect(agent, req, &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_SET_PASSWORD:
+ pr_notice("SET PASSWORD not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_LOGOUT:
+ sbp_management_request_logout(agent, req, &status_data_len);
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_ABORT_TASK:
+ pr_notice("ABORT TASK not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET:
+ pr_notice("ABORT TASK SET not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET:
+ pr_notice("LOGICAL UNIT RESET not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ case MANAGEMENT_ORB_FUNCTION_TARGET_RESET:
+ pr_notice("TARGET RESET not implemented\n");
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+
+ default:
+ pr_notice("unknown management function 0x%x\n",
+ MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc)));
+
+ req->status.status = cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
+
+ break;
+ }
+
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_SRC(1) | /* Response to ORB, next_ORB absent */
+ STATUS_BLOCK_LEN(DIV_ROUND_UP(status_data_len, 4) + 1) |
+ STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_offset >> 32));
+ req->status.orb_low = cpu_to_be32(agent->orb_offset);
+
+ /* write the status block back to the initiator */
+ ret = sbp_run_transaction(req->card, TCODE_WRITE_BLOCK_REQUEST,
+ req->node_addr, req->generation, req->speed,
+ sbp2_pointer_to_addr(&req->orb.status_fifo),
+ &req->status, 8 + status_data_len);
+ if (ret != RCODE_COMPLETE) {
+ pr_debug("mgt_orb status write failed: %x\n", ret);
+ goto out;
+ }
+
+out:
+ fw_card_put(req->card);
+ kfree(req);
+
+ spin_lock_bh(&agent->lock);
+ agent->state = MANAGEMENT_AGENT_STATE_IDLE;
+ spin_unlock_bh(&agent->lock);
+}
+
+static void sbp_mgt_agent_rw(struct fw_card *card,
+ struct fw_request *request, int tcode, int destination, int source,
+ int generation, unsigned long long offset, void *data, size_t length,
+ void *callback_data)
+{
+ struct sbp_management_agent *agent = callback_data;
+ struct sbp2_pointer *ptr = data;
+ int rcode = RCODE_ADDRESS_ERROR;
+
+ if (!agent->tport->enable)
+ goto out;
+
+ if ((offset != agent->handler.offset) || (length != 8))
+ goto out;
+
+ if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
+ struct sbp_management_request *req;
+ int prev_state;
+
+ spin_lock_bh(&agent->lock);
+ prev_state = agent->state;
+ agent->state = MANAGEMENT_AGENT_STATE_BUSY;
+ spin_unlock_bh(&agent->lock);
+
+ if (prev_state == MANAGEMENT_AGENT_STATE_BUSY) {
+ pr_notice("ignoring management request while busy\n");
+ rcode = RCODE_CONFLICT_ERROR;
+ goto out;
+ }
+
+ req = kzalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ rcode = RCODE_CONFLICT_ERROR;
+ goto out;
+ }
+
+ req->card = fw_card_get(card);
+ req->generation = generation;
+ req->node_addr = source;
+ req->speed = fw_get_request_speed(request);
+
+ agent->orb_offset = sbp2_pointer_to_addr(ptr);
+ agent->request = req;
+
+ queue_work(system_unbound_wq, &agent->work);
+ rcode = RCODE_COMPLETE;
+ } else if (tcode == TCODE_READ_BLOCK_REQUEST) {
+ addr_to_sbp2_pointer(agent->orb_offset, ptr);
+ rcode = RCODE_COMPLETE;
+ } else {
+ rcode = RCODE_TYPE_ERROR;
+ }
+
+out:
+ fw_send_response(card, request, rcode);
+}
+
+static struct sbp_management_agent *sbp_management_agent_register(
+ struct sbp_tport *tport)
+{
+ int ret;
+ struct sbp_management_agent *agent;
+
+ agent = kmalloc(sizeof(*agent), GFP_KERNEL);
+ if (!agent)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock_init(&agent->lock);
+ agent->tport = tport;
+ agent->handler.length = 0x08;
+ agent->handler.address_callback = sbp_mgt_agent_rw;
+ agent->handler.callback_data = agent;
+ agent->state = MANAGEMENT_AGENT_STATE_IDLE;
+ INIT_WORK(&agent->work, sbp_mgt_agent_process);
+ agent->orb_offset = 0;
+ agent->request = NULL;
+
+ ret = fw_core_add_address_handler(&agent->handler,
+ &sbp_register_region);
+ if (ret < 0) {
+ kfree(agent);
+ return ERR_PTR(ret);
+ }
+
+ return agent;
+}
+
+static void sbp_management_agent_unregister(struct sbp_management_agent *agent)
+{
+ fw_core_remove_address_handler(&agent->handler);
+ cancel_work_sync(&agent->work);
+ kfree(agent);
+}
+
+static int sbp_check_true(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int sbp_check_false(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+static char *sbp_get_fabric_name(void)
+{
+ return "sbp";
+}
+
+static char *sbp_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+
+ return &tport->tport_name[0];
+}
+
+static u16 sbp_get_tag(struct se_portal_group *se_tpg)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ return tpg->tport_tpgt;
+}
+
+static u32 sbp_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static struct se_node_acl *sbp_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+ struct sbp_nacl *nacl;
+
+ nacl = kzalloc(sizeof(struct sbp_nacl), GFP_KERNEL);
+ if (!nacl) {
+ pr_err("Unable to allocate struct sbp_nacl\n");
+ return NULL;
+ }
+
+ return &nacl->se_node_acl;
+}
+
+static void sbp_release_fabric_acl(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl)
+{
+ struct sbp_nacl *nacl =
+ container_of(se_nacl, struct sbp_nacl, se_node_acl);
+ kfree(nacl);
+}
+
+static u32 sbp_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static void sbp_release_cmd(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ sbp_free_request(req);
+}
+
+static int sbp_shutdown_session(struct se_session *se_sess)
+{
+ return 0;
+}
+
+static void sbp_close_session(struct se_session *se_sess)
+{
+ return;
+}
+
+static u32 sbp_sess_get_index(struct se_session *se_sess)
+{
+ return 0;
+}
+
+static int sbp_write_pending(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+ int ret;
+
+ ret = sbp_rw_data(req);
+ if (ret) {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(
+ STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(
+ SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ return ret;
+ }
+
+ target_execute_cmd(se_cmd);
+ return 0;
+}
+
+static int sbp_write_pending_status(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static void sbp_set_default_node_attrs(struct se_node_acl *nacl)
+{
+ return;
+}
+
+static u32 sbp_get_task_tag(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ /* only used for printk until we do TMRs */
+ return (u32)req->orb_pointer;
+}
+
+static int sbp_get_cmd_state(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static int sbp_queue_data_in(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+ int ret;
+
+ ret = sbp_rw_data(req);
+ if (ret) {
+ req->status.status |= cpu_to_be32(
+ STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
+ STATUS_BLOCK_DEAD(0) |
+ STATUS_BLOCK_LEN(1) |
+ STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
+ sbp_send_status(req);
+ return ret;
+ }
+
+ return sbp_send_sense(req);
+}
+
+/*
+ * Called after command (no data transfer) or after the write (to device)
+ * operation is completed
+ */
+static int sbp_queue_status(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ return sbp_send_sense(req);
+}
+
+static void sbp_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+}
+
+static void sbp_aborted_task(struct se_cmd *se_cmd)
+{
+ return;
+}
+
+static int sbp_check_stop_free(struct se_cmd *se_cmd)
+{
+ struct sbp_target_request *req = container_of(se_cmd,
+ struct sbp_target_request, se_cmd);
+
+ transport_generic_free_cmd(&req->se_cmd, 0);
+ return 1;
+}
+
+/*
+ * Handlers for Serial Bus Protocol 2/3 (SBP-2 / SBP-3)
+ */
+static u8 sbp_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+ /*
+ * Return a IEEE 1394 SCSI Protocol identifier for loopback operations
+ * This is defined in section 7.5.1 Table 362 in spc4r17
+ */
+ return SCSI_PROTOCOL_SBP;
+}
+
+static u32 sbp_get_pr_transport_id(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code,
+ unsigned char *buf)
+{
+ int ret;
+
+ /*
+ * Set PROTOCOL IDENTIFIER to 3h for SBP
+ */
+ buf[0] = SCSI_PROTOCOL_SBP;
+ /*
+ * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+ * over IEEE 1394
+ */
+ ret = hex2bin(&buf[8], se_nacl->initiatorname, 8);
+ if (ret < 0)
+ pr_debug("sbp transport_id: invalid hex string\n");
+
+ /*
+ * The IEEE 1394 Transport ID is a hardcoded 24-byte length
+ */
+ return 24;
+}
+
+static u32 sbp_get_pr_transport_id_len(
+ struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code)
+{
+ *format_code = 0;
+ /*
+ * From spc4r17, 7.5.4.4 TransportID for initiator ports using SCSI
+ * over IEEE 1394
+ *
+ * The SBP Transport ID is a hardcoded 24-byte length
+ */
+ return 24;
+}
+
+/*
+ * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above
+ * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations.
+ */
+static char *sbp_parse_pr_out_transport_id(
+ struct se_portal_group *se_tpg,
+ const char *buf,
+ u32 *out_tid_len,
+ char **port_nexus_ptr)
+{
+ /*
+ * Assume the FORMAT CODE 00b from spc4r17, 7.5.4.4 TransportID
+ * for initiator ports using SCSI over SBP Serial SCSI Protocol
+ *
+ * The TransportID for a IEEE 1394 Initiator Port is of fixed size of
+ * 24 bytes, and IEEE 1394 does not contain a I_T nexus identifier,
+ * so we return the **port_nexus_ptr set to NULL.
+ */
+ *port_nexus_ptr = NULL;
+ *out_tid_len = 24;
+
+ return (char *)&buf[8];
+}
+
+static int sbp_count_se_tpg_luns(struct se_portal_group *tpg)
+{
+ int i, count = 0;
+
+ spin_lock(&tpg->tpg_lun_lock);
+ for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+ struct se_lun *se_lun = tpg->tpg_lun_list[i];
+
+ if (se_lun->lun_status == TRANSPORT_LUN_STATUS_FREE)
+ continue;
+
+ count++;
+ }
+ spin_unlock(&tpg->tpg_lun_lock);
+
+ return count;
+}
+
+static int sbp_update_unit_directory(struct sbp_tport *tport)
+{
+ int num_luns, num_entries, idx = 0, mgt_agt_addr, ret, i;
+ u32 *data;
+
+ if (tport->unit_directory.data) {
+ fw_core_remove_descriptor(&tport->unit_directory);
+ kfree(tport->unit_directory.data);
+ tport->unit_directory.data = NULL;
+ }
+
+ if (!tport->enable || !tport->tpg)
+ return 0;
+
+ num_luns = sbp_count_se_tpg_luns(&tport->tpg->se_tpg);
+
+ /*
+ * Number of entries in the final unit directory:
+ * - all of those in the template
+ * - management_agent
+ * - unit_characteristics
+ * - reconnect_timeout
+ * - unit unique ID
+ * - one for each LUN
+ *
+ * MUST NOT include leaf or sub-directory entries
+ */
+ num_entries = ARRAY_SIZE(sbp_unit_directory_template) + 4 + num_luns;
+
+ if (tport->directory_id != -1)
+ num_entries++;
+
+ /* allocate num_entries + 4 for the header and unique ID leaf */
+ data = kcalloc((num_entries + 4), sizeof(u32), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* directory_length */
+ data[idx++] = num_entries << 16;
+
+ /* directory_id */
+ if (tport->directory_id != -1)
+ data[idx++] = (CSR_DIRECTORY_ID << 24) | tport->directory_id;
+
+ /* unit directory template */
+ memcpy(&data[idx], sbp_unit_directory_template,
+ sizeof(sbp_unit_directory_template));
+ idx += ARRAY_SIZE(sbp_unit_directory_template);
+
+ /* management_agent */
+ mgt_agt_addr = (tport->mgt_agt->handler.offset - CSR_REGISTER_BASE) / 4;
+ data[idx++] = 0x54000000 | (mgt_agt_addr & 0x00ffffff);
+
+ /* unit_characteristics */
+ data[idx++] = 0x3a000000 |
+ (((tport->mgt_orb_timeout * 2) << 8) & 0xff00) |
+ SBP_ORB_FETCH_SIZE;
+
+ /* reconnect_timeout */
+ data[idx++] = 0x3d000000 | (tport->max_reconnect_timeout & 0xffff);
+
+ /* unit unique ID (leaf is just after LUNs) */
+ data[idx++] = 0x8d000000 | (num_luns + 1);
+
+ spin_lock(&tport->tpg->se_tpg.tpg_lun_lock);
+ for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+ struct se_lun *se_lun = tport->tpg->se_tpg.tpg_lun_list[i];
+ struct se_device *dev;
+ int type;
+
+ if (se_lun->lun_status == TRANSPORT_LUN_STATUS_FREE)
+ continue;
+
+ spin_unlock(&tport->tpg->se_tpg.tpg_lun_lock);
+
+ dev = se_lun->lun_se_dev;
+ type = dev->transport->get_device_type(dev);
+
+ /* logical_unit_number */
+ data[idx++] = 0x14000000 |
+ ((type << 16) & 0x1f0000) |
+ (se_lun->unpacked_lun & 0xffff);
+
+ spin_lock(&tport->tpg->se_tpg.tpg_lun_lock);
+ }
+ spin_unlock(&tport->tpg->se_tpg.tpg_lun_lock);
+
+ /* unit unique ID leaf */
+ data[idx++] = 2 << 16;
+ data[idx++] = tport->guid >> 32;
+ data[idx++] = tport->guid;
+
+ tport->unit_directory.length = idx;
+ tport->unit_directory.key = (CSR_DIRECTORY | CSR_UNIT) << 24;
+ tport->unit_directory.data = data;
+
+ ret = fw_core_add_descriptor(&tport->unit_directory);
+ if (ret < 0) {
+ kfree(tport->unit_directory.data);
+ tport->unit_directory.data = NULL;
+ }
+
+ return ret;
+}
+
+static ssize_t sbp_parse_wwn(const char *name, u64 *wwn)
+{
+ const char *cp;
+ char c, nibble;
+ int pos = 0, err;
+
+ *wwn = 0;
+ for (cp = name; cp < &name[SBP_NAMELEN - 1]; cp++) {
+ c = *cp;
+ if (c == '\n' && cp[1] == '\0')
+ continue;
+ if (c == '\0') {
+ err = 2;
+ if (pos != 16)
+ goto fail;
+ return cp - name;
+ }
+ err = 3;
+ if (isdigit(c))
+ nibble = c - '0';
+ else if (isxdigit(c))
+ nibble = tolower(c) - 'a' + 10;
+ else
+ goto fail;
+ *wwn = (*wwn << 4) | nibble;
+ pos++;
+ }
+ err = 4;
+fail:
+ printk(KERN_INFO "err %u len %zu pos %u\n",
+ err, cp - name, pos);
+ return -1;
+}
+
+static ssize_t sbp_format_wwn(char *buf, size_t len, u64 wwn)
+{
+ return snprintf(buf, len, "%016llx", wwn);
+}
+
+static struct se_node_acl *sbp_make_nodeacl(
+ struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
+{
+ struct se_node_acl *se_nacl, *se_nacl_new;
+ struct sbp_nacl *nacl;
+ u64 guid = 0;
+ u32 nexus_depth = 1;
+
+ if (sbp_parse_wwn(name, &guid) < 0)
+ return ERR_PTR(-EINVAL);
+
+ se_nacl_new = sbp_alloc_fabric_acl(se_tpg);
+ if (!se_nacl_new)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+ * when converting a NodeACL from demo mode -> explict
+ */
+ se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+ name, nexus_depth);
+ if (IS_ERR(se_nacl)) {
+ sbp_release_fabric_acl(se_tpg, se_nacl_new);
+ return se_nacl;
+ }
+
+ nacl = container_of(se_nacl, struct sbp_nacl, se_node_acl);
+ nacl->guid = guid;
+ sbp_format_wwn(nacl->iport_name, SBP_NAMELEN, guid);
+
+ return se_nacl;
+}
+
+static void sbp_drop_nodeacl(struct se_node_acl *se_acl)
+{
+ struct sbp_nacl *nacl =
+ container_of(se_acl, struct sbp_nacl, se_node_acl);
+
+ core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+ kfree(nacl);
+}
+
+static int sbp_post_link_lun(
+ struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+
+ return sbp_update_unit_directory(tpg->tport);
+}
+
+static void sbp_pre_unlink_lun(
+ struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ int ret;
+
+ if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0)
+ tport->enable = 0;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0)
+ pr_err("unlink LUN: failed to update unit directory\n");
+}
+
+static struct se_portal_group *sbp_make_tpg(
+ struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct sbp_tport *tport =
+ container_of(wwn, struct sbp_tport, tport_wwn);
+
+ struct sbp_tpg *tpg;
+ unsigned long tpgt;
+ int ret;
+
+ if (strstr(name, "tpgt_") != name)
+ return ERR_PTR(-EINVAL);
+ if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
+ return ERR_PTR(-EINVAL);
+
+ if (tport->tpg) {
+ pr_err("Only one TPG per Unit is possible.\n");
+ return ERR_PTR(-EBUSY);
+ }
+
+ tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
+ if (!tpg) {
+ pr_err("Unable to allocate struct sbp_tpg\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ tpg->tport = tport;
+ tpg->tport_tpgt = tpgt;
+ tport->tpg = tpg;
+
+ /* default attribute values */
+ tport->enable = 0;
+ tport->directory_id = -1;
+ tport->mgt_orb_timeout = 15;
+ tport->max_reconnect_timeout = 5;
+ tport->max_logins_per_lun = 1;
+
+ tport->mgt_agt = sbp_management_agent_register(tport);
+ if (IS_ERR(tport->mgt_agt)) {
+ ret = PTR_ERR(tport->mgt_agt);
+ goto out_free_tpg;
+ }
+
+ ret = core_tpg_register(&sbp_fabric_configfs->tf_ops, wwn,
+ &tpg->se_tpg, (void *)tpg,
+ TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0)
+ goto out_unreg_mgt_agt;
+
+ return &tpg->se_tpg;
+
+out_unreg_mgt_agt:
+ sbp_management_agent_unregister(tport->mgt_agt);
+out_free_tpg:
+ tport->tpg = NULL;
+ kfree(tpg);
+ return ERR_PTR(ret);
+}
+
+static void sbp_drop_tpg(struct se_portal_group *se_tpg)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+
+ core_tpg_deregister(se_tpg);
+ sbp_management_agent_unregister(tport->mgt_agt);
+ tport->tpg = NULL;
+ kfree(tpg);
+}
+
+static struct se_wwn *sbp_make_tport(
+ struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct sbp_tport *tport;
+ u64 guid = 0;
+
+ if (sbp_parse_wwn(name, &guid) < 0)
+ return ERR_PTR(-EINVAL);
+
+ tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+ if (!tport) {
+ pr_err("Unable to allocate struct sbp_tport\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ tport->guid = guid;
+ sbp_format_wwn(tport->tport_name, SBP_NAMELEN, guid);
+
+ return &tport->tport_wwn;
+}
+
+static void sbp_drop_tport(struct se_wwn *wwn)
+{
+ struct sbp_tport *tport =
+ container_of(wwn, struct sbp_tport, tport_wwn);
+
+ kfree(tport);
+}
+
+static ssize_t sbp_wwn_show_attr_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "FireWire SBP fabric module %s\n", SBP_VERSION);
+}
+
+TF_WWN_ATTR_RO(sbp, version);
+
+static struct configfs_attribute *sbp_wwn_attrs[] = {
+ &sbp_wwn_version.attr,
+ NULL,
+};
+
+static ssize_t sbp_tpg_show_directory_id(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+
+ if (tport->directory_id == -1)
+ return sprintf(page, "implicit\n");
+ else
+ return sprintf(page, "%06x\n", tport->directory_id);
+}
+
+static ssize_t sbp_tpg_store_directory_id(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+
+ if (tport->enable) {
+ pr_err("Cannot change the directory_id on an active target.\n");
+ return -EBUSY;
+ }
+
+ if (strstr(page, "implicit") == page) {
+ tport->directory_id = -1;
+ } else {
+ if (kstrtoul(page, 16, &val) < 0)
+ return -EINVAL;
+ if (val > 0xffffff)
+ return -EINVAL;
+
+ tport->directory_id = val;
+ }
+
+ return count;
+}
+
+static ssize_t sbp_tpg_show_enable(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->enable);
+}
+
+static ssize_t sbp_tpg_store_enable(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val != 0) && (val != 1))
+ return -EINVAL;
+
+ if (tport->enable == val)
+ return count;
+
+ if (val) {
+ if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0) {
+ pr_err("Cannot enable a target with no LUNs!\n");
+ return -EINVAL;
+ }
+ } else {
+ /* XXX: force-shutdown sessions instead? */
+ spin_lock_bh(&se_tpg->session_lock);
+ if (!list_empty(&se_tpg->tpg_sess_list)) {
+ spin_unlock_bh(&se_tpg->session_lock);
+ return -EBUSY;
+ }
+ spin_unlock_bh(&se_tpg->session_lock);
+ }
+
+ tport->enable = val;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0) {
+ pr_err("Could not update Config ROM\n");
+ return ret;
+ }
+
+ return count;
+}
+
+TF_TPG_BASE_ATTR(sbp, directory_id, S_IRUGO | S_IWUSR);
+TF_TPG_BASE_ATTR(sbp, enable, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_base_attrs[] = {
+ &sbp_tpg_directory_id.attr,
+ &sbp_tpg_enable.attr,
+ NULL,
+};
+
+static ssize_t sbp_tpg_attrib_show_mgt_orb_timeout(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->mgt_orb_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_mgt_orb_timeout(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val < 1) || (val > 127))
+ return -EINVAL;
+
+ if (tport->mgt_orb_timeout == val)
+ return count;
+
+ tport->mgt_orb_timeout = val;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_reconnect_timeout(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->max_reconnect_timeout);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_reconnect_timeout(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val < 1) || (val > 32767))
+ return -EINVAL;
+
+ if (tport->max_reconnect_timeout == val)
+ return count;
+
+ tport->max_reconnect_timeout = val;
+
+ ret = sbp_update_unit_directory(tport);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t sbp_tpg_attrib_show_max_logins_per_lun(
+ struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ return sprintf(page, "%d\n", tport->max_logins_per_lun);
+}
+
+static ssize_t sbp_tpg_attrib_store_max_logins_per_lun(
+ struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
+ struct sbp_tport *tport = tpg->tport;
+ unsigned long val;
+
+ if (kstrtoul(page, 0, &val) < 0)
+ return -EINVAL;
+ if ((val < 1) || (val > 127))
+ return -EINVAL;
+
+ /* XXX: also check against current count? */
+
+ tport->max_logins_per_lun = val;
+
+ return count;
+}
+
+TF_TPG_ATTRIB_ATTR(sbp, mgt_orb_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_reconnect_timeout, S_IRUGO | S_IWUSR);
+TF_TPG_ATTRIB_ATTR(sbp, max_logins_per_lun, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *sbp_tpg_attrib_attrs[] = {
+ &sbp_tpg_attrib_mgt_orb_timeout.attr,
+ &sbp_tpg_attrib_max_reconnect_timeout.attr,
+ &sbp_tpg_attrib_max_logins_per_lun.attr,
+ NULL,
+};
+
+static struct target_core_fabric_ops sbp_ops = {
+ .get_fabric_name = sbp_get_fabric_name,
+ .get_fabric_proto_ident = sbp_get_fabric_proto_ident,
+ .tpg_get_wwn = sbp_get_fabric_wwn,
+ .tpg_get_tag = sbp_get_tag,
+ .tpg_get_default_depth = sbp_get_default_depth,
+ .tpg_get_pr_transport_id = sbp_get_pr_transport_id,
+ .tpg_get_pr_transport_id_len = sbp_get_pr_transport_id_len,
+ .tpg_parse_pr_out_transport_id = sbp_parse_pr_out_transport_id,
+ .tpg_check_demo_mode = sbp_check_true,
+ .tpg_check_demo_mode_cache = sbp_check_true,
+ .tpg_check_demo_mode_write_protect = sbp_check_false,
+ .tpg_check_prod_mode_write_protect = sbp_check_false,
+ .tpg_alloc_fabric_acl = sbp_alloc_fabric_acl,
+ .tpg_release_fabric_acl = sbp_release_fabric_acl,
+ .tpg_get_inst_index = sbp_tpg_get_inst_index,
+ .release_cmd = sbp_release_cmd,
+ .shutdown_session = sbp_shutdown_session,
+ .close_session = sbp_close_session,
+ .sess_get_index = sbp_sess_get_index,
+ .write_pending = sbp_write_pending,
+ .write_pending_status = sbp_write_pending_status,
+ .set_default_node_attributes = sbp_set_default_node_attrs,
+ .get_task_tag = sbp_get_task_tag,
+ .get_cmd_state = sbp_get_cmd_state,
+ .queue_data_in = sbp_queue_data_in,
+ .queue_status = sbp_queue_status,
+ .queue_tm_rsp = sbp_queue_tm_rsp,
+ .aborted_task = sbp_aborted_task,
+ .check_stop_free = sbp_check_stop_free,
+
+ .fabric_make_wwn = sbp_make_tport,
+ .fabric_drop_wwn = sbp_drop_tport,
+ .fabric_make_tpg = sbp_make_tpg,
+ .fabric_drop_tpg = sbp_drop_tpg,
+ .fabric_post_link = sbp_post_link_lun,
+ .fabric_pre_unlink = sbp_pre_unlink_lun,
+ .fabric_make_np = NULL,
+ .fabric_drop_np = NULL,
+ .fabric_make_nodeacl = sbp_make_nodeacl,
+ .fabric_drop_nodeacl = sbp_drop_nodeacl,
+};
+
+static int sbp_register_configfs(void)
+{
+ struct target_fabric_configfs *fabric;
+ int ret;
+
+ fabric = target_fabric_configfs_init(THIS_MODULE, "sbp");
+ if (IS_ERR(fabric)) {
+ pr_err("target_fabric_configfs_init() failed\n");
+ return PTR_ERR(fabric);
+ }
+
+ fabric->tf_ops = sbp_ops;
+
+ /*
+ * Setup default attribute lists for various fabric->tf_cit_tmpl
+ */
+ fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = sbp_wwn_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = sbp_tpg_base_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = sbp_tpg_attrib_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+
+ ret = target_fabric_configfs_register(fabric);
+ if (ret < 0) {
+ pr_err("target_fabric_configfs_register() failed for SBP\n");
+ return ret;
+ }
+
+ sbp_fabric_configfs = fabric;
+
+ return 0;
+};
+
+static void sbp_deregister_configfs(void)
+{
+ if (!sbp_fabric_configfs)
+ return;
+
+ target_fabric_configfs_deregister(sbp_fabric_configfs);
+ sbp_fabric_configfs = NULL;
+};
+
+static int __init sbp_init(void)
+{
+ int ret;
+
+ ret = sbp_register_configfs();
+ if (ret < 0)
+ return ret;
+
+ return 0;
+};
+
+static void __exit sbp_exit(void)
+{
+ sbp_deregister_configfs();
+};
+
+MODULE_DESCRIPTION("FireWire SBP fabric driver");
+MODULE_LICENSE("GPL");
+module_init(sbp_init);
+module_exit(sbp_exit);
diff --git a/drivers/target/sbp/sbp_target.h b/drivers/target/sbp/sbp_target.h
new file mode 100644
index 00000000000..6d0d74a2c54
--- /dev/null
+++ b/drivers/target/sbp/sbp_target.h
@@ -0,0 +1,251 @@
+#ifndef _SBP_BASE_H
+#define _SBP_BASE_H
+
+#include <linux/firewire.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <target/target_core_base.h>
+
+#define SBP_VERSION "v0.1"
+#define SBP_NAMELEN 32
+
+#define SBP_ORB_FETCH_SIZE 8
+
+#define MANAGEMENT_AGENT_STATE_IDLE 0
+#define MANAGEMENT_AGENT_STATE_BUSY 1
+
+#define ORB_NOTIFY(v) (((v) >> 31) & 0x01)
+#define ORB_REQUEST_FORMAT(v) (((v) >> 29) & 0x03)
+
+#define MANAGEMENT_ORB_FUNCTION(v) (((v) >> 16) & 0x0f)
+
+#define MANAGEMENT_ORB_FUNCTION_LOGIN 0x0
+#define MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS 0x1
+#define MANAGEMENT_ORB_FUNCTION_RECONNECT 0x3
+#define MANAGEMENT_ORB_FUNCTION_SET_PASSWORD 0x4
+#define MANAGEMENT_ORB_FUNCTION_LOGOUT 0x7
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK 0xb
+#define MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET 0xc
+#define MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET 0xe
+#define MANAGEMENT_ORB_FUNCTION_TARGET_RESET 0xf
+
+#define LOGIN_ORB_EXCLUSIVE(v) (((v) >> 28) & 0x01)
+#define LOGIN_ORB_RESERVED(v) (((v) >> 24) & 0x0f)
+#define LOGIN_ORB_RECONNECT(v) (((v) >> 20) & 0x0f)
+#define LOGIN_ORB_LUN(v) (((v) >> 0) & 0xffff)
+#define LOGIN_ORB_PASSWORD_LENGTH(v) (((v) >> 16) & 0xffff)
+#define LOGIN_ORB_RESPONSE_LENGTH(v) (((v) >> 0) & 0xffff)
+
+#define RECONNECT_ORB_LOGIN_ID(v) (((v) >> 0) & 0xffff)
+#define LOGOUT_ORB_LOGIN_ID(v) (((v) >> 0) & 0xffff)
+
+#define CMDBLK_ORB_DIRECTION(v) (((v) >> 27) & 0x01)
+#define CMDBLK_ORB_SPEED(v) (((v) >> 24) & 0x07)
+#define CMDBLK_ORB_MAX_PAYLOAD(v) (((v) >> 20) & 0x0f)
+#define CMDBLK_ORB_PG_TBL_PRESENT(v) (((v) >> 19) & 0x01)
+#define CMDBLK_ORB_PG_SIZE(v) (((v) >> 16) & 0x07)
+#define CMDBLK_ORB_DATA_SIZE(v) (((v) >> 0) & 0xffff)
+
+#define STATUS_BLOCK_SRC(v) (((v) & 0x03) << 30)
+#define STATUS_BLOCK_RESP(v) (((v) & 0x03) << 28)
+#define STATUS_BLOCK_DEAD(v) (((v) ? 1 : 0) << 27)
+#define STATUS_BLOCK_LEN(v) (((v) & 0x07) << 24)
+#define STATUS_BLOCK_SBP_STATUS(v) (((v) & 0xff) << 16)
+#define STATUS_BLOCK_ORB_OFFSET_HIGH(v) (((v) & 0xffff) << 0)
+
+#define STATUS_SRC_ORB_CONTINUING 0
+#define STATUS_SRC_ORB_FINISHED 1
+#define STATUS_SRC_UNSOLICITED 2
+
+#define STATUS_RESP_REQUEST_COMPLETE 0
+#define STATUS_RESP_TRANSPORT_FAILURE 1
+#define STATUS_RESP_ILLEGAL_REQUEST 2
+#define STATUS_RESP_VENDOR_DEPENDENT 3
+
+#define SBP_STATUS_OK 0
+#define SBP_STATUS_REQ_TYPE_NOTSUPP 1
+#define SBP_STATUS_SPEED_NOTSUPP 2
+#define SBP_STATUS_PAGE_SIZE_NOTSUPP 3
+#define SBP_STATUS_ACCESS_DENIED 4
+#define SBP_STATUS_LUN_NOTSUPP 5
+#define SBP_STATUS_PAYLOAD_TOO_SMALL 6
+/* 7 is reserved */
+#define SBP_STATUS_RESOURCES_UNAVAIL 8
+#define SBP_STATUS_FUNCTION_REJECTED 9
+#define SBP_STATUS_LOGIN_ID_UNKNOWN 10
+#define SBP_STATUS_DUMMY_ORB_COMPLETE 11
+#define SBP_STATUS_REQUEST_ABORTED 12
+#define SBP_STATUS_UNSPECIFIED_ERROR 0xff
+
+#define AGENT_STATE_RESET 0
+#define AGENT_STATE_ACTIVE 1
+#define AGENT_STATE_SUSPENDED 2
+#define AGENT_STATE_DEAD 3
+
+struct sbp2_pointer {
+ __be32 high;
+ __be32 low;
+};
+
+struct sbp_command_block_orb {
+ struct sbp2_pointer next_orb;
+ struct sbp2_pointer data_descriptor;
+ __be32 misc;
+ u8 command_block[12];
+};
+
+struct sbp_page_table_entry {
+ __be16 segment_length;
+ __be16 segment_base_hi;
+ __be32 segment_base_lo;
+};
+
+struct sbp_management_orb {
+ struct sbp2_pointer ptr1;
+ struct sbp2_pointer ptr2;
+ __be32 misc;
+ __be32 length;
+ struct sbp2_pointer status_fifo;
+};
+
+struct sbp_status_block {
+ __be32 status;
+ __be32 orb_low;
+ u8 data[24];
+};
+
+struct sbp_login_response_block {
+ __be32 misc;
+ struct sbp2_pointer command_block_agent;
+ __be32 reconnect_hold;
+};
+
+struct sbp_login_descriptor {
+ struct sbp_session *sess;
+ struct list_head link;
+
+ struct se_lun *lun;
+
+ u64 status_fifo_addr;
+ int exclusive;
+ u16 login_id;
+
+ struct sbp_target_agent *tgt_agt;
+};
+
+struct sbp_session {
+ spinlock_t lock;
+ struct se_session *se_sess;
+ struct list_head login_list;
+ struct delayed_work maint_work;
+
+ u64 guid; /* login_owner_EUI_64 */
+ int node_id; /* login_owner_ID */
+
+ struct fw_card *card;
+ int generation;
+ int speed;
+
+ int reconnect_hold;
+ u64 reconnect_expires;
+};
+
+struct sbp_nacl {
+ /* Initiator EUI-64 */
+ u64 guid;
+ /* ASCII formatted GUID for SBP Initiator port */
+ char iport_name[SBP_NAMELEN];
+ /* Returned by sbp_make_nodeacl() */
+ struct se_node_acl se_node_acl;
+};
+
+struct sbp_tpg {
+ /* Target portal group tag for TCM */
+ u16 tport_tpgt;
+ /* Pointer back to sbp_tport */
+ struct sbp_tport *tport;
+ /* Returned by sbp_make_tpg() */
+ struct se_portal_group se_tpg;
+};
+
+struct sbp_tport {
+ /* Target Unit Identifier (EUI-64) */
+ u64 guid;
+ /* Target port name */
+ char tport_name[SBP_NAMELEN];
+ /* Returned by sbp_make_tport() */
+ struct se_wwn tport_wwn;
+
+ struct sbp_tpg *tpg;
+
+ /* FireWire unit directory */
+ struct fw_descriptor unit_directory;
+
+ /* SBP Management Agent */
+ struct sbp_management_agent *mgt_agt;
+
+ /* Parameters */
+ int enable;
+ s32 directory_id;
+ int mgt_orb_timeout;
+ int max_reconnect_timeout;
+ int max_logins_per_lun;
+};
+
+static inline u64 sbp2_pointer_to_addr(const struct sbp2_pointer *ptr)
+{
+ return (u64)(be32_to_cpu(ptr->high) & 0x0000ffff) << 32 |
+ (be32_to_cpu(ptr->low) & 0xfffffffc);
+}
+
+static inline void addr_to_sbp2_pointer(u64 addr, struct sbp2_pointer *ptr)
+{
+ ptr->high = cpu_to_be32(addr >> 32);
+ ptr->low = cpu_to_be32(addr);
+}
+
+struct sbp_target_agent {
+ spinlock_t lock;
+ struct fw_address_handler handler;
+ struct sbp_login_descriptor *login;
+ int state;
+ struct work_struct work;
+ u64 orb_pointer;
+ bool doorbell;
+};
+
+struct sbp_target_request {
+ struct sbp_login_descriptor *login;
+ u64 orb_pointer;
+ struct sbp_command_block_orb orb;
+ struct sbp_status_block status;
+ struct work_struct work;
+
+ struct se_cmd se_cmd;
+ struct sbp_page_table_entry *pg_tbl;
+ void *cmd_buf;
+
+ unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+struct sbp_management_agent {
+ spinlock_t lock;
+ struct sbp_tport *tport;
+ struct fw_address_handler handler;
+ int state;
+ struct work_struct work;
+ u64 orb_offset;
+ struct sbp_management_request *request;
+};
+
+struct sbp_management_request {
+ struct sbp_management_orb orb;
+ struct sbp_status_block status;
+ struct fw_card *card;
+ int generation;
+ int node_addr;
+ int speed;
+};
+
+#endif
diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c
index 2c5fcfed593..fbc5ebb5f76 100644
--- a/drivers/target/target_core_alua.c
+++ b/drivers/target/target_core_alua.c
@@ -3,8 +3,7 @@
*
* 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
+ * (c) Copyright 2009-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -24,47 +23,170 @@
*
******************************************************************************/
-#include <linux/version.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/configfs.h>
+#include <linux/export.h>
+#include <linux/file.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
+#include <asm/unaligned.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_backend.h>
+#include <target/target_core_fabric.h>
#include <target/target_core_configfs.h>
+#include "target_core_internal.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 sense_reason_t core_alua_check_transition(int state, int valid,
+ 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);
+ struct se_port *port, int explicit, int offline);
+
+static char *core_alua_dump_state(int state);
+
+static u16 alua_lu_gps_counter;
+static u32 alua_lu_gps_count;
+
+static DEFINE_SPINLOCK(lu_gps_lock);
+static LIST_HEAD(lu_gps_list);
+
+struct t10_alua_lu_gp *default_lu_gp;
+
+/*
+ * REPORT REFERRALS
+ *
+ * See sbc3r35 section 5.23
+ */
+sense_reason_t
+target_emulate_report_referrals(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct t10_alua_lba_map *map;
+ struct t10_alua_lba_map_member *map_mem;
+ unsigned char *buf;
+ u32 rd_len = 0, off;
+
+ if (cmd->data_length < 4) {
+ pr_warn("REPORT REFERRALS allocation length %u too"
+ " small\n", cmd->data_length);
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ off = 4;
+ spin_lock(&dev->t10_alua.lba_map_lock);
+ if (list_empty(&dev->t10_alua.lba_map_list)) {
+ spin_unlock(&dev->t10_alua.lba_map_lock);
+ transport_kunmap_data_sg(cmd);
+
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+ }
+
+ list_for_each_entry(map, &dev->t10_alua.lba_map_list,
+ lba_map_list) {
+ int desc_num = off + 3;
+ int pg_num;
+
+ off += 4;
+ if (cmd->data_length > off)
+ put_unaligned_be64(map->lba_map_first_lba, &buf[off]);
+ off += 8;
+ if (cmd->data_length > off)
+ put_unaligned_be64(map->lba_map_last_lba, &buf[off]);
+ off += 8;
+ rd_len += 20;
+ pg_num = 0;
+ list_for_each_entry(map_mem, &map->lba_map_mem_list,
+ lba_map_mem_list) {
+ int alua_state = map_mem->lba_map_mem_alua_state;
+ int alua_pg_id = map_mem->lba_map_mem_alua_pg_id;
+
+ if (cmd->data_length > off)
+ buf[off] = alua_state & 0x0f;
+ off += 2;
+ if (cmd->data_length > off)
+ buf[off] = (alua_pg_id >> 8) & 0xff;
+ off++;
+ if (cmd->data_length > off)
+ buf[off] = (alua_pg_id & 0xff);
+ off++;
+ rd_len += 4;
+ pg_num++;
+ }
+ if (cmd->data_length > desc_num)
+ buf[desc_num] = pg_num;
+ }
+ spin_unlock(&dev->t10_alua.lba_map_lock);
+
+ /*
+ * Set the RETURN DATA LENGTH set in the header of the DataIN Payload
+ */
+ put_unaligned_be16(rd_len, &buf[2]);
+
+ transport_kunmap_data_sg(cmd);
+
+ target_complete_cmd(cmd, GOOD);
+ return 0;
+}
/*
* REPORT_TARGET_PORT_GROUPS
*
* See spc4r17 section 6.27
*/
-int core_emulate_report_target_port_groups(struct se_cmd *cmd)
+sense_reason_t
+target_emulate_report_target_port_groups(struct se_cmd *cmd)
{
- struct se_subsystem_dev *su_dev = SE_DEV(cmd)->se_sub_dev;
+ struct se_device *dev = cmd->se_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 */
+ unsigned char *buf;
+ u32 rd_len = 0, off;
+ int ext_hdr = (cmd->t_task_cdb[1] & 0x20);
+
+ /*
+ * Skip over RESERVED area to first Target port group descriptor
+ * depending on the PARAMETER DATA FORMAT type..
+ */
+ if (ext_hdr != 0)
+ off = 8;
+ else
+ off = 4;
- 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,
+ if (cmd->data_length < off) {
+ pr_warn("REPORT TARGET PORT GROUPS allocation length %u too"
+ " small for %s header\n", cmd->data_length,
+ (ext_hdr) ? "extended" : "normal");
+ return TCM_INVALID_CDB_FIELD;
+ }
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ spin_lock(&dev->t10_alua.tg_pt_gps_lock);
+ list_for_each_entry(tg_pt_gp, &dev->t10_alua.tg_pt_gps_list,
tg_pt_gp_list) {
/*
+ * Check if the Target port group and Target port descriptor list
+ * based on tg_pt_gp_members count will fit into the response payload.
+ * Otherwise, bump rd_len to let the initiator know we have exceeded
+ * the allocation length and the response is truncated.
+ */
+ if ((off + 8 + (tg_pt_gp->tg_pt_gp_members * 4)) >
+ cmd->data_length) {
+ rd_len += 8 + (tg_pt_gp->tg_pt_gp_members * 4);
+ continue;
+ }
+ /*
* PREF: Preferred target port bit, determine if this
* bit should be set for port group.
*/
@@ -78,12 +200,7 @@ int core_emulate_report_target_port_groups(struct se_cmd *cmd)
/*
* 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 */
+ buf[off++] |= tg_pt_gp->tg_pt_gp_alua_supported_states;
/*
* TARGET PORT GROUP
*/
@@ -124,73 +241,115 @@ int core_emulate_report_target_port_groups(struct se_cmd *cmd)
}
spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
}
- spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+ spin_unlock(&dev->t10_alua.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);
+ put_unaligned_be32(rd_len, &buf[0]);
+
+ /*
+ * Fill in the Extended header parameter data format if requested
+ */
+ if (ext_hdr != 0) {
+ buf[4] = 0x10;
+ /*
+ * Set the implicit transition time (in seconds) for the application
+ * client to use as a base for it's transition timeout value.
+ *
+ * Use the current tg_pt_gp_mem -> tg_pt_gp membership from the LUN
+ * this CDB was received upon to determine this value individually
+ * for ALUA target port group.
+ */
+ port = cmd->se_lun->lun_sep;
+ tg_pt_gp_mem = port->sep_alua_tg_pt_gp_mem;
+ if (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;
+ if (tg_pt_gp)
+ buf[5] = tg_pt_gp->tg_pt_gp_implicit_trans_secs;
+ spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
+ }
+ }
+ transport_kunmap_data_sg(cmd);
+ target_complete_cmd(cmd, GOOD);
return 0;
}
/*
- * SET_TARGET_PORT_GROUPS for explict ALUA operation.
+ * SET_TARGET_PORT_GROUPS for explicit ALUA operation.
*
* See spc4r17 section 6.35
*/
-int core_emulate_set_target_port_groups(struct se_cmd *cmd)
+sense_reason_t
+target_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 se_device *dev = cmd->se_dev;
+ struct se_port *port, *l_port = cmd->se_lun->lun_sep;
+ struct se_node_acl *nacl = cmd->se_sess->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 */
+ unsigned char *buf;
+ unsigned char *ptr;
+ sense_reason_t rc = TCM_NO_SENSE;
u32 len = 4; /* Skip over RESERVED area in header */
- int alua_access_state, primary = 0, rc;
+ int alua_access_state, primary = 0, valid_states;
u16 tg_pt_id, rtpi;
- if (!(l_port))
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ if (!l_port)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ if (cmd->data_length < 4) {
+ pr_warn("SET TARGET PORT GROUPS parameter list length %u too"
+ " small\n", cmd->data_length);
+ return TCM_INVALID_PARAMETER_LIST;
+ }
+
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
/*
- * Determine if explict ALUA via SET_TARGET_PORT_GROUPS is allowed
+ * Determine if explicit 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;
+ if (!l_tg_pt_gp_mem) {
+ pr_err("Unable to access l_port->sep_alua_tg_pt_gp_mem\n");
+ rc = TCM_UNSUPPORTED_SCSI_OPCODE;
+ goto out;
}
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)) {
+ 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;
+ pr_err("Unable to access *l_tg_pt_gp_mem->tg_pt_gp\n");
+ rc = TCM_UNSUPPORTED_SCSI_OPCODE;
+ goto out;
}
- 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;
+ if (!(l_tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICIT_ALUA)) {
+ pr_debug("Unable to process SET_TARGET_PORT_GROUPS"
+ " while TPGS_EXPLICIT_ALUA is disabled\n");
+ rc = TCM_UNSUPPORTED_SCSI_OPCODE;
+ goto out;
}
+ valid_states = l_tg_pt_gp->tg_pt_gp_alua_supported_states;
+
+ ptr = &buf[4]; /* Skip over RESERVED area in header */
while (len < cmd->data_length) {
+ bool found = false;
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) {
+ rc = core_alua_check_transition(alua_access_state,
+ valid_states, &primary);
+ if (rc) {
/*
* If the SET TARGET PORT GROUPS attempts to establish
* an invalid combination of target port asymmetric
@@ -201,9 +360,9 @@ int core_emulate_set_target_port_groups(struct se_cmd *cmd)
* REQUEST, and the additional sense code set to INVALID
* FIELD IN PARAMETER LIST.
*/
- return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ goto out;
}
- rc = -1;
+
/*
* If the ASYMMETRIC ACCESS STATE field (see table 267)
* specifies a primary target port asymmetric access state,
@@ -218,52 +377,46 @@ int core_emulate_set_target_port_groups(struct se_cmd *cmd)
* changed.
*/
if (primary) {
- tg_pt_id = ((ptr[2] << 8) & 0xff);
- tg_pt_id |= (ptr[3] & 0xff);
+ tg_pt_id = get_unaligned_be16(ptr + 2);
/*
* Locate the matching target port group ID from
* the global tg_pt_gp list
*/
- spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+ spin_lock(&dev->t10_alua.tg_pt_gps_lock);
list_for_each_entry(tg_pt_gp,
- &T10_ALUA(su_dev)->tg_pt_gps_list,
+ &dev->t10_alua.tg_pt_gps_list,
tg_pt_gp_list) {
- if (!(tg_pt_gp->tg_pt_gp_valid_id))
+ 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);
+ smp_mb__after_atomic();
+
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
- rc = core_alua_do_port_transition(tg_pt_gp,
+ if (!core_alua_do_port_transition(tg_pt_gp,
dev, l_port, nacl,
- alua_access_state, 1);
+ alua_access_state, 1))
+ found = true;
- spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+ spin_lock(&dev->t10_alua.tg_pt_gps_lock);
atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
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;
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
} else {
/*
- * Extact the RELATIVE TARGET PORT IDENTIFIER to identify
+ * Extract 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);
+ rtpi = get_unaligned_be16(ptr + 2);
/*
- * Locate the matching relative target port identifer
+ * Locate the matching relative target port identifier
* for the struct se_device storage object.
*/
spin_lock(&dev->se_port_lock);
@@ -273,36 +426,55 @@ int core_emulate_set_target_port_groups(struct se_cmd *cmd)
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);
+ if (!core_alua_set_tg_pt_secondary_state(
+ tg_pt_gp_mem, port, 1, 1))
+ found = true;
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;
+ }
+
+ if (!found) {
+ rc = TCM_INVALID_PARAMETER_LIST;
+ goto out;
}
ptr += 4;
len += 4;
}
- return 0;
+out:
+ transport_kunmap_data_sg(cmd);
+ if (!rc)
+ target_complete_cmd(cmd, GOOD);
+ return rc;
}
-static inline int core_alua_state_nonoptimized(
+static inline void set_ascq(struct se_cmd *cmd, u8 alua_ascq)
+{
+ /*
+ * Set SCSI additional sense code (ASC) to 'LUN Not Accessible';
+ * The ALUA additional sense code qualifier (ASCQ) is determined
+ * by the ALUA primary or secondary access state..
+ */
+ pr_debug("[%s]: ALUA TG Port not available, "
+ "SenseKey: NOT_READY, ASC/ASCQ: "
+ "0x04/0x%02x\n",
+ cmd->se_tfo->get_fabric_name(), alua_ascq);
+
+ cmd->scsi_asc = 0x04;
+ cmd->scsi_ascq = alua_ascq;
+}
+
+static inline void core_alua_state_nonoptimized(
struct se_cmd *cmd,
unsigned char *cdb,
- int nonop_delay_msecs,
- u8 *alua_ascq)
+ int nonop_delay_msecs)
{
/*
* Set SCF_ALUA_NON_OPTIMIZED here, this value will be checked
@@ -311,13 +483,85 @@ static inline int core_alua_state_nonoptimized(
*/
cmd->se_cmd_flags |= SCF_ALUA_NON_OPTIMIZED;
cmd->alua_nonop_delay = nonop_delay_msecs;
+}
+
+static inline int core_alua_state_lba_dependent(
+ struct se_cmd *cmd,
+ struct t10_alua_tg_pt_gp *tg_pt_gp)
+{
+ struct se_device *dev = cmd->se_dev;
+ u64 segment_size, segment_mult, sectors, lba;
+
+ /* Only need to check for cdb actually containing LBAs */
+ if (!(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB))
+ return 0;
+
+ spin_lock(&dev->t10_alua.lba_map_lock);
+ segment_size = dev->t10_alua.lba_map_segment_size;
+ segment_mult = dev->t10_alua.lba_map_segment_multiplier;
+ sectors = cmd->data_length / dev->dev_attrib.block_size;
+
+ lba = cmd->t_task_lba;
+ while (lba < cmd->t_task_lba + sectors) {
+ struct t10_alua_lba_map *cur_map = NULL, *map;
+ struct t10_alua_lba_map_member *map_mem;
+
+ list_for_each_entry(map, &dev->t10_alua.lba_map_list,
+ lba_map_list) {
+ u64 start_lba, last_lba;
+ u64 first_lba = map->lba_map_first_lba;
+
+ if (segment_mult) {
+ u64 tmp = lba;
+ start_lba = do_div(tmp, segment_size * segment_mult);
+
+ last_lba = first_lba + segment_size - 1;
+ if (start_lba >= first_lba &&
+ start_lba <= last_lba) {
+ lba += segment_size;
+ cur_map = map;
+ break;
+ }
+ } else {
+ last_lba = map->lba_map_last_lba;
+ if (lba >= first_lba && lba <= last_lba) {
+ lba = last_lba + 1;
+ cur_map = map;
+ break;
+ }
+ }
+ }
+ if (!cur_map) {
+ spin_unlock(&dev->t10_alua.lba_map_lock);
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);
+ return 1;
+ }
+ list_for_each_entry(map_mem, &cur_map->lba_map_mem_list,
+ lba_map_mem_list) {
+ if (map_mem->lba_map_mem_alua_pg_id !=
+ tg_pt_gp->tg_pt_gp_id)
+ continue;
+ switch(map_mem->lba_map_mem_alua_state) {
+ case ALUA_ACCESS_STATE_STANDBY:
+ spin_unlock(&dev->t10_alua.lba_map_lock);
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY);
+ return 1;
+ case ALUA_ACCESS_STATE_UNAVAILABLE:
+ spin_unlock(&dev->t10_alua.lba_map_lock);
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);
+ return 1;
+ default:
+ break;
+ }
+ }
+ }
+ spin_unlock(&dev->t10_alua.lba_map_lock);
return 0;
}
static inline int core_alua_state_standby(
struct se_cmd *cmd,
- unsigned char *cdb,
- u8 *alua_ascq)
+ unsigned char *cdb)
{
/*
* Allowed CDBs for ALUA_ACCESS_STATE_STANDBY as defined by
@@ -332,12 +576,22 @@ static inline int core_alua_state_standby(
case REPORT_LUNS:
case RECEIVE_DIAGNOSTIC:
case SEND_DIAGNOSTIC:
+ case READ_CAPACITY:
+ return 0;
+ case SERVICE_ACTION_IN:
+ switch (cdb[1] & 0x1f) {
+ case SAI_READ_CAPACITY_16:
+ return 0;
+ default:
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY);
+ return 1;
+ }
case MAINTENANCE_IN:
- switch (cdb[1]) {
+ switch (cdb[1] & 0x1f) {
case MI_REPORT_TARGET_PGS:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY);
return 1;
}
case MAINTENANCE_OUT:
@@ -345,7 +599,7 @@ static inline int core_alua_state_standby(
case MO_SET_TARGET_PGS:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY);
return 1;
}
case REQUEST_SENSE:
@@ -355,7 +609,7 @@ static inline int core_alua_state_standby(
case WRITE_BUFFER:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY);
return 1;
}
@@ -364,8 +618,7 @@ static inline int core_alua_state_standby(
static inline int core_alua_state_unavailable(
struct se_cmd *cmd,
- unsigned char *cdb,
- u8 *alua_ascq)
+ unsigned char *cdb)
{
/*
* Allowed CDBs for ALUA_ACCESS_STATE_UNAVAILABLE as defined by
@@ -374,12 +627,13 @@ static inline int core_alua_state_unavailable(
switch (cdb[0]) {
case INQUIRY:
case REPORT_LUNS:
+ return 0;
case MAINTENANCE_IN:
- switch (cdb[1]) {
+ switch (cdb[1] & 0x1f) {
case MI_REPORT_TARGET_PGS:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);
return 1;
}
case MAINTENANCE_OUT:
@@ -387,7 +641,7 @@ static inline int core_alua_state_unavailable(
case MO_SET_TARGET_PGS:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);
return 1;
}
case REQUEST_SENSE:
@@ -395,7 +649,7 @@ static inline int core_alua_state_unavailable(
case WRITE_BUFFER:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
+ set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE);
return 1;
}
@@ -404,22 +658,22 @@ static inline int core_alua_state_unavailable(
static inline int core_alua_state_transition(
struct se_cmd *cmd,
- unsigned char *cdb,
- u8 *alua_ascq)
+ unsigned char *cdb)
{
/*
- * Allowed CDBs for ALUA_ACCESS_STATE_TRANSITIO as defined by
+ * Allowed CDBs for ALUA_ACCESS_STATE_TRANSITION as defined by
* spc4r17 section 5.9.2.5
*/
switch (cdb[0]) {
case INQUIRY:
case REPORT_LUNS:
+ return 0;
case MAINTENANCE_IN:
- switch (cdb[1]) {
+ switch (cdb[1] & 0x1f) {
case MI_REPORT_TARGET_PGS:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_STATE_TRANSITION;
+ set_ascq(cmd, ASCQ_04H_ALUA_STATE_TRANSITION);
return 1;
}
case REQUEST_SENSE:
@@ -427,7 +681,7 @@ static inline int core_alua_state_transition(
case WRITE_BUFFER:
return 0;
default:
- *alua_ascq = ASCQ_04H_ALUA_STATE_TRANSITION;
+ set_ascq(cmd, ASCQ_04H_ALUA_STATE_TRANSITION);
return 1;
}
@@ -435,53 +689,37 @@ static inline int core_alua_state_transition(
}
/*
- * 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 1: Is used to signal LUN not accessible, and check condition/not ready
* return 0: Used to signal success
- * reutrn -1: Used to signal failure, and invalid cdb field
+ * return -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)
+sense_reason_t
+target_alua_state_check(struct se_cmd *cmd)
{
- struct se_lun *lun = SE_LUN(cmd);
+ struct se_device *dev = cmd->se_dev;
+ unsigned char *cdb = cmd->t_task_cdb;
+ struct se_lun *lun = cmd->se_lun;
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))
+ if (dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE)
+ return 0;
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ return 0;
+
+ 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"
+ pr_debug("ALUA: Got secondary offline status for local"
" target port\n");
- *alua_ascq = ASCQ_04H_ALUA_OFFLINE;
- return 1;
+ set_ascq(cmd, ASCQ_04H_ALUA_OFFLINE);
+ return TCM_CHECK_CONDITION_NOT_READY;
}
/*
* Second, obtain the struct t10_alua_tg_pt_gp_member pointer to the
@@ -490,58 +728,91 @@ static int core_alua_state_check(
* a ALUA logical unit group.
*/
tg_pt_gp_mem = port->sep_alua_tg_pt_gp_mem;
+ if (!tg_pt_gp_mem)
+ return 0;
+
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.
+ * Process ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED in a separate conditional
+ * statement so the compiler knows explicitly to check this case first.
* For the Optimized ALUA access state case, we want to process the
* incoming fabric cmd ASAP..
*/
- if (out_alua_state == ALUA_ACCESS_STATE_ACTIVE_OPTMIZED)
+ if (out_alua_state == ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED)
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);
+ core_alua_state_nonoptimized(cmd, cdb, nonop_delay_msecs);
+ break;
case ALUA_ACCESS_STATE_STANDBY:
- return core_alua_state_standby(cmd, cdb, alua_ascq);
+ if (core_alua_state_standby(cmd, cdb))
+ return TCM_CHECK_CONDITION_NOT_READY;
+ break;
case ALUA_ACCESS_STATE_UNAVAILABLE:
- return core_alua_state_unavailable(cmd, cdb, alua_ascq);
+ if (core_alua_state_unavailable(cmd, cdb))
+ return TCM_CHECK_CONDITION_NOT_READY;
+ break;
case ALUA_ACCESS_STATE_TRANSITION:
- return core_alua_state_transition(cmd, cdb, alua_ascq);
+ if (core_alua_state_transition(cmd, cdb))
+ return TCM_CHECK_CONDITION_NOT_READY;
+ break;
+ case ALUA_ACCESS_STATE_LBA_DEPENDENT:
+ if (core_alua_state_lba_dependent(cmd, tg_pt_gp))
+ return TCM_CHECK_CONDITION_NOT_READY;
+ break;
/*
* 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",
+ pr_err("Unknown ALUA access state: 0x%02x\n",
out_alua_state);
- return -1;
+ return TCM_INVALID_CDB_FIELD;
}
return 0;
}
/*
- * Check implict and explict ALUA state change request.
+ * Check implicit and explicit ALUA state change request.
*/
-static int core_alua_check_transition(int state, int *primary)
+static sense_reason_t
+core_alua_check_transition(int state, int valid, int *primary)
{
+ /*
+ * OPTIMIZED, NON-OPTIMIZED, STANDBY and UNAVAILABLE are
+ * defined as primary target port asymmetric access states.
+ */
switch (state) {
- case ALUA_ACCESS_STATE_ACTIVE_OPTMIZED:
+ case ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED:
+ if (!(valid & ALUA_AO_SUP))
+ goto not_supported;
+ *primary = 1;
+ break;
case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED:
+ if (!(valid & ALUA_AN_SUP))
+ goto not_supported;
+ *primary = 1;
+ break;
case ALUA_ACCESS_STATE_STANDBY:
+ if (!(valid & ALUA_S_SUP))
+ goto not_supported;
+ *primary = 1;
+ break;
case ALUA_ACCESS_STATE_UNAVAILABLE:
- /*
- * OPTIMIZED, NON-OPTIMIZED, STANDBY and UNAVAILABLE are
- * defined as primary target port asymmetric access states.
- */
+ if (!(valid & ALUA_U_SUP))
+ goto not_supported;
+ *primary = 1;
+ break;
+ case ALUA_ACCESS_STATE_LBA_DEPENDENT:
+ if (!(valid & ALUA_LBD_SUP))
+ goto not_supported;
*primary = 1;
break;
case ALUA_ACCESS_STATE_OFFLINE:
@@ -549,29 +820,46 @@ static int core_alua_check_transition(int state, int *primary)
* OFFLINE state is defined as a secondary target port
* asymmetric access state.
*/
+ if (!(valid & ALUA_O_SUP))
+ goto not_supported;
*primary = 0;
break;
+ case ALUA_ACCESS_STATE_TRANSITION:
+ /*
+ * Transitioning is set internally, and
+ * cannot be selected manually.
+ */
+ goto not_supported;
default:
- printk(KERN_ERR "Unknown ALUA access state: 0x%02x\n", state);
- return -1;
+ pr_err("Unknown ALUA access state: 0x%02x\n", state);
+ return TCM_INVALID_PARAMETER_LIST;
}
return 0;
+
+not_supported:
+ pr_err("ALUA access state %s not supported",
+ core_alua_dump_state(state));
+ return TCM_INVALID_PARAMETER_LIST;
}
static char *core_alua_dump_state(int state)
{
switch (state) {
- case ALUA_ACCESS_STATE_ACTIVE_OPTMIZED:
+ case ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED:
return "Active/Optimized";
case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED:
return "Active/NonOptimized";
+ case ALUA_ACCESS_STATE_LBA_DEPENDENT:
+ return "LBA Dependent";
case ALUA_ACCESS_STATE_STANDBY:
return "Standby";
case ALUA_ACCESS_STATE_UNAVAILABLE:
return "Unavailable";
case ALUA_ACCESS_STATE_OFFLINE:
return "Offline";
+ case ALUA_ACCESS_STATE_TRANSITION:
+ return "Transitioning";
default:
return "Unknown";
}
@@ -584,10 +872,10 @@ 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";
+ case ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG:
+ return "Altered by Explicit STPG";
+ case ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA:
+ return "Altered by Implicit ALUA";
default:
return "Unknown";
}
@@ -610,7 +898,7 @@ int core_alua_check_nonop_delay(
* The ALUA Active/NonOptimized access state delay can be disabled
* in via configfs with a value of zero
*/
- if (!(cmd->alua_nonop_delay))
+ if (!cmd->alua_nonop_delay)
return 0;
/*
* struct se_cmd->alua_nonop_delay gets set by a target port group
@@ -630,95 +918,67 @@ static int core_alua_write_tpg_metadata(
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));
+ struct file *file = filp_open(path, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ int ret;
- 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);
+ if (IS_ERR(file)) {
+ pr_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;
+ ret = kernel_write(file, md_buf, md_buf_len, 0);
+ if (ret < 0)
+ pr_err("Error writing ALUA metadata file: %s\n", path);
+ fput(file);
+ return (ret < 0) ? -EIO : 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 t10_alua_tg_pt_gp *tg_pt_gp)
{
- struct se_subsystem_dev *su_dev = tg_pt_gp->tg_pt_gp_su_dev;
- struct t10_wwn *wwn = &su_dev->t10_wwn;
+ unsigned char *md_buf;
+ struct t10_wwn *wwn = &tg_pt_gp->tg_pt_gp_dev->t10_wwn;
char path[ALUA_METADATA_PATH_LEN];
- int len;
+ int len, rc;
+
+ md_buf = kzalloc(ALUA_MD_BUF_LEN, GFP_KERNEL);
+ if (!md_buf) {
+ pr_err("Unable to allocate buf for ALUA metadata\n");
+ return -ENOMEM;
+ }
memset(path, 0, ALUA_METADATA_PATH_LEN);
- len = snprintf(md_buf, tg_pt_gp->tg_pt_gp_md_buf_len,
+ len = snprintf(md_buf, ALUA_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_id,
+ tg_pt_gp->tg_pt_gp_alua_pending_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);
+ rc = core_alua_write_tpg_metadata(path, md_buf, len);
+ kfree(md_buf);
+ return rc;
}
-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)
+static void core_alua_do_transition_tg_pt_work(struct work_struct *work)
{
+ struct t10_alua_tg_pt_gp *tg_pt_gp = container_of(work,
+ struct t10_alua_tg_pt_gp, tg_pt_gp_transition_work.work);
+ struct se_device *dev = tg_pt_gp->tg_pt_gp_dev;
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);
+ bool explicit = (tg_pt_gp->tg_pt_gp_alua_access_status ==
+ ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG);
spin_lock(&tg_pt_gp->tg_pt_gp_lock);
list_for_each_entry(mem, &tg_pt_gp->tg_pt_gp_mem_list,
@@ -729,7 +989,7 @@ static int core_alua_do_transition_tg_pt(
* 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.
+ * ACCESS STATE CHANGED.
*
* After an explicit target port asymmetric access state
* change, a device server shall establish a unit attention
@@ -739,7 +999,7 @@ static int core_alua_do_transition_tg_pt(
* TARGET PORT GROUPS command
*/
atomic_inc(&mem->tg_pt_gp_mem_ref_cnt);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
spin_lock_bh(&port->sep_alua_lock);
@@ -748,14 +1008,17 @@ static int core_alua_do_transition_tg_pt(
lacl = se_deve->se_lun_acl;
/*
* se_deve->se_lun_acl pointer may be NULL for a
- * entry created without explict Node+MappedLUN ACLs
+ * entry created without explicit Node+MappedLUN ACLs
*/
- if (!(lacl))
+ if (!lacl)
continue;
- if (explict &&
- (nacl != NULL) && (nacl == lacl->se_lun_nacl) &&
- (l_port != NULL) && (l_port == port))
+ if ((tg_pt_gp->tg_pt_gp_alua_access_status ==
+ ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG) &&
+ (tg_pt_gp->tg_pt_gp_alua_nacl != NULL) &&
+ (tg_pt_gp->tg_pt_gp_alua_nacl == lacl->se_lun_nacl) &&
+ (tg_pt_gp->tg_pt_gp_alua_port != NULL) &&
+ (tg_pt_gp->tg_pt_gp_alua_port == port))
continue;
core_scsi3_ua_allocate(lacl->se_lun_nacl,
@@ -766,7 +1029,7 @@ static int core_alua_do_transition_tg_pt(
spin_lock(&tg_pt_gp->tg_pt_gp_lock);
atomic_dec(&mem->tg_pt_gp_mem_ref_cnt);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
}
spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
/*
@@ -783,20 +1046,102 @@ static int core_alua_do_transition_tg_pt(
*/
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);
+ core_alua_update_tpg_primary_metadata(tg_pt_gp);
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);
+ atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state,
+ tg_pt_gp->tg_pt_gp_alua_pending_state);
+
+ pr_debug("Successful %s ALUA transition TG PT Group: %s ID: %hu"
+ " from primary access state %s to %s\n", (explicit) ? "explicit" :
+ "implicit", config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item),
+ tg_pt_gp->tg_pt_gp_id,
+ core_alua_dump_state(tg_pt_gp->tg_pt_gp_alua_previous_state),
+ core_alua_dump_state(tg_pt_gp->tg_pt_gp_alua_pending_state));
+ spin_lock(&dev->t10_alua.tg_pt_gps_lock);
+ atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt);
+ smp_mb__after_atomic();
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
+
+ if (tg_pt_gp->tg_pt_gp_transition_complete)
+ complete(tg_pt_gp->tg_pt_gp_transition_complete);
+}
+
+static int core_alua_do_transition_tg_pt(
+ struct t10_alua_tg_pt_gp *tg_pt_gp,
+ int new_state,
+ int explicit)
+{
+ struct se_device *dev = tg_pt_gp->tg_pt_gp_dev;
+ DECLARE_COMPLETION_ONSTACK(wait);
+
+ /* Nothing to be done here */
+ if (atomic_read(&tg_pt_gp->tg_pt_gp_alua_access_state) == new_state)
+ return 0;
+
+ if (new_state == ALUA_ACCESS_STATE_TRANSITION)
+ return -EAGAIN;
+
+ /*
+ * Flush any pending transitions
+ */
+ if (!explicit && tg_pt_gp->tg_pt_gp_implicit_trans_secs &&
+ atomic_read(&tg_pt_gp->tg_pt_gp_alua_access_state) ==
+ ALUA_ACCESS_STATE_TRANSITION) {
+ /* Just in case */
+ tg_pt_gp->tg_pt_gp_alua_pending_state = new_state;
+ tg_pt_gp->tg_pt_gp_transition_complete = &wait;
+ flush_delayed_work(&tg_pt_gp->tg_pt_gp_transition_work);
+ wait_for_completion(&wait);
+ tg_pt_gp->tg_pt_gp_transition_complete = NULL;
+ return 0;
+ }
+
+ /*
+ * Save the old primary ALUA access state, and set the current state
+ * to ALUA_ACCESS_STATE_TRANSITION.
+ */
+ tg_pt_gp->tg_pt_gp_alua_previous_state =
+ atomic_read(&tg_pt_gp->tg_pt_gp_alua_access_state);
+ tg_pt_gp->tg_pt_gp_alua_pending_state = new_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 = (explicit) ?
+ ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG :
+ ALUA_STATUS_ALTERED_BY_IMPLICIT_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);
- 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));
+ /*
+ * Take a reference for workqueue item
+ */
+ spin_lock(&dev->t10_alua.tg_pt_gps_lock);
+ atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt);
+ smp_mb__after_atomic();
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
+
+ if (!explicit && tg_pt_gp->tg_pt_gp_implicit_trans_secs) {
+ unsigned long transition_tmo;
+
+ transition_tmo = tg_pt_gp->tg_pt_gp_implicit_trans_secs * HZ;
+ queue_delayed_work(tg_pt_gp->tg_pt_gp_dev->tmr_wq,
+ &tg_pt_gp->tg_pt_gp_transition_work,
+ transition_tmo);
+ } else {
+ tg_pt_gp->tg_pt_gp_transition_complete = &wait;
+ queue_delayed_work(tg_pt_gp->tg_pt_gp_dev->tmr_wq,
+ &tg_pt_gp->tg_pt_gp_transition_work, 0);
+ wait_for_completion(&wait);
+ tg_pt_gp->tg_pt_gp_transition_complete = NULL;
+ }
return 0;
}
@@ -807,49 +1152,41 @@ int core_alua_do_port_transition(
struct se_port *l_port,
struct se_node_acl *l_nacl,
int new_state,
- int explict)
+ int explicit)
{
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;
+ int primary, valid_states, rc = 0;
- if (core_alua_check_transition(new_state, &primary) != 0)
+ valid_states = l_tg_pt_gp->tg_pt_gp_alua_supported_states;
+ if (core_alua_check_transition(new_state, valid_states, &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();
+ smp_mb__after_atomic();
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)) {
+ 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);
+ l_tg_pt_gp->tg_pt_gp_alua_port = l_port;
+ l_tg_pt_gp->tg_pt_gp_alua_nacl = l_nacl;
+ rc = core_alua_do_transition_tg_pt(l_tg_pt_gp,
+ new_state, explicit);
atomic_dec(&lu_gp->lu_gp_ref_cnt);
- smp_mb__after_atomic_dec();
- kfree(md_buf);
- return 0;
+ smp_mb__after_atomic();
+ return rc;
}
/*
* For all other LU groups aside from 'default_lu_gp', walk all of
@@ -861,21 +1198,20 @@ int core_alua_do_port_transition(
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();
+ smp_mb__after_atomic();
spin_unlock(&lu_gp->lu_gp_lock);
- spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+ spin_lock(&dev->t10_alua.tg_pt_gps_lock);
list_for_each_entry(tg_pt_gp,
- &T10_ALUA(su_dev)->tg_pt_gps_list,
+ &dev->t10_alua.tg_pt_gps_list,
tg_pt_gp_list) {
- if (!(tg_pt_gp->tg_pt_gp_valid_id))
+ 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
+ * is changed for any target port group accessible 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
@@ -885,44 +1221,48 @@ int core_alua_do_port_transition(
continue;
if (l_tg_pt_gp == tg_pt_gp) {
- port = l_port;
- nacl = l_nacl;
+ tg_pt_gp->tg_pt_gp_alua_port = l_port;
+ tg_pt_gp->tg_pt_gp_alua_nacl = l_nacl;
} else {
- port = NULL;
- nacl = NULL;
+ tg_pt_gp->tg_pt_gp_alua_port = NULL;
+ tg_pt_gp->tg_pt_gp_alua_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);
+ smp_mb__after_atomic();
+ spin_unlock(&dev->t10_alua.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);
+ rc = core_alua_do_transition_tg_pt(tg_pt_gp,
+ new_state, explicit);
- spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+ spin_lock(&dev->t10_alua.tg_pt_gps_lock);
atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
+ if (rc)
+ break;
}
- spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+ spin_unlock(&dev->t10_alua.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();
+ smp_mb__after_atomic();
}
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));
+ if (!rc) {
+ pr_debug("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,
+ (explicit) ? "explicit" : "implicit",
+ core_alua_dump_state(new_state));
+ }
atomic_dec(&lu_gp->lu_gp_ref_cnt);
- smp_mb__after_atomic_dec();
- kfree(md_buf);
- return 0;
+ smp_mb__after_atomic();
+ return rc;
}
/*
@@ -930,54 +1270,60 @@ int core_alua_do_port_transition(
*/
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_port *port)
{
+ unsigned char *md_buf;
struct se_portal_group *se_tpg = port->sep_tpg;
char path[ALUA_METADATA_PATH_LEN], wwn[ALUA_SECONDARY_METADATA_WWN_LEN];
- int len;
+ int len, rc;
+
+ md_buf = kzalloc(ALUA_MD_BUF_LEN, GFP_KERNEL);
+ if (!md_buf) {
+ pr_err("Unable to allocate buf for ALUA metadata\n");
+ return -ENOMEM;
+ }
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));
+ se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg));
- if (TPG_TFO(se_tpg)->tpg_get_tag != NULL)
+ if (se_tpg->se_tpg_tfo->tpg_get_tag != NULL)
snprintf(wwn+len, ALUA_SECONDARY_METADATA_WWN_LEN-len, "+%hu",
- TPG_TFO(se_tpg)->tpg_get_tag(se_tpg));
+ se_tpg->se_tpg_tfo->tpg_get_tag(se_tpg));
- len = snprintf(md_buf, md_buf_len, "alua_tg_pt_offline=%d\n"
+ len = snprintf(md_buf, ALUA_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,
+ se_tpg->se_tpg_tfo->get_fabric_name(), wwn,
port->sep_lun->unpacked_lun);
- return core_alua_write_tpg_metadata(path, md_buf, len);
+ rc = core_alua_write_tpg_metadata(path, md_buf, len);
+ kfree(md_buf);
+
+ return rc;
}
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 explicit,
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)) {
+ if (!tg_pt_gp) {
spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
- printk(KERN_ERR "Unable to complete secondary state"
+ pr_err("Unable to complete secondary state"
" transition\n");
- return -1;
+ return -EINVAL;
}
trans_delay_msecs = tg_pt_gp->tg_pt_gp_trans_delay_msecs;
/*
@@ -989,14 +1335,13 @@ static int core_alua_set_tg_pt_secondary_state(
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;
+ port->sep_tg_pt_secondary_stat = (explicit) ?
+ ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG :
+ ALUA_STATUS_ALTERED_BY_IMPLICIT_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),
+ pr_debug("Successful %s ALUA transition TG PT Group: %s ID: %hu"
+ " to secondary access state: %s\n", (explicit) ? "explicit" :
+ "implicit", 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);
@@ -1011,42 +1356,134 @@ static int core_alua_set_tg_pt_secondary_state(
* 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);
+ core_alua_update_tpg_secondary_metadata(tg_pt_gp_mem, port);
mutex_unlock(&port->sep_tg_pt_md_mutex);
+ }
- kfree(md_buf);
+ return 0;
+}
+
+struct t10_alua_lba_map *
+core_alua_allocate_lba_map(struct list_head *list,
+ u64 first_lba, u64 last_lba)
+{
+ struct t10_alua_lba_map *lba_map;
+
+ lba_map = kmem_cache_zalloc(t10_alua_lba_map_cache, GFP_KERNEL);
+ if (!lba_map) {
+ pr_err("Unable to allocate struct t10_alua_lba_map\n");
+ return ERR_PTR(-ENOMEM);
}
+ INIT_LIST_HEAD(&lba_map->lba_map_mem_list);
+ lba_map->lba_map_first_lba = first_lba;
+ lba_map->lba_map_last_lba = last_lba;
+
+ list_add_tail(&lba_map->lba_map_list, list);
+ return lba_map;
+}
+
+int
+core_alua_allocate_lba_map_mem(struct t10_alua_lba_map *lba_map,
+ int pg_id, int state)
+{
+ struct t10_alua_lba_map_member *lba_map_mem;
+ list_for_each_entry(lba_map_mem, &lba_map->lba_map_mem_list,
+ lba_map_mem_list) {
+ if (lba_map_mem->lba_map_mem_alua_pg_id == pg_id) {
+ pr_err("Duplicate pg_id %d in lba_map\n", pg_id);
+ return -EINVAL;
+ }
+ }
+
+ lba_map_mem = kmem_cache_zalloc(t10_alua_lba_map_mem_cache, GFP_KERNEL);
+ if (!lba_map_mem) {
+ pr_err("Unable to allocate struct t10_alua_lba_map_mem\n");
+ return -ENOMEM;
+ }
+ lba_map_mem->lba_map_mem_alua_state = state;
+ lba_map_mem->lba_map_mem_alua_pg_id = pg_id;
+
+ list_add_tail(&lba_map_mem->lba_map_mem_list,
+ &lba_map->lba_map_mem_list);
return 0;
}
+void
+core_alua_free_lba_map(struct list_head *lba_list)
+{
+ struct t10_alua_lba_map *lba_map, *lba_map_tmp;
+ struct t10_alua_lba_map_member *lba_map_mem, *lba_map_mem_tmp;
+
+ list_for_each_entry_safe(lba_map, lba_map_tmp, lba_list,
+ lba_map_list) {
+ list_for_each_entry_safe(lba_map_mem, lba_map_mem_tmp,
+ &lba_map->lba_map_mem_list,
+ lba_map_mem_list) {
+ list_del(&lba_map_mem->lba_map_mem_list);
+ kmem_cache_free(t10_alua_lba_map_mem_cache,
+ lba_map_mem);
+ }
+ list_del(&lba_map->lba_map_list);
+ kmem_cache_free(t10_alua_lba_map_cache, lba_map);
+ }
+}
+
+void
+core_alua_set_lba_map(struct se_device *dev, struct list_head *lba_map_list,
+ int segment_size, int segment_mult)
+{
+ struct list_head old_lba_map_list;
+ struct t10_alua_tg_pt_gp *tg_pt_gp;
+ int activate = 0, supported;
+
+ INIT_LIST_HEAD(&old_lba_map_list);
+ spin_lock(&dev->t10_alua.lba_map_lock);
+ dev->t10_alua.lba_map_segment_size = segment_size;
+ dev->t10_alua.lba_map_segment_multiplier = segment_mult;
+ list_splice_init(&dev->t10_alua.lba_map_list, &old_lba_map_list);
+ if (lba_map_list) {
+ list_splice_init(lba_map_list, &dev->t10_alua.lba_map_list);
+ activate = 1;
+ }
+ spin_unlock(&dev->t10_alua.lba_map_lock);
+ spin_lock(&dev->t10_alua.tg_pt_gps_lock);
+ list_for_each_entry(tg_pt_gp, &dev->t10_alua.tg_pt_gps_list,
+ tg_pt_gp_list) {
+
+ if (!tg_pt_gp->tg_pt_gp_valid_id)
+ continue;
+ supported = tg_pt_gp->tg_pt_gp_alua_supported_states;
+ if (activate)
+ supported |= ALUA_LBD_SUP;
+ else
+ supported &= ~ALUA_LBD_SUP;
+ tg_pt_gp->tg_pt_gp_alua_supported_states = supported;
+ }
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
+ core_alua_free_lba_map(&old_lba_map_list);
+}
+
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);;
+ if (!lu_gp) {
+ pr_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_node);
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_id = alua_lu_gps_counter++;
lu_gp->lu_gp_valid_id = 1;
- se_global->alua_lu_gps_count++;
+ alua_lu_gps_count++;
}
return lu_gp;
@@ -1060,41 +1497,41 @@ int core_alua_set_lu_gp_id(struct t10_alua_lu_gp *lu_gp, u16 lu_gp_id)
* 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,"
+ pr_warn("ALUA LU Group already has a valid ID,"
" ignoring request\n");
- return -1;
+ return -EINVAL;
}
- 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:"
+ spin_lock(&lu_gps_lock);
+ if (alua_lu_gps_count == 0x0000ffff) {
+ pr_err("Maximum ALUA alua_lu_gps_count:"
" 0x0000ffff reached\n");
- spin_unlock(&se_global->lu_gps_lock);
+ spin_unlock(&lu_gps_lock);
kmem_cache_free(t10_alua_lu_gp_cache, lu_gp);
- return -1;
+ return -ENOSPC;
}
again:
lu_gp_id_tmp = (lu_gp_id != 0) ? lu_gp_id :
- se_global->alua_lu_gps_counter++;
+ alua_lu_gps_counter++;
- list_for_each_entry(lu_gp_tmp, &se_global->g_lu_gps_list, lu_gp_list) {
+ list_for_each_entry(lu_gp_tmp, &lu_gps_list, lu_gp_node) {
if (lu_gp_tmp->lu_gp_id == lu_gp_id_tmp) {
- if (!(lu_gp_id))
+ if (!lu_gp_id)
goto again;
- printk(KERN_WARNING "ALUA Logical Unit Group ID: %hu"
+ pr_warn("ALUA Logical Unit Group ID: %hu"
" already exists, ignoring request\n",
lu_gp_id);
- spin_unlock(&se_global->lu_gps_lock);
- return -1;
+ spin_unlock(&lu_gps_lock);
+ return -EINVAL;
}
}
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);
+ list_add_tail(&lu_gp->lu_gp_node, &lu_gps_list);
+ alua_lu_gps_count++;
+ spin_unlock(&lu_gps_lock);
return 0;
}
@@ -1105,8 +1542,8 @@ 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");
+ if (!lu_gp_mem) {
+ pr_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);
@@ -1130,11 +1567,10 @@ void core_alua_free_lu_gp(struct t10_alua_lu_gp *lu_gp)
* 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);
+ spin_lock(&lu_gps_lock);
+ list_del(&lu_gp->lu_gp_node);
+ alua_lu_gps_count--;
+ spin_unlock(&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
@@ -1157,17 +1593,17 @@ void core_alua_free_lu_gp(struct t10_alua_lu_gp *lu_gp)
spin_unlock(&lu_gp->lu_gp_lock);
/*
*
- * lu_gp_mem is assoicated with a single
+ * lu_gp_mem is associated with a single
* struct se_device->dev_alua_lu_gp_mem, and is released when
* struct se_device is released via core_alua_free_lu_gp_mem().
*
* 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.
+ * we want to re-associate 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)
+ if (lu_gp != default_lu_gp)
__core_alua_attach_lu_gp_mem(lu_gp_mem,
- se_global->default_lu_gp);
+ default_lu_gp);
else
lu_gp_mem->lu_gp = NULL;
spin_unlock(&lu_gp_mem->lu_gp_mem_lock);
@@ -1181,16 +1617,11 @@ void core_alua_free_lu_gp(struct t10_alua_lu_gp *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))
+ if (!lu_gp_mem)
return;
while (atomic_read(&lu_gp_mem->lu_gp_mem_ref_cnt))
@@ -1198,7 +1629,7 @@ void core_alua_free_lu_gp_mem(struct se_device *dev)
spin_lock(&lu_gp_mem->lu_gp_mem_lock);
lu_gp = lu_gp_mem->lu_gp;
- if ((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);
@@ -1218,27 +1649,27 @@ 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))
+ spin_lock(&lu_gps_lock);
+ list_for_each_entry(lu_gp, &lu_gps_list, lu_gp_node) {
+ if (!lu_gp->lu_gp_valid_id)
continue;
ci = &lu_gp->lu_gp_group.cg_item;
- if (!(strcmp(config_item_name(ci), name))) {
+ if (!strcmp(config_item_name(ci), name)) {
atomic_inc(&lu_gp->lu_gp_ref_cnt);
- spin_unlock(&se_global->lu_gps_lock);
+ spin_unlock(&lu_gps_lock);
return lu_gp;
}
}
- spin_unlock(&se_global->lu_gps_lock);
+ spin_unlock(&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);
+ spin_lock(&lu_gps_lock);
atomic_dec(&lu_gp->lu_gp_ref_cnt);
- spin_unlock(&se_global->lu_gps_lock);
+ spin_unlock(&lu_gps_lock);
}
/*
@@ -1271,16 +1702,14 @@ void __core_alua_drop_lu_gp_mem(
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 *core_alua_allocate_tg_pt_gp(struct se_device *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");
+ if (!tg_pt_gp) {
+ pr_err("Unable to allocate struct t10_alua_tg_pt_gp\n");
return NULL;
}
INIT_LIST_HEAD(&tg_pt_gp->tg_pt_gp_list);
@@ -1288,30 +1717,39 @@ struct t10_alua_tg_pt_gp *core_alua_allocate_tg_pt_gp(
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;
+ INIT_DELAYED_WORK(&tg_pt_gp->tg_pt_gp_transition_work,
+ core_alua_do_transition_tg_pt_work);
+ tg_pt_gp->tg_pt_gp_dev = dev;
atomic_set(&tg_pt_gp->tg_pt_gp_alua_access_state,
- ALUA_ACCESS_STATE_ACTIVE_OPTMIZED);
+ ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED);
/*
- * Enable both explict and implict ALUA support by default
+ * Enable both explicit and implicit ALUA support by default
*/
tg_pt_gp->tg_pt_gp_alua_access_type =
- TPGS_EXPLICT_ALUA | TPGS_IMPLICT_ALUA;
+ TPGS_EXPLICIT_ALUA | TPGS_IMPLICIT_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;
+ tg_pt_gp->tg_pt_gp_implicit_trans_secs = ALUA_DEFAULT_IMPLICIT_TRANS_SECS;
+
+ /*
+ * Enable all supported states
+ */
+ tg_pt_gp->tg_pt_gp_alua_supported_states =
+ ALUA_T_SUP | ALUA_O_SUP |
+ ALUA_U_SUP | ALUA_S_SUP | ALUA_AN_SUP | ALUA_AO_SUP;
if (def_group) {
- spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+ spin_lock(&dev->t10_alua.tg_pt_gps_lock);
tg_pt_gp->tg_pt_gp_id =
- T10_ALUA(su_dev)->alua_tg_pt_gps_counter++;
+ dev->t10_alua.alua_tg_pt_gps_counter++;
tg_pt_gp->tg_pt_gp_valid_id = 1;
- T10_ALUA(su_dev)->alua_tg_pt_gps_count++;
+ dev->t10_alua.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);
+ &dev->t10_alua.tg_pt_gps_list);
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
}
return tg_pt_gp;
@@ -1321,49 +1759,50 @@ 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 se_device *dev = tg_pt_gp->tg_pt_gp_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,"
+ pr_warn("ALUA TG PT Group already has a valid ID,"
" ignoring request\n");
- return -1;
+ return -EINVAL;
}
- 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:"
+ spin_lock(&dev->t10_alua.tg_pt_gps_lock);
+ if (dev->t10_alua.alua_tg_pt_gps_count == 0x0000ffff) {
+ pr_err("Maximum ALUA alua_tg_pt_gps_count:"
" 0x0000ffff reached\n");
- spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
kmem_cache_free(t10_alua_tg_pt_gp_cache, tg_pt_gp);
- return -1;
+ return -ENOSPC;
}
again:
tg_pt_gp_id_tmp = (tg_pt_gp_id != 0) ? tg_pt_gp_id :
- T10_ALUA(su_dev)->alua_tg_pt_gps_counter++;
+ dev->t10_alua.alua_tg_pt_gps_counter++;
- list_for_each_entry(tg_pt_gp_tmp, &T10_ALUA(su_dev)->tg_pt_gps_list,
+ list_for_each_entry(tg_pt_gp_tmp, &dev->t10_alua.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))
+ if (!tg_pt_gp_id)
goto again;
- printk(KERN_ERR "ALUA Target Port Group ID: %hu already"
+ pr_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;
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
+ return -EINVAL;
}
}
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);
+ &dev->t10_alua.tg_pt_gps_list);
+ dev->t10_alua.alua_tg_pt_gps_count++;
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
return 0;
}
@@ -1375,8 +1814,8 @@ struct t10_alua_tg_pt_gp_member *core_alua_allocate_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");
+ if (!tg_pt_gp_mem) {
+ pr_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);
@@ -1385,7 +1824,6 @@ struct t10_alua_tg_pt_gp_member *core_alua_allocate_tg_pt_gp_mem(
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;
}
@@ -1393,20 +1831,24 @@ struct t10_alua_tg_pt_gp_member *core_alua_allocate_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 se_device *dev = tg_pt_gp->tg_pt_gp_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
+ * no associations *OR* explicit 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);
+ spin_lock(&dev->t10_alua.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);
+ dev->t10_alua.alua_tg_pt_gps_counter--;
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
+
+ flush_delayed_work(&tg_pt_gp->tg_pt_gp_transition_work);
+
/*
* Allow a struct t10_alua_tg_pt_gp_member * referenced by
* core_alua_get_tg_pt_gp_by_name() in
@@ -1415,6 +1857,7 @@ void core_alua_free_tg_pt_gp(
*/
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.
@@ -1429,18 +1872,18 @@ void core_alua_free_tg_pt_gp(
}
spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
/*
- * tg_pt_gp_mem is assoicated with a single
+ * tg_pt_gp_mem is associated with a single
* se_port->sep_alua_tg_pt_gp_mem, and is released via
* core_alua_free_tg_pt_gp_mem().
*
* 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
+ * assume we want to re-associate 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) {
+ if (tg_pt_gp != dev->t10_alua.default_tg_pt_gp) {
__core_alua_attach_tg_pt_gp_mem(tg_pt_gp_mem,
- T10_ALUA(su_dev)->default_tg_pt_gp);
+ dev->t10_alua.default_tg_pt_gp);
} else
tg_pt_gp_mem->tg_pt_gp = NULL;
spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
@@ -1454,16 +1897,11 @@ void core_alua_free_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))
+ if (!tg_pt_gp_mem)
return;
while (atomic_read(&tg_pt_gp_mem->tg_pt_gp_mem_ref_cnt))
@@ -1471,7 +1909,7 @@ void core_alua_free_tg_pt_gp_mem(struct se_port *port)
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)) {
+ 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);
@@ -1487,25 +1925,24 @@ void core_alua_free_tg_pt_gp_mem(struct se_port *port)
}
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 se_device *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,
+ spin_lock(&dev->t10_alua.tg_pt_gps_lock);
+ list_for_each_entry(tg_pt_gp, &dev->t10_alua.tg_pt_gps_list,
tg_pt_gp_list) {
- if (!(tg_pt_gp->tg_pt_gp_valid_id))
+ 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))) {
+ 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);
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
return tg_pt_gp;
}
}
- spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
return NULL;
}
@@ -1513,11 +1950,11 @@ static struct t10_alua_tg_pt_gp *core_alua_get_tg_pt_gp_by_name(
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;
+ struct se_device *dev = tg_pt_gp->tg_pt_gp_dev;
- spin_lock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+ spin_lock(&dev->t10_alua.tg_pt_gps_lock);
atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt);
- spin_unlock(&T10_ALUA(su_dev)->tg_pt_gps_lock);
+ spin_unlock(&dev->t10_alua.tg_pt_gps_lock);
}
/*
@@ -1553,23 +1990,18 @@ static void __core_alua_drop_tg_pt_gp_mem(
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))
+ 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)) {
+ 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 "
@@ -1596,7 +2028,7 @@ ssize_t core_alua_store_tg_pt_gp_info(
{
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 se_device *dev = port->sep_lun->lun_se_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];
@@ -1605,16 +2037,12 @@ ssize_t core_alua_store_tg_pt_gp_info(
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;
- }
+ tg_pt_gp_mem = port->sep_alua_tg_pt_gp_mem;
+ if (!tg_pt_gp_mem)
+ return 0;
if (count > TG_PT_GROUP_NAME_BUF) {
- printk(KERN_ERR "ALUA Target Port Group alias too large!\n");
+ pr_err("ALUA Target Port Group alias too large!\n");
return -EINVAL;
}
memset(buf, 0, TG_PT_GROUP_NAME_BUF);
@@ -1629,33 +2057,26 @@ ssize_t core_alua_store_tg_pt_gp_info(
* 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,
+ tg_pt_gp_new = core_alua_get_tg_pt_gp_by_name(dev,
strstrip(buf));
- if (!(tg_pt_gp_new))
+ 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)) {
+ 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"
+ if (!tg_pt_gp_new) {
+ pr_debug("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),
+ tpg->se_tpg_tfo->tpg_get_wwn(tpg),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg),
config_item_name(&lun->lun_group.cg_item),
config_item_name(
&tg_pt_gp->tg_pt_gp_group.cg_item),
@@ -1663,7 +2084,7 @@ ssize_t core_alua_store_tg_pt_gp_info(
__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);
+ dev->t10_alua.default_tg_pt_gp);
spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
return count;
@@ -1679,10 +2100,10 @@ ssize_t core_alua_store_tg_pt_gp_info(
*/
__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"
+ pr_debug("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),
+ "Moving" : "Adding", tpg->se_tpg_tfo->tpg_get_wwn(tpg),
+ tpg->se_tpg_tfo->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);
@@ -1695,13 +2116,13 @@ 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");
+ if ((tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICIT_ALUA) &&
+ (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICIT_ALUA))
+ return sprintf(page, "Implicit and Explicit\n");
+ else if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICIT_ALUA)
+ return sprintf(page, "Implicit\n");
+ else if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICIT_ALUA)
+ return sprintf(page, "Explicit\n");
else
return sprintf(page, "None\n");
}
@@ -1714,23 +2135,23 @@ ssize_t core_alua_store_access_type(
unsigned long tmp;
int ret;
- ret = strict_strtoul(page, 0, &tmp);
+ ret = kstrtoul(page, 0, &tmp);
if (ret < 0) {
- printk(KERN_ERR "Unable to extract alua_access_type\n");
- return -EINVAL;
+ pr_err("Unable to extract alua_access_type\n");
+ return ret;
}
if ((tmp != 0) && (tmp != 1) && (tmp != 2) && (tmp != 3)) {
- printk(KERN_ERR "Illegal value for alua_access_type:"
+ pr_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;
+ TPGS_IMPLICIT_ALUA | TPGS_EXPLICIT_ALUA;
else if (tmp == 2)
- tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_EXPLICT_ALUA;
+ tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_EXPLICIT_ALUA;
else if (tmp == 1)
- tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_IMPLICT_ALUA;
+ tg_pt_gp->tg_pt_gp_alua_access_type = TPGS_IMPLICIT_ALUA;
else
tg_pt_gp->tg_pt_gp_alua_access_type = 0;
@@ -1752,13 +2173,13 @@ ssize_t core_alua_store_nonop_delay_msecs(
unsigned long tmp;
int ret;
- ret = strict_strtoul(page, 0, &tmp);
+ ret = kstrtoul(page, 0, &tmp);
if (ret < 0) {
- printk(KERN_ERR "Unable to extract nonop_delay_msecs\n");
- return -EINVAL;
+ pr_err("Unable to extract nonop_delay_msecs\n");
+ return ret;
}
if (tmp > ALUA_MAX_NONOP_DELAY_MSECS) {
- printk(KERN_ERR "Passed nonop_delay_msecs: %lu, exceeds"
+ pr_err("Passed nonop_delay_msecs: %lu, exceeds"
" ALUA_MAX_NONOP_DELAY_MSECS: %d\n", tmp,
ALUA_MAX_NONOP_DELAY_MSECS);
return -EINVAL;
@@ -1783,13 +2204,13 @@ ssize_t core_alua_store_trans_delay_msecs(
unsigned long tmp;
int ret;
- ret = strict_strtoul(page, 0, &tmp);
+ ret = kstrtoul(page, 0, &tmp);
if (ret < 0) {
- printk(KERN_ERR "Unable to extract trans_delay_msecs\n");
- return -EINVAL;
+ pr_err("Unable to extract trans_delay_msecs\n");
+ return ret;
}
if (tmp > ALUA_MAX_TRANS_DELAY_MSECS) {
- printk(KERN_ERR "Passed trans_delay_msecs: %lu, exceeds"
+ pr_err("Passed trans_delay_msecs: %lu, exceeds"
" ALUA_MAX_TRANS_DELAY_MSECS: %d\n", tmp,
ALUA_MAX_TRANS_DELAY_MSECS);
return -EINVAL;
@@ -1799,6 +2220,37 @@ ssize_t core_alua_store_trans_delay_msecs(
return count;
}
+ssize_t core_alua_show_implicit_trans_secs(
+ struct t10_alua_tg_pt_gp *tg_pt_gp,
+ char *page)
+{
+ return sprintf(page, "%d\n", tg_pt_gp->tg_pt_gp_implicit_trans_secs);
+}
+
+ssize_t core_alua_store_implicit_trans_secs(
+ struct t10_alua_tg_pt_gp *tg_pt_gp,
+ const char *page,
+ size_t count)
+{
+ unsigned long tmp;
+ int ret;
+
+ ret = kstrtoul(page, 0, &tmp);
+ if (ret < 0) {
+ pr_err("Unable to extract implicit_trans_secs\n");
+ return ret;
+ }
+ if (tmp > ALUA_MAX_IMPLICIT_TRANS_SECS) {
+ pr_err("Passed implicit_trans_secs: %lu, exceeds"
+ " ALUA_MAX_IMPLICIT_TRANS_SECS: %d\n", tmp,
+ ALUA_MAX_IMPLICIT_TRANS_SECS);
+ return -EINVAL;
+ }
+ tg_pt_gp->tg_pt_gp_implicit_trans_secs = (int)tmp;
+
+ return count;
+}
+
ssize_t core_alua_show_preferred_bit(
struct t10_alua_tg_pt_gp *tg_pt_gp,
char *page)
@@ -1814,13 +2266,13 @@ ssize_t core_alua_store_preferred_bit(
unsigned long tmp;
int ret;
- ret = strict_strtoul(page, 0, &tmp);
+ ret = kstrtoul(page, 0, &tmp);
if (ret < 0) {
- printk(KERN_ERR "Unable to extract preferred ALUA value\n");
- return -EINVAL;
+ pr_err("Unable to extract preferred ALUA value\n");
+ return ret;
}
if ((tmp != 0) && (tmp != 1)) {
- printk(KERN_ERR "Illegal value for preferred ALUA: %lu\n", tmp);
+ pr_err("Illegal value for preferred ALUA: %lu\n", tmp);
return -EINVAL;
}
tg_pt_gp->tg_pt_gp_pref = (int)tmp;
@@ -1830,7 +2282,7 @@ ssize_t core_alua_store_preferred_bit(
ssize_t core_alua_show_offline_bit(struct se_lun *lun, char *page)
{
- if (!(lun->lun_sep))
+ if (!lun->lun_sep)
return -ENODEV;
return sprintf(page, "%d\n",
@@ -1846,22 +2298,22 @@ ssize_t core_alua_store_offline_bit(
unsigned long tmp;
int ret;
- if (!(lun->lun_sep))
+ if (!lun->lun_sep)
return -ENODEV;
- ret = strict_strtoul(page, 0, &tmp);
+ ret = kstrtoul(page, 0, &tmp);
if (ret < 0) {
- printk(KERN_ERR "Unable to extract alua_tg_pt_offline value\n");
- return -EINVAL;
+ pr_err("Unable to extract alua_tg_pt_offline value\n");
+ return ret;
}
if ((tmp != 0) && (tmp != 1)) {
- printk(KERN_ERR "Illegal value for alua_tg_pt_offline: %lu\n",
+ pr_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");
+ if (!tg_pt_gp_mem) {
+ pr_err("Unable to locate *tg_pt_gp_mem\n");
return -EINVAL;
}
@@ -1888,15 +2340,15 @@ ssize_t core_alua_store_secondary_status(
unsigned long tmp;
int ret;
- ret = strict_strtoul(page, 0, &tmp);
+ ret = kstrtoul(page, 0, &tmp);
if (ret < 0) {
- printk(KERN_ERR "Unable to extract alua_tg_pt_status\n");
- return -EINVAL;
+ pr_err("Unable to extract alua_tg_pt_status\n");
+ return ret;
}
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 != ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG) &&
+ (tmp != ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA)) {
+ pr_err("Illegal value for alua_tg_pt_status: %lu\n",
tmp);
return -EINVAL;
}
@@ -1921,13 +2373,13 @@ ssize_t core_alua_store_secondary_write_metadata(
unsigned long tmp;
int ret;
- ret = strict_strtoul(page, 0, &tmp);
+ ret = kstrtoul(page, 0, &tmp);
if (ret < 0) {
- printk(KERN_ERR "Unable to extract alua_tg_pt_write_md\n");
- return -EINVAL;
+ pr_err("Unable to extract alua_tg_pt_write_md\n");
+ return ret;
}
if ((tmp != 0) && (tmp != 1)) {
- printk(KERN_ERR "Illegal value for alua_tg_pt_write_md:"
+ pr_err("Illegal value for alua_tg_pt_write_md:"
" %lu\n", tmp);
return -EINVAL;
}
@@ -1936,55 +2388,28 @@ ssize_t core_alua_store_secondary_write_metadata(
return count;
}
-int core_setup_alua(struct se_device *dev, int force_pt)
+int core_setup_alua(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_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);
+ if (dev->transport->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV &&
+ !(dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE)) {
+ struct t10_alua_lu_gp_member *lu_gp_mem;
+
/*
- * Assoicate this struct se_device with the default ALUA
+ * Associate this struct se_device with the default ALUA
* LUN Group.
*/
lu_gp_mem = core_alua_allocate_lu_gp_mem(dev);
- if (IS_ERR(lu_gp_mem) || !lu_gp_mem)
- return -1;
+ if (IS_ERR(lu_gp_mem))
+ return PTR_ERR(lu_gp_mem);
- 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);
+ default_lu_gp);
spin_unlock(&lu_gp_mem->lu_gp_mem_lock);
- printk(KERN_INFO "%s: Adding to default ALUA LU Group:"
+ pr_debug("%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);
+ dev->transport->name);
}
return 0;
diff --git a/drivers/target/target_core_alua.h b/drivers/target/target_core_alua.h
index c86f97a081e..0a7d65e8040 100644
--- a/drivers/target/target_core_alua.h
+++ b/drivers/target/target_core_alua.h
@@ -7,29 +7,41 @@
* from spc4r17 section 6.4.2 Table 135
*/
#define TPGS_NO_ALUA 0x00
-#define TPGS_IMPLICT_ALUA 0x10
-#define TPGS_EXPLICT_ALUA 0x20
+#define TPGS_IMPLICIT_ALUA 0x10
+#define TPGS_EXPLICIT_ALUA 0x20
/*
* ASYMMETRIC ACCESS STATE field
*
- * from spc4r17 section 6.27 Table 245
+ * from spc4r36j section 6.37 Table 307
*/
-#define ALUA_ACCESS_STATE_ACTIVE_OPTMIZED 0x0
+#define ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED 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_LBA_DEPENDENT 0x4
#define ALUA_ACCESS_STATE_OFFLINE 0xe
#define ALUA_ACCESS_STATE_TRANSITION 0xf
/*
+ * from spc4r36j section 6.37 Table 306
+ */
+#define ALUA_T_SUP 0x80
+#define ALUA_O_SUP 0x40
+#define ALUA_LBD_SUP 0x10
+#define ALUA_U_SUP 0x08
+#define ALUA_S_SUP 0x04
+#define ALUA_AN_SUP 0x02
+#define ALUA_AO_SUP 0x01
+
+/*
* 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
+#define ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG 0x01
+#define ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA 0x02
/*
* From spc4r17, Table D.1: ASC and ASCQ Assignement
@@ -46,12 +58,18 @@
#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
+ * Used for implicit and explicit 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 for the recommended application client implicit transition timeout
+ * in seconds, returned by the REPORT_TARGET_PORT_GROUPS w/ extended header.
+ */
+#define ALUA_DEFAULT_IMPLICIT_TRANS_SECS 0
+#define ALUA_MAX_IMPLICIT_TRANS_SECS 255
+/*
* Used by core_alua_update_tpg_primary_metadata() and
* core_alua_update_tpg_secondary_metadata()
*/
@@ -61,18 +79,30 @@
*/
#define ALUA_SECONDARY_METADATA_WWN_LEN 256
+/* Used by core_alua_update_tpg_(primary,secondary)_metadata */
+#define ALUA_MD_BUF_LEN 1024
+
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 struct kmem_cache *t10_alua_lba_map_cache;
+extern struct kmem_cache *t10_alua_lba_map_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 sense_reason_t target_emulate_report_target_port_groups(struct se_cmd *);
+extern sense_reason_t target_emulate_set_target_port_groups(struct se_cmd *);
+extern sense_reason_t target_emulate_report_referrals(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_lba_map *core_alua_allocate_lba_map(
+ struct list_head *, u64, u64);
+extern int core_alua_allocate_lba_map_mem(struct t10_alua_lba_map *, int, int);
+extern void core_alua_free_lba_map(struct list_head *);
+extern void core_alua_set_lba_map(struct se_device *, struct list_head *,
+ int, 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 *);
@@ -85,7 +115,7 @@ 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);
+ struct se_device *, 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 *);
@@ -107,6 +137,10 @@ 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_implicit_trans_secs(struct t10_alua_tg_pt_gp *,
+ char *);
+extern ssize_t core_alua_store_implicit_trans_secs(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 *,
@@ -121,6 +155,7 @@ 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);
+extern int core_setup_alua(struct se_device *);
+extern sense_reason_t target_alua_state_check(struct se_cmd *cmd);
#endif /* TARGET_CORE_ALUA_H */
diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c
deleted file mode 100644
index 366080baf47..00000000000
--- a/drivers/target/target_core_cdb.c
+++ /dev/null
@@ -1,1131 +0,0 @@
-/*
- * 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
index caf8dc18ee0..bf55c5a04cf 100644
--- a/drivers/target/target_core_configfs.c
+++ b/drivers/target/target_core_configfs.c
@@ -3,8 +3,7 @@
*
* 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
+ * (c) Copyright 2008-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -23,7 +22,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/version.h>
#include <generated/utsrelease.h>
#include <linux/utsname.h>
#include <linux/init.h>
@@ -37,22 +35,25 @@
#include <linux/parser.h>
#include <linux/syscalls.h>
#include <linux/configfs.h>
+#include <linux/spinlock.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_backend.h>
+#include <target/target_core_fabric.h>
#include <target/target_core_fabric_configfs.h>
#include <target/target_core_configfs.h>
#include <target/configfs_macros.h>
+#include "target_core_internal.h"
#include "target_core_alua.h"
-#include "target_core_hba.h"
#include "target_core_pr.h"
#include "target_core_rd.h"
+#include "target_core_xcopy.h"
-static struct list_head g_tf_list;
-static struct mutex g_tf_lock;
+extern struct t10_alua_lu_gp *default_lu_gp;
+
+static LIST_HEAD(g_tf_list);
+static DEFINE_MUTEX(g_tf_lock);
struct target_core_configfs_attribute {
struct configfs_attribute attr;
@@ -60,6 +61,10 @@ struct target_core_configfs_attribute {
ssize_t (*store)(void *, const char *, size_t);
};
+static struct config_group target_core_hbagroup;
+static struct config_group alua_group;
+static struct config_group alua_lu_gps_group;
+
static inline struct se_hba *
item_to_hba(struct config_item *item)
{
@@ -93,12 +98,12 @@ static struct target_fabric_configfs *target_core_get_fabric(
{
struct target_fabric_configfs *tf;
- if (!(name))
+ 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))) {
+ if (!strcmp(tf->tf_name, name)) {
atomic_inc(&tf->tf_access_cnt);
mutex_unlock(&g_tf_lock);
return tf;
@@ -119,17 +124,9 @@ static struct config_group *target_core_register_fabric(
struct target_fabric_configfs *tf;
int ret;
- printk(KERN_INFO "Target_Core_ConfigFS: REGISTER -> group: %p name:"
+ pr_debug("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:
*
@@ -139,7 +136,7 @@ static struct config_group *target_core_register_fabric(
* registered, but simply provids auto loading logic for modules with
* mkdir(2) system calls with known TCM fabric modules.
*/
- if (!(strncmp(name, "iscsi", 5))) {
+ if (!strncmp(name, "iscsi", 5)) {
/*
* Automatically load the LIO Target fabric module when the
* following is called:
@@ -148,11 +145,11 @@ static struct config_group *target_core_register_fabric(
*/
ret = request_module("iscsi_target_mod");
if (ret < 0) {
- printk(KERN_ERR "request_module() failed for"
+ pr_err("request_module() failed for"
" iscsi_target_mod.ko: %d\n", ret);
return ERR_PTR(-EINVAL);
}
- } else if (!(strncmp(name, "loopback", 8))) {
+ } else if (!strncmp(name, "loopback", 8)) {
/*
* Automatically load the tcm_loop fabric module when the
* following is called:
@@ -161,44 +158,44 @@ static struct config_group *target_core_register_fabric(
*/
ret = request_module("tcm_loop");
if (ret < 0) {
- printk(KERN_ERR "request_module() failed for"
+ pr_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",
+ if (!tf) {
+ pr_err("target_core_get_fabric() failed for %s\n",
name);
return ERR_PTR(-EINVAL);
}
- printk(KERN_INFO "Target_Core_ConfigFS: REGISTER -> Located fabric:"
+ pr_debug("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);
+ pr_debug("Target_Core_ConfigFS: REGISTER tfc_wwn_cit -> %p\n",
+ &tf->tf_cit_tmpl.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);
+ &tf->tf_cit_tmpl.tfc_wwn_cit);
config_group_init_type_name(&tf->tf_disc_group, "discovery_auth",
- &TF_CIT_TMPL(tf)->tfc_discovery_cit);
+ &tf->tf_cit_tmpl.tfc_discovery_cit);
- printk(KERN_INFO "Target_Core_ConfigFS: REGISTER -> Allocated Fabric:"
+ pr_debug("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"
+ pr_debug("Target_Core_ConfigFS: REGISTER -> Set tf->tf_fabric"
" for %s\n", name);
return &tf->tf_group;
@@ -217,18 +214,18 @@ static void target_core_deregister_fabric(
struct config_item *df_item;
int i;
- printk(KERN_INFO "Target_Core_ConfigFS: DEREGISTER -> Looking up %s in"
+ pr_debug("Target_Core_ConfigFS: DEREGISTER -> Looking up %s in"
" tf list\n", config_item_name(item));
- printk(KERN_INFO "Target_Core_ConfigFS: DEREGISTER -> located fabric:"
+ pr_debug("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"
+ pr_debug("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"
+ pr_debug("Target_Core_ConfigFS: DEREGISTER -> Releasing ci"
" %s\n", config_item_name(item));
tf_group = &tf->tf_group;
@@ -272,7 +269,7 @@ static struct configfs_subsystem target_core_fabrics = {
},
};
-static struct configfs_subsystem *target_core_subsystem[] = {
+struct configfs_subsystem *target_core_subsystem[] = {
&target_core_fabrics,
NULL,
};
@@ -295,22 +292,18 @@ struct target_fabric_configfs *target_fabric_configfs_init(
{
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;
+ pr_err("Unable to locate passed fabric name\n");
+ return ERR_PTR(-EINVAL);
}
- if (strlen(name) > TARGET_FABRIC_NAME_SIZE) {
- printk(KERN_ERR "Passed name: %s exceeds TARGET_FABRIC"
+ if (strlen(name) >= TARGET_FABRIC_NAME_SIZE) {
+ pr_err("Passed name: %s exceeds TARGET_FABRIC"
"_NAME_SIZE\n", name);
- return NULL;
+ return ERR_PTR(-EINVAL);
}
tf = kzalloc(sizeof(struct target_fabric_configfs), GFP_KERNEL);
- if (!(tf))
+ if (!tf)
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&tf->tf_list);
@@ -329,9 +322,9 @@ struct target_fabric_configfs *target_fabric_configfs_init(
list_add_tail(&tf->tf_list, &g_tf_list);
mutex_unlock(&g_tf_lock);
- printk(KERN_INFO "<<<<<<<<<<<<<<<<<<<<<< BEGIN FABRIC API >>>>>>>>"
+ pr_debug("<<<<<<<<<<<<<<<<<<<<<< BEGIN FABRIC API >>>>>>>>"
">>>>>>>>>>>>>>\n");
- printk(KERN_INFO "Initialized struct target_fabric_configfs: %p for"
+ pr_debug("Initialized struct target_fabric_configfs: %p for"
" %s\n", tf, tf->tf_name);
return tf;
}
@@ -360,144 +353,112 @@ static int target_fabric_tf_ops_check(
{
struct target_core_fabric_ops *tfo = &tf->tf_ops;
- if (!(tfo->get_fabric_name)) {
- printk(KERN_ERR "Missing tfo->get_fabric_name()\n");
+ if (!tfo->get_fabric_name) {
+ pr_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");
+ if (!tfo->get_fabric_proto_ident) {
+ pr_err("Missing tfo->get_fabric_proto_ident()\n");
return -EINVAL;
}
- if (!(tfo->tpg_get_wwn)) {
- printk(KERN_ERR "Missing tfo->tpg_get_wwn()\n");
+ if (!tfo->tpg_get_wwn) {
+ pr_err("Missing tfo->tpg_get_wwn()\n");
return -EINVAL;
}
- if (!(tfo->tpg_get_tag)) {
- printk(KERN_ERR "Missing tfo->tpg_get_tag()\n");
+ if (!tfo->tpg_get_tag) {
+ pr_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");
+ if (!tfo->tpg_get_default_depth) {
+ pr_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");
+ if (!tfo->tpg_get_pr_transport_id) {
+ pr_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");
+ if (!tfo->tpg_get_pr_transport_id_len) {
+ pr_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");
+ if (!tfo->tpg_check_demo_mode) {
+ pr_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");
+ if (!tfo->tpg_check_demo_mode_cache) {
+ pr_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");
+ if (!tfo->tpg_check_demo_mode_write_protect) {
+ pr_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");
+ if (!tfo->tpg_check_prod_mode_write_protect) {
+ pr_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");
+ if (!tfo->tpg_alloc_fabric_acl) {
+ pr_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");
+ if (!tfo->tpg_release_fabric_acl) {
+ pr_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");
+ if (!tfo->tpg_get_inst_index) {
+ pr_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");
+ if (!tfo->release_cmd) {
+ pr_err("Missing tfo->release_cmd()\n");
return -EINVAL;
}
- if (!(tfo->release_cmd_direct)) {
- printk(KERN_ERR "Missing tfo->release_cmd_direct()\n");
+ if (!tfo->shutdown_session) {
+ pr_err("Missing tfo->shutdown_session()\n");
return -EINVAL;
}
- if (!(tfo->shutdown_session)) {
- printk(KERN_ERR "Missing tfo->shutdown_session()\n");
+ if (!tfo->close_session) {
+ pr_err("Missing tfo->close_session()\n");
return -EINVAL;
}
- if (!(tfo->close_session)) {
- printk(KERN_ERR "Missing tfo->close_session()\n");
+ if (!tfo->sess_get_index) {
+ pr_err("Missing tfo->sess_get_index()\n");
return -EINVAL;
}
- if (!(tfo->stop_session)) {
- printk(KERN_ERR "Missing tfo->stop_session()\n");
+ if (!tfo->write_pending) {
+ pr_err("Missing tfo->write_pending()\n");
return -EINVAL;
}
- if (!(tfo->fall_back_to_erl0)) {
- printk(KERN_ERR "Missing tfo->fall_back_to_erl0()\n");
+ if (!tfo->write_pending_status) {
+ pr_err("Missing tfo->write_pending_status()\n");
return -EINVAL;
}
- if (!(tfo->sess_logged_in)) {
- printk(KERN_ERR "Missing tfo->sess_logged_in()\n");
+ if (!tfo->set_default_node_attributes) {
+ pr_err("Missing tfo->set_default_node_attributes()\n");
return -EINVAL;
}
- if (!(tfo->sess_get_index)) {
- printk(KERN_ERR "Missing tfo->sess_get_index()\n");
+ if (!tfo->get_task_tag) {
+ pr_err("Missing tfo->get_task_tag()\n");
return -EINVAL;
}
- if (!(tfo->write_pending)) {
- printk(KERN_ERR "Missing tfo->write_pending()\n");
+ if (!tfo->get_cmd_state) {
+ pr_err("Missing tfo->get_cmd_state()\n");
return -EINVAL;
}
- if (!(tfo->write_pending_status)) {
- printk(KERN_ERR "Missing tfo->write_pending_status()\n");
+ if (!tfo->queue_data_in) {
+ pr_err("Missing tfo->queue_data_in()\n");
return -EINVAL;
}
- if (!(tfo->set_default_node_attributes)) {
- printk(KERN_ERR "Missing tfo->set_default_node_attributes()\n");
+ if (!tfo->queue_status) {
+ pr_err("Missing tfo->queue_status()\n");
return -EINVAL;
}
- if (!(tfo->get_task_tag)) {
- printk(KERN_ERR "Missing tfo->get_task_tag()\n");
+ if (!tfo->queue_tm_rsp) {
+ pr_err("Missing tfo->queue_tm_rsp()\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");
+ if (!tfo->aborted_task) {
+ pr_err("Missing tfo->aborted_task()\n");
return -EINVAL;
}
/*
@@ -505,20 +466,20 @@ static int target_fabric_tf_ops_check(
* 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");
+ if (!tfo->fabric_make_wwn) {
+ pr_err("Missing tfo->fabric_make_wwn()\n");
return -EINVAL;
}
- if (!(tfo->fabric_drop_wwn)) {
- printk(KERN_ERR "Missing tfo->fabric_drop_wwn()\n");
+ if (!tfo->fabric_drop_wwn) {
+ pr_err("Missing tfo->fabric_drop_wwn()\n");
return -EINVAL;
}
- if (!(tfo->fabric_make_tpg)) {
- printk(KERN_ERR "Missing tfo->fabric_make_tpg()\n");
+ if (!tfo->fabric_make_tpg) {
+ pr_err("Missing tfo->fabric_make_tpg()\n");
return -EINVAL;
}
- if (!(tfo->fabric_drop_tpg)) {
- printk(KERN_ERR "Missing tfo->fabric_drop_tpg()\n");
+ if (!tfo->fabric_drop_tpg) {
+ pr_err("Missing tfo->fabric_drop_tpg()\n");
return -EINVAL;
}
@@ -536,22 +497,15 @@ static int target_fabric_tf_ops_check(
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"
+ if (!tf) {
+ pr_err("Unable to locate target_fabric_configfs"
" pointer\n");
return -EINVAL;
}
- su_group = &tf->tf_subsys->su_group;
- if (!(su_group)) {
- printk(KERN_ERR "Unable to locate target struct config_group"
+ if (!tf->tf_subsys) {
+ pr_err("Unable to target struct config_subsystem"
" pointer\n");
return -EINVAL;
}
@@ -559,7 +513,7 @@ int target_fabric_configfs_register(
if (ret < 0)
return ret;
- printk(KERN_INFO "<<<<<<<<<<<<<<<<<<<<<< END FABRIC API >>>>>>>>>>>>"
+ pr_debug("<<<<<<<<<<<<<<<<<<<<<< END FABRIC API >>>>>>>>>>>>"
">>>>>>>>>>\n");
return 0;
}
@@ -568,48 +522,39 @@ 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_"
+ if (!tf) {
+ pr_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"
+ if (!su) {
+ pr_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 >>>>>>>>>>"
+ pr_debug("<<<<<<<<<<<<<<<<<<<<<< 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",
+ pr_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:"
+ pr_debug("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 >>>>>>>>>>>>>>>>>"
+ pr_debug("<<<<<<<<<<<<<<<<<<<<<< END FABRIC API >>>>>>>>>>>>>>>>>"
">>>>>\n");
- return;
}
EXPORT_SYMBOL(target_fabric_configfs_deregister);
@@ -624,20 +569,8 @@ 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; \
+ return snprintf(page, PAGE_SIZE, "%u\n", \
+ (u32)da->da_dev->dev_attrib._name); \
}
#define DEF_DEV_ATTRIB_STORE(_name) \
@@ -646,26 +579,16 @@ static ssize_t target_core_dev_store_attr_##_name( \
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); \
+ ret = kstrtoul(page, 0, &val); \
if (ret < 0) { \
- spin_unlock(&se_dev->se_dev_lock); \
- printk(KERN_ERR "strict_strtoul() failed with" \
+ pr_err("kstrtoul() failed with" \
" ret: %d\n", ret); \
return -EINVAL; \
} \
- ret = se_dev_set_##_name(dev, (u32)val); \
- spin_unlock(&se_dev->se_dev_lock); \
+ ret = se_dev_set_##_name(da->da_dev, (u32)val); \
\
return (!ret) ? count : -EINVAL; \
}
@@ -691,6 +614,9 @@ static struct target_core_dev_attrib_attribute \
__CONFIGFS_EATTR_RO(_name, \
target_core_dev_show_attr_##_name);
+DEF_DEV_ATTRIB(emulate_model_alias);
+SE_DEV_ATTR(emulate_model_alias, S_IRUGO | S_IWUSR);
+
DEF_DEV_ATTRIB(emulate_dpo);
SE_DEV_ATTR(emulate_dpo, S_IRUGO | S_IWUSR);
@@ -715,9 +641,30 @@ 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(emulate_caw);
+SE_DEV_ATTR(emulate_caw, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(emulate_3pc);
+SE_DEV_ATTR(emulate_3pc, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(pi_prot_type);
+SE_DEV_ATTR(pi_prot_type, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB_RO(hw_pi_prot_type);
+SE_DEV_ATTR_RO(hw_pi_prot_type);
+
+DEF_DEV_ATTRIB(pi_prot_format);
+SE_DEV_ATTR(pi_prot_format, S_IRUGO | S_IWUSR);
+
DEF_DEV_ATTRIB(enforce_pr_isids);
SE_DEV_ATTR(enforce_pr_isids, S_IRUGO | S_IWUSR);
+DEF_DEV_ATTRIB(is_nonrot);
+SE_DEV_ATTR(is_nonrot, S_IRUGO | S_IWUSR);
+
+DEF_DEV_ATTRIB(emulate_rest_reord);
+SE_DEV_ATTR(emulate_rest_reord, S_IRUGO | S_IWUSR);
+
DEF_DEV_ATTRIB_RO(hw_block_size);
SE_DEV_ATTR_RO(hw_block_size);
@@ -727,8 +674,8 @@ 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(fabric_max_sectors);
+SE_DEV_ATTR(fabric_max_sectors, S_IRUGO | S_IWUSR);
DEF_DEV_ATTRIB(optimal_sectors);
SE_DEV_ATTR(optimal_sectors, S_IRUGO | S_IWUSR);
@@ -739,9 +686,6 @@ 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);
@@ -754,9 +698,13 @@ 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);
+DEF_DEV_ATTRIB(max_write_same_len);
+SE_DEV_ATTR(max_write_same_len, 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_model_alias.attr,
&target_core_dev_attrib_emulate_dpo.attr,
&target_core_dev_attrib_emulate_fua_write.attr,
&target_core_dev_attrib_emulate_fua_read.attr,
@@ -765,19 +713,26 @@ static struct configfs_attribute *target_core_dev_attrib_attrs[] = {
&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_emulate_caw.attr,
+ &target_core_dev_attrib_emulate_3pc.attr,
+ &target_core_dev_attrib_pi_prot_type.attr,
+ &target_core_dev_attrib_hw_pi_prot_type.attr,
+ &target_core_dev_attrib_pi_prot_format.attr,
&target_core_dev_attrib_enforce_pr_isids.attr,
+ &target_core_dev_attrib_is_nonrot.attr,
+ &target_core_dev_attrib_emulate_rest_reord.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_fabric_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,
+ &target_core_dev_attrib_max_write_same_len.attr,
NULL,
};
@@ -818,13 +773,6 @@ 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]);
}
@@ -834,8 +782,7 @@ static ssize_t target_core_dev_wwn_store_attr_vpd_unit_serial(
const char *page,
size_t count)
{
- struct se_subsystem_dev *su_dev = t10_wwn->t10_sub_dev;
- struct se_device *dev;
+ struct se_device *dev = t10_wwn->t10_dev;
unsigned char buf[INQUIRY_VPD_SERIAL_LEN];
/*
@@ -848,14 +795,14 @@ static ssize_t target_core_dev_wwn_store_attr_vpd_unit_serial(
* 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"
+ if (dev->dev_flags & DF_FIRMWARE_VPD_UNIT_SERIAL) {
+ pr_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"
+ if (strlen(page) >= INQUIRY_VPD_SERIAL_LEN) {
+ pr_err("Emulated VPD Unit Serial exceeds"
" INQUIRY_VPD_SERIAL_LEN: %d\n", INQUIRY_VPD_SERIAL_LEN);
return -EOVERFLOW;
}
@@ -865,15 +812,13 @@ static ssize_t target_core_dev_wwn_store_attr_vpd_unit_serial(
* (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;
- }
+ if (dev->export_count) {
+ pr_err("Unable to set VPD Unit Serial while"
+ " active %d $FABRIC_MOD exports exist\n",
+ dev->export_count);
+ return -EINVAL;
}
+
/*
* This currently assumes ASCII encoding for emulated VPD Unit Serial.
*
@@ -882,12 +827,12 @@ static ssize_t target_core_dev_wwn_store_attr_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,
+ snprintf(dev->t10_wwn.unit_serial, INQUIRY_VPD_SERIAL_LEN,
"%s", strstrip(buf));
- su_dev->su_dev_flags |= SDF_EMULATED_VPD_UNIT_SERIAL;
+ dev->dev_flags |= DF_EMULATED_VPD_UNIT_SERIAL;
- printk(KERN_INFO "Target_Core_ConfigFS: Set emulated VPD Unit Serial:"
- " %s\n", su_dev->t10_wwn.unit_serial);
+ pr_debug("Target_Core_ConfigFS: Set emulated VPD Unit Serial:"
+ " %s\n", dev->t10_wwn.unit_serial);
return count;
}
@@ -901,26 +846,20 @@ 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))
+ if (!vpd->protocol_identifier_set)
continue;
transport_dump_vpd_proto_id(vpd, buf, VPD_TMP_BUF_SIZE);
- if ((len + strlen(buf) > PAGE_SIZE))
+ if (len + strlen(buf) >= PAGE_SIZE)
break;
len += sprintf(page+len, "%s", buf);
@@ -948,16 +887,10 @@ 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) \
@@ -965,19 +898,19 @@ static ssize_t target_core_dev_wwn_show_attr_##_name( \
\
memset(buf, 0, VPD_TMP_BUF_SIZE); \
transport_dump_vpd_assoc(vpd, buf, VPD_TMP_BUF_SIZE); \
- if ((len + strlen(buf) > PAGE_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)) \
+ 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)) \
+ if (len + strlen(buf) >= PAGE_SIZE) \
break; \
len += sprintf(page+len, "%s", buf); \
} \
@@ -987,7 +920,7 @@ static ssize_t target_core_dev_wwn_show_attr_##_name( \
}
/*
- * VPD page 0x83 Assoication: Logical Unit
+ * VPD page 0x83 Association: Logical Unit
*/
DEF_DEV_WWN_ASSOC_SHOW(vpd_assoc_logical_unit, 0x00);
@@ -1057,7 +990,7 @@ static 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);
+CONFIGFS_EATTR_STRUCT(target_core_dev_pr, se_device);
#define SE_DEV_PR_ATTR(_name, _mode) \
static struct target_core_dev_pr_attribute target_core_dev_pr_##_name = \
__CONFIGFS_EATTR(_name, _mode, \
@@ -1069,149 +1002,90 @@ 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)
+static ssize_t target_core_dev_pr_show_spc3_res(struct se_device *dev,
+ char *page)
{
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);
+ if (!pr_reg)
+ return sprintf(page, "No SPC-3 Reservation holder\n");
- *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);
+ se_nacl = pr_reg->pr_reg_nacl;
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
- return *len;
+ return sprintf(page, "SPC-3 Reservation: %s Initiator: %s%s\n",
+ se_nacl->se_tpg->se_tpg_tfo->get_fabric_name(),
+ se_nacl->initiatorname, i_buf);
}
-static ssize_t target_core_dev_pr_show_spc2_res(
- struct se_device *dev,
- char *page,
- ssize_t *len)
+static ssize_t target_core_dev_pr_show_spc2_res(struct se_device *dev,
+ char *page)
{
struct se_node_acl *se_nacl;
+ ssize_t len;
- 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;
+ if (se_nacl) {
+ len = sprintf(page,
+ "SPC-2 Reservation: %s Initiator: %s\n",
+ se_nacl->se_tpg->se_tpg_tfo->get_fabric_name(),
+ se_nacl->initiatorname);
+ } else {
+ len = sprintf(page, "No SPC-2 Reservation holder\n");
+ }
+ return len;
}
-static ssize_t target_core_dev_pr_show_attr_res_holder(
- struct se_subsystem_dev *su_dev,
- char *page)
+static ssize_t target_core_dev_pr_show_attr_res_holder(struct se_device *dev,
+ char *page)
{
- ssize_t len = 0;
-
- if (!(su_dev->se_dev_ptr))
- return -ENODEV;
+ int ret;
- 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;
- }
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ return sprintf(page, "Passthrough\n");
- return len;
+ spin_lock(&dev->dev_reservation_lock);
+ if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
+ ret = target_core_dev_pr_show_spc2_res(dev, page);
+ else
+ ret = target_core_dev_pr_show_spc3_res(dev, page);
+ spin_unlock(&dev->dev_reservation_lock);
+ return ret;
}
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, 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)) {
+ if (!dev->dev_pr_res_holder) {
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)
+ } else if (dev->dev_pr_res_holder->pr_reg_all_tg_pt) {
len = sprintf(page, "SPC-3 Reservation: All Target"
" Ports registration\n");
- else
+ } else {
len = sprintf(page, "SPC-3 Reservation: Single"
" Target Port registration\n");
- spin_unlock(&dev->dev_reservation_lock);
+ }
+ 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)
+ struct se_device *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);
+ return sprintf(page, "0x%08x\n", dev->t10_pr.pr_generation);
}
SE_DEV_PR_ATTR_RO(res_pr_generation);
@@ -1220,10 +1094,8 @@ 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, char *page)
{
- struct se_device *dev;
struct se_node_acl *se_nacl;
struct se_lun *lun;
struct se_portal_group *se_tpg;
@@ -1231,86 +1103,69 @@ static ssize_t target_core_dev_pr_show_attr_res_pr_holder_tg_port(
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)) {
+ if (!pr_reg) {
len = sprintf(page, "No SPC-3 Reservation holder\n");
- spin_unlock(&dev->dev_reservation_lock);
- return len;
+ goto out_unlock;
}
+
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);
+ tfo = se_tpg->se_tpg_tfo;
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"
+ " Identifier 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);
+out_unlock:
+ 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 se_device *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;
+ int reg_count = 0;
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,
+ spin_lock(&dev->t10_pr.registration_lock);
+ list_for_each_entry(pr_reg, &dev->t10_pr.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],
+ core_pr_dump_initiator_port(pr_reg, i_buf,
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_reg_nacl->initiatorname, i_buf, pr_reg->pr_res_key,
pr_reg->pr_res_generation);
- if ((len + strlen(buf) > PAGE_SIZE))
+ if (len + strlen(buf) >= PAGE_SIZE)
break;
len += sprintf(page+len, "%s", buf);
reg_count++;
}
- spin_unlock(&T10_RES(su_dev)->registration_lock);
+ spin_unlock(&dev->t10_pr.registration_lock);
- if (!(reg_count))
+ if (!reg_count)
len += sprintf(page+len, "None\n");
return len;
@@ -1318,88 +1173,48 @@ static ssize_t target_core_dev_pr_show_attr_res_pr_registered_i_pts(
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, 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)) {
+ if (pr_reg) {
+ len = sprintf(page, "SPC-3 Reservation Type: %s\n",
+ core_scsi3_pr_dump_type(pr_reg->pr_res_type));
+ } else {
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);
+ 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)
+ struct se_device *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;
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ return sprintf(page, "SPC_PASSTHROUGH\n");
+ else if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
+ return sprintf(page, "SPC2_RESERVATIONS\n");
+ else
+ return sprintf(page, "SPC3_PERSISTENT_RESERVATIONS\n");
}
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)
+ struct se_device *dev, char *page)
{
- if (!(su_dev->se_dev_ptr))
- return -ENODEV;
-
- if (T10_RES(su_dev)->res_type != SPC3_PERSISTENT_RESERVATIONS)
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
return 0;
return sprintf(page, "APTPL Bit Status: %s\n",
- (T10_RES(su_dev)->pr_aptpl_active) ? "Activated" : "Disabled");
+ (dev->t10_pr.pr_aptpl_active) ? "Activated" : "Disabled");
}
SE_DEV_PR_ATTR_RO(res_aptpl_active);
@@ -1408,13 +1223,9 @@ 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)
+ struct se_device *dev, char *page)
{
- if (!(su_dev->se_dev_ptr))
- return -ENODEV;
-
- if (T10_RES(su_dev)->res_type != SPC3_PERSISTENT_RESERVATIONS)
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
return 0;
return sprintf(page, "Ready to process PR APTPL metadata..\n");
@@ -1446,13 +1257,12 @@ static match_table_t tokens = {
};
static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
- struct se_subsystem_dev *su_dev,
+ struct se_device *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;
+ unsigned char *i_fabric = NULL, *i_port = NULL, *isid = NULL;
+ unsigned char *t_fabric = NULL, *t_port = NULL;
char *orig, *ptr, *arg_p, *opts;
substring_t args[MAX_OPT_ARGS];
unsigned long long tmp_ll;
@@ -1462,15 +1272,13 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
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)
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ return 0;
+ if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
return 0;
- if (atomic_read(&dev->dev_export_obj.obj_access_count)) {
- printk(KERN_INFO "Unable to process APTPL metadata while"
+ if (dev->export_count) {
+ pr_debug("Unable to process APTPL metadata while"
" active fabric exports exist\n");
return -EINVAL;
}
@@ -1480,7 +1288,7 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
return -ENOMEM;
orig = opts;
- while ((ptr = strsep(&opts, ",")) != NULL) {
+ while ((ptr = strsep(&opts, ",\n")) != NULL) {
if (!*ptr)
continue;
@@ -1488,11 +1296,19 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
switch (token) {
case Opt_initiator_fabric:
i_fabric = match_strdup(&args[0]);
+ if (!i_fabric) {
+ ret = -ENOMEM;
+ goto out;
+ }
break;
case Opt_initiator_node:
i_port = match_strdup(&args[0]);
- if (strlen(i_port) > PR_APTPL_MAX_IPORT_LEN) {
- printk(KERN_ERR "APTPL metadata initiator_node="
+ if (!i_port) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if (strlen(i_port) >= PR_APTPL_MAX_IPORT_LEN) {
+ pr_err("APTPL metadata initiator_node="
" exceeds PR_APTPL_MAX_IPORT_LEN: %d\n",
PR_APTPL_MAX_IPORT_LEN);
ret = -EINVAL;
@@ -1501,8 +1317,12 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
break;
case Opt_initiator_sid:
isid = match_strdup(&args[0]);
- if (strlen(isid) > PR_REG_ISID_LEN) {
- printk(KERN_ERR "APTPL metadata initiator_isid"
+ if (!isid) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if (strlen(isid) >= PR_REG_ISID_LEN) {
+ pr_err("APTPL metadata initiator_isid"
"= exceeds PR_REG_ISID_LEN: %d\n",
PR_REG_ISID_LEN);
ret = -EINVAL;
@@ -1511,9 +1331,13 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
break;
case Opt_sa_res_key:
arg_p = match_strdup(&args[0]);
- ret = strict_strtoull(arg_p, 0, &tmp_ll);
+ if (!arg_p) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = kstrtoull(arg_p, 0, &tmp_ll);
if (ret < 0) {
- printk(KERN_ERR "strict_strtoull() failed for"
+ pr_err("kstrtoull() failed for"
" sa_res_key=\n");
goto out;
}
@@ -1547,11 +1371,19 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
*/
case Opt_target_fabric:
t_fabric = match_strdup(&args[0]);
+ if (!t_fabric) {
+ ret = -ENOMEM;
+ goto out;
+ }
break;
case Opt_target_node:
t_port = match_strdup(&args[0]);
- if (strlen(t_port) > PR_APTPL_MAX_TPORT_LEN) {
- printk(KERN_ERR "APTPL metadata target_node="
+ if (!t_port) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if (strlen(t_port) >= PR_APTPL_MAX_TPORT_LEN) {
+ pr_err("APTPL metadata target_node="
" exceeds PR_APTPL_MAX_TPORT_LEN: %d\n",
PR_APTPL_MAX_TPORT_LEN);
ret = -EINVAL;
@@ -1575,30 +1407,35 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
}
}
- if (!(i_port) || !(t_port) || !(sa_res_key)) {
- printk(KERN_ERR "Illegal parameters for APTPL registration\n");
+ if (!i_port || !t_port || !sa_res_key) {
+ pr_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"
+ pr_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,
+ ret = core_scsi3_alloc_aptpl_registration(&dev->t10_pr, sa_res_key,
i_port, isid, mapped_lun, t_port, tpgt, target_lun,
res_holder, all_tg_pt, type);
out:
+ kfree(i_fabric);
+ kfree(i_port);
+ kfree(isid);
+ kfree(t_fabric);
+ kfree(t_port);
kfree(orig);
return (ret == 0) ? count : ret;
}
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);
+CONFIGFS_EATTR_OPS(target_core_dev_pr, se_device, dev_pr_group);
static struct configfs_attribute *target_core_dev_pr_attrs[] = {
&target_core_dev_pr_res_holder.attr,
@@ -1630,18 +1467,14 @@ static struct config_item_type target_core_dev_pr_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;
+ struct se_device *dev = p;
+ struct se_subsystem_api *t = dev->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);
+ transport_dump_dev_state(dev, page, &bl);
read_bytes += bl;
- read_bytes += t->show_configfs_dev_params(hba, se_dev, page+read_bytes);
+ read_bytes += t->show_configfs_dev_params(dev, page+read_bytes);
return read_bytes;
}
@@ -1658,17 +1491,10 @@ static ssize_t target_core_store_dev_control(
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;
- }
+ struct se_device *dev = p;
+ struct se_subsystem_api *t = dev->transport;
- return t->set_configfs_dev_params(hba, se_dev, page, count);
+ return t->set_configfs_dev_params(dev, page, count);
}
static struct target_core_configfs_attribute target_core_attr_dev_control = {
@@ -1681,12 +1507,12 @@ static struct target_core_configfs_attribute target_core_attr_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;
+ struct se_device *dev = p;
- if (!(se_dev->su_dev_flags & SDF_USING_ALIAS))
+ if (!(dev->dev_flags & DF_USING_ALIAS))
return 0;
- return snprintf(page, PAGE_SIZE, "%s\n", se_dev->se_dev_alias);
+ return snprintf(page, PAGE_SIZE, "%s\n", dev->dev_alias);
}
static ssize_t target_core_store_dev_alias(
@@ -1694,25 +1520,29 @@ static ssize_t target_core_store_dev_alias(
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_device *dev = p;
+ struct se_hba *hba = dev->se_hba;
ssize_t read_bytes;
if (count > (SE_DEV_ALIAS_LEN-1)) {
- printk(KERN_ERR "alias count: %d exceeds"
+ pr_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);
+ read_bytes = snprintf(&dev->dev_alias[0], SE_DEV_ALIAS_LEN, "%s", page);
+ if (!read_bytes)
+ return -EINVAL;
+ if (dev->dev_alias[read_bytes - 1] == '\n')
+ dev->dev_alias[read_bytes - 1] = '\0';
- printk(KERN_INFO "Target_Core_ConfigFS: %s/%s set alias: %s\n",
+ dev->dev_flags |= DF_USING_ALIAS;
+
+ pr_debug("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);
+ config_item_name(&dev->dev_group.cg_item),
+ dev->dev_alias);
return read_bytes;
}
@@ -1727,12 +1557,12 @@ static struct target_core_configfs_attribute target_core_attr_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;
+ struct se_device *dev = p;
- if (!(se_dev->su_dev_flags & SDF_USING_UDEV_PATH))
+ if (!(dev->dev_flags & DF_USING_UDEV_PATH))
return 0;
- return snprintf(page, PAGE_SIZE, "%s\n", se_dev->se_dev_udev_path);
+ return snprintf(page, PAGE_SIZE, "%s\n", dev->udev_path);
}
static ssize_t target_core_store_dev_udev_path(
@@ -1740,25 +1570,30 @@ static ssize_t target_core_store_dev_udev_path(
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_device *dev = p;
+ struct se_hba *hba = dev->se_hba;
ssize_t read_bytes;
if (count > (SE_UDEV_PATH_LEN-1)) {
- printk(KERN_ERR "udev_path count: %d exceeds"
+ pr_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,
+ read_bytes = snprintf(&dev->udev_path[0], SE_UDEV_PATH_LEN,
"%s", page);
+ if (!read_bytes)
+ return -EINVAL;
+ if (dev->udev_path[read_bytes - 1] == '\n')
+ dev->udev_path[read_bytes - 1] = '\0';
+
+ dev->dev_flags |= DF_USING_UDEV_PATH;
- printk(KERN_INFO "Target_Core_ConfigFS: %s/%s set udev_path: %s\n",
+ pr_debug("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);
+ config_item_name(&dev->dev_group.cg_item),
+ dev->udev_path);
return read_bytes;
}
@@ -1771,77 +1606,58 @@ static struct target_core_configfs_attribute target_core_attr_dev_udev_path = {
.store = target_core_store_dev_udev_path,
};
+static ssize_t target_core_show_dev_enable(void *p, char *page)
+{
+ struct se_device *dev = p;
+
+ return snprintf(page, PAGE_SIZE, "%d\n", !!(dev->dev_flags & DF_CONFIGURED));
+}
+
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;
+ struct se_device *dev = p;
char *ptr;
+ int ret;
ptr = strstr(page, "1");
- if (!(ptr)) {
- printk(KERN_ERR "For dev_enable ops, only valid value"
+ if (!ptr) {
+ pr_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);
+ ret = target_configure_device(dev);
+ if (ret)
+ return ret;
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,
+ .ca_mode = S_IRUGO | S_IWUSR },
+ .show = target_core_show_dev_enable,
.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 se_device *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;
- }
+ if (!lu_gp_mem)
+ return 0;
spin_lock(&lu_gp_mem->lu_gp_mem_lock);
lu_gp = lu_gp_mem->lu_gp;
- if ((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);
@@ -1856,26 +1672,19 @@ static ssize_t target_core_store_alua_lu_gp(
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 se_device *dev = p;
+ struct se_hba *hba = dev->se_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;
+ lu_gp_mem = dev->dev_alua_lu_gp_mem;
+ if (!lu_gp_mem)
+ return 0;
- 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");
+ pr_err("ALUA LU Group Alias too large!\n");
return -EINVAL;
}
memset(buf, 0, LU_GROUP_NAME_BUF);
@@ -1891,31 +1700,23 @@ static ssize_t target_core_store_alua_lu_gp(
* core_alua_get_lu_gp_by_name below().
*/
lu_gp_new = core_alua_get_lu_gp_by_name(strstrip(buf));
- if (!(lu_gp_new))
+ 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)) {
+ 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"
+ if (!lu_gp_new) {
+ pr_debug("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(&dev->dev_group.cg_item),
config_item_name(&lu_gp->lu_gp_group.cg_item),
lu_gp->lu_gp_id);
@@ -1936,11 +1737,11 @@ static ssize_t target_core_store_alua_lu_gp(
__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:"
+ pr_debug("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(&dev->dev_group.cg_item),
config_item_name(&lu_gp_new->lu_gp_group.cg_item),
lu_gp_new->lu_gp_id);
@@ -1956,6 +1757,176 @@ static struct target_core_configfs_attribute target_core_attr_dev_alua_lu_gp = {
.store = target_core_store_alua_lu_gp,
};
+static ssize_t target_core_show_dev_lba_map(void *p, char *page)
+{
+ struct se_device *dev = p;
+ struct t10_alua_lba_map *map;
+ struct t10_alua_lba_map_member *mem;
+ char *b = page;
+ int bl = 0;
+ char state;
+
+ spin_lock(&dev->t10_alua.lba_map_lock);
+ if (!list_empty(&dev->t10_alua.lba_map_list))
+ bl += sprintf(b + bl, "%u %u\n",
+ dev->t10_alua.lba_map_segment_size,
+ dev->t10_alua.lba_map_segment_multiplier);
+ list_for_each_entry(map, &dev->t10_alua.lba_map_list, lba_map_list) {
+ bl += sprintf(b + bl, "%llu %llu",
+ map->lba_map_first_lba, map->lba_map_last_lba);
+ list_for_each_entry(mem, &map->lba_map_mem_list,
+ lba_map_mem_list) {
+ switch (mem->lba_map_mem_alua_state) {
+ case ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED:
+ state = 'O';
+ break;
+ case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED:
+ state = 'A';
+ break;
+ case ALUA_ACCESS_STATE_STANDBY:
+ state = 'S';
+ break;
+ case ALUA_ACCESS_STATE_UNAVAILABLE:
+ state = 'U';
+ break;
+ default:
+ state = '.';
+ break;
+ }
+ bl += sprintf(b + bl, " %d:%c",
+ mem->lba_map_mem_alua_pg_id, state);
+ }
+ bl += sprintf(b + bl, "\n");
+ }
+ spin_unlock(&dev->t10_alua.lba_map_lock);
+ return bl;
+}
+
+static ssize_t target_core_store_dev_lba_map(
+ void *p,
+ const char *page,
+ size_t count)
+{
+ struct se_device *dev = p;
+ struct t10_alua_lba_map *lba_map = NULL;
+ struct list_head lba_list;
+ char *map_entries, *ptr;
+ char state;
+ int pg_num = -1, pg;
+ int ret = 0, num = 0, pg_id, alua_state;
+ unsigned long start_lba = -1, end_lba = -1;
+ unsigned long segment_size = -1, segment_mult = -1;
+
+ map_entries = kstrdup(page, GFP_KERNEL);
+ if (!map_entries)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&lba_list);
+ while ((ptr = strsep(&map_entries, "\n")) != NULL) {
+ if (!*ptr)
+ continue;
+
+ if (num == 0) {
+ if (sscanf(ptr, "%lu %lu\n",
+ &segment_size, &segment_mult) != 2) {
+ pr_err("Invalid line %d\n", num);
+ ret = -EINVAL;
+ break;
+ }
+ num++;
+ continue;
+ }
+ if (sscanf(ptr, "%lu %lu", &start_lba, &end_lba) != 2) {
+ pr_err("Invalid line %d\n", num);
+ ret = -EINVAL;
+ break;
+ }
+ ptr = strchr(ptr, ' ');
+ if (!ptr) {
+ pr_err("Invalid line %d, missing end lba\n", num);
+ ret = -EINVAL;
+ break;
+ }
+ ptr++;
+ ptr = strchr(ptr, ' ');
+ if (!ptr) {
+ pr_err("Invalid line %d, missing state definitions\n",
+ num);
+ ret = -EINVAL;
+ break;
+ }
+ ptr++;
+ lba_map = core_alua_allocate_lba_map(&lba_list,
+ start_lba, end_lba);
+ if (IS_ERR(lba_map)) {
+ ret = PTR_ERR(lba_map);
+ break;
+ }
+ pg = 0;
+ while (sscanf(ptr, "%d:%c", &pg_id, &state) == 2) {
+ switch (state) {
+ case 'O':
+ alua_state = ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED;
+ break;
+ case 'A':
+ alua_state = ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED;
+ break;
+ case 'S':
+ alua_state = ALUA_ACCESS_STATE_STANDBY;
+ break;
+ case 'U':
+ alua_state = ALUA_ACCESS_STATE_UNAVAILABLE;
+ break;
+ default:
+ pr_err("Invalid ALUA state '%c'\n", state);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = core_alua_allocate_lba_map_mem(lba_map,
+ pg_id, alua_state);
+ if (ret) {
+ pr_err("Invalid target descriptor %d:%c "
+ "at line %d\n",
+ pg_id, state, num);
+ break;
+ }
+ pg++;
+ ptr = strchr(ptr, ' ');
+ if (ptr)
+ ptr++;
+ else
+ break;
+ }
+ if (pg_num == -1)
+ pg_num = pg;
+ else if (pg != pg_num) {
+ pr_err("Only %d from %d port groups definitions "
+ "at line %d\n", pg, pg_num, num);
+ ret = -EINVAL;
+ break;
+ }
+ num++;
+ }
+out:
+ if (ret) {
+ core_alua_free_lba_map(&lba_list);
+ count = ret;
+ } else
+ core_alua_set_lba_map(dev, &lba_list,
+ segment_size, segment_mult);
+ kfree(map_entries);
+ return count;
+}
+
+static struct target_core_configfs_attribute target_core_attr_dev_lba_map = {
+ .attr = { .ca_owner = THIS_MODULE,
+ .ca_name = "lba_map",
+ .ca_mode = S_IRUGO | S_IWUSR },
+ .show = target_core_show_dev_lba_map,
+ .store = target_core_store_dev_lba_map,
+};
+
static struct configfs_attribute *lio_core_dev_attrs[] = {
&target_core_attr_dev_info.attr,
&target_core_attr_dev_control.attr,
@@ -1963,74 +1934,50 @@ static struct configfs_attribute *lio_core_dev_attrs[] = {
&target_core_attr_dev_udev_path.attr,
&target_core_attr_dev_enable.attr,
&target_core_attr_dev_alua_lu_gp.attr,
+ &target_core_attr_dev_lba_map.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 se_hba *hba = item_to_hba(&se_dev->se_dev_hba->hba_group.cg_item);
- struct se_subsystem_api *t = hba->transport;
- struct config_group *dev_cg = &se_dev->se_dev_group;
+ struct config_group *dev_cg = to_config_group(item);
+ struct se_device *dev =
+ container_of(dev_cg, struct se_device, dev_group);
kfree(dev_cg->default_groups);
- /*
- * 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);
-
- se_free_virtual_device(se_dev->se_dev_ptr, hba);
- } 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);
- kfree(se_dev);
+ target_free_device(dev);
}
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 config_group *dev_cg = to_config_group(item);
+ struct se_device *dev =
+ container_of(dev_cg, struct se_device, dev_group);
struct target_core_configfs_attribute *tc_attr = container_of(
attr, struct target_core_configfs_attribute, attr);
- if (!(tc_attr->show))
+ if (!tc_attr->show)
return -EINVAL;
- return tc_attr->show((void *)se_dev, page);
+ return tc_attr->show(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 config_group *dev_cg = to_config_group(item);
+ struct se_device *dev =
+ container_of(dev_cg, struct se_device, dev_group);
struct target_core_configfs_attribute *tc_attr = container_of(
attr, struct target_core_configfs_attribute, attr);
- if (!(tc_attr->store))
+ if (!tc_attr->store)
return -EINVAL;
- return tc_attr->store((void *)se_dev, page, count);
+ return tc_attr->store(dev, page, count);
}
static struct configfs_item_operations target_core_dev_item_ops = {
@@ -2070,7 +2017,7 @@ 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))
+ if (!lu_gp->lu_gp_valid_id)
return 0;
return sprintf(page, "%hu\n", lu_gp->lu_gp_id);
@@ -2085,14 +2032,14 @@ static ssize_t target_core_alua_lu_gp_store_attr_lu_gp_id(
unsigned long lu_gp_id;
int ret;
- ret = strict_strtoul(page, 0, &lu_gp_id);
+ ret = kstrtoul(page, 0, &lu_gp_id);
if (ret < 0) {
- printk(KERN_ERR "strict_strtoul() returned %d for"
+ pr_err("kstrtoul() returned %d for"
" lu_gp_id\n", ret);
- return -EINVAL;
+ return ret;
}
if (lu_gp_id > 0x0000ffff) {
- printk(KERN_ERR "ALUA lu_gp_id: %lu exceeds maximum:"
+ pr_err("ALUA lu_gp_id: %lu exceeds maximum:"
" 0x0000ffff\n", lu_gp_id);
return -EINVAL;
}
@@ -2101,7 +2048,7 @@ static ssize_t target_core_alua_lu_gp_store_attr_lu_gp_id(
if (ret < 0)
return -EINVAL;
- printk(KERN_INFO "Target_Core_ConfigFS: Set ALUA Logical Unit"
+ pr_debug("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);
@@ -2120,7 +2067,6 @@ static ssize_t target_core_alua_lu_gp_show_attr_members(
{
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];
@@ -2130,16 +2076,15 @@ static ssize_t target_core_alua_lu_gp_show_attr_members(
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;
+ hba = dev->se_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));
+ config_item_name(&dev->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"
+ pr_warn("Ran out of lu_gp_show_attr"
"_members buffer\n");
break;
}
@@ -2203,7 +2148,7 @@ static struct config_group *target_core_alua_create_lu_gp(
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"
+ pr_debug("Target_Core_ConfigFS: Allocated ALUA Logical Unit"
" Group: core/alua/lu_gps/%s\n",
config_item_name(alua_lu_gp_ci));
@@ -2218,7 +2163,7 @@ static void target_core_alua_drop_lu_gp(
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"
+ pr_debug("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);
/*
@@ -2273,31 +2218,43 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_access_state(
const char *page,
size_t count)
{
- struct se_subsystem_dev *su_dev = tg_pt_gp->tg_pt_gp_su_dev;
+ struct se_device *dev = tg_pt_gp->tg_pt_gp_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"
+ if (!tg_pt_gp->tg_pt_gp_valid_id) {
+ pr_err("Unable to do implicit ALUA on non valid"
" tg_pt_gp ID: %hu\n", tg_pt_gp->tg_pt_gp_valid_id);
return -EINVAL;
}
+ if (!(dev->dev_flags & DF_CONFIGURED)) {
+ pr_err("Unable to set alua_access_state while device is"
+ " not configured\n");
+ return -ENODEV;
+ }
- ret = strict_strtoul(page, 0, &tmp);
+ ret = kstrtoul(page, 0, &tmp);
if (ret < 0) {
- printk("Unable to extract new ALUA access state from"
+ pr_err("Unable to extract new ALUA access state from"
" %s\n", page);
- return -EINVAL;
+ return ret;
}
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");
+ if (!(tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICIT_ALUA)) {
+ pr_err("Unable to process implicit configfs ALUA"
+ " transition while TPGS_IMPLICIT_ALUA is disabled\n");
+ return -EINVAL;
+ }
+ if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICIT_ALUA &&
+ new_state == ALUA_ACCESS_STATE_LBA_DEPENDENT) {
+ /* LBA DEPENDENT is only allowed with implicit ALUA */
+ pr_err("Unable to process implicit configfs ALUA transition"
+ " while explicit ALUA management is enabled\n");
return -EINVAL;
}
- ret = core_alua_do_port_transition(tg_pt_gp, su_dev->se_dev_ptr,
+ ret = core_alua_do_port_transition(tg_pt_gp, dev,
NULL, NULL, new_state, 0);
return (!ret) ? count : -EINVAL;
}
@@ -2323,25 +2280,25 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_access_status(
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"
+ if (!tg_pt_gp->tg_pt_gp_valid_id) {
+ pr_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);
+ ret = kstrtoul(page, 0, &tmp);
if (ret < 0) {
- printk(KERN_ERR "Unable to extract new ALUA access status"
+ pr_err("Unable to extract new ALUA access status"
" from %s\n", page);
- return -EINVAL;
+ return ret;
}
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 != ALUA_STATUS_ALTERED_BY_EXPLICIT_STPG) &&
+ (new_status != ALUA_STATUS_ALTERED_BY_IMPLICIT_ALUA)) {
+ pr_err("Illegal ALUA access status: 0x%02x\n",
new_status);
return -EINVAL;
}
@@ -2373,6 +2330,90 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_access_type(
SE_DEV_ALUA_TG_PT_ATTR(alua_access_type, S_IRUGO | S_IWUSR);
/*
+ * alua_supported_states
+ */
+
+#define SE_DEV_ALUA_SUPPORT_STATE_SHOW(_name, _var, _bit) \
+static ssize_t target_core_alua_tg_pt_gp_show_attr_alua_support_##_name( \
+ struct t10_alua_tg_pt_gp *t, char *p) \
+{ \
+ return sprintf(p, "%d\n", !!(t->_var & _bit)); \
+}
+
+#define SE_DEV_ALUA_SUPPORT_STATE_STORE(_name, _var, _bit) \
+static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_support_##_name(\
+ struct t10_alua_tg_pt_gp *t, const char *p, size_t c) \
+{ \
+ unsigned long tmp; \
+ int ret; \
+ \
+ if (!t->tg_pt_gp_valid_id) { \
+ pr_err("Unable to do set ##_name ALUA state on non" \
+ " valid tg_pt_gp ID: %hu\n", \
+ t->tg_pt_gp_valid_id); \
+ return -EINVAL; \
+ } \
+ \
+ ret = kstrtoul(p, 0, &tmp); \
+ if (ret < 0) { \
+ pr_err("Invalid value '%s', must be '0' or '1'\n", p); \
+ return -EINVAL; \
+ } \
+ if (tmp > 1) { \
+ pr_err("Invalid value '%ld', must be '0' or '1'\n", tmp); \
+ return -EINVAL; \
+ } \
+ if (!tmp) \
+ t->_var |= _bit; \
+ else \
+ t->_var &= ~_bit; \
+ \
+ return c; \
+}
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(transitioning,
+ tg_pt_gp_alua_supported_states, ALUA_T_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(transitioning,
+ tg_pt_gp_alua_supported_states, ALUA_T_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_transitioning, S_IRUGO | S_IWUSR);
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(offline,
+ tg_pt_gp_alua_supported_states, ALUA_O_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(offline,
+ tg_pt_gp_alua_supported_states, ALUA_O_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_offline, S_IRUGO | S_IWUSR);
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(lba_dependent,
+ tg_pt_gp_alua_supported_states, ALUA_LBD_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(lba_dependent,
+ tg_pt_gp_alua_supported_states, ALUA_LBD_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_lba_dependent, S_IRUGO);
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(unavailable,
+ tg_pt_gp_alua_supported_states, ALUA_U_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(unavailable,
+ tg_pt_gp_alua_supported_states, ALUA_U_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_unavailable, S_IRUGO | S_IWUSR);
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(standby,
+ tg_pt_gp_alua_supported_states, ALUA_S_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(standby,
+ tg_pt_gp_alua_supported_states, ALUA_S_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_standby, S_IRUGO | S_IWUSR);
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(active_optimized,
+ tg_pt_gp_alua_supported_states, ALUA_AO_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(active_optimized,
+ tg_pt_gp_alua_supported_states, ALUA_AO_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_active_optimized, S_IRUGO | S_IWUSR);
+
+SE_DEV_ALUA_SUPPORT_STATE_SHOW(active_nonoptimized,
+ tg_pt_gp_alua_supported_states, ALUA_AN_SUP);
+SE_DEV_ALUA_SUPPORT_STATE_STORE(active_nonoptimized,
+ tg_pt_gp_alua_supported_states, ALUA_AN_SUP);
+SE_DEV_ALUA_TG_PT_ATTR(alua_support_active_nonoptimized, S_IRUGO | S_IWUSR);
+
+/*
* alua_write_metadata
*/
static ssize_t target_core_alua_tg_pt_gp_show_attr_alua_write_metadata(
@@ -2390,14 +2431,14 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_write_metadata(
unsigned long tmp;
int ret;
- ret = strict_strtoul(page, 0, &tmp);
+ ret = kstrtoul(page, 0, &tmp);
if (ret < 0) {
- printk(KERN_ERR "Unable to extract alua_write_metadata\n");
- return -EINVAL;
+ pr_err("Unable to extract alua_write_metadata\n");
+ return ret;
}
if ((tmp != 0) && (tmp != 1)) {
- printk(KERN_ERR "Illegal value for alua_write_metadata:"
+ pr_err("Illegal value for alua_write_metadata:"
" %lu\n", tmp);
return -EINVAL;
}
@@ -2452,6 +2493,26 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_trans_delay_msecs(
SE_DEV_ALUA_TG_PT_ATTR(trans_delay_msecs, S_IRUGO | S_IWUSR);
/*
+ * implicit_trans_secs
+ */
+static ssize_t target_core_alua_tg_pt_gp_show_attr_implicit_trans_secs(
+ struct t10_alua_tg_pt_gp *tg_pt_gp,
+ char *page)
+{
+ return core_alua_show_implicit_trans_secs(tg_pt_gp, page);
+}
+
+static ssize_t target_core_alua_tg_pt_gp_store_attr_implicit_trans_secs(
+ struct t10_alua_tg_pt_gp *tg_pt_gp,
+ const char *page,
+ size_t count)
+{
+ return core_alua_store_implicit_trans_secs(tg_pt_gp, page, count);
+}
+
+SE_DEV_ALUA_TG_PT_ATTR(implicit_trans_secs, S_IRUGO | S_IWUSR);
+
+/*
* preferred
*/
@@ -2479,7 +2540,7 @@ 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))
+ if (!tg_pt_gp->tg_pt_gp_valid_id)
return 0;
return sprintf(page, "%hu\n", tg_pt_gp->tg_pt_gp_id);
@@ -2494,14 +2555,14 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_tg_pt_gp_id(
unsigned long tg_pt_gp_id;
int ret;
- ret = strict_strtoul(page, 0, &tg_pt_gp_id);
+ ret = kstrtoul(page, 0, &tg_pt_gp_id);
if (ret < 0) {
- printk(KERN_ERR "strict_strtoul() returned %d for"
+ pr_err("kstrtoul() returned %d for"
" tg_pt_gp_id\n", ret);
- return -EINVAL;
+ return ret;
}
if (tg_pt_gp_id > 0x0000ffff) {
- printk(KERN_ERR "ALUA tg_pt_gp_id: %lu exceeds maximum:"
+ pr_err("ALUA tg_pt_gp_id: %lu exceeds maximum:"
" 0x0000ffff\n", tg_pt_gp_id);
return -EINVAL;
}
@@ -2510,7 +2571,7 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_tg_pt_gp_id(
if (ret < 0)
return -EINVAL;
- printk(KERN_INFO "Target_Core_ConfigFS: Set ALUA Target Port Group: "
+ pr_debug("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);
@@ -2544,14 +2605,14 @@ static ssize_t target_core_alua_tg_pt_gp_show_attr_members(
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),
+ "/%s\n", tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_wwn(tpg),
+ tpg->se_tpg_tfo->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"
+ pr_warn("Ran out of lu_gp_show_attr"
"_members buffer\n");
break;
}
@@ -2572,9 +2633,17 @@ 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_support_transitioning.attr,
+ &target_core_alua_tg_pt_gp_alua_support_offline.attr,
+ &target_core_alua_tg_pt_gp_alua_support_lba_dependent.attr,
+ &target_core_alua_tg_pt_gp_alua_support_unavailable.attr,
+ &target_core_alua_tg_pt_gp_alua_support_standby.attr,
+ &target_core_alua_tg_pt_gp_alua_support_active_nonoptimized.attr,
+ &target_core_alua_tg_pt_gp_alua_support_active_optimized.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_implicit_trans_secs.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,
@@ -2612,12 +2681,11 @@ static struct config_group *target_core_alua_create_tg_pt_gp(
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))
+ tg_pt_gp = core_alua_allocate_tg_pt_gp(alua->t10_dev, name, 0);
+ if (!tg_pt_gp)
return NULL;
alua_tg_pt_gp_cg = &tg_pt_gp->tg_pt_gp_group;
@@ -2626,7 +2694,7 @@ static struct config_group *target_core_alua_create_tg_pt_gp(
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"
+ pr_debug("Target_Core_ConfigFS: Allocated ALUA Target Port"
" Group: alua/tg_pt_gps/%s\n",
config_item_name(alua_tg_pt_gp_ci));
@@ -2640,7 +2708,7 @@ static void target_core_alua_drop_tg_pt_gp(
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"
+ pr_debug("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);
/*
@@ -2678,6 +2746,34 @@ static struct config_item_type target_core_alua_cit = {
/* End functions for struct config_item_type target_core_alua_cit */
+/* Start functions for struct config_item_type target_core_stat_cit */
+
+static struct config_group *target_core_stat_mkdir(
+ struct config_group *group,
+ const char *name)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static void target_core_stat_rmdir(
+ struct config_group *group,
+ struct config_item *item)
+{
+ return;
+}
+
+static struct configfs_group_operations target_core_stat_group_ops = {
+ .make_group = &target_core_stat_mkdir,
+ .drop_item = &target_core_stat_rmdir,
+};
+
+static struct config_item_type target_core_stat_cit = {
+ .ct_group_ops = &target_core_stat_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* End functions for struct config_item_type target_core_stat_cit */
+
/* Start functions for struct config_item_type target_core_hba_cit */
static struct config_group *target_core_make_subdev(
@@ -2685,147 +2781,125 @@ static struct config_group *target_core_make_subdev(
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 se_device *dev;
struct config_group *dev_cg = NULL, *tg_pt_gp_cg = NULL;
+ struct config_group *dev_stat_grp = NULL;
+ int errno = -ENOMEM, ret;
- if (mutex_lock_interruptible(&hba->hba_access_mutex))
- return NULL;
-
+ ret = mutex_lock_interruptible(&hba->hba_access_mutex);
+ if (ret)
+ return ERR_PTR(ret);
/*
* Locate the struct se_subsystem_api from parent's struct se_hba.
*/
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,
+ dev = target_alloc_device(hba, name);
+ if (!dev)
+ goto out_unlock;
+
+ dev_cg = &dev->dev_group;
+
+ dev_cg->default_groups = kmalloc(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);
+ if (!dev_cg->default_groups)
+ goto out_free_device;
- 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",
+ config_group_init_type_name(dev_cg, name, &target_core_dev_cit);
+ config_group_init_type_name(&dev->dev_attrib.da_group, "attrib",
&target_core_dev_attrib_cit);
- config_group_init_type_name(&se_dev->se_dev_pr_group, "pr",
+ config_group_init_type_name(&dev->dev_pr_group, "pr",
&target_core_dev_pr_cit);
- config_group_init_type_name(&se_dev->t10_wwn.t10_wwn_group, "wwn",
+ config_group_init_type_name(&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,
+ config_group_init_type_name(&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;
+ config_group_init_type_name(&dev->dev_stat_grps.stat_group,
+ "statistics", &target_core_stat_cit);
+
+ dev_cg->default_groups[0] = &dev->dev_attrib.da_group;
+ dev_cg->default_groups[1] = &dev->dev_pr_group;
+ dev_cg->default_groups[2] = &dev->t10_wwn.t10_wwn_group;
+ dev_cg->default_groups[3] = &dev->t10_alua.alua_tg_pt_gps_group;
+ dev_cg->default_groups[4] = &dev->dev_stat_grps.stat_group;
+ dev_cg->default_groups[5] = NULL;
/*
- * Add core/$HBA/$DEV/alua/tg_pt_gps/default_tg_pt_gp
+ * Add core/$HBA/$DEV/alua/default_tg_pt_gp
*/
- tg_pt_gp = core_alua_allocate_tg_pt_gp(se_dev, "default_tg_pt_gp", 1);
- if (!(tg_pt_gp))
- goto out;
+ tg_pt_gp = core_alua_allocate_tg_pt_gp(dev, "default_tg_pt_gp", 1);
+ if (!tg_pt_gp)
+ goto out_free_dev_cg_default_groups;
+ dev->t10_alua.default_tg_pt_gp = tg_pt_gp;
- 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,
+ tg_pt_gp_cg = &dev->t10_alua.alua_tg_pt_gps_group;
+ tg_pt_gp_cg->default_groups = kmalloc(sizeof(struct config_group *) * 2,
GFP_KERNEL);
- if (!(tg_pt_gp_cg->default_groups)) {
- printk(KERN_ERR "Unable to allocate tg_pt_gp_cg->"
+ if (!tg_pt_gp_cg->default_groups) {
+ pr_err("Unable to allocate tg_pt_gp_cg->"
"default_groups\n");
- goto out;
+ goto out_free_tg_pt_gp;
}
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);
+ /*
+ * Add core/$HBA/$DEV/statistics/ default groups
+ */
+ dev_stat_grp = &dev->dev_stat_grps.stat_group;
+ dev_stat_grp->default_groups = kmalloc(sizeof(struct config_group *) * 4,
+ GFP_KERNEL);
+ if (!dev_stat_grp->default_groups) {
+ pr_err("Unable to allocate dev_stat_grp->default_groups\n");
+ goto out_free_tg_pt_gp_cg_default_groups;
+ }
+ target_stat_setup_dev_default_groups(dev);
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:
+ return dev_cg;
+
+out_free_tg_pt_gp_cg_default_groups:
+ kfree(tg_pt_gp_cg->default_groups);
+out_free_tg_pt_gp:
+ core_alua_free_tg_pt_gp(tg_pt_gp);
+out_free_dev_cg_default_groups:
+ kfree(dev_cg->default_groups);
+out_free_device:
+ target_free_device(dev);
+out_unlock:
mutex_unlock(&hba->hba_access_mutex);
- return NULL;
+ return ERR_PTR(errno);
}
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 config_group *dev_cg = to_config_group(item);
+ struct se_device *dev =
+ container_of(dev_cg, struct se_device, 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;
+ struct config_group *tg_pt_gp_cg, *dev_stat_grp;
int i;
- hba = item_to_hba(&se_dev->se_dev_hba->hba_group.cg_item);
+ hba = item_to_hba(&dev->se_hba->hba_group.cg_item);
mutex_lock(&hba->hba_access_mutex);
- 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);
+ dev_stat_grp = &dev->dev_stat_grps.stat_group;
+ for (i = 0; dev_stat_grp->default_groups[i]; i++) {
+ df_item = &dev_stat_grp->default_groups[i]->cg_item;
+ dev_stat_grp->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+ kfree(dev_stat_grp->default_groups);
- tg_pt_gp_cg = &T10_ALUA(se_dev)->alua_tg_pt_gps_group;
+ tg_pt_gp_cg = &dev->t10_alua.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;
@@ -2836,17 +2910,15 @@ static void target_core_drop_subdev(
* core_alua_free_tg_pt_gp() is called from ->default_tg_pt_gp
* directly from target_core_alua_tg_pt_gp_release().
*/
- T10_ALUA(se_dev)->default_tg_pt_gp = NULL;
+ dev->t10_alua.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);
}
/*
- * The releasing of se_dev and associated se_dev->se_dev_ptr is done
- * from target_core_dev_item_ops->release() ->target_core_dev_release().
+ * se_dev is released from target_core_dev_item_ops->release()
*/
config_item_put(item);
mutex_unlock(&hba->hba_access_mutex);
@@ -2903,19 +2975,16 @@ static ssize_t target_core_hba_store_attr_hba_mode(struct se_hba *hba,
if (transport->pmode_enable_hba == NULL)
return -EINVAL;
- ret = strict_strtoul(page, 0, &mode_flag);
+ ret = kstrtoul(page, 0, &mode_flag);
if (ret < 0) {
- printk(KERN_ERR "Unable to extract hba mode flag: %d\n", ret);
- return -EINVAL;
+ pr_err("Unable to extract hba mode flag: %d\n", ret);
+ return ret;
}
- 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);
+ if (hba->dev_count) {
+ pr_err("Unable to set hba_mode with active devices\n");
return -EINVAL;
}
- spin_unlock(&hba->device_lock);
ret = transport->pmode_enable_hba(hba, mode_flag);
if (ret < 0)
@@ -2969,8 +3038,8 @@ static struct config_group *target_core_call_addhbatotarget(
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"
+ if (strlen(name) >= TARGET_CORE_NAME_MAX_LEN) {
+ pr_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);
@@ -2978,8 +3047,8 @@ static struct config_group *target_core_call_addhbatotarget(
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");
+ if (!str) {
+ pr_err("Unable to locate \"_\" for $SUBSYSTEM_PLUGIN_$HOST_ID\n");
return ERR_PTR(-EINVAL);
}
se_plugin_str = buf;
@@ -2988,7 +3057,7 @@ static struct config_group *target_core_call_addhbatotarget(
* Namely rd_direct and rd_mcp..
*/
str2 = strstr(str+1, "_");
- if ((str2)) {
+ if (str2) {
*str2 = '\0'; /* Terminate for *se_plugin_str */
str2++; /* Skip to start of plugin dependent ID */
str = str2;
@@ -2997,17 +3066,16 @@ static struct config_group *target_core_call_addhbatotarget(
str++; /* Skip to start of plugin dependent ID */
}
- ret = strict_strtoul(str, 0, &plugin_dep_id);
+ ret = kstrtoul(str, 0, &plugin_dep_id);
if (ret < 0) {
- printk(KERN_ERR "strict_strtoul() returned %d for"
+ pr_err("kstrtoul() returned %d for"
" plugin_dep_id\n", ret);
- return ERR_PTR(-EINVAL);
+ return ERR_PTR(ret);
}
/*
* Load up TCM subsystem plugins if they have not already been loaded.
*/
- if (transport_subsystem_check_init() < 0)
- return ERR_PTR(-EINVAL);
+ transport_subsystem_check_init();
hba = core_alloc_hba(se_plugin_str, plugin_dep_id, 0);
if (IS_ERR(hba))
@@ -3044,7 +3112,7 @@ static struct config_item_type target_core_cit = {
/* Stop functions for struct config_item_type target_core_hba_cit */
-static int target_core_init_configfs(void)
+static int __init target_core_init_configfs(void)
{
struct config_group *target_cg, *hba_cg = NULL, *alua_cg = NULL;
struct config_group *lu_gp_cg = NULL;
@@ -3052,7 +3120,7 @@ static int target_core_init_configfs(void)
struct t10_alua_lu_gp *lu_gp;
int ret;
- printk(KERN_INFO "TARGET_CORE[0]: Loading Generic Kernel Storage"
+ pr_debug("TARGET_CORE[0]: Loading Generic Kernel Storage"
" Engine: %s on %s/%s on "UTS_RELEASE"\n",
TARGET_CORE_VERSION, utsname()->sysname, utsname()->machine);
@@ -3060,70 +3128,73 @@ static int target_core_init_configfs(void)
config_group_init(&subsys->su_group);
mutex_init(&subsys->su_mutex);
- INIT_LIST_HEAD(&g_tf_list);
- mutex_init(&g_tf_lock);
- init_scsi_index_table();
- ret = init_se_global();
+ ret = init_se_kmem_caches();
if (ret < 0)
- return -1;
+ return ret;
/*
* Create $CONFIGFS/target/core default group for HBA <-> Storage Object
* and ALUA Logical Unit Group and Target Port Group infrastructure.
*/
target_cg = &subsys->su_group;
- target_cg->default_groups = kzalloc(sizeof(struct config_group) * 2,
+ target_cg->default_groups = kmalloc(sizeof(struct config_group *) * 2,
GFP_KERNEL);
- if (!(target_cg->default_groups)) {
- printk(KERN_ERR "Unable to allocate target_cg->default_groups\n");
+ if (!target_cg->default_groups) {
+ pr_err("Unable to allocate target_cg->default_groups\n");
+ ret = -ENOMEM;
goto out_global;
}
- config_group_init_type_name(&se_global->target_core_hbagroup,
+ config_group_init_type_name(&target_core_hbagroup,
"core", &target_core_cit);
- target_cg->default_groups[0] = &se_global->target_core_hbagroup;
+ target_cg->default_groups[0] = &target_core_hbagroup;
target_cg->default_groups[1] = NULL;
/*
* Create ALUA infrastructure under /sys/kernel/config/target/core/alua/
*/
- hba_cg = &se_global->target_core_hbagroup;
- hba_cg->default_groups = kzalloc(sizeof(struct config_group) * 2,
+ hba_cg = &target_core_hbagroup;
+ hba_cg->default_groups = kmalloc(sizeof(struct config_group *) * 2,
GFP_KERNEL);
- if (!(hba_cg->default_groups)) {
- printk(KERN_ERR "Unable to allocate hba_cg->default_groups\n");
+ if (!hba_cg->default_groups) {
+ pr_err("Unable to allocate hba_cg->default_groups\n");
+ ret = -ENOMEM;
goto out_global;
}
- config_group_init_type_name(&se_global->alua_group,
+ config_group_init_type_name(&alua_group,
"alua", &target_core_alua_cit);
- hba_cg->default_groups[0] = &se_global->alua_group;
+ hba_cg->default_groups[0] = &alua_group;
hba_cg->default_groups[1] = NULL;
/*
* Add ALUA Logical Unit Group and Target Port Group ConfigFS
* groups under /sys/kernel/config/target/core/alua/
*/
- alua_cg = &se_global->alua_group;
- alua_cg->default_groups = kzalloc(sizeof(struct config_group) * 2,
+ alua_cg = &alua_group;
+ alua_cg->default_groups = kmalloc(sizeof(struct config_group *) * 2,
GFP_KERNEL);
- if (!(alua_cg->default_groups)) {
- printk(KERN_ERR "Unable to allocate alua_cg->default_groups\n");
+ if (!alua_cg->default_groups) {
+ pr_err("Unable to allocate alua_cg->default_groups\n");
+ ret = -ENOMEM;
goto out_global;
}
- config_group_init_type_name(&se_global->alua_lu_gps_group,
+ config_group_init_type_name(&alua_lu_gps_group,
"lu_gps", &target_core_alua_lu_gps_cit);
- alua_cg->default_groups[0] = &se_global->alua_lu_gps_group;
+ alua_cg->default_groups[0] = &alua_lu_gps_group;
alua_cg->default_groups[1] = NULL;
/*
* Add core/alua/lu_gps/default_lu_gp
*/
lu_gp = core_alua_allocate_lu_gp("default_lu_gp", 1);
- if (IS_ERR(lu_gp))
+ if (IS_ERR(lu_gp)) {
+ ret = -ENOMEM;
goto out_global;
+ }
- lu_gp_cg = &se_global->alua_lu_gps_group;
- lu_gp_cg->default_groups = kzalloc(sizeof(struct config_group) * 2,
+ lu_gp_cg = &alua_lu_gps_group;
+ lu_gp_cg->default_groups = kmalloc(sizeof(struct config_group *) * 2,
GFP_KERNEL);
- if (!(lu_gp_cg->default_groups)) {
- printk(KERN_ERR "Unable to allocate lu_gp_cg->default_groups\n");
+ if (!lu_gp_cg->default_groups) {
+ pr_err("Unable to allocate lu_gp_cg->default_groups\n");
+ ret = -ENOMEM;
goto out_global;
}
@@ -3131,17 +3202,17 @@ static int target_core_init_configfs(void)
&target_core_alua_lu_gp_cit);
lu_gp_cg->default_groups[0] = &lu_gp->lu_gp_group;
lu_gp_cg->default_groups[1] = NULL;
- se_global->default_lu_gp = lu_gp;
+ default_lu_gp = lu_gp;
/*
* Register the target_core_mod subsystem with configfs.
*/
ret = configfs_register_subsystem(subsys);
if (ret < 0) {
- printk(KERN_ERR "Error %d while registering subsystem %s\n",
+ pr_err("Error %d while registering subsystem %s\n",
ret, subsys->su_group.cg_item.ci_namebuf);
goto out_global;
}
- printk(KERN_INFO "TARGET_CORE[0]: Initialized ConfigFS Fabric"
+ pr_debug("TARGET_CORE[0]: Initialized ConfigFS Fabric"
" Infrastructure: "TARGET_CORE_CONFIGFS_VERSION" on %s/%s"
" on "UTS_RELEASE"\n", utsname()->sysname, utsname()->machine);
/*
@@ -3151,7 +3222,12 @@ static int target_core_init_configfs(void)
if (ret < 0)
goto out;
- if (core_dev_setup_virtual_lun0() < 0)
+ ret = core_dev_setup_virtual_lun0();
+ if (ret < 0)
+ goto out;
+
+ ret = target_xcopy_setup_pt();
+ if (ret < 0)
goto out;
return 0;
@@ -3161,9 +3237,9 @@ out:
core_dev_release_virtual_lun0();
rd_module_exit();
out_global:
- if (se_global->default_lu_gp) {
- core_alua_free_lu_gp(se_global->default_lu_gp);
- se_global->default_lu_gp = NULL;
+ if (default_lu_gp) {
+ core_alua_free_lu_gp(default_lu_gp);
+ default_lu_gp = NULL;
}
if (lu_gp_cg)
kfree(lu_gp_cg->default_groups);
@@ -3172,21 +3248,20 @@ out_global:
if (hba_cg)
kfree(hba_cg->default_groups);
kfree(target_cg->default_groups);
- release_se_global();
- return -1;
+ release_se_kmem_caches();
+ return ret;
}
-static void target_core_exit_configfs(void)
+static void __exit target_core_exit_configfs(void)
{
struct configfs_subsystem *subsys;
struct config_group *hba_cg, *alua_cg, *lu_gp_cg;
struct config_item *item;
int i;
- se_global->in_shutdown = 1;
subsys = target_core_subsystem[0];
- lu_gp_cg = &se_global->alua_lu_gps_group;
+ lu_gp_cg = &alua_lu_gps_group;
for (i = 0; lu_gp_cg->default_groups[i]; i++) {
item = &lu_gp_cg->default_groups[i]->cg_item;
lu_gp_cg->default_groups[i] = NULL;
@@ -3195,7 +3270,7 @@ static void target_core_exit_configfs(void)
kfree(lu_gp_cg->default_groups);
lu_gp_cg->default_groups = NULL;
- alua_cg = &se_global->alua_group;
+ alua_cg = &alua_group;
for (i = 0; alua_cg->default_groups[i]; i++) {
item = &alua_cg->default_groups[i]->cg_item;
alua_cg->default_groups[i] = NULL;
@@ -3204,7 +3279,7 @@ static void target_core_exit_configfs(void)
kfree(alua_cg->default_groups);
alua_cg->default_groups = NULL;
- hba_cg = &se_global->target_core_hbagroup;
+ hba_cg = &target_core_hbagroup;
for (i = 0; hba_cg->default_groups[i]; i++) {
item = &hba_cg->default_groups[i]->cg_item;
hba_cg->default_groups[i] = NULL;
@@ -3219,17 +3294,16 @@ static void target_core_exit_configfs(void)
configfs_unregister_subsystem(subsys);
kfree(subsys->su_group.default_groups);
- core_alua_free_lu_gp(se_global->default_lu_gp);
- se_global->default_lu_gp = NULL;
+ core_alua_free_lu_gp(default_lu_gp);
+ default_lu_gp = NULL;
- printk(KERN_INFO "TARGET_CORE[0]: Released ConfigFS Fabric"
+ pr_debug("TARGET_CORE[0]: Released ConfigFS Fabric"
" Infrastructure\n");
core_dev_release_virtual_lun0();
rd_module_exit();
- release_se_global();
-
- return;
+ target_xcopy_release_pt();
+ release_se_kmem_caches();
}
MODULE_DESCRIPTION("Target_Core_Mod/ConfigFS");
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index 5da051a07fa..98da9016715 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -1,13 +1,10 @@
/*******************************************************************************
* Filename: target_core_device.c (based on iscsi_target_device.c)
*
- * This file contains the iSCSI Virtual Device and Disk Transport
+ * This file contains the TCM Virtual Device and Disk Transport
* agnostic related functions.
*
- * Copyright (c) 2003, 2004, 2005 PyX Technologies, Inc.
- * Copyright (c) 2005-2006 SBE, Inc. All Rights Reserved.
- * Copyright (c) 2007-2010 Rising Tide Systems
- * Copyright (c) 2008-2010 Linux-iSCSI.org
+ * (c) Copyright 2003-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -33,197 +30,160 @@
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
#include <linux/kthread.h>
#include <linux/in.h>
+#include <linux/export.h>
#include <net/sock.h>
#include <net/tcp.h>
#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
#include <target/target_core_base.h>
-#include <target/target_core_device.h>
-#include <target/target_core_tpg.h>
-#include <target/target_core_transport.h>
-#include <target/target_core_fabric_ops.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+#include "target_core_internal.h"
#include "target_core_alua.h"
-#include "target_core_hba.h"
#include "target_core_pr.h"
#include "target_core_ua.h"
-static void se_dev_start(struct se_device *dev);
-static void se_dev_stop(struct se_device *dev);
+DEFINE_MUTEX(g_device_mutex);
+LIST_HEAD(g_device_list);
-int transport_get_lun_for_cmd(
- struct se_cmd *se_cmd,
- unsigned char *cdb,
- u32 unpacked_lun)
+static struct se_hba *lun0_hba;
+/* not static, needed by tpg.c */
+struct se_device *g_lun0_dev;
+
+sense_reason_t
+transport_lookup_cmd_lun(struct se_cmd *se_cmd, u32 unpacked_lun)
{
- struct se_dev_entry *deve;
struct se_lun *se_lun = NULL;
- struct se_session *se_sess = SE_SESS(se_cmd);
+ struct se_session *se_sess = se_cmd->se_sess;
+ struct se_device *dev;
unsigned long flags;
- int read_only = 0;
- spin_lock_irq(&SE_NODE_ACL(se_sess)->device_list_lock);
- deve = se_cmd->se_deve =
- &SE_NODE_ACL(se_sess)->device_list[unpacked_lun];
- if (deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS) {
- if (se_cmd) {
- deve->total_cmds++;
- deve->total_bytes += se_cmd->data_length;
-
- if (se_cmd->data_direction == DMA_TO_DEVICE) {
- if (deve->lun_flags &
- TRANSPORT_LUNFLAGS_READ_ONLY) {
- read_only = 1;
- goto out;
- }
- deve->write_bytes += se_cmd->data_length;
- } else if (se_cmd->data_direction ==
- DMA_FROM_DEVICE) {
- deve->read_bytes += se_cmd->data_length;
- }
+ if (unpacked_lun >= TRANSPORT_MAX_LUNS_PER_TPG)
+ return TCM_NON_EXISTENT_LUN;
+
+ spin_lock_irqsave(&se_sess->se_node_acl->device_list_lock, flags);
+ se_cmd->se_deve = se_sess->se_node_acl->device_list[unpacked_lun];
+ if (se_cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS) {
+ struct se_dev_entry *deve = se_cmd->se_deve;
+
+ deve->total_cmds++;
+
+ if ((se_cmd->data_direction == DMA_TO_DEVICE) &&
+ (deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY)) {
+ pr_err("TARGET_CORE[%s]: Detected WRITE_PROTECTED LUN"
+ " Access for 0x%08x\n",
+ se_cmd->se_tfo->get_fabric_name(),
+ unpacked_lun);
+ spin_unlock_irqrestore(&se_sess->se_node_acl->device_list_lock, flags);
+ return TCM_WRITE_PROTECTED;
}
- deve->deve_cmds++;
- se_lun = se_cmd->se_lun = deve->se_lun;
+ if (se_cmd->data_direction == DMA_TO_DEVICE)
+ deve->write_bytes += se_cmd->data_length;
+ else if (se_cmd->data_direction == DMA_FROM_DEVICE)
+ deve->read_bytes += se_cmd->data_length;
+
+ se_lun = deve->se_lun;
+ se_cmd->se_lun = deve->se_lun;
se_cmd->pr_res_key = deve->pr_res_key;
se_cmd->orig_fe_lun = unpacked_lun;
- se_cmd->se_orig_obj_ptr = SE_LUN(se_cmd)->lun_se_dev;
se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD;
+
+ percpu_ref_get(&se_lun->lun_ref);
+ se_cmd->lun_ref_active = true;
}
-out:
- spin_unlock_irq(&SE_NODE_ACL(se_sess)->device_list_lock);
+ spin_unlock_irqrestore(&se_sess->se_node_acl->device_list_lock, flags);
if (!se_lun) {
- if (read_only) {
- se_cmd->scsi_sense_reason = TCM_WRITE_PROTECTED;
- se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- printk("TARGET_CORE[%s]: Detected WRITE_PROTECTED LUN"
+ /*
+ * Use the se_portal_group->tpg_virt_lun0 to allow for
+ * REPORT_LUNS, et al to be returned when no active
+ * MappedLUN=0 exists for this Initiator Port.
+ */
+ if (unpacked_lun != 0) {
+ pr_err("TARGET_CORE[%s]: Detected NON_EXISTENT_LUN"
" Access for 0x%08x\n",
- CMD_TFO(se_cmd)->get_fabric_name(),
+ se_cmd->se_tfo->get_fabric_name(),
unpacked_lun);
- return -1;
- } else {
- /*
- * Use the se_portal_group->tpg_virt_lun0 to allow for
- * REPORT_LUNS, et al to be returned when no active
- * MappedLUN=0 exists for this Initiator Port.
- */
- if (unpacked_lun != 0) {
- se_cmd->scsi_sense_reason = TCM_NON_EXISTENT_LUN;
- se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- printk("TARGET_CORE[%s]: Detected NON_EXISTENT_LUN"
- " Access for 0x%08x\n",
- CMD_TFO(se_cmd)->get_fabric_name(),
- unpacked_lun);
- return -1;
- }
- /*
- * Force WRITE PROTECT for virtual LUN 0
- */
- if ((se_cmd->data_direction != DMA_FROM_DEVICE) &&
- (se_cmd->data_direction != DMA_NONE)) {
- se_cmd->scsi_sense_reason = TCM_WRITE_PROTECTED;
- se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- return -1;
- }
-#if 0
- printk("TARGET_CORE[%s]: Using virtual LUN0! :-)\n",
- CMD_TFO(se_cmd)->get_fabric_name());
-#endif
- se_lun = se_cmd->se_lun = &se_sess->se_tpg->tpg_virt_lun0;
- se_cmd->orig_fe_lun = 0;
- se_cmd->se_orig_obj_ptr = SE_LUN(se_cmd)->lun_se_dev;
- se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD;
+ return TCM_NON_EXISTENT_LUN;
}
- }
- /*
- * Determine if the struct se_lun is online.
- */
-/* #warning FIXME: Check for LUN_RESET + UNIT Attention */
- if (se_dev_check_online(se_lun->lun_se_dev) != 0) {
- se_cmd->scsi_sense_reason = TCM_NON_EXISTENT_LUN;
- se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- return -1;
+ /*
+ * Force WRITE PROTECT for virtual LUN 0
+ */
+ if ((se_cmd->data_direction != DMA_FROM_DEVICE) &&
+ (se_cmd->data_direction != DMA_NONE))
+ return TCM_WRITE_PROTECTED;
+
+ se_lun = &se_sess->se_tpg->tpg_virt_lun0;
+ se_cmd->se_lun = &se_sess->se_tpg->tpg_virt_lun0;
+ se_cmd->orig_fe_lun = 0;
+ se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD;
+
+ percpu_ref_get(&se_lun->lun_ref);
+ se_cmd->lun_ref_active = true;
}
- {
- struct se_device *dev = se_lun->lun_se_dev;
- spin_lock(&dev->stats_lock);
- dev->num_cmds++;
+ /* Directly associate cmd with se_dev */
+ se_cmd->se_dev = se_lun->lun_se_dev;
+
+ dev = se_lun->lun_se_dev;
+ atomic_long_inc(&dev->num_cmds);
if (se_cmd->data_direction == DMA_TO_DEVICE)
- dev->write_bytes += se_cmd->data_length;
+ atomic_long_add(se_cmd->data_length, &dev->write_bytes);
else if (se_cmd->data_direction == DMA_FROM_DEVICE)
- dev->read_bytes += se_cmd->data_length;
- spin_unlock(&dev->stats_lock);
- }
-
- /*
- * Add the iscsi_cmd_t to the struct se_lun's cmd list. This list is used
- * for tracking state of struct se_cmds during LUN shutdown events.
- */
- spin_lock_irqsave(&se_lun->lun_cmd_lock, flags);
- list_add_tail(&se_cmd->se_lun_list, &se_lun->lun_cmd_list);
- atomic_set(&T_TASK(se_cmd)->transport_lun_active, 1);
-#if 0
- printk(KERN_INFO "Adding ITT: 0x%08x to LUN LIST[%d]\n",
- CMD_TFO(se_cmd)->get_task_tag(se_cmd), se_lun->unpacked_lun);
-#endif
- spin_unlock_irqrestore(&se_lun->lun_cmd_lock, flags);
+ atomic_long_add(se_cmd->data_length, &dev->read_bytes);
return 0;
}
-EXPORT_SYMBOL(transport_get_lun_for_cmd);
+EXPORT_SYMBOL(transport_lookup_cmd_lun);
-int transport_get_lun_for_tmr(
- struct se_cmd *se_cmd,
- u32 unpacked_lun)
+int transport_lookup_tmr_lun(struct se_cmd *se_cmd, u32 unpacked_lun)
{
- struct se_device *dev = NULL;
struct se_dev_entry *deve;
struct se_lun *se_lun = NULL;
- struct se_session *se_sess = SE_SESS(se_cmd);
+ struct se_session *se_sess = se_cmd->se_sess;
struct se_tmr_req *se_tmr = se_cmd->se_tmr_req;
+ unsigned long flags;
+
+ if (unpacked_lun >= TRANSPORT_MAX_LUNS_PER_TPG)
+ return -ENODEV;
+
+ spin_lock_irqsave(&se_sess->se_node_acl->device_list_lock, flags);
+ se_cmd->se_deve = se_sess->se_node_acl->device_list[unpacked_lun];
+ deve = se_cmd->se_deve;
- spin_lock_irq(&SE_NODE_ACL(se_sess)->device_list_lock);
- deve = se_cmd->se_deve =
- &SE_NODE_ACL(se_sess)->device_list[unpacked_lun];
if (deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS) {
- se_lun = se_cmd->se_lun = se_tmr->tmr_lun = deve->se_lun;
- dev = se_tmr->tmr_dev = se_lun->lun_se_dev;
+ se_tmr->tmr_lun = deve->se_lun;
+ se_cmd->se_lun = deve->se_lun;
+ se_lun = deve->se_lun;
se_cmd->pr_res_key = deve->pr_res_key;
se_cmd->orig_fe_lun = unpacked_lun;
- se_cmd->se_orig_obj_ptr = SE_LUN(se_cmd)->lun_se_dev;
-/* se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD; */
}
- spin_unlock_irq(&SE_NODE_ACL(se_sess)->device_list_lock);
+ spin_unlock_irqrestore(&se_sess->se_node_acl->device_list_lock, flags);
if (!se_lun) {
- printk(KERN_INFO "TARGET_CORE[%s]: Detected NON_EXISTENT_LUN"
+ pr_debug("TARGET_CORE[%s]: Detected NON_EXISTENT_LUN"
" Access for 0x%08x\n",
- CMD_TFO(se_cmd)->get_fabric_name(),
+ se_cmd->se_tfo->get_fabric_name(),
unpacked_lun);
- se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- return -1;
- }
- /*
- * Determine if the struct se_lun is online.
- */
-/* #warning FIXME: Check for LUN_RESET + UNIT Attention */
- if (se_dev_check_online(se_lun->lun_se_dev) != 0) {
- se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- return -1;
+ return -ENODEV;
}
- spin_lock(&dev->se_tmr_lock);
- list_add_tail(&se_tmr->tmr_list, &dev->dev_tmr_list);
- spin_unlock(&dev->se_tmr_lock);
+ /* Directly associate cmd with se_dev */
+ se_cmd->se_dev = se_lun->lun_se_dev;
+ se_tmr->tmr_dev = se_lun->lun_se_dev;
+
+ spin_lock_irqsave(&se_tmr->tmr_dev->se_tmr_lock, flags);
+ list_add_tail(&se_tmr->tmr_list, &se_tmr->tmr_dev->dev_tmr_list);
+ spin_unlock_irqrestore(&se_tmr->tmr_dev->se_tmr_lock, flags);
return 0;
}
-EXPORT_SYMBOL(transport_get_lun_for_tmr);
+EXPORT_SYMBOL(transport_lookup_tmr_lun);
/*
* This function is called from core_scsi3_emulate_pro_register_and_move()
@@ -242,30 +202,30 @@ struct se_dev_entry *core_get_se_deve_from_rtpi(
spin_lock_irq(&nacl->device_list_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- deve = &nacl->device_list[i];
+ deve = nacl->device_list[i];
if (!(deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS))
continue;
lun = deve->se_lun;
- if (!(lun)) {
- printk(KERN_ERR "%s device entries device pointer is"
+ if (!lun) {
+ pr_err("%s device entries device pointer is"
" NULL, but Initiator has access.\n",
- TPG_TFO(tpg)->get_fabric_name());
+ tpg->se_tpg_tfo->get_fabric_name());
continue;
}
port = lun->lun_sep;
- if (!(port)) {
- printk(KERN_ERR "%s device entries device pointer is"
+ if (!port) {
+ pr_err("%s device entries device pointer is"
" NULL, but Initiator has access.\n",
- TPG_TFO(tpg)->get_fabric_name());
+ tpg->se_tpg_tfo->get_fabric_name());
continue;
}
if (port->sep_rtpi != rtpi)
continue;
atomic_inc(&deve->pr_ref_count);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
spin_unlock_irq(&nacl->device_list_lock);
return deve;
@@ -288,44 +248,32 @@ int core_free_device_list_for_node(
spin_lock_irq(&nacl->device_list_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- deve = &nacl->device_list[i];
+ deve = nacl->device_list[i];
if (!(deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS))
continue;
if (!deve->se_lun) {
- printk(KERN_ERR "%s device entries device pointer is"
+ pr_err("%s device entries device pointer is"
" NULL, but Initiator has access.\n",
- TPG_TFO(tpg)->get_fabric_name());
+ tpg->se_tpg_tfo->get_fabric_name());
continue;
}
lun = deve->se_lun;
spin_unlock_irq(&nacl->device_list_lock);
- core_update_device_list_for_node(lun, NULL, deve->mapped_lun,
- TRANSPORT_LUNFLAGS_NO_ACCESS, nacl, tpg, 0);
+ core_disable_device_list_for_node(lun, NULL, deve->mapped_lun,
+ TRANSPORT_LUNFLAGS_NO_ACCESS, nacl, tpg);
spin_lock_irq(&nacl->device_list_lock);
}
spin_unlock_irq(&nacl->device_list_lock);
- kfree(nacl->device_list);
+ array_free(nacl->device_list, TRANSPORT_MAX_LUNS_PER_TPG);
nacl->device_list = NULL;
return 0;
}
-void core_dec_lacl_count(struct se_node_acl *se_nacl, struct se_cmd *se_cmd)
-{
- struct se_dev_entry *deve;
-
- spin_lock_irq(&se_nacl->device_list_lock);
- deve = &se_nacl->device_list[se_cmd->orig_fe_lun];
- deve->deve_cmds--;
- spin_unlock_irq(&se_nacl->device_list_lock);
-
- return;
-}
-
void core_update_device_list_access(
u32 mapped_lun,
u32 lun_access,
@@ -334,7 +282,7 @@ void core_update_device_list_access(
struct se_dev_entry *deve;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[mapped_lun];
+ deve = nacl->device_list[mapped_lun];
if (lun_access & TRANSPORT_LUNFLAGS_READ_WRITE) {
deve->lun_flags &= ~TRANSPORT_LUNFLAGS_READ_ONLY;
deve->lun_flags |= TRANSPORT_LUNFLAGS_READ_WRITE;
@@ -343,76 +291,48 @@ void core_update_device_list_access(
deve->lun_flags |= TRANSPORT_LUNFLAGS_READ_ONLY;
}
spin_unlock_irq(&nacl->device_list_lock);
-
- return;
}
-/* core_update_device_list_for_node():
+/* core_enable_device_list_for_node():
*
*
*/
-int core_update_device_list_for_node(
+int core_enable_device_list_for_node(
struct se_lun *lun,
struct se_lun_acl *lun_acl,
u32 mapped_lun,
u32 lun_access,
struct se_node_acl *nacl,
- struct se_portal_group *tpg,
- int enable)
+ struct se_portal_group *tpg)
{
struct se_port *port = lun->lun_sep;
- struct se_dev_entry *deve = &nacl->device_list[mapped_lun];
- int trans = 0;
- /*
- * If the MappedLUN entry is being disabled, the entry in
- * port->sep_alua_list must be removed now before clearing the
- * struct se_dev_entry pointers below as logic in
- * core_alua_do_transition_tg_pt() depends on these being present.
- */
- if (!(enable)) {
- /*
- * deve->se_lun_acl will be NULL for demo-mode created LUNs
- * that have not been explictly concerted to MappedLUNs ->
- * struct se_lun_acl, but we remove deve->alua_port_list from
- * port->sep_alua_list. This also means that active UAs and
- * NodeACL context specific PR metadata for demo-mode
- * MappedLUN *deve will be released below..
- */
- spin_lock_bh(&port->sep_alua_lock);
- list_del(&deve->alua_port_list);
- spin_unlock_bh(&port->sep_alua_lock);
- }
+ struct se_dev_entry *deve;
spin_lock_irq(&nacl->device_list_lock);
- if (enable) {
- /*
- * Check if the call is handling demo mode -> explict LUN ACL
- * transition. This transition must be for the same struct se_lun
- * + mapped_lun that was setup in demo mode..
- */
- if (deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS) {
- if (deve->se_lun_acl != NULL) {
- printk(KERN_ERR "struct se_dev_entry->se_lun_acl"
- " already set for demo mode -> explict"
- " LUN ACL transition\n");
- spin_unlock_irq(&nacl->device_list_lock);
- return -1;
- }
- if (deve->se_lun != lun) {
- printk(KERN_ERR "struct se_dev_entry->se_lun does"
- " match passed struct se_lun for demo mode"
- " -> explict LUN ACL transition\n");
- spin_unlock_irq(&nacl->device_list_lock);
- return -1;
- }
- deve->se_lun_acl = lun_acl;
- trans = 1;
- } else {
- deve->se_lun = lun;
- deve->se_lun_acl = lun_acl;
- deve->mapped_lun = mapped_lun;
- deve->lun_flags |= TRANSPORT_LUNFLAGS_INITIATOR_ACCESS;
+
+ deve = nacl->device_list[mapped_lun];
+
+ /*
+ * Check if the call is handling demo mode -> explicit LUN ACL
+ * transition. This transition must be for the same struct se_lun
+ * + mapped_lun that was setup in demo mode..
+ */
+ if (deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS) {
+ if (deve->se_lun_acl != NULL) {
+ pr_err("struct se_dev_entry->se_lun_acl"
+ " already set for demo mode -> explicit"
+ " LUN ACL transition\n");
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -EINVAL;
+ }
+ if (deve->se_lun != lun) {
+ pr_err("struct se_dev_entry->se_lun does"
+ " match passed struct se_lun for demo mode"
+ " -> explicit LUN ACL transition\n");
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -EINVAL;
}
+ deve->se_lun_acl = lun_acl;
if (lun_access & TRANSPORT_LUNFLAGS_READ_WRITE) {
deve->lun_flags &= ~TRANSPORT_LUNFLAGS_READ_ONLY;
@@ -422,27 +342,72 @@ int core_update_device_list_for_node(
deve->lun_flags |= TRANSPORT_LUNFLAGS_READ_ONLY;
}
- if (trans) {
- spin_unlock_irq(&nacl->device_list_lock);
- return 0;
- }
- deve->creation_time = get_jiffies_64();
- deve->attach_count++;
spin_unlock_irq(&nacl->device_list_lock);
+ return 0;
+ }
- spin_lock_bh(&port->sep_alua_lock);
- list_add_tail(&deve->alua_port_list, &port->sep_alua_list);
- spin_unlock_bh(&port->sep_alua_lock);
+ deve->se_lun = lun;
+ deve->se_lun_acl = lun_acl;
+ deve->mapped_lun = mapped_lun;
+ deve->lun_flags |= TRANSPORT_LUNFLAGS_INITIATOR_ACCESS;
- return 0;
+ if (lun_access & TRANSPORT_LUNFLAGS_READ_WRITE) {
+ deve->lun_flags &= ~TRANSPORT_LUNFLAGS_READ_ONLY;
+ deve->lun_flags |= TRANSPORT_LUNFLAGS_READ_WRITE;
+ } else {
+ deve->lun_flags &= ~TRANSPORT_LUNFLAGS_READ_WRITE;
+ deve->lun_flags |= TRANSPORT_LUNFLAGS_READ_ONLY;
}
+
+ deve->creation_time = get_jiffies_64();
+ deve->attach_count++;
+ spin_unlock_irq(&nacl->device_list_lock);
+
+ spin_lock_bh(&port->sep_alua_lock);
+ list_add_tail(&deve->alua_port_list, &port->sep_alua_list);
+ spin_unlock_bh(&port->sep_alua_lock);
+
+ return 0;
+}
+
+/* core_disable_device_list_for_node():
+ *
+ *
+ */
+int core_disable_device_list_for_node(
+ struct se_lun *lun,
+ struct se_lun_acl *lun_acl,
+ u32 mapped_lun,
+ u32 lun_access,
+ struct se_node_acl *nacl,
+ struct se_portal_group *tpg)
+{
+ struct se_port *port = lun->lun_sep;
+ struct se_dev_entry *deve = nacl->device_list[mapped_lun];
+
+ /*
+ * If the MappedLUN entry is being disabled, the entry in
+ * port->sep_alua_list must be removed now before clearing the
+ * struct se_dev_entry pointers below as logic in
+ * core_alua_do_transition_tg_pt() depends on these being present.
+ *
+ * deve->se_lun_acl will be NULL for demo-mode created LUNs
+ * that have not been explicitly converted to MappedLUNs ->
+ * struct se_lun_acl, but we remove deve->alua_port_list from
+ * port->sep_alua_list. This also means that active UAs and
+ * NodeACL context specific PR metadata for demo-mode
+ * MappedLUN *deve will be released below..
+ */
+ spin_lock_bh(&port->sep_alua_lock);
+ list_del(&deve->alua_port_list);
+ spin_unlock_bh(&port->sep_alua_lock);
/*
* Wait for any in process SPEC_I_PT=1 or REGISTER_AND_MOVE
* PR operation to complete.
*/
- spin_unlock_irq(&nacl->device_list_lock);
while (atomic_read(&deve->pr_ref_count) != 0)
cpu_relax();
+
spin_lock_irq(&nacl->device_list_lock);
/*
* Disable struct se_dev_entry LUN ACL mapping
@@ -469,30 +434,28 @@ void core_clear_lun_from_tpg(struct se_lun *lun, struct se_portal_group *tpg)
struct se_dev_entry *deve;
u32 i;
- spin_lock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
list_for_each_entry(nacl, &tpg->acl_node_list, acl_list) {
- spin_unlock_bh(&tpg->acl_node_lock);
+ spin_unlock_irq(&tpg->acl_node_lock);
spin_lock_irq(&nacl->device_list_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- deve = &nacl->device_list[i];
+ deve = nacl->device_list[i];
if (lun != deve->se_lun)
continue;
spin_unlock_irq(&nacl->device_list_lock);
- core_update_device_list_for_node(lun, NULL,
+ core_disable_device_list_for_node(lun, NULL,
deve->mapped_lun, TRANSPORT_LUNFLAGS_NO_ACCESS,
- nacl, tpg, 0);
+ nacl, tpg);
spin_lock_irq(&nacl->device_list_lock);
}
spin_unlock_irq(&nacl->device_list_lock);
- spin_lock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
}
- spin_unlock_bh(&tpg->acl_node_lock);
-
- return;
+ spin_unlock_irq(&tpg->acl_node_lock);
}
static struct se_port *core_alloc_port(struct se_device *dev)
@@ -500,9 +463,9 @@ static struct se_port *core_alloc_port(struct se_device *dev)
struct se_port *port, *port_tmp;
port = kzalloc(sizeof(struct se_port), GFP_KERNEL);
- if (!(port)) {
- printk(KERN_ERR "Unable to allocate struct se_port\n");
- return NULL;
+ if (!port) {
+ pr_err("Unable to allocate struct se_port\n");
+ return ERR_PTR(-ENOMEM);
}
INIT_LIST_HEAD(&port->sep_alua_list);
INIT_LIST_HEAD(&port->sep_list);
@@ -512,14 +475,14 @@ static struct se_port *core_alloc_port(struct se_device *dev)
spin_lock(&dev->se_port_lock);
if (dev->dev_port_count == 0x0000ffff) {
- printk(KERN_WARNING "Reached dev->dev_port_count =="
+ pr_warn("Reached dev->dev_port_count =="
" 0x0000ffff\n");
spin_unlock(&dev->se_port_lock);
- return NULL;
+ return ERR_PTR(-ENOSPC);
}
again:
/*
- * Allocate the next RELATIVE TARGET PORT IDENTIFER for this struct se_device
+ * Allocate the next RELATIVE TARGET PORT IDENTIFIER for this struct se_device
* Here is the table from spc4r17 section 7.7.3.8.
*
* Table 473 -- RELATIVE TARGET PORT IDENTIFIER field
@@ -531,12 +494,12 @@ again:
* 3h to FFFFh Relative port 3 through 65 535
*/
port->sep_rtpi = dev->dev_rpti_counter++;
- if (!(port->sep_rtpi))
+ if (!port->sep_rtpi)
goto again;
list_for_each_entry(port_tmp, &dev->dev_sep_list, sep_list) {
/*
- * Make sure RELATIVE TARGET PORT IDENTIFER is unique
+ * Make sure RELATIVE TARGET PORT IDENTIFIER is unique
* for 16-bit wrap..
*/
if (port->sep_rtpi == port_tmp->sep_rtpi)
@@ -553,7 +516,6 @@ static void core_export_port(
struct se_port *port,
struct se_lun *lun)
{
- struct se_subsystem_dev *su_dev = SU_DEV(dev);
struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem = NULL;
spin_lock(&dev->se_port_lock);
@@ -566,30 +528,32 @@ static void core_export_port(
list_add_tail(&port->sep_list, &dev->dev_sep_list);
spin_unlock(&dev->se_port_lock);
- if (T10_ALUA(su_dev)->alua_type == SPC3_ALUA_EMULATED) {
+ if (dev->transport->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV &&
+ !(dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE)) {
tg_pt_gp_mem = core_alua_allocate_tg_pt_gp_mem(port);
if (IS_ERR(tg_pt_gp_mem) || !tg_pt_gp_mem) {
- printk(KERN_ERR "Unable to allocate t10_alua_tg_pt"
+ pr_err("Unable to allocate t10_alua_tg_pt"
"_gp_member_t\n");
return;
}
spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
__core_alua_attach_tg_pt_gp_mem(tg_pt_gp_mem,
- T10_ALUA(su_dev)->default_tg_pt_gp);
+ dev->t10_alua.default_tg_pt_gp);
spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
- printk(KERN_INFO "%s/%s: Adding to default ALUA Target Port"
+ pr_debug("%s/%s: Adding to default ALUA Target Port"
" Group: alua/default_tg_pt_gp\n",
- TRANSPORT(dev)->name, TPG_TFO(tpg)->get_fabric_name());
+ dev->transport->name, tpg->se_tpg_tfo->get_fabric_name());
}
dev->dev_port_count++;
- port->sep_index = port->sep_rtpi; /* RELATIVE TARGET PORT IDENTIFER */
+ port->sep_index = port->sep_rtpi; /* RELATIVE TARGET PORT IDENTIFIER */
}
/*
* Called with struct se_device->se_port_lock spinlock held.
*/
static void core_release_port(struct se_device *dev, struct se_port *port)
+ __releases(&dev->se_port_lock) __acquires(&dev->se_port_lock)
{
/*
* Wait for any port reference for PR ALL_TG_PT=1 operation
@@ -605,8 +569,6 @@ static void core_release_port(struct se_device *dev, struct se_port *port)
list_del(&port->sep_list);
dev->dev_port_count--;
kfree(port);
-
- return;
}
int core_dev_export(
@@ -614,16 +576,19 @@ int core_dev_export(
struct se_portal_group *tpg,
struct se_lun *lun)
{
+ struct se_hba *hba = dev->se_hba;
struct se_port *port;
port = core_alloc_port(dev);
- if (!(port))
- return -1;
+ if (IS_ERR(port))
+ return PTR_ERR(port);
lun->lun_se_dev = dev;
- se_dev_start(dev);
- atomic_inc(&dev->dev_export_obj.obj_access_count);
+ spin_lock(&hba->device_lock);
+ dev->export_count++;
+ spin_unlock(&hba->device_lock);
+
core_export_port(dev, tpg, port, lun);
return 0;
}
@@ -633,6 +598,7 @@ void core_dev_unexport(
struct se_portal_group *tpg,
struct se_lun *lun)
{
+ struct se_hba *hba = dev->se_hba;
struct se_port *port = lun->lun_sep;
spin_lock(&lun->lun_sep_lock);
@@ -643,327 +609,55 @@ void core_dev_unexport(
spin_unlock(&lun->lun_sep_lock);
spin_lock(&dev->se_port_lock);
- atomic_dec(&dev->dev_export_obj.obj_access_count);
core_release_port(dev, port);
spin_unlock(&dev->se_port_lock);
- se_dev_stop(dev);
- lun->lun_se_dev = NULL;
-}
-
-int transport_core_report_lun_response(struct se_cmd *se_cmd)
-{
- struct se_dev_entry *deve;
- struct se_lun *se_lun;
- struct se_session *se_sess = SE_SESS(se_cmd);
- struct se_task *se_task;
- unsigned char *buf = (unsigned char *)T_TASK(se_cmd)->t_task_buf;
- u32 cdb_offset = 0, lun_count = 0, offset = 8;
- u64 i, lun;
-
- list_for_each_entry(se_task, &T_TASK(se_cmd)->t_task_list, t_list)
- break;
-
- if (!(se_task)) {
- printk(KERN_ERR "Unable to locate struct se_task for struct se_cmd\n");
- return PYX_TRANSPORT_LU_COMM_FAILURE;
- }
-
- /*
- * If no struct se_session pointer is present, this struct se_cmd is
- * coming via a target_core_mod PASSTHROUGH op, and not through
- * a $FABRIC_MOD. In that case, report LUN=0 only.
- */
- if (!(se_sess)) {
- lun = 0;
- buf[offset++] = ((lun >> 56) & 0xff);
- buf[offset++] = ((lun >> 48) & 0xff);
- buf[offset++] = ((lun >> 40) & 0xff);
- buf[offset++] = ((lun >> 32) & 0xff);
- buf[offset++] = ((lun >> 24) & 0xff);
- buf[offset++] = ((lun >> 16) & 0xff);
- buf[offset++] = ((lun >> 8) & 0xff);
- buf[offset++] = (lun & 0xff);
- lun_count = 1;
- goto done;
- }
-
- spin_lock_irq(&SE_NODE_ACL(se_sess)->device_list_lock);
- for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- deve = &SE_NODE_ACL(se_sess)->device_list[i];
- if (!(deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS))
- continue;
- se_lun = deve->se_lun;
- /*
- * We determine the correct LUN LIST LENGTH even once we
- * have reached the initial allocation length.
- * See SPC2-R20 7.19.
- */
- lun_count++;
- if ((cdb_offset + 8) >= se_cmd->data_length)
- continue;
-
- lun = cpu_to_be64(CMD_TFO(se_cmd)->pack_lun(deve->mapped_lun));
- buf[offset++] = ((lun >> 56) & 0xff);
- buf[offset++] = ((lun >> 48) & 0xff);
- buf[offset++] = ((lun >> 40) & 0xff);
- buf[offset++] = ((lun >> 32) & 0xff);
- buf[offset++] = ((lun >> 24) & 0xff);
- buf[offset++] = ((lun >> 16) & 0xff);
- buf[offset++] = ((lun >> 8) & 0xff);
- buf[offset++] = (lun & 0xff);
- cdb_offset += 8;
- }
- spin_unlock_irq(&SE_NODE_ACL(se_sess)->device_list_lock);
-
- /*
- * See SPC3 r07, page 159.
- */
-done:
- lun_count *= 8;
- buf[0] = ((lun_count >> 24) & 0xff);
- buf[1] = ((lun_count >> 16) & 0xff);
- buf[2] = ((lun_count >> 8) & 0xff);
- buf[3] = (lun_count & 0xff);
-
- return PYX_TRANSPORT_SENT_TO_TRANSPORT;
-}
-
-/* se_release_device_for_hba():
- *
- *
- */
-void se_release_device_for_hba(struct se_device *dev)
-{
- struct se_hba *hba = dev->se_hba;
-
- if ((dev->dev_status & TRANSPORT_DEVICE_ACTIVATED) ||
- (dev->dev_status & TRANSPORT_DEVICE_DEACTIVATED) ||
- (dev->dev_status & TRANSPORT_DEVICE_SHUTDOWN) ||
- (dev->dev_status & TRANSPORT_DEVICE_OFFLINE_ACTIVATED) ||
- (dev->dev_status & TRANSPORT_DEVICE_OFFLINE_DEACTIVATED))
- se_dev_stop(dev);
-
- if (dev->dev_ptr) {
- kthread_stop(dev->process_thread);
- if (dev->transport->free_device)
- dev->transport->free_device(dev->dev_ptr);
- }
-
spin_lock(&hba->device_lock);
- list_del(&dev->dev_list);
- hba->dev_count--;
+ dev->export_count--;
spin_unlock(&hba->device_lock);
- core_scsi3_free_all_registrations(dev);
- se_release_vpd_for_dev(dev);
-
- kfree(dev->dev_status_queue_obj);
- kfree(dev->dev_queue_obj);
- kfree(dev);
-
- return;
+ lun->lun_sep = NULL;
+ lun->lun_se_dev = NULL;
}
-void se_release_vpd_for_dev(struct se_device *dev)
+static void se_release_vpd_for_dev(struct se_device *dev)
{
struct t10_vpd *vpd, *vpd_tmp;
- spin_lock(&DEV_T10_WWN(dev)->t10_vpd_lock);
+ spin_lock(&dev->t10_wwn.t10_vpd_lock);
list_for_each_entry_safe(vpd, vpd_tmp,
- &DEV_T10_WWN(dev)->t10_vpd_list, vpd_list) {
+ &dev->t10_wwn.t10_vpd_list, vpd_list) {
list_del(&vpd->vpd_list);
kfree(vpd);
}
- spin_unlock(&DEV_T10_WWN(dev)->t10_vpd_lock);
-
- return;
+ spin_unlock(&dev->t10_wwn.t10_vpd_lock);
}
-/*
- * Called with struct se_hba->device_lock held.
- */
-void se_clear_dev_ports(struct se_device *dev)
+static u32 se_dev_align_max_sectors(u32 max_sectors, u32 block_size)
{
- struct se_hba *hba = dev->se_hba;
- struct se_lun *lun;
- struct se_portal_group *tpg;
- struct se_port *sep, *sep_tmp;
-
- spin_lock(&dev->se_port_lock);
- list_for_each_entry_safe(sep, sep_tmp, &dev->dev_sep_list, sep_list) {
- spin_unlock(&dev->se_port_lock);
- spin_unlock(&hba->device_lock);
-
- lun = sep->sep_lun;
- tpg = sep->sep_tpg;
- spin_lock(&lun->lun_sep_lock);
- if (lun->lun_se_dev == NULL) {
- spin_unlock(&lun->lun_sep_lock);
- continue;
- }
- spin_unlock(&lun->lun_sep_lock);
-
- core_dev_del_lun(tpg, lun->unpacked_lun);
-
- spin_lock(&hba->device_lock);
- spin_lock(&dev->se_port_lock);
- }
- spin_unlock(&dev->se_port_lock);
-
- return;
-}
-
-/* se_free_virtual_device():
- *
- * Used for IBLOCK, RAMDISK, and FILEIO Transport Drivers.
- */
-int se_free_virtual_device(struct se_device *dev, struct se_hba *hba)
-{
- spin_lock(&hba->device_lock);
- se_clear_dev_ports(dev);
- spin_unlock(&hba->device_lock);
-
- core_alua_free_lu_gp_mem(dev);
- se_release_device_for_hba(dev);
-
- return 0;
-}
-
-static void se_dev_start(struct se_device *dev)
-{
- struct se_hba *hba = dev->se_hba;
-
- spin_lock(&hba->device_lock);
- atomic_inc(&dev->dev_obj.obj_access_count);
- if (atomic_read(&dev->dev_obj.obj_access_count) == 1) {
- if (dev->dev_status & TRANSPORT_DEVICE_DEACTIVATED) {
- dev->dev_status &= ~TRANSPORT_DEVICE_DEACTIVATED;
- dev->dev_status |= TRANSPORT_DEVICE_ACTIVATED;
- } else if (dev->dev_status &
- TRANSPORT_DEVICE_OFFLINE_DEACTIVATED) {
- dev->dev_status &=
- ~TRANSPORT_DEVICE_OFFLINE_DEACTIVATED;
- dev->dev_status |= TRANSPORT_DEVICE_OFFLINE_ACTIVATED;
- }
- }
- spin_unlock(&hba->device_lock);
-}
-
-static void se_dev_stop(struct se_device *dev)
-{
- struct se_hba *hba = dev->se_hba;
-
- spin_lock(&hba->device_lock);
- atomic_dec(&dev->dev_obj.obj_access_count);
- if (atomic_read(&dev->dev_obj.obj_access_count) == 0) {
- if (dev->dev_status & TRANSPORT_DEVICE_ACTIVATED) {
- dev->dev_status &= ~TRANSPORT_DEVICE_ACTIVATED;
- dev->dev_status |= TRANSPORT_DEVICE_DEACTIVATED;
- } else if (dev->dev_status &
- TRANSPORT_DEVICE_OFFLINE_ACTIVATED) {
- dev->dev_status &= ~TRANSPORT_DEVICE_OFFLINE_ACTIVATED;
- dev->dev_status |= TRANSPORT_DEVICE_OFFLINE_DEACTIVATED;
- }
- }
- spin_unlock(&hba->device_lock);
-}
-
-int se_dev_check_online(struct se_device *dev)
-{
- int ret;
-
- spin_lock_irq(&dev->dev_status_lock);
- ret = ((dev->dev_status & TRANSPORT_DEVICE_ACTIVATED) ||
- (dev->dev_status & TRANSPORT_DEVICE_DEACTIVATED)) ? 0 : 1;
- spin_unlock_irq(&dev->dev_status_lock);
-
- return ret;
-}
-
-int se_dev_check_shutdown(struct se_device *dev)
-{
- int ret;
-
- spin_lock_irq(&dev->dev_status_lock);
- ret = (dev->dev_status & TRANSPORT_DEVICE_SHUTDOWN);
- spin_unlock_irq(&dev->dev_status_lock);
-
- return ret;
-}
-
-void se_dev_set_default_attribs(
- struct se_device *dev,
- struct se_dev_limits *dev_limits)
-{
- struct queue_limits *limits = &dev_limits->limits;
-
- DEV_ATTRIB(dev)->emulate_dpo = DA_EMULATE_DPO;
- DEV_ATTRIB(dev)->emulate_fua_write = DA_EMULATE_FUA_WRITE;
- DEV_ATTRIB(dev)->emulate_fua_read = DA_EMULATE_FUA_READ;
- DEV_ATTRIB(dev)->emulate_write_cache = DA_EMULATE_WRITE_CACHE;
- DEV_ATTRIB(dev)->emulate_ua_intlck_ctrl = DA_EMULATE_UA_INTLLCK_CTRL;
- DEV_ATTRIB(dev)->emulate_tas = DA_EMULATE_TAS;
- DEV_ATTRIB(dev)->emulate_tpu = DA_EMULATE_TPU;
- DEV_ATTRIB(dev)->emulate_tpws = DA_EMULATE_TPWS;
- DEV_ATTRIB(dev)->emulate_reservations = DA_EMULATE_RESERVATIONS;
- DEV_ATTRIB(dev)->emulate_alua = DA_EMULATE_ALUA;
- DEV_ATTRIB(dev)->enforce_pr_isids = DA_ENFORCE_PR_ISIDS;
+ u32 aligned_max_sectors;
+ u32 alignment;
/*
- * The TPU=1 and TPWS=1 settings will be set in TCM/IBLOCK
- * iblock_create_virtdevice() from struct queue_limits values
- * if blk_queue_discard()==1
+ * Limit max_sectors to a PAGE_SIZE aligned value for modern
+ * transport_allocate_data_tasks() operation.
*/
- DEV_ATTRIB(dev)->max_unmap_lba_count = DA_MAX_UNMAP_LBA_COUNT;
- DEV_ATTRIB(dev)->max_unmap_block_desc_count =
- DA_MAX_UNMAP_BLOCK_DESC_COUNT;
- DEV_ATTRIB(dev)->unmap_granularity = DA_UNMAP_GRANULARITY_DEFAULT;
- DEV_ATTRIB(dev)->unmap_granularity_alignment =
- DA_UNMAP_GRANULARITY_ALIGNMENT_DEFAULT;
- /*
- * block_size is based on subsystem plugin dependent requirements.
- */
- DEV_ATTRIB(dev)->hw_block_size = limits->logical_block_size;
- DEV_ATTRIB(dev)->block_size = limits->logical_block_size;
- /*
- * max_sectors is based on subsystem plugin dependent requirements.
- */
- DEV_ATTRIB(dev)->hw_max_sectors = limits->max_hw_sectors;
- DEV_ATTRIB(dev)->max_sectors = limits->max_sectors;
- /*
- * Set optimal_sectors from max_sectors, which can be lowered via
- * configfs.
- */
- DEV_ATTRIB(dev)->optimal_sectors = limits->max_sectors;
- /*
- * queue_depth is based on subsystem plugin dependent requirements.
- */
- DEV_ATTRIB(dev)->hw_queue_depth = dev_limits->hw_queue_depth;
- DEV_ATTRIB(dev)->queue_depth = dev_limits->queue_depth;
-}
+ alignment = max(1ul, PAGE_SIZE / block_size);
+ aligned_max_sectors = rounddown(max_sectors, alignment);
-int se_dev_set_task_timeout(struct se_device *dev, u32 task_timeout)
-{
- if (task_timeout > DA_TASK_TIMEOUT_MAX) {
- printk(KERN_ERR "dev[%p]: Passed task_timeout: %u larger then"
- " DA_TASK_TIMEOUT_MAX\n", dev, task_timeout);
- return -1;
- } else {
- DEV_ATTRIB(dev)->task_timeout = task_timeout;
- printk(KERN_INFO "dev[%p]: Set SE Device task_timeout: %u\n",
- dev, task_timeout);
- }
+ if (max_sectors != aligned_max_sectors)
+ pr_info("Rounding down aligned max_sectors from %u to %u\n",
+ max_sectors, aligned_max_sectors);
- return 0;
+ return aligned_max_sectors;
}
int se_dev_set_max_unmap_lba_count(
struct se_device *dev,
u32 max_unmap_lba_count)
{
- DEV_ATTRIB(dev)->max_unmap_lba_count = max_unmap_lba_count;
- printk(KERN_INFO "dev[%p]: Set max_unmap_lba_count: %u\n",
- dev, DEV_ATTRIB(dev)->max_unmap_lba_count);
+ dev->dev_attrib.max_unmap_lba_count = max_unmap_lba_count;
+ pr_debug("dev[%p]: Set max_unmap_lba_count: %u\n",
+ dev, dev->dev_attrib.max_unmap_lba_count);
return 0;
}
@@ -971,9 +665,10 @@ int se_dev_set_max_unmap_block_desc_count(
struct se_device *dev,
u32 max_unmap_block_desc_count)
{
- DEV_ATTRIB(dev)->max_unmap_block_desc_count = max_unmap_block_desc_count;
- printk(KERN_INFO "dev[%p]: Set max_unmap_block_desc_count: %u\n",
- dev, DEV_ATTRIB(dev)->max_unmap_block_desc_count);
+ dev->dev_attrib.max_unmap_block_desc_count =
+ max_unmap_block_desc_count;
+ pr_debug("dev[%p]: Set max_unmap_block_desc_count: %u\n",
+ dev, dev->dev_attrib.max_unmap_block_desc_count);
return 0;
}
@@ -981,9 +676,9 @@ int se_dev_set_unmap_granularity(
struct se_device *dev,
u32 unmap_granularity)
{
- DEV_ATTRIB(dev)->unmap_granularity = unmap_granularity;
- printk(KERN_INFO "dev[%p]: Set unmap_granularity: %u\n",
- dev, DEV_ATTRIB(dev)->unmap_granularity);
+ dev->dev_attrib.unmap_granularity = unmap_granularity;
+ pr_debug("dev[%p]: Set unmap_granularity: %u\n",
+ dev, dev->dev_attrib.unmap_granularity);
return 0;
}
@@ -991,109 +686,147 @@ int se_dev_set_unmap_granularity_alignment(
struct se_device *dev,
u32 unmap_granularity_alignment)
{
- DEV_ATTRIB(dev)->unmap_granularity_alignment = unmap_granularity_alignment;
- printk(KERN_INFO "dev[%p]: Set unmap_granularity_alignment: %u\n",
- dev, DEV_ATTRIB(dev)->unmap_granularity_alignment);
+ dev->dev_attrib.unmap_granularity_alignment = unmap_granularity_alignment;
+ pr_debug("dev[%p]: Set unmap_granularity_alignment: %u\n",
+ dev, dev->dev_attrib.unmap_granularity_alignment);
return 0;
}
-int se_dev_set_emulate_dpo(struct se_device *dev, int flag)
+int se_dev_set_max_write_same_len(
+ struct se_device *dev,
+ u32 max_write_same_len)
{
- if ((flag != 0) && (flag != 1)) {
- printk(KERN_ERR "Illegal value %d\n", flag);
- return -1;
+ dev->dev_attrib.max_write_same_len = max_write_same_len;
+ pr_debug("dev[%p]: Set max_write_same_len: %u\n",
+ dev, dev->dev_attrib.max_write_same_len);
+ return 0;
+}
+
+static void dev_set_t10_wwn_model_alias(struct se_device *dev)
+{
+ const char *configname;
+
+ configname = config_item_name(&dev->dev_group.cg_item);
+ if (strlen(configname) >= 16) {
+ pr_warn("dev[%p]: Backstore name '%s' is too long for "
+ "INQUIRY_MODEL, truncating to 16 bytes\n", dev,
+ configname);
}
- if (TRANSPORT(dev)->dpo_emulated == NULL) {
- printk(KERN_ERR "TRANSPORT(dev)->dpo_emulated is NULL\n");
- return -1;
+ snprintf(&dev->t10_wwn.model[0], 16, "%s", configname);
+}
+
+int se_dev_set_emulate_model_alias(struct se_device *dev, int flag)
+{
+ if (dev->export_count) {
+ pr_err("dev[%p]: Unable to change model alias"
+ " while export_count is %d\n",
+ dev, dev->export_count);
+ return -EINVAL;
+ }
+
+ if (flag != 0 && flag != 1) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
}
- if (TRANSPORT(dev)->dpo_emulated(dev) == 0) {
- printk(KERN_ERR "TRANSPORT(dev)->dpo_emulated not supported\n");
- return -1;
+
+ if (flag) {
+ dev_set_t10_wwn_model_alias(dev);
+ } else {
+ strncpy(&dev->t10_wwn.model[0],
+ dev->transport->inquiry_prod, 16);
}
- DEV_ATTRIB(dev)->emulate_dpo = flag;
- printk(KERN_INFO "dev[%p]: SE Device Page Out (DPO) Emulation"
- " bit: %d\n", dev, DEV_ATTRIB(dev)->emulate_dpo);
+ dev->dev_attrib.emulate_model_alias = flag;
+
return 0;
}
-int se_dev_set_emulate_fua_write(struct se_device *dev, int flag)
+int se_dev_set_emulate_dpo(struct se_device *dev, int flag)
{
- if ((flag != 0) && (flag != 1)) {
- printk(KERN_ERR "Illegal value %d\n", flag);
- return -1;
+ if (flag != 0 && flag != 1) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
}
- if (TRANSPORT(dev)->fua_write_emulated == NULL) {
- printk(KERN_ERR "TRANSPORT(dev)->fua_write_emulated is NULL\n");
- return -1;
+
+ if (flag) {
+ pr_err("dpo_emulated not supported\n");
+ return -EINVAL;
}
- if (TRANSPORT(dev)->fua_write_emulated(dev) == 0) {
- printk(KERN_ERR "TRANSPORT(dev)->fua_write_emulated not supported\n");
- return -1;
+
+ return 0;
+}
+
+int se_dev_set_emulate_fua_write(struct se_device *dev, int flag)
+{
+ if (flag != 0 && flag != 1) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
}
- DEV_ATTRIB(dev)->emulate_fua_write = flag;
- printk(KERN_INFO "dev[%p]: SE Device Forced Unit Access WRITEs: %d\n",
- dev, DEV_ATTRIB(dev)->emulate_fua_write);
+
+ if (flag &&
+ dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) {
+ pr_err("emulate_fua_write not supported for pSCSI\n");
+ return -EINVAL;
+ }
+ dev->dev_attrib.emulate_fua_write = flag;
+ pr_debug("dev[%p]: SE Device Forced Unit Access WRITEs: %d\n",
+ dev, dev->dev_attrib.emulate_fua_write);
return 0;
}
int se_dev_set_emulate_fua_read(struct se_device *dev, int flag)
{
- if ((flag != 0) && (flag != 1)) {
- printk(KERN_ERR "Illegal value %d\n", flag);
- return -1;
- }
- if (TRANSPORT(dev)->fua_read_emulated == NULL) {
- printk(KERN_ERR "TRANSPORT(dev)->fua_read_emulated is NULL\n");
- return -1;
+ if (flag != 0 && flag != 1) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
}
- if (TRANSPORT(dev)->fua_read_emulated(dev) == 0) {
- printk(KERN_ERR "TRANSPORT(dev)->fua_read_emulated not supported\n");
- return -1;
+
+ if (flag) {
+ pr_err("ua read emulated not supported\n");
+ return -EINVAL;
}
- DEV_ATTRIB(dev)->emulate_fua_read = flag;
- printk(KERN_INFO "dev[%p]: SE Device Forced Unit Access READs: %d\n",
- dev, DEV_ATTRIB(dev)->emulate_fua_read);
+
return 0;
}
int se_dev_set_emulate_write_cache(struct se_device *dev, int flag)
{
- if ((flag != 0) && (flag != 1)) {
- printk(KERN_ERR "Illegal value %d\n", flag);
- return -1;
+ if (flag != 0 && flag != 1) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
}
- if (TRANSPORT(dev)->write_cache_emulated == NULL) {
- printk(KERN_ERR "TRANSPORT(dev)->write_cache_emulated is NULL\n");
- return -1;
+ if (flag &&
+ dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) {
+ pr_err("emulate_write_cache not supported for pSCSI\n");
+ return -EINVAL;
}
- if (TRANSPORT(dev)->write_cache_emulated(dev) == 0) {
- printk(KERN_ERR "TRANSPORT(dev)->write_cache_emulated not supported\n");
- return -1;
+ if (flag &&
+ dev->transport->get_write_cache) {
+ pr_err("emulate_write_cache not supported for this device\n");
+ return -EINVAL;
}
- DEV_ATTRIB(dev)->emulate_write_cache = flag;
- printk(KERN_INFO "dev[%p]: SE Device WRITE_CACHE_EMULATION flag: %d\n",
- dev, DEV_ATTRIB(dev)->emulate_write_cache);
+
+ dev->dev_attrib.emulate_write_cache = flag;
+ pr_debug("dev[%p]: SE Device WRITE_CACHE_EMULATION flag: %d\n",
+ dev, dev->dev_attrib.emulate_write_cache);
return 0;
}
int se_dev_set_emulate_ua_intlck_ctrl(struct se_device *dev, int flag)
{
if ((flag != 0) && (flag != 1) && (flag != 2)) {
- printk(KERN_ERR "Illegal value %d\n", flag);
- return -1;
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
}
- if (atomic_read(&dev->dev_export_obj.obj_access_count)) {
- printk(KERN_ERR "dev[%p]: Unable to change SE Device"
- " UA_INTRLCK_CTRL while dev_export_obj: %d count"
- " exists\n", dev,
- atomic_read(&dev->dev_export_obj.obj_access_count));
- return -1;
+ if (dev->export_count) {
+ pr_err("dev[%p]: Unable to change SE Device"
+ " UA_INTRLCK_CTRL while export_count is %d\n",
+ dev, dev->export_count);
+ return -EINVAL;
}
- DEV_ATTRIB(dev)->emulate_ua_intlck_ctrl = flag;
- printk(KERN_INFO "dev[%p]: SE Device UA_INTRLCK_CTRL flag: %d\n",
- dev, DEV_ATTRIB(dev)->emulate_ua_intlck_ctrl);
+ dev->dev_attrib.emulate_ua_intlck_ctrl = flag;
+ pr_debug("dev[%p]: SE Device UA_INTRLCK_CTRL flag: %d\n",
+ dev, dev->dev_attrib.emulate_ua_intlck_ctrl);
return 0;
}
@@ -1101,19 +834,19 @@ int se_dev_set_emulate_ua_intlck_ctrl(struct se_device *dev, int flag)
int se_dev_set_emulate_tas(struct se_device *dev, int flag)
{
if ((flag != 0) && (flag != 1)) {
- printk(KERN_ERR "Illegal value %d\n", flag);
- return -1;
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
}
- if (atomic_read(&dev->dev_export_obj.obj_access_count)) {
- printk(KERN_ERR "dev[%p]: Unable to change SE Device TAS while"
- " dev_export_obj: %d count exists\n", dev,
- atomic_read(&dev->dev_export_obj.obj_access_count));
- return -1;
+ if (dev->export_count) {
+ pr_err("dev[%p]: Unable to change SE Device TAS while"
+ " export_count is %d\n",
+ dev, dev->export_count);
+ return -EINVAL;
}
- DEV_ATTRIB(dev)->emulate_tas = flag;
- printk(KERN_INFO "dev[%p]: SE Device TASK_ABORTED status bit: %s\n",
- dev, (DEV_ATTRIB(dev)->emulate_tas) ? "Enabled" : "Disabled");
+ dev->dev_attrib.emulate_tas = flag;
+ pr_debug("dev[%p]: SE Device TASK_ABORTED status bit: %s\n",
+ dev, (dev->dev_attrib.emulate_tas) ? "Enabled" : "Disabled");
return 0;
}
@@ -1121,20 +854,20 @@ int se_dev_set_emulate_tas(struct se_device *dev, int flag)
int se_dev_set_emulate_tpu(struct se_device *dev, int flag)
{
if ((flag != 0) && (flag != 1)) {
- printk(KERN_ERR "Illegal value %d\n", flag);
- return -1;
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
}
/*
* We expect this value to be non-zero when generic Block Layer
* Discard supported is detected iblock_create_virtdevice().
*/
- if (!(DEV_ATTRIB(dev)->max_unmap_block_desc_count)) {
- printk(KERN_ERR "Generic Block Discard not supported\n");
+ if (flag && !dev->dev_attrib.max_unmap_block_desc_count) {
+ pr_err("Generic Block Discard not supported\n");
return -ENOSYS;
}
- DEV_ATTRIB(dev)->emulate_tpu = flag;
- printk(KERN_INFO "dev[%p]: SE Device Thin Provisioning UNMAP bit: %d\n",
+ dev->dev_attrib.emulate_tpu = flag;
+ pr_debug("dev[%p]: SE Device Thin Provisioning UNMAP bit: %d\n",
dev, flag);
return 0;
}
@@ -1142,33 +875,171 @@ int se_dev_set_emulate_tpu(struct se_device *dev, int flag)
int se_dev_set_emulate_tpws(struct se_device *dev, int flag)
{
if ((flag != 0) && (flag != 1)) {
- printk(KERN_ERR "Illegal value %d\n", flag);
- return -1;
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
}
/*
* We expect this value to be non-zero when generic Block Layer
* Discard supported is detected iblock_create_virtdevice().
*/
- if (!(DEV_ATTRIB(dev)->max_unmap_block_desc_count)) {
- printk(KERN_ERR "Generic Block Discard not supported\n");
+ if (flag && !dev->dev_attrib.max_unmap_block_desc_count) {
+ pr_err("Generic Block Discard not supported\n");
return -ENOSYS;
}
- DEV_ATTRIB(dev)->emulate_tpws = flag;
- printk(KERN_INFO "dev[%p]: SE Device Thin Provisioning WRITE_SAME: %d\n",
+ dev->dev_attrib.emulate_tpws = flag;
+ pr_debug("dev[%p]: SE Device Thin Provisioning WRITE_SAME: %d\n",
dev, flag);
return 0;
}
+int se_dev_set_emulate_caw(struct se_device *dev, int flag)
+{
+ if (flag != 0 && flag != 1) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
+ }
+ dev->dev_attrib.emulate_caw = flag;
+ pr_debug("dev[%p]: SE Device CompareAndWrite (AtomicTestandSet): %d\n",
+ dev, flag);
+
+ return 0;
+}
+
+int se_dev_set_emulate_3pc(struct se_device *dev, int flag)
+{
+ if (flag != 0 && flag != 1) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
+ }
+ dev->dev_attrib.emulate_3pc = flag;
+ pr_debug("dev[%p]: SE Device 3rd Party Copy (EXTENDED_COPY): %d\n",
+ dev, flag);
+
+ return 0;
+}
+
+int se_dev_set_pi_prot_type(struct se_device *dev, int flag)
+{
+ int rc, old_prot = dev->dev_attrib.pi_prot_type;
+
+ if (flag != 0 && flag != 1 && flag != 2 && flag != 3) {
+ pr_err("Illegal value %d for pi_prot_type\n", flag);
+ return -EINVAL;
+ }
+ if (flag == 2) {
+ pr_err("DIF TYPE2 protection currently not supported\n");
+ return -ENOSYS;
+ }
+ if (dev->dev_attrib.hw_pi_prot_type) {
+ pr_warn("DIF protection enabled on underlying hardware,"
+ " ignoring\n");
+ return 0;
+ }
+ if (!dev->transport->init_prot || !dev->transport->free_prot) {
+ /* 0 is only allowed value for non-supporting backends */
+ if (flag == 0)
+ return 0;
+
+ pr_err("DIF protection not supported by backend: %s\n",
+ dev->transport->name);
+ return -ENOSYS;
+ }
+ if (!(dev->dev_flags & DF_CONFIGURED)) {
+ pr_err("DIF protection requires device to be configured\n");
+ return -ENODEV;
+ }
+ if (dev->export_count) {
+ pr_err("dev[%p]: Unable to change SE Device PROT type while"
+ " export_count is %d\n", dev, dev->export_count);
+ return -EINVAL;
+ }
+
+ dev->dev_attrib.pi_prot_type = flag;
+
+ if (flag && !old_prot) {
+ rc = dev->transport->init_prot(dev);
+ if (rc) {
+ dev->dev_attrib.pi_prot_type = old_prot;
+ return rc;
+ }
+
+ } else if (!flag && old_prot) {
+ dev->transport->free_prot(dev);
+ }
+ pr_debug("dev[%p]: SE Device Protection Type: %d\n", dev, flag);
+
+ return 0;
+}
+
+int se_dev_set_pi_prot_format(struct se_device *dev, int flag)
+{
+ int rc;
+
+ if (!flag)
+ return 0;
+
+ if (flag != 1) {
+ pr_err("Illegal value %d for pi_prot_format\n", flag);
+ return -EINVAL;
+ }
+ if (!dev->transport->format_prot) {
+ pr_err("DIF protection format not supported by backend %s\n",
+ dev->transport->name);
+ return -ENOSYS;
+ }
+ if (!(dev->dev_flags & DF_CONFIGURED)) {
+ pr_err("DIF protection format requires device to be configured\n");
+ return -ENODEV;
+ }
+ if (dev->export_count) {
+ pr_err("dev[%p]: Unable to format SE Device PROT type while"
+ " export_count is %d\n", dev, dev->export_count);
+ return -EINVAL;
+ }
+
+ rc = dev->transport->format_prot(dev);
+ if (rc)
+ return rc;
+
+ pr_debug("dev[%p]: SE Device Protection Format complete\n", dev);
+
+ return 0;
+}
+
int se_dev_set_enforce_pr_isids(struct se_device *dev, int flag)
{
if ((flag != 0) && (flag != 1)) {
+ pr_err("Illegal value %d\n", flag);
+ return -EINVAL;
+ }
+ dev->dev_attrib.enforce_pr_isids = flag;
+ pr_debug("dev[%p]: SE Device enforce_pr_isids bit: %s\n", dev,
+ (dev->dev_attrib.enforce_pr_isids) ? "Enabled" : "Disabled");
+ return 0;
+}
+
+int se_dev_set_is_nonrot(struct se_device *dev, int flag)
+{
+ if ((flag != 0) && (flag != 1)) {
printk(KERN_ERR "Illegal value %d\n", flag);
- return -1;
+ return -EINVAL;
+ }
+ dev->dev_attrib.is_nonrot = flag;
+ pr_debug("dev[%p]: SE Device is_nonrot bit: %d\n",
+ dev, flag);
+ return 0;
+}
+
+int se_dev_set_emulate_rest_reord(struct se_device *dev, int flag)
+{
+ if (flag != 0) {
+ printk(KERN_ERR "dev[%p]: SE Device emulatation of restricted"
+ " reordering not implemented\n", dev);
+ return -ENOSYS;
}
- DEV_ATTRIB(dev)->enforce_pr_isids = flag;
- printk(KERN_INFO "dev[%p]: SE Device enforce_pr_isids bit: %s\n", dev,
- (DEV_ATTRIB(dev)->enforce_pr_isids) ? "Enabled" : "Disabled");
+ dev->dev_attrib.emulate_rest_reord = flag;
+ pr_debug("dev[%p]: SE Device emulate_rest_reord: %d\n", dev, flag);
return 0;
}
@@ -1177,211 +1048,202 @@ int se_dev_set_enforce_pr_isids(struct se_device *dev, int flag)
*/
int se_dev_set_queue_depth(struct se_device *dev, u32 queue_depth)
{
- u32 orig_queue_depth = dev->queue_depth;
-
- if (atomic_read(&dev->dev_export_obj.obj_access_count)) {
- printk(KERN_ERR "dev[%p]: Unable to change SE Device TCQ while"
- " dev_export_obj: %d count exists\n", dev,
- atomic_read(&dev->dev_export_obj.obj_access_count));
- return -1;
+ if (dev->export_count) {
+ pr_err("dev[%p]: Unable to change SE Device TCQ while"
+ " export_count is %d\n",
+ dev, dev->export_count);
+ return -EINVAL;
}
- if (!(queue_depth)) {
- printk(KERN_ERR "dev[%p]: Illegal ZERO value for queue"
+ if (!queue_depth) {
+ pr_err("dev[%p]: Illegal ZERO value for queue"
"_depth\n", dev);
- return -1;
+ return -EINVAL;
}
- if (TRANSPORT(dev)->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) {
- if (queue_depth > DEV_ATTRIB(dev)->hw_queue_depth) {
- printk(KERN_ERR "dev[%p]: Passed queue_depth: %u"
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) {
+ if (queue_depth > dev->dev_attrib.hw_queue_depth) {
+ pr_err("dev[%p]: Passed queue_depth: %u"
" exceeds TCM/SE_Device TCQ: %u\n",
dev, queue_depth,
- DEV_ATTRIB(dev)->hw_queue_depth);
- return -1;
+ dev->dev_attrib.hw_queue_depth);
+ return -EINVAL;
}
} else {
- if (queue_depth > DEV_ATTRIB(dev)->queue_depth) {
- if (queue_depth > DEV_ATTRIB(dev)->hw_queue_depth) {
- printk(KERN_ERR "dev[%p]: Passed queue_depth:"
+ if (queue_depth > dev->dev_attrib.queue_depth) {
+ if (queue_depth > dev->dev_attrib.hw_queue_depth) {
+ pr_err("dev[%p]: Passed queue_depth:"
" %u exceeds TCM/SE_Device MAX"
" TCQ: %u\n", dev, queue_depth,
- DEV_ATTRIB(dev)->hw_queue_depth);
- return -1;
+ dev->dev_attrib.hw_queue_depth);
+ return -EINVAL;
}
}
}
- DEV_ATTRIB(dev)->queue_depth = dev->queue_depth = queue_depth;
- if (queue_depth > orig_queue_depth)
- atomic_add(queue_depth - orig_queue_depth, &dev->depth_left);
- else if (queue_depth < orig_queue_depth)
- atomic_sub(orig_queue_depth - queue_depth, &dev->depth_left);
-
- printk(KERN_INFO "dev[%p]: SE Device TCQ Depth changed to: %u\n",
+ dev->dev_attrib.queue_depth = dev->queue_depth = queue_depth;
+ pr_debug("dev[%p]: SE Device TCQ Depth changed to: %u\n",
dev, queue_depth);
return 0;
}
-int se_dev_set_max_sectors(struct se_device *dev, u32 max_sectors)
+int se_dev_set_fabric_max_sectors(struct se_device *dev, u32 fabric_max_sectors)
{
- int force = 0; /* Force setting for VDEVS */
-
- if (atomic_read(&dev->dev_export_obj.obj_access_count)) {
- printk(KERN_ERR "dev[%p]: Unable to change SE Device"
- " max_sectors while dev_export_obj: %d count exists\n",
- dev, atomic_read(&dev->dev_export_obj.obj_access_count));
- return -1;
- }
- if (!(max_sectors)) {
- printk(KERN_ERR "dev[%p]: Illegal ZERO value for"
- " max_sectors\n", dev);
- return -1;
- }
- if (max_sectors < DA_STATUS_MAX_SECTORS_MIN) {
- printk(KERN_ERR "dev[%p]: Passed max_sectors: %u less than"
- " DA_STATUS_MAX_SECTORS_MIN: %u\n", dev, max_sectors,
+ int block_size = dev->dev_attrib.block_size;
+
+ if (dev->export_count) {
+ pr_err("dev[%p]: Unable to change SE Device"
+ " fabric_max_sectors while export_count is %d\n",
+ dev, dev->export_count);
+ return -EINVAL;
+ }
+ if (!fabric_max_sectors) {
+ pr_err("dev[%p]: Illegal ZERO value for"
+ " fabric_max_sectors\n", dev);
+ return -EINVAL;
+ }
+ if (fabric_max_sectors < DA_STATUS_MAX_SECTORS_MIN) {
+ pr_err("dev[%p]: Passed fabric_max_sectors: %u less than"
+ " DA_STATUS_MAX_SECTORS_MIN: %u\n", dev, fabric_max_sectors,
DA_STATUS_MAX_SECTORS_MIN);
- return -1;
+ return -EINVAL;
}
- if (TRANSPORT(dev)->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) {
- if (max_sectors > DEV_ATTRIB(dev)->hw_max_sectors) {
- printk(KERN_ERR "dev[%p]: Passed max_sectors: %u"
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) {
+ if (fabric_max_sectors > dev->dev_attrib.hw_max_sectors) {
+ pr_err("dev[%p]: Passed fabric_max_sectors: %u"
" greater than TCM/SE_Device max_sectors:"
- " %u\n", dev, max_sectors,
- DEV_ATTRIB(dev)->hw_max_sectors);
- return -1;
+ " %u\n", dev, fabric_max_sectors,
+ dev->dev_attrib.hw_max_sectors);
+ return -EINVAL;
}
} else {
- if (!(force) && (max_sectors >
- DEV_ATTRIB(dev)->hw_max_sectors)) {
- printk(KERN_ERR "dev[%p]: Passed max_sectors: %u"
- " greater than TCM/SE_Device max_sectors"
- ": %u, use force=1 to override.\n", dev,
- max_sectors, DEV_ATTRIB(dev)->hw_max_sectors);
- return -1;
- }
- if (max_sectors > DA_STATUS_MAX_SECTORS_MAX) {
- printk(KERN_ERR "dev[%p]: Passed max_sectors: %u"
+ if (fabric_max_sectors > DA_STATUS_MAX_SECTORS_MAX) {
+ pr_err("dev[%p]: Passed fabric_max_sectors: %u"
" greater than DA_STATUS_MAX_SECTORS_MAX:"
- " %u\n", dev, max_sectors,
+ " %u\n", dev, fabric_max_sectors,
DA_STATUS_MAX_SECTORS_MAX);
- return -1;
+ return -EINVAL;
}
}
+ /*
+ * Align max_sectors down to PAGE_SIZE to follow transport_allocate_data_tasks()
+ */
+ if (!block_size) {
+ block_size = 512;
+ pr_warn("Defaulting to 512 for zero block_size\n");
+ }
+ fabric_max_sectors = se_dev_align_max_sectors(fabric_max_sectors,
+ block_size);
- DEV_ATTRIB(dev)->max_sectors = max_sectors;
- printk("dev[%p]: SE Device max_sectors changed to %u\n",
- dev, max_sectors);
+ dev->dev_attrib.fabric_max_sectors = fabric_max_sectors;
+ pr_debug("dev[%p]: SE Device max_sectors changed to %u\n",
+ dev, fabric_max_sectors);
return 0;
}
int se_dev_set_optimal_sectors(struct se_device *dev, u32 optimal_sectors)
{
- if (atomic_read(&dev->dev_export_obj.obj_access_count)) {
- printk(KERN_ERR "dev[%p]: Unable to change SE Device"
- " optimal_sectors while dev_export_obj: %d count exists\n",
- dev, atomic_read(&dev->dev_export_obj.obj_access_count));
+ if (dev->export_count) {
+ pr_err("dev[%p]: Unable to change SE Device"
+ " optimal_sectors while export_count is %d\n",
+ dev, dev->export_count);
return -EINVAL;
}
- if (TRANSPORT(dev)->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) {
- printk(KERN_ERR "dev[%p]: Passed optimal_sectors cannot be"
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) {
+ pr_err("dev[%p]: Passed optimal_sectors cannot be"
" changed for TCM/pSCSI\n", dev);
return -EINVAL;
}
- if (optimal_sectors > DEV_ATTRIB(dev)->max_sectors) {
- printk(KERN_ERR "dev[%p]: Passed optimal_sectors %u cannot be"
- " greater than max_sectors: %u\n", dev,
- optimal_sectors, DEV_ATTRIB(dev)->max_sectors);
+ if (optimal_sectors > dev->dev_attrib.fabric_max_sectors) {
+ pr_err("dev[%p]: Passed optimal_sectors %u cannot be"
+ " greater than fabric_max_sectors: %u\n", dev,
+ optimal_sectors, dev->dev_attrib.fabric_max_sectors);
return -EINVAL;
}
- DEV_ATTRIB(dev)->optimal_sectors = optimal_sectors;
- printk(KERN_INFO "dev[%p]: SE Device optimal_sectors changed to %u\n",
+ dev->dev_attrib.optimal_sectors = optimal_sectors;
+ pr_debug("dev[%p]: SE Device optimal_sectors changed to %u\n",
dev, optimal_sectors);
return 0;
}
int se_dev_set_block_size(struct se_device *dev, u32 block_size)
{
- if (atomic_read(&dev->dev_export_obj.obj_access_count)) {
- printk(KERN_ERR "dev[%p]: Unable to change SE Device block_size"
- " while dev_export_obj: %d count exists\n", dev,
- atomic_read(&dev->dev_export_obj.obj_access_count));
- return -1;
+ if (dev->export_count) {
+ pr_err("dev[%p]: Unable to change SE Device block_size"
+ " while export_count is %d\n",
+ dev, dev->export_count);
+ return -EINVAL;
}
if ((block_size != 512) &&
(block_size != 1024) &&
(block_size != 2048) &&
(block_size != 4096)) {
- printk(KERN_ERR "dev[%p]: Illegal value for block_device: %u"
+ pr_err("dev[%p]: Illegal value for block_device: %u"
" for SE device, must be 512, 1024, 2048 or 4096\n",
dev, block_size);
- return -1;
+ return -EINVAL;
}
- if (TRANSPORT(dev)->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) {
- printk(KERN_ERR "dev[%p]: Not allowed to change block_size for"
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) {
+ pr_err("dev[%p]: Not allowed to change block_size for"
" Physical Device, use for Linux/SCSI to change"
" block_size for underlying hardware\n", dev);
- return -1;
+ return -EINVAL;
}
- DEV_ATTRIB(dev)->block_size = block_size;
- printk(KERN_INFO "dev[%p]: SE Device block_size changed to %u\n",
+ dev->dev_attrib.block_size = block_size;
+ pr_debug("dev[%p]: SE Device block_size changed to %u\n",
dev, block_size);
+
+ if (dev->dev_attrib.max_bytes_per_io)
+ dev->dev_attrib.hw_max_sectors =
+ dev->dev_attrib.max_bytes_per_io / block_size;
+
return 0;
}
struct se_lun *core_dev_add_lun(
struct se_portal_group *tpg,
- struct se_hba *hba,
struct se_device *dev,
- u32 lun)
+ u32 unpacked_lun)
{
- struct se_lun *lun_p;
- u32 lun_access = 0;
-
- if (atomic_read(&dev->dev_access_obj.obj_access_count) != 0) {
- printk(KERN_ERR "Unable to export struct se_device while dev_access_obj: %d\n",
- atomic_read(&dev->dev_access_obj.obj_access_count));
- return NULL;
- }
+ struct se_lun *lun;
+ int rc;
- lun_p = core_tpg_pre_addlun(tpg, lun);
- if ((IS_ERR(lun_p)) || !(lun_p))
- return NULL;
+ lun = core_tpg_alloc_lun(tpg, unpacked_lun);
+ if (IS_ERR(lun))
+ return lun;
- if (dev->dev_flags & DF_READ_ONLY)
- lun_access = TRANSPORT_LUNFLAGS_READ_ONLY;
- else
- lun_access = TRANSPORT_LUNFLAGS_READ_WRITE;
+ rc = core_tpg_add_lun(tpg, lun,
+ TRANSPORT_LUNFLAGS_READ_WRITE, dev);
+ if (rc < 0)
+ return ERR_PTR(rc);
- if (core_tpg_post_addlun(tpg, lun_p, lun_access, dev) < 0)
- return NULL;
-
- printk(KERN_INFO "%s_TPG[%u]_LUN[%u] - Activated %s Logical Unit from"
- " CORE HBA: %u\n", TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_tag(tpg), lun_p->unpacked_lun,
- TPG_TFO(tpg)->get_fabric_name(), hba->hba_id);
+ pr_debug("%s_TPG[%u]_LUN[%u] - Activated %s Logical Unit from"
+ " CORE HBA: %u\n", tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg), lun->unpacked_lun,
+ tpg->se_tpg_tfo->get_fabric_name(), dev->se_hba->hba_id);
/*
* Update LUN maps for dynamically added initiators when
* generate_node_acl is enabled.
*/
- if (TPG_TFO(tpg)->tpg_check_demo_mode(tpg)) {
+ if (tpg->se_tpg_tfo->tpg_check_demo_mode(tpg)) {
struct se_node_acl *acl;
- spin_lock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
list_for_each_entry(acl, &tpg->acl_node_list, acl_list) {
- if (acl->dynamic_node_acl) {
- spin_unlock_bh(&tpg->acl_node_lock);
+ if (acl->dynamic_node_acl &&
+ (!tpg->se_tpg_tfo->tpg_check_demo_mode_login_only ||
+ !tpg->se_tpg_tfo->tpg_check_demo_mode_login_only(tpg))) {
+ spin_unlock_irq(&tpg->acl_node_lock);
core_tpg_add_node_to_devs(acl, tpg);
- spin_lock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
}
}
- spin_unlock_bh(&tpg->acl_node_lock);
+ spin_unlock_irq(&tpg->acl_node_lock);
}
- return lun_p;
+ return lun;
}
/* core_dev_del_lun():
@@ -1393,18 +1255,17 @@ int core_dev_del_lun(
u32 unpacked_lun)
{
struct se_lun *lun;
- int ret = 0;
- lun = core_tpg_pre_dellun(tpg, unpacked_lun, &ret);
- if (!(lun))
- return ret;
+ lun = core_tpg_pre_dellun(tpg, unpacked_lun);
+ if (IS_ERR(lun))
+ return PTR_ERR(lun);
core_tpg_post_dellun(tpg, lun);
- printk(KERN_INFO "%s_TPG[%u]_LUN[%u] - Deactivated %s Logical Unit from"
- " device object\n", TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_tag(tpg), unpacked_lun,
- TPG_TFO(tpg)->get_fabric_name());
+ pr_debug("%s_TPG[%u]_LUN[%u] - Deactivated %s Logical Unit from"
+ " device object\n", tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg), unpacked_lun,
+ tpg->se_tpg_tfo->get_fabric_name());
return 0;
}
@@ -1415,21 +1276,21 @@ struct se_lun *core_get_lun_from_tpg(struct se_portal_group *tpg, u32 unpacked_l
spin_lock(&tpg->tpg_lun_lock);
if (unpacked_lun > (TRANSPORT_MAX_LUNS_PER_TPG-1)) {
- printk(KERN_ERR "%s LUN: %u exceeds TRANSPORT_MAX_LUNS"
+ pr_err("%s LUN: %u exceeds TRANSPORT_MAX_LUNS"
"_PER_TPG-1: %u for Target Portal Group: %hu\n",
- TPG_TFO(tpg)->get_fabric_name(), unpacked_lun,
+ tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun,
TRANSPORT_MAX_LUNS_PER_TPG-1,
- TPG_TFO(tpg)->tpg_get_tag(tpg));
+ tpg->se_tpg_tfo->tpg_get_tag(tpg));
spin_unlock(&tpg->tpg_lun_lock);
return NULL;
}
- lun = &tpg->tpg_lun_list[unpacked_lun];
+ lun = tpg->tpg_lun_list[unpacked_lun];
if (lun->lun_status != TRANSPORT_LUN_STATUS_FREE) {
- printk(KERN_ERR "%s Logical Unit Number: %u is not free on"
+ pr_err("%s Logical Unit Number: %u is not free on"
" Target Portal Group: %hu, ignoring request.\n",
- TPG_TFO(tpg)->get_fabric_name(), unpacked_lun,
- TPG_TFO(tpg)->tpg_get_tag(tpg));
+ tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun,
+ tpg->se_tpg_tfo->tpg_get_tag(tpg));
spin_unlock(&tpg->tpg_lun_lock);
return NULL;
}
@@ -1448,21 +1309,21 @@ static struct se_lun *core_dev_get_lun(struct se_portal_group *tpg, u32 unpacked
spin_lock(&tpg->tpg_lun_lock);
if (unpacked_lun > (TRANSPORT_MAX_LUNS_PER_TPG-1)) {
- printk(KERN_ERR "%s LUN: %u exceeds TRANSPORT_MAX_LUNS_PER"
+ pr_err("%s LUN: %u exceeds TRANSPORT_MAX_LUNS_PER"
"_TPG-1: %u for Target Portal Group: %hu\n",
- TPG_TFO(tpg)->get_fabric_name(), unpacked_lun,
+ tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun,
TRANSPORT_MAX_LUNS_PER_TPG-1,
- TPG_TFO(tpg)->tpg_get_tag(tpg));
+ tpg->se_tpg_tfo->tpg_get_tag(tpg));
spin_unlock(&tpg->tpg_lun_lock);
return NULL;
}
- lun = &tpg->tpg_lun_list[unpacked_lun];
+ lun = tpg->tpg_lun_list[unpacked_lun];
if (lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE) {
- printk(KERN_ERR "%s Logical Unit Number: %u is not active on"
+ pr_err("%s Logical Unit Number: %u is not active on"
" Target Portal Group: %hu, ignoring request.\n",
- TPG_TFO(tpg)->get_fabric_name(), unpacked_lun,
- TPG_TFO(tpg)->tpg_get_tag(tpg));
+ tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun,
+ tpg->se_tpg_tfo->tpg_get_tag(tpg));
spin_unlock(&tpg->tpg_lun_lock);
return NULL;
}
@@ -1473,27 +1334,21 @@ static struct se_lun *core_dev_get_lun(struct se_portal_group *tpg, u32 unpacked
struct se_lun_acl *core_dev_init_initiator_node_lun_acl(
struct se_portal_group *tpg,
+ struct se_node_acl *nacl,
u32 mapped_lun,
- char *initiatorname,
int *ret)
{
struct se_lun_acl *lacl;
- struct se_node_acl *nacl;
- if (strlen(initiatorname) > TRANSPORT_IQN_LEN) {
- printk(KERN_ERR "%s InitiatorName exceeds maximum size.\n",
- TPG_TFO(tpg)->get_fabric_name());
+ if (strlen(nacl->initiatorname) >= TRANSPORT_IQN_LEN) {
+ pr_err("%s InitiatorName exceeds maximum size.\n",
+ tpg->se_tpg_tfo->get_fabric_name());
*ret = -EOVERFLOW;
return NULL;
}
- nacl = core_tpg_get_initiator_node_acl(tpg, initiatorname);
- if (!(nacl)) {
- *ret = -EINVAL;
- return NULL;
- }
lacl = kzalloc(sizeof(struct se_lun_acl), GFP_KERNEL);
- if (!(lacl)) {
- printk(KERN_ERR "Unable to allocate memory for struct se_lun_acl.\n");
+ if (!lacl) {
+ pr_err("Unable to allocate memory for struct se_lun_acl.\n");
*ret = -ENOMEM;
return NULL;
}
@@ -1501,7 +1356,8 @@ struct se_lun_acl *core_dev_init_initiator_node_lun_acl(
INIT_LIST_HEAD(&lacl->lacl_list);
lacl->mapped_lun = mapped_lun;
lacl->se_lun_nacl = nacl;
- snprintf(lacl->initiatorname, TRANSPORT_IQN_LEN, "%s", initiatorname);
+ snprintf(lacl->initiatorname, TRANSPORT_IQN_LEN, "%s",
+ nacl->initiatorname);
return lacl;
}
@@ -1516,16 +1372,16 @@ int core_dev_add_initiator_node_lun_acl(
struct se_node_acl *nacl;
lun = core_dev_get_lun(tpg, unpacked_lun);
- if (!(lun)) {
- printk(KERN_ERR "%s Logical Unit Number: %u is not active on"
+ if (!lun) {
+ pr_err("%s Logical Unit Number: %u is not active on"
" Target Portal Group: %hu, ignoring request.\n",
- TPG_TFO(tpg)->get_fabric_name(), unpacked_lun,
- TPG_TFO(tpg)->tpg_get_tag(tpg));
+ tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun,
+ tpg->se_tpg_tfo->tpg_get_tag(tpg));
return -EINVAL;
}
nacl = lacl->se_lun_nacl;
- if (!(nacl))
+ if (!nacl)
return -EINVAL;
if ((lun->lun_access & TRANSPORT_LUNFLAGS_READ_ONLY) &&
@@ -1534,19 +1390,19 @@ int core_dev_add_initiator_node_lun_acl(
lacl->se_lun = lun;
- if (core_update_device_list_for_node(lun, lacl, lacl->mapped_lun,
- lun_access, nacl, tpg, 1) < 0)
+ if (core_enable_device_list_for_node(lun, lacl, lacl->mapped_lun,
+ lun_access, nacl, tpg) < 0)
return -EINVAL;
spin_lock(&lun->lun_acl_lock);
list_add_tail(&lacl->lacl_list, &lun->lun_acl_list);
atomic_inc(&lun->lun_acl_count);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
spin_unlock(&lun->lun_acl_lock);
- printk(KERN_INFO "%s_TPG[%hu]_LUN[%u->%u] - Added %s ACL for "
- " InitiatorNode: %s\n", TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_tag(tpg), unpacked_lun, lacl->mapped_lun,
+ pr_debug("%s_TPG[%hu]_LUN[%u->%u] - Added %s ACL for "
+ " InitiatorNode: %s\n", tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg), unpacked_lun, lacl->mapped_lun,
(lun_access & TRANSPORT_LUNFLAGS_READ_WRITE) ? "RW" : "RO",
lacl->initiatorname);
/*
@@ -1569,24 +1425,24 @@ int core_dev_del_initiator_node_lun_acl(
struct se_node_acl *nacl;
nacl = lacl->se_lun_nacl;
- if (!(nacl))
+ if (!nacl)
return -EINVAL;
spin_lock(&lun->lun_acl_lock);
list_del(&lacl->lacl_list);
atomic_dec(&lun->lun_acl_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
spin_unlock(&lun->lun_acl_lock);
- core_update_device_list_for_node(lun, NULL, lacl->mapped_lun,
- TRANSPORT_LUNFLAGS_NO_ACCESS, nacl, tpg, 0);
+ core_disable_device_list_for_node(lun, NULL, lacl->mapped_lun,
+ TRANSPORT_LUNFLAGS_NO_ACCESS, nacl, tpg);
lacl->se_lun = NULL;
- printk(KERN_INFO "%s_TPG[%hu]_LUN[%u] - Removed ACL for"
+ pr_debug("%s_TPG[%hu]_LUN[%u] - Removed ACL for"
" InitiatorNode: %s Mapped LUN: %u\n",
- TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_tag(tpg), lun->unpacked_lun,
+ tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg), lun->unpacked_lun,
lacl->initiatorname, lacl->mapped_lun);
return 0;
@@ -1596,98 +1452,288 @@ void core_dev_free_initiator_node_lun_acl(
struct se_portal_group *tpg,
struct se_lun_acl *lacl)
{
- printk("%s_TPG[%hu] - Freeing ACL for %s InitiatorNode: %s"
- " Mapped LUN: %u\n", TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_tag(tpg),
- TPG_TFO(tpg)->get_fabric_name(),
+ pr_debug("%s_TPG[%hu] - Freeing ACL for %s InitiatorNode: %s"
+ " Mapped LUN: %u\n", tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg),
+ tpg->se_tpg_tfo->get_fabric_name(),
lacl->initiatorname, lacl->mapped_lun);
kfree(lacl);
}
-int core_dev_setup_virtual_lun0(void)
+static void scsi_dump_inquiry(struct se_device *dev)
+{
+ struct t10_wwn *wwn = &dev->t10_wwn;
+ char buf[17];
+ int i, device_type;
+ /*
+ * Print Linux/SCSI style INQUIRY formatting to the kernel ring buffer
+ */
+ for (i = 0; i < 8; i++)
+ if (wwn->vendor[i] >= 0x20)
+ buf[i] = wwn->vendor[i];
+ else
+ buf[i] = ' ';
+ buf[i] = '\0';
+ pr_debug(" Vendor: %s\n", buf);
+
+ for (i = 0; i < 16; i++)
+ if (wwn->model[i] >= 0x20)
+ buf[i] = wwn->model[i];
+ else
+ buf[i] = ' ';
+ buf[i] = '\0';
+ pr_debug(" Model: %s\n", buf);
+
+ for (i = 0; i < 4; i++)
+ if (wwn->revision[i] >= 0x20)
+ buf[i] = wwn->revision[i];
+ else
+ buf[i] = ' ';
+ buf[i] = '\0';
+ pr_debug(" Revision: %s\n", buf);
+
+ device_type = dev->transport->get_device_type(dev);
+ pr_debug(" Type: %s ", scsi_device_type(device_type));
+}
+
+struct se_device *target_alloc_device(struct se_hba *hba, const char *name)
{
- struct se_hba *hba;
struct se_device *dev;
- struct se_subsystem_dev *se_dev = NULL;
- struct se_subsystem_api *t;
- char buf[16];
+ struct se_lun *xcopy_lun;
+
+ dev = hba->transport->alloc_device(hba, name);
+ if (!dev)
+ return NULL;
+
+ dev->dev_link_magic = SE_DEV_LINK_MAGIC;
+ dev->se_hba = hba;
+ dev->transport = hba->transport;
+ dev->prot_length = sizeof(struct se_dif_v1_tuple);
+
+ INIT_LIST_HEAD(&dev->dev_list);
+ INIT_LIST_HEAD(&dev->dev_sep_list);
+ INIT_LIST_HEAD(&dev->dev_tmr_list);
+ INIT_LIST_HEAD(&dev->delayed_cmd_list);
+ INIT_LIST_HEAD(&dev->state_list);
+ INIT_LIST_HEAD(&dev->qf_cmd_list);
+ INIT_LIST_HEAD(&dev->g_dev_node);
+ spin_lock_init(&dev->execute_task_lock);
+ spin_lock_init(&dev->delayed_cmd_lock);
+ spin_lock_init(&dev->dev_reservation_lock);
+ spin_lock_init(&dev->se_port_lock);
+ spin_lock_init(&dev->se_tmr_lock);
+ spin_lock_init(&dev->qf_cmd_lock);
+ sema_init(&dev->caw_sem, 1);
+ atomic_set(&dev->dev_ordered_id, 0);
+ INIT_LIST_HEAD(&dev->t10_wwn.t10_vpd_list);
+ spin_lock_init(&dev->t10_wwn.t10_vpd_lock);
+ INIT_LIST_HEAD(&dev->t10_pr.registration_list);
+ INIT_LIST_HEAD(&dev->t10_pr.aptpl_reg_list);
+ spin_lock_init(&dev->t10_pr.registration_lock);
+ spin_lock_init(&dev->t10_pr.aptpl_reg_lock);
+ INIT_LIST_HEAD(&dev->t10_alua.tg_pt_gps_list);
+ spin_lock_init(&dev->t10_alua.tg_pt_gps_lock);
+ INIT_LIST_HEAD(&dev->t10_alua.lba_map_list);
+ spin_lock_init(&dev->t10_alua.lba_map_lock);
+
+ dev->t10_wwn.t10_dev = dev;
+ dev->t10_alua.t10_dev = dev;
+
+ dev->dev_attrib.da_dev = dev;
+ dev->dev_attrib.emulate_model_alias = DA_EMULATE_MODEL_ALIAS;
+ dev->dev_attrib.emulate_dpo = DA_EMULATE_DPO;
+ dev->dev_attrib.emulate_fua_write = DA_EMULATE_FUA_WRITE;
+ dev->dev_attrib.emulate_fua_read = DA_EMULATE_FUA_READ;
+ dev->dev_attrib.emulate_write_cache = DA_EMULATE_WRITE_CACHE;
+ dev->dev_attrib.emulate_ua_intlck_ctrl = DA_EMULATE_UA_INTLLCK_CTRL;
+ dev->dev_attrib.emulate_tas = DA_EMULATE_TAS;
+ dev->dev_attrib.emulate_tpu = DA_EMULATE_TPU;
+ dev->dev_attrib.emulate_tpws = DA_EMULATE_TPWS;
+ dev->dev_attrib.emulate_caw = DA_EMULATE_CAW;
+ dev->dev_attrib.emulate_3pc = DA_EMULATE_3PC;
+ dev->dev_attrib.pi_prot_type = TARGET_DIF_TYPE0_PROT;
+ dev->dev_attrib.enforce_pr_isids = DA_ENFORCE_PR_ISIDS;
+ dev->dev_attrib.is_nonrot = DA_IS_NONROT;
+ dev->dev_attrib.emulate_rest_reord = DA_EMULATE_REST_REORD;
+ dev->dev_attrib.max_unmap_lba_count = DA_MAX_UNMAP_LBA_COUNT;
+ dev->dev_attrib.max_unmap_block_desc_count =
+ DA_MAX_UNMAP_BLOCK_DESC_COUNT;
+ dev->dev_attrib.unmap_granularity = DA_UNMAP_GRANULARITY_DEFAULT;
+ dev->dev_attrib.unmap_granularity_alignment =
+ DA_UNMAP_GRANULARITY_ALIGNMENT_DEFAULT;
+ dev->dev_attrib.max_write_same_len = DA_MAX_WRITE_SAME_LEN;
+ dev->dev_attrib.fabric_max_sectors = DA_FABRIC_MAX_SECTORS;
+ dev->dev_attrib.optimal_sectors = DA_FABRIC_MAX_SECTORS;
+
+ xcopy_lun = &dev->xcopy_lun;
+ xcopy_lun->lun_se_dev = dev;
+ init_completion(&xcopy_lun->lun_shutdown_comp);
+ INIT_LIST_HEAD(&xcopy_lun->lun_acl_list);
+ spin_lock_init(&xcopy_lun->lun_acl_lock);
+ spin_lock_init(&xcopy_lun->lun_sep_lock);
+ init_completion(&xcopy_lun->lun_ref_comp);
+
+ return dev;
+}
+
+int target_configure_device(struct se_device *dev)
+{
+ struct se_hba *hba = dev->se_hba;
int ret;
- hba = core_alloc_hba("rd_dr", 0, HBA_FLAGS_INTERNAL_USE);
- if (IS_ERR(hba))
- return PTR_ERR(hba);
+ if (dev->dev_flags & DF_CONFIGURED) {
+ pr_err("se_dev->se_dev_ptr already set for storage"
+ " object\n");
+ return -EEXIST;
+ }
- se_global->g_lun0_hba = hba;
- t = hba->transport;
+ ret = dev->transport->configure_device(dev);
+ if (ret)
+ goto out;
+ dev->dev_flags |= DF_CONFIGURED;
- 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");
- ret = -ENOMEM;
+ /*
+ * XXX: there is not much point to have two different values here..
+ */
+ dev->dev_attrib.block_size = dev->dev_attrib.hw_block_size;
+ dev->dev_attrib.queue_depth = dev->dev_attrib.hw_queue_depth;
+
+ /*
+ * Align max_hw_sectors down to PAGE_SIZE I/O transfers
+ */
+ dev->dev_attrib.hw_max_sectors =
+ se_dev_align_max_sectors(dev->dev_attrib.hw_max_sectors,
+ dev->dev_attrib.hw_block_size);
+
+ dev->dev_index = scsi_get_new_index(SCSI_DEVICE_INDEX);
+ dev->creation_time = get_jiffies_64();
+
+ ret = core_setup_alua(dev);
+ if (ret)
goto out;
- }
- 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;
-
- se_dev->se_dev_su_ptr = t->allocate_virtdevice(hba, "virt_lun0");
- if (!(se_dev->se_dev_su_ptr)) {
- printk(KERN_ERR "Unable to locate subsystem dependent pointer"
- " from allocate_virtdevice()\n");
+
+ /*
+ * Startup the struct se_device processing thread
+ */
+ dev->tmr_wq = alloc_workqueue("tmr-%s", WQ_MEM_RECLAIM | WQ_UNBOUND, 1,
+ dev->transport->name);
+ if (!dev->tmr_wq) {
+ pr_err("Unable to create tmr workqueue for %s\n",
+ dev->transport->name);
ret = -ENOMEM;
- goto out;
+ goto out_free_alua;
}
- se_global->g_lun0_su_dev = se_dev;
- memset(buf, 0, 16);
- sprintf(buf, "rd_pages=8");
- t->set_configfs_dev_params(hba, se_dev, buf, sizeof(buf));
+ /*
+ * Setup work_queue for QUEUE_FULL
+ */
+ INIT_WORK(&dev->qf_work_queue, target_qf_do_work);
- dev = t->create_virtdevice(hba, se_dev, se_dev->se_dev_su_ptr);
- if (!(dev) || IS_ERR(dev)) {
- ret = -ENOMEM;
- goto out;
+ /*
+ * Preload the initial INQUIRY const values if we are doing
+ * anything virtual (IBLOCK, FILEIO, RAMDISK), but not for TCM/pSCSI
+ * passthrough because this is being provided by the backend LLD.
+ */
+ if (dev->transport->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV) {
+ strncpy(&dev->t10_wwn.vendor[0], "LIO-ORG", 8);
+ strncpy(&dev->t10_wwn.model[0],
+ dev->transport->inquiry_prod, 16);
+ strncpy(&dev->t10_wwn.revision[0],
+ dev->transport->inquiry_rev, 4);
}
- se_dev->se_dev_ptr = dev;
- se_global->g_lun0_dev = dev;
+
+ scsi_dump_inquiry(dev);
+
+ spin_lock(&hba->device_lock);
+ hba->dev_count++;
+ spin_unlock(&hba->device_lock);
+
+ mutex_lock(&g_device_mutex);
+ list_add_tail(&dev->g_dev_node, &g_device_list);
+ mutex_unlock(&g_device_mutex);
return 0;
+
+out_free_alua:
+ core_alua_free_lu_gp_mem(dev);
out:
- se_global->g_lun0_su_dev = NULL;
- kfree(se_dev);
- if (se_global->g_lun0_hba) {
- core_delete_hba(se_global->g_lun0_hba);
- se_global->g_lun0_hba = NULL;
+ se_release_vpd_for_dev(dev);
+ return ret;
+}
+
+void target_free_device(struct se_device *dev)
+{
+ struct se_hba *hba = dev->se_hba;
+
+ WARN_ON(!list_empty(&dev->dev_sep_list));
+
+ if (dev->dev_flags & DF_CONFIGURED) {
+ destroy_workqueue(dev->tmr_wq);
+
+ mutex_lock(&g_device_mutex);
+ list_del(&dev->g_dev_node);
+ mutex_unlock(&g_device_mutex);
+
+ spin_lock(&hba->device_lock);
+ hba->dev_count--;
+ spin_unlock(&hba->device_lock);
+ }
+
+ core_alua_free_lu_gp_mem(dev);
+ core_alua_set_lba_map(dev, NULL, 0, 0);
+ core_scsi3_free_all_registrations(dev);
+ se_release_vpd_for_dev(dev);
+
+ if (dev->transport->free_prot)
+ dev->transport->free_prot(dev);
+
+ dev->transport->free_device(dev);
+}
+
+int core_dev_setup_virtual_lun0(void)
+{
+ struct se_hba *hba;
+ struct se_device *dev;
+ char buf[] = "rd_pages=8,rd_nullio=1";
+ int ret;
+
+ hba = core_alloc_hba("rd_mcp", 0, HBA_FLAGS_INTERNAL_USE);
+ if (IS_ERR(hba))
+ return PTR_ERR(hba);
+
+ dev = target_alloc_device(hba, "virt_lun0");
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out_free_hba;
}
+
+ hba->transport->set_configfs_dev_params(dev, buf, sizeof(buf));
+
+ ret = target_configure_device(dev);
+ if (ret)
+ goto out_free_se_dev;
+
+ lun0_hba = hba;
+ g_lun0_dev = dev;
+ return 0;
+
+out_free_se_dev:
+ target_free_device(dev);
+out_free_hba:
+ core_delete_hba(hba);
return ret;
}
void core_dev_release_virtual_lun0(void)
{
- struct se_hba *hba = se_global->g_lun0_hba;
- struct se_subsystem_dev *su_dev = se_global->g_lun0_su_dev;
+ struct se_hba *hba = lun0_hba;
- if (!(hba))
+ if (!hba)
return;
- if (se_global->g_lun0_dev)
- se_free_virtual_device(se_global->g_lun0_dev, hba);
-
- kfree(su_dev);
+ if (g_lun0_dev)
+ target_free_device(g_lun0_dev);
core_delete_hba(hba);
}
diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c
index b65d1c8e774..7de9f0475d0 100644
--- a/drivers/target/target_core_fabric_configfs.c
+++ b/drivers/target/target_core_fabric_configfs.c
@@ -4,10 +4,9 @@
* This file contains generic fabric module configfs infrastructure for
* TCM v4.x code
*
- * Copyright (c) 2010 Rising Tide Systems
- * Copyright (c) 2010 Linux-iSCSI.org
+ * (c) Copyright 2010-2013 Datera, Inc.
*
- * Copyright (c) 2010 Nicholas A. Bellinger <nab@linux-iscsi.org>
+ * Nicholas A. Bellinger <nab@linux-iscsi.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,8 +21,6 @@
#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>
@@ -37,16 +34,13 @@
#include <linux/configfs.h>
#include <target/target_core_base.h>
-#include <target/target_core_device.h>
-#include <target/target_core_tpg.h>
-#include <target/target_core_transport.h>
-#include <target/target_core_fabric_ops.h>
+#include <target/target_core_fabric.h>
#include <target/target_core_fabric_configfs.h>
#include <target/target_core_configfs.h>
#include <target/configfs_macros.h>
+#include "target_core_internal.h"
#include "target_core_alua.h"
-#include "target_core_hba.h"
#include "target_core_pr.h"
#define TF_CIT_SETUP(_name, _item_ops, _group_ops, _attrs) \
@@ -59,7 +53,7 @@ static void target_fabric_setup_##_name##_cit(struct target_fabric_configfs *tf)
cit->ct_group_ops = _group_ops; \
cit->ct_attrs = _attrs; \
cit->ct_owner = tf->tf_module; \
- printk("Setup generic %s\n", __stringify(_name)); \
+ pr_debug("Setup generic %s\n", __stringify(_name)); \
}
/* Start of tfc_tpg_mappedlun_cit */
@@ -76,11 +70,17 @@ static int target_fabric_mappedlun_link(
struct se_portal_group *se_tpg;
struct config_item *nacl_ci, *tpg_ci, *tpg_ci_s, *wwn_ci, *wwn_ci_s;
int ret = 0, lun_access;
+
+ if (lun->lun_link_magic != SE_LUN_LINK_MAGIC) {
+ pr_err("Bad lun->lun_link_magic, not a valid lun_ci pointer:"
+ " %p to struct lun: %p\n", lun_ci, lun);
+ return -EFAULT;
+ }
/*
* Ensure that the source port exists
*/
- if (!(lun->lun_sep) || !(lun->lun_sep->sep_tpg)) {
- printk(KERN_ERR "Source se_lun->lun_sep or lun->lun_sep->sep"
+ if (!lun->lun_sep || !lun->lun_sep->sep_tpg) {
+ pr_err("Source se_lun->lun_sep or lun->lun_sep->sep"
"_tpg does not exist\n");
return -EINVAL;
}
@@ -95,12 +95,12 @@ static int target_fabric_mappedlun_link(
* Make sure the SymLink is going to the same $FABRIC/$WWN/tpgt_$TPGT
*/
if (strcmp(config_item_name(wwn_ci), config_item_name(wwn_ci_s))) {
- printk(KERN_ERR "Illegal Initiator ACL SymLink outside of %s\n",
+ pr_err("Illegal Initiator ACL SymLink outside of %s\n",
config_item_name(wwn_ci));
return -EINVAL;
}
if (strcmp(config_item_name(tpg_ci), config_item_name(tpg_ci_s))) {
- printk(KERN_ERR "Illegal Initiator ACL Symlink outside of %s"
+ pr_err("Illegal Initiator ACL Symlink outside of %s"
" TPGT: %s\n", config_item_name(wwn_ci),
config_item_name(tpg_ci));
return -EINVAL;
@@ -112,12 +112,12 @@ static int target_fabric_mappedlun_link(
* tpg_1/attrib/demo_mode_write_protect=1
*/
spin_lock_irq(&lacl->se_lun_nacl->device_list_lock);
- deve = &lacl->se_lun_nacl->device_list[lacl->mapped_lun];
+ deve = lacl->se_lun_nacl->device_list[lacl->mapped_lun];
if (deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS)
lun_access = deve->lun_flags;
else
lun_access =
- (TPG_TFO(se_tpg)->tpg_check_prod_mode_write_protect(
+ (se_tpg->se_tpg_tfo->tpg_check_prod_mode_write_protect(
se_tpg)) ? TRANSPORT_LUNFLAGS_READ_ONLY :
TRANSPORT_LUNFLAGS_READ_WRITE;
spin_unlock_irq(&lacl->se_lun_nacl->device_list_lock);
@@ -141,12 +141,12 @@ static int target_fabric_mappedlun_unlink(
struct se_lun_acl *lacl = container_of(to_config_group(lun_acl_ci),
struct se_lun_acl, se_lun_group);
struct se_node_acl *nacl = lacl->se_lun_nacl;
- struct se_dev_entry *deve = &nacl->device_list[lacl->mapped_lun];
+ struct se_dev_entry *deve = nacl->device_list[lacl->mapped_lun];
struct se_portal_group *se_tpg;
/*
* Determine if the underlying MappedLUN has already been released..
*/
- if (!(deve->se_lun))
+ if (!deve->se_lun)
return 0;
lun = container_of(to_config_group(lun_ci), struct se_lun, lun_group);
@@ -172,7 +172,7 @@ static ssize_t target_fabric_mappedlun_show_write_protect(
ssize_t len;
spin_lock_irq(&se_nacl->device_list_lock);
- deve = &se_nacl->device_list[lacl->mapped_lun];
+ deve = se_nacl->device_list[lacl->mapped_lun];
len = sprintf(page, "%d\n",
(deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY) ?
1 : 0);
@@ -189,9 +189,11 @@ static ssize_t target_fabric_mappedlun_store_write_protect(
struct se_node_acl *se_nacl = lacl->se_lun_nacl;
struct se_portal_group *se_tpg = se_nacl->se_tpg;
unsigned long op;
+ int ret;
- if (strict_strtoul(page, 0, &op))
- return -EINVAL;
+ ret = kstrtoul(page, 0, &op);
+ if (ret)
+ return ret;
if ((op != 1) && (op != 0))
return -EINVAL;
@@ -201,9 +203,9 @@ static ssize_t target_fabric_mappedlun_store_write_protect(
TRANSPORT_LUNFLAGS_READ_WRITE,
lacl->se_lun_nacl);
- printk(KERN_INFO "%s_ConfigFS: Changed Initiator ACL: %s"
+ pr_debug("%s_ConfigFS: Changed Initiator ACL: %s"
" Mapped LUN: %u Write Protect bit to %s\n",
- TPG_TFO(se_tpg)->get_fabric_name(),
+ se_tpg->se_tpg_tfo->get_fabric_name(),
lacl->initiatorname, lacl->mapped_lun, (op) ? "ON" : "OFF");
return count;
@@ -241,6 +243,32 @@ TF_CIT_SETUP(tpg_mappedlun, &target_fabric_mappedlun_item_ops, NULL,
/* End of tfc_tpg_mappedlun_cit */
+/* Start of tfc_tpg_mappedlun_port_cit */
+
+static struct config_group *target_core_mappedlun_stat_mkdir(
+ struct config_group *group,
+ const char *name)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static void target_core_mappedlun_stat_rmdir(
+ struct config_group *group,
+ struct config_item *item)
+{
+ return;
+}
+
+static struct configfs_group_operations target_fabric_mappedlun_stat_group_ops = {
+ .make_group = target_core_mappedlun_stat_mkdir,
+ .drop_item = target_core_mappedlun_stat_rmdir,
+};
+
+TF_CIT_SETUP(tpg_mappedlun_stat, NULL, &target_fabric_mappedlun_stat_group_ops,
+ NULL);
+
+/* End of tfc_tpg_mappedlun_port_cit */
+
/* Start of tfc_tpg_nacl_attrib_cit */
CONFIGFS_EATTR_OPS(target_fabric_nacl_attrib, se_node_acl, acl_attrib_group);
@@ -294,19 +322,20 @@ static struct config_group *target_fabric_make_mappedlun(
struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf;
struct se_lun_acl *lacl;
struct config_item *acl_ci;
+ struct config_group *lacl_cg = NULL, *ml_stat_grp = NULL;
char *buf;
unsigned long mapped_lun;
int ret = 0;
acl_ci = &group->cg_item;
- if (!(acl_ci)) {
- printk(KERN_ERR "Unable to locatel acl_ci\n");
+ if (!acl_ci) {
+ pr_err("Unable to locatel acl_ci\n");
return NULL;
}
buf = kzalloc(strlen(name) + 1, GFP_KERNEL);
- if (!(buf)) {
- printk(KERN_ERR "Unable to allocate memory for name buf\n");
+ if (!buf) {
+ pr_err("Unable to allocate memory for name buf\n");
return ERR_PTR(-ENOMEM);
}
snprintf(buf, strlen(name) + 1, "%s", name);
@@ -314,7 +343,7 @@ static struct config_group *target_fabric_make_mappedlun(
* Make sure user is creating iscsi/$IQN/$TPGT/acls/$INITIATOR/lun_$ID.
*/
if (strstr(buf, "lun_") != buf) {
- printk(KERN_ERR "Unable to locate \"lun_\" from buf: %s"
+ pr_err("Unable to locate \"lun_\" from buf: %s"
" name: %s\n", buf, name);
ret = -EINVAL;
goto out;
@@ -323,22 +352,60 @@ static struct config_group *target_fabric_make_mappedlun(
* Determine the Mapped LUN value. This is what the SCSI Initiator
* Port will actually see.
*/
- if (strict_strtoul(buf + 4, 0, &mapped_lun) || mapped_lun > UINT_MAX) {
+ ret = kstrtoul(buf + 4, 0, &mapped_lun);
+ if (ret)
+ goto out;
+ if (mapped_lun > UINT_MAX) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (mapped_lun > (TRANSPORT_MAX_LUNS_PER_TPG-1)) {
+ pr_err("Mapped LUN: %lu exceeds TRANSPORT_MAX_LUNS_PER_TPG"
+ "-1: %u for Target Portal Group: %u\n", mapped_lun,
+ TRANSPORT_MAX_LUNS_PER_TPG-1,
+ se_tpg->se_tpg_tfo->tpg_get_tag(se_tpg));
ret = -EINVAL;
goto out;
}
- lacl = core_dev_init_initiator_node_lun_acl(se_tpg, mapped_lun,
- config_item_name(acl_ci), &ret);
- if (!(lacl))
+ lacl = core_dev_init_initiator_node_lun_acl(se_tpg, se_nacl,
+ mapped_lun, &ret);
+ if (!lacl) {
+ ret = -EINVAL;
goto out;
+ }
+
+ lacl_cg = &lacl->se_lun_group;
+ lacl_cg->default_groups = kmalloc(sizeof(struct config_group *) * 2,
+ GFP_KERNEL);
+ if (!lacl_cg->default_groups) {
+ pr_err("Unable to allocate lacl_cg->default_groups\n");
+ ret = -ENOMEM;
+ goto out;
+ }
config_group_init_type_name(&lacl->se_lun_group, name,
- &TF_CIT_TMPL(tf)->tfc_tpg_mappedlun_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_mappedlun_cit);
+ config_group_init_type_name(&lacl->ml_stat_grps.stat_group,
+ "statistics", &tf->tf_cit_tmpl.tfc_tpg_mappedlun_stat_cit);
+ lacl_cg->default_groups[0] = &lacl->ml_stat_grps.stat_group;
+ lacl_cg->default_groups[1] = NULL;
+
+ ml_stat_grp = &lacl->ml_stat_grps.stat_group;
+ ml_stat_grp->default_groups = kmalloc(sizeof(struct config_group *) * 3,
+ GFP_KERNEL);
+ if (!ml_stat_grp->default_groups) {
+ pr_err("Unable to allocate ml_stat_grp->default_groups\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ target_stat_setup_mappedlun_default_groups(lacl);
kfree(buf);
return &lacl->se_lun_group;
out:
+ if (lacl_cg)
+ kfree(lacl_cg->default_groups);
kfree(buf);
return ERR_PTR(ret);
}
@@ -347,6 +414,28 @@ static void target_fabric_drop_mappedlun(
struct config_group *group,
struct config_item *item)
{
+ struct se_lun_acl *lacl = container_of(to_config_group(item),
+ struct se_lun_acl, se_lun_group);
+ struct config_item *df_item;
+ struct config_group *lacl_cg = NULL, *ml_stat_grp = NULL;
+ int i;
+
+ ml_stat_grp = &lacl->ml_stat_grps.stat_group;
+ for (i = 0; ml_stat_grp->default_groups[i]; i++) {
+ df_item = &ml_stat_grp->default_groups[i]->cg_item;
+ ml_stat_grp->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+ kfree(ml_stat_grp->default_groups);
+
+ lacl_cg = &lacl->se_lun_group;
+ for (i = 0; lacl_cg->default_groups[i]; i++) {
+ df_item = &lacl_cg->default_groups[i]->cg_item;
+ lacl_cg->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+ kfree(lacl_cg->default_groups);
+
config_item_put(item);
}
@@ -376,6 +465,15 @@ TF_CIT_SETUP(tpg_nacl_base, &target_fabric_nacl_base_item_ops,
/* End of tfc_tpg_nacl_base_cit */
+/* Start of tfc_node_fabric_stats_cit */
+/*
+ * This is used as a placeholder for struct se_node_acl->acl_fabric_stat_group
+ * to allow fabrics access to ->acl_fabric_stat_group->default_groups[]
+ */
+TF_CIT_SETUP(tpg_nacl_stat, NULL, NULL, NULL);
+
+/* End of tfc_wwn_fabric_stats_cit */
+
/* Start of tfc_tpg_nacl_cit */
static struct config_group *target_fabric_make_nodeacl(
@@ -388,30 +486,34 @@ static struct config_group *target_fabric_make_nodeacl(
struct se_node_acl *se_nacl;
struct config_group *nacl_cg;
- if (!(tf->tf_ops.fabric_make_nodeacl)) {
- printk(KERN_ERR "tf->tf_ops.fabric_make_nodeacl is NULL\n");
+ if (!tf->tf_ops.fabric_make_nodeacl) {
+ pr_err("tf->tf_ops.fabric_make_nodeacl is NULL\n");
return ERR_PTR(-ENOSYS);
}
se_nacl = tf->tf_ops.fabric_make_nodeacl(se_tpg, group, name);
if (IS_ERR(se_nacl))
- return ERR_PTR(PTR_ERR(se_nacl));
+ return ERR_CAST(se_nacl);
nacl_cg = &se_nacl->acl_group;
nacl_cg->default_groups = se_nacl->acl_default_groups;
nacl_cg->default_groups[0] = &se_nacl->acl_attrib_group;
nacl_cg->default_groups[1] = &se_nacl->acl_auth_group;
nacl_cg->default_groups[2] = &se_nacl->acl_param_group;
- nacl_cg->default_groups[3] = NULL;
+ nacl_cg->default_groups[3] = &se_nacl->acl_fabric_stat_group;
+ nacl_cg->default_groups[4] = NULL;
config_group_init_type_name(&se_nacl->acl_group, name,
- &TF_CIT_TMPL(tf)->tfc_tpg_nacl_base_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_nacl_base_cit);
config_group_init_type_name(&se_nacl->acl_attrib_group, "attrib",
- &TF_CIT_TMPL(tf)->tfc_tpg_nacl_attrib_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit);
config_group_init_type_name(&se_nacl->acl_auth_group, "auth",
- &TF_CIT_TMPL(tf)->tfc_tpg_nacl_auth_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_nacl_auth_cit);
config_group_init_type_name(&se_nacl->acl_param_group, "param",
- &TF_CIT_TMPL(tf)->tfc_tpg_nacl_param_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_nacl_param_cit);
+ config_group_init_type_name(&se_nacl->acl_fabric_stat_group,
+ "fabric_statistics",
+ &tf->tf_cit_tmpl.tfc_tpg_nacl_stat_cit);
return &se_nacl->acl_group;
}
@@ -482,18 +584,18 @@ static struct config_group *target_fabric_make_np(
struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf;
struct se_tpg_np *se_tpg_np;
- if (!(tf->tf_ops.fabric_make_np)) {
- printk(KERN_ERR "tf->tf_ops.fabric_make_np is NULL\n");
+ if (!tf->tf_ops.fabric_make_np) {
+ pr_err("tf->tf_ops.fabric_make_np is NULL\n");
return ERR_PTR(-ENOSYS);
}
se_tpg_np = tf->tf_ops.fabric_make_np(se_tpg, group, name);
- if (!(se_tpg_np) || IS_ERR(se_tpg_np))
+ if (!se_tpg_np || IS_ERR(se_tpg_np))
return ERR_PTR(-EINVAL);
se_tpg_np->tpg_np_parent = se_tpg;
config_group_init_type_name(&se_tpg_np->tpg_np_group, name,
- &TF_CIT_TMPL(tf)->tfc_tpg_np_base_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_np_base_cit);
return &se_tpg_np->tpg_np_group;
}
@@ -537,10 +639,7 @@ static ssize_t target_fabric_port_show_attr_alua_tg_pt_gp(
struct se_lun *lun,
char *page)
{
- if (!(lun))
- return -ENODEV;
-
- if (!(lun->lun_sep))
+ if (!lun || !lun->lun_sep)
return -ENODEV;
return core_alua_show_tg_pt_gp_info(lun->lun_sep, page);
@@ -551,10 +650,7 @@ static ssize_t target_fabric_port_store_attr_alua_tg_pt_gp(
const char *page,
size_t count)
{
- if (!(lun))
- return -ENODEV;
-
- if (!(lun->lun_sep))
+ if (!lun || !lun->lun_sep)
return -ENODEV;
return core_alua_store_tg_pt_gp_info(lun->lun_sep, page, count);
@@ -569,10 +665,7 @@ static ssize_t target_fabric_port_show_attr_alua_tg_pt_offline(
struct se_lun *lun,
char *page)
{
- if (!(lun))
- return -ENODEV;
-
- if (!(lun->lun_sep))
+ if (!lun || !lun->lun_sep)
return -ENODEV;
return core_alua_show_offline_bit(lun, page);
@@ -583,10 +676,7 @@ static ssize_t target_fabric_port_store_attr_alua_tg_pt_offline(
const char *page,
size_t count)
{
- if (!(lun))
- return -ENODEV;
-
- if (!(lun->lun_sep))
+ if (!lun || !lun->lun_sep)
return -ENODEV;
return core_alua_store_offline_bit(lun, page, count);
@@ -601,10 +691,7 @@ static ssize_t target_fabric_port_show_attr_alua_tg_pt_status(
struct se_lun *lun,
char *page)
{
- if (!(lun))
- return -ENODEV;
-
- if (!(lun->lun_sep))
+ if (!lun || !lun->lun_sep)
return -ENODEV;
return core_alua_show_secondary_status(lun, page);
@@ -615,10 +702,7 @@ static ssize_t target_fabric_port_store_attr_alua_tg_pt_status(
const char *page,
size_t count)
{
- if (!(lun))
- return -ENODEV;
-
- if (!(lun->lun_sep))
+ if (!lun || !lun->lun_sep)
return -ENODEV;
return core_alua_store_secondary_status(lun, page, count);
@@ -633,10 +717,7 @@ static ssize_t target_fabric_port_show_attr_alua_tg_pt_write_md(
struct se_lun *lun,
char *page)
{
- if (!(lun))
- return -ENODEV;
-
- if (!(lun->lun_sep))
+ if (!lun || !lun->lun_sep)
return -ENODEV;
return core_alua_show_secondary_write_metadata(lun, page);
@@ -647,10 +728,7 @@ static ssize_t target_fabric_port_store_attr_alua_tg_pt_write_md(
const char *page,
size_t count)
{
- if (!(lun))
- return -ENODEV;
-
- if (!(lun->lun_sep))
+ if (!lun || !lun->lun_sep)
return -ENODEV;
return core_alua_store_secondary_write_metadata(lun, page, count);
@@ -674,40 +752,40 @@ static int target_fabric_port_link(
struct config_item *se_dev_ci)
{
struct config_item *tpg_ci;
- struct se_device *dev;
struct se_lun *lun = container_of(to_config_group(lun_ci),
struct se_lun, lun_group);
struct se_lun *lun_p;
struct se_portal_group *se_tpg;
- struct se_subsystem_dev *se_dev = container_of(
- to_config_group(se_dev_ci), struct se_subsystem_dev,
- se_dev_group);
+ struct se_device *dev =
+ container_of(to_config_group(se_dev_ci), struct se_device, dev_group);
struct target_fabric_configfs *tf;
int ret;
+ if (dev->dev_link_magic != SE_DEV_LINK_MAGIC) {
+ pr_err("Bad dev->dev_link_magic, not a valid se_dev_ci pointer:"
+ " %p to struct se_device: %p\n", se_dev_ci, dev);
+ return -EFAULT;
+ }
+
+ if (!(dev->dev_flags & DF_CONFIGURED)) {
+ pr_err("se_device not configured yet, cannot port link\n");
+ return -ENODEV;
+ }
+
tpg_ci = &lun_ci->ci_parent->ci_group->cg_item;
se_tpg = container_of(to_config_group(tpg_ci),
struct se_portal_group, tpg_group);
tf = se_tpg->se_tpg_wwn->wwn_tf;
if (lun->lun_se_dev != NULL) {
- printk(KERN_ERR "Port Symlink already exists\n");
+ pr_err("Port Symlink already exists\n");
return -EEXIST;
}
- dev = se_dev->se_dev_ptr;
- if (!(dev)) {
- printk(KERN_ERR "Unable to locate struct se_device pointer from"
- " %s\n", config_item_name(se_dev_ci));
- ret = -ENODEV;
- goto out;
- }
-
- lun_p = core_dev_add_lun(se_tpg, dev->se_hba, dev,
- lun->unpacked_lun);
- if ((IS_ERR(lun_p)) || !(lun_p)) {
- printk(KERN_ERR "core_dev_add_lun() failed\n");
- ret = -EINVAL;
+ lun_p = core_dev_add_lun(se_tpg, dev, lun->unpacked_lun);
+ if (IS_ERR(lun_p)) {
+ pr_err("core_dev_add_lun() failed\n");
+ ret = PTR_ERR(lun_p);
goto out;
}
@@ -758,6 +836,31 @@ TF_CIT_SETUP(tpg_port, &target_fabric_port_item_ops, NULL, target_fabric_port_at
/* End of tfc_tpg_port_cit */
+/* Start of tfc_tpg_port_stat_cit */
+
+static struct config_group *target_core_port_stat_mkdir(
+ struct config_group *group,
+ const char *name)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static void target_core_port_stat_rmdir(
+ struct config_group *group,
+ struct config_item *item)
+{
+ return;
+}
+
+static struct configfs_group_operations target_fabric_port_stat_group_ops = {
+ .make_group = target_core_port_stat_mkdir,
+ .drop_item = target_core_port_stat_rmdir,
+};
+
+TF_CIT_SETUP(tpg_port_stat, NULL, &target_fabric_port_stat_group_ops, NULL);
+
+/* End of tfc_tpg_port_stat_cit */
+
/* Start of tfc_tpg_lun_cit */
static struct config_group *target_fabric_make_lun(
@@ -768,30 +871,83 @@ static struct config_group *target_fabric_make_lun(
struct se_portal_group *se_tpg = container_of(group,
struct se_portal_group, tpg_lun_group);
struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf;
+ struct config_group *lun_cg = NULL, *port_stat_grp = NULL;
unsigned long unpacked_lun;
+ int errno;
if (strstr(name, "lun_") != name) {
- printk(KERN_ERR "Unable to locate \'_\" in"
+ pr_err("Unable to locate \'_\" in"
" \"lun_$LUN_NUMBER\"\n");
return ERR_PTR(-EINVAL);
}
- if (strict_strtoul(name + 4, 0, &unpacked_lun) || unpacked_lun > UINT_MAX)
+ errno = kstrtoul(name + 4, 0, &unpacked_lun);
+ if (errno)
+ return ERR_PTR(errno);
+ if (unpacked_lun > UINT_MAX)
return ERR_PTR(-EINVAL);
lun = core_get_lun_from_tpg(se_tpg, unpacked_lun);
- if (!(lun))
+ if (!lun)
return ERR_PTR(-EINVAL);
+ lun_cg = &lun->lun_group;
+ lun_cg->default_groups = kmalloc(sizeof(struct config_group *) * 2,
+ GFP_KERNEL);
+ if (!lun_cg->default_groups) {
+ pr_err("Unable to allocate lun_cg->default_groups\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
config_group_init_type_name(&lun->lun_group, name,
- &TF_CIT_TMPL(tf)->tfc_tpg_port_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_port_cit);
+ config_group_init_type_name(&lun->port_stat_grps.stat_group,
+ "statistics", &tf->tf_cit_tmpl.tfc_tpg_port_stat_cit);
+ lun_cg->default_groups[0] = &lun->port_stat_grps.stat_group;
+ lun_cg->default_groups[1] = NULL;
+
+ port_stat_grp = &lun->port_stat_grps.stat_group;
+ port_stat_grp->default_groups = kzalloc(sizeof(struct config_group *) * 4,
+ GFP_KERNEL);
+ if (!port_stat_grp->default_groups) {
+ pr_err("Unable to allocate port_stat_grp->default_groups\n");
+ errno = -ENOMEM;
+ goto out;
+ }
+ target_stat_setup_port_default_groups(lun);
return &lun->lun_group;
+out:
+ if (lun_cg)
+ kfree(lun_cg->default_groups);
+ return ERR_PTR(errno);
}
static void target_fabric_drop_lun(
struct config_group *group,
struct config_item *item)
{
+ struct se_lun *lun = container_of(to_config_group(item),
+ struct se_lun, lun_group);
+ struct config_item *df_item;
+ struct config_group *lun_cg, *port_stat_grp;
+ int i;
+
+ port_stat_grp = &lun->port_stat_grps.stat_group;
+ for (i = 0; port_stat_grp->default_groups[i]; i++) {
+ df_item = &port_stat_grp->default_groups[i]->cg_item;
+ port_stat_grp->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+ kfree(port_stat_grp->default_groups);
+
+ lun_cg = &lun->lun_group;
+ for (i = 0; lun_cg->default_groups[i]; i++) {
+ df_item = &lun_cg->default_groups[i]->cg_item;
+ lun_cg->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+ kfree(lun_cg->default_groups);
+
config_item_put(item);
}
@@ -817,6 +973,19 @@ TF_CIT_SETUP(tpg_attrib, &target_fabric_tpg_attrib_item_ops, NULL, NULL);
/* End of tfc_tpg_attrib_cit */
+/* Start of tfc_tpg_auth_cit */
+
+CONFIGFS_EATTR_OPS(target_fabric_tpg_auth, se_portal_group, tpg_auth_group);
+
+static struct configfs_item_operations target_fabric_tpg_auth_item_ops = {
+ .show_attribute = target_fabric_tpg_auth_attr_show,
+ .store_attribute = target_fabric_tpg_auth_attr_store,
+};
+
+TF_CIT_SETUP(tpg_auth, &target_fabric_tpg_auth_item_ops, NULL, NULL);
+
+/* End of tfc_tpg_attrib_cit */
+
/* Start of tfc_tpg_param_cit */
CONFIGFS_EATTR_OPS(target_fabric_tpg_param, se_portal_group, tpg_param_group);
@@ -866,13 +1035,13 @@ static struct config_group *target_fabric_make_tpg(
struct target_fabric_configfs *tf = wwn->wwn_tf;
struct se_portal_group *se_tpg;
- if (!(tf->tf_ops.fabric_make_tpg)) {
- printk(KERN_ERR "tf->tf_ops.fabric_make_tpg is NULL\n");
+ if (!tf->tf_ops.fabric_make_tpg) {
+ pr_err("tf->tf_ops.fabric_make_tpg is NULL\n");
return ERR_PTR(-ENOSYS);
}
se_tpg = tf->tf_ops.fabric_make_tpg(wwn, group, name);
- if (!(se_tpg) || IS_ERR(se_tpg))
+ if (!se_tpg || IS_ERR(se_tpg))
return ERR_PTR(-EINVAL);
/*
* Setup default groups from pre-allocated se_tpg->tpg_default_groups
@@ -882,21 +1051,24 @@ static struct config_group *target_fabric_make_tpg(
se_tpg->tpg_group.default_groups[1] = &se_tpg->tpg_np_group;
se_tpg->tpg_group.default_groups[2] = &se_tpg->tpg_acl_group;
se_tpg->tpg_group.default_groups[3] = &se_tpg->tpg_attrib_group;
- se_tpg->tpg_group.default_groups[4] = &se_tpg->tpg_param_group;
- se_tpg->tpg_group.default_groups[5] = NULL;
+ se_tpg->tpg_group.default_groups[4] = &se_tpg->tpg_auth_group;
+ se_tpg->tpg_group.default_groups[5] = &se_tpg->tpg_param_group;
+ se_tpg->tpg_group.default_groups[6] = NULL;
config_group_init_type_name(&se_tpg->tpg_group, name,
- &TF_CIT_TMPL(tf)->tfc_tpg_base_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_base_cit);
config_group_init_type_name(&se_tpg->tpg_lun_group, "lun",
- &TF_CIT_TMPL(tf)->tfc_tpg_lun_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_lun_cit);
config_group_init_type_name(&se_tpg->tpg_np_group, "np",
- &TF_CIT_TMPL(tf)->tfc_tpg_np_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_np_cit);
config_group_init_type_name(&se_tpg->tpg_acl_group, "acls",
- &TF_CIT_TMPL(tf)->tfc_tpg_nacl_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_nacl_cit);
config_group_init_type_name(&se_tpg->tpg_attrib_group, "attrib",
- &TF_CIT_TMPL(tf)->tfc_tpg_attrib_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_attrib_cit);
+ config_group_init_type_name(&se_tpg->tpg_auth_group, "auth",
+ &tf->tf_cit_tmpl.tfc_tpg_auth_cit);
config_group_init_type_name(&se_tpg->tpg_param_group, "param",
- &TF_CIT_TMPL(tf)->tfc_tpg_param_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_param_cit);
return &se_tpg->tpg_group;
}
@@ -946,6 +1118,15 @@ TF_CIT_SETUP(tpg, &target_fabric_tpg_item_ops, &target_fabric_tpg_group_ops,
/* End of tfc_tpg_cit */
+/* Start of tfc_wwn_fabric_stats_cit */
+/*
+ * This is used as a placeholder for struct se_wwn->fabric_stat_group
+ * to allow fabrics access to ->fabric_stat_group->default_groups[]
+ */
+TF_CIT_SETUP(wwn_fabric_stats, NULL, NULL, NULL);
+
+/* End of tfc_wwn_fabric_stats_cit */
+
/* Start of tfc_wwn_cit */
static struct config_group *target_fabric_make_wwn(
@@ -956,18 +1137,27 @@ static struct config_group *target_fabric_make_wwn(
struct target_fabric_configfs, tf_group);
struct se_wwn *wwn;
- if (!(tf->tf_ops.fabric_make_wwn)) {
- printk(KERN_ERR "tf->tf_ops.fabric_make_wwn is NULL\n");
+ if (!tf->tf_ops.fabric_make_wwn) {
+ pr_err("tf->tf_ops.fabric_make_wwn is NULL\n");
return ERR_PTR(-ENOSYS);
}
wwn = tf->tf_ops.fabric_make_wwn(tf, group, name);
- if (!(wwn) || IS_ERR(wwn))
+ if (!wwn || IS_ERR(wwn))
return ERR_PTR(-EINVAL);
wwn->wwn_tf = tf;
+ /*
+ * Setup default groups from pre-allocated wwn->wwn_default_groups
+ */
+ wwn->wwn_group.default_groups = wwn->wwn_default_groups;
+ wwn->wwn_group.default_groups[0] = &wwn->fabric_stat_group;
+ wwn->wwn_group.default_groups[1] = NULL;
+
config_group_init_type_name(&wwn->wwn_group, name,
- &TF_CIT_TMPL(tf)->tfc_tpg_cit);
+ &tf->tf_cit_tmpl.tfc_tpg_cit);
+ config_group_init_type_name(&wwn->fabric_stat_group, "fabric_statistics",
+ &tf->tf_cit_tmpl.tfc_wwn_fabric_stats_cit);
return &wwn->wwn_group;
}
@@ -976,6 +1166,18 @@ static void target_fabric_drop_wwn(
struct config_group *group,
struct config_item *item)
{
+ struct se_wwn *wwn = container_of(to_config_group(item),
+ struct se_wwn, wwn_group);
+ struct config_item *df_item;
+ struct config_group *cg = &wwn->wwn_group;
+ int i;
+
+ for (i = 0; cg->default_groups[i]; i++) {
+ df_item = &cg->default_groups[i]->cg_item;
+ cg->default_groups[i] = NULL;
+ config_item_put(df_item);
+ }
+
config_item_put(item);
}
@@ -1015,20 +1217,25 @@ int target_fabric_setup_cits(struct target_fabric_configfs *tf)
{
target_fabric_setup_discovery_cit(tf);
target_fabric_setup_wwn_cit(tf);
+ target_fabric_setup_wwn_fabric_stats_cit(tf);
target_fabric_setup_tpg_cit(tf);
target_fabric_setup_tpg_base_cit(tf);
target_fabric_setup_tpg_port_cit(tf);
+ target_fabric_setup_tpg_port_stat_cit(tf);
target_fabric_setup_tpg_lun_cit(tf);
target_fabric_setup_tpg_np_cit(tf);
target_fabric_setup_tpg_np_base_cit(tf);
target_fabric_setup_tpg_attrib_cit(tf);
+ target_fabric_setup_tpg_auth_cit(tf);
target_fabric_setup_tpg_param_cit(tf);
target_fabric_setup_tpg_nacl_cit(tf);
target_fabric_setup_tpg_nacl_base_cit(tf);
target_fabric_setup_tpg_nacl_attrib_cit(tf);
target_fabric_setup_tpg_nacl_auth_cit(tf);
target_fabric_setup_tpg_nacl_param_cit(tf);
+ target_fabric_setup_tpg_nacl_stat_cit(tf);
target_fabric_setup_tpg_mappedlun_cit(tf);
+ target_fabric_setup_tpg_mappedlun_stat_cit(tf);
return 0;
}
diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c
index 26285644e4d..0d1cf8b4f49 100644
--- a/drivers/target/target_core_fabric_lib.c
+++ b/drivers/target/target_core_fabric_lib.c
@@ -4,8 +4,7 @@
* This file contains generic high level protocol identifier and PR
* handlers for TCM fabric modules
*
- * Copyright (c) 2010 Rising Tide Systems, Inc.
- * Copyright (c) 2010 Linux-iSCSI.org
+ * (c) Copyright 2010-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@linux-iscsi.org>
*
@@ -25,20 +24,19 @@
*
******************************************************************************/
+#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
+#include <linux/export.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_fabric.h>
#include <target/target_core_configfs.h>
-#include "target_core_hba.h"
+#include "target_core_internal.h"
#include "target_core_pr.h"
/*
@@ -61,9 +59,9 @@ u32 sas_get_pr_transport_id(
int *format_code,
unsigned char *buf)
{
- unsigned char binary, *ptr;
- int i;
- u32 off = 4;
+ unsigned char *ptr;
+ int ret;
+
/*
* Set PROTOCOL IDENTIFIER to 6h for SAS
*/
@@ -74,10 +72,10 @@ u32 sas_get_pr_transport_id(
*/
ptr = &se_nacl->initiatorname[4]; /* Skip over 'naa. prefix */
- for (i = 0; i < 16; i += 2) {
- binary = transport_asciihex_to_binaryhex(&ptr[i]);
- buf[off++] = binary;
- }
+ ret = hex2bin(&buf[4], ptr, 8);
+ if (ret < 0)
+ pr_debug("sas transport_id: invalid hex string\n");
+
/*
* The SAS Transport ID is a hardcoded 24-byte length
*/
@@ -157,9 +155,10 @@ u32 fc_get_pr_transport_id(
int *format_code,
unsigned char *buf)
{
- unsigned char binary, *ptr;
- int i;
+ unsigned char *ptr;
+ int i, ret;
u32 off = 8;
+
/*
* PROTOCOL IDENTIFIER is 0h for FCP-2
*
@@ -172,12 +171,13 @@ u32 fc_get_pr_transport_id(
ptr = &se_nacl->initiatorname[0];
for (i = 0; i < 24; ) {
- if (!(strncmp(&ptr[i], ":", 1))) {
+ if (!strncmp(&ptr[i], ":", 1)) {
i++;
continue;
}
- binary = transport_asciihex_to_binaryhex(&ptr[i]);
- buf[off++] = binary;
+ ret = hex2bin(&buf[off++], &ptr[i], 1);
+ if (ret < 0)
+ pr_debug("fc transport_id: invalid hex string\n");
i += 2;
}
/*
@@ -337,7 +337,7 @@ u32 iscsi_get_pr_transport_id_len(
* 00b: iSCSI Initiator device TransportID format
*/
if (pr_reg->isid_present_at_reg) {
- len += 5; /* For ",i,0x" ASCII seperator */
+ len += 5; /* For ",i,0x" ASCII separator */
len += 7; /* For iSCSI Initiator Session ID + Null terminator */
*format_code = 1;
} else
@@ -386,7 +386,7 @@ char *iscsi_parse_pr_out_transport_id(
* Reserved
*/
if ((format_code != 0x00) && (format_code != 0x40)) {
- printk(KERN_ERR "Illegal format code: 0x%02x for iSCSI"
+ pr_err("Illegal format code: 0x%02x for iSCSI"
" Initiator Transport ID\n", format_code);
return NULL;
}
@@ -398,7 +398,7 @@ char *iscsi_parse_pr_out_transport_id(
add_len = ((buf[2] >> 8) & 0xff);
add_len |= (buf[3] & 0xff);
- tid_len = strlen((char *)&buf[4]);
+ tid_len = strlen(&buf[4]);
tid_len += 4; /* Add four bytes for iSCSI Transport ID header */
tid_len += 1; /* Add one byte for NULL terminator */
padding = ((-tid_len) & 3);
@@ -406,7 +406,7 @@ char *iscsi_parse_pr_out_transport_id(
tid_len += padding;
if ((add_len + 4) != tid_len) {
- printk(KERN_INFO "LIO-Target Extracted add_len: %hu "
+ pr_debug("LIO-Target Extracted add_len: %hu "
"does not match calculated tid_len: %u,"
" using tid_len instead\n", add_len+4, tid_len);
*out_tid_len = tid_len;
@@ -414,26 +414,26 @@ char *iscsi_parse_pr_out_transport_id(
*out_tid_len = (add_len + 4);
}
/*
- * Check for ',i,0x' seperator between iSCSI Name and iSCSI Initiator
+ * Check for ',i,0x' separator between iSCSI Name and iSCSI Initiator
* Session ID as defined in Table 390 - iSCSI initiator port TransportID
* format.
*/
if (format_code == 0x40) {
- p = strstr((char *)&buf[4], ",i,0x");
- if (!(p)) {
- printk(KERN_ERR "Unable to locate \",i,0x\" seperator"
+ p = strstr(&buf[4], ",i,0x");
+ if (!p) {
+ pr_err("Unable to locate \",i,0x\" separator"
" for Initiator port identifier: %s\n",
- (char *)&buf[4]);
+ &buf[4]);
return NULL;
}
*p = '\0'; /* Terminate iSCSI Name */
- p += 5; /* Skip over ",i,0x" seperator */
+ p += 5; /* Skip over ",i,0x" separator */
*port_nexus_ptr = p;
/*
* Go ahead and do the lower case conversion of the received
* 12 ASCII characters representing the ISID in the TransportID
- * for comparision against the running iSCSI session's ISID from
+ * for comparison against the running iSCSI session's ISID from
* iscsi_target.c:lio_sess_get_initiator_sid()
*/
for (i = 0; i < 12; i++) {
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index 0aaca885668..7d6cddaec52 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -3,10 +3,7 @@
*
* This file contains the Storage Engine <-> FILEIO transport specific functions
*
- * Copyright (c) 2005 PyX Technologies, Inc.
- * Copyright (c) 2005-2006 SBE, Inc. All Rights Reserved.
- * Copyright (c) 2007-2010 Rising Tide Systems
- * Copyright (c) 2008-2010 Linux-iSCSI.org
+ * (c) Copyright 2005-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -26,36 +23,27 @@
*
******************************************************************************/
-#include <linux/version.h>
#include <linux/string.h>
#include <linux/parser.h>
#include <linux/timer.h>
#include <linux/blkdev.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
+#include <linux/module.h>
+#include <linux/falloc.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
+#include <asm/unaligned.h>
#include <target/target_core_base.h>
-#include <target/target_core_device.h>
-#include <target/target_core_transport.h>
+#include <target/target_core_backend.h>
#include "target_core_file.h"
-#if 1
-#define DEBUG_FD_CACHE(x...) printk(x)
-#else
-#define DEBUG_FD_CACHE(x...)
-#endif
-
-#if 1
-#define DEBUG_FD_FUA(x...) printk(x)
-#else
-#define DEBUG_FD_FUA(x...)
-#endif
-
-static struct se_subsystem_api fileio_template;
+static inline struct fd_dev *FD_DEV(struct se_device *dev)
+{
+ return container_of(dev, struct fd_dev, dev);
+}
/* fd_attach_hba(): (Part of se_subsystem_api_t template)
*
@@ -66,24 +54,20 @@ static int fd_attach_hba(struct se_hba *hba, u32 host_id)
struct fd_host *fd_host;
fd_host = kzalloc(sizeof(struct fd_host), GFP_KERNEL);
- if (!(fd_host)) {
- printk(KERN_ERR "Unable to allocate memory for struct fd_host\n");
- return -1;
+ if (!fd_host) {
+ pr_err("Unable to allocate memory for struct fd_host\n");
+ return -ENOMEM;
}
fd_host->fd_host_id = host_id;
- atomic_set(&hba->left_queue_depth, FD_HBA_QUEUE_DEPTH);
- atomic_set(&hba->max_queue_depth, FD_HBA_QUEUE_DEPTH);
- hba->hba_ptr = (void *) fd_host;
+ hba->hba_ptr = fd_host;
- printk(KERN_INFO "CORE_HBA[%d] - TCM FILEIO HBA Driver %s on Generic"
+ pr_debug("CORE_HBA[%d] - TCM FILEIO HBA Driver %s on Generic"
" Target Core Stack %s\n", hba->hba_id, FD_VERSION,
TARGET_CORE_MOD_VERSION);
- printk(KERN_INFO "CORE_HBA[%d] - Attached FILEIO HBA: %u to Generic"
- " Target Core with TCQ Depth: %d MaxSectors: %u\n",
- hba->hba_id, fd_host->fd_host_id,
- atomic_read(&hba->max_queue_depth), FD_MAX_SECTORS);
+ pr_debug("CORE_HBA[%d] - Attached FILEIO HBA: %u to Generic\n",
+ hba->hba_id, fd_host->fd_host_id);
return 0;
}
@@ -92,83 +76,68 @@ static void fd_detach_hba(struct se_hba *hba)
{
struct fd_host *fd_host = hba->hba_ptr;
- printk(KERN_INFO "CORE_HBA[%d] - Detached FILEIO HBA: %u from Generic"
+ pr_debug("CORE_HBA[%d] - Detached FILEIO HBA: %u from Generic"
" Target Core\n", hba->hba_id, fd_host->fd_host_id);
kfree(fd_host);
hba->hba_ptr = NULL;
}
-static void *fd_allocate_virtdevice(struct se_hba *hba, const char *name)
+static struct se_device *fd_alloc_device(struct se_hba *hba, const char *name)
{
struct fd_dev *fd_dev;
- struct fd_host *fd_host = (struct fd_host *) hba->hba_ptr;
+ struct fd_host *fd_host = hba->hba_ptr;
fd_dev = kzalloc(sizeof(struct fd_dev), GFP_KERNEL);
- if (!(fd_dev)) {
- printk(KERN_ERR "Unable to allocate memory for struct fd_dev\n");
+ if (!fd_dev) {
+ pr_err("Unable to allocate memory for struct fd_dev\n");
return NULL;
}
fd_dev->fd_host = fd_host;
- printk(KERN_INFO "FILEIO: Allocated fd_dev for %p\n", name);
+ pr_debug("FILEIO: Allocated fd_dev for %p\n", name);
- return fd_dev;
+ return &fd_dev->dev;
}
-/* fd_create_virtdevice(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static struct se_device *fd_create_virtdevice(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- void *p)
+static int fd_configure_device(struct se_device *dev)
{
- char *dev_p = NULL;
- struct se_device *dev;
- struct se_dev_limits dev_limits;
- struct queue_limits *limits;
- struct fd_dev *fd_dev = (struct fd_dev *) p;
- struct fd_host *fd_host = (struct fd_host *) hba->hba_ptr;
- mm_segment_t old_fs;
+ struct fd_dev *fd_dev = FD_DEV(dev);
+ struct fd_host *fd_host = dev->se_hba->hba_ptr;
struct file *file;
struct inode *inode = NULL;
- int dev_flags = 0, flags;
-
- memset(&dev_limits, 0, sizeof(struct se_dev_limits));
-
- old_fs = get_fs();
- set_fs(get_ds());
- dev_p = getname(fd_dev->fd_dev_name);
- set_fs(old_fs);
+ int flags, ret = -EINVAL;
- if (IS_ERR(dev_p)) {
- printk(KERN_ERR "getname(%s) failed: %lu\n",
- fd_dev->fd_dev_name, IS_ERR(dev_p));
- goto fail;
+ if (!(fd_dev->fbd_flags & FBDF_HAS_PATH)) {
+ pr_err("Missing fd_dev_name=\n");
+ return -EINVAL;
}
-#if 0
- if (di->no_create_file)
- flags = O_RDWR | O_LARGEFILE;
- else
- flags = O_RDWR | O_CREAT | O_LARGEFILE;
-#else
- flags = O_RDWR | O_CREAT | O_LARGEFILE;
-#endif
-/* flags |= O_DIRECT; */
+
/*
- * If fd_buffered_io=1 has not been set explictly (the default),
- * use O_SYNC to force FILEIO writes to disk.
+ * Use O_DSYNC by default instead of O_SYNC to forgo syncing
+ * of pure timestamp updates.
*/
- if (!(fd_dev->fbd_flags & FDBD_USE_BUFFERED_IO))
- flags |= O_SYNC;
+ flags = O_RDWR | O_CREAT | O_LARGEFILE | O_DSYNC;
- file = filp_open(dev_p, flags, 0600);
+ /*
+ * Optionally allow fd_buffered_io=1 to be enabled for people
+ * who want use the fs buffer cache as an WriteCache mechanism.
+ *
+ * This means that in event of a hard failure, there is a risk
+ * of silent data-loss if the SCSI client has *not* performed a
+ * forced unit access (FUA) write, or issued SYNCHRONIZE_CACHE
+ * to write-out the entire device cache.
+ */
+ if (fd_dev->fbd_flags & FDBD_HAS_BUFFERED_IO_WCE) {
+ pr_debug("FILEIO: Disabling O_DSYNC, using buffered FILEIO\n");
+ flags &= ~O_DSYNC;
+ }
- if (IS_ERR(file) || !file || !file->f_dentry) {
- printk(KERN_ERR "filp_open(%s) failed\n", dev_p);
+ file = filp_open(fd_dev->fd_dev_name, flags, 0600);
+ if (IS_ERR(file)) {
+ pr_err("filp_open(%s) failed\n", fd_dev->fd_dev_name);
+ ret = PTR_ERR(file);
goto fail;
}
fd_dev->fd_file = file;
@@ -180,78 +149,105 @@ static struct se_device *fd_create_virtdevice(
*/
inode = file->f_mapping->host;
if (S_ISBLK(inode->i_mode)) {
- struct request_queue *q;
- /*
- * Setup the local scope queue_limits from struct request_queue->limits
- * to pass into transport_add_device_to_core_hba() as struct se_dev_limits.
- */
- q = bdev_get_queue(inode->i_bdev);
- limits = &dev_limits.limits;
- limits->logical_block_size = bdev_logical_block_size(inode->i_bdev);
- limits->max_hw_sectors = queue_max_hw_sectors(q);
- limits->max_sectors = queue_max_sectors(q);
+ struct request_queue *q = bdev_get_queue(inode->i_bdev);
+ unsigned long long dev_size;
+
+ fd_dev->fd_block_size = bdev_logical_block_size(inode->i_bdev);
/*
* Determine the number of bytes from i_size_read() minus
* one (1) logical sector from underlying struct block_device
*/
- fd_dev->fd_block_size = bdev_logical_block_size(inode->i_bdev);
- fd_dev->fd_dev_size = (i_size_read(file->f_mapping->host) -
+ dev_size = (i_size_read(file->f_mapping->host) -
fd_dev->fd_block_size);
- printk(KERN_INFO "FILEIO: Using size: %llu bytes from struct"
+ pr_debug("FILEIO: Using size: %llu bytes from struct"
" block_device blocks: %llu logical_block_size: %d\n",
- fd_dev->fd_dev_size,
- div_u64(fd_dev->fd_dev_size, fd_dev->fd_block_size),
+ dev_size, div_u64(dev_size, fd_dev->fd_block_size),
fd_dev->fd_block_size);
+ /*
+ * Check if the underlying struct block_device request_queue supports
+ * the QUEUE_FLAG_DISCARD bit for UNMAP/WRITE_SAME in SCSI + TRIM
+ * in ATA and we need to set TPE=1
+ */
+ if (blk_queue_discard(q)) {
+ dev->dev_attrib.max_unmap_lba_count =
+ q->limits.max_discard_sectors;
+ /*
+ * Currently hardcoded to 1 in Linux/SCSI code..
+ */
+ dev->dev_attrib.max_unmap_block_desc_count = 1;
+ dev->dev_attrib.unmap_granularity =
+ q->limits.discard_granularity >> 9;
+ dev->dev_attrib.unmap_granularity_alignment =
+ q->limits.discard_alignment;
+ pr_debug("IFILE: BLOCK Discard support available,"
+ " disabled by default\n");
+ }
+ /*
+ * Enable write same emulation for IBLOCK and use 0xFFFF as
+ * the smaller WRITE_SAME(10) only has a two-byte block count.
+ */
+ dev->dev_attrib.max_write_same_len = 0xFFFF;
+
+ if (blk_queue_nonrot(q))
+ dev->dev_attrib.is_nonrot = 1;
} else {
if (!(fd_dev->fbd_flags & FBDF_HAS_SIZE)) {
- printk(KERN_ERR "FILEIO: Missing fd_dev_size="
+ pr_err("FILEIO: Missing fd_dev_size="
" parameter, and no backing struct"
" block_device\n");
goto fail;
}
- limits = &dev_limits.limits;
- limits->logical_block_size = FD_BLOCKSIZE;
- limits->max_hw_sectors = FD_MAX_SECTORS;
- limits->max_sectors = FD_MAX_SECTORS;
fd_dev->fd_block_size = FD_BLOCKSIZE;
+ /*
+ * Limit UNMAP emulation to 8k Number of LBAs (NoLB)
+ */
+ dev->dev_attrib.max_unmap_lba_count = 0x2000;
+ /*
+ * Currently hardcoded to 1 in Linux/SCSI code..
+ */
+ dev->dev_attrib.max_unmap_block_desc_count = 1;
+ dev->dev_attrib.unmap_granularity = 1;
+ dev->dev_attrib.unmap_granularity_alignment = 0;
+
+ /*
+ * Limit WRITE_SAME w/ UNMAP=0 emulation to 8k Number of LBAs (NoLB)
+ * based upon struct iovec limit for vfs_writev()
+ */
+ dev->dev_attrib.max_write_same_len = 0x1000;
}
- dev_limits.hw_queue_depth = FD_MAX_DEVICE_QUEUE_DEPTH;
- dev_limits.queue_depth = FD_DEVICE_QUEUE_DEPTH;
+ dev->dev_attrib.hw_block_size = fd_dev->fd_block_size;
+ dev->dev_attrib.max_bytes_per_io = FD_MAX_BYTES;
+ dev->dev_attrib.hw_max_sectors = FD_MAX_BYTES / fd_dev->fd_block_size;
+ dev->dev_attrib.hw_queue_depth = FD_MAX_DEVICE_QUEUE_DEPTH;
- dev = transport_add_device_to_core_hba(hba, &fileio_template,
- se_dev, dev_flags, (void *)fd_dev,
- &dev_limits, "FILEIO", FD_VERSION);
- if (!(dev))
- goto fail;
+ if (fd_dev->fbd_flags & FDBD_HAS_BUFFERED_IO_WCE) {
+ pr_debug("FILEIO: Forcing setting of emulate_write_cache=1"
+ " with FDBD_HAS_BUFFERED_IO_WCE\n");
+ dev->dev_attrib.emulate_write_cache = 1;
+ }
fd_dev->fd_dev_id = fd_host->fd_host_dev_id_count++;
fd_dev->fd_queue_depth = dev->queue_depth;
- printk(KERN_INFO "CORE_FILE[%u] - Added TCM FILEIO Device ID: %u at %s,"
+ pr_debug("CORE_FILE[%u] - Added TCM FILEIO Device ID: %u at %s,"
" %llu total bytes\n", fd_host->fd_host_id, fd_dev->fd_dev_id,
fd_dev->fd_dev_name, fd_dev->fd_dev_size);
- putname(dev_p);
- return dev;
+ return 0;
fail:
if (fd_dev->fd_file) {
filp_close(fd_dev->fd_file, NULL);
fd_dev->fd_file = NULL;
}
- putname(dev_p);
- return NULL;
+ return ret;
}
-/* fd_free_device(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static void fd_free_device(void *p)
+static void fd_free_device(struct se_device *dev)
{
- struct fd_dev *fd_dev = (struct fd_dev *) p;
+ struct fd_dev *fd_dev = FD_DEV(dev);
if (fd_dev->fd_file) {
filp_close(fd_dev->fd_file, NULL);
@@ -261,120 +257,145 @@ static void fd_free_device(void *p)
kfree(fd_dev);
}
-static inline struct fd_request *FILE_REQ(struct se_task *task)
-{
- return container_of(task, struct fd_request, fd_task);
-}
-
-
-static struct se_task *
-fd_alloc_task(struct se_cmd *cmd)
+static int fd_do_prot_rw(struct se_cmd *cmd, struct fd_prot *fd_prot,
+ int is_write)
{
- struct fd_request *fd_req;
-
- fd_req = kzalloc(sizeof(struct fd_request), GFP_KERNEL);
- if (!(fd_req)) {
- printk(KERN_ERR "Unable to allocate struct fd_request\n");
- return NULL;
- }
-
- fd_req->fd_dev = SE_DEV(cmd)->dev_ptr;
-
- return &fd_req->fd_task;
-}
-
-static int fd_do_readv(struct se_task *task)
-{
- struct fd_request *req = FILE_REQ(task);
- struct file *fd = req->fd_dev->fd_file;
- struct scatterlist *sg = task->task_sg;
- struct iovec *iov;
- mm_segment_t old_fs;
- loff_t pos = (task->task_lba * DEV_ATTRIB(task->se_dev)->block_size);
- int ret = 0, i;
+ struct se_device *se_dev = cmd->se_dev;
+ struct fd_dev *dev = FD_DEV(se_dev);
+ struct file *prot_fd = dev->fd_prot_file;
+ struct scatterlist *sg;
+ loff_t pos = (cmd->t_task_lba * se_dev->prot_length);
+ unsigned char *buf;
+ u32 prot_size, len, size;
+ int rc, ret = 1, i;
+
+ prot_size = (cmd->data_length / se_dev->dev_attrib.block_size) *
+ se_dev->prot_length;
+
+ if (!is_write) {
+ fd_prot->prot_buf = vzalloc(prot_size);
+ if (!fd_prot->prot_buf) {
+ pr_err("Unable to allocate fd_prot->prot_buf\n");
+ return -ENOMEM;
+ }
+ buf = fd_prot->prot_buf;
+
+ fd_prot->prot_sg_nents = cmd->t_prot_nents;
+ fd_prot->prot_sg = kzalloc(sizeof(struct scatterlist) *
+ fd_prot->prot_sg_nents, GFP_KERNEL);
+ if (!fd_prot->prot_sg) {
+ pr_err("Unable to allocate fd_prot->prot_sg\n");
+ vfree(fd_prot->prot_buf);
+ return -ENOMEM;
+ }
+ size = prot_size;
- iov = kzalloc(sizeof(struct iovec) * task->task_sg_num, GFP_KERNEL);
- if (!(iov)) {
- printk(KERN_ERR "Unable to allocate fd_do_readv iov[]\n");
- return -1;
- }
+ for_each_sg(fd_prot->prot_sg, sg, fd_prot->prot_sg_nents, i) {
- for (i = 0; i < task->task_sg_num; i++) {
- iov[i].iov_len = sg[i].length;
- iov[i].iov_base = sg_virt(&sg[i]);
+ len = min_t(u32, PAGE_SIZE, size);
+ sg_set_buf(sg, buf, len);
+ size -= len;
+ buf += len;
+ }
}
- old_fs = get_fs();
- set_fs(get_ds());
- ret = vfs_readv(fd, &iov[0], task->task_sg_num, &pos);
- set_fs(old_fs);
-
- kfree(iov);
- /*
- * Return zeros and GOOD status even if the READ did not return
- * the expected virt_size for struct file w/o a backing struct
- * block_device.
- */
- if (S_ISBLK(fd->f_dentry->d_inode->i_mode)) {
- if (ret < 0 || ret != task->task_size) {
- printk(KERN_ERR "vfs_readv() returned %d,"
- " expecting %d for S_ISBLK\n", ret,
- (int)task->task_size);
- return -1;
+ if (is_write) {
+ rc = kernel_write(prot_fd, fd_prot->prot_buf, prot_size, pos);
+ if (rc < 0 || prot_size != rc) {
+ pr_err("kernel_write() for fd_do_prot_rw failed:"
+ " %d\n", rc);
+ ret = -EINVAL;
}
} else {
- if (ret < 0) {
- printk(KERN_ERR "vfs_readv() returned %d for non"
- " S_ISBLK\n", ret);
- return -1;
+ rc = kernel_read(prot_fd, pos, fd_prot->prot_buf, prot_size);
+ if (rc < 0) {
+ pr_err("kernel_read() for fd_do_prot_rw failed:"
+ " %d\n", rc);
+ ret = -EINVAL;
}
}
- return 1;
+ if (is_write || ret < 0) {
+ kfree(fd_prot->prot_sg);
+ vfree(fd_prot->prot_buf);
+ }
+
+ return ret;
}
-static int fd_do_writev(struct se_task *task)
+static int fd_do_rw(struct se_cmd *cmd, struct scatterlist *sgl,
+ u32 sgl_nents, int is_write)
{
- struct fd_request *req = FILE_REQ(task);
- struct file *fd = req->fd_dev->fd_file;
- struct scatterlist *sg = task->task_sg;
+ struct se_device *se_dev = cmd->se_dev;
+ struct fd_dev *dev = FD_DEV(se_dev);
+ struct file *fd = dev->fd_file;
+ struct scatterlist *sg;
struct iovec *iov;
mm_segment_t old_fs;
- loff_t pos = (task->task_lba * DEV_ATTRIB(task->se_dev)->block_size);
- int ret, i = 0;
+ loff_t pos = (cmd->t_task_lba * se_dev->dev_attrib.block_size);
+ int ret = 0, i;
- iov = kzalloc(sizeof(struct iovec) * task->task_sg_num, GFP_KERNEL);
- if (!(iov)) {
- printk(KERN_ERR "Unable to allocate fd_do_writev iov[]\n");
- return -1;
+ iov = kzalloc(sizeof(struct iovec) * sgl_nents, GFP_KERNEL);
+ if (!iov) {
+ pr_err("Unable to allocate fd_do_readv iov[]\n");
+ return -ENOMEM;
}
- for (i = 0; i < task->task_sg_num; i++) {
- iov[i].iov_len = sg[i].length;
- iov[i].iov_base = sg_virt(&sg[i]);
+ for_each_sg(sgl, sg, sgl_nents, i) {
+ iov[i].iov_len = sg->length;
+ iov[i].iov_base = kmap(sg_page(sg)) + sg->offset;
}
old_fs = get_fs();
set_fs(get_ds());
- ret = vfs_writev(fd, &iov[0], task->task_sg_num, &pos);
+
+ if (is_write)
+ ret = vfs_writev(fd, &iov[0], sgl_nents, &pos);
+ else
+ ret = vfs_readv(fd, &iov[0], sgl_nents, &pos);
+
set_fs(old_fs);
+ for_each_sg(sgl, sg, sgl_nents, i)
+ kunmap(sg_page(sg));
+
kfree(iov);
- if (ret < 0 || ret != task->task_size) {
- printk(KERN_ERR "vfs_writev() returned %d\n", ret);
- return -1;
+ if (is_write) {
+ if (ret < 0 || ret != cmd->data_length) {
+ pr_err("%s() write returned %d\n", __func__, ret);
+ return (ret < 0 ? ret : -EINVAL);
+ }
+ } else {
+ /*
+ * Return zeros and GOOD status even if the READ did not return
+ * the expected virt_size for struct file w/o a backing struct
+ * block_device.
+ */
+ if (S_ISBLK(file_inode(fd)->i_mode)) {
+ if (ret < 0 || ret != cmd->data_length) {
+ pr_err("%s() returned %d, expecting %u for "
+ "S_ISBLK\n", __func__, ret,
+ cmd->data_length);
+ return (ret < 0 ? ret : -EINVAL);
+ }
+ } else {
+ if (ret < 0) {
+ pr_err("%s() returned %d for non S_ISBLK\n",
+ __func__, ret);
+ return ret;
+ }
+ }
}
-
return 1;
}
-static void fd_emulate_sync_cache(struct se_task *task)
+static sense_reason_t
+fd_execute_sync_cache(struct se_cmd *cmd)
{
- struct se_cmd *cmd = TASK_CMD(task);
struct se_device *dev = cmd->se_dev;
- struct fd_dev *fd_dev = dev->dev_ptr;
- int immed = (cmd->t_task->t_task_cdb[1] & 0x2);
+ struct fd_dev *fd_dev = FD_DEV(dev);
+ int immed = (cmd->t_task_cdb[1] & 0x2);
loff_t start, end;
int ret;
@@ -383,16 +404,16 @@ static void fd_emulate_sync_cache(struct se_task *task)
* for this SYNCHRONIZE_CACHE op
*/
if (immed)
- transport_complete_sync_cache(cmd, 1);
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
/*
* Determine if we will be flushing the entire device.
*/
- if (cmd->t_task->t_task_lba == 0 && cmd->data_length == 0) {
+ if (cmd->t_task_lba == 0 && cmd->data_length == 0) {
start = 0;
end = LLONG_MAX;
} else {
- start = cmd->t_task->t_task_lba * DEV_ATTRIB(dev)->block_size;
+ start = cmd->t_task_lba * dev->dev_attrib.block_size;
if (cmd->data_length)
end = start + cmd->data_length;
else
@@ -401,106 +422,285 @@ static void fd_emulate_sync_cache(struct se_task *task)
ret = vfs_fsync_range(fd_dev->fd_file, start, end, 1);
if (ret != 0)
- printk(KERN_ERR "FILEIO: vfs_fsync_range() failed: %d\n", ret);
+ pr_err("FILEIO: vfs_fsync_range() failed: %d\n", ret);
+
+ if (immed)
+ return 0;
+
+ if (ret)
+ target_complete_cmd(cmd, SAM_STAT_CHECK_CONDITION);
+ else
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
- if (!immed)
- transport_complete_sync_cache(cmd, ret == 0);
+ return 0;
}
-/*
- * Tell TCM Core that we are capable of WriteCache emulation for
- * an underlying struct se_device.
- */
-static int fd_emulated_write_cache(struct se_device *dev)
+static unsigned char *
+fd_setup_write_same_buf(struct se_cmd *cmd, struct scatterlist *sg,
+ unsigned int len)
{
- return 1;
+ struct se_device *se_dev = cmd->se_dev;
+ unsigned int block_size = se_dev->dev_attrib.block_size;
+ unsigned int i = 0, end;
+ unsigned char *buf, *p, *kmap_buf;
+
+ buf = kzalloc(min_t(unsigned int, len, PAGE_SIZE), GFP_KERNEL);
+ if (!buf) {
+ pr_err("Unable to allocate fd_execute_write_same buf\n");
+ return NULL;
+ }
+
+ kmap_buf = kmap(sg_page(sg)) + sg->offset;
+ if (!kmap_buf) {
+ pr_err("kmap() failed in fd_setup_write_same\n");
+ kfree(buf);
+ return NULL;
+ }
+ /*
+ * Fill local *buf to contain multiple WRITE_SAME blocks up to
+ * min(len, PAGE_SIZE)
+ */
+ p = buf;
+ end = min_t(unsigned int, len, PAGE_SIZE);
+
+ while (i < end) {
+ memcpy(p, kmap_buf, block_size);
+
+ i += block_size;
+ p += block_size;
+ }
+ kunmap(sg_page(sg));
+
+ return buf;
}
-static int fd_emulated_dpo(struct se_device *dev)
+static sense_reason_t
+fd_execute_write_same(struct se_cmd *cmd)
{
+ struct se_device *se_dev = cmd->se_dev;
+ struct fd_dev *fd_dev = FD_DEV(se_dev);
+ struct file *f = fd_dev->fd_file;
+ struct scatterlist *sg;
+ struct iovec *iov;
+ mm_segment_t old_fs;
+ sector_t nolb = sbc_get_write_same_sectors(cmd);
+ loff_t pos = cmd->t_task_lba * se_dev->dev_attrib.block_size;
+ unsigned int len, len_tmp, iov_num;
+ int i, rc;
+ unsigned char *buf;
+
+ if (!nolb) {
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
+ return 0;
+ }
+ sg = &cmd->t_data_sg[0];
+
+ if (cmd->t_data_nents > 1 ||
+ sg->length != cmd->se_dev->dev_attrib.block_size) {
+ pr_err("WRITE_SAME: Illegal SGL t_data_nents: %u length: %u"
+ " block_size: %u\n", cmd->t_data_nents, sg->length,
+ cmd->se_dev->dev_attrib.block_size);
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ len = len_tmp = nolb * se_dev->dev_attrib.block_size;
+ iov_num = DIV_ROUND_UP(len, PAGE_SIZE);
+
+ buf = fd_setup_write_same_buf(cmd, sg, len);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ iov = vzalloc(sizeof(struct iovec) * iov_num);
+ if (!iov) {
+ pr_err("Unable to allocate fd_execute_write_same iovecs\n");
+ kfree(buf);
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+ /*
+ * Map the single fabric received scatterlist block now populated
+ * in *buf into each iovec for I/O submission.
+ */
+ for (i = 0; i < iov_num; i++) {
+ iov[i].iov_base = buf;
+ iov[i].iov_len = min_t(unsigned int, len_tmp, PAGE_SIZE);
+ len_tmp -= iov[i].iov_len;
+ }
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ rc = vfs_writev(f, &iov[0], iov_num, &pos);
+ set_fs(old_fs);
+
+ vfree(iov);
+ kfree(buf);
+
+ if (rc < 0 || rc != len) {
+ pr_err("vfs_writev() returned %d for write same\n", rc);
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
return 0;
}
-/*
- * Tell TCM Core that we will be emulating Forced Unit Access (FUA) for WRITEs
- * for TYPE_DISK.
- */
-static int fd_emulated_fua_write(struct se_device *dev)
-{
- return 1;
-}
-static int fd_emulated_fua_read(struct se_device *dev)
+static sense_reason_t
+fd_do_unmap(struct se_cmd *cmd, void *priv, sector_t lba, sector_t nolb)
{
+ struct file *file = priv;
+ struct inode *inode = file->f_mapping->host;
+ int ret;
+
+ if (S_ISBLK(inode->i_mode)) {
+ /* The backend is block device, use discard */
+ struct block_device *bdev = inode->i_bdev;
+
+ ret = blkdev_issue_discard(bdev, lba,
+ nolb, GFP_KERNEL, 0);
+ if (ret < 0) {
+ pr_warn("FILEIO: blkdev_issue_discard() failed: %d\n",
+ ret);
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+ } else {
+ /* The backend is normal file, use fallocate */
+ struct se_device *se_dev = cmd->se_dev;
+ loff_t pos = lba * se_dev->dev_attrib.block_size;
+ unsigned int len = nolb * se_dev->dev_attrib.block_size;
+ int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
+
+ if (!file->f_op->fallocate)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ ret = file->f_op->fallocate(file, mode, pos, len);
+ if (ret < 0) {
+ pr_warn("FILEIO: fallocate() failed: %d\n", ret);
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+ }
+
return 0;
}
-/*
- * WRITE Force Unit Access (FUA) emulation on a per struct se_task
- * LBA range basis..
- */
-static void fd_emulate_write_fua(struct se_cmd *cmd, struct se_task *task)
+static sense_reason_t
+fd_execute_write_same_unmap(struct se_cmd *cmd)
{
- struct se_device *dev = cmd->se_dev;
- struct fd_dev *fd_dev = dev->dev_ptr;
- loff_t start = task->task_lba * DEV_ATTRIB(dev)->block_size;
- loff_t end = start + task->task_size;
+ struct se_device *se_dev = cmd->se_dev;
+ struct fd_dev *fd_dev = FD_DEV(se_dev);
+ struct file *file = fd_dev->fd_file;
+ sector_t lba = cmd->t_task_lba;
+ sector_t nolb = sbc_get_write_same_sectors(cmd);
int ret;
- DEBUG_FD_CACHE("FILEIO: FUA WRITE LBA: %llu, bytes: %u\n",
- task->task_lba, task->task_size);
+ if (!nolb) {
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
+ return 0;
+ }
- ret = vfs_fsync_range(fd_dev->fd_file, start, end, 1);
- if (ret != 0)
- printk(KERN_ERR "FILEIO: vfs_fsync_range() failed: %d\n", ret);
+ ret = fd_do_unmap(cmd, file, lba, nolb);
+ if (ret)
+ return ret;
+
+ target_complete_cmd(cmd, GOOD);
+ return 0;
}
-static int fd_do_task(struct se_task *task)
+static sense_reason_t
+fd_execute_unmap(struct se_cmd *cmd)
+{
+ struct file *file = FD_DEV(cmd->se_dev)->fd_file;
+
+ return sbc_execute_unmap(cmd, fd_do_unmap, file);
+}
+
+static sense_reason_t
+fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
+ enum dma_data_direction data_direction)
{
- struct se_cmd *cmd = task->task_se_cmd;
struct se_device *dev = cmd->se_dev;
+ struct fd_prot fd_prot;
+ sense_reason_t rc;
int ret = 0;
/*
* Call vectorized fileio functions to map struct scatterlist
* physical memory addresses to struct iovec virtual memory.
*/
- if (task->task_data_direction == DMA_FROM_DEVICE) {
- ret = fd_do_readv(task);
+ if (data_direction == DMA_FROM_DEVICE) {
+ memset(&fd_prot, 0, sizeof(struct fd_prot));
+
+ if (cmd->prot_type) {
+ ret = fd_do_prot_rw(cmd, &fd_prot, false);
+ if (ret < 0)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+
+ ret = fd_do_rw(cmd, sgl, sgl_nents, 0);
+
+ if (ret > 0 && cmd->prot_type) {
+ u32 sectors = cmd->data_length / dev->dev_attrib.block_size;
+
+ rc = sbc_dif_verify_read(cmd, cmd->t_task_lba, sectors,
+ 0, fd_prot.prot_sg, 0);
+ if (rc) {
+ kfree(fd_prot.prot_sg);
+ vfree(fd_prot.prot_buf);
+ return rc;
+ }
+ kfree(fd_prot.prot_sg);
+ vfree(fd_prot.prot_buf);
+ }
} else {
- ret = fd_do_writev(task);
+ memset(&fd_prot, 0, sizeof(struct fd_prot));
+
+ if (cmd->prot_type) {
+ u32 sectors = cmd->data_length / dev->dev_attrib.block_size;
+
+ ret = fd_do_prot_rw(cmd, &fd_prot, false);
+ if (ret < 0)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ rc = sbc_dif_verify_write(cmd, cmd->t_task_lba, sectors,
+ 0, fd_prot.prot_sg, 0);
+ if (rc) {
+ kfree(fd_prot.prot_sg);
+ vfree(fd_prot.prot_buf);
+ return rc;
+ }
+ }
+ ret = fd_do_rw(cmd, sgl, sgl_nents, 1);
+ /*
+ * Perform implicit vfs_fsync_range() for fd_do_writev() ops
+ * for SCSI WRITEs with Forced Unit Access (FUA) set.
+ * Allow this to happen independent of WCE=0 setting.
+ */
if (ret > 0 &&
- DEV_ATTRIB(dev)->emulate_write_cache > 0 &&
- DEV_ATTRIB(dev)->emulate_fua_write > 0 &&
- T_TASK(cmd)->t_tasks_fua) {
- /*
- * We might need to be a bit smarter here
- * and return some sense data to let the initiator
- * know the FUA WRITE cache sync failed..?
- */
- fd_emulate_write_fua(cmd, task);
+ dev->dev_attrib.emulate_fua_write > 0 &&
+ (cmd->se_cmd_flags & SCF_FUA)) {
+ struct fd_dev *fd_dev = FD_DEV(dev);
+ loff_t start = cmd->t_task_lba *
+ dev->dev_attrib.block_size;
+ loff_t end = start + cmd->data_length;
+
+ vfs_fsync_range(fd_dev->fd_file, start, end, 1);
}
+ if (ret > 0 && cmd->prot_type) {
+ ret = fd_do_prot_rw(cmd, &fd_prot, true);
+ if (ret < 0)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
}
- if (ret < 0)
- return ret;
- if (ret) {
- task->task_scsi_status = GOOD;
- transport_complete_task(task, 1);
+ if (ret < 0) {
+ kfree(fd_prot.prot_sg);
+ vfree(fd_prot.prot_buf);
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
- return PYX_TRANSPORT_SENT_TO_TRANSPORT;
-}
-
-/* fd_free_task(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static void fd_free_task(struct se_task *task)
-{
- struct fd_request *req = FILE_REQ(task);
- kfree(req);
+ if (ret)
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
+ return 0;
}
enum {
@@ -510,16 +710,14 @@ enum {
static match_table_t tokens = {
{Opt_fd_dev_name, "fd_dev_name=%s"},
{Opt_fd_dev_size, "fd_dev_size=%s"},
- {Opt_fd_buffered_io, "fd_buffered_id=%d"},
+ {Opt_fd_buffered_io, "fd_buffered_io=%d"},
{Opt_err, NULL}
};
-static ssize_t fd_set_configfs_dev_params(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- const char *page, ssize_t count)
+static ssize_t fd_set_configfs_dev_params(struct se_device *dev,
+ const char *page, ssize_t count)
{
- struct fd_dev *fd_dev = se_dev->se_dev_su_ptr;
+ struct fd_dev *fd_dev = FD_DEV(dev);
char *orig, *ptr, *arg_p, *opts;
substring_t args[MAX_OPT_ARGS];
int ret = 0, arg, token;
@@ -530,43 +728,51 @@ static ssize_t fd_set_configfs_dev_params(
orig = opts;
- while ((ptr = strsep(&opts, ",")) != NULL) {
+ while ((ptr = strsep(&opts, ",\n")) != NULL) {
if (!*ptr)
continue;
token = match_token(ptr, tokens, args);
switch (token) {
case Opt_fd_dev_name:
- snprintf(fd_dev->fd_dev_name, FD_MAX_DEV_NAME,
- "%s", match_strdup(&args[0]));
- printk(KERN_INFO "FILEIO: Referencing Path: %s\n",
+ if (match_strlcpy(fd_dev->fd_dev_name, &args[0],
+ FD_MAX_DEV_NAME) == 0) {
+ ret = -EINVAL;
+ break;
+ }
+ pr_debug("FILEIO: Referencing Path: %s\n",
fd_dev->fd_dev_name);
fd_dev->fbd_flags |= FBDF_HAS_PATH;
break;
case Opt_fd_dev_size:
arg_p = match_strdup(&args[0]);
- ret = strict_strtoull(arg_p, 0, &fd_dev->fd_dev_size);
+ if (!arg_p) {
+ ret = -ENOMEM;
+ break;
+ }
+ ret = kstrtoull(arg_p, 0, &fd_dev->fd_dev_size);
+ kfree(arg_p);
if (ret < 0) {
- printk(KERN_ERR "strict_strtoull() failed for"
+ pr_err("kstrtoull() failed for"
" fd_dev_size=\n");
goto out;
}
- printk(KERN_INFO "FILEIO: Referencing Size: %llu"
+ pr_debug("FILEIO: Referencing Size: %llu"
" bytes\n", fd_dev->fd_dev_size);
fd_dev->fbd_flags |= FBDF_HAS_SIZE;
break;
case Opt_fd_buffered_io:
match_int(args, &arg);
if (arg != 1) {
- printk(KERN_ERR "bogus fd_buffered_io=%d value\n", arg);
+ pr_err("bogus fd_buffered_io=%d value\n", arg);
ret = -EINVAL;
goto out;
}
- printk(KERN_INFO "FILEIO: Using buffered I/O"
+ pr_debug("FILEIO: Using buffered I/O"
" operations for struct fd_dev\n");
- fd_dev->fbd_flags |= FDBD_USE_BUFFERED_IO;
+ fd_dev->fbd_flags |= FDBD_HAS_BUFFERED_IO_WCE;
break;
default:
break;
@@ -578,96 +784,168 @@ out:
return (!ret) ? count : ret;
}
-static ssize_t fd_check_configfs_dev_params(struct se_hba *hba, struct se_subsystem_dev *se_dev)
-{
- struct fd_dev *fd_dev = (struct fd_dev *) se_dev->se_dev_su_ptr;
-
- if (!(fd_dev->fbd_flags & FBDF_HAS_PATH)) {
- printk(KERN_ERR "Missing fd_dev_name=\n");
- return -1;
- }
-
- return 0;
-}
-
-static ssize_t fd_show_configfs_dev_params(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- char *b)
+static ssize_t fd_show_configfs_dev_params(struct se_device *dev, char *b)
{
- struct fd_dev *fd_dev = se_dev->se_dev_su_ptr;
+ struct fd_dev *fd_dev = FD_DEV(dev);
ssize_t bl = 0;
bl = sprintf(b + bl, "TCM FILEIO ID: %u", fd_dev->fd_dev_id);
bl += sprintf(b + bl, " File: %s Size: %llu Mode: %s\n",
fd_dev->fd_dev_name, fd_dev->fd_dev_size,
- (fd_dev->fbd_flags & FDBD_USE_BUFFERED_IO) ?
- "Buffered" : "Synchronous");
+ (fd_dev->fbd_flags & FDBD_HAS_BUFFERED_IO_WCE) ?
+ "Buffered-WCE" : "O_DSYNC");
return bl;
}
-/* fd_get_cdb(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static unsigned char *fd_get_cdb(struct se_task *task)
+static sector_t fd_get_blocks(struct se_device *dev)
{
- struct fd_request *req = FILE_REQ(task);
+ struct fd_dev *fd_dev = FD_DEV(dev);
+ struct file *f = fd_dev->fd_file;
+ struct inode *i = f->f_mapping->host;
+ unsigned long long dev_size;
+ /*
+ * When using a file that references an underlying struct block_device,
+ * ensure dev_size is always based on the current inode size in order
+ * to handle underlying block_device resize operations.
+ */
+ if (S_ISBLK(i->i_mode))
+ dev_size = i_size_read(i);
+ else
+ dev_size = fd_dev->fd_dev_size;
- return req->fd_scsi_cdb;
+ return div_u64(dev_size - dev->dev_attrib.block_size,
+ dev->dev_attrib.block_size);
}
-/* fd_get_device_rev(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static u32 fd_get_device_rev(struct se_device *dev)
+static int fd_init_prot(struct se_device *dev)
{
- return SCSI_SPC_2; /* Returns SPC-3 in Initiator Data */
+ struct fd_dev *fd_dev = FD_DEV(dev);
+ struct file *prot_file, *file = fd_dev->fd_file;
+ struct inode *inode;
+ int ret, flags = O_RDWR | O_CREAT | O_LARGEFILE | O_DSYNC;
+ char buf[FD_MAX_DEV_PROT_NAME];
+
+ if (!file) {
+ pr_err("Unable to locate fd_dev->fd_file\n");
+ return -ENODEV;
+ }
+
+ inode = file->f_mapping->host;
+ if (S_ISBLK(inode->i_mode)) {
+ pr_err("FILEIO Protection emulation only supported on"
+ " !S_ISBLK\n");
+ return -ENOSYS;
+ }
+
+ if (fd_dev->fbd_flags & FDBD_HAS_BUFFERED_IO_WCE)
+ flags &= ~O_DSYNC;
+
+ snprintf(buf, FD_MAX_DEV_PROT_NAME, "%s.protection",
+ fd_dev->fd_dev_name);
+
+ prot_file = filp_open(buf, flags, 0600);
+ if (IS_ERR(prot_file)) {
+ pr_err("filp_open(%s) failed\n", buf);
+ ret = PTR_ERR(prot_file);
+ return ret;
+ }
+ fd_dev->fd_prot_file = prot_file;
+
+ return 0;
}
-/* fd_get_device_type(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static u32 fd_get_device_type(struct se_device *dev)
+static int fd_format_prot(struct se_device *dev)
{
- return TYPE_DISK;
+ struct fd_dev *fd_dev = FD_DEV(dev);
+ struct file *prot_fd = fd_dev->fd_prot_file;
+ sector_t prot_length, prot;
+ unsigned char *buf;
+ loff_t pos = 0;
+ int unit_size = FDBD_FORMAT_UNIT_SIZE * dev->dev_attrib.block_size;
+ int rc, ret = 0, size, len;
+
+ if (!dev->dev_attrib.pi_prot_type) {
+ pr_err("Unable to format_prot while pi_prot_type == 0\n");
+ return -ENODEV;
+ }
+ if (!prot_fd) {
+ pr_err("Unable to locate fd_dev->fd_prot_file\n");
+ return -ENODEV;
+ }
+
+ buf = vzalloc(unit_size);
+ if (!buf) {
+ pr_err("Unable to allocate FILEIO prot buf\n");
+ return -ENOMEM;
+ }
+ prot_length = (dev->transport->get_blocks(dev) + 1) * dev->prot_length;
+ size = prot_length;
+
+ pr_debug("Using FILEIO prot_length: %llu\n",
+ (unsigned long long)prot_length);
+
+ memset(buf, 0xff, unit_size);
+ for (prot = 0; prot < prot_length; prot += unit_size) {
+ len = min(unit_size, size);
+ rc = kernel_write(prot_fd, buf, len, pos);
+ if (rc != len) {
+ pr_err("vfs_write to prot file failed: %d\n", rc);
+ ret = -ENODEV;
+ goto out;
+ }
+ pos += len;
+ size -= len;
+ }
+
+out:
+ vfree(buf);
+ return ret;
}
-static sector_t fd_get_blocks(struct se_device *dev)
+static void fd_free_prot(struct se_device *dev)
{
- struct fd_dev *fd_dev = dev->dev_ptr;
- unsigned long long blocks_long = div_u64(fd_dev->fd_dev_size,
- DEV_ATTRIB(dev)->block_size);
+ struct fd_dev *fd_dev = FD_DEV(dev);
+
+ if (!fd_dev->fd_prot_file)
+ return;
+
+ filp_close(fd_dev->fd_prot_file, NULL);
+ fd_dev->fd_prot_file = NULL;
+}
- return blocks_long;
+static struct sbc_ops fd_sbc_ops = {
+ .execute_rw = fd_execute_rw,
+ .execute_sync_cache = fd_execute_sync_cache,
+ .execute_write_same = fd_execute_write_same,
+ .execute_write_same_unmap = fd_execute_write_same_unmap,
+ .execute_unmap = fd_execute_unmap,
+};
+
+static sense_reason_t
+fd_parse_cdb(struct se_cmd *cmd)
+{
+ return sbc_parse_cdb(cmd, &fd_sbc_ops);
}
static struct se_subsystem_api fileio_template = {
.name = "fileio",
+ .inquiry_prod = "FILEIO",
+ .inquiry_rev = FD_VERSION,
.owner = THIS_MODULE,
.transport_type = TRANSPORT_PLUGIN_VHBA_PDEV,
.attach_hba = fd_attach_hba,
.detach_hba = fd_detach_hba,
- .allocate_virtdevice = fd_allocate_virtdevice,
- .create_virtdevice = fd_create_virtdevice,
+ .alloc_device = fd_alloc_device,
+ .configure_device = fd_configure_device,
.free_device = fd_free_device,
- .dpo_emulated = fd_emulated_dpo,
- .fua_write_emulated = fd_emulated_fua_write,
- .fua_read_emulated = fd_emulated_fua_read,
- .write_cache_emulated = fd_emulated_write_cache,
- .alloc_task = fd_alloc_task,
- .do_task = fd_do_task,
- .do_sync_cache = fd_emulate_sync_cache,
- .free_task = fd_free_task,
- .check_configfs_dev_params = fd_check_configfs_dev_params,
+ .parse_cdb = fd_parse_cdb,
.set_configfs_dev_params = fd_set_configfs_dev_params,
.show_configfs_dev_params = fd_show_configfs_dev_params,
- .get_cdb = fd_get_cdb,
- .get_device_rev = fd_get_device_rev,
- .get_device_type = fd_get_device_type,
+ .get_device_type = sbc_get_device_type,
.get_blocks = fd_get_blocks,
+ .init_prot = fd_init_prot,
+ .format_prot = fd_format_prot,
+ .free_prot = fd_free_prot,
};
static int __init fileio_module_init(void)
@@ -675,7 +953,7 @@ static int __init fileio_module_init(void)
return transport_subsystem_register(&fileio_template);
}
-static void fileio_module_exit(void)
+static void __exit fileio_module_exit(void)
{
transport_subsystem_release(&fileio_template);
}
diff --git a/drivers/target/target_core_file.h b/drivers/target/target_core_file.h
index ef4de2b4bd4..182cbb29503 100644
--- a/drivers/target/target_core_file.h
+++ b/drivers/target/target_core_file.h
@@ -4,29 +4,32 @@
#define FD_VERSION "4.0"
#define FD_MAX_DEV_NAME 256
-/* Maximum queuedepth for the FILEIO HBA */
-#define FD_HBA_QUEUE_DEPTH 256
+#define FD_MAX_DEV_PROT_NAME FD_MAX_DEV_NAME + 16
#define FD_DEVICE_QUEUE_DEPTH 32
#define FD_MAX_DEVICE_QUEUE_DEPTH 128
#define FD_BLOCKSIZE 512
-#define FD_MAX_SECTORS 1024
+/*
+ * Limited by the number of iovecs (2048) per vfs_[writev,readv] call
+ */
+#define FD_MAX_BYTES 8388608
#define RRF_EMULATE_CDB 0x01
#define RRF_GOT_LBA 0x02
-struct fd_request {
- struct se_task fd_task;
- /* SCSI CDB from iSCSI Command PDU */
- unsigned char fd_scsi_cdb[TCM_MAX_COMMAND_SIZE];
- /* FILEIO device */
- struct fd_dev *fd_dev;
-} ____cacheline_aligned;
-
#define FBDF_HAS_PATH 0x01
#define FBDF_HAS_SIZE 0x02
-#define FDBD_USE_BUFFERED_IO 0x04
+#define FDBD_HAS_BUFFERED_IO_WCE 0x04
+#define FDBD_FORMAT_UNIT_SIZE 2048
+
+struct fd_prot {
+ unsigned char *prot_buf;
+ struct scatterlist *prot_sg;
+ u32 prot_sg_nents;
+};
struct fd_dev {
+ struct se_device dev;
+
u32 fbd_flags;
unsigned char fd_dev_name[FD_MAX_DEV_NAME];
/* Unique Ramdisk Device ID in Ramdisk HBA */
@@ -37,6 +40,7 @@ struct fd_dev {
u32 fd_block_size;
unsigned long long fd_dev_size;
struct file *fd_file;
+ struct file *fd_prot_file;
/* FILEIO HBA device is connected to */
struct fd_host *fd_host;
} ____cacheline_aligned;
diff --git a/drivers/target/target_core_hba.c b/drivers/target/target_core_hba.c
index 4bbe8208b24..a25051a37dd 100644
--- a/drivers/target/target_core_hba.c
+++ b/drivers/target/target_core_hba.c
@@ -1,12 +1,9 @@
/*******************************************************************************
* Filename: target_core_hba.c
*
- * This file copntains the iSCSI HBA Transport related functions.
+ * This file contains the TCM HBA Transport related functions.
*
- * Copyright (c) 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
+ * (c) Copyright 2003-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -31,22 +28,25 @@
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
#include <linux/in.h>
+#include <linux/module.h>
#include <net/sock.h>
#include <net/tcp.h>
#include <target/target_core_base.h>
-#include <target/target_core_device.h>
-#include <target/target_core_device.h>
-#include <target/target_core_tpg.h>
-#include <target/target_core_transport.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
-#include "target_core_hba.h"
+#include "target_core_internal.h"
static LIST_HEAD(subsystem_list);
static DEFINE_MUTEX(subsystem_mutex);
+static u32 hba_id_counter;
+
+static DEFINE_SPINLOCK(hba_lock);
+static LIST_HEAD(hba_list);
+
int transport_subsystem_register(struct se_subsystem_api *sub_api)
{
struct se_subsystem_api *s;
@@ -55,8 +55,8 @@ int transport_subsystem_register(struct se_subsystem_api *sub_api)
mutex_lock(&subsystem_mutex);
list_for_each_entry(s, &subsystem_list, sub_api_list) {
- if (!(strcmp(s->name, sub_api->name))) {
- printk(KERN_ERR "%p is already registered with"
+ if (!strcmp(s->name, sub_api->name)) {
+ pr_err("%p is already registered with"
" duplicate name %s, unable to process"
" request\n", s, s->name);
mutex_unlock(&subsystem_mutex);
@@ -66,7 +66,7 @@ int transport_subsystem_register(struct se_subsystem_api *sub_api)
list_add_tail(&sub_api->sub_api_list, &subsystem_list);
mutex_unlock(&subsystem_mutex);
- printk(KERN_INFO "TCM: Registered subsystem plugin: %s struct module:"
+ pr_debug("TCM: Registered subsystem plugin: %s struct module:"
" %p\n", sub_api->name, sub_api->owner);
return 0;
}
@@ -106,21 +106,16 @@ core_alloc_hba(const char *plugin_name, u32 plugin_dep_id, u32 hba_flags)
hba = kzalloc(sizeof(*hba), GFP_KERNEL);
if (!hba) {
- printk(KERN_ERR "Unable to allocate struct se_hba\n");
+ pr_err("Unable to allocate struct se_hba\n");
return ERR_PTR(-ENOMEM);
}
- INIT_LIST_HEAD(&hba->hba_dev_list);
spin_lock_init(&hba->device_lock);
- spin_lock_init(&hba->hba_queue_lock);
mutex_init(&hba->hba_access_mutex);
hba->hba_index = scsi_get_new_index(SCSI_INST_INDEX);
hba->hba_flags |= hba_flags;
- atomic_set(&hba->max_queue_depth, 0);
- atomic_set(&hba->left_queue_depth, 0);
-
hba->transport = core_get_backend(plugin_name);
if (!hba->transport) {
ret = -EINVAL;
@@ -131,12 +126,12 @@ core_alloc_hba(const char *plugin_name, u32 plugin_dep_id, u32 hba_flags)
if (ret < 0)
goto out_module_put;
- spin_lock(&se_global->hba_lock);
- hba->hba_id = se_global->g_hba_id_counter++;
- list_add_tail(&hba->hba_list, &se_global->g_hba_list);
- spin_unlock(&se_global->hba_lock);
+ spin_lock(&hba_lock);
+ hba->hba_id = hba_id_counter++;
+ list_add_tail(&hba->hba_node, &hba_list);
+ spin_unlock(&hba_lock);
- printk(KERN_INFO "CORE_HBA[%d] - Attached HBA to Generic Target"
+ pr_debug("CORE_HBA[%d] - Attached HBA to Generic Target"
" Core\n", hba->hba_id);
return hba;
@@ -153,27 +148,15 @@ out_free_hba:
int
core_delete_hba(struct se_hba *hba)
{
- struct se_device *dev, *dev_tmp;
-
- spin_lock(&hba->device_lock);
- list_for_each_entry_safe(dev, dev_tmp, &hba->hba_dev_list, dev_list) {
-
- se_clear_dev_ports(dev);
- spin_unlock(&hba->device_lock);
-
- se_release_device_for_hba(dev);
-
- spin_lock(&hba->device_lock);
- }
- spin_unlock(&hba->device_lock);
+ WARN_ON(hba->dev_count);
hba->transport->detach_hba(hba);
- spin_lock(&se_global->hba_lock);
- list_del(&hba->hba_list);
- spin_unlock(&se_global->hba_lock);
+ spin_lock(&hba_lock);
+ list_del(&hba->hba_node);
+ spin_unlock(&hba_lock);
- printk(KERN_INFO "CORE_HBA[%d] - Detached HBA from Generic Target"
+ pr_debug("CORE_HBA[%d] - Detached HBA from Generic Target"
" Core\n", hba->hba_id);
if (hba->transport->owner)
diff --git a/drivers/target/target_core_hba.h b/drivers/target/target_core_hba.h
deleted file mode 100644
index bb0fea5f730..00000000000
--- a/drivers/target/target_core_hba.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef TARGET_CORE_HBA_H
-#define TARGET_CORE_HBA_H
-
-extern struct se_hba *core_alloc_hba(const char *, u32, u32);
-extern int core_delete_hba(struct se_hba *);
-
-#endif /* TARGET_CORE_HBA_H */
diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
index 67f0c09983c..7e6b857c6b3 100644
--- a/drivers/target/target_core_iblock.c
+++ b/drivers/target/target_core_iblock.c
@@ -4,10 +4,7 @@
* This file contains the Storage Engine <-> Linux BlockIO transport
* specific functions.
*
- * Copyright (c) 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
+ * (c) Copyright 2003-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -27,7 +24,6 @@
*
******************************************************************************/
-#include <linux/version.h>
#include <linux/string.h>
#include <linux/parser.h>
#include <linux/timer.h>
@@ -35,28 +31,29 @@
#include <linux/blkdev.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
#include <linux/bio.h>
#include <linux/genhd.h>
#include <linux/file.h>
+#include <linux/module.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
+#include <asm/unaligned.h>
#include <target/target_core_base.h>
-#include <target/target_core_device.h>
-#include <target/target_core_transport.h>
+#include <target/target_core_backend.h>
#include "target_core_iblock.h"
-#if 0
-#define DEBUG_IBLOCK(x...) printk(x)
-#else
-#define DEBUG_IBLOCK(x...)
-#endif
+#define IBLOCK_MAX_BIO_PER_TASK 32 /* max # of bios to submit at a time */
+#define IBLOCK_BIO_POOL_SIZE 128
-static struct se_subsystem_api iblock_template;
+static inline struct iblock_dev *IBLOCK_DEV(struct se_device *dev)
+{
+ return container_of(dev, struct iblock_dev, dev);
+}
-static void iblock_bio_done(struct bio *, int);
+
+static struct se_subsystem_api iblock_template;
/* iblock_attach_hba(): (Part of se_subsystem_api_t template)
*
@@ -64,188 +61,152 @@ static void iblock_bio_done(struct bio *, int);
*/
static int iblock_attach_hba(struct se_hba *hba, u32 host_id)
{
- struct iblock_hba *ib_host;
-
- ib_host = kzalloc(sizeof(struct iblock_hba), GFP_KERNEL);
- if (!(ib_host)) {
- printk(KERN_ERR "Unable to allocate memory for"
- " struct iblock_hba\n");
- return -ENOMEM;
- }
-
- ib_host->iblock_host_id = host_id;
-
- atomic_set(&hba->left_queue_depth, IBLOCK_HBA_QUEUE_DEPTH);
- atomic_set(&hba->max_queue_depth, IBLOCK_HBA_QUEUE_DEPTH);
- hba->hba_ptr = (void *) ib_host;
-
- printk(KERN_INFO "CORE_HBA[%d] - TCM iBlock HBA Driver %s on"
+ pr_debug("CORE_HBA[%d] - TCM iBlock HBA Driver %s on"
" Generic Target Core Stack %s\n", hba->hba_id,
IBLOCK_VERSION, TARGET_CORE_MOD_VERSION);
-
- printk(KERN_INFO "CORE_HBA[%d] - Attached iBlock HBA: %u to Generic"
- " Target Core TCQ Depth: %d\n", hba->hba_id,
- ib_host->iblock_host_id, atomic_read(&hba->max_queue_depth));
-
return 0;
}
static void iblock_detach_hba(struct se_hba *hba)
{
- struct iblock_hba *ib_host = hba->hba_ptr;
-
- printk(KERN_INFO "CORE_HBA[%d] - Detached iBlock HBA: %u from Generic"
- " Target Core\n", hba->hba_id, ib_host->iblock_host_id);
-
- kfree(ib_host);
- hba->hba_ptr = NULL;
}
-static void *iblock_allocate_virtdevice(struct se_hba *hba, const char *name)
+static struct se_device *iblock_alloc_device(struct se_hba *hba, const char *name)
{
struct iblock_dev *ib_dev = NULL;
- struct iblock_hba *ib_host = hba->hba_ptr;
ib_dev = kzalloc(sizeof(struct iblock_dev), GFP_KERNEL);
- if (!(ib_dev)) {
- printk(KERN_ERR "Unable to allocate struct iblock_dev\n");
+ if (!ib_dev) {
+ pr_err("Unable to allocate struct iblock_dev\n");
return NULL;
}
- ib_dev->ibd_host = ib_host;
- printk(KERN_INFO "IBLOCK: Allocated ib_dev for %s\n", name);
+ pr_debug( "IBLOCK: Allocated ib_dev for %s\n", name);
- return ib_dev;
+ return &ib_dev->dev;
}
-static struct se_device *iblock_create_virtdevice(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- void *p)
+static int iblock_configure_device(struct se_device *dev)
{
- struct iblock_dev *ib_dev = p;
- struct se_device *dev;
- struct se_dev_limits dev_limits;
- struct block_device *bd = NULL;
+ struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
struct request_queue *q;
- struct queue_limits *limits;
- u32 dev_flags = 0;
+ struct block_device *bd = NULL;
+ struct blk_integrity *bi;
+ fmode_t mode;
+ int ret = -ENOMEM;
- if (!(ib_dev)) {
- printk(KERN_ERR "Unable to locate struct iblock_dev parameter\n");
- return 0;
+ if (!(ib_dev->ibd_flags & IBDF_HAS_UDEV_PATH)) {
+ pr_err("Missing udev_path= parameters for IBLOCK\n");
+ return -EINVAL;
}
- memset(&dev_limits, 0, sizeof(struct se_dev_limits));
- /*
- * These settings need to be made tunable..
- */
- ib_dev->ibd_bio_set = bioset_create(32, 64);
- if (!(ib_dev->ibd_bio_set)) {
- printk(KERN_ERR "IBLOCK: Unable to create bioset()\n");
- return 0;
+
+ ib_dev->ibd_bio_set = bioset_create(IBLOCK_BIO_POOL_SIZE, 0);
+ if (!ib_dev->ibd_bio_set) {
+ pr_err("IBLOCK: Unable to create bioset\n");
+ goto out;
}
- printk(KERN_INFO "IBLOCK: Created bio_set()\n");
- /*
- * iblock_check_configfs_dev_params() ensures that ib_dev->ibd_udev_path
- * must already have been set in order for echo 1 > $HBA/$DEV/enable to run.
- */
- printk(KERN_INFO "IBLOCK: Claiming struct block_device: %s\n",
+
+ pr_debug( "IBLOCK: Claiming struct block_device: %s\n",
ib_dev->ibd_udev_path);
- bd = blkdev_get_by_path(ib_dev->ibd_udev_path,
- FMODE_WRITE|FMODE_READ|FMODE_EXCL, ib_dev);
- if (IS_ERR(bd))
- goto failed;
- /*
- * Setup the local scope queue_limits from struct request_queue->limits
- * to pass into transport_add_device_to_core_hba() as struct se_dev_limits.
- */
- q = bdev_get_queue(bd);
- limits = &dev_limits.limits;
- limits->logical_block_size = bdev_logical_block_size(bd);
- limits->max_hw_sectors = queue_max_hw_sectors(q);
- limits->max_sectors = queue_max_sectors(q);
- dev_limits.hw_queue_depth = IBLOCK_MAX_DEVICE_QUEUE_DEPTH;
- dev_limits.queue_depth = IBLOCK_DEVICE_QUEUE_DEPTH;
-
- ib_dev->ibd_major = MAJOR(bd->bd_dev);
- ib_dev->ibd_minor = MINOR(bd->bd_dev);
+ mode = FMODE_READ|FMODE_EXCL;
+ if (!ib_dev->ibd_readonly)
+ mode |= FMODE_WRITE;
+
+ bd = blkdev_get_by_path(ib_dev->ibd_udev_path, mode, ib_dev);
+ if (IS_ERR(bd)) {
+ ret = PTR_ERR(bd);
+ goto out_free_bioset;
+ }
ib_dev->ibd_bd = bd;
- dev = transport_add_device_to_core_hba(hba,
- &iblock_template, se_dev, dev_flags, (void *)ib_dev,
- &dev_limits, "IBLOCK", IBLOCK_VERSION);
- if (!(dev))
- goto failed;
+ q = bdev_get_queue(bd);
- ib_dev->ibd_depth = dev->queue_depth;
+ dev->dev_attrib.hw_block_size = bdev_logical_block_size(bd);
+ dev->dev_attrib.hw_max_sectors = UINT_MAX;
+ dev->dev_attrib.hw_queue_depth = q->nr_requests;
/*
* Check if the underlying struct block_device request_queue supports
* the QUEUE_FLAG_DISCARD bit for UNMAP/WRITE_SAME in SCSI + TRIM
* in ATA and we need to set TPE=1
*/
- if (blk_queue_discard(bdev_get_queue(bd))) {
- struct request_queue *q = bdev_get_queue(bd);
-
- DEV_ATTRIB(dev)->max_unmap_lba_count =
+ if (blk_queue_discard(q)) {
+ dev->dev_attrib.max_unmap_lba_count =
q->limits.max_discard_sectors;
+
/*
* Currently hardcoded to 1 in Linux/SCSI code..
*/
- DEV_ATTRIB(dev)->max_unmap_block_desc_count = 1;
- DEV_ATTRIB(dev)->unmap_granularity =
- q->limits.discard_granularity;
- DEV_ATTRIB(dev)->unmap_granularity_alignment =
+ dev->dev_attrib.max_unmap_block_desc_count = 1;
+ dev->dev_attrib.unmap_granularity =
+ q->limits.discard_granularity >> 9;
+ dev->dev_attrib.unmap_granularity_alignment =
q->limits.discard_alignment;
- printk(KERN_INFO "IBLOCK: BLOCK Discard support available,"
+ pr_debug("IBLOCK: BLOCK Discard support available,"
" disabled by default\n");
}
+ /*
+ * Enable write same emulation for IBLOCK and use 0xFFFF as
+ * the smaller WRITE_SAME(10) only has a two-byte block count.
+ */
+ dev->dev_attrib.max_write_same_len = 0xFFFF;
- return dev;
+ if (blk_queue_nonrot(q))
+ dev->dev_attrib.is_nonrot = 1;
-failed:
- if (ib_dev->ibd_bio_set) {
- bioset_free(ib_dev->ibd_bio_set);
- ib_dev->ibd_bio_set = NULL;
+ bi = bdev_get_integrity(bd);
+ if (bi) {
+ struct bio_set *bs = ib_dev->ibd_bio_set;
+
+ if (!strcmp(bi->name, "T10-DIF-TYPE3-IP") ||
+ !strcmp(bi->name, "T10-DIF-TYPE1-IP")) {
+ pr_err("IBLOCK export of blk_integrity: %s not"
+ " supported\n", bi->name);
+ ret = -ENOSYS;
+ goto out_blkdev_put;
+ }
+
+ if (!strcmp(bi->name, "T10-DIF-TYPE3-CRC")) {
+ dev->dev_attrib.pi_prot_type = TARGET_DIF_TYPE3_PROT;
+ } else if (!strcmp(bi->name, "T10-DIF-TYPE1-CRC")) {
+ dev->dev_attrib.pi_prot_type = TARGET_DIF_TYPE1_PROT;
+ }
+
+ if (dev->dev_attrib.pi_prot_type) {
+ if (bioset_integrity_create(bs, IBLOCK_BIO_POOL_SIZE) < 0) {
+ pr_err("Unable to allocate bioset for PI\n");
+ ret = -ENOMEM;
+ goto out_blkdev_put;
+ }
+ pr_debug("IBLOCK setup BIP bs->bio_integrity_pool: %p\n",
+ bs->bio_integrity_pool);
+ }
+ dev->dev_attrib.hw_pi_prot_type = dev->dev_attrib.pi_prot_type;
}
- ib_dev->ibd_bd = NULL;
- ib_dev->ibd_major = 0;
- ib_dev->ibd_minor = 0;
- return NULL;
+
+ return 0;
+
+out_blkdev_put:
+ blkdev_put(ib_dev->ibd_bd, FMODE_WRITE|FMODE_READ|FMODE_EXCL);
+out_free_bioset:
+ bioset_free(ib_dev->ibd_bio_set);
+ ib_dev->ibd_bio_set = NULL;
+out:
+ return ret;
}
-static void iblock_free_device(void *p)
+static void iblock_free_device(struct se_device *dev)
{
- struct iblock_dev *ib_dev = p;
+ struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
if (ib_dev->ibd_bd != NULL)
blkdev_put(ib_dev->ibd_bd, FMODE_WRITE|FMODE_READ|FMODE_EXCL);
if (ib_dev->ibd_bio_set != NULL)
bioset_free(ib_dev->ibd_bio_set);
- kfree(ib_dev);
-}
-static inline struct iblock_req *IBLOCK_REQ(struct se_task *task)
-{
- return container_of(task, struct iblock_req, ib_task);
-}
-
-static struct se_task *
-iblock_alloc_task(struct se_cmd *cmd)
-{
- struct iblock_req *ib_req;
-
- ib_req = kzalloc(sizeof(struct iblock_req), GFP_KERNEL);
- if (!(ib_req)) {
- printk(KERN_ERR "Unable to allocate memory for struct iblock_req\n");
- return NULL;
- }
-
- ib_req->ib_dev = SE_DEV(cmd)->dev_ptr;
- atomic_set(&ib_req->ib_bio_cnt, 0);
- return &ib_req->ib_task;
+ kfree(ib_dev);
}
static unsigned long long iblock_emulate_read_cap_with_block_size(
@@ -257,12 +218,12 @@ static unsigned long long iblock_emulate_read_cap_with_block_size(
bdev_logical_block_size(bd)) - 1);
u32 block_size = bdev_logical_block_size(bd);
- if (block_size == DEV_ATTRIB(dev)->block_size)
+ if (block_size == dev->dev_attrib.block_size)
return blocks_long;
switch (block_size) {
case 4096:
- switch (DEV_ATTRIB(dev)->block_size) {
+ switch (dev->dev_attrib.block_size) {
case 2048:
blocks_long <<= 1;
break;
@@ -276,7 +237,7 @@ static unsigned long long iblock_emulate_read_cap_with_block_size(
}
break;
case 2048:
- switch (DEV_ATTRIB(dev)->block_size) {
+ switch (dev->dev_attrib.block_size) {
case 4096:
blocks_long >>= 1;
break;
@@ -291,7 +252,7 @@ static unsigned long long iblock_emulate_read_cap_with_block_size(
}
break;
case 1024:
- switch (DEV_ATTRIB(dev)->block_size) {
+ switch (dev->dev_attrib.block_size) {
case 4096:
blocks_long >>= 2;
break;
@@ -306,7 +267,7 @@ static unsigned long long iblock_emulate_read_cap_with_block_size(
}
break;
case 512:
- switch (DEV_ATTRIB(dev)->block_size) {
+ switch (dev->dev_attrib.block_size) {
case 4096:
blocks_long >>= 3;
break;
@@ -327,151 +288,253 @@ static unsigned long long iblock_emulate_read_cap_with_block_size(
return blocks_long;
}
-/*
- * Emulate SYCHRONIZE_CACHE_*
- */
-static void iblock_emulate_sync_cache(struct se_task *task)
+static void iblock_complete_cmd(struct se_cmd *cmd)
{
- struct se_cmd *cmd = TASK_CMD(task);
- struct iblock_dev *ib_dev = cmd->se_dev->dev_ptr;
- int immed = (T_TASK(cmd)->t_task_cdb[1] & 0x2);
- sector_t error_sector;
- int ret;
+ struct iblock_req *ibr = cmd->priv;
+ u8 status;
+
+ if (!atomic_dec_and_test(&ibr->pending))
+ return;
+
+ if (atomic_read(&ibr->ib_bio_err_cnt))
+ status = SAM_STAT_CHECK_CONDITION;
+ else
+ status = SAM_STAT_GOOD;
+
+ target_complete_cmd(cmd, status);
+ kfree(ibr);
+}
+
+static void iblock_bio_done(struct bio *bio, int err)
+{
+ struct se_cmd *cmd = bio->bi_private;
+ struct iblock_req *ibr = cmd->priv;
/*
- * If the Immediate bit is set, queue up the GOOD response
- * for this SYNCHRONIZE_CACHE op
+ * Set -EIO if !BIO_UPTODATE and the passed is still err=0
*/
- if (immed)
- transport_complete_sync_cache(cmd, 1);
+ if (!test_bit(BIO_UPTODATE, &bio->bi_flags) && !err)
+ err = -EIO;
+
+ if (err != 0) {
+ pr_err("test_bit(BIO_UPTODATE) failed for bio: %p,"
+ " err: %d\n", bio, err);
+ /*
+ * Bump the ib_bio_err_cnt and release bio.
+ */
+ atomic_inc(&ibr->ib_bio_err_cnt);
+ smp_mb__after_atomic();
+ }
+
+ bio_put(bio);
+
+ iblock_complete_cmd(cmd);
+}
+
+static struct bio *
+iblock_get_bio(struct se_cmd *cmd, sector_t lba, u32 sg_num)
+{
+ struct iblock_dev *ib_dev = IBLOCK_DEV(cmd->se_dev);
+ struct bio *bio;
/*
- * blkdev_issue_flush() does not support a specifying a range, so
- * we have to flush the entire cache.
+ * Only allocate as many vector entries as the bio code allows us to,
+ * we'll loop later on until we have handled the whole request.
*/
- ret = blkdev_issue_flush(ib_dev->ibd_bd, GFP_KERNEL, &error_sector);
- if (ret != 0) {
- printk(KERN_ERR "IBLOCK: block_issue_flush() failed: %d "
- " error_sector: %llu\n", ret,
- (unsigned long long)error_sector);
+ if (sg_num > BIO_MAX_PAGES)
+ sg_num = BIO_MAX_PAGES;
+
+ bio = bio_alloc_bioset(GFP_NOIO, sg_num, ib_dev->ibd_bio_set);
+ if (!bio) {
+ pr_err("Unable to allocate memory for bio\n");
+ return NULL;
}
- if (!immed)
- transport_complete_sync_cache(cmd, ret == 0);
+ bio->bi_bdev = ib_dev->ibd_bd;
+ bio->bi_private = cmd;
+ bio->bi_end_io = &iblock_bio_done;
+ bio->bi_iter.bi_sector = lba;
+
+ return bio;
}
-/*
- * Tell TCM Core that we are capable of WriteCache emulation for
- * an underlying struct se_device.
- */
-static int iblock_emulated_write_cache(struct se_device *dev)
+static void iblock_submit_bios(struct bio_list *list, int rw)
{
- return 1;
+ struct blk_plug plug;
+ struct bio *bio;
+
+ blk_start_plug(&plug);
+ while ((bio = bio_list_pop(list)))
+ submit_bio(rw, bio);
+ blk_finish_plug(&plug);
}
-static int iblock_emulated_dpo(struct se_device *dev)
+static void iblock_end_io_flush(struct bio *bio, int err)
{
- return 0;
+ struct se_cmd *cmd = bio->bi_private;
+
+ if (err)
+ pr_err("IBLOCK: cache flush failed: %d\n", err);
+
+ if (cmd) {
+ if (err)
+ target_complete_cmd(cmd, SAM_STAT_CHECK_CONDITION);
+ else
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
+ }
+
+ bio_put(bio);
}
/*
- * Tell TCM Core that we will be emulating Forced Unit Access (FUA) for WRITEs
- * for TYPE_DISK.
+ * Implement SYCHRONIZE CACHE. Note that we can't handle lba ranges and must
+ * always flush the whole cache.
*/
-static int iblock_emulated_fua_write(struct se_device *dev)
+static sense_reason_t
+iblock_execute_sync_cache(struct se_cmd *cmd)
{
- return 1;
-}
+ struct iblock_dev *ib_dev = IBLOCK_DEV(cmd->se_dev);
+ int immed = (cmd->t_task_cdb[1] & 0x2);
+ struct bio *bio;
-static int iblock_emulated_fua_read(struct se_device *dev)
-{
+ /*
+ * If the Immediate bit is set, queue up the GOOD response
+ * for this SYNCHRONIZE_CACHE op.
+ */
+ if (immed)
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
+
+ bio = bio_alloc(GFP_KERNEL, 0);
+ bio->bi_end_io = iblock_end_io_flush;
+ bio->bi_bdev = ib_dev->ibd_bd;
+ if (!immed)
+ bio->bi_private = cmd;
+ submit_bio(WRITE_FLUSH, bio);
return 0;
}
-static int iblock_do_task(struct se_task *task)
+static sense_reason_t
+iblock_do_unmap(struct se_cmd *cmd, void *priv,
+ sector_t lba, sector_t nolb)
{
- struct se_device *dev = task->task_se_cmd->se_dev;
- struct iblock_req *req = IBLOCK_REQ(task);
- struct iblock_dev *ibd = (struct iblock_dev *)req->ib_dev;
- struct request_queue *q = bdev_get_queue(ibd->ibd_bd);
- struct bio *bio = req->ib_bio, *nbio = NULL;
- int rw;
-
- if (task->task_data_direction == DMA_TO_DEVICE) {
- /*
- * Force data to disk if we pretend to not have a volatile
- * write cache, or the initiator set the Force Unit Access bit.
- */
- if (DEV_ATTRIB(dev)->emulate_write_cache == 0 ||
- (DEV_ATTRIB(dev)->emulate_fua_write > 0 &&
- T_TASK(task->task_se_cmd)->t_tasks_fua))
- rw = WRITE_FUA;
- else
- rw = WRITE;
- } else {
- rw = READ;
+ struct block_device *bdev = priv;
+ int ret;
+
+ ret = blkdev_issue_discard(bdev, lba, nolb, GFP_KERNEL, 0);
+ if (ret < 0) {
+ pr_err("blkdev_issue_discard() failed: %d\n", ret);
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
- while (bio) {
- nbio = bio->bi_next;
- bio->bi_next = NULL;
- DEBUG_IBLOCK("Calling submit_bio() task: %p bio: %p"
- " bio->bi_sector: %llu\n", task, bio, bio->bi_sector);
+ return 0;
+}
- submit_bio(rw, bio);
- bio = nbio;
- }
+static sense_reason_t
+iblock_execute_unmap(struct se_cmd *cmd)
+{
+ struct block_device *bdev = IBLOCK_DEV(cmd->se_dev)->ibd_bd;
- if (q->unplug_fn)
- q->unplug_fn(q);
- return PYX_TRANSPORT_SENT_TO_TRANSPORT;
+ return sbc_execute_unmap(cmd, iblock_do_unmap, bdev);
}
-static int iblock_do_discard(struct se_device *dev, sector_t lba, u32 range)
+static sense_reason_t
+iblock_execute_write_same_unmap(struct se_cmd *cmd)
{
- struct iblock_dev *ibd = dev->dev_ptr;
- struct block_device *bd = ibd->ibd_bd;
- int barrier = 0;
+ struct block_device *bdev = IBLOCK_DEV(cmd->se_dev)->ibd_bd;
+ sector_t lba = cmd->t_task_lba;
+ sector_t nolb = sbc_get_write_same_sectors(cmd);
+ int ret;
+
+ ret = iblock_do_unmap(cmd, bdev, lba, nolb);
+ if (ret)
+ return ret;
- return blkdev_issue_discard(bd, lba, range, GFP_KERNEL, barrier);
+ target_complete_cmd(cmd, GOOD);
+ return 0;
}
-static void iblock_free_task(struct se_task *task)
+static sense_reason_t
+iblock_execute_write_same(struct se_cmd *cmd)
{
- struct iblock_req *req = IBLOCK_REQ(task);
- struct bio *bio, *hbio = req->ib_bio;
- /*
- * We only release the bio(s) here if iblock_bio_done() has not called
- * bio_put() -> iblock_bio_destructor().
- */
- while (hbio != NULL) {
- bio = hbio;
- hbio = hbio->bi_next;
- bio->bi_next = NULL;
- bio_put(bio);
+ struct iblock_req *ibr;
+ struct scatterlist *sg;
+ struct bio *bio;
+ struct bio_list list;
+ sector_t block_lba = cmd->t_task_lba;
+ sector_t sectors = sbc_get_write_same_sectors(cmd);
+
+ sg = &cmd->t_data_sg[0];
+
+ if (cmd->t_data_nents > 1 ||
+ sg->length != cmd->se_dev->dev_attrib.block_size) {
+ pr_err("WRITE_SAME: Illegal SGL t_data_nents: %u length: %u"
+ " block_size: %u\n", cmd->t_data_nents, sg->length,
+ cmd->se_dev->dev_attrib.block_size);
+ return TCM_INVALID_CDB_FIELD;
}
- kfree(req);
+ ibr = kzalloc(sizeof(struct iblock_req), GFP_KERNEL);
+ if (!ibr)
+ goto fail;
+ cmd->priv = ibr;
+
+ bio = iblock_get_bio(cmd, block_lba, 1);
+ if (!bio)
+ goto fail_free_ibr;
+
+ bio_list_init(&list);
+ bio_list_add(&list, bio);
+
+ atomic_set(&ibr->pending, 1);
+
+ while (sectors) {
+ while (bio_add_page(bio, sg_page(sg), sg->length, sg->offset)
+ != sg->length) {
+
+ bio = iblock_get_bio(cmd, block_lba, 1);
+ if (!bio)
+ goto fail_put_bios;
+
+ atomic_inc(&ibr->pending);
+ bio_list_add(&list, bio);
+ }
+
+ /* Always in 512 byte units for Linux/Block */
+ block_lba += sg->length >> IBLOCK_LBA_SHIFT;
+ sectors -= 1;
+ }
+
+ iblock_submit_bios(&list, WRITE);
+ return 0;
+
+fail_put_bios:
+ while ((bio = bio_list_pop(&list)))
+ bio_put(bio);
+fail_free_ibr:
+ kfree(ibr);
+fail:
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
enum {
- Opt_udev_path, Opt_force, Opt_err
+ Opt_udev_path, Opt_readonly, Opt_force, Opt_err
};
static match_table_t tokens = {
{Opt_udev_path, "udev_path=%s"},
+ {Opt_readonly, "readonly=%d"},
{Opt_force, "force=%d"},
{Opt_err, NULL}
};
-static ssize_t iblock_set_configfs_dev_params(struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- const char *page, ssize_t count)
+static ssize_t iblock_set_configfs_dev_params(struct se_device *dev,
+ const char *page, ssize_t count)
{
- struct iblock_dev *ib_dev = se_dev->se_dev_su_ptr;
- char *orig, *ptr, *opts;
+ struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+ char *orig, *ptr, *arg_p, *opts;
substring_t args[MAX_OPT_ARGS];
- int ret = 0, arg, token;
+ int ret = 0, token;
+ unsigned long tmp_readonly;
opts = kstrdup(page, GFP_KERNEL);
if (!opts)
@@ -479,7 +542,7 @@ static ssize_t iblock_set_configfs_dev_params(struct se_hba *hba,
orig = opts;
- while ((ptr = strsep(&opts, ",")) != NULL) {
+ while ((ptr = strsep(&opts, ",\n")) != NULL) {
if (!*ptr)
continue;
@@ -487,23 +550,37 @@ static ssize_t iblock_set_configfs_dev_params(struct se_hba *hba,
switch (token) {
case Opt_udev_path:
if (ib_dev->ibd_bd) {
- printk(KERN_ERR "Unable to set udev_path= while"
+ pr_err("Unable to set udev_path= while"
" ib_dev->ibd_bd exists\n");
ret = -EEXIST;
goto out;
}
-
- ret = snprintf(ib_dev->ibd_udev_path, SE_UDEV_PATH_LEN,
- "%s", match_strdup(&args[0]));
- printk(KERN_INFO "IBLOCK: Referencing UDEV path: %s\n",
+ if (match_strlcpy(ib_dev->ibd_udev_path, &args[0],
+ SE_UDEV_PATH_LEN) == 0) {
+ ret = -EINVAL;
+ break;
+ }
+ pr_debug("IBLOCK: Referencing UDEV path: %s\n",
ib_dev->ibd_udev_path);
ib_dev->ibd_flags |= IBDF_HAS_UDEV_PATH;
break;
+ case Opt_readonly:
+ arg_p = match_strdup(&args[0]);
+ if (!arg_p) {
+ ret = -ENOMEM;
+ break;
+ }
+ ret = kstrtoul(arg_p, 0, &tmp_readonly);
+ kfree(arg_p);
+ if (ret < 0) {
+ pr_err("kstrtoul() failed for"
+ " readonly=\n");
+ goto out;
+ }
+ ib_dev->ibd_readonly = tmp_readonly;
+ pr_debug("IBLOCK: readonly: %d\n", ib_dev->ibd_readonly);
+ break;
case Opt_force:
- match_int(args, &arg);
- ib_dev->ibd_force = arg;
- printk(KERN_INFO "IBLOCK: Set force=%d\n",
- ib_dev->ibd_force);
break;
default:
break;
@@ -515,281 +592,293 @@ out:
return (!ret) ? count : ret;
}
-static ssize_t iblock_check_configfs_dev_params(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev)
-{
- struct iblock_dev *ibd = se_dev->se_dev_su_ptr;
-
- if (!(ibd->ibd_flags & IBDF_HAS_UDEV_PATH)) {
- printk(KERN_ERR "Missing udev_path= parameters for IBLOCK\n");
- return -1;
- }
-
- return 0;
-}
-
-static ssize_t iblock_show_configfs_dev_params(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- char *b)
+static ssize_t iblock_show_configfs_dev_params(struct se_device *dev, char *b)
{
- struct iblock_dev *ibd = se_dev->se_dev_su_ptr;
- struct block_device *bd = ibd->ibd_bd;
+ struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+ struct block_device *bd = ib_dev->ibd_bd;
char buf[BDEVNAME_SIZE];
ssize_t bl = 0;
if (bd)
bl += sprintf(b + bl, "iBlock device: %s",
bdevname(bd, buf));
- if (ibd->ibd_flags & IBDF_HAS_UDEV_PATH) {
- bl += sprintf(b + bl, " UDEV PATH: %s\n",
- ibd->ibd_udev_path);
- } else
- bl += sprintf(b + bl, "\n");
+ if (ib_dev->ibd_flags & IBDF_HAS_UDEV_PATH)
+ bl += sprintf(b + bl, " UDEV PATH: %s",
+ ib_dev->ibd_udev_path);
+ bl += sprintf(b + bl, " readonly: %d\n", ib_dev->ibd_readonly);
bl += sprintf(b + bl, " ");
if (bd) {
bl += sprintf(b + bl, "Major: %d Minor: %d %s\n",
- ibd->ibd_major, ibd->ibd_minor, (!bd->bd_contains) ?
- "" : (bd->bd_holder == (struct iblock_dev *)ibd) ?
+ MAJOR(bd->bd_dev), MINOR(bd->bd_dev), (!bd->bd_contains) ?
+ "" : (bd->bd_holder == ib_dev) ?
"CLAIMED: IBLOCK" : "CLAIMED: OS");
} else {
- bl += sprintf(b + bl, "Major: %d Minor: %d\n",
- ibd->ibd_major, ibd->ibd_minor);
+ bl += sprintf(b + bl, "Major: 0 Minor: 0\n");
}
return bl;
}
-static void iblock_bio_destructor(struct bio *bio)
+static int
+iblock_alloc_bip(struct se_cmd *cmd, struct bio *bio)
{
- struct se_task *task = bio->bi_private;
- struct iblock_dev *ib_dev = task->se_dev->dev_ptr;
-
- bio_free(bio, ib_dev->ibd_bio_set);
-}
+ struct se_device *dev = cmd->se_dev;
+ struct blk_integrity *bi;
+ struct bio_integrity_payload *bip;
+ struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+ struct scatterlist *sg;
+ int i, rc;
-static struct bio *iblock_get_bio(
- struct se_task *task,
- struct iblock_req *ib_req,
- struct iblock_dev *ib_dev,
- int *ret,
- sector_t lba,
- u32 sg_num)
-{
- struct bio *bio;
+ bi = bdev_get_integrity(ib_dev->ibd_bd);
+ if (!bi) {
+ pr_err("Unable to locate bio_integrity\n");
+ return -ENODEV;
+ }
- bio = bio_alloc_bioset(GFP_NOIO, sg_num, ib_dev->ibd_bio_set);
- if (!(bio)) {
- printk(KERN_ERR "Unable to allocate memory for bio\n");
- *ret = PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES;
- return NULL;
+ bip = bio_integrity_alloc(bio, GFP_NOIO, cmd->t_prot_nents);
+ if (!bip) {
+ pr_err("Unable to allocate bio_integrity_payload\n");
+ return -ENOMEM;
}
- DEBUG_IBLOCK("Allocated bio: %p task_sg_num: %u using ibd_bio_set:"
- " %p\n", bio, task->task_sg_num, ib_dev->ibd_bio_set);
- DEBUG_IBLOCK("Allocated bio: %p task_size: %u\n", bio, task->task_size);
+ bip->bip_iter.bi_size = (cmd->data_length / dev->dev_attrib.block_size) *
+ dev->prot_length;
+ bip->bip_iter.bi_sector = bio->bi_iter.bi_sector;
- bio->bi_bdev = ib_dev->ibd_bd;
- bio->bi_private = (void *) task;
- bio->bi_destructor = iblock_bio_destructor;
- bio->bi_end_io = &iblock_bio_done;
- bio->bi_sector = lba;
- atomic_inc(&ib_req->ib_bio_cnt);
+ pr_debug("IBLOCK BIP Size: %u Sector: %llu\n", bip->bip_iter.bi_size,
+ (unsigned long long)bip->bip_iter.bi_sector);
- DEBUG_IBLOCK("Set bio->bi_sector: %llu\n", bio->bi_sector);
- DEBUG_IBLOCK("Set ib_req->ib_bio_cnt: %d\n",
- atomic_read(&ib_req->ib_bio_cnt));
- return bio;
+ for_each_sg(cmd->t_prot_sg, sg, cmd->t_prot_nents, i) {
+
+ rc = bio_integrity_add_page(bio, sg_page(sg), sg->length,
+ sg->offset);
+ if (rc != sg->length) {
+ pr_err("bio_integrity_add_page() failed; %d\n", rc);
+ return -ENOMEM;
+ }
+
+ pr_debug("Added bio integrity page: %p length: %d offset; %d\n",
+ sg_page(sg), sg->length, sg->offset);
+ }
+
+ return 0;
}
-static int iblock_map_task_SG(struct se_task *task)
+static sense_reason_t
+iblock_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
+ enum dma_data_direction data_direction)
{
- struct se_cmd *cmd = task->task_se_cmd;
- struct se_device *dev = SE_DEV(cmd);
- struct iblock_dev *ib_dev = task->se_dev->dev_ptr;
- struct iblock_req *ib_req = IBLOCK_REQ(task);
- struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ struct se_device *dev = cmd->se_dev;
+ struct iblock_req *ibr;
+ struct bio *bio, *bio_start;
+ struct bio_list list;
struct scatterlist *sg;
- int ret = 0;
- u32 i, sg_num = task->task_sg_num;
+ u32 sg_num = sgl_nents;
sector_t block_lba;
+ unsigned bio_cnt;
+ int rw = 0;
+ int i;
+
+ if (data_direction == DMA_TO_DEVICE) {
+ struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+ struct request_queue *q = bdev_get_queue(ib_dev->ibd_bd);
+ /*
+ * Force writethrough using WRITE_FUA if a volatile write cache
+ * is not enabled, or if initiator set the Force Unit Access bit.
+ */
+ if (q->flush_flags & REQ_FUA) {
+ if (cmd->se_cmd_flags & SCF_FUA)
+ rw = WRITE_FUA;
+ else if (!(q->flush_flags & REQ_FLUSH))
+ rw = WRITE_FUA;
+ else
+ rw = WRITE;
+ } else {
+ rw = WRITE;
+ }
+ } else {
+ rw = READ;
+ }
+
/*
- * Do starting conversion up from non 512-byte blocksize with
- * struct se_task SCSI blocksize into Linux/Block 512 units for BIO.
+ * Convert the blocksize advertised to the initiator to the 512 byte
+ * units unconditionally used by the Linux block layer.
*/
- if (DEV_ATTRIB(dev)->block_size == 4096)
- block_lba = (task->task_lba << 3);
- else if (DEV_ATTRIB(dev)->block_size == 2048)
- block_lba = (task->task_lba << 2);
- else if (DEV_ATTRIB(dev)->block_size == 1024)
- block_lba = (task->task_lba << 1);
- else if (DEV_ATTRIB(dev)->block_size == 512)
- block_lba = task->task_lba;
+ if (dev->dev_attrib.block_size == 4096)
+ block_lba = (cmd->t_task_lba << 3);
+ else if (dev->dev_attrib.block_size == 2048)
+ block_lba = (cmd->t_task_lba << 2);
+ else if (dev->dev_attrib.block_size == 1024)
+ block_lba = (cmd->t_task_lba << 1);
+ else if (dev->dev_attrib.block_size == 512)
+ block_lba = cmd->t_task_lba;
else {
- printk(KERN_ERR "Unsupported SCSI -> BLOCK LBA conversion:"
- " %u\n", DEV_ATTRIB(dev)->block_size);
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ pr_err("Unsupported SCSI -> BLOCK LBA conversion:"
+ " %u\n", dev->dev_attrib.block_size);
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
- bio = iblock_get_bio(task, ib_req, ib_dev, &ret, block_lba, sg_num);
- if (!(bio))
- return ret;
+ ibr = kzalloc(sizeof(struct iblock_req), GFP_KERNEL);
+ if (!ibr)
+ goto fail;
+ cmd->priv = ibr;
- ib_req->ib_bio = bio;
- hbio = tbio = bio;
- /*
- * Use fs/bio.c:bio_add_pages() to setup the bio_vec maplist
- * from TCM struct se_mem -> task->task_sg -> struct scatterlist memory.
- */
- for_each_sg(task->task_sg, sg, task->task_sg_num, i) {
- DEBUG_IBLOCK("task: %p bio: %p Calling bio_add_page(): page:"
- " %p len: %u offset: %u\n", task, bio, sg_page(sg),
- sg->length, sg->offset);
-again:
- ret = bio_add_page(bio, sg_page(sg), sg->length, sg->offset);
- if (ret != sg->length) {
-
- DEBUG_IBLOCK("*** Set bio->bi_sector: %llu\n",
- bio->bi_sector);
- DEBUG_IBLOCK("** task->task_size: %u\n",
- task->task_size);
- DEBUG_IBLOCK("*** bio->bi_max_vecs: %u\n",
- bio->bi_max_vecs);
- DEBUG_IBLOCK("*** bio->bi_vcnt: %u\n",
- bio->bi_vcnt);
-
- bio = iblock_get_bio(task, ib_req, ib_dev, &ret,
- block_lba, sg_num);
- if (!(bio))
- goto fail;
-
- tbio = tbio->bi_next = bio;
- DEBUG_IBLOCK("-----------------> Added +1 bio: %p to"
- " list, Going to again\n", bio);
- goto again;
+ if (!sgl_nents) {
+ atomic_set(&ibr->pending, 1);
+ iblock_complete_cmd(cmd);
+ return 0;
+ }
+
+ bio = iblock_get_bio(cmd, block_lba, sgl_nents);
+ if (!bio)
+ goto fail_free_ibr;
+
+ bio_start = bio;
+ bio_list_init(&list);
+ bio_list_add(&list, bio);
+
+ atomic_set(&ibr->pending, 2);
+ bio_cnt = 1;
+
+ for_each_sg(sgl, sg, sgl_nents, i) {
+ /*
+ * XXX: if the length the device accepts is shorter than the
+ * length of the S/G list entry this will cause and
+ * endless loop. Better hope no driver uses huge pages.
+ */
+ while (bio_add_page(bio, sg_page(sg), sg->length, sg->offset)
+ != sg->length) {
+ if (bio_cnt >= IBLOCK_MAX_BIO_PER_TASK) {
+ iblock_submit_bios(&list, rw);
+ bio_cnt = 0;
+ }
+
+ bio = iblock_get_bio(cmd, block_lba, sg_num);
+ if (!bio)
+ goto fail_put_bios;
+
+ atomic_inc(&ibr->pending);
+ bio_list_add(&list, bio);
+ bio_cnt++;
}
+
/* Always in 512 byte units for Linux/Block */
block_lba += sg->length >> IBLOCK_LBA_SHIFT;
sg_num--;
- DEBUG_IBLOCK("task: %p bio-add_page() passed!, decremented"
- " sg_num to %u\n", task, sg_num);
- DEBUG_IBLOCK("task: %p bio_add_page() passed!, increased lba"
- " to %llu\n", task, block_lba);
- DEBUG_IBLOCK("task: %p bio_add_page() passed!, bio->bi_vcnt:"
- " %u\n", task, bio->bi_vcnt);
}
+ if (cmd->prot_type) {
+ int rc = iblock_alloc_bip(cmd, bio_start);
+ if (rc)
+ goto fail_put_bios;
+ }
+
+ iblock_submit_bios(&list, rw);
+ iblock_complete_cmd(cmd);
return 0;
-fail:
- while (hbio) {
- bio = hbio;
- hbio = hbio->bi_next;
- bio->bi_next = NULL;
+
+fail_put_bios:
+ while ((bio = bio_list_pop(&list)))
bio_put(bio);
- }
- return ret;
+fail_free_ibr:
+ kfree(ibr);
+fail:
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
-static unsigned char *iblock_get_cdb(struct se_task *task)
+static sector_t iblock_get_blocks(struct se_device *dev)
{
- return IBLOCK_REQ(task)->ib_scsi_cdb;
+ struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+ struct block_device *bd = ib_dev->ibd_bd;
+ struct request_queue *q = bdev_get_queue(bd);
+
+ return iblock_emulate_read_cap_with_block_size(dev, bd, q);
}
-static u32 iblock_get_device_rev(struct se_device *dev)
+static sector_t iblock_get_alignment_offset_lbas(struct se_device *dev)
{
- return SCSI_SPC_2; /* Returns SPC-3 in Initiator Data */
+ struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+ struct block_device *bd = ib_dev->ibd_bd;
+ int ret;
+
+ ret = bdev_alignment_offset(bd);
+ if (ret == -1)
+ return 0;
+
+ /* convert offset-bytes to offset-lbas */
+ return ret / bdev_logical_block_size(bd);
}
-static u32 iblock_get_device_type(struct se_device *dev)
+static unsigned int iblock_get_lbppbe(struct se_device *dev)
{
- return TYPE_DISK;
+ struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+ struct block_device *bd = ib_dev->ibd_bd;
+ int logs_per_phys = bdev_physical_block_size(bd) / bdev_logical_block_size(bd);
+
+ return ilog2(logs_per_phys);
}
-static sector_t iblock_get_blocks(struct se_device *dev)
+static unsigned int iblock_get_io_min(struct se_device *dev)
{
- struct iblock_dev *ibd = dev->dev_ptr;
- struct block_device *bd = ibd->ibd_bd;
- struct request_queue *q = bdev_get_queue(bd);
+ struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+ struct block_device *bd = ib_dev->ibd_bd;
- return iblock_emulate_read_cap_with_block_size(dev, bd, q);
+ return bdev_io_min(bd);
}
-static void iblock_bio_done(struct bio *bio, int err)
+static unsigned int iblock_get_io_opt(struct se_device *dev)
{
- struct se_task *task = bio->bi_private;
- struct iblock_req *ibr = IBLOCK_REQ(task);
- /*
- * Set -EIO if !BIO_UPTODATE and the passed is still err=0
- */
- if (!(test_bit(BIO_UPTODATE, &bio->bi_flags)) && !(err))
- err = -EIO;
+ struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+ struct block_device *bd = ib_dev->ibd_bd;
- if (err != 0) {
- printk(KERN_ERR "test_bit(BIO_UPTODATE) failed for bio: %p,"
- " err: %d\n", bio, err);
- /*
- * Bump the ib_bio_err_cnt and release bio.
- */
- atomic_inc(&ibr->ib_bio_err_cnt);
- smp_mb__after_atomic_inc();
- bio_put(bio);
- /*
- * Wait to complete the task until the last bio as completed.
- */
- if (!(atomic_dec_and_test(&ibr->ib_bio_cnt)))
- return;
+ return bdev_io_opt(bd);
+}
- ibr->ib_bio = NULL;
- transport_complete_task(task, 0);
- return;
- }
- DEBUG_IBLOCK("done[%p] bio: %p task_lba: %llu bio_lba: %llu err=%d\n",
- task, bio, task->task_lba, bio->bi_sector, err);
- /*
- * bio_put() will call iblock_bio_destructor() to release the bio back
- * to ibr->ib_bio_set.
- */
- bio_put(bio);
- /*
- * Wait to complete the task until the last bio as completed.
- */
- if (!(atomic_dec_and_test(&ibr->ib_bio_cnt)))
- return;
- /*
- * Return GOOD status for task if zero ib_bio_err_cnt exists.
- */
- ibr->ib_bio = NULL;
- transport_complete_task(task, (!atomic_read(&ibr->ib_bio_err_cnt)));
+static struct sbc_ops iblock_sbc_ops = {
+ .execute_rw = iblock_execute_rw,
+ .execute_sync_cache = iblock_execute_sync_cache,
+ .execute_write_same = iblock_execute_write_same,
+ .execute_write_same_unmap = iblock_execute_write_same_unmap,
+ .execute_unmap = iblock_execute_unmap,
+};
+
+static sense_reason_t
+iblock_parse_cdb(struct se_cmd *cmd)
+{
+ return sbc_parse_cdb(cmd, &iblock_sbc_ops);
+}
+
+static bool iblock_get_write_cache(struct se_device *dev)
+{
+ struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
+ struct block_device *bd = ib_dev->ibd_bd;
+ struct request_queue *q = bdev_get_queue(bd);
+
+ return q->flush_flags & REQ_FLUSH;
}
static struct se_subsystem_api iblock_template = {
.name = "iblock",
+ .inquiry_prod = "IBLOCK",
+ .inquiry_rev = IBLOCK_VERSION,
.owner = THIS_MODULE,
.transport_type = TRANSPORT_PLUGIN_VHBA_PDEV,
- .map_task_SG = iblock_map_task_SG,
.attach_hba = iblock_attach_hba,
.detach_hba = iblock_detach_hba,
- .allocate_virtdevice = iblock_allocate_virtdevice,
- .create_virtdevice = iblock_create_virtdevice,
+ .alloc_device = iblock_alloc_device,
+ .configure_device = iblock_configure_device,
.free_device = iblock_free_device,
- .dpo_emulated = iblock_emulated_dpo,
- .fua_write_emulated = iblock_emulated_fua_write,
- .fua_read_emulated = iblock_emulated_fua_read,
- .write_cache_emulated = iblock_emulated_write_cache,
- .alloc_task = iblock_alloc_task,
- .do_task = iblock_do_task,
- .do_discard = iblock_do_discard,
- .do_sync_cache = iblock_emulate_sync_cache,
- .free_task = iblock_free_task,
- .check_configfs_dev_params = iblock_check_configfs_dev_params,
+ .parse_cdb = iblock_parse_cdb,
.set_configfs_dev_params = iblock_set_configfs_dev_params,
.show_configfs_dev_params = iblock_show_configfs_dev_params,
- .get_cdb = iblock_get_cdb,
- .get_device_rev = iblock_get_device_rev,
- .get_device_type = iblock_get_device_type,
+ .get_device_type = sbc_get_device_type,
.get_blocks = iblock_get_blocks,
+ .get_alignment_offset_lbas = iblock_get_alignment_offset_lbas,
+ .get_lbppbe = iblock_get_lbppbe,
+ .get_io_min = iblock_get_io_min,
+ .get_io_opt = iblock_get_io_opt,
+ .get_write_cache = iblock_get_write_cache,
};
static int __init iblock_module_init(void)
@@ -797,7 +886,7 @@ static int __init iblock_module_init(void)
return transport_subsystem_register(&iblock_template);
}
-static void iblock_module_exit(void)
+static void __exit iblock_module_exit(void)
{
transport_subsystem_release(&iblock_template);
}
diff --git a/drivers/target/target_core_iblock.h b/drivers/target/target_core_iblock.h
index 64c1f4d69f7..01c2afd8150 100644
--- a/drivers/target/target_core_iblock.h
+++ b/drivers/target/target_core_iblock.h
@@ -3,38 +3,23 @@
#define IBLOCK_VERSION "4.0"
-#define IBLOCK_HBA_QUEUE_DEPTH 512
-#define IBLOCK_DEVICE_QUEUE_DEPTH 32
-#define IBLOCK_MAX_DEVICE_QUEUE_DEPTH 128
#define IBLOCK_MAX_CDBS 16
#define IBLOCK_LBA_SHIFT 9
struct iblock_req {
- struct se_task ib_task;
- unsigned char ib_scsi_cdb[TCM_MAX_COMMAND_SIZE];
- atomic_t ib_bio_cnt;
+ atomic_t pending;
atomic_t ib_bio_err_cnt;
- struct bio *ib_bio;
- struct iblock_dev *ib_dev;
} ____cacheline_aligned;
#define IBDF_HAS_UDEV_PATH 0x01
-#define IBDF_HAS_FORCE 0x02
struct iblock_dev {
+ struct se_device dev;
unsigned char ibd_udev_path[SE_UDEV_PATH_LEN];
- int ibd_force;
- int ibd_major;
- int ibd_minor;
- u32 ibd_depth;
u32 ibd_flags;
struct bio_set *ibd_bio_set;
struct block_device *ibd_bd;
- struct iblock_hba *ibd_host;
-} ____cacheline_aligned;
-
-struct iblock_hba {
- int iblock_host_id;
+ bool ibd_readonly;
} ____cacheline_aligned;
#endif /* TARGET_CORE_IBLOCK_H */
diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h
new file mode 100644
index 00000000000..de9cab708f4
--- /dev/null
+++ b/drivers/target/target_core_internal.h
@@ -0,0 +1,115 @@
+#ifndef TARGET_CORE_INTERNAL_H
+#define TARGET_CORE_INTERNAL_H
+
+/* target_core_alua.c */
+extern struct t10_alua_lu_gp *default_lu_gp;
+
+/* target_core_device.c */
+struct se_dev_entry *core_get_se_deve_from_rtpi(struct se_node_acl *, u16);
+int core_free_device_list_for_node(struct se_node_acl *,
+ struct se_portal_group *);
+void core_update_device_list_access(u32, u32, struct se_node_acl *);
+int core_enable_device_list_for_node(struct se_lun *, struct se_lun_acl *,
+ u32, u32, struct se_node_acl *, struct se_portal_group *);
+int core_disable_device_list_for_node(struct se_lun *, struct se_lun_acl *,
+ u32, u32, struct se_node_acl *, struct se_portal_group *);
+void core_clear_lun_from_tpg(struct se_lun *, struct se_portal_group *);
+int core_dev_export(struct se_device *, struct se_portal_group *,
+ struct se_lun *);
+void core_dev_unexport(struct se_device *, struct se_portal_group *,
+ struct se_lun *);
+int se_dev_set_task_timeout(struct se_device *, u32);
+int se_dev_set_max_unmap_lba_count(struct se_device *, u32);
+int se_dev_set_max_unmap_block_desc_count(struct se_device *, u32);
+int se_dev_set_unmap_granularity(struct se_device *, u32);
+int se_dev_set_unmap_granularity_alignment(struct se_device *, u32);
+int se_dev_set_max_write_same_len(struct se_device *, u32);
+int se_dev_set_emulate_model_alias(struct se_device *, int);
+int se_dev_set_emulate_dpo(struct se_device *, int);
+int se_dev_set_emulate_fua_write(struct se_device *, int);
+int se_dev_set_emulate_fua_read(struct se_device *, int);
+int se_dev_set_emulate_write_cache(struct se_device *, int);
+int se_dev_set_emulate_ua_intlck_ctrl(struct se_device *, int);
+int se_dev_set_emulate_tas(struct se_device *, int);
+int se_dev_set_emulate_tpu(struct se_device *, int);
+int se_dev_set_emulate_tpws(struct se_device *, int);
+int se_dev_set_emulate_caw(struct se_device *, int);
+int se_dev_set_emulate_3pc(struct se_device *, int);
+int se_dev_set_pi_prot_type(struct se_device *, int);
+int se_dev_set_pi_prot_format(struct se_device *, int);
+int se_dev_set_enforce_pr_isids(struct se_device *, int);
+int se_dev_set_is_nonrot(struct se_device *, int);
+int se_dev_set_emulate_rest_reord(struct se_device *dev, int);
+int se_dev_set_queue_depth(struct se_device *, u32);
+int se_dev_set_max_sectors(struct se_device *, u32);
+int se_dev_set_fabric_max_sectors(struct se_device *, u32);
+int se_dev_set_optimal_sectors(struct se_device *, u32);
+int se_dev_set_block_size(struct se_device *, u32);
+struct se_lun *core_dev_add_lun(struct se_portal_group *, struct se_device *, u32);
+int core_dev_del_lun(struct se_portal_group *, u32);
+struct se_lun *core_get_lun_from_tpg(struct se_portal_group *, u32);
+struct se_lun_acl *core_dev_init_initiator_node_lun_acl(struct se_portal_group *,
+ struct se_node_acl *, u32, int *);
+int core_dev_add_initiator_node_lun_acl(struct se_portal_group *,
+ struct se_lun_acl *, u32, u32);
+int core_dev_del_initiator_node_lun_acl(struct se_portal_group *,
+ struct se_lun *, struct se_lun_acl *);
+void core_dev_free_initiator_node_lun_acl(struct se_portal_group *,
+ struct se_lun_acl *lacl);
+int core_dev_setup_virtual_lun0(void);
+void core_dev_release_virtual_lun0(void);
+struct se_device *target_alloc_device(struct se_hba *hba, const char *name);
+int target_configure_device(struct se_device *dev);
+void target_free_device(struct se_device *);
+
+/* target_core_hba.c */
+struct se_hba *core_alloc_hba(const char *, u32, u32);
+int core_delete_hba(struct se_hba *);
+
+/* target_core_tmr.c */
+void core_tmr_abort_task(struct se_device *, struct se_tmr_req *,
+ struct se_session *);
+int core_tmr_lun_reset(struct se_device *, struct se_tmr_req *,
+ struct list_head *, struct se_cmd *);
+
+/* target_core_tpg.c */
+extern struct se_device *g_lun0_dev;
+
+struct se_node_acl *__core_tpg_get_initiator_node_acl(struct se_portal_group *tpg,
+ const char *);
+void core_tpg_add_node_to_devs(struct se_node_acl *, struct se_portal_group *);
+void core_tpg_wait_for_nacl_pr_ref(struct se_node_acl *);
+struct se_lun *core_tpg_alloc_lun(struct se_portal_group *, u32);
+int core_tpg_add_lun(struct se_portal_group *, struct se_lun *,
+ u32, struct se_device *);
+struct se_lun *core_tpg_pre_dellun(struct se_portal_group *, u32 unpacked_lun);
+int core_tpg_post_dellun(struct se_portal_group *, struct se_lun *);
+
+/* target_core_transport.c */
+extern struct kmem_cache *se_tmr_req_cache;
+
+int init_se_kmem_caches(void);
+void release_se_kmem_caches(void);
+u32 scsi_get_new_index(scsi_index_t);
+void transport_subsystem_check_init(void);
+void transport_cmd_finish_abort(struct se_cmd *, int);
+unsigned char *transport_dump_cmd_direction(struct se_cmd *);
+void transport_dump_dev_state(struct se_device *, char *, int *);
+void transport_dump_dev_info(struct se_device *, struct se_lun *,
+ unsigned long long, char *, int *);
+void transport_dump_vpd_proto_id(struct t10_vpd *, unsigned char *, int);
+int transport_dump_vpd_assoc(struct t10_vpd *, unsigned char *, int);
+int transport_dump_vpd_ident_type(struct t10_vpd *, unsigned char *, int);
+int transport_dump_vpd_ident(struct t10_vpd *, unsigned char *, int);
+bool target_stop_cmd(struct se_cmd *cmd, unsigned long *flags);
+int transport_clear_lun_ref(struct se_lun *);
+void transport_send_task_abort(struct se_cmd *);
+sense_reason_t target_cmd_size_check(struct se_cmd *cmd, unsigned int size);
+void target_qf_do_work(struct work_struct *work);
+
+/* target_core_stat.c */
+void target_stat_setup_dev_default_groups(struct se_device *);
+void target_stat_setup_port_default_groups(struct se_lun *);
+void target_stat_setup_mappedlun_default_groups(struct se_lun_acl *);
+
+#endif /* TARGET_CORE_INTERNAL_H */
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 2521f75362c..df357862286 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -4,8 +4,7 @@
* This file contains SPC-3 compliant persistent reservations and
* legacy SPC-2 reservations with compatible reservation handling (CRH=1)
*
- * Copyright (c) 2009, 2010 Rising Tide Systems
- * Copyright (c) 2009, 2010 Linux-iSCSI.org
+ * (c) Copyright 2009-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -25,23 +24,20 @@
*
******************************************************************************/
-#include <linux/version.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/list.h>
+#include <linux/file.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <asm/unaligned.h>
#include <target/target_core_base.h>
-#include <target/target_core_device.h>
-#include <target/target_core_tmr.h>
-#include <target/target_core_tpg.h>
-#include <target/target_core_transport.h>
-#include <target/target_core_fabric_ops.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
#include <target/target_core_configfs.h>
-#include "target_core_hba.h"
+#include "target_core_internal.h"
#include "target_core_pr.h"
#include "target_core_ua.h"
@@ -57,146 +53,56 @@ struct pr_transport_id_holder {
struct list_head dest_list;
};
-int core_pr_dump_initiator_port(
+void core_pr_dump_initiator_port(
struct t10_pr_registration *pr_reg,
char *buf,
u32 size)
{
- if (!(pr_reg->isid_present_at_reg))
- return 0;
+ if (!pr_reg->isid_present_at_reg)
+ buf[0] = '\0';
- snprintf(buf, size, ",i,0x%s", &pr_reg->pr_reg_isid[0]);
- return 1;
+ snprintf(buf, size, ",i,0x%s", pr_reg->pr_reg_isid);
}
+enum register_type {
+ REGISTER,
+ REGISTER_AND_IGNORE_EXISTING_KEY,
+ REGISTER_AND_MOVE,
+};
+
+enum preempt_type {
+ PREEMPT,
+ PREEMPT_AND_ABORT,
+};
+
static void __core_scsi3_complete_pro_release(struct se_device *, struct se_node_acl *,
struct t10_pr_registration *, int);
-static int core_scsi2_reservation_seq_non_holder(
- struct se_cmd *cmd,
- unsigned char *cdb,
- u32 pr_reg_type)
+static sense_reason_t
+target_scsi2_reservation_check(struct se_cmd *cmd)
{
- switch (cdb[0]) {
+ struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
+
+ switch (cmd->t_task_cdb[0]) {
case INQUIRY:
case RELEASE:
case RELEASE_10:
return 0;
default:
- return 1;
- }
-
- return 1;
-}
-
-static int core_scsi2_reservation_check(struct se_cmd *cmd, u32 *pr_reg_type)
-{
- struct se_device *dev = cmd->se_dev;
- struct se_session *sess = cmd->se_sess;
- int ret;
-
- if (!(sess))
- return 0;
-
- spin_lock(&dev->dev_reservation_lock);
- if (!dev->dev_reserved_node_acl || !sess) {
- spin_unlock(&dev->dev_reservation_lock);
- return 0;
- }
- if (dev->dev_reserved_node_acl != sess->se_node_acl) {
- spin_unlock(&dev->dev_reservation_lock);
- return -1;
- }
- if (!(dev->dev_flags & DF_SPC2_RESERVATIONS_WITH_ISID)) {
- spin_unlock(&dev->dev_reservation_lock);
- return 0;
+ break;
}
- ret = (dev->dev_res_bin_isid == sess->sess_bin_isid) ? 0 : -1;
- spin_unlock(&dev->dev_reservation_lock);
-
- return ret;
-}
-
-static int core_scsi2_reservation_release(struct se_cmd *cmd)
-{
- struct se_device *dev = cmd->se_dev;
- struct se_session *sess = cmd->se_sess;
- struct se_portal_group *tpg = sess->se_tpg;
- if (!(sess) || !(tpg))
+ if (!dev->dev_reserved_node_acl || !sess)
return 0;
- spin_lock(&dev->dev_reservation_lock);
- if (!dev->dev_reserved_node_acl || !sess) {
- spin_unlock(&dev->dev_reservation_lock);
- return 0;
- }
+ if (dev->dev_reserved_node_acl != sess->se_node_acl)
+ return TCM_RESERVATION_CONFLICT;
- if (dev->dev_reserved_node_acl != sess->se_node_acl) {
- spin_unlock(&dev->dev_reservation_lock);
- return 0;
+ if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS_WITH_ISID) {
+ if (dev->dev_res_bin_isid != sess->sess_bin_isid)
+ return TCM_RESERVATION_CONFLICT;
}
- dev->dev_reserved_node_acl = NULL;
- dev->dev_flags &= ~DF_SPC2_RESERVATIONS;
- if (dev->dev_flags & DF_SPC2_RESERVATIONS_WITH_ISID) {
- dev->dev_res_bin_isid = 0;
- dev->dev_flags &= ~DF_SPC2_RESERVATIONS_WITH_ISID;
- }
- printk(KERN_INFO "SCSI-2 Released reservation for %s LUN: %u ->"
- " MAPPED LUN: %u for %s\n", TPG_TFO(tpg)->get_fabric_name(),
- SE_LUN(cmd)->unpacked_lun, cmd->se_deve->mapped_lun,
- sess->se_node_acl->initiatorname);
- spin_unlock(&dev->dev_reservation_lock);
-
- return 0;
-}
-
-static int core_scsi2_reservation_reserve(struct se_cmd *cmd)
-{
- struct se_device *dev = cmd->se_dev;
- struct se_session *sess = cmd->se_sess;
- struct se_portal_group *tpg = sess->se_tpg;
-
- if ((T_TASK(cmd)->t_task_cdb[1] & 0x01) &&
- (T_TASK(cmd)->t_task_cdb[1] & 0x02)) {
- printk(KERN_ERR "LongIO and Obselete Bits set, returning"
- " ILLEGAL_REQUEST\n");
- return PYX_TRANSPORT_ILLEGAL_REQUEST;
- }
- /*
- * This is currently the case for target_core_mod passthrough struct se_cmd
- * ops
- */
- if (!(sess) || !(tpg))
- return 0;
-
- spin_lock(&dev->dev_reservation_lock);
- if (dev->dev_reserved_node_acl &&
- (dev->dev_reserved_node_acl != sess->se_node_acl)) {
- printk(KERN_ERR "SCSI-2 RESERVATION CONFLIFT for %s fabric\n",
- TPG_TFO(tpg)->get_fabric_name());
- printk(KERN_ERR "Original reserver LUN: %u %s\n",
- SE_LUN(cmd)->unpacked_lun,
- dev->dev_reserved_node_acl->initiatorname);
- printk(KERN_ERR "Current attempt - LUN: %u -> MAPPED LUN: %u"
- " from %s \n", SE_LUN(cmd)->unpacked_lun,
- cmd->se_deve->mapped_lun,
- sess->se_node_acl->initiatorname);
- spin_unlock(&dev->dev_reservation_lock);
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
- }
-
- dev->dev_reserved_node_acl = sess->se_node_acl;
- dev->dev_flags |= DF_SPC2_RESERVATIONS;
- if (sess->sess_bin_isid != 0) {
- dev->dev_res_bin_isid = sess->sess_bin_isid;
- dev->dev_flags |= DF_SPC2_RESERVATIONS_WITH_ISID;
- }
- printk(KERN_INFO "SCSI-2 Reserved %s LUN: %u -> MAPPED LUN: %u"
- " for %s\n", TPG_TFO(tpg)->get_fabric_name(),
- SE_LUN(cmd)->unpacked_lun, cmd->se_deve->mapped_lun,
- sess->se_node_acl->initiatorname);
- spin_unlock(&dev->dev_reservation_lock);
return 0;
}
@@ -205,27 +111,14 @@ static struct t10_pr_registration *core_scsi3_locate_pr_reg(struct se_device *,
struct se_node_acl *, struct se_session *);
static void core_scsi3_put_pr_reg(struct t10_pr_registration *);
-/*
- * Setup in target_core_transport.c:transport_generic_cmd_sequencer()
- * and called via struct se_cmd->transport_emulate_cdb() in TCM processing
- * thread context.
- */
-int core_scsi2_emulate_crh(struct se_cmd *cmd)
+static int target_check_scsi2_reservation_conflict(struct se_cmd *cmd)
{
struct se_session *se_sess = cmd->se_sess;
- struct se_subsystem_dev *su_dev = cmd->se_dev->se_sub_dev;
+ struct se_device *dev = cmd->se_dev;
struct t10_pr_registration *pr_reg;
- struct t10_reservation_template *pr_tmpl = &su_dev->t10_reservation;
- unsigned char *cdb = &T_TASK(cmd)->t_task_cdb[0];
- int crh = (T10_RES(su_dev)->res_type == SPC3_PERSISTENT_RESERVATIONS);
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
int conflict = 0;
- if (!(se_sess))
- return 0;
-
- if (!(crh))
- goto after_crh;
-
pr_reg = core_scsi3_locate_pr_reg(cmd->se_dev, se_sess->se_node_acl,
se_sess);
if (pr_reg) {
@@ -252,14 +145,14 @@ int core_scsi2_emulate_crh(struct se_cmd *cmd)
*/
if (pr_reg->pr_res_holder) {
core_scsi3_put_pr_reg(pr_reg);
- return 0;
+ return 1;
}
if ((pr_reg->pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_REGONLY) ||
(pr_reg->pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_REGONLY) ||
(pr_reg->pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG) ||
(pr_reg->pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG)) {
core_scsi3_put_pr_reg(pr_reg);
- return 0;
+ return 1;
}
core_scsi3_put_pr_reg(pr_reg);
conflict = 1;
@@ -280,21 +173,125 @@ int core_scsi2_emulate_crh(struct se_cmd *cmd)
}
if (conflict) {
- printk(KERN_ERR "Received legacy SPC-2 RESERVE/RELEASE"
+ pr_err("Received legacy SPC-2 RESERVE/RELEASE"
" while active SPC-3 registrations exist,"
" returning RESERVATION_CONFLICT\n");
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ return -EBUSY;
}
-after_crh:
- if ((cdb[0] == RESERVE) || (cdb[0] == RESERVE_10))
- return core_scsi2_reservation_reserve(cmd);
- else if ((cdb[0] == RELEASE) || (cdb[0] == RELEASE_10))
- return core_scsi2_reservation_release(cmd);
- else
- return PYX_TRANSPORT_INVALID_CDB_FIELD;
+ return 0;
+}
+
+sense_reason_t
+target_scsi2_reservation_release(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
+ struct se_portal_group *tpg;
+ int rc;
+
+ if (!sess || !sess->se_tpg)
+ goto out;
+ rc = target_check_scsi2_reservation_conflict(cmd);
+ if (rc == 1)
+ goto out;
+ if (rc < 0)
+ return TCM_RESERVATION_CONFLICT;
+
+ spin_lock(&dev->dev_reservation_lock);
+ if (!dev->dev_reserved_node_acl || !sess)
+ goto out_unlock;
+
+ if (dev->dev_reserved_node_acl != sess->se_node_acl)
+ goto out_unlock;
+
+ if (dev->dev_res_bin_isid != sess->sess_bin_isid)
+ goto out_unlock;
+
+ dev->dev_reserved_node_acl = NULL;
+ dev->dev_reservation_flags &= ~DRF_SPC2_RESERVATIONS;
+ if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS_WITH_ISID) {
+ dev->dev_res_bin_isid = 0;
+ dev->dev_reservation_flags &= ~DRF_SPC2_RESERVATIONS_WITH_ISID;
+ }
+ tpg = sess->se_tpg;
+ pr_debug("SCSI-2 Released reservation for %s LUN: %u ->"
+ " MAPPED LUN: %u for %s\n", tpg->se_tpg_tfo->get_fabric_name(),
+ cmd->se_lun->unpacked_lun, cmd->se_deve->mapped_lun,
+ sess->se_node_acl->initiatorname);
+
+out_unlock:
+ spin_unlock(&dev->dev_reservation_lock);
+out:
+ target_complete_cmd(cmd, GOOD);
+ return 0;
+}
+
+sense_reason_t
+target_scsi2_reservation_reserve(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
+ struct se_portal_group *tpg;
+ sense_reason_t ret = 0;
+ int rc;
+
+ if ((cmd->t_task_cdb[1] & 0x01) &&
+ (cmd->t_task_cdb[1] & 0x02)) {
+ pr_err("LongIO and Obselete Bits set, returning"
+ " ILLEGAL_REQUEST\n");
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+ }
+ /*
+ * This is currently the case for target_core_mod passthrough struct se_cmd
+ * ops
+ */
+ if (!sess || !sess->se_tpg)
+ goto out;
+ rc = target_check_scsi2_reservation_conflict(cmd);
+ if (rc == 1)
+ goto out;
+
+ if (rc < 0)
+ return TCM_RESERVATION_CONFLICT;
+
+ tpg = sess->se_tpg;
+ spin_lock(&dev->dev_reservation_lock);
+ if (dev->dev_reserved_node_acl &&
+ (dev->dev_reserved_node_acl != sess->se_node_acl)) {
+ pr_err("SCSI-2 RESERVATION CONFLIFT for %s fabric\n",
+ tpg->se_tpg_tfo->get_fabric_name());
+ pr_err("Original reserver LUN: %u %s\n",
+ cmd->se_lun->unpacked_lun,
+ dev->dev_reserved_node_acl->initiatorname);
+ pr_err("Current attempt - LUN: %u -> MAPPED LUN: %u"
+ " from %s \n", cmd->se_lun->unpacked_lun,
+ cmd->se_deve->mapped_lun,
+ sess->se_node_acl->initiatorname);
+ ret = TCM_RESERVATION_CONFLICT;
+ goto out_unlock;
+ }
+
+ dev->dev_reserved_node_acl = sess->se_node_acl;
+ dev->dev_reservation_flags |= DRF_SPC2_RESERVATIONS;
+ if (sess->sess_bin_isid != 0) {
+ dev->dev_res_bin_isid = sess->sess_bin_isid;
+ dev->dev_reservation_flags |= DRF_SPC2_RESERVATIONS_WITH_ISID;
+ }
+ pr_debug("SCSI-2 Reserved %s LUN: %u -> MAPPED LUN: %u"
+ " for %s\n", tpg->se_tpg_tfo->get_fabric_name(),
+ cmd->se_lun->unpacked_lun, cmd->se_deve->mapped_lun,
+ sess->se_node_acl->initiatorname);
+
+out_unlock:
+ spin_unlock(&dev->dev_reservation_lock);
+out:
+ if (!ret)
+ target_complete_cmd(cmd, GOOD);
+ return ret;
}
+
/*
* Begin SPC-3/SPC-4 Persistent Reservations emulation support
*
@@ -303,28 +300,22 @@ after_crh:
*/
static int core_scsi3_pr_seq_non_holder(
struct se_cmd *cmd,
- unsigned char *cdb,
u32 pr_reg_type)
{
+ unsigned char *cdb = cmd->t_task_cdb;
struct se_dev_entry *se_deve;
- struct se_session *se_sess = SE_SESS(cmd);
+ struct se_session *se_sess = cmd->se_sess;
int other_cdb = 0, ignore_reg;
int registered_nexus = 0, ret = 1; /* Conflict by default */
int all_reg = 0, reg_only = 0; /* ALL_REG, REG_ONLY */
int we = 0; /* Write Exclusive */
int legacy = 0; /* Act like a legacy device and return
* RESERVATION CONFLICT on some CDBs */
- /*
- * A legacy SPC-2 reservation is being held.
- */
- if (cmd->se_dev->dev_flags & DF_SPC2_RESERVATIONS)
- return core_scsi2_reservation_seq_non_holder(cmd,
- cdb, pr_reg_type);
- se_deve = &se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
+ se_deve = se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
/*
* Determine if the registration should be ignored due to
- * non-matching ISIDs in core_scsi3_pr_reservation_check().
+ * non-matching ISIDs in target_scsi3_pr_reservation_check().
*/
ignore_reg = (pr_reg_type & 0x80000000);
if (ignore_reg)
@@ -362,7 +353,7 @@ static int core_scsi3_pr_seq_non_holder(
registered_nexus = 1;
break;
default:
- return -1;
+ return -EINVAL;
}
/*
* Referenced from spc4r17 table 45 for *NON* PR holder access
@@ -412,19 +403,19 @@ static int core_scsi3_pr_seq_non_holder(
ret = (registered_nexus) ? 0 : 1;
break;
default:
- printk(KERN_ERR "Unknown PERSISTENT_RESERVE_OUT service"
+ pr_err("Unknown PERSISTENT_RESERVE_OUT service"
" action: 0x%02x\n", cdb[1] & 0x1f);
- return -1;
+ return -EINVAL;
}
break;
case RELEASE:
case RELEASE_10:
- /* Handled by CRH=1 in core_scsi2_emulate_crh() */
+ /* Handled by CRH=1 in target_scsi2_reservation_release() */
ret = 0;
break;
case RESERVE:
case RESERVE_10:
- /* Handled by CRH=1 in core_scsi2_emulate_crh() */
+ /* Handled by CRH=1 in target_scsi2_reservation_reserve() */
ret = 0;
break;
case TEST_UNIT_READY:
@@ -459,9 +450,9 @@ static int core_scsi3_pr_seq_non_holder(
ret = 0; /* Allowed */
break;
default:
- printk(KERN_ERR "Unknown MI Service Action: 0x%02x\n",
+ pr_err("Unknown MI Service Action: 0x%02x\n",
(cdb[1] & 0x1f));
- return -1;
+ return -EINVAL;
}
break;
case ACCESS_CONTROL_IN:
@@ -471,6 +462,7 @@ static int core_scsi3_pr_seq_non_holder(
case READ_MEDIA_SERIAL_NUMBER:
case REPORT_LUNS:
case REQUEST_SENSE:
+ case PERSISTENT_RESERVE_IN:
ret = 0; /*/ Allowed CDBs */
break;
default:
@@ -478,27 +470,26 @@ static int core_scsi3_pr_seq_non_holder(
break;
}
/*
- * Case where the CDB is explictly allowed in the above switch
+ * Case where the CDB is explicitly allowed in the above switch
* statement.
*/
- if (!(ret) && !(other_cdb)) {
-#if 0
- printk(KERN_INFO "Allowing explict CDB: 0x%02x for %s"
+ if (!ret && !other_cdb) {
+ pr_debug("Allowing explicit CDB: 0x%02x for %s"
" reservation holder\n", cdb[0],
core_scsi3_pr_dump_type(pr_reg_type));
-#endif
+
return ret;
}
/*
* Check if write exclusive initiator ports *NOT* holding the
* WRITE_EXCLUSIVE_* reservation.
*/
- if ((we) && !(registered_nexus)) {
+ if (we && !registered_nexus) {
if (cmd->data_direction == DMA_TO_DEVICE) {
/*
* Conflict for write exclusive
*/
- printk(KERN_INFO "%s Conflict for unregistered nexus"
+ pr_debug("%s Conflict for unregistered nexus"
" %s CDB: 0x%02x to %s reservation\n",
transport_dump_cmd_direction(cmd),
se_sess->se_node_acl->initiatorname, cdb[0],
@@ -514,14 +505,14 @@ static int core_scsi3_pr_seq_non_holder(
* as we expect registered non-reservation holding
* nexuses to issue CDBs.
*/
-#if 0
- if (!(registered_nexus)) {
- printk(KERN_INFO "Allowing implict CDB: 0x%02x"
+
+ if (!registered_nexus) {
+ pr_debug("Allowing implicit CDB: 0x%02x"
" for %s reservation on unregistered"
" nexus\n", cdb[0],
core_scsi3_pr_dump_type(pr_reg_type));
}
-#endif
+
return 0;
}
} else if ((reg_only) || (all_reg)) {
@@ -530,15 +521,15 @@ static int core_scsi3_pr_seq_non_holder(
* For PR_*_REG_ONLY and PR_*_ALL_REG reservations,
* allow commands from registered nexuses.
*/
-#if 0
- printk(KERN_INFO "Allowing implict CDB: 0x%02x for %s"
+
+ pr_debug("Allowing implicit CDB: 0x%02x for %s"
" reservation\n", cdb[0],
core_scsi3_pr_dump_type(pr_reg_type));
-#endif
+
return 0;
}
}
- printk(KERN_INFO "%s Conflict for %sregistered nexus %s CDB: 0x%2x"
+ pr_debug("%s Conflict for %sregistered nexus %s CDB: 0x%2x"
" for %s reservation\n", transport_dump_cmd_direction(cmd),
(registered_nexus) ? "" : "un",
se_sess->se_node_acl->initiatorname, cdb[0],
@@ -547,10 +538,41 @@ static int core_scsi3_pr_seq_non_holder(
return 1; /* Conflict by default */
}
+static sense_reason_t
+target_scsi3_pr_reservation_check(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
+ u32 pr_reg_type;
+
+ if (!dev->dev_pr_res_holder)
+ return 0;
+
+ pr_reg_type = dev->dev_pr_res_holder->pr_res_type;
+ cmd->pr_res_key = dev->dev_pr_res_holder->pr_res_key;
+ if (dev->dev_pr_res_holder->pr_reg_nacl != sess->se_node_acl)
+ goto check_nonholder;
+
+ if (dev->dev_pr_res_holder->isid_present_at_reg) {
+ if (dev->dev_pr_res_holder->pr_reg_bin_isid !=
+ sess->sess_bin_isid) {
+ pr_reg_type |= 0x80000000;
+ goto check_nonholder;
+ }
+ }
+
+ return 0;
+
+check_nonholder:
+ if (core_scsi3_pr_seq_non_holder(cmd, pr_reg_type))
+ return TCM_RESERVATION_CONFLICT;
+ return 0;
+}
+
static u32 core_scsi3_pr_generation(struct se_device *dev)
{
- struct se_subsystem_dev *su_dev = SU_DEV(dev);
u32 prg;
+
/*
* PRGeneration field shall contain the value of a 32-bit wrapping
* counter mainted by the device server.
@@ -561,56 +583,12 @@ static u32 core_scsi3_pr_generation(struct se_device *dev)
* See spc4r17 section 6.3.12 READ_KEYS service action
*/
spin_lock(&dev->dev_reservation_lock);
- prg = T10_RES(su_dev)->pr_generation++;
+ prg = dev->t10_pr.pr_generation++;
spin_unlock(&dev->dev_reservation_lock);
return prg;
}
-static int core_scsi3_pr_reservation_check(
- struct se_cmd *cmd,
- u32 *pr_reg_type)
-{
- struct se_device *dev = cmd->se_dev;
- struct se_session *sess = cmd->se_sess;
- int ret;
-
- if (!(sess))
- return 0;
- /*
- * A legacy SPC-2 reservation is being held.
- */
- if (dev->dev_flags & DF_SPC2_RESERVATIONS)
- return core_scsi2_reservation_check(cmd, pr_reg_type);
-
- spin_lock(&dev->dev_reservation_lock);
- if (!(dev->dev_pr_res_holder)) {
- spin_unlock(&dev->dev_reservation_lock);
- return 0;
- }
- *pr_reg_type = dev->dev_pr_res_holder->pr_res_type;
- cmd->pr_res_key = dev->dev_pr_res_holder->pr_res_key;
- if (dev->dev_pr_res_holder->pr_reg_nacl != sess->se_node_acl) {
- spin_unlock(&dev->dev_reservation_lock);
- return -1;
- }
- if (!(dev->dev_pr_res_holder->isid_present_at_reg)) {
- spin_unlock(&dev->dev_reservation_lock);
- return 0;
- }
- ret = (dev->dev_pr_res_holder->pr_reg_bin_isid ==
- sess->sess_bin_isid) ? 0 : -1;
- /*
- * Use bit in *pr_reg_type to notify ISID mismatch in
- * core_scsi3_pr_seq_non_holder().
- */
- if (ret != 0)
- *pr_reg_type |= 0x80000000;
- spin_unlock(&dev->dev_reservation_lock);
-
- return ret;
-}
-
static struct t10_pr_registration *__core_scsi3_do_alloc_registration(
struct se_device *dev,
struct se_node_acl *nacl,
@@ -620,20 +598,11 @@ static struct t10_pr_registration *__core_scsi3_do_alloc_registration(
int all_tg_pt,
int aptpl)
{
- struct se_subsystem_dev *su_dev = SU_DEV(dev);
struct t10_pr_registration *pr_reg;
pr_reg = kmem_cache_zalloc(t10_pr_reg_cache, GFP_ATOMIC);
- if (!(pr_reg)) {
- printk(KERN_ERR "Unable to allocate struct t10_pr_registration\n");
- return NULL;
- }
-
- pr_reg->pr_aptpl_buf = kzalloc(T10_RES(su_dev)->pr_aptpl_buf_len,
- GFP_ATOMIC);
- if (!(pr_reg->pr_aptpl_buf)) {
- printk(KERN_ERR "Unable to allocate pr_reg->pr_aptpl_buf\n");
- kmem_cache_free(t10_pr_reg_cache, pr_reg);
+ if (!pr_reg) {
+ pr_err("Unable to allocate struct t10_pr_registration\n");
return NULL;
}
@@ -692,12 +661,12 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration(
*/
pr_reg = __core_scsi3_do_alloc_registration(dev, nacl, deve, isid,
sa_res_key, all_tg_pt, aptpl);
- if (!(pr_reg))
+ if (!pr_reg)
return NULL;
/*
* Return pointer to pr_reg for ALL_TG_PT=0
*/
- if (!(all_tg_pt))
+ if (!all_tg_pt)
return pr_reg;
/*
* Create list of matching SCSI Initiator Port registrations
@@ -706,7 +675,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration(
spin_lock(&dev->se_port_lock);
list_for_each_entry_safe(port, port_tmp, &dev->dev_sep_list, sep_list) {
atomic_inc(&port->sep_tg_pt_ref_cnt);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
spin_unlock(&dev->se_port_lock);
spin_lock_bh(&port->sep_alua_lock);
@@ -714,10 +683,10 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration(
alua_port_list) {
/*
* This pointer will be NULL for demo mode MappedLUNs
- * that have not been make explict via a ConfigFS
+ * that have not been make explicit via a ConfigFS
* MappedLUN group for the SCSI Initiator Node ACL.
*/
- if (!(deve_tmp->se_lun_acl))
+ if (!deve_tmp->se_lun_acl)
continue;
nacl_tmp = deve_tmp->se_lun_acl->se_lun_nacl;
@@ -741,7 +710,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration(
continue;
atomic_inc(&deve_tmp->pr_ref_count);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
spin_unlock_bh(&port->sep_alua_lock);
/*
* Grab a configfs group dependency that is released
@@ -751,12 +720,12 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration(
*/
ret = core_scsi3_lunacl_depend_item(deve_tmp);
if (ret < 0) {
- printk(KERN_ERR "core_scsi3_lunacl_depend"
+ pr_err("core_scsi3_lunacl_depend"
"_item() failed\n");
atomic_dec(&port->sep_tg_pt_ref_cnt);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
atomic_dec(&deve_tmp->pr_ref_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
goto out;
}
/*
@@ -769,11 +738,11 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration(
pr_reg_atp = __core_scsi3_do_alloc_registration(dev,
nacl_tmp, deve_tmp, NULL,
sa_res_key, all_tg_pt, aptpl);
- if (!(pr_reg_atp)) {
+ if (!pr_reg_atp) {
atomic_dec(&port->sep_tg_pt_ref_cnt);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
atomic_dec(&deve_tmp->pr_ref_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
core_scsi3_lunacl_undepend_item(deve_tmp);
goto out;
}
@@ -786,7 +755,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration(
spin_lock(&dev->se_port_lock);
atomic_dec(&port->sep_tg_pt_ref_cnt);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
}
spin_unlock(&dev->se_port_lock);
@@ -803,7 +772,7 @@ out:
}
int core_scsi3_alloc_aptpl_registration(
- struct t10_reservation_template *pr_tmpl,
+ struct t10_reservation *pr_tmpl,
u64 sa_res_key,
unsigned char *i_port,
unsigned char *isid,
@@ -817,17 +786,16 @@ int core_scsi3_alloc_aptpl_registration(
{
struct t10_pr_registration *pr_reg;
- if (!(i_port) || !(t_port) || !(sa_res_key)) {
- printk(KERN_ERR "Illegal parameters for APTPL registration\n");
- return -1;
+ if (!i_port || !t_port || !sa_res_key) {
+ pr_err("Illegal parameters for APTPL registration\n");
+ return -EINVAL;
}
pr_reg = kmem_cache_zalloc(t10_pr_reg_cache, GFP_KERNEL);
- if (!(pr_reg)) {
- printk(KERN_ERR "Unable to allocate struct t10_pr_registration\n");
- return -1;
+ if (!pr_reg) {
+ pr_err("Unable to allocate struct t10_pr_registration\n");
+ return -ENOMEM;
}
- pr_reg->pr_aptpl_buf = kzalloc(pr_tmpl->pr_aptpl_buf_len, GFP_KERNEL);
INIT_LIST_HEAD(&pr_reg->pr_reg_list);
INIT_LIST_HEAD(&pr_reg->pr_reg_abort_list);
@@ -869,7 +837,7 @@ int core_scsi3_alloc_aptpl_registration(
pr_reg->pr_res_holder = res_holder;
list_add_tail(&pr_reg->pr_reg_aptpl_list, &pr_tmpl->aptpl_reg_list);
- printk(KERN_INFO "SPC-3 PR APTPL Successfully added registration%s from"
+ pr_debug("SPC-3 PR APTPL Successfully added registration%s from"
" metadata\n", (res_holder) ? "+reservation" : "");
return 0;
}
@@ -881,28 +849,26 @@ static void core_scsi3_aptpl_reserve(
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);
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
spin_lock(&dev->dev_reservation_lock);
dev->dev_pr_res_holder = pr_reg;
spin_unlock(&dev->dev_reservation_lock);
- printk(KERN_INFO "SPC-3 PR [%s] Service Action: APTPL RESERVE created"
+ pr_debug("SPC-3 PR [%s] Service Action: APTPL RESERVE created"
" new reservation holder TYPE: %s ALL_TG_PT: %d\n",
- TPG_TFO(tpg)->get_fabric_name(),
+ tpg->se_tpg_tfo->get_fabric_name(),
core_scsi3_pr_dump_type(pr_reg->pr_res_type),
(pr_reg->pr_reg_all_tg_pt) ? 1 : 0);
- printk(KERN_INFO "SPC-3 PR [%s] RESERVE Node: %s%s\n",
- TPG_TFO(tpg)->get_fabric_name(), node_acl->initiatorname,
- (prf_isid) ? &i_buf[0] : "");
+ pr_debug("SPC-3 PR [%s] RESERVE Node: %s%s\n",
+ tpg->se_tpg_tfo->get_fabric_name(), node_acl->initiatorname,
+ i_buf);
}
static void __core_scsi3_add_registration(struct se_device *, struct se_node_acl *,
- struct t10_pr_registration *, int, int);
+ struct t10_pr_registration *, enum register_type, int);
static int __core_scsi3_check_aptpl_registration(
struct se_device *dev,
@@ -913,7 +879,7 @@ static int __core_scsi3_check_aptpl_registration(
struct se_dev_entry *deve)
{
struct t10_pr_registration *pr_reg, *pr_reg_tmp;
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
unsigned char i_port[PR_APTPL_MAX_IPORT_LEN];
unsigned char t_port[PR_APTPL_MAX_TPORT_LEN];
u16 tpgt;
@@ -925,8 +891,8 @@ static int __core_scsi3_check_aptpl_registration(
*/
snprintf(i_port, PR_APTPL_MAX_IPORT_LEN, "%s", nacl->initiatorname);
snprintf(t_port, PR_APTPL_MAX_TPORT_LEN, "%s",
- TPG_TFO(tpg)->tpg_get_wwn(tpg));
- tpgt = TPG_TFO(tpg)->tpg_get_tag(tpg);
+ tpg->se_tpg_tfo->tpg_get_wwn(tpg));
+ tpgt = tpg->se_tpg_tfo->tpg_get_tag(tpg);
/*
* Look for the matching registrations+reservation from those
* created from APTPL metadata. Note that multiple registrations
@@ -936,7 +902,7 @@ static int __core_scsi3_check_aptpl_registration(
spin_lock(&pr_tmpl->aptpl_reg_lock);
list_for_each_entry_safe(pr_reg, pr_reg_tmp, &pr_tmpl->aptpl_reg_list,
pr_reg_aptpl_list) {
- if (!(strcmp(pr_reg->pr_iport, i_port)) &&
+ if (!strcmp(pr_reg->pr_iport, i_port) &&
(pr_reg->pr_res_mapped_lun == deve->mapped_lun) &&
!(strcmp(pr_reg->pr_tport, t_port)) &&
(pr_reg->pr_reg_tpgt == tpgt) &&
@@ -980,11 +946,10 @@ int core_scsi3_check_aptpl_registration(
struct se_lun *lun,
struct se_lun_acl *lun_acl)
{
- struct se_subsystem_dev *su_dev = SU_DEV(dev);
struct se_node_acl *nacl = lun_acl->se_lun_nacl;
- struct se_dev_entry *deve = &nacl->device_list[lun_acl->mapped_lun];
+ struct se_dev_entry *deve = nacl->device_list[lun_acl->mapped_lun];
- if (T10_RES(su_dev)->res_type != SPC3_PERSISTENT_RESERVATIONS)
+ if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
return 0;
return __core_scsi3_check_aptpl_registration(dev, tpg, lun,
@@ -996,29 +961,27 @@ static void __core_scsi3_dump_registration(
struct se_device *dev,
struct se_node_acl *nacl,
struct t10_pr_registration *pr_reg,
- int register_type)
+ enum register_type register_type)
{
struct se_portal_group *se_tpg = nacl->se_tpg;
char i_buf[PR_REG_ISID_ID_LEN];
- int prf_isid;
memset(&i_buf[0], 0, PR_REG_ISID_ID_LEN);
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
- printk(KERN_INFO "SPC-3 PR [%s] Service Action: REGISTER%s Initiator"
- " Node: %s%s\n", tfo->get_fabric_name(), (register_type == 2) ?
- "_AND_MOVE" : (register_type == 1) ?
+ pr_debug("SPC-3 PR [%s] Service Action: REGISTER%s Initiator"
+ " Node: %s%s\n", tfo->get_fabric_name(), (register_type == REGISTER_AND_MOVE) ?
+ "_AND_MOVE" : (register_type == REGISTER_AND_IGNORE_EXISTING_KEY) ?
"_AND_IGNORE_EXISTING_KEY" : "", nacl->initiatorname,
- (prf_isid) ? i_buf : "");
- printk(KERN_INFO "SPC-3 PR [%s] registration on Target Port: %s,0x%04x\n",
+ i_buf);
+ pr_debug("SPC-3 PR [%s] registration on Target Port: %s,0x%04x\n",
tfo->get_fabric_name(), tfo->tpg_get_wwn(se_tpg),
tfo->tpg_get_tag(se_tpg));
- printk(KERN_INFO "SPC-3 PR [%s] for %s TCM Subsystem %s Object Target"
+ pr_debug("SPC-3 PR [%s] for %s TCM Subsystem %s Object Target"
" Port(s)\n", tfo->get_fabric_name(),
(pr_reg->pr_reg_all_tg_pt) ? "ALL" : "SINGLE",
- TRANSPORT(dev)->name);
- printk(KERN_INFO "SPC-3 PR [%s] SA Res Key: 0x%016Lx PRgeneration:"
+ dev->transport->name);
+ pr_debug("SPC-3 PR [%s] SA Res Key: 0x%016Lx PRgeneration:"
" 0x%08x APTPL: %d\n", tfo->get_fabric_name(),
pr_reg->pr_res_key, pr_reg->pr_res_generation,
pr_reg->pr_reg_aptpl);
@@ -1032,13 +995,12 @@ static void __core_scsi3_add_registration(
struct se_device *dev,
struct se_node_acl *nacl,
struct t10_pr_registration *pr_reg,
- int register_type,
+ enum register_type register_type,
int register_move)
{
- struct se_subsystem_dev *su_dev = SU_DEV(dev);
struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo;
struct t10_pr_registration *pr_reg_tmp, *pr_reg_tmp_safe;
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
/*
* Increment PRgeneration counter for struct se_device upon a successful
@@ -1050,7 +1012,7 @@ static void __core_scsi3_add_registration(
* for the REGISTER.
*/
pr_reg->pr_res_generation = (register_move) ?
- T10_RES(su_dev)->pr_generation++ :
+ dev->t10_pr.pr_generation++ :
core_scsi3_pr_generation(dev);
spin_lock(&pr_tmpl->registration_lock);
@@ -1062,7 +1024,7 @@ static void __core_scsi3_add_registration(
/*
* Skip extra processing for ALL_TG_PT=0 or REGISTER_AND_MOVE.
*/
- if (!(pr_reg->pr_reg_all_tg_pt) || (register_move))
+ if (!pr_reg->pr_reg_all_tg_pt || register_move)
return;
/*
* Walk pr_reg->pr_reg_atp_list and add registrations for ALL_TG_PT=1
@@ -1099,15 +1061,15 @@ static int core_scsi3_alloc_registration(
u64 sa_res_key,
int all_tg_pt,
int aptpl,
- int register_type,
+ enum register_type register_type,
int register_move)
{
struct t10_pr_registration *pr_reg;
pr_reg = __core_scsi3_alloc_registration(dev, nacl, deve, isid,
sa_res_key, all_tg_pt, aptpl);
- if (!(pr_reg))
- return -1;
+ if (!pr_reg)
+ return -EPERM;
__core_scsi3_add_registration(dev, nacl, pr_reg,
register_type, register_move);
@@ -1119,7 +1081,7 @@ static struct t10_pr_registration *__core_scsi3_locate_pr_reg(
struct se_node_acl *nacl,
unsigned char *isid)
{
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
struct t10_pr_registration *pr_reg, *pr_reg_tmp;
struct se_portal_group *tpg;
@@ -1137,18 +1099,18 @@ static struct t10_pr_registration *__core_scsi3_locate_pr_reg(
* If this registration does NOT contain a fabric provided
* ISID, then we have found a match.
*/
- if (!(pr_reg->isid_present_at_reg)) {
+ if (!pr_reg->isid_present_at_reg) {
/*
* Determine if this SCSI device server requires that
* SCSI Intiatior TransportID w/ ISIDs is enforced
* for fabric modules (iSCSI) requiring them.
*/
- if (TPG_TFO(tpg)->sess_get_initiator_sid != NULL) {
- if (DEV_ATTRIB(dev)->enforce_pr_isids)
+ if (tpg->se_tpg_tfo->sess_get_initiator_sid != NULL) {
+ if (dev->dev_attrib.enforce_pr_isids)
continue;
}
atomic_inc(&pr_reg->pr_res_holders);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
spin_unlock(&pr_tmpl->registration_lock);
return pr_reg;
}
@@ -1157,13 +1119,13 @@ static struct t10_pr_registration *__core_scsi3_locate_pr_reg(
* SCSI Initiator Port TransportIDs, then we expect a valid
* matching ISID to be provided by the local SCSI Initiator Port.
*/
- if (!(isid))
+ if (!isid)
continue;
if (strcmp(isid, pr_reg->pr_reg_isid))
continue;
atomic_inc(&pr_reg->pr_res_holders);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
spin_unlock(&pr_tmpl->registration_lock);
return pr_reg;
}
@@ -1180,9 +1142,9 @@ static struct t10_pr_registration *core_scsi3_locate_pr_reg(
struct se_portal_group *tpg = nacl->se_tpg;
unsigned char buf[PR_REG_ISID_LEN], *isid_ptr = NULL;
- if (TPG_TFO(tpg)->sess_get_initiator_sid != NULL) {
+ if (tpg->se_tpg_tfo->sess_get_initiator_sid != NULL) {
memset(&buf[0], 0, PR_REG_ISID_LEN);
- TPG_TFO(tpg)->sess_get_initiator_sid(sess, &buf[0],
+ tpg->se_tpg_tfo->sess_get_initiator_sid(sess, &buf[0],
PR_REG_ISID_LEN);
isid_ptr = &buf[0];
}
@@ -1193,10 +1155,10 @@ static struct t10_pr_registration *core_scsi3_locate_pr_reg(
static void core_scsi3_put_pr_reg(struct t10_pr_registration *pr_reg)
{
atomic_dec(&pr_reg->pr_res_holders);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
}
-static int core_scsi3_check_implict_release(
+static int core_scsi3_check_implicit_release(
struct se_device *dev,
struct t10_pr_registration *pr_reg)
{
@@ -1206,13 +1168,13 @@ static int core_scsi3_check_implict_release(
spin_lock(&dev->dev_reservation_lock);
pr_res_holder = dev->dev_pr_res_holder;
- if (!(pr_res_holder)) {
+ if (!pr_res_holder) {
spin_unlock(&dev->dev_reservation_lock);
return ret;
}
if (pr_res_holder == pr_reg) {
/*
- * Perform an implict RELEASE if the registration that
+ * Perform an implicit RELEASE if the registration that
* is being released is holding the reservation.
*
* From spc4r17, section 5.7.11.1:
@@ -1230,17 +1192,17 @@ static int core_scsi3_check_implict_release(
* For 'All Registrants' reservation types, all existing
* registrations are still processed as reservation holders
* in core_scsi3_pr_seq_non_holder() after the initial
- * reservation holder is implictly released here.
+ * reservation holder is implicitly released here.
*/
} else if (pr_reg->pr_reg_all_tg_pt &&
(!strcmp(pr_res_holder->pr_reg_nacl->initiatorname,
pr_reg->pr_reg_nacl->initiatorname)) &&
(pr_res_holder->pr_res_key == pr_reg->pr_res_key)) {
- printk(KERN_ERR "SPC-3 PR: Unable to perform ALL_TG_PT=1"
+ pr_err("SPC-3 PR: Unable to perform ALL_TG_PT=1"
" UNREGISTER while existing reservation with matching"
" key 0x%016Lx is present from another SCSI Initiator"
" Port\n", pr_reg->pr_res_key);
- ret = -1;
+ ret = -EPERM;
}
spin_unlock(&dev->dev_reservation_lock);
@@ -1248,7 +1210,7 @@ static int core_scsi3_check_implict_release(
}
/*
- * Called with struct t10_reservation_template->registration_lock held.
+ * Called with struct t10_reservation->registration_lock held.
*/
static void __core_scsi3_free_registration(
struct se_device *dev,
@@ -1258,13 +1220,11 @@ static void __core_scsi3_free_registration(
{
struct target_core_fabric_ops *tfo =
pr_reg->pr_reg_nacl->se_tpg->se_tpg_tfo;
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
char i_buf[PR_REG_ISID_ID_LEN];
- int prf_isid;
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
pr_reg->pr_reg_deve->def_pr_registered = 0;
pr_reg->pr_reg_deve->pr_res_key = 0;
@@ -1283,28 +1243,27 @@ static void __core_scsi3_free_registration(
*/
while (atomic_read(&pr_reg->pr_res_holders) != 0) {
spin_unlock(&pr_tmpl->registration_lock);
- printk("SPC-3 PR [%s] waiting for pr_res_holders\n",
+ pr_debug("SPC-3 PR [%s] waiting for pr_res_holders\n",
tfo->get_fabric_name());
cpu_relax();
spin_lock(&pr_tmpl->registration_lock);
}
- printk(KERN_INFO "SPC-3 PR [%s] Service Action: UNREGISTER Initiator"
+ pr_debug("SPC-3 PR [%s] Service Action: UNREGISTER Initiator"
" Node: %s%s\n", tfo->get_fabric_name(),
pr_reg->pr_reg_nacl->initiatorname,
- (prf_isid) ? &i_buf[0] : "");
- printk(KERN_INFO "SPC-3 PR [%s] for %s TCM Subsystem %s Object Target"
+ i_buf);
+ pr_debug("SPC-3 PR [%s] for %s TCM Subsystem %s Object Target"
" Port(s)\n", tfo->get_fabric_name(),
(pr_reg->pr_reg_all_tg_pt) ? "ALL" : "SINGLE",
- TRANSPORT(dev)->name);
- printk(KERN_INFO "SPC-3 PR [%s] SA Res Key: 0x%016Lx PRgeneration:"
+ dev->transport->name);
+ pr_debug("SPC-3 PR [%s] SA Res Key: 0x%016Lx PRgeneration:"
" 0x%08x\n", tfo->get_fabric_name(), pr_reg->pr_res_key,
pr_reg->pr_res_generation);
- if (!(preempt_and_abort_list)) {
+ if (!preempt_and_abort_list) {
pr_reg->pr_reg_deve = NULL;
pr_reg->pr_reg_nacl = NULL;
- kfree(pr_reg->pr_aptpl_buf);
kmem_cache_free(t10_pr_reg_cache, pr_reg);
return;
}
@@ -1319,7 +1278,7 @@ void core_scsi3_free_pr_reg_from_nacl(
struct se_device *dev,
struct se_node_acl *nacl)
{
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
struct t10_pr_registration *pr_reg, *pr_reg_tmp, *pr_res_holder;
/*
* If the passed se_node_acl matches the reservation holder,
@@ -1349,7 +1308,7 @@ void core_scsi3_free_pr_reg_from_nacl(
void core_scsi3_free_all_registrations(
struct se_device *dev)
{
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
struct t10_pr_registration *pr_reg, *pr_reg_tmp, *pr_res_holder;
spin_lock(&dev->dev_reservation_lock);
@@ -1373,7 +1332,6 @@ void core_scsi3_free_all_registrations(
list_for_each_entry_safe(pr_reg, pr_reg_tmp, &pr_tmpl->aptpl_reg_list,
pr_reg_aptpl_list) {
list_del(&pr_reg->pr_reg_aptpl_list);
- kfree(pr_reg->pr_aptpl_buf);
kmem_cache_free(t10_pr_reg_cache, pr_reg);
}
spin_unlock(&pr_tmpl->aptpl_reg_lock);
@@ -1381,17 +1339,17 @@ void core_scsi3_free_all_registrations(
static int core_scsi3_tpg_depend_item(struct se_portal_group *tpg)
{
- return configfs_depend_item(TPG_TFO(tpg)->tf_subsys,
+ return configfs_depend_item(tpg->se_tpg_tfo->tf_subsys,
&tpg->tpg_group.cg_item);
}
static void core_scsi3_tpg_undepend_item(struct se_portal_group *tpg)
{
- configfs_undepend_item(TPG_TFO(tpg)->tf_subsys,
+ configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys,
&tpg->tpg_group.cg_item);
atomic_dec(&tpg->tpg_pr_ref_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
}
static int core_scsi3_nodeacl_depend_item(struct se_node_acl *nacl)
@@ -1401,7 +1359,7 @@ static int core_scsi3_nodeacl_depend_item(struct se_node_acl *nacl)
if (nacl->dynamic_node_acl)
return 0;
- return configfs_depend_item(TPG_TFO(tpg)->tf_subsys,
+ return configfs_depend_item(tpg->se_tpg_tfo->tf_subsys,
&nacl->acl_group.cg_item);
}
@@ -1411,15 +1369,15 @@ static void core_scsi3_nodeacl_undepend_item(struct se_node_acl *nacl)
if (nacl->dynamic_node_acl) {
atomic_dec(&nacl->acl_pr_ref_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
return;
}
- configfs_undepend_item(TPG_TFO(tpg)->tf_subsys,
+ configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys,
&nacl->acl_group.cg_item);
atomic_dec(&nacl->acl_pr_ref_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
}
static int core_scsi3_lunacl_depend_item(struct se_dev_entry *se_deve)
@@ -1430,13 +1388,13 @@ static int core_scsi3_lunacl_depend_item(struct se_dev_entry *se_deve)
/*
* For nacl->dynamic_node_acl=1
*/
- if (!(lun_acl))
+ if (!lun_acl)
return 0;
nacl = lun_acl->se_lun_nacl;
tpg = nacl->se_tpg;
- return configfs_depend_item(TPG_TFO(tpg)->tf_subsys,
+ return configfs_depend_item(tpg->se_tpg_tfo->tf_subsys,
&lun_acl->se_lun_group.cg_item);
}
@@ -1448,22 +1406,23 @@ static void core_scsi3_lunacl_undepend_item(struct se_dev_entry *se_deve)
/*
* For nacl->dynamic_node_acl=1
*/
- if (!(lun_acl)) {
+ if (!lun_acl) {
atomic_dec(&se_deve->pr_ref_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
return;
}
nacl = lun_acl->se_lun_nacl;
tpg = nacl->se_tpg;
- configfs_undepend_item(TPG_TFO(tpg)->tf_subsys,
+ configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys,
&lun_acl->se_lun_group.cg_item);
atomic_dec(&se_deve->pr_ref_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
}
-static int core_scsi3_decode_spec_i_port(
+static sense_reason_t
+core_scsi3_decode_spec_i_port(
struct se_cmd *cmd,
struct se_portal_group *tpg,
unsigned char *l_isid,
@@ -1471,28 +1430,28 @@ static int core_scsi3_decode_spec_i_port(
int all_tg_pt,
int aptpl)
{
- struct se_device *dev = SE_DEV(cmd);
+ struct se_device *dev = cmd->se_dev;
struct se_port *tmp_port;
struct se_portal_group *dest_tpg = NULL, *tmp_tpg;
- struct se_session *se_sess = SE_SESS(cmd);
+ struct se_session *se_sess = cmd->se_sess;
struct se_node_acl *dest_node_acl = NULL;
struct se_dev_entry *dest_se_deve = NULL, *local_se_deve;
struct t10_pr_registration *dest_pr_reg, *local_pr_reg, *pr_reg_e;
struct t10_pr_registration *pr_reg_tmp, *pr_reg_tmp_safe;
- struct list_head tid_dest_list;
+ LIST_HEAD(tid_dest_list);
struct pr_transport_id_holder *tidh_new, *tidh, *tidh_tmp;
struct target_core_fabric_ops *tmp_tf_ops;
- unsigned char *buf = (unsigned char *)T_TASK(cmd)->t_task_buf;
+ unsigned char *buf;
unsigned char *ptr, *i_str = NULL, proto_ident, tmp_proto_ident;
char *iport_ptr = NULL, dest_iport[64], i_buf[PR_REG_ISID_ID_LEN];
+ sense_reason_t ret;
u32 tpdl, tid_len = 0;
- int ret, dest_local_nexus, prf_isid;
+ int dest_local_nexus;
u32 dest_rtpi = 0;
memset(dest_iport, 0, 64);
- INIT_LIST_HEAD(&tid_dest_list);
- local_se_deve = &se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
+ local_se_deve = se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
/*
* Allocate a struct pr_transport_id_holder and setup the
* local_node_acl and local_se_deve pointers and add to
@@ -1500,21 +1459,21 @@ static int core_scsi3_decode_spec_i_port(
* processing in the loop of tid_dest_list below.
*/
tidh_new = kzalloc(sizeof(struct pr_transport_id_holder), GFP_KERNEL);
- if (!(tidh_new)) {
- printk(KERN_ERR "Unable to allocate tidh_new\n");
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ if (!tidh_new) {
+ pr_err("Unable to allocate tidh_new\n");
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
INIT_LIST_HEAD(&tidh_new->dest_list);
tidh_new->dest_tpg = tpg;
tidh_new->dest_node_acl = se_sess->se_node_acl;
tidh_new->dest_se_deve = local_se_deve;
- local_pr_reg = __core_scsi3_alloc_registration(SE_DEV(cmd),
+ local_pr_reg = __core_scsi3_alloc_registration(cmd->se_dev,
se_sess->se_node_acl, local_se_deve, l_isid,
sa_res_key, all_tg_pt, aptpl);
- if (!(local_pr_reg)) {
+ if (!local_pr_reg) {
kfree(tidh_new);
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
tidh_new->dest_pr_reg = local_pr_reg;
/*
@@ -1524,6 +1483,20 @@ static int core_scsi3_decode_spec_i_port(
*/
tidh_new->dest_local_nexus = 1;
list_add_tail(&tidh_new->dest_list, &tid_dest_list);
+
+ if (cmd->data_length < 28) {
+ pr_warn("SPC-PR: Received PR OUT parameter list"
+ " length too small: %u\n", cmd->data_length);
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto out;
+ }
+
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf) {
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ goto out;
+ }
+
/*
* For a PERSISTENT RESERVE OUT specify initiator ports payload,
* first extract TransportID Parameter Data Length, and make sure
@@ -1535,11 +1508,11 @@ static int core_scsi3_decode_spec_i_port(
tpdl |= buf[27] & 0xff;
if ((tpdl + 28) != cmd->data_length) {
- printk(KERN_ERR "SPC-3 PR: Illegal tpdl: %u + 28 byte header"
+ pr_err("SPC-3 PR: Illegal tpdl: %u + 28 byte header"
" does not equal CDB data_length: %u\n", tpdl,
cmd->data_length);
- ret = PYX_TRANSPORT_INVALID_PARAMETER_LIST;
- goto out;
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto out_unmap;
}
/*
* Start processing the received transport IDs using the
@@ -1555,13 +1528,13 @@ static int core_scsi3_decode_spec_i_port(
spin_lock(&dev->se_port_lock);
list_for_each_entry(tmp_port, &dev->dev_sep_list, sep_list) {
tmp_tpg = tmp_port->sep_tpg;
- if (!(tmp_tpg))
+ if (!tmp_tpg)
continue;
- tmp_tf_ops = TPG_TFO(tmp_tpg);
- if (!(tmp_tf_ops))
+ tmp_tf_ops = tmp_tpg->se_tpg_tfo;
+ if (!tmp_tf_ops)
continue;
- if (!(tmp_tf_ops->get_fabric_proto_ident) ||
- !(tmp_tf_ops->tpg_parse_pr_out_transport_id))
+ if (!tmp_tf_ops->get_fabric_proto_ident ||
+ !tmp_tf_ops->tpg_parse_pr_out_transport_id)
continue;
/*
* Look for the matching proto_ident provided by
@@ -1575,57 +1548,55 @@ static int core_scsi3_decode_spec_i_port(
i_str = tmp_tf_ops->tpg_parse_pr_out_transport_id(
tmp_tpg, (const char *)ptr, &tid_len,
&iport_ptr);
- if (!(i_str))
+ if (!i_str)
continue;
atomic_inc(&tmp_tpg->tpg_pr_ref_count);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
spin_unlock(&dev->se_port_lock);
- ret = core_scsi3_tpg_depend_item(tmp_tpg);
- if (ret != 0) {
- printk(KERN_ERR " core_scsi3_tpg_depend_item()"
+ if (core_scsi3_tpg_depend_item(tmp_tpg)) {
+ pr_err(" core_scsi3_tpg_depend_item()"
" for tmp_tpg\n");
atomic_dec(&tmp_tpg->tpg_pr_ref_count);
- smp_mb__after_atomic_dec();
- ret = PYX_TRANSPORT_LU_COMM_FAILURE;
- goto out;
+ smp_mb__after_atomic();
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ goto out_unmap;
}
/*
- * Locate the desination initiator ACL to be registered
+ * Locate the destination initiator ACL to be registered
* from the decoded fabric module specific TransportID
* at *i_str.
*/
- spin_lock_bh(&tmp_tpg->acl_node_lock);
+ spin_lock_irq(&tmp_tpg->acl_node_lock);
dest_node_acl = __core_tpg_get_initiator_node_acl(
tmp_tpg, i_str);
if (dest_node_acl) {
atomic_inc(&dest_node_acl->acl_pr_ref_count);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
}
- spin_unlock_bh(&tmp_tpg->acl_node_lock);
+ spin_unlock_irq(&tmp_tpg->acl_node_lock);
- if (!(dest_node_acl)) {
+ if (!dest_node_acl) {
core_scsi3_tpg_undepend_item(tmp_tpg);
spin_lock(&dev->se_port_lock);
continue;
}
- ret = core_scsi3_nodeacl_depend_item(dest_node_acl);
- if (ret != 0) {
- printk(KERN_ERR "configfs_depend_item() failed"
+ if (core_scsi3_nodeacl_depend_item(dest_node_acl)) {
+ pr_err("configfs_depend_item() failed"
" for dest_node_acl->acl_group\n");
atomic_dec(&dest_node_acl->acl_pr_ref_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
core_scsi3_tpg_undepend_item(tmp_tpg);
- ret = PYX_TRANSPORT_LU_COMM_FAILURE;
- goto out;
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ goto out_unmap;
}
dest_tpg = tmp_tpg;
- printk(KERN_INFO "SPC-3 PR SPEC_I_PT: Located %s Node:"
+ pr_debug("SPC-3 PR SPEC_I_PT: Located %s Node:"
" %s Port RTPI: %hu\n",
- TPG_TFO(dest_tpg)->get_fabric_name(),
+ dest_tpg->se_tpg_tfo->get_fabric_name(),
dest_node_acl->initiatorname, dest_rtpi);
spin_lock(&dev->se_port_lock);
@@ -1633,25 +1604,25 @@ static int core_scsi3_decode_spec_i_port(
}
spin_unlock(&dev->se_port_lock);
- if (!(dest_tpg)) {
- printk(KERN_ERR "SPC-3 PR SPEC_I_PT: Unable to locate"
+ if (!dest_tpg) {
+ pr_err("SPC-3 PR SPEC_I_PT: Unable to locate"
" dest_tpg\n");
- ret = PYX_TRANSPORT_INVALID_PARAMETER_LIST;
- goto out;
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto out_unmap;
}
-#if 0
- printk("SPC-3 PR SPEC_I_PT: Got %s data_length: %u tpdl: %u"
+
+ pr_debug("SPC-3 PR SPEC_I_PT: Got %s data_length: %u tpdl: %u"
" tid_len: %d for %s + %s\n",
- TPG_TFO(dest_tpg)->get_fabric_name(), cmd->data_length,
+ dest_tpg->se_tpg_tfo->get_fabric_name(), cmd->data_length,
tpdl, tid_len, i_str, iport_ptr);
-#endif
+
if (tid_len > tpdl) {
- printk(KERN_ERR "SPC-3 PR SPEC_I_PT: Illegal tid_len:"
+ pr_err("SPC-3 PR SPEC_I_PT: Illegal tid_len:"
" %u for Transport ID: %s\n", tid_len, ptr);
core_scsi3_nodeacl_undepend_item(dest_node_acl);
core_scsi3_tpg_undepend_item(dest_tpg);
- ret = PYX_TRANSPORT_INVALID_PARAMETER_LIST;
- goto out;
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto out_unmap;
}
/*
* Locate the desintation struct se_dev_entry pointer for matching
@@ -1660,35 +1631,34 @@ static int core_scsi3_decode_spec_i_port(
*/
dest_se_deve = core_get_se_deve_from_rtpi(dest_node_acl,
dest_rtpi);
- if (!(dest_se_deve)) {
- printk(KERN_ERR "Unable to locate %s dest_se_deve"
+ if (!dest_se_deve) {
+ pr_err("Unable to locate %s dest_se_deve"
" from destination RTPI: %hu\n",
- TPG_TFO(dest_tpg)->get_fabric_name(),
+ dest_tpg->se_tpg_tfo->get_fabric_name(),
dest_rtpi);
core_scsi3_nodeacl_undepend_item(dest_node_acl);
core_scsi3_tpg_undepend_item(dest_tpg);
- ret = PYX_TRANSPORT_INVALID_PARAMETER_LIST;
- goto out;
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto out_unmap;
}
- ret = core_scsi3_lunacl_depend_item(dest_se_deve);
- if (ret < 0) {
- printk(KERN_ERR "core_scsi3_lunacl_depend_item()"
+ if (core_scsi3_lunacl_depend_item(dest_se_deve)) {
+ pr_err("core_scsi3_lunacl_depend_item()"
" failed\n");
atomic_dec(&dest_se_deve->pr_ref_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
core_scsi3_nodeacl_undepend_item(dest_node_acl);
core_scsi3_tpg_undepend_item(dest_tpg);
- ret = PYX_TRANSPORT_LU_COMM_FAILURE;
- goto out;
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ goto out_unmap;
}
-#if 0
- printk(KERN_INFO "SPC-3 PR SPEC_I_PT: Located %s Node: %s"
+
+ pr_debug("SPC-3 PR SPEC_I_PT: Located %s Node: %s"
" dest_se_deve mapped_lun: %u\n",
- TPG_TFO(dest_tpg)->get_fabric_name(),
+ dest_tpg->se_tpg_tfo->get_fabric_name(),
dest_node_acl->initiatorname, dest_se_deve->mapped_lun);
-#endif
+
/*
* Skip any TransportIDs that already have a registration for
* this target port.
@@ -1712,13 +1682,13 @@ static int core_scsi3_decode_spec_i_port(
*/
tidh_new = kzalloc(sizeof(struct pr_transport_id_holder),
GFP_KERNEL);
- if (!(tidh_new)) {
- printk(KERN_ERR "Unable to allocate tidh_new\n");
+ if (!tidh_new) {
+ pr_err("Unable to allocate tidh_new\n");
core_scsi3_lunacl_undepend_item(dest_se_deve);
core_scsi3_nodeacl_undepend_item(dest_node_acl);
core_scsi3_tpg_undepend_item(dest_tpg);
- ret = PYX_TRANSPORT_LU_COMM_FAILURE;
- goto out;
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ goto out_unmap;
}
INIT_LIST_HEAD(&tidh_new->dest_list);
tidh_new->dest_tpg = dest_tpg;
@@ -1741,16 +1711,16 @@ static int core_scsi3_decode_spec_i_port(
* and then call __core_scsi3_add_registration() in the
* 2nd loop which will never fail.
*/
- dest_pr_reg = __core_scsi3_alloc_registration(SE_DEV(cmd),
+ dest_pr_reg = __core_scsi3_alloc_registration(cmd->se_dev,
dest_node_acl, dest_se_deve, iport_ptr,
sa_res_key, all_tg_pt, aptpl);
- if (!(dest_pr_reg)) {
+ if (!dest_pr_reg) {
core_scsi3_lunacl_undepend_item(dest_se_deve);
core_scsi3_nodeacl_undepend_item(dest_node_acl);
core_scsi3_tpg_undepend_item(dest_tpg);
kfree(tidh_new);
- ret = PYX_TRANSPORT_INVALID_PARAMETER_LIST;
- goto out;
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto out_unmap;
}
tidh_new->dest_pr_reg = dest_pr_reg;
list_add_tail(&tidh_new->dest_list, &tid_dest_list);
@@ -1760,6 +1730,9 @@ static int core_scsi3_decode_spec_i_port(
tid_len = 0;
}
+
+ transport_kunmap_data_sg(cmd);
+
/*
* Go ahead and create a registrations from tid_dest_list for the
* SPEC_I_PT provided TransportID for the *tidh referenced dest_node_acl
@@ -1784,17 +1757,15 @@ static int core_scsi3_decode_spec_i_port(
kfree(tidh);
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
- prf_isid = core_pr_dump_initiator_port(dest_pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(dest_pr_reg, i_buf, PR_REG_ISID_ID_LEN);
- __core_scsi3_add_registration(SE_DEV(cmd), dest_node_acl,
+ __core_scsi3_add_registration(cmd->se_dev, dest_node_acl,
dest_pr_reg, 0, 0);
- printk(KERN_INFO "SPC-3 PR [%s] SPEC_I_PT: Successfully"
+ pr_debug("SPC-3 PR [%s] SPEC_I_PT: Successfully"
" registered Transport ID for Node: %s%s Mapped LUN:"
- " %u\n", TPG_TFO(dest_tpg)->get_fabric_name(),
- dest_node_acl->initiatorname, (prf_isid) ?
- &i_buf[0] : "", dest_se_deve->mapped_lun);
+ " %u\n", dest_tpg->se_tpg_tfo->get_fabric_name(),
+ dest_node_acl->initiatorname, i_buf, dest_se_deve->mapped_lun);
if (dest_local_nexus)
continue;
@@ -1805,6 +1776,8 @@ static int core_scsi3_decode_spec_i_port(
}
return 0;
+out_unmap:
+ transport_kunmap_data_sg(cmd);
out:
/*
* For the failure case, release everything from tid_dest_list
@@ -1831,7 +1804,6 @@ out:
kmem_cache_free(t10_pr_reg_cache, pr_reg_tmp);
}
- kfree(dest_pr_reg->pr_aptpl_buf);
kmem_cache_free(t10_pr_reg_cache, dest_pr_reg);
if (dest_local_nexus)
@@ -1844,37 +1816,25 @@ out:
return ret;
}
-/*
- * Called with struct se_device->dev_reservation_lock held
- */
-static int __core_scsi3_update_aptpl_buf(
+static int core_scsi3_update_aptpl_buf(
struct se_device *dev,
unsigned char *buf,
- u32 pr_aptpl_buf_len,
- int clear_aptpl_metadata)
+ u32 pr_aptpl_buf_len)
{
struct se_lun *lun;
struct se_portal_group *tpg;
- struct se_subsystem_dev *su_dev = SU_DEV(dev);
struct t10_pr_registration *pr_reg;
unsigned char tmp[512], isid_buf[32];
ssize_t len = 0;
int reg_count = 0;
+ int ret = 0;
- memset(buf, 0, pr_aptpl_buf_len);
- /*
- * Called to clear metadata once APTPL has been deactivated.
- */
- if (clear_aptpl_metadata) {
- snprintf(buf, pr_aptpl_buf_len,
- "No Registrations or Reservations\n");
- return 0;
- }
+ spin_lock(&dev->dev_reservation_lock);
+ spin_lock(&dev->t10_pr.registration_lock);
/*
* Walk the registration list..
*/
- spin_lock(&T10_RES(su_dev)->registration_lock);
- list_for_each_entry(pr_reg, &T10_RES(su_dev)->registration_list,
+ list_for_each_entry(pr_reg, &dev->t10_pr.registration_list,
pr_reg_list) {
tmp[0] = '\0';
@@ -1900,7 +1860,7 @@ static int __core_scsi3_update_aptpl_buf(
"res_holder=1\nres_type=%02x\n"
"res_scope=%02x\nres_all_tg_pt=%d\n"
"mapped_lun=%u\n", reg_count,
- TPG_TFO(tpg)->get_fabric_name(),
+ tpg->se_tpg_tfo->get_fabric_name(),
pr_reg->pr_reg_nacl->initiatorname, isid_buf,
pr_reg->pr_res_key, pr_reg->pr_res_type,
pr_reg->pr_res_scope, pr_reg->pr_reg_all_tg_pt,
@@ -1910,17 +1870,17 @@ static int __core_scsi3_update_aptpl_buf(
"initiator_fabric=%s\ninitiator_node=%s\n%s"
"sa_res_key=%llu\nres_holder=0\n"
"res_all_tg_pt=%d\nmapped_lun=%u\n",
- reg_count, TPG_TFO(tpg)->get_fabric_name(),
+ reg_count, tpg->se_tpg_tfo->get_fabric_name(),
pr_reg->pr_reg_nacl->initiatorname, isid_buf,
pr_reg->pr_res_key, pr_reg->pr_reg_all_tg_pt,
pr_reg->pr_res_mapped_lun);
}
- if ((len + strlen(tmp) > pr_aptpl_buf_len)) {
- printk(KERN_ERR "Unable to update renaming"
+ if ((len + strlen(tmp) >= pr_aptpl_buf_len)) {
+ pr_err("Unable to update renaming"
" APTPL metadata\n");
- spin_unlock(&T10_RES(su_dev)->registration_lock);
- return -1;
+ ret = -EMSGSIZE;
+ goto out;
}
len += sprintf(buf+len, "%s", tmp);
@@ -1929,202 +1889,170 @@ static int __core_scsi3_update_aptpl_buf(
*/
snprintf(tmp, 512, "target_fabric=%s\ntarget_node=%s\n"
"tpgt=%hu\nport_rtpi=%hu\ntarget_lun=%u\nPR_REG_END:"
- " %d\n", TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_wwn(tpg),
- TPG_TFO(tpg)->tpg_get_tag(tpg),
+ " %d\n", tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_wwn(tpg),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg),
lun->lun_sep->sep_rtpi, lun->unpacked_lun, reg_count);
- if ((len + strlen(tmp) > pr_aptpl_buf_len)) {
- printk(KERN_ERR "Unable to update renaming"
+ if ((len + strlen(tmp) >= pr_aptpl_buf_len)) {
+ pr_err("Unable to update renaming"
" APTPL metadata\n");
- spin_unlock(&T10_RES(su_dev)->registration_lock);
- return -1;
+ ret = -EMSGSIZE;
+ goto out;
}
len += sprintf(buf+len, "%s", tmp);
reg_count++;
}
- spin_unlock(&T10_RES(su_dev)->registration_lock);
- if (!(reg_count))
+ if (!reg_count)
len += sprintf(buf+len, "No Registrations or Reservations");
- return 0;
-}
-
-static int core_scsi3_update_aptpl_buf(
- struct se_device *dev,
- unsigned char *buf,
- u32 pr_aptpl_buf_len,
- int clear_aptpl_metadata)
-{
- int ret;
-
- spin_lock(&dev->dev_reservation_lock);
- ret = __core_scsi3_update_aptpl_buf(dev, buf, pr_aptpl_buf_len,
- clear_aptpl_metadata);
+out:
+ spin_unlock(&dev->t10_pr.registration_lock);
spin_unlock(&dev->dev_reservation_lock);
return ret;
}
-/*
- * Called with struct se_device->aptpl_file_mutex held
- */
static int __core_scsi3_write_aptpl_to_file(
struct se_device *dev,
- unsigned char *buf,
- u32 pr_aptpl_buf_len)
+ unsigned char *buf)
{
- struct t10_wwn *wwn = &SU_DEV(dev)->t10_wwn;
+ struct t10_wwn *wwn = &dev->t10_wwn;
struct file *file;
- struct iovec iov[1];
- mm_segment_t old_fs;
int flags = O_RDWR | O_CREAT | O_TRUNC;
char path[512];
+ u32 pr_aptpl_buf_len;
int ret;
- memset(iov, 0, sizeof(struct iovec));
memset(path, 0, 512);
- if (strlen(&wwn->unit_serial[0]) > 512) {
- printk(KERN_ERR "WWN value for struct se_device does not fit"
+ if (strlen(&wwn->unit_serial[0]) >= 512) {
+ pr_err("WWN value for struct se_device does not fit"
" into path buffer\n");
- return -1;
+ return -EMSGSIZE;
}
snprintf(path, 512, "/var/target/pr/aptpl_%s", &wwn->unit_serial[0]);
file = filp_open(path, flags, 0600);
- if (IS_ERR(file) || !file || !file->f_dentry) {
- printk(KERN_ERR "filp_open(%s) for APTPL metadata"
+ if (IS_ERR(file)) {
+ pr_err("filp_open(%s) for APTPL metadata"
" failed\n", path);
- return -1;
+ return PTR_ERR(file);
}
- iov[0].iov_base = &buf[0];
- if (!(pr_aptpl_buf_len))
- iov[0].iov_len = (strlen(&buf[0]) + 1); /* Add extra for NULL */
- else
- iov[0].iov_len = pr_aptpl_buf_len;
+ pr_aptpl_buf_len = (strlen(buf) + 1); /* Add extra for NULL */
- old_fs = get_fs();
- set_fs(get_ds());
- ret = vfs_writev(file, &iov[0], 1, &file->f_pos);
- set_fs(old_fs);
+ ret = kernel_write(file, buf, pr_aptpl_buf_len, 0);
- if (ret < 0) {
- printk("Error writing APTPL metadata file: %s\n", path);
- filp_close(file, NULL);
- return -1;
- }
- filp_close(file, NULL);
+ if (ret < 0)
+ pr_debug("Error writing APTPL metadata file: %s\n", path);
+ fput(file);
- return 0;
+ return (ret < 0) ? -EIO : 0;
}
-static int core_scsi3_update_and_write_aptpl(
- struct se_device *dev,
- unsigned char *in_buf,
- u32 in_pr_aptpl_buf_len)
+/*
+ * Clear the APTPL metadata if APTPL has been disabled, otherwise
+ * write out the updated metadata to struct file for this SCSI device.
+ */
+static sense_reason_t core_scsi3_update_and_write_aptpl(struct se_device *dev, bool aptpl)
{
- unsigned char null_buf[64], *buf;
- u32 pr_aptpl_buf_len;
- int ret, clear_aptpl_metadata = 0;
- /*
- * Can be called with a NULL pointer from PROUT service action CLEAR
- */
- if (!(in_buf)) {
- memset(null_buf, 0, 64);
- buf = &null_buf[0];
- /*
- * This will clear the APTPL metadata to:
- * "No Registrations or Reservations" status
- */
- pr_aptpl_buf_len = 64;
- clear_aptpl_metadata = 1;
- } else {
- buf = in_buf;
- pr_aptpl_buf_len = in_pr_aptpl_buf_len;
+ unsigned char *buf;
+ int rc;
+
+ if (!aptpl) {
+ char *null_buf = "No Registrations or Reservations\n";
+
+ rc = __core_scsi3_write_aptpl_to_file(dev, null_buf);
+ dev->t10_pr.pr_aptpl_active = 0;
+ pr_debug("SPC-3 PR: Set APTPL Bit Deactivated\n");
+
+ if (rc)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ return 0;
}
- ret = core_scsi3_update_aptpl_buf(dev, buf, pr_aptpl_buf_len,
- clear_aptpl_metadata);
- if (ret != 0)
- return -1;
- /*
- * __core_scsi3_write_aptpl_to_file() will call strlen()
- * on the passed buf to determine pr_aptpl_buf_len.
- */
- ret = __core_scsi3_write_aptpl_to_file(dev, buf, 0);
- if (ret != 0)
- return -1;
+ buf = kzalloc(PR_APTPL_BUF_LEN, GFP_KERNEL);
+ if (!buf)
+ return TCM_OUT_OF_RESOURCES;
- return ret;
+ rc = core_scsi3_update_aptpl_buf(dev, buf, PR_APTPL_BUF_LEN);
+ if (rc < 0) {
+ kfree(buf);
+ return TCM_OUT_OF_RESOURCES;
+ }
+
+ rc = __core_scsi3_write_aptpl_to_file(dev, buf);
+ if (rc != 0) {
+ pr_err("SPC-3 PR: Could not update APTPL\n");
+ kfree(buf);
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+ dev->t10_pr.pr_aptpl_active = 1;
+ kfree(buf);
+ pr_debug("SPC-3 PR: Set APTPL Bit Activated\n");
+ return 0;
}
-static int core_scsi3_emulate_pro_register(
- struct se_cmd *cmd,
- u64 res_key,
- u64 sa_res_key,
- int aptpl,
- int all_tg_pt,
- int spec_i_pt,
- int ignore_key)
+static sense_reason_t
+core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key,
+ bool aptpl, bool all_tg_pt, bool spec_i_pt, enum register_type register_type)
{
- struct se_session *se_sess = SE_SESS(cmd);
- struct se_device *dev = SE_DEV(cmd);
+ struct se_session *se_sess = cmd->se_sess;
+ struct se_device *dev = cmd->se_dev;
struct se_dev_entry *se_deve;
- struct se_lun *se_lun = SE_LUN(cmd);
+ struct se_lun *se_lun = cmd->se_lun;
struct se_portal_group *se_tpg;
- struct t10_pr_registration *pr_reg, *pr_reg_p, *pr_reg_tmp, *pr_reg_e;
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
- /* Used for APTPL metadata w/ UNREGISTER */
- unsigned char *pr_aptpl_buf = NULL;
+ struct t10_pr_registration *pr_reg, *pr_reg_p, *pr_reg_tmp;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
unsigned char isid_buf[PR_REG_ISID_LEN], *isid_ptr = NULL;
- int pr_holder = 0, ret = 0, type;
+ sense_reason_t ret = TCM_NO_SENSE;
+ int pr_holder = 0, type;
- if (!(se_sess) || !(se_lun)) {
- printk(KERN_ERR "SPC-3 PR: se_sess || struct se_lun is NULL!\n");
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ if (!se_sess || !se_lun) {
+ pr_err("SPC-3 PR: se_sess || struct se_lun is NULL!\n");
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
se_tpg = se_sess->se_tpg;
- se_deve = &se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
+ se_deve = se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
- if (TPG_TFO(se_tpg)->sess_get_initiator_sid != NULL) {
+ if (se_tpg->se_tpg_tfo->sess_get_initiator_sid) {
memset(&isid_buf[0], 0, PR_REG_ISID_LEN);
- TPG_TFO(se_tpg)->sess_get_initiator_sid(se_sess, &isid_buf[0],
+ se_tpg->se_tpg_tfo->sess_get_initiator_sid(se_sess, &isid_buf[0],
PR_REG_ISID_LEN);
isid_ptr = &isid_buf[0];
}
/*
* Follow logic from spc4r17 Section 5.7.7, Register Behaviors Table 47
*/
- pr_reg_e = core_scsi3_locate_pr_reg(dev, se_sess->se_node_acl, se_sess);
- if (!(pr_reg_e)) {
+ pr_reg = core_scsi3_locate_pr_reg(dev, se_sess->se_node_acl, se_sess);
+ if (!pr_reg) {
if (res_key) {
- printk(KERN_WARNING "SPC-3 PR: Reservation Key non-zero"
+ pr_warn("SPC-3 PR: Reservation Key non-zero"
" for SA REGISTER, returning CONFLICT\n");
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ return TCM_RESERVATION_CONFLICT;
}
/*
* Do nothing but return GOOD status.
*/
- if (!(sa_res_key))
- return PYX_TRANSPORT_SENT_TO_TRANSPORT;
+ if (!sa_res_key)
+ return 0;
- if (!(spec_i_pt)) {
+ if (!spec_i_pt) {
/*
* Perform the Service Action REGISTER on the Initiator
* Port Endpoint that the PRO was received from on the
* Logical Unit of the SCSI device server.
*/
- ret = core_scsi3_alloc_registration(SE_DEV(cmd),
+ if (core_scsi3_alloc_registration(cmd->se_dev,
se_sess->se_node_acl, se_deve, isid_ptr,
sa_res_key, all_tg_pt, aptpl,
- ignore_key, 0);
- if (ret != 0) {
- printk(KERN_ERR "Unable to allocate"
+ register_type, 0)) {
+ pr_err("Unable to allocate"
" struct t10_pr_registration\n");
- return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ return TCM_INVALID_PARAMETER_LIST;
}
} else {
/*
@@ -2140,213 +2068,137 @@ static int core_scsi3_emulate_pro_register(
if (ret != 0)
return ret;
}
+
+ return core_scsi3_update_and_write_aptpl(dev, aptpl);
+ }
+
+ /* ok, existing registration */
+
+ if ((register_type == REGISTER) && (res_key != pr_reg->pr_res_key)) {
+ pr_err("SPC-3 PR REGISTER: Received"
+ " res_key: 0x%016Lx does not match"
+ " existing SA REGISTER res_key:"
+ " 0x%016Lx\n", res_key,
+ pr_reg->pr_res_key);
+ ret = TCM_RESERVATION_CONFLICT;
+ goto out;
+ }
+
+ if (spec_i_pt) {
+ pr_err("SPC-3 PR REGISTER: SPEC_I_PT"
+ " set on a registered nexus\n");
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto out;
+ }
+
+ /*
+ * An existing ALL_TG_PT=1 registration being released
+ * must also set ALL_TG_PT=1 in the incoming PROUT.
+ */
+ if (pr_reg->pr_reg_all_tg_pt && !all_tg_pt) {
+ pr_err("SPC-3 PR REGISTER: ALL_TG_PT=1"
+ " registration exists, but ALL_TG_PT=1 bit not"
+ " present in received PROUT\n");
+ ret = TCM_INVALID_CDB_FIELD;
+ goto out;
+ }
+
+ /*
+ * sa_res_key=1 Change Reservation Key for registered I_T Nexus.
+ */
+ if (sa_res_key) {
/*
- * Nothing left to do for the APTPL=0 case.
+ * Increment PRgeneration counter for struct se_device"
+ * upon a successful REGISTER, see spc4r17 section 6.3.2
+ * READ_KEYS service action.
*/
- if (!(aptpl)) {
- pr_tmpl->pr_aptpl_active = 0;
- core_scsi3_update_and_write_aptpl(SE_DEV(cmd), NULL, 0);
- printk("SPC-3 PR: Set APTPL Bit Deactivated for"
- " REGISTER\n");
- return 0;
- }
+ pr_reg->pr_res_generation = core_scsi3_pr_generation(cmd->se_dev);
+ pr_reg->pr_res_key = sa_res_key;
+ pr_debug("SPC-3 PR [%s] REGISTER%s: Changed Reservation"
+ " Key for %s to: 0x%016Lx PRgeneration:"
+ " 0x%08x\n", cmd->se_tfo->get_fabric_name(),
+ (register_type == REGISTER_AND_IGNORE_EXISTING_KEY) ? "_AND_IGNORE_EXISTING_KEY" : "",
+ pr_reg->pr_reg_nacl->initiatorname,
+ pr_reg->pr_res_key, pr_reg->pr_res_generation);
+
+ } else {
/*
- * Locate the newly allocated local I_T Nexus *pr_reg, and
- * update the APTPL metadata information using its
- * preallocated *pr_reg->pr_aptpl_buf.
+ * sa_res_key=0 Unregister Reservation Key for registered I_T Nexus.
*/
- pr_reg = core_scsi3_locate_pr_reg(SE_DEV(cmd),
- se_sess->se_node_acl, se_sess);
-
- ret = core_scsi3_update_and_write_aptpl(SE_DEV(cmd),
- &pr_reg->pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len);
- if (!(ret)) {
- pr_tmpl->pr_aptpl_active = 1;
- printk("SPC-3 PR: Set APTPL Bit Activated for REGISTER\n");
+ pr_holder = core_scsi3_check_implicit_release(
+ cmd->se_dev, pr_reg);
+ if (pr_holder < 0) {
+ ret = TCM_RESERVATION_CONFLICT;
+ goto out;
}
+ type = pr_reg->pr_res_type;
- core_scsi3_put_pr_reg(pr_reg);
- return ret;
- } else {
+ spin_lock(&pr_tmpl->registration_lock);
/*
- * Locate the existing *pr_reg via struct se_node_acl pointers
+ * Release all ALL_TG_PT=1 for the matching SCSI Initiator Port
+ * and matching pr_res_key.
*/
- pr_reg = pr_reg_e;
- type = pr_reg->pr_res_type;
+ if (pr_reg->pr_reg_all_tg_pt) {
+ list_for_each_entry_safe(pr_reg_p, pr_reg_tmp,
+ &pr_tmpl->registration_list,
+ pr_reg_list) {
+
+ if (!pr_reg_p->pr_reg_all_tg_pt)
+ continue;
+ if (pr_reg_p->pr_res_key != res_key)
+ continue;
+ if (pr_reg == pr_reg_p)
+ continue;
+ if (strcmp(pr_reg->pr_reg_nacl->initiatorname,
+ pr_reg_p->pr_reg_nacl->initiatorname))
+ continue;
- if (!(ignore_key)) {
- if (res_key != pr_reg->pr_res_key) {
- printk(KERN_ERR "SPC-3 PR REGISTER: Received"
- " res_key: 0x%016Lx does not match"
- " existing SA REGISTER res_key:"
- " 0x%016Lx\n", res_key,
- pr_reg->pr_res_key);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ __core_scsi3_free_registration(dev,
+ pr_reg_p, NULL, 0);
}
}
- if (spec_i_pt) {
- printk(KERN_ERR "SPC-3 PR UNREGISTER: SPEC_I_PT"
- " set while sa_res_key=0\n");
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
- }
+
/*
- * An existing ALL_TG_PT=1 registration being released
- * must also set ALL_TG_PT=1 in the incoming PROUT.
+ * Release the calling I_T Nexus registration now..
*/
- if (pr_reg->pr_reg_all_tg_pt && !(all_tg_pt)) {
- printk(KERN_ERR "SPC-3 PR UNREGISTER: ALL_TG_PT=1"
- " registration exists, but ALL_TG_PT=1 bit not"
- " present in received PROUT\n");
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_INVALID_CDB_FIELD;
- }
+ __core_scsi3_free_registration(cmd->se_dev, pr_reg, NULL, 1);
+ pr_reg = NULL;
+
/*
- * Allocate APTPL metadata buffer used for UNREGISTER ops
+ * From spc4r17, section 5.7.11.3 Unregistering
+ *
+ * If the persistent reservation is a registrants only
+ * type, the device server shall establish a unit
+ * attention condition for the initiator port associated
+ * with every registered I_T nexus except for the I_T
+ * nexus on which the PERSISTENT RESERVE OUT command was
+ * received, with the additional sense code set to
+ * RESERVATIONS RELEASED.
*/
- if (aptpl) {
- pr_aptpl_buf = kzalloc(pr_tmpl->pr_aptpl_buf_len,
- GFP_KERNEL);
- if (!(pr_aptpl_buf)) {
- printk(KERN_ERR "Unable to allocate"
- " pr_aptpl_buf\n");
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ if (pr_holder &&
+ (type == PR_TYPE_WRITE_EXCLUSIVE_REGONLY ||
+ type == PR_TYPE_EXCLUSIVE_ACCESS_REGONLY)) {
+ list_for_each_entry(pr_reg_p,
+ &pr_tmpl->registration_list,
+ pr_reg_list) {
+
+ core_scsi3_ua_allocate(
+ pr_reg_p->pr_reg_nacl,
+ pr_reg_p->pr_res_mapped_lun,
+ 0x2A,
+ ASCQ_2AH_RESERVATIONS_RELEASED);
}
}
- /*
- * sa_res_key=0 Unregister Reservation Key for registered I_T
- * Nexus sa_res_key=1 Change Reservation Key for registered I_T
- * Nexus.
- */
- if (!(sa_res_key)) {
- pr_holder = core_scsi3_check_implict_release(
- SE_DEV(cmd), pr_reg);
- if (pr_holder < 0) {
- kfree(pr_aptpl_buf);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
- }
-
- spin_lock(&pr_tmpl->registration_lock);
- /*
- * Release all ALL_TG_PT=1 for the matching SCSI Initiator Port
- * and matching pr_res_key.
- */
- if (pr_reg->pr_reg_all_tg_pt) {
- list_for_each_entry_safe(pr_reg_p, pr_reg_tmp,
- &pr_tmpl->registration_list,
- pr_reg_list) {
-
- if (!(pr_reg_p->pr_reg_all_tg_pt))
- continue;
-
- if (pr_reg_p->pr_res_key != res_key)
- continue;
-
- if (pr_reg == pr_reg_p)
- continue;
-
- if (strcmp(pr_reg->pr_reg_nacl->initiatorname,
- pr_reg_p->pr_reg_nacl->initiatorname))
- continue;
-
- __core_scsi3_free_registration(dev,
- pr_reg_p, NULL, 0);
- }
- }
- /*
- * Release the calling I_T Nexus registration now..
- */
- __core_scsi3_free_registration(SE_DEV(cmd), pr_reg,
- NULL, 1);
- /*
- * From spc4r17, section 5.7.11.3 Unregistering
- *
- * If the persistent reservation is a registrants only
- * type, the device server shall establish a unit
- * attention condition for the initiator port associated
- * with every registered I_T nexus except for the I_T
- * nexus on which the PERSISTENT RESERVE OUT command was
- * received, with the additional sense code set to
- * RESERVATIONS RELEASED.
- */
- if (pr_holder &&
- ((type == PR_TYPE_WRITE_EXCLUSIVE_REGONLY) ||
- (type == PR_TYPE_EXCLUSIVE_ACCESS_REGONLY))) {
- list_for_each_entry(pr_reg_p,
- &pr_tmpl->registration_list,
- pr_reg_list) {
-
- core_scsi3_ua_allocate(
- pr_reg_p->pr_reg_nacl,
- pr_reg_p->pr_res_mapped_lun,
- 0x2A,
- ASCQ_2AH_RESERVATIONS_RELEASED);
- }
- }
- spin_unlock(&pr_tmpl->registration_lock);
-
- if (!(aptpl)) {
- pr_tmpl->pr_aptpl_active = 0;
- core_scsi3_update_and_write_aptpl(dev, NULL, 0);
- printk("SPC-3 PR: Set APTPL Bit Deactivated"
- " for UNREGISTER\n");
- return 0;
- }
-
- ret = core_scsi3_update_and_write_aptpl(dev,
- &pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len);
- if (!(ret)) {
- pr_tmpl->pr_aptpl_active = 1;
- printk("SPC-3 PR: Set APTPL Bit Activated"
- " for UNREGISTER\n");
- }
- kfree(pr_aptpl_buf);
- return ret;
- } else {
- /*
- * Increment PRgeneration counter for struct se_device"
- * upon a successful REGISTER, see spc4r17 section 6.3.2
- * READ_KEYS service action.
- */
- pr_reg->pr_res_generation = core_scsi3_pr_generation(
- SE_DEV(cmd));
- pr_reg->pr_res_key = sa_res_key;
- printk("SPC-3 PR [%s] REGISTER%s: Changed Reservation"
- " Key for %s to: 0x%016Lx PRgeneration:"
- " 0x%08x\n", CMD_TFO(cmd)->get_fabric_name(),
- (ignore_key) ? "_AND_IGNORE_EXISTING_KEY" : "",
- pr_reg->pr_reg_nacl->initiatorname,
- pr_reg->pr_res_key, pr_reg->pr_res_generation);
-
- if (!(aptpl)) {
- pr_tmpl->pr_aptpl_active = 0;
- core_scsi3_update_and_write_aptpl(dev, NULL, 0);
- core_scsi3_put_pr_reg(pr_reg);
- printk("SPC-3 PR: Set APTPL Bit Deactivated"
- " for REGISTER\n");
- return 0;
- }
+ spin_unlock(&pr_tmpl->registration_lock);
+ }
- ret = core_scsi3_update_and_write_aptpl(dev,
- &pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len);
- if (!(ret)) {
- pr_tmpl->pr_aptpl_active = 1;
- printk("SPC-3 PR: Set APTPL Bit Activated"
- " for REGISTER\n");
- }
+ ret = core_scsi3_update_and_write_aptpl(dev, aptpl);
- kfree(pr_aptpl_buf);
- core_scsi3_put_pr_reg(pr_reg);
- }
- }
- return 0;
+out:
+ if (pr_reg)
+ core_scsi3_put_pr_reg(pr_reg);
+ return ret;
}
unsigned char *core_scsi3_pr_dump_type(int type)
@@ -2371,39 +2223,32 @@ unsigned char *core_scsi3_pr_dump_type(int type)
return "Unknown SPC-3 PR Type";
}
-static int core_scsi3_pro_reserve(
- struct se_cmd *cmd,
- struct se_device *dev,
- int type,
- int scope,
- u64 res_key)
+static sense_reason_t
+core_scsi3_pro_reserve(struct se_cmd *cmd, int type, int scope, u64 res_key)
{
- struct se_session *se_sess = SE_SESS(cmd);
- struct se_dev_entry *se_deve;
- struct se_lun *se_lun = SE_LUN(cmd);
- struct se_portal_group *se_tpg;
+ struct se_device *dev = cmd->se_dev;
+ struct se_session *se_sess = cmd->se_sess;
+ struct se_lun *se_lun = cmd->se_lun;
struct t10_pr_registration *pr_reg, *pr_res_holder;
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
char i_buf[PR_REG_ISID_ID_LEN];
- int ret, prf_isid;
+ sense_reason_t ret;
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
- if (!(se_sess) || !(se_lun)) {
- printk(KERN_ERR "SPC-3 PR: se_sess || struct se_lun is NULL!\n");
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ if (!se_sess || !se_lun) {
+ pr_err("SPC-3 PR: se_sess || struct se_lun is NULL!\n");
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
- se_tpg = se_sess->se_tpg;
- se_deve = &se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
/*
* Locate the existing *pr_reg via struct se_node_acl pointers
*/
- pr_reg = core_scsi3_locate_pr_reg(SE_DEV(cmd), se_sess->se_node_acl,
+ pr_reg = core_scsi3_locate_pr_reg(cmd->se_dev, se_sess->se_node_acl,
se_sess);
- if (!(pr_reg)) {
- printk(KERN_ERR "SPC-3 PR: Unable to locate"
+ if (!pr_reg) {
+ pr_err("SPC-3 PR: Unable to locate"
" PR_REGISTERED *pr_reg for RESERVE\n");
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
/*
* From spc4r17 Section 5.7.9: Reserving:
@@ -2415,11 +2260,11 @@ static int core_scsi3_pro_reserve(
* registered with the logical unit for the I_T nexus; and
*/
if (res_key != pr_reg->pr_res_key) {
- printk(KERN_ERR "SPC-3 PR RESERVE: Received res_key: 0x%016Lx"
+ pr_err("SPC-3 PR RESERVE: Received res_key: 0x%016Lx"
" does not match existing SA REGISTER res_key:"
" 0x%016Lx\n", res_key, pr_reg->pr_res_key);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ ret = TCM_RESERVATION_CONFLICT;
+ goto out_put_pr_reg;
}
/*
* From spc4r17 Section 5.7.9: Reserving:
@@ -2432,9 +2277,9 @@ static int core_scsi3_pro_reserve(
* and that persistent reservation has a scope of LU_SCOPE.
*/
if (scope != PR_SCOPE_LU_SCOPE) {
- printk(KERN_ERR "SPC-3 PR: Illegal SCOPE: 0x%02x\n", scope);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ pr_err("SPC-3 PR: Illegal SCOPE: 0x%02x\n", scope);
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto out_put_pr_reg;
}
/*
* See if we have an existing PR reservation holder pointer at
@@ -2443,7 +2288,7 @@ static int core_scsi3_pro_reserve(
*/
spin_lock(&dev->dev_reservation_lock);
pr_res_holder = dev->dev_pr_res_holder;
- if ((pr_res_holder)) {
+ if (pr_res_holder) {
/*
* From spc4r17 Section 5.7.9: Reserving:
*
@@ -2456,17 +2301,17 @@ static int core_scsi3_pro_reserve(
*/
if (pr_res_holder != pr_reg) {
struct se_node_acl *pr_res_nacl = pr_res_holder->pr_reg_nacl;
- printk(KERN_ERR "SPC-3 PR: Attempted RESERVE from"
+ pr_err("SPC-3 PR: Attempted RESERVE from"
" [%s]: %s while reservation already held by"
" [%s]: %s, returning RESERVATION_CONFLICT\n",
- CMD_TFO(cmd)->get_fabric_name(),
+ cmd->se_tfo->get_fabric_name(),
se_sess->se_node_acl->initiatorname,
- TPG_TFO(pr_res_nacl->se_tpg)->get_fabric_name(),
+ pr_res_nacl->se_tpg->se_tpg_tfo->get_fabric_name(),
pr_res_holder->pr_reg_nacl->initiatorname);
spin_unlock(&dev->dev_reservation_lock);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ ret = TCM_RESERVATION_CONFLICT;
+ goto out_put_pr_reg;
}
/*
* From spc4r17 Section 5.7.9: Reserving:
@@ -2478,18 +2323,18 @@ static int core_scsi3_pro_reserve(
if ((pr_res_holder->pr_res_type != type) ||
(pr_res_holder->pr_res_scope != scope)) {
struct se_node_acl *pr_res_nacl = pr_res_holder->pr_reg_nacl;
- printk(KERN_ERR "SPC-3 PR: Attempted RESERVE from"
+ pr_err("SPC-3 PR: Attempted RESERVE from"
" [%s]: %s trying to change TYPE and/or SCOPE,"
" while reservation already held by [%s]: %s,"
" returning RESERVATION_CONFLICT\n",
- CMD_TFO(cmd)->get_fabric_name(),
+ cmd->se_tfo->get_fabric_name(),
se_sess->se_node_acl->initiatorname,
- TPG_TFO(pr_res_nacl->se_tpg)->get_fabric_name(),
+ pr_res_nacl->se_tpg->se_tpg_tfo->get_fabric_name(),
pr_res_holder->pr_reg_nacl->initiatorname);
spin_unlock(&dev->dev_reservation_lock);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ ret = TCM_RESERVATION_CONFLICT;
+ goto out_put_pr_reg;
}
/*
* From spc4r17 Section 5.7.9: Reserving:
@@ -2502,8 +2347,8 @@ static int core_scsi3_pro_reserve(
* shall completethe command with GOOD status.
*/
spin_unlock(&dev->dev_reservation_lock);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_SENT_TO_TRANSPORT;
+ ret = 0;
+ goto out_put_pr_reg;
}
/*
* Otherwise, our *pr_reg becomes the PR reservation holder for said
@@ -2513,41 +2358,31 @@ static int core_scsi3_pro_reserve(
pr_reg->pr_res_type = type;
pr_reg->pr_res_holder = 1;
dev->dev_pr_res_holder = pr_reg;
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
- printk(KERN_INFO "SPC-3 PR [%s] Service Action: RESERVE created new"
+ pr_debug("SPC-3 PR [%s] Service Action: RESERVE created new"
" reservation holder TYPE: %s ALL_TG_PT: %d\n",
- CMD_TFO(cmd)->get_fabric_name(), core_scsi3_pr_dump_type(type),
+ cmd->se_tfo->get_fabric_name(), core_scsi3_pr_dump_type(type),
(pr_reg->pr_reg_all_tg_pt) ? 1 : 0);
- printk(KERN_INFO "SPC-3 PR [%s] RESERVE Node: %s%s\n",
- CMD_TFO(cmd)->get_fabric_name(),
+ pr_debug("SPC-3 PR [%s] RESERVE Node: %s%s\n",
+ cmd->se_tfo->get_fabric_name(),
se_sess->se_node_acl->initiatorname,
- (prf_isid) ? &i_buf[0] : "");
+ i_buf);
spin_unlock(&dev->dev_reservation_lock);
- if (pr_tmpl->pr_aptpl_active) {
- ret = core_scsi3_update_and_write_aptpl(SE_DEV(cmd),
- &pr_reg->pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len);
- if (!(ret))
- printk(KERN_INFO "SPC-3 PR: Updated APTPL metadata"
- " for RESERVE\n");
- }
+ if (pr_tmpl->pr_aptpl_active)
+ core_scsi3_update_and_write_aptpl(cmd->se_dev, true);
+ ret = 0;
+out_put_pr_reg:
core_scsi3_put_pr_reg(pr_reg);
- return 0;
+ return ret;
}
-static int core_scsi3_emulate_pro_reserve(
- struct se_cmd *cmd,
- int type,
- int scope,
- u64 res_key)
+static sense_reason_t
+core_scsi3_emulate_pro_reserve(struct se_cmd *cmd, int type, int scope,
+ u64 res_key)
{
- struct se_device *dev = cmd->se_dev;
- int ret = 0;
-
switch (type) {
case PR_TYPE_WRITE_EXCLUSIVE:
case PR_TYPE_EXCLUSIVE_ACCESS:
@@ -2555,15 +2390,12 @@ static int core_scsi3_emulate_pro_reserve(
case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY:
case PR_TYPE_WRITE_EXCLUSIVE_ALLREG:
case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG:
- ret = core_scsi3_pro_reserve(cmd, dev, type, scope, res_key);
- break;
+ return core_scsi3_pro_reserve(cmd, type, scope, res_key);
default:
- printk(KERN_ERR "SPC-3 PR: Unknown Service Action RESERVE Type:"
+ pr_err("SPC-3 PR: Unknown Service Action RESERVE Type:"
" 0x%02x\n", type);
- return PYX_TRANSPORT_INVALID_CDB_FIELD;
+ return TCM_INVALID_CDB_FIELD;
}
-
- return ret;
}
/*
@@ -2573,59 +2405,56 @@ static void __core_scsi3_complete_pro_release(
struct se_device *dev,
struct se_node_acl *se_nacl,
struct t10_pr_registration *pr_reg,
- int explict)
+ int explicit)
{
struct target_core_fabric_ops *tfo = se_nacl->se_tpg->se_tpg_tfo;
char i_buf[PR_REG_ISID_ID_LEN];
- int prf_isid;
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
/*
* Go ahead and release the current PR reservation holder.
*/
dev->dev_pr_res_holder = NULL;
- printk(KERN_INFO "SPC-3 PR [%s] Service Action: %s RELEASE cleared"
+ pr_debug("SPC-3 PR [%s] Service Action: %s RELEASE cleared"
" reservation holder TYPE: %s ALL_TG_PT: %d\n",
- tfo->get_fabric_name(), (explict) ? "explict" : "implict",
+ tfo->get_fabric_name(), (explicit) ? "explicit" : "implicit",
core_scsi3_pr_dump_type(pr_reg->pr_res_type),
(pr_reg->pr_reg_all_tg_pt) ? 1 : 0);
- printk(KERN_INFO "SPC-3 PR [%s] RELEASE Node: %s%s\n",
+ pr_debug("SPC-3 PR [%s] RELEASE Node: %s%s\n",
tfo->get_fabric_name(), se_nacl->initiatorname,
- (prf_isid) ? &i_buf[0] : "");
+ i_buf);
/*
* Clear TYPE and SCOPE for the next PROUT Service Action: RESERVE
*/
pr_reg->pr_res_holder = pr_reg->pr_res_type = pr_reg->pr_res_scope = 0;
}
-static int core_scsi3_emulate_pro_release(
- struct se_cmd *cmd,
- int type,
- int scope,
- u64 res_key)
+static sense_reason_t
+core_scsi3_emulate_pro_release(struct se_cmd *cmd, int type, int scope,
+ u64 res_key)
{
struct se_device *dev = cmd->se_dev;
- struct se_session *se_sess = SE_SESS(cmd);
- struct se_lun *se_lun = SE_LUN(cmd);
+ struct se_session *se_sess = cmd->se_sess;
+ struct se_lun *se_lun = cmd->se_lun;
struct t10_pr_registration *pr_reg, *pr_reg_p, *pr_res_holder;
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
- int ret, all_reg = 0;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
+ int all_reg = 0;
+ sense_reason_t ret = 0;
- if (!(se_sess) || !(se_lun)) {
- printk(KERN_ERR "SPC-3 PR: se_sess || struct se_lun is NULL!\n");
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ if (!se_sess || !se_lun) {
+ pr_err("SPC-3 PR: se_sess || struct se_lun is NULL!\n");
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
/*
* Locate the existing *pr_reg via struct se_node_acl pointers
*/
pr_reg = core_scsi3_locate_pr_reg(dev, se_sess->se_node_acl, se_sess);
- if (!(pr_reg)) {
- printk(KERN_ERR "SPC-3 PR: Unable to locate"
+ if (!pr_reg) {
+ pr_err("SPC-3 PR: Unable to locate"
" PR_REGISTERED *pr_reg for RELEASE\n");
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
/*
* From spc4r17 Section 5.7.11.2 Releasing:
@@ -2641,13 +2470,12 @@ static int core_scsi3_emulate_pro_release(
*/
spin_lock(&dev->dev_reservation_lock);
pr_res_holder = dev->dev_pr_res_holder;
- if (!(pr_res_holder)) {
+ if (!pr_res_holder) {
/*
* No persistent reservation, return GOOD status.
*/
spin_unlock(&dev->dev_reservation_lock);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_SENT_TO_TRANSPORT;
+ goto out_put_pr_reg;
}
if ((pr_res_holder->pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG) ||
(pr_res_holder->pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG))
@@ -2660,9 +2488,9 @@ static int core_scsi3_emulate_pro_release(
* persistent reservation holder. return GOOD status.
*/
spin_unlock(&dev->dev_reservation_lock);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_SENT_TO_TRANSPORT;
+ goto out_put_pr_reg;
}
+
/*
* From spc4r17 Section 5.7.11.2 Releasing:
*
@@ -2678,12 +2506,12 @@ static int core_scsi3_emulate_pro_release(
* that is registered with the logical unit for the I_T nexus;
*/
if (res_key != pr_reg->pr_res_key) {
- printk(KERN_ERR "SPC-3 PR RELEASE: Received res_key: 0x%016Lx"
+ pr_err("SPC-3 PR RELEASE: Received res_key: 0x%016Lx"
" does not match existing SA REGISTER res_key:"
" 0x%016Lx\n", res_key, pr_reg->pr_res_key);
spin_unlock(&dev->dev_reservation_lock);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ ret = TCM_RESERVATION_CONFLICT;
+ goto out_put_pr_reg;
}
/*
* From spc4r17 Section 5.7.11.2 Releasing and above:
@@ -2694,18 +2522,18 @@ static int core_scsi3_emulate_pro_release(
if ((pr_res_holder->pr_res_type != type) ||
(pr_res_holder->pr_res_scope != scope)) {
struct se_node_acl *pr_res_nacl = pr_res_holder->pr_reg_nacl;
- printk(KERN_ERR "SPC-3 PR RELEASE: Attempted to release"
+ pr_err("SPC-3 PR RELEASE: Attempted to release"
" reservation from [%s]: %s with different TYPE "
"and/or SCOPE while reservation already held by"
" [%s]: %s, returning RESERVATION_CONFLICT\n",
- CMD_TFO(cmd)->get_fabric_name(),
+ cmd->se_tfo->get_fabric_name(),
se_sess->se_node_acl->initiatorname,
- TPG_TFO(pr_res_nacl->se_tpg)->get_fabric_name(),
+ pr_res_nacl->se_tpg->se_tpg_tfo->get_fabric_name(),
pr_res_holder->pr_reg_nacl->initiatorname);
spin_unlock(&dev->dev_reservation_lock);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ ret = TCM_RESERVATION_CONFLICT;
+ goto out_put_pr_reg;
}
/*
* In response to a persistent reservation release request from the
@@ -2757,38 +2585,33 @@ static int core_scsi3_emulate_pro_release(
spin_unlock(&pr_tmpl->registration_lock);
write_aptpl:
- if (pr_tmpl->pr_aptpl_active) {
- ret = core_scsi3_update_and_write_aptpl(SE_DEV(cmd),
- &pr_reg->pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len);
- if (!(ret))
- printk("SPC-3 PR: Updated APTPL metadata for RELEASE\n");
- }
+ if (pr_tmpl->pr_aptpl_active)
+ core_scsi3_update_and_write_aptpl(cmd->se_dev, true);
+out_put_pr_reg:
core_scsi3_put_pr_reg(pr_reg);
- return 0;
+ return ret;
}
-static int core_scsi3_emulate_pro_clear(
- struct se_cmd *cmd,
- u64 res_key)
+static sense_reason_t
+core_scsi3_emulate_pro_clear(struct se_cmd *cmd, u64 res_key)
{
struct se_device *dev = cmd->se_dev;
struct se_node_acl *pr_reg_nacl;
- struct se_session *se_sess = SE_SESS(cmd);
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
+ struct se_session *se_sess = cmd->se_sess;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
struct t10_pr_registration *pr_reg, *pr_reg_tmp, *pr_reg_n, *pr_res_holder;
u32 pr_res_mapped_lun = 0;
int calling_it_nexus = 0;
/*
* Locate the existing *pr_reg via struct se_node_acl pointers
*/
- pr_reg_n = core_scsi3_locate_pr_reg(SE_DEV(cmd),
+ pr_reg_n = core_scsi3_locate_pr_reg(cmd->se_dev,
se_sess->se_node_acl, se_sess);
- if (!(pr_reg_n)) {
- printk(KERN_ERR "SPC-3 PR: Unable to locate"
+ if (!pr_reg_n) {
+ pr_err("SPC-3 PR: Unable to locate"
" PR_REGISTERED *pr_reg for CLEAR\n");
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
/*
* From spc4r17 section 5.7.11.6, Clearing:
@@ -2802,12 +2625,12 @@ static int core_scsi3_emulate_pro_clear(
* that is registered with the logical unit for the I_T nexus.
*/
if (res_key != pr_reg_n->pr_res_key) {
- printk(KERN_ERR "SPC-3 PR REGISTER: Received"
+ pr_err("SPC-3 PR REGISTER: Received"
" res_key: 0x%016Lx does not match"
" existing SA REGISTER res_key:"
" 0x%016Lx\n", res_key, pr_reg_n->pr_res_key);
core_scsi3_put_pr_reg(pr_reg_n);
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ return TCM_RESERVATION_CONFLICT;
}
/*
* a) Release the persistent reservation, if any;
@@ -2839,20 +2662,16 @@ static int core_scsi3_emulate_pro_clear(
* command with CLEAR service action was received, with the
* additional sense code set to RESERVATIONS PREEMPTED.
*/
- if (!(calling_it_nexus))
+ if (!calling_it_nexus)
core_scsi3_ua_allocate(pr_reg_nacl, pr_res_mapped_lun,
0x2A, ASCQ_2AH_RESERVATIONS_PREEMPTED);
}
spin_unlock(&pr_tmpl->registration_lock);
- printk(KERN_INFO "SPC-3 PR [%s] Service Action: CLEAR complete\n",
- CMD_TFO(cmd)->get_fabric_name());
+ pr_debug("SPC-3 PR [%s] Service Action: CLEAR complete\n",
+ cmd->se_tfo->get_fabric_name());
- if (pr_tmpl->pr_aptpl_active) {
- core_scsi3_update_and_write_aptpl(SE_DEV(cmd), NULL, 0);
- printk(KERN_INFO "SPC-3 PR: Updated APTPL metadata"
- " for CLEAR\n");
- }
+ core_scsi3_update_and_write_aptpl(cmd->se_dev, false);
core_scsi3_pr_generation(dev);
return 0;
@@ -2867,18 +2686,16 @@ static void __core_scsi3_complete_pro_preempt(
struct list_head *preempt_and_abort_list,
int type,
int scope,
- int abort)
+ enum preempt_type preempt_type)
{
struct se_node_acl *nacl = pr_reg->pr_reg_nacl;
struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo;
char i_buf[PR_REG_ISID_ID_LEN];
- int prf_isid;
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
/*
- * Do an implict RELEASE of the existing reservation.
+ * Do an implicit RELEASE of the existing reservation.
*/
if (dev->dev_pr_res_holder)
__core_scsi3_complete_pro_release(dev, nacl,
@@ -2889,14 +2706,14 @@ static void __core_scsi3_complete_pro_preempt(
pr_reg->pr_res_type = type;
pr_reg->pr_res_scope = scope;
- printk(KERN_INFO "SPC-3 PR [%s] Service Action: PREEMPT%s created new"
+ pr_debug("SPC-3 PR [%s] Service Action: PREEMPT%s created new"
" reservation holder TYPE: %s ALL_TG_PT: %d\n",
- tfo->get_fabric_name(), (abort) ? "_AND_ABORT" : "",
+ tfo->get_fabric_name(), (preempt_type == PREEMPT_AND_ABORT) ? "_AND_ABORT" : "",
core_scsi3_pr_dump_type(type),
(pr_reg->pr_reg_all_tg_pt) ? 1 : 0);
- printk(KERN_INFO "SPC-3 PR [%s] PREEMPT%s from Node: %s%s\n",
- tfo->get_fabric_name(), (abort) ? "_AND_ABORT" : "",
- nacl->initiatorname, (prf_isid) ? &i_buf[0] : "");
+ pr_debug("SPC-3 PR [%s] PREEMPT%s from Node: %s%s\n",
+ tfo->get_fabric_name(), (preempt_type == PREEMPT_AND_ABORT) ? "_AND_ABORT" : "",
+ nacl->initiatorname, i_buf);
/*
* For PREEMPT_AND_ABORT, add the preempting reservation's
* struct t10_pr_registration to the list that will be compared
@@ -2920,73 +2737,50 @@ static void core_scsi3_release_preempt_and_abort(
if (pr_reg_holder == pr_reg)
continue;
if (pr_reg->pr_res_holder) {
- printk(KERN_WARNING "pr_reg->pr_res_holder still set\n");
+ pr_warn("pr_reg->pr_res_holder still set\n");
continue;
}
pr_reg->pr_reg_deve = NULL;
pr_reg->pr_reg_nacl = NULL;
- kfree(pr_reg->pr_aptpl_buf);
kmem_cache_free(t10_pr_reg_cache, pr_reg);
}
}
-int core_scsi3_check_cdb_abort_and_preempt(
- struct list_head *preempt_and_abort_list,
- struct se_cmd *cmd)
-{
- struct t10_pr_registration *pr_reg, *pr_reg_tmp;
-
- list_for_each_entry_safe(pr_reg, pr_reg_tmp, preempt_and_abort_list,
- pr_reg_abort_list) {
- if (pr_reg->pr_res_key == cmd->pr_res_key)
- return 0;
- }
-
- return 1;
-}
-
-static int core_scsi3_pro_preempt(
- struct se_cmd *cmd,
- int type,
- int scope,
- u64 res_key,
- u64 sa_res_key,
- int abort)
+static sense_reason_t
+core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key,
+ u64 sa_res_key, enum preempt_type preempt_type)
{
- struct se_device *dev = SE_DEV(cmd);
- struct se_dev_entry *se_deve;
+ struct se_device *dev = cmd->se_dev;
struct se_node_acl *pr_reg_nacl;
- struct se_session *se_sess = SE_SESS(cmd);
- struct list_head preempt_and_abort_list;
+ struct se_session *se_sess = cmd->se_sess;
+ LIST_HEAD(preempt_and_abort_list);
struct t10_pr_registration *pr_reg, *pr_reg_tmp, *pr_reg_n, *pr_res_holder;
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
u32 pr_res_mapped_lun = 0;
int all_reg = 0, calling_it_nexus = 0, released_regs = 0;
- int prh_type = 0, prh_scope = 0, ret;
+ int prh_type = 0, prh_scope = 0;
- if (!(se_sess))
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ if (!se_sess)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
- se_deve = &se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
- pr_reg_n = core_scsi3_locate_pr_reg(SE_DEV(cmd), se_sess->se_node_acl,
+ pr_reg_n = core_scsi3_locate_pr_reg(cmd->se_dev, se_sess->se_node_acl,
se_sess);
- if (!(pr_reg_n)) {
- printk(KERN_ERR "SPC-3 PR: Unable to locate"
+ if (!pr_reg_n) {
+ pr_err("SPC-3 PR: Unable to locate"
" PR_REGISTERED *pr_reg for PREEMPT%s\n",
- (abort) ? "_AND_ABORT" : "");
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ (preempt_type == PREEMPT_AND_ABORT) ? "_AND_ABORT" : "");
+ return TCM_RESERVATION_CONFLICT;
}
if (pr_reg_n->pr_res_key != res_key) {
core_scsi3_put_pr_reg(pr_reg_n);
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ return TCM_RESERVATION_CONFLICT;
}
if (scope != PR_SCOPE_LU_SCOPE) {
- printk(KERN_ERR "SPC-3 PR: Illegal SCOPE: 0x%02x\n", scope);
+ pr_err("SPC-3 PR: Illegal SCOPE: 0x%02x\n", scope);
core_scsi3_put_pr_reg(pr_reg_n);
- return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ return TCM_INVALID_PARAMETER_LIST;
}
- INIT_LIST_HEAD(&preempt_and_abort_list);
spin_lock(&dev->dev_reservation_lock);
pr_res_holder = dev->dev_pr_res_holder;
@@ -2995,10 +2789,10 @@ static int core_scsi3_pro_preempt(
(pr_res_holder->pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG)))
all_reg = 1;
- if (!(all_reg) && !(sa_res_key)) {
+ if (!all_reg && !sa_res_key) {
spin_unlock(&dev->dev_reservation_lock);
core_scsi3_put_pr_reg(pr_reg_n);
- return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ return TCM_INVALID_PARAMETER_LIST;
}
/*
* From spc4r17, section 5.7.11.4.4 Removing Registrations:
@@ -3009,7 +2803,7 @@ static int core_scsi3_pro_preempt(
* server shall perform a preempt by doing the following in an
* uninterrupted series of actions. (See below..)
*/
- if (!(pr_res_holder) || (pr_res_holder->pr_res_key != sa_res_key)) {
+ if (!pr_res_holder || (pr_res_holder->pr_res_key != sa_res_key)) {
/*
* No existing or SA Reservation Key matching reservations..
*
@@ -3036,7 +2830,7 @@ static int core_scsi3_pro_preempt(
* was received, with the additional sense code set
* to REGISTRATIONS PREEMPTED.
*/
- if (!(all_reg)) {
+ if (!all_reg) {
if (pr_reg->pr_res_key != sa_res_key)
continue;
@@ -3044,7 +2838,7 @@ static int core_scsi3_pro_preempt(
pr_reg_nacl = pr_reg->pr_reg_nacl;
pr_res_mapped_lun = pr_reg->pr_res_mapped_lun;
__core_scsi3_free_registration(dev, pr_reg,
- (abort) ? &preempt_and_abort_list :
+ (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list :
NULL, calling_it_nexus);
released_regs++;
} else {
@@ -3054,7 +2848,7 @@ static int core_scsi3_pro_preempt(
* 5.7.11.4 Preempting, Table 52 and Figure 7.
*
* For a ZERO SA Reservation key, release
- * all other registrations and do an implict
+ * all other registrations and do an implicit
* release of active persistent reservation.
*
* For a non-ZERO SA Reservation key, only
@@ -3072,14 +2866,14 @@ static int core_scsi3_pro_preempt(
pr_reg_nacl = pr_reg->pr_reg_nacl;
pr_res_mapped_lun = pr_reg->pr_res_mapped_lun;
__core_scsi3_free_registration(dev, pr_reg,
- (abort) ? &preempt_and_abort_list :
+ (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list :
NULL, 0);
released_regs++;
}
- if (!(calling_it_nexus))
+ if (!calling_it_nexus)
core_scsi3_ua_allocate(pr_reg_nacl,
pr_res_mapped_lun, 0x2A,
- ASCQ_2AH_RESERVATIONS_PREEMPTED);
+ ASCQ_2AH_REGISTRATIONS_PREEMPTED);
}
spin_unlock(&pr_tmpl->registration_lock);
/*
@@ -3089,10 +2883,10 @@ static int core_scsi3_pro_preempt(
* registered reservation key, then the device server shall
* complete the command with RESERVATION CONFLICT status.
*/
- if (!(released_regs)) {
+ if (!released_regs) {
spin_unlock(&dev->dev_reservation_lock);
core_scsi3_put_pr_reg(pr_reg_n);
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ return TCM_RESERVATION_CONFLICT;
}
/*
* For an existing all registrants type reservation
@@ -3101,27 +2895,20 @@ static int core_scsi3_pro_preempt(
*/
if (pr_res_holder && all_reg && !(sa_res_key)) {
__core_scsi3_complete_pro_preempt(dev, pr_reg_n,
- (abort) ? &preempt_and_abort_list : NULL,
- type, scope, abort);
+ (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list : NULL,
+ type, scope, preempt_type);
- if (abort)
+ if (preempt_type == PREEMPT_AND_ABORT)
core_scsi3_release_preempt_and_abort(
&preempt_and_abort_list, pr_reg_n);
}
spin_unlock(&dev->dev_reservation_lock);
- if (pr_tmpl->pr_aptpl_active) {
- ret = core_scsi3_update_and_write_aptpl(SE_DEV(cmd),
- &pr_reg_n->pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len);
- if (!(ret))
- printk(KERN_INFO "SPC-3 PR: Updated APTPL"
- " metadata for PREEMPT%s\n", (abort) ?
- "_AND_ABORT" : "");
- }
+ if (pr_tmpl->pr_aptpl_active)
+ core_scsi3_update_and_write_aptpl(cmd->se_dev, true);
core_scsi3_put_pr_reg(pr_reg_n);
- core_scsi3_pr_generation(SE_DEV(cmd));
+ core_scsi3_pr_generation(cmd->se_dev);
return 0;
}
/*
@@ -3182,7 +2969,7 @@ static int core_scsi3_pro_preempt(
pr_reg_nacl = pr_reg->pr_reg_nacl;
pr_res_mapped_lun = pr_reg->pr_res_mapped_lun;
__core_scsi3_free_registration(dev, pr_reg,
- (abort) ? &preempt_and_abort_list : NULL,
+ (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list : NULL,
calling_it_nexus);
/*
* e) Establish a unit attention condition for the initiator
@@ -3191,7 +2978,7 @@ static int core_scsi3_pro_preempt(
* additional sense code set to REGISTRATIONS PREEMPTED;
*/
core_scsi3_ua_allocate(pr_reg_nacl, pr_res_mapped_lun, 0x2A,
- ASCQ_2AH_RESERVATIONS_PREEMPTED);
+ ASCQ_2AH_REGISTRATIONS_PREEMPTED);
}
spin_unlock(&pr_tmpl->registration_lock);
/*
@@ -3199,8 +2986,8 @@ static int core_scsi3_pro_preempt(
* I_T nexus using the contents of the SCOPE and TYPE fields;
*/
__core_scsi3_complete_pro_preempt(dev, pr_reg_n,
- (abort) ? &preempt_and_abort_list : NULL,
- type, scope, abort);
+ (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list : NULL,
+ type, scope, preempt_type);
/*
* d) Process tasks as defined in 5.7.1;
* e) See above..
@@ -3240,36 +3027,24 @@ static int core_scsi3_pro_preempt(
* been removed from the primary pr_reg list), except the
* new persistent reservation holder, the calling Initiator Port.
*/
- if (abort) {
+ if (preempt_type == PREEMPT_AND_ABORT) {
core_tmr_lun_reset(dev, NULL, &preempt_and_abort_list, cmd);
core_scsi3_release_preempt_and_abort(&preempt_and_abort_list,
pr_reg_n);
}
- if (pr_tmpl->pr_aptpl_active) {
- ret = core_scsi3_update_and_write_aptpl(SE_DEV(cmd),
- &pr_reg_n->pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len);
- if (!(ret))
- printk("SPC-3 PR: Updated APTPL metadata for PREEMPT"
- "%s\n", (abort) ? "_AND_ABORT" : "");
- }
+ if (pr_tmpl->pr_aptpl_active)
+ core_scsi3_update_and_write_aptpl(cmd->se_dev, true);
core_scsi3_put_pr_reg(pr_reg_n);
- core_scsi3_pr_generation(SE_DEV(cmd));
+ core_scsi3_pr_generation(cmd->se_dev);
return 0;
}
-static int core_scsi3_emulate_pro_preempt(
- struct se_cmd *cmd,
- int type,
- int scope,
- u64 res_key,
- u64 sa_res_key,
- int abort)
+static sense_reason_t
+core_scsi3_emulate_pro_preempt(struct se_cmd *cmd, int type, int scope,
+ u64 res_key, u64 sa_res_key, enum preempt_type preempt_type)
{
- int ret = 0;
-
switch (type) {
case PR_TYPE_WRITE_EXCLUSIVE:
case PR_TYPE_EXCLUSIVE_ACCESS:
@@ -3277,104 +3052,108 @@ static int core_scsi3_emulate_pro_preempt(
case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY:
case PR_TYPE_WRITE_EXCLUSIVE_ALLREG:
case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG:
- ret = core_scsi3_pro_preempt(cmd, type, scope,
- res_key, sa_res_key, abort);
- break;
+ return core_scsi3_pro_preempt(cmd, type, scope, res_key,
+ sa_res_key, preempt_type);
default:
- printk(KERN_ERR "SPC-3 PR: Unknown Service Action PREEMPT%s"
- " Type: 0x%02x\n", (abort) ? "_AND_ABORT" : "", type);
- return PYX_TRANSPORT_INVALID_CDB_FIELD;
+ pr_err("SPC-3 PR: Unknown Service Action PREEMPT%s"
+ " Type: 0x%02x\n", (preempt_type == PREEMPT_AND_ABORT) ? "_AND_ABORT" : "", type);
+ return TCM_INVALID_CDB_FIELD;
}
-
- return ret;
}
-static int core_scsi3_emulate_pro_register_and_move(
- struct se_cmd *cmd,
- u64 res_key,
- u64 sa_res_key,
- int aptpl,
- int unreg)
+static sense_reason_t
+core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
+ u64 sa_res_key, int aptpl, int unreg)
{
- struct se_session *se_sess = SE_SESS(cmd);
- struct se_device *dev = SE_DEV(cmd);
- struct se_dev_entry *se_deve, *dest_se_deve = NULL;
- struct se_lun *se_lun = SE_LUN(cmd);
+ struct se_session *se_sess = cmd->se_sess;
+ struct se_device *dev = cmd->se_dev;
+ struct se_dev_entry *dest_se_deve = NULL;
+ struct se_lun *se_lun = cmd->se_lun;
struct se_node_acl *pr_res_nacl, *pr_reg_nacl, *dest_node_acl = NULL;
struct se_port *se_port;
struct se_portal_group *se_tpg, *dest_se_tpg = NULL;
struct target_core_fabric_ops *dest_tf_ops = NULL, *tf_ops;
struct t10_pr_registration *pr_reg, *pr_res_holder, *dest_pr_reg;
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
- unsigned char *buf = (unsigned char *)T_TASK(cmd)->t_task_buf;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
+ unsigned char *buf;
unsigned char *initiator_str;
char *iport_ptr = NULL, dest_iport[64], i_buf[PR_REG_ISID_ID_LEN];
u32 tid_len, tmp_tid_len;
- int new_reg = 0, type, scope, ret, matching_iname, prf_isid;
+ int new_reg = 0, type, scope, matching_iname;
+ sense_reason_t ret;
unsigned short rtpi;
unsigned char proto_ident;
- if (!(se_sess) || !(se_lun)) {
- printk(KERN_ERR "SPC-3 PR: se_sess || struct se_lun is NULL!\n");
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ if (!se_sess || !se_lun) {
+ pr_err("SPC-3 PR: se_sess || struct se_lun is NULL!\n");
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
+
memset(dest_iport, 0, 64);
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
se_tpg = se_sess->se_tpg;
- tf_ops = TPG_TFO(se_tpg);
- se_deve = &se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
+ tf_ops = se_tpg->se_tpg_tfo;
/*
* Follow logic from spc4r17 Section 5.7.8, Table 50 --
* Register behaviors for a REGISTER AND MOVE service action
*
* Locate the existing *pr_reg via struct se_node_acl pointers
*/
- pr_reg = core_scsi3_locate_pr_reg(SE_DEV(cmd), se_sess->se_node_acl,
+ pr_reg = core_scsi3_locate_pr_reg(cmd->se_dev, se_sess->se_node_acl,
se_sess);
- if (!(pr_reg)) {
- printk(KERN_ERR "SPC-3 PR: Unable to locate PR_REGISTERED"
+ if (!pr_reg) {
+ pr_err("SPC-3 PR: Unable to locate PR_REGISTERED"
" *pr_reg for REGISTER_AND_MOVE\n");
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
/*
* The provided reservation key much match the existing reservation key
* provided during this initiator's I_T nexus registration.
*/
if (res_key != pr_reg->pr_res_key) {
- printk(KERN_WARNING "SPC-3 PR REGISTER_AND_MOVE: Received"
+ pr_warn("SPC-3 PR REGISTER_AND_MOVE: Received"
" res_key: 0x%016Lx does not match existing SA REGISTER"
" res_key: 0x%016Lx\n", res_key, pr_reg->pr_res_key);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ ret = TCM_RESERVATION_CONFLICT;
+ goto out_put_pr_reg;
}
/*
* The service active reservation key needs to be non zero
*/
- if (!(sa_res_key)) {
- printk(KERN_WARNING "SPC-3 PR REGISTER_AND_MOVE: Received zero"
+ if (!sa_res_key) {
+ pr_warn("SPC-3 PR REGISTER_AND_MOVE: Received zero"
" sa_res_key\n");
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto out_put_pr_reg;
}
+
/*
* Determine the Relative Target Port Identifier where the reservation
* will be moved to for the TransportID containing SCSI initiator WWN
* information.
*/
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf) {
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ goto out_put_pr_reg;
+ }
+
rtpi = (buf[18] & 0xff) << 8;
rtpi |= buf[19] & 0xff;
tid_len = (buf[20] & 0xff) << 24;
tid_len |= (buf[21] & 0xff) << 16;
tid_len |= (buf[22] & 0xff) << 8;
tid_len |= buf[23] & 0xff;
+ transport_kunmap_data_sg(cmd);
+ buf = NULL;
if ((tid_len + 24) != cmd->data_length) {
- printk(KERN_ERR "SPC-3 PR: Illegal tid_len: %u + 24 byte header"
+ pr_err("SPC-3 PR: Illegal tid_len: %u + 24 byte header"
" does not equal CDB data_length: %u\n", tid_len,
cmd->data_length);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto out_put_pr_reg;
}
spin_lock(&dev->se_port_lock);
@@ -3382,24 +3161,23 @@ static int core_scsi3_emulate_pro_register_and_move(
if (se_port->sep_rtpi != rtpi)
continue;
dest_se_tpg = se_port->sep_tpg;
- if (!(dest_se_tpg))
+ if (!dest_se_tpg)
continue;
- dest_tf_ops = TPG_TFO(dest_se_tpg);
- if (!(dest_tf_ops))
+ dest_tf_ops = dest_se_tpg->se_tpg_tfo;
+ if (!dest_tf_ops)
continue;
atomic_inc(&dest_se_tpg->tpg_pr_ref_count);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
spin_unlock(&dev->se_port_lock);
- ret = core_scsi3_tpg_depend_item(dest_se_tpg);
- if (ret != 0) {
- printk(KERN_ERR "core_scsi3_tpg_depend_item() failed"
+ if (core_scsi3_tpg_depend_item(dest_se_tpg)) {
+ pr_err("core_scsi3_tpg_depend_item() failed"
" for dest_se_tpg\n");
atomic_dec(&dest_se_tpg->tpg_pr_ref_count);
- smp_mb__after_atomic_dec();
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ smp_mb__after_atomic();
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ goto out_put_pr_reg;
}
spin_lock(&dev->se_port_lock);
@@ -3407,44 +3185,53 @@ static int core_scsi3_emulate_pro_register_and_move(
}
spin_unlock(&dev->se_port_lock);
- if (!(dest_se_tpg) || (!dest_tf_ops)) {
- printk(KERN_ERR "SPC-3 PR REGISTER_AND_MOVE: Unable to locate"
+ if (!dest_se_tpg || !dest_tf_ops) {
+ pr_err("SPC-3 PR REGISTER_AND_MOVE: Unable to locate"
" fabric ops from Relative Target Port Identifier:"
" %hu\n", rtpi);
- core_scsi3_put_pr_reg(pr_reg);
- return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto out_put_pr_reg;
+ }
+
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf) {
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ goto out_put_pr_reg;
}
proto_ident = (buf[24] & 0x0f);
-#if 0
- printk("SPC-3 PR REGISTER_AND_MOVE: Extracted Protocol Identifier:"
+
+ pr_debug("SPC-3 PR REGISTER_AND_MOVE: Extracted Protocol Identifier:"
" 0x%02x\n", proto_ident);
-#endif
+
if (proto_ident != dest_tf_ops->get_fabric_proto_ident(dest_se_tpg)) {
- printk(KERN_ERR "SPC-3 PR REGISTER_AND_MOVE: Received"
+ pr_err("SPC-3 PR REGISTER_AND_MOVE: Received"
" proto_ident: 0x%02x does not match ident: 0x%02x"
" from fabric: %s\n", proto_ident,
dest_tf_ops->get_fabric_proto_ident(dest_se_tpg),
dest_tf_ops->get_fabric_name());
- ret = PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ ret = TCM_INVALID_PARAMETER_LIST;
goto out;
}
if (dest_tf_ops->tpg_parse_pr_out_transport_id == NULL) {
- printk(KERN_ERR "SPC-3 PR REGISTER_AND_MOVE: Fabric does not"
+ pr_err("SPC-3 PR REGISTER_AND_MOVE: Fabric does not"
" containg a valid tpg_parse_pr_out_transport_id"
" function pointer\n");
- ret = PYX_TRANSPORT_LU_COMM_FAILURE;
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
goto out;
}
initiator_str = dest_tf_ops->tpg_parse_pr_out_transport_id(dest_se_tpg,
(const char *)&buf[24], &tmp_tid_len, &iport_ptr);
- if (!(initiator_str)) {
- printk(KERN_ERR "SPC-3 PR REGISTER_AND_MOVE: Unable to locate"
+ if (!initiator_str) {
+ pr_err("SPC-3 PR REGISTER_AND_MOVE: Unable to locate"
" initiator_str from Transport ID\n");
- ret = PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ ret = TCM_INVALID_PARAMETER_LIST;
goto out;
}
- printk(KERN_INFO "SPC-3 PR [%s] Extracted initiator %s identifier: %s"
+ transport_kunmap_data_sg(cmd);
+ buf = NULL;
+
+ pr_debug("SPC-3 PR [%s] Extracted initiator %s identifier: %s"
" %s\n", dest_tf_ops->get_fabric_name(), (iport_ptr != NULL) ?
"port" : "device", initiator_str, (iport_ptr != NULL) ?
iport_ptr : "");
@@ -3459,97 +3246,96 @@ static int core_scsi3_emulate_pro_register_and_move(
pr_reg_nacl = pr_reg->pr_reg_nacl;
matching_iname = (!strcmp(initiator_str,
pr_reg_nacl->initiatorname)) ? 1 : 0;
- if (!(matching_iname))
+ if (!matching_iname)
goto after_iport_check;
- if (!(iport_ptr) || !(pr_reg->isid_present_at_reg)) {
- printk(KERN_ERR "SPC-3 PR REGISTER_AND_MOVE: TransportID: %s"
+ if (!iport_ptr || !pr_reg->isid_present_at_reg) {
+ pr_err("SPC-3 PR REGISTER_AND_MOVE: TransportID: %s"
" matches: %s on received I_T Nexus\n", initiator_str,
pr_reg_nacl->initiatorname);
- ret = PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ ret = TCM_INVALID_PARAMETER_LIST;
goto out;
}
- if (!(strcmp(iport_ptr, pr_reg->pr_reg_isid))) {
- printk(KERN_ERR "SPC-3 PR REGISTER_AND_MOVE: TransportID: %s %s"
+ if (!strcmp(iport_ptr, pr_reg->pr_reg_isid)) {
+ pr_err("SPC-3 PR REGISTER_AND_MOVE: TransportID: %s %s"
" matches: %s %s on received I_T Nexus\n",
initiator_str, iport_ptr, pr_reg_nacl->initiatorname,
pr_reg->pr_reg_isid);
- ret = PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ ret = TCM_INVALID_PARAMETER_LIST;
goto out;
}
after_iport_check:
/*
* Locate the destination struct se_node_acl from the received Transport ID
*/
- spin_lock_bh(&dest_se_tpg->acl_node_lock);
+ spin_lock_irq(&dest_se_tpg->acl_node_lock);
dest_node_acl = __core_tpg_get_initiator_node_acl(dest_se_tpg,
initiator_str);
if (dest_node_acl) {
atomic_inc(&dest_node_acl->acl_pr_ref_count);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
}
- spin_unlock_bh(&dest_se_tpg->acl_node_lock);
+ spin_unlock_irq(&dest_se_tpg->acl_node_lock);
- if (!(dest_node_acl)) {
- printk(KERN_ERR "Unable to locate %s dest_node_acl for"
+ if (!dest_node_acl) {
+ pr_err("Unable to locate %s dest_node_acl for"
" TransportID%s\n", dest_tf_ops->get_fabric_name(),
initiator_str);
- ret = PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ ret = TCM_INVALID_PARAMETER_LIST;
goto out;
}
- ret = core_scsi3_nodeacl_depend_item(dest_node_acl);
- if (ret != 0) {
- printk(KERN_ERR "core_scsi3_nodeacl_depend_item() for"
+
+ if (core_scsi3_nodeacl_depend_item(dest_node_acl)) {
+ pr_err("core_scsi3_nodeacl_depend_item() for"
" dest_node_acl\n");
atomic_dec(&dest_node_acl->acl_pr_ref_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
dest_node_acl = NULL;
- ret = PYX_TRANSPORT_LU_COMM_FAILURE;
+ ret = TCM_INVALID_PARAMETER_LIST;
goto out;
}
-#if 0
- printk(KERN_INFO "SPC-3 PR REGISTER_AND_MOVE: Found %s dest_node_acl:"
+
+ pr_debug("SPC-3 PR REGISTER_AND_MOVE: Found %s dest_node_acl:"
" %s from TransportID\n", dest_tf_ops->get_fabric_name(),
dest_node_acl->initiatorname);
-#endif
+
/*
* Locate the struct se_dev_entry pointer for the matching RELATIVE TARGET
* PORT IDENTIFIER.
*/
dest_se_deve = core_get_se_deve_from_rtpi(dest_node_acl, rtpi);
- if (!(dest_se_deve)) {
- printk(KERN_ERR "Unable to locate %s dest_se_deve from RTPI:"
+ if (!dest_se_deve) {
+ pr_err("Unable to locate %s dest_se_deve from RTPI:"
" %hu\n", dest_tf_ops->get_fabric_name(), rtpi);
- ret = PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ ret = TCM_INVALID_PARAMETER_LIST;
goto out;
}
- ret = core_scsi3_lunacl_depend_item(dest_se_deve);
- if (ret < 0) {
- printk(KERN_ERR "core_scsi3_lunacl_depend_item() failed\n");
+ if (core_scsi3_lunacl_depend_item(dest_se_deve)) {
+ pr_err("core_scsi3_lunacl_depend_item() failed\n");
atomic_dec(&dest_se_deve->pr_ref_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
dest_se_deve = NULL;
- ret = PYX_TRANSPORT_LU_COMM_FAILURE;
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
goto out;
}
-#if 0
- printk(KERN_INFO "SPC-3 PR REGISTER_AND_MOVE: Located %s node %s LUN"
+
+ pr_debug("SPC-3 PR REGISTER_AND_MOVE: Located %s node %s LUN"
" ACL for dest_se_deve->mapped_lun: %u\n",
dest_tf_ops->get_fabric_name(), dest_node_acl->initiatorname,
dest_se_deve->mapped_lun);
-#endif
+
/*
* A persistent reservation needs to already existing in order to
* successfully complete the REGISTER_AND_MOVE service action..
*/
spin_lock(&dev->dev_reservation_lock);
pr_res_holder = dev->dev_pr_res_holder;
- if (!(pr_res_holder)) {
- printk(KERN_WARNING "SPC-3 PR REGISTER_AND_MOVE: No reservation"
+ if (!pr_res_holder) {
+ pr_warn("SPC-3 PR REGISTER_AND_MOVE: No reservation"
" currently held\n");
spin_unlock(&dev->dev_reservation_lock);
- ret = PYX_TRANSPORT_INVALID_CDB_FIELD;
+ ret = TCM_INVALID_CDB_FIELD;
goto out;
}
/*
@@ -3559,10 +3345,10 @@ after_iport_check:
* Register behaviors for a REGISTER AND MOVE service action
*/
if (pr_res_holder != pr_reg) {
- printk(KERN_WARNING "SPC-3 PR REGISTER_AND_MOVE: Calling I_T"
+ pr_warn("SPC-3 PR REGISTER_AND_MOVE: Calling I_T"
" Nexus is not reservation holder\n");
spin_unlock(&dev->dev_reservation_lock);
- ret = PYX_TRANSPORT_RESERVATION_CONFLICT;
+ ret = TCM_RESERVATION_CONFLICT;
goto out;
}
/*
@@ -3576,11 +3362,11 @@ after_iport_check:
*/
if ((pr_res_holder->pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG) ||
(pr_res_holder->pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG)) {
- printk(KERN_WARNING "SPC-3 PR REGISTER_AND_MOVE: Unable to move"
+ pr_warn("SPC-3 PR REGISTER_AND_MOVE: Unable to move"
" reservation for type: %s\n",
core_scsi3_pr_dump_type(pr_res_holder->pr_res_type));
spin_unlock(&dev->dev_reservation_lock);
- ret = PYX_TRANSPORT_RESERVATION_CONFLICT;
+ ret = TCM_RESERVATION_CONFLICT;
goto out;
}
pr_res_nacl = pr_res_holder->pr_reg_nacl;
@@ -3611,13 +3397,12 @@ after_iport_check:
*/
dest_pr_reg = __core_scsi3_locate_pr_reg(dev, dest_node_acl,
iport_ptr);
- if (!(dest_pr_reg)) {
- ret = core_scsi3_alloc_registration(SE_DEV(cmd),
+ if (!dest_pr_reg) {
+ if (core_scsi3_alloc_registration(cmd->se_dev,
dest_node_acl, dest_se_deve, iport_ptr,
- sa_res_key, 0, aptpl, 2, 1);
- if (ret != 0) {
+ sa_res_key, 0, aptpl, 2, 1)) {
spin_unlock(&dev->dev_reservation_lock);
- ret = PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ ret = TCM_INVALID_PARAMETER_LIST;
goto out;
}
dest_pr_reg = __core_scsi3_locate_pr_reg(dev, dest_node_acl,
@@ -3639,24 +3424,23 @@ after_iport_check:
dest_pr_reg->pr_res_holder = 1;
dest_pr_reg->pr_res_type = type;
pr_reg->pr_res_scope = scope;
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
/*
* Increment PRGeneration for existing registrations..
*/
- if (!(new_reg))
+ if (!new_reg)
dest_pr_reg->pr_res_generation = pr_tmpl->pr_generation++;
spin_unlock(&dev->dev_reservation_lock);
- printk(KERN_INFO "SPC-3 PR [%s] Service Action: REGISTER_AND_MOVE"
+ pr_debug("SPC-3 PR [%s] Service Action: REGISTER_AND_MOVE"
" created new reservation holder TYPE: %s on object RTPI:"
" %hu PRGeneration: 0x%08x\n", dest_tf_ops->get_fabric_name(),
core_scsi3_pr_dump_type(type), rtpi,
dest_pr_reg->pr_res_generation);
- printk(KERN_INFO "SPC-3 PR Successfully moved reservation from"
+ pr_debug("SPC-3 PR Successfully moved reservation from"
" %s Fabric Node: %s%s -> %s Fabric Node: %s %s\n",
tf_ops->get_fabric_name(), pr_reg_nacl->initiatorname,
- (prf_isid) ? &i_buf[0] : "", dest_tf_ops->get_fabric_name(),
+ i_buf, dest_tf_ops->get_fabric_name(),
dest_node_acl->initiatorname, (iport_ptr != NULL) ?
iport_ptr : "");
/*
@@ -3677,33 +3461,22 @@ after_iport_check:
} else
core_scsi3_put_pr_reg(pr_reg);
- /*
- * Clear the APTPL metadata if APTPL has been disabled, otherwise
- * write out the updated metadata to struct file for this SCSI device.
- */
- if (!(aptpl)) {
- pr_tmpl->pr_aptpl_active = 0;
- core_scsi3_update_and_write_aptpl(SE_DEV(cmd), NULL, 0);
- printk("SPC-3 PR: Set APTPL Bit Deactivated for"
- " REGISTER_AND_MOVE\n");
- } else {
- pr_tmpl->pr_aptpl_active = 1;
- ret = core_scsi3_update_and_write_aptpl(SE_DEV(cmd),
- &dest_pr_reg->pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len);
- if (!(ret))
- printk("SPC-3 PR: Set APTPL Bit Activated for"
- " REGISTER_AND_MOVE\n");
- }
+ core_scsi3_update_and_write_aptpl(cmd->se_dev, aptpl);
+
+ transport_kunmap_data_sg(cmd);
core_scsi3_put_pr_reg(dest_pr_reg);
return 0;
out:
+ if (buf)
+ transport_kunmap_data_sg(cmd);
if (dest_se_deve)
core_scsi3_lunacl_undepend_item(dest_se_deve);
if (dest_node_acl)
core_scsi3_nodeacl_undepend_item(dest_node_acl);
core_scsi3_tpg_undepend_item(dest_se_tpg);
+
+out_put_pr_reg:
core_scsi3_put_pr_reg(pr_reg);
return ret;
}
@@ -3721,30 +3494,56 @@ static unsigned long long core_scsi3_extract_reservation_key(unsigned char *cdb)
/*
* See spc4r17 section 6.14 Table 170
*/
-static int core_scsi3_emulate_pr_out(struct se_cmd *cmd, unsigned char *cdb)
+sense_reason_t
+target_scsi3_emulate_pr_out(struct se_cmd *cmd)
{
- unsigned char *buf = (unsigned char *)T_TASK(cmd)->t_task_buf;
+ unsigned char *cdb = &cmd->t_task_cdb[0];
+ unsigned char *buf;
u64 res_key, sa_res_key;
int sa, scope, type, aptpl;
int spec_i_pt = 0, all_tg_pt = 0, unreg = 0;
+ sense_reason_t ret;
+
+ /*
+ * Following spc2r20 5.5.1 Reservations overview:
+ *
+ * If a logical unit has been reserved by any RESERVE command and is
+ * still reserved by any initiator, all PERSISTENT RESERVE IN and all
+ * PERSISTENT RESERVE OUT commands shall conflict regardless of
+ * initiator or service action and shall terminate with a RESERVATION
+ * CONFLICT status.
+ */
+ if (cmd->se_dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS) {
+ pr_err("Received PERSISTENT_RESERVE CDB while legacy"
+ " SPC-2 reservation is held, returning"
+ " RESERVATION_CONFLICT\n");
+ return TCM_RESERVATION_CONFLICT;
+ }
+
/*
* FIXME: A NULL struct se_session pointer means an this is not coming from
* a $FABRIC_MOD's nexus, but from internal passthrough ops.
*/
- if (!(SE_SESS(cmd)))
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ if (!cmd->se_sess)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
if (cmd->data_length < 24) {
- printk(KERN_WARNING "SPC-PR: Recieved PR OUT parameter list"
+ pr_warn("SPC-PR: Received PR OUT parameter list"
" length too small: %u\n", cmd->data_length);
- return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ return TCM_INVALID_PARAMETER_LIST;
}
+
/*
* From the PERSISTENT_RESERVE_OUT command descriptor block (CDB)
*/
sa = (cdb[1] & 0x1f);
scope = (cdb[2] & 0xf0);
type = (cdb[2] & 0x0f);
+
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
/*
* From PERSISTENT_RESERVE_OUT parameter list (payload)
*/
@@ -3762,11 +3561,15 @@ static int core_scsi3_emulate_pr_out(struct se_cmd *cmd, unsigned char *cdb)
aptpl = (buf[17] & 0x01);
unreg = (buf[17] & 0x02);
}
+ transport_kunmap_data_sg(cmd);
+ buf = NULL;
+
/*
* SPEC_I_PT=1 is only valid for Service action: REGISTER
*/
if (spec_i_pt && ((cdb[1] & 0x1f) != PRO_REGISTER))
- return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ return TCM_INVALID_PARAMETER_LIST;
+
/*
* From spc4r17 section 6.14:
*
@@ -3776,12 +3579,13 @@ static int core_scsi3_emulate_pr_out(struct se_cmd *cmd, unsigned char *cdb)
* the sense key set to ILLEGAL REQUEST, and the additional sense
* code set to PARAMETER LIST LENGTH ERROR.
*/
- if (!(spec_i_pt) && ((cdb[1] & 0x1f) != PRO_REGISTER_AND_MOVE) &&
+ if (!spec_i_pt && ((cdb[1] & 0x1f) != PRO_REGISTER_AND_MOVE) &&
(cmd->data_length != 24)) {
- printk(KERN_WARNING "SPC-PR: Recieved PR OUT illegal parameter"
+ pr_warn("SPC-PR: Received PR OUT illegal parameter"
" list length: %u\n", cmd->data_length);
- return PYX_TRANSPORT_INVALID_PARAMETER_LIST;
+ return TCM_INVALID_PARAMETER_LIST;
}
+
/*
* (core_scsi3_emulate_pro_* function parameters
* are defined by spc4r17 Table 174:
@@ -3789,35 +3593,43 @@ static int core_scsi3_emulate_pr_out(struct se_cmd *cmd, unsigned char *cdb)
*/
switch (sa) {
case PRO_REGISTER:
- return core_scsi3_emulate_pro_register(cmd,
- res_key, sa_res_key, aptpl, all_tg_pt, spec_i_pt, 0);
+ ret = core_scsi3_emulate_pro_register(cmd,
+ res_key, sa_res_key, aptpl, all_tg_pt, spec_i_pt, REGISTER);
+ break;
case PRO_RESERVE:
- return core_scsi3_emulate_pro_reserve(cmd,
- type, scope, res_key);
+ ret = core_scsi3_emulate_pro_reserve(cmd, type, scope, res_key);
+ break;
case PRO_RELEASE:
- return core_scsi3_emulate_pro_release(cmd,
- type, scope, res_key);
+ ret = core_scsi3_emulate_pro_release(cmd, type, scope, res_key);
+ break;
case PRO_CLEAR:
- return core_scsi3_emulate_pro_clear(cmd, res_key);
+ ret = core_scsi3_emulate_pro_clear(cmd, res_key);
+ break;
case PRO_PREEMPT:
- return core_scsi3_emulate_pro_preempt(cmd, type, scope,
- res_key, sa_res_key, 0);
+ ret = core_scsi3_emulate_pro_preempt(cmd, type, scope,
+ res_key, sa_res_key, PREEMPT);
+ break;
case PRO_PREEMPT_AND_ABORT:
- return core_scsi3_emulate_pro_preempt(cmd, type, scope,
- res_key, sa_res_key, 1);
+ ret = core_scsi3_emulate_pro_preempt(cmd, type, scope,
+ res_key, sa_res_key, PREEMPT_AND_ABORT);
+ break;
case PRO_REGISTER_AND_IGNORE_EXISTING_KEY:
- return core_scsi3_emulate_pro_register(cmd,
- 0, sa_res_key, aptpl, all_tg_pt, spec_i_pt, 1);
+ ret = core_scsi3_emulate_pro_register(cmd,
+ 0, sa_res_key, aptpl, all_tg_pt, spec_i_pt, REGISTER_AND_IGNORE_EXISTING_KEY);
+ break;
case PRO_REGISTER_AND_MOVE:
- return core_scsi3_emulate_pro_register_and_move(cmd, res_key,
+ ret = core_scsi3_emulate_pro_register_and_move(cmd, res_key,
sa_res_key, aptpl, unreg);
+ break;
default:
- printk(KERN_ERR "Unknown PERSISTENT_RESERVE_OUT service"
+ pr_err("Unknown PERSISTENT_RESERVE_OUT service"
" action: 0x%02x\n", cdb[1] & 0x1f);
- return PYX_TRANSPORT_INVALID_CDB_FIELD;
+ return TCM_INVALID_CDB_FIELD;
}
- return PYX_TRANSPORT_INVALID_CDB_FIELD;
+ if (!ret)
+ target_complete_cmd(cmd, GOOD);
+ return ret;
}
/*
@@ -3825,27 +3637,31 @@ static int core_scsi3_emulate_pr_out(struct se_cmd *cmd, unsigned char *cdb)
*
* See spc4r17 section 5.7.6.2 and section 6.13.2, Table 160
*/
-static int core_scsi3_pri_read_keys(struct se_cmd *cmd)
+static sense_reason_t
+core_scsi3_pri_read_keys(struct se_cmd *cmd)
{
- struct se_device *se_dev = SE_DEV(cmd);
- struct se_subsystem_dev *su_dev = SU_DEV(se_dev);
+ struct se_device *dev = cmd->se_dev;
struct t10_pr_registration *pr_reg;
- unsigned char *buf = (unsigned char *)T_TASK(cmd)->t_task_buf;
+ unsigned char *buf;
u32 add_len = 0, off = 8;
if (cmd->data_length < 8) {
- printk(KERN_ERR "PRIN SA READ_KEYS SCSI Data Length: %u"
+ pr_err("PRIN SA READ_KEYS SCSI Data Length: %u"
" too small\n", cmd->data_length);
- return PYX_TRANSPORT_INVALID_CDB_FIELD;
+ return TCM_INVALID_CDB_FIELD;
}
- buf[0] = ((T10_RES(su_dev)->pr_generation >> 24) & 0xff);
- buf[1] = ((T10_RES(su_dev)->pr_generation >> 16) & 0xff);
- buf[2] = ((T10_RES(su_dev)->pr_generation >> 8) & 0xff);
- buf[3] = (T10_RES(su_dev)->pr_generation & 0xff);
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ buf[0] = ((dev->t10_pr.pr_generation >> 24) & 0xff);
+ buf[1] = ((dev->t10_pr.pr_generation >> 16) & 0xff);
+ buf[2] = ((dev->t10_pr.pr_generation >> 8) & 0xff);
+ buf[3] = (dev->t10_pr.pr_generation & 0xff);
- spin_lock(&T10_RES(su_dev)->registration_lock);
- list_for_each_entry(pr_reg, &T10_RES(su_dev)->registration_list,
+ spin_lock(&dev->t10_pr.registration_lock);
+ list_for_each_entry(pr_reg, &dev->t10_pr.registration_list,
pr_reg_list) {
/*
* Check for overflow of 8byte PRI READ_KEYS payload and
@@ -3865,13 +3681,15 @@ static int core_scsi3_pri_read_keys(struct se_cmd *cmd)
add_len += 8;
}
- spin_unlock(&T10_RES(su_dev)->registration_lock);
+ spin_unlock(&dev->t10_pr.registration_lock);
buf[4] = ((add_len >> 24) & 0xff);
buf[5] = ((add_len >> 16) & 0xff);
buf[6] = ((add_len >> 8) & 0xff);
buf[7] = (add_len & 0xff);
+ transport_kunmap_data_sg(cmd);
+
return 0;
}
@@ -3880,29 +3698,33 @@ static int core_scsi3_pri_read_keys(struct se_cmd *cmd)
*
* See spc4r17 section 5.7.6.3 and section 6.13.3.2 Table 161 and 162
*/
-static int core_scsi3_pri_read_reservation(struct se_cmd *cmd)
+static sense_reason_t
+core_scsi3_pri_read_reservation(struct se_cmd *cmd)
{
- struct se_device *se_dev = SE_DEV(cmd);
- struct se_subsystem_dev *su_dev = SU_DEV(se_dev);
+ struct se_device *dev = cmd->se_dev;
struct t10_pr_registration *pr_reg;
- unsigned char *buf = (unsigned char *)T_TASK(cmd)->t_task_buf;
+ unsigned char *buf;
u64 pr_res_key;
u32 add_len = 16; /* Hardcoded to 16 when a reservation is held. */
if (cmd->data_length < 8) {
- printk(KERN_ERR "PRIN SA READ_RESERVATIONS SCSI Data Length: %u"
+ pr_err("PRIN SA READ_RESERVATIONS SCSI Data Length: %u"
" too small\n", cmd->data_length);
- return PYX_TRANSPORT_INVALID_CDB_FIELD;
+ return TCM_INVALID_CDB_FIELD;
}
- buf[0] = ((T10_RES(su_dev)->pr_generation >> 24) & 0xff);
- buf[1] = ((T10_RES(su_dev)->pr_generation >> 16) & 0xff);
- buf[2] = ((T10_RES(su_dev)->pr_generation >> 8) & 0xff);
- buf[3] = (T10_RES(su_dev)->pr_generation & 0xff);
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ buf[0] = ((dev->t10_pr.pr_generation >> 24) & 0xff);
+ buf[1] = ((dev->t10_pr.pr_generation >> 16) & 0xff);
+ buf[2] = ((dev->t10_pr.pr_generation >> 8) & 0xff);
+ buf[3] = (dev->t10_pr.pr_generation & 0xff);
- spin_lock(&se_dev->dev_reservation_lock);
- pr_reg = se_dev->dev_pr_res_holder;
- if ((pr_reg)) {
+ spin_lock(&dev->dev_reservation_lock);
+ pr_reg = dev->dev_pr_res_holder;
+ if (pr_reg) {
/*
* Set the hardcoded Additional Length
*/
@@ -3911,10 +3733,9 @@ static int core_scsi3_pri_read_reservation(struct se_cmd *cmd)
buf[6] = ((add_len >> 8) & 0xff);
buf[7] = (add_len & 0xff);
- if (cmd->data_length < 22) {
- spin_unlock(&se_dev->dev_reservation_lock);
- return 0;
- }
+ if (cmd->data_length < 22)
+ goto err;
+
/*
* Set the Reservation key.
*
@@ -3951,7 +3772,10 @@ static int core_scsi3_pri_read_reservation(struct se_cmd *cmd)
buf[21] = (pr_reg->pr_res_scope & 0xf0) |
(pr_reg->pr_res_type & 0x0f);
}
- spin_unlock(&se_dev->dev_reservation_lock);
+
+err:
+ spin_unlock(&dev->dev_reservation_lock);
+ transport_kunmap_data_sg(cmd);
return 0;
}
@@ -3961,19 +3785,24 @@ static int core_scsi3_pri_read_reservation(struct se_cmd *cmd)
*
* See spc4r17 section 6.13.4 Table 165
*/
-static int core_scsi3_pri_report_capabilities(struct se_cmd *cmd)
+static sense_reason_t
+core_scsi3_pri_report_capabilities(struct se_cmd *cmd)
{
- struct se_device *dev = SE_DEV(cmd);
- struct t10_reservation_template *pr_tmpl = &SU_DEV(dev)->t10_reservation;
- unsigned char *buf = (unsigned char *)T_TASK(cmd)->t_task_buf;
+ struct se_device *dev = cmd->se_dev;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
+ unsigned char *buf;
u16 add_len = 8; /* Hardcoded to 8. */
if (cmd->data_length < 6) {
- printk(KERN_ERR "PRIN SA REPORT_CAPABILITIES SCSI Data Length:"
+ pr_err("PRIN SA REPORT_CAPABILITIES SCSI Data Length:"
" %u too small\n", cmd->data_length);
- return PYX_TRANSPORT_INVALID_CDB_FIELD;
+ return TCM_INVALID_CDB_FIELD;
}
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
buf[0] = ((add_len << 8) & 0xff);
buf[1] = (add_len & 0xff);
buf[2] |= 0x10; /* CRH: Compatible Reservation Hanlding bit. */
@@ -4004,6 +3833,8 @@ static int core_scsi3_pri_report_capabilities(struct se_cmd *cmd)
buf[4] |= 0x02; /* PR_TYPE_WRITE_EXCLUSIVE */
buf[5] |= 0x01; /* PR_TYPE_EXCLUSIVE_ACCESS_ALLREG */
+ transport_kunmap_data_sg(cmd);
+
return 0;
}
@@ -4012,29 +3843,33 @@ static int core_scsi3_pri_report_capabilities(struct se_cmd *cmd)
*
* See spc4r17 section 6.13.5 Table 168 and 169
*/
-static int core_scsi3_pri_read_full_status(struct se_cmd *cmd)
+static sense_reason_t
+core_scsi3_pri_read_full_status(struct se_cmd *cmd)
{
- struct se_device *se_dev = SE_DEV(cmd);
+ struct se_device *dev = cmd->se_dev;
struct se_node_acl *se_nacl;
- struct se_subsystem_dev *su_dev = SU_DEV(se_dev);
struct se_portal_group *se_tpg;
struct t10_pr_registration *pr_reg, *pr_reg_tmp;
- struct t10_reservation_template *pr_tmpl = &SU_DEV(se_dev)->t10_reservation;
- unsigned char *buf = (unsigned char *)T_TASK(cmd)->t_task_buf;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
+ unsigned char *buf;
u32 add_desc_len = 0, add_len = 0, desc_len, exp_desc_len;
u32 off = 8; /* off into first Full Status descriptor */
int format_code = 0;
if (cmd->data_length < 8) {
- printk(KERN_ERR "PRIN SA READ_FULL_STATUS SCSI Data Length: %u"
+ pr_err("PRIN SA READ_FULL_STATUS SCSI Data Length: %u"
" too small\n", cmd->data_length);
- return PYX_TRANSPORT_INVALID_CDB_FIELD;
+ return TCM_INVALID_CDB_FIELD;
}
- buf[0] = ((T10_RES(su_dev)->pr_generation >> 24) & 0xff);
- buf[1] = ((T10_RES(su_dev)->pr_generation >> 16) & 0xff);
- buf[2] = ((T10_RES(su_dev)->pr_generation >> 8) & 0xff);
- buf[3] = (T10_RES(su_dev)->pr_generation & 0xff);
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ buf[0] = ((dev->t10_pr.pr_generation >> 24) & 0xff);
+ buf[1] = ((dev->t10_pr.pr_generation >> 16) & 0xff);
+ buf[2] = ((dev->t10_pr.pr_generation >> 8) & 0xff);
+ buf[3] = (dev->t10_pr.pr_generation & 0xff);
spin_lock(&pr_tmpl->registration_lock);
list_for_each_entry_safe(pr_reg, pr_reg_tmp,
@@ -4045,21 +3880,21 @@ static int core_scsi3_pri_read_full_status(struct se_cmd *cmd)
add_desc_len = 0;
atomic_inc(&pr_reg->pr_res_holders);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
spin_unlock(&pr_tmpl->registration_lock);
/*
* Determine expected length of $FABRIC_MOD specific
* TransportID full status descriptor..
*/
- exp_desc_len = TPG_TFO(se_tpg)->tpg_get_pr_transport_id_len(
+ exp_desc_len = se_tpg->se_tpg_tfo->tpg_get_pr_transport_id_len(
se_tpg, se_nacl, pr_reg, &format_code);
if ((exp_desc_len + add_len) > cmd->data_length) {
- printk(KERN_WARNING "SPC-3 PRIN READ_FULL_STATUS ran"
+ pr_warn("SPC-3 PRIN READ_FULL_STATUS ran"
" out of buffer: %d\n", cmd->data_length);
spin_lock(&pr_tmpl->registration_lock);
atomic_dec(&pr_reg->pr_res_holders);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
break;
}
/*
@@ -4105,23 +3940,23 @@ static int core_scsi3_pri_read_full_status(struct se_cmd *cmd)
* bit is set to one, the contents of the RELATIVE TARGET PORT
* IDENTIFIER field are not defined by this standard.
*/
- if (!(pr_reg->pr_reg_all_tg_pt)) {
+ if (!pr_reg->pr_reg_all_tg_pt) {
struct se_port *port = pr_reg->pr_reg_tg_pt_lun->lun_sep;
buf[off++] = ((port->sep_rtpi >> 8) & 0xff);
buf[off++] = (port->sep_rtpi & 0xff);
} else
- off += 2; /* Skip over RELATIVE TARGET PORT IDENTIFER */
+ off += 2; /* Skip over RELATIVE TARGET PORT IDENTIFIER */
/*
* Now, have the $FABRIC_MOD fill in the protocol identifier
*/
- desc_len = TPG_TFO(se_tpg)->tpg_get_pr_transport_id(se_tpg,
+ desc_len = se_tpg->se_tpg_tfo->tpg_get_pr_transport_id(se_tpg,
se_nacl, pr_reg, &format_code, &buf[off+4]);
spin_lock(&pr_tmpl->registration_lock);
atomic_dec(&pr_reg->pr_res_holders);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
/*
* Set the ADDITIONAL DESCRIPTOR LENGTH
*/
@@ -4150,32 +3985,16 @@ static int core_scsi3_pri_read_full_status(struct se_cmd *cmd)
buf[6] = ((add_len >> 8) & 0xff);
buf[7] = (add_len & 0xff);
+ transport_kunmap_data_sg(cmd);
+
return 0;
}
-static int core_scsi3_emulate_pr_in(struct se_cmd *cmd, unsigned char *cdb)
+sense_reason_t
+target_scsi3_emulate_pr_in(struct se_cmd *cmd)
{
- switch (cdb[1] & 0x1f) {
- case PRI_READ_KEYS:
- return core_scsi3_pri_read_keys(cmd);
- case PRI_READ_RESERVATION:
- return core_scsi3_pri_read_reservation(cmd);
- case PRI_REPORT_CAPABILITIES:
- return core_scsi3_pri_report_capabilities(cmd);
- case PRI_READ_FULL_STATUS:
- return core_scsi3_pri_read_full_status(cmd);
- default:
- printk(KERN_ERR "Unknown PERSISTENT_RESERVE_IN service"
- " action: 0x%02x\n", cdb[1] & 0x1f);
- return PYX_TRANSPORT_INVALID_CDB_FIELD;
- }
+ sense_reason_t ret;
-}
-
-int core_scsi3_emulate_pr(struct se_cmd *cmd)
-{
- unsigned char *cdb = &T_TASK(cmd)->t_task_cdb[0];
- struct se_device *dev = cmd->se_dev;
/*
* Following spc2r20 5.5.1 Reservations overview:
*
@@ -4185,68 +4004,56 @@ int core_scsi3_emulate_pr(struct se_cmd *cmd)
* initiator or service action and shall terminate with a RESERVATION
* CONFLICT status.
*/
- if (dev->dev_flags & DF_SPC2_RESERVATIONS) {
- printk(KERN_ERR "Received PERSISTENT_RESERVE CDB while legacy"
+ if (cmd->se_dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS) {
+ pr_err("Received PERSISTENT_RESERVE CDB while legacy"
" SPC-2 reservation is held, returning"
" RESERVATION_CONFLICT\n");
- return PYX_TRANSPORT_RESERVATION_CONFLICT;
+ return TCM_RESERVATION_CONFLICT;
}
- return (cdb[0] == PERSISTENT_RESERVE_OUT) ?
- core_scsi3_emulate_pr_out(cmd, cdb) :
- core_scsi3_emulate_pr_in(cmd, cdb);
-}
+ switch (cmd->t_task_cdb[1] & 0x1f) {
+ case PRI_READ_KEYS:
+ ret = core_scsi3_pri_read_keys(cmd);
+ break;
+ case PRI_READ_RESERVATION:
+ ret = core_scsi3_pri_read_reservation(cmd);
+ break;
+ case PRI_REPORT_CAPABILITIES:
+ ret = core_scsi3_pri_report_capabilities(cmd);
+ break;
+ case PRI_READ_FULL_STATUS:
+ ret = core_scsi3_pri_read_full_status(cmd);
+ break;
+ default:
+ pr_err("Unknown PERSISTENT_RESERVE_IN service"
+ " action: 0x%02x\n", cmd->t_task_cdb[1] & 0x1f);
+ return TCM_INVALID_CDB_FIELD;
+ }
-static int core_pt_reservation_check(struct se_cmd *cmd, u32 *pr_res_type)
-{
- return 0;
+ if (!ret)
+ target_complete_cmd(cmd, GOOD);
+ return ret;
}
-static int core_pt_seq_non_holder(
- struct se_cmd *cmd,
- unsigned char *cdb,
- u32 pr_reg_type)
+sense_reason_t
+target_check_reservation(struct se_cmd *cmd)
{
- return 0;
-}
+ struct se_device *dev = cmd->se_dev;
+ sense_reason_t ret;
-int core_setup_reservations(struct se_device *dev, int force_pt)
-{
- struct se_subsystem_dev *su_dev = dev->se_sub_dev;
- struct t10_reservation_template *rest = &su_dev->t10_reservation;
- /*
- * If this device is from Target_Core_Mod/pSCSI, use the reservations
- * 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 to emulate reservations themselves.
- */
- if (((TRANSPORT(dev)->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) &&
- !(DEV_ATTRIB(dev)->emulate_reservations)) || force_pt) {
- rest->res_type = SPC_PASSTHROUGH;
- rest->pr_ops.t10_reservation_check = &core_pt_reservation_check;
- rest->pr_ops.t10_seq_non_holder = &core_pt_seq_non_holder;
- printk(KERN_INFO "%s: Using SPC_PASSTHROUGH, no reservation"
- " emulation\n", TRANSPORT(dev)->name);
+ if (!cmd->se_sess)
+ return 0;
+ if (dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE)
+ return 0;
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
return 0;
- }
- /*
- * If SPC-3 or above is reported by real or emulated struct se_device,
- * use emulated Persistent Reservations.
- */
- if (TRANSPORT(dev)->get_device_rev(dev) >= SCSI_3) {
- rest->res_type = SPC3_PERSISTENT_RESERVATIONS;
- rest->pr_ops.t10_reservation_check = &core_scsi3_pr_reservation_check;
- rest->pr_ops.t10_seq_non_holder = &core_scsi3_pr_seq_non_holder;
- printk(KERN_INFO "%s: Using SPC3_PERSISTENT_RESERVATIONS"
- " emulation\n", TRANSPORT(dev)->name);
- } else {
- rest->res_type = SPC2_RESERVATIONS;
- rest->pr_ops.t10_reservation_check = &core_scsi2_reservation_check;
- rest->pr_ops.t10_seq_non_holder =
- &core_scsi2_reservation_seq_non_holder;
- printk(KERN_INFO "%s: Using SPC2_RESERVATIONS emulation\n",
- TRANSPORT(dev)->name);
- }
- return 0;
+ spin_lock(&dev->dev_reservation_lock);
+ if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
+ ret = target_scsi2_reservation_check(cmd);
+ else
+ ret = target_scsi3_pr_reservation_check(cmd);
+ spin_unlock(&dev->dev_reservation_lock);
+
+ return ret;
}
diff --git a/drivers/target/target_core_pr.h b/drivers/target/target_core_pr.h
index 5603bcfd86d..2ee2936fa0b 100644
--- a/drivers/target/target_core_pr.h
+++ b/drivers/target/target_core_pr.h
@@ -43,13 +43,19 @@
#define PR_APTPL_MAX_IPORT_LEN 256
#define PR_APTPL_MAX_TPORT_LEN 256
+/*
+ * Function defined in target_core_spc.c
+ */
+void spc_parse_naa_6h_vendor_specific(struct se_device *, unsigned char *);
+
extern struct kmem_cache *t10_pr_reg_cache;
-extern int core_pr_dump_initiator_port(struct t10_pr_registration *,
+extern void core_pr_dump_initiator_port(struct t10_pr_registration *,
char *, u32);
-extern int core_scsi2_emulate_crh(struct se_cmd *);
+extern sense_reason_t target_scsi2_reservation_release(struct se_cmd *);
+extern sense_reason_t target_scsi2_reservation_reserve(struct se_cmd *);
extern int core_scsi3_alloc_aptpl_registration(
- struct t10_reservation_template *, u64,
+ struct t10_reservation *, u64,
unsigned char *, unsigned char *, u32,
unsigned char *, u16, u32, int, int, u8);
extern int core_scsi3_check_aptpl_registration(struct se_device *,
@@ -59,9 +65,9 @@ extern void core_scsi3_free_pr_reg_from_nacl(struct se_device *,
struct se_node_acl *);
extern void core_scsi3_free_all_registrations(struct se_device *);
extern unsigned char *core_scsi3_pr_dump_type(int);
-extern int core_scsi3_check_cdb_abort_and_preempt(struct list_head *,
- struct se_cmd *);
-extern int core_scsi3_emulate_pr(struct se_cmd *);
-extern int core_setup_reservations(struct se_device *, int);
+
+extern sense_reason_t target_scsi3_emulate_pr_in(struct se_cmd *);
+extern sense_reason_t target_scsi3_emulate_pr_out(struct se_cmd *);
+extern sense_reason_t target_check_reservation(struct se_cmd *);
#endif /* TARGET_CORE_PR_H */
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
index f2a08477a68..94d00df28f3 100644
--- a/drivers/target/target_core_pscsi.c
+++ b/drivers/target/target_core_pscsi.c
@@ -3,10 +3,7 @@
*
* This file contains the generic target mode <-> Linux SCSI subsystem plugin.
*
- * Copyright (c) 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
+ * (c) Copyright 2003-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -26,7 +23,6 @@
*
******************************************************************************/
-#include <linux/version.h>
#include <linux/string.h>
#include <linux/parser.h>
#include <linux/timer.h>
@@ -34,45 +30,35 @@
#include <linux/blk_types.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
#include <linux/genhd.h>
#include <linux/cdrom.h>
-#include <linux/file.h>
+#include <linux/ratelimit.h>
+#include <linux/module.h>
+#include <asm/unaligned.h>
+
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_host.h>
-#include <scsi/libsas.h> /* For TASK_ATTR_* */
+#include <scsi/scsi_tcq.h>
#include <target/target_core_base.h>
-#include <target/target_core_device.h>
-#include <target/target_core_transport.h>
+#include <target/target_core_backend.h>
+#include "target_core_alua.h"
#include "target_core_pscsi.h"
#define ISPRINT(a) ((a >= ' ') && (a <= '~'))
-static struct se_subsystem_api pscsi_template;
-
-static void pscsi_req_done(struct request *, int);
-
-/* pscsi_get_sh():
- *
- *
- */
-static struct Scsi_Host *pscsi_get_sh(u32 host_no)
+static inline struct pscsi_dev_virt *PSCSI_DEV(struct se_device *dev)
{
- struct Scsi_Host *sh = NULL;
+ return container_of(dev, struct pscsi_dev_virt, dev);
+}
- sh = scsi_host_lookup(host_no);
- if (IS_ERR(sh)) {
- printk(KERN_ERR "Unable to locate SCSI HBA with Host ID:"
- " %u\n", host_no);
- return NULL;
- }
+static struct se_subsystem_api pscsi_template;
- return sh;
-}
+static sense_reason_t pscsi_execute_cmd(struct se_cmd *cmd);
+static void pscsi_req_done(struct request *, int);
/* pscsi_attach_hba():
*
@@ -81,28 +67,23 @@ static struct Scsi_Host *pscsi_get_sh(u32 host_no)
*/
static int pscsi_attach_hba(struct se_hba *hba, u32 host_id)
{
- int hba_depth;
struct pscsi_hba_virt *phv;
phv = kzalloc(sizeof(struct pscsi_hba_virt), GFP_KERNEL);
- if (!(phv)) {
- printk(KERN_ERR "Unable to allocate struct pscsi_hba_virt\n");
- return -1;
+ if (!phv) {
+ pr_err("Unable to allocate struct pscsi_hba_virt\n");
+ return -ENOMEM;
}
phv->phv_host_id = host_id;
- phv->phv_mode = PHV_VIRUTAL_HOST_ID;
- hba_depth = PSCSI_VIRTUAL_HBA_DEPTH;
- atomic_set(&hba->left_queue_depth, hba_depth);
- atomic_set(&hba->max_queue_depth, hba_depth);
+ phv->phv_mode = PHV_VIRTUAL_HOST_ID;
- hba->hba_ptr = (void *)phv;
+ hba->hba_ptr = phv;
- printk(KERN_INFO "CORE_HBA[%d] - TCM SCSI HBA Driver %s on"
+ pr_debug("CORE_HBA[%d] - TCM SCSI HBA Driver %s on"
" Generic Target Core Stack %s\n", hba->hba_id,
PSCSI_VERSION, TARGET_CORE_MOD_VERSION);
- printk(KERN_INFO "CORE_HBA[%d] - Attached SCSI HBA to Generic"
- " Target Core with TCQ Depth: %d\n", hba->hba_id,
- atomic_read(&hba->max_queue_depth));
+ pr_debug("CORE_HBA[%d] - Attached SCSI HBA to Generic\n",
+ hba->hba_id);
return 0;
}
@@ -115,12 +96,12 @@ static void pscsi_detach_hba(struct se_hba *hba)
if (scsi_host) {
scsi_host_put(scsi_host);
- printk(KERN_INFO "CORE_HBA[%d] - Detached SCSI HBA: %s from"
+ pr_debug("CORE_HBA[%d] - Detached SCSI HBA: %s from"
" Generic Target Core\n", hba->hba_id,
(scsi_host->hostt->name) ? (scsi_host->hostt->name) :
"Unknown");
} else
- printk(KERN_INFO "CORE_HBA[%d] - Detached Virtual SCSI HBA"
+ pr_debug("CORE_HBA[%d] - Detached Virtual SCSI HBA"
" from Generic Target Core\n", hba->hba_id);
kfree(phv);
@@ -129,22 +110,19 @@ static void pscsi_detach_hba(struct se_hba *hba)
static int pscsi_pmode_enable_hba(struct se_hba *hba, unsigned long mode_flag)
{
- struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)hba->hba_ptr;
+ struct pscsi_hba_virt *phv = hba->hba_ptr;
struct Scsi_Host *sh = phv->phv_lld_host;
- int hba_depth = PSCSI_VIRTUAL_HBA_DEPTH;
/*
* Release the struct Scsi_Host
*/
- if (!(mode_flag)) {
- if (!(sh))
+ if (!mode_flag) {
+ if (!sh)
return 0;
phv->phv_lld_host = NULL;
- phv->phv_mode = PHV_VIRUTAL_HOST_ID;
- atomic_set(&hba->left_queue_depth, hba_depth);
- atomic_set(&hba->max_queue_depth, hba_depth);
+ phv->phv_mode = PHV_VIRTUAL_HOST_ID;
- printk(KERN_INFO "CORE_HBA[%d] - Disabled pSCSI HBA Passthrough"
+ pr_debug("CORE_HBA[%d] - Disabled pSCSI HBA Passthrough"
" %s\n", hba->hba_id, (sh->hostt->name) ?
(sh->hostt->name) : "Unknown");
@@ -155,27 +133,17 @@ static int pscsi_pmode_enable_hba(struct se_hba *hba, unsigned long mode_flag)
* Otherwise, locate struct Scsi_Host from the original passed
* pSCSI Host ID and enable for phba mode
*/
- sh = pscsi_get_sh(phv->phv_host_id);
- if (!(sh)) {
- printk(KERN_ERR "pSCSI: Unable to locate SCSI Host for"
+ sh = scsi_host_lookup(phv->phv_host_id);
+ if (!sh) {
+ pr_err("pSCSI: Unable to locate SCSI Host for"
" phv_host_id: %d\n", phv->phv_host_id);
- return -1;
+ return -EINVAL;
}
- /*
- * Usually the SCSI LLD will use the hostt->can_queue value to define
- * its HBA TCQ depth. Some other drivers (like 2.6 megaraid) don't set
- * this at all and set sh->can_queue at runtime.
- */
- hba_depth = (sh->hostt->can_queue > sh->can_queue) ?
- sh->hostt->can_queue : sh->can_queue;
-
- atomic_set(&hba->left_queue_depth, hba_depth);
- atomic_set(&hba->max_queue_depth, hba_depth);
phv->phv_lld_host = sh;
phv->phv_mode = PHV_LLD_SCSI_HOST_NO;
- printk(KERN_INFO "CORE_HBA[%d] - Enabled pSCSI HBA Passthrough %s\n",
+ pr_debug("CORE_HBA[%d] - Enabled pSCSI HBA Passthrough %s\n",
hba->hba_id, (sh->hostt->name) ? (sh->hostt->name) : "Unknown");
return 1;
@@ -237,7 +205,7 @@ pscsi_get_inquiry_vpd_serial(struct scsi_device *sdev, struct t10_wwn *wwn)
buf = kzalloc(INQUIRY_VPD_SERIAL_LEN, GFP_KERNEL);
if (!buf)
- return -1;
+ return -ENOMEM;
memset(cdb, 0, MAX_COMMAND_SIZE);
cdb[0] = INQUIRY;
@@ -253,14 +221,14 @@ pscsi_get_inquiry_vpd_serial(struct scsi_device *sdev, struct t10_wwn *wwn)
snprintf(&wwn->unit_serial[0], INQUIRY_VPD_SERIAL_LEN, "%s", &buf[4]);
- wwn->t10_sub_dev->su_dev_flags |= SDF_FIRMWARE_VPD_UNIT_SERIAL;
+ wwn->t10_dev->dev_flags |= DF_FIRMWARE_VPD_UNIT_SERIAL;
kfree(buf);
return 0;
out_free:
kfree(buf);
- return -1;
+ return -EPERM;
}
static void
@@ -294,15 +262,15 @@ pscsi_get_inquiry_vpd_device_ident(struct scsi_device *sdev,
page_83 = &buf[off];
ident_len = page_83[3];
if (!ident_len) {
- printk(KERN_ERR "page_83[3]: identifier"
+ pr_err("page_83[3]: identifier"
" length zero!\n");
break;
}
- printk(KERN_INFO "T10 VPD Identifer Length: %d\n", ident_len);
+ pr_debug("T10 VPD Identifier Length: %d\n", ident_len);
vpd = kzalloc(sizeof(struct t10_vpd), GFP_KERNEL);
if (!vpd) {
- printk(KERN_ERR "Unable to allocate memory for"
+ pr_err("Unable to allocate memory for"
" struct t10_vpd\n");
goto out;
}
@@ -333,82 +301,42 @@ out:
kfree(buf);
}
-/* pscsi_add_device_to_list():
- *
- *
- */
-static struct se_device *pscsi_add_device_to_list(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- struct pscsi_dev_virt *pdv,
- struct scsi_device *sd,
- int dev_flags)
+static int pscsi_add_device_to_list(struct se_device *dev,
+ struct scsi_device *sd)
{
- struct se_device *dev;
- struct se_dev_limits dev_limits;
- struct request_queue *q;
- struct queue_limits *limits;
+ struct pscsi_dev_virt *pdv = PSCSI_DEV(dev);
+ struct request_queue *q = sd->request_queue;
- memset(&dev_limits, 0, sizeof(struct se_dev_limits));
+ pdv->pdv_sd = sd;
if (!sd->queue_depth) {
sd->queue_depth = PSCSI_DEFAULT_QUEUEDEPTH;
- printk(KERN_ERR "Set broken SCSI Device %d:%d:%d"
+ pr_err("Set broken SCSI Device %d:%d:%d"
" queue_depth to %d\n", sd->channel, sd->id,
sd->lun, sd->queue_depth);
}
- /*
- * Setup the local scope queue_limits from struct request_queue->limits
- * to pass into transport_add_device_to_core_hba() as struct se_dev_limits.
- */
- q = sd->request_queue;
- limits = &dev_limits.limits;
- limits->logical_block_size = sd->sector_size;
- limits->max_hw_sectors = (sd->host->max_sectors > queue_max_hw_sectors(q)) ?
- queue_max_hw_sectors(q) : sd->host->max_sectors;
- limits->max_sectors = (sd->host->max_sectors > queue_max_sectors(q)) ?
- queue_max_sectors(q) : sd->host->max_sectors;
- dev_limits.hw_queue_depth = sd->queue_depth;
- dev_limits.queue_depth = sd->queue_depth;
+
+ dev->dev_attrib.hw_block_size = sd->sector_size;
+ dev->dev_attrib.hw_max_sectors =
+ min_t(int, sd->host->max_sectors, queue_max_hw_sectors(q));
+ dev->dev_attrib.hw_queue_depth = sd->queue_depth;
+
/*
* Setup our standard INQUIRY info into se_dev->t10_wwn
*/
- pscsi_set_inquiry_info(sd, &se_dev->t10_wwn);
-
- /*
- * Set the pointer pdv->pdv_sd to from passed struct scsi_device,
- * which has already been referenced with Linux SCSI code with
- * scsi_device_get() in this file's pscsi_create_virtdevice().
- *
- * The passthrough operations called by the transport_add_device_*
- * function below will require this pointer to be set for passthroug
- * ops.
- *
- * For the shutdown case in pscsi_free_device(), this struct
- * scsi_device reference is released with Linux SCSI code
- * scsi_device_put() and the pdv->pdv_sd cleared.
- */
- pdv->pdv_sd = sd;
-
- dev = transport_add_device_to_core_hba(hba, &pscsi_template,
- se_dev, dev_flags, (void *)pdv,
- &dev_limits, NULL, NULL);
- if (!(dev)) {
- pdv->pdv_sd = NULL;
- return NULL;
- }
+ pscsi_set_inquiry_info(sd, &dev->t10_wwn);
/*
* Locate VPD WWN Information used for various purposes within
* the Storage Engine.
*/
- if (!pscsi_get_inquiry_vpd_serial(sd, &se_dev->t10_wwn)) {
+ if (!pscsi_get_inquiry_vpd_serial(sd, &dev->t10_wwn)) {
/*
* If VPD Unit Serial returned GOOD status, try
* VPD Device Identification page (0x83).
*/
- pscsi_get_inquiry_vpd_device_ident(sd, &se_dev->t10_wwn);
+ pscsi_get_inquiry_vpd_device_ident(sd, &dev->t10_wwn);
}
/*
@@ -416,201 +344,188 @@ static struct se_device *pscsi_add_device_to_list(
*/
if (sd->type == TYPE_TAPE)
pscsi_tape_read_blocksize(dev, sd);
- return dev;
+ return 0;
}
-static void *pscsi_allocate_virtdevice(struct se_hba *hba, const char *name)
+static struct se_device *pscsi_alloc_device(struct se_hba *hba,
+ const char *name)
{
struct pscsi_dev_virt *pdv;
pdv = kzalloc(sizeof(struct pscsi_dev_virt), GFP_KERNEL);
- if (!(pdv)) {
- printk(KERN_ERR "Unable to allocate memory for struct pscsi_dev_virt\n");
+ if (!pdv) {
+ pr_err("Unable to allocate memory for struct pscsi_dev_virt\n");
return NULL;
}
- pdv->pdv_se_hba = hba;
- printk(KERN_INFO "PSCSI: Allocated pdv: %p for %s\n", pdv, name);
- return (void *)pdv;
+ pr_debug("PSCSI: Allocated pdv: %p for %s\n", pdv, name);
+ return &pdv->dev;
}
/*
* Called with struct Scsi_Host->host_lock called.
*/
-static struct se_device *pscsi_create_type_disk(
- struct scsi_device *sd,
- struct pscsi_dev_virt *pdv,
- struct se_subsystem_dev *se_dev,
- struct se_hba *hba)
+static int pscsi_create_type_disk(struct se_device *dev, struct scsi_device *sd)
+ __releases(sh->host_lock)
{
- struct se_device *dev;
- struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)pdv->pdv_se_hba->hba_ptr;
+ struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr;
+ struct pscsi_dev_virt *pdv = PSCSI_DEV(dev);
struct Scsi_Host *sh = sd->host;
struct block_device *bd;
- u32 dev_flags = 0;
+ int ret;
if (scsi_device_get(sd)) {
- printk(KERN_ERR "scsi_device_get() failed for %d:%d:%d:%d\n",
+ pr_err("scsi_device_get() failed for %d:%d:%d:%d\n",
sh->host_no, sd->channel, sd->id, sd->lun);
spin_unlock_irq(sh->host_lock);
- return NULL;
+ return -EIO;
}
spin_unlock_irq(sh->host_lock);
/*
* Claim exclusive struct block_device access to struct scsi_device
* for TYPE_DISK using supplied udev_path
*/
- bd = blkdev_get_by_path(se_dev->se_dev_udev_path,
+ bd = blkdev_get_by_path(dev->udev_path,
FMODE_WRITE|FMODE_READ|FMODE_EXCL, pdv);
if (IS_ERR(bd)) {
- printk(KERN_ERR "pSCSI: blkdev_get_by_path() failed\n");
+ pr_err("pSCSI: blkdev_get_by_path() failed\n");
scsi_device_put(sd);
- return NULL;
+ return PTR_ERR(bd);
}
pdv->pdv_bd = bd;
- dev = pscsi_add_device_to_list(hba, se_dev, pdv, sd, dev_flags);
- if (!(dev)) {
+ ret = pscsi_add_device_to_list(dev, sd);
+ if (ret) {
blkdev_put(pdv->pdv_bd, FMODE_WRITE|FMODE_READ|FMODE_EXCL);
scsi_device_put(sd);
- return NULL;
+ return ret;
}
- printk(KERN_INFO "CORE_PSCSI[%d] - Added TYPE_DISK for %d:%d:%d:%d\n",
- phv->phv_host_id, sh->host_no, sd->channel, sd->id, sd->lun);
- return dev;
+ pr_debug("CORE_PSCSI[%d] - Added TYPE_DISK for %d:%d:%d:%d\n",
+ phv->phv_host_id, sh->host_no, sd->channel, sd->id, sd->lun);
+ return 0;
}
/*
* Called with struct Scsi_Host->host_lock called.
*/
-static struct se_device *pscsi_create_type_rom(
- struct scsi_device *sd,
- struct pscsi_dev_virt *pdv,
- struct se_subsystem_dev *se_dev,
- struct se_hba *hba)
+static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd)
+ __releases(sh->host_lock)
{
- struct se_device *dev;
- struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)pdv->pdv_se_hba->hba_ptr;
+ struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr;
struct Scsi_Host *sh = sd->host;
- u32 dev_flags = 0;
+ int ret;
if (scsi_device_get(sd)) {
- printk(KERN_ERR "scsi_device_get() failed for %d:%d:%d:%d\n",
+ pr_err("scsi_device_get() failed for %d:%d:%d:%d\n",
sh->host_no, sd->channel, sd->id, sd->lun);
spin_unlock_irq(sh->host_lock);
- return NULL;
+ return -EIO;
}
spin_unlock_irq(sh->host_lock);
- dev = pscsi_add_device_to_list(hba, se_dev, pdv, sd, dev_flags);
- if (!(dev)) {
+ ret = pscsi_add_device_to_list(dev, sd);
+ if (ret) {
scsi_device_put(sd);
- return NULL;
+ return ret;
}
- printk(KERN_INFO "CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%d\n",
+ pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%d\n",
phv->phv_host_id, scsi_device_type(sd->type), sh->host_no,
sd->channel, sd->id, sd->lun);
- return dev;
+ return 0;
}
/*
- *Called with struct Scsi_Host->host_lock called.
+ * Called with struct Scsi_Host->host_lock called.
*/
-static struct se_device *pscsi_create_type_other(
- struct scsi_device *sd,
- struct pscsi_dev_virt *pdv,
- struct se_subsystem_dev *se_dev,
- struct se_hba *hba)
+static int pscsi_create_type_other(struct se_device *dev,
+ struct scsi_device *sd)
+ __releases(sh->host_lock)
{
- struct se_device *dev;
- struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)pdv->pdv_se_hba->hba_ptr;
+ struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr;
struct Scsi_Host *sh = sd->host;
- u32 dev_flags = 0;
+ int ret;
spin_unlock_irq(sh->host_lock);
- dev = pscsi_add_device_to_list(hba, se_dev, pdv, sd, dev_flags);
- if (!(dev))
- return NULL;
+ ret = pscsi_add_device_to_list(dev, sd);
+ if (ret)
+ return ret;
- printk(KERN_INFO "CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%d\n",
+ pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%d\n",
phv->phv_host_id, scsi_device_type(sd->type), sh->host_no,
sd->channel, sd->id, sd->lun);
-
- return dev;
+ return 0;
}
-static struct se_device *pscsi_create_virtdevice(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- void *p)
+static int pscsi_configure_device(struct se_device *dev)
{
- struct pscsi_dev_virt *pdv = (struct pscsi_dev_virt *)p;
- struct se_device *dev;
+ struct se_hba *hba = dev->se_hba;
+ struct pscsi_dev_virt *pdv = PSCSI_DEV(dev);
struct scsi_device *sd;
- struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)hba->hba_ptr;
+ struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr;
struct Scsi_Host *sh = phv->phv_lld_host;
int legacy_mode_enable = 0;
+ int ret;
- if (!(pdv)) {
- printk(KERN_ERR "Unable to locate struct pscsi_dev_virt"
- " parameter\n");
- return NULL;
+ if (!(pdv->pdv_flags & PDF_HAS_CHANNEL_ID) ||
+ !(pdv->pdv_flags & PDF_HAS_TARGET_ID) ||
+ !(pdv->pdv_flags & PDF_HAS_LUN_ID)) {
+ pr_err("Missing scsi_channel_id=, scsi_target_id= and"
+ " scsi_lun_id= parameters\n");
+ return -EINVAL;
}
+
/*
* If not running in PHV_LLD_SCSI_HOST_NO mode, locate the
* struct Scsi_Host we will need to bring the TCM/pSCSI object online
*/
- if (!(sh)) {
+ if (!sh) {
if (phv->phv_mode == PHV_LLD_SCSI_HOST_NO) {
- printk(KERN_ERR "pSCSI: Unable to locate struct"
+ pr_err("pSCSI: Unable to locate struct"
" Scsi_Host for PHV_LLD_SCSI_HOST_NO\n");
- return NULL;
+ return -ENODEV;
}
/*
- * For the newer PHV_VIRUTAL_HOST_ID struct scsi_device
+ * For the newer PHV_VIRTUAL_HOST_ID struct scsi_device
* reference, we enforce that udev_path has been set
*/
- if (!(se_dev->su_dev_flags & SDF_USING_UDEV_PATH)) {
- printk(KERN_ERR "pSCSI: udev_path attribute has not"
+ if (!(dev->dev_flags & DF_USING_UDEV_PATH)) {
+ pr_err("pSCSI: udev_path attribute has not"
" been set before ENABLE=1\n");
- return NULL;
+ return -EINVAL;
}
/*
- * If no scsi_host_id= was passed for PHV_VIRUTAL_HOST_ID,
+ * If no scsi_host_id= was passed for PHV_VIRTUAL_HOST_ID,
* use the original TCM hba ID to reference Linux/SCSI Host No
* and enable for PHV_LLD_SCSI_HOST_NO mode.
*/
if (!(pdv->pdv_flags & PDF_HAS_VIRT_HOST_ID)) {
- spin_lock(&hba->device_lock);
- if (!(list_empty(&hba->hba_dev_list))) {
- printk(KERN_ERR "pSCSI: Unable to set hba_mode"
+ if (hba->dev_count) {
+ pr_err("pSCSI: Unable to set hba_mode"
" with active devices\n");
- spin_unlock(&hba->device_lock);
- return NULL;
+ return -EEXIST;
}
- spin_unlock(&hba->device_lock);
if (pscsi_pmode_enable_hba(hba, 1) != 1)
- return NULL;
+ return -ENODEV;
legacy_mode_enable = 1;
hba->hba_flags |= HBA_FLAGS_PSCSI_MODE;
sh = phv->phv_lld_host;
} else {
- sh = pscsi_get_sh(pdv->pdv_host_id);
- if (!(sh)) {
- printk(KERN_ERR "pSCSI: Unable to locate"
+ sh = scsi_host_lookup(pdv->pdv_host_id);
+ if (!sh) {
+ pr_err("pSCSI: Unable to locate"
" pdv_host_id: %d\n", pdv->pdv_host_id);
- return NULL;
+ return -EINVAL;
}
}
} else {
- if (phv->phv_mode == PHV_VIRUTAL_HOST_ID) {
- printk(KERN_ERR "pSCSI: PHV_VIRUTAL_HOST_ID set while"
+ if (phv->phv_mode == PHV_VIRTUAL_HOST_ID) {
+ pr_err("pSCSI: PHV_VIRTUAL_HOST_ID set while"
" struct Scsi_Host exists\n");
- return NULL;
+ return -EEXIST;
}
}
@@ -627,51 +542,47 @@ static struct se_device *pscsi_create_virtdevice(
*/
switch (sd->type) {
case TYPE_DISK:
- dev = pscsi_create_type_disk(sd, pdv, se_dev, hba);
+ ret = pscsi_create_type_disk(dev, sd);
break;
case TYPE_ROM:
- dev = pscsi_create_type_rom(sd, pdv, se_dev, hba);
+ ret = pscsi_create_type_rom(dev, sd);
break;
default:
- dev = pscsi_create_type_other(sd, pdv, se_dev, hba);
+ ret = pscsi_create_type_other(dev, sd);
break;
}
- if (!(dev)) {
- if (phv->phv_mode == PHV_VIRUTAL_HOST_ID)
+ if (ret) {
+ if (phv->phv_mode == PHV_VIRTUAL_HOST_ID)
scsi_host_put(sh);
else if (legacy_mode_enable) {
pscsi_pmode_enable_hba(hba, 0);
hba->hba_flags &= ~HBA_FLAGS_PSCSI_MODE;
}
pdv->pdv_sd = NULL;
- return NULL;
+ return ret;
}
- return dev;
+ return 0;
}
spin_unlock_irq(sh->host_lock);
- printk(KERN_ERR "pSCSI: Unable to locate %d:%d:%d:%d\n", sh->host_no,
+ pr_err("pSCSI: Unable to locate %d:%d:%d:%d\n", sh->host_no,
pdv->pdv_channel_id, pdv->pdv_target_id, pdv->pdv_lun_id);
- if (phv->phv_mode == PHV_VIRUTAL_HOST_ID)
+ if (phv->phv_mode == PHV_VIRTUAL_HOST_ID)
scsi_host_put(sh);
else if (legacy_mode_enable) {
pscsi_pmode_enable_hba(hba, 0);
hba->hba_flags &= ~HBA_FLAGS_PSCSI_MODE;
}
- return NULL;
+ return -ENODEV;
}
-/* pscsi_free_device(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static void pscsi_free_device(void *p)
+static void pscsi_free_device(struct se_device *dev)
{
- struct pscsi_dev_virt *pdv = p;
- struct pscsi_hba_virt *phv = pdv->pdv_se_hba->hba_ptr;
+ struct pscsi_dev_virt *pdv = PSCSI_DEV(dev);
+ struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr;
struct scsi_device *sd = pdv->pdv_sd;
if (sd) {
@@ -701,38 +612,38 @@ static void pscsi_free_device(void *p)
kfree(pdv);
}
-static inline struct pscsi_plugin_task *PSCSI_TASK(struct se_task *task)
-{
- return container_of(task, struct pscsi_plugin_task, pscsi_task);
-}
-
-
-/* pscsi_transport_complete():
- *
- *
- */
-static int pscsi_transport_complete(struct se_task *task)
+static void pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg,
+ unsigned char *sense_buffer)
{
- struct pscsi_dev_virt *pdv = task->se_dev->dev_ptr;
+ struct pscsi_dev_virt *pdv = PSCSI_DEV(cmd->se_dev);
struct scsi_device *sd = pdv->pdv_sd;
int result;
- struct pscsi_plugin_task *pt = PSCSI_TASK(task);
- unsigned char *cdb = &pt->pscsi_cdb[0];
+ struct pscsi_plugin_task *pt = cmd->priv;
+ unsigned char *cdb;
+ /*
+ * Special case for REPORT_LUNs handling where pscsi_plugin_task has
+ * not been allocated because TCM is handling the emulation directly.
+ */
+ if (!pt)
+ return;
+ cdb = &pt->pscsi_cdb[0];
result = pt->pscsi_result;
/*
* Hack to make sure that Write-Protect modepage is set if R/O mode is
* forced.
*/
+ if (!cmd->se_deve || !cmd->data_length)
+ goto after_mode_sense;
+
if (((cdb[0] == MODE_SENSE) || (cdb[0] == MODE_SENSE_10)) &&
(status_byte(result) << 1) == SAM_STAT_GOOD) {
- if (!TASK_CMD(task)->se_deve)
- goto after_mode_sense;
+ if (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY) {
+ unsigned char *buf;
- if (TASK_CMD(task)->se_deve->lun_flags &
- TRANSPORT_LUNFLAGS_READ_ONLY) {
- unsigned char *buf = (unsigned char *)
- T_TASK(task->task_se_cmd)->t_task_buf;
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ ; /* XXX: TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE */
if (cdb[0] == MODE_SENSE_10) {
if (!(buf[3] & 0x80))
@@ -741,11 +652,13 @@ static int pscsi_transport_complete(struct se_task *task)
if (!(buf[2] & 0x80))
buf[2] |= 0x80;
}
+
+ transport_kunmap_data_sg(cmd);
}
}
after_mode_sense:
- if (sd->type != TYPE_TAPE)
+ if (sd->type != TYPE_TAPE || !cmd->data_length)
goto after_mode_select;
/*
@@ -759,13 +672,12 @@ after_mode_sense:
if (((cdb[0] == MODE_SELECT) || (cdb[0] == MODE_SELECT_10)) &&
(status_byte(result) << 1) == SAM_STAT_GOOD) {
unsigned char *buf;
- struct scatterlist *sg = task->task_sg;
u16 bdl;
u32 blocksize;
buf = sg_virt(&sg[0]);
- if (!(buf)) {
- printk(KERN_ERR "Unable to get buf for scatterlist\n");
+ if (!buf) {
+ pr_err("Unable to get buf for scatterlist\n");
goto after_mode_select;
}
@@ -788,148 +700,10 @@ after_mode_sense:
}
after_mode_select:
- if (status_byte(result) & CHECK_CONDITION)
- return 1;
-
- return 0;
-}
-
-static struct se_task *
-pscsi_alloc_task(struct se_cmd *cmd)
-{
- struct pscsi_plugin_task *pt;
- unsigned char *cdb = T_TASK(cmd)->t_task_cdb;
-
- pt = kzalloc(sizeof(struct pscsi_plugin_task), GFP_KERNEL);
- if (!pt) {
- printk(KERN_ERR "Unable to allocate struct pscsi_plugin_task\n");
- return NULL;
+ if (sense_buffer && (status_byte(result) & CHECK_CONDITION)) {
+ memcpy(sense_buffer, pt->pscsi_sense, TRANSPORT_SENSE_BUFFER);
+ cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE;
}
-
- /*
- * If TCM Core is signaling a > TCM_MAX_COMMAND_SIZE allocation,
- * allocate the extended CDB buffer for per struct se_task context
- * pt->pscsi_cdb now.
- */
- if (T_TASK(cmd)->t_task_cdb != T_TASK(cmd)->__t_task_cdb) {
-
- pt->pscsi_cdb = kzalloc(scsi_command_size(cdb), GFP_KERNEL);
- if (!(pt->pscsi_cdb)) {
- printk(KERN_ERR "pSCSI: Unable to allocate extended"
- " pt->pscsi_cdb\n");
- return NULL;
- }
- } else
- pt->pscsi_cdb = &pt->__pscsi_cdb[0];
-
- return &pt->pscsi_task;
-}
-
-static inline void pscsi_blk_init_request(
- struct se_task *task,
- struct pscsi_plugin_task *pt,
- struct request *req,
- int bidi_read)
-{
- /*
- * Defined as "scsi command" in include/linux/blkdev.h.
- */
- req->cmd_type = REQ_TYPE_BLOCK_PC;
- /*
- * For the extra BIDI-COMMAND READ struct request we do not
- * need to setup the remaining structure members
- */
- if (bidi_read)
- return;
- /*
- * Setup the done function pointer for struct request,
- * also set the end_io_data pointer.to struct se_task.
- */
- req->end_io = pscsi_req_done;
- req->end_io_data = (void *)task;
- /*
- * Load the referenced struct se_task's SCSI CDB into
- * include/linux/blkdev.h:struct request->cmd
- */
- req->cmd_len = scsi_command_size(pt->pscsi_cdb);
- req->cmd = &pt->pscsi_cdb[0];
- /*
- * Setup pointer for outgoing sense data.
- */
- req->sense = (void *)&pt->pscsi_sense[0];
- req->sense_len = 0;
-}
-
-/*
- * Used for pSCSI data payloads for all *NON* SCF_SCSI_DATA_SG_IO_CDB
-*/
-static int pscsi_blk_get_request(struct se_task *task)
-{
- struct pscsi_plugin_task *pt = PSCSI_TASK(task);
- struct pscsi_dev_virt *pdv = task->se_dev->dev_ptr;
-
- pt->pscsi_req = blk_get_request(pdv->pdv_sd->request_queue,
- (task->task_data_direction == DMA_TO_DEVICE),
- GFP_KERNEL);
- if (!(pt->pscsi_req) || IS_ERR(pt->pscsi_req)) {
- printk(KERN_ERR "PSCSI: blk_get_request() failed: %ld\n",
- IS_ERR(pt->pscsi_req));
- return PYX_TRANSPORT_LU_COMM_FAILURE;
- }
- /*
- * Setup the newly allocated struct request for REQ_TYPE_BLOCK_PC,
- * and setup rq callback, CDB and sense.
- */
- pscsi_blk_init_request(task, pt, pt->pscsi_req, 0);
- return 0;
-}
-
-/* pscsi_do_task(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static int pscsi_do_task(struct se_task *task)
-{
- struct pscsi_plugin_task *pt = PSCSI_TASK(task);
- struct pscsi_dev_virt *pdv = task->se_dev->dev_ptr;
- /*
- * Set the struct request->timeout value based on peripheral
- * device type from SCSI.
- */
- if (pdv->pdv_sd->type == TYPE_DISK)
- pt->pscsi_req->timeout = PS_TIMEOUT_DISK;
- else
- pt->pscsi_req->timeout = PS_TIMEOUT_OTHER;
-
- pt->pscsi_req->retries = PS_RETRY;
- /*
- * Queue the struct request into the struct scsi_device->request_queue.
- * Also check for HEAD_OF_QUEUE SAM TASK attr from received se_cmd
- * descriptor
- */
- blk_execute_rq_nowait(pdv->pdv_sd->request_queue, NULL, pt->pscsi_req,
- (task->task_se_cmd->sam_task_attr == TASK_ATTR_HOQ),
- pscsi_req_done);
-
- return PYX_TRANSPORT_SENT_TO_TRANSPORT;
-}
-
-static void pscsi_free_task(struct se_task *task)
-{
- struct pscsi_plugin_task *pt = PSCSI_TASK(task);
- struct se_cmd *cmd = task->task_se_cmd;
-
- /*
- * Release the extended CDB allocation from pscsi_alloc_task()
- * if one exists.
- */
- if (T_TASK(cmd)->t_task_cdb != T_TASK(cmd)->__t_task_cdb)
- kfree(pt->pscsi_cdb);
- /*
- * We do not release the bio(s) here associated with this task, as
- * this is handled by bio_put() and pscsi_bi_endio().
- */
- kfree(pt);
}
enum {
@@ -945,13 +719,11 @@ static match_table_t tokens = {
{Opt_err, NULL}
};
-static ssize_t pscsi_set_configfs_dev_params(struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- const char *page,
- ssize_t count)
+static ssize_t pscsi_set_configfs_dev_params(struct se_device *dev,
+ const char *page, ssize_t count)
{
- struct pscsi_dev_virt *pdv = se_dev->se_dev_su_ptr;
- struct pscsi_hba_virt *phv = hba->hba_ptr;
+ struct pscsi_dev_virt *pdv = PSCSI_DEV(dev);
+ struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr;
char *orig, *ptr, *opts;
substring_t args[MAX_OPT_ARGS];
int ret = 0, arg, token;
@@ -962,7 +734,7 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_hba *hba,
orig = opts;
- while ((ptr = strsep(&opts, ",")) != NULL) {
+ while ((ptr = strsep(&opts, ",\n")) != NULL) {
if (!*ptr)
continue;
@@ -970,7 +742,7 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_hba *hba,
switch (token) {
case Opt_scsi_host_id:
if (phv->phv_mode == PHV_LLD_SCSI_HOST_NO) {
- printk(KERN_ERR "PSCSI[%d]: Unable to accept"
+ pr_err("PSCSI[%d]: Unable to accept"
" scsi_host_id while phv_mode =="
" PHV_LLD_SCSI_HOST_NO\n",
phv->phv_host_id);
@@ -979,14 +751,14 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_hba *hba,
}
match_int(args, &arg);
pdv->pdv_host_id = arg;
- printk(KERN_INFO "PSCSI[%d]: Referencing SCSI Host ID:"
+ pr_debug("PSCSI[%d]: Referencing SCSI Host ID:"
" %d\n", phv->phv_host_id, pdv->pdv_host_id);
pdv->pdv_flags |= PDF_HAS_VIRT_HOST_ID;
break;
case Opt_scsi_channel_id:
match_int(args, &arg);
pdv->pdv_channel_id = arg;
- printk(KERN_INFO "PSCSI[%d]: Referencing SCSI Channel"
+ pr_debug("PSCSI[%d]: Referencing SCSI Channel"
" ID: %d\n", phv->phv_host_id,
pdv->pdv_channel_id);
pdv->pdv_flags |= PDF_HAS_CHANNEL_ID;
@@ -994,7 +766,7 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_hba *hba,
case Opt_scsi_target_id:
match_int(args, &arg);
pdv->pdv_target_id = arg;
- printk(KERN_INFO "PSCSI[%d]: Referencing SCSI Target"
+ pr_debug("PSCSI[%d]: Referencing SCSI Target"
" ID: %d\n", phv->phv_host_id,
pdv->pdv_target_id);
pdv->pdv_flags |= PDF_HAS_TARGET_ID;
@@ -1002,7 +774,7 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_hba *hba,
case Opt_scsi_lun_id:
match_int(args, &arg);
pdv->pdv_lun_id = arg;
- printk(KERN_INFO "PSCSI[%d]: Referencing SCSI LUN ID:"
+ pr_debug("PSCSI[%d]: Referencing SCSI LUN ID:"
" %d\n", phv->phv_host_id, pdv->pdv_lun_id);
pdv->pdv_flags |= PDF_HAS_LUN_ID;
break;
@@ -1016,35 +788,16 @@ out:
return (!ret) ? count : ret;
}
-static ssize_t pscsi_check_configfs_dev_params(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev)
-{
- struct pscsi_dev_virt *pdv = se_dev->se_dev_su_ptr;
-
- if (!(pdv->pdv_flags & PDF_HAS_CHANNEL_ID) ||
- !(pdv->pdv_flags & PDF_HAS_TARGET_ID) ||
- !(pdv->pdv_flags & PDF_HAS_LUN_ID)) {
- printk(KERN_ERR "Missing scsi_channel_id=, scsi_target_id= and"
- " scsi_lun_id= parameters\n");
- return -1;
- }
-
- return 0;
-}
-
-static ssize_t pscsi_show_configfs_dev_params(struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- char *b)
+static ssize_t pscsi_show_configfs_dev_params(struct se_device *dev, char *b)
{
- struct pscsi_hba_virt *phv = hba->hba_ptr;
- struct pscsi_dev_virt *pdv = se_dev->se_dev_su_ptr;
+ struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr;
+ struct pscsi_dev_virt *pdv = PSCSI_DEV(dev);
struct scsi_device *sd = pdv->pdv_sd;
unsigned char host_id[16];
ssize_t bl;
int i;
- if (phv->phv_mode == PHV_VIRUTAL_HOST_ID)
+ if (phv->phv_mode == PHV_VIRTUAL_HOST_ID)
snprintf(host_id, 16, "%d", pdv->pdv_host_id);
else
snprintf(host_id, 16, "PHBA Mode");
@@ -1087,16 +840,16 @@ static void pscsi_bi_endio(struct bio *bio, int error)
bio_put(bio);
}
-static inline struct bio *pscsi_get_bio(struct pscsi_dev_virt *pdv, int sg_num)
+static inline struct bio *pscsi_get_bio(int nr_vecs)
{
struct bio *bio;
/*
* Use bio_malloc() following the comment in for bio -> struct request
* in block/blk-core.c:blk_make_request()
*/
- bio = bio_kmalloc(GFP_KERNEL, sg_num);
- if (!(bio)) {
- printk(KERN_ERR "PSCSI: bio_kmalloc() failed\n");
+ bio = bio_kmalloc(GFP_KERNEL, nr_vecs);
+ if (!bio) {
+ pr_err("PSCSI: bio_kmalloc() failed\n");
return NULL;
}
bio->bi_end_io = pscsi_bi_endio;
@@ -1104,85 +857,72 @@ static inline struct bio *pscsi_get_bio(struct pscsi_dev_virt *pdv, int sg_num)
return bio;
}
-#if 0
-#define DEBUG_PSCSI(x...) printk(x)
-#else
-#define DEBUG_PSCSI(x...)
-#endif
-
-static int __pscsi_map_task_SG(
- struct se_task *task,
- struct scatterlist *task_sg,
- u32 task_sg_num,
- int bidi_read)
+static sense_reason_t
+pscsi_map_sg(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
+ enum dma_data_direction data_direction, struct bio **hbio)
{
- struct pscsi_plugin_task *pt = PSCSI_TASK(task);
- struct pscsi_dev_virt *pdv = task->se_dev->dev_ptr;
- struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ struct pscsi_dev_virt *pdv = PSCSI_DEV(cmd->se_dev);
+ struct bio *bio = NULL, *tbio = NULL;
struct page *page;
struct scatterlist *sg;
- u32 data_len = task->task_size, i, len, bytes, off;
- int nr_pages = (task->task_size + task_sg[0].offset +
+ u32 data_len = cmd->data_length, i, len, bytes, off;
+ int nr_pages = (cmd->data_length + sgl[0].offset +
PAGE_SIZE - 1) >> PAGE_SHIFT;
- int nr_vecs = 0, rc, ret = PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES;
- int rw = (task->task_data_direction == DMA_TO_DEVICE);
+ int nr_vecs = 0, rc;
+ int rw = (data_direction == DMA_TO_DEVICE);
- if (!task->task_size)
- return 0;
- /*
- * For SCF_SCSI_DATA_SG_IO_CDB, Use fs/bio.c:bio_add_page() to setup
- * the bio_vec maplist from TC< struct se_mem -> task->task_sg ->
- * struct scatterlist memory. The struct se_task->task_sg[] currently needs
- * to be attached to struct bios for submission to Linux/SCSI using
- * struct request to struct scsi_device->request_queue.
- *
- * Note that this will be changing post v2.6.28 as Target_Core_Mod/pSCSI
- * is ported to upstream SCSI passthrough functionality that accepts
- * struct scatterlist->page_link or struct page as a paraemeter.
- */
- DEBUG_PSCSI("PSCSI: nr_pages: %d\n", nr_pages);
+ *hbio = NULL;
+
+ pr_debug("PSCSI: nr_pages: %d\n", nr_pages);
- for_each_sg(task_sg, sg, task_sg_num, i) {
+ for_each_sg(sgl, sg, sgl_nents, i) {
page = sg_page(sg);
off = sg->offset;
len = sg->length;
- DEBUG_PSCSI("PSCSI: i: %d page: %p len: %d off: %d\n", i,
+ pr_debug("PSCSI: i: %d page: %p len: %d off: %d\n", i,
page, len, off);
- while (len > 0 && data_len > 0) {
+ /*
+ * We only have one page of data in each sg element,
+ * we can not cross a page boundary.
+ */
+ if (off + len > PAGE_SIZE)
+ goto fail;
+
+ if (len > 0 && data_len > 0) {
bytes = min_t(unsigned int, len, PAGE_SIZE - off);
bytes = min(bytes, data_len);
- if (!(bio)) {
+ if (!bio) {
nr_vecs = min_t(int, BIO_MAX_PAGES, nr_pages);
nr_pages -= nr_vecs;
/*
* Calls bio_kmalloc() and sets bio->bi_end_io()
*/
- bio = pscsi_get_bio(pdv, nr_vecs);
- if (!(bio))
+ bio = pscsi_get_bio(nr_vecs);
+ if (!bio)
goto fail;
if (rw)
bio->bi_rw |= REQ_WRITE;
- DEBUG_PSCSI("PSCSI: Allocated bio: %p,"
+ pr_debug("PSCSI: Allocated bio: %p,"
" dir: %s nr_vecs: %d\n", bio,
(rw) ? "rw" : "r", nr_vecs);
/*
* Set *hbio pointer to handle the case:
* nr_pages > BIO_MAX_PAGES, where additional
* bios need to be added to complete a given
- * struct se_task
+ * command.
*/
- if (!hbio)
- hbio = tbio = bio;
+ if (!*hbio)
+ *hbio = tbio = bio;
else
tbio = tbio->bi_next = bio;
}
- DEBUG_PSCSI("PSCSI: Calling bio_add_pc_page() i: %d"
+ pr_debug("PSCSI: Calling bio_add_pc_page() i: %d"
" bio: %p page: %p len: %d off: %d\n", i, bio,
page, len, off);
@@ -1191,11 +931,11 @@ static int __pscsi_map_task_SG(
if (rc != bytes)
goto fail;
- DEBUG_PSCSI("PSCSI: bio->bi_vcnt: %d nr_vecs: %d\n",
+ pr_debug("PSCSI: bio->bi_vcnt: %d nr_vecs: %d\n",
bio->bi_vcnt, nr_vecs);
if (bio->bi_vcnt > nr_vecs) {
- DEBUG_PSCSI("PSCSI: Reached bio->bi_vcnt max:"
+ pr_debug("PSCSI: Reached bio->bi_vcnt max:"
" %d i: %d bio: %p, allocating another"
" bio\n", bio->bi_vcnt, i, bio);
/*
@@ -1207,145 +947,160 @@ static int __pscsi_map_task_SG(
bio = NULL;
}
- page++;
- len -= bytes;
data_len -= bytes;
- off = 0;
}
}
- /*
- * Setup the primary pt->pscsi_req used for non BIDI and BIDI-COMMAND
- * primary SCSI WRITE poayload mapped for struct se_task->task_sg[]
- */
- if (!(bidi_read)) {
- /*
- * Starting with v2.6.31, call blk_make_request() passing in *hbio to
- * allocate the pSCSI task a struct request.
- */
- pt->pscsi_req = blk_make_request(pdv->pdv_sd->request_queue,
- hbio, GFP_KERNEL);
- if (!(pt->pscsi_req)) {
- printk(KERN_ERR "pSCSI: blk_make_request() failed\n");
- goto fail;
- }
- /*
- * Setup the newly allocated struct request for REQ_TYPE_BLOCK_PC,
- * and setup rq callback, CDB and sense.
- */
- pscsi_blk_init_request(task, pt, pt->pscsi_req, 0);
-
- return task->task_sg_num;
- }
- /*
- * Setup the secondary pt->pscsi_req->next_rq used for the extra BIDI-COMMAND
- * SCSI READ paylaod mapped for struct se_task->task_sg_bidi[]
- */
- pt->pscsi_req->next_rq = blk_make_request(pdv->pdv_sd->request_queue,
- hbio, GFP_KERNEL);
- if (!(pt->pscsi_req->next_rq)) {
- printk(KERN_ERR "pSCSI: blk_make_request() failed for BIDI\n");
- goto fail;
- }
- pscsi_blk_init_request(task, pt, pt->pscsi_req->next_rq, 1);
- return task->task_sg_num;
+ return 0;
fail:
- while (hbio) {
- bio = hbio;
- hbio = hbio->bi_next;
- bio->bi_next = NULL;
- bio_endio(bio, 0);
+ while (*hbio) {
+ bio = *hbio;
+ *hbio = (*hbio)->bi_next;
+ bio_endio(bio, 0); /* XXX: should be error */
}
- return ret;
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
-static int pscsi_map_task_SG(struct se_task *task)
+/*
+ * Clear a lun set in the cdb if the initiator talking to use spoke
+ * and old standards version, as we can't assume the underlying device
+ * won't choke up on it.
+ */
+static inline void pscsi_clear_cdb_lun(unsigned char *cdb)
{
- int ret;
-
- /*
- * Setup the main struct request for the task->task_sg[] payload
- */
-
- ret = __pscsi_map_task_SG(task, task->task_sg, task->task_sg_num, 0);
- if (ret >= 0 && task->task_sg_bidi) {
- /*
- * If present, set up the extra BIDI-COMMAND SCSI READ
- * struct request and payload.
- */
- ret = __pscsi_map_task_SG(task, task->task_sg_bidi,
- task->task_sg_num, 1);
+ switch (cdb[0]) {
+ case READ_10: /* SBC - RDProtect */
+ case READ_12: /* SBC - RDProtect */
+ case READ_16: /* SBC - RDProtect */
+ case SEND_DIAGNOSTIC: /* SPC - SELF-TEST Code */
+ case VERIFY: /* SBC - VRProtect */
+ case VERIFY_16: /* SBC - VRProtect */
+ case WRITE_VERIFY: /* SBC - VRProtect */
+ case WRITE_VERIFY_12: /* SBC - VRProtect */
+ case MAINTENANCE_IN: /* SPC - Parameter Data Format for SA RTPG */
+ break;
+ default:
+ cdb[1] &= 0x1f; /* clear logical unit number */
+ break;
}
-
- if (ret < 0)
- return PYX_TRANSPORT_LU_COMM_FAILURE;
- return 0;
}
-/* pscsi_map_task_non_SG():
- *
- *
- */
-static int pscsi_map_task_non_SG(struct se_task *task)
+static sense_reason_t
+pscsi_parse_cdb(struct se_cmd *cmd)
{
- struct se_cmd *cmd = TASK_CMD(task);
- struct pscsi_plugin_task *pt = PSCSI_TASK(task);
- struct pscsi_dev_virt *pdv = task->se_dev->dev_ptr;
- int ret = 0;
+ unsigned char *cdb = cmd->t_task_cdb;
- if (pscsi_blk_get_request(task) < 0)
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ if (cmd->se_cmd_flags & SCF_BIDI)
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
- if (!task->task_size)
- return 0;
+ pscsi_clear_cdb_lun(cdb);
- ret = blk_rq_map_kern(pdv->pdv_sd->request_queue,
- pt->pscsi_req, T_TASK(cmd)->t_task_buf,
- task->task_size, GFP_KERNEL);
- if (ret < 0) {
- printk(KERN_ERR "PSCSI: blk_rq_map_kern() failed: %d\n", ret);
- return PYX_TRANSPORT_LU_COMM_FAILURE;
+ /*
+ * For REPORT LUNS we always need to emulate the response, for everything
+ * else the default for pSCSI is to pass the command to the underlying
+ * LLD / physical hardware.
+ */
+ switch (cdb[0]) {
+ case REPORT_LUNS:
+ cmd->execute_cmd = spc_emulate_report_luns;
+ return 0;
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ case WRITE_VERIFY:
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+ /* FALLTHROUGH*/
+ default:
+ cmd->execute_cmd = pscsi_execute_cmd;
+ return 0;
}
- return 0;
}
-static int pscsi_CDB_none(struct se_task *task)
+static sense_reason_t
+pscsi_execute_cmd(struct se_cmd *cmd)
{
- return pscsi_blk_get_request(task);
-}
+ struct scatterlist *sgl = cmd->t_data_sg;
+ u32 sgl_nents = cmd->t_data_nents;
+ enum dma_data_direction data_direction = cmd->data_direction;
+ struct pscsi_dev_virt *pdv = PSCSI_DEV(cmd->se_dev);
+ struct pscsi_plugin_task *pt;
+ struct request *req;
+ struct bio *hbio;
+ sense_reason_t ret;
-/* pscsi_get_cdb():
- *
- *
- */
-static unsigned char *pscsi_get_cdb(struct se_task *task)
-{
- struct pscsi_plugin_task *pt = PSCSI_TASK(task);
+ /*
+ * Dynamically alloc cdb space, since it may be larger than
+ * TCM_MAX_COMMAND_SIZE
+ */
+ pt = kzalloc(sizeof(*pt) + scsi_command_size(cmd->t_task_cdb), GFP_KERNEL);
+ if (!pt) {
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+ cmd->priv = pt;
+
+ memcpy(pt->pscsi_cdb, cmd->t_task_cdb,
+ scsi_command_size(cmd->t_task_cdb));
+
+ if (!sgl) {
+ req = blk_get_request(pdv->pdv_sd->request_queue,
+ (data_direction == DMA_TO_DEVICE),
+ GFP_KERNEL);
+ if (!req) {
+ pr_err("PSCSI: blk_get_request() failed\n");
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ goto fail;
+ }
- return pt->pscsi_cdb;
-}
+ blk_rq_set_block_pc(req);
+ } else {
+ BUG_ON(!cmd->data_length);
-/* pscsi_get_sense_buffer():
- *
- *
- */
-static unsigned char *pscsi_get_sense_buffer(struct se_task *task)
-{
- struct pscsi_plugin_task *pt = PSCSI_TASK(task);
+ ret = pscsi_map_sg(cmd, sgl, sgl_nents, data_direction, &hbio);
+ if (ret)
+ goto fail;
- return (unsigned char *)&pt->pscsi_sense[0];
-}
+ req = blk_make_request(pdv->pdv_sd->request_queue, hbio,
+ GFP_KERNEL);
+ if (IS_ERR(req)) {
+ pr_err("pSCSI: blk_make_request() failed\n");
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ goto fail_free_bio;
+ }
+ }
-/* pscsi_get_device_rev():
- *
- *
- */
-static u32 pscsi_get_device_rev(struct se_device *dev)
-{
- struct pscsi_dev_virt *pdv = dev->dev_ptr;
- struct scsi_device *sd = pdv->pdv_sd;
+ req->end_io = pscsi_req_done;
+ req->end_io_data = cmd;
+ req->cmd_len = scsi_command_size(pt->pscsi_cdb);
+ req->cmd = &pt->pscsi_cdb[0];
+ req->sense = &pt->pscsi_sense[0];
+ req->sense_len = 0;
+ if (pdv->pdv_sd->type == TYPE_DISK)
+ req->timeout = PS_TIMEOUT_DISK;
+ else
+ req->timeout = PS_TIMEOUT_OTHER;
+ req->retries = PS_RETRY;
+
+ blk_execute_rq_nowait(pdv->pdv_sd->request_queue, NULL, req,
+ (cmd->sam_task_attr == MSG_HEAD_TAG),
+ pscsi_req_done);
- return (sd->scsi_level - 1) ? sd->scsi_level - 1 : 1;
+ return 0;
+
+fail_free_bio:
+ while (hbio) {
+ struct bio *bio = hbio;
+ hbio = hbio->bi_next;
+ bio_endio(bio, 0); /* XXX: should be error */
+ }
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+fail:
+ kfree(pt);
+ return ret;
}
/* pscsi_get_device_type():
@@ -1354,7 +1109,7 @@ static u32 pscsi_get_device_rev(struct se_device *dev)
*/
static u32 pscsi_get_device_type(struct se_device *dev)
{
- struct pscsi_dev_virt *pdv = dev->dev_ptr;
+ struct pscsi_dev_virt *pdv = PSCSI_DEV(dev);
struct scsi_device *sd = pdv->pdv_sd;
return sd->type;
@@ -1362,7 +1117,7 @@ static u32 pscsi_get_device_type(struct se_device *dev)
static sector_t pscsi_get_blocks(struct se_device *dev)
{
- struct pscsi_dev_virt *pdv = dev->dev_ptr;
+ struct pscsi_dev_virt *pdv = PSCSI_DEV(dev);
if (pdv->pdv_bd && pdv->pdv_bd->bd_part)
return pdv->pdv_bd->bd_part->nr_sects;
@@ -1371,83 +1126,51 @@ static sector_t pscsi_get_blocks(struct se_device *dev)
return 0;
}
-/* pscsi_handle_SAM_STATUS_failures():
- *
- *
- */
-static inline void pscsi_process_SAM_status(
- struct se_task *task,
- struct pscsi_plugin_task *pt)
+static void pscsi_req_done(struct request *req, int uptodate)
{
- task->task_scsi_status = status_byte(pt->pscsi_result);
- if ((task->task_scsi_status)) {
- task->task_scsi_status <<= 1;
- printk(KERN_INFO "PSCSI Status Byte exception at task: %p CDB:"
- " 0x%02x Result: 0x%08x\n", task, pt->pscsi_cdb[0],
+ struct se_cmd *cmd = req->end_io_data;
+ struct pscsi_plugin_task *pt = cmd->priv;
+
+ pt->pscsi_result = req->errors;
+ pt->pscsi_resid = req->resid_len;
+
+ cmd->scsi_status = status_byte(pt->pscsi_result) << 1;
+ if (cmd->scsi_status) {
+ pr_debug("PSCSI Status Byte exception at cmd: %p CDB:"
+ " 0x%02x Result: 0x%08x\n", cmd, pt->pscsi_cdb[0],
pt->pscsi_result);
}
switch (host_byte(pt->pscsi_result)) {
case DID_OK:
- transport_complete_task(task, (!task->task_scsi_status));
+ target_complete_cmd(cmd, cmd->scsi_status);
break;
default:
- printk(KERN_INFO "PSCSI Host Byte exception at task: %p CDB:"
- " 0x%02x Result: 0x%08x\n", task, pt->pscsi_cdb[0],
+ pr_debug("PSCSI Host Byte exception at cmd: %p CDB:"
+ " 0x%02x Result: 0x%08x\n", cmd, pt->pscsi_cdb[0],
pt->pscsi_result);
- task->task_scsi_status = SAM_STAT_CHECK_CONDITION;
- task->task_error_status = PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
- TASK_CMD(task)->transport_error_status =
- PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
- transport_complete_task(task, 0);
+ target_complete_cmd(cmd, SAM_STAT_CHECK_CONDITION);
break;
}
- return;
-}
-
-static void pscsi_req_done(struct request *req, int uptodate)
-{
- struct se_task *task = req->end_io_data;
- struct pscsi_plugin_task *pt = PSCSI_TASK(task);
-
- pt->pscsi_result = req->errors;
- pt->pscsi_resid = req->resid_len;
-
- pscsi_process_SAM_status(task, pt);
- /*
- * Release BIDI-READ if present
- */
- if (req->next_rq != NULL)
- __blk_put_request(req->q, req->next_rq);
-
__blk_put_request(req->q, req);
- pt->pscsi_req = NULL;
+ kfree(pt);
}
static struct se_subsystem_api pscsi_template = {
.name = "pscsi",
.owner = THIS_MODULE,
.transport_type = TRANSPORT_PLUGIN_PHBA_PDEV,
- .cdb_none = pscsi_CDB_none,
- .map_task_non_SG = pscsi_map_task_non_SG,
- .map_task_SG = pscsi_map_task_SG,
.attach_hba = pscsi_attach_hba,
.detach_hba = pscsi_detach_hba,
.pmode_enable_hba = pscsi_pmode_enable_hba,
- .allocate_virtdevice = pscsi_allocate_virtdevice,
- .create_virtdevice = pscsi_create_virtdevice,
+ .alloc_device = pscsi_alloc_device,
+ .configure_device = pscsi_configure_device,
.free_device = pscsi_free_device,
.transport_complete = pscsi_transport_complete,
- .alloc_task = pscsi_alloc_task,
- .do_task = pscsi_do_task,
- .free_task = pscsi_free_task,
- .check_configfs_dev_params = pscsi_check_configfs_dev_params,
+ .parse_cdb = pscsi_parse_cdb,
.set_configfs_dev_params = pscsi_set_configfs_dev_params,
.show_configfs_dev_params = pscsi_show_configfs_dev_params,
- .get_cdb = pscsi_get_cdb,
- .get_sense_buffer = pscsi_get_sense_buffer,
- .get_device_rev = pscsi_get_device_rev,
.get_device_type = pscsi_get_device_type,
.get_blocks = pscsi_get_blocks,
};
@@ -1457,7 +1180,7 @@ static int __init pscsi_module_init(void)
return transport_subsystem_register(&pscsi_template);
}
-static void pscsi_module_exit(void)
+static void __exit pscsi_module_exit(void)
{
transport_subsystem_release(&pscsi_template);
}
diff --git a/drivers/target/target_core_pscsi.h b/drivers/target/target_core_pscsi.h
index a4cd5d352c3..1bd757dff8e 100644
--- a/drivers/target/target_core_pscsi.h
+++ b/drivers/target/target_core_pscsi.h
@@ -2,7 +2,6 @@
#define TARGET_CORE_PSCSI_H
#define PSCSI_VERSION "v4.0"
-#define PSCSI_VIRTUAL_HBA_DEPTH 2048
/* used in pscsi_find_alloc_len() */
#ifndef INQUIRY_DATA_SIZE
@@ -23,14 +22,11 @@
#include <linux/kobject.h>
struct pscsi_plugin_task {
- struct se_task pscsi_task;
- unsigned char *pscsi_cdb;
- unsigned char __pscsi_cdb[TCM_MAX_COMMAND_SIZE];
unsigned char pscsi_sense[SCSI_SENSE_BUFFERSIZE];
int pscsi_direction;
int pscsi_result;
u32 pscsi_resid;
- struct request *pscsi_req;
+ unsigned char pscsi_cdb[0];
} ____cacheline_aligned;
#define PDF_HAS_CHANNEL_ID 0x01
@@ -41,6 +37,7 @@ struct pscsi_plugin_task {
#define PDF_HAS_VIRT_HOST_ID 0x20
struct pscsi_dev_virt {
+ struct se_device dev;
int pdv_flags;
int pdv_host_id;
int pdv_channel_id;
@@ -48,11 +45,10 @@ struct pscsi_dev_virt {
int pdv_lun_id;
struct block_device *pdv_bd;
struct scsi_device *pdv_sd;
- struct se_hba *pdv_se_hba;
} ____cacheline_aligned;
typedef enum phv_modes {
- PHV_VIRUTAL_HOST_ID,
+ PHV_VIRTUAL_HOST_ID,
PHV_LLD_SCSI_HOST_NO
} phv_modes_t;
diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c
index 979aebf2001..b920db3388c 100644
--- a/drivers/target/target_core_rd.c
+++ b/drivers/target/target_core_rd.c
@@ -4,10 +4,7 @@
* This file contains the Storage Engine <-> Ramdisk transport
* specific functions.
*
- * Copyright (c) 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
+ * (c) Copyright 2003-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -27,29 +24,23 @@
*
******************************************************************************/
-#include <linux/version.h>
#include <linux/string.h>
#include <linux/parser.h>
#include <linux/timer.h>
-#include <linux/blkdev.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.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_backend.h>
#include "target_core_rd.h"
-static struct se_subsystem_api rd_dr_template;
-static struct se_subsystem_api rd_mcp_template;
-
-/* #define DEBUG_RAMDISK_MCP */
-/* #define DEBUG_RAMDISK_DR */
+static inline struct rd_dev *RD_DEV(struct se_device *dev)
+{
+ return container_of(dev, struct rd_dev, dev);
+}
/* rd_attach_hba(): (Part of se_subsystem_api_t template)
*
@@ -60,24 +51,18 @@ static int rd_attach_hba(struct se_hba *hba, u32 host_id)
struct rd_host *rd_host;
rd_host = kzalloc(sizeof(struct rd_host), GFP_KERNEL);
- if (!(rd_host)) {
- printk(KERN_ERR "Unable to allocate memory for struct rd_host\n");
+ if (!rd_host) {
+ pr_err("Unable to allocate memory for struct rd_host\n");
return -ENOMEM;
}
rd_host->rd_host_id = host_id;
- atomic_set(&hba->left_queue_depth, RD_HBA_QUEUE_DEPTH);
- atomic_set(&hba->max_queue_depth, RD_HBA_QUEUE_DEPTH);
- hba->hba_ptr = (void *) rd_host;
+ hba->hba_ptr = rd_host;
- printk(KERN_INFO "CORE_HBA[%d] - TCM Ramdisk HBA Driver %s on"
+ pr_debug("CORE_HBA[%d] - TCM Ramdisk HBA Driver %s on"
" Generic Target Core Stack %s\n", hba->hba_id,
RD_HBA_VERSION, TARGET_CORE_MOD_VERSION);
- printk(KERN_INFO "CORE_HBA[%d] - Attached Ramdisk HBA: %u to Generic"
- " Target Core TCQ Depth: %d MaxSectors: %u\n", hba->hba_id,
- rd_host->rd_host_id, atomic_read(&hba->max_queue_depth),
- RD_MAX_SECTORS);
return 0;
}
@@ -86,50 +71,53 @@ static void rd_detach_hba(struct se_hba *hba)
{
struct rd_host *rd_host = hba->hba_ptr;
- printk(KERN_INFO "CORE_HBA[%d] - Detached Ramdisk HBA: %u from"
+ pr_debug("CORE_HBA[%d] - Detached Ramdisk HBA: %u from"
" Generic Target Core\n", hba->hba_id, rd_host->rd_host_id);
kfree(rd_host);
hba->hba_ptr = NULL;
}
-/* rd_release_device_space():
- *
- *
- */
-static void rd_release_device_space(struct rd_dev *rd_dev)
+static u32 rd_release_sgl_table(struct rd_dev *rd_dev, struct rd_dev_sg_table *sg_table,
+ u32 sg_table_count)
{
- u32 i, j, page_count = 0, sg_per_table;
- struct rd_dev_sg_table *sg_table;
struct page *pg;
struct scatterlist *sg;
+ u32 i, j, page_count = 0, sg_per_table;
- if (!rd_dev->sg_table_array || !rd_dev->sg_table_count)
- return;
-
- sg_table = rd_dev->sg_table_array;
-
- for (i = 0; i < rd_dev->sg_table_count; i++) {
+ for (i = 0; i < sg_table_count; i++) {
sg = sg_table[i].sg_table;
sg_per_table = sg_table[i].rd_sg_count;
for (j = 0; j < sg_per_table; j++) {
pg = sg_page(&sg[j]);
- if ((pg)) {
+ if (pg) {
__free_page(pg);
page_count++;
}
}
-
kfree(sg);
}
- printk(KERN_INFO "CORE_RD[%u] - Released device space for Ramdisk"
+ kfree(sg_table);
+ return page_count;
+}
+
+static void rd_release_device_space(struct rd_dev *rd_dev)
+{
+ u32 page_count;
+
+ if (!rd_dev->sg_table_array || !rd_dev->sg_table_count)
+ return;
+
+ page_count = rd_release_sgl_table(rd_dev, rd_dev->sg_table_array,
+ rd_dev->sg_table_count);
+
+ pr_debug("CORE_RD[%u] - Released device space for Ramdisk"
" Device ID: %u, pages %u in %u tables total bytes %lu\n",
rd_dev->rd_host->rd_host_id, rd_dev->rd_dev_id, page_count,
rd_dev->sg_table_count, (unsigned long)page_count * PAGE_SIZE);
- kfree(sg_table);
rd_dev->sg_table_array = NULL;
rd_dev->sg_table_count = 0;
}
@@ -139,33 +127,15 @@ static void rd_release_device_space(struct rd_dev *rd_dev)
*
*
*/
-static int rd_build_device_space(struct rd_dev *rd_dev)
+static int rd_allocate_sgl_table(struct rd_dev *rd_dev, struct rd_dev_sg_table *sg_table,
+ u32 total_sg_needed, unsigned char init_payload)
{
- u32 i = 0, j, page_offset = 0, sg_per_table, sg_tables, total_sg_needed;
+ u32 i = 0, j, page_offset = 0, sg_per_table;
u32 max_sg_per_table = (RD_MAX_ALLOCATION_SIZE /
sizeof(struct scatterlist));
- struct rd_dev_sg_table *sg_table;
struct page *pg;
struct scatterlist *sg;
-
- if (rd_dev->rd_page_count <= 0) {
- printk(KERN_ERR "Illegal page count: %u for Ramdisk device\n",
- rd_dev->rd_page_count);
- return -1;
- }
- total_sg_needed = rd_dev->rd_page_count;
-
- sg_tables = (total_sg_needed / max_sg_per_table) + 1;
-
- sg_table = kzalloc(sg_tables * sizeof(struct rd_dev_sg_table), GFP_KERNEL);
- if (!(sg_table)) {
- printk(KERN_ERR "Unable to allocate memory for Ramdisk"
- " scatterlist tables\n");
- return -1;
- }
-
- rd_dev->sg_table_array = sg_table;
- rd_dev->sg_table_count = sg_tables;
+ unsigned char *p;
while (total_sg_needed) {
sg_per_table = (total_sg_needed > max_sg_per_table) ?
@@ -173,13 +143,13 @@ static int rd_build_device_space(struct rd_dev *rd_dev)
sg = kzalloc(sg_per_table * sizeof(struct scatterlist),
GFP_KERNEL);
- if (!(sg)) {
- printk(KERN_ERR "Unable to allocate scatterlist array"
+ if (!sg) {
+ pr_err("Unable to allocate scatterlist array"
" for struct rd_dev\n");
- return -1;
+ return -ENOMEM;
}
- sg_init_table((struct scatterlist *)&sg[0], sg_per_table);
+ sg_init_table(sg, sg_per_table);
sg_table[i].sg_table = sg;
sg_table[i].rd_sg_count = sg_per_table;
@@ -189,754 +159,388 @@ static int rd_build_device_space(struct rd_dev *rd_dev)
for (j = 0; j < sg_per_table; j++) {
pg = alloc_pages(GFP_KERNEL, 0);
- if (!(pg)) {
- printk(KERN_ERR "Unable to allocate scatterlist"
+ if (!pg) {
+ pr_err("Unable to allocate scatterlist"
" pages for struct rd_dev_sg_table\n");
- return -1;
+ return -ENOMEM;
}
sg_assign_page(&sg[j], pg);
sg[j].length = PAGE_SIZE;
+
+ p = kmap(pg);
+ memset(p, init_payload, PAGE_SIZE);
+ kunmap(pg);
}
page_offset += sg_per_table;
total_sg_needed -= sg_per_table;
}
- printk(KERN_INFO "CORE_RD[%u] - Built Ramdisk Device ID: %u space of"
- " %u pages in %u tables\n", rd_dev->rd_host->rd_host_id,
- rd_dev->rd_dev_id, rd_dev->rd_page_count,
- rd_dev->sg_table_count);
-
return 0;
}
-static void *rd_allocate_virtdevice(
- struct se_hba *hba,
- const char *name,
- int rd_direct)
+static int rd_build_device_space(struct rd_dev *rd_dev)
{
- struct rd_dev *rd_dev;
- struct rd_host *rd_host = hba->hba_ptr;
+ struct rd_dev_sg_table *sg_table;
+ u32 sg_tables, total_sg_needed;
+ u32 max_sg_per_table = (RD_MAX_ALLOCATION_SIZE /
+ sizeof(struct scatterlist));
+ int rc;
- rd_dev = kzalloc(sizeof(struct rd_dev), GFP_KERNEL);
- if (!(rd_dev)) {
- printk(KERN_ERR "Unable to allocate memory for struct rd_dev\n");
- return NULL;
+ if (rd_dev->rd_page_count <= 0) {
+ pr_err("Illegal page count: %u for Ramdisk device\n",
+ rd_dev->rd_page_count);
+ return -EINVAL;
}
- rd_dev->rd_host = rd_host;
- rd_dev->rd_direct = rd_direct;
+ /* Don't need backing pages for NULLIO */
+ if (rd_dev->rd_flags & RDF_NULLIO)
+ return 0;
+
+ total_sg_needed = rd_dev->rd_page_count;
+
+ sg_tables = (total_sg_needed / max_sg_per_table) + 1;
+
+ sg_table = kzalloc(sg_tables * sizeof(struct rd_dev_sg_table), GFP_KERNEL);
+ if (!sg_table) {
+ pr_err("Unable to allocate memory for Ramdisk"
+ " scatterlist tables\n");
+ return -ENOMEM;
+ }
+
+ rd_dev->sg_table_array = sg_table;
+ rd_dev->sg_table_count = sg_tables;
+
+ rc = rd_allocate_sgl_table(rd_dev, sg_table, total_sg_needed, 0x00);
+ if (rc)
+ return rc;
- return rd_dev;
+ pr_debug("CORE_RD[%u] - Built Ramdisk Device ID: %u space of"
+ " %u pages in %u tables\n", rd_dev->rd_host->rd_host_id,
+ rd_dev->rd_dev_id, rd_dev->rd_page_count,
+ rd_dev->sg_table_count);
+
+ return 0;
}
-static void *rd_DIRECT_allocate_virtdevice(struct se_hba *hba, const char *name)
+static void rd_release_prot_space(struct rd_dev *rd_dev)
{
- return rd_allocate_virtdevice(hba, name, 1);
+ u32 page_count;
+
+ if (!rd_dev->sg_prot_array || !rd_dev->sg_prot_count)
+ return;
+
+ page_count = rd_release_sgl_table(rd_dev, rd_dev->sg_prot_array,
+ rd_dev->sg_prot_count);
+
+ pr_debug("CORE_RD[%u] - Released protection space for Ramdisk"
+ " Device ID: %u, pages %u in %u tables total bytes %lu\n",
+ rd_dev->rd_host->rd_host_id, rd_dev->rd_dev_id, page_count,
+ rd_dev->sg_table_count, (unsigned long)page_count * PAGE_SIZE);
+
+ rd_dev->sg_prot_array = NULL;
+ rd_dev->sg_prot_count = 0;
}
-static void *rd_MEMCPY_allocate_virtdevice(struct se_hba *hba, const char *name)
+static int rd_build_prot_space(struct rd_dev *rd_dev, int prot_length, int block_size)
{
- return rd_allocate_virtdevice(hba, name, 0);
+ struct rd_dev_sg_table *sg_table;
+ u32 total_sg_needed, sg_tables;
+ u32 max_sg_per_table = (RD_MAX_ALLOCATION_SIZE /
+ sizeof(struct scatterlist));
+ int rc;
+
+ if (rd_dev->rd_flags & RDF_NULLIO)
+ return 0;
+ /*
+ * prot_length=8byte dif data
+ * tot sg needed = rd_page_count * (PGSZ/block_size) *
+ * (prot_length/block_size) + pad
+ * PGSZ canceled each other.
+ */
+ total_sg_needed = (rd_dev->rd_page_count * prot_length / block_size) + 1;
+
+ sg_tables = (total_sg_needed / max_sg_per_table) + 1;
+
+ sg_table = kzalloc(sg_tables * sizeof(struct rd_dev_sg_table), GFP_KERNEL);
+ if (!sg_table) {
+ pr_err("Unable to allocate memory for Ramdisk protection"
+ " scatterlist tables\n");
+ return -ENOMEM;
+ }
+
+ rd_dev->sg_prot_array = sg_table;
+ rd_dev->sg_prot_count = sg_tables;
+
+ rc = rd_allocate_sgl_table(rd_dev, sg_table, total_sg_needed, 0xff);
+ if (rc)
+ return rc;
+
+ pr_debug("CORE_RD[%u] - Built Ramdisk Device ID: %u prot space of"
+ " %u pages in %u tables\n", rd_dev->rd_host->rd_host_id,
+ rd_dev->rd_dev_id, total_sg_needed, rd_dev->sg_prot_count);
+
+ return 0;
}
-/* rd_create_virtdevice():
- *
- *
- */
-static struct se_device *rd_create_virtdevice(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- void *p,
- int rd_direct)
+static struct se_device *rd_alloc_device(struct se_hba *hba, const char *name)
{
- struct se_device *dev;
- struct se_dev_limits dev_limits;
- struct rd_dev *rd_dev = p;
+ struct rd_dev *rd_dev;
struct rd_host *rd_host = hba->hba_ptr;
- int dev_flags = 0;
- char prod[16], rev[4];
- memset(&dev_limits, 0, sizeof(struct se_dev_limits));
+ rd_dev = kzalloc(sizeof(struct rd_dev), GFP_KERNEL);
+ if (!rd_dev) {
+ pr_err("Unable to allocate memory for struct rd_dev\n");
+ return NULL;
+ }
+
+ rd_dev->rd_host = rd_host;
- if (rd_build_device_space(rd_dev) < 0)
- goto fail;
+ return &rd_dev->dev;
+}
- snprintf(prod, 16, "RAMDISK-%s", (rd_dev->rd_direct) ? "DR" : "MCP");
- snprintf(rev, 4, "%s", (rd_dev->rd_direct) ? RD_DR_VERSION :
- RD_MCP_VERSION);
-
- dev_limits.limits.logical_block_size = RD_BLOCKSIZE;
- dev_limits.limits.max_hw_sectors = RD_MAX_SECTORS;
- dev_limits.limits.max_sectors = RD_MAX_SECTORS;
- dev_limits.hw_queue_depth = RD_MAX_DEVICE_QUEUE_DEPTH;
- dev_limits.queue_depth = RD_DEVICE_QUEUE_DEPTH;
-
- dev = transport_add_device_to_core_hba(hba,
- (rd_dev->rd_direct) ? &rd_dr_template :
- &rd_mcp_template, se_dev, dev_flags, (void *)rd_dev,
- &dev_limits, prod, rev);
- if (!(dev))
+static int rd_configure_device(struct se_device *dev)
+{
+ struct rd_dev *rd_dev = RD_DEV(dev);
+ struct rd_host *rd_host = dev->se_hba->hba_ptr;
+ int ret;
+
+ if (!(rd_dev->rd_flags & RDF_HAS_PAGE_COUNT)) {
+ pr_debug("Missing rd_pages= parameter\n");
+ return -EINVAL;
+ }
+
+ ret = rd_build_device_space(rd_dev);
+ if (ret < 0)
goto fail;
+ dev->dev_attrib.hw_block_size = RD_BLOCKSIZE;
+ dev->dev_attrib.hw_max_sectors = UINT_MAX;
+ dev->dev_attrib.hw_queue_depth = RD_MAX_DEVICE_QUEUE_DEPTH;
+
rd_dev->rd_dev_id = rd_host->rd_host_dev_id_count++;
- rd_dev->rd_queue_depth = dev->queue_depth;
- printk(KERN_INFO "CORE_RD[%u] - Added TCM %s Ramdisk Device ID: %u of"
+ pr_debug("CORE_RD[%u] - Added TCM MEMCPY Ramdisk Device ID: %u of"
" %u pages in %u tables, %lu total bytes\n",
- rd_host->rd_host_id, (!rd_dev->rd_direct) ? "MEMCPY" :
- "DIRECT", rd_dev->rd_dev_id, rd_dev->rd_page_count,
+ rd_host->rd_host_id, rd_dev->rd_dev_id, rd_dev->rd_page_count,
rd_dev->sg_table_count,
(unsigned long)(rd_dev->rd_page_count * PAGE_SIZE));
- return dev;
+ return 0;
fail:
rd_release_device_space(rd_dev);
- return NULL;
-}
-
-static struct se_device *rd_DIRECT_create_virtdevice(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- void *p)
-{
- return rd_create_virtdevice(hba, se_dev, p, 1);
+ return ret;
}
-static struct se_device *rd_MEMCPY_create_virtdevice(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- void *p)
+static void rd_free_device(struct se_device *dev)
{
- return rd_create_virtdevice(hba, se_dev, p, 0);
-}
-
-/* rd_free_device(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static void rd_free_device(void *p)
-{
- struct rd_dev *rd_dev = p;
+ struct rd_dev *rd_dev = RD_DEV(dev);
rd_release_device_space(rd_dev);
kfree(rd_dev);
}
-static inline struct rd_request *RD_REQ(struct se_task *task)
-{
- return container_of(task, struct rd_request, rd_task);
-}
-
-static struct se_task *
-rd_alloc_task(struct se_cmd *cmd)
-{
- struct rd_request *rd_req;
-
- rd_req = kzalloc(sizeof(struct rd_request), GFP_KERNEL);
- if (!rd_req) {
- printk(KERN_ERR "Unable to allocate struct rd_request\n");
- return NULL;
- }
- rd_req->rd_dev = SE_DEV(cmd)->dev_ptr;
-
- return &rd_req->rd_task;
-}
-
-/* rd_get_sg_table():
- *
- *
- */
static struct rd_dev_sg_table *rd_get_sg_table(struct rd_dev *rd_dev, u32 page)
{
- u32 i;
struct rd_dev_sg_table *sg_table;
+ u32 i, sg_per_table = (RD_MAX_ALLOCATION_SIZE /
+ sizeof(struct scatterlist));
- for (i = 0; i < rd_dev->sg_table_count; i++) {
+ i = page / sg_per_table;
+ if (i < rd_dev->sg_table_count) {
sg_table = &rd_dev->sg_table_array[i];
if ((sg_table->page_start_offset <= page) &&
(sg_table->page_end_offset >= page))
return sg_table;
}
- printk(KERN_ERR "Unable to locate struct rd_dev_sg_table for page: %u\n",
+ pr_err("Unable to locate struct rd_dev_sg_table for page: %u\n",
page);
return NULL;
}
-/* rd_MEMCPY_read():
- *
- *
- */
-static int rd_MEMCPY_read(struct rd_request *req)
+static struct rd_dev_sg_table *rd_get_prot_table(struct rd_dev *rd_dev, u32 page)
{
- struct se_task *task = &req->rd_task;
- struct rd_dev *dev = req->rd_dev;
- struct rd_dev_sg_table *table;
- struct scatterlist *sg_d, *sg_s;
- void *dst, *src;
- u32 i = 0, j = 0, dst_offset = 0, src_offset = 0;
- u32 length, page_end = 0, table_sg_end;
- u32 rd_offset = req->rd_offset;
-
- table = rd_get_sg_table(dev, req->rd_page);
- if (!(table))
- return -1;
-
- table_sg_end = (table->page_end_offset - req->rd_page);
- sg_d = task->task_sg;
- sg_s = &table->sg_table[req->rd_page - table->page_start_offset];
-#ifdef DEBUG_RAMDISK_MCP
- printk(KERN_INFO "RD[%u]: Read LBA: %llu, Size: %u Page: %u, Offset:"
- " %u\n", dev->rd_dev_id, task->task_lba, req->rd_size,
- req->rd_page, req->rd_offset);
-#endif
- src_offset = rd_offset;
-
- while (req->rd_size) {
- if ((sg_d[i].length - dst_offset) <
- (sg_s[j].length - src_offset)) {
- length = (sg_d[i].length - dst_offset);
-#ifdef DEBUG_RAMDISK_MCP
- printk(KERN_INFO "Step 1 - sg_d[%d]: %p length: %d"
- " offset: %u sg_s[%d].length: %u\n", i,
- &sg_d[i], sg_d[i].length, sg_d[i].offset, j,
- sg_s[j].length);
- printk(KERN_INFO "Step 1 - length: %u dst_offset: %u"
- " src_offset: %u\n", length, dst_offset,
- src_offset);
-#endif
- if (length > req->rd_size)
- length = req->rd_size;
-
- dst = sg_virt(&sg_d[i++]) + dst_offset;
- if (!dst)
- BUG();
-
- src = sg_virt(&sg_s[j]) + src_offset;
- if (!src)
- BUG();
-
- dst_offset = 0;
- src_offset = length;
- page_end = 0;
- } else {
- length = (sg_s[j].length - src_offset);
-#ifdef DEBUG_RAMDISK_MCP
- printk(KERN_INFO "Step 2 - sg_d[%d]: %p length: %d"
- " offset: %u sg_s[%d].length: %u\n", i,
- &sg_d[i], sg_d[i].length, sg_d[i].offset,
- j, sg_s[j].length);
- printk(KERN_INFO "Step 2 - length: %u dst_offset: %u"
- " src_offset: %u\n", length, dst_offset,
- src_offset);
-#endif
- if (length > req->rd_size)
- length = req->rd_size;
-
- dst = sg_virt(&sg_d[i]) + dst_offset;
- if (!dst)
- BUG();
-
- if (sg_d[i].length == length) {
- i++;
- dst_offset = 0;
- } else
- dst_offset = length;
-
- src = sg_virt(&sg_s[j++]) + src_offset;
- if (!src)
- BUG();
-
- src_offset = 0;
- page_end = 1;
- }
-
- memcpy(dst, src, length);
-
-#ifdef DEBUG_RAMDISK_MCP
- printk(KERN_INFO "page: %u, remaining size: %u, length: %u,"
- " i: %u, j: %u\n", req->rd_page,
- (req->rd_size - length), length, i, j);
-#endif
- req->rd_size -= length;
- if (!(req->rd_size))
- return 0;
-
- if (!page_end)
- continue;
+ struct rd_dev_sg_table *sg_table;
+ u32 i, sg_per_table = (RD_MAX_ALLOCATION_SIZE /
+ sizeof(struct scatterlist));
- if (++req->rd_page <= table->page_end_offset) {
-#ifdef DEBUG_RAMDISK_MCP
- printk(KERN_INFO "page: %u in same page table\n",
- req->rd_page);
-#endif
- continue;
- }
-#ifdef DEBUG_RAMDISK_MCP
- printk(KERN_INFO "getting new page table for page: %u\n",
- req->rd_page);
-#endif
- table = rd_get_sg_table(dev, req->rd_page);
- if (!(table))
- return -1;
-
- sg_s = &table->sg_table[j = 0];
+ i = page / sg_per_table;
+ if (i < rd_dev->sg_prot_count) {
+ sg_table = &rd_dev->sg_prot_array[i];
+ if ((sg_table->page_start_offset <= page) &&
+ (sg_table->page_end_offset >= page))
+ return sg_table;
}
- return 0;
+ pr_err("Unable to locate struct prot rd_dev_sg_table for page: %u\n",
+ page);
+
+ return NULL;
}
-/* rd_MEMCPY_write():
- *
- *
- */
-static int rd_MEMCPY_write(struct rd_request *req)
+static sense_reason_t
+rd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
+ enum dma_data_direction data_direction)
{
- struct se_task *task = &req->rd_task;
- struct rd_dev *dev = req->rd_dev;
+ struct se_device *se_dev = cmd->se_dev;
+ struct rd_dev *dev = RD_DEV(se_dev);
struct rd_dev_sg_table *table;
- struct scatterlist *sg_d, *sg_s;
- void *dst, *src;
- u32 i = 0, j = 0, dst_offset = 0, src_offset = 0;
- u32 length, page_end = 0, table_sg_end;
- u32 rd_offset = req->rd_offset;
-
- table = rd_get_sg_table(dev, req->rd_page);
- if (!(table))
- return -1;
-
- table_sg_end = (table->page_end_offset - req->rd_page);
- sg_d = &table->sg_table[req->rd_page - table->page_start_offset];
- sg_s = task->task_sg;
-#ifdef DEBUG_RAMDISK_MCP
- printk(KERN_INFO "RD[%d] Write LBA: %llu, Size: %u, Page: %u,"
- " Offset: %u\n", dev->rd_dev_id, task->task_lba, req->rd_size,
- req->rd_page, req->rd_offset);
-#endif
- dst_offset = rd_offset;
-
- while (req->rd_size) {
- if ((sg_s[i].length - src_offset) <
- (sg_d[j].length - dst_offset)) {
- length = (sg_s[i].length - src_offset);
-#ifdef DEBUG_RAMDISK_MCP
- printk(KERN_INFO "Step 1 - sg_s[%d]: %p length: %d"
- " offset: %d sg_d[%d].length: %u\n", i,
- &sg_s[i], sg_s[i].length, sg_s[i].offset,
- j, sg_d[j].length);
- printk(KERN_INFO "Step 1 - length: %u src_offset: %u"
- " dst_offset: %u\n", length, src_offset,
- dst_offset);
-#endif
- if (length > req->rd_size)
- length = req->rd_size;
-
- src = sg_virt(&sg_s[i++]) + src_offset;
- if (!src)
- BUG();
-
- dst = sg_virt(&sg_d[j]) + dst_offset;
- if (!dst)
- BUG();
-
- src_offset = 0;
- dst_offset = length;
- page_end = 0;
- } else {
- length = (sg_d[j].length - dst_offset);
-#ifdef DEBUG_RAMDISK_MCP
- printk(KERN_INFO "Step 2 - sg_s[%d]: %p length: %d"
- " offset: %d sg_d[%d].length: %u\n", i,
- &sg_s[i], sg_s[i].length, sg_s[i].offset,
- j, sg_d[j].length);
- printk(KERN_INFO "Step 2 - length: %u src_offset: %u"
- " dst_offset: %u\n", length, src_offset,
- dst_offset);
-#endif
- if (length > req->rd_size)
- length = req->rd_size;
-
- src = sg_virt(&sg_s[i]) + src_offset;
- if (!src)
- BUG();
-
- if (sg_s[i].length == length) {
- i++;
- src_offset = 0;
- } else
- src_offset = length;
-
- dst = sg_virt(&sg_d[j++]) + dst_offset;
- if (!dst)
- BUG();
-
- dst_offset = 0;
- page_end = 1;
- }
-
- memcpy(dst, src, length);
-
-#ifdef DEBUG_RAMDISK_MCP
- printk(KERN_INFO "page: %u, remaining size: %u, length: %u,"
- " i: %u, j: %u\n", req->rd_page,
- (req->rd_size - length), length, i, j);
-#endif
- req->rd_size -= length;
- if (!(req->rd_size))
- return 0;
-
- if (!page_end)
- continue;
-
- if (++req->rd_page <= table->page_end_offset) {
-#ifdef DEBUG_RAMDISK_MCP
- printk(KERN_INFO "page: %u in same page table\n",
- req->rd_page);
-#endif
- continue;
- }
-#ifdef DEBUG_RAMDISK_MCP
- printk(KERN_INFO "getting new page table for page: %u\n",
- req->rd_page);
-#endif
- table = rd_get_sg_table(dev, req->rd_page);
- if (!(table))
- return -1;
-
- sg_d = &table->sg_table[j = 0];
+ struct scatterlist *rd_sg;
+ struct sg_mapping_iter m;
+ u32 rd_offset;
+ u32 rd_size;
+ u32 rd_page;
+ u32 src_len;
+ u64 tmp;
+ sense_reason_t rc;
+
+ if (dev->rd_flags & RDF_NULLIO) {
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
+ return 0;
}
- return 0;
-}
-
-/* rd_MEMCPY_do_task(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static int rd_MEMCPY_do_task(struct se_task *task)
-{
- struct se_device *dev = task->se_dev;
- struct rd_request *req = RD_REQ(task);
- unsigned long long lba;
- int ret;
+ tmp = cmd->t_task_lba * se_dev->dev_attrib.block_size;
+ rd_offset = do_div(tmp, PAGE_SIZE);
+ rd_page = tmp;
+ rd_size = cmd->data_length;
- req->rd_page = (task->task_lba * DEV_ATTRIB(dev)->block_size) / PAGE_SIZE;
- lba = task->task_lba;
- req->rd_offset = (do_div(lba,
- (PAGE_SIZE / DEV_ATTRIB(dev)->block_size))) *
- DEV_ATTRIB(dev)->block_size;
- req->rd_size = task->task_size;
+ table = rd_get_sg_table(dev, rd_page);
+ if (!table)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
- if (task->task_data_direction == DMA_FROM_DEVICE)
- ret = rd_MEMCPY_read(req);
- else
- ret = rd_MEMCPY_write(req);
+ rd_sg = &table->sg_table[rd_page - table->page_start_offset];
- if (ret != 0)
- return ret;
+ pr_debug("RD[%u]: %s LBA: %llu, Size: %u Page: %u, Offset: %u\n",
+ dev->rd_dev_id,
+ data_direction == DMA_FROM_DEVICE ? "Read" : "Write",
+ cmd->t_task_lba, rd_size, rd_page, rd_offset);
- task->task_scsi_status = GOOD;
- transport_complete_task(task, 1);
+ if (cmd->prot_type && data_direction == DMA_TO_DEVICE) {
+ struct rd_dev_sg_table *prot_table;
+ struct scatterlist *prot_sg;
+ u32 sectors = cmd->data_length / se_dev->dev_attrib.block_size;
+ u32 prot_offset, prot_page;
- return PYX_TRANSPORT_SENT_TO_TRANSPORT;
-}
+ tmp = cmd->t_task_lba * se_dev->prot_length;
+ prot_offset = do_div(tmp, PAGE_SIZE);
+ prot_page = tmp;
-/* rd_DIRECT_with_offset():
- *
- *
- */
-static int rd_DIRECT_with_offset(
- struct se_task *task,
- struct list_head *se_mem_list,
- u32 *se_mem_cnt,
- u32 *task_offset)
-{
- struct rd_request *req = RD_REQ(task);
- struct rd_dev *dev = req->rd_dev;
- struct rd_dev_sg_table *table;
- struct se_mem *se_mem;
- struct scatterlist *sg_s;
- u32 j = 0, set_offset = 1;
- u32 get_next_table = 0, offset_length, table_sg_end;
-
- table = rd_get_sg_table(dev, req->rd_page);
- if (!(table))
- return -1;
-
- table_sg_end = (table->page_end_offset - req->rd_page);
- sg_s = &table->sg_table[req->rd_page - table->page_start_offset];
-#ifdef DEBUG_RAMDISK_DR
- printk(KERN_INFO "%s DIRECT LBA: %llu, Size: %u Page: %u, Offset: %u\n",
- (task->task_data_direction == DMA_TO_DEVICE) ?
- "Write" : "Read",
- task->task_lba, req->rd_size, req->rd_page, req->rd_offset);
-#endif
- while (req->rd_size) {
- se_mem = kmem_cache_zalloc(se_mem_cache, GFP_KERNEL);
- if (!(se_mem)) {
- printk(KERN_ERR "Unable to allocate struct se_mem\n");
- return -1;
- }
- INIT_LIST_HEAD(&se_mem->se_list);
+ prot_table = rd_get_prot_table(dev, prot_page);
+ if (!prot_table)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
- if (set_offset) {
- offset_length = sg_s[j].length - req->rd_offset;
- if (offset_length > req->rd_size)
- offset_length = req->rd_size;
+ prot_sg = &prot_table->sg_table[prot_page - prot_table->page_start_offset];
- se_mem->se_page = sg_page(&sg_s[j++]);
- se_mem->se_off = req->rd_offset;
- se_mem->se_len = offset_length;
+ rc = sbc_dif_verify_write(cmd, cmd->t_task_lba, sectors, 0,
+ prot_sg, prot_offset);
+ if (rc)
+ return rc;
+ }
- set_offset = 0;
- get_next_table = (j > table_sg_end);
- goto check_eot;
+ src_len = PAGE_SIZE - rd_offset;
+ sg_miter_start(&m, sgl, sgl_nents,
+ data_direction == DMA_FROM_DEVICE ?
+ SG_MITER_TO_SG : SG_MITER_FROM_SG);
+ while (rd_size) {
+ u32 len;
+ void *rd_addr;
+
+ sg_miter_next(&m);
+ if (!(u32)m.length) {
+ pr_debug("RD[%u]: invalid sgl %p len %zu\n",
+ dev->rd_dev_id, m.addr, m.length);
+ sg_miter_stop(&m);
+ return TCM_INCORRECT_AMOUNT_OF_DATA;
}
+ len = min((u32)m.length, src_len);
+ if (len > rd_size) {
+ pr_debug("RD[%u]: size underrun page %d offset %d "
+ "size %d\n", dev->rd_dev_id,
+ rd_page, rd_offset, rd_size);
+ len = rd_size;
+ }
+ m.consumed = len;
- offset_length = (req->rd_size < req->rd_offset) ?
- req->rd_size : req->rd_offset;
-
- se_mem->se_page = sg_page(&sg_s[j]);
- se_mem->se_len = offset_length;
-
- set_offset = 1;
-
-check_eot:
-#ifdef DEBUG_RAMDISK_DR
- printk(KERN_INFO "page: %u, size: %u, offset_length: %u, j: %u"
- " se_mem: %p, se_page: %p se_off: %u se_len: %u\n",
- req->rd_page, req->rd_size, offset_length, j, se_mem,
- se_mem->se_page, se_mem->se_off, se_mem->se_len);
-#endif
- list_add_tail(&se_mem->se_list, se_mem_list);
- (*se_mem_cnt)++;
+ rd_addr = sg_virt(rd_sg) + rd_offset;
- req->rd_size -= offset_length;
- if (!(req->rd_size))
- goto out;
+ if (data_direction == DMA_FROM_DEVICE)
+ memcpy(m.addr, rd_addr, len);
+ else
+ memcpy(rd_addr, m.addr, len);
- if (!set_offset && !get_next_table)
+ rd_size -= len;
+ if (!rd_size)
continue;
- if (++req->rd_page <= table->page_end_offset) {
-#ifdef DEBUG_RAMDISK_DR
- printk(KERN_INFO "page: %u in same page table\n",
- req->rd_page);
-#endif
+ src_len -= len;
+ if (src_len) {
+ rd_offset += len;
continue;
}
-#ifdef DEBUG_RAMDISK_DR
- printk(KERN_INFO "getting new page table for page: %u\n",
- req->rd_page);
-#endif
- table = rd_get_sg_table(dev, req->rd_page);
- if (!(table))
- return -1;
-
- sg_s = &table->sg_table[j = 0];
- }
-out:
- T_TASK(task->task_se_cmd)->t_tasks_se_num += *se_mem_cnt;
-#ifdef DEBUG_RAMDISK_DR
- printk(KERN_INFO "RD_DR - Allocated %u struct se_mem segments for task\n",
- *se_mem_cnt);
-#endif
- return 0;
-}
-
-/* rd_DIRECT_without_offset():
- *
- *
- */
-static int rd_DIRECT_without_offset(
- struct se_task *task,
- struct list_head *se_mem_list,
- u32 *se_mem_cnt,
- u32 *task_offset)
-{
- struct rd_request *req = RD_REQ(task);
- struct rd_dev *dev = req->rd_dev;
- struct rd_dev_sg_table *table;
- struct se_mem *se_mem;
- struct scatterlist *sg_s;
- u32 length, j = 0;
-
- table = rd_get_sg_table(dev, req->rd_page);
- if (!(table))
- return -1;
-
- sg_s = &table->sg_table[req->rd_page - table->page_start_offset];
-#ifdef DEBUG_RAMDISK_DR
- printk(KERN_INFO "%s DIRECT LBA: %llu, Size: %u, Page: %u\n",
- (task->task_data_direction == DMA_TO_DEVICE) ?
- "Write" : "Read",
- task->task_lba, req->rd_size, req->rd_page);
-#endif
- while (req->rd_size) {
- se_mem = kmem_cache_zalloc(se_mem_cache, GFP_KERNEL);
- if (!(se_mem)) {
- printk(KERN_ERR "Unable to allocate struct se_mem\n");
- return -1;
- }
- INIT_LIST_HEAD(&se_mem->se_list);
-
- length = (req->rd_size < sg_s[j].length) ?
- req->rd_size : sg_s[j].length;
-
- se_mem->se_page = sg_page(&sg_s[j++]);
- se_mem->se_len = length;
-
-#ifdef DEBUG_RAMDISK_DR
- printk(KERN_INFO "page: %u, size: %u, j: %u se_mem: %p,"
- " se_page: %p se_off: %u se_len: %u\n", req->rd_page,
- req->rd_size, j, se_mem, se_mem->se_page,
- se_mem->se_off, se_mem->se_len);
-#endif
- list_add_tail(&se_mem->se_list, se_mem_list);
- (*se_mem_cnt)++;
-
- req->rd_size -= length;
- if (!(req->rd_size))
- goto out;
-
- if (++req->rd_page <= table->page_end_offset) {
-#ifdef DEBUG_RAMDISK_DR
- printk("page: %u in same page table\n",
- req->rd_page);
-#endif
+ /* rd page completed, next one please */
+ rd_page++;
+ rd_offset = 0;
+ src_len = PAGE_SIZE;
+ if (rd_page <= table->page_end_offset) {
+ rd_sg++;
continue;
}
-#ifdef DEBUG_RAMDISK_DR
- printk(KERN_INFO "getting new page table for page: %u\n",
- req->rd_page);
-#endif
- table = rd_get_sg_table(dev, req->rd_page);
- if (!(table))
- return -1;
-
- sg_s = &table->sg_table[j = 0];
- }
-out:
- T_TASK(task->task_se_cmd)->t_tasks_se_num += *se_mem_cnt;
-#ifdef DEBUG_RAMDISK_DR
- printk(KERN_INFO "RD_DR - Allocated %u struct se_mem segments for task\n",
- *se_mem_cnt);
-#endif
- return 0;
-}
+ table = rd_get_sg_table(dev, rd_page);
+ if (!table) {
+ sg_miter_stop(&m);
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
-/* rd_DIRECT_do_se_mem_map():
- *
- *
- */
-static int rd_DIRECT_do_se_mem_map(
- struct se_task *task,
- struct list_head *se_mem_list,
- void *in_mem,
- struct se_mem *in_se_mem,
- struct se_mem **out_se_mem,
- u32 *se_mem_cnt,
- u32 *task_offset_in)
-{
- struct se_cmd *cmd = task->task_se_cmd;
- struct rd_request *req = RD_REQ(task);
- u32 task_offset = *task_offset_in;
- unsigned long long lba;
- int ret;
+ /* since we increment, the first sg entry is correct */
+ rd_sg = table->sg_table;
+ }
+ sg_miter_stop(&m);
- req->rd_page = ((task->task_lba * DEV_ATTRIB(task->se_dev)->block_size) /
- PAGE_SIZE);
- lba = task->task_lba;
- req->rd_offset = (do_div(lba,
- (PAGE_SIZE / DEV_ATTRIB(task->se_dev)->block_size))) *
- DEV_ATTRIB(task->se_dev)->block_size;
- req->rd_size = task->task_size;
-
- if (req->rd_offset)
- ret = rd_DIRECT_with_offset(task, se_mem_list, se_mem_cnt,
- task_offset_in);
- else
- ret = rd_DIRECT_without_offset(task, se_mem_list, se_mem_cnt,
- task_offset_in);
+ if (cmd->prot_type && data_direction == DMA_FROM_DEVICE) {
+ struct rd_dev_sg_table *prot_table;
+ struct scatterlist *prot_sg;
+ u32 sectors = cmd->data_length / se_dev->dev_attrib.block_size;
+ u32 prot_offset, prot_page;
- if (ret < 0)
- return ret;
+ tmp = cmd->t_task_lba * se_dev->prot_length;
+ prot_offset = do_div(tmp, PAGE_SIZE);
+ prot_page = tmp;
- if (CMD_TFO(cmd)->task_sg_chaining == 0)
- return 0;
- /*
- * Currently prevent writers from multiple HW fabrics doing
- * pci_map_sg() to RD_DR's internal scatterlist memory.
- */
- if (cmd->data_direction == DMA_TO_DEVICE) {
- printk(KERN_ERR "DMA_TO_DEVICE not supported for"
- " RAMDISK_DR with task_sg_chaining=1\n");
- return -1;
- }
- /*
- * Special case for if task_sg_chaining is enabled, then
- * we setup struct se_task->task_sg[], as it will be used by
- * transport_do_task_sg_chain() for creating chainged SGLs
- * across multiple struct se_task->task_sg[].
- */
- if (!(transport_calc_sg_num(task,
- list_entry(T_TASK(cmd)->t_mem_list->next,
- struct se_mem, se_list),
- task_offset)))
- return -1;
-
- return transport_map_mem_to_sg(task, se_mem_list, task->task_sg,
- list_entry(T_TASK(cmd)->t_mem_list->next,
- struct se_mem, se_list),
- out_se_mem, se_mem_cnt, task_offset_in);
-}
+ prot_table = rd_get_prot_table(dev, prot_page);
+ if (!prot_table)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
-/* rd_DIRECT_do_task(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static int rd_DIRECT_do_task(struct se_task *task)
-{
- /*
- * At this point the locally allocated RD tables have been mapped
- * to struct se_mem elements in rd_DIRECT_do_se_mem_map().
- */
- task->task_scsi_status = GOOD;
- transport_complete_task(task, 1);
+ prot_sg = &prot_table->sg_table[prot_page - prot_table->page_start_offset];
- return PYX_TRANSPORT_SENT_TO_TRANSPORT;
-}
+ rc = sbc_dif_verify_read(cmd, cmd->t_task_lba, sectors, 0,
+ prot_sg, prot_offset);
+ if (rc)
+ return rc;
+ }
-/* rd_free_task(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static void rd_free_task(struct se_task *task)
-{
- kfree(RD_REQ(task));
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
+ return 0;
}
enum {
- Opt_rd_pages, Opt_err
+ Opt_rd_pages, Opt_rd_nullio, Opt_err
};
static match_table_t tokens = {
{Opt_rd_pages, "rd_pages=%d"},
+ {Opt_rd_nullio, "rd_nullio=%d"},
{Opt_err, NULL}
};
-static ssize_t rd_set_configfs_dev_params(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- const char *page,
- ssize_t count)
+static ssize_t rd_set_configfs_dev_params(struct se_device *dev,
+ const char *page, ssize_t count)
{
- struct rd_dev *rd_dev = se_dev->se_dev_su_ptr;
+ struct rd_dev *rd_dev = RD_DEV(dev);
char *orig, *ptr, *opts;
substring_t args[MAX_OPT_ARGS];
int ret = 0, arg, token;
@@ -947,7 +551,7 @@ static ssize_t rd_set_configfs_dev_params(
orig = opts;
- while ((ptr = strsep(&opts, ",")) != NULL) {
+ while ((ptr = strsep(&opts, ",\n")) != NULL) {
if (!*ptr)
continue;
@@ -956,10 +560,18 @@ static ssize_t rd_set_configfs_dev_params(
case Opt_rd_pages:
match_int(args, &arg);
rd_dev->rd_page_count = arg;
- printk(KERN_INFO "RAMDISK: Referencing Page"
+ pr_debug("RAMDISK: Referencing Page"
" Count: %u\n", rd_dev->rd_page_count);
rd_dev->rd_flags |= RDF_HAS_PAGE_COUNT;
break;
+ case Opt_rd_nullio:
+ match_int(args, &arg);
+ if (arg != 1)
+ break;
+
+ pr_debug("RAMDISK: Setting NULLIO flag: %d\n", arg);
+ rd_dev->rd_flags |= RDF_NULLIO;
+ break;
default:
break;
}
@@ -969,115 +581,82 @@ static ssize_t rd_set_configfs_dev_params(
return (!ret) ? count : ret;
}
-static ssize_t rd_check_configfs_dev_params(struct se_hba *hba, struct se_subsystem_dev *se_dev)
+static ssize_t rd_show_configfs_dev_params(struct se_device *dev, char *b)
{
- struct rd_dev *rd_dev = se_dev->se_dev_su_ptr;
-
- if (!(rd_dev->rd_flags & RDF_HAS_PAGE_COUNT)) {
- printk(KERN_INFO "Missing rd_pages= parameter\n");
- return -1;
- }
-
- return 0;
-}
+ struct rd_dev *rd_dev = RD_DEV(dev);
-static ssize_t rd_show_configfs_dev_params(
- struct se_hba *hba,
- struct se_subsystem_dev *se_dev,
- char *b)
-{
- struct rd_dev *rd_dev = se_dev->se_dev_su_ptr;
- ssize_t bl = sprintf(b, "TCM RamDisk ID: %u RamDisk Makeup: %s\n",
- rd_dev->rd_dev_id, (rd_dev->rd_direct) ?
- "rd_direct" : "rd_mcp");
+ ssize_t bl = sprintf(b, "TCM RamDisk ID: %u RamDisk Makeup: rd_mcp\n",
+ rd_dev->rd_dev_id);
bl += sprintf(b + bl, " PAGES/PAGE_SIZE: %u*%lu"
- " SG_table_count: %u\n", rd_dev->rd_page_count,
- PAGE_SIZE, rd_dev->sg_table_count);
+ " SG_table_count: %u nullio: %d\n", rd_dev->rd_page_count,
+ PAGE_SIZE, rd_dev->sg_table_count,
+ !!(rd_dev->rd_flags & RDF_NULLIO));
return bl;
}
-/* rd_get_cdb(): (Part of se_subsystem_api_t template)
- *
- *
- */
-static unsigned char *rd_get_cdb(struct se_task *task)
+static sector_t rd_get_blocks(struct se_device *dev)
{
- struct rd_request *req = RD_REQ(task);
+ struct rd_dev *rd_dev = RD_DEV(dev);
- return req->rd_scsi_cdb;
-}
+ unsigned long long blocks_long = ((rd_dev->rd_page_count * PAGE_SIZE) /
+ dev->dev_attrib.block_size) - 1;
-static u32 rd_get_device_rev(struct se_device *dev)
-{
- return SCSI_SPC_2; /* Returns SPC-3 in Initiator Data */
+ return blocks_long;
}
-static u32 rd_get_device_type(struct se_device *dev)
+static int rd_init_prot(struct se_device *dev)
{
- return TYPE_DISK;
+ struct rd_dev *rd_dev = RD_DEV(dev);
+
+ if (!dev->dev_attrib.pi_prot_type)
+ return 0;
+
+ return rd_build_prot_space(rd_dev, dev->prot_length,
+ dev->dev_attrib.block_size);
}
-static sector_t rd_get_blocks(struct se_device *dev)
+static void rd_free_prot(struct se_device *dev)
{
- struct rd_dev *rd_dev = dev->dev_ptr;
- unsigned long long blocks_long = ((rd_dev->rd_page_count * PAGE_SIZE) /
- DEV_ATTRIB(dev)->block_size) - 1;
+ struct rd_dev *rd_dev = RD_DEV(dev);
- return blocks_long;
+ rd_release_prot_space(rd_dev);
}
-static struct se_subsystem_api rd_dr_template = {
- .name = "rd_dr",
- .transport_type = TRANSPORT_PLUGIN_VHBA_VDEV,
- .attach_hba = rd_attach_hba,
- .detach_hba = rd_detach_hba,
- .allocate_virtdevice = rd_DIRECT_allocate_virtdevice,
- .create_virtdevice = rd_DIRECT_create_virtdevice,
- .free_device = rd_free_device,
- .alloc_task = rd_alloc_task,
- .do_task = rd_DIRECT_do_task,
- .free_task = rd_free_task,
- .check_configfs_dev_params = rd_check_configfs_dev_params,
- .set_configfs_dev_params = rd_set_configfs_dev_params,
- .show_configfs_dev_params = rd_show_configfs_dev_params,
- .get_cdb = rd_get_cdb,
- .get_device_rev = rd_get_device_rev,
- .get_device_type = rd_get_device_type,
- .get_blocks = rd_get_blocks,
- .do_se_mem_map = rd_DIRECT_do_se_mem_map,
+static struct sbc_ops rd_sbc_ops = {
+ .execute_rw = rd_execute_rw,
};
+static sense_reason_t
+rd_parse_cdb(struct se_cmd *cmd)
+{
+ return sbc_parse_cdb(cmd, &rd_sbc_ops);
+}
+
static struct se_subsystem_api rd_mcp_template = {
.name = "rd_mcp",
+ .inquiry_prod = "RAMDISK-MCP",
+ .inquiry_rev = RD_MCP_VERSION,
.transport_type = TRANSPORT_PLUGIN_VHBA_VDEV,
.attach_hba = rd_attach_hba,
.detach_hba = rd_detach_hba,
- .allocate_virtdevice = rd_MEMCPY_allocate_virtdevice,
- .create_virtdevice = rd_MEMCPY_create_virtdevice,
+ .alloc_device = rd_alloc_device,
+ .configure_device = rd_configure_device,
.free_device = rd_free_device,
- .alloc_task = rd_alloc_task,
- .do_task = rd_MEMCPY_do_task,
- .free_task = rd_free_task,
- .check_configfs_dev_params = rd_check_configfs_dev_params,
+ .parse_cdb = rd_parse_cdb,
.set_configfs_dev_params = rd_set_configfs_dev_params,
.show_configfs_dev_params = rd_show_configfs_dev_params,
- .get_cdb = rd_get_cdb,
- .get_device_rev = rd_get_device_rev,
- .get_device_type = rd_get_device_type,
+ .get_device_type = sbc_get_device_type,
.get_blocks = rd_get_blocks,
+ .init_prot = rd_init_prot,
+ .free_prot = rd_free_prot,
};
int __init rd_module_init(void)
{
int ret;
- ret = transport_subsystem_register(&rd_dr_template);
- if (ret < 0)
- return ret;
-
ret = transport_subsystem_register(&rd_mcp_template);
if (ret < 0) {
- transport_subsystem_release(&rd_dr_template);
return ret;
}
@@ -1086,6 +665,5 @@ int __init rd_module_init(void)
void rd_module_exit(void)
{
- transport_subsystem_release(&rd_dr_template);
transport_subsystem_release(&rd_mcp_template);
}
diff --git a/drivers/target/target_core_rd.h b/drivers/target/target_core_rd.h
index 13badfbaf9c..cc46a6a89b3 100644
--- a/drivers/target/target_core_rd.h
+++ b/drivers/target/target_core_rd.h
@@ -2,44 +2,18 @@
#define TARGET_CORE_RD_H
#define RD_HBA_VERSION "v4.0"
-#define RD_DR_VERSION "4.0"
#define RD_MCP_VERSION "4.0"
/* Largest piece of memory kmalloc can allocate */
#define RD_MAX_ALLOCATION_SIZE 65536
-/* Maximum queuedepth for the Ramdisk HBA */
-#define RD_HBA_QUEUE_DEPTH 256
#define RD_DEVICE_QUEUE_DEPTH 32
#define RD_MAX_DEVICE_QUEUE_DEPTH 128
#define RD_BLOCKSIZE 512
-#define RD_MAX_SECTORS 1024
-
-extern struct kmem_cache *se_mem_cache;
/* Used in target_core_init_configfs() for virtual LUN 0 access */
int __init rd_module_init(void);
void rd_module_exit(void);
-#define RRF_EMULATE_CDB 0x01
-#define RRF_GOT_LBA 0x02
-
-struct rd_request {
- struct se_task rd_task;
-
- /* SCSI CDB from iSCSI Command PDU */
- unsigned char rd_scsi_cdb[TCM_MAX_COMMAND_SIZE];
- /* Offset from start of page */
- u32 rd_offset;
- /* Starting page in Ramdisk for request */
- u32 rd_page;
- /* Total number of pages needed for request */
- u32 rd_page_count;
- /* Scatterlist count */
- u32 rd_size;
- /* Ramdisk device */
- struct rd_dev *rd_dev;
-} ____cacheline_aligned;
-
struct rd_dev_sg_table {
u32 page_start_offset;
u32 page_end_offset;
@@ -48,9 +22,10 @@ struct rd_dev_sg_table {
} ____cacheline_aligned;
#define RDF_HAS_PAGE_COUNT 0x01
+#define RDF_NULLIO 0x02
struct rd_dev {
- int rd_direct;
+ struct se_device dev;
u32 rd_flags;
/* Unique Ramdisk Device ID in Ramdisk HBA */
u32 rd_dev_id;
@@ -58,9 +33,12 @@ struct rd_dev {
u32 rd_page_count;
/* Number of SG tables in sg_table_array */
u32 sg_table_count;
- u32 rd_queue_depth;
+ /* Number of SG tables in sg_prot_array */
+ u32 sg_prot_count;
/* Array of rd_dev_sg_table_t containing scatterlists */
struct rd_dev_sg_table *sg_table_array;
+ /* Array of rd_dev_sg_table containing protection scatterlists */
+ struct rd_dev_sg_table *sg_prot_array;
/* Ramdisk HBA device is connected to */
struct rd_host *rd_host;
} ____cacheline_aligned;
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
new file mode 100644
index 00000000000..bd78d9235ac
--- /dev/null
+++ b/drivers/target/target_core_sbc.c
@@ -0,0 +1,1336 @@
+/*
+ * SCSI Block Commands (SBC) parsing and emulation.
+ *
+ * (c) Copyright 2002-2013 Datera, Inc.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+#include <linux/crc-t10dif.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+
+#include "target_core_internal.h"
+#include "target_core_ua.h"
+#include "target_core_alua.h"
+
+static sense_reason_t
+sbc_emulate_readcapacity(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ unsigned char *cdb = cmd->t_task_cdb;
+ unsigned long long blocks_long = dev->transport->get_blocks(dev);
+ unsigned char *rbuf;
+ unsigned char buf[8];
+ u32 blocks;
+
+ /*
+ * SBC-2 says:
+ * If the PMI bit is set to zero and the LOGICAL BLOCK
+ * ADDRESS field is not set to zero, the device server shall
+ * terminate the command with CHECK CONDITION status with
+ * the sense key set to ILLEGAL REQUEST and the additional
+ * sense code set to INVALID FIELD IN CDB.
+ *
+ * In SBC-3, these fields are obsolete, but some SCSI
+ * compliance tests actually check this, so we might as well
+ * follow SBC-2.
+ */
+ if (!(cdb[8] & 1) && !!(cdb[2] | cdb[3] | cdb[4] | cdb[5]))
+ return TCM_INVALID_CDB_FIELD;
+
+ if (blocks_long >= 0x00000000ffffffff)
+ blocks = 0xffffffff;
+ else
+ blocks = (u32)blocks_long;
+
+ buf[0] = (blocks >> 24) & 0xff;
+ buf[1] = (blocks >> 16) & 0xff;
+ buf[2] = (blocks >> 8) & 0xff;
+ buf[3] = blocks & 0xff;
+ buf[4] = (dev->dev_attrib.block_size >> 24) & 0xff;
+ buf[5] = (dev->dev_attrib.block_size >> 16) & 0xff;
+ buf[6] = (dev->dev_attrib.block_size >> 8) & 0xff;
+ buf[7] = dev->dev_attrib.block_size & 0xff;
+
+ rbuf = transport_kmap_data_sg(cmd);
+ if (rbuf) {
+ memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length));
+ transport_kunmap_data_sg(cmd);
+ }
+
+ target_complete_cmd_with_length(cmd, GOOD, 8);
+ return 0;
+}
+
+static sense_reason_t
+sbc_emulate_readcapacity_16(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
+ unsigned char *rbuf;
+ unsigned char buf[32];
+ unsigned long long blocks = dev->transport->get_blocks(dev);
+
+ memset(buf, 0, sizeof(buf));
+ 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->dev_attrib.block_size >> 24) & 0xff;
+ buf[9] = (dev->dev_attrib.block_size >> 16) & 0xff;
+ buf[10] = (dev->dev_attrib.block_size >> 8) & 0xff;
+ buf[11] = dev->dev_attrib.block_size & 0xff;
+ /*
+ * Set P_TYPE and PROT_EN bits for DIF support
+ */
+ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) {
+ if (dev->dev_attrib.pi_prot_type)
+ buf[12] = (dev->dev_attrib.pi_prot_type - 1) << 1 | 0x1;
+ }
+
+ if (dev->transport->get_lbppbe)
+ buf[13] = dev->transport->get_lbppbe(dev) & 0x0f;
+
+ if (dev->transport->get_alignment_offset_lbas) {
+ u16 lalba = dev->transport->get_alignment_offset_lbas(dev);
+ buf[14] = (lalba >> 8) & 0x3f;
+ buf[15] = lalba & 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->dev_attrib.emulate_tpu || dev->dev_attrib.emulate_tpws)
+ buf[14] |= 0x80;
+
+ rbuf = transport_kmap_data_sg(cmd);
+ if (rbuf) {
+ memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length));
+ transport_kunmap_data_sg(cmd);
+ }
+
+ target_complete_cmd_with_length(cmd, GOOD, 32);
+ return 0;
+}
+
+sector_t sbc_get_write_same_sectors(struct se_cmd *cmd)
+{
+ u32 num_blocks;
+
+ if (cmd->t_task_cdb[0] == WRITE_SAME)
+ num_blocks = get_unaligned_be16(&cmd->t_task_cdb[7]);
+ else if (cmd->t_task_cdb[0] == WRITE_SAME_16)
+ num_blocks = get_unaligned_be32(&cmd->t_task_cdb[10]);
+ else /* WRITE_SAME_32 via VARIABLE_LENGTH_CMD */
+ num_blocks = get_unaligned_be32(&cmd->t_task_cdb[28]);
+
+ /*
+ * Use the explicit range when non zero is supplied, otherwise calculate
+ * the remaining range based on ->get_blocks() - starting LBA.
+ */
+ if (num_blocks)
+ return num_blocks;
+
+ return cmd->se_dev->transport->get_blocks(cmd->se_dev) -
+ cmd->t_task_lba + 1;
+}
+EXPORT_SYMBOL(sbc_get_write_same_sectors);
+
+static sense_reason_t
+sbc_emulate_noop(struct se_cmd *cmd)
+{
+ target_complete_cmd(cmd, GOOD);
+ return 0;
+}
+
+static inline u32 sbc_get_size(struct se_cmd *cmd, u32 sectors)
+{
+ return cmd->se_dev->dev_attrib.block_size * sectors;
+}
+
+static inline u32 transport_get_sectors_6(unsigned char *cdb)
+{
+ /*
+ * Use 8-bit sector value. SBC-3 says:
+ *
+ * A TRANSFER LENGTH field set to zero specifies that 256
+ * logical blocks shall be written. Any other value
+ * specifies the number of logical blocks that shall be
+ * written.
+ */
+ return cdb[4] ? : 256;
+}
+
+static inline u32 transport_get_sectors_10(unsigned char *cdb)
+{
+ return (u32)(cdb[7] << 8) + cdb[8];
+}
+
+static inline u32 transport_get_sectors_12(unsigned char *cdb)
+{
+ return (u32)(cdb[6] << 24) + (cdb[7] << 16) + (cdb[8] << 8) + cdb[9];
+}
+
+static inline u32 transport_get_sectors_16(unsigned char *cdb)
+{
+ return (u32)(cdb[10] << 24) + (cdb[11] << 16) +
+ (cdb[12] << 8) + cdb[13];
+}
+
+/*
+ * Used for VARIABLE_LENGTH_CDB WRITE_32 and READ_32 variants
+ */
+static inline u32 transport_get_sectors_32(unsigned char *cdb)
+{
+ return (u32)(cdb[28] << 24) + (cdb[29] << 16) +
+ (cdb[30] << 8) + cdb[31];
+
+}
+
+static inline u32 transport_lba_21(unsigned char *cdb)
+{
+ return ((cdb[1] & 0x1f) << 16) | (cdb[2] << 8) | cdb[3];
+}
+
+static inline u32 transport_lba_32(unsigned char *cdb)
+{
+ return (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
+}
+
+static inline unsigned long long transport_lba_64(unsigned char *cdb)
+{
+ unsigned int __v1, __v2;
+
+ __v1 = (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
+ __v2 = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9];
+
+ return ((unsigned long long)__v2) | (unsigned long long)__v1 << 32;
+}
+
+/*
+ * For VARIABLE_LENGTH_CDB w/ 32 byte extended CDBs
+ */
+static inline unsigned long long transport_lba_64_ext(unsigned char *cdb)
+{
+ unsigned int __v1, __v2;
+
+ __v1 = (cdb[12] << 24) | (cdb[13] << 16) | (cdb[14] << 8) | cdb[15];
+ __v2 = (cdb[16] << 24) | (cdb[17] << 16) | (cdb[18] << 8) | cdb[19];
+
+ return ((unsigned long long)__v2) | (unsigned long long)__v1 << 32;
+}
+
+static sense_reason_t
+sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *ops)
+{
+ unsigned int sectors = sbc_get_write_same_sectors(cmd);
+
+ if ((flags[0] & 0x04) || (flags[0] & 0x02)) {
+ pr_err("WRITE_SAME PBDATA and LBDATA"
+ " bits not supported for Block Discard"
+ " Emulation\n");
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+ }
+ if (sectors > cmd->se_dev->dev_attrib.max_write_same_len) {
+ pr_warn("WRITE_SAME sectors: %u exceeds max_write_same_len: %u\n",
+ sectors, cmd->se_dev->dev_attrib.max_write_same_len);
+ return TCM_INVALID_CDB_FIELD;
+ }
+ /* We always have ANC_SUP == 0 so setting ANCHOR is always an error */
+ if (flags[0] & 0x10) {
+ pr_warn("WRITE SAME with ANCHOR not supported\n");
+ return TCM_INVALID_CDB_FIELD;
+ }
+ /*
+ * Special case for WRITE_SAME w/ UNMAP=1 that ends up getting
+ * translated into block discard requests within backend code.
+ */
+ if (flags[0] & 0x08) {
+ if (!ops->execute_write_same_unmap)
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+
+ cmd->execute_cmd = ops->execute_write_same_unmap;
+ return 0;
+ }
+ if (!ops->execute_write_same)
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+
+ cmd->execute_cmd = ops->execute_write_same;
+ return 0;
+}
+
+static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd)
+{
+ unsigned char *buf, *addr;
+ struct scatterlist *sg;
+ unsigned int offset;
+ sense_reason_t ret = TCM_NO_SENSE;
+ int i, count;
+ /*
+ * From sbc3r22.pdf section 5.48 XDWRITEREAD (10) command
+ *
+ * 1) read the specified logical block(s);
+ * 2) transfer logical blocks from the data-out buffer;
+ * 3) XOR the logical blocks transferred from the data-out buffer with
+ * the logical blocks read, storing the resulting XOR data in a buffer;
+ * 4) if the DISABLE WRITE bit is set to zero, then write the logical
+ * blocks transferred from the data-out buffer; and
+ * 5) transfer the resulting XOR data to the data-in buffer.
+ */
+ buf = kmalloc(cmd->data_length, GFP_KERNEL);
+ if (!buf) {
+ pr_err("Unable to allocate xor_callback buf\n");
+ return TCM_OUT_OF_RESOURCES;
+ }
+ /*
+ * Copy the scatterlist WRITE buffer located at cmd->t_data_sg
+ * into the locally allocated *buf
+ */
+ sg_copy_to_buffer(cmd->t_data_sg,
+ cmd->t_data_nents,
+ buf,
+ cmd->data_length);
+
+ /*
+ * Now perform the XOR against the BIDI read memory located at
+ * cmd->t_mem_bidi_list
+ */
+
+ offset = 0;
+ for_each_sg(cmd->t_bidi_data_sg, sg, cmd->t_bidi_data_nents, count) {
+ addr = kmap_atomic(sg_page(sg));
+ if (!addr) {
+ ret = TCM_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ for (i = 0; i < sg->length; i++)
+ *(addr + sg->offset + i) ^= *(buf + offset + i);
+
+ offset += sg->length;
+ kunmap_atomic(addr);
+ }
+
+out:
+ kfree(buf);
+ return ret;
+}
+
+static sense_reason_t
+sbc_execute_rw(struct se_cmd *cmd)
+{
+ return cmd->execute_rw(cmd, cmd->t_data_sg, cmd->t_data_nents,
+ cmd->data_direction);
+}
+
+static sense_reason_t compare_and_write_post(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+
+ /*
+ * Only set SCF_COMPARE_AND_WRITE_POST to force a response fall-through
+ * within target_complete_ok_work() if the command was successfully
+ * sent to the backend driver.
+ */
+ spin_lock_irq(&cmd->t_state_lock);
+ if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status)
+ cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST;
+ spin_unlock_irq(&cmd->t_state_lock);
+
+ /*
+ * Unlock ->caw_sem originally obtained during sbc_compare_and_write()
+ * before the original READ I/O submission.
+ */
+ up(&dev->caw_sem);
+
+ return TCM_NO_SENSE;
+}
+
+static sense_reason_t compare_and_write_callback(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct scatterlist *write_sg = NULL, *sg;
+ unsigned char *buf = NULL, *addr;
+ struct sg_mapping_iter m;
+ unsigned int offset = 0, len;
+ unsigned int nlbas = cmd->t_task_nolb;
+ unsigned int block_size = dev->dev_attrib.block_size;
+ unsigned int compare_len = (nlbas * block_size);
+ sense_reason_t ret = TCM_NO_SENSE;
+ int rc, i;
+
+ /*
+ * Handle early failure in transport_generic_request_failure(),
+ * which will not have taken ->caw_mutex yet..
+ */
+ if (!cmd->t_data_sg || !cmd->t_bidi_data_sg)
+ return TCM_NO_SENSE;
+ /*
+ * Immediately exit + release dev->caw_sem if command has already
+ * been failed with a non-zero SCSI status.
+ */
+ if (cmd->scsi_status) {
+ pr_err("compare_and_write_callback: non zero scsi_status:"
+ " 0x%02x\n", cmd->scsi_status);
+ goto out;
+ }
+
+ buf = kzalloc(cmd->data_length, GFP_KERNEL);
+ if (!buf) {
+ pr_err("Unable to allocate compare_and_write buf\n");
+ ret = TCM_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ write_sg = kmalloc(sizeof(struct scatterlist) * cmd->t_data_nents,
+ GFP_KERNEL);
+ if (!write_sg) {
+ pr_err("Unable to allocate compare_and_write sg\n");
+ ret = TCM_OUT_OF_RESOURCES;
+ goto out;
+ }
+ sg_init_table(write_sg, cmd->t_data_nents);
+ /*
+ * Setup verify and write data payloads from total NumberLBAs.
+ */
+ rc = sg_copy_to_buffer(cmd->t_data_sg, cmd->t_data_nents, buf,
+ cmd->data_length);
+ if (!rc) {
+ pr_err("sg_copy_to_buffer() failed for compare_and_write\n");
+ ret = TCM_OUT_OF_RESOURCES;
+ goto out;
+ }
+ /*
+ * Compare against SCSI READ payload against verify payload
+ */
+ for_each_sg(cmd->t_bidi_data_sg, sg, cmd->t_bidi_data_nents, i) {
+ addr = (unsigned char *)kmap_atomic(sg_page(sg));
+ if (!addr) {
+ ret = TCM_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ len = min(sg->length, compare_len);
+
+ if (memcmp(addr, buf + offset, len)) {
+ pr_warn("Detected MISCOMPARE for addr: %p buf: %p\n",
+ addr, buf + offset);
+ kunmap_atomic(addr);
+ goto miscompare;
+ }
+ kunmap_atomic(addr);
+
+ offset += len;
+ compare_len -= len;
+ if (!compare_len)
+ break;
+ }
+
+ i = 0;
+ len = cmd->t_task_nolb * block_size;
+ sg_miter_start(&m, cmd->t_data_sg, cmd->t_data_nents, SG_MITER_TO_SG);
+ /*
+ * Currently assumes NoLB=1 and SGLs are PAGE_SIZE..
+ */
+ while (len) {
+ sg_miter_next(&m);
+
+ if (block_size < PAGE_SIZE) {
+ sg_set_page(&write_sg[i], m.page, block_size,
+ block_size);
+ } else {
+ sg_miter_next(&m);
+ sg_set_page(&write_sg[i], m.page, block_size,
+ 0);
+ }
+ len -= block_size;
+ i++;
+ }
+ sg_miter_stop(&m);
+ /*
+ * Save the original SGL + nents values before updating to new
+ * assignments, to be released in transport_free_pages() ->
+ * transport_reset_sgl_orig()
+ */
+ cmd->t_data_sg_orig = cmd->t_data_sg;
+ cmd->t_data_sg = write_sg;
+ cmd->t_data_nents_orig = cmd->t_data_nents;
+ cmd->t_data_nents = 1;
+
+ cmd->sam_task_attr = MSG_HEAD_TAG;
+ cmd->transport_complete_callback = compare_and_write_post;
+ /*
+ * Now reset ->execute_cmd() to the normal sbc_execute_rw() handler
+ * for submitting the adjusted SGL to write instance user-data.
+ */
+ cmd->execute_cmd = sbc_execute_rw;
+
+ spin_lock_irq(&cmd->t_state_lock);
+ cmd->t_state = TRANSPORT_PROCESSING;
+ cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT;
+ spin_unlock_irq(&cmd->t_state_lock);
+
+ __target_execute_cmd(cmd);
+
+ kfree(buf);
+ return ret;
+
+miscompare:
+ pr_warn("Target/%s: Send MISCOMPARE check condition and sense\n",
+ dev->transport->name);
+ ret = TCM_MISCOMPARE_VERIFY;
+out:
+ /*
+ * In the MISCOMPARE or failure case, unlock ->caw_sem obtained in
+ * sbc_compare_and_write() before the original READ I/O submission.
+ */
+ up(&dev->caw_sem);
+ kfree(write_sg);
+ kfree(buf);
+ return ret;
+}
+
+static sense_reason_t
+sbc_compare_and_write(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ sense_reason_t ret;
+ int rc;
+ /*
+ * Submit the READ first for COMPARE_AND_WRITE to perform the
+ * comparision using SGLs at cmd->t_bidi_data_sg..
+ */
+ rc = down_interruptible(&dev->caw_sem);
+ if ((rc != 0) || signal_pending(current)) {
+ cmd->transport_complete_callback = NULL;
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+ /*
+ * Reset cmd->data_length to individual block_size in order to not
+ * confuse backend drivers that depend on this value matching the
+ * size of the I/O being submitted.
+ */
+ cmd->data_length = cmd->t_task_nolb * dev->dev_attrib.block_size;
+
+ ret = cmd->execute_rw(cmd, cmd->t_bidi_data_sg, cmd->t_bidi_data_nents,
+ DMA_FROM_DEVICE);
+ if (ret) {
+ cmd->transport_complete_callback = NULL;
+ up(&dev->caw_sem);
+ return ret;
+ }
+ /*
+ * Unlock of dev->caw_sem to occur in compare_and_write_callback()
+ * upon MISCOMPARE, or in compare_and_write_done() upon completion
+ * of WRITE instance user-data.
+ */
+ return TCM_NO_SENSE;
+}
+
+static int
+sbc_set_prot_op_checks(u8 protect, enum target_prot_type prot_type,
+ bool is_write, struct se_cmd *cmd)
+{
+ if (is_write) {
+ cmd->prot_op = protect ? TARGET_PROT_DOUT_PASS :
+ TARGET_PROT_DOUT_INSERT;
+ switch (protect) {
+ case 0x0:
+ case 0x3:
+ cmd->prot_checks = 0;
+ break;
+ case 0x1:
+ case 0x5:
+ cmd->prot_checks = TARGET_DIF_CHECK_GUARD;
+ if (prot_type == TARGET_DIF_TYPE1_PROT)
+ cmd->prot_checks |= TARGET_DIF_CHECK_REFTAG;
+ break;
+ case 0x2:
+ if (prot_type == TARGET_DIF_TYPE1_PROT)
+ cmd->prot_checks = TARGET_DIF_CHECK_REFTAG;
+ break;
+ case 0x4:
+ cmd->prot_checks = TARGET_DIF_CHECK_GUARD;
+ break;
+ default:
+ pr_err("Unsupported protect field %d\n", protect);
+ return -EINVAL;
+ }
+ } else {
+ cmd->prot_op = protect ? TARGET_PROT_DIN_PASS :
+ TARGET_PROT_DIN_STRIP;
+ switch (protect) {
+ case 0x0:
+ case 0x1:
+ case 0x5:
+ cmd->prot_checks = TARGET_DIF_CHECK_GUARD;
+ if (prot_type == TARGET_DIF_TYPE1_PROT)
+ cmd->prot_checks |= TARGET_DIF_CHECK_REFTAG;
+ break;
+ case 0x2:
+ if (prot_type == TARGET_DIF_TYPE1_PROT)
+ cmd->prot_checks = TARGET_DIF_CHECK_REFTAG;
+ break;
+ case 0x3:
+ cmd->prot_checks = 0;
+ break;
+ case 0x4:
+ cmd->prot_checks = TARGET_DIF_CHECK_GUARD;
+ break;
+ default:
+ pr_err("Unsupported protect field %d\n", protect);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static bool
+sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb,
+ u32 sectors, bool is_write)
+{
+ u8 protect = cdb[1] >> 5;
+
+ if ((!cmd->t_prot_sg || !cmd->t_prot_nents) && cmd->prot_pto)
+ return true;
+
+ switch (dev->dev_attrib.pi_prot_type) {
+ case TARGET_DIF_TYPE3_PROT:
+ cmd->reftag_seed = 0xffffffff;
+ break;
+ case TARGET_DIF_TYPE2_PROT:
+ if (protect)
+ return false;
+
+ cmd->reftag_seed = cmd->t_task_lba;
+ break;
+ case TARGET_DIF_TYPE1_PROT:
+ cmd->reftag_seed = cmd->t_task_lba;
+ break;
+ case TARGET_DIF_TYPE0_PROT:
+ default:
+ return true;
+ }
+
+ if (sbc_set_prot_op_checks(protect, dev->dev_attrib.pi_prot_type,
+ is_write, cmd))
+ return false;
+
+ cmd->prot_type = dev->dev_attrib.pi_prot_type;
+ cmd->prot_length = dev->prot_length * sectors;
+
+ /**
+ * In case protection information exists over the wire
+ * we modify command data length to describe pure data.
+ * The actual transfer length is data length + protection
+ * length
+ **/
+ if (protect)
+ cmd->data_length = sectors * dev->dev_attrib.block_size;
+
+ pr_debug("%s: prot_type=%d, data_length=%d, prot_length=%d "
+ "prot_op=%d prot_checks=%d\n",
+ __func__, cmd->prot_type, cmd->data_length, cmd->prot_length,
+ cmd->prot_op, cmd->prot_checks);
+
+ return true;
+}
+
+sense_reason_t
+sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
+{
+ struct se_device *dev = cmd->se_dev;
+ unsigned char *cdb = cmd->t_task_cdb;
+ unsigned int size;
+ u32 sectors = 0;
+ sense_reason_t ret;
+
+ switch (cdb[0]) {
+ case READ_6:
+ sectors = transport_get_sectors_6(cdb);
+ cmd->t_task_lba = transport_lba_21(cdb);
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+ cmd->execute_rw = ops->execute_rw;
+ cmd->execute_cmd = sbc_execute_rw;
+ break;
+ case READ_10:
+ sectors = transport_get_sectors_10(cdb);
+ cmd->t_task_lba = transport_lba_32(cdb);
+
+ if (!sbc_check_prot(dev, cmd, cdb, sectors, false))
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+ cmd->execute_rw = ops->execute_rw;
+ cmd->execute_cmd = sbc_execute_rw;
+ break;
+ case READ_12:
+ sectors = transport_get_sectors_12(cdb);
+ cmd->t_task_lba = transport_lba_32(cdb);
+
+ if (!sbc_check_prot(dev, cmd, cdb, sectors, false))
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+ cmd->execute_rw = ops->execute_rw;
+ cmd->execute_cmd = sbc_execute_rw;
+ break;
+ case READ_16:
+ sectors = transport_get_sectors_16(cdb);
+ cmd->t_task_lba = transport_lba_64(cdb);
+
+ if (!sbc_check_prot(dev, cmd, cdb, sectors, false))
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+ cmd->execute_rw = ops->execute_rw;
+ cmd->execute_cmd = sbc_execute_rw;
+ break;
+ case WRITE_6:
+ sectors = transport_get_sectors_6(cdb);
+ cmd->t_task_lba = transport_lba_21(cdb);
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+ cmd->execute_rw = ops->execute_rw;
+ cmd->execute_cmd = sbc_execute_rw;
+ break;
+ case WRITE_10:
+ case WRITE_VERIFY:
+ sectors = transport_get_sectors_10(cdb);
+ cmd->t_task_lba = transport_lba_32(cdb);
+
+ if (!sbc_check_prot(dev, cmd, cdb, sectors, true))
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+
+ if (cdb[1] & 0x8)
+ cmd->se_cmd_flags |= SCF_FUA;
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+ cmd->execute_rw = ops->execute_rw;
+ cmd->execute_cmd = sbc_execute_rw;
+ break;
+ case WRITE_12:
+ sectors = transport_get_sectors_12(cdb);
+ cmd->t_task_lba = transport_lba_32(cdb);
+
+ if (!sbc_check_prot(dev, cmd, cdb, sectors, true))
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+
+ if (cdb[1] & 0x8)
+ cmd->se_cmd_flags |= SCF_FUA;
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+ cmd->execute_rw = ops->execute_rw;
+ cmd->execute_cmd = sbc_execute_rw;
+ break;
+ case WRITE_16:
+ sectors = transport_get_sectors_16(cdb);
+ cmd->t_task_lba = transport_lba_64(cdb);
+
+ if (!sbc_check_prot(dev, cmd, cdb, sectors, true))
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+
+ if (cdb[1] & 0x8)
+ cmd->se_cmd_flags |= SCF_FUA;
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+ cmd->execute_rw = ops->execute_rw;
+ cmd->execute_cmd = sbc_execute_rw;
+ break;
+ case XDWRITEREAD_10:
+ if (cmd->data_direction != DMA_TO_DEVICE ||
+ !(cmd->se_cmd_flags & SCF_BIDI))
+ return TCM_INVALID_CDB_FIELD;
+ sectors = transport_get_sectors_10(cdb);
+
+ cmd->t_task_lba = transport_lba_32(cdb);
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+
+ /*
+ * Setup BIDI XOR callback to be run after I/O completion.
+ */
+ cmd->execute_rw = ops->execute_rw;
+ cmd->execute_cmd = sbc_execute_rw;
+ cmd->transport_complete_callback = &xdreadwrite_callback;
+ if (cdb[1] & 0x8)
+ cmd->se_cmd_flags |= SCF_FUA;
+ break;
+ case VARIABLE_LENGTH_CMD:
+ {
+ u16 service_action = get_unaligned_be16(&cdb[8]);
+ switch (service_action) {
+ case XDWRITEREAD_32:
+ sectors = transport_get_sectors_32(cdb);
+
+ /*
+ * Use WRITE_32 and READ_32 opcodes for the emulated
+ * XDWRITE_READ_32 logic.
+ */
+ cmd->t_task_lba = transport_lba_64_ext(cdb);
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
+
+ /*
+ * Setup BIDI XOR callback to be run during after I/O
+ * completion.
+ */
+ cmd->execute_rw = ops->execute_rw;
+ cmd->execute_cmd = sbc_execute_rw;
+ cmd->transport_complete_callback = &xdreadwrite_callback;
+ if (cdb[1] & 0x8)
+ cmd->se_cmd_flags |= SCF_FUA;
+ break;
+ case WRITE_SAME_32:
+ sectors = transport_get_sectors_32(cdb);
+ if (!sectors) {
+ pr_err("WSNZ=1, WRITE_SAME w/sectors=0 not"
+ " supported\n");
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ size = sbc_get_size(cmd, 1);
+ cmd->t_task_lba = get_unaligned_be64(&cdb[12]);
+
+ ret = sbc_setup_write_same(cmd, &cdb[10], ops);
+ if (ret)
+ return ret;
+ break;
+ default:
+ pr_err("VARIABLE_LENGTH_CMD service action"
+ " 0x%04x not supported\n", service_action);
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+ }
+ break;
+ }
+ case COMPARE_AND_WRITE:
+ sectors = cdb[13];
+ /*
+ * Currently enforce COMPARE_AND_WRITE for a single sector
+ */
+ if (sectors > 1) {
+ pr_err("COMPARE_AND_WRITE contains NoLB: %u greater"
+ " than 1\n", sectors);
+ return TCM_INVALID_CDB_FIELD;
+ }
+ /*
+ * Double size because we have two buffers, note that
+ * zero is not an error..
+ */
+ size = 2 * sbc_get_size(cmd, sectors);
+ cmd->t_task_lba = get_unaligned_be64(&cdb[2]);
+ cmd->t_task_nolb = sectors;
+ cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB | SCF_COMPARE_AND_WRITE;
+ cmd->execute_rw = ops->execute_rw;
+ cmd->execute_cmd = sbc_compare_and_write;
+ cmd->transport_complete_callback = compare_and_write_callback;
+ break;
+ case READ_CAPACITY:
+ size = READ_CAP_LEN;
+ cmd->execute_cmd = sbc_emulate_readcapacity;
+ break;
+ case SERVICE_ACTION_IN:
+ switch (cmd->t_task_cdb[1] & 0x1f) {
+ case SAI_READ_CAPACITY_16:
+ cmd->execute_cmd = sbc_emulate_readcapacity_16;
+ break;
+ case SAI_REPORT_REFERRALS:
+ cmd->execute_cmd = target_emulate_report_referrals;
+ break;
+ default:
+ pr_err("Unsupported SA: 0x%02x\n",
+ cmd->t_task_cdb[1] & 0x1f);
+ return TCM_INVALID_CDB_FIELD;
+ }
+ size = (cdb[10] << 24) | (cdb[11] << 16) |
+ (cdb[12] << 8) | cdb[13];
+ break;
+ case SYNCHRONIZE_CACHE:
+ case SYNCHRONIZE_CACHE_16:
+ if (cdb[0] == SYNCHRONIZE_CACHE) {
+ sectors = transport_get_sectors_10(cdb);
+ cmd->t_task_lba = transport_lba_32(cdb);
+ } else {
+ sectors = transport_get_sectors_16(cdb);
+ cmd->t_task_lba = transport_lba_64(cdb);
+ }
+ if (ops->execute_sync_cache) {
+ cmd->execute_cmd = ops->execute_sync_cache;
+ goto check_lba;
+ }
+ size = 0;
+ cmd->execute_cmd = sbc_emulate_noop;
+ break;
+ case UNMAP:
+ if (!ops->execute_unmap)
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+
+ size = get_unaligned_be16(&cdb[7]);
+ cmd->execute_cmd = ops->execute_unmap;
+ break;
+ case WRITE_SAME_16:
+ sectors = transport_get_sectors_16(cdb);
+ if (!sectors) {
+ pr_err("WSNZ=1, WRITE_SAME w/sectors=0 not supported\n");
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ size = sbc_get_size(cmd, 1);
+ cmd->t_task_lba = get_unaligned_be64(&cdb[2]);
+
+ ret = sbc_setup_write_same(cmd, &cdb[1], ops);
+ if (ret)
+ return ret;
+ break;
+ case WRITE_SAME:
+ sectors = transport_get_sectors_10(cdb);
+ if (!sectors) {
+ pr_err("WSNZ=1, WRITE_SAME w/sectors=0 not supported\n");
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ size = sbc_get_size(cmd, 1);
+ cmd->t_task_lba = get_unaligned_be32(&cdb[2]);
+
+ /*
+ * Follow sbcr26 with WRITE_SAME (10) and check for the existence
+ * of byte 1 bit 3 UNMAP instead of original reserved field
+ */
+ ret = sbc_setup_write_same(cmd, &cdb[1], ops);
+ if (ret)
+ return ret;
+ break;
+ case VERIFY:
+ size = 0;
+ sectors = transport_get_sectors_10(cdb);
+ cmd->t_task_lba = transport_lba_32(cdb);
+ cmd->execute_cmd = sbc_emulate_noop;
+ goto check_lba;
+ case REZERO_UNIT:
+ case SEEK_6:
+ case SEEK_10:
+ /*
+ * There are still clients out there which use these old SCSI-2
+ * commands. This mainly happens when running VMs with legacy
+ * guest systems, connected via SCSI command pass-through to
+ * iSCSI targets. Make them happy and return status GOOD.
+ */
+ size = 0;
+ cmd->execute_cmd = sbc_emulate_noop;
+ break;
+ default:
+ ret = spc_parse_cdb(cmd, &size);
+ if (ret)
+ return ret;
+ }
+
+ /* reject any command that we don't have a handler for */
+ if (!(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) && !cmd->execute_cmd)
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+
+ if (cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) {
+ unsigned long long end_lba;
+
+ if (sectors > dev->dev_attrib.fabric_max_sectors) {
+ printk_ratelimited(KERN_ERR "SCSI OP %02xh with too"
+ " big sectors %u exceeds fabric_max_sectors:"
+ " %u\n", cdb[0], sectors,
+ dev->dev_attrib.fabric_max_sectors);
+ return TCM_INVALID_CDB_FIELD;
+ }
+ if (sectors > dev->dev_attrib.hw_max_sectors) {
+ printk_ratelimited(KERN_ERR "SCSI OP %02xh with too"
+ " big sectors %u exceeds backend hw_max_sectors:"
+ " %u\n", cdb[0], sectors,
+ dev->dev_attrib.hw_max_sectors);
+ return TCM_INVALID_CDB_FIELD;
+ }
+check_lba:
+ end_lba = dev->transport->get_blocks(dev) + 1;
+ if (cmd->t_task_lba + sectors > end_lba) {
+ pr_err("cmd exceeds last lba %llu "
+ "(lba %llu, sectors %u)\n",
+ end_lba, cmd->t_task_lba, sectors);
+ return TCM_ADDRESS_OUT_OF_RANGE;
+ }
+
+ if (!(cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE))
+ size = sbc_get_size(cmd, sectors);
+ }
+
+ return target_cmd_size_check(cmd, size);
+}
+EXPORT_SYMBOL(sbc_parse_cdb);
+
+u32 sbc_get_device_type(struct se_device *dev)
+{
+ return TYPE_DISK;
+}
+EXPORT_SYMBOL(sbc_get_device_type);
+
+sense_reason_t
+sbc_execute_unmap(struct se_cmd *cmd,
+ sense_reason_t (*do_unmap_fn)(struct se_cmd *, void *,
+ sector_t, sector_t),
+ void *priv)
+{
+ struct se_device *dev = cmd->se_dev;
+ unsigned char *buf, *ptr = NULL;
+ sector_t lba;
+ int size;
+ u32 range;
+ sense_reason_t ret = 0;
+ int dl, bd_dl;
+
+ /* We never set ANC_SUP */
+ if (cmd->t_task_cdb[1])
+ return TCM_INVALID_CDB_FIELD;
+
+ if (cmd->data_length == 0) {
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
+ return 0;
+ }
+
+ if (cmd->data_length < 8) {
+ pr_warn("UNMAP parameter list length %u too small\n",
+ cmd->data_length);
+ return TCM_PARAMETER_LIST_LENGTH_ERROR;
+ }
+
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ dl = get_unaligned_be16(&buf[0]);
+ bd_dl = get_unaligned_be16(&buf[2]);
+
+ size = cmd->data_length - 8;
+ if (bd_dl > size)
+ pr_warn("UNMAP parameter list length %u too small, ignoring bd_dl %u\n",
+ cmd->data_length, bd_dl);
+ else
+ size = bd_dl;
+
+ if (size / 16 > dev->dev_attrib.max_unmap_block_desc_count) {
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto err;
+ }
+
+ /* First UNMAP block descriptor starts at 8 byte offset */
+ ptr = &buf[8];
+ pr_debug("UNMAP: Sub: %s Using dl: %u bd_dl: %u size: %u"
+ " ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr);
+
+ while (size >= 16) {
+ lba = get_unaligned_be64(&ptr[0]);
+ range = get_unaligned_be32(&ptr[8]);
+ pr_debug("UNMAP: Using lba: %llu and range: %u\n",
+ (unsigned long long)lba, range);
+
+ if (range > dev->dev_attrib.max_unmap_lba_count) {
+ ret = TCM_INVALID_PARAMETER_LIST;
+ goto err;
+ }
+
+ if (lba + range > dev->transport->get_blocks(dev) + 1) {
+ ret = TCM_ADDRESS_OUT_OF_RANGE;
+ goto err;
+ }
+
+ ret = do_unmap_fn(cmd, priv, lba, range);
+ if (ret)
+ goto err;
+
+ ptr += 16;
+ size -= 16;
+ }
+
+err:
+ transport_kunmap_data_sg(cmd);
+ if (!ret)
+ target_complete_cmd(cmd, GOOD);
+ return ret;
+}
+EXPORT_SYMBOL(sbc_execute_unmap);
+
+void
+sbc_dif_generate(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_dif_v1_tuple *sdt;
+ struct scatterlist *dsg, *psg = cmd->t_prot_sg;
+ sector_t sector = cmd->t_task_lba;
+ void *daddr, *paddr;
+ int i, j, offset = 0;
+
+ for_each_sg(cmd->t_data_sg, dsg, cmd->t_data_nents, i) {
+ daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
+ paddr = kmap_atomic(sg_page(psg)) + psg->offset;
+
+ for (j = 0; j < dsg->length; j += dev->dev_attrib.block_size) {
+
+ if (offset >= psg->length) {
+ kunmap_atomic(paddr);
+ psg = sg_next(psg);
+ paddr = kmap_atomic(sg_page(psg)) + psg->offset;
+ offset = 0;
+ }
+
+ sdt = paddr + offset;
+ sdt->guard_tag = cpu_to_be16(crc_t10dif(daddr + j,
+ dev->dev_attrib.block_size));
+ if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT)
+ sdt->ref_tag = cpu_to_be32(sector & 0xffffffff);
+ sdt->app_tag = 0;
+
+ pr_debug("DIF WRITE INSERT sector: %llu guard_tag: 0x%04x"
+ " app_tag: 0x%04x ref_tag: %u\n",
+ (unsigned long long)sector, sdt->guard_tag,
+ sdt->app_tag, be32_to_cpu(sdt->ref_tag));
+
+ sector++;
+ offset += sizeof(struct se_dif_v1_tuple);
+ }
+
+ kunmap_atomic(paddr);
+ kunmap_atomic(daddr);
+ }
+}
+
+static sense_reason_t
+sbc_dif_v1_verify(struct se_device *dev, struct se_dif_v1_tuple *sdt,
+ const void *p, sector_t sector, unsigned int ei_lba)
+{
+ int block_size = dev->dev_attrib.block_size;
+ __be16 csum;
+
+ csum = cpu_to_be16(crc_t10dif(p, block_size));
+
+ if (sdt->guard_tag != csum) {
+ pr_err("DIFv1 checksum failed on sector %llu guard tag 0x%04x"
+ " csum 0x%04x\n", (unsigned long long)sector,
+ be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum));
+ return TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED;
+ }
+
+ if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT &&
+ be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) {
+ pr_err("DIFv1 Type 1 reference failed on sector: %llu tag: 0x%08x"
+ " sector MSB: 0x%08x\n", (unsigned long long)sector,
+ be32_to_cpu(sdt->ref_tag), (u32)(sector & 0xffffffff));
+ return TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED;
+ }
+
+ if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE2_PROT &&
+ be32_to_cpu(sdt->ref_tag) != ei_lba) {
+ pr_err("DIFv1 Type 2 reference failed on sector: %llu tag: 0x%08x"
+ " ei_lba: 0x%08x\n", (unsigned long long)sector,
+ be32_to_cpu(sdt->ref_tag), ei_lba);
+ return TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED;
+ }
+
+ return 0;
+}
+
+static void
+sbc_dif_copy_prot(struct se_cmd *cmd, unsigned int sectors, bool read,
+ struct scatterlist *sg, int sg_off)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct scatterlist *psg;
+ void *paddr, *addr;
+ unsigned int i, len, left;
+ unsigned int offset = sg_off;
+
+ left = sectors * dev->prot_length;
+
+ for_each_sg(cmd->t_prot_sg, psg, cmd->t_prot_nents, i) {
+ unsigned int psg_len, copied = 0;
+
+ paddr = kmap_atomic(sg_page(psg)) + psg->offset;
+ psg_len = min(left, psg->length);
+ while (psg_len) {
+ len = min(psg_len, sg->length - offset);
+ addr = kmap_atomic(sg_page(sg)) + sg->offset + offset;
+
+ if (read)
+ memcpy(paddr + copied, addr, len);
+ else
+ memcpy(addr, paddr + copied, len);
+
+ left -= len;
+ offset += len;
+ copied += len;
+ psg_len -= len;
+
+ if (offset >= sg->length) {
+ sg = sg_next(sg);
+ offset = 0;
+ }
+ kunmap_atomic(addr);
+ }
+ kunmap_atomic(paddr);
+ }
+}
+
+sense_reason_t
+sbc_dif_verify_write(struct se_cmd *cmd, sector_t start, unsigned int sectors,
+ unsigned int ei_lba, struct scatterlist *sg, int sg_off)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_dif_v1_tuple *sdt;
+ struct scatterlist *dsg, *psg = cmd->t_prot_sg;
+ sector_t sector = start;
+ void *daddr, *paddr;
+ int i, j, offset = 0;
+ sense_reason_t rc;
+
+ for_each_sg(cmd->t_data_sg, dsg, cmd->t_data_nents, i) {
+ daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
+ paddr = kmap_atomic(sg_page(psg)) + psg->offset;
+
+ for (j = 0; j < dsg->length; j += dev->dev_attrib.block_size) {
+
+ if (offset >= psg->length) {
+ kunmap_atomic(paddr);
+ psg = sg_next(psg);
+ paddr = kmap_atomic(sg_page(psg)) + psg->offset;
+ offset = 0;
+ }
+
+ sdt = paddr + offset;
+
+ pr_debug("DIF WRITE sector: %llu guard_tag: 0x%04x"
+ " app_tag: 0x%04x ref_tag: %u\n",
+ (unsigned long long)sector, sdt->guard_tag,
+ sdt->app_tag, be32_to_cpu(sdt->ref_tag));
+
+ rc = sbc_dif_v1_verify(dev, sdt, daddr + j, sector,
+ ei_lba);
+ if (rc) {
+ kunmap_atomic(paddr);
+ kunmap_atomic(daddr);
+ cmd->bad_sector = sector;
+ return rc;
+ }
+
+ sector++;
+ ei_lba++;
+ offset += sizeof(struct se_dif_v1_tuple);
+ }
+
+ kunmap_atomic(paddr);
+ kunmap_atomic(daddr);
+ }
+ sbc_dif_copy_prot(cmd, sectors, false, sg, sg_off);
+
+ return 0;
+}
+EXPORT_SYMBOL(sbc_dif_verify_write);
+
+static sense_reason_t
+__sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors,
+ unsigned int ei_lba, struct scatterlist *sg, int sg_off)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_dif_v1_tuple *sdt;
+ struct scatterlist *dsg, *psg = sg;
+ sector_t sector = start;
+ void *daddr, *paddr;
+ int i, j, offset = sg_off;
+ sense_reason_t rc;
+
+ for_each_sg(cmd->t_data_sg, dsg, cmd->t_data_nents, i) {
+ daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
+ paddr = kmap_atomic(sg_page(psg)) + sg->offset;
+
+ for (j = 0; j < dsg->length; j += dev->dev_attrib.block_size) {
+
+ if (offset >= psg->length) {
+ kunmap_atomic(paddr);
+ psg = sg_next(psg);
+ paddr = kmap_atomic(sg_page(psg)) + psg->offset;
+ offset = 0;
+ }
+
+ sdt = paddr + offset;
+
+ pr_debug("DIF READ sector: %llu guard_tag: 0x%04x"
+ " app_tag: 0x%04x ref_tag: %u\n",
+ (unsigned long long)sector, sdt->guard_tag,
+ sdt->app_tag, be32_to_cpu(sdt->ref_tag));
+
+ if (sdt->app_tag == cpu_to_be16(0xffff)) {
+ sector++;
+ offset += sizeof(struct se_dif_v1_tuple);
+ continue;
+ }
+
+ rc = sbc_dif_v1_verify(dev, sdt, daddr + j, sector,
+ ei_lba);
+ if (rc) {
+ kunmap_atomic(paddr);
+ kunmap_atomic(daddr);
+ cmd->bad_sector = sector;
+ return rc;
+ }
+
+ sector++;
+ ei_lba++;
+ offset += sizeof(struct se_dif_v1_tuple);
+ }
+
+ kunmap_atomic(paddr);
+ kunmap_atomic(daddr);
+ }
+
+ return 0;
+}
+
+sense_reason_t
+sbc_dif_read_strip(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ u32 sectors = cmd->prot_length / dev->prot_length;
+
+ return __sbc_dif_verify_read(cmd, cmd->t_task_lba, sectors, 0,
+ cmd->t_prot_sg, 0);
+}
+
+sense_reason_t
+sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors,
+ unsigned int ei_lba, struct scatterlist *sg, int sg_off)
+{
+ sense_reason_t rc;
+
+ rc = __sbc_dif_verify_read(cmd, start, sectors, ei_lba, sg, sg_off);
+ if (rc)
+ return rc;
+
+ sbc_dif_copy_prot(cmd, sectors, true, sg, sg_off);
+ return 0;
+}
+EXPORT_SYMBOL(sbc_dif_verify_read);
diff --git a/drivers/target/target_core_scdb.c b/drivers/target/target_core_scdb.c
deleted file mode 100644
index dc6fed037ab..00000000000
--- a/drivers/target/target_core_scdb.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*******************************************************************************
- * Filename: target_core_scdb.c
- *
- * This file contains the generic target engine Split CDB related functions.
- *
- * Copyright (c) 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 <linux/net.h>
-#include <linux/string.h>
-#include <scsi/scsi.h>
-#include <asm/unaligned.h>
-
-#include <target/target_core_base.h>
-#include <target/target_core_transport.h>
-
-#include "target_core_scdb.h"
-
-/* split_cdb_XX_6():
- *
- * 21-bit LBA w/ 8-bit SECTORS
- */
-void split_cdb_XX_6(
- unsigned long long lba,
- u32 *sectors,
- unsigned char *cdb)
-{
- cdb[1] = (lba >> 16) & 0x1f;
- cdb[2] = (lba >> 8) & 0xff;
- cdb[3] = lba & 0xff;
- cdb[4] = *sectors & 0xff;
-}
-
-/* split_cdb_XX_10():
- *
- * 32-bit LBA w/ 16-bit SECTORS
- */
-void split_cdb_XX_10(
- unsigned long long lba,
- u32 *sectors,
- unsigned char *cdb)
-{
- put_unaligned_be32(lba, &cdb[2]);
- put_unaligned_be16(*sectors, &cdb[7]);
-}
-
-/* split_cdb_XX_12():
- *
- * 32-bit LBA w/ 32-bit SECTORS
- */
-void split_cdb_XX_12(
- unsigned long long lba,
- u32 *sectors,
- unsigned char *cdb)
-{
- put_unaligned_be32(lba, &cdb[2]);
- put_unaligned_be32(*sectors, &cdb[6]);
-}
-
-/* split_cdb_XX_16():
- *
- * 64-bit LBA w/ 32-bit SECTORS
- */
-void split_cdb_XX_16(
- unsigned long long lba,
- u32 *sectors,
- unsigned char *cdb)
-{
- put_unaligned_be64(lba, &cdb[2]);
- put_unaligned_be32(*sectors, &cdb[10]);
-}
-
-/*
- * split_cdb_XX_32():
- *
- * 64-bit LBA w/ 32-bit SECTORS such as READ_32, WRITE_32 and emulated XDWRITEREAD_32
- */
-void split_cdb_XX_32(
- unsigned long long lba,
- u32 *sectors,
- unsigned char *cdb)
-{
- put_unaligned_be64(lba, &cdb[12]);
- put_unaligned_be32(*sectors, &cdb[28]);
-}
diff --git a/drivers/target/target_core_scdb.h b/drivers/target/target_core_scdb.h
deleted file mode 100644
index 98cd1c01ed8..00000000000
--- a/drivers/target/target_core_scdb.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef TARGET_CORE_SCDB_H
-#define TARGET_CORE_SCDB_H
-
-extern void split_cdb_XX_6(unsigned long long, u32 *, unsigned char *);
-extern void split_cdb_XX_10(unsigned long long, u32 *, unsigned char *);
-extern void split_cdb_XX_12(unsigned long long, u32 *, unsigned char *);
-extern void split_cdb_XX_16(unsigned long long, u32 *, unsigned char *);
-extern void split_cdb_XX_32(unsigned long long, u32 *, unsigned char *);
-
-#endif /* TARGET_CORE_SCDB_H */
diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c
new file mode 100644
index 00000000000..6cd7222738f
--- /dev/null
+++ b/drivers/target/target_core_spc.c
@@ -0,0 +1,1445 @@
+/*
+ * SCSI Primary Commands (SPC) parsing and emulation.
+ *
+ * (c) Copyright 2002-2013 Datera, Inc.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <asm/unaligned.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+
+#include "target_core_internal.h"
+#include "target_core_alua.h"
+#include "target_core_pr.h"
+#include "target_core_ua.h"
+#include "target_core_xcopy.h"
+
+static void spc_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 explicit and/or implicit 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);
+}
+
+sense_reason_t
+spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char *buf)
+{
+ struct se_lun *lun = cmd->se_lun;
+ struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
+
+ /* Set RMB (removable media) for tape devices */
+ if (dev->transport->get_device_type(dev) == TYPE_TAPE)
+ buf[1] = 0x80;
+
+ buf[2] = 0x05; /* SPC-3 */
+
+ /*
+ * NORMACA and HISUP = 0, RESPONSE DATA FORMAT = 2
+ *
+ * SPC4 says:
+ * A RESPONSE DATA FORMAT field set to 2h indicates that the
+ * standard INQUIRY data is in the format defined in this
+ * standard. Response data format values less than 2h are
+ * obsolete. Response data format values greater than 2h are
+ * reserved.
+ */
+ buf[3] = 2;
+
+ /*
+ * Enable SCCS and TPGS fields for Emulated ALUA
+ */
+ spc_fill_alua_data(lun->lun_sep, buf);
+
+ /*
+ * Set Third-Party Copy (3PC) bit to indicate support for EXTENDED_COPY
+ */
+ if (dev->dev_attrib.emulate_3pc)
+ buf[5] |= 0x8;
+ /*
+ * Set Protection (PROTECT) bit when DIF has been enabled on the
+ * device, and the transport supports VERIFY + PASS.
+ */
+ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) {
+ if (dev->dev_attrib.pi_prot_type)
+ buf[5] |= 0x1;
+ }
+
+ buf[7] = 0x2; /* CmdQue=1 */
+
+ memcpy(&buf[8], "LIO-ORG ", 8);
+ memset(&buf[16], 0x20, 16);
+ memcpy(&buf[16], dev->t10_wwn.model,
+ min_t(size_t, strlen(dev->t10_wwn.model), 16));
+ memcpy(&buf[32], dev->t10_wwn.revision,
+ min_t(size_t, strlen(dev->t10_wwn.revision), 4));
+ buf[4] = 31; /* Set additional length to 31 */
+
+ return 0;
+}
+EXPORT_SYMBOL(spc_emulate_inquiry_std);
+
+/* unit serial number */
+static sense_reason_t
+spc_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf)
+{
+ struct se_device *dev = cmd->se_dev;
+ u16 len;
+
+ if (dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) {
+ len = sprintf(&buf[4], "%s", dev->t10_wwn.unit_serial);
+ len++; /* Extra Byte for NULL Terminator */
+ buf[3] = len;
+ }
+ return 0;
+}
+
+void spc_parse_naa_6h_vendor_specific(struct se_device *dev,
+ unsigned char *buf)
+{
+ unsigned char *p = &dev->t10_wwn.unit_serial[0];
+ int cnt;
+ bool next = true;
+
+ /*
+ * Generate up to 36 bits of VENDOR SPECIFIC IDENTIFIER starting on
+ * byte 3 bit 3-0 for NAA IEEE Registered Extended DESIGNATOR field
+ * format, followed by 64 bits of VENDOR SPECIFIC IDENTIFIER EXTENSION
+ * to complete the payload. These are based from VPD=0x80 PRODUCT SERIAL
+ * NUMBER set via vpd_unit_serial in target_core_configfs.c to ensure
+ * per device uniqeness.
+ */
+ for (cnt = 0; *p && cnt < 13; p++) {
+ int val = hex_to_bin(*p);
+
+ if (val < 0)
+ continue;
+
+ if (next) {
+ next = false;
+ buf[cnt++] |= val;
+ } else {
+ next = true;
+ buf[cnt] = val << 4;
+ }
+ }
+}
+
+/*
+ * Device identification VPD, for a complete list of
+ * DESIGNATOR TYPEs see spc4r17 Table 459.
+ */
+sense_reason_t
+spc_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_lun *lun = cmd->se_lun;
+ 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 *prod = &dev->t10_wwn.model[0];
+ u32 prod_len;
+ u32 unit_serial_len, off = 0;
+ u16 len = 0, id_len;
+
+ 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->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL))
+ goto check_t10_vend_desc;
+
+ /* CODE SET == Binary */
+ buf[off++] = 0x1;
+
+ /* Set ASSOCIATION == 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
+ */
+ spc_parse_naa_6h_vendor_specific(dev, &buf[off]);
+
+ 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->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) {
+ unit_serial_len = strlen(&dev->t10_wwn.unit_serial[0]);
+ unit_serial_len++; /* For NULL Terminator */
+
+ id_len += sprintf(&buf[off+12], "%s:%s", prod,
+ &dev->t10_wwn.unit_serial[0]);
+ }
+ buf[off] = 0x2; /* ASCII */
+ buf[off+1] = 0x1; /* T10 Vendor ID */
+ buf[off+2] = 0x0;
+ memcpy(&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
+ */
+ port = lun->lun_sep;
+ if (port) {
+ struct t10_alua_lu_gp *lu_gp;
+ u32 padding, scsi_name_len, scsi_target_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
+ */
+ buf[off] =
+ (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
+ buf[off++] |= 0x1; /* CODE SET == Binary */
+ buf[off] = 0x80; /* Set PIV=1 */
+ /* Set ASSOCIATION == 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
+ */
+ 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->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
+ buf[off++] |= 0x1; /* CODE SET == Binary */
+ buf[off] = 0x80; /* Set PIV=1 */
+ /* Set ASSOCIATION == 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:
+ 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:
+ buf[off] =
+ (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
+ buf[off++] |= 0x3; /* CODE SET == UTF-8 */
+ buf[off] = 0x80; /* Set PIV=1 */
+ /* Set ASSOCIATION == 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->se_tpg_tfo->tpg_get_tag(tpg);
+ scsi_name_len = sprintf(&buf[off], "%s,t,0x%04x",
+ tpg->se_tpg_tfo->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.
+ */
+ padding = ((-scsi_name_len) & 3);
+ if (padding)
+ scsi_name_len += padding;
+ if (scsi_name_len > 256)
+ scsi_name_len = 256;
+
+ buf[off-1] = scsi_name_len;
+ off += scsi_name_len;
+ /* Header size + Designation descriptor */
+ len += (scsi_name_len + 4);
+
+ /*
+ * Target device designator
+ */
+ buf[off] =
+ (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
+ buf[off++] |= 0x3; /* CODE SET == UTF-8 */
+ buf[off] = 0x80; /* Set PIV=1 */
+ /* Set ASSOCIATION == target device: 10b */
+ buf[off] |= 0x20;
+ /* 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>" in
+ * UTF-8 encoding.
+ */
+ scsi_target_len = sprintf(&buf[off], "%s",
+ tpg->se_tpg_tfo->tpg_get_wwn(tpg));
+ scsi_target_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.
+ */
+ padding = ((-scsi_target_len) & 3);
+ if (padding)
+ scsi_target_len += padding;
+ if (scsi_target_len > 256)
+ scsi_target_len = 256;
+
+ buf[off-1] = scsi_target_len;
+ off += scsi_target_len;
+
+ /* Header size + Designation descriptor */
+ len += (scsi_target_len + 4);
+ }
+ buf[2] = ((len >> 8) & 0xff);
+ buf[3] = (len & 0xff); /* Page Length for VPD 0x83 */
+ return 0;
+}
+EXPORT_SYMBOL(spc_emulate_evpd_83);
+
+static bool
+spc_check_dev_wce(struct se_device *dev)
+{
+ bool wce = false;
+
+ if (dev->transport->get_write_cache)
+ wce = dev->transport->get_write_cache(dev);
+ else if (dev->dev_attrib.emulate_write_cache > 0)
+ wce = true;
+
+ return wce;
+}
+
+/* Extended INQUIRY Data VPD Page */
+static sense_reason_t
+spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
+
+ buf[3] = 0x3c;
+ /*
+ * Set GRD_CHK + REF_CHK for TYPE1 protection, or GRD_CHK
+ * only for TYPE3 protection.
+ */
+ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) {
+ if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT)
+ buf[4] = 0x5;
+ else if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE3_PROT)
+ buf[4] = 0x4;
+ }
+
+ /* Set HEADSUP, ORDSUP, SIMPSUP */
+ buf[5] = 0x07;
+
+ /* If WriteCache emulation is enabled, set V_SUP */
+ if (spc_check_dev_wce(dev))
+ buf[6] = 0x01;
+ /* If an LBA map is present set R_SUP */
+ spin_lock(&cmd->se_dev->t10_alua.lba_map_lock);
+ if (!list_empty(&dev->t10_alua.lba_map_list))
+ buf[8] = 0x10;
+ spin_unlock(&cmd->se_dev->t10_alua.lba_map_lock);
+ return 0;
+}
+
+/* Block Limits VPD page */
+static sense_reason_t
+spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
+{
+ struct se_device *dev = cmd->se_dev;
+ u32 max_sectors;
+ int have_tp = 0;
+ int opt, min;
+
+ /*
+ * Following spc3r22 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->dev_attrib.emulate_tpu || dev->dev_attrib.emulate_tpws)
+ have_tp = 1;
+
+ buf[0] = dev->transport->get_device_type(dev);
+ buf[3] = have_tp ? 0x3c : 0x10;
+
+ /* Set WSNZ to 1 */
+ buf[4] = 0x01;
+ /*
+ * Set MAXIMUM COMPARE AND WRITE LENGTH
+ */
+ if (dev->dev_attrib.emulate_caw)
+ buf[5] = 0x01;
+
+ /*
+ * Set OPTIMAL TRANSFER LENGTH GRANULARITY
+ */
+ if (dev->transport->get_io_min && (min = dev->transport->get_io_min(dev)))
+ put_unaligned_be16(min / dev->dev_attrib.block_size, &buf[6]);
+ else
+ put_unaligned_be16(1, &buf[6]);
+
+ /*
+ * Set MAXIMUM TRANSFER LENGTH
+ */
+ max_sectors = min(dev->dev_attrib.fabric_max_sectors,
+ dev->dev_attrib.hw_max_sectors);
+ put_unaligned_be32(max_sectors, &buf[8]);
+
+ /*
+ * Set OPTIMAL TRANSFER LENGTH
+ */
+ if (dev->transport->get_io_opt && (opt = dev->transport->get_io_opt(dev)))
+ put_unaligned_be32(opt / dev->dev_attrib.block_size, &buf[12]);
+ else
+ put_unaligned_be32(dev->dev_attrib.optimal_sectors, &buf[12]);
+
+ /*
+ * Exit now if we don't support TP.
+ */
+ if (!have_tp)
+ goto max_write_same;
+
+ /*
+ * Set MAXIMUM UNMAP LBA COUNT
+ */
+ put_unaligned_be32(dev->dev_attrib.max_unmap_lba_count, &buf[20]);
+
+ /*
+ * Set MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT
+ */
+ put_unaligned_be32(dev->dev_attrib.max_unmap_block_desc_count,
+ &buf[24]);
+
+ /*
+ * Set OPTIMAL UNMAP GRANULARITY
+ */
+ put_unaligned_be32(dev->dev_attrib.unmap_granularity, &buf[28]);
+
+ /*
+ * UNMAP GRANULARITY ALIGNMENT
+ */
+ put_unaligned_be32(dev->dev_attrib.unmap_granularity_alignment,
+ &buf[32]);
+ if (dev->dev_attrib.unmap_granularity_alignment != 0)
+ buf[32] |= 0x80; /* Set the UGAVALID bit */
+
+ /*
+ * MAXIMUM WRITE SAME LENGTH
+ */
+max_write_same:
+ put_unaligned_be64(dev->dev_attrib.max_write_same_len, &buf[36]);
+
+ return 0;
+}
+
+/* Block Device Characteristics VPD page */
+static sense_reason_t
+spc_emulate_evpd_b1(struct se_cmd *cmd, unsigned char *buf)
+{
+ struct se_device *dev = cmd->se_dev;
+
+ buf[0] = dev->transport->get_device_type(dev);
+ buf[3] = 0x3c;
+ buf[5] = dev->dev_attrib.is_nonrot ? 1 : 0;
+
+ return 0;
+}
+
+/* Thin Provisioning VPD */
+static sense_reason_t
+spc_emulate_evpd_b2(struct se_cmd *cmd, unsigned char *buf)
+{
+ struct se_device *dev = cmd->se_dev;
+
+ /*
+ * From spc3r22 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);
+
+ /*
+ * 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->dev_attrib.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->dev_attrib.emulate_tpws != 0)
+ buf[5] |= 0x40;
+
+ return 0;
+}
+
+/* Referrals VPD page */
+static sense_reason_t
+spc_emulate_evpd_b3(struct se_cmd *cmd, unsigned char *buf)
+{
+ struct se_device *dev = cmd->se_dev;
+
+ buf[0] = dev->transport->get_device_type(dev);
+ buf[3] = 0x0c;
+ put_unaligned_be32(dev->t10_alua.lba_map_segment_size, &buf[8]);
+ put_unaligned_be32(dev->t10_alua.lba_map_segment_size, &buf[12]);
+
+ return 0;
+}
+
+static sense_reason_t
+spc_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf);
+
+static struct {
+ uint8_t page;
+ sense_reason_t (*emulate)(struct se_cmd *, unsigned char *);
+} evpd_handlers[] = {
+ { .page = 0x00, .emulate = spc_emulate_evpd_00 },
+ { .page = 0x80, .emulate = spc_emulate_evpd_80 },
+ { .page = 0x83, .emulate = spc_emulate_evpd_83 },
+ { .page = 0x86, .emulate = spc_emulate_evpd_86 },
+ { .page = 0xb0, .emulate = spc_emulate_evpd_b0 },
+ { .page = 0xb1, .emulate = spc_emulate_evpd_b1 },
+ { .page = 0xb2, .emulate = spc_emulate_evpd_b2 },
+ { .page = 0xb3, .emulate = spc_emulate_evpd_b3 },
+};
+
+/* supported vital product data pages */
+static sense_reason_t
+spc_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf)
+{
+ int p;
+
+ /*
+ * 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 (cmd->se_dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) {
+ buf[3] = ARRAY_SIZE(evpd_handlers);
+ for (p = 0; p < ARRAY_SIZE(evpd_handlers); ++p)
+ buf[p + 4] = evpd_handlers[p].page;
+ }
+
+ return 0;
+}
+
+static sense_reason_t
+spc_emulate_inquiry(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_portal_group *tpg = cmd->se_lun->lun_sep->sep_tpg;
+ unsigned char *rbuf;
+ unsigned char *cdb = cmd->t_task_cdb;
+ unsigned char *buf;
+ sense_reason_t ret;
+ int p;
+ int len = 0;
+
+ buf = kzalloc(SE_INQUIRY_BUF, GFP_KERNEL);
+ if (!buf) {
+ pr_err("Unable to allocate response buffer for INQUIRY\n");
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+
+ if (dev == tpg->tpg_virt_lun0.lun_se_dev)
+ buf[0] = 0x3f; /* Not connected */
+ else
+ buf[0] = dev->transport->get_device_type(dev);
+
+ if (!(cdb[1] & 0x1)) {
+ if (cdb[2]) {
+ pr_err("INQUIRY with EVPD==0 but PAGE CODE=%02x\n",
+ cdb[2]);
+ ret = TCM_INVALID_CDB_FIELD;
+ goto out;
+ }
+
+ ret = spc_emulate_inquiry_std(cmd, buf);
+ len = buf[4] + 5;
+ goto out;
+ }
+
+ for (p = 0; p < ARRAY_SIZE(evpd_handlers); ++p) {
+ if (cdb[2] == evpd_handlers[p].page) {
+ buf[1] = cdb[2];
+ ret = evpd_handlers[p].emulate(cmd, buf);
+ len = get_unaligned_be16(&buf[2]) + 4;
+ goto out;
+ }
+ }
+
+ pr_err("Unknown VPD Code: 0x%02x\n", cdb[2]);
+ ret = TCM_INVALID_CDB_FIELD;
+
+out:
+ rbuf = transport_kmap_data_sg(cmd);
+ if (rbuf) {
+ memcpy(rbuf, buf, min_t(u32, SE_INQUIRY_BUF, cmd->data_length));
+ transport_kunmap_data_sg(cmd);
+ }
+ kfree(buf);
+
+ if (!ret)
+ target_complete_cmd_with_length(cmd, GOOD, len);
+ return ret;
+}
+
+static int spc_modesense_rwrecovery(struct se_cmd *cmd, u8 pc, u8 *p)
+{
+ p[0] = 0x01;
+ p[1] = 0x0a;
+
+ /* No changeable values for now */
+ if (pc == 1)
+ goto out;
+
+out:
+ return 12;
+}
+
+static int spc_modesense_control(struct se_cmd *cmd, u8 pc, u8 *p)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
+
+ p[0] = 0x0a;
+ p[1] = 0x0a;
+
+ /* No changeable values for now */
+ if (pc == 1)
+ goto out;
+
+ p[2] = 2;
+ /*
+ * From spc4r23, 7.4.7 Control mode page
+ *
+ * The QUEUE ALGORITHM MODIFIER field (see table 368) specifies
+ * restrictions on the algorithm used for reordering commands
+ * having the SIMPLE task attribute (see SAM-4).
+ *
+ * Table 368 -- QUEUE ALGORITHM MODIFIER field
+ * Code Description
+ * 0h Restricted reordering
+ * 1h Unrestricted reordering allowed
+ * 2h to 7h Reserved
+ * 8h to Fh Vendor specific
+ *
+ * A value of zero in the QUEUE ALGORITHM MODIFIER field specifies that
+ * the device server shall order the processing sequence of commands
+ * having the SIMPLE task attribute such that data integrity is maintained
+ * for that I_T nexus (i.e., if the transmission of new SCSI transport protocol
+ * requests is halted at any time, the final value of all data observable
+ * on the medium shall be the same as if all the commands had been processed
+ * with the ORDERED task attribute).
+ *
+ * A value of one in the QUEUE ALGORITHM MODIFIER field specifies that the
+ * device server may reorder the processing sequence of commands having the
+ * SIMPLE task attribute in any manner. Any data integrity exposures related to
+ * command sequence order shall be explicitly handled by the application client
+ * through the selection of appropriate ommands and task attributes.
+ */
+ p[3] = (dev->dev_attrib.emulate_rest_reord == 1) ? 0x00 : 0x10;
+ /*
+ * 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->dev_attrib.emulate_ua_intlck_ctrl == 2) ? 0x30 :
+ (dev->dev_attrib.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->dev_attrib.emulate_tas) ? 0x40 : 0x00;
+ /*
+ * From spc4r30, section 7.5.7 Control mode page
+ *
+ * Application Tag Owner (ATO) bit set to one.
+ *
+ * If the ATO bit is set to one the device server shall not modify the
+ * LOGICAL BLOCK APPLICATION TAG field and, depending on the protection
+ * type, shall not modify the contents of the LOGICAL BLOCK REFERENCE
+ * TAG field.
+ */
+ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) {
+ if (dev->dev_attrib.pi_prot_type)
+ p[5] |= 0x80;
+ }
+
+ p[8] = 0xff;
+ p[9] = 0xff;
+ p[11] = 30;
+
+out:
+ return 12;
+}
+
+static int spc_modesense_caching(struct se_cmd *cmd, u8 pc, u8 *p)
+{
+ struct se_device *dev = cmd->se_dev;
+
+ p[0] = 0x08;
+ p[1] = 0x12;
+
+ /* No changeable values for now */
+ if (pc == 1)
+ goto out;
+
+ if (spc_check_dev_wce(dev))
+ p[2] = 0x04; /* Write Cache Enable */
+ p[12] = 0x20; /* Disabled Read Ahead */
+
+out:
+ return 20;
+}
+
+static int spc_modesense_informational_exceptions(struct se_cmd *cmd, u8 pc, unsigned char *p)
+{
+ p[0] = 0x1c;
+ p[1] = 0x0a;
+
+ /* No changeable values for now */
+ if (pc == 1)
+ goto out;
+
+out:
+ return 12;
+}
+
+static struct {
+ uint8_t page;
+ uint8_t subpage;
+ int (*emulate)(struct se_cmd *, u8, unsigned char *);
+} modesense_handlers[] = {
+ { .page = 0x01, .subpage = 0x00, .emulate = spc_modesense_rwrecovery },
+ { .page = 0x08, .subpage = 0x00, .emulate = spc_modesense_caching },
+ { .page = 0x0a, .subpage = 0x00, .emulate = spc_modesense_control },
+ { .page = 0x1c, .subpage = 0x00, .emulate = spc_modesense_informational_exceptions },
+};
+
+static void spc_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 spc_modesense_dpofua(unsigned char *buf, int type)
+{
+ switch (type) {
+ case TYPE_DISK:
+ buf[0] |= 0x10; /* DPOFUA bit */
+ break;
+ default:
+ break;
+ }
+}
+
+static int spc_modesense_blockdesc(unsigned char *buf, u64 blocks, u32 block_size)
+{
+ *buf++ = 8;
+ put_unaligned_be32(min(blocks, 0xffffffffull), buf);
+ buf += 4;
+ put_unaligned_be32(block_size, buf);
+ return 9;
+}
+
+static int spc_modesense_long_blockdesc(unsigned char *buf, u64 blocks, u32 block_size)
+{
+ if (blocks <= 0xffffffff)
+ return spc_modesense_blockdesc(buf + 3, blocks, block_size) + 3;
+
+ *buf++ = 1; /* LONGLBA */
+ buf += 2;
+ *buf++ = 16;
+ put_unaligned_be64(blocks, buf);
+ buf += 12;
+ put_unaligned_be32(block_size, buf);
+
+ return 17;
+}
+
+static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ char *cdb = cmd->t_task_cdb;
+ unsigned char buf[SE_MODE_PAGE_BUF], *rbuf;
+ int type = dev->transport->get_device_type(dev);
+ int ten = (cmd->t_task_cdb[0] == MODE_SENSE_10);
+ bool dbd = !!(cdb[1] & 0x08);
+ bool llba = ten ? !!(cdb[1] & 0x10) : false;
+ u8 pc = cdb[2] >> 6;
+ u8 page = cdb[2] & 0x3f;
+ u8 subpage = cdb[3];
+ int length = 0;
+ int ret;
+ int i;
+
+ memset(buf, 0, SE_MODE_PAGE_BUF);
+
+ /*
+ * Skip over MODE DATA LENGTH + MEDIUM TYPE fields to byte 3 for
+ * MODE_SENSE_10 and byte 2 for MODE_SENSE (6).
+ */
+ length = ten ? 3 : 2;
+
+ /* DEVICE-SPECIFIC PARAMETER */
+ if ((cmd->se_lun->lun_access & TRANSPORT_LUNFLAGS_READ_ONLY) ||
+ (cmd->se_deve &&
+ (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY)))
+ spc_modesense_write_protect(&buf[length], type);
+
+ if ((spc_check_dev_wce(dev)) &&
+ (dev->dev_attrib.emulate_fua_write > 0))
+ spc_modesense_dpofua(&buf[length], type);
+
+ ++length;
+
+ /* BLOCK DESCRIPTOR */
+
+ /*
+ * For now we only include a block descriptor for disk (SBC)
+ * devices; other command sets use a slightly different format.
+ */
+ if (!dbd && type == TYPE_DISK) {
+ u64 blocks = dev->transport->get_blocks(dev);
+ u32 block_size = dev->dev_attrib.block_size;
+
+ if (ten) {
+ if (llba) {
+ length += spc_modesense_long_blockdesc(&buf[length],
+ blocks, block_size);
+ } else {
+ length += 3;
+ length += spc_modesense_blockdesc(&buf[length],
+ blocks, block_size);
+ }
+ } else {
+ length += spc_modesense_blockdesc(&buf[length], blocks,
+ block_size);
+ }
+ } else {
+ if (ten)
+ length += 4;
+ else
+ length += 1;
+ }
+
+ if (page == 0x3f) {
+ if (subpage != 0x00 && subpage != 0xff) {
+ pr_warn("MODE_SENSE: Invalid subpage code: 0x%02x\n", subpage);
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(modesense_handlers); ++i) {
+ /*
+ * Tricky way to say all subpage 00h for
+ * subpage==0, all subpages for subpage==0xff
+ * (and we just checked above that those are
+ * the only two possibilities).
+ */
+ if ((modesense_handlers[i].subpage & ~subpage) == 0) {
+ ret = modesense_handlers[i].emulate(cmd, pc, &buf[length]);
+ if (!ten && length + ret >= 255)
+ break;
+ length += ret;
+ }
+ }
+
+ goto set_length;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(modesense_handlers); ++i)
+ if (modesense_handlers[i].page == page &&
+ modesense_handlers[i].subpage == subpage) {
+ length += modesense_handlers[i].emulate(cmd, pc, &buf[length]);
+ goto set_length;
+ }
+
+ /*
+ * We don't intend to implement:
+ * - obsolete page 03h "format parameters" (checked by Solaris)
+ */
+ if (page != 0x03)
+ pr_err("MODE SENSE: unimplemented page/subpage: 0x%02x/0x%02x\n",
+ page, subpage);
+
+ return TCM_UNKNOWN_MODE_PAGE;
+
+set_length:
+ if (ten)
+ put_unaligned_be16(length - 2, buf);
+ else
+ buf[0] = length - 1;
+
+ rbuf = transport_kmap_data_sg(cmd);
+ if (rbuf) {
+ memcpy(rbuf, buf, min_t(u32, SE_MODE_PAGE_BUF, cmd->data_length));
+ transport_kunmap_data_sg(cmd);
+ }
+
+ target_complete_cmd_with_length(cmd, GOOD, length);
+ return 0;
+}
+
+static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd)
+{
+ char *cdb = cmd->t_task_cdb;
+ bool ten = cdb[0] == MODE_SELECT_10;
+ int off = ten ? 8 : 4;
+ bool pf = !!(cdb[1] & 0x10);
+ u8 page, subpage;
+ unsigned char *buf;
+ unsigned char tbuf[SE_MODE_PAGE_BUF];
+ int length;
+ int ret = 0;
+ int i;
+
+ if (!cmd->data_length) {
+ target_complete_cmd(cmd, GOOD);
+ return 0;
+ }
+
+ if (cmd->data_length < off + 2)
+ return TCM_PARAMETER_LIST_LENGTH_ERROR;
+
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ if (!pf) {
+ ret = TCM_INVALID_CDB_FIELD;
+ goto out;
+ }
+
+ page = buf[off] & 0x3f;
+ subpage = buf[off] & 0x40 ? buf[off + 1] : 0;
+
+ for (i = 0; i < ARRAY_SIZE(modesense_handlers); ++i)
+ if (modesense_handlers[i].page == page &&
+ modesense_handlers[i].subpage == subpage) {
+ memset(tbuf, 0, SE_MODE_PAGE_BUF);
+ length = modesense_handlers[i].emulate(cmd, 0, tbuf);
+ goto check_contents;
+ }
+
+ ret = TCM_UNKNOWN_MODE_PAGE;
+ goto out;
+
+check_contents:
+ if (cmd->data_length < off + length) {
+ ret = TCM_PARAMETER_LIST_LENGTH_ERROR;
+ goto out;
+ }
+
+ if (memcmp(buf + off, tbuf, length))
+ ret = TCM_INVALID_PARAMETER_LIST;
+
+out:
+ transport_kunmap_data_sg(cmd);
+
+ if (!ret)
+ target_complete_cmd(cmd, GOOD);
+ return ret;
+}
+
+static sense_reason_t spc_emulate_request_sense(struct se_cmd *cmd)
+{
+ unsigned char *cdb = cmd->t_task_cdb;
+ unsigned char *rbuf;
+ u8 ua_asc = 0, ua_ascq = 0;
+ unsigned char buf[SE_SENSE_BUF];
+
+ memset(buf, 0, SE_SENSE_BUF);
+
+ if (cdb[1] & 0x01) {
+ pr_err("REQUEST_SENSE description emulation not"
+ " supported\n");
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ rbuf = transport_kmap_data_sg(cmd);
+ if (!rbuf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ 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;
+
+ /*
+ * 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;
+
+ /*
+ * NO ADDITIONAL SENSE INFORMATION
+ */
+ buf[SPC_ASC_KEY_OFFSET] = 0x00;
+ buf[7] = 0x0A;
+ }
+
+ memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length));
+ transport_kunmap_data_sg(cmd);
+
+ target_complete_cmd(cmd, GOOD);
+ return 0;
+}
+
+sense_reason_t spc_emulate_report_luns(struct se_cmd *cmd)
+{
+ struct se_dev_entry *deve;
+ struct se_session *sess = cmd->se_sess;
+ unsigned char *buf;
+ u32 lun_count = 0, offset = 8, i;
+
+ if (cmd->data_length < 16) {
+ pr_warn("REPORT LUNS allocation length %u too small\n",
+ cmd->data_length);
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ /*
+ * If no struct se_session pointer is present, this struct se_cmd is
+ * coming via a target_core_mod PASSTHROUGH op, and not through
+ * a $FABRIC_MOD. In that case, report LUN=0 only.
+ */
+ if (!sess) {
+ int_to_scsilun(0, (struct scsi_lun *)&buf[offset]);
+ lun_count = 1;
+ goto done;
+ }
+
+ spin_lock_irq(&sess->se_node_acl->device_list_lock);
+ for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
+ deve = sess->se_node_acl->device_list[i];
+ if (!(deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS))
+ continue;
+ /*
+ * We determine the correct LUN LIST LENGTH even once we
+ * have reached the initial allocation length.
+ * See SPC2-R20 7.19.
+ */
+ lun_count++;
+ if ((offset + 8) > cmd->data_length)
+ continue;
+
+ int_to_scsilun(deve->mapped_lun, (struct scsi_lun *)&buf[offset]);
+ offset += 8;
+ }
+ spin_unlock_irq(&sess->se_node_acl->device_list_lock);
+
+ /*
+ * See SPC3 r07, page 159.
+ */
+done:
+ lun_count *= 8;
+ buf[0] = ((lun_count >> 24) & 0xff);
+ buf[1] = ((lun_count >> 16) & 0xff);
+ buf[2] = ((lun_count >> 8) & 0xff);
+ buf[3] = (lun_count & 0xff);
+ transport_kunmap_data_sg(cmd);
+
+ target_complete_cmd_with_length(cmd, GOOD, 8 + lun_count * 8);
+ return 0;
+}
+EXPORT_SYMBOL(spc_emulate_report_luns);
+
+static sense_reason_t
+spc_emulate_testunitready(struct se_cmd *cmd)
+{
+ target_complete_cmd(cmd, GOOD);
+ return 0;
+}
+
+sense_reason_t
+spc_parse_cdb(struct se_cmd *cmd, unsigned int *size)
+{
+ struct se_device *dev = cmd->se_dev;
+ unsigned char *cdb = cmd->t_task_cdb;
+
+ switch (cdb[0]) {
+ case MODE_SELECT:
+ *size = cdb[4];
+ cmd->execute_cmd = spc_emulate_modeselect;
+ break;
+ case MODE_SELECT_10:
+ *size = (cdb[7] << 8) + cdb[8];
+ cmd->execute_cmd = spc_emulate_modeselect;
+ break;
+ case MODE_SENSE:
+ *size = cdb[4];
+ cmd->execute_cmd = spc_emulate_modesense;
+ break;
+ case MODE_SENSE_10:
+ *size = (cdb[7] << 8) + cdb[8];
+ cmd->execute_cmd = spc_emulate_modesense;
+ break;
+ case LOG_SELECT:
+ case LOG_SENSE:
+ *size = (cdb[7] << 8) + cdb[8];
+ break;
+ case PERSISTENT_RESERVE_IN:
+ *size = (cdb[7] << 8) + cdb[8];
+ cmd->execute_cmd = target_scsi3_emulate_pr_in;
+ break;
+ case PERSISTENT_RESERVE_OUT:
+ *size = (cdb[7] << 8) + cdb[8];
+ cmd->execute_cmd = target_scsi3_emulate_pr_out;
+ break;
+ case RELEASE:
+ case RELEASE_10:
+ if (cdb[0] == RELEASE_10)
+ *size = (cdb[7] << 8) | cdb[8];
+ else
+ *size = cmd->data_length;
+
+ cmd->execute_cmd = target_scsi2_reservation_release;
+ break;
+ case RESERVE:
+ case RESERVE_10:
+ /*
+ * The SPC-2 RESERVE does not contain a size in the SCSI CDB.
+ * Assume the passthrough or $FABRIC_MOD will tell us about it.
+ */
+ if (cdb[0] == RESERVE_10)
+ *size = (cdb[7] << 8) | cdb[8];
+ else
+ *size = cmd->data_length;
+
+ cmd->execute_cmd = target_scsi2_reservation_reserve;
+ break;
+ case REQUEST_SENSE:
+ *size = cdb[4];
+ cmd->execute_cmd = spc_emulate_request_sense;
+ break;
+ case INQUIRY:
+ *size = (cdb[3] << 8) + cdb[4];
+
+ /*
+ * Do implicit HEAD_OF_QUEUE processing for INQUIRY.
+ * See spc4r17 section 5.3
+ */
+ cmd->sam_task_attr = MSG_HEAD_TAG;
+ cmd->execute_cmd = spc_emulate_inquiry;
+ break;
+ case SECURITY_PROTOCOL_IN:
+ case SECURITY_PROTOCOL_OUT:
+ *size = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9];
+ break;
+ case EXTENDED_COPY:
+ *size = get_unaligned_be32(&cdb[10]);
+ cmd->execute_cmd = target_do_xcopy;
+ break;
+ case RECEIVE_COPY_RESULTS:
+ *size = get_unaligned_be32(&cdb[10]);
+ cmd->execute_cmd = target_do_receive_copy_results;
+ break;
+ case READ_ATTRIBUTE:
+ case WRITE_ATTRIBUTE:
+ *size = (cdb[10] << 24) | (cdb[11] << 16) |
+ (cdb[12] << 8) | cdb[13];
+ break;
+ case RECEIVE_DIAGNOSTIC:
+ case SEND_DIAGNOSTIC:
+ *size = (cdb[3] << 8) | cdb[4];
+ break;
+ case WRITE_BUFFER:
+ *size = (cdb[6] << 16) + (cdb[7] << 8) + cdb[8];
+ break;
+ case REPORT_LUNS:
+ cmd->execute_cmd = spc_emulate_report_luns;
+ *size = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9];
+ /*
+ * Do implicit HEAD_OF_QUEUE processing for REPORT_LUNS
+ * See spc4r17 section 5.3
+ */
+ cmd->sam_task_attr = MSG_HEAD_TAG;
+ break;
+ case TEST_UNIT_READY:
+ cmd->execute_cmd = spc_emulate_testunitready;
+ *size = 0;
+ break;
+ case MAINTENANCE_IN:
+ if (dev->transport->get_device_type(dev) != TYPE_ROM) {
+ /*
+ * MAINTENANCE_IN from SCC-2
+ * Check for emulated MI_REPORT_TARGET_PGS
+ */
+ if ((cdb[1] & 0x1f) == MI_REPORT_TARGET_PGS) {
+ cmd->execute_cmd =
+ target_emulate_report_target_port_groups;
+ }
+ *size = get_unaligned_be32(&cdb[6]);
+ } else {
+ /*
+ * GPCMD_SEND_KEY from multi media commands
+ */
+ *size = get_unaligned_be16(&cdb[8]);
+ }
+ break;
+ case MAINTENANCE_OUT:
+ if (dev->transport->get_device_type(dev) != TYPE_ROM) {
+ /*
+ * MAINTENANCE_OUT from SCC-2
+ * Check for emulated MO_SET_TARGET_PGS.
+ */
+ if (cdb[1] == MO_SET_TARGET_PGS) {
+ cmd->execute_cmd =
+ target_emulate_set_target_port_groups;
+ }
+ *size = get_unaligned_be32(&cdb[6]);
+ } else {
+ /*
+ * GPCMD_SEND_KEY from multi media commands
+ */
+ *size = get_unaligned_be16(&cdb[8]);
+ }
+ break;
+ default:
+ pr_warn("TARGET_CORE[%s]: Unsupported SCSI Opcode"
+ " 0x%02x, sending CHECK_CONDITION.\n",
+ cmd->se_tfo->get_fabric_name(), cdb[0]);
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(spc_parse_cdb);
diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c
new file mode 100644
index 00000000000..03538994d2f
--- /dev/null
+++ b/drivers/target/target_core_stat.c
@@ -0,0 +1,1635 @@
+/*******************************************************************************
+ * Filename: target_core_stat.c
+ *
+ * Modern ConfigFS group context specific statistics based on original
+ * target_core_mib.c code
+ *
+ * (c) Copyright 2006-2013 Datera, Inc.
+ *
+ * Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ******************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/utsname.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/configfs.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "target_core_internal.h"
+
+#ifndef INITIAL_JIFFIES
+#define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ))
+#endif
+
+#define NONE "None"
+#define ISPRINT(a) ((a >= ' ') && (a <= '~'))
+
+#define SCSI_LU_INDEX 1
+#define LU_COUNT 1
+
+/*
+ * SCSI Device Table
+ */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_dev, se_dev_stat_grps);
+#define DEV_STAT_SCSI_DEV_ATTR(_name, _mode) \
+static struct target_stat_scsi_dev_attribute \
+ target_stat_scsi_dev_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_dev_show_attr_##_name, \
+ target_stat_scsi_dev_store_attr_##_name);
+
+#define DEV_STAT_SCSI_DEV_ATTR_RO(_name) \
+static struct target_stat_scsi_dev_attribute \
+ target_stat_scsi_dev_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_dev_show_attr_##_name);
+
+static ssize_t target_stat_scsi_dev_show_attr_inst(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+ struct se_hba *hba = dev->se_hba;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index);
+}
+DEV_STAT_SCSI_DEV_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_dev_show_attr_indx(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index);
+}
+DEV_STAT_SCSI_DEV_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_dev_show_attr_role(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "Target\n");
+}
+DEV_STAT_SCSI_DEV_ATTR_RO(role);
+
+static ssize_t target_stat_scsi_dev_show_attr_ports(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", dev->dev_port_count);
+}
+DEV_STAT_SCSI_DEV_ATTR_RO(ports);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_dev, se_dev_stat_grps, scsi_dev_group);
+
+static struct configfs_attribute *target_stat_scsi_dev_attrs[] = {
+ &target_stat_scsi_dev_inst.attr,
+ &target_stat_scsi_dev_indx.attr,
+ &target_stat_scsi_dev_role.attr,
+ &target_stat_scsi_dev_ports.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_dev_attrib_ops = {
+ .show_attribute = target_stat_scsi_dev_attr_show,
+ .store_attribute = target_stat_scsi_dev_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_dev_cit = {
+ .ct_item_ops = &target_stat_scsi_dev_attrib_ops,
+ .ct_attrs = target_stat_scsi_dev_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * SCSI Target Device Table
+ */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_tgt_dev, se_dev_stat_grps);
+#define DEV_STAT_SCSI_TGT_DEV_ATTR(_name, _mode) \
+static struct target_stat_scsi_tgt_dev_attribute \
+ target_stat_scsi_tgt_dev_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_tgt_dev_show_attr_##_name, \
+ target_stat_scsi_tgt_dev_store_attr_##_name);
+
+#define DEV_STAT_SCSI_TGT_DEV_ATTR_RO(_name) \
+static struct target_stat_scsi_tgt_dev_attribute \
+ target_stat_scsi_tgt_dev_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_tgt_dev_show_attr_##_name);
+
+static ssize_t target_stat_scsi_tgt_dev_show_attr_inst(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+ struct se_hba *hba = dev->se_hba;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index);
+}
+DEV_STAT_SCSI_TGT_DEV_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_tgt_dev_show_attr_indx(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index);
+}
+DEV_STAT_SCSI_TGT_DEV_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_tgt_dev_show_attr_num_lus(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%u\n", LU_COUNT);
+}
+DEV_STAT_SCSI_TGT_DEV_ATTR_RO(num_lus);
+
+static ssize_t target_stat_scsi_tgt_dev_show_attr_status(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ if (dev->export_count)
+ return snprintf(page, PAGE_SIZE, "activated");
+ else
+ return snprintf(page, PAGE_SIZE, "deactivated");
+}
+DEV_STAT_SCSI_TGT_DEV_ATTR_RO(status);
+
+static ssize_t target_stat_scsi_tgt_dev_show_attr_non_access_lus(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+ int non_accessible_lus;
+
+ if (dev->export_count)
+ non_accessible_lus = 0;
+ else
+ non_accessible_lus = 1;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", non_accessible_lus);
+}
+DEV_STAT_SCSI_TGT_DEV_ATTR_RO(non_access_lus);
+
+static ssize_t target_stat_scsi_tgt_dev_show_attr_resets(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ return snprintf(page, PAGE_SIZE, "%lu\n",
+ atomic_long_read(&dev->num_resets));
+}
+DEV_STAT_SCSI_TGT_DEV_ATTR_RO(resets);
+
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_tgt_dev, se_dev_stat_grps, scsi_tgt_dev_group);
+
+static struct configfs_attribute *target_stat_scsi_tgt_dev_attrs[] = {
+ &target_stat_scsi_tgt_dev_inst.attr,
+ &target_stat_scsi_tgt_dev_indx.attr,
+ &target_stat_scsi_tgt_dev_num_lus.attr,
+ &target_stat_scsi_tgt_dev_status.attr,
+ &target_stat_scsi_tgt_dev_non_access_lus.attr,
+ &target_stat_scsi_tgt_dev_resets.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_tgt_dev_attrib_ops = {
+ .show_attribute = target_stat_scsi_tgt_dev_attr_show,
+ .store_attribute = target_stat_scsi_tgt_dev_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_tgt_dev_cit = {
+ .ct_item_ops = &target_stat_scsi_tgt_dev_attrib_ops,
+ .ct_attrs = target_stat_scsi_tgt_dev_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * SCSI Logical Unit Table
+ */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_lu, se_dev_stat_grps);
+#define DEV_STAT_SCSI_LU_ATTR(_name, _mode) \
+static struct target_stat_scsi_lu_attribute target_stat_scsi_lu_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_lu_show_attr_##_name, \
+ target_stat_scsi_lu_store_attr_##_name);
+
+#define DEV_STAT_SCSI_LU_ATTR_RO(_name) \
+static struct target_stat_scsi_lu_attribute target_stat_scsi_lu_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_lu_show_attr_##_name);
+
+static ssize_t target_stat_scsi_lu_show_attr_inst(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+ struct se_hba *hba = dev->se_hba;
+
+ return snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_lu_show_attr_dev(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ return snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(dev);
+
+static ssize_t target_stat_scsi_lu_show_attr_indx(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%u\n", SCSI_LU_INDEX);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_lu_show_attr_lun(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ /* FIXME: scsiLuDefaultLun */
+ return snprintf(page, PAGE_SIZE, "%llu\n", (unsigned long long)0);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(lun);
+
+static ssize_t target_stat_scsi_lu_show_attr_lu_name(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ /* scsiLuWwnName */
+ return snprintf(page, PAGE_SIZE, "%s\n",
+ (strlen(dev->t10_wwn.unit_serial)) ?
+ dev->t10_wwn.unit_serial : "None");
+}
+DEV_STAT_SCSI_LU_ATTR_RO(lu_name);
+
+static ssize_t target_stat_scsi_lu_show_attr_vend(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+ int i;
+ char str[sizeof(dev->t10_wwn.vendor)+1];
+
+ /* scsiLuVendorId */
+ for (i = 0; i < sizeof(dev->t10_wwn.vendor); i++)
+ str[i] = ISPRINT(dev->t10_wwn.vendor[i]) ?
+ dev->t10_wwn.vendor[i] : ' ';
+ str[i] = '\0';
+ return snprintf(page, PAGE_SIZE, "%s\n", str);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(vend);
+
+static ssize_t target_stat_scsi_lu_show_attr_prod(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+ int i;
+ char str[sizeof(dev->t10_wwn.model)+1];
+
+ /* scsiLuProductId */
+ for (i = 0; i < sizeof(dev->t10_wwn.vendor); i++)
+ str[i] = ISPRINT(dev->t10_wwn.model[i]) ?
+ dev->t10_wwn.model[i] : ' ';
+ str[i] = '\0';
+ return snprintf(page, PAGE_SIZE, "%s\n", str);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(prod);
+
+static ssize_t target_stat_scsi_lu_show_attr_rev(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+ int i;
+ char str[sizeof(dev->t10_wwn.revision)+1];
+
+ /* scsiLuRevisionId */
+ for (i = 0; i < sizeof(dev->t10_wwn.revision); i++)
+ str[i] = ISPRINT(dev->t10_wwn.revision[i]) ?
+ dev->t10_wwn.revision[i] : ' ';
+ str[i] = '\0';
+ return snprintf(page, PAGE_SIZE, "%s\n", str);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(rev);
+
+static ssize_t target_stat_scsi_lu_show_attr_dev_type(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ /* scsiLuPeripheralType */
+ return snprintf(page, PAGE_SIZE, "%u\n",
+ dev->transport->get_device_type(dev));
+}
+DEV_STAT_SCSI_LU_ATTR_RO(dev_type);
+
+static ssize_t target_stat_scsi_lu_show_attr_status(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ /* scsiLuStatus */
+ return snprintf(page, PAGE_SIZE, "%s\n",
+ (dev->export_count) ? "available" : "notavailable");
+}
+DEV_STAT_SCSI_LU_ATTR_RO(status);
+
+static ssize_t target_stat_scsi_lu_show_attr_state_bit(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ /* scsiLuState */
+ return snprintf(page, PAGE_SIZE, "exposed\n");
+}
+DEV_STAT_SCSI_LU_ATTR_RO(state_bit);
+
+static ssize_t target_stat_scsi_lu_show_attr_num_cmds(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ /* scsiLuNumCommands */
+ return snprintf(page, PAGE_SIZE, "%lu\n",
+ atomic_long_read(&dev->num_cmds));
+}
+DEV_STAT_SCSI_LU_ATTR_RO(num_cmds);
+
+static ssize_t target_stat_scsi_lu_show_attr_read_mbytes(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ /* scsiLuReadMegaBytes */
+ return snprintf(page, PAGE_SIZE, "%lu\n",
+ atomic_long_read(&dev->read_bytes) >> 20);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(read_mbytes);
+
+static ssize_t target_stat_scsi_lu_show_attr_write_mbytes(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ /* scsiLuWrittenMegaBytes */
+ return snprintf(page, PAGE_SIZE, "%lu\n",
+ atomic_long_read(&dev->write_bytes) >> 20);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(write_mbytes);
+
+static ssize_t target_stat_scsi_lu_show_attr_resets(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ /* scsiLuInResets */
+ return snprintf(page, PAGE_SIZE, "%lu\n", atomic_long_read(&dev->num_resets));
+}
+DEV_STAT_SCSI_LU_ATTR_RO(resets);
+
+static ssize_t target_stat_scsi_lu_show_attr_full_stat(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ /* FIXME: scsiLuOutTaskSetFullStatus */
+ return snprintf(page, PAGE_SIZE, "%u\n", 0);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(full_stat);
+
+static ssize_t target_stat_scsi_lu_show_attr_hs_num_cmds(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ /* FIXME: scsiLuHSInCommands */
+ return snprintf(page, PAGE_SIZE, "%u\n", 0);
+}
+DEV_STAT_SCSI_LU_ATTR_RO(hs_num_cmds);
+
+static ssize_t target_stat_scsi_lu_show_attr_creation_time(
+ struct se_dev_stat_grps *sgrps, char *page)
+{
+ struct se_device *dev =
+ container_of(sgrps, struct se_device, dev_stat_grps);
+
+ /* scsiLuCreationTime */
+ return snprintf(page, PAGE_SIZE, "%u\n", (u32)(((u32)dev->creation_time -
+ INITIAL_JIFFIES) * 100 / HZ));
+}
+DEV_STAT_SCSI_LU_ATTR_RO(creation_time);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_lu, se_dev_stat_grps, scsi_lu_group);
+
+static struct configfs_attribute *target_stat_scsi_lu_attrs[] = {
+ &target_stat_scsi_lu_inst.attr,
+ &target_stat_scsi_lu_dev.attr,
+ &target_stat_scsi_lu_indx.attr,
+ &target_stat_scsi_lu_lun.attr,
+ &target_stat_scsi_lu_lu_name.attr,
+ &target_stat_scsi_lu_vend.attr,
+ &target_stat_scsi_lu_prod.attr,
+ &target_stat_scsi_lu_rev.attr,
+ &target_stat_scsi_lu_dev_type.attr,
+ &target_stat_scsi_lu_status.attr,
+ &target_stat_scsi_lu_state_bit.attr,
+ &target_stat_scsi_lu_num_cmds.attr,
+ &target_stat_scsi_lu_read_mbytes.attr,
+ &target_stat_scsi_lu_write_mbytes.attr,
+ &target_stat_scsi_lu_resets.attr,
+ &target_stat_scsi_lu_full_stat.attr,
+ &target_stat_scsi_lu_hs_num_cmds.attr,
+ &target_stat_scsi_lu_creation_time.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_lu_attrib_ops = {
+ .show_attribute = target_stat_scsi_lu_attr_show,
+ .store_attribute = target_stat_scsi_lu_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_lu_cit = {
+ .ct_item_ops = &target_stat_scsi_lu_attrib_ops,
+ .ct_attrs = target_stat_scsi_lu_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Called from target_core_configfs.c:target_core_make_subdev() to setup
+ * the target statistics groups + configfs CITs located in target_core_stat.c
+ */
+void target_stat_setup_dev_default_groups(struct se_device *dev)
+{
+ struct config_group *dev_stat_grp = &dev->dev_stat_grps.stat_group;
+
+ config_group_init_type_name(&dev->dev_stat_grps.scsi_dev_group,
+ "scsi_dev", &target_stat_scsi_dev_cit);
+ config_group_init_type_name(&dev->dev_stat_grps.scsi_tgt_dev_group,
+ "scsi_tgt_dev", &target_stat_scsi_tgt_dev_cit);
+ config_group_init_type_name(&dev->dev_stat_grps.scsi_lu_group,
+ "scsi_lu", &target_stat_scsi_lu_cit);
+
+ dev_stat_grp->default_groups[0] = &dev->dev_stat_grps.scsi_dev_group;
+ dev_stat_grp->default_groups[1] = &dev->dev_stat_grps.scsi_tgt_dev_group;
+ dev_stat_grp->default_groups[2] = &dev->dev_stat_grps.scsi_lu_group;
+ dev_stat_grp->default_groups[3] = NULL;
+}
+
+/*
+ * SCSI Port Table
+ */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_port, se_port_stat_grps);
+#define DEV_STAT_SCSI_PORT_ATTR(_name, _mode) \
+static struct target_stat_scsi_port_attribute \
+ target_stat_scsi_port_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_port_show_attr_##_name, \
+ target_stat_scsi_port_store_attr_##_name);
+
+#define DEV_STAT_SCSI_PORT_ATTR_RO(_name) \
+static struct target_stat_scsi_port_attribute \
+ target_stat_scsi_port_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_port_show_attr_##_name);
+
+static ssize_t target_stat_scsi_port_show_attr_inst(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_device *dev = lun->lun_se_dev;
+ struct se_hba *hba;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ hba = dev->se_hba;
+ ret = snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_PORT_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_port_show_attr_dev(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_device *dev = lun->lun_se_dev;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_PORT_ATTR_RO(dev);
+
+static ssize_t target_stat_scsi_port_show_attr_indx(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%u\n", sep->sep_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_PORT_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_port_show_attr_role(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_device *dev = lun->lun_se_dev;
+ struct se_port *sep;
+ ssize_t ret;
+
+ if (!dev)
+ return -ENODEV;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%s%u\n", "Device", dev->dev_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_PORT_ATTR_RO(role);
+
+static ssize_t target_stat_scsi_port_show_attr_busy_count(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ /* FIXME: scsiPortBusyStatuses */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", 0);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_PORT_ATTR_RO(busy_count);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_port, se_port_stat_grps, scsi_port_group);
+
+static struct configfs_attribute *target_stat_scsi_port_attrs[] = {
+ &target_stat_scsi_port_inst.attr,
+ &target_stat_scsi_port_dev.attr,
+ &target_stat_scsi_port_indx.attr,
+ &target_stat_scsi_port_role.attr,
+ &target_stat_scsi_port_busy_count.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_port_attrib_ops = {
+ .show_attribute = target_stat_scsi_port_attr_show,
+ .store_attribute = target_stat_scsi_port_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_port_cit = {
+ .ct_item_ops = &target_stat_scsi_port_attrib_ops,
+ .ct_attrs = target_stat_scsi_port_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * SCSI Target Port Table
+ */
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_tgt_port, se_port_stat_grps);
+#define DEV_STAT_SCSI_TGT_PORT_ATTR(_name, _mode) \
+static struct target_stat_scsi_tgt_port_attribute \
+ target_stat_scsi_tgt_port_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_tgt_port_show_attr_##_name, \
+ target_stat_scsi_tgt_port_store_attr_##_name);
+
+#define DEV_STAT_SCSI_TGT_PORT_ATTR_RO(_name) \
+static struct target_stat_scsi_tgt_port_attribute \
+ target_stat_scsi_tgt_port_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_tgt_port_show_attr_##_name);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_inst(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_device *dev = lun->lun_se_dev;
+ struct se_port *sep;
+ struct se_hba *hba;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ hba = dev->se_hba;
+ ret = snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_dev(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_device *dev = lun->lun_se_dev;
+ struct se_port *sep;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(dev);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_indx(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%u\n", sep->sep_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_name(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+
+ ret = snprintf(page, PAGE_SIZE, "%sPort#%u\n",
+ tpg->se_tpg_tfo->get_fabric_name(), sep->sep_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(name);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_port_index(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+
+ ret = snprintf(page, PAGE_SIZE, "%s%s%d\n",
+ tpg->se_tpg_tfo->tpg_get_wwn(tpg), "+t+",
+ tpg->se_tpg_tfo->tpg_get_tag(tpg));
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(port_index);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_in_cmds(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+
+ ret = snprintf(page, PAGE_SIZE, "%llu\n", sep->sep_stats.cmd_pdus);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(in_cmds);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_write_mbytes(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ (u32)(sep->sep_stats.rx_data_octets >> 20));
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(write_mbytes);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_read_mbytes(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ (u32)(sep->sep_stats.tx_data_octets >> 20));
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(read_mbytes);
+
+static ssize_t target_stat_scsi_tgt_port_show_attr_hs_in_cmds(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+
+ /* FIXME: scsiTgtPortHsInCommands */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", 0);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TGT_PORT_ATTR_RO(hs_in_cmds);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_tgt_port, se_port_stat_grps,
+ scsi_tgt_port_group);
+
+static struct configfs_attribute *target_stat_scsi_tgt_port_attrs[] = {
+ &target_stat_scsi_tgt_port_inst.attr,
+ &target_stat_scsi_tgt_port_dev.attr,
+ &target_stat_scsi_tgt_port_indx.attr,
+ &target_stat_scsi_tgt_port_name.attr,
+ &target_stat_scsi_tgt_port_port_index.attr,
+ &target_stat_scsi_tgt_port_in_cmds.attr,
+ &target_stat_scsi_tgt_port_write_mbytes.attr,
+ &target_stat_scsi_tgt_port_read_mbytes.attr,
+ &target_stat_scsi_tgt_port_hs_in_cmds.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_tgt_port_attrib_ops = {
+ .show_attribute = target_stat_scsi_tgt_port_attr_show,
+ .store_attribute = target_stat_scsi_tgt_port_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_tgt_port_cit = {
+ .ct_item_ops = &target_stat_scsi_tgt_port_attrib_ops,
+ .ct_attrs = target_stat_scsi_tgt_port_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * SCSI Transport Table
+o */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_transport, se_port_stat_grps);
+#define DEV_STAT_SCSI_TRANSPORT_ATTR(_name, _mode) \
+static struct target_stat_scsi_transport_attribute \
+ target_stat_scsi_transport_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_transport_show_attr_##_name, \
+ target_stat_scsi_transport_store_attr_##_name);
+
+#define DEV_STAT_SCSI_TRANSPORT_ATTR_RO(_name) \
+static struct target_stat_scsi_transport_attribute \
+ target_stat_scsi_transport_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_transport_show_attr_##_name);
+
+static ssize_t target_stat_scsi_transport_show_attr_inst(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_device *dev = lun->lun_se_dev;
+ struct se_port *sep;
+ struct se_hba *hba;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+
+ hba = dev->se_hba;
+ ret = snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TRANSPORT_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_transport_show_attr_device(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+ /* scsiTransportType */
+ ret = snprintf(page, PAGE_SIZE, "scsiTransport%s\n",
+ tpg->se_tpg_tfo->get_fabric_name());
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TRANSPORT_ATTR_RO(device);
+
+static ssize_t target_stat_scsi_transport_show_attr_indx(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ tpg->se_tpg_tfo->tpg_get_inst_index(tpg));
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TRANSPORT_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_transport_show_attr_dev_name(
+ struct se_port_stat_grps *pgrps, char *page)
+{
+ struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
+ struct se_device *dev = lun->lun_se_dev;
+ struct se_port *sep;
+ struct se_portal_group *tpg;
+ struct t10_wwn *wwn;
+ ssize_t ret;
+
+ spin_lock(&lun->lun_sep_lock);
+ sep = lun->lun_sep;
+ if (!sep) {
+ spin_unlock(&lun->lun_sep_lock);
+ return -ENODEV;
+ }
+ tpg = sep->sep_tpg;
+ wwn = &dev->t10_wwn;
+ /* scsiTransportDevName */
+ ret = snprintf(page, PAGE_SIZE, "%s+%s\n",
+ tpg->se_tpg_tfo->tpg_get_wwn(tpg),
+ (strlen(wwn->unit_serial)) ? wwn->unit_serial :
+ wwn->vendor);
+ spin_unlock(&lun->lun_sep_lock);
+ return ret;
+}
+DEV_STAT_SCSI_TRANSPORT_ATTR_RO(dev_name);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_transport, se_port_stat_grps,
+ scsi_transport_group);
+
+static struct configfs_attribute *target_stat_scsi_transport_attrs[] = {
+ &target_stat_scsi_transport_inst.attr,
+ &target_stat_scsi_transport_device.attr,
+ &target_stat_scsi_transport_indx.attr,
+ &target_stat_scsi_transport_dev_name.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_transport_attrib_ops = {
+ .show_attribute = target_stat_scsi_transport_attr_show,
+ .store_attribute = target_stat_scsi_transport_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_transport_cit = {
+ .ct_item_ops = &target_stat_scsi_transport_attrib_ops,
+ .ct_attrs = target_stat_scsi_transport_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Called from target_core_fabric_configfs.c:target_fabric_make_lun() to setup
+ * the target port statistics groups + configfs CITs located in target_core_stat.c
+ */
+void target_stat_setup_port_default_groups(struct se_lun *lun)
+{
+ struct config_group *port_stat_grp = &lun->port_stat_grps.stat_group;
+
+ config_group_init_type_name(&lun->port_stat_grps.scsi_port_group,
+ "scsi_port", &target_stat_scsi_port_cit);
+ config_group_init_type_name(&lun->port_stat_grps.scsi_tgt_port_group,
+ "scsi_tgt_port", &target_stat_scsi_tgt_port_cit);
+ config_group_init_type_name(&lun->port_stat_grps.scsi_transport_group,
+ "scsi_transport", &target_stat_scsi_transport_cit);
+
+ port_stat_grp->default_groups[0] = &lun->port_stat_grps.scsi_port_group;
+ port_stat_grp->default_groups[1] = &lun->port_stat_grps.scsi_tgt_port_group;
+ port_stat_grp->default_groups[2] = &lun->port_stat_grps.scsi_transport_group;
+ port_stat_grp->default_groups[3] = NULL;
+}
+
+/*
+ * SCSI Authorized Initiator Table
+ */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_auth_intr, se_ml_stat_grps);
+#define DEV_STAT_SCSI_AUTH_INTR_ATTR(_name, _mode) \
+static struct target_stat_scsi_auth_intr_attribute \
+ target_stat_scsi_auth_intr_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_auth_intr_show_attr_##_name, \
+ target_stat_scsi_auth_intr_store_attr_##_name);
+
+#define DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(_name) \
+static struct target_stat_scsi_auth_intr_attribute \
+ target_stat_scsi_auth_intr_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_auth_intr_show_attr_##_name);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_inst(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ tpg = nacl->se_tpg;
+ /* scsiInstIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ tpg->se_tpg_tfo->tpg_get_inst_index(tpg));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_dev(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ struct se_lun *lun;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ lun = deve->se_lun;
+ /* scsiDeviceIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", lun->lun_se_dev->dev_index);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(dev);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_port(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ tpg = nacl->se_tpg;
+ /* scsiAuthIntrTgtPortIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", tpg->se_tpg_tfo->tpg_get_tag(tpg));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(port);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_indx(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", nacl->acl_index);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_dev_or_port(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrDevOrPort */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", 1);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(dev_or_port);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_intr_name(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrName */
+ ret = snprintf(page, PAGE_SIZE, "%s\n", nacl->initiatorname);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(intr_name);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_map_indx(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* FIXME: scsiAuthIntrLunMapIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", 0);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(map_indx);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_att_count(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrAttachedTimes */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", deve->attach_count);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(att_count);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_num_cmds(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrOutCommands */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", deve->total_cmds);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(num_cmds);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_read_mbytes(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrReadMegaBytes */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", (u32)(deve->read_bytes >> 20));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(read_mbytes);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_write_mbytes(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrWrittenMegaBytes */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", (u32)(deve->write_bytes >> 20));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(write_mbytes);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_hs_num_cmds(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* FIXME: scsiAuthIntrHSOutCommands */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", 0);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(hs_num_cmds);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_creation_time(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAuthIntrLastCreation */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", (u32)(((u32)deve->creation_time -
+ INITIAL_JIFFIES) * 100 / HZ));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(creation_time);
+
+static ssize_t target_stat_scsi_auth_intr_show_attr_row_status(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* FIXME: scsiAuthIntrRowStatus */
+ ret = snprintf(page, PAGE_SIZE, "Ready\n");
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(row_status);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_auth_intr, se_ml_stat_grps,
+ scsi_auth_intr_group);
+
+static struct configfs_attribute *target_stat_scsi_auth_intr_attrs[] = {
+ &target_stat_scsi_auth_intr_inst.attr,
+ &target_stat_scsi_auth_intr_dev.attr,
+ &target_stat_scsi_auth_intr_port.attr,
+ &target_stat_scsi_auth_intr_indx.attr,
+ &target_stat_scsi_auth_intr_dev_or_port.attr,
+ &target_stat_scsi_auth_intr_intr_name.attr,
+ &target_stat_scsi_auth_intr_map_indx.attr,
+ &target_stat_scsi_auth_intr_att_count.attr,
+ &target_stat_scsi_auth_intr_num_cmds.attr,
+ &target_stat_scsi_auth_intr_read_mbytes.attr,
+ &target_stat_scsi_auth_intr_write_mbytes.attr,
+ &target_stat_scsi_auth_intr_hs_num_cmds.attr,
+ &target_stat_scsi_auth_intr_creation_time.attr,
+ &target_stat_scsi_auth_intr_row_status.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_auth_intr_attrib_ops = {
+ .show_attribute = target_stat_scsi_auth_intr_attr_show,
+ .store_attribute = target_stat_scsi_auth_intr_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_auth_intr_cit = {
+ .ct_item_ops = &target_stat_scsi_auth_intr_attrib_ops,
+ .ct_attrs = target_stat_scsi_auth_intr_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * SCSI Attached Initiator Port Table
+ */
+
+CONFIGFS_EATTR_STRUCT(target_stat_scsi_att_intr_port, se_ml_stat_grps);
+#define DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR(_name, _mode) \
+static struct target_stat_scsi_att_intr_port_attribute \
+ target_stat_scsi_att_intr_port_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ target_stat_scsi_att_intr_port_show_attr_##_name, \
+ target_stat_scsi_att_intr_port_store_attr_##_name);
+
+#define DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(_name) \
+static struct target_stat_scsi_att_intr_port_attribute \
+ target_stat_scsi_att_intr_port_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ target_stat_scsi_att_intr_port_show_attr_##_name);
+
+static ssize_t target_stat_scsi_att_intr_port_show_attr_inst(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ tpg = nacl->se_tpg;
+ /* scsiInstIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ tpg->se_tpg_tfo->tpg_get_inst_index(tpg));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(inst);
+
+static ssize_t target_stat_scsi_att_intr_port_show_attr_dev(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ struct se_lun *lun;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ lun = deve->se_lun;
+ /* scsiDeviceIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", lun->lun_se_dev->dev_index);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(dev);
+
+static ssize_t target_stat_scsi_att_intr_port_show_attr_port(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ tpg = nacl->se_tpg;
+ /* scsiPortIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", tpg->se_tpg_tfo->tpg_get_tag(tpg));
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(port);
+
+static ssize_t target_stat_scsi_att_intr_port_show_attr_indx(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_session *se_sess;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->nacl_sess_lock);
+ se_sess = nacl->nacl_sess;
+ if (!se_sess) {
+ spin_unlock_irq(&nacl->nacl_sess_lock);
+ return -ENODEV;
+ }
+
+ tpg = nacl->se_tpg;
+ /* scsiAttIntrPortIndex */
+ ret = snprintf(page, PAGE_SIZE, "%u\n",
+ tpg->se_tpg_tfo->sess_get_index(se_sess));
+ spin_unlock_irq(&nacl->nacl_sess_lock);
+ return ret;
+}
+DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(indx);
+
+static ssize_t target_stat_scsi_att_intr_port_show_attr_port_auth_indx(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_dev_entry *deve;
+ ssize_t ret;
+
+ spin_lock_irq(&nacl->device_list_lock);
+ deve = nacl->device_list[lacl->mapped_lun];
+ if (!deve->se_lun || !deve->se_lun_acl) {
+ spin_unlock_irq(&nacl->device_list_lock);
+ return -ENODEV;
+ }
+ /* scsiAttIntrPortAuthIntrIdx */
+ ret = snprintf(page, PAGE_SIZE, "%u\n", nacl->acl_index);
+ spin_unlock_irq(&nacl->device_list_lock);
+ return ret;
+}
+DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(port_auth_indx);
+
+static ssize_t target_stat_scsi_att_intr_port_show_attr_port_ident(
+ struct se_ml_stat_grps *lgrps, char *page)
+{
+ struct se_lun_acl *lacl = container_of(lgrps,
+ struct se_lun_acl, ml_stat_grps);
+ struct se_node_acl *nacl = lacl->se_lun_nacl;
+ struct se_session *se_sess;
+ struct se_portal_group *tpg;
+ ssize_t ret;
+ unsigned char buf[64];
+
+ spin_lock_irq(&nacl->nacl_sess_lock);
+ se_sess = nacl->nacl_sess;
+ if (!se_sess) {
+ spin_unlock_irq(&nacl->nacl_sess_lock);
+ return -ENODEV;
+ }
+
+ tpg = nacl->se_tpg;
+ /* scsiAttIntrPortName+scsiAttIntrPortIdentifier */
+ memset(buf, 0, 64);
+ if (tpg->se_tpg_tfo->sess_get_initiator_sid != NULL)
+ tpg->se_tpg_tfo->sess_get_initiator_sid(se_sess, buf, 64);
+
+ ret = snprintf(page, PAGE_SIZE, "%s+i+%s\n", nacl->initiatorname, buf);
+ spin_unlock_irq(&nacl->nacl_sess_lock);
+ return ret;
+}
+DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(port_ident);
+
+CONFIGFS_EATTR_OPS(target_stat_scsi_att_intr_port, se_ml_stat_grps,
+ scsi_att_intr_port_group);
+
+static struct configfs_attribute *target_stat_scsi_ath_intr_port_attrs[] = {
+ &target_stat_scsi_att_intr_port_inst.attr,
+ &target_stat_scsi_att_intr_port_dev.attr,
+ &target_stat_scsi_att_intr_port_port.attr,
+ &target_stat_scsi_att_intr_port_indx.attr,
+ &target_stat_scsi_att_intr_port_port_auth_indx.attr,
+ &target_stat_scsi_att_intr_port_port_ident.attr,
+ NULL,
+};
+
+static struct configfs_item_operations target_stat_scsi_att_intr_port_attrib_ops = {
+ .show_attribute = target_stat_scsi_att_intr_port_attr_show,
+ .store_attribute = target_stat_scsi_att_intr_port_attr_store,
+};
+
+static struct config_item_type target_stat_scsi_att_intr_port_cit = {
+ .ct_item_ops = &target_stat_scsi_att_intr_port_attrib_ops,
+ .ct_attrs = target_stat_scsi_ath_intr_port_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Called from target_core_fabric_configfs.c:target_fabric_make_mappedlun() to setup
+ * the target MappedLUN statistics groups + configfs CITs located in target_core_stat.c
+ */
+void target_stat_setup_mappedlun_default_groups(struct se_lun_acl *lacl)
+{
+ struct config_group *ml_stat_grp = &lacl->ml_stat_grps.stat_group;
+
+ config_group_init_type_name(&lacl->ml_stat_grps.scsi_auth_intr_group,
+ "scsi_auth_intr", &target_stat_scsi_auth_intr_cit);
+ config_group_init_type_name(&lacl->ml_stat_grps.scsi_att_intr_port_group,
+ "scsi_att_intr_port", &target_stat_scsi_att_intr_port_cit);
+
+ ml_stat_grp->default_groups[0] = &lacl->ml_stat_grps.scsi_auth_intr_group;
+ ml_stat_grp->default_groups[1] = &lacl->ml_stat_grps.scsi_att_intr_port_group;
+ ml_stat_grp->default_groups[2] = NULL;
+}
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index 158cecbec71..f7cd95e8111 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -3,8 +3,7 @@
*
* This file contains SPC-3 task management infrastructure
*
- * Copyright (c) 2009,2010 Rising Tide Systems
- * Copyright (c) 2009,2010 Linux-iSCSI.org
+ * (c) Copyright 2009-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -24,48 +23,44 @@
*
******************************************************************************/
-#include <linux/version.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/list.h>
+#include <linux/export.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_tmr.h>
-#include <target/target_core_transport.h>
-#include <target/target_core_fabric_ops.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
#include <target/target_core_configfs.h>
+#include "target_core_internal.h"
#include "target_core_alua.h"
#include "target_core_pr.h"
-#define DEBUG_LUN_RESET
-#ifdef DEBUG_LUN_RESET
-#define DEBUG_LR(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_LR(x...)
-#endif
-
-struct se_tmr_req *core_tmr_alloc_req(
+int core_tmr_alloc_req(
struct se_cmd *se_cmd,
void *fabric_tmr_ptr,
- u8 function)
+ u8 function,
+ gfp_t gfp_flags)
{
struct se_tmr_req *tmr;
- tmr = kmem_cache_zalloc(se_tmr_req_cache, GFP_KERNEL);
- if (!(tmr)) {
- printk(KERN_ERR "Unable to allocate struct se_tmr_req\n");
- return ERR_PTR(-ENOMEM);
+ tmr = kzalloc(sizeof(struct se_tmr_req), gfp_flags);
+ if (!tmr) {
+ pr_err("Unable to allocate struct se_tmr_req\n");
+ return -ENOMEM;
}
+
+ se_cmd->se_cmd_flags |= SCF_SCSI_TMR_CDB;
+ se_cmd->se_tmr_req = tmr;
tmr->task_cmd = se_cmd;
tmr->fabric_tmr_ptr = fabric_tmr_ptr;
tmr->function = function;
INIT_LIST_HEAD(&tmr->tmr_list);
- return tmr;
+ return 0;
}
EXPORT_SYMBOL(core_tmr_alloc_req);
@@ -73,92 +68,137 @@ void core_tmr_release_req(
struct se_tmr_req *tmr)
{
struct se_device *dev = tmr->tmr_dev;
+ unsigned long flags;
- spin_lock(&dev->se_tmr_lock);
+ if (!dev) {
+ kfree(tmr);
+ return;
+ }
+
+ spin_lock_irqsave(&dev->se_tmr_lock, flags);
list_del(&tmr->tmr_list);
- kmem_cache_free(se_tmr_req_cache, tmr);
- spin_unlock(&dev->se_tmr_lock);
+ spin_unlock_irqrestore(&dev->se_tmr_lock, flags);
+
+ kfree(tmr);
}
static void core_tmr_handle_tas_abort(
struct se_node_acl *tmr_nacl,
struct se_cmd *cmd,
- int tas,
- int fe_count)
+ int tas)
{
- if (!(fe_count)) {
- transport_cmd_finish_abort(cmd, 1);
- return;
- }
+ bool remove = true;
/*
* TASK ABORTED status (TAS) bit support
*/
- if (((tmr_nacl != NULL) &&
- (tmr_nacl == cmd->se_sess->se_node_acl)) || tas)
+ if ((tmr_nacl &&
+ (tmr_nacl != cmd->se_sess->se_node_acl)) && tas) {
+ remove = false;
transport_send_task_abort(cmd);
+ }
- transport_cmd_finish_abort(cmd, 0);
+ transport_cmd_finish_abort(cmd, remove);
}
-int core_tmr_lun_reset(
+static int target_check_cdb_and_preempt(struct list_head *list,
+ struct se_cmd *cmd)
+{
+ struct t10_pr_registration *reg;
+
+ if (!list)
+ return 0;
+ list_for_each_entry(reg, list, pr_reg_abort_list) {
+ if (reg->pr_res_key == cmd->pr_res_key)
+ return 0;
+ }
+
+ return 1;
+}
+
+void core_tmr_abort_task(
struct se_device *dev,
struct se_tmr_req *tmr,
- struct list_head *preempt_and_abort_list,
- struct se_cmd *prout_cmd)
+ struct se_session *se_sess)
{
- struct se_cmd *cmd;
- struct se_queue_req *qr, *qr_tmp;
- struct se_node_acl *tmr_nacl = NULL;
- struct se_portal_group *tmr_tpg = NULL;
- struct se_queue_obj *qobj = dev->dev_queue_obj;
- struct se_tmr_req *tmr_p, *tmr_pp;
- struct se_task *task, *task_tmp;
+ struct se_cmd *se_cmd, *tmp_cmd;
unsigned long flags;
- int fe_count, state, tas;
- /*
- * TASK_ABORTED status bit, this is configurable via ConfigFS
- * struct se_device attributes. spc4r17 section 7.4.6 Control mode page
- *
- * 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).
- */
- tas = DEV_ATTRIB(dev)->emulate_tas;
- /*
- * Determine if this se_tmr is coming from a $FABRIC_MOD
- * or struct se_device passthrough..
- */
- if (tmr && tmr->task_cmd && tmr->task_cmd->se_sess) {
- tmr_nacl = tmr->task_cmd->se_sess->se_node_acl;
- tmr_tpg = tmr->task_cmd->se_sess->se_tpg;
- if (tmr_nacl && tmr_tpg) {
- DEBUG_LR("LUN_RESET: TMR caller fabric: %s"
- " initiator port %s\n",
- TPG_TFO(tmr_tpg)->get_fabric_name(),
- tmr_nacl->initiatorname);
+ int ref_tag;
+
+ spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
+ list_for_each_entry_safe(se_cmd, tmp_cmd,
+ &se_sess->sess_cmd_list, se_cmd_list) {
+
+ if (dev != se_cmd->se_dev)
+ continue;
+
+ /* skip se_cmd associated with tmr */
+ if (tmr->task_cmd == se_cmd)
+ continue;
+
+ ref_tag = se_cmd->se_tfo->get_task_tag(se_cmd);
+ if (tmr->ref_task_tag != ref_tag)
+ continue;
+
+ printk("ABORT_TASK: Found referenced %s task_tag: %u\n",
+ se_cmd->se_tfo->get_fabric_name(), ref_tag);
+
+ spin_lock(&se_cmd->t_state_lock);
+ if (se_cmd->transport_state & CMD_T_COMPLETE) {
+ printk("ABORT_TASK: ref_tag: %u already complete, skipping\n", ref_tag);
+ spin_unlock(&se_cmd->t_state_lock);
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+ goto out;
}
+ se_cmd->transport_state |= CMD_T_ABORTED;
+ spin_unlock(&se_cmd->t_state_lock);
+
+ list_del_init(&se_cmd->se_cmd_list);
+ kref_get(&se_cmd->cmd_kref);
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+
+ cancel_work_sync(&se_cmd->work);
+ transport_wait_for_tasks(se_cmd);
+
+ target_put_sess_cmd(se_sess, se_cmd);
+ transport_cmd_finish_abort(se_cmd, true);
+
+ printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for"
+ " ref_tag: %d\n", ref_tag);
+ tmr->response = TMR_FUNCTION_COMPLETE;
+ return;
}
- DEBUG_LR("LUN_RESET: %s starting for [%s], tas: %d\n",
- (preempt_and_abort_list) ? "Preempt" : "TMR",
- TRANSPORT(dev)->name, tas);
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+
+out:
+ printk("ABORT_TASK: Sending TMR_TASK_DOES_NOT_EXIST for ref_tag: %d\n",
+ tmr->ref_task_tag);
+ tmr->response = TMR_TASK_DOES_NOT_EXIST;
+}
+
+static void core_tmr_drain_tmr_list(
+ struct se_device *dev,
+ struct se_tmr_req *tmr,
+ struct list_head *preempt_and_abort_list)
+{
+ LIST_HEAD(drain_tmr_list);
+ struct se_tmr_req *tmr_p, *tmr_pp;
+ struct se_cmd *cmd;
+ unsigned long flags;
/*
* Release all pending and outgoing TMRs aside from the received
* LUN_RESET tmr..
*/
- spin_lock(&dev->se_tmr_lock);
+ spin_lock_irqsave(&dev->se_tmr_lock, flags);
list_for_each_entry_safe(tmr_p, tmr_pp, &dev->dev_tmr_list, tmr_list) {
/*
* Allow the received TMR to return with FUNCTION_COMPLETE.
*/
- if (tmr && (tmr_p == tmr))
+ if (tmr_p == tmr)
continue;
cmd = tmr_p->task_cmd;
- if (!(cmd)) {
- printk(KERN_ERR "Unable to locate struct se_cmd for TMR\n");
+ if (!cmd) {
+ pr_err("Unable to locate struct se_cmd for TMR\n");
continue;
}
/*
@@ -166,35 +206,51 @@ int core_tmr_lun_reset(
* parameter (eg: for PROUT PREEMPT_AND_ABORT service action
* skip non regisration key matching TMRs.
*/
- if ((preempt_and_abort_list != NULL) &&
- (core_scsi3_check_cdb_abort_and_preempt(
- preempt_and_abort_list, cmd) != 0))
+ if (target_check_cdb_and_preempt(preempt_and_abort_list, cmd))
continue;
- spin_unlock(&dev->se_tmr_lock);
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- if (!(atomic_read(&T_TASK(cmd)->t_transport_active))) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- spin_lock(&dev->se_tmr_lock);
+ spin_lock(&cmd->t_state_lock);
+ if (!(cmd->transport_state & CMD_T_ACTIVE)) {
+ spin_unlock(&cmd->t_state_lock);
continue;
}
if (cmd->t_state == TRANSPORT_ISTATE_PROCESSING) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- spin_lock(&dev->se_tmr_lock);
+ spin_unlock(&cmd->t_state_lock);
continue;
}
- DEBUG_LR("LUN_RESET: %s releasing TMR %p Function: 0x%02x,"
+ spin_unlock(&cmd->t_state_lock);
+
+ list_move_tail(&tmr_p->tmr_list, &drain_tmr_list);
+ }
+ spin_unlock_irqrestore(&dev->se_tmr_lock, flags);
+
+ list_for_each_entry_safe(tmr_p, tmr_pp, &drain_tmr_list, tmr_list) {
+ list_del_init(&tmr_p->tmr_list);
+ cmd = tmr_p->task_cmd;
+
+ pr_debug("LUN_RESET: %s releasing TMR %p Function: 0x%02x,"
" Response: 0x%02x, t_state: %d\n",
(preempt_and_abort_list) ? "Preempt" : "", tmr_p,
tmr_p->function, tmr_p->response, cmd->t_state);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- transport_cmd_finish_abort_tmr(cmd);
- spin_lock(&dev->se_tmr_lock);
+ transport_cmd_finish_abort(cmd, 1);
}
- spin_unlock(&dev->se_tmr_lock);
+}
+
+static void core_tmr_drain_state_list(
+ struct se_device *dev,
+ struct se_cmd *prout_cmd,
+ struct se_node_acl *tmr_nacl,
+ int tas,
+ struct list_head *preempt_and_abort_list)
+{
+ LIST_HEAD(drain_task_list);
+ struct se_cmd *cmd, *next;
+ unsigned long flags;
+
/*
- * Complete outstanding struct se_task CDBs with TASK_ABORTED SAM status.
+ * Complete outstanding commands with TASK_ABORTED SAM status.
+ *
* This is following sam4r17, section 5.6 Aborting commands, Table 38
* for TMR LUN_RESET:
*
@@ -215,190 +271,125 @@ int core_tmr_lun_reset(
* in the Control Mode Page.
*/
spin_lock_irqsave(&dev->execute_task_lock, flags);
- list_for_each_entry_safe(task, task_tmp, &dev->state_task_list,
- t_state_list) {
- if (!(TASK_CMD(task))) {
- printk(KERN_ERR "TASK_CMD(task) is NULL!\n");
- continue;
- }
- cmd = TASK_CMD(task);
-
- if (!T_TASK(cmd)) {
- printk(KERN_ERR "T_TASK(cmd) is NULL for task: %p cmd:"
- " %p ITT: 0x%08x\n", task, cmd,
- CMD_TFO(cmd)->get_task_tag(cmd));
- continue;
- }
+ list_for_each_entry_safe(cmd, next, &dev->state_list, state_list) {
/*
* For PREEMPT_AND_ABORT usage, only process commands
* with a matching reservation key.
*/
- if ((preempt_and_abort_list != NULL) &&
- (core_scsi3_check_cdb_abort_and_preempt(
- preempt_and_abort_list, cmd) != 0))
+ if (target_check_cdb_and_preempt(preempt_and_abort_list, cmd))
continue;
+
/*
* Not aborting PROUT PREEMPT_AND_ABORT CDB..
*/
if (prout_cmd == cmd)
continue;
- list_del(&task->t_state_list);
- atomic_set(&task->task_state_active, 0);
- spin_unlock_irqrestore(&dev->execute_task_lock, flags);
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- DEBUG_LR("LUN_RESET: %s cmd: %p task: %p"
- " ITT/CmdSN: 0x%08x/0x%08x, i_state: %d, t_state/"
- "def_t_state: %d/%d cdb: 0x%02x\n",
- (preempt_and_abort_list) ? "Preempt" : "", cmd, task,
- CMD_TFO(cmd)->get_task_tag(cmd), 0,
- CMD_TFO(cmd)->get_cmd_state(cmd), cmd->t_state,
- cmd->deferred_t_state, T_TASK(cmd)->t_task_cdb[0]);
- DEBUG_LR("LUN_RESET: ITT[0x%08x] - pr_res_key: 0x%016Lx"
- " t_task_cdbs: %d t_task_cdbs_left: %d"
- " t_task_cdbs_sent: %d -- t_transport_active: %d"
- " t_transport_stop: %d t_transport_sent: %d\n",
- CMD_TFO(cmd)->get_task_tag(cmd), cmd->pr_res_key,
- T_TASK(cmd)->t_task_cdbs,
- atomic_read(&T_TASK(cmd)->t_task_cdbs_left),
- atomic_read(&T_TASK(cmd)->t_task_cdbs_sent),
- atomic_read(&T_TASK(cmd)->t_transport_active),
- atomic_read(&T_TASK(cmd)->t_transport_stop),
- atomic_read(&T_TASK(cmd)->t_transport_sent));
-
- if (atomic_read(&task->task_active)) {
- atomic_set(&task->task_stop, 1);
- spin_unlock_irqrestore(
- &T_TASK(cmd)->t_state_lock, flags);
-
- DEBUG_LR("LUN_RESET: Waiting for task: %p to shutdown"
- " for dev: %p\n", task, dev);
- wait_for_completion(&task->task_stop_comp);
- DEBUG_LR("LUN_RESET Completed task: %p shutdown for"
- " dev: %p\n", task, dev);
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- atomic_dec(&T_TASK(cmd)->t_task_cdbs_left);
-
- atomic_set(&task->task_active, 0);
- atomic_set(&task->task_stop, 0);
- }
- __transport_stop_task_timer(task, &flags);
+ list_move_tail(&cmd->state_list, &drain_task_list);
+ cmd->state_active = false;
+ }
+ spin_unlock_irqrestore(&dev->execute_task_lock, flags);
- if (!(atomic_dec_and_test(&T_TASK(cmd)->t_task_cdbs_ex_left))) {
- spin_unlock_irqrestore(
- &T_TASK(cmd)->t_state_lock, flags);
- DEBUG_LR("LUN_RESET: Skipping task: %p, dev: %p for"
- " t_task_cdbs_ex_left: %d\n", task, dev,
- atomic_read(&T_TASK(cmd)->t_task_cdbs_ex_left));
+ while (!list_empty(&drain_task_list)) {
+ cmd = list_entry(drain_task_list.next, struct se_cmd, state_list);
+ list_del(&cmd->state_list);
+
+ pr_debug("LUN_RESET: %s cmd: %p"
+ " ITT/CmdSN: 0x%08x/0x%08x, i_state: %d, t_state: %d"
+ "cdb: 0x%02x\n",
+ (preempt_and_abort_list) ? "Preempt" : "", cmd,
+ cmd->se_tfo->get_task_tag(cmd), 0,
+ cmd->se_tfo->get_cmd_state(cmd), cmd->t_state,
+ cmd->t_task_cdb[0]);
+ pr_debug("LUN_RESET: ITT[0x%08x] - pr_res_key: 0x%016Lx"
+ " -- CMD_T_ACTIVE: %d"
+ " CMD_T_STOP: %d CMD_T_SENT: %d\n",
+ cmd->se_tfo->get_task_tag(cmd), cmd->pr_res_key,
+ (cmd->transport_state & CMD_T_ACTIVE) != 0,
+ (cmd->transport_state & CMD_T_STOP) != 0,
+ (cmd->transport_state & CMD_T_SENT) != 0);
- spin_lock_irqsave(&dev->execute_task_lock, flags);
- continue;
- }
- fe_count = atomic_read(&T_TASK(cmd)->t_fe_count);
+ /*
+ * If the command may be queued onto a workqueue cancel it now.
+ *
+ * This is equivalent to removal from the execute queue in the
+ * loop above, but we do it down here given that
+ * cancel_work_sync may block.
+ */
+ if (cmd->t_state == TRANSPORT_COMPLETE)
+ cancel_work_sync(&cmd->work);
- if (atomic_read(&T_TASK(cmd)->t_transport_active)) {
- DEBUG_LR("LUN_RESET: got t_transport_active = 1 for"
- " task: %p, t_fe_count: %d dev: %p\n", task,
- fe_count, dev);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock,
- flags);
- core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count);
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ target_stop_cmd(cmd, &flags);
- spin_lock_irqsave(&dev->execute_task_lock, flags);
- continue;
- }
- DEBUG_LR("LUN_RESET: Got t_transport_active = 0 for task: %p,"
- " t_fe_count: %d dev: %p\n", task, fe_count, dev);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count);
+ cmd->transport_state |= CMD_T_ABORTED;
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- spin_lock_irqsave(&dev->execute_task_lock, flags);
+ core_tmr_handle_tas_abort(tmr_nacl, cmd, tas);
}
- spin_unlock_irqrestore(&dev->execute_task_lock, flags);
- /*
- * Release all commands remaining in the struct se_device cmd queue.
+}
+
+int core_tmr_lun_reset(
+ struct se_device *dev,
+ struct se_tmr_req *tmr,
+ struct list_head *preempt_and_abort_list,
+ struct se_cmd *prout_cmd)
+{
+ struct se_node_acl *tmr_nacl = NULL;
+ struct se_portal_group *tmr_tpg = NULL;
+ int tas;
+ /*
+ * TASK_ABORTED status bit, this is configurable via ConfigFS
+ * struct se_device attributes. spc4r17 section 7.4.6 Control mode page
*
- * This follows the same logic as above for the struct se_device
- * struct se_task state list, where commands are returned with
- * TASK_ABORTED status, if there is an outstanding $FABRIC_MOD
- * reference, otherwise the struct se_cmd is released.
+ * 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).
*/
- spin_lock_irqsave(&qobj->cmd_queue_lock, flags);
- list_for_each_entry_safe(qr, qr_tmp, &qobj->qobj_list, qr_list) {
- cmd = (struct se_cmd *)qr->cmd;
- if (!(cmd)) {
- /*
- * Skip these for non PREEMPT_AND_ABORT usage..
- */
- if (preempt_and_abort_list != NULL)
- continue;
-
- atomic_dec(&qobj->queue_cnt);
- list_del(&qr->qr_list);
- kfree(qr);
- continue;
+ tas = dev->dev_attrib.emulate_tas;
+ /*
+ * Determine if this se_tmr is coming from a $FABRIC_MOD
+ * or struct se_device passthrough..
+ */
+ if (tmr && tmr->task_cmd && tmr->task_cmd->se_sess) {
+ tmr_nacl = tmr->task_cmd->se_sess->se_node_acl;
+ tmr_tpg = tmr->task_cmd->se_sess->se_tpg;
+ if (tmr_nacl && tmr_tpg) {
+ pr_debug("LUN_RESET: TMR caller fabric: %s"
+ " initiator port %s\n",
+ tmr_tpg->se_tpg_tfo->get_fabric_name(),
+ tmr_nacl->initiatorname);
}
- /*
- * For PREEMPT_AND_ABORT usage, only process commands
- * with a matching reservation key.
- */
- if ((preempt_and_abort_list != NULL) &&
- (core_scsi3_check_cdb_abort_and_preempt(
- preempt_and_abort_list, cmd) != 0))
- continue;
- /*
- * Not aborting PROUT PREEMPT_AND_ABORT CDB..
- */
- if (prout_cmd == cmd)
- continue;
-
- atomic_dec(&T_TASK(cmd)->t_transport_queue_active);
- atomic_dec(&qobj->queue_cnt);
- list_del(&qr->qr_list);
- spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
+ }
+ pr_debug("LUN_RESET: %s starting for [%s], tas: %d\n",
+ (preempt_and_abort_list) ? "Preempt" : "TMR",
+ dev->transport->name, tas);
- state = qr->state;
- kfree(qr);
+ core_tmr_drain_tmr_list(dev, tmr, preempt_and_abort_list);
+ core_tmr_drain_state_list(dev, prout_cmd, tmr_nacl, tas,
+ preempt_and_abort_list);
- DEBUG_LR("LUN_RESET: %s from Device Queue: cmd: %p t_state:"
- " %d t_fe_count: %d\n", (preempt_and_abort_list) ?
- "Preempt" : "", cmd, state,
- atomic_read(&T_TASK(cmd)->t_fe_count));
- /*
- * Signal that the command has failed via cmd->se_cmd_flags,
- * and call TFO->new_cmd_failure() to wakeup any fabric
- * dependent code used to wait for unsolicited data out
- * allocation to complete. The fabric module is expected
- * to dump any remaining unsolicited data out for the aborted
- * command at this point.
- */
- transport_new_cmd_failure(cmd);
-
- core_tmr_handle_tas_abort(tmr_nacl, cmd, tas,
- atomic_read(&T_TASK(cmd)->t_fe_count));
- spin_lock_irqsave(&qobj->cmd_queue_lock, flags);
- }
- spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
/*
* Clear any legacy SPC-2 reservation when called during
* LOGICAL UNIT RESET
*/
- if (!(preempt_and_abort_list) &&
- (dev->dev_flags & DF_SPC2_RESERVATIONS)) {
+ if (!preempt_and_abort_list &&
+ (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)) {
spin_lock(&dev->dev_reservation_lock);
dev->dev_reserved_node_acl = NULL;
- dev->dev_flags &= ~DF_SPC2_RESERVATIONS;
+ dev->dev_reservation_flags &= ~DRF_SPC2_RESERVATIONS;
spin_unlock(&dev->dev_reservation_lock);
- printk(KERN_INFO "LUN_RESET: SCSI-2 Released reservation\n");
+ pr_debug("LUN_RESET: SCSI-2 Released reservation\n");
}
- spin_lock(&dev->stats_lock);
- dev->num_resets++;
- spin_unlock(&dev->stats_lock);
+ atomic_long_inc(&dev->num_resets);
- DEBUG_LR("LUN_RESET: %s for [%s] Complete\n",
+ pr_debug("LUN_RESET: %s for [%s] Complete\n",
(preempt_and_abort_list) ? "Preempt" : "TMR",
- TRANSPORT(dev)->name);
+ dev->transport->name);
return 0;
}
+
diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c
index c26f6746762..c036595b17c 100644
--- a/drivers/target/target_core_tpg.c
+++ b/drivers/target/target_core_tpg.c
@@ -3,10 +3,7 @@
*
* This file contains generic Target Portal Group related functions.
*
- * 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
+ * (c) Copyright 2002-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -31,20 +28,23 @@
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
#include <linux/in.h>
+#include <linux/export.h>
#include <net/sock.h>
#include <net/tcp.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_tpg.h>
-#include <target/target_core_transport.h>
-#include <target/target_core_fabric_ops.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
-#include "target_core_hba.h"
+#include "target_core_internal.h"
+
+extern struct se_device *g_lun0_dev;
+
+static DEFINE_SPINLOCK(tpg_lock);
+static LIST_HEAD(tpg_list);
/* core_clear_initiator_node_from_tpg():
*
@@ -57,50 +57,27 @@ static void core_clear_initiator_node_from_tpg(
int i;
struct se_dev_entry *deve;
struct se_lun *lun;
- struct se_lun_acl *acl, *acl_tmp;
spin_lock_irq(&nacl->device_list_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- deve = &nacl->device_list[i];
+ deve = nacl->device_list[i];
if (!(deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS))
continue;
if (!deve->se_lun) {
- printk(KERN_ERR "%s device entries device pointer is"
+ pr_err("%s device entries device pointer is"
" NULL, but Initiator has access.\n",
- TPG_TFO(tpg)->get_fabric_name());
+ tpg->se_tpg_tfo->get_fabric_name());
continue;
}
lun = deve->se_lun;
spin_unlock_irq(&nacl->device_list_lock);
- core_update_device_list_for_node(lun, NULL, deve->mapped_lun,
- TRANSPORT_LUNFLAGS_NO_ACCESS, nacl, tpg, 0);
-
- spin_lock(&lun->lun_acl_lock);
- list_for_each_entry_safe(acl, acl_tmp,
- &lun->lun_acl_list, lacl_list) {
- if (!(strcmp(acl->initiatorname,
- nacl->initiatorname)) &&
- (acl->mapped_lun == deve->mapped_lun))
- break;
- }
-
- if (!acl) {
- printk(KERN_ERR "Unable to locate struct se_lun_acl for %s,"
- " mapped_lun: %u\n", nacl->initiatorname,
- deve->mapped_lun);
- spin_unlock(&lun->lun_acl_lock);
- spin_lock_irq(&nacl->device_list_lock);
- continue;
- }
-
- list_del(&acl->lacl_list);
- spin_unlock(&lun->lun_acl_lock);
+ core_disable_device_list_for_node(lun, NULL, deve->mapped_lun,
+ TRANSPORT_LUNFLAGS_NO_ACCESS, nacl, tpg);
spin_lock_irq(&nacl->device_list_lock);
- kfree(acl);
}
spin_unlock_irq(&nacl->device_list_lock);
}
@@ -116,7 +93,7 @@ struct se_node_acl *__core_tpg_get_initiator_node_acl(
struct se_node_acl *acl;
list_for_each_entry(acl, &tpg->acl_node_list, acl_list) {
- if (!(strcmp(acl->initiatorname, initiatorname)))
+ if (!strcmp(acl->initiatorname, initiatorname))
return acl;
}
@@ -133,18 +110,13 @@ struct se_node_acl *core_tpg_get_initiator_node_acl(
{
struct se_node_acl *acl;
- spin_lock_bh(&tpg->acl_node_lock);
- list_for_each_entry(acl, &tpg->acl_node_list, acl_list) {
- if (!(strcmp(acl->initiatorname, initiatorname)) &&
- (!(acl->dynamic_node_acl))) {
- spin_unlock_bh(&tpg->acl_node_lock);
- return acl;
- }
- }
- spin_unlock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
+ acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname);
+ spin_unlock_irq(&tpg->acl_node_lock);
- return NULL;
+ return acl;
}
+EXPORT_SYMBOL(core_tpg_get_initiator_node_acl);
/* core_tpg_add_node_to_devs():
*
@@ -161,7 +133,7 @@ void core_tpg_add_node_to_devs(
spin_lock(&tpg->tpg_lun_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- lun = &tpg->tpg_lun_list[i];
+ lun = tpg->tpg_lun_list[i];
if (lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE)
continue;
@@ -172,31 +144,28 @@ void core_tpg_add_node_to_devs(
* By default in LIO-Target $FABRIC_MOD,
* demo_mode_write_protect is ON, or READ_ONLY;
*/
- if (!(TPG_TFO(tpg)->tpg_check_demo_mode_write_protect(tpg))) {
- if (dev->dev_flags & DF_READ_ONLY)
- lun_access = TRANSPORT_LUNFLAGS_READ_ONLY;
- else
- lun_access = TRANSPORT_LUNFLAGS_READ_WRITE;
+ if (!tpg->se_tpg_tfo->tpg_check_demo_mode_write_protect(tpg)) {
+ lun_access = TRANSPORT_LUNFLAGS_READ_WRITE;
} else {
/*
* Allow only optical drives to issue R/W in default RO
* demo mode.
*/
- if (TRANSPORT(dev)->get_device_type(dev) == TYPE_DISK)
+ if (dev->transport->get_device_type(dev) == TYPE_DISK)
lun_access = TRANSPORT_LUNFLAGS_READ_ONLY;
else
lun_access = TRANSPORT_LUNFLAGS_READ_WRITE;
}
- printk(KERN_INFO "TARGET_CORE[%s]->TPG[%u]_LUN[%u] - Adding %s"
+ pr_debug("TARGET_CORE[%s]->TPG[%u]_LUN[%u] - Adding %s"
" access for LUN in Demo Mode\n",
- TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_tag(tpg), lun->unpacked_lun,
+ tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg), lun->unpacked_lun,
(lun_access == TRANSPORT_LUNFLAGS_READ_WRITE) ?
"READ-WRITE" : "READ-ONLY");
- core_update_device_list_for_node(lun, NULL, lun->unpacked_lun,
- lun_access, acl, tpg, 1);
+ core_enable_device_list_for_node(lun, NULL, lun->unpacked_lun,
+ lun_access, acl, tpg);
spin_lock(&tpg->tpg_lun_lock);
}
spin_unlock(&tpg->tpg_lun_lock);
@@ -211,8 +180,8 @@ static int core_set_queue_depth_for_node(
struct se_node_acl *acl)
{
if (!acl->queue_depth) {
- printk(KERN_ERR "Queue depth for %s Initiator Node: %s is 0,"
- "defaulting to 1.\n", TPG_TFO(tpg)->get_fabric_name(),
+ pr_err("Queue depth for %s Initiator Node: %s is 0,"
+ "defaulting to 1.\n", tpg->se_tpg_tfo->get_fabric_name(),
acl->initiatorname);
acl->queue_depth = 1;
}
@@ -220,6 +189,34 @@ static int core_set_queue_depth_for_node(
return 0;
}
+void array_free(void *array, int n)
+{
+ void **a = array;
+ int i;
+
+ for (i = 0; i < n; i++)
+ kfree(a[i]);
+ kfree(a);
+}
+
+static void *array_zalloc(int n, size_t size, gfp_t flags)
+{
+ void **a;
+ int i;
+
+ a = kzalloc(n * sizeof(void*), flags);
+ if (!a)
+ return NULL;
+ for (i = 0; i < n; i++) {
+ a[i] = kzalloc(size, flags);
+ if (!a[i]) {
+ array_free(a, n);
+ return NULL;
+ }
+ }
+ return a;
+}
+
/* core_create_device_list_for_node():
*
*
@@ -229,15 +226,15 @@ static int core_create_device_list_for_node(struct se_node_acl *nacl)
struct se_dev_entry *deve;
int i;
- nacl->device_list = kzalloc(sizeof(struct se_dev_entry) *
- TRANSPORT_MAX_LUNS_PER_TPG, GFP_KERNEL);
- if (!(nacl->device_list)) {
- printk(KERN_ERR "Unable to allocate memory for"
+ nacl->device_list = array_zalloc(TRANSPORT_MAX_LUNS_PER_TPG,
+ sizeof(struct se_dev_entry), GFP_KERNEL);
+ if (!nacl->device_list) {
+ pr_err("Unable to allocate memory for"
" struct se_node_acl->device_list\n");
- return -1;
+ return -ENOMEM;
}
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- deve = &nacl->device_list[i];
+ deve = nacl->device_list[i];
atomic_set(&deve->ua_count, 0);
atomic_set(&deve->pr_ref_count, 0);
@@ -260,52 +257,59 @@ struct se_node_acl *core_tpg_check_initiator_node_acl(
struct se_node_acl *acl;
acl = core_tpg_get_initiator_node_acl(tpg, initiatorname);
- if ((acl))
+ if (acl)
return acl;
- if (!(TPG_TFO(tpg)->tpg_check_demo_mode(tpg)))
+ if (!tpg->se_tpg_tfo->tpg_check_demo_mode(tpg))
return NULL;
- acl = TPG_TFO(tpg)->tpg_alloc_fabric_acl(tpg);
- if (!(acl))
+ acl = tpg->se_tpg_tfo->tpg_alloc_fabric_acl(tpg);
+ if (!acl)
return NULL;
INIT_LIST_HEAD(&acl->acl_list);
INIT_LIST_HEAD(&acl->acl_sess_list);
+ kref_init(&acl->acl_kref);
+ init_completion(&acl->acl_free_comp);
spin_lock_init(&acl->device_list_lock);
spin_lock_init(&acl->nacl_sess_lock);
atomic_set(&acl->acl_pr_ref_count, 0);
- acl->queue_depth = TPG_TFO(tpg)->tpg_get_default_depth(tpg);
+ acl->queue_depth = tpg->se_tpg_tfo->tpg_get_default_depth(tpg);
snprintf(acl->initiatorname, TRANSPORT_IQN_LEN, "%s", initiatorname);
acl->se_tpg = tpg;
acl->acl_index = scsi_get_new_index(SCSI_AUTH_INTR_INDEX);
- spin_lock_init(&acl->stats_lock);
acl->dynamic_node_acl = 1;
- TPG_TFO(tpg)->set_default_node_attributes(acl);
+ tpg->se_tpg_tfo->set_default_node_attributes(acl);
if (core_create_device_list_for_node(acl) < 0) {
- TPG_TFO(tpg)->tpg_release_fabric_acl(tpg, acl);
+ tpg->se_tpg_tfo->tpg_release_fabric_acl(tpg, acl);
return NULL;
}
if (core_set_queue_depth_for_node(tpg, acl) < 0) {
core_free_device_list_for_node(acl, tpg);
- TPG_TFO(tpg)->tpg_release_fabric_acl(tpg, acl);
+ tpg->se_tpg_tfo->tpg_release_fabric_acl(tpg, acl);
return NULL;
}
+ /*
+ * Here we only create demo-mode MappedLUNs from the active
+ * TPG LUNs if the fabric is not explicitly asking for
+ * tpg_check_demo_mode_login_only() == 1.
+ */
+ if ((tpg->se_tpg_tfo->tpg_check_demo_mode_login_only == NULL) ||
+ (tpg->se_tpg_tfo->tpg_check_demo_mode_login_only(tpg) != 1))
+ core_tpg_add_node_to_devs(acl, tpg);
- core_tpg_add_node_to_devs(acl, tpg);
-
- spin_lock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
list_add_tail(&acl->acl_list, &tpg->acl_node_list);
tpg->num_node_acls++;
- spin_unlock_bh(&tpg->acl_node_lock);
+ spin_unlock_irq(&tpg->acl_node_lock);
- printk("%s_TPG[%u] - Added DYNAMIC ACL with TCQ Depth: %d for %s"
- " Initiator Node: %s\n", TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_tag(tpg), acl->queue_depth,
- TPG_TFO(tpg)->get_fabric_name(), initiatorname);
+ pr_debug("%s_TPG[%u] - Added DYNAMIC ACL with TCQ Depth: %d for %s"
+ " Initiator Node: %s\n", tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg), acl->queue_depth,
+ tpg->se_tpg_tfo->get_fabric_name(), initiatorname);
return acl;
}
@@ -319,19 +323,19 @@ void core_tpg_wait_for_nacl_pr_ref(struct se_node_acl *nacl)
void core_tpg_clear_object_luns(struct se_portal_group *tpg)
{
- int i, ret;
+ int i;
struct se_lun *lun;
spin_lock(&tpg->tpg_lun_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- lun = &tpg->tpg_lun_list[i];
+ lun = tpg->tpg_lun_list[i];
if ((lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE) ||
(lun->lun_se_dev == NULL))
continue;
spin_unlock(&tpg->tpg_lun_lock);
- ret = core_dev_del_lun(tpg, lun->unpacked_lun);
+ core_dev_del_lun(tpg, lun->unpacked_lun);
spin_lock(&tpg->tpg_lun_lock);
}
spin_unlock(&tpg->tpg_lun_lock);
@@ -350,37 +354,37 @@ struct se_node_acl *core_tpg_add_initiator_node_acl(
{
struct se_node_acl *acl = NULL;
- spin_lock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname);
- if ((acl)) {
+ if (acl) {
if (acl->dynamic_node_acl) {
acl->dynamic_node_acl = 0;
- printk(KERN_INFO "%s_TPG[%u] - Replacing dynamic ACL"
- " for %s\n", TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_tag(tpg), initiatorname);
- spin_unlock_bh(&tpg->acl_node_lock);
+ pr_debug("%s_TPG[%u] - Replacing dynamic ACL"
+ " for %s\n", tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg), initiatorname);
+ spin_unlock_irq(&tpg->acl_node_lock);
/*
* Release the locally allocated struct se_node_acl
* because * core_tpg_add_initiator_node_acl() returned
* a pointer to an existing demo mode node ACL.
*/
if (se_nacl)
- TPG_TFO(tpg)->tpg_release_fabric_acl(tpg,
+ tpg->se_tpg_tfo->tpg_release_fabric_acl(tpg,
se_nacl);
goto done;
}
- printk(KERN_ERR "ACL entry for %s Initiator"
+ pr_err("ACL entry for %s Initiator"
" Node %s already exists for TPG %u, ignoring"
- " request.\n", TPG_TFO(tpg)->get_fabric_name(),
- initiatorname, TPG_TFO(tpg)->tpg_get_tag(tpg));
- spin_unlock_bh(&tpg->acl_node_lock);
+ " request.\n", tpg->se_tpg_tfo->get_fabric_name(),
+ initiatorname, tpg->se_tpg_tfo->tpg_get_tag(tpg));
+ spin_unlock_irq(&tpg->acl_node_lock);
return ERR_PTR(-EEXIST);
}
- spin_unlock_bh(&tpg->acl_node_lock);
+ spin_unlock_irq(&tpg->acl_node_lock);
- if (!(se_nacl)) {
- printk("struct se_node_acl pointer is NULL\n");
+ if (!se_nacl) {
+ pr_err("struct se_node_acl pointer is NULL\n");
return ERR_PTR(-EINVAL);
}
/*
@@ -392,6 +396,8 @@ struct se_node_acl *core_tpg_add_initiator_node_acl(
INIT_LIST_HEAD(&acl->acl_list);
INIT_LIST_HEAD(&acl->acl_sess_list);
+ kref_init(&acl->acl_kref);
+ init_completion(&acl->acl_free_comp);
spin_lock_init(&acl->device_list_lock);
spin_lock_init(&acl->nacl_sess_lock);
atomic_set(&acl->acl_pr_ref_count, 0);
@@ -399,31 +405,30 @@ struct se_node_acl *core_tpg_add_initiator_node_acl(
snprintf(acl->initiatorname, TRANSPORT_IQN_LEN, "%s", initiatorname);
acl->se_tpg = tpg;
acl->acl_index = scsi_get_new_index(SCSI_AUTH_INTR_INDEX);
- spin_lock_init(&acl->stats_lock);
- TPG_TFO(tpg)->set_default_node_attributes(acl);
+ tpg->se_tpg_tfo->set_default_node_attributes(acl);
if (core_create_device_list_for_node(acl) < 0) {
- TPG_TFO(tpg)->tpg_release_fabric_acl(tpg, acl);
+ tpg->se_tpg_tfo->tpg_release_fabric_acl(tpg, acl);
return ERR_PTR(-ENOMEM);
}
if (core_set_queue_depth_for_node(tpg, acl) < 0) {
core_free_device_list_for_node(acl, tpg);
- TPG_TFO(tpg)->tpg_release_fabric_acl(tpg, acl);
+ tpg->se_tpg_tfo->tpg_release_fabric_acl(tpg, acl);
return ERR_PTR(-EINVAL);
}
- spin_lock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
list_add_tail(&acl->acl_list, &tpg->acl_node_list);
tpg->num_node_acls++;
- spin_unlock_bh(&tpg->acl_node_lock);
+ spin_unlock_irq(&tpg->acl_node_lock);
done:
- printk(KERN_INFO "%s_TPG[%hu] - Added ACL with TCQ Depth: %d for %s"
- " Initiator Node: %s\n", TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_tag(tpg), acl->queue_depth,
- TPG_TFO(tpg)->get_fabric_name(), initiatorname);
+ pr_debug("%s_TPG[%hu] - Added ACL with TCQ Depth: %d for %s"
+ " Initiator Node: %s\n", tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg), acl->queue_depth,
+ tpg->se_tpg_tfo->get_fabric_name(), initiatorname);
return acl;
}
@@ -438,48 +443,56 @@ int core_tpg_del_initiator_node_acl(
struct se_node_acl *acl,
int force)
{
+ LIST_HEAD(sess_list);
struct se_session *sess, *sess_tmp;
- int dynamic_acl = 0;
+ unsigned long flags;
+ int rc;
- spin_lock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
if (acl->dynamic_node_acl) {
acl->dynamic_node_acl = 0;
- dynamic_acl = 1;
}
list_del(&acl->acl_list);
tpg->num_node_acls--;
- spin_unlock_bh(&tpg->acl_node_lock);
+ spin_unlock_irq(&tpg->acl_node_lock);
- spin_lock_bh(&tpg->session_lock);
- list_for_each_entry_safe(sess, sess_tmp,
- &tpg->tpg_sess_list, sess_list) {
- if (sess->se_node_acl != acl)
- continue;
- /*
- * Determine if the session needs to be closed by our context.
- */
- if (!(TPG_TFO(tpg)->shutdown_session(sess)))
+ spin_lock_irqsave(&acl->nacl_sess_lock, flags);
+ acl->acl_stop = 1;
+
+ list_for_each_entry_safe(sess, sess_tmp, &acl->acl_sess_list,
+ sess_acl_list) {
+ if (sess->sess_tearing_down != 0)
continue;
- spin_unlock_bh(&tpg->session_lock);
- /*
- * If the $FABRIC_MOD session for the Initiator Node ACL exists,
- * forcefully shutdown the $FABRIC_MOD session/nexus.
- */
- TPG_TFO(tpg)->close_session(sess);
+ target_get_session(sess);
+ list_move(&sess->sess_acl_list, &sess_list);
+ }
+ spin_unlock_irqrestore(&acl->nacl_sess_lock, flags);
+
+ list_for_each_entry_safe(sess, sess_tmp, &sess_list, sess_acl_list) {
+ list_del(&sess->sess_acl_list);
- spin_lock_bh(&tpg->session_lock);
+ rc = tpg->se_tpg_tfo->shutdown_session(sess);
+ target_put_session(sess);
+ if (!rc)
+ continue;
+ target_put_session(sess);
}
- spin_unlock_bh(&tpg->session_lock);
+ target_put_nacl(acl);
+ /*
+ * Wait for last target_put_nacl() to complete in target_complete_nacl()
+ * for active fabric session transport_deregister_session() callbacks.
+ */
+ wait_for_completion(&acl->acl_free_comp);
core_tpg_wait_for_nacl_pr_ref(acl);
core_clear_initiator_node_from_tpg(acl, tpg);
core_free_device_list_for_node(acl, tpg);
- printk(KERN_INFO "%s_TPG[%hu] - Deleted ACL with TCQ Depth: %d for %s"
- " Initiator Node: %s\n", TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_tag(tpg), acl->queue_depth,
- TPG_TFO(tpg)->get_fabric_name(), acl->initiatorname);
+ pr_debug("%s_TPG[%hu] - Deleted ACL with TCQ Depth: %d for %s"
+ " Initiator Node: %s\n", tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg), acl->queue_depth,
+ tpg->se_tpg_tfo->get_fabric_name(), acl->initiatorname);
return 0;
}
@@ -497,48 +510,49 @@ int core_tpg_set_initiator_node_queue_depth(
{
struct se_session *sess, *init_sess = NULL;
struct se_node_acl *acl;
+ unsigned long flags;
int dynamic_acl = 0;
- spin_lock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname);
- if (!(acl)) {
- printk(KERN_ERR "Access Control List entry for %s Initiator"
+ if (!acl) {
+ pr_err("Access Control List entry for %s Initiator"
" Node %s does not exists for TPG %hu, ignoring"
- " request.\n", TPG_TFO(tpg)->get_fabric_name(),
- initiatorname, TPG_TFO(tpg)->tpg_get_tag(tpg));
- spin_unlock_bh(&tpg->acl_node_lock);
+ " request.\n", tpg->se_tpg_tfo->get_fabric_name(),
+ initiatorname, tpg->se_tpg_tfo->tpg_get_tag(tpg));
+ spin_unlock_irq(&tpg->acl_node_lock);
return -ENODEV;
}
if (acl->dynamic_node_acl) {
acl->dynamic_node_acl = 0;
dynamic_acl = 1;
}
- spin_unlock_bh(&tpg->acl_node_lock);
+ spin_unlock_irq(&tpg->acl_node_lock);
- spin_lock_bh(&tpg->session_lock);
+ spin_lock_irqsave(&tpg->session_lock, flags);
list_for_each_entry(sess, &tpg->tpg_sess_list, sess_list) {
if (sess->se_node_acl != acl)
continue;
if (!force) {
- printk(KERN_ERR "Unable to change queue depth for %s"
+ pr_err("Unable to change queue depth for %s"
" Initiator Node: %s while session is"
" operational. To forcefully change the queue"
" depth and force session reinstatement"
" use the \"force=1\" parameter.\n",
- TPG_TFO(tpg)->get_fabric_name(), initiatorname);
- spin_unlock_bh(&tpg->session_lock);
+ tpg->se_tpg_tfo->get_fabric_name(), initiatorname);
+ spin_unlock_irqrestore(&tpg->session_lock, flags);
- spin_lock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
if (dynamic_acl)
acl->dynamic_node_acl = 1;
- spin_unlock_bh(&tpg->acl_node_lock);
+ spin_unlock_irq(&tpg->acl_node_lock);
return -EEXIST;
}
/*
* Determine if the session needs to be closed by our context.
*/
- if (!(TPG_TFO(tpg)->shutdown_session(sess)))
+ if (!tpg->se_tpg_tfo->shutdown_session(sess))
continue;
init_sess = sess;
@@ -550,55 +564,85 @@ int core_tpg_set_initiator_node_queue_depth(
* Change the value in the Node's struct se_node_acl, and call
* core_set_queue_depth_for_node() to add the requested queue depth.
*
- * Finally call TPG_TFO(tpg)->close_session() to force session
+ * Finally call tpg->se_tpg_tfo->close_session() to force session
* reinstatement to occur if there is an active session for the
* $FABRIC_MOD Initiator Node in question.
*/
acl->queue_depth = queue_depth;
if (core_set_queue_depth_for_node(tpg, acl) < 0) {
- spin_unlock_bh(&tpg->session_lock);
+ spin_unlock_irqrestore(&tpg->session_lock, flags);
/*
* Force session reinstatement if
* core_set_queue_depth_for_node() failed, because we assume
* the $FABRIC_MOD has already the set session reinstatement
- * bit from TPG_TFO(tpg)->shutdown_session() called above.
+ * bit from tpg->se_tpg_tfo->shutdown_session() called above.
*/
if (init_sess)
- TPG_TFO(tpg)->close_session(init_sess);
+ tpg->se_tpg_tfo->close_session(init_sess);
- spin_lock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
if (dynamic_acl)
acl->dynamic_node_acl = 1;
- spin_unlock_bh(&tpg->acl_node_lock);
+ spin_unlock_irq(&tpg->acl_node_lock);
return -EINVAL;
}
- spin_unlock_bh(&tpg->session_lock);
+ spin_unlock_irqrestore(&tpg->session_lock, flags);
/*
* If the $FABRIC_MOD session for the Initiator Node ACL exists,
* forcefully shutdown the $FABRIC_MOD session/nexus.
*/
if (init_sess)
- TPG_TFO(tpg)->close_session(init_sess);
+ tpg->se_tpg_tfo->close_session(init_sess);
- printk(KERN_INFO "Successfuly changed queue depth to: %d for Initiator"
+ pr_debug("Successfully changed queue depth to: %d for Initiator"
" Node: %s on %s Target Portal Group: %u\n", queue_depth,
- initiatorname, TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_tag(tpg));
+ initiatorname, tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg));
- spin_lock_bh(&tpg->acl_node_lock);
+ spin_lock_irq(&tpg->acl_node_lock);
if (dynamic_acl)
acl->dynamic_node_acl = 1;
- spin_unlock_bh(&tpg->acl_node_lock);
+ spin_unlock_irq(&tpg->acl_node_lock);
return 0;
}
EXPORT_SYMBOL(core_tpg_set_initiator_node_queue_depth);
+/* core_tpg_set_initiator_node_tag():
+ *
+ * Initiator nodeacl tags are not used internally, but may be used by
+ * userspace to emulate aliases or groups.
+ * Returns length of newly-set tag or -EINVAL.
+ */
+int core_tpg_set_initiator_node_tag(
+ struct se_portal_group *tpg,
+ struct se_node_acl *acl,
+ const char *new_tag)
+{
+ if (strlen(new_tag) >= MAX_ACL_TAG_SIZE)
+ return -EINVAL;
+
+ if (!strncmp("NULL", new_tag, 4)) {
+ acl->acl_tag[0] = '\0';
+ return 0;
+ }
+
+ return snprintf(acl->acl_tag, MAX_ACL_TAG_SIZE, "%s", new_tag);
+}
+EXPORT_SYMBOL(core_tpg_set_initiator_node_tag);
+
+static void core_tpg_lun_ref_release(struct percpu_ref *ref)
+{
+ struct se_lun *lun = container_of(ref, struct se_lun, lun_ref);
+
+ complete(&lun->lun_ref_comp);
+}
+
static int core_tpg_setup_virtual_lun0(struct se_portal_group *se_tpg)
{
/* Set in core_dev_setup_virtual_lun0() */
- struct se_device *dev = se_global->g_lun0_dev;
+ struct se_device *dev = g_lun0_dev;
struct se_lun *lun = &se_tpg->tpg_virt_lun0;
u32 lun_access = TRANSPORT_LUNFLAGS_READ_ONLY;
int ret;
@@ -608,14 +652,13 @@ static int core_tpg_setup_virtual_lun0(struct se_portal_group *se_tpg)
atomic_set(&lun->lun_acl_count, 0);
init_completion(&lun->lun_shutdown_comp);
INIT_LIST_HEAD(&lun->lun_acl_list);
- INIT_LIST_HEAD(&lun->lun_cmd_list);
spin_lock_init(&lun->lun_acl_lock);
- spin_lock_init(&lun->lun_cmd_lock);
spin_lock_init(&lun->lun_sep_lock);
+ init_completion(&lun->lun_ref_comp);
- ret = core_tpg_post_addlun(se_tpg, lun, lun_access, dev);
+ ret = core_tpg_add_lun(se_tpg, lun, lun_access, dev);
if (ret < 0)
- return -1;
+ return ret;
return 0;
}
@@ -637,25 +680,25 @@ int core_tpg_register(
struct se_lun *lun;
u32 i;
- se_tpg->tpg_lun_list = kzalloc((sizeof(struct se_lun) *
- TRANSPORT_MAX_LUNS_PER_TPG), GFP_KERNEL);
- if (!(se_tpg->tpg_lun_list)) {
- printk(KERN_ERR "Unable to allocate struct se_portal_group->"
+ se_tpg->tpg_lun_list = array_zalloc(TRANSPORT_MAX_LUNS_PER_TPG,
+ sizeof(struct se_lun), GFP_KERNEL);
+ if (!se_tpg->tpg_lun_list) {
+ pr_err("Unable to allocate struct se_portal_group->"
"tpg_lun_list\n");
return -ENOMEM;
}
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- lun = &se_tpg->tpg_lun_list[i];
+ lun = se_tpg->tpg_lun_list[i];
lun->unpacked_lun = i;
+ lun->lun_link_magic = SE_LUN_LINK_MAGIC;
lun->lun_status = TRANSPORT_LUN_STATUS_FREE;
atomic_set(&lun->lun_acl_count, 0);
init_completion(&lun->lun_shutdown_comp);
INIT_LIST_HEAD(&lun->lun_acl_list);
- INIT_LIST_HEAD(&lun->lun_cmd_list);
spin_lock_init(&lun->lun_acl_lock);
- spin_lock_init(&lun->lun_cmd_lock);
spin_lock_init(&lun->lun_sep_lock);
+ init_completion(&lun->lun_ref_comp);
}
se_tpg->se_tpg_type = se_tpg_type;
@@ -664,7 +707,7 @@ int core_tpg_register(
se_tpg->se_tpg_wwn = se_wwn;
atomic_set(&se_tpg->tpg_pr_ref_count, 0);
INIT_LIST_HEAD(&se_tpg->acl_node_list);
- INIT_LIST_HEAD(&se_tpg->se_tpg_list);
+ INIT_LIST_HEAD(&se_tpg->se_tpg_node);
INIT_LIST_HEAD(&se_tpg->tpg_sess_list);
spin_lock_init(&se_tpg->acl_node_lock);
spin_lock_init(&se_tpg->session_lock);
@@ -672,16 +715,17 @@ int core_tpg_register(
if (se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL) {
if (core_tpg_setup_virtual_lun0(se_tpg) < 0) {
- kfree(se_tpg);
+ array_free(se_tpg->tpg_lun_list,
+ TRANSPORT_MAX_LUNS_PER_TPG);
return -ENOMEM;
}
}
- spin_lock_bh(&se_global->se_tpg_lock);
- list_add_tail(&se_tpg->se_tpg_list, &se_global->g_se_tpg_list);
- spin_unlock_bh(&se_global->se_tpg_lock);
+ spin_lock_bh(&tpg_lock);
+ list_add_tail(&se_tpg->se_tpg_node, &tpg_list);
+ spin_unlock_bh(&tpg_lock);
- printk(KERN_INFO "TARGET_CORE[%s]: Allocated %s struct se_portal_group for"
+ pr_debug("TARGET_CORE[%s]: Allocated %s struct se_portal_group for"
" endpoint: %s, Portal Tag: %u\n", tfo->get_fabric_name(),
(se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL) ?
"Normal" : "Discovery", (tfo->tpg_get_wwn(se_tpg) == NULL) ?
@@ -695,16 +739,16 @@ int core_tpg_deregister(struct se_portal_group *se_tpg)
{
struct se_node_acl *nacl, *nacl_tmp;
- printk(KERN_INFO "TARGET_CORE[%s]: Deallocating %s struct se_portal_group"
+ pr_debug("TARGET_CORE[%s]: Deallocating %s struct se_portal_group"
" for endpoint: %s Portal Tag %u\n",
(se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL) ?
- "Normal" : "Discovery", TPG_TFO(se_tpg)->get_fabric_name(),
- TPG_TFO(se_tpg)->tpg_get_wwn(se_tpg),
- TPG_TFO(se_tpg)->tpg_get_tag(se_tpg));
+ "Normal" : "Discovery", se_tpg->se_tpg_tfo->get_fabric_name(),
+ se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg),
+ se_tpg->se_tpg_tfo->tpg_get_tag(se_tpg));
- spin_lock_bh(&se_global->se_tpg_lock);
- list_del(&se_tpg->se_tpg_list);
- spin_unlock_bh(&se_global->se_tpg_lock);
+ spin_lock_bh(&tpg_lock);
+ list_del(&se_tpg->se_tpg_node);
+ spin_unlock_bh(&tpg_lock);
while (atomic_read(&se_tpg->tpg_pr_ref_count) != 0)
cpu_relax();
@@ -713,52 +757,52 @@ int core_tpg_deregister(struct se_portal_group *se_tpg)
* not been released because of TFO->tpg_check_demo_mode_cache() == 1
* in transport_deregister_session().
*/
- spin_lock_bh(&se_tpg->acl_node_lock);
+ spin_lock_irq(&se_tpg->acl_node_lock);
list_for_each_entry_safe(nacl, nacl_tmp, &se_tpg->acl_node_list,
acl_list) {
list_del(&nacl->acl_list);
se_tpg->num_node_acls--;
- spin_unlock_bh(&se_tpg->acl_node_lock);
+ spin_unlock_irq(&se_tpg->acl_node_lock);
core_tpg_wait_for_nacl_pr_ref(nacl);
core_free_device_list_for_node(nacl, se_tpg);
- TPG_TFO(se_tpg)->tpg_release_fabric_acl(se_tpg, nacl);
+ se_tpg->se_tpg_tfo->tpg_release_fabric_acl(se_tpg, nacl);
- spin_lock_bh(&se_tpg->acl_node_lock);
+ spin_lock_irq(&se_tpg->acl_node_lock);
}
- spin_unlock_bh(&se_tpg->acl_node_lock);
+ spin_unlock_irq(&se_tpg->acl_node_lock);
if (se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL)
core_tpg_release_virtual_lun0(se_tpg);
se_tpg->se_tpg_fabric_ptr = NULL;
- kfree(se_tpg->tpg_lun_list);
+ array_free(se_tpg->tpg_lun_list, TRANSPORT_MAX_LUNS_PER_TPG);
return 0;
}
EXPORT_SYMBOL(core_tpg_deregister);
-struct se_lun *core_tpg_pre_addlun(
+struct se_lun *core_tpg_alloc_lun(
struct se_portal_group *tpg,
u32 unpacked_lun)
{
struct se_lun *lun;
if (unpacked_lun > (TRANSPORT_MAX_LUNS_PER_TPG-1)) {
- printk(KERN_ERR "%s LUN: %u exceeds TRANSPORT_MAX_LUNS_PER_TPG"
+ pr_err("%s LUN: %u exceeds TRANSPORT_MAX_LUNS_PER_TPG"
"-1: %u for Target Portal Group: %u\n",
- TPG_TFO(tpg)->get_fabric_name(),
+ tpg->se_tpg_tfo->get_fabric_name(),
unpacked_lun, TRANSPORT_MAX_LUNS_PER_TPG-1,
- TPG_TFO(tpg)->tpg_get_tag(tpg));
+ tpg->se_tpg_tfo->tpg_get_tag(tpg));
return ERR_PTR(-EOVERFLOW);
}
spin_lock(&tpg->tpg_lun_lock);
- lun = &tpg->tpg_lun_list[unpacked_lun];
+ lun = tpg->tpg_lun_list[unpacked_lun];
if (lun->lun_status == TRANSPORT_LUN_STATUS_ACTIVE) {
- printk(KERN_ERR "TPG Logical Unit Number: %u is already active"
+ pr_err("TPG Logical Unit Number: %u is already active"
" on %s Target Portal Group: %u, ignoring request.\n",
- unpacked_lun, TPG_TFO(tpg)->get_fabric_name(),
- TPG_TFO(tpg)->tpg_get_tag(tpg));
+ unpacked_lun, tpg->se_tpg_tfo->get_fabric_name(),
+ tpg->se_tpg_tfo->tpg_get_tag(tpg));
spin_unlock(&tpg->tpg_lun_lock);
return ERR_PTR(-EINVAL);
}
@@ -767,14 +811,23 @@ struct se_lun *core_tpg_pre_addlun(
return lun;
}
-int core_tpg_post_addlun(
+int core_tpg_add_lun(
struct se_portal_group *tpg,
struct se_lun *lun,
u32 lun_access,
- void *lun_ptr)
+ struct se_device *dev)
{
- if (core_dev_export(lun_ptr, tpg, lun) < 0)
- return -1;
+ int ret;
+
+ ret = percpu_ref_init(&lun->lun_ref, core_tpg_lun_ref_release);
+ if (ret < 0)
+ return ret;
+
+ ret = core_dev_export(dev, tpg, lun);
+ if (ret < 0) {
+ percpu_ref_cancel_init(&lun->lun_ref);
+ return ret;
+ }
spin_lock(&tpg->tpg_lun_lock);
lun->lun_access = lun_access;
@@ -784,37 +837,28 @@ int core_tpg_post_addlun(
return 0;
}
-static void core_tpg_shutdown_lun(
- struct se_portal_group *tpg,
- struct se_lun *lun)
-{
- core_clear_lun_from_tpg(lun, tpg);
- transport_clear_lun_from_sessions(lun);
-}
-
struct se_lun *core_tpg_pre_dellun(
struct se_portal_group *tpg,
- u32 unpacked_lun,
- int *ret)
+ u32 unpacked_lun)
{
struct se_lun *lun;
if (unpacked_lun > (TRANSPORT_MAX_LUNS_PER_TPG-1)) {
- printk(KERN_ERR "%s LUN: %u exceeds TRANSPORT_MAX_LUNS_PER_TPG"
+ pr_err("%s LUN: %u exceeds TRANSPORT_MAX_LUNS_PER_TPG"
"-1: %u for Target Portal Group: %u\n",
- TPG_TFO(tpg)->get_fabric_name(), unpacked_lun,
+ tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun,
TRANSPORT_MAX_LUNS_PER_TPG-1,
- TPG_TFO(tpg)->tpg_get_tag(tpg));
+ tpg->se_tpg_tfo->tpg_get_tag(tpg));
return ERR_PTR(-EOVERFLOW);
}
spin_lock(&tpg->tpg_lun_lock);
- lun = &tpg->tpg_lun_list[unpacked_lun];
+ lun = tpg->tpg_lun_list[unpacked_lun];
if (lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE) {
- printk(KERN_ERR "%s Logical Unit Number: %u is not active on"
+ pr_err("%s Logical Unit Number: %u is not active on"
" Target Portal Group: %u, ignoring request.\n",
- TPG_TFO(tpg)->get_fabric_name(), unpacked_lun,
- TPG_TFO(tpg)->tpg_get_tag(tpg));
+ tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun,
+ tpg->se_tpg_tfo->tpg_get_tag(tpg));
spin_unlock(&tpg->tpg_lun_lock);
return ERR_PTR(-ENODEV);
}
@@ -827,7 +871,8 @@ int core_tpg_post_dellun(
struct se_portal_group *tpg,
struct se_lun *lun)
{
- core_tpg_shutdown_lun(tpg, lun);
+ core_clear_lun_from_tpg(lun, tpg);
+ transport_clear_lun_ref(lun);
core_dev_unexport(lun->lun_se_dev, tpg, lun);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 236e22d8cfa..7fa62fc93e0 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -3,10 +3,7 @@
*
* This file contains the Generic Target Engine Core.
*
- * 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
+ * (c) Copyright 2002-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -26,371 +23,178 @@
*
******************************************************************************/
-#include <linux/version.h>
#include <linux/net.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/slab.h>
-#include <linux/blkdev.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
#include <linux/kthread.h>
#include <linux/in.h>
#include <linux/cdrom.h>
+#include <linux/module.h>
+#include <linux/ratelimit.h>
#include <asm/unaligned.h>
#include <net/sock.h>
#include <net/tcp.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
-#include <scsi/libsas.h> /* For TASK_ATTR_* */
+#include <scsi/scsi_tcq.h>
#include <target/target_core_base.h>
-#include <target/target_core_device.h>
-#include <target/target_core_tmr.h>
-#include <target/target_core_tpg.h>
-#include <target/target_core_transport.h>
-#include <target/target_core_fabric_ops.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
#include <target/target_core_configfs.h>
+#include "target_core_internal.h"
#include "target_core_alua.h"
-#include "target_core_hba.h"
#include "target_core_pr.h"
-#include "target_core_scdb.h"
#include "target_core_ua.h"
-/* #define DEBUG_CDB_HANDLER */
-#ifdef DEBUG_CDB_HANDLER
-#define DEBUG_CDB_H(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_CDB_H(x...)
-#endif
-
-/* #define DEBUG_CMD_MAP */
-#ifdef DEBUG_CMD_MAP
-#define DEBUG_CMD_M(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_CMD_M(x...)
-#endif
-
-/* #define DEBUG_MEM_ALLOC */
-#ifdef DEBUG_MEM_ALLOC
-#define DEBUG_MEM(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_MEM(x...)
-#endif
-
-/* #define DEBUG_MEM2_ALLOC */
-#ifdef DEBUG_MEM2_ALLOC
-#define DEBUG_MEM2(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_MEM2(x...)
-#endif
-
-/* #define DEBUG_SG_CALC */
-#ifdef DEBUG_SG_CALC
-#define DEBUG_SC(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_SC(x...)
-#endif
-
-/* #define DEBUG_SE_OBJ */
-#ifdef DEBUG_SE_OBJ
-#define DEBUG_SO(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_SO(x...)
-#endif
-
-/* #define DEBUG_CMD_VOL */
-#ifdef DEBUG_CMD_VOL
-#define DEBUG_VOL(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_VOL(x...)
-#endif
-
-/* #define DEBUG_CMD_STOP */
-#ifdef DEBUG_CMD_STOP
-#define DEBUG_CS(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_CS(x...)
-#endif
-
-/* #define DEBUG_PASSTHROUGH */
-#ifdef DEBUG_PASSTHROUGH
-#define DEBUG_PT(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_PT(x...)
-#endif
-
-/* #define DEBUG_TASK_STOP */
-#ifdef DEBUG_TASK_STOP
-#define DEBUG_TS(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_TS(x...)
-#endif
-
-/* #define DEBUG_TRANSPORT_STOP */
-#ifdef DEBUG_TRANSPORT_STOP
-#define DEBUG_TRANSPORT_S(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_TRANSPORT_S(x...)
-#endif
-
-/* #define DEBUG_TASK_FAILURE */
-#ifdef DEBUG_TASK_FAILURE
-#define DEBUG_TF(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_TF(x...)
-#endif
-
-/* #define DEBUG_DEV_OFFLINE */
-#ifdef DEBUG_DEV_OFFLINE
-#define DEBUG_DO(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_DO(x...)
-#endif
-
-/* #define DEBUG_TASK_STATE */
-#ifdef DEBUG_TASK_STATE
-#define DEBUG_TSTATE(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_TSTATE(x...)
-#endif
-
-/* #define DEBUG_STATUS_THR */
-#ifdef DEBUG_STATUS_THR
-#define DEBUG_ST(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_ST(x...)
-#endif
-
-/* #define DEBUG_TASK_TIMEOUT */
-#ifdef DEBUG_TASK_TIMEOUT
-#define DEBUG_TT(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_TT(x...)
-#endif
-
-/* #define DEBUG_GENERIC_REQUEST_FAILURE */
-#ifdef DEBUG_GENERIC_REQUEST_FAILURE
-#define DEBUG_GRF(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_GRF(x...)
-#endif
-
-/* #define DEBUG_SAM_TASK_ATTRS */
-#ifdef DEBUG_SAM_TASK_ATTRS
-#define DEBUG_STA(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_STA(x...)
-#endif
-
-struct se_global *se_global;
-
-static struct kmem_cache *se_cmd_cache;
+#define CREATE_TRACE_POINTS
+#include <trace/events/target.h>
+
+static struct workqueue_struct *target_completion_wq;
static struct kmem_cache *se_sess_cache;
-struct kmem_cache *se_tmr_req_cache;
struct kmem_cache *se_ua_cache;
-struct kmem_cache *se_mem_cache;
struct kmem_cache *t10_pr_reg_cache;
struct kmem_cache *t10_alua_lu_gp_cache;
struct kmem_cache *t10_alua_lu_gp_mem_cache;
struct kmem_cache *t10_alua_tg_pt_gp_cache;
struct kmem_cache *t10_alua_tg_pt_gp_mem_cache;
+struct kmem_cache *t10_alua_lba_map_cache;
+struct kmem_cache *t10_alua_lba_map_mem_cache;
-/* Used for transport_dev_get_map_*() */
-typedef int (*map_func_t)(struct se_task *, u32);
-
-static int transport_generic_write_pending(struct se_cmd *);
-static int transport_processing_thread(void *);
-static int __transport_execute_tasks(struct se_device *dev);
static void transport_complete_task_attr(struct se_cmd *cmd);
-static void transport_direct_request_timeout(struct se_cmd *cmd);
-static void transport_free_dev_tasks(struct se_cmd *cmd);
-static u32 transport_generic_get_cdb_count(struct se_cmd *cmd,
- unsigned long long starting_lba, u32 sectors,
- enum dma_data_direction data_direction,
- struct list_head *mem_list, int set_counts);
-static int transport_generic_get_mem(struct se_cmd *cmd, u32 length,
- u32 dma_size);
-static int transport_generic_remove(struct se_cmd *cmd,
- int release_to_pool, int session_reinstatement);
-static int transport_get_sectors(struct se_cmd *cmd);
-static struct list_head *transport_init_se_mem_list(void);
-static int transport_map_sg_to_mem(struct se_cmd *cmd,
- struct list_head *se_mem_list, void *in_mem,
- u32 *se_mem_cnt);
-static void transport_memcpy_se_mem_read_contig(struct se_cmd *cmd,
- unsigned char *dst, struct list_head *se_mem_list);
-static void transport_release_fe_cmd(struct se_cmd *cmd);
-static void transport_remove_cmd_from_queue(struct se_cmd *cmd,
- struct se_queue_obj *qobj);
-static int transport_set_sense_codes(struct se_cmd *cmd, u8 asc, u8 ascq);
-static void transport_stop_all_task_timers(struct se_cmd *cmd);
-
-int transport_emulate_control_cdb(struct se_task *task);
-
-int init_se_global(void)
-{
- struct se_global *global;
+static void transport_handle_queue_full(struct se_cmd *cmd,
+ struct se_device *dev);
+static int transport_put_cmd(struct se_cmd *cmd);
+static void target_complete_ok_work(struct work_struct *work);
- global = kzalloc(sizeof(struct se_global), GFP_KERNEL);
- if (!(global)) {
- printk(KERN_ERR "Unable to allocate memory for struct se_global\n");
- return -1;
- }
-
- INIT_LIST_HEAD(&global->g_lu_gps_list);
- INIT_LIST_HEAD(&global->g_se_tpg_list);
- INIT_LIST_HEAD(&global->g_hba_list);
- INIT_LIST_HEAD(&global->g_se_dev_list);
- spin_lock_init(&global->g_device_lock);
- spin_lock_init(&global->hba_lock);
- spin_lock_init(&global->se_tpg_lock);
- spin_lock_init(&global->lu_gps_lock);
- spin_lock_init(&global->plugin_class_lock);
-
- se_cmd_cache = kmem_cache_create("se_cmd_cache",
- sizeof(struct se_cmd), __alignof__(struct se_cmd), 0, NULL);
- if (!(se_cmd_cache)) {
- printk(KERN_ERR "kmem_cache_create for struct se_cmd failed\n");
- goto out;
- }
- se_tmr_req_cache = kmem_cache_create("se_tmr_cache",
- sizeof(struct se_tmr_req), __alignof__(struct se_tmr_req),
- 0, NULL);
- if (!(se_tmr_req_cache)) {
- printk(KERN_ERR "kmem_cache_create() for struct se_tmr_req"
- " failed\n");
- goto out;
- }
+int init_se_kmem_caches(void)
+{
se_sess_cache = kmem_cache_create("se_sess_cache",
sizeof(struct se_session), __alignof__(struct se_session),
0, NULL);
- if (!(se_sess_cache)) {
- printk(KERN_ERR "kmem_cache_create() for struct se_session"
+ if (!se_sess_cache) {
+ pr_err("kmem_cache_create() for struct se_session"
" failed\n");
goto out;
}
se_ua_cache = kmem_cache_create("se_ua_cache",
sizeof(struct se_ua), __alignof__(struct se_ua),
0, NULL);
- if (!(se_ua_cache)) {
- printk(KERN_ERR "kmem_cache_create() for struct se_ua failed\n");
- goto out;
- }
- se_mem_cache = kmem_cache_create("se_mem_cache",
- sizeof(struct se_mem), __alignof__(struct se_mem), 0, NULL);
- if (!(se_mem_cache)) {
- printk(KERN_ERR "kmem_cache_create() for struct se_mem failed\n");
- goto out;
+ if (!se_ua_cache) {
+ pr_err("kmem_cache_create() for struct se_ua failed\n");
+ goto out_free_sess_cache;
}
t10_pr_reg_cache = kmem_cache_create("t10_pr_reg_cache",
sizeof(struct t10_pr_registration),
__alignof__(struct t10_pr_registration), 0, NULL);
- if (!(t10_pr_reg_cache)) {
- printk(KERN_ERR "kmem_cache_create() for struct t10_pr_registration"
+ if (!t10_pr_reg_cache) {
+ pr_err("kmem_cache_create() for struct t10_pr_registration"
" failed\n");
- goto out;
+ goto out_free_ua_cache;
}
t10_alua_lu_gp_cache = kmem_cache_create("t10_alua_lu_gp_cache",
sizeof(struct t10_alua_lu_gp), __alignof__(struct t10_alua_lu_gp),
0, NULL);
- if (!(t10_alua_lu_gp_cache)) {
- printk(KERN_ERR "kmem_cache_create() for t10_alua_lu_gp_cache"
+ if (!t10_alua_lu_gp_cache) {
+ pr_err("kmem_cache_create() for t10_alua_lu_gp_cache"
" failed\n");
- goto out;
+ goto out_free_pr_reg_cache;
}
t10_alua_lu_gp_mem_cache = kmem_cache_create("t10_alua_lu_gp_mem_cache",
sizeof(struct t10_alua_lu_gp_member),
__alignof__(struct t10_alua_lu_gp_member), 0, NULL);
- if (!(t10_alua_lu_gp_mem_cache)) {
- printk(KERN_ERR "kmem_cache_create() for t10_alua_lu_gp_mem_"
+ if (!t10_alua_lu_gp_mem_cache) {
+ pr_err("kmem_cache_create() for t10_alua_lu_gp_mem_"
"cache failed\n");
- goto out;
+ goto out_free_lu_gp_cache;
}
t10_alua_tg_pt_gp_cache = kmem_cache_create("t10_alua_tg_pt_gp_cache",
sizeof(struct t10_alua_tg_pt_gp),
__alignof__(struct t10_alua_tg_pt_gp), 0, NULL);
- if (!(t10_alua_tg_pt_gp_cache)) {
- printk(KERN_ERR "kmem_cache_create() for t10_alua_tg_pt_gp_"
+ if (!t10_alua_tg_pt_gp_cache) {
+ pr_err("kmem_cache_create() for t10_alua_tg_pt_gp_"
"cache failed\n");
- goto out;
+ goto out_free_lu_gp_mem_cache;
}
t10_alua_tg_pt_gp_mem_cache = kmem_cache_create(
"t10_alua_tg_pt_gp_mem_cache",
sizeof(struct t10_alua_tg_pt_gp_member),
__alignof__(struct t10_alua_tg_pt_gp_member),
0, NULL);
- if (!(t10_alua_tg_pt_gp_mem_cache)) {
- printk(KERN_ERR "kmem_cache_create() for t10_alua_tg_pt_gp_"
+ if (!t10_alua_tg_pt_gp_mem_cache) {
+ pr_err("kmem_cache_create() for t10_alua_tg_pt_gp_"
"mem_t failed\n");
- goto out;
+ goto out_free_tg_pt_gp_cache;
+ }
+ t10_alua_lba_map_cache = kmem_cache_create(
+ "t10_alua_lba_map_cache",
+ sizeof(struct t10_alua_lba_map),
+ __alignof__(struct t10_alua_lba_map), 0, NULL);
+ if (!t10_alua_lba_map_cache) {
+ pr_err("kmem_cache_create() for t10_alua_lba_map_"
+ "cache failed\n");
+ goto out_free_tg_pt_gp_mem_cache;
+ }
+ t10_alua_lba_map_mem_cache = kmem_cache_create(
+ "t10_alua_lba_map_mem_cache",
+ sizeof(struct t10_alua_lba_map_member),
+ __alignof__(struct t10_alua_lba_map_member), 0, NULL);
+ if (!t10_alua_lba_map_mem_cache) {
+ pr_err("kmem_cache_create() for t10_alua_lba_map_mem_"
+ "cache failed\n");
+ goto out_free_lba_map_cache;
}
- se_global = global;
+ target_completion_wq = alloc_workqueue("target_completion",
+ WQ_MEM_RECLAIM, 0);
+ if (!target_completion_wq)
+ goto out_free_lba_map_mem_cache;
return 0;
+
+out_free_lba_map_mem_cache:
+ kmem_cache_destroy(t10_alua_lba_map_mem_cache);
+out_free_lba_map_cache:
+ kmem_cache_destroy(t10_alua_lba_map_cache);
+out_free_tg_pt_gp_mem_cache:
+ kmem_cache_destroy(t10_alua_tg_pt_gp_mem_cache);
+out_free_tg_pt_gp_cache:
+ kmem_cache_destroy(t10_alua_tg_pt_gp_cache);
+out_free_lu_gp_mem_cache:
+ kmem_cache_destroy(t10_alua_lu_gp_mem_cache);
+out_free_lu_gp_cache:
+ kmem_cache_destroy(t10_alua_lu_gp_cache);
+out_free_pr_reg_cache:
+ kmem_cache_destroy(t10_pr_reg_cache);
+out_free_ua_cache:
+ kmem_cache_destroy(se_ua_cache);
+out_free_sess_cache:
+ kmem_cache_destroy(se_sess_cache);
out:
- if (se_cmd_cache)
- kmem_cache_destroy(se_cmd_cache);
- if (se_tmr_req_cache)
- kmem_cache_destroy(se_tmr_req_cache);
- if (se_sess_cache)
- kmem_cache_destroy(se_sess_cache);
- if (se_ua_cache)
- kmem_cache_destroy(se_ua_cache);
- if (se_mem_cache)
- kmem_cache_destroy(se_mem_cache);
- if (t10_pr_reg_cache)
- kmem_cache_destroy(t10_pr_reg_cache);
- if (t10_alua_lu_gp_cache)
- kmem_cache_destroy(t10_alua_lu_gp_cache);
- if (t10_alua_lu_gp_mem_cache)
- kmem_cache_destroy(t10_alua_lu_gp_mem_cache);
- if (t10_alua_tg_pt_gp_cache)
- kmem_cache_destroy(t10_alua_tg_pt_gp_cache);
- if (t10_alua_tg_pt_gp_mem_cache)
- kmem_cache_destroy(t10_alua_tg_pt_gp_mem_cache);
- kfree(global);
- return -1;
+ return -ENOMEM;
}
-void release_se_global(void)
+void release_se_kmem_caches(void)
{
- struct se_global *global;
-
- global = se_global;
- if (!(global))
- return;
-
- kmem_cache_destroy(se_cmd_cache);
- kmem_cache_destroy(se_tmr_req_cache);
+ destroy_workqueue(target_completion_wq);
kmem_cache_destroy(se_sess_cache);
kmem_cache_destroy(se_ua_cache);
- kmem_cache_destroy(se_mem_cache);
kmem_cache_destroy(t10_pr_reg_cache);
kmem_cache_destroy(t10_alua_lu_gp_cache);
kmem_cache_destroy(t10_alua_lu_gp_mem_cache);
kmem_cache_destroy(t10_alua_tg_pt_gp_cache);
kmem_cache_destroy(t10_alua_tg_pt_gp_mem_cache);
- kfree(global);
-
- se_global = NULL;
+ kmem_cache_destroy(t10_alua_lba_map_cache);
+ kmem_cache_destroy(t10_alua_lba_map_mem_cache);
}
-/* SCSI statistics table index */
-static struct scsi_index_table scsi_index_table;
-
-/*
- * Initialize the index table for allocating unique row indexes to various mib
- * tables.
- */
-void init_scsi_index_table(void)
-{
- memset(&scsi_index_table, 0, sizeof(struct scsi_index_table));
- spin_lock_init(&scsi_index_table.lock);
-}
+/* This code ensures unique mib indexes are handed out. */
+static DEFINE_SPINLOCK(scsi_mib_index_lock);
+static u32 scsi_mib_index[SCSI_INDEX_TYPE_MAX];
/*
* Allocate a new row index for the entry type specified
@@ -399,85 +203,114 @@ u32 scsi_get_new_index(scsi_index_t type)
{
u32 new_index;
- if ((type < 0) || (type >= SCSI_INDEX_TYPE_MAX)) {
- printk(KERN_ERR "Invalid index type %d\n", type);
- return -EINVAL;
- }
+ BUG_ON((type < 0) || (type >= SCSI_INDEX_TYPE_MAX));
- spin_lock(&scsi_index_table.lock);
- new_index = ++scsi_index_table.scsi_mib_index[type];
- if (new_index == 0)
- new_index = ++scsi_index_table.scsi_mib_index[type];
- spin_unlock(&scsi_index_table.lock);
+ spin_lock(&scsi_mib_index_lock);
+ new_index = ++scsi_mib_index[type];
+ spin_unlock(&scsi_mib_index_lock);
return new_index;
}
-void transport_init_queue_obj(struct se_queue_obj *qobj)
-{
- atomic_set(&qobj->queue_cnt, 0);
- INIT_LIST_HEAD(&qobj->qobj_list);
- init_waitqueue_head(&qobj->thread_wq);
- spin_lock_init(&qobj->cmd_queue_lock);
-}
-EXPORT_SYMBOL(transport_init_queue_obj);
-
-static int transport_subsystem_reqmods(void)
+void transport_subsystem_check_init(void)
{
int ret;
+ static int sub_api_initialized;
+
+ if (sub_api_initialized)
+ return;
ret = request_module("target_core_iblock");
if (ret != 0)
- printk(KERN_ERR "Unable to load target_core_iblock\n");
+ pr_err("Unable to load target_core_iblock\n");
ret = request_module("target_core_file");
if (ret != 0)
- printk(KERN_ERR "Unable to load target_core_file\n");
+ pr_err("Unable to load target_core_file\n");
ret = request_module("target_core_pscsi");
if (ret != 0)
- printk(KERN_ERR "Unable to load target_core_pscsi\n");
+ pr_err("Unable to load target_core_pscsi\n");
- ret = request_module("target_core_stgt");
- if (ret != 0)
- printk(KERN_ERR "Unable to load target_core_stgt\n");
+ sub_api_initialized = 1;
+}
- return 0;
+struct se_session *transport_init_session(enum target_prot_op sup_prot_ops)
+{
+ struct se_session *se_sess;
+
+ se_sess = kmem_cache_zalloc(se_sess_cache, GFP_KERNEL);
+ if (!se_sess) {
+ pr_err("Unable to allocate struct se_session from"
+ " se_sess_cache\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ INIT_LIST_HEAD(&se_sess->sess_list);
+ INIT_LIST_HEAD(&se_sess->sess_acl_list);
+ INIT_LIST_HEAD(&se_sess->sess_cmd_list);
+ INIT_LIST_HEAD(&se_sess->sess_wait_list);
+ spin_lock_init(&se_sess->sess_cmd_lock);
+ kref_init(&se_sess->sess_kref);
+ se_sess->sup_prot_ops = sup_prot_ops;
+
+ return se_sess;
}
+EXPORT_SYMBOL(transport_init_session);
-int transport_subsystem_check_init(void)
+int transport_alloc_session_tags(struct se_session *se_sess,
+ unsigned int tag_num, unsigned int tag_size)
{
- if (se_global->g_sub_api_initialized)
- return 0;
- /*
- * Request the loading of known TCM subsystem plugins..
- */
- if (transport_subsystem_reqmods() < 0)
- return -1;
+ int rc;
+
+ se_sess->sess_cmd_map = kzalloc(tag_num * tag_size,
+ GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
+ if (!se_sess->sess_cmd_map) {
+ se_sess->sess_cmd_map = vzalloc(tag_num * tag_size);
+ if (!se_sess->sess_cmd_map) {
+ pr_err("Unable to allocate se_sess->sess_cmd_map\n");
+ return -ENOMEM;
+ }
+ }
+
+ rc = percpu_ida_init(&se_sess->sess_tag_pool, tag_num);
+ if (rc < 0) {
+ pr_err("Unable to init se_sess->sess_tag_pool,"
+ " tag_num: %u\n", tag_num);
+ if (is_vmalloc_addr(se_sess->sess_cmd_map))
+ vfree(se_sess->sess_cmd_map);
+ else
+ kfree(se_sess->sess_cmd_map);
+ se_sess->sess_cmd_map = NULL;
+ return -ENOMEM;
+ }
- se_global->g_sub_api_initialized = 1;
return 0;
}
+EXPORT_SYMBOL(transport_alloc_session_tags);
-struct se_session *transport_init_session(void)
+struct se_session *transport_init_session_tags(unsigned int tag_num,
+ unsigned int tag_size,
+ enum target_prot_op sup_prot_ops)
{
struct se_session *se_sess;
+ int rc;
- se_sess = kmem_cache_zalloc(se_sess_cache, GFP_KERNEL);
- if (!(se_sess)) {
- printk(KERN_ERR "Unable to allocate struct se_session from"
- " se_sess_cache\n");
+ se_sess = transport_init_session(sup_prot_ops);
+ if (IS_ERR(se_sess))
+ return se_sess;
+
+ rc = transport_alloc_session_tags(se_sess, tag_num, tag_size);
+ if (rc < 0) {
+ transport_free_session(se_sess);
return ERR_PTR(-ENOMEM);
}
- INIT_LIST_HEAD(&se_sess->sess_list);
- INIT_LIST_HEAD(&se_sess->sess_acl_list);
return se_sess;
}
-EXPORT_SYMBOL(transport_init_session);
+EXPORT_SYMBOL(transport_init_session_tags);
/*
- * Called with spin_lock_bh(&struct se_portal_group->session_lock called.
+ * Called with spin_lock_irqsave(&struct se_portal_group->session_lock called.
*/
void __transport_register_session(
struct se_portal_group *se_tpg,
@@ -500,12 +333,14 @@ void __transport_register_session(
* If the fabric module supports an ISID based TransportID,
* save this value in binary from the fabric I_T Nexus now.
*/
- if (TPG_TFO(se_tpg)->sess_get_initiator_sid != NULL) {
+ if (se_tpg->se_tpg_tfo->sess_get_initiator_sid != NULL) {
memset(&buf[0], 0, PR_REG_ISID_LEN);
- TPG_TFO(se_tpg)->sess_get_initiator_sid(se_sess,
+ se_tpg->se_tpg_tfo->sess_get_initiator_sid(se_sess,
&buf[0], PR_REG_ISID_LEN);
se_sess->sess_bin_isid = get_unaligned_be64(&buf[0]);
}
+ kref_get(&se_nacl->acl_kref);
+
spin_lock_irq(&se_nacl->nacl_sess_lock);
/*
* The se_nacl->nacl_sess pointer will be set to the
@@ -519,8 +354,8 @@ void __transport_register_session(
}
list_add_tail(&se_sess->sess_list, &se_tpg->tpg_sess_list);
- printk(KERN_INFO "TARGET_CORE[%s]: Registered fabric_sess_ptr: %p\n",
- TPG_TFO(se_tpg)->get_fabric_name(), se_sess->fabric_sess_ptr);
+ pr_debug("TARGET_CORE[%s]: Registered fabric_sess_ptr: %p\n",
+ se_tpg->se_tpg_tfo->get_fabric_name(), se_sess->fabric_sess_ptr);
}
EXPORT_SYMBOL(__transport_register_session);
@@ -530,23 +365,66 @@ void transport_register_session(
struct se_session *se_sess,
void *fabric_sess_ptr)
{
- spin_lock_bh(&se_tpg->session_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&se_tpg->session_lock, flags);
__transport_register_session(se_tpg, se_nacl, se_sess, fabric_sess_ptr);
- spin_unlock_bh(&se_tpg->session_lock);
+ spin_unlock_irqrestore(&se_tpg->session_lock, flags);
}
EXPORT_SYMBOL(transport_register_session);
+static void target_release_session(struct kref *kref)
+{
+ struct se_session *se_sess = container_of(kref,
+ struct se_session, sess_kref);
+ struct se_portal_group *se_tpg = se_sess->se_tpg;
+
+ se_tpg->se_tpg_tfo->close_session(se_sess);
+}
+
+void target_get_session(struct se_session *se_sess)
+{
+ kref_get(&se_sess->sess_kref);
+}
+EXPORT_SYMBOL(target_get_session);
+
+void target_put_session(struct se_session *se_sess)
+{
+ struct se_portal_group *tpg = se_sess->se_tpg;
+
+ if (tpg->se_tpg_tfo->put_session != NULL) {
+ tpg->se_tpg_tfo->put_session(se_sess);
+ return;
+ }
+ kref_put(&se_sess->sess_kref, target_release_session);
+}
+EXPORT_SYMBOL(target_put_session);
+
+static void target_complete_nacl(struct kref *kref)
+{
+ struct se_node_acl *nacl = container_of(kref,
+ struct se_node_acl, acl_kref);
+
+ complete(&nacl->acl_free_comp);
+}
+
+void target_put_nacl(struct se_node_acl *nacl)
+{
+ kref_put(&nacl->acl_kref, target_complete_nacl);
+}
+
void transport_deregister_session_configfs(struct se_session *se_sess)
{
struct se_node_acl *se_nacl;
-
+ unsigned long flags;
/*
* Used by struct se_node_acl's under ConfigFS to locate active struct se_session
*/
se_nacl = se_sess->se_node_acl;
- if ((se_nacl)) {
- spin_lock_irq(&se_nacl->nacl_sess_lock);
- list_del(&se_sess->sess_acl_list);
+ if (se_nacl) {
+ spin_lock_irqsave(&se_nacl->nacl_sess_lock, flags);
+ if (se_nacl->acl_stop == 0)
+ list_del(&se_sess->sess_acl_list);
/*
* If the session list is empty, then clear the pointer.
* Otherwise, set the struct se_session pointer from the tail
@@ -559,13 +437,20 @@ void transport_deregister_session_configfs(struct se_session *se_sess)
se_nacl->acl_sess_list.prev,
struct se_session, sess_acl_list);
}
- spin_unlock_irq(&se_nacl->nacl_sess_lock);
+ spin_unlock_irqrestore(&se_nacl->nacl_sess_lock, flags);
}
}
EXPORT_SYMBOL(transport_deregister_session_configfs);
void transport_free_session(struct se_session *se_sess)
{
+ if (se_sess->sess_cmd_map) {
+ percpu_ida_destroy(&se_sess->sess_tag_pool);
+ if (is_vmalloc_addr(se_sess->sess_cmd_map))
+ vfree(se_sess->sess_cmd_map);
+ else
+ kfree(se_sess->sess_cmd_map);
+ }
kmem_cache_free(se_sess_cache, se_sess);
}
EXPORT_SYMBOL(transport_free_session);
@@ -573,650 +458,314 @@ EXPORT_SYMBOL(transport_free_session);
void transport_deregister_session(struct se_session *se_sess)
{
struct se_portal_group *se_tpg = se_sess->se_tpg;
+ struct target_core_fabric_ops *se_tfo;
struct se_node_acl *se_nacl;
+ unsigned long flags;
+ bool comp_nacl = true;
- if (!(se_tpg)) {
+ if (!se_tpg) {
transport_free_session(se_sess);
return;
}
+ se_tfo = se_tpg->se_tpg_tfo;
- spin_lock_bh(&se_tpg->session_lock);
+ spin_lock_irqsave(&se_tpg->session_lock, flags);
list_del(&se_sess->sess_list);
se_sess->se_tpg = NULL;
se_sess->fabric_sess_ptr = NULL;
- spin_unlock_bh(&se_tpg->session_lock);
+ spin_unlock_irqrestore(&se_tpg->session_lock, flags);
/*
* Determine if we need to do extra work for this initiator node's
* struct se_node_acl if it had been previously dynamically generated.
*/
se_nacl = se_sess->se_node_acl;
- if ((se_nacl)) {
- spin_lock_bh(&se_tpg->acl_node_lock);
- if (se_nacl->dynamic_node_acl) {
- if (!(TPG_TFO(se_tpg)->tpg_check_demo_mode_cache(
- se_tpg))) {
- list_del(&se_nacl->acl_list);
- se_tpg->num_node_acls--;
- spin_unlock_bh(&se_tpg->acl_node_lock);
-
- core_tpg_wait_for_nacl_pr_ref(se_nacl);
- core_free_device_list_for_node(se_nacl, se_tpg);
- TPG_TFO(se_tpg)->tpg_release_fabric_acl(se_tpg,
- se_nacl);
- spin_lock_bh(&se_tpg->acl_node_lock);
- }
+
+ spin_lock_irqsave(&se_tpg->acl_node_lock, flags);
+ if (se_nacl && se_nacl->dynamic_node_acl) {
+ if (!se_tfo->tpg_check_demo_mode_cache(se_tpg)) {
+ list_del(&se_nacl->acl_list);
+ se_tpg->num_node_acls--;
+ spin_unlock_irqrestore(&se_tpg->acl_node_lock, flags);
+ core_tpg_wait_for_nacl_pr_ref(se_nacl);
+ core_free_device_list_for_node(se_nacl, se_tpg);
+ se_tfo->tpg_release_fabric_acl(se_tpg, se_nacl);
+
+ comp_nacl = false;
+ spin_lock_irqsave(&se_tpg->acl_node_lock, flags);
}
- spin_unlock_bh(&se_tpg->acl_node_lock);
}
+ spin_unlock_irqrestore(&se_tpg->acl_node_lock, flags);
- transport_free_session(se_sess);
+ pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n",
+ se_tpg->se_tpg_tfo->get_fabric_name());
+ /*
+ * If last kref is dropping now for an explicit NodeACL, awake sleeping
+ * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group
+ * removal context.
+ */
+ if (se_nacl && comp_nacl)
+ target_put_nacl(se_nacl);
- printk(KERN_INFO "TARGET_CORE[%s]: Deregistered fabric_sess\n",
- TPG_TFO(se_tpg)->get_fabric_name());
+ transport_free_session(se_sess);
}
EXPORT_SYMBOL(transport_deregister_session);
/*
- * Called with T_TASK(cmd)->t_state_lock held.
+ * Called with cmd->t_state_lock held.
*/
-static void transport_all_task_dev_remove_state(struct se_cmd *cmd)
+static void target_remove_from_state_list(struct se_cmd *cmd)
{
- struct se_device *dev;
- struct se_task *task;
+ struct se_device *dev = cmd->se_dev;
unsigned long flags;
- if (!T_TASK(cmd))
+ if (!dev)
return;
- list_for_each_entry(task, &T_TASK(cmd)->t_task_list, t_list) {
- dev = task->se_dev;
- if (!(dev))
- continue;
-
- if (atomic_read(&task->task_active))
- continue;
-
- if (!(atomic_read(&task->task_state_active)))
- continue;
-
- spin_lock_irqsave(&dev->execute_task_lock, flags);
- list_del(&task->t_state_list);
- DEBUG_TSTATE("Removed ITT: 0x%08x dev: %p task[%p]\n",
- CMD_TFO(cmd)->tfo_get_task_tag(cmd), dev, task);
- spin_unlock_irqrestore(&dev->execute_task_lock, flags);
+ if (cmd->transport_state & CMD_T_BUSY)
+ return;
- atomic_set(&task->task_state_active, 0);
- atomic_dec(&T_TASK(cmd)->t_task_cdbs_ex_left);
+ spin_lock_irqsave(&dev->execute_task_lock, flags);
+ if (cmd->state_active) {
+ list_del(&cmd->state_list);
+ cmd->state_active = false;
}
+ spin_unlock_irqrestore(&dev->execute_task_lock, flags);
}
-/* transport_cmd_check_stop():
- *
- * 'transport_off = 1' determines if t_transport_active should be cleared.
- * 'transport_off = 2' determines if task_dev_state should be removed.
- *
- * A non-zero u8 t_state sets cmd->t_state.
- * Returns 1 when command is stopped, else 0.
- */
-static int transport_cmd_check_stop(
- struct se_cmd *cmd,
- int transport_off,
- u8 t_state)
+static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists,
+ bool write_pending)
{
unsigned long flags;
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- /*
- * Determine if IOCTL context caller in requesting the stopping of this
- * command for LUN shutdown purposes.
- */
- if (atomic_read(&T_TASK(cmd)->transport_lun_stop)) {
- DEBUG_CS("%s:%d atomic_read(&T_TASK(cmd)->transport_lun_stop)"
- " == TRUE for ITT: 0x%08x\n", __func__, __LINE__,
- CMD_TFO(cmd)->get_task_tag(cmd));
-
- cmd->deferred_t_state = cmd->t_state;
- cmd->t_state = TRANSPORT_DEFERRED_CMD;
- atomic_set(&T_TASK(cmd)->t_transport_active, 0);
- if (transport_off == 2)
- transport_all_task_dev_remove_state(cmd);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-
- complete(&T_TASK(cmd)->transport_lun_stop_comp);
- return 1;
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ if (write_pending)
+ cmd->t_state = TRANSPORT_WRITE_PENDING;
+
+ if (remove_from_lists) {
+ target_remove_from_state_list(cmd);
+
+ /*
+ * Clear struct se_cmd->se_lun before the handoff to FE.
+ */
+ cmd->se_lun = NULL;
}
+
/*
* Determine if frontend context caller is requesting the stopping of
- * this command for frontend excpections.
+ * this command for frontend exceptions.
*/
- if (atomic_read(&T_TASK(cmd)->t_transport_stop)) {
- DEBUG_CS("%s:%d atomic_read(&T_TASK(cmd)->t_transport_stop) =="
- " TRUE for ITT: 0x%08x\n", __func__, __LINE__,
- CMD_TFO(cmd)->get_task_tag(cmd));
+ if (cmd->transport_state & CMD_T_STOP) {
+ pr_debug("%s:%d CMD_T_STOP for ITT: 0x%08x\n",
+ __func__, __LINE__,
+ cmd->se_tfo->get_task_tag(cmd));
- cmd->deferred_t_state = cmd->t_state;
- cmd->t_state = TRANSPORT_DEFERRED_CMD;
- if (transport_off == 2)
- transport_all_task_dev_remove_state(cmd);
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- /*
- * Clear struct se_cmd->se_lun before the transport_off == 2 handoff
- * to FE.
- */
- if (transport_off == 2)
- cmd->se_lun = NULL;
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-
- complete(&T_TASK(cmd)->t_transport_stop_comp);
+ complete_all(&cmd->t_transport_stop_comp);
return 1;
}
- if (transport_off) {
- atomic_set(&T_TASK(cmd)->t_transport_active, 0);
- if (transport_off == 2) {
- transport_all_task_dev_remove_state(cmd);
- /*
- * Clear struct se_cmd->se_lun before the transport_off == 2
- * handoff to fabric module.
- */
- cmd->se_lun = NULL;
- /*
- * Some fabric modules like tcm_loop can release
- * their internally allocated I/O refrence now and
- * struct se_cmd now.
- */
- if (CMD_TFO(cmd)->check_stop_free != NULL) {
- spin_unlock_irqrestore(
- &T_TASK(cmd)->t_state_lock, flags);
-
- CMD_TFO(cmd)->check_stop_free(cmd);
- return 1;
- }
- }
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- return 0;
- } else if (t_state)
- cmd->t_state = t_state;
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
+ cmd->transport_state &= ~CMD_T_ACTIVE;
+ if (remove_from_lists) {
+ /*
+ * Some fabric modules like tcm_loop can release
+ * their internally allocated I/O reference now and
+ * struct se_cmd now.
+ *
+ * Fabric modules are expected to return '1' here if the
+ * se_cmd being passed is released at this point,
+ * or zero if not being released.
+ */
+ if (cmd->se_tfo->check_stop_free != NULL) {
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ return cmd->se_tfo->check_stop_free(cmd);
+ }
+ }
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
return 0;
}
static int transport_cmd_check_stop_to_fabric(struct se_cmd *cmd)
{
- return transport_cmd_check_stop(cmd, 2, 0);
+ return transport_cmd_check_stop(cmd, true, false);
}
static void transport_lun_remove_cmd(struct se_cmd *cmd)
{
- struct se_lun *lun = SE_LUN(cmd);
- unsigned long flags;
+ struct se_lun *lun = cmd->se_lun;
if (!lun)
return;
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- if (!(atomic_read(&T_TASK(cmd)->transport_dev_active))) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- goto check_lun;
- }
- atomic_set(&T_TASK(cmd)->transport_dev_active, 0);
- transport_all_task_dev_remove_state(cmd);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-
- transport_free_dev_tasks(cmd);
-
-check_lun:
- spin_lock_irqsave(&lun->lun_cmd_lock, flags);
- if (atomic_read(&T_TASK(cmd)->transport_lun_active)) {
- list_del(&cmd->se_lun_list);
- atomic_set(&T_TASK(cmd)->transport_lun_active, 0);
-#if 0
- printk(KERN_INFO "Removed ITT: 0x%08x from LUN LIST[%d]\n"
- CMD_TFO(cmd)->get_task_tag(cmd), lun->unpacked_lun);
-#endif
- }
- spin_unlock_irqrestore(&lun->lun_cmd_lock, flags);
+ if (cmpxchg(&cmd->lun_ref_active, true, false))
+ percpu_ref_put(&lun->lun_ref);
}
void transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
{
- transport_remove_cmd_from_queue(cmd, SE_DEV(cmd)->dev_queue_obj);
- transport_lun_remove_cmd(cmd);
-
- if (transport_cmd_check_stop_to_fabric(cmd))
- return;
+ if (cmd->se_cmd_flags & SCF_SE_LUN_CMD)
+ transport_lun_remove_cmd(cmd);
+ /*
+ * Allow the fabric driver to unmap any resources before
+ * releasing the descriptor via TFO->release_cmd()
+ */
if (remove)
- transport_generic_remove(cmd, 0, 0);
-}
-
-void transport_cmd_finish_abort_tmr(struct se_cmd *cmd)
-{
- transport_remove_cmd_from_queue(cmd, SE_DEV(cmd)->dev_queue_obj);
+ cmd->se_tfo->aborted_task(cmd);
if (transport_cmd_check_stop_to_fabric(cmd))
return;
-
- transport_generic_remove(cmd, 0, 0);
+ if (remove)
+ transport_put_cmd(cmd);
}
-static int transport_add_cmd_to_queue(
- struct se_cmd *cmd,
- int t_state)
+static void target_complete_failure_work(struct work_struct *work)
{
- struct se_device *dev = cmd->se_dev;
- struct se_queue_obj *qobj = dev->dev_queue_obj;
- struct se_queue_req *qr;
- unsigned long flags;
-
- qr = kzalloc(sizeof(struct se_queue_req), GFP_ATOMIC);
- if (!(qr)) {
- printk(KERN_ERR "Unable to allocate memory for"
- " struct se_queue_req\n");
- return -1;
- }
- INIT_LIST_HEAD(&qr->qr_list);
-
- qr->cmd = (void *)cmd;
- qr->state = t_state;
+ struct se_cmd *cmd = container_of(work, struct se_cmd, work);
- if (t_state) {
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- cmd->t_state = t_state;
- atomic_set(&T_TASK(cmd)->t_transport_active, 1);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- }
-
- spin_lock_irqsave(&qobj->cmd_queue_lock, flags);
- list_add_tail(&qr->qr_list, &qobj->qobj_list);
- atomic_inc(&T_TASK(cmd)->t_transport_queue_active);
- spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
-
- atomic_inc(&qobj->queue_cnt);
- wake_up_interruptible(&qobj->thread_wq);
- return 0;
+ transport_generic_request_failure(cmd,
+ TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE);
}
/*
- * Called with struct se_queue_obj->cmd_queue_lock held.
+ * Used when asking transport to copy Sense Data from the underlying
+ * Linux/SCSI struct scsi_cmnd
*/
-static struct se_queue_req *
-__transport_get_qr_from_queue(struct se_queue_obj *qobj)
+static unsigned char *transport_get_sense_buffer(struct se_cmd *cmd)
{
- struct se_cmd *cmd;
- struct se_queue_req *qr = NULL;
-
- if (list_empty(&qobj->qobj_list))
- return NULL;
-
- list_for_each_entry(qr, &qobj->qobj_list, qr_list)
- break;
+ struct se_device *dev = cmd->se_dev;
- if (qr->cmd) {
- cmd = (struct se_cmd *)qr->cmd;
- atomic_dec(&T_TASK(cmd)->t_transport_queue_active);
- }
- list_del(&qr->qr_list);
- atomic_dec(&qobj->queue_cnt);
+ WARN_ON(!cmd->se_lun);
- return qr;
-}
-
-static struct se_queue_req *
-transport_get_qr_from_queue(struct se_queue_obj *qobj)
-{
- struct se_cmd *cmd;
- struct se_queue_req *qr;
- unsigned long flags;
-
- spin_lock_irqsave(&qobj->cmd_queue_lock, flags);
- if (list_empty(&qobj->qobj_list)) {
- spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
+ if (!dev)
return NULL;
- }
- list_for_each_entry(qr, &qobj->qobj_list, qr_list)
- break;
+ if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION)
+ return NULL;
- if (qr->cmd) {
- cmd = (struct se_cmd *)qr->cmd;
- atomic_dec(&T_TASK(cmd)->t_transport_queue_active);
- }
- list_del(&qr->qr_list);
- atomic_dec(&qobj->queue_cnt);
- spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
+ cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER;
- return qr;
+ pr_debug("HBA_[%u]_PLUG[%s]: Requesting sense for SAM STATUS: 0x%02x\n",
+ dev->se_hba->hba_id, dev->transport->name, cmd->scsi_status);
+ return cmd->sense_buffer;
}
-static void transport_remove_cmd_from_queue(struct se_cmd *cmd,
- struct se_queue_obj *qobj)
+void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
{
- struct se_cmd *q_cmd;
- struct se_queue_req *qr = NULL, *qr_p = NULL;
+ struct se_device *dev = cmd->se_dev;
+ int success = scsi_status == GOOD;
unsigned long flags;
- spin_lock_irqsave(&qobj->cmd_queue_lock, flags);
- if (!(atomic_read(&T_TASK(cmd)->t_transport_queue_active))) {
- spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
- return;
- }
-
- list_for_each_entry_safe(qr, qr_p, &qobj->qobj_list, qr_list) {
- q_cmd = (struct se_cmd *)qr->cmd;
- if (q_cmd != cmd)
- continue;
-
- atomic_dec(&T_TASK(q_cmd)->t_transport_queue_active);
- atomic_dec(&qobj->queue_cnt);
- list_del(&qr->qr_list);
- kfree(qr);
- }
- spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
-
- if (atomic_read(&T_TASK(cmd)->t_transport_queue_active)) {
- printk(KERN_ERR "ITT: 0x%08x t_transport_queue_active: %d\n",
- CMD_TFO(cmd)->get_task_tag(cmd),
- atomic_read(&T_TASK(cmd)->t_transport_queue_active));
- }
-}
-
-/*
- * Completion function used by TCM subsystem plugins (such as FILEIO)
- * for queueing up response from struct se_subsystem_api->do_task()
- */
-void transport_complete_sync_cache(struct se_cmd *cmd, int good)
-{
- struct se_task *task = list_entry(T_TASK(cmd)->t_task_list.next,
- struct se_task, t_list);
-
- if (good) {
- cmd->scsi_status = SAM_STAT_GOOD;
- task->task_scsi_status = GOOD;
- } else {
- task->task_scsi_status = SAM_STAT_CHECK_CONDITION;
- task->task_error_status = PYX_TRANSPORT_ILLEGAL_REQUEST;
- TASK_CMD(task)->transport_error_status =
- PYX_TRANSPORT_ILLEGAL_REQUEST;
- }
+ cmd->scsi_status = scsi_status;
- transport_complete_task(task, good);
-}
-EXPORT_SYMBOL(transport_complete_sync_cache);
-/* transport_complete_task():
- *
- * Called from interrupt and non interrupt context depending
- * on the transport plugin.
- */
-void transport_complete_task(struct se_task *task, int success)
-{
- struct se_cmd *cmd = TASK_CMD(task);
- struct se_device *dev = task->se_dev;
- int t_state;
- unsigned long flags;
-#if 0
- printk(KERN_INFO "task: %p CDB: 0x%02x obj_ptr: %p\n", task,
- T_TASK(cmd)->t_task_cdb[0], dev);
-#endif
- if (dev) {
- spin_lock_irqsave(&SE_HBA(dev)->hba_queue_lock, flags);
- atomic_inc(&dev->depth_left);
- atomic_inc(&SE_HBA(dev)->left_queue_depth);
- spin_unlock_irqrestore(&SE_HBA(dev)->hba_queue_lock, flags);
- }
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- atomic_set(&task->task_active, 0);
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ cmd->transport_state &= ~CMD_T_BUSY;
- /*
- * See if any sense data exists, if so set the TASK_SENSE flag.
- * Also check for any other post completion work that needs to be
- * done by the plugins.
- */
if (dev && dev->transport->transport_complete) {
- if (dev->transport->transport_complete(task) != 0) {
- cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE;
- task->task_sense = 1;
+ dev->transport->transport_complete(cmd,
+ cmd->t_data_sg,
+ transport_get_sense_buffer(cmd));
+ if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE)
success = 1;
- }
}
/*
- * See if we are waiting for outstanding struct se_task
- * to complete for an exception condition
+ * See if we are waiting to complete for an exception condition.
*/
- if (atomic_read(&task->task_stop)) {
- /*
- * Decrement T_TASK(cmd)->t_se_count if this task had
- * previously thrown its timeout exception handler.
- */
- if (atomic_read(&task->task_timeout)) {
- atomic_dec(&T_TASK(cmd)->t_se_count);
- atomic_set(&task->task_timeout, 0);
- }
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-
- complete(&task->task_stop_comp);
- return;
- }
- /*
- * If the task's timeout handler has fired, use the t_task_cdbs_timeout
- * left counter to determine when the struct se_cmd is ready to be queued to
- * the processing thread.
- */
- if (atomic_read(&task->task_timeout)) {
- if (!(atomic_dec_and_test(
- &T_TASK(cmd)->t_task_cdbs_timeout_left))) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock,
- flags);
- return;
- }
- t_state = TRANSPORT_COMPLETE_TIMEOUT;
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-
- transport_add_cmd_to_queue(cmd, t_state);
+ if (cmd->transport_state & CMD_T_REQUEST_STOP) {
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ complete(&cmd->task_stop_comp);
return;
}
- atomic_dec(&T_TASK(cmd)->t_task_cdbs_timeout_left);
/*
- * Decrement the outstanding t_task_cdbs_left count. The last
- * struct se_task from struct se_cmd will complete itself into the
- * device queue depending upon int success.
+ * Check for case where an explicit ABORT_TASK has been received
+ * and transport_wait_for_tasks() will be waiting for completion..
*/
- if (!(atomic_dec_and_test(&T_TASK(cmd)->t_task_cdbs_left))) {
- if (!success)
- T_TASK(cmd)->t_tasks_failed = 1;
-
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
+ if (cmd->transport_state & CMD_T_ABORTED &&
+ cmd->transport_state & CMD_T_STOP) {
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ complete_all(&cmd->t_transport_stop_comp);
return;
- }
-
- if (!success || T_TASK(cmd)->t_tasks_failed) {
- t_state = TRANSPORT_COMPLETE_FAILURE;
- if (!task->task_error_status) {
- task->task_error_status =
- PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
- cmd->transport_error_status =
- PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
- }
+ } else if (!success) {
+ INIT_WORK(&cmd->work, target_complete_failure_work);
} else {
- atomic_set(&T_TASK(cmd)->t_transport_complete, 1);
- t_state = TRANSPORT_COMPLETE_OK;
- }
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-
- transport_add_cmd_to_queue(cmd, t_state);
-}
-EXPORT_SYMBOL(transport_complete_task);
-
-/*
- * Called by transport_add_tasks_from_cmd() once a struct se_cmd's
- * struct se_task list are ready to be added to the active execution list
- * struct se_device
-
- * Called with se_dev_t->execute_task_lock called.
- */
-static inline int transport_add_task_check_sam_attr(
- struct se_task *task,
- struct se_task *task_prev,
- struct se_device *dev)
-{
- /*
- * No SAM Task attribute emulation enabled, add to tail of
- * execution queue
- */
- if (dev->dev_task_attr_type != SAM_TASK_ATTR_EMULATED) {
- list_add_tail(&task->t_execute_list, &dev->execute_task_list);
- return 0;
+ INIT_WORK(&cmd->work, target_complete_ok_work);
}
- /*
- * HEAD_OF_QUEUE attribute for received CDB, which means
- * the first task that is associated with a struct se_cmd goes to
- * head of the struct se_device->execute_task_list, and task_prev
- * after that for each subsequent task
- */
- if (task->task_se_cmd->sam_task_attr == TASK_ATTR_HOQ) {
- list_add(&task->t_execute_list,
- (task_prev != NULL) ?
- &task_prev->t_execute_list :
- &dev->execute_task_list);
-
- DEBUG_STA("Set HEAD_OF_QUEUE for task CDB: 0x%02x"
- " in execution queue\n",
- T_TASK(task->task_se_cmd)->t_task_cdb[0]);
- return 1;
- }
- /*
- * For ORDERED, SIMPLE or UNTAGGED attribute tasks once they have been
- * transitioned from Dermant -> Active state, and are added to the end
- * of the struct se_device->execute_task_list
- */
- list_add_tail(&task->t_execute_list, &dev->execute_task_list);
- return 0;
-}
-/* __transport_add_task_to_execute_queue():
- *
- * Called with se_dev_t->execute_task_lock called.
- */
-static void __transport_add_task_to_execute_queue(
- struct se_task *task,
- struct se_task *task_prev,
- struct se_device *dev)
-{
- int head_of_queue;
-
- head_of_queue = transport_add_task_check_sam_attr(task, task_prev, dev);
- atomic_inc(&dev->execute_tasks);
+ cmd->t_state = TRANSPORT_COMPLETE;
+ cmd->transport_state |= (CMD_T_COMPLETE | CMD_T_ACTIVE);
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- if (atomic_read(&task->task_state_active))
- return;
- /*
- * Determine if this task needs to go to HEAD_OF_QUEUE for the
- * state list as well. Running with SAM Task Attribute emulation
- * will always return head_of_queue == 0 here
- */
- if (head_of_queue)
- list_add(&task->t_state_list, (task_prev) ?
- &task_prev->t_state_list :
- &dev->state_task_list);
- else
- list_add_tail(&task->t_state_list, &dev->state_task_list);
-
- atomic_set(&task->task_state_active, 1);
-
- DEBUG_TSTATE("Added ITT: 0x%08x task[%p] to dev: %p\n",
- CMD_TFO(task->task_se_cmd)->get_task_tag(task->task_se_cmd),
- task, dev);
+ queue_work(target_completion_wq, &cmd->work);
}
+EXPORT_SYMBOL(target_complete_cmd);
-static void transport_add_tasks_to_state_queue(struct se_cmd *cmd)
+void target_complete_cmd_with_length(struct se_cmd *cmd, u8 scsi_status, int length)
{
- struct se_device *dev;
- struct se_task *task;
- unsigned long flags;
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- list_for_each_entry(task, &T_TASK(cmd)->t_task_list, t_list) {
- dev = task->se_dev;
-
- if (atomic_read(&task->task_state_active))
- continue;
-
- spin_lock(&dev->execute_task_lock);
- list_add_tail(&task->t_state_list, &dev->state_task_list);
- atomic_set(&task->task_state_active, 1);
-
- DEBUG_TSTATE("Added ITT: 0x%08x task[%p] to dev: %p\n",
- CMD_TFO(task->task_se_cmd)->get_task_tag(
- task->task_se_cmd), task, dev);
+ if (scsi_status == SAM_STAT_GOOD && length < cmd->data_length) {
+ if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
+ cmd->residual_count += cmd->data_length - length;
+ } else {
+ cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;
+ cmd->residual_count = cmd->data_length - length;
+ }
- spin_unlock(&dev->execute_task_lock);
+ cmd->data_length = length;
}
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
+
+ target_complete_cmd(cmd, scsi_status);
}
+EXPORT_SYMBOL(target_complete_cmd_with_length);
-static void transport_add_tasks_from_cmd(struct se_cmd *cmd)
+static void target_add_to_state_list(struct se_cmd *cmd)
{
- struct se_device *dev = SE_DEV(cmd);
- struct se_task *task, *task_prev = NULL;
+ struct se_device *dev = cmd->se_dev;
unsigned long flags;
spin_lock_irqsave(&dev->execute_task_lock, flags);
- list_for_each_entry(task, &T_TASK(cmd)->t_task_list, t_list) {
- if (atomic_read(&task->task_execute_queue))
- continue;
- /*
- * __transport_add_task_to_execute_queue() handles the
- * SAM Task Attribute emulation if enabled
- */
- __transport_add_task_to_execute_queue(task, task_prev, dev);
- atomic_set(&task->task_execute_queue, 1);
- task_prev = task;
+ if (!cmd->state_active) {
+ list_add_tail(&cmd->state_list, &dev->state_list);
+ cmd->state_active = true;
}
spin_unlock_irqrestore(&dev->execute_task_lock, flags);
-
- return;
}
-/* transport_get_task_from_execute_queue():
- *
- * Called with dev->execute_task_lock held.
+/*
+ * Handle QUEUE_FULL / -EAGAIN and -ENOMEM status
*/
-static struct se_task *
-transport_get_task_from_execute_queue(struct se_device *dev)
+static void transport_write_pending_qf(struct se_cmd *cmd);
+static void transport_complete_qf(struct se_cmd *cmd);
+
+void target_qf_do_work(struct work_struct *work)
{
- struct se_task *task;
+ struct se_device *dev = container_of(work, struct se_device,
+ qf_work_queue);
+ LIST_HEAD(qf_cmd_list);
+ struct se_cmd *cmd, *cmd_tmp;
- if (list_empty(&dev->execute_task_list))
- return NULL;
+ spin_lock_irq(&dev->qf_cmd_lock);
+ list_splice_init(&dev->qf_cmd_list, &qf_cmd_list);
+ spin_unlock_irq(&dev->qf_cmd_lock);
- list_for_each_entry(task, &dev->execute_task_list, t_execute_list)
- break;
+ list_for_each_entry_safe(cmd, cmd_tmp, &qf_cmd_list, se_qf_node) {
+ list_del(&cmd->se_qf_node);
+ atomic_dec(&dev->dev_qf_count);
+ smp_mb__after_atomic();
- list_del(&task->t_execute_list);
- atomic_dec(&dev->execute_tasks);
+ pr_debug("Processing %s cmd: %p QUEUE_FULL in work queue"
+ " context: %s\n", cmd->se_tfo->get_fabric_name(), cmd,
+ (cmd->t_state == TRANSPORT_COMPLETE_QF_OK) ? "COMPLETE_OK" :
+ (cmd->t_state == TRANSPORT_COMPLETE_QF_WP) ? "WRITE_PENDING"
+ : "UNKNOWN");
- return task;
-}
-
-/* transport_remove_task_from_execute_queue():
- *
- *
- */
-static void transport_remove_task_from_execute_queue(
- struct se_task *task,
- struct se_device *dev)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->execute_task_lock, flags);
- list_del(&task->t_execute_list);
- atomic_dec(&dev->execute_tasks);
- spin_unlock_irqrestore(&dev->execute_task_lock, flags);
+ if (cmd->t_state == TRANSPORT_COMPLETE_QF_WP)
+ transport_write_pending_qf(cmd);
+ else if (cmd->t_state == TRANSPORT_COMPLETE_QF_OK)
+ transport_complete_qf(cmd);
+ }
}
unsigned char *transport_dump_cmd_direction(struct se_cmd *cmd)
@@ -1243,72 +792,18 @@ void transport_dump_dev_state(
int *bl)
{
*bl += sprintf(b + *bl, "Status: ");
- switch (dev->dev_status) {
- case TRANSPORT_DEVICE_ACTIVATED:
+ if (dev->export_count)
*bl += sprintf(b + *bl, "ACTIVATED");
- break;
- case TRANSPORT_DEVICE_DEACTIVATED:
+ else
*bl += sprintf(b + *bl, "DEACTIVATED");
- break;
- case TRANSPORT_DEVICE_SHUTDOWN:
- *bl += sprintf(b + *bl, "SHUTDOWN");
- break;
- case TRANSPORT_DEVICE_OFFLINE_ACTIVATED:
- case TRANSPORT_DEVICE_OFFLINE_DEACTIVATED:
- *bl += sprintf(b + *bl, "OFFLINE");
- break;
- default:
- *bl += sprintf(b + *bl, "UNKNOWN=%d", dev->dev_status);
- break;
- }
- *bl += sprintf(b + *bl, " Execute/Left/Max Queue Depth: %d/%d/%d",
- atomic_read(&dev->execute_tasks), atomic_read(&dev->depth_left),
- dev->queue_depth);
- *bl += sprintf(b + *bl, " SectorSize: %u MaxSectors: %u\n",
- DEV_ATTRIB(dev)->block_size, DEV_ATTRIB(dev)->max_sectors);
+ *bl += sprintf(b + *bl, " Max Queue Depth: %d", dev->queue_depth);
+ *bl += sprintf(b + *bl, " SectorSize: %u HwMaxSectors: %u\n",
+ dev->dev_attrib.block_size,
+ dev->dev_attrib.hw_max_sectors);
*bl += sprintf(b + *bl, " ");
}
-/* transport_release_all_cmds():
- *
- *
- */
-static void transport_release_all_cmds(struct se_device *dev)
-{
- struct se_cmd *cmd = NULL;
- struct se_queue_req *qr = NULL, *qr_p = NULL;
- int bug_out = 0, t_state;
- unsigned long flags;
-
- spin_lock_irqsave(&dev->dev_queue_obj->cmd_queue_lock, flags);
- list_for_each_entry_safe(qr, qr_p, &dev->dev_queue_obj->qobj_list,
- qr_list) {
-
- cmd = (struct se_cmd *)qr->cmd;
- t_state = qr->state;
- list_del(&qr->qr_list);
- kfree(qr);
- spin_unlock_irqrestore(&dev->dev_queue_obj->cmd_queue_lock,
- flags);
-
- printk(KERN_ERR "Releasing ITT: 0x%08x, i_state: %u,"
- " t_state: %u directly\n",
- CMD_TFO(cmd)->get_task_tag(cmd),
- CMD_TFO(cmd)->get_cmd_state(cmd), t_state);
-
- transport_release_fe_cmd(cmd);
- bug_out = 1;
-
- spin_lock_irqsave(&dev->dev_queue_obj->cmd_queue_lock, flags);
- }
- spin_unlock_irqrestore(&dev->dev_queue_obj->cmd_queue_lock, flags);
-#if 0
- if (bug_out)
- BUG();
-#endif
-}
-
void transport_dump_vpd_proto_id(
struct t10_vpd *vpd,
unsigned char *p_buf,
@@ -1359,7 +854,7 @@ void transport_dump_vpd_proto_id(
if (p_buf)
strncpy(p_buf, buf, p_buf_len);
else
- printk(KERN_INFO "%s", buf);
+ pr_debug("%s", buf);
}
void
@@ -1384,7 +879,8 @@ int transport_dump_vpd_assoc(
int p_buf_len)
{
unsigned char buf[VPD_TMP_BUF_SIZE];
- int ret = 0, len;
+ int ret = 0;
+ int len;
memset(buf, 0, VPD_TMP_BUF_SIZE);
len = sprintf(buf, "T10 VPD Identifier Association: ");
@@ -1401,14 +897,14 @@ int transport_dump_vpd_assoc(
break;
default:
sprintf(buf+len, "Unknown 0x%02x\n", vpd->association);
- ret = -1;
+ ret = -EINVAL;
break;
}
if (p_buf)
strncpy(p_buf, buf, p_buf_len);
else
- printk("%s", buf);
+ pr_debug("%s", buf);
return ret;
}
@@ -1431,7 +927,8 @@ int transport_dump_vpd_ident_type(
int p_buf_len)
{
unsigned char buf[VPD_TMP_BUF_SIZE];
- int ret = 0, len;
+ int ret = 0;
+ int len;
memset(buf, 0, VPD_TMP_BUF_SIZE);
len = sprintf(buf, "T10 VPD Identifier Type: ");
@@ -1458,14 +955,17 @@ int transport_dump_vpd_ident_type(
default:
sprintf(buf+len, "Unsupported: 0x%02x\n",
vpd->device_identifier_type);
- ret = -1;
+ ret = -EINVAL;
break;
}
- if (p_buf)
+ if (p_buf) {
+ if (p_buf_len < strlen(buf)+1)
+ return -EINVAL;
strncpy(p_buf, buf, p_buf_len);
- else
- printk("%s", buf);
+ } else {
+ pr_debug("%s", buf);
+ }
return ret;
}
@@ -1494,28 +994,31 @@ int transport_dump_vpd_ident(
switch (vpd->device_identifier_code_set) {
case 0x01: /* Binary */
- sprintf(buf, "T10 VPD Binary Device Identifier: %s\n",
+ snprintf(buf, sizeof(buf),
+ "T10 VPD Binary Device Identifier: %s\n",
&vpd->device_identifier[0]);
break;
case 0x02: /* ASCII */
- sprintf(buf, "T10 VPD ASCII Device Identifier: %s\n",
+ snprintf(buf, sizeof(buf),
+ "T10 VPD ASCII Device Identifier: %s\n",
&vpd->device_identifier[0]);
break;
case 0x03: /* UTF-8 */
- sprintf(buf, "T10 VPD UTF-8 Device Identifier: %s\n",
+ snprintf(buf, sizeof(buf),
+ "T10 VPD UTF-8 Device Identifier: %s\n",
&vpd->device_identifier[0]);
break;
default:
sprintf(buf, "T10 VPD Device Identifier encoding unsupported:"
" 0x%02x", vpd->device_identifier_code_set);
- ret = -1;
+ ret = -EINVAL;
break;
}
if (p_buf)
strncpy(p_buf, buf, p_buf_len);
else
- printk("%s", buf);
+ pr_debug("%s", buf);
return ret;
}
@@ -1524,7 +1027,7 @@ int
transport_set_vpd_ident(struct t10_vpd *vpd, unsigned char *page_83)
{
static const char hex_str[] = "0123456789abcdef";
- int j = 0, i = 4; /* offset to start of the identifer */
+ int j = 0, i = 4; /* offset to start of the identifier */
/*
* The VPD Code Set (encoding)
@@ -1557,273 +1060,54 @@ transport_set_vpd_ident(struct t10_vpd *vpd, unsigned char *page_83)
}
EXPORT_SYMBOL(transport_set_vpd_ident);
-static void core_setup_task_attr_emulation(struct se_device *dev)
+sense_reason_t
+target_cmd_size_check(struct se_cmd *cmd, unsigned int size)
{
- /*
- * If this device is from Target_Core_Mod/pSCSI, disable the
- * SAM Task Attribute emulation.
- *
- * This is currently not available in upsream Linux/SCSI Target
- * mode code, and is assumed to be disabled while using TCM/pSCSI.
- */
- if (TRANSPORT(dev)->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) {
- dev->dev_task_attr_type = SAM_TASK_ATTR_PASSTHROUGH;
- return;
- }
-
- dev->dev_task_attr_type = SAM_TASK_ATTR_EMULATED;
- DEBUG_STA("%s: Using SAM_TASK_ATTR_EMULATED for SPC: 0x%02x"
- " device\n", TRANSPORT(dev)->name,
- TRANSPORT(dev)->get_device_rev(dev));
-}
-
-static void scsi_dump_inquiry(struct se_device *dev)
-{
- struct t10_wwn *wwn = DEV_T10_WWN(dev);
- int i, device_type;
- /*
- * Print Linux/SCSI style INQUIRY formatting to the kernel ring buffer
- */
- printk(" Vendor: ");
- for (i = 0; i < 8; i++)
- if (wwn->vendor[i] >= 0x20)
- printk("%c", wwn->vendor[i]);
- else
- printk(" ");
-
- printk(" Model: ");
- for (i = 0; i < 16; i++)
- if (wwn->model[i] >= 0x20)
- printk("%c", wwn->model[i]);
- else
- printk(" ");
-
- printk(" Revision: ");
- for (i = 0; i < 4; i++)
- if (wwn->revision[i] >= 0x20)
- printk("%c", wwn->revision[i]);
- else
- printk(" ");
-
- printk("\n");
-
- device_type = TRANSPORT(dev)->get_device_type(dev);
- printk(" Type: %s ", scsi_device_type(device_type));
- printk(" ANSI SCSI revision: %02x\n",
- TRANSPORT(dev)->get_device_rev(dev));
-}
-
-struct se_device *transport_add_device_to_core_hba(
- struct se_hba *hba,
- struct se_subsystem_api *transport,
- struct se_subsystem_dev *se_dev,
- u32 device_flags,
- void *transport_dev,
- struct se_dev_limits *dev_limits,
- const char *inquiry_prod,
- const char *inquiry_rev)
-{
- int ret = 0, force_pt;
- struct se_device *dev;
-
- dev = kzalloc(sizeof(struct se_device), GFP_KERNEL);
- if (!(dev)) {
- printk(KERN_ERR "Unable to allocate memory for se_dev_t\n");
- return NULL;
- }
- dev->dev_queue_obj = kzalloc(sizeof(struct se_queue_obj), GFP_KERNEL);
- if (!(dev->dev_queue_obj)) {
- printk(KERN_ERR "Unable to allocate memory for"
- " dev->dev_queue_obj\n");
- kfree(dev);
- return NULL;
- }
- transport_init_queue_obj(dev->dev_queue_obj);
-
- dev->dev_status_queue_obj = kzalloc(sizeof(struct se_queue_obj),
- GFP_KERNEL);
- if (!(dev->dev_status_queue_obj)) {
- printk(KERN_ERR "Unable to allocate memory for"
- " dev->dev_status_queue_obj\n");
- kfree(dev->dev_queue_obj);
- kfree(dev);
- return NULL;
- }
- transport_init_queue_obj(dev->dev_status_queue_obj);
-
- dev->dev_flags = device_flags;
- dev->dev_status |= TRANSPORT_DEVICE_DEACTIVATED;
- dev->dev_ptr = (void *) transport_dev;
- dev->se_hba = hba;
- dev->se_sub_dev = se_dev;
- dev->transport = transport;
- atomic_set(&dev->active_cmds, 0);
- INIT_LIST_HEAD(&dev->dev_list);
- INIT_LIST_HEAD(&dev->dev_sep_list);
- INIT_LIST_HEAD(&dev->dev_tmr_list);
- INIT_LIST_HEAD(&dev->execute_task_list);
- INIT_LIST_HEAD(&dev->delayed_cmd_list);
- INIT_LIST_HEAD(&dev->ordered_cmd_list);
- INIT_LIST_HEAD(&dev->state_task_list);
- spin_lock_init(&dev->execute_task_lock);
- spin_lock_init(&dev->delayed_cmd_lock);
- spin_lock_init(&dev->ordered_cmd_lock);
- spin_lock_init(&dev->state_task_lock);
- spin_lock_init(&dev->dev_alua_lock);
- spin_lock_init(&dev->dev_reservation_lock);
- spin_lock_init(&dev->dev_status_lock);
- spin_lock_init(&dev->dev_status_thr_lock);
- spin_lock_init(&dev->se_port_lock);
- spin_lock_init(&dev->se_tmr_lock);
-
- dev->queue_depth = dev_limits->queue_depth;
- atomic_set(&dev->depth_left, dev->queue_depth);
- atomic_set(&dev->dev_ordered_id, 0);
-
- se_dev_set_default_attribs(dev, dev_limits);
-
- dev->dev_index = scsi_get_new_index(SCSI_DEVICE_INDEX);
- dev->creation_time = get_jiffies_64();
- spin_lock_init(&dev->stats_lock);
-
- spin_lock(&hba->device_lock);
- list_add_tail(&dev->dev_list, &hba->hba_dev_list);
- hba->dev_count++;
- spin_unlock(&hba->device_lock);
- /*
- * Setup the SAM Task Attribute emulation for struct se_device
- */
- core_setup_task_attr_emulation(dev);
- /*
- * Force PR and ALUA passthrough emulation with internal object use.
- */
- force_pt = (hba->hba_flags & HBA_FLAGS_INTERNAL_USE);
- /*
- * Setup the Reservations infrastructure for struct se_device
- */
- core_setup_reservations(dev, force_pt);
- /*
- * Setup the Asymmetric Logical Unit Assignment for struct se_device
- */
- if (core_setup_alua(dev, force_pt) < 0)
- goto out;
+ struct se_device *dev = cmd->se_dev;
- /*
- * Startup the struct se_device processing thread
- */
- dev->process_thread = kthread_run(transport_processing_thread, dev,
- "LIO_%s", TRANSPORT(dev)->name);
- if (IS_ERR(dev->process_thread)) {
- printk(KERN_ERR "Unable to create kthread: LIO_%s\n",
- TRANSPORT(dev)->name);
- goto out;
- }
+ if (cmd->unknown_data_length) {
+ cmd->data_length = size;
+ } else if (size != cmd->data_length) {
+ pr_warn("TARGET_CORE[%s]: Expected Transfer Length:"
+ " %u does not match SCSI CDB Length: %u for SAM Opcode:"
+ " 0x%02x\n", cmd->se_tfo->get_fabric_name(),
+ cmd->data_length, size, cmd->t_task_cdb[0]);
- /*
- * Preload the initial INQUIRY const values if we are doing
- * anything virtual (IBLOCK, FILEIO, RAMDISK), but not for TCM/pSCSI
- * passthrough because this is being provided by the backend LLD.
- * This is required so that transport_get_inquiry() copies these
- * originals once back into DEV_T10_WWN(dev) for the virtual device
- * setup.
- */
- if (TRANSPORT(dev)->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV) {
- if (!(inquiry_prod) || !(inquiry_prod)) {
- printk(KERN_ERR "All non TCM/pSCSI plugins require"
- " INQUIRY consts\n");
- goto out;
+ if (cmd->data_direction == DMA_TO_DEVICE) {
+ pr_err("Rejecting underflow/overflow"
+ " WRITE data\n");
+ return TCM_INVALID_CDB_FIELD;
+ }
+ /*
+ * Reject READ_* or WRITE_* with overflow/underflow for
+ * type SCF_SCSI_DATA_CDB.
+ */
+ if (dev->dev_attrib.block_size != 512) {
+ pr_err("Failing OVERFLOW/UNDERFLOW for LBA op"
+ " CDB on non 512-byte sector setup subsystem"
+ " plugin: %s\n", dev->transport->name);
+ /* Returns CHECK_CONDITION + INVALID_CDB_FIELD */
+ return TCM_INVALID_CDB_FIELD;
+ }
+ /*
+ * For the overflow case keep the existing fabric provided
+ * ->data_length. Otherwise for the underflow case, reset
+ * ->data_length to the smaller SCSI expected data transfer
+ * length.
+ */
+ if (size > cmd->data_length) {
+ cmd->se_cmd_flags |= SCF_OVERFLOW_BIT;
+ cmd->residual_count = (size - cmd->data_length);
+ } else {
+ cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;
+ cmd->residual_count = (cmd->data_length - size);
+ cmd->data_length = size;
}
-
- strncpy(&DEV_T10_WWN(dev)->vendor[0], "LIO-ORG", 8);
- strncpy(&DEV_T10_WWN(dev)->model[0], inquiry_prod, 16);
- strncpy(&DEV_T10_WWN(dev)->revision[0], inquiry_rev, 4);
- }
- scsi_dump_inquiry(dev);
-
-out:
- if (!ret)
- return dev;
- kthread_stop(dev->process_thread);
-
- spin_lock(&hba->device_lock);
- list_del(&dev->dev_list);
- hba->dev_count--;
- spin_unlock(&hba->device_lock);
-
- se_release_vpd_for_dev(dev);
-
- kfree(dev->dev_status_queue_obj);
- kfree(dev->dev_queue_obj);
- kfree(dev);
-
- return NULL;
-}
-EXPORT_SYMBOL(transport_add_device_to_core_hba);
-
-/* transport_generic_prepare_cdb():
- *
- * Since the Initiator sees iSCSI devices as LUNs, the SCSI CDB will
- * contain the iSCSI LUN in bits 7-5 of byte 1 as per SAM-2.
- * The point of this is since we are mapping iSCSI LUNs to
- * SCSI Target IDs having a non-zero LUN in the CDB will throw the
- * devices and HBAs for a loop.
- */
-static inline void transport_generic_prepare_cdb(
- unsigned char *cdb)
-{
- switch (cdb[0]) {
- case READ_10: /* SBC - RDProtect */
- case READ_12: /* SBC - RDProtect */
- case READ_16: /* SBC - RDProtect */
- case SEND_DIAGNOSTIC: /* SPC - SELF-TEST Code */
- case VERIFY: /* SBC - VRProtect */
- case VERIFY_16: /* SBC - VRProtect */
- case WRITE_VERIFY: /* SBC - VRProtect */
- case WRITE_VERIFY_12: /* SBC - VRProtect */
- break;
- default:
- cdb[1] &= 0x1f; /* clear logical unit number */
- break;
- }
-}
-
-static struct se_task *
-transport_generic_get_task(struct se_cmd *cmd,
- enum dma_data_direction data_direction)
-{
- struct se_task *task;
- struct se_device *dev = SE_DEV(cmd);
- unsigned long flags;
-
- task = dev->transport->alloc_task(cmd);
- if (!task) {
- printk(KERN_ERR "Unable to allocate struct se_task\n");
- return NULL;
}
- INIT_LIST_HEAD(&task->t_list);
- INIT_LIST_HEAD(&task->t_execute_list);
- INIT_LIST_HEAD(&task->t_state_list);
- init_completion(&task->task_stop_comp);
- task->task_no = T_TASK(cmd)->t_tasks_no++;
- task->task_se_cmd = cmd;
- task->se_dev = dev;
- task->task_data_direction = data_direction;
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- list_add_tail(&task->t_list, &T_TASK(cmd)->t_task_list);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-
- return task;
-}
-
-static int transport_generic_cmd_sequencer(struct se_cmd *, unsigned char *);
+ return 0;
-void transport_device_setup_cmd(struct se_cmd *cmd)
-{
- cmd->se_dev = SE_LUN(cmd)->lun_se_dev;
}
-EXPORT_SYMBOL(transport_device_setup_cmd);
/*
* Used by fabric modules containing a local struct se_cmd within their
@@ -1838,20 +1122,16 @@ void transport_init_se_cmd(
int task_attr,
unsigned char *sense_buffer)
{
- INIT_LIST_HEAD(&cmd->se_lun_list);
- INIT_LIST_HEAD(&cmd->se_delayed_list);
- INIT_LIST_HEAD(&cmd->se_ordered_list);
- /*
- * Setup t_task pointer to t_task_backstore
- */
- cmd->t_task = &cmd->t_task_backstore;
-
- INIT_LIST_HEAD(&T_TASK(cmd)->t_task_list);
- init_completion(&T_TASK(cmd)->transport_lun_fe_stop_comp);
- init_completion(&T_TASK(cmd)->transport_lun_stop_comp);
- init_completion(&T_TASK(cmd)->t_transport_stop_comp);
- spin_lock_init(&T_TASK(cmd)->t_state_lock);
- atomic_set(&T_TASK(cmd)->transport_dev_active, 1);
+ INIT_LIST_HEAD(&cmd->se_delayed_node);
+ INIT_LIST_HEAD(&cmd->se_qf_node);
+ INIT_LIST_HEAD(&cmd->se_cmd_list);
+ INIT_LIST_HEAD(&cmd->state_list);
+ init_completion(&cmd->t_transport_stop_comp);
+ init_completion(&cmd->cmd_wait_comp);
+ init_completion(&cmd->task_stop_comp);
+ spin_lock_init(&cmd->t_state_lock);
+ kref_init(&cmd->cmd_kref);
+ cmd->transport_state = CMD_T_DEV_ACTIVE;
cmd->se_tfo = tfo;
cmd->se_sess = se_sess;
@@ -1859,2067 +1139,919 @@ void transport_init_se_cmd(
cmd->data_direction = data_direction;
cmd->sam_task_attr = task_attr;
cmd->sense_buffer = sense_buffer;
+
+ cmd->state_active = false;
}
EXPORT_SYMBOL(transport_init_se_cmd);
-static int transport_check_alloc_task_attr(struct se_cmd *cmd)
+static sense_reason_t
+transport_check_alloc_task_attr(struct se_cmd *cmd)
{
+ struct se_device *dev = cmd->se_dev;
+
/*
* Check if SAM Task Attribute emulation is enabled for this
* struct se_device storage object
*/
- if (SE_DEV(cmd)->dev_task_attr_type != SAM_TASK_ATTR_EMULATED)
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
return 0;
- if (cmd->sam_task_attr == TASK_ATTR_ACA) {
- DEBUG_STA("SAM Task Attribute ACA"
+ if (cmd->sam_task_attr == MSG_ACA_TAG) {
+ pr_debug("SAM Task Attribute ACA"
" emulation is not supported\n");
- return -1;
+ return TCM_INVALID_CDB_FIELD;
}
/*
* Used to determine when ORDERED commands should go from
* Dormant to Active status.
*/
- cmd->se_ordered_id = atomic_inc_return(&SE_DEV(cmd)->dev_ordered_id);
- smp_mb__after_atomic_inc();
- DEBUG_STA("Allocated se_ordered_id: %u for Task Attr: 0x%02x on %s\n",
+ cmd->se_ordered_id = atomic_inc_return(&dev->dev_ordered_id);
+ smp_mb__after_atomic();
+ pr_debug("Allocated se_ordered_id: %u for Task Attr: 0x%02x on %s\n",
cmd->se_ordered_id, cmd->sam_task_attr,
- TRANSPORT(cmd->se_dev)->name);
+ dev->transport->name);
return 0;
}
-void transport_free_se_cmd(
- struct se_cmd *se_cmd)
-{
- if (se_cmd->se_tmr_req)
- core_tmr_release_req(se_cmd->se_tmr_req);
- /*
- * Check and free any extended CDB buffer that was allocated
- */
- if (T_TASK(se_cmd)->t_task_cdb != T_TASK(se_cmd)->__t_task_cdb)
- kfree(T_TASK(se_cmd)->t_task_cdb);
-}
-EXPORT_SYMBOL(transport_free_se_cmd);
-
-static void transport_generic_wait_for_tasks(struct se_cmd *, int, int);
-
-/* transport_generic_allocate_tasks():
- *
- * Called from fabric RX Thread.
- */
-int transport_generic_allocate_tasks(
- struct se_cmd *cmd,
- unsigned char *cdb)
+sense_reason_t
+target_setup_cmd_from_cdb(struct se_cmd *cmd, unsigned char *cdb)
{
- int ret;
-
- transport_generic_prepare_cdb(cdb);
-
- /*
- * This is needed for early exceptions.
- */
- cmd->transport_wait_for_tasks = &transport_generic_wait_for_tasks;
+ struct se_device *dev = cmd->se_dev;
+ sense_reason_t ret;
- transport_device_setup_cmd(cmd);
/*
* Ensure that the received CDB is less than the max (252 + 8) bytes
* for VARIABLE_LENGTH_CMD
*/
if (scsi_command_size(cdb) > SCSI_MAX_VARLEN_CDB_SIZE) {
- printk(KERN_ERR "Received SCSI CDB with command_size: %d that"
+ pr_err("Received SCSI CDB with command_size: %d that"
" exceeds SCSI_MAX_VARLEN_CDB_SIZE: %d\n",
scsi_command_size(cdb), SCSI_MAX_VARLEN_CDB_SIZE);
- return -1;
+ return TCM_INVALID_CDB_FIELD;
}
/*
* If the received CDB is larger than TCM_MAX_COMMAND_SIZE,
* allocate the additional extended CDB buffer now.. Otherwise
* setup the pointer from __t_task_cdb to t_task_cdb.
*/
- if (scsi_command_size(cdb) > sizeof(T_TASK(cmd)->__t_task_cdb)) {
- T_TASK(cmd)->t_task_cdb = kzalloc(scsi_command_size(cdb),
+ if (scsi_command_size(cdb) > sizeof(cmd->__t_task_cdb)) {
+ cmd->t_task_cdb = kzalloc(scsi_command_size(cdb),
GFP_KERNEL);
- if (!(T_TASK(cmd)->t_task_cdb)) {
- printk(KERN_ERR "Unable to allocate T_TASK(cmd)->t_task_cdb"
- " %u > sizeof(T_TASK(cmd)->__t_task_cdb): %lu ops\n",
+ if (!cmd->t_task_cdb) {
+ pr_err("Unable to allocate cmd->t_task_cdb"
+ " %u > sizeof(cmd->__t_task_cdb): %lu ops\n",
scsi_command_size(cdb),
- (unsigned long)sizeof(T_TASK(cmd)->__t_task_cdb));
- return -1;
+ (unsigned long)sizeof(cmd->__t_task_cdb));
+ return TCM_OUT_OF_RESOURCES;
}
} else
- T_TASK(cmd)->t_task_cdb = &T_TASK(cmd)->__t_task_cdb[0];
+ cmd->t_task_cdb = &cmd->__t_task_cdb[0];
/*
- * Copy the original CDB into T_TASK(cmd).
+ * Copy the original CDB into cmd->
*/
- memcpy(T_TASK(cmd)->t_task_cdb, cdb, scsi_command_size(cdb));
+ memcpy(cmd->t_task_cdb, cdb, scsi_command_size(cdb));
+
+ trace_target_sequencer_start(cmd);
+
/*
- * Setup the received CDB based on SCSI defined opcodes and
- * perform unit attention, persistent reservations and ALUA
- * checks for virtual device backends. The T_TASK(cmd)->t_task_cdb
- * pointer is expected to be setup before we reach this point.
+ * Check for an existing UNIT ATTENTION condition
*/
- ret = transport_generic_cmd_sequencer(cmd, cdb);
- if (ret < 0)
+ ret = target_scsi3_ua_check(cmd);
+ if (ret)
+ return ret;
+
+ ret = target_alua_state_check(cmd);
+ if (ret)
+ return ret;
+
+ ret = target_check_reservation(cmd);
+ if (ret) {
+ cmd->scsi_status = SAM_STAT_RESERVATION_CONFLICT;
return ret;
- /*
- * Check for SAM Task Attribute Emulation
- */
- if (transport_check_alloc_task_attr(cmd) < 0) {
- cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
- return -2;
}
+
+ ret = dev->transport->parse_cdb(cmd);
+ if (ret)
+ return ret;
+
+ ret = transport_check_alloc_task_attr(cmd);
+ if (ret)
+ return ret;
+
+ cmd->se_cmd_flags |= SCF_SUPPORTED_SAM_OPCODE;
+
spin_lock(&cmd->se_lun->lun_sep_lock);
if (cmd->se_lun->lun_sep)
cmd->se_lun->lun_sep->sep_stats.cmd_pdus++;
spin_unlock(&cmd->se_lun->lun_sep_lock);
return 0;
}
-EXPORT_SYMBOL(transport_generic_allocate_tasks);
+EXPORT_SYMBOL(target_setup_cmd_from_cdb);
/*
- * Used by fabric module frontends not defining a TFO->new_cmd_map()
- * to queue up a newly setup se_cmd w/ TRANSPORT_NEW_CMD statis
+ * Used by fabric module frontends to queue tasks directly.
+ * Many only be used from process context only
*/
-int transport_generic_handle_cdb(
+int transport_handle_cdb_direct(
struct se_cmd *cmd)
{
- if (!SE_LUN(cmd)) {
+ sense_reason_t ret;
+
+ if (!cmd->se_lun) {
+ dump_stack();
+ pr_err("cmd->se_lun is NULL\n");
+ return -EINVAL;
+ }
+ if (in_interrupt()) {
dump_stack();
- printk(KERN_ERR "SE_LUN(cmd) is NULL\n");
- return -1;
+ pr_err("transport_generic_handle_cdb cannot be called"
+ " from interrupt context\n");
+ return -EINVAL;
}
+ /*
+ * Set TRANSPORT_NEW_CMD state and CMD_T_ACTIVE to ensure that
+ * outstanding descriptors are handled correctly during shutdown via
+ * transport_wait_for_tasks()
+ *
+ * Also, we don't take cmd->t_state_lock here as we only expect
+ * this to be called for initial descriptor submission.
+ */
+ cmd->t_state = TRANSPORT_NEW_CMD;
+ cmd->transport_state |= CMD_T_ACTIVE;
- transport_add_cmd_to_queue(cmd, TRANSPORT_NEW_CMD);
+ /*
+ * transport_generic_new_cmd() is already handling QUEUE_FULL,
+ * so follow TRANSPORT_NEW_CMD processing thread context usage
+ * and call transport_generic_request_failure() if necessary..
+ */
+ ret = transport_generic_new_cmd(cmd);
+ if (ret)
+ transport_generic_request_failure(cmd, ret);
return 0;
}
-EXPORT_SYMBOL(transport_generic_handle_cdb);
+EXPORT_SYMBOL(transport_handle_cdb_direct);
-/*
- * Used by fabric module frontends defining a TFO->new_cmd_map() caller
- * to queue up a newly setup se_cmd w/ TRANSPORT_NEW_CMD_MAP in order to
- * complete setup in TCM process context w/ TFO->new_cmd_map().
- */
-int transport_generic_handle_cdb_map(
- struct se_cmd *cmd)
+sense_reason_t
+transport_generic_map_mem_to_cmd(struct se_cmd *cmd, struct scatterlist *sgl,
+ u32 sgl_count, struct scatterlist *sgl_bidi, u32 sgl_bidi_count)
{
- if (!SE_LUN(cmd)) {
- dump_stack();
- printk(KERN_ERR "SE_LUN(cmd) is NULL\n");
- return -1;
+ if (!sgl || !sgl_count)
+ return 0;
+
+ /*
+ * Reject SCSI data overflow with map_mem_to_cmd() as incoming
+ * scatterlists already have been set to follow what the fabric
+ * passes for the original expected data transfer length.
+ */
+ if (cmd->se_cmd_flags & SCF_OVERFLOW_BIT) {
+ pr_warn("Rejecting SCSI DATA overflow for fabric using"
+ " SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC\n");
+ return TCM_INVALID_CDB_FIELD;
}
- transport_add_cmd_to_queue(cmd, TRANSPORT_NEW_CMD_MAP);
+ cmd->t_data_sg = sgl;
+ cmd->t_data_nents = sgl_count;
+
+ if (sgl_bidi && sgl_bidi_count) {
+ cmd->t_bidi_data_sg = sgl_bidi;
+ cmd->t_bidi_data_nents = sgl_bidi_count;
+ }
+ cmd->se_cmd_flags |= SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
return 0;
}
-EXPORT_SYMBOL(transport_generic_handle_cdb_map);
-/* transport_generic_handle_data():
+/*
+ * target_submit_cmd_map_sgls - lookup unpacked lun and submit uninitialized
+ * se_cmd + use pre-allocated SGL memory.
+ *
+ * @se_cmd: command descriptor to submit
+ * @se_sess: associated se_sess for endpoint
+ * @cdb: pointer to SCSI CDB
+ * @sense: pointer to SCSI sense buffer
+ * @unpacked_lun: unpacked LUN to reference for struct se_lun
+ * @data_length: fabric expected data transfer length
+ * @task_addr: SAM task attribute
+ * @data_dir: DMA data direction
+ * @flags: flags for command submission from target_sc_flags_tables
+ * @sgl: struct scatterlist memory for unidirectional mapping
+ * @sgl_count: scatterlist count for unidirectional mapping
+ * @sgl_bidi: struct scatterlist memory for bidirectional READ mapping
+ * @sgl_bidi_count: scatterlist count for bidirectional READ mapping
+ * @sgl_prot: struct scatterlist memory protection information
+ * @sgl_prot_count: scatterlist count for protection information
*
+ * Returns non zero to signal active I/O shutdown failure. All other
+ * setup exceptions will be returned as a SCSI CHECK_CONDITION response,
+ * but still return zero here.
*
+ * This may only be called from process context, and also currently
+ * assumes internal allocation of fabric payload buffer by target-core.
*/
-int transport_generic_handle_data(
- struct se_cmd *cmd)
+int target_submit_cmd_map_sgls(struct se_cmd *se_cmd, struct se_session *se_sess,
+ unsigned char *cdb, unsigned char *sense, u32 unpacked_lun,
+ u32 data_length, int task_attr, int data_dir, int flags,
+ struct scatterlist *sgl, u32 sgl_count,
+ struct scatterlist *sgl_bidi, u32 sgl_bidi_count,
+ struct scatterlist *sgl_prot, u32 sgl_prot_count)
{
+ struct se_portal_group *se_tpg;
+ sense_reason_t rc;
+ int ret;
+
+ se_tpg = se_sess->se_tpg;
+ BUG_ON(!se_tpg);
+ BUG_ON(se_cmd->se_tfo || se_cmd->se_sess);
+ BUG_ON(in_interrupt());
/*
- * For the software fabric case, then we assume the nexus is being
- * failed/shutdown when signals are pending from the kthread context
- * caller, so we return a failure. For the HW target mode case running
- * in interrupt code, the signal_pending() check is skipped.
+ * Initialize se_cmd for target operation. From this point
+ * exceptions are handled by sending exception status via
+ * target_core_fabric_ops->queue_status() callback
*/
- if (!in_interrupt() && signal_pending(current))
- return -1;
+ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess,
+ data_length, data_dir, task_attr, sense);
+ if (flags & TARGET_SCF_UNKNOWN_SIZE)
+ se_cmd->unknown_data_length = 1;
/*
- * If the received CDB has aleady been ABORTED by the generic
- * target engine, we now call transport_check_aborted_status()
- * to queue any delated TASK_ABORTED status for the received CDB to the
- * fabric module as we are expecting no futher incoming DATA OUT
- * sequences at this point.
+ * Obtain struct se_cmd->cmd_kref reference and add new cmd to
+ * se_sess->sess_cmd_list. A second kref_get here is necessary
+ * for fabrics using TARGET_SCF_ACK_KREF that expect a second
+ * kref_put() to happen during fabric packet acknowledgement.
*/
- if (transport_check_aborted_status(cmd, 1) != 0)
- return 0;
-
- transport_add_cmd_to_queue(cmd, TRANSPORT_PROCESS_WRITE);
- return 0;
-}
-EXPORT_SYMBOL(transport_generic_handle_data);
-
-/* transport_generic_handle_tmr():
- *
- *
- */
-int transport_generic_handle_tmr(
- struct se_cmd *cmd)
-{
+ ret = target_get_sess_cmd(se_sess, se_cmd, (flags & TARGET_SCF_ACK_KREF));
+ if (ret)
+ return ret;
/*
- * This is needed for early exceptions.
+ * Signal bidirectional data payloads to target-core
*/
- cmd->transport_wait_for_tasks = &transport_generic_wait_for_tasks;
- transport_device_setup_cmd(cmd);
-
- transport_add_cmd_to_queue(cmd, TRANSPORT_PROCESS_TMR);
- return 0;
-}
-EXPORT_SYMBOL(transport_generic_handle_tmr);
-
-static int transport_stop_tasks_for_cmd(struct se_cmd *cmd)
-{
- struct se_task *task, *task_tmp;
- unsigned long flags;
- int ret = 0;
-
- DEBUG_TS("ITT[0x%08x] - Stopping tasks\n",
- CMD_TFO(cmd)->get_task_tag(cmd));
-
+ if (flags & TARGET_SCF_BIDI_OP)
+ se_cmd->se_cmd_flags |= SCF_BIDI;
/*
- * No tasks remain in the execution queue
+ * Locate se_lun pointer and attach it to struct se_cmd
*/
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- list_for_each_entry_safe(task, task_tmp,
- &T_TASK(cmd)->t_task_list, t_list) {
- DEBUG_TS("task_no[%d] - Processing task %p\n",
- task->task_no, task);
- /*
- * If the struct se_task has not been sent and is not active,
- * remove the struct se_task from the execution queue.
- */
- if (!atomic_read(&task->task_sent) &&
- !atomic_read(&task->task_active)) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock,
- flags);
- transport_remove_task_from_execute_queue(task,
- task->se_dev);
-
- DEBUG_TS("task_no[%d] - Removed from execute queue\n",
- task->task_no);
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- continue;
- }
-
- /*
- * If the struct se_task is active, sleep until it is returned
- * from the plugin.
- */
- if (atomic_read(&task->task_active)) {
- atomic_set(&task->task_stop, 1);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock,
- flags);
-
- DEBUG_TS("task_no[%d] - Waiting to complete\n",
- task->task_no);
- wait_for_completion(&task->task_stop_comp);
- DEBUG_TS("task_no[%d] - Stopped successfully\n",
- task->task_no);
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- atomic_dec(&T_TASK(cmd)->t_task_cdbs_left);
-
- atomic_set(&task->task_active, 0);
- atomic_set(&task->task_stop, 0);
- } else {
- DEBUG_TS("task_no[%d] - Did nothing\n", task->task_no);
- ret++;
- }
-
- __transport_stop_task_timer(task, &flags);
+ rc = transport_lookup_cmd_lun(se_cmd, unpacked_lun);
+ if (rc) {
+ transport_send_check_condition_and_sense(se_cmd, rc, 0);
+ target_put_sess_cmd(se_sess, se_cmd);
+ return 0;
}
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- return ret;
-}
-
-static void transport_failure_reset_queue_depth(struct se_device *dev)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&SE_HBA(dev)->hba_queue_lock, flags);;
- atomic_inc(&dev->depth_left);
- atomic_inc(&SE_HBA(dev)->left_queue_depth);
- spin_unlock_irqrestore(&SE_HBA(dev)->hba_queue_lock, flags);
-}
+ rc = target_setup_cmd_from_cdb(se_cmd, cdb);
+ if (rc != 0) {
+ transport_generic_request_failure(se_cmd, rc);
+ return 0;
+ }
-/*
- * Handle SAM-esque emulation for generic transport request failures.
- */
-static void transport_generic_request_failure(
- struct se_cmd *cmd,
- struct se_device *dev,
- int complete,
- int sc)
-{
- DEBUG_GRF("-----[ Storage Engine Exception for cmd: %p ITT: 0x%08x"
- " CDB: 0x%02x\n", cmd, CMD_TFO(cmd)->get_task_tag(cmd),
- T_TASK(cmd)->t_task_cdb[0]);
- DEBUG_GRF("-----[ i_state: %d t_state/def_t_state:"
- " %d/%d transport_error_status: %d\n",
- CMD_TFO(cmd)->get_cmd_state(cmd),
- cmd->t_state, cmd->deferred_t_state,
- cmd->transport_error_status);
- DEBUG_GRF("-----[ t_task_cdbs: %d t_task_cdbs_left: %d"
- " t_task_cdbs_sent: %d t_task_cdbs_ex_left: %d --"
- " t_transport_active: %d t_transport_stop: %d"
- " t_transport_sent: %d\n", T_TASK(cmd)->t_task_cdbs,
- atomic_read(&T_TASK(cmd)->t_task_cdbs_left),
- atomic_read(&T_TASK(cmd)->t_task_cdbs_sent),
- atomic_read(&T_TASK(cmd)->t_task_cdbs_ex_left),
- atomic_read(&T_TASK(cmd)->t_transport_active),
- atomic_read(&T_TASK(cmd)->t_transport_stop),
- atomic_read(&T_TASK(cmd)->t_transport_sent));
-
- transport_stop_all_task_timers(cmd);
-
- if (dev)
- transport_failure_reset_queue_depth(dev);
/*
- * For SAM Task Attribute emulation for failed struct se_cmd
+ * Save pointers for SGLs containing protection information,
+ * if present.
*/
- if (cmd->se_dev->dev_task_attr_type == SAM_TASK_ATTR_EMULATED)
- transport_complete_task_attr(cmd);
-
- if (complete) {
- transport_direct_request_timeout(cmd);
- cmd->transport_error_status = PYX_TRANSPORT_LU_COMM_FAILURE;
+ if (sgl_prot_count) {
+ se_cmd->t_prot_sg = sgl_prot;
+ se_cmd->t_prot_nents = sgl_prot_count;
}
- switch (cmd->transport_error_status) {
- case PYX_TRANSPORT_UNKNOWN_SAM_OPCODE:
- cmd->scsi_sense_reason = TCM_UNSUPPORTED_SCSI_OPCODE;
- break;
- case PYX_TRANSPORT_REQ_TOO_MANY_SECTORS:
- cmd->scsi_sense_reason = TCM_SECTOR_COUNT_TOO_MANY;
- break;
- case PYX_TRANSPORT_INVALID_CDB_FIELD:
- cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
- break;
- case PYX_TRANSPORT_INVALID_PARAMETER_LIST:
- cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
- break;
- case PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES:
- if (!sc)
- transport_new_cmd_failure(cmd);
- /*
- * Currently for PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES,
- * we force this session to fall back to session
- * recovery.
- */
- CMD_TFO(cmd)->fall_back_to_erl0(cmd->se_sess);
- CMD_TFO(cmd)->stop_session(cmd->se_sess, 0, 0);
-
- goto check_stop;
- case PYX_TRANSPORT_LU_COMM_FAILURE:
- case PYX_TRANSPORT_ILLEGAL_REQUEST:
- cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
- break;
- case PYX_TRANSPORT_UNKNOWN_MODE_PAGE:
- cmd->scsi_sense_reason = TCM_UNKNOWN_MODE_PAGE;
- break;
- case PYX_TRANSPORT_WRITE_PROTECTED:
- cmd->scsi_sense_reason = TCM_WRITE_PROTECTED;
- break;
- case PYX_TRANSPORT_RESERVATION_CONFLICT:
- /*
- * No SENSE Data payload for this case, set SCSI Status
- * and queue the response to $FABRIC_MOD.
- *
- * Uses linux/include/scsi/scsi.h SAM status codes defs
- */
- cmd->scsi_status = SAM_STAT_RESERVATION_CONFLICT;
- /*
- * For UA Interlock Code 11b, a RESERVATION CONFLICT will
- * establish a UNIT ATTENTION with PREVIOUS RESERVATION
- * CONFLICT STATUS.
- *
- * See spc4r17, section 7.4.6 Control Mode Page, Table 349
- */
- if (SE_SESS(cmd) &&
- DEV_ATTRIB(cmd->se_dev)->emulate_ua_intlck_ctrl == 2)
- core_scsi3_ua_allocate(SE_SESS(cmd)->se_node_acl,
- cmd->orig_fe_lun, 0x2C,
- ASCQ_2CH_PREVIOUS_RESERVATION_CONFLICT_STATUS);
+ /*
+ * When a non zero sgl_count has been passed perform SGL passthrough
+ * mapping for pre-allocated fabric memory instead of having target
+ * core perform an internal SGL allocation..
+ */
+ if (sgl_count != 0) {
+ BUG_ON(!sgl);
- CMD_TFO(cmd)->queue_status(cmd);
- goto check_stop;
- case PYX_TRANSPORT_USE_SENSE_REASON:
/*
- * struct se_cmd->scsi_sense_reason already set
+ * A work-around for tcm_loop as some userspace code via
+ * scsi-generic do not memset their associated read buffers,
+ * so go ahead and do that here for type non-data CDBs. Also
+ * note that this is currently guaranteed to be a single SGL
+ * for this case by target core in target_setup_cmd_from_cdb()
+ * -> transport_generic_cmd_sequencer().
*/
- break;
- default:
- printk(KERN_ERR "Unknown transport error for CDB 0x%02x: %d\n",
- T_TASK(cmd)->t_task_cdb[0],
- cmd->transport_error_status);
- cmd->scsi_sense_reason = TCM_UNSUPPORTED_SCSI_OPCODE;
- break;
- }
+ if (!(se_cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) &&
+ se_cmd->data_direction == DMA_FROM_DEVICE) {
+ unsigned char *buf = NULL;
- if (!sc)
- transport_new_cmd_failure(cmd);
- else
- transport_send_check_condition_and_sense(cmd,
- cmd->scsi_sense_reason, 0);
-check_stop:
- transport_lun_remove_cmd(cmd);
- if (!(transport_cmd_check_stop_to_fabric(cmd)))
- ;
-}
+ if (sgl)
+ buf = kmap(sg_page(sgl)) + sgl->offset;
-static void transport_direct_request_timeout(struct se_cmd *cmd)
-{
- unsigned long flags;
+ if (buf) {
+ memset(buf, 0, sgl->length);
+ kunmap(sg_page(sgl));
+ }
+ }
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- if (!(atomic_read(&T_TASK(cmd)->t_transport_timeout))) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- return;
- }
- if (atomic_read(&T_TASK(cmd)->t_task_cdbs_timeout_left)) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- return;
+ rc = transport_generic_map_mem_to_cmd(se_cmd, sgl, sgl_count,
+ sgl_bidi, sgl_bidi_count);
+ if (rc != 0) {
+ transport_generic_request_failure(se_cmd, rc);
+ return 0;
+ }
}
- atomic_sub(atomic_read(&T_TASK(cmd)->t_transport_timeout),
- &T_TASK(cmd)->t_se_count);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-}
-
-static void transport_generic_request_timeout(struct se_cmd *cmd)
-{
- unsigned long flags;
-
/*
- * Reset T_TASK(cmd)->t_se_count to allow transport_generic_remove()
- * to allow last call to free memory resources.
+ * Check if we need to delay processing because of ALUA
+ * Active/NonOptimized primary access state..
*/
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- if (atomic_read(&T_TASK(cmd)->t_transport_timeout) > 1) {
- int tmp = (atomic_read(&T_TASK(cmd)->t_transport_timeout) - 1);
-
- atomic_sub(tmp, &T_TASK(cmd)->t_se_count);
- }
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-
- transport_generic_remove(cmd, 0, 0);
-}
-
-static int
-transport_generic_allocate_buf(struct se_cmd *cmd, u32 data_length)
-{
- unsigned char *buf;
-
- buf = kzalloc(data_length, GFP_KERNEL);
- if (!(buf)) {
- printk(KERN_ERR "Unable to allocate memory for buffer\n");
- return -1;
- }
-
- T_TASK(cmd)->t_tasks_se_num = 0;
- T_TASK(cmd)->t_task_buf = buf;
+ core_alua_check_nonop_delay(se_cmd);
+ transport_handle_cdb_direct(se_cmd);
return 0;
}
-
-static inline u32 transport_lba_21(unsigned char *cdb)
-{
- return ((cdb[1] & 0x1f) << 16) | (cdb[2] << 8) | cdb[3];
-}
-
-static inline u32 transport_lba_32(unsigned char *cdb)
-{
- return (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
-}
-
-static inline unsigned long long transport_lba_64(unsigned char *cdb)
-{
- unsigned int __v1, __v2;
-
- __v1 = (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
- __v2 = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9];
-
- return ((unsigned long long)__v2) | (unsigned long long)__v1 << 32;
-}
+EXPORT_SYMBOL(target_submit_cmd_map_sgls);
/*
- * For VARIABLE_LENGTH_CDB w/ 32 byte extended CDBs
+ * target_submit_cmd - lookup unpacked lun and submit uninitialized se_cmd
+ *
+ * @se_cmd: command descriptor to submit
+ * @se_sess: associated se_sess for endpoint
+ * @cdb: pointer to SCSI CDB
+ * @sense: pointer to SCSI sense buffer
+ * @unpacked_lun: unpacked LUN to reference for struct se_lun
+ * @data_length: fabric expected data transfer length
+ * @task_addr: SAM task attribute
+ * @data_dir: DMA data direction
+ * @flags: flags for command submission from target_sc_flags_tables
+ *
+ * Returns non zero to signal active I/O shutdown failure. All other
+ * setup exceptions will be returned as a SCSI CHECK_CONDITION response,
+ * but still return zero here.
+ *
+ * This may only be called from process context, and also currently
+ * assumes internal allocation of fabric payload buffer by target-core.
+ *
+ * It also assumes interal target core SGL memory allocation.
*/
-static inline unsigned long long transport_lba_64_ext(unsigned char *cdb)
+int target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
+ unsigned char *cdb, unsigned char *sense, u32 unpacked_lun,
+ u32 data_length, int task_attr, int data_dir, int flags)
{
- unsigned int __v1, __v2;
-
- __v1 = (cdb[12] << 24) | (cdb[13] << 16) | (cdb[14] << 8) | cdb[15];
- __v2 = (cdb[16] << 24) | (cdb[17] << 16) | (cdb[18] << 8) | cdb[19];
-
- return ((unsigned long long)__v2) | (unsigned long long)__v1 << 32;
+ return target_submit_cmd_map_sgls(se_cmd, se_sess, cdb, sense,
+ unpacked_lun, data_length, task_attr, data_dir,
+ flags, NULL, 0, NULL, 0, NULL, 0);
}
+EXPORT_SYMBOL(target_submit_cmd);
-static void transport_set_supported_SAM_opcode(struct se_cmd *se_cmd)
+static void target_complete_tmr_failure(struct work_struct *work)
{
- unsigned long flags;
+ struct se_cmd *se_cmd = container_of(work, struct se_cmd, work);
- spin_lock_irqsave(&T_TASK(se_cmd)->t_state_lock, flags);
- se_cmd->se_cmd_flags |= SCF_SUPPORTED_SAM_OPCODE;
- spin_unlock_irqrestore(&T_TASK(se_cmd)->t_state_lock, flags);
+ se_cmd->se_tmr_req->response = TMR_LUN_DOES_NOT_EXIST;
+ se_cmd->se_tfo->queue_tm_rsp(se_cmd);
+
+ transport_cmd_check_stop_to_fabric(se_cmd);
}
-/*
- * Called from interrupt context.
- */
-static void transport_task_timeout_handler(unsigned long data)
+/**
+ * target_submit_tmr - lookup unpacked lun and submit uninitialized se_cmd
+ * for TMR CDBs
+ *
+ * @se_cmd: command descriptor to submit
+ * @se_sess: associated se_sess for endpoint
+ * @sense: pointer to SCSI sense buffer
+ * @unpacked_lun: unpacked LUN to reference for struct se_lun
+ * @fabric_context: fabric context for TMR req
+ * @tm_type: Type of TM request
+ * @gfp: gfp type for caller
+ * @tag: referenced task tag for TMR_ABORT_TASK
+ * @flags: submit cmd flags
+ *
+ * Callable from all contexts.
+ **/
+
+int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
+ unsigned char *sense, u32 unpacked_lun,
+ void *fabric_tmr_ptr, unsigned char tm_type,
+ gfp_t gfp, unsigned int tag, int flags)
{
- struct se_task *task = (struct se_task *)data;
- struct se_cmd *cmd = TASK_CMD(task);
- unsigned long flags;
+ struct se_portal_group *se_tpg;
+ int ret;
- DEBUG_TT("transport task timeout fired! task: %p cmd: %p\n", task, cmd);
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- if (task->task_flags & TF_STOP) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- return;
- }
- task->task_flags &= ~TF_RUNNING;
+ se_tpg = se_sess->se_tpg;
+ BUG_ON(!se_tpg);
+ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess,
+ 0, DMA_NONE, MSG_SIMPLE_TAG, sense);
/*
- * Determine if transport_complete_task() has already been called.
+ * FIXME: Currently expect caller to handle se_cmd->se_tmr_req
+ * allocation failure.
*/
- if (!(atomic_read(&task->task_active))) {
- DEBUG_TT("transport task: %p cmd: %p timeout task_active"
- " == 0\n", task, cmd);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- return;
- }
-
- atomic_inc(&T_TASK(cmd)->t_se_count);
- atomic_inc(&T_TASK(cmd)->t_transport_timeout);
- T_TASK(cmd)->t_tasks_failed = 1;
+ ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp);
+ if (ret < 0)
+ return -ENOMEM;
- atomic_set(&task->task_timeout, 1);
- task->task_error_status = PYX_TRANSPORT_TASK_TIMEOUT;
- task->task_scsi_status = 1;
+ if (tm_type == TMR_ABORT_TASK)
+ se_cmd->se_tmr_req->ref_task_tag = tag;
- if (atomic_read(&task->task_stop)) {
- DEBUG_TT("transport task: %p cmd: %p timeout task_stop"
- " == 1\n", task, cmd);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- complete(&task->task_stop_comp);
- return;
+ /* See target_submit_cmd for commentary */
+ ret = target_get_sess_cmd(se_sess, se_cmd, (flags & TARGET_SCF_ACK_KREF));
+ if (ret) {
+ core_tmr_release_req(se_cmd->se_tmr_req);
+ return ret;
}
- if (!(atomic_dec_and_test(&T_TASK(cmd)->t_task_cdbs_left))) {
- DEBUG_TT("transport task: %p cmd: %p timeout non zero"
- " t_task_cdbs_left\n", task, cmd);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- return;
+ ret = transport_lookup_tmr_lun(se_cmd, unpacked_lun);
+ if (ret) {
+ /*
+ * For callback during failure handling, push this work off
+ * to process context with TMR_LUN_DOES_NOT_EXIST status.
+ */
+ INIT_WORK(&se_cmd->work, target_complete_tmr_failure);
+ schedule_work(&se_cmd->work);
+ return 0;
}
- DEBUG_TT("transport task: %p cmd: %p timeout ZERO t_task_cdbs_left\n",
- task, cmd);
-
- cmd->t_state = TRANSPORT_COMPLETE_FAILURE;
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-
- transport_add_cmd_to_queue(cmd, TRANSPORT_COMPLETE_FAILURE);
-}
-
-/*
- * Called with T_TASK(cmd)->t_state_lock held.
- */
-static void transport_start_task_timer(struct se_task *task)
-{
- struct se_device *dev = task->se_dev;
- int timeout;
-
- if (task->task_flags & TF_RUNNING)
- return;
- /*
- * If the task_timeout is disabled, exit now.
- */
- timeout = DEV_ATTRIB(dev)->task_timeout;
- if (!(timeout))
- return;
-
- init_timer(&task->task_timer);
- task->task_timer.expires = (get_jiffies_64() + timeout * HZ);
- task->task_timer.data = (unsigned long) task;
- task->task_timer.function = transport_task_timeout_handler;
-
- task->task_flags |= TF_RUNNING;
- add_timer(&task->task_timer);
-#if 0
- printk(KERN_INFO "Starting task timer for cmd: %p task: %p seconds:"
- " %d\n", task->task_se_cmd, task, timeout);
-#endif
+ transport_generic_handle_tmr(se_cmd);
+ return 0;
}
+EXPORT_SYMBOL(target_submit_tmr);
/*
- * Called with spin_lock_irq(&T_TASK(cmd)->t_state_lock) held.
+ * If the cmd is active, request it to be stopped and sleep until it
+ * has completed.
*/
-void __transport_stop_task_timer(struct se_task *task, unsigned long *flags)
+bool target_stop_cmd(struct se_cmd *cmd, unsigned long *flags)
{
- struct se_cmd *cmd = TASK_CMD(task);
-
- if (!(task->task_flags & TF_RUNNING))
- return;
-
- task->task_flags |= TF_STOP;
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, *flags);
-
- del_timer_sync(&task->task_timer);
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, *flags);
- task->task_flags &= ~TF_RUNNING;
- task->task_flags &= ~TF_STOP;
-}
+ bool was_active = false;
-static void transport_stop_all_task_timers(struct se_cmd *cmd)
-{
- struct se_task *task = NULL, *task_tmp;
- unsigned long flags;
+ if (cmd->transport_state & CMD_T_BUSY) {
+ cmd->transport_state |= CMD_T_REQUEST_STOP;
+ spin_unlock_irqrestore(&cmd->t_state_lock, *flags);
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- list_for_each_entry_safe(task, task_tmp,
- &T_TASK(cmd)->t_task_list, t_list)
- __transport_stop_task_timer(task, &flags);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-}
+ pr_debug("cmd %p waiting to complete\n", cmd);
+ wait_for_completion(&cmd->task_stop_comp);
+ pr_debug("cmd %p stopped successfully\n", cmd);
-static inline int transport_tcq_window_closed(struct se_device *dev)
-{
- if (dev->dev_tcq_window_closed++ <
- PYX_TRANSPORT_WINDOW_CLOSED_THRESHOLD) {
- msleep(PYX_TRANSPORT_WINDOW_CLOSED_WAIT_SHORT);
- } else
- msleep(PYX_TRANSPORT_WINDOW_CLOSED_WAIT_LONG);
+ spin_lock_irqsave(&cmd->t_state_lock, *flags);
+ cmd->transport_state &= ~CMD_T_REQUEST_STOP;
+ cmd->transport_state &= ~CMD_T_BUSY;
+ was_active = true;
+ }
- wake_up_interruptible(&dev->dev_queue_obj->thread_wq);
- return 0;
+ return was_active;
}
/*
- * Called from Fabric Module context from transport_execute_tasks()
- *
- * The return of this function determins if the tasks from struct se_cmd
- * get added to the execution queue in transport_execute_tasks(),
- * or are added to the delayed or ordered lists here.
+ * Handle SAM-esque emulation for generic transport request failures.
*/
-static inline int transport_execute_task_attr(struct se_cmd *cmd)
+void transport_generic_request_failure(struct se_cmd *cmd,
+ sense_reason_t sense_reason)
{
- if (SE_DEV(cmd)->dev_task_attr_type != SAM_TASK_ATTR_EMULATED)
- return 1;
+ int ret = 0;
+
+ pr_debug("-----[ Storage Engine Exception for cmd: %p ITT: 0x%08x"
+ " CDB: 0x%02x\n", cmd, cmd->se_tfo->get_task_tag(cmd),
+ cmd->t_task_cdb[0]);
+ pr_debug("-----[ i_state: %d t_state: %d sense_reason: %d\n",
+ cmd->se_tfo->get_cmd_state(cmd),
+ cmd->t_state, sense_reason);
+ pr_debug("-----[ CMD_T_ACTIVE: %d CMD_T_STOP: %d CMD_T_SENT: %d\n",
+ (cmd->transport_state & CMD_T_ACTIVE) != 0,
+ (cmd->transport_state & CMD_T_STOP) != 0,
+ (cmd->transport_state & CMD_T_SENT) != 0);
+
/*
- * Check for the existance of HEAD_OF_QUEUE, and if true return 1
- * to allow the passed struct se_cmd list of tasks to the front of the list.
+ * For SAM Task Attribute emulation for failed struct se_cmd
*/
- if (cmd->sam_task_attr == TASK_ATTR_HOQ) {
- atomic_inc(&SE_DEV(cmd)->dev_hoq_count);
- smp_mb__after_atomic_inc();
- DEBUG_STA("Added HEAD_OF_QUEUE for CDB:"
- " 0x%02x, se_ordered_id: %u\n",
- T_TASK(cmd)->t_task_cdb[0],
- cmd->se_ordered_id);
- return 1;
- } else if (cmd->sam_task_attr == TASK_ATTR_ORDERED) {
- spin_lock(&SE_DEV(cmd)->ordered_cmd_lock);
- list_add_tail(&cmd->se_ordered_list,
- &SE_DEV(cmd)->ordered_cmd_list);
- spin_unlock(&SE_DEV(cmd)->ordered_cmd_lock);
-
- atomic_inc(&SE_DEV(cmd)->dev_ordered_sync);
- smp_mb__after_atomic_inc();
-
- DEBUG_STA("Added ORDERED for CDB: 0x%02x to ordered"
- " list, se_ordered_id: %u\n",
- T_TASK(cmd)->t_task_cdb[0],
- cmd->se_ordered_id);
- /*
- * Add ORDERED command to tail of execution queue if
- * no other older commands exist that need to be
- * completed first.
- */
- if (!(atomic_read(&SE_DEV(cmd)->simple_cmds)))
- return 1;
- } else {
- /*
- * For SIMPLE and UNTAGGED Task Attribute commands
- */
- atomic_inc(&SE_DEV(cmd)->simple_cmds);
- smp_mb__after_atomic_inc();
- }
+ transport_complete_task_attr(cmd);
/*
- * Otherwise if one or more outstanding ORDERED task attribute exist,
- * add the dormant task(s) built for the passed struct se_cmd to the
- * execution queue and become in Active state for this struct se_device.
+ * Handle special case for COMPARE_AND_WRITE failure, where the
+ * callback is expected to drop the per device ->caw_mutex.
*/
- if (atomic_read(&SE_DEV(cmd)->dev_ordered_sync) != 0) {
+ if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
+ cmd->transport_complete_callback)
+ cmd->transport_complete_callback(cmd);
+
+ switch (sense_reason) {
+ case TCM_NON_EXISTENT_LUN:
+ case TCM_UNSUPPORTED_SCSI_OPCODE:
+ case TCM_INVALID_CDB_FIELD:
+ case TCM_INVALID_PARAMETER_LIST:
+ case TCM_PARAMETER_LIST_LENGTH_ERROR:
+ case TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE:
+ case TCM_UNKNOWN_MODE_PAGE:
+ case TCM_WRITE_PROTECTED:
+ case TCM_ADDRESS_OUT_OF_RANGE:
+ case TCM_CHECK_CONDITION_ABORT_CMD:
+ case TCM_CHECK_CONDITION_UNIT_ATTENTION:
+ case TCM_CHECK_CONDITION_NOT_READY:
+ case TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED:
+ case TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED:
+ case TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED:
+ break;
+ case TCM_OUT_OF_RESOURCES:
+ sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ break;
+ case TCM_RESERVATION_CONFLICT:
/*
- * Otherwise, add cmd w/ tasks to delayed cmd queue that
- * will be drained upon competion of HEAD_OF_QUEUE task.
+ * No SENSE Data payload for this case, set SCSI Status
+ * and queue the response to $FABRIC_MOD.
+ *
+ * Uses linux/include/scsi/scsi.h SAM status codes defs
*/
- spin_lock(&SE_DEV(cmd)->delayed_cmd_lock);
- cmd->se_cmd_flags |= SCF_DELAYED_CMD_FROM_SAM_ATTR;
- list_add_tail(&cmd->se_delayed_list,
- &SE_DEV(cmd)->delayed_cmd_list);
- spin_unlock(&SE_DEV(cmd)->delayed_cmd_lock);
-
- DEBUG_STA("Added CDB: 0x%02x Task Attr: 0x%02x to"
- " delayed CMD list, se_ordered_id: %u\n",
- T_TASK(cmd)->t_task_cdb[0], cmd->sam_task_attr,
- cmd->se_ordered_id);
+ cmd->scsi_status = SAM_STAT_RESERVATION_CONFLICT;
/*
- * Return zero to let transport_execute_tasks() know
- * not to add the delayed tasks to the execution list.
+ * For UA Interlock Code 11b, a RESERVATION CONFLICT will
+ * establish a UNIT ATTENTION with PREVIOUS RESERVATION
+ * CONFLICT STATUS.
+ *
+ * See spc4r17, section 7.4.6 Control Mode Page, Table 349
*/
- return 0;
+ if (cmd->se_sess &&
+ cmd->se_dev->dev_attrib.emulate_ua_intlck_ctrl == 2)
+ core_scsi3_ua_allocate(cmd->se_sess->se_node_acl,
+ cmd->orig_fe_lun, 0x2C,
+ ASCQ_2CH_PREVIOUS_RESERVATION_CONFLICT_STATUS);
+
+ trace_target_cmd_complete(cmd);
+ ret = cmd->se_tfo-> queue_status(cmd);
+ if (ret == -EAGAIN || ret == -ENOMEM)
+ goto queue_full;
+ goto check_stop;
+ default:
+ pr_err("Unknown transport error for CDB 0x%02x: %d\n",
+ cmd->t_task_cdb[0], sense_reason);
+ sense_reason = TCM_UNSUPPORTED_SCSI_OPCODE;
+ break;
}
- /*
- * Otherwise, no ORDERED task attributes exist..
- */
- return 1;
+
+ ret = transport_send_check_condition_and_sense(cmd, sense_reason, 0);
+ if (ret == -EAGAIN || ret == -ENOMEM)
+ goto queue_full;
+
+check_stop:
+ transport_lun_remove_cmd(cmd);
+ if (!transport_cmd_check_stop_to_fabric(cmd))
+ ;
+ return;
+
+queue_full:
+ cmd->t_state = TRANSPORT_COMPLETE_QF_OK;
+ transport_handle_queue_full(cmd, cmd->se_dev);
}
+EXPORT_SYMBOL(transport_generic_request_failure);
-/*
- * Called from fabric module context in transport_generic_new_cmd() and
- * transport_generic_process_write()
- */
-static int transport_execute_tasks(struct se_cmd *cmd)
+void __target_execute_cmd(struct se_cmd *cmd)
{
- int add_tasks;
+ sense_reason_t ret;
- if (!(cmd->se_cmd_flags & SCF_SE_DISABLE_ONLINE_CHECK)) {
- if (se_dev_check_online(cmd->se_orig_obj_ptr) != 0) {
- cmd->transport_error_status =
- PYX_TRANSPORT_LU_COMM_FAILURE;
- transport_generic_request_failure(cmd, NULL, 0, 1);
- return 0;
+ if (cmd->execute_cmd) {
+ ret = cmd->execute_cmd(cmd);
+ if (ret) {
+ spin_lock_irq(&cmd->t_state_lock);
+ cmd->transport_state &= ~(CMD_T_BUSY|CMD_T_SENT);
+ spin_unlock_irq(&cmd->t_state_lock);
+
+ transport_generic_request_failure(cmd, ret);
}
}
- /*
- * Call transport_cmd_check_stop() to see if a fabric exception
- * has occured that prevents execution.
- */
- if (!(transport_cmd_check_stop(cmd, 0, TRANSPORT_PROCESSING))) {
- /*
- * Check for SAM Task Attribute emulation and HEAD_OF_QUEUE
- * attribute for the tasks of the received struct se_cmd CDB
- */
- add_tasks = transport_execute_task_attr(cmd);
- if (add_tasks == 0)
- goto execute_tasks;
- /*
- * This calls transport_add_tasks_from_cmd() to handle
- * HEAD_OF_QUEUE ordering for SAM Task Attribute emulation
- * (if enabled) in __transport_add_task_to_execute_queue() and
- * transport_add_task_check_sam_attr().
- */
- transport_add_tasks_from_cmd(cmd);
- }
- /*
- * Kick the execution queue for the cmd associated struct se_device
- * storage object.
- */
-execute_tasks:
- __transport_execute_tasks(SE_DEV(cmd));
- return 0;
}
-/*
- * Called to check struct se_device tcq depth window, and once open pull struct se_task
- * from struct se_device->execute_task_list and
- *
- * Called from transport_processing_thread()
- */
-static int __transport_execute_tasks(struct se_device *dev)
+static bool target_handle_task_attr(struct se_cmd *cmd)
{
- int error;
- struct se_cmd *cmd = NULL;
- struct se_task *task;
- unsigned long flags;
+ struct se_device *dev = cmd->se_dev;
+
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ return false;
/*
- * Check if there is enough room in the device and HBA queue to send
- * struct se_transport_task's to the selected transport.
+ * Check for the existence of HEAD_OF_QUEUE, and if true return 1
+ * to allow the passed struct se_cmd list of tasks to the front of the list.
*/
-check_depth:
- spin_lock_irqsave(&SE_HBA(dev)->hba_queue_lock, flags);
- if (!(atomic_read(&dev->depth_left)) ||
- !(atomic_read(&SE_HBA(dev)->left_queue_depth))) {
- spin_unlock_irqrestore(&SE_HBA(dev)->hba_queue_lock, flags);
- return transport_tcq_window_closed(dev);
- }
- dev->dev_tcq_window_closed = 0;
-
- spin_lock(&dev->execute_task_lock);
- task = transport_get_task_from_execute_queue(dev);
- spin_unlock(&dev->execute_task_lock);
-
- if (!task) {
- spin_unlock_irqrestore(&SE_HBA(dev)->hba_queue_lock, flags);
- return 0;
- }
-
- atomic_dec(&dev->depth_left);
- atomic_dec(&SE_HBA(dev)->left_queue_depth);
- spin_unlock_irqrestore(&SE_HBA(dev)->hba_queue_lock, flags);
-
- cmd = TASK_CMD(task);
+ switch (cmd->sam_task_attr) {
+ case MSG_HEAD_TAG:
+ pr_debug("Added HEAD_OF_QUEUE for CDB: 0x%02x, "
+ "se_ordered_id: %u\n",
+ cmd->t_task_cdb[0], cmd->se_ordered_id);
+ return false;
+ case MSG_ORDERED_TAG:
+ atomic_inc(&dev->dev_ordered_sync);
+ smp_mb__after_atomic();
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- atomic_set(&task->task_active, 1);
- atomic_set(&task->task_sent, 1);
- atomic_inc(&T_TASK(cmd)->t_task_cdbs_sent);
+ pr_debug("Added ORDERED for CDB: 0x%02x to ordered list, "
+ " se_ordered_id: %u\n",
+ cmd->t_task_cdb[0], cmd->se_ordered_id);
- if (atomic_read(&T_TASK(cmd)->t_task_cdbs_sent) ==
- T_TASK(cmd)->t_task_cdbs)
- atomic_set(&cmd->transport_sent, 1);
-
- transport_start_task_timer(task);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- /*
- * The struct se_cmd->transport_emulate_cdb() function pointer is used
- * to grab REPORT_LUNS CDBs before they hit the
- * struct se_subsystem_api->do_task() caller below.
- */
- if (cmd->transport_emulate_cdb) {
- error = cmd->transport_emulate_cdb(cmd);
- if (error != 0) {
- cmd->transport_error_status = error;
- atomic_set(&task->task_active, 0);
- atomic_set(&cmd->transport_sent, 0);
- transport_stop_tasks_for_cmd(cmd);
- transport_generic_request_failure(cmd, dev, 0, 1);
- goto check_depth;
- }
/*
- * Handle the successful completion for transport_emulate_cdb()
- * for synchronous operation, following SCF_EMULATE_CDB_ASYNC
- * Otherwise the caller is expected to complete the task with
- * proper status.
+ * Execute an ORDERED command if no other older commands
+ * exist that need to be completed first.
*/
- if (!(cmd->se_cmd_flags & SCF_EMULATE_CDB_ASYNC)) {
- cmd->scsi_status = SAM_STAT_GOOD;
- task->task_scsi_status = GOOD;
- transport_complete_task(task, 1);
- }
- } else {
+ if (!atomic_read(&dev->simple_cmds))
+ return false;
+ break;
+ default:
/*
- * Currently for all virtual TCM plugins including IBLOCK, FILEIO and
- * RAMDISK we use the internal transport_emulate_control_cdb() logic
- * with struct se_subsystem_api callers for the primary SPC-3 TYPE_DISK
- * LUN emulation code.
- *
- * For TCM/pSCSI and all other SCF_SCSI_DATA_SG_IO_CDB I/O tasks we
- * call ->do_task() directly and let the underlying TCM subsystem plugin
- * code handle the CDB emulation.
+ * For SIMPLE and UNTAGGED Task Attribute commands
*/
- if ((TRANSPORT(dev)->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV) &&
- (!(TASK_CMD(task)->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB)))
- error = transport_emulate_control_cdb(task);
- else
- error = TRANSPORT(dev)->do_task(task);
-
- if (error != 0) {
- cmd->transport_error_status = error;
- atomic_set(&task->task_active, 0);
- atomic_set(&cmd->transport_sent, 0);
- transport_stop_tasks_for_cmd(cmd);
- transport_generic_request_failure(cmd, dev, 0, 1);
- }
+ atomic_inc(&dev->simple_cmds);
+ smp_mb__after_atomic();
+ break;
}
- goto check_depth;
+ if (atomic_read(&dev->dev_ordered_sync) == 0)
+ return false;
- return 0;
-}
-
-void transport_new_cmd_failure(struct se_cmd *se_cmd)
-{
- unsigned long flags;
- /*
- * Any unsolicited data will get dumped for failed command inside of
- * the fabric plugin
- */
- spin_lock_irqsave(&T_TASK(se_cmd)->t_state_lock, flags);
- se_cmd->se_cmd_flags |= SCF_SE_CMD_FAILED;
- se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- spin_unlock_irqrestore(&T_TASK(se_cmd)->t_state_lock, flags);
+ spin_lock(&dev->delayed_cmd_lock);
+ list_add_tail(&cmd->se_delayed_node, &dev->delayed_cmd_list);
+ spin_unlock(&dev->delayed_cmd_lock);
- CMD_TFO(se_cmd)->new_cmd_failure(se_cmd);
+ pr_debug("Added CDB: 0x%02x Task Attr: 0x%02x to"
+ " delayed CMD list, se_ordered_id: %u\n",
+ cmd->t_task_cdb[0], cmd->sam_task_attr,
+ cmd->se_ordered_id);
+ return true;
}
-static void transport_nop_wait_for_tasks(struct se_cmd *, int, int);
-
-static inline u32 transport_get_sectors_6(
- unsigned char *cdb,
- struct se_cmd *cmd,
- int *ret)
+void target_execute_cmd(struct se_cmd *cmd)
{
- struct se_device *dev = SE_LUN(cmd)->lun_se_dev;
-
/*
- * Assume TYPE_DISK for non struct se_device objects.
- * Use 8-bit sector value.
+ * If the received CDB has aleady been aborted stop processing it here.
*/
- if (!dev)
- goto type_disk;
-
- /*
- * Use 24-bit allocation length for TYPE_TAPE.
- */
- if (TRANSPORT(dev)->get_device_type(dev) == TYPE_TAPE)
- return (u32)(cdb[2] << 16) + (cdb[3] << 8) + cdb[4];
-
- /*
- * Everything else assume TYPE_DISK Sector CDB location.
- * Use 8-bit sector value.
- */
-type_disk:
- return (u32)cdb[4];
-}
-
-static inline u32 transport_get_sectors_10(
- unsigned char *cdb,
- struct se_cmd *cmd,
- int *ret)
-{
- struct se_device *dev = SE_LUN(cmd)->lun_se_dev;
+ if (transport_check_aborted_status(cmd, 1))
+ return;
/*
- * Assume TYPE_DISK for non struct se_device objects.
- * Use 16-bit sector value.
+ * Determine if frontend context caller is requesting the stopping of
+ * this command for frontend exceptions.
*/
- if (!dev)
- goto type_disk;
+ spin_lock_irq(&cmd->t_state_lock);
+ if (cmd->transport_state & CMD_T_STOP) {
+ pr_debug("%s:%d CMD_T_STOP for ITT: 0x%08x\n",
+ __func__, __LINE__,
+ cmd->se_tfo->get_task_tag(cmd));
- /*
- * XXX_10 is not defined in SSC, throw an exception
- */
- if (TRANSPORT(dev)->get_device_type(dev) == TYPE_TAPE) {
- *ret = -1;
- return 0;
+ spin_unlock_irq(&cmd->t_state_lock);
+ complete_all(&cmd->t_transport_stop_comp);
+ return;
}
+ cmd->t_state = TRANSPORT_PROCESSING;
+ cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT;
+ spin_unlock_irq(&cmd->t_state_lock);
/*
- * Everything else assume TYPE_DISK Sector CDB location.
- * Use 16-bit sector value.
+ * Perform WRITE_INSERT of PI using software emulation when backend
+ * device has PI enabled, if the transport has not already generated
+ * PI using hardware WRITE_INSERT offload.
*/
-type_disk:
- return (u32)(cdb[7] << 8) + cdb[8];
-}
-
-static inline u32 transport_get_sectors_12(
- unsigned char *cdb,
- struct se_cmd *cmd,
- int *ret)
-{
- struct se_device *dev = SE_LUN(cmd)->lun_se_dev;
-
- /*
- * Assume TYPE_DISK for non struct se_device objects.
- * Use 32-bit sector value.
- */
- if (!dev)
- goto type_disk;
-
- /*
- * XXX_12 is not defined in SSC, throw an exception
- */
- if (TRANSPORT(dev)->get_device_type(dev) == TYPE_TAPE) {
- *ret = -1;
- return 0;
+ if (cmd->prot_op == TARGET_PROT_DOUT_INSERT) {
+ if (!(cmd->se_sess->sup_prot_ops & TARGET_PROT_DOUT_INSERT))
+ sbc_dif_generate(cmd);
}
- /*
- * Everything else assume TYPE_DISK Sector CDB location.
- * Use 32-bit sector value.
- */
-type_disk:
- return (u32)(cdb[6] << 24) + (cdb[7] << 16) + (cdb[8] << 8) + cdb[9];
-}
-
-static inline u32 transport_get_sectors_16(
- unsigned char *cdb,
- struct se_cmd *cmd,
- int *ret)
-{
- struct se_device *dev = SE_LUN(cmd)->lun_se_dev;
-
- /*
- * Assume TYPE_DISK for non struct se_device objects.
- * Use 32-bit sector value.
- */
- if (!dev)
- goto type_disk;
-
- /*
- * Use 24-bit allocation length for TYPE_TAPE.
- */
- if (TRANSPORT(dev)->get_device_type(dev) == TYPE_TAPE)
- return (u32)(cdb[12] << 16) + (cdb[13] << 8) + cdb[14];
+ if (target_handle_task_attr(cmd)) {
+ spin_lock_irq(&cmd->t_state_lock);
+ cmd->transport_state &= ~CMD_T_BUSY|CMD_T_SENT;
+ spin_unlock_irq(&cmd->t_state_lock);
+ return;
+ }
-type_disk:
- return (u32)(cdb[10] << 24) + (cdb[11] << 16) +
- (cdb[12] << 8) + cdb[13];
+ __target_execute_cmd(cmd);
}
+EXPORT_SYMBOL(target_execute_cmd);
/*
- * Used for VARIABLE_LENGTH_CDB WRITE_32 and READ_32 variants
+ * Process all commands up to the last received ORDERED task attribute which
+ * requires another blocking boundary
*/
-static inline u32 transport_get_sectors_32(
- unsigned char *cdb,
- struct se_cmd *cmd,
- int *ret)
+static void target_restart_delayed_cmds(struct se_device *dev)
{
- /*
- * Assume TYPE_DISK for non struct se_device objects.
- * Use 32-bit sector value.
- */
- return (u32)(cdb[28] << 24) + (cdb[29] << 16) +
- (cdb[30] << 8) + cdb[31];
+ for (;;) {
+ struct se_cmd *cmd;
-}
-
-static inline u32 transport_get_size(
- u32 sectors,
- unsigned char *cdb,
- struct se_cmd *cmd)
-{
- struct se_device *dev = SE_DEV(cmd);
-
- if (TRANSPORT(dev)->get_device_type(dev) == TYPE_TAPE) {
- if (cdb[1] & 1) { /* sectors */
- return DEV_ATTRIB(dev)->block_size * sectors;
- } else /* bytes */
- return sectors;
- }
-#if 0
- printk(KERN_INFO "Returning block_size: %u, sectors: %u == %u for"
- " %s object\n", DEV_ATTRIB(dev)->block_size, sectors,
- DEV_ATTRIB(dev)->block_size * sectors,
- TRANSPORT(dev)->name);
-#endif
- return DEV_ATTRIB(dev)->block_size * sectors;
-}
-
-unsigned char transport_asciihex_to_binaryhex(unsigned char val[2])
-{
- unsigned char result = 0;
- /*
- * MSB
- */
- if ((val[0] >= 'a') && (val[0] <= 'f'))
- result = ((val[0] - 'a' + 10) & 0xf) << 4;
- else
- if ((val[0] >= 'A') && (val[0] <= 'F'))
- result = ((val[0] - 'A' + 10) & 0xf) << 4;
- else /* digit */
- result = ((val[0] - '0') & 0xf) << 4;
- /*
- * LSB
- */
- if ((val[1] >= 'a') && (val[1] <= 'f'))
- result |= ((val[1] - 'a' + 10) & 0xf);
- else
- if ((val[1] >= 'A') && (val[1] <= 'F'))
- result |= ((val[1] - 'A' + 10) & 0xf);
- else /* digit */
- result |= ((val[1] - '0') & 0xf);
-
- return result;
-}
-EXPORT_SYMBOL(transport_asciihex_to_binaryhex);
-
-static void transport_xor_callback(struct se_cmd *cmd)
-{
- unsigned char *buf, *addr;
- struct se_mem *se_mem;
- unsigned int offset;
- int i;
- /*
- * From sbc3r22.pdf section 5.48 XDWRITEREAD (10) command
- *
- * 1) read the specified logical block(s);
- * 2) transfer logical blocks from the data-out buffer;
- * 3) XOR the logical blocks transferred from the data-out buffer with
- * the logical blocks read, storing the resulting XOR data in a buffer;
- * 4) if the DISABLE WRITE bit is set to zero, then write the logical
- * blocks transferred from the data-out buffer; and
- * 5) transfer the resulting XOR data to the data-in buffer.
- */
- buf = kmalloc(cmd->data_length, GFP_KERNEL);
- if (!(buf)) {
- printk(KERN_ERR "Unable to allocate xor_callback buf\n");
- return;
- }
- /*
- * Copy the scatterlist WRITE buffer located at T_TASK(cmd)->t_mem_list
- * into the locally allocated *buf
- */
- transport_memcpy_se_mem_read_contig(cmd, buf, T_TASK(cmd)->t_mem_list);
- /*
- * Now perform the XOR against the BIDI read memory located at
- * T_TASK(cmd)->t_mem_bidi_list
- */
+ spin_lock(&dev->delayed_cmd_lock);
+ if (list_empty(&dev->delayed_cmd_list)) {
+ spin_unlock(&dev->delayed_cmd_lock);
+ break;
+ }
- offset = 0;
- list_for_each_entry(se_mem, T_TASK(cmd)->t_mem_bidi_list, se_list) {
- addr = (unsigned char *)kmap_atomic(se_mem->se_page, KM_USER0);
- if (!(addr))
- goto out;
+ cmd = list_entry(dev->delayed_cmd_list.next,
+ struct se_cmd, se_delayed_node);
+ list_del(&cmd->se_delayed_node);
+ spin_unlock(&dev->delayed_cmd_lock);
- for (i = 0; i < se_mem->se_len; i++)
- *(addr + se_mem->se_off + i) ^= *(buf + offset + i);
+ __target_execute_cmd(cmd);
- offset += se_mem->se_len;
- kunmap_atomic(addr, KM_USER0);
+ if (cmd->sam_task_attr == MSG_ORDERED_TAG)
+ break;
}
-out:
- kfree(buf);
}
/*
- * Used to obtain Sense Data from underlying Linux/SCSI struct scsi_cmnd
+ * Called from I/O completion to determine which dormant/delayed
+ * and ordered cmds need to have their tasks added to the execution queue.
*/
-static int transport_get_sense_data(struct se_cmd *cmd)
+static void transport_complete_task_attr(struct se_cmd *cmd)
{
- unsigned char *buffer = cmd->sense_buffer, *sense_buffer = NULL;
- struct se_device *dev;
- struct se_task *task = NULL, *task_tmp;
- unsigned long flags;
- u32 offset = 0;
-
- if (!SE_LUN(cmd)) {
- printk(KERN_ERR "SE_LUN(cmd) is NULL\n");
- return -1;
- }
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- return 0;
- }
-
- list_for_each_entry_safe(task, task_tmp,
- &T_TASK(cmd)->t_task_list, t_list) {
-
- if (!task->task_sense)
- continue;
+ struct se_device *dev = cmd->se_dev;
- dev = task->se_dev;
- if (!(dev))
- continue;
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
+ return;
- if (!TRANSPORT(dev)->get_sense_buffer) {
- printk(KERN_ERR "TRANSPORT(dev)->get_sense_buffer"
- " is NULL\n");
- continue;
- }
+ if (cmd->sam_task_attr == MSG_SIMPLE_TAG) {
+ atomic_dec(&dev->simple_cmds);
+ smp_mb__after_atomic();
+ dev->dev_cur_ordered_id++;
+ pr_debug("Incremented dev->dev_cur_ordered_id: %u for"
+ " SIMPLE: %u\n", dev->dev_cur_ordered_id,
+ cmd->se_ordered_id);
+ } else if (cmd->sam_task_attr == MSG_HEAD_TAG) {
+ dev->dev_cur_ordered_id++;
+ pr_debug("Incremented dev_cur_ordered_id: %u for"
+ " HEAD_OF_QUEUE: %u\n", dev->dev_cur_ordered_id,
+ cmd->se_ordered_id);
+ } else if (cmd->sam_task_attr == MSG_ORDERED_TAG) {
+ atomic_dec(&dev->dev_ordered_sync);
+ smp_mb__after_atomic();
- sense_buffer = TRANSPORT(dev)->get_sense_buffer(task);
- if (!(sense_buffer)) {
- printk(KERN_ERR "ITT[0x%08x]_TASK[%d]: Unable to locate"
- " sense buffer for task with sense\n",
- CMD_TFO(cmd)->get_task_tag(cmd), task->task_no);
- continue;
- }
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-
- offset = CMD_TFO(cmd)->set_fabric_sense_len(cmd,
- TRANSPORT_SENSE_BUFFER);
-
- memcpy((void *)&buffer[offset], (void *)sense_buffer,
- TRANSPORT_SENSE_BUFFER);
- cmd->scsi_status = task->task_scsi_status;
- /* Automatically padded */
- cmd->scsi_sense_length =
- (TRANSPORT_SENSE_BUFFER + offset);
-
- printk(KERN_INFO "HBA_[%u]_PLUG[%s]: Set SAM STATUS: 0x%02x"
- " and sense\n",
- dev->se_hba->hba_id, TRANSPORT(dev)->name,
- cmd->scsi_status);
- return 0;
+ dev->dev_cur_ordered_id++;
+ pr_debug("Incremented dev_cur_ordered_id: %u for ORDERED:"
+ " %u\n", dev->dev_cur_ordered_id, cmd->se_ordered_id);
}
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- return -1;
+ target_restart_delayed_cmds(dev);
}
-static int transport_allocate_resources(struct se_cmd *cmd)
+static void transport_complete_qf(struct se_cmd *cmd)
{
- u32 length = cmd->data_length;
-
- if ((cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) ||
- (cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB))
- return transport_generic_get_mem(cmd, length, PAGE_SIZE);
- else if (cmd->se_cmd_flags & SCF_SCSI_CONTROL_NONSG_IO_CDB)
- return transport_generic_allocate_buf(cmd, length);
- else
- return 0;
-}
+ int ret = 0;
-static int
-transport_handle_reservation_conflict(struct se_cmd *cmd)
-{
- cmd->transport_wait_for_tasks = &transport_nop_wait_for_tasks;
- cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- cmd->se_cmd_flags |= SCF_SCSI_RESERVATION_CONFLICT;
- cmd->scsi_status = SAM_STAT_RESERVATION_CONFLICT;
- /*
- * For UA Interlock Code 11b, a RESERVATION CONFLICT will
- * establish a UNIT ATTENTION with PREVIOUS RESERVATION
- * CONFLICT STATUS.
- *
- * See spc4r17, section 7.4.6 Control Mode Page, Table 349
- */
- if (SE_SESS(cmd) &&
- DEV_ATTRIB(cmd->se_dev)->emulate_ua_intlck_ctrl == 2)
- core_scsi3_ua_allocate(SE_SESS(cmd)->se_node_acl,
- cmd->orig_fe_lun, 0x2C,
- ASCQ_2CH_PREVIOUS_RESERVATION_CONFLICT_STATUS);
- return -2;
-}
+ transport_complete_task_attr(cmd);
-/* transport_generic_cmd_sequencer():
- *
- * Generic Command Sequencer that should work for most DAS transport
- * drivers.
- *
- * Called from transport_generic_allocate_tasks() in the $FABRIC_MOD
- * RX Thread.
- *
- * FIXME: Need to support other SCSI OPCODES where as well.
- */
-static int transport_generic_cmd_sequencer(
- struct se_cmd *cmd,
- unsigned char *cdb)
-{
- struct se_device *dev = SE_DEV(cmd);
- struct se_subsystem_dev *su_dev = dev->se_sub_dev;
- int ret = 0, sector_ret = 0, passthrough;
- u32 sectors = 0, size = 0, pr_reg_type = 0;
- u16 service_action;
- u8 alua_ascq = 0;
- /*
- * Check for an existing UNIT ATTENTION condition
- */
- if (core_scsi3_ua_check(cmd, cdb) < 0) {
- cmd->transport_wait_for_tasks =
- &transport_nop_wait_for_tasks;
- cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- cmd->scsi_sense_reason = TCM_CHECK_CONDITION_UNIT_ATTENTION;
- return -2;
- }
- /*
- * Check status of Asymmetric Logical Unit Assignment port
- */
- ret = T10_ALUA(su_dev)->alua_state_check(cmd, cdb, &alua_ascq);
- if (ret != 0) {
- cmd->transport_wait_for_tasks = &transport_nop_wait_for_tasks;
- /*
- * Set SCSI additional sense code (ASC) to 'LUN Not Accessable';
- * The ALUA additional sense code qualifier (ASCQ) is determined
- * by the ALUA primary or secondary access state..
- */
- if (ret > 0) {
-#if 0
- printk(KERN_INFO "[%s]: ALUA TG Port not available,"
- " SenseKey: NOT_READY, ASC/ASCQ: 0x04/0x%02x\n",
- CMD_TFO(cmd)->get_fabric_name(), alua_ascq);
-#endif
- transport_set_sense_codes(cmd, 0x04, alua_ascq);
- cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- cmd->scsi_sense_reason = TCM_CHECK_CONDITION_NOT_READY;
- return -2;
- }
- goto out_invalid_cdb_field;
- }
- /*
- * Check status for SPC-3 Persistent Reservations
- */
- if (T10_PR_OPS(su_dev)->t10_reservation_check(cmd, &pr_reg_type) != 0) {
- if (T10_PR_OPS(su_dev)->t10_seq_non_holder(
- cmd, cdb, pr_reg_type) != 0)
- return transport_handle_reservation_conflict(cmd);
- /*
- * This means the CDB is allowed for the SCSI Initiator port
- * when said port is *NOT* holding the legacy SPC-2 or
- * SPC-3 Persistent Reservation.
- */
+ if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) {
+ trace_target_cmd_complete(cmd);
+ ret = cmd->se_tfo->queue_status(cmd);
+ if (ret)
+ goto out;
}
- switch (cdb[0]) {
- case READ_6:
- sectors = transport_get_sectors_6(cdb, cmd, &sector_ret);
- if (sector_ret)
- goto out_unsupported_cdb;
- size = transport_get_size(sectors, cdb, cmd);
- cmd->transport_split_cdb = &split_cdb_XX_6;
- T_TASK(cmd)->t_task_lba = transport_lba_21(cdb);
- cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB;
- break;
- case READ_10:
- sectors = transport_get_sectors_10(cdb, cmd, &sector_ret);
- if (sector_ret)
- goto out_unsupported_cdb;
- size = transport_get_size(sectors, cdb, cmd);
- cmd->transport_split_cdb = &split_cdb_XX_10;
- T_TASK(cmd)->t_task_lba = transport_lba_32(cdb);
- cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB;
- break;
- case READ_12:
- sectors = transport_get_sectors_12(cdb, cmd, &sector_ret);
- if (sector_ret)
- goto out_unsupported_cdb;
- size = transport_get_size(sectors, cdb, cmd);
- cmd->transport_split_cdb = &split_cdb_XX_12;
- T_TASK(cmd)->t_task_lba = transport_lba_32(cdb);
- cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB;
- break;
- case READ_16:
- sectors = transport_get_sectors_16(cdb, cmd, &sector_ret);
- if (sector_ret)
- goto out_unsupported_cdb;
- size = transport_get_size(sectors, cdb, cmd);
- cmd->transport_split_cdb = &split_cdb_XX_16;
- T_TASK(cmd)->t_task_lba = transport_lba_64(cdb);
- cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB;
- break;
- case WRITE_6:
- sectors = transport_get_sectors_6(cdb, cmd, &sector_ret);
- if (sector_ret)
- goto out_unsupported_cdb;
- size = transport_get_size(sectors, cdb, cmd);
- cmd->transport_split_cdb = &split_cdb_XX_6;
- T_TASK(cmd)->t_task_lba = transport_lba_21(cdb);
- cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB;
- break;
- case WRITE_10:
- sectors = transport_get_sectors_10(cdb, cmd, &sector_ret);
- if (sector_ret)
- goto out_unsupported_cdb;
- size = transport_get_size(sectors, cdb, cmd);
- cmd->transport_split_cdb = &split_cdb_XX_10;
- T_TASK(cmd)->t_task_lba = transport_lba_32(cdb);
- T_TASK(cmd)->t_tasks_fua = (cdb[1] & 0x8);
- cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB;
- break;
- case WRITE_12:
- sectors = transport_get_sectors_12(cdb, cmd, &sector_ret);
- if (sector_ret)
- goto out_unsupported_cdb;
- size = transport_get_size(sectors, cdb, cmd);
- cmd->transport_split_cdb = &split_cdb_XX_12;
- T_TASK(cmd)->t_task_lba = transport_lba_32(cdb);
- T_TASK(cmd)->t_tasks_fua = (cdb[1] & 0x8);
- cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB;
- break;
- case WRITE_16:
- sectors = transport_get_sectors_16(cdb, cmd, &sector_ret);
- if (sector_ret)
- goto out_unsupported_cdb;
- size = transport_get_size(sectors, cdb, cmd);
- cmd->transport_split_cdb = &split_cdb_XX_16;
- T_TASK(cmd)->t_task_lba = transport_lba_64(cdb);
- T_TASK(cmd)->t_tasks_fua = (cdb[1] & 0x8);
- cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB;
- break;
- case XDWRITEREAD_10:
- if ((cmd->data_direction != DMA_TO_DEVICE) ||
- !(T_TASK(cmd)->t_tasks_bidi))
- goto out_invalid_cdb_field;
- sectors = transport_get_sectors_10(cdb, cmd, &sector_ret);
- if (sector_ret)
- goto out_unsupported_cdb;
- size = transport_get_size(sectors, cdb, cmd);
- cmd->transport_split_cdb = &split_cdb_XX_10;
- T_TASK(cmd)->t_task_lba = transport_lba_32(cdb);
- cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB;
- passthrough = (TRANSPORT(dev)->transport_type ==
- TRANSPORT_PLUGIN_PHBA_PDEV);
- /*
- * Skip the remaining assignments for TCM/PSCSI passthrough
- */
- if (passthrough)
- break;
- /*
- * Setup BIDI XOR callback to be run during transport_generic_complete_ok()
- */
- cmd->transport_complete_callback = &transport_xor_callback;
- T_TASK(cmd)->t_tasks_fua = (cdb[1] & 0x8);
+ switch (cmd->data_direction) {
+ case DMA_FROM_DEVICE:
+ trace_target_cmd_complete(cmd);
+ ret = cmd->se_tfo->queue_data_in(cmd);
break;
- case VARIABLE_LENGTH_CMD:
- service_action = get_unaligned_be16(&cdb[8]);
- /*
- * Determine if this is TCM/PSCSI device and we should disable
- * internal emulation for this CDB.
- */
- passthrough = (TRANSPORT(dev)->transport_type ==
- TRANSPORT_PLUGIN_PHBA_PDEV);
-
- switch (service_action) {
- case XDWRITEREAD_32:
- sectors = transport_get_sectors_32(cdb, cmd, &sector_ret);
- if (sector_ret)
- goto out_unsupported_cdb;
- size = transport_get_size(sectors, cdb, cmd);
- /*
- * Use WRITE_32 and READ_32 opcodes for the emulated
- * XDWRITE_READ_32 logic.
- */
- cmd->transport_split_cdb = &split_cdb_XX_32;
- T_TASK(cmd)->t_task_lba = transport_lba_64_ext(cdb);
- cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB;
-
- /*
- * Skip the remaining assignments for TCM/PSCSI passthrough
- */
- if (passthrough)
- break;
-
- /*
- * Setup BIDI XOR callback to be run during
- * transport_generic_complete_ok()
- */
- cmd->transport_complete_callback = &transport_xor_callback;
- T_TASK(cmd)->t_tasks_fua = (cdb[10] & 0x8);
- break;
- case WRITE_SAME_32:
- sectors = transport_get_sectors_32(cdb, cmd, &sector_ret);
- if (sector_ret)
- goto out_unsupported_cdb;
- size = transport_get_size(sectors, cdb, cmd);
- T_TASK(cmd)->t_task_lba = get_unaligned_be64(&cdb[12]);
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
-
- /*
- * Skip the remaining assignments for TCM/PSCSI passthrough
- */
- if (passthrough)
+ case DMA_TO_DEVICE:
+ if (cmd->se_cmd_flags & SCF_BIDI) {
+ ret = cmd->se_tfo->queue_data_in(cmd);
+ if (ret < 0)
break;
-
- if ((cdb[10] & 0x04) || (cdb[10] & 0x02)) {
- printk(KERN_ERR "WRITE_SAME PBDATA and LBDATA"
- " bits not supported for Block Discard"
- " Emulation\n");
- goto out_invalid_cdb_field;
- }
- /*
- * Currently for the emulated case we only accept
- * tpws with the UNMAP=1 bit set.
- */
- if (!(cdb[10] & 0x08)) {
- printk(KERN_ERR "WRITE_SAME w/o UNMAP bit not"
- " supported for Block Discard Emulation\n");
- goto out_invalid_cdb_field;
- }
- break;
- default:
- printk(KERN_ERR "VARIABLE_LENGTH_CMD service action"
- " 0x%04x not supported\n", service_action);
- goto out_unsupported_cdb;
- }
- break;
- case 0xa3:
- if (TRANSPORT(dev)->get_device_type(dev) != TYPE_ROM) {
- /* MAINTENANCE_IN from SCC-2 */
- /*
- * Check for emulated MI_REPORT_TARGET_PGS.
- */
- if (cdb[1] == MI_REPORT_TARGET_PGS) {
- cmd->transport_emulate_cdb =
- (T10_ALUA(su_dev)->alua_type ==
- SPC3_ALUA_EMULATED) ?
- &core_emulate_report_target_port_groups :
- NULL;
- }
- size = (cdb[6] << 24) | (cdb[7] << 16) |
- (cdb[8] << 8) | cdb[9];
- } else {
- /* GPCMD_SEND_KEY from multi media commands */
- size = (cdb[8] << 8) + cdb[9];
}
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case MODE_SELECT:
- size = cdb[4];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
- break;
- case MODE_SELECT_10:
- size = (cdb[7] << 8) + cdb[8];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
- break;
- case MODE_SENSE:
- size = cdb[4];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case MODE_SENSE_10:
- case GPCMD_READ_BUFFER_CAPACITY:
- case GPCMD_SEND_OPC:
- case LOG_SELECT:
- case LOG_SENSE:
- size = (cdb[7] << 8) + cdb[8];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case READ_BLOCK_LIMITS:
- size = READ_BLOCK_LEN;
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case GPCMD_GET_CONFIGURATION:
- case GPCMD_READ_FORMAT_CAPACITIES:
- case GPCMD_READ_DISC_INFO:
- case GPCMD_READ_TRACK_RZONE_INFO:
- size = (cdb[7] << 8) + cdb[8];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
- break;
- case PERSISTENT_RESERVE_IN:
- case PERSISTENT_RESERVE_OUT:
- cmd->transport_emulate_cdb =
- (T10_RES(su_dev)->res_type ==
- SPC3_PERSISTENT_RESERVATIONS) ?
- &core_scsi3_emulate_pr : NULL;
- size = (cdb[7] << 8) + cdb[8];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case GPCMD_MECHANISM_STATUS:
- case GPCMD_READ_DVD_STRUCTURE:
- size = (cdb[8] << 8) + cdb[9];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
- break;
- case READ_POSITION:
- size = READ_POSITION_LEN;
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case 0xa4:
- if (TRANSPORT(dev)->get_device_type(dev) != TYPE_ROM) {
- /* MAINTENANCE_OUT from SCC-2
- *
- * Check for emulated MO_SET_TARGET_PGS.
- */
- if (cdb[1] == MO_SET_TARGET_PGS) {
- cmd->transport_emulate_cdb =
- (T10_ALUA(su_dev)->alua_type ==
- SPC3_ALUA_EMULATED) ?
- &core_emulate_set_target_port_groups :
- NULL;
- }
-
- size = (cdb[6] << 24) | (cdb[7] << 16) |
- (cdb[8] << 8) | cdb[9];
- } else {
- /* GPCMD_REPORT_KEY from multi media commands */
- size = (cdb[8] << 8) + cdb[9];
- }
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case INQUIRY:
- size = (cdb[3] << 8) + cdb[4];
- /*
- * Do implict HEAD_OF_QUEUE processing for INQUIRY.
- * See spc4r17 section 5.3
- */
- if (SE_DEV(cmd)->dev_task_attr_type == SAM_TASK_ATTR_EMULATED)
- cmd->sam_task_attr = TASK_ATTR_HOQ;
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case READ_BUFFER:
- size = (cdb[6] << 16) + (cdb[7] << 8) + cdb[8];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case READ_CAPACITY:
- size = READ_CAP_LEN;
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case READ_MEDIA_SERIAL_NUMBER:
- case SECURITY_PROTOCOL_IN:
- case SECURITY_PROTOCOL_OUT:
- size = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case SERVICE_ACTION_IN:
- case ACCESS_CONTROL_IN:
- case ACCESS_CONTROL_OUT:
- case EXTENDED_COPY:
- case READ_ATTRIBUTE:
- case RECEIVE_COPY_RESULTS:
- case WRITE_ATTRIBUTE:
- size = (cdb[10] << 24) | (cdb[11] << 16) |
- (cdb[12] << 8) | cdb[13];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case RECEIVE_DIAGNOSTIC:
- case SEND_DIAGNOSTIC:
- size = (cdb[3] << 8) | cdb[4];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
-/* #warning FIXME: Figure out correct GPCMD_READ_CD blocksize. */
-#if 0
- case GPCMD_READ_CD:
- sectors = (cdb[6] << 16) + (cdb[7] << 8) + cdb[8];
- size = (2336 * sectors);
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
-#endif
- case READ_TOC:
- size = cdb[8];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case REQUEST_SENSE:
- size = cdb[4];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case READ_ELEMENT_STATUS:
- size = 65536 * cdb[7] + 256 * cdb[8] + cdb[9];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case WRITE_BUFFER:
- size = (cdb[6] << 16) + (cdb[7] << 8) + cdb[8];
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case RESERVE:
- case RESERVE_10:
- /*
- * The SPC-2 RESERVE does not contain a size in the SCSI CDB.
- * Assume the passthrough or $FABRIC_MOD will tell us about it.
- */
- if (cdb[0] == RESERVE_10)
- size = (cdb[7] << 8) | cdb[8];
- else
- size = cmd->data_length;
-
- /*
- * Setup the legacy emulated handler for SPC-2 and
- * >= SPC-3 compatible reservation handling (CRH=1)
- * Otherwise, we assume the underlying SCSI logic is
- * is running in SPC_PASSTHROUGH, and wants reservations
- * emulation disabled.
- */
- cmd->transport_emulate_cdb =
- (T10_RES(su_dev)->res_type !=
- SPC_PASSTHROUGH) ?
- &core_scsi2_emulate_crh : NULL;
- cmd->se_cmd_flags |= SCF_SCSI_NON_DATA_CDB;
- break;
- case RELEASE:
- case RELEASE_10:
- /*
- * The SPC-2 RELEASE does not contain a size in the SCSI CDB.
- * Assume the passthrough or $FABRIC_MOD will tell us about it.
- */
- if (cdb[0] == RELEASE_10)
- size = (cdb[7] << 8) | cdb[8];
- else
- size = cmd->data_length;
-
- cmd->transport_emulate_cdb =
- (T10_RES(su_dev)->res_type !=
- SPC_PASSTHROUGH) ?
- &core_scsi2_emulate_crh : NULL;
- cmd->se_cmd_flags |= SCF_SCSI_NON_DATA_CDB;
- break;
- case SYNCHRONIZE_CACHE:
- case 0x91: /* SYNCHRONIZE_CACHE_16: */
- /*
- * Extract LBA and range to be flushed for emulated SYNCHRONIZE_CACHE
- */
- if (cdb[0] == SYNCHRONIZE_CACHE) {
- sectors = transport_get_sectors_10(cdb, cmd, &sector_ret);
- T_TASK(cmd)->t_task_lba = transport_lba_32(cdb);
- } else {
- sectors = transport_get_sectors_16(cdb, cmd, &sector_ret);
- T_TASK(cmd)->t_task_lba = transport_lba_64(cdb);
- }
- if (sector_ret)
- goto out_unsupported_cdb;
-
- size = transport_get_size(sectors, cdb, cmd);
- cmd->se_cmd_flags |= SCF_SCSI_NON_DATA_CDB;
-
- /*
- * For TCM/pSCSI passthrough, skip cmd->transport_emulate_cdb()
- */
- if (TRANSPORT(dev)->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)
- break;
- /*
- * Set SCF_EMULATE_CDB_ASYNC to ensure asynchronous operation
- * for SYNCHRONIZE_CACHE* Immed=1 case in __transport_execute_tasks()
- */
- cmd->se_cmd_flags |= SCF_EMULATE_CDB_ASYNC;
- /*
- * Check to ensure that LBA + Range does not exceed past end of
- * device.
- */
- if (transport_get_sectors(cmd) < 0)
- goto out_invalid_cdb_field;
- break;
- case UNMAP:
- size = get_unaligned_be16(&cdb[7]);
- passthrough = (TRANSPORT(dev)->transport_type ==
- TRANSPORT_PLUGIN_PHBA_PDEV);
- /*
- * Determine if the received UNMAP used to for direct passthrough
- * into Linux/SCSI with struct request via TCM/pSCSI or we are
- * signaling the use of internal transport_generic_unmap() emulation
- * for UNMAP -> Linux/BLOCK disbard with TCM/IBLOCK and TCM/FILEIO
- * subsystem plugin backstores.
- */
- if (!(passthrough))
- cmd->se_cmd_flags |= SCF_EMULATE_SYNC_UNMAP;
-
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
- break;
- case WRITE_SAME_16:
- sectors = transport_get_sectors_16(cdb, cmd, &sector_ret);
- if (sector_ret)
- goto out_unsupported_cdb;
- size = transport_get_size(sectors, cdb, cmd);
- T_TASK(cmd)->t_task_lba = get_unaligned_be16(&cdb[2]);
- passthrough = (TRANSPORT(dev)->transport_type ==
- TRANSPORT_PLUGIN_PHBA_PDEV);
- /*
- * Determine if the received WRITE_SAME_16 is used to for direct
- * passthrough into Linux/SCSI with struct request via TCM/pSCSI
- * or we are signaling the use of internal WRITE_SAME + UNMAP=1
- * emulation for -> Linux/BLOCK disbard with TCM/IBLOCK and
- * TCM/FILEIO subsystem plugin backstores.
- */
- if (!(passthrough)) {
- if ((cdb[1] & 0x04) || (cdb[1] & 0x02)) {
- printk(KERN_ERR "WRITE_SAME PBDATA and LBDATA"
- " bits not supported for Block Discard"
- " Emulation\n");
- goto out_invalid_cdb_field;
- }
- /*
- * Currently for the emulated case we only accept
- * tpws with the UNMAP=1 bit set.
- */
- if (!(cdb[1] & 0x08)) {
- printk(KERN_ERR "WRITE_SAME w/o UNMAP bit not "
- " supported for Block Discard Emulation\n");
- goto out_invalid_cdb_field;
- }
- }
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
- break;
- case ALLOW_MEDIUM_REMOVAL:
- case GPCMD_CLOSE_TRACK:
- case ERASE:
- case INITIALIZE_ELEMENT_STATUS:
- case GPCMD_LOAD_UNLOAD:
- case REZERO_UNIT:
- case SEEK_10:
- case GPCMD_SET_SPEED:
- case SPACE:
- case START_STOP:
- case TEST_UNIT_READY:
- case VERIFY:
- case WRITE_FILEMARKS:
- case MOVE_MEDIUM:
- cmd->se_cmd_flags |= SCF_SCSI_NON_DATA_CDB;
- break;
- case REPORT_LUNS:
- cmd->transport_emulate_cdb =
- &transport_core_report_lun_response;
- size = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9];
- /*
- * Do implict HEAD_OF_QUEUE processing for REPORT_LUNS
- * See spc4r17 section 5.3
- */
- if (SE_DEV(cmd)->dev_task_attr_type == SAM_TASK_ATTR_EMULATED)
- cmd->sam_task_attr = TASK_ATTR_HOQ;
- cmd->se_cmd_flags |= SCF_SCSI_CONTROL_NONSG_IO_CDB;
+ /* Fall through for DMA_TO_DEVICE */
+ case DMA_NONE:
+ trace_target_cmd_complete(cmd);
+ ret = cmd->se_tfo->queue_status(cmd);
break;
default:
- printk(KERN_WARNING "TARGET_CORE[%s]: Unsupported SCSI Opcode"
- " 0x%02x, sending CHECK_CONDITION.\n",
- CMD_TFO(cmd)->get_fabric_name(), cdb[0]);
- cmd->transport_wait_for_tasks = &transport_nop_wait_for_tasks;
- goto out_unsupported_cdb;
+ break;
}
- if (size != cmd->data_length) {
- printk(KERN_WARNING "TARGET_CORE[%s]: Expected Transfer Length:"
- " %u does not match SCSI CDB Length: %u for SAM Opcode:"
- " 0x%02x\n", CMD_TFO(cmd)->get_fabric_name(),
- cmd->data_length, size, cdb[0]);
-
- cmd->cmd_spdtl = size;
-
- if (cmd->data_direction == DMA_TO_DEVICE) {
- printk(KERN_ERR "Rejecting underflow/overflow"
- " WRITE data\n");
- goto out_invalid_cdb_field;
- }
- /*
- * Reject READ_* or WRITE_* with overflow/underflow for
- * type SCF_SCSI_DATA_SG_IO_CDB.
- */
- if (!(ret) && (DEV_ATTRIB(dev)->block_size != 512)) {
- printk(KERN_ERR "Failing OVERFLOW/UNDERFLOW for LBA op"
- " CDB on non 512-byte sector setup subsystem"
- " plugin: %s\n", TRANSPORT(dev)->name);
- /* Returns CHECK_CONDITION + INVALID_CDB_FIELD */
- goto out_invalid_cdb_field;
- }
-
- if (size > cmd->data_length) {
- cmd->se_cmd_flags |= SCF_OVERFLOW_BIT;
- cmd->residual_count = (size - cmd->data_length);
- } else {
- cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;
- cmd->residual_count = (cmd->data_length - size);
- }
- cmd->data_length = size;
+out:
+ if (ret < 0) {
+ transport_handle_queue_full(cmd, cmd->se_dev);
+ return;
}
-
- transport_set_supported_SAM_opcode(cmd);
- return ret;
-
-out_unsupported_cdb:
- cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- cmd->scsi_sense_reason = TCM_UNSUPPORTED_SCSI_OPCODE;
- return -2;
-out_invalid_cdb_field:
- cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
- return -2;
+ transport_lun_remove_cmd(cmd);
+ transport_cmd_check_stop_to_fabric(cmd);
}
-static inline void transport_release_tasks(struct se_cmd *);
-
-/*
- * This function will copy a contiguous *src buffer into a destination
- * struct scatterlist array.
- */
-static void transport_memcpy_write_contig(
+static void transport_handle_queue_full(
struct se_cmd *cmd,
- struct scatterlist *sg_d,
- unsigned char *src)
+ struct se_device *dev)
{
- u32 i = 0, length = 0, total_length = cmd->data_length;
- void *dst;
-
- while (total_length) {
- length = sg_d[i].length;
-
- if (length > total_length)
- length = total_length;
-
- dst = sg_virt(&sg_d[i]);
-
- memcpy(dst, src, length);
-
- if (!(total_length -= length))
- return;
+ spin_lock_irq(&dev->qf_cmd_lock);
+ list_add_tail(&cmd->se_qf_node, &cmd->se_dev->qf_cmd_list);
+ atomic_inc(&dev->dev_qf_count);
+ smp_mb__after_atomic();
+ spin_unlock_irq(&cmd->se_dev->qf_cmd_lock);
- src += length;
- i++;
- }
+ schedule_work(&cmd->se_dev->qf_work_queue);
}
-/*
- * This function will copy a struct scatterlist array *sg_s into a destination
- * contiguous *dst buffer.
- */
-static void transport_memcpy_read_contig(
- struct se_cmd *cmd,
- unsigned char *dst,
- struct scatterlist *sg_s)
+static bool target_check_read_strip(struct se_cmd *cmd)
{
- u32 i = 0, length = 0, total_length = cmd->data_length;
- void *src;
-
- while (total_length) {
- length = sg_s[i].length;
-
- if (length > total_length)
- length = total_length;
+ sense_reason_t rc;
- src = sg_virt(&sg_s[i]);
-
- memcpy(dst, src, length);
-
- if (!(total_length -= length))
- return;
-
- dst += length;
- i++;
+ if (!(cmd->se_sess->sup_prot_ops & TARGET_PROT_DIN_STRIP)) {
+ rc = sbc_dif_read_strip(cmd);
+ if (rc) {
+ cmd->pi_err = rc;
+ return true;
+ }
}
-}
-
-static void transport_memcpy_se_mem_read_contig(
- struct se_cmd *cmd,
- unsigned char *dst,
- struct list_head *se_mem_list)
-{
- struct se_mem *se_mem;
- void *src;
- u32 length = 0, total_length = cmd->data_length;
-
- list_for_each_entry(se_mem, se_mem_list, se_list) {
- length = se_mem->se_len;
-
- if (length > total_length)
- length = total_length;
-
- src = page_address(se_mem->se_page) + se_mem->se_off;
- memcpy(dst, src, length);
-
- if (!(total_length -= length))
- return;
-
- dst += length;
- }
+ return false;
}
-/*
- * Called from transport_generic_complete_ok() and
- * transport_generic_request_failure() to determine which dormant/delayed
- * and ordered cmds need to have their tasks added to the execution queue.
- */
-static void transport_complete_task_attr(struct se_cmd *cmd)
+static void target_complete_ok_work(struct work_struct *work)
{
- struct se_device *dev = SE_DEV(cmd);
- struct se_cmd *cmd_p, *cmd_tmp;
- int new_active_tasks = 0;
-
- if (cmd->sam_task_attr == TASK_ATTR_SIMPLE) {
- atomic_dec(&dev->simple_cmds);
- smp_mb__after_atomic_dec();
- dev->dev_cur_ordered_id++;
- DEBUG_STA("Incremented dev->dev_cur_ordered_id: %u for"
- " SIMPLE: %u\n", dev->dev_cur_ordered_id,
- cmd->se_ordered_id);
- } else if (cmd->sam_task_attr == TASK_ATTR_HOQ) {
- atomic_dec(&dev->dev_hoq_count);
- smp_mb__after_atomic_dec();
- dev->dev_cur_ordered_id++;
- DEBUG_STA("Incremented dev_cur_ordered_id: %u for"
- " HEAD_OF_QUEUE: %u\n", dev->dev_cur_ordered_id,
- cmd->se_ordered_id);
- } else if (cmd->sam_task_attr == TASK_ATTR_ORDERED) {
- spin_lock(&dev->ordered_cmd_lock);
- list_del(&cmd->se_ordered_list);
- atomic_dec(&dev->dev_ordered_sync);
- smp_mb__after_atomic_dec();
- spin_unlock(&dev->ordered_cmd_lock);
+ struct se_cmd *cmd = container_of(work, struct se_cmd, work);
+ int ret;
- dev->dev_cur_ordered_id++;
- DEBUG_STA("Incremented dev_cur_ordered_id: %u for ORDERED:"
- " %u\n", dev->dev_cur_ordered_id, cmd->se_ordered_id);
- }
/*
- * Process all commands up to the last received
- * ORDERED task attribute which requires another blocking
- * boundary
+ * Check if we need to move delayed/dormant tasks from cmds on the
+ * delayed execution list after a HEAD_OF_QUEUE or ORDERED Task
+ * Attribute.
*/
- spin_lock(&dev->delayed_cmd_lock);
- list_for_each_entry_safe(cmd_p, cmd_tmp,
- &dev->delayed_cmd_list, se_delayed_list) {
-
- list_del(&cmd_p->se_delayed_list);
- spin_unlock(&dev->delayed_cmd_lock);
+ transport_complete_task_attr(cmd);
- DEBUG_STA("Calling add_tasks() for"
- " cmd_p: 0x%02x Task Attr: 0x%02x"
- " Dormant -> Active, se_ordered_id: %u\n",
- T_TASK(cmd_p)->t_task_cdb[0],
- cmd_p->sam_task_attr, cmd_p->se_ordered_id);
-
- transport_add_tasks_from_cmd(cmd_p);
- new_active_tasks++;
-
- spin_lock(&dev->delayed_cmd_lock);
- if (cmd_p->sam_task_attr == TASK_ATTR_ORDERED)
- break;
- }
- spin_unlock(&dev->delayed_cmd_lock);
/*
- * If new tasks have become active, wake up the transport thread
- * to do the processing of the Active tasks.
+ * Check to schedule QUEUE_FULL work, or execute an existing
+ * cmd->transport_qf_callback()
*/
- if (new_active_tasks != 0)
- wake_up_interruptible(&dev->dev_queue_obj->thread_wq);
-}
+ if (atomic_read(&cmd->se_dev->dev_qf_count) != 0)
+ schedule_work(&cmd->se_dev->qf_work_queue);
-static void transport_generic_complete_ok(struct se_cmd *cmd)
-{
- int reason = 0;
- /*
- * Check if we need to move delayed/dormant tasks from cmds on the
- * delayed execution list after a HEAD_OF_QUEUE or ORDERED Task
- * Attribute.
- */
- if (SE_DEV(cmd)->dev_task_attr_type == SAM_TASK_ATTR_EMULATED)
- transport_complete_task_attr(cmd);
/*
- * Check if we need to retrieve a sense buffer from
+ * Check if we need to send a sense buffer from
* the struct se_cmd in question.
*/
if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) {
- if (transport_get_sense_data(cmd) < 0)
- reason = TCM_NON_EXISTENT_LUN;
+ WARN_ON(!cmd->scsi_status);
+ ret = transport_send_check_condition_and_sense(
+ cmd, 0, 1);
+ if (ret == -EAGAIN || ret == -ENOMEM)
+ goto queue_full;
+
+ transport_lun_remove_cmd(cmd);
+ transport_cmd_check_stop_to_fabric(cmd);
+ return;
+ }
+ /*
+ * Check for a callback, used by amongst other things
+ * XDWRITE_READ_10 and COMPARE_AND_WRITE emulation.
+ */
+ if (cmd->transport_complete_callback) {
+ sense_reason_t rc;
+
+ rc = cmd->transport_complete_callback(cmd);
+ if (!rc && !(cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE_POST)) {
+ return;
+ } else if (rc) {
+ ret = transport_send_check_condition_and_sense(cmd,
+ rc, 0);
+ if (ret == -EAGAIN || ret == -ENOMEM)
+ goto queue_full;
- /*
- * Only set when an struct se_task->task_scsi_status returned
- * a non GOOD status.
- */
- if (cmd->scsi_status) {
- transport_send_check_condition_and_sense(
- cmd, reason, 1);
transport_lun_remove_cmd(cmd);
transport_cmd_check_stop_to_fabric(cmd);
return;
}
}
- /*
- * Check for a callback, used by amoungst other things
- * XDWRITE_READ_10 emulation.
- */
- if (cmd->transport_complete_callback)
- cmd->transport_complete_callback(cmd);
switch (cmd->data_direction) {
case DMA_FROM_DEVICE:
spin_lock(&cmd->se_lun->lun_sep_lock);
- if (SE_LUN(cmd)->lun_sep) {
- SE_LUN(cmd)->lun_sep->sep_stats.tx_data_octets +=
+ if (cmd->se_lun->lun_sep) {
+ cmd->se_lun->lun_sep->sep_stats.tx_data_octets +=
cmd->data_length;
}
spin_unlock(&cmd->se_lun->lun_sep_lock);
/*
- * If enabled by TCM fabirc module pre-registered SGL
- * memory, perform the memcpy() from the TCM internal
- * contigious buffer back to the original SGL.
+ * Perform READ_STRIP of PI using software emulation when
+ * backend had PI enabled, if the transport will not be
+ * performing hardware READ_STRIP offload.
*/
- if (cmd->se_cmd_flags & SCF_PASSTHROUGH_CONTIG_TO_SG)
- transport_memcpy_write_contig(cmd,
- T_TASK(cmd)->t_task_pt_sgl,
- T_TASK(cmd)->t_task_buf);
+ if (cmd->prot_op == TARGET_PROT_DIN_STRIP &&
+ target_check_read_strip(cmd)) {
+ ret = transport_send_check_condition_and_sense(cmd,
+ cmd->pi_err, 0);
+ if (ret == -EAGAIN || ret == -ENOMEM)
+ goto queue_full;
- CMD_TFO(cmd)->queue_data_in(cmd);
+ transport_lun_remove_cmd(cmd);
+ transport_cmd_check_stop_to_fabric(cmd);
+ return;
+ }
+
+ trace_target_cmd_complete(cmd);
+ ret = cmd->se_tfo->queue_data_in(cmd);
+ if (ret == -EAGAIN || ret == -ENOMEM)
+ goto queue_full;
break;
case DMA_TO_DEVICE:
spin_lock(&cmd->se_lun->lun_sep_lock);
- if (SE_LUN(cmd)->lun_sep) {
- SE_LUN(cmd)->lun_sep->sep_stats.rx_data_octets +=
+ if (cmd->se_lun->lun_sep) {
+ cmd->se_lun->lun_sep->sep_stats.rx_data_octets +=
cmd->data_length;
}
spin_unlock(&cmd->se_lun->lun_sep_lock);
/*
* Check if we need to send READ payload for BIDI-COMMAND
*/
- if (T_TASK(cmd)->t_mem_bidi_list != NULL) {
+ if (cmd->se_cmd_flags & SCF_BIDI) {
spin_lock(&cmd->se_lun->lun_sep_lock);
- if (SE_LUN(cmd)->lun_sep) {
- SE_LUN(cmd)->lun_sep->sep_stats.tx_data_octets +=
+ if (cmd->se_lun->lun_sep) {
+ cmd->se_lun->lun_sep->sep_stats.tx_data_octets +=
cmd->data_length;
}
spin_unlock(&cmd->se_lun->lun_sep_lock);
- CMD_TFO(cmd)->queue_data_in(cmd);
+ ret = cmd->se_tfo->queue_data_in(cmd);
+ if (ret == -EAGAIN || ret == -ENOMEM)
+ goto queue_full;
break;
}
/* Fall through for DMA_TO_DEVICE */
case DMA_NONE:
- CMD_TFO(cmd)->queue_status(cmd);
+ trace_target_cmd_complete(cmd);
+ ret = cmd->se_tfo->queue_status(cmd);
+ if (ret == -EAGAIN || ret == -ENOMEM)
+ goto queue_full;
break;
default:
break;
@@ -3927,1659 +2059,515 @@ static void transport_generic_complete_ok(struct se_cmd *cmd)
transport_lun_remove_cmd(cmd);
transport_cmd_check_stop_to_fabric(cmd);
+ return;
+
+queue_full:
+ pr_debug("Handling complete_ok QUEUE_FULL: se_cmd: %p,"
+ " data_direction: %d\n", cmd, cmd->data_direction);
+ cmd->t_state = TRANSPORT_COMPLETE_QF_OK;
+ transport_handle_queue_full(cmd, cmd->se_dev);
}
-static void transport_free_dev_tasks(struct se_cmd *cmd)
+static inline void transport_free_sgl(struct scatterlist *sgl, int nents)
{
- struct se_task *task, *task_tmp;
- unsigned long flags;
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- list_for_each_entry_safe(task, task_tmp,
- &T_TASK(cmd)->t_task_list, t_list) {
- if (atomic_read(&task->task_active))
- continue;
-
- kfree(task->task_sg_bidi);
- kfree(task->task_sg);
+ struct scatterlist *sg;
+ int count;
- list_del(&task->t_list);
+ for_each_sg(sgl, sg, nents, count)
+ __free_page(sg_page(sg));
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- if (task->se_dev)
- TRANSPORT(task->se_dev)->free_task(task);
- else
- printk(KERN_ERR "task[%u] - task->se_dev is NULL\n",
- task->task_no);
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- }
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
+ kfree(sgl);
}
-static inline void transport_free_pages(struct se_cmd *cmd)
+static inline void transport_reset_sgl_orig(struct se_cmd *cmd)
{
- struct se_mem *se_mem, *se_mem_tmp;
- int free_page = 1;
-
- if (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC)
- free_page = 0;
- if (cmd->se_dev->transport->do_se_mem_map)
- free_page = 0;
-
- if (T_TASK(cmd)->t_task_buf) {
- kfree(T_TASK(cmd)->t_task_buf);
- T_TASK(cmd)->t_task_buf = NULL;
- return;
- }
-
/*
- * Caller will handle releasing of struct se_mem.
+ * Check for saved t_data_sg that may be used for COMPARE_AND_WRITE
+ * emulation, and free + reset pointers if necessary..
*/
- if (cmd->se_cmd_flags & SCF_CMD_PASSTHROUGH_NOALLOC)
+ if (!cmd->t_data_sg_orig)
return;
- if (!(T_TASK(cmd)->t_tasks_se_num))
- return;
-
- list_for_each_entry_safe(se_mem, se_mem_tmp,
- T_TASK(cmd)->t_mem_list, se_list) {
- /*
- * We only release call __free_page(struct se_mem->se_page) when
- * SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC is NOT in use,
- */
- if (free_page)
- __free_page(se_mem->se_page);
-
- list_del(&se_mem->se_list);
- kmem_cache_free(se_mem_cache, se_mem);
- }
-
- if (T_TASK(cmd)->t_mem_bidi_list && T_TASK(cmd)->t_tasks_se_bidi_num) {
- list_for_each_entry_safe(se_mem, se_mem_tmp,
- T_TASK(cmd)->t_mem_bidi_list, se_list) {
- /*
- * We only release call __free_page(struct se_mem->se_page) when
- * SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC is NOT in use,
- */
- if (free_page)
- __free_page(se_mem->se_page);
-
- list_del(&se_mem->se_list);
- kmem_cache_free(se_mem_cache, se_mem);
- }
- }
-
- kfree(T_TASK(cmd)->t_mem_bidi_list);
- T_TASK(cmd)->t_mem_bidi_list = NULL;
- kfree(T_TASK(cmd)->t_mem_list);
- T_TASK(cmd)->t_mem_list = NULL;
- T_TASK(cmd)->t_tasks_se_num = 0;
+ kfree(cmd->t_data_sg);
+ cmd->t_data_sg = cmd->t_data_sg_orig;
+ cmd->t_data_sg_orig = NULL;
+ cmd->t_data_nents = cmd->t_data_nents_orig;
+ cmd->t_data_nents_orig = 0;
}
-static inline void transport_release_tasks(struct se_cmd *cmd)
-{
- transport_free_dev_tasks(cmd);
-}
-
-static inline int transport_dec_and_check(struct se_cmd *cmd)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- if (atomic_read(&T_TASK(cmd)->t_fe_count)) {
- if (!(atomic_dec_and_test(&T_TASK(cmd)->t_fe_count))) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock,
- flags);
- return 1;
- }
- }
-
- if (atomic_read(&T_TASK(cmd)->t_se_count)) {
- if (!(atomic_dec_and_test(&T_TASK(cmd)->t_se_count))) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock,
- flags);
- return 1;
- }
- }
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-
- return 0;
-}
-
-static void transport_release_fe_cmd(struct se_cmd *cmd)
+static inline void transport_free_pages(struct se_cmd *cmd)
{
- unsigned long flags;
-
- if (transport_dec_and_check(cmd))
+ if (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) {
+ transport_reset_sgl_orig(cmd);
return;
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- if (!(atomic_read(&T_TASK(cmd)->transport_dev_active))) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- goto free_pages;
- }
- atomic_set(&T_TASK(cmd)->transport_dev_active, 0);
- transport_all_task_dev_remove_state(cmd);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
-
- transport_release_tasks(cmd);
-free_pages:
- transport_free_pages(cmd);
- transport_free_se_cmd(cmd);
- CMD_TFO(cmd)->release_cmd_direct(cmd);
-}
-
-static int transport_generic_remove(
- struct se_cmd *cmd,
- int release_to_pool,
- int session_reinstatement)
-{
- unsigned long flags;
-
- if (!(T_TASK(cmd)))
- goto release_cmd;
-
- if (transport_dec_and_check(cmd)) {
- if (session_reinstatement) {
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- transport_all_task_dev_remove_state(cmd);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock,
- flags);
- }
- return 1;
- }
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- if (!(atomic_read(&T_TASK(cmd)->transport_dev_active))) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- goto free_pages;
}
- atomic_set(&T_TASK(cmd)->transport_dev_active, 0);
- transport_all_task_dev_remove_state(cmd);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
+ transport_reset_sgl_orig(cmd);
- transport_release_tasks(cmd);
-free_pages:
- transport_free_pages(cmd);
+ transport_free_sgl(cmd->t_data_sg, cmd->t_data_nents);
+ cmd->t_data_sg = NULL;
+ cmd->t_data_nents = 0;
-release_cmd:
- if (release_to_pool) {
- transport_release_cmd_to_pool(cmd);
- } else {
- transport_free_se_cmd(cmd);
- CMD_TFO(cmd)->release_cmd_direct(cmd);
- }
+ transport_free_sgl(cmd->t_bidi_data_sg, cmd->t_bidi_data_nents);
+ cmd->t_bidi_data_sg = NULL;
+ cmd->t_bidi_data_nents = 0;
- return 0;
+ transport_free_sgl(cmd->t_prot_sg, cmd->t_prot_nents);
+ cmd->t_prot_sg = NULL;
+ cmd->t_prot_nents = 0;
}
-/*
- * transport_generic_map_mem_to_cmd - Perform SGL -> struct se_mem map
- * @cmd: Associated se_cmd descriptor
- * @mem: SGL style memory for TCM WRITE / READ
- * @sg_mem_num: Number of SGL elements
- * @mem_bidi_in: SGL style memory for TCM BIDI READ
- * @sg_mem_bidi_num: Number of BIDI READ SGL elements
+/**
+ * transport_release_cmd - free a command
+ * @cmd: command to free
*
- * Return: nonzero return cmd was rejected for -ENOMEM or inproper usage
- * of parameters.
+ * This routine unconditionally frees a command, and reference counting
+ * or list removal must be done in the caller.
*/
-int transport_generic_map_mem_to_cmd(
- struct se_cmd *cmd,
- struct scatterlist *mem,
- u32 sg_mem_num,
- struct scatterlist *mem_bidi_in,
- u32 sg_mem_bidi_num)
+static int transport_release_cmd(struct se_cmd *cmd)
{
- u32 se_mem_cnt_out = 0;
- int ret;
+ BUG_ON(!cmd->se_tfo);
- if (!(mem) || !(sg_mem_num))
- return 0;
- /*
- * Passed *mem will contain a list_head containing preformatted
- * struct se_mem elements...
- */
- if (!(cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM)) {
- if ((mem_bidi_in) || (sg_mem_bidi_num)) {
- printk(KERN_ERR "SCF_CMD_PASSTHROUGH_NOALLOC not supported"
- " with BIDI-COMMAND\n");
- return -ENOSYS;
- }
-
- T_TASK(cmd)->t_mem_list = (struct list_head *)mem;
- T_TASK(cmd)->t_tasks_se_num = sg_mem_num;
- cmd->se_cmd_flags |= SCF_CMD_PASSTHROUGH_NOALLOC;
- return 0;
- }
+ if (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
+ core_tmr_release_req(cmd->se_tmr_req);
+ if (cmd->t_task_cdb != cmd->__t_task_cdb)
+ kfree(cmd->t_task_cdb);
/*
- * Otherwise, assume the caller is passing a struct scatterlist
- * array from include/linux/scatterlist.h
+ * If this cmd has been setup with target_get_sess_cmd(), drop
+ * the kref and call ->release_cmd() in kref callback.
*/
- if ((cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) ||
- (cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB)) {
- /*
- * For CDB using TCM struct se_mem linked list scatterlist memory
- * processed into a TCM struct se_subsystem_dev, we do the mapping
- * from the passed physical memory to struct se_mem->se_page here.
- */
- T_TASK(cmd)->t_mem_list = transport_init_se_mem_list();
- if (!(T_TASK(cmd)->t_mem_list))
- return -ENOMEM;
-
- ret = transport_map_sg_to_mem(cmd,
- T_TASK(cmd)->t_mem_list, mem, &se_mem_cnt_out);
- if (ret < 0)
- return -ENOMEM;
-
- T_TASK(cmd)->t_tasks_se_num = se_mem_cnt_out;
- /*
- * Setup BIDI READ list of struct se_mem elements
- */
- if ((mem_bidi_in) && (sg_mem_bidi_num)) {
- T_TASK(cmd)->t_mem_bidi_list = transport_init_se_mem_list();
- if (!(T_TASK(cmd)->t_mem_bidi_list)) {
- kfree(T_TASK(cmd)->t_mem_list);
- return -ENOMEM;
- }
- se_mem_cnt_out = 0;
-
- ret = transport_map_sg_to_mem(cmd,
- T_TASK(cmd)->t_mem_bidi_list, mem_bidi_in,
- &se_mem_cnt_out);
- if (ret < 0) {
- kfree(T_TASK(cmd)->t_mem_list);
- return -ENOMEM;
- }
-
- T_TASK(cmd)->t_tasks_se_bidi_num = se_mem_cnt_out;
- }
- cmd->se_cmd_flags |= SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
-
- } else if (cmd->se_cmd_flags & SCF_SCSI_CONTROL_NONSG_IO_CDB) {
- if (mem_bidi_in || sg_mem_bidi_num) {
- printk(KERN_ERR "BIDI-Commands not supported using "
- "SCF_SCSI_CONTROL_NONSG_IO_CDB\n");
- return -ENOSYS;
- }
- /*
- * For incoming CDBs using a contiguous buffer internall with TCM,
- * save the passed struct scatterlist memory. After TCM storage object
- * processing has completed for this struct se_cmd, TCM core will call
- * transport_memcpy_[write,read]_contig() as necessary from
- * transport_generic_complete_ok() and transport_write_pending() in order
- * to copy the TCM buffer to/from the original passed *mem in SGL ->
- * struct scatterlist format.
- */
- cmd->se_cmd_flags |= SCF_PASSTHROUGH_CONTIG_TO_SG;
- T_TASK(cmd)->t_task_pt_sgl = mem;
- }
-
- return 0;
+ return target_put_sess_cmd(cmd->se_sess, cmd);
}
-EXPORT_SYMBOL(transport_generic_map_mem_to_cmd);
-
-static inline long long transport_dev_end_lba(struct se_device *dev)
+/**
+ * transport_put_cmd - release a reference to a command
+ * @cmd: command to release
+ *
+ * This routine releases our reference to the command and frees it if possible.
+ */
+static int transport_put_cmd(struct se_cmd *cmd)
{
- return dev->transport->get_blocks(dev) + 1;
+ transport_free_pages(cmd);
+ return transport_release_cmd(cmd);
}
-static int transport_get_sectors(struct se_cmd *cmd)
+void *transport_kmap_data_sg(struct se_cmd *cmd)
{
- struct se_device *dev = SE_DEV(cmd);
-
- T_TASK(cmd)->t_tasks_sectors =
- (cmd->data_length / DEV_ATTRIB(dev)->block_size);
- if (!(T_TASK(cmd)->t_tasks_sectors))
- T_TASK(cmd)->t_tasks_sectors = 1;
-
- if (TRANSPORT(dev)->get_device_type(dev) != TYPE_DISK)
- return 0;
-
- if ((T_TASK(cmd)->t_task_lba + T_TASK(cmd)->t_tasks_sectors) >
- transport_dev_end_lba(dev)) {
- printk(KERN_ERR "LBA: %llu Sectors: %u exceeds"
- " transport_dev_end_lba(): %llu\n",
- T_TASK(cmd)->t_task_lba, T_TASK(cmd)->t_tasks_sectors,
- transport_dev_end_lba(dev));
- cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- cmd->scsi_sense_reason = TCM_SECTOR_COUNT_TOO_MANY;
- return PYX_TRANSPORT_REQ_TOO_MANY_SECTORS;
- }
+ struct scatterlist *sg = cmd->t_data_sg;
+ struct page **pages;
+ int i;
- return 0;
-}
+ /*
+ * We need to take into account a possible offset here for fabrics like
+ * tcm_loop who may be using a contig buffer from the SCSI midlayer for
+ * control CDBs passed as SGLs via transport_generic_map_mem_to_cmd()
+ */
+ if (!cmd->t_data_nents)
+ return NULL;
-static int transport_new_cmd_obj(struct se_cmd *cmd)
-{
- struct se_device *dev = SE_DEV(cmd);
- u32 task_cdbs = 0, rc;
+ BUG_ON(!sg);
+ if (cmd->t_data_nents == 1)
+ return kmap(sg_page(sg)) + sg->offset;
- if (!(cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB)) {
- task_cdbs++;
- T_TASK(cmd)->t_task_cdbs++;
- } else {
- int set_counts = 1;
+ /* >1 page. use vmap */
+ pages = kmalloc(sizeof(*pages) * cmd->t_data_nents, GFP_KERNEL);
+ if (!pages)
+ return NULL;
- /*
- * Setup any BIDI READ tasks and memory from
- * T_TASK(cmd)->t_mem_bidi_list so the READ struct se_tasks
- * are queued first for the non pSCSI passthrough case.
- */
- if ((T_TASK(cmd)->t_mem_bidi_list != NULL) &&
- (TRANSPORT(dev)->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV)) {
- rc = transport_generic_get_cdb_count(cmd,
- T_TASK(cmd)->t_task_lba,
- T_TASK(cmd)->t_tasks_sectors,
- DMA_FROM_DEVICE, T_TASK(cmd)->t_mem_bidi_list,
- set_counts);
- if (!(rc)) {
- cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- cmd->scsi_sense_reason =
- TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
- return PYX_TRANSPORT_LU_COMM_FAILURE;
- }
- set_counts = 0;
- }
- /*
- * Setup the tasks and memory from T_TASK(cmd)->t_mem_list
- * Note for BIDI transfers this will contain the WRITE payload
- */
- task_cdbs = transport_generic_get_cdb_count(cmd,
- T_TASK(cmd)->t_task_lba,
- T_TASK(cmd)->t_tasks_sectors,
- cmd->data_direction, T_TASK(cmd)->t_mem_list,
- set_counts);
- if (!(task_cdbs)) {
- cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
- cmd->scsi_sense_reason =
- TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
- return PYX_TRANSPORT_LU_COMM_FAILURE;
- }
- T_TASK(cmd)->t_task_cdbs += task_cdbs;
-
-#if 0
- printk(KERN_INFO "data_length: %u, LBA: %llu t_tasks_sectors:"
- " %u, t_task_cdbs: %u\n", obj_ptr, cmd->data_length,
- T_TASK(cmd)->t_task_lba, T_TASK(cmd)->t_tasks_sectors,
- T_TASK(cmd)->t_task_cdbs);
-#endif
+ /* convert sg[] to pages[] */
+ for_each_sg(cmd->t_data_sg, sg, cmd->t_data_nents, i) {
+ pages[i] = sg_page(sg);
}
- atomic_set(&T_TASK(cmd)->t_task_cdbs_left, task_cdbs);
- atomic_set(&T_TASK(cmd)->t_task_cdbs_ex_left, task_cdbs);
- atomic_set(&T_TASK(cmd)->t_task_cdbs_timeout_left, task_cdbs);
- return 0;
+ cmd->t_data_vmap = vmap(pages, cmd->t_data_nents, VM_MAP, PAGE_KERNEL);
+ kfree(pages);
+ if (!cmd->t_data_vmap)
+ return NULL;
+
+ return cmd->t_data_vmap + cmd->t_data_sg[0].offset;
}
+EXPORT_SYMBOL(transport_kmap_data_sg);
-static struct list_head *transport_init_se_mem_list(void)
+void transport_kunmap_data_sg(struct se_cmd *cmd)
{
- struct list_head *se_mem_list;
-
- se_mem_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
- if (!(se_mem_list)) {
- printk(KERN_ERR "Unable to allocate memory for se_mem_list\n");
- return NULL;
+ if (!cmd->t_data_nents) {
+ return;
+ } else if (cmd->t_data_nents == 1) {
+ kunmap(sg_page(cmd->t_data_sg));
+ return;
}
- INIT_LIST_HEAD(se_mem_list);
- return se_mem_list;
+ vunmap(cmd->t_data_vmap);
+ cmd->t_data_vmap = NULL;
}
+EXPORT_SYMBOL(transport_kunmap_data_sg);
-static int
-transport_generic_get_mem(struct se_cmd *cmd, u32 length, u32 dma_size)
+int
+target_alloc_sgl(struct scatterlist **sgl, unsigned int *nents, u32 length,
+ bool zero_page)
{
- unsigned char *buf;
- struct se_mem *se_mem;
-
- T_TASK(cmd)->t_mem_list = transport_init_se_mem_list();
- if (!(T_TASK(cmd)->t_mem_list))
+ struct scatterlist *sg;
+ struct page *page;
+ gfp_t zero_flag = (zero_page) ? __GFP_ZERO : 0;
+ unsigned int nent;
+ int i = 0;
+
+ nent = DIV_ROUND_UP(length, PAGE_SIZE);
+ sg = kmalloc(sizeof(struct scatterlist) * nent, GFP_KERNEL);
+ if (!sg)
return -ENOMEM;
- /*
- * If the device uses memory mapping this is enough.
- */
- if (cmd->se_dev->transport->do_se_mem_map)
- return 0;
-
- /*
- * Setup BIDI-COMMAND READ list of struct se_mem elements
- */
- if (T_TASK(cmd)->t_tasks_bidi) {
- T_TASK(cmd)->t_mem_bidi_list = transport_init_se_mem_list();
- if (!(T_TASK(cmd)->t_mem_bidi_list)) {
- kfree(T_TASK(cmd)->t_mem_list);
- return -ENOMEM;
- }
- }
+ sg_init_table(sg, nent);
while (length) {
- se_mem = kmem_cache_zalloc(se_mem_cache, GFP_KERNEL);
- if (!(se_mem)) {
- printk(KERN_ERR "Unable to allocate struct se_mem\n");
+ u32 page_len = min_t(u32, length, PAGE_SIZE);
+ page = alloc_page(GFP_KERNEL | zero_flag);
+ if (!page)
goto out;
- }
- INIT_LIST_HEAD(&se_mem->se_list);
- se_mem->se_len = (length > dma_size) ? dma_size : length;
-/* #warning FIXME Allocate contigous pages for struct se_mem elements */
- se_mem->se_page = (struct page *) alloc_pages(GFP_KERNEL, 0);
- if (!(se_mem->se_page)) {
- printk(KERN_ERR "alloc_pages() failed\n");
- goto out;
- }
-
- buf = kmap_atomic(se_mem->se_page, KM_IRQ0);
- if (!(buf)) {
- printk(KERN_ERR "kmap_atomic() failed\n");
- goto out;
- }
- memset(buf, 0, se_mem->se_len);
- kunmap_atomic(buf, KM_IRQ0);
-
- list_add_tail(&se_mem->se_list, T_TASK(cmd)->t_mem_list);
- T_TASK(cmd)->t_tasks_se_num++;
-
- DEBUG_MEM("Allocated struct se_mem page(%p) Length(%u)"
- " Offset(%u)\n", se_mem->se_page, se_mem->se_len,
- se_mem->se_off);
-
- length -= se_mem->se_len;
+ sg_set_page(&sg[i], page, page_len, 0);
+ length -= page_len;
+ i++;
}
-
- DEBUG_MEM("Allocated total struct se_mem elements(%u)\n",
- T_TASK(cmd)->t_tasks_se_num);
-
+ *sgl = sg;
+ *nents = nent;
return 0;
+
out:
- return -1;
+ while (i > 0) {
+ i--;
+ __free_page(sg_page(&sg[i]));
+ }
+ kfree(sg);
+ return -ENOMEM;
}
-extern u32 transport_calc_sg_num(
- struct se_task *task,
- struct se_mem *in_se_mem,
- u32 task_offset)
+/*
+ * Allocate any required resources to execute the command. For writes we
+ * might not have the payload yet, so notify the fabric via a call to
+ * ->write_pending instead. Otherwise place it on the execution queue.
+ */
+sense_reason_t
+transport_generic_new_cmd(struct se_cmd *cmd)
{
- struct se_cmd *se_cmd = task->task_se_cmd;
- struct se_device *se_dev = SE_DEV(se_cmd);
- struct se_mem *se_mem = in_se_mem;
- struct target_core_fabric_ops *tfo = CMD_TFO(se_cmd);
- u32 sg_length, task_size = task->task_size, task_sg_num_padded;
-
- while (task_size != 0) {
- DEBUG_SC("se_mem->se_page(%p) se_mem->se_len(%u)"
- " se_mem->se_off(%u) task_offset(%u)\n",
- se_mem->se_page, se_mem->se_len,
- se_mem->se_off, task_offset);
-
- if (task_offset == 0) {
- if (task_size >= se_mem->se_len) {
- sg_length = se_mem->se_len;
-
- if (!(list_is_last(&se_mem->se_list,
- T_TASK(se_cmd)->t_mem_list)))
- se_mem = list_entry(se_mem->se_list.next,
- struct se_mem, se_list);
- } else {
- sg_length = task_size;
- task_size -= sg_length;
- goto next;
- }
+ int ret = 0;
- DEBUG_SC("sg_length(%u) task_size(%u)\n",
- sg_length, task_size);
- } else {
- if ((se_mem->se_len - task_offset) > task_size) {
- sg_length = task_size;
- task_size -= sg_length;
- goto next;
- } else {
- sg_length = (se_mem->se_len - task_offset);
-
- if (!(list_is_last(&se_mem->se_list,
- T_TASK(se_cmd)->t_mem_list)))
- se_mem = list_entry(se_mem->se_list.next,
- struct se_mem, se_list);
- }
+ /*
+ * Determine is the TCM fabric module has already allocated physical
+ * memory, and is directly calling transport_generic_map_mem_to_cmd()
+ * beforehand.
+ */
+ if (!(cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) &&
+ cmd->data_length) {
+ bool zero_flag = !(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB);
- DEBUG_SC("sg_length(%u) task_size(%u)\n",
- sg_length, task_size);
+ if ((cmd->se_cmd_flags & SCF_BIDI) ||
+ (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE)) {
+ u32 bidi_length;
- task_offset = 0;
- }
- task_size -= sg_length;
-next:
- DEBUG_SC("task[%u] - Reducing task_size to(%u)\n",
- task->task_no, task_size);
+ if (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE)
+ bidi_length = cmd->t_task_nolb *
+ cmd->se_dev->dev_attrib.block_size;
+ else
+ bidi_length = cmd->data_length;
- task->task_sg_num++;
- }
- /*
- * Check if the fabric module driver is requesting that all
- * struct se_task->task_sg[] be chained together.. If so,
- * then allocate an extra padding SG entry for linking and
- * marking the end of the chained SGL.
- */
- if (tfo->task_sg_chaining) {
- task_sg_num_padded = (task->task_sg_num + 1);
- task->task_padded_sg = 1;
- } else
- task_sg_num_padded = task->task_sg_num;
+ ret = target_alloc_sgl(&cmd->t_bidi_data_sg,
+ &cmd->t_bidi_data_nents,
+ bidi_length, zero_flag);
+ if (ret < 0)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
- task->task_sg = kzalloc(task_sg_num_padded *
- sizeof(struct scatterlist), GFP_KERNEL);
- if (!(task->task_sg)) {
- printk(KERN_ERR "Unable to allocate memory for"
- " task->task_sg\n");
- return 0;
- }
- sg_init_table(&task->task_sg[0], task_sg_num_padded);
- /*
- * Setup task->task_sg_bidi for SCSI READ payload for
- * TCM/pSCSI passthrough if present for BIDI-COMMAND
- */
- if ((T_TASK(se_cmd)->t_mem_bidi_list != NULL) &&
- (TRANSPORT(se_dev)->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV)) {
- task->task_sg_bidi = kzalloc(task_sg_num_padded *
- sizeof(struct scatterlist), GFP_KERNEL);
- if (!(task->task_sg_bidi)) {
- printk(KERN_ERR "Unable to allocate memory for"
- " task->task_sg_bidi\n");
- return 0;
+ if (cmd->prot_op != TARGET_PROT_NORMAL) {
+ ret = target_alloc_sgl(&cmd->t_prot_sg,
+ &cmd->t_prot_nents,
+ cmd->prot_length, true);
+ if (ret < 0)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
- sg_init_table(&task->task_sg_bidi[0], task_sg_num_padded);
+
+ ret = target_alloc_sgl(&cmd->t_data_sg, &cmd->t_data_nents,
+ cmd->data_length, zero_flag);
+ if (ret < 0)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
/*
- * For the chaining case, setup the proper end of SGL for the
- * initial submission struct task into struct se_subsystem_api.
- * This will be cleared later by transport_do_task_sg_chain()
+ * If this command is not a write we can execute it right here,
+ * for write buffers we need to notify the fabric driver first
+ * and let it call back once the write buffers are ready.
*/
- if (task->task_padded_sg) {
- sg_mark_end(&task->task_sg[task->task_sg_num - 1]);
- /*
- * Added the 'if' check before marking end of bi-directional
- * scatterlist (which gets created only in case of request
- * (RD + WR).
- */
- if (task->task_sg_bidi)
- sg_mark_end(&task->task_sg_bidi[task->task_sg_num - 1]);
+ target_add_to_state_list(cmd);
+ if (cmd->data_direction != DMA_TO_DEVICE) {
+ target_execute_cmd(cmd);
+ return 0;
}
+ transport_cmd_check_stop(cmd, false, true);
- DEBUG_SC("Successfully allocated task->task_sg_num(%u),"
- " task_sg_num_padded(%u)\n", task->task_sg_num,
- task_sg_num_padded);
+ ret = cmd->se_tfo->write_pending(cmd);
+ if (ret == -EAGAIN || ret == -ENOMEM)
+ goto queue_full;
- return task->task_sg_num;
-}
+ /* fabric drivers should only return -EAGAIN or -ENOMEM as error */
+ WARN_ON(ret);
-static inline int transport_set_tasks_sectors_disk(
- struct se_task *task,
- struct se_device *dev,
- unsigned long long lba,
- u32 sectors,
- int *max_sectors_set)
-{
- if ((lba + sectors) > transport_dev_end_lba(dev)) {
- task->task_sectors = ((transport_dev_end_lba(dev) - lba) + 1);
-
- if (task->task_sectors > DEV_ATTRIB(dev)->max_sectors) {
- task->task_sectors = DEV_ATTRIB(dev)->max_sectors;
- *max_sectors_set = 1;
- }
- } else {
- if (sectors > DEV_ATTRIB(dev)->max_sectors) {
- task->task_sectors = DEV_ATTRIB(dev)->max_sectors;
- *max_sectors_set = 1;
- } else
- task->task_sectors = sectors;
- }
-
- return 0;
-}
-
-static inline int transport_set_tasks_sectors_non_disk(
- struct se_task *task,
- struct se_device *dev,
- unsigned long long lba,
- u32 sectors,
- int *max_sectors_set)
-{
- if (sectors > DEV_ATTRIB(dev)->max_sectors) {
- task->task_sectors = DEV_ATTRIB(dev)->max_sectors;
- *max_sectors_set = 1;
- } else
- task->task_sectors = sectors;
+ return (!ret) ? 0 : TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+queue_full:
+ pr_debug("Handling write_pending QUEUE__FULL: se_cmd: %p\n", cmd);
+ cmd->t_state = TRANSPORT_COMPLETE_QF_WP;
+ transport_handle_queue_full(cmd, cmd->se_dev);
return 0;
}
+EXPORT_SYMBOL(transport_generic_new_cmd);
-static inline int transport_set_tasks_sectors(
- struct se_task *task,
- struct se_device *dev,
- unsigned long long lba,
- u32 sectors,
- int *max_sectors_set)
+static void transport_write_pending_qf(struct se_cmd *cmd)
{
- return (TRANSPORT(dev)->get_device_type(dev) == TYPE_DISK) ?
- transport_set_tasks_sectors_disk(task, dev, lba, sectors,
- max_sectors_set) :
- transport_set_tasks_sectors_non_disk(task, dev, lba, sectors,
- max_sectors_set);
-}
-
-static int transport_map_sg_to_mem(
- struct se_cmd *cmd,
- struct list_head *se_mem_list,
- void *in_mem,
- u32 *se_mem_cnt)
-{
- struct se_mem *se_mem;
- struct scatterlist *sg;
- u32 sg_count = 1, cmd_size = cmd->data_length;
-
- if (!in_mem) {
- printk(KERN_ERR "No source scatterlist\n");
- return -1;
- }
- sg = (struct scatterlist *)in_mem;
+ int ret;
- while (cmd_size) {
- se_mem = kmem_cache_zalloc(se_mem_cache, GFP_KERNEL);
- if (!(se_mem)) {
- printk(KERN_ERR "Unable to allocate struct se_mem\n");
- return -1;
- }
- INIT_LIST_HEAD(&se_mem->se_list);
- DEBUG_MEM("sg_to_mem: Starting loop with cmd_size: %u"
- " sg_page: %p offset: %d length: %d\n", cmd_size,
- sg_page(sg), sg->offset, sg->length);
-
- se_mem->se_page = sg_page(sg);
- se_mem->se_off = sg->offset;
-
- if (cmd_size > sg->length) {
- se_mem->se_len = sg->length;
- sg = sg_next(sg);
- sg_count++;
- } else
- se_mem->se_len = cmd_size;
-
- cmd_size -= se_mem->se_len;
-
- DEBUG_MEM("sg_to_mem: *se_mem_cnt: %u cmd_size: %u\n",
- *se_mem_cnt, cmd_size);
- DEBUG_MEM("sg_to_mem: Final se_page: %p se_off: %d se_len: %d\n",
- se_mem->se_page, se_mem->se_off, se_mem->se_len);
-
- list_add_tail(&se_mem->se_list, se_mem_list);
- (*se_mem_cnt)++;
+ ret = cmd->se_tfo->write_pending(cmd);
+ if (ret == -EAGAIN || ret == -ENOMEM) {
+ pr_debug("Handling write_pending QUEUE__FULL: se_cmd: %p\n",
+ cmd);
+ transport_handle_queue_full(cmd, cmd->se_dev);
}
-
- DEBUG_MEM("task[0] - Mapped(%u) struct scatterlist segments to(%u)"
- " struct se_mem\n", sg_count, *se_mem_cnt);
-
- if (sg_count != *se_mem_cnt)
- BUG();
-
- return 0;
}
-/* transport_map_mem_to_sg():
- *
- *
- */
-int transport_map_mem_to_sg(
- struct se_task *task,
- struct list_head *se_mem_list,
- void *in_mem,
- struct se_mem *in_se_mem,
- struct se_mem **out_se_mem,
- u32 *se_mem_cnt,
- u32 *task_offset)
+int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
{
- struct se_cmd *se_cmd = task->task_se_cmd;
- struct se_mem *se_mem = in_se_mem;
- struct scatterlist *sg = (struct scatterlist *)in_mem;
- u32 task_size = task->task_size, sg_no = 0;
-
- if (!sg) {
- printk(KERN_ERR "Unable to locate valid struct"
- " scatterlist pointer\n");
- return -1;
- }
+ unsigned long flags;
+ int ret = 0;
+
+ if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD)) {
+ if (wait_for_tasks && (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB))
+ transport_wait_for_tasks(cmd);
- while (task_size != 0) {
+ ret = transport_release_cmd(cmd);
+ } else {
+ if (wait_for_tasks)
+ transport_wait_for_tasks(cmd);
/*
- * Setup the contigious array of scatterlists for
- * this struct se_task.
+ * Handle WRITE failure case where transport_generic_new_cmd()
+ * has already added se_cmd to state_list, but fabric has
+ * failed command before I/O submission.
*/
- sg_assign_page(sg, se_mem->se_page);
-
- if (*task_offset == 0) {
- sg->offset = se_mem->se_off;
-
- if (task_size >= se_mem->se_len) {
- sg->length = se_mem->se_len;
-
- if (!(list_is_last(&se_mem->se_list,
- T_TASK(se_cmd)->t_mem_list))) {
- se_mem = list_entry(se_mem->se_list.next,
- struct se_mem, se_list);
- (*se_mem_cnt)++;
- }
- } else {
- sg->length = task_size;
- /*
- * Determine if we need to calculate an offset
- * into the struct se_mem on the next go around..
- */
- task_size -= sg->length;
- if (!(task_size))
- *task_offset = sg->length;
-
- goto next;
- }
-
- } else {
- sg->offset = (*task_offset + se_mem->se_off);
-
- if ((se_mem->se_len - *task_offset) > task_size) {
- sg->length = task_size;
- /*
- * Determine if we need to calculate an offset
- * into the struct se_mem on the next go around..
- */
- task_size -= sg->length;
- if (!(task_size))
- *task_offset += sg->length;
-
- goto next;
- } else {
- sg->length = (se_mem->se_len - *task_offset);
-
- if (!(list_is_last(&se_mem->se_list,
- T_TASK(se_cmd)->t_mem_list))) {
- se_mem = list_entry(se_mem->se_list.next,
- struct se_mem, se_list);
- (*se_mem_cnt)++;
- }
- }
-
- *task_offset = 0;
+ if (cmd->state_active) {
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ target_remove_from_state_list(cmd);
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
}
- task_size -= sg->length;
-next:
- DEBUG_MEM("task[%u] mem_to_sg - sg[%u](%p)(%u)(%u) - Reducing"
- " task_size to(%u), task_offset: %u\n", task->task_no, sg_no,
- sg_page(sg), sg->length, sg->offset, task_size, *task_offset);
-
- sg_no++;
- if (!(task_size))
- break;
- sg = sg_next(sg);
+ if (cmd->se_lun)
+ transport_lun_remove_cmd(cmd);
- if (task_size > se_cmd->data_length)
- BUG();
+ ret = transport_put_cmd(cmd);
}
- *out_se_mem = se_mem;
-
- DEBUG_MEM("task[%u] - Mapped(%u) struct se_mem segments to total(%u)"
- " SGs\n", task->task_no, *se_mem_cnt, sg_no);
-
- return 0;
+ return ret;
}
+EXPORT_SYMBOL(transport_generic_free_cmd);
-/*
- * This function can be used by HW target mode drivers to create a linked
- * scatterlist from all contiguously allocated struct se_task->task_sg[].
- * This is intended to be called during the completion path by TCM Core
- * when struct target_core_fabric_ops->check_task_sg_chaining is enabled.
+/* target_get_sess_cmd - Add command to active ->sess_cmd_list
+ * @se_sess: session to reference
+ * @se_cmd: command descriptor to add
+ * @ack_kref: Signal that fabric will perform an ack target_put_sess_cmd()
*/
-void transport_do_task_sg_chain(struct se_cmd *cmd)
-{
- struct scatterlist *sg_head = NULL, *sg_link = NULL, *sg_first = NULL;
- struct scatterlist *sg_head_cur = NULL, *sg_link_cur = NULL;
- struct scatterlist *sg, *sg_end = NULL, *sg_end_cur = NULL;
- struct se_task *task;
- struct target_core_fabric_ops *tfo = CMD_TFO(cmd);
- u32 task_sg_num = 0, sg_count = 0;
- int i;
-
- if (tfo->task_sg_chaining == 0) {
- printk(KERN_ERR "task_sg_chaining is diabled for fabric module:"
- " %s\n", tfo->get_fabric_name());
- dump_stack();
- return;
- }
- /*
- * Walk the struct se_task list and setup scatterlist chains
- * for each contiguosly allocated struct se_task->task_sg[].
- */
- list_for_each_entry(task, &T_TASK(cmd)->t_task_list, t_list) {
- if (!(task->task_sg) || !(task->task_padded_sg))
- continue;
-
- if (sg_head && sg_link) {
- sg_head_cur = &task->task_sg[0];
- sg_link_cur = &task->task_sg[task->task_sg_num];
- /*
- * Either add chain or mark end of scatterlist
- */
- if (!(list_is_last(&task->t_list,
- &T_TASK(cmd)->t_task_list))) {
- /*
- * Clear existing SGL termination bit set in
- * transport_calc_sg_num(), see sg_mark_end()
- */
- sg_end_cur = &task->task_sg[task->task_sg_num - 1];
- sg_end_cur->page_link &= ~0x02;
-
- sg_chain(sg_head, task_sg_num, sg_head_cur);
- sg_count += (task->task_sg_num + 1);
- } else
- sg_count += task->task_sg_num;
-
- sg_head = sg_head_cur;
- sg_link = sg_link_cur;
- task_sg_num = task->task_sg_num;
- continue;
- }
- sg_head = sg_first = &task->task_sg[0];
- sg_link = &task->task_sg[task->task_sg_num];
- task_sg_num = task->task_sg_num;
- /*
- * Check for single task..
- */
- if (!(list_is_last(&task->t_list, &T_TASK(cmd)->t_task_list))) {
- /*
- * Clear existing SGL termination bit set in
- * transport_calc_sg_num(), see sg_mark_end()
- */
- sg_end = &task->task_sg[task->task_sg_num - 1];
- sg_end->page_link &= ~0x02;
- sg_count += (task->task_sg_num + 1);
- } else
- sg_count += task->task_sg_num;
- }
- /*
- * Setup the starting pointer and total t_tasks_sg_linked_no including
- * padding SGs for linking and to mark the end.
- */
- T_TASK(cmd)->t_tasks_sg_chained = sg_first;
- T_TASK(cmd)->t_tasks_sg_chained_no = sg_count;
-
- DEBUG_CMD_M("Setup T_TASK(cmd)->t_tasks_sg_chained: %p and"
- " t_tasks_sg_chained_no: %u\n", T_TASK(cmd)->t_tasks_sg_chained,
- T_TASK(cmd)->t_tasks_sg_chained_no);
-
- for_each_sg(T_TASK(cmd)->t_tasks_sg_chained, sg,
- T_TASK(cmd)->t_tasks_sg_chained_no, i) {
-
- DEBUG_CMD_M("SG: %p page: %p length: %d offset: %d\n",
- sg, sg_page(sg), sg->length, sg->offset);
- if (sg_is_chain(sg))
- DEBUG_CMD_M("SG: %p sg_is_chain=1\n", sg);
- if (sg_is_last(sg))
- DEBUG_CMD_M("SG: %p sg_is_last=1\n", sg);
- }
-
-}
-EXPORT_SYMBOL(transport_do_task_sg_chain);
-
-static int transport_do_se_mem_map(
- struct se_device *dev,
- struct se_task *task,
- struct list_head *se_mem_list,
- void *in_mem,
- struct se_mem *in_se_mem,
- struct se_mem **out_se_mem,
- u32 *se_mem_cnt,
- u32 *task_offset_in)
+int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd,
+ bool ack_kref)
{
- u32 task_offset = *task_offset_in;
+ unsigned long flags;
int ret = 0;
- /*
- * se_subsystem_api_t->do_se_mem_map is used when internal allocation
- * has been done by the transport plugin.
- */
- if (TRANSPORT(dev)->do_se_mem_map) {
- ret = TRANSPORT(dev)->do_se_mem_map(task, se_mem_list,
- in_mem, in_se_mem, out_se_mem, se_mem_cnt,
- task_offset_in);
- if (ret == 0)
- T_TASK(task->task_se_cmd)->t_tasks_se_num += *se_mem_cnt;
- return ret;
- }
-
- BUG_ON(list_empty(se_mem_list));
/*
- * This is the normal path for all normal non BIDI and BIDI-COMMAND
- * WRITE payloads.. If we need to do BIDI READ passthrough for
- * TCM/pSCSI the first call to transport_do_se_mem_map ->
- * transport_calc_sg_num() -> transport_map_mem_to_sg() will do the
- * allocation for task->task_sg_bidi, and the subsequent call to
- * transport_do_se_mem_map() from transport_generic_get_cdb_count()
+ * Add a second kref if the fabric caller is expecting to handle
+ * fabric acknowledgement that requires two target_put_sess_cmd()
+ * invocations before se_cmd descriptor release.
*/
- if (!(task->task_sg_bidi)) {
- /*
- * Assume default that transport plugin speaks preallocated
- * scatterlists.
- */
- if (!(transport_calc_sg_num(task, in_se_mem, task_offset)))
- return -1;
- /*
- * struct se_task->task_sg now contains the struct scatterlist array.
- */
- return transport_map_mem_to_sg(task, se_mem_list, task->task_sg,
- in_se_mem, out_se_mem, se_mem_cnt,
- task_offset_in);
+ if (ack_kref) {
+ kref_get(&se_cmd->cmd_kref);
+ se_cmd->se_cmd_flags |= SCF_ACK_KREF;
}
- /*
- * Handle the se_mem_list -> struct task->task_sg_bidi
- * memory map for the extra BIDI READ payload
- */
- return transport_map_mem_to_sg(task, se_mem_list, task->task_sg_bidi,
- in_se_mem, out_se_mem, se_mem_cnt,
- task_offset_in);
-}
-static u32 transport_generic_get_cdb_count(
- struct se_cmd *cmd,
- unsigned long long lba,
- u32 sectors,
- enum dma_data_direction data_direction,
- struct list_head *mem_list,
- int set_counts)
-{
- unsigned char *cdb = NULL;
- struct se_task *task;
- struct se_mem *se_mem = NULL, *se_mem_lout = NULL;
- struct se_mem *se_mem_bidi = NULL, *se_mem_bidi_lout = NULL;
- struct se_device *dev = SE_DEV(cmd);
- int max_sectors_set = 0, ret;
- u32 task_offset_in = 0, se_mem_cnt = 0, se_mem_bidi_cnt = 0, task_cdbs = 0;
-
- if (!mem_list) {
- printk(KERN_ERR "mem_list is NULL in transport_generic_get"
- "_cdb_count()\n");
- return 0;
- }
- /*
- * While using RAMDISK_DR backstores is the only case where
- * mem_list will ever be empty at this point.
- */
- if (!(list_empty(mem_list)))
- se_mem = list_entry(mem_list->next, struct se_mem, se_list);
- /*
- * Check for extra se_mem_bidi mapping for BIDI-COMMANDs to
- * struct se_task->task_sg_bidi for TCM/pSCSI passthrough operation
- */
- if ((T_TASK(cmd)->t_mem_bidi_list != NULL) &&
- !(list_empty(T_TASK(cmd)->t_mem_bidi_list)) &&
- (TRANSPORT(dev)->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV))
- se_mem_bidi = list_entry(T_TASK(cmd)->t_mem_bidi_list->next,
- struct se_mem, se_list);
-
- while (sectors) {
- DEBUG_VOL("ITT[0x%08x] LBA(%llu) SectorsLeft(%u) EOBJ(%llu)\n",
- CMD_TFO(cmd)->get_task_tag(cmd), lba, sectors,
- transport_dev_end_lba(dev));
-
- task = transport_generic_get_task(cmd, data_direction);
- if (!(task))
- goto out;
-
- transport_set_tasks_sectors(task, dev, lba, sectors,
- &max_sectors_set);
-
- task->task_lba = lba;
- lba += task->task_sectors;
- sectors -= task->task_sectors;
- task->task_size = (task->task_sectors *
- DEV_ATTRIB(dev)->block_size);
-
- cdb = TRANSPORT(dev)->get_cdb(task);
- if ((cdb)) {
- memcpy(cdb, T_TASK(cmd)->t_task_cdb,
- scsi_command_size(T_TASK(cmd)->t_task_cdb));
- cmd->transport_split_cdb(task->task_lba,
- &task->task_sectors, cdb);
- }
-
- /*
- * Perform the SE OBJ plugin and/or Transport plugin specific
- * mapping for T_TASK(cmd)->t_mem_list. And setup the
- * task->task_sg and if necessary task->task_sg_bidi
- */
- ret = transport_do_se_mem_map(dev, task, mem_list,
- NULL, se_mem, &se_mem_lout, &se_mem_cnt,
- &task_offset_in);
- if (ret < 0)
- goto out;
-
- se_mem = se_mem_lout;
- /*
- * Setup the T_TASK(cmd)->t_mem_bidi_list -> task->task_sg_bidi
- * mapping for SCSI READ for BIDI-COMMAND passthrough with TCM/pSCSI
- *
- * Note that the first call to transport_do_se_mem_map() above will
- * allocate struct se_task->task_sg_bidi in transport_do_se_mem_map()
- * -> transport_calc_sg_num(), and the second here will do the
- * mapping for SCSI READ for BIDI-COMMAND passthrough with TCM/pSCSI.
- */
- if (task->task_sg_bidi != NULL) {
- ret = transport_do_se_mem_map(dev, task,
- T_TASK(cmd)->t_mem_bidi_list, NULL,
- se_mem_bidi, &se_mem_bidi_lout, &se_mem_bidi_cnt,
- &task_offset_in);
- if (ret < 0)
- goto out;
-
- se_mem_bidi = se_mem_bidi_lout;
- }
- task_cdbs++;
-
- DEBUG_VOL("Incremented task_cdbs(%u) task->task_sg_num(%u)\n",
- task_cdbs, task->task_sg_num);
-
- if (max_sectors_set) {
- max_sectors_set = 0;
- continue;
- }
-
- if (!sectors)
- break;
- }
-
- if (set_counts) {
- atomic_inc(&T_TASK(cmd)->t_fe_count);
- atomic_inc(&T_TASK(cmd)->t_se_count);
+ spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
+ if (se_sess->sess_tearing_down) {
+ ret = -ESHUTDOWN;
+ goto out;
}
-
- DEBUG_VOL("ITT[0x%08x] total %s cdbs(%u)\n",
- CMD_TFO(cmd)->get_task_tag(cmd), (data_direction == DMA_TO_DEVICE)
- ? "DMA_TO_DEVICE" : "DMA_FROM_DEVICE", task_cdbs);
-
- return task_cdbs;
+ list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list);
out:
- return 0;
-}
-
-static int
-transport_map_control_cmd_to_task(struct se_cmd *cmd)
-{
- struct se_device *dev = SE_DEV(cmd);
- unsigned char *cdb;
- struct se_task *task;
- int ret;
-
- task = transport_generic_get_task(cmd, cmd->data_direction);
- if (!task)
- return PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES;
-
- cdb = TRANSPORT(dev)->get_cdb(task);
- if (cdb)
- memcpy(cdb, cmd->t_task->t_task_cdb,
- scsi_command_size(cmd->t_task->t_task_cdb));
-
- task->task_size = cmd->data_length;
- task->task_sg_num =
- (cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB) ? 1 : 0;
-
- atomic_inc(&cmd->t_task->t_fe_count);
- atomic_inc(&cmd->t_task->t_se_count);
-
- if (cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB) {
- struct se_mem *se_mem = NULL, *se_mem_lout = NULL;
- u32 se_mem_cnt = 0, task_offset = 0;
-
- if (!list_empty(T_TASK(cmd)->t_mem_list))
- se_mem = list_entry(T_TASK(cmd)->t_mem_list->next,
- struct se_mem, se_list);
-
- ret = transport_do_se_mem_map(dev, task,
- cmd->t_task->t_mem_list, NULL, se_mem,
- &se_mem_lout, &se_mem_cnt, &task_offset);
- if (ret < 0)
- return PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES;
-
- if (dev->transport->map_task_SG)
- return dev->transport->map_task_SG(task);
- return 0;
- } else if (cmd->se_cmd_flags & SCF_SCSI_CONTROL_NONSG_IO_CDB) {
- if (dev->transport->map_task_non_SG)
- return dev->transport->map_task_non_SG(task);
- return 0;
- } else if (cmd->se_cmd_flags & SCF_SCSI_NON_DATA_CDB) {
- if (dev->transport->cdb_none)
- return dev->transport->cdb_none(task);
- return 0;
- } else {
- BUG();
- return PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES;
- }
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+ return ret;
}
+EXPORT_SYMBOL(target_get_sess_cmd);
-/* transport_generic_new_cmd(): Called from transport_processing_thread()
- *
- * Allocate storage transport resources from a set of values predefined
- * by transport_generic_cmd_sequencer() from the iSCSI Target RX process.
- * Any non zero return here is treated as an "out of resource' op here.
- */
- /*
- * Generate struct se_task(s) and/or their payloads for this CDB.
- */
-static int transport_generic_new_cmd(struct se_cmd *cmd)
+static void target_release_cmd_kref(struct kref *kref)
{
- struct se_portal_group *se_tpg;
- struct se_task *task;
- struct se_device *dev = SE_DEV(cmd);
- int ret = 0;
+ struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref);
+ struct se_session *se_sess = se_cmd->se_sess;
- /*
- * Determine is the TCM fabric module has already allocated physical
- * memory, and is directly calling transport_generic_map_mem_to_cmd()
- * to setup beforehand the linked list of physical memory at
- * T_TASK(cmd)->t_mem_list of struct se_mem->se_page
- */
- if (!(cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC)) {
- ret = transport_allocate_resources(cmd);
- if (ret < 0)
- return ret;
- }
-
- ret = transport_get_sectors(cmd);
- if (ret < 0)
- return ret;
-
- ret = transport_new_cmd_obj(cmd);
- if (ret < 0)
- return ret;
-
- /*
- * Determine if the calling TCM fabric module is talking to
- * Linux/NET via kernel sockets and needs to allocate a
- * struct iovec array to complete the struct se_cmd
- */
- se_tpg = SE_LUN(cmd)->lun_sep->sep_tpg;
- if (TPG_TFO(se_tpg)->alloc_cmd_iovecs != NULL) {
- ret = TPG_TFO(se_tpg)->alloc_cmd_iovecs(cmd);
- if (ret < 0)
- return PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES;
+ if (list_empty(&se_cmd->se_cmd_list)) {
+ spin_unlock(&se_sess->sess_cmd_lock);
+ se_cmd->se_tfo->release_cmd(se_cmd);
+ return;
}
-
- if (cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) {
- list_for_each_entry(task, &T_TASK(cmd)->t_task_list, t_list) {
- if (atomic_read(&task->task_sent))
- continue;
- if (!dev->transport->map_task_SG)
- continue;
-
- ret = dev->transport->map_task_SG(task);
- if (ret < 0)
- return ret;
- }
- } else {
- ret = transport_map_control_cmd_to_task(cmd);
- if (ret < 0)
- return ret;
+ if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) {
+ spin_unlock(&se_sess->sess_cmd_lock);
+ complete(&se_cmd->cmd_wait_comp);
+ return;
}
+ list_del(&se_cmd->se_cmd_list);
+ spin_unlock(&se_sess->sess_cmd_lock);
- /*
- * For WRITEs, let the iSCSI Target RX Thread know its buffer is ready..
- * This WRITE struct se_cmd (and all of its associated struct se_task's)
- * will be added to the struct se_device execution queue after its WRITE
- * data has arrived. (ie: It gets handled by the transport processing
- * thread a second time)
- */
- if (cmd->data_direction == DMA_TO_DEVICE) {
- transport_add_tasks_to_state_queue(cmd);
- return transport_generic_write_pending(cmd);
- }
- /*
- * Everything else but a WRITE, add the struct se_cmd's struct se_task's
- * to the execution queue.
- */
- transport_execute_tasks(cmd);
- return 0;
+ se_cmd->se_tfo->release_cmd(se_cmd);
}
-/* transport_generic_process_write():
- *
- *
+/* target_put_sess_cmd - Check for active I/O shutdown via kref_put
+ * @se_sess: session to reference
+ * @se_cmd: command descriptor to drop
*/
-void transport_generic_process_write(struct se_cmd *cmd)
+int target_put_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd)
{
-#if 0
- /*
- * Copy SCSI Presented DTL sector(s) from received buffers allocated to
- * original EDTL
- */
- if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
- if (!T_TASK(cmd)->t_tasks_se_num) {
- unsigned char *dst, *buf =
- (unsigned char *)T_TASK(cmd)->t_task_buf;
-
- dst = kzalloc(cmd->cmd_spdtl), GFP_KERNEL);
- if (!(dst)) {
- printk(KERN_ERR "Unable to allocate memory for"
- " WRITE underflow\n");
- transport_generic_request_failure(cmd, NULL,
- PYX_TRANSPORT_REQ_TOO_MANY_SECTORS, 1);
- return;
- }
- memcpy(dst, buf, cmd->cmd_spdtl);
-
- kfree(T_TASK(cmd)->t_task_buf);
- T_TASK(cmd)->t_task_buf = dst;
- } else {
- struct scatterlist *sg =
- (struct scatterlist *sg)T_TASK(cmd)->t_task_buf;
- struct scatterlist *orig_sg;
-
- orig_sg = kzalloc(sizeof(struct scatterlist) *
- T_TASK(cmd)->t_tasks_se_num,
- GFP_KERNEL))) {
- if (!(orig_sg)) {
- printk(KERN_ERR "Unable to allocate memory"
- " for WRITE underflow\n");
- transport_generic_request_failure(cmd, NULL,
- PYX_TRANSPORT_REQ_TOO_MANY_SECTORS, 1);
- return;
- }
-
- memcpy(orig_sg, T_TASK(cmd)->t_task_buf,
- sizeof(struct scatterlist) *
- T_TASK(cmd)->t_tasks_se_num);
-
- cmd->data_length = cmd->cmd_spdtl;
- /*
- * FIXME, clear out original struct se_task and state
- * information.
- */
- if (transport_generic_new_cmd(cmd) < 0) {
- transport_generic_request_failure(cmd, NULL,
- PYX_TRANSPORT_REQ_TOO_MANY_SECTORS, 1);
- kfree(orig_sg);
- return;
- }
-
- transport_memcpy_write_sg(cmd, orig_sg);
- }
+ if (!se_sess) {
+ se_cmd->se_tfo->release_cmd(se_cmd);
+ return 1;
}
-#endif
- transport_execute_tasks(cmd);
+ return kref_put_spinlock_irqsave(&se_cmd->cmd_kref, target_release_cmd_kref,
+ &se_sess->sess_cmd_lock);
}
-EXPORT_SYMBOL(transport_generic_process_write);
+EXPORT_SYMBOL(target_put_sess_cmd);
-/* transport_generic_write_pending():
- *
- *
+/* target_sess_cmd_list_set_waiting - Flag all commands in
+ * sess_cmd_list to complete cmd_wait_comp. Set
+ * sess_tearing_down so no more commands are queued.
+ * @se_sess: session to flag
*/
-static int transport_generic_write_pending(struct se_cmd *cmd)
+void target_sess_cmd_list_set_waiting(struct se_session *se_sess)
{
+ struct se_cmd *se_cmd;
unsigned long flags;
- int ret;
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- cmd->t_state = TRANSPORT_WRITE_PENDING;
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- /*
- * For the TCM control CDBs using a contiguous buffer, do the memcpy
- * from the passed Linux/SCSI struct scatterlist located at
- * T_TASK(se_cmd)->t_task_pt_buf to the contiguous buffer at
- * T_TASK(se_cmd)->t_task_buf.
- */
- if (cmd->se_cmd_flags & SCF_PASSTHROUGH_CONTIG_TO_SG)
- transport_memcpy_read_contig(cmd,
- T_TASK(cmd)->t_task_buf,
- T_TASK(cmd)->t_task_pt_sgl);
- /*
- * Clear the se_cmd for WRITE_PENDING status in order to set
- * T_TASK(cmd)->t_transport_active=0 so that transport_generic_handle_data
- * can be called from HW target mode interrupt code. This is safe
- * to be called with transport_off=1 before the CMD_TFO(cmd)->write_pending
- * because the se_cmd->se_lun pointer is not being cleared.
- */
- transport_cmd_check_stop(cmd, 1, 0);
+ spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
+ if (se_sess->sess_tearing_down) {
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+ return;
+ }
+ se_sess->sess_tearing_down = 1;
+ list_splice_init(&se_sess->sess_cmd_list, &se_sess->sess_wait_list);
- /*
- * Call the fabric write_pending function here to let the
- * frontend know that WRITE buffers are ready.
- */
- ret = CMD_TFO(cmd)->write_pending(cmd);
- if (ret < 0)
- return ret;
+ list_for_each_entry(se_cmd, &se_sess->sess_wait_list, se_cmd_list)
+ se_cmd->cmd_wait_set = 1;
- return PYX_TRANSPORT_WRITE_PENDING;
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
}
+EXPORT_SYMBOL(target_sess_cmd_list_set_waiting);
-/* transport_release_cmd_to_pool():
- *
- *
+/* target_wait_for_sess_cmds - Wait for outstanding descriptors
+ * @se_sess: session to wait for active I/O
*/
-void transport_release_cmd_to_pool(struct se_cmd *cmd)
+void target_wait_for_sess_cmds(struct se_session *se_sess)
{
- BUG_ON(!T_TASK(cmd));
- BUG_ON(!CMD_TFO(cmd));
-
- transport_free_se_cmd(cmd);
- CMD_TFO(cmd)->release_cmd_to_pool(cmd);
-}
-EXPORT_SYMBOL(transport_release_cmd_to_pool);
-
-/* transport_generic_free_cmd():
- *
- * Called from processing frontend to release storage engine resources
- */
-void transport_generic_free_cmd(
- struct se_cmd *cmd,
- int wait_for_tasks,
- int release_to_pool,
- int session_reinstatement)
-{
- if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD) || !T_TASK(cmd))
- transport_release_cmd_to_pool(cmd);
- else {
- core_dec_lacl_count(cmd->se_sess->se_node_acl, cmd);
-
- if (SE_LUN(cmd)) {
-#if 0
- printk(KERN_INFO "cmd: %p ITT: 0x%08x contains"
- " SE_LUN(cmd)\n", cmd,
- CMD_TFO(cmd)->get_task_tag(cmd));
-#endif
- transport_lun_remove_cmd(cmd);
- }
+ struct se_cmd *se_cmd, *tmp_cmd;
+ unsigned long flags;
- if (wait_for_tasks && cmd->transport_wait_for_tasks)
- cmd->transport_wait_for_tasks(cmd, 0, 0);
+ list_for_each_entry_safe(se_cmd, tmp_cmd,
+ &se_sess->sess_wait_list, se_cmd_list) {
+ list_del(&se_cmd->se_cmd_list);
- transport_generic_remove(cmd, release_to_pool,
- session_reinstatement);
- }
-}
-EXPORT_SYMBOL(transport_generic_free_cmd);
+ pr_debug("Waiting for se_cmd: %p t_state: %d, fabric state:"
+ " %d\n", se_cmd, se_cmd->t_state,
+ se_cmd->se_tfo->get_cmd_state(se_cmd));
-static void transport_nop_wait_for_tasks(
- struct se_cmd *cmd,
- int remove_cmd,
- int session_reinstatement)
-{
- return;
-}
+ wait_for_completion(&se_cmd->cmd_wait_comp);
+ pr_debug("After cmd_wait_comp: se_cmd: %p t_state: %d"
+ " fabric state: %d\n", se_cmd, se_cmd->t_state,
+ se_cmd->se_tfo->get_cmd_state(se_cmd));
-/* transport_lun_wait_for_tasks():
- *
- * Called from ConfigFS context to stop the passed struct se_cmd to allow
- * an struct se_lun to be successfully shutdown.
- */
-static int transport_lun_wait_for_tasks(struct se_cmd *cmd, struct se_lun *lun)
-{
- unsigned long flags;
- int ret;
- /*
- * If the frontend has already requested this struct se_cmd to
- * be stopped, we can safely ignore this struct se_cmd.
- */
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- if (atomic_read(&T_TASK(cmd)->t_transport_stop)) {
- atomic_set(&T_TASK(cmd)->transport_lun_stop, 0);
- DEBUG_TRANSPORT_S("ConfigFS ITT[0x%08x] - t_transport_stop =="
- " TRUE, skipping\n", CMD_TFO(cmd)->get_task_tag(cmd));
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- transport_cmd_check_stop(cmd, 1, 0);
- return -1;
+ se_cmd->se_tfo->release_cmd(se_cmd);
}
- atomic_set(&T_TASK(cmd)->transport_lun_fe_stop, 1);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- wake_up_interruptible(&SE_DEV(cmd)->dev_queue_obj->thread_wq);
+ spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
+ WARN_ON(!list_empty(&se_sess->sess_cmd_list));
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
- ret = transport_stop_tasks_for_cmd(cmd);
-
- DEBUG_TRANSPORT_S("ConfigFS: cmd: %p t_task_cdbs: %d stop tasks ret:"
- " %d\n", cmd, T_TASK(cmd)->t_task_cdbs, ret);
- if (!ret) {
- DEBUG_TRANSPORT_S("ConfigFS: ITT[0x%08x] - stopping cmd....\n",
- CMD_TFO(cmd)->get_task_tag(cmd));
- wait_for_completion(&T_TASK(cmd)->transport_lun_stop_comp);
- DEBUG_TRANSPORT_S("ConfigFS: ITT[0x%08x] - stopped cmd....\n",
- CMD_TFO(cmd)->get_task_tag(cmd));
- }
- transport_remove_cmd_from_queue(cmd, SE_DEV(cmd)->dev_queue_obj);
-
- return 0;
}
+EXPORT_SYMBOL(target_wait_for_sess_cmds);
-/* #define DEBUG_CLEAR_LUN */
-#ifdef DEBUG_CLEAR_LUN
-#define DEBUG_CLEAR_L(x...) printk(KERN_INFO x)
-#else
-#define DEBUG_CLEAR_L(x...)
-#endif
-
-static void __transport_clear_lun_from_sessions(struct se_lun *lun)
+static int transport_clear_lun_ref_thread(void *p)
{
- struct se_cmd *cmd = NULL;
- unsigned long lun_flags, cmd_flags;
- /*
- * Do exception processing and return CHECK_CONDITION status to the
- * Initiator Port.
- */
- spin_lock_irqsave(&lun->lun_cmd_lock, lun_flags);
- while (!list_empty_careful(&lun->lun_cmd_list)) {
- cmd = list_entry(lun->lun_cmd_list.next,
- struct se_cmd, se_lun_list);
- list_del(&cmd->se_lun_list);
-
- if (!(T_TASK(cmd))) {
- printk(KERN_ERR "ITT: 0x%08x, T_TASK(cmd) = NULL"
- "[i,t]_state: %u/%u\n",
- CMD_TFO(cmd)->get_task_tag(cmd),
- CMD_TFO(cmd)->get_cmd_state(cmd), cmd->t_state);
- BUG();
- }
- atomic_set(&T_TASK(cmd)->transport_lun_active, 0);
- /*
- * This will notify iscsi_target_transport.c:
- * transport_cmd_check_stop() that a LUN shutdown is in
- * progress for the iscsi_cmd_t.
- */
- spin_lock(&T_TASK(cmd)->t_state_lock);
- DEBUG_CLEAR_L("SE_LUN[%d] - Setting T_TASK(cmd)->transport"
- "_lun_stop for ITT: 0x%08x\n",
- SE_LUN(cmd)->unpacked_lun,
- CMD_TFO(cmd)->get_task_tag(cmd));
- atomic_set(&T_TASK(cmd)->transport_lun_stop, 1);
- spin_unlock(&T_TASK(cmd)->t_state_lock);
-
- spin_unlock_irqrestore(&lun->lun_cmd_lock, lun_flags);
-
- if (!(SE_LUN(cmd))) {
- printk(KERN_ERR "ITT: 0x%08x, [i,t]_state: %u/%u\n",
- CMD_TFO(cmd)->get_task_tag(cmd),
- CMD_TFO(cmd)->get_cmd_state(cmd), cmd->t_state);
- BUG();
- }
- /*
- * If the Storage engine still owns the iscsi_cmd_t, determine
- * and/or stop its context.
- */
- DEBUG_CLEAR_L("SE_LUN[%d] - ITT: 0x%08x before transport"
- "_lun_wait_for_tasks()\n", SE_LUN(cmd)->unpacked_lun,
- CMD_TFO(cmd)->get_task_tag(cmd));
+ struct se_lun *lun = p;
- if (transport_lun_wait_for_tasks(cmd, SE_LUN(cmd)) < 0) {
- spin_lock_irqsave(&lun->lun_cmd_lock, lun_flags);
- continue;
- }
-
- DEBUG_CLEAR_L("SE_LUN[%d] - ITT: 0x%08x after transport_lun"
- "_wait_for_tasks(): SUCCESS\n",
- SE_LUN(cmd)->unpacked_lun,
- CMD_TFO(cmd)->get_task_tag(cmd));
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, cmd_flags);
- if (!(atomic_read(&T_TASK(cmd)->transport_dev_active))) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, cmd_flags);
- goto check_cond;
- }
- atomic_set(&T_TASK(cmd)->transport_dev_active, 0);
- transport_all_task_dev_remove_state(cmd);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, cmd_flags);
+ percpu_ref_kill(&lun->lun_ref);
- transport_free_dev_tasks(cmd);
- /*
- * The Storage engine stopped this struct se_cmd before it was
- * send to the fabric frontend for delivery back to the
- * Initiator Node. Return this SCSI CDB back with an
- * CHECK_CONDITION status.
- */
-check_cond:
- transport_send_check_condition_and_sense(cmd,
- TCM_NON_EXISTENT_LUN, 0);
- /*
- * If the fabric frontend is waiting for this iscsi_cmd_t to
- * be released, notify the waiting thread now that LU has
- * finished accessing it.
- */
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, cmd_flags);
- if (atomic_read(&T_TASK(cmd)->transport_lun_fe_stop)) {
- DEBUG_CLEAR_L("SE_LUN[%d] - Detected FE stop for"
- " struct se_cmd: %p ITT: 0x%08x\n",
- lun->unpacked_lun,
- cmd, CMD_TFO(cmd)->get_task_tag(cmd));
-
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock,
- cmd_flags);
- transport_cmd_check_stop(cmd, 1, 0);
- complete(&T_TASK(cmd)->transport_lun_fe_stop_comp);
- spin_lock_irqsave(&lun->lun_cmd_lock, lun_flags);
- continue;
- }
- DEBUG_CLEAR_L("SE_LUN[%d] - ITT: 0x%08x finished processing\n",
- lun->unpacked_lun, CMD_TFO(cmd)->get_task_tag(cmd));
-
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, cmd_flags);
- spin_lock_irqsave(&lun->lun_cmd_lock, lun_flags);
- }
- spin_unlock_irqrestore(&lun->lun_cmd_lock, lun_flags);
-}
-
-static int transport_clear_lun_thread(void *p)
-{
- struct se_lun *lun = (struct se_lun *)p;
-
- __transport_clear_lun_from_sessions(lun);
+ wait_for_completion(&lun->lun_ref_comp);
complete(&lun->lun_shutdown_comp);
return 0;
}
-int transport_clear_lun_from_sessions(struct se_lun *lun)
+int transport_clear_lun_ref(struct se_lun *lun)
{
struct task_struct *kt;
- kt = kthread_run(transport_clear_lun_thread, (void *)lun,
+ kt = kthread_run(transport_clear_lun_ref_thread, lun,
"tcm_cl_%u", lun->unpacked_lun);
if (IS_ERR(kt)) {
- printk(KERN_ERR "Unable to start clear_lun thread\n");
- return -1;
+ pr_err("Unable to start clear_lun thread\n");
+ return PTR_ERR(kt);
}
wait_for_completion(&lun->lun_shutdown_comp);
return 0;
}
-/* transport_generic_wait_for_tasks():
+/**
+ * transport_wait_for_tasks - wait for completion to occur
+ * @cmd: command to wait
*
- * Called from frontend or passthrough context to wait for storage engine
- * to pause and/or release frontend generated struct se_cmd.
+ * Called from frontend fabric context to wait for storage engine
+ * to pause and/or release frontend generated struct se_cmd.
*/
-static void transport_generic_wait_for_tasks(
- struct se_cmd *cmd,
- int remove_cmd,
- int session_reinstatement)
+bool transport_wait_for_tasks(struct se_cmd *cmd)
{
unsigned long flags;
- if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD) && !(cmd->se_tmr_req))
- return;
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- /*
- * If we are already stopped due to an external event (ie: LUN shutdown)
- * sleep until the connection can have the passed struct se_cmd back.
- * The T_TASK(cmd)->transport_lun_stopped_sem will be upped by
- * transport_clear_lun_from_sessions() once the ConfigFS context caller
- * has completed its operation on the struct se_cmd.
- */
- if (atomic_read(&T_TASK(cmd)->transport_lun_stop)) {
-
- DEBUG_TRANSPORT_S("wait_for_tasks: Stopping"
- " wait_for_completion(&T_TASK(cmd)transport_lun_fe"
- "_stop_comp); for ITT: 0x%08x\n",
- CMD_TFO(cmd)->get_task_tag(cmd));
- /*
- * There is a special case for WRITES where a FE exception +
- * LUN shutdown means ConfigFS context is still sleeping on
- * transport_lun_stop_comp in transport_lun_wait_for_tasks().
- * We go ahead and up transport_lun_stop_comp just to be sure
- * here.
- */
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- complete(&T_TASK(cmd)->transport_lun_stop_comp);
- wait_for_completion(&T_TASK(cmd)->transport_lun_fe_stop_comp);
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD) &&
+ !(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) {
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ return false;
+ }
- transport_all_task_dev_remove_state(cmd);
- /*
- * At this point, the frontend who was the originator of this
- * struct se_cmd, now owns the structure and can be released through
- * normal means below.
- */
- DEBUG_TRANSPORT_S("wait_for_tasks: Stopped"
- " wait_for_completion(&T_TASK(cmd)transport_lun_fe_"
- "stop_comp); for ITT: 0x%08x\n",
- CMD_TFO(cmd)->get_task_tag(cmd));
+ if (!(cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE) &&
+ !(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) {
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ return false;
+ }
- atomic_set(&T_TASK(cmd)->transport_lun_stop, 0);
+ if (!(cmd->transport_state & CMD_T_ACTIVE)) {
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ return false;
}
- if (!atomic_read(&T_TASK(cmd)->t_transport_active))
- goto remove;
- atomic_set(&T_TASK(cmd)->t_transport_stop, 1);
+ cmd->transport_state |= CMD_T_STOP;
- DEBUG_TRANSPORT_S("wait_for_tasks: Stopping %p ITT: 0x%08x"
- " i_state: %d, t_state/def_t_state: %d/%d, t_transport_stop"
- " = TRUE\n", cmd, CMD_TFO(cmd)->get_task_tag(cmd),
- CMD_TFO(cmd)->get_cmd_state(cmd), cmd->t_state,
- cmd->deferred_t_state);
+ pr_debug("wait_for_tasks: Stopping %p ITT: 0x%08x"
+ " i_state: %d, t_state: %d, CMD_T_STOP\n",
+ cmd, cmd->se_tfo->get_task_tag(cmd),
+ cmd->se_tfo->get_cmd_state(cmd), cmd->t_state);
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- wake_up_interruptible(&SE_DEV(cmd)->dev_queue_obj->thread_wq);
+ wait_for_completion(&cmd->t_transport_stop_comp);
- wait_for_completion(&T_TASK(cmd)->t_transport_stop_comp);
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ cmd->transport_state &= ~(CMD_T_ACTIVE | CMD_T_STOP);
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- atomic_set(&T_TASK(cmd)->t_transport_active, 0);
- atomic_set(&T_TASK(cmd)->t_transport_stop, 0);
+ pr_debug("wait_for_tasks: Stopped wait_for_completion("
+ "&cmd->t_transport_stop_comp) for ITT: 0x%08x\n",
+ cmd->se_tfo->get_task_tag(cmd));
- DEBUG_TRANSPORT_S("wait_for_tasks: Stopped wait_for_compltion("
- "&T_TASK(cmd)->t_transport_stop_comp) for ITT: 0x%08x\n",
- CMD_TFO(cmd)->get_task_tag(cmd));
-remove:
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
- if (!remove_cmd)
- return;
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- transport_generic_free_cmd(cmd, 0, 0, session_reinstatement);
+ return true;
}
+EXPORT_SYMBOL(transport_wait_for_tasks);
static int transport_get_sense_codes(
struct se_cmd *cmd,
@@ -5592,170 +2580,259 @@ static int transport_get_sense_codes(
return 0;
}
-static int transport_set_sense_codes(
- struct se_cmd *cmd,
- u8 asc,
- u8 ascq)
+static
+void transport_err_sector_info(unsigned char *buffer, sector_t bad_sector)
{
- cmd->scsi_asc = asc;
- cmd->scsi_ascq = ascq;
+ /* Place failed LBA in sense data information descriptor 0. */
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 0xc;
+ buffer[SPC_DESC_TYPE_OFFSET] = 0; /* Information */
+ buffer[SPC_ADDITIONAL_DESC_LEN_OFFSET] = 0xa;
+ buffer[SPC_VALIDITY_OFFSET] = 0x80;
- return 0;
+ /* Descriptor Information: failing sector */
+ put_unaligned_be64(bad_sector, &buffer[12]);
}
-int transport_send_check_condition_and_sense(
- struct se_cmd *cmd,
- u8 reason,
- int from_transport)
+int
+transport_send_check_condition_and_sense(struct se_cmd *cmd,
+ sense_reason_t reason, int from_transport)
{
unsigned char *buffer = cmd->sense_buffer;
unsigned long flags;
- int offset;
u8 asc = 0, ascq = 0;
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) {
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
return 0;
}
cmd->se_cmd_flags |= SCF_SENT_CHECK_CONDITION;
- spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
if (!reason && from_transport)
goto after_reason;
if (!from_transport)
cmd->se_cmd_flags |= SCF_EMULATED_TASK_SENSE;
- /*
- * Data Segment and SenseLength of the fabric response PDU.
- *
- * TRANSPORT_SENSE_BUFFER is now set to SCSI_SENSE_BUFFERSIZE
- * from include/scsi/scsi_cmnd.h
- */
- offset = CMD_TFO(cmd)->set_fabric_sense_len(cmd,
- TRANSPORT_SENSE_BUFFER);
+
/*
* Actual SENSE DATA, see SPC-3 7.23.2 SPC_SENSE_KEY_OFFSET uses
* SENSE KEY values from include/scsi/scsi.h
*/
switch (reason) {
+ case TCM_NO_SENSE:
+ /* CURRENT ERROR */
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+ /* Not Ready */
+ buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY;
+ /* NO ADDITIONAL SENSE INFORMATION */
+ buffer[SPC_ASC_KEY_OFFSET] = 0;
+ buffer[SPC_ASCQ_KEY_OFFSET] = 0;
+ break;
case TCM_NON_EXISTENT_LUN:
+ /* CURRENT ERROR */
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+ /* ILLEGAL REQUEST */
+ buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
+ /* LOGICAL UNIT NOT SUPPORTED */
+ buffer[SPC_ASC_KEY_OFFSET] = 0x25;
+ break;
case TCM_UNSUPPORTED_SCSI_OPCODE:
case TCM_SECTOR_COUNT_TOO_MANY:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
/* ILLEGAL REQUEST */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
+ buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
/* INVALID COMMAND OPERATION CODE */
- buffer[offset+SPC_ASC_KEY_OFFSET] = 0x20;
+ buffer[SPC_ASC_KEY_OFFSET] = 0x20;
break;
case TCM_UNKNOWN_MODE_PAGE:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
/* ILLEGAL REQUEST */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
+ buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
/* INVALID FIELD IN CDB */
- buffer[offset+SPC_ASC_KEY_OFFSET] = 0x24;
+ buffer[SPC_ASC_KEY_OFFSET] = 0x24;
break;
case TCM_CHECK_CONDITION_ABORT_CMD:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
/* ABORTED COMMAND */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
+ buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
/* BUS DEVICE RESET FUNCTION OCCURRED */
- buffer[offset+SPC_ASC_KEY_OFFSET] = 0x29;
- buffer[offset+SPC_ASCQ_KEY_OFFSET] = 0x03;
+ buffer[SPC_ASC_KEY_OFFSET] = 0x29;
+ buffer[SPC_ASCQ_KEY_OFFSET] = 0x03;
break;
case TCM_INCORRECT_AMOUNT_OF_DATA:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
/* ABORTED COMMAND */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
+ buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
/* WRITE ERROR */
- buffer[offset+SPC_ASC_KEY_OFFSET] = 0x0c;
+ buffer[SPC_ASC_KEY_OFFSET] = 0x0c;
/* NOT ENOUGH UNSOLICITED DATA */
- buffer[offset+SPC_ASCQ_KEY_OFFSET] = 0x0d;
+ buffer[SPC_ASCQ_KEY_OFFSET] = 0x0d;
break;
case TCM_INVALID_CDB_FIELD:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
- /* ABORTED COMMAND */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+ /* ILLEGAL REQUEST */
+ buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
/* INVALID FIELD IN CDB */
- buffer[offset+SPC_ASC_KEY_OFFSET] = 0x24;
+ buffer[SPC_ASC_KEY_OFFSET] = 0x24;
break;
case TCM_INVALID_PARAMETER_LIST:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
- /* ABORTED COMMAND */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+ /* ILLEGAL REQUEST */
+ buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
/* INVALID FIELD IN PARAMETER LIST */
- buffer[offset+SPC_ASC_KEY_OFFSET] = 0x26;
+ buffer[SPC_ASC_KEY_OFFSET] = 0x26;
+ break;
+ case TCM_PARAMETER_LIST_LENGTH_ERROR:
+ /* CURRENT ERROR */
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+ /* ILLEGAL REQUEST */
+ buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
+ /* PARAMETER LIST LENGTH ERROR */
+ buffer[SPC_ASC_KEY_OFFSET] = 0x1a;
break;
case TCM_UNEXPECTED_UNSOLICITED_DATA:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
/* ABORTED COMMAND */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
+ buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
/* WRITE ERROR */
- buffer[offset+SPC_ASC_KEY_OFFSET] = 0x0c;
+ buffer[SPC_ASC_KEY_OFFSET] = 0x0c;
/* UNEXPECTED_UNSOLICITED_DATA */
- buffer[offset+SPC_ASCQ_KEY_OFFSET] = 0x0c;
+ buffer[SPC_ASCQ_KEY_OFFSET] = 0x0c;
break;
case TCM_SERVICE_CRC_ERROR:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
/* ABORTED COMMAND */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
+ buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
/* PROTOCOL SERVICE CRC ERROR */
- buffer[offset+SPC_ASC_KEY_OFFSET] = 0x47;
+ buffer[SPC_ASC_KEY_OFFSET] = 0x47;
/* N/A */
- buffer[offset+SPC_ASCQ_KEY_OFFSET] = 0x05;
+ buffer[SPC_ASCQ_KEY_OFFSET] = 0x05;
break;
case TCM_SNACK_REJECTED:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
/* ABORTED COMMAND */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
+ buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
/* READ ERROR */
- buffer[offset+SPC_ASC_KEY_OFFSET] = 0x11;
+ buffer[SPC_ASC_KEY_OFFSET] = 0x11;
/* FAILED RETRANSMISSION REQUEST */
- buffer[offset+SPC_ASCQ_KEY_OFFSET] = 0x13;
+ buffer[SPC_ASCQ_KEY_OFFSET] = 0x13;
break;
case TCM_WRITE_PROTECTED:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
/* DATA PROTECT */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = DATA_PROTECT;
+ buffer[SPC_SENSE_KEY_OFFSET] = DATA_PROTECT;
/* WRITE PROTECTED */
- buffer[offset+SPC_ASC_KEY_OFFSET] = 0x27;
+ buffer[SPC_ASC_KEY_OFFSET] = 0x27;
+ break;
+ case TCM_ADDRESS_OUT_OF_RANGE:
+ /* CURRENT ERROR */
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+ /* ILLEGAL REQUEST */
+ buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
+ /* LOGICAL BLOCK ADDRESS OUT OF RANGE */
+ buffer[SPC_ASC_KEY_OFFSET] = 0x21;
break;
case TCM_CHECK_CONDITION_UNIT_ATTENTION:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
/* UNIT ATTENTION */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION;
+ buffer[SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION;
core_scsi3_ua_for_check_condition(cmd, &asc, &ascq);
- buffer[offset+SPC_ASC_KEY_OFFSET] = asc;
- buffer[offset+SPC_ASCQ_KEY_OFFSET] = ascq;
+ buffer[SPC_ASC_KEY_OFFSET] = asc;
+ buffer[SPC_ASCQ_KEY_OFFSET] = ascq;
break;
case TCM_CHECK_CONDITION_NOT_READY:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
/* Not Ready */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = NOT_READY;
+ buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY;
transport_get_sense_codes(cmd, &asc, &ascq);
- buffer[offset+SPC_ASC_KEY_OFFSET] = asc;
- buffer[offset+SPC_ASCQ_KEY_OFFSET] = ascq;
+ buffer[SPC_ASC_KEY_OFFSET] = asc;
+ buffer[SPC_ASCQ_KEY_OFFSET] = ascq;
+ break;
+ case TCM_MISCOMPARE_VERIFY:
+ /* CURRENT ERROR */
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+ buffer[SPC_SENSE_KEY_OFFSET] = MISCOMPARE;
+ /* MISCOMPARE DURING VERIFY OPERATION */
+ buffer[SPC_ASC_KEY_OFFSET] = 0x1d;
+ buffer[SPC_ASCQ_KEY_OFFSET] = 0x00;
+ break;
+ case TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED:
+ /* CURRENT ERROR */
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+ /* ILLEGAL REQUEST */
+ buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
+ /* LOGICAL BLOCK GUARD CHECK FAILED */
+ buffer[SPC_ASC_KEY_OFFSET] = 0x10;
+ buffer[SPC_ASCQ_KEY_OFFSET] = 0x01;
+ transport_err_sector_info(buffer, cmd->bad_sector);
+ break;
+ case TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED:
+ /* CURRENT ERROR */
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+ /* ILLEGAL REQUEST */
+ buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
+ /* LOGICAL BLOCK APPLICATION TAG CHECK FAILED */
+ buffer[SPC_ASC_KEY_OFFSET] = 0x10;
+ buffer[SPC_ASCQ_KEY_OFFSET] = 0x02;
+ transport_err_sector_info(buffer, cmd->bad_sector);
+ break;
+ case TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED:
+ /* CURRENT ERROR */
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+ /* ILLEGAL REQUEST */
+ buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
+ /* LOGICAL BLOCK REFERENCE TAG CHECK FAILED */
+ buffer[SPC_ASC_KEY_OFFSET] = 0x10;
+ buffer[SPC_ASCQ_KEY_OFFSET] = 0x03;
+ transport_err_sector_info(buffer, cmd->bad_sector);
break;
case TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE:
default:
/* CURRENT ERROR */
- buffer[offset] = 0x70;
- /* ILLEGAL REQUEST */
- buffer[offset+SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
+ buffer[0] = 0x70;
+ buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+ /*
+ * Returning ILLEGAL REQUEST would cause immediate IO errors on
+ * Solaris initiators. Returning NOT READY instead means the
+ * operations will be retried a finite number of times and we
+ * can survive intermittent errors.
+ */
+ buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY;
/* LOGICAL UNIT COMMUNICATION FAILURE */
- buffer[offset+SPC_ASC_KEY_OFFSET] = 0x80;
+ buffer[SPC_ASC_KEY_OFFSET] = 0x08;
break;
}
/*
@@ -5766,38 +2843,49 @@ int transport_send_check_condition_and_sense(
* Automatically padded, this value is encoded in the fabric's
* data_length response PDU containing the SCSI defined sense data.
*/
- cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER + offset;
+ cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER;
after_reason:
- CMD_TFO(cmd)->queue_status(cmd);
- return 0;
+ trace_target_cmd_complete(cmd);
+ return cmd->se_tfo->queue_status(cmd);
}
EXPORT_SYMBOL(transport_send_check_condition_and_sense);
int transport_check_aborted_status(struct se_cmd *cmd, int send_status)
{
- int ret = 0;
+ if (!(cmd->transport_state & CMD_T_ABORTED))
+ return 0;
- if (atomic_read(&T_TASK(cmd)->t_transport_aborted) != 0) {
- if (!(send_status) ||
- (cmd->se_cmd_flags & SCF_SENT_DELAYED_TAS))
- return 1;
-#if 0
- printk(KERN_INFO "Sending delayed SAM_STAT_TASK_ABORTED"
- " status for CDB: 0x%02x ITT: 0x%08x\n",
- T_TASK(cmd)->t_task_cdb[0],
- CMD_TFO(cmd)->get_task_tag(cmd));
-#endif
- cmd->se_cmd_flags |= SCF_SENT_DELAYED_TAS;
- CMD_TFO(cmd)->queue_status(cmd);
- ret = 1;
- }
- return ret;
+ /*
+ * If cmd has been aborted but either no status is to be sent or it has
+ * already been sent, just return
+ */
+ if (!send_status || !(cmd->se_cmd_flags & SCF_SEND_DELAYED_TAS))
+ return 1;
+
+ pr_debug("Sending delayed SAM_STAT_TASK_ABORTED status for CDB: 0x%02x ITT: 0x%08x\n",
+ cmd->t_task_cdb[0], cmd->se_tfo->get_task_tag(cmd));
+
+ cmd->se_cmd_flags &= ~SCF_SEND_DELAYED_TAS;
+ cmd->scsi_status = SAM_STAT_TASK_ABORTED;
+ trace_target_cmd_complete(cmd);
+ cmd->se_tfo->queue_status(cmd);
+
+ return 1;
}
EXPORT_SYMBOL(transport_check_aborted_status);
void transport_send_task_abort(struct se_cmd *cmd)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ if (cmd->se_cmd_flags & (SCF_SENT_CHECK_CONDITION)) {
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+
/*
* If there are still expected incoming fabric WRITEs, we wait
* until until they have completed before sending a TASK_ABORTED
@@ -5805,360 +2893,76 @@ void transport_send_task_abort(struct se_cmd *cmd)
* queued back to fabric module by transport_check_aborted_status().
*/
if (cmd->data_direction == DMA_TO_DEVICE) {
- if (CMD_TFO(cmd)->write_pending_status(cmd) != 0) {
- atomic_inc(&T_TASK(cmd)->t_transport_aborted);
- smp_mb__after_atomic_inc();
- cmd->scsi_status = SAM_STAT_TASK_ABORTED;
- transport_new_cmd_failure(cmd);
+ if (cmd->se_tfo->write_pending_status(cmd) != 0) {
+ cmd->transport_state |= CMD_T_ABORTED;
+ cmd->se_cmd_flags |= SCF_SEND_DELAYED_TAS;
+ smp_mb__after_atomic();
return;
}
}
cmd->scsi_status = SAM_STAT_TASK_ABORTED;
-#if 0
- printk(KERN_INFO "Setting SAM_STAT_TASK_ABORTED status for CDB: 0x%02x,"
- " ITT: 0x%08x\n", T_TASK(cmd)->t_task_cdb[0],
- CMD_TFO(cmd)->get_task_tag(cmd));
-#endif
- CMD_TFO(cmd)->queue_status(cmd);
+
+ transport_lun_remove_cmd(cmd);
+
+ pr_debug("Setting SAM_STAT_TASK_ABORTED status for CDB: 0x%02x,"
+ " ITT: 0x%08x\n", cmd->t_task_cdb[0],
+ cmd->se_tfo->get_task_tag(cmd));
+
+ trace_target_cmd_complete(cmd);
+ cmd->se_tfo->queue_status(cmd);
}
-/* transport_generic_do_tmr():
- *
- *
- */
-int transport_generic_do_tmr(struct se_cmd *cmd)
+static void target_tmr_work(struct work_struct *work)
{
- struct se_cmd *ref_cmd;
- struct se_device *dev = SE_DEV(cmd);
+ struct se_cmd *cmd = container_of(work, struct se_cmd, work);
+ struct se_device *dev = cmd->se_dev;
struct se_tmr_req *tmr = cmd->se_tmr_req;
int ret;
switch (tmr->function) {
- case ABORT_TASK:
- ref_cmd = tmr->ref_cmd;
- tmr->response = TMR_FUNCTION_REJECTED;
+ case TMR_ABORT_TASK:
+ core_tmr_abort_task(dev, tmr, cmd->se_sess);
break;
- case ABORT_TASK_SET:
- case CLEAR_ACA:
- case CLEAR_TASK_SET:
+ case TMR_ABORT_TASK_SET:
+ case TMR_CLEAR_ACA:
+ case TMR_CLEAR_TASK_SET:
tmr->response = TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED;
break;
- case LUN_RESET:
+ case TMR_LUN_RESET:
ret = core_tmr_lun_reset(dev, tmr, NULL, NULL);
tmr->response = (!ret) ? TMR_FUNCTION_COMPLETE :
TMR_FUNCTION_REJECTED;
break;
-#if 0
- case TARGET_WARM_RESET:
- transport_generic_host_reset(dev->se_hba);
+ case TMR_TARGET_WARM_RESET:
tmr->response = TMR_FUNCTION_REJECTED;
break;
- case TARGET_COLD_RESET:
- transport_generic_host_reset(dev->se_hba);
- transport_generic_cold_reset(dev->se_hba);
+ case TMR_TARGET_COLD_RESET:
tmr->response = TMR_FUNCTION_REJECTED;
break;
-#endif
default:
- printk(KERN_ERR "Uknown TMR function: 0x%02x.\n",
+ pr_err("Uknown TMR function: 0x%02x.\n",
tmr->function);
tmr->response = TMR_FUNCTION_REJECTED;
break;
}
cmd->t_state = TRANSPORT_ISTATE_PROCESSING;
- CMD_TFO(cmd)->queue_tm_rsp(cmd);
-
- transport_cmd_check_stop(cmd, 2, 0);
- return 0;
-}
-
-/*
- * Called with spin_lock_irq(&dev->execute_task_lock); held
- *
- */
-static struct se_task *
-transport_get_task_from_state_list(struct se_device *dev)
-{
- struct se_task *task;
+ cmd->se_tfo->queue_tm_rsp(cmd);
- if (list_empty(&dev->state_task_list))
- return NULL;
-
- list_for_each_entry(task, &dev->state_task_list, t_state_list)
- break;
-
- list_del(&task->t_state_list);
- atomic_set(&task->task_state_active, 0);
-
- return task;
+ transport_cmd_check_stop_to_fabric(cmd);
}
-static void transport_processing_shutdown(struct se_device *dev)
+int transport_generic_handle_tmr(
+ struct se_cmd *cmd)
{
- struct se_cmd *cmd;
- struct se_queue_req *qr;
- struct se_task *task;
- u8 state;
unsigned long flags;
- /*
- * Empty the struct se_device's struct se_task state list.
- */
- spin_lock_irqsave(&dev->execute_task_lock, flags);
- while ((task = transport_get_task_from_state_list(dev))) {
- if (!(TASK_CMD(task))) {
- printk(KERN_ERR "TASK_CMD(task) is NULL!\n");
- continue;
- }
- cmd = TASK_CMD(task);
- if (!T_TASK(cmd)) {
- printk(KERN_ERR "T_TASK(cmd) is NULL for task: %p cmd:"
- " %p ITT: 0x%08x\n", task, cmd,
- CMD_TFO(cmd)->get_task_tag(cmd));
- continue;
- }
- spin_unlock_irqrestore(&dev->execute_task_lock, flags);
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
-
- DEBUG_DO("PT: cmd: %p task: %p ITT/CmdSN: 0x%08x/0x%08x,"
- " i_state/def_i_state: %d/%d, t_state/def_t_state:"
- " %d/%d cdb: 0x%02x\n", cmd, task,
- CMD_TFO(cmd)->get_task_tag(cmd), cmd->cmd_sn,
- CMD_TFO(cmd)->get_cmd_state(cmd), cmd->deferred_i_state,
- cmd->t_state, cmd->deferred_t_state,
- T_TASK(cmd)->t_task_cdb[0]);
- DEBUG_DO("PT: ITT[0x%08x] - t_task_cdbs: %d t_task_cdbs_left:"
- " %d t_task_cdbs_sent: %d -- t_transport_active: %d"
- " t_transport_stop: %d t_transport_sent: %d\n",
- CMD_TFO(cmd)->get_task_tag(cmd),
- T_TASK(cmd)->t_task_cdbs,
- atomic_read(&T_TASK(cmd)->t_task_cdbs_left),
- atomic_read(&T_TASK(cmd)->t_task_cdbs_sent),
- atomic_read(&T_TASK(cmd)->t_transport_active),
- atomic_read(&T_TASK(cmd)->t_transport_stop),
- atomic_read(&T_TASK(cmd)->t_transport_sent));
-
- if (atomic_read(&task->task_active)) {
- atomic_set(&task->task_stop, 1);
- spin_unlock_irqrestore(
- &T_TASK(cmd)->t_state_lock, flags);
-
- DEBUG_DO("Waiting for task: %p to shutdown for dev:"
- " %p\n", task, dev);
- wait_for_completion(&task->task_stop_comp);
- DEBUG_DO("Completed task: %p shutdown for dev: %p\n",
- task, dev);
-
- spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
- atomic_dec(&T_TASK(cmd)->t_task_cdbs_left);
-
- atomic_set(&task->task_active, 0);
- atomic_set(&task->task_stop, 0);
- }
- __transport_stop_task_timer(task, &flags);
-
- if (!(atomic_dec_and_test(&T_TASK(cmd)->t_task_cdbs_ex_left))) {
- spin_unlock_irqrestore(
- &T_TASK(cmd)->t_state_lock, flags);
-
- DEBUG_DO("Skipping task: %p, dev: %p for"
- " t_task_cdbs_ex_left: %d\n", task, dev,
- atomic_read(&T_TASK(cmd)->t_task_cdbs_ex_left));
-
- spin_lock_irqsave(&dev->execute_task_lock, flags);
- continue;
- }
-
- if (atomic_read(&T_TASK(cmd)->t_transport_active)) {
- DEBUG_DO("got t_transport_active = 1 for task: %p, dev:"
- " %p\n", task, dev);
-
- if (atomic_read(&T_TASK(cmd)->t_fe_count)) {
- spin_unlock_irqrestore(
- &T_TASK(cmd)->t_state_lock, flags);
- transport_send_check_condition_and_sense(
- cmd, TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE,
- 0);
- transport_remove_cmd_from_queue(cmd,
- SE_DEV(cmd)->dev_queue_obj);
-
- transport_lun_remove_cmd(cmd);
- transport_cmd_check_stop(cmd, 1, 0);
- } else {
- spin_unlock_irqrestore(
- &T_TASK(cmd)->t_state_lock, flags);
-
- transport_remove_cmd_from_queue(cmd,
- SE_DEV(cmd)->dev_queue_obj);
-
- transport_lun_remove_cmd(cmd);
-
- if (transport_cmd_check_stop(cmd, 1, 0))
- transport_generic_remove(cmd, 0, 0);
- }
-
- spin_lock_irqsave(&dev->execute_task_lock, flags);
- continue;
- }
- DEBUG_DO("Got t_transport_active = 0 for task: %p, dev: %p\n",
- task, dev);
+ spin_lock_irqsave(&cmd->t_state_lock, flags);
+ cmd->transport_state |= CMD_T_ACTIVE;
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- if (atomic_read(&T_TASK(cmd)->t_fe_count)) {
- spin_unlock_irqrestore(
- &T_TASK(cmd)->t_state_lock, flags);
- transport_send_check_condition_and_sense(cmd,
- TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
- transport_remove_cmd_from_queue(cmd,
- SE_DEV(cmd)->dev_queue_obj);
-
- transport_lun_remove_cmd(cmd);
- transport_cmd_check_stop(cmd, 1, 0);
- } else {
- spin_unlock_irqrestore(
- &T_TASK(cmd)->t_state_lock, flags);
-
- transport_remove_cmd_from_queue(cmd,
- SE_DEV(cmd)->dev_queue_obj);
- transport_lun_remove_cmd(cmd);
-
- if (transport_cmd_check_stop(cmd, 1, 0))
- transport_generic_remove(cmd, 0, 0);
- }
-
- spin_lock_irqsave(&dev->execute_task_lock, flags);
- }
- spin_unlock_irqrestore(&dev->execute_task_lock, flags);
- /*
- * Empty the struct se_device's struct se_cmd list.
- */
- spin_lock_irqsave(&dev->dev_queue_obj->cmd_queue_lock, flags);
- while ((qr = __transport_get_qr_from_queue(dev->dev_queue_obj))) {
- spin_unlock_irqrestore(
- &dev->dev_queue_obj->cmd_queue_lock, flags);
- cmd = (struct se_cmd *)qr->cmd;
- state = qr->state;
- kfree(qr);
-
- DEBUG_DO("From Device Queue: cmd: %p t_state: %d\n",
- cmd, state);
-
- if (atomic_read(&T_TASK(cmd)->t_fe_count)) {
- transport_send_check_condition_and_sense(cmd,
- TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
-
- transport_lun_remove_cmd(cmd);
- transport_cmd_check_stop(cmd, 1, 0);
- } else {
- transport_lun_remove_cmd(cmd);
- if (transport_cmd_check_stop(cmd, 1, 0))
- transport_generic_remove(cmd, 0, 0);
- }
- spin_lock_irqsave(&dev->dev_queue_obj->cmd_queue_lock, flags);
- }
- spin_unlock_irqrestore(&dev->dev_queue_obj->cmd_queue_lock, flags);
-}
-
-/* transport_processing_thread():
- *
- *
- */
-static int transport_processing_thread(void *param)
-{
- int ret, t_state;
- struct se_cmd *cmd;
- struct se_device *dev = (struct se_device *) param;
- struct se_queue_req *qr;
-
- set_user_nice(current, -20);
-
- while (!kthread_should_stop()) {
- ret = wait_event_interruptible(dev->dev_queue_obj->thread_wq,
- atomic_read(&dev->dev_queue_obj->queue_cnt) ||
- kthread_should_stop());
- if (ret < 0)
- goto out;
-
- spin_lock_irq(&dev->dev_status_lock);
- if (dev->dev_status & TRANSPORT_DEVICE_SHUTDOWN) {
- spin_unlock_irq(&dev->dev_status_lock);
- transport_processing_shutdown(dev);
- continue;
- }
- spin_unlock_irq(&dev->dev_status_lock);
-
-get_cmd:
- __transport_execute_tasks(dev);
-
- qr = transport_get_qr_from_queue(dev->dev_queue_obj);
- if (!(qr))
- continue;
-
- cmd = (struct se_cmd *)qr->cmd;
- t_state = qr->state;
- kfree(qr);
-
- switch (t_state) {
- case TRANSPORT_NEW_CMD_MAP:
- if (!(CMD_TFO(cmd)->new_cmd_map)) {
- printk(KERN_ERR "CMD_TFO(cmd)->new_cmd_map is"
- " NULL for TRANSPORT_NEW_CMD_MAP\n");
- BUG();
- }
- ret = CMD_TFO(cmd)->new_cmd_map(cmd);
- if (ret < 0) {
- cmd->transport_error_status = ret;
- transport_generic_request_failure(cmd, NULL,
- 0, (cmd->data_direction !=
- DMA_TO_DEVICE));
- break;
- }
- /* Fall through */
- case TRANSPORT_NEW_CMD:
- ret = transport_generic_new_cmd(cmd);
- if (ret < 0) {
- cmd->transport_error_status = ret;
- transport_generic_request_failure(cmd, NULL,
- 0, (cmd->data_direction !=
- DMA_TO_DEVICE));
- }
- break;
- case TRANSPORT_PROCESS_WRITE:
- transport_generic_process_write(cmd);
- break;
- case TRANSPORT_COMPLETE_OK:
- transport_stop_all_task_timers(cmd);
- transport_generic_complete_ok(cmd);
- break;
- case TRANSPORT_REMOVE:
- transport_generic_remove(cmd, 1, 0);
- break;
- case TRANSPORT_PROCESS_TMR:
- transport_generic_do_tmr(cmd);
- break;
- case TRANSPORT_COMPLETE_FAILURE:
- transport_generic_request_failure(cmd, NULL, 1, 1);
- break;
- case TRANSPORT_COMPLETE_TIMEOUT:
- transport_stop_all_task_timers(cmd);
- transport_generic_request_timeout(cmd);
- break;
- default:
- printk(KERN_ERR "Unknown t_state: %d deferred_t_state:"
- " %d for ITT: 0x%08x i_state: %d on SE LUN:"
- " %u\n", t_state, cmd->deferred_t_state,
- CMD_TFO(cmd)->get_task_tag(cmd),
- CMD_TFO(cmd)->get_cmd_state(cmd),
- SE_LUN(cmd)->unpacked_lun);
- BUG();
- }
-
- goto get_cmd;
- }
-
-out:
- transport_release_all_cmds(dev);
- dev->process_thread = NULL;
+ INIT_WORK(&cmd->work, target_tmr_work);
+ queue_work(cmd->se_dev->tmr_wq, &cmd->work);
return 0;
}
+EXPORT_SYMBOL(transport_generic_handle_tmr);
diff --git a/drivers/target/target_core_ua.c b/drivers/target/target_core_ua.c
index a2ef346087e..101858e245b 100644
--- a/drivers/target/target_core_ua.c
+++ b/drivers/target/target_core_ua.c
@@ -3,8 +3,7 @@
*
* This file contains logic for SPC-3 Unit Attention emulation
*
- * Copyright (c) 2009,2010 Rising Tide Systems
- * Copyright (c) 2009,2010 Linux-iSCSI.org
+ * (c) Copyright 2009-2013 Datera, Inc.
*
* Nicholas A. Bellinger <nab@kernel.org>
*
@@ -24,40 +23,36 @@
*
******************************************************************************/
-#include <linux/version.h>
#include <linux/slab.h>
#include <linux/spinlock.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_fabric.h>
#include <target/target_core_configfs.h>
+#include "target_core_internal.h"
#include "target_core_alua.h"
-#include "target_core_hba.h"
#include "target_core_pr.h"
#include "target_core_ua.h"
-int core_scsi3_ua_check(
- struct se_cmd *cmd,
- unsigned char *cdb)
+sense_reason_t
+target_scsi3_ua_check(struct se_cmd *cmd)
{
struct se_dev_entry *deve;
struct se_session *sess = cmd->se_sess;
struct se_node_acl *nacl;
- if (!(sess))
+ if (!sess)
return 0;
nacl = sess->se_node_acl;
- if (!(nacl))
+ if (!nacl)
return 0;
- deve = &nacl->device_list[cmd->orig_fe_lun];
- if (!(atomic_read(&deve->ua_count)))
+ deve = nacl->device_list[cmd->orig_fe_lun];
+ if (!atomic_read(&deve->ua_count))
return 0;
/*
* From sam4r14, section 5.14 Unit attention condition:
@@ -74,16 +69,14 @@ int core_scsi3_ua_check(
* was received, then the device server shall process the command
* and either:
*/
- switch (cdb[0]) {
+ switch (cmd->t_task_cdb[0]) {
case INQUIRY:
case REPORT_LUNS:
case REQUEST_SENSE:
return 0;
default:
- return -1;
+ return TCM_CHECK_CONDITION_UNIT_ATTENTION;
}
-
- return -1;
}
int core_scsi3_ua_allocate(
@@ -97,15 +90,14 @@ int core_scsi3_ua_allocate(
/*
* PASSTHROUGH OPS
*/
- if (!(nacl))
- return -1;
+ if (!nacl)
+ return -EINVAL;
ua = kmem_cache_zalloc(se_ua_cache, GFP_ATOMIC);
- if (!(ua)) {
- printk(KERN_ERR "Unable to allocate struct se_ua\n");
- return -1;
+ if (!ua) {
+ pr_err("Unable to allocate struct se_ua\n");
+ return -ENOMEM;
}
- INIT_LIST_HEAD(&ua->ua_dev_list);
INIT_LIST_HEAD(&ua->ua_nacl_list);
ua->ua_nacl = nacl;
@@ -113,7 +105,7 @@ int core_scsi3_ua_allocate(
ua->ua_ascq = ascq;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[unpacked_lun];
+ deve = nacl->device_list[unpacked_lun];
spin_lock(&deve->ua_lock);
list_for_each_entry_safe(ua_p, ua_tmp, &deve->ua_list, ua_nacl_list) {
@@ -170,20 +162,20 @@ int core_scsi3_ua_allocate(
spin_unlock_irq(&nacl->device_list_lock);
atomic_inc(&deve->ua_count);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
return 0;
}
list_add_tail(&ua->ua_nacl_list, &deve->ua_list);
spin_unlock(&deve->ua_lock);
spin_unlock_irq(&nacl->device_list_lock);
- printk(KERN_INFO "[%s]: Allocated UNIT ATTENTION, mapped LUN: %u, ASC:"
+ pr_debug("[%s]: Allocated UNIT ATTENTION, mapped LUN: %u, ASC:"
" 0x%02x, ASCQ: 0x%02x\n",
- TPG_TFO(nacl->se_tpg)->get_fabric_name(), unpacked_lun,
+ nacl->se_tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun,
asc, ascq);
atomic_inc(&deve->ua_count);
- smp_mb__after_atomic_inc();
+ smp_mb__after_atomic();
return 0;
}
@@ -198,7 +190,7 @@ void core_scsi3_ua_release_all(
kmem_cache_free(se_ua_cache, ua);
atomic_dec(&deve->ua_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
}
spin_unlock(&deve->ua_lock);
}
@@ -208,23 +200,23 @@ void core_scsi3_ua_for_check_condition(
u8 *asc,
u8 *ascq)
{
- struct se_device *dev = SE_DEV(cmd);
+ struct se_device *dev = cmd->se_dev;
struct se_dev_entry *deve;
struct se_session *sess = cmd->se_sess;
struct se_node_acl *nacl;
struct se_ua *ua = NULL, *ua_p;
int head = 1;
- if (!(sess))
+ if (!sess)
return;
nacl = sess->se_node_acl;
- if (!(nacl))
+ if (!nacl)
return;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[cmd->orig_fe_lun];
- if (!(atomic_read(&deve->ua_count))) {
+ deve = nacl->device_list[cmd->orig_fe_lun];
+ if (!atomic_read(&deve->ua_count)) {
spin_unlock_irq(&nacl->device_list_lock);
return;
}
@@ -240,14 +232,14 @@ void core_scsi3_ua_for_check_condition(
* highest priority UNIT_ATTENTION and ASC/ASCQ without
* clearing it.
*/
- if (DEV_ATTRIB(dev)->emulate_ua_intlck_ctrl != 0) {
+ if (dev->dev_attrib.emulate_ua_intlck_ctrl != 0) {
*asc = ua->ua_asc;
*ascq = ua->ua_ascq;
break;
}
/*
* Otherwise for the default 00b, release the UNIT ATTENTION
- * condition. Return the ASC/ASCQ of the higest priority UA
+ * condition. Return the ASC/ASCQ of the highest priority UA
* (head of the list) in the outgoing CHECK_CONDITION + sense.
*/
if (head) {
@@ -259,18 +251,18 @@ void core_scsi3_ua_for_check_condition(
kmem_cache_free(se_ua_cache, ua);
atomic_dec(&deve->ua_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
}
spin_unlock(&deve->ua_lock);
spin_unlock_irq(&nacl->device_list_lock);
- printk(KERN_INFO "[%s]: %s UNIT ATTENTION condition with"
+ pr_debug("[%s]: %s UNIT ATTENTION condition with"
" INTLCK_CTRL: %d, mapped LUN: %u, got CDB: 0x%02x"
" reported ASC: 0x%02x, ASCQ: 0x%02x\n",
- TPG_TFO(nacl->se_tpg)->get_fabric_name(),
- (DEV_ATTRIB(dev)->emulate_ua_intlck_ctrl != 0) ? "Reporting" :
- "Releasing", DEV_ATTRIB(dev)->emulate_ua_intlck_ctrl,
- cmd->orig_fe_lun, T_TASK(cmd)->t_task_cdb[0], *asc, *ascq);
+ nacl->se_tpg->se_tpg_tfo->get_fabric_name(),
+ (dev->dev_attrib.emulate_ua_intlck_ctrl != 0) ? "Reporting" :
+ "Releasing", dev->dev_attrib.emulate_ua_intlck_ctrl,
+ cmd->orig_fe_lun, cmd->t_task_cdb[0], *asc, *ascq);
}
int core_scsi3_ua_clear_for_request_sense(
@@ -284,18 +276,18 @@ int core_scsi3_ua_clear_for_request_sense(
struct se_ua *ua = NULL, *ua_p;
int head = 1;
- if (!(sess))
- return -1;
+ if (!sess)
+ return -EINVAL;
nacl = sess->se_node_acl;
- if (!(nacl))
- return -1;
+ if (!nacl)
+ return -EINVAL;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[cmd->orig_fe_lun];
- if (!(atomic_read(&deve->ua_count))) {
+ deve = nacl->device_list[cmd->orig_fe_lun];
+ if (!atomic_read(&deve->ua_count)) {
spin_unlock_irq(&nacl->device_list_lock);
- return -1;
+ return -EPERM;
}
/*
* The highest priority Unit Attentions are placed at the head of the
@@ -304,7 +296,7 @@ int core_scsi3_ua_clear_for_request_sense(
* matching struct se_lun.
*
* Once the returning ASC/ASCQ values are set, we go ahead and
- * release all of the Unit Attention conditions for the assoicated
+ * release all of the Unit Attention conditions for the associated
* struct se_lun.
*/
spin_lock(&deve->ua_lock);
@@ -318,15 +310,15 @@ int core_scsi3_ua_clear_for_request_sense(
kmem_cache_free(se_ua_cache, ua);
atomic_dec(&deve->ua_count);
- smp_mb__after_atomic_dec();
+ smp_mb__after_atomic();
}
spin_unlock(&deve->ua_lock);
spin_unlock_irq(&nacl->device_list_lock);
- printk(KERN_INFO "[%s]: Released UNIT ATTENTION condition, mapped"
+ pr_debug("[%s]: Released UNIT ATTENTION condition, mapped"
" LUN: %u, got REQUEST_SENSE reported ASC: 0x%02x,"
- " ASCQ: 0x%02x\n", TPG_TFO(nacl->se_tpg)->get_fabric_name(),
+ " ASCQ: 0x%02x\n", nacl->se_tpg->se_tpg_tfo->get_fabric_name(),
cmd->orig_fe_lun, *asc, *ascq);
- return (head) ? -1 : 0;
+ return (head) ? -EPERM : 0;
}
diff --git a/drivers/target/target_core_ua.h b/drivers/target/target_core_ua.h
index 6e6b03460a1..be912b36daa 100644
--- a/drivers/target/target_core_ua.h
+++ b/drivers/target/target_core_ua.h
@@ -19,14 +19,14 @@
#define ASCQ_2AH_RESERVATIONS_RELEASED 0x04
#define ASCQ_2AH_REGISTRATIONS_PREEMPTED 0x05
#define ASCQ_2AH_ASYMMETRIC_ACCESS_STATE_CHANGED 0x06
-#define ASCQ_2AH_IMPLICT_ASYMMETRIC_ACCESS_STATE_TRANSITION_FAILED 0x07
+#define ASCQ_2AH_IMPLICIT_ASYMMETRIC_ACCESS_STATE_TRANSITION_FAILED 0x07
#define ASCQ_2AH_PRIORITY_CHANGED 0x08
#define ASCQ_2CH_PREVIOUS_RESERVATION_CONFLICT_STATUS 0x09
extern struct kmem_cache *se_ua_cache;
-extern int core_scsi3_ua_check(struct se_cmd *, unsigned char *);
+extern sense_reason_t target_scsi3_ua_check(struct se_cmd *);
extern int core_scsi3_ua_allocate(struct se_node_acl *, u32, u8, u8);
extern void core_scsi3_ua_release_all(struct se_dev_entry *);
extern void core_scsi3_ua_for_check_condition(struct se_cmd *, u8 *, u8 *);
diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c
new file mode 100644
index 00000000000..e9186cdf35e
--- /dev/null
+++ b/drivers/target/target_core_xcopy.c
@@ -0,0 +1,1081 @@
+/*******************************************************************************
+ * Filename: target_core_xcopy.c
+ *
+ * This file contains support for SPC-4 Extended-Copy offload with generic
+ * TCM backends.
+ *
+ * Copyright (c) 2011-2013 Datera, Inc. All rights reserved.
+ *
+ * Author:
+ * Nicholas A. Bellinger <nab@daterainc.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ ******************************************************************************/
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/configfs.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <asm/unaligned.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_backend.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+
+#include "target_core_pr.h"
+#include "target_core_ua.h"
+#include "target_core_xcopy.h"
+
+static struct workqueue_struct *xcopy_wq = NULL;
+/*
+ * From target_core_device.c
+ */
+extern struct mutex g_device_mutex;
+extern struct list_head g_device_list;
+/*
+ * From target_core_configfs.c
+ */
+extern struct configfs_subsystem *target_core_subsystem[];
+
+static int target_xcopy_gen_naa_ieee(struct se_device *dev, unsigned char *buf)
+{
+ int off = 0;
+
+ buf[off++] = (0x6 << 4);
+ buf[off++] = 0x01;
+ buf[off++] = 0x40;
+ buf[off] = (0x5 << 4);
+
+ spc_parse_naa_6h_vendor_specific(dev, &buf[off]);
+ return 0;
+}
+
+static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op *xop,
+ bool src)
+{
+ struct se_device *se_dev;
+ struct configfs_subsystem *subsys = target_core_subsystem[0];
+ unsigned char tmp_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN], *dev_wwn;
+ int rc;
+
+ if (src)
+ dev_wwn = &xop->dst_tid_wwn[0];
+ else
+ dev_wwn = &xop->src_tid_wwn[0];
+
+ mutex_lock(&g_device_mutex);
+ list_for_each_entry(se_dev, &g_device_list, g_dev_node) {
+
+ if (!se_dev->dev_attrib.emulate_3pc)
+ continue;
+
+ memset(&tmp_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN);
+ target_xcopy_gen_naa_ieee(se_dev, &tmp_dev_wwn[0]);
+
+ rc = memcmp(&tmp_dev_wwn[0], dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN);
+ if (rc != 0)
+ continue;
+
+ if (src) {
+ xop->dst_dev = se_dev;
+ pr_debug("XCOPY 0xe4: Setting xop->dst_dev: %p from located"
+ " se_dev\n", xop->dst_dev);
+ } else {
+ xop->src_dev = se_dev;
+ pr_debug("XCOPY 0xe4: Setting xop->src_dev: %p from located"
+ " se_dev\n", xop->src_dev);
+ }
+
+ rc = configfs_depend_item(subsys,
+ &se_dev->dev_group.cg_item);
+ if (rc != 0) {
+ pr_err("configfs_depend_item attempt failed:"
+ " %d for se_dev: %p\n", rc, se_dev);
+ mutex_unlock(&g_device_mutex);
+ return rc;
+ }
+
+ pr_debug("Called configfs_depend_item for subsys: %p se_dev: %p"
+ " se_dev->se_dev_group: %p\n", subsys, se_dev,
+ &se_dev->dev_group);
+
+ mutex_unlock(&g_device_mutex);
+ return 0;
+ }
+ mutex_unlock(&g_device_mutex);
+
+ pr_err("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
+ return -EINVAL;
+}
+
+static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op *xop,
+ unsigned char *p, bool src)
+{
+ unsigned char *desc = p;
+ unsigned short ript;
+ u8 desig_len;
+ /*
+ * Extract RELATIVE INITIATOR PORT IDENTIFIER
+ */
+ ript = get_unaligned_be16(&desc[2]);
+ pr_debug("XCOPY 0xe4: RELATIVE INITIATOR PORT IDENTIFIER: %hu\n", ript);
+ /*
+ * Check for supported code set, association, and designator type
+ */
+ if ((desc[4] & 0x0f) != 0x1) {
+ pr_err("XCOPY 0xe4: code set of non binary type not supported\n");
+ return -EINVAL;
+ }
+ if ((desc[5] & 0x30) != 0x00) {
+ pr_err("XCOPY 0xe4: association other than LUN not supported\n");
+ return -EINVAL;
+ }
+ if ((desc[5] & 0x0f) != 0x3) {
+ pr_err("XCOPY 0xe4: designator type unsupported: 0x%02x\n",
+ (desc[5] & 0x0f));
+ return -EINVAL;
+ }
+ /*
+ * Check for matching 16 byte length for NAA IEEE Registered Extended
+ * Assigned designator
+ */
+ desig_len = desc[7];
+ if (desig_len != 16) {
+ pr_err("XCOPY 0xe4: invalid desig_len: %d\n", (int)desig_len);
+ return -EINVAL;
+ }
+ pr_debug("XCOPY 0xe4: desig_len: %d\n", (int)desig_len);
+ /*
+ * Check for NAA IEEE Registered Extended Assigned header..
+ */
+ if ((desc[8] & 0xf0) != 0x60) {
+ pr_err("XCOPY 0xe4: Unsupported DESIGNATOR TYPE: 0x%02x\n",
+ (desc[8] & 0xf0));
+ return -EINVAL;
+ }
+
+ if (src) {
+ memcpy(&xop->src_tid_wwn[0], &desc[8], XCOPY_NAA_IEEE_REGEX_LEN);
+ /*
+ * Determine if the source designator matches the local device
+ */
+ if (!memcmp(&xop->local_dev_wwn[0], &xop->src_tid_wwn[0],
+ XCOPY_NAA_IEEE_REGEX_LEN)) {
+ xop->op_origin = XCOL_SOURCE_RECV_OP;
+ xop->src_dev = se_cmd->se_dev;
+ pr_debug("XCOPY 0xe4: Set xop->src_dev %p from source"
+ " received xop\n", xop->src_dev);
+ }
+ } else {
+ memcpy(&xop->dst_tid_wwn[0], &desc[8], XCOPY_NAA_IEEE_REGEX_LEN);
+ /*
+ * Determine if the destination designator matches the local device
+ */
+ if (!memcmp(&xop->local_dev_wwn[0], &xop->dst_tid_wwn[0],
+ XCOPY_NAA_IEEE_REGEX_LEN)) {
+ xop->op_origin = XCOL_DEST_RECV_OP;
+ xop->dst_dev = se_cmd->se_dev;
+ pr_debug("XCOPY 0xe4: Set xop->dst_dev: %p from destination"
+ " received xop\n", xop->dst_dev);
+ }
+ }
+
+ return 0;
+}
+
+static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
+ struct xcopy_op *xop, unsigned char *p,
+ unsigned short tdll)
+{
+ struct se_device *local_dev = se_cmd->se_dev;
+ unsigned char *desc = p;
+ int offset = tdll % XCOPY_TARGET_DESC_LEN, rc, ret = 0;
+ unsigned short start = 0;
+ bool src = true;
+
+ if (offset != 0) {
+ pr_err("XCOPY target descriptor list length is not"
+ " multiple of %d\n", XCOPY_TARGET_DESC_LEN);
+ return -EINVAL;
+ }
+ if (tdll > 64) {
+ pr_err("XCOPY target descriptor supports a maximum"
+ " two src/dest descriptors, tdll: %hu too large..\n", tdll);
+ return -EINVAL;
+ }
+ /*
+ * Generate an IEEE Registered Extended designator based upon the
+ * se_device the XCOPY was received upon..
+ */
+ memset(&xop->local_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN);
+ target_xcopy_gen_naa_ieee(local_dev, &xop->local_dev_wwn[0]);
+
+ while (start < tdll) {
+ /*
+ * Check target descriptor identification with 0xE4 type with
+ * use VPD 0x83 WWPN matching ..
+ */
+ switch (desc[0]) {
+ case 0xe4:
+ rc = target_xcopy_parse_tiddesc_e4(se_cmd, xop,
+ &desc[0], src);
+ if (rc != 0)
+ goto out;
+ /*
+ * Assume target descriptors are in source -> destination order..
+ */
+ if (src)
+ src = false;
+ else
+ src = true;
+ start += XCOPY_TARGET_DESC_LEN;
+ desc += XCOPY_TARGET_DESC_LEN;
+ ret++;
+ break;
+ default:
+ pr_err("XCOPY unsupported descriptor type code:"
+ " 0x%02x\n", desc[0]);
+ goto out;
+ }
+ }
+
+ if (xop->op_origin == XCOL_SOURCE_RECV_OP)
+ rc = target_xcopy_locate_se_dev_e4(se_cmd, xop, true);
+ else
+ rc = target_xcopy_locate_se_dev_e4(se_cmd, xop, false);
+
+ if (rc < 0)
+ goto out;
+
+ pr_debug("XCOPY TGT desc: Source dev: %p NAA IEEE WWN: 0x%16phN\n",
+ xop->src_dev, &xop->src_tid_wwn[0]);
+ pr_debug("XCOPY TGT desc: Dest dev: %p NAA IEEE WWN: 0x%16phN\n",
+ xop->dst_dev, &xop->dst_tid_wwn[0]);
+
+ return ret;
+
+out:
+ return -EINVAL;
+}
+
+static int target_xcopy_parse_segdesc_02(struct se_cmd *se_cmd, struct xcopy_op *xop,
+ unsigned char *p)
+{
+ unsigned char *desc = p;
+ int dc = (desc[1] & 0x02);
+ unsigned short desc_len;
+
+ desc_len = get_unaligned_be16(&desc[2]);
+ if (desc_len != 0x18) {
+ pr_err("XCOPY segment desc 0x02: Illegal desc_len:"
+ " %hu\n", desc_len);
+ return -EINVAL;
+ }
+
+ xop->stdi = get_unaligned_be16(&desc[4]);
+ xop->dtdi = get_unaligned_be16(&desc[6]);
+ pr_debug("XCOPY seg desc 0x02: desc_len: %hu stdi: %hu dtdi: %hu, DC: %d\n",
+ desc_len, xop->stdi, xop->dtdi, dc);
+
+ xop->nolb = get_unaligned_be16(&desc[10]);
+ xop->src_lba = get_unaligned_be64(&desc[12]);
+ xop->dst_lba = get_unaligned_be64(&desc[20]);
+ pr_debug("XCOPY seg desc 0x02: nolb: %hu src_lba: %llu dst_lba: %llu\n",
+ xop->nolb, (unsigned long long)xop->src_lba,
+ (unsigned long long)xop->dst_lba);
+
+ if (dc != 0) {
+ xop->dbl = (desc[29] & 0xff) << 16;
+ xop->dbl |= (desc[30] & 0xff) << 8;
+ xop->dbl |= desc[31] & 0xff;
+
+ pr_debug("XCOPY seg desc 0x02: DC=1 w/ dbl: %u\n", xop->dbl);
+ }
+ return 0;
+}
+
+static int target_xcopy_parse_segment_descriptors(struct se_cmd *se_cmd,
+ struct xcopy_op *xop, unsigned char *p,
+ unsigned int sdll)
+{
+ unsigned char *desc = p;
+ unsigned int start = 0;
+ int offset = sdll % XCOPY_SEGMENT_DESC_LEN, rc, ret = 0;
+
+ if (offset != 0) {
+ pr_err("XCOPY segment descriptor list length is not"
+ " multiple of %d\n", XCOPY_SEGMENT_DESC_LEN);
+ return -EINVAL;
+ }
+
+ while (start < sdll) {
+ /*
+ * Check segment descriptor type code for block -> block
+ */
+ switch (desc[0]) {
+ case 0x02:
+ rc = target_xcopy_parse_segdesc_02(se_cmd, xop, desc);
+ if (rc < 0)
+ goto out;
+
+ ret++;
+ start += XCOPY_SEGMENT_DESC_LEN;
+ desc += XCOPY_SEGMENT_DESC_LEN;
+ break;
+ default:
+ pr_err("XCOPY unspported segment descriptor"
+ "type: 0x%02x\n", desc[0]);
+ goto out;
+ }
+ }
+
+ return ret;
+
+out:
+ return -EINVAL;
+}
+
+/*
+ * Start xcopy_pt ops
+ */
+
+struct xcopy_pt_cmd {
+ bool remote_port;
+ struct se_cmd se_cmd;
+ struct xcopy_op *xcopy_op;
+ struct completion xpt_passthrough_sem;
+ unsigned char sense_buffer[TRANSPORT_SENSE_BUFFER];
+};
+
+static struct se_port xcopy_pt_port;
+static struct se_portal_group xcopy_pt_tpg;
+static struct se_session xcopy_pt_sess;
+static struct se_node_acl xcopy_pt_nacl;
+
+static char *xcopy_pt_get_fabric_name(void)
+{
+ return "xcopy-pt";
+}
+
+static u32 xcopy_pt_get_tag(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static int xcopy_pt_get_cmd_state(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static void xcopy_pt_undepend_remotedev(struct xcopy_op *xop)
+{
+ struct configfs_subsystem *subsys = target_core_subsystem[0];
+ struct se_device *remote_dev;
+
+ if (xop->op_origin == XCOL_SOURCE_RECV_OP)
+ remote_dev = xop->dst_dev;
+ else
+ remote_dev = xop->src_dev;
+
+ pr_debug("Calling configfs_undepend_item for subsys: %p"
+ " remote_dev: %p remote_dev->dev_group: %p\n",
+ subsys, remote_dev, &remote_dev->dev_group.cg_item);
+
+ configfs_undepend_item(subsys, &remote_dev->dev_group.cg_item);
+}
+
+static void xcopy_pt_release_cmd(struct se_cmd *se_cmd)
+{
+ struct xcopy_pt_cmd *xpt_cmd = container_of(se_cmd,
+ struct xcopy_pt_cmd, se_cmd);
+
+ kfree(xpt_cmd);
+}
+
+static int xcopy_pt_check_stop_free(struct se_cmd *se_cmd)
+{
+ struct xcopy_pt_cmd *xpt_cmd = container_of(se_cmd,
+ struct xcopy_pt_cmd, se_cmd);
+
+ complete(&xpt_cmd->xpt_passthrough_sem);
+ return 0;
+}
+
+static int xcopy_pt_write_pending(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static int xcopy_pt_write_pending_status(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static int xcopy_pt_queue_data_in(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static int xcopy_pt_queue_status(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static struct target_core_fabric_ops xcopy_pt_tfo = {
+ .get_fabric_name = xcopy_pt_get_fabric_name,
+ .get_task_tag = xcopy_pt_get_tag,
+ .get_cmd_state = xcopy_pt_get_cmd_state,
+ .release_cmd = xcopy_pt_release_cmd,
+ .check_stop_free = xcopy_pt_check_stop_free,
+ .write_pending = xcopy_pt_write_pending,
+ .write_pending_status = xcopy_pt_write_pending_status,
+ .queue_data_in = xcopy_pt_queue_data_in,
+ .queue_status = xcopy_pt_queue_status,
+};
+
+/*
+ * End xcopy_pt_ops
+ */
+
+int target_xcopy_setup_pt(void)
+{
+ xcopy_wq = alloc_workqueue("xcopy_wq", WQ_MEM_RECLAIM, 0);
+ if (!xcopy_wq) {
+ pr_err("Unable to allocate xcopy_wq\n");
+ return -ENOMEM;
+ }
+
+ memset(&xcopy_pt_port, 0, sizeof(struct se_port));
+ INIT_LIST_HEAD(&xcopy_pt_port.sep_alua_list);
+ INIT_LIST_HEAD(&xcopy_pt_port.sep_list);
+ mutex_init(&xcopy_pt_port.sep_tg_pt_md_mutex);
+
+ memset(&xcopy_pt_tpg, 0, sizeof(struct se_portal_group));
+ INIT_LIST_HEAD(&xcopy_pt_tpg.se_tpg_node);
+ INIT_LIST_HEAD(&xcopy_pt_tpg.acl_node_list);
+ INIT_LIST_HEAD(&xcopy_pt_tpg.tpg_sess_list);
+
+ xcopy_pt_port.sep_tpg = &xcopy_pt_tpg;
+ xcopy_pt_tpg.se_tpg_tfo = &xcopy_pt_tfo;
+
+ memset(&xcopy_pt_nacl, 0, sizeof(struct se_node_acl));
+ INIT_LIST_HEAD(&xcopy_pt_nacl.acl_list);
+ INIT_LIST_HEAD(&xcopy_pt_nacl.acl_sess_list);
+ memset(&xcopy_pt_sess, 0, sizeof(struct se_session));
+ INIT_LIST_HEAD(&xcopy_pt_sess.sess_list);
+ INIT_LIST_HEAD(&xcopy_pt_sess.sess_acl_list);
+
+ xcopy_pt_nacl.se_tpg = &xcopy_pt_tpg;
+ xcopy_pt_nacl.nacl_sess = &xcopy_pt_sess;
+
+ xcopy_pt_sess.se_tpg = &xcopy_pt_tpg;
+ xcopy_pt_sess.se_node_acl = &xcopy_pt_nacl;
+
+ return 0;
+}
+
+void target_xcopy_release_pt(void)
+{
+ if (xcopy_wq)
+ destroy_workqueue(xcopy_wq);
+}
+
+static void target_xcopy_setup_pt_port(
+ struct xcopy_pt_cmd *xpt_cmd,
+ struct xcopy_op *xop,
+ bool remote_port)
+{
+ struct se_cmd *ec_cmd = xop->xop_se_cmd;
+ struct se_cmd *pt_cmd = &xpt_cmd->se_cmd;
+
+ if (xop->op_origin == XCOL_SOURCE_RECV_OP) {
+ /*
+ * Honor destination port reservations for X-COPY PUSH emulation
+ * when CDB is received on local source port, and READs blocks to
+ * WRITE on remote destination port.
+ */
+ if (remote_port) {
+ xpt_cmd->remote_port = remote_port;
+ pt_cmd->se_lun->lun_sep = &xcopy_pt_port;
+ pr_debug("Setup emulated remote DEST xcopy_pt_port: %p to"
+ " cmd->se_lun->lun_sep for X-COPY data PUSH\n",
+ pt_cmd->se_lun->lun_sep);
+ } else {
+ pt_cmd->se_lun = ec_cmd->se_lun;
+ pt_cmd->se_dev = ec_cmd->se_dev;
+
+ pr_debug("Honoring local SRC port from ec_cmd->se_dev:"
+ " %p\n", pt_cmd->se_dev);
+ pt_cmd->se_lun = ec_cmd->se_lun;
+ pr_debug("Honoring local SRC port from ec_cmd->se_lun: %p\n",
+ pt_cmd->se_lun);
+ }
+ } else {
+ /*
+ * Honor source port reservation for X-COPY PULL emulation
+ * when CDB is received on local desintation port, and READs
+ * blocks from the remote source port to WRITE on local
+ * destination port.
+ */
+ if (remote_port) {
+ xpt_cmd->remote_port = remote_port;
+ pt_cmd->se_lun->lun_sep = &xcopy_pt_port;
+ pr_debug("Setup emulated remote SRC xcopy_pt_port: %p to"
+ " cmd->se_lun->lun_sep for X-COPY data PULL\n",
+ pt_cmd->se_lun->lun_sep);
+ } else {
+ pt_cmd->se_lun = ec_cmd->se_lun;
+ pt_cmd->se_dev = ec_cmd->se_dev;
+
+ pr_debug("Honoring local DST port from ec_cmd->se_dev:"
+ " %p\n", pt_cmd->se_dev);
+ pt_cmd->se_lun = ec_cmd->se_lun;
+ pr_debug("Honoring local DST port from ec_cmd->se_lun: %p\n",
+ pt_cmd->se_lun);
+ }
+ }
+}
+
+static int target_xcopy_init_pt_lun(
+ struct xcopy_pt_cmd *xpt_cmd,
+ struct xcopy_op *xop,
+ struct se_device *se_dev,
+ struct se_cmd *pt_cmd,
+ bool remote_port)
+{
+ /*
+ * Don't allocate + init an pt_cmd->se_lun if honoring local port for
+ * reservations. The pt_cmd->se_lun pointer will be setup from within
+ * target_xcopy_setup_pt_port()
+ */
+ if (!remote_port) {
+ pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD | SCF_CMD_XCOPY_PASSTHROUGH;
+ return 0;
+ }
+
+ pt_cmd->se_lun = &se_dev->xcopy_lun;
+ pt_cmd->se_dev = se_dev;
+
+ pr_debug("Setup emulated se_dev: %p from se_dev\n", pt_cmd->se_dev);
+ pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD | SCF_CMD_XCOPY_PASSTHROUGH;
+
+ pr_debug("Setup emulated se_dev: %p to pt_cmd->se_lun->lun_se_dev\n",
+ pt_cmd->se_lun->lun_se_dev);
+
+ return 0;
+}
+
+static int target_xcopy_setup_pt_cmd(
+ struct xcopy_pt_cmd *xpt_cmd,
+ struct xcopy_op *xop,
+ struct se_device *se_dev,
+ unsigned char *cdb,
+ bool remote_port,
+ bool alloc_mem)
+{
+ struct se_cmd *cmd = &xpt_cmd->se_cmd;
+ sense_reason_t sense_rc;
+ int ret = 0, rc;
+ /*
+ * Setup LUN+port to honor reservations based upon xop->op_origin for
+ * X-COPY PUSH or X-COPY PULL based upon where the CDB was received.
+ */
+ rc = target_xcopy_init_pt_lun(xpt_cmd, xop, se_dev, cmd, remote_port);
+ if (rc < 0) {
+ ret = rc;
+ goto out;
+ }
+ xpt_cmd->xcopy_op = xop;
+ target_xcopy_setup_pt_port(xpt_cmd, xop, remote_port);
+
+ sense_rc = target_setup_cmd_from_cdb(cmd, cdb);
+ if (sense_rc) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (alloc_mem) {
+ rc = target_alloc_sgl(&cmd->t_data_sg, &cmd->t_data_nents,
+ cmd->data_length, false);
+ if (rc < 0) {
+ ret = rc;
+ goto out;
+ }
+ /*
+ * Set this bit so that transport_free_pages() allows the
+ * caller to release SGLs + physical memory allocated by
+ * transport_generic_get_mem()..
+ */
+ cmd->se_cmd_flags |= SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
+ } else {
+ /*
+ * Here the previously allocated SGLs for the internal READ
+ * are mapped zero-copy to the internal WRITE.
+ */
+ sense_rc = transport_generic_map_mem_to_cmd(cmd,
+ xop->xop_data_sg, xop->xop_data_nents,
+ NULL, 0);
+ if (sense_rc) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pr_debug("Setup PASSTHROUGH_NOALLOC t_data_sg: %p t_data_nents:"
+ " %u\n", cmd->t_data_sg, cmd->t_data_nents);
+ }
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static int target_xcopy_issue_pt_cmd(struct xcopy_pt_cmd *xpt_cmd)
+{
+ struct se_cmd *se_cmd = &xpt_cmd->se_cmd;
+ sense_reason_t sense_rc;
+
+ sense_rc = transport_generic_new_cmd(se_cmd);
+ if (sense_rc)
+ return -EINVAL;
+
+ if (se_cmd->data_direction == DMA_TO_DEVICE)
+ target_execute_cmd(se_cmd);
+
+ wait_for_completion_interruptible(&xpt_cmd->xpt_passthrough_sem);
+
+ pr_debug("target_xcopy_issue_pt_cmd(): SCSI status: 0x%02x\n",
+ se_cmd->scsi_status);
+
+ return (se_cmd->scsi_status) ? -EINVAL : 0;
+}
+
+static int target_xcopy_read_source(
+ struct se_cmd *ec_cmd,
+ struct xcopy_op *xop,
+ struct se_device *src_dev,
+ sector_t src_lba,
+ u32 src_sectors)
+{
+ struct xcopy_pt_cmd *xpt_cmd;
+ struct se_cmd *se_cmd;
+ u32 length = (src_sectors * src_dev->dev_attrib.block_size);
+ int rc;
+ unsigned char cdb[16];
+ bool remote_port = (xop->op_origin == XCOL_DEST_RECV_OP);
+
+ xpt_cmd = kzalloc(sizeof(struct xcopy_pt_cmd), GFP_KERNEL);
+ if (!xpt_cmd) {
+ pr_err("Unable to allocate xcopy_pt_cmd\n");
+ return -ENOMEM;
+ }
+ init_completion(&xpt_cmd->xpt_passthrough_sem);
+ se_cmd = &xpt_cmd->se_cmd;
+
+ memset(&cdb[0], 0, 16);
+ cdb[0] = READ_16;
+ put_unaligned_be64(src_lba, &cdb[2]);
+ put_unaligned_be32(src_sectors, &cdb[10]);
+ pr_debug("XCOPY: Built READ_16: LBA: %llu Sectors: %u Length: %u\n",
+ (unsigned long long)src_lba, src_sectors, length);
+
+ transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length,
+ DMA_FROM_DEVICE, 0, &xpt_cmd->sense_buffer[0]);
+ xop->src_pt_cmd = xpt_cmd;
+
+ rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, src_dev, &cdb[0],
+ remote_port, true);
+ if (rc < 0) {
+ transport_generic_free_cmd(se_cmd, 0);
+ return rc;
+ }
+
+ xop->xop_data_sg = se_cmd->t_data_sg;
+ xop->xop_data_nents = se_cmd->t_data_nents;
+ pr_debug("XCOPY-READ: Saved xop->xop_data_sg: %p, num: %u for READ"
+ " memory\n", xop->xop_data_sg, xop->xop_data_nents);
+
+ rc = target_xcopy_issue_pt_cmd(xpt_cmd);
+ if (rc < 0) {
+ transport_generic_free_cmd(se_cmd, 0);
+ return rc;
+ }
+ /*
+ * Clear off the allocated t_data_sg, that has been saved for
+ * zero-copy WRITE submission reuse in struct xcopy_op..
+ */
+ se_cmd->t_data_sg = NULL;
+ se_cmd->t_data_nents = 0;
+
+ return 0;
+}
+
+static int target_xcopy_write_destination(
+ struct se_cmd *ec_cmd,
+ struct xcopy_op *xop,
+ struct se_device *dst_dev,
+ sector_t dst_lba,
+ u32 dst_sectors)
+{
+ struct xcopy_pt_cmd *xpt_cmd;
+ struct se_cmd *se_cmd;
+ u32 length = (dst_sectors * dst_dev->dev_attrib.block_size);
+ int rc;
+ unsigned char cdb[16];
+ bool remote_port = (xop->op_origin == XCOL_SOURCE_RECV_OP);
+
+ xpt_cmd = kzalloc(sizeof(struct xcopy_pt_cmd), GFP_KERNEL);
+ if (!xpt_cmd) {
+ pr_err("Unable to allocate xcopy_pt_cmd\n");
+ return -ENOMEM;
+ }
+ init_completion(&xpt_cmd->xpt_passthrough_sem);
+ se_cmd = &xpt_cmd->se_cmd;
+
+ memset(&cdb[0], 0, 16);
+ cdb[0] = WRITE_16;
+ put_unaligned_be64(dst_lba, &cdb[2]);
+ put_unaligned_be32(dst_sectors, &cdb[10]);
+ pr_debug("XCOPY: Built WRITE_16: LBA: %llu Sectors: %u Length: %u\n",
+ (unsigned long long)dst_lba, dst_sectors, length);
+
+ transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length,
+ DMA_TO_DEVICE, 0, &xpt_cmd->sense_buffer[0]);
+ xop->dst_pt_cmd = xpt_cmd;
+
+ rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, dst_dev, &cdb[0],
+ remote_port, false);
+ if (rc < 0) {
+ struct se_cmd *src_cmd = &xop->src_pt_cmd->se_cmd;
+ /*
+ * If the failure happened before the t_mem_list hand-off in
+ * target_xcopy_setup_pt_cmd(), Reset memory + clear flag so that
+ * core releases this memory on error during X-COPY WRITE I/O.
+ */
+ src_cmd->se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
+ src_cmd->t_data_sg = xop->xop_data_sg;
+ src_cmd->t_data_nents = xop->xop_data_nents;
+
+ transport_generic_free_cmd(se_cmd, 0);
+ return rc;
+ }
+
+ rc = target_xcopy_issue_pt_cmd(xpt_cmd);
+ if (rc < 0) {
+ se_cmd->se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
+ transport_generic_free_cmd(se_cmd, 0);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void target_xcopy_do_work(struct work_struct *work)
+{
+ struct xcopy_op *xop = container_of(work, struct xcopy_op, xop_work);
+ struct se_device *src_dev = xop->src_dev, *dst_dev = xop->dst_dev;
+ struct se_cmd *ec_cmd = xop->xop_se_cmd;
+ sector_t src_lba = xop->src_lba, dst_lba = xop->dst_lba, end_lba;
+ unsigned int max_sectors;
+ int rc;
+ unsigned short nolb = xop->nolb, cur_nolb, max_nolb, copied_nolb = 0;
+
+ end_lba = src_lba + nolb;
+ /*
+ * Break up XCOPY I/O into hw_max_sectors sized I/O based on the
+ * smallest max_sectors between src_dev + dev_dev, or
+ */
+ max_sectors = min(src_dev->dev_attrib.hw_max_sectors,
+ dst_dev->dev_attrib.hw_max_sectors);
+ max_sectors = min_t(u32, max_sectors, XCOPY_MAX_SECTORS);
+
+ max_nolb = min_t(u16, max_sectors, ((u16)(~0U)));
+
+ pr_debug("target_xcopy_do_work: nolb: %hu, max_nolb: %hu end_lba: %llu\n",
+ nolb, max_nolb, (unsigned long long)end_lba);
+ pr_debug("target_xcopy_do_work: Starting src_lba: %llu, dst_lba: %llu\n",
+ (unsigned long long)src_lba, (unsigned long long)dst_lba);
+
+ while (src_lba < end_lba) {
+ cur_nolb = min(nolb, max_nolb);
+
+ pr_debug("target_xcopy_do_work: Calling read src_dev: %p src_lba: %llu,"
+ " cur_nolb: %hu\n", src_dev, (unsigned long long)src_lba, cur_nolb);
+
+ rc = target_xcopy_read_source(ec_cmd, xop, src_dev, src_lba, cur_nolb);
+ if (rc < 0)
+ goto out;
+
+ src_lba += cur_nolb;
+ pr_debug("target_xcopy_do_work: Incremented READ src_lba to %llu\n",
+ (unsigned long long)src_lba);
+
+ pr_debug("target_xcopy_do_work: Calling write dst_dev: %p dst_lba: %llu,"
+ " cur_nolb: %hu\n", dst_dev, (unsigned long long)dst_lba, cur_nolb);
+
+ rc = target_xcopy_write_destination(ec_cmd, xop, dst_dev,
+ dst_lba, cur_nolb);
+ if (rc < 0) {
+ transport_generic_free_cmd(&xop->src_pt_cmd->se_cmd, 0);
+ goto out;
+ }
+
+ dst_lba += cur_nolb;
+ pr_debug("target_xcopy_do_work: Incremented WRITE dst_lba to %llu\n",
+ (unsigned long long)dst_lba);
+
+ copied_nolb += cur_nolb;
+ nolb -= cur_nolb;
+
+ transport_generic_free_cmd(&xop->src_pt_cmd->se_cmd, 0);
+ xop->dst_pt_cmd->se_cmd.se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
+
+ transport_generic_free_cmd(&xop->dst_pt_cmd->se_cmd, 0);
+ }
+
+ xcopy_pt_undepend_remotedev(xop);
+ kfree(xop);
+
+ pr_debug("target_xcopy_do_work: Final src_lba: %llu, dst_lba: %llu\n",
+ (unsigned long long)src_lba, (unsigned long long)dst_lba);
+ pr_debug("target_xcopy_do_work: Blocks copied: %hu, Bytes Copied: %u\n",
+ copied_nolb, copied_nolb * dst_dev->dev_attrib.block_size);
+
+ pr_debug("target_xcopy_do_work: Setting X-COPY GOOD status -> sending response\n");
+ target_complete_cmd(ec_cmd, SAM_STAT_GOOD);
+ return;
+
+out:
+ xcopy_pt_undepend_remotedev(xop);
+ kfree(xop);
+
+ pr_warn("target_xcopy_do_work: Setting X-COPY CHECK_CONDITION -> sending response\n");
+ ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
+ target_complete_cmd(ec_cmd, SAM_STAT_CHECK_CONDITION);
+}
+
+sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
+{
+ struct se_device *dev = se_cmd->se_dev;
+ struct xcopy_op *xop = NULL;
+ unsigned char *p = NULL, *seg_desc;
+ unsigned int list_id, list_id_usage, sdll, inline_dl, sa;
+ sense_reason_t ret = TCM_INVALID_PARAMETER_LIST;
+ int rc;
+ unsigned short tdll;
+
+ if (!dev->dev_attrib.emulate_3pc) {
+ pr_err("EXTENDED_COPY operation explicitly disabled\n");
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+ }
+
+ sa = se_cmd->t_task_cdb[1] & 0x1f;
+ if (sa != 0x00) {
+ pr_err("EXTENDED_COPY(LID4) not supported\n");
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+ }
+
+ xop = kzalloc(sizeof(struct xcopy_op), GFP_KERNEL);
+ if (!xop) {
+ pr_err("Unable to allocate xcopy_op\n");
+ return TCM_OUT_OF_RESOURCES;
+ }
+ xop->xop_se_cmd = se_cmd;
+
+ p = transport_kmap_data_sg(se_cmd);
+ if (!p) {
+ pr_err("transport_kmap_data_sg() failed in target_do_xcopy\n");
+ kfree(xop);
+ return TCM_OUT_OF_RESOURCES;
+ }
+
+ list_id = p[0];
+ list_id_usage = (p[1] & 0x18) >> 3;
+
+ /*
+ * Determine TARGET DESCRIPTOR LIST LENGTH + SEGMENT DESCRIPTOR LIST LENGTH
+ */
+ tdll = get_unaligned_be16(&p[2]);
+ sdll = get_unaligned_be32(&p[8]);
+
+ inline_dl = get_unaligned_be32(&p[12]);
+ if (inline_dl != 0) {
+ pr_err("XCOPY with non zero inline data length\n");
+ goto out;
+ }
+
+ pr_debug("Processing XCOPY with list_id: 0x%02x list_id_usage: 0x%02x"
+ " tdll: %hu sdll: %u inline_dl: %u\n", list_id, list_id_usage,
+ tdll, sdll, inline_dl);
+
+ rc = target_xcopy_parse_target_descriptors(se_cmd, xop, &p[16], tdll);
+ if (rc <= 0)
+ goto out;
+
+ if (xop->src_dev->dev_attrib.block_size !=
+ xop->dst_dev->dev_attrib.block_size) {
+ pr_err("XCOPY: Non matching src_dev block_size: %u + dst_dev"
+ " block_size: %u currently unsupported\n",
+ xop->src_dev->dev_attrib.block_size,
+ xop->dst_dev->dev_attrib.block_size);
+ xcopy_pt_undepend_remotedev(xop);
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ goto out;
+ }
+
+ pr_debug("XCOPY: Processed %d target descriptors, length: %u\n", rc,
+ rc * XCOPY_TARGET_DESC_LEN);
+ seg_desc = &p[16];
+ seg_desc += (rc * XCOPY_TARGET_DESC_LEN);
+
+ rc = target_xcopy_parse_segment_descriptors(se_cmd, xop, seg_desc, sdll);
+ if (rc <= 0) {
+ xcopy_pt_undepend_remotedev(xop);
+ goto out;
+ }
+ transport_kunmap_data_sg(se_cmd);
+
+ pr_debug("XCOPY: Processed %d segment descriptors, length: %u\n", rc,
+ rc * XCOPY_SEGMENT_DESC_LEN);
+ INIT_WORK(&xop->xop_work, target_xcopy_do_work);
+ queue_work(xcopy_wq, &xop->xop_work);
+ return TCM_NO_SENSE;
+
+out:
+ if (p)
+ transport_kunmap_data_sg(se_cmd);
+ kfree(xop);
+ return ret;
+}
+
+static sense_reason_t target_rcr_operating_parameters(struct se_cmd *se_cmd)
+{
+ unsigned char *p;
+
+ p = transport_kmap_data_sg(se_cmd);
+ if (!p) {
+ pr_err("transport_kmap_data_sg failed in"
+ " target_rcr_operating_parameters\n");
+ return TCM_OUT_OF_RESOURCES;
+ }
+
+ if (se_cmd->data_length < 54) {
+ pr_err("Receive Copy Results Op Parameters length"
+ " too small: %u\n", se_cmd->data_length);
+ transport_kunmap_data_sg(se_cmd);
+ return TCM_INVALID_CDB_FIELD;
+ }
+ /*
+ * Set SNLID=1 (Supports no List ID)
+ */
+ p[4] = 0x1;
+ /*
+ * MAXIMUM TARGET DESCRIPTOR COUNT
+ */
+ put_unaligned_be16(RCR_OP_MAX_TARGET_DESC_COUNT, &p[8]);
+ /*
+ * MAXIMUM SEGMENT DESCRIPTOR COUNT
+ */
+ put_unaligned_be16(RCR_OP_MAX_SG_DESC_COUNT, &p[10]);
+ /*
+ * MAXIMUM DESCRIPTOR LIST LENGTH
+ */
+ put_unaligned_be32(RCR_OP_MAX_DESC_LIST_LEN, &p[12]);
+ /*
+ * MAXIMUM SEGMENT LENGTH
+ */
+ put_unaligned_be32(RCR_OP_MAX_SEGMENT_LEN, &p[16]);
+ /*
+ * MAXIMUM INLINE DATA LENGTH for SA 0x04 (NOT SUPPORTED)
+ */
+ put_unaligned_be32(0x0, &p[20]);
+ /*
+ * HELD DATA LIMIT
+ */
+ put_unaligned_be32(0x0, &p[24]);
+ /*
+ * MAXIMUM STREAM DEVICE TRANSFER SIZE
+ */
+ put_unaligned_be32(0x0, &p[28]);
+ /*
+ * TOTAL CONCURRENT COPIES
+ */
+ put_unaligned_be16(RCR_OP_TOTAL_CONCURR_COPIES, &p[34]);
+ /*
+ * MAXIMUM CONCURRENT COPIES
+ */
+ p[36] = RCR_OP_MAX_CONCURR_COPIES;
+ /*
+ * DATA SEGMENT GRANULARITY (log 2)
+ */
+ p[37] = RCR_OP_DATA_SEG_GRAN_LOG2;
+ /*
+ * INLINE DATA GRANULARITY log 2)
+ */
+ p[38] = RCR_OP_INLINE_DATA_GRAN_LOG2;
+ /*
+ * HELD DATA GRANULARITY
+ */
+ p[39] = RCR_OP_HELD_DATA_GRAN_LOG2;
+ /*
+ * IMPLEMENTED DESCRIPTOR LIST LENGTH
+ */
+ p[43] = 0x2;
+ /*
+ * List of implemented descriptor type codes (ordered)
+ */
+ p[44] = 0x02; /* Copy Block to Block device */
+ p[45] = 0xe4; /* Identification descriptor target descriptor */
+
+ /*
+ * AVAILABLE DATA (n-3)
+ */
+ put_unaligned_be32(42, &p[0]);
+
+ transport_kunmap_data_sg(se_cmd);
+ target_complete_cmd(se_cmd, GOOD);
+
+ return TCM_NO_SENSE;
+}
+
+sense_reason_t target_do_receive_copy_results(struct se_cmd *se_cmd)
+{
+ unsigned char *cdb = &se_cmd->t_task_cdb[0];
+ int sa = (cdb[1] & 0x1f), list_id = cdb[2];
+ sense_reason_t rc = TCM_NO_SENSE;
+
+ pr_debug("Entering target_do_receive_copy_results: SA: 0x%02x, List ID:"
+ " 0x%02x, AL: %u\n", sa, list_id, se_cmd->data_length);
+
+ if (list_id != 0) {
+ pr_err("Receive Copy Results with non zero list identifier"
+ " not supported\n");
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ switch (sa) {
+ case RCR_SA_OPERATING_PARAMETERS:
+ rc = target_rcr_operating_parameters(se_cmd);
+ break;
+ case RCR_SA_COPY_STATUS:
+ case RCR_SA_RECEIVE_DATA:
+ case RCR_SA_FAILED_SEGMENT_DETAILS:
+ default:
+ pr_err("Unsupported SA for receive copy results: 0x%02x\n", sa);
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ return rc;
+}
diff --git a/drivers/target/target_core_xcopy.h b/drivers/target/target_core_xcopy.h
new file mode 100644
index 00000000000..700a981c7b4
--- /dev/null
+++ b/drivers/target/target_core_xcopy.h
@@ -0,0 +1,62 @@
+#define XCOPY_TARGET_DESC_LEN 32
+#define XCOPY_SEGMENT_DESC_LEN 28
+#define XCOPY_NAA_IEEE_REGEX_LEN 16
+#define XCOPY_MAX_SECTORS 1024
+
+enum xcopy_origin_list {
+ XCOL_SOURCE_RECV_OP = 0x01,
+ XCOL_DEST_RECV_OP = 0x02,
+};
+
+struct xcopy_pt_cmd;
+
+struct xcopy_op {
+ int op_origin;
+
+ struct se_cmd *xop_se_cmd;
+ struct se_device *src_dev;
+ unsigned char src_tid_wwn[XCOPY_NAA_IEEE_REGEX_LEN];
+ struct se_device *dst_dev;
+ unsigned char dst_tid_wwn[XCOPY_NAA_IEEE_REGEX_LEN];
+ unsigned char local_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN];
+
+ sector_t src_lba;
+ sector_t dst_lba;
+ unsigned short stdi;
+ unsigned short dtdi;
+ unsigned short nolb;
+ unsigned int dbl;
+
+ struct xcopy_pt_cmd *src_pt_cmd;
+ struct xcopy_pt_cmd *dst_pt_cmd;
+
+ u32 xop_data_nents;
+ struct scatterlist *xop_data_sg;
+ struct work_struct xop_work;
+};
+
+/*
+ * Receive Copy Results Sevice Actions
+ */
+#define RCR_SA_COPY_STATUS 0x00
+#define RCR_SA_RECEIVE_DATA 0x01
+#define RCR_SA_OPERATING_PARAMETERS 0x03
+#define RCR_SA_FAILED_SEGMENT_DETAILS 0x04
+
+/*
+ * Receive Copy Results defs for Operating Parameters
+ */
+#define RCR_OP_MAX_TARGET_DESC_COUNT 0x2
+#define RCR_OP_MAX_SG_DESC_COUNT 0x1
+#define RCR_OP_MAX_DESC_LIST_LEN 1024
+#define RCR_OP_MAX_SEGMENT_LEN 268435456 /* 256 MB */
+#define RCR_OP_TOTAL_CONCURR_COPIES 0x1 /* Must be <= 16384 */
+#define RCR_OP_MAX_CONCURR_COPIES 0x1 /* Must be <= 255 */
+#define RCR_OP_DATA_SEG_GRAN_LOG2 9 /* 512 bytes in log 2 */
+#define RCR_OP_INLINE_DATA_GRAN_LOG2 9 /* 512 bytes in log 2 */
+#define RCR_OP_HELD_DATA_GRAN_LOG2 9 /* 512 bytes in log 2 */
+
+extern int target_xcopy_setup_pt(void);
+extern void target_xcopy_release_pt(void);
+extern sense_reason_t target_do_xcopy(struct se_cmd *);
+extern sense_reason_t target_do_receive_copy_results(struct se_cmd *);
diff --git a/drivers/target/tcm_fc/Kconfig b/drivers/target/tcm_fc/Kconfig
new file mode 100644
index 00000000000..40caf458e89
--- /dev/null
+++ b/drivers/target/tcm_fc/Kconfig
@@ -0,0 +1,5 @@
+config TCM_FC
+ tristate "TCM_FC fabric Plugin"
+ depends on LIBFC
+ help
+ Say Y here to enable the TCM FC plugin for accessing FC fabrics in TCM
diff --git a/drivers/target/tcm_fc/Makefile b/drivers/target/tcm_fc/Makefile
new file mode 100644
index 00000000000..20b14bb087c
--- /dev/null
+++ b/drivers/target/tcm_fc/Makefile
@@ -0,0 +1,6 @@
+tcm_fc-y += tfc_cmd.o \
+ tfc_conf.o \
+ tfc_io.o \
+ tfc_sess.o
+
+obj-$(CONFIG_TCM_FC) += tcm_fc.o
diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h
new file mode 100644
index 00000000000..a0bcfd3e7e7
--- /dev/null
+++ b/drivers/target/tcm_fc/tcm_fc.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef __TCM_FC_H__
+#define __TCM_FC_H__
+
+#define FT_VERSION "0.4"
+
+#define FT_NAMELEN 32 /* length of ASCII WWPNs including pad */
+#define FT_TPG_NAMELEN 32 /* max length of TPG name */
+#define FT_LUN_NAMELEN 32 /* max length of LUN name */
+#define TCM_FC_DEFAULT_TAGS 512 /* tags used for per-session preallocation */
+
+struct ft_transport_id {
+ __u8 format;
+ __u8 __resvd1[7];
+ __u8 wwpn[8];
+ __u8 __resvd2[8];
+} __attribute__((__packed__));
+
+/*
+ * Session (remote port).
+ */
+struct ft_sess {
+ u32 port_id; /* for hash lookup use only */
+ u32 params;
+ u16 max_frame; /* maximum frame size */
+ u64 port_name; /* port name for transport ID */
+ struct ft_tport *tport;
+ struct se_session *se_sess;
+ struct hlist_node hash; /* linkage in ft_sess_hash table */
+ struct rcu_head rcu;
+ struct kref kref; /* ref for hash and outstanding I/Os */
+};
+
+/*
+ * Hash table of sessions per local port.
+ * Hash lookup by remote port FC_ID.
+ */
+#define FT_SESS_HASH_BITS 6
+#define FT_SESS_HASH_SIZE (1 << FT_SESS_HASH_BITS)
+
+/*
+ * Per local port data.
+ * This is created only after a TPG exists that allows target function
+ * for the local port. If the TPG exists, this is allocated when
+ * we're notified that the local port has been created, or when
+ * the first PRLI provider callback is received.
+ */
+struct ft_tport {
+ struct fc_lport *lport;
+ struct ft_tpg *tpg; /* NULL if TPG deleted before tport */
+ u32 sess_count; /* number of sessions in hash */
+ struct rcu_head rcu;
+ struct hlist_head hash[FT_SESS_HASH_SIZE]; /* list of sessions */
+};
+
+/*
+ * Node ID and authentication.
+ */
+struct ft_node_auth {
+ u64 port_name;
+ u64 node_name;
+};
+
+/*
+ * Node ACL for FC remote port session.
+ */
+struct ft_node_acl {
+ struct ft_node_auth node_auth;
+ struct se_node_acl se_node_acl;
+};
+
+struct ft_lun {
+ u32 index;
+ char name[FT_LUN_NAMELEN];
+};
+
+/*
+ * Target portal group (local port).
+ */
+struct ft_tpg {
+ u32 index;
+ struct ft_lport_wwn *lport_wwn;
+ struct ft_tport *tport; /* active tport or NULL */
+ struct list_head lun_list; /* head of LUNs */
+ struct se_portal_group se_tpg;
+ struct workqueue_struct *workqueue;
+};
+
+struct ft_lport_wwn {
+ u64 wwpn;
+ char name[FT_NAMELEN];
+ struct list_head ft_wwn_node;
+ struct ft_tpg *tpg;
+ struct se_wwn se_wwn;
+};
+
+/*
+ * Commands
+ */
+struct ft_cmd {
+ struct ft_sess *sess; /* session held for cmd */
+ struct fc_seq *seq; /* sequence in exchange mgr */
+ struct se_cmd se_cmd; /* Local TCM I/O descriptor */
+ struct fc_frame *req_frame;
+ u32 write_data_len; /* data received on writes */
+ struct work_struct work;
+ /* Local sense buffer */
+ unsigned char ft_sense_buffer[TRANSPORT_SENSE_BUFFER];
+ u32 was_ddp_setup:1; /* Set only if ddp is setup */
+ u32 aborted:1; /* Set if aborted by reset or timeout */
+ struct scatterlist *sg; /* Set only if DDP is setup */
+ u32 sg_cnt; /* No. of item in scatterlist */
+};
+
+extern struct mutex ft_lport_lock;
+extern struct fc4_prov ft_prov;
+extern struct target_fabric_configfs *ft_configfs;
+extern unsigned int ft_debug_logging;
+
+/*
+ * Fabric methods.
+ */
+
+/*
+ * Session ops.
+ */
+void ft_sess_put(struct ft_sess *);
+int ft_sess_shutdown(struct se_session *);
+void ft_sess_close(struct se_session *);
+u32 ft_sess_get_index(struct se_session *);
+u32 ft_sess_get_port_name(struct se_session *, unsigned char *, u32);
+
+void ft_lport_add(struct fc_lport *, void *);
+void ft_lport_del(struct fc_lport *, void *);
+int ft_lport_notify(struct notifier_block *, unsigned long, void *);
+
+/*
+ * IO methods.
+ */
+int ft_check_stop_free(struct se_cmd *);
+void ft_release_cmd(struct se_cmd *);
+int ft_queue_status(struct se_cmd *);
+int ft_queue_data_in(struct se_cmd *);
+int ft_write_pending(struct se_cmd *);
+int ft_write_pending_status(struct se_cmd *);
+u32 ft_get_task_tag(struct se_cmd *);
+int ft_get_cmd_state(struct se_cmd *);
+void ft_queue_tm_resp(struct se_cmd *);
+void ft_aborted_task(struct se_cmd *);
+
+/*
+ * other internal functions.
+ */
+void ft_recv_req(struct ft_sess *, struct fc_frame *);
+struct ft_tpg *ft_lport_find_tpg(struct fc_lport *);
+struct ft_node_acl *ft_acl_get(struct ft_tpg *, struct fc_rport_priv *);
+
+void ft_recv_write_data(struct ft_cmd *, struct fc_frame *);
+void ft_dump_cmd(struct ft_cmd *, const char *caller);
+
+ssize_t ft_format_wwn(char *, size_t, u64);
+
+/*
+ * Underlying HW specific helper function
+ */
+void ft_invl_hw_context(struct ft_cmd *);
+
+#endif /* __TCM_FC_H__ */
diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c
new file mode 100644
index 00000000000..be0c0d08c56
--- /dev/null
+++ b/drivers/target/tcm_fc/tfc_cmd.c
@@ -0,0 +1,585 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* XXX TBD some includes may be extraneous */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/hash.h>
+#include <linux/percpu_ida.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "tcm_fc.h"
+
+/*
+ * Dump cmd state for debugging.
+ */
+static void _ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
+{
+ struct fc_exch *ep;
+ struct fc_seq *sp;
+ struct se_cmd *se_cmd;
+ struct scatterlist *sg;
+ int count;
+
+ se_cmd = &cmd->se_cmd;
+ pr_debug("%s: cmd %p sess %p seq %p se_cmd %p\n",
+ caller, cmd, cmd->sess, cmd->seq, se_cmd);
+
+ pr_debug("%s: cmd %p data_nents %u len %u se_cmd_flags <0x%x>\n",
+ caller, cmd, se_cmd->t_data_nents,
+ se_cmd->data_length, se_cmd->se_cmd_flags);
+
+ for_each_sg(se_cmd->t_data_sg, sg, se_cmd->t_data_nents, count)
+ pr_debug("%s: cmd %p sg %p page %p "
+ "len 0x%x off 0x%x\n",
+ caller, cmd, sg,
+ sg_page(sg), sg->length, sg->offset);
+
+ sp = cmd->seq;
+ if (sp) {
+ ep = fc_seq_exch(sp);
+ pr_debug("%s: cmd %p sid %x did %x "
+ "ox_id %x rx_id %x seq_id %x e_stat %x\n",
+ caller, cmd, ep->sid, ep->did, ep->oxid, ep->rxid,
+ sp->id, ep->esb_stat);
+ }
+}
+
+void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
+{
+ if (unlikely(ft_debug_logging))
+ _ft_dump_cmd(cmd, caller);
+}
+
+static void ft_free_cmd(struct ft_cmd *cmd)
+{
+ struct fc_frame *fp;
+ struct fc_lport *lport;
+ struct ft_sess *sess;
+
+ if (!cmd)
+ return;
+ sess = cmd->sess;
+ fp = cmd->req_frame;
+ lport = fr_dev(fp);
+ if (fr_seq(fp))
+ lport->tt.seq_release(fr_seq(fp));
+ fc_frame_free(fp);
+ percpu_ida_free(&sess->se_sess->sess_tag_pool, cmd->se_cmd.map_tag);
+ ft_sess_put(sess); /* undo get from lookup at recv */
+}
+
+void ft_release_cmd(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+
+ ft_free_cmd(cmd);
+}
+
+int ft_check_stop_free(struct se_cmd *se_cmd)
+{
+ transport_generic_free_cmd(se_cmd, 0);
+ return 1;
+}
+
+/*
+ * Send response.
+ */
+int ft_queue_status(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+ struct fc_frame *fp;
+ struct fcp_resp_with_ext *fcp;
+ struct fc_lport *lport;
+ struct fc_exch *ep;
+ size_t len;
+ int rc;
+
+ if (cmd->aborted)
+ return 0;
+ ft_dump_cmd(cmd, __func__);
+ ep = fc_seq_exch(cmd->seq);
+ lport = ep->lp;
+ len = sizeof(*fcp) + se_cmd->scsi_sense_length;
+ fp = fc_frame_alloc(lport, len);
+ if (!fp) {
+ se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
+ return -ENOMEM;
+ }
+
+ fcp = fc_frame_payload_get(fp, len);
+ memset(fcp, 0, len);
+ fcp->resp.fr_status = se_cmd->scsi_status;
+
+ len = se_cmd->scsi_sense_length;
+ if (len) {
+ fcp->resp.fr_flags |= FCP_SNS_LEN_VAL;
+ fcp->ext.fr_sns_len = htonl(len);
+ memcpy((fcp + 1), se_cmd->sense_buffer, len);
+ }
+
+ /*
+ * Test underflow and overflow with one mask. Usually both are off.
+ * Bidirectional commands are not handled yet.
+ */
+ if (se_cmd->se_cmd_flags & (SCF_OVERFLOW_BIT | SCF_UNDERFLOW_BIT)) {
+ if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT)
+ fcp->resp.fr_flags |= FCP_RESID_OVER;
+ else
+ fcp->resp.fr_flags |= FCP_RESID_UNDER;
+ fcp->ext.fr_resid = cpu_to_be32(se_cmd->residual_count);
+ }
+
+ /*
+ * Send response.
+ */
+ cmd->seq = lport->tt.seq_start_next(cmd->seq);
+ fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
+ FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);
+
+ rc = lport->tt.seq_send(lport, cmd->seq, fp);
+ if (rc) {
+ pr_info_ratelimited("%s: Failed to send response frame %p, "
+ "xid <0x%x>\n", __func__, fp, ep->xid);
+ /*
+ * Generate a TASK_SET_FULL status to notify the initiator
+ * to reduce it's queue_depth after the se_cmd response has
+ * been re-queued by target-core.
+ */
+ se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
+ return -ENOMEM;
+ }
+ lport->tt.exch_done(cmd->seq);
+ return 0;
+}
+
+int ft_write_pending_status(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+
+ return cmd->write_data_len != se_cmd->data_length;
+}
+
+/*
+ * Send TX_RDY (transfer ready).
+ */
+int ft_write_pending(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+ struct fc_frame *fp;
+ struct fcp_txrdy *txrdy;
+ struct fc_lport *lport;
+ struct fc_exch *ep;
+ struct fc_frame_header *fh;
+ u32 f_ctl;
+
+ ft_dump_cmd(cmd, __func__);
+
+ if (cmd->aborted)
+ return 0;
+ ep = fc_seq_exch(cmd->seq);
+ lport = ep->lp;
+ fp = fc_frame_alloc(lport, sizeof(*txrdy));
+ if (!fp)
+ return -ENOMEM; /* Signal QUEUE_FULL */
+
+ txrdy = fc_frame_payload_get(fp, sizeof(*txrdy));
+ memset(txrdy, 0, sizeof(*txrdy));
+ txrdy->ft_burst_len = htonl(se_cmd->data_length);
+
+ cmd->seq = lport->tt.seq_start_next(cmd->seq);
+ fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP,
+ FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+ fh = fc_frame_header_get(fp);
+ f_ctl = ntoh24(fh->fh_f_ctl);
+
+ /* Only if it is 'Exchange Responder' */
+ if (f_ctl & FC_FC_EX_CTX) {
+ /* Target is 'exchange responder' and sending XFER_READY
+ * to 'exchange initiator (initiator)'
+ */
+ if ((ep->xid <= lport->lro_xid) &&
+ (fh->fh_r_ctl == FC_RCTL_DD_DATA_DESC)) {
+ if ((se_cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) &&
+ lport->tt.ddp_target(lport, ep->xid,
+ se_cmd->t_data_sg,
+ se_cmd->t_data_nents))
+ cmd->was_ddp_setup = 1;
+ }
+ }
+ lport->tt.seq_send(lport, cmd->seq, fp);
+ return 0;
+}
+
+u32 ft_get_task_tag(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+
+ if (cmd->aborted)
+ return ~0;
+ return fc_seq_exch(cmd->seq)->rxid;
+}
+
+int ft_get_cmd_state(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+/*
+ * FC sequence response handler for follow-on sequences (data) and aborts.
+ */
+static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg)
+{
+ struct ft_cmd *cmd = arg;
+ struct fc_frame_header *fh;
+
+ if (unlikely(IS_ERR(fp))) {
+ /* XXX need to find cmd if queued */
+ cmd->seq = NULL;
+ cmd->aborted = true;
+ return;
+ }
+
+ fh = fc_frame_header_get(fp);
+
+ switch (fh->fh_r_ctl) {
+ case FC_RCTL_DD_SOL_DATA: /* write data */
+ ft_recv_write_data(cmd, fp);
+ break;
+ case FC_RCTL_DD_UNSOL_CTL: /* command */
+ case FC_RCTL_DD_SOL_CTL: /* transfer ready */
+ case FC_RCTL_DD_DATA_DESC: /* transfer ready */
+ default:
+ pr_debug("%s: unhandled frame r_ctl %x\n",
+ __func__, fh->fh_r_ctl);
+ ft_invl_hw_context(cmd);
+ fc_frame_free(fp);
+ transport_generic_free_cmd(&cmd->se_cmd, 0);
+ break;
+ }
+}
+
+/*
+ * Send a FCP response including SCSI status and optional FCP rsp_code.
+ * status is SAM_STAT_GOOD (zero) iff code is valid.
+ * This is used in error cases, such as allocation failures.
+ */
+static void ft_send_resp_status(struct fc_lport *lport,
+ const struct fc_frame *rx_fp,
+ u32 status, enum fcp_resp_rsp_codes code)
+{
+ struct fc_frame *fp;
+ struct fc_seq *sp;
+ const struct fc_frame_header *fh;
+ size_t len;
+ struct fcp_resp_with_ext *fcp;
+ struct fcp_resp_rsp_info *info;
+
+ fh = fc_frame_header_get(rx_fp);
+ pr_debug("FCP error response: did %x oxid %x status %x code %x\n",
+ ntoh24(fh->fh_s_id), ntohs(fh->fh_ox_id), status, code);
+ len = sizeof(*fcp);
+ if (status == SAM_STAT_GOOD)
+ len += sizeof(*info);
+ fp = fc_frame_alloc(lport, len);
+ if (!fp)
+ return;
+ fcp = fc_frame_payload_get(fp, len);
+ memset(fcp, 0, len);
+ fcp->resp.fr_status = status;
+ if (status == SAM_STAT_GOOD) {
+ fcp->ext.fr_rsp_len = htonl(sizeof(*info));
+ fcp->resp.fr_flags |= FCP_RSP_LEN_VAL;
+ info = (struct fcp_resp_rsp_info *)(fcp + 1);
+ info->rsp_code = code;
+ }
+
+ fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_DD_CMD_STATUS, 0);
+ sp = fr_seq(fp);
+ if (sp) {
+ lport->tt.seq_send(lport, sp, fp);
+ lport->tt.exch_done(sp);
+ } else {
+ lport->tt.frame_send(lport, fp);
+ }
+}
+
+/*
+ * Send error or task management response.
+ */
+static void ft_send_resp_code(struct ft_cmd *cmd,
+ enum fcp_resp_rsp_codes code)
+{
+ ft_send_resp_status(cmd->sess->tport->lport,
+ cmd->req_frame, SAM_STAT_GOOD, code);
+}
+
+
+/*
+ * Send error or task management response.
+ * Always frees the cmd and associated state.
+ */
+static void ft_send_resp_code_and_free(struct ft_cmd *cmd,
+ enum fcp_resp_rsp_codes code)
+{
+ ft_send_resp_code(cmd, code);
+ ft_free_cmd(cmd);
+}
+
+/*
+ * Handle Task Management Request.
+ */
+static void ft_send_tm(struct ft_cmd *cmd)
+{
+ struct fcp_cmnd *fcp;
+ int rc;
+ u8 tm_func;
+
+ fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp));
+
+ switch (fcp->fc_tm_flags) {
+ case FCP_TMF_LUN_RESET:
+ tm_func = TMR_LUN_RESET;
+ break;
+ case FCP_TMF_TGT_RESET:
+ tm_func = TMR_TARGET_WARM_RESET;
+ break;
+ case FCP_TMF_CLR_TASK_SET:
+ tm_func = TMR_CLEAR_TASK_SET;
+ break;
+ case FCP_TMF_ABT_TASK_SET:
+ tm_func = TMR_ABORT_TASK_SET;
+ break;
+ case FCP_TMF_CLR_ACA:
+ tm_func = TMR_CLEAR_ACA;
+ break;
+ default:
+ /*
+ * FCP4r01 indicates having a combination of
+ * tm_flags set is invalid.
+ */
+ pr_debug("invalid FCP tm_flags %x\n", fcp->fc_tm_flags);
+ ft_send_resp_code_and_free(cmd, FCP_CMND_FIELDS_INVALID);
+ return;
+ }
+
+ /* FIXME: Add referenced task tag for ABORT_TASK */
+ rc = target_submit_tmr(&cmd->se_cmd, cmd->sess->se_sess,
+ &cmd->ft_sense_buffer[0], scsilun_to_int(&fcp->fc_lun),
+ cmd, tm_func, GFP_KERNEL, 0, 0);
+ if (rc < 0)
+ ft_send_resp_code_and_free(cmd, FCP_TMF_FAILED);
+}
+
+/*
+ * Send status from completed task management request.
+ */
+void ft_queue_tm_resp(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+ struct se_tmr_req *tmr = se_cmd->se_tmr_req;
+ enum fcp_resp_rsp_codes code;
+
+ if (cmd->aborted)
+ return;
+ switch (tmr->response) {
+ case TMR_FUNCTION_COMPLETE:
+ code = FCP_TMF_CMPL;
+ break;
+ case TMR_LUN_DOES_NOT_EXIST:
+ code = FCP_TMF_INVALID_LUN;
+ break;
+ case TMR_FUNCTION_REJECTED:
+ code = FCP_TMF_REJECTED;
+ break;
+ case TMR_TASK_DOES_NOT_EXIST:
+ case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED:
+ default:
+ code = FCP_TMF_FAILED;
+ break;
+ }
+ pr_debug("tmr fn %d resp %d fcp code %d\n",
+ tmr->function, tmr->response, code);
+ ft_send_resp_code(cmd, code);
+}
+
+void ft_aborted_task(struct se_cmd *se_cmd)
+{
+ return;
+}
+
+static void ft_send_work(struct work_struct *work);
+
+/*
+ * Handle incoming FCP command.
+ */
+static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp)
+{
+ struct ft_cmd *cmd;
+ struct fc_lport *lport = sess->tport->lport;
+ struct se_session *se_sess = sess->se_sess;
+ int tag;
+
+ tag = percpu_ida_alloc(&se_sess->sess_tag_pool, TASK_RUNNING);
+ if (tag < 0)
+ goto busy;
+
+ cmd = &((struct ft_cmd *)se_sess->sess_cmd_map)[tag];
+ memset(cmd, 0, sizeof(struct ft_cmd));
+
+ cmd->se_cmd.map_tag = tag;
+ cmd->sess = sess;
+ cmd->seq = lport->tt.seq_assign(lport, fp);
+ if (!cmd->seq) {
+ percpu_ida_free(&se_sess->sess_tag_pool, tag);
+ goto busy;
+ }
+ cmd->req_frame = fp; /* hold frame during cmd */
+
+ INIT_WORK(&cmd->work, ft_send_work);
+ queue_work(sess->tport->tpg->workqueue, &cmd->work);
+ return;
+
+busy:
+ pr_debug("cmd or seq allocation failure - sending BUSY\n");
+ ft_send_resp_status(lport, fp, SAM_STAT_BUSY, 0);
+ fc_frame_free(fp);
+ ft_sess_put(sess); /* undo get from lookup */
+}
+
+
+/*
+ * Handle incoming FCP frame.
+ * Caller has verified that the frame is type FCP.
+ */
+void ft_recv_req(struct ft_sess *sess, struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+
+ switch (fh->fh_r_ctl) {
+ case FC_RCTL_DD_UNSOL_CMD: /* command */
+ ft_recv_cmd(sess, fp);
+ break;
+ case FC_RCTL_DD_SOL_DATA: /* write data */
+ case FC_RCTL_DD_UNSOL_CTL:
+ case FC_RCTL_DD_SOL_CTL:
+ case FC_RCTL_DD_DATA_DESC: /* transfer ready */
+ case FC_RCTL_ELS4_REQ: /* SRR, perhaps */
+ default:
+ pr_debug("%s: unhandled frame r_ctl %x\n",
+ __func__, fh->fh_r_ctl);
+ fc_frame_free(fp);
+ ft_sess_put(sess); /* undo get from lookup */
+ break;
+ }
+}
+
+/*
+ * Send new command to target.
+ */
+static void ft_send_work(struct work_struct *work)
+{
+ struct ft_cmd *cmd = container_of(work, struct ft_cmd, work);
+ struct fc_frame_header *fh = fc_frame_header_get(cmd->req_frame);
+ struct fcp_cmnd *fcp;
+ int data_dir = 0;
+ int task_attr;
+
+ fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp));
+ if (!fcp)
+ goto err;
+
+ if (fcp->fc_flags & FCP_CFL_LEN_MASK)
+ goto err; /* not handling longer CDBs yet */
+
+ /*
+ * Check for FCP task management flags
+ */
+ if (fcp->fc_tm_flags) {
+ ft_send_tm(cmd);
+ return;
+ }
+
+ switch (fcp->fc_flags & (FCP_CFL_RDDATA | FCP_CFL_WRDATA)) {
+ case 0:
+ data_dir = DMA_NONE;
+ break;
+ case FCP_CFL_RDDATA:
+ data_dir = DMA_FROM_DEVICE;
+ break;
+ case FCP_CFL_WRDATA:
+ data_dir = DMA_TO_DEVICE;
+ break;
+ case FCP_CFL_WRDATA | FCP_CFL_RDDATA:
+ goto err; /* TBD not supported by tcm_fc yet */
+ }
+ /*
+ * Locate the SAM Task Attr from fc_pri_ta
+ */
+ switch (fcp->fc_pri_ta & FCP_PTA_MASK) {
+ case FCP_PTA_HEADQ:
+ task_attr = MSG_HEAD_TAG;
+ break;
+ case FCP_PTA_ORDERED:
+ task_attr = MSG_ORDERED_TAG;
+ break;
+ case FCP_PTA_ACA:
+ task_attr = MSG_ACA_TAG;
+ break;
+ case FCP_PTA_SIMPLE: /* Fallthrough */
+ default:
+ task_attr = MSG_SIMPLE_TAG;
+ }
+
+ fc_seq_exch(cmd->seq)->lp->tt.seq_set_resp(cmd->seq, ft_recv_seq, cmd);
+ /*
+ * Use a single se_cmd->cmd_kref as we expect to release se_cmd
+ * directly from ft_check_stop_free callback in response path.
+ */
+ if (target_submit_cmd(&cmd->se_cmd, cmd->sess->se_sess, fcp->fc_cdb,
+ &cmd->ft_sense_buffer[0], scsilun_to_int(&fcp->fc_lun),
+ ntohl(fcp->fc_dl), task_attr, data_dir, 0))
+ goto err;
+
+ pr_debug("r_ctl %x alloc target_submit_cmd\n", fh->fh_r_ctl);
+ return;
+
+err:
+ ft_send_resp_code_and_free(cmd, FCP_CMND_FIELDS_INVALID);
+}
diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c
new file mode 100644
index 00000000000..efdcb9663a1
--- /dev/null
+++ b/drivers/target/tcm_fc/tfc_conf.c
@@ -0,0 +1,642 @@
+/*******************************************************************************
+ * Filename: tcm_fc.c
+ *
+ * This file contains the configfs implementation for TCM_fc fabric node.
+ * Based on tcm_loop_configfs.c
+ *
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ * Copyright (c) 2009,2010 Rising Tide, Inc.
+ * Copyright (c) 2009,2010 Linux-iSCSI.org
+ *
+ * Copyright (c) 2009,2010 Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <generated/utsrelease.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "tcm_fc.h"
+
+struct target_fabric_configfs *ft_configfs;
+
+static LIST_HEAD(ft_wwn_list);
+DEFINE_MUTEX(ft_lport_lock);
+
+unsigned int ft_debug_logging;
+module_param_named(debug_logging, ft_debug_logging, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+
+/*
+ * Parse WWN.
+ * If strict, we require lower-case hex and colon separators to be sure
+ * the name is the same as what would be generated by ft_format_wwn()
+ * so the name and wwn are mapped one-to-one.
+ */
+static ssize_t ft_parse_wwn(const char *name, u64 *wwn, int strict)
+{
+ const char *cp;
+ char c;
+ u32 byte = 0;
+ u32 pos = 0;
+ u32 err;
+ int val;
+
+ *wwn = 0;
+ for (cp = name; cp < &name[FT_NAMELEN - 1]; cp++) {
+ c = *cp;
+ if (c == '\n' && cp[1] == '\0')
+ continue;
+ if (strict && pos++ == 2 && byte++ < 7) {
+ pos = 0;
+ if (c == ':')
+ continue;
+ err = 1;
+ goto fail;
+ }
+ if (c == '\0') {
+ err = 2;
+ if (strict && byte != 8)
+ goto fail;
+ return cp - name;
+ }
+ err = 3;
+ val = hex_to_bin(c);
+ if (val < 0 || (strict && isupper(c)))
+ goto fail;
+ *wwn = (*wwn << 4) | val;
+ }
+ err = 4;
+fail:
+ pr_debug("err %u len %zu pos %u byte %u\n",
+ err, cp - name, pos, byte);
+ return -1;
+}
+
+ssize_t ft_format_wwn(char *buf, size_t len, u64 wwn)
+{
+ u8 b[8];
+
+ put_unaligned_be64(wwn, b);
+ return snprintf(buf, len,
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+}
+
+static ssize_t ft_wwn_show(void *arg, char *buf)
+{
+ u64 *wwn = arg;
+ ssize_t len;
+
+ len = ft_format_wwn(buf, PAGE_SIZE - 2, *wwn);
+ buf[len++] = '\n';
+ return len;
+}
+
+static ssize_t ft_wwn_store(void *arg, const char *buf, size_t len)
+{
+ ssize_t ret;
+ u64 wwn;
+
+ ret = ft_parse_wwn(buf, &wwn, 0);
+ if (ret > 0)
+ *(u64 *)arg = wwn;
+ return ret;
+}
+
+/*
+ * ACL auth ops.
+ */
+
+static ssize_t ft_nacl_show_port_name(
+ struct se_node_acl *se_nacl,
+ char *page)
+{
+ struct ft_node_acl *acl = container_of(se_nacl,
+ struct ft_node_acl, se_node_acl);
+
+ return ft_wwn_show(&acl->node_auth.port_name, page);
+}
+
+static ssize_t ft_nacl_store_port_name(
+ struct se_node_acl *se_nacl,
+ const char *page,
+ size_t count)
+{
+ struct ft_node_acl *acl = container_of(se_nacl,
+ struct ft_node_acl, se_node_acl);
+
+ return ft_wwn_store(&acl->node_auth.port_name, page, count);
+}
+
+TF_NACL_BASE_ATTR(ft, port_name, S_IRUGO | S_IWUSR);
+
+static ssize_t ft_nacl_show_node_name(
+ struct se_node_acl *se_nacl,
+ char *page)
+{
+ struct ft_node_acl *acl = container_of(se_nacl,
+ struct ft_node_acl, se_node_acl);
+
+ return ft_wwn_show(&acl->node_auth.node_name, page);
+}
+
+static ssize_t ft_nacl_store_node_name(
+ struct se_node_acl *se_nacl,
+ const char *page,
+ size_t count)
+{
+ struct ft_node_acl *acl = container_of(se_nacl,
+ struct ft_node_acl, se_node_acl);
+
+ return ft_wwn_store(&acl->node_auth.node_name, page, count);
+}
+
+TF_NACL_BASE_ATTR(ft, node_name, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *ft_nacl_base_attrs[] = {
+ &ft_nacl_port_name.attr,
+ &ft_nacl_node_name.attr,
+ NULL,
+};
+
+/*
+ * ACL ops.
+ */
+
+/*
+ * Add ACL for an initiator. The ACL is named arbitrarily.
+ * The port_name and/or node_name are attributes.
+ */
+static struct se_node_acl *ft_add_acl(
+ struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
+{
+ struct ft_node_acl *acl;
+ struct ft_tpg *tpg;
+ u64 wwpn;
+ u32 q_depth;
+
+ pr_debug("add acl %s\n", name);
+ tpg = container_of(se_tpg, struct ft_tpg, se_tpg);
+
+ if (ft_parse_wwn(name, &wwpn, 1) < 0)
+ return ERR_PTR(-EINVAL);
+
+ acl = kzalloc(sizeof(struct ft_node_acl), GFP_KERNEL);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+ acl->node_auth.port_name = wwpn;
+
+ q_depth = 32; /* XXX bogus default - get from tpg? */
+ return core_tpg_add_initiator_node_acl(&tpg->se_tpg,
+ &acl->se_node_acl, name, q_depth);
+}
+
+static void ft_del_acl(struct se_node_acl *se_acl)
+{
+ struct se_portal_group *se_tpg = se_acl->se_tpg;
+ struct ft_tpg *tpg;
+ struct ft_node_acl *acl = container_of(se_acl,
+ struct ft_node_acl, se_node_acl);
+
+ pr_debug("del acl %s\n",
+ config_item_name(&se_acl->acl_group.cg_item));
+
+ tpg = container_of(se_tpg, struct ft_tpg, se_tpg);
+ pr_debug("del acl %p se_acl %p tpg %p se_tpg %p\n",
+ acl, se_acl, tpg, &tpg->se_tpg);
+
+ core_tpg_del_initiator_node_acl(&tpg->se_tpg, se_acl, 1);
+ kfree(acl);
+}
+
+struct ft_node_acl *ft_acl_get(struct ft_tpg *tpg, struct fc_rport_priv *rdata)
+{
+ struct ft_node_acl *found = NULL;
+ struct ft_node_acl *acl;
+ struct se_portal_group *se_tpg = &tpg->se_tpg;
+ struct se_node_acl *se_acl;
+
+ spin_lock_irq(&se_tpg->acl_node_lock);
+ list_for_each_entry(se_acl, &se_tpg->acl_node_list, acl_list) {
+ acl = container_of(se_acl, struct ft_node_acl, se_node_acl);
+ pr_debug("acl %p port_name %llx\n",
+ acl, (unsigned long long)acl->node_auth.port_name);
+ if (acl->node_auth.port_name == rdata->ids.port_name ||
+ acl->node_auth.node_name == rdata->ids.node_name) {
+ pr_debug("acl %p port_name %llx matched\n", acl,
+ (unsigned long long)rdata->ids.port_name);
+ found = acl;
+ /* XXX need to hold onto ACL */
+ break;
+ }
+ }
+ spin_unlock_irq(&se_tpg->acl_node_lock);
+ return found;
+}
+
+static struct se_node_acl *ft_tpg_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+ struct ft_node_acl *acl;
+
+ acl = kzalloc(sizeof(*acl), GFP_KERNEL);
+ if (!acl) {
+ pr_err("Unable to allocate struct ft_node_acl\n");
+ return NULL;
+ }
+ pr_debug("acl %p\n", acl);
+ return &acl->se_node_acl;
+}
+
+static void ft_tpg_release_fabric_acl(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_acl)
+{
+ struct ft_node_acl *acl = container_of(se_acl,
+ struct ft_node_acl, se_node_acl);
+
+ pr_debug("acl %p\n", acl);
+ kfree(acl);
+}
+
+/*
+ * local_port port_group (tpg) ops.
+ */
+static struct se_portal_group *ft_add_tpg(
+ struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct ft_lport_wwn *ft_wwn;
+ struct ft_tpg *tpg;
+ struct workqueue_struct *wq;
+ unsigned long index;
+ int ret;
+
+ pr_debug("tcm_fc: add tpg %s\n", name);
+
+ /*
+ * Name must be "tpgt_" followed by the index.
+ */
+ if (strstr(name, "tpgt_") != name)
+ return NULL;
+
+ ret = kstrtoul(name + 5, 10, &index);
+ if (ret)
+ return NULL;
+ if (index > UINT_MAX)
+ return NULL;
+
+ if ((index != 1)) {
+ pr_err("Error, a single TPG=1 is used for HW port mappings\n");
+ return ERR_PTR(-ENOSYS);
+ }
+
+ ft_wwn = container_of(wwn, struct ft_lport_wwn, se_wwn);
+ tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
+ if (!tpg)
+ return NULL;
+ tpg->index = index;
+ tpg->lport_wwn = ft_wwn;
+ INIT_LIST_HEAD(&tpg->lun_list);
+
+ wq = alloc_workqueue("tcm_fc", 0, 1);
+ if (!wq) {
+ kfree(tpg);
+ return NULL;
+ }
+
+ ret = core_tpg_register(&ft_configfs->tf_ops, wwn, &tpg->se_tpg,
+ tpg, TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0) {
+ destroy_workqueue(wq);
+ kfree(tpg);
+ return NULL;
+ }
+ tpg->workqueue = wq;
+
+ mutex_lock(&ft_lport_lock);
+ ft_wwn->tpg = tpg;
+ mutex_unlock(&ft_lport_lock);
+
+ return &tpg->se_tpg;
+}
+
+static void ft_del_tpg(struct se_portal_group *se_tpg)
+{
+ struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg);
+ struct ft_lport_wwn *ft_wwn = tpg->lport_wwn;
+
+ pr_debug("del tpg %s\n",
+ config_item_name(&tpg->se_tpg.tpg_group.cg_item));
+
+ destroy_workqueue(tpg->workqueue);
+
+ /* Wait for sessions to be freed thru RCU, for BUG_ON below */
+ synchronize_rcu();
+
+ mutex_lock(&ft_lport_lock);
+ ft_wwn->tpg = NULL;
+ if (tpg->tport) {
+ tpg->tport->tpg = NULL;
+ tpg->tport = NULL;
+ }
+ mutex_unlock(&ft_lport_lock);
+
+ core_tpg_deregister(se_tpg);
+ kfree(tpg);
+}
+
+/*
+ * Verify that an lport is configured to use the tcm_fc module, and return
+ * the target port group that should be used.
+ *
+ * The caller holds ft_lport_lock.
+ */
+struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport)
+{
+ struct ft_lport_wwn *ft_wwn;
+
+ list_for_each_entry(ft_wwn, &ft_wwn_list, ft_wwn_node) {
+ if (ft_wwn->wwpn == lport->wwpn)
+ return ft_wwn->tpg;
+ }
+ return NULL;
+}
+
+/*
+ * target config instance ops.
+ */
+
+/*
+ * Add lport to allowed config.
+ * The name is the WWPN in lower-case ASCII, colon-separated bytes.
+ */
+static struct se_wwn *ft_add_wwn(
+ struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct ft_lport_wwn *ft_wwn;
+ struct ft_lport_wwn *old_ft_wwn;
+ u64 wwpn;
+
+ pr_debug("add wwn %s\n", name);
+ if (ft_parse_wwn(name, &wwpn, 1) < 0)
+ return NULL;
+ ft_wwn = kzalloc(sizeof(*ft_wwn), GFP_KERNEL);
+ if (!ft_wwn)
+ return NULL;
+ ft_wwn->wwpn = wwpn;
+
+ mutex_lock(&ft_lport_lock);
+ list_for_each_entry(old_ft_wwn, &ft_wwn_list, ft_wwn_node) {
+ if (old_ft_wwn->wwpn == wwpn) {
+ mutex_unlock(&ft_lport_lock);
+ kfree(ft_wwn);
+ return NULL;
+ }
+ }
+ list_add_tail(&ft_wwn->ft_wwn_node, &ft_wwn_list);
+ ft_format_wwn(ft_wwn->name, sizeof(ft_wwn->name), wwpn);
+ mutex_unlock(&ft_lport_lock);
+
+ return &ft_wwn->se_wwn;
+}
+
+static void ft_del_wwn(struct se_wwn *wwn)
+{
+ struct ft_lport_wwn *ft_wwn = container_of(wwn,
+ struct ft_lport_wwn, se_wwn);
+
+ pr_debug("del wwn %s\n", ft_wwn->name);
+ mutex_lock(&ft_lport_lock);
+ list_del(&ft_wwn->ft_wwn_node);
+ mutex_unlock(&ft_lport_lock);
+
+ kfree(ft_wwn);
+}
+
+static ssize_t ft_wwn_show_attr_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "TCM FC " FT_VERSION " on %s/%s on "
+ ""UTS_RELEASE"\n", utsname()->sysname, utsname()->machine);
+}
+
+TF_WWN_ATTR_RO(ft, version);
+
+static struct configfs_attribute *ft_wwn_attrs[] = {
+ &ft_wwn_version.attr,
+ NULL,
+};
+
+static char *ft_get_fabric_name(void)
+{
+ return "fc";
+}
+
+static char *ft_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ return tpg->lport_wwn->name;
+}
+
+static u16 ft_get_tag(struct se_portal_group *se_tpg)
+{
+ struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ /*
+ * This tag is used when forming SCSI Name identifier in EVPD=1 0x83
+ * to represent the SCSI Target Port.
+ */
+ return tpg->index;
+}
+
+static u32 ft_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int ft_check_false(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+static void ft_set_default_node_attr(struct se_node_acl *se_nacl)
+{
+}
+
+static u32 ft_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+ struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ return tpg->index;
+}
+
+static struct target_core_fabric_ops ft_fabric_ops = {
+ .get_fabric_name = ft_get_fabric_name,
+ .get_fabric_proto_ident = fc_get_fabric_proto_ident,
+ .tpg_get_wwn = ft_get_fabric_wwn,
+ .tpg_get_tag = ft_get_tag,
+ .tpg_get_default_depth = ft_get_default_depth,
+ .tpg_get_pr_transport_id = fc_get_pr_transport_id,
+ .tpg_get_pr_transport_id_len = fc_get_pr_transport_id_len,
+ .tpg_parse_pr_out_transport_id = fc_parse_pr_out_transport_id,
+ .tpg_check_demo_mode = ft_check_false,
+ .tpg_check_demo_mode_cache = ft_check_false,
+ .tpg_check_demo_mode_write_protect = ft_check_false,
+ .tpg_check_prod_mode_write_protect = ft_check_false,
+ .tpg_alloc_fabric_acl = ft_tpg_alloc_fabric_acl,
+ .tpg_release_fabric_acl = ft_tpg_release_fabric_acl,
+ .tpg_get_inst_index = ft_tpg_get_inst_index,
+ .check_stop_free = ft_check_stop_free,
+ .release_cmd = ft_release_cmd,
+ .shutdown_session = ft_sess_shutdown,
+ .close_session = ft_sess_close,
+ .sess_get_index = ft_sess_get_index,
+ .sess_get_initiator_sid = NULL,
+ .write_pending = ft_write_pending,
+ .write_pending_status = ft_write_pending_status,
+ .set_default_node_attributes = ft_set_default_node_attr,
+ .get_task_tag = ft_get_task_tag,
+ .get_cmd_state = ft_get_cmd_state,
+ .queue_data_in = ft_queue_data_in,
+ .queue_status = ft_queue_status,
+ .queue_tm_rsp = ft_queue_tm_resp,
+ .aborted_task = ft_aborted_task,
+ /*
+ * Setup function pointers for generic logic in
+ * target_core_fabric_configfs.c
+ */
+ .fabric_make_wwn = &ft_add_wwn,
+ .fabric_drop_wwn = &ft_del_wwn,
+ .fabric_make_tpg = &ft_add_tpg,
+ .fabric_drop_tpg = &ft_del_tpg,
+ .fabric_post_link = NULL,
+ .fabric_pre_unlink = NULL,
+ .fabric_make_np = NULL,
+ .fabric_drop_np = NULL,
+ .fabric_make_nodeacl = &ft_add_acl,
+ .fabric_drop_nodeacl = &ft_del_acl,
+};
+
+static int ft_register_configfs(void)
+{
+ struct target_fabric_configfs *fabric;
+ int ret;
+
+ /*
+ * Register the top level struct config_item_type with TCM core
+ */
+ fabric = target_fabric_configfs_init(THIS_MODULE, "fc");
+ if (IS_ERR(fabric)) {
+ pr_err("%s: target_fabric_configfs_init() failed!\n",
+ __func__);
+ return PTR_ERR(fabric);
+ }
+ fabric->tf_ops = ft_fabric_ops;
+
+ /*
+ * Setup default attribute lists for various fabric->tf_cit_tmpl
+ */
+ fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = ft_wwn_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs =
+ ft_nacl_base_attrs;
+ fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+ fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+ /*
+ * register the fabric for use within TCM
+ */
+ ret = target_fabric_configfs_register(fabric);
+ if (ret < 0) {
+ pr_debug("target_fabric_configfs_register() for"
+ " FC Target failed!\n");
+ target_fabric_configfs_free(fabric);
+ return -1;
+ }
+
+ /*
+ * Setup our local pointer to *fabric.
+ */
+ ft_configfs = fabric;
+ return 0;
+}
+
+static void ft_deregister_configfs(void)
+{
+ if (!ft_configfs)
+ return;
+ target_fabric_configfs_deregister(ft_configfs);
+ ft_configfs = NULL;
+}
+
+static struct notifier_block ft_notifier = {
+ .notifier_call = ft_lport_notify
+};
+
+static int __init ft_init(void)
+{
+ if (ft_register_configfs())
+ return -1;
+ if (fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov)) {
+ ft_deregister_configfs();
+ return -1;
+ }
+ blocking_notifier_chain_register(&fc_lport_notifier_head, &ft_notifier);
+ fc_lport_iterate(ft_lport_add, NULL);
+ return 0;
+}
+
+static void __exit ft_exit(void)
+{
+ blocking_notifier_chain_unregister(&fc_lport_notifier_head,
+ &ft_notifier);
+ fc_fc4_deregister_provider(FC_TYPE_FCP, &ft_prov);
+ fc_lport_iterate(ft_lport_del, NULL);
+ ft_deregister_configfs();
+ synchronize_rcu();
+}
+
+MODULE_DESCRIPTION("FC TCM fabric driver " FT_VERSION);
+MODULE_LICENSE("GPL");
+module_init(ft_init);
+module_exit(ft_exit);
diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c
new file mode 100644
index 00000000000..97b486c3dda
--- /dev/null
+++ b/drivers/target/tcm_fc/tfc_io.c
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * Portions based on tcm_loop_fabric_scsi.c and libfc/fc_fcp.c
+ *
+ * Copyright (c) 2007 Intel Corporation. All rights reserved.
+ * Copyright (c) 2008 Red Hat, Inc. All rights reserved.
+ * Copyright (c) 2008 Mike Christie
+ * Copyright (c) 2009 Rising Tide, Inc.
+ * Copyright (c) 2009 Linux-iSCSI.org
+ * Copyright (c) 2009 Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* XXX TBD some includes may be extraneous */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/hash.h>
+#include <linux/ratelimit.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "tcm_fc.h"
+
+/*
+ * Deliver read data back to initiator.
+ * XXX TBD handle resource problems later.
+ */
+int ft_queue_data_in(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+ struct fc_frame *fp = NULL;
+ struct fc_exch *ep;
+ struct fc_lport *lport;
+ struct scatterlist *sg = NULL;
+ size_t remaining;
+ u32 f_ctl = FC_FC_EX_CTX | FC_FC_REL_OFF;
+ u32 mem_off = 0;
+ u32 fh_off = 0;
+ u32 frame_off = 0;
+ size_t frame_len = 0;
+ size_t mem_len = 0;
+ size_t tlen;
+ size_t off_in_page;
+ struct page *page = NULL;
+ int use_sg;
+ int error;
+ void *page_addr;
+ void *from;
+ void *to = NULL;
+
+ if (cmd->aborted)
+ return 0;
+
+ if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL)
+ goto queue_status;
+
+ ep = fc_seq_exch(cmd->seq);
+ lport = ep->lp;
+ cmd->seq = lport->tt.seq_start_next(cmd->seq);
+
+ remaining = se_cmd->data_length;
+
+ /*
+ * Setup to use first mem list entry, unless no data.
+ */
+ BUG_ON(remaining && !se_cmd->t_data_sg);
+ if (remaining) {
+ sg = se_cmd->t_data_sg;
+ mem_len = sg->length;
+ mem_off = sg->offset;
+ page = sg_page(sg);
+ }
+
+ /* no scatter/gather in skb for odd word length due to fc_seq_send() */
+ use_sg = !(remaining % 4);
+
+ while (remaining) {
+ struct fc_seq *seq = cmd->seq;
+
+ if (!seq) {
+ pr_debug("%s: Command aborted, xid 0x%x\n",
+ __func__, ep->xid);
+ break;
+ }
+ if (!mem_len) {
+ sg = sg_next(sg);
+ mem_len = min((size_t)sg->length, remaining);
+ mem_off = sg->offset;
+ page = sg_page(sg);
+ }
+ if (!frame_len) {
+ /*
+ * If lport's has capability of Large Send Offload LSO)
+ * , then allow 'frame_len' to be as big as 'lso_max'
+ * if indicated transfer length is >= lport->lso_max
+ */
+ frame_len = (lport->seq_offload) ? lport->lso_max :
+ cmd->sess->max_frame;
+ frame_len = min(frame_len, remaining);
+ fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len);
+ if (!fp)
+ return -ENOMEM;
+ to = fc_frame_payload_get(fp, 0);
+ fh_off = frame_off;
+ frame_off += frame_len;
+ /*
+ * Setup the frame's max payload which is used by base
+ * driver to indicate HW about max frame size, so that
+ * HW can do fragmentation appropriately based on
+ * "gso_max_size" of underline netdev.
+ */
+ fr_max_payload(fp) = cmd->sess->max_frame;
+ }
+ tlen = min(mem_len, frame_len);
+
+ if (use_sg) {
+ off_in_page = mem_off;
+ BUG_ON(!page);
+ get_page(page);
+ skb_fill_page_desc(fp_skb(fp),
+ skb_shinfo(fp_skb(fp))->nr_frags,
+ page, off_in_page, tlen);
+ fr_len(fp) += tlen;
+ fp_skb(fp)->data_len += tlen;
+ fp_skb(fp)->truesize +=
+ PAGE_SIZE << compound_order(page);
+ } else {
+ BUG_ON(!page);
+ from = kmap_atomic(page + (mem_off >> PAGE_SHIFT));
+ page_addr = from;
+ from += mem_off & ~PAGE_MASK;
+ tlen = min(tlen, (size_t)(PAGE_SIZE -
+ (mem_off & ~PAGE_MASK)));
+ memcpy(to, from, tlen);
+ kunmap_atomic(page_addr);
+ to += tlen;
+ }
+
+ mem_off += tlen;
+ mem_len -= tlen;
+ frame_len -= tlen;
+ remaining -= tlen;
+
+ if (frame_len &&
+ (skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN))
+ continue;
+ if (!remaining)
+ f_ctl |= FC_FC_END_SEQ;
+ fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
+ FC_TYPE_FCP, f_ctl, fh_off);
+ error = lport->tt.seq_send(lport, seq, fp);
+ if (error) {
+ pr_info_ratelimited("%s: Failed to send frame %p, "
+ "xid <0x%x>, remaining %zu, "
+ "lso_max <0x%x>\n",
+ __func__, fp, ep->xid,
+ remaining, lport->lso_max);
+ /*
+ * Go ahead and set TASK_SET_FULL status ignoring the
+ * rest of the DataIN, and immediately attempt to
+ * send the response via ft_queue_status() in order
+ * to notify the initiator that it should reduce it's
+ * per LUN queue_depth.
+ */
+ se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
+ break;
+ }
+ }
+queue_status:
+ return ft_queue_status(se_cmd);
+}
+
+static void ft_execute_work(struct work_struct *work)
+{
+ struct ft_cmd *cmd = container_of(work, struct ft_cmd, work);
+
+ target_execute_cmd(&cmd->se_cmd);
+}
+
+/*
+ * Receive write data frame.
+ */
+void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *fp)
+{
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct fc_seq *seq = cmd->seq;
+ struct fc_exch *ep;
+ struct fc_lport *lport;
+ struct fc_frame_header *fh;
+ struct scatterlist *sg = NULL;
+ u32 mem_off = 0;
+ u32 rel_off;
+ size_t frame_len;
+ size_t mem_len = 0;
+ size_t tlen;
+ struct page *page = NULL;
+ void *page_addr;
+ void *from;
+ void *to;
+ u32 f_ctl;
+ void *buf;
+
+ fh = fc_frame_header_get(fp);
+ if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF))
+ goto drop;
+
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ ep = fc_seq_exch(seq);
+ lport = ep->lp;
+ if (cmd->was_ddp_setup) {
+ BUG_ON(!ep);
+ BUG_ON(!lport);
+ /*
+ * Since DDP (Large Rx offload) was setup for this request,
+ * payload is expected to be copied directly to user buffers.
+ */
+ buf = fc_frame_payload_get(fp, 1);
+ if (buf)
+ pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, "
+ "cmd->sg_cnt 0x%x. DDP was setup"
+ " hence not expected to receive frame with "
+ "payload, Frame will be dropped if"
+ "'Sequence Initiative' bit in f_ctl is"
+ "not set\n", __func__, ep->xid, f_ctl,
+ se_cmd->t_data_sg, se_cmd->t_data_nents);
+ /*
+ * Invalidate HW DDP context if it was setup for respective
+ * command. Invalidation of HW DDP context is requited in both
+ * situation (success and error).
+ */
+ ft_invl_hw_context(cmd);
+
+ /*
+ * If "Sequence Initiative (TSI)" bit set in f_ctl, means last
+ * write data frame is received successfully where payload is
+ * posted directly to user buffer and only the last frame's
+ * header is posted in receive queue.
+ *
+ * If "Sequence Initiative (TSI)" bit is not set, means error
+ * condition w.r.t. DDP, hence drop the packet and let explict
+ * ABORTS from other end of exchange timer trigger the recovery.
+ */
+ if (f_ctl & FC_FC_SEQ_INIT)
+ goto last_frame;
+ else
+ goto drop;
+ }
+
+ rel_off = ntohl(fh->fh_parm_offset);
+ frame_len = fr_len(fp);
+ if (frame_len <= sizeof(*fh))
+ goto drop;
+ frame_len -= sizeof(*fh);
+ from = fc_frame_payload_get(fp, 0);
+ if (rel_off >= se_cmd->data_length)
+ goto drop;
+ if (frame_len + rel_off > se_cmd->data_length)
+ frame_len = se_cmd->data_length - rel_off;
+
+ /*
+ * Setup to use first mem list entry, unless no data.
+ */
+ BUG_ON(frame_len && !se_cmd->t_data_sg);
+ if (frame_len) {
+ sg = se_cmd->t_data_sg;
+ mem_len = sg->length;
+ mem_off = sg->offset;
+ page = sg_page(sg);
+ }
+
+ while (frame_len) {
+ if (!mem_len) {
+ sg = sg_next(sg);
+ mem_len = sg->length;
+ mem_off = sg->offset;
+ page = sg_page(sg);
+ }
+ if (rel_off >= mem_len) {
+ rel_off -= mem_len;
+ mem_len = 0;
+ continue;
+ }
+ mem_off += rel_off;
+ mem_len -= rel_off;
+ rel_off = 0;
+
+ tlen = min(mem_len, frame_len);
+
+ to = kmap_atomic(page + (mem_off >> PAGE_SHIFT));
+ page_addr = to;
+ to += mem_off & ~PAGE_MASK;
+ tlen = min(tlen, (size_t)(PAGE_SIZE -
+ (mem_off & ~PAGE_MASK)));
+ memcpy(to, from, tlen);
+ kunmap_atomic(page_addr);
+
+ from += tlen;
+ frame_len -= tlen;
+ mem_off += tlen;
+ mem_len -= tlen;
+ cmd->write_data_len += tlen;
+ }
+last_frame:
+ if (cmd->write_data_len == se_cmd->data_length) {
+ INIT_WORK(&cmd->work, ft_execute_work);
+ queue_work(cmd->sess->tport->tpg->workqueue, &cmd->work);
+ }
+drop:
+ fc_frame_free(fp);
+}
+
+/*
+ * Handle and cleanup any HW specific resources if
+ * received ABORTS, errors, timeouts.
+ */
+void ft_invl_hw_context(struct ft_cmd *cmd)
+{
+ struct fc_seq *seq;
+ struct fc_exch *ep = NULL;
+ struct fc_lport *lport = NULL;
+
+ BUG_ON(!cmd);
+ seq = cmd->seq;
+
+ /* Cleanup the DDP context in HW if DDP was setup */
+ if (cmd->was_ddp_setup && seq) {
+ ep = fc_seq_exch(seq);
+ if (ep) {
+ lport = ep->lp;
+ if (lport && (ep->xid <= lport->lro_xid))
+ /*
+ * "ddp_done" trigger invalidation of HW
+ * specific DDP context
+ */
+ cmd->write_data_len = lport->tt.ddp_done(lport,
+ ep->xid);
+
+ /*
+ * Resetting same variable to indicate HW's
+ * DDP context has been invalidated to avoid
+ * re_invalidation of same context (context is
+ * identified using ep->xid)
+ */
+ cmd->was_ddp_setup = 0;
+ }
+ }
+}
diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c
new file mode 100644
index 00000000000..21ce50880c7
--- /dev/null
+++ b/drivers/target/tcm_fc/tfc_sess.c
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* XXX TBD some includes may be extraneous */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/hash.h>
+#include <linux/rcupdate.h>
+#include <linux/rculist.h>
+#include <linux/kref.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+
+#include "tcm_fc.h"
+
+static void ft_sess_delete_all(struct ft_tport *);
+
+/*
+ * Lookup or allocate target local port.
+ * Caller holds ft_lport_lock.
+ */
+static struct ft_tport *ft_tport_get(struct fc_lport *lport)
+{
+ struct ft_tpg *tpg;
+ struct ft_tport *tport;
+ int i;
+
+ tport = rcu_dereference_protected(lport->prov[FC_TYPE_FCP],
+ lockdep_is_held(&ft_lport_lock));
+ if (tport && tport->tpg)
+ return tport;
+
+ tpg = ft_lport_find_tpg(lport);
+ if (!tpg)
+ return NULL;
+
+ if (tport) {
+ tport->tpg = tpg;
+ tpg->tport = tport;
+ return tport;
+ }
+
+ tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+ if (!tport)
+ return NULL;
+
+ tport->lport = lport;
+ tport->tpg = tpg;
+ tpg->tport = tport;
+ for (i = 0; i < FT_SESS_HASH_SIZE; i++)
+ INIT_HLIST_HEAD(&tport->hash[i]);
+
+ rcu_assign_pointer(lport->prov[FC_TYPE_FCP], tport);
+ return tport;
+}
+
+/*
+ * Delete a target local port.
+ * Caller holds ft_lport_lock.
+ */
+static void ft_tport_delete(struct ft_tport *tport)
+{
+ struct fc_lport *lport;
+ struct ft_tpg *tpg;
+
+ ft_sess_delete_all(tport);
+ lport = tport->lport;
+ BUG_ON(tport != lport->prov[FC_TYPE_FCP]);
+ rcu_assign_pointer(lport->prov[FC_TYPE_FCP], NULL);
+
+ tpg = tport->tpg;
+ if (tpg) {
+ tpg->tport = NULL;
+ tport->tpg = NULL;
+ }
+ kfree_rcu(tport, rcu);
+}
+
+/*
+ * Add local port.
+ * Called thru fc_lport_iterate().
+ */
+void ft_lport_add(struct fc_lport *lport, void *arg)
+{
+ mutex_lock(&ft_lport_lock);
+ ft_tport_get(lport);
+ mutex_unlock(&ft_lport_lock);
+}
+
+/*
+ * Delete local port.
+ * Called thru fc_lport_iterate().
+ */
+void ft_lport_del(struct fc_lport *lport, void *arg)
+{
+ struct ft_tport *tport;
+
+ mutex_lock(&ft_lport_lock);
+ tport = lport->prov[FC_TYPE_FCP];
+ if (tport)
+ ft_tport_delete(tport);
+ mutex_unlock(&ft_lport_lock);
+}
+
+/*
+ * Notification of local port change from libfc.
+ * Create or delete local port and associated tport.
+ */
+int ft_lport_notify(struct notifier_block *nb, unsigned long event, void *arg)
+{
+ struct fc_lport *lport = arg;
+
+ switch (event) {
+ case FC_LPORT_EV_ADD:
+ ft_lport_add(lport, NULL);
+ break;
+ case FC_LPORT_EV_DEL:
+ ft_lport_del(lport, NULL);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+/*
+ * Hash function for FC_IDs.
+ */
+static u32 ft_sess_hash(u32 port_id)
+{
+ return hash_32(port_id, FT_SESS_HASH_BITS);
+}
+
+/*
+ * Find session in local port.
+ * Sessions and hash lists are RCU-protected.
+ * A reference is taken which must be eventually freed.
+ */
+static struct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id)
+{
+ struct ft_tport *tport;
+ struct hlist_head *head;
+ struct ft_sess *sess;
+
+ rcu_read_lock();
+ tport = rcu_dereference(lport->prov[FC_TYPE_FCP]);
+ if (!tport)
+ goto out;
+
+ head = &tport->hash[ft_sess_hash(port_id)];
+ hlist_for_each_entry_rcu(sess, head, hash) {
+ if (sess->port_id == port_id) {
+ kref_get(&sess->kref);
+ rcu_read_unlock();
+ pr_debug("port_id %x found %p\n", port_id, sess);
+ return sess;
+ }
+ }
+out:
+ rcu_read_unlock();
+ pr_debug("port_id %x not found\n", port_id);
+ return NULL;
+}
+
+/*
+ * Allocate session and enter it in the hash for the local port.
+ * Caller holds ft_lport_lock.
+ */
+static struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id,
+ struct ft_node_acl *acl)
+{
+ struct ft_sess *sess;
+ struct hlist_head *head;
+
+ head = &tport->hash[ft_sess_hash(port_id)];
+ hlist_for_each_entry_rcu(sess, head, hash)
+ if (sess->port_id == port_id)
+ return sess;
+
+ sess = kzalloc(sizeof(*sess), GFP_KERNEL);
+ if (!sess)
+ return NULL;
+
+ sess->se_sess = transport_init_session_tags(TCM_FC_DEFAULT_TAGS,
+ sizeof(struct ft_cmd),
+ TARGET_PROT_NORMAL);
+ if (IS_ERR(sess->se_sess)) {
+ kfree(sess);
+ return NULL;
+ }
+ sess->se_sess->se_node_acl = &acl->se_node_acl;
+ sess->tport = tport;
+ sess->port_id = port_id;
+ kref_init(&sess->kref); /* ref for table entry */
+ hlist_add_head_rcu(&sess->hash, head);
+ tport->sess_count++;
+
+ pr_debug("port_id %x sess %p\n", port_id, sess);
+
+ transport_register_session(&tport->tpg->se_tpg, &acl->se_node_acl,
+ sess->se_sess, sess);
+ return sess;
+}
+
+/*
+ * Unhash the session.
+ * Caller holds ft_lport_lock.
+ */
+static void ft_sess_unhash(struct ft_sess *sess)
+{
+ struct ft_tport *tport = sess->tport;
+
+ hlist_del_rcu(&sess->hash);
+ BUG_ON(!tport->sess_count);
+ tport->sess_count--;
+ sess->port_id = -1;
+ sess->params = 0;
+}
+
+/*
+ * Delete session from hash.
+ * Caller holds ft_lport_lock.
+ */
+static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id)
+{
+ struct hlist_head *head;
+ struct ft_sess *sess;
+
+ head = &tport->hash[ft_sess_hash(port_id)];
+ hlist_for_each_entry_rcu(sess, head, hash) {
+ if (sess->port_id == port_id) {
+ ft_sess_unhash(sess);
+ return sess;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Delete all sessions from tport.
+ * Caller holds ft_lport_lock.
+ */
+static void ft_sess_delete_all(struct ft_tport *tport)
+{
+ struct hlist_head *head;
+ struct ft_sess *sess;
+
+ for (head = tport->hash;
+ head < &tport->hash[FT_SESS_HASH_SIZE]; head++) {
+ hlist_for_each_entry_rcu(sess, head, hash) {
+ ft_sess_unhash(sess);
+ transport_deregister_session_configfs(sess->se_sess);
+ ft_sess_put(sess); /* release from table */
+ }
+ }
+}
+
+/*
+ * TCM ops for sessions.
+ */
+
+/*
+ * Determine whether session is allowed to be shutdown in the current context.
+ * Returns non-zero if the session should be shutdown.
+ */
+int ft_sess_shutdown(struct se_session *se_sess)
+{
+ struct ft_sess *sess = se_sess->fabric_sess_ptr;
+
+ pr_debug("port_id %x\n", sess->port_id);
+ return 1;
+}
+
+/*
+ * Remove session and send PRLO.
+ * This is called when the ACL is being deleted or queue depth is changing.
+ */
+void ft_sess_close(struct se_session *se_sess)
+{
+ struct ft_sess *sess = se_sess->fabric_sess_ptr;
+ u32 port_id;
+
+ mutex_lock(&ft_lport_lock);
+ port_id = sess->port_id;
+ if (port_id == -1) {
+ mutex_unlock(&ft_lport_lock);
+ return;
+ }
+ pr_debug("port_id %x\n", port_id);
+ ft_sess_unhash(sess);
+ mutex_unlock(&ft_lport_lock);
+ transport_deregister_session_configfs(se_sess);
+ ft_sess_put(sess);
+ /* XXX Send LOGO or PRLO */
+ synchronize_rcu(); /* let transport deregister happen */
+}
+
+u32 ft_sess_get_index(struct se_session *se_sess)
+{
+ struct ft_sess *sess = se_sess->fabric_sess_ptr;
+
+ return sess->port_id; /* XXX TBD probably not what is needed */
+}
+
+u32 ft_sess_get_port_name(struct se_session *se_sess,
+ unsigned char *buf, u32 len)
+{
+ struct ft_sess *sess = se_sess->fabric_sess_ptr;
+
+ return ft_format_wwn(buf, len, sess->port_name);
+}
+
+/*
+ * libfc ops involving sessions.
+ */
+
+static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *rspp, struct fc_els_spp *spp)
+{
+ struct ft_tport *tport;
+ struct ft_sess *sess;
+ struct ft_node_acl *acl;
+ u32 fcp_parm;
+
+ tport = ft_tport_get(rdata->local_port);
+ if (!tport)
+ goto not_target; /* not a target for this local port */
+
+ acl = ft_acl_get(tport->tpg, rdata);
+ if (!acl)
+ goto not_target; /* no target for this remote */
+
+ if (!rspp)
+ goto fill;
+
+ if (rspp->spp_flags & (FC_SPP_OPA_VAL | FC_SPP_RPA_VAL))
+ return FC_SPP_RESP_NO_PA;
+
+ /*
+ * If both target and initiator bits are off, the SPP is invalid.
+ */
+ fcp_parm = ntohl(rspp->spp_params);
+ if (!(fcp_parm & (FCP_SPPF_INIT_FCN | FCP_SPPF_TARG_FCN)))
+ return FC_SPP_RESP_INVL;
+
+ /*
+ * Create session (image pair) only if requested by
+ * EST_IMG_PAIR flag and if the requestor is an initiator.
+ */
+ if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) {
+ spp->spp_flags |= FC_SPP_EST_IMG_PAIR;
+ if (!(fcp_parm & FCP_SPPF_INIT_FCN))
+ return FC_SPP_RESP_CONF;
+ sess = ft_sess_create(tport, rdata->ids.port_id, acl);
+ if (!sess)
+ return FC_SPP_RESP_RES;
+ if (!sess->params)
+ rdata->prli_count++;
+ sess->params = fcp_parm;
+ sess->port_name = rdata->ids.port_name;
+ sess->max_frame = rdata->maxframe_size;
+
+ /* XXX TBD - clearing actions. unit attn, see 4.10 */
+ }
+
+ /*
+ * OR in our service parameters with other provider (initiator), if any.
+ */
+fill:
+ fcp_parm = ntohl(spp->spp_params);
+ fcp_parm &= ~FCP_SPPF_RETRY;
+ spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN);
+ return FC_SPP_RESP_ACK;
+
+not_target:
+ fcp_parm = ntohl(spp->spp_params);
+ fcp_parm &= ~FCP_SPPF_TARG_FCN;
+ spp->spp_params = htonl(fcp_parm);
+ return 0;
+}
+
+/**
+ * tcm_fcp_prli() - Handle incoming or outgoing PRLI for the FCP target
+ * @rdata: remote port private
+ * @spp_len: service parameter page length
+ * @rspp: received service parameter page (NULL for outgoing PRLI)
+ * @spp: response service parameter page
+ *
+ * Returns spp response code.
+ */
+static int ft_prli(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *rspp, struct fc_els_spp *spp)
+{
+ int ret;
+
+ mutex_lock(&ft_lport_lock);
+ ret = ft_prli_locked(rdata, spp_len, rspp, spp);
+ mutex_unlock(&ft_lport_lock);
+ pr_debug("port_id %x flags %x ret %x\n",
+ rdata->ids.port_id, rspp ? rspp->spp_flags : 0, ret);
+ return ret;
+}
+
+static void ft_sess_free(struct kref *kref)
+{
+ struct ft_sess *sess = container_of(kref, struct ft_sess, kref);
+
+ transport_deregister_session(sess->se_sess);
+ kfree_rcu(sess, rcu);
+}
+
+void ft_sess_put(struct ft_sess *sess)
+{
+ int sess_held = atomic_read(&sess->kref.refcount);
+
+ BUG_ON(!sess_held);
+ kref_put(&sess->kref, ft_sess_free);
+}
+
+static void ft_prlo(struct fc_rport_priv *rdata)
+{
+ struct ft_sess *sess;
+ struct ft_tport *tport;
+
+ mutex_lock(&ft_lport_lock);
+ tport = rcu_dereference_protected(rdata->local_port->prov[FC_TYPE_FCP],
+ lockdep_is_held(&ft_lport_lock));
+
+ if (!tport) {
+ mutex_unlock(&ft_lport_lock);
+ return;
+ }
+ sess = ft_sess_delete(tport, rdata->ids.port_id);
+ if (!sess) {
+ mutex_unlock(&ft_lport_lock);
+ return;
+ }
+ mutex_unlock(&ft_lport_lock);
+ transport_deregister_session_configfs(sess->se_sess);
+ ft_sess_put(sess); /* release from table */
+ rdata->prli_count--;
+ /* XXX TBD - clearing actions. unit attn, see 4.10 */
+}
+
+/*
+ * Handle incoming FCP request.
+ * Caller has verified that the frame is type FCP.
+ */
+static void ft_recv(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct ft_sess *sess;
+ u32 sid = fc_frame_sid(fp);
+
+ pr_debug("sid %x\n", sid);
+
+ sess = ft_sess_get(lport, sid);
+ if (!sess) {
+ pr_debug("sid %x sess lookup failed\n", sid);
+ /* TBD XXX - if FCP_CMND, send PRLO */
+ fc_frame_free(fp);
+ return;
+ }
+ ft_recv_req(sess, fp); /* must do ft_sess_put() */
+}
+
+/*
+ * Provider ops for libfc.
+ */
+struct fc4_prov ft_prov = {
+ .prli = ft_prli,
+ .prlo = ft_prlo,
+ .recv = ft_recv,
+ .module = THIS_MODULE,
+};