[KARMIC] UBUNTU: Upgrade iscsitarget source from 0.4.17 to 1.4.19
Manoj Iyer
manoj.iyer at canonical.com
Fri Dec 4 02:35:47 UTC 2009
upgraded iscsitarget code under kernel/ubuntu from v0.4.17 to v1.4.19, I
had to make changes to the Makefile and patch file-io.c (submitted patch
to iscsi target devel list) inorder to get it to build. I have a test
kernel in http://people.canonical.com/~manjo/lucid/iscsi/
The following changes since commit
764f8a95fa7a713916e7bf9520f2315676a6998d:
Manoj Iyer (1):
UBUNTU: Upgrade iscsitarget source from 0.4.17 to 1.4.19
are available in the git repository at:
ssh://zinc.canonical.com/srv/kernel.ubuntu.com/git/manjo/ubuntu-lucid.git
iscsitarget
>From 764f8a95fa7a713916e7bf9520f2315676a6998d Mon Sep 17 00:00:00 2001
From: Manoj Iyer <manoj.iyer at canonical.com>
Date: Thu, 3 Dec 2009 19:54:20 -0600
Subject: [PATCH] UBUNTU: Upgrade iscsitarget source from 0.4.17 to 1.4.19
ExternalDriver: iscsitarget
Description: iSCSI storage system on Linux
Url: svn://svn.berlios.de/iscsitarget/trunk
Mask:
Version: 1.4.19
Signed-off-by: Manoj Iyer <manoj.iyer at canonical.com>
---
ubuntu/Makefile | 2 +-
ubuntu/iscsitarget/Makefile | 2 +-
ubuntu/iscsitarget/config.c | 8 +
ubuntu/iscsitarget/conn.c | 11 ++
ubuntu/iscsitarget/file-io.c | 2 +-
ubuntu/iscsitarget/include/iet_u.h | 12 ++-
ubuntu/iscsitarget/iscsi.c | 326 +++++++++++++++++++++++++-----------
ubuntu/iscsitarget/iscsi.h | 64 +++++++-
ubuntu/iscsitarget/iscsi_dbg.h | 7 +
ubuntu/iscsitarget/iscsi_hdr.h | 4 +-
ubuntu/iscsitarget/nthread.c | 67 +++++++-
ubuntu/iscsitarget/param.c | 11 +-
ubuntu/iscsitarget/session.c | 56 ++++++-
ubuntu/iscsitarget/target.c | 61 +++++--
ubuntu/iscsitarget/target_disk.c | 165 +++++++++++++-----
ubuntu/iscsitarget/ua.c | 164 ++++++++++++++++++
16 files changed, 780 insertions(+), 182 deletions(-)
create mode 100644 ubuntu/iscsitarget/ua.c
diff --git a/ubuntu/Makefile b/ubuntu/Makefile
index 60242f0..a51cb77 100644
--- a/ubuntu/Makefile
+++ b/ubuntu/Makefile
@@ -8,7 +8,7 @@ obj-$(CONFIG_AUFS_FS) += aufs/
obj-$(CONFIG_BLK_DEV_COMPCACHE) += compcache/
obj-$(CONFIG_DM_RAID45) += dm-raid4-5/
#obj-$(CONFIG_BLK_DEV_DRBD) += drbd/
-#obj-$(CONFIG_SCSI_ISCSITARGET) += iscsitarget/
+obj-$(CONFIG_SCSI_ISCSITARGET) += iscsitarget/
obj-$(CONFIG_LENOVO_SL_LAPTOP) += lenovo-sl-laptop/
obj-$(CONFIG_LIRC_DEV) += lirc/
obj-m += misc/
diff --git a/ubuntu/iscsitarget/Makefile b/ubuntu/iscsitarget/Makefile
index b54a26b..727c706 100644
--- a/ubuntu/iscsitarget/Makefile
+++ b/ubuntu/iscsitarget/Makefile
@@ -13,5 +13,5 @@ obj-m += iscsi_trgt.o
iscsi_trgt-objs := tio.o iscsi.o nthread.o wthread.o config.o digest.o \
conn.o session.o target.o volume.o iotype.o \
file-io.o null-io.o target_disk.o event.o param.o \
- block-io.o
+ block-io.o ua.o
diff --git a/ubuntu/iscsitarget/config.c b/ubuntu/iscsitarget/config.c
index 71fc6d5..51331fb 100644
--- a/ubuntu/iscsitarget/config.c
+++ b/ubuntu/iscsitarget/config.c
@@ -307,8 +307,16 @@ done:
return err;
}
+static int release(struct inode *i __attribute__((unused)),
+ struct file *f __attribute__((unused)))
+{
+ target_del_all();
+ return 0;
+}
+
struct file_operations ctr_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = ioctl,
.compat_ioctl = ioctl,
+ .release = release
};
diff --git a/ubuntu/iscsitarget/conn.c b/ubuntu/iscsitarget/conn.c
index 360a7a0..2c89304 100644
--- a/ubuntu/iscsitarget/conn.c
+++ b/ubuntu/iscsitarget/conn.c
@@ -158,6 +158,7 @@ int conn_free(struct iscsi_conn *conn)
list_del(&conn->list);
list_del(&conn->poll_list);
+ del_timer_sync(&conn->nop_timer);
digest_cleanup(conn);
kfree(conn);
@@ -192,6 +193,7 @@ static int iet_conn_alloc(struct iscsi_session *session, struct conn_info *info)
INIT_LIST_HEAD(&conn->pdu_list);
INIT_LIST_HEAD(&conn->write_list);
INIT_LIST_HEAD(&conn->poll_list);
+ init_timer(&conn->nop_timer);
list_add(&conn->list, &session->conn_list);
@@ -240,3 +242,12 @@ int conn_del(struct iscsi_session *session, struct conn_info *info)
return 0;
}
+
+/* target_lock() supposed to be held */
+void conn_del_all(struct iscsi_session *session)
+{
+ struct iscsi_conn *conn;
+
+ list_for_each_entry(conn, &session->conn_list, list)
+ conn_close(conn);
+}
diff --git a/ubuntu/iscsitarget/file-io.c b/ubuntu/iscsitarget/file-io.c
index dbf7b1c..4eb2025 100644
--- a/ubuntu/iscsitarget/file-io.c
+++ b/ubuntu/iscsitarget/file-io.c
@@ -88,7 +88,7 @@ static int fileio_sync(struct iet_volume *lu, struct tio *tio)
count = lu->blk_cnt << lu->blk_shift;
}
- res = sync_page_range(inode, mapping, ppos, count);
+ res = generic_write_sync(p->filp, ppos, count);
if (res) {
eprintk("I/O error: syncing pages failed: %d\n", res);
return -EIO;
diff --git a/ubuntu/iscsitarget/include/iet_u.h b/ubuntu/iscsitarget/include/iet_u.h
index 9730b10..620b3c4 100644
--- a/ubuntu/iscsitarget/include/iet_u.h
+++ b/ubuntu/iscsitarget/include/iet_u.h
@@ -1,7 +1,7 @@
#ifndef _IET_U_H
#define _IET_U_H
-#define IET_VERSION_STRING "0.4.17"
+#define IET_VERSION_STRING "1.4.19"
/* The maximum length of 223 bytes in the RFC. */
#define ISCSI_NAME_LEN 256
@@ -81,6 +81,8 @@ enum {
key_wthreads,
key_target_type,
key_queued_cmnds,
+ key_nop_interval,
+ key_nop_timeout,
target_key_last,
};
@@ -119,6 +121,14 @@ struct iet_event {
#define MIN_NR_QUEUED_CMNDS 1
#define MAX_NR_QUEUED_CMNDS 256
+#define DEFAULT_NOP_INTERVAL 0
+#define MIN_NOP_INTERVAL 0
+#define MAX_NOP_INTERVAL 90
+
+#define DEFAULT_NOP_TIMEOUT 0
+#define MIN_NOP_TIMEOUT 0
+#define MAX_NOP_TIMEOUT 90
+
#define NETLINK_IET 21
#define ADD_TARGET _IOW('i', 0, struct target_info)
diff --git a/ubuntu/iscsitarget/iscsi.c b/ubuntu/iscsitarget/iscsi.c
index 79e73b0..4f62b8b 100644
--- a/ubuntu/iscsitarget/iscsi.c
+++ b/ubuntu/iscsitarget/iscsi.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman at ardistech.com>
+ * Copyright (C) 2008 Arne Redlich <agr at powerkom-dd.de>
*
* Released under the terms of the GNU GPL v2.0.
*/
@@ -139,7 +140,7 @@ static void iscsi_scsi_dequeuecmnd(struct iscsi_cmnd *cmnd)
/**
* create a new command.
*
- * iscsi_cmnd_create -
+ * iscsi_cmnd_create -
* @conn: ptr to connection (for i/o)
*
* @return ptr to command or NULL
@@ -173,7 +174,7 @@ struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *conn, int req)
/**
* create a new command used as response.
*
- * iscsi_cmnd_create_rsp_cmnd -
+ * iscsi_cmnd_create_rsp_cmnd -
* @cmnd: ptr to request command
*
* @return ptr to response command or NULL
@@ -302,6 +303,7 @@ static struct iscsi_cmnd *create_scsi_rsp(struct iscsi_cmnd *req)
struct iscsi_cmnd *rsp;
struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req);
struct iscsi_scsi_rsp_hdr *rsp_hdr;
+ struct iscsi_sense_data *sense;
rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
@@ -309,99 +311,93 @@ static struct iscsi_cmnd *create_scsi_rsp(struct iscsi_cmnd *req)
rsp_hdr->opcode = ISCSI_OP_SCSI_RSP;
rsp_hdr->flags = ISCSI_FLG_FINAL;
rsp_hdr->response = ISCSI_RESPONSE_COMMAND_COMPLETED;
- rsp_hdr->cmd_status = SAM_STAT_GOOD;
+ rsp_hdr->cmd_status = req->status;
rsp_hdr->itt = req_hdr->itt;
+ if (req->status == SAM_STAT_CHECK_CONDITION) {
+ assert(!rsp->tio);
+ rsp->tio = tio_alloc(1);
+ sense = (struct iscsi_sense_data *)
+ page_address(rsp->tio->pvec[0]);
+
+ assert(sense);
+ clear_page(sense);
+ sense->length = cpu_to_be16(IET_SENSE_BUF_SIZE);
+
+ memcpy(sense->data, req->sense_buf, IET_SENSE_BUF_SIZE);
+ rsp->pdu.datasize = sizeof(struct iscsi_sense_data) +
+ IET_SENSE_BUF_SIZE;
+
+ rsp->tio->size = (rsp->pdu.datasize + 3) & -4;
+ rsp->tio->offset = 0;
+ }
+
return rsp;
}
-static struct iscsi_cmnd *create_sense_rsp(struct iscsi_cmnd *req,
- u8 sense_key, u8 asc, u8 ascq)
+void iscsi_cmnd_set_sense(struct iscsi_cmnd *cmnd, u8 sense_key, u8 asc,
+ u8 ascq)
{
- struct iscsi_cmnd *rsp;
- struct iscsi_scsi_rsp_hdr *rsp_hdr;
- struct tio *tio;
- struct iscsi_sense_data *sense;
+ cmnd->status = SAM_STAT_CHECK_CONDITION;
- rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
-
- rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs;
- rsp_hdr->opcode = ISCSI_OP_SCSI_RSP;
- rsp_hdr->flags = ISCSI_FLG_FINAL;
- rsp_hdr->response = ISCSI_RESPONSE_COMMAND_COMPLETED;
- rsp_hdr->cmd_status = SAM_STAT_CHECK_CONDITION;
- rsp_hdr->itt = cmnd_hdr(req)->itt;
-
- tio = rsp->tio = tio_alloc(1);
- sense = (struct iscsi_sense_data *) page_address(tio->pvec[0]);
- assert(sense);
- clear_page(sense);
- sense->length = cpu_to_be16(14);
- sense->data[0] = 0xf0;
- sense->data[2] = sense_key;
- sense->data[7] = 6; // Additional sense length
- sense->data[12] = asc;
- sense->data[13] = ascq;
-
- rsp->pdu.datasize = sizeof(struct iscsi_sense_data) + 14;
- tio->size = (rsp->pdu.datasize + 3) & -4;
- tio->offset = 0;
+ cmnd->sense_buf[0] = 0xf0;
+ cmnd->sense_buf[2] = sense_key;
+ cmnd->sense_buf[7] = 6; // Additional sense length
+ cmnd->sense_buf[12] = asc;
+ cmnd->sense_buf[13] = ascq;
+}
- return rsp;
+static struct iscsi_cmnd *create_sense_rsp(struct iscsi_cmnd *req,
+ u8 sense_key, u8 asc, u8 ascq)
+{
+ iscsi_cmnd_set_sense(req, sense_key, asc, ascq);
+ return create_scsi_rsp(req);
}
-void send_scsi_rsp(struct iscsi_cmnd *req, int (*func)(struct iscsi_cmnd *))
+void send_scsi_rsp(struct iscsi_cmnd *req, void (*func)(struct iscsi_cmnd *))
{
struct iscsi_cmnd *rsp;
struct iscsi_scsi_rsp_hdr *rsp_hdr;
u32 size;
- int ret = func(req);
- switch (ret) {
- case 0:
- case -EBUSY:
- rsp = create_scsi_rsp(req);
+ func(req);
+ rsp = create_scsi_rsp(req);
+
+ switch (req->status) {
+ case SAM_STAT_GOOD:
+ case SAM_STAT_RESERVATION_CONFLICT:
rsp_hdr = (struct iscsi_scsi_rsp_hdr *) &rsp->pdu.bhs;
if ((size = cmnd_read_size(req)) != 0) {
rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
rsp_hdr->residual_count = cpu_to_be32(size);
}
- if (ret == -EBUSY)
- rsp_hdr->cmd_status = SAM_STAT_RESERVATION_CONFLICT;
- break;
- case -EIO:
- /* Medium Error/Write Fault */
- rsp = create_sense_rsp(req, MEDIUM_ERROR, 0x03, 0x0);
break;
default:
- rsp = create_sense_rsp(req, ILLEGAL_REQUEST, 0x24, 0x0);
+ break;
}
+
iscsi_cmnd_init_write(rsp);
}
-void send_data_rsp(struct iscsi_cmnd *req, int (*func)(struct iscsi_cmnd *))
+void send_data_rsp(struct iscsi_cmnd *req, void (*func)(struct iscsi_cmnd *))
{
struct iscsi_cmnd *rsp;
- switch (func(req)) {
- case 0:
+ func(req);
+
+ if (req->status == SAM_STAT_GOOD)
do_send_data_rsp(req);
- return;
- case -EIO:
- /* Medium Error/Unrecovered Read Error */
- rsp = create_sense_rsp(req, MEDIUM_ERROR, 0x11, 0x0);
- break;
- default:
- rsp = create_sense_rsp(req, ILLEGAL_REQUEST, 0x24, 0x0);
+ else {
+ rsp = create_scsi_rsp(req);
+ iscsi_cmnd_init_write(rsp);
}
- iscsi_cmnd_init_write(rsp);
}
/**
* Free a command.
* Also frees the additional header.
*
- * iscsi_cmnd_remove -
+ * iscsi_cmnd_remove -
* @cmnd: ptr to command
*/
@@ -411,6 +407,12 @@ static void iscsi_cmnd_remove(struct iscsi_cmnd *cmnd)
if (!cmnd)
return;
+
+ if (cmnd_timer_active(cmnd)) {
+ clear_cmnd_timer_active(cmnd);
+ del_timer_sync(&cmnd->timer);
+ }
+
dprintk(D_GENERIC, "%p\n", cmnd);
conn = cmnd->conn;
kfree(cmnd->pdu.ahs);
@@ -570,7 +572,7 @@ static struct iscsi_cmnd *cmnd_find_hash(struct iscsi_session *session, u32 itt,
return cmnd;
}
-static int cmnd_insert_hash(struct iscsi_cmnd *cmnd)
+static int cmnd_insert_hash_ttt(struct iscsi_cmnd *cmnd, u32 ttt)
{
struct iscsi_session *session = cmnd->conn->session;
struct iscsi_cmnd *tmp;
@@ -578,17 +580,11 @@ static int cmnd_insert_hash(struct iscsi_cmnd *cmnd)
int err = 0;
u32 itt = cmnd->pdu.bhs.itt;
- dprintk(D_GENERIC, "%p:%x\n", cmnd, itt);
- if (itt == ISCSI_RESERVED_TAG) {
- err = -ISCSI_REASON_PROTOCOL_ERROR;
- goto out;
- }
-
- head = &session->cmnd_hash[cmnd_hashfn(cmnd->pdu.bhs.itt)];
+ head = &session->cmnd_hash[cmnd_hashfn(itt)];
spin_lock(&session->cmnd_hash_lock);
- tmp = __cmnd_find_hash(session, itt, ISCSI_RESERVED_TAG);
+ tmp = __cmnd_find_hash(session, itt, ttt);
if (!tmp) {
list_add_tail(&cmnd->hash_list, head);
set_cmnd_hashed(cmnd);
@@ -597,12 +593,24 @@ static int cmnd_insert_hash(struct iscsi_cmnd *cmnd)
spin_unlock(&session->cmnd_hash_lock);
+ return err;
+}
+
+static int cmnd_insert_hash(struct iscsi_cmnd *cmnd)
+{
+ int err;
+
+ dprintk(D_GENERIC, "%p:%x\n", cmnd, cmnd->pdu.bhs.itt);
+
+ if (cmnd->pdu.bhs.itt == ISCSI_RESERVED_TAG)
+ return -ISCSI_REASON_PROTOCOL_ERROR;
+
+ err = cmnd_insert_hash_ttt(cmnd, ISCSI_RESERVED_TAG);
if (!err) {
update_stat_sn(cmnd);
err = check_cmd_sn(cmnd);
}
-out:
return err;
}
@@ -618,8 +626,8 @@ static void cmnd_remove_hash(struct iscsi_cmnd *cmnd)
spin_lock(&session->cmnd_hash_lock);
- tmp = __cmnd_find_hash(session, cmnd->pdu.bhs.itt, ISCSI_RESERVED_TAG);
-
+ tmp = __cmnd_find_hash(session, cmnd->pdu.bhs.itt,
+ cmnd->target_task_tag);
if (tmp && tmp == cmnd)
__cmnd_remove_hash(tmp);
else
@@ -830,24 +838,34 @@ static void scsi_cmnd_exec(struct iscsi_cmnd *cmnd)
}
}
-static int noop_out_start(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
+static int nop_out_start(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
{
u32 size, tmp;
int i, err = 0;
if (cmnd_ttt(cmnd) != cpu_to_be32(ISCSI_RESERVED_TAG)) {
- /*
- * We don't request a NOP-Out by sending a NOP-In.
- * See 10.18.2 in the draft 20.
- */
- eprintk("initiator bug %x\n", cmnd_itt(cmnd));
- err = -ISCSI_REASON_PROTOCOL_ERROR;
- goto out;
+ cmnd->req = cmnd_find_hash(conn->session, cmnd->pdu.bhs.itt,
+ cmnd->pdu.bhs.ttt);
+ if (!cmnd->req) {
+ /*
+ * We didn't request this NOP-Out (by sending a
+ * NOP-In, see 10.18.2 of the RFC) or our fake NOP-Out
+ * timed out.
+ */
+ eprintk("initiator bug %x\n", cmnd_itt(cmnd));
+ err = -ISCSI_REASON_PROTOCOL_ERROR;
+ goto out;
+ }
+
+ del_timer_sync(&cmnd->req->timer);
+ clear_cmnd_timer_active(cmnd->req);
+ dprintk(D_GENERIC, "NOP-Out: %p, ttt %x, timer %p\n",
+ cmnd->req, cmnd_ttt(cmnd->req), &cmnd->req->timer);
}
if (cmnd_itt(cmnd) == cpu_to_be32(ISCSI_RESERVED_TAG)) {
if (!(cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE))
- eprintk("%s\n","initiator bug!");
+ eprintk("%s\n", "initiator bug!");
update_stat_sn(cmnd);
err = check_cmd_sn(cmnd);
if (err)
@@ -1159,7 +1177,7 @@ static int target_reset(struct iscsi_cmnd *req, u32 lun, int all)
struct iscsi_session *session;
struct iscsi_conn *conn;
struct iscsi_cmnd *cmnd, *tmp;
- struct iet_volume *volumes;
+ struct iet_volume *volume;
list_for_each_entry(session, &target->session_list, list) {
list_for_each_entry(conn, &session->conn_list, list) {
@@ -1175,10 +1193,15 @@ static int target_reset(struct iscsi_cmnd *req, u32 lun, int all)
}
}
- list_for_each_entry(volumes, &target->volumes, list)
- if (all || volumes->lun == lun)
+ list_for_each_entry(volume, &target->volumes, list) {
+ if (all || volume->lun == lun) {
/* force release */
- volume_release(volumes, 0, 1);
+ volume_release(volume, 0, 1);
+ /* power-on, reset, or bus device reset occurred */
+ ua_establish_for_all_sessions(target, volume->lun,
+ 0x29, 0x0);
+ }
+ }
return 0;
}
@@ -1211,7 +1234,7 @@ static inline char *tmf_desc(int fun)
"Task Reassign",
};
- if ((fun < ISCSI_FUNCTION_ABORT_TASK) ||
+ if ((fun < ISCSI_FUNCTION_ABORT_TASK) ||
(fun > ISCSI_FUNCTION_TASK_REASSIGN))
fun = 0;
@@ -1313,19 +1336,24 @@ out:
iscsi_cmnd_init_write(rsp);
}
-static void noop_out_exec(struct iscsi_cmnd *req)
+static void nop_hdr_setup(struct iscsi_hdr *hdr, u8 opcode, __be32 itt,
+ __be32 ttt)
+{
+ hdr->opcode = opcode;
+ hdr->flags = ISCSI_FLG_FINAL;
+ hdr->itt = itt;
+ hdr->ttt = ttt;
+}
+
+static void nop_out_exec(struct iscsi_cmnd *req)
{
struct iscsi_cmnd *rsp;
- struct iscsi_nop_in_hdr *rsp_hdr;
if (cmnd_itt(req) != cpu_to_be32(ISCSI_RESERVED_TAG)) {
rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
- rsp_hdr = (struct iscsi_nop_in_hdr *)&rsp->pdu.bhs;
- rsp_hdr->opcode = ISCSI_OP_NOOP_IN;
- rsp_hdr->flags = ISCSI_FLG_FINAL;
- rsp_hdr->itt = req->pdu.bhs.itt;
- rsp_hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
+ nop_hdr_setup(&rsp->pdu.bhs, ISCSI_OP_NOP_IN, req->pdu.bhs.itt,
+ cpu_to_be32(ISCSI_RESERVED_TAG));
if (req->pdu.datasize)
assert(req->tio);
@@ -1340,8 +1368,86 @@ static void noop_out_exec(struct iscsi_cmnd *req)
assert(get_pgcnt(req->pdu.datasize, 0) < ISCSI_CONN_IOV_MAX);
rsp->pdu.datasize = req->pdu.datasize;
iscsi_cmnd_init_write(rsp);
- } else
+ } else {
+ if (req->req) {
+ dprintk(D_GENERIC, "releasing NOP-Out %p, ttt %x; "
+ "removing NOP-In %p, ttt %x\n", req->req,
+ cmnd_ttt(req->req), req, cmnd_ttt(req));
+ cmnd_release(req->req, 0);
+ }
iscsi_cmnd_remove(req);
+ }
+}
+
+static void nop_in_timeout(unsigned long data)
+{
+ struct iscsi_cmnd *req = (struct iscsi_cmnd *)data;
+
+ printk(KERN_INFO "NOP-In ping timed out - closing sid:cid %llu:%u\n",
+ req->conn->session->sid, req->conn->cid);
+ clear_cmnd_timer_active(req);
+ conn_close(req->conn);
+}
+
+/* create a fake NOP-Out req and treat the NOP-In as our rsp to it */
+void send_nop_in(struct iscsi_conn *conn)
+{
+ struct iscsi_cmnd *req = cmnd_alloc(conn, 1);
+ struct iscsi_cmnd *rsp = iscsi_cmnd_create_rsp_cmnd(req, 0);
+
+ req->target_task_tag = get_next_ttt(conn->session);
+
+
+ nop_hdr_setup(&req->pdu.bhs, ISCSI_OP_NOP_OUT,
+ cpu_to_be32(ISCSI_RESERVED_TAG), req->target_task_tag);
+ nop_hdr_setup(&rsp->pdu.bhs, ISCSI_OP_NOP_IN,
+ cpu_to_be32(ISCSI_RESERVED_TAG), req->target_task_tag);
+
+ dprintk(D_GENERIC, "NOP-Out: %p, ttt %x, timer %p; "
+ "NOP-In: %p, ttt %x;\n", req, cmnd_ttt(req), &req->timer, rsp,
+ cmnd_ttt(rsp));
+
+ init_timer(&req->timer);
+ req->timer.data = (unsigned long)req;
+ req->timer.function = nop_in_timeout;
+
+ if (cmnd_insert_hash_ttt(req, req->target_task_tag)) {
+ eprintk("%s\n",
+ "failed to insert fake NOP-Out into hash table");
+ cmnd_release(rsp, 0);
+ cmnd_release(req, 0);
+ } else
+ iscsi_cmnd_init_write(rsp);
+}
+
+static void nop_in_tx_end(struct iscsi_cmnd *cmnd)
+{
+ struct iscsi_conn *conn = cmnd->conn;
+ u32 t;
+
+ if (cmnd->pdu.bhs.ttt == cpu_to_be32(ISCSI_RESERVED_TAG))
+ return;
+
+ /*
+ * NOP-In ping issued by the target.
+ * FIXME: Sanitize the NOP timeout earlier, during configuration
+ */
+ t = conn->session->target->trgt_param.nop_timeout;
+
+ if (!t || t > conn->session->target->trgt_param.nop_interval) {
+ eprintk("Adjusting NOPTimeout of tid %u from %u to %u "
+ "(== NOPInterval)\n", conn->session->target->tid,
+ t,
+ conn->session->target->trgt_param.nop_interval);
+ t = conn->session->target->trgt_param.nop_interval;
+ conn->session->target->trgt_param.nop_timeout = t;
+ }
+
+ dprintk(D_GENERIC, "NOP-In %p, %x: timer %p\n", cmnd, cmnd_ttt(cmnd),
+ &cmnd->req->timer);
+
+ set_cmnd_timer_active(cmnd->req);
+ mod_timer(&cmnd->req->timer, jiffies + HZ * t);
}
static void logout_exec(struct iscsi_cmnd *req)
@@ -1365,8 +1471,8 @@ static void iscsi_cmnd_exec(struct iscsi_cmnd *cmnd)
dprintk(D_GENERIC, "%p,%x,%u\n", cmnd, cmnd_opcode(cmnd), cmnd->pdu.bhs.sn);
switch (cmnd_opcode(cmnd)) {
- case ISCSI_OP_NOOP_OUT:
- noop_out_exec(cmnd);
+ case ISCSI_OP_NOP_OUT:
+ nop_out_exec(cmnd);
break;
case ISCSI_OP_SCSI_CMD:
scsi_cmnd_exec(cmnd);
@@ -1491,8 +1597,15 @@ void cmnd_tx_start(struct iscsi_cmnd *cmnd)
conn->write_size = sizeof(cmnd->pdu.bhs);
switch (cmnd_opcode(cmnd)) {
- case ISCSI_OP_NOOP_IN:
- cmnd_set_sn(cmnd, 1);
+ case ISCSI_OP_NOP_IN:
+ if (cmnd->pdu.bhs.itt == ISCSI_RESERVED_TAG) {
+ /* NOP-In ping generated by us. Don't advance StatSN. */
+ cmnd_set_sn(cmnd, 0);
+ cmnd_set_sn(cmnd->req, 0);
+ cmnd->pdu.bhs.sn = cpu_to_be32(conn->stat_sn);
+ cmnd->req->pdu.bhs.sn = cpu_to_be32(conn->stat_sn);
+ } else
+ cmnd_set_sn(cmnd, 1);
cmnd_send_pdu(conn, cmnd);
break;
case ISCSI_OP_SCSI_RSP:
@@ -1547,7 +1660,9 @@ void cmnd_tx_end(struct iscsi_cmnd *cmnd)
dprintk(D_GENERIC, "%p:%x\n", cmnd, cmnd_opcode(cmnd));
switch (cmnd_opcode(cmnd)) {
- case ISCSI_OP_NOOP_IN:
+ case ISCSI_OP_NOP_IN:
+ nop_in_tx_end(cmnd);
+ break;
case ISCSI_OP_SCSI_RSP:
case ISCSI_OP_SCSI_TASK_MGT_RSP:
case ISCSI_OP_TEXT_RSP:
@@ -1575,7 +1690,7 @@ void cmnd_tx_end(struct iscsi_cmnd *cmnd)
* This functions reorders the commands.
* Called from the read thread.
*
- * iscsi_session_push_cmnd -
+ * iscsi_session_push_cmnd -
* @cmnd: ptr to command
*/
@@ -1662,8 +1777,8 @@ void cmnd_rx_start(struct iscsi_cmnd *cmnd)
return;
switch (cmnd_opcode(cmnd)) {
- case ISCSI_OP_NOOP_OUT:
- err = noop_out_start(conn, cmnd);
+ case ISCSI_OP_NOP_OUT:
+ err = nop_out_start(conn, cmnd);
break;
case ISCSI_OP_SCSI_CMD:
if (!(err = cmnd_insert_hash(cmnd)))
@@ -1705,7 +1820,7 @@ void cmnd_rx_end(struct iscsi_cmnd *cmnd)
dprintk(D_GENERIC, "%p:%x\n", cmnd, cmnd_opcode(cmnd));
switch (cmnd_opcode(cmnd)) {
case ISCSI_OP_SCSI_REJECT:
- case ISCSI_OP_NOOP_OUT:
+ case ISCSI_OP_NOP_OUT:
case ISCSI_OP_SCSI_CMD:
case ISCSI_OP_SCSI_TASK_MGT_MSG:
case ISCSI_OP_TEXT_CMD:
@@ -1744,6 +1859,8 @@ static void iscsi_exit(void)
iotype_exit();
+ ua_exit();
+
if (iscsi_cmnd_cache)
kmem_cache_destroy(iscsi_cmnd_cache);
}
@@ -1769,6 +1886,10 @@ static int iscsi_init(void)
if (!iscsi_cmnd_cache)
goto err;
+ err = ua_init();
+ if (err < 0)
+ goto err;
+
if ((err = tio_init()) < 0)
goto err;
@@ -1797,4 +1918,7 @@ MODULE_PARM_DESC(debug_enable_flags,
module_init(iscsi_init);
module_exit(iscsi_exit);
+MODULE_VERSION(IET_VERSION_STRING);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("iSCSI Enterprise Target");
+MODULE_AUTHOR("IET development team <iscsitarget-devel at lists.sourceforge.net>");
diff --git a/ubuntu/iscsitarget/iscsi.h b/ubuntu/iscsitarget/iscsi.h
index 89ff2c3..92ce252 100644
--- a/ubuntu/iscsitarget/iscsi.h
+++ b/ubuntu/iscsitarget/iscsi.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman at ardistech.com>
+ * Copyright (C) 2008 Arne Redlich <agr at powerkom-dd.de>
*
* Released under the terms of the GNU GPL v2.0.
*/
@@ -7,6 +8,7 @@
#ifndef __ISCSI_H__
#define __ISCSI_H__
+#include <linux/completion.h>
#include <linux/pagemap.h>
#include <linux/seq_file.h>
#include <linux/mm.h>
@@ -17,6 +19,8 @@
#include "iscsi_hdr.h"
#include "iet_u.h"
+#define IET_SENSE_BUF_SIZE 18
+
struct iscsi_sess_param {
int initial_r2t;
int immediate_data;
@@ -43,6 +47,8 @@ struct iscsi_trgt_param {
int wthreads;
int target_type;
int queued_cmnds;
+ int nop_interval;
+ int nop_timeout;
};
struct tio {
@@ -113,11 +119,15 @@ struct iscsi_target {
struct list_head volumes;
struct list_head session_list;
+ /* Prevents races between add/del session and adding UAs */
+ spinlock_t session_list_lock;
+
struct network_thread_info nthread_info;
/* Points either to own list or global pool */
struct worker_thread_info * wthread_info;
struct semaphore target_sem;
+ struct completion *done;
};
struct iscsi_queue {
@@ -173,10 +183,12 @@ enum lu_flags {
#define IET_HASH_ORDER 8
#define cmnd_hashfn(itt) hash_long((itt), IET_HASH_ORDER)
+#define UA_HASH_LEN 8
+
struct iscsi_session {
struct list_head list;
struct iscsi_target *target;
-
+ struct completion *done;
char *initiator;
u64 sid;
@@ -193,6 +205,9 @@ struct iscsi_session {
spinlock_t cmnd_hash_lock;
struct list_head cmnd_hash[1 << IET_HASH_ORDER];
+ spinlock_t ua_hash_lock;
+ struct list_head ua_hash[UA_HASH_LEN];
+
u32 next_ttt;
};
@@ -200,6 +215,7 @@ enum connection_state_bit {
CONN_ACTIVE,
CONN_CLOSING,
CONN_WSPACE_WAIT,
+ CONN_NEED_NOP_IN,
};
#define ISCSI_CONN_IOV_MAX (((256 << 10) >> PAGE_SHIFT) + 1)
@@ -226,6 +242,7 @@ struct iscsi_conn {
atomic_t nr_busy_cmnds;
struct list_head pdu_list; /* in/outcoming pdus */
struct list_head write_list; /* list of data pdus to be sent */
+ struct timer_list nop_timer;
struct iscsi_cmnd *read_cmnd;
struct msghdr read_msg;
@@ -270,6 +287,10 @@ struct iscsi_cmnd {
struct tio *tio;
+ u8 status;
+
+ struct timer_list timer;
+
u32 r2t_sn;
u32 r2t_length;
u32 is_unsolicited_data;
@@ -280,6 +301,16 @@ struct iscsi_cmnd {
u32 ddigest;
struct iscsi_cmnd *req;
+
+ unsigned char sense_buf[IET_SENSE_BUF_SIZE];
+};
+
+struct ua_entry {
+ struct list_head entry;
+ struct iscsi_session *session; /* only used for debugging ATM */
+ u32 lun;
+ u8 asc;
+ u8 ascq;
};
#define ISCSI_OP_SCSI_REJECT ISCSI_OP_VENDOR1_CMD
@@ -295,13 +326,17 @@ extern void cmnd_rx_end(struct iscsi_cmnd *);
extern void cmnd_tx_start(struct iscsi_cmnd *);
extern void cmnd_tx_end(struct iscsi_cmnd *);
extern void cmnd_release(struct iscsi_cmnd *, int);
-extern void send_data_rsp(struct iscsi_cmnd *, int (*)(struct iscsi_cmnd *));
-extern void send_scsi_rsp(struct iscsi_cmnd *, int (*)(struct iscsi_cmnd *));
+extern void send_data_rsp(struct iscsi_cmnd *, void (*)(struct iscsi_cmnd *));
+extern void send_scsi_rsp(struct iscsi_cmnd *, void (*)(struct iscsi_cmnd *));
+extern void iscsi_cmnd_set_sense(struct iscsi_cmnd *, u8 sense_key, u8 asc,
+ u8 ascq);
+extern void send_nop_in(struct iscsi_conn *);
/* conn.c */
extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16);
extern int conn_add(struct iscsi_session *, struct conn_info *);
extern int conn_del(struct iscsi_session *, struct conn_info *);
+extern void conn_del_all(struct iscsi_session *);
extern int conn_free(struct iscsi_conn *);
extern void conn_close(struct iscsi_conn *);
extern void conn_info_show(struct seq_file *, struct iscsi_session *);
@@ -329,6 +364,7 @@ extern void target_unlock(struct iscsi_target *);
struct iscsi_target *target_lookup_by_id(u32);
extern int target_add(struct target_info *);
extern int target_del(u32 id);
+extern void target_del_all(void);
extern struct seq_operations iet_seq_op;
/* config.c */
@@ -341,6 +377,7 @@ extern struct file_operations session_seq_fops;
extern struct iscsi_session *session_lookup(struct iscsi_target *, u64);
extern int session_add(struct iscsi_target *, struct session_info *);
extern int session_del(struct iscsi_target *, u64);
+extern void session_del_all(struct iscsi_target *);
/* volume.c */
extern struct file_operations volume_seq_fops;
@@ -380,6 +417,21 @@ extern int event_send(u32, u64, u32, u32, int);
extern int event_init(void);
extern void event_exit(void);
+/* ua.c */
+int ua_init(void);
+void ua_exit(void);
+struct ua_entry * ua_get_first(struct iscsi_session *, u32 lun);
+struct ua_entry * ua_get_match(struct iscsi_session *, u32 lun, u8 asc,
+ u8 ascq);
+void ua_free(struct ua_entry *);
+int ua_pending(struct iscsi_session *, u32 lun);
+void ua_establish_for_session(struct iscsi_session *, u32 lun, u8 asc,
+ u8 ascq);
+void ua_establish_for_other_sessions(struct iscsi_session *, u32 lun, u8 asc,
+ u8 ascq);
+void ua_establish_for_all_sessions(struct iscsi_target *, u32 lun, u8 asc,
+ u8 ascq);
+
#define get_pgcnt(size, offset) ((((size) + ((offset) & ~PAGE_CACHE_MASK)) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)
static inline void iscsi_cmnd_get_length(struct iscsi_pdu *pdu)
@@ -425,6 +477,7 @@ enum cmnd_flags {
CMND_pending,
CMND_tmfabort,
CMND_rxstart,
+ CMND_timer_active,
};
#define set_cmnd_hashed(cmnd) set_bit(CMND_hashed, &(cmnd)->flags)
@@ -455,6 +508,11 @@ enum cmnd_flags {
#define set_cmnd_rxstart(cmnd) set_bit(CMND_rxstart, &(cmnd)->flags)
#define cmnd_rxstart(cmnd) test_bit(CMND_rxstart, &(cmnd)->flags)
+#define set_cmnd_timer_active(cmnd) set_bit(CMND_timer_active, &(cmnd)->flags)
+#define clear_cmnd_timer_active(cmnd) \
+ clear_bit(CMND_timer_active, &(cmnd)->flags)
+#define cmnd_timer_active(cmnd) test_bit(CMND_timer_active, &(cmnd)->flags)
+
#define VENDOR_ID "IET"
#define PRODUCT_ID "VIRTUAL-DISK"
#define PRODUCT_REV "0"
diff --git a/ubuntu/iscsitarget/iscsi_dbg.h b/ubuntu/iscsitarget/iscsi_dbg.h
index bc83b54..d8d5966 100644
--- a/ubuntu/iscsitarget/iscsi_dbg.h
+++ b/ubuntu/iscsitarget/iscsi_dbg.h
@@ -10,6 +10,7 @@
#define D_THREAD (1UL << 6)
#define D_TASK_MGT (1UL << 7)
#define D_IOMODE (1UL << 8)
+#define D_UAC (1UL << 9)
#define D_DATA (D_READ | D_WRITE)
@@ -24,6 +25,12 @@ extern unsigned long debug_enable_flags;
} \
} while (0)
+#define dprintk_ua(ua, sess, lun) \
+ dprintk(D_UAC, "sess %llu, lun %u: %p %x %x\n", \
+ (sess)->sid, lun, ua, \
+ (ua) ? (ua)->asc : 0, \
+ (ua) ? (ua)->ascq : 0)
+
#define eprintk(fmt, args...) do { \
printk(KERN_ERR PFX "%s(%d) " fmt, __FUNCTION__, \
__LINE__, args);\
diff --git a/ubuntu/iscsitarget/iscsi_hdr.h b/ubuntu/iscsitarget/iscsi_hdr.h
index 1233dd2..2cbcd4f 100644
--- a/ubuntu/iscsitarget/iscsi_hdr.h
+++ b/ubuntu/iscsitarget/iscsi_hdr.h
@@ -43,7 +43,7 @@ struct iscsi_hdr {
#define ISCSI_OPCODE_MASK 0x3F
/* Client to Server Message Opcode values */
-#define ISCSI_OP_NOOP_OUT 0x00
+#define ISCSI_OP_NOP_OUT 0x00
#define ISCSI_OP_SCSI_CMD 0x01
#define ISCSI_OP_SCSI_TASK_MGT_MSG 0x02
#define ISCSI_OP_LOGIN_CMD 0x03
@@ -58,7 +58,7 @@ struct iscsi_hdr {
#define ISCSI_OP_VENDOR4_CMD 0x1f
/* Server to Client Message Opcode values */
-#define ISCSI_OP_NOOP_IN 0x20
+#define ISCSI_OP_NOP_IN 0x20
#define ISCSI_OP_SCSI_RSP 0x21
#define ISCSI_OP_SCSI_TASK_MGT_RSP 0x22
#define ISCSI_OP_LOGIN_RSP 0x23
diff --git a/ubuntu/iscsitarget/nthread.c b/ubuntu/iscsitarget/nthread.c
index ec0c3eb..ec54ead 100644
--- a/ubuntu/iscsitarget/nthread.c
+++ b/ubuntu/iscsitarget/nthread.c
@@ -1,6 +1,8 @@
/*
* Network thread.
* (C) 2004 - 2005 FUJITA Tomonori <tomof at acm.org>
+ * (C) 2008 Arne Redlich <agr at powerkom-dd.de>
+ *
* This code is licenced under the GPL.
*/
@@ -567,6 +569,45 @@ static int send(struct iscsi_conn *conn)
return 0;
}
+static void conn_nop_timeout(unsigned long data)
+{
+ struct iscsi_conn *conn = (struct iscsi_conn *)data;
+
+ if (test_bit(CONN_ACTIVE, &conn->state))
+ set_bit(CONN_NEED_NOP_IN, &conn->state);
+
+ dprintk(D_THREAD, "conn %llu:%hu, NOP timer %p\n", conn->session->sid,
+ conn->cid, &conn->nop_timer);
+
+ nthread_wakeup(conn->session->target);
+}
+
+static void conn_reset_nop_timer(struct iscsi_conn *conn)
+{
+ struct iscsi_target *target = conn->session->target;
+
+ if (target->trgt_param.nop_interval)
+ mod_timer(&conn->nop_timer,
+ jiffies + HZ * target->trgt_param.nop_interval);
+}
+
+static void conn_start_nop_timer(struct iscsi_conn *conn)
+{
+ struct iscsi_target *target = conn->session->target;
+
+ if (!target->trgt_param.nop_interval || timer_pending(&conn->nop_timer))
+ return;
+
+ conn->nop_timer.data = (unsigned long)conn;
+ conn->nop_timer.function = conn_nop_timeout;
+
+ dprintk(D_THREAD, "conn %llu:%hu, NOP timer %p\n", conn->session->sid,
+ conn->cid, &conn->nop_timer);
+
+ mod_timer(&conn->nop_timer,
+ jiffies + HZ * target->trgt_param.nop_interval);
+}
+
static void process_io(struct iscsi_conn *conn)
{
struct iscsi_target *target = conn->session->target;
@@ -574,8 +615,10 @@ static void process_io(struct iscsi_conn *conn)
res = recv(conn);
- if (is_data_available(conn) > 0 || res > 0)
+ if (is_data_available(conn) > 0 || res > 0) {
+ conn_reset_nop_timer(conn);
wakeup = 1;
+ }
if (!test_bit(CONN_ACTIVE, &conn->state)) {
wakeup = 1;
@@ -587,12 +630,19 @@ static void process_io(struct iscsi_conn *conn)
res = send(conn);
- if (!list_empty(&conn->write_list) || conn->write_cmnd)
+ if (!list_empty(&conn->write_list) || conn->write_cmnd) {
+ conn_reset_nop_timer(conn);
wakeup = 1;
+ }
out:
if (wakeup)
nthread_wakeup(target);
+ else if (test_and_clear_bit(CONN_NEED_NOP_IN, &conn->state)) {
+ send_nop_in(conn);
+ nthread_wakeup(target);
+ } else
+ conn_start_nop_timer(conn);
return;
}
@@ -600,10 +650,11 @@ out:
static void close_conn(struct iscsi_conn *conn)
{
struct iscsi_session *session = conn->session;
- struct iscsi_target *target = session->target;
+ struct iscsi_target *target = conn->session->target;
struct iscsi_cmnd *cmnd;
- assert(conn);
+ if (target->trgt_param.nop_interval)
+ del_timer_sync(&conn->nop_timer);
conn->sock->ops->shutdown(conn->sock, 2);
@@ -637,8 +688,12 @@ static void close_conn(struct iscsi_conn *conn)
event_send(target->tid, session->sid, conn->cid, E_CONN_CLOSE, 0);
conn_free(conn);
- if (list_empty(&session->conn_list))
- session_del(target, session->sid);
+ if (list_empty(&session->conn_list)) {
+ if (session->done)
+ complete(session->done);
+ else
+ session_del(target, session->sid);
+ }
}
static int istd(void *arg)
diff --git a/ubuntu/iscsitarget/param.c b/ubuntu/iscsitarget/param.c
index 3292d4d..57ad301 100644
--- a/ubuntu/iscsitarget/param.c
+++ b/ubuntu/iscsitarget/param.c
@@ -116,7 +116,12 @@ static void trgt_param_check(struct iscsi_param_info *info)
CHECK_PARAM(info, iparam, wthreads, MIN_NR_WTHREADS, MAX_NR_WTHREADS);
CHECK_PARAM(info, iparam, target_type, 0,
(unsigned int) ARRAY_SIZE(target_type_array) - 1);
- CHECK_PARAM(info, iparam, queued_cmnds, MIN_NR_QUEUED_CMNDS, MAX_NR_QUEUED_CMNDS);
+ CHECK_PARAM(info, iparam, queued_cmnds, MIN_NR_QUEUED_CMNDS,
+ MAX_NR_QUEUED_CMNDS);
+ CHECK_PARAM(info, iparam, nop_interval, MIN_NOP_INTERVAL,
+ MAX_NOP_INTERVAL);
+ CHECK_PARAM(info, iparam, nop_timeout, MIN_NOP_TIMEOUT,
+ MAX_NOP_TIMEOUT);
}
static void trgt_param_set(struct iscsi_target *target, struct iscsi_param_info *info)
@@ -130,6 +135,8 @@ static void trgt_param_set(struct iscsi_target *target, struct iscsi_param_info
target->trgt_param.wthreads, target->tid);
SET_PARAM(param, info, iparam, target_type);
SET_PARAM(param, info, iparam, queued_cmnds);
+ SET_PARAM(param, info, iparam, nop_interval);
+ SET_PARAM(param, info, iparam, nop_timeout);
}
static void trgt_param_get(struct iscsi_trgt_param *param, struct iscsi_param_info *info)
@@ -139,6 +146,8 @@ static void trgt_param_get(struct iscsi_trgt_param *param, struct iscsi_param_in
GET_PARAM(param, info, iparam, wthreads);
GET_PARAM(param, info, iparam, target_type);
GET_PARAM(param, info, iparam, queued_cmnds);
+ GET_PARAM(param, info, iparam, nop_interval);
+ GET_PARAM(param, info, iparam, nop_timeout);
}
static int trgt_param(struct iscsi_target *target, struct iscsi_param_info *info, int set)
diff --git a/ubuntu/iscsitarget/session.c b/ubuntu/iscsitarget/session.c
index 1f1420e..6365373 100644
--- a/ubuntu/iscsitarget/session.c
+++ b/ubuntu/iscsitarget/session.c
@@ -23,6 +23,7 @@ iet_session_alloc(struct iscsi_target *target, struct session_info *info)
{
int i;
struct iscsi_session *session;
+ struct iet_volume *vol;
dprintk(D_SETUP, "%p %u %#Lx\n", target, target->tid,
(unsigned long long) info->sid);
@@ -52,9 +53,19 @@ iet_session_alloc(struct iscsi_target *target, struct session_info *info)
for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
INIT_LIST_HEAD(&session->cmnd_hash[i]);
+ spin_lock_init(&session->ua_hash_lock);
+ for (i = 0; i < ARRAY_SIZE(session->ua_hash); i++)
+ INIT_LIST_HEAD(&session->ua_hash[i]);
+
+ list_for_each_entry(vol, &target->volumes, list)
+ /* power-on, reset, or bus device reset occurred */
+ ua_establish_for_session(session, vol->lun, 0x29, 0x0);
+
session->next_ttt = 1;
+ spin_lock(&target->session_list_lock);
list_add(&session->list, &target->session_list);
+ spin_unlock(&target->session_list_lock);
return session;
}
@@ -62,9 +73,14 @@ iet_session_alloc(struct iscsi_target *target, struct session_info *info)
static int session_free(struct iscsi_session *session)
{
int i;
+ struct ua_entry *ua, *tmp;
+ struct list_head *l;
+ struct iscsi_target *target = session->target;
dprintk(D_SETUP, "%#Lx\n", (unsigned long long) session->sid);
+ spin_lock(&target->session_list_lock);
+
assert(list_empty(&session->conn_list));
for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++) {
@@ -72,28 +88,37 @@ static int session_free(struct iscsi_session *session)
BUG();
}
+ for (i = 0; i < ARRAY_SIZE(session->ua_hash); i++) {
+ l = &session->ua_hash[i];
+ list_for_each_entry_safe(ua, tmp, l, entry) {
+ list_del_init(&ua->entry);
+ ua_free(ua);
+ }
+ }
+
list_del(&session->list);
kfree(session->initiator);
kfree(session);
+ spin_unlock(&target->session_list_lock);
+
return 0;
}
int session_add(struct iscsi_target *target, struct session_info *info)
{
struct iscsi_session *session;
- int err = -EEXIST;
session = session_lookup(target, info->sid);
if (session)
- return err;
+ return -EEXIST;
session = iet_session_alloc(target, info);
if (!session)
- err = -ENOMEM;
+ return -ENOMEM;
- return err;
+ return 0;
}
int session_del(struct iscsi_target *target, u64 sid)
@@ -112,6 +137,29 @@ int session_del(struct iscsi_target *target, u64 sid)
return session_free(session);
}
+void session_del_all(struct iscsi_target *target)
+{
+ DECLARE_COMPLETION_ONSTACK(done);
+ struct iscsi_session *sess;
+
+ while (!list_empty(&target->session_list)) {
+ init_completion(&done);
+ target_lock(target, 0);
+ sess = list_entry(target->session_list.next, struct
+ iscsi_session, list);
+ sess->done = &done;
+ conn_del_all(sess);
+ target_unlock(target);
+ wait_for_completion(&done);
+ target_lock(target, 0);
+ session_free(sess);
+ target_unlock(target);
+ }
+
+ if (target->done)
+ complete(target->done);
+}
+
static void iet_session_info_show(struct seq_file *seq, struct iscsi_target *target)
{
struct iscsi_session *session;
diff --git a/ubuntu/iscsitarget/target.c b/ubuntu/iscsitarget/target.c
index a0879b8..15c0715 100644
--- a/ubuntu/iscsitarget/target.c
+++ b/ubuntu/iscsitarget/target.c
@@ -158,6 +158,7 @@ static int iscsi_target_create(struct target_info *info, u32 tid)
strncpy(target->name, name, sizeof(target->name) - 1);
init_MUTEX(&target->target_sem);
+ spin_lock_init(&target->session_list_lock);
INIT_LIST_HEAD(&target->session_list);
INIT_LIST_HEAD(&target->volumes);
@@ -244,40 +245,66 @@ static void target_destroy(struct iscsi_target *target)
module_put(THIS_MODULE);
}
-int target_del(u32 id)
+/* @locking: target_list_sem must be locked */
+int __target_del(struct iscsi_target *target)
{
- struct iscsi_target *target;
- int err;
-
- if ((err = down_interruptible(&target_list_sem)) < 0)
- return err;
-
- if (!(target = __target_lookup_by_id(id))) {
- err = -ENOENT;
- goto out;
- }
-
target_lock(target, 0);
if (!list_empty(&target->session_list)) {
- err = -EBUSY;
target_unlock(target);
- goto out;
+ return -EBUSY;
}
list_del(&target->t_list);
nr_targets--;
target_unlock(target);
- up(&target_list_sem);
-
target_destroy(target);
return 0;
-out:
+}
+
+int target_del(u32 id)
+{
+ struct iscsi_target *target;
+ int err = down_interruptible(&target_list_sem);
+ if (err < 0)
+ return err;
+
+ target = __target_lookup_by_id(id);
+ if (!target) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ err = __target_del(target);
+ out:
up(&target_list_sem);
return err;
}
+void target_del_all(void)
+{
+ DECLARE_COMPLETION_ONSTACK(done);
+ struct iscsi_target *target, *tmp;
+
+ down(&target_list_sem);
+
+ if (!list_empty(&target_list))
+ iprintk("Removing all connections, sessions and targets\n");
+
+ list_for_each_entry_safe(target, tmp, &target_list, t_list) {
+ init_completion(&done);
+ target->done = &done;
+ session_del_all(target);
+ wait_for_completion(&done);
+ __target_del(target);
+ }
+
+ next_target_id = 0;
+
+ up(&target_list_sem);
+}
+
static void *iet_seq_start(struct seq_file *m, loff_t *pos)
{
int err;
diff --git a/ubuntu/iscsitarget/target_disk.c b/ubuntu/iscsitarget/target_disk.c
index 4488bc5..694edb2 100644
--- a/ubuntu/iscsitarget/target_disk.c
+++ b/ubuntu/iscsitarget/target_disk.c
@@ -84,7 +84,7 @@ static int insert_geo_m_pg(u8 *ptr, u64 sec)
return sizeof(geo_m_pg);
}
-static int build_mode_sense_response(struct iscsi_cmnd *cmnd)
+static void build_mode_sense_response(struct iscsi_cmnd *cmnd)
{
struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
struct tio *tio = cmnd->tio;
@@ -94,7 +94,7 @@ static int build_mode_sense_response(struct iscsi_cmnd *cmnd)
/* changeable parameter mode pages are unsupported */
if ((scb[2] & 0xc0) >> 6 == 0x1)
- return -1;
+ goto set_sense;
pcode = req->scb[2] & 0x3f;
@@ -152,14 +152,20 @@ static int build_mode_sense_response(struct iscsi_cmnd *cmnd)
err = -1;
}
- data[0] = len - 1;
-
- tio_set(tio, len, 0);
+ if (!err) {
+ data[0] = len - 1;
+ tio_set(tio, len, 0);
+ return;
+ }
- return err;
+ tio_put(tio);
+ cmnd->tio = NULL;
+ set_sense:
+ /* Invalid Field In CDB */
+ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x24, 0x0);
}
-static int build_inquiry_response(struct iscsi_cmnd *cmnd)
+static void build_inquiry_response(struct iscsi_cmnd *cmnd)
{
struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
struct tio *tio = cmnd->tio;
@@ -172,7 +178,7 @@ static int build_inquiry_response(struct iscsi_cmnd *cmnd)
* - CmdDt set: not supported
*/
if ((scb[1] & 0x3) > 0x1 || (!(scb[1] & 0x3) && scb[2]))
- return err;
+ goto set_sense;
assert(!tio);
tio = cmnd->tio = tio_alloc(1);
@@ -245,14 +251,21 @@ static int build_inquiry_response(struct iscsi_cmnd *cmnd)
}
}
- tio_set(tio, min_t(u8, tio->size, scb[4]), 0);
- if (!cmnd->lun)
- data[0] = TYPE_NO_LUN;
+ if (!err) {
+ tio_set(tio, min_t(u8, tio->size, scb[4]), 0);
+ if (!cmnd->lun)
+ data[0] = TYPE_NO_LUN;
+ return;
+ }
- return err;
+ tio_put(tio);
+ cmnd->tio = NULL;
+ set_sense:
+ /* Invalid Field In CDB */
+ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x24, 0x0);
}
-static int build_report_luns_response(struct iscsi_cmnd *cmnd)
+static void build_report_luns_response(struct iscsi_cmnd *cmnd)
{
struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
struct tio *tio = cmnd->tio;
@@ -262,8 +275,11 @@ static int build_report_luns_response(struct iscsi_cmnd *cmnd)
size = (u32)req->scb[6] << 24 | (u32)req->scb[7] << 16 |
(u32)req->scb[8] << 8 | (u32)req->scb[9];
- if (size < 16)
- return -1;
+ if (size < 16) {
+ /* Invalid Field In CDB */
+ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x24, 0x0);
+ return;
+ }
len = atomic_read(&cmnd->conn->session->target->nr_volumes) * 8;
size = min(size & ~(8 - 1), len + 8);
@@ -293,11 +309,9 @@ static int build_report_luns_response(struct iscsi_cmnd *cmnd)
rest = PAGE_CACHE_SIZE;
}
}
-
- return 0;
}
-static int build_read_capacity_response(struct iscsi_cmnd *cmnd)
+static void build_read_capacity_response(struct iscsi_cmnd *cmnd)
{
struct tio *tio = cmnd->tio;
u32 *data;
@@ -313,10 +327,9 @@ static int build_read_capacity_response(struct iscsi_cmnd *cmnd)
data[1] = cpu_to_be32(1U << cmnd->lun->blk_shift);
tio_set(tio, 8, 0);
- return 0;
}
-static int build_request_sense_response(struct iscsi_cmnd *cmnd)
+static void build_request_sense_response(struct iscsi_cmnd *cmnd)
{
struct tio *tio = cmnd->tio;
u8 *data;
@@ -331,11 +344,9 @@ static int build_request_sense_response(struct iscsi_cmnd *cmnd)
data[2] = NO_SENSE;
data[7] = 10;
tio_set(tio, 18, 0);
-
- return 0;
}
-static int build_service_action_in_response(struct iscsi_cmnd *cmnd)
+static void build_service_action_in_response(struct iscsi_cmnd *cmnd)
{
struct tio *tio = cmnd->tio;
u32 *data;
@@ -344,8 +355,11 @@ static int build_service_action_in_response(struct iscsi_cmnd *cmnd)
assert(!tio);
/* only READ_CAPACITY_16 service action is currently supported */
- if ((cmnd_hdr(cmnd)->scb[1] & 0x1F) != 0x10)
- return -1;
+ if ((cmnd_hdr(cmnd)->scb[1] & 0x1F) != 0x10) {
+ /* Invalid Field In CDB */
+ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x24, 0x0);
+ return;
+ }
tio = cmnd->tio = tio_alloc(1);
data = page_address(tio->pvec[0]);
@@ -356,20 +370,21 @@ static int build_service_action_in_response(struct iscsi_cmnd *cmnd)
data[2] = cpu_to_be32(1UL << cmnd->lun->blk_shift);
tio_set(tio, 12, 0);
- return 0;
}
-static int build_read_response(struct iscsi_cmnd *cmnd)
+static void build_read_response(struct iscsi_cmnd *cmnd)
{
struct tio *tio = cmnd->tio;
assert(tio);
assert(cmnd->lun);
- return tio_read(cmnd->lun, tio);
+ if (tio_read(cmnd->lun, tio))
+ /* Medium Error/Unrecovered Read Error */
+ iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x11, 0x0);
}
-static int build_write_response(struct iscsi_cmnd *cmnd)
+static void build_write_response(struct iscsi_cmnd *cmnd)
{
int err;
struct tio *tio = cmnd->tio;
@@ -382,41 +397,83 @@ static int build_write_response(struct iscsi_cmnd *cmnd)
if (!err && !LUWCache(cmnd->lun))
err = tio_sync(cmnd->lun, tio);
- return err;
+ if (err)
+ /* Medium Error/Write Fault */
+ iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x03, 0x0);
}
-static int build_sync_cache_response(struct iscsi_cmnd *cmnd)
+static void build_sync_cache_response(struct iscsi_cmnd *cmnd)
{
assert(cmnd->lun);
- return tio_sync(cmnd->lun, NULL);
+ if (tio_sync(cmnd->lun, NULL))
+ /* Medium Error/Write Fault */
+ iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x03, 0x0);
}
-static int build_generic_response(struct iscsi_cmnd *cmnd)
+static void build_generic_response(struct iscsi_cmnd *cmnd)
{
- return 0;
+ return;
}
-static int build_reserve_response(struct iscsi_cmnd *cmnd)
+static void build_reserve_response(struct iscsi_cmnd *cmnd)
{
- return volume_reserve(cmnd->lun, cmnd->conn->session->sid);
+ switch (volume_reserve(cmnd->lun, cmnd->conn->session->sid)) {
+ case -ENOENT:
+ /* Logical Unit Not Supported (?) */
+ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x25, 0x0);
+ break;
+ case -EBUSY:
+ cmnd->status = SAM_STAT_RESERVATION_CONFLICT;
+ break;
+ default:
+ break;
+ }
}
-static int build_release_response(struct iscsi_cmnd *cmnd)
+static void build_release_response(struct iscsi_cmnd *cmnd)
{
- return volume_release(cmnd->lun,
- cmnd->conn->session->sid, 0);
+ if (volume_release(cmnd->lun,
+ cmnd->conn->session->sid, 0))
+ cmnd->status = SAM_STAT_RESERVATION_CONFLICT;
}
-static int build_reservation_conflict_response(struct iscsi_cmnd *cmnd)
+static void build_reservation_conflict_response(struct iscsi_cmnd *cmnd)
{
- return -EBUSY;
+ cmnd->status = SAM_STAT_RESERVATION_CONFLICT;
}
-static int disk_execute_cmnd(struct iscsi_cmnd *cmnd)
+static int disk_check_ua(struct iscsi_cmnd *cmnd)
{
struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
+ struct ua_entry *ua;
- req->opcode &= ISCSI_OPCODE_MASK;
+ if (cmnd->lun && ua_pending(cmnd->conn->session, cmnd->lun->lun)) {
+ switch(req->scb[0]){
+ case INQUIRY:
+ case REQUEST_SENSE:
+ break;
+ case REPORT_LUNS:
+ ua = ua_get_match(cmnd->conn->session,
+ cmnd->lun->lun,
+ /* reported luns data has changed */
+ 0x3f, 0x0e);
+ ua_free(ua);
+ break;
+ default:
+ ua = ua_get_first(cmnd->conn->session, cmnd->lun->lun);
+ iscsi_cmnd_set_sense(cmnd, UNIT_ATTENTION, ua->asc,
+ ua->ascq);
+ ua_free(ua);
+ send_scsi_rsp(cmnd, build_generic_response);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int disk_check_reservation(struct iscsi_cmnd *cmnd)
+{
+ struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
if (is_volume_reserved(cmnd->lun,
cmnd->conn->session->sid)) {
@@ -425,16 +482,36 @@ static int disk_execute_cmnd(struct iscsi_cmnd *cmnd)
case RELEASE:
case REPORT_LUNS:
case REQUEST_SENSE:
+ case READ_CAPACITY:
/* allowed commands when reserved */
break;
+ case SERVICE_ACTION_IN:
+ if ((cmnd_hdr(cmnd)->scb[1] & 0x1F) == 0x10)
+ break;
+ /* fall through */
default:
/* return reservation conflict for all others */
send_scsi_rsp(cmnd,
build_reservation_conflict_response);
- return 0;
+ return 1;
}
}
+ return 0;
+}
+
+static int disk_execute_cmnd(struct iscsi_cmnd *cmnd)
+{
+ struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
+
+ req->opcode &= ISCSI_OPCODE_MASK;
+
+ if (disk_check_ua(cmnd))
+ return 0;
+
+ if (disk_check_reservation(cmnd))
+ return 0;
+
switch (req->scb[0]) {
case INQUIRY:
send_data_rsp(cmnd, build_inquiry_response);
diff --git a/ubuntu/iscsitarget/ua.c b/ubuntu/iscsitarget/ua.c
new file mode 100644
index 0000000..db08169
--- /dev/null
+++ b/ubuntu/iscsitarget/ua.c
@@ -0,0 +1,164 @@
+/*
+ * IET Unit Attention support
+ *
+ * Copyright (C) 2009 Xie Gang <xiegang112 at gmail.com>
+ * Copyright (C) 2009 Arne Redlich <arne.redlich at googlemail.com>
+ *
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <scsi/scsi.h>
+
+#include "iscsi.h"
+#include "iscsi_dbg.h"
+
+#define ua_hashfn(lun) ((lun % UA_HASH_LEN))
+
+static struct kmem_cache *ua_cache;
+
+int ua_init(void)
+{
+ ua_cache = KMEM_CACHE(ua_entry, 0);
+ if (!ua_cache) {
+ eprintk("%s", "Failed to create ua cache\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void ua_exit(void)
+{
+ if (ua_cache)
+ kmem_cache_destroy(ua_cache);
+}
+
+/* sess->ua_hash_lock needs to be held */
+static struct ua_entry * ua_find_hash(struct iscsi_session *sess, u32 lun,
+ u8 asc, u8 ascq, int match)
+{
+ struct ua_entry *ua;
+ struct list_head *h = &sess->ua_hash[ua_hashfn(lun)];
+
+ list_for_each_entry(ua, h, entry) {
+ if (ua->lun == lun) {
+ if (!match)
+ return ua;
+ if (ua->asc == asc && ua->ascq == ascq)
+ return ua;
+ }
+ }
+
+ return NULL;
+}
+
+int ua_pending(struct iscsi_session *sess, u32 lun)
+{
+ struct ua_entry *ua;
+
+ spin_lock(&sess->ua_hash_lock);
+ ua = ua_find_hash(sess, lun, 0, 0, 0);
+ spin_unlock(&sess->ua_hash_lock);
+
+ dprintk_ua(ua, sess, lun);
+
+ return ua ? 1 : 0;
+}
+
+/* sess->ua_hash_lock needs to be held */
+static struct ua_entry * __ua_get_hash(struct iscsi_session *sess, u32 lun,
+ u8 asc, u8 ascq, int match)
+{
+ struct ua_entry *ua = ua_find_hash(sess, lun, asc, ascq, match);
+
+ if (ua)
+ list_del_init(&ua->entry);
+
+ return ua;
+}
+
+struct ua_entry * ua_get_first(struct iscsi_session *sess, u32 lun)
+{
+ struct ua_entry *ua;
+
+ spin_lock(&sess->ua_hash_lock);
+ ua = __ua_get_hash(sess, lun, 0, 0, 0);
+ spin_unlock(&sess->ua_hash_lock);
+
+ dprintk_ua(ua, sess, lun);
+
+ return ua;
+}
+
+struct ua_entry * ua_get_match(struct iscsi_session *sess, u32 lun,
+ u8 asc, u8 ascq)
+{
+ struct ua_entry *ua;
+
+ spin_lock(&sess->ua_hash_lock);
+ ua = __ua_get_hash(sess, lun, asc, ascq, 1);
+ spin_unlock(&sess->ua_hash_lock);
+
+ dprintk_ua(ua, sess, lun);
+
+ return ua;
+}
+
+void ua_establish_for_session(struct iscsi_session *sess, u32 lun,
+ u8 asc, u8 ascq)
+{
+ struct list_head *l = &sess->ua_hash[ua_hashfn(lun)];
+ struct ua_entry *ua = kmem_cache_alloc(ua_cache, GFP_KERNEL);
+
+ if (!ua) {
+ eprintk("%s", "Failed to alloc ua");
+ return;
+ }
+
+ ua->asc = asc;
+ ua->ascq = ascq;
+ ua->lun = lun;
+ ua->session = sess;
+
+ spin_lock(&sess->ua_hash_lock);
+ list_add_tail(&ua->entry, l);
+ spin_unlock(&sess->ua_hash_lock);
+
+ dprintk_ua(ua, sess, lun);
+}
+
+void ua_establish_for_other_sessions(struct iscsi_session *sess, u32 lun,
+ u8 asc, u8 ascq)
+{
+ struct list_head *l = &sess->target->session_list;
+ struct iscsi_session *s;
+
+ spin_lock(&sess->target->session_list_lock);
+ list_for_each_entry(s, l, list)
+ if (s->sid != sess->sid)
+ ua_establish_for_session(s, lun, asc, ascq);
+ spin_unlock(&sess->target->session_list_lock);
+}
+
+void ua_establish_for_all_sessions(struct iscsi_target *target, u32 lun,
+ u8 asc, u8 ascq)
+{
+ struct list_head *l = &target->session_list;
+ struct iscsi_session *s;
+
+ spin_lock(&target->session_list_lock);
+ list_for_each_entry(s, l, list)
+ ua_establish_for_session(s, lun, asc, ascq);
+ spin_unlock(&target->session_list_lock);
+
+}
+
+void ua_free(struct ua_entry *ua)
+{
+ if (!ua)
+ return;
+
+ dprintk_ua(ua, ua->session, ua->lun);
+ BUG_ON(!list_empty(&ua->entry));
+ kmem_cache_free(ua_cache, ua);
+}
--
1.6.3.3
More information about the kernel-team
mailing list