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 28/* 29 * "Blacklisting" of certain devices: 30 * Device numbers given in the commandline as cio_ignore=... won't be known 31 * to Linux. 32 * 33 * These can be single devices or ranges of devices 34 */ 35 36/* 65536 bits for each set to indicate if a devno is blacklisted or not */ 37#define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \ 38 (8*sizeof(long))) 39static unsigned long bl_dev[__MAX_SSID + 1][__BL_DEV_WORDS]; 40typedef enum {add, free} range_action; 41 42/* 43 * Function: blacklist_range 44 * (Un-)blacklist the devices from-to 45 */ 46static void 47blacklist_range (range_action action, unsigned int from, unsigned int to, 48 unsigned int ssid) 49{ 50 if (!to) 51 to = from; 52 53 if (from > to || to > __MAX_SUBCHANNEL || ssid > __MAX_SSID) { 54 printk (KERN_WARNING "Invalid blacklist range " 55 "0.%x.%04x to 0.%x.%04x, skipping\n", 56 ssid, from, ssid, to); 57 return; 58 } 59 for (; from <= to; from++) { 60 if (action == add) 61 set_bit (from, bl_dev[ssid]); 62 else 63 clear_bit (from, bl_dev[ssid]); 64 } 65} 66 67/* 68 * Function: blacklist_busid 69 * Get devno/busid from given string. 70 * Shamelessly grabbed from dasd_devmap.c. 71 */ 72static int 73blacklist_busid(char **str, int *id0, int *ssid, int *devno) 74{ 75 int val, old_style; 76 char *sav; 77 78 sav = *str; 79 80 /* check for leading '0x' */ 81 old_style = 0; 82 if ((*str)[0] == '0' && (*str)[1] == 'x') { 83 *str += 2; 84 old_style = 1; 85 } 86 if (!isxdigit((*str)[0])) /* We require at least one hex digit */ 87 goto confused; 88 val = simple_strtoul(*str, str, 16); 89 if (old_style || (*str)[0] != '.') { 90 *id0 = *ssid = 0; 91 if (val < 0 || val > 0xffff) 92 goto confused; 93 *devno = val; 94 if ((*str)[0] != ',' && (*str)[0] != '-' && 95 (*str)[0] != '\n' && (*str)[0] != '\0') 96 goto confused; 97 return 0; 98 } 99 /* New style x.y.z busid */ 100 if (val < 0 || val > 0xff) 101 goto confused; 102 *id0 = val; 103 (*str)++; 104 if (!isxdigit((*str)[0])) /* We require at least one hex digit */ 105 goto confused; 106 val = simple_strtoul(*str, str, 16); 107 if (val < 0 || val > 0xff || (*str)++[0] != '.') 108 goto confused; 109 *ssid = val; 110 if (!isxdigit((*str)[0])) /* We require at least one hex digit */ 111 goto confused; 112 val = simple_strtoul(*str, str, 16); 113 if (val < 0 || val > 0xffff) 114 goto confused; 115 *devno = val; 116 if ((*str)[0] != ',' && (*str)[0] != '-' && 117 (*str)[0] != '\n' && (*str)[0] != '\0') 118 goto confused; 119 return 0; 120confused: 121 strsep(str, ",\n"); 122 printk(KERN_WARNING "Invalid cio_ignore parameter '%s'\n", sav); 123 return 1; 124} 125 126static int 127blacklist_parse_parameters (char *str, range_action action) 128{ 129 int from, to, from_id0, to_id0, from_ssid, to_ssid; 130 131 while (*str != 0 && *str != '\n') { 132 range_action ra = action; 133 while(*str == ',') 134 str++; 135 if (*str == '!') { 136 ra = !action; 137 ++str; 138 } 139 140 /* 141 * Since we have to parse the proc commands and the 142 * kernel arguments we have to check four cases 143 */ 144 if (strncmp(str,"all,",4) == 0 || strcmp(str,"all") == 0 || 145 strncmp(str,"all\n",4) == 0 || strncmp(str,"all ",4) == 0) { 146 int j; 147 148 str += 3; 149 for (j=0; j <= __MAX_SSID; j++) 150 blacklist_range(ra, 0, __MAX_SUBCHANNEL, j); 151 } else { 152 int rc; 153 154 rc = blacklist_busid(&str, &from_id0, 155 &from_ssid, &from); 156 if (rc) 157 continue; 158 to = from; 159 to_id0 = from_id0; 160 to_ssid = from_ssid; 161 if (*str == '-') { 162 str++; 163 rc = blacklist_busid(&str, &to_id0, 164 &to_ssid, &to); 165 if (rc) 166 continue; 167 } 168 if (*str == '-') { 169 printk(KERN_WARNING "invalid cio_ignore " 170 "parameter '%s'\n", 171 strsep(&str, ",\n")); 172 continue; 173 } 174 if ((from_id0 != to_id0) || 175 (from_ssid != to_ssid)) { 176 printk(KERN_WARNING "invalid cio_ignore range " 177 "%x.%x.%04x-%x.%x.%04x\n", 178 from_id0, from_ssid, from, 179 to_id0, to_ssid, to); 180 continue; 181 } 182 pr_debug("blacklist_setup: adding range " 183 "from %x.%x.%04x to %x.%x.%04x\n", 184 from_id0, from_ssid, from, to_id0, to_ssid, to); 185 blacklist_range (ra, from, to, to_ssid); 186 } 187 } 188 return 1; 189} 190 191/* Parsing the commandline for blacklist parameters, e.g. to blacklist 192 * bus ids 0.0.1234, 0.0.1235 and 0.0.1236, you could use any of: 193 * - cio_ignore=1234-1236 194 * - cio_ignore=0x1234-0x1235,1236 195 * - cio_ignore=0x1234,1235-1236 196 * - cio_ignore=1236 cio_ignore=1234-0x1236 197 * - cio_ignore=1234 cio_ignore=1236 cio_ignore=0x1235 198 * - cio_ignore=0.0.1234-0.0.1236 199 * - cio_ignore=0.0.1234,0x1235,1236 200 * - ... 201 */ 202static int __init 203blacklist_setup (char *str) 204{ 205 CIO_MSG_EVENT(6, "Reading blacklist parameters\n"); 206 return blacklist_parse_parameters (str, add); 207} 208 209__setup ("cio_ignore=", blacklist_setup); 210 211/* Checking if devices are blacklisted */ 212 213/* 214 * Function: is_blacklisted 215 * Returns 1 if the given devicenumber can be found in the blacklist, 216 * otherwise 0. 217 * Used by validate_subchannel() 218 */ 219int 220is_blacklisted (int ssid, int devno) 221{ 222 return test_bit (devno, bl_dev[ssid]); 223} 224 225#ifdef CONFIG_PROC_FS 226/* 227 * Function: blacklist_parse_proc_parameters 228 * parse the stuff which is piped to /proc/cio_ignore 229 */ 230static void 231blacklist_parse_proc_parameters (char *buf) 232{ 233 if (strncmp (buf, "free ", 5) == 0) { 234 blacklist_parse_parameters (buf + 5, free); 235 } else if (strncmp (buf, "add ", 4) == 0) { 236 /* 237 * We don't need to check for known devices since 238 * css_probe_device will handle this correctly. 239 */ 240 blacklist_parse_parameters (buf + 4, add); 241 } else { 242 printk (KERN_WARNING "cio_ignore: Parse error; \n" 243 KERN_WARNING "try using 'free all|<devno-range>," 244 "<devno-range>,...'\n" 245 KERN_WARNING "or 'add <devno-range>," 246 "<devno-range>,...'\n"); 247 return; 248 } 249 250 css_schedule_reprobe(); 251} 252 253/* Iterator struct for all devices. */ 254struct ccwdev_iter { 255 int devno; 256 int ssid; 257 int in_range; 258}; 259 260static void * 261cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset) 262{ 263 struct ccwdev_iter *iter; 264 265 if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1)) 266 return NULL; 267 iter = kzalloc(sizeof(struct ccwdev_iter), GFP_KERNEL); 268 if (!iter) 269 return ERR_PTR(-ENOMEM); 270 iter->ssid = *offset / (__MAX_SUBCHANNEL + 1); 271 iter->devno = *offset % (__MAX_SUBCHANNEL + 1); 272 return iter; 273} 274 275static void 276cio_ignore_proc_seq_stop(struct seq_file *s, void *it) 277{ 278 if (!IS_ERR(it)) 279 kfree(it); 280} 281 282static void * 283cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset) 284{ 285 struct ccwdev_iter *iter; 286 287 if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1)) 288 return NULL; 289 iter = it; 290 if (iter->devno == __MAX_SUBCHANNEL) { 291 iter->devno = 0; 292 iter->ssid++; 293 if (iter->ssid > __MAX_SSID) 294 return NULL; 295 } else 296 iter->devno++; 297 (*offset)++; 298 return iter; 299} 300 301static int 302cio_ignore_proc_seq_show(struct seq_file *s, void *it) 303{ 304 struct ccwdev_iter *iter; 305 306 iter = it; 307 if (!is_blacklisted(iter->ssid, iter->devno)) 308 /* Not blacklisted, nothing to output. */ 309 return 0; 310 if (!iter->in_range) { 311 /* First device in range. */ 312 if ((iter->devno == __MAX_SUBCHANNEL) || 313 !is_blacklisted(iter->ssid, iter->devno + 1)) 314 /* Singular device. */ 315 return seq_printf(s, "0.%x.%04x\n", 316 iter->ssid, iter->devno); 317 iter->in_range = 1; 318 return seq_printf(s, "0.%x.%04x-", iter->ssid, iter->devno); 319 } 320 if ((iter->devno == __MAX_SUBCHANNEL) || 321 !is_blacklisted(iter->ssid, iter->devno + 1)) { 322 /* Last device in range. */ 323 iter->in_range = 0; 324 return seq_printf(s, "0.%x.%04x\n", iter->ssid, iter->devno); 325 } 326 return 0; 327} 328 329static ssize_t 330cio_ignore_write(struct file *file, const char __user *user_buf, 331 size_t user_len, loff_t *offset) 332{ 333 char *buf; 334 335 if (*offset) 336 return -EINVAL; 337 if (user_len > 65536) 338 user_len = 65536; 339 buf = vmalloc (user_len + 1); /* maybe better use the stack? */ 340 if (buf == NULL) 341 return -ENOMEM; 342 if (strncpy_from_user (buf, user_buf, user_len) < 0) { 343 vfree (buf); 344 return -EFAULT; 345 } 346 buf[user_len] = '\0'; 347 348 blacklist_parse_proc_parameters (buf); 349 350 vfree (buf); 351 return user_len; 352} 353 354static struct seq_operations cio_ignore_proc_seq_ops = { 355 .start = cio_ignore_proc_seq_start, 356 .stop = cio_ignore_proc_seq_stop, 357 .next = cio_ignore_proc_seq_next, 358 .show = cio_ignore_proc_seq_show, 359}; 360 361static int 362cio_ignore_proc_open(struct inode *inode, struct file *file) 363{ 364 return seq_open(file, &cio_ignore_proc_seq_ops); 365} 366 367static const struct file_operations cio_ignore_proc_fops = { 368 .open = cio_ignore_proc_open, 369 .read = seq_read, 370 .llseek = seq_lseek, 371 .release = seq_release, 372 .write = cio_ignore_write, 373}; 374 375static int 376cio_ignore_proc_init (void) 377{ 378 struct proc_dir_entry *entry; 379 380 entry = create_proc_entry ("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR, 381 &proc_root); 382 if (!entry) 383 return -ENOENT; 384 385 entry->proc_fops = &cio_ignore_proc_fops; 386 387 return 0; 388} 389 390__initcall (cio_ignore_proc_init); 391 392#endif /* CONFIG_PROC_FS */ 393