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;
54 static unsigned long long totalram_bytes = 0;
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.
60 static struct memcg_info *memcg_root;
62 static struct memcg_conf *memcg_conf = NULL;
64 static struct memcg_info gmemcg_info[CGROUP_END] = {
72 void register_totalram_bytes(unsigned long long ram_bytes)
74 totalram_bytes = ram_bytes;
77 int set_mem_action_conf(struct mem_action *mem_action, const char *value)
79 char *ptr = strchr(value, ',');
80 char *second_value = ptr + 1;
84 _E("Cannot find ',' in the string (%s)", value);
85 return RESOURCED_ERROR_FAIL;
88 if (value > (ptr - 2)) {
89 _E("Size of string should be larger than 2");
90 return RESOURCED_ERROR_FAIL;
93 if (*(ptr - 1) == 'B') {
98 mem_action->memory_bytes = GBYTE_TO_BYTE(atoi(value));
100 else if (temp == 'M') {
101 mem_action->memory_bytes = MBYTE_TO_BYTE(atoi(value));
103 else if (temp == 'K') {
104 mem_action->memory_bytes = KBYTE_TO_BYTE(atoi(value));
107 _E("Memory size unit should be GB or MB or KB");
108 return RESOURCED_ERROR_FAIL;
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;
124 _E("action (%s) is not supported", second_value);
125 return RESOURCED_ERROR_FAIL;
129 _E("Memory size unit should be XB");
130 return RESOURCED_ERROR_FAIL;
133 return RESOURCED_ERROR_NONE;
136 int set_memcg_conf_threshold(bool percent, char size, int lvl, const char *value)
140 memcg_conf->threshold[lvl].threshold =
141 GBYTE_TO_MBYTE(atoi(value));
143 else if (size == 'M') {
144 memcg_conf->threshold[lvl].threshold =
147 else if (size == 'K') {
148 memcg_conf->threshold[lvl].threshold =
149 KBYTE_TO_MBYTE(atoi(value));
152 _E("Memory size unit should be GB or MB or KB");
153 return RESOURCED_ERROR_FAIL;
157 memcg_conf->threshold[lvl].threshold = atoi(value);
160 memcg_conf->threshold[lvl].percent = percent;
161 return RESOURCED_ERROR_NONE;
164 struct memcg_conf *get_memcg_conf(void)
167 memcg_conf = (struct memcg_conf *)calloc(1, sizeof (struct memcg_conf));
169 _E("Failed to alloc memory for cpu configuration");
177 void free_memcg_conf(void)
183 static void set_limit_in_bytes(const char *dir, unsigned long long limit_bytes)
186 unsigned long long prev_bytes;
188 error = cgroup_read_node_ulonglong(dir, MEMCG_LIMIT_BYTE, &prev_bytes);
190 _E("[MEMORY-LIMIT] Failed to get %s from %s", MEMCG_LIMIT_BYTE, dir);
194 if (limit_bytes == prev_bytes)
197 if (totalram_bytes > 0 && limit_bytes > totalram_bytes)
198 limit_bytes = totalram_bytes;
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);
205 cgroup_write_node_ulonglong(dir, MEMCG_SWAP_LIMIT_BYTE, limit_bytes);
206 cgroup_write_node_ulonglong(dir, MEMCG_LIMIT_BYTE, limit_bytes);
210 int check_oom_and_set_limit(const char *dir, unsigned long long limit_bytes)
213 static unsigned int poo = -1;
216 error = fread_uint("/proc/sys/vm/panic_on_oom", &poo);
218 _E("[MEMORY-LIMIT] Failed to get %s from %s", "/proc/sys/vm/panic_on_oom", dir);
223 /* If panic_on_oom is true (> 1), turn of panic_on_oom */
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);
228 fread_uint("/proc/sys/vm/panic_on_oom", &poo);
230 _E("[MEMORY-LIMIT] Failed to update %s", "/proc/sys/vm/panic_on_oom");
231 return RESOURCED_ERROR_FAIL;
235 set_limit_in_bytes(dir, limit_bytes);
236 return RESOURCED_ERROR_NONE;
239 static int memcg_write_limiter_info(struct memcg_info *mi)
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);
250 if (mi->limit_ratio == MEMCG_NO_LIMIT)
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);
259 static int memcg_write_optimizer_info(struct memcg_info *mi)
261 const char *name = mi->name;
262 int ret = RESOURCED_ERROR_NONE;
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.
270 if (mi->swappiness >= 0)
271 swappiness = mi->swappiness;
272 else if (default_swappiness >= 0)
273 swappiness = default_swappiness;
275 if (swappiness >= 0) {
276 ret = cgroup_write_node_uint32(name,
277 MEMCG_SWAPPINESS, swappiness);
279 _I("[SWAP] failed to write %s %d to %s the",
280 MEMCG_SWAPPINESS, swappiness, name);
286 int memcg_write_limiter_params(void)
289 unsigned long long lower_group_limit_bytes = 0;
291 for (i = CGROUP_LOW; i > CGROUP_ROOT; i--) {
292 struct memcg_info *mi = get_memcg_info(i);
294 if (mi->limit_bytes < lower_group_limit_bytes)
295 mi->limit_bytes = lower_group_limit_bytes;
297 memcg_write_limiter_info(mi);
298 lower_group_limit_bytes = mi->limit_bytes;
301 return RESOURCED_ERROR_NONE;
304 int memcg_write_optimizer_params(void)
308 for (i = CGROUP_VIP; i < CGROUP_END; i++) {
309 struct memcg_info *mi = get_memcg_info(i);
310 memcg_write_optimizer_info(mi);
313 return RESOURCED_ERROR_NONE;
316 void memcg_set_threshold(int type, int level, int value)
318 struct memcg_info *mi = get_memcg_info(type);
320 _E("memory cgroup of %d is NULL", type);
322 mi->threshold_mb[level] = value;
325 void memcg_set_leave_threshold(int type, int value)
327 struct memcg_info *mi = get_memcg_info(type);
329 _E("memory cgroup of %d is NULL", type);
331 mi->threshold_leave_mb = value;
334 void memcg_info_set_limit(struct memcg_info *mi, float ratio,
335 unsigned long long totalram_bytes)
340 mi->limit_bytes = (double)totalram_bytes * ratio;
341 mi->limit_ratio = ratio;
344 void memcg_set_default_swappiness(int swappiness)
346 default_swappiness = swappiness;
349 void memcg_info_set_swappiness(struct memcg_info *mi, int swappiness)
354 mi->swappiness = swappiness;
357 static void memcg_memory_stat_init(struct cgroup_memory_stat *mem_stat, long long val)
359 enum cgroup_memory_stat_id id;
363 for (id = 0; id < CGROUP_MEMORY_STAT_MAX; id++)
364 mem_stat->value[id] = val;
367 int memcg_get_memory_stat(const char *name, struct cgroup_memory_stat **mem_stat)
369 _cleanup_fclose_ FILE *f = NULL;
370 struct cgroup_memory_stat *st;
371 char p[PATH_MAX] = "";
373 const char *memory_stat = "memory.stat";
376 snprintf(p, PATH_MAX, "%s/%s", name, memory_stat);
378 snprintf(p, PATH_MAX, "%s/%s", MEMCG_PATH, memory_stat);
384 st = (struct cgroup_memory_stat *)malloc(sizeof(struct cgroup_memory_stat));
388 memcg_memory_stat_init(st, -1);
391 enum cgroup_memory_stat_id id;
394 if (!fgets(buf, sizeof(buf), f)) {
402 l = strcspn(buf, " ");
408 id = cgroup_memory_stat_string_to_id(buf);
409 if (id < 0 || id >= CGROUP_MEMORY_STAT_MAX)
412 st->value[id] = atoll(buf + l + 1);
420 int memcg_get_anon_usage(char *memcg, unsigned long long *anon_usage_bytes)
423 _cleanup_free_ struct cgroup_memory_stat *mem_stat = NULL;
425 r = memcg_get_memory_stat(memcg, &mem_stat);
427 _D("fail to get memory status : %s", memcg);
431 *anon_usage_bytes = mem_stat->value[CGROUP_MEMORY_STAT_INACTIVE_ANON] +
432 mem_stat->value[CGROUP_MEMORY_STAT_ACTIVE_ANON];
436 int memcg_get_swap_usage(char *memcg, unsigned long long *usage_bytes)
439 _cleanup_free_ struct cgroup_memory_stat *mem_stat = NULL;
441 r = memcg_get_memory_stat(memcg, &mem_stat);
443 _D("fail to get memory status : %s", memcg);
447 *usage_bytes = mem_stat->value[CGROUP_MEMORY_STAT_SWAP];
451 int memcg_init_eventfd(int evfd, const char *memcg, const char *event, const char *value)
453 _cleanup_close_ int mcgfd = -1;
454 _cleanup_close_ int cgfd = -1;
456 char buf[PATH_MAX] = {0,};
458 /* open a node of memory cgroup */
459 snprintf(buf, PATH_MAX, "%s/%s", memcg, MEMCG_EVENTFD_CONTROL);
460 cgfd = open(buf, O_WRONLY);
462 const int saved_errno = errno;
463 _E("open event_control failed");
465 return RESOURCED_ERROR_FAIL;
468 snprintf(buf, PATH_MAX, "%s/%s", memcg, event);
469 mcgfd = open(buf, O_RDONLY);
471 const int saved_errno = errno;
472 _E("open memory control failed");
474 return RESOURCED_ERROR_FAIL;
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);
481 res = write(cgfd, buf, sz);
483 int saved_errno = errno;
484 _E("write cgfd failed : %d", res);
486 return RESOURCED_ERROR_FAIL;
489 return RESOURCED_ERROR_NONE;
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
499 * Current memory cgroup supports eventfd about only
500 * usage_in_byte, oom_control and pressure_level.
502 int memcg_set_eventfd(const char *memcg, const char *event, const char *value)
507 /* create an eventfd using eventfd(2)*/
508 evfd = eventfd(0, 0);
509 ret = fcntl(evfd, F_SETFL, O_NONBLOCK);
511 return RESOURCED_ERROR_FAIL;
513 ret = memcg_init_eventfd(evfd, memcg, event, value);
514 if (ret == RESOURCED_ERROR_NONE)
520 struct memcg_info *get_root_memcg_info(void)
525 void memcg_params_init(void)
528 GSList *child_cgroups;
530 for (idx = CGROUP_ROOT; idx < CGROUP_END; idx++) {
531 struct memcg_info *mi = &gmemcg_info[idx];
533 set_memcg_info(idx, mi);
534 if(idx == CGROUP_ROOT)
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);
543 _I("init memory cgroup for %s", mi->name);