tizen 2.3.1 release
[kernel/api/system-resource.git] / src / memory / lowmem-handler.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2012 - 2013 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_handler.c
21  *
22  * @desc lowmem handler using memcgroup
23  *
24  * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
25  *
26  */
27
28 #include <stdio.h>
29 #include <fcntl.h>
30 #include <assert.h>
31 #include <limits.h>
32 #include <vconf.h>
33 #include <unistd.h>
34 #include <time.h>
35 #include <limits.h>
36 #include <fcntl.h>
37 #include <dirent.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/shm.h>
42 #include <sys/eventfd.h>
43 #include <Ecore.h>
44
45 #include "trace.h"
46 #include "cgroup.h"
47 #include "proc-main.h"
48 #include "lowmem-handler.h"
49 #include "proc-process.h"
50 #include "swap-common.h"
51 #include "lowmem-common.h"
52 #include "resourced.h"
53 #include "macro.h"
54 #include "module.h"
55 #include "notifier.h"
56
57 enum {
58         MEMGC_OOM_NORMAL,
59         MEMGC_OOM_SOFTSWAP,
60         MEMGC_OOM_WARNING,
61         MEMGC_OOM_HIGH,
62         MEMGC_OOM_CRITICAL,
63 };
64
65 enum {
66         MEMGC_GROUP_FOREGROUND,
67         MEMGC_GROUP_BACKGROUND,
68 };
69
70 enum {
71         MEM_SWAP_OFF,
72         MEM_SWAP_ENABLE,
73         MEM_SWAP_DECREASE,
74         MEM_SWAP_INCREASE,
75 };
76
77 #define MEM_SOFTSWAP_ENABLE 1
78 #define MEMCG_GROUP_MAX         2
79
80 #define MEMINFO_PATH    "/proc/meminfo"
81 #define MEMCG_PATH              "/sys/fs/cgroup/memory"
82 #define VICTIM_TASK             "/sys/class/lowmemnotify/victims"
83 #define SET_LEAVE_THRESHOLD     "/sys/class/lowmemnotify/leave_threshold"
84 #define SET_CGROUP_LEAVE_THRESHOLD "/sys/class/lowmemnotify/cgroup_leave_threshold"
85 #define SET_THRESHOLD_LV1 "/sys/class/lowmemnotify/threshold_level1"
86 #define SET_THRESHOLD_LV2 "/sys/class/lowmemnotify/threshold_level2"
87 #define SET_THRESHOLD_RECLAIM "/sys/class/lowmemnotify/threshold_reclaim"
88
89 #define MEMPS_LOG_FILE  "/var/log/memps"
90
91 #define DELETE_SM               "sh -c "PREFIX"/bin/delete.sm"
92 #define MEMPS_EXEC_PATH         "usr/bin/memps"
93
94
95 #define _SYS_RES_CLEANUP        "RES_CLEANUP"
96
97 #define BtoMB(x)                ((x) / 1024 / 1024)
98 #define MBtoB(x)                (x<<20)
99
100 #define MEMCG_FOREGROUND_LIMIT_RATIO    0.6
101 #define MEMCG_BACKGROUND_LIMIT_RATIO    0.7
102
103 #define MEMCG_FOREGROUND_MIN_LIMIT      MBtoB(400)
104 #define MEMCG_BACKGROUND_MIN_LIMIT      UINT_MAX
105
106 /* threshold lv 1 : wakeup softswapd */
107 #define MEMCG_TRHES_SOFTSWAP_RATIO              0.75
108
109 /* threshold lv 2 : lowmem warning */
110 #define MEMCG_THRES_WARNING_RATIO               0.92
111
112 /* threshold lv 3 : victim kill */
113 #define MEMCG_THRES_OOM_RATIO                   0.96
114
115 /* leave threshold */
116 #define MEMCG_OOMLEAVE_RATIO                    0.88
117
118 #define MEMNOTIFY_NORMAL        0x0000
119 #define MEMNOTIFY_RECLAIM       0xecae
120 #define MEMNOTIFY_LOW           0xfaac
121 #define MEMNOTIFY_CRITICAL      0xdead
122
123 /* define threshold limit */
124 #define MAX_OOM_THRES                           0x04600000      /* 70M */
125 #define MIN_OOM_THRES                           0x03000000      /* 48M */
126 #define MAX_WARN_THRES                          0x07800000      /* 120M */
127 #define MAX_LEAVE_THRES                         0x0B400000      /* 180M */
128 #define MIN_OOM_WARN_GAP                        0x01400000      /* 30M */
129
130 #define MEM_THRESHOLD_RECLAIM                   300
131 #define MEM_THRESHOLD_LV1                       180
132 #define MEM_THRESHOLD_LV2                       160
133 #define MEM_LEAVE_THRESHOLD                     200
134 #define LOWMEM_PATH_MAX                         100
135
136 #define MAX_VICTIMS             30
137
138 static int lowmem_fd = -1;
139 static Ecore_Fd_Handler *lowmem_efd;
140 static int cur_mem_state = MEMNOTIFY_NORMAL;
141
142 static Ecore_Timer *oom_check_timer;
143 #define OOM_TIMER_INTERVAL      3
144 #define OOM_MULTIKILL_WAIT      (1000*1000)
145 #define OOM_CHECK_PROC_WAIT     (2000*1000)
146
147 unsigned int oom_delete_sm_time;
148
149 /* low memory action function */
150 static int memory_low_act(void *ad);
151 static int memory_oom_act(void *ad);
152 static int memory_normal_act(void *ad);
153 static int memory_reclaim_act(void *ad);
154
155
156 /* low memory action function for cgroup */
157 static int memory_cgroup_oom_act(int memcg_index);
158
159 static int lowmem_fd_start();
160 static int lowmem_fd_stop(int fd);
161
162 struct memcg_class {
163         unsigned int event_fd;
164         unsigned int min_limit;
165         float   limit_ratio;
166         unsigned int oomlevel;
167         unsigned int oomalert;
168         unsigned int oomleave;
169         char *cgroup_name;
170         unsigned int total_limit;
171         unsigned int thres_lv1;
172         unsigned int thres_lv2;
173         unsigned int thres_lv3;
174         unsigned int thres_leave;
175 };
176
177 struct lowmem_process_entry {
178         unsigned cur_mem_state;
179         unsigned new_mem_state;
180         int (*action) (void *);
181 };
182
183 static struct lowmem_process_entry lpe[] = {
184         {MEMNOTIFY_NORMAL,      MEMNOTIFY_RECLAIM,      memory_reclaim_act},
185         {MEMNOTIFY_NORMAL,      MEMNOTIFY_LOW,          memory_low_act},
186         {MEMNOTIFY_NORMAL,      MEMNOTIFY_CRITICAL,     memory_oom_act},
187         {MEMNOTIFY_RECLAIM,     MEMNOTIFY_LOW,          memory_low_act},
188         {MEMNOTIFY_RECLAIM,     MEMNOTIFY_CRITICAL,     memory_oom_act},
189         {MEMNOTIFY_LOW,         MEMNOTIFY_CRITICAL,     memory_oom_act},
190         {MEMNOTIFY_CRITICAL,    MEMNOTIFY_CRITICAL,     memory_oom_act},
191         {MEMNOTIFY_LOW,         MEMNOTIFY_RECLAIM,      memory_reclaim_act},
192         {MEMNOTIFY_LOW,         MEMNOTIFY_NORMAL,       memory_normal_act},
193         {MEMNOTIFY_CRITICAL,    MEMNOTIFY_NORMAL,       memory_normal_act},
194         {MEMNOTIFY_CRITICAL,    MEMNOTIFY_RECLAIM,      memory_reclaim_act},
195         {MEMNOTIFY_RECLAIM,     MEMNOTIFY_NORMAL,       memory_normal_act},
196 };
197
198 static struct memcg_class memcg_class[MEMCG_GROUP_MAX] = {
199         {0, MEMCG_FOREGROUND_MIN_LIMIT, MEMCG_FOREGROUND_LIMIT_RATIO, 0, 0, 0, "foreground",
200                 0, 0, 0, 0, 0},
201         {0, MEMCG_BACKGROUND_MIN_LIMIT, MEMCG_BACKGROUND_LIMIT_RATIO, 0, 0, 0, "background",
202                 0, 0, 0, 0, 0},
203 };
204
205 static const struct module_ops memory_modules_ops;
206 static const struct module_ops *lowmem_ops;
207
208 unsigned int get_available(void)
209 {
210         char buf[PATH_MAX];
211         FILE *fp;
212         char *idx;
213         unsigned int free = 0, cached = 0;
214         unsigned int available = 0;
215
216         fp = fopen(MEMINFO_PATH, "r");
217         if (!fp) {
218                 _E("%s open failed, %d", buf, fp);
219                 return available;
220         }
221
222         while (fgets(buf, PATH_MAX, fp) != NULL) {
223                 if ((idx = strstr(buf, "MemFree:"))) {
224                         idx += strlen("MemFree:");
225                         while (*idx < '0' || *idx > '9')
226                                 idx++;
227                         free = atoi(idx);
228                 } else if ((idx = strstr(buf, "MemAvailable:"))) {
229                         idx += strlen("MemAvailable:");
230                         while (*idx < '0' || *idx > '9')
231                                 idx++;
232                         available = atoi(idx);
233                         break;
234                 } else if((idx = strstr(buf, "Cached:"))) {
235                         idx += strlen("Cached:");
236                         while (*idx < '0' || *idx > '9')
237                                 idx++;
238                         cached = atoi(idx);
239                         break;
240                 }
241         }
242
243         if (available == 0)
244                 available = free + cached;
245         available >>= 10;
246         fclose(fp);
247
248         return available;
249 }
250
251 void lowmem_dynamic_process_killer(int type)
252 {
253         /* This function is not supported */
254 }
255
256 void change_memory_state(int state, int force)
257 {
258         int mem_state;
259
260         if (force) {
261                 mem_state = state;
262         } else {
263                 mem_state = cur_mem_state;
264                 _D("mem_state = %d", mem_state);
265         }
266
267         switch (mem_state) {
268         case MEMNOTIFY_NORMAL:
269                 memory_normal_act(NULL);
270                 break;
271         case MEMNOTIFY_RECLAIM:
272                 memory_reclaim_act(NULL);
273                 break;
274         case MEMNOTIFY_LOW:
275                 memory_low_act(NULL);
276                 break;
277         case MEMNOTIFY_CRITICAL:
278                 memory_oom_act(NULL);
279                 break;
280         default:
281                 assert(0);
282         }
283 }
284
285 static unsigned int _get_total_memory(void)
286 {
287         char buf[PATH_MAX];
288         FILE *fp;
289         char *idx;
290         unsigned int total = 0;
291
292         fp = fopen(MEMINFO_PATH, "r");
293
294         if (!fp)
295                 return total;
296
297         while (fgets(buf, PATH_MAX, fp) != NULL) {
298                 if ((idx = strstr(buf, "MemTotal:"))) {
299                         idx += strlen("MemTotal:");
300                         while (*idx < '0' || *idx > '9')
301                                 idx++;
302                         total = atoi(idx);
303                         total *= 1024;
304                         break;
305                 }
306         }
307         fclose(fp);
308         return total;
309 }
310
311 static void _calc_threshold(int type, int limit)
312 {
313         unsigned int val, check;
314
315         /* calculate theshold lv3 */
316         val = (unsigned int)(memcg_class[type].total_limit*
317                         (float)MEMCG_THRES_OOM_RATIO);
318
319         /* check MIN & MAX value about threshold lv3*/
320         if (limit - val > MAX_OOM_THRES)
321                 val = (unsigned int)(limit - MAX_OOM_THRES);
322         else if (limit - val < MIN_OOM_THRES)
323                 val = (unsigned int)(limit - MIN_OOM_THRES);
324
325         /* set threshold lv3 */
326         memcg_class[type].thres_lv3 = val;
327
328         /* calculate threshold lv2 */
329         val = (unsigned int)(memcg_class[type].total_limit*
330                         (float)MEMCG_THRES_WARNING_RATIO);
331
332         check = memcg_class[type].thres_lv3;
333
334         /* check MIN & MAX value about threshold lv2*/
335         if (check - val < MIN_OOM_WARN_GAP)
336                 val = (unsigned int)(check - MIN_OOM_WARN_GAP);
337         else if (limit - val > MAX_WARN_THRES)
338                 val = (unsigned int)(limit - MAX_WARN_THRES);
339
340         /* set threshold lv2 */
341         memcg_class[type].thres_lv2 = val;
342
343         /* calculate threshold lv1 */
344         val = (unsigned int)(memcg_class[type].total_limit*
345                         (float)MEMCG_TRHES_SOFTSWAP_RATIO);
346
347         /* check MIN value about threshold lv1*/
348         check = memcg_class[type].thres_lv2;
349
350         if (check - val < MIN_OOM_WARN_GAP)
351                 val = (unsigned int)(check - MIN_OOM_WARN_GAP);
352
353         memcg_class[type].thres_lv1 = val;
354
355         /* set leave threshold */
356         val = (unsigned int)(memcg_class[type].total_limit*
357                         (float)MEMCG_OOMLEAVE_RATIO);
358
359         check = memcg_class[type].thres_lv1;
360
361         /* check MIN & MAX value about leave threshold */
362         if (check - val < MIN_OOM_WARN_GAP)
363                 val = (unsigned int)(check - MIN_OOM_WARN_GAP);
364         else if (limit - val > MAX_LEAVE_THRES)
365                 val = (unsigned int)(limit - MAX_WARN_THRES);
366
367         memcg_class[type].oomleave = val;
368 }
369
370 static unsigned int get_mem_usage(int idx)
371 {
372         FILE *f;
373         char buf[LOWMEM_PATH_MAX] = {0,};
374         unsigned int usage;
375
376         snprintf(buf, sizeof(buf), "%s/%s/memory.usage_in_bytes",
377                         MEMCG_PATH, memcg_class[idx].cgroup_name);
378
379         f = fopen(buf, "r");
380         if (!f) {
381                 _E("%s open failed, %d", buf, f);
382                 return RESOURCED_ERROR_FAIL;
383         }
384         if (fgets(buf, 32, f) == NULL) {
385                 _E("fgets failed\n");
386                 fclose(f);
387                 return RESOURCED_ERROR_FAIL;
388         }
389         usage = atoi(buf);
390         fclose(f);
391
392         return usage;
393 }
394
395 static int get_current_oom(int idx)
396 {
397         FILE *f;
398         char buf[LOWMEM_PATH_MAX] = {0,};
399         char *oom;
400         unsigned int level;
401
402         snprintf(buf, sizeof(buf), "%s/%s/memory.oom_usr_control",
403                         MEMCG_PATH, memcg_class[idx].cgroup_name);
404
405         f = fopen(buf, "r");
406         if (!f) {
407                 _E("%s open failed, %d", buf, f);
408                 return RESOURCED_ERROR_FAIL;
409         }
410         if (fgets(buf, 32, f) == NULL) {
411                 _E("fgets failed\n");
412                 fclose(f);
413                 return RESOURCED_ERROR_FAIL;
414         }
415         oom = strstr(buf, "oom_usr_control");
416         oom += strlen("oom_usr_control");
417         while (*oom < '0' || *oom > '9')
418                 oom++;
419         level = atoi(oom);
420         fclose(f);
421         _D("get_current_oom : %d", level);
422         return level;
423 }
424
425 static int remove_shm(void)
426 {
427         int maxid, shmid, id;
428         struct shmid_ds shmseg;
429         struct shm_info shm_info;
430
431         maxid = shmctl(0, SHM_INFO, (struct shmid_ds *)(void *)&shm_info);
432         if (maxid < 0) {
433                 _E("shared mem error\n");
434                 return RESOURCED_ERROR_FAIL;
435         }
436
437         for (id = 0; id <= maxid; id++) {
438                 shmid = shmctl(id, SHM_STAT, &shmseg);
439                 if (shmid < 0)
440                         continue;
441                 if (shmseg.shm_nattch == 0) {
442                         _D("shared memory killer ==> %d killed\n",
443                                   shmid);
444                         shmctl(shmid, IPC_RMID, NULL);
445                 }
446         }
447         return 0;
448 }
449
450 static void print_mem_state(void)
451 {
452         unsigned int usage, i;
453
454         for (i = 0; i < MEMCG_GROUP_MAX; i++) {
455                 usage = get_mem_usage(i);
456                 _I("[MEM STATE] memcg : %s, usage %d oom level : %d",
457                                 memcg_class[i].cgroup_name, usage,
458                                 memcg_class[i].oomlevel);
459         }
460 }
461
462 static void make_memps_log(char *file, pid_t pid, char *victim_name)
463 {
464         time_t now;
465         struct tm *cur_tm;
466         char new_log[512];
467         static pid_t old_pid;
468
469         if (old_pid == pid)
470                 return;
471         old_pid = pid;
472
473         now = time(NULL);
474         cur_tm = (struct tm *)malloc(sizeof(struct tm));
475         if (cur_tm == NULL) {
476                 _E("Fail to memory allocation");
477                 return;
478         }
479
480         if (localtime_r(&now, cur_tm) == NULL) {
481                 _E("Fail to get localtime");
482                 free(cur_tm);
483                 return;
484         }
485
486         snprintf(new_log, sizeof(new_log),
487                  "%s_%s_%d_%.4d%.2d%.2d_%.2d%.2d%.2d.log", file, victim_name,
488                  pid, (1900 + cur_tm->tm_year), 1 + cur_tm->tm_mon,
489                  cur_tm->tm_mday, cur_tm->tm_hour, cur_tm->tm_min,
490                  cur_tm->tm_sec);
491
492         free(cur_tm);
493         if (fork() == 0) {
494                 execl(MEMPS_EXEC_PATH, MEMPS_EXEC_PATH, "-f", new_log, (char *)NULL);
495                 exit(0);
496         }
497 }
498
499 static int lowmem_check_current_state(int memcg_index,
500                 int total_size, int oom_usage)
501 {
502         unsigned int usage, oomleave, check = 0;
503
504         oomleave = memcg_class[memcg_index].oomleave;
505         usage = get_mem_usage(memcg_index);
506         if (usage < oomleave) {
507                 _D("%s : usage : %d, oomleave : %d",
508                                 __func__, usage, oomleave);
509                 check++;
510         }
511         if (oom_usage - total_size < oomleave) {
512                 _D("%s : oom_usage : %d, total size : %d, oomleave : %d",
513                                 __func__, oom_usage, total_size, oomleave);
514                 check++;
515         }
516         return check;
517 }
518
519 static int lowmem_get_victim_pid(int *pid_arry, unsigned int* pid_size)
520 {
521         int count, num_pid = 0;
522         FILE *f;
523         char buf[LOWMEM_PATH_MAX] = {0, };
524
525         f = fopen(VICTIM_TASK, "r");
526
527         if (!f) {
528                 _E("Fail to file open");
529                 return RESOURCED_ERROR_FAIL;
530         }
531
532         /* firstly, read victim count */
533         if (fgets(buf, 32, f) == NULL) {
534                 _E("victim list is empty");
535                 goto out;
536         }
537
538         count = atoi(buf);
539
540         if (count > MAX_VICTIMS) {
541                 _E("Victim count is wrong value");
542                 goto out;
543         }
544
545         while (fgets(buf, 32, f) != NULL) {
546                 pid_arry[num_pid] = atoi(buf);
547                 if (fgets(buf, 32, f) != NULL)
548                         pid_size[num_pid] = atoi(buf);
549                 else {
550                         _E("Victim size is needed\n");
551                         goto out;
552                 }
553                 num_pid++;
554         }
555
556         if (count != num_pid)
557                 _I("Number of pids is wrong\n");
558
559         fclose(f);
560         return num_pid;
561 out:
562         fclose(f);
563         return RESOURCED_ERROR_FAIL;
564
565
566 }
567
568 static int lowmem_set_cgroup_leave_threshold(unsigned int value)
569 {
570         FILE *f;
571         f = fopen(SET_CGROUP_LEAVE_THRESHOLD, "w");
572
573         if (!f) {
574                 _E("Fail to file open");
575                 return RESOURCED_ERROR_FAIL;
576         }
577         fprintf(f, "%d", value);
578         fclose(f);
579         return 0;
580 }
581
582 static int lowmem_set_threshold(void)
583 {
584         FILE *f;
585         unsigned int val, total;
586
587         f = fopen(SET_THRESHOLD_RECLAIM, "w");
588
589         if (!f) {
590                 _E("Fail to file open : current kernel can't support swap cgroup");
591                 return RESOURCED_ERROR_FAIL;
592         }
593
594         /* set threshold reclaim */
595         total = _get_total_memory();
596
597         /*
598          * check total memory because total memory is over 1GiB,
599          * we want to start reclaim under 300 MiB remained memory.
600          * But, we check condition 700MiB because reserved memory.
601          */
602         if (total > MBtoB(700))
603                 val = MEM_THRESHOLD_RECLAIM;
604         else
605                 val = MEM_THRESHOLD_RECLAIM >> 1;
606         fprintf(f, "%d", val);
607         fclose(f);
608
609         /* set threshold level1 */
610         f = fopen(SET_THRESHOLD_LV1, "w");
611
612         if (!f) {
613                 _E("Fail to file open");
614                 return RESOURCED_ERROR_FAIL;
615         }
616         fprintf(f, "%d", MEM_THRESHOLD_LV1);
617         fclose(f);
618
619         /* set threshold level2 */
620         f = fopen(SET_THRESHOLD_LV2, "w");
621
622         if (!f) {
623                 _E("Fail to file open");
624                 return RESOURCED_ERROR_FAIL;
625         }
626         fprintf(f, "%d", MEM_THRESHOLD_LV2);
627         fclose(f);
628
629         /* set leave threshold */
630         f = fopen(SET_LEAVE_THRESHOLD, "w");
631
632         if (!f) {
633                 _E("Fail to file open");
634                 return RESOURCED_ERROR_FAIL;
635         }
636         fprintf(f, "%d", MEM_LEAVE_THRESHOLD);
637         fclose(f);
638         return 0;
639 }
640
641 void *_lowmem_oom_killer_cb(void *data)
642 {
643         int pid, ret, oom_score_adj, count, i;
644         char appname[PROC_NAME_MAX];
645         char popupname[PROC_NAME_MAX];
646         int pid_array[MAX_VICTIMS];
647         unsigned int pid_size[MAX_VICTIMS];
648         unsigned int total_size = 0, forgrd_pid = 0, forgrd_size = 0;
649
650         /* get multiple victims from kernel */
651         count = lowmem_get_victim_pid((int *)pid_array,
652                                 (unsigned int *)pid_size);
653
654         if (count < 0) {
655                 _E("get victim was failed");
656                 return NULL;
657         }
658
659         /* kill all selected process */
660         for (i = 0; i < count; i++) {
661                 pid = pid_array[i];
662
663                 if (pid <= 0)
664                         continue;
665                 _D("oom total memory size : %d", total_size);
666                 ret = proc_get_cmdline(pid, appname);
667                 if (ret != 0) {
668                         _D("invalid pid(%d) was selected", pid);
669                         continue;
670                 }
671                 if (!strcmp("memps", appname)) {
672                         _E("memps(%d) was selected, skip it", pid);
673                         continue;
674                 }
675                 if (!strcmp("crash-worker", appname)) {
676                         _E("crash-worker(%d) was selected, skip it", pid);
677                         continue;
678                 }
679
680                 /* make memps log for killing application firstly */
681                 if (i == 0)
682                         make_memps_log(MEMPS_LOG_FILE, pid, appname);
683
684                 if (proc_get_oom_score_adj(pid, &oom_score_adj) < 0) {
685                         _D("pid(%d) was already terminated", pid);
686                         continue;
687                 }
688
689                 /* just continue if selected process moved to foreground */
690                 if (BtoMB(total_size) > MEM_LEAVE_THRESHOLD && oom_score_adj < OOMADJ_BACKGRD_UNLOCKED)
691                         continue;
692
693                 proc_remove_process_list(pid);
694                 kill(pid, SIGKILL);
695
696                 total_size += pid_size[i];
697                 _I("we killed, lowmem lv2 = %d (%s) oom = %d\n",
698                                 pid, appname, oom_score_adj);
699
700                 /* wait OOM_MULTIKILL_WAIT for removing a latency about killing proesss */
701                 if (BtoMB(total_size) > MEM_LEAVE_THRESHOLD && i%5==0)
702                         usleep(OOM_MULTIKILL_WAIT);
703
704                 if (oom_score_adj > OOMADJ_FOREGRD_UNLOCKED)
705                         continue;
706
707                 if (forgrd_size < pid_size[i]) {
708                         forgrd_pid = pid;
709                         forgrd_size = pid_size[i];
710                         strncpy(popupname, appname, PROC_NAME_MAX-1);
711                 }
712         }
713
714         if (forgrd_pid)
715                 make_memps_log(MEMPS_LOG_FILE, forgrd_pid, popupname);
716
717         return NULL;
718 }
719
720 int lowmem_oom_killer_cb(int memcg_idx, int flags)
721 {
722         int memcg_index = memcg_idx;
723         _lowmem_oom_killer_cb((void *)&memcg_index);
724         return 0;
725 }
726
727 static void lowmem_cgroup_oom_killer(int memcg_index)
728 {
729         int pid, ret, oom_score_adj, count, i;
730         char appname[PATH_MAX];
731         int pid_array[32];
732         unsigned int pid_size[32];
733         unsigned int total_size = 0, oom_usage = 0;
734
735         oom_usage = get_mem_usage(memcg_index);
736         /* get multiple victims from kernel */
737         count = lowmem_get_victim_pid((int *)pid_array,
738                                 (unsigned int *)pid_size);
739
740         if (count < 0) {
741                 _E("get victim was failed");
742                 return;
743         }
744
745         for (i = 0; i < count; i++) {
746                 pid = pid_array[i];
747
748                 if (pid <= 0)
749                         continue;
750                 _D("oom total memory size : %d", total_size);
751                 ret = proc_get_cmdline(pid, appname);
752                 if (ret != 0) {
753                         _E("invalid pid(%d) was selected", pid);
754                         continue;
755                 }
756                 if (!strcmp("memps", appname)) {
757                         _E("memps(%d) was selected, skip it", pid);
758                         continue;
759                 }
760                 if (!strcmp("crash-worker", appname)) {
761                         _E("crash-worker(%d) was selected, skip it", pid);
762                         continue;
763                 }
764                 if (proc_get_oom_score_adj(pid, &oom_score_adj) < 0) {
765                         _D("pid(%d) was already terminated", pid);
766                         continue;
767                 }
768
769                 /* check current memory status */
770                 if (lowmem_check_current_state(memcg_index, total_size,
771                                         oom_usage) > 0)
772                         return;
773
774                 /* make memps log for killing application firstly */
775                 if (i==0)
776                         make_memps_log(MEMPS_LOG_FILE, pid, appname);
777
778                 proc_remove_process_list(pid);
779                 kill(pid, SIGTERM);
780
781                 total_size += pid_size[i];
782                 _I("we killed, lowmem lv2 = %d (%s) oom = %d\n",
783                                 pid, appname, oom_score_adj);
784
785                 if (oom_score_adj > OOMADJ_FOREGRD_UNLOCKED)
786                         continue;
787
788                 if (i != 0)
789                         make_memps_log(MEMPS_LOG_FILE, pid, appname);
790         }
791 }
792
793 static char *convert_to_str(unsigned int mem_state)
794 {
795         char *tmp = NULL;
796         switch (mem_state) {
797         case MEMNOTIFY_NORMAL:
798         case MEMNOTIFY_RECLAIM:
799                 tmp = "mem normal";
800                 break;
801         case MEMNOTIFY_LOW:
802                 tmp = "mem low";
803                 break;
804         case MEMNOTIFY_CRITICAL:
805                 tmp = "mem critical";
806                 break;
807         default:
808                 assert(0);
809         }
810         return tmp;
811 }
812
813 static void print_lowmem_state(unsigned int mem_state)
814 {
815         _I("[LOW MEM STATE] %s ==> %s", convert_to_str(cur_mem_state),
816                 convert_to_str(mem_state));
817 }
818
819 static void lowmem_swap_memory(void)
820 {
821         pid_t pid;
822         int swap_type;
823
824         if (cur_mem_state == MEMNOTIFY_NORMAL)
825                 return;
826
827         swap_type = swap_status(SWAP_GET_TYPE, NULL);
828
829         if (swap_type == SWAP_ON) {
830                 while (1)
831                 {
832                         pid = (pid_t)swap_status(SWAP_GET_CANDIDATE_PID, NULL);
833                         if (!pid)
834                                 break;
835                         _I("swap cgroup entered : pid : %d", (int)pid);
836                         resourced_notify(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, (void*)&pid);
837                 }
838                 if (swap_status(SWAP_GET_STATUS, NULL) == SWAP_OFF)
839                         resourced_notify(RESOURCED_NOTIFIER_SWAP_RESTART, NULL);
840                 resourced_notify(RESOURCED_NOTIFIER_SWAP_START, NULL);
841         }
842 }
843
844 static int memory_reclaim_act(void *data)
845 {
846         int ret, status;
847         _I("[LOW MEM STATE] memory reclaim state");
848         ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status);
849         if (ret != 0) {
850                 _E("vconf get failed(VCONFKEY_SYSMAN_LOW_MEMORY)\n");
851                 return RESOURCED_ERROR_FAIL;
852         }
853         if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL)
854                 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
855                                   VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
856         else
857                 lowmem_swap_memory();
858
859         return 0;
860 }
861
862 static int memory_low_act(void *data)
863 {
864         _I("[LOW MEM STATE] memory low state");
865         print_mem_state();
866         remove_shm();
867
868         vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
869                       VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING);
870
871         return 0;
872 }
873
874 static int memory_oom_act(void *ad)
875 {
876         pthread_t pth;
877         int ret;
878
879         _I("[LOW MEM STATE] memory oom state");
880
881         print_mem_state();
882
883         ret = pthread_create(&pth, 0, _lowmem_oom_killer_cb, (void*)NULL);
884         if (ret < 0) {
885                 _E("pthread creation failed!, call directly!");
886                 _lowmem_oom_killer_cb((void*)NULL);
887         } else
888                 pthread_detach(pth);
889
890         vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
891                       VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING);
892         return 1;
893 }
894
895 static int memory_cgroup_oom_act(int memcg_index)
896 {
897         _I("[LOW MEM STATE] memory oom state");
898
899         print_mem_state();
900
901         lowmem_cgroup_oom_killer(memcg_index);
902
903         vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
904                       VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING);
905         return 1;
906 }
907
908 static int memory_normal_act(void *data)
909 {
910         _I("[LOW MEM STATE] memory normal state");
911         vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
912                       VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
913         return 0;
914 }
915
916 static int lowmem_process(unsigned int mem_state, void *ad)
917 {
918         int i;
919         for (i = 0; i < ARRAY_SIZE(lpe); i++) {
920                 if ((cur_mem_state == lpe[i].cur_mem_state)
921                                 && (mem_state == lpe[i].new_mem_state)) {
922                         if (oom_check_timer != NULL) {
923                                 ecore_timer_del(oom_check_timer);
924                                 oom_check_timer = NULL;
925                         }
926                         cur_mem_state = mem_state;
927                         lpe[i].action(ad);
928                         if (mem_state == MEMNOTIFY_CRITICAL)
929                                 oom_check_timer =
930                                         ecore_timer_add(OOM_TIMER_INTERVAL, (const void *)lpe[i].action, ad);
931                         return 0;
932                 }
933         }
934         cur_mem_state = mem_state;
935         return 0;
936 }
937
938 static unsigned int lowmem_eventfd_read(int fd)
939 {
940         unsigned int ret;
941         uint64_t dummy_state;
942         ret = read(fd, &dummy_state, sizeof(dummy_state));
943         return ret;
944 }
945
946 static Eina_Bool lowmem_cb(void *data, Ecore_Fd_Handler *fd_handler)
947 {
948         int fd, i, currentoom;
949         struct ss_main_data *ad = (struct ss_main_data *)data;
950
951         if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) {
952                 _E("ecore_main_fd_handler_active_get error , return\n");
953                 return ECORE_CALLBACK_CANCEL;
954         }
955
956         fd = ecore_main_fd_handler_fd_get(fd_handler);
957         if (fd < 0) {
958                 _E("ecore_main_fd_handler_fd_get error , return\n");
959                 return ECORE_CALLBACK_CANCEL;
960         }
961         lowmem_eventfd_read(fd);
962
963         for (i = 0; i < MEMCG_GROUP_MAX; i++) {
964                 currentoom = get_current_oom(i);
965                 if (currentoom == MEMGC_OOM_NORMAL) {
966                         if (memcg_class[i].oomalert)
967                                 memory_normal_act(ad);
968                 }
969                 if (currentoom > memcg_class[i].oomlevel) {
970                         switch (currentoom) {
971                         case MEMGC_OOM_WARNING:
972                                 memory_low_act(ad);
973                                 break;
974                         case MEMGC_OOM_HIGH:
975                                 memcg_class[i].oomalert = 1;
976                                 memory_cgroup_oom_act(i);
977                                 break;
978                         case MEMGC_OOM_CRITICAL:
979                                 memcg_class[i].oomalert = 1;
980                                 break;
981                         default:
982                                 break;
983                         }
984                 }
985                 memcg_class[i].oomlevel = currentoom;
986         }
987
988         return ECORE_CALLBACK_RENEW;
989 }
990
991 /*
992 From memory.txt kernel document -
993 To register a notifier, application need:
994 - create an eventfd using eventfd(2)
995 - open memory.oom_control file
996 - write string like "<event_fd> <fd of memory.oom_control>"
997 to cgroup.event_control
998 */
999
1000 static int setup_eventfd(void)
1001 {
1002         unsigned int thres, i;
1003         int mcgfd, cgfd, evfd, res, sz, ret = -1;
1004         char buf[LOWMEM_PATH_MAX] = {0,};
1005         int buflen = sizeof(buf);
1006
1007         /* create an eventfd using eventfd(2)
1008         use same event fd for using ecore event loop */
1009         evfd = eventfd(0, 0);
1010         ret = fcntl(evfd, F_SETFL, O_NONBLOCK);
1011         if (ret < 0)
1012                 return RESOURCED_ERROR_FAIL;
1013
1014         for (i = 0; i < MEMCG_GROUP_MAX; i++) {
1015                 /* open cgroup.event_control */
1016                 snprintf(buf, buflen, "%s/%s/cgroup.event_control",
1017                                 MEMCG_PATH, memcg_class[i].cgroup_name);
1018                 cgfd = open(buf, O_WRONLY);
1019                 if (cgfd < 0) {
1020                         _E("open event_control failed");
1021                         return RESOURCED_ERROR_FAIL;
1022                 }
1023
1024                 /* register event in usage_in_byte */
1025                 snprintf(buf, buflen, "%s/%s/memory.usage_in_bytes",
1026                                 MEMCG_PATH, memcg_class[i].cgroup_name);
1027                 mcgfd = open(buf, O_RDONLY);
1028                 if (mcgfd < 0) {
1029                         _E("open memory control failed");
1030                         close(cgfd);
1031                         return RESOURCED_ERROR_FAIL;
1032                 }
1033
1034                 /* threshold lv 1 : wakeup softswapd */
1035                 /* write event fd about threshold lv1 */
1036                 thres = memcg_class[i].thres_lv1;
1037                 sz = snprintf(buf, buflen, "%d %d %d", evfd, mcgfd, thres);
1038                 sz += 1;
1039                 res = write(cgfd, buf, sz);
1040                 if (res != sz) {
1041                         _E("write cgfd failed : %d", res);
1042                         close(cgfd);
1043                         close(mcgfd);
1044                         return RESOURCED_ERROR_FAIL;
1045                 }
1046
1047                 /* calculate threshold lv_2 */
1048                 /* threshold lv 2 : lowmem warning */
1049                 thres = memcg_class[i].thres_lv2;
1050
1051                 /* write event fd about threshold lv1 */
1052                 sz = snprintf(buf, buflen, "%d %d %d", evfd, mcgfd, thres);
1053                 sz += 1;
1054                 res = write(cgfd, buf, sz);
1055                 if (res != sz) {
1056                         _E("write cgfd failed : %d", res);
1057                         close(cgfd);
1058                         close(mcgfd);
1059                         return RESOURCED_ERROR_FAIL;
1060                 }
1061
1062                 /* calculate threshold lv_3 */
1063                 /* threshold lv 3 : victim kill */
1064                 thres = memcg_class[i].thres_lv3;
1065
1066                 /* write event fd about threshold lv2 */
1067                 sz = snprintf(buf, buflen, "%d %d %d", evfd, mcgfd, thres);
1068                 sz += 1;
1069                 res = write(cgfd, buf, sz);
1070                 if (res != sz) {
1071                         _E("write cgfd failed : %d", res);
1072                         close(cgfd);
1073                         close(mcgfd);
1074                         return RESOURCED_ERROR_FAIL;
1075                 }
1076                 close(mcgfd);
1077
1078                 /* register event in oom_control */
1079                 snprintf(buf, buflen, "%s/%s/memory.oom_control",
1080                                 MEMCG_PATH, memcg_class[i].cgroup_name);
1081
1082                 mcgfd = open(buf, O_RDONLY);
1083                 if (mcgfd < 0) {
1084                         _E("open memory control failed");
1085                         close(cgfd);
1086                         return RESOURCED_ERROR_FAIL;
1087                 }
1088
1089                 /* write event fd about oom control with zero threshold*/
1090                 thres = 0;
1091                 sz = snprintf(buf, buflen, "%d %d %d", evfd, mcgfd, thres);
1092                 sz += 1;
1093                 res = write(cgfd, buf, sz);
1094                 if (res != sz) {
1095                         _E("write cgfd failed : %d", res);
1096                         close(cgfd);
1097                         close(mcgfd);
1098                         return RESOURCED_ERROR_FAIL;
1099                 }
1100                 close(cgfd);
1101                 close(mcgfd);
1102         }
1103         return evfd;
1104 }
1105
1106 void set_threshold(int level, int thres)
1107 {
1108         return;
1109 }
1110
1111 void set_leave_threshold(int thres)
1112 {
1113         return;
1114 }
1115
1116 static int init_memcg(void)
1117 {
1118         unsigned int total, i, limit, size;
1119         char buf[LOWMEM_PATH_MAX] = {0,};
1120         FILE *f;
1121         total = _get_total_memory();
1122         _D("Total : %d", total);
1123
1124         for (i = 0; i < MEMCG_GROUP_MAX; i++) {
1125                 /* write limit_in_bytes */
1126                 snprintf(buf, sizeof(buf), "%s/%s/memory.limit_in_bytes",
1127                                 MEMCG_PATH, memcg_class[i].cgroup_name);
1128                 _D("buf : %s", buf);
1129                 f = fopen(buf, "w");
1130                 if (!f) {
1131                         _E("%s open failed", buf);
1132                         return RESOURCED_ERROR_FAIL;
1133                 }
1134
1135                 limit = (unsigned int)(memcg_class[i].limit_ratio*(float)total);
1136
1137                 if (limit > memcg_class[i].min_limit)
1138                         limit = memcg_class[i].min_limit;
1139
1140                 size = snprintf(buf, sizeof(buf), "%u", limit);
1141                 if (fwrite(buf, size, 1, f) != 1)
1142                         _E("fwrite memory.limit_in_bytes : %d\n", limit);
1143                 fclose(f);
1144
1145                 /* save memory limitation for calculating threshold */
1146                 memcg_class[i].total_limit = limit;
1147
1148                 _calc_threshold(i, limit);
1149
1150                 /* set leave threshold value to kernel */
1151                 lowmem_set_cgroup_leave_threshold(memcg_class[i].oomleave);
1152
1153                 /* enable cgroup move */
1154                 snprintf(buf, sizeof(buf), "%s/%s/memory.move_charge_at_immigrate",
1155                                 MEMCG_PATH, memcg_class[i].cgroup_name);
1156                 _D("buf : %s", buf);
1157                 f = fopen(buf, "w");
1158                 if (!f) {
1159                         _E("%s open failed", buf);
1160                         return RESOURCED_ERROR_FAIL;
1161                 }
1162                 size = snprintf(buf, sizeof(buf), "3");
1163                 if (fwrite(buf, size, 1, f) != 1)
1164                         _E("fwrite memory.move_charge_at_immigrate\n");
1165                 fclose(f);
1166
1167         }
1168         return 0;
1169 }
1170
1171 static void lowmem_move_memcgroup(int pid, int oom_score_adj)
1172 {
1173         char buf[LOWMEM_PATH_MAX] = {0,};
1174         FILE *f;
1175         int size, background = 0;
1176         unsigned long swap_args[1] = {0,};
1177
1178         if (oom_score_adj > OOMADJ_BACKGRD_LOCKED) {
1179                 snprintf(buf, sizeof(buf), "%s/background/cgroup.procs", MEMCG_PATH);
1180                 background = 1;
1181         }
1182         else if (oom_score_adj >= OOMADJ_FOREGRD_LOCKED &&
1183                                         oom_score_adj < OOMADJ_BACKGRD_LOCKED)
1184                 snprintf(buf, sizeof(buf), "%s/foreground/cgroup.procs", MEMCG_PATH);
1185         else
1186                 return;
1187
1188         swap_args[0] = (unsigned long)pid;
1189         if (!swap_status(SWAP_CHECK_PID, swap_args) || !background) {
1190                 _I("buf : %s, pid : %d, oom : %d", buf, pid, oom_score_adj);
1191                 f = fopen(buf, "w");
1192                 if (!f) {
1193                         _E("%s open failed", buf);
1194                         return;
1195                 }
1196                 size = snprintf(buf, sizeof(buf), "%d", pid);
1197                 if (fwrite(buf, size, 1, f) != 1)
1198                         _E("fwrite cgroup tasks : %d\n", pid);
1199                 fclose(f);
1200         }
1201         if (background)
1202                 lowmem_swap_memory();
1203 }
1204
1205 static void lowmem_cgroup_foregrd_manage(int currentpid)
1206 {
1207         char buf[LOWMEM_PATH_MAX] = {0,};
1208         int pid, pgid;
1209         FILE *f;
1210         snprintf(buf, sizeof(buf), "%s/background/cgroup.procs", MEMCG_PATH);
1211         f = fopen(buf, "r");
1212         if (!f) {
1213                 _E("%s open failed", buf);
1214                 return;
1215         }
1216         while (fgets(buf, LOWMEM_PATH_MAX, f) != NULL) {
1217                 pid = atoi(buf);
1218                 if (currentpid == pid)
1219                         continue;
1220                 pgid = getpgid(pid);
1221                 if (currentpid == pgid)
1222                         lowmem_move_memcgroup(pid, OOMADJ_APP_LIMIT);
1223         }
1224         fclose(f);
1225 }
1226
1227 static unsigned int lowmem_read(int fd)
1228 {
1229         unsigned int mem_state;
1230         if (read(fd, &mem_state, sizeof(mem_state)) < 0) {
1231                 _E("error lowmem state");
1232                 return RESOURCED_ERROR_FAIL;
1233         }
1234         return mem_state;
1235 }
1236
1237 static Eina_Bool lowmem_efd_cb(void *data, Ecore_Fd_Handler *fd_handler)
1238 {
1239         int fd;
1240         struct ss_main_data *ad = (struct ss_main_data *)data;
1241         unsigned int mem_state;
1242
1243         if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) {
1244                 _E("ecore_main_fd_handler_active_get_error, return\n");
1245                 return ECORE_CALLBACK_CANCEL;
1246         }
1247
1248         fd = ecore_main_fd_handler_fd_get(fd_handler);
1249         if (fd < 0) {
1250                 _E("ecore_main_fd_handler_fd_get error, return\n");
1251                 return ECORE_CALLBACK_CANCEL;
1252         }
1253
1254         mem_state = lowmem_read(fd);
1255         if (mem_state == -1) {
1256                 lowmem_fd_stop(fd);
1257                 _E("error lowmem_read, restart lowmem fd");
1258                 lowmem_fd_start();
1259                 return ECORE_CALLBACK_CANCEL;
1260         }
1261
1262         print_lowmem_state(mem_state);
1263         lowmem_process(mem_state, ad);
1264
1265         return ECORE_CALLBACK_RENEW;
1266 }
1267
1268 static int lowmem_fd_start(void)
1269 {
1270         lowmem_fd = open("/dev/lowmemnotify", O_RDONLY);
1271         if (lowmem_fd < 0) {
1272                 _E("lowmem_fd_start fd open failed");
1273                 return RESOURCED_ERROR_FAIL;
1274         } else
1275                 _D("lowmem_fd_start open /dev/lowmemnotify sucess\n");
1276
1277         oom_check_timer = NULL;
1278         lowmem_efd = ecore_main_fd_handler_add(lowmem_fd, ECORE_FD_READ,
1279                                                (Ecore_Fd_Cb)lowmem_efd_cb, NULL,
1280                                                NULL, NULL);
1281         if (!lowmem_efd) {
1282                 _E("error ecore_main_fd_handler_add in lowmem_fd_start\n");
1283                 return RESOURCED_ERROR_FAIL;
1284         }
1285         return 0;
1286 }
1287
1288 int lowmem_init(void)
1289 {
1290         int ret = RESOURCED_ERROR_NONE;
1291
1292         /* set default memcg value */
1293         ret = init_memcg();
1294         if (ret < 0) {
1295                 _E("memory cgroup init failed");
1296                 return RESOURCED_ERROR_FAIL;
1297         }
1298
1299         ret = lowmem_fd_start();
1300         if (ret < 0) {
1301                 _E("lowmem_fd_start fail\n");
1302                 return RESOURCED_ERROR_FAIL;
1303         }
1304
1305         /* set threshold level 1, level 2, leave threshold */
1306         ret = lowmem_set_threshold();
1307         if (ret < 0) {
1308                 _E("lowmem_set_threshold fail\n");
1309                 return RESOURCED_ERROR_FAIL;
1310         }
1311
1312         /* register threshold and event fd */
1313         lowmem_fd = setup_eventfd();
1314         if (lowmem_fd < 0) {
1315                 _E("setup event fd is failed");
1316                 return RESOURCED_ERROR_FAIL;
1317         }
1318
1319         ecore_main_fd_handler_add(lowmem_fd, ECORE_FD_READ,
1320                                   (Ecore_Fd_Cb)lowmem_cb, NULL, NULL, NULL);
1321
1322         _I("lowmem_swaptype : %d", swap_status(SWAP_GET_TYPE, NULL));
1323
1324         lowmem_dbus_init();
1325
1326         return 0;
1327 }
1328
1329 static int lowmem_fd_stop(int fd)
1330 {
1331         if (lowmem_efd) {
1332                 ecore_main_fd_handler_del(lowmem_efd);
1333                 lowmem_efd = NULL;
1334         }
1335         if (fd >= 0) {
1336                 close(fd);
1337                 fd = -1;
1338         }
1339         return 0;
1340 }
1341
1342 static int resourced_memory_control(void *data)
1343 {
1344         int ret = RESOURCED_ERROR_NONE;
1345         struct lowmem_data_type *l_data;
1346
1347         l_data = (struct lowmem_data_type *)data;
1348         switch(l_data->control_type) {
1349         case LOWMEM_MOVE_CGROUP:
1350                 if (l_data->args)
1351                         lowmem_move_memcgroup((pid_t)l_data->args[0], l_data->args[1]);
1352                 break;
1353         case LOWMEM_MANAGE_FOREGROUND:
1354                 if (l_data->args)
1355                         lowmem_cgroup_foregrd_manage((pid_t)l_data->args[0]);
1356                 break;
1357
1358         }
1359         return ret;
1360 }
1361
1362 static int resourced_memory_init(void *data)
1363 {
1364         lowmem_ops = &memory_modules_ops;
1365
1366         return lowmem_init();
1367 }
1368
1369 static int resourced_memory_finalize(void *data)
1370 {
1371         return RESOURCED_ERROR_NONE;
1372 }
1373
1374 int lowmem_control(enum lowmem_control_type type, unsigned long *args)
1375 {
1376         struct lowmem_data_type l_data;
1377
1378         if (lowmem_ops) {
1379                 l_data.control_type = type;
1380                 l_data.args = args;
1381                 return lowmem_ops->control(&l_data);
1382         }
1383
1384         return RESOURCED_ERROR_NONE;
1385 }
1386
1387 static const struct module_ops memory_modules_ops = {
1388         .priority       = MODULE_PRIORITY_NORMAL,
1389         .name           = "lowmem",
1390         .init           = resourced_memory_init,
1391         .exit           = resourced_memory_finalize,
1392         .control        = resourced_memory_control,
1393 };
1394
1395 MODULE_REGISTER(&memory_modules_ops)