lowmem-monitor: Disable PSI monitoring method
[platform/core/system/resourced.git] / src / resource-limiter / memory / lowmem-monitor-psi.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 express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 /**
20  * @file lowmem-monitor-psi.c
21  * @desc Provides monitor functionalities to detect lowmem state using PSI
22  */
23
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <sys/epoll.h>
28 #include <sys/eventfd.h>
29 #include <pthread.h>
30
31 #include "lowmem.h"
32 #include "procfs.h"
33 #include "trace.h"
34 #include "module.h"
35
36 #define EPOLL_LISTENER_POLLING_PERIOD_SECONDS   3
37 #define EPOLL_LISTENER_MAX_EVENTS               100
38 #define BUFF_MAX                                255
39
40 #define PSI_PATH                                "/proc/pressure"
41 #define PSI_MEMORY_PATH PSI_PATH                "/memory"
42 #define PSI_TYPE_SOME                           "some"
43 #define PSI_TYPE_FULL                           "full"
44
45 struct psi_memory_monitor_info {
46         const int mem_level;
47         const char *mem_level_str;
48         const char *psi_type;
49         const int stall_us;
50         const int window_us;
51         int fd;
52 };
53
54 typedef void *(*epoll_event_handler)(void *data);
55
56 struct epoll_event_data {
57         epoll_event_handler handler;
58         void *data;
59 };
60
61 static int g_psi_monitor_epoll_fd = -1;
62 static pthread_t g_psi_monitor_thread;
63 static int g_psi_monitor_thread_destroy_event_fd = -1;
64
65 static void assert_mem_level(int mem_level)
66 {
67         switch (mem_level) {
68         case MEM_LEVEL_HIGH:
69         case MEM_LEVEL_MEDIUM:
70         case MEM_LEVEL_LOW:
71         case MEM_LEVEL_CRITICAL:
72         case MEM_LEVEL_OOM:
73                 return;
74         default:
75                 assert("Invalid memory level" && 0);
76         }
77 }
78
79 static void *psi_memory_monitor_handler(struct psi_memory_monitor_info *info)
80 {
81         assert_mem_level(info->mem_level);
82
83         lowmem_trigger_memory_state_action(info->mem_level);
84
85         return NULL;
86 }
87
88 static void *psi_monitor_thread_destroy_handler(int *efd)
89 {
90         eventfd_t dummy = 0;
91         eventfd_read(*efd, &dummy);
92
93         pthread_exit(NULL);
94 }
95
96 static struct epoll_event_data g_psi_monitor_thread_destroy_event_data = {
97         .handler = (epoll_event_handler)psi_monitor_thread_destroy_handler,
98         .data = &g_psi_monitor_thread_destroy_event_fd,
99 };
100
101 static int register_psi_event_epoll(struct epoll_event_data *event_data)
102 {
103         char trigger_description[BUFF_MAX] = { 0 };
104         int trigger_description_len = -1;
105         int ret = 0;
106         int fd = -1;
107         struct psi_memory_monitor_info *info = NULL;
108         struct epoll_event event;
109
110         assert(g_psi_monitor_epoll_fd >= 0);
111
112         assert(event_data);
113         info = event_data->data;
114         assert(info);
115         assert_mem_level(info->mem_level);
116
117         fd = open(PSI_MEMORY_PATH, O_RDWR | O_NONBLOCK);
118         if (fd < 0) {
119                 _E("Failed to open psi node: path=%s, errno=%d",
120                                         PSI_MEMORY_PATH, errno);
121                 goto error_1;
122         }
123         info->fd = fd;
124
125         event.events = EPOLLPRI;
126         event.data.ptr = (void *)event_data;
127
128         trigger_description_len = snprintf(trigger_description, BUFF_MAX,
129                                                         "%s %d %d",
130                                                         info->psi_type,
131                                                         info->stall_us,
132                                                         info->window_us);
133         if (trigger_description_len < 0) {
134                 _E("Failed to write PSI trigger description: returned value=%d",
135                                                 trigger_description_len);
136                 goto error_2;
137         }
138         if (trigger_description_len >= BUFF_MAX) {
139                 _E("Failed to write PSI trigger description: buffer is not enough");
140                 goto error_2;
141         }
142
143         ret = write(fd, trigger_description, trigger_description_len + 1);
144         if (ret < 0) {
145                 _E("Failed to write PSI trigger description(\"%s\"): errno=%d",
146                                                 trigger_description, errno);
147                 goto error_2;
148         }
149
150         ret = epoll_ctl(g_psi_monitor_epoll_fd, EPOLL_CTL_ADD, fd, &event);
151         if (ret < 0) {
152                 _E("Failed to add epoll: errno=%d", errno);
153                 goto error_2;
154         }
155
156         _I("PSI event registered(%s %d %d) as %s mem_level", info->psi_type,
157                                                         info->stall_us,
158                                                         info->window_us,
159                                                         info->mem_level_str);
160
161         return 0;
162
163 error_2:
164         close(fd);
165         info->fd = -1;
166 error_1:
167         _E("Failed to register psi event: mem_level=%s", info->mem_level_str);
168
169         return RESOURCED_ERROR_FAIL;
170 }
171
172 static void unregister_psi_event_epoll(struct epoll_event_data *event_data)
173 {
174         struct psi_memory_monitor_info *info = NULL;
175
176         assert(event_data);
177
178         info = event_data->data;
179         assert(info);
180         assert_mem_level(info->mem_level);
181
182         if (info->fd < 0)
183                 return;
184
185         epoll_ctl(g_psi_monitor_epoll_fd, EPOLL_CTL_DEL, info->fd, NULL);
186         close(info->fd);
187         info->fd = -1;
188 }
189
190 static void *psi_monitor_thread_worker(void *data)
191 {
192         struct epoll_event events[EPOLL_LISTENER_MAX_EVENTS];
193         int events_num = 0;
194
195         while (1) {
196                 events_num = epoll_wait(g_psi_monitor_epoll_fd, events, EPOLL_LISTENER_MAX_EVENTS, 0);
197
198                 for (int i = 0; i < events_num; ++i) {
199                         if (events[i].events & (EPOLLERR | EPOLLHUP))
200                                 continue;
201
202                         struct epoll_event_data *event_data = events[i].data.ptr;
203                         event_data->handler(event_data->data);
204                 }
205
206                 sleep(EPOLL_LISTENER_POLLING_PERIOD_SECONDS);
207         }
208
209         pthread_exit(NULL);
210 }
211
212 static int create_psi_monitor_thread(void)
213 {
214         int ret = 0;
215         struct epoll_event event;
216
217         g_psi_monitor_thread_destroy_event_fd = eventfd(0, EFD_CLOEXEC);
218         if (g_psi_monitor_thread_destroy_event_fd < 0) {
219                 _E("Failed to create eventfd for thread: errno=%d", errno);
220                 return RESOURCED_ERROR_FAIL;
221         }
222         g_psi_monitor_thread_destroy_event_data.data =
223                         (void *)(&g_psi_monitor_thread_destroy_event_fd);
224
225         event.events = EPOLLIN;
226         event.data.ptr = (void *)(&g_psi_monitor_thread_destroy_event_data);
227
228         ret = epoll_ctl(g_psi_monitor_epoll_fd, EPOLL_CTL_ADD, g_psi_monitor_thread_destroy_event_fd, &event);
229         if (ret != 0) {
230                 close(g_psi_monitor_thread_destroy_event_fd);
231                 g_psi_monitor_thread_destroy_event_fd = -1;
232                 _E("Failed to add eventfd for thread: errno=%d", errno);
233                 return RESOURCED_ERROR_FAIL;
234         }
235
236         ret = pthread_create(&g_psi_monitor_thread, NULL, psi_monitor_thread_worker, NULL);
237         if (ret != 0) {
238                 epoll_ctl(g_psi_monitor_epoll_fd, EPOLL_CTL_DEL, g_psi_monitor_thread_destroy_event_fd, NULL);
239                 close(g_psi_monitor_thread_destroy_event_fd);
240                 g_psi_monitor_thread_destroy_event_fd = -1;
241                 _E("Failed to create psi monitor thread: errno=%d", errno);
242                 return RESOURCED_ERROR_FAIL;
243         }
244
245         return RESOURCED_ERROR_NONE;
246 }
247
248 static void destroy_psi_monitor_thread(void)
249 {
250         eventfd_write(g_psi_monitor_thread_destroy_event_fd, 1);
251         pthread_join(g_psi_monitor_thread, NULL);
252
253         epoll_ctl(g_psi_monitor_epoll_fd, EPOLL_CTL_DEL, g_psi_monitor_thread_destroy_event_fd, NULL);
254         close(g_psi_monitor_thread_destroy_event_fd);
255         g_psi_monitor_thread_destroy_event_fd = -1;
256 }
257
258 static struct psi_memory_monitor_info g_psi_memory_monitor_info_list[MEM_LEVEL_MAX] = {
259         [MEM_LEVEL_HIGH] = {
260                 .mem_level = MEM_LEVEL_HIGH,
261                 .mem_level_str = "high",
262                 .psi_type = PSI_TYPE_SOME,
263                 .stall_us = 70000,
264                 .window_us = 1000000,
265                 .fd = -1,
266         },
267         [MEM_LEVEL_MEDIUM] = {
268                 .mem_level = MEM_LEVEL_MEDIUM,
269                 .mem_level_str = "medium",
270                 .psi_type = PSI_TYPE_SOME,
271                 .stall_us = 70000,
272                 .window_us = 1000000,
273                 .fd = -1,
274         },
275         [MEM_LEVEL_LOW] = {
276                 .mem_level = MEM_LEVEL_LOW,
277                 .mem_level_str = "low",
278                 .psi_type = PSI_TYPE_SOME,
279                 .stall_us = 150000,
280                 .window_us = 1000000,
281                 .fd = -1,
282         },
283         [MEM_LEVEL_CRITICAL] = {
284                 .mem_level = MEM_LEVEL_CRITICAL,
285                 .mem_level_str = "critical",
286                 .psi_type = PSI_TYPE_FULL,
287                 .stall_us = 70000,
288                 .window_us = 1000000,
289                 .fd = -1,
290         },
291         [MEM_LEVEL_OOM] = {
292                 .mem_level = MEM_LEVEL_OOM,
293                 .mem_level_str = "oom",
294                 .psi_type = PSI_TYPE_FULL,
295                 .stall_us = 150000,
296                 .window_us = 1000000,
297                 .fd = -1,
298         }
299 };
300
301 static struct epoll_event_data g_psi_event_datas[MEM_LEVEL_MAX] = {
302         [MEM_LEVEL_HIGH] = {
303                 .handler = (epoll_event_handler)psi_memory_monitor_handler,
304                 .data = &g_psi_memory_monitor_info_list[MEM_LEVEL_HIGH],
305         },
306         [MEM_LEVEL_MEDIUM] = {
307                 .handler = (epoll_event_handler)psi_memory_monitor_handler,
308                 .data = &g_psi_memory_monitor_info_list[MEM_LEVEL_MEDIUM],
309         },
310         [MEM_LEVEL_LOW] = {
311                 .handler = (epoll_event_handler)psi_memory_monitor_handler,
312                 .data = &g_psi_memory_monitor_info_list[MEM_LEVEL_LOW],
313         },
314         [MEM_LEVEL_CRITICAL] = {
315                 .handler = (epoll_event_handler)psi_memory_monitor_handler,
316                 .data = &g_psi_memory_monitor_info_list[MEM_LEVEL_CRITICAL],
317         },
318         [MEM_LEVEL_OOM] = {
319                 .handler = (epoll_event_handler)psi_memory_monitor_handler,
320                 .data = &g_psi_memory_monitor_info_list[MEM_LEVEL_OOM],
321         }
322 };
323
324 static int register_psi_events(void)
325 {
326         int mem_level = -1;
327
328         g_psi_monitor_epoll_fd = epoll_create(1);
329         if (g_psi_monitor_epoll_fd < 0) {
330                 _E("Failed to create epoll fd: %d", -errno);
331                 return RESOURCED_ERROR_FAIL;
332         }
333
334         for (mem_level = MEM_LEVEL_HIGH; mem_level < MEM_LEVEL_MAX; ++mem_level) {
335                 if (register_psi_event_epoll(&g_psi_event_datas[mem_level]) < 0)
336                         break;
337         }
338
339         if (mem_level < MEM_LEVEL_MAX) {
340                 for (mem_level = mem_level - 1; mem_level >= MEM_LEVEL_HIGH; --mem_level)
341                         unregister_psi_event_epoll(&g_psi_event_datas[mem_level]);
342                 close(g_psi_monitor_epoll_fd);
343                 g_psi_monitor_epoll_fd = -1;
344                 return RESOURCED_ERROR_FAIL;
345         }
346
347         return RESOURCED_ERROR_NONE;
348 }
349
350 static void unregister_psi_events(void)
351 {
352         for (int mem_level = MEM_LEVEL_HIGH; mem_level < MEM_LEVEL_MAX; ++mem_level)
353                 unregister_psi_event_epoll(&g_psi_event_datas[mem_level]);
354         close(g_psi_monitor_epoll_fd);
355         g_psi_monitor_epoll_fd = -1;
356 }
357
358 static int lowmem_monitor_psi_initialize(void *data)
359 {
360         /**
361          * FIXME: Disable PSI monitoring feature temporarily.
362          * Enable it when the PSI monitoring is ready to apply.
363          */
364         return RESOURCED_ERROR_NONE;
365
366         if (register_psi_events() != RESOURCED_ERROR_NONE) {
367                 _E("Failed to register psi fds to epoll fd");
368                 return RESOURCED_ERROR_FAIL;
369         }
370
371         if (create_psi_monitor_thread() != RESOURCED_ERROR_NONE) {
372                 _E("Failed to create psi monitor thread");
373                 unregister_psi_events();
374                 return RESOURCED_ERROR_FAIL;
375         }
376
377         return RESOURCED_ERROR_NONE;
378 }
379
380 static int lowmem_monitor_psi_finalize(void *data)
381 {
382         /**
383          * FIXME: Disable PSI monitoring feature temporarily.
384          * Enable it when the PSI monitoring is ready to apply.
385          */
386         return RESOURCED_ERROR_NONE;
387
388         destroy_psi_monitor_thread();
389         unregister_psi_events();
390
391         return RESOURCED_ERROR_NONE;
392 }
393
394 static struct module_ops g_lowmem_monitor_psi_modules_ops = {
395         .priority       = MODULE_PRIORITY_INITIAL,
396         .name           = "lowmem-monitor-psi",
397         .init           = lowmem_monitor_psi_initialize,
398         .exit           = lowmem_monitor_psi_finalize,
399 };
400
401 MODULE_REGISTER(&g_lowmem_monitor_psi_modules_ops)