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