lowmem-controller: Split up lowmem-controller feature from lowmem module
[platform/core/system/resourced.git] / src / resource-limiter / memory / lowmem-controller.c
1 /**
2  * resourced
3  *
4  * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
5  *
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
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  */
18
19 /**
20  * @file lowmem-controller.c
21  * @desc Provides controller functionalities to kill procs/apps
22  */
23
24 #include <string.h>
25 #include <limits.h>
26
27 #include "lowmem.h"
28 #include "lowmem-controller.h"
29 #include "proc-common.h"
30 #include "proc-main.h"
31 #include "resourced.h"
32 #include "procfs.h"
33 #include "trace.h"
34 #include "util.h"
35 #include "safe-kill.h"
36
37 #define MAX_VICTIMS_BETWEEN_CHECK  3
38
39 static int num_vict_between_check = MAX_VICTIMS_BETWEEN_CHECK;
40
41 static int lowmem_kill_victim(const struct task_info *tsk, int flags,
42                                 int memps_log, unsigned int *victim_size,
43                                 void(*oom_popup)(void))
44 {
45         pid_t pid;
46         int ret;
47         char appname[PATH_MAX];
48         int sigterm = 0;
49         struct proc_app_info *pai;
50
51         pid = tsk->pid;
52
53         if (pid <= 0 || pid == getpid())
54                 return RESOURCED_ERROR_FAIL;
55
56         ret = proc_get_cmdline(pid, appname, sizeof appname);
57         if (ret == RESOURCED_ERROR_FAIL)
58                 return RESOURCED_ERROR_FAIL;
59
60         if (!strcmp("memps", appname) ||
61                         !strcmp("crash-worker", appname) ||
62                         !strcmp("system-syspopup", appname)) {
63                 _E("%s(%d) was selected, skip it", appname, pid);
64                 return RESOURCED_ERROR_FAIL;
65         }
66
67         pai = tsk->pai;
68         if (pai) {
69                 resourced_proc_status_change(PROC_CGROUP_SET_TERMINATE_REQUEST,
70                         pid, NULL, NULL, PROC_TYPE_NONE);
71
72                 if (tsk->oom_score_lru <= OOMADJ_BACKGRD_LOCKED) {
73                         sigterm = 1;
74                 } else if (tsk->oom_score_lru > OOMADJ_BACKGRD_LOCKED &&
75                                 tsk->oom_score_lru < OOMADJ_BACKGRD_UNLOCKED) {
76                         int app_flag = pai->flags;
77                         sigterm = app_flag & PROC_SIGTERM;
78                 }
79
80                 if (pai->memory.oom_killed)
81                         sigterm = 0;
82
83                 pai->memory.oom_killed = true;
84         }
85
86         if (sigterm)
87                 safe_kill(pid, SIGTERM);
88         else
89                 safe_kill(pid, SIGKILL);
90
91         _D("[LMK] we killed, force(%d), %d (%s) score = %d, size: rss = %u KB, sigterm = %d\n",
92                         flags & OOM_FORCE, pid, appname, tsk->oom_score_adj,
93                         tsk->size, sigterm);
94         *victim_size = tsk->size;
95
96         if (tsk->oom_score_lru > OOMADJ_FOREGRD_UNLOCKED)
97                 return RESOURCED_ERROR_NONE;
98
99         oom_popup();
100
101         return RESOURCED_ERROR_NONE;
102 }
103
104 /* return LOWMEM_RECLAIM_CONT when killing should be continued */
105 static int lowmem_check_kill_continued(struct task_info *tsk, int flags,
106                                         unsigned lmk_start_threshold_mb)
107 {
108         unsigned int available_mb = 0;
109
110         /**
111          * Processes with the priority higher than perceptible are killed
112          * only when the available memory is less than dynamic oom threshold.
113          */
114         if (tsk->oom_score_lru > OOMADJ_BACKGRD_PERCEPTIBLE)
115                 return LOWMEM_RECLAIM_CONT;
116
117         if (flags & (OOM_FORCE | OOM_SINGLE_SHOT)) {
118                 _I("[LMK] %d is dropped during force kill, flag=%d",
119                         tsk->pid, flags);
120                 return LOWMEM_RECLAIM_DROP;
121         }
122         available_mb = proc_get_mem_available();
123         if (available_mb > lmk_start_threshold_mb) {
124                 _I("[LMK] available=%d MB, larger than %u MB, do not kill foreground",
125                         available_mb, lmk_start_threshold_mb);
126                 return LOWMEM_RECLAIM_RETRY;
127         }
128         return LOWMEM_RECLAIM_CONT;
129 }
130
131 int lowmem_controller_kill_candidates(GArray *candidates,
132                         unsigned should_be_freed, unsigned int threshold,
133                         int max_victims, int flags,
134                         int *status, unsigned int *total_victim_size,
135                         unsigned lmk_start_threshold_mb,
136                         void(*oom_popup)(void))
137 {
138         int ret_kill = 0;
139         int victim_cnt = 0;
140         unsigned int victim_size = 0;
141         unsigned int victim_size_sum = 0;
142         int killer_status = *status;
143         int should_be_freed_kb = MBYTE_TO_KBYTE(should_be_freed);
144
145         for (int i = 0; i < candidates->len; i++) {
146                 struct task_info *tsk;
147
148                 if (i >= max_victims) {
149                         killer_status = LOWMEM_RECLAIM_NEXT_TYPE;
150                         break;
151                 }
152
153                 /**
154                  * Available memory is checking only every
155                  * num_vict_between_check process for reducing burden.
156                  */
157                 if (!(i % num_vict_between_check) &&
158                                 proc_get_mem_available() > threshold) {
159                         killer_status = LOWMEM_RECLAIM_DONE;
160                         break;
161                 }
162
163                 if (!(flags & OOM_NOMEMORY_CHECK) &&
164                                 victim_size_sum >= should_be_freed_kb) {
165                         _D("[LMK] victim=%d, max_victims=%d, total_size=%uKB",
166                                 victim_cnt, max_victims, victim_size_sum);
167                         killer_status = LOWMEM_RECLAIM_DONE;
168                         break;
169                 }
170
171                 tsk = &g_array_index(candidates, struct task_info, i);
172
173                 killer_status = lowmem_check_kill_continued(tsk, flags,
174                                                         lmk_start_threshold_mb);
175                 if (killer_status != LOWMEM_RECLAIM_CONT)
176                         break;
177
178                 _I("[LMK] select victims from proc_app_list pid(%d) with oom_score_adj(%d)\n",
179                                 tsk->pid, tsk->oom_score_adj);
180
181                 ret_kill = lowmem_kill_victim(tsk, flags, i, &victim_size,
182                                                 oom_popup);
183                 if (ret_kill != RESOURCED_ERROR_NONE)
184                         continue;
185                 victim_cnt++;
186                 victim_size_sum += victim_size;
187         }
188
189         *status = killer_status;
190         *total_victim_size = victim_size_sum;
191
192         return victim_cnt;
193 }