Add 'CpuSched' & Rearrange 'Swap' sections
[platform/core/system/resourced.git] / src / resource-optimizer / cpu / cpu-sched.c
1 #include <stdio.h>
2 #include "module.h"
3 #include "macro.h"
4 #include "resourced.h"
5 #include "config-parser.h"
6 #include "trace.h"
7 #include "cgroup.h"
8 #include "notifier.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"
14 #include "util.h"
15
16 #define MOUNTS_PATH                     "/proc/mounts"
17 #define CPUSET_CGROUP           "/sys/fs/cgroup/cpuset"
18
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
23 struct core {
24         int id;
25         bool on;
26 };
27
28 struct coreset {
29         GSList *cores;
30         char *name;
31         bool disclaimer_shown;
32 };
33
34 struct cpu_sched {
35         struct coreset *fg;
36         GSList *apps;
37         bool is_initialized;
38 };
39
40 static struct cpu_sched cs;
41
42 struct coreset *cpu_sched_find_coreset(const char *name)
43 {
44         GSList *i;
45         struct coreset *c;
46
47         gslist_for_each_item(i, cs.apps) {
48                 c = (struct coreset *)i->data;
49                 if (!strcmp(name, c->name))
50                         return c;
51         }
52         return NULL;
53 }
54
55 /* check if cpuset subsystem is mounted at the right location */
56 static bool cpu_sched_is_cpuset_mounted()
57 {
58         FILE *f;
59         ssize_t r;
60         size_t len = 0;
61         char *buf = NULL;
62         char mountpoint[128], opts[128];
63         bool ret = false;
64
65         f = fopen(MOUNTS_PATH, "r");
66         if (f == NULL) {
67                 _E("cpu-sched: could not open " MOUNTS_PATH);
68                 return ret;
69         }
70
71         while ((r = getline(&buf, &len, f)) != -1) {
72                 if (sscanf(buf, "%*s %127s %*s %127s %*d %*d", mountpoint, opts) != 2)
73                         continue;
74
75                 if (!strcmp(mountpoint, CPUSET_CGROUP) && strstr(opts, "cpuset") != NULL) {
76                         ret = true;
77                         break;
78                 }
79         }
80
81         free(buf);
82         fclose(f);
83
84         return ret;
85 }
86
87 static int cpu_sched_init_cgroup_set(const struct coreset *set)
88 {
89         int r;
90         char buf[512];
91
92         assert(set);
93         assert(set->name);
94
95         r = cgroup_make_subdir(CPUSET_CGROUP, set->name, NULL);
96         if (r < 0) {
97                 _E("[DEBUG] failed to make cpuset cgroup (%s)", set->name);
98                 return r;
99         }
100
101         r = snprintf(buf, sizeof buf, "%s/%s", CPUSET_CGROUP, set->name);
102         if (r < 0) {
103                 _E("[DEBUG] failed to setup memory nodes for cpuset (%s)", set->name);
104                 return r;
105         }
106
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");
111
112         return 0;
113 }
114
115 /* init cpuset's cgroups */
116 static int cpu_sched_init_cgroup(struct cpu_sched *cs)
117 {
118         int r;
119         GSList *i;
120         struct coreset *c;
121
122         if (cpu_sched_is_cpuset_mounted() == false) {
123                 r = cgroup_make_subdir(CGROUP_PATH, "cpuset", NULL);
124                 if (r < 0) {
125                         _E("failed to make cpuset cgroup");
126                         return r;
127                 }
128
129                 r = cgroup_mount_subsystem("cpuset", CPUSET_CGROUP, "cpuset");
130                 if (r < 0) {
131                         _E("failed to mount cpuset cgroup: %m");
132                         return r;
133                 }
134         }
135
136         gslist_for_each_item(i, cs->apps) {
137                 c = (struct coreset *)i->data;
138                 r = cpu_sched_init_cgroup_set(c);
139                 if (r < 0) {
140                         _E("cpu-set: error setting up cpuset (%s)", c->name);
141                         return r;
142                 }
143         }
144
145         return cs->fg ? cpu_sched_init_cgroup_set(cs->fg) : 0;
146 }
147
148 /* create single core struct representation */
149 static int cpu_sched_new_core(struct coreset *set, int core_id)
150 {
151         struct core *c;
152
153         assert(set);
154         assert(core_id >= 0);
155
156         c = (struct core *)calloc(1, sizeof *c);
157         if (c == NULL) {
158                 _E("cpu-sched: could not allocate memory for core struct");
159                 return RESOURCED_ERROR_FAIL;
160         }
161         c->id = core_id;
162         c->on = false;
163
164         set->cores = g_slist_append(set->cores, c);
165         return RESOURCED_ERROR_NONE;
166 }
167
168 /* free memory allocated in coreset structure */
169 static void cpu_sched_free_cpuset(struct coreset **c)
170 {
171         struct coreset *set = *c;
172
173         if (set->cores != NULL)
174                 g_slist_free_full(set->cores, free);
175         set->cores = NULL;
176
177         free(set->name);
178         set->name = NULL;
179
180         free(set);
181         *c = NULL;
182 }
183
184 /* free memory allocated in coreset structure and the structure */
185 static void cpu_sched_free_cpuset_full(void *data)
186 {
187         struct coreset *set = (struct coreset *)data;
188
189         assert(set);
190
191         cpu_sched_free_cpuset(&set);
192 }
193
194 /* parse config for specific coreset line */
195 static int cpu_sched_parse_cpuset(struct coreset *set, char *value)
196 {
197         char *ptr, *saveptr;
198         int min, max;
199         int r;
200
201         assert(set);
202         assert(value);
203         assert(!set->cores);
204
205         ptr = strtok_r(value, ",", &saveptr);
206         while (ptr) {
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;
212                         } else {
213                                 _E("cpu-sched: error parsing cpuset (%s)", ptr);
214                                 goto parse_cpuset_fail;
215                         }
216                 } else { /* range */
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;
222                                 }
223                         } else {
224                                 _E("cpu-sched: error parsing cpuset (%s)", ptr);
225                                 goto parse_cpuset_fail;
226                         }
227
228                 }
229
230                 ptr = strtok_r(NULL, ",", &saveptr);
231         }
232         return RESOURCED_ERROR_NONE;
233 parse_cpuset_fail:
234         cpu_sched_free_cpuset(&set);
235         return RESOURCED_ERROR_FAIL;
236 }
237
238 static int load_cpu_affinity_config(struct cpu_sched *data)
239 {
240         char *name;
241         bool is_fg;
242
243         assert(data);
244
245         if (!get_cpucg_conf_name())
246                 return RESOURCED_ERROR_NONE;
247
248         name = strdup(get_cpucg_conf_name());
249         is_fg = !strcmp(name, FOREGROUND_APPS);
250
251         struct coreset *c = (struct coreset *)calloc(1, sizeof *c);
252         if (c == NULL) {
253                 free(name);
254                 return RESOURCED_ERROR_OUT_OF_MEMORY;
255         }
256
257         c->name = name;
258
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;
262         }
263
264         if (is_fg)
265                 data->fg = c;
266         else
267                 data->apps = g_slist_append(data->apps, c);
268
269         free_cpucg_conf();
270         return RESOURCED_ERROR_NONE;
271 }
272
273 static int load_cpu_sched_config(void)
274 {
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;
279         }
280
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);
286         }
287
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");
292                 }
293                 else {
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");
299
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");
303                 }
304         }
305
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);
309
310         free_cpu_sched_conf();
311         return RESOURCED_ERROR_NONE;
312 }
313
314 static int cpu_sched_parse_config(struct cpu_sched *data)
315 {
316         load_cpu_affinity_config(data);
317         load_cpu_sched_config();
318         return RESOURCED_ERROR_NONE;
319 }
320
321 static int cpu_sched_write_coreset(struct coreset *set)
322 {
323         GSList *i;
324         struct core *c;
325         char path[128], coreset[128];
326         int r;
327
328         assert(set);
329
330         r = snprintf(path, sizeof path, "%s/%s", CPUSET_CGROUP, set->name);
331         if (r < 0) {
332                 _E("cpu-sched: failed to setup path for cpuset (%s)", set->name);
333                 return r;
334         }
335
336         r = 0;
337         gslist_for_each_item(i, set->cores) {
338                 c = (struct core *)i->data;
339                 if (c == NULL || c->on == false)
340                         continue;
341
342                 r += snprintf(coreset + r, sizeof coreset - r, "%d,", c->id);
343         }
344
345         if (r > 0)
346                 coreset[--r] = '\0';
347
348         return cgroup_write_node_str(path, "cpuset.cpus", coreset);
349 }
350
351 static int cpu_sched_cpu_on_for_coreset(struct coreset *set, int core_id)
352 {
353         GSList *i;
354         struct core *c;
355         bool refresh = false;
356
357         assert(set);
358         assert(core_id >= 0);
359
360         if (set->cores == NULL)
361                 return 0;
362
363         gslist_for_each_item(i, set->cores) {
364                 c = (struct core *)i->data;
365                 if (c == NULL || c->id != core_id)
366                         continue;
367
368                 _D("cpu-sched: core %d on for coreset %s", core_id, set->name);
369                 c->on = true;
370                 refresh = true;
371                 break;
372         }
373
374         if (refresh == false)
375                 return 0;
376
377         return cpu_sched_write_coreset(set);
378 }
379
380 static int cpu_sched_cpu_off_for_coreset(struct coreset *set, int core_id)
381 {
382         GSList *i;
383         struct core *c;
384         bool refresh = false;
385
386         assert(set);
387         assert(core_id >= 0);
388
389         if (set->cores == NULL)
390                 return 0;
391
392         gslist_for_each_item(i, set->cores) {
393                 c = (struct core *)i->data;
394                 if (c == NULL || c->id != core_id)
395                         continue;
396
397                 _D("cpu-sched: core %d off for coreset %s", core_id, set->name);
398                 c->on = false;
399                 refresh = true;
400                 break;
401         }
402
403         if (refresh == false)
404                 return 0;
405
406         return cpu_sched_write_coreset(set);
407 }
408
409 static int cpu_sched_cpu_on(void *data)
410 {
411         int id;
412         GSList *i;
413
414         assert(data);
415
416         id = *(int*)(data);
417
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);
421         }
422         if (cs.fg)
423                 cpu_sched_cpu_on_for_coreset(cs.fg, id);
424
425         return RESOURCED_ERROR_NONE;
426 }
427
428 static int cpu_sched_cpu_off(void *data)
429 {
430         int id;
431         GSList *i;
432
433         assert(data);
434
435         id = *(int*)(data);
436
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);
440         }
441
442         if (cs.fg)
443                 cpu_sched_cpu_off_for_coreset(cs.fg, id);
444
445         return RESOURCED_ERROR_NONE;
446 }
447
448 static int cpu_sched_add_pid_to_cpuset(struct coreset *set, pid_t pid)
449 {
450         assert(set);
451
452         _D("cpu-sched: add pid %d to cpuset %s", pid, set->name);
453
454         cgroup_write_pid(CPUSET_CGROUP, set->name, pid);
455
456         return RESOURCED_ERROR_NONE;
457 }
458
459 static int cpu_sched_remove_pid_from_cpuset(struct coreset *set, pid_t pid)
460 {
461         assert(set);
462
463         _D("cpu-sched: moving pid %d to toplevel cpuset (from %s cpuset)", pid, set->name);
464
465         cgroup_write_pid_fullpath(CPUSET_CGROUP, pid);
466
467         return RESOURCED_ERROR_NONE;
468 }
469
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)
472 {
473         assert (appid);
474
475         struct coreset *c = cpu_sched_find_coreset(appid);
476         if (!c)
477                 return false;
478
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;
482         }
483
484         return true;
485 }
486
487 static int cpu_sched_app_foreground(void *data)
488 {
489         struct proc_status *ps = (struct proc_status *)data;
490
491         assert(cs.fg);
492         assert(ps);
493         assert(ps->pai);
494
495         if (cs.is_initialized == false || cpu_sched_is_static_app(ps->pai->appid) == true)
496                 return RESOURCED_ERROR_NONE;
497
498         _D("cpu-sched: app %s moved to foreground; pid=%d", ps->pai->appid, ps->pid);
499
500         return cpu_sched_add_pid_to_cpuset(cs.fg, ps->pid);
501 }
502
503 static int cpu_sched_app_background(void *data)
504 {
505         struct proc_status *ps = (struct proc_status *)data;
506
507         assert(cs.fg);
508         assert(ps);
509         assert(ps->pai);
510
511         if (cs.is_initialized == false || cpu_sched_is_static_app(ps->pai->appid) == true)
512                 return RESOURCED_ERROR_NONE;
513
514         _D("cpu-sched: app %s moved to background; pid=%d", ps->pai->appid, ps->pid);
515
516         return cpu_sched_remove_pid_from_cpuset(cs.fg, ps->pid);
517 }
518
519 static int cpu_sched_app_launch(void *data)
520 {
521         struct proc_status *ps = (struct proc_status *)data;
522         struct coreset *c;
523
524         assert(ps);
525         assert(ps->pai);
526
527         _D("cpu-sched: app launch: %s", ps->pai->appid);
528
529         c = cpu_sched_find_coreset(ps->pai->appid);
530         if (c == NULL)
531                 return 0;
532
533         return cpu_sched_add_pid_to_cpuset(c, ps->pid);
534 }
535
536 static void register_notifiers()
537 {
538         if (cs.fg) {
539                 register_notifier(RESOURCED_NOTIFIER_APP_RESUME, cpu_sched_app_foreground);
540                 register_notifier(RESOURCED_NOTIFIER_APP_SUSPEND, cpu_sched_app_background);
541
542                 register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_sched_app_foreground);
543                 register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_sched_app_background);
544
545                 register_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, cpu_sched_app_foreground);
546                 register_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, cpu_sched_app_background);
547         }
548
549         register_notifier(RESOURCED_NOTIFIER_CPU_ON, cpu_sched_cpu_on);
550         register_notifier(RESOURCED_NOTIFIER_CPU_OFF, cpu_sched_cpu_off);
551
552         register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, cpu_sched_app_launch);
553         register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, cpu_sched_app_launch);
554 }
555
556 static void unregister_notifiers()
557 {
558         if (cs.fg) {
559                 unregister_notifier(RESOURCED_NOTIFIER_APP_RESUME, cpu_sched_app_foreground);
560                 unregister_notifier(RESOURCED_NOTIFIER_APP_SUSPEND, cpu_sched_app_background);
561
562                 unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_sched_app_foreground);
563                 unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_sched_app_background);
564
565                 unregister_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, cpu_sched_app_foreground);
566                 unregister_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, cpu_sched_app_background);
567         }
568
569         unregister_notifier(RESOURCED_NOTIFIER_CPU_ON, cpu_sched_cpu_on);
570         unregister_notifier(RESOURCED_NOTIFIER_CPU_OFF, cpu_sched_cpu_off);
571
572         unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, cpu_sched_app_launch);
573         unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, cpu_sched_app_launch);
574 }
575
576 static void cpu_sched_free_cpusets()
577 {
578         g_slist_free_full(g_steal_pointer(&cs.apps), cpu_sched_free_cpuset_full);
579         if (cs.fg)
580                 cpu_sched_free_cpuset(&cs.fg);
581 }
582
583 static void cpu_sched_check_apps()
584 {
585         _cleanup_app_list_close_ GSList *proc_app_list = PAL_INIT_VALUE;
586         GSList *giter;
587         struct proc_app_info *pai;
588         struct coreset *c;
589
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)
594                         continue;
595
596                 c = cpu_sched_find_coreset(pai->appid);
597                 if (c != NULL) {
598                         cpu_sched_add_pid_to_cpuset(c, pai->main_pid);
599                         continue;
600                 }
601
602                 if (cs.fg && pai->state == PROC_STATE_FOREGROUND)
603                         cpu_sched_add_pid_to_cpuset(cs.fg, pai->main_pid);
604         }
605 }
606
607 static int cpu_sched_init(void *data)
608 {
609         int r;
610
611         if (cpu_sched_parse_config(&cs) != RESOURCED_ERROR_NONE) {
612                 _E("cpu-sched: error parsing config");
613                 return RESOURCED_ERROR_FAIL;
614         }
615
616         r = cpu_sched_init_cgroup(&cs);
617         if (r < 0)
618                 goto init_failed;
619
620         register_notifiers();
621         if (cpu_hotplug_init() != 0) {
622                 _E("cpu_sched: could not setup cpu hotplugging");
623                 return RESOURCED_ERROR_FAIL;
624         }
625
626         cs.is_initialized = true;
627         cpu_sched_check_apps();
628         return RESOURCED_ERROR_NONE;
629
630 init_failed:
631         unregister_notifiers();
632         cpu_hotplug_finalize();
633         cpu_sched_free_cpusets();
634         cs.is_initialized = false;
635         return RESOURCED_ERROR_FAIL;
636 }
637
638 static int cpu_sched_finalize(void *data)
639 {
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;
646 }
647
648 static struct module_ops cpu_sched_modules_ops = {
649         .priority = MODULE_PRIORITY_NORMAL,
650         .name = "cpu-sched",
651         .init = cpu_sched_init,
652         .exit = cpu_sched_finalize,
653 };
654
655 MODULE_REGISTER(&cpu_sched_modules_ops)