4 * Copyright (c) 2015 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 /* For asprintf(3). Only affects this one file
23 * because tests dislike GNU_SOURCE since it
24 * makes mockability difficult. */
32 #include <sys/types.h>
39 #include "module-data.h"
40 #include "lowmem-handler.h"
45 #include "proc-common.h"
46 #include "memory-cgroup.h"
49 #include "config-parser.h"
50 #include "resourced-helper-worker.h"
51 #include "fd-handler.h"
52 #include "dbus-handler.h"
53 #include "safe-kill.h"
55 #define MEM_CONF_FILE RD_CONFIG_FILE(limiter)
56 #define MEMLIMIT_CONFIG_SECTION "MemLimit"
57 #define MEMLIMIT_CONFIG_TRIGGER "MemLimitTrigger"
59 #define MEMLIMIT_CONFIG_LIM_PFX MEMLIMIT_CONFIG_SECTION
60 #define MEMLIMIT_CONFIG_SERVICE MEMLIMIT_CONFIG_LIM_PFX "Service"
61 #define MEMLIMIT_CONFIG_GUIAPP MEMLIMIT_CONFIG_LIM_PFX "GUIApp"
62 #define MEMLIMIT_CONFIG_WIDGET MEMLIMIT_CONFIG_LIM_PFX "Widget"
63 #define MEMLIMIT_CONFIG_BGAPP MEMLIMIT_CONFIG_LIM_PFX "BgApp"
64 #define MIN_LIMIT_VALUE MBYTE_TO_BYTE(1) /* Byte */
66 /*enum mem_limit_type {
72 static enum mem_limit_type mem_limit;*/
75 static GHashTable *memory_limit_hash;
76 static char *registerpath;
77 static unsigned long long mem_service_limit_bytes;
78 static unsigned long long mem_widget_limit_bytes;
79 static unsigned long long mem_guiapp_limit_bytes;
80 static unsigned long long mem_bgapp_limit_bytes;
82 static int mem_service_action = PROC_ACTION_IGNORE;
83 static int mem_widget_action = PROC_ACTION_IGNORE;
84 static int mem_guiapp_action = PROC_ACTION_IGNORE;
85 static int mem_bgapp_action = PROC_ACTION_IGNORE;
87 static void memory_limit_hash_destroy(gpointer data)
89 struct memory_limit_event *mle = (struct memory_limit_event *)data;
91 _E("[MEMORY-LIMIT] Memory limit event structure is NULL");
101 /* NB: `mle->fdh` is NOT cleaned up here. This is because the removal
102 * of `memory_limit_event` only happens in two cases:
104 * a) at the end of `lowmem_limit_cb`, which returns `false`
105 * on removal, which results in `fd_handler` getting cleaned
106 * up independently (see `channel_changed` in `fd-handler.c`)
108 * b) at exit, by which time GIO channels should've already been
109 * cleaned up (due to `g_main_loop_quit` et al.). */
114 static int lowmem_limit_broadcast(pid_t pid)
117 char appname[PROC_NAME_MAX];
118 struct proc_app_info *pai = NULL;
121 pai = find_app_info(pid);
125 ret = proc_get_cmdline(pid, appname, sizeof appname);
127 _E("[MEMORY-LIMIT] Failed to get cmdline basename of pid(%d)", pid);
133 ret = d_bus_broadcast_signal_gvariant(RESOURCED_PATH_OOM,
134 RESOURCED_INTERFACE_OOM, SIGNAL_OOM_MEMLIMIT_EVENT,
135 g_variant_new("(is)", pid, appid));
137 _E("[MEMORY-LIMIT] Fail to broadcast dbus signal with pid(%d), appname(%s)", pid, appname);
142 static gboolean liveness_check_cb(gpointer data)
144 struct memory_limit_event *mle = (struct memory_limit_event *)data;
147 _E("[MEMORY-LIMIT] memory limit event structure is NULL");
151 if (mle->pids_array == NULL) {
152 _E("[MEMORY-LIMIT] pids array should not be NULL");
156 for (int i = 0; i < mle->pids_array->len; i++) {
157 pid_t pid = g_array_index(mle->pids_array, pid_t, i);
161 if (kill(pid, 0) == 0) {
162 _I("[MEMORY-LIMIT] dir %s's pid (%d) is still avlie, so kill forcely", mle->path, pid);
163 safe_kill(pid, SIGKILL);
167 g_array_free(mle->pids_array, true);
169 mle->pids_array = NULL;
171 return G_SOURCE_REMOVE;
174 static bool memory_action_cb(int fd, void *data)
178 GArray *pids_array = NULL;
179 unsigned long long usage_bytes;
180 unsigned long long dummy_efd;
181 char *cg_dir = (char *)data;
182 struct memory_limit_event *mle;
184 mle = g_hash_table_lookup(memory_limit_hash, cg_dir);
186 _E("[MEMORY-LIMIT] invalid event\n");
190 result = read(fd, &dummy_efd, sizeof(dummy_efd));
192 _E("[MEMORY-LIMIT] wrong eventfd %s\n", cg_dir);
196 if (access(cg_dir, F_OK) == -1) {
197 _D("[MEMORY-LIMIT] there is no (%s) cgroup any longer, removing it", cg_dir);
201 result = cgroup_read_node_ulonglong(cg_dir, MEMCG_SWAP_USAGE, &usage_bytes);
203 result = cgroup_read_node_ulonglong(cg_dir, MEMCG_USAGE, &usage_bytes);
205 _D("[MEMORY-LIMIT] there is no (%s) cgroup any longer, removed it", cg_dir);
210 if (usage_bytes < mle->threshold_bytes) {
211 _D("[MEMORY-LIMIT] (%s) cgroup escaped low memory status. usage(%llu) bytes, threshold(%llu) bytes",
212 cg_dir, usage_bytes, mle->threshold_bytes);
216 switch (mle->action) {
217 case PROC_ACTION_BROADCAST:
218 result = cgroup_get_pids(cg_dir, &pids_array);
219 if (result < 0 || !pids_array->len) {
220 g_array_free(pids_array, true);
224 for (int i = 0; i < pids_array->len; i++) {
225 pid = g_array_index(pids_array, pid_t, i);
229 if (lowmem_limit_broadcast(pid)) {
230 _E("[MEMORY-LIMIT] Failed to broadcast of process (%s)", cg_dir);
231 safe_kill(pid, SIGTERM);
235 g_array_free(pids_array, true);
237 case PROC_ACTION_RECLAIM:
238 lowmem_trigger_swap(0, cg_dir, false);
240 case PROC_ACTION_KILL:
241 result = cgroup_get_pids(cg_dir, &pids_array);
242 if (result < 0 || !pids_array->len) {
243 g_array_free(pids_array, true);
247 for (int i = 0; i < pids_array->len; i++) {
248 pid = g_array_index(pids_array, pid_t, i);
252 safe_kill(pid, SIGTERM);
255 if (mle->pids_array == NULL) {
256 mle->pids_array = pids_array;
257 g_timeout_add_seconds(2, liveness_check_cb, mle);
260 g_array_free(pids_array, true);
264 _E("[MEMORY-LIMIT] Unkown action of memory threshold");
270 g_hash_table_remove(memory_limit_hash, cg_dir);
274 int lowmem_reassign_limit(const char *dir,
275 unsigned long long limit_bytes, enum proc_action action)
278 fd_handler_h fdh = NULL;
280 struct memory_limit_event *mle = NULL;
281 char buf[MAX_DEC_SIZE(int)] = {0};
282 unsigned long long max_limit_bytes;
284 if (memory_limit_hash) {
285 /* TO DO: currently concurrent processes with same app name are located
286 * in the same directory.
287 * Fix to distinguish processes with the same app name
289 hash_entry = g_hash_table_lookup(memory_limit_hash, dir);
291 mle = (struct memory_limit_event *)hash_entry;
292 if (mle->threshold_bytes == limit_bytes) {
293 return RESOURCED_ERROR_NONE;
298 memory_limit_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
299 NULL, memory_limit_hash_destroy);
300 if (!memory_limit_hash) {
301 _E("[MEMORY-LIMIT] Failed to create hash table");
302 return RESOURCED_ERROR_FAIL;
306 if (limit_bytes > lowmem_get_totalram()) {
307 max_limit_bytes = lowmem_get_totalram();
308 limit_bytes = lowmem_get_totalram();
310 else if (limit_bytes * 1.2 > lowmem_get_totalram()) {
311 max_limit_bytes = lowmem_get_totalram();
314 max_limit_bytes = limit_bytes * 1.2;
316 check_oom_and_set_limit(dir, max_limit_bytes);
318 snprintf(buf, sizeof(buf), "%llu", limit_bytes);
321 mle->threshold_bytes = limit_bytes;
322 memcg_init_eventfd(mle->fd, dir, registerpath, buf);
323 return RESOURCED_ERROR_NONE;
326 fd = memcg_set_eventfd(dir, registerpath, buf);
328 mle = calloc(1, sizeof(struct memory_limit_event));
330 _E("[MEMORY-LIMIT] out of memory");
331 return RESOURCED_ERROR_OUT_OF_MEMORY;
334 mle->path = (char *)strdup(dir);
336 _E("[MEMORY-LIMIT] out of memory");
338 return RESOURCED_ERROR_OUT_OF_MEMORY;
340 mle->action = action;
341 mle->threshold_bytes = limit_bytes;
342 mle->pids_array = NULL;
343 add_fd_read_handler(fd, memory_action_cb, mle->path, NULL, &fdh);
345 g_hash_table_insert(memory_limit_hash, (gpointer)mle->path,
348 return RESOURCED_ERROR_NONE;
354 int lowmem_limit_move_cgroup(struct proc_app_info *pai)
357 _cleanup_free_ char *path = NULL;
361 return RESOURCED_ERROR_NO_DATA;
363 if (!pai->memory.use_mem_limit)
364 return RESOURCED_ERROR_NO_DATA;
366 ret = asprintf(&path, "%s/%s", MEMCG_HIGH_PP_PATH, pai->appid);
368 _E("[MEMORY-LIMIT] not enough memory");
369 return RESOURCED_ERROR_OUT_OF_MEMORY;
371 cgroup_write_pid_fullpath(path, pai->main_pid);
373 gslist_for_each_item(iter, pai->childs)
374 cgroup_write_pid_fullpath(path, GPOINTER_TO_PID(iter->data));
376 return RESOURCED_ERROR_NONE;
379 int lowmem_limit_set_system_service(pid_t pid, unsigned long long limit_bytes,
380 const char *name, enum proc_action action)
382 _cleanup_free_ char *path = NULL;
384 unsigned long long totalram_bytes = lowmem_get_totalram();
386 if (limit_bytes < MIN_LIMIT_VALUE || limit_bytes > totalram_bytes) {
387 _E("[MEMORY-LIMIT] It's meaningless to set memory limit with size (%llu) bytes", limit_bytes);
388 return RESOURCED_ERROR_INVALID_PARAMETER;
391 if (action == PROC_ACTION_IGNORE)
392 return RESOURCED_ERROR_NONE;
395 _E("[MEMORY-LIMIT] service name is NULL");
396 return RESOURCED_ERROR_FAIL;
399 result = asprintf(&path, "%s/%s", MEMCG_HIGH_PP_PATH, name);
401 _E("[MEMORY-LIMIT] not enough memory");
402 return RESOURCED_ERROR_OUT_OF_MEMORY;
405 result = cgroup_make_subdir(MEMCG_HIGH_PP_PATH, name, NULL);
407 _E("[MEMORY-LIMIT] Failed to create cgroup subdir '%s/%s'",
408 MEMCG_HIGH_PP_PATH, name);
412 result = lowmem_reassign_limit(path, limit_bytes, action);
414 _W("[MEMORY-LIMIT] Failed to reassign limit for %s", path);
418 result = cgroup_write_node_uint32(path, MEMCG_MOVE_CHARGE, 3U);
420 _W("[MEMORY-LIMIT] Failed to set immigrate mode for %s (non-crucial, continuing)", path);
424 cgroup_write_pid_fullpath(path, pid);
426 return RESOURCED_ERROR_NONE;
429 int lowmem_limit_set_app(unsigned long long limit_bytes, struct proc_app_info *pai,
430 enum proc_action action)
432 _cleanup_free_ char *path = NULL;
435 unsigned long long totalram_bytes = lowmem_get_totalram();
437 if (limit_bytes < MIN_LIMIT_VALUE || limit_bytes > totalram_bytes) {
438 _E("[MEMORY-LIMIT] It's meaningless to set memory limit with size (%llu) bytes", limit_bytes);
439 return RESOURCED_ERROR_INVALID_PARAMETER;
442 if (action == PROC_ACTION_IGNORE)
443 return RESOURCED_ERROR_NONE;
446 _E("[MEMORY-LIMIT] process app information is NULL");
447 return RESOURCED_ERROR_INVALID_PARAMETER;
450 result = asprintf(&path, "%s/%s", MEMCG_HIGH_PP_PATH, pai->appid);
452 _E("[MEMORY-LIMIT] not enough memory");
453 return RESOURCED_ERROR_OUT_OF_MEMORY;
456 result = cgroup_make_subdir(MEMCG_HIGH_PP_PATH, pai->appid, NULL);
458 _E("[MEMORY-LIMIT] Failed to create cgroup subdir '%s/%s'",
459 MEMCG_HIGH_PP_PATH, pai->appid);
463 result = lowmem_reassign_limit(path, limit_bytes, action);
465 _W("[MEMORY-LIMIT] Failed to reassign limit for %s", path);
469 result = cgroup_write_node_uint32(path, MEMCG_MOVE_CHARGE, 3U);
471 _W("[MEMORY-LIMIT] Failed to set immigrate mode for %s (non-crucial, continuing)", path);
473 cgroup_write_pid_fullpath(path, pai->main_pid);
475 gslist_for_each_item(iter, pai->childs)
476 cgroup_write_pid_fullpath(path, GPOINTER_TO_PID(iter->data));
479 pai->memory.use_mem_limit = true;
481 return RESOURCED_ERROR_NONE;
484 static int lowmem_limit_app(void *data)
490 struct proc_limit_status *pls = (struct proc_limit_status *)data;
492 error = lowmem_limit_set_app(pls->limit_bytes, pls->ps.pai, pls->action);
494 pls->ps.pai->memory.memlimit_update_exclude = true;
499 static int lowmem_limit_system_service(void *data)
505 struct proc_limit_status *pls = (struct proc_limit_status *)data;
507 error = lowmem_limit_set_system_service(pls->ps.pid, pls->limit_bytes,
508 pls->ps.pci->name, pls->action);
513 static int lowmem_limit_service(void *data)
517 struct proc_status *ps = (struct proc_status *)data;
519 if (ps->pai && ps->pai->memory.memlimit_update_exclude)
520 return RESOURCED_ERROR_NONE;
522 if (mem_service_limit_bytes && mem_service_action != PROC_ACTION_IGNORE) {
523 lowmem_limit_set_app(mem_service_limit_bytes, ps->pai, mem_service_action);
525 return RESOURCED_ERROR_NONE;
528 static int lowmem_limit_appwidget(void *data)
532 struct proc_status *ps = (struct proc_status *)data;
534 if (ps->pai && ps->pai->memory.memlimit_update_exclude)
535 return RESOURCED_ERROR_NONE;
537 if (mem_guiapp_limit_bytes && mem_guiapp_action != PROC_ACTION_IGNORE &&
538 ps->pai->type == PROC_TYPE_GUI) {
539 lowmem_limit_set_app(mem_guiapp_limit_bytes, ps->pai, mem_guiapp_action);
542 if (mem_widget_limit_bytes && mem_widget_action != PROC_ACTION_IGNORE &&
543 ps->pai->type == PROC_TYPE_WIDGET) {
544 lowmem_limit_set_app(mem_widget_limit_bytes, ps->pai, mem_widget_action);
547 return RESOURCED_ERROR_NONE;
550 static int lowmem_limit_bgapp(void *data)
554 struct proc_status *ps = (struct proc_status *)data;
556 if (ps->pai && ps->pai->memory.memlimit_update_exclude)
557 return RESOURCED_ERROR_NONE;
559 lowmem_limit_set_app(mem_bgapp_limit_bytes, ps->pai, mem_bgapp_action);
561 return RESOURCED_ERROR_NONE;
564 static int lowmem_limit_fgapp(void *data)
568 struct proc_status *ps = (struct proc_status *)data;
570 if ((mem_guiapp_limit_bytes && ps->pai->type == PROC_TYPE_GUI) ||
571 (mem_widget_limit_bytes && ps->pai->type == PROC_TYPE_WIDGET))
572 return lowmem_limit_appwidget(data);
574 _E("[MEMORY-LIMIT] Unable to set foreground app limit - app type not supported");
576 return RESOURCED_ERROR_NONE;
579 void lowmem_memory_init(unsigned long long service_limit_bytes, unsigned long long widget_limit_bytes,
580 unsigned long long guiapp_limit_bytes, unsigned long long bgapp_limit_bytes)
582 mem_service_limit_bytes = service_limit_bytes;
583 mem_widget_limit_bytes = widget_limit_bytes;
584 mem_guiapp_limit_bytes = guiapp_limit_bytes;
585 mem_bgapp_limit_bytes = bgapp_limit_bytes;
587 _I("[MEMORY-LIMIT] service = %llu bytes, widget = %llu bytes, guiapp = %llu bytes, bgapp = %llu",
588 mem_service_limit_bytes, mem_widget_limit_bytes, mem_guiapp_limit_bytes, mem_bgapp_limit_bytes);
591 void lowmem_action_init(int service_action, int widget_action,
592 int guiapp_action, int bgapp_action)
594 if (service_action > 0)
595 mem_service_action = service_action;
597 if (widget_action > 0)
598 mem_widget_action = widget_action;
600 if (guiapp_action > 0)
601 mem_guiapp_action = guiapp_action;
603 if (bgapp_action > 0)
604 mem_bgapp_action = bgapp_action;
607 void lowmem_limit_init(void)
610 unsigned long long usage_bytes;
612 result = cgroup_read_node_ulonglong(MEMCG_PATH, MEMCG_SWAP_USAGE, &usage_bytes);
613 if (result == RESOURCED_ERROR_NONE)
614 registerpath = MEMCG_SWAP_USAGE;
616 registerpath = MEMCG_USAGE;
618 register_notifier(RESOURCED_NOTIFIER_LIMIT_SYSTEM_SERVICE, lowmem_limit_system_service);
619 register_notifier(RESOURCED_NOTIFIER_LIMIT_APP, lowmem_limit_app);
621 if (mem_service_limit_bytes && mem_service_action != PROC_ACTION_IGNORE)
622 register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, lowmem_limit_service);
623 if ((mem_guiapp_limit_bytes && mem_guiapp_action != PROC_ACTION_IGNORE) ||
624 (mem_widget_limit_bytes && mem_widget_action != PROC_ACTION_IGNORE))
625 register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, lowmem_limit_appwidget);
626 if (mem_bgapp_limit_bytes && mem_bgapp_action != PROC_ACTION_IGNORE) {
627 if (!(mem_guiapp_limit_bytes && mem_guiapp_action != PROC_ACTION_IGNORE) ||
628 !(mem_widget_limit_bytes && mem_widget_action != PROC_ACTION_IGNORE)) {
629 _W("[MEMORY-LIMIT] Background app limit requires that both GUIApp and Widget limits to be set to work properly. Ignoring.");
631 register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, lowmem_limit_bgapp);
632 register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, lowmem_limit_fgapp);
633 register_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, lowmem_limit_fgapp);
634 register_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, lowmem_limit_bgapp);
639 void lowmem_limit_exit(void)
641 if (memory_limit_hash) {
642 g_hash_table_destroy(memory_limit_hash);
644 memory_limit_hash = NULL;
648 unregister_notifier(RESOURCED_NOTIFIER_LIMIT_SYSTEM_SERVICE, lowmem_limit_system_service);
649 unregister_notifier(RESOURCED_NOTIFIER_LIMIT_APP, lowmem_limit_app);
650 unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, lowmem_limit_service);
651 unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, lowmem_limit_appwidget);
652 unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, lowmem_limit_bgapp);
653 unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, lowmem_limit_fgapp);
654 unregister_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, lowmem_limit_fgapp);
655 unregister_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, lowmem_limit_bgapp);