13bd94003SHeinz Mauelshagen // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
42b06cfffSAlasdair G Kergon * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved.
51da177e4SLinus Torvalds *
61da177e4SLinus Torvalds * This file is released under the GPL.
71da177e4SLinus Torvalds */
81da177e4SLinus Torvalds
94cc96131SMike Snitzer #include "dm-core.h"
1091ccbbacSTushar Sugandhi #include "dm-ima.h"
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/vmalloc.h>
131da177e4SLinus Torvalds #include <linux/miscdevice.h>
145b3cc15aSIngo Molnar #include <linux/sched/mm.h>
151da177e4SLinus Torvalds #include <linux/init.h>
161da177e4SLinus Torvalds #include <linux/wait.h>
171da177e4SLinus Torvalds #include <linux/slab.h>
18b82096afSMikulas Patocka #include <linux/rbtree.h>
191da177e4SLinus Torvalds #include <linux/dm-ioctl.h>
203ac51e74SDarrick J. Wong #include <linux/hdreg.h>
2176c072b4SMilan Broz #include <linux/compat.h>
22cd9c88daSJordy Zomer #include <linux/nospec.h>
231da177e4SLinus Torvalds
247c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
2591ccbbacSTushar Sugandhi #include <linux/ima.h>
261da177e4SLinus Torvalds
2772d94861SAlasdair G Kergon #define DM_MSG_PREFIX "ioctl"
281da177e4SLinus Torvalds #define DM_DRIVER_EMAIL "dm-devel@redhat.com"
291da177e4SLinus Torvalds
3093e6442cSMikulas Patocka struct dm_file {
3193e6442cSMikulas Patocka /*
3293e6442cSMikulas Patocka * poll will wait until the global event number is greater than
3393e6442cSMikulas Patocka * this value.
3493e6442cSMikulas Patocka */
3586a3238cSHeinz Mauelshagen volatile unsigned int global_event_nr;
3693e6442cSMikulas Patocka };
3793e6442cSMikulas Patocka
38a4a82ce3SHeinz Mauelshagen /*
39a4a82ce3SHeinz Mauelshagen *---------------------------------------------------------------
401da177e4SLinus Torvalds * The ioctl interface needs to be able to look up devices by
411da177e4SLinus Torvalds * name or uuid.
42a4a82ce3SHeinz Mauelshagen *---------------------------------------------------------------
43a4a82ce3SHeinz Mauelshagen */
441da177e4SLinus Torvalds struct hash_cell {
45b82096afSMikulas Patocka struct rb_node name_node;
46b82096afSMikulas Patocka struct rb_node uuid_node;
47b82096afSMikulas Patocka bool name_set;
48b82096afSMikulas Patocka bool uuid_set;
491da177e4SLinus Torvalds
501da177e4SLinus Torvalds char *name;
511da177e4SLinus Torvalds char *uuid;
521da177e4SLinus Torvalds struct mapped_device *md;
531da177e4SLinus Torvalds struct dm_table *new_map;
541da177e4SLinus Torvalds };
551da177e4SLinus Torvalds
561da177e4SLinus Torvalds struct vers_iter {
571da177e4SLinus Torvalds size_t param_size;
581da177e4SLinus Torvalds struct dm_target_versions *vers, *old_vers;
591da177e4SLinus Torvalds char *end;
601da177e4SLinus Torvalds uint32_t flags;
611da177e4SLinus Torvalds };
621da177e4SLinus Torvalds
631da177e4SLinus Torvalds
64b82096afSMikulas Patocka static struct rb_root name_rb_tree = RB_ROOT;
65b82096afSMikulas Patocka static struct rb_root uuid_rb_tree = RB_ROOT;
661da177e4SLinus Torvalds
672c140a24SMikulas Patocka static void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred);
681da177e4SLinus Torvalds
691da177e4SLinus Torvalds /*
701da177e4SLinus Torvalds * Guards access to both hash tables.
711da177e4SLinus Torvalds */
721da177e4SLinus Torvalds static DECLARE_RWSEM(_hash_lock);
731da177e4SLinus Torvalds
746076905bSMikulas Patocka /*
756076905bSMikulas Patocka * Protects use of mdptr to obtain hash cell name and uuid from mapped device.
766076905bSMikulas Patocka */
776076905bSMikulas Patocka static DEFINE_MUTEX(dm_hash_cells_mutex);
786076905bSMikulas Patocka
dm_hash_exit(void)791da177e4SLinus Torvalds static void dm_hash_exit(void)
801da177e4SLinus Torvalds {
812c140a24SMikulas Patocka dm_hash_remove_all(false, false, false);
821da177e4SLinus Torvalds }
831da177e4SLinus Torvalds
84a4a82ce3SHeinz Mauelshagen /*
85a4a82ce3SHeinz Mauelshagen *---------------------------------------------------------------
861da177e4SLinus Torvalds * Code for looking up a device by name
87a4a82ce3SHeinz Mauelshagen *---------------------------------------------------------------
88a4a82ce3SHeinz Mauelshagen */
__get_name_cell(const char * str)891da177e4SLinus Torvalds static struct hash_cell *__get_name_cell(const char *str)
901da177e4SLinus Torvalds {
91b82096afSMikulas Patocka struct rb_node *n = name_rb_tree.rb_node;
921da177e4SLinus Torvalds
93b82096afSMikulas Patocka while (n) {
94b82096afSMikulas Patocka struct hash_cell *hc = container_of(n, struct hash_cell, name_node);
950ef0b471SHeinz Mauelshagen int c;
960ef0b471SHeinz Mauelshagen
970ef0b471SHeinz Mauelshagen c = strcmp(hc->name, str);
98b82096afSMikulas Patocka if (!c) {
997ec75f25SJeff Mahoney dm_get(hc->md);
1001da177e4SLinus Torvalds return hc;
1017ec75f25SJeff Mahoney }
102b82096afSMikulas Patocka n = c >= 0 ? n->rb_left : n->rb_right;
103b82096afSMikulas Patocka }
1041da177e4SLinus Torvalds
1051da177e4SLinus Torvalds return NULL;
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds
__get_uuid_cell(const char * str)1081da177e4SLinus Torvalds static struct hash_cell *__get_uuid_cell(const char *str)
1091da177e4SLinus Torvalds {
110b82096afSMikulas Patocka struct rb_node *n = uuid_rb_tree.rb_node;
1111da177e4SLinus Torvalds
112b82096afSMikulas Patocka while (n) {
113b82096afSMikulas Patocka struct hash_cell *hc = container_of(n, struct hash_cell, uuid_node);
1140ef0b471SHeinz Mauelshagen int c;
1150ef0b471SHeinz Mauelshagen
1160ef0b471SHeinz Mauelshagen c = strcmp(hc->uuid, str);
117b82096afSMikulas Patocka if (!c) {
1187ec75f25SJeff Mahoney dm_get(hc->md);
1191da177e4SLinus Torvalds return hc;
1207ec75f25SJeff Mahoney }
121b82096afSMikulas Patocka n = c >= 0 ? n->rb_left : n->rb_right;
122b82096afSMikulas Patocka }
1231da177e4SLinus Torvalds
1241da177e4SLinus Torvalds return NULL;
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds
__unlink_name(struct hash_cell * hc)127b82096afSMikulas Patocka static void __unlink_name(struct hash_cell *hc)
128b82096afSMikulas Patocka {
129b82096afSMikulas Patocka if (hc->name_set) {
130b82096afSMikulas Patocka hc->name_set = false;
131b82096afSMikulas Patocka rb_erase(&hc->name_node, &name_rb_tree);
132b82096afSMikulas Patocka }
133b82096afSMikulas Patocka }
134b82096afSMikulas Patocka
__unlink_uuid(struct hash_cell * hc)135b82096afSMikulas Patocka static void __unlink_uuid(struct hash_cell *hc)
136b82096afSMikulas Patocka {
137b82096afSMikulas Patocka if (hc->uuid_set) {
138b82096afSMikulas Patocka hc->uuid_set = false;
139b82096afSMikulas Patocka rb_erase(&hc->uuid_node, &uuid_rb_tree);
140b82096afSMikulas Patocka }
141b82096afSMikulas Patocka }
142b82096afSMikulas Patocka
__link_name(struct hash_cell * new_hc)143b82096afSMikulas Patocka static void __link_name(struct hash_cell *new_hc)
144b82096afSMikulas Patocka {
145b82096afSMikulas Patocka struct rb_node **n, *parent;
146b82096afSMikulas Patocka
147b82096afSMikulas Patocka __unlink_name(new_hc);
148b82096afSMikulas Patocka
149b82096afSMikulas Patocka new_hc->name_set = true;
150b82096afSMikulas Patocka
151b82096afSMikulas Patocka n = &name_rb_tree.rb_node;
152b82096afSMikulas Patocka parent = NULL;
153b82096afSMikulas Patocka
154b82096afSMikulas Patocka while (*n) {
155b82096afSMikulas Patocka struct hash_cell *hc = container_of(*n, struct hash_cell, name_node);
1560ef0b471SHeinz Mauelshagen int c;
1570ef0b471SHeinz Mauelshagen
1580ef0b471SHeinz Mauelshagen c = strcmp(hc->name, new_hc->name);
159b82096afSMikulas Patocka BUG_ON(!c);
160b82096afSMikulas Patocka parent = *n;
161b82096afSMikulas Patocka n = c >= 0 ? &hc->name_node.rb_left : &hc->name_node.rb_right;
162b82096afSMikulas Patocka }
163b82096afSMikulas Patocka
164b82096afSMikulas Patocka rb_link_node(&new_hc->name_node, parent, n);
165b82096afSMikulas Patocka rb_insert_color(&new_hc->name_node, &name_rb_tree);
166b82096afSMikulas Patocka }
167b82096afSMikulas Patocka
__link_uuid(struct hash_cell * new_hc)168b82096afSMikulas Patocka static void __link_uuid(struct hash_cell *new_hc)
169b82096afSMikulas Patocka {
170b82096afSMikulas Patocka struct rb_node **n, *parent;
171b82096afSMikulas Patocka
172b82096afSMikulas Patocka __unlink_uuid(new_hc);
173b82096afSMikulas Patocka
174b82096afSMikulas Patocka new_hc->uuid_set = true;
175b82096afSMikulas Patocka
176b82096afSMikulas Patocka n = &uuid_rb_tree.rb_node;
177b82096afSMikulas Patocka parent = NULL;
178b82096afSMikulas Patocka
179b82096afSMikulas Patocka while (*n) {
180b82096afSMikulas Patocka struct hash_cell *hc = container_of(*n, struct hash_cell, uuid_node);
1810ef0b471SHeinz Mauelshagen int c;
1820ef0b471SHeinz Mauelshagen
1830ef0b471SHeinz Mauelshagen c = strcmp(hc->uuid, new_hc->uuid);
184b82096afSMikulas Patocka BUG_ON(!c);
185b82096afSMikulas Patocka parent = *n;
186b82096afSMikulas Patocka n = c > 0 ? &hc->uuid_node.rb_left : &hc->uuid_node.rb_right;
187b82096afSMikulas Patocka }
188b82096afSMikulas Patocka
189b82096afSMikulas Patocka rb_link_node(&new_hc->uuid_node, parent, n);
190b82096afSMikulas Patocka rb_insert_color(&new_hc->uuid_node, &uuid_rb_tree);
191b82096afSMikulas Patocka }
192b82096afSMikulas Patocka
__get_dev_cell(uint64_t dev)193ba2e19b0SMikulas Patocka static struct hash_cell *__get_dev_cell(uint64_t dev)
194ba2e19b0SMikulas Patocka {
195ba2e19b0SMikulas Patocka struct mapped_device *md;
196ba2e19b0SMikulas Patocka struct hash_cell *hc;
197ba2e19b0SMikulas Patocka
198ba2e19b0SMikulas Patocka md = dm_get_md(huge_decode_dev(dev));
199ba2e19b0SMikulas Patocka if (!md)
200ba2e19b0SMikulas Patocka return NULL;
201ba2e19b0SMikulas Patocka
202ba2e19b0SMikulas Patocka hc = dm_get_mdptr(md);
203ba2e19b0SMikulas Patocka if (!hc) {
204ba2e19b0SMikulas Patocka dm_put(md);
205ba2e19b0SMikulas Patocka return NULL;
206ba2e19b0SMikulas Patocka }
207ba2e19b0SMikulas Patocka
208ba2e19b0SMikulas Patocka return hc;
209ba2e19b0SMikulas Patocka }
210ba2e19b0SMikulas Patocka
211a4a82ce3SHeinz Mauelshagen /*
212a4a82ce3SHeinz Mauelshagen *---------------------------------------------------------------
2131da177e4SLinus Torvalds * Inserting, removing and renaming a device.
214a4a82ce3SHeinz Mauelshagen *---------------------------------------------------------------
215a4a82ce3SHeinz Mauelshagen */
alloc_cell(const char * name,const char * uuid,struct mapped_device * md)2161da177e4SLinus Torvalds static struct hash_cell *alloc_cell(const char *name, const char *uuid,
2171da177e4SLinus Torvalds struct mapped_device *md)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds struct hash_cell *hc;
2201da177e4SLinus Torvalds
2211da177e4SLinus Torvalds hc = kmalloc(sizeof(*hc), GFP_KERNEL);
2221da177e4SLinus Torvalds if (!hc)
2231da177e4SLinus Torvalds return NULL;
2241da177e4SLinus Torvalds
225543537bdSPaulo Marques hc->name = kstrdup(name, GFP_KERNEL);
2261da177e4SLinus Torvalds if (!hc->name) {
2271da177e4SLinus Torvalds kfree(hc);
2281da177e4SLinus Torvalds return NULL;
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds
2311da177e4SLinus Torvalds if (!uuid)
2321da177e4SLinus Torvalds hc->uuid = NULL;
2331da177e4SLinus Torvalds
2341da177e4SLinus Torvalds else {
235543537bdSPaulo Marques hc->uuid = kstrdup(uuid, GFP_KERNEL);
2361da177e4SLinus Torvalds if (!hc->uuid) {
2371da177e4SLinus Torvalds kfree(hc->name);
2381da177e4SLinus Torvalds kfree(hc);
2391da177e4SLinus Torvalds return NULL;
2401da177e4SLinus Torvalds }
2411da177e4SLinus Torvalds }
2421da177e4SLinus Torvalds
243b82096afSMikulas Patocka hc->name_set = hc->uuid_set = false;
2441da177e4SLinus Torvalds hc->md = md;
2451da177e4SLinus Torvalds hc->new_map = NULL;
2461da177e4SLinus Torvalds return hc;
2471da177e4SLinus Torvalds }
2481da177e4SLinus Torvalds
free_cell(struct hash_cell * hc)2491da177e4SLinus Torvalds static void free_cell(struct hash_cell *hc)
2501da177e4SLinus Torvalds {
2511da177e4SLinus Torvalds if (hc) {
2521da177e4SLinus Torvalds kfree(hc->name);
2531da177e4SLinus Torvalds kfree(hc->uuid);
2541da177e4SLinus Torvalds kfree(hc);
2551da177e4SLinus Torvalds }
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds
2581da177e4SLinus Torvalds /*
2591da177e4SLinus Torvalds * The kdev_t and uuid of a device can never change once it is
2601da177e4SLinus Torvalds * initially inserted.
2611da177e4SLinus Torvalds */
dm_hash_insert(const char * name,const char * uuid,struct mapped_device * md)2621da177e4SLinus Torvalds static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
2631da177e4SLinus Torvalds {
2647ec75f25SJeff Mahoney struct hash_cell *cell, *hc;
2651da177e4SLinus Torvalds
2661da177e4SLinus Torvalds /*
2671da177e4SLinus Torvalds * Allocate the new cells.
2681da177e4SLinus Torvalds */
2691da177e4SLinus Torvalds cell = alloc_cell(name, uuid, md);
2701da177e4SLinus Torvalds if (!cell)
2711da177e4SLinus Torvalds return -ENOMEM;
2721da177e4SLinus Torvalds
2731da177e4SLinus Torvalds /*
2741da177e4SLinus Torvalds * Insert the cell into both hash tables.
2751da177e4SLinus Torvalds */
2761da177e4SLinus Torvalds down_write(&_hash_lock);
2777ec75f25SJeff Mahoney hc = __get_name_cell(name);
2787ec75f25SJeff Mahoney if (hc) {
2797ec75f25SJeff Mahoney dm_put(hc->md);
2801da177e4SLinus Torvalds goto bad;
2817ec75f25SJeff Mahoney }
2821da177e4SLinus Torvalds
283b82096afSMikulas Patocka __link_name(cell);
2841da177e4SLinus Torvalds
2851da177e4SLinus Torvalds if (uuid) {
2867ec75f25SJeff Mahoney hc = __get_uuid_cell(uuid);
2877ec75f25SJeff Mahoney if (hc) {
288b82096afSMikulas Patocka __unlink_name(cell);
2897ec75f25SJeff Mahoney dm_put(hc->md);
2901da177e4SLinus Torvalds goto bad;
2911da177e4SLinus Torvalds }
292b82096afSMikulas Patocka __link_uuid(cell);
2931da177e4SLinus Torvalds }
2941da177e4SLinus Torvalds dm_get(md);
2956076905bSMikulas Patocka mutex_lock(&dm_hash_cells_mutex);
2961da177e4SLinus Torvalds dm_set_mdptr(md, cell);
2976076905bSMikulas Patocka mutex_unlock(&dm_hash_cells_mutex);
2981da177e4SLinus Torvalds up_write(&_hash_lock);
2991da177e4SLinus Torvalds
3001da177e4SLinus Torvalds return 0;
3011da177e4SLinus Torvalds
3021da177e4SLinus Torvalds bad:
3031da177e4SLinus Torvalds up_write(&_hash_lock);
3041da177e4SLinus Torvalds free_cell(cell);
3051da177e4SLinus Torvalds return -EBUSY;
3061da177e4SLinus Torvalds }
3071da177e4SLinus Torvalds
__hash_remove(struct hash_cell * hc)30883d5e5b0SMikulas Patocka static struct dm_table *__hash_remove(struct hash_cell *hc)
3091da177e4SLinus Torvalds {
310269fd2a6Sgoggin, edward struct dm_table *table;
31183d5e5b0SMikulas Patocka int srcu_idx;
312269fd2a6Sgoggin, edward
31369868bebSMike Snitzer lockdep_assert_held(&_hash_lock);
31469868bebSMike Snitzer
315b82096afSMikulas Patocka /* remove from the dev trees */
316b82096afSMikulas Patocka __unlink_name(hc);
317b82096afSMikulas Patocka __unlink_uuid(hc);
3186076905bSMikulas Patocka mutex_lock(&dm_hash_cells_mutex);
3191da177e4SLinus Torvalds dm_set_mdptr(hc->md, NULL);
3206076905bSMikulas Patocka mutex_unlock(&dm_hash_cells_mutex);
321269fd2a6Sgoggin, edward
32283d5e5b0SMikulas Patocka table = dm_get_live_table(hc->md, &srcu_idx);
32383d5e5b0SMikulas Patocka if (table)
324269fd2a6Sgoggin, edward dm_table_event(table);
32583d5e5b0SMikulas Patocka dm_put_live_table(hc->md, srcu_idx);
326269fd2a6Sgoggin, edward
32783d5e5b0SMikulas Patocka table = NULL;
3281da177e4SLinus Torvalds if (hc->new_map)
32983d5e5b0SMikulas Patocka table = hc->new_map;
3301134e5aeSMike Anderson dm_put(hc->md);
3311da177e4SLinus Torvalds free_cell(hc);
33283d5e5b0SMikulas Patocka
33383d5e5b0SMikulas Patocka return table;
3341da177e4SLinus Torvalds }
3351da177e4SLinus Torvalds
dm_hash_remove_all(bool keep_open_devices,bool mark_deferred,bool only_deferred)3362c140a24SMikulas Patocka static void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred)
3371da177e4SLinus Torvalds {
338b82096afSMikulas Patocka int dev_skipped;
339b82096afSMikulas Patocka struct rb_node *n;
3401da177e4SLinus Torvalds struct hash_cell *hc;
34198f33285SKiyoshi Ueda struct mapped_device *md;
34283d5e5b0SMikulas Patocka struct dm_table *t;
34398f33285SKiyoshi Ueda
34498f33285SKiyoshi Ueda retry:
34598f33285SKiyoshi Ueda dev_skipped = 0;
3461da177e4SLinus Torvalds
3471da177e4SLinus Torvalds down_write(&_hash_lock);
3485c6bd75dSAlasdair G Kergon
349b82096afSMikulas Patocka for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) {
350b82096afSMikulas Patocka hc = container_of(n, struct hash_cell, name_node);
35198f33285SKiyoshi Ueda md = hc->md;
35298f33285SKiyoshi Ueda dm_get(md);
3535c6bd75dSAlasdair G Kergon
3542c140a24SMikulas Patocka if (keep_open_devices &&
3552c140a24SMikulas Patocka dm_lock_for_deletion(md, mark_deferred, only_deferred)) {
35698f33285SKiyoshi Ueda dm_put(md);
3575c6bd75dSAlasdair G Kergon dev_skipped++;
3585c6bd75dSAlasdair G Kergon continue;
3595c6bd75dSAlasdair G Kergon }
36098f33285SKiyoshi Ueda
36183d5e5b0SMikulas Patocka t = __hash_remove(hc);
36298f33285SKiyoshi Ueda
36398f33285SKiyoshi Ueda up_write(&_hash_lock);
36498f33285SKiyoshi Ueda
36583d5e5b0SMikulas Patocka if (t) {
36683d5e5b0SMikulas Patocka dm_sync_table(md);
36783d5e5b0SMikulas Patocka dm_table_destroy(t);
36883d5e5b0SMikulas Patocka }
36984010e51STushar Sugandhi dm_ima_measure_on_device_remove(md, true);
37098f33285SKiyoshi Ueda dm_put(md);
3713f77316dSKiyoshi Ueda if (likely(keep_open_devices))
3723f77316dSKiyoshi Ueda dm_destroy(md);
3733f77316dSKiyoshi Ueda else
3743f77316dSKiyoshi Ueda dm_destroy_immediate(md);
3755c6bd75dSAlasdair G Kergon
3765c6bd75dSAlasdair G Kergon /*
37798f33285SKiyoshi Ueda * Some mapped devices may be using other mapped
37898f33285SKiyoshi Ueda * devices, so repeat until we make no further
37998f33285SKiyoshi Ueda * progress. If a new mapped device is created
38098f33285SKiyoshi Ueda * here it will also get removed.
3815c6bd75dSAlasdair G Kergon */
3825c6bd75dSAlasdair G Kergon goto retry;
38398f33285SKiyoshi Ueda }
3845c6bd75dSAlasdair G Kergon
3851da177e4SLinus Torvalds up_write(&_hash_lock);
38698f33285SKiyoshi Ueda
38798f33285SKiyoshi Ueda if (dev_skipped)
38898f33285SKiyoshi Ueda DMWARN("remove_all left %d open device(s)", dev_skipped);
3891da177e4SLinus Torvalds }
3901da177e4SLinus Torvalds
39184c89557SPeter Jones /*
39284c89557SPeter Jones * Set the uuid of a hash_cell that isn't already set.
39384c89557SPeter Jones */
__set_cell_uuid(struct hash_cell * hc,char * new_uuid)39484c89557SPeter Jones static void __set_cell_uuid(struct hash_cell *hc, char *new_uuid)
39584c89557SPeter Jones {
39684c89557SPeter Jones mutex_lock(&dm_hash_cells_mutex);
39784c89557SPeter Jones hc->uuid = new_uuid;
39884c89557SPeter Jones mutex_unlock(&dm_hash_cells_mutex);
39984c89557SPeter Jones
400b82096afSMikulas Patocka __link_uuid(hc);
40184c89557SPeter Jones }
40284c89557SPeter Jones
40384c89557SPeter Jones /*
40484c89557SPeter Jones * Changes the name of a hash_cell and returns the old name for
40584c89557SPeter Jones * the caller to free.
40684c89557SPeter Jones */
__change_cell_name(struct hash_cell * hc,char * new_name)40784c89557SPeter Jones static char *__change_cell_name(struct hash_cell *hc, char *new_name)
40884c89557SPeter Jones {
40984c89557SPeter Jones char *old_name;
41084c89557SPeter Jones
41184c89557SPeter Jones /*
41284c89557SPeter Jones * Rename and move the name cell.
41384c89557SPeter Jones */
414b82096afSMikulas Patocka __unlink_name(hc);
41584c89557SPeter Jones old_name = hc->name;
41684c89557SPeter Jones
41784c89557SPeter Jones mutex_lock(&dm_hash_cells_mutex);
41884c89557SPeter Jones hc->name = new_name;
41984c89557SPeter Jones mutex_unlock(&dm_hash_cells_mutex);
42084c89557SPeter Jones
421b82096afSMikulas Patocka __link_name(hc);
42284c89557SPeter Jones
42384c89557SPeter Jones return old_name;
42484c89557SPeter Jones }
42584c89557SPeter Jones
dm_hash_rename(struct dm_ioctl * param,const char * new)426856a6f1dSPeter Rajnoha static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
4273abf85b5SPeter Rajnoha const char *new)
4281da177e4SLinus Torvalds {
42984c89557SPeter Jones char *new_data, *old_name = NULL;
4301da177e4SLinus Torvalds struct hash_cell *hc;
43181f1777aSgoggin, edward struct dm_table *table;
432856a6f1dSPeter Rajnoha struct mapped_device *md;
43386a3238cSHeinz Mauelshagen unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
43483d5e5b0SMikulas Patocka int srcu_idx;
4351da177e4SLinus Torvalds
4361da177e4SLinus Torvalds /*
4371da177e4SLinus Torvalds * duplicate new.
4381da177e4SLinus Torvalds */
43984c89557SPeter Jones new_data = kstrdup(new, GFP_KERNEL);
44084c89557SPeter Jones if (!new_data)
441856a6f1dSPeter Rajnoha return ERR_PTR(-ENOMEM);
4421da177e4SLinus Torvalds
4431da177e4SLinus Torvalds down_write(&_hash_lock);
4441da177e4SLinus Torvalds
4451da177e4SLinus Torvalds /*
4461da177e4SLinus Torvalds * Is new free ?
4471da177e4SLinus Torvalds */
44884c89557SPeter Jones if (change_uuid)
44984c89557SPeter Jones hc = __get_uuid_cell(new);
45084c89557SPeter Jones else
4511da177e4SLinus Torvalds hc = __get_name_cell(new);
45284c89557SPeter Jones
4531da177e4SLinus Torvalds if (hc) {
4542e84fecfSHeinz Mauelshagen DMERR("Unable to change %s on mapped device %s to one that already exists: %s",
45584c89557SPeter Jones change_uuid ? "uuid" : "name",
456856a6f1dSPeter Rajnoha param->name, new);
4577ec75f25SJeff Mahoney dm_put(hc->md);
4581da177e4SLinus Torvalds up_write(&_hash_lock);
45984c89557SPeter Jones kfree(new_data);
460856a6f1dSPeter Rajnoha return ERR_PTR(-EBUSY);
4611da177e4SLinus Torvalds }
4621da177e4SLinus Torvalds
4631da177e4SLinus Torvalds /*
4641da177e4SLinus Torvalds * Is there such a device as 'old' ?
4651da177e4SLinus Torvalds */
466856a6f1dSPeter Rajnoha hc = __get_name_cell(param->name);
4671da177e4SLinus Torvalds if (!hc) {
46843e6c111SMikulas Patocka DMERR("Unable to rename non-existent device, %s to %s%s",
46984c89557SPeter Jones param->name, change_uuid ? "uuid " : "", new);
4701da177e4SLinus Torvalds up_write(&_hash_lock);
47184c89557SPeter Jones kfree(new_data);
472856a6f1dSPeter Rajnoha return ERR_PTR(-ENXIO);
4731da177e4SLinus Torvalds }
4741da177e4SLinus Torvalds
4751da177e4SLinus Torvalds /*
47684c89557SPeter Jones * Does this device already have a uuid?
4771da177e4SLinus Torvalds */
47884c89557SPeter Jones if (change_uuid && hc->uuid) {
47943e6c111SMikulas Patocka DMERR("Unable to change uuid of mapped device %s to %s "
48084c89557SPeter Jones "because uuid is already set to %s",
48184c89557SPeter Jones param->name, new, hc->uuid);
48284c89557SPeter Jones dm_put(hc->md);
48384c89557SPeter Jones up_write(&_hash_lock);
48484c89557SPeter Jones kfree(new_data);
48584c89557SPeter Jones return ERR_PTR(-EINVAL);
48684c89557SPeter Jones }
48784c89557SPeter Jones
48884c89557SPeter Jones if (change_uuid)
48984c89557SPeter Jones __set_cell_uuid(hc, new_data);
49084c89557SPeter Jones else
49184c89557SPeter Jones old_name = __change_cell_name(hc, new_data);
4921da177e4SLinus Torvalds
49381f1777aSgoggin, edward /*
49481f1777aSgoggin, edward * Wake up any dm event waiters.
49581f1777aSgoggin, edward */
49683d5e5b0SMikulas Patocka table = dm_get_live_table(hc->md, &srcu_idx);
49783d5e5b0SMikulas Patocka if (table)
49881f1777aSgoggin, edward dm_table_event(table);
49983d5e5b0SMikulas Patocka dm_put_live_table(hc->md, srcu_idx);
50081f1777aSgoggin, edward
5017533afa1SMikulas Patocka if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr, false))
502856a6f1dSPeter Rajnoha param->flags |= DM_UEVENT_GENERATED_FLAG;
50369267a30SAlasdair G Kergon
504856a6f1dSPeter Rajnoha md = hc->md;
5057d1d1df8STushar Sugandhi
5067d1d1df8STushar Sugandhi dm_ima_measure_on_device_rename(md);
5077d1d1df8STushar Sugandhi
5081da177e4SLinus Torvalds up_write(&_hash_lock);
5091da177e4SLinus Torvalds kfree(old_name);
510856a6f1dSPeter Rajnoha
511856a6f1dSPeter Rajnoha return md;
5121da177e4SLinus Torvalds }
5131da177e4SLinus Torvalds
dm_deferred_remove(void)5142c140a24SMikulas Patocka void dm_deferred_remove(void)
5152c140a24SMikulas Patocka {
5162c140a24SMikulas Patocka dm_hash_remove_all(true, false, true);
5172c140a24SMikulas Patocka }
5182c140a24SMikulas Patocka
519a4a82ce3SHeinz Mauelshagen /*
520a4a82ce3SHeinz Mauelshagen *---------------------------------------------------------------
5211da177e4SLinus Torvalds * Implementation of the ioctl commands
522a4a82ce3SHeinz Mauelshagen *---------------------------------------------------------------
523a4a82ce3SHeinz Mauelshagen */
5241da177e4SLinus Torvalds /*
5251da177e4SLinus Torvalds * All the ioctl commands get dispatched to functions with this
5261da177e4SLinus Torvalds * prototype.
5271da177e4SLinus Torvalds */
528fc1841e1SMikulas Patocka typedef int (*ioctl_fn)(struct file *filp, struct dm_ioctl *param, size_t param_size);
5291da177e4SLinus Torvalds
remove_all(struct file * filp,struct dm_ioctl * param,size_t param_size)530fc1841e1SMikulas Patocka static int remove_all(struct file *filp, struct dm_ioctl *param, size_t param_size)
5311da177e4SLinus Torvalds {
5322c140a24SMikulas Patocka dm_hash_remove_all(true, !!(param->flags & DM_DEFERRED_REMOVE), false);
5331da177e4SLinus Torvalds param->data_size = 0;
5341da177e4SLinus Torvalds return 0;
5351da177e4SLinus Torvalds }
5361da177e4SLinus Torvalds
5371da177e4SLinus Torvalds /*
5381da177e4SLinus Torvalds * Round up the ptr to an 8-byte boundary.
5391da177e4SLinus Torvalds */
5401da177e4SLinus Torvalds #define ALIGN_MASK 7
align_val(size_t val)54162e08243SMikulas Patocka static inline size_t align_val(size_t val)
54262e08243SMikulas Patocka {
54362e08243SMikulas Patocka return (val + ALIGN_MASK) & ~ALIGN_MASK;
54462e08243SMikulas Patocka }
align_ptr(void * ptr)5451da177e4SLinus Torvalds static inline void *align_ptr(void *ptr)
5461da177e4SLinus Torvalds {
54762e08243SMikulas Patocka return (void *)align_val((size_t)ptr);
5481da177e4SLinus Torvalds }
5491da177e4SLinus Torvalds
5501da177e4SLinus Torvalds /*
5511da177e4SLinus Torvalds * Retrieves the data payload buffer from an already allocated
5521da177e4SLinus Torvalds * struct dm_ioctl.
5531da177e4SLinus Torvalds */
get_result_buffer(struct dm_ioctl * param,size_t param_size,size_t * len)5541da177e4SLinus Torvalds static void *get_result_buffer(struct dm_ioctl *param, size_t param_size,
5551da177e4SLinus Torvalds size_t *len)
5561da177e4SLinus Torvalds {
5571da177e4SLinus Torvalds param->data_start = align_ptr(param + 1) - (void *) param;
5581da177e4SLinus Torvalds
5591da177e4SLinus Torvalds if (param->data_start < param_size)
5601da177e4SLinus Torvalds *len = param_size - param->data_start;
5611da177e4SLinus Torvalds else
5621da177e4SLinus Torvalds *len = 0;
5631da177e4SLinus Torvalds
5641da177e4SLinus Torvalds return ((void *) param) + param->data_start;
5651da177e4SLinus Torvalds }
5661da177e4SLinus Torvalds
filter_device(struct hash_cell * hc,const char * pfx_name,const char * pfx_uuid)567c909085bSMikulas Patocka static bool filter_device(struct hash_cell *hc, const char *pfx_name, const char *pfx_uuid)
568c909085bSMikulas Patocka {
569c909085bSMikulas Patocka const char *val;
570c909085bSMikulas Patocka size_t val_len, pfx_len;
571c909085bSMikulas Patocka
572c909085bSMikulas Patocka val = hc->name;
573c909085bSMikulas Patocka val_len = strlen(val);
574c909085bSMikulas Patocka pfx_len = strnlen(pfx_name, DM_NAME_LEN);
575c909085bSMikulas Patocka if (pfx_len > val_len)
576c909085bSMikulas Patocka return false;
577c909085bSMikulas Patocka if (memcmp(val, pfx_name, pfx_len))
578c909085bSMikulas Patocka return false;
579c909085bSMikulas Patocka
580c909085bSMikulas Patocka val = hc->uuid ? hc->uuid : "";
581c909085bSMikulas Patocka val_len = strlen(val);
582c909085bSMikulas Patocka pfx_len = strnlen(pfx_uuid, DM_UUID_LEN);
583c909085bSMikulas Patocka if (pfx_len > val_len)
584c909085bSMikulas Patocka return false;
585c909085bSMikulas Patocka if (memcmp(val, pfx_uuid, pfx_len))
586c909085bSMikulas Patocka return false;
587c909085bSMikulas Patocka
588c909085bSMikulas Patocka return true;
589c909085bSMikulas Patocka }
590c909085bSMikulas Patocka
list_devices(struct file * filp,struct dm_ioctl * param,size_t param_size)591fc1841e1SMikulas Patocka static int list_devices(struct file *filp, struct dm_ioctl *param, size_t param_size)
5921da177e4SLinus Torvalds {
593b82096afSMikulas Patocka struct rb_node *n;
5941da177e4SLinus Torvalds struct hash_cell *hc;
5951da177e4SLinus Torvalds size_t len, needed = 0;
5961da177e4SLinus Torvalds struct gendisk *disk;
59762e08243SMikulas Patocka struct dm_name_list *orig_nl, *nl, *old_nl = NULL;
59823d70c5eSMikulas Patocka uint32_t *event_nr;
5991da177e4SLinus Torvalds
6001da177e4SLinus Torvalds down_write(&_hash_lock);
6011da177e4SLinus Torvalds
6021da177e4SLinus Torvalds /*
6031da177e4SLinus Torvalds * Loop through all the devices working out how much
6041da177e4SLinus Torvalds * space we need.
6051da177e4SLinus Torvalds */
606b82096afSMikulas Patocka for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) {
607b82096afSMikulas Patocka hc = container_of(n, struct hash_cell, name_node);
608c909085bSMikulas Patocka if (!filter_device(hc, param->name, param->uuid))
609c909085bSMikulas Patocka continue;
61062e08243SMikulas Patocka needed += align_val(offsetof(struct dm_name_list, name) + strlen(hc->name) + 1);
6118b638081SMikulas Patocka needed += align_val(sizeof(uint32_t) * 2);
6128b638081SMikulas Patocka if (param->flags & DM_UUID_FLAG && hc->uuid)
6138b638081SMikulas Patocka needed += align_val(strlen(hc->uuid) + 1);
6141da177e4SLinus Torvalds }
6151da177e4SLinus Torvalds
6161da177e4SLinus Torvalds /*
6171da177e4SLinus Torvalds * Grab our output buffer.
6181da177e4SLinus Torvalds */
61962e08243SMikulas Patocka nl = orig_nl = get_result_buffer(param, param_size, &len);
6204edbe1d7SMikulas Patocka if (len < needed || len < sizeof(nl->dev)) {
6211da177e4SLinus Torvalds param->flags |= DM_BUFFER_FULL_FLAG;
6221da177e4SLinus Torvalds goto out;
6231da177e4SLinus Torvalds }
6241da177e4SLinus Torvalds param->data_size = param->data_start + needed;
6251da177e4SLinus Torvalds
6261da177e4SLinus Torvalds nl->dev = 0; /* Flags no data */
6271da177e4SLinus Torvalds
6281da177e4SLinus Torvalds /*
6291da177e4SLinus Torvalds * Now loop through filling out the names.
6301da177e4SLinus Torvalds */
631b82096afSMikulas Patocka for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) {
6328b638081SMikulas Patocka void *uuid_ptr;
6330ef0b471SHeinz Mauelshagen
634b82096afSMikulas Patocka hc = container_of(n, struct hash_cell, name_node);
635c909085bSMikulas Patocka if (!filter_device(hc, param->name, param->uuid))
636c909085bSMikulas Patocka continue;
6371da177e4SLinus Torvalds if (old_nl)
6381da177e4SLinus Torvalds old_nl->next = (uint32_t) ((void *) nl -
6391da177e4SLinus Torvalds (void *) old_nl);
6401da177e4SLinus Torvalds disk = dm_disk(hc->md);
641f331c029STejun Heo nl->dev = huge_encode_dev(disk_devt(disk));
6421da177e4SLinus Torvalds nl->next = 0;
6431da177e4SLinus Torvalds strcpy(nl->name, hc->name);
6441da177e4SLinus Torvalds
6451da177e4SLinus Torvalds old_nl = nl;
64662e08243SMikulas Patocka event_nr = align_ptr(nl->name + strlen(hc->name) + 1);
6478b638081SMikulas Patocka event_nr[0] = dm_get_event_nr(hc->md);
6488b638081SMikulas Patocka event_nr[1] = 0;
6498b638081SMikulas Patocka uuid_ptr = align_ptr(event_nr + 2);
6508b638081SMikulas Patocka if (param->flags & DM_UUID_FLAG) {
6518b638081SMikulas Patocka if (hc->uuid) {
6528b638081SMikulas Patocka event_nr[1] |= DM_NAME_LIST_FLAG_HAS_UUID;
6538b638081SMikulas Patocka strcpy(uuid_ptr, hc->uuid);
6548b638081SMikulas Patocka uuid_ptr = align_ptr(uuid_ptr + strlen(hc->uuid) + 1);
6558b638081SMikulas Patocka } else {
6568b638081SMikulas Patocka event_nr[1] |= DM_NAME_LIST_FLAG_DOESNT_HAVE_UUID;
6578b638081SMikulas Patocka }
6588b638081SMikulas Patocka }
6598b638081SMikulas Patocka nl = uuid_ptr;
6601da177e4SLinus Torvalds }
66162e08243SMikulas Patocka /*
66262e08243SMikulas Patocka * If mismatch happens, security may be compromised due to buffer
66362e08243SMikulas Patocka * overflow, so it's better to crash.
66462e08243SMikulas Patocka */
66562e08243SMikulas Patocka BUG_ON((char *)nl - (char *)orig_nl != needed);
6661da177e4SLinus Torvalds
6671da177e4SLinus Torvalds out:
6681da177e4SLinus Torvalds up_write(&_hash_lock);
6691da177e4SLinus Torvalds return 0;
6701da177e4SLinus Torvalds }
6711da177e4SLinus Torvalds
list_version_get_needed(struct target_type * tt,void * needed_param)6721da177e4SLinus Torvalds static void list_version_get_needed(struct target_type *tt, void *needed_param)
6731da177e4SLinus Torvalds {
6741da177e4SLinus Torvalds size_t *needed = needed_param;
6751da177e4SLinus Torvalds
676c4cc6635SAlasdair G Kergon *needed += sizeof(struct dm_target_versions);
6774fe1ec99SMikulas Patocka *needed += strlen(tt->name) + 1;
6781da177e4SLinus Torvalds *needed += ALIGN_MASK;
6791da177e4SLinus Torvalds }
6801da177e4SLinus Torvalds
list_version_get_info(struct target_type * tt,void * param)6811da177e4SLinus Torvalds static void list_version_get_info(struct target_type *tt, void *param)
6821da177e4SLinus Torvalds {
6831da177e4SLinus Torvalds struct vers_iter *info = param;
6841da177e4SLinus Torvalds
6851da177e4SLinus Torvalds /* Check space - it might have changed since the first iteration */
6868ca817c4SHeinz Mauelshagen if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 > info->end) {
6871da177e4SLinus Torvalds info->flags = DM_BUFFER_FULL_FLAG;
6881da177e4SLinus Torvalds return;
6891da177e4SLinus Torvalds }
6901da177e4SLinus Torvalds
6911da177e4SLinus Torvalds if (info->old_vers)
6928ca817c4SHeinz Mauelshagen info->old_vers->next = (uint32_t) ((void *)info->vers - (void *)info->old_vers);
6938ca817c4SHeinz Mauelshagen
6941da177e4SLinus Torvalds info->vers->version[0] = tt->version[0];
6951da177e4SLinus Torvalds info->vers->version[1] = tt->version[1];
6961da177e4SLinus Torvalds info->vers->version[2] = tt->version[2];
6971da177e4SLinus Torvalds info->vers->next = 0;
6981da177e4SLinus Torvalds strcpy(info->vers->name, tt->name);
6991da177e4SLinus Torvalds
7001da177e4SLinus Torvalds info->old_vers = info->vers;
701d043f9a1SMikulas Patocka info->vers = align_ptr((void *)(info->vers + 1) + strlen(tt->name) + 1);
7021da177e4SLinus Torvalds }
7031da177e4SLinus Torvalds
__list_versions(struct dm_ioctl * param,size_t param_size,const char * name)704afa179ebSMikulas Patocka static int __list_versions(struct dm_ioctl *param, size_t param_size, const char *name)
7051da177e4SLinus Torvalds {
7061da177e4SLinus Torvalds size_t len, needed = 0;
7071da177e4SLinus Torvalds struct dm_target_versions *vers;
7081da177e4SLinus Torvalds struct vers_iter iter_info;
709afa179ebSMikulas Patocka struct target_type *tt = NULL;
710afa179ebSMikulas Patocka
711afa179ebSMikulas Patocka if (name) {
712afa179ebSMikulas Patocka tt = dm_get_target_type(name);
713afa179ebSMikulas Patocka if (!tt)
714afa179ebSMikulas Patocka return -EINVAL;
715afa179ebSMikulas Patocka }
7161da177e4SLinus Torvalds
7171da177e4SLinus Torvalds /*
7181da177e4SLinus Torvalds * Loop through all the devices working out how much
7191da177e4SLinus Torvalds * space we need.
7201da177e4SLinus Torvalds */
721afa179ebSMikulas Patocka if (!tt)
7221da177e4SLinus Torvalds dm_target_iterate(list_version_get_needed, &needed);
723afa179ebSMikulas Patocka else
724afa179ebSMikulas Patocka list_version_get_needed(tt, &needed);
7251da177e4SLinus Torvalds
7261da177e4SLinus Torvalds /*
7271da177e4SLinus Torvalds * Grab our output buffer.
7281da177e4SLinus Torvalds */
7291da177e4SLinus Torvalds vers = get_result_buffer(param, param_size, &len);
7301da177e4SLinus Torvalds if (len < needed) {
7311da177e4SLinus Torvalds param->flags |= DM_BUFFER_FULL_FLAG;
7321da177e4SLinus Torvalds goto out;
7331da177e4SLinus Torvalds }
7341da177e4SLinus Torvalds param->data_size = param->data_start + needed;
7351da177e4SLinus Torvalds
7361da177e4SLinus Torvalds iter_info.param_size = param_size;
7371da177e4SLinus Torvalds iter_info.old_vers = NULL;
7381da177e4SLinus Torvalds iter_info.vers = vers;
7391da177e4SLinus Torvalds iter_info.flags = 0;
7404fe1ec99SMikulas Patocka iter_info.end = (char *)vers + needed;
7411da177e4SLinus Torvalds
7421da177e4SLinus Torvalds /*
7431da177e4SLinus Torvalds * Now loop through filling out the names & versions.
7441da177e4SLinus Torvalds */
745afa179ebSMikulas Patocka if (!tt)
7461da177e4SLinus Torvalds dm_target_iterate(list_version_get_info, &iter_info);
747afa179ebSMikulas Patocka else
748afa179ebSMikulas Patocka list_version_get_info(tt, &iter_info);
7491da177e4SLinus Torvalds param->flags |= iter_info.flags;
7501da177e4SLinus Torvalds
7511da177e4SLinus Torvalds out:
752afa179ebSMikulas Patocka if (tt)
753afa179ebSMikulas Patocka dm_put_target_type(tt);
7541da177e4SLinus Torvalds return 0;
7551da177e4SLinus Torvalds }
7561da177e4SLinus Torvalds
list_versions(struct file * filp,struct dm_ioctl * param,size_t param_size)757afa179ebSMikulas Patocka static int list_versions(struct file *filp, struct dm_ioctl *param, size_t param_size)
758afa179ebSMikulas Patocka {
759afa179ebSMikulas Patocka return __list_versions(param, param_size, NULL);
760afa179ebSMikulas Patocka }
761afa179ebSMikulas Patocka
get_target_version(struct file * filp,struct dm_ioctl * param,size_t param_size)762afa179ebSMikulas Patocka static int get_target_version(struct file *filp, struct dm_ioctl *param, size_t param_size)
763afa179ebSMikulas Patocka {
764afa179ebSMikulas Patocka return __list_versions(param, param_size, param->name);
765afa179ebSMikulas Patocka }
766afa179ebSMikulas Patocka
check_name(const char * name)7671da177e4SLinus Torvalds static int check_name(const char *name)
7681da177e4SLinus Torvalds {
7691da177e4SLinus Torvalds if (strchr(name, '/')) {
770a85f1a9dSDemi Marie Obenour DMERR("device name cannot contain '/'");
771a85f1a9dSDemi Marie Obenour return -EINVAL;
772a85f1a9dSDemi Marie Obenour }
773a85f1a9dSDemi Marie Obenour
77481ca2dbeSDemi Marie Obenour if (strcmp(name, DM_CONTROL_NODE) == 0 ||
77581ca2dbeSDemi Marie Obenour strcmp(name, ".") == 0 ||
77681ca2dbeSDemi Marie Obenour strcmp(name, "..") == 0) {
77781ca2dbeSDemi Marie Obenour DMERR("device name cannot be \"%s\", \".\", or \"..\"", DM_CONTROL_NODE);
7781da177e4SLinus Torvalds return -EINVAL;
7791da177e4SLinus Torvalds }
7801da177e4SLinus Torvalds
7811da177e4SLinus Torvalds return 0;
7821da177e4SLinus Torvalds }
7831da177e4SLinus Torvalds
7841da177e4SLinus Torvalds /*
7851d0f3ce8SMike Snitzer * On successful return, the caller must not attempt to acquire
78688e2f901SJunxiao Bi * _hash_lock without first calling dm_put_live_table, because dm_table_destroy
78788e2f901SJunxiao Bi * waits for this dm_put_live_table and could be called under this lock.
7881d0f3ce8SMike Snitzer */
dm_get_inactive_table(struct mapped_device * md,int * srcu_idx)78983d5e5b0SMikulas Patocka static struct dm_table *dm_get_inactive_table(struct mapped_device *md, int *srcu_idx)
7901d0f3ce8SMike Snitzer {
7911d0f3ce8SMike Snitzer struct hash_cell *hc;
7921d0f3ce8SMike Snitzer struct dm_table *table = NULL;
7931d0f3ce8SMike Snitzer
79483d5e5b0SMikulas Patocka /* increment rcu count, we don't care about the table pointer */
79583d5e5b0SMikulas Patocka dm_get_live_table(md, srcu_idx);
79683d5e5b0SMikulas Patocka
7971d0f3ce8SMike Snitzer down_read(&_hash_lock);
7981d0f3ce8SMike Snitzer hc = dm_get_mdptr(md);
799a2f998a7SHou Tao if (!hc) {
80043e6c111SMikulas Patocka DMERR("device has been removed from the dev hash table.");
8011d0f3ce8SMike Snitzer goto out;
8021d0f3ce8SMike Snitzer }
8031d0f3ce8SMike Snitzer
8041d0f3ce8SMike Snitzer table = hc->new_map;
8051d0f3ce8SMike Snitzer
8061d0f3ce8SMike Snitzer out:
8071d0f3ce8SMike Snitzer up_read(&_hash_lock);
8081d0f3ce8SMike Snitzer
8091d0f3ce8SMike Snitzer return table;
8101d0f3ce8SMike Snitzer }
8111d0f3ce8SMike Snitzer
dm_get_live_or_inactive_table(struct mapped_device * md,struct dm_ioctl * param,int * srcu_idx)8121d0f3ce8SMike Snitzer static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md,
81383d5e5b0SMikulas Patocka struct dm_ioctl *param,
81483d5e5b0SMikulas Patocka int *srcu_idx)
8151d0f3ce8SMike Snitzer {
8161d0f3ce8SMike Snitzer return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ?
81783d5e5b0SMikulas Patocka dm_get_inactive_table(md, srcu_idx) : dm_get_live_table(md, srcu_idx);
8181d0f3ce8SMike Snitzer }
8191d0f3ce8SMike Snitzer
8201d0f3ce8SMike Snitzer /*
8211da177e4SLinus Torvalds * Fills in a dm_ioctl structure, ready for sending back to
8221da177e4SLinus Torvalds * userland.
8231da177e4SLinus Torvalds */
__dev_status(struct mapped_device * md,struct dm_ioctl * param)824094ea9a0SAlasdair G Kergon static void __dev_status(struct mapped_device *md, struct dm_ioctl *param)
8251da177e4SLinus Torvalds {
8261da177e4SLinus Torvalds struct gendisk *disk = dm_disk(md);
8271da177e4SLinus Torvalds struct dm_table *table;
82883d5e5b0SMikulas Patocka int srcu_idx;
8291da177e4SLinus Torvalds
8301da177e4SLinus Torvalds param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
831ffcc3936SMike Snitzer DM_ACTIVE_PRESENT_FLAG | DM_INTERNAL_SUSPEND_FLAG);
8321da177e4SLinus Torvalds
8334f186f8bSKiyoshi Ueda if (dm_suspended_md(md))
8341da177e4SLinus Torvalds param->flags |= DM_SUSPEND_FLAG;
8351da177e4SLinus Torvalds
836ffcc3936SMike Snitzer if (dm_suspended_internally_md(md))
837ffcc3936SMike Snitzer param->flags |= DM_INTERNAL_SUSPEND_FLAG;
838ffcc3936SMike Snitzer
8392c140a24SMikulas Patocka if (dm_test_deferred_remove_flag(md))
8402c140a24SMikulas Patocka param->flags |= DM_DEFERRED_REMOVE;
8412c140a24SMikulas Patocka
842f331c029STejun Heo param->dev = huge_encode_dev(disk_devt(disk));
8431da177e4SLinus Torvalds
8441da177e4SLinus Torvalds /*
8451da177e4SLinus Torvalds * Yes, this will be out of date by the time it gets back
8461da177e4SLinus Torvalds * to userland, but it is still very useful for
8471da177e4SLinus Torvalds * debugging.
8481da177e4SLinus Torvalds */
8495c6bd75dSAlasdair G Kergon param->open_count = dm_open_count(md);
8501da177e4SLinus Torvalds
8511da177e4SLinus Torvalds param->event_nr = dm_get_event_nr(md);
8521d0f3ce8SMike Snitzer param->target_count = 0;
8531da177e4SLinus Torvalds
85483d5e5b0SMikulas Patocka table = dm_get_live_table(md, &srcu_idx);
8551da177e4SLinus Torvalds if (table) {
8561d0f3ce8SMike Snitzer if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) {
8571d0f3ce8SMike Snitzer if (get_disk_ro(disk))
8581d0f3ce8SMike Snitzer param->flags |= DM_READONLY_FLAG;
8592aec377aSMike Snitzer param->target_count = table->num_targets;
8601d0f3ce8SMike Snitzer }
8611d0f3ce8SMike Snitzer
8621da177e4SLinus Torvalds param->flags |= DM_ACTIVE_PRESENT_FLAG;
8631d0f3ce8SMike Snitzer }
86483d5e5b0SMikulas Patocka dm_put_live_table(md, srcu_idx);
8651d0f3ce8SMike Snitzer
8661d0f3ce8SMike Snitzer if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) {
86783d5e5b0SMikulas Patocka int srcu_idx;
8680ef0b471SHeinz Mauelshagen
86983d5e5b0SMikulas Patocka table = dm_get_inactive_table(md, &srcu_idx);
8701d0f3ce8SMike Snitzer if (table) {
87105bdb996SChristoph Hellwig if (!(dm_table_get_mode(table) & BLK_OPEN_WRITE))
8721d0f3ce8SMike Snitzer param->flags |= DM_READONLY_FLAG;
8732aec377aSMike Snitzer param->target_count = table->num_targets;
8741d0f3ce8SMike Snitzer }
87583d5e5b0SMikulas Patocka dm_put_live_table(md, srcu_idx);
8761d0f3ce8SMike Snitzer }
8771da177e4SLinus Torvalds }
8781da177e4SLinus Torvalds
dev_create(struct file * filp,struct dm_ioctl * param,size_t param_size)879fc1841e1SMikulas Patocka static int dev_create(struct file *filp, struct dm_ioctl *param, size_t param_size)
8801da177e4SLinus Torvalds {
8812b06cfffSAlasdair G Kergon int r, m = DM_ANY_MINOR;
8821da177e4SLinus Torvalds struct mapped_device *md;
8831da177e4SLinus Torvalds
8841da177e4SLinus Torvalds r = check_name(param->name);
8851da177e4SLinus Torvalds if (r)
8861da177e4SLinus Torvalds return r;
8871da177e4SLinus Torvalds
8881da177e4SLinus Torvalds if (param->flags & DM_PERSISTENT_DEV_FLAG)
8892b06cfffSAlasdair G Kergon m = MINOR(huge_decode_dev(param->dev));
8901da177e4SLinus Torvalds
8912b06cfffSAlasdair G Kergon r = dm_create(m, &md);
8921da177e4SLinus Torvalds if (r)
8931da177e4SLinus Torvalds return r;
8941da177e4SLinus Torvalds
8951da177e4SLinus Torvalds r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
8963f77316dSKiyoshi Ueda if (r) {
8973f77316dSKiyoshi Ueda dm_put(md);
8983f77316dSKiyoshi Ueda dm_destroy(md);
8993f77316dSKiyoshi Ueda return r;
9003f77316dSKiyoshi Ueda }
9011da177e4SLinus Torvalds
9021da177e4SLinus Torvalds param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
9031da177e4SLinus Torvalds
904094ea9a0SAlasdair G Kergon __dev_status(md, param);
905094ea9a0SAlasdair G Kergon
9061da177e4SLinus Torvalds dm_put(md);
9071da177e4SLinus Torvalds
9083f77316dSKiyoshi Ueda return 0;
9091da177e4SLinus Torvalds }
9101da177e4SLinus Torvalds
9111da177e4SLinus Torvalds /*
9121da177e4SLinus Torvalds * Always use UUID for lookups if it's present, otherwise use name or dev.
9131da177e4SLinus Torvalds */
__find_device_hash_cell(struct dm_ioctl * param)914858119e1SArjan van de Ven static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
9151da177e4SLinus Torvalds {
9160ddf9644SMikulas Patocka struct hash_cell *hc = NULL;
9179ade92a9SAlasdair G Kergon
9180ddf9644SMikulas Patocka if (*param->uuid) {
919dbdcc906SMikulas Patocka if (*param->name || param->dev) {
920dbdcc906SMikulas Patocka DMERR("Invalid ioctl structure: uuid %s, name %s, dev %llx",
921dbdcc906SMikulas Patocka param->uuid, param->name, (unsigned long long)param->dev);
922759dea20SMikulas Patocka return NULL;
923dbdcc906SMikulas Patocka }
924759dea20SMikulas Patocka
9250ddf9644SMikulas Patocka hc = __get_uuid_cell(param->uuid);
9260ddf9644SMikulas Patocka if (!hc)
9270ddf9644SMikulas Patocka return NULL;
928ba2e19b0SMikulas Patocka } else if (*param->name) {
929dbdcc906SMikulas Patocka if (param->dev) {
930dbdcc906SMikulas Patocka DMERR("Invalid ioctl structure: name %s, dev %llx",
931dbdcc906SMikulas Patocka param->name, (unsigned long long)param->dev);
932759dea20SMikulas Patocka return NULL;
933dbdcc906SMikulas Patocka }
934759dea20SMikulas Patocka
9350ddf9644SMikulas Patocka hc = __get_name_cell(param->name);
9360ddf9644SMikulas Patocka if (!hc)
9370ddf9644SMikulas Patocka return NULL;
938ba2e19b0SMikulas Patocka } else if (param->dev) {
939ba2e19b0SMikulas Patocka hc = __get_dev_cell(param->dev);
940ba2e19b0SMikulas Patocka if (!hc)
941ba2e19b0SMikulas Patocka return NULL;
942ba2e19b0SMikulas Patocka } else
9430ddf9644SMikulas Patocka return NULL;
9449ade92a9SAlasdair G Kergon
9451da177e4SLinus Torvalds /*
9461da177e4SLinus Torvalds * Sneakily write in both the name and the uuid
9471da177e4SLinus Torvalds * while we have the cell.
9481da177e4SLinus Torvalds */
94922a8b849SHeinz Mauelshagen strscpy(param->name, hc->name, sizeof(param->name));
9501da177e4SLinus Torvalds if (hc->uuid)
95122a8b849SHeinz Mauelshagen strscpy(param->uuid, hc->uuid, sizeof(param->uuid));
9521da177e4SLinus Torvalds else
9531da177e4SLinus Torvalds param->uuid[0] = '\0';
9541da177e4SLinus Torvalds
9551da177e4SLinus Torvalds if (hc->new_map)
9561da177e4SLinus Torvalds param->flags |= DM_INACTIVE_PRESENT_FLAG;
9571da177e4SLinus Torvalds else
9581da177e4SLinus Torvalds param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
9590ddf9644SMikulas Patocka
9600ddf9644SMikulas Patocka return hc;
9611da177e4SLinus Torvalds }
9620ddf9644SMikulas Patocka
find_device(struct dm_ioctl * param)9630ddf9644SMikulas Patocka static struct mapped_device *find_device(struct dm_ioctl *param)
9640ddf9644SMikulas Patocka {
9650ddf9644SMikulas Patocka struct hash_cell *hc;
9660ddf9644SMikulas Patocka struct mapped_device *md = NULL;
9670ddf9644SMikulas Patocka
9680ddf9644SMikulas Patocka down_read(&_hash_lock);
9690ddf9644SMikulas Patocka hc = __find_device_hash_cell(param);
9700ddf9644SMikulas Patocka if (hc)
9710ddf9644SMikulas Patocka md = hc->md;
9721da177e4SLinus Torvalds up_read(&_hash_lock);
9731da177e4SLinus Torvalds
9741da177e4SLinus Torvalds return md;
9751da177e4SLinus Torvalds }
9761da177e4SLinus Torvalds
dev_remove(struct file * filp,struct dm_ioctl * param,size_t param_size)977fc1841e1SMikulas Patocka static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_size)
9781da177e4SLinus Torvalds {
9791da177e4SLinus Torvalds struct hash_cell *hc;
9807ec75f25SJeff Mahoney struct mapped_device *md;
9815c6bd75dSAlasdair G Kergon int r;
98283d5e5b0SMikulas Patocka struct dm_table *t;
9831da177e4SLinus Torvalds
9841da177e4SLinus Torvalds down_write(&_hash_lock);
9851da177e4SLinus Torvalds hc = __find_device_hash_cell(param);
9861da177e4SLinus Torvalds
9871da177e4SLinus Torvalds if (!hc) {
988810b4923SMilan Broz DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
9891da177e4SLinus Torvalds up_write(&_hash_lock);
9901da177e4SLinus Torvalds return -ENXIO;
9911da177e4SLinus Torvalds }
9921da177e4SLinus Torvalds
9937ec75f25SJeff Mahoney md = hc->md;
9947ec75f25SJeff Mahoney
9955c6bd75dSAlasdair G Kergon /*
9965c6bd75dSAlasdair G Kergon * Ensure the device is not open and nothing further can open it.
9975c6bd75dSAlasdair G Kergon */
9982c140a24SMikulas Patocka r = dm_lock_for_deletion(md, !!(param->flags & DM_DEFERRED_REMOVE), false);
9995c6bd75dSAlasdair G Kergon if (r) {
10002c140a24SMikulas Patocka if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) {
10012c140a24SMikulas Patocka up_write(&_hash_lock);
10022c140a24SMikulas Patocka dm_put(md);
10032c140a24SMikulas Patocka return 0;
10042c140a24SMikulas Patocka }
1005810b4923SMilan Broz DMDEBUG_LIMIT("unable to remove open device %s", hc->name);
10065c6bd75dSAlasdair G Kergon up_write(&_hash_lock);
10075c6bd75dSAlasdair G Kergon dm_put(md);
10085c6bd75dSAlasdair G Kergon return r;
10095c6bd75dSAlasdair G Kergon }
10105c6bd75dSAlasdair G Kergon
101183d5e5b0SMikulas Patocka t = __hash_remove(hc);
10121da177e4SLinus Torvalds up_write(&_hash_lock);
101360935eb2SMilan Broz
101483d5e5b0SMikulas Patocka if (t) {
101583d5e5b0SMikulas Patocka dm_sync_table(md);
101683d5e5b0SMikulas Patocka dm_table_destroy(t);
101783d5e5b0SMikulas Patocka }
101883d5e5b0SMikulas Patocka
10192c140a24SMikulas Patocka param->flags &= ~DM_DEFERRED_REMOVE;
10202c140a24SMikulas Patocka
102184010e51STushar Sugandhi dm_ima_measure_on_device_remove(md, false);
102284010e51STushar Sugandhi
10237533afa1SMikulas Patocka if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr, false))
10243abf85b5SPeter Rajnoha param->flags |= DM_UEVENT_GENERATED_FLAG;
102560935eb2SMilan Broz
10267ec75f25SJeff Mahoney dm_put(md);
10273f77316dSKiyoshi Ueda dm_destroy(md);
10281da177e4SLinus Torvalds return 0;
10291da177e4SLinus Torvalds }
10301da177e4SLinus Torvalds
10311da177e4SLinus Torvalds /*
10321da177e4SLinus Torvalds * Check a string doesn't overrun the chunk of
10331da177e4SLinus Torvalds * memory we copied from userland.
10341da177e4SLinus Torvalds */
invalid_str(char * str,void * end)10351da177e4SLinus Torvalds static int invalid_str(char *str, void *end)
10361da177e4SLinus Torvalds {
10371da177e4SLinus Torvalds while ((void *) str < end)
10381da177e4SLinus Torvalds if (!*str++)
10391da177e4SLinus Torvalds return 0;
10401da177e4SLinus Torvalds
10411da177e4SLinus Torvalds return -EINVAL;
10421da177e4SLinus Torvalds }
10431da177e4SLinus Torvalds
dev_rename(struct file * filp,struct dm_ioctl * param,size_t param_size)1044fc1841e1SMikulas Patocka static int dev_rename(struct file *filp, struct dm_ioctl *param, size_t param_size)
10451da177e4SLinus Torvalds {
10461da177e4SLinus Torvalds int r;
104784c89557SPeter Jones char *new_data = (char *) param + param->data_start;
1048856a6f1dSPeter Rajnoha struct mapped_device *md;
104986a3238cSHeinz Mauelshagen unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
10501da177e4SLinus Torvalds
105184c89557SPeter Jones if (new_data < param->data ||
1052c2b04824SAlasdair Kergon invalid_str(new_data, (void *) param + param_size) || !*new_data ||
105384c89557SPeter Jones strlen(new_data) > (change_uuid ? DM_UUID_LEN - 1 : DM_NAME_LEN - 1)) {
105443e6c111SMikulas Patocka DMERR("Invalid new mapped device name or uuid string supplied.");
10551da177e4SLinus Torvalds return -EINVAL;
10561da177e4SLinus Torvalds }
10571da177e4SLinus Torvalds
105884c89557SPeter Jones if (!change_uuid) {
105984c89557SPeter Jones r = check_name(new_data);
10601da177e4SLinus Torvalds if (r)
10611da177e4SLinus Torvalds return r;
106284c89557SPeter Jones }
10631da177e4SLinus Torvalds
106484c89557SPeter Jones md = dm_hash_rename(param, new_data);
1065856a6f1dSPeter Rajnoha if (IS_ERR(md))
1066856a6f1dSPeter Rajnoha return PTR_ERR(md);
10673abf85b5SPeter Rajnoha
1068856a6f1dSPeter Rajnoha __dev_status(md, param);
1069856a6f1dSPeter Rajnoha dm_put(md);
1070856a6f1dSPeter Rajnoha
1071856a6f1dSPeter Rajnoha return 0;
10721da177e4SLinus Torvalds }
10731da177e4SLinus Torvalds
dev_set_geometry(struct file * filp,struct dm_ioctl * param,size_t param_size)1074fc1841e1SMikulas Patocka static int dev_set_geometry(struct file *filp, struct dm_ioctl *param, size_t param_size)
10753ac51e74SDarrick J. Wong {
10763ac51e74SDarrick J. Wong int r = -EINVAL, x;
10773ac51e74SDarrick J. Wong struct mapped_device *md;
10783ac51e74SDarrick J. Wong struct hd_geometry geometry;
10793ac51e74SDarrick J. Wong unsigned long indata[4];
10803ac51e74SDarrick J. Wong char *geostr = (char *) param + param->data_start;
108131998ef1SMikulas Patocka char dummy;
10823ac51e74SDarrick J. Wong
10833ac51e74SDarrick J. Wong md = find_device(param);
10843ac51e74SDarrick J. Wong if (!md)
10853ac51e74SDarrick J. Wong return -ENXIO;
10863ac51e74SDarrick J. Wong
108727238b2bSAlasdair G Kergon if (geostr < param->data ||
10883ac51e74SDarrick J. Wong invalid_str(geostr, (void *) param + param_size)) {
108943e6c111SMikulas Patocka DMERR("Invalid geometry supplied.");
10903ac51e74SDarrick J. Wong goto out;
10913ac51e74SDarrick J. Wong }
10923ac51e74SDarrick J. Wong
109331998ef1SMikulas Patocka x = sscanf(geostr, "%lu %lu %lu %lu%c", indata,
109431998ef1SMikulas Patocka indata + 1, indata + 2, indata + 3, &dummy);
10953ac51e74SDarrick J. Wong
10963ac51e74SDarrick J. Wong if (x != 4) {
109743e6c111SMikulas Patocka DMERR("Unable to interpret geometry settings.");
10983ac51e74SDarrick J. Wong goto out;
10993ac51e74SDarrick J. Wong }
11003ac51e74SDarrick J. Wong
1101151d8122SSergey Shtylyov if (indata[0] > 65535 || indata[1] > 255 || indata[2] > 255) {
110243e6c111SMikulas Patocka DMERR("Geometry exceeds range limits.");
11033ac51e74SDarrick J. Wong goto out;
11043ac51e74SDarrick J. Wong }
11053ac51e74SDarrick J. Wong
11063ac51e74SDarrick J. Wong geometry.cylinders = indata[0];
11073ac51e74SDarrick J. Wong geometry.heads = indata[1];
11083ac51e74SDarrick J. Wong geometry.sectors = indata[2];
11093ac51e74SDarrick J. Wong geometry.start = indata[3];
11103ac51e74SDarrick J. Wong
11113ac51e74SDarrick J. Wong r = dm_set_geometry(md, &geometry);
11123ac51e74SDarrick J. Wong
11133ac51e74SDarrick J. Wong param->data_size = 0;
11143ac51e74SDarrick J. Wong
11153ac51e74SDarrick J. Wong out:
11163ac51e74SDarrick J. Wong dm_put(md);
11173ac51e74SDarrick J. Wong return r;
11183ac51e74SDarrick J. Wong }
11193ac51e74SDarrick J. Wong
do_suspend(struct dm_ioctl * param)11201da177e4SLinus Torvalds static int do_suspend(struct dm_ioctl *param)
11211da177e4SLinus Torvalds {
11221da177e4SLinus Torvalds int r = 0;
112386a3238cSHeinz Mauelshagen unsigned int suspend_flags = DM_SUSPEND_LOCKFS_FLAG;
11241da177e4SLinus Torvalds struct mapped_device *md;
11251da177e4SLinus Torvalds
11261da177e4SLinus Torvalds md = find_device(param);
11271da177e4SLinus Torvalds if (!md)
11281da177e4SLinus Torvalds return -ENXIO;
11291da177e4SLinus Torvalds
11306da487dcSAlasdair G Kergon if (param->flags & DM_SKIP_LOCKFS_FLAG)
1131a3d77d35SKiyoshi Ueda suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
113281fdb096SKiyoshi Ueda if (param->flags & DM_NOFLUSH_FLAG)
113381fdb096SKiyoshi Ueda suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
11346da487dcSAlasdair G Kergon
1135094ea9a0SAlasdair G Kergon if (!dm_suspended_md(md)) {
1136a3d77d35SKiyoshi Ueda r = dm_suspend(md, suspend_flags);
1137094ea9a0SAlasdair G Kergon if (r)
1138094ea9a0SAlasdair G Kergon goto out;
1139094ea9a0SAlasdair G Kergon }
11401da177e4SLinus Torvalds
1141094ea9a0SAlasdair G Kergon __dev_status(md, param);
11421da177e4SLinus Torvalds
1143094ea9a0SAlasdair G Kergon out:
11441da177e4SLinus Torvalds dm_put(md);
1145094ea9a0SAlasdair G Kergon
11461da177e4SLinus Torvalds return r;
11471da177e4SLinus Torvalds }
11481da177e4SLinus Torvalds
do_resume(struct dm_ioctl * param)11491da177e4SLinus Torvalds static int do_resume(struct dm_ioctl *param)
11501da177e4SLinus Torvalds {
11511da177e4SLinus Torvalds int r = 0;
115286a3238cSHeinz Mauelshagen unsigned int suspend_flags = DM_SUSPEND_LOCKFS_FLAG;
11531da177e4SLinus Torvalds struct hash_cell *hc;
11541da177e4SLinus Torvalds struct mapped_device *md;
1155042d2a9bSAlasdair G Kergon struct dm_table *new_map, *old_map = NULL;
11567533afa1SMikulas Patocka bool need_resize_uevent = false;
11571da177e4SLinus Torvalds
11581da177e4SLinus Torvalds down_write(&_hash_lock);
11591da177e4SLinus Torvalds
11601da177e4SLinus Torvalds hc = __find_device_hash_cell(param);
11611da177e4SLinus Torvalds if (!hc) {
1162810b4923SMilan Broz DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
11631da177e4SLinus Torvalds up_write(&_hash_lock);
11641da177e4SLinus Torvalds return -ENXIO;
11651da177e4SLinus Torvalds }
11661da177e4SLinus Torvalds
11671da177e4SLinus Torvalds md = hc->md;
11681da177e4SLinus Torvalds
11691da177e4SLinus Torvalds new_map = hc->new_map;
11701da177e4SLinus Torvalds hc->new_map = NULL;
11711da177e4SLinus Torvalds param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
11721da177e4SLinus Torvalds
11731da177e4SLinus Torvalds up_write(&_hash_lock);
11741da177e4SLinus Torvalds
11751da177e4SLinus Torvalds /* Do we need to load a new map ? */
11761da177e4SLinus Torvalds if (new_map) {
11777533afa1SMikulas Patocka sector_t old_size, new_size;
11787533afa1SMikulas Patocka
11791da177e4SLinus Torvalds /* Suspend if it isn't already suspended */
11802760904dSLi Lingfeng if (param->flags & DM_SKIP_LOCKFS_FLAG)
1181a3d77d35SKiyoshi Ueda suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
118281fdb096SKiyoshi Ueda if (param->flags & DM_NOFLUSH_FLAG)
118381fdb096SKiyoshi Ueda suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
11844f186f8bSKiyoshi Ueda if (!dm_suspended_md(md))
1185a3d77d35SKiyoshi Ueda dm_suspend(md, suspend_flags);
11861da177e4SLinus Torvalds
11877533afa1SMikulas Patocka old_size = dm_get_size(md);
1188042d2a9bSAlasdair G Kergon old_map = dm_swap_table(md, new_map);
1189042d2a9bSAlasdair G Kergon if (IS_ERR(old_map)) {
119083d5e5b0SMikulas Patocka dm_sync_table(md);
1191d5816876SMikulas Patocka dm_table_destroy(new_map);
11921da177e4SLinus Torvalds dm_put(md);
1193042d2a9bSAlasdair G Kergon return PTR_ERR(old_map);
11941da177e4SLinus Torvalds }
11957533afa1SMikulas Patocka new_size = dm_get_size(md);
11967533afa1SMikulas Patocka if (old_size && new_size && old_size != new_size)
11977533afa1SMikulas Patocka need_resize_uevent = true;
11981da177e4SLinus Torvalds
119905bdb996SChristoph Hellwig if (dm_table_get_mode(new_map) & BLK_OPEN_WRITE)
12001da177e4SLinus Torvalds set_disk_ro(dm_disk(md), 0);
12011da177e4SLinus Torvalds else
12021da177e4SLinus Torvalds set_disk_ro(dm_disk(md), 1);
12031da177e4SLinus Torvalds }
12041da177e4SLinus Torvalds
12050f3649a9SMike Snitzer if (dm_suspended_md(md)) {
12061da177e4SLinus Torvalds r = dm_resume(md);
12078eb6fab4STushar Sugandhi if (!r) {
12088eb6fab4STushar Sugandhi dm_ima_measure_on_device_resume(md, new_map ? true : false);
12098eb6fab4STushar Sugandhi
12107533afa1SMikulas Patocka if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr, need_resize_uevent))
12113abf85b5SPeter Rajnoha param->flags |= DM_UEVENT_GENERATED_FLAG;
12120f3649a9SMike Snitzer }
12138eb6fab4STushar Sugandhi }
12141da177e4SLinus Torvalds
121583d5e5b0SMikulas Patocka /*
121683d5e5b0SMikulas Patocka * Since dm_swap_table synchronizes RCU, nobody should be in
121783d5e5b0SMikulas Patocka * read-side critical section already.
121883d5e5b0SMikulas Patocka */
1219042d2a9bSAlasdair G Kergon if (old_map)
1220042d2a9bSAlasdair G Kergon dm_table_destroy(old_map);
122160935eb2SMilan Broz
12220f3649a9SMike Snitzer if (!r)
1223094ea9a0SAlasdair G Kergon __dev_status(md, param);
12241da177e4SLinus Torvalds
12251da177e4SLinus Torvalds dm_put(md);
12261da177e4SLinus Torvalds return r;
12271da177e4SLinus Torvalds }
12281da177e4SLinus Torvalds
12291da177e4SLinus Torvalds /*
12301da177e4SLinus Torvalds * Set or unset the suspension state of a device.
12311da177e4SLinus Torvalds * If the device already is in the requested state we just return its status.
12321da177e4SLinus Torvalds */
dev_suspend(struct file * filp,struct dm_ioctl * param,size_t param_size)1233fc1841e1SMikulas Patocka static int dev_suspend(struct file *filp, struct dm_ioctl *param, size_t param_size)
12341da177e4SLinus Torvalds {
12351da177e4SLinus Torvalds if (param->flags & DM_SUSPEND_FLAG)
12361da177e4SLinus Torvalds return do_suspend(param);
12371da177e4SLinus Torvalds
12381da177e4SLinus Torvalds return do_resume(param);
12391da177e4SLinus Torvalds }
12401da177e4SLinus Torvalds
12411da177e4SLinus Torvalds /*
12421da177e4SLinus Torvalds * Copies device info back to user space, used by
12431da177e4SLinus Torvalds * the create and info ioctls.
12441da177e4SLinus Torvalds */
dev_status(struct file * filp,struct dm_ioctl * param,size_t param_size)1245fc1841e1SMikulas Patocka static int dev_status(struct file *filp, struct dm_ioctl *param, size_t param_size)
12461da177e4SLinus Torvalds {
12471da177e4SLinus Torvalds struct mapped_device *md;
12481da177e4SLinus Torvalds
12491da177e4SLinus Torvalds md = find_device(param);
12501da177e4SLinus Torvalds if (!md)
12511da177e4SLinus Torvalds return -ENXIO;
12521da177e4SLinus Torvalds
1253094ea9a0SAlasdair G Kergon __dev_status(md, param);
12541da177e4SLinus Torvalds dm_put(md);
1255094ea9a0SAlasdair G Kergon
1256094ea9a0SAlasdair G Kergon return 0;
12571da177e4SLinus Torvalds }
12581da177e4SLinus Torvalds
12591da177e4SLinus Torvalds /*
12601da177e4SLinus Torvalds * Build up the status struct for each target
12611da177e4SLinus Torvalds */
retrieve_status(struct dm_table * table,struct dm_ioctl * param,size_t param_size)12621da177e4SLinus Torvalds static void retrieve_status(struct dm_table *table,
12631da177e4SLinus Torvalds struct dm_ioctl *param, size_t param_size)
12641da177e4SLinus Torvalds {
12651da177e4SLinus Torvalds unsigned int i, num_targets;
12661da177e4SLinus Torvalds struct dm_target_spec *spec;
12671da177e4SLinus Torvalds char *outbuf, *outptr;
12681da177e4SLinus Torvalds status_type_t type;
12691da177e4SLinus Torvalds size_t remaining, len, used = 0;
127086a3238cSHeinz Mauelshagen unsigned int status_flags = 0;
12711da177e4SLinus Torvalds
12721da177e4SLinus Torvalds outptr = outbuf = get_result_buffer(param, param_size, &len);
12731da177e4SLinus Torvalds
12741da177e4SLinus Torvalds if (param->flags & DM_STATUS_TABLE_FLAG)
12751da177e4SLinus Torvalds type = STATUSTYPE_TABLE;
127691ccbbacSTushar Sugandhi else if (param->flags & DM_IMA_MEASUREMENT_FLAG)
127791ccbbacSTushar Sugandhi type = STATUSTYPE_IMA;
12781da177e4SLinus Torvalds else
12791da177e4SLinus Torvalds type = STATUSTYPE_INFO;
12801da177e4SLinus Torvalds
12811da177e4SLinus Torvalds /* Get all the target info */
12822aec377aSMike Snitzer num_targets = table->num_targets;
12831da177e4SLinus Torvalds for (i = 0; i < num_targets; i++) {
12841da177e4SLinus Torvalds struct dm_target *ti = dm_table_get_target(table, i);
1285fd7c092eSMikulas Patocka size_t l;
12861da177e4SLinus Torvalds
12871da177e4SLinus Torvalds remaining = len - (outptr - outbuf);
12881da177e4SLinus Torvalds if (remaining <= sizeof(struct dm_target_spec)) {
12891da177e4SLinus Torvalds param->flags |= DM_BUFFER_FULL_FLAG;
12901da177e4SLinus Torvalds break;
12911da177e4SLinus Torvalds }
12921da177e4SLinus Torvalds
12931da177e4SLinus Torvalds spec = (struct dm_target_spec *) outptr;
12941da177e4SLinus Torvalds
12951da177e4SLinus Torvalds spec->status = 0;
12961da177e4SLinus Torvalds spec->sector_start = ti->begin;
12971da177e4SLinus Torvalds spec->length = ti->len;
12981da177e4SLinus Torvalds strncpy(spec->target_type, ti->type->name,
12991aeb6e7cSDamien Le Moal sizeof(spec->target_type) - 1);
13001da177e4SLinus Torvalds
13011da177e4SLinus Torvalds outptr += sizeof(struct dm_target_spec);
13021da177e4SLinus Torvalds remaining = len - (outptr - outbuf);
13031da177e4SLinus Torvalds if (remaining <= 0) {
13041da177e4SLinus Torvalds param->flags |= DM_BUFFER_FULL_FLAG;
13051da177e4SLinus Torvalds break;
13061da177e4SLinus Torvalds }
13071da177e4SLinus Torvalds
13081da177e4SLinus Torvalds /* Get the status/table string from the target driver */
13091da177e4SLinus Torvalds if (ti->type->status) {
13101f4e0ff0SAlasdair G Kergon if (param->flags & DM_NOFLUSH_FLAG)
13111f4e0ff0SAlasdair G Kergon status_flags |= DM_STATUS_NOFLUSH_FLAG;
1312fd7c092eSMikulas Patocka ti->type->status(ti, type, status_flags, outptr, remaining);
13131da177e4SLinus Torvalds } else
13141da177e4SLinus Torvalds outptr[0] = '\0';
13151da177e4SLinus Torvalds
1316fd7c092eSMikulas Patocka l = strlen(outptr) + 1;
1317fd7c092eSMikulas Patocka if (l == remaining) {
1318fd7c092eSMikulas Patocka param->flags |= DM_BUFFER_FULL_FLAG;
1319fd7c092eSMikulas Patocka break;
1320fd7c092eSMikulas Patocka }
1321fd7c092eSMikulas Patocka
1322fd7c092eSMikulas Patocka outptr += l;
13231da177e4SLinus Torvalds used = param->data_start + (outptr - outbuf);
13241da177e4SLinus Torvalds
13251da177e4SLinus Torvalds outptr = align_ptr(outptr);
13261da177e4SLinus Torvalds spec->next = outptr - outbuf;
13271da177e4SLinus Torvalds }
13281da177e4SLinus Torvalds
13291da177e4SLinus Torvalds if (used)
13301da177e4SLinus Torvalds param->data_size = used;
13311da177e4SLinus Torvalds
13321da177e4SLinus Torvalds param->target_count = num_targets;
13331da177e4SLinus Torvalds }
13341da177e4SLinus Torvalds
13351da177e4SLinus Torvalds /*
13361da177e4SLinus Torvalds * Wait for a device to report an event
13371da177e4SLinus Torvalds */
dev_wait(struct file * filp,struct dm_ioctl * param,size_t param_size)1338fc1841e1SMikulas Patocka static int dev_wait(struct file *filp, struct dm_ioctl *param, size_t param_size)
13391da177e4SLinus Torvalds {
1340094ea9a0SAlasdair G Kergon int r = 0;
13411da177e4SLinus Torvalds struct mapped_device *md;
13421da177e4SLinus Torvalds struct dm_table *table;
134383d5e5b0SMikulas Patocka int srcu_idx;
13441da177e4SLinus Torvalds
13451da177e4SLinus Torvalds md = find_device(param);
13461da177e4SLinus Torvalds if (!md)
13471da177e4SLinus Torvalds return -ENXIO;
13481da177e4SLinus Torvalds
13491da177e4SLinus Torvalds /*
13501da177e4SLinus Torvalds * Wait for a notification event
13511da177e4SLinus Torvalds */
13521da177e4SLinus Torvalds if (dm_wait_event(md, param->event_nr)) {
13531da177e4SLinus Torvalds r = -ERESTARTSYS;
13541da177e4SLinus Torvalds goto out;
13551da177e4SLinus Torvalds }
13561da177e4SLinus Torvalds
13571da177e4SLinus Torvalds /*
13581da177e4SLinus Torvalds * The userland program is going to want to know what
13591da177e4SLinus Torvalds * changed to trigger the event, so we may as well tell
13601da177e4SLinus Torvalds * him and save an ioctl.
13611da177e4SLinus Torvalds */
1362094ea9a0SAlasdair G Kergon __dev_status(md, param);
13631da177e4SLinus Torvalds
136483d5e5b0SMikulas Patocka table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
136583d5e5b0SMikulas Patocka if (table)
13661da177e4SLinus Torvalds retrieve_status(table, param, param_size);
136783d5e5b0SMikulas Patocka dm_put_live_table(md, srcu_idx);
13681da177e4SLinus Torvalds
13691da177e4SLinus Torvalds out:
13701da177e4SLinus Torvalds dm_put(md);
1371094ea9a0SAlasdair G Kergon
13721da177e4SLinus Torvalds return r;
13731da177e4SLinus Torvalds }
13741da177e4SLinus Torvalds
1375fc1841e1SMikulas Patocka /*
1376fc1841e1SMikulas Patocka * Remember the global event number and make it possible to poll
1377fc1841e1SMikulas Patocka * for further events.
1378fc1841e1SMikulas Patocka */
dev_arm_poll(struct file * filp,struct dm_ioctl * param,size_t param_size)1379fc1841e1SMikulas Patocka static int dev_arm_poll(struct file *filp, struct dm_ioctl *param, size_t param_size)
1380fc1841e1SMikulas Patocka {
1381fc1841e1SMikulas Patocka struct dm_file *priv = filp->private_data;
1382fc1841e1SMikulas Patocka
1383fc1841e1SMikulas Patocka priv->global_event_nr = atomic_read(&dm_global_event_nr);
1384fc1841e1SMikulas Patocka
1385fc1841e1SMikulas Patocka return 0;
1386fc1841e1SMikulas Patocka }
1387fc1841e1SMikulas Patocka
get_mode(struct dm_ioctl * param)138805bdb996SChristoph Hellwig static inline blk_mode_t get_mode(struct dm_ioctl *param)
13891da177e4SLinus Torvalds {
139005bdb996SChristoph Hellwig blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_WRITE;
13911da177e4SLinus Torvalds
13921da177e4SLinus Torvalds if (param->flags & DM_READONLY_FLAG)
139305bdb996SChristoph Hellwig mode = BLK_OPEN_READ;
13941da177e4SLinus Torvalds
13951da177e4SLinus Torvalds return mode;
13961da177e4SLinus Torvalds }
13971da177e4SLinus Torvalds
next_target(struct dm_target_spec * last,uint32_t next,const char * end,struct dm_target_spec ** spec,char ** target_params)139810655c7aSDemi Marie Obenour static int next_target(struct dm_target_spec *last, uint32_t next, const char *end,
13991da177e4SLinus Torvalds struct dm_target_spec **spec, char **target_params)
14001da177e4SLinus Torvalds {
1401b60528d9SDemi Marie Obenour static_assert(__alignof__(struct dm_target_spec) <= 8,
1402b60528d9SDemi Marie Obenour "struct dm_target_spec must not require more than 8-byte alignment");
1403b60528d9SDemi Marie Obenour
140413f4a697SDemi Marie Obenour /*
140513f4a697SDemi Marie Obenour * Number of bytes remaining, starting with last. This is always
140613f4a697SDemi Marie Obenour * sizeof(struct dm_target_spec) or more, as otherwise *last was
140713f4a697SDemi Marie Obenour * out of bounds already.
140813f4a697SDemi Marie Obenour */
140910655c7aSDemi Marie Obenour size_t remaining = end - (char *)last;
141013f4a697SDemi Marie Obenour
141113f4a697SDemi Marie Obenour /*
141213f4a697SDemi Marie Obenour * There must be room for both the next target spec and the
141313f4a697SDemi Marie Obenour * NUL-terminator of the target itself.
141413f4a697SDemi Marie Obenour */
141513f4a697SDemi Marie Obenour if (remaining - sizeof(struct dm_target_spec) <= next) {
141613f4a697SDemi Marie Obenour DMERR("Target spec extends beyond end of parameters");
141713f4a697SDemi Marie Obenour return -EINVAL;
141813f4a697SDemi Marie Obenour }
141913f4a697SDemi Marie Obenour
1420b60528d9SDemi Marie Obenour if (next % __alignof__(struct dm_target_spec)) {
1421b60528d9SDemi Marie Obenour DMERR("Next dm_target_spec (offset %u) is not %zu-byte aligned",
1422b60528d9SDemi Marie Obenour next, __alignof__(struct dm_target_spec));
1423b60528d9SDemi Marie Obenour return -EINVAL;
1424b60528d9SDemi Marie Obenour }
1425b60528d9SDemi Marie Obenour
14261da177e4SLinus Torvalds *spec = (struct dm_target_spec *) ((unsigned char *) last + next);
14271da177e4SLinus Torvalds *target_params = (char *) (*spec + 1);
14281da177e4SLinus Torvalds
142910655c7aSDemi Marie Obenour return 0;
14301da177e4SLinus Torvalds }
14311da177e4SLinus Torvalds
populate_table(struct dm_table * table,struct dm_ioctl * param,size_t param_size)14321da177e4SLinus Torvalds static int populate_table(struct dm_table *table,
14331da177e4SLinus Torvalds struct dm_ioctl *param, size_t param_size)
14341da177e4SLinus Torvalds {
14351da177e4SLinus Torvalds int r;
14361da177e4SLinus Torvalds unsigned int i = 0;
14371da177e4SLinus Torvalds struct dm_target_spec *spec = (struct dm_target_spec *) param;
14381da177e4SLinus Torvalds uint32_t next = param->data_start;
143910655c7aSDemi Marie Obenour const char *const end = (const char *) param + param_size;
14401da177e4SLinus Torvalds char *target_params;
144110655c7aSDemi Marie Obenour size_t min_size = sizeof(struct dm_ioctl);
14421da177e4SLinus Torvalds
14431da177e4SLinus Torvalds if (!param->target_count) {
14441c131886SHeinz Mauelshagen DMERR("%s: no targets specified", __func__);
14451da177e4SLinus Torvalds return -EINVAL;
14461da177e4SLinus Torvalds }
14471da177e4SLinus Torvalds
14481da177e4SLinus Torvalds for (i = 0; i < param->target_count; i++) {
144910655c7aSDemi Marie Obenour const char *nul_terminator;
145010655c7aSDemi Marie Obenour
145110655c7aSDemi Marie Obenour if (next < min_size) {
145210655c7aSDemi Marie Obenour DMERR("%s: next target spec (offset %u) overlaps %s",
145310655c7aSDemi Marie Obenour __func__, next, i ? "previous target" : "'struct dm_ioctl'");
145410655c7aSDemi Marie Obenour return -EINVAL;
145510655c7aSDemi Marie Obenour }
14561da177e4SLinus Torvalds
14571da177e4SLinus Torvalds r = next_target(spec, next, end, &spec, &target_params);
14581da177e4SLinus Torvalds if (r) {
145943e6c111SMikulas Patocka DMERR("unable to find target");
14601da177e4SLinus Torvalds return r;
14611da177e4SLinus Torvalds }
14621da177e4SLinus Torvalds
146310655c7aSDemi Marie Obenour nul_terminator = memchr(target_params, 0, (size_t)(end - target_params));
146410655c7aSDemi Marie Obenour if (nul_terminator == NULL) {
146510655c7aSDemi Marie Obenour DMERR("%s: target parameters not NUL-terminated", __func__);
146610655c7aSDemi Marie Obenour return -EINVAL;
146710655c7aSDemi Marie Obenour }
146810655c7aSDemi Marie Obenour
146910655c7aSDemi Marie Obenour /* Add 1 for NUL terminator */
147010655c7aSDemi Marie Obenour min_size = (size_t)(nul_terminator - (const char *)spec) + 1;
147110655c7aSDemi Marie Obenour
14721da177e4SLinus Torvalds r = dm_table_add_target(table, spec->target_type,
14731da177e4SLinus Torvalds (sector_t) spec->sector_start,
14741da177e4SLinus Torvalds (sector_t) spec->length,
14751da177e4SLinus Torvalds target_params);
14761da177e4SLinus Torvalds if (r) {
147743e6c111SMikulas Patocka DMERR("error adding target to table");
14781da177e4SLinus Torvalds return r;
14791da177e4SLinus Torvalds }
14801da177e4SLinus Torvalds
14811da177e4SLinus Torvalds next = spec->next;
14821da177e4SLinus Torvalds }
14831da177e4SLinus Torvalds
14841da177e4SLinus Torvalds return dm_table_complete(table);
14851da177e4SLinus Torvalds }
14861da177e4SLinus Torvalds
is_valid_type(enum dm_queue_mode cur,enum dm_queue_mode new)14877e0d574fSBart Van Assche static bool is_valid_type(enum dm_queue_mode cur, enum dm_queue_mode new)
1488b5ab4a9bSToshi Kani {
1489b5ab4a9bSToshi Kani if (cur == new ||
1490b5ab4a9bSToshi Kani (cur == DM_TYPE_BIO_BASED && new == DM_TYPE_DAX_BIO_BASED))
1491b5ab4a9bSToshi Kani return true;
1492b5ab4a9bSToshi Kani
1493b5ab4a9bSToshi Kani return false;
1494b5ab4a9bSToshi Kani }
1495b5ab4a9bSToshi Kani
table_load(struct file * filp,struct dm_ioctl * param,size_t param_size)1496fc1841e1SMikulas Patocka static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_size)
14971da177e4SLinus Torvalds {
14981da177e4SLinus Torvalds int r;
14991da177e4SLinus Torvalds struct hash_cell *hc;
150083d5e5b0SMikulas Patocka struct dm_table *t, *old_map = NULL;
15011134e5aeSMike Anderson struct mapped_device *md;
150236a0456fSAlasdair G Kergon struct target_type *immutable_target_type;
15031da177e4SLinus Torvalds
15041134e5aeSMike Anderson md = find_device(param);
15051134e5aeSMike Anderson if (!md)
15061134e5aeSMike Anderson return -ENXIO;
15071134e5aeSMike Anderson
15081134e5aeSMike Anderson r = dm_table_create(&t, get_mode(param), param->target_count, md);
15091da177e4SLinus Torvalds if (r)
1510f11c1c56SMike Snitzer goto err;
15111da177e4SLinus Torvalds
151200c4fc3bSMike Snitzer /* Protect md->type and md->queue against concurrent table loads. */
151300c4fc3bSMike Snitzer dm_lock_md_type(md);
15141da177e4SLinus Torvalds r = populate_table(t, param, param_size);
1515f11c1c56SMike Snitzer if (r)
1516f11c1c56SMike Snitzer goto err_unlock_md_type;
15171da177e4SLinus Torvalds
151891ccbbacSTushar Sugandhi dm_ima_measure_on_table_load(t, STATUSTYPE_IMA);
151991ccbbacSTushar Sugandhi
152036a0456fSAlasdair G Kergon immutable_target_type = dm_get_immutable_target_type(md);
152136a0456fSAlasdair G Kergon if (immutable_target_type &&
1522f083b09bSMike Snitzer (immutable_target_type != dm_table_get_immutable_target_type(t)) &&
1523f083b09bSMike Snitzer !dm_table_get_wildcard_target(t)) {
152443e6c111SMikulas Patocka DMERR("can't replace immutable target type %s",
152536a0456fSAlasdair G Kergon immutable_target_type->name);
152636a0456fSAlasdair G Kergon r = -EINVAL;
1527f11c1c56SMike Snitzer goto err_unlock_md_type;
152836a0456fSAlasdair G Kergon }
152936a0456fSAlasdair G Kergon
15303e6180f0SChristoph Hellwig if (dm_get_md_type(md) == DM_TYPE_NONE) {
15314a0b4ddfSMike Snitzer /* setup md->queue to reflect md's type (may block) */
1532591ddcfcSMike Snitzer r = dm_setup_md_queue(md, t);
15334a0b4ddfSMike Snitzer if (r) {
153443e6c111SMikulas Patocka DMERR("unable to set up device queue for new table.");
1535f11c1c56SMike Snitzer goto err_unlock_md_type;
15364a0b4ddfSMike Snitzer }
1537b5ab4a9bSToshi Kani } else if (!is_valid_type(dm_get_md_type(md), dm_table_get_type(t))) {
153843e6c111SMikulas Patocka DMERR("can't change device type (old=%u vs new=%u) after initial table load.",
1539b2b04e7eSMike Snitzer dm_get_md_type(md), dm_table_get_type(t));
15403e6180f0SChristoph Hellwig r = -EINVAL;
15413e6180f0SChristoph Hellwig goto err_unlock_md_type;
15423e6180f0SChristoph Hellwig }
15433e6180f0SChristoph Hellwig
1544a5664dadSMike Snitzer dm_unlock_md_type(md);
1545a5664dadSMike Snitzer
1546a5664dadSMike Snitzer /* stage inactive table */
15471da177e4SLinus Torvalds down_write(&_hash_lock);
15481134e5aeSMike Anderson hc = dm_get_mdptr(md);
1549a2f998a7SHou Tao if (!hc) {
155043e6c111SMikulas Patocka DMERR("device has been removed from the dev hash table.");
15511134e5aeSMike Anderson up_write(&_hash_lock);
15521134e5aeSMike Anderson r = -ENXIO;
1553f11c1c56SMike Snitzer goto err_destroy_table;
15541da177e4SLinus Torvalds }
15551da177e4SLinus Torvalds
15561da177e4SLinus Torvalds if (hc->new_map)
155783d5e5b0SMikulas Patocka old_map = hc->new_map;
15581da177e4SLinus Torvalds hc->new_map = t;
15591da177e4SLinus Torvalds up_write(&_hash_lock);
15601134e5aeSMike Anderson
15611134e5aeSMike Anderson param->flags |= DM_INACTIVE_PRESENT_FLAG;
1562094ea9a0SAlasdair G Kergon __dev_status(md, param);
15631134e5aeSMike Anderson
156483d5e5b0SMikulas Patocka if (old_map) {
156583d5e5b0SMikulas Patocka dm_sync_table(md);
156683d5e5b0SMikulas Patocka dm_table_destroy(old_map);
156783d5e5b0SMikulas Patocka }
156883d5e5b0SMikulas Patocka
15691134e5aeSMike Anderson dm_put(md);
15701134e5aeSMike Anderson
1571f11c1c56SMike Snitzer return 0;
1572f11c1c56SMike Snitzer
1573f11c1c56SMike Snitzer err_unlock_md_type:
1574f11c1c56SMike Snitzer dm_unlock_md_type(md);
1575f11c1c56SMike Snitzer err_destroy_table:
1576f11c1c56SMike Snitzer dm_table_destroy(t);
1577f11c1c56SMike Snitzer err:
1578f11c1c56SMike Snitzer dm_put(md);
1579f11c1c56SMike Snitzer
15801da177e4SLinus Torvalds return r;
15811da177e4SLinus Torvalds }
15821da177e4SLinus Torvalds
table_clear(struct file * filp,struct dm_ioctl * param,size_t param_size)1583fc1841e1SMikulas Patocka static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_size)
15841da177e4SLinus Torvalds {
15851da177e4SLinus Torvalds struct hash_cell *hc;
15867ec75f25SJeff Mahoney struct mapped_device *md;
158783d5e5b0SMikulas Patocka struct dm_table *old_map = NULL;
158899169b93STushar Sugandhi bool has_new_map = false;
15891da177e4SLinus Torvalds
15901da177e4SLinus Torvalds down_write(&_hash_lock);
15911da177e4SLinus Torvalds
15921da177e4SLinus Torvalds hc = __find_device_hash_cell(param);
15931da177e4SLinus Torvalds if (!hc) {
1594810b4923SMilan Broz DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
15951da177e4SLinus Torvalds up_write(&_hash_lock);
15961da177e4SLinus Torvalds return -ENXIO;
15971da177e4SLinus Torvalds }
15981da177e4SLinus Torvalds
15991da177e4SLinus Torvalds if (hc->new_map) {
160083d5e5b0SMikulas Patocka old_map = hc->new_map;
16011da177e4SLinus Torvalds hc->new_map = NULL;
160299169b93STushar Sugandhi has_new_map = true;
16031da177e4SLinus Torvalds }
16041da177e4SLinus Torvalds
16057ec75f25SJeff Mahoney md = hc->md;
16061da177e4SLinus Torvalds up_write(&_hash_lock);
16073d32aaa7SMike Snitzer
16083d32aaa7SMike Snitzer param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
16093d32aaa7SMike Snitzer __dev_status(md, param);
16103d32aaa7SMike Snitzer
161183d5e5b0SMikulas Patocka if (old_map) {
161283d5e5b0SMikulas Patocka dm_sync_table(md);
161383d5e5b0SMikulas Patocka dm_table_destroy(old_map);
161483d5e5b0SMikulas Patocka }
161599169b93STushar Sugandhi dm_ima_measure_on_table_clear(md, has_new_map);
16167ec75f25SJeff Mahoney dm_put(md);
1617094ea9a0SAlasdair G Kergon
1618094ea9a0SAlasdair G Kergon return 0;
16191da177e4SLinus Torvalds }
16201da177e4SLinus Torvalds
16211da177e4SLinus Torvalds /*
16221da177e4SLinus Torvalds * Retrieves a list of devices used by a particular dm device.
16231da177e4SLinus Torvalds */
retrieve_deps(struct dm_table * table,struct dm_ioctl * param,size_t param_size)16241da177e4SLinus Torvalds static void retrieve_deps(struct dm_table *table,
16251da177e4SLinus Torvalds struct dm_ioctl *param, size_t param_size)
16261da177e4SLinus Torvalds {
16271da177e4SLinus Torvalds unsigned int count = 0;
16281da177e4SLinus Torvalds struct list_head *tmp;
16291da177e4SLinus Torvalds size_t len, needed;
163082b1519bSMikulas Patocka struct dm_dev_internal *dd;
16311da177e4SLinus Torvalds struct dm_target_deps *deps;
16321da177e4SLinus Torvalds
1633f6007dceSMikulas Patocka down_read(&table->devices_lock);
1634f6007dceSMikulas Patocka
16351da177e4SLinus Torvalds deps = get_result_buffer(param, param_size, &len);
16361da177e4SLinus Torvalds
16371da177e4SLinus Torvalds /*
16381da177e4SLinus Torvalds * Count the devices.
16391da177e4SLinus Torvalds */
16401da177e4SLinus Torvalds list_for_each(tmp, dm_table_get_devices(table))
16411da177e4SLinus Torvalds count++;
16421da177e4SLinus Torvalds
16431da177e4SLinus Torvalds /*
16441da177e4SLinus Torvalds * Check we have enough space.
16451da177e4SLinus Torvalds */
1646da899625SGustavo A. R. Silva needed = struct_size(deps, dev, count);
16471da177e4SLinus Torvalds if (len < needed) {
16481da177e4SLinus Torvalds param->flags |= DM_BUFFER_FULL_FLAG;
1649f6007dceSMikulas Patocka goto out;
16501da177e4SLinus Torvalds }
16511da177e4SLinus Torvalds
16521da177e4SLinus Torvalds /*
16531da177e4SLinus Torvalds * Fill in the devices.
16541da177e4SLinus Torvalds */
16551da177e4SLinus Torvalds deps->count = count;
16561da177e4SLinus Torvalds count = 0;
16571da177e4SLinus Torvalds list_for_each_entry(dd, dm_table_get_devices(table), list)
165886f1152bSBenjamin Marzinski deps->dev[count++] = huge_encode_dev(dd->dm_dev->bdev->bd_dev);
16591da177e4SLinus Torvalds
16601da177e4SLinus Torvalds param->data_size = param->data_start + needed;
1661f6007dceSMikulas Patocka
1662f6007dceSMikulas Patocka out:
1663f6007dceSMikulas Patocka up_read(&table->devices_lock);
16641da177e4SLinus Torvalds }
16651da177e4SLinus Torvalds
table_deps(struct file * filp,struct dm_ioctl * param,size_t param_size)1666fc1841e1SMikulas Patocka static int table_deps(struct file *filp, struct dm_ioctl *param, size_t param_size)
16671da177e4SLinus Torvalds {
16681da177e4SLinus Torvalds struct mapped_device *md;
16691da177e4SLinus Torvalds struct dm_table *table;
167083d5e5b0SMikulas Patocka int srcu_idx;
16711da177e4SLinus Torvalds
16721da177e4SLinus Torvalds md = find_device(param);
16731da177e4SLinus Torvalds if (!md)
16741da177e4SLinus Torvalds return -ENXIO;
16751da177e4SLinus Torvalds
1676094ea9a0SAlasdair G Kergon __dev_status(md, param);
16771da177e4SLinus Torvalds
167883d5e5b0SMikulas Patocka table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
167983d5e5b0SMikulas Patocka if (table)
16801da177e4SLinus Torvalds retrieve_deps(table, param, param_size);
168183d5e5b0SMikulas Patocka dm_put_live_table(md, srcu_idx);
16821da177e4SLinus Torvalds
16831da177e4SLinus Torvalds dm_put(md);
1684094ea9a0SAlasdair G Kergon
1685094ea9a0SAlasdair G Kergon return 0;
16861da177e4SLinus Torvalds }
16871da177e4SLinus Torvalds
16881da177e4SLinus Torvalds /*
16891da177e4SLinus Torvalds * Return the status of a device as a text string for each
16901da177e4SLinus Torvalds * target.
16911da177e4SLinus Torvalds */
table_status(struct file * filp,struct dm_ioctl * param,size_t param_size)1692fc1841e1SMikulas Patocka static int table_status(struct file *filp, struct dm_ioctl *param, size_t param_size)
16931da177e4SLinus Torvalds {
16941da177e4SLinus Torvalds struct mapped_device *md;
16951da177e4SLinus Torvalds struct dm_table *table;
169683d5e5b0SMikulas Patocka int srcu_idx;
16971da177e4SLinus Torvalds
16981da177e4SLinus Torvalds md = find_device(param);
16991da177e4SLinus Torvalds if (!md)
17001da177e4SLinus Torvalds return -ENXIO;
17011da177e4SLinus Torvalds
1702094ea9a0SAlasdair G Kergon __dev_status(md, param);
17031da177e4SLinus Torvalds
170483d5e5b0SMikulas Patocka table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
170583d5e5b0SMikulas Patocka if (table)
17061da177e4SLinus Torvalds retrieve_status(table, param, param_size);
170783d5e5b0SMikulas Patocka dm_put_live_table(md, srcu_idx);
17081da177e4SLinus Torvalds
17091da177e4SLinus Torvalds dm_put(md);
1710094ea9a0SAlasdair G Kergon
1711094ea9a0SAlasdair G Kergon return 0;
17121da177e4SLinus Torvalds }
17131da177e4SLinus Torvalds
1714a2606241SMikulas Patocka /*
1715fd2ed4d2SMikulas Patocka * Process device-mapper dependent messages. Messages prefixed with '@'
1716fd2ed4d2SMikulas Patocka * are processed by the DM core. All others are delivered to the target.
1717a2606241SMikulas Patocka * Returns a number <= 1 if message was processed by device mapper.
1718a2606241SMikulas Patocka * Returns 2 if message should be delivered to the target.
1719a2606241SMikulas Patocka */
message_for_md(struct mapped_device * md,unsigned int argc,char ** argv,char * result,unsigned int maxlen)172086a3238cSHeinz Mauelshagen static int message_for_md(struct mapped_device *md, unsigned int argc, char **argv,
172186a3238cSHeinz Mauelshagen char *result, unsigned int maxlen)
1722a2606241SMikulas Patocka {
1723fd2ed4d2SMikulas Patocka int r;
1724fd2ed4d2SMikulas Patocka
1725fd2ed4d2SMikulas Patocka if (**argv != '@')
1726fd2ed4d2SMikulas Patocka return 2; /* no '@' prefix, deliver to target */
1727fd2ed4d2SMikulas Patocka
17282c140a24SMikulas Patocka if (!strcasecmp(argv[0], "@cancel_deferred_remove")) {
17292c140a24SMikulas Patocka if (argc != 1) {
17302c140a24SMikulas Patocka DMERR("Invalid arguments for @cancel_deferred_remove");
17312c140a24SMikulas Patocka return -EINVAL;
17322c140a24SMikulas Patocka }
17332c140a24SMikulas Patocka return dm_cancel_deferred_remove(md);
17342c140a24SMikulas Patocka }
17352c140a24SMikulas Patocka
1736fd2ed4d2SMikulas Patocka r = dm_stats_message(md, argc, argv, result, maxlen);
1737fd2ed4d2SMikulas Patocka if (r < 2)
1738fd2ed4d2SMikulas Patocka return r;
1739fd2ed4d2SMikulas Patocka
1740fd2ed4d2SMikulas Patocka DMERR("Unsupported message sent to DM core: %s", argv[0]);
1741fd2ed4d2SMikulas Patocka return -EINVAL;
1742a2606241SMikulas Patocka }
1743a2606241SMikulas Patocka
17441da177e4SLinus Torvalds /*
17451da177e4SLinus Torvalds * Pass a message to the target that's at the supplied device offset.
17461da177e4SLinus Torvalds */
target_message(struct file * filp,struct dm_ioctl * param,size_t param_size)1747fc1841e1SMikulas Patocka static int target_message(struct file *filp, struct dm_ioctl *param, size_t param_size)
17481da177e4SLinus Torvalds {
17491da177e4SLinus Torvalds int r, argc;
17501da177e4SLinus Torvalds char **argv;
17511da177e4SLinus Torvalds struct mapped_device *md;
17521da177e4SLinus Torvalds struct dm_table *table;
17531da177e4SLinus Torvalds struct dm_target *ti;
17541da177e4SLinus Torvalds struct dm_target_msg *tmsg = (void *) param + param->data_start;
1755a2606241SMikulas Patocka size_t maxlen;
1756a2606241SMikulas Patocka char *result = get_result_buffer(param, param_size, &maxlen);
175783d5e5b0SMikulas Patocka int srcu_idx;
17581da177e4SLinus Torvalds
17591da177e4SLinus Torvalds md = find_device(param);
17601da177e4SLinus Torvalds if (!md)
17611da177e4SLinus Torvalds return -ENXIO;
17621da177e4SLinus Torvalds
1763027d50f9SMilan Broz if (tmsg < (struct dm_target_msg *) param->data ||
17641da177e4SLinus Torvalds invalid_str(tmsg->message, (void *) param + param_size)) {
176543e6c111SMikulas Patocka DMERR("Invalid target message parameters.");
17661da177e4SLinus Torvalds r = -EINVAL;
17671da177e4SLinus Torvalds goto out;
17681da177e4SLinus Torvalds }
17691da177e4SLinus Torvalds
17701da177e4SLinus Torvalds r = dm_split_args(&argc, &argv, tmsg->message);
17711da177e4SLinus Torvalds if (r) {
177243e6c111SMikulas Patocka DMERR("Failed to split target message parameters");
17731da177e4SLinus Torvalds goto out;
17741da177e4SLinus Torvalds }
17751da177e4SLinus Torvalds
17762ca4c92fSAlasdair G Kergon if (!argc) {
177743e6c111SMikulas Patocka DMERR("Empty message received.");
17784d7659bfSQinglang Miao r = -EINVAL;
1779902c6a96SJesper Juhl goto out_argv;
17802ca4c92fSAlasdair G Kergon }
17812ca4c92fSAlasdair G Kergon
1782a2606241SMikulas Patocka r = message_for_md(md, argc, argv, result, maxlen);
1783a2606241SMikulas Patocka if (r <= 1)
1784a2606241SMikulas Patocka goto out_argv;
1785a2606241SMikulas Patocka
178683d5e5b0SMikulas Patocka table = dm_get_live_table(md, &srcu_idx);
17871da177e4SLinus Torvalds if (!table)
178883d5e5b0SMikulas Patocka goto out_table;
17891da177e4SLinus Torvalds
1790c50abeb3SMike Anderson if (dm_deleting_md(md)) {
1791c50abeb3SMike Anderson r = -ENXIO;
1792c50abeb3SMike Anderson goto out_table;
1793c50abeb3SMike Anderson }
1794c50abeb3SMike Anderson
1795512875bdSJun'ichi Nomura ti = dm_table_find_target(table, tmsg->sector);
1796123d87d5SMikulas Patocka if (!ti) {
179743e6c111SMikulas Patocka DMERR("Target message sector outside device.");
17981da177e4SLinus Torvalds r = -EINVAL;
1799512875bdSJun'ichi Nomura } else if (ti->type->message)
18001eb5fa84SMike Snitzer r = ti->type->message(ti, argc, argv, result, maxlen);
18011da177e4SLinus Torvalds else {
180243e6c111SMikulas Patocka DMERR("Target type does not support messages");
18031da177e4SLinus Torvalds r = -EINVAL;
18041da177e4SLinus Torvalds }
18051da177e4SLinus Torvalds
1806c50abeb3SMike Anderson out_table:
180783d5e5b0SMikulas Patocka dm_put_live_table(md, srcu_idx);
18081da177e4SLinus Torvalds out_argv:
18091da177e4SLinus Torvalds kfree(argv);
18101da177e4SLinus Torvalds out:
1811a2606241SMikulas Patocka if (r >= 0)
1812a2606241SMikulas Patocka __dev_status(md, param);
1813a2606241SMikulas Patocka
1814a2606241SMikulas Patocka if (r == 1) {
1815a2606241SMikulas Patocka param->flags |= DM_DATA_OUT_FLAG;
1816fd2ed4d2SMikulas Patocka if (dm_message_test_buffer_overflow(result, maxlen))
1817a2606241SMikulas Patocka param->flags |= DM_BUFFER_FULL_FLAG;
1818a2606241SMikulas Patocka else
1819a2606241SMikulas Patocka param->data_size = param->data_start + strlen(result) + 1;
1820a2606241SMikulas Patocka r = 0;
1821a2606241SMikulas Patocka }
1822a2606241SMikulas Patocka
18231da177e4SLinus Torvalds dm_put(md);
18241da177e4SLinus Torvalds return r;
18251da177e4SLinus Torvalds }
18261da177e4SLinus Torvalds
1827e2914cc2SMikulas Patocka /*
1828e2914cc2SMikulas Patocka * The ioctl parameter block consists of two parts, a dm_ioctl struct
1829e2914cc2SMikulas Patocka * followed by a data buffer. This flag is set if the second part,
1830e2914cc2SMikulas Patocka * which has a variable size, is not used by the function processing
1831e2914cc2SMikulas Patocka * the ioctl.
1832e2914cc2SMikulas Patocka */
1833e2914cc2SMikulas Patocka #define IOCTL_FLAGS_NO_PARAMS 1
183462e08243SMikulas Patocka #define IOCTL_FLAGS_ISSUE_GLOBAL_EVENT 2
1835e2914cc2SMikulas Patocka
1836a4a82ce3SHeinz Mauelshagen /*
1837a4a82ce3SHeinz Mauelshagen *---------------------------------------------------------------
1838a4a82ce3SHeinz Mauelshagen * Implementation of open/close/ioctl on the special char device.
1839a4a82ce3SHeinz Mauelshagen *---------------------------------------------------------------
1840a4a82ce3SHeinz Mauelshagen */
lookup_ioctl(unsigned int cmd,int * ioctl_flags)1841e2914cc2SMikulas Patocka static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags)
18421da177e4SLinus Torvalds {
1843cf0dec66SEric Biggers static const struct {
18441da177e4SLinus Torvalds int cmd;
1845e2914cc2SMikulas Patocka int flags;
18461da177e4SLinus Torvalds ioctl_fn fn;
18471da177e4SLinus Torvalds } _ioctls[] = {
1848e2914cc2SMikulas Patocka {DM_VERSION_CMD, 0, NULL}, /* version is dealt with elsewhere */
184962e08243SMikulas Patocka {DM_REMOVE_ALL_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, remove_all},
1850e2914cc2SMikulas Patocka {DM_LIST_DEVICES_CMD, 0, list_devices},
18511da177e4SLinus Torvalds
185262e08243SMikulas Patocka {DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_create},
185362e08243SMikulas Patocka {DM_DEV_REMOVE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_remove},
185462e08243SMikulas Patocka {DM_DEV_RENAME_CMD, IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_rename},
1855e2914cc2SMikulas Patocka {DM_DEV_SUSPEND_CMD, IOCTL_FLAGS_NO_PARAMS, dev_suspend},
1856e2914cc2SMikulas Patocka {DM_DEV_STATUS_CMD, IOCTL_FLAGS_NO_PARAMS, dev_status},
1857e2914cc2SMikulas Patocka {DM_DEV_WAIT_CMD, 0, dev_wait},
18581da177e4SLinus Torvalds
1859e2914cc2SMikulas Patocka {DM_TABLE_LOAD_CMD, 0, table_load},
1860e2914cc2SMikulas Patocka {DM_TABLE_CLEAR_CMD, IOCTL_FLAGS_NO_PARAMS, table_clear},
1861e2914cc2SMikulas Patocka {DM_TABLE_DEPS_CMD, 0, table_deps},
1862e2914cc2SMikulas Patocka {DM_TABLE_STATUS_CMD, 0, table_status},
18631da177e4SLinus Torvalds
1864e2914cc2SMikulas Patocka {DM_LIST_VERSIONS_CMD, 0, list_versions},
18651da177e4SLinus Torvalds
1866e2914cc2SMikulas Patocka {DM_TARGET_MSG_CMD, 0, target_message},
1867fc1841e1SMikulas Patocka {DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry},
1868b52c3de8SMikulas Patocka {DM_DEV_ARM_POLL_CMD, IOCTL_FLAGS_NO_PARAMS, dev_arm_poll},
1869b52c3de8SMikulas Patocka {DM_GET_TARGET_VERSION_CMD, 0, get_target_version},
18701da177e4SLinus Torvalds };
18711da177e4SLinus Torvalds
1872e2914cc2SMikulas Patocka if (unlikely(cmd >= ARRAY_SIZE(_ioctls)))
1873e2914cc2SMikulas Patocka return NULL;
1874e2914cc2SMikulas Patocka
1875cd9c88daSJordy Zomer cmd = array_index_nospec(cmd, ARRAY_SIZE(_ioctls));
1876e2914cc2SMikulas Patocka *ioctl_flags = _ioctls[cmd].flags;
1877e2914cc2SMikulas Patocka return _ioctls[cmd].fn;
18781da177e4SLinus Torvalds }
18791da177e4SLinus Torvalds
18801da177e4SLinus Torvalds /*
18811da177e4SLinus Torvalds * As well as checking the version compatibility this always
18821da177e4SLinus Torvalds * copies the kernel interface version out.
18831da177e4SLinus Torvalds */
check_version(unsigned int cmd,struct dm_ioctl __user * user,struct dm_ioctl * kernel_params)1884249bed82SDemi Marie Obenour static int check_version(unsigned int cmd, struct dm_ioctl __user *user,
1885249bed82SDemi Marie Obenour struct dm_ioctl *kernel_params)
18861da177e4SLinus Torvalds {
18871da177e4SLinus Torvalds int r = 0;
18881da177e4SLinus Torvalds
1889249bed82SDemi Marie Obenour /* Make certain version is first member of dm_ioctl struct */
1890249bed82SDemi Marie Obenour BUILD_BUG_ON(offsetof(struct dm_ioctl, version) != 0);
1891249bed82SDemi Marie Obenour
1892249bed82SDemi Marie Obenour if (copy_from_user(kernel_params->version, user->version, sizeof(kernel_params->version)))
18931da177e4SLinus Torvalds return -EFAULT;
18941da177e4SLinus Torvalds
1895249bed82SDemi Marie Obenour if ((kernel_params->version[0] != DM_VERSION_MAJOR) ||
1896249bed82SDemi Marie Obenour (kernel_params->version[1] > DM_VERSION_MINOR)) {
18972e84fecfSHeinz Mauelshagen DMERR("ioctl interface mismatch: kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)",
18981da177e4SLinus Torvalds DM_VERSION_MAJOR, DM_VERSION_MINOR,
18991da177e4SLinus Torvalds DM_VERSION_PATCHLEVEL,
1900249bed82SDemi Marie Obenour kernel_params->version[0],
1901249bed82SDemi Marie Obenour kernel_params->version[1],
1902249bed82SDemi Marie Obenour kernel_params->version[2],
1903249bed82SDemi Marie Obenour cmd);
19041da177e4SLinus Torvalds r = -EINVAL;
19051da177e4SLinus Torvalds }
19061da177e4SLinus Torvalds
19071da177e4SLinus Torvalds /*
19081da177e4SLinus Torvalds * Fill in the kernel version.
19091da177e4SLinus Torvalds */
1910249bed82SDemi Marie Obenour kernel_params->version[0] = DM_VERSION_MAJOR;
1911249bed82SDemi Marie Obenour kernel_params->version[1] = DM_VERSION_MINOR;
1912249bed82SDemi Marie Obenour kernel_params->version[2] = DM_VERSION_PATCHLEVEL;
1913249bed82SDemi Marie Obenour if (copy_to_user(user->version, kernel_params->version, sizeof(kernel_params->version)))
19141da177e4SLinus Torvalds return -EFAULT;
19151da177e4SLinus Torvalds
19161da177e4SLinus Torvalds return r;
19171da177e4SLinus Torvalds }
19181da177e4SLinus Torvalds
1919028b39e3SBart Van Assche #define DM_PARAMS_MALLOC 0x0001 /* Params allocated with kvmalloc() */
19209c5091f2SMikulas Patocka #define DM_WIPE_BUFFER 0x0010 /* Wipe input buffer before returning from ioctl */
19219c5091f2SMikulas Patocka
free_params(struct dm_ioctl * param,size_t param_size,int param_flags)19229c5091f2SMikulas Patocka static void free_params(struct dm_ioctl *param, size_t param_size, int param_flags)
19239c5091f2SMikulas Patocka {
19249c5091f2SMikulas Patocka if (param_flags & DM_WIPE_BUFFER)
19259c5091f2SMikulas Patocka memset(param, 0, param_size);
19269c5091f2SMikulas Patocka
1927028b39e3SBart Van Assche if (param_flags & DM_PARAMS_MALLOC)
1928028b39e3SBart Van Assche kvfree(param);
19299c5091f2SMikulas Patocka }
19309c5091f2SMikulas Patocka
copy_params(struct dm_ioctl __user * user,struct dm_ioctl * param_kernel,int ioctl_flags,struct dm_ioctl ** param,int * param_flags)193102cde50bSMikulas Patocka static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kernel,
1932800a7340SWenwen Wang int ioctl_flags, struct dm_ioctl **param, int *param_flags)
19331da177e4SLinus Torvalds {
193402cde50bSMikulas Patocka struct dm_ioctl *dmi;
1935f8681205SMilan Broz int secure_data;
19366080758dSBart Van Assche const size_t minimum_data_size = offsetof(struct dm_ioctl, data);
19371da177e4SLinus Torvalds
1938249bed82SDemi Marie Obenour /* check_version() already copied version from userspace, avoid TOCTOU */
1939249bed82SDemi Marie Obenour if (copy_from_user((char *)param_kernel + sizeof(param_kernel->version),
1940249bed82SDemi Marie Obenour (char __user *)user + sizeof(param_kernel->version),
1941249bed82SDemi Marie Obenour minimum_data_size - sizeof(param_kernel->version)))
19421da177e4SLinus Torvalds return -EFAULT;
19431da177e4SLinus Torvalds
1944*438d1949SMikulas Patocka if (unlikely(param_kernel->data_size < minimum_data_size) ||
1945*438d1949SMikulas Patocka unlikely(param_kernel->data_size > DM_MAX_TARGETS * DM_MAX_TARGET_PARAMS)) {
1946dbdcc906SMikulas Patocka DMERR("Invalid data size in the ioctl structure: %u",
1947dbdcc906SMikulas Patocka param_kernel->data_size);
19481da177e4SLinus Torvalds return -EINVAL;
1949dbdcc906SMikulas Patocka }
19501da177e4SLinus Torvalds
195102cde50bSMikulas Patocka secure_data = param_kernel->flags & DM_SECURE_DATA_FLAG;
1952f8681205SMilan Broz
19539c5091f2SMikulas Patocka *param_flags = secure_data ? DM_WIPE_BUFFER : 0;
19549c5091f2SMikulas Patocka
195502cde50bSMikulas Patocka if (ioctl_flags & IOCTL_FLAGS_NO_PARAMS) {
195602cde50bSMikulas Patocka dmi = param_kernel;
195702cde50bSMikulas Patocka dmi->data_size = minimum_data_size;
195802cde50bSMikulas Patocka goto data_copied;
195902cde50bSMikulas Patocka }
196002cde50bSMikulas Patocka
19615023e5cfSMikulas Patocka /*
19628c1e2162SJunaid Shahid * Use __GFP_HIGH to avoid low memory issues when a device is
19638c1e2162SJunaid Shahid * suspended and the ioctl is needed to resume it.
19649c5091f2SMikulas Patocka * Use kmalloc() rather than vmalloc() when we can.
19655023e5cfSMikulas Patocka */
19669c5091f2SMikulas Patocka dmi = NULL;
1967e2c789caSMikulas Patocka dmi = kvmalloc(param_kernel->data_size, GFP_NOIO | __GFP_HIGH);
19689c5091f2SMikulas Patocka
1969f8681205SMilan Broz if (!dmi) {
197002cde50bSMikulas Patocka if (secure_data && clear_user(user, param_kernel->data_size))
1971f8681205SMilan Broz return -EFAULT;
19721da177e4SLinus Torvalds return -ENOMEM;
1973f8681205SMilan Broz }
19741da177e4SLinus Torvalds
1975028b39e3SBart Van Assche *param_flags |= DM_PARAMS_MALLOC;
1976028b39e3SBart Van Assche
1977800a7340SWenwen Wang /* Copy from param_kernel (which was already copied from user) */
1978800a7340SWenwen Wang memcpy(dmi, param_kernel, minimum_data_size);
19791da177e4SLinus Torvalds
1980800a7340SWenwen Wang if (copy_from_user(&dmi->data, (char __user *)user + minimum_data_size,
1981800a7340SWenwen Wang param_kernel->data_size - minimum_data_size))
1982800a7340SWenwen Wang goto bad;
198302cde50bSMikulas Patocka data_copied:
1984f8681205SMilan Broz /* Wipe the user buffer so we do not return it to userspace */
198502cde50bSMikulas Patocka if (secure_data && clear_user(user, param_kernel->data_size))
1986f8681205SMilan Broz goto bad;
1987f8681205SMilan Broz
19881da177e4SLinus Torvalds *param = dmi;
19891da177e4SLinus Torvalds return 0;
19906bb43b5dSMilan Broz
19916bb43b5dSMilan Broz bad:
199202cde50bSMikulas Patocka free_params(dmi, param_kernel->data_size, *param_flags);
19939c5091f2SMikulas Patocka
19946bb43b5dSMilan Broz return -EFAULT;
19951da177e4SLinus Torvalds }
19961da177e4SLinus Torvalds
validate_params(uint cmd,struct dm_ioctl * param)19971da177e4SLinus Torvalds static int validate_params(uint cmd, struct dm_ioctl *param)
19981da177e4SLinus Torvalds {
19991da177e4SLinus Torvalds /* Always clear this flag */
20001da177e4SLinus Torvalds param->flags &= ~DM_BUFFER_FULL_FLAG;
20013abf85b5SPeter Rajnoha param->flags &= ~DM_UEVENT_GENERATED_FLAG;
2002f8681205SMilan Broz param->flags &= ~DM_SECURE_DATA_FLAG;
2003a2606241SMikulas Patocka param->flags &= ~DM_DATA_OUT_FLAG;
20041da177e4SLinus Torvalds
20051da177e4SLinus Torvalds /* Ignores parameters */
20061da177e4SLinus Torvalds if (cmd == DM_REMOVE_ALL_CMD ||
20071da177e4SLinus Torvalds cmd == DM_LIST_DEVICES_CMD ||
20081da177e4SLinus Torvalds cmd == DM_LIST_VERSIONS_CMD)
20091da177e4SLinus Torvalds return 0;
20101da177e4SLinus Torvalds
2011e36215d8SMatthias Kaehlcke if (cmd == DM_DEV_CREATE_CMD) {
20121da177e4SLinus Torvalds if (!*param->name) {
201343e6c111SMikulas Patocka DMERR("name not supplied when creating device");
20141da177e4SLinus Torvalds return -EINVAL;
20151da177e4SLinus Torvalds }
2016e36215d8SMatthias Kaehlcke } else if (*param->uuid && *param->name) {
201743e6c111SMikulas Patocka DMERR("only supply one of name or uuid, cmd(%u)", cmd);
20181da177e4SLinus Torvalds return -EINVAL;
20191da177e4SLinus Torvalds }
20201da177e4SLinus Torvalds
20211da177e4SLinus Torvalds /* Ensure strings are terminated */
20221da177e4SLinus Torvalds param->name[DM_NAME_LEN - 1] = '\0';
20231da177e4SLinus Torvalds param->uuid[DM_UUID_LEN - 1] = '\0';
20241da177e4SLinus Torvalds
20251da177e4SLinus Torvalds return 0;
20261da177e4SLinus Torvalds }
20271da177e4SLinus Torvalds
ctl_ioctl(struct file * file,uint command,struct dm_ioctl __user * user)2028fc1841e1SMikulas Patocka static int ctl_ioctl(struct file *file, uint command, struct dm_ioctl __user *user)
20291da177e4SLinus Torvalds {
20301da177e4SLinus Torvalds int r = 0;
2031e2914cc2SMikulas Patocka int ioctl_flags;
20329c5091f2SMikulas Patocka int param_flags;
20331da177e4SLinus Torvalds unsigned int cmd;
20343f649ab7SKees Cook struct dm_ioctl *param;
20351da177e4SLinus Torvalds ioctl_fn fn = NULL;
20366bb43b5dSMilan Broz size_t input_param_size;
203702cde50bSMikulas Patocka struct dm_ioctl param_kernel;
20381da177e4SLinus Torvalds
20391da177e4SLinus Torvalds /* only root can play with this */
20401da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN))
20411da177e4SLinus Torvalds return -EACCES;
20421da177e4SLinus Torvalds
20431da177e4SLinus Torvalds if (_IOC_TYPE(command) != DM_IOCTL)
20441da177e4SLinus Torvalds return -ENOTTY;
20451da177e4SLinus Torvalds
20461da177e4SLinus Torvalds cmd = _IOC_NR(command);
20471da177e4SLinus Torvalds
20481da177e4SLinus Torvalds /*
20491da177e4SLinus Torvalds * Check the interface version passed in. This also
20501da177e4SLinus Torvalds * writes out the kernel's interface version.
20511da177e4SLinus Torvalds */
2052249bed82SDemi Marie Obenour r = check_version(cmd, user, ¶m_kernel);
20531da177e4SLinus Torvalds if (r)
20541da177e4SLinus Torvalds return r;
20551da177e4SLinus Torvalds
20561da177e4SLinus Torvalds /*
20571da177e4SLinus Torvalds * Nothing more to do for the version command.
20581da177e4SLinus Torvalds */
20591da177e4SLinus Torvalds if (cmd == DM_VERSION_CMD)
20601da177e4SLinus Torvalds return 0;
20611da177e4SLinus Torvalds
2062e2914cc2SMikulas Patocka fn = lookup_ioctl(cmd, &ioctl_flags);
20631da177e4SLinus Torvalds if (!fn) {
206443e6c111SMikulas Patocka DMERR("dm_ctl_ioctl: unknown command 0x%x", command);
20651da177e4SLinus Torvalds return -ENOTTY;
20661da177e4SLinus Torvalds }
20671da177e4SLinus Torvalds
20681da177e4SLinus Torvalds /*
20691da177e4SLinus Torvalds * Copy the parameters into kernel space.
20701da177e4SLinus Torvalds */
207102cde50bSMikulas Patocka r = copy_params(user, ¶m_kernel, ioctl_flags, ¶m, ¶m_flags);
20721da177e4SLinus Torvalds
2073dab6a429SAlasdair G Kergon if (r)
2074dab6a429SAlasdair G Kergon return r;
20751da177e4SLinus Torvalds
2076f8681205SMilan Broz input_param_size = param->data_size;
20771da177e4SLinus Torvalds r = validate_params(cmd, param);
20781da177e4SLinus Torvalds if (r)
20791da177e4SLinus Torvalds goto out;
20801da177e4SLinus Torvalds
20814617f564SAdrian Salido param->data_size = offsetof(struct dm_ioctl, data);
2082fc1841e1SMikulas Patocka r = fn(file, param, input_param_size);
20831da177e4SLinus Torvalds
2084e2914cc2SMikulas Patocka if (unlikely(param->flags & DM_BUFFER_FULL_FLAG) &&
2085e2914cc2SMikulas Patocka unlikely(ioctl_flags & IOCTL_FLAGS_NO_PARAMS))
2086e2914cc2SMikulas Patocka DMERR("ioctl %d tried to output some data but has IOCTL_FLAGS_NO_PARAMS set", cmd);
2087e2914cc2SMikulas Patocka
208862e08243SMikulas Patocka if (!r && ioctl_flags & IOCTL_FLAGS_ISSUE_GLOBAL_EVENT)
208962e08243SMikulas Patocka dm_issue_global_event();
209062e08243SMikulas Patocka
20911da177e4SLinus Torvalds /*
20921da177e4SLinus Torvalds * Copy the results back to userland.
20931da177e4SLinus Torvalds */
20941da177e4SLinus Torvalds if (!r && copy_to_user(user, param, param->data_size))
20951da177e4SLinus Torvalds r = -EFAULT;
20961da177e4SLinus Torvalds
20971da177e4SLinus Torvalds out:
20989c5091f2SMikulas Patocka free_params(param, input_param_size, param_flags);
20991da177e4SLinus Torvalds return r;
21001da177e4SLinus Torvalds }
21011da177e4SLinus Torvalds
dm_ctl_ioctl(struct file * file,uint command,ulong u)210227238b2bSAlasdair G Kergon static long dm_ctl_ioctl(struct file *file, uint command, ulong u)
210327238b2bSAlasdair G Kergon {
2104fc1841e1SMikulas Patocka return (long)ctl_ioctl(file, command, (struct dm_ioctl __user *)u);
210527238b2bSAlasdair G Kergon }
210627238b2bSAlasdair G Kergon
210776c072b4SMilan Broz #ifdef CONFIG_COMPAT
dm_compat_ctl_ioctl(struct file * file,uint command,ulong u)210876c072b4SMilan Broz static long dm_compat_ctl_ioctl(struct file *file, uint command, ulong u)
210976c072b4SMilan Broz {
211076c072b4SMilan Broz return (long)dm_ctl_ioctl(file, command, (ulong) compat_ptr(u));
211176c072b4SMilan Broz }
211276c072b4SMilan Broz #else
211376c072b4SMilan Broz #define dm_compat_ctl_ioctl NULL
211476c072b4SMilan Broz #endif
211576c072b4SMilan Broz
dm_open(struct inode * inode,struct file * filp)211693e6442cSMikulas Patocka static int dm_open(struct inode *inode, struct file *filp)
211793e6442cSMikulas Patocka {
211893e6442cSMikulas Patocka int r;
211993e6442cSMikulas Patocka struct dm_file *priv;
212093e6442cSMikulas Patocka
212193e6442cSMikulas Patocka r = nonseekable_open(inode, filp);
212293e6442cSMikulas Patocka if (unlikely(r))
212393e6442cSMikulas Patocka return r;
212493e6442cSMikulas Patocka
212593e6442cSMikulas Patocka priv = filp->private_data = kmalloc(sizeof(struct dm_file), GFP_KERNEL);
212693e6442cSMikulas Patocka if (!priv)
212793e6442cSMikulas Patocka return -ENOMEM;
212893e6442cSMikulas Patocka
212993e6442cSMikulas Patocka priv->global_event_nr = atomic_read(&dm_global_event_nr);
213093e6442cSMikulas Patocka
213193e6442cSMikulas Patocka return 0;
213293e6442cSMikulas Patocka }
213393e6442cSMikulas Patocka
dm_release(struct inode * inode,struct file * filp)213493e6442cSMikulas Patocka static int dm_release(struct inode *inode, struct file *filp)
213593e6442cSMikulas Patocka {
213693e6442cSMikulas Patocka kfree(filp->private_data);
213793e6442cSMikulas Patocka return 0;
213893e6442cSMikulas Patocka }
213993e6442cSMikulas Patocka
dm_poll(struct file * filp,poll_table * wait)2140afc9a42bSAl Viro static __poll_t dm_poll(struct file *filp, poll_table *wait)
214193e6442cSMikulas Patocka {
214293e6442cSMikulas Patocka struct dm_file *priv = filp->private_data;
2143afc9a42bSAl Viro __poll_t mask = 0;
214493e6442cSMikulas Patocka
214593e6442cSMikulas Patocka poll_wait(filp, &dm_global_eventq, wait);
214693e6442cSMikulas Patocka
214793e6442cSMikulas Patocka if ((int)(atomic_read(&dm_global_event_nr) - priv->global_event_nr) > 0)
2148a9a08845SLinus Torvalds mask |= EPOLLIN;
214993e6442cSMikulas Patocka
215093e6442cSMikulas Patocka return mask;
215193e6442cSMikulas Patocka }
215293e6442cSMikulas Patocka
2153fa027c2aSArjan van de Ven static const struct file_operations _ctl_fops = {
215493e6442cSMikulas Patocka .open = dm_open,
215593e6442cSMikulas Patocka .release = dm_release,
215693e6442cSMikulas Patocka .poll = dm_poll,
215727238b2bSAlasdair G Kergon .unlocked_ioctl = dm_ctl_ioctl,
215876c072b4SMilan Broz .compat_ioctl = dm_compat_ctl_ioctl,
21591da177e4SLinus Torvalds .owner = THIS_MODULE,
21606038f373SArnd Bergmann .llseek = noop_llseek,
21611da177e4SLinus Torvalds };
21621da177e4SLinus Torvalds
21631da177e4SLinus Torvalds static struct miscdevice _dm_misc = {
21647e507eb6SPeter Rajnoha .minor = MAPPER_CTRL_MINOR,
21651da177e4SLinus Torvalds .name = DM_NAME,
21667e507eb6SPeter Rajnoha .nodename = DM_DIR "/" DM_CONTROL_NODE,
21671da177e4SLinus Torvalds .fops = &_ctl_fops
21681da177e4SLinus Torvalds };
21691da177e4SLinus Torvalds
21707e507eb6SPeter Rajnoha MODULE_ALIAS_MISCDEV(MAPPER_CTRL_MINOR);
21717e507eb6SPeter Rajnoha MODULE_ALIAS("devname:" DM_DIR "/" DM_CONTROL_NODE);
21727e507eb6SPeter Rajnoha
21731da177e4SLinus Torvalds /*
21741da177e4SLinus Torvalds * Create misc character device and link to DM_DIR/control.
21751da177e4SLinus Torvalds */
dm_interface_init(void)21761da177e4SLinus Torvalds int __init dm_interface_init(void)
21771da177e4SLinus Torvalds {
21781da177e4SLinus Torvalds int r;
21791da177e4SLinus Torvalds
21801da177e4SLinus Torvalds r = misc_register(&_dm_misc);
21811da177e4SLinus Torvalds if (r) {
21821da177e4SLinus Torvalds DMERR("misc_register failed for control device");
21831da177e4SLinus Torvalds return r;
21841da177e4SLinus Torvalds }
21851da177e4SLinus Torvalds
21861da177e4SLinus Torvalds DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR,
21871da177e4SLinus Torvalds DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA,
21881da177e4SLinus Torvalds DM_DRIVER_EMAIL);
21891da177e4SLinus Torvalds return 0;
21901da177e4SLinus Torvalds }
21911da177e4SLinus Torvalds
dm_interface_exit(void)21921da177e4SLinus Torvalds void dm_interface_exit(void)
21931da177e4SLinus Torvalds {
2194f368ed60SGreg Kroah-Hartman misc_deregister(&_dm_misc);
21951da177e4SLinus Torvalds dm_hash_exit();
21961da177e4SLinus Torvalds }
219796a1f7dbSMike Anderson
219896a1f7dbSMike Anderson /**
219996a1f7dbSMike Anderson * dm_copy_name_and_uuid - Copy mapped device name & uuid into supplied buffers
220096a1f7dbSMike Anderson * @md: Pointer to mapped_device
220196a1f7dbSMike Anderson * @name: Buffer (size DM_NAME_LEN) for name
220296a1f7dbSMike Anderson * @uuid: Buffer (size DM_UUID_LEN) for uuid or empty string if uuid not defined
220396a1f7dbSMike Anderson */
dm_copy_name_and_uuid(struct mapped_device * md,char * name,char * uuid)220496a1f7dbSMike Anderson int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid)
220596a1f7dbSMike Anderson {
220696a1f7dbSMike Anderson int r = 0;
220796a1f7dbSMike Anderson struct hash_cell *hc;
220896a1f7dbSMike Anderson
220996a1f7dbSMike Anderson if (!md)
221096a1f7dbSMike Anderson return -ENXIO;
221196a1f7dbSMike Anderson
22126076905bSMikulas Patocka mutex_lock(&dm_hash_cells_mutex);
221396a1f7dbSMike Anderson hc = dm_get_mdptr(md);
2214a2f998a7SHou Tao if (!hc) {
221596a1f7dbSMike Anderson r = -ENXIO;
221696a1f7dbSMike Anderson goto out;
221796a1f7dbSMike Anderson }
221896a1f7dbSMike Anderson
221923d39f63SMilan Broz if (name)
222096a1f7dbSMike Anderson strcpy(name, hc->name);
222123d39f63SMilan Broz if (uuid)
222296a1f7dbSMike Anderson strcpy(uuid, hc->uuid ? : "");
222396a1f7dbSMike Anderson
222496a1f7dbSMike Anderson out:
22256076905bSMikulas Patocka mutex_unlock(&dm_hash_cells_mutex);
222696a1f7dbSMike Anderson
222796a1f7dbSMike Anderson return r;
222896a1f7dbSMike Anderson }
222961931c0eSMike Snitzer EXPORT_SYMBOL_GPL(dm_copy_name_and_uuid);
22306bbc923dSHelen Koike
22316bbc923dSHelen Koike /**
22326bbc923dSHelen Koike * dm_early_create - create a mapped device in early boot.
22336bbc923dSHelen Koike *
22346bbc923dSHelen Koike * @dmi: Contains main information of the device mapping to be created.
22356bbc923dSHelen Koike * @spec_array: array of pointers to struct dm_target_spec. Describes the
22366bbc923dSHelen Koike * mapping table of the device.
22376bbc923dSHelen Koike * @target_params_array: array of strings with the parameters to a specific
22386bbc923dSHelen Koike * target.
22396bbc923dSHelen Koike *
22406bbc923dSHelen Koike * Instead of having the struct dm_target_spec and the parameters for every
22416bbc923dSHelen Koike * target embedded at the end of struct dm_ioctl (as performed in a normal
22426bbc923dSHelen Koike * ioctl), pass them as arguments, so the caller doesn't need to serialize them.
22436bbc923dSHelen Koike * The size of the spec_array and target_params_array is given by
22446bbc923dSHelen Koike * @dmi->target_count.
22456bbc923dSHelen Koike * This function is supposed to be called in early boot, so locking mechanisms
22466bbc923dSHelen Koike * to protect against concurrent loads are not required.
22476bbc923dSHelen Koike */
dm_early_create(struct dm_ioctl * dmi,struct dm_target_spec ** spec_array,char ** target_params_array)22486bbc923dSHelen Koike int __init dm_early_create(struct dm_ioctl *dmi,
22496bbc923dSHelen Koike struct dm_target_spec **spec_array,
22506bbc923dSHelen Koike char **target_params_array)
22516bbc923dSHelen Koike {
22526bbc923dSHelen Koike int r, m = DM_ANY_MINOR;
22536bbc923dSHelen Koike struct dm_table *t, *old_map;
22546bbc923dSHelen Koike struct mapped_device *md;
22556bbc923dSHelen Koike unsigned int i;
22566bbc923dSHelen Koike
22576bbc923dSHelen Koike if (!dmi->target_count)
22586bbc923dSHelen Koike return -EINVAL;
22596bbc923dSHelen Koike
22606bbc923dSHelen Koike r = check_name(dmi->name);
22616bbc923dSHelen Koike if (r)
22626bbc923dSHelen Koike return r;
22636bbc923dSHelen Koike
22646bbc923dSHelen Koike if (dmi->flags & DM_PERSISTENT_DEV_FLAG)
22656bbc923dSHelen Koike m = MINOR(huge_decode_dev(dmi->dev));
22666bbc923dSHelen Koike
22676bbc923dSHelen Koike /* alloc dm device */
22686bbc923dSHelen Koike r = dm_create(m, &md);
22696bbc923dSHelen Koike if (r)
22706bbc923dSHelen Koike return r;
22716bbc923dSHelen Koike
22726bbc923dSHelen Koike /* hash insert */
22736bbc923dSHelen Koike r = dm_hash_insert(dmi->name, *dmi->uuid ? dmi->uuid : NULL, md);
22746bbc923dSHelen Koike if (r)
22756bbc923dSHelen Koike goto err_destroy_dm;
22766bbc923dSHelen Koike
22776bbc923dSHelen Koike /* alloc table */
22786bbc923dSHelen Koike r = dm_table_create(&t, get_mode(dmi), dmi->target_count, md);
22796bbc923dSHelen Koike if (r)
22800f41fcf7SHelen Koike goto err_hash_remove;
22816bbc923dSHelen Koike
22826bbc923dSHelen Koike /* add targets */
22836bbc923dSHelen Koike for (i = 0; i < dmi->target_count; i++) {
22846bbc923dSHelen Koike r = dm_table_add_target(t, spec_array[i]->target_type,
22856bbc923dSHelen Koike (sector_t) spec_array[i]->sector_start,
22866bbc923dSHelen Koike (sector_t) spec_array[i]->length,
22876bbc923dSHelen Koike target_params_array[i]);
22886bbc923dSHelen Koike if (r) {
228943e6c111SMikulas Patocka DMERR("error adding target to table");
22906bbc923dSHelen Koike goto err_destroy_table;
22916bbc923dSHelen Koike }
22926bbc923dSHelen Koike }
22936bbc923dSHelen Koike
22946bbc923dSHelen Koike /* finish table */
22956bbc923dSHelen Koike r = dm_table_complete(t);
22966bbc923dSHelen Koike if (r)
22976bbc923dSHelen Koike goto err_destroy_table;
22986bbc923dSHelen Koike
22996bbc923dSHelen Koike /* setup md->queue to reflect md's type (may block) */
23006bbc923dSHelen Koike r = dm_setup_md_queue(md, t);
23016bbc923dSHelen Koike if (r) {
230243e6c111SMikulas Patocka DMERR("unable to set up device queue for new table.");
23036bbc923dSHelen Koike goto err_destroy_table;
23046bbc923dSHelen Koike }
23056bbc923dSHelen Koike
23066bbc923dSHelen Koike /* Set new map */
23076bbc923dSHelen Koike dm_suspend(md, 0);
23086bbc923dSHelen Koike old_map = dm_swap_table(md, t);
23096bbc923dSHelen Koike if (IS_ERR(old_map)) {
23106bbc923dSHelen Koike r = PTR_ERR(old_map);
23116bbc923dSHelen Koike goto err_destroy_table;
23126bbc923dSHelen Koike }
23136bbc923dSHelen Koike set_disk_ro(dm_disk(md), !!(dmi->flags & DM_READONLY_FLAG));
23146bbc923dSHelen Koike
23156bbc923dSHelen Koike /* resume device */
23166bbc923dSHelen Koike r = dm_resume(md);
23176bbc923dSHelen Koike if (r)
23186bbc923dSHelen Koike goto err_destroy_table;
23196bbc923dSHelen Koike
23206bbc923dSHelen Koike DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name);
23216bbc923dSHelen Koike dm_put(md);
23226bbc923dSHelen Koike return 0;
23236bbc923dSHelen Koike
23246bbc923dSHelen Koike err_destroy_table:
23256bbc923dSHelen Koike dm_table_destroy(t);
23260f41fcf7SHelen Koike err_hash_remove:
232769868bebSMike Snitzer down_write(&_hash_lock);
23280f41fcf7SHelen Koike (void) __hash_remove(__get_name_cell(dmi->name));
232969868bebSMike Snitzer up_write(&_hash_lock);
23300f41fcf7SHelen Koike /* release reference from __get_name_cell */
23310f41fcf7SHelen Koike dm_put(md);
23326bbc923dSHelen Koike err_destroy_dm:
23336bbc923dSHelen Koike dm_put(md);
23346bbc923dSHelen Koike dm_destroy(md);
23356bbc923dSHelen Koike return r;
23366bbc923dSHelen Koike }
2337