From patchwork Fri Dec 4 02:35:47 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manoj Iyer X-Patchwork-Id: 40290 X-Patchwork-Delegate: apw@canonical.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from chlorine.canonical.com (chlorine.canonical.com [91.189.94.204]) by ozlabs.org (Postfix) with ESMTP id 40654B7BC0 for ; Fri, 4 Dec 2009 13:36:06 +1100 (EST) Received: from localhost ([127.0.0.1] helo=chlorine.canonical.com) by chlorine.canonical.com with esmtp (Exim 4.60) (envelope-from ) id 1NGO1U-0002wX-HM; Fri, 04 Dec 2009 02:35:56 +0000 Received: from adelie.canonical.com ([91.189.90.139]) by chlorine.canonical.com with esmtp (Exim 4.60) (envelope-from ) id 1NGO1P-0002wN-FD for kernel-team@lists.ubuntu.com; Fri, 04 Dec 2009 02:35:51 +0000 Received: from hutte.canonical.com ([91.189.90.181]) by adelie.canonical.com with esmtp (Exim 4.69 #1 (Debian)) id 1NGO1P-0007WD-DI for ; Fri, 04 Dec 2009 02:35:51 +0000 Received: from [70.114.236.114] (helo=hungry.local) by hutte.canonical.com with esmtpsa (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.69) (envelope-from ) id 1NGO1O-0007OC-Mm for kernel-team@lists.ubuntu.com; Fri, 04 Dec 2009 02:35:51 +0000 Date: Thu, 3 Dec 2009 20:35:47 -0600 (CST) From: Manoj Iyer To: Ubuntu Kernel Team Subject: [KARMIC] UBUNTU: Upgrade iscsitarget source from 0.4.17 to 1.4.19 Message-ID: User-Agent: Alpine 2.00 (DEB 1167 2008-08-23) MIME-Version: 1.0 X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.8 Precedence: list Reply-To: Manoj Iyer List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: kernel-team-bounces@lists.ubuntu.com Errors-To: kernel-team-bounces@lists.ubuntu.com 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 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 --- 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 + * Copyright (C) 2008 Arne Redlich * * 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 "); 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 + * Copyright (C) 2008 Arne Redlich * * Released under the terms of the GNU GPL v2.0. */ @@ -7,6 +8,7 @@ #ifndef __ISCSI_H__ #define __ISCSI_H__ +#include #include #include #include @@ -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 + * (C) 2008 Arne Redlich + * * 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 + * Copyright (C) 2009 Arne Redlich + * + * Released under the terms of the GNU GPL v2.0. + */ + +#include + +#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); +}