summaryrefslogtreecommitdiff
path: root/src/core/unit.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2013-09-26 20:14:24 +0200
committerLennart Poettering <lennart@poettering.net>2013-09-26 20:20:30 +0200
commita57f7e2c828b852eb32fd810dcea041bb2975501 (patch)
tree9d088f212995c20b3ba2d2574ca8027fad25a51b /src/core/unit.c
parent6270c1bd8f83e9985458c63688f452be7626766f (diff)
core: rework how we match mount units against each other
Previously to automatically create dependencies between mount units we matched every mount unit agains all others resulting in O(n^2) complexity. On setups with large amounts of mount units this might make things slow. This change replaces the matching code to use a hashtable that is keyed by a path prefix, and points to a set of units that require that path to be around. When a new mount unit is installed it is hence sufficient to simply look up this set of units via its own file system paths to know which units to order after itself. This patch also changes all unit types to only create automatic mount dependencies via the RequiresMountsFor= logic, and this is exposed to the outside to make things more transparent. With this change we still have some O(n) complexities in place when handling mounts, but that's currently unavoidable due to kernel APIs, and still substantially better than O(n^2) as before. https://bugs.freedesktop.org/show_bug.cgi?id=69740
Diffstat (limited to 'src/core/unit.c')
-rw-r--r--src/core/unit.c162
1 files changed, 133 insertions, 29 deletions
diff --git a/src/core/unit.c b/src/core/unit.c
index ab313b9b9..4b9771076 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -374,6 +374,34 @@ static void unit_remove_transient(Unit *u) {
}
}
+static void unit_free_requires_mounts_for(Unit *u) {
+ char **j;
+
+ STRV_FOREACH(j, u->requires_mounts_for) {
+ char s[strlen(*j) + 1];
+
+ PATH_FOREACH_PREFIX_MORE(s, *j) {
+ char *y;
+ Set *x;
+
+ x = hashmap_get2(u->manager->units_requiring_mounts_for, s, (void**) &y);
+ if (!x)
+ continue;
+
+ set_remove(x, u);
+
+ if (set_isempty(x)) {
+ hashmap_remove(u->manager->units_requiring_mounts_for, y);
+ free(y);
+ set_free(x);
+ }
+ }
+ }
+
+ strv_free(u->requires_mounts_for);
+ u->requires_mounts_for = NULL;
+}
+
void unit_free(Unit *u) {
UnitDependency d;
Iterator i;
@@ -390,6 +418,8 @@ void unit_free(Unit *u) {
if (UNIT_VTABLE(u)->done)
UNIT_VTABLE(u)->done(u);
+ unit_free_requires_mounts_for(u);
+
SET_FOREACH(t, u->names, i)
hashmap_remove_value(u->manager->units, t, u);
@@ -408,11 +438,6 @@ void unit_free(Unit *u) {
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
bidi_set_free(u, u->dependencies[d]);
- if (u->requires_mounts_for) {
- LIST_REMOVE(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u);
- strv_free(u->requires_mounts_for);
- }
-
if (u->type != _UNIT_TYPE_INVALID)
LIST_REMOVE(Unit, units_by_type, u->manager->units_by_type[u->type], u);
@@ -2659,40 +2684,39 @@ void unit_ref_unset(UnitRef *ref) {
ref->unit = NULL;
}
-int unit_add_one_mount_link(Unit *u, Mount *m) {
+int unit_add_mount_links(Unit *u) {
char **i;
+ int r;
assert(u);
- assert(m);
-
- if (u->load_state != UNIT_LOADED ||
- UNIT(m)->load_state != UNIT_LOADED)
- return 0;
STRV_FOREACH(i, u->requires_mounts_for) {
+ char prefix[strlen(*i) + 1];
- if (UNIT(m) == u)
- continue;
+ PATH_FOREACH_PREFIX_MORE(prefix, *i) {
+ Unit *m;
- if (!path_startswith(*i, m->where))
- continue;
-
- return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
- }
-
- return 0;
-}
+ r = manager_get_unit_by_path(u->manager, prefix, ".mount", &m);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+ if (m == u)
+ continue;
-int unit_add_mount_links(Unit *u) {
- Unit *other;
- int r;
+ if (m->load_state != UNIT_LOADED)
+ continue;
- assert(u);
+ r = unit_add_dependency(u, UNIT_AFTER, m, true);
+ if (r < 0)
+ return r;
- LIST_FOREACH(units_by_type, other, u->manager->units_by_type[UNIT_MOUNT]) {
- r = unit_add_one_mount_link(u, MOUNT(other));
- if (r < 0)
- return r;
+ if (m->fragment_path) {
+ r = unit_add_dependency(u, UNIT_REQUIRES, m, true);
+ if (r < 0)
+ return r;
+ }
+ }
}
return 0;
@@ -3012,6 +3036,86 @@ int unit_kill_context(
return wait_for_exit;
}
+int unit_require_mounts_for(Unit *u, const char *path) {
+ char prefix[strlen(path) + 1], *p;
+ int r;
+
+ assert(u);
+ assert(path);
+
+ /* Registers a unit for requiring a certain path and all its
+ * prefixes. We keep a simple array of these paths in the
+ * unit, since its usually short. However, we build a prefix
+ * table for all possible prefixes so that new appearing mount
+ * units can easily determine which units to make themselves a
+ * dependency of. */
+
+ p = strdup(path);
+ if (!p)
+ return -ENOMEM;
+
+ path_kill_slashes(p);
+
+ if (!path_is_absolute(p)) {
+ free(p);
+ return -EINVAL;
+ }
+
+ if (!path_is_safe(p)) {
+ free(p);
+ return -EPERM;
+ }
+
+ if (strv_contains(u->requires_mounts_for, p)) {
+ free(p);
+ return 0;
+ }
+
+ r = strv_push(&u->requires_mounts_for, p);
+ if (r < 0) {
+ free(p);
+ return r;
+ }
+
+ PATH_FOREACH_PREFIX_MORE(prefix, p) {
+ Set *x;
+
+ x = hashmap_get(u->manager->units_requiring_mounts_for, prefix);
+ if (!x) {
+ char *q;
+
+ if (!u->manager->units_requiring_mounts_for) {
+ u->manager->units_requiring_mounts_for = hashmap_new(string_hash_func, string_compare_func);
+ if (!u->manager->units_requiring_mounts_for)
+ return -ENOMEM;
+ }
+
+ q = strdup(prefix);
+ if (!q)
+ return -ENOMEM;
+
+ x = set_new(NULL, NULL);
+ if (!x) {
+ free(q);
+ return -ENOMEM;
+ }
+
+ r = hashmap_put(u->manager->units_requiring_mounts_for, q, x);
+ if (r < 0) {
+ free(q);
+ set_free(x);
+ return r;
+ }
+ }
+
+ r = set_put(x, u);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
[UNIT_ACTIVE] = "active",
[UNIT_RELOADING] = "reloading",