]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/s390/block/dcssblk.c
[S390] struct device - replace bus_id with dev_name(), dev_set_name()
[linux-2.6-omap-h63xx.git] / drivers / s390 / block / dcssblk.c
1 /*
2  * dcssblk.c -- the S/390 block driver for dcss memory
3  *
4  * Authors: Carsten Otte, Stefan Weinhuber, Gerald Schaefer
5  */
6
7 #include <linux/module.h>
8 #include <linux/moduleparam.h>
9 #include <linux/ctype.h>
10 #include <linux/errno.h>
11 #include <linux/init.h>
12 #include <linux/slab.h>
13 #include <linux/blkdev.h>
14 #include <asm/extmem.h>
15 #include <asm/io.h>
16 #include <linux/completion.h>
17 #include <linux/interrupt.h>
18 #include <asm/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 #define DCSS_BUS_ID_SIZE 20
25
26 #ifdef DCSSBLK_DEBUG
27 #define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSSBLK_NAME " debug: " x)
28 #else
29 #define PRINT_DEBUG(x...) do {} while (0)
30 #endif
31 #define PRINT_INFO(x...)  printk(KERN_INFO DCSSBLK_NAME " info: " x)
32 #define PRINT_WARN(x...)  printk(KERN_WARNING DCSSBLK_NAME " warning: " x)
33 #define PRINT_ERR(x...)   printk(KERN_ERR DCSSBLK_NAME " error: " x)
34
35 static int dcssblk_open(struct block_device *bdev, fmode_t mode);
36 static int dcssblk_release(struct gendisk *disk, fmode_t mode);
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                                  void **kaddr, unsigned long *pfn);
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 struct dcssblk_dev_info {
52         struct list_head lh;
53         struct device dev;
54         char segment_name[DCSS_BUS_ID_SIZE];
55         atomic_t use_count;
56         struct gendisk *gd;
57         unsigned long start;
58         unsigned long end;
59         int segment_type;
60         unsigned char save_pending;
61         unsigned char is_shared;
62         struct request_queue *dcssblk_queue;
63         int num_of_segments;
64         struct list_head seg_list;
65 };
66
67 struct segment_info {
68         struct list_head lh;
69         char segment_name[DCSS_BUS_ID_SIZE];
70         unsigned long start;
71         unsigned long end;
72         int segment_type;
73 };
74
75 static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf,
76                                   size_t count);
77 static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf,
78                                   size_t count);
79 static ssize_t dcssblk_save_store(struct device * dev, struct device_attribute *attr, const char * buf,
80                                   size_t count);
81 static ssize_t dcssblk_save_show(struct device *dev, struct device_attribute *attr, char *buf);
82 static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf,
83                                   size_t count);
84 static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf);
85 static ssize_t dcssblk_seglist_show(struct device *dev,
86                                 struct device_attribute *attr,
87                                 char *buf);
88
89 static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store);
90 static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store);
91 static DEVICE_ATTR(save, S_IWUSR | S_IRUSR, dcssblk_save_show,
92                    dcssblk_save_store);
93 static DEVICE_ATTR(shared, S_IWUSR | S_IRUSR, dcssblk_shared_show,
94                    dcssblk_shared_store);
95 static DEVICE_ATTR(seglist, S_IRUSR, dcssblk_seglist_show, NULL);
96
97 static struct device *dcssblk_root_dev;
98
99 static LIST_HEAD(dcssblk_devices);
100 static struct rw_semaphore dcssblk_devices_sem;
101
102 /*
103  * release function for segment device.
104  */
105 static void
106 dcssblk_release_segment(struct device *dev)
107 {
108         struct dcssblk_dev_info *dev_info;
109         struct segment_info *entry, *temp;
110
111         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
112         list_for_each_entry_safe(entry, temp, &dev_info->seg_list, lh) {
113                 list_del(&entry->lh);
114                 kfree(entry);
115         }
116         kfree(dev_info);
117         module_put(THIS_MODULE);
118 }
119
120 /*
121  * get a minor number. needs to be called with
122  * down_write(&dcssblk_devices_sem) and the
123  * device needs to be enqueued before the semaphore is
124  * freed.
125  */
126 static int
127 dcssblk_assign_free_minor(struct dcssblk_dev_info *dev_info)
128 {
129         int minor, found;
130         struct dcssblk_dev_info *entry;
131
132         if (dev_info == NULL)
133                 return -EINVAL;
134         for (minor = 0; minor < (1<<MINORBITS); minor++) {
135                 found = 0;
136                 // test if minor available
137                 list_for_each_entry(entry, &dcssblk_devices, lh)
138                         if (minor == MINOR(disk_devt(entry->gd)))
139                                 found++;
140                 if (!found) break; // got unused minor
141         }
142         if (found)
143                 return -EBUSY;
144         dev_info->gd->first_minor = minor;
145         return 0;
146 }
147
148 /*
149  * get the struct dcssblk_dev_info from dcssblk_devices
150  * for the given name.
151  * down_read(&dcssblk_devices_sem) must be held.
152  */
153 static struct dcssblk_dev_info *
154 dcssblk_get_device_by_name(char *name)
155 {
156         struct dcssblk_dev_info *entry;
157
158         list_for_each_entry(entry, &dcssblk_devices, lh) {
159                 if (!strcmp(name, entry->segment_name)) {
160                         return entry;
161                 }
162         }
163         return NULL;
164 }
165
166 /*
167  * get the struct segment_info from seg_list
168  * for the given name.
169  * down_read(&dcssblk_devices_sem) must be held.
170  */
171 static struct segment_info *
172 dcssblk_get_segment_by_name(char *name)
173 {
174         struct dcssblk_dev_info *dev_info;
175         struct segment_info *entry;
176
177         list_for_each_entry(dev_info, &dcssblk_devices, lh) {
178                 list_for_each_entry(entry, &dev_info->seg_list, lh) {
179                         if (!strcmp(name, entry->segment_name))
180                                 return entry;
181                 }
182         }
183         return NULL;
184 }
185
186 /*
187  * get the highest address of the multi-segment block.
188  */
189 static unsigned long
190 dcssblk_find_highest_addr(struct dcssblk_dev_info *dev_info)
191 {
192         unsigned long highest_addr;
193         struct segment_info *entry;
194
195         highest_addr = 0;
196         list_for_each_entry(entry, &dev_info->seg_list, lh) {
197                 if (highest_addr < entry->end)
198                         highest_addr = entry->end;
199         }
200         return highest_addr;
201 }
202
203 /*
204  * get the lowest address of the multi-segment block.
205  */
206 static unsigned long
207 dcssblk_find_lowest_addr(struct dcssblk_dev_info *dev_info)
208 {
209         int set_first;
210         unsigned long lowest_addr;
211         struct segment_info *entry;
212
213         set_first = 0;
214         lowest_addr = 0;
215         list_for_each_entry(entry, &dev_info->seg_list, lh) {
216                 if (set_first == 0) {
217                         lowest_addr = entry->start;
218                         set_first = 1;
219                 } else {
220                         if (lowest_addr > entry->start)
221                                 lowest_addr = entry->start;
222                 }
223         }
224         return lowest_addr;
225 }
226
227 /*
228  * Check continuity of segments.
229  */
230 static int
231 dcssblk_is_continuous(struct dcssblk_dev_info *dev_info)
232 {
233         int i, j, rc;
234         struct segment_info *sort_list, *entry, temp;
235
236         if (dev_info->num_of_segments <= 1)
237                 return 0;
238
239         sort_list = kzalloc(
240                         sizeof(struct segment_info) * dev_info->num_of_segments,
241                         GFP_KERNEL);
242         if (sort_list == NULL)
243                 return -ENOMEM;
244         i = 0;
245         list_for_each_entry(entry, &dev_info->seg_list, lh) {
246                 memcpy(&sort_list[i], entry, sizeof(struct segment_info));
247                 i++;
248         }
249
250         /* sort segments */
251         for (i = 0; i < dev_info->num_of_segments; i++)
252                 for (j = 0; j < dev_info->num_of_segments; j++)
253                         if (sort_list[j].start > sort_list[i].start) {
254                                 memcpy(&temp, &sort_list[i],
255                                         sizeof(struct segment_info));
256                                 memcpy(&sort_list[i], &sort_list[j],
257                                         sizeof(struct segment_info));
258                                 memcpy(&sort_list[j], &temp,
259                                         sizeof(struct segment_info));
260                         }
261
262         /* check continuity */
263         for (i = 0; i < dev_info->num_of_segments - 1; i++) {
264                 if ((sort_list[i].end + 1) != sort_list[i+1].start) {
265                         PRINT_ERR("Segment %s is not contiguous with "
266                                 "segment %s\n",
267                                 sort_list[i].segment_name,
268                                 sort_list[i+1].segment_name);
269                         rc = -EINVAL;
270                         goto out;
271                 }
272                 /* EN and EW are allowed in a block device */
273                 if (sort_list[i].segment_type != sort_list[i+1].segment_type) {
274                         if (!(sort_list[i].segment_type & SEGMENT_EXCLUSIVE) ||
275                                 (sort_list[i].segment_type == SEG_TYPE_ER) ||
276                                 !(sort_list[i+1].segment_type &
277                                 SEGMENT_EXCLUSIVE) ||
278                                 (sort_list[i+1].segment_type == SEG_TYPE_ER)) {
279                                 PRINT_ERR("Segment %s has different type from "
280                                         "segment %s\n",
281                                         sort_list[i].segment_name,
282                                         sort_list[i+1].segment_name);
283                                 rc = -EINVAL;
284                                 goto out;
285                         }
286                 }
287         }
288         rc = 0;
289 out:
290         kfree(sort_list);
291         return rc;
292 }
293
294 /*
295  * Load a segment
296  */
297 static int
298 dcssblk_load_segment(char *name, struct segment_info **seg_info)
299 {
300         int rc;
301
302         /* already loaded? */
303         down_read(&dcssblk_devices_sem);
304         *seg_info = dcssblk_get_segment_by_name(name);
305         up_read(&dcssblk_devices_sem);
306         if (*seg_info != NULL)
307                 return -EEXIST;
308
309         /* get a struct segment_info */
310         *seg_info = kzalloc(sizeof(struct segment_info), GFP_KERNEL);
311         if (*seg_info == NULL)
312                 return -ENOMEM;
313
314         strcpy((*seg_info)->segment_name, name);
315
316         /* load the segment */
317         rc = segment_load(name, SEGMENT_SHARED,
318                         &(*seg_info)->start, &(*seg_info)->end);
319         if (rc < 0) {
320                 segment_warning(rc, (*seg_info)->segment_name);
321                 kfree(*seg_info);
322         } else {
323                 INIT_LIST_HEAD(&(*seg_info)->lh);
324                 (*seg_info)->segment_type = rc;
325         }
326         return rc;
327 }
328
329 static void dcssblk_unregister_callback(struct device *dev)
330 {
331         device_unregister(dev);
332         put_device(dev);
333 }
334
335 /*
336  * device attribute for switching shared/nonshared (exclusive)
337  * operation (show + store)
338  */
339 static ssize_t
340 dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf)
341 {
342         struct dcssblk_dev_info *dev_info;
343
344         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
345         return sprintf(buf, dev_info->is_shared ? "1\n" : "0\n");
346 }
347
348 static ssize_t
349 dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
350 {
351         struct dcssblk_dev_info *dev_info;
352         struct segment_info *entry, *temp;
353         int rc;
354
355         if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
356                 return -EINVAL;
357         down_write(&dcssblk_devices_sem);
358         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
359         if (atomic_read(&dev_info->use_count)) {
360                 rc = -EBUSY;
361                 goto out;
362         }
363         if (inbuf[0] == '1') {
364                 /* reload segments in shared mode */
365                 list_for_each_entry(entry, &dev_info->seg_list, lh) {
366                         rc = segment_modify_shared(entry->segment_name,
367                                                 SEGMENT_SHARED);
368                         if (rc < 0) {
369                                 BUG_ON(rc == -EINVAL);
370                                 if (rc != -EAGAIN)
371                                         goto removeseg;
372                         }
373                 }
374                 dev_info->is_shared = 1;
375                 switch (dev_info->segment_type) {
376                 case SEG_TYPE_SR:
377                 case SEG_TYPE_ER:
378                 case SEG_TYPE_SC:
379                         set_disk_ro(dev_info->gd, 1);
380                 }
381         } else if (inbuf[0] == '0') {
382                 /* reload segments in exclusive mode */
383                 if (dev_info->segment_type == SEG_TYPE_SC) {
384                         PRINT_ERR("Segment type SC (%s) cannot be loaded in "
385                                 "non-shared mode\n", dev_info->segment_name);
386                         rc = -EINVAL;
387                         goto out;
388                 }
389                 list_for_each_entry(entry, &dev_info->seg_list, lh) {
390                         rc = segment_modify_shared(entry->segment_name,
391                                                    SEGMENT_EXCLUSIVE);
392                         if (rc < 0) {
393                                 BUG_ON(rc == -EINVAL);
394                                 if (rc != -EAGAIN)
395                                         goto removeseg;
396                         }
397                 }
398                 dev_info->is_shared = 0;
399                 set_disk_ro(dev_info->gd, 0);
400         } else {
401                 rc = -EINVAL;
402                 goto out;
403         }
404         rc = count;
405         goto out;
406
407 removeseg:
408         PRINT_ERR("Could not reload segment(s) of the device %s, removing "
409                 "segment(s) now!\n",
410                 dev_info->segment_name);
411         temp = entry;
412         list_for_each_entry(entry, &dev_info->seg_list, lh) {
413                 if (entry != temp)
414                         segment_unload(entry->segment_name);
415         }
416         list_del(&dev_info->lh);
417
418         del_gendisk(dev_info->gd);
419         blk_cleanup_queue(dev_info->dcssblk_queue);
420         dev_info->gd->queue = NULL;
421         put_disk(dev_info->gd);
422         rc = device_schedule_callback(dev, dcssblk_unregister_callback);
423 out:
424         up_write(&dcssblk_devices_sem);
425         return rc;
426 }
427
428 /*
429  * device attribute for save operation on current copy
430  * of the segment. If the segment is busy, saving will
431  * become pending until it gets released, which can be
432  * undone by storing a non-true value to this entry.
433  * (show + store)
434  */
435 static ssize_t
436 dcssblk_save_show(struct device *dev, struct device_attribute *attr, char *buf)
437 {
438         struct dcssblk_dev_info *dev_info;
439
440         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
441         return sprintf(buf, dev_info->save_pending ? "1\n" : "0\n");
442 }
443
444 static ssize_t
445 dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
446 {
447         struct dcssblk_dev_info *dev_info;
448         struct segment_info *entry;
449
450         if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
451                 return -EINVAL;
452         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
453
454         down_write(&dcssblk_devices_sem);
455         if (inbuf[0] == '1') {
456                 if (atomic_read(&dev_info->use_count) == 0) {
457                         // device is idle => we save immediately
458                         PRINT_INFO("Saving segment(s) of the device %s\n",
459                                    dev_info->segment_name);
460                         list_for_each_entry(entry, &dev_info->seg_list, lh) {
461                                 segment_save(entry->segment_name);
462                         }
463                 }  else {
464                         // device is busy => we save it when it becomes
465                         // idle in dcssblk_release
466                         PRINT_INFO("Device %s is currently busy, segment(s) "
467                                    "will be saved when it becomes idle...\n",
468                                    dev_info->segment_name);
469                         dev_info->save_pending = 1;
470                 }
471         } else if (inbuf[0] == '0') {
472                 if (dev_info->save_pending) {
473                         // device is busy & the user wants to undo his save
474                         // request
475                         dev_info->save_pending = 0;
476                         PRINT_INFO("Pending save for segment(s) of the device "
477                                         "%s deactivated\n",
478                                         dev_info->segment_name);
479                 }
480         } else {
481                 up_write(&dcssblk_devices_sem);
482                 return -EINVAL;
483         }
484         up_write(&dcssblk_devices_sem);
485         return count;
486 }
487
488 /*
489  * device attribute for showing all segments in a device
490  */
491 static ssize_t
492 dcssblk_seglist_show(struct device *dev, struct device_attribute *attr,
493                 char *buf)
494 {
495         int i;
496
497         struct dcssblk_dev_info *dev_info;
498         struct segment_info *entry;
499
500         down_read(&dcssblk_devices_sem);
501         dev_info = container_of(dev, struct dcssblk_dev_info, dev);
502         i = 0;
503         buf[0] = '\0';
504         list_for_each_entry(entry, &dev_info->seg_list, lh) {
505                 strcpy(&buf[i], entry->segment_name);
506                 i += strlen(entry->segment_name);
507                 buf[i] = '\n';
508                 i++;
509         }
510         up_read(&dcssblk_devices_sem);
511         return i;
512 }
513
514 /*
515  * device attribute for adding devices
516  */
517 static ssize_t
518 dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
519 {
520         int rc, i, j, num_of_segments;
521         struct dcssblk_dev_info *dev_info;
522         struct segment_info *seg_info, *temp;
523         char *local_buf;
524         unsigned long seg_byte_size;
525
526         dev_info = NULL;
527         seg_info = NULL;
528         if (dev != dcssblk_root_dev) {
529                 rc = -EINVAL;
530                 goto out_nobuf;
531         }
532         if ((count < 1) || (buf[0] == '\0') || (buf[0] == '\n')) {
533                 rc = -ENAMETOOLONG;
534                 goto out_nobuf;
535         }
536
537         local_buf = kmalloc(count + 1, GFP_KERNEL);
538         if (local_buf == NULL) {
539                 rc = -ENOMEM;
540                 goto out_nobuf;
541         }
542
543         /*
544          * parse input
545          */
546         num_of_segments = 0;
547         for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) {
548                 for (j = i; (buf[j] != ':') &&
549                         (buf[j] != '\0') &&
550                         (buf[j] != '\n') &&
551                         j < count; j++) {
552                         local_buf[j-i] = toupper(buf[j]);
553                 }
554                 local_buf[j-i] = '\0';
555                 if (((j - i) == 0) || ((j - i) > 8)) {
556                         rc = -ENAMETOOLONG;
557                         goto seg_list_del;
558                 }
559
560                 rc = dcssblk_load_segment(local_buf, &seg_info);
561                 if (rc < 0)
562                         goto seg_list_del;
563                 /*
564                  * get a struct dcssblk_dev_info
565                  */
566                 if (num_of_segments == 0) {
567                         dev_info = kzalloc(sizeof(struct dcssblk_dev_info),
568                                         GFP_KERNEL);
569                         if (dev_info == NULL) {
570                                 rc = -ENOMEM;
571                                 goto out;
572                         }
573                         strcpy(dev_info->segment_name, local_buf);
574                         dev_info->segment_type = seg_info->segment_type;
575                         INIT_LIST_HEAD(&dev_info->seg_list);
576                 }
577                 list_add_tail(&seg_info->lh, &dev_info->seg_list);
578                 num_of_segments++;
579                 i = j;
580
581                 if ((buf[j] == '\0') || (buf[j] == '\n'))
582                         break;
583         }
584
585         /* no trailing colon at the end of the input */
586         if ((i > 0) && (buf[i-1] == ':')) {
587                 rc = -ENAMETOOLONG;
588                 goto seg_list_del;
589         }
590         strlcpy(local_buf, buf, i + 1);
591         dev_info->num_of_segments = num_of_segments;
592         rc = dcssblk_is_continuous(dev_info);
593         if (rc < 0)
594                 goto seg_list_del;
595
596         dev_info->start = dcssblk_find_lowest_addr(dev_info);
597         dev_info->end = dcssblk_find_highest_addr(dev_info);
598
599         dev_set_name(&dev_info->dev, dev_info->segment_name);
600         dev_info->dev.release = dcssblk_release_segment;
601         INIT_LIST_HEAD(&dev_info->lh);
602         dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK);
603         if (dev_info->gd == NULL) {
604                 rc = -ENOMEM;
605                 goto seg_list_del;
606         }
607         dev_info->gd->major = dcssblk_major;
608         dev_info->gd->fops = &dcssblk_devops;
609         dev_info->dcssblk_queue = blk_alloc_queue(GFP_KERNEL);
610         dev_info->gd->queue = dev_info->dcssblk_queue;
611         dev_info->gd->private_data = dev_info;
612         dev_info->gd->driverfs_dev = &dev_info->dev;
613         blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
614         blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096);
615
616         seg_byte_size = (dev_info->end - dev_info->start + 1);
617         set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
618         PRINT_INFO("Loaded segment(s) %s, size = %lu Byte, "
619                    "capacity = %lu (512 Byte) sectors\n", local_buf,
620                    seg_byte_size, seg_byte_size >> 9);
621
622         dev_info->save_pending = 0;
623         dev_info->is_shared = 1;
624         dev_info->dev.parent = dcssblk_root_dev;
625
626         /*
627          *get minor, add to list
628          */
629         down_write(&dcssblk_devices_sem);
630         if (dcssblk_get_segment_by_name(local_buf)) {
631                 rc = -EEXIST;
632                 goto release_gd;
633         }
634         rc = dcssblk_assign_free_minor(dev_info);
635         if (rc)
636                 goto release_gd;
637         sprintf(dev_info->gd->disk_name, "dcssblk%d",
638                 MINOR(disk_devt(dev_info->gd)));
639         list_add_tail(&dev_info->lh, &dcssblk_devices);
640
641         if (!try_module_get(THIS_MODULE)) {
642                 rc = -ENODEV;
643                 goto dev_list_del;
644         }
645         /*
646          * register the device
647          */
648         rc = device_register(&dev_info->dev);
649         if (rc) {
650                 module_put(THIS_MODULE);
651                 goto dev_list_del;
652         }
653         get_device(&dev_info->dev);
654         rc = device_create_file(&dev_info->dev, &dev_attr_shared);
655         if (rc)
656                 goto unregister_dev;
657         rc = device_create_file(&dev_info->dev, &dev_attr_save);
658         if (rc)
659                 goto unregister_dev;
660         rc = device_create_file(&dev_info->dev, &dev_attr_seglist);
661         if (rc)
662                 goto unregister_dev;
663
664         add_disk(dev_info->gd);
665
666         switch (dev_info->segment_type) {
667                 case SEG_TYPE_SR:
668                 case SEG_TYPE_ER:
669                 case SEG_TYPE_SC:
670                         set_disk_ro(dev_info->gd,1);
671                         break;
672                 default:
673                         set_disk_ro(dev_info->gd,0);
674                         break;
675         }
676         up_write(&dcssblk_devices_sem);
677         rc = count;
678         goto out;
679
680 unregister_dev:
681         list_del(&dev_info->lh);
682         blk_cleanup_queue(dev_info->dcssblk_queue);
683         dev_info->gd->queue = NULL;
684         put_disk(dev_info->gd);
685         device_unregister(&dev_info->dev);
686         list_for_each_entry(seg_info, &dev_info->seg_list, lh) {
687                 segment_unload(seg_info->segment_name);
688         }
689         put_device(&dev_info->dev);
690         up_write(&dcssblk_devices_sem);
691         goto out;
692 dev_list_del:
693         list_del(&dev_info->lh);
694 release_gd:
695         blk_cleanup_queue(dev_info->dcssblk_queue);
696         dev_info->gd->queue = NULL;
697         put_disk(dev_info->gd);
698         up_write(&dcssblk_devices_sem);
699 seg_list_del:
700         if (dev_info == NULL)
701                 goto out;
702         list_for_each_entry_safe(seg_info, temp, &dev_info->seg_list, lh) {
703                 list_del(&seg_info->lh);
704                 segment_unload(seg_info->segment_name);
705                 kfree(seg_info);
706         }
707         kfree(dev_info);
708 out:
709         kfree(local_buf);
710 out_nobuf:
711         return rc;
712 }
713
714 /*
715  * device attribute for removing devices
716  */
717 static ssize_t
718 dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
719 {
720         struct dcssblk_dev_info *dev_info;
721         struct segment_info *entry;
722         int rc, i;
723         char *local_buf;
724
725         if (dev != dcssblk_root_dev) {
726                 return -EINVAL;
727         }
728         local_buf = kmalloc(count + 1, GFP_KERNEL);
729         if (local_buf == NULL) {
730                 return -ENOMEM;
731         }
732         /*
733          * parse input
734          */
735         for (i = 0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i < count); i++) {
736                 local_buf[i] = toupper(buf[i]);
737         }
738         local_buf[i] = '\0';
739         if ((i == 0) || (i > 8)) {
740                 rc = -ENAMETOOLONG;
741                 goto out_buf;
742         }
743
744         down_write(&dcssblk_devices_sem);
745         dev_info = dcssblk_get_device_by_name(local_buf);
746         if (dev_info == NULL) {
747                 up_write(&dcssblk_devices_sem);
748                 PRINT_WARN("Device %s is not loaded!\n", local_buf);
749                 rc = -ENODEV;
750                 goto out_buf;
751         }
752         if (atomic_read(&dev_info->use_count) != 0) {
753                 up_write(&dcssblk_devices_sem);
754                 PRINT_WARN("Device %s is in use!\n", local_buf);
755                 rc = -EBUSY;
756                 goto out_buf;
757         }
758
759         list_del(&dev_info->lh);
760         del_gendisk(dev_info->gd);
761         blk_cleanup_queue(dev_info->dcssblk_queue);
762         dev_info->gd->queue = NULL;
763         put_disk(dev_info->gd);
764         device_unregister(&dev_info->dev);
765
766         /* unload all related segments */
767         list_for_each_entry(entry, &dev_info->seg_list, lh)
768                 segment_unload(entry->segment_name);
769
770         put_device(&dev_info->dev);
771         up_write(&dcssblk_devices_sem);
772
773         rc = count;
774 out_buf:
775         kfree(local_buf);
776         return rc;
777 }
778
779 static int
780 dcssblk_open(struct block_device *bdev, fmode_t mode)
781 {
782         struct dcssblk_dev_info *dev_info;
783         int rc;
784
785         dev_info = bdev->bd_disk->private_data;
786         if (NULL == dev_info) {
787                 rc = -ENODEV;
788                 goto out;
789         }
790         atomic_inc(&dev_info->use_count);
791         bdev->bd_block_size = 4096;
792         rc = 0;
793 out:
794         return rc;
795 }
796
797 static int
798 dcssblk_release(struct gendisk *disk, fmode_t mode)
799 {
800         struct dcssblk_dev_info *dev_info = disk->private_data;
801         struct segment_info *entry;
802         int rc;
803
804         if (!dev_info) {
805                 rc = -ENODEV;
806                 goto out;
807         }
808         down_write(&dcssblk_devices_sem);
809         if (atomic_dec_and_test(&dev_info->use_count)
810             && (dev_info->save_pending)) {
811                 PRINT_INFO("Device %s became idle and is being saved now\n",
812                             dev_info->segment_name);
813                 list_for_each_entry(entry, &dev_info->seg_list, lh) {
814                         segment_save(entry->segment_name);
815                 }
816                 dev_info->save_pending = 0;
817         }
818         up_write(&dcssblk_devices_sem);
819         rc = 0;
820 out:
821         return rc;
822 }
823
824 static int
825 dcssblk_make_request(struct request_queue *q, struct bio *bio)
826 {
827         struct dcssblk_dev_info *dev_info;
828         struct bio_vec *bvec;
829         unsigned long index;
830         unsigned long page_addr;
831         unsigned long source_addr;
832         unsigned long bytes_done;
833         int i;
834
835         bytes_done = 0;
836         dev_info = bio->bi_bdev->bd_disk->private_data;
837         if (dev_info == NULL)
838                 goto fail;
839         if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0)
840                 /* Request is not page-aligned. */
841                 goto fail;
842         if (((bio->bi_size >> 9) + bio->bi_sector)
843                         > get_capacity(bio->bi_bdev->bd_disk)) {
844                 /* Request beyond end of DCSS segment. */
845                 goto fail;
846         }
847         /* verify data transfer direction */
848         if (dev_info->is_shared) {
849                 switch (dev_info->segment_type) {
850                 case SEG_TYPE_SR:
851                 case SEG_TYPE_ER:
852                 case SEG_TYPE_SC:
853                         /* cannot write to these segments */
854                         if (bio_data_dir(bio) == WRITE) {
855                                 PRINT_WARN("rejecting write to ro device %s\n",
856                                            dev_name(&dev_info->dev));
857                                 goto fail;
858                         }
859                 }
860         }
861
862         index = (bio->bi_sector >> 3);
863         bio_for_each_segment(bvec, bio, i) {
864                 page_addr = (unsigned long)
865                         page_address(bvec->bv_page) + bvec->bv_offset;
866                 source_addr = dev_info->start + (index<<12) + bytes_done;
867                 if (unlikely((page_addr & 4095) != 0) || (bvec->bv_len & 4095) != 0)
868                         // More paranoia.
869                         goto fail;
870                 if (bio_data_dir(bio) == READ) {
871                         memcpy((void*)page_addr, (void*)source_addr,
872                                 bvec->bv_len);
873                 } else {
874                         memcpy((void*)source_addr, (void*)page_addr,
875                                 bvec->bv_len);
876                 }
877                 bytes_done += bvec->bv_len;
878         }
879         bio_endio(bio, 0);
880         return 0;
881 fail:
882         bio_io_error(bio);
883         return 0;
884 }
885
886 static int
887 dcssblk_direct_access (struct block_device *bdev, sector_t secnum,
888                         void **kaddr, unsigned long *pfn)
889 {
890         struct dcssblk_dev_info *dev_info;
891         unsigned long pgoff;
892
893         dev_info = bdev->bd_disk->private_data;
894         if (!dev_info)
895                 return -ENODEV;
896         if (secnum % (PAGE_SIZE/512))
897                 return -EINVAL;
898         pgoff = secnum / (PAGE_SIZE / 512);
899         if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start)
900                 return -ERANGE;
901         *kaddr = (void *) (dev_info->start+pgoff*PAGE_SIZE);
902         *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT;
903
904         return 0;
905 }
906
907 static void
908 dcssblk_check_params(void)
909 {
910         int rc, i, j, k;
911         char buf[DCSSBLK_PARM_LEN + 1];
912         struct dcssblk_dev_info *dev_info;
913
914         for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0');
915              i++) {
916                 for (j = i; (dcssblk_segments[j] != ',')  &&
917                             (dcssblk_segments[j] != '\0') &&
918                             (dcssblk_segments[j] != '(')  &&
919                             (j < DCSSBLK_PARM_LEN); j++)
920                 {
921                         buf[j-i] = dcssblk_segments[j];
922                 }
923                 buf[j-i] = '\0';
924                 rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i);
925                 if ((rc >= 0) && (dcssblk_segments[j] == '(')) {
926                         for (k = 0; (buf[k] != ':') && (buf[k] != '\0'); k++)
927                                 buf[k] = toupper(buf[k]);
928                         buf[k] = '\0';
929                         if (!strncmp(&dcssblk_segments[j], "(local)", 7)) {
930                                 down_read(&dcssblk_devices_sem);
931                                 dev_info = dcssblk_get_device_by_name(buf);
932                                 up_read(&dcssblk_devices_sem);
933                                 if (dev_info)
934                                         dcssblk_shared_store(&dev_info->dev,
935                                                              NULL, "0\n", 2);
936                         }
937                 }
938                 while ((dcssblk_segments[j] != ',') &&
939                        (dcssblk_segments[j] != '\0'))
940                 {
941                         j++;
942                 }
943                 if (dcssblk_segments[j] == '\0')
944                         break;
945                 i = j;
946         }
947 }
948
949 /*
950  * The init/exit functions.
951  */
952 static void __exit
953 dcssblk_exit(void)
954 {
955         s390_root_dev_unregister(dcssblk_root_dev);
956         unregister_blkdev(dcssblk_major, DCSSBLK_NAME);
957 }
958
959 static int __init
960 dcssblk_init(void)
961 {
962         int rc;
963
964         dcssblk_root_dev = s390_root_dev_register("dcssblk");
965         if (IS_ERR(dcssblk_root_dev))
966                 return PTR_ERR(dcssblk_root_dev);
967         rc = device_create_file(dcssblk_root_dev, &dev_attr_add);
968         if (rc) {
969                 s390_root_dev_unregister(dcssblk_root_dev);
970                 return rc;
971         }
972         rc = device_create_file(dcssblk_root_dev, &dev_attr_remove);
973         if (rc) {
974                 s390_root_dev_unregister(dcssblk_root_dev);
975                 return rc;
976         }
977         rc = register_blkdev(0, DCSSBLK_NAME);
978         if (rc < 0) {
979                 s390_root_dev_unregister(dcssblk_root_dev);
980                 return rc;
981         }
982         dcssblk_major = rc;
983         init_rwsem(&dcssblk_devices_sem);
984
985         dcssblk_check_params();
986
987         return 0;
988 }
989
990 module_init(dcssblk_init);
991 module_exit(dcssblk_exit);
992
993 module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444);
994 MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, "
995                  "comma-separated list, names in each set separated "
996                  "by commas are separated by colons, each set contains "
997                  "names of contiguous segments and each name max. 8 chars.\n"
998                  "Adding \"(local)\" to the end of each set equals echoing 0 "
999                  "to /sys/devices/dcssblk/<device name>/shared after loading "
1000                  "the contiguous segments - \n"
1001                  "e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\"");
1002
1003 MODULE_LICENSE("GPL");