#include "resourced.h"
#include "trace.h"
#include "vconf.h"
+#include "util.h"
#include "cgroup.h"
#include "config-parser.h"
#include "const.h"
#define CPU_BACKGROUND_PRI 1
#define CPU_CONTROL_PRI 10
-static void cpu_priority_update(int which, pid_t pid, int priority, struct proc_app_info *pai)
+bool throttling_success = false;
+bool skip_bandwidth = false;
+bool skip_share = false;
+struct sched_attr throttling_attr;
+struct sched_attr normal_attr;
+
+static int move_process_to_throttling_group(pid_t pid, struct proc_app_info *pai)
{
- if (pai && pai->app_cpu_nice_update_exclude)
- return;
+ GSList *iter = NULL;
+ pid_t child_pid;
+
+ if (!throttling_success)
+ return RESOURCED_ERROR_NONE;
+
+ if (pai) {
+ if (pai->app_cpu_nice_update_exclude)
+ goto cpu_cgroup_move;
+
+ sched_setattr_of_all_tasks(pai->main_pid, &throttling_attr, 0);
+ if (pai->childs) {
+ gslist_for_each_item(iter, pai->childs) {
+ child_pid = GPOINTER_TO_PID(iter->data);
+ sched_setattr_of_all_tasks(child_pid, &throttling_attr, 0);
+ }
+ }
+ }
+ else {
+ sched_setattr_of_all_tasks(pid, &throttling_attr, 0);
+ }
+
+cpu_cgroup_move:
+ if (!skip_share || !skip_bandwidth)
+ return cpu_move_cgroup_foreach(pid, pai, CPUCG_THROTTLING_PATH);
+ else
+ return RESOURCED_ERROR_NONE;
+}
+
+static int move_out_process_from_throttling_group(pid_t pid, struct proc_app_info *pai)
+{
+ GSList *iter = NULL;
+ pid_t child_pid;
+
+ if (!throttling_success)
+ return RESOURCED_ERROR_NONE;
+
+ if (pai) {
+ if (pai->app_cpu_nice_update_exclude)
+ goto cpu_cgroup_move;
+
+ sched_setattr_of_all_tasks(pai->main_pid, &normal_attr, 0);
+ if (pai->childs) {
+ gslist_for_each_item(iter, pai->childs) {
+ child_pid = GPOINTER_TO_PID(iter->data);
+ sched_setattr_of_all_tasks(child_pid, &normal_attr, 0);
+ }
+ }
+ }
+ else {
+ sched_setattr_of_all_tasks(pid, &normal_attr, 0);
+ }
- setpriority(which, pid, priority);
+cpu_cgroup_move:
+ if (!skip_share || !skip_bandwidth)
+ return cpu_move_cgroup_foreach(pid, pai, CPUCG_PATH);
+ else
+ return RESOURCED_ERROR_NONE;
}
static int cpu_foreground_state(void *data)
{
+ int ret;
struct proc_status *ps = (struct proc_status *)data;
- int pri;
assert(ps);
_D("app foreground: pid = %d", ps->pid);
- pri = getpriority(PRIO_PROCESS, ps->pid);
- if (pri == -1 || pri > CPU_DEFAULT_PRI)
- cpu_priority_update(PRIO_PGRP, ps->pid, CPU_DEFAULT_PRI, ps->pai);
+ ret = move_out_process_from_throttling_group(ps->pid, ps->pai);
+ if (ret < 0)
+ _E("Failed to throttle cpu resource of %s%s process (%d)",
+ ps->pai ? "App " : "System service",
+ ps->pai ? ps->pai->appid : "", ps->pid);
return RESOURCED_ERROR_NONE;
}
static int cpu_background_state(void *data)
{
+ int ret;
struct proc_status *ps = (struct proc_status *)data;
assert(ps);
_D("app background: pid = %d", ps->pid);
- cpu_priority_update(PRIO_PGRP, ps->pid, CPU_BACKGROUND_PRI, ps->pai);
+ ret = move_process_to_throttling_group(ps->pid, ps->pai);
+ if (ret < 0)
+ _E("Failed to throttle cpu resource of %s%s process (%d)",
+ ps->pai ? "App " : "System service",
+ ps->pai ? ps->pai->appid : "", ps->pid);
return RESOURCED_ERROR_NONE;
}
-static int resourced_cpu_init(void *data)
+static void load_cpu_throttling_config(void)
{
- int ret_code;
+ int ret;
+ int cpu_nice;
+ long long cpu_runtime_us;
+ unsigned long long cpu_period_us;
+ unsigned long long cpu_share;
+ enum cpu_sched_type cpu_sched_type;
+
+ struct cpu_throttling_conf *cpu_throttling_conf = get_cpu_throttling_conf();
+ if (cpu_throttling_conf == NULL) {
+ _E("[CPU-THROTTLING] cpu sched configuration structure should not be NULL");
+ return;
+ }
+
+ if (!cpu_throttling_conf->enable) {
+ _D("[CPU-THROTTLING] cpu throttling is disabled");
+ goto free_cpu_throttling_conf;
+ }
+ cpu_nice = cpu_throttling_conf->cpu_sched_info.cpu_nice;
+ cpu_sched_type = cpu_throttling_conf->cpu_sched_info.cpu_sched_type;
+ cpu_share = cpu_throttling_conf->cpu_cgroup_info.cpu_share;
+ cpu_period_us = cpu_throttling_conf->cpu_cgroup_info.cfs_period_us;
+ cpu_runtime_us = cpu_throttling_conf->cpu_cgroup_info.cfs_runtime_us;
+
+ if (cpu_share == 0) {
+ _E("[CPU-THROTTLING] cpu share cannot be 0");
+ cpu_share = CPU_THROTTLING_SHARE;
+ skip_share = true;
+ }
+
+ if (cpu_period_us == 0 || cpu_runtime_us == 0 ||
+ cpu_period_us < cpu_runtime_us) {
+ _E("[CPU-THROTTLING] cpu period (%llu) and runtime (%lld) is out of scope",
+ cpu_period_us, cpu_runtime_us);
+ skip_bandwidth = true;
+ }
+
+ if (cpu_sched_type < CPU_SCHED_IDLE || cpu_sched_type > CPU_SCHED_OTHER) {
+ _E("[CPU-THROTTLING] cpu scheduler type is unknown or realtime");
+ cpu_sched_type = CPU_SCHED_IDLE;
+ }
+
+ if (cpu_sched_type != CPU_SCHED_IDLE &&
+ (cpu_nice < CPU_MIN_NICE || cpu_nice > CPU_MAX_NICE)) {
+ _W("[CPU-THROTTLING] cpu nice is out of scope");
+ cpu_nice = CPU_MAX_NICE; /* the worst nice value */
+ }
+
+ /* Make a cpu throttling directory at the cpu cgroup */
+ if (!skip_share || !skip_bandwidth) {
+ ret = cpucg_make_full_subdir(CPUCG_PATH);
+ if (ret < 0) {
+ _E("Failed to initialize cpu cgroup for throttling\n");
+ goto free_cpu_throttling_conf;
+ }
+
+ /* Initialize cpu throttling cgroup */
+ cgroup_write_node_ulonglong(CPUCG_THROTTLING_PATH, CPUCG_SHARE, cpu_share);
+ cgroup_write_node_longlong(CPUCG_THROTTLING_PATH, CPUCG_CONTROL_BANDWIDTH,
+ cpu_runtime_us);
+ cgroup_write_node_ulonglong(CPUCG_THROTTLING_PATH, CPUCG_CONTROL_FULL_BANDWIDTH,
+ cpu_period_us);
+ }
+
+ /* Initialize type and nice of cpu throttling scheduler */
+ memset(&normal_attr, 0, sizeof(struct sched_attr));
+ normal_attr.size = sizeof(struct sched_attr);
+ normal_attr.sched_nice = CPU_DEFAULT_PRI;
+ normal_attr.sched_policy = SCHED_OTHER;
+
+ memset(&throttling_attr, 0, sizeof(struct sched_attr));
+ throttling_attr.size = sizeof(struct sched_attr);
+ throttling_attr.sched_nice = cpu_nice;
+ switch (cpu_sched_type) {
+ case CPU_SCHED_OTHER:
+ throttling_attr.sched_policy = SCHED_OTHER;
+ break;
+ case CPU_SCHED_IDLE:
+ throttling_attr.sched_policy = SCHED_IDLE;
+ break;
+ case CPU_SCHED_BATCH:
+ throttling_attr.sched_policy = SCHED_BATCH;
+ break;
+ default:
+ if (!skip_share || !skip_bandwidth)
+ rmdir(CPUCG_THROTTLING_PATH);
+ goto free_cpu_throttling_conf;
+ }
+
+ throttling_success = true;
+
+ _I("[CPU-THROTTLING] throttling policy = %s",
+ throttling_attr.sched_policy == SCHED_IDLE ? "SCHED_IDLE" :
+ throttling_attr.sched_policy == SCHED_BATCH ? "SCHED_BATCH" :
+ throttling_attr.sched_policy == SCHED_OTHER ? "SCHED_OTHER" : "wrong scheduler type");
+ _I("[CPU-THROTTLING] throttling nice = %d", throttling_attr.sched_nice);
+
+free_cpu_throttling_conf:
+ free_cpu_throttling_conf();
+}
+
+static int resourced_cpu_init(void *data)
+{
_D("resourced cpu init start");
- ret_code = cpucg_make_full_subdir(CPUCG_PATH);
- ret_value_msg_if(ret_code < 0, ret_code, "cpu cgroup init failed\n");
+ load_cpu_throttling_config();
+
register_notifier(RESOURCED_NOTIFIER_APP_RESUME, cpu_foreground_state);
register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_foreground_state);
register_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, cpu_foreground_state);
register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_background_state);
+ register_notifier(RESOURCED_NOTIFIER_THROTTLING_SYSTEM_SERVICE, cpu_background_state);
return RESOURCED_ERROR_NONE;
}
unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_foreground_state);
unregister_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, cpu_foreground_state);
unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_background_state);
+ unregister_notifier(RESOURCED_NOTIFIER_THROTTLING_SYSTEM_SERVICE, cpu_background_state);
return RESOURCED_ERROR_NONE;
}