Add unit(in variable) & fix bugs
[platform/core/system/resourced.git] / src / resource-limiter / memory / lowmem-system.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2015-2019 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 #ifndef _GNU_SOURCE
21         /* For asprintf(3). Only affects this one file
22          * because tests dislike GNU_SOURCE since it
23          * makes mockability difficult. */
24         #define _GNU_SOURCE
25         #include <stdio.h>
26         #undef _GNU_SOURCE
27 #else
28         #include <stdio.h>
29 #endif
30
31 #include <sys/types.h>
32 #include <signal.h>
33 #include <unistd.h>
34
35 #include "trace.h"
36 #include "macro.h"
37 #include "module.h"
38 #include "module-data.h"
39 #include "lowmem-handler.h"
40 #include "notifier.h"
41 #include "procfs.h"
42 #include "cgroup.h"
43 #include "const.h"
44 #include "proc-common.h"
45 #include "memory-cgroup.h"
46 #include "util.h"
47 #include "smaps.h"
48 #include "config-parser.h"
49
50 static GSource *lowmem_checksystemd_timer;
51
52 #define CHECKLIMIT_FIRST_INTERVAL         30
53 #define CHECKLIMIT_INTERVAL               24*3600
54 #define MEMORY_SYSTEMD_SYSTEM   "/sys/fs/cgroup/memory/system.slice"
55 #define MEMORY_SYSTEMD_USER     "/sys/fs/cgroup/memory/user.slice"
56
57 /*
58  * Resourced couldn't control any systemd services
59  * even though some systemd services set either the memory limit or the oom score value.
60  * If resourced controls all processes including systemd services,
61  * it has to check system.slice of the memcg.
62  */
63 static int search_systemd_cgroup(const char *dir)
64 {
65         _cleanup_closedir_ DIR *d = NULL;
66         struct dirent *de;
67         int ret;
68         int rootswappiness, memcgswappiness;
69         int changeswappiness = -1;
70
71         d = opendir(dir);
72         if (!d) {
73                 _D("Failed to open dir: %s", dir);
74                 return -errno;
75         }
76
77         ret = cgroup_read_node_int32(MEMCG_PATH, MEMCG_SWAPPINESS,
78                         &rootswappiness);
79         if (ret != RESOURCED_ERROR_NONE)
80                 return -errno;
81
82         ret = cgroup_read_node_int32(dir, MEMCG_SWAPPINESS,
83                         &memcgswappiness);
84         if (ret != RESOURCED_ERROR_NONE)
85                 return -errno;
86
87         if ((rootswappiness >=0) && (rootswappiness != memcgswappiness)) {
88                 changeswappiness = rootswappiness;
89                 ret = cgroup_write_node_uint32(dir,
90                         MEMCG_SWAPPINESS, changeswappiness);
91                 if (ret != RESOURCED_ERROR_NONE) {
92                         _I("failed to write %s %d to %s the",
93                                 MEMCG_SWAPPINESS, changeswappiness, dir);
94                         return -errno;
95                 }
96         }
97
98         /*
99          * set memory limit
100          * The purpose of this operation is to make debug information
101          * when systemd services spent much memory more than limit.
102          * And it can even track the amount of swapped memory.
103          */
104         FOREACH_DIRENT(de, d, return -errno) {
105                 _cleanup_free_ char *path = NULL;
106
107                 unsigned long long limit_bytes;
108
109                 if (de->d_type != DT_DIR)
110                         continue;
111
112                 ret = asprintf(&path, "%s/%s", dir, de->d_name);
113                 if (ret < 0)
114                         return -ENOMEM;
115
116                 ret = cgroup_read_node_ulonglong(path, MEMCG_LIMIT_BYTE, &limit_bytes);
117                 if (ret != RESOURCED_ERROR_NONE ||limit_bytes <= 0)
118                         continue;
119
120                 if (changeswappiness >= 0) {
121                         ret = cgroup_write_node_uint32(path,
122                                         MEMCG_SWAPPINESS, changeswappiness);
123                         if (ret != RESOURCED_ERROR_NONE)
124                                 _I("failed to write %s %d to %s the",
125                                         MEMCG_SWAPPINESS, changeswappiness, path);
126                 }
127
128                 lowmem_reassign_limit(path, limit_bytes, PROC_ACTION_KILL);
129         }
130         return RESOURCED_ERROR_NONE;
131 }
132
133 static gboolean checksystemd_cb(gpointer data)
134 {
135         search_systemd_cgroup(MEMORY_SYSTEMD_SYSTEM);
136         search_systemd_cgroup(MEMORY_SYSTEMD_USER);
137
138         static bool first = true;
139         if (first) {
140                 first = false; // pop this cherry
141                 g_source_destroy(lowmem_checksystemd_timer);
142                 lowmem_checksystemd_timer = g_timeout_source_new_seconds(CHECKLIMIT_INTERVAL);
143                 g_source_set_callback(lowmem_checksystemd_timer, checksystemd_cb, NULL, NULL);
144                 g_source_attach(lowmem_checksystemd_timer, NULL);
145                 return false;
146         }
147         return true;
148 }
149
150 static int booting_done(void *data)
151 {
152         lowmem_checksystemd_timer =
153                 g_timeout_source_new_seconds(CHECKLIMIT_FIRST_INTERVAL);
154         g_source_set_callback(lowmem_checksystemd_timer, checksystemd_cb, NULL, NULL);
155         g_source_attach(lowmem_checksystemd_timer, NULL);
156
157         return RESOURCED_ERROR_NONE;
158 }
159
160 void lowmem_system_init()
161 {
162         register_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, booting_done);
163 }
164
165 void lowmem_system_exit(void)
166 {
167         if (lowmem_checksystemd_timer) {
168                 g_source_destroy(lowmem_checksystemd_timer);
169                 lowmem_checksystemd_timer = NULL;
170         }
171         unregister_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, booting_done);
172 }