252e0e4facfd62aec0c7711f52218bb610561965
[platform/core/system/system-server.git] / ss_lowmem_handler.c
1 /*
2  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include <fcntl.h>
19 #include <assert.h>
20 #include <limits.h>
21 #include <sysman.h>
22 #include <heynoti.h>
23 #include <vconf.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/shm.h>
27
28 #include "ss_device_plugin.h"
29 #include "ss_log.h"
30 #include "ss_noti.h"
31 #include "ss_queue.h"
32 #include "include/ss_data.h"
33
34 #define DELETE_SM               "sh -c "PREFIX"/bin/delete.sm"
35
36 #define MEMNOTIFY_NORMAL        0x0000
37 #define MEMNOTIFY_LOW           0xfaac
38 #define MEMNOTIFY_CRITICAL      0xdead
39 #define MEMNOTIFY_REBOOT        0xb00f
40
41 #define _SYS_RES_CLEANUP        "RES_CLEANUP"
42
43 #define MEM_THRESHOLD_LV1       60
44 #define MEM_THRESHOLD_LV2       40
45
46
47 struct lowmem_process_entry {
48         unsigned cur_mem_state;
49         unsigned new_mem_state;
50         int (*action) (void *);
51 };
52
53 static int lowmem_fd = -1;
54 static int cur_mem_state = MEMNOTIFY_NORMAL;
55
56 Ecore_Timer *oom_timer;
57 #define OOM_TIMER_INTERVAL      5
58
59 static int memory_low_act(void *ad);
60 static int memory_oom_act(void *ad);
61 static int memory_normal_act(void *ad);
62
63 static struct lowmem_process_entry lpe[] = {
64         {MEMNOTIFY_NORMAL, MEMNOTIFY_LOW, memory_low_act},
65         {MEMNOTIFY_NORMAL, MEMNOTIFY_CRITICAL, memory_oom_act},
66         {MEMNOTIFY_LOW, MEMNOTIFY_CRITICAL, memory_oom_act},
67         {MEMNOTIFY_CRITICAL, MEMNOTIFY_CRITICAL, memory_oom_act},
68         {MEMNOTIFY_LOW, MEMNOTIFY_NORMAL, memory_normal_act},
69         {MEMNOTIFY_CRITICAL, MEMNOTIFY_NORMAL, memory_normal_act},
70
71 };
72
73 unsigned int oom_delete_sm_time = 0;
74
75 static int remove_shm()
76 {
77         int maxid, shmid, id;
78         struct shmid_ds shmseg;
79         struct shm_info shm_info;
80
81         maxid = shmctl(0, SHM_INFO, (struct shmid_ds *)(void *)&shm_info);
82         if (maxid < 0) {
83                 PRT_TRACE_ERR("shared mem error\n");
84                 return -1;
85         }
86
87         for (id = 0; id <= maxid; id++) {
88                 shmid = shmctl(id, SHM_STAT, &shmseg);
89                 if (shmid < 0)
90                         continue;
91                 if (shmseg.shm_nattch == 0) {
92                         PRT_TRACE("shared memory killer ==> %d killed\n",
93                                   shmid);
94                         shmctl(shmid, IPC_RMID, NULL);
95                 }
96         }
97         return 0;
98 }
99
100 static char *convert_to_str(unsigned int mem_state)
101 {
102         char *tmp;
103         switch (mem_state) {
104         case MEMNOTIFY_NORMAL:
105                 tmp = "mem normal";
106                 break;
107         case MEMNOTIFY_LOW:
108                 tmp = "mem low";
109                 break;
110         case MEMNOTIFY_CRITICAL:
111                 tmp = "mem critical";
112                 break;
113         case MEMNOTIFY_REBOOT:
114                 tmp = "mem reboot";
115                 break;
116         default:
117                 assert(0);
118         }
119         return tmp;
120 }
121
122 static void print_lowmem_state(unsigned int mem_state)
123 {
124         PRT_TRACE("[LOW MEM STATE] %s ==> %s", convert_to_str(cur_mem_state),
125                   convert_to_str(mem_state));
126 }
127 #define BUF_MAX 1024
128 static int get_lowmemnotify_info(FILE *output_fp)
129 {
130         FILE *fp;
131         char line[BUF_MAX];
132
133         if (output_fp == NULL)
134                 return -1;
135
136         fp = fopen("/sys/class/memnotify/meminfo", "r");
137         if (fp == NULL)
138                 return -1;
139         PRT_TRACE("make LOWMEM_LOG");
140         fprintf(output_fp,
141                 "====================================================================\n");
142         fprintf(output_fp, "MEMORY INFO by lowmemnotify\n");
143
144         while (fgets(line, BUF_MAX, fp) != NULL) {
145                 PRT_TRACE("%s",line);
146                 fputs(line, output_fp);
147         }
148         fclose(fp);
149
150         return 0;
151 }
152
153 static void make_LMM_log(char *file, pid_t pid, char *victim_name)
154 {
155         time_t now;
156         struct tm *cur_tm;
157         char new_log[NAME_MAX];
158         static pid_t old_pid = 0;
159         int ret=-1;
160         FILE *output_file = NULL;
161
162         if (old_pid == pid)
163                 return;
164         old_pid = pid;
165
166         now = time(NULL);
167         cur_tm = (struct tm *)malloc(sizeof(struct tm));
168         if (cur_tm == NULL) {
169                 PRT_TRACE_ERR("Fail to memory allocation");
170                 return;
171         }
172
173         if (localtime_r(&now, cur_tm) == NULL) {
174                 PRT_TRACE_ERR("Fail to get localtime");
175                 free(cur_tm);
176                 return;
177         }
178
179         PRT_TRACE("%s_%s_%d_%.4d%.2d%.2d_%.2d%.2d%.2d.log", file, victim_name,
180                  pid, (1900 + cur_tm->tm_year), 1 + cur_tm->tm_mon,
181                  cur_tm->tm_mday, cur_tm->tm_hour, cur_tm->tm_min,
182                  cur_tm->tm_sec);
183         snprintf(new_log, sizeof(new_log),
184                  "%s_%s_%d_%.4d%.2d%.2d_%.2d%.2d%.2d.log", file, victim_name,
185                  pid, (1900 + cur_tm->tm_year), 1 + cur_tm->tm_mon,
186                  cur_tm->tm_mday, cur_tm->tm_hour, cur_tm->tm_min,
187                  cur_tm->tm_sec);
188
189         output_file = fopen(new_log, "w+");
190         if(!output_file) {
191                 PRT_TRACE_ERR("cannot open output file(%s)",new_log);
192                 free(cur_tm);
193                 return;
194         }
195         get_lowmemnotify_info(output_file);
196         fclose(output_file);
197         free(cur_tm);
198 }
199
200
201
202 static int memory_low_act(void *data)
203 {
204         char lowmem_noti_name[NAME_MAX];
205
206         PRT_TRACE("[LOW MEM STATE] memory low state");
207         make_LMM_log("/var/log/memps", 1, "LOWMEM_WARNING");
208         remove_shm();
209
210         heynoti_get_snoti_name(_SYS_RES_CLEANUP, lowmem_noti_name, NAME_MAX);
211         ss_noti_send(lowmem_noti_name);
212         vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
213                       VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING);
214
215         return 0;
216 }
217
218 static int memory_oom_act(void *data)
219 {
220         unsigned int cur_time;
221         char lowmem_noti_name[NAME_MAX];
222
223         PRT_TRACE("[LOW MEM STATE] memory oom state");
224         cur_time = time(NULL);
225         PRT_TRACE("cur=%d, old=%d, cur-old=%d", cur_time, oom_delete_sm_time,
226                   cur_time - oom_delete_sm_time);
227         if (cur_time - oom_delete_sm_time > 15) {
228                 remove_shm();
229                 oom_delete_sm_time = cur_time;
230                 /* Also clean up unreturned memory of applications */
231                 heynoti_get_snoti_name(_SYS_RES_CLEANUP, lowmem_noti_name,
232                                        NAME_MAX);
233                 ss_noti_send(lowmem_noti_name);
234         }
235         ss_action_entry_call_internal(PREDEF_LOWMEM, 1, OOM_MEM_ACT);
236
237         vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
238                       VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING);
239
240
241         return 1;
242 }
243
244 static int memory_normal_act(void *data)
245 {
246         PRT_TRACE("[LOW MEM STATE] memory normal state");
247         vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
248                       VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
249         return 0;
250 }
251
252 static int lowmem_process(unsigned int mem_state, void *ad)
253 {
254         int i;
255         for (i = 0; i < sizeof(lpe) / sizeof(struct lowmem_process_entry); i++) {
256                 if ((cur_mem_state == lpe[i].cur_mem_state)
257                     && (mem_state == lpe[i].new_mem_state)) {
258
259                         if(oom_timer != NULL) {
260                                 ecore_timer_del(oom_timer);
261                                 oom_timer = NULL;
262                         }
263                         lpe[i].action(ad);
264                         if(mem_state == MEMNOTIFY_CRITICAL) 
265                                 oom_timer = ecore_timer_add(OOM_TIMER_INTERVAL,lpe[i].action, ad);
266                         return 0;
267                 }
268         }
269         return 0;
270 }
271
272 static unsigned int lowmem_read(int fd)
273 {
274         unsigned int mem_state;
275         read(fd, &mem_state, sizeof(mem_state));
276         return mem_state;
277 }
278
279 static int lowmem_cb(void *data, Ecore_Fd_Handler * fd_handler)
280 {
281         int fd;
282         struct ss_main_data *ad = (struct ss_main_data *)data;
283         unsigned int mem_state;
284
285         if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) {
286                 PRT_TRACE_ERR
287                     ("ecore_main_fd_handler_active_get error , return\n");
288                 return 1;
289         }
290
291         fd = ecore_main_fd_handler_fd_get(fd_handler);
292         if (fd < 0) {
293                 PRT_TRACE_ERR("ecore_main_fd_handler_fd_get error , return");
294                 return 1;
295         }
296         mem_state = lowmem_read(fd);
297         print_lowmem_state(mem_state);
298         lowmem_process(mem_state, ad);
299         cur_mem_state = mem_state;
300
301         return 1;
302 }
303
304 static int set_threshold()
305 {
306         if (0 > plugin_intf->OEM_sys_set_memnotify_threshold_lv1(MEM_THRESHOLD_LV1)) {
307                 PRT_TRACE_ERR("Set memnorify threshold lv1 failed");
308                 return -1;
309         }
310
311         if (0 > plugin_intf->OEM_sys_set_memnotify_threshold_lv2(MEM_THRESHOLD_LV2)) {
312                 PRT_TRACE_ERR("Set memnorify threshold lv2 failed");
313                 return -1;
314         }
315
316         return 0;
317 }
318
319 int ss_lowmem_init(struct ss_main_data *ad)
320 {
321         char lowmem_dev_node[PATH_MAX];
322
323         if (0 > plugin_intf->OEM_sys_get_memnotify_node(lowmem_dev_node)) {
324                 PRT_TRACE_ERR("Low memory handler fd init failed");
325                 return -1;
326         }
327
328         lowmem_fd = open(lowmem_dev_node, O_RDONLY);
329         if (lowmem_fd < 0) {
330                 PRT_TRACE_ERR("ss_lowmem_init fd open failed");
331                 return -1;
332         }
333
334         oom_timer = NULL;
335         ecore_main_fd_handler_add(lowmem_fd, ECORE_FD_READ, lowmem_cb, ad, NULL,
336                                   NULL);
337         if (set_threshold() < 0) {
338                 PRT_TRACE_ERR("Setting lowmem threshold is failed");
339                 return -1;
340         }
341
342         return 0;
343 }