lowmem-limit: Add oom based event trigger and handler 92/303592/1
authorUnsung Lee <unsung.lee@samsung.com>
Wed, 8 Nov 2023 12:21:52 +0000 (21:21 +0900)
committerUnsung Lee <unsung.lee@samsung.com>
Tue, 2 Jan 2024 08:44:40 +0000 (17:44 +0900)
Add oom based event trigger and handler to restore 'MemLimitTrigger=oom' in tizen 6.0.
If memory limit is triggered by oom instead of threshold, then do NOT kill or
reclaim forcely in the resourced (Kernel's oom job).

Change-Id: I3c00412ddac9e89080aee3a2b8bfa5297ef7e5d9
Signed-off-by: Unsung Lee <unsung.lee@samsung.com>
src/common/cgroup/memory-cgroup.c
src/common/cgroup/memory-cgroup.h
src/resource-limiter/memory/lowmem-limit.c
src/resource-limiter/memory/lowmem.c

index 3cb036e..51aeb53 100644 (file)
@@ -58,6 +58,8 @@ static struct memcg_info *memcg_root;
 
 static struct memcg_conf *memcg_conf = NULL;
 
+static enum memcg_limit_trigger g_limit_trigger;
+
 /*
  * This structure has full hierarchy of cgroups on running system.
  **/
@@ -593,6 +595,9 @@ int memcg_init_eventfd(int evfd, const char *memcg, const char *event, const cha
        int res = 0, sz;
        char buf[PATH_MAX] = {0,};
 
+       if (!event)
+               return RESOURCED_ERROR_INVALID_PARAMETER;
+
        /* open a node of memory cgroup */
        snprintf(buf, PATH_MAX, "%s/%s", memcg, MEMCG_EVENTFD_CONTROL);
        cgfd = open(buf, O_WRONLY);
@@ -642,6 +647,9 @@ int memcg_set_eventfd(const char *memcg, const char *event, const char *value)
        int ret;
        int evfd;
 
+       if (!event)
+               return RESOURCED_ERROR_INVALID_PARAMETER;
+
        /* create an eventfd using eventfd(2)*/
        evfd = eventfd(0, 0);
        ret = fcntl(evfd, F_SETFL, O_NONBLOCK);
@@ -695,3 +703,13 @@ int memcg_make_full_subdir(const char* parentdir)
 
        return RESOURCED_ERROR_NONE;
 }
+
+void set_memcg_limit_trigger(enum memcg_limit_trigger limit_trigger)
+{
+       g_limit_trigger = limit_trigger;
+}
+
+enum memcg_limit_trigger get_memcg_limit_trigger(void)
+{
+       return g_limit_trigger;
+}
index 843921c..6a64274 100644 (file)
@@ -287,6 +287,9 @@ int cgroup_get_type(int oom_score_adj);
 struct memcg_info *get_memcg_info(int idx);
 struct cgroup *get_cgroup_tree(int idx);
 
+void set_memcg_limit_trigger(enum memcg_limit_trigger limit_trigger);
+enum memcg_limit_trigger get_memcg_limit_trigger(void);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
index 1c722d9..b2f5a57 100644 (file)
@@ -171,12 +171,111 @@ timer_out:
        return G_SOURCE_REMOVE;
 }
 
+static int handle_trigger_oom(enum proc_action proc_action, char *cg_dir)
+{
+       _D("Oom event is triggered from kernel, action = %s (path = %s)",
+                       proc_action == PROC_ACTION_BROADCAST ? "broadcast" :
+                       proc_action == PROC_ACTION_RECLAIM ? "reclaim" :
+                       proc_action == PROC_ACTION_KILL ? "kill" : "unknown", cg_dir);
+
+       switch (proc_action) {
+       case PROC_ACTION_KILL:
+               /* Trun off oom_kill_disable bit to be killed by kernel oom */
+               if (cgroup_write_node_uint32(cg_dir, MEMCG_OOM_CONTROL, 0) < 0)
+                       _I("Skip turn off oom_kill_disable because of low kernel version");
+               return RESOURCED_ERROR_NONE;
+       case PROC_ACTION_BROADCAST:
+       case PROC_ACTION_RECLAIM:
+                /* Do nothing in the resourced, oom will handle the situation */
+               return RESOURCED_ERROR_NONE;
+       default:
+               _E("[MEMORY-LIMIT] Unkown action of oom event");
+               return RESOURCED_ERROR_INVALID_PARAMETER;
+       }
+}
+
+static int handle_trigger_threshold(enum proc_action proc_action,
+               unsigned long long threshold_bytes, char *cg_dir)
+{
+       unsigned long long usage_bytes;
+       GArray *pids_array = NULL;
+       pid_t pid;
+       int result;
+
+       if (memcg_memsw_is_supported())
+               result = cgroup_read_node_ulonglong(cg_dir, MEMCG_SWAP_USAGE, &usage_bytes);
+       else
+               result = cgroup_read_node_ulonglong(cg_dir, MEMCG_USAGE, &usage_bytes);
+
+       if (result < 0) {
+               _D("[MEMORY-LIMIT] there is no (%s) cgroup any longer, removed it", cg_dir);
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       if (usage_bytes < threshold_bytes) {
+               _D("[MEMORY-LIMIT] (%s) cgroup escaped low memory status. usage(%llu) bytes, threshold(%llu) bytes",
+                               cg_dir, usage_bytes, threshold_bytes);
+               return RESOURCED_ERROR_NONE;
+       }
+
+       _D("Memory limit event is triggered, usage = %llu KB, action = %s (path = %s)",
+                       BYTE_TO_KBYTE(usage_bytes),
+                       proc_action == PROC_ACTION_BROADCAST ? "broadcast" :
+                       proc_action == PROC_ACTION_RECLAIM ? "reclaim" :
+                       proc_action == PROC_ACTION_KILL ? "kill" : "unknown", cg_dir);
+
+       switch (proc_action) {
+       case PROC_ACTION_BROADCAST:
+               result = cgroup_get_pids(cg_dir, &pids_array);
+               if (result < 0 || !pids_array->len) {
+                       g_array_free(pids_array, true);
+                       return RESOURCED_ERROR_FAIL;
+               }
+
+               for (int i = 0; i < pids_array->len; i++) {
+                       pid = g_array_index(pids_array, pid_t, i);
+                       if (!pid)
+                               continue;
+
+                       if (lowmem_limit_broadcast(pid)) {
+                               _E("[MEMORY-LIMIT] Failed to broadcast of process (%s)", cg_dir);
+                               safe_kill(pid, SIGTERM);
+                       }
+               }
+
+               g_array_free(pids_array, true);
+               break;
+       case PROC_ACTION_RECLAIM:
+               lowmem_trigger_swap(0, cg_dir, false);
+               break;
+       case PROC_ACTION_KILL:
+               result = cgroup_get_pids(cg_dir, &pids_array);
+               if (result < 0 || !pids_array->len) {
+                       g_array_free(pids_array, true);
+                       return RESOURCED_ERROR_FAIL;
+               }
+
+               for (int i = 0; i < pids_array->len; i++) {
+                       pid = g_array_index(pids_array, pid_t, i);
+                       if (!pid)
+                               continue;
+
+                       safe_kill(pid, SIGTERM);
+               }
+               g_timeout_add_seconds(2, liveness_check_cb, pids_array);
+               break;
+       default:
+               _E("[MEMORY-LIMIT] Unkown action of memory threshold");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       return RESOURCED_ERROR_NONE;
+}
+
 static bool memory_action_cb(int fd, void *data)
 {
+       enum memcg_limit_trigger limit_trigger = get_memcg_limit_trigger();
        int result;
-       pid_t pid;
-       GArray *pids_array = NULL;
-       unsigned long long usage_bytes;
        unsigned long long dummy_efd;
        char *cg_dir = (char *)data;
        struct memory_limit_event *mle;
@@ -198,95 +297,36 @@ static bool memory_action_cb(int fd, void *data)
                goto remove_mle;
        }
 
-       if (memcg_memsw_is_supported())
-               result = cgroup_read_node_ulonglong(cg_dir, MEMCG_SWAP_USAGE, &usage_bytes);
-       else
-               result = cgroup_read_node_ulonglong(cg_dir, MEMCG_USAGE, &usage_bytes);
-
-       if (result < 0) {
-               _D("[MEMORY-LIMIT] there is no (%s) cgroup any longer, removed it", cg_dir);
-               goto remove_mle;
+       switch (limit_trigger) {
+       case MEMCG_TRIGGER_OOM:
+               result = handle_trigger_oom(mle->action, cg_dir);
+               break;
+       case MEMCG_TRIGGER_THRESHOLD:
+               result = handle_trigger_threshold(mle->action, mle->threshold_bytes, cg_dir);
+               break;
+       default:
+               _E("Unknown memcg trigger = %d", limit_trigger);
+               assert(0);
        }
 
-       if (usage_bytes < mle->threshold_bytes) {
-               _D("[MEMORY-LIMIT] (%s) cgroup escaped low memory status. usage(%llu) bytes, threshold(%llu) bytes",
-                       cg_dir, usage_bytes, mle->threshold_bytes);
+       if (result == RESOURCED_ERROR_NONE)
                return true;
-       }
-
-       switch (mle->action) {
-               case PROC_ACTION_BROADCAST:
-                       result = cgroup_get_pids(cg_dir, &pids_array);
-                       if (result < 0 || !pids_array->len) {
-                               g_array_free(pids_array, true);
-                               goto remove_mle;
-                       }
-
-                       for (int i = 0; i < pids_array->len; i++) {
-                               pid = g_array_index(pids_array, pid_t, i);
-                               if (!pid)
-                                       continue;
-
-                               if (lowmem_limit_broadcast(pid)) {
-                                       _E("[MEMORY-LIMIT] Failed to broadcast of process (%s)", cg_dir);
-                                       safe_kill(pid, SIGTERM);
-                               }
-                       }
-
-                       g_array_free(pids_array, true);
-                       break;
-               case PROC_ACTION_RECLAIM:
-                       lowmem_trigger_swap(0, cg_dir, false);
-                       break;
-               case PROC_ACTION_KILL:
-                       result = cgroup_get_pids(cg_dir, &pids_array);
-                       if (result < 0 || !pids_array->len) {
-                               g_array_free(pids_array, true);
-                               goto remove_mle;
-                       }
-
-                       for (int i = 0; i < pids_array->len; i++) {
-                               pid = g_array_index(pids_array, pid_t, i);
-                               if (!pid)
-                                       continue;
-
-                               safe_kill(pid, SIGTERM);
-                       }
-                       g_timeout_add_seconds(2, liveness_check_cb, pids_array);
-
-                       break;
-               default:
-                       _E("[MEMORY-LIMIT] Unkown action of memory threshold");
-       }
-
-       return true;
 
 remove_mle:
        g_hash_table_remove(memory_limit_hash, cg_dir);
+
        return false;
 }
 
 int lowmem_reassign_limit(const char *dir,
                unsigned long long limit_bytes, enum proc_action action)
 {
+       enum memcg_limit_trigger limit_trigger = get_memcg_limit_trigger();
        int fd;
        gpointer hash_entry;
        struct memory_limit_event *mle = NULL;
        char buf[MAX_DEC_SIZE(int)] = {0};
-       unsigned long long max_limit_bytes = limit_bytes;
-
-
-       if (lowmem_get_totalram() > 0) {
-               if (limit_bytes > lowmem_get_totalram()) {
-                       max_limit_bytes = lowmem_get_totalram();
-                       limit_bytes = lowmem_get_totalram();
-               }
-               else if (limit_bytes * 1.2 > lowmem_get_totalram()) {
-                       max_limit_bytes = lowmem_get_totalram();
-               }
-               else
-                       max_limit_bytes = limit_bytes * 1.2;
-       }
+       unsigned long long max_limit_bytes;
 
        if (memory_limit_hash) {
                /* TO DO: currently concurrent processes with same app name are located
@@ -310,10 +350,41 @@ int lowmem_reassign_limit(const char *dir,
                }
        }
 
+       if (lowmem_get_totalram() > 0 && limit_bytes > lowmem_get_totalram())
+               limit_bytes = lowmem_get_totalram();
+
+       max_limit_bytes = limit_bytes;
+       switch (limit_trigger) {
+       case MEMCG_TRIGGER_NONE:
+               break;
+       case MEMCG_TRIGGER_OOM:
+               /**
+                * Previous kernel didn't support to oom_kill_disable in user space.
+                * But recent kernel over 4.0 version allows to disable oom.
+                * If kernel supports it, resourced can receive oom event
+                * before killing application in kernel side.
+                */
+               if (cgroup_write_node_uint32(dir, MEMCG_OOM_CONTROL, 1) < 0)
+                       _I("Skip turn on oom_kill_disable because of low kernel version");
+               break;
+       case MEMCG_TRIGGER_THRESHOLD:
+               snprintf(buf, sizeof(buf), "%llu", limit_bytes);
+               if (lowmem_get_totalram() > 0 && max_limit_bytes * 1.2 > lowmem_get_totalram())
+                       max_limit_bytes = lowmem_get_totalram();
+               else
+                       max_limit_bytes *= 1.2;
+
+               break;
+       default:
+               _E("Unknown memcg trigger = %d", limit_trigger);
+               assert(0);
+       }
 
        check_oom_and_set_limit(dir, max_limit_bytes);
 
-       snprintf(buf, sizeof(buf), "%llu", limit_bytes);
+       /* Do not trap event if limiter_trigger type is MEMCG_TRIGGER_NONE */
+       if (limit_trigger == MEMCG_TRIGGER_NONE)
+               return RESOURCED_ERROR_NONE;
 
        if (mle) {
                mle->threshold_bytes = limit_bytes;
@@ -621,14 +692,34 @@ void lowmem_action_init(int service_action, int widget_action,
 
 void lowmem_limit_init(void)
 {
-       if (memcg_memsw_is_supported())
-               registerpath = MEMCG_SWAP_USAGE;
-       else
-               registerpath = MEMCG_USAGE;
+       enum memcg_limit_trigger limit_trigger = get_memcg_limit_trigger();
 
        register_notifier(RESOURCED_NOTIFIER_LIMIT_SYSTEM_SERVICE, lowmem_limit_system_service);
        register_notifier(RESOURCED_NOTIFIER_LIMIT_APP, lowmem_limit_app);
 
+       switch (limit_trigger) {
+       case MEMCG_TRIGGER_NONE:
+               registerpath = NULL;
+               return;
+       case MEMCG_TRIGGER_OOM:
+               registerpath = MEMCG_OOM_CONTROL;
+               break;
+       case MEMCG_TRIGGER_THRESHOLD:
+               if (memcg_memsw_is_supported())
+                       registerpath = MEMCG_SWAP_USAGE;
+               else
+                       registerpath = MEMCG_USAGE;
+               break;
+       default:
+               _E("Unknown memcg trigger = %d", limit_trigger);
+               assert(0);
+       }
+
+       _D("registerpath = %s, trigger = %s", registerpath,
+                       limit_trigger == MEMCG_TRIGGER_NONE ? "None" :
+                       limit_trigger == MEMCG_TRIGGER_OOM ? "Oom" :
+                       limit_trigger == MEMCG_TRIGGER_THRESHOLD ? "Threshold" : "Unknown");
+
        if (mem_service_limit_bytes && mem_service_action != PROC_ACTION_IGNORE)
                register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, lowmem_limit_service);
        else if (lmk_governor_post_is_enabled_of_wss())
index e059094..2c7d611 100644 (file)
@@ -1588,6 +1588,7 @@ static void load_configs(void)
                }
        }
        oom_popup_enable = memcg_conf->oom_popup;
+       set_memcg_limit_trigger(memcg_conf->limit_trigger);
 
        /* set MemoryAppTypeLimit and MemoryAppStatusLimit section */
        lowmem_memory_init(memcg_conf->service.memory_bytes, memcg_conf->widget.memory_bytes,