* Copyright (c) 2020, 2022, Red Hat Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "cgroupV2Subsystem_linux.hpp"
*
* Return the amount of cpu shares available to the process
*
* return:
* Share number (typically a number relative to 1024)
* (2048 typically expresses 2 CPUs worth of processing)
* -1 for no share setup
* OSCONTAINER_ERROR for not supported
*/
int CgroupV2Subsystem::cpu_shares() {
GET_CONTAINER_INFO(int, _unified, "/cpu.weight",
"Raw value for CPU Shares is: %d", "%d", shares);
if (shares == 100) {
if (PrintContainerInfo) {
tty->print_cr("CPU Shares is: %d", -1);
}
return -1;
}
int x = 262142 * shares - 1;
double frac = x/9999.0;
x = ((int)frac) + 2;
if (PrintContainerInfo) {
tty->print_cr("Scaled CPU shares value is: %d", x);
}
if ( x <= PER_CPU_SHARES ) {
if (PrintContainerInfo) {
tty->print_cr("CPU Shares is: %d", x);
}
return x;
}
int f = x/PER_CPU_SHARES;
int lower_multiple = f * PER_CPU_SHARES;
int upper_multiple = (f + 1) * PER_CPU_SHARES;
int distance_lower = MAX2(lower_multiple, x) - MIN2(lower_multiple, x);
int distance_upper = MAX2(upper_multiple, x) - MIN2(upper_multiple, x);
x = distance_lower <= distance_upper ? lower_multiple : upper_multiple;
if (PrintContainerInfo) {
tty->print_cr("Closest multiple of %d of the CPU Shares value is: %d", PER_CPU_SHARES, x);
tty->print_cr("CPU Shares is: %d", x);
}
return x;
}
*
* Return the number of microseconds per period
* process is guaranteed to run.
*
* return:
* quota time in microseconds
* -1 for no quota
* OSCONTAINER_ERROR for not supported
*/
int CgroupV2Subsystem::cpu_quota() {
char * cpu_quota_str = cpu_quota_val();
int limit = (int)limit_from_str(cpu_quota_str);
if (PrintContainerInfo) {
tty->print_cr("CPU Quota is: %d", limit);
}
return limit;
}
char * CgroupV2Subsystem::cpu_cpuset_cpus() {
GET_CONTAINER_INFO_CPTR(cptr, _unified, "/cpuset.cpus",
"cpuset.cpus is: %s", "%1023s", cpus, 1024);
if (cpus == NULL) {
return NULL;
}
return os::strdup(cpus);
}
char* CgroupV2Subsystem::cpu_quota_val() {
GET_CONTAINER_INFO_CPTR(cptr, _unified, "/cpu.max",
"Raw value for CPU quota is: %s", "%s %*d", quota, 1024);
if (quota == NULL) {
return NULL;
}
return os::strdup(quota);
}
char * CgroupV2Subsystem::cpu_cpuset_memory_nodes() {
GET_CONTAINER_INFO_CPTR(cptr, _unified, "/cpuset.mems",
"cpuset.mems is: %s", "%1023s", mems, 1024);
if (mems == NULL) {
return NULL;
}
return os::strdup(mems);
}
int CgroupV2Subsystem::cpu_period() {
GET_CONTAINER_INFO(int, _unified, "/cpu.max",
"CPU Period is: %d", "%*s %d", period);
return period;
}
*
* Return the amount of used memory used by this cgroup and decendents
*
* return:
* memory usage in bytes or
* -1 for unlimited
* OSCONTAINER_ERROR for not supported
*/
jlong CgroupV2Subsystem::memory_usage_in_bytes() {
GET_CONTAINER_INFO(jlong, _unified, "/memory.current",
"Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memusage);
return memusage;
}
jlong CgroupV2Subsystem::memory_soft_limit_in_bytes() {
char* mem_soft_limit_str = mem_soft_limit_val();
return limit_from_str(mem_soft_limit_str);
}
jlong CgroupV2Subsystem::memory_max_usage_in_bytes() {
if (PrintContainerInfo) {
tty->print_cr("Maximum Memory Usage is not supported.");
}
return OSCONTAINER_ERROR;
}
char* CgroupV2Subsystem::mem_soft_limit_val() {
GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.low",
"Memory Soft Limit is: %s", "%s", mem_soft_limit_str, 1024);
if (mem_soft_limit_str == NULL) {
return NULL;
}
return os::strdup(mem_soft_limit_str);
}
jlong CgroupV2Subsystem::memory_and_swap_limit_in_bytes() {
char* mem_swp_limit_str = mem_swp_limit_val();
jlong swap_limit = limit_from_str(mem_swp_limit_str);
if (swap_limit >= 0) {
jlong memory_limit = read_memory_limit_in_bytes();
assert(memory_limit >= 0, "swap limit without memory limit?");
return memory_limit + swap_limit;
}
return swap_limit;
}
char* CgroupV2Subsystem::mem_swp_limit_val() {
GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.swap.max",
"Memory and Swap Limit is: %s", "%s", mem_swp_limit_str, 1024);
if (mem_swp_limit_str == NULL) {
return NULL;
}
return os::strdup(mem_swp_limit_str);
}
char* CgroupV2Subsystem::mem_swp_current_val() {
GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.swap.current",
"Swap currently used is: %s", "%s", mem_swp_current_str, 1024);
if (mem_swp_current_str == NULL) {
return NULL;
}
return os::strdup(mem_swp_current_str);
}
*
* Return the limit of available memory for this process.
*
* return:
* memory limit in bytes or
* -1 for unlimited, OSCONTAINER_ERROR for an error
*/
jlong CgroupV2Subsystem::read_memory_limit_in_bytes() {
char * mem_limit_str = mem_limit_val();
jlong limit = limit_from_str(mem_limit_str);
if (PrintContainerInfo) {
if (limit == -1) {
tty->print_cr("Memory Limit is: Unlimited");
} else {
tty->print_cr("Memory Limit is: " JLONG_FORMAT, limit);
}
}
return limit;
}
jlong CgroupV2Subsystem::limit_from_str(char* limit_str) {
if (limit_str == NULL) {
return OSCONTAINER_ERROR;
}
if (strcmp("max", limit_str) == 0) {
os::free(limit_str);
return (jlong)-1;
}
julong limit;
if (sscanf(limit_str, JULONG_FORMAT, &limit) != 1) {
os::free(limit_str);
return OSCONTAINER_ERROR;
}
os::free(limit_str);
return (jlong)limit;
}
char* CgroupV2Subsystem::mem_limit_val() {
GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.max",
"Raw value for memory limit is: %s", "%s", mem_limit_str, 1024);
if (mem_limit_str == NULL) {
return NULL;
}
return os::strdup(mem_limit_str);
}
void CgroupV2Subsystem::print_version_specific_info(outputStream* st) {
char* mem_swp_current_str = mem_swp_current_val();
jlong swap_current = limit_from_str(mem_swp_current_str);
char* mem_swp_limit_str = mem_swp_limit_val();
jlong swap_limit = limit_from_str(mem_swp_limit_str);
OSContainer::print_container_helper(st, swap_current, "memory_swap_current_in_bytes");
OSContainer::print_container_helper(st, swap_limit, "memory_swap_max_limit_in_bytes");
}
char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) {
char buf[MAXPATHLEN+1];
int buflen;
strncpy(buf, mount_path, MAXPATHLEN);
buf[MAXPATHLEN] = '\0';
buflen = strlen(buf);
if ((buflen + strlen(cgroup_path)) > MAXPATHLEN) {
return NULL;
}
strncat(buf, cgroup_path, MAXPATHLEN-buflen);
buf[MAXPATHLEN] = '\0';
return os::strdup(buf);
}