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