vserver 2.0 rc7
[linux-2.6.git] / drivers / s390 / block / dasd_devmap.c
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.40 $
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                 get_device(&cdev->dev);
517                 device->cdev = cdev;
518                 rc = 0;
519         } else
520                 /* Someone else was faster. */
521                 rc = -EBUSY;
522         spin_unlock(&dasd_devmap_lock);
523
524         if (rc) {
525                 dasd_free_device(device);
526                 return ERR_PTR(rc);
527         }
528         return device;
529 }
530
531 /*
532  * Wait queue for dasd_delete_device waits.
533  */
534 static DECLARE_WAIT_QUEUE_HEAD(dasd_delete_wq);
535
536 /*
537  * Remove a dasd device structure. The passed referenced
538  * is destroyed.
539  */
540 void
541 dasd_delete_device(struct dasd_device *device)
542 {
543         struct ccw_device *cdev;
544         struct dasd_devmap *devmap;
545
546         /* First remove device pointer from devmap. */
547         devmap = dasd_find_busid(device->cdev->dev.bus_id);
548         if (IS_ERR(devmap))
549                 BUG();
550         spin_lock(&dasd_devmap_lock);
551         if (devmap->device != device) {
552                 spin_unlock(&dasd_devmap_lock);
553                 dasd_put_device(device);
554                 return;
555         }
556         devmap->device = NULL;
557         spin_unlock(&dasd_devmap_lock);
558
559         /* Drop ref_count by 2, one for the devmap reference and
560          * one for the passed reference. */
561         atomic_sub(2, &device->ref_count);
562
563         /* Wait for reference counter to drop to zero. */
564         wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
565
566         /* Disconnect dasd_device structure from ccw_device structure. */
567         cdev = device->cdev;
568         device->cdev = NULL;
569
570         /* Disconnect dasd_devmap structure from ccw_device structure. */
571         cdev->dev.driver_data = NULL;
572
573         /* Put ccw_device structure. */
574         put_device(&cdev->dev);
575
576         /* Now the device structure can be freed. */
577         dasd_free_device(device);
578 }
579
580 /*
581  * Reference counter dropped to zero. Wake up waiter
582  * in dasd_delete_device.
583  */
584 void
585 dasd_put_device_wake(struct dasd_device *device)
586 {
587         wake_up(&dasd_delete_wq);
588 }
589
590 /*
591  * Return dasd_device structure associated with cdev.
592  */
593 struct dasd_device *
594 dasd_device_from_cdev(struct ccw_device *cdev)
595 {
596         struct dasd_devmap *devmap;
597         struct dasd_device *device;
598
599         device = ERR_PTR(-ENODEV);
600         spin_lock(&dasd_devmap_lock);
601         devmap = cdev->dev.driver_data;
602         if (devmap && devmap->device) {
603                 device = devmap->device;
604                 dasd_get_device(device);
605         }
606         spin_unlock(&dasd_devmap_lock);
607         return device;
608 }
609
610 /*
611  * SECTION: files in sysfs
612  */
613
614 /*
615  * readonly controls the readonly status of a dasd
616  */
617 static ssize_t
618 dasd_ro_show(struct device *dev, char *buf)
619 {
620         struct dasd_devmap *devmap;
621         int ro_flag;
622
623         devmap = dasd_find_busid(dev->bus_id);
624         if (!IS_ERR(devmap))
625                 ro_flag = (devmap->features & DASD_FEATURE_READONLY) != 0;
626         else
627                 ro_flag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_READONLY) != 0;
628         return snprintf(buf, PAGE_SIZE, ro_flag ? "1\n" : "0\n");
629 }
630
631 static ssize_t
632 dasd_ro_store(struct device *dev, const char *buf, size_t count)
633 {
634         struct dasd_devmap *devmap;
635         int ro_flag;
636
637         devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
638         if (IS_ERR(devmap))
639                 return PTR_ERR(devmap);
640         ro_flag = buf[0] == '1';
641         spin_lock(&dasd_devmap_lock);
642         if (ro_flag)
643                 devmap->features |= DASD_FEATURE_READONLY;
644         else
645                 devmap->features &= ~DASD_FEATURE_READONLY;
646         if (devmap->device && devmap->device->gdp)
647                 set_disk_ro(devmap->device->gdp, ro_flag);
648         spin_unlock(&dasd_devmap_lock);
649         return count;
650 }
651
652 static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store);
653
654 /*
655  * use_diag controls whether the driver should use diag rather than ssch
656  * to talk to the device
657  */
658 static ssize_t 
659 dasd_use_diag_show(struct device *dev, char *buf)
660 {
661         struct dasd_devmap *devmap;
662         int use_diag;
663
664         devmap = dasd_find_busid(dev->bus_id);
665         if (!IS_ERR(devmap))
666                 use_diag = (devmap->features & DASD_FEATURE_USEDIAG) != 0;
667         else
668                 use_diag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_USEDIAG) != 0;
669         return sprintf(buf, use_diag ? "1\n" : "0\n");
670 }
671
672 static ssize_t
673 dasd_use_diag_store(struct device *dev, const char *buf, size_t count)
674 {
675         struct dasd_devmap *devmap;
676         ssize_t rc;
677         int use_diag;
678
679         devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
680         if (IS_ERR(devmap))
681                 return PTR_ERR(devmap);
682         use_diag = buf[0] == '1';
683         spin_lock(&dasd_devmap_lock);
684         /* Changing diag discipline flag is only allowed in offline state. */
685         rc = count;
686         if (!devmap->device) {
687                 if (use_diag)
688                         devmap->features |= DASD_FEATURE_USEDIAG;
689                 else
690                         devmap->features &= ~DASD_FEATURE_USEDIAG;
691         } else
692                 rc = -EPERM;
693         spin_unlock(&dasd_devmap_lock);
694         return rc;
695 }
696
697 static
698 DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store);
699
700 static ssize_t
701 dasd_discipline_show(struct device *dev, char *buf)
702 {
703         struct dasd_devmap *devmap;
704         char *dname;
705
706         spin_lock(&dasd_devmap_lock);
707         dname = "none";
708         devmap = dev->driver_data;
709         if (devmap && devmap->device && devmap->device->discipline)
710                 dname = devmap->device->discipline->name;
711         spin_unlock(&dasd_devmap_lock);
712         return snprintf(buf, PAGE_SIZE, "%s\n", dname);
713 }
714
715 static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
716
717 static struct attribute * dasd_attrs[] = {
718         &dev_attr_readonly.attr,
719         &dev_attr_discipline.attr,
720         &dev_attr_use_diag.attr,
721         NULL,
722 };
723
724 static struct attribute_group dasd_attr_group = {
725         .attrs = dasd_attrs,
726 };
727
728 /*
729  * Return value of the specified feature.
730  */
731 int
732 dasd_get_feature(struct ccw_device *cdev, int feature)
733 {
734         struct dasd_devmap *devmap;
735
736         devmap = dasd_find_busid(cdev->dev.bus_id);
737         if (IS_ERR(devmap))
738                 return (int) PTR_ERR(devmap);
739
740         return ((devmap->features & feature) != 0);
741 }
742
743 /*
744  * Set / reset given feature.
745  * Flag indicates wether to set (!=0) or the reset (=0) the feature.
746  */
747 int
748 dasd_set_feature(struct ccw_device *cdev, int feature, int flag)
749 {
750         struct dasd_devmap *devmap;
751
752         devmap = dasd_find_busid(cdev->dev.bus_id);
753         if (IS_ERR(devmap))
754                 return (int) PTR_ERR(devmap);
755
756         spin_lock(&dasd_devmap_lock);
757         if (flag)
758                 devmap->features |= feature;
759         else
760                 devmap->features &= ~feature;
761
762         spin_unlock(&dasd_devmap_lock);
763         return 0;
764 }
765
766
767 int
768 dasd_add_sysfs_files(struct ccw_device *cdev)
769 {
770         return sysfs_create_group(&cdev->dev.kobj, &dasd_attr_group);
771 }
772
773 void
774 dasd_remove_sysfs_files(struct ccw_device *cdev)
775 {
776         sysfs_remove_group(&cdev->dev.kobj, &dasd_attr_group);
777 }
778
779
780 int
781 dasd_devmap_init(void)
782 {
783         int i;
784
785         /* Initialize devmap structures. */
786         dasd_max_devindex = 0;
787         for (i = 0; i < 256; i++)
788                 INIT_LIST_HEAD(&dasd_hashlists[i]);
789         return 0;
790
791 }
792
793 void
794 dasd_devmap_exit(void)
795 {
796         dasd_forget_ranges();
797 }