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