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"
15 #define MOUNTS_PATH "/proc/mounts"
16 #define CPUSET_CGROUP "/sys/fs/cgroup/cpuset"
27 bool disclaimer_shown;
36 static struct cpu_sched cs;
38 struct coreset *cpu_sched_find_coreset(const char *name)
43 gslist_for_each_item(i, cs.apps) {
44 c = (struct coreset *)i->data;
45 if (!strcmp(name, c->name))
51 /* check if cpuset subsystem is mounted at the right location */
52 static bool cpu_sched_is_cpuset_mounted()
58 char mountpoint[128], opts[128];
61 f = fopen(MOUNTS_PATH, "r");
63 _E("cpu-sched: could not open " MOUNTS_PATH);
67 while ((r = getline(&buf, &len, f)) != -1) {
68 if (sscanf(buf, "%*s %127s %*s %127s %*d %*d", mountpoint, opts) != 2)
71 if (!strcmp(mountpoint, CPUSET_CGROUP) && strstr(opts, "cpuset") != NULL) {
83 static int cpu_sched_init_cgroup_set(const struct coreset *set)
91 r = cgroup_make_subdir(CPUSET_CGROUP, set->name, NULL);
93 _E("[DEBUG] failed to make cpuset cgroup (%s)", set->name);
97 r = snprintf(buf, sizeof buf, "%s/%s", CPUSET_CGROUP, set->name);
99 _E("[DEBUG] failed to setup memory nodes for cpuset (%s)", set->name);
103 /* don't force any memory nodes with this cpuset */
104 r = cgroup_write_node_uint32(buf, "cpuset.mems", 0);
105 ret_value_msg_if(r < 0, r,
106 "[DEBUG] Failed to reset memory nodes for cpuset: %m");
111 /* init cpuset's cgroups */
112 static int cpu_sched_init_cgroup(struct cpu_sched *cs)
118 if (cpu_sched_is_cpuset_mounted() == false) {
119 r = cgroup_make_subdir(CGROUP_PATH, "cpuset", NULL);
121 _E("failed to make cpuset cgroup");
125 r = cgroup_mount_subsystem("cpuset", CPUSET_CGROUP, "cpuset");
127 _E("failed to mount cpuset cgroup: %m");
132 gslist_for_each_item(i, cs->apps) {
133 c = (struct coreset *)i->data;
134 r = cpu_sched_init_cgroup_set(c);
136 _E("cpu-set: error setting up cpuset (%s)", c->name);
141 return cs->fg ? cpu_sched_init_cgroup_set(cs->fg) : 0;
144 /* create single core struct representation */
145 static int cpu_sched_new_core(struct coreset *set, int core_id)
150 assert(core_id >= 0);
152 c = (struct core *)calloc(1, sizeof *c);
154 _E("cpu-sched: could not allocate memory for core struct");
155 return RESOURCED_ERROR_FAIL;
160 set->cores = g_slist_append(set->cores, c);
161 return RESOURCED_ERROR_NONE;
164 /* free memory allocated in coreset structure */
165 static void cpu_sched_free_cpuset(struct coreset **c)
167 struct coreset *set = *c;
169 if (set->cores != NULL)
170 g_slist_free_full(set->cores, free);
180 /* free memory allocated in coreset structure and the structure */
181 static void cpu_sched_free_cpuset_full(void *data)
183 struct coreset *set = (struct coreset *)data;
187 cpu_sched_free_cpuset(&set);
190 /* parse config for specific coreset line */
191 static int cpu_sched_parse_cpuset(struct coreset *set, char *value)
201 ptr = strtok_r(value, ",", &saveptr);
203 if (strstr(ptr, "-") == NULL) { /* single value */
204 r = sscanf(ptr, "%d", &min);
205 if (r == 1 && min >= 0) {
206 if (cpu_sched_new_core(set, min) != RESOURCED_ERROR_NONE)
207 goto parse_cpuset_fail;
209 _E("cpu-sched: error parsing cpuset (%s)", ptr);
210 goto parse_cpuset_fail;
213 r = sscanf(ptr, "%d-%d", &min, &max);
214 if (r == 2 && min >= 0 && max >= 0 && max > min) {
215 for (int i = min; i <= max; i++) {
216 if (cpu_sched_new_core(set, i) != RESOURCED_ERROR_NONE)
217 goto parse_cpuset_fail;
220 _E("cpu-sched: error parsing cpuset (%s)", ptr);
221 goto parse_cpuset_fail;
226 ptr = strtok_r(NULL, ",", &saveptr);
228 return RESOURCED_ERROR_NONE;
230 cpu_sched_free_cpuset(&set);
231 return RESOURCED_ERROR_FAIL;
234 static int load_config(struct cpu_sched *data)
241 if (!get_cpucg_conf_name())
242 return RESOURCED_ERROR_NONE;
244 name = strdup(get_cpucg_conf_name());
245 is_fg = !strcmp(name, FOREGROUND_APPS);
247 struct coreset *c = (struct coreset *)calloc(1, sizeof *c);
250 return RESOURCED_ERROR_OUT_OF_MEMORY;
255 if (cpu_sched_parse_cpuset(c, get_cpucg_conf_value()) < 0) {
256 _E("[DEBUG] cpu-sched parse %s coreset: could not parse", name);
257 return RESOURCED_ERROR_FAIL;
263 data->apps = g_slist_append(data->apps, c);
266 return RESOURCED_ERROR_NONE;
269 static int cpu_sched_parse_config(struct cpu_sched *data)
272 return RESOURCED_ERROR_NONE;
275 static int cpu_sched_write_coreset(struct coreset *set)
279 char path[128], coreset[128];
284 r = snprintf(path, sizeof path, "%s/%s", CPUSET_CGROUP, set->name);
286 _E("cpu-sched: failed to setup path for cpuset (%s)", set->name);
291 gslist_for_each_item(i, set->cores) {
292 c = (struct core *)i->data;
293 if (c == NULL || c->on == false)
296 r += snprintf(coreset + r, sizeof coreset - r, "%d,", c->id);
302 return cgroup_write_node_str(path, "cpuset.cpus", coreset);
305 static int cpu_sched_cpu_on_for_coreset(struct coreset *set, int core_id)
309 bool refresh = false;
312 assert(core_id >= 0);
314 if (set->cores == NULL)
317 gslist_for_each_item(i, set->cores) {
318 c = (struct core *)i->data;
319 if (c == NULL || c->id != core_id)
322 _D("cpu-sched: core %d on for coreset %s", core_id, set->name);
328 if (refresh == false)
331 return cpu_sched_write_coreset(set);
334 static int cpu_sched_cpu_off_for_coreset(struct coreset *set, int core_id)
338 bool refresh = false;
341 assert(core_id >= 0);
343 if (set->cores == NULL)
346 gslist_for_each_item(i, set->cores) {
347 c = (struct core *)i->data;
348 if (c == NULL || c->id != core_id)
351 _D("cpu-sched: core %d off for coreset %s", core_id, set->name);
357 if (refresh == false)
360 return cpu_sched_write_coreset(set);
363 static int cpu_sched_cpu_on(void *data)
372 _D("cpu-sched: core %d plugged in", id);
373 gslist_for_each_item(i, cs.apps) {
374 cpu_sched_cpu_on_for_coreset((struct coreset *)i->data, id);
377 cpu_sched_cpu_on_for_coreset(cs.fg, id);
379 return RESOURCED_ERROR_NONE;
382 static int cpu_sched_cpu_off(void *data)
391 _D("cpu-sched: core %d plugged out", id);
392 gslist_for_each_item(i, cs.apps) {
393 cpu_sched_cpu_off_for_coreset((struct coreset *)i->data, id);
397 cpu_sched_cpu_off_for_coreset(cs.fg, id);
399 return RESOURCED_ERROR_NONE;
402 static int cpu_sched_add_pid_to_cpuset(struct coreset *set, pid_t pid)
406 _D("cpu-sched: add pid %d to cpuset %s", pid, set->name);
408 cgroup_write_pid(CPUSET_CGROUP, set->name, pid);
410 return RESOURCED_ERROR_NONE;
413 static int cpu_sched_remove_pid_from_cpuset(struct coreset *set, pid_t pid)
417 _D("cpu-sched: moving pid %d to toplevel cpuset (from %s cpuset)", pid, set->name);
419 cgroup_write_pid_fullpath(CPUSET_CGROUP, pid);
421 return RESOURCED_ERROR_NONE;
424 /* if app is subject to static coreset config its coreset should not be changed */
425 static bool cpu_sched_is_static_app(const char *appid)
429 struct coreset *c = cpu_sched_find_coreset(appid);
433 if (c->disclaimer_shown == false) {
434 _D("cpu-sched: appid %s is statically configured - not subject to cpuset change", appid);
435 c->disclaimer_shown = true;
441 static int cpu_sched_app_foreground(void *data)
443 struct proc_status *ps = (struct proc_status *)data;
449 if (cs.is_initalized == false || cpu_sched_is_static_app(ps->pai->appid) == true)
450 return RESOURCED_ERROR_NONE;
452 _D("cpu-sched: app %s moved to foreground; pid=%d", ps->pai->appid, ps->pid);
454 return cpu_sched_add_pid_to_cpuset(cs.fg, ps->pid);
457 static int cpu_sched_app_background(void *data)
459 struct proc_status *ps = (struct proc_status *)data;
465 if (cs.is_initalized == false || cpu_sched_is_static_app(ps->pai->appid) == true)
466 return RESOURCED_ERROR_NONE;
468 _D("cpu-sched: app %s moved to background; pid=%d", ps->pai->appid, ps->pid);
470 return cpu_sched_remove_pid_from_cpuset(cs.fg, ps->pid);
473 static int cpu_sched_app_launch(void *data)
475 struct proc_status *ps = (struct proc_status *)data;
481 _D("cpu-sched: app launch: %s", ps->pai->appid);
483 c = cpu_sched_find_coreset(ps->pai->appid);
487 return cpu_sched_add_pid_to_cpuset(c, ps->pid);
490 static void register_notifiers()
493 register_notifier(RESOURCED_NOTIFIER_APP_RESUME, cpu_sched_app_foreground);
494 register_notifier(RESOURCED_NOTIFIER_APP_SUSPEND, cpu_sched_app_background);
496 register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_sched_app_foreground);
497 register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_sched_app_background);
499 register_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, cpu_sched_app_foreground);
500 register_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, cpu_sched_app_background);
503 register_notifier(RESOURCED_NOTIFIER_CPU_ON, cpu_sched_cpu_on);
504 register_notifier(RESOURCED_NOTIFIER_CPU_OFF, cpu_sched_cpu_off);
506 register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, cpu_sched_app_launch);
507 register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, cpu_sched_app_launch);
510 static void unregister_notifiers()
513 unregister_notifier(RESOURCED_NOTIFIER_APP_RESUME, cpu_sched_app_foreground);
514 unregister_notifier(RESOURCED_NOTIFIER_APP_SUSPEND, cpu_sched_app_background);
516 unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_sched_app_foreground);
517 unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_sched_app_background);
519 unregister_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, cpu_sched_app_foreground);
520 unregister_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, cpu_sched_app_background);
523 unregister_notifier(RESOURCED_NOTIFIER_CPU_ON, cpu_sched_cpu_on);
524 unregister_notifier(RESOURCED_NOTIFIER_CPU_OFF, cpu_sched_cpu_off);
526 unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, cpu_sched_app_launch);
527 unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, cpu_sched_app_launch);
530 static void cpu_sched_free_cpusets()
532 g_slist_free_full(g_steal_pointer(&cs.apps), cpu_sched_free_cpuset_full);
534 cpu_sched_free_cpuset(&cs.fg);
537 static void cpu_sched_check_apps()
539 _cleanup_app_list_close_ GSList *proc_app_list = PAL_INIT_VALUE;
541 struct proc_app_info *pai;
544 proc_app_list = proc_app_list_open();
545 gslist_for_each_item(giter, proc_app_list) {
546 pai = (struct proc_app_info *)giter->data;
547 if (!pai || !pai->main_pid)
550 c = cpu_sched_find_coreset(pai->appid);
552 cpu_sched_add_pid_to_cpuset(c, pai->main_pid);
556 if (cs.fg && pai->state == PROC_STATE_FOREGROUND)
557 cpu_sched_add_pid_to_cpuset(cs.fg, pai->main_pid);
561 static int cpu_sched_init(void *data)
565 if (cpu_sched_parse_config(&cs) != RESOURCED_ERROR_NONE) {
566 _E("cpu-sched: error parsing config");
567 return RESOURCED_ERROR_FAIL;
570 r = cpu_sched_init_cgroup(&cs);
574 register_notifiers();
575 if (cpu_hotplug_init() != 0) {
576 _E("cpu_sched: could not setup cpu hotplugging");
577 return RESOURCED_ERROR_FAIL;
580 cs.is_initalized = true;
581 cpu_sched_check_apps();
582 return RESOURCED_ERROR_NONE;
585 unregister_notifiers();
586 cpu_hotplug_finalize();
587 cpu_sched_free_cpusets();
588 cs.is_initalized = false;
589 return RESOURCED_ERROR_FAIL;
592 static int cpu_sched_finalize(void *data)
594 _D("cpu-sched: deinit module");
595 unregister_notifiers();
596 cpu_hotplug_finalize();
597 cpu_sched_free_cpusets();
598 cs.is_initalized = false;
599 return RESOURCED_ERROR_NONE;
602 static struct module_ops cpu_sched_modules_ops = {
603 .priority = MODULE_PRIORITY_NORMAL,
605 .init = cpu_sched_init,
606 .exit = cpu_sched_finalize,
609 MODULE_REGISTER(&cpu_sched_modules_ops)