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