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