4 * Copyright (c) 2015-2019 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.
21 /* For asprintf(3). Only affects this one file
22 * because tests dislike GNU_SOURCE since it
23 * makes mockability difficult. */
31 #include <sys/types.h>
38 #include "module-data.h"
39 #include "lowmem-handler.h"
44 #include "proc-common.h"
45 #include "memory-cgroup.h"
48 #include "config-parser.h"
50 static GSource *lowmem_checksystemd_timer;
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"
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.
63 static int search_systemd_cgroup(const char *dir)
65 _cleanup_closedir_ DIR *d = NULL;
68 int rootswappiness, memcgswappiness;
69 int changeswappiness = -1;
73 _D("Failed to open dir: %s", dir);
77 ret = cgroup_read_node_int32(MEMCG_PATH, MEMCG_SWAPPINESS,
79 if (ret != RESOURCED_ERROR_NONE)
82 ret = cgroup_read_node_int32(dir, MEMCG_SWAPPINESS,
84 if (ret != RESOURCED_ERROR_NONE)
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);
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.
104 FOREACH_DIRENT(de, d, return -errno) {
105 _cleanup_free_ char *path = NULL;
107 unsigned long long limit_bytes;
109 if (de->d_type != DT_DIR)
112 ret = asprintf(&path, "%s/%s", dir, de->d_name);
116 ret = cgroup_read_node_ulonglong(path, MEMCG_LIMIT_BYTE, &limit_bytes);
117 if (ret != RESOURCED_ERROR_NONE ||limit_bytes <= 0)
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);
128 lowmem_reassign_limit(path, limit_bytes, PROC_ACTION_KILL);
130 return RESOURCED_ERROR_NONE;
133 static gboolean checksystemd_cb(gpointer data)
135 search_systemd_cgroup(MEMORY_SYSTEMD_SYSTEM);
136 search_systemd_cgroup(MEMORY_SYSTEMD_USER);
138 static bool first = true;
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);
150 static int booting_done(void *data)
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);
157 return RESOURCED_ERROR_NONE;
160 void lowmem_system_init()
162 register_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, booting_done);
165 void lowmem_system_exit(void)
167 if (lowmem_checksystemd_timer) {
168 g_source_destroy(lowmem_checksystemd_timer);
169 lowmem_checksystemd_timer = NULL;
171 unregister_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, booting_done);