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;
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
}
}
+ 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;
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())