sys/dev/disk/dm: Don't implement "status" as a subset of "table" [1/2]
[dragonfly.git] / sys / dev / disk / dm / dm_ioctl.c
CommitLineData
ff56536e
AH
1/* $NetBSD: dm_ioctl.c,v 1.21 2010/02/25 20:48:58 jakllsch Exp $ */
2
3/*
ba2ce341 4 * Copyright (c) 2010-2011 Alex Hornung <alex@alexhornung.com>
ff56536e
AH
5 * Copyright (c) 2008 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Adam Hamsik.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * Locking is used to synchronise between ioctl calls and between dm_table's
35 * users.
36 *
37 * ioctl locking:
38 * Simple reference counting, to count users of device will be used routines
39 * dm_dev_busy/dm_dev_unbusy are used for that.
40 * dm_dev_lookup/dm_dev_rem call dm_dev_busy before return(caller is therefore
41 * holder of reference_counter last).
42 *
43 * ioctl routines which change/remove dm_dev parameters must wait on
44 * dm_dev::dev_cv and when last user will call dm_dev_unbusy they will wake
45 * up them.
46 *
47 * table_head locking:
48 * To access table entries dm_table_* routines must be used.
49 *
50 * dm_table_get_entry will increment table users reference
5b279a20 51 * counter. It will return active or inactive table depends
ff56536e
AH
52 * on uint8_t argument.
53 *
54 * dm_table_release must be called for every table_entry from
55 * dm_table_get_entry. Between these to calls tables can'tbe switched
56 * or destroyed.
57 *
58 * dm_table_head_init initialize talbe_entries SLISTS and io_cv.
59 *
60 * dm_table_head_destroy destroy cv.
61 *
62 * There are two types of users for dm_table_head first type will
63 * only read list and try to do anything with it e.g. dmstrategy,
64 * dm_table_size etc. There is another user for table_head which wants
65 * to change table lists e.g. dm_dev_resume_ioctl, dm_dev_remove_ioctl,
66 * dm_table_clear_ioctl.
67 *
68 * NOTE: It is not allowed to call dm_table_destroy, dm_table_switch_tables
69 * with hold table reference counter. Table reference counter is hold
70 * after calling dm_table_get_entry routine. After calling this
71 * function user must call dm_table_release before any writer table
72 * operation.
73 *
74 * Example: dm_table_get_entry
75 * dm_table_destroy/dm_table_switch_tables
76 * This exaple will lead to deadlock situation because after dm_table_get_entry
77 * table reference counter is != 0 and dm_table_destroy have to wait on cv until
78 * reference counter is 0.
79 *
80 */
81
82#include <sys/types.h>
ff56536e 83#include <sys/device.h>
5b279a20 84#include <sys/malloc.h>
ff56536e 85#include <sys/vnode.h>
a84e173e 86#include <dev/disk/dm/dm.h>
ff56536e 87
ff56536e 88#include "netbsd-dm.h"
ff56536e 89
ff56536e
AH
90#define DM_REMOVE_FLAG(flag, name) do { \
91 prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \
92 flag &= ~name; \
93 prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \
94} while (/*CONSTCOND*/0)
95
96#define DM_ADD_FLAG(flag, name) do { \
97 prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \
98 flag |= name; \
99 prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \
100} while (/*CONSTCOND*/0)
101
ff56536e
AH
102/*
103 * Print flags sent to the kernel from libevmapper.
104 */
105static int
106dm_dbg_print_flags(int flags)
107{
108 aprint_debug("dbg_print --- %d\n", flags);
109
110 if (flags & DM_READONLY_FLAG)
111 aprint_debug("dbg_flags: DM_READONLY_FLAG set In/Out\n");
112
113 if (flags & DM_SUSPEND_FLAG)
a370741a 114 aprint_debug("dbg_flags: DM_SUSPEND_FLAG set In/Out\n");
ff56536e
AH
115
116 if (flags & DM_PERSISTENT_DEV_FLAG)
a370741a 117 aprint_debug("dbg_flags: DM_PERSISTENT_DEV_FLAG set In\n");
ff56536e
AH
118
119 if (flags & DM_STATUS_TABLE_FLAG)
120 aprint_debug("dbg_flags: DM_STATUS_TABLE_FLAG set In\n");
121
122 if (flags & DM_ACTIVE_PRESENT_FLAG)
123 aprint_debug("dbg_flags: DM_ACTIVE_PRESENT_FLAG set Out\n");
124
125 if (flags & DM_INACTIVE_PRESENT_FLAG)
126 aprint_debug("dbg_flags: DM_INACTIVE_PRESENT_FLAG set Out\n");
127
128 if (flags & DM_BUFFER_FULL_FLAG)
129 aprint_debug("dbg_flags: DM_BUFFER_FULL_FLAG set Out\n");
130
131 if (flags & DM_SKIP_BDGET_FLAG)
132 aprint_debug("dbg_flags: DM_SKIP_BDGET_FLAG set In\n");
133
134 if (flags & DM_SKIP_LOCKFS_FLAG)
135 aprint_debug("dbg_flags: DM_SKIP_LOCKFS_FLAG set In\n");
136
137 if (flags & DM_NOFLUSH_FLAG)
138 aprint_debug("dbg_flags: DM_NOFLUSH_FLAG set In\n");
139
140 return 0;
141}
ff56536e
AH
142/*
143 * Get list of all available targets from global
144 * target list and sent them back to libdevmapper.
145 */
146int
147dm_list_versions_ioctl(prop_dictionary_t dm_dict)
148{
149 prop_array_t target_list;
150 uint32_t flags;
151
152 flags = 0;
153
154 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
155
156 dm_dbg_print_flags(flags);
157 target_list = dm_target_prop_list();
158
159 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, target_list);
160 prop_object_release(target_list);
161
162 return 0;
163}
164/*
165 * Create in-kernel entry for device. Device attributes such as name, uuid are
166 * taken from proplib dictionary.
167 *
168 */
169int
170dm_dev_create_ioctl(prop_dictionary_t dm_dict)
171{
172 dm_dev_t *dmv;
173 const char *name, *uuid;
9fada28a 174 int r, flags;
ff56536e
AH
175
176 r = 0;
177 flags = 0;
178 name = NULL;
179 uuid = NULL;
180
181 /* Get needed values from dictionary. */
182 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
183 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
184 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
185
186 dm_dbg_print_flags(flags);
187
188 /* Lookup name and uuid if device already exist quit. */
189 if ((dmv = dm_dev_lookup(name, uuid, -1)) != NULL) {
190 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); /* Device already exists */
191 dm_dev_unbusy(dmv);
192 return EEXIST;
193 }
b878986e 194
9fada28a 195 r = dm_dev_create(&dmv, name, uuid, flags);
9fada28a
AH
196 if (r == 0) {
197 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
198 DM_ADD_FLAG(flags, DM_EXISTS_FLAG);
199 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
200 }
ff56536e
AH
201
202 return r;
203}
204/*
205 * Get list of created device-mapper devices fromglobal list and
206 * send it to kernel.
207 *
208 * Output dictionary:
209 *
210 * <key>cmd_data</key>
211 * <array>
212 * <dict>
213 * <key>name<key>
214 * <string>...</string>
215 *
216 * <key>dev</key>
217 * <integer>...</integer>
218 * </dict>
219 * </array>
220 *
221 */
222int
223dm_dev_list_ioctl(prop_dictionary_t dm_dict)
224{
225 prop_array_t dev_list;
226
227 uint32_t flags;
228
229 flags = 0;
230
231 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
232
233 dm_dbg_print_flags(flags);
234
235 dev_list = dm_dev_prop_list();
236
237 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, dev_list);
238 prop_object_release(dev_list);
239
240 return 0;
241}
242/*
243 * Rename selected devices old name is in struct dm_ioctl.
244 * newname is taken from dictionary
245 *
246 * <key>cmd_data</key>
247 * <array>
248 * <string>...</string>
249 * </array>
250 */
251int
252dm_dev_rename_ioctl(prop_dictionary_t dm_dict)
253{
9fada28a 254#if 0
ff56536e
AH
255 prop_array_t cmd_array;
256 dm_dev_t *dmv;
257
258 const char *name, *uuid, *n_name;
259 uint32_t flags, minor;
260
261 name = NULL;
262 uuid = NULL;
263 minor = 0;
264
265 /* Get needed values from dictionary. */
266 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
267 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
268 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
269 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
270
271 dm_dbg_print_flags(flags);
272
273 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA);
274
275 prop_array_get_cstring_nocopy(cmd_array, 0, &n_name);
276
277 if (strlen(n_name) + 1 > DM_NAME_LEN)
278 return EINVAL;
279
4c28af7b 280 if ((dmv = dm_dev_rem(name, uuid, minor)) == NULL) {
ff56536e
AH
281 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
282 return ENOENT;
283 }
284 /* change device name */
285 /*
286 * XXX How to deal with this change, name only used in
287 * dm_dev_routines, should I add dm_dev_change_name which will run
288 * under the dm_dev_list mutex ?
289 */
290 strlcpy(dmv->name, n_name, DM_NAME_LEN);
291
292 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
293 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
294 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid);
295
296 dm_dev_insert(dmv);
9fada28a 297#endif
ff56536e 298
9fada28a
AH
299 /*
300 * XXX: the rename is not yet implemented. The main complication
301 * here is devfs. We'd probably need a new function, rename_dev()
302 * that would trigger a node rename in devfs.
303 */
304 kprintf("dm_dev_rename_ioctl called, but not implemented!\n");
83eeaffa 305 return ENOSYS;
ff56536e 306}
9fada28a 307
ff56536e 308/*
9fada28a 309 * Remove device
ff56536e
AH
310 */
311int
312dm_dev_remove_ioctl(prop_dictionary_t dm_dict)
313{
314 dm_dev_t *dmv;
315 const char *name, *uuid;
ba65987c 316 uint32_t flags, minor, is_open;
ff56536e
AH
317
318 flags = 0;
319 name = NULL;
320 uuid = NULL;
321
322 /* Get needed values from dictionary. */
323 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
324 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
325 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
326 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
327
328 dm_dbg_print_flags(flags);
329
ff56536e
AH
330 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
331 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
332 return ENOENT;
333 }
ff56536e 334
ba65987c
AH
335 is_open = dmv->is_open;
336
ff56536e
AH
337 dm_dev_unbusy(dmv);
338
ba65987c 339 if (is_open)
956c8d71
AH
340 return EBUSY;
341
ff56536e 342 /*
7350b8b6 343 * This will call dm_dev_rem_dev routine which will actually remove
ff56536e
AH
344 * device.
345 */
9fada28a 346 return dm_dev_remove(dmv);
ff56536e 347}
9fada28a
AH
348
349/*
350 * Try to remove all devices
351 */
352int
353dm_dev_remove_all_ioctl(prop_dictionary_t dm_dict)
354{
355 uint32_t flags = 0;
356
357 /* Get needed values from dictionary. */
358 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
359
360 dm_dbg_print_flags(flags);
361
362 /* Gently remove all devices, if possible */
363 return dm_dev_remove_all(1);
364}
365
ff56536e
AH
366/*
367 * Return actual state of device to libdevmapper.
368 */
369int
370dm_dev_status_ioctl(prop_dictionary_t dm_dict)
371{
372 dm_dev_t *dmv;
373 const char *name, *uuid;
374 uint32_t flags, j, minor;
375
376 name = NULL;
377 uuid = NULL;
378 flags = 0;
379 j = 0;
380
381 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
382 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
383 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
384 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
385
386 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
387 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
388 return ENOENT;
389 }
390 dm_dbg_print_flags(dmv->flags);
391
392 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
393 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
394 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid);
395
396 if (dmv->flags & DM_SUSPEND_FLAG)
397 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG);
398
399 /*
400 * Add status flags for tables I have to check both active and
401 * inactive tables.
402 */
403 if ((j = dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE))) {
404 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
405 } else
406 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
407
408 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_TARGET_COUNT, j);
409
410 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE))
411 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
412 else
413 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
414
415 dm_dev_unbusy(dmv);
416
417 return 0;
418}
419/*
420 * Set only flag to suggest that device is suspended. This call is
421 * not supported in NetBSD.
422 *
423 */
424int
425dm_dev_suspend_ioctl(prop_dictionary_t dm_dict)
426{
427 dm_dev_t *dmv;
428 const char *name, *uuid;
429 uint32_t flags, minor;
430
431 name = NULL;
432 uuid = NULL;
433 flags = 0;
434
435 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
436 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
437 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
438 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
439
440 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
441 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
442 return ENOENT;
443 }
5b279a20 444 atomic_set_int(&dmv->flags, DM_SUSPEND_FLAG);
ff56536e
AH
445
446 dm_dbg_print_flags(dmv->flags);
447
448 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
449 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, dmv->flags);
450 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
451
452 dm_dev_unbusy(dmv);
453
454 /* Add flags to dictionary flag after dmv -> dict copy */
455 DM_ADD_FLAG(flags, DM_EXISTS_FLAG);
456
457 return 0;
458}
459/*
460 * Simulate Linux behaviour better and switch tables here and not in
461 * dm_table_load_ioctl.
462 */
463int
464dm_dev_resume_ioctl(prop_dictionary_t dm_dict)
465{
466 dm_dev_t *dmv;
467 const char *name, *uuid;
468 uint32_t flags, minor;
469
470 name = NULL;
471 uuid = NULL;
472 flags = 0;
473
474 /*
475 * char *xml; xml = prop_dictionary_externalize(dm_dict);
476 * printf("%s\n",xml);
477 */
478
479 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
480 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
481 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
482 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
483
484 /* Remove device from global device list */
485 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
486 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
487 return ENOENT;
488 }
5b279a20
AH
489 atomic_clear_int(&dmv->flags, (DM_SUSPEND_FLAG | DM_INACTIVE_PRESENT_FLAG));
490 atomic_set_int(&dmv->flags, DM_ACTIVE_PRESENT_FLAG);
ff56536e
AH
491
492 dm_table_switch_tables(&dmv->table_head);
493
494 DM_ADD_FLAG(flags, DM_EXISTS_FLAG);
495
5b279a20 496 dmsetdiskinfo(dmv->diskp, &dmv->table_head);
ff56536e
AH
497
498 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
499 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags);
500 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
501
502 dm_dev_unbusy(dmv);
503
504 /* Destroy inactive table after resume. */
505 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
506
507 return 0;
508}
509/*
510 * Table management routines
511 * lvm2tools doens't send name/uuid to kernel with table
512 * for lookup I have to use minor number.
513 */
514
515/*
516 * Remove inactive table from device. Routines which work's with inactive tables
517 * doesn't need to synchronise with dmstrategy. They can synchronise themselves with mutex?.
518 *
519 */
520int
521dm_table_clear_ioctl(prop_dictionary_t dm_dict)
522{
523 dm_dev_t *dmv;
524 const char *name, *uuid;
525 uint32_t flags, minor;
526
527 dmv = NULL;
528 name = NULL;
529 uuid = NULL;
530 flags = 0;
531 minor = 0;
532
533 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
534 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
535 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
536 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
537
538 aprint_debug("Clearing inactive table from device: %s--%s\n",
539 name, uuid);
540
541 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
542 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
543 return ENOENT;
544 }
545 /* Select unused table */
546 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
547
5b279a20 548 atomic_clear_int(&dmv->flags, DM_INACTIVE_PRESENT_FLAG);
ff56536e
AH
549
550 dm_dev_unbusy(dmv);
551
552 return 0;
553}
554/*
555 * Get list of physical devices for active table.
556 * Get dev_t from pdev vnode and insert it into cmd_array.
557 *
558 * XXX. This function is called from lvm2tools to get information
559 * about physical devices, too e.g. during vgcreate.
560 */
561int
562dm_table_deps_ioctl(prop_dictionary_t dm_dict)
563{
564 dm_dev_t *dmv;
565 dm_table_t *tbl;
566 dm_table_entry_t *table_en;
567
568 prop_array_t cmd_array;
569 const char *name, *uuid;
570 uint32_t flags, minor;
571
572 int table_type;
ff56536e
AH
573
574 name = NULL;
575 uuid = NULL;
576 dmv = NULL;
577 flags = 0;
578
ff56536e
AH
579 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
580 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
581 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
582 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
583
584 /* create array for dev_t's */
585 cmd_array = prop_array_create();
586
587 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
588 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
589 return ENOENT;
590 }
591 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
592 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_NAME, dmv->name);
593 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid);
594
595 aprint_debug("Getting table deps for device: %s\n", dmv->name);
596
597 /*
598 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query
599 * INACTIVE TABLE
600 */
601 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG)
602 table_type = DM_TABLE_INACTIVE;
603 else
604 table_type = DM_TABLE_ACTIVE;
605
606 tbl = dm_table_get_entry(&dmv->table_head, table_type);
607
608 SLIST_FOREACH(table_en, tbl, next)
609 table_en->target->deps(table_en, cmd_array);
610
611 dm_table_release(&dmv->table_head, table_type);
612 dm_dev_unbusy(dmv);
613
614 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array);
615 prop_object_release(cmd_array);
616
617 return 0;
618}
619/*
620 * Load new table/tables to device.
621 * Call apropriate target init routine open all physical pdev's and
622 * link them to device. For other targets mirror, strip, snapshot
623 * etc. also add dependency devices to upcalls list.
624 *
625 * Load table to inactive slot table are switched in dm_device_resume_ioctl.
626 * This simulates Linux behaviour better there should not be any difference.
627 *
628 */
629int
630dm_table_load_ioctl(prop_dictionary_t dm_dict)
631{
632 dm_dev_t *dmv;
633 dm_table_entry_t *table_en, *last_table;
634 dm_table_t *tbl;
635 dm_target_t *target;
636
637 prop_object_iterator_t iter;
638 prop_array_t cmd_array;
639 prop_dictionary_t target_dict;
640
641 const char *name, *uuid, *type;
642
643 uint32_t flags, ret, minor;
644
645 char *str;
646
647 ret = 0;
648 flags = 0;
649 name = NULL;
650 uuid = NULL;
651 dmv = NULL;
652 last_table = NULL;
653 str = NULL;
654
655 /*
656 * char *xml; xml = prop_dictionary_externalize(dm_dict);
657 * printf("%s\n",xml);
658 */
659
660 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
661 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
662 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
663 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
664
665 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA);
666 iter = prop_array_iterator(cmd_array);
667 dm_dbg_print_flags(flags);
668
669 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
670 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
671 return ENOENT;
672 }
673 aprint_debug("Loading table to device: %s--%d\n", name,
674 dmv->table_head.cur_active_table);
675
676 /*
677 * I have to check if this table slot is not used by another table list.
678 * if it is used I should free them.
679 */
680 if (dmv->flags & DM_INACTIVE_PRESENT_FLAG)
681 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
682
683 dm_dbg_print_flags(dmv->flags);
684 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_INACTIVE);
685
686 aprint_debug("dmv->name = %s\n", dmv->name);
687
688 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
689
690 while ((target_dict = prop_object_iterator_next(iter)) != NULL) {
ff56536e
AH
691 prop_dictionary_get_cstring_nocopy(target_dict,
692 DM_TABLE_TYPE, &type);
693 /*
694 * If we want to deny table with 2 or more different
695 * target we should do it here
696 */
7115a22b
AH
697 if (((target = dm_target_lookup(type)) == NULL) &&
698 ((target = dm_target_autoload(type)) == NULL)) {
ff56536e
AH
699 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
700 dm_dev_unbusy(dmv);
701 return ENOENT;
702 }
5b279a20
AH
703 if ((table_en = kmalloc(sizeof(dm_table_entry_t),
704 M_DM, M_WAITOK)) == NULL) {
ff56536e
AH
705 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
706 dm_dev_unbusy(dmv);
12a49c9b 707 dm_target_unbusy(target);
ff56536e
AH
708 return ENOMEM;
709 }
710 prop_dictionary_get_uint64(target_dict, DM_TABLE_START,
711 &table_en->start);
712 prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH,
713 &table_en->length);
714
e8e2bcda
AH
715 aprint_debug("dm_ioctl.c... table_en->start = %ju, "
716 "table_en->length = %ju\n",
717 (uintmax_t)table_en->start,
718 (uintmax_t)table_en->length);
5b279a20 719
ff56536e
AH
720 table_en->target = target;
721 table_en->dm_dev = dmv;
722 table_en->target_config = NULL;
723
724 /*
725 * There is a parameter string after dm_target_spec
726 * structure which points to /dev/wd0a 284 part of
727 * table. String str points to this text. This can be
728 * null and therefore it should be checked before we try to
729 * use it.
730 */
731 prop_dictionary_get_cstring(target_dict,
8062d810 732 DM_TABLE_PARAMS, &str);
ff56536e
AH
733
734 if (SLIST_EMPTY(tbl))
735 /* insert this table to head */
736 SLIST_INSERT_HEAD(tbl, table_en, next);
737 else
738 SLIST_INSERT_AFTER(last_table, table_en, next);
739
740 /*
741 * Params string is different for every target,
742 * therfore I have to pass it to target init
743 * routine and parse parameters there.
744 */
a370741a 745 aprint_debug("DM: str passed in is: \"%s\"\n", str);
ff56536e
AH
746 if ((ret = target->init(dmv, &table_en->target_config,
747 str)) != 0) {
748
749 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
750 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
5b279a20 751 kfree(str, M_TEMP);
ff56536e
AH
752
753 dm_dev_unbusy(dmv);
754 return ret;
755 }
756 last_table = table_en;
5b279a20 757 kfree(str, M_TEMP);
ff56536e
AH
758 }
759 prop_object_iterator_release(iter);
760
761 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
5b279a20 762 atomic_set_int(&dmv->flags, DM_INACTIVE_PRESENT_FLAG);
ff56536e
AH
763
764 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
3b48c3c1 765
ff56536e 766 dm_dev_unbusy(dmv);
3b48c3c1
AH
767#if 0
768 dmsetdiskinfo(dmv->diskp, &dmv->table_head);
769#endif
ff56536e
AH
770 return 0;
771}
772/*
773 * Get description of all tables loaded to device from kernel
774 * and send it to libdevmapper.
775 *
776 * Output dictionary for every table:
777 *
778 * <key>cmd_data</key>
779 * <array>
780 * <dict>
781 * <key>type<key>
782 * <string>...</string>
783 *
784 * <key>start</key>
785 * <integer>...</integer>
786 *
787 * <key>length</key>
788 * <integer>...</integer>
789 *
790 * <key>params</key>
791 * <string>...</string>
792 * </dict>
793 * </array>
794 *
795 */
796int
797dm_table_status_ioctl(prop_dictionary_t dm_dict)
798{
799 dm_dev_t *dmv;
800 dm_table_t *tbl;
801 dm_table_entry_t *table_en;
802
803 prop_array_t cmd_array;
804 prop_dictionary_t target_dict;
805
3d089c23 806 uint32_t minor;
ff56536e
AH
807
808 const char *name, *uuid;
809 char *params;
810 int flags;
811 int table_type;
812
813 dmv = NULL;
814 uuid = NULL;
815 name = NULL;
816 params = NULL;
817 flags = 0;
ff56536e
AH
818
819 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
820 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
821 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
822 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
823
824 cmd_array = prop_array_create();
825
826 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
827 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
828 return ENOENT;
829 }
830 /*
831 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query
832 * INACTIVE TABLE
833 */
834 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG)
835 table_type = DM_TABLE_INACTIVE;
836 else
837 table_type = DM_TABLE_ACTIVE;
838
839 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE))
840 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
841 else {
842 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
843
844 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE))
845 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
846 else {
847 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
848 }
849 }
850
851 if (dmv->flags & DM_SUSPEND_FLAG)
852 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG);
853
854 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
855
856 aprint_debug("Status of device tables: %s--%d\n",
857 name, dmv->table_head.cur_active_table);
858
859 tbl = dm_table_get_entry(&dmv->table_head, table_type);
860
861 SLIST_FOREACH(table_en, tbl, next) {
862 target_dict = prop_dictionary_create();
863 aprint_debug("%016" PRIu64 ", length %016" PRIu64
864 ", target %s\n", table_en->start, table_en->length,
865 table_en->target->name);
866
867 prop_dictionary_set_uint64(target_dict, DM_TABLE_START,
868 table_en->start);
869 prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH,
870 table_en->length);
871
872 prop_dictionary_set_cstring(target_dict, DM_TABLE_TYPE,
873 table_en->target->name);
874
875 /* dm_table_get_cur_actv.table ?? */
876 prop_dictionary_set_int32(target_dict, DM_TABLE_STAT,
877 dmv->table_head.cur_active_table);
878
0bc04bb1 879 if (flags & DM_STATUS_TABLE_FLAG) {
ff56536e
AH
880 params = table_en->target->status
881 (table_en->target_config);
f134a703
TK
882 } else if (table_en->target->info) {
883 params = table_en->target->info
884 (table_en->target_config);
885 } else {
886 params = NULL;
ff56536e 887 }
f134a703
TK
888 if (params != NULL) {
889 prop_dictionary_set_cstring(target_dict,
890 DM_TABLE_PARAMS, params);
891 kfree(params, M_DM);
892 }
893
ff56536e
AH
894 prop_array_add(cmd_array, target_dict);
895 prop_object_release(target_dict);
896 }
897
898 dm_table_release(&dmv->table_head, table_type);
899 dm_dev_unbusy(dmv);
900
901 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags);
902 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array);
903 prop_object_release(cmd_array);
904
905 return 0;
906}
907
51292ec6
AH
908int
909dm_message_ioctl(prop_dictionary_t dm_dict)
910{
911 dm_table_t *tbl;
912 dm_table_entry_t *table_en;
913 dm_dev_t *dmv;
914 const char *name, *uuid;
915 uint32_t flags, minor;
916 uint64_t table_start, table_end, sector;
917 char *msg;
918 int ret, found = 0;
919
920 flags = 0;
921 name = NULL;
922 uuid = NULL;
923
924 /* Get needed values from dictionary. */
925 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
926 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
927 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
928 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
929 prop_dictionary_get_uint64(dm_dict, DM_MESSAGE_SECTOR, &sector);
930
931 dm_dbg_print_flags(flags);
932
933 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
934 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
935 return ENOENT;
936 }
937
938 /* Get message string */
939 prop_dictionary_get_cstring(dm_dict, DM_MESSAGE_STR, &msg);
940
941 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE);
942
943 ret = EINVAL;
944
945 if (sector == 0) {
946 if (!SLIST_EMPTY(tbl)) {
947 table_en = SLIST_FIRST(tbl);
948 found = 1;
949 }
950 } else {
951 SLIST_FOREACH(table_en, tbl, next) {
952 table_start = table_en->start;
953 table_end = table_start + (table_en->length);
954
955 if ((sector >= table_start) && (sector < table_end)) {
956 found = 1;
957 break;
958 }
959 }
960 }
961
962 if (found) {
963 if (table_en->target->message != NULL)
964 ret = table_en->target->message(table_en, msg);
965 }
966
967 dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE);
968
969
970 kfree(msg, M_TEMP);
971 dm_dev_unbusy(dmv);
972
973 return ret;
974}
ff56536e
AH
975
976/*
977 * For every call I have to set kernel driver version.
978 * Because I can have commands supported only in other
979 * newer/later version. This routine is called for every
980 * ioctl command.
981 */
982int
983dm_check_version(prop_dictionary_t dm_dict)
984{
985 size_t i;
986 int dm_version[3];
987 prop_array_t ver;
988
989 ver = prop_dictionary_get(dm_dict, DM_IOCTL_VERSION);
990
991 for (i = 0; i < 3; i++)
992 prop_array_get_uint32(ver, i, &dm_version[i]);
993
994 if (DM_VERSION_MAJOR != dm_version[0] || DM_VERSION_MINOR < dm_version[1]) {
995 aprint_debug("libdevmapper/kernel version mismatch "
996 "kernel: %d.%d.%d libdevmapper: %d.%d.%d\n",
997 DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL,
998 dm_version[0], dm_version[1], dm_version[2]);
999
1000 return EIO;
1001 }
1002 prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR);
1003 prop_array_set_uint32(ver, 1, DM_VERSION_MINOR);
1004 prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL);
1005
1006 prop_dictionary_set(dm_dict, DM_IOCTL_VERSION, ver);
1007
1008 return 0;
1009}