cpu-sched: Add background notifiers for fixed oom score app
[platform/core/system/resourced.git] / src / process / proc-process.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2000 - 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 #include <ctype.h>
21 #include <stdio.h>
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <sys/types.h>
28
29 #include "freezer.h"
30 #include "resourced.h"
31 #include "trace.h"
32 #include "proc-main.h"
33 #include "cgroup.h"
34 #include "proc-process.h"
35 #include "procfs.h"
36 #include "memory-cgroup.h"
37 #include "macro.h"
38 #include "notifier.h"
39 #include "proc-appusage.h"
40 #include "safe-kill.h"
41
42 #define PROC_SWEEP_TIMER        3
43 static GHashTable *proc_sweep_list;
44 static GSource *proc_sweep_timer;
45
46 enum proc_background_type {
47         PROC_BACKGROUND_INACTIVE,
48         PROC_BACKGROUND_ACTIVE,
49 };
50
51 int proc_set_service_oomscore(const pid_t pid, const int oom_score, struct proc_app_info *pai)
52 {
53         int service_oom;
54         if (oom_score > 0 && oom_score != OOMADJ_SERVICE_DEFAULT)
55                 service_oom = oom_score - OOMADJ_SERVICE_GAP;
56         else
57                 service_oom = OOMADJ_SERVICE_DEFAULT;
58         return proc_set_oom_score_adj(pid, service_oom, pai);
59 }
60
61 static void proc_set_oom_score_childs(struct proc_app_info *pai, int oom_score_adj)
62 {
63         GSList *iter;
64
65         if(!pai)
66                 return;
67
68         if (!pai->childs)
69                 return;
70
71         gslist_for_each_item(iter, pai->childs) {
72                 proc_set_oom_score_adj(GPOINTER_TO_PID(iter->data), oom_score_adj, pai);
73         }
74 }
75
76 static void proc_set_oom_score_services(int state, GSList *svcs,
77                 int oom_score_adj)
78 {
79         GSList *iter;
80
81         if (!svcs)
82                 return;
83
84         gslist_for_each_item(iter, svcs) {
85                 struct proc_app_info *svc = (struct proc_app_info *)(iter->data);
86                 svc->state = state;
87                 proc_set_service_oomscore(svc->main_pid, oom_score_adj, svc);
88         }
89 }
90
91 static int proc_get_lowest_oom_score(GSList *uiapps)
92 {
93         GSList *iter;
94         int min_oom_score = OOMADJ_APP_MAX;
95         int oom_score_adj = 0, ret;
96
97         if (!uiapps)
98                 return OOMADJ_SU;
99
100         gslist_for_each_item(iter, uiapps) {
101                 struct proc_app_info *pai = (struct proc_app_info *)(iter->data);
102
103                 ret = proc_get_oom_score_adj(pai->main_pid, &oom_score_adj);
104                 if (ret)
105                         continue;
106                 min_oom_score = (oom_score_adj < min_oom_score) ? oom_score_adj : min_oom_score;
107         }
108
109         return min_oom_score;
110 }
111
112 static int proc_backgrd_manage(int currentpid, int active, int oom_score_adj, struct proc_app_info *pai)
113 {
114         pid_t pid = -1;
115         int flag = RESOURCED_NOTIFIER_APP_BACKGRD;
116         struct proc_status ps;
117         FILE *fp;
118         char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
119         int cur_oom = -1;
120         static int checkprevpid;
121         int freeze_val = 0;
122         GSList *iter;
123         struct proc_program_info *ppi;
124         int is_favorite = 0;
125
126         if (proc_get_freezer_status() != CGROUP_FREEZER_DISABLED)
127                 freeze_val = resourced_freezer_proc_late_control();
128
129         if (!pai) {
130                 _E("can't find app info about pid %d", currentpid);
131                 return RESOURCED_ERROR_INVALID_PARAMETER;
132         }
133
134         /*
135          * About groupd process with multiple applications,
136          * all application with same group could be went to background state.
137          * If one application has already managed to background application
138          * it skipped to go to the background again.
139          */
140         if (pai->lru_state >= PROC_BACKGROUND) {
141                 _D("pid %d already in background", currentpid);
142                 return RESOURCED_ERROR_NONE;
143         }
144
145         ps.pid = currentpid;
146         ps.pai = pai;
147         if (active)
148                 flag = RESOURCED_NOTIFIER_APP_BACKGRD_ACTIVE;
149         resourced_notify(flag, &ps);
150
151         if (active)
152                 goto set_oom;
153
154         pai->lru_state = PROC_BACKGROUND;
155         if (proc_check_favorite_app(pai->appid)) {
156                 _D("detect favorite application : %s", pai->appid);
157                 oom_score_adj = OOMADJ_FAVORITE;
158                 pai->lru_state = PROC_FAVORITE;
159                 is_favorite = 1;
160         }
161
162         if (checkprevpid != currentpid) {
163                 _cleanup_app_list_close_ GSList *proc_app_list = PAL_INIT_VALUE;
164
165                 proc_app_list = proc_app_list_open();
166                 gslist_for_each_item(iter, proc_app_list) {
167                         struct proc_app_info *spi = (struct proc_app_info *)iter->data;
168                         int new_oom;
169                         int lru_offset = freeze_val;
170
171                         if (!spi->main_pid || spi->type != PROC_TYPE_GUI)
172                                 continue;
173
174                         /*
175                          * It checks all applications from proc_app_list
176                          * So, current background process can be checked again.
177                          * It is because lru value of this application was already changed,
178                          * it should be skipped
179                          */
180
181                         if (spi == pai)
182                                 continue;
183
184                         pid = spi->main_pid;
185                         snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
186                         fp = fopen(buf, "r+");
187                         if (fp == NULL) {
188                                 spi->main_pid = 0;
189                                 continue;
190                         }
191                         if (fgets(buf, sizeof(buf), fp) == NULL) {
192                                 fclose(fp);
193                                 spi->main_pid = 0;
194                                 continue;
195                         }
196                         cur_oom = atoi(buf);
197
198                         if (spi->lru_state == PROC_BACKGROUND ||
199                                         spi->lru_state == PROC_FAVORITE) {
200                                 memset(&ps, 0, sizeof(struct proc_status));
201                                 ps.pai = spi;
202                                 ps.pid = pid;
203                                 resourced_notify(
204                                             RESOURCED_NOTIFIER_APP_SUSPEND_READY,
205                                             &ps);
206                         }
207                         /*
208                          * clear lru offset if platform controls background application
209                          */
210                         if (spi->flags & PROC_BGCTRL_PLATFORM)
211                                 lru_offset = 0;
212
213                         if (proc_check_lru_suspend(lru_offset, spi->lru_state) &&
214                             (proc_check_suspend_state(spi) == PROC_STATE_SUSPEND)) {
215                                 memset(&ps, 0, sizeof(struct proc_status));
216                                 ps.pai = spi;
217                                 ps.pid = pid;
218                                 resourced_notify(
219                                             RESOURCED_NOTIFIER_APP_SUSPEND,
220                                             &ps);
221                         }
222
223                         if (is_favorite) {
224                                 if (spi->lru_state >= PROC_FAVORITE &&
225                                                 spi->lru_state < PROC_FAVORITE_LRU_MAX) {
226                                         spi->lru_state++;
227                                         _D("FAVORITE : process %d increase lru %d",
228                                                         pid, spi->lru_state - PROC_FAVORITE + 1);
229                                 }
230
231                                 if (cur_oom >= OOMADJ_FAVORITE &&
232                                                 cur_oom < OOMADJ_FAVORITE_APP_MAX) {
233                                         new_oom = cur_oom + OOMADJ_FAVORITE_APP_INCREASE;
234                                         _D("FAVORITE : process %d set score %d (beford %d)",
235                                                         pid, new_oom, cur_oom);
236                                         proc_set_oom_score_adj(pid, new_oom, spi);
237                                         proc_set_oom_score_childs(spi, new_oom);
238                                 }
239                         } else {
240                                 if (spi->lru_state >= PROC_BACKGROUND &&
241                                                 spi->lru_state < PROC_LRU_MAX) {
242                                         spi->lru_state++;
243                                         _D("BACKGRD : process %d increase lru %d", pid, spi->lru_state);
244                                 }
245
246                                 if (cur_oom >= OOMADJ_APP_MAX) {
247                                         fclose(fp);
248                                         continue;
249                                 } else if (cur_oom >= OOMADJ_BACKGRD_UNLOCKED) {
250                                         new_oom = cur_oom + OOMADJ_APP_INCREASE;
251                                         _D("BACKGRD : process %d set score %d (before %d)",
252                                                         pid, new_oom, cur_oom);
253                                         proc_set_oom_score_adj(pid, new_oom, spi);
254                                         proc_set_oom_score_childs(spi, new_oom);
255                                 }
256                         }
257                         fclose(fp);
258                 }
259         }
260
261 set_oom:
262         proc_set_oom_score_adj(pai->main_pid, oom_score_adj, pai);
263
264         /* change oom score about child pids */
265         proc_set_oom_score_childs(pai, oom_score_adj);
266
267         /* change oom score about grouped service processes */
268         ppi = pai->program;
269         if (ppi && proc_get_svc_state(ppi) == PROC_STATE_BACKGROUND)
270                 proc_set_oom_score_services(PROC_STATE_BACKGROUND, ppi->svc_list,
271                         (proc_get_lowest_oom_score(ppi->app_list) >= oom_score_adj) ? OOMADJ_BACKGRD_UNLOCKED : OOMADJ_SERVICE_DEFAULT);
272
273         checkprevpid = currentpid;
274         return RESOURCED_ERROR_NONE;
275 }
276
277 static int proc_foregrd_manage(int pid, int oom_score_adj, struct proc_app_info *pai)
278 {
279         int ret = 0;
280         struct proc_program_info *ppi;
281
282         if (!pai) {
283                 _E("process app info is NULL");
284                 proc_set_oom_score_adj(pid, oom_score_adj, pai);
285                 return RESOURCED_ERROR_NO_DATA;
286         }
287
288         proc_set_oom_score_adj(pai->main_pid, oom_score_adj, pai);
289
290         /* change oom score about child pids */
291         proc_set_oom_score_childs(pai, oom_score_adj);
292
293         pai->lru_state = PROC_FOREGROUND;
294
295         /* change oom score about grouped service processes */
296         ppi = pai->program;
297         if (ppi)
298                 proc_set_oom_score_services(PROC_STATE_FOREGROUND, ppi->svc_list,
299                     oom_score_adj);
300
301         return ret;
302 }
303
304 void proc_kill_victiom(gpointer key, gpointer value, gpointer user_data)
305 {
306         int pid = *(gint *)key;
307         int cur_oom = -1;
308         char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
309         FILE *fp;
310
311         snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
312         fp = fopen(buf, "r+");
313         if (fp == NULL) {
314                 _D("sweep proc_kill_victim : background process %d already terminated", pid);
315                 return;
316         }
317         if (fgets(buf, sizeof(buf), fp) == NULL) {
318                 fclose(fp);
319                 return;
320         }
321         cur_oom = atoi(buf);
322         if (cur_oom >= OOMADJ_FAVORITE) {
323                 safe_kill(pid, SIGKILL);
324                 _D("sweep memory : background process %d killed by sigkill", pid);
325         }
326         fclose(fp);
327 }
328
329 static gboolean proc_check_sweep_cb(gpointer data)
330 {
331         GHashTable *List = (GHashTable *)data;
332         g_hash_table_foreach(List, proc_kill_victiom, NULL);
333         g_hash_table_destroy(List);
334         proc_sweep_list = NULL;
335
336         return false;
337 }
338
339 int proc_sweep_memory(enum proc_sweep_type type, pid_t callpid)
340 {
341         _cleanup_app_list_close_ GSList *proc_app_list = PAL_INIT_VALUE;
342         pid_t pid = -1;
343         int count = 0, ret;
344         FILE *fp;
345         gint *piddata;
346         char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
347         char appname[PROC_NAME_MAX];
348
349         int cur_oom = -1;
350         int select_sweep_limit;
351         GSList *iter;
352         struct proc_app_info *pai;
353
354         if (proc_sweep_timer)
355                 g_source_destroy(proc_sweep_timer);
356         if (proc_sweep_list)
357                 g_hash_table_destroy(proc_sweep_list);
358         proc_sweep_list = g_hash_table_new(g_int_hash, g_int_equal);
359
360         if (type == PROC_SWEEP_EXCLUDE_ACTIVE)
361                 select_sweep_limit = OOMADJ_FAVORITE;
362         else
363                 select_sweep_limit = OOMADJ_BACKGRD_LOCKED;
364
365         proc_app_list = proc_app_list_open();
366         gslist_for_each_item(iter, proc_app_list) {
367                 pai = (struct proc_app_info *)iter->data;
368                 if (!pai->main_pid || pai->type != PROC_TYPE_GUI)
369                         continue;
370
371                 pid = pai->main_pid;
372
373                 snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
374                 fp = fopen(buf, "r+");
375                 if (fp == NULL)
376                         continue;
377                 if (fgets(buf, sizeof(buf), fp) == NULL) {
378                         fclose(fp);
379                         continue;
380                 }
381                 cur_oom = atoi(buf);
382                 if (cur_oom >= select_sweep_limit) {
383                         ret = proc_get_cmdline(pid, appname, sizeof appname);
384                         if (ret != 0) {
385                                 fclose(fp);
386                                 continue;
387                         }
388                         resourced_proc_status_change(PROC_CGROUP_SET_TERMINATE_REQUEST,
389                                     pid, NULL, NULL, PROC_TYPE_NONE);
390                         piddata = g_new(gint, 1);
391                         *piddata = pid;
392                         g_hash_table_insert(proc_sweep_list, piddata, NULL);
393                         safe_kill(pid, SIGTERM);
394                         _D("sweep memory : background process %d(%s) killed",
395                                         pid, appname);
396                         count++;
397                 }
398                 fclose(fp);
399         }
400         if (count > 0) {
401                 proc_sweep_timer = g_timeout_source_new_seconds(PROC_SWEEP_TIMER);
402                 g_source_set_callback(proc_sweep_timer, proc_check_sweep_cb, (gpointer)proc_sweep_list, NULL);
403                 g_source_attach(proc_sweep_timer, NULL);
404         }
405
406         return count;
407 }
408
409 int proc_set_foregrd(pid_t pid, int oom_score_adj, struct proc_app_info *pai)
410 {
411         int ret = 0;
412
413         switch (oom_score_adj) {
414         case OOMADJ_FOREGRD_UNLOCKED:
415         case OOMADJ_SU:
416                 ret = 0;
417                 break;
418         case OOMADJ_FOREGRD_LOCKED:
419         case OOMADJ_BACKGRD_LOCKED:
420                 ret = proc_foregrd_manage(pid, OOMADJ_FOREGRD_LOCKED, pai);
421                 break;
422         case OOMADJ_BACKGRD_UNLOCKED:
423         case OOMADJ_INIT:
424                 ret = proc_foregrd_manage(pid, OOMADJ_FOREGRD_UNLOCKED, pai);
425                 break;
426         default:
427                 if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED ||
428                                 (oom_score_adj >= OOMADJ_FAVORITE &&
429                                  oom_score_adj <= OOMADJ_FAVORITE_APP_MAX))
430                         ret = proc_foregrd_manage(pid, OOMADJ_FOREGRD_UNLOCKED, pai);
431                 else
432                         ret = -1;
433                 break;
434
435         }
436         return ret;
437 }
438
439 static int check_oom_score_for_backgrd(int pid, int oom_score_adj, struct proc_app_info *pai)
440 {
441         struct proc_status ps;
442
443         ps.pid = pid;
444         ps.pai = pai;
445
446         if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED)
447                 return 0;
448
449         if (ps.pid < 0)
450                 return -1;
451
452         if (ps.pai == NULL)
453                 return -1;
454
455         if (proc_oom_priority_is_oom_fixed_process(pid)) {
456                 resourced_notify(RESOURCED_NOTIFIER_APP_BACKGRD_WITH_FIXED_OOM_SCORE, &ps);
457                 return 0;
458         }
459
460         if (oom_score_adj == OOMADJ_BACKGRD_PERCEPTIBLE) {
461                 resourced_notify(RESOURCED_NOTIFIER_APP_BACKGRD_WITH_FIXED_OOM_SCORE, &ps);
462                 return 0;
463         }
464
465         return -1;
466 }
467
468 int proc_set_backgrd(int pid, int oom_score_adj, struct proc_app_info *pai)
469 {
470         int ret = 0;
471
472         switch (oom_score_adj) {
473         case OOMADJ_BACKGRD_LOCKED:
474         case OOMADJ_BACKGRD_UNLOCKED:
475         case OOMADJ_SU:
476                 ret = -1;
477                 break;
478         case OOMADJ_FOREGRD_LOCKED:
479                 ret = proc_backgrd_manage(pid, PROC_BACKGROUND_ACTIVE, OOMADJ_BACKGRD_LOCKED, pai);
480                 break;
481         case OOMADJ_FOREGRD_UNLOCKED:
482         case OOMADJ_INIT:
483                 ret = proc_backgrd_manage(pid, PROC_BACKGROUND_INACTIVE, OOMADJ_BACKGRD_UNLOCKED, pai);
484                 break;
485         default:
486                 ret = check_oom_score_for_backgrd(pid, oom_score_adj, pai);
487                 break;
488         }
489         return ret;
490 }
491
492 int proc_set_active(int pid, int oom_score_adj)
493 {
494         int ret = 0;
495         struct proc_app_info *pai;
496
497         switch (oom_score_adj) {
498         case OOMADJ_FOREGRD_LOCKED:
499         case OOMADJ_BACKGRD_LOCKED:
500         case OOMADJ_SU:
501                 /* don't change oom value pid */
502                 ret = -1;
503                 break;
504         case OOMADJ_INIT:
505         case OOMADJ_FOREGRD_UNLOCKED:
506                 ret = proc_set_oom_score_adj(pid, OOMADJ_FOREGRD_LOCKED, find_app_info(pid));
507                 break;
508         case OOMADJ_BACKGRD_UNLOCKED:
509                 pai = find_app_info(pid);
510                 if (pai)
511                         pai->lru_state = PROC_ACTIVE;
512                 ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_LOCKED, pai);
513                 break;
514         case OOMADJ_PREVIOUS_BACKGRD:
515                 ret = proc_set_oom_score_adj(pid, OOMADJ_PREVIOUS_DEFAULT, find_app_info(pid));
516                 break;
517         default:
518                 if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED) {
519                         pai = find_app_info(pid);
520                         if (pai)
521                                 pai->lru_state = PROC_ACTIVE;
522                         ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_LOCKED, pai);
523                 } else
524                         ret = -1;
525                 break;
526         }
527         return ret;
528 }
529
530 int proc_set_inactive(int pid, int oom_score_adj)
531 {
532         int ret = 0;
533         struct proc_app_info *pai;
534         switch (oom_score_adj) {
535         case OOMADJ_FOREGRD_UNLOCKED:
536         case OOMADJ_BACKGRD_UNLOCKED:
537         case OOMADJ_SU:
538         case OOMADJ_INIT:
539                 /* don't change oom value pid */
540                 ret = -1;
541                 break;
542         case OOMADJ_FOREGRD_LOCKED:
543                 ret = proc_set_oom_score_adj(pid, OOMADJ_FOREGRD_UNLOCKED, find_app_info(pid));
544                 break;
545         case OOMADJ_BACKGRD_LOCKED:
546                 pai = find_app_info(pid);
547                 if (pai) {
548                         struct proc_status ps = {0};
549                         ps.pid = pid;
550                         ps.pai = pai;
551                         pai->lru_state = PROC_BACKGROUND;
552                         ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_UNLOCKED, pai);
553                         resourced_notify(RESOURCED_NOTIFIER_APP_BACKGRD, &ps);
554                         struct proc_program_info *const ppi = pai->program;
555                         if (ppi && proc_get_svc_state(ppi) == PROC_STATE_BACKGROUND)
556                                 proc_set_oom_score_services(PROC_STATE_BACKGROUND, ppi->svc_list,
557                                         (proc_get_lowest_oom_score(ppi->app_list) >= oom_score_adj) ? OOMADJ_BACKGRD_UNLOCKED : OOMADJ_SERVICE_DEFAULT);
558                 }
559                 break;
560         default:
561                 if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED)
562                         ret = 0;
563                 else
564                         ret = -1;
565                 break;
566
567         }
568         return ret;
569 }