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