18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. 38c2ecf20Sopenharmony_ci * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This file is released under the GPL. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "dm-core.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 128c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/wait.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/dm-ioctl.h> 188c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 198c2ecf20Sopenharmony_ci#include <linux/compat.h> 208c2ecf20Sopenharmony_ci#include <linux/nospec.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define DM_MSG_PREFIX "ioctl" 258c2ecf20Sopenharmony_ci#define DM_DRIVER_EMAIL "dm-devel@redhat.com" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct dm_file { 288c2ecf20Sopenharmony_ci /* 298c2ecf20Sopenharmony_ci * poll will wait until the global event number is greater than 308c2ecf20Sopenharmony_ci * this value. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci volatile unsigned global_event_nr; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/*----------------------------------------------------------------- 368c2ecf20Sopenharmony_ci * The ioctl interface needs to be able to look up devices by 378c2ecf20Sopenharmony_ci * name or uuid. 388c2ecf20Sopenharmony_ci *---------------------------------------------------------------*/ 398c2ecf20Sopenharmony_cistruct hash_cell { 408c2ecf20Sopenharmony_ci struct list_head name_list; 418c2ecf20Sopenharmony_ci struct list_head uuid_list; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci char *name; 448c2ecf20Sopenharmony_ci char *uuid; 458c2ecf20Sopenharmony_ci struct mapped_device *md; 468c2ecf20Sopenharmony_ci struct dm_table *new_map; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct vers_iter { 508c2ecf20Sopenharmony_ci size_t param_size; 518c2ecf20Sopenharmony_ci struct dm_target_versions *vers, *old_vers; 528c2ecf20Sopenharmony_ci char *end; 538c2ecf20Sopenharmony_ci uint32_t flags; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define NUM_BUCKETS 64 588c2ecf20Sopenharmony_ci#define MASK_BUCKETS (NUM_BUCKETS - 1) 598c2ecf20Sopenharmony_cistatic struct list_head _name_buckets[NUM_BUCKETS]; 608c2ecf20Sopenharmony_cistatic struct list_head _uuid_buckets[NUM_BUCKETS]; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * Guards access to both hash tables. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(_hash_lock); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * Protects use of mdptr to obtain hash cell name and uuid from mapped device. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dm_hash_cells_mutex); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void init_buckets(struct list_head *buckets) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci unsigned int i; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci for (i = 0; i < NUM_BUCKETS; i++) 798c2ecf20Sopenharmony_ci INIT_LIST_HEAD(buckets + i); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int dm_hash_init(void) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci init_buckets(_name_buckets); 858c2ecf20Sopenharmony_ci init_buckets(_uuid_buckets); 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void dm_hash_exit(void) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci dm_hash_remove_all(false, false, false); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/*----------------------------------------------------------------- 958c2ecf20Sopenharmony_ci * Hash function: 968c2ecf20Sopenharmony_ci * We're not really concerned with the str hash function being 978c2ecf20Sopenharmony_ci * fast since it's only used by the ioctl interface. 988c2ecf20Sopenharmony_ci *---------------------------------------------------------------*/ 998c2ecf20Sopenharmony_cistatic unsigned int hash_str(const char *str) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci const unsigned int hash_mult = 2654435387U; 1028c2ecf20Sopenharmony_ci unsigned int h = 0; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci while (*str) 1058c2ecf20Sopenharmony_ci h = (h + (unsigned int) *str++) * hash_mult; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return h & MASK_BUCKETS; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/*----------------------------------------------------------------- 1118c2ecf20Sopenharmony_ci * Code for looking up a device by name 1128c2ecf20Sopenharmony_ci *---------------------------------------------------------------*/ 1138c2ecf20Sopenharmony_cistatic struct hash_cell *__get_name_cell(const char *str) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct hash_cell *hc; 1168c2ecf20Sopenharmony_ci unsigned int h = hash_str(str); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci list_for_each_entry (hc, _name_buckets + h, name_list) 1198c2ecf20Sopenharmony_ci if (!strcmp(hc->name, str)) { 1208c2ecf20Sopenharmony_ci dm_get(hc->md); 1218c2ecf20Sopenharmony_ci return hc; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return NULL; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic struct hash_cell *__get_uuid_cell(const char *str) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct hash_cell *hc; 1308c2ecf20Sopenharmony_ci unsigned int h = hash_str(str); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci list_for_each_entry (hc, _uuid_buckets + h, uuid_list) 1338c2ecf20Sopenharmony_ci if (!strcmp(hc->uuid, str)) { 1348c2ecf20Sopenharmony_ci dm_get(hc->md); 1358c2ecf20Sopenharmony_ci return hc; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return NULL; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic struct hash_cell *__get_dev_cell(uint64_t dev) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct mapped_device *md; 1448c2ecf20Sopenharmony_ci struct hash_cell *hc; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci md = dm_get_md(huge_decode_dev(dev)); 1478c2ecf20Sopenharmony_ci if (!md) 1488c2ecf20Sopenharmony_ci return NULL; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci hc = dm_get_mdptr(md); 1518c2ecf20Sopenharmony_ci if (!hc) { 1528c2ecf20Sopenharmony_ci dm_put(md); 1538c2ecf20Sopenharmony_ci return NULL; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return hc; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/*----------------------------------------------------------------- 1608c2ecf20Sopenharmony_ci * Inserting, removing and renaming a device. 1618c2ecf20Sopenharmony_ci *---------------------------------------------------------------*/ 1628c2ecf20Sopenharmony_cistatic struct hash_cell *alloc_cell(const char *name, const char *uuid, 1638c2ecf20Sopenharmony_ci struct mapped_device *md) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct hash_cell *hc; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci hc = kmalloc(sizeof(*hc), GFP_KERNEL); 1688c2ecf20Sopenharmony_ci if (!hc) 1698c2ecf20Sopenharmony_ci return NULL; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci hc->name = kstrdup(name, GFP_KERNEL); 1728c2ecf20Sopenharmony_ci if (!hc->name) { 1738c2ecf20Sopenharmony_ci kfree(hc); 1748c2ecf20Sopenharmony_ci return NULL; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (!uuid) 1788c2ecf20Sopenharmony_ci hc->uuid = NULL; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci else { 1818c2ecf20Sopenharmony_ci hc->uuid = kstrdup(uuid, GFP_KERNEL); 1828c2ecf20Sopenharmony_ci if (!hc->uuid) { 1838c2ecf20Sopenharmony_ci kfree(hc->name); 1848c2ecf20Sopenharmony_ci kfree(hc); 1858c2ecf20Sopenharmony_ci return NULL; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&hc->name_list); 1908c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&hc->uuid_list); 1918c2ecf20Sopenharmony_ci hc->md = md; 1928c2ecf20Sopenharmony_ci hc->new_map = NULL; 1938c2ecf20Sopenharmony_ci return hc; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void free_cell(struct hash_cell *hc) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci if (hc) { 1998c2ecf20Sopenharmony_ci kfree(hc->name); 2008c2ecf20Sopenharmony_ci kfree(hc->uuid); 2018c2ecf20Sopenharmony_ci kfree(hc); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* 2068c2ecf20Sopenharmony_ci * The kdev_t and uuid of a device can never change once it is 2078c2ecf20Sopenharmony_ci * initially inserted. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_cistatic int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct hash_cell *cell, *hc; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* 2148c2ecf20Sopenharmony_ci * Allocate the new cells. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci cell = alloc_cell(name, uuid, md); 2178c2ecf20Sopenharmony_ci if (!cell) 2188c2ecf20Sopenharmony_ci return -ENOMEM; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * Insert the cell into both hash tables. 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci down_write(&_hash_lock); 2248c2ecf20Sopenharmony_ci hc = __get_name_cell(name); 2258c2ecf20Sopenharmony_ci if (hc) { 2268c2ecf20Sopenharmony_ci dm_put(hc->md); 2278c2ecf20Sopenharmony_ci goto bad; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci list_add(&cell->name_list, _name_buckets + hash_str(name)); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (uuid) { 2338c2ecf20Sopenharmony_ci hc = __get_uuid_cell(uuid); 2348c2ecf20Sopenharmony_ci if (hc) { 2358c2ecf20Sopenharmony_ci list_del(&cell->name_list); 2368c2ecf20Sopenharmony_ci dm_put(hc->md); 2378c2ecf20Sopenharmony_ci goto bad; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci dm_get(md); 2428c2ecf20Sopenharmony_ci mutex_lock(&dm_hash_cells_mutex); 2438c2ecf20Sopenharmony_ci dm_set_mdptr(md, cell); 2448c2ecf20Sopenharmony_ci mutex_unlock(&dm_hash_cells_mutex); 2458c2ecf20Sopenharmony_ci up_write(&_hash_lock); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci bad: 2508c2ecf20Sopenharmony_ci up_write(&_hash_lock); 2518c2ecf20Sopenharmony_ci free_cell(cell); 2528c2ecf20Sopenharmony_ci return -EBUSY; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic struct dm_table *__hash_remove(struct hash_cell *hc) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct dm_table *table; 2588c2ecf20Sopenharmony_ci int srcu_idx; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* remove from the dev hash */ 2618c2ecf20Sopenharmony_ci list_del(&hc->uuid_list); 2628c2ecf20Sopenharmony_ci list_del(&hc->name_list); 2638c2ecf20Sopenharmony_ci mutex_lock(&dm_hash_cells_mutex); 2648c2ecf20Sopenharmony_ci dm_set_mdptr(hc->md, NULL); 2658c2ecf20Sopenharmony_ci mutex_unlock(&dm_hash_cells_mutex); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci table = dm_get_live_table(hc->md, &srcu_idx); 2688c2ecf20Sopenharmony_ci if (table) 2698c2ecf20Sopenharmony_ci dm_table_event(table); 2708c2ecf20Sopenharmony_ci dm_put_live_table(hc->md, srcu_idx); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci table = NULL; 2738c2ecf20Sopenharmony_ci if (hc->new_map) 2748c2ecf20Sopenharmony_ci table = hc->new_map; 2758c2ecf20Sopenharmony_ci dm_put(hc->md); 2768c2ecf20Sopenharmony_ci free_cell(hc); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return table; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci int i, dev_skipped; 2848c2ecf20Sopenharmony_ci struct hash_cell *hc; 2858c2ecf20Sopenharmony_ci struct mapped_device *md; 2868c2ecf20Sopenharmony_ci struct dm_table *t; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ciretry: 2898c2ecf20Sopenharmony_ci dev_skipped = 0; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci down_write(&_hash_lock); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci for (i = 0; i < NUM_BUCKETS; i++) { 2948c2ecf20Sopenharmony_ci list_for_each_entry(hc, _name_buckets + i, name_list) { 2958c2ecf20Sopenharmony_ci md = hc->md; 2968c2ecf20Sopenharmony_ci dm_get(md); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (keep_open_devices && 2998c2ecf20Sopenharmony_ci dm_lock_for_deletion(md, mark_deferred, only_deferred)) { 3008c2ecf20Sopenharmony_ci dm_put(md); 3018c2ecf20Sopenharmony_ci dev_skipped++; 3028c2ecf20Sopenharmony_ci continue; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci t = __hash_remove(hc); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci up_write(&_hash_lock); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (t) { 3108c2ecf20Sopenharmony_ci dm_sync_table(md); 3118c2ecf20Sopenharmony_ci dm_table_destroy(t); 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci dm_put(md); 3148c2ecf20Sopenharmony_ci if (likely(keep_open_devices)) 3158c2ecf20Sopenharmony_ci dm_destroy(md); 3168c2ecf20Sopenharmony_ci else 3178c2ecf20Sopenharmony_ci dm_destroy_immediate(md); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* 3208c2ecf20Sopenharmony_ci * Some mapped devices may be using other mapped 3218c2ecf20Sopenharmony_ci * devices, so repeat until we make no further 3228c2ecf20Sopenharmony_ci * progress. If a new mapped device is created 3238c2ecf20Sopenharmony_ci * here it will also get removed. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci goto retry; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci up_write(&_hash_lock); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (dev_skipped) 3328c2ecf20Sopenharmony_ci DMWARN("remove_all left %d open device(s)", dev_skipped); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci/* 3368c2ecf20Sopenharmony_ci * Set the uuid of a hash_cell that isn't already set. 3378c2ecf20Sopenharmony_ci */ 3388c2ecf20Sopenharmony_cistatic void __set_cell_uuid(struct hash_cell *hc, char *new_uuid) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci mutex_lock(&dm_hash_cells_mutex); 3418c2ecf20Sopenharmony_ci hc->uuid = new_uuid; 3428c2ecf20Sopenharmony_ci mutex_unlock(&dm_hash_cells_mutex); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci list_add(&hc->uuid_list, _uuid_buckets + hash_str(new_uuid)); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci/* 3488c2ecf20Sopenharmony_ci * Changes the name of a hash_cell and returns the old name for 3498c2ecf20Sopenharmony_ci * the caller to free. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_cistatic char *__change_cell_name(struct hash_cell *hc, char *new_name) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci char *old_name; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* 3568c2ecf20Sopenharmony_ci * Rename and move the name cell. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci list_del(&hc->name_list); 3598c2ecf20Sopenharmony_ci old_name = hc->name; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci mutex_lock(&dm_hash_cells_mutex); 3628c2ecf20Sopenharmony_ci hc->name = new_name; 3638c2ecf20Sopenharmony_ci mutex_unlock(&dm_hash_cells_mutex); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci list_add(&hc->name_list, _name_buckets + hash_str(new_name)); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return old_name; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic struct mapped_device *dm_hash_rename(struct dm_ioctl *param, 3718c2ecf20Sopenharmony_ci const char *new) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci char *new_data, *old_name = NULL; 3748c2ecf20Sopenharmony_ci struct hash_cell *hc; 3758c2ecf20Sopenharmony_ci struct dm_table *table; 3768c2ecf20Sopenharmony_ci struct mapped_device *md; 3778c2ecf20Sopenharmony_ci unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0; 3788c2ecf20Sopenharmony_ci int srcu_idx; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* 3818c2ecf20Sopenharmony_ci * duplicate new. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci new_data = kstrdup(new, GFP_KERNEL); 3848c2ecf20Sopenharmony_ci if (!new_data) 3858c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci down_write(&_hash_lock); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* 3908c2ecf20Sopenharmony_ci * Is new free ? 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ci if (change_uuid) 3938c2ecf20Sopenharmony_ci hc = __get_uuid_cell(new); 3948c2ecf20Sopenharmony_ci else 3958c2ecf20Sopenharmony_ci hc = __get_name_cell(new); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (hc) { 3988c2ecf20Sopenharmony_ci DMWARN("Unable to change %s on mapped device %s to one that " 3998c2ecf20Sopenharmony_ci "already exists: %s", 4008c2ecf20Sopenharmony_ci change_uuid ? "uuid" : "name", 4018c2ecf20Sopenharmony_ci param->name, new); 4028c2ecf20Sopenharmony_ci dm_put(hc->md); 4038c2ecf20Sopenharmony_ci up_write(&_hash_lock); 4048c2ecf20Sopenharmony_ci kfree(new_data); 4058c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* 4098c2ecf20Sopenharmony_ci * Is there such a device as 'old' ? 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci hc = __get_name_cell(param->name); 4128c2ecf20Sopenharmony_ci if (!hc) { 4138c2ecf20Sopenharmony_ci DMWARN("Unable to rename non-existent device, %s to %s%s", 4148c2ecf20Sopenharmony_ci param->name, change_uuid ? "uuid " : "", new); 4158c2ecf20Sopenharmony_ci up_write(&_hash_lock); 4168c2ecf20Sopenharmony_ci kfree(new_data); 4178c2ecf20Sopenharmony_ci return ERR_PTR(-ENXIO); 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* 4218c2ecf20Sopenharmony_ci * Does this device already have a uuid? 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_ci if (change_uuid && hc->uuid) { 4248c2ecf20Sopenharmony_ci DMWARN("Unable to change uuid of mapped device %s to %s " 4258c2ecf20Sopenharmony_ci "because uuid is already set to %s", 4268c2ecf20Sopenharmony_ci param->name, new, hc->uuid); 4278c2ecf20Sopenharmony_ci dm_put(hc->md); 4288c2ecf20Sopenharmony_ci up_write(&_hash_lock); 4298c2ecf20Sopenharmony_ci kfree(new_data); 4308c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (change_uuid) 4348c2ecf20Sopenharmony_ci __set_cell_uuid(hc, new_data); 4358c2ecf20Sopenharmony_ci else 4368c2ecf20Sopenharmony_ci old_name = __change_cell_name(hc, new_data); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* 4398c2ecf20Sopenharmony_ci * Wake up any dm event waiters. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci table = dm_get_live_table(hc->md, &srcu_idx); 4428c2ecf20Sopenharmony_ci if (table) 4438c2ecf20Sopenharmony_ci dm_table_event(table); 4448c2ecf20Sopenharmony_ci dm_put_live_table(hc->md, srcu_idx); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr)) 4478c2ecf20Sopenharmony_ci param->flags |= DM_UEVENT_GENERATED_FLAG; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci md = hc->md; 4508c2ecf20Sopenharmony_ci up_write(&_hash_lock); 4518c2ecf20Sopenharmony_ci kfree(old_name); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return md; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_civoid dm_deferred_remove(void) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci dm_hash_remove_all(true, false, true); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci/*----------------------------------------------------------------- 4628c2ecf20Sopenharmony_ci * Implementation of the ioctl commands 4638c2ecf20Sopenharmony_ci *---------------------------------------------------------------*/ 4648c2ecf20Sopenharmony_ci/* 4658c2ecf20Sopenharmony_ci * All the ioctl commands get dispatched to functions with this 4668c2ecf20Sopenharmony_ci * prototype. 4678c2ecf20Sopenharmony_ci */ 4688c2ecf20Sopenharmony_citypedef int (*ioctl_fn)(struct file *filp, struct dm_ioctl *param, size_t param_size); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int remove_all(struct file *filp, struct dm_ioctl *param, size_t param_size) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci dm_hash_remove_all(true, !!(param->flags & DM_DEFERRED_REMOVE), false); 4738c2ecf20Sopenharmony_ci param->data_size = 0; 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci/* 4788c2ecf20Sopenharmony_ci * Round up the ptr to an 8-byte boundary. 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_ci#define ALIGN_MASK 7 4818c2ecf20Sopenharmony_cistatic inline size_t align_val(size_t val) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci return (val + ALIGN_MASK) & ~ALIGN_MASK; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_cistatic inline void *align_ptr(void *ptr) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci return (void *)align_val((size_t)ptr); 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci/* 4918c2ecf20Sopenharmony_ci * Retrieves the data payload buffer from an already allocated 4928c2ecf20Sopenharmony_ci * struct dm_ioctl. 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_cistatic void *get_result_buffer(struct dm_ioctl *param, size_t param_size, 4958c2ecf20Sopenharmony_ci size_t *len) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci param->data_start = align_ptr(param + 1) - (void *) param; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (param->data_start < param_size) 5008c2ecf20Sopenharmony_ci *len = param_size - param->data_start; 5018c2ecf20Sopenharmony_ci else 5028c2ecf20Sopenharmony_ci *len = 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return ((void *) param) + param->data_start; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int list_devices(struct file *filp, struct dm_ioctl *param, size_t param_size) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci unsigned int i; 5108c2ecf20Sopenharmony_ci struct hash_cell *hc; 5118c2ecf20Sopenharmony_ci size_t len, needed = 0; 5128c2ecf20Sopenharmony_ci struct gendisk *disk; 5138c2ecf20Sopenharmony_ci struct dm_name_list *orig_nl, *nl, *old_nl = NULL; 5148c2ecf20Sopenharmony_ci uint32_t *event_nr; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci down_write(&_hash_lock); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* 5198c2ecf20Sopenharmony_ci * Loop through all the devices working out how much 5208c2ecf20Sopenharmony_ci * space we need. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci for (i = 0; i < NUM_BUCKETS; i++) { 5238c2ecf20Sopenharmony_ci list_for_each_entry (hc, _name_buckets + i, name_list) { 5248c2ecf20Sopenharmony_ci needed += align_val(offsetof(struct dm_name_list, name) + strlen(hc->name) + 1); 5258c2ecf20Sopenharmony_ci needed += align_val(sizeof(uint32_t)); 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* 5308c2ecf20Sopenharmony_ci * Grab our output buffer. 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ci nl = orig_nl = get_result_buffer(param, param_size, &len); 5338c2ecf20Sopenharmony_ci if (len < needed || len < sizeof(nl->dev)) { 5348c2ecf20Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 5358c2ecf20Sopenharmony_ci goto out; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci param->data_size = param->data_start + needed; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci nl->dev = 0; /* Flags no data */ 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* 5428c2ecf20Sopenharmony_ci * Now loop through filling out the names. 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_ci for (i = 0; i < NUM_BUCKETS; i++) { 5458c2ecf20Sopenharmony_ci list_for_each_entry (hc, _name_buckets + i, name_list) { 5468c2ecf20Sopenharmony_ci if (old_nl) 5478c2ecf20Sopenharmony_ci old_nl->next = (uint32_t) ((void *) nl - 5488c2ecf20Sopenharmony_ci (void *) old_nl); 5498c2ecf20Sopenharmony_ci disk = dm_disk(hc->md); 5508c2ecf20Sopenharmony_ci nl->dev = huge_encode_dev(disk_devt(disk)); 5518c2ecf20Sopenharmony_ci nl->next = 0; 5528c2ecf20Sopenharmony_ci strcpy(nl->name, hc->name); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci old_nl = nl; 5558c2ecf20Sopenharmony_ci event_nr = align_ptr(nl->name + strlen(hc->name) + 1); 5568c2ecf20Sopenharmony_ci *event_nr = dm_get_event_nr(hc->md); 5578c2ecf20Sopenharmony_ci nl = align_ptr(event_nr + 1); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci /* 5618c2ecf20Sopenharmony_ci * If mismatch happens, security may be compromised due to buffer 5628c2ecf20Sopenharmony_ci * overflow, so it's better to crash. 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_ci BUG_ON((char *)nl - (char *)orig_nl != needed); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci out: 5678c2ecf20Sopenharmony_ci up_write(&_hash_lock); 5688c2ecf20Sopenharmony_ci return 0; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic void list_version_get_needed(struct target_type *tt, void *needed_param) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci size_t *needed = needed_param; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci *needed += sizeof(struct dm_target_versions); 5768c2ecf20Sopenharmony_ci *needed += strlen(tt->name) + 1; 5778c2ecf20Sopenharmony_ci *needed += ALIGN_MASK; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic void list_version_get_info(struct target_type *tt, void *param) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct vers_iter *info = param; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Check space - it might have changed since the first iteration */ 5858c2ecf20Sopenharmony_ci if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 > 5868c2ecf20Sopenharmony_ci info->end) { 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci info->flags = DM_BUFFER_FULL_FLAG; 5898c2ecf20Sopenharmony_ci return; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (info->old_vers) 5938c2ecf20Sopenharmony_ci info->old_vers->next = (uint32_t) ((void *)info->vers - 5948c2ecf20Sopenharmony_ci (void *)info->old_vers); 5958c2ecf20Sopenharmony_ci info->vers->version[0] = tt->version[0]; 5968c2ecf20Sopenharmony_ci info->vers->version[1] = tt->version[1]; 5978c2ecf20Sopenharmony_ci info->vers->version[2] = tt->version[2]; 5988c2ecf20Sopenharmony_ci info->vers->next = 0; 5998c2ecf20Sopenharmony_ci strcpy(info->vers->name, tt->name); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci info->old_vers = info->vers; 6028c2ecf20Sopenharmony_ci info->vers = align_ptr(((void *) ++info->vers) + strlen(tt->name) + 1); 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic int __list_versions(struct dm_ioctl *param, size_t param_size, const char *name) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci size_t len, needed = 0; 6088c2ecf20Sopenharmony_ci struct dm_target_versions *vers; 6098c2ecf20Sopenharmony_ci struct vers_iter iter_info; 6108c2ecf20Sopenharmony_ci struct target_type *tt = NULL; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (name) { 6138c2ecf20Sopenharmony_ci tt = dm_get_target_type(name); 6148c2ecf20Sopenharmony_ci if (!tt) 6158c2ecf20Sopenharmony_ci return -EINVAL; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci /* 6198c2ecf20Sopenharmony_ci * Loop through all the devices working out how much 6208c2ecf20Sopenharmony_ci * space we need. 6218c2ecf20Sopenharmony_ci */ 6228c2ecf20Sopenharmony_ci if (!tt) 6238c2ecf20Sopenharmony_ci dm_target_iterate(list_version_get_needed, &needed); 6248c2ecf20Sopenharmony_ci else 6258c2ecf20Sopenharmony_ci list_version_get_needed(tt, &needed); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* 6288c2ecf20Sopenharmony_ci * Grab our output buffer. 6298c2ecf20Sopenharmony_ci */ 6308c2ecf20Sopenharmony_ci vers = get_result_buffer(param, param_size, &len); 6318c2ecf20Sopenharmony_ci if (len < needed) { 6328c2ecf20Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 6338c2ecf20Sopenharmony_ci goto out; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci param->data_size = param->data_start + needed; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci iter_info.param_size = param_size; 6388c2ecf20Sopenharmony_ci iter_info.old_vers = NULL; 6398c2ecf20Sopenharmony_ci iter_info.vers = vers; 6408c2ecf20Sopenharmony_ci iter_info.flags = 0; 6418c2ecf20Sopenharmony_ci iter_info.end = (char *)vers + needed; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* 6448c2ecf20Sopenharmony_ci * Now loop through filling out the names & versions. 6458c2ecf20Sopenharmony_ci */ 6468c2ecf20Sopenharmony_ci if (!tt) 6478c2ecf20Sopenharmony_ci dm_target_iterate(list_version_get_info, &iter_info); 6488c2ecf20Sopenharmony_ci else 6498c2ecf20Sopenharmony_ci list_version_get_info(tt, &iter_info); 6508c2ecf20Sopenharmony_ci param->flags |= iter_info.flags; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci out: 6538c2ecf20Sopenharmony_ci if (tt) 6548c2ecf20Sopenharmony_ci dm_put_target_type(tt); 6558c2ecf20Sopenharmony_ci return 0; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic int list_versions(struct file *filp, struct dm_ioctl *param, size_t param_size) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci return __list_versions(param, param_size, NULL); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic int get_target_version(struct file *filp, struct dm_ioctl *param, size_t param_size) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci return __list_versions(param, param_size, param->name); 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic int check_name(const char *name) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci if (strchr(name, '/')) { 6718c2ecf20Sopenharmony_ci DMWARN("invalid device name"); 6728c2ecf20Sopenharmony_ci return -EINVAL; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci return 0; 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci/* 6798c2ecf20Sopenharmony_ci * On successful return, the caller must not attempt to acquire 6808c2ecf20Sopenharmony_ci * _hash_lock without first calling dm_put_live_table, because dm_table_destroy 6818c2ecf20Sopenharmony_ci * waits for this dm_put_live_table and could be called under this lock. 6828c2ecf20Sopenharmony_ci */ 6838c2ecf20Sopenharmony_cistatic struct dm_table *dm_get_inactive_table(struct mapped_device *md, int *srcu_idx) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct hash_cell *hc; 6868c2ecf20Sopenharmony_ci struct dm_table *table = NULL; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* increment rcu count, we don't care about the table pointer */ 6898c2ecf20Sopenharmony_ci dm_get_live_table(md, srcu_idx); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci down_read(&_hash_lock); 6928c2ecf20Sopenharmony_ci hc = dm_get_mdptr(md); 6938c2ecf20Sopenharmony_ci if (!hc || hc->md != md) { 6948c2ecf20Sopenharmony_ci DMWARN("device has been removed from the dev hash table."); 6958c2ecf20Sopenharmony_ci goto out; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci table = hc->new_map; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ciout: 7018c2ecf20Sopenharmony_ci up_read(&_hash_lock); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return table; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md, 7078c2ecf20Sopenharmony_ci struct dm_ioctl *param, 7088c2ecf20Sopenharmony_ci int *srcu_idx) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ? 7118c2ecf20Sopenharmony_ci dm_get_inactive_table(md, srcu_idx) : dm_get_live_table(md, srcu_idx); 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci/* 7158c2ecf20Sopenharmony_ci * Fills in a dm_ioctl structure, ready for sending back to 7168c2ecf20Sopenharmony_ci * userland. 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_cistatic void __dev_status(struct mapped_device *md, struct dm_ioctl *param) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci struct gendisk *disk = dm_disk(md); 7218c2ecf20Sopenharmony_ci struct dm_table *table; 7228c2ecf20Sopenharmony_ci int srcu_idx; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | 7258c2ecf20Sopenharmony_ci DM_ACTIVE_PRESENT_FLAG | DM_INTERNAL_SUSPEND_FLAG); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (dm_suspended_md(md)) 7288c2ecf20Sopenharmony_ci param->flags |= DM_SUSPEND_FLAG; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (dm_suspended_internally_md(md)) 7318c2ecf20Sopenharmony_ci param->flags |= DM_INTERNAL_SUSPEND_FLAG; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (dm_test_deferred_remove_flag(md)) 7348c2ecf20Sopenharmony_ci param->flags |= DM_DEFERRED_REMOVE; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci param->dev = huge_encode_dev(disk_devt(disk)); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* 7398c2ecf20Sopenharmony_ci * Yes, this will be out of date by the time it gets back 7408c2ecf20Sopenharmony_ci * to userland, but it is still very useful for 7418c2ecf20Sopenharmony_ci * debugging. 7428c2ecf20Sopenharmony_ci */ 7438c2ecf20Sopenharmony_ci param->open_count = dm_open_count(md); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci param->event_nr = dm_get_event_nr(md); 7468c2ecf20Sopenharmony_ci param->target_count = 0; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci table = dm_get_live_table(md, &srcu_idx); 7498c2ecf20Sopenharmony_ci if (table) { 7508c2ecf20Sopenharmony_ci if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) { 7518c2ecf20Sopenharmony_ci if (get_disk_ro(disk)) 7528c2ecf20Sopenharmony_ci param->flags |= DM_READONLY_FLAG; 7538c2ecf20Sopenharmony_ci param->target_count = dm_table_get_num_targets(table); 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci param->flags |= DM_ACTIVE_PRESENT_FLAG; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci dm_put_live_table(md, srcu_idx); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) { 7618c2ecf20Sopenharmony_ci int srcu_idx; 7628c2ecf20Sopenharmony_ci table = dm_get_inactive_table(md, &srcu_idx); 7638c2ecf20Sopenharmony_ci if (table) { 7648c2ecf20Sopenharmony_ci if (!(dm_table_get_mode(table) & FMODE_WRITE)) 7658c2ecf20Sopenharmony_ci param->flags |= DM_READONLY_FLAG; 7668c2ecf20Sopenharmony_ci param->target_count = dm_table_get_num_targets(table); 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci dm_put_live_table(md, srcu_idx); 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic int dev_create(struct file *filp, struct dm_ioctl *param, size_t param_size) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci int r, m = DM_ANY_MINOR; 7758c2ecf20Sopenharmony_ci struct mapped_device *md; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci r = check_name(param->name); 7788c2ecf20Sopenharmony_ci if (r) 7798c2ecf20Sopenharmony_ci return r; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (param->flags & DM_PERSISTENT_DEV_FLAG) 7828c2ecf20Sopenharmony_ci m = MINOR(huge_decode_dev(param->dev)); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci r = dm_create(m, &md); 7858c2ecf20Sopenharmony_ci if (r) 7868c2ecf20Sopenharmony_ci return r; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md); 7898c2ecf20Sopenharmony_ci if (r) { 7908c2ecf20Sopenharmony_ci dm_put(md); 7918c2ecf20Sopenharmony_ci dm_destroy(md); 7928c2ecf20Sopenharmony_ci return r; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci param->flags &= ~DM_INACTIVE_PRESENT_FLAG; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci __dev_status(md, param); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci dm_put(md); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci return 0; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci/* 8058c2ecf20Sopenharmony_ci * Always use UUID for lookups if it's present, otherwise use name or dev. 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_cistatic struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct hash_cell *hc = NULL; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (*param->uuid) { 8128c2ecf20Sopenharmony_ci if (*param->name || param->dev) 8138c2ecf20Sopenharmony_ci return NULL; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci hc = __get_uuid_cell(param->uuid); 8168c2ecf20Sopenharmony_ci if (!hc) 8178c2ecf20Sopenharmony_ci return NULL; 8188c2ecf20Sopenharmony_ci } else if (*param->name) { 8198c2ecf20Sopenharmony_ci if (param->dev) 8208c2ecf20Sopenharmony_ci return NULL; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci hc = __get_name_cell(param->name); 8238c2ecf20Sopenharmony_ci if (!hc) 8248c2ecf20Sopenharmony_ci return NULL; 8258c2ecf20Sopenharmony_ci } else if (param->dev) { 8268c2ecf20Sopenharmony_ci hc = __get_dev_cell(param->dev); 8278c2ecf20Sopenharmony_ci if (!hc) 8288c2ecf20Sopenharmony_ci return NULL; 8298c2ecf20Sopenharmony_ci } else 8308c2ecf20Sopenharmony_ci return NULL; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci /* 8338c2ecf20Sopenharmony_ci * Sneakily write in both the name and the uuid 8348c2ecf20Sopenharmony_ci * while we have the cell. 8358c2ecf20Sopenharmony_ci */ 8368c2ecf20Sopenharmony_ci strlcpy(param->name, hc->name, sizeof(param->name)); 8378c2ecf20Sopenharmony_ci if (hc->uuid) 8388c2ecf20Sopenharmony_ci strlcpy(param->uuid, hc->uuid, sizeof(param->uuid)); 8398c2ecf20Sopenharmony_ci else 8408c2ecf20Sopenharmony_ci param->uuid[0] = '\0'; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (hc->new_map) 8438c2ecf20Sopenharmony_ci param->flags |= DM_INACTIVE_PRESENT_FLAG; 8448c2ecf20Sopenharmony_ci else 8458c2ecf20Sopenharmony_ci param->flags &= ~DM_INACTIVE_PRESENT_FLAG; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci return hc; 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic struct mapped_device *find_device(struct dm_ioctl *param) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci struct hash_cell *hc; 8538c2ecf20Sopenharmony_ci struct mapped_device *md = NULL; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci down_read(&_hash_lock); 8568c2ecf20Sopenharmony_ci hc = __find_device_hash_cell(param); 8578c2ecf20Sopenharmony_ci if (hc) 8588c2ecf20Sopenharmony_ci md = hc->md; 8598c2ecf20Sopenharmony_ci up_read(&_hash_lock); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci return md; 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_size) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct hash_cell *hc; 8678c2ecf20Sopenharmony_ci struct mapped_device *md; 8688c2ecf20Sopenharmony_ci int r; 8698c2ecf20Sopenharmony_ci struct dm_table *t; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci down_write(&_hash_lock); 8728c2ecf20Sopenharmony_ci hc = __find_device_hash_cell(param); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (!hc) { 8758c2ecf20Sopenharmony_ci DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table."); 8768c2ecf20Sopenharmony_ci up_write(&_hash_lock); 8778c2ecf20Sopenharmony_ci return -ENXIO; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci md = hc->md; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* 8838c2ecf20Sopenharmony_ci * Ensure the device is not open and nothing further can open it. 8848c2ecf20Sopenharmony_ci */ 8858c2ecf20Sopenharmony_ci r = dm_lock_for_deletion(md, !!(param->flags & DM_DEFERRED_REMOVE), false); 8868c2ecf20Sopenharmony_ci if (r) { 8878c2ecf20Sopenharmony_ci if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) { 8888c2ecf20Sopenharmony_ci up_write(&_hash_lock); 8898c2ecf20Sopenharmony_ci dm_put(md); 8908c2ecf20Sopenharmony_ci return 0; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci DMDEBUG_LIMIT("unable to remove open device %s", hc->name); 8938c2ecf20Sopenharmony_ci up_write(&_hash_lock); 8948c2ecf20Sopenharmony_ci dm_put(md); 8958c2ecf20Sopenharmony_ci return r; 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci t = __hash_remove(hc); 8998c2ecf20Sopenharmony_ci up_write(&_hash_lock); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (t) { 9028c2ecf20Sopenharmony_ci dm_sync_table(md); 9038c2ecf20Sopenharmony_ci dm_table_destroy(t); 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci param->flags &= ~DM_DEFERRED_REMOVE; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr)) 9098c2ecf20Sopenharmony_ci param->flags |= DM_UEVENT_GENERATED_FLAG; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci dm_put(md); 9128c2ecf20Sopenharmony_ci dm_destroy(md); 9138c2ecf20Sopenharmony_ci return 0; 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci/* 9178c2ecf20Sopenharmony_ci * Check a string doesn't overrun the chunk of 9188c2ecf20Sopenharmony_ci * memory we copied from userland. 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_cistatic int invalid_str(char *str, void *end) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci while ((void *) str < end) 9238c2ecf20Sopenharmony_ci if (!*str++) 9248c2ecf20Sopenharmony_ci return 0; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci return -EINVAL; 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic int dev_rename(struct file *filp, struct dm_ioctl *param, size_t param_size) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci int r; 9328c2ecf20Sopenharmony_ci char *new_data = (char *) param + param->data_start; 9338c2ecf20Sopenharmony_ci struct mapped_device *md; 9348c2ecf20Sopenharmony_ci unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (new_data < param->data || 9378c2ecf20Sopenharmony_ci invalid_str(new_data, (void *) param + param_size) || !*new_data || 9388c2ecf20Sopenharmony_ci strlen(new_data) > (change_uuid ? DM_UUID_LEN - 1 : DM_NAME_LEN - 1)) { 9398c2ecf20Sopenharmony_ci DMWARN("Invalid new mapped device name or uuid string supplied."); 9408c2ecf20Sopenharmony_ci return -EINVAL; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (!change_uuid) { 9448c2ecf20Sopenharmony_ci r = check_name(new_data); 9458c2ecf20Sopenharmony_ci if (r) 9468c2ecf20Sopenharmony_ci return r; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci md = dm_hash_rename(param, new_data); 9508c2ecf20Sopenharmony_ci if (IS_ERR(md)) 9518c2ecf20Sopenharmony_ci return PTR_ERR(md); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci __dev_status(md, param); 9548c2ecf20Sopenharmony_ci dm_put(md); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci return 0; 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic int dev_set_geometry(struct file *filp, struct dm_ioctl *param, size_t param_size) 9608c2ecf20Sopenharmony_ci{ 9618c2ecf20Sopenharmony_ci int r = -EINVAL, x; 9628c2ecf20Sopenharmony_ci struct mapped_device *md; 9638c2ecf20Sopenharmony_ci struct hd_geometry geometry; 9648c2ecf20Sopenharmony_ci unsigned long indata[4]; 9658c2ecf20Sopenharmony_ci char *geostr = (char *) param + param->data_start; 9668c2ecf20Sopenharmony_ci char dummy; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci md = find_device(param); 9698c2ecf20Sopenharmony_ci if (!md) 9708c2ecf20Sopenharmony_ci return -ENXIO; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (geostr < param->data || 9738c2ecf20Sopenharmony_ci invalid_str(geostr, (void *) param + param_size)) { 9748c2ecf20Sopenharmony_ci DMWARN("Invalid geometry supplied."); 9758c2ecf20Sopenharmony_ci goto out; 9768c2ecf20Sopenharmony_ci } 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci x = sscanf(geostr, "%lu %lu %lu %lu%c", indata, 9798c2ecf20Sopenharmony_ci indata + 1, indata + 2, indata + 3, &dummy); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci if (x != 4) { 9828c2ecf20Sopenharmony_ci DMWARN("Unable to interpret geometry settings."); 9838c2ecf20Sopenharmony_ci goto out; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci if (indata[0] > 65535 || indata[1] > 255 || 9878c2ecf20Sopenharmony_ci indata[2] > 255 || indata[3] > ULONG_MAX) { 9888c2ecf20Sopenharmony_ci DMWARN("Geometry exceeds range limits."); 9898c2ecf20Sopenharmony_ci goto out; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci geometry.cylinders = indata[0]; 9938c2ecf20Sopenharmony_ci geometry.heads = indata[1]; 9948c2ecf20Sopenharmony_ci geometry.sectors = indata[2]; 9958c2ecf20Sopenharmony_ci geometry.start = indata[3]; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci r = dm_set_geometry(md, &geometry); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci param->data_size = 0; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ciout: 10028c2ecf20Sopenharmony_ci dm_put(md); 10038c2ecf20Sopenharmony_ci return r; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic int do_suspend(struct dm_ioctl *param) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci int r = 0; 10098c2ecf20Sopenharmony_ci unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG; 10108c2ecf20Sopenharmony_ci struct mapped_device *md; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci md = find_device(param); 10138c2ecf20Sopenharmony_ci if (!md) 10148c2ecf20Sopenharmony_ci return -ENXIO; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if (param->flags & DM_SKIP_LOCKFS_FLAG) 10178c2ecf20Sopenharmony_ci suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; 10188c2ecf20Sopenharmony_ci if (param->flags & DM_NOFLUSH_FLAG) 10198c2ecf20Sopenharmony_ci suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (!dm_suspended_md(md)) { 10228c2ecf20Sopenharmony_ci r = dm_suspend(md, suspend_flags); 10238c2ecf20Sopenharmony_ci if (r) 10248c2ecf20Sopenharmony_ci goto out; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci __dev_status(md, param); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ciout: 10308c2ecf20Sopenharmony_ci dm_put(md); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci return r; 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic int do_resume(struct dm_ioctl *param) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci int r = 0; 10388c2ecf20Sopenharmony_ci unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG; 10398c2ecf20Sopenharmony_ci struct hash_cell *hc; 10408c2ecf20Sopenharmony_ci struct mapped_device *md; 10418c2ecf20Sopenharmony_ci struct dm_table *new_map, *old_map = NULL; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci down_write(&_hash_lock); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci hc = __find_device_hash_cell(param); 10468c2ecf20Sopenharmony_ci if (!hc) { 10478c2ecf20Sopenharmony_ci DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table."); 10488c2ecf20Sopenharmony_ci up_write(&_hash_lock); 10498c2ecf20Sopenharmony_ci return -ENXIO; 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci md = hc->md; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci new_map = hc->new_map; 10558c2ecf20Sopenharmony_ci hc->new_map = NULL; 10568c2ecf20Sopenharmony_ci param->flags &= ~DM_INACTIVE_PRESENT_FLAG; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci up_write(&_hash_lock); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* Do we need to load a new map ? */ 10618c2ecf20Sopenharmony_ci if (new_map) { 10628c2ecf20Sopenharmony_ci /* Suspend if it isn't already suspended */ 10638c2ecf20Sopenharmony_ci if (param->flags & DM_SKIP_LOCKFS_FLAG) 10648c2ecf20Sopenharmony_ci suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; 10658c2ecf20Sopenharmony_ci if (param->flags & DM_NOFLUSH_FLAG) 10668c2ecf20Sopenharmony_ci suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; 10678c2ecf20Sopenharmony_ci if (!dm_suspended_md(md)) 10688c2ecf20Sopenharmony_ci dm_suspend(md, suspend_flags); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci old_map = dm_swap_table(md, new_map); 10718c2ecf20Sopenharmony_ci if (IS_ERR(old_map)) { 10728c2ecf20Sopenharmony_ci dm_sync_table(md); 10738c2ecf20Sopenharmony_ci dm_table_destroy(new_map); 10748c2ecf20Sopenharmony_ci dm_put(md); 10758c2ecf20Sopenharmony_ci return PTR_ERR(old_map); 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (dm_table_get_mode(new_map) & FMODE_WRITE) 10798c2ecf20Sopenharmony_ci set_disk_ro(dm_disk(md), 0); 10808c2ecf20Sopenharmony_ci else 10818c2ecf20Sopenharmony_ci set_disk_ro(dm_disk(md), 1); 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci if (dm_suspended_md(md)) { 10858c2ecf20Sopenharmony_ci r = dm_resume(md); 10868c2ecf20Sopenharmony_ci if (!r && !dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr)) 10878c2ecf20Sopenharmony_ci param->flags |= DM_UEVENT_GENERATED_FLAG; 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci /* 10918c2ecf20Sopenharmony_ci * Since dm_swap_table synchronizes RCU, nobody should be in 10928c2ecf20Sopenharmony_ci * read-side critical section already. 10938c2ecf20Sopenharmony_ci */ 10948c2ecf20Sopenharmony_ci if (old_map) 10958c2ecf20Sopenharmony_ci dm_table_destroy(old_map); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci if (!r) 10988c2ecf20Sopenharmony_ci __dev_status(md, param); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci dm_put(md); 11018c2ecf20Sopenharmony_ci return r; 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci/* 11058c2ecf20Sopenharmony_ci * Set or unset the suspension state of a device. 11068c2ecf20Sopenharmony_ci * If the device already is in the requested state we just return its status. 11078c2ecf20Sopenharmony_ci */ 11088c2ecf20Sopenharmony_cistatic int dev_suspend(struct file *filp, struct dm_ioctl *param, size_t param_size) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci if (param->flags & DM_SUSPEND_FLAG) 11118c2ecf20Sopenharmony_ci return do_suspend(param); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci return do_resume(param); 11148c2ecf20Sopenharmony_ci} 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci/* 11178c2ecf20Sopenharmony_ci * Copies device info back to user space, used by 11188c2ecf20Sopenharmony_ci * the create and info ioctls. 11198c2ecf20Sopenharmony_ci */ 11208c2ecf20Sopenharmony_cistatic int dev_status(struct file *filp, struct dm_ioctl *param, size_t param_size) 11218c2ecf20Sopenharmony_ci{ 11228c2ecf20Sopenharmony_ci struct mapped_device *md; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci md = find_device(param); 11258c2ecf20Sopenharmony_ci if (!md) 11268c2ecf20Sopenharmony_ci return -ENXIO; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci __dev_status(md, param); 11298c2ecf20Sopenharmony_ci dm_put(md); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci return 0; 11328c2ecf20Sopenharmony_ci} 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci/* 11358c2ecf20Sopenharmony_ci * Build up the status struct for each target 11368c2ecf20Sopenharmony_ci */ 11378c2ecf20Sopenharmony_cistatic void retrieve_status(struct dm_table *table, 11388c2ecf20Sopenharmony_ci struct dm_ioctl *param, size_t param_size) 11398c2ecf20Sopenharmony_ci{ 11408c2ecf20Sopenharmony_ci unsigned int i, num_targets; 11418c2ecf20Sopenharmony_ci struct dm_target_spec *spec; 11428c2ecf20Sopenharmony_ci char *outbuf, *outptr; 11438c2ecf20Sopenharmony_ci status_type_t type; 11448c2ecf20Sopenharmony_ci size_t remaining, len, used = 0; 11458c2ecf20Sopenharmony_ci unsigned status_flags = 0; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci outptr = outbuf = get_result_buffer(param, param_size, &len); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (param->flags & DM_STATUS_TABLE_FLAG) 11508c2ecf20Sopenharmony_ci type = STATUSTYPE_TABLE; 11518c2ecf20Sopenharmony_ci else 11528c2ecf20Sopenharmony_ci type = STATUSTYPE_INFO; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci /* Get all the target info */ 11558c2ecf20Sopenharmony_ci num_targets = dm_table_get_num_targets(table); 11568c2ecf20Sopenharmony_ci for (i = 0; i < num_targets; i++) { 11578c2ecf20Sopenharmony_ci struct dm_target *ti = dm_table_get_target(table, i); 11588c2ecf20Sopenharmony_ci size_t l; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci remaining = len - (outptr - outbuf); 11618c2ecf20Sopenharmony_ci if (remaining <= sizeof(struct dm_target_spec)) { 11628c2ecf20Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 11638c2ecf20Sopenharmony_ci break; 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci spec = (struct dm_target_spec *) outptr; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci spec->status = 0; 11698c2ecf20Sopenharmony_ci spec->sector_start = ti->begin; 11708c2ecf20Sopenharmony_ci spec->length = ti->len; 11718c2ecf20Sopenharmony_ci strncpy(spec->target_type, ti->type->name, 11728c2ecf20Sopenharmony_ci sizeof(spec->target_type) - 1); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci outptr += sizeof(struct dm_target_spec); 11758c2ecf20Sopenharmony_ci remaining = len - (outptr - outbuf); 11768c2ecf20Sopenharmony_ci if (remaining <= 0) { 11778c2ecf20Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 11788c2ecf20Sopenharmony_ci break; 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci /* Get the status/table string from the target driver */ 11828c2ecf20Sopenharmony_ci if (ti->type->status) { 11838c2ecf20Sopenharmony_ci if (param->flags & DM_NOFLUSH_FLAG) 11848c2ecf20Sopenharmony_ci status_flags |= DM_STATUS_NOFLUSH_FLAG; 11858c2ecf20Sopenharmony_ci ti->type->status(ti, type, status_flags, outptr, remaining); 11868c2ecf20Sopenharmony_ci } else 11878c2ecf20Sopenharmony_ci outptr[0] = '\0'; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci l = strlen(outptr) + 1; 11908c2ecf20Sopenharmony_ci if (l == remaining) { 11918c2ecf20Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 11928c2ecf20Sopenharmony_ci break; 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci outptr += l; 11968c2ecf20Sopenharmony_ci used = param->data_start + (outptr - outbuf); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci outptr = align_ptr(outptr); 11998c2ecf20Sopenharmony_ci spec->next = outptr - outbuf; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (used) 12038c2ecf20Sopenharmony_ci param->data_size = used; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci param->target_count = num_targets; 12068c2ecf20Sopenharmony_ci} 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci/* 12098c2ecf20Sopenharmony_ci * Wait for a device to report an event 12108c2ecf20Sopenharmony_ci */ 12118c2ecf20Sopenharmony_cistatic int dev_wait(struct file *filp, struct dm_ioctl *param, size_t param_size) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci int r = 0; 12148c2ecf20Sopenharmony_ci struct mapped_device *md; 12158c2ecf20Sopenharmony_ci struct dm_table *table; 12168c2ecf20Sopenharmony_ci int srcu_idx; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci md = find_device(param); 12198c2ecf20Sopenharmony_ci if (!md) 12208c2ecf20Sopenharmony_ci return -ENXIO; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* 12238c2ecf20Sopenharmony_ci * Wait for a notification event 12248c2ecf20Sopenharmony_ci */ 12258c2ecf20Sopenharmony_ci if (dm_wait_event(md, param->event_nr)) { 12268c2ecf20Sopenharmony_ci r = -ERESTARTSYS; 12278c2ecf20Sopenharmony_ci goto out; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci /* 12318c2ecf20Sopenharmony_ci * The userland program is going to want to know what 12328c2ecf20Sopenharmony_ci * changed to trigger the event, so we may as well tell 12338c2ecf20Sopenharmony_ci * him and save an ioctl. 12348c2ecf20Sopenharmony_ci */ 12358c2ecf20Sopenharmony_ci __dev_status(md, param); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci table = dm_get_live_or_inactive_table(md, param, &srcu_idx); 12388c2ecf20Sopenharmony_ci if (table) 12398c2ecf20Sopenharmony_ci retrieve_status(table, param, param_size); 12408c2ecf20Sopenharmony_ci dm_put_live_table(md, srcu_idx); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ciout: 12438c2ecf20Sopenharmony_ci dm_put(md); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci return r; 12468c2ecf20Sopenharmony_ci} 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci/* 12498c2ecf20Sopenharmony_ci * Remember the global event number and make it possible to poll 12508c2ecf20Sopenharmony_ci * for further events. 12518c2ecf20Sopenharmony_ci */ 12528c2ecf20Sopenharmony_cistatic int dev_arm_poll(struct file *filp, struct dm_ioctl *param, size_t param_size) 12538c2ecf20Sopenharmony_ci{ 12548c2ecf20Sopenharmony_ci struct dm_file *priv = filp->private_data; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci priv->global_event_nr = atomic_read(&dm_global_event_nr); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci return 0; 12598c2ecf20Sopenharmony_ci} 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_cistatic inline fmode_t get_mode(struct dm_ioctl *param) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci fmode_t mode = FMODE_READ | FMODE_WRITE; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci if (param->flags & DM_READONLY_FLAG) 12668c2ecf20Sopenharmony_ci mode = FMODE_READ; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci return mode; 12698c2ecf20Sopenharmony_ci} 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_cistatic int next_target(struct dm_target_spec *last, uint32_t next, void *end, 12728c2ecf20Sopenharmony_ci struct dm_target_spec **spec, char **target_params) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci *spec = (struct dm_target_spec *) ((unsigned char *) last + next); 12758c2ecf20Sopenharmony_ci *target_params = (char *) (*spec + 1); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if (*spec < (last + 1)) 12788c2ecf20Sopenharmony_ci return -EINVAL; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci return invalid_str(*target_params, end); 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_cistatic int populate_table(struct dm_table *table, 12848c2ecf20Sopenharmony_ci struct dm_ioctl *param, size_t param_size) 12858c2ecf20Sopenharmony_ci{ 12868c2ecf20Sopenharmony_ci int r; 12878c2ecf20Sopenharmony_ci unsigned int i = 0; 12888c2ecf20Sopenharmony_ci struct dm_target_spec *spec = (struct dm_target_spec *) param; 12898c2ecf20Sopenharmony_ci uint32_t next = param->data_start; 12908c2ecf20Sopenharmony_ci void *end = (void *) param + param_size; 12918c2ecf20Sopenharmony_ci char *target_params; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci if (!param->target_count) { 12948c2ecf20Sopenharmony_ci DMWARN("populate_table: no targets specified"); 12958c2ecf20Sopenharmony_ci return -EINVAL; 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci for (i = 0; i < param->target_count; i++) { 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci r = next_target(spec, next, end, &spec, &target_params); 13018c2ecf20Sopenharmony_ci if (r) { 13028c2ecf20Sopenharmony_ci DMWARN("unable to find target"); 13038c2ecf20Sopenharmony_ci return r; 13048c2ecf20Sopenharmony_ci } 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci r = dm_table_add_target(table, spec->target_type, 13078c2ecf20Sopenharmony_ci (sector_t) spec->sector_start, 13088c2ecf20Sopenharmony_ci (sector_t) spec->length, 13098c2ecf20Sopenharmony_ci target_params); 13108c2ecf20Sopenharmony_ci if (r) { 13118c2ecf20Sopenharmony_ci DMWARN("error adding target to table"); 13128c2ecf20Sopenharmony_ci return r; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci next = spec->next; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci return dm_table_complete(table); 13198c2ecf20Sopenharmony_ci} 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_cistatic bool is_valid_type(enum dm_queue_mode cur, enum dm_queue_mode new) 13228c2ecf20Sopenharmony_ci{ 13238c2ecf20Sopenharmony_ci if (cur == new || 13248c2ecf20Sopenharmony_ci (cur == DM_TYPE_BIO_BASED && new == DM_TYPE_DAX_BIO_BASED)) 13258c2ecf20Sopenharmony_ci return true; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci return false; 13288c2ecf20Sopenharmony_ci} 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_cistatic int table_load(struct file *filp, struct dm_ioctl *param, size_t param_size) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci int r; 13338c2ecf20Sopenharmony_ci struct hash_cell *hc; 13348c2ecf20Sopenharmony_ci struct dm_table *t, *old_map = NULL; 13358c2ecf20Sopenharmony_ci struct mapped_device *md; 13368c2ecf20Sopenharmony_ci struct target_type *immutable_target_type; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci md = find_device(param); 13398c2ecf20Sopenharmony_ci if (!md) 13408c2ecf20Sopenharmony_ci return -ENXIO; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci r = dm_table_create(&t, get_mode(param), param->target_count, md); 13438c2ecf20Sopenharmony_ci if (r) 13448c2ecf20Sopenharmony_ci goto err; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci /* Protect md->type and md->queue against concurrent table loads. */ 13478c2ecf20Sopenharmony_ci dm_lock_md_type(md); 13488c2ecf20Sopenharmony_ci r = populate_table(t, param, param_size); 13498c2ecf20Sopenharmony_ci if (r) 13508c2ecf20Sopenharmony_ci goto err_unlock_md_type; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci immutable_target_type = dm_get_immutable_target_type(md); 13538c2ecf20Sopenharmony_ci if (immutable_target_type && 13548c2ecf20Sopenharmony_ci (immutable_target_type != dm_table_get_immutable_target_type(t)) && 13558c2ecf20Sopenharmony_ci !dm_table_get_wildcard_target(t)) { 13568c2ecf20Sopenharmony_ci DMWARN("can't replace immutable target type %s", 13578c2ecf20Sopenharmony_ci immutable_target_type->name); 13588c2ecf20Sopenharmony_ci r = -EINVAL; 13598c2ecf20Sopenharmony_ci goto err_unlock_md_type; 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci if (dm_get_md_type(md) == DM_TYPE_NONE) { 13638c2ecf20Sopenharmony_ci /* Initial table load: acquire type of table. */ 13648c2ecf20Sopenharmony_ci dm_set_md_type(md, dm_table_get_type(t)); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci /* setup md->queue to reflect md's type (may block) */ 13678c2ecf20Sopenharmony_ci r = dm_setup_md_queue(md, t); 13688c2ecf20Sopenharmony_ci if (r) { 13698c2ecf20Sopenharmony_ci DMWARN("unable to set up device queue for new table."); 13708c2ecf20Sopenharmony_ci goto err_unlock_md_type; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci } else if (!is_valid_type(dm_get_md_type(md), dm_table_get_type(t))) { 13738c2ecf20Sopenharmony_ci DMWARN("can't change device type (old=%u vs new=%u) after initial table load.", 13748c2ecf20Sopenharmony_ci dm_get_md_type(md), dm_table_get_type(t)); 13758c2ecf20Sopenharmony_ci r = -EINVAL; 13768c2ecf20Sopenharmony_ci goto err_unlock_md_type; 13778c2ecf20Sopenharmony_ci } 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci dm_unlock_md_type(md); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci /* stage inactive table */ 13828c2ecf20Sopenharmony_ci down_write(&_hash_lock); 13838c2ecf20Sopenharmony_ci hc = dm_get_mdptr(md); 13848c2ecf20Sopenharmony_ci if (!hc || hc->md != md) { 13858c2ecf20Sopenharmony_ci DMWARN("device has been removed from the dev hash table."); 13868c2ecf20Sopenharmony_ci up_write(&_hash_lock); 13878c2ecf20Sopenharmony_ci r = -ENXIO; 13888c2ecf20Sopenharmony_ci goto err_destroy_table; 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci if (hc->new_map) 13928c2ecf20Sopenharmony_ci old_map = hc->new_map; 13938c2ecf20Sopenharmony_ci hc->new_map = t; 13948c2ecf20Sopenharmony_ci up_write(&_hash_lock); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci param->flags |= DM_INACTIVE_PRESENT_FLAG; 13978c2ecf20Sopenharmony_ci __dev_status(md, param); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci if (old_map) { 14008c2ecf20Sopenharmony_ci dm_sync_table(md); 14018c2ecf20Sopenharmony_ci dm_table_destroy(old_map); 14028c2ecf20Sopenharmony_ci } 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci dm_put(md); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci return 0; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_cierr_unlock_md_type: 14098c2ecf20Sopenharmony_ci dm_unlock_md_type(md); 14108c2ecf20Sopenharmony_cierr_destroy_table: 14118c2ecf20Sopenharmony_ci dm_table_destroy(t); 14128c2ecf20Sopenharmony_cierr: 14138c2ecf20Sopenharmony_ci dm_put(md); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci return r; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_size) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci struct hash_cell *hc; 14218c2ecf20Sopenharmony_ci struct mapped_device *md; 14228c2ecf20Sopenharmony_ci struct dm_table *old_map = NULL; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci down_write(&_hash_lock); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci hc = __find_device_hash_cell(param); 14278c2ecf20Sopenharmony_ci if (!hc) { 14288c2ecf20Sopenharmony_ci DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table."); 14298c2ecf20Sopenharmony_ci up_write(&_hash_lock); 14308c2ecf20Sopenharmony_ci return -ENXIO; 14318c2ecf20Sopenharmony_ci } 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci if (hc->new_map) { 14348c2ecf20Sopenharmony_ci old_map = hc->new_map; 14358c2ecf20Sopenharmony_ci hc->new_map = NULL; 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci md = hc->md; 14398c2ecf20Sopenharmony_ci up_write(&_hash_lock); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci param->flags &= ~DM_INACTIVE_PRESENT_FLAG; 14428c2ecf20Sopenharmony_ci __dev_status(md, param); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci if (old_map) { 14458c2ecf20Sopenharmony_ci dm_sync_table(md); 14468c2ecf20Sopenharmony_ci dm_table_destroy(old_map); 14478c2ecf20Sopenharmony_ci } 14488c2ecf20Sopenharmony_ci dm_put(md); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci return 0; 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci/* 14548c2ecf20Sopenharmony_ci * Retrieves a list of devices used by a particular dm device. 14558c2ecf20Sopenharmony_ci */ 14568c2ecf20Sopenharmony_cistatic void retrieve_deps(struct dm_table *table, 14578c2ecf20Sopenharmony_ci struct dm_ioctl *param, size_t param_size) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci unsigned int count = 0; 14608c2ecf20Sopenharmony_ci struct list_head *tmp; 14618c2ecf20Sopenharmony_ci size_t len, needed; 14628c2ecf20Sopenharmony_ci struct dm_dev_internal *dd; 14638c2ecf20Sopenharmony_ci struct dm_target_deps *deps; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci deps = get_result_buffer(param, param_size, &len); 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci /* 14688c2ecf20Sopenharmony_ci * Count the devices. 14698c2ecf20Sopenharmony_ci */ 14708c2ecf20Sopenharmony_ci list_for_each (tmp, dm_table_get_devices(table)) 14718c2ecf20Sopenharmony_ci count++; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci /* 14748c2ecf20Sopenharmony_ci * Check we have enough space. 14758c2ecf20Sopenharmony_ci */ 14768c2ecf20Sopenharmony_ci needed = struct_size(deps, dev, count); 14778c2ecf20Sopenharmony_ci if (len < needed) { 14788c2ecf20Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 14798c2ecf20Sopenharmony_ci return; 14808c2ecf20Sopenharmony_ci } 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci /* 14838c2ecf20Sopenharmony_ci * Fill in the devices. 14848c2ecf20Sopenharmony_ci */ 14858c2ecf20Sopenharmony_ci deps->count = count; 14868c2ecf20Sopenharmony_ci count = 0; 14878c2ecf20Sopenharmony_ci list_for_each_entry (dd, dm_table_get_devices(table), list) 14888c2ecf20Sopenharmony_ci deps->dev[count++] = huge_encode_dev(dd->dm_dev->bdev->bd_dev); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci param->data_size = param->data_start + needed; 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_cistatic int table_deps(struct file *filp, struct dm_ioctl *param, size_t param_size) 14948c2ecf20Sopenharmony_ci{ 14958c2ecf20Sopenharmony_ci struct mapped_device *md; 14968c2ecf20Sopenharmony_ci struct dm_table *table; 14978c2ecf20Sopenharmony_ci int srcu_idx; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci md = find_device(param); 15008c2ecf20Sopenharmony_ci if (!md) 15018c2ecf20Sopenharmony_ci return -ENXIO; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci __dev_status(md, param); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci table = dm_get_live_or_inactive_table(md, param, &srcu_idx); 15068c2ecf20Sopenharmony_ci if (table) 15078c2ecf20Sopenharmony_ci retrieve_deps(table, param, param_size); 15088c2ecf20Sopenharmony_ci dm_put_live_table(md, srcu_idx); 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci dm_put(md); 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci return 0; 15138c2ecf20Sopenharmony_ci} 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci/* 15168c2ecf20Sopenharmony_ci * Return the status of a device as a text string for each 15178c2ecf20Sopenharmony_ci * target. 15188c2ecf20Sopenharmony_ci */ 15198c2ecf20Sopenharmony_cistatic int table_status(struct file *filp, struct dm_ioctl *param, size_t param_size) 15208c2ecf20Sopenharmony_ci{ 15218c2ecf20Sopenharmony_ci struct mapped_device *md; 15228c2ecf20Sopenharmony_ci struct dm_table *table; 15238c2ecf20Sopenharmony_ci int srcu_idx; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci md = find_device(param); 15268c2ecf20Sopenharmony_ci if (!md) 15278c2ecf20Sopenharmony_ci return -ENXIO; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci __dev_status(md, param); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci table = dm_get_live_or_inactive_table(md, param, &srcu_idx); 15328c2ecf20Sopenharmony_ci if (table) 15338c2ecf20Sopenharmony_ci retrieve_status(table, param, param_size); 15348c2ecf20Sopenharmony_ci dm_put_live_table(md, srcu_idx); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci dm_put(md); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci return 0; 15398c2ecf20Sopenharmony_ci} 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci/* 15428c2ecf20Sopenharmony_ci * Process device-mapper dependent messages. Messages prefixed with '@' 15438c2ecf20Sopenharmony_ci * are processed by the DM core. All others are delivered to the target. 15448c2ecf20Sopenharmony_ci * Returns a number <= 1 if message was processed by device mapper. 15458c2ecf20Sopenharmony_ci * Returns 2 if message should be delivered to the target. 15468c2ecf20Sopenharmony_ci */ 15478c2ecf20Sopenharmony_cistatic int message_for_md(struct mapped_device *md, unsigned argc, char **argv, 15488c2ecf20Sopenharmony_ci char *result, unsigned maxlen) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci int r; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci if (**argv != '@') 15538c2ecf20Sopenharmony_ci return 2; /* no '@' prefix, deliver to target */ 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci if (!strcasecmp(argv[0], "@cancel_deferred_remove")) { 15568c2ecf20Sopenharmony_ci if (argc != 1) { 15578c2ecf20Sopenharmony_ci DMERR("Invalid arguments for @cancel_deferred_remove"); 15588c2ecf20Sopenharmony_ci return -EINVAL; 15598c2ecf20Sopenharmony_ci } 15608c2ecf20Sopenharmony_ci return dm_cancel_deferred_remove(md); 15618c2ecf20Sopenharmony_ci } 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci r = dm_stats_message(md, argc, argv, result, maxlen); 15648c2ecf20Sopenharmony_ci if (r < 2) 15658c2ecf20Sopenharmony_ci return r; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci DMERR("Unsupported message sent to DM core: %s", argv[0]); 15688c2ecf20Sopenharmony_ci return -EINVAL; 15698c2ecf20Sopenharmony_ci} 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci/* 15728c2ecf20Sopenharmony_ci * Pass a message to the target that's at the supplied device offset. 15738c2ecf20Sopenharmony_ci */ 15748c2ecf20Sopenharmony_cistatic int target_message(struct file *filp, struct dm_ioctl *param, size_t param_size) 15758c2ecf20Sopenharmony_ci{ 15768c2ecf20Sopenharmony_ci int r, argc; 15778c2ecf20Sopenharmony_ci char **argv; 15788c2ecf20Sopenharmony_ci struct mapped_device *md; 15798c2ecf20Sopenharmony_ci struct dm_table *table; 15808c2ecf20Sopenharmony_ci struct dm_target *ti; 15818c2ecf20Sopenharmony_ci struct dm_target_msg *tmsg = (void *) param + param->data_start; 15828c2ecf20Sopenharmony_ci size_t maxlen; 15838c2ecf20Sopenharmony_ci char *result = get_result_buffer(param, param_size, &maxlen); 15848c2ecf20Sopenharmony_ci int srcu_idx; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci md = find_device(param); 15878c2ecf20Sopenharmony_ci if (!md) 15888c2ecf20Sopenharmony_ci return -ENXIO; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci if (tmsg < (struct dm_target_msg *) param->data || 15918c2ecf20Sopenharmony_ci invalid_str(tmsg->message, (void *) param + param_size)) { 15928c2ecf20Sopenharmony_ci DMWARN("Invalid target message parameters."); 15938c2ecf20Sopenharmony_ci r = -EINVAL; 15948c2ecf20Sopenharmony_ci goto out; 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci r = dm_split_args(&argc, &argv, tmsg->message); 15988c2ecf20Sopenharmony_ci if (r) { 15998c2ecf20Sopenharmony_ci DMWARN("Failed to split target message parameters"); 16008c2ecf20Sopenharmony_ci goto out; 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci if (!argc) { 16048c2ecf20Sopenharmony_ci DMWARN("Empty message received."); 16058c2ecf20Sopenharmony_ci r = -EINVAL; 16068c2ecf20Sopenharmony_ci goto out_argv; 16078c2ecf20Sopenharmony_ci } 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci r = message_for_md(md, argc, argv, result, maxlen); 16108c2ecf20Sopenharmony_ci if (r <= 1) 16118c2ecf20Sopenharmony_ci goto out_argv; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci table = dm_get_live_table(md, &srcu_idx); 16148c2ecf20Sopenharmony_ci if (!table) 16158c2ecf20Sopenharmony_ci goto out_table; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci if (dm_deleting_md(md)) { 16188c2ecf20Sopenharmony_ci r = -ENXIO; 16198c2ecf20Sopenharmony_ci goto out_table; 16208c2ecf20Sopenharmony_ci } 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci ti = dm_table_find_target(table, tmsg->sector); 16238c2ecf20Sopenharmony_ci if (!ti) { 16248c2ecf20Sopenharmony_ci DMWARN("Target message sector outside device."); 16258c2ecf20Sopenharmony_ci r = -EINVAL; 16268c2ecf20Sopenharmony_ci } else if (ti->type->message) 16278c2ecf20Sopenharmony_ci r = ti->type->message(ti, argc, argv, result, maxlen); 16288c2ecf20Sopenharmony_ci else { 16298c2ecf20Sopenharmony_ci DMWARN("Target type does not support messages"); 16308c2ecf20Sopenharmony_ci r = -EINVAL; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci out_table: 16348c2ecf20Sopenharmony_ci dm_put_live_table(md, srcu_idx); 16358c2ecf20Sopenharmony_ci out_argv: 16368c2ecf20Sopenharmony_ci kfree(argv); 16378c2ecf20Sopenharmony_ci out: 16388c2ecf20Sopenharmony_ci if (r >= 0) 16398c2ecf20Sopenharmony_ci __dev_status(md, param); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci if (r == 1) { 16428c2ecf20Sopenharmony_ci param->flags |= DM_DATA_OUT_FLAG; 16438c2ecf20Sopenharmony_ci if (dm_message_test_buffer_overflow(result, maxlen)) 16448c2ecf20Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 16458c2ecf20Sopenharmony_ci else 16468c2ecf20Sopenharmony_ci param->data_size = param->data_start + strlen(result) + 1; 16478c2ecf20Sopenharmony_ci r = 0; 16488c2ecf20Sopenharmony_ci } 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci dm_put(md); 16518c2ecf20Sopenharmony_ci return r; 16528c2ecf20Sopenharmony_ci} 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci/* 16558c2ecf20Sopenharmony_ci * The ioctl parameter block consists of two parts, a dm_ioctl struct 16568c2ecf20Sopenharmony_ci * followed by a data buffer. This flag is set if the second part, 16578c2ecf20Sopenharmony_ci * which has a variable size, is not used by the function processing 16588c2ecf20Sopenharmony_ci * the ioctl. 16598c2ecf20Sopenharmony_ci */ 16608c2ecf20Sopenharmony_ci#define IOCTL_FLAGS_NO_PARAMS 1 16618c2ecf20Sopenharmony_ci#define IOCTL_FLAGS_ISSUE_GLOBAL_EVENT 2 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci/*----------------------------------------------------------------- 16648c2ecf20Sopenharmony_ci * Implementation of open/close/ioctl on the special char 16658c2ecf20Sopenharmony_ci * device. 16668c2ecf20Sopenharmony_ci *---------------------------------------------------------------*/ 16678c2ecf20Sopenharmony_cistatic ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags) 16688c2ecf20Sopenharmony_ci{ 16698c2ecf20Sopenharmony_ci static const struct { 16708c2ecf20Sopenharmony_ci int cmd; 16718c2ecf20Sopenharmony_ci int flags; 16728c2ecf20Sopenharmony_ci ioctl_fn fn; 16738c2ecf20Sopenharmony_ci } _ioctls[] = { 16748c2ecf20Sopenharmony_ci {DM_VERSION_CMD, 0, NULL}, /* version is dealt with elsewhere */ 16758c2ecf20Sopenharmony_ci {DM_REMOVE_ALL_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, remove_all}, 16768c2ecf20Sopenharmony_ci {DM_LIST_DEVICES_CMD, 0, list_devices}, 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci {DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_create}, 16798c2ecf20Sopenharmony_ci {DM_DEV_REMOVE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_remove}, 16808c2ecf20Sopenharmony_ci {DM_DEV_RENAME_CMD, IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_rename}, 16818c2ecf20Sopenharmony_ci {DM_DEV_SUSPEND_CMD, IOCTL_FLAGS_NO_PARAMS, dev_suspend}, 16828c2ecf20Sopenharmony_ci {DM_DEV_STATUS_CMD, IOCTL_FLAGS_NO_PARAMS, dev_status}, 16838c2ecf20Sopenharmony_ci {DM_DEV_WAIT_CMD, 0, dev_wait}, 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci {DM_TABLE_LOAD_CMD, 0, table_load}, 16868c2ecf20Sopenharmony_ci {DM_TABLE_CLEAR_CMD, IOCTL_FLAGS_NO_PARAMS, table_clear}, 16878c2ecf20Sopenharmony_ci {DM_TABLE_DEPS_CMD, 0, table_deps}, 16888c2ecf20Sopenharmony_ci {DM_TABLE_STATUS_CMD, 0, table_status}, 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci {DM_LIST_VERSIONS_CMD, 0, list_versions}, 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci {DM_TARGET_MSG_CMD, 0, target_message}, 16938c2ecf20Sopenharmony_ci {DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry}, 16948c2ecf20Sopenharmony_ci {DM_DEV_ARM_POLL, IOCTL_FLAGS_NO_PARAMS, dev_arm_poll}, 16958c2ecf20Sopenharmony_ci {DM_GET_TARGET_VERSION, 0, get_target_version}, 16968c2ecf20Sopenharmony_ci }; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci if (unlikely(cmd >= ARRAY_SIZE(_ioctls))) 16998c2ecf20Sopenharmony_ci return NULL; 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci cmd = array_index_nospec(cmd, ARRAY_SIZE(_ioctls)); 17028c2ecf20Sopenharmony_ci *ioctl_flags = _ioctls[cmd].flags; 17038c2ecf20Sopenharmony_ci return _ioctls[cmd].fn; 17048c2ecf20Sopenharmony_ci} 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci/* 17078c2ecf20Sopenharmony_ci * As well as checking the version compatibility this always 17088c2ecf20Sopenharmony_ci * copies the kernel interface version out. 17098c2ecf20Sopenharmony_ci */ 17108c2ecf20Sopenharmony_cistatic int check_version(unsigned int cmd, struct dm_ioctl __user *user) 17118c2ecf20Sopenharmony_ci{ 17128c2ecf20Sopenharmony_ci uint32_t version[3]; 17138c2ecf20Sopenharmony_ci int r = 0; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci if (copy_from_user(version, user->version, sizeof(version))) 17168c2ecf20Sopenharmony_ci return -EFAULT; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci if ((DM_VERSION_MAJOR != version[0]) || 17198c2ecf20Sopenharmony_ci (DM_VERSION_MINOR < version[1])) { 17208c2ecf20Sopenharmony_ci DMWARN("ioctl interface mismatch: " 17218c2ecf20Sopenharmony_ci "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", 17228c2ecf20Sopenharmony_ci DM_VERSION_MAJOR, DM_VERSION_MINOR, 17238c2ecf20Sopenharmony_ci DM_VERSION_PATCHLEVEL, 17248c2ecf20Sopenharmony_ci version[0], version[1], version[2], cmd); 17258c2ecf20Sopenharmony_ci r = -EINVAL; 17268c2ecf20Sopenharmony_ci } 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci /* 17298c2ecf20Sopenharmony_ci * Fill in the kernel version. 17308c2ecf20Sopenharmony_ci */ 17318c2ecf20Sopenharmony_ci version[0] = DM_VERSION_MAJOR; 17328c2ecf20Sopenharmony_ci version[1] = DM_VERSION_MINOR; 17338c2ecf20Sopenharmony_ci version[2] = DM_VERSION_PATCHLEVEL; 17348c2ecf20Sopenharmony_ci if (copy_to_user(user->version, version, sizeof(version))) 17358c2ecf20Sopenharmony_ci return -EFAULT; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci return r; 17388c2ecf20Sopenharmony_ci} 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci#define DM_PARAMS_MALLOC 0x0001 /* Params allocated with kvmalloc() */ 17418c2ecf20Sopenharmony_ci#define DM_WIPE_BUFFER 0x0010 /* Wipe input buffer before returning from ioctl */ 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_cistatic void free_params(struct dm_ioctl *param, size_t param_size, int param_flags) 17448c2ecf20Sopenharmony_ci{ 17458c2ecf20Sopenharmony_ci if (param_flags & DM_WIPE_BUFFER) 17468c2ecf20Sopenharmony_ci memset(param, 0, param_size); 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci if (param_flags & DM_PARAMS_MALLOC) 17498c2ecf20Sopenharmony_ci kvfree(param); 17508c2ecf20Sopenharmony_ci} 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_cistatic int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kernel, 17538c2ecf20Sopenharmony_ci int ioctl_flags, struct dm_ioctl **param, int *param_flags) 17548c2ecf20Sopenharmony_ci{ 17558c2ecf20Sopenharmony_ci struct dm_ioctl *dmi; 17568c2ecf20Sopenharmony_ci int secure_data; 17578c2ecf20Sopenharmony_ci const size_t minimum_data_size = offsetof(struct dm_ioctl, data); 17588c2ecf20Sopenharmony_ci unsigned noio_flag; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci if (copy_from_user(param_kernel, user, minimum_data_size)) 17618c2ecf20Sopenharmony_ci return -EFAULT; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci if (unlikely(param_kernel->data_size < minimum_data_size) || 17648c2ecf20Sopenharmony_ci unlikely(param_kernel->data_size > DM_MAX_TARGETS * DM_MAX_TARGET_PARAMS)) 17658c2ecf20Sopenharmony_ci return -EINVAL; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci secure_data = param_kernel->flags & DM_SECURE_DATA_FLAG; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci *param_flags = secure_data ? DM_WIPE_BUFFER : 0; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci if (ioctl_flags & IOCTL_FLAGS_NO_PARAMS) { 17728c2ecf20Sopenharmony_ci dmi = param_kernel; 17738c2ecf20Sopenharmony_ci dmi->data_size = minimum_data_size; 17748c2ecf20Sopenharmony_ci goto data_copied; 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci /* 17788c2ecf20Sopenharmony_ci * Use __GFP_HIGH to avoid low memory issues when a device is 17798c2ecf20Sopenharmony_ci * suspended and the ioctl is needed to resume it. 17808c2ecf20Sopenharmony_ci * Use kmalloc() rather than vmalloc() when we can. 17818c2ecf20Sopenharmony_ci */ 17828c2ecf20Sopenharmony_ci dmi = NULL; 17838c2ecf20Sopenharmony_ci noio_flag = memalloc_noio_save(); 17848c2ecf20Sopenharmony_ci dmi = kvmalloc(param_kernel->data_size, GFP_KERNEL | __GFP_HIGH); 17858c2ecf20Sopenharmony_ci memalloc_noio_restore(noio_flag); 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci if (!dmi) { 17888c2ecf20Sopenharmony_ci if (secure_data && clear_user(user, param_kernel->data_size)) 17898c2ecf20Sopenharmony_ci return -EFAULT; 17908c2ecf20Sopenharmony_ci return -ENOMEM; 17918c2ecf20Sopenharmony_ci } 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci *param_flags |= DM_PARAMS_MALLOC; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci /* Copy from param_kernel (which was already copied from user) */ 17968c2ecf20Sopenharmony_ci memcpy(dmi, param_kernel, minimum_data_size); 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci if (copy_from_user(&dmi->data, (char __user *)user + minimum_data_size, 17998c2ecf20Sopenharmony_ci param_kernel->data_size - minimum_data_size)) 18008c2ecf20Sopenharmony_ci goto bad; 18018c2ecf20Sopenharmony_cidata_copied: 18028c2ecf20Sopenharmony_ci /* Wipe the user buffer so we do not return it to userspace */ 18038c2ecf20Sopenharmony_ci if (secure_data && clear_user(user, param_kernel->data_size)) 18048c2ecf20Sopenharmony_ci goto bad; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci *param = dmi; 18078c2ecf20Sopenharmony_ci return 0; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_cibad: 18108c2ecf20Sopenharmony_ci free_params(dmi, param_kernel->data_size, *param_flags); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci return -EFAULT; 18138c2ecf20Sopenharmony_ci} 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_cistatic int validate_params(uint cmd, struct dm_ioctl *param) 18168c2ecf20Sopenharmony_ci{ 18178c2ecf20Sopenharmony_ci /* Always clear this flag */ 18188c2ecf20Sopenharmony_ci param->flags &= ~DM_BUFFER_FULL_FLAG; 18198c2ecf20Sopenharmony_ci param->flags &= ~DM_UEVENT_GENERATED_FLAG; 18208c2ecf20Sopenharmony_ci param->flags &= ~DM_SECURE_DATA_FLAG; 18218c2ecf20Sopenharmony_ci param->flags &= ~DM_DATA_OUT_FLAG; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci /* Ignores parameters */ 18248c2ecf20Sopenharmony_ci if (cmd == DM_REMOVE_ALL_CMD || 18258c2ecf20Sopenharmony_ci cmd == DM_LIST_DEVICES_CMD || 18268c2ecf20Sopenharmony_ci cmd == DM_LIST_VERSIONS_CMD) 18278c2ecf20Sopenharmony_ci return 0; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci if (cmd == DM_DEV_CREATE_CMD) { 18308c2ecf20Sopenharmony_ci if (!*param->name) { 18318c2ecf20Sopenharmony_ci DMWARN("name not supplied when creating device"); 18328c2ecf20Sopenharmony_ci return -EINVAL; 18338c2ecf20Sopenharmony_ci } 18348c2ecf20Sopenharmony_ci } else if (*param->uuid && *param->name) { 18358c2ecf20Sopenharmony_ci DMWARN("only supply one of name or uuid, cmd(%u)", cmd); 18368c2ecf20Sopenharmony_ci return -EINVAL; 18378c2ecf20Sopenharmony_ci } 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci /* Ensure strings are terminated */ 18408c2ecf20Sopenharmony_ci param->name[DM_NAME_LEN - 1] = '\0'; 18418c2ecf20Sopenharmony_ci param->uuid[DM_UUID_LEN - 1] = '\0'; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci return 0; 18448c2ecf20Sopenharmony_ci} 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_cistatic int ctl_ioctl(struct file *file, uint command, struct dm_ioctl __user *user) 18478c2ecf20Sopenharmony_ci{ 18488c2ecf20Sopenharmony_ci int r = 0; 18498c2ecf20Sopenharmony_ci int ioctl_flags; 18508c2ecf20Sopenharmony_ci int param_flags; 18518c2ecf20Sopenharmony_ci unsigned int cmd; 18528c2ecf20Sopenharmony_ci struct dm_ioctl *param; 18538c2ecf20Sopenharmony_ci ioctl_fn fn = NULL; 18548c2ecf20Sopenharmony_ci size_t input_param_size; 18558c2ecf20Sopenharmony_ci struct dm_ioctl param_kernel; 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci /* only root can play with this */ 18588c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 18598c2ecf20Sopenharmony_ci return -EACCES; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci if (_IOC_TYPE(command) != DM_IOCTL) 18628c2ecf20Sopenharmony_ci return -ENOTTY; 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci cmd = _IOC_NR(command); 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci /* 18678c2ecf20Sopenharmony_ci * Check the interface version passed in. This also 18688c2ecf20Sopenharmony_ci * writes out the kernel's interface version. 18698c2ecf20Sopenharmony_ci */ 18708c2ecf20Sopenharmony_ci r = check_version(cmd, user); 18718c2ecf20Sopenharmony_ci if (r) 18728c2ecf20Sopenharmony_ci return r; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci /* 18758c2ecf20Sopenharmony_ci * Nothing more to do for the version command. 18768c2ecf20Sopenharmony_ci */ 18778c2ecf20Sopenharmony_ci if (cmd == DM_VERSION_CMD) 18788c2ecf20Sopenharmony_ci return 0; 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci fn = lookup_ioctl(cmd, &ioctl_flags); 18818c2ecf20Sopenharmony_ci if (!fn) { 18828c2ecf20Sopenharmony_ci DMWARN("dm_ctl_ioctl: unknown command 0x%x", command); 18838c2ecf20Sopenharmony_ci return -ENOTTY; 18848c2ecf20Sopenharmony_ci } 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci /* 18878c2ecf20Sopenharmony_ci * Copy the parameters into kernel space. 18888c2ecf20Sopenharmony_ci */ 18898c2ecf20Sopenharmony_ci r = copy_params(user, ¶m_kernel, ioctl_flags, ¶m, ¶m_flags); 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci if (r) 18928c2ecf20Sopenharmony_ci return r; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci input_param_size = param->data_size; 18958c2ecf20Sopenharmony_ci r = validate_params(cmd, param); 18968c2ecf20Sopenharmony_ci if (r) 18978c2ecf20Sopenharmony_ci goto out; 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci param->data_size = offsetof(struct dm_ioctl, data); 19008c2ecf20Sopenharmony_ci r = fn(file, param, input_param_size); 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci if (unlikely(param->flags & DM_BUFFER_FULL_FLAG) && 19038c2ecf20Sopenharmony_ci unlikely(ioctl_flags & IOCTL_FLAGS_NO_PARAMS)) 19048c2ecf20Sopenharmony_ci DMERR("ioctl %d tried to output some data but has IOCTL_FLAGS_NO_PARAMS set", cmd); 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci if (!r && ioctl_flags & IOCTL_FLAGS_ISSUE_GLOBAL_EVENT) 19078c2ecf20Sopenharmony_ci dm_issue_global_event(); 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci /* 19108c2ecf20Sopenharmony_ci * Copy the results back to userland. 19118c2ecf20Sopenharmony_ci */ 19128c2ecf20Sopenharmony_ci if (!r && copy_to_user(user, param, param->data_size)) 19138c2ecf20Sopenharmony_ci r = -EFAULT; 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ciout: 19168c2ecf20Sopenharmony_ci free_params(param, input_param_size, param_flags); 19178c2ecf20Sopenharmony_ci return r; 19188c2ecf20Sopenharmony_ci} 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_cistatic long dm_ctl_ioctl(struct file *file, uint command, ulong u) 19218c2ecf20Sopenharmony_ci{ 19228c2ecf20Sopenharmony_ci return (long)ctl_ioctl(file, command, (struct dm_ioctl __user *)u); 19238c2ecf20Sopenharmony_ci} 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 19268c2ecf20Sopenharmony_cistatic long dm_compat_ctl_ioctl(struct file *file, uint command, ulong u) 19278c2ecf20Sopenharmony_ci{ 19288c2ecf20Sopenharmony_ci return (long)dm_ctl_ioctl(file, command, (ulong) compat_ptr(u)); 19298c2ecf20Sopenharmony_ci} 19308c2ecf20Sopenharmony_ci#else 19318c2ecf20Sopenharmony_ci#define dm_compat_ctl_ioctl NULL 19328c2ecf20Sopenharmony_ci#endif 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_cistatic int dm_open(struct inode *inode, struct file *filp) 19358c2ecf20Sopenharmony_ci{ 19368c2ecf20Sopenharmony_ci int r; 19378c2ecf20Sopenharmony_ci struct dm_file *priv; 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci r = nonseekable_open(inode, filp); 19408c2ecf20Sopenharmony_ci if (unlikely(r)) 19418c2ecf20Sopenharmony_ci return r; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci priv = filp->private_data = kmalloc(sizeof(struct dm_file), GFP_KERNEL); 19448c2ecf20Sopenharmony_ci if (!priv) 19458c2ecf20Sopenharmony_ci return -ENOMEM; 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci priv->global_event_nr = atomic_read(&dm_global_event_nr); 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci return 0; 19508c2ecf20Sopenharmony_ci} 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_cistatic int dm_release(struct inode *inode, struct file *filp) 19538c2ecf20Sopenharmony_ci{ 19548c2ecf20Sopenharmony_ci kfree(filp->private_data); 19558c2ecf20Sopenharmony_ci return 0; 19568c2ecf20Sopenharmony_ci} 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_cistatic __poll_t dm_poll(struct file *filp, poll_table *wait) 19598c2ecf20Sopenharmony_ci{ 19608c2ecf20Sopenharmony_ci struct dm_file *priv = filp->private_data; 19618c2ecf20Sopenharmony_ci __poll_t mask = 0; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci poll_wait(filp, &dm_global_eventq, wait); 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci if ((int)(atomic_read(&dm_global_event_nr) - priv->global_event_nr) > 0) 19668c2ecf20Sopenharmony_ci mask |= EPOLLIN; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci return mask; 19698c2ecf20Sopenharmony_ci} 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_cistatic const struct file_operations _ctl_fops = { 19728c2ecf20Sopenharmony_ci .open = dm_open, 19738c2ecf20Sopenharmony_ci .release = dm_release, 19748c2ecf20Sopenharmony_ci .poll = dm_poll, 19758c2ecf20Sopenharmony_ci .unlocked_ioctl = dm_ctl_ioctl, 19768c2ecf20Sopenharmony_ci .compat_ioctl = dm_compat_ctl_ioctl, 19778c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 19788c2ecf20Sopenharmony_ci .llseek = noop_llseek, 19798c2ecf20Sopenharmony_ci}; 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_cistatic struct miscdevice _dm_misc = { 19828c2ecf20Sopenharmony_ci .minor = MAPPER_CTRL_MINOR, 19838c2ecf20Sopenharmony_ci .name = DM_NAME, 19848c2ecf20Sopenharmony_ci .nodename = DM_DIR "/" DM_CONTROL_NODE, 19858c2ecf20Sopenharmony_ci .fops = &_ctl_fops 19868c2ecf20Sopenharmony_ci}; 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ciMODULE_ALIAS_MISCDEV(MAPPER_CTRL_MINOR); 19898c2ecf20Sopenharmony_ciMODULE_ALIAS("devname:" DM_DIR "/" DM_CONTROL_NODE); 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci/* 19928c2ecf20Sopenharmony_ci * Create misc character device and link to DM_DIR/control. 19938c2ecf20Sopenharmony_ci */ 19948c2ecf20Sopenharmony_ciint __init dm_interface_init(void) 19958c2ecf20Sopenharmony_ci{ 19968c2ecf20Sopenharmony_ci int r; 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci r = dm_hash_init(); 19998c2ecf20Sopenharmony_ci if (r) 20008c2ecf20Sopenharmony_ci return r; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci r = misc_register(&_dm_misc); 20038c2ecf20Sopenharmony_ci if (r) { 20048c2ecf20Sopenharmony_ci DMERR("misc_register failed for control device"); 20058c2ecf20Sopenharmony_ci dm_hash_exit(); 20068c2ecf20Sopenharmony_ci return r; 20078c2ecf20Sopenharmony_ci } 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR, 20108c2ecf20Sopenharmony_ci DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA, 20118c2ecf20Sopenharmony_ci DM_DRIVER_EMAIL); 20128c2ecf20Sopenharmony_ci return 0; 20138c2ecf20Sopenharmony_ci} 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_civoid dm_interface_exit(void) 20168c2ecf20Sopenharmony_ci{ 20178c2ecf20Sopenharmony_ci misc_deregister(&_dm_misc); 20188c2ecf20Sopenharmony_ci dm_hash_exit(); 20198c2ecf20Sopenharmony_ci} 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci/** 20228c2ecf20Sopenharmony_ci * dm_copy_name_and_uuid - Copy mapped device name & uuid into supplied buffers 20238c2ecf20Sopenharmony_ci * @md: Pointer to mapped_device 20248c2ecf20Sopenharmony_ci * @name: Buffer (size DM_NAME_LEN) for name 20258c2ecf20Sopenharmony_ci * @uuid: Buffer (size DM_UUID_LEN) for uuid or empty string if uuid not defined 20268c2ecf20Sopenharmony_ci */ 20278c2ecf20Sopenharmony_ciint dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid) 20288c2ecf20Sopenharmony_ci{ 20298c2ecf20Sopenharmony_ci int r = 0; 20308c2ecf20Sopenharmony_ci struct hash_cell *hc; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci if (!md) 20338c2ecf20Sopenharmony_ci return -ENXIO; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci mutex_lock(&dm_hash_cells_mutex); 20368c2ecf20Sopenharmony_ci hc = dm_get_mdptr(md); 20378c2ecf20Sopenharmony_ci if (!hc || hc->md != md) { 20388c2ecf20Sopenharmony_ci r = -ENXIO; 20398c2ecf20Sopenharmony_ci goto out; 20408c2ecf20Sopenharmony_ci } 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci if (name) 20438c2ecf20Sopenharmony_ci strcpy(name, hc->name); 20448c2ecf20Sopenharmony_ci if (uuid) 20458c2ecf20Sopenharmony_ci strcpy(uuid, hc->uuid ? : ""); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ciout: 20488c2ecf20Sopenharmony_ci mutex_unlock(&dm_hash_cells_mutex); 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci return r; 20518c2ecf20Sopenharmony_ci} 20528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dm_copy_name_and_uuid); 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci/** 20558c2ecf20Sopenharmony_ci * dm_early_create - create a mapped device in early boot. 20568c2ecf20Sopenharmony_ci * 20578c2ecf20Sopenharmony_ci * @dmi: Contains main information of the device mapping to be created. 20588c2ecf20Sopenharmony_ci * @spec_array: array of pointers to struct dm_target_spec. Describes the 20598c2ecf20Sopenharmony_ci * mapping table of the device. 20608c2ecf20Sopenharmony_ci * @target_params_array: array of strings with the parameters to a specific 20618c2ecf20Sopenharmony_ci * target. 20628c2ecf20Sopenharmony_ci * 20638c2ecf20Sopenharmony_ci * Instead of having the struct dm_target_spec and the parameters for every 20648c2ecf20Sopenharmony_ci * target embedded at the end of struct dm_ioctl (as performed in a normal 20658c2ecf20Sopenharmony_ci * ioctl), pass them as arguments, so the caller doesn't need to serialize them. 20668c2ecf20Sopenharmony_ci * The size of the spec_array and target_params_array is given by 20678c2ecf20Sopenharmony_ci * @dmi->target_count. 20688c2ecf20Sopenharmony_ci * This function is supposed to be called in early boot, so locking mechanisms 20698c2ecf20Sopenharmony_ci * to protect against concurrent loads are not required. 20708c2ecf20Sopenharmony_ci */ 20718c2ecf20Sopenharmony_ciint __init dm_early_create(struct dm_ioctl *dmi, 20728c2ecf20Sopenharmony_ci struct dm_target_spec **spec_array, 20738c2ecf20Sopenharmony_ci char **target_params_array) 20748c2ecf20Sopenharmony_ci{ 20758c2ecf20Sopenharmony_ci int r, m = DM_ANY_MINOR; 20768c2ecf20Sopenharmony_ci struct dm_table *t, *old_map; 20778c2ecf20Sopenharmony_ci struct mapped_device *md; 20788c2ecf20Sopenharmony_ci unsigned int i; 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci if (!dmi->target_count) 20818c2ecf20Sopenharmony_ci return -EINVAL; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci r = check_name(dmi->name); 20848c2ecf20Sopenharmony_ci if (r) 20858c2ecf20Sopenharmony_ci return r; 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci if (dmi->flags & DM_PERSISTENT_DEV_FLAG) 20888c2ecf20Sopenharmony_ci m = MINOR(huge_decode_dev(dmi->dev)); 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci /* alloc dm device */ 20918c2ecf20Sopenharmony_ci r = dm_create(m, &md); 20928c2ecf20Sopenharmony_ci if (r) 20938c2ecf20Sopenharmony_ci return r; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci /* hash insert */ 20968c2ecf20Sopenharmony_ci r = dm_hash_insert(dmi->name, *dmi->uuid ? dmi->uuid : NULL, md); 20978c2ecf20Sopenharmony_ci if (r) 20988c2ecf20Sopenharmony_ci goto err_destroy_dm; 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci /* alloc table */ 21018c2ecf20Sopenharmony_ci r = dm_table_create(&t, get_mode(dmi), dmi->target_count, md); 21028c2ecf20Sopenharmony_ci if (r) 21038c2ecf20Sopenharmony_ci goto err_hash_remove; 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci /* add targets */ 21068c2ecf20Sopenharmony_ci for (i = 0; i < dmi->target_count; i++) { 21078c2ecf20Sopenharmony_ci r = dm_table_add_target(t, spec_array[i]->target_type, 21088c2ecf20Sopenharmony_ci (sector_t) spec_array[i]->sector_start, 21098c2ecf20Sopenharmony_ci (sector_t) spec_array[i]->length, 21108c2ecf20Sopenharmony_ci target_params_array[i]); 21118c2ecf20Sopenharmony_ci if (r) { 21128c2ecf20Sopenharmony_ci DMWARN("error adding target to table"); 21138c2ecf20Sopenharmony_ci goto err_destroy_table; 21148c2ecf20Sopenharmony_ci } 21158c2ecf20Sopenharmony_ci } 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci /* finish table */ 21188c2ecf20Sopenharmony_ci r = dm_table_complete(t); 21198c2ecf20Sopenharmony_ci if (r) 21208c2ecf20Sopenharmony_ci goto err_destroy_table; 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci md->type = dm_table_get_type(t); 21238c2ecf20Sopenharmony_ci /* setup md->queue to reflect md's type (may block) */ 21248c2ecf20Sopenharmony_ci r = dm_setup_md_queue(md, t); 21258c2ecf20Sopenharmony_ci if (r) { 21268c2ecf20Sopenharmony_ci DMWARN("unable to set up device queue for new table."); 21278c2ecf20Sopenharmony_ci goto err_destroy_table; 21288c2ecf20Sopenharmony_ci } 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci /* Set new map */ 21318c2ecf20Sopenharmony_ci dm_suspend(md, 0); 21328c2ecf20Sopenharmony_ci old_map = dm_swap_table(md, t); 21338c2ecf20Sopenharmony_ci if (IS_ERR(old_map)) { 21348c2ecf20Sopenharmony_ci r = PTR_ERR(old_map); 21358c2ecf20Sopenharmony_ci goto err_destroy_table; 21368c2ecf20Sopenharmony_ci } 21378c2ecf20Sopenharmony_ci set_disk_ro(dm_disk(md), !!(dmi->flags & DM_READONLY_FLAG)); 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci /* resume device */ 21408c2ecf20Sopenharmony_ci r = dm_resume(md); 21418c2ecf20Sopenharmony_ci if (r) 21428c2ecf20Sopenharmony_ci goto err_destroy_table; 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name); 21458c2ecf20Sopenharmony_ci dm_put(md); 21468c2ecf20Sopenharmony_ci return 0; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_cierr_destroy_table: 21498c2ecf20Sopenharmony_ci dm_table_destroy(t); 21508c2ecf20Sopenharmony_cierr_hash_remove: 21518c2ecf20Sopenharmony_ci (void) __hash_remove(__get_name_cell(dmi->name)); 21528c2ecf20Sopenharmony_ci /* release reference from __get_name_cell */ 21538c2ecf20Sopenharmony_ci dm_put(md); 21548c2ecf20Sopenharmony_cierr_destroy_dm: 21558c2ecf20Sopenharmony_ci dm_put(md); 21568c2ecf20Sopenharmony_ci dm_destroy(md); 21578c2ecf20Sopenharmony_ci return r; 21588c2ecf20Sopenharmony_ci} 2159