4 * Copyright (c) 2023 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 excontroller or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
20 * @file lowmem-controller.c
21 * @desc Provides controller functionalities to kill procs/apps
28 #include <eventsystem.h>
31 #include "lowmem-limit.h"
32 #include "proc-common.h"
33 #include "proc-main.h"
34 #include "resourced.h"
38 #include "safe-kill.h"
42 #include "swap-common.h"
43 #include "dedup-common.h"
45 #define MAX_VICTIMS_BETWEEN_CHECK 3
47 static int num_vict_between_check = MAX_VICTIMS_BETWEEN_CHECK;
49 static int lowmem_kill_victim(const struct task_info *tsk, int flags,
50 int memps_log, unsigned int *victim_size,
51 void(*oom_popup)(void))
55 char appname[PATH_MAX];
60 if (pid <= 0 || pid == getpid())
61 return RESOURCED_ERROR_FAIL;
63 ret = proc_get_cmdline(pid, appname, sizeof appname);
64 if (ret == RESOURCED_ERROR_FAIL)
65 return RESOURCED_ERROR_FAIL;
67 if (!strcmp("memps", appname) ||
68 !strcmp("crash-worker", appname) ||
69 !strcmp("system-syspopup", appname)) {
70 _E("%s(%d) was selected, skip it", appname, pid);
71 return RESOURCED_ERROR_FAIL;
75 * if tsk is an app, then proc_app_info_oom_killed is not NULL,
76 * else proc_app_info_oom_killed is NULL.
78 if (tsk->proc_app_info_oom_killed != NULL) {
79 resourced_proc_status_change(PROC_CGROUP_SET_TERMINATE_REQUEST,
80 pid, NULL, NULL, PROC_TYPE_NONE);
82 if (tsk->oom_score_lru <= OOMADJ_BACKGRD_LOCKED) {
84 } else if (tsk->oom_score_lru > OOMADJ_BACKGRD_LOCKED &&
85 tsk->oom_score_lru < OOMADJ_BACKGRD_UNLOCKED) {
86 sigterm = tsk->proc_app_info_flags & PROC_SIGTERM;
89 if (*(tsk->proc_app_info_oom_killed))
92 *(tsk->proc_app_info_oom_killed) = true;
96 safe_kill(pid, SIGTERM);
98 safe_kill(pid, SIGKILL);
100 _D("[LMK] we killed, force(%d), %d (%s) score = %d, size: rss = %u KB, sigterm = %d\n",
101 flags & OOM_FORCE, pid, appname, tsk->oom_score_adj,
103 *victim_size = tsk->size;
105 if (tsk->oom_score_lru > OOMADJ_FOREGRD_UNLOCKED)
106 return RESOURCED_ERROR_NONE;
110 return RESOURCED_ERROR_NONE;
113 /* return LOWMEM_RECLAIM_CONT when killing should be continued */
114 static int lowmem_check_kill_continued(struct task_info *tsk, int flags,
115 unsigned lmk_start_threshold_mb)
117 unsigned int available_mb = 0;
120 * Processes with the priority higher than perceptible are killed
121 * only when the available memory is less than dynamic oom threshold.
123 if (tsk->oom_score_lru > OOMADJ_BACKGRD_PERCEPTIBLE)
124 return LOWMEM_RECLAIM_CONT;
126 if (flags & (OOM_FORCE | OOM_SINGLE_SHOT)) {
127 _I("[LMK] %d is dropped during force kill, flag=%d",
129 return LOWMEM_RECLAIM_DROP;
131 available_mb = proc_get_mem_available();
132 if (available_mb > lmk_start_threshold_mb) {
133 _I("[LMK] available=%d MB, larger than %u MB, do not kill foreground",
134 available_mb, lmk_start_threshold_mb);
135 return LOWMEM_RECLAIM_RETRY;
137 return LOWMEM_RECLAIM_CONT;
140 static int lowmem_controller_kill_candidates(GArray *candidates,
141 unsigned should_be_freed, unsigned int threshold,
142 int max_victims, int flags,
143 int *status, unsigned int *total_victim_size,
144 unsigned lmk_start_threshold_mb,
145 void(*oom_popup)(void))
149 unsigned int victim_size = 0;
150 unsigned int victim_size_sum = 0;
151 int killer_status = *status;
152 int should_be_freed_kb = MBYTE_TO_KBYTE(should_be_freed);
154 for (int i = 0; i < candidates->len; i++) {
155 struct task_info *tsk;
157 if (i >= max_victims) {
158 killer_status = LOWMEM_RECLAIM_NEXT_TYPE;
163 * Available memory is checking only every
164 * num_vict_between_check process for reducing burden.
166 if (!(i % num_vict_between_check) &&
167 proc_get_mem_available() > threshold) {
168 killer_status = LOWMEM_RECLAIM_DONE;
172 if (!(flags & OOM_NOMEMORY_CHECK) &&
173 victim_size_sum >= should_be_freed_kb) {
174 _D("[LMK] victim=%d, max_victims=%d, total_size=%uKB",
175 victim_cnt, max_victims, victim_size_sum);
176 killer_status = LOWMEM_RECLAIM_DONE;
180 tsk = g_array_index(candidates, struct task_info *, i);
182 killer_status = lowmem_check_kill_continued(tsk, flags,
183 lmk_start_threshold_mb);
184 if (killer_status != LOWMEM_RECLAIM_CONT)
187 _I("[LMK] select victims from proc_app_list pid(%d) with oom_score_adj(%d)\n",
188 tsk->pid, tsk->oom_score_adj);
190 ret_kill = lowmem_kill_victim(tsk, flags, i, &victim_size,
192 if (ret_kill != RESOURCED_ERROR_NONE)
195 victim_size_sum += victim_size;
198 *status = killer_status;
199 *total_victim_size = victim_size_sum;
204 static int dedup_prepare(void)
206 if (dedup_get_state() != DEDUP_ONE_SHOT)
207 return RESOURCED_ERROR_FAIL;
209 if (proc_get_freezer_status() == CGROUP_FREEZER_PAUSED)
210 resourced_notify(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
211 (void *)CGROUP_FREEZER_ENABLED);
213 return RESOURCED_ERROR_NONE;
216 static void swap_activate_act(void)
220 ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status);
222 _E("vconf get failed %s", VCONFKEY_SYSMAN_LOW_MEMORY);
224 if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL) {
225 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
226 VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
227 lowmem_memory_level_send_system_event(MEM_LEVEL_LOW);
229 lowmem_change_lowmem_state(MEM_LEVEL_LOW);
230 if (proc_get_freezer_status() == CGROUP_FREEZER_PAUSED)
231 resourced_notify(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
232 (void *)CGROUP_FREEZER_ENABLED);
234 if (swap_get_state() != SWAP_ON)
235 resourced_notify(RESOURCED_NOTIFIER_SWAP_ACTIVATE, NULL);
238 static void lowmem_swap_memory(char *path)
240 /* The services cannot call this function but the apps can. */
241 unsigned int available_mb;
242 unsigned int cur_mem_state;
244 cur_mem_state = lowmem_get_lowmem_state();
245 if (cur_mem_state == MEM_LEVEL_HIGH)
248 if (swap_get_state() != SWAP_ON)
251 available_mb = proc_get_mem_available();
252 if (cur_mem_state != MEM_LEVEL_LOW &&
253 available_mb <= get_root_memcg_info()->threshold_mb[MEM_LEVEL_LOW])
256 resourced_notify(RESOURCED_NOTIFIER_SWAP_START, path);
257 lowmem_set_memcg_swap_status(true);
260 static void lowmem_move_memcgroup(int pid, int next_oom_score_adj, struct proc_app_info *pai)
262 int cur_oom_score_adj;
264 struct memcg_info *mi;
265 int next_memcg_idx = cgroup_get_type(next_oom_score_adj);
267 mi = get_memcg_info(next_memcg_idx);
273 cgroup_write_pid_fullpath(mi->name, pid);
277 if (pai->main_pid == pid) {
279 cur_oom_score_adj = pai->memory.oom_score_adj;
280 cur_memcg_idx = cgroup_get_type(cur_oom_score_adj);
282 if (cur_oom_score_adj == next_oom_score_adj) {
283 _D("next oom_score_adj (%d) is same with current one", next_oom_score_adj);
287 proc_set_process_memory_state(pai, next_memcg_idx, mi, next_oom_score_adj);
289 if (!lowmem_limit_move_cgroup(pai))
292 if(cur_memcg_idx == next_memcg_idx)
295 _I("app (%s) memory cgroup move from %s to %s", pai->appid,
296 lowmem_convert_cgroup_type_to_str(cur_memcg_idx),
297 lowmem_convert_cgroup_type_to_str(next_memcg_idx));
299 cgroup_write_pid_fullpath(mi->name, pid);
300 if (next_memcg_idx == MEMCG_THROTTLING)
301 lowmem_swap_memory(get_memcg_info(MEMCG_THROTTLING)->name);
304 if (pai->memory.use_mem_limit)
307 cgroup_write_pid_fullpath(mi->name, pid);
311 int lowmem_control_handler(void *data)
313 struct lowmem_control_data *lowmem_data = (struct lowmem_control_data *)data;
315 switch (lowmem_data->control_type) {
316 case LOWMEM_MOVE_CGROUP:
317 lowmem_move_memcgroup((pid_t)lowmem_data->pid,
318 lowmem_data->oom_score_adj,
325 return RESOURCED_ERROR_NONE;
329 static int high_mem_act(void *data)
333 ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status);
335 _D("vconf_get_int fail %s", VCONFKEY_SYSMAN_LOW_MEMORY);
336 if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL) {
337 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
338 VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
339 lowmem_memory_level_send_system_event(MEM_LEVEL_HIGH);
342 lowmem_change_lowmem_state(MEM_LEVEL_HIGH);
344 if (swap_get_state() == SWAP_ON && lowmem_get_memcg_swap_status()) {
345 resourced_notify(RESOURCED_NOTIFIER_SWAP_UNSET_LIMIT, get_memcg_info(MEMCG_THROTTLING));
346 lowmem_set_memcg_swap_status(false);
348 if (proc_get_freezer_status() == CGROUP_FREEZER_PAUSED)
349 resourced_notify(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
350 (void *)CGROUP_FREEZER_ENABLED);
352 return RESOURCED_ERROR_NONE;
355 static int medium_mem_act(void *data)
358 int scan_mode = KSM_SCAN_PARTIAL;
360 if (dedup_prepare() < 0)
361 return RESOURCED_ERROR_FAIL;
363 if (vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status) != 0)
364 _E("vconf get failed %s", VCONFKEY_SYSMAN_LOW_MEMORY);
366 if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL) {
367 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
368 VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
369 lowmem_memory_level_send_system_event(MEM_LEVEL_MEDIUM);
371 lowmem_change_lowmem_state(MEM_LEVEL_MEDIUM);
373 resourced_notify(RESOURCED_NOTIFIER_DEDUP_SCAN, &scan_mode);
375 return RESOURCED_ERROR_NONE;
377 static int low_mem_act(void *data)
381 return RESOURCED_ERROR_NONE;
383 static int critical_mem_act(void *data)
385 int scan_mode = KSM_SCAN_FULL;
387 if (dedup_prepare() >= 0)
388 resourced_notify(RESOURCED_NOTIFIER_DEDUP_SCAN, &scan_mode);
390 lowmem_change_lowmem_state(MEM_LEVEL_CRITICAL);
391 resourced_notify(RESOURCED_NOTIFIER_SWAP_COMPACT,
392 (void *)SWAP_COMPACT_MEM_LEVEL_CRITICAL);
393 lowmem_memory_level_send_system_event(MEM_LEVEL_CRITICAL);
395 return RESOURCED_ERROR_NONE;
399 static int lowmem_controller_initialize(void *data)
402 * FIXME: Consider to use one function to initialize the actions
403 * (killer for the lowmem killer and actions for the memory levels)
406 /* Initialize the action(killer) for the lowmem_worker(lowmem killer) */
407 lowmem_initialize_kill_candidates(lowmem_controller_kill_candidates);
409 /* Initialize actions for the memory levels */
410 lowmem_initialize_controller_ops_action(MEM_LEVEL_HIGH, high_mem_act);
411 lowmem_initialize_controller_ops_action(MEM_LEVEL_MEDIUM, medium_mem_act);
412 lowmem_initialize_controller_ops_action(MEM_LEVEL_LOW, low_mem_act);
413 lowmem_initialize_controller_ops_action(MEM_LEVEL_CRITICAL, critical_mem_act);
415 register_notifier(RESOURCED_NOTIFIER_MEM_CONTROL, lowmem_control_handler);
417 return RESOURCED_ERROR_NONE;
420 static int lowmem_controller_finalize(void *data)
422 unregister_notifier(RESOURCED_NOTIFIER_MEM_CONTROL, lowmem_control_handler);
424 return RESOURCED_ERROR_NONE;
427 static struct module_ops lowmem_controller_module_ops = {
428 .priority = MODULE_PRIORITY_INITIAL,
429 .name = "lowmem-controller",
430 .init = lowmem_controller_initialize,
431 .exit = lowmem_controller_finalize,
434 MODULE_REGISTER(&lowmem_controller_module_ops)