Files
scst/iscsi-scst/kernel/conn.c
T
Yan Burman de6aa12e9c Merged revisions 5409-5452,5454 via svnmerge from
svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk

........
  r5409 | bvassche | 2014-04-06 23:13:53 +0300 (Sun, 06 Apr 2014) | 4 lines
  
  scstadmin: Restore LUNs in "scstadmin -list_sessions" output
  
  Signed-off-by: Dave Butler <tears.the@gmail.com>
........
  r5410 | bvassche | 2014-04-06 23:26:08 +0300 (Sun, 06 Apr 2014) | 1 line
  
  scstadmin: List keys alphabetically in the -list_sessions output
........
  r5411 | vlnb | 2014-04-10 02:58:20 +0300 (Thu, 10 Apr 2014) | 10 lines
  
  vdisk_blockio: Specify REQ_SYNC for synchronous I/O requests
  
  Using READ_SYNC instead of READ increases the priority of read requests.
  Using WRITE_SYNC instead of REQ_WRITE increases the priority of write
  requests and avoids that the CFQ scheduler queues such writes waiting
  for further write requests.
  
  Signed-off-by: Bart Van Assche <bvanassche@acm.org>
........
  r5412 | vlnb | 2014-04-10 02:59:02 +0300 (Thu, 10 Apr 2014) | 18 lines
  
  vdisk_blockio: Reenable COMPARE AND WRITE
  
  The COMPARE AND WRITE implementation has been tested as follows against
  an SCST vdisk_blockio device:
  
  lba=7
  bdev=$bdev
  for ((i=0;i<4;i++)); do dd if=/dev/urandom of=b$i bs=8k count=1; done
  for p in "0 1" "1 2" "2 3" "3 0"; do set $p; cat b$1 b$2 >b$1$2; done
  dd if=/dev/urandom of=$bdev
  dd if=b0 of=$bdev seek=$lba
  md5sum $bdev
  for ((i=0;i<10000;i++)); do for f in b01 b12 b23 b30; do sg_compare_and_write -l $lba -i $f -n 16 -x 16384 $bdev; done; done
  md5sum $bdev
  
  Signed-off-by: Bart Van Assche <bvanassche@acm.org>
........
  r5413 | bvassche | 2014-04-15 09:03:59 +0300 (Tue, 15 Apr 2014) | 1 line
  
  ib_srpt: Fix a sparse warning
........
  r5414 | vlnb | 2014-04-16 00:26:06 +0300 (Wed, 16 Apr 2014) | 5 lines
  
  Fix READ(6)/WRITE(6)
  
  LBA in those commands is 3 bytes long, not 2.
........
  r5415 | vlnb | 2014-04-16 00:30:26 +0300 (Wed, 16 Apr 2014) | 3 lines
  
  Add SYNCHRONIZE_CACHE(16)
........
  r5416 | vlnb | 2014-04-16 01:04:54 +0300 (Wed, 16 Apr 2014) | 6 lines
  
  Make HEAD OF QUEUE requests sync
  
  Since the block layer has no way to specify bio as HQ, there's no
  choice, but to use every measure to approximate it as close as possible.
........
  r5417 | vlnb | 2014-04-16 01:17:34 +0300 (Wed, 16 Apr 2014) | 3 lines
  
  Add NULLIO VERIFY
........
  r5418 | vlnb | 2014-04-16 04:02:50 +0300 (Wed, 16 Apr 2014) | 3 lines
  
  REPORT SUPPORTED OPERATION CODES added
........
  r5419 | vlnb | 2014-04-16 04:05:49 +0300 (Wed, 16 Apr 2014) | 3 lines
  
  Cleanup
........
  r5420 | vlnb | 2014-04-16 05:24:44 +0300 (Wed, 16 Apr 2014) | 3 lines
  
  Follow up for r5418: some cleanups and fixes
........
  r5421 | bvassche | 2014-04-16 09:17:19 +0300 (Wed, 16 Apr 2014) | 6 lines
  
  scst_targ: Fix a checkpatch complaint
  
  Avoid that checkpatch reports the following message:
  
  ERROR: space required before the open parenthesis '('
........
  r5422 | bvassche | 2014-04-16 09:35:19 +0300 (Wed, 16 Apr 2014) | 1 line
  
  scst_vdisk: Build fix for kernels < 2.6.36 (see also r5416)
........
  r5423 | bvassche | 2014-04-16 10:30:34 +0300 (Wed, 16 Apr 2014) | 6 lines
  
  ib_srpt: Disable RDMA access by the initiator
  
  With the SRP protocol all RDMA operations are initiated by the target.
  Since no RDMA operations are initiated by the initiator, do not grant
  the initiator permission to submit RDMA reads or writes to the target.
........
  r5424 | bvassche | 2014-04-16 11:01:58 +0300 (Wed, 16 Apr 2014) | 1 line
  
  ib_srpt: Constify two arguments of srpt_get_nexus()
........
  r5425 | bvassche | 2014-04-16 11:08:50 +0300 (Wed, 16 Apr 2014) | 1 line
  
  ib_srpt: Clean up the code that prints the dgid during login
........
  r5426 | bvassche | 2014-04-16 11:23:11 +0300 (Wed, 16 Apr 2014) | 1 line
  
  ib_srpt: Cache P_Key lookups
........
  r5427 | bvassche | 2014-04-16 12:14:31 +0300 (Wed, 16 Apr 2014) | 1 line
  
  ib_srpt: Remove a superfluous assignment
........
  r5428 | bvassche | 2014-04-16 12:31:56 +0300 (Wed, 16 Apr 2014) | 1 line
  
  scst_vdisk: Avoid that checkpatch complains about unnecessary line continuations
........
  r5429 | bvassche | 2014-04-16 12:35:18 +0300 (Wed, 16 Apr 2014) | 1 line
  
  scst_lib: Avoid that checkpatch complains about unnecessary line continuations
........
  r5430 | bvassche | 2014-04-17 09:09:41 +0300 (Thu, 17 Apr 2014) | 1 line
  
  scst_vdisk: Kernel 2.6.27 build fix
........
  r5431 | bvassche | 2014-04-17 09:12:07 +0300 (Thu, 17 Apr 2014) | 5 lines
  
  scst_vdisk: Fix a kernel 2.6.27 compiler warning
  
  Avoid that the compiler reports that variables 'start_sector' and 'nr_sects'
  are set but not used when building against kernel 2.6.27.
........
  r5432 | vlnb | 2014-04-18 03:32:51 +0300 (Fri, 18 Apr 2014) | 3 lines
  
  Update to kernels 3.14
........
  r5433 | bvassche | 2014-04-18 08:50:04 +0300 (Fri, 18 Apr 2014) | 1 line
  
  nightly build: Add kernel 3.14 build infrastructure
........
  r5434 | bvassche | 2014-04-18 08:54:29 +0300 (Fri, 18 Apr 2014) | 1 line
  
  nightly build: Add kernel version 3.14.1 and update other kernel versions
........
  r5435 | bvassche | 2014-04-18 08:56:35 +0300 (Fri, 18 Apr 2014) | 6 lines
  
  iscsi-scst: Fix a checkpatch warning
  
  Fix the following checkpatch 3.14 warning:
  
  Unnecessary parentheses - maybe == should be = ?
........
  r5436 | bvassche | 2014-04-18 09:03:34 +0300 (Fri, 18 Apr 2014) | 6 lines
  
  scst.h: Fix a checkpatch warning
  
  Fix the following checkpatch 3.14 warning:
  
  Unnecessary space after function pointer name
........
  r5437 | bvassche | 2014-04-18 09:08:07 +0300 (Fri, 18 Apr 2014) | 6 lines
  
  scst: Fix a checkpatch warning
  
  Fix the following checkpatch 3.14 warning:
      
  Unnecessary space after function pointer name
........
  r5438 | bvassche | 2014-04-18 09:13:03 +0300 (Fri, 18 Apr 2014) | 6 lines
  
  scst: Fix a checkpatch 3.14 warning about whitespace
  
  Fix the following checkpatch 3.14 warning:
  
  missing space after return type
........
  r5439 | bvassche | 2014-04-18 14:09:55 +0300 (Fri, 18 Apr 2014) | 1 line
  
  ib_srpt: Update README
........
  r5440 | bvassche | 2014-04-18 15:04:05 +0300 (Fri, 18 Apr 2014) | 2 lines
  
  ib_srpt: Move IB/CM knowledge out of srpt_cm_req_recv()
........
  r5441 | bvassche | 2014-04-18 15:08:00 +0300 (Fri, 18 Apr 2014) | 6 lines
  
  ib_srpt: Remove a superfluous check from the REQ handler
  
  ib_send_cm_rep() checks the connection state before sending a response.
  Hence checking ch->state before calling ib_send_cm_rep() is superfluous,
  so remove that check and also the locking that is no longer needed.
........
  r5442 | bvassche | 2014-04-18 15:09:33 +0300 (Fri, 18 Apr 2014) | 5 lines
  
  ib_srpt: Prepare RDMA/CM support
      
  Move IB/CM members into a new struct. Report channel pointer instead of CM ID
  pointer in diagnostic messages.
........
  r5443 | bvassche | 2014-04-18 15:10:47 +0300 (Fri, 18 Apr 2014) | 5 lines
  
  ib_srpt: Use a mutex instead of a spinlock to protect the channel list
      
  This is allowed because all CM callback functions are invoked from thread
  context.
........
  r5444 | bvassche | 2014-04-18 15:11:18 +0300 (Fri, 18 Apr 2014) | 2 lines
  
  ib_srpt: Move the code for checking the QP timeout
........
  r5445 | bvassche | 2014-04-18 15:22:30 +0300 (Fri, 18 Apr 2014) | 4 lines
  
  ib_srpt: Add RDMA/CM support
  
  Or in other words, add RoCE and iWARP support.
........
  r5446 | bvassche | 2014-04-18 15:38:39 +0300 (Fri, 18 Apr 2014) | 1 line
  
  ib_srpt: Build fix for kernel versions < 3.0
........
  r5447 | bvassche | 2014-04-18 15:44:48 +0300 (Fri, 18 Apr 2014) | 1 line
  
  ib_srpt: RHEL 6.5 build fix
........
  r5448 | bvassche | 2014-04-19 14:48:33 +0300 (Sat, 19 Apr 2014) | 1 line
  
  scst_vdisk, COMPARE AND WRITE: Convert a kernel warning into a SCSI sense code
........
  r5449 | bvassche | 2014-04-19 14:52:34 +0300 (Sat, 19 Apr 2014) | 33 lines
  
  vdisk_blockio: Make COMPARE AND WRITE compatible with the scsi_debug driver
  
  This patch fixes the following kernel oops:
  
  BUG: unable to handle kernel paging request at ffffeae380000690
  Call Trace:
   [<ffffffff812f69f9>] sg_miter_next+0x9/0xd0
   [<ffffffff812f6b60>] sg_copy_buffer+0xa0/0x100
   [<ffffffffa0507fc6>] do_device_access.isra.8+0xa6/0x150 [scsi_debug]
   [<ffffffffa05085f4>] resp_read+0xe4/0x240 [scsi_debug]
   [<ffffffffa050b285>] scsi_debug_queuecommand_lck+0x11e5/0x2060 [scsi_debug]
   [<ffffffffa050c130>] scsi_debug_queuecommand+0x30/0x48 [scsi_debug]
   [<ffffffff813fe23f>] scsi_dispatch_cmd+0xaf/0x260
   [<ffffffff814068ed>] scsi_request_fn+0x32d/0x540
   [<ffffffff812bda2a>] __blk_run_queue+0x2a/0x40
   [<ffffffff812c25a4>] blk_queue_bio+0x274/0x350
   [<ffffffff812bd878>] generic_make_request+0xa8/0xf0
   [<ffffffff812bd92c>] submit_bio+0x6c/0x140
   [<ffffffffa06247f6>] blockio_rw_sync.isra.29+0x106/0x170 [scst_vdisk]
   [<ffffffffa0629bf9>] vdisk_exec_caw+0xd9/0x3c0 [scst_vdisk]
   [<ffffffffa061ff5e>] vdev_do_job+0x9e/0x320 [scst_vdisk]
   [<ffffffffa0622c47>] non_fileio_exec+0x57/0xd0 [scst_vdisk]
   [<ffffffffa054cde2>] scst_do_real_exec+0x92/0x3b0 [scst]
   [<ffffffffa05502d2>] scst_exec_check_blocking+0xe2/0x300 [scst]
   [<ffffffffa055066b>] scst_exec_check_sn+0x17b/0x2d0 [scst]
   [<ffffffffa0553231>] scst_process_active_cmd+0x431/0x770 [scst]
   [<ffffffffa0554a0a>] scst_do_job_active+0xea/0x180 [scst]
   [<ffffffffa0554bc6>] scst_cmd_thread+0x126/0x290 [scst]
   [<ffffffff81075241>] kthread+0xc1/0xe0
   [<ffffffff81598ccc>] ret_from_fork+0x7c/0xb0
  
  Reported-by: Sebastian Herbszt <herbszt@gmx.de>
........
  r5450 | bvassche | 2014-04-20 09:24:23 +0300 (Sun, 20 Apr 2014) | 1 line
  
  iscsi-scst/kernel/patches/put_page_callback-3.2.57.patch: Add
........
  r5451 | bvassche | 2014-04-22 09:56:37 +0300 (Tue, 22 Apr 2014) | 1 line
  
  scst: Revert r5438, a whitespace-only change
........
  r5452 | bvassche | 2014-04-22 10:05:21 +0300 (Tue, 22 Apr 2014) | 1 line
  
  scripts/run-regression-tests: Suppress the checkpatch warning "missing space after return type"
........
  r5454 | bvassche | 2014-04-22 10:32:44 +0300 (Tue, 22 Apr 2014) | 1 line
  
  scst/README: Update the section about Linux initiator ALUA support
........


git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5455 d57e44dd-8a1f-0410-8b47-8ef2f437770f
2014-04-22 07:50:34 +00:00

1075 lines
27 KiB
C

/*
* Copyright (C) 2002 - 2003 Ardis Technolgies <roman@ardistech.com>
* Copyright (C) 2007 - 2014 Vladislav Bolkhovitin
* Copyright (C) 2007 - 2014 Fusion-io, Inc.
*
* 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, version 2
* of the License.
*
* 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/file.h>
#include <linux/ip.h>
#include <net/tcp.h>
#include "iscsi.h"
#include "digest.h"
#include "iscsit_transport.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
#if defined(CONFIG_LOCKDEP) && !defined(CONFIG_SCST_PROC)
static struct lock_class_key scst_conn_key;
static struct lockdep_map scst_conn_dep_map =
STATIC_LOCKDEP_MAP_INIT("iscsi_conn_kref", &scst_conn_key);
#endif
#endif
static int print_conn_state(char *p, size_t size, struct iscsi_conn *conn)
{
int pos = 0;
if (conn->closing) {
pos += scnprintf(p, size, "%s", "closing");
goto out;
}
switch (conn->rd_state) {
case ISCSI_CONN_RD_STATE_PROCESSING:
pos += scnprintf(&p[pos], size - pos, "%s", "read_processing ");
break;
case ISCSI_CONN_RD_STATE_IN_LIST:
pos += scnprintf(&p[pos], size - pos, "%s", "in_read_list ");
break;
}
switch (conn->wr_state) {
case ISCSI_CONN_WR_STATE_PROCESSING:
pos += scnprintf(&p[pos], size - pos, "%s", "write_processing ");
break;
case ISCSI_CONN_WR_STATE_IN_LIST:
pos += scnprintf(&p[pos], size - pos, "%s", "in_write_list ");
break;
case ISCSI_CONN_WR_STATE_SPACE_WAIT:
pos += scnprintf(&p[pos], size - pos, "%s", "space_waiting ");
break;
}
if (test_bit(ISCSI_CONN_REINSTATING, &conn->conn_aflags))
pos += scnprintf(&p[pos], size - pos, "%s", "reinstating ");
else if (pos == 0)
pos += scnprintf(&p[pos], size - pos, "%s", "established idle ");
out:
return pos;
}
#ifdef CONFIG_SCST_PROC
static int print_digest_state(char *p, size_t size, unsigned long flags)
{
int pos;
if (DIGEST_NONE & flags)
pos = scnprintf(p, size, "%s", "none");
else if (DIGEST_CRC32C & flags)
pos = scnprintf(p, size, "%s", "crc32c");
else
pos = scnprintf(p, size, "%s", "unknown");
return pos;
}
/* target_mutex supposed to be locked */
void conn_info_show(struct seq_file *seq, struct iscsi_session *session)
{
struct iscsi_conn *conn;
struct sock *sk;
char buf[64];
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&session->target->target_mutex);
#endif
list_for_each_entry(conn, &session->conn_list, conn_list_entry) {
sk = conn->sock->sk;
switch (sk->sk_family) {
case AF_INET:
snprintf(buf, sizeof(buf),
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
"%u.%u.%u.%u", NIPQUAD(inet_sk(sk)->daddr));
#else
"%pI4", &inet_sk(sk)->inet_daddr);
#endif
break;
case AF_INET6:
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
snprintf(buf, sizeof(buf),
"[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]",
NIP6(inet6_sk(sk)->daddr));
#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
snprintf(buf, sizeof(buf), "[%p6]",
&inet6_sk(sk)->daddr);
#else
snprintf(buf, sizeof(buf), "[%p6]",
&sk->sk_v6_daddr);
#endif
break;
default:
snprintf(buf, sizeof(buf), "Unknown family %d",
sk->sk_family);
break;
}
seq_printf(seq, "\t\tcid:%u ip:%s ", conn->cid, buf);
print_conn_state(buf, sizeof(buf), conn);
seq_printf(seq, "state:%s ", buf);
print_digest_state(buf, sizeof(buf), conn->hdigest_type);
seq_printf(seq, "hd:%s ", buf);
print_digest_state(buf, sizeof(buf), conn->ddigest_type);
seq_printf(seq, "dd:%s\n", buf);
}
}
#else /* CONFIG_SCST_PROC */
static void iscsi_conn_release(struct kobject *kobj)
{
struct iscsi_conn *conn;
TRACE_ENTRY();
conn = container_of(kobj, struct iscsi_conn, conn_kobj);
if (conn->conn_kobj_release_cmpl != NULL)
complete_all(conn->conn_kobj_release_cmpl);
TRACE_EXIT();
return;
}
struct kobj_type iscsi_conn_ktype = {
.release = iscsi_conn_release,
};
static ssize_t iscsi_get_initiator_ip(struct iscsi_conn *conn,
char *buf, int size)
{
return conn->transport->iscsit_get_initiator_ip(conn, buf, size);
}
static ssize_t iscsi_conn_ip_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int pos;
struct iscsi_conn *conn;
TRACE_ENTRY();
conn = container_of(kobj, struct iscsi_conn, conn_kobj);
pos = iscsi_get_initiator_ip(conn, buf, SCST_SYSFS_BLOCK_SIZE);
TRACE_EXIT_RES(pos);
return pos;
}
static struct kobj_attribute iscsi_conn_ip_attr =
__ATTR(ip, S_IRUGO, iscsi_conn_ip_show, NULL);
static ssize_t iscsi_conn_cid_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int pos;
struct iscsi_conn *conn;
TRACE_ENTRY();
conn = container_of(kobj, struct iscsi_conn, conn_kobj);
pos = sprintf(buf, "%u", conn->cid);
TRACE_EXIT_RES(pos);
return pos;
}
static struct kobj_attribute iscsi_conn_cid_attr =
__ATTR(cid, S_IRUGO, iscsi_conn_cid_show, NULL);
static ssize_t iscsi_conn_state_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int pos;
struct iscsi_conn *conn;
TRACE_ENTRY();
conn = container_of(kobj, struct iscsi_conn, conn_kobj);
pos = print_conn_state(buf, SCST_SYSFS_BLOCK_SIZE, conn);
TRACE_EXIT_RES(pos);
return pos;
}
static struct kobj_attribute iscsi_conn_state_attr =
__ATTR(state, S_IRUGO, iscsi_conn_state_show, NULL);
static void conn_sysfs_del(struct iscsi_conn *conn)
{
DECLARE_COMPLETION_ONSTACK(c);
TRACE_ENTRY();
conn->conn_kobj_release_cmpl = &c;
kobject_del(&conn->conn_kobj);
SCST_KOBJECT_PUT_AND_WAIT(&conn->conn_kobj, "conn",
conn->conn_kobj_release_cmpl,
&scst_conn_dep_map);
TRACE_EXIT();
return;
}
int conn_sysfs_add(struct iscsi_conn *conn)
{
int res;
struct iscsi_session *session = conn->session;
struct iscsi_conn *c;
int n = 1;
char addr[64];
TRACE_ENTRY();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&conn->target->target_mutex);
#endif
iscsi_get_initiator_ip(conn, addr, sizeof(addr));
restart:
list_for_each_entry(c, &session->conn_list, conn_list_entry) {
if (strcmp(addr, kobject_name(&c->conn_kobj)) == 0) {
char c_addr[64];
iscsi_get_initiator_ip(conn, c_addr, sizeof(c_addr));
TRACE_DBG("Duplicated conn from the same initiator "
"%s found", c_addr);
snprintf(addr, sizeof(addr), "%s_%d", c_addr, n);
n++;
goto restart;
}
}
res = kobject_init_and_add(&conn->conn_kobj, &iscsi_conn_ktype,
scst_sysfs_get_sess_kobj(session->scst_sess), addr);
if (res != 0) {
PRINT_ERROR("Unable create sysfs entries for conn %s",
addr);
goto out;
}
TRACE_DBG("conn %p, conn_kobj %p", conn, &conn->conn_kobj);
res = sysfs_create_file(&conn->conn_kobj,
&iscsi_conn_state_attr.attr);
if (res != 0) {
PRINT_ERROR("Unable create sysfs attribute %s for conn %s",
iscsi_conn_state_attr.attr.name, addr);
goto out_err;
}
res = sysfs_create_file(&conn->conn_kobj,
&iscsi_conn_cid_attr.attr);
if (res != 0) {
PRINT_ERROR("Unable create sysfs attribute %s for conn %s",
iscsi_conn_cid_attr.attr.name, addr);
goto out_err;
}
res = sysfs_create_file(&conn->conn_kobj,
&iscsi_conn_ip_attr.attr);
if (res != 0) {
PRINT_ERROR("Unable create sysfs attribute %s for conn %s",
iscsi_conn_ip_attr.attr.name, addr);
goto out_err;
}
out:
TRACE_EXIT_RES(res);
return res;
out_err:
conn_sysfs_del(conn);
goto out;
}
EXPORT_SYMBOL(conn_sysfs_add);
#endif /* CONFIG_SCST_PROC */
/* target_mutex supposed to be locked */
struct iscsi_conn *conn_lookup(struct iscsi_session *session, u16 cid)
{
struct iscsi_conn *conn;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&session->target->target_mutex);
#endif
/*
* We need to find the latest conn to correctly handle
* multi-reinstatements
*/
list_for_each_entry_reverse(conn, &session->conn_list,
conn_list_entry) {
if (conn->cid == cid)
return conn;
}
return NULL;
}
void iscsi_make_conn_rd_active(struct iscsi_conn *conn)
{
struct iscsi_thread_pool *p = conn->conn_thr_pool;
TRACE_ENTRY();
spin_lock_bh(&p->rd_lock);
TRACE_DBG("conn %p, rd_state %x, rd_data_ready %d", conn,
conn->rd_state, conn->rd_data_ready);
/*
* Let's start processing ASAP not waiting for all the being waited
* data be received, even if we need several wakup iteration to receive
* them all, because starting ASAP, i.e. in parallel, is better for
* performance, especially on multi-CPU/core systems.
*/
conn->rd_data_ready = 1;
if (conn->rd_state == ISCSI_CONN_RD_STATE_IDLE) {
list_add_tail(&conn->rd_list_entry, &p->rd_list);
conn->rd_state = ISCSI_CONN_RD_STATE_IN_LIST;
wake_up(&p->rd_waitQ);
}
spin_unlock_bh(&p->rd_lock);
TRACE_EXIT();
return;
}
void iscsi_make_conn_wr_active(struct iscsi_conn *conn)
{
struct iscsi_thread_pool *p = conn->conn_thr_pool;
TRACE_ENTRY();
spin_lock_bh(&p->wr_lock);
TRACE_DBG("conn %p, wr_state %x, wr_space_ready %d", conn,
conn->wr_state, conn->wr_space_ready);
/*
* Let's start sending waiting to be sent data ASAP, even if there's
* still not all the needed buffers ready and we need several wakup
* iteration to send them all, because starting ASAP, i.e. in parallel,
* is better for performance, especially on multi-CPU/core systems.
*/
if (conn->wr_state == ISCSI_CONN_WR_STATE_IDLE) {
list_add_tail(&conn->wr_list_entry, &p->wr_list);
conn->wr_state = ISCSI_CONN_WR_STATE_IN_LIST;
wake_up(&p->wr_waitQ);
}
spin_unlock_bh(&p->wr_lock);
TRACE_EXIT();
return;
}
void iscsi_tcp_mark_conn_closed(struct iscsi_conn *conn, int flags)
{
spin_lock_bh(&conn->conn_thr_pool->rd_lock);
conn->closing = 1;
if (flags & ISCSI_CONN_ACTIVE_CLOSE)
conn->active_close = 1;
if (flags & ISCSI_CONN_DELETING)
conn->deleting = 1;
spin_unlock_bh(&conn->conn_thr_pool->rd_lock);
iscsi_make_conn_rd_active(conn);
}
void __mark_conn_closed(struct iscsi_conn *conn, int flags)
{
conn->transport->iscsit_mark_conn_closed(conn, flags);
}
void mark_conn_closed(struct iscsi_conn *conn)
{
__mark_conn_closed(conn, ISCSI_CONN_ACTIVE_CLOSE);
}
EXPORT_SYMBOL(mark_conn_closed);
static void __iscsi_state_change(struct sock *sk)
{
struct iscsi_conn *conn = sk->sk_user_data;
TRACE_ENTRY();
if (unlikely(sk->sk_state != TCP_ESTABLISHED)) {
if (!conn->closing) {
PRINT_ERROR("Connection with initiator %s "
"unexpectedly closed!",
conn->session->initiator_name);
TRACE_MGMT_DBG("conn %p, sk state %d", conn,
sk->sk_state);
__mark_conn_closed(conn, 0);
}
} else
iscsi_make_conn_rd_active(conn);
TRACE_EXIT();
return;
}
static void iscsi_state_change(struct sock *sk)
{
struct iscsi_conn *conn = sk->sk_user_data;
__iscsi_state_change(sk);
conn->old_state_change(sk);
return;
}
static void iscsi_data_ready(struct sock *sk, int len)
{
struct iscsi_conn *conn = sk->sk_user_data;
TRACE_ENTRY();
iscsi_make_conn_rd_active(conn);
conn->old_data_ready(sk, len);
TRACE_EXIT();
return;
}
void __iscsi_write_space_ready(struct iscsi_conn *conn)
{
struct iscsi_thread_pool *p = conn->conn_thr_pool;
TRACE_ENTRY();
spin_lock_bh(&p->wr_lock);
conn->wr_space_ready = 1;
if (conn->wr_state == ISCSI_CONN_WR_STATE_SPACE_WAIT) {
TRACE_DBG("wr space ready (conn %p)", conn);
list_add_tail(&conn->wr_list_entry, &p->wr_list);
conn->wr_state = ISCSI_CONN_WR_STATE_IN_LIST;
wake_up(&p->wr_waitQ);
}
spin_unlock_bh(&p->wr_lock);
TRACE_EXIT();
return;
}
static void iscsi_write_space_ready(struct sock *sk)
{
struct iscsi_conn *conn = sk->sk_user_data;
TRACE_ENTRY();
TRACE_DBG("Write space ready for conn %p", conn);
__iscsi_write_space_ready(conn);
conn->old_write_space(sk);
TRACE_EXIT();
return;
}
static void conn_rsp_timer_fn(unsigned long arg)
{
struct iscsi_conn *conn = (struct iscsi_conn *)arg;
struct iscsi_cmnd *cmnd;
unsigned long j = jiffies;
TRACE_ENTRY();
TRACE_DBG("Timer (conn %p)", conn);
spin_lock_bh(&conn->write_list_lock);
if (!list_empty(&conn->write_timeout_list)) {
unsigned long timeout_time;
cmnd = list_first_entry(&conn->write_timeout_list,
struct iscsi_cmnd, write_timeout_list_entry);
timeout_time = iscsi_get_timeout_time(cmnd) + ISCSI_ADD_SCHED_TIME;
if (unlikely(time_after_eq(j, iscsi_get_timeout_time(cmnd)))) {
if (!conn->closing) {
PRINT_ERROR("Timeout %ld sec sending data/waiting "
"for reply to/from initiator "
"%s (SID %llx), closing connection",
iscsi_get_timeout(cmnd)/HZ,
conn->session->initiator_name,
(long long unsigned int)
conn->session->sid);
/*
* We must call mark_conn_closed() outside of
* write_list_lock or we will have a circular
* locking dependency with rd_lock.
*/
spin_unlock_bh(&conn->write_list_lock);
mark_conn_closed(conn);
goto out;
}
} else if (!timer_pending(&conn->rsp_timer) ||
time_after(conn->rsp_timer.expires, timeout_time)) {
TRACE_DBG("Restarting timer on %ld (conn %p)",
timeout_time, conn);
/*
* Timer might have been restarted while we were
* entering here.
*
* Since we have not empty write_timeout_list, we are
* safe to restart the timer, because we not race with
* del_timer_sync() in conn_free().
*/
mod_timer(&conn->rsp_timer, timeout_time);
}
}
spin_unlock_bh(&conn->write_list_lock);
if (unlikely(conn->conn_tm_active)) {
TRACE_MGMT_DBG("TM active: making conn %p RD active", conn);
iscsi_make_conn_rd_active(conn);
}
out:
TRACE_EXIT();
return;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
static void conn_nop_in_delayed_work_fn(void *p)
#else
static void conn_nop_in_delayed_work_fn(struct work_struct *work)
#endif
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
struct iscsi_conn *conn = (struct iscsi_conn *)p;
#else
struct iscsi_conn *conn = container_of(work, struct iscsi_conn,
nop_in_delayed_work.work);
#endif
unsigned long next_timeout = 0;
TRACE_ENTRY();
if (time_after_eq(jiffies, conn->last_rcv_time +
conn->nop_in_interval)) {
if (list_empty(&conn->nop_req_list))
iscsi_send_nop_in(conn);
next_timeout = conn->nop_in_interval;
}
if ((conn->nop_in_interval > 0) &&
!test_bit(ISCSI_CONN_SHUTTINGDOWN, &conn->conn_aflags)) {
if (next_timeout == 0)
next_timeout = conn->nop_in_interval -
(jiffies - conn->last_rcv_time);
TRACE_DBG("Reschedule Nop-In work for conn %p in %lu", conn,
next_timeout + ISCSI_ADD_SCHED_TIME);
schedule_delayed_work(&conn->nop_in_delayed_work,
next_timeout + ISCSI_ADD_SCHED_TIME);
}
TRACE_EXIT();
return;
}
/* Must be called from rd thread only */
void iscsi_check_tm_data_wait_timeouts(struct iscsi_conn *conn, bool force)
{
struct iscsi_cmnd *cmnd;
unsigned long j = jiffies;
bool aborted_cmds_pending;
unsigned long timeout_time = j + ISCSI_TM_DATA_WAIT_TIMEOUT +
ISCSI_ADD_SCHED_TIME;
TRACE_ENTRY();
TRACE_DBG_FLAG(TRACE_MGMT_DEBUG, "conn %p, read_cmnd %p, read_state "
"%d, j %ld (TIMEOUT %d, force %d)", conn, conn->read_cmnd,
conn->read_state, j,
ISCSI_TM_DATA_WAIT_TIMEOUT + ISCSI_ADD_SCHED_TIME, force);
iscsi_extracheck_is_rd_thread(conn);
again:
spin_lock_bh(&conn->conn_thr_pool->rd_lock);
spin_lock(&conn->write_list_lock);
aborted_cmds_pending = false;
list_for_each_entry(cmnd, &conn->write_timeout_list,
write_timeout_list_entry) {
/*
* This should not happen, because DATA OUT commands can't get
* into write_timeout_list.
*/
sBUG_ON(cmnd->cmd_req != NULL);
if (test_bit(ISCSI_CMD_ABORTED, &cmnd->prelim_compl_flags)) {
TRACE_MGMT_DBG("Checking aborted cmnd %p (scst_state "
"%d, on_write_timeout_list %d, write_start "
"%ld, r2t_len_to_receive %d)", cmnd,
cmnd->scst_state, cmnd->on_write_timeout_list,
cmnd->write_start, cmnd->r2t_len_to_receive);
if ((cmnd == conn->read_cmnd) ||
cmnd->data_out_in_data_receiving) {
sBUG_ON((cmnd == conn->read_cmnd) && force);
/*
* We can't abort command waiting for data from
* the net, because otherwise we are risking to
* get out of sync with the sender, so we have
* to wait until the timeout timer gets into the
* action and close this connection.
*/
TRACE_MGMT_DBG("Aborted cmnd %p is %s, "
"keep waiting", cmnd,
(cmnd == conn->read_cmnd) ? "RX cmnd" :
"waiting for DATA OUT data");
goto cont;
}
if ((cmnd->r2t_len_to_receive != 0) &&
(time_after_eq(j, cmnd->write_start + ISCSI_TM_DATA_WAIT_TIMEOUT) ||
force)) {
spin_unlock(&conn->write_list_lock);
spin_unlock_bh(&conn->conn_thr_pool->rd_lock);
iscsi_fail_data_waiting_cmnd(cmnd);
goto again;
}
cont:
aborted_cmds_pending = true;
}
}
if (aborted_cmds_pending) {
if (!force &&
(!timer_pending(&conn->rsp_timer) ||
time_after(conn->rsp_timer.expires, timeout_time))) {
TRACE_MGMT_DBG("Mod timer on %ld (conn %p)",
timeout_time, conn);
mod_timer(&conn->rsp_timer, timeout_time);
}
} else {
TRACE_MGMT_DBG("Clearing conn_tm_active for conn %p", conn);
conn->conn_tm_active = 0;
}
spin_unlock(&conn->write_list_lock);
spin_unlock_bh(&conn->conn_thr_pool->rd_lock);
TRACE_EXIT();
return;
}
/* target_mutex supposed to be locked */
void conn_reinst_finished(struct iscsi_conn *conn)
{
struct iscsi_cmnd *cmnd, *t;
TRACE_ENTRY();
clear_bit(ISCSI_CONN_REINSTATING, &conn->conn_aflags);
list_for_each_entry_safe(cmnd, t, &conn->reinst_pending_cmd_list,
reinst_pending_cmd_list_entry) {
TRACE_MGMT_DBG("Restarting reinst pending cmnd %p",
cmnd);
list_del(&cmnd->reinst_pending_cmd_list_entry);
/* Restore the state for preliminary completion/cmnd_done() */
cmnd->scst_state = ISCSI_CMD_STATE_AFTER_PREPROC;
iscsi_restart_cmnd(cmnd);
}
TRACE_EXIT();
return;
}
int conn_activate(struct iscsi_conn *conn)
{
TRACE_MGMT_DBG("Enabling conn %p", conn);
/* Catch double bind */
sBUG_ON(conn->sock->sk->sk_state_change == iscsi_state_change);
write_lock_bh(&conn->sock->sk->sk_callback_lock);
conn->old_state_change = conn->sock->sk->sk_state_change;
conn->sock->sk->sk_state_change = iscsi_state_change;
conn->old_data_ready = conn->sock->sk->sk_data_ready;
conn->sock->sk->sk_data_ready = iscsi_data_ready;
conn->old_write_space = conn->sock->sk->sk_write_space;
conn->sock->sk->sk_write_space = iscsi_write_space_ready;
write_unlock_bh(&conn->sock->sk->sk_callback_lock);
/*
* Check, if conn was closed while we were initializing it.
* This function will make conn rd_active, if necessary.
*/
__iscsi_state_change(conn->sock->sk);
return 0;
}
/*
* Note: the code below passes a kernel space pointer (&opt) to setsockopt()
* while the declaration of setsockopt specifies that it expects a user space
* pointer. This seems to work fine, and this approach is also used in some
* other parts of the Linux kernel (see e.g. fs/ocfs2/cluster/tcp.c).
*/
static int conn_setup_sock(struct iscsi_conn *conn)
{
int res = 0;
int opt = 1;
mm_segment_t oldfs;
struct iscsi_session *session = conn->session;
TRACE_DBG("%llx", (long long unsigned int)session->sid);
conn->sock = SOCKET_I(conn->file->f_dentry->d_inode);
if (conn->sock->ops->sendpage == NULL) {
PRINT_ERROR("Socket for sid %llx doesn't support sendpage()",
(long long unsigned int)session->sid);
res = -EINVAL;
goto out;
}
#if 0
conn->sock->sk->sk_allocation = GFP_NOIO;
#endif
conn->sock->sk->sk_user_data = conn;
oldfs = get_fs();
set_fs(get_ds());
conn->sock->ops->setsockopt(conn->sock, SOL_TCP, TCP_NODELAY,
(void __force __user *)&opt, sizeof(opt));
set_fs(oldfs);
out:
return res;
}
void iscsi_tcp_conn_free(struct iscsi_conn *conn)
{
fput(conn->file);
conn->file = NULL;
conn->sock = NULL;
free_page((unsigned long)conn->read_iov);
kmem_cache_free(iscsi_conn_cache, conn);
}
/* target_mutex supposed to be locked */
void conn_free(struct iscsi_conn *conn)
{
struct iscsi_session *session = conn->session;
TRACE_ENTRY();
TRACE_MGMT_DBG("Freeing conn %p (sess=%p, %#Lx %u)", conn,
session, (long long unsigned int)session->sid, conn->cid);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&conn->target->target_mutex);
#endif
del_timer_sync(&conn->rsp_timer);
#ifndef CONFIG_SCST_PROC
conn_sysfs_del(conn);
#endif
sBUG_ON(atomic_read(&conn->conn_ref_cnt) != 0);
sBUG_ON(!list_empty(&conn->cmd_list));
sBUG_ON(!list_empty(&conn->write_list));
sBUG_ON(!list_empty(&conn->write_timeout_list));
sBUG_ON(conn->conn_reinst_successor != NULL);
sBUG_ON(!test_bit(ISCSI_CONN_SHUTTINGDOWN, &conn->conn_aflags));
/* Just in case if new conn gets freed before the old one */
if (test_bit(ISCSI_CONN_REINSTATING, &conn->conn_aflags)) {
struct iscsi_conn *c;
TRACE_MGMT_DBG("Freeing being reinstated conn %p", conn);
list_for_each_entry(c, &session->conn_list,
conn_list_entry) {
if (c->conn_reinst_successor == conn) {
c->conn_reinst_successor = NULL;
break;
}
}
}
list_del(&conn->conn_list_entry);
conn->transport->iscsit_conn_free(conn);
if (list_empty(&session->conn_list)) {
sBUG_ON(session->sess_reinst_successor != NULL);
session_free(session, true);
}
}
int iscsi_init_conn(struct iscsi_session *session,
struct iscsi_kern_conn_info *info,
struct iscsi_conn *conn)
{
int res;
atomic_set(&conn->conn_ref_cnt, 0);
conn->session = session;
if (session->sess_reinstating)
__set_bit(ISCSI_CONN_REINSTATING, &conn->conn_aflags);
conn->cid = info->cid;
conn->stat_sn = info->stat_sn;
conn->exp_stat_sn = info->exp_stat_sn;
conn->rd_state = ISCSI_CONN_RD_STATE_IDLE;
conn->wr_state = ISCSI_CONN_WR_STATE_IDLE;
conn->hdigest_type = session->sess_params.header_digest;
conn->ddigest_type = session->sess_params.data_digest;
res = digest_init(conn);
if (res != 0)
return res;
conn->target = session->target;
spin_lock_init(&conn->cmd_list_lock);
INIT_LIST_HEAD(&conn->cmd_list);
spin_lock_init(&conn->write_list_lock);
INIT_LIST_HEAD(&conn->write_list);
INIT_LIST_HEAD(&conn->write_timeout_list);
setup_timer(&conn->rsp_timer, conn_rsp_timer_fn, (unsigned long)conn);
init_waitqueue_head(&conn->read_state_waitQ);
init_completion(&conn->ready_to_free);
INIT_LIST_HEAD(&conn->reinst_pending_cmd_list);
INIT_LIST_HEAD(&conn->nop_req_list);
spin_lock_init(&conn->nop_req_list_lock);
conn->conn_thr_pool = session->sess_thr_pool;
conn->nop_in_ttt = 0;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20))
INIT_DELAYED_WORK(&conn->nop_in_delayed_work,
conn_nop_in_delayed_work_fn);
#else
INIT_WORK(&conn->nop_in_delayed_work, conn_nop_in_delayed_work_fn,
conn);
#endif
conn->last_rcv_time = jiffies;
conn->data_rsp_timeout = session->tgt_params.rsp_timeout * HZ;
conn->nop_in_interval = session->tgt_params.nop_in_interval * HZ;
conn->nop_in_timeout = session->tgt_params.nop_in_timeout * HZ;
if (conn->nop_in_interval > 0) {
TRACE_DBG("Schedule Nop-In work for conn %p", conn);
schedule_delayed_work(&conn->nop_in_delayed_work,
conn->nop_in_interval + ISCSI_ADD_SCHED_TIME);
}
return 0;
}
EXPORT_SYMBOL(iscsi_init_conn);
/* target_mutex supposed to be locked */
int iscsi_conn_alloc(struct iscsi_session *session,
struct iscsi_kern_conn_info *info, struct iscsi_conn **new_conn,
struct iscsit_transport *t)
{
struct iscsi_conn *conn;
int res = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&session->target->target_mutex);
#endif
conn = kmem_cache_zalloc(iscsi_conn_cache, GFP_KERNEL);
if (!conn) {
res = -ENOMEM;
goto out_err;
}
TRACE_MGMT_DBG("Creating connection %p for sid %#Lx, cid %u", conn,
(long long unsigned int)session->sid, info->cid);
conn->transport = t;
/* Changing it, change ISCSI_CONN_IOV_MAX as well !! */
conn->read_iov = (struct iovec *)get_zeroed_page(GFP_KERNEL);
if (conn->read_iov == NULL) {
res = -ENOMEM;
goto out_err_free_conn;
}
res = iscsi_init_conn(session, info, conn);
if (res != 0)
goto out_free_iov;
conn->file = fget(info->fd);
res = conn_setup_sock(conn);
if (res != 0)
goto out_fput;
#ifndef CONFIG_SCST_PROC
res = conn_sysfs_add(conn);
if (res != 0)
goto out_fput;
#endif
list_add_tail(&conn->conn_list_entry, &session->conn_list);
*new_conn = conn;
out:
return res;
out_fput:
fput(conn->file);
out_free_iov:
free_page((unsigned long)conn->read_iov);
out_err_free_conn:
kmem_cache_free(iscsi_conn_cache, conn);
out_err:
goto out;
}
/* target_mutex supposed to be locked */
int __add_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
{
struct iscsi_conn *conn, *new_conn = NULL;
int err;
bool reinstatement = false;
struct iscsit_transport *t;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&session->target->target_mutex);
#endif
conn = conn_lookup(session, info->cid);
if ((conn != NULL) &&
!test_bit(ISCSI_CONN_SHUTTINGDOWN, &conn->conn_aflags)) {
/* conn reinstatement */
reinstatement = true;
} else if (!list_empty(&session->conn_list)) {
err = -EEXIST;
goto out;
}
if (session->sess_params.rdma_extensions)
t = iscsit_get_transport(ISCSI_RDMA);
else
t = iscsit_get_transport(ISCSI_TCP);
if (!t) {
err = -ENOENT;
goto out;
}
err = t->iscsit_conn_alloc(session, info, &new_conn, t);
if (err != 0)
goto out;
if (reinstatement) {
TRACE_MGMT_DBG("Reinstating conn (old %p, new %p)", conn,
new_conn);
conn->conn_reinst_successor = new_conn;
__set_bit(ISCSI_CONN_REINSTATING, &new_conn->conn_aflags);
__mark_conn_closed(conn, 0);
}
err = t->iscsit_conn_activate(new_conn);
out:
return err;
}
/* target_mutex supposed to be locked */
int __del_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
{
struct iscsi_conn *conn;
int err = -EEXIST;
conn = conn_lookup(session, info->cid);
if (!conn) {
PRINT_WARNING("Connection %d not found", info->cid);
return err;
}
PRINT_INFO("Deleting connection with initiator %s (%p)",
conn->session->initiator_name, conn);
__mark_conn_closed(conn, ISCSI_CONN_ACTIVE_CLOSE|ISCSI_CONN_DELETING);
return 0;
}
#ifdef CONFIG_SCST_EXTRACHECKS
void iscsi_extracheck_is_rd_thread(struct iscsi_conn *conn)
{
if (unlikely(current != conn->rd_task)) {
pr_emerg("conn %p rd_task != current %p (pid %d)\n",
conn, current, current->pid);
while (in_softirq())
local_bh_enable();
pr_emerg("rd_state %x\n", conn->rd_state);
pr_emerg("rd_task %p\n", conn->rd_task);
pr_emerg("rd_task->pid %d\n", conn->rd_task->pid);
BUG();
}
}
void iscsi_extracheck_is_wr_thread(struct iscsi_conn *conn)
{
if (unlikely(current != conn->wr_task)) {
pr_emerg("conn %p wr_task != current %p (pid %d)\n",
conn, current, current->pid);
while (in_softirq())
local_bh_enable();
pr_emerg("wr_state %x\n", conn->wr_state);
pr_emerg("wr_task %p\n", conn->wr_task);
pr_emerg("wr_task->pid %d\n", conn->wr_task->pid);
BUG();
}
}
#endif /* CONFIG_SCST_EXTRACHECKS */