blob: 6a6d475f8e801784b9ac5e7ed7e3e4677e2c3444 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
Alasdair G Kergon2b06cff2006-06-26 00:27:32 -07003 * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved.
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
5 * This file is released under the GPL.
6 */
7
8#include "dm.h"
9
10#include <linux/module.h>
11#include <linux/vmalloc.h>
12#include <linux/miscdevice.h>
13#include <linux/init.h>
14#include <linux/wait.h>
15#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/dm-ioctl.h>
Darrick J. Wong3ac51e72006-03-27 01:17:54 -080017#include <linux/hdreg.h>
Milan Broz76c072b2008-02-08 02:09:56 +000018#include <linux/compat.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019
20#include <asm/uaccess.h>
21
Alasdair G Kergon72d94862006-06-26 00:27:35 -070022#define DM_MSG_PREFIX "ioctl"
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#define DM_DRIVER_EMAIL "dm-devel@redhat.com"
24
25/*-----------------------------------------------------------------
26 * The ioctl interface needs to be able to look up devices by
27 * name or uuid.
28 *---------------------------------------------------------------*/
29struct hash_cell {
30 struct list_head name_list;
31 struct list_head uuid_list;
32
33 char *name;
34 char *uuid;
35 struct mapped_device *md;
36 struct dm_table *new_map;
37};
38
39struct vers_iter {
40 size_t param_size;
41 struct dm_target_versions *vers, *old_vers;
42 char *end;
43 uint32_t flags;
44};
45
46
47#define NUM_BUCKETS 64
48#define MASK_BUCKETS (NUM_BUCKETS - 1)
49static struct list_head _name_buckets[NUM_BUCKETS];
50static struct list_head _uuid_buckets[NUM_BUCKETS];
51
Alasdair G Kergon5c6bd752006-06-26 00:27:34 -070052static void dm_hash_remove_all(int keep_open_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
54/*
55 * Guards access to both hash tables.
56 */
57static DECLARE_RWSEM(_hash_lock);
58
Mikulas Patocka60769052009-12-10 23:51:52 +000059/*
60 * Protects use of mdptr to obtain hash cell name and uuid from mapped device.
61 */
62static DEFINE_MUTEX(dm_hash_cells_mutex);
63
Linus Torvalds1da177e2005-04-16 15:20:36 -070064static void init_buckets(struct list_head *buckets)
65{
66 unsigned int i;
67
68 for (i = 0; i < NUM_BUCKETS; i++)
69 INIT_LIST_HEAD(buckets + i);
70}
71
72static int dm_hash_init(void)
73{
74 init_buckets(_name_buckets);
75 init_buckets(_uuid_buckets);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 return 0;
77}
78
79static void dm_hash_exit(void)
80{
Alasdair G Kergon5c6bd752006-06-26 00:27:34 -070081 dm_hash_remove_all(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070082}
83
84/*-----------------------------------------------------------------
85 * Hash function:
86 * We're not really concerned with the str hash function being
87 * fast since it's only used by the ioctl interface.
88 *---------------------------------------------------------------*/
89static unsigned int hash_str(const char *str)
90{
91 const unsigned int hash_mult = 2654435387U;
92 unsigned int h = 0;
93
94 while (*str)
95 h = (h + (unsigned int) *str++) * hash_mult;
96
97 return h & MASK_BUCKETS;
98}
99
100/*-----------------------------------------------------------------
101 * Code for looking up a device by name
102 *---------------------------------------------------------------*/
103static struct hash_cell *__get_name_cell(const char *str)
104{
105 struct hash_cell *hc;
106 unsigned int h = hash_str(str);
107
108 list_for_each_entry (hc, _name_buckets + h, name_list)
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700109 if (!strcmp(hc->name, str)) {
110 dm_get(hc->md);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 return hc;
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700112 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
114 return NULL;
115}
116
117static struct hash_cell *__get_uuid_cell(const char *str)
118{
119 struct hash_cell *hc;
120 unsigned int h = hash_str(str);
121
122 list_for_each_entry (hc, _uuid_buckets + h, uuid_list)
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700123 if (!strcmp(hc->uuid, str)) {
124 dm_get(hc->md);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 return hc;
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700126 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
128 return NULL;
129}
130
131/*-----------------------------------------------------------------
132 * Inserting, removing and renaming a device.
133 *---------------------------------------------------------------*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134static struct hash_cell *alloc_cell(const char *name, const char *uuid,
135 struct mapped_device *md)
136{
137 struct hash_cell *hc;
138
139 hc = kmalloc(sizeof(*hc), GFP_KERNEL);
140 if (!hc)
141 return NULL;
142
Paulo Marques543537b2005-06-23 00:09:02 -0700143 hc->name = kstrdup(name, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 if (!hc->name) {
145 kfree(hc);
146 return NULL;
147 }
148
149 if (!uuid)
150 hc->uuid = NULL;
151
152 else {
Paulo Marques543537b2005-06-23 00:09:02 -0700153 hc->uuid = kstrdup(uuid, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 if (!hc->uuid) {
155 kfree(hc->name);
156 kfree(hc);
157 return NULL;
158 }
159 }
160
161 INIT_LIST_HEAD(&hc->name_list);
162 INIT_LIST_HEAD(&hc->uuid_list);
163 hc->md = md;
164 hc->new_map = NULL;
165 return hc;
166}
167
168static void free_cell(struct hash_cell *hc)
169{
170 if (hc) {
171 kfree(hc->name);
172 kfree(hc->uuid);
173 kfree(hc);
174 }
175}
176
177/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 * The kdev_t and uuid of a device can never change once it is
179 * initially inserted.
180 */
181static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
182{
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700183 struct hash_cell *cell, *hc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
185 /*
186 * Allocate the new cells.
187 */
188 cell = alloc_cell(name, uuid, md);
189 if (!cell)
190 return -ENOMEM;
191
192 /*
193 * Insert the cell into both hash tables.
194 */
195 down_write(&_hash_lock);
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700196 hc = __get_name_cell(name);
197 if (hc) {
198 dm_put(hc->md);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 goto bad;
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700200 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201
202 list_add(&cell->name_list, _name_buckets + hash_str(name));
203
204 if (uuid) {
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700205 hc = __get_uuid_cell(uuid);
206 if (hc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 list_del(&cell->name_list);
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700208 dm_put(hc->md);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 goto bad;
210 }
211 list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));
212 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 dm_get(md);
Mikulas Patocka60769052009-12-10 23:51:52 +0000214 mutex_lock(&dm_hash_cells_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 dm_set_mdptr(md, cell);
Mikulas Patocka60769052009-12-10 23:51:52 +0000216 mutex_unlock(&dm_hash_cells_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 up_write(&_hash_lock);
218
219 return 0;
220
221 bad:
222 up_write(&_hash_lock);
223 free_cell(cell);
224 return -EBUSY;
225}
226
227static void __hash_remove(struct hash_cell *hc)
228{
goggin, edward269fd2a2005-09-27 21:45:44 -0700229 struct dm_table *table;
230
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 /* remove from the dev hash */
232 list_del(&hc->uuid_list);
233 list_del(&hc->name_list);
Mikulas Patocka60769052009-12-10 23:51:52 +0000234 mutex_lock(&dm_hash_cells_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 dm_set_mdptr(hc->md, NULL);
Mikulas Patocka60769052009-12-10 23:51:52 +0000236 mutex_unlock(&dm_hash_cells_mutex);
goggin, edward269fd2a2005-09-27 21:45:44 -0700237
Alasdair G Kergon7c666412009-12-10 23:52:19 +0000238 table = dm_get_live_table(hc->md);
goggin, edward269fd2a2005-09-27 21:45:44 -0700239 if (table) {
240 dm_table_event(table);
241 dm_table_put(table);
242 }
243
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 if (hc->new_map)
Mikulas Patockad5816872009-01-06 03:05:10 +0000245 dm_table_destroy(hc->new_map);
Mike Anderson1134e5a2006-03-27 01:17:54 -0800246 dm_put(hc->md);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 free_cell(hc);
248}
249
Alasdair G Kergon5c6bd752006-06-26 00:27:34 -0700250static void dm_hash_remove_all(int keep_open_devices)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251{
Kiyoshi Ueda98f33282010-08-12 04:13:55 +0100252 int i, dev_skipped;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 struct hash_cell *hc;
Kiyoshi Ueda98f33282010-08-12 04:13:55 +0100254 struct mapped_device *md;
255
256retry:
257 dev_skipped = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
259 down_write(&_hash_lock);
Alasdair G Kergon5c6bd752006-06-26 00:27:34 -0700260
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 for (i = 0; i < NUM_BUCKETS; i++) {
Kiyoshi Ueda98f33282010-08-12 04:13:55 +0100262 list_for_each_entry(hc, _name_buckets + i, name_list) {
263 md = hc->md;
264 dm_get(md);
Alasdair G Kergon5c6bd752006-06-26 00:27:34 -0700265
Kiyoshi Ueda98f33282010-08-12 04:13:55 +0100266 if (keep_open_devices && dm_lock_for_deletion(md)) {
267 dm_put(md);
Alasdair G Kergon5c6bd752006-06-26 00:27:34 -0700268 dev_skipped++;
269 continue;
270 }
Kiyoshi Ueda98f33282010-08-12 04:13:55 +0100271
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 __hash_remove(hc);
Kiyoshi Ueda98f33282010-08-12 04:13:55 +0100273
274 up_write(&_hash_lock);
275
276 dm_put(md);
277
278 /*
279 * Some mapped devices may be using other mapped
280 * devices, so repeat until we make no further
281 * progress. If a new mapped device is created
282 * here it will also get removed.
283 */
284 goto retry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 }
286 }
Alasdair G Kergon5c6bd752006-06-26 00:27:34 -0700287
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 up_write(&_hash_lock);
Kiyoshi Ueda98f33282010-08-12 04:13:55 +0100289
290 if (dev_skipped)
291 DMWARN("remove_all left %d open device(s)", dev_skipped);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292}
293
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100294static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
295 const char *new)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
297 char *new_name, *old_name;
298 struct hash_cell *hc;
goggin, edward81f17772006-01-06 00:20:01 -0800299 struct dm_table *table;
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100300 struct mapped_device *md;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
302 /*
303 * duplicate new.
304 */
Paulo Marques543537b2005-06-23 00:09:02 -0700305 new_name = kstrdup(new, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 if (!new_name)
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100307 return ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309 down_write(&_hash_lock);
310
311 /*
312 * Is new free ?
313 */
314 hc = __get_name_cell(new);
315 if (hc) {
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100316 DMWARN("asked to rename to an already-existing name %s -> %s",
317 param->name, new);
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700318 dm_put(hc->md);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 up_write(&_hash_lock);
320 kfree(new_name);
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100321 return ERR_PTR(-EBUSY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 }
323
324 /*
325 * Is there such a device as 'old' ?
326 */
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100327 hc = __get_name_cell(param->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 if (!hc) {
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100329 DMWARN("asked to rename a non-existent device %s -> %s",
330 param->name, new);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 up_write(&_hash_lock);
332 kfree(new_name);
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100333 return ERR_PTR(-ENXIO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 }
335
336 /*
337 * rename and move the name cell.
338 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 list_del(&hc->name_list);
340 old_name = hc->name;
Mikulas Patocka60769052009-12-10 23:51:52 +0000341 mutex_lock(&dm_hash_cells_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 hc->name = new_name;
Mikulas Patocka60769052009-12-10 23:51:52 +0000343 mutex_unlock(&dm_hash_cells_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 list_add(&hc->name_list, _name_buckets + hash_str(new_name));
345
goggin, edward81f17772006-01-06 00:20:01 -0800346 /*
347 * Wake up any dm event waiters.
348 */
Alasdair G Kergon7c666412009-12-10 23:52:19 +0000349 table = dm_get_live_table(hc->md);
goggin, edward81f17772006-01-06 00:20:01 -0800350 if (table) {
351 dm_table_event(table);
352 dm_table_put(table);
353 }
354
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100355 if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr))
356 param->flags |= DM_UEVENT_GENERATED_FLAG;
Alasdair G Kergon69267a32007-12-13 14:15:57 +0000357
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100358 md = hc->md;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 up_write(&_hash_lock);
360 kfree(old_name);
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100361
362 return md;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363}
364
365/*-----------------------------------------------------------------
366 * Implementation of the ioctl commands
367 *---------------------------------------------------------------*/
368/*
369 * All the ioctl commands get dispatched to functions with this
370 * prototype.
371 */
372typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size);
373
374static int remove_all(struct dm_ioctl *param, size_t param_size)
375{
Alasdair G Kergon5c6bd752006-06-26 00:27:34 -0700376 dm_hash_remove_all(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 param->data_size = 0;
378 return 0;
379}
380
381/*
382 * Round up the ptr to an 8-byte boundary.
383 */
384#define ALIGN_MASK 7
385static inline void *align_ptr(void *ptr)
386{
387 return (void *) (((size_t) (ptr + ALIGN_MASK)) & ~ALIGN_MASK);
388}
389
390/*
391 * Retrieves the data payload buffer from an already allocated
392 * struct dm_ioctl.
393 */
394static void *get_result_buffer(struct dm_ioctl *param, size_t param_size,
395 size_t *len)
396{
397 param->data_start = align_ptr(param + 1) - (void *) param;
398
399 if (param->data_start < param_size)
400 *len = param_size - param->data_start;
401 else
402 *len = 0;
403
404 return ((void *) param) + param->data_start;
405}
406
407static int list_devices(struct dm_ioctl *param, size_t param_size)
408{
409 unsigned int i;
410 struct hash_cell *hc;
411 size_t len, needed = 0;
412 struct gendisk *disk;
413 struct dm_name_list *nl, *old_nl = NULL;
414
415 down_write(&_hash_lock);
416
417 /*
418 * Loop through all the devices working out how much
419 * space we need.
420 */
421 for (i = 0; i < NUM_BUCKETS; i++) {
422 list_for_each_entry (hc, _name_buckets + i, name_list) {
423 needed += sizeof(struct dm_name_list);
424 needed += strlen(hc->name) + 1;
425 needed += ALIGN_MASK;
426 }
427 }
428
429 /*
430 * Grab our output buffer.
431 */
432 nl = get_result_buffer(param, param_size, &len);
433 if (len < needed) {
434 param->flags |= DM_BUFFER_FULL_FLAG;
435 goto out;
436 }
437 param->data_size = param->data_start + needed;
438
439 nl->dev = 0; /* Flags no data */
440
441 /*
442 * Now loop through filling out the names.
443 */
444 for (i = 0; i < NUM_BUCKETS; i++) {
445 list_for_each_entry (hc, _name_buckets + i, name_list) {
446 if (old_nl)
447 old_nl->next = (uint32_t) ((void *) nl -
448 (void *) old_nl);
449 disk = dm_disk(hc->md);
Tejun Heof331c022008-09-03 09:01:48 +0200450 nl->dev = huge_encode_dev(disk_devt(disk));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 nl->next = 0;
452 strcpy(nl->name, hc->name);
453
454 old_nl = nl;
455 nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1);
456 }
457 }
458
459 out:
460 up_write(&_hash_lock);
461 return 0;
462}
463
464static void list_version_get_needed(struct target_type *tt, void *needed_param)
465{
466 size_t *needed = needed_param;
467
Alasdair G Kergonc4cc6632005-11-21 21:32:33 -0800468 *needed += sizeof(struct dm_target_versions);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 *needed += strlen(tt->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 *needed += ALIGN_MASK;
471}
472
473static void list_version_get_info(struct target_type *tt, void *param)
474{
475 struct vers_iter *info = param;
476
477 /* Check space - it might have changed since the first iteration */
478 if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 >
479 info->end) {
480
481 info->flags = DM_BUFFER_FULL_FLAG;
482 return;
483 }
484
485 if (info->old_vers)
486 info->old_vers->next = (uint32_t) ((void *)info->vers -
487 (void *)info->old_vers);
488 info->vers->version[0] = tt->version[0];
489 info->vers->version[1] = tt->version[1];
490 info->vers->version[2] = tt->version[2];
491 info->vers->next = 0;
492 strcpy(info->vers->name, tt->name);
493
494 info->old_vers = info->vers;
495 info->vers = align_ptr(((void *) ++info->vers) + strlen(tt->name) + 1);
496}
497
498static int list_versions(struct dm_ioctl *param, size_t param_size)
499{
500 size_t len, needed = 0;
501 struct dm_target_versions *vers;
502 struct vers_iter iter_info;
503
504 /*
505 * Loop through all the devices working out how much
506 * space we need.
507 */
508 dm_target_iterate(list_version_get_needed, &needed);
509
510 /*
511 * Grab our output buffer.
512 */
513 vers = get_result_buffer(param, param_size, &len);
514 if (len < needed) {
515 param->flags |= DM_BUFFER_FULL_FLAG;
516 goto out;
517 }
518 param->data_size = param->data_start + needed;
519
520 iter_info.param_size = param_size;
521 iter_info.old_vers = NULL;
522 iter_info.vers = vers;
523 iter_info.flags = 0;
524 iter_info.end = (char *)vers+len;
525
526 /*
527 * Now loop through filling out the names & versions.
528 */
529 dm_target_iterate(list_version_get_info, &iter_info);
530 param->flags |= iter_info.flags;
531
532 out:
533 return 0;
534}
535
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536static int check_name(const char *name)
537{
538 if (strchr(name, '/')) {
539 DMWARN("invalid device name");
540 return -EINVAL;
541 }
542
543 return 0;
544}
545
546/*
Mike Snitzer1d0f3ce2009-12-10 23:52:22 +0000547 * On successful return, the caller must not attempt to acquire
548 * _hash_lock without first calling dm_table_put, because dm_table_destroy
549 * waits for this dm_table_put and could be called under this lock.
550 */
551static struct dm_table *dm_get_inactive_table(struct mapped_device *md)
552{
553 struct hash_cell *hc;
554 struct dm_table *table = NULL;
555
556 down_read(&_hash_lock);
557 hc = dm_get_mdptr(md);
558 if (!hc || hc->md != md) {
559 DMWARN("device has been removed from the dev hash table.");
560 goto out;
561 }
562
563 table = hc->new_map;
564 if (table)
565 dm_table_get(table);
566
567out:
568 up_read(&_hash_lock);
569
570 return table;
571}
572
573static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md,
574 struct dm_ioctl *param)
575{
576 return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ?
577 dm_get_inactive_table(md) : dm_get_live_table(md);
578}
579
580/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 * Fills in a dm_ioctl structure, ready for sending back to
582 * userland.
583 */
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +0100584static void __dev_status(struct mapped_device *md, struct dm_ioctl *param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585{
586 struct gendisk *disk = dm_disk(md);
587 struct dm_table *table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
589 param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
590 DM_ACTIVE_PRESENT_FLAG);
591
Kiyoshi Ueda4f186f82009-12-10 23:52:26 +0000592 if (dm_suspended_md(md))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 param->flags |= DM_SUSPEND_FLAG;
594
Tejun Heof331c022008-09-03 09:01:48 +0200595 param->dev = huge_encode_dev(disk_devt(disk));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Alasdair G Kergon5c6bd752006-06-26 00:27:34 -0700597 /*
598 * Yes, this will be out of date by the time it gets back
599 * to userland, but it is still very useful for
600 * debugging.
601 */
602 param->open_count = dm_open_count(md);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 param->event_nr = dm_get_event_nr(md);
Mike Snitzer1d0f3ce2009-12-10 23:52:22 +0000605 param->target_count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
Alasdair G Kergon7c666412009-12-10 23:52:19 +0000607 table = dm_get_live_table(md);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 if (table) {
Mike Snitzer1d0f3ce2009-12-10 23:52:22 +0000609 if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) {
610 if (get_disk_ro(disk))
611 param->flags |= DM_READONLY_FLAG;
612 param->target_count = dm_table_get_num_targets(table);
613 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 dm_table_put(table);
Mike Snitzer1d0f3ce2009-12-10 23:52:22 +0000615
616 param->flags |= DM_ACTIVE_PRESENT_FLAG;
617 }
618
619 if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) {
620 table = dm_get_inactive_table(md);
621 if (table) {
622 if (!(dm_table_get_mode(table) & FMODE_WRITE))
623 param->flags |= DM_READONLY_FLAG;
624 param->target_count = dm_table_get_num_targets(table);
625 dm_table_put(table);
626 }
627 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628}
629
630static int dev_create(struct dm_ioctl *param, size_t param_size)
631{
Alasdair G Kergon2b06cff2006-06-26 00:27:32 -0700632 int r, m = DM_ANY_MINOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 struct mapped_device *md;
634
635 r = check_name(param->name);
636 if (r)
637 return r;
638
639 if (param->flags & DM_PERSISTENT_DEV_FLAG)
Alasdair G Kergon2b06cff2006-06-26 00:27:32 -0700640 m = MINOR(huge_decode_dev(param->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641
Alasdair G Kergon2b06cff2006-06-26 00:27:32 -0700642 r = dm_create(m, &md);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 if (r)
644 return r;
645
646 r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +0100647 if (r)
648 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
650 param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
651
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +0100652 __dev_status(md, param);
653
654out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 dm_put(md);
656
657 return r;
658}
659
660/*
661 * Always use UUID for lookups if it's present, otherwise use name or dev.
662 */
Arjan van de Ven858119e2006-01-14 13:20:43 -0800663static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664{
Alasdair G Kergon9ade92a2006-03-27 01:17:53 -0800665 struct mapped_device *md;
666 void *mdptr = NULL;
667
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 if (*param->uuid)
669 return __get_uuid_cell(param->uuid);
Alasdair G Kergon9ade92a2006-03-27 01:17:53 -0800670
671 if (*param->name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 return __get_name_cell(param->name);
Alasdair G Kergon9ade92a2006-03-27 01:17:53 -0800673
674 md = dm_get_md(huge_decode_dev(param->dev));
Alasdair G Kergonbfc5ecd2006-11-08 17:44:42 -0800675 if (!md)
676 goto out;
Alasdair G Kergon9ade92a2006-03-27 01:17:53 -0800677
Alasdair G Kergonbfc5ecd2006-11-08 17:44:42 -0800678 mdptr = dm_get_mdptr(md);
679 if (!mdptr)
680 dm_put(md);
681
682out:
Alasdair G Kergon9ade92a2006-03-27 01:17:53 -0800683 return mdptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684}
685
Arjan van de Ven858119e2006-01-14 13:20:43 -0800686static struct mapped_device *find_device(struct dm_ioctl *param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687{
688 struct hash_cell *hc;
689 struct mapped_device *md = NULL;
690
691 down_read(&_hash_lock);
692 hc = __find_device_hash_cell(param);
693 if (hc) {
694 md = hc->md;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
696 /*
697 * Sneakily write in both the name and the uuid
698 * while we have the cell.
699 */
Roel Kluina518b862009-12-10 23:52:07 +0000700 strlcpy(param->name, hc->name, sizeof(param->name));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 if (hc->uuid)
Roel Kluina518b862009-12-10 23:52:07 +0000702 strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 else
704 param->uuid[0] = '\0';
705
706 if (hc->new_map)
707 param->flags |= DM_INACTIVE_PRESENT_FLAG;
708 else
709 param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
710 }
711 up_read(&_hash_lock);
712
713 return md;
714}
715
716static int dev_remove(struct dm_ioctl *param, size_t param_size)
717{
718 struct hash_cell *hc;
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700719 struct mapped_device *md;
Alasdair G Kergon5c6bd752006-06-26 00:27:34 -0700720 int r;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
722 down_write(&_hash_lock);
723 hc = __find_device_hash_cell(param);
724
725 if (!hc) {
726 DMWARN("device doesn't appear to be in the dev hash table.");
727 up_write(&_hash_lock);
728 return -ENXIO;
729 }
730
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700731 md = hc->md;
732
Alasdair G Kergon5c6bd752006-06-26 00:27:34 -0700733 /*
734 * Ensure the device is not open and nothing further can open it.
735 */
736 r = dm_lock_for_deletion(md);
737 if (r) {
738 DMWARN("unable to remove open device %s", hc->name);
739 up_write(&_hash_lock);
740 dm_put(md);
741 return r;
742 }
743
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 __hash_remove(hc);
745 up_write(&_hash_lock);
Milan Broz60935eb2009-06-22 10:12:30 +0100746
Peter Rajnoha3abf85b2010-03-06 02:32:31 +0000747 if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr))
748 param->flags |= DM_UEVENT_GENERATED_FLAG;
Milan Broz60935eb2009-06-22 10:12:30 +0100749
Jeff Mahoney7ec75f22006-06-26 00:27:24 -0700750 dm_put(md);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 return 0;
752}
753
754/*
755 * Check a string doesn't overrun the chunk of
756 * memory we copied from userland.
757 */
758static int invalid_str(char *str, void *end)
759{
760 while ((void *) str < end)
761 if (!*str++)
762 return 0;
763
764 return -EINVAL;
765}
766
767static int dev_rename(struct dm_ioctl *param, size_t param_size)
768{
769 int r;
770 char *new_name = (char *) param + param->data_start;
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100771 struct mapped_device *md;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
Alasdair G Kergon27238b22008-02-08 02:09:53 +0000773 if (new_name < param->data ||
Milan Brozbc0fd672009-03-16 16:56:01 +0000774 invalid_str(new_name, (void *) param + param_size) ||
775 strlen(new_name) > DM_NAME_LEN - 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 DMWARN("Invalid new logical volume name supplied.");
777 return -EINVAL;
778 }
779
780 r = check_name(new_name);
781 if (r)
782 return r;
783
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100784 md = dm_hash_rename(param, new_name);
785 if (IS_ERR(md))
786 return PTR_ERR(md);
Peter Rajnoha3abf85b2010-03-06 02:32:31 +0000787
Peter Rajnoha856a6f12010-08-12 04:13:53 +0100788 __dev_status(md, param);
789 dm_put(md);
790
791 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792}
793
Darrick J. Wong3ac51e72006-03-27 01:17:54 -0800794static int dev_set_geometry(struct dm_ioctl *param, size_t param_size)
795{
796 int r = -EINVAL, x;
797 struct mapped_device *md;
798 struct hd_geometry geometry;
799 unsigned long indata[4];
800 char *geostr = (char *) param + param->data_start;
801
802 md = find_device(param);
803 if (!md)
804 return -ENXIO;
805
Alasdair G Kergon27238b22008-02-08 02:09:53 +0000806 if (geostr < param->data ||
Darrick J. Wong3ac51e72006-03-27 01:17:54 -0800807 invalid_str(geostr, (void *) param + param_size)) {
808 DMWARN("Invalid geometry supplied.");
809 goto out;
810 }
811
812 x = sscanf(geostr, "%lu %lu %lu %lu", indata,
813 indata + 1, indata + 2, indata + 3);
814
815 if (x != 4) {
816 DMWARN("Unable to interpret geometry settings.");
817 goto out;
818 }
819
820 if (indata[0] > 65535 || indata[1] > 255 ||
821 indata[2] > 255 || indata[3] > ULONG_MAX) {
822 DMWARN("Geometry exceeds range limits.");
823 goto out;
824 }
825
826 geometry.cylinders = indata[0];
827 geometry.heads = indata[1];
828 geometry.sectors = indata[2];
829 geometry.start = indata[3];
830
831 r = dm_set_geometry(md, &geometry);
Darrick J. Wong3ac51e72006-03-27 01:17:54 -0800832
833 param->data_size = 0;
834
835out:
836 dm_put(md);
837 return r;
838}
839
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840static int do_suspend(struct dm_ioctl *param)
841{
842 int r = 0;
Kiyoshi Uedaa3d77d32006-12-08 02:41:04 -0800843 unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 struct mapped_device *md;
845
846 md = find_device(param);
847 if (!md)
848 return -ENXIO;
849
Alasdair G Kergon6da487d2006-01-06 00:20:07 -0800850 if (param->flags & DM_SKIP_LOCKFS_FLAG)
Kiyoshi Uedaa3d77d32006-12-08 02:41:04 -0800851 suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
Kiyoshi Ueda81fdb092006-12-08 02:41:07 -0800852 if (param->flags & DM_NOFLUSH_FLAG)
853 suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
Alasdair G Kergon6da487d2006-01-06 00:20:07 -0800854
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +0100855 if (!dm_suspended_md(md)) {
Kiyoshi Uedaa3d77d32006-12-08 02:41:04 -0800856 r = dm_suspend(md, suspend_flags);
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +0100857 if (r)
858 goto out;
859 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +0100861 __dev_status(md, param);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +0100863out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 dm_put(md);
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +0100865
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 return r;
867}
868
869static int do_resume(struct dm_ioctl *param)
870{
871 int r = 0;
Kiyoshi Uedaa3d77d32006-12-08 02:41:04 -0800872 unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 struct hash_cell *hc;
874 struct mapped_device *md;
Alasdair G Kergon042d2a92009-12-10 23:52:24 +0000875 struct dm_table *new_map, *old_map = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876
877 down_write(&_hash_lock);
878
879 hc = __find_device_hash_cell(param);
880 if (!hc) {
881 DMWARN("device doesn't appear to be in the dev hash table.");
882 up_write(&_hash_lock);
883 return -ENXIO;
884 }
885
886 md = hc->md;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887
888 new_map = hc->new_map;
889 hc->new_map = NULL;
890 param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
891
892 up_write(&_hash_lock);
893
894 /* Do we need to load a new map ? */
895 if (new_map) {
896 /* Suspend if it isn't already suspended */
Alasdair G Kergon6da487d2006-01-06 00:20:07 -0800897 if (param->flags & DM_SKIP_LOCKFS_FLAG)
Kiyoshi Uedaa3d77d32006-12-08 02:41:04 -0800898 suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
Kiyoshi Ueda81fdb092006-12-08 02:41:07 -0800899 if (param->flags & DM_NOFLUSH_FLAG)
900 suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
Kiyoshi Ueda4f186f82009-12-10 23:52:26 +0000901 if (!dm_suspended_md(md))
Kiyoshi Uedaa3d77d32006-12-08 02:41:04 -0800902 dm_suspend(md, suspend_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
Alasdair G Kergon042d2a92009-12-10 23:52:24 +0000904 old_map = dm_swap_table(md, new_map);
905 if (IS_ERR(old_map)) {
Mikulas Patockad5816872009-01-06 03:05:10 +0000906 dm_table_destroy(new_map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 dm_put(md);
Alasdair G Kergon042d2a92009-12-10 23:52:24 +0000908 return PTR_ERR(old_map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 }
910
911 if (dm_table_get_mode(new_map) & FMODE_WRITE)
912 set_disk_ro(dm_disk(md), 0);
913 else
914 set_disk_ro(dm_disk(md), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 }
916
Mike Snitzer0f3649a92010-03-06 02:32:24 +0000917 if (dm_suspended_md(md)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 r = dm_resume(md);
Peter Rajnoha3abf85b2010-03-06 02:32:31 +0000919 if (!r && !dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr))
920 param->flags |= DM_UEVENT_GENERATED_FLAG;
Mike Snitzer0f3649a92010-03-06 02:32:24 +0000921 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
Alasdair G Kergon042d2a92009-12-10 23:52:24 +0000923 if (old_map)
924 dm_table_destroy(old_map);
Milan Broz60935eb2009-06-22 10:12:30 +0100925
Mike Snitzer0f3649a92010-03-06 02:32:24 +0000926 if (!r)
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +0100927 __dev_status(md, param);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
929 dm_put(md);
930 return r;
931}
932
933/*
934 * Set or unset the suspension state of a device.
935 * If the device already is in the requested state we just return its status.
936 */
937static int dev_suspend(struct dm_ioctl *param, size_t param_size)
938{
939 if (param->flags & DM_SUSPEND_FLAG)
940 return do_suspend(param);
941
942 return do_resume(param);
943}
944
945/*
946 * Copies device info back to user space, used by
947 * the create and info ioctls.
948 */
949static int dev_status(struct dm_ioctl *param, size_t param_size)
950{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 struct mapped_device *md;
952
953 md = find_device(param);
954 if (!md)
955 return -ENXIO;
956
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +0100957 __dev_status(md, param);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 dm_put(md);
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +0100959
960 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961}
962
963/*
964 * Build up the status struct for each target
965 */
966static void retrieve_status(struct dm_table *table,
967 struct dm_ioctl *param, size_t param_size)
968{
969 unsigned int i, num_targets;
970 struct dm_target_spec *spec;
971 char *outbuf, *outptr;
972 status_type_t type;
973 size_t remaining, len, used = 0;
974
975 outptr = outbuf = get_result_buffer(param, param_size, &len);
976
977 if (param->flags & DM_STATUS_TABLE_FLAG)
978 type = STATUSTYPE_TABLE;
979 else
980 type = STATUSTYPE_INFO;
981
982 /* Get all the target info */
983 num_targets = dm_table_get_num_targets(table);
984 for (i = 0; i < num_targets; i++) {
985 struct dm_target *ti = dm_table_get_target(table, i);
986
987 remaining = len - (outptr - outbuf);
988 if (remaining <= sizeof(struct dm_target_spec)) {
989 param->flags |= DM_BUFFER_FULL_FLAG;
990 break;
991 }
992
993 spec = (struct dm_target_spec *) outptr;
994
995 spec->status = 0;
996 spec->sector_start = ti->begin;
997 spec->length = ti->len;
998 strncpy(spec->target_type, ti->type->name,
999 sizeof(spec->target_type));
1000
1001 outptr += sizeof(struct dm_target_spec);
1002 remaining = len - (outptr - outbuf);
1003 if (remaining <= 0) {
1004 param->flags |= DM_BUFFER_FULL_FLAG;
1005 break;
1006 }
1007
1008 /* Get the status/table string from the target driver */
1009 if (ti->type->status) {
1010 if (ti->type->status(ti, type, outptr, remaining)) {
1011 param->flags |= DM_BUFFER_FULL_FLAG;
1012 break;
1013 }
1014 } else
1015 outptr[0] = '\0';
1016
1017 outptr += strlen(outptr) + 1;
1018 used = param->data_start + (outptr - outbuf);
1019
1020 outptr = align_ptr(outptr);
1021 spec->next = outptr - outbuf;
1022 }
1023
1024 if (used)
1025 param->data_size = used;
1026
1027 param->target_count = num_targets;
1028}
1029
1030/*
1031 * Wait for a device to report an event
1032 */
1033static int dev_wait(struct dm_ioctl *param, size_t param_size)
1034{
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +01001035 int r = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 struct mapped_device *md;
1037 struct dm_table *table;
1038
1039 md = find_device(param);
1040 if (!md)
1041 return -ENXIO;
1042
1043 /*
1044 * Wait for a notification event
1045 */
1046 if (dm_wait_event(md, param->event_nr)) {
1047 r = -ERESTARTSYS;
1048 goto out;
1049 }
1050
1051 /*
1052 * The userland program is going to want to know what
1053 * changed to trigger the event, so we may as well tell
1054 * him and save an ioctl.
1055 */
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +01001056 __dev_status(md, param);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057
Mike Snitzer1d0f3ce2009-12-10 23:52:22 +00001058 table = dm_get_live_or_inactive_table(md, param);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 if (table) {
1060 retrieve_status(table, param, param_size);
1061 dm_table_put(table);
1062 }
1063
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +01001064out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 dm_put(md);
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +01001066
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 return r;
1068}
1069
Al Viroaeb5d722008-09-02 15:28:45 -04001070static inline fmode_t get_mode(struct dm_ioctl *param)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071{
Al Viroaeb5d722008-09-02 15:28:45 -04001072 fmode_t mode = FMODE_READ | FMODE_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073
1074 if (param->flags & DM_READONLY_FLAG)
1075 mode = FMODE_READ;
1076
1077 return mode;
1078}
1079
1080static int next_target(struct dm_target_spec *last, uint32_t next, void *end,
1081 struct dm_target_spec **spec, char **target_params)
1082{
1083 *spec = (struct dm_target_spec *) ((unsigned char *) last + next);
1084 *target_params = (char *) (*spec + 1);
1085
1086 if (*spec < (last + 1))
1087 return -EINVAL;
1088
1089 return invalid_str(*target_params, end);
1090}
1091
1092static int populate_table(struct dm_table *table,
1093 struct dm_ioctl *param, size_t param_size)
1094{
1095 int r;
1096 unsigned int i = 0;
1097 struct dm_target_spec *spec = (struct dm_target_spec *) param;
1098 uint32_t next = param->data_start;
1099 void *end = (void *) param + param_size;
1100 char *target_params;
1101
1102 if (!param->target_count) {
1103 DMWARN("populate_table: no targets specified");
1104 return -EINVAL;
1105 }
1106
1107 for (i = 0; i < param->target_count; i++) {
1108
1109 r = next_target(spec, next, end, &spec, &target_params);
1110 if (r) {
1111 DMWARN("unable to find target");
1112 return r;
1113 }
1114
1115 r = dm_table_add_target(table, spec->target_type,
1116 (sector_t) spec->sector_start,
1117 (sector_t) spec->length,
1118 target_params);
1119 if (r) {
1120 DMWARN("error adding target to table");
1121 return r;
1122 }
1123
1124 next = spec->next;
1125 }
1126
Kiyoshi Uedae6ee8c02009-06-22 10:12:36 +01001127 r = dm_table_set_type(table);
1128 if (r) {
1129 DMWARN("unable to set table type");
1130 return r;
1131 }
1132
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 return dm_table_complete(table);
1134}
1135
Martin K. Petersen9c470082009-04-09 00:27:12 +01001136static int table_prealloc_integrity(struct dm_table *t,
1137 struct mapped_device *md)
1138{
1139 struct list_head *devices = dm_table_get_devices(t);
1140 struct dm_dev_internal *dd;
1141
1142 list_for_each_entry(dd, devices, list)
1143 if (bdev_get_integrity(dd->dm_dev.bdev))
1144 return blk_integrity_register(dm_disk(md), NULL);
1145
1146 return 0;
1147}
1148
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149static int table_load(struct dm_ioctl *param, size_t param_size)
1150{
1151 int r;
1152 struct hash_cell *hc;
1153 struct dm_table *t;
Mike Anderson1134e5a2006-03-27 01:17:54 -08001154 struct mapped_device *md;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155
Mike Anderson1134e5a2006-03-27 01:17:54 -08001156 md = find_device(param);
1157 if (!md)
1158 return -ENXIO;
1159
1160 r = dm_table_create(&t, get_mode(param), param->target_count, md);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 if (r)
Mike Anderson1134e5a2006-03-27 01:17:54 -08001162 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163
1164 r = populate_table(t, param, param_size);
1165 if (r) {
Mikulas Patockaf80a5572009-03-16 17:44:26 +00001166 dm_table_destroy(t);
Mike Anderson1134e5a2006-03-27 01:17:54 -08001167 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 }
1169
Martin K. Petersen9c470082009-04-09 00:27:12 +01001170 r = table_prealloc_integrity(t, md);
1171 if (r) {
1172 DMERR("%s: could not register integrity profile.",
1173 dm_device_name(md));
1174 dm_table_destroy(t);
1175 goto out;
1176 }
1177
Kiyoshi Uedae6ee8c02009-06-22 10:12:36 +01001178 r = dm_table_alloc_md_mempools(t);
1179 if (r) {
1180 DMWARN("unable to allocate mempools for this table");
1181 dm_table_destroy(t);
1182 goto out;
1183 }
1184
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 down_write(&_hash_lock);
Mike Anderson1134e5a2006-03-27 01:17:54 -08001186 hc = dm_get_mdptr(md);
1187 if (!hc || hc->md != md) {
1188 DMWARN("device has been removed from the dev hash table.");
Mikulas Patockaf80a5572009-03-16 17:44:26 +00001189 dm_table_destroy(t);
Mike Anderson1134e5a2006-03-27 01:17:54 -08001190 up_write(&_hash_lock);
1191 r = -ENXIO;
1192 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 }
1194
1195 if (hc->new_map)
Mikulas Patockad5816872009-01-06 03:05:10 +00001196 dm_table_destroy(hc->new_map);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 hc->new_map = t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 up_write(&_hash_lock);
Mike Anderson1134e5a2006-03-27 01:17:54 -08001199
1200 param->flags |= DM_INACTIVE_PRESENT_FLAG;
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +01001201 __dev_status(md, param);
Mike Anderson1134e5a2006-03-27 01:17:54 -08001202
1203out:
1204 dm_put(md);
1205
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 return r;
1207}
1208
1209static int table_clear(struct dm_ioctl *param, size_t param_size)
1210{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 struct hash_cell *hc;
Jeff Mahoney7ec75f22006-06-26 00:27:24 -07001212 struct mapped_device *md;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213
1214 down_write(&_hash_lock);
1215
1216 hc = __find_device_hash_cell(param);
1217 if (!hc) {
1218 DMWARN("device doesn't appear to be in the dev hash table.");
1219 up_write(&_hash_lock);
1220 return -ENXIO;
1221 }
1222
1223 if (hc->new_map) {
Mikulas Patockad5816872009-01-06 03:05:10 +00001224 dm_table_destroy(hc->new_map);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 hc->new_map = NULL;
1226 }
1227
1228 param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
1229
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +01001230 __dev_status(hc->md, param);
Jeff Mahoney7ec75f22006-06-26 00:27:24 -07001231 md = hc->md;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 up_write(&_hash_lock);
Jeff Mahoney7ec75f22006-06-26 00:27:24 -07001233 dm_put(md);
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +01001234
1235 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236}
1237
1238/*
1239 * Retrieves a list of devices used by a particular dm device.
1240 */
1241static void retrieve_deps(struct dm_table *table,
1242 struct dm_ioctl *param, size_t param_size)
1243{
1244 unsigned int count = 0;
1245 struct list_head *tmp;
1246 size_t len, needed;
Mikulas Patocka82b15192008-10-10 13:37:09 +01001247 struct dm_dev_internal *dd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 struct dm_target_deps *deps;
1249
1250 deps = get_result_buffer(param, param_size, &len);
1251
1252 /*
1253 * Count the devices.
1254 */
1255 list_for_each (tmp, dm_table_get_devices(table))
1256 count++;
1257
1258 /*
1259 * Check we have enough space.
1260 */
1261 needed = sizeof(*deps) + (sizeof(*deps->dev) * count);
1262 if (len < needed) {
1263 param->flags |= DM_BUFFER_FULL_FLAG;
1264 return;
1265 }
1266
1267 /*
1268 * Fill in the devices.
1269 */
1270 deps->count = count;
1271 count = 0;
1272 list_for_each_entry (dd, dm_table_get_devices(table), list)
Mikulas Patocka82b15192008-10-10 13:37:09 +01001273 deps->dev[count++] = huge_encode_dev(dd->dm_dev.bdev->bd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
1275 param->data_size = param->data_start + needed;
1276}
1277
1278static int table_deps(struct dm_ioctl *param, size_t param_size)
1279{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 struct mapped_device *md;
1281 struct dm_table *table;
1282
1283 md = find_device(param);
1284 if (!md)
1285 return -ENXIO;
1286
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +01001287 __dev_status(md, param);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288
Mike Snitzer1d0f3ce2009-12-10 23:52:22 +00001289 table = dm_get_live_or_inactive_table(md, param);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 if (table) {
1291 retrieve_deps(table, param, param_size);
1292 dm_table_put(table);
1293 }
1294
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 dm_put(md);
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +01001296
1297 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298}
1299
1300/*
1301 * Return the status of a device as a text string for each
1302 * target.
1303 */
1304static int table_status(struct dm_ioctl *param, size_t param_size)
1305{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 struct mapped_device *md;
1307 struct dm_table *table;
1308
1309 md = find_device(param);
1310 if (!md)
1311 return -ENXIO;
1312
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +01001313 __dev_status(md, param);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314
Mike Snitzer1d0f3ce2009-12-10 23:52:22 +00001315 table = dm_get_live_or_inactive_table(md, param);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 if (table) {
1317 retrieve_status(table, param, param_size);
1318 dm_table_put(table);
1319 }
1320
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 dm_put(md);
Alasdair G Kergon094ea9a2010-08-12 04:13:52 +01001322
1323 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324}
1325
1326/*
1327 * Pass a message to the target that's at the supplied device offset.
1328 */
1329static int target_message(struct dm_ioctl *param, size_t param_size)
1330{
1331 int r, argc;
1332 char **argv;
1333 struct mapped_device *md;
1334 struct dm_table *table;
1335 struct dm_target *ti;
1336 struct dm_target_msg *tmsg = (void *) param + param->data_start;
1337
1338 md = find_device(param);
1339 if (!md)
1340 return -ENXIO;
1341
Milan Broz027d50f2007-10-19 22:38:36 +01001342 if (tmsg < (struct dm_target_msg *) param->data ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 invalid_str(tmsg->message, (void *) param + param_size)) {
1344 DMWARN("Invalid target message parameters.");
1345 r = -EINVAL;
1346 goto out;
1347 }
1348
1349 r = dm_split_args(&argc, &argv, tmsg->message);
1350 if (r) {
1351 DMWARN("Failed to split target message parameters");
1352 goto out;
1353 }
1354
Alasdair G Kergon7c666412009-12-10 23:52:19 +00001355 table = dm_get_live_table(md);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 if (!table)
1357 goto out_argv;
1358
Mike Andersonc50abeb2009-12-10 23:52:20 +00001359 if (dm_deleting_md(md)) {
1360 r = -ENXIO;
1361 goto out_table;
1362 }
1363
Jun'ichi Nomura512875b2007-12-13 14:15:25 +00001364 ti = dm_table_find_target(table, tmsg->sector);
1365 if (!dm_target_is_valid(ti)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 DMWARN("Target message sector outside device.");
1367 r = -EINVAL;
Jun'ichi Nomura512875b2007-12-13 14:15:25 +00001368 } else if (ti->type->message)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 r = ti->type->message(ti, argc, argv);
1370 else {
1371 DMWARN("Target type does not support messages");
1372 r = -EINVAL;
1373 }
1374
Mike Andersonc50abeb2009-12-10 23:52:20 +00001375 out_table:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 dm_table_put(table);
1377 out_argv:
1378 kfree(argv);
1379 out:
1380 param->data_size = 0;
1381 dm_put(md);
1382 return r;
1383}
1384
1385/*-----------------------------------------------------------------
1386 * Implementation of open/close/ioctl on the special char
1387 * device.
1388 *---------------------------------------------------------------*/
1389static ioctl_fn lookup_ioctl(unsigned int cmd)
1390{
1391 static struct {
1392 int cmd;
1393 ioctl_fn fn;
1394 } _ioctls[] = {
1395 {DM_VERSION_CMD, NULL}, /* version is dealt with elsewhere */
1396 {DM_REMOVE_ALL_CMD, remove_all},
1397 {DM_LIST_DEVICES_CMD, list_devices},
1398
1399 {DM_DEV_CREATE_CMD, dev_create},
1400 {DM_DEV_REMOVE_CMD, dev_remove},
1401 {DM_DEV_RENAME_CMD, dev_rename},
1402 {DM_DEV_SUSPEND_CMD, dev_suspend},
1403 {DM_DEV_STATUS_CMD, dev_status},
1404 {DM_DEV_WAIT_CMD, dev_wait},
1405
1406 {DM_TABLE_LOAD_CMD, table_load},
1407 {DM_TABLE_CLEAR_CMD, table_clear},
1408 {DM_TABLE_DEPS_CMD, table_deps},
1409 {DM_TABLE_STATUS_CMD, table_status},
1410
1411 {DM_LIST_VERSIONS_CMD, list_versions},
1412
Darrick J. Wong3ac51e72006-03-27 01:17:54 -08001413 {DM_TARGET_MSG_CMD, target_message},
1414 {DM_DEV_SET_GEOMETRY_CMD, dev_set_geometry}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 };
1416
1417 return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn;
1418}
1419
1420/*
1421 * As well as checking the version compatibility this always
1422 * copies the kernel interface version out.
1423 */
1424static int check_version(unsigned int cmd, struct dm_ioctl __user *user)
1425{
1426 uint32_t version[3];
1427 int r = 0;
1428
1429 if (copy_from_user(version, user->version, sizeof(version)))
1430 return -EFAULT;
1431
1432 if ((DM_VERSION_MAJOR != version[0]) ||
1433 (DM_VERSION_MINOR < version[1])) {
1434 DMWARN("ioctl interface mismatch: "
1435 "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)",
1436 DM_VERSION_MAJOR, DM_VERSION_MINOR,
1437 DM_VERSION_PATCHLEVEL,
1438 version[0], version[1], version[2], cmd);
1439 r = -EINVAL;
1440 }
1441
1442 /*
1443 * Fill in the kernel version.
1444 */
1445 version[0] = DM_VERSION_MAJOR;
1446 version[1] = DM_VERSION_MINOR;
1447 version[2] = DM_VERSION_PATCHLEVEL;
1448 if (copy_to_user(user->version, version, sizeof(version)))
1449 return -EFAULT;
1450
1451 return r;
1452}
1453
1454static void free_params(struct dm_ioctl *param)
1455{
1456 vfree(param);
1457}
1458
1459static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param)
1460{
1461 struct dm_ioctl tmp, *dmi;
1462
Milan Broz76c072b2008-02-08 02:09:56 +00001463 if (copy_from_user(&tmp, user, sizeof(tmp) - sizeof(tmp.data)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 return -EFAULT;
1465
Milan Broz76c072b2008-02-08 02:09:56 +00001466 if (tmp.data_size < (sizeof(tmp) - sizeof(tmp.data)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 return -EINVAL;
1468
Jesper Juhlbb56acf2007-10-19 22:38:54 +01001469 dmi = vmalloc(tmp.data_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470 if (!dmi)
1471 return -ENOMEM;
1472
1473 if (copy_from_user(dmi, user, tmp.data_size)) {
1474 vfree(dmi);
1475 return -EFAULT;
1476 }
1477
1478 *param = dmi;
1479 return 0;
1480}
1481
1482static int validate_params(uint cmd, struct dm_ioctl *param)
1483{
1484 /* Always clear this flag */
1485 param->flags &= ~DM_BUFFER_FULL_FLAG;
Peter Rajnoha3abf85b2010-03-06 02:32:31 +00001486 param->flags &= ~DM_UEVENT_GENERATED_FLAG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487
1488 /* Ignores parameters */
1489 if (cmd == DM_REMOVE_ALL_CMD ||
1490 cmd == DM_LIST_DEVICES_CMD ||
1491 cmd == DM_LIST_VERSIONS_CMD)
1492 return 0;
1493
1494 if ((cmd == DM_DEV_CREATE_CMD)) {
1495 if (!*param->name) {
1496 DMWARN("name not supplied when creating device");
1497 return -EINVAL;
1498 }
1499 } else if ((*param->uuid && *param->name)) {
1500 DMWARN("only supply one of name or uuid, cmd(%u)", cmd);
1501 return -EINVAL;
1502 }
1503
1504 /* Ensure strings are terminated */
1505 param->name[DM_NAME_LEN - 1] = '\0';
1506 param->uuid[DM_UUID_LEN - 1] = '\0';
1507
1508 return 0;
1509}
1510
Alasdair G Kergon27238b22008-02-08 02:09:53 +00001511static int ctl_ioctl(uint command, struct dm_ioctl __user *user)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512{
1513 int r = 0;
1514 unsigned int cmd;
Andrew Mortona26ffd42008-02-08 02:10:16 +00001515 struct dm_ioctl *uninitialized_var(param);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 ioctl_fn fn = NULL;
1517 size_t param_size;
1518
1519 /* only root can play with this */
1520 if (!capable(CAP_SYS_ADMIN))
1521 return -EACCES;
1522
1523 if (_IOC_TYPE(command) != DM_IOCTL)
1524 return -ENOTTY;
1525
1526 cmd = _IOC_NR(command);
1527
1528 /*
1529 * Check the interface version passed in. This also
1530 * writes out the kernel's interface version.
1531 */
1532 r = check_version(cmd, user);
1533 if (r)
1534 return r;
1535
1536 /*
1537 * Nothing more to do for the version command.
1538 */
1539 if (cmd == DM_VERSION_CMD)
1540 return 0;
1541
1542 fn = lookup_ioctl(cmd);
1543 if (!fn) {
1544 DMWARN("dm_ctl_ioctl: unknown command 0x%x", command);
1545 return -ENOTTY;
1546 }
1547
1548 /*
1549 * Trying to avoid low memory issues when a device is
1550 * suspended.
1551 */
1552 current->flags |= PF_MEMALLOC;
1553
1554 /*
1555 * Copy the parameters into kernel space.
1556 */
1557 r = copy_params(user, &param);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558
Alasdair G Kergondab6a422006-02-01 03:04:52 -08001559 current->flags &= ~PF_MEMALLOC;
1560
1561 if (r)
1562 return r;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563
1564 r = validate_params(cmd, param);
1565 if (r)
1566 goto out;
1567
1568 param_size = param->data_size;
1569 param->data_size = sizeof(*param);
1570 r = fn(param, param_size);
1571
1572 /*
1573 * Copy the results back to userland.
1574 */
1575 if (!r && copy_to_user(user, param, param->data_size))
1576 r = -EFAULT;
1577
1578 out:
1579 free_params(param);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 return r;
1581}
1582
Alasdair G Kergon27238b22008-02-08 02:09:53 +00001583static long dm_ctl_ioctl(struct file *file, uint command, ulong u)
1584{
1585 return (long)ctl_ioctl(command, (struct dm_ioctl __user *)u);
1586}
1587
Milan Broz76c072b2008-02-08 02:09:56 +00001588#ifdef CONFIG_COMPAT
1589static long dm_compat_ctl_ioctl(struct file *file, uint command, ulong u)
1590{
1591 return (long)dm_ctl_ioctl(file, command, (ulong) compat_ptr(u));
1592}
1593#else
1594#define dm_compat_ctl_ioctl NULL
1595#endif
1596
Arjan van de Venfa027c22007-02-12 00:55:33 -08001597static const struct file_operations _ctl_fops = {
Alasdair G Kergon27238b22008-02-08 02:09:53 +00001598 .unlocked_ioctl = dm_ctl_ioctl,
Milan Broz76c072b2008-02-08 02:09:56 +00001599 .compat_ioctl = dm_compat_ctl_ioctl,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 .owner = THIS_MODULE,
1601};
1602
1603static struct miscdevice _dm_misc = {
1604 .minor = MISC_DYNAMIC_MINOR,
1605 .name = DM_NAME,
Kay Sieverse454cea2009-09-18 23:01:12 +02001606 .nodename = "mapper/control",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 .fops = &_ctl_fops
1608};
1609
1610/*
1611 * Create misc character device and link to DM_DIR/control.
1612 */
1613int __init dm_interface_init(void)
1614{
1615 int r;
1616
1617 r = dm_hash_init();
1618 if (r)
1619 return r;
1620
1621 r = misc_register(&_dm_misc);
1622 if (r) {
1623 DMERR("misc_register failed for control device");
1624 dm_hash_exit();
1625 return r;
1626 }
1627
1628 DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR,
1629 DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA,
1630 DM_DRIVER_EMAIL);
1631 return 0;
1632}
1633
1634void dm_interface_exit(void)
1635{
1636 if (misc_deregister(&_dm_misc) < 0)
1637 DMERR("misc_deregister failed for control device");
1638
1639 dm_hash_exit();
1640}
Mike Anderson96a1f7d2007-10-19 22:47:59 +01001641
1642/**
1643 * dm_copy_name_and_uuid - Copy mapped device name & uuid into supplied buffers
1644 * @md: Pointer to mapped_device
1645 * @name: Buffer (size DM_NAME_LEN) for name
1646 * @uuid: Buffer (size DM_UUID_LEN) for uuid or empty string if uuid not defined
1647 */
1648int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid)
1649{
1650 int r = 0;
1651 struct hash_cell *hc;
1652
1653 if (!md)
1654 return -ENXIO;
1655
Mikulas Patocka60769052009-12-10 23:51:52 +00001656 mutex_lock(&dm_hash_cells_mutex);
Mike Anderson96a1f7d2007-10-19 22:47:59 +01001657 hc = dm_get_mdptr(md);
1658 if (!hc || hc->md != md) {
1659 r = -ENXIO;
1660 goto out;
1661 }
1662
Milan Broz23d39f62009-01-06 03:05:04 +00001663 if (name)
1664 strcpy(name, hc->name);
1665 if (uuid)
1666 strcpy(uuid, hc->uuid ? : "");
Mike Anderson96a1f7d2007-10-19 22:47:59 +01001667
1668out:
Mikulas Patocka60769052009-12-10 23:51:52 +00001669 mutex_unlock(&dm_hash_cells_mutex);
Mike Anderson96a1f7d2007-10-19 22:47:59 +01001670
1671 return r;
1672}