[S390] cio: Fix parsing mechanism for blacklisted devices.
authorMichael Ernst <mernst@de.ibm.com>
Wed, 7 May 2008 07:22:55 +0000 (09:22 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 7 May 2008 07:23:01 +0000 (09:23 +0200)
New format cssid.ssid.devno is now parsed correctly.

Signed-off-by: Michael Ernst <mernst@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/cio/blacklist.c

index 084447618999937bdd19dd16fcbe926de343fe1f..9c21b8f43f9b70ab8b559d9a880bfb160d73ec01 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <asm/cio.h>
 #include <asm/uaccess.h>
+#include <asm/cio.h>
 
 #include "blacklist.h"
 #include "cio.h"
@@ -43,163 +44,169 @@ typedef enum {add, free} range_action;
  * Function: blacklist_range
  * (Un-)blacklist the devices from-to
  */
-static void
-blacklist_range (range_action action, unsigned int from, unsigned int to,
-                unsigned int ssid)
+static int blacklist_range(range_action action, unsigned int from_ssid,
+                          unsigned int to_ssid, unsigned int from,
+                          unsigned int to, int msgtrigger)
 {
-       if (!to)
-               to = from;
-
-       if (from > to || to > __MAX_SUBCHANNEL || ssid > __MAX_SSID) {
-               printk (KERN_WARNING "cio: Invalid blacklist range "
-                       "0.%x.%04x to 0.%x.%04x, skipping\n",
-                       ssid, from, ssid, to);
-               return;
+       if ((from_ssid > to_ssid) || ((from_ssid == to_ssid) && (from > to))) {
+               if (msgtrigger)
+                       printk(KERN_WARNING "cio: Invalid cio_ignore range "
+                              "0.%x.%04x-0.%x.%04x\n", from_ssid, from,
+                              to_ssid, to);
+               return 1;
        }
-       for (; from <= to; from++) {
+
+       while ((from_ssid < to_ssid) || ((from_ssid == to_ssid) &&
+              (from <= to))) {
                if (action == add)
-                       set_bit (from, bl_dev[ssid]);
+                       set_bit(from, bl_dev[from_ssid]);
                else
-                       clear_bit (from, bl_dev[ssid]);
+                       clear_bit(from, bl_dev[from_ssid]);
+               from++;
+               if (from > __MAX_SUBCHANNEL) {
+                       from_ssid++;
+                       from = 0;
+               }
        }
+
+       return 0;
 }
 
-/*
- * Function: blacklist_busid
- * Get devno/busid from given string.
- * Shamelessly grabbed from dasd_devmap.c.
- */
-static int
-blacklist_busid(char **str, int *id0, int *ssid, int *devno)
+static int pure_hex(char **cp, unsigned int *val, int min_digit,
+                   int max_digit, int max_val)
 {
-       int val, old_style;
-       char *sav;
+       int diff;
+       unsigned int value;
 
-       sav = *str;
+       diff = 0;
+       *val = 0;
 
-       /* check for leading '0x' */
-       old_style = 0;
-       if ((*str)[0] == '0' && (*str)[1] == 'x') {
-               *str += 2;
-               old_style = 1;
-       }
-       if (!isxdigit((*str)[0]))       /* We require at least one hex digit */
-               goto confused;
-       val = simple_strtoul(*str, str, 16);
-       if (old_style || (*str)[0] != '.') {
-               *id0 = *ssid = 0;
-               if (val < 0 || val > 0xffff)
-                       goto confused;
-               *devno = val;
-               if ((*str)[0] != ',' && (*str)[0] != '-' &&
-                   (*str)[0] != '\n' && (*str)[0] != '\0')
-                       goto confused;
-               return 0;
+       while (isxdigit(**cp) && (diff <= max_digit)) {
+
+               if (isdigit(**cp))
+                       value = **cp - '0';
+               else
+                       value = tolower(**cp) - 'a' + 10;
+               *val = *val * 16 + value;
+               (*cp)++;
+               diff++;
        }
-       /* New style x.y.z busid */
-       if (val < 0 || val > 0xff)
-               goto confused;
-       *id0 = val;
-       (*str)++;
-       if (!isxdigit((*str)[0]))       /* We require at least one hex digit */
-               goto confused;
-       val = simple_strtoul(*str, str, 16);
-       if (val < 0 || val > 0xff || (*str)++[0] != '.')
-               goto confused;
-       *ssid = val;
-       if (!isxdigit((*str)[0]))       /* We require at least one hex digit */
-               goto confused;
-       val = simple_strtoul(*str, str, 16);
-       if (val < 0 || val > 0xffff)
-               goto confused;
-       *devno = val;
-       if ((*str)[0] != ',' && (*str)[0] != '-' &&
-           (*str)[0] != '\n' && (*str)[0] != '\0')
-               goto confused;
+
+       if ((diff < min_digit) || (diff > max_digit) || (*val > max_val))
+               return 1;
+
        return 0;
-confused:
-       strsep(str, ",\n");
-       printk(KERN_WARNING "cio: Invalid cio_ignore parameter '%s'\n", sav);
-       return 1;
 }
 
-static int
-blacklist_parse_parameters (char *str, range_action action)
+static int parse_busid(char *str, int *cssid, int *ssid, int *devno,
+                      int msgtrigger)
 {
-       int from, to, from_id0, to_id0, from_ssid, to_ssid;
-
-       while (*str != 0 && *str != '\n') {
-               range_action ra = action;
-               while(*str == ',')
-                       str++;
-               if (*str == '!') {
-                       ra = !action;
-                       ++str;
+       char *str_work;
+       int val, rc, ret;
+
+       rc = 1;
+
+       if (*str == '\0')
+               goto out;
+
+       /* old style */
+       str_work = str;
+       val = simple_strtoul(str, &str_work, 16);
+
+       if (*str_work == '\0') {
+               if (val <= __MAX_SUBCHANNEL) {
+                       *devno = val;
+                       *ssid = 0;
+                       *cssid = 0;
+                       rc = 0;
                }
+               goto out;
+       }
 
-               /*
-                * Since we have to parse the proc commands and the
-                * kernel arguments we have to check four cases
-                */
-               if (strncmp(str,"all,",4) == 0 || strcmp(str,"all") == 0 ||
-                   strncmp(str,"all\n",4) == 0 || strncmp(str,"all ",4) == 0) {
-                       int j;
-
-                       str += 3;
-                       for (j=0; j <= __MAX_SSID; j++)
-                               blacklist_range(ra, 0, __MAX_SUBCHANNEL, j);
-               } else {
-                       int rc;
+       /* new style */
+       str_work = str;
+       ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID);
+       if (ret || (str_work[0] != '.'))
+               goto out;
+       str_work++;
+       ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID);
+       if (ret || (str_work[0] != '.'))
+               goto out;
+       str_work++;
+       ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL);
+       if (ret || (str_work[0] != '\0'))
+               goto out;
+
+       rc = 0;
+out:
+       if (rc && msgtrigger)
+               printk(KERN_WARNING "cio: Invalid cio_ignore device '%s'\n",
+                      str);
+
+       return rc;
+}
 
-                       rc = blacklist_busid(&str, &from_id0,
-                                            &from_ssid, &from);
-                       if (rc)
-                               continue;
-                       to = from;
-                       to_id0 = from_id0;
-                       to_ssid = from_ssid;
-                       if (*str == '-') {
-                               str++;
-                               rc = blacklist_busid(&str, &to_id0,
-                                                    &to_ssid, &to);
-                               if (rc)
-                                       continue;
-                       }
-                       if (*str == '-') {
-                               printk(KERN_WARNING "cio: invalid cio_ignore "
-                                       "parameter '%s'\n",
-                                       strsep(&str, ",\n"));
-                               continue;
-                       }
-                       if ((from_id0 != to_id0) ||
-                           (from_ssid != to_ssid)) {
-                               printk(KERN_WARNING "cio: invalid cio_ignore "
-                                      "range %x.%x.%04x-%x.%x.%04x\n",
-                                      from_id0, from_ssid, from,
-                                      to_id0, to_ssid, to);
-                               continue;
+static int blacklist_parse_parameters(char *str, range_action action,
+                                     int msgtrigger)
+{
+       int from_cssid, to_cssid, from_ssid, to_ssid, from, to;
+       int rc, totalrc;
+       char *parm;
+       range_action ra;
+
+       totalrc = 0;
+
+       while ((parm = strsep(&str, ","))) {
+               rc = 0;
+               ra = action;
+               if (*parm == '!') {
+                       if (ra == add)
+                               ra = free;
+                       else
+                               ra = add;
+                       parm++;
+               }
+               if (strcmp(parm, "all") == 0) {
+                       from_cssid = 0;
+                       from_ssid = 0;
+                       from = 0;
+                       to_cssid = __MAX_CSSID;
+                       to_ssid = __MAX_SSID;
+                       to = __MAX_SUBCHANNEL;
+               } else {
+                       rc = parse_busid(strsep(&parm, "-"), &from_cssid,
+                                        &from_ssid, &from, msgtrigger);
+                       if (!rc) {
+                               if (parm != NULL)
+                                       rc = parse_busid(parm, &to_cssid,
+                                                        &to_ssid, &to,
+                                                        msgtrigger);
+                               else {
+                                       to_cssid = from_cssid;
+                                       to_ssid = from_ssid;
+                                       to = from;
+                               }
                        }
-                       blacklist_range (ra, from, to, to_ssid);
                }
+               if (!rc) {
+                       rc = blacklist_range(ra, from_ssid, to_ssid, from, to,
+                                            msgtrigger);
+                       if (rc)
+                               totalrc = 1;
+               } else
+                       totalrc = 1;
        }
-       return 1;
+
+       return totalrc;
 }
 
-/* Parsing the commandline for blacklist parameters, e.g. to blacklist
- * bus ids 0.0.1234, 0.0.1235 and 0.0.1236, you could use any of:
- * - cio_ignore=1234-1236
- * - cio_ignore=0x1234-0x1235,1236
- * - cio_ignore=0x1234,1235-1236
- * - cio_ignore=1236 cio_ignore=1234-0x1236
- * - cio_ignore=1234 cio_ignore=1236 cio_ignore=0x1235
- * - cio_ignore=0.0.1234-0.0.1236
- * - cio_ignore=0.0.1234,0x1235,1236
- * - ...
- */
 static int __init
 blacklist_setup (char *str)
 {
-       return blacklist_parse_parameters (str, add);
+       CIO_MSG_EVENT(6, "Reading blacklist parameters\n");
+       if (blacklist_parse_parameters(str, add, 1))
+               return 0;
+       return 1;
 }
 
 __setup ("cio_ignore=", blacklist_setup);
@@ -223,27 +230,23 @@ is_blacklisted (int ssid, int devno)
  * Function: blacklist_parse_proc_parameters
  * parse the stuff which is piped to /proc/cio_ignore
  */
-static void
-blacklist_parse_proc_parameters (char *buf)
+static int blacklist_parse_proc_parameters(char *buf)
 {
-       if (strncmp (buf, "free ", 5) == 0) {
-               blacklist_parse_parameters (buf + 5, free);
-       } else if (strncmp (buf, "add ", 4) == 0) {
-               /* 
-                * We don't need to check for known devices since
-                * css_probe_device will handle this correctly. 
-                */
-               blacklist_parse_parameters (buf + 4, add);
-       } else {
-               printk (KERN_WARNING "cio: cio_ignore: Parse error; \n"
-                       KERN_WARNING "try using 'free all|<devno-range>,"
-                                    "<devno-range>,...'\n"
-                       KERN_WARNING "or 'add <devno-range>,"
-                                    "<devno-range>,...'\n");
-               return;
-       }
+       int rc;
+       char *parm;
+
+       parm = strsep(&buf, " ");
+
+       if (strcmp("free", parm) == 0)
+               rc = blacklist_parse_parameters(buf, free, 0);
+       else if (strcmp("add", parm) == 0)
+               rc = blacklist_parse_parameters(buf, add, 0);
+       else
+               return 1;
 
        css_schedule_reprobe();
+
+       return rc;
 }
 
 /* Iterator struct for all devices. */
@@ -327,6 +330,8 @@ cio_ignore_write(struct file *file, const char __user *user_buf,
                 size_t user_len, loff_t *offset)
 {
        char *buf;
+       size_t i;
+       ssize_t rc, ret;
 
        if (*offset)
                return -EINVAL;
@@ -335,16 +340,27 @@ cio_ignore_write(struct file *file, const char __user *user_buf,
        buf = vmalloc (user_len + 1); /* maybe better use the stack? */
        if (buf == NULL)
                return -ENOMEM;
+       memset(buf, 0, user_len + 1);
+
        if (strncpy_from_user (buf, user_buf, user_len) < 0) {
-               vfree (buf);
-               return -EFAULT;
+               rc = -EFAULT;
+               goto out_free;
        }
-       buf[user_len] = '\0';
 
-       blacklist_parse_proc_parameters (buf);
+       i = user_len - 1;
+       while ((i >= 0) && (isspace(buf[i]) || (buf[i] == 0))) {
+               buf[i] = '\0';
+               i--;
+       }
+       ret = blacklist_parse_proc_parameters(buf);
+       if (ret)
+               rc = -EINVAL;
+       else
+               rc = user_len;
 
+out_free:
        vfree (buf);
-       return user_len;
+       return rc;
 }
 
 static const struct seq_operations cio_ignore_proc_seq_ops = {