FAQ Search Today's Posts Mark Forums Read
» Video Reviews

» Linux Archive

Linux-archive is a website aiming to archive linux email lists and to make them easily accessible for linux users/developers.


» Sponsor

» Partners

» Sponsor

Go Back   Linux Archive > Ubuntu > Ubuntu Kernel Team

 
 
LinkBack Thread Tools
 
Old 05-26-2010, 04:44 AM
Leann Ogasawara
 
Default UBUNTU: ubuntu: iscsitarget -- version 1.4.20.1

Hi All,

Per our UDS Ubuntu kernel delta review session we identified that
iscsitarget could be updated to a newer version. The following patch
pulls us up to date with the latest stable version of iscsitarget,
v1.4.20.1. We were at v1.4.19.

Thanks,
Leann

>From 73acda31864e7c6d53c477912af255bffc7a90fd Mon Sep 17 00:00:00 2001
From: Leann Ogasawara <leann.ogasawara@canonical.com>
Date: Tue, 25 May 2010 19:31:39 -0700
Subject: [PATCH] UBUNTU: ubuntu: iscsitarget -- version 1.4.20.1

Update iscsitarget to the latest stable version (v1.4.20.1) from
sourceforge.

ExternalDriver: iscsi_trgt
Url: http://sourceforge.net/projects/iscsitarget/files/
Version: 1.4.20.1

Signed-off-by: Leann Ogasawara <leann.ogasawara@canonical.com>
---
ubuntu/iscsitarget/BOM | 2 +-
ubuntu/iscsitarget/block-io.c | 122 +++++----------------
ubuntu/iscsitarget/config.c | 174 ++++++++++++++++++------------
ubuntu/iscsitarget/conn.c | 33 ++++--
ubuntu/iscsitarget/file-io.c | 97 +++--------------
ubuntu/iscsitarget/include/iet_u.h | 38 ++++---
ubuntu/iscsitarget/iotype.h | 13 +++
ubuntu/iscsitarget/iscsi.c | 145 ++++++++++++++++---------
ubuntu/iscsitarget/iscsi.h | 13 ++-
ubuntu/iscsitarget/nthread.c | 6 +-
ubuntu/iscsitarget/null-io.c | 58 ++++-------
ubuntu/iscsitarget/param.c | 2 +-
ubuntu/iscsitarget/session.c | 32 ++----
ubuntu/iscsitarget/target.c | 53 ++++++---
ubuntu/iscsitarget/target_disk.c | 63 +++++++----
ubuntu/iscsitarget/ua.c | 12 ++
ubuntu/iscsitarget/volume.c | 211 +++++++++++++++++++++++++++++++-----
ubuntu/iscsitarget/wthread.c | 27 +++++-
18 files changed, 644 insertions(+), 457 deletions(-)

diff --git a/ubuntu/iscsitarget/BOM b/ubuntu/iscsitarget/BOM
index cda1135..0fde30c 100644
--- a/ubuntu/iscsitarget/BOM
+++ b/ubuntu/iscsitarget/BOM
@@ -1,2 +1,2 @@
Downloaded from: svn://svn.berlios.de/iscsitarget/trunk
-Current Version: 1.4.19
+Current Version: 1.4.20.1
diff --git a/ubuntu/iscsitarget/block-io.c b/ubuntu/iscsitarget/block-io.c
index 708f101..4de1d20 100644
--- a/ubuntu/iscsitarget/block-io.c
+++ b/ubuntu/iscsitarget/block-io.c
@@ -86,7 +86,8 @@ blockio_make_request(struct iet_volume *volume, struct tio *tio, int rw)
goto out;
}

- bio->bi_sector = ppos >> volume->blk_shift;
+ /* bi_sector is ALWAYS in units of 512 bytes */
+ bio->bi_sector = ppos >> 9;
bio->bi_bdev = bio_data->bdev;
bio->bi_end_io = blockio_bio_endio;
bio->bi_private = tio_work;
@@ -174,77 +175,21 @@ blockio_open_path(struct iet_volume *volume, const char *path)
return err;
}

-static int
-set_scsiid(struct iet_volume *volume, const char *id)
-{
- size_t len;
-
- if ((len = strlen(id)) > SCSI_ID_LEN - VENDOR_ID_LEN) {
- eprintk("SCSI ID too long, %zd provided, %u max
", len,
- SCSI_ID_LEN - VENDOR_ID_LEN);
- return -EINVAL;
- }
-
- memcpy(volume->scsi_id + VENDOR_ID_LEN, id, len);
-
- return 0;
-}
-
-static void
-gen_scsiid(struct iet_volume *volume, struct inode *inode)
-{
- int i;
- u32 *p;
-
- strlcpy(volume->scsi_id, VENDOR_ID, VENDOR_ID_LEN);
-
- for (i = VENDOR_ID_LEN; i < SCSI_ID_LEN; i++)
- if (volume->scsi_id[i])
- return;
-
- /* If a scsi id doesn't exist generate a 16 byte one:
- * Bytes 1-4: target type
- * Bytes 5-8: target id
- * Bytes 9-12: inode number
- * Bytes 13-16: device type
- */
- p = (u32 *) (volume->scsi_id + VENDOR_ID_LEN);
- *(p + 0) = volume->target->trgt_param.target_type;
- *(p + 1) = volume->target->tid;
- *(p + 2) = volume->lun;
- *(p + 3) = (unsigned int) inode->i_sb->s_dev;
-}
-
-static int
-set_scsisn(struct iet_volume *volume, const char *sn)
-{
- size_t len;
-
- if ((len = strlen(sn)) > SCSI_SN_LEN) {
- eprintk("SCSI SN too long, %zd provided, %u max
", len,
- SCSI_SN_LEN);
- return -EINVAL;
- }
-
- memcpy(volume->scsi_sn, sn, len);
-
- return 0;
-}
-
/* Create an enumeration of our accepted actions */
enum
{
- Opt_scsiid, Opt_scsisn, Opt_path, Opt_ignore, Opt_err,
+ opt_path, opt_ignore, opt_err,
};

/* 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_err, NULL},
+ {opt_path, "path=%s"},
+ {opt_ignore, "scsiid=%s"},
+ {opt_ignore, "scsisn=%s"},
+ {opt_ignore, "type=%s"},
+ {opt_ignore, "iomode=%s"},
+ {opt_ignore, "blocksize=%s"},
+ {opt_err, NULL},
};

static int
@@ -262,29 +207,10 @@ parse_blockio_params(struct iet_volume *volume, char *params)
int token;
if (!*p)
continue;
+ iet_strtolower(p);
token = match_token(p, tokens, args);
switch (token) {
- case Opt_scsiid:
- if (!(q = match_strdup(&args[0]))) {
- err = -ENOMEM;
- goto out;
- }
- err = set_scsiid(volume, q);
- kfree(q);
- if (err < 0)
- goto out;
- break;
- case Opt_scsisn:
- if (!(q = match_strdup(&args[0]))) {
- err = -ENOMEM;
- goto out;
- }
- err = set_scsisn(volume, q);
- kfree(q);
- if (err < 0)
- goto out;
- break;
- case Opt_path:
+ case opt_path:
if (info->path) {
iprintk("Target %s, LUN %u: "
"duplicate "Path" param
",
@@ -301,7 +227,7 @@ parse_blockio_params(struct iet_volume *volume, char *params)
if (err < 0)
goto out;
break;
- case Opt_ignore:
+ case opt_ignore:
break;
default:
iprintk("Target %s, LUN %u: unknown param %s
",
@@ -315,6 +241,7 @@ parse_blockio_params(struct iet_volume *volume, char *params)
volume->target->name, volume->lun);
err = -EINVAL;
}
+
out:
return err;
}
@@ -350,22 +277,31 @@ blockio_attach(struct iet_volume *volume, char *args)

volume->private = bio_data;

- if ((err = parse_blockio_params(volume, args)) < 0) {
+ err = parse_blockio_params(volume, args);
+ if (!err) {
+ /* see Documentation/ABI/testing/sysfs-block */
+ unsigned bsz = bdev_logical_block_size(bio_data->bdev);
+ if (!volume->blk_shift)
+ volume->blk_shift = ilog2(bsz);
+ else if (volume->blk_shift < ilog2(bsz)) {
+ eprintk("Specified block size (%u) smaller than "
+ "device %s logical block size (%u)
",
+ (1 << volume->blk_shift), bio_data->path, bsz);
+ err = -EINVAL;
+ }
+ }
+ if (err < 0) {
eprintk("Error attaching Lun %u to Target %s
",
volume->lun, volume->target->name);
goto out;
}

- /* Assign a vendor id, generate scsi id if none exists */
- gen_scsiid(volume, bio_data->bdev->bd_inode);
+ volume->blk_cnt = bio_data->bdev->bd_inode->i_size >> volume->blk_shift;

/* Offer neither write nor read caching */
ClearLURCache(volume);
ClearLUWCache(volume);

- volume->blk_shift = SECTOR_SIZE_BITS;
- volume->blk_cnt = bio_data->bdev->bd_inode->i_size >> volume->blk_shift;
-
out:
if (err < 0)
blockio_detach(volume);
diff --git a/ubuntu/iscsitarget/config.c b/ubuntu/iscsitarget/config.c
index 51331fb..87fa44b 100644
--- a/ubuntu/iscsitarget/config.c
+++ b/ubuntu/iscsitarget/config.c
@@ -9,6 +9,8 @@
#include "iscsi.h"
#include "iscsi_dbg.h"

+static DECLARE_MUTEX(ioctl_sem);
+
struct proc_entries {
const char *name;
struct file_operations *fops;
@@ -60,26 +62,45 @@ err:
return -ENOMEM;
}

-static int get_conn_info(struct iscsi_target *target, unsigned long ptr)
+static int get_module_info(unsigned long ptr)
{
+ struct module_info info;
int err;
+
+ snprintf(info.version, sizeof(info.version), "%s", IET_VERSION_STRING);
+
+ err = copy_to_user((void *) ptr, &info, sizeof(info));
+ if (err)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int get_conn_info(struct iscsi_target *target, unsigned long ptr)
+{
struct iscsi_session *session;
- struct conn_info info;
struct iscsi_conn *conn;
+ struct conn_info info;
+ int err;

- if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
- return err;
+ err = copy_from_user(&info, (void *) ptr, sizeof(info));
+ if (err)
+ return -EFAULT;

session = session_lookup(target, info.sid);
if (!session)
return -ENOENT;
+
conn = conn_lookup(session, info.cid);
+ if (!conn)
+ return -ENOENT;

info.cid = conn->cid;
info.stat_sn = conn->stat_sn;
info.exp_stat_sn = conn->exp_stat_sn;

- if (copy_to_user((void *) ptr, &info, sizeof(info)))
+ err = copy_to_user((void *) ptr, &info, sizeof(info));
+ if (err)
return -EFAULT;

return 0;
@@ -87,14 +108,16 @@ static int get_conn_info(struct iscsi_target *target, unsigned long ptr)

static int add_conn(struct iscsi_target *target, unsigned long ptr)
{
- int err;
struct iscsi_session *session;
struct conn_info info;
+ int err;

- if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
- return err;
+ err = copy_from_user(&info, (void *) ptr, sizeof(info));
+ if (err)
+ return -EFAULT;

- if (!(session = session_lookup(target, info.sid)))
+ session = session_lookup(target, info.sid);
+ if (!session)
return -ENOENT;

return conn_add(session, &info);
@@ -102,14 +125,16 @@ static int add_conn(struct iscsi_target *target, unsigned long ptr)

static int del_conn(struct iscsi_target *target, unsigned long ptr)
{
- int err;
struct iscsi_session *session;
struct conn_info info;
+ int err;

- if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
- return err;
+ err = copy_from_user(&info, (void *) ptr, sizeof(info));
+ if (err)
+ return -EFAULT;

- if (!(session = session_lookup(target, info.sid)))
+ session = session_lookup(target, info.sid);
+ if (!session)
return -ENOENT;

return conn_del(session, &info);
@@ -117,22 +142,23 @@ static int del_conn(struct iscsi_target *target, unsigned long ptr)

static int get_session_info(struct iscsi_target *target, unsigned long ptr)
{
- int err;
struct iscsi_session *session;
struct session_info info;
+ int err;

- if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
- return err;
+ err = copy_from_user(&info, (void *) ptr, sizeof(info));
+ if (err)
+ return -EFAULT;

session = session_lookup(target, info.sid);
-
if (!session)
return -ENOENT;

info.exp_cmd_sn = session->exp_cmd_sn;
info.max_cmd_sn = session->max_cmd_sn;

- if (copy_to_user((void *) ptr, &info, sizeof(info)))
+ err = copy_to_user((void *) ptr, &info, sizeof(info));
+ if (err)
return -EFAULT;

return 0;
@@ -140,78 +166,90 @@ static int get_session_info(struct iscsi_target *target, unsigned long ptr)

static int add_session(struct iscsi_target *target, unsigned long ptr)
{
- int err;
struct session_info info;
+ int err;

- if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
- return err;
+ err = copy_from_user(&info, (void *) ptr, sizeof(info));
+ if (err)
+ return -EFAULT;

return session_add(target, &info);
}

static int del_session(struct iscsi_target *target, unsigned long ptr)
{
- int err;
struct session_info info;
+ int err;

- if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
- return err;
+ err = copy_from_user(&info, (void *) ptr, sizeof(info));
+ if (err)
+ return -EFAULT;

return session_del(target, info.sid);
}

static int add_volume(struct iscsi_target *target, unsigned long ptr)
{
- int err;
struct volume_info info;
+ int err;

- if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
- return err;
+ err = copy_from_user(&info, (void *) ptr, sizeof(info));
+ if (err)
+ return -EFAULT;

return volume_add(target, &info);
}

static int del_volume(struct iscsi_target *target, unsigned long ptr)
{
- int err;
struct volume_info info;
+ int err;

- if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
- return err;
+ err = copy_from_user(&info, (void *) ptr, sizeof(info));
+ if (err)
+ return -EFAULT;

return iscsi_volume_del(target, &info);
}

static int iscsi_param_config(struct iscsi_target *target, unsigned long ptr, int set)
{
- int err;
struct iscsi_param_info info;
+ int err;

- if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
- goto out;
+ err = copy_from_user(&info, (void *) ptr, sizeof(info));
+ if (err)
+ return -EFAULT;

- if ((err = iscsi_param_set(target, &info, set)) < 0)
- goto out;
+ err = iscsi_param_set(target, &info, set);
+ if (err < 0 || set)
+ return err;

- if (!set)
- err = copy_to_user((void *) ptr, &info, sizeof(info));
+ err = copy_to_user((void *) ptr, &info, sizeof(info));
+ if (err)
+ return -EFAULT;

-out:
- return err;
+ return 0;
}

static int add_target(unsigned long ptr)
{
- int err;
struct target_info info;
+ int err;

- if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
+ err = copy_from_user(&info, (void *) ptr, sizeof(info));
+ if (err)
+ return -EFAULT;
+
+ err = target_add(&info);
+ if (err < 0)
return err;

- if (!(err = target_add(&info)))
- err = copy_to_user((void *) ptr, &info, sizeof(info));
+ err = copy_to_user((void *) ptr, &info, sizeof(info));
+ if (err)
+ return -EFAULT;

- return err;
+ return 0;
}

static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -220,40 +258,39 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
long err;
u32 id;

- if ((err = get_user(id, (u32 *) arg)) != 0)
- goto done;
+ err = down_interruptible(&ioctl_sem);
+ if (err < 0)
+ return err;

- if (cmd == DEL_TARGET) {
- err = target_del(id);
+ if (cmd == GET_MODULE_INFO) {
+ err = get_module_info(arg);
goto done;
}

- target = target_lookup_by_id(id);
+ if (cmd == ADD_TARGET) {
+ err = add_target(arg);
+ goto done;
+ }

- if (cmd == ADD_TARGET)
- if (target) {
- err = -EEXIST;
- eprintk("Target %u already exist!
", id);
- goto done;
- }
+ err = get_user(id, (u32 *) arg);
+ if (err < 0)
+ goto done;

- switch (cmd) {
- case ADD_TARGET:
- assert(!target);
- err = add_target(arg);
+ /* locking handled in target_del */
+ if (cmd == DEL_TARGET) {
+ err = target_del(id);
goto done;
}

+ target = target_lookup_by_id(id);
if (!target) {
- eprintk("can't find the target %u
", id);
- err = -EINVAL;
+ err = -ENOENT;
goto done;
}

- if ((err = target_lock(target, 1)) < 0) {
- eprintk("interrupted %ld %d
", err, cmd);
+ err = target_lock(target, 1);
+ if (err < 0)
goto done;
- }

switch (cmd) {
case ADD_VOLUME:
@@ -300,17 +337,20 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
err = -EINVAL;
}

- if (target)
- target_unlock(target);
-
+ target_unlock(target);
done:
+ up(&ioctl_sem);
+
return err;
}

static int release(struct inode *i __attribute__((unused)),
struct file *f __attribute__((unused)))
{
+ down(&ioctl_sem);
target_del_all();
+ up(&ioctl_sem);
+
return 0;
}

diff --git a/ubuntu/iscsitarget/conn.c b/ubuntu/iscsitarget/conn.c
index 5f91e3d..ec6dada 100644
--- a/ubuntu/iscsitarget/conn.c
+++ b/ubuntu/iscsitarget/conn.c
@@ -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,22 +212,39 @@ static int iet_conn_alloc(struct iscsi_session *session, struct conn_info *info)

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);
+ 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);
+
nthread_wakeup(conn->session->target);
}

int conn_add(struct iscsi_session *session, struct conn_info *info)
{
struct iscsi_conn *conn;
- int err = -EEXIST;
+ int err;

conn = conn_lookup(session, info->cid);
if (conn)
- return err;
+ conn_close(conn);

- return iet_conn_alloc(session, info);
+ err = iet_conn_alloc(session, info);
+ if (!err && conn)
+ err = -EEXIST;
+
+ return err;
}

int conn_del(struct iscsi_session *session, struct conn_info *info)
@@ -242,12 +260,3 @@ int conn_del(struct iscsi_session *session, struct conn_info *info)

return 0;
}
-
-/* target_lock() supposed to be held */
-void conn_del_all(struct iscsi_session *session)
-{
- struct iscsi_conn *conn;
-
- list_for_each_entry(conn, &session->conn_list, list)
- conn_close(conn);
-}
diff --git a/ubuntu/iscsitarget/file-io.c b/ubuntu/iscsitarget/file-io.c
index a492ce4..38951a9 100644
--- a/ubuntu/iscsitarget/file-io.c
+++ b/ubuntu/iscsitarget/file-io.c
@@ -53,9 +53,9 @@ static int fileio_make_request(struct iet_volume *lu, struct tio *tio, int rw)
set_fs(get_ds());

if (rw == READ)
- ret = do_sync_read(filp, buf, count, &ppos);
+ ret = vfs_read(filp, buf, count, &ppos);
else
- ret = do_sync_write(filp, buf, count, &ppos);
+ ret = vfs_write(filp, buf, count, &ppos);

set_fs(oldfs);

@@ -82,6 +82,7 @@ static int fileio_sync(struct iet_volume *lu, struct tio *tio)

if (tio) {
ppos = (loff_t) tio->idx << PAGE_CACHE_SHIFT;
+ ppos += tio->offset;
count = tio->size;
} else {
ppos = 0;
@@ -124,63 +125,18 @@ static int open_path(struct iet_volume *volume, const char *path)
return err;
}

-static int set_scsiid(struct iet_volume *volume, const char *id)
-{
- size_t len;
-
- if ((len = strlen(id)) > SCSI_ID_LEN - VENDOR_ID_LEN) {
- eprintk("SCSI ID too long, %zd provided, %u max
", len,
- SCSI_ID_LEN - VENDOR_ID_LEN);
- return -EINVAL;
- }
-
- memcpy(volume->scsi_id + VENDOR_ID_LEN, id, len);
-
- return 0;
-}
-
-static void gen_scsiid(struct iet_volume *volume, struct inode *inode)
-{
- int i;
- u32 *p;
-
- strlcpy(volume->scsi_id, VENDOR_ID, VENDOR_ID_LEN);
-
- for (i = VENDOR_ID_LEN; i < SCSI_ID_LEN; i++)
- if (volume->scsi_id[i])
- return;
-
- p = (u32 *) (volume->scsi_id + VENDOR_ID_LEN);
- *(p + 0) = volume->target->trgt_param.target_type;
- *(p + 1) = volume->target->tid;
- *(p + 2) = (unsigned int) inode->i_ino;
- *(p + 3) = (unsigned int) inode->i_sb->s_dev;
-}
-
-static int set_scsisn(struct iet_volume *volume, const char *sn)
-{
- size_t len;
-
- if ((len = strlen(sn)) > SCSI_SN_LEN) {
- eprintk("SCSI SN too long, %zd provided, %u max
", len,
- SCSI_SN_LEN);
- return -EINVAL;
- }
- memcpy(volume->scsi_sn, sn, len);
- return 0;
-}
-
enum {
- Opt_scsiid, Opt_scsisn, Opt_path, Opt_ignore, Opt_err,
+ opt_path, opt_ignore, opt_err,
};

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_err, NULL},
+ {opt_path, "path=%s"},
+ {opt_ignore, "scsiid=%s"},
+ {opt_ignore, "scsisn=%s"},
+ {opt_ignore, "type=%s"},
+ {opt_ignore, "iomode=%s"},
+ {opt_ignore, "blocksize=%s"},
+ {opt_err, NULL},
};

static int parse_fileio_params(struct iet_volume *volume, char *params)
@@ -194,29 +150,10 @@ static int parse_fileio_params(struct iet_volume *volume, char *params)
int token;
if (!*p)
continue;
+ iet_strtolower(p);
token = match_token(p, tokens, args);
switch (token) {
- case Opt_scsiid:
- if (!(q = match_strdup(&args[0]))) {
- err = -ENOMEM;
- goto out;
- }
- err = set_scsiid(volume, q);
- kfree(q);
- if (err < 0)
- goto out;
- break;
- case Opt_scsisn:
- if (!(q = match_strdup(&args[0]))) {
- err = -ENOMEM;
- goto out;
- }
- err = set_scsisn(volume, q);
- kfree(q);
- if (err < 0)
- goto out;
- break;
- case Opt_path:
+ case opt_path:
if (info->path) {
iprintk("Target %s, LUN %u: "
"duplicate "Path" param
",
@@ -233,7 +170,7 @@ static int parse_fileio_params(struct iet_volume *volume, char *params)
if (err < 0)
goto out;
break;
- case Opt_ignore:
+ case opt_ignore:
break;
default:
iprintk("Target %s, LUN %u: unknown param %s
",
@@ -285,8 +222,6 @@ static int fileio_attach(struct iet_volume *lu, char *args)
}
inode = p->filp->f_dentry->d_inode;

- gen_scsiid(lu, inode);
-
if (S_ISREG(inode->i_mode))
;
else if (S_ISBLK(inode->i_mode))
@@ -296,7 +231,9 @@ static int fileio_attach(struct iet_volume *lu, char *args)
goto out;
}

- lu->blk_shift = SECTOR_SIZE_BITS;
+ if (!lu->blk_shift)
+ lu->blk_shift = ilog2(IET_DEF_BLOCK_SIZE);
+
lu->blk_cnt = inode->i_size >> lu->blk_shift;

/* we're using the page cache */
diff --git a/ubuntu/iscsitarget/include/iet_u.h b/ubuntu/iscsitarget/include/iet_u.h
index 620b3c4..174f0dc 100644
--- a/ubuntu/iscsitarget/include/iet_u.h
+++ b/ubuntu/iscsitarget/include/iet_u.h
@@ -1,7 +1,7 @@
#ifndef _IET_U_H
#define _IET_U_H

-#define IET_VERSION_STRING "1.4.19"
+#define IET_VERSION_STRING "1.4.20.1"

/* The maximum length of 223 bytes in the RFC. */
#define ISCSI_NAME_LEN 256
@@ -9,14 +9,17 @@

#define ISCSI_LISTEN_PORT 3260

-#define VENDOR_ID_LEN 8
-#define SCSI_ID_LEN 24
-#define SCSI_SN_LEN 16
+#define SCSI_ID_LEN 16
+#define SCSI_SN_LEN (SCSI_ID_LEN * 2)

#ifndef aligned_u64
#define aligned_u64 unsigned long long __attribute__((aligned(8)))
#endif

+struct module_info {
+ char version[128];
+};
+
struct target_info {
u32 tid;
char name[ISCSI_NAME_LEN];
@@ -131,19 +134,18 @@ struct iet_event {

#define NETLINK_IET 21

-#define ADD_TARGET _IOW('i', 0, struct target_info)
-#define DEL_TARGET _IOW('i', 1, struct target_info)
-#define START_TARGET _IO('i', 2)
-#define STOP_TARGET _IO('i', 3)
-#define ADD_VOLUME _IOW('i', 4, struct volume_info)
-#define DEL_VOLUME _IOW('i', 5, struct volume_info)
-#define ADD_SESSION _IOW('i', 6, struct session_info)
-#define DEL_SESSION _IOW('i', 7, struct session_info)
-#define GET_SESSION_INFO _IOWR('i', 8, struct session_info)
-#define ADD_CONN _IOW('i', 9, struct conn_info)
-#define DEL_CONN _IOW('i', 10, struct conn_info)
-#define GET_CONN_INFO _IOWR('i', 11, struct conn_info)
-#define ISCSI_PARAM_SET _IOW('i', 12, struct iscsi_param_info)
-#define ISCSI_PARAM_GET _IOWR('i', 13, struct iscsi_param_info)
+#define GET_MODULE_INFO _IOW('i', 20, struct module_info)
+#define ADD_TARGET _IOWR('i', 21, struct target_info)
+#define DEL_TARGET _IOW('i', 22, struct target_info)
+#define ADD_VOLUME _IOW('i', 24, struct volume_info)
+#define DEL_VOLUME _IOW('i', 25, struct volume_info)
+#define ADD_SESSION _IOW('i', 26, struct session_info)
+#define DEL_SESSION _IOW('i', 27, struct session_info)
+#define GET_SESSION_INFO _IOWR('i', 28, struct session_info)
+#define ADD_CONN _IOW('i', 29, struct conn_info)
+#define DEL_CONN _IOW('i', 30, struct conn_info)
+#define GET_CONN_INFO _IOWR('i', 31, struct conn_info)
+#define ISCSI_PARAM_SET _IOW('i', 32, struct iscsi_param_info)
+#define ISCSI_PARAM_GET _IOWR('i', 33, struct iscsi_param_info)

#endif
diff --git a/ubuntu/iscsitarget/iotype.h b/ubuntu/iscsitarget/iotype.h
index bd7fbd0..db7956a 100644
--- a/ubuntu/iscsitarget/iotype.h
+++ b/ubuntu/iscsitarget/iotype.h
@@ -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 struct iotype blockio;
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
diff --git a/ubuntu/iscsitarget/iscsi.c b/ubuntu/iscsitarget/iscsi.c
index 4f62b8b..05197f2 100644
--- a/ubuntu/iscsitarget/iscsi.c
+++ b/ubuntu/iscsitarget/iscsi.c
@@ -345,6 +345,8 @@ void iscsi_cmnd_set_sense(struct iscsi_cmnd *cmnd, u8 sense_key, u8 asc,
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,
@@ -534,10 +536,20 @@ static int check_cmd_sn(struct iscsi_cmnd *cmnd)
u32 cmd_sn;

cmnd->pdu.bhs.sn = cmd_sn = be32_to_cpu(cmnd->pdu.bhs.sn);
- dprintk(D_GENERIC, "%d(%d)
", cmd_sn, session->exp_cmd_sn);
- if ((s32)(cmd_sn - session->exp_cmd_sn) >= 0)
+
+ dprintk(D_GENERIC, "cmd_sn(%u) exp_cmd_sn(%u) max_cmd_sn(%u)
",
+ cmd_sn, session->exp_cmd_sn, session->max_cmd_sn);
+
+ if (between(cmd_sn, session->exp_cmd_sn, session->max_cmd_sn))
+ return 0;
+ else if (cmnd_immediate(cmnd))
return 0;
- eprintk("sequence error (%x,%x)
", cmd_sn, session->exp_cmd_sn);
+
+ eprintk("sequence error: cmd_sn(%u) exp_cmd_sn(%u) max_cmd_sn(%u)
",
+ cmd_sn, session->exp_cmd_sn, session->max_cmd_sn);
+
+ set_cmnd_tmfabort(cmnd);
+
return -ISCSI_REASON_PROTOCOL_ERROR;
}

@@ -609,7 +621,8 @@ static int cmnd_insert_hash(struct iscsi_cmnd *cmnd)
if (!err) {
update_stat_sn(cmnd);
err = check_cmd_sn(cmnd);
- }
+ } else if (!cmnd_immediate(cmnd))
+ set_cmnd_tmfabort(cmnd);

return err;
}
@@ -826,15 +839,12 @@ static void send_r2t(struct iscsi_cmnd *req)

static void scsi_cmnd_exec(struct iscsi_cmnd *cmnd)
{
- if (cmnd->r2t_length) {
- if (!cmnd->is_unsolicited_data)
- send_r2t(cmnd);
+ assert(!cmnd->r2t_length);
+
+ if (cmnd->lun) {
+ iscsi_scsi_queuecmnd(cmnd);
} else {
- if (cmnd->lun) {
- iscsi_scsi_queuecmnd(cmnd);
- } else {
- iscsi_device_queue_cmnd(cmnd);
- }
+ iscsi_device_queue_cmnd(cmnd);
}
}

@@ -864,7 +874,7 @@ static int nop_out_start(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
}

if (cmnd_itt(cmnd) == cpu_to_be32(ISCSI_RESERVED_TAG)) {
- if (!(cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE))
+ if (!cmnd_immediate(cmnd))
eprintk("%s
", "initiator bug!");
update_stat_sn(cmnd);
err = check_cmd_sn(cmnd);
@@ -1093,6 +1103,8 @@ skip_data:
return;
}

+static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd);
+
static void data_out_end(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
{
struct iscsi_data_out_hdr *req = (struct iscsi_data_out_hdr *) &cmnd->pdu.bhs;
@@ -1116,8 +1128,7 @@ static void data_out_end(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
if (req->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) {
if (req->flags & ISCSI_FLG_FINAL) {
scsi_cmnd->is_unsolicited_data = 0;
- if (!cmnd_pending(scsi_cmnd))
- scsi_cmnd_exec(scsi_cmnd);
+ iscsi_session_push_cmnd(scsi_cmnd);
}
} else {
/* TODO : proper error handling */
@@ -1132,7 +1143,7 @@ static void data_out_end(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
if (scsi_cmnd->r2t_length == 0)
assert(list_empty(&scsi_cmnd->pdu_list));

- scsi_cmnd_exec(scsi_cmnd);
+ iscsi_session_push_cmnd(scsi_cmnd);
}

out:
@@ -1140,35 +1151,64 @@ out:
return;
}

-static int __cmnd_abort(struct iscsi_cmnd *cmnd)
+static void __cmnd_abort(struct iscsi_cmnd *cmnd)
{
+ if (cmnd_rxstart(cmnd))
+ set_cmnd_tmfabort(cmnd);
+
if (cmnd_waitio(cmnd))
- return -ISCSI_RESPONSE_UNKNOWN_TASK;
+ return;

if (cmnd->conn->read_cmnd != cmnd)
cmnd_release(cmnd, 1);
- else if (cmnd_rxstart(cmnd))
- set_cmnd_tmfabort(cmnd);
- else
- return -ISCSI_RESPONSE_UNKNOWN_TASK;
-
- return 0;
}

-static int cmnd_abort(struct iscsi_session *session, u32 itt)
+static int cmnd_abort(struct iscsi_session *session, struct iscsi_cmnd *req)
{
+ struct iscsi_task_mgt_hdr *req_hdr =
+ (struct iscsi_task_mgt_hdr *)&req->pdu.bhs;
struct iscsi_cmnd *cmnd;
- int err = -ISCSI_RESPONSE_UNKNOWN_TASK;
-
- if ((cmnd = cmnd_find_hash(session, itt, ISCSI_RESERVED_TAG))) {
- eprintk("%x %x %x %u %u %u %u
", cmnd_itt(cmnd), cmnd_opcode(cmnd),
- cmnd->r2t_length, cmnd_scsicode(cmnd),
- cmnd_write_size(cmnd), cmnd->is_unsolicited_data,
- cmnd->outstanding_r2t);
- err = __cmnd_abort(cmnd);
+
+ u32 min_cmd_sn = req_hdr->cmd_sn - session->max_queued_cmnds;
+
+ req_hdr->ref_cmd_sn = be32_to_cpu(req_hdr->ref_cmd_sn);
+
+ dprintk(D_GENERIC, "cmd_sn(%u) ref_cmd_sn(%u) min_cmd_sn(%u) rtt(%x)"
+ " lun(%d) cid(%u)
",
+ req_hdr->cmd_sn, req_hdr->ref_cmd_sn, min_cmd_sn, req_hdr->rtt,
+ translate_lun(req_hdr->lun), req->conn->cid);
+
+ if (after(req_hdr->ref_cmd_sn, req_hdr->cmd_sn))
+ return ISCSI_RESPONSE_FUNCTION_REJECTED;
+
+ if (!(cmnd = cmnd_find_hash(session, req_hdr->rtt, ISCSI_RESERVED_TAG))) {
+ if (between(req_hdr->ref_cmd_sn, min_cmd_sn, req_hdr->cmd_sn))
+ return ISCSI_RESPONSE_FUNCTION_COMPLETE;
+ else
+ return ISCSI_RESPONSE_UNKNOWN_TASK;
}

- return err;
+ dprintk(D_GENERIC, "itt(%x) opcode(%x) scsicode(%x) lun(%d) cid(%u)
",
+ cmnd_itt(cmnd), cmnd_opcode(cmnd), cmnd_scsicode(cmnd),
+ translate_lun(cmnd_hdr(cmnd)->lun), cmnd->conn->cid);
+
+ if (cmnd_opcode(cmnd) == ISCSI_OP_SCSI_TASK_MGT_MSG)
+ return ISCSI_RESPONSE_FUNCTION_REJECTED;
+
+ if (translate_lun(cmnd_hdr(cmnd)->lun) !=
+ translate_lun(req_hdr->lun))
+ return ISCSI_RESPONSE_FUNCTION_REJECTED;
+
+ if (cmnd->conn && test_bit(CONN_ACTIVE, &cmnd->conn->state)) {
+ if (cmnd->conn->cid != req->conn->cid)
+ return ISCSI_RESPONSE_FUNCTION_REJECTED;
+ } else {
+ /* Switch cmnd connection allegiance */
+ }
+
+ __cmnd_abort(cmnd);
+
+ return ISCSI_RESPONSE_FUNCTION_COMPLETE;
}

static int target_reset(struct iscsi_cmnd *req, u32 lun, int all)
@@ -1187,7 +1227,8 @@ static int target_reset(struct iscsi_cmnd *req, u32 lun, int all)

if (all)
__cmnd_abort(cmnd);
- else if (translate_lun(cmnd_hdr(cmnd)->lun) == lun)
+ else if (translate_lun(cmnd_hdr(cmnd)->lun)
+ == lun)
__cmnd_abort(cmnd);
}
}
@@ -1214,7 +1255,12 @@ static void task_set_abort(struct iscsi_cmnd *req)

list_for_each_entry(conn, &session->conn_list, list) {
list_for_each_entry_safe(cmnd, tmp, &conn->pdu_list, conn_list) {
- if (cmnd != req)
+ if (translate_lun(cmnd_hdr(cmnd)->lun)
+ != translate_lun(cmnd_hdr(req)->lun))
+ continue;
+
+ if (before(cmnd_hdr(cmnd)->cmd_sn,
+ cmnd_hdr(req)->cmd_sn))
__cmnd_abort(cmnd);
}
}
@@ -1274,7 +1320,7 @@ static void execute_task_management(struct iscsi_cmnd *req)
struct iscsi_task_mgt_hdr *req_hdr = (struct iscsi_task_mgt_hdr *)&req->pdu.bhs;
struct iscsi_task_rsp_hdr *rsp_hdr;
u32 lun;
- int err, function = req_hdr->function & ISCSI_FUNCTION_MASK;
+ int function = req_hdr->function & ISCSI_FUNCTION_MASK;

rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
rsp_hdr = (struct iscsi_task_rsp_hdr *)&rsp->pdu.bhs;
@@ -1299,8 +1345,7 @@ static void execute_task_management(struct iscsi_cmnd *req)

switch (function) {
case ISCSI_FUNCTION_ABORT_TASK:
- if ((err = cmnd_abort(conn->session, req_hdr->rtt)) < 0)
- rsp_hdr->response = -err;
+ rsp_hdr->response = cmnd_abort(conn->session, req);
break;
case ISCSI_FUNCTION_ABORT_TASK_SET:
task_set_abort(req);
@@ -1443,7 +1488,7 @@ static void nop_in_tx_end(struct iscsi_cmnd *cmnd)
conn->session->target->trgt_param.nop_timeout = t;
}

- dprintk(D_GENERIC, "NOP-In %p, %x: timer %p
", cmnd, cmnd_ttt(cmnd),
+ dprintk(D_GENERIC, "NOP-In %p, %x: timer %p
", cmnd, cmnd_ttt(cmnd),
&cmnd->req->timer);

set_cmnd_timer_active(cmnd->req);
@@ -1700,10 +1745,16 @@ static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd)
struct list_head *entry;
u32 cmd_sn;

+ if (cmnd->r2t_length) {
+ if (!cmnd->is_unsolicited_data)
+ send_r2t(cmnd);
+ return;
+ }
+
dprintk(D_GENERIC, "%p:%x %u,%u
",
cmnd, cmnd_opcode(cmnd), cmnd->pdu.bhs.sn, session->exp_cmd_sn);

- if (cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE) {
+ if (cmnd_immediate(cmnd)) {
iscsi_cmnd_exec(cmnd);
return;
}
@@ -1716,24 +1767,16 @@ static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd)

if (list_empty(&session->pending_list))
break;
+
cmnd = list_entry(session->pending_list.next, struct iscsi_cmnd, list);
if (cmnd->pdu.bhs.sn != cmd_sn)
break;
-/* eprintk("find out-of-order %x %u %u
", */
-/* cmnd_itt(cmnd), cmd_sn, cmnd->pdu.bhs.sn); */
+
list_del_init(&cmnd->list);
clear_cmnd_pending(cmnd);
}
} else {
-/* eprintk("out-of-order %x %u %u
", */
-/* cmnd_itt(cmnd), cmd_sn, session->exp_cmd_sn); */
-
set_cmnd_pending(cmnd);
- if (before(cmd_sn, session->exp_cmd_sn)) /* close the conn */
- eprintk("unexpected cmd_sn (%u,%u)
", cmd_sn, session->exp_cmd_sn);
-
- if (after(cmd_sn, session->exp_cmd_sn + session->max_queued_cmnds))
- eprintk("too large cmd_sn (%u,%u)
", cmd_sn, session->exp_cmd_sn);

list_for_each(entry, &session->pending_list) {
struct iscsi_cmnd *tmp = list_entry(entry, struct iscsi_cmnd, list);
diff --git a/ubuntu/iscsitarget/iscsi.h b/ubuntu/iscsitarget/iscsi.h
index 92ce252..e9d37a4 100644
--- a/ubuntu/iscsitarget/iscsi.h
+++ b/ubuntu/iscsitarget/iscsi.h
@@ -8,6 +8,7 @@
#ifndef __ISCSI_H__
#define __ISCSI_H__

+#include <linux/blkdev.h>
#include <linux/completion.h>
#include <linux/pagemap.h>
#include <linux/seq_file.h>
@@ -92,6 +93,8 @@ struct worker_thread_info {
struct list_head work_queue;

wait_queue_head_t wthread_sleep;
+
+ struct io_context *wthread_ioc;
};

struct iscsi_cmnd;
@@ -127,7 +130,6 @@ struct iscsi_target {
struct worker_thread_info * wthread_info;

struct semaphore target_sem;
- struct completion *done;
};

struct iscsi_queue {
@@ -149,7 +151,7 @@ struct iet_volume {
struct iscsi_queue queue;

u8 scsi_id[SCSI_ID_LEN];
- u8 scsi_sn[SCSI_SN_LEN];
+ u8 scsi_sn[SCSI_SN_LEN + 1];

u32 blk_shift;
u64 blk_cnt;
@@ -336,7 +338,6 @@ extern void send_nop_in(struct iscsi_conn *);
extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16);
extern int conn_add(struct iscsi_session *, struct conn_info *);
extern int conn_del(struct iscsi_session *, struct conn_info *);
-extern void conn_del_all(struct iscsi_session *);
extern int conn_free(struct iscsi_conn *);
extern void conn_close(struct iscsi_conn *);
extern void conn_info_show(struct seq_file *, struct iscsi_session *);
@@ -377,7 +378,6 @@ extern struct file_operations session_seq_fops;
extern struct iscsi_session *session_lookup(struct iscsi_target *, u64);
extern int session_add(struct iscsi_target *, struct session_info *);
extern int session_del(struct iscsi_target *, u64);
-extern void session_del_all(struct iscsi_target *);

/* volume.c */
extern struct file_operations volume_seq_fops;
@@ -464,8 +464,11 @@ static inline void iscsi_cmnd_set_length(struct iscsi_pdu *pdu)
#define cmnd_itt(cmnd) cpu_to_be32((cmnd)->pdu.bhs.itt)
#define cmnd_opcode(cmnd) ((cmnd)->pdu.bhs.opcode & ISCSI_OPCODE_MASK)
#define cmnd_scsicode(cmnd) cmnd_hdr(cmnd)->scb[0]
+#define cmnd_immediate(cmnd) ((cmnd)->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE)

-#define SECTOR_SIZE_BITS 9
+/* default and maximum scsi level block sizes */
+#define IET_DEF_BLOCK_SIZE 512
+#define IET_MAX_BLOCK_SIZE 4096

enum cmnd_flags {
CMND_hashed,
diff --git a/ubuntu/iscsitarget/nthread.c b/ubuntu/iscsitarget/nthread.c
index ec54ead..52cd69a 100644
--- a/ubuntu/iscsitarget/nthread.c
+++ b/ubuntu/iscsitarget/nthread.c
@@ -209,6 +209,7 @@ static int recv(struct iscsi_conn *conn)
hdigest = conn->hdigest_type & DIGEST_NONE ? 0 : 1;
ddigest = conn->ddigest_type & DIGEST_NONE ? 0 : 1;

+next_state:
switch (conn->read_state) {
case RX_INIT_BHS:
assert(!cmnd);
@@ -270,7 +271,7 @@ static int recv(struct iscsi_conn *conn)
return res;

if (conn->read_state != RX_END)
- return res;
+ goto next_state;

if (conn->read_size) {
eprintk("%d %x %d
", res, cmnd_opcode(cmnd), conn->read_size);
@@ -525,6 +526,7 @@ static int send(struct iscsi_conn *conn)

ddigest = conn->ddigest_type != DIGEST_NONE ? 1 : 0;

+next_state:
switch (conn->write_state) {
case TX_INIT:
assert(!cmnd);
@@ -555,7 +557,7 @@ static int send(struct iscsi_conn *conn)
return res;

if (conn->write_state != TX_END)
- return res;
+ goto next_state;

if (conn->write_size) {
eprintk("%d %x %u
", res, cmnd_opcode(cmnd), conn->write_size);
diff --git a/ubuntu/iscsitarget/null-io.c b/ubuntu/iscsitarget/null-io.c
index cfa5899..f64b18c 100644
--- a/ubuntu/iscsitarget/null-io.c
+++ b/ubuntu/iscsitarget/null-io.c
@@ -16,41 +16,44 @@
#include "iscsi_dbg.h"
#include "iotype.h"

-struct nullio_data {
- u64 sectors;
-};
-
enum {
- Opt_sectors, Opt_ignore, Opt_err,
+ opt_blk_cnt, opt_ignore, opt_err,
};

static match_table_t tokens = {
- {Opt_sectors, "Sectors=%u"},
- {Opt_ignore, "Type=%s"},
- {Opt_err, NULL},
+ /* alias for compatibility with existing setups and documentation */
+ {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, "scsiid=%s"},
+ {opt_ignore, "scsisn=%s"},
+ {opt_ignore, "blocksize=%s"},
+ {opt_ignore, "type=%s"},
+ {opt_err, NULL},
};

static int parse_nullio_params(struct iet_volume *volume, char *params)
{
int err = 0;
char *p, *q;
- struct nullio_data *data = volume->private;

while ((p = strsep(&params, ",")) != NULL) {
substring_t args[MAX_OPT_ARGS];
int token;
if (!*p)
continue;
+ iet_strtolower(p);
token = match_token(p, tokens, args);
switch (token) {
- case Opt_sectors:
+ case opt_blk_cnt:
q = match_strdup(&args[0]);
if (!q)
return -ENOMEM;
- data->sectors = simple_strtoull(q, NULL, 10);
+ volume->blk_cnt = simple_strtoull(q, NULL, 10);
kfree(q);
break;
- case Opt_ignore:
+ case opt_ignore:
break;
default:
eprintk("Unknown %s
", p);
@@ -63,35 +66,23 @@ static int parse_nullio_params(struct iet_volume *volume, char *params)

static void nullio_detach(struct iet_volume *lu)
{
- struct nullio_data *p = lu->private;
-
- kfree(p);
- lu->private = NULL;
}

static int nullio_attach(struct iet_volume *lu, char *args)
{
int err = 0;
- struct nullio_data *p;
-
- if (lu->private) {
- printk("already attached ? %d
", lu->lun);
- return -EBUSY;
- }
-
- p = kzalloc(sizeof(*p), GFP_KERNEL);
- if (!p)
- return -ENOMEM;
-
- lu->private = p;

if ((err = parse_nullio_params(lu, args)) < 0) {
eprintk("%d
", err);
goto out;
}

- lu->blk_shift = SECTOR_SIZE_BITS;
- lu->blk_cnt = (p->sectors = p->sectors ? : 1 << 27); /* 64 GB */
+ if (!lu->blk_shift)
+ lu->blk_shift = ilog2(IET_DEF_BLOCK_SIZE);
+
+ /* defaults to 64 GiB */
+ if (!lu->blk_cnt)
+ lu->blk_cnt = 1 << (36 - lu->blk_shift);

out:
if (err < 0)
@@ -99,16 +90,9 @@ out:
return err;
}

-void nullio_show(struct iet_volume *lu, struct seq_file *seq)
-{
- struct nullio_data *p = lu->private;
- seq_printf(seq, " sectors:%llu
", p->sectors);
-}
-
struct iotype nullio =
{
.name = "nullio",
.attach = nullio_attach,
.detach = nullio_detach,
- .show = nullio_show,
};
diff --git a/ubuntu/iscsitarget/param.c b/ubuntu/iscsitarget/param.c
index 57ad301..482d00c 100644
--- a/ubuntu/iscsitarget/param.c
+++ b/ubuntu/iscsitarget/param.c
@@ -43,7 +43,7 @@ static void sess_param_check(struct iscsi_param_info *info)
{
u32 *iparam = info->session_param;

- CHECK_PARAM(info, iparam, max_connections, 1, 1);
+ CHECK_PARAM(info, iparam, max_connections, 1, 65535);
CHECK_PARAM(info, iparam, max_recv_data_length, 512,
(u32) ((ISCSI_CONN_IOV_MAX - 1) * PAGE_CACHE_SIZE));
CHECK_PARAM(info, iparam, max_xmit_data_length, 512,
diff --git a/ubuntu/iscsitarget/session.c b/ubuntu/iscsitarget/session.c
index 6365373..a566d8b 100644
--- a/ubuntu/iscsitarget/session.c
+++ b/ubuntu/iscsitarget/session.c
@@ -124,40 +124,30 @@ int session_add(struct iscsi_target *target, struct session_info *info)
int session_del(struct iscsi_target *target, u64 sid)
{
struct iscsi_session *session;
+ struct iet_volume *volume;

session = session_lookup(target, sid);
if (!session)
return -ENOENT;

if (!list_empty(&session->conn_list)) {
- eprintk("%llu still have connections
", (unsigned long long) session->sid);
- return -EBUSY;
- }
+ DECLARE_COMPLETION_ONSTACK(done);
+ struct iscsi_conn *conn;

- return session_free(session);
-}
-
-void session_del_all(struct iscsi_target *target)
-{
- DECLARE_COMPLETION_ONSTACK(done);
- struct iscsi_session *sess;
+ session->done = &done;
+ list_for_each_entry(conn, &session->conn_list, list)
+ conn_close(conn);

- while (!list_empty(&target->session_list)) {
- init_completion(&done);
- target_lock(target, 0);
- sess = list_entry(target->session_list.next, struct
- iscsi_session, list);
- sess->done = &done;
- conn_del_all(sess);
target_unlock(target);
wait_for_completion(&done);
target_lock(target, 0);
- session_free(sess);
- target_unlock(target);
}

- if (target->done)
- complete(target->done);
+ list_for_each_entry(volume, &target->volumes, list){
+ volume_release(volume, sid, 0);
+ }
+
+ return session_free(session);
}

static void iet_session_info_show(struct seq_file *seq, struct iscsi_target *target)
diff --git a/ubuntu/iscsitarget/target.c b/ubuntu/iscsitarget/target.c
index 15c0715..43326dc 100644
--- a/ubuntu/iscsitarget/target.c
+++ b/ubuntu/iscsitarget/target.c
@@ -192,21 +192,23 @@ out:

int target_add(struct target_info *info)
{
- int err = -EEXIST;
u32 tid = info->tid;
+ int err;

- down(&target_list_sem);
+ err = down_interruptible(&target_list_sem);
+ if (err < 0)
+ return err;

if (nr_targets > MAX_NR_TARGETS) {
err = -EBUSY;
goto out;
}

- if (__target_lookup_by_name(info->name))
- goto out;
-
- if (tid && __target_lookup_by_id(tid))
+ if (__target_lookup_by_name(info->name) ||
+ (tid && __target_lookup_by_id(tid))) {
+ err = -EEXIST;
goto out;
+ }

if (!tid) {
do {
@@ -217,7 +219,8 @@ int target_add(struct target_info *info)
tid = next_target_id;
}

- if (!(err = iscsi_target_create(info, tid)))
+ err = iscsi_target_create(info, tid);
+ if (!err)
nr_targets++;
out:
up(&target_list_sem);
@@ -246,13 +249,24 @@ static void target_destroy(struct iscsi_target *target)
}

/* @locking: target_list_sem must be locked */
-int __target_del(struct iscsi_target *target)
+static int __target_del(struct iscsi_target *target)
{
+ int err;
+
target_lock(target, 0);

if (!list_empty(&target->session_list)) {
- target_unlock(target);
- return -EBUSY;
+ struct iscsi_session *session;
+
+ do {
+ session = list_entry(target->session_list.next,
+ struct iscsi_session, list);
+ err = session_del(target, session->sid);
+ if (err < 0) {
+ target_unlock(target);
+ return err;
+ }
+ } while (!list_empty(&target->session_list));
}

list_del(&target->t_list);
@@ -260,13 +274,16 @@ int __target_del(struct iscsi_target *target)

target_unlock(target);
target_destroy(target);
+
return 0;
}

int target_del(u32 id)
{
struct iscsi_target *target;
- int err = down_interruptible(&target_list_sem);
+ int err;
+
+ err = down_interruptible(&target_list_sem);
if (err < 0)
return err;

@@ -277,15 +294,16 @@ int target_del(u32 id)
}

err = __target_del(target);
- out:
+out:
up(&target_list_sem);
+
return err;
}

void target_del_all(void)
{
- DECLARE_COMPLETION_ONSTACK(done);
struct iscsi_target *target, *tmp;
+ int err;

down(&target_list_sem);

@@ -293,11 +311,10 @@ void target_del_all(void)
iprintk("Removing all connections, sessions and targets
");

list_for_each_entry_safe(target, tmp, &target_list, t_list) {
- init_completion(&done);
- target->done = &done;
- session_del_all(target);
- wait_for_completion(&done);
- __target_del(target);
+ u32 tid = target->tid;
+ err =__target_del(target);
+ if (err)
+ eprintk("Error deleteing target %u: %d
", tid, err);
}

next_target_id = 0;
diff --git a/ubuntu/iscsitarget/target_disk.c b/ubuntu/iscsitarget/target_disk.c
index 694edb2..1f7693c 100644
--- a/ubuntu/iscsitarget/target_disk.c
+++ b/ubuntu/iscsitarget/target_disk.c
@@ -193,11 +193,11 @@ static void build_inquiry_response(struct iscsi_cmnd *cmnd)
data[7] = 0x02;
memset(data + 8, 0x20, 28);
memcpy(data + 8,
- VENDOR_ID, min_t(size_t, strlen(VENDOR_ID), 8));
+ VENDOR_ID, min_t(size_t, strlen(VENDOR_ID), 8));
memcpy(data + 16,
- PRODUCT_ID, min_t(size_t, strlen(PRODUCT_ID), 16));
+ PRODUCT_ID, min_t(size_t, strlen(PRODUCT_ID), 16));
memcpy(data + 32,
- PRODUCT_REV, min_t(size_t, strlen(PRODUCT_REV), 4));
+ PRODUCT_REV, min_t(size_t, strlen(PRODUCT_REV), 4));
data[58] = 0x03;
data[59] = 0x20;
data[60] = 0x09;
@@ -217,35 +217,41 @@ static void build_inquiry_response(struct iscsi_cmnd *cmnd)
tio_set(tio, 7, 0);
err = 0;
} else if (scb[2] == 0x80) {
- int len = (cmnd->lun && strlen(cmnd->lun->scsi_sn)) ?
- SCSI_SN_LEN : 4;
+ u32 len = 4;
+
+ if (cmnd->lun) {
+ if (strlen(cmnd->lun->scsi_sn) <= 16)
+ len = 16;
+ else
+ len = SCSI_SN_LEN;
+ }

data[1] = 0x80;
data[3] = len;
memset(data + 4, 0x20, len);
+ if (cmnd->lun) {
+ size_t offset = len -
+ strlen(cmnd->lun->scsi_sn);
+ memcpy(data + 4 + offset, cmnd->lun->scsi_sn,
+ strlen(cmnd->lun->scsi_sn));
+ }
tio_set(tio, len + 4, 0);
err = 0;
-
- if (len == SCSI_SN_LEN) {
- char *p, *q;
-
- p = data + 4 + len - 1;
- q = cmnd->lun->scsi_sn + len - 1;
-
- for (; len > 0; len--, q--)
- if (isascii(*q) && isprint(*q))
- *(p--) = *q;
- }
} else if (scb[2] == 0x83) {
- u32 len = SCSI_ID_LEN * sizeof(u8);
+ u32 len = SCSI_ID_LEN + 8;

data[1] = 0x83;
data[3] = len + 4;
data[4] = 0x1;
data[5] = 0x1;
data[7] = len;
- if (cmnd->lun) /* We need this ? */
- memcpy(data + 8, cmnd->lun->scsi_id, len);
+ if (cmnd->lun) { /* We need this ? */
+ memset(data + 8, 0x00, 8);
+ memcpy(data + 8, VENDOR_ID,
+ min_t(size_t, strlen(VENDOR_ID), 8));
+ memcpy(data + 16, cmnd->lun->scsi_id,
+ SCSI_ID_LEN);
+ }
tio_set(tio, len + 8, 0);
err = 0;
}
@@ -432,9 +438,19 @@ static void build_reserve_response(struct iscsi_cmnd *cmnd)

static void build_release_response(struct iscsi_cmnd *cmnd)
{
- if (volume_release(cmnd->lun,
- cmnd->conn->session->sid, 0))
+ int ret = volume_release(cmnd->lun,
+ cmnd->conn->session->sid, 0);
+ switch (ret) {
+ case -ENOENT:
+ /* Logical Unit Not Supported (?) */
+ iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x25, 0x0);
+ break;
+ case -EBUSY:
cmnd->status = SAM_STAT_RESERVATION_CONFLICT;
+ break;
+ default:
+ break;
+ }
}

static void build_reservation_conflict_response(struct iscsi_cmnd *cmnd)
@@ -475,8 +491,9 @@ static int disk_check_reservation(struct iscsi_cmnd *cmnd)
{
struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);

- if (is_volume_reserved(cmnd->lun,
- cmnd->conn->session->sid)) {
+ int ret = is_volume_reserved(cmnd->lun,
+ cmnd->conn->session->sid);
+ if (ret == -EBUSY) {
switch (req->scb[0]) {
case INQUIRY:
case RELEASE:
diff --git a/ubuntu/iscsitarget/ua.c b/ubuntu/iscsitarget/ua.c
index db08169..c4ba4c2 100644
--- a/ubuntu/iscsitarget/ua.c
+++ b/ubuntu/iscsitarget/ua.c
@@ -109,6 +109,7 @@ void ua_establish_for_session(struct iscsi_session *sess, u32 lun,
{
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");
@@ -119,8 +120,19 @@ void ua_establish_for_session(struct iscsi_session *sess, u32 lun,
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 */
+ 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);

diff --git a/ubuntu/iscsitarget/volume.c b/ubuntu/iscsitarget/volume.c
index ca0d9ca..6abe16b 100644
--- a/ubuntu/iscsitarget/volume.c
+++ b/ubuntu/iscsitarget/volume.c
@@ -6,6 +6,7 @@

#include <linux/types.h>
#include <linux/parser.h>
+#include <linux/log2.h>

#include "iscsi.h"
#include "iscsi_dbg.h"
@@ -23,25 +24,108 @@ struct iet_volume *volume_lookup(struct iscsi_target *target, u32 lun)
}

enum {
- Opt_type,
- Opt_iomode,
- Opt_err,
+ opt_type,
+ opt_iomode,
+ opt_scsiid,
+ opt_scsisn,
+ opt_blk_size,
+ opt_err,
};

static match_table_t tokens = {
- {Opt_type, "Type=%s"},
- {Opt_iomode, "IOMode=%s"},
- {Opt_err, NULL},
+ {opt_type, "type=%s"},
+ {opt_iomode, "iomode=%s"},
+ {opt_scsiid, "scsiid=%s"},
+ {opt_scsisn, "scsisn=%s"},
+ {opt_blk_size, "blocksize=%u"},
+ {opt_err, NULL},
};

-static int set_iotype(struct iet_volume *volume, char *params)
+static int set_scsiid(struct iet_volume *volume, const char *id)
+{
+ size_t len;
+
+ if ((len = strlen(id)) > SCSI_ID_LEN) {
+ eprintk("SCSI ID too long, %zd provided, %u max
", len,
+ SCSI_ID_LEN);
+ return -EINVAL;
+ }
+
+ memcpy(volume->scsi_id, id, len);
+
+ return 0;
+}
+
+static int set_scsisn(struct iet_volume *volume, const char *sn)
+{
+ size_t len;
+ int i;
+
+ if ((len = strlen(sn)) > SCSI_SN_LEN) {
+ eprintk("SCSI SN too long, %zd provided, %u max
", len,
+ SCSI_SN_LEN);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (!isascii(*(sn + i)) || !isprint(*(sn + i))) {
+ eprintk("invalid characters in SCSI SN, %s
",
+ "only printable ascii characters allowed!");
+ return -EINVAL;
+ }
+ }
+
+ memcpy(volume->scsi_sn, sn, len);
+
+ return 0;
+}
+
+/* Generate a MD5 hash of the target IQN and LUN number */
+static void gen_scsiid(struct iet_volume *volume)
+{
+ struct hash_desc hash;
+
+ hash.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
+ hash.flags = 0;
+
+ if (hash.tfm) {
+ struct scatterlist sg[2];
+ unsigned int nbytes = 0;
+
+ sg_init_table(sg, 2);
+
+ sg_set_buf(&sg[0], volume->target->name,
+ strlen(volume->target->name));
+ nbytes += strlen(volume->target->name);
+
+ sg_set_buf(&sg[1], &volume->lun, sizeof(volume->lun));
+ nbytes += sizeof(volume->lun);
+
+ crypto_hash_init(&hash);
+ crypto_hash_update(&hash, sg, nbytes);
+ crypto_hash_final(&hash, volume->scsi_id);
+
+ crypto_free_hash(hash.tfm);
+ } else {
+ /* If no MD5 available set ID to TID and LUN */
+ memcpy(volume->scsi_id, &volume->target->tid,
+ sizeof(volume->target->tid));
+ memcpy(volume->scsi_id + sizeof(volume->target->tid),
+ &volume->lun, sizeof(volume->lun));
+ }
+
+}
+
+static int parse_volume_params(struct iet_volume *volume, char *params)
{
int err = 0;
+ unsigned blk_sz;
substring_t args[MAX_OPT_ARGS];
char *p, *argp = NULL, *buf = (char *) get_zeroed_page(GFP_USER);

if (!buf)
return -ENOMEM;
+
strncpy(buf, params, PAGE_CACHE_SIZE);

while ((p = strsep(&buf, ",")) != NULL) {
@@ -49,22 +133,65 @@ static int set_iotype(struct iet_volume *volume, char *params)

if (!*p)
continue;
+ iet_strtolower(p);
token = match_token(p, tokens, args);
switch (token) {
- case Opt_type:
- if (!(argp = match_strdup(&args[0])))
+ case opt_type:
+ argp = match_strdup(&args[0]);
+ if (!argp) {
err = -ENOMEM;
- if (argp && !(volume->iotype = get_iotype(argp)))
+ break;
+ }
+ if (!(volume->iotype = get_iotype(argp)))
err = -ENOENT;
kfree(argp);
break;
- case Opt_iomode:
- if (!(argp = match_strdup(&args[0])))
+ case opt_iomode:
+ argp = match_strdup(&args[0]);
+ if (!argp) {
err = -ENOMEM;
- if (argp && !strcmp(argp, "ro"))
+ break;
+ }
+ if (!strcmp(argp, "ro"))
SetLUReadonly(volume);
- else if (argp && !strcmp(argp, "wb"))
+ else if (!strcmp(argp, "wb"))
SetLUWCache(volume);
+ else if (strcmp(argp, "wt"))
+ err = -EINVAL;
+ kfree(argp);
+ break;
+ case opt_scsiid:
+ argp = match_strdup(&args[0]);
+ if (!argp) {
+ err = -ENOMEM;
+ break;
+ }
+ err = set_scsiid(volume, argp);
+ kfree(argp);
+ break;
+ case opt_scsisn:
+ argp = match_strdup(&args[0]);
+ if (!argp) {
+ err = -ENOMEM;
+ break;
+ }
+ err = set_scsisn(volume, argp);
+ kfree(argp);
+ break;
+ case opt_blk_size:
+ argp = match_strdup(&args[0]);
+ if (!argp) {
+ err = -ENOMEM;
+ break;
+ }
+ blk_sz = simple_strtoull(argp, NULL, 10);
+ if (is_power_of_2(blk_sz) &&
+ 512 <= blk_sz && blk_sz <= IET_MAX_BLOCK_SIZE)
+ volume->blk_shift = ilog2(blk_sz);
+ else {
+ eprintk("invalid BlockSize=%u
", blk_sz);
+ err = -EINVAL;
+ }
kfree(argp);
break;
default:
@@ -115,7 +242,7 @@ int volume_add(struct iscsi_target *target, struct volume_info *info)
goto free_args;
}

- ret = set_iotype(volume, args);
+ ret = parse_volume_params(volume, args);
if (ret < 0)
goto free_args;

@@ -123,6 +250,17 @@ int volume_add(struct iscsi_target *target, struct volume_info *info)
if (ret < 0)
goto free_args;

+ if (!volume->scsi_id[0])
+ gen_scsiid(volume);
+
+ if (!volume->scsi_sn[0]) {
+ int i;
+
+ for (i = 0; i < SCSI_ID_LEN; i++)
+ snprintf(volume->scsi_sn + (i * 2), 3, "%02x",
+ volume->scsi_id[i]);
+ }
+
INIT_LIST_HEAD(&volume->queue.wait_list);
spin_lock_init(&volume->queue.queue_lock);
spin_lock_init(&volume->reserve_lock);
@@ -193,35 +331,54 @@ void volume_put(struct iet_volume *volume)

int volume_reserve(struct iet_volume *volume, u64 sid)
{
+ int err = 0;
+
if (!volume)
return -ENOENT;

spin_lock(&volume->reserve_lock);
- if (volume->reserve_sid && volume->reserve_sid != sid) {
- spin_unlock(&volume->reserve_lock);
- return -EBUSY;
- }
+ if (volume->reserve_sid && volume->reserve_sid != sid)
+ err = -EBUSY;
+ else
+ volume->reserve_sid = sid;

- volume->reserve_sid = sid;
spin_unlock(&volume->reserve_lock);
-
- return 0;
+ return err;
}

int is_volume_reserved(struct iet_volume *volume, u64 sid)
{
- if (!volume || !volume->reserve_sid || volume->reserve_sid == sid)
- return 0;
+ int err = 0;

- return -EBUSY;
+ if (!volume)
+ return -ENOENT;
+
+ spin_lock(&volume->reserve_lock);
+ if (!volume->reserve_sid || volume->reserve_sid == sid)
+ err = 0;
+ else
+ err = -EBUSY;
+
+ spin_unlock(&volume->reserve_lock);
+ return err;
}

int volume_release(struct iet_volume *volume, u64 sid, int force)
{
+ int err = 0;
+
+ if (!volume)
+ return -ENOENT;
+
+ spin_lock(&volume->reserve_lock);
+
if (force || volume->reserve_sid == sid)
volume->reserve_sid = 0;
+ else
+ err = -EBUSY;

- return 0;
+ spin_unlock(&volume->reserve_lock);
+ return err;
}

static void iet_volume_info_show(struct seq_file *seq, struct iscsi_target *target)
@@ -238,6 +395,8 @@ static void iet_volume_info_show(struct seq_file *seq, struct iscsi_target *targ
else
seq_printf(seq, " iomode:wt");

+ seq_printf(seq, " blocks:%llu blocksize:%u",
+ volume->blk_cnt, 1 << volume->blk_shift);
if (volume->iotype->show)
volume->iotype->show(volume, seq);
else
diff --git a/ubuntu/iscsitarget/wthread.c b/ubuntu/iscsitarget/wthread.c
index b49ddb7..997a3d6 100644
--- a/ubuntu/iscsitarget/wthread.c
+++ b/ubuntu/iscsitarget/wthread.c
@@ -67,6 +67,15 @@ static int worker_thread(void *arg)
struct iscsi_conn *conn;
DECLARE_WAITQUEUE(wait, current);

+ get_io_context(GFP_KERNEL, -1);
+
+ if (!current->io_context)
+ eprintk("%s
", "Failed to get IO context");
+ else if (info->wthread_ioc)
+ copy_io_context(&current->io_context, &info->wthread_ioc);
+ else
+ info->wthread_ioc = current->io_context;
+
add_wait_queue(&info->wthread_sleep, &wait);

__set_current_state(TASK_RUNNING);
@@ -74,12 +83,15 @@ static int worker_thread(void *arg)
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);
}

- __set_current_state(TASK_INTERRUPTIBLE);
+ set_current_state(TASK_INTERRUPTIBLE);
if (list_empty(&info->work_queue))
schedule();

@@ -88,6 +100,16 @@ static int worker_thread(void *arg)

remove_wait_queue(&info->wthread_sleep, &wait);

+ if (current->io_context) {
+ struct io_context *ioc = current->io_context;
+
+ task_lock(current);
+ current->io_context = NULL;
+ task_unlock(current);
+
+ put_io_context(ioc);
+ }
+
return 0;
}

@@ -138,6 +160,7 @@ int wthread_init(struct worker_thread_info *info)
spin_lock_init(&info->wthread_lock);

info->nr_running_wthreads = 0;
+ info->wthread_ioc = NULL;

INIT_LIST_HEAD(&info->work_queue);
INIT_LIST_HEAD(&info->wthread_list);
--
1.7.0.4




--
kernel-team mailing list
kernel-team@lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/kernel-team
 

Thread Tools




All times are GMT. The time now is 07:39 AM.

VBulletin, Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO ©2007, Crawlability, Inc.
Copyright 2007 - 2008, www.linux-archive.org