[S390] device_schedule_callback() for dcssblk.
[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 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         /*
419          * load the segment
420          */
421         rc = segment_load(local_buf, SEGMENT_SHARED,
422                                 &dev_info->start, &dev_info->end);
423         if (rc < 0) {
424                 dcssblk_segment_warn(rc, dev_info->segment_name);
425                 goto dealloc_gendisk;
426         }
427         seg_byte_size = (dev_info->end - dev_info->start + 1);
428         set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
429         PRINT_INFO("Loaded segment %s, size = %lu Byte, "
430                    "capacity = %lu (512 Byte) sectors\n", local_buf,
431                    seg_byte_size, seg_byte_size >> 9);
432
433         dev_info->segment_type = rc;
434         dev_info->save_pending = 0;
435         dev_info->is_shared = 1;
436         dev_info->dev.parent = dcssblk_root_dev;
437
438         /*
439          * get minor, add to list
440          */
441         down_write(&dcssblk_devices_sem);
442         rc = dcssblk_assign_free_minor(dev_info);
443         if (rc) {
444                 up_write(&dcssblk_devices_sem);
445                 PRINT_ERR("No free minor number available! "
446                           "Unloading segment...\n");
447                 goto unload_seg;
448         }
449         sprintf(dev_info->gd->disk_name, "dcssblk%d",
450                 dev_info->gd->first_minor);
451         list_add_tail(&dev_info->lh, &dcssblk_devices);
452
453         if (!try_module_get(THIS_MODULE)) {
454                 rc = -ENODEV;
455                 goto list_del;
456         }
457         /*
458          * register the device
459          */
460         rc = device_register(&dev_info->dev);
461         if (rc) {
462                 PRINT_ERR("Segment %s could not be registered RC=%d\n",
463                                 local_buf, rc);
464                 module_put(THIS_MODULE);
465                 goto list_del;
466         }
467         get_device(&dev_info->dev);
468         rc = device_create_file(&dev_info->dev, &dev_attr_shared);
469         if (rc)
470                 goto unregister_dev;
471         rc = device_create_file(&dev_info->dev, &dev_attr_save);
472         if (rc)
473                 goto unregister_dev;
474
475         add_disk(dev_info->gd);
476
477         blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
478         blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096);
479
480         switch (dev_info->segment_type) {
481                 case SEG_TYPE_SR:
482                 case SEG_TYPE_ER:
483                 case SEG_TYPE_SC:
484                         set_disk_ro(dev_info->gd,1);
485                         break;
486                 default:
487                         set_disk_ro(dev_info->gd,0);
488                         break;
489         }
490         PRINT_DEBUG("Segment %s loaded successfully\n", local_buf);
491         up_write(&dcssblk_devices_sem);
492         rc = count;
493         goto out;
494
495 unregister_dev:
496         PRINT_ERR("device_create_file() failed!\n");
497         list_del(&dev_info->lh);
498         blk_cleanup_queue(dev_info->dcssblk_queue);
499         dev_info->gd->queue = NULL;
500         put_disk(dev_info->gd);
501         device_unregister(&dev_info->dev);
502         segment_unload(dev_info->segment_name);
503         put_device(&dev_info->dev);
504         up_write(&dcssblk_devices_sem);
505         goto out;
506 list_del:
507         list_del(&dev_info->lh);
508         up_write(&dcssblk_devices_sem);
509 unload_seg:
510         segment_unload(local_buf);
511 dealloc_gendisk:
512         blk_cleanup_queue(dev_info->dcssblk_queue);
513         dev_info->gd->queue = NULL;
514         put_disk(dev_info->gd);
515 free_dev_info:
516         kfree(dev_info);
517 out:
518         kfree(local_buf);
519 out_nobuf:
520         return rc;
521 }
522
523 /*
524  * device attribute for removing devices
525  */
526 static ssize_t
527 dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
528 {
529         struct dcssblk_dev_info *dev_info;
530         int rc, i;
531         char *local_buf;
532
533         if (dev != dcssblk_root_dev) {
534                 return -EINVAL;
535         }
536         local_buf = kmalloc(count + 1, GFP_KERNEL);
537         if (local_buf == NULL) {
538                 return -ENOMEM;
539         }
540         /*
541          * parse input
542          */
543         for (i = 0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i < count); i++) {
544                 local_buf[i] = toupper(buf[i]);
545         }
546         local_buf[i] = '\0';
547         if ((i == 0) || (i > 8)) {
548                 rc = -ENAMETOOLONG;
549                 goto out_buf;
550         }
551
552         down_write(&dcssblk_devices_sem);
553         dev_info = dcssblk_get_device_by_name(local_buf);
554         if (dev_info == NULL) {
555                 up_write(&dcssblk_devices_sem);
556                 PRINT_WARN("Segment %s is not loaded!\n", local_buf);
557                 rc = -ENODEV;
558                 goto out_buf;
559         }
560         if (atomic_read(&dev_info->use_count) != 0) {
561                 up_write(&dcssblk_devices_sem);
562                 PRINT_WARN("Segment %s is in use!\n", local_buf);
563                 rc = -EBUSY;
564                 goto out_buf;
565         }
566         list_del(&dev_info->lh);
567
568         del_gendisk(dev_info->gd);
569         blk_cleanup_queue(dev_info->dcssblk_queue);
570         dev_info->gd->queue = NULL;
571         put_disk(dev_info->gd);
572         device_unregister(&dev_info->dev);
573         segment_unload(dev_info->segment_name);
574         PRINT_DEBUG("Segment %s unloaded successfully\n",
575                         dev_info->segment_name);
576         put_device(&dev_info->dev);
577         up_write(&dcssblk_devices_sem);
578
579         rc = count;
580 out_buf:
581         kfree(local_buf);
582         return rc;
583 }
584
585 static int
586 dcssblk_open(struct inode *inode, struct file *filp)
587 {
588         struct dcssblk_dev_info *dev_info;
589         int rc;
590
591         dev_info = inode->i_bdev->bd_disk->private_data;
592         if (NULL == dev_info) {
593                 rc = -ENODEV;
594                 goto out;
595         }
596         atomic_inc(&dev_info->use_count);
597         inode->i_bdev->bd_block_size = 4096;
598         rc = 0;
599 out:
600         return rc;
601 }
602
603 static int
604 dcssblk_release(struct inode *inode, struct file *filp)
605 {
606         struct dcssblk_dev_info *dev_info;
607         int rc;
608
609         dev_info = inode->i_bdev->bd_disk->private_data;
610         if (NULL == dev_info) {
611                 rc = -ENODEV;
612                 goto out;
613         }
614         down_write(&dcssblk_devices_sem);
615         if (atomic_dec_and_test(&dev_info->use_count)
616             && (dev_info->save_pending)) {
617                 PRINT_INFO("Segment %s became idle and is being saved now\n",
618                             dev_info->segment_name);
619                 segment_save(dev_info->segment_name);
620                 dev_info->save_pending = 0;
621         }
622         up_write(&dcssblk_devices_sem);
623         rc = 0;
624 out:
625         return rc;
626 }
627
628 static int
629 dcssblk_make_request(struct request_queue *q, struct bio *bio)
630 {
631         struct dcssblk_dev_info *dev_info;
632         struct bio_vec *bvec;
633         unsigned long index;
634         unsigned long page_addr;
635         unsigned long source_addr;
636         unsigned long bytes_done;
637         int i;
638
639         bytes_done = 0;
640         dev_info = bio->bi_bdev->bd_disk->private_data;
641         if (dev_info == NULL)
642                 goto fail;
643         if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0)
644                 /* Request is not page-aligned. */
645                 goto fail;
646         if (((bio->bi_size >> 9) + bio->bi_sector)
647                         > get_capacity(bio->bi_bdev->bd_disk)) {
648                 /* Request beyond end of DCSS segment. */
649                 goto fail;
650         }
651         /* verify data transfer direction */
652         if (dev_info->is_shared) {
653                 switch (dev_info->segment_type) {
654                 case SEG_TYPE_SR:
655                 case SEG_TYPE_ER:
656                 case SEG_TYPE_SC:
657                         /* cannot write to these segments */
658                         if (bio_data_dir(bio) == WRITE) {
659                                 PRINT_WARN("rejecting write to ro segment %s\n", dev_info->dev.bus_id);
660                                 goto fail;
661                         }
662                 }
663         }
664
665         index = (bio->bi_sector >> 3);
666         bio_for_each_segment(bvec, bio, i) {
667                 page_addr = (unsigned long)
668                         page_address(bvec->bv_page) + bvec->bv_offset;
669                 source_addr = dev_info->start + (index<<12) + bytes_done;
670                 if (unlikely(page_addr & 4095) != 0 || (bvec->bv_len & 4095) != 0)
671                         // More paranoia.
672                         goto fail;
673                 if (bio_data_dir(bio) == READ) {
674                         memcpy((void*)page_addr, (void*)source_addr,
675                                 bvec->bv_len);
676                 } else {
677                         memcpy((void*)source_addr, (void*)page_addr,
678                                 bvec->bv_len);
679                 }
680                 bytes_done += bvec->bv_len;
681         }
682         bio_endio(bio, 0);
683         return 0;
684 fail:
685         bio_io_error(bio);
686         return 0;
687 }
688
689 static int
690 dcssblk_direct_access (struct block_device *bdev, sector_t secnum,
691                         unsigned long *data)
692 {
693         struct dcssblk_dev_info *dev_info;
694         unsigned long pgoff;
695
696         dev_info = bdev->bd_disk->private_data;
697         if (!dev_info)
698                 return -ENODEV;
699         if (secnum % (PAGE_SIZE/512))
700                 return -EINVAL;
701         pgoff = secnum / (PAGE_SIZE / 512);
702         if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start)
703                 return -ERANGE;
704         *data = (unsigned long) (dev_info->start+pgoff*PAGE_SIZE);
705         return 0;
706 }
707
708 static void
709 dcssblk_check_params(void)
710 {
711         int rc, i, j, k;
712         char buf[9];
713         struct dcssblk_dev_info *dev_info;
714
715         for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0');
716              i++) {
717                 for (j = i; (dcssblk_segments[j] != ',')  &&
718                             (dcssblk_segments[j] != '\0') &&
719                             (dcssblk_segments[j] != '(')  &&
720                             (j - i) < 8; j++)
721                 {
722                         buf[j-i] = dcssblk_segments[j];
723                 }
724                 buf[j-i] = '\0';
725                 rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i);
726                 if ((rc >= 0) && (dcssblk_segments[j] == '(')) {
727                         for (k = 0; buf[k] != '\0'; k++)
728                                 buf[k] = toupper(buf[k]);
729                         if (!strncmp(&dcssblk_segments[j], "(local)", 7)) {
730                                 down_read(&dcssblk_devices_sem);
731                                 dev_info = dcssblk_get_device_by_name(buf);
732                                 up_read(&dcssblk_devices_sem);
733                                 if (dev_info)
734                                         dcssblk_shared_store(&dev_info->dev,
735                                                              NULL, "0\n", 2);
736                         }
737                 }
738                 while ((dcssblk_segments[j] != ',') &&
739                        (dcssblk_segments[j] != '\0'))
740                 {
741                         j++;
742                 }
743                 if (dcssblk_segments[j] == '\0')
744                         break;
745                 i = j;
746         }
747 }
748
749 /*
750  * The init/exit functions.
751  */
752 static void __exit
753 dcssblk_exit(void)
754 {
755         PRINT_DEBUG("DCSSBLOCK EXIT...\n");
756         s390_root_dev_unregister(dcssblk_root_dev);
757         unregister_blkdev(dcssblk_major, DCSSBLK_NAME);
758         PRINT_DEBUG("...finished!\n");
759 }
760
761 static int __init
762 dcssblk_init(void)
763 {
764         int rc;
765
766         PRINT_DEBUG("DCSSBLOCK INIT...\n");
767         dcssblk_root_dev = s390_root_dev_register("dcssblk");
768         if (IS_ERR(dcssblk_root_dev)) {
769                 PRINT_ERR("device_register() failed!\n");
770                 return PTR_ERR(dcssblk_root_dev);
771         }
772         rc = device_create_file(dcssblk_root_dev, &dev_attr_add);
773         if (rc) {
774                 PRINT_ERR("device_create_file(add) failed!\n");
775                 s390_root_dev_unregister(dcssblk_root_dev);
776                 return rc;
777         }
778         rc = device_create_file(dcssblk_root_dev, &dev_attr_remove);
779         if (rc) {
780                 PRINT_ERR("device_create_file(remove) failed!\n");
781                 s390_root_dev_unregister(dcssblk_root_dev);
782                 return rc;
783         }
784         rc = register_blkdev(0, DCSSBLK_NAME);
785         if (rc < 0) {
786                 PRINT_ERR("Can't get dynamic major!\n");
787                 s390_root_dev_unregister(dcssblk_root_dev);
788                 return rc;
789         }
790         dcssblk_major = rc;
791         init_rwsem(&dcssblk_devices_sem);
792
793         dcssblk_check_params();
794
795         PRINT_DEBUG("...finished!\n");
796         return 0;
797 }
798
799 module_init(dcssblk_init);
800 module_exit(dcssblk_exit);
801
802 module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444);
803 MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, "
804                  "comma-separated list, each name max. 8 chars.\n"
805                  "Adding \"(local)\" to segment name equals echoing 0 to "
806                  "/sys/devices/dcssblk/<segment name>/shared after loading "
807                  "the segment - \n"
808                  "e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\"");
809
810 MODULE_LICENSE("GPL");