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");
297 while (fgets(buf, PATH_MAX, fp) != NULL) {
298 if ((idx = strstr(buf, "MemTotal:"))) {
299 idx += strlen("MemTotal:");
300 while (*idx < '0' || *idx > '9')
311 static void _calc_threshold(int type, int limit)
313 unsigned int val, check;
315 /* calculate theshold lv3 */
316 val = (unsigned int)(memcg_class[type].total_limit*
317 (float)MEMCG_THRES_OOM_RATIO);
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);
325 /* set threshold lv3 */
326 memcg_class[type].thres_lv3 = val;
328 /* calculate threshold lv2 */
329 val = (unsigned int)(memcg_class[type].total_limit*
330 (float)MEMCG_THRES_WARNING_RATIO);
332 check = memcg_class[type].thres_lv3;
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);
340 /* set threshold lv2 */
341 memcg_class[type].thres_lv2 = val;
343 /* calculate threshold lv1 */
344 val = (unsigned int)(memcg_class[type].total_limit*
345 (float)MEMCG_TRHES_SOFTSWAP_RATIO);
347 /* check MIN value about threshold lv1*/
348 check = memcg_class[type].thres_lv2;
350 if (check - val < MIN_OOM_WARN_GAP)
351 val = (unsigned int)(check - MIN_OOM_WARN_GAP);
353 memcg_class[type].thres_lv1 = val;
355 /* set leave threshold */
356 val = (unsigned int)(memcg_class[type].total_limit*
357 (float)MEMCG_OOMLEAVE_RATIO);
359 check = memcg_class[type].thres_lv1;
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);
367 memcg_class[type].oomleave = val;
370 static unsigned int get_mem_usage(int idx)
373 char buf[LOWMEM_PATH_MAX] = {0,};
376 snprintf(buf, sizeof(buf), "%s/%s/memory.usage_in_bytes",
377 MEMCG_PATH, memcg_class[idx].cgroup_name);
381 _E("%s open failed, %d", buf, f);
382 return RESOURCED_ERROR_FAIL;
384 if (fgets(buf, 32, f) == NULL) {
385 _E("fgets failed\n");
387 return RESOURCED_ERROR_FAIL;
395 static int get_current_oom(int idx)
398 char buf[LOWMEM_PATH_MAX] = {0,};
402 snprintf(buf, sizeof(buf), "%s/%s/memory.oom_usr_control",
403 MEMCG_PATH, memcg_class[idx].cgroup_name);
407 _E("%s open failed, %d", buf, f);
408 return RESOURCED_ERROR_FAIL;
410 if (fgets(buf, 32, f) == NULL) {
411 _E("fgets failed\n");
413 return RESOURCED_ERROR_FAIL;
415 oom = strstr(buf, "oom_usr_control");
416 oom += strlen("oom_usr_control");
417 while (*oom < '0' || *oom > '9')
421 _D("get_current_oom : %d", level);
425 static int remove_shm(void)
427 int maxid, shmid, id;
428 struct shmid_ds shmseg;
429 struct shm_info shm_info;
431 maxid = shmctl(0, SHM_INFO, (struct shmid_ds *)(void *)&shm_info);
433 _E("shared mem error\n");
434 return RESOURCED_ERROR_FAIL;
437 for (id = 0; id <= maxid; id++) {
438 shmid = shmctl(id, SHM_STAT, &shmseg);
441 if (shmseg.shm_nattch == 0) {
442 _D("shared memory killer ==> %d killed\n",
444 shmctl(shmid, IPC_RMID, NULL);
450 static void print_mem_state(void)
452 unsigned int usage, i;
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);
462 static void make_memps_log(char *file, pid_t pid, char *victim_name)
467 static pid_t old_pid;
474 cur_tm = (struct tm *)malloc(sizeof(struct tm));
475 if (cur_tm == NULL) {
476 _E("Fail to memory allocation");
480 if (localtime_r(&now, cur_tm) == NULL) {
481 _E("Fail to get localtime");
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,
494 execl(MEMPS_EXEC_PATH, MEMPS_EXEC_PATH, "-f", new_log, (char *)NULL);
499 static int lowmem_check_current_state(int memcg_index,
500 int total_size, int oom_usage)
502 unsigned int usage, oomleave, check = 0;
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);
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);
519 static int lowmem_get_victim_pid(int *pid_arry, unsigned int* pid_size)
521 int count, num_pid = 0;
523 char buf[LOWMEM_PATH_MAX] = {0, };
525 f = fopen(VICTIM_TASK, "r");
528 _E("Fail to file open");
529 return RESOURCED_ERROR_FAIL;
532 /* firstly, read victim count */
533 if (fgets(buf, 32, f) == NULL) {
534 _E("victim list is empty");
540 if (count > MAX_VICTIMS) {
541 _E("Victim count is wrong value");
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);
550 _E("Victim size is needed\n");
556 if (count != num_pid)
557 _I("Number of pids is wrong\n");
563 return RESOURCED_ERROR_FAIL;
568 static int lowmem_set_cgroup_leave_threshold(unsigned int value)
571 f = fopen(SET_CGROUP_LEAVE_THRESHOLD, "w");
574 _E("Fail to file open");
575 return RESOURCED_ERROR_FAIL;
577 fprintf(f, "%d", value);
582 static int lowmem_set_threshold(void)
585 unsigned int val, total;
587 f = fopen(SET_THRESHOLD_RECLAIM, "w");
590 _E("Fail to file open : current kernel can't support swap cgroup");
591 return RESOURCED_ERROR_FAIL;
594 /* set threshold reclaim */
595 total = _get_total_memory();
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.
602 if (total > MBtoB(700))
603 val = MEM_THRESHOLD_RECLAIM;
605 val = MEM_THRESHOLD_RECLAIM >> 1;
606 fprintf(f, "%d", val);
609 /* set threshold level1 */
610 f = fopen(SET_THRESHOLD_LV1, "w");
613 _E("Fail to file open");
614 return RESOURCED_ERROR_FAIL;
616 fprintf(f, "%d", MEM_THRESHOLD_LV1);
619 /* set threshold level2 */
620 f = fopen(SET_THRESHOLD_LV2, "w");
623 _E("Fail to file open");
624 return RESOURCED_ERROR_FAIL;
626 fprintf(f, "%d", MEM_THRESHOLD_LV2);
629 /* set leave threshold */
630 f = fopen(SET_LEAVE_THRESHOLD, "w");
633 _E("Fail to file open");
634 return RESOURCED_ERROR_FAIL;
636 fprintf(f, "%d", MEM_LEAVE_THRESHOLD);
641 void *_lowmem_oom_killer_cb(void *data)
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;
650 /* get multiple victims from kernel */
651 count = lowmem_get_victim_pid((int *)pid_array,
652 (unsigned int *)pid_size);
655 _E("get victim was failed");
659 /* kill all selected process */
660 for (i = 0; i < count; i++) {
665 _D("oom total memory size : %d", total_size);
666 ret = proc_get_cmdline(pid, appname);
668 _D("invalid pid(%d) was selected", pid);
671 if (!strcmp("memps", appname)) {
672 _E("memps(%d) was selected, skip it", pid);
675 if (!strcmp("crash-worker", appname)) {
676 _E("crash-worker(%d) was selected, skip it", pid);
680 /* make memps log for killing application firstly */
682 make_memps_log(MEMPS_LOG_FILE, pid, appname);
684 if (proc_get_oom_score_adj(pid, &oom_score_adj) < 0) {
685 _D("pid(%d) was already terminated", pid);
689 /* just continue if selected process moved to foreground */
690 if (BtoMB(total_size) > MEM_LEAVE_THRESHOLD && oom_score_adj < OOMADJ_BACKGRD_UNLOCKED)
693 proc_remove_process_list(pid);
696 total_size += pid_size[i];
697 _I("we killed, lowmem lv2 = %d (%s) oom = %d\n",
698 pid, appname, oom_score_adj);
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);
704 if (oom_score_adj > OOMADJ_FOREGRD_UNLOCKED)
707 if (forgrd_size < pid_size[i]) {
709 forgrd_size = pid_size[i];
710 strncpy(popupname, appname, PROC_NAME_MAX-1);
715 make_memps_log(MEMPS_LOG_FILE, forgrd_pid, popupname);
720 int lowmem_oom_killer_cb(int memcg_idx, int flags)
722 int memcg_index = memcg_idx;
723 _lowmem_oom_killer_cb((void *)&memcg_index);
727 static void lowmem_cgroup_oom_killer(int memcg_index)
729 int pid, ret, oom_score_adj, count, i;
730 char appname[PATH_MAX];
732 unsigned int pid_size[32];
733 unsigned int total_size = 0, oom_usage = 0;
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);
741 _E("get victim was failed");
745 for (i = 0; i < count; i++) {
750 _D("oom total memory size : %d", total_size);
751 ret = proc_get_cmdline(pid, appname);
753 _E("invalid pid(%d) was selected", pid);
756 if (!strcmp("memps", appname)) {
757 _E("memps(%d) was selected, skip it", pid);
760 if (!strcmp("crash-worker", appname)) {
761 _E("crash-worker(%d) was selected, skip it", pid);
764 if (proc_get_oom_score_adj(pid, &oom_score_adj) < 0) {
765 _D("pid(%d) was already terminated", pid);
769 /* check current memory status */
770 if (lowmem_check_current_state(memcg_index, total_size,
774 /* make memps log for killing application firstly */
776 make_memps_log(MEMPS_LOG_FILE, pid, appname);
778 proc_remove_process_list(pid);
781 total_size += pid_size[i];
782 _I("we killed, lowmem lv2 = %d (%s) oom = %d\n",
783 pid, appname, oom_score_adj);
785 if (oom_score_adj > OOMADJ_FOREGRD_UNLOCKED)
789 make_memps_log(MEMPS_LOG_FILE, pid, appname);
793 static char *convert_to_str(unsigned int mem_state)
797 case MEMNOTIFY_NORMAL:
798 case MEMNOTIFY_RECLAIM:
804 case MEMNOTIFY_CRITICAL:
805 tmp = "mem critical";
813 static void print_lowmem_state(unsigned int mem_state)
815 _I("[LOW MEM STATE] %s ==> %s", convert_to_str(cur_mem_state),
816 convert_to_str(mem_state));
819 static void lowmem_swap_memory(void)
824 if (cur_mem_state == MEMNOTIFY_NORMAL)
827 swap_type = swap_status(SWAP_GET_TYPE, NULL);
829 if (swap_type == SWAP_ON) {
832 pid = (pid_t)swap_status(SWAP_GET_CANDIDATE_PID, NULL);
835 _I("swap cgroup entered : pid : %d", (int)pid);
836 resourced_notify(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, (void*)&pid);
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);
844 static int memory_reclaim_act(void *data)
847 _I("[LOW MEM STATE] memory reclaim state");
848 ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status);
850 _E("vconf get failed(VCONFKEY_SYSMAN_LOW_MEMORY)\n");
851 return RESOURCED_ERROR_FAIL;
853 if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL)
854 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
855 VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
857 lowmem_swap_memory();
862 static int memory_low_act(void *data)
864 _I("[LOW MEM STATE] memory low state");
868 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
869 VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING);
874 static int memory_oom_act(void *ad)
879 _I("[LOW MEM STATE] memory oom state");
883 ret = pthread_create(&pth, 0, _lowmem_oom_killer_cb, (void*)NULL);
885 _E("pthread creation failed!, call directly!");
886 _lowmem_oom_killer_cb((void*)NULL);
890 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
891 VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING);
895 static int memory_cgroup_oom_act(int memcg_index)
897 _I("[LOW MEM STATE] memory oom state");
901 lowmem_cgroup_oom_killer(memcg_index);
903 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
904 VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING);
908 static int memory_normal_act(void *data)
910 _I("[LOW MEM STATE] memory normal state");
911 vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
912 VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
916 static int lowmem_process(unsigned int mem_state, void *ad)
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;
926 cur_mem_state = mem_state;
928 if (mem_state == MEMNOTIFY_CRITICAL)
930 ecore_timer_add(OOM_TIMER_INTERVAL, (const void *)lpe[i].action, ad);
934 cur_mem_state = mem_state;
938 static unsigned int lowmem_eventfd_read(int fd)
941 uint64_t dummy_state;
942 ret = read(fd, &dummy_state, sizeof(dummy_state));
946 static Eina_Bool lowmem_cb(void *data, Ecore_Fd_Handler *fd_handler)
948 int fd, i, currentoom;
949 struct ss_main_data *ad = (struct ss_main_data *)data;
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;
956 fd = ecore_main_fd_handler_fd_get(fd_handler);
958 _E("ecore_main_fd_handler_fd_get error , return\n");
959 return ECORE_CALLBACK_CANCEL;
961 lowmem_eventfd_read(fd);
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);
969 if (currentoom > memcg_class[i].oomlevel) {
970 switch (currentoom) {
971 case MEMGC_OOM_WARNING:
975 memcg_class[i].oomalert = 1;
976 memory_cgroup_oom_act(i);
978 case MEMGC_OOM_CRITICAL:
979 memcg_class[i].oomalert = 1;
985 memcg_class[i].oomlevel = currentoom;
988 return ECORE_CALLBACK_RENEW;
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
1000 static int setup_eventfd(void)
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);
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);
1012 return RESOURCED_ERROR_FAIL;
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);
1020 _E("open event_control failed");
1021 return RESOURCED_ERROR_FAIL;
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);
1029 _E("open memory control failed");
1031 return RESOURCED_ERROR_FAIL;
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);
1039 res = write(cgfd, buf, sz);
1041 _E("write cgfd failed : %d", res);
1044 return RESOURCED_ERROR_FAIL;
1047 /* calculate threshold lv_2 */
1048 /* threshold lv 2 : lowmem warning */
1049 thres = memcg_class[i].thres_lv2;
1051 /* write event fd about threshold lv1 */
1052 sz = snprintf(buf, buflen, "%d %d %d", evfd, mcgfd, thres);
1054 res = write(cgfd, buf, sz);
1056 _E("write cgfd failed : %d", res);
1059 return RESOURCED_ERROR_FAIL;
1062 /* calculate threshold lv_3 */
1063 /* threshold lv 3 : victim kill */
1064 thres = memcg_class[i].thres_lv3;
1066 /* write event fd about threshold lv2 */
1067 sz = snprintf(buf, buflen, "%d %d %d", evfd, mcgfd, thres);
1069 res = write(cgfd, buf, sz);
1071 _E("write cgfd failed : %d", res);
1074 return RESOURCED_ERROR_FAIL;
1078 /* register event in oom_control */
1079 snprintf(buf, buflen, "%s/%s/memory.oom_control",
1080 MEMCG_PATH, memcg_class[i].cgroup_name);
1082 mcgfd = open(buf, O_RDONLY);
1084 _E("open memory control failed");
1086 return RESOURCED_ERROR_FAIL;
1089 /* write event fd about oom control with zero threshold*/
1091 sz = snprintf(buf, buflen, "%d %d %d", evfd, mcgfd, thres);
1093 res = write(cgfd, buf, sz);
1095 _E("write cgfd failed : %d", res);
1098 return RESOURCED_ERROR_FAIL;
1106 void set_threshold(int level, int thres)
1111 void set_leave_threshold(int thres)
1116 static int init_memcg(void)
1118 unsigned int total, i, limit, size;
1119 char buf[LOWMEM_PATH_MAX] = {0,};
1121 total = _get_total_memory();
1122 _D("Total : %d", total);
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");
1131 _E("%s open failed", buf);
1132 return RESOURCED_ERROR_FAIL;
1135 limit = (unsigned int)(memcg_class[i].limit_ratio*(float)total);
1137 if (limit > memcg_class[i].min_limit)
1138 limit = memcg_class[i].min_limit;
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);
1145 /* save memory limitation for calculating threshold */
1146 memcg_class[i].total_limit = limit;
1148 _calc_threshold(i, limit);
1150 /* set leave threshold value to kernel */
1151 lowmem_set_cgroup_leave_threshold(memcg_class[i].oomleave);
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");
1159 _E("%s open failed", buf);
1160 return RESOURCED_ERROR_FAIL;
1162 size = snprintf(buf, sizeof(buf), "3");
1163 if (fwrite(buf, size, 1, f) != 1)
1164 _E("fwrite memory.move_charge_at_immigrate\n");
1171 static void lowmem_move_memcgroup(int pid, int oom_score_adj)
1173 char buf[LOWMEM_PATH_MAX] = {0,};
1175 int size, background = 0;
1176 unsigned long swap_args[1] = {0,};
1178 if (oom_score_adj > OOMADJ_BACKGRD_LOCKED) {
1179 snprintf(buf, sizeof(buf), "%s/background/cgroup.procs", MEMCG_PATH);
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);
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");
1193 _E("%s open failed", buf);
1196 size = snprintf(buf, sizeof(buf), "%d", pid);
1197 if (fwrite(buf, size, 1, f) != 1)
1198 _E("fwrite cgroup tasks : %d\n", pid);
1202 lowmem_swap_memory();
1205 static void lowmem_cgroup_foregrd_manage(int currentpid)
1207 char buf[LOWMEM_PATH_MAX] = {0,};
1210 snprintf(buf, sizeof(buf), "%s/background/cgroup.procs", MEMCG_PATH);
1211 f = fopen(buf, "r");
1213 _E("%s open failed", buf);
1216 while (fgets(buf, LOWMEM_PATH_MAX, f) != NULL) {
1218 if (currentpid == pid)
1220 pgid = getpgid(pid);
1221 if (currentpid == pgid)
1222 lowmem_move_memcgroup(pid, OOMADJ_APP_LIMIT);
1227 static unsigned int lowmem_read(int fd)
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;
1237 static Eina_Bool lowmem_efd_cb(void *data, Ecore_Fd_Handler *fd_handler)
1240 struct ss_main_data *ad = (struct ss_main_data *)data;
1241 unsigned int mem_state;
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;
1248 fd = ecore_main_fd_handler_fd_get(fd_handler);
1250 _E("ecore_main_fd_handler_fd_get error, return\n");
1251 return ECORE_CALLBACK_CANCEL;
1254 mem_state = lowmem_read(fd);
1255 if (mem_state == -1) {
1257 _E("error lowmem_read, restart lowmem fd");
1259 return ECORE_CALLBACK_CANCEL;
1262 print_lowmem_state(mem_state);
1263 lowmem_process(mem_state, ad);
1265 return ECORE_CALLBACK_RENEW;
1268 static int lowmem_fd_start(void)
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;
1275 _D("lowmem_fd_start open /dev/lowmemnotify sucess\n");
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,
1282 _E("error ecore_main_fd_handler_add in lowmem_fd_start\n");
1283 return RESOURCED_ERROR_FAIL;
1288 int lowmem_init(void)
1290 int ret = RESOURCED_ERROR_NONE;
1292 /* set default memcg value */
1295 _E("memory cgroup init failed");
1296 return RESOURCED_ERROR_FAIL;
1299 ret = lowmem_fd_start();
1301 _E("lowmem_fd_start fail\n");
1302 return RESOURCED_ERROR_FAIL;
1305 /* set threshold level 1, level 2, leave threshold */
1306 ret = lowmem_set_threshold();
1308 _E("lowmem_set_threshold fail\n");
1309 return RESOURCED_ERROR_FAIL;
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;
1319 ecore_main_fd_handler_add(lowmem_fd, ECORE_FD_READ,
1320 (Ecore_Fd_Cb)lowmem_cb, NULL, NULL, NULL);
1322 _I("lowmem_swaptype : %d", swap_status(SWAP_GET_TYPE, NULL));
1329 static int lowmem_fd_stop(int fd)
1332 ecore_main_fd_handler_del(lowmem_efd);
1342 static int resourced_memory_control(void *data)
1344 int ret = RESOURCED_ERROR_NONE;
1345 struct lowmem_data_type *l_data;
1347 l_data = (struct lowmem_data_type *)data;
1348 switch(l_data->control_type) {
1349 case LOWMEM_MOVE_CGROUP:
1351 lowmem_move_memcgroup((pid_t)l_data->args[0], l_data->args[1]);
1353 case LOWMEM_MANAGE_FOREGROUND:
1355 lowmem_cgroup_foregrd_manage((pid_t)l_data->args[0]);
1362 static int resourced_memory_init(void *data)
1364 lowmem_ops = &memory_modules_ops;
1366 return lowmem_init();
1369 static int resourced_memory_finalize(void *data)
1371 return RESOURCED_ERROR_NONE;
1374 int lowmem_control(enum lowmem_control_type type, unsigned long *args)
1376 struct lowmem_data_type l_data;
1379 l_data.control_type = type;
1381 return lowmem_ops->control(&l_data);
1384 return RESOURCED_ERROR_NONE;
1387 static const struct module_ops memory_modules_ops = {
1388 .priority = MODULE_PRIORITY_NORMAL,
1390 .init = resourced_memory_init,
1391 .exit = resourced_memory_finalize,
1392 .control = resourced_memory_control,
1395 MODULE_REGISTER(&memory_modules_ops)