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