dm_ioctl.c revision 1.48 1 /* $NetBSD: dm_ioctl.c,v 1.48 2019/12/21 11:59:03 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.48 2019/12/21 11:59:03 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 #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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
206 prop_dictionary_get_cstring_nocopy(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
327 prop_dictionary_get_cstring_nocopy(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_cstring_nocopy(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_cstring(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
379 prop_dictionary_get_cstring_nocopy(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
419 prop_dictionary_get_cstring_nocopy(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_cstring(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
473 prop_dictionary_get_cstring_nocopy(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
518 prop_dictionary_get_cstring_nocopy(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
574 prop_dictionary_get_cstring_nocopy(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
619 prop_dictionary_get_cstring_nocopy(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_cstring(dm_dict, DM_IOCTL_NAME, dmv->name);
632 prop_dictionary_set_cstring(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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
724 prop_dictionary_get_cstring_nocopy(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 char *str = NULL;
757
758 prop_dictionary_get_cstring_nocopy(target_dict,
759 DM_TABLE_TYPE, &type);
760 /*
761 * If we want to deny table with 2 or more different
762 * target we should do it here
763 */
764 if (((target = dm_target_lookup(type)) == NULL) &&
765 ((target = dm_target_autoload(type)) == NULL)) {
766 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
767 dm_dev_unbusy(dmv);
768 prop_object_iterator_release(iter);
769 return ENOENT;
770 }
771 table_en = kmem_alloc(sizeof(dm_table_entry_t), KM_SLEEP);
772 prop_dictionary_get_uint64(target_dict, DM_TABLE_START,
773 &table_en->start);
774 prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH,
775 &table_en->length);
776
777 table_en->target = target;
778 table_en->dm_dev = dmv;
779 table_en->target_config = NULL;
780 TAILQ_INIT(&table_en->pdev_maps);
781
782 /*
783 * There is a parameter string after dm_target_spec
784 * structure which points to /dev/wd0a 284 part of
785 * table. String str points to this text. This can be
786 * null and therefore it should be checked before we try to
787 * use it.
788 */
789 prop_dictionary_get_cstring(target_dict,
790 DM_TABLE_PARAMS, (char **) &str);
791
792 if (SLIST_EMPTY(tbl) || last_table == NULL)
793 /* insert this table to head */
794 SLIST_INSERT_HEAD(tbl, table_en, next);
795 else
796 SLIST_INSERT_AFTER(last_table, table_en, next);
797
798 if ((ret = dm_table_init(target, table_en, str)) != 0) {
799 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
800 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
801 free(str, M_TEMP);
802
803 dm_dev_unbusy(dmv);
804 prop_object_iterator_release(iter);
805 return ret;
806 }
807 last_table = table_en;
808 free(str, M_TEMP);
809 }
810 prop_object_iterator_release(iter);
811
812 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
813 atomic_or_32(&dmv->flags, DM_INACTIVE_PRESENT_FLAG);
814
815 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
816 dm_dev_unbusy(dmv);
817 return 0;
818 }
819
820 static int
821 dm_table_init(dm_target_t *target, dm_table_entry_t *table_en, char *params)
822 {
823 int i, n, ret, argc;
824 char **ap, **argv;
825
826 if (params == NULL)
827 return EINVAL;
828
829 n = target->max_argc;
830 if (n)
831 aprint_debug("Max argc %d for %s target\n", n, target->name);
832 else
833 n = 32; /* large enough for most targets */
834
835 argv = kmem_alloc(sizeof(*argv) * n, KM_SLEEP);
836
837 for (ap = argv;
838 ap < &argv[n] && (*ap = strsep(¶ms, " \t")) != NULL;) {
839 if (**ap != '\0')
840 ap++;
841 }
842 argc = ap - argv;
843
844 for (i = 0; i < argc; i++)
845 aprint_debug("DM: argv[%d] = \"%s\"\n", i, argv[i]);
846
847 KASSERT(target->init);
848 ret = target->init(table_en, argc, argv);
849
850 kmem_free(argv, sizeof(*argv) * n);
851
852 return ret;
853 }
854
855 /*
856 * Get description of all tables loaded to device from kernel
857 * and send it to libdevmapper.
858 *
859 * Output dictionary for every table:
860 *
861 * <key>cmd_data</key>
862 * <array>
863 * <dict>
864 * <key>type<key>
865 * <string>...</string>
866 *
867 * <key>start</key>
868 * <integer>...</integer>
869 *
870 * <key>length</key>
871 * <integer>...</integer>
872 *
873 * <key>params</key>
874 * <string>...</string>
875 * </dict>
876 * </array>
877 */
878 int
879 dm_table_status_ioctl(prop_dictionary_t dm_dict)
880 {
881 dm_dev_t *dmv;
882 dm_table_t *tbl;
883 dm_table_entry_t *table_en;
884
885 prop_array_t cmd_array;
886 prop_dictionary_t target_dict;
887
888 uint32_t minor, flags;
889 const char *name, *uuid;
890 int table_type;
891
892 uuid = NULL;
893 name = NULL;
894 flags = 0;
895
896 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
897 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
898 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
899 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
900
901 cmd_array = prop_array_create();
902
903 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
904 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
905 return ENOENT;
906 }
907 /*
908 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query
909 * INACTIVE TABLE
910 */
911 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG)
912 table_type = DM_TABLE_INACTIVE;
913 else
914 table_type = DM_TABLE_ACTIVE;
915
916 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE)) {
917 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
918 } else {
919 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
920
921 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE))
922 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
923 else
924 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
925 }
926
927 if (dmv->flags & DM_SUSPEND_FLAG)
928 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG);
929
930 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
931
932 aprint_debug("Status of device tables: %s--%d\n",
933 name, dmv->table_head.cur_active_table);
934
935 tbl = dm_table_get_entry(&dmv->table_head, table_type);
936
937 SLIST_FOREACH(table_en, tbl, next) {
938 char *params;
939
940 target_dict = prop_dictionary_create();
941 aprint_debug("%016" PRIu64 ", length %016" PRIu64
942 ", target %s\n", table_en->start, table_en->length,
943 table_en->target->name);
944
945 prop_dictionary_set_uint64(target_dict, DM_TABLE_START,
946 table_en->start);
947 prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH,
948 table_en->length);
949
950 prop_dictionary_set_cstring(target_dict, DM_TABLE_TYPE,
951 table_en->target->name);
952
953 /* dm_table_get_cur_actv.table ?? */
954 prop_dictionary_set_int32(target_dict, DM_TABLE_STAT,
955 dmv->table_head.cur_active_table);
956
957 /*
958 * Explicitly clear DM_TABLE_PARAMS to prevent dmsetup(8) from
959 * printing junk when DM_TABLE_PARAMS was never initialized.
960 */
961 prop_dictionary_set_cstring(target_dict, DM_TABLE_PARAMS, "");
962
963 if (flags & DM_STATUS_TABLE_FLAG)
964 params = table_en->target->table(
965 table_en->target_config);
966 else if (table_en->target->info)
967 params = table_en->target->info(
968 table_en->target_config);
969 else
970 params = NULL;
971
972 if (params != NULL) {
973 prop_dictionary_set_cstring(target_dict,
974 DM_TABLE_PARAMS, params);
975 kmem_free(params, DM_MAX_PARAMS_SIZE);
976 }
977
978 prop_array_add(cmd_array, target_dict);
979 prop_object_release(target_dict);
980 }
981
982 dm_table_release(&dmv->table_head, table_type);
983 dm_dev_unbusy(dmv);
984
985 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags);
986 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array);
987 prop_object_release(cmd_array);
988
989 return 0;
990 }
991
992 /*
993 * For every call I have to set kernel driver version.
994 * Because I can have commands supported only in other
995 * newer/later version. This routine is called for every
996 * ioctl command.
997 */
998 int
999 dm_check_version(prop_dictionary_t dm_dict)
1000 {
1001 int i;
1002 uint32_t dm_version[3];
1003 prop_array_t ver;
1004
1005 ver = prop_dictionary_get(dm_dict, DM_IOCTL_VERSION);
1006
1007 for (i = 0; i < 3; i++)
1008 prop_array_get_uint32(ver, i, &dm_version[i]);
1009
1010 if (DM_VERSION_MAJOR != dm_version[0] || DM_VERSION_MINOR < dm_version[1]) {
1011 aprint_debug("libdevmapper/kernel version mismatch "
1012 "kernel: %d.%d.%d libdevmapper: %d.%d.%d\n",
1013 DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL,
1014 dm_version[0], dm_version[1], dm_version[2]);
1015
1016 return EIO;
1017 }
1018 prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR);
1019 prop_array_set_uint32(ver, 1, DM_VERSION_MINOR);
1020 prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL);
1021
1022 prop_dictionary_set(dm_dict, DM_IOCTL_VERSION, ver);
1023
1024 return 0;
1025 }
1026