Add unit(in variable) & fix bugs
[platform/core/system/resourced.git] / src / common / cgroup / memory-cgroup.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 /*
20  * @file memcontrol.c
21  *
22  * @desc structure and operation for memory cgroups
23  *
24  * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
25  *
26  */
27
28 #include <assert.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <sys/eventfd.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37
38 #include "resourced.h"
39 #include "trace.h"
40 #include "macro.h"
41 #include "memory-cgroup.h"
42 #include "cgroup.h"
43 #include "module.h"
44 #include "util.h"
45 #include "file-helper.h"
46 #include "config-parser.h"
47 #include "proc-common.h"
48
49 #define BUF_MAX                         1023
50 #define MEMCG_NO_LIMIT                  0
51
52 static int default_swappiness = 0;
53
54 static unsigned long long totalram_bytes = 0;
55
56 /*
57  * Special node that point's to /sys/fs/cgroup/memory - root of memcg group.
58  * This is the same as memcg_tree[CGROUP_ROOT]->info.
59  */
60 static struct memcg_info *memcg_root;
61
62 static struct memcg_conf *memcg_conf = NULL;
63
64 static struct memcg_info gmemcg_info[CGROUP_END] = {
65         {MEMCG_PATH,},
66         {MEMCG_VIP_PATH,},
67         {MEMCG_HIGH_PATH,},
68         {MEMCG_MEDIUM_PATH,},
69         {MEMCG_LOW_PATH,},
70 };
71
72 void register_totalram_bytes(unsigned long long ram_bytes)
73 {
74         totalram_bytes = ram_bytes;
75 }
76
77 int set_mem_action_conf(struct mem_action *mem_action, const char *value)
78 {
79         char *ptr = strchr(value, ',');
80         char *second_value = ptr + 1;
81         char temp;
82
83         if (ptr == NULL) {
84                 _E("Cannot find ',' in the string (%s)", value);
85                 return RESOURCED_ERROR_FAIL;
86         }
87
88         if (value > (ptr - 2)) {
89                 _E("Size of string should be larger than 2");
90                 return RESOURCED_ERROR_FAIL;
91         }
92
93         if (*(ptr - 1) == 'B') {
94                 temp = *(ptr - 2);
95                 *(ptr - 2) = '\0';
96
97                 if (temp == 'G') {
98                         mem_action->memory_bytes = GBYTE_TO_BYTE(atoi(value));
99                 }
100                 else if (temp == 'M') {
101                         mem_action->memory_bytes = MBYTE_TO_BYTE(atoi(value));
102                 }
103                 else if (temp == 'K') {
104                         mem_action->memory_bytes = KBYTE_TO_BYTE(atoi(value));
105                 }
106                 else {
107                         _E("Memory size unit should be GB or MB or KB");
108                         return RESOURCED_ERROR_FAIL;
109                 }
110
111                 if (!strncmp(second_value, ACTION_BROADCAST_VALUE_CONF,
112                                         strlen(ACTION_BROADCAST_VALUE_CONF)+1))
113                         mem_action->action = PROC_ACTION_BROADCAST;
114                 else if (!strncmp(second_value, ACTION_RECLAIM_VALUE_CONF,
115                                         strlen(ACTION_RECLAIM_VALUE_CONF)+1))
116                         mem_action->action = PROC_ACTION_RECLAIM;
117                 else if (!strncmp(second_value, ACTION_KILL_VALUE_CONF,
118                                         strlen(ACTION_KILL_VALUE_CONF)+1))
119                         mem_action->action = PROC_ACTION_KILL;
120                 else if (!strncmp(second_value, ACTION_IGNORE_VALUE_CONF,
121                                         strlen(ACTION_IGNORE_VALUE_CONF)+1))
122                         mem_action->action = PROC_ACTION_IGNORE;
123                 else {
124                         _E("action (%s) is not supported", second_value);
125                         return RESOURCED_ERROR_FAIL;
126                 }
127         }
128         else {
129                 _E("Memory size unit should be XB");
130                 return RESOURCED_ERROR_FAIL;
131         }
132
133         return RESOURCED_ERROR_NONE;
134 }
135
136 int set_memcg_conf_threshold(bool percent, char size, int lvl, const char *value)
137 {
138         if (!percent) {
139                 if (size == 'G') {
140                         memcg_conf->threshold[lvl].threshold =
141                                 GBYTE_TO_MBYTE(atoi(value));
142                 }
143                 else if (size == 'M') {
144                         memcg_conf->threshold[lvl].threshold =
145                                 atoi(value);
146                 }
147                 else if (size == 'K') {
148                         memcg_conf->threshold[lvl].threshold =
149                                 KBYTE_TO_MBYTE(atoi(value));
150                 }
151                 else {
152                         _E("Memory size unit should be GB or MB or KB");
153                         return RESOURCED_ERROR_FAIL;
154                 }
155         }
156         else {
157                 memcg_conf->threshold[lvl].threshold = atoi(value);
158         }
159
160         memcg_conf->threshold[lvl].percent = percent;
161         return RESOURCED_ERROR_NONE;
162 }
163
164 struct memcg_conf *get_memcg_conf(void)
165 {
166         if (!memcg_conf) {
167                 memcg_conf = (struct memcg_conf *)calloc(1, sizeof (struct memcg_conf));
168                 if (!memcg_conf) {
169                         _E("Failed to alloc memory for cpu configuration");
170                         return NULL;
171                 }
172         }
173
174         return memcg_conf;
175 }
176
177 void free_memcg_conf(void)
178 {
179         if (memcg_conf)
180                 free(memcg_conf);
181 }
182
183 static void set_limit_in_bytes(const char *dir, unsigned long long limit_bytes)
184 {
185         int error;
186         unsigned long long prev_bytes;
187
188         error = cgroup_read_node_ulonglong(dir, MEMCG_LIMIT_BYTE, &prev_bytes);
189         if (error) {
190                 _E("[MEMORY-LIMIT] Failed to get %s from %s", MEMCG_LIMIT_BYTE, dir);
191                 return;
192         }
193
194         if (limit_bytes == prev_bytes)
195                 return;
196
197         if (totalram_bytes > 0 && limit_bytes > totalram_bytes)
198                 limit_bytes = totalram_bytes;
199
200         if (prev_bytes > limit_bytes) {
201                 cgroup_write_node_ulonglong(dir, MEMCG_LIMIT_BYTE, limit_bytes);
202                 cgroup_write_node_ulonglong(dir, MEMCG_SWAP_LIMIT_BYTE, limit_bytes);
203         }
204         else {
205                 cgroup_write_node_ulonglong(dir, MEMCG_SWAP_LIMIT_BYTE, limit_bytes);
206                 cgroup_write_node_ulonglong(dir, MEMCG_LIMIT_BYTE, limit_bytes);
207         }
208 }
209
210 int check_oom_and_set_limit(const char *dir, unsigned long long limit_bytes)
211 {
212         int error;
213         static unsigned int poo = -1;
214
215         if (poo == -1) {
216                 error = fread_uint("/proc/sys/vm/panic_on_oom", &poo);
217                 if (error) {
218                         _E("[MEMORY-LIMIT] Failed to get %s from %s", "/proc/sys/vm/panic_on_oom", dir);
219                         poo = 0;
220                 }
221         }
222
223         /* If panic_on_oom is true (> 1), turn of panic_on_oom */
224         if (poo != 0) {
225                 _I("[MEMORY-LIMIT] %s's value is %d. That is, kernel panic will be inevitable on %s, when oom happens", "/proc/sys/vm/panic_on_oom", poo, dir);
226                 error = fwrite_uint("/proc/sys/vm/panic_on_oom", 0);
227                 if (!error)
228                         fread_uint("/proc/sys/vm/panic_on_oom", &poo);
229                 else {
230                         _E("[MEMORY-LIMIT] Failed to update %s", "/proc/sys/vm/panic_on_oom");
231                         return RESOURCED_ERROR_FAIL;
232                 }
233         }
234
235         set_limit_in_bytes(dir, limit_bytes);
236         return RESOURCED_ERROR_NONE;
237 }
238
239 static int memcg_write_limiter_info(struct memcg_info *mi)
240 {
241         unsigned long long limit_bytes = mi->limit_bytes;
242         const char *name = mi->name;
243         int ret = RESOURCED_ERROR_NONE;
244         /* enable cgroup move */
245         ret = cgroup_write_node_uint32(name,
246                         MEMCG_MOVE_CHARGE, 3);
247         if (ret)
248                 return ret;
249
250         if (mi->limit_ratio == MEMCG_NO_LIMIT)
251                 return ret;
252
253         _I("[MEMORY-LIMIT] dir = %s, limit = %llu bytes", name, limit_bytes);
254         /* write limit_in_bytes */
255         ret = check_oom_and_set_limit(name, limit_bytes);
256         return ret;
257 }
258
259 static int memcg_write_optimizer_info(struct memcg_info *mi)
260 {
261         const char *name = mi->name;
262         int ret = RESOURCED_ERROR_NONE;
263         int swappiness = -1;
264
265         /*
266          * write swapness if it has a meaningful value.
267          * if it has own swappiness value, set it to memcg at first.
268          * otherwise, check default_swappiness value and use it.
269          */
270         if (mi->swappiness >= 0)
271                 swappiness = mi->swappiness;
272         else if (default_swappiness >= 0)
273                 swappiness = default_swappiness;
274
275         if (swappiness >= 0) {
276                 ret = cgroup_write_node_uint32(name,
277                         MEMCG_SWAPPINESS, swappiness);
278                 if (ret)
279                         _I("[SWAP] failed to write %s %d to %s the",
280                                 MEMCG_SWAPPINESS, swappiness, name);
281         }
282
283         return ret;
284 }
285
286 int memcg_write_limiter_params(void)
287 {
288         unsigned int i;
289         unsigned long long lower_group_limit_bytes = 0;
290
291         for (i = CGROUP_LOW; i > CGROUP_ROOT; i--) {
292                 struct memcg_info *mi = get_memcg_info(i);
293
294                 if (mi->limit_bytes < lower_group_limit_bytes)
295                         mi->limit_bytes = lower_group_limit_bytes;
296
297                 memcg_write_limiter_info(mi);
298                 lower_group_limit_bytes = mi->limit_bytes;
299         }
300
301         return RESOURCED_ERROR_NONE;
302 }
303
304 int memcg_write_optimizer_params(void)
305 {
306         unsigned int i;
307
308         for (i = CGROUP_VIP; i < CGROUP_END; i++) {
309                 struct memcg_info *mi = get_memcg_info(i);
310                 memcg_write_optimizer_info(mi);
311         }
312
313         return RESOURCED_ERROR_NONE;
314 }
315
316 void memcg_set_threshold(int type, int level, int value)
317 {
318         struct memcg_info *mi = get_memcg_info(type);
319         if(!mi)
320                 _E("memory cgroup of %d is NULL", type);
321         else
322                 mi->threshold_mb[level] = value;
323 }
324
325 void memcg_set_leave_threshold(int type, int value)
326 {
327         struct memcg_info *mi = get_memcg_info(type);
328         if(!mi)
329                 _E("memory cgroup of %d is NULL", type);
330         else
331                 mi->threshold_leave_mb = value;
332 }
333
334 void memcg_info_set_limit(struct memcg_info *mi, float ratio,
335         unsigned long long totalram_bytes)
336 {
337         if (!mi)
338                 return;
339
340         mi->limit_bytes = (double)totalram_bytes * ratio;
341         mi->limit_ratio = ratio;
342 }
343
344 void memcg_set_default_swappiness(int swappiness)
345 {
346         default_swappiness = swappiness;
347 }
348
349 void memcg_info_set_swappiness(struct memcg_info *mi, int swappiness)
350 {
351         if (!mi)
352                 return;
353
354         mi->swappiness = swappiness;
355 }
356
357 static void memcg_memory_stat_init(struct cgroup_memory_stat *mem_stat, long long val)
358 {
359         enum cgroup_memory_stat_id id;
360
361         assert(mem_stat);
362
363         for (id = 0; id < CGROUP_MEMORY_STAT_MAX; id++)
364                 mem_stat->value[id] = val;
365 }
366
367 int memcg_get_memory_stat(const char *name, struct cgroup_memory_stat **mem_stat)
368 {
369         _cleanup_fclose_ FILE *f = NULL;
370         struct cgroup_memory_stat *st;
371         char p[PATH_MAX] = "";
372         char buf[LINE_MAX];
373         const char *memory_stat = "memory.stat";
374
375         if (name) {
376                 snprintf(p, PATH_MAX, "%s/%s", name, memory_stat);
377         } else
378                 snprintf(p, PATH_MAX, "%s/%s", MEMCG_PATH, memory_stat);
379
380         f = fopen(p, "re");
381         if (!f)
382                 return -errno;
383
384         st = (struct cgroup_memory_stat *)malloc(sizeof(struct cgroup_memory_stat));
385         if (!st)
386                 return -ENOMEM;
387
388         memcg_memory_stat_init(st, -1);
389
390         for (;;) {
391                 enum cgroup_memory_stat_id id;
392                 size_t l;
393
394                 if (!fgets(buf, sizeof(buf), f)) {
395                         if (ferror(f)) {
396                                 free(st);
397                                 return -errno;
398                         }
399                         break;
400                 }
401
402                 l = strcspn(buf, " ");
403                 if (!l)
404                         break;
405
406                 buf[l] = 0;
407
408                 id = cgroup_memory_stat_string_to_id(buf);
409                 if (id < 0 || id >= CGROUP_MEMORY_STAT_MAX)
410                         continue;
411
412                 st->value[id] = atoll(buf + l + 1);
413         }
414
415         *mem_stat = st;
416
417         return 0;
418 }
419
420 int memcg_get_anon_usage(char *memcg, unsigned long long *anon_usage_bytes)
421 {
422         int r;
423         _cleanup_free_ struct cgroup_memory_stat *mem_stat = NULL;
424
425         r = memcg_get_memory_stat(memcg, &mem_stat);
426         if (r) {
427                 _D("fail to get memory status : %s", memcg);
428                 return r;
429         }
430
431         *anon_usage_bytes = mem_stat->value[CGROUP_MEMORY_STAT_INACTIVE_ANON] +
432                                 mem_stat->value[CGROUP_MEMORY_STAT_ACTIVE_ANON];
433         return 0;
434 }
435
436 int memcg_get_swap_usage(char *memcg, unsigned long long *usage_bytes)
437 {
438         int r;
439         _cleanup_free_ struct cgroup_memory_stat *mem_stat = NULL;
440
441         r = memcg_get_memory_stat(memcg, &mem_stat);
442         if (r) {
443                 _D("fail to get memory status : %s", memcg);
444                 return r;
445         }
446
447         *usage_bytes = mem_stat->value[CGROUP_MEMORY_STAT_SWAP];
448         return 0;
449 }
450
451 int memcg_init_eventfd(int evfd, const char *memcg, const char *event, const char *value)
452 {
453         _cleanup_close_ int mcgfd = -1;
454         _cleanup_close_ int cgfd = -1;
455         int res = 0, sz;
456         char buf[PATH_MAX] = {0,};
457
458         /* open a node of memory cgroup */
459         snprintf(buf, PATH_MAX, "%s/%s", memcg, MEMCG_EVENTFD_CONTROL);
460         cgfd = open(buf, O_WRONLY);
461         if (cgfd < 0) {
462                 const int saved_errno = errno;
463                 _E("open event_control failed");
464                 errno = saved_errno;
465                 return RESOURCED_ERROR_FAIL;
466         }
467
468         snprintf(buf, PATH_MAX, "%s/%s", memcg, event);
469         mcgfd = open(buf, O_RDONLY);
470         if (mcgfd < 0) {
471                 const int saved_errno = errno;
472                 _E("open memory control failed");
473                 errno = saved_errno;
474                 return RESOURCED_ERROR_FAIL;
475         }
476
477         _D("%s %s %s registerd", memcg, event, value);
478         /* write string like "<event_fd> <opened fd> <value>" to cgroup.event_control */
479         sz = snprintf(buf, PATH_MAX, "%d %d %s", evfd, mcgfd, value);
480         sz += 1;
481         res = write(cgfd, buf, sz);
482         if (res != sz) {
483                 int saved_errno = errno;
484                 _E("write cgfd failed : %d", res);
485                 errno = saved_errno;
486                 return RESOURCED_ERROR_FAIL;
487         }
488
489         return RESOURCED_ERROR_NONE;
490 }
491
492 /*
493  * From memory.txt kernel document,
494  * To register a event for memcg, an application must:
495  *  - create an eventfd using eventfd(2);
496  * - open a node of memory cgroup
497  * - write string like "<event_fd> <opened fd> <value>" to cgroup.event_control
498  *
499  * Current memory cgroup supports eventfd about only
500  * usage_in_byte, oom_control and pressure_level.
501  */
502 int memcg_set_eventfd(const char *memcg, const char *event, const char *value)
503 {
504         int ret;
505         int evfd;
506
507         /* create an eventfd using eventfd(2)*/
508         evfd = eventfd(0, 0);
509         ret = fcntl(evfd, F_SETFL, O_NONBLOCK);
510         if (ret < 0)
511                 return RESOURCED_ERROR_FAIL;
512
513         ret = memcg_init_eventfd(evfd, memcg, event, value);
514         if (ret == RESOURCED_ERROR_NONE)
515                 return evfd;
516         else
517                 return ret;
518 }
519
520 struct memcg_info *get_root_memcg_info(void)
521 {
522         return memcg_root;
523 }
524
525 void memcg_params_init(void)
526 {
527         int idx = 0;
528         GSList *child_cgroups;
529
530         for (idx = CGROUP_ROOT; idx < CGROUP_END; idx++) {
531                 struct memcg_info *mi = &gmemcg_info[idx];
532                 
533                 set_memcg_info(idx, mi);
534                 if(idx == CGROUP_ROOT)
535                         memcg_root = mi;
536                 else {
537                         int parent_idx = get_parent_cgroup(idx);
538                         child_cgroups = get_child_cgroups(parent_idx);
539                         child_cgroups = g_slist_prepend(child_cgroups, get_cgroup_tree(idx));
540                         set_use_hierarchy(parent_idx, true);
541                 }
542
543                 _I("init memory cgroup for %s", mi->name);
544         }
545 }