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 */
67 /* check only swap usage also */
69 /* register OOM event and control application in kernel side */
71 /* register threshold event and control application in resourced*/
75 static enum mem_limit_type mem_limit;
78 static GHashTable *memory_limit_hash;
79 static char *registerpath;
80 static unsigned int mem_service_limit;
81 static unsigned int mem_widget_limit;
82 static unsigned int mem_guiapp_limit;
83 static unsigned int mem_bgapp_limit;
85 static int mem_service_action = PROC_ACTION_KILL;
86 static int mem_widget_action = PROC_ACTION_KILL;
87 static int mem_guiapp_action = PROC_ACTION_KILL;
88 static int mem_bgapp_action = PROC_ACTION_KILL;
95 struct memory_limit_log {
101 static int get_pid_use_max_memory(GArray *pids_array, unsigned int *max_mem)
104 struct memory_info mi = {0, 0};
106 if (!pids_array->len)
107 return RESOURCED_ERROR_NO_DATA;
109 for (i = 0; i < pids_array->len; i++) {
113 mpid = g_array_index(pids_array, pid_t, i);
117 ret = proc_get_mem_usage(mpid, &size);
118 if (ret != RESOURCED_ERROR_NONE)
121 _D("%d used %d memory", mpid, size);
123 if (size > mi.size) {
129 _D("%d used max %d memory", mi.pid, mi.size);
133 static pid_t get_main_pid(const char *dir, unsigned int *max_mem)
135 GArray *pids_array = NULL;
136 _cleanup_free_ char *path = NULL;
140 ret = asprintf(&path, "%s/", dir);
144 ret = cgroup_get_pids(path, &pids_array);
145 if (ret < 0 || !pids_array->len) {
146 g_array_free(pids_array, true);
147 return RESOURCED_ERROR_NO_DATA;
150 main_pid = get_pid_use_max_memory(pids_array, max_mem);
151 g_array_free(pids_array, true);
155 static void memory_limit_hash_destroy(gpointer data)
157 struct memory_limit_event *mle = (struct memory_limit_event *)data;
159 _E("[DEBUG] Memory limit event structure is NULL");
169 /* NB: `mle->fdh` is NOT cleaned up here. This is because the removal
170 * of `memory_limit_event` only happens in two cases:
172 * a) at the end of `lowmem_limit_cb`, which returns `false`
173 * on removal, which results in `fd_handler` getting cleaned
174 * up independently (see `channel_changed` in `fd-handler.c`)
176 * b) at exit, by which time GIO channels should've already been
177 * cleaned up (due to `g_main_loop_quit` et al.). */
182 static int lowmem_limit_broadcast(pid_t pid)
185 char appname[PROC_NAME_MAX];
186 struct proc_app_info *pai = NULL;
189 pai = find_app_info(pid);
193 ret = proc_get_cmdline(pid, appname, sizeof appname);
195 _E("[DEBUG] Failed to get cmdline basename of pid(%d)", pid);
201 ret = d_bus_broadcast_signal_gvariant(RESOURCED_PATH_OOM,
202 RESOURCED_INTERFACE_OOM, SIGNAL_OOM_MEMLIMIT_EVENT,
203 g_variant_new("(is)", pid, appid));
205 _E("[DEBUG] Fail to broadcast dbus signal with pid(%d), appname(%s)", pid, appname);
210 static gboolean liveness_check_cb(gpointer data)
212 struct memory_limit_event *mle = (struct memory_limit_event *)data;
215 _E("[DEBUG] memory limit event structure is NULL");
220 _E("[DEBUG] pid should be larger than 0");
224 if (kill(mle->pid, 0) == 0) {
225 safe_kill(mle->pid, SIGKILL);
231 return G_SOURCE_REMOVE;
234 static bool memory_action_cb(int fd, void *data)
238 uint32_t usage, max_mem;
240 char *cg_dir = (char *)data;
241 struct memory_limit_event *mle;
242 _cleanup_free_ struct cgroup_memory_stat *mem_stat = NULL;
244 mle = g_hash_table_lookup(memory_limit_hash, cg_dir);
246 _E("invalid event\n");
250 result = read(fd, &dummy_efd, sizeof(dummy_efd));
252 _E("[DEBUG] wrong eventfd %s\n", cg_dir);
256 if (access(cg_dir, F_OK) == -1) {
257 _D("[DEBUG] there is no (%s) cgroup any longer, removing it", cg_dir);
261 result = cgroup_read_node_uint32(cg_dir, MEMCG_SWAP_USAGE, &usage);
263 result = cgroup_read_node_uint32(cg_dir, MEMCG_USAGE, &usage);
265 _D("[DEBUG] there is no (%s) cgroup any longer, removed it", cg_dir);
270 if (usage < mle->threshold) {
271 _D("[DEBUG] (%s) cgroup escaped low memory status. usage(%d), threshold(%d)",
272 cg_dir, usage, mle->threshold);
276 result = memcg_get_memory_stat(cg_dir, &mem_stat);
278 _E("[DEBUG] Failed to get memory status : %s", cg_dir);
282 switch (mle->action) {
283 case PROC_ACTION_BROADCAST:
284 main_pid = get_main_pid(cg_dir, &max_mem);
286 _D("[DEBUG] there is no victim, removed cgroup : %s", cg_dir);
290 if (lowmem_limit_broadcast(main_pid)) {
291 _E("[DEBUG] Failed to broadcast of process (%s)", cg_dir);
295 case PROC_ACTION_RECLAIM:
296 lowmem_trigger_swap(0, cg_dir, false);
298 case PROC_ACTION_KILL:
299 main_pid = get_main_pid(cg_dir, &max_mem);
301 _D("[DEBUG] there is no victim, removed cgroup : %s", cg_dir);
304 safe_kill(main_pid, SIGTERM);
306 if (mle->pid == -1) {
308 g_timeout_add_seconds(2, liveness_check_cb, mle);
313 _E("[DEBUG] Unkown action of memory threshold");
319 g_hash_table_remove(memory_limit_hash, cg_dir);
323 int lowmem_reassign_limit(const char *dir,
324 unsigned int limit, enum proc_action action)
327 fd_handler_h fdh = NULL;
329 struct memory_limit_event *mle = NULL;
330 char buf[MAX_DEC_SIZE(int)] = {0};
332 if (memory_limit_hash) {
333 /* TO DO: currently concurrent processes with same app name are located
334 * in the same directory.
335 * Fix to distinguish processes with the same app name
337 hash_entry = g_hash_table_lookup(memory_limit_hash, dir);
339 mle = (struct memory_limit_event *)hash_entry;
340 if (mle->threshold == limit) {
341 return RESOURCED_ERROR_NONE;
346 memory_limit_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
347 NULL, memory_limit_hash_destroy);
348 if (!memory_limit_hash) {
349 _E("[DEBUG] Failed to create hash table");
350 return RESOURCED_ERROR_FAIL;
354 check_oom_and_set_limit(dir, limit * 1.2);
355 snprintf(buf, sizeof(buf), "%d", limit);
358 mle->threshold = limit;
359 memcg_init_eventfd(mle->fd, dir, registerpath, buf);
360 return RESOURCED_ERROR_NONE;
363 fd = memcg_set_eventfd(dir, registerpath, buf);
365 mle = calloc(1, sizeof(struct memory_limit_event));
367 _E("[DEBUG] out of memory");
368 return RESOURCED_ERROR_OUT_OF_MEMORY;
371 mle->path = (char *)strdup(dir);
373 _E("[DEBUG] out of memory");
375 return RESOURCED_ERROR_OUT_OF_MEMORY;
377 mle->action = action;
378 mle->threshold = limit;
380 add_fd_read_handler(fd, memory_action_cb, mle->path, NULL, &fdh);
382 g_hash_table_insert(memory_limit_hash, (gpointer)mle->path,
385 return RESOURCED_ERROR_NONE;
391 int lowmem_limit_move_cgroup(struct proc_app_info *pai)
394 _cleanup_free_ char *path = NULL;
398 return RESOURCED_ERROR_NO_DATA;
400 if (!pai->memory.use_mem_limit)
401 return RESOURCED_ERROR_NO_DATA;
403 ret = asprintf(&path, "%s/%s", MEMCG_HIGH_PP_PATH, pai->appid);
405 _E("not enough memory");
406 return RESOURCED_ERROR_OUT_OF_MEMORY;
408 cgroup_write_pid_fullpath(path, pai->main_pid);
410 gslist_for_each_item(iter, pai->childs)
411 cgroup_write_pid_fullpath(path, GPOINTER_TO_PID(iter->data));
413 return RESOURCED_ERROR_NONE;
416 void lowmem_limit_set_system_service(pid_t pid, unsigned int limit,
417 const char *name, enum proc_action action)
419 _cleanup_free_ char *path = NULL;
421 unsigned int totalram = lowmem_get_totalram();
423 if (limit < MIN_LIMIT_VALUE || limit > totalram) {
424 _E("[DEBUG] It's meaningless to set memory limit with size (%d)", limit);
429 _E("[DEBUG] service name is NULL");
433 result = asprintf(&path, "%s/%s", MEMCG_HIGH_PP_PATH, name);
435 _E("[DEBUG] not enough memory");
439 result = cgroup_make_subdir(MEMCG_HIGH_PP_PATH, name, NULL);
441 _E("[DEBUG] Failed to create cgroup subdir '%s/%s'",
442 MEMCG_HIGH_PP_PATH, name);
446 result = lowmem_reassign_limit(path, limit, action);
448 _W("Failed to reassign limit for %s", path);
452 result = cgroup_write_node_uint32(path, MEMCG_MOVE_CHARGE, 3U);
454 _W("[DEBUG] Failed to set immigrate mode for %s (non-crucial, continuing)", path);
456 cgroup_write_pid_fullpath(path, pid);
459 int lowmem_limit_set_app(unsigned int limit, struct proc_app_info *pai,
460 enum proc_action action)
462 _cleanup_free_ char *path = NULL;
465 unsigned int totalram = lowmem_get_totalram();
467 if (limit < MIN_LIMIT_VALUE || limit > totalram) {
468 _E("[DEBUG] It's meaningless to set memory limit with size (%d)", limit);
469 return RESOURCED_ERROR_INVALID_PARAMETER;
473 _E("process app information is NULL");
474 return RESOURCED_ERROR_INVALID_PARAMETER;
477 result = asprintf(&path, "%s/%s", MEMCG_HIGH_PP_PATH, pai->appid);
479 _E("not enough memory");
480 return RESOURCED_ERROR_OUT_OF_MEMORY;
483 result = cgroup_make_subdir(MEMCG_HIGH_PP_PATH, pai->appid, NULL);
485 _E("Failed to create cgroup subdir '%s/%s'",
486 MEMCG_HIGH_PP_PATH, pai->appid);
490 result = lowmem_reassign_limit(path, limit, action);
492 _W("Failed to reassign limit for %s", path);
496 result = cgroup_write_node_uint32(path, MEMCG_MOVE_CHARGE, 3U);
498 _W("Failed to set immigrate mode for %s (non-crucial, continuing)", path);
500 cgroup_write_pid_fullpath(path, pai->main_pid);
502 gslist_for_each_item(iter, pai->childs)
503 cgroup_write_pid_fullpath(path, GPOINTER_TO_PID(iter->data));
506 pai->memory.use_mem_limit = true;
508 return RESOURCED_ERROR_NONE;
511 static int lowmem_limit_app(void *data)
517 struct proc_limit_status *pls = (struct proc_limit_status *)data;
519 error = lowmem_limit_set_app(pls->limit, pls->ps.pai, pls->action);
521 pls->ps.pai->memory.memlimit_update_exclude = true;
522 return RESOURCED_ERROR_NONE;
525 static int lowmem_limit_system_service(void *data)
529 struct proc_limit_status *pls = (struct proc_limit_status *)data;
531 lowmem_limit_set_system_service(pls->ps.pid, pls->limit, pls->ps.pci->name, pls->action);
532 return RESOURCED_ERROR_NONE;
535 static int lowmem_limit_service(void *data)
539 struct proc_status *ps = (struct proc_status *)data;
541 if (ps->pai && ps->pai->memory.memlimit_update_exclude)
542 return RESOURCED_ERROR_NONE;
544 if (mem_service_limit) {
545 lowmem_limit_set_app(mem_service_limit, ps->pai, mem_service_action);
547 return RESOURCED_ERROR_NONE;
550 static int lowmem_limit_appwidget(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 if (mem_guiapp_limit && ps->pai->type == PROC_TYPE_GUI) {
560 lowmem_limit_set_app(mem_guiapp_limit, ps->pai, mem_guiapp_action);
562 if (mem_widget_limit && ps->pai->type == PROC_TYPE_WIDGET) {
563 lowmem_limit_set_app(mem_widget_limit, ps->pai, mem_widget_action);
566 return RESOURCED_ERROR_NONE;
569 static int lowmem_limit_bgapp(void *data)
573 struct proc_status *ps = (struct proc_status *)data;
575 if (ps->pai && ps->pai->memory.memlimit_update_exclude)
576 return RESOURCED_ERROR_NONE;
578 lowmem_limit_set_app(mem_bgapp_limit, ps->pai, mem_bgapp_action);
580 return RESOURCED_ERROR_NONE;
583 static int lowmem_limit_fgapp(void *data)
587 struct proc_status *ps = (struct proc_status *)data;
589 if ((mem_guiapp_limit && ps->pai->type == PROC_TYPE_GUI) ||
590 (mem_widget_limit && ps->pai->type == PROC_TYPE_WIDGET))
591 return lowmem_limit_appwidget(data);
593 _E("[DEBUG] Unable to set foreground app limit - app type not supported");
595 return RESOURCED_ERROR_NONE;
598 void lowmem_memory_init(unsigned int service_limit, unsigned int widget_limit,
599 unsigned int guiapp_limit, unsigned int bgapp_limit)
601 mem_service_limit = service_limit;
602 mem_widget_limit = widget_limit;
603 mem_guiapp_limit = guiapp_limit;
604 mem_bgapp_limit = bgapp_limit;
607 void lowmem_action_init(int service_action, int widget_action,
608 int guiapp_action, int bgapp_action)
610 if (service_action > 0)
611 mem_service_action = service_action;
613 if (widget_action > 0)
614 mem_widget_action = widget_action;
616 if (guiapp_action > 0)
617 mem_guiapp_action = guiapp_action;
619 if (bgapp_action > 0)
620 mem_bgapp_action = bgapp_action;
623 void lowmem_limit_init(void)
628 mem_limit = MEM_LIMIT_TRHESHOLD;
629 result = cgroup_read_node_uint32(MEMCG_PATH, MEMCG_SWAP_USAGE, &usage);
630 if (result == RESOURCED_ERROR_NONE)
631 registerpath = MEMCG_SWAP_USAGE;
633 registerpath = MEMCG_USAGE;
635 register_notifier(RESOURCED_NOTIFIER_LIMIT_SYSTEM_SERVICE, lowmem_limit_system_service);
636 register_notifier(RESOURCED_NOTIFIER_LIMIT_APP, lowmem_limit_app);
637 if (mem_limit == MEM_LIMIT_NONE)
640 if (mem_service_limit)
641 register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, lowmem_limit_service);
642 if (mem_guiapp_limit || mem_widget_limit)
643 register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, lowmem_limit_appwidget);
644 if (mem_bgapp_limit) {
645 if (!mem_guiapp_limit || !mem_widget_limit) {
646 _W("Background app limit requires that both GUIApp and Widget limits to be set to work properly. Ignoring.");
648 register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, lowmem_limit_bgapp);
649 register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, lowmem_limit_fgapp);
650 register_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, lowmem_limit_fgapp);
651 register_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, lowmem_limit_bgapp);
656 void lowmem_limit_exit(void)
658 if (memory_limit_hash) {
659 g_hash_table_destroy(memory_limit_hash);
661 memory_limit_hash = NULL;
665 unregister_notifier(RESOURCED_NOTIFIER_LIMIT_SYSTEM_SERVICE, lowmem_limit_system_service);
666 unregister_notifier(RESOURCED_NOTIFIER_LIMIT_APP, lowmem_limit_app);
667 unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, lowmem_limit_service);
668 unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, lowmem_limit_appwidget);
669 unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, lowmem_limit_bgapp);
670 unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, lowmem_limit_fgapp);
671 unregister_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, lowmem_limit_fgapp);
672 unregister_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, lowmem_limit_bgapp);