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