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