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