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