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, &param_kernel, ioctl_flags, &param, &param_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