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