18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * dcssblk.c -- the S/390 block driver for dcss memory
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors: Carsten Otte, Stefan Weinhuber, Gerald Schaefer
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "dcssblk"
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
138c2ecf20Sopenharmony_ci#include <linux/ctype.h>
148c2ecf20Sopenharmony_ci#include <linux/errno.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
188c2ecf20Sopenharmony_ci#include <linux/completion.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/pfn_t.h>
228c2ecf20Sopenharmony_ci#include <linux/uio.h>
238c2ecf20Sopenharmony_ci#include <linux/dax.h>
248c2ecf20Sopenharmony_ci#include <asm/extmem.h>
258c2ecf20Sopenharmony_ci#include <asm/io.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define DCSSBLK_NAME "dcssblk"
288c2ecf20Sopenharmony_ci#define DCSSBLK_MINORS_PER_DISK 1
298c2ecf20Sopenharmony_ci#define DCSSBLK_PARM_LEN 400
308c2ecf20Sopenharmony_ci#define DCSS_BUS_ID_SIZE 20
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int dcssblk_open(struct block_device *bdev, fmode_t mode);
338c2ecf20Sopenharmony_cistatic void dcssblk_release(struct gendisk *disk, fmode_t mode);
348c2ecf20Sopenharmony_cistatic blk_qc_t dcssblk_submit_bio(struct bio *bio);
358c2ecf20Sopenharmony_cistatic long dcssblk_dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff,
368c2ecf20Sopenharmony_ci		long nr_pages, void **kaddr, pfn_t *pfn);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0";
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int dcssblk_major;
418c2ecf20Sopenharmony_cistatic const struct block_device_operations dcssblk_devops = {
428c2ecf20Sopenharmony_ci	.owner   	= THIS_MODULE,
438c2ecf20Sopenharmony_ci	.submit_bio	= dcssblk_submit_bio,
448c2ecf20Sopenharmony_ci	.open    	= dcssblk_open,
458c2ecf20Sopenharmony_ci	.release 	= dcssblk_release,
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic size_t dcssblk_dax_copy_from_iter(struct dax_device *dax_dev,
498c2ecf20Sopenharmony_ci		pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	return copy_from_iter(addr, bytes, i);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic size_t dcssblk_dax_copy_to_iter(struct dax_device *dax_dev,
558c2ecf20Sopenharmony_ci		pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	return copy_to_iter(addr, bytes, i);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int dcssblk_dax_zero_page_range(struct dax_device *dax_dev,
618c2ecf20Sopenharmony_ci				       pgoff_t pgoff, size_t nr_pages)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	long rc;
648c2ecf20Sopenharmony_ci	void *kaddr;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	rc = dax_direct_access(dax_dev, pgoff, nr_pages, &kaddr, NULL);
678c2ecf20Sopenharmony_ci	if (rc < 0)
688c2ecf20Sopenharmony_ci		return rc;
698c2ecf20Sopenharmony_ci	memset(kaddr, 0, nr_pages << PAGE_SHIFT);
708c2ecf20Sopenharmony_ci	dax_flush(dax_dev, kaddr, nr_pages << PAGE_SHIFT);
718c2ecf20Sopenharmony_ci	return 0;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic const struct dax_operations dcssblk_dax_ops = {
758c2ecf20Sopenharmony_ci	.direct_access = dcssblk_dax_direct_access,
768c2ecf20Sopenharmony_ci	.dax_supported = generic_fsdax_supported,
778c2ecf20Sopenharmony_ci	.copy_from_iter = dcssblk_dax_copy_from_iter,
788c2ecf20Sopenharmony_ci	.copy_to_iter = dcssblk_dax_copy_to_iter,
798c2ecf20Sopenharmony_ci	.zero_page_range = dcssblk_dax_zero_page_range,
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistruct dcssblk_dev_info {
838c2ecf20Sopenharmony_ci	struct list_head lh;
848c2ecf20Sopenharmony_ci	struct device dev;
858c2ecf20Sopenharmony_ci	char segment_name[DCSS_BUS_ID_SIZE];
868c2ecf20Sopenharmony_ci	atomic_t use_count;
878c2ecf20Sopenharmony_ci	struct gendisk *gd;
888c2ecf20Sopenharmony_ci	unsigned long start;
898c2ecf20Sopenharmony_ci	unsigned long end;
908c2ecf20Sopenharmony_ci	int segment_type;
918c2ecf20Sopenharmony_ci	unsigned char save_pending;
928c2ecf20Sopenharmony_ci	unsigned char is_shared;
938c2ecf20Sopenharmony_ci	struct request_queue *dcssblk_queue;
948c2ecf20Sopenharmony_ci	int num_of_segments;
958c2ecf20Sopenharmony_ci	struct list_head seg_list;
968c2ecf20Sopenharmony_ci	struct dax_device *dax_dev;
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistruct segment_info {
1008c2ecf20Sopenharmony_ci	struct list_head lh;
1018c2ecf20Sopenharmony_ci	char segment_name[DCSS_BUS_ID_SIZE];
1028c2ecf20Sopenharmony_ci	unsigned long start;
1038c2ecf20Sopenharmony_ci	unsigned long end;
1048c2ecf20Sopenharmony_ci	int segment_type;
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf,
1088c2ecf20Sopenharmony_ci				  size_t count);
1098c2ecf20Sopenharmony_cistatic ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf,
1108c2ecf20Sopenharmony_ci				  size_t count);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store);
1138c2ecf20Sopenharmony_cistatic DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic struct device *dcssblk_root_dev;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic LIST_HEAD(dcssblk_devices);
1188c2ecf20Sopenharmony_cistatic struct rw_semaphore dcssblk_devices_sem;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/*
1218c2ecf20Sopenharmony_ci * release function for segment device.
1228c2ecf20Sopenharmony_ci */
1238c2ecf20Sopenharmony_cistatic void
1248c2ecf20Sopenharmony_cidcssblk_release_segment(struct device *dev)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
1278c2ecf20Sopenharmony_ci	struct segment_info *entry, *temp;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	dev_info = container_of(dev, struct dcssblk_dev_info, dev);
1308c2ecf20Sopenharmony_ci	list_for_each_entry_safe(entry, temp, &dev_info->seg_list, lh) {
1318c2ecf20Sopenharmony_ci		list_del(&entry->lh);
1328c2ecf20Sopenharmony_ci		kfree(entry);
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci	kfree(dev_info);
1358c2ecf20Sopenharmony_ci	module_put(THIS_MODULE);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/*
1398c2ecf20Sopenharmony_ci * get a minor number. needs to be called with
1408c2ecf20Sopenharmony_ci * down_write(&dcssblk_devices_sem) and the
1418c2ecf20Sopenharmony_ci * device needs to be enqueued before the semaphore is
1428c2ecf20Sopenharmony_ci * freed.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_cistatic int
1458c2ecf20Sopenharmony_cidcssblk_assign_free_minor(struct dcssblk_dev_info *dev_info)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	int minor, found;
1488c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *entry;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (dev_info == NULL)
1518c2ecf20Sopenharmony_ci		return -EINVAL;
1528c2ecf20Sopenharmony_ci	for (minor = 0; minor < (1<<MINORBITS); minor++) {
1538c2ecf20Sopenharmony_ci		found = 0;
1548c2ecf20Sopenharmony_ci		// test if minor available
1558c2ecf20Sopenharmony_ci		list_for_each_entry(entry, &dcssblk_devices, lh)
1568c2ecf20Sopenharmony_ci			if (minor == entry->gd->first_minor)
1578c2ecf20Sopenharmony_ci				found++;
1588c2ecf20Sopenharmony_ci		if (!found) break; // got unused minor
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci	if (found)
1618c2ecf20Sopenharmony_ci		return -EBUSY;
1628c2ecf20Sopenharmony_ci	dev_info->gd->first_minor = minor;
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/*
1678c2ecf20Sopenharmony_ci * get the struct dcssblk_dev_info from dcssblk_devices
1688c2ecf20Sopenharmony_ci * for the given name.
1698c2ecf20Sopenharmony_ci * down_read(&dcssblk_devices_sem) must be held.
1708c2ecf20Sopenharmony_ci */
1718c2ecf20Sopenharmony_cistatic struct dcssblk_dev_info *
1728c2ecf20Sopenharmony_cidcssblk_get_device_by_name(char *name)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *entry;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &dcssblk_devices, lh) {
1778c2ecf20Sopenharmony_ci		if (!strcmp(name, entry->segment_name)) {
1788c2ecf20Sopenharmony_ci			return entry;
1798c2ecf20Sopenharmony_ci		}
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci	return NULL;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/*
1858c2ecf20Sopenharmony_ci * get the struct segment_info from seg_list
1868c2ecf20Sopenharmony_ci * for the given name.
1878c2ecf20Sopenharmony_ci * down_read(&dcssblk_devices_sem) must be held.
1888c2ecf20Sopenharmony_ci */
1898c2ecf20Sopenharmony_cistatic struct segment_info *
1908c2ecf20Sopenharmony_cidcssblk_get_segment_by_name(char *name)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
1938c2ecf20Sopenharmony_ci	struct segment_info *entry;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	list_for_each_entry(dev_info, &dcssblk_devices, lh) {
1968c2ecf20Sopenharmony_ci		list_for_each_entry(entry, &dev_info->seg_list, lh) {
1978c2ecf20Sopenharmony_ci			if (!strcmp(name, entry->segment_name))
1988c2ecf20Sopenharmony_ci				return entry;
1998c2ecf20Sopenharmony_ci		}
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci	return NULL;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/*
2058c2ecf20Sopenharmony_ci * get the highest address of the multi-segment block.
2068c2ecf20Sopenharmony_ci */
2078c2ecf20Sopenharmony_cistatic unsigned long
2088c2ecf20Sopenharmony_cidcssblk_find_highest_addr(struct dcssblk_dev_info *dev_info)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	unsigned long highest_addr;
2118c2ecf20Sopenharmony_ci	struct segment_info *entry;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	highest_addr = 0;
2148c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &dev_info->seg_list, lh) {
2158c2ecf20Sopenharmony_ci		if (highest_addr < entry->end)
2168c2ecf20Sopenharmony_ci			highest_addr = entry->end;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci	return highest_addr;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/*
2228c2ecf20Sopenharmony_ci * get the lowest address of the multi-segment block.
2238c2ecf20Sopenharmony_ci */
2248c2ecf20Sopenharmony_cistatic unsigned long
2258c2ecf20Sopenharmony_cidcssblk_find_lowest_addr(struct dcssblk_dev_info *dev_info)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	int set_first;
2288c2ecf20Sopenharmony_ci	unsigned long lowest_addr;
2298c2ecf20Sopenharmony_ci	struct segment_info *entry;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	set_first = 0;
2328c2ecf20Sopenharmony_ci	lowest_addr = 0;
2338c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &dev_info->seg_list, lh) {
2348c2ecf20Sopenharmony_ci		if (set_first == 0) {
2358c2ecf20Sopenharmony_ci			lowest_addr = entry->start;
2368c2ecf20Sopenharmony_ci			set_first = 1;
2378c2ecf20Sopenharmony_ci		} else {
2388c2ecf20Sopenharmony_ci			if (lowest_addr > entry->start)
2398c2ecf20Sopenharmony_ci				lowest_addr = entry->start;
2408c2ecf20Sopenharmony_ci		}
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci	return lowest_addr;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci/*
2468c2ecf20Sopenharmony_ci * Check continuity of segments.
2478c2ecf20Sopenharmony_ci */
2488c2ecf20Sopenharmony_cistatic int
2498c2ecf20Sopenharmony_cidcssblk_is_continuous(struct dcssblk_dev_info *dev_info)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	int i, j, rc;
2528c2ecf20Sopenharmony_ci	struct segment_info *sort_list, *entry, temp;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (dev_info->num_of_segments <= 1)
2558c2ecf20Sopenharmony_ci		return 0;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	sort_list = kcalloc(dev_info->num_of_segments,
2588c2ecf20Sopenharmony_ci			    sizeof(struct segment_info),
2598c2ecf20Sopenharmony_ci			    GFP_KERNEL);
2608c2ecf20Sopenharmony_ci	if (sort_list == NULL)
2618c2ecf20Sopenharmony_ci		return -ENOMEM;
2628c2ecf20Sopenharmony_ci	i = 0;
2638c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &dev_info->seg_list, lh) {
2648c2ecf20Sopenharmony_ci		memcpy(&sort_list[i], entry, sizeof(struct segment_info));
2658c2ecf20Sopenharmony_ci		i++;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/* sort segments */
2698c2ecf20Sopenharmony_ci	for (i = 0; i < dev_info->num_of_segments; i++)
2708c2ecf20Sopenharmony_ci		for (j = 0; j < dev_info->num_of_segments; j++)
2718c2ecf20Sopenharmony_ci			if (sort_list[j].start > sort_list[i].start) {
2728c2ecf20Sopenharmony_ci				memcpy(&temp, &sort_list[i],
2738c2ecf20Sopenharmony_ci					sizeof(struct segment_info));
2748c2ecf20Sopenharmony_ci				memcpy(&sort_list[i], &sort_list[j],
2758c2ecf20Sopenharmony_ci					sizeof(struct segment_info));
2768c2ecf20Sopenharmony_ci				memcpy(&sort_list[j], &temp,
2778c2ecf20Sopenharmony_ci					sizeof(struct segment_info));
2788c2ecf20Sopenharmony_ci			}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/* check continuity */
2818c2ecf20Sopenharmony_ci	for (i = 0; i < dev_info->num_of_segments - 1; i++) {
2828c2ecf20Sopenharmony_ci		if ((sort_list[i].end + 1) != sort_list[i+1].start) {
2838c2ecf20Sopenharmony_ci			pr_err("Adjacent DCSSs %s and %s are not "
2848c2ecf20Sopenharmony_ci			       "contiguous\n", sort_list[i].segment_name,
2858c2ecf20Sopenharmony_ci			       sort_list[i+1].segment_name);
2868c2ecf20Sopenharmony_ci			rc = -EINVAL;
2878c2ecf20Sopenharmony_ci			goto out;
2888c2ecf20Sopenharmony_ci		}
2898c2ecf20Sopenharmony_ci		/* EN and EW are allowed in a block device */
2908c2ecf20Sopenharmony_ci		if (sort_list[i].segment_type != sort_list[i+1].segment_type) {
2918c2ecf20Sopenharmony_ci			if (!(sort_list[i].segment_type & SEGMENT_EXCLUSIVE) ||
2928c2ecf20Sopenharmony_ci				(sort_list[i].segment_type == SEG_TYPE_ER) ||
2938c2ecf20Sopenharmony_ci				!(sort_list[i+1].segment_type &
2948c2ecf20Sopenharmony_ci				SEGMENT_EXCLUSIVE) ||
2958c2ecf20Sopenharmony_ci				(sort_list[i+1].segment_type == SEG_TYPE_ER)) {
2968c2ecf20Sopenharmony_ci				pr_err("DCSS %s and DCSS %s have "
2978c2ecf20Sopenharmony_ci				       "incompatible types\n",
2988c2ecf20Sopenharmony_ci				       sort_list[i].segment_name,
2998c2ecf20Sopenharmony_ci				       sort_list[i+1].segment_name);
3008c2ecf20Sopenharmony_ci				rc = -EINVAL;
3018c2ecf20Sopenharmony_ci				goto out;
3028c2ecf20Sopenharmony_ci			}
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci	rc = 0;
3068c2ecf20Sopenharmony_ciout:
3078c2ecf20Sopenharmony_ci	kfree(sort_list);
3088c2ecf20Sopenharmony_ci	return rc;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci/*
3128c2ecf20Sopenharmony_ci * Load a segment
3138c2ecf20Sopenharmony_ci */
3148c2ecf20Sopenharmony_cistatic int
3158c2ecf20Sopenharmony_cidcssblk_load_segment(char *name, struct segment_info **seg_info)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	int rc;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/* already loaded? */
3208c2ecf20Sopenharmony_ci	down_read(&dcssblk_devices_sem);
3218c2ecf20Sopenharmony_ci	*seg_info = dcssblk_get_segment_by_name(name);
3228c2ecf20Sopenharmony_ci	up_read(&dcssblk_devices_sem);
3238c2ecf20Sopenharmony_ci	if (*seg_info != NULL)
3248c2ecf20Sopenharmony_ci		return -EEXIST;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/* get a struct segment_info */
3278c2ecf20Sopenharmony_ci	*seg_info = kzalloc(sizeof(struct segment_info), GFP_KERNEL);
3288c2ecf20Sopenharmony_ci	if (*seg_info == NULL)
3298c2ecf20Sopenharmony_ci		return -ENOMEM;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	strcpy((*seg_info)->segment_name, name);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/* load the segment */
3348c2ecf20Sopenharmony_ci	rc = segment_load(name, SEGMENT_SHARED,
3358c2ecf20Sopenharmony_ci			&(*seg_info)->start, &(*seg_info)->end);
3368c2ecf20Sopenharmony_ci	if (rc < 0) {
3378c2ecf20Sopenharmony_ci		segment_warning(rc, (*seg_info)->segment_name);
3388c2ecf20Sopenharmony_ci		kfree(*seg_info);
3398c2ecf20Sopenharmony_ci	} else {
3408c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&(*seg_info)->lh);
3418c2ecf20Sopenharmony_ci		(*seg_info)->segment_type = rc;
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci	return rc;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci/*
3478c2ecf20Sopenharmony_ci * device attribute for switching shared/nonshared (exclusive)
3488c2ecf20Sopenharmony_ci * operation (show + store)
3498c2ecf20Sopenharmony_ci */
3508c2ecf20Sopenharmony_cistatic ssize_t
3518c2ecf20Sopenharmony_cidcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	dev_info = container_of(dev, struct dcssblk_dev_info, dev);
3568c2ecf20Sopenharmony_ci	return sprintf(buf, dev_info->is_shared ? "1\n" : "0\n");
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic ssize_t
3608c2ecf20Sopenharmony_cidcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
3638c2ecf20Sopenharmony_ci	struct segment_info *entry, *temp;
3648c2ecf20Sopenharmony_ci	int rc;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
3678c2ecf20Sopenharmony_ci		return -EINVAL;
3688c2ecf20Sopenharmony_ci	down_write(&dcssblk_devices_sem);
3698c2ecf20Sopenharmony_ci	dev_info = container_of(dev, struct dcssblk_dev_info, dev);
3708c2ecf20Sopenharmony_ci	if (atomic_read(&dev_info->use_count)) {
3718c2ecf20Sopenharmony_ci		rc = -EBUSY;
3728c2ecf20Sopenharmony_ci		goto out;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci	if (inbuf[0] == '1') {
3758c2ecf20Sopenharmony_ci		/* reload segments in shared mode */
3768c2ecf20Sopenharmony_ci		list_for_each_entry(entry, &dev_info->seg_list, lh) {
3778c2ecf20Sopenharmony_ci			rc = segment_modify_shared(entry->segment_name,
3788c2ecf20Sopenharmony_ci						SEGMENT_SHARED);
3798c2ecf20Sopenharmony_ci			if (rc < 0) {
3808c2ecf20Sopenharmony_ci				BUG_ON(rc == -EINVAL);
3818c2ecf20Sopenharmony_ci				if (rc != -EAGAIN)
3828c2ecf20Sopenharmony_ci					goto removeseg;
3838c2ecf20Sopenharmony_ci			}
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci		dev_info->is_shared = 1;
3868c2ecf20Sopenharmony_ci		switch (dev_info->segment_type) {
3878c2ecf20Sopenharmony_ci		case SEG_TYPE_SR:
3888c2ecf20Sopenharmony_ci		case SEG_TYPE_ER:
3898c2ecf20Sopenharmony_ci		case SEG_TYPE_SC:
3908c2ecf20Sopenharmony_ci			set_disk_ro(dev_info->gd, 1);
3918c2ecf20Sopenharmony_ci		}
3928c2ecf20Sopenharmony_ci	} else if (inbuf[0] == '0') {
3938c2ecf20Sopenharmony_ci		/* reload segments in exclusive mode */
3948c2ecf20Sopenharmony_ci		if (dev_info->segment_type == SEG_TYPE_SC) {
3958c2ecf20Sopenharmony_ci			pr_err("DCSS %s is of type SC and cannot be "
3968c2ecf20Sopenharmony_ci			       "loaded as exclusive-writable\n",
3978c2ecf20Sopenharmony_ci			       dev_info->segment_name);
3988c2ecf20Sopenharmony_ci			rc = -EINVAL;
3998c2ecf20Sopenharmony_ci			goto out;
4008c2ecf20Sopenharmony_ci		}
4018c2ecf20Sopenharmony_ci		list_for_each_entry(entry, &dev_info->seg_list, lh) {
4028c2ecf20Sopenharmony_ci			rc = segment_modify_shared(entry->segment_name,
4038c2ecf20Sopenharmony_ci						   SEGMENT_EXCLUSIVE);
4048c2ecf20Sopenharmony_ci			if (rc < 0) {
4058c2ecf20Sopenharmony_ci				BUG_ON(rc == -EINVAL);
4068c2ecf20Sopenharmony_ci				if (rc != -EAGAIN)
4078c2ecf20Sopenharmony_ci					goto removeseg;
4088c2ecf20Sopenharmony_ci			}
4098c2ecf20Sopenharmony_ci		}
4108c2ecf20Sopenharmony_ci		dev_info->is_shared = 0;
4118c2ecf20Sopenharmony_ci		set_disk_ro(dev_info->gd, 0);
4128c2ecf20Sopenharmony_ci	} else {
4138c2ecf20Sopenharmony_ci		rc = -EINVAL;
4148c2ecf20Sopenharmony_ci		goto out;
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci	rc = count;
4178c2ecf20Sopenharmony_ci	goto out;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ciremoveseg:
4208c2ecf20Sopenharmony_ci	pr_err("DCSS device %s is removed after a failed access mode "
4218c2ecf20Sopenharmony_ci	       "change\n", dev_info->segment_name);
4228c2ecf20Sopenharmony_ci	temp = entry;
4238c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &dev_info->seg_list, lh) {
4248c2ecf20Sopenharmony_ci		if (entry != temp)
4258c2ecf20Sopenharmony_ci			segment_unload(entry->segment_name);
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci	list_del(&dev_info->lh);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	kill_dax(dev_info->dax_dev);
4308c2ecf20Sopenharmony_ci	put_dax(dev_info->dax_dev);
4318c2ecf20Sopenharmony_ci	del_gendisk(dev_info->gd);
4328c2ecf20Sopenharmony_ci	blk_cleanup_queue(dev_info->dcssblk_queue);
4338c2ecf20Sopenharmony_ci	dev_info->gd->queue = NULL;
4348c2ecf20Sopenharmony_ci	put_disk(dev_info->gd);
4358c2ecf20Sopenharmony_ci	up_write(&dcssblk_devices_sem);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (device_remove_file_self(dev, attr)) {
4388c2ecf20Sopenharmony_ci		device_unregister(dev);
4398c2ecf20Sopenharmony_ci		put_device(dev);
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci	return rc;
4428c2ecf20Sopenharmony_ciout:
4438c2ecf20Sopenharmony_ci	up_write(&dcssblk_devices_sem);
4448c2ecf20Sopenharmony_ci	return rc;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_cistatic DEVICE_ATTR(shared, S_IWUSR | S_IRUSR, dcssblk_shared_show,
4478c2ecf20Sopenharmony_ci		   dcssblk_shared_store);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci/*
4508c2ecf20Sopenharmony_ci * device attribute for save operation on current copy
4518c2ecf20Sopenharmony_ci * of the segment. If the segment is busy, saving will
4528c2ecf20Sopenharmony_ci * become pending until it gets released, which can be
4538c2ecf20Sopenharmony_ci * undone by storing a non-true value to this entry.
4548c2ecf20Sopenharmony_ci * (show + store)
4558c2ecf20Sopenharmony_ci */
4568c2ecf20Sopenharmony_cistatic ssize_t
4578c2ecf20Sopenharmony_cidcssblk_save_show(struct device *dev, struct device_attribute *attr, char *buf)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	dev_info = container_of(dev, struct dcssblk_dev_info, dev);
4628c2ecf20Sopenharmony_ci	return sprintf(buf, dev_info->save_pending ? "1\n" : "0\n");
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic ssize_t
4668c2ecf20Sopenharmony_cidcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
4698c2ecf20Sopenharmony_ci	struct segment_info *entry;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
4728c2ecf20Sopenharmony_ci		return -EINVAL;
4738c2ecf20Sopenharmony_ci	dev_info = container_of(dev, struct dcssblk_dev_info, dev);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	down_write(&dcssblk_devices_sem);
4768c2ecf20Sopenharmony_ci	if (inbuf[0] == '1') {
4778c2ecf20Sopenharmony_ci		if (atomic_read(&dev_info->use_count) == 0) {
4788c2ecf20Sopenharmony_ci			// device is idle => we save immediately
4798c2ecf20Sopenharmony_ci			pr_info("All DCSSs that map to device %s are "
4808c2ecf20Sopenharmony_ci				"saved\n", dev_info->segment_name);
4818c2ecf20Sopenharmony_ci			list_for_each_entry(entry, &dev_info->seg_list, lh) {
4828c2ecf20Sopenharmony_ci				if (entry->segment_type == SEG_TYPE_EN ||
4838c2ecf20Sopenharmony_ci				    entry->segment_type == SEG_TYPE_SN)
4848c2ecf20Sopenharmony_ci					pr_warn("DCSS %s is of type SN or EN"
4858c2ecf20Sopenharmony_ci						" and cannot be saved\n",
4868c2ecf20Sopenharmony_ci						entry->segment_name);
4878c2ecf20Sopenharmony_ci				else
4888c2ecf20Sopenharmony_ci					segment_save(entry->segment_name);
4898c2ecf20Sopenharmony_ci			}
4908c2ecf20Sopenharmony_ci		}  else {
4918c2ecf20Sopenharmony_ci			// device is busy => we save it when it becomes
4928c2ecf20Sopenharmony_ci			// idle in dcssblk_release
4938c2ecf20Sopenharmony_ci			pr_info("Device %s is in use, its DCSSs will be "
4948c2ecf20Sopenharmony_ci				"saved when it becomes idle\n",
4958c2ecf20Sopenharmony_ci				dev_info->segment_name);
4968c2ecf20Sopenharmony_ci			dev_info->save_pending = 1;
4978c2ecf20Sopenharmony_ci		}
4988c2ecf20Sopenharmony_ci	} else if (inbuf[0] == '0') {
4998c2ecf20Sopenharmony_ci		if (dev_info->save_pending) {
5008c2ecf20Sopenharmony_ci			// device is busy & the user wants to undo his save
5018c2ecf20Sopenharmony_ci			// request
5028c2ecf20Sopenharmony_ci			dev_info->save_pending = 0;
5038c2ecf20Sopenharmony_ci			pr_info("A pending save request for device %s "
5048c2ecf20Sopenharmony_ci				"has been canceled\n",
5058c2ecf20Sopenharmony_ci				dev_info->segment_name);
5068c2ecf20Sopenharmony_ci		}
5078c2ecf20Sopenharmony_ci	} else {
5088c2ecf20Sopenharmony_ci		up_write(&dcssblk_devices_sem);
5098c2ecf20Sopenharmony_ci		return -EINVAL;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci	up_write(&dcssblk_devices_sem);
5128c2ecf20Sopenharmony_ci	return count;
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_cistatic DEVICE_ATTR(save, S_IWUSR | S_IRUSR, dcssblk_save_show,
5158c2ecf20Sopenharmony_ci		   dcssblk_save_store);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci/*
5188c2ecf20Sopenharmony_ci * device attribute for showing all segments in a device
5198c2ecf20Sopenharmony_ci */
5208c2ecf20Sopenharmony_cistatic ssize_t
5218c2ecf20Sopenharmony_cidcssblk_seglist_show(struct device *dev, struct device_attribute *attr,
5228c2ecf20Sopenharmony_ci		char *buf)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	int i;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
5278c2ecf20Sopenharmony_ci	struct segment_info *entry;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	down_read(&dcssblk_devices_sem);
5308c2ecf20Sopenharmony_ci	dev_info = container_of(dev, struct dcssblk_dev_info, dev);
5318c2ecf20Sopenharmony_ci	i = 0;
5328c2ecf20Sopenharmony_ci	buf[0] = '\0';
5338c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &dev_info->seg_list, lh) {
5348c2ecf20Sopenharmony_ci		strcpy(&buf[i], entry->segment_name);
5358c2ecf20Sopenharmony_ci		i += strlen(entry->segment_name);
5368c2ecf20Sopenharmony_ci		buf[i] = '\n';
5378c2ecf20Sopenharmony_ci		i++;
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci	up_read(&dcssblk_devices_sem);
5408c2ecf20Sopenharmony_ci	return i;
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_cistatic DEVICE_ATTR(seglist, S_IRUSR, dcssblk_seglist_show, NULL);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic struct attribute *dcssblk_dev_attrs[] = {
5458c2ecf20Sopenharmony_ci	&dev_attr_shared.attr,
5468c2ecf20Sopenharmony_ci	&dev_attr_save.attr,
5478c2ecf20Sopenharmony_ci	&dev_attr_seglist.attr,
5488c2ecf20Sopenharmony_ci	NULL,
5498c2ecf20Sopenharmony_ci};
5508c2ecf20Sopenharmony_cistatic struct attribute_group dcssblk_dev_attr_group = {
5518c2ecf20Sopenharmony_ci	.attrs = dcssblk_dev_attrs,
5528c2ecf20Sopenharmony_ci};
5538c2ecf20Sopenharmony_cistatic const struct attribute_group *dcssblk_dev_attr_groups[] = {
5548c2ecf20Sopenharmony_ci	&dcssblk_dev_attr_group,
5558c2ecf20Sopenharmony_ci	NULL,
5568c2ecf20Sopenharmony_ci};
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci/*
5598c2ecf20Sopenharmony_ci * device attribute for adding devices
5608c2ecf20Sopenharmony_ci */
5618c2ecf20Sopenharmony_cistatic ssize_t
5628c2ecf20Sopenharmony_cidcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	int rc, i, j, num_of_segments;
5658c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
5668c2ecf20Sopenharmony_ci	struct segment_info *seg_info, *temp;
5678c2ecf20Sopenharmony_ci	char *local_buf;
5688c2ecf20Sopenharmony_ci	unsigned long seg_byte_size;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	dev_info = NULL;
5718c2ecf20Sopenharmony_ci	seg_info = NULL;
5728c2ecf20Sopenharmony_ci	if (dev != dcssblk_root_dev) {
5738c2ecf20Sopenharmony_ci		rc = -EINVAL;
5748c2ecf20Sopenharmony_ci		goto out_nobuf;
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci	if ((count < 1) || (buf[0] == '\0') || (buf[0] == '\n')) {
5778c2ecf20Sopenharmony_ci		rc = -ENAMETOOLONG;
5788c2ecf20Sopenharmony_ci		goto out_nobuf;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	local_buf = kmalloc(count + 1, GFP_KERNEL);
5828c2ecf20Sopenharmony_ci	if (local_buf == NULL) {
5838c2ecf20Sopenharmony_ci		rc = -ENOMEM;
5848c2ecf20Sopenharmony_ci		goto out_nobuf;
5858c2ecf20Sopenharmony_ci	}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	/*
5888c2ecf20Sopenharmony_ci	 * parse input
5898c2ecf20Sopenharmony_ci	 */
5908c2ecf20Sopenharmony_ci	num_of_segments = 0;
5918c2ecf20Sopenharmony_ci	for (i = 0; (i < count && (buf[i] != '\0') && (buf[i] != '\n')); i++) {
5928c2ecf20Sopenharmony_ci		for (j = i; j < count &&
5938c2ecf20Sopenharmony_ci			(buf[j] != ':') &&
5948c2ecf20Sopenharmony_ci			(buf[j] != '\0') &&
5958c2ecf20Sopenharmony_ci			(buf[j] != '\n'); j++) {
5968c2ecf20Sopenharmony_ci			local_buf[j-i] = toupper(buf[j]);
5978c2ecf20Sopenharmony_ci		}
5988c2ecf20Sopenharmony_ci		local_buf[j-i] = '\0';
5998c2ecf20Sopenharmony_ci		if (((j - i) == 0) || ((j - i) > 8)) {
6008c2ecf20Sopenharmony_ci			rc = -ENAMETOOLONG;
6018c2ecf20Sopenharmony_ci			goto seg_list_del;
6028c2ecf20Sopenharmony_ci		}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci		rc = dcssblk_load_segment(local_buf, &seg_info);
6058c2ecf20Sopenharmony_ci		if (rc < 0)
6068c2ecf20Sopenharmony_ci			goto seg_list_del;
6078c2ecf20Sopenharmony_ci		/*
6088c2ecf20Sopenharmony_ci		 * get a struct dcssblk_dev_info
6098c2ecf20Sopenharmony_ci		 */
6108c2ecf20Sopenharmony_ci		if (num_of_segments == 0) {
6118c2ecf20Sopenharmony_ci			dev_info = kzalloc(sizeof(struct dcssblk_dev_info),
6128c2ecf20Sopenharmony_ci					GFP_KERNEL);
6138c2ecf20Sopenharmony_ci			if (dev_info == NULL) {
6148c2ecf20Sopenharmony_ci				rc = -ENOMEM;
6158c2ecf20Sopenharmony_ci				goto out;
6168c2ecf20Sopenharmony_ci			}
6178c2ecf20Sopenharmony_ci			strcpy(dev_info->segment_name, local_buf);
6188c2ecf20Sopenharmony_ci			dev_info->segment_type = seg_info->segment_type;
6198c2ecf20Sopenharmony_ci			INIT_LIST_HEAD(&dev_info->seg_list);
6208c2ecf20Sopenharmony_ci		}
6218c2ecf20Sopenharmony_ci		list_add_tail(&seg_info->lh, &dev_info->seg_list);
6228c2ecf20Sopenharmony_ci		num_of_segments++;
6238c2ecf20Sopenharmony_ci		i = j;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci		if ((buf[j] == '\0') || (buf[j] == '\n'))
6268c2ecf20Sopenharmony_ci			break;
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	/* no trailing colon at the end of the input */
6308c2ecf20Sopenharmony_ci	if ((i > 0) && (buf[i-1] == ':')) {
6318c2ecf20Sopenharmony_ci		rc = -ENAMETOOLONG;
6328c2ecf20Sopenharmony_ci		goto seg_list_del;
6338c2ecf20Sopenharmony_ci	}
6348c2ecf20Sopenharmony_ci	strlcpy(local_buf, buf, i + 1);
6358c2ecf20Sopenharmony_ci	dev_info->num_of_segments = num_of_segments;
6368c2ecf20Sopenharmony_ci	rc = dcssblk_is_continuous(dev_info);
6378c2ecf20Sopenharmony_ci	if (rc < 0)
6388c2ecf20Sopenharmony_ci		goto seg_list_del;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	dev_info->start = dcssblk_find_lowest_addr(dev_info);
6418c2ecf20Sopenharmony_ci	dev_info->end = dcssblk_find_highest_addr(dev_info);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	dev_set_name(&dev_info->dev, "%s", dev_info->segment_name);
6448c2ecf20Sopenharmony_ci	dev_info->dev.release = dcssblk_release_segment;
6458c2ecf20Sopenharmony_ci	dev_info->dev.groups = dcssblk_dev_attr_groups;
6468c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dev_info->lh);
6478c2ecf20Sopenharmony_ci	dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK);
6488c2ecf20Sopenharmony_ci	if (dev_info->gd == NULL) {
6498c2ecf20Sopenharmony_ci		rc = -ENOMEM;
6508c2ecf20Sopenharmony_ci		goto seg_list_del;
6518c2ecf20Sopenharmony_ci	}
6528c2ecf20Sopenharmony_ci	dev_info->gd->major = dcssblk_major;
6538c2ecf20Sopenharmony_ci	dev_info->gd->fops = &dcssblk_devops;
6548c2ecf20Sopenharmony_ci	dev_info->dcssblk_queue = blk_alloc_queue(NUMA_NO_NODE);
6558c2ecf20Sopenharmony_ci	dev_info->gd->queue = dev_info->dcssblk_queue;
6568c2ecf20Sopenharmony_ci	dev_info->gd->private_data = dev_info;
6578c2ecf20Sopenharmony_ci	blk_queue_logical_block_size(dev_info->dcssblk_queue, 4096);
6588c2ecf20Sopenharmony_ci	blk_queue_flag_set(QUEUE_FLAG_DAX, dev_info->dcssblk_queue);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	seg_byte_size = (dev_info->end - dev_info->start + 1);
6618c2ecf20Sopenharmony_ci	set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
6628c2ecf20Sopenharmony_ci	pr_info("Loaded %s with total size %lu bytes and capacity %lu "
6638c2ecf20Sopenharmony_ci		"sectors\n", local_buf, seg_byte_size, seg_byte_size >> 9);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	dev_info->save_pending = 0;
6668c2ecf20Sopenharmony_ci	dev_info->is_shared = 1;
6678c2ecf20Sopenharmony_ci	dev_info->dev.parent = dcssblk_root_dev;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	/*
6708c2ecf20Sopenharmony_ci	 *get minor, add to list
6718c2ecf20Sopenharmony_ci	 */
6728c2ecf20Sopenharmony_ci	down_write(&dcssblk_devices_sem);
6738c2ecf20Sopenharmony_ci	if (dcssblk_get_segment_by_name(local_buf)) {
6748c2ecf20Sopenharmony_ci		rc = -EEXIST;
6758c2ecf20Sopenharmony_ci		goto release_gd;
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci	rc = dcssblk_assign_free_minor(dev_info);
6788c2ecf20Sopenharmony_ci	if (rc)
6798c2ecf20Sopenharmony_ci		goto release_gd;
6808c2ecf20Sopenharmony_ci	sprintf(dev_info->gd->disk_name, "dcssblk%d",
6818c2ecf20Sopenharmony_ci		dev_info->gd->first_minor);
6828c2ecf20Sopenharmony_ci	list_add_tail(&dev_info->lh, &dcssblk_devices);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (!try_module_get(THIS_MODULE)) {
6858c2ecf20Sopenharmony_ci		rc = -ENODEV;
6868c2ecf20Sopenharmony_ci		goto dev_list_del;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci	/*
6898c2ecf20Sopenharmony_ci	 * register the device
6908c2ecf20Sopenharmony_ci	 */
6918c2ecf20Sopenharmony_ci	rc = device_register(&dev_info->dev);
6928c2ecf20Sopenharmony_ci	if (rc)
6938c2ecf20Sopenharmony_ci		goto put_dev;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	dev_info->dax_dev = alloc_dax(dev_info, dev_info->gd->disk_name,
6968c2ecf20Sopenharmony_ci			&dcssblk_dax_ops, DAXDEV_F_SYNC);
6978c2ecf20Sopenharmony_ci	if (IS_ERR(dev_info->dax_dev)) {
6988c2ecf20Sopenharmony_ci		rc = PTR_ERR(dev_info->dax_dev);
6998c2ecf20Sopenharmony_ci		dev_info->dax_dev = NULL;
7008c2ecf20Sopenharmony_ci		goto put_dev;
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	get_device(&dev_info->dev);
7048c2ecf20Sopenharmony_ci	device_add_disk(&dev_info->dev, dev_info->gd, NULL);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	switch (dev_info->segment_type) {
7078c2ecf20Sopenharmony_ci		case SEG_TYPE_SR:
7088c2ecf20Sopenharmony_ci		case SEG_TYPE_ER:
7098c2ecf20Sopenharmony_ci		case SEG_TYPE_SC:
7108c2ecf20Sopenharmony_ci			set_disk_ro(dev_info->gd,1);
7118c2ecf20Sopenharmony_ci			break;
7128c2ecf20Sopenharmony_ci		default:
7138c2ecf20Sopenharmony_ci			set_disk_ro(dev_info->gd,0);
7148c2ecf20Sopenharmony_ci			break;
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci	up_write(&dcssblk_devices_sem);
7178c2ecf20Sopenharmony_ci	rc = count;
7188c2ecf20Sopenharmony_ci	goto out;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ciput_dev:
7218c2ecf20Sopenharmony_ci	list_del(&dev_info->lh);
7228c2ecf20Sopenharmony_ci	blk_cleanup_queue(dev_info->dcssblk_queue);
7238c2ecf20Sopenharmony_ci	dev_info->gd->queue = NULL;
7248c2ecf20Sopenharmony_ci	put_disk(dev_info->gd);
7258c2ecf20Sopenharmony_ci	list_for_each_entry(seg_info, &dev_info->seg_list, lh) {
7268c2ecf20Sopenharmony_ci		segment_unload(seg_info->segment_name);
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci	put_device(&dev_info->dev);
7298c2ecf20Sopenharmony_ci	up_write(&dcssblk_devices_sem);
7308c2ecf20Sopenharmony_ci	goto out;
7318c2ecf20Sopenharmony_cidev_list_del:
7328c2ecf20Sopenharmony_ci	list_del(&dev_info->lh);
7338c2ecf20Sopenharmony_cirelease_gd:
7348c2ecf20Sopenharmony_ci	blk_cleanup_queue(dev_info->dcssblk_queue);
7358c2ecf20Sopenharmony_ci	dev_info->gd->queue = NULL;
7368c2ecf20Sopenharmony_ci	put_disk(dev_info->gd);
7378c2ecf20Sopenharmony_ci	up_write(&dcssblk_devices_sem);
7388c2ecf20Sopenharmony_ciseg_list_del:
7398c2ecf20Sopenharmony_ci	if (dev_info == NULL)
7408c2ecf20Sopenharmony_ci		goto out;
7418c2ecf20Sopenharmony_ci	list_for_each_entry_safe(seg_info, temp, &dev_info->seg_list, lh) {
7428c2ecf20Sopenharmony_ci		list_del(&seg_info->lh);
7438c2ecf20Sopenharmony_ci		segment_unload(seg_info->segment_name);
7448c2ecf20Sopenharmony_ci		kfree(seg_info);
7458c2ecf20Sopenharmony_ci	}
7468c2ecf20Sopenharmony_ci	kfree(dev_info);
7478c2ecf20Sopenharmony_ciout:
7488c2ecf20Sopenharmony_ci	kfree(local_buf);
7498c2ecf20Sopenharmony_ciout_nobuf:
7508c2ecf20Sopenharmony_ci	return rc;
7518c2ecf20Sopenharmony_ci}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci/*
7548c2ecf20Sopenharmony_ci * device attribute for removing devices
7558c2ecf20Sopenharmony_ci */
7568c2ecf20Sopenharmony_cistatic ssize_t
7578c2ecf20Sopenharmony_cidcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
7608c2ecf20Sopenharmony_ci	struct segment_info *entry;
7618c2ecf20Sopenharmony_ci	int rc, i;
7628c2ecf20Sopenharmony_ci	char *local_buf;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	if (dev != dcssblk_root_dev) {
7658c2ecf20Sopenharmony_ci		return -EINVAL;
7668c2ecf20Sopenharmony_ci	}
7678c2ecf20Sopenharmony_ci	local_buf = kmalloc(count + 1, GFP_KERNEL);
7688c2ecf20Sopenharmony_ci	if (local_buf == NULL) {
7698c2ecf20Sopenharmony_ci		return -ENOMEM;
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci	/*
7728c2ecf20Sopenharmony_ci	 * parse input
7738c2ecf20Sopenharmony_ci	 */
7748c2ecf20Sopenharmony_ci	for (i = 0; (i < count && (*(buf+i)!='\0') && (*(buf+i)!='\n')); i++) {
7758c2ecf20Sopenharmony_ci		local_buf[i] = toupper(buf[i]);
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci	local_buf[i] = '\0';
7788c2ecf20Sopenharmony_ci	if ((i == 0) || (i > 8)) {
7798c2ecf20Sopenharmony_ci		rc = -ENAMETOOLONG;
7808c2ecf20Sopenharmony_ci		goto out_buf;
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	down_write(&dcssblk_devices_sem);
7848c2ecf20Sopenharmony_ci	dev_info = dcssblk_get_device_by_name(local_buf);
7858c2ecf20Sopenharmony_ci	if (dev_info == NULL) {
7868c2ecf20Sopenharmony_ci		up_write(&dcssblk_devices_sem);
7878c2ecf20Sopenharmony_ci		pr_warn("Device %s cannot be removed because it is not a known device\n",
7888c2ecf20Sopenharmony_ci			local_buf);
7898c2ecf20Sopenharmony_ci		rc = -ENODEV;
7908c2ecf20Sopenharmony_ci		goto out_buf;
7918c2ecf20Sopenharmony_ci	}
7928c2ecf20Sopenharmony_ci	if (atomic_read(&dev_info->use_count) != 0) {
7938c2ecf20Sopenharmony_ci		up_write(&dcssblk_devices_sem);
7948c2ecf20Sopenharmony_ci		pr_warn("Device %s cannot be removed while it is in use\n",
7958c2ecf20Sopenharmony_ci			local_buf);
7968c2ecf20Sopenharmony_ci		rc = -EBUSY;
7978c2ecf20Sopenharmony_ci		goto out_buf;
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	list_del(&dev_info->lh);
8018c2ecf20Sopenharmony_ci	kill_dax(dev_info->dax_dev);
8028c2ecf20Sopenharmony_ci	put_dax(dev_info->dax_dev);
8038c2ecf20Sopenharmony_ci	del_gendisk(dev_info->gd);
8048c2ecf20Sopenharmony_ci	blk_cleanup_queue(dev_info->dcssblk_queue);
8058c2ecf20Sopenharmony_ci	dev_info->gd->queue = NULL;
8068c2ecf20Sopenharmony_ci	put_disk(dev_info->gd);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	/* unload all related segments */
8098c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &dev_info->seg_list, lh)
8108c2ecf20Sopenharmony_ci		segment_unload(entry->segment_name);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	up_write(&dcssblk_devices_sem);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	device_unregister(&dev_info->dev);
8158c2ecf20Sopenharmony_ci	put_device(&dev_info->dev);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	rc = count;
8188c2ecf20Sopenharmony_ciout_buf:
8198c2ecf20Sopenharmony_ci	kfree(local_buf);
8208c2ecf20Sopenharmony_ci	return rc;
8218c2ecf20Sopenharmony_ci}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_cistatic int
8248c2ecf20Sopenharmony_cidcssblk_open(struct block_device *bdev, fmode_t mode)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
8278c2ecf20Sopenharmony_ci	int rc;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	dev_info = bdev->bd_disk->private_data;
8308c2ecf20Sopenharmony_ci	if (NULL == dev_info) {
8318c2ecf20Sopenharmony_ci		rc = -ENODEV;
8328c2ecf20Sopenharmony_ci		goto out;
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci	atomic_inc(&dev_info->use_count);
8358c2ecf20Sopenharmony_ci	rc = 0;
8368c2ecf20Sopenharmony_ciout:
8378c2ecf20Sopenharmony_ci	return rc;
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_cistatic void
8418c2ecf20Sopenharmony_cidcssblk_release(struct gendisk *disk, fmode_t mode)
8428c2ecf20Sopenharmony_ci{
8438c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info = disk->private_data;
8448c2ecf20Sopenharmony_ci	struct segment_info *entry;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	if (!dev_info) {
8478c2ecf20Sopenharmony_ci		WARN_ON(1);
8488c2ecf20Sopenharmony_ci		return;
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci	down_write(&dcssblk_devices_sem);
8518c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&dev_info->use_count)
8528c2ecf20Sopenharmony_ci	    && (dev_info->save_pending)) {
8538c2ecf20Sopenharmony_ci		pr_info("Device %s has become idle and is being saved "
8548c2ecf20Sopenharmony_ci			"now\n", dev_info->segment_name);
8558c2ecf20Sopenharmony_ci		list_for_each_entry(entry, &dev_info->seg_list, lh) {
8568c2ecf20Sopenharmony_ci			if (entry->segment_type == SEG_TYPE_EN ||
8578c2ecf20Sopenharmony_ci			    entry->segment_type == SEG_TYPE_SN)
8588c2ecf20Sopenharmony_ci				pr_warn("DCSS %s is of type SN or EN and cannot"
8598c2ecf20Sopenharmony_ci					" be saved\n", entry->segment_name);
8608c2ecf20Sopenharmony_ci			else
8618c2ecf20Sopenharmony_ci				segment_save(entry->segment_name);
8628c2ecf20Sopenharmony_ci		}
8638c2ecf20Sopenharmony_ci		dev_info->save_pending = 0;
8648c2ecf20Sopenharmony_ci	}
8658c2ecf20Sopenharmony_ci	up_write(&dcssblk_devices_sem);
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_cistatic blk_qc_t
8698c2ecf20Sopenharmony_cidcssblk_submit_bio(struct bio *bio)
8708c2ecf20Sopenharmony_ci{
8718c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
8728c2ecf20Sopenharmony_ci	struct bio_vec bvec;
8738c2ecf20Sopenharmony_ci	struct bvec_iter iter;
8748c2ecf20Sopenharmony_ci	unsigned long index;
8758c2ecf20Sopenharmony_ci	unsigned long page_addr;
8768c2ecf20Sopenharmony_ci	unsigned long source_addr;
8778c2ecf20Sopenharmony_ci	unsigned long bytes_done;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	blk_queue_split(&bio);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	bytes_done = 0;
8828c2ecf20Sopenharmony_ci	dev_info = bio->bi_disk->private_data;
8838c2ecf20Sopenharmony_ci	if (dev_info == NULL)
8848c2ecf20Sopenharmony_ci		goto fail;
8858c2ecf20Sopenharmony_ci	if ((bio->bi_iter.bi_sector & 7) != 0 ||
8868c2ecf20Sopenharmony_ci	    (bio->bi_iter.bi_size & 4095) != 0)
8878c2ecf20Sopenharmony_ci		/* Request is not page-aligned. */
8888c2ecf20Sopenharmony_ci		goto fail;
8898c2ecf20Sopenharmony_ci	if (bio_end_sector(bio) > get_capacity(bio->bi_disk)) {
8908c2ecf20Sopenharmony_ci		/* Request beyond end of DCSS segment. */
8918c2ecf20Sopenharmony_ci		goto fail;
8928c2ecf20Sopenharmony_ci	}
8938c2ecf20Sopenharmony_ci	/* verify data transfer direction */
8948c2ecf20Sopenharmony_ci	if (dev_info->is_shared) {
8958c2ecf20Sopenharmony_ci		switch (dev_info->segment_type) {
8968c2ecf20Sopenharmony_ci		case SEG_TYPE_SR:
8978c2ecf20Sopenharmony_ci		case SEG_TYPE_ER:
8988c2ecf20Sopenharmony_ci		case SEG_TYPE_SC:
8998c2ecf20Sopenharmony_ci			/* cannot write to these segments */
9008c2ecf20Sopenharmony_ci			if (bio_data_dir(bio) == WRITE) {
9018c2ecf20Sopenharmony_ci				pr_warn("Writing to %s failed because it is a read-only device\n",
9028c2ecf20Sopenharmony_ci					dev_name(&dev_info->dev));
9038c2ecf20Sopenharmony_ci				goto fail;
9048c2ecf20Sopenharmony_ci			}
9058c2ecf20Sopenharmony_ci		}
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	index = (bio->bi_iter.bi_sector >> 3);
9098c2ecf20Sopenharmony_ci	bio_for_each_segment(bvec, bio, iter) {
9108c2ecf20Sopenharmony_ci		page_addr = (unsigned long)
9118c2ecf20Sopenharmony_ci			page_address(bvec.bv_page) + bvec.bv_offset;
9128c2ecf20Sopenharmony_ci		source_addr = dev_info->start + (index<<12) + bytes_done;
9138c2ecf20Sopenharmony_ci		if (unlikely((page_addr & 4095) != 0) || (bvec.bv_len & 4095) != 0)
9148c2ecf20Sopenharmony_ci			// More paranoia.
9158c2ecf20Sopenharmony_ci			goto fail;
9168c2ecf20Sopenharmony_ci		if (bio_data_dir(bio) == READ) {
9178c2ecf20Sopenharmony_ci			memcpy((void*)page_addr, (void*)source_addr,
9188c2ecf20Sopenharmony_ci				bvec.bv_len);
9198c2ecf20Sopenharmony_ci		} else {
9208c2ecf20Sopenharmony_ci			memcpy((void*)source_addr, (void*)page_addr,
9218c2ecf20Sopenharmony_ci				bvec.bv_len);
9228c2ecf20Sopenharmony_ci		}
9238c2ecf20Sopenharmony_ci		bytes_done += bvec.bv_len;
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci	bio_endio(bio);
9268c2ecf20Sopenharmony_ci	return BLK_QC_T_NONE;
9278c2ecf20Sopenharmony_cifail:
9288c2ecf20Sopenharmony_ci	bio_io_error(bio);
9298c2ecf20Sopenharmony_ci	return BLK_QC_T_NONE;
9308c2ecf20Sopenharmony_ci}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_cistatic long
9338c2ecf20Sopenharmony_ci__dcssblk_direct_access(struct dcssblk_dev_info *dev_info, pgoff_t pgoff,
9348c2ecf20Sopenharmony_ci		long nr_pages, void **kaddr, pfn_t *pfn)
9358c2ecf20Sopenharmony_ci{
9368c2ecf20Sopenharmony_ci	resource_size_t offset = pgoff * PAGE_SIZE;
9378c2ecf20Sopenharmony_ci	unsigned long dev_sz;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	dev_sz = dev_info->end - dev_info->start + 1;
9408c2ecf20Sopenharmony_ci	if (kaddr)
9418c2ecf20Sopenharmony_ci		*kaddr = (void *) dev_info->start + offset;
9428c2ecf20Sopenharmony_ci	if (pfn)
9438c2ecf20Sopenharmony_ci		*pfn = __pfn_to_pfn_t(PFN_DOWN(dev_info->start + offset),
9448c2ecf20Sopenharmony_ci				PFN_DEV|PFN_SPECIAL);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	return (dev_sz - offset) / PAGE_SIZE;
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_cistatic long
9508c2ecf20Sopenharmony_cidcssblk_dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff,
9518c2ecf20Sopenharmony_ci		long nr_pages, void **kaddr, pfn_t *pfn)
9528c2ecf20Sopenharmony_ci{
9538c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info = dax_get_private(dax_dev);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	return __dcssblk_direct_access(dev_info, pgoff, nr_pages, kaddr, pfn);
9568c2ecf20Sopenharmony_ci}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_cistatic void
9598c2ecf20Sopenharmony_cidcssblk_check_params(void)
9608c2ecf20Sopenharmony_ci{
9618c2ecf20Sopenharmony_ci	int rc, i, j, k;
9628c2ecf20Sopenharmony_ci	char buf[DCSSBLK_PARM_LEN + 1];
9638c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0');
9668c2ecf20Sopenharmony_ci	     i++) {
9678c2ecf20Sopenharmony_ci		for (j = i; (j < DCSSBLK_PARM_LEN) &&
9688c2ecf20Sopenharmony_ci			    (dcssblk_segments[j] != ',')  &&
9698c2ecf20Sopenharmony_ci			    (dcssblk_segments[j] != '\0') &&
9708c2ecf20Sopenharmony_ci			    (dcssblk_segments[j] != '('); j++)
9718c2ecf20Sopenharmony_ci		{
9728c2ecf20Sopenharmony_ci			buf[j-i] = dcssblk_segments[j];
9738c2ecf20Sopenharmony_ci		}
9748c2ecf20Sopenharmony_ci		buf[j-i] = '\0';
9758c2ecf20Sopenharmony_ci		rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i);
9768c2ecf20Sopenharmony_ci		if ((rc >= 0) && (dcssblk_segments[j] == '(')) {
9778c2ecf20Sopenharmony_ci			for (k = 0; (buf[k] != ':') && (buf[k] != '\0'); k++)
9788c2ecf20Sopenharmony_ci				buf[k] = toupper(buf[k]);
9798c2ecf20Sopenharmony_ci			buf[k] = '\0';
9808c2ecf20Sopenharmony_ci			if (!strncmp(&dcssblk_segments[j], "(local)", 7)) {
9818c2ecf20Sopenharmony_ci				down_read(&dcssblk_devices_sem);
9828c2ecf20Sopenharmony_ci				dev_info = dcssblk_get_device_by_name(buf);
9838c2ecf20Sopenharmony_ci				up_read(&dcssblk_devices_sem);
9848c2ecf20Sopenharmony_ci				if (dev_info)
9858c2ecf20Sopenharmony_ci					dcssblk_shared_store(&dev_info->dev,
9868c2ecf20Sopenharmony_ci							     NULL, "0\n", 2);
9878c2ecf20Sopenharmony_ci			}
9888c2ecf20Sopenharmony_ci		}
9898c2ecf20Sopenharmony_ci		while ((dcssblk_segments[j] != ',') &&
9908c2ecf20Sopenharmony_ci		       (dcssblk_segments[j] != '\0'))
9918c2ecf20Sopenharmony_ci		{
9928c2ecf20Sopenharmony_ci			j++;
9938c2ecf20Sopenharmony_ci		}
9948c2ecf20Sopenharmony_ci		if (dcssblk_segments[j] == '\0')
9958c2ecf20Sopenharmony_ci			break;
9968c2ecf20Sopenharmony_ci		i = j;
9978c2ecf20Sopenharmony_ci	}
9988c2ecf20Sopenharmony_ci}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci/*
10018c2ecf20Sopenharmony_ci * Suspend / Resume
10028c2ecf20Sopenharmony_ci */
10038c2ecf20Sopenharmony_cistatic int dcssblk_freeze(struct device *dev)
10048c2ecf20Sopenharmony_ci{
10058c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
10068c2ecf20Sopenharmony_ci	int rc = 0;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	list_for_each_entry(dev_info, &dcssblk_devices, lh) {
10098c2ecf20Sopenharmony_ci		switch (dev_info->segment_type) {
10108c2ecf20Sopenharmony_ci			case SEG_TYPE_SR:
10118c2ecf20Sopenharmony_ci			case SEG_TYPE_ER:
10128c2ecf20Sopenharmony_ci			case SEG_TYPE_SC:
10138c2ecf20Sopenharmony_ci				if (!dev_info->is_shared)
10148c2ecf20Sopenharmony_ci					rc = -EINVAL;
10158c2ecf20Sopenharmony_ci				break;
10168c2ecf20Sopenharmony_ci			default:
10178c2ecf20Sopenharmony_ci				rc = -EINVAL;
10188c2ecf20Sopenharmony_ci				break;
10198c2ecf20Sopenharmony_ci		}
10208c2ecf20Sopenharmony_ci		if (rc)
10218c2ecf20Sopenharmony_ci			break;
10228c2ecf20Sopenharmony_ci	}
10238c2ecf20Sopenharmony_ci	if (rc)
10248c2ecf20Sopenharmony_ci		pr_err("Suspending the system failed because DCSS device %s "
10258c2ecf20Sopenharmony_ci		       "is writable\n",
10268c2ecf20Sopenharmony_ci		       dev_info->segment_name);
10278c2ecf20Sopenharmony_ci	return rc;
10288c2ecf20Sopenharmony_ci}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_cistatic int dcssblk_restore(struct device *dev)
10318c2ecf20Sopenharmony_ci{
10328c2ecf20Sopenharmony_ci	struct dcssblk_dev_info *dev_info;
10338c2ecf20Sopenharmony_ci	struct segment_info *entry;
10348c2ecf20Sopenharmony_ci	unsigned long start, end;
10358c2ecf20Sopenharmony_ci	int rc = 0;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	list_for_each_entry(dev_info, &dcssblk_devices, lh) {
10388c2ecf20Sopenharmony_ci		list_for_each_entry(entry, &dev_info->seg_list, lh) {
10398c2ecf20Sopenharmony_ci			segment_unload(entry->segment_name);
10408c2ecf20Sopenharmony_ci			rc = segment_load(entry->segment_name, SEGMENT_SHARED,
10418c2ecf20Sopenharmony_ci					  &start, &end);
10428c2ecf20Sopenharmony_ci			if (rc < 0) {
10438c2ecf20Sopenharmony_ci// TODO in_use check ?
10448c2ecf20Sopenharmony_ci				segment_warning(rc, entry->segment_name);
10458c2ecf20Sopenharmony_ci				goto out_panic;
10468c2ecf20Sopenharmony_ci			}
10478c2ecf20Sopenharmony_ci			if (start != entry->start || end != entry->end) {
10488c2ecf20Sopenharmony_ci				pr_err("The address range of DCSS %s changed "
10498c2ecf20Sopenharmony_ci				       "while the system was suspended\n",
10508c2ecf20Sopenharmony_ci				       entry->segment_name);
10518c2ecf20Sopenharmony_ci				goto out_panic;
10528c2ecf20Sopenharmony_ci			}
10538c2ecf20Sopenharmony_ci		}
10548c2ecf20Sopenharmony_ci	}
10558c2ecf20Sopenharmony_ci	return 0;
10568c2ecf20Sopenharmony_ciout_panic:
10578c2ecf20Sopenharmony_ci	panic("fatal dcssblk resume error\n");
10588c2ecf20Sopenharmony_ci}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_cistatic int dcssblk_thaw(struct device *dev)
10618c2ecf20Sopenharmony_ci{
10628c2ecf20Sopenharmony_ci	return 0;
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistatic const struct dev_pm_ops dcssblk_pm_ops = {
10668c2ecf20Sopenharmony_ci	.freeze		= dcssblk_freeze,
10678c2ecf20Sopenharmony_ci	.thaw		= dcssblk_thaw,
10688c2ecf20Sopenharmony_ci	.restore	= dcssblk_restore,
10698c2ecf20Sopenharmony_ci};
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_cistatic struct platform_driver dcssblk_pdrv = {
10728c2ecf20Sopenharmony_ci	.driver = {
10738c2ecf20Sopenharmony_ci		.name	= "dcssblk",
10748c2ecf20Sopenharmony_ci		.pm	= &dcssblk_pm_ops,
10758c2ecf20Sopenharmony_ci	},
10768c2ecf20Sopenharmony_ci};
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_cistatic struct platform_device *dcssblk_pdev;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci/*
10828c2ecf20Sopenharmony_ci * The init/exit functions.
10838c2ecf20Sopenharmony_ci */
10848c2ecf20Sopenharmony_cistatic void __exit
10858c2ecf20Sopenharmony_cidcssblk_exit(void)
10868c2ecf20Sopenharmony_ci{
10878c2ecf20Sopenharmony_ci	platform_device_unregister(dcssblk_pdev);
10888c2ecf20Sopenharmony_ci	platform_driver_unregister(&dcssblk_pdrv);
10898c2ecf20Sopenharmony_ci	root_device_unregister(dcssblk_root_dev);
10908c2ecf20Sopenharmony_ci	unregister_blkdev(dcssblk_major, DCSSBLK_NAME);
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_cistatic int __init
10948c2ecf20Sopenharmony_cidcssblk_init(void)
10958c2ecf20Sopenharmony_ci{
10968c2ecf20Sopenharmony_ci	int rc;
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	rc = platform_driver_register(&dcssblk_pdrv);
10998c2ecf20Sopenharmony_ci	if (rc)
11008c2ecf20Sopenharmony_ci		return rc;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	dcssblk_pdev = platform_device_register_simple("dcssblk", -1, NULL,
11038c2ecf20Sopenharmony_ci							0);
11048c2ecf20Sopenharmony_ci	if (IS_ERR(dcssblk_pdev)) {
11058c2ecf20Sopenharmony_ci		rc = PTR_ERR(dcssblk_pdev);
11068c2ecf20Sopenharmony_ci		goto out_pdrv;
11078c2ecf20Sopenharmony_ci	}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	dcssblk_root_dev = root_device_register("dcssblk");
11108c2ecf20Sopenharmony_ci	if (IS_ERR(dcssblk_root_dev)) {
11118c2ecf20Sopenharmony_ci		rc = PTR_ERR(dcssblk_root_dev);
11128c2ecf20Sopenharmony_ci		goto out_pdev;
11138c2ecf20Sopenharmony_ci	}
11148c2ecf20Sopenharmony_ci	rc = device_create_file(dcssblk_root_dev, &dev_attr_add);
11158c2ecf20Sopenharmony_ci	if (rc)
11168c2ecf20Sopenharmony_ci		goto out_root;
11178c2ecf20Sopenharmony_ci	rc = device_create_file(dcssblk_root_dev, &dev_attr_remove);
11188c2ecf20Sopenharmony_ci	if (rc)
11198c2ecf20Sopenharmony_ci		goto out_root;
11208c2ecf20Sopenharmony_ci	rc = register_blkdev(0, DCSSBLK_NAME);
11218c2ecf20Sopenharmony_ci	if (rc < 0)
11228c2ecf20Sopenharmony_ci		goto out_root;
11238c2ecf20Sopenharmony_ci	dcssblk_major = rc;
11248c2ecf20Sopenharmony_ci	init_rwsem(&dcssblk_devices_sem);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	dcssblk_check_params();
11278c2ecf20Sopenharmony_ci	return 0;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ciout_root:
11308c2ecf20Sopenharmony_ci	root_device_unregister(dcssblk_root_dev);
11318c2ecf20Sopenharmony_ciout_pdev:
11328c2ecf20Sopenharmony_ci	platform_device_unregister(dcssblk_pdev);
11338c2ecf20Sopenharmony_ciout_pdrv:
11348c2ecf20Sopenharmony_ci	platform_driver_unregister(&dcssblk_pdrv);
11358c2ecf20Sopenharmony_ci	return rc;
11368c2ecf20Sopenharmony_ci}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_cimodule_init(dcssblk_init);
11398c2ecf20Sopenharmony_cimodule_exit(dcssblk_exit);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_cimodule_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444);
11428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, "
11438c2ecf20Sopenharmony_ci		 "comma-separated list, names in each set separated "
11448c2ecf20Sopenharmony_ci		 "by commas are separated by colons, each set contains "
11458c2ecf20Sopenharmony_ci		 "names of contiguous segments and each name max. 8 chars.\n"
11468c2ecf20Sopenharmony_ci		 "Adding \"(local)\" to the end of each set equals echoing 0 "
11478c2ecf20Sopenharmony_ci		 "to /sys/devices/dcssblk/<device name>/shared after loading "
11488c2ecf20Sopenharmony_ci		 "the contiguous segments - \n"
11498c2ecf20Sopenharmony_ci		 "e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\"");
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1152