|
From: <swg...@us...> - 2008-04-21 10:28:02
|
Revision: 342
http://scst.svn.sourceforge.net/scst/?rev=342&view=rev
Author: swgruszka
Date: 2008-04-21 03:27:59 -0700 (Mon, 21 Apr 2008)
Log Message:
-----------
Change proc interface to SCST like. Allow to enable/disable each lun for SPI device.
Modified Paths:
--------------
trunk/qla_isp/README.scst
trunk/qla_isp/linux/isp_scst.c
Modified: trunk/qla_isp/README.scst
===================================================================
--- trunk/qla_isp/README.scst 2008-04-21 08:36:14 UTC (rev 341)
+++ trunk/qla_isp/README.scst 2008-04-21 10:27:59 UTC (rev 342)
@@ -56,15 +56,15 @@
Now anytime the system boot and QLA card is plugged, modules will load
automatically.
-When driver is loaded, enable all logical units for each HBA to work in
-target mode. You need to write string "1" to procfs configuration file, ie:
+When driver is loaded, enable all channels for each HBA to work in
+target mode. You need to write string "enable all" to procfs configuration file, ie:
-$ echo 1 > /proc/scsi_tgt/qla_isp/0
+$ echo "enable all" > /proc/scsi_tgt/qla_isp/3
To unload isp_mod.ko module you must disable target mode for all cards ie:
-$ echo 0 > /proc/scsi_tgt/qla_isp/0
-$ echo 0 > /proc/scsi_tgt/qla_isp/1
+$ echo "disable all" > /proc/scsi_tgt/qla_isp/3
+$ echo "disable all" > /proc/scsi_tgt/qla_isp/4
Enable/disable target mode is not the same as switching target/initiator role.
If you have more then one device, role of each you can set using isp_roles
Modified: trunk/qla_isp/linux/isp_scst.c
===================================================================
--- trunk/qla_isp/linux/isp_scst.c 2008-04-21 08:36:14 UTC (rev 341)
+++ trunk/qla_isp/linux/isp_scst.c 2008-04-21 10:27:59 UTC (rev 342)
@@ -84,9 +84,12 @@
#include <linux/spinlock.h>
#include <asm/scatterlist.h>
#include <asm/system.h>
+#include <linux/ctype.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
+#define LOG_PREFIX "qla_isp"
+
#include <scsi/scsi_host.h>
#include <scst.h>
#include <scst_debug.h>
@@ -160,7 +163,7 @@
tmd_cmd_t * tmds_tail;
struct tasklet_struct tasklet;
struct scst_tgt * scst_tgt;
- int enable; /* is target mode enabled in low level driver */
+ uint64_t enable; /* is target mode enabled in low level driver, one bit per lun */
bus_t * bus; /* back pointer */
};
@@ -169,6 +172,7 @@
int need_reg; /* helpers for registration / unregistration */
hba_register_t * unreg_hp;
bus_chan_t * bchan; /* channels */
+ struct scst_proc_data proc_data;
};
#define SDprintk if (debug) printk
@@ -187,7 +191,7 @@
static void scsi_target_start_cmd(tmd_cmd_t *, int);
static void scsi_target_done_cmd(tmd_cmd_t *, int);
static int scsi_target_thread(void *);
-static int scsi_target_enadis(bus_chan_t *, int);
+static int scsi_target_enadis(bus_t *, uint64_t, int, int);
static bus_t busses[MAX_BUS];
@@ -991,28 +995,39 @@
}
static int
-scsi_target_enadis(bus_chan_t *bc, int en)
+scsi_target_enadis(bus_t *bp, uint64_t en, int chan, int lun)
{
DECLARE_MUTEX_LOCKED(rsem);
enadis_t ec;
info_t info;
- int chan, err;
- bus_t *bp;
+ bus_chan_t *bc;
+ uint64_t mask;
- if (en == bc->enable) {
- return (0);
+ BUG_ON(chan < 0 || chan >= bp->h.r_nchannels);
+ BUG_ON(lun != LUN_ANY && (lun < 0 || lun >= MAX_LUN));
+ bc = &bp->bchan[chan];
+
+ if (bp->h.r_type == R_FC) {
+ if (en == bc->enable) {
+ return (0);
+ }
+ } else {
+ if (lun == LUN_ANY) {
+ return (-EINVAL);
+ } else {
+ mask = ~(1 << lun);
+ if ((en << lun) == (bc->enable & mask)) {
+ return (0);
+ }
+ }
}
- bp = bc->bus;
- chan = (bc - bp->bchan);
- BUG_ON(bp == NULL || chan >= bp->h.r_nchannels);
memset(&info, 0, sizeof (info));
info.i_identity = bp->h.r_identity;
info.i_channel = chan;
(*bp->h.r_action)(QIN_GETINFO, &info);
if (info.i_error) {
- err = info.i_error;
- goto failed;
+ return (info.i_error);
}
memset(&ec, 0, sizeof (ec));
@@ -1021,23 +1036,24 @@
if (bp->h.r_type == R_FC) {
ec.en_lun = LUN_ANY;
} else {
- ec.en_lun = 0;
+ ec.en_lun = lun;
}
+
ec.en_private = &rsem;
-
(*bp->h.r_action)(en ? QIN_ENABLE : QIN_DISABLE, &ec);
down(&rsem);
if (ec.en_error) {
- err = ec.en_error;
- goto failed;
+ return (ec.en_error);
}
- bc->enable = en;
+ if (bp->h.r_type == R_FC) {
+ bc->enable = en;
+ } else {
+ mask = ~(1 << lun);
+ bc->enable &= mask;
+ bc->enable |= (en << lun);
+ }
return (0);
-
-failed:
- Eprintk("%s%d: %s channel %d failed with error %d\n", bp->h.r_name, bp->h.r_inst, en ? "enable" : "disable", chan, err);
- return (err);
}
static int
@@ -1171,41 +1187,203 @@
(*bp->h.r_action) (QIN_NOTIFY_ACK, np);
}
+static DEFINE_MUTEX(proc_mutex);
+
static int
-isp_read_proc(struct seq_file *seq, struct scst_tgt *tgt)
+isp_read_proc(struct seq_file *seq, void *v)
{
+ bus_t *bp = seq->private;
bus_chan_t *bc;
+ int chan;
- bc = tgt->tgt_priv;
- if (!bc) {
+ if (bp == NULL || bp->bchan == NULL) {
return (-ENODEV);
}
- seq_printf(seq, "%d\n", bc->enable);
+
+ if (mutex_lock_interruptible(&proc_mutex)) {
+ return (-ERESTARTSYS);
+ }
+
+ seq_printf(seq, "%s HBA %s%d DEVID %x\n", bp->h.r_type == R_FC ? "FC" : "SPI", bp->h.r_name, bp->h.r_inst, bp->h.r_locator);
+ for (chan = 0; chan < bp->h.r_nchannels; chan++) {
+ bc = &bp->bchan[chan];
+ if (bp->h.r_type == R_FC) {
+ seq_printf(seq, "%-2d: %d\n", chan, bc->enable ? 1 : 0);
+ } else {
+ seq_printf(seq, "%-2d: 0x%llx\n", chan, bc->enable);
+ }
+ }
+
+ mutex_unlock(&proc_mutex);
return (0);
}
static int
-isp_write_proc(char *buf, char **start, off_t offset, int len, int *eof, struct scst_tgt *tgt)
+isp_write_proc(struct file *file, const char __user *buf, size_t len, loff_t *off)
{
- bus_chan_t *bc = tgt->tgt_priv;
- int ret, en;
+ char *ptr, *p, *old;
+ enum { DISABLE = 0, ENABLE = 1, TEST } action;
+ int en = -1, res = -EINVAL;
+ int all_channels = 0, all_luns = 0;
+ int lun, chan;
+ bus_t *bp = PDE(file->f_dentry->d_inode)->data;
- if (!bc) {
+ if (bp == NULL || bp->bchan == NULL) {
return (-ENODEV);
}
- if (len < 2 || len > 3) {
- return (-EINVAL);
+ if (!buf) {
+ goto out;
}
- en = buf[0] - '0';
- if (en < 0 || en > 1) {
- return (-EINVAL);
+ ptr = (char *)__get_free_page(GFP_KERNEL);
+ if (ptr == NULL) {
+ res = -ENOMEM;
+ goto out;
}
- ret = scsi_target_enadis(bc, en);
- if (ret < 0) {
- return (ret);
+ if (copy_from_user(ptr, buf, len)) {
+ res = -EFAULT;
+ goto out_free;
}
- *eof = 1;
- return (len);
+ if (len < PAGE_SIZE) {
+ ptr[len] = '\0';
+ } else if (ptr[PAGE_SIZE-1]) {
+ goto out_free;
+ }
+
+ /*
+ * Usage: echo "enable|disable chan lun" > /proc/scsi_tgt/qla_isp/N
+ * or echo "test" > /proc/scsi_tgt/qla_isp/N
+ */
+ p = ptr;
+ if (p[strlen(p) - 1] == '\n') {
+ p[strlen(p) - 1] = '\0';
+ }
+ if (!strncasecmp("enable", p, 6)) {
+ p += 6;
+ action = ENABLE;
+ } else if (!strncasecmp("disable", p, 7)) {
+ p += 7;
+ action = DISABLE;
+ } else if (!strncasecmp("test", p, 4)) {
+ action = TEST;
+ } else {
+ PRINT_ERROR("unknown action \"%s\"", p);
+ goto out_free;
+ }
+
+ switch (action) {
+ case ENABLE:
+ case DISABLE:
+ if (!isspace(*p)) {
+ PRINT_ERROR("cannot parse arguments for action \"%s\"", action == DISABLE ? "disable" : "enable");
+ goto out_free;
+ }
+
+ /* get channel */
+ while (isspace(*p) && *p != '\0') {
+ p++;
+ }
+ old = p;
+ chan = simple_strtoul(p, &p, 0);
+ if (old == p) {
+ if (!strncasecmp("all", p, 3)) {
+ all_channels = 1;
+ } else {
+ PRINT_ERROR("cannot parse channel for action \"%s\"", action == DISABLE ? "disable" : "enable");
+ goto out_free;
+ }
+ } else if (chan < 0 || chan >= bp->h.r_nchannels) {
+ PRINT_ERROR("bad channel number %d", chan);
+ goto out_free;
+ }
+
+ /* get lun */
+ if (bp->h.r_type == R_SPI) {
+ while (isspace(*p) && *p != '\0') {
+ p++;
+ }
+ old = p;
+ lun = simple_strtoul(p, &p, 0);
+ if (old == p) {
+ if (!strncasecmp("all", p, 3)) {
+ all_luns = 1;
+ } else {
+ PRINT_ERROR("cannot parse lun for action \"%s\"", action == DISABLE ? "disable" : "enable");
+ goto out_free;
+ }
+ } else if (lun < 0 && lun >= MAX_LUN) {
+ PRINT_ERROR("bad lun %d", lun);
+ goto out_free;
+ }
+ } else {
+ lun = LUN_ANY;
+ }
+
+ en = action;
+ break;
+ case TEST:
+ printk("%s test\n", __FUNCTION__);
+ res = len;
+ break;
+ }
+
+ if (en == 0 || en == 1) {
+ /*
+ * channel 0 must be enabled first and disabled last, so when enabling all
+ * channels do it in ascending order and when disabling all in descending order
+ */
+ int chan_srt, chan_end, chan_inc;
+ int lun_srt, lun_end;
+
+ if (all_channels) {
+ if (en) {
+ chan_srt = 0;
+ chan_end = bp->h.r_nchannels;
+ chan_inc = 1;
+ } else {
+ chan_srt = bp->h.r_nchannels - 1;
+ chan_end = -1;
+ chan_inc = -1;
+ }
+ } else {
+ chan_srt = chan;
+ chan_end = chan + 1;
+ chan_inc = 1;
+ }
+
+ if (bp->h.r_type == R_FC) {
+ lun_srt = LUN_ANY;
+ lun_end = LUN_ANY + 1;
+ } else {
+ if (all_luns) {
+ lun_srt = 0;
+ lun_end = MAX_LUN;
+ } else {
+ lun_srt = lun;
+ lun_end = lun + 1;
+ }
+ }
+
+ if (mutex_lock_interruptible(&proc_mutex)) {
+ res = -ERESTARTSYS;
+ goto out_free;
+ }
+ for (chan = chan_srt; chan != chan_end; chan += chan_inc) {
+ for (lun = lun_srt; lun != lun_end; lun++) {
+ res = scsi_target_enadis(bp, en, chan, lun);
+ if (res < 0) {
+ PRINT_ERROR("%s channel %d failed with error %d", en ? "enable" : "disable", chan, res);
+ /* processed anyway */
+ }
+ }
+ }
+ res = len;
+ mutex_unlock(&proc_mutex);
+ }
+
+out_free:
+ free_page((unsigned long)ptr);
+out:
+ return (res);
}
static struct scst_tgt_template isp_tgt_template =
@@ -1227,11 +1405,20 @@
.task_mgmt_fn_done = isp_task_mgmt_fn_done,
//.report_aen = isp_report_aen,
- .read_proc = isp_read_proc,
- .write_proc = isp_write_proc,
};
static void
+bus_set_proc_data(bus_t *bp)
+{
+ const struct scst_proc_data proc_data = {
+ SCST_DEF_RW_SEQ_OP(isp_write_proc)
+ .show = isp_read_proc,
+ };
+ bp->proc_data = proc_data;
+ bp->proc_data.data = bp;
+}
+
+static void
register_hba(bus_t *bp)
{
char name[32];
@@ -1239,6 +1426,7 @@
int chan;
bus_chan_t *bchan, *bc;
struct scst_tgt *scst_tgt;
+ struct proc_dir_entry *pde;
bchan = kzalloc(bp->h.r_nchannels * sizeof(bus_chan_t), GFP_KERNEL);
if (bchan == NULL) {
@@ -1286,6 +1474,14 @@
scst_tgt->tgt_priv = bc;
}
+ snprintf(name, sizeof(name), "%d", ((ispsoftc_t *)bp->h.r_identity)->isp_osinfo.host->host_no);
+ bus_set_proc_data(bp);
+ pde = scst_create_proc_entry(scst_proc_get_tgt_root(&isp_tgt_template), name, &bp->proc_data);
+ if (pde == NULL) {
+ Eprintk("cannot create entry %s in /proc\n", name);
+ goto err_free_chan;
+ }
+
spin_lock_irq(&scsi_target_lock);
bp->bchan = bchan;
spin_unlock_irq(&scsi_target_lock);
@@ -1312,6 +1508,7 @@
unregister_hba(bus_t *bp, hba_register_t *unreg_hp)
{
int i, chan;
+ char name[32];
for (chan = 0; chan < bp->h.r_nchannels; chan++) {
/* remove existing initiators */
@@ -1325,12 +1522,14 @@
} while ((ptr = ini_next) != NULL);
}
}
-
if (bp->bchan[chan].scst_tgt) {
scst_unregister(bp->bchan[chan].scst_tgt);
}
}
+ snprintf(name, sizeof(name), "%d", ((ispsoftc_t *)bp->h.r_identity)->isp_osinfo.host->host_no);
+ remove_proc_entry(name, scst_proc_get_tgt_root(&isp_tgt_template));
+
/* it's safe now to reinit bp */
kfree(bp->bchan);
spin_lock_irq(&scsi_target_lock);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|