From: <rsw...@us...> - 2010-03-02 02:56:23
|
Revision: 303 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=303&view=rev Author: rswwalker Date: 2010-03-02 02:56:17 +0000 (Tue, 02 Mar 2010) Log Message: ----------- I always got confused with ScsiSn and ScsiID. No, wait, it was ScsiId and ScsiSN. Or was it? Signed-off-by: Lars Ellenberg <lar...@li...> Modified Paths: -------------- trunk/kernel/block-io.c trunk/kernel/file-io.c trunk/kernel/iotype.h trunk/kernel/null-io.c trunk/kernel/volume.c Modified: trunk/kernel/block-io.c =================================================================== --- trunk/kernel/block-io.c 2010-03-02 02:47:57 UTC (rev 302) +++ trunk/kernel/block-io.c 2010-03-02 02:56:17 UTC (rev 303) @@ -240,12 +240,12 @@ /* Create a match table using our action enums and their matching options */ static match_table_t tokens = { - {Opt_scsiid, "ScsiId=%s"}, - {Opt_scsisn, "ScsiSN=%s"}, - {Opt_path, "Path=%s"}, - {Opt_ignore, "Type=%s"}, - {Opt_ignore, "IOMode=%s"}, - {Opt_ignore, "BlockSize=%s"}, + {Opt_scsiid, "scsiid=%s"}, + {Opt_scsisn, "scsisn=%s"}, + {Opt_path, "path=%s"}, + {Opt_ignore, "type=%s"}, + {Opt_ignore, "iomode=%s"}, + {Opt_ignore, "blocksize=%s"}, {Opt_err, NULL}, }; @@ -264,6 +264,7 @@ int token; if (!*p) continue; + iet_strtolower(p); token = match_token(p, tokens, args); switch (token) { case Opt_scsiid: Modified: trunk/kernel/file-io.c =================================================================== --- trunk/kernel/file-io.c 2010-03-02 02:47:57 UTC (rev 302) +++ trunk/kernel/file-io.c 2010-03-02 02:56:17 UTC (rev 303) @@ -176,12 +176,12 @@ }; static match_table_t tokens = { - {Opt_scsiid, "ScsiId=%s"}, - {Opt_scsisn, "ScsiSN=%s"}, - {Opt_path, "Path=%s"}, - {Opt_ignore, "Type=%s"}, - {Opt_ignore, "IOMode=%s"}, - {Opt_ignore, "BlockSize=%s"}, + {Opt_scsiid, "scsiid=%s"}, + {Opt_scsisn, "scsisn=%s"}, + {Opt_path, "path=%s"}, + {Opt_ignore, "type=%s"}, + {Opt_ignore, "iomode=%s"}, + {Opt_ignore, "blocksize=%s"}, {Opt_err, NULL}, }; @@ -196,6 +196,7 @@ int token; if (!*p) continue; + iet_strtolower(p); token = match_token(p, tokens, args); switch (token) { case Opt_scsiid: Modified: trunk/kernel/iotype.h =================================================================== --- trunk/kernel/iotype.h 2010-03-02 02:47:57 UTC (rev 302) +++ trunk/kernel/iotype.h 2010-03-02 02:56:17 UTC (rev 303) @@ -3,6 +3,7 @@ * This code is licenced under the GPL. */ +#include <linux/ctype.h> #include "iscsi.h" #ifndef __IOTYPE_H__ @@ -26,4 +27,16 @@ extern int iotype_init(void); extern void iotype_exit(void); +/* For option parameter parsing. + * This is slightly iet specific: we only tolower() up to the first '='. + * Note that this changes *c _in place_, but our parsing + * routines copy the input to a scratch page before parsing anyways. */ +static inline void iet_strtolower(char *c) +{ + if (!c) + return; + for (; *c && *c != '='; c++) + *c = tolower(*c); +} + #endif Modified: trunk/kernel/null-io.c =================================================================== --- trunk/kernel/null-io.c 2010-03-02 02:47:57 UTC (rev 302) +++ trunk/kernel/null-io.c 2010-03-02 02:56:17 UTC (rev 303) @@ -22,12 +22,12 @@ static match_table_t tokens = { /* alias for compatibility with existing setups and documentation */ - {Opt_blk_cnt, "Sectors=%u"}, + {Opt_blk_cnt, "sectors=%u"}, /* but actually it is the scsi block count, now that we can * specify the block size. */ - {Opt_blk_cnt, "Blocks=%u"}, - {Opt_ignore, "BlockSize=%s"}, - {Opt_ignore, "Type=%s"}, + {Opt_blk_cnt, "blocks=%u"}, + {Opt_ignore, "blocksize=%s"}, + {Opt_ignore, "type=%s"}, {Opt_err, NULL}, }; @@ -41,6 +41,7 @@ int token; if (!*p) continue; + iet_strtolower(p); token = match_token(p, tokens, args); switch (token) { case Opt_blk_cnt: Modified: trunk/kernel/volume.c =================================================================== --- trunk/kernel/volume.c 2010-03-02 02:47:57 UTC (rev 302) +++ trunk/kernel/volume.c 2010-03-02 02:56:17 UTC (rev 303) @@ -31,9 +31,9 @@ }; static match_table_t tokens = { - {Opt_type, "Type=%s"}, - {Opt_iomode, "IOMode=%s"}, - {Opt_blk_size, "BlockSize=%u"}, + {Opt_type, "type=%s"}, + {Opt_iomode, "iomode=%s"}, + {Opt_blk_size, "blocksize=%u"}, {Opt_err, NULL}, }; @@ -53,6 +53,7 @@ if (!*p) continue; + iet_strtolower(p); token = match_token(p, tokens, args); switch (token) { case Opt_type: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <rsw...@us...> - 2010-04-08 20:49:13
|
Revision: 314 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=314&view=rev Author: rswwalker Date: 2010-04-08 20:49:07 +0000 (Thu, 08 Apr 2010) Log Message: ----------- This patch makes sure that all tasks in processing at the time of an abort are aborted. For closing connections with active tasks it will also abort all those tasks and send a UA "SOME COMMANDS CLEARED BY ISCSI PROTOCOL EVENT" on the session for all luns affected. Commented where future ACA/UAI condition handling should occur. Compiled on CentOS5 Signed-off-by: Ross Walker Modified Paths: -------------- trunk/kernel/conn.c trunk/kernel/iscsi.c trunk/kernel/ua.c trunk/kernel/wthread.c Modified: trunk/kernel/conn.c =================================================================== --- trunk/kernel/conn.c 2010-04-08 20:45:28 UTC (rev 313) +++ trunk/kernel/conn.c 2010-04-08 20:49:07 UTC (rev 314) @@ -7,6 +7,7 @@ #include <linux/file.h> #include <linux/ip.h> #include <net/tcp.h> +#include <scsi/scsi.h> #include "iscsi.h" #include "iscsi_dbg.h" @@ -211,9 +212,20 @@ void conn_close(struct iscsi_conn *conn) { + struct iscsi_cmnd *cmnd; + struct iscsi_session *session = conn->session; + if (test_and_clear_bit(CONN_ACTIVE, &conn->state)) set_bit(CONN_CLOSING, &conn->state); + spin_lock(&conn->list_lock); + list_for_each_entry(cmnd, &conn->pdu_list, conn_list) { + set_cmnd_tmfabort(cmnd); + ua_establish_for_session(session, cmnd->lun->lun, 0x47, 0x7f); + /* Call to ACA/UAI handler */ + } + spin_unlock(&conn->list_lock); + nthread_wakeup(conn->session->target); } Modified: trunk/kernel/iscsi.c =================================================================== --- trunk/kernel/iscsi.c 2010-04-08 20:45:28 UTC (rev 313) +++ trunk/kernel/iscsi.c 2010-04-08 20:49:07 UTC (rev 314) @@ -345,6 +345,8 @@ cmnd->sense_buf[7] = 6; // Additional sense length cmnd->sense_buf[12] = asc; cmnd->sense_buf[13] = ascq; + + /* Call to ACA/UAI handler */ } static struct iscsi_cmnd *create_sense_rsp(struct iscsi_cmnd *req, @@ -1140,13 +1142,14 @@ static void __cmnd_abort(struct iscsi_cmnd *cmnd) { + if (cmnd_rxstart(cmnd)) + set_cmnd_tmfabort(cmnd); + if (cmnd_waitio(cmnd)) return; if (cmnd->conn->read_cmnd != cmnd) cmnd_release(cmnd, 1); - else if (cmnd_rxstart(cmnd)) - set_cmnd_tmfabort(cmnd); } static int cmnd_abort(struct iscsi_session *session, struct iscsi_cmnd *req) Modified: trunk/kernel/ua.c =================================================================== --- trunk/kernel/ua.c 2010-04-08 20:45:28 UTC (rev 313) +++ trunk/kernel/ua.c 2010-04-08 20:49:07 UTC (rev 314) @@ -109,6 +109,7 @@ { struct list_head *l = &sess->ua_hash[ua_hashfn(lun)]; struct ua_entry *ua = kmem_cache_alloc(ua_cache, GFP_KERNEL); + struct ua_entry *e; if (!ua) { eprintk("%s", "Failed to alloc ua"); @@ -121,6 +122,16 @@ ua->session = sess; spin_lock(&sess->ua_hash_lock); + /* One UA per occurrence of an event */ + list_for_each_entry(e, l, entry) { + if (e->session == sess && e->lun == lun && + e->asc == asc && e->ascq == ascq && + e->session->exp_cmd_sn == sess->exp_cmd_sn) { + spin_unlock(&sess->ua_hash_lock); + ua_free(ua); + return; + } + } list_add_tail(&ua->entry, l); spin_unlock(&sess->ua_hash_lock); Modified: trunk/kernel/wthread.c =================================================================== --- trunk/kernel/wthread.c 2010-04-08 20:45:28 UTC (rev 313) +++ trunk/kernel/wthread.c 2010-04-08 20:49:07 UTC (rev 314) @@ -83,7 +83,10 @@ while (!list_empty(&info->work_queue) && (cmnd = get_ready_cmnd(info))) { conn = cmnd->conn; - cmnd_execute(cmnd); + if (cmnd_tmfabort(cmnd)) + cmnd_release(cmnd, 1); + else + cmnd_execute(cmnd); assert(conn); atomic_dec(&conn->nr_busy_cmnds); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <rsw...@us...> - 2010-04-11 15:13:49
|
Revision: 319 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=319&view=rev Author: rswwalker Date: 2010-04-11 15:13:42 +0000 (Sun, 11 Apr 2010) Log Message: ----------- Previous patch that aborts commands on connection close didn't check to see if those commands referenced a lun which causes a null pointer dereference. Proper asc/ascq codes for iscsi_cmnd_set_sense() were found, so the call was put back, the call is merely internal for future ACA handling. The UA establishment code also didn't initialize the entry list head and if it is free'd on a duplicate entry it would also cause the BUG check in ua_free() to be hit. Signed-off-by: Ross Walker Modified Paths: -------------- trunk/kernel/conn.c trunk/kernel/ua.c Modified: trunk/kernel/conn.c =================================================================== --- trunk/kernel/conn.c 2010-04-11 15:09:03 UTC (rev 318) +++ trunk/kernel/conn.c 2010-04-11 15:13:42 UTC (rev 319) @@ -7,6 +7,7 @@ #include <linux/file.h> #include <linux/ip.h> #include <net/tcp.h> +#include <scsi/scsi.h> #include "iscsi.h" #include "iscsi_dbg.h" @@ -220,8 +221,10 @@ spin_lock(&conn->list_lock); list_for_each_entry(cmnd, &conn->pdu_list, conn_list) { set_cmnd_tmfabort(cmnd); - ua_establish_for_session(session, cmnd->lun->lun, 0x47, 0x7f); - /* Call to ACA/UAI handler */ + if (cmnd->lun) { + ua_establish_for_session(session, cmnd->lun->lun, 0x47, 0x7f); + iscsi_cmnd_set_sense(cmnd, UNIT_ATTENTION, 0x6e, 0x0); + } } spin_unlock(&conn->list_lock); Modified: trunk/kernel/ua.c =================================================================== --- trunk/kernel/ua.c 2010-04-11 15:09:03 UTC (rev 318) +++ trunk/kernel/ua.c 2010-04-11 15:13:42 UTC (rev 319) @@ -120,6 +120,7 @@ ua->ascq = ascq; ua->lun = lun; ua->session = sess; + INIT_LIST_HEAD(&ua->entry); spin_lock(&sess->ua_hash_lock); /* One UA per occurrence of an event */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2011-01-12 20:39:42
|
Revision: 378 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=378&view=rev Author: agr1 Date: 2011-01-12 20:39:36 +0000 (Wed, 12 Jan 2011) Log Message: ----------- Convert semaphores to mutexes As of 2.6.37, using semaphores as mutexes is finally not supported anymore after having been deprecated for several years now. Compile tested on 2.6.35. Signed-off-by: Arne Redlich <arn...@go...> Modified Paths: -------------- trunk/kernel/config.c trunk/kernel/iscsi.h trunk/kernel/target.c trunk/kernel/volume.c Modified: trunk/kernel/config.c =================================================================== --- trunk/kernel/config.c 2011-01-10 22:35:06 UTC (rev 377) +++ trunk/kernel/config.c 2011-01-12 20:39:36 UTC (rev 378) @@ -9,7 +9,7 @@ #include "iscsi.h" #include "iscsi_dbg.h" -static DECLARE_MUTEX(ioctl_sem); +static DEFINE_MUTEX(ioctl_mutex); struct proc_entries { const char *name; @@ -258,7 +258,7 @@ long err; u32 id; - err = down_interruptible(&ioctl_sem); + err = mutex_lock_interruptible(&ioctl_mutex); if (err < 0) return err; @@ -339,7 +339,7 @@ target_unlock(target); done: - up(&ioctl_sem); + mutex_unlock(&ioctl_mutex); return err; } @@ -347,9 +347,9 @@ static int release(struct inode *i __attribute__((unused)), struct file *f __attribute__((unused))) { - down(&ioctl_sem); + mutex_lock(&ioctl_mutex); target_del_all(); - up(&ioctl_sem); + mutex_unlock(&ioctl_mutex); return 0; } Modified: trunk/kernel/iscsi.h =================================================================== --- trunk/kernel/iscsi.h 2011-01-10 22:35:06 UTC (rev 377) +++ trunk/kernel/iscsi.h 2011-01-12 20:39:36 UTC (rev 378) @@ -130,7 +130,7 @@ /* Points either to own list or global pool */ struct worker_thread_info * wthread_info; - struct semaphore target_sem; + struct mutex target_mutex; }; struct iscsi_queue { Modified: trunk/kernel/target.c =================================================================== --- trunk/kernel/target.c 2011-01-10 22:35:06 UTC (rev 377) +++ trunk/kernel/target.c 2011-01-12 20:39:36 UTC (rev 378) @@ -4,6 +4,8 @@ * Released under the terms of the GNU GPL v2.0. */ +#include <linux/mutex.h> + #include "iscsi.h" #include "digest.h" #include "iscsi_dbg.h" @@ -11,7 +13,7 @@ #define MAX_NR_TARGETS (1UL << 30) static LIST_HEAD(target_list); -static DECLARE_MUTEX(target_list_sem); +static DEFINE_MUTEX(target_list_mutex); static u32 next_target_id; static u32 nr_targets; @@ -48,16 +50,16 @@ int err = 0; if (interruptible) - err = down_interruptible(&target->target_sem); + err = mutex_lock_interruptible(&target->target_mutex); else - down(&target->target_sem); + mutex_lock(&target->target_mutex); return err; } inline void target_unlock(struct iscsi_target *target) { - up(&target->target_sem); + mutex_unlock(&target->target_mutex); } static struct iscsi_target *__target_lookup_by_id(u32 id) @@ -86,9 +88,9 @@ { struct iscsi_target *target; - down(&target_list_sem); + mutex_lock(&target_list_mutex); target = __target_lookup_by_id(id); - up(&target_list_sem); + mutex_unlock(&target_list_mutex); return target; } @@ -157,7 +159,7 @@ strncpy(target->name, name, sizeof(target->name) - 1); - init_MUTEX(&target->target_sem); + mutex_init(&target->target_mutex); spin_lock_init(&target->session_list_lock); INIT_LIST_HEAD(&target->session_list); @@ -195,7 +197,7 @@ u32 tid = info->tid; int err; - err = down_interruptible(&target_list_sem); + err = mutex_lock_interruptible(&target_list_mutex); if (err < 0) return err; @@ -204,7 +206,7 @@ goto out; } - if (__target_lookup_by_name(info->name) || + if (__target_lookup_by_name(info->name) || (tid && __target_lookup_by_id(tid))) { err = -EEXIST; goto out; @@ -223,7 +225,7 @@ if (!err) nr_targets++; out: - up(&target_list_sem); + mutex_unlock(&target_list_mutex); return err; } @@ -248,7 +250,7 @@ module_put(THIS_MODULE); } -/* @locking: target_list_sem must be locked */ +/* @locking: target_list_mutex must be locked */ static int __target_del(struct iscsi_target *target) { int err; @@ -283,7 +285,7 @@ struct iscsi_target *target; int err; - err = down_interruptible(&target_list_sem); + err = mutex_lock_interruptible(&target_list_mutex); if (err < 0) return err; @@ -295,7 +297,7 @@ err = __target_del(target); out: - up(&target_list_sem); + mutex_unlock(&target_list_mutex); return err; } @@ -305,7 +307,7 @@ struct iscsi_target *target, *tmp; int err; - down(&target_list_sem); + mutex_lock(&target_list_mutex); if (!list_empty(&target_list)) iprintk("Removing all connections, sessions and targets\n"); @@ -319,7 +321,7 @@ next_target_id = 0; - up(&target_list_sem); + mutex_unlock(&target_list_mutex); } static void *iet_seq_start(struct seq_file *m, loff_t *pos) @@ -327,7 +329,7 @@ int err; /* are you sure this is to be interruptible? */ - err = down_interruptible(&target_list_sem); + err = mutex_lock_interruptible(&target_list_mutex); if (err < 0) return ERR_PTR(err); @@ -341,7 +343,7 @@ static void iet_seq_stop(struct seq_file *m, void *v) { - up(&target_list_sem); + mutex_unlock(&target_list_mutex); } static int iet_seq_show(struct seq_file *m, void *p) Modified: trunk/kernel/volume.c =================================================================== --- trunk/kernel/volume.c 2011-01-10 22:35:06 UTC (rev 377) +++ trunk/kernel/volume.c 2011-01-12 20:39:36 UTC (rev 378) @@ -6,6 +6,7 @@ #include <linux/types.h> #include <linux/parser.h> +#include <linux/blkdev.h> #include "iscsi.h" #include "iscsi_dbg.h" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <rsw...@us...> - 2011-03-16 16:46:07
|
Revision: 413 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=413&view=rev Author: rswwalker Date: 2011-03-16 16:46:01 +0000 (Wed, 16 Mar 2011) Log Message: ----------- This patch fixes the handling of outstanding R2Ts by using r2t_length to account for bytes not yet solicited instead of bytes not yet received. It keeps track of the bytes received by using a new counter, exp_offset, so partially received R2Ts don't cause the command to be queued. It also keeps the scsi cmnd from being queued while there are outstanding R2Ts and makes sure that no more then max outstanding R2Ts are outstanding at once. This patch supersedes my earlier two revisions. Compile tested on CentOS5 Signed-off-by: Ross Walker Modified Paths: -------------- trunk/kernel/iscsi.c trunk/kernel/iscsi.h Modified: trunk/kernel/iscsi.c =================================================================== --- trunk/kernel/iscsi.c 2011-03-12 12:30:24 UTC (rev 412) +++ trunk/kernel/iscsi.c 2011-03-16 16:46:01 UTC (rev 413) @@ -224,12 +224,12 @@ LIST_HEAD(head); if (!list_empty(&cmnd->list)) { - eprintk("%x %x %x %x %lx %u %u %u %u %u %u %u %d %d\n", + eprintk("%x %x %x %x %lx %u %u %u %u %u %u %u %u %d %d\n", cmnd_itt(cmnd), cmnd_ttt(cmnd), cmnd_opcode(cmnd), cmnd_scsicode(cmnd), cmnd->flags, cmnd->r2t_sn, - cmnd->r2t_length, cmnd->is_unsolicited_data, - cmnd->target_task_tag, cmnd->outstanding_r2t, - cmnd->hdigest, cmnd->ddigest, + cmnd->r2t_length, cmnd->exp_offset, + cmnd->is_unsolicited_data, cmnd->target_task_tag, + cmnd->outstanding_r2t, cmnd->hdigest, cmnd->ddigest, list_empty(&cmnd->pdu_list), list_empty(&cmnd->hash_list)); assert(list_empty(&cmnd->list)); @@ -797,16 +797,19 @@ static void send_r2t(struct iscsi_cmnd *req) { + struct iscsi_sess_param *param = &req->conn->session->param; struct iscsi_cmnd *rsp; struct iscsi_r2t_hdr *rsp_hdr; - u32 length, offset, burst; + u32 offset, burst; LIST_HEAD(send); - length = req->r2t_length; - burst = req->conn->session->param.max_burst_length; - offset = be32_to_cpu(cmnd_hdr(req)->data_length) - length; + if (req->outstanding_r2t >= param->max_outstanding_r2t) + return; - do { + burst = param->max_burst_length; + offset = cmnd_write_size(req) - req->r2t_length; + + while (req->r2t_length) { rsp = iscsi_cmnd_create_rsp_cmnd(req, 0); rsp->pdu.bhs.ttt = req->target_task_tag; @@ -817,13 +820,13 @@ rsp_hdr->itt = cmnd_hdr(req)->itt; rsp_hdr->r2t_sn = cpu_to_be32(req->r2t_sn++); rsp_hdr->buffer_offset = cpu_to_be32(offset); - if (length > burst) { + if (req->r2t_length > burst) { rsp_hdr->data_length = cpu_to_be32(burst); - length -= burst; + req->r2t_length -= burst; offset += burst; } else { - rsp_hdr->data_length = cpu_to_be32(length); - length = 0; + rsp_hdr->data_length = cpu_to_be32(req->r2t_length); + req->r2t_length = 0; } dprintk(D_WRITE, "%x %u %u %u %u\n", cmnd_itt(req), @@ -833,17 +836,17 @@ list_add_tail(&rsp->list, &send); - if (++req->outstanding_r2t >= req->conn->session->param.max_outstanding_r2t) + if (++req->outstanding_r2t >= param->max_outstanding_r2t) break; + } - } while (length); - - iscsi_cmnds_init_write(&send); + if (!list_empty(&send)) + iscsi_cmnds_init_write(&send); } static void scsi_cmnd_exec(struct iscsi_cmnd *cmnd) { - assert(!cmnd->r2t_length); + assert(!(cmnd->r2t_length || cmnd->outstanding_r2t)); if (cmnd->lun) { iscsi_scsi_queuecmnd(cmnd); @@ -1012,7 +1015,8 @@ loff_t offset; u32 length; - req->r2t_length = be32_to_cpu(req_hdr->data_length) - req->pdu.datasize; + req->exp_offset = req->pdu.datasize; + req->r2t_length = cmnd_write_size(req) - req->pdu.datasize; req->is_unsolicited_data = !(req_hdr->flags & ISCSI_CMD_FINAL); req->target_task_tag = get_next_ttt(conn->session); @@ -1058,6 +1062,7 @@ static void data_out_start(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd) { + struct iscsi_sess_param *param = &conn->session->param; struct iscsi_data_out_hdr *req = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs; struct iscsi_cmnd *scsi_cmnd = NULL; u32 offset = be32_to_cpu(req->buffer_offset); @@ -1071,27 +1076,31 @@ goto skip_data; } - if (scsi_cmnd->r2t_length < cmnd->pdu.datasize) { - eprintk("invalid data len %x %u %u\n", - cmnd_itt(scsi_cmnd), cmnd->pdu.datasize, scsi_cmnd->r2t_length); + if (param->data_pdu_inorder && offset != scsi_cmnd->exp_offset) { + eprintk("invalid data offset %x %u %u\n", + cmnd_itt(scsi_cmnd), offset, cmnd->exp_offset); goto skip_data; } - if (scsi_cmnd->r2t_length + offset != cmnd_write_size(scsi_cmnd)) { - eprintk("%x %u %u %u\n", cmnd_itt(scsi_cmnd), scsi_cmnd->r2t_length, - offset, cmnd_write_size(scsi_cmnd)); + if (offset + cmnd->pdu.datasize > cmnd_write_size(scsi_cmnd)) { + eprintk("invalid data length %x %u %u\n", + cmnd_itt(scsi_cmnd),(offset + cmnd->pdu.datasize), + cmnd_write_size(scsi_cmnd)); goto skip_data; } - scsi_cmnd->r2t_length -= cmnd->pdu.datasize; - - if (req->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) { - /* unsolicited burst data */ - if (scsi_cmnd->pdu.bhs.flags & ISCSI_FLG_FINAL) { - eprintk("unexpected data from %x %x\n", + if (scsi_cmnd->is_unsolicited_data) { + if (offset + cmnd->pdu.datasize > param->first_burst_length) { + eprintk("unsolicited data > first burst length %x %x\n", cmnd_itt(cmnd), cmnd_ttt(cmnd)); goto skip_data; } + } else { + if (req->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) { + eprintk("unexpected unsolicited data %x %x\n", + cmnd_itt(cmnd), cmnd_ttt(cmnd)); + goto skip_data; + } } dprintk(D_WRITE, "%u %p %p %p %u %u\n", req->ttt, cmnd, scsi_cmnd, @@ -1099,6 +1108,12 @@ if (cmnd_recv_pdu(conn, scsi_cmnd->tio, offset, cmnd->pdu.datasize) < 0) goto skip_data; + + if (scsi_cmnd->is_unsolicited_data) + scsi_cmnd->r2t_length -= cmnd->pdu.datasize; + + scsi_cmnd->exp_offset += cmnd->pdu.datasize; + return; skip_data: @@ -1120,7 +1135,8 @@ assert(scsi_cmnd); if (conn->read_overflow) { - eprintk("%x %u\n", cmnd_itt(cmnd), conn->read_overflow); + eprintk("connection read overflow %x %u\n", + cmnd_itt(cmnd), conn->read_overflow); assert(scsi_cmnd->tio); offset = be32_to_cpu(req->buffer_offset); offset += cmnd->pdu.datasize - conn->read_overflow; @@ -1129,28 +1145,18 @@ return; } - if (req->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) { - if (req->flags & ISCSI_FLG_FINAL) { + if (req->flags & ISCSI_FLG_FINAL) { + if (req->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) scsi_cmnd->is_unsolicited_data = 0; - iscsi_session_push_cmnd(scsi_cmnd); - } - } else { - /* TODO : proper error handling */ - if (!(req->flags & ISCSI_FLG_FINAL) && scsi_cmnd->r2t_length == 0) - eprintk("initiator error %x\n", cmnd_itt(scsi_cmnd)); + else + scsi_cmnd->outstanding_r2t--; - if (!(req->flags & ISCSI_FLG_FINAL)) - goto out; - - scsi_cmnd->outstanding_r2t--; - - if (scsi_cmnd->r2t_length == 0) + if (scsi_cmnd->outstanding_r2t == 0) assert(list_empty(&scsi_cmnd->pdu_list)); iscsi_session_push_cmnd(scsi_cmnd); } -out: iscsi_cmnd_remove(cmnd); return; } @@ -1755,6 +1761,9 @@ return; } + if (cmnd->outstanding_r2t) + return; + dprintk(D_GENERIC, "%p:%x %u,%u\n", cmnd, cmnd_opcode(cmnd), cmnd->pdu.bhs.sn, session->exp_cmd_sn); Modified: trunk/kernel/iscsi.h =================================================================== --- trunk/kernel/iscsi.h 2011-03-12 12:30:24 UTC (rev 412) +++ trunk/kernel/iscsi.h 2011-03-16 16:46:01 UTC (rev 413) @@ -296,6 +296,7 @@ u32 r2t_sn; u32 r2t_length; + u32 exp_offset; u32 is_unsolicited_data; u32 target_task_tag; u32 outstanding_r2t; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <rsw...@us...> - 2011-03-31 16:52:44
|
Revision: 440 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=440&view=rev Author: rswwalker Date: 2011-03-31 16:52:38 +0000 (Thu, 31 Mar 2011) Log Message: ----------- Through iSCSI conformance tests it was found that IET currently doesn't support zero length EDTL, SPDTL and data segments. This fix bypasses do_send_data_rsp() if EDTL or SPDTL is zero so only the SCSI response is sent, cmnd_recv_pdu() returns success for zero length data segments, tio_read/write short circuits zero length read and write operations in case io types don't support them. Compile tested on CentOS5 Signed-off-by: Ross Walker Modified Paths: -------------- trunk/kernel/iscsi.c trunk/kernel/tio.c Modified: trunk/kernel/iscsi.c =================================================================== --- trunk/kernel/iscsi.c 2011-03-31 16:41:51 UTC (rev 439) +++ trunk/kernel/iscsi.c 2011-03-31 16:52:38 UTC (rev 440) @@ -384,10 +384,14 @@ void send_data_rsp(struct iscsi_cmnd *req, void (*func)(struct iscsi_cmnd *)) { struct iscsi_cmnd *rsp; + u32 datasize = 0; func(req); - if (req->status == SAM_STAT_GOOD) + if (req->tio) + datasize = min(req->tio->size, cmnd_read_size(req)); + + if (req->status == SAM_STAT_GOOD && datasize) do_send_data_rsp(req); else { rsp = create_scsi_rsp(req); @@ -695,6 +699,9 @@ dprintk(D_GENERIC, "%p %u,%u\n", tio, offset, size); offset += tio->offset; + if (!size) + return 0; + if (!(offset < tio->offset + tio->size) || !(offset + size <= tio->offset + tio->size)) { eprintk("%u %u %u %u", offset, size, tio->offset, tio->size); Modified: trunk/kernel/tio.c =================================================================== --- trunk/kernel/tio.c 2011-03-31 16:41:51 UTC (rev 439) +++ trunk/kernel/tio.c 2011-03-31 16:52:38 UTC (rev 440) @@ -91,6 +91,8 @@ { struct iotype *iot = lu->iotype; assert(iot); + if (!tio->size) + return 0; return iot->make_request ? iot->make_request(lu, tio, READ) : 0; } @@ -98,6 +100,8 @@ { struct iotype *iot = lu->iotype; assert(iot); + if (!tio->size) + return 0; return iot->make_request ? iot->make_request(lu, tio, WRITE) : 0; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <rsw...@us...> - 2011-04-14 21:55:31
|
Revision: 446 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=446&view=rev Author: rswwalker Date: 2011-04-14 21:55:25 +0000 (Thu, 14 Apr 2011) Log Message: ----------- Through iSCSI conformance tests it was determined that IET doesn't handle iSCSI logout requests properly. Revision 2 - tightened up connection logout for different cid, make sure connection exists in session, make sure connection isn't active Compile tested on CentOS5 Conformance tested with iSWAT Signed-off-by: Ross Walker Revision Links: -------------- http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=2&view=rev Modified Paths: -------------- trunk/kernel/iscsi.c trunk/kernel/iscsi.h trunk/kernel/iscsi_hdr.h Modified: trunk/kernel/iscsi.c =================================================================== --- trunk/kernel/iscsi.c 2011-04-14 21:52:30 UTC (rev 445) +++ trunk/kernel/iscsi.c 2011-04-14 21:55:25 UTC (rev 446) @@ -1529,6 +1529,8 @@ struct iscsi_logout_req_hdr *req_hdr; struct iscsi_cmnd *rsp; struct iscsi_logout_rsp_hdr *rsp_hdr; + struct iscsi_conn *conn; + u8 reason; req_hdr = (struct iscsi_logout_req_hdr *)&req->pdu.bhs; rsp = iscsi_cmnd_create_rsp_cmnd(req, 1); @@ -1536,7 +1538,30 @@ rsp_hdr->opcode = ISCSI_OP_LOGOUT_RSP; rsp_hdr->flags = ISCSI_FLG_FINAL; rsp_hdr->itt = req_hdr->itt; - set_cmnd_close(rsp); + + reason = req_hdr->flags & ISCSI_FUNCTION_MASK; + + if (reason == ISCSI_LOGOUT_SESSION) + set_cmnd_closeit(rsp); + else if (reason == ISCSI_LOGOUT_CONNECTION) { + if (req_hdr->cid != req->conn->cid) { + conn = conn_lookup(req->conn->session, req_hdr->cid); + + if (!conn) + rsp_hdr->response = 1; + else if (test_bit(CONN_ACTIVE, &conn->state)) + rsp_hdr->response = 3; + else { + /* end time2wait timer for conn */ + } + } else + set_cmnd_close(rsp); + } else if (reason == ISCSI_LOGOUT_CONNECTION_RECOVER) + rsp_hdr->response = 2; + else + /* protocol error */ + conn_close(req->conn); + iscsi_cmnd_init_write(rsp); } @@ -1730,6 +1755,7 @@ void cmnd_tx_end(struct iscsi_cmnd *cmnd) { + struct iscsi_session *session = cmnd->conn->session; struct iscsi_conn *conn = cmnd->conn; dprintk(D_GENERIC, "%p:%x\n", cmnd, cmnd_opcode(cmnd)); @@ -1754,6 +1780,9 @@ if (cmnd_close(cmnd)) conn_close(conn); + else if (cmnd_closeit(cmnd)) + list_for_each_entry(conn, &session->conn_list, list) + conn_close(conn); list_del_init(&cmnd->list); set_cork(cmnd->conn->sock, 0); Modified: trunk/kernel/iscsi.h =================================================================== --- trunk/kernel/iscsi.h 2011-04-14 21:52:30 UTC (rev 445) +++ trunk/kernel/iscsi.h 2011-04-14 21:55:25 UTC (rev 446) @@ -478,6 +478,7 @@ CMND_final, CMND_waitio, CMND_close, + CMND_closeit, CMND_lunit, CMND_pending, CMND_tmfabort, @@ -500,6 +501,9 @@ #define set_cmnd_close(cmnd) set_bit(CMND_close, &(cmnd)->flags) #define cmnd_close(cmnd) test_bit(CMND_close, &(cmnd)->flags) +#define set_cmnd_closeit(cmnd) set_bit(CMND_closeit, &(cmnd)->flags) +#define cmnd_closeit(cmnd) test_bit(CMND_closeit, &(cmnd)->flags) + #define set_cmnd_lunit(cmnd) set_bit(CMND_lunit, &(cmnd)->flags) #define cmnd_lunit(cmnd) test_bit(CMND_lunit, &(cmnd)->flags) Modified: trunk/kernel/iscsi_hdr.h =================================================================== --- trunk/kernel/iscsi_hdr.h 2011-04-14 21:52:30 UTC (rev 445) +++ trunk/kernel/iscsi_hdr.h 2011-04-14 21:55:25 UTC (rev 446) @@ -428,6 +428,10 @@ u32 rsvd5; } __packed; +#define ISCSI_LOGOUT_SESSION 0 +#define ISCSI_LOGOUT_CONNECTION 1 +#define ISCSI_LOGOUT_CONNECTION_RECOVER 2 + struct iscsi_snack_req_hdr { u8 opcode; u8 flags; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2011-09-09 06:28:59
|
Revision: 456 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=456&view=rev Author: agr1 Date: 2011-09-09 06:28:52 +0000 (Fri, 09 Sep 2011) Log Message: ----------- Initial merge of support for SPC-3 Persistent Reserverations From: Shivaram Upadhyayula shi...@qu... Attached is a patch for SPC3 Persistent Reservations support. Most of the code is based on our storage virtualization product which i'm allowed to open source. Rest of the changes involved adapting the code for iscsi interface and IET code. Pending is "REGISTER AND MOVE" service action for a PERSISTENT RESERVE OUT command, i'm not yet sure on how to implement this for IET. Code with the patch applied successfully passed the Failover cluster tests for persistent reservations on Windows Server 2008 R2. Unrelated to persistent reservations, the failover cluster tests failed for SCSI VPD 83 page. It seems that naa identifier should fix that issue. Signed-off-by: Shivaram Upadhyayula <shi...@qu...> Revision Links: -------------- http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=2&view=rev Modified Paths: -------------- trunk/kernel/Makefile trunk/kernel/iscsi.c trunk/kernel/iscsi.h trunk/kernel/target_disk.c trunk/kernel/volume.c Added Paths: ----------- trunk/kernel/persist.c trunk/kernel/persist.h Modified: trunk/kernel/Makefile =================================================================== --- trunk/kernel/Makefile 2011-09-03 12:02:22 UTC (rev 455) +++ trunk/kernel/Makefile 2011-09-09 06:28:52 UTC (rev 456) @@ -13,5 +13,5 @@ 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 ua.o + block-io.o ua.o persist.o Modified: trunk/kernel/iscsi.c =================================================================== --- trunk/kernel/iscsi.c 2011-09-03 12:02:22 UTC (rev 455) +++ trunk/kernel/iscsi.c 2011-09-09 06:28:52 UTC (rev 456) @@ -792,7 +792,7 @@ *len <<= lu->blk_shift; } -static u32 translate_lun(u16 * data) +u32 translate_lun(u16 *data) { u8 *p = (u8 *) data; u32 lun = ~0U; @@ -996,6 +996,7 @@ case REQUEST_SENSE: case RESERVE: case RELEASE: + case PERSISTENT_RESERVE_IN: { if (!(req_hdr->flags & ISCSI_CMD_FINAL) || req->pdu.datasize) { /* unexpected unsolicited data */ @@ -1067,6 +1068,23 @@ } break; } + case PERSISTENT_RESERVE_OUT: + { + u16 length; + + length = READ_WORD(req_hdr->scb[7], req_hdr->scb[8]); + if (!length) + goto error; + + req->tio = tio_alloc(1); + tio_set(req->tio, length, 0); + if (req->pdu.datasize) { + if (cmnd_recv_pdu(conn, req->tio, 0, req->pdu.datasize) < 0) + assert(0); + } + break; + } + error: default: eprintk("Unsupported %x\n", req_hdr->scb[0]); @@ -1180,7 +1198,7 @@ return; } -static void __cmnd_abort(struct iscsi_cmnd *cmnd) +void __cmnd_abort(struct iscsi_cmnd *cmnd) { if (cmnd_rxstart(cmnd)) set_cmnd_tmfabort(cmnd); Modified: trunk/kernel/iscsi.h =================================================================== --- trunk/kernel/iscsi.h 2011-09-03 12:02:22 UTC (rev 455) +++ trunk/kernel/iscsi.h 2011-09-09 06:28:52 UTC (rev 456) @@ -20,6 +20,7 @@ #include "iscsi_hdr.h" #include "iet_u.h" #include "compat.h" +#include "persist.h" #define IET_SENSE_BUF_SIZE 18 @@ -157,7 +158,7 @@ u32 blk_shift; u64 blk_cnt; - u64 reserve_sid; + struct reservation reservation; spinlock_t reserve_lock; unsigned long flags; @@ -212,6 +213,7 @@ struct list_head ua_hash[UA_HASH_LEN]; u32 next_ttt; + }; enum connection_state_bit { @@ -335,6 +337,8 @@ extern void iscsi_cmnd_set_sense(struct iscsi_cmnd *, u8 sense_key, u8 asc, u8 ascq); extern void send_nop_in(struct iscsi_conn *); +extern u32 translate_lun(u16 *data); +extern void __cmnd_abort(struct iscsi_cmnd *cmnd); /* conn.c */ extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16); @@ -391,7 +395,7 @@ extern void volume_put(struct iet_volume *); extern int volume_reserve(struct iet_volume *volume, u64 sid); extern int volume_release(struct iet_volume *volume, u64 sid, int force); -extern int is_volume_reserved(struct iet_volume *volume, u64 sid); +extern int is_volume_reserved(struct iet_volume *volume, u64 sid, u8 *scb); /* tio.c */ extern int tio_init(void); Added: trunk/kernel/persist.c =================================================================== --- trunk/kernel/persist.c (rev 0) +++ trunk/kernel/persist.c 2011-09-09 06:28:52 UTC (rev 456) @@ -0,0 +1,731 @@ +/* + * Copyright (C) 2011 Shivaram U, shi...@qu... + * + * Released under the terms of the GNU GPL v2.0. + */ + +#include <scsi/scsi.h> +#include "iscsi.h" +#include "iscsi_dbg.h" +#include "persist.h" + +static void +persistent_reservation_read_capabilities(struct iscsi_cmnd *cmnd, + u16 allocation_length) +{ + struct reservation_capabilities cap; + u16 min_len; + struct tio *tio; + u8 *data; + + memset(&cap, 0, sizeof(cap)); + + cap.length = cpu_to_be16(8); + cap.tmv |= 0x80; /* TMV */ + cap.tmask1 |= 0x2; /* WR_EX/Write Exclusive access */ + cap.tmask1 |= 0x8; /* EX_AC/Exclusive access */ + cap.tmask1 |= 0x20; /* WR_EX_RO/Write Exclusive Registrants Only */ + cap.tmask1 |= 0x40; /* EX_AC_RO/Exclusive Access Registrants Only */ + min_len = min_t(u16, allocation_length, sizeof(cap)); + tio = cmnd->tio = tio_alloc(1); + data = page_address(tio->pvec[0]); + assert(data); + memcpy(data, &cap, min_len); + tio_set(tio, min_len, 0); +} + +static void +persistent_reservation_read_reservations(struct iscsi_cmnd *cmnd, + u16 allocation_length) +{ + u16 done = 0; + u8 *data; + struct pin_data *pin_data; + struct tio *tio; + struct iet_volume *volume = cmnd->lun; + struct reservation *reservation = &volume->reservation; + + + tio = cmnd->tio = tio_alloc(1); + data = page_address(tio->pvec[0]); + assert(data); + + spin_lock(&volume->reserve_lock); + *((u32 *)data) = cpu_to_be32(reservation->generation); + + done = 8; + if (reservation->is_reserved) { + pin_data = (struct pin_data *)(data + 8); + memset(pin_data, 0, sizeof(struct pin_data)); + pin_data->reservation_key = cpu_to_be64(reservation->reservation_key); + pin_data->type = reservation->persistent_type; + done += sizeof(struct pin_data); + } + spin_unlock(&volume->reserve_lock); + + *((u32 *)(data + 4)) = cpu_to_be32(done - 8); + done = min_t(u16, done, allocation_length); + tio_set(tio, done, 0); +} + +/* + * For writes that may span page boundaries + * Assumption is that len is not greater than PAGE_SIZE + */ +static void +tio_write_buffer(struct tio *tio, u8 *data, int len, + int *write_idx, int *write_offset) +{ + int idx = *write_idx, offset = *write_offset; + int min_len; + u8 *ptr; + + ptr = page_address(tio->pvec[idx]); + min_len = min_t(int, (PAGE_SIZE - offset), len); + + memcpy(ptr + offset, data, min_len); + len -= min_len; + if (!len) { + offset += min_len; + if (offset == PAGE_SIZE) { + *write_idx = idx + 1; + offset = 0; + } + *write_offset = offset; + return; + } + + /* Copy the remaining from the next page */ + data += min_len; + idx++; + ptr = page_address(tio->pvec[idx]); + memcpy(ptr, data, len); + *write_idx = idx; + *write_offset = len; +} + +static void +persistent_reservation_read_full(struct iscsi_cmnd *cmnd, + u16 allocation_length) +{ + struct list_head *l; + struct registration *iter; + u16 done; + u8 *data; + u16 desc_size; + u16 transport_size; + struct pfull_data pfull; + struct iscsi_session *session = cmnd->conn->session; + struct iet_volume *volume = cmnd->lun; + struct reservation *reservation = &volume->reservation; + struct tio *tio; + int idx = 0, offset = 0; + struct transport_id_common tid; + + tio = cmnd->tio = tio_alloc(get_pgcnt(allocation_length, 0)); + + data = page_address(tio->pvec[0]); + + spin_lock(&volume->reserve_lock); + *((u32 *)data) = cpu_to_be32(reservation->generation); + + done = 8; + offset = 8; + list_for_each(l, &reservation->registration_list) { + iter = list_entry(l, struct registration, r_list); + + desc_size = sizeof(struct pfull_data); + transport_size = sizeof(struct transport_id_common) + + strlen(iter->init_name); + desc_size += transport_size; + if ((done + desc_size) > allocation_length) { + done += desc_size; + continue; + } + + memset(&pfull, 0, sizeof(pfull)); + pfull.reservation_key = cpu_to_be64(iter->reservation_key); + pfull.addl_len = cpu_to_be16(transport_size); + + if (reservation->is_reserved + && reservation->sid == session->sid) { + pfull.r_holder |= 0x01; + pfull.type = reservation->persistent_type; + } + + pfull.rel_tgt_port = cpu_to_be16(1); /* rel tgt port set to 1 */ + tio_write_buffer(tio, (u8 *)&pfull, + sizeof(pfull), &idx, &offset); + + done += sizeof(struct pfull_data); + memset(&tid, 0, sizeof(tid)); + tid.protocol_id = 0x05; + tid.addl_len = cpu_to_be16(transport_size - sizeof(struct transport_id_common)); + tio_write_buffer(tio, (u8 *)&tid, + sizeof(tid), &idx, &offset); + done += sizeof(struct transport_id_common); + tio_write_buffer(tio, iter->init_name, + strlen(iter->init_name), &idx, &offset); + done += strlen(iter->init_name); + } + spin_unlock(&volume->reserve_lock); + + *((u32 *)(data + 4)) = cpu_to_be32(done - 8); + done = min_t(u16, done, allocation_length); + tio_set(tio, done, 0); +} + +static void +persistent_reservation_read_keys(struct iscsi_cmnd *cmnd, + u16 allocation_length) +{ + struct list_head *l; + struct registration *registration; + u16 done; + u8 *data; + u8 key_size = 8; + struct iet_volume *volume = cmnd->lun; + struct reservation *reservation = &volume->reservation; + struct tio *tio; + u64 reservation_key; + int idx = 0, offset; + + tio = cmnd->tio = tio_alloc(get_pgcnt(allocation_length, 0)); + data = page_address(tio->pvec[0]); + + spin_lock(&volume->reserve_lock); + *((u32 *)data) = cpu_to_be32(reservation->generation); + + done = 8; + offset = 8; + list_for_each(l, &reservation->registration_list) { + registration = list_entry(l, struct registration, r_list); + + if ((done + key_size) > allocation_length) { + done += key_size; + continue; + } + + reservation_key = cpu_to_be64(registration->reservation_key); + tio_write_buffer(tio, (u8 *)&reservation_key, + sizeof(reservation_key), &idx, &offset); + done += key_size; + } + spin_unlock(&volume->reserve_lock); + + *((u32 *)(data + 4)) = cpu_to_be32(done - 8); + done = min_t(u16, done, allocation_length); + tio_set(tio, done, 0); +} + +static int +initiator_has_registered(struct list_head *registration_list, + u64 reservation_key, u64 sid, + struct registration **ret) +{ + struct list_head *l; + struct registration *iter; + + list_for_each(l, registration_list) { + iter = list_entry(l, struct registration, r_list); + if (iter->sid == sid) { + if (ret) + *ret = iter; + return 1; + } + } + return 0; +} + +static void +persistent_reservation_handle_register(struct iscsi_cmnd *cmnd, int ignore) +{ + struct registration *iter, *registration; + struct list_head *l; + struct reservation_parameter *param; + u64 service_action_key; + u64 reservation_key; + int count = 0; + u8 *data; + u8 spec_i_pt; + struct iscsi_session *session = cmnd->conn->session; + struct iet_volume *volume = cmnd->lun; + struct reservation *reservation = &volume->reservation; + struct tio *tio = cmnd->tio; + + data = page_address(tio->pvec[0]); + param = (struct reservation_parameter *)(data); + reservation_key = be64_to_cpu(param->reservation_key); + service_action_key = be64_to_cpu(param->service_action_key); + spec_i_pt = (param->aptpl >> 3) & 0x1; + + registration = kzalloc(sizeof(*registration), GFP_KERNEL|__GFP_NOFAIL); + spin_lock(&volume->reserve_lock); + list_for_each(l, &reservation->registration_list) { + iter = list_entry(l, struct registration, r_list); + count++; + + if (iter->sid != session->sid) + continue; + + if (!ignore && reservation_key != iter->reservation_key) { + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, + INVALID_COMMAND_OPERATION_CODE_ASC, + INVALID_COMMAND_OPERATION_CODE_ASCQ); + goto out; + } + + if (spec_i_pt) { + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); + goto out; + } + + if (!service_action_key) { + if (reservation->is_reserved + && session->sid == reservation->sid) { + reservation->is_reserved = 0; + reservation->type = 0; + reservation->persistent_type = 0; + reservation->reservation_key = 0; + ua_establish_for_other_sessions(session, + volume->lun, + RESERVATIONS_RELEASED_ASC, + RESERVATIONS_RELEASED_ASCQ); + } + list_del(l); + kfree(iter); + } else { + iter->reservation_key = service_action_key; + } + reservation->generation++; + goto out; + } + + if (!reservation_key && !service_action_key) { + reservation->generation++; + goto out; + } + + if (reservation_key) { + cmnd->status = SAM_STAT_RESERVATION_CONFLICT; + goto out; + } + + if (count >= PERSISTENT_RESERVE_MAX_REGISTRATIONS) { + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, + INSUFFICIENT_REGISTRATION_RESOURCES_ASC, + INSUFFICIENT_REGISTRATION_RESOURCES_ASCQ); + goto out; + } + + registration->sid = session->sid; + registration->reservation_key = service_action_key; + strcpy(registration->init_name, cmnd->conn->session->initiator); + list_add_tail(®istration->r_list, &reservation->registration_list); + reservation->generation++; + spin_unlock(&volume->reserve_lock); + return; +out: + kfree(registration); + spin_unlock(&volume->reserve_lock); +} + +static int +persistent_type_valid(int type) +{ + switch (type) { + case RESERVATION_TYPE_WRITE_EXCLUSIVE: + case RESERVATION_TYPE_EXCLUSIVE_ACCESS: + case RESERVATION_TYPE_WRITE_EXCLUSIVE_RO: + case RESERVATION_TYPE_EXCLUSIVE_ACCESS_RO: + return 1; + } + return 0; +} + +static void +persistent_reservation_handle_reserve(struct iscsi_cmnd *cmnd, u8 type) +{ + struct reservation_parameter *param; + u64 reservation_key; + int retval; + u8 *data; + struct iscsi_session *session = cmnd->conn->session; + struct iet_volume *volume = cmnd->lun; + struct reservation *reservation = &volume->reservation; + struct tio *tio = cmnd->tio; + + data = page_address(tio->pvec[0]); + param = (struct reservation_parameter *)(data); + reservation_key = be64_to_cpu(param->reservation_key); + + spin_lock(&volume->reserve_lock); + retval = initiator_has_registered(&reservation->registration_list, + reservation_key, session->sid, NULL); + if (!retval) { + cmnd->status = SAM_STAT_RESERVATION_CONFLICT; + goto out; + } + + if (reservation->is_reserved && reservation->sid != session->sid) { + cmnd->status = SAM_STAT_RESERVATION_CONFLICT; + goto out; + } + + if (reservation->is_reserved + && reservation->reservation_key != reservation_key) { + cmnd->status = SAM_STAT_RESERVATION_CONFLICT; + goto out; + } + + if (reservation->is_reserved && reservation->persistent_type != type) { + cmnd->status = SAM_STAT_RESERVATION_CONFLICT; + goto out; + } + + if (reservation->is_reserved) /* No change */ + goto out; + + if (!persistent_type_valid(type)) { + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); + goto out; + } + + reservation->is_reserved = 1; + reservation->type = RESERVATION_TYPE_PERSISTENT; + reservation->persistent_type = type; + reservation->reservation_key = reservation_key; + reservation->sid = session->sid; +out: + spin_unlock(&volume->reserve_lock); +} + +static void +persistent_reservation_handle_release(struct iscsi_cmnd *cmnd, + u8 type, u8 scope) +{ + struct reservation_parameter *param; + u64 reservation_key; + int retval; + u8 *data; + struct iscsi_session *session = cmnd->conn->session; + struct iet_volume *volume = cmnd->lun; + struct reservation *reservation = &volume->reservation; + struct tio *tio = cmnd->tio; + int send_ua; + + data = page_address(tio->pvec[0]); + param = (struct reservation_parameter *)(data); + reservation_key = be64_to_cpu(param->reservation_key); + + spin_lock(&volume->reserve_lock); + if (!reservation->is_reserved) + goto out; + + retval = initiator_has_registered(&reservation->registration_list, + reservation_key, session->sid, NULL); + if (!retval) { + cmnd->status = SAM_STAT_RESERVATION_CONFLICT; + goto out; + } + + if (reservation->sid != session->sid) + goto out; + + if (reservation->reservation_key != reservation_key) { + cmnd->status = SAM_STAT_RESERVATION_CONFLICT; + goto out; + } + + if (scope || (reservation->persistent_type != type)) { + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, + INVALID_RELEASE_OF_PERSISTENT_RESERVATION_ASC, + INVALID_RELEASE_OF_PERSISTENT_RESERVATION_ASCQ); + goto out; + } + + switch (reservation->persistent_type) { + case RESERVATION_TYPE_WRITE_EXCLUSIVE_RO: + case RESERVATION_TYPE_EXCLUSIVE_ACCESS_RO: + send_ua = 1; + break; + default: + send_ua = 0; + } + + reservation->is_reserved = 0; + reservation->type = 0; + reservation->persistent_type = 0; + reservation->reservation_key = 0; + if (send_ua) + ua_establish_for_other_sessions(session, volume->lun, + RESERVATIONS_RELEASED_ASC, + RESERVATIONS_RELEASED_ASCQ); +out: + spin_unlock(&volume->reserve_lock); +} + +static void +persistent_reservation_handle_clear(struct iscsi_cmnd *cmnd) +{ + struct reservation_parameter *param; + u64 reservation_key; + int retval; + u8 *data; + struct iscsi_session *tmp_session, *session = cmnd->conn->session; + struct iscsi_target *target = session->target; + struct iet_volume *volume = cmnd->lun; + struct list_head *l, *n; + struct reservation *reservation = &volume->reservation; + struct registration *iter; + struct tio *tio = cmnd->tio; + + data = page_address(tio->pvec[0]); + param = (struct reservation_parameter *)(data); + reservation_key = be64_to_cpu(param->reservation_key); + + spin_lock(&volume->reserve_lock); + retval = initiator_has_registered(&reservation->registration_list, + reservation_key, session->sid, NULL); + if (!retval) { + cmnd->status = SAM_STAT_RESERVATION_CONFLICT; + goto out; + } + + list_for_each_safe(l, n, &reservation->registration_list) { + iter = list_entry(l, struct registration, r_list); + if (iter->sid != session->sid) { + tmp_session = session_lookup(target, iter->sid); + if (tmp_session) + ua_establish_for_session(session, volume->lun, + RESERVATIONS_PREEMPTED_ASC, + RESERVATIONS_PREEMPTED_ASCQ); + } + list_del(l); + kfree(iter); + } + + reservation->is_reserved = 0; + reservation->type = 0; + reservation->persistent_type = 0; + reservation->reservation_key = 0; + reservation->generation++; +out: + spin_unlock(&volume->reserve_lock); +} + +static void +session_abort_tasks(struct iscsi_session *session, u32 lun) +{ + struct iscsi_conn *conn; + struct iscsi_cmnd *cmnd, *tmp; + + list_for_each_entry(conn, &session->conn_list, list) { + list_for_each_entry_safe(cmnd, tmp, &conn->pdu_list, conn_list) { + if (translate_lun(cmnd_hdr(cmnd)->lun) != lun) + continue; + + __cmnd_abort(cmnd); + } + } +} + +static void +persistent_reservation_handle_preempt(struct iscsi_cmnd *cmnd, + u8 type, int abort) +{ + struct registration *iter; + struct list_head *l, *n; + struct reservation_parameter *param; + u64 reservation_key; + u64 service_action_key; + int retval; + u8 *data; + struct iscsi_session *session = cmnd->conn->session; + struct iscsi_session *reserv_session; + struct iscsi_target *target = session->target; + struct iet_volume *volume = cmnd->lun; + struct reservation *reservation = &volume->reservation; + struct tio *tio = cmnd->tio; + int all = 0; + + data = page_address(tio->pvec[0]); + param = (struct reservation_parameter *)(data); + reservation_key = be64_to_cpu(param->reservation_key); + service_action_key = be64_to_cpu(param->service_action_key); + + if (!service_action_key) { + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, + INVALID_FIELD_IN_PARAMETER_LIST_ASC, + INVALID_FIELD_IN_PARAMETER_LIST_ASCQ); + return; + } + + spin_lock(&volume->reserve_lock); + retval = initiator_has_registered(&reservation->registration_list, + reservation_key, session->sid, NULL); + if (!retval) { + cmnd->status = SAM_STAT_RESERVATION_CONFLICT; + goto out; + } + + if (reservation->is_reserved + && reservation->reservation_key == service_action_key + && reservation->sid != session->sid) { + if (!persistent_type_valid(type)) { + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); + goto out; + } + + reserv_session = session_lookup(target, reservation->sid); + if (reserv_session) { + if (abort) + session_abort_tasks(reserv_session, volume->lun); + ua_establish_for_session(reserv_session, volume->lun, + RESERVATIONS_PREEMPTED_ASC, + RESERVATIONS_PREEMPTED_ASCQ); + } + + reservation->type = type; + reservation->sid = session->sid; + reservation->reservation_key = reservation_key; + reservation->persistent_type = type; + all = 1; + } + + list_for_each_safe(l, n, &reservation->registration_list) { + iter = list_entry(l, struct registration, r_list); + + if (iter->sid == session->sid) + continue; + + if (!all && iter->reservation_key != service_action_key) + continue; + + reserv_session = session_lookup(target, iter->sid); + if (reserv_session) + ua_establish_for_session(reserv_session, volume->lun, + REGISTRATIONS_PREEMPTED_ASC, + REGISTRATIONS_PREEMPTED_ASCQ); + + list_del(l); + kfree(iter); + } + + reservation->generation++; +out: + spin_unlock(&volume->reserve_lock); +} + +static void +persistent_reservation_handle_preempt_abort(struct iscsi_cmnd *cmnd, u8 type) +{ + persistent_reservation_handle_preempt(cmnd, type, 1); +} + +void +build_persistent_reserve_in_response(struct iscsi_cmnd *cmnd) +{ + struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); + u8 *cdb = req->scb; + u8 service_action; + u16 allocation_length; + + service_action = (cdb[1] & 0x1F); + allocation_length = be16_to_cpu(*((u16 *)(&cdb[7]))); + + if (allocation_length < 8) { + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); + return; + } + + switch (service_action) { + case SERVICE_ACTION_READ_KEYS: + persistent_reservation_read_keys(cmnd, allocation_length); + break; + case SERVICE_ACTION_READ_RESERVATIONS: + persistent_reservation_read_reservations(cmnd, + allocation_length); + break; + case SERVICE_ACTION_READ_CAPABILITIES: + persistent_reservation_read_capabilities(cmnd, + allocation_length); + break; + case SERVICE_ACTION_READ_FULL: + persistent_reservation_read_full(cmnd, allocation_length); + break; + default: + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); + break; + } +} + +void +build_persistent_reserve_out_response(struct iscsi_cmnd *cmnd) +{ + struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); + u8 *cdb = req->scb; + u8 service_action; + u8 scope, type; + u16 parameter_list_length; + + type = cdb[2] & 0xF; + scope = (cdb[2] >> 4) & 0xF; + + if (scope) { + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); + return; + } + + service_action = (cdb[1] & 0x1F); + + parameter_list_length = READ_WORD(cdb[7], cdb[8]); + + if (parameter_list_length != 24) { + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, + PARAMETER_LIST_LENGTH_ERROR_ASC, + PARAMETER_LIST_LENGTH_ERROR_ASCQ); + return; + } + + switch (service_action) { + case SERVICE_ACTION_REGISTER: + persistent_reservation_handle_register(cmnd, 0); + break; + case SERVICE_ACTION_REGISTER_IGNORE: + persistent_reservation_handle_register(cmnd, 1); + break; + case SERVICE_ACTION_RESERVE: + persistent_reservation_handle_reserve(cmnd, type); + break; + case SERVICE_ACTION_RELEASE: + persistent_reservation_handle_release(cmnd, type, scope); + break; + case SERVICE_ACTION_CLEAR: + persistent_reservation_handle_clear(cmnd); + break; + case SERVICE_ACTION_PREEMPT: + persistent_reservation_handle_preempt(cmnd, type, 0); + break; + case SERVICE_ACTION_PREEMPT_ABORT: + persistent_reservation_handle_preempt_abort(cmnd, type); + break; + default: + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); + break; + } +} Added: trunk/kernel/persist.h =================================================================== --- trunk/kernel/persist.h (rev 0) +++ trunk/kernel/persist.h 2011-09-09 06:28:52 UTC (rev 456) @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2011 Shivaram U, shi...@qu... + * + * Released under the terms of the GNU GPL v2.0. + */ +#ifndef IET_PERSIST_H_ +#define IET_PERSIST_H_ + +struct reservation_parameter { + u64 reservation_key; + u64 service_action_key; + u32 scope; + u8 aptpl; + u8 rsvd; + u16 rel_tgt_port; +} __attribute__ ((__packed__)); + +struct transport_id_common { + u8 protocol_id; + u8 rsvd; + u16 addl_len; +} __attribute__ ((__packed__)); + +struct registration { + u64 sid; + u64 reservation_key; + char init_name[256]; + struct list_head r_list; +}; + +struct reservation_capabilities { + u16 length; + u8 ptpl_c; + u8 tmv; + u8 tmask1; + u8 tmask2; + u8 rsvd1; + u8 rsvd2; +} __attribute__ ((__packed__)); + +struct reservation { + u8 is_reserved; + u8 type; + u8 persistent_type; + u8 pad; + u32 generation; + u64 sid; + u64 reservation_key; + struct list_head registration_list; +}; + +struct pfull_data { + u64 reservation_key; + u32 rsvd; + u8 r_holder; + u8 type; + u32 rsvd2; + u16 rel_tgt_port; + u32 addl_len; + u8 transport_id[0]; +} __attribute__ ((__packed__)); + +struct pin_data { + u64 reservation_key; + u32 scope; + u8 rsvd; + u8 type; + u16 obsolete; +} __attribute__ ((__packed__)); + +#define PARAMETER_LIST_LENGTH_ERROR_ASC 0x1A +#define PARAMETER_LIST_LENGTH_ERROR_ASCQ 0x00 + +#define INVALID_COMMAND_OPERATION_CODE_ASC 0x20 +#define INVALID_COMMAND_OPERATION_CODE_ASCQ 0x00 + +#define INVALID_FIELD_IN_CDB_ASC 0x24 +#define INVALID_FIELD_IN_CDB_ASCQ 0x00 + +#define INVALID_FIELD_IN_PARAMETER_LIST_ASC 0x26 +#define INVALID_FIELD_IN_PARAMETER_LIST_ASCQ 0x00 + +#define INVALID_RELEASE_OF_PERSISTENT_RESERVATION_ASC 0x26 +#define INVALID_RELEASE_OF_PERSISTENT_RESERVATION_ASCQ 0x04 + +#define RESERVATIONS_PREEMPTED_ASC 0x2A +#define RESERVATIONS_PREEMPTED_ASCQ 0x03 + +#define RESERVATIONS_RELEASED_ASC 0x2A +#define RESERVATIONS_RELEASED_ASCQ 0x04 + +#define REGISTRATIONS_PREEMPTED_ASC 0x2A +#define REGISTRATIONS_PREEMPTED_ASCQ 0x05 + +#define INSUFFICIENT_RESERVATION_RESOURCES_ASC 0x55 +#define INSUFFICIENT_RESERVATION_RESOURCES_ASCQ 0x02 + +#define INSUFFICIENT_REGISTRATION_RESOURCES_ASC 0x55 +#define INSUFFICIENT_REGISTRATION_RESOURCES_ASCQ 0x04 + +#define PERSISTENT_RESERVE_MAX_REGISTRATIONS 256 +#define SERVICE_ACTION_READ_KEYS 0x00 +#define SERVICE_ACTION_READ_RESERVATIONS 0x01 +#define SERVICE_ACTION_READ_CAPABILITIES 0x02 +#define SERVICE_ACTION_READ_FULL 0x03 + +enum { + SERVICE_ACTION_REGISTER, + SERVICE_ACTION_RESERVE, + SERVICE_ACTION_RELEASE, + SERVICE_ACTION_CLEAR, + SERVICE_ACTION_PREEMPT, + SERVICE_ACTION_PREEMPT_ABORT, + SERVICE_ACTION_REGISTER_IGNORE, + SERVICE_ACTION_REGISTER_MOVE +}; + +#define RESERVATION_TYPE_READ_SHARED 0x00 /* obsolete */ +#define RESERVATION_TYPE_WRITE_EXCLUSIVE 0x01 +#define RESERVATION_TYPE_READ_EXCLUSIVE 0x02 /* obsolete */ +#define RESERVATION_TYPE_EXCLUSIVE_ACCESS 0x03 +#define RESERVATION_TYPE_SHARED_ACCESS 0x04 /* obsolete */ +#define RESERVATION_TYPE_WRITE_EXCLUSIVE_RO 0x05 +#define RESERVATION_TYPE_EXCLUSIVE_ACCESS_RO 0x06 + +enum { + RESERVATION_TYPE_RESERVE, + RESERVATION_TYPE_PERSISTENT +}; + +#define READ_WORD(a, b) (((a) << 8) | (b)) + +struct iscsi_cmnd; +void build_persistent_reserve_out_response(struct iscsi_cmnd *cmnd); +void build_persistent_reserve_in_response(struct iscsi_cmnd *cmnd); + +#endif Modified: trunk/kernel/target_disk.c =================================================================== --- trunk/kernel/target_disk.c 2011-09-03 12:02:22 UTC (rev 455) +++ trunk/kernel/target_disk.c 2011-09-09 06:28:52 UTC (rev 456) @@ -498,26 +498,11 @@ struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); int ret = is_volume_reserved(cmnd->lun, - cmnd->conn->session->sid); + cmnd->conn->session->sid, req->scb); if (ret == -EBUSY) { - switch (req->scb[0]) { - case INQUIRY: - 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 1; - } + send_scsi_rsp(cmnd, + build_reservation_conflict_response); + return 1; } return 0; @@ -580,6 +565,12 @@ case VERIFY_16: send_scsi_rsp(cmnd, build_generic_response); break; + case PERSISTENT_RESERVE_IN: + send_data_rsp(cmnd, build_persistent_reserve_in_response); + break; + case PERSISTENT_RESERVE_OUT: + send_scsi_rsp(cmnd, build_persistent_reserve_out_response); + break; default: eprintk("%s\n", "we should not come here!"); break; Modified: trunk/kernel/volume.c =================================================================== --- trunk/kernel/volume.c 2011-09-03 12:02:22 UTC (rev 455) +++ trunk/kernel/volume.c 2011-09-09 06:28:52 UTC (rev 456) @@ -7,6 +7,7 @@ #include <linux/types.h> #include <linux/parser.h> #include <linux/blkdev.h> +#include <scsi/scsi.h> #include "iscsi.h" #include "iscsi_dbg.h" @@ -210,6 +211,20 @@ return err; } +static void +volume_reservation_exit(struct iet_volume *volume) +{ + struct list_head *l, *n; + struct registration *tmp; + struct reservation *reservation = &volume->reservation; + + list_for_each_safe(l, n, &reservation->registration_list) { + tmp = list_entry(l, struct registration, r_list); + list_del(l); + kfree(tmp); + } +} + int volume_add(struct iscsi_target *target, struct volume_info *info) { int ret; @@ -229,6 +244,7 @@ volume->target = target; volume->lun = info->lun; + INIT_LIST_HEAD(&volume->reservation.registration_list); args = kzalloc(info->args_len + 1, GFP_KERNEL); if (!args) { @@ -292,6 +308,7 @@ volume->iotype->detach(volume); put_iotype(volume->iotype); list_del(&volume->list); + volume_reservation_exit(volume); kfree(volume); } @@ -333,33 +350,134 @@ int volume_reserve(struct iet_volume *volume, u64 sid) { int err = 0; + struct reservation *reservation; if (!volume) return -ENOENT; + reservation = &volume->reservation; spin_lock(&volume->reserve_lock); - if (volume->reserve_sid && volume->reserve_sid != sid) + if (reservation->is_reserved && reservation->sid != sid) err = -EBUSY; - else - volume->reserve_sid = sid; + else { + reservation->is_reserved = 1; + reservation->type = RESERVATION_TYPE_RESERVE; + reservation->sid = sid; + } spin_unlock(&volume->reserve_lock); return err; } -int is_volume_reserved(struct iet_volume *volume, u64 sid) +int is_volume_reserved(struct iet_volume *volume, u64 sid, u8 *scb) { int err = 0; + struct reservation *reservation; + struct list_head *l; + int registered = 0; + u8 write_excl = 0; + u8 excl_access = 0; + u8 write_excl_ro = 0; + u8 excl_access_ro = 0; if (!volume) return -ENOENT; + reservation = &volume->reservation; spin_lock(&volume->reserve_lock); - if (!volume->reserve_sid || volume->reserve_sid == sid) - err = 0; - else + if (!reservation->is_reserved || reservation->sid == sid) { + spin_unlock(&volume->reserve_lock); + return 0; + } + + if (reservation->type == RESERVATION_TYPE_RESERVE) { + switch (scb[0]) { + case INQUIRY: + case RELEASE: + case REPORT_LUNS: + case REQUEST_SENSE: + case READ_CAPACITY: + /* allowed commands when reserved */ + break; + case SERVICE_ACTION_IN: + if ((scb[1] & 0x1F) == 0x10) + break; + /* fall through */ + default: + err = -EBUSY; + break; + } + spin_unlock(&volume->reserve_lock); + return err; + } + + list_for_each(l, &reservation->registration_list) { + struct registration *tmp; + tmp = list_entry(l, struct registration, r_list); + if (tmp->sid == sid) { + registered = 1; + break; + } + } + + switch (reservation->persistent_type) { + case RESERVATION_TYPE_WRITE_EXCLUSIVE: + write_excl = 1; + break; + case RESERVATION_TYPE_EXCLUSIVE_ACCESS: + excl_access = 1; + break; + case RESERVATION_TYPE_WRITE_EXCLUSIVE_RO: + write_excl_ro = 1; + break; + case RESERVATION_TYPE_EXCLUSIVE_ACCESS_RO: + excl_access_ro = 1; + break; + } + + switch (scb[0]) { + case INQUIRY: + case TEST_UNIT_READY: + case PERSISTENT_RESERVE_IN: + case PERSISTENT_RESERVE_OUT: + case REPORT_LUNS: + case REQUEST_SENSE: + case READ_CAPACITY: + case START_STOP: + break; + case MODE_SENSE: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + case WRITE_VERIFY: + case SYNCHRONIZE_CACHE: + if (write_excl || excl_access) + err = -EBUSY; + if ((write_excl_ro || excl_access_ro) && !registered) + err = -EBUSY; + break; + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case VERIFY: + case VERIFY_16: + if (excl_access) + err = -EBUSY; + if (excl_access_ro && !registered) + err = -EBUSY; + break; + case SERVICE_ACTION_IN: + if ((scb[1] & 0x1F) == 0x10) + break; + /* fall through */ + case RELEASE: + case RESERVE: + default: err = -EBUSY; - + break; + } spin_unlock(&volume->reserve_lock); return err; } @@ -367,16 +485,22 @@ int volume_release(struct iet_volume *volume, u64 sid, int force) { int err = 0; + struct reservation *reservation; if (!volume) return -ENOENT; + reservation = &volume->reservation; spin_lock(&volume->reserve_lock); - if (force || volume->reserve_sid == sid) - volume->reserve_sid = 0; - else + if (reservation->type == RESERVATION_TYPE_RESERVE && + (force || reservation->sid == sid)) { + reservation->is_reserved = 0; + reservation->type = 0; + reservation->sid = 0; + } else { err = -EBUSY; + } spin_unlock(&volume->reserve_lock); return err; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2011-09-09 06:29:28
|
Revision: 457 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=457&view=rev Author: agr1 Date: 2011-09-09 06:29:20 +0000 (Fri, 09 Sep 2011) Log Message: ----------- Persistent Reservations: initial cleanup + fixes Signed-off-by: Arne Redlich <arn...@go...> Modified Paths: -------------- trunk/kernel/iscsi.c trunk/kernel/iscsi.h trunk/kernel/iscsi_dbg.h trunk/kernel/persist.c trunk/kernel/persist.h trunk/kernel/session.c trunk/kernel/tio.c trunk/kernel/ua.c trunk/kernel/volume.c Modified: trunk/kernel/iscsi.c =================================================================== --- trunk/kernel/iscsi.c 2011-09-09 06:28:52 UTC (rev 456) +++ trunk/kernel/iscsi.c 2011-09-09 06:29:20 UTC (rev 457) @@ -392,7 +392,7 @@ assert(req->tio); size = min(req->tio->size, cmnd_read_size(req)); - + if (req->status == SAM_STAT_GOOD && size) do_send_data_rsp(req); else { @@ -756,7 +756,10 @@ return 0; } -static void set_offset_and_length(struct iet_volume *lu, u8 *cmd, loff_t *off, u32 *len) +static void set_offset_and_length(const struct iet_volume *lu, + const u8 *cmd, + loff_t *off, + u32 *len) { assert(lu); @@ -784,6 +787,14 @@ *len = (u32)cmd[10] << 24 | (u32)cmd[11] << 16 | (u32)cmd[12] << 8 | (u32)cmd[13]; break; + case PERSISTENT_RESERVE_OUT: + { + const struct persistent_reserve_out *pr_out = + (const struct persistent_reserve_out *)cmd; + *off = 0; + *len = be32_to_cpu(pr_out->parameter_list_length); + return; + } default: BUG(); } @@ -1026,6 +1037,7 @@ tio_set(req->tio, length, offset); break; } + case PERSISTENT_RESERVE_OUT: case WRITE_6: case WRITE_10: case WRITE_16: @@ -1040,7 +1052,9 @@ req->is_unsolicited_data = !(req_hdr->flags & ISCSI_CMD_FINAL); req->target_task_tag = get_next_ttt(conn->session); - if (LUReadonly(req->lun)) { + /* the readonly check needs to go */ + if (req_hdr->scb[0] != PERSISTENT_RESERVE_OUT && + LUReadonly(req->lun)) { create_sense_rsp(req, DATA_PROTECT, 0x27, 0x0); cmnd_skip_data(req); break; @@ -1068,23 +1082,6 @@ } break; } - case PERSISTENT_RESERVE_OUT: - { - u16 length; - - length = READ_WORD(req_hdr->scb[7], req_hdr->scb[8]); - if (!length) - goto error; - - req->tio = tio_alloc(1); - tio_set(req->tio, length, 0); - if (req->pdu.datasize) { - if (cmnd_recv_pdu(conn, req->tio, 0, req->pdu.datasize) < 0) - assert(0); - } - break; - } - error: default: eprintk("Unsupported %x\n", req_hdr->scb[0]); Modified: trunk/kernel/iscsi.h =================================================================== --- trunk/kernel/iscsi.h 2011-09-09 06:28:52 UTC (rev 456) +++ trunk/kernel/iscsi.h 2011-09-09 06:29:20 UTC (rev 457) @@ -66,6 +66,13 @@ atomic_t count; }; +struct tio_iterator { + struct tio *tio; + u32 size; + u32 pg_idx; + u32 pg_off; +}; + struct network_thread_info { struct task_struct *task; unsigned long flags; @@ -384,6 +391,7 @@ 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_abort_tasks(struct iscsi_session *, u32 lun); /* volume.c */ extern struct file_operations volume_seq_fops; @@ -408,6 +416,13 @@ extern int tio_write(struct iet_volume *, struct tio *); extern int tio_sync(struct iet_volume *, struct tio *); +extern void tio_init_iterator(struct tio *tio, + struct tio_iterator *iter); + +extern size_t tio_add_data(struct tio_iterator *iter, + const u8 *data, + size_t len); + /* iotype.c */ extern struct iotype *get_iotype(const char *name); extern void put_iotype(struct iotype *iot); @@ -472,6 +487,9 @@ #define cmnd_scsicode(cmnd) cmnd_hdr(cmnd)->scb[0] #define cmnd_immediate(cmnd) ((cmnd)->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE) +#define PAD_TO_4_BYTES(n) \ + (((n) + 3) & -4) + /* default and maximum scsi level block sizes */ #define IET_DEF_BLOCK_SIZE 512 #define IET_MAX_BLOCK_SIZE 4096 Modified: trunk/kernel/iscsi_dbg.h =================================================================== --- trunk/kernel/iscsi_dbg.h 2011-09-09 06:28:52 UTC (rev 456) +++ trunk/kernel/iscsi_dbg.h 2011-09-09 06:29:20 UTC (rev 457) @@ -11,6 +11,7 @@ #define D_TASK_MGT (1UL << 7) #define D_IOMODE (1UL << 8) #define D_UAC (1UL << 9) +#define D_PR (1UL << 10) #define D_DATA (D_READ | D_WRITE) @@ -18,12 +19,15 @@ #define PFX "iscsi_trgt: " -#define dprintk(debug, fmt, args...) do { \ - if ((debug) & debug_enable_flags) { \ - printk(KERN_DEBUG PFX "%s(%d) " fmt, __FUNCTION__,\ - __LINE__, args);\ - } \ -} while (0) +#define dprintk(debug, fmt, args...) \ + do { \ + if ((debug) & debug_enable_flags) { \ + printk(KERN_DEBUG PFX "%s(%d) " fmt, \ + __FUNCTION__, \ + __LINE__, \ + ##args); \ + } \ + } while (0) #define dprintk_ua(ua, sess, lun) \ dprintk(D_UAC, "sess %llu, lun %u: %p %x %x\n", \ @@ -31,21 +35,34 @@ (ua) ? (ua)->asc : 0, \ (ua) ? (ua)->ascq : 0) -#define eprintk(fmt, args...) do { \ - printk(KERN_ERR PFX "%s(%d) " fmt, __FUNCTION__, \ - __LINE__, args);\ -} while (0) +#define dprintk_pr(cmd, fmt, args...) \ + dprintk(D_PR, "%#Lx:%hu, lun %u, cmnd %p: " fmt, \ + cmnd->conn->session->sid, \ + cmnd->conn->cid, \ + cmnd->lun->lun, \ + cmnd, \ + ##args) +#define eprintk(fmt, args...) \ + do { \ + printk(KERN_ERR PFX "%s(%d) " fmt, \ + __FUNCTION__, \ + __LINE__, \ + ##args); \ + } while (0) + #define iprintk(X...) printk(KERN_INFO PFX X) -#define assert(p) do { \ - if (!(p)) { \ - printk(KERN_CRIT PFX "BUG at %s:%d assert(%s)\n",\ - __FILE__, __LINE__, #p); \ - dump_stack(); \ - BUG(); \ - } \ -} while (0) +/* this has to go away - use BUG() and friends instead */ +#define assert(p) \ + do { \ + if (!(p)) { \ + printk(KERN_CRIT PFX "BUG at %s:%d assert(%s)\n", \ + __FILE__, __LINE__, #p); \ + dump_stack(); \ + BUG(); \ + } \ + } while (0) #ifdef D_IOV static inline void iscsi_dump_iov(struct msghdr *msg) Modified: trunk/kernel/persist.c =================================================================== --- trunk/kernel/persist.c 2011-09-09 06:28:52 UTC (rev 456) +++ trunk/kernel/persist.c 2011-09-09 06:29:20 UTC (rev 457) @@ -9,426 +9,508 @@ #include "iscsi_dbg.h" #include "persist.h" +bool +pr_is_reserved_by_session(const struct reservation *res, + const struct iscsi_session *sess) +{ + return (pr_is_reserved(res) && res->sid == sess->sid); +} + +bool +pr_initiator_has_registered(const struct reservation *res, + u64 sid) +{ + struct registration *reg; + + list_for_each_entry(reg, &res->registration_list, r_list) { + if (reg->sid == sid) + return true; + } + + return false; +} + +static const struct pr_in_report_capabilities_data pr_capabilities = { + .length = cpu_to_be16(8), + .ptpl_c = 0, + .atp_c = 0, + .sip_c = 0, + .crh = 0, + + .ptpl_a = 0, + .allow_commands = 0, + .tmv = 1, + + .type_mask = cpu_to_be16(PR_TYPE_WR_EX| + PR_TYPE_EX_AC| + PR_TYPE_WR_EX_RO| + PR_TYPE_EX_AC_RO), +}; + static void -persistent_reservation_read_capabilities(struct iscsi_cmnd *cmnd, - u16 allocation_length) +pr_in_report_capabilities(struct iscsi_cmnd *cmnd, + u16 allocation_length) { - struct reservation_capabilities cap; - u16 min_len; - struct tio *tio; - u8 *data; + struct tio *tio = tio_alloc(1); + u8 *data = page_address(tio->pvec[0]); + const u32 min_len = min_t(u16, allocation_length, sizeof(pr_capabilities)); - memset(&cap, 0, sizeof(cap)); + BUG_ON(!data); + cmnd->tio = tio; - cap.length = cpu_to_be16(8); - cap.tmv |= 0x80; /* TMV */ - cap.tmask1 |= 0x2; /* WR_EX/Write Exclusive access */ - cap.tmask1 |= 0x8; /* EX_AC/Exclusive access */ - cap.tmask1 |= 0x20; /* WR_EX_RO/Write Exclusive Registrants Only */ - cap.tmask1 |= 0x40; /* EX_AC_RO/Exclusive Access Registrants Only */ - min_len = min_t(u16, allocation_length, sizeof(cap)); - tio = cmnd->tio = tio_alloc(1); - data = page_address(tio->pvec[0]); - assert(data); - memcpy(data, &cap, min_len); + memcpy(data, &pr_capabilities, min_len); tio_set(tio, min_len, 0); + + dprintk_pr(cmnd, "ret len %u\n", min_len); } static void -persistent_reservation_read_reservations(struct iscsi_cmnd *cmnd, - u16 allocation_length) +pr_in_read_reservation(struct iscsi_cmnd *cmnd, + u16 allocation_length) { - u16 done = 0; - u8 *data; - struct pin_data *pin_data; - struct tio *tio; struct iet_volume *volume = cmnd->lun; - struct reservation *reservation = &volume->reservation; + const struct reservation *reservation = &volume->reservation; + struct tio *tio = tio_alloc(1); + struct pr_in_read_reservation_data *pin_data = + (struct pr_in_read_reservation_data *)page_address(tio->pvec[0]); + u32 size; + BUG_ON(!pin_data); + memset(pin_data, 0x0, sizeof(*pin_data)); - tio = cmnd->tio = tio_alloc(1); - data = page_address(tio->pvec[0]); - assert(data); + cmnd->tio = tio; spin_lock(&volume->reserve_lock); - *((u32 *)data) = cpu_to_be32(reservation->generation); + pin_data->generation = cpu_to_be32(reservation->generation); - done = 8; - if (reservation->is_reserved) { - pin_data = (struct pin_data *)(data + 8); - memset(pin_data, 0, sizeof(struct pin_data)); - pin_data->reservation_key = cpu_to_be64(reservation->reservation_key); + if (pr_is_reserved(reservation)) { + pin_data->reservation_key = reservation->reservation_key; + pin_data->scope = PR_SCOPE_LU; pin_data->type = reservation->persistent_type; - done += sizeof(struct pin_data); + /* + * SPC-3, 6.11.3.2 + * "The ADDITIONAL LENGTH field contains a count of the number of + * bytes to follow and shall be set to 16" + */ + pin_data->additional_length = cpu_to_be32(16); + size = sizeof(*pin_data); + + dprintk_pr(cmnd, "key %#Lx, size %u\n", + pin_data->reservation_key, + size); + } else { + size = 8; /* generation + additional length */ + + dprintk_pr(cmnd, + "size %u\n", + size); } - spin_unlock(&volume->reserve_lock); - *((u32 *)(data + 4)) = cpu_to_be32(done - 8); - done = min_t(u16, done, allocation_length); - tio_set(tio, done, 0); -} -/* - * For writes that may span page boundaries - * Assumption is that len is not greater than PAGE_SIZE - */ -static void -tio_write_buffer(struct tio *tio, u8 *data, int len, - int *write_idx, int *write_offset) -{ - int idx = *write_idx, offset = *write_offset; - int min_len; - u8 *ptr; + spin_unlock(&volume->reserve_lock); - ptr = page_address(tio->pvec[idx]); - min_len = min_t(int, (PAGE_SIZE - offset), len); - memcpy(ptr + offset, data, min_len); - len -= min_len; - if (!len) { - offset += min_len; - if (offset == PAGE_SIZE) { - *write_idx = idx + 1; - offset = 0; - } - *write_offset = offset; - return; - } - - /* Copy the remaining from the next page */ - data += min_len; - idx++; - ptr = page_address(tio->pvec[idx]); - memcpy(ptr, data, len); - *write_idx = idx; - *write_offset = len; + tio_set(tio, min_t(u32, size, allocation_length), 0); } static void -persistent_reservation_read_full(struct iscsi_cmnd *cmnd, - u16 allocation_length) +pr_in_read_full_status(struct iscsi_cmnd *cmnd, + u16 allocation_length) { - struct list_head *l; - struct registration *iter; - u16 done; - u8 *data; - u16 desc_size; - u16 transport_size; - struct pfull_data pfull; - struct iscsi_session *session = cmnd->conn->session; struct iet_volume *volume = cmnd->lun; - struct reservation *reservation = &volume->reservation; - struct tio *tio; - int idx = 0, offset = 0; - struct transport_id_common tid; + const struct reservation *reservation = &volume->reservation; + struct tio *tio = tio_alloc(get_pgcnt(allocation_length, 0)); + struct tio_iterator tio_it; + struct pr_in_read_full_status_data *pfull = + (struct pr_in_read_full_status_data *)page_address(tio->pvec[0]); + struct registration *reg; + u16 left = allocation_length - 8; + u32 addl_data_len = 0; - tio = cmnd->tio = tio_alloc(get_pgcnt(allocation_length, 0)); + if (allocation_length < 8) { + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); + return; + } - data = page_address(tio->pvec[0]); + cmnd->tio = tio; + tio_init_iterator(tio, &tio_it); + tio_it.pg_off += 8; /* generation + additional length */ spin_lock(&volume->reserve_lock); - *((u32 *)data) = cpu_to_be32(reservation->generation); - done = 8; - offset = 8; - list_for_each(l, &reservation->registration_list) { - iter = list_entry(l, struct registration, r_list); + pfull->generation = cpu_to_be32(reservation->generation); - desc_size = sizeof(struct pfull_data); - transport_size = sizeof(struct transport_id_common) + - strlen(iter->init_name); - desc_size += transport_size; - if ((done + desc_size) > allocation_length) { - done += desc_size; - continue; - } + list_for_each_entry(reg, &reservation->registration_list, r_list) { + const size_t init_name_len = PAD_TO_4_BYTES(strlen(reg->init_name)); + const struct iscsi_transport_id tid = { + .format_code = 0, + .protocol_identifier = PROTOCOL_IDENTIFIER_ISCSI, + .additional_length = cpu_to_be16(init_name_len), + }; + const struct pr_in_full_status_descriptor desc = { + .reservation_key = reg->reservation_key, + .all_tg_pt = 0, + .r_holder = pr_is_reserved_by_session(reservation, + cmnd->conn->session), + .scope = PR_SCOPE_LU, + .type = reservation->persistent_type, + /* only rel_tgt_port_id 1 is supported */ + .rel_tgt_port_id = cpu_to_be16(1), + .additional_desc_length = cpu_to_be32(sizeof(tid) + init_name_len), + }; - memset(&pfull, 0, sizeof(pfull)); - pfull.reservation_key = cpu_to_be64(iter->reservation_key); - pfull.addl_len = cpu_to_be16(transport_size); + left -= tio_add_data(&tio_it, + (const u8 *)&desc, + min_t(u16, left, sizeof(desc))); - if (reservation->is_reserved - && reservation->sid == session->sid) { - pfull.r_holder |= 0x01; - pfull.type = reservation->persistent_type; - } + left -= tio_add_data(&tio_it, + (const u8 *)&tid, + min_t(u16, left, sizeof(tid))); - pfull.rel_tgt_port = cpu_to_be16(1); /* rel tgt port set to 1 */ - tio_write_buffer(tio, (u8 *)&pfull, - sizeof(pfull), &idx, &offset); + left -= tio_add_data(&tio_it, + (const u8 *)reg->init_name, + min_t(u16, left, init_name_len)); - done += sizeof(struct pfull_data); - memset(&tid, 0, sizeof(tid)); - tid.protocol_id = 0x05; - tid.addl_len = cpu_to_be16(transport_size - sizeof(struct transport_id_common)); - tio_write_buffer(tio, (u8 *)&tid, - sizeof(tid), &idx, &offset); - done += sizeof(struct transport_id_common); - tio_write_buffer(tio, iter->init_name, - strlen(iter->init_name), &idx, &offset); - done += strlen(iter->init_name); + addl_data_len += sizeof(desc) + sizeof(tid) + init_name_len; + + dprintk_pr(cmnd, + "init name %s, sess %#Lx, key %#Lx, rtype %d, ptype %d, r_holder %d, desc.addlen %u, tid.addlen %u, addlen %u, left %u\n", + reg->init_name, + reg->sid, + reg->reservation_key, + reservation->reservation_type, + desc.type, + desc.r_holder, + be32_to_cpu(desc.additional_desc_length), + be16_to_cpu(tid.additional_length), + addl_data_len, + left); } + spin_unlock(&volume->reserve_lock); - *((u32 *)(data + 4)) = cpu_to_be32(done - 8); - done = min_t(u16, done, allocation_length); - tio_set(tio, done, 0); + dprintk_pr(cmnd, + "dlen %u, tlen %u\n", + addl_data_len, + allocation_length - left); + + pfull->additional_length = cpu_to_be32(addl_data_len); + tio_set(tio, allocation_length - left, 0); } static void -persistent_reservation_read_keys(struct iscsi_cmnd *cmnd, - u16 allocation_length) +pr_in_read_keys(struct iscsi_cmnd *cmnd, + u16 allocation_length) { - struct list_head *l; - struct registration *registration; - u16 done; - u8 *data; - u8 key_size = 8; struct iet_volume *volume = cmnd->lun; - struct reservation *reservation = &volume->reservation; - struct tio *tio; - u64 reservation_key; - int idx = 0, offset; + const struct reservation *reservation = &volume->reservation; + struct tio *tio = tio_alloc(get_pgcnt(allocation_length, 0)); + struct tio_iterator tio_it; + struct pr_in_read_keys_data *kdata = + (struct pr_in_read_keys_data *)page_address(tio->pvec[0]); + struct registration *reg; + u16 left = allocation_length - 8; + u32 addl_data_len = 0; - tio = cmnd->tio = tio_alloc(get_pgcnt(allocation_length, 0)); - data = page_address(tio->pvec[0]); + if (allocation_length < 8) { + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); + return; + } + cmnd->tio = tio; + tio_init_iterator(tio, &tio_it); + tio_it.pg_off += 8; /* generation + additional length */ + spin_lock(&volume->reserve_lock); - *((u32 *)data) = cpu_to_be32(reservation->generation); - done = 8; - offset = 8; - list_for_each(l, &reservation->registration_list) { - registration = list_entry(l, struct registration, r_list); + kdata->generation = cpu_to_be32(reservation->generation); - if ((done + key_size) > allocation_length) { - done += key_size; - continue; - } + list_for_each_entry(reg, &reservation->registration_list, r_list) { - reservation_key = cpu_to_be64(registration->reservation_key); - tio_write_buffer(tio, (u8 *)&reservation_key, - sizeof(reservation_key), &idx, &offset); - done += key_size; + left -= tio_add_data(&tio_it, + (const u8 *)®->reservation_key, + min_t(u16, + left, + sizeof(reg->reservation_key))); + + addl_data_len += sizeof(reg->reservation_key); + + dprintk_pr(cmnd, + "found reg, init name %s, sess %#Lx, key %#Lx, kdata len %u, left %u\n", + reg->init_name, + reg->sid, + reg->reservation_key, + addl_data_len, + left); } + spin_unlock(&volume->reserve_lock); - *((u32 *)(data + 4)) = cpu_to_be32(done - 8); - done = min_t(u16, done, allocation_length); - tio_set(tio, done, 0); + dprintk_pr(cmnd, + "dlen %u, tlen %u\n", + addl_data_len, + allocation_length - left); + + kdata->additional_length = cpu_to_be32(addl_data_len); + tio_set(tio, allocation_length - left, 0); + + dprintk_pr(cmnd, + "keys[0]: %#Lx\n", kdata->keys[0]); } -static int -initiator_has_registered(struct list_head *registration_list, - u64 reservation_key, u64 sid, - struct registration **ret) +void +build_persistent_reserve_in_response(struct iscsi_cmnd *cmnd) { - struct list_head *l; - struct registration *iter; + const struct persistent_reserve_in *pr_in = + (const struct persistent_reserve_in *)(cmnd_hdr(cmnd)->scb); + const u16 allocation_length = be16_to_cpu(pr_in->allocation_length); - list_for_each(l, registration_list) { - iter = list_entry(l, struct registration, r_list); - if (iter->sid == sid) { - if (ret) - *ret = iter; - return 1; - } + dprintk_pr(cmnd, + "svc action %x, alloc len %u\n", + pr_in->service_action, + allocation_length); + + if (allocation_length < 8) { + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); + return; } - return 0; + + switch (pr_in->service_action) { + case SERVICE_ACTION_READ_KEYS: + pr_in_read_keys(cmnd, allocation_length); + break; + case SERVICE_ACTION_READ_RESERVATION: + pr_in_read_reservation(cmnd, allocation_length); + break; + case SERVICE_ACTION_REPORT_CAPABILITIES: + pr_in_report_capabilities(cmnd, allocation_length); + break; + case SERVICE_ACTION_READ_FULL_STATUS: + pr_in_read_full_status(cmnd, allocation_length); + break; + default: + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); + break; + } } static void -persistent_reservation_handle_register(struct iscsi_cmnd *cmnd, int ignore) +pr_out_register(struct iscsi_cmnd *cmnd, bool ignore) { - struct registration *iter, *registration; - struct list_head *l; - struct reservation_parameter *param; - u64 service_action_key; - u64 reservation_key; - int count = 0; - u8 *data; - u8 spec_i_pt; + const struct pr_out_param_list *param = + (const struct pr_out_param_list *)page_address(cmnd->tio->pvec[0]); struct iscsi_session *session = cmnd->conn->session; struct iet_volume *volume = cmnd->lun; struct reservation *reservation = &volume->reservation; - struct tio *tio = cmnd->tio; + struct registration *reg; + struct registration *new_reg = kzalloc(sizeof(*new_reg), GFP_KERNEL); - data = page_address(tio->pvec[0]); - param = (struct reservation_parameter *)(data); - reservation_key = be64_to_cpu(param->reservation_key); - service_action_key = be64_to_cpu(param->service_action_key); - spec_i_pt = (param->aptpl >> 3) & 0x1; + dprintk_pr(cmnd, "rkey %#Lx, skey %#Lx, ignore %d\n", + param->reservation_key, + param->service_action_key, + ignore); - registration = kzalloc(sizeof(*registration), GFP_KERNEL|__GFP_NOFAIL); + if (!new_reg) { + eprintk("%#Lx:%hu: failed to alloc new registration\n", + cmnd->conn->session->sid, + cmnd->conn->cid); + + iscsi_cmnd_set_sense(cmnd, + /* TODO: verify sense key / asc / ascq */ + ILLEGAL_REQUEST, + INSUFFICIENT_REGISTRATION_RESOURCES_ASC, + INSUFFICIENT_REGISTRATION_RESOURCES_ASCQ); + return; + } + spin_lock(&volume->reserve_lock); - list_for_each(l, &reservation->registration_list) { - iter = list_entry(l, struct registration, r_list); - count++; - if (iter->sid != session->sid) + list_for_each_entry(reg, &reservation->registration_list, r_list) { + dprintk_pr(cmnd, + "found reg, init name %s, sess %#Lx, key %#Lx\n", + reg->init_name, + reg->sid, + reg->reservation_key); + + if (reg->sid != session->sid) continue; - if (!ignore && reservation_key != iter->reservation_key) { - iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, - INVALID_COMMAND_OPERATION_CODE_ASC, - INVALID_COMMAND_OPERATION_CODE_ASCQ); + if (!ignore && param->reservation_key != reg->reservation_key) { + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + INVALID_COMMAND_OPERATION_CODE_ASC, + INVALID_COMMAND_OPERATION_CODE_ASCQ); goto out; } - if (spec_i_pt) { - iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB_ASC, - INVALID_FIELD_IN_CDB_ASCQ); + if (param->spec_i_pt) { + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); goto out; } - if (!service_action_key) { - if (reservation->is_reserved - && session->sid == reservation->sid) { - reservation->is_reserved = 0; - reservation->type = 0; + if (!param->service_action_key) { + if (pr_is_reserved_by_session(reservation, session)) { + reservation->reservation_type = RESERVATION_TYPE_NONE; reservation->persistent_type = 0; reservation->reservation_key = 0; ua_establish_for_other_sessions(session, - volume->lun, - RESERVATIONS_RELEASED_ASC, - RESERVATIONS_RELEASED_ASCQ); + volume->lun, + RESERVATIONS_RELEASED_ASC, + RESERVATIONS_RELEASED_ASCQ); } - list_del(l); - kfree(iter); + list_del(®->r_list); + kfree(reg); } else { - iter->reservation_key = service_action_key; + reg->reservation_key = param->service_action_key; } reservation->generation++; goto out; } - if (!reservation_key && !service_action_key) { + if (!param->reservation_key && !param->service_action_key) { reservation->generation++; goto out; } - if (reservation_key) { + if (param->reservation_key) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } - if (count >= PERSISTENT_RESERVE_MAX_REGISTRATIONS) { - iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, - INSUFFICIENT_REGISTRATION_RESOURCES_ASC, - INSUFFICIENT_REGISTRATION_RESOURCES_ASCQ); - goto out; - } + new_reg->sid = session->sid; + new_reg->reservation_key = param->service_action_key; + strncpy(new_reg->init_name, + cmnd->conn->session->initiator, + sizeof(new_reg->init_name)); - registration->sid = session->sid; - registration->reservation_key = service_action_key; - strcpy(registration->init_name, cmnd->conn->session->initiator); - list_add_tail(®istration->r_list, &reservation->registration_list); + INIT_LIST_HEAD(&new_reg->r_list); + list_add_tail(&new_reg->r_list, &reservation->registration_list); reservation->generation++; + spin_unlock(&volume->reserve_lock); + + dprintk_pr(cmnd, + "init_name %s, key %#Lx, generation %u\n", + new_reg->init_name, + new_reg->reservation_key, + reservation->generation); + return; out: - kfree(registration); + kfree(new_reg); spin_unlock(&volume->reserve_lock); } -static int +static bool persistent_type_valid(int type) { switch (type) { - case RESERVATION_TYPE_WRITE_EXCLUSIVE: - case RESERVATION_TYPE_EXCLUSIVE_ACCESS: - case RESERVATION_TYPE_WRITE_EXCLUSIVE_RO: - case RESERVATION_TYPE_EXCLUSIVE_ACCESS_RO: - return 1; + case PR_TYPE_WRITE_EXCLUSIVE: + case PR_TYPE_EXCLUSIVE_ACCESS: + case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY: + case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY: + return true; + default: + return false; } - return 0; } static void -persistent_reservation_handle_reserve(struct iscsi_cmnd *cmnd, u8 type) +pr_out_reserve(struct iscsi_cmnd *cmnd, enum persistent_reservation_type type) { - struct reservation_parameter *param; - u64 reservation_key; - int retval; - u8 *data; + const struct pr_out_param_list *param = + (const struct pr_out_param_list *)page_address(cmnd->tio->pvec[0]); + bool registered; struct iscsi_session *session = cmnd->conn->session; struct iet_volume *volume = cmnd->lun; struct reservation *reservation = &volume->reservation; - struct tio *tio = cmnd->tio; - data = page_address(tio->pvec[0]); - param = (struct reservation_parameter *)(data); - reservation_key = be64_to_cpu(param->reservation_key); + spin_lock(&volume->reserve_lock); - spin_lock(&volume->reserve_lock); - retval = initiator_has_registered(&reservation->registration_list, - reservation_key, session->sid, NULL); - if (!retval) { + registered = pr_initiator_has_registered(reservation, session->sid); + if (!registered) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } - if (reservation->is_reserved && reservation->sid != session->sid) { + if (pr_is_reserved(reservation) && reservation->sid != session->sid) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } - if (reservation->is_reserved - && reservation->reservation_key != reservation_key) { + if (pr_is_reserved(reservation) && + reservation->reservation_key != param->reservation_key) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } - if (reservation->is_reserved && reservation->persistent_type != type) { + if (pr_is_reserved(reservation) && reservation->persistent_type != type) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } - if (reservation->is_reserved) /* No change */ + if (pr_is_reserved(reservation)) goto out; if (!persistent_type_valid(type)) { - iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB_ASC, - INVALID_FIELD_IN_CDB_ASCQ); + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); goto out; } - reservation->is_reserved = 1; - reservation->type = RESERVATION_TYPE_PERSISTENT; + reservation->reservation_type = RESERVATION_TYPE_PERSISTENT; reservation->persistent_type = type; - reservation->reservation_key = reservation_key; + reservation->reservation_key = param->reservation_key; reservation->sid = session->sid; + + dprintk_pr(cmnd, + "key %#Lx, sess %#Lx, generation %u, rtype %d, ptype %d\n", + reservation->reservation_key, + reservation->sid, + reservation->generation, + reservation->reservation_type, + reservation->persistent_type); + out: spin_unlock(&volume->reserve_lock); } static void -persistent_reservation_handle_release(struct iscsi_cmnd *cmnd, - u8 type, u8 scope) +pr_out_release(struct iscsi_cmnd *cmnd, + enum persistent_reservation_type type, + enum persistent_reservation_scope scope) { - struct reservation_parameter *param; - u64 reservation_key; - int retval; - u8 *data; + const struct pr_out_param_list *param = + (const struct pr_out_param_list *)page_address(cmnd->tio->pvec[0]); + bool registered; struct iscsi_session *session = cmnd->conn->session; struct iet_volume *volume = cmnd->lun; struct reservation *reservation = &volume->reservation; - struct tio *tio = cmnd->tio; - int send_ua; + bool send_ua; - data = page_address(tio->pvec[0]); - param = (struct reservation_parameter *)(data); - reservation_key = be64_to_cpu(param->reservation_key); - spin_lock(&volume->reserve_lock); - if (!reservation->is_reserved) + if (!pr_is_reserved(reservation)) goto out; - retval = initiator_has_registered(&reservation->registration_list, - reservation_key, session->sid, NULL); - if (!retval) { + registered = pr_initiator_has_registered(reservation, session->sid); + if (!registered) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } @@ -436,33 +518,45 @@ if (reservation->sid != session->sid) goto out; - if (reservation->reservation_key != reservation_key) { + if (reservation->reservation_key != param->reservation_key) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } - if (scope || (reservation->persistent_type != type)) { - iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, - INVALID_RELEASE_OF_PERSISTENT_RESERVATION_ASC, - INVALID_RELEASE_OF_PERSISTENT_RESERVATION_ASCQ); + if (scope != PR_SCOPE_LU || + (reservation->persistent_type != type)) { + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + INVALID_RELEASE_OF_PERSISTENT_RESERVATION_ASC, + INVALID_RELEASE_OF_PERSISTENT_RESERVATION_ASCQ); goto out; } switch (reservation->persistent_type) { - case RESERVATION_TYPE_WRITE_EXCLUSIVE_RO: - case RESERVATION_TYPE_EXCLUSIVE_ACCESS_RO: - send_ua = 1; + case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY: + case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY: + send_ua = true; break; default: - send_ua = 0; + send_ua = false; } - reservation->is_reserved = 0; - reservation->type = 0; - reservation->persistent_type = 0; + dprintk_pr(cmnd, + "key %#Lx, sess %#Lx, generation %u, rtype %d, ptype %d, ua %d\n", + reservation->reservation_key, + reservation->sid, + reservation->generation, + reservation->reservation_type, + reservation->persistent_type, + send_ua); + + reservation->reservation_type = RESERVATION_TYPE_NONE; + reservation->persistent_type = PR_TYPE_NONE; reservation->reservation_key = 0; + if (send_ua) - ua_establish_for_other_sessions(session, volume->lun, + ua_establish_for_other_sessions(session, + volume->lun, RESERVATIONS_RELEASED_ASC, RESERVATIONS_RELEASED_ASCQ); out: @@ -470,48 +564,45 @@ } static void -persistent_reservation_handle_clear(struct iscsi_cmnd *cmnd) +pr_out_clear(struct iscsi_cmnd *cmnd) { - struct reservation_parameter *param; - u64 reservation_key; - int retval; - u8 *data; + bool registered; struct iscsi_session *tmp_session, *session = cmnd->conn->session; struct iscsi_target *target = session->target; struct iet_volume *volume = cmnd->lun; - struct list_head *l, *n; struct reservation *reservation = &volume->reservation; - struct registration *iter; - struct tio *tio = cmnd->tio; + struct registration *reg, *tmp_reg; - data = page_address(tio->pvec[0]); - param = (struct reservation_parameter *)(data); - reservation_key = be64_to_cpu(param->reservation_key); - spin_lock(&volume->reserve_lock); - retval = initiator_has_registered(&reservation->registration_list, - reservation_key, session->sid, NULL); - if (!retval) { + registered = pr_initiator_has_registered(reservation, session->sid); + if (!registered) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } - list_for_each_safe(l, n, &reservation->registration_list) { - iter = list_entry(l, struct registration, r_list); - if (iter->sid != session->sid) { - tmp_session = session_lookup(target, iter->sid); + list_for_each_entry_safe(reg, tmp_reg, &reservation->registration_list, r_list) { + if (reg->sid != session->sid) { + tmp_session = session_lookup(target, reg->sid); if (tmp_session) - ua_establish_for_session(session, volume->lun, - RESERVATIONS_PREEMPTED_ASC, - RESERVATIONS_PREEMPTED_ASCQ); + ua_establish_for_session(session, + volume->lun, + RESERVATIONS_PREEMPTED_ASC, + RESERVATIONS_PREEMPTED_ASCQ); } - list_del(l); - kfree(iter); + list_del(®->r_list); + kfree(reg); } - reservation->is_reserved = 0; - reservation->type = 0; - reservation->persistent_type = 0; + dprintk_pr(cmnd, + "key %#Lx, sess %#Lx, generation %u, rtype %d, ptype %d\n", + reservation->reservation_key, + reservation->sid, + reservation->generation, + reservation->reservation_type, + reservation->persistent_type); + + reservation->reservation_type = RESERVATION_TYPE_NONE; + reservation->persistent_type = PR_TYPE_NONE; reservation->reservation_key = 0; reservation->generation++; out: @@ -519,67 +610,44 @@ } static void -session_abort_tasks(struct iscsi_session *session, u32 lun) +pr_out_preempt(struct iscsi_cmnd *cmnd, + enum persistent_reservation_type pr_type, + bool abort) { - struct iscsi_conn *conn; - struct iscsi_cmnd *cmnd, *tmp; - - list_for_each_entry(conn, &session->conn_list, list) { - list_for_each_entry_safe(cmnd, tmp, &conn->pdu_list, conn_list) { - if (translate_lun(cmnd_hdr(cmnd)->lun) != lun) - continue; - - __cmnd_abort(cmnd); - } - } -} - -static void -persistent_reservation_handle_preempt(struct iscsi_cmnd *cmnd, - u8 type, int abort) -{ - struct registration *iter; - struct list_head *l, *n; - struct reservation_parameter *param; - u64 reservation_key; - u64 service_action_key; - int retval; - u8 *data; + const struct pr_out_param_list *param = + (const struct pr_out_param_list *)page_address(cmnd->tio->pvec[0]); + struct registration *reg, *tmp_reg; + bool registered; struct iscsi_session *session = cmnd->conn->session; struct iscsi_session *reserv_session; struct iscsi_target *target = session->target; struct iet_volume *volume = cmnd->lun; struct reservation *reservation = &volume->reservation; - struct tio *tio = cmnd->tio; - int all = 0; + bool all = 0; - data = page_address(tio->pvec[0]); - param = (struct reservation_parameter *)(data); - reservation_key = be64_to_cpu(param->reservation_key); - service_action_key = be64_to_cpu(param->service_action_key); - - if (!service_action_key) { - iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, - INVALID_FIELD_IN_PARAMETER_LIST_ASC, - INVALID_FIELD_IN_PARAMETER_LIST_ASCQ); + if (!param->service_action_key) { + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + INVALID_FIELD_IN_PARAMETER_LIST_ASC, + INVALID_FIELD_IN_PARAMETER_LIST_ASCQ); return; } spin_lock(&volume->reserve_lock); - retval = initiator_has_registered(&reservation->registration_list, - reservation_key, session->sid, NULL); - if (!retval) { + registered = pr_initiator_has_registered(reservation, session->sid); + if (!registered) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } - if (reservation->is_reserved - && reservation->reservation_key == service_action_key - && reservation->sid != session->sid) { - if (!persistent_type_valid(type)) { - iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB_ASC, - INVALID_FIELD_IN_CDB_ASCQ); + if (pr_is_reserved(reservation) && + reservation->reservation_key == param->service_action_key && + reservation->sid != session->sid) { + if (!persistent_type_valid(pr_type)) { + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); goto out; } @@ -587,35 +655,35 @@ if (reserv_session) { if (abort) session_abort_tasks(reserv_session, volume->lun); - ua_establish_for_session(reserv_session, volume->lun, - RESERVATIONS_PREEMPTED_ASC, - RESERVATIONS_PREEMPTED_ASCQ); + ua_establish_for_session(reserv_session, + volume->lun, + RESERVATIONS_PREEMPTED_ASC, + RESERVATIONS_PREEMPTED_ASCQ); } - reservation->type = type; + reservation->reservation_type = RESERVATION_TYPE_PERSISTENT; reservation->sid = session->sid; - reservation->reservation_key = reservation_key; - reservation->persistent_type = type; - all = 1; + reservation->reservation_key = param->reservation_key; + reservation->persistent_type = pr_type; + all = true; } - list_for_each_safe(l, n, &reservation->registration_list) { - iter = list_entry(l, struct registration, r_list); - - if (iter->sid == session->sid) + list_for_each_entry_safe(reg, tmp_reg, &reservation->registration_list, r_list) { + if (reg->sid == session->sid) continue; - if (!all && iter->reservation_key != service_action_key) + if (!all && reg->reservation_key != param->service_action_key) continue; - reserv_session = session_lookup(target, iter->sid); + reserv_session = session_lookup(target, reg->sid); if (reserv_session) - ua_establish_for_session(reserv_session, volume->lun, - REGISTRATIONS_PREEMPTED_ASC, - REGISTRATIONS_PREEMPTED_ASCQ); + ua_establish_for_session(reserv_session, + volume->lun, + REGISTRATIONS_PREEMPTED_ASC, + REGISTRATIONS_PREEMPTED_ASCQ); - list_del(l); - kfree(iter); + list_del(®->r_list); + kfree(reg); } reservation->generation++; @@ -623,109 +691,62 @@ spin_unlock(&volume->reserve_lock); } -static void -persistent_reservation_handle_preempt_abort(struct iscsi_cmnd *cmnd, u8 type) -{ - persistent_reservation_handle_preempt(cmnd, type, 1); -} - void -build_persistent_reserve_in_response(struct iscsi_cmnd *cmnd) -{ - struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); - u8 *cdb = req->scb; - u8 service_action; - u16 allocation_length; - - service_action = (cdb[1] & 0x1F); - allocation_length = be16_to_cpu(*((u16 *)(&cdb[7]))); - - if (allocation_length < 8) { - iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB_ASC, - INVALID_FIELD_IN_CDB_ASCQ); - return; - } - - switch (service_action) { - case SERVICE_ACTION_READ_KEYS: - persistent_reservation_read_keys(cmnd, allocation_length); - break; - case SERVICE_ACTION_READ_RESERVATIONS: - persistent_reservation_read_reservations(cmnd, - allocation_length); - break; - case SERVICE_ACTION_READ_CAPABILITIES: - persistent_reservation_read_capabilities(cmnd, - allocation_length); - break; - case SERVICE_ACTION_READ_FULL: - persistent_reservation_read_full(cmnd, allocation_length); - break; - default: - iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB_ASC, - INVALID_FIELD_IN_CDB_ASCQ); - break; - } -} - -void build_persistent_reserve_out_response(struct iscsi_cmnd *cmnd) { - struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); - u8 *cdb = req->scb; - u8 service_action; - u8 scope, type; - u16 parameter_list_length; + const struct persistent_reserve_out *pr_out = + (const struct persistent_reserve_out *)(cmnd_hdr(cmnd)->scb); - type = cdb[2] & 0xF; - scope = (cdb[2] >> 4) & 0xF; + dprintk_pr(cmnd, + "svc action %x, scope %x, type %x, param len %u\n", + pr_out->service_action, + pr_out->scope, + pr_out->type, + be32_to_cpu(pr_out->parameter_list_length)); - if (scope) { - iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB_ASC, - INVALID_FIELD_IN_CDB_ASCQ); + if (pr_out->scope) { + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); return; } - service_action = (cdb[1] & 0x1F); - - parameter_list_length = READ_WORD(cdb[7], cdb[8]); - - if (parameter_list_length != 24) { - iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, - PARAMETER_LIST_LENGTH_ERROR_ASC, - PARAMETER_LIST_LENGTH_ERROR_ASCQ); + if (be32_to_cpu(pr_out->parameter_list_length) != 24) { + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + PARAMETER_LIST_LENGTH_ERROR_ASC, + PARAMETER_LIST_LENGTH_ERROR_ASCQ); return; } - switch (service_action) { + switch (pr_out->service_action) { case SERVICE_ACTION_REGISTER: - persistent_reservation_handle_register(cmnd, 0); + pr_out_register(cmnd, false); break; case SERVICE_ACTION_REGISTER_IGNORE: - persistent_reservation_handle_register(cmnd, 1); + pr_out_register(cmnd, true); break; case SERVICE_ACTION_RESERVE: - persistent_reservation_handle_reserve(cmnd, type); + pr_out_reserve(cmnd, pr_out->type); break; case SERVICE_ACTION_RELEASE: - persistent_reservation_handle_release(cmnd, type, scope); + pr_out_release(cmnd, pr_out->type, pr_out->scope); break; case SERVICE_ACTION_CLEAR: - persistent_reservation_handle_clear(cmnd); + pr_out_clear(cmnd); break; case SERVICE_ACTION_PREEMPT: - persistent_reservation_handle_preempt(cmnd, type, 0); + pr_out_preempt(cmnd, pr_out->type, false); break; case SERVICE_ACTION_PREEMPT_ABORT: - persistent_reservation_handle_preempt_abort(cmnd, type); + pr_out_preempt(cmnd, pr_out->type, true); break; default: - iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB_ASC, - INVALID_FIELD_IN_CDB_ASCQ); + iscsi_cmnd_set_sense(cmnd, + ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB_ASC, + INVALID_FIELD_IN_CDB_ASCQ); break; } } Modified: trunk/kernel/persist.h =================================================================== --- trunk/kernel/persist.h 2011-09-09 06:28:52 UTC (rev 456) +++ trunk/kernel/persist.h 2011-09-09 06:29:20 UTC (rev 457) @@ -6,68 +6,13 @@ #ifndef IET_PERSIST_H_ #define IET_PERSIST_H_ -struct reservation_parameter { - u64 reservation_key; - u64 service_action_key; - u32 scope; - u8 aptpl; - u8 rsvd; - u16 rel_tgt_port; -} __attribute__ ((__packed__)); - -struct transport_id_common { - u8 protocol_id; - u8 rsvd; - u16 addl_len; -} __attribute__ ((__packed__)); - struct registration { u64 sid; - u64 reservation_key; - char init_name[256]; + __be64 reservation_key; + char init_name[ISCSI_NAME_LEN]; struct list_head r_list; }; -struct reservation_capabilities { - u16 length; - u8 ptpl_c; - u8 tmv; - u8 tmask1; - u8 tmask2; - u8 rsvd1; - u8 rsvd2; -} __attribute__ ((__packed__)); - -struct reservation { - u8 is_reserved; - u8 type; - u8 persistent_type; - u8 pad; - u32 generation; - u64 sid; - u64 reservation_key; - struct list_head registration_list; -}; - -struct pfull_data { - u64 reservation_key; - u32 rsvd; - u8 r_holder; - u8 type; - u32 rsvd2; - u16 rel_tgt_port; - u32 addl_len; - u8 transport_id[0]; -} __attribute__ ((__packed__)); - -struct pin_data { - u64 reservation_key; - u32 scope; - u8 rsvd; - u8 type; - u16 obsolete; -} __attribute__ ((__packed__)); - #define PARAMETER_LIST_LENGTH_ERROR_ASC 0x1A #define PARAMETER_LIST_LENGTH_ERROR_ASCQ 0x00 @@ -98,40 +43,202 @@ #define INSUFFICIENT_REGISTRATION_RESOURCES_ASC 0x55 #define INSUFFICIENT_REGISTRATION_RESOURCES_ASCQ 0x04 -#define PERSISTENT_RESERVE_MAX_REGISTRATIONS 256 -#define SERVICE_ACTION_READ_KEYS 0x00 -#define SERVICE_ACTION_READ_RESERVATIONS 0x01 -#define SERVICE_ACTION_READ_CAPABILITIES 0x02 -#define SERVICE_ACTION_READ_FULL 0x03 +enum persistent_reserve_in_service_actions { + SERVICE_ACTION_READ_KEYS = 0x0, + SERVICE_ACTION_READ_RESERVATION = 0x1, + SERVICE_ACTION_REPORT_CAPABILITIES = 0x2, + SERVICE_ACTION_READ_FULL_STATUS = 0x3 +}; -enum { - SERVICE_ACTION_REGISTER, - SERVICE_ACTION_RESERVE, - SERVICE_ACTION_RELEASE, - SERVICE_ACTION_CLEAR, - SERVICE_ACTION_PREEMPT, - SERVICE_ACTION_PREEMPT_ABORT, - SERVICE_ACTION_REGISTER_IGNORE, - SERVICE_ACTION_REGISTER_MOVE +struct persistent_reserve_in { + u8 opcode; /* PERSISTENT_RESERVE_IN == 0x5e */ + u8 service_action:5; + u8 rsvd1:3; + u8 rsvd2[5]; + __be16 allocation_length; + u8 control; +} __packed; + + +enum pr_type_mask { + PR_TYPE_WR_EX_AR = 0x8000, /* Write Excl., All Registrants */ + PR_TYPE_EX_AC_RO = 0x4000, /* Excl. Access, Registrants Only */ + PR_TYPE_WR_EX_RO = 0x2000, /* Write Excl., Registrants Only */ + PR_TYPE_EX_AC = 0x800, /* Excl. Access */ + PR_TYPE_WR_EX = 0x200, /* Write Excl. */ + PR_TYPE_EX_AC_AR = 0x1, /* Excl. Access, All Registrants */ }; -#define RESERVATION_TYPE_READ_SHARED 0x00 /* obsolete */ -#define RESERVATION_TYPE_WRITE_EXCLUSIVE 0x01 -#define RESERVATION_TYPE_READ_EXCLUSIVE 0x02 /* obsolete */ -#define RESERVATION_TYPE_EXCLUSIVE_ACCESS 0x03 -#define RESERVATION_TYPE_SHARED_ACCESS 0x04 /* obsolete */ -#define RESERVATION_TYPE_WRITE_EXCLUSIVE_RO 0x05 -#define RESERVATION_TYPE_EXCLUSIVE_ACCESS_RO 0x06 +struct pr_in_report_capabilities_data { + __be16 length; -enum { + u8 ptpl_c:1; /* Persist Through Power Loss Capable */ + u8 rsvd1:1; + u8 atp_c:1; /* All Target Ports Capable */ + u8 sip_c:1; /* Specify Initiator Ports Capable */ + u8 crh:1; /* Compatible Reservation Handling */ + u8 rsvd2:3; + + u8 ptpl_a:1; /* Persist Through Power Loss Activated */ + u8 rsvd3:3; + u8 allow_commands:3; /* SPC-4, reserved in SPC-3 */ + u8 tmv:1; /* Type Mask Valid */ + + __be16 type_mask; + u8 rsvd4[2]; +} __packed; + +struct pr_in_read_reservation_data { + __be32 generation; + __be32 additional_length; + __be64 reservation_key; + u8 obsolete1[4]; + u8 rsvd; + + u8 type:4; + u8 scope:4; + + u8 obsolete2[2]; +} __packed; + +enum protocol_identifier { + PROTOCOL_IDENTIFIER_ISCSI = 0x5, +}; + +struct iscsi_transport_id { + u8 protocol_identifier:4; + u8 rsvd1:2; + u8 format_code:2; + + u8 rsvd2; + __be16 additional_length; + u8 iscsi_name[0]; +} __packed; + +/* this is iscsi specific */ +struct pr_in_full_status_descriptor { + __be64 reservation_key; + u8 rsvd1[4]; + + u8 r_holder:1; + u8 all_tg_pt:1; + u8 rsvd2:6; + + u8 type:4; + u8 scope:4; + + u8 rsvd3[4]; + __be16 rel_tgt_port_id; + __be32 additional_desc_length; + struct iscsi_transport_id iscsi_transport_id[0]; +} __packed; + +struct pr_in_read_full_status_data { + __be32 generation; + __be32 additional_length; + struct pr_in_full_status_descriptor descriptors[0]; +} __packed; + +struct pr_in_read_keys_data { + __be32 generation; + __be32 additional_length; + __be64 keys[0]; +} __packed; + +enum persistent_reserve_out_service_actions { + SERVICE_ACTION_REGISTER = 0x0, + SERVICE_ACTION_RESERVE = 0x1, + SERVICE_ACTION_RELEASE = 0x2, + SERVICE_ACTION_CLEAR = 0x3, + SERVICE_ACTION_PREEMPT = 0x4, + SERVICE_ACTION_PREEMPT_ABORT = 0x5, + SERVICE_ACTION_REGISTER_IGNORE = 0x6, + SERVICE_ACTION_REGISTER_MOVE = 0x7 +}; + +enum persistent_reservation_scope { + PR_SCOPE_LU = 0x0, +}; + +enum persistent_reservation_type { + PR_TYPE_NONE = 0x0, /* "abuse" obsolete value */ + PR_TYPE_WRITE_EXCLUSIVE = 0x1, + PR_TYPE_EXCLUSIVE_ACCESS = 0x3, + PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY = 0x5, + PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY = 0x6, + PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS = 0x7, + PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS = 0x8 +}; + +struct persistent_reserve_out { + u8 opcode; /* PERSISTENT_RESERVE_OUT == 0x5f */ + u8 service_action:5; + u8 rsvd1:3; + + u8 type:4; + u8 scope:4; + + u8 rsvd2[2]; + __be32 parameter_list_length; + u8 control; +} __packed; + +struct pr_out_param_list { + __be64 reservation_key; + __be64 service_action_key; + + u8 obsolete1[4]; + + u8 aptpl:1; + u8 rsvd2:1; + u8 all_tg_pt:1; + u8 spec_i_pt:1; + + u8 rsvd1:4; + + u8 rsvd3; + u8 obsolete2[2]; + u8 additional_parameter_data[0]; +} __packed; + +enum reservation_type { + RESERVATION_TYPE_NONE, RESERVATION_TYPE_RESERVE, RESERVATION_TYPE_PERSISTENT }; -#define READ_WORD(a, b) (((a) << 8) | (b)) +struct reservation { + /* RESERVATION_TYPE_NONE indicates "not reserved" */ + enum reservation_type reservation_type; + enum persistent_reservation_type persistent_type; + u32 generation; + u64 sid; + __be64 reservation_key; + struct list_head registration_list; +}; +static inline bool +pr_is_reserved(const struct reservation* res) +{ + return res->reservation_type != RESERVATION_TYPE_NONE; +} + +struct iscsi_session; + +bool +pr_is_reserved_by_session(const struct reservation *res, + const struct iscsi_session *sess); + +bool +pr_initiator_has_registered(const struct reservation *res, + u64 sid); + struct iscsi_cmnd; -void build_persistent_reserve_out_response(struct iscsi_cmnd *cmnd); -void build_persistent_reserve_in_response(struct iscsi_cmnd *cmnd); +void +build_persistent_reserve_out_response(struct iscsi_cmnd *cmnd); + +void +build_persistent_reserve_in_response(struct iscsi_cmnd *cmnd); + #endif Modified: trunk/kernel/session.c =================================================================== --- trunk/kernel/session.c 2011-09-09 06:28:52 UTC (rev 456) +++ trunk/kernel/session.c 2011-09-09 06:29:20 UTC (rev 457) @@ -121,6 +121,22 @@ return 0; } +void +session_abort_tasks(struct iscsi_session *session, u32 lun) +{ + struct iscsi_conn *conn; + struct iscsi_cmnd *cmnd, *tmp; + + list_for_each_entry(conn, &session->conn_list, list) { + list_for_each_entry_safe(cmnd, tmp, &conn->pdu_list, conn_list) { + if (translate_lun(cmnd_hdr(cmnd)->lun) != lun) + continue; + + __cmnd_abort(cmnd); + } + } +} + int session_del(struct iscsi_target *target, u64 sid) { struct iscsi_session *session; Modified: trunk/kernel/tio.c =================================================================== --- trunk/kernel/tio.c 2011-09-09 06:28:52 UTC (rev 456) +++ trunk/kernel/tio.c 2011-09-09 06:29:20 UTC (rev 457) @@ -57,6 +57,43 @@ return tio; } +void +tio_init_iterator(struct tio *tio, + struct tio_iterator *iter) +{ + iter->tio = tio; + iter->size = 0; + iter->pg_idx = 0; + iter->pg_off = 0; +} + +size_t +tio_add_data(struct tio_iterator *iter, + const u8 *data, + size_t len) +{ + struct tio *tio = iter->tio; + const size_t to_copy = min(tio->pg_cnt * PAGE_SIZE - iter->size, len); + size_t residual = to_copy; + + BUG_ON(tio->size < iter->size); + + do { + u8 *ptr = page_address(iter->tio->pvec[iter->pg_idx]) + iter->pg_off; + size_t chunk = min(PAGE_SIZE - iter->pg_off, residual); + memcpy(ptr, data, chunk); + residual -= chunk; + if (residual || + iter->pg_off + chunk == PAGE_SIZE) { + ++iter->pg_idx; + iter->pg_off = 0; + } else + iter->pg_off += chunk; + } while (residual); + + return to_copy; +} + static void tio_free(struct tio *tio) { int i; Modified: trunk/kernel/ua.c =================================================================== --- trunk/kernel/ua.c 2011-09-09 06:28:52 UTC (rev 456) +++ trunk/kernel/ua.c 2011-09-09 06:29:20 UTC (rev 457) @@ -126,8 +126,8 @@ /* One UA per occurrence of an event */ list_for_each_entry(e, l, entry) { if (e->session == sess && e->lun == lun && - e->asc == asc && e->ascq == ascq && - e->session->exp_cmd_sn == sess->exp_cmd_sn) { + e->asc == asc && e->ascq == ascq && + e->session->exp_cmd_sn == sess->exp_cmd_sn) { spin_unlock(&sess->ua_hash_lock); ua_free(ua); return; Modified: trunk/kernel/volume.c =================================================================== --- trunk/kernel/volume.c 2011-09-09 06:28:52 UTC (rev 456) +++ trunk/kernel/volume.c 2011-09-09 06:29:20 UTC (rev 457) @@ -357,11 +357,10 @@ reservation = &volume->reservation; spin_lock(&volume->reserve_lock); - if (reservation->is_reserved && reservation->sid != sid) + if (pr_is_reserved(reservation) && reservation->sid != sid) err = -EBUSY; else { - reservation->is_reserved = 1; - reservation->type = RESERVATION_TYPE_RESERVE; + reservation->reservation_type = RESERVATION_TYPE_RESERVE; reservation->sid = sid; } @@ -373,24 +372,24 @@ { int err = 0; struct reservation *reservation; - struct list_head *l; - int registered = 0; - u8 write_excl = 0; - u8 excl_access = 0; - u8 write_excl_ro = 0; - u8 excl_access_ro = 0; + bool registered = false; + bool write_excl = false; + bool excl_access = false; + bool write_excl_ro = false; + bool excl_access_ro = false; if (!volume) return -ENOENT; reservation = &volume->reservation; + spin_lock(&volume->reserve_lock); - if (!reservation->is_reserved || reservation->sid == sid) { + if (!pr_is_reserved(reservation) || reservation->sid == sid) { spin_unlock(&volume->reserve_lock); return 0; } - if (reservation->type == RESERVATION_TYPE_RESERVE) { + if (reservation->reservation_type == RESERVATION_TYPE_RESERVE) { switch (scb[0]) { case INQUIRY: case RELEASE: @@ -411,28 +410,23 @@ return err; } - list_for_each(l, &reservation->registration_list) { - struct registration *tmp; - tmp = list_entry(l, struct registration, r_list); - if (tmp->sid == sid) { - registered = 1; - break; - } - } + registered = pr_initiator_has_registered(reservation, sid); switch (reservation->persistent_type) { - case RESERVATION_TYPE_WRITE_EXCLUSIVE: - write_excl = 1; + case PR_TYPE_WRITE_EXCLUSIVE: + write_excl = true; break; - case RESERVATION_TYPE_EXCLUSIVE_ACCESS: - excl_access = 1; + case PR_TYPE_EXCLUSIVE_ACCESS: + excl_access = true; break; - case RESERVATION_TYPE_WRITE_EXCLUSIVE_RO: - write_excl_ro = 1; + case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY: + write_excl_ro = true; break; - case RESERVATION_TYPE_EXCLUSIVE_ACCESS_RO: - excl_access_ro = 1; + case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY: + excl_access_ro = true; break; + default: + break; } switch (scb[0]) { @@ -493,10 +487,9 @@ reservation = &volume->reservation; spin_lock(&volume->reserve_lock); - if (reservation->type == RESERVATION_TYPE_RESERVE && - (force || reservation->sid == sid)) { - reservation->is_reserved = 0; - reservation->type = 0; + if (reservation->reservation_type == RESERVATION_TYPE_RESERVE && + (force || reservation->sid == sid)) { + reservation->reservation_type = RESERVATION_TYPE_NONE; reservation->sid = 0; } else { err = -EBUSY; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2011-09-11 12:16:07
|
Revision: 458 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=458&view=rev Author: agr1 Date: 2011-09-11 12:16:00 +0000 (Sun, 11 Sep 2011) Log Message: ----------- Persistent Reservations: remove usage of bitfields again As much as bitfields improve readability - they're not portable. Signed-off-by: Arne Redlich <arn...@go...> Modified Paths: -------------- trunk/kernel/persist.c trunk/kernel/persist.h Modified: trunk/kernel/persist.c =================================================================== --- trunk/kernel/persist.c 2011-09-09 06:29:20 UTC (rev 457) +++ trunk/kernel/persist.c 2011-09-11 12:16:00 UTC (rev 458) @@ -32,15 +32,8 @@ static const struct pr_in_report_capabilities_data pr_capabilities = { .length = cpu_to_be16(8), - .ptpl_c = 0, - .atp_c = 0, - .sip_c = 0, - .crh = 0, - - .ptpl_a = 0, - .allow_commands = 0, - .tmv = 1, - + .crh_sip_atp_ptpl_c = 0, + .tmv_ptpl_a = PR_IN_REPORT_CAP_TMV, .type_mask = cpu_to_be16(PR_TYPE_WR_EX| PR_TYPE_EX_AC| PR_TYPE_WR_EX_RO| @@ -85,8 +78,7 @@ if (pr_is_reserved(reservation)) { pin_data->reservation_key = reservation->reservation_key; - pin_data->scope = PR_SCOPE_LU; - pin_data->type = reservation->persistent_type; + pin_data->scope_type = PR_SCOPE_LU | reservation->persistent_type; /* * SPC-3, 6.11.3.2 * "The ADDITIONAL LENGTH field contains a count of the number of @@ -146,17 +138,16 @@ list_for_each_entry(reg, &reservation->registration_list, r_list) { const size_t init_name_len = PAD_TO_4_BYTES(strlen(reg->init_name)); const struct iscsi_transport_id tid = { - .format_code = 0, - .protocol_identifier = PROTOCOL_IDENTIFIER_ISCSI, + .fmt_code_proto_id = + TRANSPORT_ID_FMT_CODE_ISCSI|TRANSPORT_ID_PROTO_ID_ISCSI, .additional_length = cpu_to_be16(init_name_len), }; const struct pr_in_full_status_descriptor desc = { .reservation_key = reg->reservation_key, - .all_tg_pt = 0, - .r_holder = pr_is_reserved_by_session(reservation, - cmnd->conn->session), - .scope = PR_SCOPE_LU, - .type = reservation->persistent_type, + .all_tg_pt_r_holder = + pr_is_reserved_by_session(reservation, + cmnd->conn->session), + .scope_type = PR_SCOPE_LU|reservation->persistent_type, /* only rel_tgt_port_id 1 is supported */ .rel_tgt_port_id = cpu_to_be16(1), .additional_desc_length = cpu_to_be32(sizeof(tid) + init_name_len), @@ -177,13 +168,13 @@ addl_data_len += sizeof(desc) + sizeof(tid) + init_name_len; dprintk_pr(cmnd, - "init name %s, sess %#Lx, key %#Lx, rtype %d, ptype %d, r_holder %d, desc.addlen %u, tid.addlen %u, addlen %u, left %u\n", + "init name %s, sess %#Lx, key %#Lx, rtype %d, scope_type %x, all_tg_pt_r_holder %x, desc.addlen %u, tid.addlen %u, addlen %u, left %u\n", reg->init_name, reg->sid, reg->reservation_key, reservation->reservation_type, - desc.type, - desc.r_holder, + desc.scope_type, + desc.all_tg_pt_r_holder, be32_to_cpu(desc.additional_desc_length), be16_to_cpu(tid.additional_length), addl_data_len, @@ -270,10 +261,12 @@ const struct persistent_reserve_in *pr_in = (const struct persistent_reserve_in *)(cmnd_hdr(cmnd)->scb); const u16 allocation_length = be16_to_cpu(pr_in->allocation_length); + const enum pr_in_service_actions action = + pr_in->service_action & PR_SERVICE_ACTION_MASK; dprintk_pr(cmnd, "svc action %x, alloc len %u\n", - pr_in->service_action, + action, allocation_length); if (allocation_length < 8) { @@ -284,7 +277,7 @@ return; } - switch (pr_in->service_action) { + switch (action) { case SERVICE_ACTION_READ_KEYS: pr_in_read_keys(cmnd, allocation_length); break; @@ -355,7 +348,7 @@ goto out; } - if (param->spec_i_pt) { + if ((param->spec_i_pt_all_tg_pt_aptl & PR_OUT_PARAM_SPEC_I_PT)) { iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB_ASC, @@ -494,8 +487,7 @@ static void pr_out_release(struct iscsi_cmnd *cmnd, - enum persistent_reservation_type type, - enum persistent_reservation_scope scope) + enum persistent_reservation_type type) { const struct pr_out_param_list *param = (const struct pr_out_param_list *)page_address(cmnd->tio->pvec[0]); @@ -523,15 +515,6 @@ goto out; } - if (scope != PR_SCOPE_LU || - (reservation->persistent_type != type)) { - iscsi_cmnd_set_sense(cmnd, - ILLEGAL_REQUEST, - INVALID_RELEASE_OF_PERSISTENT_RESERVATION_ASC, - INVALID_RELEASE_OF_PERSISTENT_RESERVATION_ASCQ); - goto out; - } - switch (reservation->persistent_type) { case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY: case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY: @@ -696,15 +679,16 @@ { const struct persistent_reserve_out *pr_out = (const struct persistent_reserve_out *)(cmnd_hdr(cmnd)->scb); + const enum pr_out_service_actions action = + pr_out->service_action & PR_SERVICE_ACTION_MASK; dprintk_pr(cmnd, - "svc action %x, scope %x, type %x, param len %u\n", - pr_out->service_action, - pr_out->scope, - pr_out->type, + "svc action %x, scope_type %x, param len %u\n", + action, + pr_out->scope_type, be32_to_cpu(pr_out->parameter_list_length)); - if (pr_out->scope) { + if ((pr_out->scope_type & PR_SCOPE_MASK)) { iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB_ASC, @@ -720,7 +704,7 @@ return; } - switch (pr_out->service_action) { + switch (action) { case SERVICE_ACTION_REGISTER: pr_out_register(cmnd, false); break; @@ -728,19 +712,23 @@ pr_out_register(cmnd, true); break; case SERVICE_ACTION_RESERVE: - pr_out_reserve(cmnd, pr_out->type); + pr_out_reserve(cmnd, pr_out->scope_type & PR_TYPE_MASK); break; case SERVICE_ACTION_RELEASE: - pr_out_release(cmnd, pr_out->type, pr_out->scope); + pr_out_release(cmnd, pr_out->scope_type & PR_TYPE_MASK); break; case SERVICE_ACTION_CLEAR: pr_out_clear(cmnd); break; case SERVICE_ACTION_PREEMPT: - pr_out_preempt(cmnd, pr_out->type, false); + pr_out_preempt(cmnd, + pr_out->scope_type & PR_TYPE_MASK, + false); break; case SERVICE_ACTION_PREEMPT_ABORT: - pr_out_preempt(cmnd, pr_out->type, true); + pr_out_preempt(cmnd, + pr_out->scope_type & PR_TYPE_MASK, + true); break; default: iscsi_cmnd_set_sense(cmnd, Modified: trunk/kernel/persist.h =================================================================== --- trunk/kernel/persist.h 2011-09-09 06:29:20 UTC (rev 457) +++ trunk/kernel/persist.h 2011-09-11 12:16:00 UTC (rev 458) @@ -43,7 +43,7 @@ #define INSUFFICIENT_REGISTRATION_RESOURCES_ASC 0x55 #define INSUFFICIENT_REGISTRATION_RESOURCES_ASCQ 0x04 -enum persistent_reserve_in_service_actions { +enum pr_in_service_actions { SERVICE_ACTION_READ_KEYS = 0x0, SERVICE_ACTION_READ_RESERVATION = 0x1, SERVICE_ACTION_REPORT_CAPABILITIES = 0x2, @@ -52,9 +52,8 @@ struct persistent_reserve_in { u8 opcode; /* PERSISTENT_RESERVE_IN == 0x5e */ - u8 service_action:5; - u8 rsvd1:3; - u8 rsvd2[5]; + u8 service_action; + u8 rsvd[5]; __be16 allocation_length; u8 control; } __packed; @@ -69,65 +68,73 @@ PR_TYPE_EX_AC_AR = 0x1, /* Excl. Access, All Registrants */ }; +enum { + PR_IN_REPORT_CAP_PTPL_C = 1, /* Persist Through Power Loss Capable */ + PR_IN_REPORT_CAP_ATP_C = 1 << 2, /* All Target Ports Capable */ + PR_IN_REPOER_CAP_SIP_C = 1 << 3, /* Specify Initiator Ports Capable */ + PR_IN_REPORT_CAP_CRH = 1 << 4 /* Compatible Reservation Handling */ +}; + + +enum { + PR_IN_REPORT_CAP_CAP_PTPL_A = 1, /* Persist Through Power Loss Activated */ + PR_IN_REPORT_CAP_TMV = 1 << 7, /* Type Mask Valid */ +}; + struct pr_in_report_capabilities_data { __be16 length; - u8 ptpl_c:1; /* Persist Through Power Loss Capable */ - u8 rsvd1:1; - u8 atp_c:1; /* All Target Ports Capable */ - u8 sip_c:1; /* Specify Initiator Ports Capable */ - u8 crh:1; /* Compatible Reservation Handling */ - u8 rsvd2:3; + u8 crh_sip_atp_ptpl_c; + u8 tmv_ptpl_a; /* SPC-4 has allow_commands here - don't care for now */ - u8 ptpl_a:1; /* Persist Through Power Loss Activated */ - u8 rsvd3:3; - u8 allow_commands:3; /* SPC-4, reserved in SPC-3 */ - u8 tmv:1; /* Type Mask Valid */ - __be16 type_mask; u8 rsvd4[2]; } __packed; +enum { + PR_SERVICE_ACTION_MASK = 0x1f, + PR_TYPE_MASK = 0xf, + PR_SCOPE_MASK = 0xf << 4, +}; + struct pr_in_read_reservation_data { __be32 generation; __be32 additional_length; __be64 reservation_key; u8 obsolete1[4]; u8 rsvd; - - u8 type:4; - u8 scope:4; - + u8 scope_type; u8 obsolete2[2]; } __packed; -enum protocol_identifier { - PROTOCOL_IDENTIFIER_ISCSI = 0x5, +enum { + TRANSPORT_ID_FMT_CODE_MASK = 0xc0, + TRANSPORT_ID_FMT_CODE_ISCSI = 0x0, + TRANSPORT_ID_PROTO_ID_MASK = 0xf, + TRANSPORT_ID_PROTO_ID_ISCSI = 0x5, }; struct iscsi_transport_id { - u8 protocol_identifier:4; - u8 rsvd1:2; - u8 format_code:2; - - u8 rsvd2; + u8 fmt_code_proto_id; + u8 rsvd; __be16 additional_length; u8 iscsi_name[0]; } __packed; +enum { + PR_OUT_STATUS_DESC_R_HOLDER = 1, + PR_OUT_STATUS_DESC_ALL_TG_PT = 1 << 1, +}; + /* this is iscsi specific */ struct pr_in_full_status_descriptor { __be64 reservation_key; u8 rsvd1[4]; - u8 r_holder:1; - u8 all_tg_pt:1; - u8 rsvd2:6; + u8 all_tg_pt_r_holder; + u8 scope_type; - u8 type:4; - u8 scope:4; - - u8 rsvd3[4]; + u8 rsvd2[4]; __be16 rel_tgt_port_id; __be32 additional_desc_length; struct iscsi_transport_id iscsi_transport_id[0]; @@ -145,7 +152,7 @@ __be64 keys[0]; } __packed; -enum persistent_reserve_out_service_actions { +enum pr_out_service_actions { SERVICE_ACTION_REGISTER = 0x0, SERVICE_ACTION_RESERVE = 0x1, SERVICE_ACTION_RELEASE = 0x2, @@ -172,31 +179,25 @@ struct persistent_reserve_out { u8 opcode; /* PERSISTENT_RESERVE_OUT == 0x5f */ - u8 service_action:5; - u8 rsvd1:3; - - u8 type:4; - u8 scope:4; - - u8 rsvd2[2]; + u8 service_action; + u8 scope_type; + u8 rsvd[2]; __be32 parameter_list_length; u8 control; } __packed; +enum { + PR_OUT_PARAM_APTPL = 1, + PR_OUT_PARAM_ALL_TG_PT = 1 << 2, + PR_OUT_PARAM_SPEC_I_PT = 1 << 3, +}; + struct pr_out_param_list { __be64 reservation_key; __be64 service_action_key; - u8 obsolete1[4]; - - u8 aptpl:1; - u8 rsvd2:1; - u8 all_tg_pt:1; - u8 spec_i_pt:1; - - u8 rsvd1:4; - - u8 rsvd3; + u8 spec_i_pt_all_tg_pt_aptl; + u8 rsvd; u8 obsolete2[2]; u8 additional_parameter_data[0]; } __packed; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-03-22 21:25:52
|
Revision: 470 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=470&view=rev Author: agr1 Date: 2012-03-22 21:25:45 +0000 (Thu, 22 Mar 2012) Log Message: ----------- Compile fix for kernels 3.1 and 3.2 Submitted by Emmanuel Florac <ef...@in...> Modified Paths: -------------- trunk/kernel/event.c trunk/kernel/iscsi.h trunk/kernel/nthread.c Modified: trunk/kernel/event.c =================================================================== --- trunk/kernel/event.c 2012-03-22 21:25:18 UTC (rev 469) +++ trunk/kernel/event.c 2012-03-22 21:25:45 UTC (rev 470) @@ -6,6 +6,7 @@ * Some functions are based on audit code. */ +#include <linux/module.h> #include <net/tcp.h> #include "iet_u.h" #include "iscsi_dbg.h" Modified: trunk/kernel/iscsi.h =================================================================== --- trunk/kernel/iscsi.h 2012-03-22 21:25:18 UTC (rev 469) +++ trunk/kernel/iscsi.h 2012-03-22 21:25:45 UTC (rev 470) @@ -9,7 +9,9 @@ #define __ISCSI_H__ #include <linux/blkdev.h> +#include <linux/module.h> #include <linux/completion.h> +#include <linux/completion.h> #include <linux/pagemap.h> #include <linux/seq_file.h> #include <linux/mm.h> Modified: trunk/kernel/nthread.c =================================================================== --- trunk/kernel/nthread.c 2012-03-22 21:25:18 UTC (rev 469) +++ trunk/kernel/nthread.c 2012-03-22 21:25:45 UTC (rev 470) @@ -6,6 +6,8 @@ * This code is licenced under the GPL. */ +#include <linux/module.h> +#include <linux/file.h> #include <linux/sched.h> #include <linux/file.h> #include <linux/kthread.h> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-04-01 13:01:54
|
Revision: 476 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=476&view=rev Author: agr1 Date: 2012-04-01 13:01:47 +0000 (Sun, 01 Apr 2012) Log Message: ----------- Add support for All-Registrants-type reservations Hi All: This patch updates the SCSI-3 Persistent Reservation code to add support for reservations of type=7 (Write Exclusive, All Registrants) and type=8 (Exclusive Access, All Registrants), as these newer reservation types are needed by some types of clustering software. Existing behavior for other reservation types does not change. Target tested on SLES 11 SP2, with one initiator on the same system, and one on a OpenSUSE 12.1 system, and one on a SLES 11 SP2 VM (all using open-iscsi). Tested: - REGISTER [and unregister] - REPORT CAPABILITIES - READ FULL STATUS - RESERVE - RELEASE - CLEAR - PREEMPT Signed-of-by: Lee Duncan <ld...@su...> persist.c | 115 +++++++++++++++++++++++++++++++++++++------------------------- persist.h | 7 +++ volume.c | 2 + 3 files changed, 79 insertions(+), 45 deletions(-) Modified Paths: -------------- trunk/kernel/persist.c trunk/kernel/persist.h trunk/kernel/volume.c Modified: trunk/kernel/persist.c =================================================================== --- trunk/kernel/persist.c 2012-04-01 11:02:06 UTC (rev 475) +++ trunk/kernel/persist.c 2012-04-01 13:01:47 UTC (rev 476) @@ -9,21 +9,28 @@ #include "iscsi_dbg.h" #include "persist.h" -bool -pr_is_reserved_by_session(const struct reservation *res, - const struct iscsi_session *sess) -{ - return (pr_is_reserved(res) && res->sid == sess->sid); -} - static bool pr_is_reserved_by_sid(const struct reservation *res, const u64 sid) { - return (pr_is_reserved(res) && res->sid == sid); + if (!pr_is_reserved(res)) + return false; + + if (pr_type_is_all_registrants(res)) { + return pr_initiator_has_registered(res, sid); + } else { + return res->sid == sid; + } } bool +pr_is_reserved_by_session(const struct reservation *res, + const struct iscsi_session *sess) +{ + return pr_is_reserved_by_sid(res, sess->sid); +} + +bool pr_initiator_has_registered(const struct reservation *res, u64 sid) { @@ -44,7 +51,9 @@ .type_mask = cpu_to_be16(PR_TYPE_WR_EX| PR_TYPE_EX_AC| PR_TYPE_WR_EX_RO| - PR_TYPE_EX_AC_RO), + PR_TYPE_EX_AC_RO| + PR_TYPE_WR_EX_AR| + PR_TYPE_EX_AC_AR), }; static void @@ -79,7 +88,10 @@ pin_data->generation = cpu_to_be32(reservation->generation); if (pr_is_reserved(reservation)) { - pin_data->reservation_key = reservation->reservation_key; + if (pr_type_is_all_registrants(reservation)) + pin_data->reservation_key = 0; + else + pin_data->reservation_key = reservation->reservation_key; pin_data->scope_type = PR_SCOPE_LU | reservation->persistent_type; /* * SPC-3, 6.11.3.2 @@ -350,7 +362,8 @@ } if (!param->service_action_key) { - if (pr_is_reserved_by_session(reservation, session)) { + if (pr_is_reserved_by_session(reservation, session) && + !pr_type_is_all_registrants(reservation)) { reservation->reservation_type = RESERVATION_TYPE_NONE; reservation->persistent_type = 0; reservation->reservation_key = 0; @@ -361,6 +374,12 @@ } list_del(®->r_list); kfree(reg); + if (list_empty(&reservation->registration_list) && + pr_type_is_all_registrants(reservation)) { + reservation->reservation_type = RESERVATION_TYPE_NONE; + reservation->persistent_type = 0; + reservation->reservation_key = 0; + } } else { reg->reservation_key = param->service_action_key; } @@ -410,6 +429,8 @@ case PR_TYPE_EXCLUSIVE_ACCESS: case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY: case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY: + case PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS: + case PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS: return true; default: return false; @@ -500,17 +521,20 @@ goto out; } - if (reservation->sid != session->sid) - goto out; - - if (reservation->reservation_key != param->reservation_key) { - cmnd->status = SAM_STAT_RESERVATION_CONFLICT; - goto out; + if (!pr_type_is_all_registrants(reservation)) { + if (reservation->sid != session->sid) + goto out; + if (reservation->reservation_key != param->reservation_key) { + cmnd->status = SAM_STAT_RESERVATION_CONFLICT; + goto out; + } } switch (reservation->persistent_type) { case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY: case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY: + case PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS: + case PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS: send_ua = true; break; default: @@ -601,54 +625,55 @@ struct reservation *reservation = &volume->reservation; bool all = 0; - if (!param->service_action_key) { + spin_lock(&volume->reserve_lock); + if (!param->service_action_key && + !pr_type_is_all_registrants(reservation)) { iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, INVALID_FIELD_IN_PARAMETER_LIST_ASC, INVALID_FIELD_IN_PARAMETER_LIST_ASCQ); - return; + goto out; } - spin_lock(&volume->reserve_lock); registered = pr_initiator_has_registered(reservation, session->sid); if (!registered) { cmnd->status = SAM_STAT_RESERVATION_CONFLICT; goto out; } - if (pr_is_reserved(reservation) && - reservation->reservation_key == param->service_action_key && - reservation->sid != session->sid) { - if (!persistent_type_valid(pr_type)) { - iscsi_cmnd_set_sense(cmnd, - ILLEGAL_REQUEST, - INVALID_FIELD_IN_CDB_ASC, - INVALID_FIELD_IN_CDB_ASCQ); - goto out; - } + if (pr_is_reserved(reservation)) { + if ((!pr_type_is_all_registrants(reservation) && + reservation->reservation_key == param->service_action_key && + reservation->sid != session->sid) || + (pr_type_is_all_registrants(reservation) && + !param->service_action_key)) { - reserv_session = session_lookup(target, reservation->sid); - if (reserv_session) { - if (abort) - session_abort_tasks(reserv_session, volume->lun); - ua_establish_for_session(reserv_session, - volume->lun, - RESERVATIONS_PREEMPTED_ASC, - RESERVATIONS_PREEMPTED_ASCQ); + reserv_session = session_lookup(target, reservation->sid); + if (reserv_session) { + if (abort) + session_abort_tasks(reserv_session, + volume->lun); + ua_establish_for_session(reserv_session, + volume->lun, + RESERVATIONS_PREEMPTED_ASC, + RESERVATIONS_PREEMPTED_ASCQ); + } + + reservation->reservation_type = RESERVATION_TYPE_PERSISTENT; + reservation->sid = session->sid; + reservation->reservation_key = param->reservation_key; + reservation->persistent_type = pr_type; + all = true; } - - reservation->reservation_type = RESERVATION_TYPE_PERSISTENT; - reservation->sid = session->sid; - reservation->reservation_key = param->reservation_key; - reservation->persistent_type = pr_type; - all = true; } list_for_each_entry_safe(reg, tmp_reg, &reservation->registration_list, r_list) { if (reg->sid == session->sid) continue; - if (!all && reg->reservation_key != param->service_action_key) + if (!all && + reg->reservation_key != param->service_action_key && + !pr_type_is_all_registrants(reservation)) continue; reserv_session = session_lookup(target, reg->sid); Modified: trunk/kernel/persist.h =================================================================== --- trunk/kernel/persist.h 2012-04-01 11:02:06 UTC (rev 475) +++ trunk/kernel/persist.h 2012-04-01 13:01:47 UTC (rev 476) @@ -224,6 +224,13 @@ return res->reservation_type != RESERVATION_TYPE_NONE; } +static inline bool +pr_type_is_all_registrants(const struct reservation *res) +{ + return ((res->persistent_type == PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS) || + (res->persistent_type == PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS)); +} + struct iscsi_session; bool Modified: trunk/kernel/volume.c =================================================================== --- trunk/kernel/volume.c 2012-04-01 11:02:06 UTC (rev 475) +++ trunk/kernel/volume.c 2012-04-01 13:01:47 UTC (rev 476) @@ -420,9 +420,11 @@ excl_access = true; break; case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY: + case PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS: write_excl_ro = true; break; case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY: + case PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS: excl_access_ro = true; break; default: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-04-16 19:48:41
|
Revision: 479 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=479&view=rev Author: agr1 Date: 2012-04-16 19:48:34 +0000 (Mon, 16 Apr 2012) Log Message: ----------- refactor tio.c 1) tio->offset is changed to loff_t and stores target bytes offset directly. all place referencing tio->offset is fixed. 2) get_pgcnt() macro now only needs size paramter to cacluate the necessary pages, and data should always starts in offset 0 in first page allocated. This patch is tested with ESXi5 initiator, badblocks repeatly with different block size. both file-io and block-io is tested. adttionaly ext4 mkfs and fsck is tested. Signed-off-by: Yucong Sun <sun...@gm...> Modified Paths: -------------- trunk/kernel/block-io.c trunk/kernel/digest.c trunk/kernel/file-io.c trunk/kernel/iscsi.c trunk/kernel/iscsi.h trunk/kernel/persist.c trunk/kernel/target_disk.c trunk/kernel/tio.c Modified: trunk/kernel/block-io.c =================================================================== --- trunk/kernel/block-io.c 2012-04-06 21:58:31 UTC (rev 478) +++ trunk/kernel/block-io.c 2012-04-16 19:48:34 UTC (rev 479) @@ -58,14 +58,13 @@ struct bio *tio_bio = NULL, *bio = NULL, *biotail = NULL; struct blk_plug plug; - u32 offset = tio->offset; u32 size = tio->size; u32 tio_index = 0; int max_pages = 1; int err = 0; - loff_t ppos = ((loff_t) tio->idx << PAGE_SHIFT) + offset; + loff_t ppos = tio->offset; /* Calculate max_pages for bio_alloc (memory saver) */ if (bdev_q) @@ -102,19 +101,17 @@ /* Loop for filling bio */ while (tio_index < tio->pg_cnt) { - unsigned int bytes = PAGE_SIZE - offset; + unsigned int bytes = PAGE_SIZE; if (bytes > size) bytes = size; - if (!bio_add_page(bio, tio->pvec[tio_index], bytes, offset)) + if (!bio_add_page(bio, tio->pvec[tio_index], bytes, 0)) break; size -= bytes; ppos += bytes; - offset = 0; - tio_index++; } } Modified: trunk/kernel/digest.c =================================================================== --- trunk/kernel/digest.c 2012-04-06 21:58:31 UTC (rev 478) +++ trunk/kernel/digest.c 2012-04-16 19:48:34 UTC (rev 479) @@ -200,29 +200,22 @@ { struct scatterlist *sg = cmnd->conn->hash_sg; u32 size, length; - int i, idx, count; + int i, idx; unsigned int nbytes; size = cmnd->pdu.datasize; nbytes = size = (size + 3) & ~3; - offset += tio->offset; idx = offset >> PAGE_CACHE_SHIFT; offset &= ~PAGE_CACHE_MASK; - count = get_pgcnt(size, offset); - assert(idx + count <= tio->pg_cnt); - assert(count <= ISCSI_CONN_IOV_MAX); + assert(idx <= ISCSI_CONN_IOV_MAX); sg_init_table(sg, ARRAY_SIZE(cmnd->conn->hash_sg)); crypto_hash_init(hash); - for (i = 0; size; i++) { - if (offset + size > PAGE_CACHE_SIZE) - length = PAGE_CACHE_SIZE - offset; - else - length = size; - + for (i = 0; size > 0; i++) { + length = min_t(u32, PAGE_CACHE_SIZE - offset, size); sg_set_page(&sg[i], tio->pvec[idx + i], length, offset); size -= length; offset = 0; Modified: trunk/kernel/file-io.c =================================================================== --- trunk/kernel/file-io.c 2012-04-06 21:58:31 UTC (rev 478) +++ trunk/kernel/file-io.c 2012-04-16 19:48:34 UTC (rev 479) @@ -24,31 +24,22 @@ struct file *filp; mm_segment_t oldfs; struct page *page; - u32 offset, size; - loff_t ppos, count; + loff_t ppos; char *buf; int i, err = 0; - ssize_t ret; + u32 count, size, ret; assert(p); filp = p->filp; size = tio->size; - offset= tio->offset; + ppos = tio->offset; - ppos = (loff_t) tio->idx << PAGE_CACHE_SHIFT; - ppos += offset; - for (i = 0; i < tio->pg_cnt; i++) { page = tio->pvec[i]; assert(page); buf = page_address(page); - buf += offset; + count = min_t(u32, PAGE_CACHE_SIZE, size); - if (offset + size > PAGE_CACHE_SIZE) - count = PAGE_CACHE_SIZE - offset; - else - count = size; - oldfs = get_fs(); set_fs(get_ds()); @@ -60,12 +51,12 @@ set_fs(oldfs); if (ret != count) { - eprintk("I/O error %lld, %ld\n", count, (long) ret); + eprintk("I/O error %u, %ld\n", count, (long) ret); err = -EIO; + break; } size -= count; - offset = 0; } assert(!size); @@ -81,8 +72,7 @@ int res; if (tio) { - ppos = (loff_t) tio->idx << PAGE_CACHE_SHIFT; - ppos += tio->offset; + ppos = tio->offset; count = tio->size; } else { ppos = 0; Modified: trunk/kernel/iscsi.c =================================================================== --- trunk/kernel/iscsi.c 2012-04-06 21:58:31 UTC (rev 478) +++ trunk/kernel/iscsi.c 2012-04-16 19:48:34 UTC (rev 479) @@ -331,8 +331,7 @@ rsp->pdu.datasize = sizeof(struct iscsi_sense_data) + IET_SENSE_BUF_SIZE; - rsp->tio->size = (rsp->pdu.datasize + 3) & -4; - rsp->tio->offset = 0; + tio_set(rsp->tio, (rsp->pdu.datasize + 3) & -4, 0); } return rsp; @@ -706,18 +705,15 @@ char *addr; dprintk(D_GENERIC, "%p %u,%u\n", tio, offset, size); - offset += tio->offset; if (!size) return 0; - if (!(offset < tio->offset + tio->size) || - !(offset + size <= tio->offset + tio->size)) { - eprintk("%u %u %u %u", offset, size, tio->offset, tio->size); + if (!(offset + size <= tio->pg_cnt * PAGE_CACHE_SIZE)) { + eprintk("bad offset: o:%u s:%u total:%lu", + offset, size, tio->pg_cnt * PAGE_CACHE_SIZE); return -EIO; } - assert(offset < tio->offset + tio->size); - assert(offset + size <= tio->offset + tio->size); idx = offset >> PAGE_CACHE_SHIFT; offset &= ~PAGE_CACHE_MASK; @@ -924,7 +920,7 @@ conn->read_msg.msg_iov = conn->read_iov; if (cmnd->pdu.bhs.itt != cpu_to_be32(ISCSI_RESERVED_TAG)) { struct tio *tio; - int pg_cnt = get_pgcnt(size, 0); + int pg_cnt = get_pgcnt(size); assert(pg_cnt < ISCSI_CONN_IOV_MAX); cmnd->tio = tio = tio_alloc(pg_cnt); @@ -1030,7 +1026,7 @@ } set_offset_and_length(req->lun, req_hdr->scb, &offset, &length); - req->tio = tio_alloc(get_pgcnt(length, offset)); + req->tio = tio_alloc(get_pgcnt(length)); tio_set(req->tio, length, offset); break; } @@ -1070,7 +1066,7 @@ if (cmnd_write_size(req) != length) eprintk("%x %u %u\n", cmnd_itt(req), cmnd_write_size(req), length); - req->tio = tio_alloc(get_pgcnt(length, offset)); + req->tio = tio_alloc(get_pgcnt(length)); tio_set(req->tio, length, offset); if (req->pdu.datasize) { @@ -1451,7 +1447,7 @@ rsp->tio = req->tio; } - assert(get_pgcnt(req->pdu.datasize, 0) < ISCSI_CONN_IOV_MAX); + assert(get_pgcnt(req->pdu.datasize) < ISCSI_CONN_IOV_MAX); rsp->pdu.datasize = req->pdu.datasize; iscsi_cmnd_init_write(rsp); } else { @@ -1609,10 +1605,8 @@ static void __cmnd_send_pdu(struct iscsi_conn *conn, struct tio *tio, u32 offset, u32 size) { dprintk(D_GENERIC, "%p %u,%u\n", tio, offset, size); - offset += tio->offset; - assert(offset <= tio->offset + tio->size); - assert(offset + size <= tio->offset + tio->size); + assert(offset + size <= tio->pg_cnt * PAGE_CACHE_SIZE); conn->write_tcmnd = tio; conn->write_offset = offset; Modified: trunk/kernel/iscsi.h =================================================================== --- trunk/kernel/iscsi.h 2012-04-06 21:58:31 UTC (rev 478) +++ trunk/kernel/iscsi.h 2012-04-16 19:48:34 UTC (rev 479) @@ -55,16 +55,15 @@ int nop_timeout; }; +/* target io */ struct tio { - u32 pg_cnt; + loff_t offset; /* byte offset on target */ + u32 size; /* total io bytes */ - pgoff_t idx; - u32 offset; - u32 size; + u32 pg_cnt; /* total page count */ + struct page **pvec; /* array of pages holding data */ - struct page **pvec; - - atomic_t count; + atomic_t count; /* ref count */ }; struct tio_iterator { @@ -454,7 +453,7 @@ 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) +#define get_pgcnt(size) (((size) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT) static inline void iscsi_cmnd_get_length(struct iscsi_pdu *pdu) { Modified: trunk/kernel/persist.c =================================================================== --- trunk/kernel/persist.c 2012-04-06 21:58:31 UTC (rev 478) +++ trunk/kernel/persist.c 2012-04-16 19:48:34 UTC (rev 479) @@ -273,7 +273,7 @@ case SERVICE_ACTION_READ_FULL_STATUS: if (allocation_length == 0) return; - cmnd->tio = tio_alloc(get_pgcnt(allocation_length, 0)); + cmnd->tio = tio_alloc(get_pgcnt(allocation_length)); break; default: eprintk("%#Lx:%hu: invalid PR In Service Action %x\n", Modified: trunk/kernel/target_disk.c =================================================================== --- trunk/kernel/target_disk.c 2012-04-06 21:58:31 UTC (rev 478) +++ trunk/kernel/target_disk.c 2012-04-16 19:48:34 UTC (rev 479) @@ -291,7 +291,7 @@ size = min(size & ~(8 - 1), len + 8); assert(!tio); - tio = cmnd->tio = tio_alloc(get_pgcnt(size, 0)); + tio = cmnd->tio = tio_alloc(get_pgcnt(size)); tio_set(tio, size, 0); data = page_address(tio->pvec[idx]); Modified: trunk/kernel/tio.c =================================================================== --- trunk/kernel/tio.c 2012-04-06 21:58:31 UTC (rev 478) +++ trunk/kernel/tio.c 2012-04-16 19:48:34 UTC (rev 479) @@ -44,7 +44,6 @@ tio = kmem_cache_alloc(tio_cache, GFP_KERNEL | __GFP_NOFAIL); tio->pg_cnt = 0; - tio->idx = 0; tio->offset = 0; tio->size = 0; tio->pvec = NULL; @@ -119,8 +118,7 @@ void tio_set(struct tio *tio, u32 size, loff_t offset) { - tio->idx = offset >> PAGE_CACHE_SHIFT; - tio->offset = offset & ~PAGE_CACHE_MASK; + tio->offset = offset; tio->size = size; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2012-04-16 19:56:42
|
Revision: 480 http://iscsitarget.svn.sourceforge.net/iscsitarget/?rev=480&view=rev Author: agr1 Date: 2012-04-16 19:56:35 +0000 (Mon, 16 Apr 2012) Log Message: ----------- Implement WRITE_SAME_16(0x93) WRITE_SAME_16 provides 1 block of data and iscsi target writes the same data to the offset and length specified. If length is 0, the target should write to end of media. This change is tested with ESXi5 lazy eager zero disk. Signed-off-by: Yucong Sun <sun...@gm...> Modified Paths: -------------- trunk/kernel/iscsi.c trunk/kernel/iscsi_dbg.h trunk/kernel/target_disk.c trunk/kernel/volume.c Modified: trunk/kernel/iscsi.c =================================================================== --- trunk/kernel/iscsi.c 2012-04-16 19:48:34 UTC (rev 479) +++ trunk/kernel/iscsi.c 2012-04-16 19:56:35 UTC (rev 480) @@ -773,6 +773,7 @@ break; case READ_16: case WRITE_16: + case WRITE_SAME_16: *off = (u64)cmd[2] << 56 | (u64)cmd[3] << 48 | (u64)cmd[4] << 40 | (u64)cmd[5] << 32 | (u64)cmd[6] << 24 | (u64)cmd[7] << 16 | @@ -1035,6 +1036,7 @@ case WRITE_10: case WRITE_16: case WRITE_VERIFY: + case WRITE_SAME_16: { struct iscsi_sess_param *param = &conn->session->param; loff_t offset; @@ -1063,12 +1065,39 @@ eprintk("Verification is ignored %x\n", cmnd_itt(req)); set_offset_and_length(req->lun, req_hdr->scb, &offset, &length); - if (cmnd_write_size(req) != length) - eprintk("%x %u %u\n", cmnd_itt(req), cmnd_write_size(req), length); - req->tio = tio_alloc(get_pgcnt(length)); - tio_set(req->tio, length, offset); + if (req_hdr->scb[0] == WRITE_SAME_16) { + dprintk(D_VAAI, "WRITE_SAME_16: D: %u L:%u O:%llu\n", + cmnd_write_size(req), length, offset); + if (unlikely(cmnd_write_size(req) > PAGE_CACHE_SIZE)) { + eprintk("WRITE_SAME data is bigger than page size" + ", this is unsupported: D:%u P:%lu\n", + cmnd_write_size(req), PAGE_CACHE_SIZE); + goto error; + } + + if (unlikely(length % cmnd_write_size(req) != 0)) { + eprintk("WRITE_SAME data size error: L:%u D:%u\n", + length, cmnd_write_size(req)); + goto error; + } + + if (unlikely((req_hdr->scb[1] & 0x06) != 0)) + eprintk("WRITE_SAME LB/PBDATA ignored: %x\n", + req_hdr->scb[1]); + + req->tio = tio_alloc(1); + tio_set(req->tio, length, offset); + } else { + if (unlikely(cmnd_write_size(req) != length)) + eprintk("%x %u %u\n", + cmnd_itt(req), cmnd_write_size(req), + length); + req->tio = tio_alloc(get_pgcnt(length)); + tio_set(req->tio, length, offset); + } + if (req->pdu.datasize) { if (cmnd_recv_pdu(conn, req->tio, 0, req->pdu.datasize) < 0) assert(0); Modified: trunk/kernel/iscsi_dbg.h =================================================================== --- trunk/kernel/iscsi_dbg.h 2012-04-16 19:48:34 UTC (rev 479) +++ trunk/kernel/iscsi_dbg.h 2012-04-16 19:56:35 UTC (rev 480) @@ -12,6 +12,7 @@ #define D_IOMODE (1UL << 8) #define D_UAC (1UL << 9) #define D_PR (1UL << 10) +#define D_VAAI (1UL << 11) #define D_DATA (D_READ | D_WRITE) Modified: trunk/kernel/target_disk.c =================================================================== --- trunk/kernel/target_disk.c 2012-04-16 19:48:34 UTC (rev 479) +++ trunk/kernel/target_disk.c 2012-04-16 19:56:35 UTC (rev 480) @@ -408,6 +408,74 @@ iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x03, 0x0); } +static void build_write_same_response(struct iscsi_cmnd *cmnd) { + int err; + struct tio *target_tio; + struct iet_volume *lu = cmnd->lun; + struct tio_iterator iter; + u32 MAX_IO_SIZE = 1 << 30; /* 1MByte */ + u32 length, medium_length; + loff_t offset; + u8 *data_addr; + u32 data_size; + + length = cmnd->tio->size; + medium_length = (u32)((loff_t) lu->blk_cnt << lu->blk_shift); + offset = cmnd->tio->offset; + + data_addr = page_address(cmnd->tio->pvec[0]); + data_size = be32_to_cpu(cmnd_hdr(cmnd)->data_length); + + /* When length = 0, it means we need to write to the end. */ + if (length == 0) { + length = medium_length - offset + 1; + dprintk(D_VAAI, + "write to end, calculated length = %u\n", length); + } + + if (length + offset > medium_length) { + /* Write out of boundary, Invalid Field in CDB */ + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x24, 0x0); + return; + } + + list_del_init(&cmnd->list); + + /* Fill target_tio with data from request, because it's all + same data, we can just reuse it later with differnt offset. */ + target_tio = tio_alloc(get_pgcnt(min_t(u32, length, MAX_IO_SIZE))); + tio_get(target_tio); + + tio_init_iterator(target_tio, &iter); + while(iter.pg_idx < target_tio->pg_cnt) { + tio_add_data(&iter, data_addr, data_size); + } + + while (length > 0) { + u32 to_write = min_t(u32, length, MAX_IO_SIZE); + tio_set(target_tio, to_write, offset); + + /* submit to IO layer */ + err = tio_write(lu, target_tio); + if (!err && !LUWCache(lu)) + err = tio_sync(lu, target_tio); + + if (err) { + /* Medium Error/Write Fault */ + iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x03, 0x0); + break; + } + + length -= to_write; + offset += to_write; + dprintk(D_VAAI, + "Committed %u bytes, %u left, offset %llu\n", + to_write, length, offset); + } + + tio_put(target_tio); +} + static void build_sync_cache_response(struct iscsi_cmnd *cmnd) { assert(cmnd->lun); @@ -550,6 +618,9 @@ case WRITE_VERIFY: send_scsi_rsp(cmnd, build_write_response); break; + case WRITE_SAME_16: + send_scsi_rsp(cmnd, build_write_same_response); + break; case SYNCHRONIZE_CACHE: send_scsi_rsp(cmnd, build_sync_cache_response); break; Modified: trunk/kernel/volume.c =================================================================== --- trunk/kernel/volume.c 2012-04-16 19:48:34 UTC (rev 479) +++ trunk/kernel/volume.c 2012-04-16 19:56:35 UTC (rev 480) @@ -446,6 +446,7 @@ case WRITE_10: case WRITE_12: case WRITE_16: + case WRITE_SAME_16: case WRITE_VERIFY: case SYNCHRONIZE_CACHE: if (write_excl || excl_access) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2013-07-03 18:34:52
|
Revision: 494 http://sourceforge.net/p/iscsitarget/code/494 Author: agr1 Date: 2013-07-03 18:34:49 +0000 (Wed, 03 Jul 2013) Log Message: ----------- Avoid zero sized bio_add_page (blockio) / vfs_{read,write} (fileio) calls From: Shivaram Upadhyayula <shi...@qu...> Let suppose the total write length for the write same command is 1048576 + 512 (1MB + 512). As per the logic the writes are split into two due to the MAX_IO_SIZE limitation of 1MB tio pg_cnt will be (1MB / 4096)) == 256 while (length > 0) { u32 to_write = (u32)min_t(u64, length, MAX_IO_SIZE); So the first iteration will be to_write == 1M The second iteration will be to_write == 512 target_tio pg_cnt will be 256 as we dont modify that and is required to free up the pages later Now examining block-io.c make_request while (tio_index < tio->pg_cnt) { Notice above that for the second iteration we will iterate from 0 to 256 but we need to write only 512 bytes. With the 512 byte write, for the first iteration in the below loop /* Loop for filling bio */ while (tio_index < tio->pg_cnt) { unsigned int bytes = PAGE_SIZE; if (bytes > size) bytes = size; if (!bio_add_page(bio, tio->pvec[tio_index], bytes, 0)) break; size -= bytes; ppos += bytes; tio_index++; In the above loop, we add the 512 bytes to the bio in the first iteration itself. In the second iteration of the above loops bytes == size == 0 and so on till 256 and in the second iteration we call bio_add_page with size 0. (redlicha: this was part of another patch and split out to a distinct patch) Signed-off-by: Shivaram Upadhyayula <shi...@qu...> Signed-off-by: Arne Redlich <arn...@go...> Modified Paths: -------------- trunk/kernel/block-io.c trunk/kernel/file-io.c Modified: trunk/kernel/block-io.c =================================================================== --- trunk/kernel/block-io.c 2013-07-02 19:33:19 UTC (rev 493) +++ trunk/kernel/block-io.c 2013-07-03 18:34:49 UTC (rev 494) @@ -79,7 +79,7 @@ init_completion(&tio_work->tio_complete); /* Main processing loop, allocate and fill all bios */ - while (tio_index < tio->pg_cnt) { + while (size && tio_index < tio->pg_cnt) { bio = bio_alloc(GFP_KERNEL, min(max_pages, BIO_MAX_PAGES)); if (!bio) { err = -ENOMEM; @@ -100,7 +100,7 @@ atomic_inc(&tio_work->bios_remaining); /* Loop for filling bio */ - while (tio_index < tio->pg_cnt) { + while (size && tio_index < tio->pg_cnt) { unsigned int bytes = PAGE_SIZE; if (bytes > size) Modified: trunk/kernel/file-io.c =================================================================== --- trunk/kernel/file-io.c 2013-07-02 19:33:19 UTC (rev 493) +++ trunk/kernel/file-io.c 2013-07-03 18:34:49 UTC (rev 494) @@ -34,7 +34,7 @@ size = tio->size; ppos = tio->offset; - for (i = 0; i < tio->pg_cnt; i++) { + for (i = 0; i < tio->pg_cnt && size; i++) { page = tio->pvec[i]; assert(page); buf = page_address(page); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ag...@us...> - 2014-05-06 20:58:59
|
Revision: 500 http://sourceforge.net/p/iscsitarget/code/500 Author: agr1 Date: 2014-05-06 20:58:55 +0000 (Tue, 06 May 2014) Log Message: ----------- Use conn->list_lock only with soft interrupts disabled Otherwise we're in for a deadlock with the network softirq: kernel: BUG: soft lockup - CPU#5 stuck for 60s! [istd9:13929] kernel: CPU 5: kernel: Modules linked in: iscsi_trgt(U) netconsole autofs4 ip_conntrack_netbios_ns ipt_REJECT xt_tcpudp xt_state ip_conntrack nfnetlink iptable_filter ip_tables x_tables dm_round_robin dm_multipath scsi_dh video backlight sbs power_meter hwmon i2c_ec i2c_core dell_wmi wmi button battery asus_acpi acpi_memhotplug ac parport_pc lp parport joydev sr_mod cdrom sg ixgbe(U) qla2xxx i7core_edac bnx2 tpm_tis edac_mc tpm tpm_bios 8021q scsi_transport_fc serio_raw pcspkr dca dm_raid45 dm_message dm_region_hash dm_mem_cache dm_snapshot dm_zero dm_mirror dm_log dm_mod ata_piix libata shpchp megaraid_sas sd_mod scsi_mod ext3 jbd uhci_hcd ohci_hcd ehci_hcd kernel: Pid: 13929, comm: istd9 Tainted: G ---- 2.6.18-308.4.1.el5 #1 kernel: RIP: 0010:[<ffffffff80064bdf>] [<ffffffff80064bdf>] .text.lock.spinlock+0x5/0x30 kernel: RSP: 0018:ffff8106451f7b88 EFLAGS: 00000286 kernel: RAX: 00000000ffffffff RBX: ffff810115bbec00 RCX: 00000000fbfce19a kernel: RDX: ffff810c10c83e68 RSI: ffff810c10c83980 RDI: ffff810c16c46058 kernel: RBP: ffff8106451f7b00 R08: 0000000000000003 R09: 00000000fbfce19a kernel: R10: 0000000080000000 R11: 0000000000000014 R12: ffffffff8005dc8e kernel: R13: ffff810c16c46000 R14: ffffffff8007944e R15: ffff8106451f7b00 kernel: FS: 0000000000000000(0000) GS:ffff81062fcdae40(0000) knlGS:0000000000000000 kernel: CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b kernel: CR2: 000000000ef82af0 CR3: 0000000000201000 CR4: 00000000000006a0 kernel: kernel: Call Trace: kernel: <IRQ> [<ffffffff885c5042>] :iscsi_trgt:conn_close+0x26/0x97 kernel: [<ffffffff885c538f>] :iscsi_trgt:iet_state_change+0x24/0x3e kernel: [<ffffffff800541df>] tcp_fin+0x19c/0x1ec Signed-off-by: Arne Redlich <arn...@go...> Revision Links: -------------- http://sourceforge.net/p/iscsitarget/code/08 http://sourceforge.net/p/iscsitarget/code/09 http://sourceforge.net/p/iscsitarget/code/10 http://sourceforge.net/p/iscsitarget/code/11 http://sourceforge.net/p/iscsitarget/code/12 http://sourceforge.net/p/iscsitarget/code/13 http://sourceforge.net/p/iscsitarget/code/14 http://sourceforge.net/p/iscsitarget/code/15 Modified Paths: -------------- trunk/kernel/conn.c trunk/kernel/iscsi.c trunk/kernel/nthread.c Modified: trunk/kernel/conn.c =================================================================== --- trunk/kernel/conn.c 2014-01-26 18:30:45 UTC (rev 499) +++ trunk/kernel/conn.c 2014-05-06 20:58:55 UTC (rev 500) @@ -218,7 +218,7 @@ if (test_and_clear_bit(CONN_ACTIVE, &conn->state)) set_bit(CONN_CLOSING, &conn->state); - spin_lock(&conn->list_lock); + spin_lock_bh(&conn->list_lock); list_for_each_entry(cmnd, &conn->pdu_list, conn_list) { set_cmnd_tmfabort(cmnd); if (cmnd->lun) { @@ -226,7 +226,7 @@ iscsi_cmnd_set_sense(cmnd, UNIT_ATTENTION, 0x6e, 0x0); } } - spin_unlock(&conn->list_lock); + spin_unlock_bh(&conn->list_lock); nthread_wakeup(conn->session->target); } Modified: trunk/kernel/iscsi.c =================================================================== --- trunk/kernel/iscsi.c 2014-01-26 18:30:45 UTC (rev 499) +++ trunk/kernel/iscsi.c 2014-05-06 20:58:55 UTC (rev 500) @@ -159,11 +159,11 @@ INIT_LIST_HEAD(&cmnd->conn_list); INIT_LIST_HEAD(&cmnd->hash_list); cmnd->conn = conn; - spin_lock(&conn->list_lock); + spin_lock_bh(&conn->list_lock); atomic_inc(&conn->nr_cmnds); if (req) list_add_tail(&cmnd->conn_list, &conn->pdu_list); - spin_unlock(&conn->list_lock); + spin_unlock_bh(&conn->list_lock); cmnd->tio = NULL; dprintk(D_GENERIC, "%p:%p\n", conn, cmnd); @@ -202,7 +202,7 @@ struct iscsi_conn *conn = cmnd->conn; struct list_head *pos, *next; - spin_lock(&conn->list_lock); + spin_lock_bh(&conn->list_lock); list_for_each_safe(pos, next, send) { cmnd = list_entry(pos, struct iscsi_cmnd, list); @@ -214,7 +214,7 @@ list_add_tail(&cmnd->list, &conn->write_list); } - spin_unlock(&conn->list_lock); + spin_unlock_bh(&conn->list_lock); nthread_wakeup(conn->session->target); } @@ -448,10 +448,10 @@ BUG(); } list_del(&cmnd->list); - spin_lock(&conn->list_lock); + spin_lock_bh(&conn->list_lock); atomic_dec(&conn->nr_cmnds); list_del(&cmnd->conn_list); - spin_unlock(&conn->list_lock); + spin_unlock_bh(&conn->list_lock); if (cmnd->tio) tio_put(cmnd->tio); Modified: trunk/kernel/nthread.c =================================================================== --- trunk/kernel/nthread.c 2014-01-26 18:30:45 UTC (rev 499) +++ trunk/kernel/nthread.c 2014-05-06 20:58:55 UTC (rev 500) @@ -58,12 +58,12 @@ { struct iscsi_cmnd *cmnd = NULL; - spin_lock(&conn->list_lock); + spin_lock_bh(&conn->list_lock); if (!list_empty(&conn->write_list)) { cmnd = list_entry(conn->write_list.next, struct iscsi_cmnd, list); list_del_init(&cmnd->list); } - spin_unlock(&conn->list_lock); + spin_unlock_bh(&conn->list_lock); return cmnd; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |