]> sourceware.org Git - lvm2.git/blame - libdm/libdm-deptree.c
precommitted flag
[lvm2.git] / libdm / libdm-deptree.c
CommitLineData
3d0480ed
AK
1/*
2 * Copyright (C) 2005 Red Hat, Inc. All rights reserved.
3 *
4 * This file is part of the device-mapper userspace tools.
5 *
6 * This copyrighted material is made available to anyone wishing to use,
7 * modify, copy, or redistribute it subject to the terms and conditions
8 * of the GNU Lesser General Public License v.2.1.
9 *
10 * You should have received a copy of the GNU Lesser General Public License
11 * along with this program; if not, write to the Free Software Foundation,
12 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
13 */
14
15#include "lib.h"
16#include "libdm-targets.h"
17#include "libdm-common.h"
18#include "list.h"
19#include "kdev_t.h"
3d0480ed
AK
20
21#include <stdarg.h>
22#include <sys/param.h>
23
24#include <linux/dm-ioctl.h>
25
26struct deptree_node {
27 struct deptree *deptree;
28
29 const char *name;
30 const char *uuid;
31 struct dm_info info;
32
33 struct list uses; /* Nodes this node uses */
34 struct list used_by; /* Nodes that use this node */
35};
36
37struct deptree {
a3f6b2ce
AK
38 struct dm_pool *mem;
39 struct dm_hash_table *devs;
3d0480ed
AK
40 struct deptree_node root;
41};
42
43struct deptree_link {
44 struct list list;
45 struct deptree_node *node;
46};
47
48struct deptree *dm_deptree_create(void)
49{
50 struct deptree *deptree;
51
a3f6b2ce 52 if (!(deptree = dm_malloc(sizeof(*deptree)))) {
3d0480ed
AK
53 log_error("dm_deptree_create malloc failed");
54 return NULL;
55 }
56
57 memset(deptree, 0, sizeof(*deptree));
58 deptree->root.deptree = deptree;
59 list_init(&deptree->root.uses);
60 list_init(&deptree->root.used_by);
61
a3f6b2ce 62 if (!(deptree->mem = dm_pool_create("deptree", 1024))) {
3d0480ed 63 log_error("deptree pool creation failed");
a3f6b2ce 64 dm_free(deptree);
3d0480ed
AK
65 return NULL;
66 }
67
a3f6b2ce 68 if (!(deptree->devs = dm_hash_create(8))) {
3d0480ed 69 log_error("deptree hash creation failed");
a3f6b2ce
AK
70 dm_pool_destroy(deptree->mem);
71 dm_free(deptree);
3d0480ed
AK
72 return NULL;
73 }
74
75 return deptree;
76}
77
78void dm_deptree_free(struct deptree *deptree)
79{
80 if (!deptree)
81 return;
82
a3f6b2ce
AK
83 dm_hash_destroy(deptree->devs);
84 dm_pool_destroy(deptree->mem);
85 dm_free(deptree);
3d0480ed
AK
86}
87
88static int _nodes_are_linked(struct deptree_node *parent,
89 struct deptree_node *child)
90{
91 struct deptree_link *dlink;
92
93 list_iterate_items(dlink, &parent->uses) {
94 if (dlink->node == child)
95 return 1;
96 }
97
98 return 0;
99}
100
101static int _link(struct list *list, struct deptree_node *node)
102{
103 struct deptree_link *dlink;
104
a3f6b2ce 105 if (!(dlink = dm_pool_alloc(node->deptree->mem, sizeof(*dlink)))) {
3d0480ed
AK
106 log_error("deptree link allocation failed");
107 return 0;
108 }
109
110 dlink->node = node;
111 list_add(list, &dlink->list);
112
113 return 1;
114}
115
116static int _link_nodes(struct deptree_node *parent,
117 struct deptree_node *child)
118{
119 if (_nodes_are_linked(parent, child))
120 return 1;
121
122 if (!_link(&parent->uses, child))
123 return 0;
124
125 if (!_link(&child->used_by, parent))
126 return 0;
127
128 return 1;
129}
130
131static void _unlink(struct list *list, struct deptree_node *node)
132{
133 struct deptree_link *dlink;
134
135 list_iterate_items(dlink, list) {
136 if (dlink->node == node) {
137 list_del(&dlink->list);
138 break;
139 }
140 }
141}
142
143static void _unlink_nodes(struct deptree_node *parent,
144 struct deptree_node *child)
145{
146 if (!_nodes_are_linked(parent, child))
147 return;
148
149 _unlink(&parent->uses, child);
150 _unlink(&child->used_by, parent);
151}
152
153static void _remove_from_toplevel(struct deptree_node *node)
154{
155 return _unlink_nodes(&node->deptree->root, node);
156}
157
158static int _add_to_bottomlevel(struct deptree_node *node)
159{
160 return _link_nodes(node, &node->deptree->root);
161}
162
163static struct deptree_node *_create_deptree_node(struct deptree *deptree,
164 struct deptree_node *parent,
165 const char *name,
166 const char *uuid,
167 struct dm_info *info)
168{
169 struct deptree_node *node;
170 uint64_t dev;
171
a3f6b2ce 172 if (!(node = dm_pool_zalloc(deptree->mem, sizeof(*node)))) {
3d0480ed
AK
173 log_error("_create_deptree_node alloc failed");
174 return NULL;
175 }
176
177 node->deptree = deptree;
178
179 node->name = name;
180 node->uuid = uuid;
181 node->info = *info;
182
183 list_init(&node->uses);
184 list_init(&node->used_by);
185
186 dev = MKDEV(info->major, info->minor);
187
a3f6b2ce 188 if (!dm_hash_insert_binary(deptree->devs, (const char *) &dev,
3d0480ed
AK
189 sizeof(dev), node)) {
190 log_error("deptree node hash insertion failed");
a3f6b2ce 191 dm_pool_free(deptree->mem, node);
3d0480ed
AK
192 return NULL;
193 }
194
195 return node;
196}
197
198static struct deptree_node *_find_deptree_node(struct deptree *deptree,
199 uint32_t major, uint32_t minor)
200{
201 uint64_t dev = MKDEV(major, minor);
202
a3f6b2ce 203 return dm_hash_lookup_binary(deptree->devs, (const char *) &dev,
3d0480ed
AK
204 sizeof(dev));
205}
206
a3f6b2ce 207static int _deps(struct dm_task **dmt, struct dm_pool *mem, uint32_t major, uint32_t minor,
3d0480ed
AK
208 const char **name, const char **uuid,
209 struct dm_info *info, struct dm_deps **deps)
210{
211 memset(info, 0, sizeof(*info));
212
213 if (!dm_is_dm_major(major)) {
214 *name = "";
215 *uuid = "";
216 *deps = NULL;
217 info->major = major;
218 info->minor = minor;
219 info->exists = 0;
220 return 1;
221 }
222
223 if (!(*dmt = dm_task_create(DM_DEVICE_DEPS))) {
224 log_error("deps dm_task creation failed");
225 return 0;
226 }
227
228 if (!dm_task_set_major(*dmt, major))
229 goto failed;
230
231 if (!dm_task_set_minor(*dmt, minor))
232 goto failed;
233
234 if (!dm_task_run(*dmt))
235 goto failed;
236
237 if (!dm_task_get_info(*dmt, info))
238 goto failed;
239
240 if (!info->exists) {
241 *name = "";
242 *uuid = "";
243 *deps = NULL;
244 } else {
245 if (info->major != major) {
246 log_error("Inconsistent deptree major number: %u != %u",
247 major, info->major);
248 goto failed;
249 }
250 if (info->minor != minor) {
251 log_error("Inconsistent deptree minor number: %u != %u",
252 minor, info->minor);
253 goto failed;
254 }
a3f6b2ce 255 if (!(*name = dm_pool_strdup(mem, dm_task_get_name(*dmt)))) {
3d0480ed
AK
256 log_error("name pool_strdup failed");
257 goto failed;
258 }
a3f6b2ce 259 if (!(*uuid = dm_pool_strdup(mem, dm_task_get_uuid(*dmt)))) {
3d0480ed
AK
260 log_error("uuid pool_strdup failed");
261 goto failed;
262 }
263 *deps = dm_task_get_deps(*dmt);
264 }
265
266 return 1;
267
268failed:
269 dm_task_destroy(*dmt);
270 return 0;
271}
272
273static int _add_dev(struct deptree *deptree, struct deptree_node *parent,
274 uint32_t major, uint32_t minor)
275{
276 struct dm_task *dmt = NULL;
277 struct dm_info info;
278 struct dm_deps *deps = NULL;
279 const char *name = NULL;
280 const char *uuid = NULL;
281 struct deptree_node *node;
282 uint32_t i;
283 int r = 0;
284 int new = 0;
285
286 /* Already in tree? */
287 if (!(node = _find_deptree_node(deptree, major, minor))) {
288 if (!_deps(&dmt, deptree->mem, major, minor, &name, &uuid, &info, &deps))
289 return 0;
290
291 if (!(node = _create_deptree_node(deptree, node, name, uuid,
292 &info)))
293 goto out;
294 new = 1;
295 }
296
297 /* If new parent not root node, remove any existing root node parent */
298 if (parent != &deptree->root)
299 _remove_from_toplevel(node);
300
301 /* Create link to parent. Use root node only if no other parents. */
302 if ((parent != &deptree->root) || !dm_deptree_node_num_children(node, 1))
303 if (!_link_nodes(parent, node))
304 goto out;
305
306 /* If node was already in tree, no need to recurse. */
307 if (!new)
308 return 1;
309
310 /* Can't recurse if not a mapped device or there are no dependencies */
311 if (!node->info.exists || !deps->count) {
312 if (!_add_to_bottomlevel(node))
313 goto out;
314 return 1;
315 }
316
317 /* Add dependencies to tree */
318 for (i = 0; i < deps->count; i++)
319 if (!_add_dev(deptree, node, MAJOR(deps->device[i]),
320 MINOR(deps->device[i])))
321 goto out;
322
323 r = 1;
324out:
325 if (dmt)
326 dm_task_destroy(dmt);
327
328 return r;
329}
330
331int dm_deptree_add_dev(struct deptree *deptree, uint32_t major, uint32_t minor)
332{
333 return _add_dev(deptree, &deptree->root, major, minor);
334}
335
336const char *dm_deptree_node_get_name(struct deptree_node *node)
337{
338 return node->info.exists ? node->name : "";
339}
340
341const char *dm_deptree_node_get_uuid(struct deptree_node *node)
342{
343 return node->info.exists ? node->uuid : "";
344}
345
346const struct dm_info *dm_deptree_node_get_info(struct deptree_node *node)
347{
348 return &node->info;
349}
350
351int dm_deptree_node_num_children(struct deptree_node *node, uint32_t inverted)
352{
353 if (inverted) {
354 if (_nodes_are_linked(&node->deptree->root, node))
355 return 0;
356 return list_size(&node->used_by);
357 }
358
359 if (_nodes_are_linked(node, &node->deptree->root))
360 return 0;
361
362 return list_size(&node->uses);
363}
364
2b69db1f
AK
365/*
366 * Returns 1 if no prefix supplied
367 */
368static int _uuid_prefix_matches(const char *uuid, const char *uuid_prefix, size_t uuid_prefix_len)
369{
370 if (!uuid_prefix)
371 return 1;
372
373 if (!strncmp(uuid, uuid_prefix, uuid_prefix_len))
374 return 1;
375
376 /* Handle transition: active device uuids might be missing the prefix */
377 if (uuid_prefix_len <= 4)
378 return 0;
379
872dea04
AK
380 if (!strncmp(uuid, "LVM-", 4))
381 return 0;
382
383 if (strncmp(uuid_prefix, "LVM-", 4))
2b69db1f
AK
384 return 0;
385
386 if (!strncmp(uuid, uuid_prefix + 4, uuid_prefix_len - 4))
387 return 1;
388
389 return 0;
390}
391
690a5da2
AK
392/*
393 * Returns 1 if no children.
394 */
395static int _children_suspended(struct deptree_node *node,
396 uint32_t inverted,
397 const char *uuid_prefix,
398 size_t uuid_prefix_len)
399{
400 struct list *list;
401 struct deptree_link *dlink;
402 const struct dm_info *dinfo;
403 const char *uuid;
404
405 if (inverted) {
406 if (_nodes_are_linked(&node->deptree->root, node))
407 return 1;
408 list = &node->used_by;
409 } else {
410 if (_nodes_are_linked(node, &node->deptree->root))
411 return 1;
412 list = &node->uses;
413 }
414
415 list_iterate_items(dlink, list) {
416 if (!(uuid = dm_deptree_node_get_uuid(dlink->node))) {
417 stack;
418 continue;
419 }
420
421 /* Ignore if it doesn't belong to this VG */
2b69db1f 422 if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
690a5da2
AK
423 continue;
424
425 if (!(dinfo = dm_deptree_node_get_info(dlink->node))) {
426 stack;
427 return 0;
428 }
429
430 if (!dinfo->suspended)
431 return 0;
432 }
433
434 return 1;
435}
436
3d0480ed
AK
437/*
438 * Set major and minor to zero for root of tree.
439 */
440struct deptree_node *dm_deptree_find_node(struct deptree *deptree,
441 uint32_t major,
442 uint32_t minor)
443{
444 if (!major && !minor)
445 return &deptree->root;
446
447 return _find_deptree_node(deptree, major, minor);
448}
449
450/*
451 * First time set *handle to NULL.
452 * Set inverted to invert the tree.
453 */
454struct deptree_node *dm_deptree_next_child(void **handle,
455 struct deptree_node *parent,
456 uint32_t inverted)
457{
458 struct list **dlink = (struct list **) handle;
459 struct list *use_list;
460
461 if (inverted)
462 use_list = &parent->used_by;
463 else
464 use_list = &parent->uses;
465
466 if (!*dlink)
467 *dlink = list_first(use_list);
468 else
469 *dlink = list_next(use_list, *dlink);
470
471 return (*dlink) ? list_item(*dlink, struct deptree_link)->node : NULL;
472}
473
3e8c6b73 474/*
a6d97ede 475 * Deactivate a device with its dependencies if the uuid prefix matches.
3e8c6b73 476 */
db208f51
AK
477static int _info_by_dev(uint32_t major, uint32_t minor, int with_open_count,
478 struct dm_info *info)
3e8c6b73
AK
479{
480 struct dm_task *dmt;
481 int r;
482
483 if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
484 log_error("_info_by_dev: dm_task creation failed");
485 return 0;
486 }
487
488 if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
489 log_error("_info_by_dev: Failed to set device number");
490 dm_task_destroy(dmt);
491 return 0;
492 }
493
db208f51
AK
494 if (!with_open_count && !dm_task_no_open_count(dmt))
495 log_error("Failed to disable open_count");
496
3e8c6b73
AK
497 if ((r = dm_task_run(dmt)))
498 r = dm_task_get_info(dmt, info);
499
500 dm_task_destroy(dmt);
501
502 return r;
503}
504
505static int _deactivate_node(const char *name, uint32_t major, uint32_t minor)
506{
507 struct dm_task *dmt;
508 int r;
509
510 log_verbose("Removing %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor);
511
512 if (!(dmt = dm_task_create(DM_DEVICE_REMOVE))) {
513 log_error("Deactivation dm_task creation failed for %s", name);
514 return 0;
515 }
516
517 if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
518 log_error("Failed to set device number for %s deactivation", name);
519 dm_task_destroy(dmt);
520 return 0;
521 }
522
523 if (!dm_task_no_open_count(dmt))
524 log_error("Failed to disable open_count");
525
526 r = dm_task_run(dmt);
527
db208f51
AK
528 /* FIXME Remove node from tree or mark invalid? */
529
530 dm_task_destroy(dmt);
531
532 return r;
533}
534
535static int _suspend_node(const char *name, uint32_t major, uint32_t minor,
536 struct dm_info *newinfo)
537{
538 struct dm_task *dmt;
539 int r;
540
541 log_verbose("Suspending %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor);
542
543 if (!(dmt = dm_task_create(DM_DEVICE_SUSPEND))) {
544 log_error("Suspend dm_task creation failed for %s", name);
545 return 0;
546 }
547
548 if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
549 log_error("Failed to set device number for %s suspension.", name);
550 dm_task_destroy(dmt);
551 return 0;
552 }
553
554 if (!dm_task_no_open_count(dmt))
555 log_error("Failed to disable open_count");
556
557 if ((r = dm_task_run(dmt)))
558 r = dm_task_get_info(dmt, newinfo);
559
3e8c6b73
AK
560 dm_task_destroy(dmt);
561
562 return r;
563}
564
a6d97ede
AK
565int dm_deptree_deactivate_children(struct deptree_node *dnode,
566 const char *uuid_prefix,
567 size_t uuid_prefix_len)
3e8c6b73
AK
568{
569 void *handle = NULL;
570 struct deptree_node *child = dnode;
571 struct dm_info info;
572 const struct dm_info *dinfo;
573 const char *name;
574 const char *uuid;
575
576 while ((child = dm_deptree_next_child(&handle, dnode, 0))) {
577 if (!(dinfo = dm_deptree_node_get_info(child))) {
578 stack;
579 continue;
580 }
581
582 if (!(name = dm_deptree_node_get_name(child))) {
583 stack;
584 continue;
585 }
586
587 if (!(uuid = dm_deptree_node_get_uuid(child))) {
588 stack;
589 continue;
590 }
591
592 /* Ignore if it doesn't belong to this VG */
2b69db1f 593 if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
3e8c6b73 594 continue;
3e8c6b73
AK
595
596 /* Refresh open_count */
db208f51 597 if (!_info_by_dev(dinfo->major, dinfo->minor, 1, &info) ||
3e8c6b73
AK
598 !info.exists || info.open_count)
599 continue;
600
601 if (!_deactivate_node(name, info.major, info.minor)) {
602 log_error("Unable to deactivate %s (%" PRIu32
603 ":%" PRIu32 ")", name, info.major,
604 info.minor);
605 continue;
606 }
607
608 if (dm_deptree_node_num_children(child, 0))
609 dm_deptree_deactivate_children(child, uuid_prefix, uuid_prefix_len);
610 }
611
612 return 1;
613}
db208f51 614
db208f51
AK
615int dm_deptree_suspend_children(struct deptree_node *dnode,
616 const char *uuid_prefix,
617 size_t uuid_prefix_len)
618{
619 void *handle = NULL;
620 struct deptree_node *child = dnode;
621 struct dm_info info, newinfo;
622 const struct dm_info *dinfo;
623 const char *name;
624 const char *uuid;
625
690a5da2 626 /* Suspend nodes at this level of the tree */
db208f51
AK
627 while ((child = dm_deptree_next_child(&handle, dnode, 0))) {
628 if (!(dinfo = dm_deptree_node_get_info(child))) {
629 stack;
630 continue;
631 }
632
633 if (!(name = dm_deptree_node_get_name(child))) {
634 stack;
635 continue;
636 }
637
638 if (!(uuid = dm_deptree_node_get_uuid(child))) {
639 stack;
640 continue;
641 }
642
643 /* Ignore if it doesn't belong to this VG */
2b69db1f 644 if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
db208f51
AK
645 continue;
646
690a5da2
AK
647 /* Ensure immediate parents are already suspended */
648 if (!_children_suspended(child, 1, uuid_prefix, uuid_prefix_len))
649 continue;
650
db208f51
AK
651 if (!_info_by_dev(dinfo->major, dinfo->minor, 0, &info) ||
652 !info.exists)
653 continue;
654
655 if (!_suspend_node(name, info.major, info.minor, &newinfo)) {
656 log_error("Unable to suspend %s (%" PRIu32
657 ":%" PRIu32 ")", name, info.major,
658 info.minor);
659 continue;
660 }
661
662 /* Update cached info */
663 child->info = newinfo;
690a5da2
AK
664 }
665
666 /* Then suspend any child nodes */
667 handle = NULL;
668
669 while ((child = dm_deptree_next_child(&handle, dnode, 0))) {
670 if (!(uuid = dm_deptree_node_get_uuid(child))) {
671 stack;
672 continue;
673 }
674
675 /* Ignore if it doesn't belong to this VG */
676 if (uuid_prefix && strncmp(uuid, uuid_prefix, uuid_prefix_len))
677 continue;
db208f51
AK
678
679 if (dm_deptree_node_num_children(child, 0))
680 dm_deptree_suspend_children(child, uuid_prefix, uuid_prefix_len);
681 }
682
683 return 1;
684}
685
686/*
687 * Returns 1 if unsure.
688 */
689int dm_deptree_children_use_uuid(struct deptree_node *dnode,
690 const char *uuid_prefix,
691 size_t uuid_prefix_len)
692{
693 void *handle = NULL;
694 struct deptree_node *child = dnode;
695 const char *uuid;
696
697 while ((child = dm_deptree_next_child(&handle, dnode, 0))) {
698 if (!(uuid = dm_deptree_node_get_uuid(child))) {
699 log_error("Failed to get uuid for deptree node.");
700 return 1;
701 }
702
872dea04 703 if (_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
db208f51
AK
704 return 1;
705
706 if (dm_deptree_node_num_children(child, 0))
707 dm_deptree_children_use_uuid(child, uuid_prefix, uuid_prefix_len);
708 }
709
710 return 0;
711}
This page took 0.093811 seconds and 5 git commands to generate.