Linux Audio

Check our new training course

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