]> sourceware.org Git - lvm2.git/blame - libdm/libdm-deptree.c
Attempt to cope with LVM- prefix transition.
[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
690a5da2
AK
365/*
366 * Returns 1 if no children.
367 */
368static int _children_suspended(struct deptree_node *node,
369 uint32_t inverted,
370 const char *uuid_prefix,
371 size_t uuid_prefix_len)
372{
373 struct list *list;
374 struct deptree_link *dlink;
375 const struct dm_info *dinfo;
376 const char *uuid;
377
378 if (inverted) {
379 if (_nodes_are_linked(&node->deptree->root, node))
380 return 1;
381 list = &node->used_by;
382 } else {
383 if (_nodes_are_linked(node, &node->deptree->root))
384 return 1;
385 list = &node->uses;
386 }
387
388 list_iterate_items(dlink, list) {
389 if (!(uuid = dm_deptree_node_get_uuid(dlink->node))) {
390 stack;
391 continue;
392 }
393
394 /* Ignore if it doesn't belong to this VG */
395 if (uuid_prefix && strncmp(uuid, uuid_prefix, uuid_prefix_len))
396 continue;
397
398 if (!(dinfo = dm_deptree_node_get_info(dlink->node))) {
399 stack;
400 return 0;
401 }
402
403 if (!dinfo->suspended)
404 return 0;
405 }
406
407 return 1;
408}
409
3d0480ed
AK
410/*
411 * Set major and minor to zero for root of tree.
412 */
413struct deptree_node *dm_deptree_find_node(struct deptree *deptree,
414 uint32_t major,
415 uint32_t minor)
416{
417 if (!major && !minor)
418 return &deptree->root;
419
420 return _find_deptree_node(deptree, major, minor);
421}
422
423/*
424 * First time set *handle to NULL.
425 * Set inverted to invert the tree.
426 */
427struct deptree_node *dm_deptree_next_child(void **handle,
428 struct deptree_node *parent,
429 uint32_t inverted)
430{
431 struct list **dlink = (struct list **) handle;
432 struct list *use_list;
433
434 if (inverted)
435 use_list = &parent->used_by;
436 else
437 use_list = &parent->uses;
438
439 if (!*dlink)
440 *dlink = list_first(use_list);
441 else
442 *dlink = list_next(use_list, *dlink);
443
444 return (*dlink) ? list_item(*dlink, struct deptree_link)->node : NULL;
445}
446
3e8c6b73 447/*
a6d97ede 448 * Deactivate a device with its dependencies if the uuid prefix matches.
3e8c6b73 449 */
db208f51
AK
450static int _info_by_dev(uint32_t major, uint32_t minor, int with_open_count,
451 struct dm_info *info)
3e8c6b73
AK
452{
453 struct dm_task *dmt;
454 int r;
455
456 if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
457 log_error("_info_by_dev: dm_task creation failed");
458 return 0;
459 }
460
461 if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
462 log_error("_info_by_dev: Failed to set device number");
463 dm_task_destroy(dmt);
464 return 0;
465 }
466
db208f51
AK
467 if (!with_open_count && !dm_task_no_open_count(dmt))
468 log_error("Failed to disable open_count");
469
3e8c6b73
AK
470 if ((r = dm_task_run(dmt)))
471 r = dm_task_get_info(dmt, info);
472
473 dm_task_destroy(dmt);
474
475 return r;
476}
477
478static int _deactivate_node(const char *name, uint32_t major, uint32_t minor)
479{
480 struct dm_task *dmt;
481 int r;
482
483 log_verbose("Removing %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor);
484
485 if (!(dmt = dm_task_create(DM_DEVICE_REMOVE))) {
486 log_error("Deactivation dm_task creation failed for %s", name);
487 return 0;
488 }
489
490 if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
491 log_error("Failed to set device number for %s deactivation", name);
492 dm_task_destroy(dmt);
493 return 0;
494 }
495
496 if (!dm_task_no_open_count(dmt))
497 log_error("Failed to disable open_count");
498
499 r = dm_task_run(dmt);
500
db208f51
AK
501 /* FIXME Remove node from tree or mark invalid? */
502
503 dm_task_destroy(dmt);
504
505 return r;
506}
507
508static int _suspend_node(const char *name, uint32_t major, uint32_t minor,
509 struct dm_info *newinfo)
510{
511 struct dm_task *dmt;
512 int r;
513
514 log_verbose("Suspending %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor);
515
516 if (!(dmt = dm_task_create(DM_DEVICE_SUSPEND))) {
517 log_error("Suspend dm_task creation failed for %s", name);
518 return 0;
519 }
520
521 if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
522 log_error("Failed to set device number for %s suspension.", name);
523 dm_task_destroy(dmt);
524 return 0;
525 }
526
527 if (!dm_task_no_open_count(dmt))
528 log_error("Failed to disable open_count");
529
530 if ((r = dm_task_run(dmt)))
531 r = dm_task_get_info(dmt, newinfo);
532
3e8c6b73
AK
533 dm_task_destroy(dmt);
534
535 return r;
536}
537
a6d97ede
AK
538int dm_deptree_deactivate_children(struct deptree_node *dnode,
539 const char *uuid_prefix,
540 size_t uuid_prefix_len)
3e8c6b73
AK
541{
542 void *handle = NULL;
543 struct deptree_node *child = dnode;
544 struct dm_info info;
545 const struct dm_info *dinfo;
546 const char *name;
547 const char *uuid;
548
549 while ((child = dm_deptree_next_child(&handle, dnode, 0))) {
550 if (!(dinfo = dm_deptree_node_get_info(child))) {
551 stack;
552 continue;
553 }
554
555 if (!(name = dm_deptree_node_get_name(child))) {
556 stack;
557 continue;
558 }
559
560 if (!(uuid = dm_deptree_node_get_uuid(child))) {
561 stack;
562 continue;
563 }
564
565 /* Ignore if it doesn't belong to this VG */
a6d97ede 566 if (uuid_prefix && strncmp(uuid, uuid_prefix, uuid_prefix_len))
3e8c6b73 567 continue;
3e8c6b73
AK
568
569 /* Refresh open_count */
db208f51 570 if (!_info_by_dev(dinfo->major, dinfo->minor, 1, &info) ||
3e8c6b73
AK
571 !info.exists || info.open_count)
572 continue;
573
574 if (!_deactivate_node(name, info.major, info.minor)) {
575 log_error("Unable to deactivate %s (%" PRIu32
576 ":%" PRIu32 ")", name, info.major,
577 info.minor);
578 continue;
579 }
580
581 if (dm_deptree_node_num_children(child, 0))
582 dm_deptree_deactivate_children(child, uuid_prefix, uuid_prefix_len);
583 }
584
585 return 1;
586}
db208f51 587
db208f51
AK
588int dm_deptree_suspend_children(struct deptree_node *dnode,
589 const char *uuid_prefix,
590 size_t uuid_prefix_len)
591{
592 void *handle = NULL;
593 struct deptree_node *child = dnode;
594 struct dm_info info, newinfo;
595 const struct dm_info *dinfo;
596 const char *name;
597 const char *uuid;
598
690a5da2 599 /* Suspend nodes at this level of the tree */
db208f51
AK
600 while ((child = dm_deptree_next_child(&handle, dnode, 0))) {
601 if (!(dinfo = dm_deptree_node_get_info(child))) {
602 stack;
603 continue;
604 }
605
606 if (!(name = dm_deptree_node_get_name(child))) {
607 stack;
608 continue;
609 }
610
611 if (!(uuid = dm_deptree_node_get_uuid(child))) {
612 stack;
613 continue;
614 }
615
616 /* Ignore if it doesn't belong to this VG */
617 if (uuid_prefix && strncmp(uuid, uuid_prefix, uuid_prefix_len))
618 continue;
619
690a5da2
AK
620 /* Ensure immediate parents are already suspended */
621 if (!_children_suspended(child, 1, uuid_prefix, uuid_prefix_len))
622 continue;
623
db208f51
AK
624 if (!_info_by_dev(dinfo->major, dinfo->minor, 0, &info) ||
625 !info.exists)
626 continue;
627
628 if (!_suspend_node(name, info.major, info.minor, &newinfo)) {
629 log_error("Unable to suspend %s (%" PRIu32
630 ":%" PRIu32 ")", name, info.major,
631 info.minor);
632 continue;
633 }
634
635 /* Update cached info */
636 child->info = newinfo;
690a5da2
AK
637 }
638
639 /* Then suspend any child nodes */
640 handle = NULL;
641
642 while ((child = dm_deptree_next_child(&handle, dnode, 0))) {
643 if (!(uuid = dm_deptree_node_get_uuid(child))) {
644 stack;
645 continue;
646 }
647
648 /* Ignore if it doesn't belong to this VG */
649 if (uuid_prefix && strncmp(uuid, uuid_prefix, uuid_prefix_len))
650 continue;
db208f51
AK
651
652 if (dm_deptree_node_num_children(child, 0))
653 dm_deptree_suspend_children(child, uuid_prefix, uuid_prefix_len);
654 }
655
656 return 1;
657}
658
659/*
660 * Returns 1 if unsure.
661 */
662int dm_deptree_children_use_uuid(struct deptree_node *dnode,
663 const char *uuid_prefix,
664 size_t uuid_prefix_len)
665{
666 void *handle = NULL;
667 struct deptree_node *child = dnode;
668 const char *uuid;
669
670 while ((child = dm_deptree_next_child(&handle, dnode, 0))) {
671 if (!(uuid = dm_deptree_node_get_uuid(child))) {
672 log_error("Failed to get uuid for deptree node.");
673 return 1;
674 }
675
676 if (!strncmp(uuid, uuid_prefix, uuid_prefix_len))
677 return 1;
678
679 if (dm_deptree_node_num_children(child, 0))
680 dm_deptree_children_use_uuid(child, uuid_prefix, uuid_prefix_len);
681 }
682
683 return 0;
684}
This page took 0.087465 seconds and 5 git commands to generate.