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