dm_ioctl.c revision 1.50 1 /* $NetBSD: dm_ioctl.c,v 1.50 2020/07/08 15:07:13 thorpej 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.50 2020/07/08 15:07:13 thorpej 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 #include <sys/device.h>
86 #include <sys/disk.h>
87 #include <sys/disklabel.h>
88 #include <sys/kmem.h>
89 #include <sys/malloc.h>
90
91 #include <machine/int_fmtio.h>
92
93 #include "netbsd-dm.h"
94 #include "dm.h"
95
96 static uint32_t sc_minor_num;
97 uint32_t dm_dev_counter;
98
99 /* Generic cf_data for device-mapper driver */
100 static struct cfdata dm_cfdata = {
101 .cf_name = "dm",
102 .cf_atname = "dm",
103 .cf_fstate = FSTATE_STAR,
104 .cf_unit = 0
105 };
106
107 #define DM_REMOVE_FLAG(flag, name) do { \
108 prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \
109 flag &= ~name; \
110 prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \
111 } while (/*CONSTCOND*/0)
112
113 #define DM_ADD_FLAG(flag, name) do { \
114 prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \
115 flag |= name; \
116 prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \
117 } while (/*CONSTCOND*/0)
118
119 static int dm_table_deps(dm_table_entry_t *, prop_array_t);
120 static int dm_table_init(dm_target_t *, dm_table_entry_t *, char *);
121
122 /*
123 * Print flags sent to the kernel from libdevmapper.
124 */
125 static int
126 dm_dbg_print_flags(uint32_t flags)
127 {
128
129 aprint_debug("dbg_print --- %d\n", flags);
130
131 if (flags & DM_READONLY_FLAG)
132 aprint_debug("dbg_flags: DM_READONLY_FLAG set In/Out\n");
133
134 if (flags & DM_SUSPEND_FLAG)
135 aprint_debug("dbg_flags: DM_SUSPEND_FLAG set In/Out\n");
136
137 if (flags & DM_PERSISTENT_DEV_FLAG)
138 aprint_debug("dbg_flags: DM_PERSISTENT_DEV_FLAG set In\n");
139
140 if (flags & DM_STATUS_TABLE_FLAG)
141 aprint_debug("dbg_flags: DM_STATUS_TABLE_FLAG set In\n");
142
143 if (flags & DM_ACTIVE_PRESENT_FLAG)
144 aprint_debug("dbg_flags: DM_ACTIVE_PRESENT_FLAG set Out\n");
145
146 if (flags & DM_INACTIVE_PRESENT_FLAG)
147 aprint_debug("dbg_flags: DM_INACTIVE_PRESENT_FLAG set Out\n");
148
149 if (flags & DM_BUFFER_FULL_FLAG)
150 aprint_debug("dbg_flags: DM_BUFFER_FULL_FLAG set Out\n");
151
152 if (flags & DM_SKIP_BDGET_FLAG)
153 aprint_debug("dbg_flags: DM_SKIP_BDGET_FLAG set In\n");
154
155 if (flags & DM_SKIP_LOCKFS_FLAG)
156 aprint_debug("dbg_flags: DM_SKIP_LOCKFS_FLAG set In\n");
157
158 if (flags & DM_NOFLUSH_FLAG)
159 aprint_debug("dbg_flags: DM_NOFLUSH_FLAG set In\n");
160
161 return 0;
162 }
163
164 /*
165 * Get list of all available targets from global
166 * target list and sent them back to libdevmapper.
167 */
168 int
169 dm_list_versions_ioctl(prop_dictionary_t dm_dict)
170 {
171 prop_array_t target_list;
172 uint32_t flags;
173
174 flags = 0;
175
176 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
177
178 dm_dbg_print_flags(flags);
179 target_list = dm_target_prop_list();
180
181 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, target_list);
182 prop_object_release(target_list);
183
184 return 0;
185 }
186
187 /*
188 * Create in-kernel entry for device. Device attributes such as name, uuid are
189 * taken from proplib dictionary.
190 */
191 int
192 dm_dev_create_ioctl(prop_dictionary_t dm_dict)
193 {
194 dm_dev_t *dmv;
195 const char *name, *uuid;
196 int r;
197 uint32_t flags;
198 device_t devt;
199
200 flags = 0;
201 name = NULL;
202 uuid = NULL;
203
204 /* Get needed values from dictionary. */
205 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name);
206 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid);
207 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
208
209 dm_dbg_print_flags(flags);
210
211 /* Lookup name and uuid if device already exist quit. */
212 if ((dmv = dm_dev_lookup(name, uuid, -1)) != NULL) {
213 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); /* Device already exists */
214 dm_dev_unbusy(dmv);
215 return EEXIST;
216 }
217 if ((devt = config_attach_pseudo(&dm_cfdata)) == NULL) {
218 aprint_error("Unable to attach pseudo device dm/%s\n", name);
219 return (ENOMEM);
220 }
221 if ((dmv = dm_dev_alloc()) == NULL)
222 return ENOMEM;
223
224 if (uuid)
225 strncpy(dmv->uuid, uuid, DM_UUID_LEN);
226 else
227 dmv->uuid[0] = '\0';
228
229 if (name)
230 strlcpy(dmv->name, name, DM_NAME_LEN);
231
232 dmv->minor = (uint64_t)atomic_inc_32_nv(&sc_minor_num);
233 dmv->flags = 0; /* device flags are set when needed */
234 dmv->ref_cnt = 0;
235 dmv->event_nr = 0;
236 dmv->devt = devt;
237
238 dm_table_head_init(&dmv->table_head);
239
240 mutex_init(&dmv->dev_mtx, MUTEX_DEFAULT, IPL_NONE);
241 mutex_init(&dmv->diskp_mtx, MUTEX_DEFAULT, IPL_NONE);
242 cv_init(&dmv->dev_cv, "dm_dev");
243
244 if (flags & DM_READONLY_FLAG)
245 dmv->flags |= DM_READONLY_FLAG;
246
247 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
248
249 disk_init(dmv->diskp, dmv->name, &dmdkdriver);
250 disk_attach(dmv->diskp);
251
252 dmv->diskp->dk_info = NULL;
253
254 if ((r = dm_dev_insert(dmv)) != 0)
255 dm_dev_free(dmv);
256
257 DM_ADD_FLAG(flags, DM_EXISTS_FLAG);
258 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
259
260 /* Increment device counter After creating device */
261 atomic_inc_32(&dm_dev_counter);
262
263 return r;
264 }
265
266 /*
267 * Get list of created device-mapper devices fromglobal list and
268 * send it to kernel.
269 *
270 * Output dictionary:
271 *
272 * <key>cmd_data</key>
273 * <array>
274 * <dict>
275 * <key>name<key>
276 * <string>...</string>
277 *
278 * <key>dev</key>
279 * <integer>...</integer>
280 * </dict>
281 * </array>
282 */
283 int
284 dm_dev_list_ioctl(prop_dictionary_t dm_dict)
285 {
286 prop_array_t dev_list;
287 uint32_t flags;
288
289 flags = 0;
290
291 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
292
293 dm_dbg_print_flags(flags);
294
295 dev_list = dm_dev_prop_list();
296
297 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, dev_list);
298 prop_object_release(dev_list);
299
300 return 0;
301 }
302
303 /*
304 * Rename selected devices old name is in struct dm_ioctl.
305 * newname is taken from dictionary
306 *
307 * <key>cmd_data</key>
308 * <array>
309 * <string>...</string>
310 * </array>
311 */
312 int
313 dm_dev_rename_ioctl(prop_dictionary_t dm_dict)
314 {
315 prop_array_t cmd_array;
316 dm_dev_t *dmv;
317
318 const char *name, *uuid, *n_name;
319 uint32_t flags, minor;
320
321 name = NULL;
322 uuid = NULL;
323 minor = 0;
324
325 /* Get needed values from dictionary. */
326 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name);
327 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid);
328 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
329 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
330
331 dm_dbg_print_flags(flags);
332
333 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA);
334
335 prop_array_get_string(cmd_array, 0, &n_name);
336
337 if (strlen(n_name) + 1 > DM_NAME_LEN)
338 return EINVAL;
339
340 if ((dmv = dm_dev_rem(name, uuid, minor)) == NULL) {
341 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
342 return ENOENT;
343 }
344 /* change device name */
345 /*
346 * XXX How to deal with this change, name only used in
347 * dm_dev_routines, should I add dm_dev_change_name which will run
348 * under the dm_dev_list mutex ?
349 */
350 strlcpy(dmv->name, n_name, DM_NAME_LEN);
351
352 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
353 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
354 prop_dictionary_set_string(dm_dict, DM_IOCTL_UUID, dmv->uuid);
355
356 dm_dev_insert(dmv);
357
358 return 0;
359 }
360
361 /*
362 * Remove device from global list I have to remove active
363 * and inactive tables first.
364 */
365 int
366 dm_dev_remove_ioctl(prop_dictionary_t dm_dict)
367 {
368 dm_dev_t *dmv;
369 const char *name, *uuid;
370 uint32_t flags, minor;
371 device_t devt;
372
373 flags = 0;
374 name = NULL;
375 uuid = NULL;
376
377 /* Get needed values from dictionary. */
378 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name);
379 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid);
380 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
381 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
382
383 dm_dbg_print_flags(flags);
384
385 /*
386 * This seems as hack to me, probably use routine dm_dev_get_devt to
387 * atomicaly get devt from device.
388 */
389 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
390 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
391 return ENOENT;
392 }
393 devt = dmv->devt;
394
395 dm_dev_unbusy(dmv);
396
397 /*
398 * This will call dm_detach routine which will actually removes
399 * device.
400 */
401 return config_detach(devt, DETACH_QUIET);
402 }
403
404 /*
405 * Return actual state of device to libdevmapper.
406 */
407 int
408 dm_dev_status_ioctl(prop_dictionary_t dm_dict)
409 {
410 dm_dev_t *dmv;
411 const char *name, *uuid;
412 uint32_t flags, j, minor;
413
414 name = NULL;
415 uuid = NULL;
416 flags = 0;
417
418 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name);
419 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid);
420 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
421 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
422
423 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
424 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
425 return ENOENT;
426 }
427 dm_dbg_print_flags(dmv->flags);
428
429 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
430 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
431 prop_dictionary_set_string(dm_dict, DM_IOCTL_UUID, dmv->uuid);
432
433 if (dmv->flags & DM_SUSPEND_FLAG)
434 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG);
435
436 /*
437 * Add status flags for tables I have to check both active and
438 * inactive tables.
439 */
440 if ((j = dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE)))
441 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
442 else
443 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
444
445 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_TARGET_COUNT, j);
446
447 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE))
448 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
449 else
450 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
451
452 dm_dev_unbusy(dmv);
453
454 return 0;
455 }
456
457 /*
458 * Set only flag to suggest that device is suspended. This call is
459 * not supported in NetBSD.
460 */
461 int
462 dm_dev_suspend_ioctl(prop_dictionary_t dm_dict)
463 {
464 dm_dev_t *dmv;
465 const char *name, *uuid;
466 uint32_t flags, minor;
467
468 name = NULL;
469 uuid = NULL;
470 flags = 0;
471
472 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name);
473 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid);
474 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
475 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
476
477 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
478 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
479 return ENOENT;
480 }
481 atomic_or_32(&dmv->flags, DM_SUSPEND_FLAG);
482
483 dm_dbg_print_flags(dmv->flags);
484
485 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
486 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, dmv->flags);
487 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
488
489 dm_dev_unbusy(dmv);
490
491 /* Add flags to dictionary flag after dmv -> dict copy */
492 DM_ADD_FLAG(flags, DM_EXISTS_FLAG);
493
494 return 0;
495 }
496
497 /*
498 * Simulate Linux behaviour better and switch tables here and not in
499 * dm_table_load_ioctl.
500 */
501 int
502 dm_dev_resume_ioctl(prop_dictionary_t dm_dict)
503 {
504 dm_dev_t *dmv;
505 const char *name, *uuid;
506 uint32_t flags, minor;
507
508 name = NULL;
509 uuid = NULL;
510 flags = 0;
511
512 /*
513 * char *xml; xml = prop_dictionary_externalize(dm_dict);
514 * printf("%s\n",xml);
515 */
516
517 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name);
518 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid);
519 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
520 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
521
522 /* Remove device from global device list */
523 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
524 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
525 return ENOENT;
526 }
527
528 /* Make inactive table active, if it exists */
529 if (dmv->flags & DM_INACTIVE_PRESENT_FLAG)
530 dm_table_switch_tables(&dmv->table_head);
531
532 atomic_and_32(&dmv->flags, ~(DM_SUSPEND_FLAG | DM_INACTIVE_PRESENT_FLAG));
533 atomic_or_32(&dmv->flags, DM_ACTIVE_PRESENT_FLAG);
534
535 DM_ADD_FLAG(flags, DM_EXISTS_FLAG);
536
537 dmgetproperties(dmv->diskp, &dmv->table_head);
538
539 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
540 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags);
541 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
542
543 dm_dev_unbusy(dmv);
544
545 /* Destroy inactive table after resume. */
546 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
547
548 return 0;
549 }
550
551 /*
552 * Table management routines
553 * lvm2tools doens't send name/uuid to kernel with table
554 * for lookup I have to use minor number.
555 */
556
557 /*
558 * Remove inactive table from device. Routines which work's with inactive tables
559 * doesn't need to synchronise with dmstrategy. They can synchronise themselves with mutex?.
560 */
561 int
562 dm_table_clear_ioctl(prop_dictionary_t dm_dict)
563 {
564 dm_dev_t *dmv;
565 const char *name, *uuid;
566 uint32_t flags, minor;
567
568 name = NULL;
569 uuid = NULL;
570 flags = 0;
571 minor = 0;
572
573 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name);
574 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid);
575 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
576 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
577
578 aprint_debug("Clearing inactive table from device: %s--%s\n",
579 name, uuid);
580
581 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
582 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
583 return ENOENT;
584 }
585 /* Select unused table */
586 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
587
588 atomic_and_32(&dmv->flags, ~DM_INACTIVE_PRESENT_FLAG);
589
590 dm_dev_unbusy(dmv);
591
592 return 0;
593 }
594
595 /*
596 * Get list of physical devices for active table.
597 * Get dev_t from pdev vnode and insert it into cmd_array.
598 *
599 * XXX. This function is called from lvm2tools to get information
600 * about physical devices, too e.g. during vgcreate.
601 */
602 int
603 dm_table_deps_ioctl(prop_dictionary_t dm_dict)
604 {
605 dm_dev_t *dmv;
606 dm_table_t *tbl;
607 dm_table_entry_t *table_en;
608
609 prop_array_t cmd_array;
610 const char *name, *uuid;
611 uint32_t flags, minor;
612 int table_type;
613
614 name = NULL;
615 uuid = NULL;
616 flags = 0;
617
618 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name);
619 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid);
620 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
621 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
622
623 /* create array for dev_t's */
624 cmd_array = prop_array_create();
625
626 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
627 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
628 return ENOENT;
629 }
630 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
631 prop_dictionary_set_string(dm_dict, DM_IOCTL_NAME, dmv->name);
632 prop_dictionary_set_string(dm_dict, DM_IOCTL_UUID, dmv->uuid);
633
634 aprint_debug("Getting table deps for device: %s\n", dmv->name);
635
636 /*
637 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query
638 * INACTIVE TABLE
639 */
640 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG)
641 table_type = DM_TABLE_INACTIVE;
642 else
643 table_type = DM_TABLE_ACTIVE;
644
645 tbl = dm_table_get_entry(&dmv->table_head, table_type);
646
647 SLIST_FOREACH(table_en, tbl, next)
648 dm_table_deps(table_en, cmd_array);
649
650 dm_table_release(&dmv->table_head, table_type);
651 dm_dev_unbusy(dmv);
652
653 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array);
654 prop_object_release(cmd_array);
655
656 return 0;
657 }
658
659 static int
660 dm_table_deps(dm_table_entry_t *table_en, prop_array_t array)
661 {
662 dm_mapping_t *map;
663 int i, size;
664 uint64_t rdev, tmp;
665
666 size = prop_array_count(array);
667
668 TAILQ_FOREACH(map, &table_en->pdev_maps, next) {
669 rdev = map->data.pdev->pdev_vnode->v_rdev;
670 for (i = 0; i < size; i++) {
671 if (prop_array_get_uint64(array, i, &tmp) == true)
672 if (rdev == tmp)
673 break; /* exists */
674 }
675 /*
676 * Ignore if the device has already been added by
677 * other tables.
678 */
679 if (i == size) {
680 prop_array_add_uint64(array, rdev);
681 aprint_debug("%s: %d:%d\n", __func__, major(rdev),
682 minor(rdev));
683 }
684 }
685
686 return 0;
687 }
688
689 /*
690 * Load new table/tables to device.
691 * Call apropriate target init routine open all physical pdev's and
692 * link them to device. For other targets mirror, strip, snapshot
693 * etc. also add dependency devices to upcalls list.
694 *
695 * Load table to inactive slot table are switched in dm_device_resume_ioctl.
696 * This simulates Linux behaviour better there should not be any difference.
697 */
698 int
699 dm_table_load_ioctl(prop_dictionary_t dm_dict)
700 {
701 dm_dev_t *dmv;
702 dm_table_entry_t *table_en, *last_table;
703 dm_table_t *tbl;
704 dm_target_t *target;
705
706 prop_object_iterator_t iter;
707 prop_array_t cmd_array;
708 prop_dictionary_t target_dict;
709
710 const char *name, *uuid, *type;
711 uint32_t flags, minor;
712
713 flags = 0;
714 name = NULL;
715 uuid = NULL;
716 last_table = NULL;
717
718 /*
719 * char *xml; xml = prop_dictionary_externalize(dm_dict);
720 * printf("%s\n",xml);
721 */
722
723 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name);
724 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid);
725 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
726 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
727
728 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA);
729 iter = prop_array_iterator(cmd_array);
730 dm_dbg_print_flags(flags);
731
732 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
733 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
734 prop_object_iterator_release(iter);
735 return ENOENT;
736 }
737 aprint_debug("Loading table to device: %s--%d\n", name,
738 dmv->table_head.cur_active_table);
739
740 /*
741 * I have to check if this table slot is not used by another table list.
742 * if it is used I should free them.
743 */
744 if (dmv->flags & DM_INACTIVE_PRESENT_FLAG)
745 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
746
747 dm_dbg_print_flags(dmv->flags);
748 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_INACTIVE);
749
750 aprint_debug("dmv->name = %s\n", dmv->name);
751
752 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
753
754 while ((target_dict = prop_object_iterator_next(iter)) != NULL) {
755 int ret;
756 const char *cp;
757 char *str;
758 size_t strsz;
759
760 prop_dictionary_get_string(target_dict,
761 DM_TABLE_TYPE, &type);
762 /*
763 * If we want to deny table with 2 or more different
764 * target we should do it here
765 */
766 if (((target = dm_target_lookup(type)) == NULL) &&
767 ((target = dm_target_autoload(type)) == NULL)) {
768 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
769 dm_dev_unbusy(dmv);
770 prop_object_iterator_release(iter);
771 return ENOENT;
772 }
773 table_en = kmem_alloc(sizeof(dm_table_entry_t), KM_SLEEP);
774 prop_dictionary_get_uint64(target_dict, DM_TABLE_START,
775 &table_en->start);
776 prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH,
777 &table_en->length);
778
779 table_en->target = target;
780 table_en->dm_dev = dmv;
781 table_en->target_config = NULL;
782 TAILQ_INIT(&table_en->pdev_maps);
783
784 /*
785 * There is a parameter string after dm_target_spec
786 * structure which points to /dev/wd0a 284 part of
787 * table. String str points to this text. This can be
788 * null and therefore it should be checked before we try to
789 * use it.
790 */
791 cp = NULL;
792 prop_dictionary_get_string(target_dict,
793 DM_TABLE_PARAMS, &cp);
794 if (cp == NULL)
795 str = NULL;
796 else
797 str = kmem_strdupsize(cp, &strsz, KM_SLEEP);
798
799 if (SLIST_EMPTY(tbl) || last_table == NULL)
800 /* insert this table to head */
801 SLIST_INSERT_HEAD(tbl, table_en, next);
802 else
803 SLIST_INSERT_AFTER(last_table, table_en, next);
804
805 if ((ret = dm_table_init(target, table_en, str)) != 0) {
806 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
807 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
808
809 if (str != NULL)
810 kmem_free(str, strsz);
811
812 dm_dev_unbusy(dmv);
813 prop_object_iterator_release(iter);
814 return ret;
815 }
816 last_table = table_en;
817 if (str != NULL)
818 kmem_free(str, strsz);
819 }
820 prop_object_iterator_release(iter);
821
822 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
823 atomic_or_32(&dmv->flags, DM_INACTIVE_PRESENT_FLAG);
824
825 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
826 dm_dev_unbusy(dmv);
827 return 0;
828 }
829
830 static int
831 dm_table_init(dm_target_t *target, dm_table_entry_t *table_en, char *params)
832 {
833 int i, n, ret, argc;
834 char **ap, **argv;
835
836 if (params == NULL)
837 return EINVAL;
838
839 n = target->max_argc;
840 if (n)
841 aprint_debug("Max argc %d for %s target\n", n, target->name);
842 else
843 n = 32; /* large enough for most targets */
844
845 argv = kmem_alloc(sizeof(*argv) * n, KM_SLEEP);
846
847 for (ap = argv;
848 ap < &argv[n] && (*ap = strsep(¶ms, " \t")) != NULL;) {
849 if (**ap != '\0')
850 ap++;
851 }
852 argc = ap - argv;
853
854 for (i = 0; i < argc; i++)
855 aprint_debug("DM: argv[%d] = \"%s\"\n", i, argv[i]);
856
857 KASSERT(target->init);
858 ret = target->init(table_en, argc, argv);
859
860 kmem_free(argv, sizeof(*argv) * n);
861
862 return ret;
863 }
864
865 /*
866 * Get description of all tables loaded to device from kernel
867 * and send it to libdevmapper.
868 *
869 * Output dictionary for every table:
870 *
871 * <key>cmd_data</key>
872 * <array>
873 * <dict>
874 * <key>type<key>
875 * <string>...</string>
876 *
877 * <key>start</key>
878 * <integer>...</integer>
879 *
880 * <key>length</key>
881 * <integer>...</integer>
882 *
883 * <key>params</key>
884 * <string>...</string>
885 * </dict>
886 * </array>
887 */
888 int
889 dm_table_status_ioctl(prop_dictionary_t dm_dict)
890 {
891 dm_dev_t *dmv;
892 dm_table_t *tbl;
893 dm_table_entry_t *table_en;
894
895 prop_array_t cmd_array;
896 prop_dictionary_t target_dict;
897
898 uint32_t minor, flags;
899 const char *name, *uuid;
900 int table_type;
901
902 uuid = NULL;
903 name = NULL;
904 flags = 0;
905
906 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
907 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
908 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
909 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
910
911 cmd_array = prop_array_create();
912
913 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
914 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
915 return ENOENT;
916 }
917 /*
918 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query
919 * INACTIVE TABLE
920 */
921 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG)
922 table_type = DM_TABLE_INACTIVE;
923 else
924 table_type = DM_TABLE_ACTIVE;
925
926 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE)) {
927 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
928 } else {
929 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
930
931 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE))
932 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
933 else
934 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
935 }
936
937 if (dmv->flags & DM_SUSPEND_FLAG)
938 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG);
939
940 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
941
942 aprint_debug("Status of device tables: %s--%d\n",
943 name, dmv->table_head.cur_active_table);
944
945 tbl = dm_table_get_entry(&dmv->table_head, table_type);
946
947 SLIST_FOREACH(table_en, tbl, next) {
948 char *params;
949 int is_table;
950
951 target_dict = prop_dictionary_create();
952 aprint_debug("%016" PRIu64 ", length %016" PRIu64
953 ", target %s\n", table_en->start, table_en->length,
954 table_en->target->name);
955
956 prop_dictionary_set_uint64(target_dict, DM_TABLE_START,
957 table_en->start);
958 prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH,
959 table_en->length);
960
961 prop_dictionary_set_string(target_dict, DM_TABLE_TYPE,
962 table_en->target->name);
963
964 /* dm_table_get_cur_actv.table ?? */
965 prop_dictionary_set_int32(target_dict, DM_TABLE_STAT,
966 dmv->table_head.cur_active_table);
967
968 /*
969 * Explicitly clear DM_TABLE_PARAMS to prevent dmsetup(8) from
970 * printing junk when DM_TABLE_PARAMS was never initialized.
971 */
972 prop_dictionary_set_string(target_dict, DM_TABLE_PARAMS, "");
973
974 is_table = (flags & DM_STATUS_TABLE_FLAG) ? 1 : 0;
975 if (is_table && table_en->target->table)
976 params = table_en->target->table(
977 table_en->target_config);
978 else if (!is_table && table_en->target->info)
979 params = table_en->target->info(
980 table_en->target_config);
981 else
982 params = NULL;
983
984 if (params != NULL) {
985 prop_dictionary_set_string(target_dict,
986 DM_TABLE_PARAMS, params);
987 kmem_free(params, DM_MAX_PARAMS_SIZE);
988 }
989
990 prop_array_add(cmd_array, target_dict);
991 prop_object_release(target_dict);
992 }
993
994 dm_table_release(&dmv->table_head, table_type);
995 dm_dev_unbusy(dmv);
996
997 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags);
998 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array);
999 prop_object_release(cmd_array);
1000
1001 return 0;
1002 }
1003
1004 /*
1005 * For every call I have to set kernel driver version.
1006 * Because I can have commands supported only in other
1007 * newer/later version. This routine is called for every
1008 * ioctl command.
1009 */
1010 int
1011 dm_check_version(prop_dictionary_t dm_dict)
1012 {
1013 int i;
1014 uint32_t dm_version[3];
1015 prop_array_t ver;
1016
1017 ver = prop_dictionary_get(dm_dict, DM_IOCTL_VERSION);
1018
1019 for (i = 0; i < 3; i++)
1020 prop_array_get_uint32(ver, i, &dm_version[i]);
1021
1022 if (DM_VERSION_MAJOR != dm_version[0] || DM_VERSION_MINOR < dm_version[1]) {
1023 aprint_debug("libdevmapper/kernel version mismatch "
1024 "kernel: %d.%d.%d libdevmapper: %d.%d.%d\n",
1025 DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL,
1026 dm_version[0], dm_version[1], dm_version[2]);
1027
1028 return EIO;
1029 }
1030 prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR);
1031 prop_array_set_uint32(ver, 1, DM_VERSION_MINOR);
1032 prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL);
1033
1034 prop_dictionary_set(dm_dict, DM_IOCTL_VERSION, ver);
1035
1036 return 0;
1037 }
1038