From: Takahiro Yasui <tyasui@redhat.com>
To: linux-scsi@vger.kernel.org
Cc: James Bottomley <James.Bottomley@HansenPartnership.com>
Subject: [RFC][PATCH 2/2] Introduce the parameter to limit scsi timeout count (take 2)
Date: Fri, 19 Jun 2009 18:38:55 -0400 [thread overview]
Message-ID: <4A3C137F.3090907@redhat.com> (raw)
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@redhat.com>
---
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@redhat.com>
+ *
+ * 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) {
reply other threads:[~2009-06-19 22:36 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4A3C137F.3090907@redhat.com \
--to=tyasui@redhat.com \
--cc=James.Bottomley@HansenPartnership.com \
--cc=linux-scsi@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).