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