Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/hotspot/os/linux/cgroupSubsystem_linux.cpp
40951 views
1
/*
2
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*
23
*/
24
25
#include <string.h>
26
#include <math.h>
27
#include <errno.h>
28
#include "cgroupSubsystem_linux.hpp"
29
#include "cgroupV1Subsystem_linux.hpp"
30
#include "cgroupV2Subsystem_linux.hpp"
31
#include "logging/log.hpp"
32
#include "memory/allocation.hpp"
33
#include "runtime/globals.hpp"
34
#include "runtime/os.hpp"
35
#include "utilities/globalDefinitions.hpp"
36
37
CgroupSubsystem* CgroupSubsystemFactory::create() {
38
CgroupV1MemoryController* memory = NULL;
39
CgroupV1Controller* cpuset = NULL;
40
CgroupV1Controller* cpu = NULL;
41
CgroupV1Controller* cpuacct = NULL;
42
CgroupInfo cg_infos[CG_INFO_LENGTH];
43
u1 cg_type_flags = INVALID_CGROUPS_GENERIC;
44
const char* proc_cgroups = "/proc/cgroups";
45
const char* proc_self_cgroup = "/proc/self/cgroup";
46
const char* proc_self_mountinfo = "/proc/self/mountinfo";
47
48
bool valid_cgroup = determine_type(cg_infos, proc_cgroups, proc_self_cgroup, proc_self_mountinfo, &cg_type_flags);
49
50
if (!valid_cgroup) {
51
// Could not detect cgroup type
52
return NULL;
53
}
54
assert(is_valid_cgroup(&cg_type_flags), "Expected valid cgroup type");
55
56
if (is_cgroup_v2(&cg_type_flags)) {
57
// Cgroups v2 case, we have all the info we need.
58
// Construct the subsystem, free resources and return
59
// Note: any index in cg_infos will do as the path is the same for
60
// all controllers.
61
CgroupController* unified = new CgroupV2Controller(cg_infos[MEMORY_IDX]._mount_path, cg_infos[MEMORY_IDX]._cgroup_path);
62
log_debug(os, container)("Detected cgroups v2 unified hierarchy");
63
cleanup(cg_infos);
64
return new CgroupV2Subsystem(unified);
65
}
66
67
/*
68
* Cgroup v1 case:
69
*
70
* Use info gathered previously from /proc/self/cgroup
71
* and map host mount point to
72
* local one via /proc/self/mountinfo content above
73
*
74
* Docker example:
75
* 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044
76
*
77
* Host example:
78
* 5:memory:/user.slice
79
*
80
* Construct a path to the process specific memory and cpuset
81
* cgroup directory.
82
*
83
* For a container running under Docker from memory example above
84
* the paths would be:
85
*
86
* /sys/fs/cgroup/memory
87
*
88
* For a Host from memory example above the path would be:
89
*
90
* /sys/fs/cgroup/memory/user.slice
91
*
92
*/
93
assert(is_cgroup_v1(&cg_type_flags), "Cgroup v1 expected");
94
for (int i = 0; i < CG_INFO_LENGTH; i++) {
95
CgroupInfo info = cg_infos[i];
96
if (strcmp(info._name, "memory") == 0) {
97
memory = new CgroupV1MemoryController(info._root_mount_path, info._mount_path);
98
memory->set_subsystem_path(info._cgroup_path);
99
} else if (strcmp(info._name, "cpuset") == 0) {
100
cpuset = new CgroupV1Controller(info._root_mount_path, info._mount_path);
101
cpuset->set_subsystem_path(info._cgroup_path);
102
} else if (strcmp(info._name, "cpu") == 0) {
103
cpu = new CgroupV1Controller(info._root_mount_path, info._mount_path);
104
cpu->set_subsystem_path(info._cgroup_path);
105
} else if (strcmp(info._name, "cpuacct") == 0) {
106
cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path);
107
cpuacct->set_subsystem_path(info._cgroup_path);
108
}
109
}
110
cleanup(cg_infos);
111
return new CgroupV1Subsystem(cpuset, cpu, cpuacct, memory);
112
}
113
114
bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos,
115
const char* proc_cgroups,
116
const char* proc_self_cgroup,
117
const char* proc_self_mountinfo,
118
u1* flags) {
119
FILE *mntinfo = NULL;
120
FILE *cgroups = NULL;
121
FILE *cgroup = NULL;
122
char buf[MAXPATHLEN+1];
123
char *p;
124
bool is_cgroupsV2;
125
// true iff all controllers, memory, cpu, cpuset, cpuacct are enabled
126
// at the kernel level.
127
bool all_controllers_enabled;
128
129
/*
130
* Read /proc/cgroups so as to be able to distinguish cgroups v2 vs cgroups v1.
131
*
132
* For cgroups v1 hierarchy (hybrid or legacy), cpu, cpuacct, cpuset, memory controllers
133
* must have non-zero for the hierarchy ID field and relevant controllers mounted.
134
* Conversely, for cgroups v2 (unified hierarchy), cpu, cpuacct, cpuset, memory
135
* controllers must have hierarchy ID 0 and the unified controller mounted.
136
*/
137
cgroups = fopen(proc_cgroups, "r");
138
if (cgroups == NULL) {
139
log_debug(os, container)("Can't open %s, %s",
140
proc_cgroups, os::strerror(errno));
141
*flags = INVALID_CGROUPS_GENERIC;
142
return false;
143
}
144
145
while ((p = fgets(buf, MAXPATHLEN, cgroups)) != NULL) {
146
char name[MAXPATHLEN+1];
147
int hierarchy_id;
148
int enabled;
149
150
// Format of /proc/cgroups documented via man 7 cgroups
151
if (sscanf(p, "%s %d %*d %d", name, &hierarchy_id, &enabled) != 3) {
152
continue;
153
}
154
if (strcmp(name, "memory") == 0) {
155
cg_infos[MEMORY_IDX]._name = os::strdup(name);
156
cg_infos[MEMORY_IDX]._hierarchy_id = hierarchy_id;
157
cg_infos[MEMORY_IDX]._enabled = (enabled == 1);
158
} else if (strcmp(name, "cpuset") == 0) {
159
cg_infos[CPUSET_IDX]._name = os::strdup(name);
160
cg_infos[CPUSET_IDX]._hierarchy_id = hierarchy_id;
161
cg_infos[CPUSET_IDX]._enabled = (enabled == 1);
162
} else if (strcmp(name, "cpu") == 0) {
163
cg_infos[CPU_IDX]._name = os::strdup(name);
164
cg_infos[CPU_IDX]._hierarchy_id = hierarchy_id;
165
cg_infos[CPU_IDX]._enabled = (enabled == 1);
166
} else if (strcmp(name, "cpuacct") == 0) {
167
cg_infos[CPUACCT_IDX]._name = os::strdup(name);
168
cg_infos[CPUACCT_IDX]._hierarchy_id = hierarchy_id;
169
cg_infos[CPUACCT_IDX]._enabled = (enabled == 1);
170
}
171
}
172
fclose(cgroups);
173
174
is_cgroupsV2 = true;
175
all_controllers_enabled = true;
176
for (int i = 0; i < CG_INFO_LENGTH; i++) {
177
is_cgroupsV2 = is_cgroupsV2 && cg_infos[i]._hierarchy_id == 0;
178
all_controllers_enabled = all_controllers_enabled && cg_infos[i]._enabled;
179
}
180
181
if (!all_controllers_enabled) {
182
// one or more controllers disabled, disable container support
183
log_debug(os, container)("One or more required controllers disabled at kernel level.");
184
cleanup(cg_infos);
185
*flags = INVALID_CGROUPS_GENERIC;
186
return false;
187
}
188
189
/*
190
* Read /proc/self/cgroup and determine:
191
* - the cgroup path for cgroups v2 or
192
* - on a cgroups v1 system, collect info for mapping
193
* the host mount point to the local one via /proc/self/mountinfo below.
194
*/
195
cgroup = fopen(proc_self_cgroup, "r");
196
if (cgroup == NULL) {
197
log_debug(os, container)("Can't open %s, %s",
198
proc_self_cgroup, os::strerror(errno));
199
cleanup(cg_infos);
200
*flags = INVALID_CGROUPS_GENERIC;
201
return false;
202
}
203
204
while ((p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) {
205
char *controllers;
206
char *token;
207
char *hierarchy_id_str;
208
int hierarchy_id;
209
char *cgroup_path;
210
211
hierarchy_id_str = strsep(&p, ":");
212
hierarchy_id = atoi(hierarchy_id_str);
213
/* Get controllers and base */
214
controllers = strsep(&p, ":");
215
cgroup_path = strsep(&p, "\n");
216
217
if (controllers == NULL) {
218
continue;
219
}
220
221
while (!is_cgroupsV2 && (token = strsep(&controllers, ",")) != NULL) {
222
if (strcmp(token, "memory") == 0) {
223
assert(hierarchy_id == cg_infos[MEMORY_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch");
224
cg_infos[MEMORY_IDX]._cgroup_path = os::strdup(cgroup_path);
225
} else if (strcmp(token, "cpuset") == 0) {
226
assert(hierarchy_id == cg_infos[CPUSET_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch");
227
cg_infos[CPUSET_IDX]._cgroup_path = os::strdup(cgroup_path);
228
} else if (strcmp(token, "cpu") == 0) {
229
assert(hierarchy_id == cg_infos[CPU_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch");
230
cg_infos[CPU_IDX]._cgroup_path = os::strdup(cgroup_path);
231
} else if (strcmp(token, "cpuacct") == 0) {
232
assert(hierarchy_id == cg_infos[CPUACCT_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch");
233
cg_infos[CPUACCT_IDX]._cgroup_path = os::strdup(cgroup_path);
234
}
235
}
236
if (is_cgroupsV2) {
237
for (int i = 0; i < CG_INFO_LENGTH; i++) {
238
cg_infos[i]._cgroup_path = os::strdup(cgroup_path);
239
}
240
}
241
}
242
fclose(cgroup);
243
244
// Find various mount points by reading /proc/self/mountinfo
245
// mountinfo format is documented at https://www.kernel.org/doc/Documentation/filesystems/proc.txt
246
mntinfo = fopen(proc_self_mountinfo, "r");
247
if (mntinfo == NULL) {
248
log_debug(os, container)("Can't open %s, %s",
249
proc_self_mountinfo, os::strerror(errno));
250
cleanup(cg_infos);
251
*flags = INVALID_CGROUPS_GENERIC;
252
return false;
253
}
254
255
bool cgroupv2_mount_point_found = false;
256
bool any_cgroup_mounts_found = false;
257
while ((p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) {
258
char tmp_mount_point[MAXPATHLEN+1];
259
char tmp_fs_type[MAXPATHLEN+1];
260
char tmproot[MAXPATHLEN+1];
261
char tmpmount[MAXPATHLEN+1];
262
char tmpcgroups[MAXPATHLEN+1];
263
char *cptr = tmpcgroups;
264
char *token;
265
266
// Cgroup v2 relevant info. We only look for the _mount_path iff is_cgroupsV2 so
267
// as to avoid memory stomping of the _mount_path pointer later on in the cgroup v1
268
// block in the hybrid case.
269
//
270
if (is_cgroupsV2 && sscanf(p, "%*d %*d %*d:%*d %*s %s %*[^-]- %s %*s %*s", tmp_mount_point, tmp_fs_type) == 2) {
271
// we likely have an early match return (e.g. cgroup fs match), be sure we have cgroup2 as fstype
272
if (!cgroupv2_mount_point_found && strcmp("cgroup2", tmp_fs_type) == 0) {
273
cgroupv2_mount_point_found = true;
274
any_cgroup_mounts_found = true;
275
for (int i = 0; i < CG_INFO_LENGTH; i++) {
276
assert(cg_infos[i]._mount_path == NULL, "_mount_path memory stomping");
277
cg_infos[i]._mount_path = os::strdup(tmp_mount_point);
278
}
279
}
280
}
281
282
/* Cgroup v1 relevant info
283
*
284
* Find the cgroup mount point for memory, cpuset, cpu, cpuacct
285
*
286
* Example for docker:
287
* 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory
288
*
289
* Example for host:
290
* 34 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory
291
*/
292
if (sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- %s %*s %s", tmproot, tmpmount, tmp_fs_type, tmpcgroups) == 4) {
293
if (strcmp("cgroup", tmp_fs_type) != 0) {
294
// Skip cgroup2 fs lines on hybrid or unified hierarchy.
295
continue;
296
}
297
while ((token = strsep(&cptr, ",")) != NULL) {
298
if (strcmp(token, "memory") == 0) {
299
any_cgroup_mounts_found = true;
300
assert(cg_infos[MEMORY_IDX]._mount_path == NULL, "stomping of _mount_path");
301
cg_infos[MEMORY_IDX]._mount_path = os::strdup(tmpmount);
302
cg_infos[MEMORY_IDX]._root_mount_path = os::strdup(tmproot);
303
cg_infos[MEMORY_IDX]._data_complete = true;
304
} else if (strcmp(token, "cpuset") == 0) {
305
any_cgroup_mounts_found = true;
306
if (cg_infos[CPUSET_IDX]._mount_path != NULL) {
307
// On some systems duplicate cpuset controllers get mounted in addition to
308
// the main cgroup controllers most likely under /sys/fs/cgroup. In that
309
// case pick the one under /sys/fs/cgroup and discard others.
310
if (strstr(cg_infos[CPUSET_IDX]._mount_path, "/sys/fs/cgroup") != cg_infos[CPUSET_IDX]._mount_path) {
311
log_warning(os, container)("Duplicate cpuset controllers detected. Picking %s, skipping %s.",
312
tmpmount, cg_infos[CPUSET_IDX]._mount_path);
313
os::free(cg_infos[CPUSET_IDX]._mount_path);
314
cg_infos[CPUSET_IDX]._mount_path = os::strdup(tmpmount);
315
} else {
316
log_warning(os, container)("Duplicate cpuset controllers detected. Picking %s, skipping %s.",
317
cg_infos[CPUSET_IDX]._mount_path, tmpmount);
318
}
319
} else {
320
cg_infos[CPUSET_IDX]._mount_path = os::strdup(tmpmount);
321
}
322
cg_infos[CPUSET_IDX]._root_mount_path = os::strdup(tmproot);
323
cg_infos[CPUSET_IDX]._data_complete = true;
324
} else if (strcmp(token, "cpu") == 0) {
325
any_cgroup_mounts_found = true;
326
assert(cg_infos[CPU_IDX]._mount_path == NULL, "stomping of _mount_path");
327
cg_infos[CPU_IDX]._mount_path = os::strdup(tmpmount);
328
cg_infos[CPU_IDX]._root_mount_path = os::strdup(tmproot);
329
cg_infos[CPU_IDX]._data_complete = true;
330
} else if (strcmp(token, "cpuacct") == 0) {
331
any_cgroup_mounts_found = true;
332
assert(cg_infos[CPUACCT_IDX]._mount_path == NULL, "stomping of _mount_path");
333
cg_infos[CPUACCT_IDX]._mount_path = os::strdup(tmpmount);
334
cg_infos[CPUACCT_IDX]._root_mount_path = os::strdup(tmproot);
335
cg_infos[CPUACCT_IDX]._data_complete = true;
336
}
337
}
338
}
339
}
340
fclose(mntinfo);
341
342
// Neither cgroup2 nor cgroup filesystems mounted via /proc/self/mountinfo
343
// No point in continuing.
344
if (!any_cgroup_mounts_found) {
345
log_trace(os, container)("No relevant cgroup controllers mounted.");
346
cleanup(cg_infos);
347
*flags = INVALID_CGROUPS_NO_MOUNT;
348
return false;
349
}
350
351
if (is_cgroupsV2) {
352
if (!cgroupv2_mount_point_found) {
353
log_trace(os, container)("Mount point for cgroupv2 not found in /proc/self/mountinfo");
354
cleanup(cg_infos);
355
*flags = INVALID_CGROUPS_V2;
356
return false;
357
}
358
// Cgroups v2 case, we have all the info we need.
359
*flags = CGROUPS_V2;
360
return true;
361
}
362
363
// What follows is cgroups v1
364
log_debug(os, container)("Detected cgroups hybrid or legacy hierarchy, using cgroups v1 controllers");
365
366
if (!cg_infos[MEMORY_IDX]._data_complete) {
367
log_debug(os, container)("Required cgroup v1 memory subsystem not found");
368
cleanup(cg_infos);
369
*flags = INVALID_CGROUPS_V1;
370
return false;
371
}
372
if (!cg_infos[CPUSET_IDX]._data_complete) {
373
log_debug(os, container)("Required cgroup v1 cpuset subsystem not found");
374
cleanup(cg_infos);
375
*flags = INVALID_CGROUPS_V1;
376
return false;
377
}
378
if (!cg_infos[CPU_IDX]._data_complete) {
379
log_debug(os, container)("Required cgroup v1 cpu subsystem not found");
380
cleanup(cg_infos);
381
*flags = INVALID_CGROUPS_V1;
382
return false;
383
}
384
if (!cg_infos[CPUACCT_IDX]._data_complete) {
385
log_debug(os, container)("Required cgroup v1 cpuacct subsystem not found");
386
cleanup(cg_infos);
387
*flags = INVALID_CGROUPS_V1;
388
return false;
389
}
390
// Cgroups v1 case, we have all the info we need.
391
*flags = CGROUPS_V1;
392
return true;
393
394
};
395
396
void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) {
397
assert(cg_infos != NULL, "Invariant");
398
for (int i = 0; i < CG_INFO_LENGTH; i++) {
399
os::free(cg_infos[i]._name);
400
os::free(cg_infos[i]._cgroup_path);
401
os::free(cg_infos[i]._root_mount_path);
402
os::free(cg_infos[i]._mount_path);
403
}
404
}
405
406
/* active_processor_count
407
*
408
* Calculate an appropriate number of active processors for the
409
* VM to use based on these three inputs.
410
*
411
* cpu affinity
412
* cgroup cpu quota & cpu period
413
* cgroup cpu shares
414
*
415
* Algorithm:
416
*
417
* Determine the number of available CPUs from sched_getaffinity
418
*
419
* If user specified a quota (quota != -1), calculate the number of
420
* required CPUs by dividing quota by period.
421
*
422
* If shares are in effect (shares != -1), calculate the number
423
* of CPUs required for the shares by dividing the share value
424
* by PER_CPU_SHARES.
425
*
426
* All results of division are rounded up to the next whole number.
427
*
428
* If neither shares or quotas have been specified, return the
429
* number of active processors in the system.
430
*
431
* If both shares and quotas have been specified, the results are
432
* based on the flag PreferContainerQuotaForCPUCount. If true,
433
* return the quota value. If false return the smallest value
434
* between shares or quotas.
435
*
436
* If shares and/or quotas have been specified, the resulting number
437
* returned will never exceed the number of active processors.
438
*
439
* return:
440
* number of CPUs
441
*/
442
int CgroupSubsystem::active_processor_count() {
443
int quota_count = 0, share_count = 0;
444
int cpu_count, limit_count;
445
int result;
446
447
// We use a cache with a timeout to avoid performing expensive
448
// computations in the event this function is called frequently.
449
// [See 8227006].
450
CachingCgroupController* contrl = cpu_controller();
451
CachedMetric* cpu_limit = contrl->metrics_cache();
452
if (!cpu_limit->should_check_metric()) {
453
int val = (int)cpu_limit->value();
454
log_trace(os, container)("CgroupSubsystem::active_processor_count (cached): %d", val);
455
return val;
456
}
457
458
cpu_count = limit_count = os::Linux::active_processor_count();
459
int quota = cpu_quota();
460
int period = cpu_period();
461
int share = cpu_shares();
462
463
if (quota > -1 && period > 0) {
464
quota_count = ceilf((float)quota / (float)period);
465
log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count);
466
}
467
if (share > -1) {
468
share_count = ceilf((float)share / (float)PER_CPU_SHARES);
469
log_trace(os, container)("CPU Share count based on shares: %d", share_count);
470
}
471
472
// If both shares and quotas are setup results depend
473
// on flag PreferContainerQuotaForCPUCount.
474
// If true, limit CPU count to quota
475
// If false, use minimum of shares and quotas
476
if (quota_count !=0 && share_count != 0) {
477
if (PreferContainerQuotaForCPUCount) {
478
limit_count = quota_count;
479
} else {
480
limit_count = MIN2(quota_count, share_count);
481
}
482
} else if (quota_count != 0) {
483
limit_count = quota_count;
484
} else if (share_count != 0) {
485
limit_count = share_count;
486
}
487
488
result = MIN2(cpu_count, limit_count);
489
log_trace(os, container)("OSContainer::active_processor_count: %d", result);
490
491
// Update cached metric to avoid re-reading container settings too often
492
cpu_limit->set_value(result, OSCONTAINER_CACHE_TIMEOUT);
493
494
return result;
495
}
496
497
/* memory_limit_in_bytes
498
*
499
* Return the limit of available memory for this process.
500
*
501
* return:
502
* memory limit in bytes or
503
* -1 for unlimited
504
* OSCONTAINER_ERROR for not supported
505
*/
506
jlong CgroupSubsystem::memory_limit_in_bytes() {
507
CachingCgroupController* contrl = memory_controller();
508
CachedMetric* memory_limit = contrl->metrics_cache();
509
if (!memory_limit->should_check_metric()) {
510
return memory_limit->value();
511
}
512
jlong mem_limit = read_memory_limit_in_bytes();
513
// Update cached metric to avoid re-reading container settings too often
514
memory_limit->set_value(mem_limit, OSCONTAINER_CACHE_TIMEOUT);
515
return mem_limit;
516
}
517
518