[RFC][PATCH 2/2] Introduce the parameter to limit scsi timeout count (take 2)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Introduce interfaces, scsi_mod module parameter (initparm) and
procfs (scsi/initparm) to define default values, timeout and
max_timeout_cnt, for the devices identified by vendor and model.

Default values can be registered through these interfaces with
the format of "v:m:t:c[,v:m:t:c]"
(v:vendor, m:model, t=timeout, c=max_timeout_cnt)

Example:

 * Load scsi module with default values, scsi timeout(10s) and
   maximum timeout count (1) for HITACHI/DF600.

    # insmod scsi_mod.ko initparm="HITACHI:DF600:10:1"

 * Show current default settings

    # cat /proc/scsi/initparm
    'HITACHI' 'DF600' timeout=10 max_timeout_cnt=1


Signed-off-by: Takahiro Yasui <tyasui@xxxxxxxxxx>
---
 drivers/scsi/Makefile        |    3 
 drivers/scsi/scsi.c          |    8 -
 drivers/scsi/scsi_initparm.c |  338 +++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/scsi_priv.h     |    5 
 drivers/scsi/scsi_scan.c     |    2 
 5 files changed, 354 insertions(+), 2 deletions(-)

Index: linux-2.6.30/drivers/scsi/Makefile
===================================================================
--- linux-2.6.30.orig/drivers/scsi/Makefile
+++ linux-2.6.30/drivers/scsi/Makefile
@@ -150,7 +150,8 @@ obj-$(CONFIG_SCSI_WAIT_SCAN)	+= scsi_wai
 scsi_mod-y			+= scsi.o hosts.o scsi_ioctl.o constants.o \
 				   scsicam.o scsi_error.o scsi_lib.o
 scsi_mod-$(CONFIG_SCSI_DMA)	+= scsi_lib_dma.o
-scsi_mod-y			+= scsi_scan.o scsi_sysfs.o scsi_devinfo.o
+scsi_mod-y			+= scsi_scan.o scsi_sysfs.o scsi_devinfo.o \
+				   scsi_initparm.o
 scsi_mod-$(CONFIG_SCSI_NETLINK)	+= scsi_netlink.o
 scsi_mod-$(CONFIG_SYSCTL)	+= scsi_sysctl.o
 scsi_mod-$(CONFIG_SCSI_PROC_FS)	+= scsi_proc.o
Index: linux-2.6.30/drivers/scsi/scsi.c
===================================================================
--- linux-2.6.30.orig/drivers/scsi/scsi.c
+++ linux-2.6.30/drivers/scsi/scsi.c
@@ -1323,9 +1323,12 @@ static int __init init_scsi(void)
 	error = scsi_init_devinfo();
 	if (error)
 		goto cleanup_procfs;
-	error = scsi_init_hosts();
+	error = scsi_init_initparm();
 	if (error)
 		goto cleanup_devlist;
+	error = scsi_init_hosts();
+	if (error)
+		goto cleanup_config;
 	error = scsi_init_sysctl();
 	if (error)
 		goto cleanup_hosts;
@@ -1342,6 +1345,8 @@ cleanup_sysctl:
 	scsi_exit_sysctl();
 cleanup_hosts:
 	scsi_exit_hosts();
+cleanup_config:
+	scsi_exit_initparm();
 cleanup_devlist:
 	scsi_exit_devinfo();
 cleanup_procfs:
@@ -1360,6 +1365,7 @@ static void __exit exit_scsi(void)
 	scsi_exit_sysctl();
 	scsi_exit_hosts();
 	scsi_exit_devinfo();
+	scsi_exit_initparm();
 	scsi_exit_procfs();
 	scsi_exit_queue();
 }
Index: linux-2.6.30/drivers/scsi/scsi_initparm.c
===================================================================
--- /dev/null
+++ linux-2.6.30/drivers/scsi/scsi_initparm.c
@@ -0,0 +1,338 @@
+/*
+ * scsi_initparm.c
+ *
+ * Written by Takahiro Yasui <tyasui@xxxxxxxxxx>
+ *
+ * Based on the code from scsi_devinfo.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <scsi/scsi_device.h>
+
+#define MAX_VENDOR_LEN	8
+#define MAX_MODEL_LEN	16
+
+/*
+ * scsi_initparm_list: structure to hold device configrations
+ */
+struct scsi_initparm {
+	struct list_head list;
+	char vendor[MAX_VENDOR_LEN];
+	char model[MAX_MODEL_LEN];
+
+	int max_timeout_cnt;
+	int timeout;
+};
+
+static LIST_HEAD(scsi_initparm_list);
+static char initparm_str[256];
+
+/**
+ * find_initparm: find the entry with vender/model from scsi_initparm_list
+ *
+ * @vendor:     vendor string
+ * @model:      model (product) string
+ * @compatible: if true, vendor/model can include some additional characters,
+ *              but if not, vendor and model needs to to be exactly same.
+ *
+ * Returns: the entry address if found, NULL if not found.
+ **/
+static struct scsi_initparm *find_initparm(const unsigned char *vendor,
+					   const unsigned char *model,
+					   int compatible)
+{
+	struct scsi_initparm *parm;
+	int vlen, mlen;
+
+	list_for_each_entry(parm, &scsi_initparm_list, list) {
+		if (compatible) {
+			vlen = strnlen(parm->vendor, MAX_VENDOR_LEN);
+			mlen = strnlen(parm->model, MAX_MODEL_LEN);
+		} else {
+			vlen = MAX_VENDOR_LEN;
+			mlen = MAX_MODEL_LEN;
+		}
+
+		if (!strncmp(parm->vendor, vendor, vlen) &&
+		    !strncmp(parm->model, model, mlen))
+			return parm;
+	}
+
+	return NULL;
+}
+
+/**
+ * scsi_override_parms: update scsi device with registered values
+ * @sdev: vendor string
+ *
+ * Description:
+ *    If the entry with vender and mdoel is registered, scsi device
+ *    is updated with the values in the entry.
+ **/
+void scsi_override_parms(struct scsi_device *sdev)
+{
+	struct scsi_initparm *parm;
+
+	parm = find_initparm(sdev->vendor, sdev->model, 1);
+	if (!parm)
+		return;
+
+	blk_queue_rq_timeout(sdev->request_queue, parm->timeout * HZ);
+	sdev->max_timeout_cnt = parm->max_timeout_cnt;
+}
+
+static int scsi_initparm_list_add(char *vendor, char *model,
+				  char *opt1, char *opt2)
+{
+	struct scsi_initparm *parm;
+	unsigned long val;
+	int insert = 0;
+
+	parm = find_initparm(vendor, model, 0);
+	if (!parm) {
+		parm = kzalloc(sizeof(*parm), GFP_KERNEL);
+		if (!parm) {
+			printk(KERN_ERR "%s: no memory\n", __func__);
+			return -ENOMEM;
+		}
+
+		strncpy(parm->vendor, vendor, sizeof(parm->vendor));
+		strncpy(parm->model,  model, sizeof(parm->model));
+
+		insert = 1;
+	}
+
+
+	if (strict_strtoul(opt1, 0, &val)) {
+		printk(KERN_ERR "%s: bad string for timeout"
+		       " '%s' '%s' '%s' '%s'\n", __func__,
+		       vendor, model, opt1, opt2);
+		return -EINVAL;
+	}
+	parm->timeout = (int)val;
+
+
+	if (strict_strtoul(opt2, 0, &val)) {
+		printk(KERN_ERR "%s: bad string for max_timeout_cnt"
+		       " '%s' '%s' '%s' '%s'\n", __func__,
+		       vendor, model, opt1, opt2);
+		return -EINVAL;
+	}
+	parm->max_timeout_cnt = (int)val;
+
+	if (insert)
+		list_add(&parm->list, &scsi_initparm_list);
+
+	return 0;
+}
+
+/**
+ * scsi_initparm_list_add_str - parse parms and add/update the entry
+ *                              to/in the scsi_initparm_list
+ * @parms: string of init parameters to add
+ *
+ * Description:
+ *    Parse parms and add entries to the scsi_initparm_list.
+ *    parms is a format of "v:m:t:c[,v:m:t:c]"
+ *    (vendor(v), model(m), timeout(t) and max_timeout_cnt(c))
+ *
+ * Returns: 0 if OK, -error on failure.
+ **/
+static int scsi_initparm_list_add_str(char *parms)
+{
+	char *vendor, *model, *opt1, *opt2;
+	char *next, *next_check;
+	int res = 0;
+
+	next = parms;
+	if (next && next[0] == '"') {
+		next++;
+		next_check = ",\"";
+	} else
+		next_check = ",";
+
+	for (vendor = strsep(&next, ":"); vendor && (vendor[0] != '\0')
+		     && (res == 0); vendor = strsep(&next, ":")) {
+		opt1 = opt2 = NULL;
+
+		model = strsep(&next, ":");
+		if (model)
+			opt1 = strsep(&next, ":");
+		if (opt1)
+			opt2 = strsep(&next, next_check);
+
+		if (!model || !opt1 || !opt2) {
+			printk(KERN_ERR "%s: bad string '%s' '%s'"
+			       " '%s' '%s'\n", __func__,
+			       vendor, model, opt1, opt2);
+			res = -EINVAL;
+		} else
+			res = scsi_initparm_list_add(vendor, model,
+						     opt1, opt2);
+	}
+
+	return res;
+}
+
+#ifdef CONFIG_SCSI_PROC_FS
+static int initparm_seq_show(struct seq_file *m, void *v)
+{
+	struct scsi_initparm *p =
+		list_entry(v, struct scsi_initparm, list);
+
+	seq_printf(m, "'%." __stringify(MAX_VENDOR_LEN) "s' '%."
+		   __stringify(MAX_MODEL_LEN) "s'", p->vendor, p->model);
+	seq_printf(m, " timeout=%d", p->timeout);
+	seq_printf(m, " max_timeout_cnt=%d\n", p->max_timeout_cnt);
+
+	return 0;
+}
+
+static void *initparm_seq_start(struct seq_file *m, loff_t *pos)
+{
+	return seq_list_start(&scsi_initparm_list, *pos);
+}
+
+static void *initparm_seq_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	return seq_list_next(v, &scsi_initparm_list, pos);
+}
+
+static void initparm_seq_stop(struct seq_file *m, void *v)
+{
+}
+
+static const struct seq_operations scsi_initparm_seq_ops = {
+	.start	= initparm_seq_start,
+	.next	= initparm_seq_next,
+	.stop	= initparm_seq_stop,
+	.show	= initparm_seq_show,
+};
+
+static int proc_initparm_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &scsi_initparm_seq_ops);
+}
+
+/**
+ * proc_scsi_initparm_write - add parms to scsi_initparm_list via /proc.
+ *
+ * Description:
+ *    Register parameters to the initparam list. Each parameter is
+ *    composed of vendor(v), model(m), timeout(t) and max_timeout_cnt(c).
+ *    To use, echo "v:m:t:c[,v:m:t:c]" > /proc/scsi/initparam
+ **/
+static ssize_t proc_initparm_write(struct file *file,
+				    const char __user *buf,
+				    size_t length, loff_t *ppos)
+{
+	char *buffer;
+	ssize_t err = length;
+
+	if (!buf || length > PAGE_SIZE)
+		return -EINVAL;
+
+	buffer = (char *)__get_free_page(GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	if (copy_from_user(buffer, buf, length)) {
+		err = -EFAULT;
+		goto out;
+	}
+
+	if (length < PAGE_SIZE)
+		buffer[length] = '\0';
+	else if (buffer[PAGE_SIZE-1]) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	scsi_initparm_list_add_str(buffer);
+
+out:
+	free_page((unsigned long)buffer);
+	return err;
+}
+
+static const struct file_operations proc_initparm_fops = {
+	.owner		= THIS_MODULE,
+	.open		= proc_initparm_open,
+	.read		= seq_read,
+	.write		= proc_initparm_write,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+#endif /* CONFIG_SCSI_PROC_FS */
+
+module_param_string(initparm, initparm_str, sizeof(initparm_str), 0);
+MODULE_PARM_DESC(initparm,
+	"Register the default values of timeout(t) and max_timeout_cnt(c)"
+	" to devices indicated by vendor(v) and model(m)"
+	" initparam=v:m:t:c[,v:m:t:c]");
+
+/**
+ * scsi_init_initparm - set up the dynamic initparm list.
+ *
+ * Description:
+ *    This function is called from scsi.c:init_scsi to parse and
+ *    setup the module parameter and create proc entry.
+ **/
+int __init scsi_init_initparm(void)
+{
+#ifdef CONFIG_SCSI_PROC_FS
+	struct proc_dir_entry *p;
+#endif
+	int error;
+
+	error = scsi_initparm_list_add_str(initparm_str);
+	if (error)
+		return error;
+
+#ifdef CONFIG_SCSI_PROC_FS
+	p = proc_create("scsi/initparm", 0, NULL, &proc_initparm_fops);
+	if (!p)
+		return -ENOMEM;
+#endif
+	return 0;
+}
+
+/**
+ * scsi_exit_initparm - clean up the dynamic initparm list
+ *
+ * Description:
+ *    This function is called from scsi.c:exit_scsi to remove the proc
+ *    entry and the members of scsi_initparm_list.
+ **/
+void scsi_exit_initparm(void)
+{
+	struct list_head *lh, *lh_next;
+	struct scsi_initparm *parm;
+
+#ifdef CONFIG_SCSI_PROC_FS
+	remove_proc_entry("scsi/initparm", NULL);
+#endif
+
+	list_for_each_safe(lh, lh_next, &scsi_initparm_list) {
+		parm = list_entry(lh, struct scsi_initparm, list);
+		kfree(parm);
+	}
+}
Index: linux-2.6.30/drivers/scsi/scsi_priv.h
===================================================================
--- linux-2.6.30.orig/drivers/scsi/scsi_priv.h
+++ linux-2.6.30/drivers/scsi/scsi_priv.h
@@ -45,6 +45,11 @@ extern int scsi_get_device_flags(struct 
 extern int __init scsi_init_devinfo(void);
 extern void scsi_exit_devinfo(void);
 
+/* scsi_initparm.c */
+extern void scsi_override_parms(struct scsi_device *sdev);
+extern int __init scsi_init_initparm(void);
+extern void scsi_exit_initparm(void);
+
 /* scsi_error.c */
 extern enum blk_eh_timer_return scsi_times_out(struct request *req);
 extern int scsi_error_handler(void *host);
Index: linux-2.6.30/drivers/scsi/scsi_scan.c
===================================================================
--- linux-2.6.30.orig/drivers/scsi/scsi_scan.c
+++ linux-2.6.30/drivers/scsi/scsi_scan.c
@@ -926,6 +926,8 @@ static int scsi_add_lun(struct scsi_devi
 	if (*bflags & BLIST_RETRY_HWERROR)
 		sdev->retry_hwerror = 1;
 
+	scsi_override_parms(sdev);
+
 	transport_configure_device(&sdev->sdev_gendev);
 
 	if (sdev->host->hostt->slave_configure) {



--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux