xref: /linux/drivers/s390/block/dasd_devmap.c (revision 2624f124b3b5d550ab2fbef7ee3bc0e1fed09722)
1 /*
2  * File...........: linux/drivers/s390/block/dasd_devmap.c
3  * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
4  *		    Horst Hummel <Horst.Hummel@de.ibm.com>
5  *		    Carsten Otte <Cotte@de.ibm.com>
6  *		    Martin Schwidefsky <schwidefsky@de.ibm.com>
7  * Bugreports.to..: <Linux390@de.ibm.com>
8  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
9  *
10  * Device mapping and dasd= parameter parsing functions. All devmap
11  * functions may not be called from interrupt context. In particular
12  * dasd_get_device is a no-no from interrupt context.
13  *
14  * $Revision: 1.43 $
15  */
16 
17 #include <linux/config.h>
18 #include <linux/ctype.h>
19 #include <linux/init.h>
20 
21 #include <asm/debug.h>
22 #include <asm/uaccess.h>
23 
24 /* This is ugly... */
25 #define PRINTK_HEADER "dasd_devmap:"
26 
27 #include "dasd_int.h"
28 
29 kmem_cache_t *dasd_page_cache;
30 EXPORT_SYMBOL(dasd_page_cache);
31 
32 /*
33  * dasd_devmap_t is used to store the features and the relation
34  * between device number and device index. To find a dasd_devmap_t
35  * that corresponds to a device number of a device index each
36  * dasd_devmap_t is added to two linked lists, one to search by
37  * the device number and one to search by the device index. As
38  * soon as big minor numbers are available the device index list
39  * can be removed since the device number will then be identical
40  * to the device index.
41  */
42 struct dasd_devmap {
43 	struct list_head list;
44 	char bus_id[BUS_ID_SIZE];
45         unsigned int devindex;
46         unsigned short features;
47 	struct dasd_device *device;
48 };
49 
50 /*
51  * Parameter parsing functions for dasd= parameter. The syntax is:
52  *   <devno>		: (0x)?[0-9a-fA-F]+
53  *   <busid>		: [0-0a-f]\.[0-9a-f]\.(0x)?[0-9a-fA-F]+
54  *   <feature>		: ro
55  *   <feature_list>	: \(<feature>(:<feature>)*\)
56  *   <devno-range>	: <devno>(-<devno>)?<feature_list>?
57  *   <busid-range>	: <busid>(-<busid>)?<feature_list>?
58  *   <devices>		: <devno-range>|<busid-range>
59  *   <dasd_module>	: dasd_diag_mod|dasd_eckd_mod|dasd_fba_mod
60  *
61  *   <dasd>		: autodetect|probeonly|<devices>(,<devices>)*
62  */
63 
64 int dasd_probeonly =  0;	/* is true, when probeonly mode is active */
65 int dasd_autodetect = 0;	/* is true, when autodetection is active */
66 
67 /*
68  * char *dasd[] is intended to hold the ranges supplied by the dasd= statement
69  * it is named 'dasd' to directly be filled by insmod with the comma separated
70  * strings when running as a module.
71  */
72 static char *dasd[256];
73 /*
74  * Single spinlock to protect devmap structures and lists.
75  */
76 static DEFINE_SPINLOCK(dasd_devmap_lock);
77 
78 /*
79  * Hash lists for devmap structures.
80  */
81 static struct list_head dasd_hashlists[256];
82 int dasd_max_devindex;
83 
84 static struct dasd_devmap *dasd_add_busid(char *, int);
85 
86 static inline int
87 dasd_hash_busid(char *bus_id)
88 {
89 	int hash, i;
90 
91 	hash = 0;
92 	for (i = 0; (i < BUS_ID_SIZE) && *bus_id; i++, bus_id++)
93 		hash += *bus_id;
94 	return hash & 0xff;
95 }
96 
97 #ifndef MODULE
98 /*
99  * The parameter parsing functions for builtin-drivers are called
100  * before kmalloc works. Store the pointers to the parameters strings
101  * into dasd[] for later processing.
102  */
103 static int __init
104 dasd_call_setup(char *str)
105 {
106 	static int count = 0;
107 
108 	if (count < 256)
109 		dasd[count++] = str;
110 	return 1;
111 }
112 
113 __setup ("dasd=", dasd_call_setup);
114 #endif	/* #ifndef MODULE */
115 
116 /*
117  * Read a device busid/devno from a string.
118  */
119 static inline int
120 dasd_busid(char **str, int *id0, int *id1, int *devno)
121 {
122 	int val, old_style;
123 
124 	/* check for leading '0x' */
125 	old_style = 0;
126 	if ((*str)[0] == '0' && (*str)[1] == 'x') {
127 		*str += 2;
128 		old_style = 1;
129 	}
130 	if (!isxdigit((*str)[0]))	/* We require at least one hex digit */
131 		return -EINVAL;
132 	val = simple_strtoul(*str, str, 16);
133 	if (old_style || (*str)[0] != '.') {
134 		*id0 = *id1 = 0;
135 		if (val < 0 || val > 0xffff)
136 			return -EINVAL;
137 		*devno = val;
138 		return 0;
139 	}
140 	/* New style x.y.z busid */
141 	if (val < 0 || val > 0xff)
142 		return -EINVAL;
143 	*id0 = val;
144 	(*str)++;
145 	if (!isxdigit((*str)[0]))	/* We require at least one hex digit */
146 		return -EINVAL;
147 	val = simple_strtoul(*str, str, 16);
148 	if (val < 0 || val > 0xff || (*str)++[0] != '.')
149 		return -EINVAL;
150 	*id1 = val;
151 	if (!isxdigit((*str)[0]))	/* We require at least one hex digit */
152 		return -EINVAL;
153 	val = simple_strtoul(*str, str, 16);
154 	if (val < 0 || val > 0xffff)
155 		return -EINVAL;
156 	*devno = val;
157 	return 0;
158 }
159 
160 /*
161  * Read colon separated list of dasd features. Currently there is
162  * only one: "ro" for read-only devices. The default feature set
163  * is empty (value 0).
164  */
165 static inline int
166 dasd_feature_list(char *str, char **endp)
167 {
168 	int features, len, rc;
169 
170 	rc = 0;
171 	if (*str != '(') {
172 		*endp = str;
173 		return DASD_FEATURE_DEFAULT;
174 	}
175 	str++;
176 	features = 0;
177 
178 	while (1) {
179 		for (len = 0;
180 		     str[len] && str[len] != ':' && str[len] != ')'; len++);
181 		if (len == 2 && !strncmp(str, "ro", 2))
182 			features |= DASD_FEATURE_READONLY;
183 		else if (len == 4 && !strncmp(str, "diag", 4))
184 			features |= DASD_FEATURE_USEDIAG;
185 		else {
186 			MESSAGE(KERN_WARNING,
187 				"unsupported feature: %*s, "
188 				"ignoring setting", len, str);
189 			rc = -EINVAL;
190 		}
191 		str += len;
192 		if (*str != ':')
193 			break;
194 		str++;
195 	}
196 	if (*str != ')') {
197 		MESSAGE(KERN_WARNING, "%s",
198 			"missing ')' in dasd parameter string\n");
199 		rc = -EINVAL;
200 	} else
201 		str++;
202 	*endp = str;
203 	if (rc != 0)
204 		return rc;
205 	return features;
206 }
207 
208 /*
209  * Try to match the first element on the comma separated parse string
210  * with one of the known keywords. If a keyword is found, take the approprate
211  * action and return a pointer to the residual string. If the first element
212  * could not be matched to any keyword then return an error code.
213  */
214 static char *
215 dasd_parse_keyword( char *parsestring ) {
216 
217 	char *nextcomma, *residual_str;
218 	int length;
219 
220 	nextcomma = strchr(parsestring,',');
221 	if (nextcomma) {
222 		length = nextcomma - parsestring;
223 		residual_str = nextcomma + 1;
224 	} else {
225 		length = strlen(parsestring);
226 		residual_str = parsestring + length;
227         }
228 	if (strncmp ("autodetect", parsestring, length) == 0) {
229 		dasd_autodetect = 1;
230 		MESSAGE (KERN_INFO, "%s",
231 			 "turning to autodetection mode");
232                 return residual_str;
233         }
234         if (strncmp ("probeonly", parsestring, length) == 0) {
235 		dasd_probeonly = 1;
236 		MESSAGE(KERN_INFO, "%s",
237 			"turning to probeonly mode");
238                 return residual_str;
239         }
240         if (strncmp ("fixedbuffers", parsestring, length) == 0) {
241 		if (dasd_page_cache)
242 			return residual_str;
243 		dasd_page_cache =
244 			kmem_cache_create("dasd_page_cache", PAGE_SIZE, 0,
245 					  SLAB_CACHE_DMA, NULL, NULL );
246 		if (!dasd_page_cache)
247 			MESSAGE(KERN_WARNING, "%s", "Failed to create slab, "
248 				"fixed buffer mode disabled.");
249 		else
250 			MESSAGE (KERN_INFO, "%s",
251 				 "turning on fixed buffer mode");
252                 return residual_str;
253         }
254 	return ERR_PTR(-EINVAL);
255 }
256 
257 /*
258  * Try to interprete the first element on the comma separated parse string
259  * as a device number or a range of devices. If the interpretation is
260  * successfull, create the matching dasd_devmap entries and return a pointer
261  * to the residual string.
262  * If interpretation fails or in case of an error, return an error code.
263  */
264 static char *
265 dasd_parse_range( char *parsestring ) {
266 
267 	struct dasd_devmap *devmap;
268 	int from, from_id0, from_id1;
269 	int to, to_id0, to_id1;
270 	int features, rc;
271 	char bus_id[BUS_ID_SIZE+1], *str;
272 
273 	str = parsestring;
274 	rc = dasd_busid(&str, &from_id0, &from_id1, &from);
275 	if (rc == 0) {
276 		to = from;
277 		to_id0 = from_id0;
278 		to_id1 = from_id1;
279 		if (*str == '-') {
280 			str++;
281 			rc = dasd_busid(&str, &to_id0, &to_id1, &to);
282 		}
283 	}
284 	if (rc == 0 &&
285 	    (from_id0 != to_id0 || from_id1 != to_id1 || from > to))
286 		rc = -EINVAL;
287 	if (rc) {
288 		MESSAGE(KERN_ERR, "Invalid device range %s", parsestring);
289 		return ERR_PTR(rc);
290 	}
291 	features = dasd_feature_list(str, &str);
292 	if (features < 0)
293 		return ERR_PTR(-EINVAL);
294 	while (from <= to) {
295 		sprintf(bus_id, "%01x.%01x.%04x",
296 			from_id0, from_id1, from++);
297 		devmap = dasd_add_busid(bus_id, features);
298 		if (IS_ERR(devmap))
299 			return (char *)devmap;
300 	}
301 	if (*str == ',')
302 		return str + 1;
303 	if (*str == '\0')
304 		return str;
305 	MESSAGE(KERN_WARNING,
306 		"junk at end of dasd parameter string: %s\n", str);
307 	return ERR_PTR(-EINVAL);
308 }
309 
310 static inline char *
311 dasd_parse_next_element( char *parsestring ) {
312 	char * residual_str;
313 	residual_str = dasd_parse_keyword(parsestring);
314 	if (!IS_ERR(residual_str))
315 		return residual_str;
316 	residual_str = dasd_parse_range(parsestring);
317 	return residual_str;
318 }
319 
320 /*
321  * Parse parameters stored in dasd[]
322  * The 'dasd=...' parameter allows to specify a comma separated list of
323  * keywords and device ranges. When the dasd driver is build into the kernel,
324  * the complete list will be stored as one element of the dasd[] array.
325  * When the dasd driver is build as a module, then the list is broken into
326  * it's elements and each dasd[] entry contains one element.
327  */
328 int
329 dasd_parse(void)
330 {
331 	int rc, i;
332 	char *parsestring;
333 
334 	rc = 0;
335 	for (i = 0; i < 256; i++) {
336 		if (dasd[i] == NULL)
337 			break;
338 		parsestring = dasd[i];
339 		/* loop over the comma separated list in the parsestring */
340 		while (*parsestring) {
341 			parsestring = dasd_parse_next_element(parsestring);
342 			if(IS_ERR(parsestring)) {
343 				rc = PTR_ERR(parsestring);
344 				break;
345 			}
346 		}
347 		if (rc) {
348 			DBF_EVENT(DBF_ALERT, "%s", "invalid range found");
349 			break;
350 		}
351 	}
352 	return rc;
353 }
354 
355 /*
356  * Add a devmap for the device specified by busid. It is possible that
357  * the devmap already exists (dasd= parameter). The order of the devices
358  * added through this function will define the kdevs for the individual
359  * devices.
360  */
361 static struct dasd_devmap *
362 dasd_add_busid(char *bus_id, int features)
363 {
364 	struct dasd_devmap *devmap, *new, *tmp;
365 	int hash;
366 
367 	new = (struct dasd_devmap *)
368 		kmalloc(sizeof(struct dasd_devmap), GFP_KERNEL);
369 	if (!new)
370 		return ERR_PTR(-ENOMEM);
371 	spin_lock(&dasd_devmap_lock);
372 	devmap = 0;
373 	hash = dasd_hash_busid(bus_id);
374 	list_for_each_entry(tmp, &dasd_hashlists[hash], list)
375 		if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
376 			devmap = tmp;
377 			break;
378 		}
379 	if (!devmap) {
380 		/* This bus_id is new. */
381 		new->devindex = dasd_max_devindex++;
382 		strncpy(new->bus_id, bus_id, BUS_ID_SIZE);
383 		new->features = features;
384 		new->device = 0;
385 		list_add(&new->list, &dasd_hashlists[hash]);
386 		devmap = new;
387 		new = 0;
388 	}
389 	spin_unlock(&dasd_devmap_lock);
390 	if (new)
391 		kfree(new);
392 	return devmap;
393 }
394 
395 /*
396  * Find devmap for device with given bus_id.
397  */
398 static struct dasd_devmap *
399 dasd_find_busid(char *bus_id)
400 {
401 	struct dasd_devmap *devmap, *tmp;
402 	int hash;
403 
404 	spin_lock(&dasd_devmap_lock);
405 	devmap = ERR_PTR(-ENODEV);
406 	hash = dasd_hash_busid(bus_id);
407 	list_for_each_entry(tmp, &dasd_hashlists[hash], list) {
408 		if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
409 			devmap = tmp;
410 			break;
411 		}
412 	}
413 	spin_unlock(&dasd_devmap_lock);
414 	return devmap;
415 }
416 
417 /*
418  * Check if busid has been added to the list of dasd ranges.
419  */
420 int
421 dasd_busid_known(char *bus_id)
422 {
423 	return IS_ERR(dasd_find_busid(bus_id)) ? -ENOENT : 0;
424 }
425 
426 /*
427  * Forget all about the device numbers added so far.
428  * This may only be called at module unload or system shutdown.
429  */
430 static void
431 dasd_forget_ranges(void)
432 {
433 	struct dasd_devmap *devmap, *n;
434 	int i;
435 
436 	spin_lock(&dasd_devmap_lock);
437 	for (i = 0; i < 256; i++) {
438 		list_for_each_entry_safe(devmap, n, &dasd_hashlists[i], list) {
439 			if (devmap->device != NULL)
440 				BUG();
441 			list_del(&devmap->list);
442 			kfree(devmap);
443 		}
444 	}
445 	spin_unlock(&dasd_devmap_lock);
446 }
447 
448 /*
449  * Find the device struct by its device index.
450  */
451 struct dasd_device *
452 dasd_device_from_devindex(int devindex)
453 {
454 	struct dasd_devmap *devmap, *tmp;
455 	struct dasd_device *device;
456 	int i;
457 
458 	spin_lock(&dasd_devmap_lock);
459 	devmap = 0;
460 	for (i = 0; (i < 256) && !devmap; i++)
461 		list_for_each_entry(tmp, &dasd_hashlists[i], list)
462 			if (tmp->devindex == devindex) {
463 				/* Found the devmap for the device. */
464 				devmap = tmp;
465 				break;
466 			}
467 	if (devmap && devmap->device) {
468 		device = devmap->device;
469 		dasd_get_device(device);
470 	} else
471 		device = ERR_PTR(-ENODEV);
472 	spin_unlock(&dasd_devmap_lock);
473 	return device;
474 }
475 
476 /*
477  * Return devmap for cdev. If no devmap exists yet, create one and
478  * connect it to the cdev.
479  */
480 static struct dasd_devmap *
481 dasd_devmap_from_cdev(struct ccw_device *cdev)
482 {
483 	struct dasd_devmap *devmap;
484 
485 	devmap = dasd_find_busid(cdev->dev.bus_id);
486 	if (IS_ERR(devmap))
487 		devmap = dasd_add_busid(cdev->dev.bus_id,
488 					DASD_FEATURE_DEFAULT);
489 	return devmap;
490 }
491 
492 /*
493  * Create a dasd device structure for cdev.
494  */
495 struct dasd_device *
496 dasd_create_device(struct ccw_device *cdev)
497 {
498 	struct dasd_devmap *devmap;
499 	struct dasd_device *device;
500 	int rc;
501 
502 	devmap = dasd_devmap_from_cdev(cdev);
503 	if (IS_ERR(devmap))
504 		return (void *) devmap;
505 	cdev->dev.driver_data = devmap;
506 
507 	device = dasd_alloc_device();
508 	if (IS_ERR(device))
509 		return device;
510 	atomic_set(&device->ref_count, 2);
511 
512 	spin_lock(&dasd_devmap_lock);
513 	if (!devmap->device) {
514 		devmap->device = device;
515 		device->devindex = devmap->devindex;
516 		device->features = devmap->features;
517 		get_device(&cdev->dev);
518 		device->cdev = cdev;
519 		rc = 0;
520 	} else
521 		/* Someone else was faster. */
522 		rc = -EBUSY;
523 	spin_unlock(&dasd_devmap_lock);
524 
525 	if (rc) {
526 		dasd_free_device(device);
527 		return ERR_PTR(rc);
528 	}
529 	return device;
530 }
531 
532 /*
533  * Wait queue for dasd_delete_device waits.
534  */
535 static DECLARE_WAIT_QUEUE_HEAD(dasd_delete_wq);
536 
537 /*
538  * Remove a dasd device structure. The passed referenced
539  * is destroyed.
540  */
541 void
542 dasd_delete_device(struct dasd_device *device)
543 {
544 	struct ccw_device *cdev;
545 	struct dasd_devmap *devmap;
546 
547 	/* First remove device pointer from devmap. */
548 	devmap = dasd_find_busid(device->cdev->dev.bus_id);
549 	if (IS_ERR(devmap))
550 		BUG();
551 	spin_lock(&dasd_devmap_lock);
552 	if (devmap->device != device) {
553 		spin_unlock(&dasd_devmap_lock);
554 		dasd_put_device(device);
555 		return;
556 	}
557 	devmap->device = NULL;
558 	spin_unlock(&dasd_devmap_lock);
559 
560 	/* Drop ref_count by 2, one for the devmap reference and
561 	 * one for the passed reference. */
562 	atomic_sub(2, &device->ref_count);
563 
564 	/* Wait for reference counter to drop to zero. */
565 	wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
566 
567 	/* Disconnect dasd_device structure from ccw_device structure. */
568 	cdev = device->cdev;
569 	device->cdev = NULL;
570 
571 	/* Disconnect dasd_devmap structure from ccw_device structure. */
572 	cdev->dev.driver_data = NULL;
573 
574 	/* Put ccw_device structure. */
575 	put_device(&cdev->dev);
576 
577 	/* Now the device structure can be freed. */
578 	dasd_free_device(device);
579 }
580 
581 /*
582  * Reference counter dropped to zero. Wake up waiter
583  * in dasd_delete_device.
584  */
585 void
586 dasd_put_device_wake(struct dasd_device *device)
587 {
588 	wake_up(&dasd_delete_wq);
589 }
590 
591 /*
592  * Return dasd_device structure associated with cdev.
593  */
594 struct dasd_device *
595 dasd_device_from_cdev(struct ccw_device *cdev)
596 {
597 	struct dasd_devmap *devmap;
598 	struct dasd_device *device;
599 
600 	device = ERR_PTR(-ENODEV);
601 	spin_lock(&dasd_devmap_lock);
602 	devmap = cdev->dev.driver_data;
603 	if (devmap && devmap->device) {
604 		device = devmap->device;
605 		dasd_get_device(device);
606 	}
607 	spin_unlock(&dasd_devmap_lock);
608 	return device;
609 }
610 
611 /*
612  * SECTION: files in sysfs
613  */
614 
615 /*
616  * readonly controls the readonly status of a dasd
617  */
618 static ssize_t
619 dasd_ro_show(struct device *dev, struct device_attribute *attr, char *buf)
620 {
621 	struct dasd_devmap *devmap;
622 	int ro_flag;
623 
624 	devmap = dasd_find_busid(dev->bus_id);
625 	if (!IS_ERR(devmap))
626 		ro_flag = (devmap->features & DASD_FEATURE_READONLY) != 0;
627 	else
628 		ro_flag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_READONLY) != 0;
629 	return snprintf(buf, PAGE_SIZE, ro_flag ? "1\n" : "0\n");
630 }
631 
632 static ssize_t
633 dasd_ro_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
634 {
635 	struct dasd_devmap *devmap;
636 	int ro_flag;
637 
638 	devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
639 	if (IS_ERR(devmap))
640 		return PTR_ERR(devmap);
641 	ro_flag = buf[0] == '1';
642 	spin_lock(&dasd_devmap_lock);
643 	if (ro_flag)
644 		devmap->features |= DASD_FEATURE_READONLY;
645 	else
646 		devmap->features &= ~DASD_FEATURE_READONLY;
647 	if (devmap->device)
648 		devmap->device->features = devmap->features;
649 	if (devmap->device && devmap->device->gdp)
650 		set_disk_ro(devmap->device->gdp, ro_flag);
651 	spin_unlock(&dasd_devmap_lock);
652 	return count;
653 }
654 
655 static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store);
656 
657 /*
658  * use_diag controls whether the driver should use diag rather than ssch
659  * to talk to the device
660  */
661 static ssize_t
662 dasd_use_diag_show(struct device *dev, struct device_attribute *attr, char *buf)
663 {
664 	struct dasd_devmap *devmap;
665 	int use_diag;
666 
667 	devmap = dasd_find_busid(dev->bus_id);
668 	if (!IS_ERR(devmap))
669 		use_diag = (devmap->features & DASD_FEATURE_USEDIAG) != 0;
670 	else
671 		use_diag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_USEDIAG) != 0;
672 	return sprintf(buf, use_diag ? "1\n" : "0\n");
673 }
674 
675 static ssize_t
676 dasd_use_diag_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
677 {
678 	struct dasd_devmap *devmap;
679 	ssize_t rc;
680 	int use_diag;
681 
682 	devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
683 	if (IS_ERR(devmap))
684 		return PTR_ERR(devmap);
685 	use_diag = buf[0] == '1';
686 	spin_lock(&dasd_devmap_lock);
687 	/* Changing diag discipline flag is only allowed in offline state. */
688 	rc = count;
689 	if (!devmap->device) {
690 		if (use_diag)
691 			devmap->features |= DASD_FEATURE_USEDIAG;
692 		else
693 			devmap->features &= ~DASD_FEATURE_USEDIAG;
694 	} else
695 		rc = -EPERM;
696 	spin_unlock(&dasd_devmap_lock);
697 	return rc;
698 }
699 
700 static
701 DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store);
702 
703 static ssize_t
704 dasd_discipline_show(struct device *dev, struct device_attribute *attr, char *buf)
705 {
706 	struct dasd_devmap *devmap;
707 	char *dname;
708 
709 	spin_lock(&dasd_devmap_lock);
710 	dname = "none";
711 	devmap = dev->driver_data;
712 	if (devmap && devmap->device && devmap->device->discipline)
713 		dname = devmap->device->discipline->name;
714 	spin_unlock(&dasd_devmap_lock);
715 	return snprintf(buf, PAGE_SIZE, "%s\n", dname);
716 }
717 
718 static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
719 
720 static struct attribute * dasd_attrs[] = {
721 	&dev_attr_readonly.attr,
722 	&dev_attr_discipline.attr,
723 	&dev_attr_use_diag.attr,
724 	NULL,
725 };
726 
727 static struct attribute_group dasd_attr_group = {
728 	.attrs = dasd_attrs,
729 };
730 
731 /*
732  * Return value of the specified feature.
733  */
734 int
735 dasd_get_feature(struct ccw_device *cdev, int feature)
736 {
737 	struct dasd_devmap *devmap;
738 
739 	devmap = dasd_find_busid(cdev->dev.bus_id);
740 	if (IS_ERR(devmap))
741 		return (int) PTR_ERR(devmap);
742 
743 	return ((devmap->features & feature) != 0);
744 }
745 
746 /*
747  * Set / reset given feature.
748  * Flag indicates wether to set (!=0) or the reset (=0) the feature.
749  */
750 int
751 dasd_set_feature(struct ccw_device *cdev, int feature, int flag)
752 {
753 	struct dasd_devmap *devmap;
754 
755 	devmap = dasd_find_busid(cdev->dev.bus_id);
756 	if (IS_ERR(devmap))
757 		return (int) PTR_ERR(devmap);
758 
759 	spin_lock(&dasd_devmap_lock);
760 	if (flag)
761 		devmap->features |= feature;
762 	else
763 		devmap->features &= ~feature;
764 	if (devmap->device)
765 		devmap->device->features = devmap->features;
766 	spin_unlock(&dasd_devmap_lock);
767 	return 0;
768 }
769 
770 
771 int
772 dasd_add_sysfs_files(struct ccw_device *cdev)
773 {
774 	return sysfs_create_group(&cdev->dev.kobj, &dasd_attr_group);
775 }
776 
777 void
778 dasd_remove_sysfs_files(struct ccw_device *cdev)
779 {
780 	sysfs_remove_group(&cdev->dev.kobj, &dasd_attr_group);
781 }
782 
783 
784 int
785 dasd_devmap_init(void)
786 {
787 	int i;
788 
789 	/* Initialize devmap structures. */
790 	dasd_max_devindex = 0;
791 	for (i = 0; i < 256; i++)
792 		INIT_LIST_HEAD(&dasd_hashlists[i]);
793 	return 0;
794 
795 }
796 
797 void
798 dasd_devmap_exit(void)
799 {
800 	dasd_forget_ranges();
801 }
802