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