[systemd-devel] [RFC] core: introduce ExitOnIdle= and ExitOnIdleSec=

Camilo Aguilar camilo.aguilar at gmail.com
Mon Apr 20 07:58:42 PDT 2015


This will be nice to have.
On Mon, Apr 20, 2015 at 10:56 AM WaLyong Cho <walyong.cho at samsung.com>
wrote:

> 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
>
> _______________________________________________
> systemd-devel mailing list
> systemd-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/systemd-devel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/systemd-devel/attachments/20150420/d0481481/attachment-0001.html>


More information about the systemd-devel mailing list