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