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