From 4950591627de2e859fb5fd1ae6d67e9c2e1b395e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Nov 2021 16:32:05 +0100 Subject: [PATCH] homed: add explicit API for requesting rebalancing too --- man/org.freedesktop.home1.xml | 7 +++++ src/home/homed-manager-bus.c | 22 +++++++++++++++ src/home/homed-manager.c | 34 +++++++++++++++++++++-- src/home/homed-manager.h | 6 ++++ src/home/org.freedesktop.home1.conf | 4 +++ src/libsystemd/sd-bus/bus-common-errors.c | 1 + src/libsystemd/sd-bus/bus-common-errors.h | 1 + 7 files changed, 72 insertions(+), 3 deletions(-) diff --git a/man/org.freedesktop.home1.xml b/man/org.freedesktop.home1.xml index b977e1b46f6..537c3730893 100644 --- a/man/org.freedesktop.home1.xml +++ b/man/org.freedesktop.home1.xml @@ -96,6 +96,7 @@ node /org/freedesktop/home1 { ReleaseHome(in s user_name); LockAllHomes(); DeactivateAllHomes(); + Rebalance(); properties: readonly a(sso) AutoLogin = [...]; }; @@ -159,6 +160,8 @@ node /org/freedesktop/home1 { + + @@ -346,6 +349,10 @@ node /org/freedesktop/home1 { DeactivateAllHomes() deactivates all home areas that are currently active. This is usually invoked automatically shortly before system shutdown. + + Rebalance() synchronously rebalances free disk space between home + areas. This only executes an operation if at least one home area using the LUKS2 backend is active and + has rebalancing enabled, and is otherwise a NOP. diff --git a/src/home/homed-manager-bus.c b/src/home/homed-manager-bus.c index 7ac5b8d0fc7..31f82dc1dc6 100644 --- a/src/home/homed-manager-bus.c +++ b/src/home/homed-manager-bus.c @@ -635,6 +635,27 @@ static int method_deactivate_all_homes(sd_bus_message *message, void *userdata, return sd_bus_reply_method_return(message, NULL); } +static int method_rebalance(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(m); + + r = manager_schedule_rebalance(m, /* immediately= */ true); + if (r == 0) + return sd_bus_reply_method_errorf(message, BUS_ERROR_REBALANCE_NOT_NEEDED, "No home directories need rebalancing."); + if (r < 0) + return r; + + /* Keep a reference to this message, so that we can reply to it once we are done */ + r = set_ensure_put(&m->rebalance_queued_method_calls, &bus_message_hash_ops, message); + if (r < 0) + return log_error_errno(r, "Failed to track rebalance bus message: %m"); + + sd_bus_message_ref(message); + return 1; +} + static const sd_bus_vtable manager_vtable[] = { SD_BUS_VTABLE_START(0), @@ -843,6 +864,7 @@ static const sd_bus_vtable manager_vtable[] = { /* An operation that acts on all homes that allow it */ SD_BUS_METHOD("LockAllHomes", NULL, NULL, method_lock_all_homes, 0), SD_BUS_METHOD("DeactivateAllHomes", NULL, NULL, method_deactivate_all_homes, 0), + SD_BUS_METHOD("Rebalance", NULL, NULL, method_rebalance, 0), SD_BUS_VTABLE_END }; diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c index 3851234a37b..3e5bd9c29d1 100644 --- a/src/home/homed-manager.c +++ b/src/home/homed-manager.c @@ -1985,6 +1985,24 @@ static int manager_rebalance_apply(Manager *m) { return c; } +static void manager_rebalance_reply_messages(Manager *m) { + int r; + + assert(m); + + for (;;) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *msg = + set_steal_first(m->rebalance_pending_method_calls); + + if (!msg) + break; + + r = sd_bus_reply_method_return(msg, NULL); + if (r < 0) + log_debug_errno(r, "Failed to reply to rebalance method call, ignoring: %m"); + } +} + static int manager_rebalance_now(Manager *m) { RebalanceState busy_state; /* the state to revert to when operation fails if busy */ int r; @@ -2006,6 +2024,13 @@ static int manager_rebalance_now(Manager *m) { /* First shrink large home dirs */ m->rebalance_state = REBALANCE_SHRINKING; busy_state = REBALANCE_PENDING; + + /* We are initiating the next rebalancing cycle now, let's make the queued methods + * calls the pending ones, and flush out any pending ones (which shouldn't exist at + * this time anyway) */ + set_clear(m->rebalance_pending_method_calls); + SWAP_TWO(m->rebalance_pending_method_calls, m->rebalance_queued_method_calls); + log_debug("Shrinking phase.."); break; @@ -2055,6 +2080,7 @@ static int manager_rebalance_now(Manager *m) { finish: /* Reset state and schedule next rebalance */ m->rebalance_state = REBALANCE_IDLE; + manager_rebalance_reply_messages(m); (void) manager_schedule_rebalance(m, /* immediately= */ false); return r; } @@ -2078,6 +2104,7 @@ int manager_schedule_rebalance(Manager *m, bool immediately) { /* Check if there are any records where rebalancing is requested */ if (!manager_shall_rebalance(m)) { log_debug("Not scheduling rebalancing, not needed."); + r = 0; /* report that we didn't schedule anything because nothing needed it */ goto turn_off; } @@ -2118,13 +2145,13 @@ int manager_schedule_rebalance(Manager *m, bool immediately) { m->rebalance_state = REBALANCE_PENDING; log_debug("Scheduled immediate rebalancing..."); - return 0; + return 1; /* report that we scheduled something */ } /* If we are told to schedule a rebalancing eventually, then do so only if we are not executing * anything yet. Also if we have something scheduled already, leave it in place */ if (!IN_SET(m->rebalance_state, REBALANCE_OFF, REBALANCE_IDLE)) - return 0; + return 1; /* report that there's already something scheduled */ if (m->rebalance_event_source) { r = sd_event_source_set_time_relative(m->rebalance_event_source, m->rebalance_interval_usec); @@ -2156,11 +2183,12 @@ int manager_schedule_rebalance(Manager *m, bool immediately) { m->rebalance_state = REBALANCE_WAITING; /* We managed to enqueue a timer event, we now wait until it fires */ log_debug("Scheduled rebalancing in %s...", FORMAT_TIMESPAN(m->rebalance_interval_usec, 0)); - return 0; + return 1; /* report that we scheduled something */ turn_off: m->rebalance_event_source = sd_event_source_disable_unref(m->rebalance_event_source); m->rebalance_state = REBALANCE_OFF; + manager_rebalance_reply_messages(m); return r; } diff --git a/src/home/homed-manager.h b/src/home/homed-manager.h index cf6d58b2586..18e7542e135 100644 --- a/src/home/homed-manager.h +++ b/src/home/homed-manager.h @@ -63,6 +63,12 @@ struct Manager { RebalanceState rebalance_state; usec_t rebalance_interval_usec; + + /* In order to allow synchronous rebalance requests via bus calls we maintain two pools of bus + * messages: 'rebalance_pending_methods' are the method calls we are currently operating on and + * running a rebalancing operation for. 'rebalance_queued_method_calls' are the method calls that + * have been queued since then and that we'll operate on once we complete the current run. */ + Set *rebalance_pending_method_calls, *rebalance_queued_method_calls; }; int manager_new(Manager **ret); diff --git a/src/home/org.freedesktop.home1.conf b/src/home/org.freedesktop.home1.conf index 1975d5f1a2b..bac6587b8e8 100644 --- a/src/home/org.freedesktop.home1.conf +++ b/src/home/org.freedesktop.home1.conf @@ -125,6 +125,10 @@ send_interface="org.freedesktop.home1.Manager" send_member="LockAllHomes"/> + +