iscsi-target: Add iSCSI fabric support for target v4.1
The Linux-iSCSI.org target module is a full featured in-kernel software implementation of iSCSI target mode (RFC-3720) for the current WIP mainline target v4.1 infrastructure code for the v3.1 kernel. More information can be found here: http://linux-iscsi.org/wiki/ISCSI This includes support for: * RFC-3720 defined request / response state machines and support for all defined iSCSI operation codes from Section 10.2.1.2 using libiscsi include/scsi/iscsi_proto.h PDU definitions * Target v4.1 compatible control plane using the generic layout in target_core_fabric_configfs.c and fabric dependent attributes within /sys/kernel/config/target/iscsi/ subdirectories. * Target v4.1 compatible iSCSI statistics based on RFC-4544 (iSCSI MIBS) * Support for IPv6 and IPv4 network portals in M:N mapping to TPGs * iSCSI Error Recovery Hierarchy support * Per iSCSI connection RX/TX thread pair scheduling affinity * crc32c + crc32c_intel SSEv4 instruction offload support using libcrypto * CHAP Authentication support using libcrypto * Conversion to use internal SGl allocation with iscsit_alloc_buffs() -> transport_generic_map_mem_to_cmd() (nab: Fix iscsi_proto.h struct scsi_lun usage from linux-next in commit: iscsi: Use struct scsi_lun in iscsi structs instead of u8[8]) (nab: Fix 32-bit compile warnings) Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Andy Grover <agrover@redhat.com> Acked-by: Roland Dreier <roland@kernel.org> Signed-off-by: Nicholas A. Bellinger <nab@linux-iscsi.org>
This commit is contained in:
parent
8304bbceee
commit
e48354ce07
41 changed files with 22099 additions and 1 deletions
|
@ -31,5 +31,6 @@ config TCM_PSCSI
|
||||||
|
|
||||||
source "drivers/target/loopback/Kconfig"
|
source "drivers/target/loopback/Kconfig"
|
||||||
source "drivers/target/tcm_fc/Kconfig"
|
source "drivers/target/tcm_fc/Kconfig"
|
||||||
|
source "drivers/target/iscsi/Kconfig"
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -24,5 +24,5 @@ obj-$(CONFIG_TCM_PSCSI) += target_core_pscsi.o
|
||||||
|
|
||||||
# Fabric modules
|
# Fabric modules
|
||||||
obj-$(CONFIG_LOOPBACK_TARGET) += loopback/
|
obj-$(CONFIG_LOOPBACK_TARGET) += loopback/
|
||||||
|
|
||||||
obj-$(CONFIG_TCM_FC) += tcm_fc/
|
obj-$(CONFIG_TCM_FC) += tcm_fc/
|
||||||
|
obj-$(CONFIG_ISCSI_TARGET) += iscsi/
|
||||||
|
|
8
drivers/target/iscsi/Kconfig
Normal file
8
drivers/target/iscsi/Kconfig
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
config ISCSI_TARGET
|
||||||
|
tristate "Linux-iSCSI.org iSCSI Target Mode Stack"
|
||||||
|
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.
|
20
drivers/target/iscsi/Makefile
Normal file
20
drivers/target/iscsi/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
obj-$(CONFIG_ISCSI_TARGET) += iscsi_target_mod.o
|
4559
drivers/target/iscsi/iscsi_target.c
Normal file
4559
drivers/target/iscsi/iscsi_target.c
Normal file
File diff suppressed because it is too large
Load diff
42
drivers/target/iscsi/iscsi_target.h
Normal file
42
drivers/target/iscsi/iscsi_target.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#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 int iscsit_deaccess_np(struct iscsi_np *, struct iscsi_portal_group *);
|
||||||
|
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 *);
|
||||||
|
extern int iscsit_del_np(struct iscsi_np *);
|
||||||
|
extern int iscsit_add_reject_from_cmd(u8, int, int, unsigned char *, 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_send_r2t(struct iscsi_cmd *, struct iscsi_conn *);
|
||||||
|
extern int iscsit_build_r2ts_for_cmd(struct iscsi_cmd *, struct iscsi_conn *, int);
|
||||||
|
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_cmd_cache;
|
||||||
|
extern struct kmem_cache *lio_qr_cache;
|
||||||
|
extern struct kmem_cache *lio_r2t_cache;
|
||||||
|
|
||||||
|
#endif /*** ISCSI_TARGET_H ***/
|
490
drivers/target/iscsi/iscsi_target_auth.c
Normal file
490
drivers/target/iscsi/iscsi_target_auth.c
Normal file
|
@ -0,0 +1,490 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* This file houses the main functions for the iSCSI CHAP support
|
||||||
|
*
|
||||||
|
* \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
|
||||||
|
*
|
||||||
|
* Licensed to the Linux Foundation under the General Public License (GPL) version 2.
|
||||||
|
*
|
||||||
|
* 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/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 unsigned char chap_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int chap_string_to_hex(unsigned char *dst, unsigned char *src, int len)
|
||||||
|
{
|
||||||
|
int i, j = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i += 2) {
|
||||||
|
dst[j++] = (unsigned char) chap_asciihex_to_binaryhex(&src[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_set_random(char *data, int length)
|
||||||
|
{
|
||||||
|
long r;
|
||||||
|
unsigned n;
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
get_random_bytes(&r, sizeof(long));
|
||||||
|
r = r ^ (r >> 8);
|
||||||
|
r = r ^ (r >> 4);
|
||||||
|
n = r & 0x7;
|
||||||
|
|
||||||
|
get_random_bytes(&r, sizeof(long));
|
||||||
|
r = r ^ (r >> 8);
|
||||||
|
r = r ^ (r >> 5);
|
||||||
|
n = (n << 3) | (r & 0x7);
|
||||||
|
|
||||||
|
get_random_bytes(&r, sizeof(long));
|
||||||
|
r = r ^ (r >> 8);
|
||||||
|
r = r ^ (r >> 5);
|
||||||
|
n = (n << 2) | (r & 0x3);
|
||||||
|
|
||||||
|
*data++ = n;
|
||||||
|
length--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = (struct iscsi_chap *) conn->auth_protocol;
|
||||||
|
|
||||||
|
memset(challenge_asciihex, 0, CHAP_CHALLENGE_LENGTH * 2 + 1);
|
||||||
|
|
||||||
|
chap_set_random(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 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)
|
||||||
|
{
|
||||||
|
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 = (struct iscsi_chap *) conn->auth_protocol;
|
||||||
|
/*
|
||||||
|
* We only support MD5 MDA presently.
|
||||||
|
*/
|
||||||
|
if (strncmp(a_str, "CHAP_A=5", 8)) {
|
||||||
|
pr_err("CHAP_A is not MD5.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
/*
|
||||||
|
* Set Identifier.
|
||||||
|
*/
|
||||||
|
chap->id = ISCSI_TPG_C(conn)->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)
|
||||||
|
{
|
||||||
|
char *endptr;
|
||||||
|
unsigned char id, 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];
|
||||||
|
struct iscsi_chap *chap = (struct iscsi_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(chap_n, auth->userid, strlen(auth->userid)) != 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, (void *)&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, (void *)&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, (void *)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)
|
||||||
|
id = (unsigned char)simple_strtoul((char *)&identifier[2],
|
||||||
|
&endptr, 0);
|
||||||
|
else
|
||||||
|
id = (unsigned char)simple_strtoul(identifier, &endptr, 0);
|
||||||
|
/*
|
||||||
|
* RFC 1994 says Identifier is no more than octet (8 bits).
|
||||||
|
*/
|
||||||
|
pr_debug("[server] Got CHAP_I=%d\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;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
sg_init_one(&sg, (void *)&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, (void *)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, (void *)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 = (struct iscsi_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 = (struct iscsi_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;
|
||||||
|
}
|
31
drivers/target/iscsi/iscsi_target_auth.h
Normal file
31
drivers/target/iscsi/iscsi_target_auth.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef _ISCSI_CHAP_H_
|
||||||
|
#define _ISCSI_CHAP_H_
|
||||||
|
|
||||||
|
#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_ ***/
|
1882
drivers/target/iscsi/iscsi_target_configfs.c
Normal file
1882
drivers/target/iscsi/iscsi_target_configfs.c
Normal file
File diff suppressed because it is too large
Load diff
7
drivers/target/iscsi/iscsi_target_configfs.h
Normal file
7
drivers/target/iscsi/iscsi_target_configfs.h
Normal file
|
@ -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 */
|
859
drivers/target/iscsi/iscsi_target_core.h
Normal file
859
drivers/target/iscsi/iscsi_target_core.h
Normal file
|
@ -0,0 +1,859 @@
|
||||||
|
#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-rc1"
|
||||||
|
#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"
|
||||||
|
|
||||||
|
/* 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 5
|
||||||
|
#define NA_NOPIN_TIMEOUT_MAX 60
|
||||||
|
#define NA_NOPIN_TIMEOUT_MIN 3
|
||||||
|
#define NA_NOPIN_RESPONSE_TIMEOUT 5
|
||||||
|
#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
|
||||||
|
#define NA_DEFAULT_ERL 0
|
||||||
|
#define NA_DEFAULT_ERL_MAX 2
|
||||||
|
#define NA_DEFAULT_ERL_MIN 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 16
|
||||||
|
#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_CACHE_CORE_NPS 0
|
||||||
|
|
||||||
|
enum tpg_np_network_transport_table {
|
||||||
|
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,
|
||||||
|
ICF_REJECT_FAIL_CONN = 0x00000100,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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,
|
||||||
|
NPF_SCTP_STRUCT_FILE = 0x01 /* Bugfix */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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] */
|
||||||
|
u8 OFMarker; /* [0,1] == [No,Yes] */
|
||||||
|
u8 IFMarker; /* [0,1] == [No,Yes] */
|
||||||
|
u32 OFMarkInt; /* [1..65535] */
|
||||||
|
u32 IFMarkInt; /* [1..65535] */
|
||||||
|
};
|
||||||
|
|
||||||
|
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]*/
|
||||||
|
};
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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_offset_end;
|
||||||
|
u32 data_sn;
|
||||||
|
u32 next_burst_len;
|
||||||
|
u32 read_data_done;
|
||||||
|
u32 seq_send_order;
|
||||||
|
struct list_head dr_list;
|
||||||
|
} ____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;
|
||||||
|
/* 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 */
|
||||||
|
u32 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;
|
||||||
|
/* Total size in bytes associated with command */
|
||||||
|
u32 data_length;
|
||||||
|
/* 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;
|
||||||
|
u32 residual_count;
|
||||||
|
/* 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;
|
||||||
|
/* 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;
|
||||||
|
atomic_t transport_sent;
|
||||||
|
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;
|
||||||
|
struct completion reject_comp;
|
||||||
|
/* 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_list;
|
||||||
|
/* 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];
|
||||||
|
|
||||||
|
struct scatterlist *t_mem_sg;
|
||||||
|
u32 t_mem_sg_nents;
|
||||||
|
|
||||||
|
u32 padding;
|
||||||
|
u8 pad_bytes[4];
|
||||||
|
|
||||||
|
struct scatterlist *first_data_sg;
|
||||||
|
u32 first_data_sg_off;
|
||||||
|
u32 kmapped_nents;
|
||||||
|
|
||||||
|
} ____cacheline_aligned;
|
||||||
|
|
||||||
|
struct iscsi_tmr_req {
|
||||||
|
bool task_reassign:1;
|
||||||
|
u32 ref_cmd_sn;
|
||||||
|
u32 exp_data_sn;
|
||||||
|
struct iscsi_conn_recovery *conn_recovery;
|
||||||
|
struct se_tmr_req *se_tmr_req;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct iscsi_conn {
|
||||||
|
/* 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;
|
||||||
|
u8 tx_immediate_queue;
|
||||||
|
u8 tx_response_queue;
|
||||||
|
/* 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;
|
||||||
|
int net_size;
|
||||||
|
u32 auth_id;
|
||||||
|
#define CONNFLAG_SCTP_STRUCT_FILE 0x01
|
||||||
|
u32 conn_flags;
|
||||||
|
/* Used for iscsi_tx_login_rsp() */
|
||||||
|
u32 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;
|
||||||
|
/* Complete Bad PDU for sending reject */
|
||||||
|
unsigned char bad_hdr[ISCSI_HDR_LEN];
|
||||||
|
#define IPV6_ADDRESS_SPACE 48
|
||||||
|
unsigned char login_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;
|
||||||
|
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;
|
||||||
|
struct timer_list nopin_timer;
|
||||||
|
struct timer_list nopin_response_timer;
|
||||||
|
struct timer_list transport_timer;
|
||||||
|
/* 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;
|
||||||
|
int conn_rx_reset_cpumask:1;
|
||||||
|
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_param_list *param_list;
|
||||||
|
/* Used for per connection auth state machine */
|
||||||
|
void *auth_protocol;
|
||||||
|
struct iscsi_login_thread_s *login_thread;
|
||||||
|
struct iscsi_portal_group *tpg;
|
||||||
|
/* 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;
|
||||||
|
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 */
|
||||||
|
u32 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;
|
||||||
|
u32 cmd_pdus;
|
||||||
|
u32 rsp_pdus;
|
||||||
|
u64 tx_data_octets;
|
||||||
|
u64 rx_data_octets;
|
||||||
|
u32 conn_digest_errors;
|
||||||
|
u32 conn_timeout_errors;
|
||||||
|
u64 creation_time;
|
||||||
|
spinlock_t session_stats_lock;
|
||||||
|
/* 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;
|
||||||
|
atomic_t transport_wait_cmds;
|
||||||
|
/* 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;
|
||||||
|
char isid[6];
|
||||||
|
u32 cmd_sn;
|
||||||
|
u32 init_task_tag;
|
||||||
|
u32 initial_exp_statsn;
|
||||||
|
u32 rsp_length;
|
||||||
|
u16 cid;
|
||||||
|
u16 tsih;
|
||||||
|
char *req;
|
||||||
|
char *rsp;
|
||||||
|
char *req_buf;
|
||||||
|
char *rsp_buf;
|
||||||
|
} ____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;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NODE_STAT_GRPS(nacl) (&(nacl)->node_stat_grps)
|
||||||
|
|
||||||
|
#define ISCSI_NODE_ATTRIB(t) (&(t)->node_attrib)
|
||||||
|
#define ISCSI_NODE_AUTH(t) (&(t)->node_auth)
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
struct iscsi_portal_group *np_login_tpg;
|
||||||
|
struct list_head np_list;
|
||||||
|
} ____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 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 mutex np_login_lock;
|
||||||
|
struct iscsi_tpg_attrib tpg_attrib;
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
#define ISCSI_TPG_C(c) ((struct iscsi_portal_group *)(c)->tpg)
|
||||||
|
#define ISCSI_TPG_LUN(c, l) ((iscsi_tpg_list_t *)(c)->tpg->tpg_lun_list_t[l])
|
||||||
|
#define ISCSI_TPG_S(s) ((struct iscsi_portal_group *)(s)->tpg)
|
||||||
|
#define ISCSI_TPG_ATTRIB(t) (&(t)->tpg_attrib)
|
||||||
|
#define SE_TPG(tpg) (&(tpg)->tpg_se_tpg)
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
#define WWN_STAT_GRPS(tiqn) (&(tiqn)->tiqn_stat_grps)
|
||||||
|
|
||||||
|
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 */
|
531
drivers/target/iscsi/iscsi_target_datain_values.c
Normal file
531
drivers/target/iscsi/iscsi_target_datain_values.c
Normal file
|
@ -0,0 +1,531 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* This file contains the iSCSI Target DataIN value generation functions.
|
||||||
|
*
|
||||||
|
* \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
|
||||||
|
*
|
||||||
|
* Licensed to the Linux Foundation under the General Public License (GPL) version 2.
|
||||||
|
*
|
||||||
|
* 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->dr_list);
|
||||||
|
|
||||||
|
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->dr_list, &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->dr_list);
|
||||||
|
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, dr_list) {
|
||||||
|
list_del(&dr->dr_list);
|
||||||
|
kmem_cache_free(lio_dr_cache, dr);
|
||||||
|
}
|
||||||
|
spin_unlock(&cmd->datain_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iscsi_datain_req *iscsit_get_datain_req(struct iscsi_cmd *cmd)
|
||||||
|
{
|
||||||
|
struct iscsi_datain_req *dr;
|
||||||
|
|
||||||
|
if (list_empty(&cmd->datain_list)) {
|
||||||
|
pr_err("cmd->datain_list is empty for ITT:"
|
||||||
|
" 0x%08x\n", cmd->init_task_tag);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
list_for_each_entry(dr, &cmd->datain_list, dr_list)
|
||||||
|
break;
|
||||||
|
|
||||||
|
return dr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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->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->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->data_length) {
|
||||||
|
datain->length = (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->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->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->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->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->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;
|
||||||
|
}
|
12
drivers/target/iscsi/iscsi_target_datain_values.h
Normal file
12
drivers/target/iscsi/iscsi_target_datain_values.h
Normal file
|
@ -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 ***/
|
87
drivers/target/iscsi/iscsi_target_device.c
Normal file
87
drivers/target/iscsi/iscsi_target_device.c
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* This file contains the iSCSI Virtual Device and Disk Transport
|
||||||
|
* agnostic related functions.
|
||||||
|
*
|
||||||
|
\u00a9 Copyright 2007-2011 RisingTide Systems LLC.
|
||||||
|
*
|
||||||
|
* Licensed to the Linux Foundation under the General Public License (GPL) version 2.
|
||||||
|
*
|
||||||
|
* 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_device.h>
|
||||||
|
#include <target/target_core_transport.h>
|
||||||
|
|
||||||
|
#include "iscsi_target_core.h"
|
||||||
|
#include "iscsi_target_device.h"
|
||||||
|
#include "iscsi_target_tpg.h"
|
||||||
|
#include "iscsi_target_util.h"
|
||||||
|
|
||||||
|
int iscsit_get_lun_for_tmr(
|
||||||
|
struct iscsi_cmd *cmd,
|
||||||
|
u64 lun)
|
||||||
|
{
|
||||||
|
u32 unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
|
||||||
|
|
||||||
|
return transport_lookup_tmr_lun(&cmd->se_cmd, unpacked_lun);
|
||||||
|
}
|
||||||
|
|
||||||
|
int iscsit_get_lun_for_cmd(
|
||||||
|
struct iscsi_cmd *cmd,
|
||||||
|
unsigned char *cdb,
|
||||||
|
u64 lun)
|
||||||
|
{
|
||||||
|
u32 unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
|
||||||
|
|
||||||
|
return transport_lookup_cmd_lun(&cmd->se_cmd, unpacked_lun);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
9
drivers/target/iscsi/iscsi_target_device.h
Normal file
9
drivers/target/iscsi/iscsi_target_device.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef ISCSI_TARGET_DEVICE_H
|
||||||
|
#define ISCSI_TARGET_DEVICE_H
|
||||||
|
|
||||||
|
extern int iscsit_get_lun_for_tmr(struct iscsi_cmd *, u64);
|
||||||
|
extern int iscsit_get_lun_for_cmd(struct iscsi_cmd *, unsigned char *, u64);
|
||||||
|
extern void iscsit_determine_maxcmdsn(struct iscsi_session *);
|
||||||
|
extern void iscsit_increment_maxcmdsn(struct iscsi_cmd *, struct iscsi_session *);
|
||||||
|
|
||||||
|
#endif /* ISCSI_TARGET_DEVICE_H */
|
1004
drivers/target/iscsi/iscsi_target_erl0.c
Normal file
1004
drivers/target/iscsi/iscsi_target_erl0.c
Normal file
File diff suppressed because it is too large
Load diff
15
drivers/target/iscsi/iscsi_target_erl0.h
Normal file
15
drivers/target/iscsi/iscsi_target_erl0.h
Normal file
|
@ -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 ***/
|
1299
drivers/target/iscsi/iscsi_target_erl1.c
Normal file
1299
drivers/target/iscsi/iscsi_target_erl1.c
Normal file
File diff suppressed because it is too large
Load diff
26
drivers/target/iscsi/iscsi_target_erl1.h
Normal file
26
drivers/target/iscsi/iscsi_target_erl1.h
Normal file
|
@ -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 *,
|
||||||
|
u32, u32, u32, u32);
|
||||||
|
extern int iscsit_handle_status_snack(struct iscsi_conn *, u32, 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 */
|
474
drivers/target/iscsi/iscsi_target_erl2.c
Normal file
474
drivers/target/iscsi/iscsi_target_erl2.c
Normal file
|
@ -0,0 +1,474 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* This file contains error recovery level two functions used by
|
||||||
|
* the iSCSI Target driver.
|
||||||
|
*
|
||||||
|
* \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
|
||||||
|
*
|
||||||
|
* Licensed to the Linux Foundation under the General Public License (GPL) version 2.
|
||||||
|
*
|
||||||
|
* 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_transport.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,
|
||||||
|
u32 exp_data_sn)
|
||||||
|
{
|
||||||
|
u32 data_sn = 0;
|
||||||
|
struct iscsi_conn *conn = cmd->conn;
|
||||||
|
|
||||||
|
cmd->next_burst_len = 0;
|
||||||
|
cmd->read_data_done = 0;
|
||||||
|
|
||||||
|
while (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_list) {
|
||||||
|
|
||||||
|
list_del(&cmd->i_list);
|
||||||
|
cmd->conn = NULL;
|
||||||
|
spin_unlock(&cr->conn_recovery_cmd_lock);
|
||||||
|
if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) ||
|
||||||
|
!(cmd->se_cmd.transport_wait_for_tasks))
|
||||||
|
iscsit_release_cmd(cmd);
|
||||||
|
else
|
||||||
|
cmd->se_cmd.transport_wait_for_tasks(
|
||||||
|
&cmd->se_cmd, 1, 1);
|
||||||
|
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_list) {
|
||||||
|
|
||||||
|
list_del(&cmd->i_list);
|
||||||
|
cmd->conn = NULL;
|
||||||
|
spin_unlock(&cr->conn_recovery_cmd_lock);
|
||||||
|
if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) ||
|
||||||
|
!(cmd->se_cmd.transport_wait_for_tasks))
|
||||||
|
iscsit_release_cmd(cmd);
|
||||||
|
else
|
||||||
|
cmd->se_cmd.transport_wait_for_tasks(
|
||||||
|
&cmd->se_cmd, 1, 1);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int 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);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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(&cmd->i_list);
|
||||||
|
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_list) {
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) ||
|
||||||
|
!(cmd->se_cmd.transport_wait_for_tasks))
|
||||||
|
iscsit_release_cmd(cmd);
|
||||||
|
else
|
||||||
|
cmd->se_cmd.transport_wait_for_tasks(
|
||||||
|
&cmd->se_cmd, 1, 0);
|
||||||
|
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_list) {
|
||||||
|
if (!(cmd->cmd_flags & ICF_OOO_CMDSN))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
list_del(&cmd->i_list);
|
||||||
|
|
||||||
|
spin_unlock_bh(&conn->cmd_lock);
|
||||||
|
if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) ||
|
||||||
|
!(cmd->se_cmd.transport_wait_for_tasks))
|
||||||
|
iscsit_release_cmd(cmd);
|
||||||
|
else
|
||||||
|
cmd->se_cmd.transport_wait_for_tasks(
|
||||||
|
&cmd->se_cmd, 1, 1);
|
||||||
|
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(&cmd->i_list); 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_list) {
|
||||||
|
|
||||||
|
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(&cmd->i_list);
|
||||||
|
spin_unlock_bh(&conn->cmd_lock);
|
||||||
|
|
||||||
|
if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) ||
|
||||||
|
!(cmd->se_cmd.transport_wait_for_tasks))
|
||||||
|
iscsit_release_cmd(cmd);
|
||||||
|
else
|
||||||
|
cmd->se_cmd.transport_wait_for_tasks(
|
||||||
|
&cmd->se_cmd, 1, 0);
|
||||||
|
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 &&
|
||||||
|
(cmd->cmd_sn >= conn->sess->exp_cmd_sn)) {
|
||||||
|
list_del(&cmd->i_list);
|
||||||
|
spin_unlock_bh(&conn->cmd_lock);
|
||||||
|
|
||||||
|
if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) ||
|
||||||
|
!(cmd->se_cmd.transport_wait_for_tasks))
|
||||||
|
iscsit_release_cmd(cmd);
|
||||||
|
else
|
||||||
|
cmd->se_cmd.transport_wait_for_tasks(
|
||||||
|
&cmd->se_cmd, 1, 1);
|
||||||
|
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(&cmd->i_list);
|
||||||
|
spin_unlock_bh(&conn->cmd_lock);
|
||||||
|
|
||||||
|
iscsit_free_all_datain_reqs(cmd);
|
||||||
|
|
||||||
|
if ((cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) &&
|
||||||
|
cmd->se_cmd.transport_wait_for_tasks)
|
||||||
|
cmd->se_cmd.transport_wait_for_tasks(&cmd->se_cmd,
|
||||||
|
0, 0);
|
||||||
|
/*
|
||||||
|
* Add the struct iscsi_cmd to the connection recovery cmd list
|
||||||
|
*/
|
||||||
|
spin_lock(&cr->conn_recovery_cmd_lock);
|
||||||
|
list_add_tail(&cmd->i_list, &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->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;
|
||||||
|
}
|
18
drivers/target/iscsi/iscsi_target_erl2.h
Normal file
18
drivers/target/iscsi/iscsi_target_erl2.h
Normal file
|
@ -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 *, u32);
|
||||||
|
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 ***/
|
1232
drivers/target/iscsi/iscsi_target_login.c
Normal file
1232
drivers/target/iscsi/iscsi_target_login.c
Normal file
File diff suppressed because it is too large
Load diff
12
drivers/target/iscsi/iscsi_target_login.h
Normal file
12
drivers/target/iscsi/iscsi_target_login.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#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 iscsi_target_setup_login_socket(struct iscsi_np *,
|
||||||
|
struct __kernel_sockaddr_storage *);
|
||||||
|
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 ***/
|
1067
drivers/target/iscsi/iscsi_target_nego.c
Normal file
1067
drivers/target/iscsi/iscsi_target_nego.c
Normal file
File diff suppressed because it is too large
Load diff
17
drivers/target/iscsi/iscsi_target_nego.h
Normal file
17
drivers/target/iscsi/iscsi_target_nego.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#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 struct iscsi_login *iscsi_target_init_negotiation(
|
||||||
|
struct iscsi_np *, struct iscsi_conn *, char *);
|
||||||
|
extern int iscsi_target_start_negotiation(
|
||||||
|
struct iscsi_login *, struct iscsi_conn *);
|
||||||
|
extern void iscsi_target_nego_release(
|
||||||
|
struct iscsi_login *, struct iscsi_conn *);
|
||||||
|
|
||||||
|
#endif /* ISCSI_TARGET_NEGO_H */
|
263
drivers/target/iscsi/iscsi_target_nodeattrib.c
Normal file
263
drivers/target/iscsi/iscsi_target_nodeattrib.c
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* This file contains the main functions related to Initiator Node Attributes.
|
||||||
|
*
|
||||||
|
* \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
|
||||||
|
*
|
||||||
|
* Licensed to the Linux Foundation under the General Public License (GPL) version 2.
|
||||||
|
*
|
||||||
|
* 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_transport.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_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 = NA_DEFAULT_ERL;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern 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 = (struct iscsi_session *)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;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern 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;
|
||||||
|
}
|
14
drivers/target/iscsi/iscsi_target_nodeattrib.h
Normal file
14
drivers/target/iscsi/iscsi_target_nodeattrib.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef ISCSI_TARGET_NODEATTRIB_H
|
||||||
|
#define ISCSI_TARGET_NODEATTRIB_H
|
||||||
|
|
||||||
|
extern void iscsit_set_default_node_attribues(struct iscsi_node_acl *);
|
||||||
|
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 */
|
1905
drivers/target/iscsi/iscsi_target_parameters.c
Normal file
1905
drivers/target/iscsi/iscsi_target_parameters.c
Normal file
File diff suppressed because it is too large
Load diff
269
drivers/target/iscsi/iscsi_target_parameters.h
Normal file
269
drivers/target/iscsi/iscsi_target_parameters.h
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
#ifndef ISCSI_PARAMETERS_H
|
||||||
|
#define ISCSI_PARAMETERS_H
|
||||||
|
|
||||||
|
struct iscsi_extra_response {
|
||||||
|
char key[64];
|
||||||
|
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(int, struct iscsi_param_list *);
|
||||||
|
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_param_list *);
|
||||||
|
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 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"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
#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"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 */
|
664
drivers/target/iscsi/iscsi_target_seq_pdu_list.c
Normal file
664
drivers/target/iscsi/iscsi_target_seq_pdu_list.c
Normal file
|
@ -0,0 +1,664 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* This file contains main functions related to iSCSI DataSequenceInOrder=No
|
||||||
|
* and DataPDUInOrder=No.
|
||||||
|
*
|
||||||
|
\u00a9 Copyright 2007-2011 RisingTide Systems LLC.
|
||||||
|
*
|
||||||
|
* Licensed to the Linux Foundation under the General Public License (GPL) version 2.
|
||||||
|
*
|
||||||
|
* 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_seq_pdu_list.h"
|
||||||
|
|
||||||
|
#define OFFLOAD_BUF_SIZE 32768
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = kzalloc(seq_count * sizeof(u32), GFP_KERNEL);
|
||||||
|
if (!array) {
|
||||||
|
pr_err("Unable to allocate memory"
|
||||||
|
" for random array.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
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 = kzalloc(seq_count * sizeof(u32), GFP_KERNEL);
|
||||||
|
if (!array) {
|
||||||
|
pr_err("Unable to allocate memory for"
|
||||||
|
" random array.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
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 = kzalloc(seq_count * sizeof(u32), GFP_KERNEL);
|
||||||
|
if (!array) {
|
||||||
|
pr_err("Unable to allocate memory for random array.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
struct iscsi_conn *conn = cmd->conn;
|
||||||
|
|
||||||
|
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 = (cmd->data_length >
|
||||||
|
conn->sess->sess_ops->FirstBurstLength) ?
|
||||||
|
conn->sess->sess_ops->FirstBurstLength : cmd->data_length;
|
||||||
|
|
||||||
|
while (offset < 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 + conn->conn_ops->MaxRecvDataSegmentLength)
|
||||||
|
>= cmd->data_length) {
|
||||||
|
unsolicited_data_length -=
|
||||||
|
(cmd->data_length - offset);
|
||||||
|
offset += (cmd->data_length - offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((offset + conn->conn_ops->MaxRecvDataSegmentLength)
|
||||||
|
>= 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 += conn->conn_ops->MaxRecvDataSegmentLength;
|
||||||
|
unsolicited_data_length -=
|
||||||
|
conn->conn_ops->MaxRecvDataSegmentLength;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >=
|
||||||
|
cmd->data_length) {
|
||||||
|
offset += (cmd->data_length - offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((burstlength + conn->conn_ops->MaxRecvDataSegmentLength) >=
|
||||||
|
conn->sess->sess_ops->MaxBurstLength) {
|
||||||
|
offset += (conn->sess->sess_ops->MaxBurstLength -
|
||||||
|
burstlength);
|
||||||
|
burstlength = 0;
|
||||||
|
*seq_count += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
burstlength += conn->conn_ops->MaxRecvDataSegmentLength;
|
||||||
|
offset += conn->conn_ops->MaxRecvDataSegmentLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Builds PDU and/or Sequence list, called while DataSequenceInOrder=No
|
||||||
|
* and DataPDUInOrder=No.
|
||||||
|
*/
|
||||||
|
static int iscsit_build_pdu_and_seq_list(
|
||||||
|
struct iscsi_cmd *cmd,
|
||||||
|
struct iscsi_build_list *bl)
|
||||||
|
{
|
||||||
|
int check_immediate = 0, datapduinorder, datasequenceinorder;
|
||||||
|
u32 burstlength = 0, offset = 0, i = 0;
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 = (cmd->data_length >
|
||||||
|
conn->sess->sess_ops->FirstBurstLength) ?
|
||||||
|
conn->sess->sess_ops->FirstBurstLength : cmd->data_length;
|
||||||
|
|
||||||
|
while (offset < 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 +
|
||||||
|
conn->conn_ops->MaxRecvDataSegmentLength) >=
|
||||||
|
cmd->data_length) {
|
||||||
|
if (!datapduinorder) {
|
||||||
|
pdu[i].type = PDUTYPE_UNSOLICITED;
|
||||||
|
pdu[i].length =
|
||||||
|
(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->data_length - offset));
|
||||||
|
}
|
||||||
|
unsolicited_data_length -=
|
||||||
|
(cmd->data_length - offset);
|
||||||
|
offset += (cmd->data_length - offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((offset +
|
||||||
|
conn->conn_ops->MaxRecvDataSegmentLength) >=
|
||||||
|
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 =
|
||||||
|
conn->conn_ops->MaxRecvDataSegmentLength;
|
||||||
|
}
|
||||||
|
burstlength += conn->conn_ops->MaxRecvDataSegmentLength;
|
||||||
|
offset += conn->conn_ops->MaxRecvDataSegmentLength;
|
||||||
|
unsolicited_data_length -=
|
||||||
|
conn->conn_ops->MaxRecvDataSegmentLength;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >=
|
||||||
|
cmd->data_length) {
|
||||||
|
if (!datapduinorder) {
|
||||||
|
pdu[i].type = PDUTYPE_NORMAL;
|
||||||
|
pdu[i].length = (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->data_length - offset));
|
||||||
|
}
|
||||||
|
offset += (cmd->data_length - offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((burstlength + conn->conn_ops->MaxRecvDataSegmentLength) >=
|
||||||
|
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 =
|
||||||
|
conn->conn_ops->MaxRecvDataSegmentLength;
|
||||||
|
}
|
||||||
|
burstlength += conn->conn_ops->MaxRecvDataSegmentLength;
|
||||||
|
offset += conn->conn_ops->MaxRecvDataSegmentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
iscsit_dump_seq_list(cmd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
iscsit_dump_pdu_list(cmd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only called while DataSequenceInOrder=No or DataPDUInOrder=No.
|
||||||
|
*/
|
||||||
|
int iscsit_do_build_list(
|
||||||
|
struct iscsi_cmd *cmd,
|
||||||
|
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;
|
||||||
|
|
||||||
|
iscsit_determine_counts_for_list(cmd, bl, &seq_count, &pdu_count);
|
||||||
|
|
||||||
|
if (!conn->sess->sess_ops->DataSequenceInOrder) {
|
||||||
|
seq = kzalloc(seq_count * sizeof(struct iscsi_seq), GFP_ATOMIC);
|
||||||
|
if (!seq) {
|
||||||
|
pr_err("Unable to allocate struct iscsi_seq list\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cmd->seq_list = seq;
|
||||||
|
cmd->seq_count = seq_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conn->sess->sess_ops->DataPDUInOrder) {
|
||||||
|
pdu = kzalloc(pdu_count * sizeof(struct iscsi_pdu), GFP_ATOMIC);
|
||||||
|
if (!pdu) {
|
||||||
|
pr_err("Unable to allocate struct iscsi_pdu list.\n");
|
||||||
|
kfree(seq);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cmd->pdu_list = pdu;
|
||||||
|
cmd->pdu_count = pdu_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iscsit_build_pdu_and_seq_list(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++) {
|
||||||
|
#if 0
|
||||||
|
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);
|
||||||
|
#endif
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
pr_debug("seq->pdu_start: %d, seq->pdu_count: %d,"
|
||||||
|
" seq->seq_no: %d\n", seq->pdu_start, seq->pdu_count,
|
||||||
|
seq->seq_no);
|
||||||
|
#endif
|
||||||
|
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++) {
|
||||||
|
#if 0
|
||||||
|
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);
|
||||||
|
#endif
|
||||||
|
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;
|
||||||
|
}
|
86
drivers/target/iscsi/iscsi_target_seq_pdu_list.h
Normal file
86
drivers/target/iscsi/iscsi_target_seq_pdu_list.h
Normal file
|
@ -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_do_build_list(struct iscsi_cmd *, struct iscsi_build_list *);
|
||||||
|
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 */
|
950
drivers/target/iscsi/iscsi_target_stat.c
Normal file
950
drivers/target/iscsi/iscsi_target_stat.c
Normal file
|
@ -0,0 +1,950 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Modern ConfigFS group context specific iSCSI statistics based on original
|
||||||
|
* iscsi_target_mib.c code
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011 Rising Tide Systems
|
||||||
|
*
|
||||||
|
* Licensed to the Linux Foundation under the General Public License (GPL) version 2.
|
||||||
|
*
|
||||||
|
* 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 <scsi/iscsi_proto.h>
|
||||||
|
#include <target/target_core_base.h>
|
||||||
|
#include <target/target_core_transport.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, "RisingTide Systems 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;
|
||||||
|
unsigned char buf[8];
|
||||||
|
|
||||||
|
spin_lock(&lstat->lock);
|
||||||
|
snprintf(buf, 8, "%s", (lstat->last_intr_fail_ip_addr != NULL) ?
|
||||||
|
"ipv6" : "ipv4");
|
||||||
|
spin_unlock(&lstat->lock);
|
||||||
|
|
||||||
|
return snprintf(page, PAGE_SIZE, "%s\n", buf);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
unsigned char buf[32];
|
||||||
|
|
||||||
|
spin_lock(&lstat->lock);
|
||||||
|
if (lstat->last_intr_fail_ip_family == AF_INET6)
|
||||||
|
snprintf(buf, 32, "[%s]", lstat->last_intr_fail_ip_addr);
|
||||||
|
else
|
||||||
|
snprintf(buf, 32, "%s", lstat->last_intr_fail_ip_addr);
|
||||||
|
spin_unlock(&lstat->lock);
|
||||||
|
|
||||||
|
return snprintf(page, PAGE_SIZE, "%s\n", buf);
|
||||||
|
}
|
||||||
|
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 = (struct iscsi_session *)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 = (struct iscsi_session *)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 = (struct iscsi_session *)se_sess->fabric_sess_ptr;
|
||||||
|
if (sess)
|
||||||
|
ret = snprintf(page, PAGE_SIZE, "%u\n", 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 = (struct iscsi_session *)se_sess->fabric_sess_ptr;
|
||||||
|
if (sess)
|
||||||
|
ret = snprintf(page, PAGE_SIZE, "%u\n", 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 = (struct iscsi_session *)se_sess->fabric_sess_ptr;
|
||||||
|
if (sess)
|
||||||
|
ret = snprintf(page, PAGE_SIZE, "%llu\n",
|
||||||
|
(unsigned long long)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 = (struct iscsi_session *)se_sess->fabric_sess_ptr;
|
||||||
|
if (sess)
|
||||||
|
ret = snprintf(page, PAGE_SIZE, "%llu\n",
|
||||||
|
(unsigned long long)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 = (struct iscsi_session *)se_sess->fabric_sess_ptr;
|
||||||
|
if (sess)
|
||||||
|
ret = snprintf(page, PAGE_SIZE, "%u\n",
|
||||||
|
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 = (struct iscsi_session *)se_sess->fabric_sess_ptr;
|
||||||
|
if (sess)
|
||||||
|
ret = snprintf(page, PAGE_SIZE, "%u\n",
|
||||||
|
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,
|
||||||
|
};
|
64
drivers/target/iscsi/iscsi_target_stat.h
Normal file
64
drivers/target/iscsi/iscsi_target_stat.h
Normal file
|
@ -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 ***/
|
849
drivers/target/iscsi/iscsi_target_tmr.c
Normal file
849
drivers/target/iscsi/iscsi_target_tmr.c
Normal file
|
@ -0,0 +1,849 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* This file contains the iSCSI Target specific Task Management functions.
|
||||||
|
*
|
||||||
|
* \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
|
||||||
|
*
|
||||||
|
* Licensed to the Linux Foundation under the General Public License (GPL) version 2.
|
||||||
|
*
|
||||||
|
* 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/iscsi_proto.h>
|
||||||
|
#include <target/target_core_base.h>
|
||||||
|
#include <target/target_core_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 ((hdr->refcmdsn >= conn->sess->exp_cmd_sn) &&
|
||||||
|
(hdr->refcmdsn <= conn->sess->max_cmd_sn)) ?
|
||||||
|
ISCSI_TMF_RSP_COMPLETE : ISCSI_TMF_RSP_NO_TASK;
|
||||||
|
}
|
||||||
|
if (ref_cmd->cmd_sn != 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 = hdr->rtt;
|
||||||
|
se_tmr->ref_cmd = &ref_cmd->se_cmd;
|
||||||
|
tmr_req->ref_cmd_sn = hdr->refcmdsn;
|
||||||
|
tmr_req->exp_data_sn = 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 0
|
||||||
|
struct iscsi_init_task_mgt_cmnd *hdr =
|
||||||
|
(struct iscsi_init_task_mgt_cmnd *) buf;
|
||||||
|
#endif
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 MaxRecvDataSegmentLength.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
se_tmr->ref_task_tag = hdr->rtt;
|
||||||
|
se_tmr->ref_cmd = &ref_cmd->se_cmd;
|
||||||
|
se_tmr->ref_task_lun = get_unaligned_le64(&hdr->lun);
|
||||||
|
tmr_req->ref_cmd_sn = hdr->refcmdsn;
|
||||||
|
tmr_req->exp_data_sn = 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 se_tmr_req *se_tmr = tmr_req->se_tmr_req;
|
||||||
|
struct se_cmd *se_cmd = se_tmr->ref_cmd;
|
||||||
|
struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_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_list, &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 (!atomic_read(&cmd->transport_sent)) {
|
||||||
|
pr_debug("WRITE ITT: 0x%08x: t_state: %d"
|
||||||
|
" never sent to transport\n",
|
||||||
|
cmd->init_task_tag, cmd->se_cmd.t_state);
|
||||||
|
return transport_generic_handle_data(se_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
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->data_length) {
|
||||||
|
no_build_r2ts = 1;
|
||||||
|
length = (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 iscsit_build_r2ts_for_cmd(cmd, conn, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (!atomic_read(&cmd->transport_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_generic_handle_cdb(se_cmd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!atomic_read(&se_cmd->t_transport_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 se_tmr_req *se_tmr = tmr_req->se_tmr_req;
|
||||||
|
struct se_cmd *se_cmd = se_tmr->ref_cmd;
|
||||||
|
struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_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_list, &conn->conn_cmd_list);
|
||||||
|
spin_unlock_bh(&conn->cmd_lock);
|
||||||
|
|
||||||
|
if (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 se_tmr_req *se_tmr = tmr_req->se_tmr_req;
|
||||||
|
struct se_cmd *se_cmd;
|
||||||
|
struct iscsi_cmd *cmd;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!se_tmr->ref_cmd) {
|
||||||
|
pr_err("TMR Request is missing a RefCmd struct iscsi_cmd.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
se_cmd = se_tmr->ref_cmd;
|
||||||
|
cmd = container_of(se_cmd, struct iscsi_cmd, se_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.
|
||||||
|
*/
|
||||||
|
extern 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nothing to do here, but leave it for good measure. :-)
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int iscsit_task_reassign_prepare_write(
|
||||||
|
struct iscsi_tmr_req *tmr_req,
|
||||||
|
struct iscsi_conn *conn)
|
||||||
|
{
|
||||||
|
struct se_tmr_req *se_tmr = tmr_req->se_tmr_req;
|
||||||
|
struct se_cmd *se_cmd = se_tmr->ref_cmd;
|
||||||
|
struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_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 se_tmr_req *se_tmr = tmr_req->se_tmr_req;
|
||||||
|
struct se_cmd *se_cmd = se_tmr->ref_cmd;
|
||||||
|
struct iscsi_cmd *ref_cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
|
||||||
|
|
||||||
|
if (ref_cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (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;
|
||||||
|
}
|
14
drivers/target/iscsi/iscsi_target_tmr.h
Normal file
14
drivers/target/iscsi/iscsi_target_tmr.h
Normal file
|
@ -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 */
|
759
drivers/target/iscsi/iscsi_target_tpg.c
Normal file
759
drivers/target/iscsi/iscsi_target_tpg.c
Normal file
|
@ -0,0 +1,759 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* This file contains iSCSI Target Portal Group related functions.
|
||||||
|
*
|
||||||
|
* \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
|
||||||
|
*
|
||||||
|
* Licensed to the Linux Foundation under the General Public License (GPL) version 2.
|
||||||
|
*
|
||||||
|
* 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_transport.h>
|
||||||
|
#include <target/target_core_fabric_ops.h>
|
||||||
|
#include <target/target_core_configfs.h>
|
||||||
|
#include <target/target_core_tpg.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"
|
||||||
|
|
||||||
|
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);
|
||||||
|
mutex_init(&tpg->np_login_lock);
|
||||||
|
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, (void *)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_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_FREE) {
|
||||||
|
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) {
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (!tpg_np->tpg_np) {
|
||||||
|
pr_err("struct iscsi_tpg_np->tpg_np is NULL!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iscsit_reset_np_thread(tpg_np->tpg_np, tpg_np, tpg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void iscsit_clear_tpg_np_login_threads(
|
||||||
|
struct iscsi_portal_group *tpg)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
ISCSI_TPG_ATTRIB(tpg)->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;
|
||||||
|
|
||||||
|
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 -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ISCSI_TPG_ATTRIB(tpg)->authentication) {
|
||||||
|
if (!strcmp(param->value, NONE))
|
||||||
|
if (iscsi_update_param_value(param, CHAP) < 0) {
|
||||||
|
spin_unlock(&tpg->tpg_state_lock);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
if (iscsit_ta_authentication(tpg, 1) < 0) {
|
||||||
|
spin_unlock(&tpg->tpg_state_lock);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
tpg_np->tpg_np = 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_network_transport == ISCSI_TCP) ? "TCP" : "SCTP");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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_network_transport == ISCSI_TCP) ? "TCP" : "SCTP");
|
||||||
|
|
||||||
|
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 -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
41
drivers/target/iscsi/iscsi_target_tpg.h
Normal file
41
drivers/target/iscsi/iscsi_target_tpg.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#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 *);
|
||||||
|
extern int iscsit_get_tpg(struct iscsi_portal_group *);
|
||||||
|
extern void iscsit_put_tpg(struct iscsi_portal_group *);
|
||||||
|
extern void iscsit_clear_tpg_np_login_threads(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);
|
||||||
|
|
||||||
|
#endif /* ISCSI_TARGET_TPG_H */
|
551
drivers/target/iscsi/iscsi_target_tq.c
Normal file
551
drivers/target/iscsi/iscsi_target_tq.c
Normal file
|
@ -0,0 +1,551 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* This file contains the iSCSI Login Thread and Thread Queue functions.
|
||||||
|
*
|
||||||
|
* \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
|
||||||
|
*
|
||||||
|
* Licensed to the Linux Foundation under the General Public License (GPL) version 2.
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(ts, &inactive_ts_list, ts_list)
|
||||||
|
break;
|
||||||
|
|
||||||
|
list_del(&ts->ts_list);
|
||||||
|
iscsit_global->inactive_ts--;
|
||||||
|
spin_unlock(&inactive_ts_lock);
|
||||||
|
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern 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_post_start_comp);
|
||||||
|
init_completion(&ts->tx_post_start_comp);
|
||||||
|
init_completion(&ts->rx_restart_comp);
|
||||||
|
init_completion(&ts->tx_restart_comp);
|
||||||
|
init_completion(&ts->rx_start_comp);
|
||||||
|
init_completion(&ts->tx_start_comp);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void iscsi_deallocate_thread_sets(void)
|
||||||
|
{
|
||||||
|
u32 released_count = 0;
|
||||||
|
struct iscsi_thread_set *ts = NULL;
|
||||||
|
|
||||||
|
while ((ts = iscsi_get_ts_from_inactive_list())) {
|
||||||
|
|
||||||
|
spin_lock_bh(&ts->ts_state_lock);
|
||||||
|
ts->status = ISCSI_THREAD_SET_DIE;
|
||||||
|
spin_unlock_bh(&ts->ts_state_lock);
|
||||||
|
|
||||||
|
if (ts->rx_thread) {
|
||||||
|
send_sig(SIGINT, ts->rx_thread, 1);
|
||||||
|
kthread_stop(ts->rx_thread);
|
||||||
|
}
|
||||||
|
if (ts->tx_thread) {
|
||||||
|
send_sig(SIGINT, ts->tx_thread, 1);
|
||||||
|
kthread_stop(ts->tx_thread);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
released_count++;
|
||||||
|
kfree(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
spin_lock_bh(&ts->ts_state_lock);
|
||||||
|
ts->status = ISCSI_THREAD_SET_DIE;
|
||||||
|
spin_unlock_bh(&ts->ts_state_lock);
|
||||||
|
|
||||||
|
if (ts->rx_thread) {
|
||||||
|
send_sig(SIGINT, ts->rx_thread, 1);
|
||||||
|
kthread_stop(ts->rx_thread);
|
||||||
|
}
|
||||||
|
if (ts->tx_thread) {
|
||||||
|
send_sig(SIGINT, ts->tx_thread, 1);
|
||||||
|
kthread_stop(ts->tx_thread);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
released_count++;
|
||||||
|
kfree(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
spin_unlock_bh(&ts->ts_state_lock);
|
||||||
|
/*
|
||||||
|
* Start up the RX thread and wait on rx_post_start_comp. The RX
|
||||||
|
* Thread will then do the same for the TX Thread in
|
||||||
|
* iscsi_rx_thread_pre_handler().
|
||||||
|
*/
|
||||||
|
complete(&ts->rx_start_comp);
|
||||||
|
wait_for_completion(&ts->rx_post_start_comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iscsi_thread_set *iscsi_get_thread_set(void)
|
||||||
|
{
|
||||||
|
int allocate_ts = 0;
|
||||||
|
struct completion comp;
|
||||||
|
struct iscsi_thread_set *ts = NULL;
|
||||||
|
/*
|
||||||
|
* If no inactive thread set is available on the first call to
|
||||||
|
* iscsi_get_ts_from_inactive_list(), sleep for a second and
|
||||||
|
* try again. If still none are available after two attempts,
|
||||||
|
* allocate a set ourselves.
|
||||||
|
*/
|
||||||
|
get_set:
|
||||||
|
ts = iscsi_get_ts_from_inactive_list();
|
||||||
|
if (!ts) {
|
||||||
|
if (allocate_ts == 2)
|
||||||
|
iscsi_allocate_thread_sets(1);
|
||||||
|
|
||||||
|
init_completion(&comp);
|
||||||
|
wait_for_completion_timeout(&comp, 1 * HZ);
|
||||||
|
|
||||||
|
allocate_ts++;
|
||||||
|
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);
|
||||||
|
|
||||||
|
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) || 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (!ts->conn) {
|
||||||
|
pr_err("struct iscsi_thread_set->conn is NULL for"
|
||||||
|
" thread_id: %d, going back to sleep\n", ts->thread_id);
|
||||||
|
goto sleep;
|
||||||
|
}
|
||||||
|
iscsi_check_to_add_additional_sets();
|
||||||
|
/*
|
||||||
|
* The RX Thread starts up the TX Thread and sleeps.
|
||||||
|
*/
|
||||||
|
ts->thread_clear |= ISCSI_CLEAR_RX_THREAD;
|
||||||
|
complete(&ts->tx_start_comp);
|
||||||
|
wait_for_completion(&ts->tx_post_start_comp);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (!ts->conn) {
|
||||||
|
pr_err("struct iscsi_thread_set->conn is NULL for "
|
||||||
|
" thread_id: %d, going back to sleep\n",
|
||||||
|
ts->thread_id);
|
||||||
|
goto sleep;
|
||||||
|
}
|
||||||
|
|
||||||
|
iscsi_check_to_add_additional_sets();
|
||||||
|
/*
|
||||||
|
* From the TX thread, up the tx_post_start_comp that the RX Thread is
|
||||||
|
* sleeping on in iscsi_rx_thread_pre_handler(), then up the
|
||||||
|
* rx_post_start_comp that iscsi_activate_thread_set() is sleeping on.
|
||||||
|
*/
|
||||||
|
ts->thread_clear |= ISCSI_CLEAR_TX_THREAD;
|
||||||
|
complete(&ts->tx_post_start_comp);
|
||||||
|
complete(&ts->rx_post_start_comp);
|
||||||
|
|
||||||
|
spin_lock_bh(&ts->ts_state_lock);
|
||||||
|
ts->status = ISCSI_THREAD_SET_ACTIVE;
|
||||||
|
spin_unlock_bh(&ts->ts_state_lock);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_init(&active_ts_lock);
|
||||||
|
spin_lock_init(&inactive_ts_lock);
|
||||||
|
spin_lock_init(&ts_bitmap_lock);
|
||||||
|
INIT_LIST_HEAD(&active_ts_list);
|
||||||
|
INIT_LIST_HEAD(&inactive_ts_list);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iscsi_thread_set_free(void)
|
||||||
|
{
|
||||||
|
kfree(iscsit_global->ts_bitmap);
|
||||||
|
}
|
88
drivers/target/iscsi/iscsi_target_tq.h
Normal file
88
drivers/target/iscsi/iscsi_target_tq.h
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#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 void iscsi_add_ts_to_inactive_list(struct iscsi_thread_set *);
|
||||||
|
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 rx side post startup */
|
||||||
|
struct completion rx_post_start_comp;
|
||||||
|
/* Used for tx side post startup */
|
||||||
|
struct completion tx_post_start_comp;
|
||||||
|
/* 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /*** ISCSI_THREAD_QUEUE_H ***/
|
1819
drivers/target/iscsi/iscsi_target_util.c
Normal file
1819
drivers/target/iscsi/iscsi_target_util.c
Normal file
File diff suppressed because it is too large
Load diff
60
drivers/target/iscsi/iscsi_target_util.h
Normal file
60
drivers/target/iscsi/iscsi_target_util.h
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#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_allocate_cmd(struct iscsi_conn *, gfp_t);
|
||||||
|
extern struct iscsi_cmd *iscsit_allocate_se_cmd(struct iscsi_conn *, u32, int, int);
|
||||||
|
extern struct iscsi_cmd *iscsit_allocate_se_cmd_for_tmr(struct iscsi_conn *, u8);
|
||||||
|
extern int iscsit_decide_list_to_build(struct iscsi_cmd *, u32);
|
||||||
|
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);
|
||||||
|
int iscsit_sequence_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, u32 cmdsn);
|
||||||
|
extern int iscsit_check_unsolicited_dataout(struct iscsi_cmd *, unsigned char *);
|
||||||
|
extern struct iscsi_cmd *iscsit_find_cmd_from_itt(struct iscsi_conn *, u32);
|
||||||
|
extern struct iscsi_cmd *iscsit_find_cmd_from_itt_or_dump(struct iscsi_conn *,
|
||||||
|
u32, 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 **, u32);
|
||||||
|
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 void iscsit_free_queue_reqs_for_conn(struct iscsi_conn *);
|
||||||
|
extern void iscsit_release_cmd(struct iscsi_cmd *);
|
||||||
|
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 ***/
|
Loading…
Reference in a new issue