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