tizen 2.3 release
[kernel/api/system-resource.git] / src / swap / swap.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2014 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 /*
21  * @file swap.c
22  * @desc swap process
23  */
24
25 #include "macro.h"
26 #include "module.h"
27 #include "module-data.h"
28 #include "edbus-handler.h"
29 #include "swap-common.h"
30 #include "notifier.h"
31 #include "proc-process.h"
32
33 #include <resourced.h>
34 #include <trace.h>
35 #include <stdlib.h>
36 #include <sys/stat.h>
37 #include <sys/vfs.h>
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <pthread.h>
42 #include <sys/sysinfo.h>
43 #include <sys/time.h>
44 #include <sys/resource.h>
45
46 #define MAX_SWAP_VICTIMS                16
47
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"
53
54 #define SWAP_ON_EXEC_PATH               "/sbin/swapon"
55 #define SWAP_OFF_EXEC_PATH              "/sbin/swapoff"
56
57 #define SIGNAL_NAME_SWAP_TYPE           "SwapType"
58 #define SIGNAL_NAME_SWAP_START_PID      "SwapStartPid"
59
60 #define SWAPFILE_NAME                   "/dev/zram0"
61
62 #define SWAP_PATH_MAX                   100
63
64 #define MBtoB(x)                        (x<<20)
65 #define MBtoPage(x)                     (x<<8)
66
67 #define BtoMB(x)                        ((x) >> 20)
68 #define BtoPAGE(x)                      ((x) >> 12)
69
70 #define SWAP_TIMER_INTERVAL             0.5
71 #define SWAP_PRIORITY                   20
72
73 #define SWAP_COUNT_MAX                 5
74
75 struct swap_data_type {
76         enum swap_status_type    status_type;
77         unsigned long *args;
78 };
79
80 static int swapon;
81 static pthread_mutex_t swap_mutex;
82 static pthread_cond_t swap_cond;
83 static Ecore_Timer *swap_timer = NULL;
84
85 static const struct module_ops swap_modules_ops;
86 static const struct module_ops *swap_ops;
87
88 pid_t swap_victims[MAX_SWAP_VICTIMS];
89
90 static int swap_set_candidate_pid(void *data)
91 {
92         unsigned long *args = data;
93         int i;
94         int *pid_array = (int*)args[0];
95         int count = (int)args[1];
96
97         memset(swap_victims, 0, sizeof(int)*MAX_SWAP_VICTIMS);
98
99         for (i = 0; i < count; i++)
100                 swap_victims[i] = pid_array[i];
101
102         return RESOURCED_ERROR_NONE;
103 }
104
105 static pid_t swap_get_candidate_pid(void)
106 {
107         int i;
108         pid_t pid = 0;
109
110         for (i = 0; i < MAX_SWAP_VICTIMS; i++)
111                 if(swap_victims[i]) {
112                         pid = swap_victims[i];
113                         swap_victims[i] = 0;
114                         break;
115                 }
116         return pid;
117 }
118
119 static int swap_get_swap_type(void)
120 {
121         struct shared_modules_data *modules_data = get_shared_modules_data();
122
123         ret_value_msg_if(modules_data == NULL, RESOURCED_ERROR_FAIL,
124                          "Invalid shared modules data\n");
125         return modules_data->swap_data.swaptype;
126 }
127
128 static void swap_set_swap_type(int type)
129 {
130         struct shared_modules_data *modules_data = get_shared_modules_data();
131
132         ret_msg_if(modules_data == NULL,
133                          "Invalid shared modules data\n");
134         modules_data->swap_data.swaptype = type;
135 }
136
137 static int swap_check_swap_pid(int pid)
138 {
139         char buf[SWAP_PATH_MAX] = {0,};
140         int swappid;
141         int ret = 0;
142         FILE *f;
143
144         f = fopen(SWAPCG_PROCS, "r");
145         if (!f) {
146                 _E("%s open failed", SWAPCG_PROCS);
147                 return RESOURCED_ERROR_FAIL;
148         }
149
150         while (fgets(buf, SWAP_PATH_MAX, f) != NULL) {
151                 swappid = atoi(buf);
152                 if (swappid == pid) {
153                         ret = swappid;
154                         break;
155                 }
156         }
157         fclose(f);
158         return ret;
159 }
160
161 static int swap_check_swap_cgroup(void)
162 {
163         char buf[SWAP_PATH_MAX] = {0,};
164         int ret = SWAP_FALSE;
165         FILE *f;
166
167         f = fopen(SWAPCG_PROCS, "r");
168         if (!f) {
169                 _E("%s open failed", SWAPCG_PROCS);
170                 return RESOURCED_ERROR_FAIL;
171         }
172         while (fgets(buf, SWAP_PATH_MAX, f) != NULL) {
173                 ret = SWAP_TRUE;
174                 break;
175         }
176         fclose(f);
177         return ret;
178 }
179
180 static int swap_thread_do(FILE *procs, FILE *usage_in_bytes, FILE *force_reclaim)
181 {
182         char buf[SWAP_PATH_MAX] = {0,};
183         char appname[SWAP_PATH_MAX];
184         pid_t pid = 0;
185         int size;
186         int swap_cg_cnt=0;
187         unsigned long usage, nr_to_reclaim;
188
189         /* check swap cgroup count */
190         while (fgets(buf, SWAP_PATH_MAX, procs) != NULL) {
191                 pid_t tpid = 0;
192                 int toom = 0;
193                 int ret;
194
195                 if (!pid) {
196                         tpid = atoi(buf);
197
198                         if (proc_get_oom_score_adj(tpid, &toom) < 0) {
199                                _D("pid(%d) was already terminated", tpid);
200                                continue;
201                         }
202
203                         if (toom < OOMADJ_BACKGRD_UNLOCKED)
204                                continue;
205
206                         ret = proc_get_cmdline(tpid, appname);
207                         if (ret == RESOURCED_ERROR_FAIL)
208                                continue;
209
210                         pid = tpid;
211                 }
212                 swap_cg_cnt++;
213         }
214
215         /* swap cgroup count is MAX, kill 1 process */
216         if (swap_cg_cnt >= SWAP_COUNT_MAX) {
217                 kill(pid, SIGKILL);
218                 _E("we killed %d (%s)", pid, appname);
219         }
220
221         /* cacluate reclaim size by usage and swap cgroup count */
222         if (fgets(buf, 32, usage_in_bytes) == NULL)
223                 return RESOURCED_ERROR_FAIL;
224
225         usage = (unsigned long)atol(buf);
226
227         nr_to_reclaim = BtoPAGE(usage) >> ((swap_cg_cnt >> 1) + 1);
228
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);
233
234         return RESOURCED_ERROR_NONE;
235 }
236
237 static void *swap_thread_main(void * data)
238 {
239         FILE *procs = NULL;
240         FILE *usage_in_bytes = NULL;
241         FILE *force_reclaim = NULL;
242
243         setpriority(PRIO_PROCESS, 0, SWAP_PRIORITY);
244
245         if (procs == NULL) {
246                 procs = fopen(SWAPCG_PROCS, "r");
247                 if (procs == NULL) {
248                         _E("%s open failed", SWAPCG_PROCS);
249                         return NULL;
250                 }
251         }
252
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);
257                         fclose(procs);
258                         return NULL;
259                 }
260         }
261
262         if (force_reclaim == NULL) {
263                 force_reclaim = fopen(SWAPCG_RECLAIM, "w");
264                 if (force_reclaim == NULL) {
265                         _E("%s open failed", SWAPCG_RECLAIM);
266                         fclose(procs);
267                         fclose(usage_in_bytes);
268                         return NULL;
269                 }
270         }
271
272         while (1) {
273                 pthread_mutex_lock(&swap_mutex);
274                 pthread_cond_wait(&swap_cond, &swap_mutex);
275
276                 /*
277                  * when signalled by main thread, it starts
278                  * swap_thread_do().
279                  */
280                 _I("swap thread conditional signal received");
281
282                 fseek(procs, 0, SEEK_SET);
283                 fseek(usage_in_bytes, 0, SEEK_SET);
284                 fseek(force_reclaim, 0, SEEK_SET);
285
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);
290         }
291
292         if (procs)
293                 fclose(procs);
294         if (usage_in_bytes)
295                 fclose(usage_in_bytes);
296         if (force_reclaim)
297                 fclose(force_reclaim);
298
299         return NULL;
300 }
301
302 static Eina_Bool swap_send_signal(void *data)
303 {
304         int ret;
305
306         _D("swap timer callback function start");
307
308         /* signal to swap_start to start swap */
309         ret = pthread_mutex_trylock(&swap_mutex);
310
311         if (ret)
312                 _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
313         else {
314                 pthread_cond_signal(&swap_cond);
315                 _I("send signal to swap thread");
316                 pthread_mutex_unlock(&swap_mutex);
317         }
318
319         _D("swap timer delete");
320
321         ecore_timer_del(swap_timer);
322         swap_timer = NULL;
323
324         return ECORE_CALLBACK_CANCEL;
325 }
326
327 static int swap_start(void *data)
328 {
329         if (swap_timer == NULL) {
330                 _D("swap timer start");
331                 swap_timer =
332                         ecore_timer_add(SWAP_TIMER_INTERVAL, swap_send_signal, (void *)NULL);
333         }
334
335         return RESOURCED_ERROR_NONE;
336 }
337
338 static int swap_thread_create(void)
339 {
340         int ret = RESOURCED_ERROR_NONE;
341         pthread_t pth;
342
343         pthread_mutex_init(&swap_mutex, NULL);
344         pthread_cond_init(&swap_cond, NULL);
345
346         ret = pthread_create(&pth, NULL, &swap_thread_main, (void*)NULL);
347         if (ret) {
348                 _E("pthread creation for swap_thread failed\n");
349                 return ret;
350         } else {
351                 pthread_detach(pth);
352         }
353
354         return ret;
355 }
356
357 static int get_swap_status(void)
358 {
359         return swapon;
360 }
361
362 static pid_t swap_on(void)
363 {
364         pid_t pid = fork();
365
366         if (pid == 0) {
367                 execl(SWAP_ON_EXEC_PATH, SWAP_ON_EXEC_PATH, "-d", SWAPFILE_NAME, (char *)NULL);
368                 exit(0);
369         }
370         swapon = 1;
371         return pid;
372 }
373
374 static pid_t swap_off(void)
375 {
376         pid_t pid = fork();
377
378         if (pid == 0) {
379                 execl(SWAP_OFF_EXEC_PATH, SWAP_OFF_EXEC_PATH, SWAPFILE_NAME, (char *)NULL);
380                 exit(0);
381         }
382         swapon = 0;
383         return pid;
384 }
385
386 static int restart_swap(void *data)
387 {
388         int status;
389         pid_t pid;
390         pid_t ret;
391
392         if (!swapon) {
393                 swap_on();
394                 return RESOURCED_ERROR_NONE;
395         }
396
397         pid = fork();
398         if (pid == -1) {
399                 _E("fork() error");
400                 return RESOURCED_ERROR_FAIL;
401         } else if (pid == 0) {
402                 pid = swap_off();
403                 ret = waitpid(pid, &status, 0);
404                 if (ret == -1) {
405                         _E("Error waiting for swap_off child process (PID: %d, status: %d)", (int)pid, status);
406                 }
407                 swap_on();
408                 exit(0);
409         }
410         swapon = 1;
411
412         return RESOURCED_ERROR_NONE;
413 }
414
415 static int swap_move_swap_cgroup(void *data)
416 {
417         int *args = data;
418         int size;
419         FILE *f;
420         char buf[SWAP_PATH_MAX] = {0,};
421
422         f = fopen(SWAPCG_PROCS, "w");
423         if (!f) {
424                 _E("Fail to %s file open", SWAPCG_PROCS);
425                 return RESOURCED_ERROR_FAIL;
426         }
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);
430         fclose(f);
431
432         return RESOURCED_ERROR_NONE;
433 }
434
435 static void swap_start_pid_edbus_signal_handler(void *data, DBusMessage *msg)
436 {
437         DBusError err;
438         int ret;
439         pid_t pid;
440
441         ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_SWAP, SIGNAL_NAME_SWAP_START_PID);
442         if (ret == 0) {
443                 _D("there is no swap type signal");
444                 return;
445         }
446
447         dbus_error_init(&err);
448
449         if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID) == 0) {
450                 _D("there is no message");
451                 return;
452         }
453
454         _I("swap cgroup entered : pid : %d", (int)pid);
455         swap_move_swap_cgroup(&pid);
456
457         if (get_swap_status() == SWAP_OFF)
458                         restart_swap(NULL);
459         swap_start(NULL);
460 }
461
462 static void swap_type_edbus_signal_handler(void *data, DBusMessage *msg)
463 {
464         DBusError err;
465         int type;
466
467         if (dbus_message_is_signal(msg, RESOURCED_INTERFACE_SWAP, SIGNAL_NAME_SWAP_TYPE) == 0) {
468                 _D("there is no swap type signal");
469                 return;
470         }
471
472         dbus_error_init(&err);
473
474         if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &type, DBUS_TYPE_INVALID) == 0) {
475                 _D("there is no message");
476                 return;
477         }
478
479         switch (type) {
480         case 0:
481                 if (swap_get_swap_type() != SWAP_OFF) {
482                         if (swapon)
483                                 swap_off();
484                         swap_set_swap_type(type);
485                 }
486                 break;
487         case 1:
488                 if (swap_get_swap_type() != SWAP_ON) {
489                         restart_swap(NULL);
490                         swap_set_swap_type(type);
491                 }
492                 break;
493         default:
494                 _D("It is not valid swap type : %d", type);
495                 break;
496         }
497 }
498
499 static DBusMessage *edbus_getswaptype(E_DBus_Object *obj, DBusMessage *msg)
500 {
501         DBusMessageIter iter;
502         DBusMessage *reply;
503         int state;
504
505         state = swap_get_swap_type();
506
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);
510
511         return reply;
512 }
513
514 static struct edbus_method edbus_methods[] = {
515         { "GetSwapType",   NULL,   "i", edbus_getswaptype },
516         /* Add methods here */
517 };
518
519 static void swap_dbus_init(void)
520 {
521         resourced_ret_c ret;
522
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);
529
530         ret = edbus_add_methods(RESOURCED_PATH_SWAP, edbus_methods,
531                           ARRAY_SIZE(edbus_methods));
532
533         ret_msg_if(ret != RESOURCED_ERROR_NONE,
534                 "DBus method registration for %s is failed",
535                         RESOURCED_PATH_SWAP);
536 }
537
538 static int swap_init(void)
539 {
540         int ret;
541
542         ret = swap_thread_create();
543         if (ret) {
544                 _E("swap thread create failed");
545                 return ret;
546         }
547
548         _I("swap_init : %d", swap_get_swap_type());
549
550         swap_dbus_init();
551
552         return ret;
553 }
554
555 static int swap_check_node(void)
556 {
557         FILE *procs = NULL;
558         FILE *usage_in_bytes = NULL;
559         FILE *force_reclaim = NULL;
560
561         procs = fopen(SWAPCG_PROCS, "r");
562         if (procs == NULL) {
563                 _E("%s open failed", SWAPCG_PROCS);
564                 return RESOURCED_ERROR_NO_DATA;
565         }
566
567         fclose(procs);
568
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;
573         }
574
575         fclose(usage_in_bytes);
576
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;
581         }
582
583         fclose(force_reclaim);
584
585         return RESOURCED_ERROR_NONE;
586 }
587
588 static int resourced_swap_check_runtime_support(void *data)
589 {
590         return swap_check_node();
591 }
592
593 static int resourced_swap_status(void *data)
594 {
595         int ret = RESOURCED_ERROR_NONE;
596         struct swap_data_type *s_data;
597
598         s_data = (struct swap_data_type *)data;
599         switch(s_data->status_type) {
600         case SWAP_GET_TYPE:
601                 ret = swap_get_swap_type();
602                 break;
603         case SWAP_GET_CANDIDATE_PID:
604                 ret = swap_get_candidate_pid();
605                 break;
606         case SWAP_GET_STATUS:
607                 ret = get_swap_status();
608                 break;
609         case SWAP_CHECK_PID:
610                 if (s_data->args)
611                         ret = swap_check_swap_pid((int)s_data->args[0]);
612                 else
613                         ret = RESOURCED_ERROR_FAIL;
614                 break;
615         case SWAP_CHECK_CGROUP:
616                 ret = swap_check_swap_cgroup();
617                 break;
618         }
619         return ret;
620 }
621
622 static int resourced_swap_init(void *data)
623 {
624         struct modules_arg *marg = (struct modules_arg *)data;
625         struct daemon_opts *dopt = marg->opts;
626
627         swap_ops = &swap_modules_ops;
628
629         if (dopt->enable_swap)
630                 swap_set_swap_type(dopt->enable_swap);
631
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);
636
637         return swap_init();
638 }
639
640 static int resourced_swap_finalize(void *data)
641 {
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);
646
647         return RESOURCED_ERROR_NONE;
648 }
649
650 int swap_status(enum swap_status_type type, unsigned long *args)
651 {
652         struct swap_data_type s_data;
653
654         if (!swap_ops)
655                 return RESOURCED_ERROR_NONE;
656
657         s_data.status_type = type;
658         s_data.args = args;
659         return swap_ops->status(&s_data);
660 }
661
662 static const struct module_ops swap_modules_ops = {
663         .priority = MODULE_PRIORITY_NORMAL,
664         .name = "swap",
665         .init = resourced_swap_init,
666         .exit = resourced_swap_finalize,
667         .check_runtime_support = resourced_swap_check_runtime_support,
668         .status = resourced_swap_status,
669 };
670
671 MODULE_REGISTER(&swap_modules_ops)