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