105 int buflen;
106 strncpy(buf, _mount_point, MAXPATHLEN);
107 buf[MAXPATHLEN-1] = '\0';
108 buflen = strlen(buf);
109 if ((buflen + strlen(cgroup_path) - strlen(_root)) > (MAXPATHLEN-1)) {
110 return;
111 }
112 strncat(buf, cgroup_path + strlen(_root), MAXPATHLEN-buflen);
113 buf[MAXPATHLEN-1] = '\0';
114 _path = os::strdup(buf);
115 }
116 }
117 }
118 }
119 }
120 }
121
122 char *subsystem_path() { return _path; }
123 };
124
125 CgroupSubsystem* memory = NULL;
126 CgroupSubsystem* cpuset = NULL;
127 CgroupSubsystem* cpu = NULL;
128 CgroupSubsystem* cpuacct = NULL;
129
130 typedef char * cptr;
131
132 PRAGMA_DIAG_PUSH
133 PRAGMA_FORMAT_NONLITERAL_IGNORED
134 template <typename T> int subsystem_file_contents(CgroupSubsystem* c,
135 const char *filename,
136 const char *scan_fmt,
137 T returnval) {
138 FILE *fp = NULL;
139 char *p;
140 char file[MAXPATHLEN+1];
141 char buf[MAXPATHLEN+1];
142
143 if (c == NULL) {
144 log_debug(os, container)("subsystem_file_contents: CgroupSubsytem* is NULL");
145 return OSCONTAINER_ERROR;
146 }
147 if (c->subsystem_path() == NULL) {
148 log_debug(os, container)("subsystem_file_contents: subsystem path is NULL");
149 return OSCONTAINER_ERROR;
150 }
151
152 strncpy(file, c->subsystem_path(), MAXPATHLEN);
153 file[MAXPATHLEN-1] = '\0';
154 int filelen = strlen(file);
155 if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) {
156 log_debug(os, container)("File path too long %s, %s", file, filename);
157 return OSCONTAINER_ERROR;
158 }
159 strncat(file, filename, MAXPATHLEN-filelen);
160 log_trace(os, container)("Path to %s is %s", filename, file);
161 fp = fopen(file, "r");
162 if (fp != NULL) {
163 p = fgets(buf, MAXPATHLEN, fp);
164 if (p != NULL) {
165 int matched = sscanf(p, scan_fmt, returnval);
166 if (matched == 1) {
167 fclose(fp);
168 return 0;
169 } else {
170 log_debug(os, container)("Type %s not found in file %s", scan_fmt, file);
171 }
172 } else {
173 log_debug(os, container)("Empty file %s", file);
174 }
175 } else {
176 log_debug(os, container)("Open of file %s failed, %s", file, os::strerror(errno));
177 }
178 if (fp != NULL)
179 fclose(fp);
180 return OSCONTAINER_ERROR;
181 }
182 PRAGMA_DIAG_POP
183
184 #define GET_CONTAINER_INFO(return_type, subsystem, filename, \
185 logstring, scan_fmt, variable) \
186 return_type variable; \
187 { \
188 int err; \
189 err = subsystem_file_contents(subsystem, \
190 filename, \
191 scan_fmt, \
192 &variable); \
193 if (err != 0) \
194 return (return_type) OSCONTAINER_ERROR; \
195 \
196 log_trace(os, container)(logstring, variable); \
197 }
198
199 #define GET_CONTAINER_INFO_CPTR(return_type, subsystem, filename, \
200 logstring, scan_fmt, variable, bufsize) \
201 char variable[bufsize]; \
202 { \
203 int err; \
204 err = subsystem_file_contents(subsystem, \
205 filename, \
206 scan_fmt, \
207 variable); \
208 if (err != 0) \
209 return (return_type) NULL; \
210 \
211 log_trace(os, container)(logstring, variable); \
212 }
213
214 /* init
215 *
216 * Initialize the container support and determine if
217 * we are running under cgroup control.
218 */
219 void OSContainer::init() {
220 FILE *mntinfo = NULL;
221 FILE *cgroup = NULL;
222 char buf[MAXPATHLEN+1];
223 char tmproot[MAXPATHLEN+1];
224 char tmpmount[MAXPATHLEN+1];
225 char *p;
226 jlong mem_limit;
227
228 assert(!_is_initialized, "Initializing OSContainer more than once");
229
230 _is_initialized = true;
231 _is_containerized = false;
232
233 _unlimited_memory = (LONG_MAX / os::vm_page_size()) * os::vm_page_size();
249 * 34 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory
250 */
251 mntinfo = fopen("/proc/self/mountinfo", "r");
252 if (mntinfo == NULL) {
253 log_debug(os, container)("Can't open /proc/self/mountinfo, %s",
254 os::strerror(errno));
255 return;
256 }
257
258 while ((p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) {
259 char tmpcgroups[MAXPATHLEN+1];
260 char *cptr = tmpcgroups;
261 char *token;
262
263 // mountinfo format is documented at https://www.kernel.org/doc/Documentation/filesystems/proc.txt
264 if (sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- cgroup %*s %s", tmproot, tmpmount, tmpcgroups) != 3) {
265 continue;
266 }
267 while ((token = strsep(&cptr, ",")) != NULL) {
268 if (strcmp(token, "memory") == 0) {
269 memory = new CgroupSubsystem(tmproot, tmpmount);
270 } else if (strcmp(token, "cpuset") == 0) {
271 cpuset = new CgroupSubsystem(tmproot, tmpmount);
272 } else if (strcmp(token, "cpu") == 0) {
273 cpu = new CgroupSubsystem(tmproot, tmpmount);
274 } else if (strcmp(token, "cpuacct") == 0) {
275 cpuacct= new CgroupSubsystem(tmproot, tmpmount);
276 }
277 }
278 }
279
280 fclose(mntinfo);
281
282 if (memory == NULL) {
283 log_debug(os, container)("Required cgroup memory subsystem not found");
284 return;
285 }
286 if (cpuset == NULL) {
287 log_debug(os, container)("Required cgroup cpuset subsystem not found");
288 return;
289 }
327 }
328
329 while ((p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) {
330 char *controllers;
331 char *token;
332 char *base;
333
334 /* Skip cgroup number */
335 strsep(&p, ":");
336 /* Get controllers and base */
337 controllers = strsep(&p, ":");
338 base = strsep(&p, "\n");
339
340 if (controllers == NULL) {
341 continue;
342 }
343
344 while ((token = strsep(&controllers, ",")) != NULL) {
345 if (strcmp(token, "memory") == 0) {
346 memory->set_subsystem_path(base);
347 } else if (strcmp(token, "cpuset") == 0) {
348 cpuset->set_subsystem_path(base);
349 } else if (strcmp(token, "cpu") == 0) {
350 cpu->set_subsystem_path(base);
351 } else if (strcmp(token, "cpuacct") == 0) {
352 cpuacct->set_subsystem_path(base);
353 }
354 }
355 }
356
357 fclose(cgroup);
358
359 // We need to update the amount of physical memory now that
360 // command line arguments have been processed.
361 if ((mem_limit = memory_limit_in_bytes()) > 0) {
362 os::Linux::set_physical_memory(mem_limit);
363 }
364
365 _is_containerized = true;
366
367 }
368
369 const char * OSContainer::container_type() {
370 if (is_containerized()) {
371 return "cgroupv1";
372 } else {
373 return NULL;
374 }
375 }
376
377
378 /* memory_limit_in_bytes
379 *
380 * Return the limit of available memory for this process.
381 *
382 * return:
383 * memory limit in bytes or
384 * -1 for unlimited
385 * OSCONTAINER_ERROR for not supported
386 */
387 jlong OSContainer::memory_limit_in_bytes() {
388 GET_CONTAINER_INFO(julong, memory, "/memory.limit_in_bytes",
389 "Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit);
390
391 if (memlimit >= _unlimited_memory) {
392 log_trace(os, container)("Memory Limit is: Unlimited");
393 return (jlong)-1;
394 }
395 else {
396 return (jlong)memlimit;
397 }
398 }
399
400 jlong OSContainer::memory_and_swap_limit_in_bytes() {
401 GET_CONTAINER_INFO(julong, memory, "/memory.memsw.limit_in_bytes",
402 "Memory and Swap Limit is: " JULONG_FORMAT, JULONG_FORMAT, memswlimit);
403 if (memswlimit >= _unlimited_memory) {
404 log_trace(os, container)("Memory and Swap Limit is: Unlimited");
405 return (jlong)-1;
406 } else {
407 return (jlong)memswlimit;
408 }
409 }
410
411 jlong OSContainer::memory_soft_limit_in_bytes() {
412 GET_CONTAINER_INFO(julong, memory, "/memory.soft_limit_in_bytes",
413 "Memory Soft Limit is: " JULONG_FORMAT, JULONG_FORMAT, memsoftlimit);
414 if (memsoftlimit >= _unlimited_memory) {
415 log_trace(os, container)("Memory Soft Limit is: Unlimited");
416 return (jlong)-1;
417 } else {
418 return (jlong)memsoftlimit;
419 }
420 }
421
422 /* memory_usage_in_bytes
423 *
424 * Return the amount of used memory for this process.
|
105 int buflen;
106 strncpy(buf, _mount_point, MAXPATHLEN);
107 buf[MAXPATHLEN-1] = '\0';
108 buflen = strlen(buf);
109 if ((buflen + strlen(cgroup_path) - strlen(_root)) > (MAXPATHLEN-1)) {
110 return;
111 }
112 strncat(buf, cgroup_path + strlen(_root), MAXPATHLEN-buflen);
113 buf[MAXPATHLEN-1] = '\0';
114 _path = os::strdup(buf);
115 }
116 }
117 }
118 }
119 }
120 }
121
122 char *subsystem_path() { return _path; }
123 };
124
125 class CgroupMemorySubsystem: CgroupSubsystem {
126 friend class OSContainer;
127
128 private:
129 /* Some container runtimes set limits via cgroup
130 * hierarchy. If set to true consider also memory.stat
131 * file if everything else seems unlimited */
132 bool _uses_mem_hierarchy;
133
134 public:
135 CgroupMemorySubsystem(char *root, char *mountpoint) : CgroupSubsystem::CgroupSubsystem(root, mountpoint) {
136 _uses_mem_hierarchy = false;
137 }
138
139 bool is_hierarchical() { return _uses_mem_hierarchy; }
140 void set_hierarchical(bool value) { _uses_mem_hierarchy = value; }
141 };
142
143 CgroupMemorySubsystem* memory = NULL;
144 CgroupSubsystem* cpuset = NULL;
145 CgroupSubsystem* cpu = NULL;
146 CgroupSubsystem* cpuacct = NULL;
147
148 typedef char * cptr;
149
150 PRAGMA_DIAG_PUSH
151 PRAGMA_FORMAT_NONLITERAL_IGNORED
152 template <typename T> int subsystem_file_line_contents(CgroupSubsystem* c,
153 const char *filename,
154 const char *matchline,
155 const char *scan_fmt,
156 T returnval) {
157 FILE *fp = NULL;
158 char *p;
159 char file[MAXPATHLEN+1];
160 char buf[MAXPATHLEN+1];
161 char discard[MAXPATHLEN+1];
162 bool found_match = false;
163
164 if (c == NULL) {
165 log_debug(os, container)("subsystem_file_line_contents: CgroupSubsytem* is NULL");
166 return OSCONTAINER_ERROR;
167 }
168 if (c->subsystem_path() == NULL) {
169 log_debug(os, container)("subsystem_file_line_contents: subsystem path is NULL");
170 return OSCONTAINER_ERROR;
171 }
172
173 strncpy(file, c->subsystem_path(), MAXPATHLEN);
174 file[MAXPATHLEN-1] = '\0';
175 int filelen = strlen(file);
176 if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) {
177 log_debug(os, container)("File path too long %s, %s", file, filename);
178 return OSCONTAINER_ERROR;
179 }
180 strncat(file, filename, MAXPATHLEN-filelen);
181 log_trace(os, container)("Path to %s is %s", filename, file);
182 fp = fopen(file, "r");
183 if (fp != NULL) {
184 int err = 0;
185 while ((p = fgets(buf, MAXPATHLEN, fp)) != NULL) {
186 found_match = false;
187 if (matchline == NULL) {
188 // single-line file case
189 int matched = sscanf(p, scan_fmt, returnval);
190 found_match = (matched == 1);
191 } else {
192 // multi-line file case
193 if (strstr(p, matchline) != NULL) {
194 // discard matchline string prefix
195 int matched = sscanf(p, scan_fmt, discard, returnval);
196 found_match = (matched == 2);
197 } else {
198 continue; // substring not found
199 }
200 }
201 if (found_match) {
202 fclose(fp);
203 return 0;
204 } else {
205 err = 1;
206 log_debug(os, container)("Type %s not found in file %s", scan_fmt, file);
207 }
208 }
209 if (err == 0) {
210 log_debug(os, container)("Empty file %s", file);
211 }
212 } else {
213 log_debug(os, container)("Open of file %s failed, %s", file, os::strerror(errno));
214 }
215 if (fp != NULL)
216 fclose(fp);
217 return OSCONTAINER_ERROR;
218 }
219 PRAGMA_DIAG_POP
220
221 #define GET_CONTAINER_INFO(return_type, subsystem, filename, \
222 logstring, scan_fmt, variable) \
223 return_type variable; \
224 { \
225 int err; \
226 err = subsystem_file_line_contents(subsystem, \
227 filename, \
228 NULL, \
229 scan_fmt, \
230 &variable); \
231 if (err != 0) \
232 return (return_type) OSCONTAINER_ERROR; \
233 \
234 log_trace(os, container)(logstring, variable); \
235 }
236
237 #define GET_CONTAINER_INFO_CPTR(return_type, subsystem, filename, \
238 logstring, scan_fmt, variable, bufsize) \
239 char variable[bufsize]; \
240 { \
241 int err; \
242 err = subsystem_file_line_contents(subsystem, \
243 filename, \
244 NULL, \
245 scan_fmt, \
246 variable); \
247 if (err != 0) \
248 return (return_type) NULL; \
249 \
250 log_trace(os, container)(logstring, variable); \
251 }
252
253 #define GET_CONTAINER_INFO_LINE(return_type, subsystem, filename, \
254 matchline, logstring, scan_fmt, variable) \
255 return_type variable; \
256 { \
257 int err; \
258 err = subsystem_file_line_contents(subsystem, \
259 filename, \
260 matchline, \
261 scan_fmt, \
262 &variable); \
263 if (err != 0) \
264 return (return_type) OSCONTAINER_ERROR; \
265 \
266 log_trace(os, container)(logstring, variable); \
267 }
268
269 /* init
270 *
271 * Initialize the container support and determine if
272 * we are running under cgroup control.
273 */
274 void OSContainer::init() {
275 FILE *mntinfo = NULL;
276 FILE *cgroup = NULL;
277 char buf[MAXPATHLEN+1];
278 char tmproot[MAXPATHLEN+1];
279 char tmpmount[MAXPATHLEN+1];
280 char *p;
281 jlong mem_limit;
282
283 assert(!_is_initialized, "Initializing OSContainer more than once");
284
285 _is_initialized = true;
286 _is_containerized = false;
287
288 _unlimited_memory = (LONG_MAX / os::vm_page_size()) * os::vm_page_size();
304 * 34 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory
305 */
306 mntinfo = fopen("/proc/self/mountinfo", "r");
307 if (mntinfo == NULL) {
308 log_debug(os, container)("Can't open /proc/self/mountinfo, %s",
309 os::strerror(errno));
310 return;
311 }
312
313 while ((p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) {
314 char tmpcgroups[MAXPATHLEN+1];
315 char *cptr = tmpcgroups;
316 char *token;
317
318 // mountinfo format is documented at https://www.kernel.org/doc/Documentation/filesystems/proc.txt
319 if (sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- cgroup %*s %s", tmproot, tmpmount, tmpcgroups) != 3) {
320 continue;
321 }
322 while ((token = strsep(&cptr, ",")) != NULL) {
323 if (strcmp(token, "memory") == 0) {
324 memory = new CgroupMemorySubsystem(tmproot, tmpmount);
325 } else if (strcmp(token, "cpuset") == 0) {
326 cpuset = new CgroupSubsystem(tmproot, tmpmount);
327 } else if (strcmp(token, "cpu") == 0) {
328 cpu = new CgroupSubsystem(tmproot, tmpmount);
329 } else if (strcmp(token, "cpuacct") == 0) {
330 cpuacct= new CgroupSubsystem(tmproot, tmpmount);
331 }
332 }
333 }
334
335 fclose(mntinfo);
336
337 if (memory == NULL) {
338 log_debug(os, container)("Required cgroup memory subsystem not found");
339 return;
340 }
341 if (cpuset == NULL) {
342 log_debug(os, container)("Required cgroup cpuset subsystem not found");
343 return;
344 }
382 }
383
384 while ((p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) {
385 char *controllers;
386 char *token;
387 char *base;
388
389 /* Skip cgroup number */
390 strsep(&p, ":");
391 /* Get controllers and base */
392 controllers = strsep(&p, ":");
393 base = strsep(&p, "\n");
394
395 if (controllers == NULL) {
396 continue;
397 }
398
399 while ((token = strsep(&controllers, ",")) != NULL) {
400 if (strcmp(token, "memory") == 0) {
401 memory->set_subsystem_path(base);
402 jlong hierarchy = uses_mem_hierarchy();
403 if (hierarchy > 0) {
404 memory->set_hierarchical(true);
405 }
406 } else if (strcmp(token, "cpuset") == 0) {
407 cpuset->set_subsystem_path(base);
408 } else if (strcmp(token, "cpu") == 0) {
409 cpu->set_subsystem_path(base);
410 } else if (strcmp(token, "cpuacct") == 0) {
411 cpuacct->set_subsystem_path(base);
412 }
413 }
414 }
415
416 fclose(cgroup);
417
418 // We need to update the amount of physical memory now that
419 // command line arguments have been processed.
420 if ((mem_limit = memory_limit_in_bytes()) > 0) {
421 os::Linux::set_physical_memory(mem_limit);
422 log_info(os, container)("Memory Limit is: " JLONG_FORMAT, mem_limit);
423 }
424
425 _is_containerized = true;
426
427 }
428
429 const char * OSContainer::container_type() {
430 if (is_containerized()) {
431 return "cgroupv1";
432 } else {
433 return NULL;
434 }
435 }
436
437 /* uses_mem_hierarchy
438 *
439 * Return whether or not hierarchical cgroup accounting is being
440 * done.
441 *
442 * return:
443 * A number > 0 if true, or
444 * OSCONTAINER_ERROR for not supported
445 */
446 jlong OSContainer::uses_mem_hierarchy() {
447 GET_CONTAINER_INFO(jlong, memory, "/memory.use_hierarchy",
448 "Use Hierarchy is: " JLONG_FORMAT, JLONG_FORMAT, use_hierarchy);
449 return use_hierarchy;
450 }
451
452
453 /* memory_limit_in_bytes
454 *
455 * Return the limit of available memory for this process.
456 *
457 * return:
458 * memory limit in bytes or
459 * -1 for unlimited
460 * OSCONTAINER_ERROR for not supported
461 */
462 jlong OSContainer::memory_limit_in_bytes() {
463 GET_CONTAINER_INFO(julong, memory, "/memory.limit_in_bytes",
464 "Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit);
465
466 if (memlimit >= _unlimited_memory) {
467 log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited");
468 if (memory->is_hierarchical()) {
469 const char* matchline = "hierarchical_memory_limit";
470 char* format = "%s " JULONG_FORMAT;
471 GET_CONTAINER_INFO_LINE(julong, memory, "/memory.stat", matchline,
472 "Hierarchical Memory Limit is: " JULONG_FORMAT, format, hier_memlimit)
473 if (hier_memlimit >= _unlimited_memory) {
474 log_trace(os, container)("Hierarchical Memory Limit is: Unlimited");
475 } else {
476 return (jlong)hier_memlimit;
477 }
478 }
479 return (jlong)-1;
480 }
481 else {
482 return (jlong)memlimit;
483 }
484 }
485
486 jlong OSContainer::memory_and_swap_limit_in_bytes() {
487 GET_CONTAINER_INFO(julong, memory, "/memory.memsw.limit_in_bytes",
488 "Memory and Swap Limit is: " JULONG_FORMAT, JULONG_FORMAT, memswlimit);
489 if (memswlimit >= _unlimited_memory) {
490 log_trace(os, container)("Non-Hierarchical Memory and Swap Limit is: Unlimited");
491 if (memory->is_hierarchical()) {
492 const char* matchline = "hierarchical_memsw_limit";
493 char* format = "%s " JULONG_FORMAT;
494 GET_CONTAINER_INFO_LINE(julong, memory, "/memory.stat", matchline,
495 "Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, format, hier_memlimit)
496 if (hier_memlimit >= _unlimited_memory) {
497 log_trace(os, container)("Hierarchical Memory and Swap Limit is: Unlimited");
498 } else {
499 return (jlong)hier_memlimit;
500 }
501 }
502 return (jlong)-1;
503 } else {
504 return (jlong)memswlimit;
505 }
506 }
507
508 jlong OSContainer::memory_soft_limit_in_bytes() {
509 GET_CONTAINER_INFO(julong, memory, "/memory.soft_limit_in_bytes",
510 "Memory Soft Limit is: " JULONG_FORMAT, JULONG_FORMAT, memsoftlimit);
511 if (memsoftlimit >= _unlimited_memory) {
512 log_trace(os, container)("Memory Soft Limit is: Unlimited");
513 return (jlong)-1;
514 } else {
515 return (jlong)memsoftlimit;
516 }
517 }
518
519 /* memory_usage_in_bytes
520 *
521 * Return the amount of used memory for this process.
|