d71e4cad5bbf3b886d5abdfd213bd4b500232402
[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 /*
55  * Special node that point's to /sys/fs/cgroup/memory - root of memcg group.
56  * This is the same as memcg_tree[CGROUP_ROOT]->info.
57  */
58 static struct memcg_info *memcg_root;
59
60 static struct memcg_conf *memcg_conf = NULL;
61
62 static struct memcg_info gmemcg_info[CGROUP_END] = {
63         {MEMCG_PATH,},
64         {MEMCG_VIP_PATH,},
65         {MEMCG_HIGH_PATH,},
66         {MEMCG_MEDIUM_PATH,},
67         {MEMCG_LOW_PATH,},
68 };
69
70 int set_mem_action_conf(struct mem_action *mem_action, const char *value)
71 {
72         char *ptr = strchr(value, ',');
73         char *second_value = ptr + 1;
74         char temp;
75
76         if (ptr == NULL) {
77                 _E("Cannot find ',' in the string (%s)", value);
78                 return RESOURCED_ERROR_FAIL;
79         }
80
81         if (value > (ptr - 2)) {
82                 _E("Size of string should be larger than 2");
83                 return RESOURCED_ERROR_FAIL;
84         }
85
86         if (*(ptr - 1) == 'B') {
87                 temp = *(ptr - 2);
88                 *(ptr - 2) = '\0';
89
90                 if (temp == 'G') {
91                         mem_action->memory = GBYTE_TO_BYTE(atoi(value));
92                 }
93                 else if (temp == 'M') {
94                         mem_action->memory = MBYTE_TO_BYTE(atoi(value));
95                 }
96                 else if (temp == 'K') {
97                         mem_action->memory = KBYTE_TO_BYTE(atoi(value));
98                 }
99                 else if (temp == ' ') {
100                         mem_action->memory = atoi(value);
101                 }
102                 else {
103                         _E("Memory size unit should be GB or MB or KB or B");
104                         return RESOURCED_ERROR_FAIL;
105                 }
106
107                 if (!strncmp(second_value, ACTION_BROADCAST_VALUE_CONF,
108                                         strlen(ACTION_BROADCAST_VALUE_CONF)+1))
109                         mem_action->action = PROC_ACTION_BROADCAST;
110                 else if (!strncmp(second_value, ACTION_RECLAIM_VALUE_CONF,
111                                         strlen(ACTION_RECLAIM_VALUE_CONF)+1))
112                         mem_action->action = PROC_ACTION_RECLAIM;
113                 else if (!strncmp(second_value, ACTION_KILL_VALUE_CONF,
114                                         strlen(ACTION_KILL_VALUE_CONF)+1))
115                         mem_action->action = PROC_ACTION_KILL;
116                 else {
117                         _E("action (%s) is not supported", second_value);
118                         return RESOURCED_ERROR_FAIL;
119                 }
120         }
121         else {
122                 _E("Memory size unit should be XB");
123                 return RESOURCED_ERROR_FAIL;
124         }
125
126         return RESOURCED_ERROR_NONE;
127 }
128
129 int set_memcg_conf_threshold(bool percent, char size, int lvl, const char *value) 
130 {
131         if (!percent) {
132                 if (size == 'G') {
133                         memcg_conf->threshold[lvl].threshold = 
134                                 GBYTE_TO_MBYTE(atoi(value));
135                 }
136                 else if (size == 'M') {
137                         memcg_conf->threshold[lvl].threshold = 
138                                 atoi(value);
139                 }
140                 else if (size == 'K') {
141                         memcg_conf->threshold[lvl].threshold = 
142                                 KBYTE_TO_MBYTE(atoi(value));
143                 }
144                 else if (size == ' ') {
145                         memcg_conf->threshold[lvl].threshold = 
146                                 BYTE_TO_MBYTE(atoi(value));
147                 }
148                 else {
149                         _E("Memory size unit should be GB or MB or KB or B");
150                         return RESOURCED_ERROR_FAIL;
151                 }
152         }
153         else {
154                 memcg_conf->threshold[lvl].threshold = atoi(value);
155         }
156
157         memcg_conf->threshold[lvl].percent = percent;
158         return RESOURCED_ERROR_NONE;
159 }
160
161 struct memcg_conf *get_memcg_conf(void)
162 {
163         if (!memcg_conf) {
164                 memcg_conf = (struct memcg_conf *)calloc(1, sizeof (struct memcg_conf));
165                 if (!memcg_conf) {
166                         _E("Failed to alloc memory for cpu configuration");
167                         return NULL;
168                 }
169         }
170
171         return memcg_conf;
172 }
173
174 void free_memcg_conf(void)
175 {
176         if (memcg_conf)
177                 free(memcg_conf);
178 }
179
180 static void set_limit_in_bytes(const char *dir, unsigned int limit)
181 {
182         int error;
183         unsigned int prev;
184
185         error = cgroup_read_node_uint32(dir, MEMCG_LIMIT_BYTE, &prev);
186         if (error) {
187                 _E("[DEBUG] Failed to get %s from %s", MEMCG_LIMIT_BYTE, dir);
188                 return;
189         }
190
191         if (limit == prev)
192                 return;
193
194         if (prev > limit) {
195                 cgroup_write_node_uint32(dir, MEMCG_LIMIT_BYTE, limit);
196                 cgroup_write_node_uint32(dir, MEMCG_SWAP_LIMIT_BYTE, limit);
197         }
198         else {
199                 cgroup_write_node_uint32(dir, MEMCG_SWAP_LIMIT_BYTE, limit);
200                 cgroup_write_node_uint32(dir, MEMCG_LIMIT_BYTE, limit);
201         }
202 }
203
204 int check_oom_and_set_limit(const char *dir, unsigned int limit)
205 {
206         int error;
207         static unsigned int poo = -1;
208
209         if (poo == -1) { 
210                 error = fread_uint("/proc/sys/vm/panic_on_oom", &poo);
211                 if (error) {
212                         _E("[DEBUG] Failed to get %s from %s", "/proc/sys/vm/panic_on_oom", dir);
213                         return RESOURCED_ERROR_FAIL;
214                 }
215         }
216
217         /* If panic_on_oom is true (> 1), oom should be disabled  */
218         if (poo != 0) {
219                 _I("[DEBUG] %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);
220                 error = cgroup_write_node_uint32(dir, MEMCG_OOM_CONTROL, 1);
221                 if (!error)
222                         set_limit_in_bytes(dir, limit);
223         }
224         else {
225                 set_limit_in_bytes(dir, limit);
226         }
227
228         return RESOURCED_ERROR_NONE;
229 }
230
231 static int memcg_write_limiter_info(struct memcg_info *mi)
232 {
233         unsigned int limit = mi->limit;
234         const char *name = mi->name;
235         int ret = RESOURCED_ERROR_NONE;
236         /* enable cgroup move */
237         ret = cgroup_write_node_uint32(name,
238                         MEMCG_MOVE_CHARGE, 3);
239         if (ret)
240                 return ret;
241
242         if (mi->limit_ratio == MEMCG_NO_LIMIT)
243                 return ret;
244
245         /* write limit_in_bytes */
246         ret = check_oom_and_set_limit(name, limit);
247         return ret;
248 }
249
250 static int memcg_write_optimizer_info(struct memcg_info *mi)
251 {
252         const char *name = mi->name;
253         int ret = RESOURCED_ERROR_NONE;
254         int swappiness = -1;
255
256         /*
257          * write swapness if it has a meaningful value.
258          * if it has own swappiness value, set it to memcg at first.
259          * otherwise, check default_swappiness value and use it.
260          */
261         if (mi->swappiness >= 0)
262                 swappiness = mi->swappiness;
263         else if (default_swappiness >= 0)
264                 swappiness = default_swappiness;
265
266         if (swappiness >= 0) {
267                 ret = cgroup_write_node_uint32(name,
268                         MEMCG_SWAPPINESS, swappiness);
269                 if (ret)
270                         _I("[DEBUG] failed to write %s %d to %s the",
271                                 MEMCG_SWAPPINESS, swappiness, name);
272         }
273
274         return ret;
275 }
276
277 int memcg_write_limiter_params(void)
278 {
279         unsigned int i;
280         unsigned int lower_group_limit = 0;
281
282         for (i = CGROUP_LOW; i > CGROUP_ROOT; i--) {
283                 struct memcg_info *mi = get_memcg_info(i);
284
285                 if (mi->limit < lower_group_limit)
286                         mi->limit = lower_group_limit;
287
288                 memcg_write_limiter_info(mi);
289                 lower_group_limit = mi->limit;
290         }
291
292         return RESOURCED_ERROR_NONE;
293 }
294
295 int memcg_write_optimizer_params(void)
296 {
297         unsigned int i;
298
299         for (i = CGROUP_VIP; i < CGROUP_END; i++) {
300                 struct memcg_info *mi = get_memcg_info(i);
301                 memcg_write_optimizer_info(mi);
302         }
303
304         return RESOURCED_ERROR_NONE;
305 }
306
307 void memcg_set_threshold(int type, int level, int value)
308 {
309         struct memcg_info *mi = get_memcg_info(type);
310         if(!mi)
311                 _E("memory cgroup of %d is NULL", type);
312         else
313                 mi->threshold[level] = value;
314 }
315
316 void memcg_set_leave_threshold(int type, 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_leave = value;
323 }
324
325 void memcg_info_set_limit(struct memcg_info *mi, float ratio,
326         unsigned int totalram)
327 {
328         if (!mi)
329                 return;
330
331
332         mi->limit = (float)totalram * ratio;
333         mi->limit_ratio = ratio;
334         mi->threshold[MEM_LEVEL_CRITICAL] = (unsigned int)(mi->limit * MEMCG_LOW_RATIO);
335         mi->threshold[MEM_LEVEL_OOM] = (unsigned int)(mi->limit * MEMCG_MEDIUM_RATIO);
336         mi->threshold_leave = (float)mi->limit * MEMCG_FOREGROUND_LEAVE_RATIO;
337         mi->oomleave = mi->limit - mi->threshold_leave;
338 }
339
340 void memcg_set_default_swappiness(int swappiness)
341 {
342         default_swappiness = swappiness;
343 }
344
345 void memcg_info_set_swappiness(struct memcg_info *mi, int swappiness)
346 {
347         if (!mi)
348                 return;
349
350         mi->swappiness = swappiness;
351 }
352
353 static void memcg_memory_stat_init(struct cgroup_memory_stat *mem_stat, long long val)
354 {
355         enum cgroup_memory_stat_id id;
356
357         assert(mem_stat);
358
359         for (id = 0; id < CGROUP_MEMORY_STAT_MAX; id++)
360                 mem_stat->value[id] = val;
361 }
362
363 int memcg_get_memory_stat(const char *name, struct cgroup_memory_stat **mem_stat)
364 {
365         _cleanup_fclose_ FILE *f = NULL;
366         struct cgroup_memory_stat *st;
367         char p[PATH_MAX] = "";
368         char buf[LINE_MAX];
369         const char *memory_stat = "memory.stat";
370
371         if (name) {
372                 snprintf(p, PATH_MAX, "%s/%s", name, memory_stat);
373         } else
374                 snprintf(p, PATH_MAX, "%s/%s", MEMCG_PATH, memory_stat);
375
376         f = fopen(p, "re");
377         if (!f)
378                 return -errno;
379
380         st = (struct cgroup_memory_stat *)malloc(sizeof(struct cgroup_memory_stat));
381         if (!st)
382                 return -ENOMEM;
383
384         memcg_memory_stat_init(st, -1);
385
386         for (;;) {
387                 enum cgroup_memory_stat_id id;
388                 size_t l;
389
390                 if (!fgets(buf, sizeof(buf), f)) {
391                         if (ferror(f)) {
392                                 free(st);
393                                 return -errno;
394                         }
395                         break;
396                 }
397
398                 l = strcspn(buf, " ");
399                 if (!l)
400                         break;
401
402                 buf[l] = 0;
403
404                 id = cgroup_memory_stat_string_to_id(buf);
405                 if (id < 0 || id >= CGROUP_MEMORY_STAT_MAX)
406                         continue;
407
408                 st->value[id] = atoll(buf + l + 1);
409         }
410
411         *mem_stat = st;
412
413         return 0;
414 }
415
416 int memcg_get_anon_usage(char *memcg, unsigned int *anon_usage)
417 {
418         int r;
419         _cleanup_free_ struct cgroup_memory_stat *mem_stat = NULL;
420
421         r = memcg_get_memory_stat(memcg, &mem_stat);
422         if (r) {
423                 _D("fail to get memory status : %s", memcg);
424                 return r;
425         }
426
427         *anon_usage = mem_stat->value[CGROUP_MEMORY_STAT_INACTIVE_ANON] +
428                                 mem_stat->value[CGROUP_MEMORY_STAT_ACTIVE_ANON];
429         return 0;
430 }
431
432 int memcg_get_swap_usage(char *memcg, unsigned int *usage)
433 {
434         int r;
435         _cleanup_free_ struct cgroup_memory_stat *mem_stat = NULL;
436
437         r = memcg_get_memory_stat(memcg, &mem_stat);
438         if (r) {
439                 _D("fail to get memory status : %s", memcg);
440                 return r;
441         }
442
443         *usage = mem_stat->value[CGROUP_MEMORY_STAT_SWAP];
444         return 0;
445 }
446
447 int memcg_init_eventfd(int evfd, const char *memcg, const char *event, const char *value)
448 {
449         _cleanup_close_ int mcgfd = -1;
450         _cleanup_close_ int cgfd = -1;
451         int res = 0, sz;
452         char buf[PATH_MAX] = {0,};
453
454         /* open a node of memory cgroup */
455         snprintf(buf, PATH_MAX, "%s/%s", memcg, MEMCG_EVENTFD_CONTROL);
456         cgfd = open(buf, O_WRONLY);
457         if (cgfd < 0) {
458                 const int saved_errno = errno;
459                 _E("open event_control failed");
460                 errno = saved_errno;
461                 return RESOURCED_ERROR_FAIL;
462         }
463
464         snprintf(buf, PATH_MAX, "%s/%s", memcg, event);
465         mcgfd = open(buf, O_RDONLY);
466         if (mcgfd < 0) {
467                 const int saved_errno = errno;
468                 _E("open memory control failed");
469                 errno = saved_errno;
470                 return RESOURCED_ERROR_FAIL;
471         }
472
473         _D("%s %s %s registerd", memcg, event, value);
474         /* write string like "<event_fd> <opened fd> <value>" to cgroup.event_control */
475         sz = snprintf(buf, PATH_MAX, "%d %d %s", evfd, mcgfd, value);
476         sz += 1;
477         res = write(cgfd, buf, sz);
478         if (res != sz) {
479                 int saved_errno = errno;
480                 _E("write cgfd failed : %d", res);
481                 errno = saved_errno;
482                 return RESOURCED_ERROR_FAIL;
483         }
484
485         return RESOURCED_ERROR_NONE;
486 }
487
488 /*
489  * From memory.txt kernel document,
490  * To register a event for memcg, an application must:
491  *  - create an eventfd using eventfd(2);
492  * - open a node of memory cgroup
493  * - write string like "<event_fd> <opened fd> <value>" to cgroup.event_control
494  *
495  * Current memory cgroup supports eventfd about only
496  * usage_in_byte, oom_control and pressure_level.
497  */
498 int memcg_set_eventfd(const char *memcg, const char *event, const char *value)
499 {
500         int ret;
501         int evfd;
502
503         /* create an eventfd using eventfd(2)*/
504         evfd = eventfd(0, 0);
505         ret = fcntl(evfd, F_SETFL, O_NONBLOCK);
506         if (ret < 0)
507                 return RESOURCED_ERROR_FAIL;
508
509         ret = memcg_init_eventfd(evfd, memcg, event, value);
510         if (ret == RESOURCED_ERROR_NONE)
511                 return evfd;
512         else
513                 return ret;
514 }
515
516 struct memcg_info *get_root_memcg_info(void)
517 {
518         return memcg_root;
519 }
520
521 void memcg_params_init(void)
522 {
523         int idx = 0;
524         GSList *child_cgroups;
525
526         for (idx = CGROUP_ROOT; idx < CGROUP_END; idx++) {
527                 struct memcg_info *mi = &gmemcg_info[idx];
528                 
529                 set_memcg_info(idx, mi);
530                 if(idx == CGROUP_ROOT)
531                         memcg_root = mi;
532                 else {
533                         int parent_idx = get_parent_cgroup(idx);
534                         child_cgroups = get_child_cgroups(parent_idx);
535                         child_cgroups = g_slist_prepend(child_cgroups, get_cgroup_tree(idx));
536                         set_use_hierarchy(parent_idx, true);
537                 }
538
539                 _I("init memory cgroup for %s", mi->name);
540         }
541 }