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 express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
20 * @file lowmem-monitor-psi.c
21 * @desc Provides monitor functionalities to detect lowmem state using PSI
27 #include <sys/epoll.h>
28 #include <sys/eventfd.h>
36 #define EPOLL_LISTENER_POLLING_PERIOD_SECONDS 3
37 #define EPOLL_LISTENER_MAX_EVENTS 100
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"
45 struct psi_memory_monitor_info {
47 const char *mem_level_str;
54 typedef void *(*epoll_event_handler)(void *data);
56 struct epoll_event_data {
57 epoll_event_handler handler;
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;
65 static void assert_mem_level(int mem_level)
69 case MEM_LEVEL_MEDIUM:
71 case MEM_LEVEL_CRITICAL:
75 assert("Invalid memory level" && 0);
79 static void *psi_memory_monitor_handler(struct psi_memory_monitor_info *info)
81 assert_mem_level(info->mem_level);
83 lowmem_trigger_memory_state_action(info->mem_level);
88 static void *psi_monitor_thread_destroy_handler(int *efd)
91 eventfd_read(*efd, &dummy);
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,
101 static int register_psi_event_epoll(struct epoll_event_data *event_data)
103 char trigger_description[BUFF_MAX] = { 0 };
104 int trigger_description_len = -1;
107 struct psi_memory_monitor_info *info = NULL;
108 struct epoll_event event;
110 assert(g_psi_monitor_epoll_fd >= 0);
113 info = event_data->data;
115 assert_mem_level(info->mem_level);
117 fd = open(PSI_MEMORY_PATH, O_RDWR | O_NONBLOCK);
119 _E("Failed to open psi node: path=%s, errno=%d",
120 PSI_MEMORY_PATH, errno);
125 event.events = EPOLLPRI;
126 event.data.ptr = (void *)event_data;
128 trigger_description_len = snprintf(trigger_description, BUFF_MAX,
133 if (trigger_description_len < 0) {
134 _E("Failed to write PSI trigger description: returned value=%d",
135 trigger_description_len);
138 if (trigger_description_len >= BUFF_MAX) {
139 _E("Failed to write PSI trigger description: buffer is not enough");
143 ret = write(fd, trigger_description, trigger_description_len + 1);
145 _E("Failed to write PSI trigger description(\"%s\"): errno=%d",
146 trigger_description, errno);
150 ret = epoll_ctl(g_psi_monitor_epoll_fd, EPOLL_CTL_ADD, fd, &event);
152 _E("Failed to add epoll: errno=%d", errno);
156 _I("PSI event registered(%s %d %d) as %s mem_level", info->psi_type,
159 info->mem_level_str);
167 _E("Failed to register psi event: mem_level=%s", info->mem_level_str);
169 return RESOURCED_ERROR_FAIL;
172 static void unregister_psi_event_epoll(struct epoll_event_data *event_data)
174 struct psi_memory_monitor_info *info = NULL;
178 info = event_data->data;
180 assert_mem_level(info->mem_level);
185 epoll_ctl(g_psi_monitor_epoll_fd, EPOLL_CTL_DEL, info->fd, NULL);
190 static void *psi_monitor_thread_worker(void *data)
192 struct epoll_event events[EPOLL_LISTENER_MAX_EVENTS];
196 events_num = epoll_wait(g_psi_monitor_epoll_fd, events, EPOLL_LISTENER_MAX_EVENTS, 0);
198 for (int i = 0; i < events_num; ++i) {
199 if (events[i].events & (EPOLLERR | EPOLLHUP))
202 struct epoll_event_data *event_data = events[i].data.ptr;
203 event_data->handler(event_data->data);
206 sleep(EPOLL_LISTENER_POLLING_PERIOD_SECONDS);
212 static int create_psi_monitor_thread(void)
215 struct epoll_event event;
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;
222 g_psi_monitor_thread_destroy_event_data.data =
223 (void *)(&g_psi_monitor_thread_destroy_event_fd);
225 event.events = EPOLLIN;
226 event.data.ptr = (void *)(&g_psi_monitor_thread_destroy_event_data);
228 ret = epoll_ctl(g_psi_monitor_epoll_fd, EPOLL_CTL_ADD, g_psi_monitor_thread_destroy_event_fd, &event);
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;
236 ret = pthread_create(&g_psi_monitor_thread, NULL, psi_monitor_thread_worker, NULL);
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;
245 return RESOURCED_ERROR_NONE;
248 static void destroy_psi_monitor_thread(void)
250 eventfd_write(g_psi_monitor_thread_destroy_event_fd, 1);
251 pthread_join(g_psi_monitor_thread, NULL);
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;
258 static struct psi_memory_monitor_info g_psi_memory_monitor_info_list[MEM_LEVEL_MAX] = {
260 .mem_level = MEM_LEVEL_HIGH,
261 .mem_level_str = "high",
262 .psi_type = PSI_TYPE_SOME,
264 .window_us = 1000000,
267 [MEM_LEVEL_MEDIUM] = {
268 .mem_level = MEM_LEVEL_MEDIUM,
269 .mem_level_str = "medium",
270 .psi_type = PSI_TYPE_SOME,
272 .window_us = 1000000,
276 .mem_level = MEM_LEVEL_LOW,
277 .mem_level_str = "low",
278 .psi_type = PSI_TYPE_SOME,
280 .window_us = 1000000,
283 [MEM_LEVEL_CRITICAL] = {
284 .mem_level = MEM_LEVEL_CRITICAL,
285 .mem_level_str = "critical",
286 .psi_type = PSI_TYPE_FULL,
288 .window_us = 1000000,
292 .mem_level = MEM_LEVEL_OOM,
293 .mem_level_str = "oom",
294 .psi_type = PSI_TYPE_FULL,
296 .window_us = 1000000,
301 static struct epoll_event_data g_psi_event_datas[MEM_LEVEL_MAX] = {
303 .handler = (epoll_event_handler)psi_memory_monitor_handler,
304 .data = &g_psi_memory_monitor_info_list[MEM_LEVEL_HIGH],
306 [MEM_LEVEL_MEDIUM] = {
307 .handler = (epoll_event_handler)psi_memory_monitor_handler,
308 .data = &g_psi_memory_monitor_info_list[MEM_LEVEL_MEDIUM],
311 .handler = (epoll_event_handler)psi_memory_monitor_handler,
312 .data = &g_psi_memory_monitor_info_list[MEM_LEVEL_LOW],
314 [MEM_LEVEL_CRITICAL] = {
315 .handler = (epoll_event_handler)psi_memory_monitor_handler,
316 .data = &g_psi_memory_monitor_info_list[MEM_LEVEL_CRITICAL],
319 .handler = (epoll_event_handler)psi_memory_monitor_handler,
320 .data = &g_psi_memory_monitor_info_list[MEM_LEVEL_OOM],
324 static int register_psi_events(void)
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;
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)
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;
347 return RESOURCED_ERROR_NONE;
350 static void unregister_psi_events(void)
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;
358 static int lowmem_monitor_psi_initialize(void *data)
361 * FIXME: Disable PSI monitoring feature temporarily.
362 * Enable it when the PSI monitoring is ready to apply.
364 return RESOURCED_ERROR_NONE;
366 if (register_psi_events() != RESOURCED_ERROR_NONE) {
367 _E("Failed to register psi fds to epoll fd");
368 return RESOURCED_ERROR_FAIL;
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;
377 return RESOURCED_ERROR_NONE;
380 static int lowmem_monitor_psi_finalize(void *data)
383 * FIXME: Disable PSI monitoring feature temporarily.
384 * Enable it when the PSI monitoring is ready to apply.
386 return RESOURCED_ERROR_NONE;
388 destroy_psi_monitor_thread();
389 unregister_psi_events();
391 return RESOURCED_ERROR_NONE;
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,
401 MODULE_REGISTER(&g_lowmem_monitor_psi_modules_ops)