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