homed: add explicit API for requesting rebalancing too

This commit is contained in:
Lennart Poettering 2021-11-04 16:32:05 +01:00
parent d357b80d33
commit 4950591627
7 changed files with 72 additions and 3 deletions

View file

@ -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 {
<variablelist class="dbus-method" generated="True" extra-ref="DeactivateAllHomes()"/>
<variablelist class="dbus-method" generated="True" extra-ref="Rebalance()"/>
<variablelist class="dbus-property" generated="True" extra-ref="AutoLogin"/>
<!--End of Autogenerated section-->
@ -346,6 +349,10 @@ node /org/freedesktop/home1 {
<para><function>DeactivateAllHomes()</function> deactivates all home areas that are currently
active. This is usually invoked automatically shortly before system shutdown.</para>
<para><function>Rebalance()</function> 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.</para>
</refsect2>
<refsect2>

View file

@ -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
};

View file

@ -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;
}

View file

@ -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);

View file

@ -125,6 +125,10 @@
send_interface="org.freedesktop.home1.Manager"
send_member="LockAllHomes"/>
<allow send_destination="org.freedesktop.home1"
send_interface="org.freedesktop.home1.Manager"
send_member="Rebalance"/>
<!-- Home object -->
<allow send_destination="org.freedesktop.home1"

View file

@ -143,6 +143,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_AUTHENTICATION_LIMIT_HIT, ETOOMANYREFS),
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_CANT_AUTHENTICATE, EKEYREVOKED),
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_IN_USE, EADDRINUSE),
SD_BUS_ERROR_MAP(BUS_ERROR_REBALANCE_NOT_NEEDED, EALREADY),
SD_BUS_ERROR_MAP_END
};

View file

@ -127,5 +127,6 @@
#define BUS_ERROR_AUTHENTICATION_LIMIT_HIT "org.freedesktop.home1.AuthenticationLimitHit"
#define BUS_ERROR_HOME_CANT_AUTHENTICATE "org.freedesktop.home1.HomeCantAuthenticate"
#define BUS_ERROR_HOME_IN_USE "org.freedesktop.home1.HomeInUse"
#define BUS_ERROR_REBALANCE_NOT_NEEDED "org.freedesktop.home1.RebalanceNotNeeded"
BUS_ERROR_MAP_ELF_USE(bus_common_errors);