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