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-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"
22 #define ONLINE_CPU_PATH "/sys/devices/system/cpu/online"
25 struct cpuset_info *fg;
30 static struct cpu_sched cs;
32 static int cpu_sched_cpu_on(void *data);
34 static struct cpuset_info *cpu_sched_find_coreset(const char *name)
36 struct proc_conf_info *pci = fixed_app_and_service_exist_check(name, APP_TYPE);
40 if (pci->cpuset_info.name == NULL)
43 if (pci->cpuset_info.cpu_info == NULL)
46 return &pci->cpuset_info;
49 /* check if cpuset subsystem is mounted at the right location */
50 static bool cpu_sched_is_cpuset_mounted()
56 char mountpoint[128], opts[128];
59 f = fopen(MOUNTS_PATH, "r");
61 _E("cpu-sched: could not open " MOUNTS_PATH);
65 while ((r = getline(&buf, &len, f)) != -1) {
66 if (sscanf(buf, "%*s %127s %*s %127s %*d %*d", mountpoint, opts) != 2)
69 if (!strcmp(mountpoint, CPUSET_CGROUP) && strstr(opts, "cpuset") != NULL) {
81 static int cpu_sched_init_cgroup_set(const struct cpuset_info *set)
91 r = cgroup_make_subdir(CPUSET_CGROUP, set->name, NULL);
93 _E("[CPU-SCHED] failed to make cpuset cgroup (%s)", set->name);
97 r = snprintf(buf, sizeof buf, "%s/%s", CPUSET_CGROUP, set->name);
99 _E("[CPU-SCHED] 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 "[CPU-SCHED] Failed to reset memory nodes for cpuset: %m");
111 static int cpu_sched_init_cpu_online_info(void)
115 struct cpuset_info cpuset_info = {0, };
116 _cleanup_free_ char *buf = NULL;
118 buf = calloc(1, PATH_MAX);
120 return RESOURCED_ERROR_OUT_OF_MEMORY;
122 /* Read the current online cpus */
123 ret = fread_str(ONLINE_CPU_PATH, &buf);
125 _E("[CPU-SCHED] failed to read online cpu");
129 if (cpu_sched_parse_cpuset(&cpuset_info, buf) < 0) {
130 _E("invalid cpu affinity value (%s)", buf);
132 if (cpuset_info.cpu_info != NULL)
133 g_slist_free_full(cpuset_info.cpu_info, free);
135 return RESOURCED_ERROR_FAIL;
138 /* Pin cpu affinity */
139 gslist_for_each_item(item, cpuset_info.cpu_info) {
140 struct cpu_info *cpu_info = (struct cpu_info *)item->data;
141 if (cpu_info == NULL)
144 cpu_sched_cpu_on(&cpu_info->cpu_id);
147 return RESOURCED_ERROR_NONE;
150 /* init cpuset's cgroups */
151 static int cpu_sched_init_cgroup(struct cpu_sched *cs)
155 struct cpuset_info *cpuset_info;
157 if (cpu_sched_is_cpuset_mounted() == false) {
158 r = cgroup_make_subdir(CGROUP_PATH, "cpuset", NULL);
160 _E("failed to make cpuset cgroup");
164 r = cgroup_mount_subsystem("cpuset", CPUSET_CGROUP, "cpuset");
166 _E("failed to mount cpuset cgroup: %m");
171 gslist_for_each_item(i, cs->apps) {
172 cpuset_info = (struct cpuset_info *)i->data;
173 r = cpu_sched_init_cgroup_set(cpuset_info);
175 _E("cpu-set: error setting up cpuset (%s)", cpuset_info->name);
180 r = cpu_sched_init_cgroup_set(cs->fg);
182 _E("cpu-set: error setting up cpuset (%s)", cs->fg->name);
186 return cpu_sched_init_cpu_online_info();
189 /* free memory allocated in cpuset_info structure */
190 static void cpu_sched_free_cpuset(struct cpuset_info **cpuset_info)
192 struct cpuset_info *set = *cpuset_info;
194 if (set->cpu_info != NULL)
195 g_slist_free_full(set->cpu_info, free);
196 set->cpu_info = NULL;
205 /* free memory allocated in cpuset_info structure and the structure */
206 static void cpu_sched_free_cpuset_full(void *data)
208 struct cpuset_info *set = (struct cpuset_info *)data;
212 cpu_sched_free_cpuset(&set);
216 static int load_cpu_affinity_config(struct cpu_sched *data)
220 gpointer proc_conf_ptr;
221 GHashTableIter app_list_iter;
225 if (get_cpu_affinity_conf_name() == NULL)
226 goto cpu_affinity_set_for_fixed_app;
228 if (strcmp(get_cpu_affinity_conf_name(), FOREGROUND_APPS_NAME_CONF)) {
229 _E("[CPU-SCHED] Wrong name (%s), is should be 'ForegroundApps'",
230 get_cpu_affinity_conf_name());
231 goto cpu_affinity_set_for_fixed_app;
234 name = strdup(get_cpu_affinity_conf_name());
236 _E("[CPU-SCHED] Failed to allocate memory during making a duplicate name (%s)",
237 get_cpu_affinity_conf_name());
238 goto cpu_affinity_set_for_fixed_app;
241 struct cpuset_info *cpuset_info = (struct cpuset_info *)calloc(1, sizeof *cpuset_info);
242 if (cpuset_info == NULL) {
244 _E("[CPU_SCHED] Failed to allocate memory");
245 goto cpu_affinity_set_for_fixed_app;
248 cpuset_info->name = name;
250 if (!get_cpu_affinity_conf_value() ||
251 cpu_sched_parse_cpuset(cpuset_info, get_cpu_affinity_conf_value()) < 0) {
252 cpu_sched_free_cpuset(&cpuset_info);
253 goto cpu_affinity_set_for_fixed_app;
256 data->fg = cpuset_info;
258 cpu_affinity_set_for_fixed_app:
259 /* Free memory if previously allocated memory for 'ForegroundApps' exists */
260 free_cpu_affinity_conf();
262 /* Register static fixed cpu affinity apps */
263 g_hash_table_iter_init(&app_list_iter, fixed_app_list_get());
264 while (g_hash_table_iter_next(&app_list_iter, &app_name, &proc_conf_ptr)) {
265 struct proc_conf_info *pci = (struct proc_conf_info *)proc_conf_ptr;
268 _W("[CPU-SCHED] Process configuration information is NULL");
272 if (pci->cpuset_info.cpu_info) {
273 pci->cpuset_info.name = pci->name;
274 data->apps = g_slist_append(data->apps, &pci->cpuset_info);
278 return RESOURCED_ERROR_NONE;
281 static int load_cpu_sched_config(void)
283 struct cpu_sched_conf *cpu_sched_conf = get_cpu_sched_conf();
284 if (cpu_sched_conf == NULL) {
285 _E("[CPU-SCHED] cpu sched configuration structure should not be NULL");
286 return RESOURCED_ERROR_FAIL;
289 if (cpu_sched_conf->cpu_cgroup_info.rt_period_us > 0 &&
290 cpu_sched_conf->cpu_cgroup_info.rt_runtime_us > 0 &&
291 cpu_sched_conf->cpu_cgroup_info.rt_period_us >
292 cpu_sched_conf->cpu_cgroup_info.rt_runtime_us) {
293 fwrite_ulonglong(CPUCG_PATH "/" CPUCG_RT_CONTROL_FULL_BANDWIDTH,
294 cpu_sched_conf->cpu_cgroup_info.rt_period_us);
295 fwrite_ulonglong(CPUCG_PATH "/" CPUCG_RT_CONTROL_BANDWIDTH,
296 cpu_sched_conf->cpu_cgroup_info.rt_runtime_us);
297 fwrite_ulonglong(GLOBAL_RT_PERIOD_US_PATH, cpu_sched_conf->cpu_cgroup_info.rt_period_us);
298 fwrite_longlong(GLOBAL_RT_RUNTIME_US_PATH, cpu_sched_conf->cpu_cgroup_info.rt_runtime_us);
301 if (cpu_sched_conf->cpu_sched_flag) {
302 if (CHECK_BIT(cpu_sched_conf->cpu_sched_flag, CPU_SCHED_RUNTIME_SHARE) &&
303 CHECK_BIT(cpu_sched_conf->cpu_sched_flag, CPU_SCHED_NO_RUNTIME_SHARE)) {
304 _E("[CPU-SCHED] RT_RUNTIME_SHARE and NO_RT_RUNTIME_SHARE cannot be coexisted");
307 /* RT_RUNTIME_SHARE */
308 if (CHECK_BIT(cpu_sched_conf->cpu_sched_flag, CPU_SCHED_RUNTIME_SHARE))
309 fwrite_str(CPU_SCHED_FEATURE_PATH, "RT_RUNTIME_SHARE");
310 else if (CHECK_BIT(cpu_sched_conf->cpu_sched_flag, CPU_SCHED_NO_RUNTIME_SHARE))
311 fwrite_str(CPU_SCHED_FEATURE_PATH, "NO_RT_RUNTIME_SHARE");
313 /* RT_RUNTIME_GREED */
314 /* if (CHECK_BIT(cpu_sched_conf->cpu_sched_flag, CPU_SCHED_RUNTIME_GREED))
315 fwrite_str(CPU_SCHED_FEATURE_PATH, "RT_RUNTIME_GREED");*/
319 _I("[CPU-SCHED] rt_period_us = %llu", cpu_sched_conf->cpu_cgroup_info.rt_period_us);
320 _I("[CPU-SCHED] rt_runtime_us = %lld", cpu_sched_conf->cpu_cgroup_info.rt_runtime_us);
321 _I("[CPU-SCHED] cpu_sched_feature = %d", cpu_sched_conf->cpu_sched_flag);
323 free_cpu_sched_conf();
324 return RESOURCED_ERROR_NONE;
327 static int cpu_sched_parse_config(struct cpu_sched *data)
329 load_cpu_affinity_config(data);
330 load_cpu_sched_config();
331 return RESOURCED_ERROR_NONE;
334 static int cpu_sched_write_coreset(struct cpuset_info *set)
337 struct cpu_info *cpu_info;
338 char path[128], coreset[128];
343 r = snprintf(path, sizeof path, "%s/%s", CPUSET_CGROUP, set->name);
345 _E("cpu-sched: failed to setup path for cpuset (%s)", set->name);
350 gslist_for_each_item(i, set->cpu_info) {
351 cpu_info = (struct cpu_info *)i->data;
352 if (cpu_info == NULL || cpu_info->online == false)
355 r += snprintf(coreset + r, sizeof coreset - r, "%d,", cpu_info->cpu_id);
361 return cgroup_write_node_str(path, "cpuset.cpus", coreset);
364 static int cpu_sched_cpu_on_for_coreset(struct cpuset_info *set, int core_id)
367 struct cpu_info *cpu_info;
368 bool refresh = false;
371 assert(core_id >= 0);
373 if (set->cpu_info == NULL)
376 gslist_for_each_item(i, set->cpu_info) {
377 cpu_info = (struct cpu_info *)i->data;
378 if (cpu_info == NULL || cpu_info->cpu_id != core_id)
381 _D("cpu-sched: core %d on for cpuset_info %s", core_id, set->name);
382 cpu_info->online = true;
387 if (refresh == false)
390 return cpu_sched_write_coreset(set);
393 static int cpu_sched_cpu_off_for_coreset(struct cpuset_info *set, int core_id)
396 struct cpu_info *cpu_info;
397 bool refresh = false;
400 assert(core_id >= 0);
402 if (set->cpu_info == NULL)
405 gslist_for_each_item(i, set->cpu_info) {
406 cpu_info = (struct cpu_info *)i->data;
407 if (cpu_info == NULL || cpu_info->cpu_id != core_id)
410 _D("cpu-sched: core %d off for cpuset_info %s", core_id, set->name);
411 cpu_info->online = false;
416 if (refresh == false)
419 return cpu_sched_write_coreset(set);
422 static int cpu_sched_cpu_on(void *data)
431 _D("cpu-sched: core %d plugged in", id);
432 gslist_for_each_item(i, cs.apps) {
433 cpu_sched_cpu_on_for_coreset((struct cpuset_info *)i->data, id);
436 cpu_sched_cpu_on_for_coreset(cs.fg, id);
438 return RESOURCED_ERROR_NONE;
441 static int cpu_sched_cpu_off(void *data)
450 _D("cpu-sched: core %d plugged out", id);
451 gslist_for_each_item(i, cs.apps) {
452 cpu_sched_cpu_off_for_coreset((struct cpuset_info *)i->data, id);
456 cpu_sched_cpu_off_for_coreset(cs.fg, id);
458 return RESOURCED_ERROR_NONE;
461 static int cpu_sched_add_pid_to_cpuset(struct cpuset_info *set, pid_t pid)
465 _D("cpu-sched: add pid %d to cpuset %s", pid, set->name);
467 cgroup_write_pid(CPUSET_CGROUP, set->name, pid);
469 return RESOURCED_ERROR_NONE;
472 static int cpu_sched_remove_pid_from_cpuset(struct cpuset_info *set, pid_t pid)
476 _D("cpu-sched: moving pid %d to toplevel cpuset (from %s cpuset)", pid, set->name);
478 cgroup_write_pid_fullpath(CPUSET_CGROUP, pid);
480 return RESOURCED_ERROR_NONE;
483 /* if app is subject to static cpuset_info config its cpuset_info should not be changed */
484 static bool cpu_sched_is_static_app(const char *appid)
488 struct cpuset_info *cpuset_info = cpu_sched_find_coreset(appid);
492 if (cpuset_info->disclaimer_shown == false) {
493 _D("cpu-sched: appid %s is statically configured - not subject to cpuset change", appid);
494 cpuset_info->disclaimer_shown = true;
500 static int cpu_sched_app_foreground(void *data)
502 struct proc_status *ps = (struct proc_status *)data;
508 if (cs.is_initialized == false || cpu_sched_is_static_app(ps->pai->appid) == true)
509 return RESOURCED_ERROR_NONE;
511 _D("cpu-sched: app %s moved to foreground; pid=%d", ps->pai->appid, ps->pid);
513 return cpu_sched_add_pid_to_cpuset(cs.fg, ps->pid);
516 static int cpu_sched_app_background(void *data)
518 struct proc_status *ps = (struct proc_status *)data;
524 if (cs.is_initialized == false || cpu_sched_is_static_app(ps->pai->appid) == true)
525 return RESOURCED_ERROR_NONE;
527 _D("cpu-sched: app %s moved to background; pid=%d", ps->pai->appid, ps->pid);
529 return cpu_sched_remove_pid_from_cpuset(cs.fg, ps->pid);
532 static int cpu_sched_app_launch(void *data)
534 struct proc_status *ps = (struct proc_status *)data;
535 struct cpuset_info *cpuset_info;
540 _D("cpu-sched: app launch: %s", ps->pai->appid);
542 cpuset_info = cpu_sched_find_coreset(ps->pai->appid);
543 if (cpuset_info == NULL)
546 return cpu_sched_add_pid_to_cpuset(cpuset_info, ps->pid);
549 /*static int cpu_sched_rt_scheduler(void *data)
551 _cleanup_free_ char *path = NULL;
554 struct proc_status *ps = (struct proc_status *)data;
558 assert(ps->pci->name);
560 result = cgroup_read_node_int32(CPUCG_PATH, CPUCG_RT_CONTROL_BANDWIDTH, &runtime);
562 _W("[CPU-SCHED] rt_runtime_us is not supported");
563 return RESOURCED_ERROR_FAIL;
566 result = asprintf(&path, "%s/%s", CPUCG_PATH, ps->pci->name);
568 _E("[CPU-SCHED] not enough memory");
569 return RESOURCED_ERROR_OUT_OF_MEMORY;
572 result = cgroup_make_subdir(CPUCG_PATH, ps->pci->name, NULL);
574 _E("[CPU-SCHED] Failed to create cgroup subdir '%s/%s'",
575 CPUCG_PATH, ps->pci->name);
576 return RESOURCED_ERROR_FAIL;
579 cgroup_write_node_int32(path, CPUCG_RT_CONTROL_FULL_BANDWIDTH, ps->pci->cpu_sched_info.rt_period_us);
580 cgroup_write_node_int32(path, CPUCG_RT_CONTROL_BANDWIDTH, ps->pci->cpu_sched_info.rt_runtime_us);
582 cgroup_write_pid_fullpath(path, ps->pid);
584 return RESOURCED_ERROR_NONE;
587 static void register_notifiers()
590 register_notifier(RESOURCED_NOTIFIER_APP_RESUME, cpu_sched_app_foreground);
591 register_notifier(RESOURCED_NOTIFIER_APP_SUSPEND, cpu_sched_app_background);
593 register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_sched_app_foreground);
594 register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_sched_app_background);
596 register_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, cpu_sched_app_foreground);
597 register_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, cpu_sched_app_background);
599 register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD_WITH_FIXED_OOM_SCORE, cpu_sched_app_background);
602 // register_notifier(RESOURCED_NOTIFIER_RT_SCHEDULER, cpu_sched_rt_scheduler);
604 register_notifier(RESOURCED_NOTIFIER_CPU_ON, cpu_sched_cpu_on);
605 register_notifier(RESOURCED_NOTIFIER_CPU_OFF, cpu_sched_cpu_off);
607 register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, cpu_sched_app_launch);
608 register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, cpu_sched_app_launch);
611 static void unregister_notifiers()
614 unregister_notifier(RESOURCED_NOTIFIER_APP_RESUME, cpu_sched_app_foreground);
615 unregister_notifier(RESOURCED_NOTIFIER_APP_SUSPEND, cpu_sched_app_background);
617 unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_sched_app_foreground);
618 unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_sched_app_background);
620 unregister_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, cpu_sched_app_foreground);
621 unregister_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, cpu_sched_app_background);
623 unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD_WITH_FIXED_OOM_SCORE, cpu_sched_app_background);
626 // unregister_notifier(RESOURCED_NOTIFIER_RT_SCHEDULER, cpu_sched_rt_scheduler);
628 unregister_notifier(RESOURCED_NOTIFIER_CPU_ON, cpu_sched_cpu_on);
629 unregister_notifier(RESOURCED_NOTIFIER_CPU_OFF, cpu_sched_cpu_off);
631 unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, cpu_sched_app_launch);
632 unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, cpu_sched_app_launch);
635 static void cpu_sched_free_cpusets()
637 g_slist_free_full(g_steal_pointer(&cs.apps), cpu_sched_free_cpuset_full);
639 cpu_sched_free_cpuset(&cs.fg);
642 static void cpu_sched_check_apps()
644 _cleanup_app_list_close_ GSList *proc_app_list = PAL_INIT_VALUE;
646 struct proc_app_info *pai;
647 struct cpuset_info *cpuset_info;
649 proc_app_list = proc_app_list_open();
650 gslist_for_each_item(giter, proc_app_list) {
651 pai = (struct proc_app_info *)giter->data;
652 if (!pai || !pai->main_pid)
655 cpuset_info = cpu_sched_find_coreset(pai->appid);
656 if (cpuset_info != NULL) {
657 cpu_sched_add_pid_to_cpuset(cpuset_info, pai->main_pid);
661 if (cs.fg && pai->state == PROC_STATE_FOREGROUND)
662 cpu_sched_add_pid_to_cpuset(cs.fg, pai->main_pid);
666 static int cpu_sched_init(void *data)
670 if (cpu_sched_parse_config(&cs) != RESOURCED_ERROR_NONE) {
671 _E("cpu-sched: error parsing config");
672 return RESOURCED_ERROR_FAIL;
675 r = cpu_sched_init_cgroup(&cs);
679 register_notifiers();
680 if (cpu_hotplug_init() != 0) {
681 _E("cpu_sched: could not setup cpu hotplugging");
682 return RESOURCED_ERROR_FAIL;
685 cs.is_initialized = true;
686 cpu_sched_check_apps();
687 return RESOURCED_ERROR_NONE;
690 unregister_notifiers();
691 cpu_hotplug_finalize();
692 cpu_sched_free_cpusets();
693 cs.is_initialized = false;
694 return RESOURCED_ERROR_FAIL;
697 static int cpu_sched_finalize(void *data)
699 _D("cpu-sched: deinit module");
700 unregister_notifiers();
701 cpu_hotplug_finalize();
702 cpu_sched_free_cpusets();
703 cs.is_initialized = false;
704 return RESOURCED_ERROR_NONE;
707 static struct module_ops cpu_sched_modules_ops = {
708 .priority = MODULE_PRIORITY_NORMAL,
710 .init = cpu_sched_init,
711 .exit = cpu_sched_finalize,
714 MODULE_REGISTER(&cpu_sched_modules_ops)