summaryrefslogtreecommitdiff
path: root/src/home/homed-home.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2021-11-02 23:11:59 +0100
committerLennart Poettering <lennart@poettering.net>2021-11-25 18:28:44 +0100
commitd357b80d331ebed709b7a9d71e014c319ba5bd79 (patch)
tree8b063226969c9f982f8d7baff2b849c67c8568ae /src/home/homed-home.c
parent21505c937ce3bcb90aae85366a262462ec8fb6b2 (diff)
downloadsystemd-d357b80d331ebed709b7a9d71e014c319ba5bd79.tar.gz
homed: add automatic grow/shrink ("rebalancing")
Diffstat (limited to 'src/home/homed-home.c')
-rw-r--r--src/home/homed-home.c67
1 files changed, 65 insertions, 2 deletions
diff --git a/src/home/homed-home.c b/src/home/homed-home.c
index 2cc1f8b384..470c7f07f6 100644
--- a/src/home/homed-home.c
+++ b/src/home/homed-home.c
@@ -161,6 +161,7 @@ int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret) {
(void) bus_manager_emit_auto_login_changed(m);
(void) bus_home_emit_change(home);
+ (void) manager_schedule_rebalance(m, /* immediately= */ false);
if (ret)
*ret = TAKE_PTR(home);
@@ -193,6 +194,8 @@ Home *home_free(Home *h) {
if (h->manager->gc_focus == h)
h->manager->gc_focus = NULL;
+
+ (void) manager_schedule_rebalance(h->manager, /* immediately= */ false);
}
user_record_unref(h->record);
@@ -489,6 +492,7 @@ static void home_set_state(Home *h, HomeState state) {
* enqueue it for GC too. */
home_schedule_operation(h, NULL, NULL);
+ manager_reschedule_rebalance(h->manager);
manager_enqueue_gc(h->manager, h);
}
}
@@ -727,6 +731,7 @@ static void home_fixate_finish(Home *h, int ret, UserRecord *hr) {
/* Reset the state to "invalid", which makes home_get_state() test if the image exists and returns
* HOME_ABSENT vs. HOME_INACTIVE as necessary. */
home_set_state(h, _HOME_STATE_INVALID);
+ (void) manager_schedule_rebalance(h->manager, /* immediately= */ false);
return;
fail:
@@ -781,6 +786,9 @@ static void home_activate_finish(Home *h, int ret, UserRecord *hr) {
finish:
h->current_operation = operation_result_unref(h->current_operation, r, &error);
home_set_state(h, _HOME_STATE_INVALID);
+
+ if (r >= 0)
+ (void) manager_schedule_rebalance(h->manager, /* immediately= */ true);
}
static void home_deactivate_finish(Home *h, int ret, UserRecord *hr) {
@@ -803,6 +811,9 @@ static void home_deactivate_finish(Home *h, int ret, UserRecord *hr) {
finish:
h->current_operation = operation_result_unref(h->current_operation, r, &error);
home_set_state(h, _HOME_STATE_INVALID);
+
+ if (r >= 0)
+ (void) manager_schedule_rebalance(h->manager, /* immediately= */ true);
}
static void home_remove_finish(Home *h, int ret, UserRecord *hr) {
@@ -841,6 +852,8 @@ static void home_remove_finish(Home *h, int ret, UserRecord *hr) {
/* Unload this record from memory too now. */
h = home_free(h);
+
+ (void) manager_schedule_rebalance(m, /* immediately= */ true);
return;
fail:
@@ -885,6 +898,8 @@ static void home_create_finish(Home *h, int ret, UserRecord *hr) {
h->current_operation = operation_result_unref(h->current_operation, 0, NULL);
home_set_state(h, _HOME_STATE_INVALID);
+
+ (void) manager_schedule_rebalance(h->manager, /* immediately= */ true);
}
static void home_change_finish(Home *h, int ret, UserRecord *hr) {
@@ -918,6 +933,7 @@ static void home_change_finish(Home *h, int ret, UserRecord *hr) {
}
log_debug("Change operation of %s completed.", h->user_name);
+ (void) manager_schedule_rebalance(h->manager, /* immediately= */ false);
r = 0;
finish:
@@ -1683,7 +1699,12 @@ int home_update(Home *h, UserRecord *hr, sd_bus_error *error) {
return 0;
}
-int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *error) {
+int home_resize(Home *h,
+ uint64_t disk_size,
+ UserRecord *secret,
+ bool automatic,
+ sd_bus_error *error) {
+
_cleanup_(user_record_unrefp) UserRecord *c = NULL;
HomeState state;
int r;
@@ -1711,6 +1732,12 @@ int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *e
if (r < 0)
return r;
+ /* If the user didn't specify any size explicitly and rebalancing is on, then the disk size is
+ * determined by automatic rebalancing and hence not user configured but determined by us and thus
+ * applied anyway. */
+ if (disk_size == UINT64_MAX && h->record->rebalance_weight != REBALANCE_WEIGHT_OFF)
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Disk size is being determined by automatic disk space rebalancing.");
+
if (disk_size == UINT64_MAX || disk_size == h->record->disk_size) {
if (h->record->disk_size == UINT64_MAX)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "No disk size to resize to specified.");
@@ -1732,6 +1759,11 @@ int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *e
if (r < 0)
return r;
+ /* If user picked an explicit size, then turn off rebalancing, so that we don't undo what user chose */
+ r = user_record_set_rebalance_weight(c, REBALANCE_WEIGHT_OFF);
+ if (r < 0)
+ return r;
+
r = user_record_update_last_changed(c, false);
if (r == -ECHRNG)
return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Record last change time of %s is newer than current time, cannot update.", h->user_name);
@@ -1746,7 +1778,7 @@ int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *e
c = TAKE_PTR(signed_c);
}
- r = home_update_internal(h, "resize", c, secret, error);
+ r = home_update_internal(h, automatic ? "resize-auto" : "resize", c, secret, error);
if (r < 0)
return r;
@@ -2965,6 +2997,8 @@ static int on_pending(sd_event_source *s, void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to disable event source: %m");
+ /* No operations pending anymore, maybe this is a good time to trigger a rebalancing */
+ manager_reschedule_rebalance(h->manager);
return 0;
}
@@ -3121,6 +3155,35 @@ int home_wait_for_worker(Home *h) {
return 1;
}
+bool home_shall_rebalance(Home *h) {
+ HomeState state;
+
+ assert(h);
+
+ /* Determines if the home directory is a candidate for rebalancing */
+
+ if (!user_record_shall_rebalance(h->record))
+ return false;
+
+ state = home_get_state(h);
+ if (!HOME_STATE_SHALL_REBALANCE(state))
+ return false;
+
+ return true;
+}
+
+bool home_is_busy(Home *h) {
+ assert(h);
+
+ if (h->current_operation)
+ return true;
+
+ if (!ordered_set_isempty(h->pending_operations))
+ return true;
+
+ return HOME_STATE_IS_EXECUTING_OPERATION(home_get_state(h));
+}
+
static const char* const home_state_table[_HOME_STATE_MAX] = {
[HOME_UNFIXATED] = "unfixated",
[HOME_ABSENT] = "absent",