4 * Copyright (c) 2014 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.
27 #include "module-data.h"
28 #include "edbus-handler.h"
29 #include "swap-common.h"
31 #include "proc-process.h"
33 #include <resourced.h>
39 #include <sys/types.h>
42 #include <sys/sysinfo.h>
44 #include <sys/resource.h>
46 #define MAX_SWAP_VICTIMS 16
48 #define MEMCG_PATH "/sys/fs/cgroup/memory"
49 #define SWAPCG_PATH MEMCG_PATH"/swap"
50 #define SWAPCG_PROCS SWAPCG_PATH"/cgroup.procs"
51 #define SWAPCG_USAGE SWAPCG_PATH"/memory.usage_in_bytes"
52 #define SWAPCG_RECLAIM SWAPCG_PATH"/memory.force_reclaim"
54 #define SWAP_ON_EXEC_PATH "/sbin/swapon"
55 #define SWAP_OFF_EXEC_PATH "/sbin/swapoff"
57 #define SIGNAL_NAME_SWAP_TYPE "SwapType"
58 #define SIGNAL_NAME_SWAP_START_PID "SwapStartPid"
60 #define SWAPFILE_NAME "/dev/zram0"
62 #define SWAP_PATH_MAX 100
64 #define MBtoB(x) (x<<20)
65 #define MBtoPage(x) (x<<8)
67 #define BtoMB(x) ((x) >> 20)
68 #define BtoPAGE(x) ((x) >> 12)
70 #define SWAP_TIMER_INTERVAL 0.5
71 #define SWAP_PRIORITY 20
73 #define SWAP_COUNT_MAX 5
75 struct swap_data_type {
76 enum swap_status_type status_type;
81 static pthread_mutex_t swap_mutex;
82 static pthread_cond_t swap_cond;
83 static Ecore_Timer *swap_timer = NULL;
85 static const struct module_ops swap_modules_ops;
86 static const struct module_ops *swap_ops;
88 pid_t swap_victims[MAX_SWAP_VICTIMS];
90 static int swap_set_candidate_pid(void *data)
92 unsigned long *args = data;
94 int *pid_array = (int*)args[0];
95 int count = (int)args[1];
97 memset(swap_victims, 0, sizeof(int)*MAX_SWAP_VICTIMS);
99 for (i = 0; i < count; i++)
100 swap_victims[i] = pid_array[i];
102 return RESOURCED_ERROR_NONE;
105 static pid_t swap_get_candidate_pid(void)
110 for (i = 0; i < MAX_SWAP_VICTIMS; i++)
111 if(swap_victims[i]) {
112 pid = swap_victims[i];
119 static int swap_get_swap_type(void)
121 struct shared_modules_data *modules_data = get_shared_modules_data();
123 ret_value_msg_if(modules_data == NULL, RESOURCED_ERROR_FAIL,
124 "Invalid shared modules data\n");
125 return modules_data->swap_data.swaptype;
128 static void swap_set_swap_type(int type)
130 struct shared_modules_data *modules_data = get_shared_modules_data();
132 ret_msg_if(modules_data == NULL,
133 "Invalid shared modules data\n");
134 modules_data->swap_data.swaptype = type;
137 static int swap_check_swap_pid(int pid)
139 char buf[SWAP_PATH_MAX] = {0,};
144 f = fopen(SWAPCG_PROCS, "r");
146 _E("%s open failed", SWAPCG_PROCS);
147 return RESOURCED_ERROR_FAIL;
150 while (fgets(buf, SWAP_PATH_MAX, f) != NULL) {
152 if (swappid == pid) {
161 static int swap_check_swap_cgroup(void)
163 char buf[SWAP_PATH_MAX] = {0,};
164 int ret = SWAP_FALSE;
167 f = fopen(SWAPCG_PROCS, "r");
169 _E("%s open failed", SWAPCG_PROCS);
170 return RESOURCED_ERROR_FAIL;
172 while (fgets(buf, SWAP_PATH_MAX, f) != NULL) {
180 static int swap_thread_do(FILE *procs, FILE *usage_in_bytes, FILE *force_reclaim)
182 char buf[SWAP_PATH_MAX] = {0,};
183 char appname[SWAP_PATH_MAX];
187 unsigned long usage, nr_to_reclaim;
189 /* check swap cgroup count */
190 while (fgets(buf, SWAP_PATH_MAX, procs) != NULL) {
198 if (proc_get_oom_score_adj(tpid, &toom) < 0) {
199 _D("pid(%d) was already terminated", tpid);
203 if (toom < OOMADJ_BACKGRD_UNLOCKED)
206 ret = proc_get_cmdline(tpid, appname);
207 if (ret == RESOURCED_ERROR_FAIL)
215 /* swap cgroup count is MAX, kill 1 process */
216 if (swap_cg_cnt >= SWAP_COUNT_MAX) {
218 _E("we killed %d (%s)", pid, appname);
221 /* cacluate reclaim size by usage and swap cgroup count */
222 if (fgets(buf, 32, usage_in_bytes) == NULL)
223 return RESOURCED_ERROR_FAIL;
225 usage = (unsigned long)atol(buf);
227 nr_to_reclaim = BtoPAGE(usage) >> ((swap_cg_cnt >> 1) + 1);
229 /* set reclaim size */
230 size = sprintf(buf, "%lu", nr_to_reclaim);
231 if (fwrite(buf, 1, size, force_reclaim) != size)
232 _E("fwrite %s\n", buf);
234 return RESOURCED_ERROR_NONE;
237 static void *swap_thread_main(void * data)
240 FILE *usage_in_bytes = NULL;
241 FILE *force_reclaim = NULL;
243 setpriority(PRIO_PROCESS, 0, SWAP_PRIORITY);
246 procs = fopen(SWAPCG_PROCS, "r");
248 _E("%s open failed", SWAPCG_PROCS);
253 if (usage_in_bytes == NULL) {
254 usage_in_bytes = fopen(SWAPCG_USAGE, "r");
255 if (usage_in_bytes == NULL) {
256 _E("%s open failed", SWAPCG_USAGE);
262 if (force_reclaim == NULL) {
263 force_reclaim = fopen(SWAPCG_RECLAIM, "w");
264 if (force_reclaim == NULL) {
265 _E("%s open failed", SWAPCG_RECLAIM);
267 fclose(usage_in_bytes);
273 pthread_mutex_lock(&swap_mutex);
274 pthread_cond_wait(&swap_cond, &swap_mutex);
277 * when signalled by main thread, it starts
280 _I("swap thread conditional signal received");
282 fseek(procs, 0, SEEK_SET);
283 fseek(usage_in_bytes, 0, SEEK_SET);
284 fseek(force_reclaim, 0, SEEK_SET);
286 _D("swap_thread_do start");
287 swap_thread_do(procs, usage_in_bytes, force_reclaim);
288 _D("swap_thread_do end");
289 pthread_mutex_unlock(&swap_mutex);
295 fclose(usage_in_bytes);
297 fclose(force_reclaim);
302 static Eina_Bool swap_send_signal(void *data)
306 _D("swap timer callback function start");
308 /* signal to swap_start to start swap */
309 ret = pthread_mutex_trylock(&swap_mutex);
312 _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
314 pthread_cond_signal(&swap_cond);
315 _I("send signal to swap thread");
316 pthread_mutex_unlock(&swap_mutex);
319 _D("swap timer delete");
321 ecore_timer_del(swap_timer);
324 return ECORE_CALLBACK_CANCEL;
327 static int swap_start(void *data)
329 if (swap_timer == NULL) {
330 _D("swap timer start");
332 ecore_timer_add(SWAP_TIMER_INTERVAL, swap_send_signal, (void *)NULL);
335 return RESOURCED_ERROR_NONE;
338 static int swap_thread_create(void)
340 int ret = RESOURCED_ERROR_NONE;
343 pthread_mutex_init(&swap_mutex, NULL);
344 pthread_cond_init(&swap_cond, NULL);
346 ret = pthread_create(&pth, NULL, &swap_thread_main, (void*)NULL);
348 _E("pthread creation for swap_thread failed\n");
357 static int get_swap_status(void)
362 static pid_t swap_on(void)
367 execl(SWAP_ON_EXEC_PATH, SWAP_ON_EXEC_PATH, "-d", SWAPFILE_NAME, (char *)NULL);
374 static pid_t swap_off(void)
379 execl(SWAP_OFF_EXEC_PATH, SWAP_OFF_EXEC_PATH, SWAPFILE_NAME, (char *)NULL);
386 static int restart_swap(void *data)
394 return RESOURCED_ERROR_NONE;
400 return RESOURCED_ERROR_FAIL;
401 } else if (pid == 0) {
403 ret = waitpid(pid, &status, 0);
405 _E("Error waiting for swap_off child process (PID: %d, status: %d)", (int)pid, status);
412 return RESOURCED_ERROR_NONE;
415 static int swap_move_swap_cgroup(void *data)
420 char buf[SWAP_PATH_MAX] = {0,};
422 f = fopen(SWAPCG_PROCS, "w");
424 _E("Fail to %s file open", SWAPCG_PROCS);
425 return RESOURCED_ERROR_FAIL;
427 size = sprintf(buf, "%d", *args);
428 if (fwrite(buf, size, 1, f) != 1)
429 _E("fwrite cgroup tasks to swap cgroup failed : %d\n", *args);
432 return RESOURCED_ERROR_NONE;
435 static void swap_start_pid_edbus_signal_handler(void *data, DBusMessage *msg)
441 ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_SWAP, SIGNAL_NAME_SWAP_START_PID);
443 _D("there is no swap type signal");
447 dbus_error_init(&err);
449 if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID) == 0) {
450 _D("there is no message");
454 _I("swap cgroup entered : pid : %d", (int)pid);
455 swap_move_swap_cgroup(&pid);
457 if (get_swap_status() == SWAP_OFF)
462 static void swap_type_edbus_signal_handler(void *data, DBusMessage *msg)
467 if (dbus_message_is_signal(msg, RESOURCED_INTERFACE_SWAP, SIGNAL_NAME_SWAP_TYPE) == 0) {
468 _D("there is no swap type signal");
472 dbus_error_init(&err);
474 if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &type, DBUS_TYPE_INVALID) == 0) {
475 _D("there is no message");
481 if (swap_get_swap_type() != SWAP_OFF) {
484 swap_set_swap_type(type);
488 if (swap_get_swap_type() != SWAP_ON) {
490 swap_set_swap_type(type);
494 _D("It is not valid swap type : %d", type);
499 static DBusMessage *edbus_getswaptype(E_DBus_Object *obj, DBusMessage *msg)
501 DBusMessageIter iter;
505 state = swap_get_swap_type();
507 reply = dbus_message_new_method_return(msg);
508 dbus_message_iter_init_append(reply, &iter);
509 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &state);
514 static struct edbus_method edbus_methods[] = {
515 { "GetSwapType", NULL, "i", edbus_getswaptype },
516 /* Add methods here */
519 static void swap_dbus_init(void)
523 register_edbus_signal_handler(RESOURCED_PATH_SWAP, RESOURCED_INTERFACE_SWAP,
524 SIGNAL_NAME_SWAP_TYPE,
525 (void *)swap_type_edbus_signal_handler);
526 register_edbus_signal_handler(RESOURCED_PATH_SWAP, RESOURCED_INTERFACE_SWAP,
527 SIGNAL_NAME_SWAP_START_PID,
528 (void *)swap_start_pid_edbus_signal_handler);
530 ret = edbus_add_methods(RESOURCED_PATH_SWAP, edbus_methods,
531 ARRAY_SIZE(edbus_methods));
533 ret_msg_if(ret != RESOURCED_ERROR_NONE,
534 "DBus method registration for %s is failed",
535 RESOURCED_PATH_SWAP);
538 static int swap_init(void)
542 ret = swap_thread_create();
544 _E("swap thread create failed");
548 _I("swap_init : %d", swap_get_swap_type());
555 static int swap_check_node(void)
558 FILE *usage_in_bytes = NULL;
559 FILE *force_reclaim = NULL;
561 procs = fopen(SWAPCG_PROCS, "r");
563 _E("%s open failed", SWAPCG_PROCS);
564 return RESOURCED_ERROR_NO_DATA;
569 usage_in_bytes = fopen(SWAPCG_USAGE, "r");
570 if (usage_in_bytes == NULL) {
571 _E("%s open failed", SWAPCG_USAGE);
572 return RESOURCED_ERROR_NO_DATA;
575 fclose(usage_in_bytes);
577 force_reclaim = fopen(SWAPCG_RECLAIM, "w");
578 if (force_reclaim == NULL) {
579 _E("%s open failed", SWAPCG_RECLAIM);
580 return RESOURCED_ERROR_NO_DATA;
583 fclose(force_reclaim);
585 return RESOURCED_ERROR_NONE;
588 static int resourced_swap_check_runtime_support(void *data)
590 return swap_check_node();
593 static int resourced_swap_status(void *data)
595 int ret = RESOURCED_ERROR_NONE;
596 struct swap_data_type *s_data;
598 s_data = (struct swap_data_type *)data;
599 switch(s_data->status_type) {
601 ret = swap_get_swap_type();
603 case SWAP_GET_CANDIDATE_PID:
604 ret = swap_get_candidate_pid();
606 case SWAP_GET_STATUS:
607 ret = get_swap_status();
611 ret = swap_check_swap_pid((int)s_data->args[0]);
613 ret = RESOURCED_ERROR_FAIL;
615 case SWAP_CHECK_CGROUP:
616 ret = swap_check_swap_cgroup();
622 static int resourced_swap_init(void *data)
624 struct modules_arg *marg = (struct modules_arg *)data;
625 struct daemon_opts *dopt = marg->opts;
627 swap_ops = &swap_modules_ops;
629 if (dopt->enable_swap)
630 swap_set_swap_type(dopt->enable_swap);
632 register_notifier(RESOURCED_NOTIFIER_SWAP_SET_CANDIDATE_PID, swap_set_candidate_pid);
633 register_notifier(RESOURCED_NOTIFIER_SWAP_START, swap_start);
634 register_notifier(RESOURCED_NOTIFIER_SWAP_RESTART, restart_swap);
635 register_notifier(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, swap_move_swap_cgroup);
640 static int resourced_swap_finalize(void *data)
642 unregister_notifier(RESOURCED_NOTIFIER_SWAP_SET_CANDIDATE_PID, swap_set_candidate_pid);
643 unregister_notifier(RESOURCED_NOTIFIER_SWAP_START, swap_start);
644 unregister_notifier(RESOURCED_NOTIFIER_SWAP_RESTART, restart_swap);
645 unregister_notifier(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, swap_move_swap_cgroup);
647 return RESOURCED_ERROR_NONE;
650 int swap_status(enum swap_status_type type, unsigned long *args)
652 struct swap_data_type s_data;
655 return RESOURCED_ERROR_NONE;
657 s_data.status_type = type;
659 return swap_ops->status(&s_data);
662 static const struct module_ops swap_modules_ops = {
663 .priority = MODULE_PRIORITY_NORMAL,
665 .init = resourced_swap_init,
666 .exit = resourced_swap_finalize,
667 .check_runtime_support = resourced_swap_check_runtime_support,
668 .status = resourced_swap_status,
671 MODULE_REGISTER(&swap_modules_ops)