From 5b11d67e242c67027bf5c0a61167fc071c36eafd Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 21 Apr 2008 10:27:59 +0000 Subject: [PATCH] Change proc interface to SCST like. Allow to enable/disable each lun for SPI device. git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@342 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- qla_isp/README.scst | 10 +- qla_isp/linux/isp_scst.c | 289 +++++++++++++++++++++++++++++++++------ 2 files changed, 249 insertions(+), 50 deletions(-) diff --git a/qla_isp/README.scst b/qla_isp/README.scst index 9f4ea71d2..5b7ae7697 100644 --- a/qla_isp/README.scst +++ b/qla_isp/README.scst @@ -56,15 +56,15 @@ $ modprobe isp_mod 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 diff --git a/qla_isp/linux/isp_scst.c b/qla_isp/linux/isp_scst.c index 6afe8f782..2bcd62a16 100644 --- a/qla_isp/linux/isp_scst.c +++ b/qla_isp/linux/isp_scst.c @@ -84,9 +84,12 @@ #include #include #include +#include #include #include +#define LOG_PREFIX "qla_isp" + #include #include #include @@ -160,7 +163,7 @@ struct bus_chan { 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 @@ struct bus { 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 __inline bus_t *bus_from_name(const char *); 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 @@ scsi_target_thread(void *arg) } 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 @@ scsi_target_enadis(bus_chan_t *bc, int en) 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; + 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 @@ isp_task_mgmt_fn_done(struct scst_mgmt_cmd *mgmt_cmd) (*bp->h.r_action) (QIN_NOTIFY_ACK, np); } -static int -isp_read_proc(struct seq_file *seq, struct scst_tgt *tgt) -{ - bus_chan_t *bc; +static DEFINE_MUTEX(proc_mutex); - bc = tgt->tgt_priv; - if (!bc) { +static int +isp_read_proc(struct seq_file *seq, void *v) +{ + bus_t *bp = seq->private; + bus_chan_t *bc; + int chan; + + 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,10 +1405,19 @@ static struct scst_tgt_template isp_tgt_template = .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) { @@ -1239,6 +1426,7 @@ register_hba(bus_t *bp) 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 @@ register_hba(bus_t *bp) 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 @@ static void 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 @@ unregister_hba(bus_t *bp, hba_register_t *unreg_hp) } 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);