[systemd-devel] [RFC] core: introduce ExitOnIdle= and ExitOnIdleSec=
WaLyong Cho
walyong.cho at samsung.com
Mon Apr 20 07:56:05 PDT 2015
If a service does not consume CPU during some time(can be configured
by ExitOnIdleSec=) and set to stopped on idle state(ExitOnIdle=), the
service will be stopped. This can be useful if the service provides
some of activation methods.
---
src/core/load-fragment-gperf.gperf.m4 | 2 +
src/core/service.c | 97 +++++++++++++++++++++++++++++++++++
src/core/service.h | 5 ++
src/core/unit.h | 1 +
4 files changed, 105 insertions(+)
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 5305984..60d573e 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -229,6 +229,8 @@ Service.BusName, config_parse_bus_name, 0,
Service.FileDescriptorStoreMax, config_parse_unsigned, 0, offsetof(Service, n_fd_store_max)
Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access)
Service.Sockets, config_parse_service_sockets, 0, 0
+Service.ExitOnIdle, config_parse_bool, 0, offsetof(Service, exit_on_idle)
+Service.ExitOnIdleSec, config_parse_sec, 0, offsetof(Service, exit_on_idle_usec)
m4_ifdef(`ENABLE_KDBUS',
`Service.BusPolicy, config_parse_bus_endpoint_policy, 0, offsetof(Service, exec_context)',
`Service.BusPolicy, config_parse_warn_compat, DISABLED_EXPERIMENTAL, 0')
diff --git a/src/core/service.c b/src/core/service.c
index fa818fc..c8752ae 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -91,6 +91,7 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata);
+static int service_dispatch_exit_on_idle_timer(sd_event_source *source, usec_t usec, void *userdata);
static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
static void service_enter_reload_by_notify(Service *s);
@@ -108,6 +109,8 @@ static void service_init(Unit *u) {
s->socket_fd = -1;
s->bus_endpoint_fd = -1;
s->guess_main_pid = true;
+ s->exit_on_idle = false;
+ s->exit_on_idle_usec = 0;
RATELIMIT_INIT(s->start_limit, u->manager->default_start_limit_interval, u->manager->default_start_limit_burst);
@@ -279,6 +282,56 @@ static void service_release_resources(Unit *u) {
assert(s->n_fd_store == 0);
}
+static void service_stop_exit_on_idle_timer(Service *s) {
+ assert(s);
+
+ s->exit_on_idle_event_source = sd_event_source_unref(s->exit_on_idle_event_source);
+ s->exit_on_idle_timestamp = DUAL_TIMESTAMP_NULL;
+}
+
+static void service_start_exit_on_idle_timer(Service *s) {
+ int r;
+
+ assert(s);
+
+ if (s->exit_on_idle_usec <= 0)
+ return;
+
+ if (s->exit_on_idle_event_source) {
+ r = sd_event_source_set_time(s->exit_on_idle_event_source, s->exit_on_idle_timestamp.monotonic + s->exit_on_idle_usec);
+ if (r < 0) {
+ log_unit_warning(UNIT(s)->id, "%s failed to reset exit-on-idle timer: %s", UNIT(s)->id, strerror(-r));
+ return;
+ }
+
+ r = sd_event_source_set_enabled(s->exit_on_idle_event_source, SD_EVENT_ON);
+ } else {
+ r = sd_event_add_time(
+ UNIT(s)->manager->event,
+ &s->exit_on_idle_event_source,
+ CLOCK_MONOTONIC,
+ s->exit_on_idle_timestamp.monotonic + s->exit_on_idle_usec, 0,
+ service_dispatch_exit_on_idle_timer, s);
+ if (r < 0) {
+ log_unit_warning(UNIT(s)->id, "%s failed to add exit-on-idle timer: %s", UNIT(s)->id, strerror(-r));
+ return;
+ }
+
+ r = sd_event_source_set_priority(s->exit_on_idle_event_source, SD_EVENT_PRIORITY_IDLE);
+ }
+
+ if (r < 0)
+ log_unit_warning(UNIT(s)->id, "%s failed to install exit-on-idle timer: %s", UNIT(s)->id, strerror(-r));
+ return;
+}
+
+static void service_reset_exit_on_idle_timer(Service *s) {
+ assert(s);
+
+ dual_timestamp_get(&s->exit_on_idle_timestamp);
+ service_start_exit_on_idle_timer(s);
+}
+
static void service_done(Unit *u) {
Service *s = SERVICE(u);
@@ -518,6 +571,7 @@ static void service_fix_output(Service *s) {
}
static int service_add_extras(Service *s) {
+ CGroupContext *cc;
int r;
assert(s);
@@ -571,6 +625,15 @@ static int service_add_extras(Service *s) {
return r;
}
+ if (s->exit_on_idle && s->exit_on_idle_usec <= 0)
+ s->exit_on_idle_usec = USEC_PER_MINUTE;
+
+ if (s->exit_on_idle) {
+ cc = unit_get_cgroup_context(UNIT(s));
+ if (cc)
+ cc->cpu_accounting = true;
+ }
+
if (UNIT(s)->default_dependencies) {
r = service_add_default_dependencies(s);
if (r < 0)
@@ -850,6 +913,12 @@ static void service_set_state(Service *s, ServiceState state) {
if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
service_stop_watchdog(s);
+ if (IN_SET(state, SERVICE_RUNNING))
+ service_start_exit_on_idle_timer(s);
+
+ if (IN_SET(state, SERVICE_DEAD))
+ service_stop_exit_on_idle_timer(s);
+
/* For the inactive states unit_notify() will trim the cgroup,
* but for exit we have to do that ourselves... */
if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0)
@@ -2770,6 +2839,34 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void
format_timespan(t, sizeof(t), s->watchdog_usec, 1));
service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG);
+ return 0;
+}
+
+static int service_dispatch_exit_on_idle_timer(sd_event_source *source, usec_t usec, void *userdata) {
+ Service *s = SERVICE(userdata);
+ nsec_t usage;
+ int r;
+
+ assert(s);
+ assert(source == s->exit_on_idle_event_source);
+
+ r = unit_get_cpu_usage(UNIT(s), &usage);
+ if (r < 0) {
+ log_unit_error_errno(UNIT(s)->id, r, "%s failed to get cpu usage: %m", UNIT(s)->id);
+ return 0;
+ }
+
+ if (usage == UNIT(s)->cpuacct_usage) {
+ log_unit_info(UNIT(s)->id, "%s is in idle state, stopping...", UNIT(s)->id);
+ r = manager_add_job(UNIT(s)->manager, JOB_STOP, UNIT(s), JOB_FAIL, true, NULL, NULL);
+ if (r < 0) {
+ log_unit_error_errno(UNIT(s)->id, r, "%s failed to enqueue stopping job: %m", UNIT(s)->id);
+ return r;
+ }
+ } else {
+ UNIT(s)->cpuacct_usage = usage;
+ service_reset_exit_on_idle_timer(s);
+ }
return 0;
}
diff --git a/src/core/service.h b/src/core/service.h
index 7da0a93..909908e 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -212,6 +212,11 @@ struct Service {
ServiceFDStore *fd_store;
unsigned n_fd_store;
unsigned n_fd_store_max;
+
+ bool exit_on_idle;
+ dual_timestamp exit_on_idle_timestamp;
+ usec_t exit_on_idle_usec;
+ sd_event_source *exit_on_idle_event_source;
};
extern const UnitVTable service_vtable;
diff --git a/src/core/unit.h b/src/core/unit.h
index 11242c2..84c0cd0 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -178,6 +178,7 @@ struct Unit {
/* Where the cpuacct.usage cgroup counter was at the time the unit was started */
nsec_t cpuacct_usage_base;
+ nsec_t cpuacct_usage;
/* Counterparts in the cgroup filesystem */
char *cgroup_path;
--
1.9.3
More information about the systemd-devel
mailing list