4 * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
20 * @file lowmem_handler.c
22 * @desc lowmem handler using memcgroup
24 * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
39 #include <sys/types.h>
42 #include <sys/eventfd.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"
66 MEMGC_GROUP_FOREGROUND,
67 MEMGC_GROUP_BACKGROUND,
77 #define MEM_SOFTSWAP_ENABLE 1
78 #define MEMCG_GROUP_MAX 2
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"
89 #define MEMPS_LOG_FILE "/var/log/memps"
91 #define DELETE_SM "sh -c "PREFIX"/bin/delete.sm"
92 #define MEMPS_EXEC_PATH "usr/bin/memps"
95 #define _SYS_RES_CLEANUP "RES_CLEANUP"
97 #define BtoMB(x) ((x) / 1024 / 1024)
98 #define MBtoB(x) (x<<20)
100 #define MEMCG_FOREGROUND_LIMIT_RATIO 0.6
101 #define MEMCG_BACKGROUND_LIMIT_RATIO 0.7
103 #define MEMCG_FOREGROUND_MIN_LIMIT MBtoB(400)
104 #define MEMCG_BACKGROUND_MIN_LIMIT UINT_MAX
106 /* threshold lv 1 : wakeup softswapd */
107 #define MEMCG_TRHES_SOFTSWAP_RATIO 0.75
109 /* threshold lv 2 : lowmem warning */
110 #define MEMCG_THRES_WARNING_RATIO 0.92
112 /* threshold lv 3 : victim kill */
113 #define MEMCG_THRES_OOM_RATIO 0.96
115 /* leave threshold */
116 #define MEMCG_OOMLEAVE_RATIO 0.88
118 #define MEMNOTIFY_NORMAL 0x0000
119 #define MEMNOTIFY_RECLAIM 0xecae
120 #define MEMNOTIFY_LOW 0xfaac
121 #define MEMNOTIFY_CRITICAL 0xdead
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 */
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
136 #define MAX_VICTIMS 30
138 static int lowmem_fd = -1;
139 static Ecore_Fd_Handler *lowmem_efd;
140 static int cur_mem_state = MEMNOTIFY_NORMAL;
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)
147 unsigned int oom_delete_sm_time;
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);
156 /* low memory action function for cgroup */
157 static int memory_cgroup_oom_act(int memcg_index);
159 static int lowmem_fd_start();
160 static int lowmem_fd_stop(int fd);
163 unsigned int event_fd;
164 unsigned int min_limit;
166 unsigned int oomlevel;
167 unsigned int oomalert;
168 unsigned int oomleave;
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;
177 struct lowmem_process_entry {
178 unsigned cur_mem_state;
179 unsigned new_mem_state;
180 int (*action) (void *);
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},
198 static struct memcg_class memcg_class[MEMCG_GROUP_MAX] = {
199 {0, MEMCG_FOREGROUND_MIN_LIMIT, MEMCG_FOREGROUND_LIMIT_RATIO, 0, 0, 0, "foreground",
201 {0, MEMCG_BACKGROUND_MIN_LIMIT, MEMCG_BACKGROUND_LIMIT_RATIO, 0, 0, 0, "background",
205 static const struct module_ops memory_modules_ops;
206 static const struct module_ops *lowmem_ops;
208 unsigned int get_available(void)
213 unsigned int free = 0, cached = 0;
214 unsigned int available = 0;
216 fp = fopen(MEMINFO_PATH, "r");
218 _E("%s open failed, %d", buf, fp);
222 while (fgets(buf, PATH_MAX, fp) != NULL) {
223 if ((idx = strstr(buf, "MemFree:"))) {
224 idx += strlen("MemFree:");
225 while (*idx < '0' || *idx > '9')
228 } else if ((idx = strstr(buf, "MemAvailable:"))) {
229 idx += strlen("MemAvailable:");
230 while (*idx < '0' || *idx > '9')
232 available = atoi(idx);
234 } else if((idx = strstr(buf, "Cached:"))) {
235 idx += strlen("Cached:");
236 while (*idx < '0' || *idx > '9')
244 available = free + cached;
251 void lowmem_dynamic_process_killer(int type)
253 /* This function is not supported */
256 void change_memory_state(int state, int force)
263 mem_state = cur_mem_state;
264 _D("mem_state = %d", mem_state);
268 case MEMNOTIFY_NORMAL:
269 memory_normal_act(NULL);
271 case MEMNOTIFY_RECLAIM:
272 memory_reclaim_act(NULL);
275 memory_low_act(NULL);
277 case MEMNOTIFY_CRITICAL:
278 memory_oom_act(NULL);
285 static unsigned int _get_total_memory(void)
290 unsigned int total = 0;
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')
307 static void _calc_threshold(int type, int limit)
309 unsigned int val, check;
311 /* calculate theshold lv3 */
312 val = (unsigned int)(memcg_class[type].total_limit*
313 (float)MEMCG_THRES_OOM_RATIO);
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);
321 /* set threshold lv3 */
322 memcg_class[type].thres_lv3 = val;
324 /* calculate threshold lv2 */
325 val = (unsigned int)(memcg_class[type].total_limit*
326 (float)MEMCG_THRES_WARNING_RATIO);
328 check = memcg_class[type].thres_lv3;
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);
336 /* set threshold lv2 */
337 memcg_class[type].thres_lv2 = val;
339 /* calculate threshold lv1 */
340 val = (unsigned int)(memcg_class[type].total_limit*
341 (float)MEMCG_TRHES_SOFTSWAP_RATIO);
343 /* check MIN value about threshold lv1*/
344 check = memcg_class[type].thres_lv2;
346 if (check - val < MIN_OOM_WARN_GAP)
347 val = (unsigned int)(check - MIN_OOM_WARN_GAP);
349 memcg_class[type].thres_lv1 = val;
351 /* set leave threshold */
352 val = (unsigned int)(memcg_class[type].total_limit*
353 (float)MEMCG_OOMLEAVE_RATIO);
355 check = memcg_class[type].thres_lv1;
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);
363 memcg_class[type].oomleave = val;
366 static unsigned int get_mem_usage(int idx)
369 char buf[LOWMEM_PATH_MAX] = {0,};
372 sprintf(buf, "%s/%s/memory.usage_in_bytes",
373 MEMCG_PATH, memcg_class[idx].cgroup_name);
377 _E("%s open failed, %d", buf, f);
378 return RESOURCED_ERROR_FAIL;
380 if (fgets(buf, 32, f) == NULL) {
381 _E("fgets failed\n");
383 return RESOURCED_ERROR_FAIL;
391 static int get_current_oom(int idx)
394 char buf[LOWMEM_PATH_MAX] = {0,};
398 sprintf(buf, "%s/%s/memory.oom_usr_control",
399 MEMCG_PATH, memcg_class[idx].cgroup_name);
403 _E("%s open failed, %d", buf, f);
404 return RESOURCED_ERROR_FAIL;
406 if (fgets(buf, 32, f) == NULL) {
407 _E("fgets failed\n");
409 return RESOURCED_ERROR_FAIL;
411 oom = strstr(buf, "oom_usr_control");
412 oom += strlen("oom_usr_control");
413 while (*oom < '0' || *oom > '9')
417 _D("get_current_oom : %d", level);
421 static int remove_shm(void)
423 int maxid, shmid, id;
424 struct shmid_ds shmseg;
425 struct shm_info shm_info;
427 maxid = shmctl(0, SHM_INFO, (struct shmid_ds *)(void *)&shm_info);
429 _E("shared mem error\n");
430 return RESOURCED_ERROR_FAIL;
433 for (id = 0; id <= maxid; id++) {
434 shmid = shmctl(id, SHM_STAT, &shmseg);
437 if (shmseg.shm_nattch == 0) {
438 _D("shared memory killer ==> %d killed\n",
440 shmctl(shmid, IPC_RMID, NULL);
446 static void print_mem_state(void)
448 unsigned int usage, i;
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);
458 static void make_memps_log(char *file, pid_t pid, char *victim_name)
463 static pid_t old_pid;
470 cur_tm = (struct tm *)malloc(sizeof(struct tm));
471 if (cur_tm == NULL) {
472 _E("Fail to memory allocation");
476 if (localtime_r(&now, cur_tm) == NULL) {
477 _E("Fail to get localtime");
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,
490 execl(MEMPS_EXEC_PATH, MEMPS_EXEC_PATH, "-f", new_log, (char *)NULL);
495 static int lowmem_check_current_state(int memcg_index,
496 int total_size, int oom_usage)
498 unsigned int usage, oomleave, check = 0;
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);
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);
515 static int lowmem_get_victim_pid(int *pid_arry, unsigned int* pid_size)
517 int count, num_pid = 0;
519 char buf[LOWMEM_PATH_MAX] = {0, };
521 f = fopen(VICTIM_TASK, "r");
524 _E("Fail to file open");
525 return RESOURCED_ERROR_FAIL;
528 /* firstly, read victim count */
529 if (fgets(buf, 32, f) == NULL) {
530 _E("victim list is empty");
536 if (count > MAX_VICTIMS) {
537 _E("Victim count is wrong value");
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);
546 _E("Victim size is needed\n");
552 if (count != num_pid)
553 _I("Number of pids is wrong\n");
559 return RESOURCED_ERROR_FAIL;
564 static int lowmem_set_cgroup_leave_threshold(unsigned int value)
567 f = fopen(SET_CGROUP_LEAVE_THRESHOLD, "w");
570 _E("Fail to file open");
571 return RESOURCED_ERROR_FAIL;
573 fprintf(f, "%d", value);
578 static int lowmem_set_threshold(void)
581 unsigned int val, total;
583 f = fopen(SET_THRESHOLD_RECLAIM, "w");
586 _E("Fail to file open : current kernel can't support swap cgroup");
587 return RESOURCED_ERROR_FAIL;
590 /* set threshold reclaim */
591 total = _get_total_memory();
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.
598 if (total > MBtoB(700))
599 val = MEM_THRESHOLD_RECLAIM;
601 val = MEM_THRESHOLD_RECLAIM >> 1;
602 fprintf(f, "%d", val);
605 /* set threshold level1 */
606 f = fopen(SET_THRESHOLD_LV1, "w");
609 _E("Fail to file open");
610 return RESOURCED_ERROR_FAIL;
612 fprintf(f, "%d", MEM_THRESHOLD_LV1);
615 /* set threshold level2 */
616 f = fopen(SET_THRESHOLD_LV2, "w");
619 _E("Fail to file open");
620 return RESOURCED_ERROR_FAIL;
622 fprintf(f, "%d", MEM_THRESHOLD_LV2);
625 /* set leave threshold */
626 f = fopen(SET_LEAVE_THRESHOLD, "w");
629 _E("Fail to file open");
630 return RESOURCED_ERROR_FAIL;
632 fprintf(f, "%d", MEM_LEAVE_THRESHOLD);
637 void *_lowmem_oom_killer_cb(void *data)
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;
646 /* get multiple victims from kernel */
647 count = lowmem_get_victim_pid((int *)pid_array,
648 (unsigned int *)pid_size);
651 _E("get victim was failed");
655 /* kill all selected process */
656 for (i = 0; i < count; i++) {
661 _D("oom total memory size : %d", total_size);
662 ret = proc_get_cmdline(pid, appname);
664 _D("invalid pid(%d) was selected", pid);
667 if (!strcmp("memps", appname)) {
668 _E("memps(%d) was selected, skip it", pid);
671 if (!strcmp("crash-worker", appname)) {
672 _E("crash-worker(%d) was selected, skip it", pid);
676 /* make memps log for killing application firstly */
678 make_memps_log(MEMPS_LOG_FILE, pid, appname);
680 if (proc_get_oom_score_adj(pid, &oom_score_adj) < 0) {
681 _D("pid(%d) was already terminated", pid);
685 /* just continue if selected process moved to foreground */
686 if (BtoMB(total_size) > MEM_LEAVE_THRESHOLD && oom_score_adj < OOMADJ_BACKGRD_UNLOCKED)
689 proc_remove_process_list(pid);
692 total_size += pid_size[i];
693 _I("we killed, lowmem lv2 = %d (%s) oom = %d\n",
694 pid, appname, oom_score_adj);
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);
700 if (oom_score_adj > OOMADJ_FOREGRD_UNLOCKED)
703 if (forgrd_size < pid_size[i]) {
705 forgrd_size = pid_size[i];
706 strncpy(popupname, appname, PROC_NAME_MAX-1);
711 make_memps_log(MEMPS_LOG_FILE, forgrd_pid, popupname);
716 int lowmem_oom_killer_cb(int memcg_idx, int flags)
718 int memcg_index = memcg_idx;
719 _lowmem_oom_killer_cb((void *)&memcg_index);
723 static void lowmem_cgroup_oom_killer(int memcg_index)
725 int pid, ret, oom_score_adj, count, i;
726 char appname[PATH_MAX];
728 unsigned int pid_size[32];
729 unsigned int total_size = 0, oom_usage = 0;
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);
737 _E("get victim was failed");
741 for (i = 0; i < count; i++) {
746 _D("oom total memory size : %d", total_size);
747 ret = proc_get_cmdline(pid, appname);
749 _E("invalid pid(%d) was selected", pid);
752 if (!strcmp("memps", appname)) {
753 _E("memps(%d) was selected, skip it", pid);
756 if (!strcmp("crash-worker", appname)) {
757 _E("crash-worker(%d) was selected, skip it", pid);
760 if (proc_get_oom_score_adj(pid, &oom_score_adj) < 0) {
761 _D("pid(%d) was already terminated", pid);
765 /* check current memory status */
766 if (lowmem_check_current_state(memcg_index, total_size,
770 /* make memps log for killing application firstly */
772 make_memps_log(MEMPS_LOG_FILE, pid, appname);
774 proc_remove_process_list(pid);
777 total_size += pid_size[i];
778 _I("we killed, lowmem lv2 = %d (%s) oom = %d\n",
779 pid, appname, oom_score_adj);
781 if (oom_score_adj > OOMADJ_FOREGRD_UNLOCKED)
785 make_memps_log(MEMPS_LOG_FILE, pid, appname);
789 static char *convert_to_str(unsigned int mem_state)
793 case MEMNOTIFY_NORMAL:
794 case MEMNOTIFY_RECLAIM:
800 case MEMNOTIFY_CRITICAL:
801 tmp = "mem critical";
809 static void print_lowmem_state(unsigned int mem_state)
811 _I("[LOW MEM STATE] %s ==> %s", convert_to_str(cur_mem_state),
812 convert_to_str(mem_state));
815 static void lowmem_swap_memory(void)
820 if (cur_mem_state == MEMNOTIFY_NORMAL)
823 swap_type = swap_status(SWAP_GET_TYPE, NULL);
825 if (swap_type == SWAP_ON) {
828 pid = (pid_t)swap_status(SWAP_GET_CANDIDATE_PID, NULL);
831 _I("swap cgroup entered : pid : %d", (int)pid);
832 resourced_notify(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, (void*)&pid);
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);
840 static int memory_reclaim_act(void *data)
843 _I("[LOW MEM STATE] memory reclaim state");
844 ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status);
846 _E("vconf get failed(VCONFKEY_SYSMAN_LOW_MEMORY)\n");
847 return RESOURCED_ERROR_FAIL;
849 if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL)
850 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
851 VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
853 lowmem_swap_memory();
858 static int memory_low_act(void *data)
860 _I("[LOW MEM STATE] memory low state");
864 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
865 VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING);
870 static int memory_oom_act(void *ad)
875 _I("[LOW MEM STATE] memory oom state");
879 ret = pthread_create(&pth, 0, _lowmem_oom_killer_cb, (void*)NULL);
881 _E("pthread creation failed!, call directly!");
882 _lowmem_oom_killer_cb((void*)NULL);
886 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
887 VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING);
891 static int memory_cgroup_oom_act(int memcg_index)
893 _I("[LOW MEM STATE] memory oom state");
897 lowmem_cgroup_oom_killer(memcg_index);
899 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
900 VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING);
904 static int memory_normal_act(void *data)
906 _I("[LOW MEM STATE] memory normal state");
907 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
908 VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
912 static int lowmem_process(unsigned int mem_state, void *ad)
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;
922 cur_mem_state = mem_state;
924 if (mem_state == MEMNOTIFY_CRITICAL)
926 ecore_timer_add(OOM_TIMER_INTERVAL, (const void *)lpe[i].action, ad);
930 cur_mem_state = mem_state;
934 static unsigned int lowmem_eventfd_read(int fd)
937 uint64_t dummy_state;
938 ret = read(fd, &dummy_state, sizeof(dummy_state));
942 static Eina_Bool lowmem_cb(void *data, Ecore_Fd_Handler *fd_handler)
944 int fd, i, currentoom;
945 struct ss_main_data *ad = (struct ss_main_data *)data;
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;
952 fd = ecore_main_fd_handler_fd_get(fd_handler);
954 _E("ecore_main_fd_handler_fd_get error , return\n");
955 return ECORE_CALLBACK_CANCEL;
957 lowmem_eventfd_read(fd);
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);
965 if (currentoom > memcg_class[i].oomlevel) {
966 switch (currentoom) {
967 case MEMGC_OOM_WARNING:
971 memcg_class[i].oomalert = 1;
972 memory_cgroup_oom_act(i);
974 case MEMGC_OOM_CRITICAL:
975 memcg_class[i].oomalert = 1;
981 memcg_class[i].oomlevel = currentoom;
984 return ECORE_CALLBACK_RENEW;
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
996 static int setup_eventfd(void)
998 unsigned int thres, i;
999 int mcgfd, cgfd, evfd, res, sz, ret = -1;
1000 char buf[LOWMEM_PATH_MAX] = {0,};
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);
1007 return RESOURCED_ERROR_FAIL;
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);
1015 _E("open event_control failed");
1016 return RESOURCED_ERROR_FAIL;
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);
1024 _E("open memory control failed");
1026 return RESOURCED_ERROR_FAIL;
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);
1034 res = write(cgfd, buf, sz);
1036 _E("write cgfd failed : %d", res);
1039 return RESOURCED_ERROR_FAIL;
1042 /* calculate threshold lv_2 */
1043 /* threshold lv 2 : lowmem warning */
1044 thres = memcg_class[i].thres_lv2;
1046 /* write event fd about threshold lv1 */
1047 sz = sprintf(buf, "%d %d %d", evfd, mcgfd, thres);
1049 res = write(cgfd, buf, sz);
1051 _E("write cgfd failed : %d", res);
1054 return RESOURCED_ERROR_FAIL;
1057 /* calculate threshold lv_3 */
1058 /* threshold lv 3 : victim kill */
1059 thres = memcg_class[i].thres_lv3;
1061 /* write event fd about threshold lv2 */
1062 sz = sprintf(buf, "%d %d %d", evfd, mcgfd, thres);
1064 res = write(cgfd, buf, sz);
1066 _E("write cgfd failed : %d", res);
1069 return RESOURCED_ERROR_FAIL;
1073 /* register event in oom_control */
1074 sprintf(buf, "%s/%s/memory.oom_control",
1075 MEMCG_PATH, memcg_class[i].cgroup_name);
1077 mcgfd = open(buf, O_RDONLY);
1079 _E("open memory control failed");
1081 return RESOURCED_ERROR_FAIL;
1084 /* write event fd about oom control with zero threshold*/
1086 sz = sprintf(buf, "%d %d %d", evfd, mcgfd, thres);
1088 res = write(cgfd, buf, sz);
1090 _E("write cgfd failed : %d", res);
1093 return RESOURCED_ERROR_FAIL;
1101 void set_threshold(int level, int thres)
1106 void set_leave_threshold(int thres)
1111 static int init_memcg(void)
1113 unsigned int total, i, limit, size;
1114 char buf[LOWMEM_PATH_MAX] = {0,};
1116 total = _get_total_memory();
1117 _D("Total : %d", total);
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");
1126 _E("%s open failed", buf);
1127 return RESOURCED_ERROR_FAIL;
1130 limit = (unsigned int)(memcg_class[i].limit_ratio*(float)total);
1132 if (limit > memcg_class[i].min_limit)
1133 limit = memcg_class[i].min_limit;
1135 size = sprintf(buf, "%u", limit);
1136 if (fwrite(buf, size, 1, f) != 1)
1137 _E("fwrite memory.limit_in_bytes : %d\n", limit);
1140 /* save memory limitation for calculating threshold */
1141 memcg_class[i].total_limit = limit;
1143 _calc_threshold(i, limit);
1145 /* set leave threshold value to kernel */
1146 lowmem_set_cgroup_leave_threshold(memcg_class[i].oomleave);
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");
1154 _E("%s open failed", buf);
1155 return RESOURCED_ERROR_FAIL;
1157 size = sprintf(buf, "3");
1158 if (fwrite(buf, size, 1, f) != 1)
1159 _E("fwrite memory.move_charge_at_immigrate\n");
1166 static void lowmem_move_memcgroup(int pid, int oom_score_adj)
1168 char buf[LOWMEM_PATH_MAX] = {0,};
1170 int size, background = 0;
1171 unsigned long swap_args[1] = {0,};
1173 if (oom_score_adj > OOMADJ_BACKGRD_LOCKED) {
1174 sprintf(buf, "%s/background/cgroup.procs", MEMCG_PATH);
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);
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");
1188 _E("%s open failed", buf);
1191 size = sprintf(buf, "%d", pid);
1192 if (fwrite(buf, size, 1, f) != 1)
1193 _E("fwrite cgroup tasks : %d\n", pid);
1197 lowmem_swap_memory();
1200 static void lowmem_cgroup_foregrd_manage(int currentpid)
1202 char buf[LOWMEM_PATH_MAX] = {0,};
1205 sprintf(buf, "%s/background/cgroup.procs", MEMCG_PATH);
1206 f = fopen(buf, "r");
1208 _E("%s open failed", buf);
1211 while (fgets(buf, LOWMEM_PATH_MAX, f) != NULL) {
1213 if (currentpid == pid)
1215 pgid = getpgid(pid);
1216 if (currentpid == pgid)
1217 lowmem_move_memcgroup(pid, OOMADJ_APP_LIMIT);
1222 static unsigned int lowmem_read(int fd)
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;
1232 static Eina_Bool lowmem_efd_cb(void *data, Ecore_Fd_Handler *fd_handler)
1235 struct ss_main_data *ad = (struct ss_main_data *)data;
1236 unsigned int mem_state;
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;
1243 fd = ecore_main_fd_handler_fd_get(fd_handler);
1245 _E("ecore_main_fd_handler_fd_get error, return\n");
1246 return ECORE_CALLBACK_CANCEL;
1249 mem_state = lowmem_read(fd);
1250 if (mem_state == -1) {
1252 _E("error lowmem_read, restart lowmem fd");
1254 return ECORE_CALLBACK_CANCEL;
1257 print_lowmem_state(mem_state);
1258 lowmem_process(mem_state, ad);
1260 return ECORE_CALLBACK_RENEW;
1263 static int lowmem_fd_start(void)
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;
1270 _D("lowmem_fd_start open /dev/lowmemnotify sucess\n");
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,
1277 _E("error ecore_main_fd_handler_add in lowmem_fd_start\n");
1278 return RESOURCED_ERROR_FAIL;
1283 int lowmem_init(void)
1285 int ret = RESOURCED_ERROR_NONE;
1287 /* set default memcg value */
1290 _E("memory cgroup init failed");
1291 return RESOURCED_ERROR_FAIL;
1294 ret = lowmem_fd_start();
1296 _E("lowmem_fd_start fail\n");
1297 return RESOURCED_ERROR_FAIL;
1300 /* set threshold level 1, level 2, leave threshold */
1301 ret = lowmem_set_threshold();
1303 _E("lowmem_set_threshold fail\n");
1304 return RESOURCED_ERROR_FAIL;
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;
1314 ecore_main_fd_handler_add(lowmem_fd, ECORE_FD_READ,
1315 (Ecore_Fd_Cb)lowmem_cb, NULL, NULL, NULL);
1317 _I("lowmem_swaptype : %d", swap_status(SWAP_GET_TYPE, NULL));
1324 static int lowmem_fd_stop(int fd)
1327 ecore_main_fd_handler_del(lowmem_efd);
1337 static int resourced_memory_control(void *data)
1339 int ret = RESOURCED_ERROR_NONE;
1340 struct lowmem_data_type *l_data;
1342 l_data = (struct lowmem_data_type *)data;
1343 switch(l_data->control_type) {
1344 case LOWMEM_MOVE_CGROUP:
1346 lowmem_move_memcgroup((pid_t)l_data->args[0], l_data->args[1]);
1348 case LOWMEM_MANAGE_FOREGROUND:
1350 lowmem_cgroup_foregrd_manage((pid_t)l_data->args[0]);
1357 static int resourced_memory_init(void *data)
1359 lowmem_ops = &memory_modules_ops;
1361 return lowmem_init();
1364 static int resourced_memory_finalize(void *data)
1366 return RESOURCED_ERROR_NONE;
1369 int lowmem_control(enum lowmem_control_type type, unsigned long *args)
1371 struct lowmem_data_type l_data;
1374 l_data.control_type = type;
1376 return lowmem_ops->control(&l_data);
1379 return RESOURCED_ERROR_NONE;
1382 static const struct module_ops memory_modules_ops = {
1383 .priority = MODULE_PRIORITY_NORMAL,
1385 .init = resourced_memory_init,
1386 .exit = resourced_memory_finalize,
1387 .control = resourced_memory_control,
1390 MODULE_REGISTER(&memory_modules_ops)