linux/drivers/s390/cio/blacklist.c
<<
>>
Prefs
   1/*
   2 *  drivers/s390/cio/blacklist.c
   3 *   S/390 common I/O routines -- blacklisting of specific devices
   4 *
   5 *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
   6 *                            IBM Corporation
   7 *    Author(s): Ingo Adlung (adlung@de.ibm.com)
   8 *               Cornelia Huck (cornelia.huck@de.ibm.com)
   9 *               Arnd Bergmann (arndb@de.ibm.com)
  10 */
  11
  12#define KMSG_COMPONENT "cio"
  13#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
  14
  15#include <linux/init.h>
  16#include <linux/vmalloc.h>
  17#include <linux/slab.h>
  18#include <linux/proc_fs.h>
  19#include <linux/seq_file.h>
  20#include <linux/ctype.h>
  21#include <linux/device.h>
  22
  23#include <asm/cio.h>
  24#include <asm/uaccess.h>
  25
  26#include "blacklist.h"
  27#include "cio.h"
  28#include "cio_debug.h"
  29#include "css.h"
  30#include "device.h"
  31
  32/*
  33 * "Blacklisting" of certain devices:
  34 * Device numbers given in the commandline as cio_ignore=... won't be known
  35 * to Linux.
  36 *
  37 * These can be single devices or ranges of devices
  38 */
  39
  40/* 65536 bits for each set to indicate if a devno is blacklisted or not */
  41#define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \
  42                         (8*sizeof(long)))
  43static unsigned long bl_dev[__MAX_SSID + 1][__BL_DEV_WORDS];
  44typedef enum {add, free} range_action;
  45
  46/*
  47 * Function: blacklist_range
  48 * (Un-)blacklist the devices from-to
  49 */
  50static int blacklist_range(range_action action, unsigned int from_ssid,
  51                           unsigned int to_ssid, unsigned int from,
  52                           unsigned int to, int msgtrigger)
  53{
  54        if ((from_ssid > to_ssid) || ((from_ssid == to_ssid) && (from > to))) {
  55                if (msgtrigger)
  56                        pr_warning("0.%x.%04x to 0.%x.%04x is not a valid "
  57                                   "range for cio_ignore\n", from_ssid, from,
  58                                   to_ssid, to);
  59
  60                return 1;
  61        }
  62
  63        while ((from_ssid < to_ssid) || ((from_ssid == to_ssid) &&
  64               (from <= to))) {
  65                if (action == add)
  66                        set_bit(from, bl_dev[from_ssid]);
  67                else
  68                        clear_bit(from, bl_dev[from_ssid]);
  69                from++;
  70                if (from > __MAX_SUBCHANNEL) {
  71                        from_ssid++;
  72                        from = 0;
  73                }
  74        }
  75
  76        return 0;
  77}
  78
  79static int pure_hex(char **cp, unsigned int *val, int min_digit,
  80                    int max_digit, int max_val)
  81{
  82        int diff;
  83        unsigned int value;
  84
  85        diff = 0;
  86        *val = 0;
  87
  88        while (isxdigit(**cp) && (diff <= max_digit)) {
  89
  90                if (isdigit(**cp))
  91                        value = **cp - '0';
  92                else
  93                        value = tolower(**cp) - 'a' + 10;
  94                *val = *val * 16 + value;
  95                (*cp)++;
  96                diff++;
  97        }
  98
  99        if ((diff < min_digit) || (diff > max_digit) || (*val > max_val))
 100                return 1;
 101
 102        return 0;
 103}
 104
 105static int parse_busid(char *str, unsigned int *cssid, unsigned int *ssid,
 106                       unsigned int *devno, int msgtrigger)
 107{
 108        char *str_work;
 109        int val, rc, ret;
 110
 111        rc = 1;
 112
 113        if (*str == '\0')
 114                goto out;
 115
 116        /* old style */
 117        str_work = str;
 118        val = simple_strtoul(str, &str_work, 16);
 119
 120        if (*str_work == '\0') {
 121                if (val <= __MAX_SUBCHANNEL) {
 122                        *devno = val;
 123                        *ssid = 0;
 124                        *cssid = 0;
 125                        rc = 0;
 126                }
 127                goto out;
 128        }
 129
 130        /* new style */
 131        str_work = str;
 132        ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID);
 133        if (ret || (str_work[0] != '.'))
 134                goto out;
 135        str_work++;
 136        ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID);
 137        if (ret || (str_work[0] != '.'))
 138                goto out;
 139        str_work++;
 140        ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL);
 141        if (ret || (str_work[0] != '\0'))
 142                goto out;
 143
 144        rc = 0;
 145out:
 146        if (rc && msgtrigger)
 147                pr_warning("%s is not a valid device for the cio_ignore "
 148                           "kernel parameter\n", str);
 149
 150        return rc;
 151}
 152
 153static int blacklist_parse_parameters(char *str, range_action action,
 154                                      int msgtrigger)
 155{
 156        unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to;
 157        int rc, totalrc;
 158        char *parm;
 159        range_action ra;
 160
 161        totalrc = 0;
 162
 163        while ((parm = strsep(&str, ","))) {
 164                rc = 0;
 165                ra = action;
 166                if (*parm == '!') {
 167                        if (ra == add)
 168                                ra = free;
 169                        else
 170                                ra = add;
 171                        parm++;
 172                }
 173                if (strcmp(parm, "all") == 0) {
 174                        from_cssid = 0;
 175                        from_ssid = 0;
 176                        from = 0;
 177                        to_cssid = __MAX_CSSID;
 178                        to_ssid = __MAX_SSID;
 179                        to = __MAX_SUBCHANNEL;
 180                } else {
 181                        rc = parse_busid(strsep(&parm, "-"), &from_cssid,
 182                                         &from_ssid, &from, msgtrigger);
 183                        if (!rc) {
 184                                if (parm != NULL)
 185                                        rc = parse_busid(parm, &to_cssid,
 186                                                         &to_ssid, &to,
 187                                                         msgtrigger);
 188                                else {
 189                                        to_cssid = from_cssid;
 190                                        to_ssid = from_ssid;
 191                                        to = from;
 192                                }
 193                        }
 194                }
 195                if (!rc) {
 196                        rc = blacklist_range(ra, from_ssid, to_ssid, from, to,
 197                                             msgtrigger);
 198                        if (rc)
 199                                totalrc = -EINVAL;
 200                } else
 201                        totalrc = -EINVAL;
 202        }
 203
 204        return totalrc;
 205}
 206
 207static int __init
 208blacklist_setup (char *str)
 209{
 210        CIO_MSG_EVENT(6, "Reading blacklist parameters\n");
 211        if (blacklist_parse_parameters(str, add, 1))
 212                return 0;
 213        return 1;
 214}
 215
 216__setup ("cio_ignore=", blacklist_setup);
 217
 218/* Checking if devices are blacklisted */
 219
 220/*
 221 * Function: is_blacklisted
 222 * Returns 1 if the given devicenumber can be found in the blacklist,
 223 * otherwise 0.
 224 * Used by validate_subchannel()
 225 */
 226int
 227is_blacklisted (int ssid, int devno)
 228{
 229        return test_bit (devno, bl_dev[ssid]);
 230}
 231
 232#ifdef CONFIG_PROC_FS
 233/*
 234 * Function: blacklist_parse_proc_parameters
 235 * parse the stuff which is piped to /proc/cio_ignore
 236 */
 237static int blacklist_parse_proc_parameters(char *buf)
 238{
 239        int rc;
 240        char *parm;
 241
 242        parm = strsep(&buf, " ");
 243
 244        if (strcmp("free", parm) == 0)
 245                rc = blacklist_parse_parameters(buf, free, 0);
 246        else if (strcmp("add", parm) == 0)
 247                rc = blacklist_parse_parameters(buf, add, 0);
 248        else if (strcmp("purge", parm) == 0)
 249                return ccw_purge_blacklisted();
 250        else
 251                return -EINVAL;
 252
 253        css_schedule_reprobe();
 254
 255        return rc;
 256}
 257
 258/* Iterator struct for all devices. */
 259struct ccwdev_iter {
 260        int devno;
 261        int ssid;
 262        int in_range;
 263};
 264
 265static void *
 266cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset)
 267{
 268        struct ccwdev_iter *iter = s->private;
 269
 270        if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
 271                return NULL;
 272        memset(iter, 0, sizeof(*iter));
 273        iter->ssid = *offset / (__MAX_SUBCHANNEL + 1);
 274        iter->devno = *offset % (__MAX_SUBCHANNEL + 1);
 275        return iter;
 276}
 277
 278static void
 279cio_ignore_proc_seq_stop(struct seq_file *s, void *it)
 280{
 281}
 282
 283static void *
 284cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset)
 285{
 286        struct ccwdev_iter *iter;
 287
 288        if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
 289                return NULL;
 290        iter = it;
 291        if (iter->devno == __MAX_SUBCHANNEL) {
 292                iter->devno = 0;
 293                iter->ssid++;
 294                if (iter->ssid > __MAX_SSID)
 295                        return NULL;
 296        } else
 297                iter->devno++;
 298        (*offset)++;
 299        return iter;
 300}
 301
 302static int
 303cio_ignore_proc_seq_show(struct seq_file *s, void *it)
 304{
 305        struct ccwdev_iter *iter;
 306
 307        iter = it;
 308        if (!is_blacklisted(iter->ssid, iter->devno))
 309                /* Not blacklisted, nothing to output. */
 310                return 0;
 311        if (!iter->in_range) {
 312                /* First device in range. */
 313                if ((iter->devno == __MAX_SUBCHANNEL) ||
 314                    !is_blacklisted(iter->ssid, iter->devno + 1))
 315                        /* Singular device. */
 316                        return seq_printf(s, "0.%x.%04x\n",
 317                                          iter->ssid, iter->devno);
 318                iter->in_range = 1;
 319                return seq_printf(s, "0.%x.%04x-", iter->ssid, iter->devno);
 320        }
 321        if ((iter->devno == __MAX_SUBCHANNEL) ||
 322            !is_blacklisted(iter->ssid, iter->devno + 1)) {
 323                /* Last device in range. */
 324                iter->in_range = 0;
 325                return seq_printf(s, "0.%x.%04x\n", iter->ssid, iter->devno);
 326        }
 327        return 0;
 328}
 329
 330static ssize_t
 331cio_ignore_write(struct file *file, const char __user *user_buf,
 332                 size_t user_len, loff_t *offset)
 333{
 334        char *buf;
 335        ssize_t rc, ret, i;
 336
 337        if (*offset)
 338                return -EINVAL;
 339        if (user_len > 65536)
 340                user_len = 65536;
 341        buf = vmalloc (user_len + 1); /* maybe better use the stack? */
 342        if (buf == NULL)
 343                return -ENOMEM;
 344        memset(buf, 0, user_len + 1);
 345
 346        if (strncpy_from_user (buf, user_buf, user_len) < 0) {
 347                rc = -EFAULT;
 348                goto out_free;
 349        }
 350
 351        i = user_len - 1;
 352        while ((i >= 0) && (isspace(buf[i]) || (buf[i] == 0))) {
 353                buf[i] = '\0';
 354                i--;
 355        }
 356        ret = blacklist_parse_proc_parameters(buf);
 357        if (ret)
 358                rc = ret;
 359        else
 360                rc = user_len;
 361
 362out_free:
 363        vfree (buf);
 364        return rc;
 365}
 366
 367static const struct seq_operations cio_ignore_proc_seq_ops = {
 368        .start = cio_ignore_proc_seq_start,
 369        .stop  = cio_ignore_proc_seq_stop,
 370        .next  = cio_ignore_proc_seq_next,
 371        .show  = cio_ignore_proc_seq_show,
 372};
 373
 374static int
 375cio_ignore_proc_open(struct inode *inode, struct file *file)
 376{
 377        return seq_open_private(file, &cio_ignore_proc_seq_ops,
 378                                sizeof(struct ccwdev_iter));
 379}
 380
 381static const struct file_operations cio_ignore_proc_fops = {
 382        .open    = cio_ignore_proc_open,
 383        .read    = seq_read,
 384        .llseek  = seq_lseek,
 385        .release = seq_release_private,
 386        .write   = cio_ignore_write,
 387};
 388
 389static int
 390cio_ignore_proc_init (void)
 391{
 392        struct proc_dir_entry *entry;
 393
 394        entry = proc_create("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR, NULL,
 395                            &cio_ignore_proc_fops);
 396        if (!entry)
 397                return -ENOENT;
 398        return 0;
 399}
 400
 401__initcall (cio_ignore_proc_init);
 402
 403#endif /* CONFIG_PROC_FS */
 404