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#include <linux/init.h>
  13#include <linux/vmalloc.h>
  14#include <linux/slab.h>
  15#include <linux/proc_fs.h>
  16#include <linux/seq_file.h>
  17#include <linux/ctype.h>
  18#include <linux/device.h>
  19
  20#include <asm/cio.h>
  21#include <asm/uaccess.h>
  22
  23#include "blacklist.h"
  24#include "cio.h"
  25#include "cio_debug.h"
  26#include "css.h"
  27#include "device.h"
  28
  29/*
  30 * "Blacklisting" of certain devices:
  31 * Device numbers given in the commandline as cio_ignore=... won't be known
  32 * to Linux.
  33 *
  34 * These can be single devices or ranges of devices
  35 */
  36
  37/* 65536 bits for each set to indicate if a devno is blacklisted or not */
  38#define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \
  39                         (8*sizeof(long)))
  40static unsigned long bl_dev[__MAX_SSID + 1][__BL_DEV_WORDS];
  41typedef enum {add, free} range_action;
  42
  43/*
  44 * Function: blacklist_range
  45 * (Un-)blacklist the devices from-to
  46 */
  47static int blacklist_range(range_action action, unsigned int from_ssid,
  48                           unsigned int to_ssid, unsigned int from,
  49                           unsigned int to, int msgtrigger)
  50{
  51        if ((from_ssid > to_ssid) || ((from_ssid == to_ssid) && (from > to))) {
  52                if (msgtrigger)
  53                        printk(KERN_WARNING "cio: Invalid cio_ignore range "
  54                               "0.%x.%04x-0.%x.%04x\n", from_ssid, from,
  55                               to_ssid, to);
  56                return 1;
  57        }
  58
  59        while ((from_ssid < to_ssid) || ((from_ssid == to_ssid) &&
  60               (from <= to))) {
  61                if (action == add)
  62                        set_bit(from, bl_dev[from_ssid]);
  63                else
  64                        clear_bit(from, bl_dev[from_ssid]);
  65                from++;
  66                if (from > __MAX_SUBCHANNEL) {
  67                        from_ssid++;
  68                        from = 0;
  69                }
  70        }
  71
  72        return 0;
  73}
  74
  75static int pure_hex(char **cp, unsigned int *val, int min_digit,
  76                    int max_digit, int max_val)
  77{
  78        int diff;
  79        unsigned int value;
  80
  81        diff = 0;
  82        *val = 0;
  83
  84        while (isxdigit(**cp) && (diff <= max_digit)) {
  85
  86                if (isdigit(**cp))
  87                        value = **cp - '0';
  88                else
  89                        value = tolower(**cp) - 'a' + 10;
  90                *val = *val * 16 + value;
  91                (*cp)++;
  92                diff++;
  93        }
  94
  95        if ((diff < min_digit) || (diff > max_digit) || (*val > max_val))
  96                return 1;
  97
  98        return 0;
  99}
 100
 101static int parse_busid(char *str, unsigned int *cssid, unsigned int *ssid,
 102                       unsigned int *devno, int msgtrigger)
 103{
 104        char *str_work;
 105        int val, rc, ret;
 106
 107        rc = 1;
 108
 109        if (*str == '\0')
 110                goto out;
 111
 112        /* old style */
 113        str_work = str;
 114        val = simple_strtoul(str, &str_work, 16);
 115
 116        if (*str_work == '\0') {
 117                if (val <= __MAX_SUBCHANNEL) {
 118                        *devno = val;
 119                        *ssid = 0;
 120                        *cssid = 0;
 121                        rc = 0;
 122                }
 123                goto out;
 124        }
 125
 126        /* new style */
 127        str_work = str;
 128        ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID);
 129        if (ret || (str_work[0] != '.'))
 130                goto out;
 131        str_work++;
 132        ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID);
 133        if (ret || (str_work[0] != '.'))
 134                goto out;
 135        str_work++;
 136        ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL);
 137        if (ret || (str_work[0] != '\0'))
 138                goto out;
 139
 140        rc = 0;
 141out:
 142        if (rc && msgtrigger)
 143                printk(KERN_WARNING "cio: Invalid cio_ignore device '%s'\n",
 144                       str);
 145
 146        return rc;
 147}
 148
 149static int blacklist_parse_parameters(char *str, range_action action,
 150                                      int msgtrigger)
 151{
 152        unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to;
 153        int rc, totalrc;
 154        char *parm;
 155        range_action ra;
 156
 157        totalrc = 0;
 158
 159        while ((parm = strsep(&str, ","))) {
 160                rc = 0;
 161                ra = action;
 162                if (*parm == '!') {
 163                        if (ra == add)
 164                                ra = free;
 165                        else
 166                                ra = add;
 167                        parm++;
 168                }
 169                if (strcmp(parm, "all") == 0) {
 170                        from_cssid = 0;
 171                        from_ssid = 0;
 172                        from = 0;
 173                        to_cssid = __MAX_CSSID;
 174                        to_ssid = __MAX_SSID;
 175                        to = __MAX_SUBCHANNEL;
 176                } else {
 177                        rc = parse_busid(strsep(&parm, "-"), &from_cssid,
 178                                         &from_ssid, &from, msgtrigger);
 179                        if (!rc) {
 180                                if (parm != NULL)
 181                                        rc = parse_busid(parm, &to_cssid,
 182                                                         &to_ssid, &to,
 183                                                         msgtrigger);
 184                                else {
 185                                        to_cssid = from_cssid;
 186                                        to_ssid = from_ssid;
 187                                        to = from;
 188                                }
 189                        }
 190                }
 191                if (!rc) {
 192                        rc = blacklist_range(ra, from_ssid, to_ssid, from, to,
 193                                             msgtrigger);
 194                        if (rc)
 195                                totalrc = -EINVAL;
 196                } else
 197                        totalrc = -EINVAL;
 198        }
 199
 200        return totalrc;
 201}
 202
 203static int __init
 204blacklist_setup (char *str)
 205{
 206        CIO_MSG_EVENT(6, "Reading blacklist parameters\n");
 207        if (blacklist_parse_parameters(str, add, 1))
 208                return 0;
 209        return 1;
 210}
 211
 212__setup ("cio_ignore=", blacklist_setup);
 213
 214/* Checking if devices are blacklisted */
 215
 216/*
 217 * Function: is_blacklisted
 218 * Returns 1 if the given devicenumber can be found in the blacklist,
 219 * otherwise 0.
 220 * Used by validate_subchannel()
 221 */
 222int
 223is_blacklisted (int ssid, int devno)
 224{
 225        return test_bit (devno, bl_dev[ssid]);
 226}
 227
 228#ifdef CONFIG_PROC_FS
 229/*
 230 * Function: blacklist_parse_proc_parameters
 231 * parse the stuff which is piped to /proc/cio_ignore
 232 */
 233static int blacklist_parse_proc_parameters(char *buf)
 234{
 235        int rc;
 236        char *parm;
 237
 238        parm = strsep(&buf, " ");
 239
 240        if (strcmp("free", parm) == 0)
 241                rc = blacklist_parse_parameters(buf, free, 0);
 242        else if (strcmp("add", parm) == 0)
 243                rc = blacklist_parse_parameters(buf, add, 0);
 244        else if (strcmp("purge", parm) == 0)
 245                return ccw_purge_blacklisted();
 246        else
 247                return -EINVAL;
 248
 249        css_schedule_reprobe();
 250
 251        return rc;
 252}
 253
 254/* Iterator struct for all devices. */
 255struct ccwdev_iter {
 256        int devno;
 257        int ssid;
 258        int in_range;
 259};
 260
 261static void *
 262cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset)
 263{
 264        struct ccwdev_iter *iter;
 265
 266        if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
 267                return NULL;
 268        iter = kzalloc(sizeof(struct ccwdev_iter), GFP_KERNEL);
 269        if (!iter)
 270                return ERR_PTR(-ENOMEM);
 271        iter->ssid = *offset / (__MAX_SUBCHANNEL + 1);
 272        iter->devno = *offset % (__MAX_SUBCHANNEL + 1);
 273        return iter;
 274}
 275
 276static void
 277cio_ignore_proc_seq_stop(struct seq_file *s, void *it)
 278{
 279        if (!IS_ERR(it))
 280                kfree(it);
 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        size_t i;
 336        ssize_t rc, ret;
 337
 338        if (*offset)
 339                return -EINVAL;
 340        if (user_len > 65536)
 341                user_len = 65536;
 342        buf = vmalloc (user_len + 1); /* maybe better use the stack? */
 343        if (buf == NULL)
 344                return -ENOMEM;
 345        memset(buf, 0, user_len + 1);
 346
 347        if (strncpy_from_user (buf, user_buf, user_len) < 0) {
 348                rc = -EFAULT;
 349                goto out_free;
 350        }
 351
 352        i = user_len - 1;
 353        while ((i >= 0) && (isspace(buf[i]) || (buf[i] == 0))) {
 354                buf[i] = '\0';
 355                i--;
 356        }
 357        ret = blacklist_parse_proc_parameters(buf);
 358        if (ret)
 359                rc = ret;
 360        else
 361                rc = user_len;
 362
 363out_free:
 364        vfree (buf);
 365        return rc;
 366}
 367
 368static const struct seq_operations cio_ignore_proc_seq_ops = {
 369        .start = cio_ignore_proc_seq_start,
 370        .stop  = cio_ignore_proc_seq_stop,
 371        .next  = cio_ignore_proc_seq_next,
 372        .show  = cio_ignore_proc_seq_show,
 373};
 374
 375static int
 376cio_ignore_proc_open(struct inode *inode, struct file *file)
 377{
 378        return seq_open(file, &cio_ignore_proc_seq_ops);
 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,
 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.