4 * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 * @desc structure and operation for memory cgroups
24 * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
34 #include <sys/eventfd.h>
35 #include <sys/types.h>
38 #include "resourced.h"
41 #include "memory-cgroup.h"
45 #include "file-helper.h"
46 #include "config-parser.h"
47 #include "proc-common.h"
50 #define MEMCG_NO_LIMIT 0
52 static int default_swappiness = 0;
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.
58 static struct memcg_info *memcg_root;
60 static struct memcg_conf *memcg_conf = NULL;
62 static struct memcg_info gmemcg_info[CGROUP_END] = {
70 int set_mem_action_conf(struct mem_action *mem_action, const char *value)
72 char *ptr = strchr(value, ',');
73 char *second_value = ptr + 1;
77 _E("Cannot find ',' in the string (%s)", value);
78 return RESOURCED_ERROR_FAIL;
81 if (value > (ptr - 2)) {
82 _E("Size of string should be larger than 2");
83 return RESOURCED_ERROR_FAIL;
86 if (*(ptr - 1) == 'B') {
91 mem_action->memory = GBYTE_TO_BYTE(atoi(value));
93 else if (temp == 'M') {
94 mem_action->memory = MBYTE_TO_BYTE(atoi(value));
96 else if (temp == 'K') {
97 mem_action->memory = KBYTE_TO_BYTE(atoi(value));
99 else if (temp == ' ') {
100 mem_action->memory = atoi(value);
103 _E("Memory size unit should be GB or MB or KB or B");
104 return RESOURCED_ERROR_FAIL;
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;
117 _E("action (%s) is not supported", second_value);
118 return RESOURCED_ERROR_FAIL;
122 _E("Memory size unit should be XB");
123 return RESOURCED_ERROR_FAIL;
126 return RESOURCED_ERROR_NONE;
129 int set_memcg_conf_threshold(bool percent, char size, int lvl, const char *value)
133 memcg_conf->threshold[lvl].threshold =
134 GBYTE_TO_MBYTE(atoi(value));
136 else if (size == 'M') {
137 memcg_conf->threshold[lvl].threshold =
140 else if (size == 'K') {
141 memcg_conf->threshold[lvl].threshold =
142 KBYTE_TO_MBYTE(atoi(value));
144 else if (size == ' ') {
145 memcg_conf->threshold[lvl].threshold =
146 BYTE_TO_MBYTE(atoi(value));
149 _E("Memory size unit should be GB or MB or KB or B");
150 return RESOURCED_ERROR_FAIL;
154 memcg_conf->threshold[lvl].threshold = atoi(value);
157 memcg_conf->threshold[lvl].percent = percent;
158 return RESOURCED_ERROR_NONE;
161 struct memcg_conf *get_memcg_conf(void)
164 memcg_conf = (struct memcg_conf *)calloc(1, sizeof (struct memcg_conf));
166 _E("Failed to alloc memory for cpu configuration");
174 void free_memcg_conf(void)
180 static void set_limit_in_bytes(const char *dir, unsigned int limit)
185 error = cgroup_read_node_uint32(dir, MEMCG_LIMIT_BYTE, &prev);
187 _E("[DEBUG] Failed to get %s from %s", MEMCG_LIMIT_BYTE, dir);
195 cgroup_write_node_uint32(dir, MEMCG_LIMIT_BYTE, limit);
196 cgroup_write_node_uint32(dir, MEMCG_SWAP_LIMIT_BYTE, limit);
199 cgroup_write_node_uint32(dir, MEMCG_SWAP_LIMIT_BYTE, limit);
200 cgroup_write_node_uint32(dir, MEMCG_LIMIT_BYTE, limit);
204 int check_oom_and_set_limit(const char *dir, unsigned int limit)
207 static unsigned int poo = -1;
210 error = fread_uint("/proc/sys/vm/panic_on_oom", &poo);
212 _E("[DEBUG] Failed to get %s from %s", "/proc/sys/vm/panic_on_oom", dir);
213 return RESOURCED_ERROR_FAIL;
217 /* If panic_on_oom is true (> 1), oom should be disabled */
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);
222 set_limit_in_bytes(dir, limit);
225 set_limit_in_bytes(dir, limit);
228 return RESOURCED_ERROR_NONE;
231 static int memcg_write_limiter_info(struct memcg_info *mi)
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);
242 if (mi->limit_ratio == MEMCG_NO_LIMIT)
245 /* write limit_in_bytes */
246 ret = check_oom_and_set_limit(name, limit);
250 static int memcg_write_optimizer_info(struct memcg_info *mi)
252 const char *name = mi->name;
253 int ret = RESOURCED_ERROR_NONE;
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.
261 if (mi->swappiness >= 0)
262 swappiness = mi->swappiness;
263 else if (default_swappiness >= 0)
264 swappiness = default_swappiness;
266 if (swappiness >= 0) {
267 ret = cgroup_write_node_uint32(name,
268 MEMCG_SWAPPINESS, swappiness);
270 _I("[DEBUG] failed to write %s %d to %s the",
271 MEMCG_SWAPPINESS, swappiness, name);
277 int memcg_write_limiter_params(void)
280 unsigned int lower_group_limit = 0;
282 for (i = CGROUP_LOW; i > CGROUP_ROOT; i--) {
283 struct memcg_info *mi = get_memcg_info(i);
285 if (mi->limit < lower_group_limit)
286 mi->limit = lower_group_limit;
288 memcg_write_limiter_info(mi);
289 lower_group_limit = mi->limit;
292 return RESOURCED_ERROR_NONE;
295 int memcg_write_optimizer_params(void)
299 for (i = CGROUP_VIP; i < CGROUP_END; i++) {
300 struct memcg_info *mi = get_memcg_info(i);
301 memcg_write_optimizer_info(mi);
304 return RESOURCED_ERROR_NONE;
307 void memcg_set_threshold(int type, int level, int value)
309 struct memcg_info *mi = get_memcg_info(type);
311 _E("memory cgroup of %d is NULL", type);
313 mi->threshold[level] = value;
316 void memcg_set_leave_threshold(int type, int value)
318 struct memcg_info *mi = get_memcg_info(type);
320 _E("memory cgroup of %d is NULL", type);
322 mi->threshold_leave = value;
325 void memcg_info_set_limit(struct memcg_info *mi, float ratio,
326 unsigned int totalram)
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;
340 void memcg_set_default_swappiness(int swappiness)
342 default_swappiness = swappiness;
345 void memcg_info_set_swappiness(struct memcg_info *mi, int swappiness)
350 mi->swappiness = swappiness;
353 static void memcg_memory_stat_init(struct cgroup_memory_stat *mem_stat, long long val)
355 enum cgroup_memory_stat_id id;
359 for (id = 0; id < CGROUP_MEMORY_STAT_MAX; id++)
360 mem_stat->value[id] = val;
363 int memcg_get_memory_stat(const char *name, struct cgroup_memory_stat **mem_stat)
365 _cleanup_fclose_ FILE *f = NULL;
366 struct cgroup_memory_stat *st;
367 char p[PATH_MAX] = "";
369 const char *memory_stat = "memory.stat";
372 snprintf(p, PATH_MAX, "%s/%s", name, memory_stat);
374 snprintf(p, PATH_MAX, "%s/%s", MEMCG_PATH, memory_stat);
380 st = (struct cgroup_memory_stat *)malloc(sizeof(struct cgroup_memory_stat));
384 memcg_memory_stat_init(st, -1);
387 enum cgroup_memory_stat_id id;
390 if (!fgets(buf, sizeof(buf), f)) {
398 l = strcspn(buf, " ");
404 id = cgroup_memory_stat_string_to_id(buf);
405 if (id < 0 || id >= CGROUP_MEMORY_STAT_MAX)
408 st->value[id] = atoll(buf + l + 1);
416 int memcg_get_anon_usage(char *memcg, unsigned int *anon_usage)
419 _cleanup_free_ struct cgroup_memory_stat *mem_stat = NULL;
421 r = memcg_get_memory_stat(memcg, &mem_stat);
423 _D("fail to get memory status : %s", memcg);
427 *anon_usage = mem_stat->value[CGROUP_MEMORY_STAT_INACTIVE_ANON] +
428 mem_stat->value[CGROUP_MEMORY_STAT_ACTIVE_ANON];
432 int memcg_get_swap_usage(char *memcg, unsigned int *usage)
435 _cleanup_free_ struct cgroup_memory_stat *mem_stat = NULL;
437 r = memcg_get_memory_stat(memcg, &mem_stat);
439 _D("fail to get memory status : %s", memcg);
443 *usage = mem_stat->value[CGROUP_MEMORY_STAT_SWAP];
447 int memcg_init_eventfd(int evfd, const char *memcg, const char *event, const char *value)
449 _cleanup_close_ int mcgfd = -1;
450 _cleanup_close_ int cgfd = -1;
452 char buf[PATH_MAX] = {0,};
454 /* open a node of memory cgroup */
455 snprintf(buf, PATH_MAX, "%s/%s", memcg, MEMCG_EVENTFD_CONTROL);
456 cgfd = open(buf, O_WRONLY);
458 const int saved_errno = errno;
459 _E("open event_control failed");
461 return RESOURCED_ERROR_FAIL;
464 snprintf(buf, PATH_MAX, "%s/%s", memcg, event);
465 mcgfd = open(buf, O_RDONLY);
467 const int saved_errno = errno;
468 _E("open memory control failed");
470 return RESOURCED_ERROR_FAIL;
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);
477 res = write(cgfd, buf, sz);
479 int saved_errno = errno;
480 _E("write cgfd failed : %d", res);
482 return RESOURCED_ERROR_FAIL;
485 return RESOURCED_ERROR_NONE;
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
495 * Current memory cgroup supports eventfd about only
496 * usage_in_byte, oom_control and pressure_level.
498 int memcg_set_eventfd(const char *memcg, const char *event, const char *value)
503 /* create an eventfd using eventfd(2)*/
504 evfd = eventfd(0, 0);
505 ret = fcntl(evfd, F_SETFL, O_NONBLOCK);
507 return RESOURCED_ERROR_FAIL;
509 ret = memcg_init_eventfd(evfd, memcg, event, value);
510 if (ret == RESOURCED_ERROR_NONE)
516 struct memcg_info *get_root_memcg_info(void)
521 void memcg_params_init(void)
524 GSList *child_cgroups;
526 for (idx = CGROUP_ROOT; idx < CGROUP_END; idx++) {
527 struct memcg_info *mi = &gmemcg_info[idx];
529 set_memcg_info(idx, mi);
530 if(idx == CGROUP_ROOT)
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);
539 _I("init memory cgroup for %s", mi->name);