patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / drivers / s390 / block / dcssblk.c
1 /*
2  * dcssblk.c -- the S/390 block driver for dcss memory
3  *
4  * Authors: Carsten Otte, Stefan Weinhuber, Gerald Schaefer
5  */
6
7 #include <linux/module.h>
8 #include <linux/ctype.h>
9 #include <linux/errno.h>
10 #include <linux/init.h>
11 #include <linux/slab.h>
12 #include <linux/blkdev.h>
13 #include <asm/extmem.h>
14 #include <asm/io.h>
15 #include <linux/completion.h>
16 #include <linux/interrupt.h>
17 #include <asm/ccwdev.h>         // for s390_root_dev_(un)register()
18
19 //#define DCSSBLK_DEBUG         /* Debug messages on/off */
20 #define DCSSBLK_NAME "dcssblk"
21 #define DCSSBLK_MINORS_PER_DISK 1
22
23 #ifdef DCSSBLK_DEBUG
24 #define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSSBLK_NAME " debug: " x)
25 #else
26 #define PRINT_DEBUG(x...) do {} while (0)
27 #endif
28 #define PRINT_INFO(x...)  printk(KERN_INFO DCSSBLK_NAME " info: " x)
29 #define PRINT_WARN(x...)  printk(KERN_WARNING DCSSBLK_NAME " warning: " x)
30 #define PRINT_ERR(x...)   printk(KERN_ERR DCSSBLK_NAME " error: " x)
31
32
33 static int dcssblk_open(struct inode *inode, struct file *filp);
34 static int dcssblk_release(struct inode *inode, struct file *filp);
35 static int dcssblk_make_request(struct request_queue *q, struct bio *bio);
36
37 static int dcssblk_major;
38 static struct block_device_operations dcssblk_devops = {
39         .owner   = THIS_MODULE,
40         .open    = dcssblk_open,
41         .release = dcssblk_release,
42 };
43
44 static ssize_t dcssblk_add_store(struct device * dev, const char * buf,
45                                   size_t count);
46 static ssize_t dcssblk_remove_store(struct device * dev, const char * buf,
47                                   size_t count);
48 static ssize_t dcssblk_save_store(struct device * dev, const char * buf,
49                                   size_t count);
50 static ssize_t dcssblk_save_show(struct device *dev, char *buf);
51 static ssize_t dcssblk_shared_store(struct device * dev, const char * buf,
52                                   size_t count);
53 static ssize_t dcssblk_shared_show(struct device *dev, char *buf);
54
55 static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store);
56 static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store);
57 static DEVICE_ATTR(save, S_IWUSR | S_IRUGO, dcssblk_save_show,
58                    dcssblk_save_store);
59 static DEVICE_ATTR(shared, S_IWUSR | S_IRUGO, dcssblk_shared_show,
60                    dcssblk_shared_store);
61
62 static struct device *dcssblk_root_dev;
63
64 struct dcssblk_dev_info {
65         struct list_head lh;
66         struct device dev;
67         char segment_name[BUS_ID_SIZE];
68         atomic_t use_count;
69         struct gendisk *gd;
70         unsigned long start;
71         unsigned long end;
72         int segment_type;
73         unsigned char save_pending;
74         unsigned char is_shared;
75         struct request_queue *dcssblk_queue;
76 };
77
78 static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices);
79 static struct rw_semaphore dcssblk_devices_sem;
80
81 /*
82  * release function for segment device.
83  */
84 static void
85 dcssblk_release_segment(struct device *dev)
86 {
87         PRINT_DEBUG("segment release fn called for %s\n", dev->bus_id);
88         kfree(container_of(dev, struct dcssblk_dev_info, dev));
89         module_put(THIS_MODULE);
90 }
91
92 /*
93  * get a minor number. needs to be called with
94  * down_write(&dcssblk_devices_sem) and the
95  * device needs to be enqueued before the semaphore is
96  * freed.
97  */
98 static inline int
99 dcssblk_assign_free_minor(struct dcssblk_dev_info *dev_info)
100 {
101         int minor, found;
102         struct dcssblk_dev_info *entry;
103
104         if (dev_info == NULL)
105                 return -EINVAL;
106         for (minor = 0; minor < (1<<MINORBITS); minor++) {
107                 found = 0;
108                 // test if minor available
109                 list_for_each_entry(entry, &dcssblk_devices, lh)
110                         if (minor == entry->gd->first_minor)
111                                 found++;
112                 if (!found) break; // got unused minor
113         }
114         if (found)
115                 return -EBUSY;
116         dev_info->gd->first_minor = minor;
117         return 0;
118 }
119
120 /*
121  * get the struct dcssblk_dev_info from dcssblk_devices
122  * for the given name.
123  * down_read(&dcssblk_devices_sem) must be held.
124  */
125 static struct dcssblk_dev_info *
126 dcssblk_get_device_by_name(char *name)
127 {
128         struct dcssblk_dev_info *entry;
129
130         list_for_each_entry(entry, &dcssblk_devices, lh) {
131                 if (!strcmp(name, entry->segment_name)) {
132                         return entry;
133                 }
134         }
135         return NULL;
136 }
137
138 /*
139  * device attribute for switching shared/nonshared (exclusive)
140  * operation (show + store)
141  */
142 static ssize_t
143 dcssblk_shared_show(struct device *dev, char *buf)
144 {
145         struct dcssblk_dev_info *dev_info;
146
147         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
148         return sprintf(buf, dev_info->is_shared ? "1\n" : "0\n");
149 }
150
151 static ssize_t
152 dcssblk_shared_store(struct device *dev, const char *inbuf, size_t count)
153 {
154         struct dcssblk_dev_info *dev_info;
155         int rc;
156
157         if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) {
158                 PRINT_WARN("Invalid value, must be 0 or 1\n");
159                 return -EINVAL;
160         }
161         down_write(&dcssblk_devices_sem);
162         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
163         if (atomic_read(&dev_info->use_count)) {
164                 PRINT_ERR("share: segment %s is busy!\n",
165                           dev_info->segment_name);
166                 up_write(&dcssblk_devices_sem);
167                 return -EBUSY;
168         }
169         if ((inbuf[0] == '1') && (dev_info->is_shared == 1)) {
170                 PRINT_WARN("Segment %s already loaded in shared mode!\n",
171                            dev_info->segment_name);
172                 up_write(&dcssblk_devices_sem);
173                 return count;
174         }
175         if ((inbuf[0] == '0') && (dev_info->is_shared == 0)) {
176                 PRINT_WARN("Segment %s already loaded in exclusive mode!\n",
177                            dev_info->segment_name);
178                 up_write(&dcssblk_devices_sem);
179                 return count;
180         }
181         if (inbuf[0] == '1') {
182                 // reload segment in shared mode
183                 segment_unload(dev_info->segment_name);
184                 rc = segment_load(dev_info->segment_name, SEGMENT_SHARED_RO,
185                                         &dev_info->start, &dev_info->end);
186                 if (rc < 0) {
187                         PRINT_ERR("Segment %s not reloaded, rc=%d\n",
188                                         dev_info->segment_name, rc);
189                         goto removeseg;
190                 }
191                 dev_info->is_shared = 1;
192                 PRINT_INFO("Segment %s reloaded, shared mode.\n",
193                            dev_info->segment_name);
194         } else if (inbuf[0] == '0') {
195                 // reload segment in exclusive mode
196                 segment_unload(dev_info->segment_name);
197                 rc = segment_load(dev_info->segment_name, SEGMENT_EXCLUSIVE_RW,
198                                         &dev_info->start, &dev_info->end);
199                 if (rc < 0) {
200                         PRINT_ERR("Segment %s not reloaded, rc=%d\n",
201                                         dev_info->segment_name, rc);
202                         goto removeseg;
203                 }
204                 dev_info->is_shared = 0;
205                 PRINT_INFO("Segment %s reloaded, exclusive (read-write) mode.\n",
206                            dev_info->segment_name);
207         } else {
208                 up_write(&dcssblk_devices_sem);
209                 PRINT_WARN("Invalid value, must be 0 or 1\n");
210                 return -EINVAL;
211         }
212         dev_info->segment_type = rc;
213         rc = count;
214
215         switch (dev_info->segment_type) {
216                 case SEGMENT_SHARED_RO:
217                 case SEGMENT_EXCLUSIVE_RO:
218                         set_disk_ro(dev_info->gd, 1);
219                         break;
220                 case SEGMENT_SHARED_RW:
221                 case SEGMENT_EXCLUSIVE_RW:
222                         set_disk_ro(dev_info->gd, 0);
223                         break;
224         }
225         if ((inbuf[0] == '1') &&
226            ((dev_info->segment_type == SEGMENT_EXCLUSIVE_RO) ||
227             (dev_info->segment_type == SEGMENT_EXCLUSIVE_RW))) {
228                 PRINT_WARN("Could not get shared copy of segment %s\n",
229                                 dev_info->segment_name);
230                 rc = -EPERM;
231         }
232         if ((inbuf[0] == '0') &&
233            ((dev_info->segment_type == SEGMENT_SHARED_RO) ||
234             (dev_info->segment_type == SEGMENT_SHARED_RW))) {
235                 PRINT_WARN("Could not get exclusive copy of segment %s\n",
236                                 dev_info->segment_name);
237                 rc = -EPERM;
238         }
239         up_write(&dcssblk_devices_sem);
240         goto out;
241
242 removeseg:
243         PRINT_ERR("Could not reload segment %s, removing it now!\n",
244                         dev_info->segment_name);
245         list_del(&dev_info->lh);
246
247         del_gendisk(dev_info->gd);
248         blk_put_queue(dev_info->dcssblk_queue);
249         dev_info->gd->queue = NULL;
250         put_disk(dev_info->gd);
251         device_unregister(dev);
252         put_device(dev);
253         up_write(&dcssblk_devices_sem);
254 out:
255         return rc;
256 }
257
258 /*
259  * device attribute for save operation on current copy
260  * of the segment. If the segment is busy, saving will
261  * become pending until it gets released, which can be
262  * undone by storing a non-true value to this entry.
263  * (show + store)
264  */
265 static ssize_t
266 dcssblk_save_show(struct device *dev, char *buf)
267 {
268         struct dcssblk_dev_info *dev_info;
269
270         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
271         return sprintf(buf, dev_info->save_pending ? "1\n" : "0\n");
272 }
273
274 static ssize_t
275 dcssblk_save_store(struct device *dev, const char *inbuf, size_t count)
276 {
277         struct dcssblk_dev_info *dev_info;
278
279         if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) {
280                 PRINT_WARN("Invalid value, must be 0 or 1\n");
281                 return -EINVAL;
282         }
283         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
284
285         down_write(&dcssblk_devices_sem);
286         if (inbuf[0] == '1') {
287                 if (atomic_read(&dev_info->use_count) == 0) {
288                         // device is idle => we save immediately
289                         PRINT_INFO("Saving segment %s\n",
290                                    dev_info->segment_name);
291                         segment_replace(dev_info->segment_name);
292                 }  else {
293                         // device is busy => we save it when it becomes
294                         // idle in dcssblk_release
295                         PRINT_INFO("Segment %s is currently busy, it will "
296                                    "be saved when it becomes idle...\n",
297                                    dev_info->segment_name);
298                         dev_info->save_pending = 1;
299                 }
300         } else if (inbuf[0] == '0') {
301                 if (dev_info->save_pending) {
302                         // device is busy & the user wants to undo his save
303                         // request
304                         dev_info->save_pending = 0;
305                         PRINT_INFO("Pending save for segment %s deactivated\n",
306                                         dev_info->segment_name);
307                 }
308         } else {
309                 up_write(&dcssblk_devices_sem);
310                 PRINT_WARN("Invalid value, must be 0 or 1\n");
311                 return -EINVAL;
312         }
313         up_write(&dcssblk_devices_sem);
314         return count;
315 }
316
317 /*
318  * device attribute for adding devices
319  */
320 static ssize_t
321 dcssblk_add_store(struct device *dev, const char *buf, size_t count)
322 {
323         int rc, i;
324         struct dcssblk_dev_info *dev_info;
325         char *local_buf;
326         unsigned long seg_byte_size;
327
328         dev_info = NULL;
329         if (dev != dcssblk_root_dev) {
330                 rc = -EINVAL;
331                 goto out_nobuf;
332         }
333         local_buf = kmalloc(count + 1, GFP_KERNEL);
334         if (local_buf == NULL) {
335                 rc = -ENOMEM;
336                 goto out_nobuf;
337         }
338         /*
339          * parse input
340          */
341         for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) {
342                 local_buf[i] = toupper(buf[i]);
343         }
344         local_buf[i] = '\0';
345         if ((i == 0) || (i > 8)) {
346                 rc = -ENAMETOOLONG;
347                 goto out;
348         }
349         /*
350          * already loaded?
351          */
352         down_read(&dcssblk_devices_sem);
353         dev_info = dcssblk_get_device_by_name(local_buf);
354         up_read(&dcssblk_devices_sem);
355         if (dev_info != NULL) {
356                 PRINT_WARN("Segment %s already loaded!\n", local_buf);
357                 rc = -EEXIST;
358                 goto out;
359         }
360         /*
361          * get a struct dcssblk_dev_info
362          */
363         dev_info = kmalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL);
364         if (dev_info == NULL) {
365                 rc = -ENOMEM;
366                 goto out;
367         }
368         memset(dev_info, 0, sizeof(struct dcssblk_dev_info));
369
370         strcpy(dev_info->segment_name, local_buf);
371         strlcpy(dev_info->dev.bus_id, local_buf, BUS_ID_SIZE);
372         dev_info->dev.release = dcssblk_release_segment;
373         INIT_LIST_HEAD(&dev_info->lh);
374
375         dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK);
376         if (dev_info->gd == NULL) {
377                 rc = -ENOMEM;
378                 goto free_dev_info;
379         }
380         dev_info->gd->major = dcssblk_major;
381         dev_info->gd->fops = &dcssblk_devops;
382         dev_info->dcssblk_queue = blk_alloc_queue(GFP_KERNEL);
383         dev_info->gd->queue = dev_info->dcssblk_queue;
384         dev_info->gd->private_data = dev_info;
385         dev_info->gd->driverfs_dev = &dev_info->dev;
386         /*
387          * load the segment
388          */
389         rc = segment_load(local_buf, SEGMENT_SHARED_RO,
390                                 &dev_info->start, &dev_info->end);
391         if (rc < 0) {
392                 PRINT_ERR("Segment %s not loaded, rc=%d\n", local_buf, rc);
393                 goto dealloc_gendisk;
394         }
395         seg_byte_size = (dev_info->end - dev_info->start + 1);
396         set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
397         PRINT_INFO("Loaded segment %s from %p to %p, size = %lu Byte, "
398                    "capacity = %lu sectors (512 Byte)\n", local_buf,
399                         (void *) dev_info->start, (void *) dev_info->end,
400                         seg_byte_size, seg_byte_size >> 9);
401
402         dev_info->segment_type = rc;
403         dev_info->save_pending = 0;
404         dev_info->is_shared = 1;
405         dev_info->dev.parent = dcssblk_root_dev;
406
407         /*
408          * get minor, add to list
409          */
410         down_write(&dcssblk_devices_sem);
411         rc = dcssblk_assign_free_minor(dev_info);
412         if (rc) {
413                 up_write(&dcssblk_devices_sem);
414                 PRINT_ERR("No free minor number available! "
415                           "Unloading segment...\n");
416                 goto unload_seg;
417         }
418         sprintf(dev_info->gd->disk_name, "dcssblk%d",
419                 dev_info->gd->first_minor);
420         list_add_tail(&dev_info->lh, &dcssblk_devices);
421
422         if (!try_module_get(THIS_MODULE)) {
423                 rc = -ENODEV;
424                 goto list_del;
425         }
426         /*
427          * register the device
428          */
429         rc = device_register(&dev_info->dev);
430         if (rc) {
431                 PRINT_ERR("Segment %s could not be registered RC=%d\n",
432                                 local_buf, rc);
433                 module_put(THIS_MODULE);
434                 goto list_del;
435         }
436         get_device(&dev_info->dev);
437         rc = device_create_file(&dev_info->dev, &dev_attr_shared);
438         if (rc)
439                 goto unregister_dev;
440         rc = device_create_file(&dev_info->dev, &dev_attr_save);
441         if (rc)
442                 goto unregister_dev;
443
444         add_disk(dev_info->gd);
445
446         blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
447         blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096);
448
449         switch (dev_info->segment_type) {
450                 case SEGMENT_SHARED_RO:
451                 case SEGMENT_EXCLUSIVE_RO:
452                         set_disk_ro(dev_info->gd,1);
453                         break;
454                 case SEGMENT_SHARED_RW:
455                 case SEGMENT_EXCLUSIVE_RW:
456                         set_disk_ro(dev_info->gd,0);
457                         break;
458         }
459         PRINT_DEBUG("Segment %s loaded successfully\n", local_buf);
460         up_write(&dcssblk_devices_sem);
461         rc = count;
462         goto out;
463
464 unregister_dev:
465         PRINT_ERR("device_create_file() failed!\n");
466         list_del(&dev_info->lh);
467         blk_put_queue(dev_info->dcssblk_queue);
468         dev_info->gd->queue = NULL;
469         put_disk(dev_info->gd);
470         device_unregister(&dev_info->dev);
471         segment_unload(dev_info->segment_name);
472         put_device(&dev_info->dev);
473         up_write(&dcssblk_devices_sem);
474         goto out;
475 list_del:
476         list_del(&dev_info->lh);
477         up_write(&dcssblk_devices_sem);
478 unload_seg:
479         segment_unload(local_buf);
480 dealloc_gendisk:
481         blk_put_queue(dev_info->dcssblk_queue);
482         dev_info->gd->queue = NULL;
483         put_disk(dev_info->gd);
484 free_dev_info:
485         kfree(dev_info);
486 out:
487         kfree(local_buf);
488 out_nobuf:
489         return rc;
490 }
491
492 /*
493  * device attribute for removing devices
494  */
495 static ssize_t
496 dcssblk_remove_store(struct device *dev, const char *buf, size_t count)
497 {
498         struct dcssblk_dev_info *dev_info;
499         int rc, i;
500         char *local_buf;
501
502         if (dev != dcssblk_root_dev) {
503                 return -EINVAL;
504         }
505         local_buf = kmalloc(count + 1, GFP_KERNEL);
506         if (local_buf == NULL) {
507                 return -ENOMEM;
508         }
509         /*
510          * parse input
511          */
512         for (i = 0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i < count); i++) {
513                 local_buf[i] = toupper(buf[i]);
514         }
515         local_buf[i] = '\0';
516         if ((i == 0) || (i > 8)) {
517                 rc = -ENAMETOOLONG;
518                 goto out_buf;
519         }
520
521         down_write(&dcssblk_devices_sem);
522         dev_info = dcssblk_get_device_by_name(local_buf);
523         if (dev_info == NULL) {
524                 up_write(&dcssblk_devices_sem);
525                 PRINT_WARN("Segment %s is not loaded!\n", local_buf);
526                 rc = -ENODEV;
527                 goto out_buf;
528         }
529         if (atomic_read(&dev_info->use_count) != 0) {
530                 up_write(&dcssblk_devices_sem);
531                 PRINT_WARN("Segment %s is in use!\n", local_buf);
532                 rc = -EBUSY;
533                 goto out_buf;
534         }
535         list_del(&dev_info->lh);
536
537         del_gendisk(dev_info->gd);
538         blk_put_queue(dev_info->dcssblk_queue);
539         dev_info->gd->queue = NULL;
540         put_disk(dev_info->gd);
541         device_unregister(&dev_info->dev);
542         segment_unload(dev_info->segment_name);
543         PRINT_DEBUG("Segment %s unloaded successfully\n",
544                         dev_info->segment_name);
545         put_device(&dev_info->dev);
546         up_write(&dcssblk_devices_sem);
547
548         rc = count;
549 out_buf:
550         kfree(local_buf);
551         return rc;
552 }
553
554 static int
555 dcssblk_open(struct inode *inode, struct file *filp)
556 {
557         struct dcssblk_dev_info *dev_info;
558         int rc;
559
560         dev_info = inode->i_bdev->bd_disk->private_data;
561         if (NULL == dev_info) {
562                 rc = -ENODEV;
563                 goto out;
564         }
565         atomic_inc(&dev_info->use_count);
566         inode->i_bdev->bd_block_size = 4096;
567         rc = 0;
568 out:
569         return rc;
570 }
571
572 static int
573 dcssblk_release(struct inode *inode, struct file *filp)
574 {
575         struct dcssblk_dev_info *dev_info;
576         int rc;
577
578         dev_info = inode->i_bdev->bd_disk->private_data;
579         if (NULL == dev_info) {
580                 rc = -ENODEV;
581                 goto out;
582         }
583         down_write(&dcssblk_devices_sem);
584         if (atomic_dec_and_test(&dev_info->use_count)
585             && (dev_info->save_pending)) {
586                 PRINT_INFO("Segment %s became idle and is being saved now\n",
587                             dev_info->segment_name);
588                 segment_replace(dev_info->segment_name);
589                 dev_info->save_pending = 0;
590         }
591         up_write(&dcssblk_devices_sem);
592         rc = 0;
593 out:
594         return rc;
595 }
596
597 static int
598 dcssblk_make_request(request_queue_t *q, struct bio *bio)
599 {
600         struct dcssblk_dev_info *dev_info;
601         struct bio_vec *bvec;
602         unsigned long index;
603         unsigned long page_addr;
604         unsigned long source_addr;
605         unsigned long bytes_done;
606         int i;
607
608         bytes_done = 0;
609         dev_info = bio->bi_bdev->bd_disk->private_data;
610         if (dev_info == NULL)
611                 goto fail;
612         if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0)
613                 /* Request is not page-aligned. */
614                 goto fail;
615         if (((bio->bi_size >> 9) + bio->bi_sector)
616                         > get_capacity(bio->bi_bdev->bd_disk)) {
617                 /* Request beyond end of DCSS segment. */
618                 goto fail;
619         }
620         index = (bio->bi_sector >> 3);
621         bio_for_each_segment(bvec, bio, i) {
622                 page_addr = (unsigned long)
623                         page_address(bvec->bv_page) + bvec->bv_offset;
624                 source_addr = dev_info->start + (index<<12) + bytes_done;
625                 if (unlikely(page_addr & 4095) != 0 || (bvec->bv_len & 4095) != 0)
626                         // More paranoia.
627                         goto fail;
628                 if (bio_data_dir(bio) == READ) {
629                         memcpy((void*)page_addr, (void*)source_addr,
630                                 bvec->bv_len);
631                 } else {
632                         memcpy((void*)source_addr, (void*)page_addr,
633                                 bvec->bv_len);
634                 }
635                 bytes_done += bvec->bv_len;
636         }
637         bio_endio(bio, bytes_done, 0);
638         return 0;
639 fail:
640         bio_io_error(bio, bytes_done);
641         return 0;
642 }
643
644 /*
645  * The init/exit functions.
646  */
647 static void __exit
648 dcssblk_exit(void)
649 {
650         int rc;
651
652         PRINT_DEBUG("DCSSBLOCK EXIT...\n");
653         s390_root_dev_unregister(dcssblk_root_dev);
654         rc = unregister_blkdev(dcssblk_major, DCSSBLK_NAME);
655         if (rc) {
656                 PRINT_ERR("unregister_blkdev() failed!\n");
657         }
658         PRINT_DEBUG("...finished!\n");
659 }
660
661 static int __init
662 dcssblk_init(void)
663 {
664         int rc;
665
666         PRINT_DEBUG("DCSSBLOCK INIT...\n");
667         dcssblk_root_dev = s390_root_dev_register("dcssblk");
668         if (IS_ERR(dcssblk_root_dev)) {
669                 PRINT_ERR("device_register() failed!\n");
670                 return PTR_ERR(dcssblk_root_dev);
671         }
672         rc = device_create_file(dcssblk_root_dev, &dev_attr_add);
673         if (rc) {
674                 PRINT_ERR("device_create_file(add) failed!\n");
675                 s390_root_dev_unregister(dcssblk_root_dev);
676                 return rc;
677         }
678         rc = device_create_file(dcssblk_root_dev, &dev_attr_remove);
679         if (rc) {
680                 PRINT_ERR("device_create_file(remove) failed!\n");
681                 s390_root_dev_unregister(dcssblk_root_dev);
682                 return rc;
683         }
684         rc = register_blkdev(0, DCSSBLK_NAME);
685         if (rc < 0) {
686                 PRINT_ERR("Can't get dynamic major!\n");
687                 s390_root_dev_unregister(dcssblk_root_dev);
688                 return rc;
689         }
690         dcssblk_major = rc;
691         init_rwsem(&dcssblk_devices_sem);
692         PRINT_DEBUG("...finished!\n");
693         return 0;
694 }
695
696 module_init(dcssblk_init);
697 module_exit(dcssblk_exit);
698
699 MODULE_LICENSE("GPL");