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