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