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))) 39 static unsigned long bl_dev[__MAX_SSID + 1][__BL_DEV_WORDS]; 40 typedef enum {add, free} range_action; 41 42 /* 43 * Function: blacklist_range 44 * (Un-)blacklist the devices from-to 45 */ 46 static inline void 47 blacklist_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 */ 72 static inline int 73 blacklist_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; 120 confused: 121 strsep(str, ",\n"); 122 printk(KERN_WARNING "Invalid cio_ignore parameter '%s'\n", sav); 123 return 1; 124 } 125 126 static inline int 127 blacklist_parse_parameters (char *str, range_action action) 128 { 129 unsigned 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 */ 202 static int __init 203 blacklist_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 */ 219 int 220 is_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 */ 230 static inline void 231 blacklist_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. */ 254 struct ccwdev_iter { 255 int devno; 256 int ssid; 257 int in_range; 258 }; 259 260 static void * 261 cio_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 275 static void 276 cio_ignore_proc_seq_stop(struct seq_file *s, void *it) 277 { 278 if (!IS_ERR(it)) 279 kfree(it); 280 } 281 282 static void * 283 cio_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 301 static int 302 cio_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 329 static ssize_t 330 cio_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 354 static 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 361 static int 362 cio_ignore_proc_open(struct inode *inode, struct file *file) 363 { 364 return seq_open(file, &cio_ignore_proc_seq_ops); 365 } 366 367 static 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 375 static int 376 cio_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