5 #include "config-parser.h"
9 #include "cpu-hotplug.h"
10 #include "proc-common.h"
11 #include "file-helper.h"
12 #include "cpu-cgroup.h"
13 #include "cpu-sched-common.h"
16 #define MOUNTS_PATH "/proc/mounts"
17 #define CPUSET_CGROUP "/sys/fs/cgroup/cpuset"
19 #define GLOBAL_RT_PERIOD_US_PATH "/proc/sys/kernel/sched_rt_period_us"
20 #define GLOBAL_RT_RUNTIME_US_PATH "/proc/sys/kernel/sched_rt_runtime_us"
21 #define CPU_SCHED_FEATURE_PATH "/sys/kernel/debug/sched_features"
31 bool disclaimer_shown;
40 static struct cpu_sched cs;
42 struct coreset *cpu_sched_find_coreset(const char *name)
47 gslist_for_each_item(i, cs.apps) {
48 c = (struct coreset *)i->data;
49 if (!strcmp(name, c->name))
55 /* check if cpuset subsystem is mounted at the right location */
56 static bool cpu_sched_is_cpuset_mounted()
62 char mountpoint[128], opts[128];
65 f = fopen(MOUNTS_PATH, "r");
67 _E("cpu-sched: could not open " MOUNTS_PATH);
71 while ((r = getline(&buf, &len, f)) != -1) {
72 if (sscanf(buf, "%*s %127s %*s %127s %*d %*d", mountpoint, opts) != 2)
75 if (!strcmp(mountpoint, CPUSET_CGROUP) && strstr(opts, "cpuset") != NULL) {
87 static int cpu_sched_init_cgroup_set(const struct coreset *set)
95 r = cgroup_make_subdir(CPUSET_CGROUP, set->name, NULL);
97 _E("[DEBUG] failed to make cpuset cgroup (%s)", set->name);
101 r = snprintf(buf, sizeof buf, "%s/%s", CPUSET_CGROUP, set->name);
103 _E("[DEBUG] failed to setup memory nodes for cpuset (%s)", set->name);
107 /* don't force any memory nodes with this cpuset */
108 r = cgroup_write_node_uint32(buf, "cpuset.mems", 0);
109 ret_value_msg_if(r < 0, r,
110 "[DEBUG] Failed to reset memory nodes for cpuset: %m");
115 /* init cpuset's cgroups */
116 static int cpu_sched_init_cgroup(struct cpu_sched *cs)
122 if (cpu_sched_is_cpuset_mounted() == false) {
123 r = cgroup_make_subdir(CGROUP_PATH, "cpuset", NULL);
125 _E("failed to make cpuset cgroup");
129 r = cgroup_mount_subsystem("cpuset", CPUSET_CGROUP, "cpuset");
131 _E("failed to mount cpuset cgroup: %m");
136 gslist_for_each_item(i, cs->apps) {
137 c = (struct coreset *)i->data;
138 r = cpu_sched_init_cgroup_set(c);
140 _E("cpu-set: error setting up cpuset (%s)", c->name);
145 return cs->fg ? cpu_sched_init_cgroup_set(cs->fg) : 0;
148 /* create single core struct representation */
149 static int cpu_sched_new_core(struct coreset *set, int core_id)
154 assert(core_id >= 0);
156 c = (struct core *)calloc(1, sizeof *c);
158 _E("cpu-sched: could not allocate memory for core struct");
159 return RESOURCED_ERROR_FAIL;
164 set->cores = g_slist_append(set->cores, c);
165 return RESOURCED_ERROR_NONE;
168 /* free memory allocated in coreset structure */
169 static void cpu_sched_free_cpuset(struct coreset **c)
171 struct coreset *set = *c;
173 if (set->cores != NULL)
174 g_slist_free_full(set->cores, free);
184 /* free memory allocated in coreset structure and the structure */
185 static void cpu_sched_free_cpuset_full(void *data)
187 struct coreset *set = (struct coreset *)data;
191 cpu_sched_free_cpuset(&set);
194 /* parse config for specific coreset line */
195 static int cpu_sched_parse_cpuset(struct coreset *set, char *value)
205 ptr = strtok_r(value, ",", &saveptr);
207 if (strstr(ptr, "-") == NULL) { /* single value */
208 r = sscanf(ptr, "%d", &min);
209 if (r == 1 && min >= 0) {
210 if (cpu_sched_new_core(set, min) != RESOURCED_ERROR_NONE)
211 goto parse_cpuset_fail;
213 _E("cpu-sched: error parsing cpuset (%s)", ptr);
214 goto parse_cpuset_fail;
217 r = sscanf(ptr, "%d-%d", &min, &max);
218 if (r == 2 && min >= 0 && max >= 0 && max > min) {
219 for (int i = min; i <= max; i++) {
220 if (cpu_sched_new_core(set, i) != RESOURCED_ERROR_NONE)
221 goto parse_cpuset_fail;
224 _E("cpu-sched: error parsing cpuset (%s)", ptr);
225 goto parse_cpuset_fail;
230 ptr = strtok_r(NULL, ",", &saveptr);
232 return RESOURCED_ERROR_NONE;
234 cpu_sched_free_cpuset(&set);
235 return RESOURCED_ERROR_FAIL;
238 static int load_cpu_affinity_config(struct cpu_sched *data)
245 if (!get_cpucg_conf_name())
246 return RESOURCED_ERROR_NONE;
248 name = strdup(get_cpucg_conf_name());
249 is_fg = !strcmp(name, FOREGROUND_APPS);
251 struct coreset *c = (struct coreset *)calloc(1, sizeof *c);
254 return RESOURCED_ERROR_OUT_OF_MEMORY;
259 if (cpu_sched_parse_cpuset(c, get_cpucg_conf_value()) < 0) {
260 _E("[DEBUG] cpu-sched parse %s coreset: could not parse", name);
261 return RESOURCED_ERROR_FAIL;
267 data->apps = g_slist_append(data->apps, c);
270 return RESOURCED_ERROR_NONE;
273 static int load_cpu_sched_config(void)
275 struct cpu_sched_conf *cpu_sched_conf = get_cpu_sched_conf();
276 if (cpu_sched_conf == NULL) {
277 _E("[DEBUG] cpu sched configuration structure should not be NULL");
278 return RESOURCED_ERROR_FAIL;
281 if (cpu_sched_conf->rt_period_us > 0 &&
282 cpu_sched_conf->rt_runtime_us > 0 &&
283 cpu_sched_conf->rt_period_us > cpu_sched_conf->rt_runtime_us) {
284 fwrite_int(GLOBAL_RT_PERIOD_US_PATH, cpu_sched_conf->rt_period_us);
285 fwrite_int(GLOBAL_RT_RUNTIME_US_PATH, cpu_sched_conf->rt_runtime_us);
288 if (cpu_sched_conf->cpu_sched_flag) {
289 if (CHECK_BIT(cpu_sched_conf->cpu_sched_flag, CPU_SCHED_RUNTIME_SHARE) &&
290 CHECK_BIT(cpu_sched_conf->cpu_sched_flag, CPU_SCHED_NO_RUNTIME_SHARE)) {
291 _E("[DEBUG] RT_RUNTIME_SHARE and NO_RT_RUNTIME_SHARE cannot be coexisted");
294 /* RT_RUNTIME_SHARE */
295 if (CHECK_BIT(cpu_sched_conf->cpu_sched_flag, CPU_SCHED_RUNTIME_SHARE))
296 fwrite_str(CPU_SCHED_FEATURE_PATH, "RT_RUNTIME_SHARE");
297 else if (CHECK_BIT(cpu_sched_conf->cpu_sched_flag, CPU_SCHED_NO_RUNTIME_SHARE))
298 fwrite_str(CPU_SCHED_FEATURE_PATH, "NO_RT_RUNTIME_SHARE");
300 /* RT_RUNTIME_GREED */
301 if (CHECK_BIT(cpu_sched_conf->cpu_sched_flag, CPU_SCHED_RUNTIME_GREED))
302 fwrite_str(CPU_SCHED_FEATURE_PATH, "RT_RUNTIME_GREED");
306 _I("[DEBUG] rt_period_us = %d", cpu_sched_conf->rt_period_us);
307 _I("[DEBUG] rt_runtime_us = %d", cpu_sched_conf->rt_runtime_us);
308 _I("[DEBUG] cpu_sched_feature = %d", cpu_sched_conf->cpu_sched_flag);
310 free_cpu_sched_conf();
311 return RESOURCED_ERROR_NONE;
314 static int cpu_sched_parse_config(struct cpu_sched *data)
316 load_cpu_affinity_config(data);
317 load_cpu_sched_config();
318 return RESOURCED_ERROR_NONE;
321 static int cpu_sched_write_coreset(struct coreset *set)
325 char path[128], coreset[128];
330 r = snprintf(path, sizeof path, "%s/%s", CPUSET_CGROUP, set->name);
332 _E("cpu-sched: failed to setup path for cpuset (%s)", set->name);
337 gslist_for_each_item(i, set->cores) {
338 c = (struct core *)i->data;
339 if (c == NULL || c->on == false)
342 r += snprintf(coreset + r, sizeof coreset - r, "%d,", c->id);
348 return cgroup_write_node_str(path, "cpuset.cpus", coreset);
351 static int cpu_sched_cpu_on_for_coreset(struct coreset *set, int core_id)
355 bool refresh = false;
358 assert(core_id >= 0);
360 if (set->cores == NULL)
363 gslist_for_each_item(i, set->cores) {
364 c = (struct core *)i->data;
365 if (c == NULL || c->id != core_id)
368 _D("cpu-sched: core %d on for coreset %s", core_id, set->name);
374 if (refresh == false)
377 return cpu_sched_write_coreset(set);
380 static int cpu_sched_cpu_off_for_coreset(struct coreset *set, int core_id)
384 bool refresh = false;
387 assert(core_id >= 0);
389 if (set->cores == NULL)
392 gslist_for_each_item(i, set->cores) {
393 c = (struct core *)i->data;
394 if (c == NULL || c->id != core_id)
397 _D("cpu-sched: core %d off for coreset %s", core_id, set->name);
403 if (refresh == false)
406 return cpu_sched_write_coreset(set);
409 static int cpu_sched_cpu_on(void *data)
418 _D("cpu-sched: core %d plugged in", id);
419 gslist_for_each_item(i, cs.apps) {
420 cpu_sched_cpu_on_for_coreset((struct coreset *)i->data, id);
423 cpu_sched_cpu_on_for_coreset(cs.fg, id);
425 return RESOURCED_ERROR_NONE;
428 static int cpu_sched_cpu_off(void *data)
437 _D("cpu-sched: core %d plugged out", id);
438 gslist_for_each_item(i, cs.apps) {
439 cpu_sched_cpu_off_for_coreset((struct coreset *)i->data, id);
443 cpu_sched_cpu_off_for_coreset(cs.fg, id);
445 return RESOURCED_ERROR_NONE;
448 static int cpu_sched_add_pid_to_cpuset(struct coreset *set, pid_t pid)
452 _D("cpu-sched: add pid %d to cpuset %s", pid, set->name);
454 cgroup_write_pid(CPUSET_CGROUP, set->name, pid);
456 return RESOURCED_ERROR_NONE;
459 static int cpu_sched_remove_pid_from_cpuset(struct coreset *set, pid_t pid)
463 _D("cpu-sched: moving pid %d to toplevel cpuset (from %s cpuset)", pid, set->name);
465 cgroup_write_pid_fullpath(CPUSET_CGROUP, pid);
467 return RESOURCED_ERROR_NONE;
470 /* if app is subject to static coreset config its coreset should not be changed */
471 static bool cpu_sched_is_static_app(const char *appid)
475 struct coreset *c = cpu_sched_find_coreset(appid);
479 if (c->disclaimer_shown == false) {
480 _D("cpu-sched: appid %s is statically configured - not subject to cpuset change", appid);
481 c->disclaimer_shown = true;
487 static int cpu_sched_app_foreground(void *data)
489 struct proc_status *ps = (struct proc_status *)data;
495 if (cs.is_initialized == false || cpu_sched_is_static_app(ps->pai->appid) == true)
496 return RESOURCED_ERROR_NONE;
498 _D("cpu-sched: app %s moved to foreground; pid=%d", ps->pai->appid, ps->pid);
500 return cpu_sched_add_pid_to_cpuset(cs.fg, ps->pid);
503 static int cpu_sched_app_background(void *data)
505 struct proc_status *ps = (struct proc_status *)data;
511 if (cs.is_initialized == false || cpu_sched_is_static_app(ps->pai->appid) == true)
512 return RESOURCED_ERROR_NONE;
514 _D("cpu-sched: app %s moved to background; pid=%d", ps->pai->appid, ps->pid);
516 return cpu_sched_remove_pid_from_cpuset(cs.fg, ps->pid);
519 static int cpu_sched_app_launch(void *data)
521 struct proc_status *ps = (struct proc_status *)data;
527 _D("cpu-sched: app launch: %s", ps->pai->appid);
529 c = cpu_sched_find_coreset(ps->pai->appid);
533 return cpu_sched_add_pid_to_cpuset(c, ps->pid);
536 static void register_notifiers()
539 register_notifier(RESOURCED_NOTIFIER_APP_RESUME, cpu_sched_app_foreground);
540 register_notifier(RESOURCED_NOTIFIER_APP_SUSPEND, cpu_sched_app_background);
542 register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_sched_app_foreground);
543 register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_sched_app_background);
545 register_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, cpu_sched_app_foreground);
546 register_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, cpu_sched_app_background);
549 register_notifier(RESOURCED_NOTIFIER_CPU_ON, cpu_sched_cpu_on);
550 register_notifier(RESOURCED_NOTIFIER_CPU_OFF, cpu_sched_cpu_off);
552 register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, cpu_sched_app_launch);
553 register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, cpu_sched_app_launch);
556 static void unregister_notifiers()
559 unregister_notifier(RESOURCED_NOTIFIER_APP_RESUME, cpu_sched_app_foreground);
560 unregister_notifier(RESOURCED_NOTIFIER_APP_SUSPEND, cpu_sched_app_background);
562 unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_sched_app_foreground);
563 unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_sched_app_background);
565 unregister_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, cpu_sched_app_foreground);
566 unregister_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, cpu_sched_app_background);
569 unregister_notifier(RESOURCED_NOTIFIER_CPU_ON, cpu_sched_cpu_on);
570 unregister_notifier(RESOURCED_NOTIFIER_CPU_OFF, cpu_sched_cpu_off);
572 unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, cpu_sched_app_launch);
573 unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, cpu_sched_app_launch);
576 static void cpu_sched_free_cpusets()
578 g_slist_free_full(g_steal_pointer(&cs.apps), cpu_sched_free_cpuset_full);
580 cpu_sched_free_cpuset(&cs.fg);
583 static void cpu_sched_check_apps()
585 _cleanup_app_list_close_ GSList *proc_app_list = PAL_INIT_VALUE;
587 struct proc_app_info *pai;
590 proc_app_list = proc_app_list_open();
591 gslist_for_each_item(giter, proc_app_list) {
592 pai = (struct proc_app_info *)giter->data;
593 if (!pai || !pai->main_pid)
596 c = cpu_sched_find_coreset(pai->appid);
598 cpu_sched_add_pid_to_cpuset(c, pai->main_pid);
602 if (cs.fg && pai->state == PROC_STATE_FOREGROUND)
603 cpu_sched_add_pid_to_cpuset(cs.fg, pai->main_pid);
607 static int cpu_sched_init(void *data)
611 if (cpu_sched_parse_config(&cs) != RESOURCED_ERROR_NONE) {
612 _E("cpu-sched: error parsing config");
613 return RESOURCED_ERROR_FAIL;
616 r = cpu_sched_init_cgroup(&cs);
620 register_notifiers();
621 if (cpu_hotplug_init() != 0) {
622 _E("cpu_sched: could not setup cpu hotplugging");
623 return RESOURCED_ERROR_FAIL;
626 cs.is_initialized = true;
627 cpu_sched_check_apps();
628 return RESOURCED_ERROR_NONE;
631 unregister_notifiers();
632 cpu_hotplug_finalize();
633 cpu_sched_free_cpusets();
634 cs.is_initialized = false;
635 return RESOURCED_ERROR_FAIL;
638 static int cpu_sched_finalize(void *data)
640 _D("cpu-sched: deinit module");
641 unregister_notifiers();
642 cpu_hotplug_finalize();
643 cpu_sched_free_cpusets();
644 cs.is_initialized = false;
645 return RESOURCED_ERROR_NONE;
648 static struct module_ops cpu_sched_modules_ops = {
649 .priority = MODULE_PRIORITY_NORMAL,
651 .init = cpu_sched_init,
652 .exit = cpu_sched_finalize,
655 MODULE_REGISTER(&cpu_sched_modules_ops)