Merge branch 'block-2.6.24' of git://git.kernel.dk/data/git/linux-2.6-block
[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 struct list_head dcssblk_devices = LIST_HEAD_INIT(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 /*
197  * device attribute for switching shared/nonshared (exclusive)
198  * operation (show + store)
199  */
200 static ssize_t
201 dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf)
202 {
203         struct dcssblk_dev_info *dev_info;
204
205         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
206         return sprintf(buf, dev_info->is_shared ? "1\n" : "0\n");
207 }
208
209 static ssize_t
210 dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
211 {
212         struct dcssblk_dev_info *dev_info;
213         int rc;
214
215         if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) {
216                 PRINT_WARN("Invalid value, must be 0 or 1\n");
217                 return -EINVAL;
218         }
219         down_write(&dcssblk_devices_sem);
220         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
221         if (atomic_read(&dev_info->use_count)) {
222                 PRINT_ERR("share: segment %s is busy!\n",
223                           dev_info->segment_name);
224                 rc = -EBUSY;
225                 goto out;
226         }
227         if (inbuf[0] == '1') {
228                 // reload segment in shared mode
229                 rc = segment_modify_shared(dev_info->segment_name,
230                                            SEGMENT_SHARED);
231                 if (rc < 0) {
232                         BUG_ON(rc == -EINVAL);
233                         if (rc != -EAGAIN)
234                                 goto removeseg;
235                 } else {
236                         dev_info->is_shared = 1;
237                         switch (dev_info->segment_type) {
238                                 case SEG_TYPE_SR:
239                                 case SEG_TYPE_ER:
240                                 case SEG_TYPE_SC:
241                                         set_disk_ro(dev_info->gd,1);
242                         }
243                 }
244         } else if (inbuf[0] == '0') {
245                 // reload segment in exclusive mode
246                 if (dev_info->segment_type == SEG_TYPE_SC) {
247                         PRINT_ERR("Segment type SC (%s) cannot be loaded in "
248                                   "non-shared mode\n", dev_info->segment_name);
249                         rc = -EINVAL;
250                         goto out;
251                 }
252                 rc = segment_modify_shared(dev_info->segment_name,
253                                            SEGMENT_EXCLUSIVE);
254                 if (rc < 0) {
255                         BUG_ON(rc == -EINVAL);
256                         if (rc != -EAGAIN)
257                                 goto removeseg;
258                 } else {
259                         dev_info->is_shared = 0;
260                         set_disk_ro(dev_info->gd, 0);
261                 }
262         } else {
263                 PRINT_WARN("Invalid value, must be 0 or 1\n");
264                 rc = -EINVAL;
265                 goto out;
266         }
267         rc = count;
268         goto out;
269
270 removeseg:
271         PRINT_ERR("Could not reload segment %s, removing it now!\n",
272                         dev_info->segment_name);
273         list_del(&dev_info->lh);
274
275         del_gendisk(dev_info->gd);
276         blk_cleanup_queue(dev_info->dcssblk_queue);
277         dev_info->gd->queue = NULL;
278         put_disk(dev_info->gd);
279         device_unregister(dev);
280         put_device(dev);
281 out:
282         up_write(&dcssblk_devices_sem);
283         return rc;
284 }
285
286 /*
287  * device attribute for save operation on current copy
288  * of the segment. If the segment is busy, saving will
289  * become pending until it gets released, which can be
290  * undone by storing a non-true value to this entry.
291  * (show + store)
292  */
293 static ssize_t
294 dcssblk_save_show(struct device *dev, struct device_attribute *attr, char *buf)
295 {
296         struct dcssblk_dev_info *dev_info;
297
298         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
299         return sprintf(buf, dev_info->save_pending ? "1\n" : "0\n");
300 }
301
302 static ssize_t
303 dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
304 {
305         struct dcssblk_dev_info *dev_info;
306
307         if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) {
308                 PRINT_WARN("Invalid value, must be 0 or 1\n");
309                 return -EINVAL;
310         }
311         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
312
313         down_write(&dcssblk_devices_sem);
314         if (inbuf[0] == '1') {
315                 if (atomic_read(&dev_info->use_count) == 0) {
316                         // device is idle => we save immediately
317                         PRINT_INFO("Saving segment %s\n",
318                                    dev_info->segment_name);
319                         segment_save(dev_info->segment_name);
320                 }  else {
321                         // device is busy => we save it when it becomes
322                         // idle in dcssblk_release
323                         PRINT_INFO("Segment %s is currently busy, it will "
324                                    "be saved when it becomes idle...\n",
325                                    dev_info->segment_name);
326                         dev_info->save_pending = 1;
327                 }
328         } else if (inbuf[0] == '0') {
329                 if (dev_info->save_pending) {
330                         // device is busy & the user wants to undo his save
331                         // request
332                         dev_info->save_pending = 0;
333                         PRINT_INFO("Pending save for segment %s deactivated\n",
334                                         dev_info->segment_name);
335                 }
336         } else {
337                 up_write(&dcssblk_devices_sem);
338                 PRINT_WARN("Invalid value, must be 0 or 1\n");
339                 return -EINVAL;
340         }
341         up_write(&dcssblk_devices_sem);
342         return count;
343 }
344
345 /*
346  * device attribute for adding devices
347  */
348 static ssize_t
349 dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
350 {
351         int rc, i;
352         struct dcssblk_dev_info *dev_info;
353         char *local_buf;
354         unsigned long seg_byte_size;
355
356         dev_info = NULL;
357         if (dev != dcssblk_root_dev) {
358                 rc = -EINVAL;
359                 goto out_nobuf;
360         }
361         local_buf = kmalloc(count + 1, GFP_KERNEL);
362         if (local_buf == NULL) {
363                 rc = -ENOMEM;
364                 goto out_nobuf;
365         }
366         /*
367          * parse input
368          */
369         for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) {
370                 local_buf[i] = toupper(buf[i]);
371         }
372         local_buf[i] = '\0';
373         if ((i == 0) || (i > 8)) {
374                 rc = -ENAMETOOLONG;
375                 goto out;
376         }
377         /*
378          * already loaded?
379          */
380         down_read(&dcssblk_devices_sem);
381         dev_info = dcssblk_get_device_by_name(local_buf);
382         up_read(&dcssblk_devices_sem);
383         if (dev_info != NULL) {
384                 PRINT_WARN("Segment %s already loaded!\n", local_buf);
385                 rc = -EEXIST;
386                 goto out;
387         }
388         /*
389          * get a struct dcssblk_dev_info
390          */
391         dev_info = kzalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL);
392         if (dev_info == NULL) {
393                 rc = -ENOMEM;
394                 goto out;
395         }
396
397         strcpy(dev_info->segment_name, local_buf);
398         strlcpy(dev_info->dev.bus_id, local_buf, BUS_ID_SIZE);
399         dev_info->dev.release = dcssblk_release_segment;
400         INIT_LIST_HEAD(&dev_info->lh);
401
402         dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK);
403         if (dev_info->gd == NULL) {
404                 rc = -ENOMEM;
405                 goto free_dev_info;
406         }
407         dev_info->gd->major = dcssblk_major;
408         dev_info->gd->fops = &dcssblk_devops;
409         dev_info->dcssblk_queue = blk_alloc_queue(GFP_KERNEL);
410         dev_info->gd->queue = dev_info->dcssblk_queue;
411         dev_info->gd->private_data = dev_info;
412         dev_info->gd->driverfs_dev = &dev_info->dev;
413         /*
414          * load the segment
415          */
416         rc = segment_load(local_buf, SEGMENT_SHARED,
417                                 &dev_info->start, &dev_info->end);
418         if (rc < 0) {
419                 dcssblk_segment_warn(rc, dev_info->segment_name);
420                 goto dealloc_gendisk;
421         }
422         seg_byte_size = (dev_info->end - dev_info->start + 1);
423         set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
424         PRINT_INFO("Loaded segment %s, size = %lu Byte, "
425                    "capacity = %lu (512 Byte) sectors\n", local_buf,
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         down_write(&dcssblk_devices_sem);
437         rc = dcssblk_assign_free_minor(dev_info);
438         if (rc) {
439                 up_write(&dcssblk_devices_sem);
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         if (!try_module_get(THIS_MODULE)) {
449                 rc = -ENODEV;
450                 goto list_del;
451         }
452         /*
453          * register the device
454          */
455         rc = device_register(&dev_info->dev);
456         if (rc) {
457                 PRINT_ERR("Segment %s could not be registered RC=%d\n",
458                                 local_buf, rc);
459                 module_put(THIS_MODULE);
460                 goto list_del;
461         }
462         get_device(&dev_info->dev);
463         rc = device_create_file(&dev_info->dev, &dev_attr_shared);
464         if (rc)
465                 goto unregister_dev;
466         rc = device_create_file(&dev_info->dev, &dev_attr_save);
467         if (rc)
468                 goto unregister_dev;
469
470         add_disk(dev_info->gd);
471
472         blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
473         blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096);
474
475         switch (dev_info->segment_type) {
476                 case SEG_TYPE_SR:
477                 case SEG_TYPE_ER:
478                 case SEG_TYPE_SC:
479                         set_disk_ro(dev_info->gd,1);
480                         break;
481                 default:
482                         set_disk_ro(dev_info->gd,0);
483                         break;
484         }
485         PRINT_DEBUG("Segment %s loaded successfully\n", local_buf);
486         up_write(&dcssblk_devices_sem);
487         rc = count;
488         goto out;
489
490 unregister_dev:
491         PRINT_ERR("device_create_file() failed!\n");
492         list_del(&dev_info->lh);
493         blk_cleanup_queue(dev_info->dcssblk_queue);
494         dev_info->gd->queue = NULL;
495         put_disk(dev_info->gd);
496         device_unregister(&dev_info->dev);
497         segment_unload(dev_info->segment_name);
498         put_device(&dev_info->dev);
499         up_write(&dcssblk_devices_sem);
500         goto out;
501 list_del:
502         list_del(&dev_info->lh);
503         up_write(&dcssblk_devices_sem);
504 unload_seg:
505         segment_unload(local_buf);
506 dealloc_gendisk:
507         blk_cleanup_queue(dev_info->dcssblk_queue);
508         dev_info->gd->queue = NULL;
509         put_disk(dev_info->gd);
510 free_dev_info:
511         kfree(dev_info);
512 out:
513         kfree(local_buf);
514 out_nobuf:
515         return rc;
516 }
517
518 /*
519  * device attribute for removing devices
520  */
521 static ssize_t
522 dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
523 {
524         struct dcssblk_dev_info *dev_info;
525         int rc, i;
526         char *local_buf;
527
528         if (dev != dcssblk_root_dev) {
529                 return -EINVAL;
530         }
531         local_buf = kmalloc(count + 1, GFP_KERNEL);
532         if (local_buf == NULL) {
533                 return -ENOMEM;
534         }
535         /*
536          * parse input
537          */
538         for (i = 0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i < count); i++) {
539                 local_buf[i] = toupper(buf[i]);
540         }
541         local_buf[i] = '\0';
542         if ((i == 0) || (i > 8)) {
543                 rc = -ENAMETOOLONG;
544                 goto out_buf;
545         }
546
547         down_write(&dcssblk_devices_sem);
548         dev_info = dcssblk_get_device_by_name(local_buf);
549         if (dev_info == NULL) {
550                 up_write(&dcssblk_devices_sem);
551                 PRINT_WARN("Segment %s is not loaded!\n", local_buf);
552                 rc = -ENODEV;
553                 goto out_buf;
554         }
555         if (atomic_read(&dev_info->use_count) != 0) {
556                 up_write(&dcssblk_devices_sem);
557                 PRINT_WARN("Segment %s is in use!\n", local_buf);
558                 rc = -EBUSY;
559                 goto out_buf;
560         }
561         list_del(&dev_info->lh);
562
563         del_gendisk(dev_info->gd);
564         blk_cleanup_queue(dev_info->dcssblk_queue);
565         dev_info->gd->queue = NULL;
566         put_disk(dev_info->gd);
567         device_unregister(&dev_info->dev);
568         segment_unload(dev_info->segment_name);
569         PRINT_DEBUG("Segment %s unloaded successfully\n",
570                         dev_info->segment_name);
571         put_device(&dev_info->dev);
572         up_write(&dcssblk_devices_sem);
573
574         rc = count;
575 out_buf:
576         kfree(local_buf);
577         return rc;
578 }
579
580 static int
581 dcssblk_open(struct inode *inode, struct file *filp)
582 {
583         struct dcssblk_dev_info *dev_info;
584         int rc;
585
586         dev_info = inode->i_bdev->bd_disk->private_data;
587         if (NULL == dev_info) {
588                 rc = -ENODEV;
589                 goto out;
590         }
591         atomic_inc(&dev_info->use_count);
592         inode->i_bdev->bd_block_size = 4096;
593         rc = 0;
594 out:
595         return rc;
596 }
597
598 static int
599 dcssblk_release(struct inode *inode, struct file *filp)
600 {
601         struct dcssblk_dev_info *dev_info;
602         int rc;
603
604         dev_info = inode->i_bdev->bd_disk->private_data;
605         if (NULL == dev_info) {
606                 rc = -ENODEV;
607                 goto out;
608         }
609         down_write(&dcssblk_devices_sem);
610         if (atomic_dec_and_test(&dev_info->use_count)
611             && (dev_info->save_pending)) {
612                 PRINT_INFO("Segment %s became idle and is being saved now\n",
613                             dev_info->segment_name);
614                 segment_save(dev_info->segment_name);
615                 dev_info->save_pending = 0;
616         }
617         up_write(&dcssblk_devices_sem);
618         rc = 0;
619 out:
620         return rc;
621 }
622
623 static int
624 dcssblk_make_request(struct request_queue *q, struct bio *bio)
625 {
626         struct dcssblk_dev_info *dev_info;
627         struct bio_vec *bvec;
628         unsigned long index;
629         unsigned long page_addr;
630         unsigned long source_addr;
631         unsigned long bytes_done;
632         int i;
633
634         bytes_done = 0;
635         dev_info = bio->bi_bdev->bd_disk->private_data;
636         if (dev_info == NULL)
637                 goto fail;
638         if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0)
639                 /* Request is not page-aligned. */
640                 goto fail;
641         if (((bio->bi_size >> 9) + bio->bi_sector)
642                         > get_capacity(bio->bi_bdev->bd_disk)) {
643                 /* Request beyond end of DCSS segment. */
644                 goto fail;
645         }
646         /* verify data transfer direction */
647         if (dev_info->is_shared) {
648                 switch (dev_info->segment_type) {
649                 case SEG_TYPE_SR:
650                 case SEG_TYPE_ER:
651                 case SEG_TYPE_SC:
652                         /* cannot write to these segments */
653                         if (bio_data_dir(bio) == WRITE) {
654                                 PRINT_WARN("rejecting write to ro segment %s\n", dev_info->dev.bus_id);
655                                 goto fail;
656                         }
657                 }
658         }
659
660         index = (bio->bi_sector >> 3);
661         bio_for_each_segment(bvec, bio, i) {
662                 page_addr = (unsigned long)
663                         page_address(bvec->bv_page) + bvec->bv_offset;
664                 source_addr = dev_info->start + (index<<12) + bytes_done;
665                 if (unlikely(page_addr & 4095) != 0 || (bvec->bv_len & 4095) != 0)
666                         // More paranoia.
667                         goto fail;
668                 if (bio_data_dir(bio) == READ) {
669                         memcpy((void*)page_addr, (void*)source_addr,
670                                 bvec->bv_len);
671                 } else {
672                         memcpy((void*)source_addr, (void*)page_addr,
673                                 bvec->bv_len);
674                 }
675                 bytes_done += bvec->bv_len;
676         }
677         bio_endio(bio, 0);
678         return 0;
679 fail:
680         bio_io_error(bio);
681         return 0;
682 }
683
684 static int
685 dcssblk_direct_access (struct block_device *bdev, sector_t secnum,
686                         unsigned long *data)
687 {
688         struct dcssblk_dev_info *dev_info;
689         unsigned long pgoff;
690
691         dev_info = bdev->bd_disk->private_data;
692         if (!dev_info)
693                 return -ENODEV;
694         if (secnum % (PAGE_SIZE/512))
695                 return -EINVAL;
696         pgoff = secnum / (PAGE_SIZE / 512);
697         if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start)
698                 return -ERANGE;
699         *data = (unsigned long) (dev_info->start+pgoff*PAGE_SIZE);
700         return 0;
701 }
702
703 static void
704 dcssblk_check_params(void)
705 {
706         int rc, i, j, k;
707         char buf[9];
708         struct dcssblk_dev_info *dev_info;
709
710         for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0');
711              i++) {
712                 for (j = i; (dcssblk_segments[j] != ',')  &&
713                             (dcssblk_segments[j] != '\0') &&
714                             (dcssblk_segments[j] != '(')  &&
715                             (j - i) < 8; j++)
716                 {
717                         buf[j-i] = dcssblk_segments[j];
718                 }
719                 buf[j-i] = '\0';
720                 rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i);
721                 if ((rc >= 0) && (dcssblk_segments[j] == '(')) {
722                         for (k = 0; buf[k] != '\0'; k++)
723                                 buf[k] = toupper(buf[k]);
724                         if (!strncmp(&dcssblk_segments[j], "(local)", 7)) {
725                                 down_read(&dcssblk_devices_sem);
726                                 dev_info = dcssblk_get_device_by_name(buf);
727                                 up_read(&dcssblk_devices_sem);
728                                 if (dev_info)
729                                         dcssblk_shared_store(&dev_info->dev,
730                                                              NULL, "0\n", 2);
731                         }
732                 }
733                 while ((dcssblk_segments[j] != ',') &&
734                        (dcssblk_segments[j] != '\0'))
735                 {
736                         j++;
737                 }
738                 if (dcssblk_segments[j] == '\0')
739                         break;
740                 i = j;
741         }
742 }
743
744 /*
745  * The init/exit functions.
746  */
747 static void __exit
748 dcssblk_exit(void)
749 {
750         PRINT_DEBUG("DCSSBLOCK EXIT...\n");
751         s390_root_dev_unregister(dcssblk_root_dev);
752         unregister_blkdev(dcssblk_major, DCSSBLK_NAME);
753         PRINT_DEBUG("...finished!\n");
754 }
755
756 static int __init
757 dcssblk_init(void)
758 {
759         int rc;
760
761         PRINT_DEBUG("DCSSBLOCK INIT...\n");
762         dcssblk_root_dev = s390_root_dev_register("dcssblk");
763         if (IS_ERR(dcssblk_root_dev)) {
764                 PRINT_ERR("device_register() failed!\n");
765                 return PTR_ERR(dcssblk_root_dev);
766         }
767         rc = device_create_file(dcssblk_root_dev, &dev_attr_add);
768         if (rc) {
769                 PRINT_ERR("device_create_file(add) failed!\n");
770                 s390_root_dev_unregister(dcssblk_root_dev);
771                 return rc;
772         }
773         rc = device_create_file(dcssblk_root_dev, &dev_attr_remove);
774         if (rc) {
775                 PRINT_ERR("device_create_file(remove) failed!\n");
776                 s390_root_dev_unregister(dcssblk_root_dev);
777                 return rc;
778         }
779         rc = register_blkdev(0, DCSSBLK_NAME);
780         if (rc < 0) {
781                 PRINT_ERR("Can't get dynamic major!\n");
782                 s390_root_dev_unregister(dcssblk_root_dev);
783                 return rc;
784         }
785         dcssblk_major = rc;
786         init_rwsem(&dcssblk_devices_sem);
787
788         dcssblk_check_params();
789
790         PRINT_DEBUG("...finished!\n");
791         return 0;
792 }
793
794 module_init(dcssblk_init);
795 module_exit(dcssblk_exit);
796
797 module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444);
798 MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, "
799                  "comma-separated list, each name max. 8 chars.\n"
800                  "Adding \"(local)\" to segment name equals echoing 0 to "
801                  "/sys/devices/dcssblk/<segment name>/shared after loading "
802                  "the segment - \n"
803                  "e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\"");
804
805 MODULE_LICENSE("GPL");