8365606: Container code should not be using jlong/julong

Reviewed-by: stuefe, cnorrbin, fitzsim
This commit is contained in:
Severin Gehwolf 2025-11-18 09:42:28 +00:00
parent 50a3049737
commit 72ebca8a0b
16 changed files with 1207 additions and 799 deletions

View File

@ -612,7 +612,6 @@ void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) {
*
* cpu affinity
* cgroup cpu quota & cpu period
* cgroup cpu shares
*
* Algorithm:
*
@ -623,19 +622,18 @@ void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) {
*
* All results of division are rounded up to the next whole number.
*
* If quotas have not been specified, return the
* number of active processors in the system.
* If quotas have not been specified, sets the result reference to
* the number of active processors in the system.
*
* If quotas have been specified, the resulting number
* returned will never exceed the number of active processors.
* If quotas have been specified, the number set in the result
* reference will never exceed the number of active processors.
*
* return:
* number of CPUs
* true if there were no errors. false otherwise.
*/
int CgroupSubsystem::active_processor_count() {
int quota_count = 0;
bool CgroupSubsystem::active_processor_count(int& value) {
int cpu_count;
int result;
int result = -1;
// We use a cache with a timeout to avoid performing expensive
// computations in the event this function is called frequently.
@ -643,38 +641,50 @@ int CgroupSubsystem::active_processor_count() {
CachingCgroupController<CgroupCpuController>* contrl = cpu_controller();
CachedMetric* cpu_limit = contrl->metrics_cache();
if (!cpu_limit->should_check_metric()) {
int val = (int)cpu_limit->value();
log_trace(os, container)("CgroupSubsystem::active_processor_count (cached): %d", val);
return val;
value = (int)cpu_limit->value();
log_trace(os, container)("CgroupSubsystem::active_processor_count (cached): %d", value);
return true;
}
cpu_count = os::Linux::active_processor_count();
result = CgroupUtil::processor_count(contrl->controller(), cpu_count);
if (!CgroupUtil::processor_count(contrl->controller(), cpu_count, result)) {
return false;
}
assert(result > 0 && result <= cpu_count, "must be");
// Update cached metric to avoid re-reading container settings too often
cpu_limit->set_value(result, OSCONTAINER_CACHE_TIMEOUT);
value = result;
return result;
return true;
}
/* memory_limit_in_bytes
*
* Return the limit of available memory for this process.
* Return the limit of available memory for this process in the provided
* physical_memory_size_type reference. If there was no limit value set in the underlying
* interface files 'value_unlimited' is returned.
*
* return:
* memory limit in bytes or
* -1 for unlimited
* OSCONTAINER_ERROR for not supported
* false if retrieving the value failed
* true if retrieving the value was successfull and the value was
* set in the 'value' reference.
*/
jlong CgroupSubsystem::memory_limit_in_bytes(julong upper_bound) {
bool CgroupSubsystem::memory_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& value) {
CachingCgroupController<CgroupMemoryController>* contrl = memory_controller();
CachedMetric* memory_limit = contrl->metrics_cache();
if (!memory_limit->should_check_metric()) {
return memory_limit->value();
value = memory_limit->value();
return true;
}
physical_memory_size_type mem_limit = 0;
if (!contrl->controller()->read_memory_limit_in_bytes(upper_bound, mem_limit)) {
return false;
}
jlong mem_limit = contrl->controller()->read_memory_limit_in_bytes(upper_bound);
// Update cached metric to avoid re-reading container settings too often
memory_limit->set_value(mem_limit, OSCONTAINER_CACHE_TIMEOUT);
return mem_limit;
value = mem_limit;
return true;
}
bool CgroupController::read_string(const char* filename, char* buf, size_t buf_size) {
@ -719,36 +729,35 @@ bool CgroupController::read_string(const char* filename, char* buf, size_t buf_s
return true;
}
bool CgroupController::read_number(const char* filename, julong* result) {
bool CgroupController::read_number(const char* filename, uint64_t& result) {
char buf[1024];
bool is_ok = read_string(filename, buf, 1024);
if (!is_ok) {
return false;
}
int matched = sscanf(buf, JULONG_FORMAT, result);
int matched = sscanf(buf, UINT64_FORMAT, &result);
if (matched == 1) {
return true;
}
return false;
}
bool CgroupController::read_number_handle_max(const char* filename, jlong* result) {
bool CgroupController::read_number_handle_max(const char* filename, uint64_t& result) {
char buf[1024];
bool is_ok = read_string(filename, buf, 1024);
if (!is_ok) {
return false;
}
jlong val = limit_from_str(buf);
if (val == OSCONTAINER_ERROR) {
uint64_t val = 0;
if (!limit_from_str(buf, val)) {
return false;
}
*result = val;
result = val;
return true;
}
bool CgroupController::read_numerical_key_value(const char* filename, const char* key, julong* result) {
bool CgroupController::read_numerical_key_value(const char* filename, const char* key, uint64_t& result) {
assert(key != nullptr, "key must be given");
assert(result != nullptr, "result pointer must not be null");
assert(filename != nullptr, "file to search in must be given");
const char* s_path = subsystem_path();
if (s_path == nullptr) {
@ -786,7 +795,7 @@ bool CgroupController::read_numerical_key_value(const char* filename, const char
&& after_key != '\n') {
// Skip key, skip space
const char* value_substr = line + key_len + 1;
int matched = sscanf(value_substr, JULONG_FORMAT, result);
int matched = sscanf(value_substr, UINT64_FORMAT, &result);
found_match = matched == 1;
if (found_match) {
break;
@ -797,12 +806,12 @@ bool CgroupController::read_numerical_key_value(const char* filename, const char
if (found_match) {
return true;
}
log_debug(os, container)("Type %s (key == %s) not found in file %s", JULONG_FORMAT,
log_debug(os, container)("Type %s (key == %s) not found in file %s", UINT64_FORMAT,
key, absolute_path);
return false;
}
bool CgroupController::read_numerical_tuple_value(const char* filename, bool use_first, jlong* result) {
bool CgroupController::read_numerical_tuple_value(const char* filename, bool use_first, uint64_t& result) {
char buf[1024];
bool is_ok = read_string(filename, buf, 1024);
if (!is_ok) {
@ -813,80 +822,90 @@ bool CgroupController::read_numerical_tuple_value(const char* filename, bool use
if (matched != 1) {
return false;
}
jlong val = limit_from_str(token);
if (val == OSCONTAINER_ERROR) {
uint64_t val = 0;
if (!limit_from_str(token, val)) {
return false;
}
*result = val;
result = val;
return true;
}
jlong CgroupController::limit_from_str(char* limit_str) {
bool CgroupController::limit_from_str(char* limit_str, uint64_t& value) {
if (limit_str == nullptr) {
return OSCONTAINER_ERROR;
return false;
}
// Unlimited memory in cgroups is the literal string 'max' for
// some controllers, for example the pids controller.
if (strcmp("max", limit_str) == 0) {
return (jlong)-1;
value = value_unlimited;
return true;
}
julong limit;
if (sscanf(limit_str, JULONG_FORMAT, &limit) != 1) {
return OSCONTAINER_ERROR;
uint64_t limit;
if (sscanf(limit_str, UINT64_FORMAT, &limit) != 1) {
return false;
}
return (jlong)limit;
value = limit;
return true;
}
// CgroupSubsystem implementations
jlong CgroupSubsystem::memory_and_swap_limit_in_bytes(julong upper_mem_bound, julong upper_swap_bound) {
return memory_controller()->controller()->memory_and_swap_limit_in_bytes(upper_mem_bound, upper_swap_bound);
bool CgroupSubsystem::memory_and_swap_limit_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& value) {
return memory_controller()->controller()->memory_and_swap_limit_in_bytes(upper_mem_bound,
upper_swap_bound,
value);
}
jlong CgroupSubsystem::memory_and_swap_usage_in_bytes(julong upper_mem_bound, julong upper_swap_bound) {
return memory_controller()->controller()->memory_and_swap_usage_in_bytes(upper_mem_bound, upper_swap_bound);
bool CgroupSubsystem::memory_and_swap_usage_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& value) {
return memory_controller()->controller()->memory_and_swap_usage_in_bytes(upper_mem_bound,
upper_swap_bound,
value);
}
jlong CgroupSubsystem::memory_soft_limit_in_bytes(julong upper_bound) {
return memory_controller()->controller()->memory_soft_limit_in_bytes(upper_bound);
bool CgroupSubsystem::memory_soft_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& value) {
return memory_controller()->controller()->memory_soft_limit_in_bytes(upper_bound, value);
}
jlong CgroupSubsystem::memory_throttle_limit_in_bytes() {
return memory_controller()->controller()->memory_throttle_limit_in_bytes();
bool CgroupSubsystem::memory_throttle_limit_in_bytes(physical_memory_size_type& value) {
return memory_controller()->controller()->memory_throttle_limit_in_bytes(value);
}
jlong CgroupSubsystem::memory_usage_in_bytes() {
return memory_controller()->controller()->memory_usage_in_bytes();
bool CgroupSubsystem::memory_usage_in_bytes(physical_memory_size_type& value) {
return memory_controller()->controller()->memory_usage_in_bytes(value);
}
jlong CgroupSubsystem::memory_max_usage_in_bytes() {
return memory_controller()->controller()->memory_max_usage_in_bytes();
bool CgroupSubsystem::memory_max_usage_in_bytes(physical_memory_size_type& value) {
return memory_controller()->controller()->memory_max_usage_in_bytes(value);
}
jlong CgroupSubsystem::rss_usage_in_bytes() {
return memory_controller()->controller()->rss_usage_in_bytes();
bool CgroupSubsystem::rss_usage_in_bytes(physical_memory_size_type& value) {
return memory_controller()->controller()->rss_usage_in_bytes(value);
}
jlong CgroupSubsystem::cache_usage_in_bytes() {
return memory_controller()->controller()->cache_usage_in_bytes();
bool CgroupSubsystem::cache_usage_in_bytes(physical_memory_size_type& value) {
return memory_controller()->controller()->cache_usage_in_bytes(value);
}
int CgroupSubsystem::cpu_quota() {
return cpu_controller()->controller()->cpu_quota();
bool CgroupSubsystem::cpu_quota(int& value) {
return cpu_controller()->controller()->cpu_quota(value);
}
int CgroupSubsystem::cpu_period() {
return cpu_controller()->controller()->cpu_period();
bool CgroupSubsystem::cpu_period(int& value) {
return cpu_controller()->controller()->cpu_period(value);
}
int CgroupSubsystem::cpu_shares() {
return cpu_controller()->controller()->cpu_shares();
bool CgroupSubsystem::cpu_shares(int& value) {
return cpu_controller()->controller()->cpu_shares(value);
}
jlong CgroupSubsystem::cpu_usage_in_micros() {
return cpuacct_controller()->cpu_usage_in_micros();
bool CgroupSubsystem::cpu_usage_in_micros(uint64_t& value) {
return cpuacct_controller()->cpu_usage_in_micros(value);
}
void CgroupSubsystem::print_version_specific_info(outputStream* st, julong upper_mem_bound) {
void CgroupSubsystem::print_version_specific_info(outputStream* st, physical_memory_size_type upper_mem_bound) {
memory_controller()->controller()->print_version_specific_info(st, upper_mem_bound);
}

View File

@ -72,23 +72,29 @@
#define CONTAINER_READ_NUMBER_CHECKED(controller, filename, log_string, retval) \
{ \
bool is_ok; \
is_ok = controller->read_number(filename, &retval); \
is_ok = controller->read_number(filename, retval); \
if (!is_ok) { \
log_trace(os, container)(log_string " failed: %d", OSCONTAINER_ERROR); \
return OSCONTAINER_ERROR; \
log_trace(os, container)(log_string " failed"); \
return false; \
} \
log_trace(os, container)(log_string " is: " JULONG_FORMAT, retval); \
log_trace(os, container)(log_string " is: " UINT64_FORMAT, retval); \
return true; \
}
#define CONTAINER_READ_NUMBER_CHECKED_MAX(controller, filename, log_string, retval) \
{ \
bool is_ok; \
is_ok = controller->read_number_handle_max(filename, &retval); \
is_ok = controller->read_number_handle_max(filename, retval); \
if (!is_ok) { \
log_trace(os, container)(log_string " failed: %d", OSCONTAINER_ERROR); \
return OSCONTAINER_ERROR; \
log_trace(os, container)(log_string " failed"); \
return false; \
} \
log_trace(os, container)(log_string " is: " JLONG_FORMAT, retval); \
if (retval == value_unlimited) { \
log_trace(os, container)(log_string " is: unlimited"); \
} else { \
log_trace(os, container)(log_string " is: " UINT64_FORMAT, retval); \
} \
return true; \
}
#define CONTAINER_READ_STRING_CHECKED(controller, filename, log_string, retval, buf_size) \
@ -96,7 +102,7 @@
bool is_ok; \
is_ok = controller->read_string(filename, retval, buf_size); \
if (!is_ok) { \
log_trace(os, container)(log_string " failed: %d", OSCONTAINER_ERROR); \
log_trace(os, container)(log_string " failed"); \
return nullptr; \
} \
log_trace(os, container)(log_string " is: %s", retval); \
@ -105,12 +111,13 @@
#define CONTAINER_READ_NUMERICAL_KEY_VALUE_CHECKED(controller, filename, key, log_string, retval) \
{ \
bool is_ok; \
is_ok = controller->read_numerical_key_value(filename, key, &retval); \
is_ok = controller->read_numerical_key_value(filename, key, retval); \
if (!is_ok) { \
log_trace(os, container)(log_string " failed: %d", OSCONTAINER_ERROR); \
return OSCONTAINER_ERROR; \
log_trace(os, container)(log_string " failed"); \
return false; \
} \
log_trace(os, container)(log_string " is: " JULONG_FORMAT, retval); \
log_trace(os, container)(log_string " is: " UINT64_FORMAT, retval); \
return true; \
}
class CgroupController: public CHeapObj<mtInternal> {
@ -124,21 +131,22 @@ class CgroupController: public CHeapObj<mtInternal> {
const char* mount_point() { return _mount_point; }
virtual bool needs_hierarchy_adjustment() { return false; }
/* Read a numerical value as unsigned long
/* Read a numerical value as uint64_t
*
* returns: false if any error occurred. true otherwise and
* the parsed value is set in the provided julong pointer.
* the parsed value is set in the provided result reference.
*/
bool read_number(const char* filename, julong* result);
bool read_number(const char* filename, uint64_t& result);
/* Convenience method to deal with numbers as well as the string 'max'
* in interface files. Otherwise same as read_number().
*
* returns: false if any error occurred. true otherwise and
* the parsed value (which might be negative) is being set in
* the provided jlong pointer.
* the parsed value will be set in the provided result reference.
* When the value was the string 'max' then 'value_unlimited' is
* being set as the value.
*/
bool read_number_handle_max(const char* filename, jlong* result);
bool read_number_handle_max(const char* filename, uint64_t& result);
/* Read a string of at most buf_size - 1 characters from the interface file.
* The provided buffer must be at least buf_size in size so as to account
@ -156,37 +164,37 @@ class CgroupController: public CHeapObj<mtInternal> {
* parsing interface files like cpu.max which contain such tuples.
*
* returns: false if any error occurred. true otherwise and the parsed
* value of the appropriate tuple entry set in the provided jlong pointer.
* value of the appropriate tuple entry set in the provided result reference.
*/
bool read_numerical_tuple_value(const char* filename, bool use_first, jlong* result);
bool read_numerical_tuple_value(const char* filename, bool use_first, uint64_t& result);
/* Read a numerical value from a multi-line interface file. The matched line is
* determined by the provided 'key'. The associated numerical value is being set
* via the passed in julong pointer. Example interface file 'memory.stat'
* via the passed in result reference. Example interface file 'memory.stat'
*
* returns: false if any error occurred. true otherwise and the parsed value is
* being set in the provided julong pointer.
* being set in the provided result reference.
*/
bool read_numerical_key_value(const char* filename, const char* key, julong* result);
bool read_numerical_key_value(const char* filename, const char* key, uint64_t& result);
private:
static jlong limit_from_str(char* limit_str);
static bool limit_from_str(char* limit_str, physical_memory_size_type& value);
};
class CachedMetric : public CHeapObj<mtInternal>{
private:
volatile jlong _metric;
volatile physical_memory_size_type _metric;
volatile jlong _next_check_counter;
public:
CachedMetric() {
_metric = -1;
_metric = value_unlimited;
_next_check_counter = min_jlong;
}
bool should_check_metric() {
return os::elapsed_counter() > _next_check_counter;
}
jlong value() { return _metric; }
void set_value(jlong value, jlong timeout) {
physical_memory_size_type value() { return _metric; }
void set_value(physical_memory_size_type value, jlong timeout) {
_metric = value;
// Metric is unlikely to change, but we want to remain
// responsive to configuration changes. A very short grace time
@ -216,9 +224,9 @@ class CachingCgroupController : public CHeapObj<mtInternal> {
// Pure virtual class representing version agnostic CPU controllers
class CgroupCpuController: public CHeapObj<mtInternal> {
public:
virtual int cpu_quota() = 0;
virtual int cpu_period() = 0;
virtual int cpu_shares() = 0;
virtual bool cpu_quota(int& value) = 0;
virtual bool cpu_period(int& value) = 0;
virtual bool cpu_shares(int& value) = 0;
virtual bool needs_hierarchy_adjustment() = 0;
virtual bool is_read_only() = 0;
virtual const char* subsystem_path() = 0;
@ -230,7 +238,7 @@ class CgroupCpuController: public CHeapObj<mtInternal> {
// Pure virtual class representing version agnostic CPU accounting controllers
class CgroupCpuacctController: public CHeapObj<mtInternal> {
public:
virtual jlong cpu_usage_in_micros() = 0;
virtual bool cpu_usage_in_micros(uint64_t& value) = 0;
virtual bool needs_hierarchy_adjustment() = 0;
virtual bool is_read_only() = 0;
virtual const char* subsystem_path() = 0;
@ -242,16 +250,22 @@ class CgroupCpuacctController: public CHeapObj<mtInternal> {
// Pure virtual class representing version agnostic memory controllers
class CgroupMemoryController: public CHeapObj<mtInternal> {
public:
virtual jlong read_memory_limit_in_bytes(julong upper_bound) = 0;
virtual jlong memory_usage_in_bytes() = 0;
virtual jlong memory_and_swap_limit_in_bytes(julong upper_mem_bound, julong upper_swap_bound) = 0;
virtual jlong memory_and_swap_usage_in_bytes(julong upper_mem_bound, julong upper_swap_bound) = 0;
virtual jlong memory_soft_limit_in_bytes(julong upper_bound) = 0;
virtual jlong memory_throttle_limit_in_bytes() = 0;
virtual jlong memory_max_usage_in_bytes() = 0;
virtual jlong rss_usage_in_bytes() = 0;
virtual jlong cache_usage_in_bytes() = 0;
virtual void print_version_specific_info(outputStream* st, julong upper_mem_bound) = 0;
virtual bool read_memory_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& value) = 0;
virtual bool memory_usage_in_bytes(physical_memory_size_type& value) = 0;
virtual bool memory_and_swap_limit_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& value) = 0;
virtual bool memory_and_swap_usage_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& value) = 0;
virtual bool memory_soft_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& value) = 0;
virtual bool memory_throttle_limit_in_bytes(physical_memory_size_type& value) = 0;
virtual bool memory_max_usage_in_bytes(physical_memory_size_type& value) = 0;
virtual bool rss_usage_in_bytes(physical_memory_size_type& value) = 0;
virtual bool cache_usage_in_bytes(physical_memory_size_type& value) = 0;
virtual void print_version_specific_info(outputStream* st, physical_memory_size_type upper_mem_bound) = 0;
virtual bool needs_hierarchy_adjustment() = 0;
virtual bool is_read_only() = 0;
virtual const char* subsystem_path() = 0;
@ -262,11 +276,11 @@ class CgroupMemoryController: public CHeapObj<mtInternal> {
class CgroupSubsystem: public CHeapObj<mtInternal> {
public:
jlong memory_limit_in_bytes(julong upper_bound);
int active_processor_count();
bool memory_limit_in_bytes(physical_memory_size_type upper_bound, physical_memory_size_type& value);
bool active_processor_count(int& value);
virtual jlong pids_max() = 0;
virtual jlong pids_current() = 0;
virtual bool pids_max(uint64_t& value) = 0;
virtual bool pids_current(uint64_t& value) = 0;
virtual bool is_containerized() = 0;
virtual char * cpu_cpuset_cpus() = 0;
@ -276,21 +290,26 @@ class CgroupSubsystem: public CHeapObj<mtInternal> {
virtual CachingCgroupController<CgroupCpuController>* cpu_controller() = 0;
virtual CgroupCpuacctController* cpuacct_controller() = 0;
int cpu_quota();
int cpu_period();
int cpu_shares();
bool cpu_quota(int& value);
bool cpu_period(int& value);
bool cpu_shares(int& value);
jlong cpu_usage_in_micros();
bool cpu_usage_in_micros(uint64_t& value);
jlong memory_usage_in_bytes();
jlong memory_and_swap_limit_in_bytes(julong upper_mem_bound, julong upper_swap_bound);
jlong memory_and_swap_usage_in_bytes(julong upper_mem_bound, julong upper_swap_bound);
jlong memory_soft_limit_in_bytes(julong upper_bound);
jlong memory_throttle_limit_in_bytes();
jlong memory_max_usage_in_bytes();
jlong rss_usage_in_bytes();
jlong cache_usage_in_bytes();
void print_version_specific_info(outputStream* st, julong upper_mem_bound);
bool memory_usage_in_bytes(physical_memory_size_type& value);
bool memory_and_swap_limit_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& value);
bool memory_and_swap_usage_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& value);
bool memory_soft_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& value);
bool memory_throttle_limit_in_bytes(physical_memory_size_type& value);
bool memory_max_usage_in_bytes(physical_memory_size_type& value);
bool rss_usage_in_bytes(physical_memory_size_type& value);
bool cache_usage_in_bytes(physical_memory_size_type& value);
void print_version_specific_info(outputStream* st, physical_memory_size_type upper_mem_bound);
};
// Utility class for storing info retrieved from /proc/cgroups,

View File

@ -25,13 +25,19 @@
#include "cgroupUtil_linux.hpp"
#include "os_linux.hpp"
int CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int host_cpus) {
assert(host_cpus > 0, "physical host cpus must be positive");
int limit_count = host_cpus;
int quota = cpu_ctrl->cpu_quota();
int period = cpu_ctrl->cpu_period();
bool CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int upper_bound, int& value) {
assert(upper_bound > 0, "upper bound of cpus must be positive");
int limit_count = upper_bound;
int quota = -1;
int period = -1;
if (!cpu_ctrl->cpu_quota(quota)) {
return false;
}
if (!cpu_ctrl->cpu_period(period)) {
return false;
}
int quota_count = 0;
int result = 0;
int result = upper_bound;
if (quota > -1 && period > 0) {
quota_count = ceilf((float)quota / (float)period);
@ -43,16 +49,50 @@ int CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int host_cpus) {
limit_count = quota_count;
}
result = MIN2(host_cpus, limit_count);
result = MIN2(upper_bound, limit_count);
log_trace(os, container)("OSContainer::active_processor_count: %d", result);
return result;
value = result;
return true;
}
// Get an updated memory limit. The return value is strictly less than or equal to the
// passed in 'lowest' value.
physical_memory_size_type CgroupUtil::get_updated_mem_limit(CgroupMemoryController* mem,
physical_memory_size_type lowest,
physical_memory_size_type upper_bound) {
assert(lowest <= upper_bound, "invariant");
physical_memory_size_type current_limit = value_unlimited;
if (mem->read_memory_limit_in_bytes(upper_bound, current_limit) && current_limit != value_unlimited) {
assert(current_limit <= upper_bound, "invariant");
if (lowest > current_limit) {
return current_limit;
}
}
return lowest;
}
// Get an updated cpu limit. The return value is strictly less than or equal to the
// passed in 'lowest' value.
int CgroupUtil::get_updated_cpu_limit(CgroupCpuController* cpu,
int lowest,
int upper_bound) {
assert(lowest > 0 && lowest <= upper_bound, "invariant");
int cpu_limit_val = -1;
if (CgroupUtil::processor_count(cpu, upper_bound, cpu_limit_val) && cpu_limit_val != upper_bound) {
assert(cpu_limit_val <= upper_bound, "invariant");
if (lowest > cpu_limit_val) {
return cpu_limit_val;
}
}
return lowest;
}
void CgroupUtil::adjust_controller(CgroupMemoryController* mem) {
assert(mem->cgroup_path() != nullptr, "invariant");
if (strstr(mem->cgroup_path(), "../") != nullptr) {
log_warning(os, container)("Cgroup memory controller path at '%s' seems to have moved to '%s', detected limits won't be accurate",
mem->mount_point(), mem->cgroup_path());
log_warning(os, container)("Cgroup memory controller path at '%s' seems to have moved "
"to '%s'. Detected limits won't be accurate",
mem->mount_point(), mem->cgroup_path());
mem->set_subsystem_path("/");
return;
}
@ -65,17 +105,18 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem) {
char* cg_path = os::strdup(orig);
char* last_slash;
assert(cg_path[0] == '/', "cgroup path must start with '/'");
julong phys_mem = static_cast<julong>(os::Linux::physical_memory());
physical_memory_size_type phys_mem = os::Linux::physical_memory();
char* limit_cg_path = nullptr;
jlong limit = mem->read_memory_limit_in_bytes(phys_mem);
jlong lowest_limit = limit < 0 ? phys_mem : limit;
julong orig_limit = ((julong)lowest_limit) != phys_mem ? lowest_limit : phys_mem;
physical_memory_size_type limit = value_unlimited;
physical_memory_size_type lowest_limit = phys_mem;
lowest_limit = get_updated_mem_limit(mem, lowest_limit, phys_mem);
physical_memory_size_type orig_limit = lowest_limit != phys_mem ? lowest_limit : phys_mem;
while ((last_slash = strrchr(cg_path, '/')) != cg_path) {
*last_slash = '\0'; // strip path
// update to shortened path and try again
mem->set_subsystem_path(cg_path);
limit = mem->read_memory_limit_in_bytes(phys_mem);
if (limit >= 0 && limit < lowest_limit) {
limit = get_updated_mem_limit(mem, lowest_limit, phys_mem);
if (limit < lowest_limit) {
lowest_limit = limit;
os::free(limit_cg_path); // handles nullptr
limit_cg_path = os::strdup(cg_path);
@ -83,24 +124,24 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem) {
}
// need to check limit at mount point
mem->set_subsystem_path("/");
limit = mem->read_memory_limit_in_bytes(phys_mem);
if (limit >= 0 && limit < lowest_limit) {
limit = get_updated_mem_limit(mem, lowest_limit, phys_mem);
if (limit < lowest_limit) {
lowest_limit = limit;
os::free(limit_cg_path); // handles nullptr
limit_cg_path = os::strdup("/");
}
assert(lowest_limit >= 0, "limit must be positive");
if ((julong)lowest_limit != orig_limit) {
assert(lowest_limit <= phys_mem, "limit must not exceed host memory");
if (lowest_limit != orig_limit) {
// we've found a lower limit anywhere in the hierarchy,
// set the path to the limit path
assert(limit_cg_path != nullptr, "limit path must be set");
mem->set_subsystem_path(limit_cg_path);
log_trace(os, container)("Adjusted controller path for memory to: %s. "
"Lowest limit was: " JLONG_FORMAT,
"Lowest limit was: " PHYS_MEM_TYPE_FORMAT,
mem->subsystem_path(),
lowest_limit);
} else {
log_trace(os, container)("Lowest limit was: " JLONG_FORMAT, lowest_limit);
log_trace(os, container)("Lowest limit was: " PHYS_MEM_TYPE_FORMAT, lowest_limit);
log_trace(os, container)("No lower limit found for memory in hierarchy %s, "
"adjusting to original path %s",
mem->mount_point(), orig);
@ -114,8 +155,9 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem) {
void CgroupUtil::adjust_controller(CgroupCpuController* cpu) {
assert(cpu->cgroup_path() != nullptr, "invariant");
if (strstr(cpu->cgroup_path(), "../") != nullptr) {
log_warning(os, container)("Cgroup cpu controller path at '%s' seems to have moved to '%s', detected limits won't be accurate",
cpu->mount_point(), cpu->cgroup_path());
log_warning(os, container)("Cgroup cpu controller path at '%s' seems to have moved "
"to '%s'. Detected limits won't be accurate",
cpu->mount_point(), cpu->cgroup_path());
cpu->set_subsystem_path("/");
return;
}
@ -129,15 +171,15 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu) {
char* last_slash;
assert(cg_path[0] == '/', "cgroup path must start with '/'");
int host_cpus = os::Linux::active_processor_count();
int cpus = CgroupUtil::processor_count(cpu, host_cpus);
int lowest_limit = cpus < host_cpus ? cpus: host_cpus;
int lowest_limit = host_cpus;
int cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus);
int orig_limit = lowest_limit != host_cpus ? lowest_limit : host_cpus;
char* limit_cg_path = nullptr;
while ((last_slash = strrchr(cg_path, '/')) != cg_path) {
*last_slash = '\0'; // strip path
// update to shortened path and try again
cpu->set_subsystem_path(cg_path);
cpus = CgroupUtil::processor_count(cpu, host_cpus);
cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus);
if (cpus != host_cpus && cpus < lowest_limit) {
lowest_limit = cpus;
os::free(limit_cg_path); // handles nullptr
@ -146,7 +188,7 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu) {
}
// need to check limit at mount point
cpu->set_subsystem_path("/");
cpus = CgroupUtil::processor_count(cpu, host_cpus);
cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus);
if (cpus != host_cpus && cpus < lowest_limit) {
lowest_limit = cpus;
os::free(limit_cg_path); // handles nullptr
@ -160,8 +202,7 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu) {
cpu->set_subsystem_path(limit_cg_path);
log_trace(os, container)("Adjusted controller path for cpu to: %s. "
"Lowest limit was: %d",
cpu->subsystem_path(),
lowest_limit);
cpu->subsystem_path(), lowest_limit);
} else {
log_trace(os, container)("Lowest limit was: %d", lowest_limit);
log_trace(os, container)("No lower limit found for cpu in hierarchy %s, "

View File

@ -31,13 +31,20 @@
class CgroupUtil: AllStatic {
public:
static int processor_count(CgroupCpuController* cpu, int host_cpus);
static bool processor_count(CgroupCpuController* cpu, int upper_bound, int& value);
// Given a memory controller, adjust its path to a point in the hierarchy
// that represents the closest memory limit.
static void adjust_controller(CgroupMemoryController* m);
// Given a cpu controller, adjust its path to a point in the hierarchy
// that represents the closest cpu limit.
static void adjust_controller(CgroupCpuController* c);
private:
static physical_memory_size_type get_updated_mem_limit(CgroupMemoryController* m,
physical_memory_size_type lowest,
physical_memory_size_type upper_bound);
static int get_updated_cpu_limit(CgroupCpuController* c,
int lowest,
int upper_bound);
};
#endif // CGROUP_UTIL_LINUX_HPP

View File

@ -124,10 +124,13 @@ void CgroupV1Controller::set_subsystem_path(const char* cgroup_path) {
}
}
jlong CgroupV1MemoryController::uses_mem_hierarchy() {
julong use_hierarchy;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.use_hierarchy", "Use Hierarchy", use_hierarchy);
return (jlong)use_hierarchy;
bool CgroupV1MemoryController::read_use_hierarchy_val(physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.use_hierarchy", "Use Hierarchy", result);
}
bool CgroupV1MemoryController::uses_mem_hierarchy() {
physical_memory_size_type use_hierarchy = 0;
return read_use_hierarchy_val(use_hierarchy) && use_hierarchy > 0;
}
/*
@ -141,125 +144,177 @@ bool CgroupV1Controller::needs_hierarchy_adjustment() {
return strcmp(_root, _cgroup_path) != 0;
}
static inline
void verbose_log(julong read_mem_limit, julong upper_mem_bound) {
if (log_is_enabled(Debug, os, container)) {
jlong mem_limit = (jlong)read_mem_limit; // account for negative values
if (mem_limit < 0 || read_mem_limit >= upper_mem_bound) {
const char *reason;
if (mem_limit == OSCONTAINER_ERROR) {
reason = "failed";
} else if (mem_limit == -1) {
reason = "unlimited";
} else {
assert(read_mem_limit >= upper_mem_bound, "Expected read value exceeding upper memory bound");
// Exceeding physical memory is treated as unlimited. This implementation
// caps it at host_mem since Cg v1 has no value to represent 'max'.
reason = "ignored";
}
log_debug(os, container)("container memory limit %s: " JLONG_FORMAT ", upper bound is " JLONG_FORMAT,
reason, mem_limit, upper_mem_bound);
bool CgroupV1MemoryController::read_memory_limit_val(physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.limit_in_bytes", "Memory Limit", result);
}
bool CgroupV1MemoryController::read_hierarchical_memory_limit_val(physical_memory_size_type& result) {
CONTAINER_READ_NUMERICAL_KEY_VALUE_CHECKED(reader(), "/memory.stat",
"hierarchical_memory_limit", "Hierarchical Memory Limit",
result);
}
bool CgroupV1MemoryController::read_memory_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& result) {
physical_memory_size_type memlimit = 0;
if (!read_memory_limit_val(memlimit)) {
log_trace(os, container)("container memory limit failed, upper bound is " PHYS_MEM_TYPE_FORMAT, upper_bound);
return false;
}
if (memlimit >= upper_bound) {
physical_memory_size_type hierlimit = 0;
if (uses_mem_hierarchy() && read_hierarchical_memory_limit_val(hierlimit) &&
hierlimit < upper_bound) {
log_trace(os, container)("Memory Limit is: " PHYS_MEM_TYPE_FORMAT, hierlimit);
result = hierlimit;
} else {
// Exceeding physical memory is treated as unlimited. This implementation
// caps it at host_mem since Cg v1 has no value to represent 'max'.
log_trace(os, container)("container memory limit ignored: " PHYS_MEM_TYPE_FORMAT
", upper bound is " PHYS_MEM_TYPE_FORMAT, memlimit, upper_bound);
result = value_unlimited;
}
} else {
result = memlimit;
}
return true;
}
jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong upper_bound) {
julong memlimit;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.limit_in_bytes", "Memory Limit", memlimit);
if (memlimit >= upper_bound && uses_mem_hierarchy()) {
CONTAINER_READ_NUMERICAL_KEY_VALUE_CHECKED(reader(), "/memory.stat",
"hierarchical_memory_limit", "Hierarchical Memory Limit",
memlimit);
}
verbose_log(memlimit, upper_bound);
return (jlong)((memlimit < upper_bound) ? memlimit : -1);
bool CgroupV1MemoryController::read_mem_swap(physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.memsw.limit_in_bytes", "Memory and Swap Limit", result);
}
/* read_mem_swap
bool CgroupV1MemoryController::read_hierarchical_mem_swap_val(physical_memory_size_type& result) {
CONTAINER_READ_NUMERICAL_KEY_VALUE_CHECKED(reader(), "/memory.stat",
"hierarchical_memsw_limit", "Hierarchical Memory and Swap Limit",
result);
}
/* memory_and_swap_limit_in_bytes
*
* Determine the memory and swap limit metric. Returns a positive limit value strictly
* lower than the physical memory and swap limit iff there is a limit. Otherwise a
* negative value is returned indicating the determined status.
* Determine the memory and swap limit metric. Sets the 'result' reference to a positive limit value or
* 'value_unlimited' (for unlimited).
*
* returns:
* * A number > 0 if the limit is available and lower than a physical upper bound.
* * OSCONTAINER_ERROR if the limit cannot be retrieved (i.e. not supported) or
* * -1 if there isn't any limit in place (note: includes values which exceed a physical
* upper bound)
* * false if an error occurred. 'result' reference remains unchanged.
* * true if the limit value has been set in the 'result' reference
*/
jlong CgroupV1MemoryController::read_mem_swap(julong upper_memsw_bound) {
julong memswlimit;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.memsw.limit_in_bytes", "Memory and Swap Limit", memswlimit);
if (memswlimit >= upper_memsw_bound && uses_mem_hierarchy()) {
CONTAINER_READ_NUMERICAL_KEY_VALUE_CHECKED(reader(), "/memory.stat",
"hierarchical_memsw_limit", "Hierarchical Memory and Swap Limit",
memswlimit);
bool CgroupV1MemoryController::memory_and_swap_limit_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& result) {
physical_memory_size_type total_mem_swap = upper_mem_bound + upper_swap_bound;
physical_memory_size_type memory_swap = 0;
bool mem_swap_read_failed = false;
if (!read_mem_swap(memory_swap)) {
mem_swap_read_failed = true;
}
if (memory_swap >= total_mem_swap) {
physical_memory_size_type hiermswlimit = 0;
if (uses_mem_hierarchy() && read_hierarchical_mem_swap_val(hiermswlimit) &&
hiermswlimit < total_mem_swap) {
log_trace(os, container)("Memory and Swap Limit is: " PHYS_MEM_TYPE_FORMAT, hiermswlimit);
memory_swap = hiermswlimit;
} else {
memory_swap = value_unlimited;
}
}
if (memory_swap == value_unlimited) {
log_trace(os, container)("Memory and Swap Limit is: Unlimited");
result = value_unlimited;
return true;
}
verbose_log(memswlimit, upper_memsw_bound);
return (jlong)((memswlimit < upper_memsw_bound) ? memswlimit : -1);
}
jlong CgroupV1MemoryController::memory_and_swap_limit_in_bytes(julong upper_mem_bound, julong upper_swap_bound) {
jlong memory_swap = read_mem_swap(upper_mem_bound + upper_swap_bound);
if (memory_swap == -1) {
return memory_swap;
}
// If there is a swap limit, but swappiness == 0, reset the limit
// to the memory limit. Do the same for cases where swap isn't
// supported.
jlong swappiness = read_mem_swappiness();
if (swappiness == 0 || memory_swap == OSCONTAINER_ERROR) {
jlong memlimit = read_memory_limit_in_bytes(upper_mem_bound);
if (memory_swap == OSCONTAINER_ERROR) {
log_trace(os, container)("Memory and Swap Limit has been reset to " JLONG_FORMAT " because swap is not supported", memlimit);
} else {
log_trace(os, container)("Memory and Swap Limit has been reset to " JLONG_FORMAT " because swappiness is 0", memlimit);
}
return memlimit;
physical_memory_size_type swappiness = 0;
if (!read_mem_swappiness(swappiness)) {
// assume no swap
mem_swap_read_failed = true;
}
return memory_swap;
if (swappiness == 0 || mem_swap_read_failed) {
physical_memory_size_type memlimit = value_unlimited;
if (!read_memory_limit_in_bytes(upper_mem_bound, memlimit)) {
return false;
}
if (memlimit == value_unlimited) {
result = value_unlimited; // No memory limit, thus no swap limit
return true;
}
if (mem_swap_read_failed) {
log_trace(os, container)("Memory and Swap Limit has been reset to " PHYS_MEM_TYPE_FORMAT
" because swap is not supported", memlimit);
} else {
log_trace(os, container)("Memory and Swap Limit has been reset to " PHYS_MEM_TYPE_FORMAT
" because swappiness is 0", memlimit);
}
result = memlimit;
return true;
}
result = memory_swap;
return true;
}
static inline
jlong memory_swap_usage_impl(CgroupController* ctrl) {
julong memory_swap_usage;
CONTAINER_READ_NUMBER_CHECKED(ctrl, "/memory.memsw.usage_in_bytes", "mem swap usage", memory_swap_usage);
return (jlong)memory_swap_usage;
bool memory_swap_usage_impl(CgroupController* ctrl, physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED(ctrl, "/memory.memsw.usage_in_bytes", "mem swap usage", result);
}
jlong CgroupV1MemoryController::memory_and_swap_usage_in_bytes(julong upper_mem_bound, julong upper_swap_bound) {
jlong memory_sw_limit = memory_and_swap_limit_in_bytes(upper_mem_bound, upper_swap_bound);
jlong memory_limit = read_memory_limit_in_bytes(upper_mem_bound);
if (memory_sw_limit > 0 && memory_limit > 0) {
jlong delta_swap = memory_sw_limit - memory_limit;
if (delta_swap > 0) {
return memory_swap_usage_impl(reader());
bool CgroupV1MemoryController::memory_and_swap_usage_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& result) {
physical_memory_size_type memory_sw_limit = value_unlimited;
if (!memory_and_swap_limit_in_bytes(upper_mem_bound, upper_swap_bound, memory_sw_limit)) {
return false;
}
physical_memory_size_type mem_limit_val = value_unlimited;
physical_memory_size_type memory_limit = value_unlimited;
if (read_memory_limit_in_bytes(upper_mem_bound, mem_limit_val)) {
if (mem_limit_val != value_unlimited) {
memory_limit = mem_limit_val;
}
}
return memory_usage_in_bytes();
}
jlong CgroupV1MemoryController::read_mem_swappiness() {
julong swappiness;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.swappiness", "Swappiness", swappiness);
return (jlong)swappiness;
}
jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong upper_bound) {
julong memsoftlimit;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.soft_limit_in_bytes", "Memory Soft Limit", memsoftlimit);
if (memsoftlimit >= upper_bound) {
log_trace(os, container)("Memory Soft Limit is: Unlimited");
return (jlong)-1;
} else {
return (jlong)memsoftlimit;
if (memory_sw_limit != value_unlimited && memory_limit != value_unlimited) {
if (memory_limit < memory_sw_limit) {
// swap allowed and > 0
physical_memory_size_type swap_usage = 0;
if (!memory_swap_usage_impl(reader(), swap_usage)) {
return false;
}
result = swap_usage;
return true;
}
}
return memory_usage_in_bytes(result);
}
jlong CgroupV1MemoryController::memory_throttle_limit_in_bytes() {
bool CgroupV1MemoryController::read_mem_swappiness(physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.swappiness", "Swappiness", result);
}
bool CgroupV1MemoryController::memory_soft_limit_val(physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.soft_limit_in_bytes", "Memory Soft Limit", result);
}
bool CgroupV1MemoryController::memory_soft_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& result) {
physical_memory_size_type mem_soft_limit = 0;
if (!memory_soft_limit_val(mem_soft_limit)) {
return false;
}
if (mem_soft_limit >= upper_bound) {
log_trace(os, container)("Memory Soft Limit is: Unlimited");
result = value_unlimited;
} else {
result = mem_soft_limit;
}
return true;
}
bool CgroupV1MemoryController::memory_throttle_limit_in_bytes(physical_memory_size_type& result) {
// Log this string at trace level so as to make tests happy.
log_trace(os, container)("Memory Throttle Limit is not supported.");
return OSCONTAINER_ERROR; // not supported
return false;
}
// Constructor
@ -288,80 +343,129 @@ bool CgroupV1Subsystem::is_containerized() {
_cpuset->is_read_only();
}
/* memory_usage_in_bytes
bool CgroupV1MemoryController::memory_usage_in_bytes(physical_memory_size_type& result) {
physical_memory_size_type memory_usage = 0;
if (!memory_usage_val(memory_usage)) {
return false;
}
result = memory_usage;
return true;
}
/* memory_usage_val
*
* Return the amount of used memory for this process.
* Read the amount of used memory for this process into the passed in reference 'result'
*
* return:
* memory usage in bytes or
* -1 for unlimited
* OSCONTAINER_ERROR for not supported
* true when reading of the file was successful and 'result' was set appropriately
* false when reading of the file failed
*/
jlong CgroupV1MemoryController::memory_usage_in_bytes() {
julong memusage;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.usage_in_bytes", "Memory Usage", memusage);
return (jlong)memusage;
bool CgroupV1MemoryController::memory_usage_val(physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.usage_in_bytes", "Memory Usage", result);
}
bool CgroupV1MemoryController::memory_max_usage_val(physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.max_usage_in_bytes", "Maximum Memory Usage", result);
}
/* memory_max_usage_in_bytes
*
* Return the maximum amount of used memory for this process.
* Return the maximum amount of used memory for this process in the
* result reference.
*
* return:
* max memory usage in bytes or
* OSCONTAINER_ERROR for not supported
* true if the result reference has been set
* false otherwise (e.g. on error)
*/
jlong CgroupV1MemoryController::memory_max_usage_in_bytes() {
julong memmaxusage;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.max_usage_in_bytes", "Maximum Memory Usage", memmaxusage);
return (jlong)memmaxusage;
}
jlong CgroupV1MemoryController::rss_usage_in_bytes() {
julong rss;
bool is_ok = reader()->read_numerical_key_value("/memory.stat", "rss", &rss);
if (!is_ok) {
return OSCONTAINER_ERROR;
bool CgroupV1MemoryController::memory_max_usage_in_bytes(physical_memory_size_type& result) {
physical_memory_size_type memory_max_usage = 0;
if (!memory_max_usage_val(memory_max_usage)) {
return false;
}
log_trace(os, container)("RSS usage is: " JULONG_FORMAT, rss);
return (jlong)rss;
result = memory_max_usage;
return true;
}
jlong CgroupV1MemoryController::cache_usage_in_bytes() {
julong cache;
bool is_ok = reader()->read_numerical_key_value("/memory.stat", "cache", &cache);
if (!is_ok) {
return OSCONTAINER_ERROR;
bool CgroupV1MemoryController::rss_usage_in_bytes(physical_memory_size_type& result) {
physical_memory_size_type rss = 0;
if (!reader()->read_numerical_key_value("/memory.stat", "rss", rss)) {
return false;
}
log_trace(os, container)("Cache usage is: " JULONG_FORMAT, cache);
return cache;
log_trace(os, container)("RSS usage is: " PHYS_MEM_TYPE_FORMAT, rss);
result = rss;
return true;
}
jlong CgroupV1MemoryController::kernel_memory_usage_in_bytes() {
julong kmem_usage;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.kmem.usage_in_bytes", "Kernel Memory Usage", kmem_usage);
return (jlong)kmem_usage;
bool CgroupV1MemoryController::cache_usage_in_bytes(physical_memory_size_type& result) {
physical_memory_size_type cache = 0;
if (!reader()->read_numerical_key_value("/memory.stat", "cache", cache)) {
return false;
}
log_trace(os, container)("Cache usage is: " PHYS_MEM_TYPE_FORMAT, cache);
result = cache;
return true;
}
jlong CgroupV1MemoryController::kernel_memory_limit_in_bytes(julong upper_bound) {
julong kmem_limit;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.kmem.limit_in_bytes", "Kernel Memory Limit", kmem_limit);
bool CgroupV1MemoryController::kernel_memory_usage_val(physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.kmem.usage_in_bytes", "Kernel Memory Usage", result);
}
bool CgroupV1MemoryController::kernel_memory_usage_in_bytes(physical_memory_size_type& result) {
physical_memory_size_type kmem_usage = 0;
if (!kernel_memory_usage_val(kmem_usage)) {
return false;
}
result = kmem_usage;
return true;
}
bool CgroupV1MemoryController::kernel_memory_limit_val(physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.kmem.limit_in_bytes", "Kernel Memory Limit", result);
}
bool CgroupV1MemoryController::kernel_memory_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& result) {
physical_memory_size_type kmem_limit = 0;
if (!kernel_memory_limit_val(kmem_limit)) {
return false;
}
if (kmem_limit >= upper_bound) {
return (jlong)-1;
kmem_limit = value_unlimited;
}
return (jlong)kmem_limit;
result = kmem_limit;
return true;
}
jlong CgroupV1MemoryController::kernel_memory_max_usage_in_bytes() {
julong kmem_max_usage;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.kmem.max_usage_in_bytes", "Maximum Kernel Memory Usage", kmem_max_usage);
return (jlong)kmem_max_usage;
bool CgroupV1MemoryController::kernel_memory_max_usage_val(physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.kmem.max_usage_in_bytes", "Maximum Kernel Memory Usage", result);
}
void CgroupV1MemoryController::print_version_specific_info(outputStream* st, julong mem_bound) {
jlong kmem_usage = kernel_memory_usage_in_bytes();
jlong kmem_limit = kernel_memory_limit_in_bytes(mem_bound);
jlong kmem_max_usage = kernel_memory_max_usage_in_bytes();
bool CgroupV1MemoryController::kernel_memory_max_usage_in_bytes(physical_memory_size_type& result) {
physical_memory_size_type kmem_max_usage = 0;
if (!kernel_memory_max_usage_val(kmem_max_usage)) {
return false;
}
result = kmem_max_usage;
return true;
}
void CgroupV1MemoryController::print_version_specific_info(outputStream* st, physical_memory_size_type mem_bound) {
MetricResult kmem_usage;
physical_memory_size_type temp = 0;
if (kernel_memory_usage_in_bytes(temp)) {
kmem_usage.set_value(temp);
}
MetricResult kmem_limit;
temp = value_unlimited;
if (kernel_memory_limit_in_bytes(mem_bound, temp)) {
kmem_limit.set_value(temp);
}
MetricResult kmem_max_usage;
temp = 0;
if (kernel_memory_max_usage_in_bytes(temp)) {
kmem_max_usage.set_value(temp);
}
OSContainer::print_container_helper(st, kmem_limit, "kernel_memory_limit_in_bytes");
OSContainer::print_container_helper(st, kmem_usage, "kernel_memory_usage_in_bytes");
@ -383,74 +487,114 @@ char* CgroupV1Subsystem::cpu_cpuset_memory_nodes() {
/* cpu_quota
*
* Return the number of microseconds per period
* process is guaranteed to run.
* a process is guaranteed to run in the provided
* result reference.
*
* return:
* quota time in microseconds
* -1 for no quota
* OSCONTAINER_ERROR for not supported
* true if the value was set in the result reference
* false on failure to read the number from the file
* and the result reference has not been touched.
*/
int CgroupV1CpuController::cpu_quota() {
julong quota;
bool is_ok = reader()->read_number("/cpu.cfs_quota_us", &quota);
if (!is_ok) {
log_trace(os, container)("CPU Quota failed: %d", OSCONTAINER_ERROR);
return OSCONTAINER_ERROR;
bool CgroupV1CpuController::cpu_quota(int& result) {
uint64_t quota = 0;
// intentionally not using the macro so as to not log a
// negative value as a large unsiged int
if (!reader()->read_number("/cpu.cfs_quota_us", quota)) {
log_trace(os, container)("CPU Quota failed");
return false;
}
// cast to int since the read value might be negative
// and we want to avoid logging -1 as a large unsigned value.
int quota_int = (int)quota;
int quota_int = static_cast<int>(quota);
log_trace(os, container)("CPU Quota is: %d", quota_int);
return quota_int;
result = quota_int;
return true;
}
int CgroupV1CpuController::cpu_period() {
julong period;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpu.cfs_period_us", "CPU Period", period);
return (int)period;
bool CgroupV1CpuController::cpu_period_val(uint64_t& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpu.cfs_period_us", "CPU Period", result);
}
bool CgroupV1CpuController::cpu_period(int& result) {
uint64_t period = value_unlimited;
if (!cpu_period_val(period)) {
return false;
}
result = static_cast<int>(period);
return true;
}
bool CgroupV1CpuController::cpu_shares_val(uint64_t& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpu.shares", "CPU Shares", result);
}
/* cpu_shares
*
* Return the amount of cpu shares available to the process
* - Share number (typically a number relative to 1024)
* - (2048 typically expresses 2 CPUs worth of processing)
*
* 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
* false on error
* true if the result has been set in the result reference
*/
int CgroupV1CpuController::cpu_shares() {
julong shares;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpu.shares", "CPU Shares", shares);
int shares_int = (int)shares;
// Convert 1024 to no shares setup
if (shares_int == 1024) return -1;
bool CgroupV1CpuController::cpu_shares(int& result) {
uint64_t shares = 0;
if (!cpu_shares_val(shares)) {
return false;
}
int shares_int = static_cast<int>(shares);
// Convert 1024 to no shares setup (-1)
if (shares_int == 1024) {
shares_int = -1;
}
return shares_int;
result = shares_int;
return true;
}
jlong CgroupV1CpuacctController::cpu_usage_in_micros() {
julong cpu_usage;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpuacct.usage", "CPU Usage", cpu_usage);
bool CgroupV1CpuacctController::cpu_usage_in_micros_val(uint64_t& result) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpuacct.usage", "CPU Usage", result);
}
bool CgroupV1CpuacctController::cpu_usage_in_micros(uint64_t& result) {
uint64_t cpu_usage = 0;
if (!cpu_usage_in_micros_val(cpu_usage)) {
return false;
}
// Output is in nanoseconds, convert to microseconds.
return (jlong)cpu_usage / 1000;
result = static_cast<uint64_t>(cpu_usage / 1000);
return true;
}
static
bool pids_max_val(CgroupController* ctrl, uint64_t& result) {
CONTAINER_READ_NUMBER_CHECKED_MAX(ctrl, "/pids.max", "Maximum number of tasks", result);
}
/* pids_max
*
* Return the maximum number of tasks available to the process
* in the passed result reference (might be value_unlimited).
*
* return:
* maximum number of tasks
* -1 for unlimited
* OSCONTAINER_ERROR for not supported
* false on error
* true when the result reference has been appropriately set
*/
jlong CgroupV1Subsystem::pids_max() {
if (_pids == nullptr) return OSCONTAINER_ERROR;
jlong pids_max;
CONTAINER_READ_NUMBER_CHECKED_MAX(_pids, "/pids.max", "Maximum number of tasks", pids_max);
return pids_max;
bool CgroupV1Subsystem::pids_max(uint64_t& result) {
if (_pids == nullptr) return false;
uint64_t pids_val = 0;
if (!pids_max_val(_pids, pids_val)) {
return false;
}
result = pids_val;
return true;
}
static
bool pids_current_val(CgroupController* ctrl, uint64_t& result) {
CONTAINER_READ_NUMBER_CHECKED(ctrl, "/pids.current", "Current number of tasks", result);
}
/* pids_current
@ -458,12 +602,15 @@ jlong CgroupV1Subsystem::pids_max() {
* The number of tasks currently in the cgroup (and its descendants) of the process
*
* return:
* current number of tasks
* OSCONTAINER_ERROR for not supported
* true if the current number of tasks has been set in the result reference
* false if an error occurred
*/
jlong CgroupV1Subsystem::pids_current() {
if (_pids == nullptr) return OSCONTAINER_ERROR;
julong pids_current;
CONTAINER_READ_NUMBER_CHECKED(_pids, "/pids.current", "Current number of tasks", pids_current);
return (jlong)pids_current;
bool CgroupV1Subsystem::pids_current(uint64_t& result) {
if (_pids == nullptr) return false;
uint64_t pids_current = 0;
if (!pids_current_val(_pids, pids_current)) {
return false;
}
result = pids_current;
return true;
}

View File

@ -73,23 +73,44 @@ class CgroupV1MemoryController final : public CgroupMemoryController {
private:
CgroupV1Controller _reader;
CgroupV1Controller* reader() { return &_reader; }
bool read_memory_limit_val(physical_memory_size_type& result);
bool read_hierarchical_memory_limit_val(physical_memory_size_type& result);
bool read_hierarchical_mem_swap_val(physical_memory_size_type& result);
bool read_use_hierarchy_val(physical_memory_size_type& result);
bool memory_usage_val(physical_memory_size_type& result);
bool read_mem_swappiness(physical_memory_size_type& result);
bool read_mem_swap(physical_memory_size_type& result);
bool memory_soft_limit_val(physical_memory_size_type& result);
bool memory_max_usage_val(physical_memory_size_type& result);
bool kernel_memory_usage_val(physical_memory_size_type& result);
bool kernel_memory_limit_val(physical_memory_size_type& result);
bool kernel_memory_max_usage_val(physical_memory_size_type& result);
bool uses_mem_hierarchy();
public:
void set_subsystem_path(const char *cgroup_path) override {
reader()->set_subsystem_path(cgroup_path);
}
jlong read_memory_limit_in_bytes(julong upper_bound) override;
jlong memory_usage_in_bytes() override;
jlong memory_and_swap_limit_in_bytes(julong upper_mem_bound, julong upper_swap_bound) override;
jlong memory_and_swap_usage_in_bytes(julong upper_mem_bound, julong upper_swap_bound) override;
jlong memory_soft_limit_in_bytes(julong upper_bound) override;
jlong memory_throttle_limit_in_bytes() override;
jlong memory_max_usage_in_bytes() override;
jlong rss_usage_in_bytes() override;
jlong cache_usage_in_bytes() override;
jlong kernel_memory_usage_in_bytes();
jlong kernel_memory_limit_in_bytes(julong upper_bound);
jlong kernel_memory_max_usage_in_bytes();
void print_version_specific_info(outputStream* st, julong upper_mem_bound) override;
bool read_memory_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& value) override;
bool memory_usage_in_bytes(physical_memory_size_type& result) override;
bool memory_and_swap_limit_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& result) override;
bool memory_and_swap_usage_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& result) override;
bool memory_soft_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& result) override;
bool memory_throttle_limit_in_bytes(physical_memory_size_type& result) override;
bool memory_max_usage_in_bytes(physical_memory_size_type& result) override;
bool rss_usage_in_bytes(physical_memory_size_type& result) override;
bool cache_usage_in_bytes(physical_memory_size_type& result) override;
bool kernel_memory_usage_in_bytes(physical_memory_size_type& result);
bool kernel_memory_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& result);
bool kernel_memory_max_usage_in_bytes(physical_memory_size_type& result);
void print_version_specific_info(outputStream* st, physical_memory_size_type upper_mem_bound) override;
bool needs_hierarchy_adjustment() override {
return reader()->needs_hierarchy_adjustment();
}
@ -99,10 +120,6 @@ class CgroupV1MemoryController final : public CgroupMemoryController {
const char* subsystem_path() override { return reader()->subsystem_path(); }
const char* mount_point() override { return reader()->mount_point(); }
const char* cgroup_path() override { return reader()->cgroup_path(); }
private:
jlong uses_mem_hierarchy();
jlong read_mem_swappiness();
jlong read_mem_swap(julong upper_memsw_bound);
public:
CgroupV1MemoryController(const CgroupV1Controller& reader)
@ -116,10 +133,12 @@ class CgroupV1CpuController final : public CgroupCpuController {
private:
CgroupV1Controller _reader;
CgroupV1Controller* reader() { return &_reader; }
bool cpu_period_val(uint64_t& result);
bool cpu_shares_val(uint64_t& result);
public:
int cpu_quota() override;
int cpu_period() override;
int cpu_shares() override;
bool cpu_quota(int& result) override;
bool cpu_period(int& result) override;
bool cpu_shares(int& result) override;
void set_subsystem_path(const char *cgroup_path) override {
reader()->set_subsystem_path(cgroup_path);
}
@ -147,8 +166,9 @@ class CgroupV1CpuacctController final : public CgroupCpuacctController {
private:
CgroupV1Controller _reader;
CgroupV1Controller* reader() { return &_reader; }
bool cpu_usage_in_micros_val(uint64_t& result);
public:
jlong cpu_usage_in_micros() override;
bool cpu_usage_in_micros(uint64_t& result) override;
void set_subsystem_path(const char *cgroup_path) override {
reader()->set_subsystem_path(cgroup_path);
}
@ -180,15 +200,15 @@ class CgroupV1Subsystem: public CgroupSubsystem {
CgroupV1Controller* pids,
CgroupV1MemoryController* memory);
jlong kernel_memory_usage_in_bytes();
jlong kernel_memory_limit_in_bytes();
jlong kernel_memory_max_usage_in_bytes();
bool kernel_memory_usage_in_bytes(physical_memory_size_type& result);
bool kernel_memory_limit_in_bytes(physical_memory_size_type& result);
bool kernel_memory_max_usage_in_bytes(physical_memory_size_type& result);
char * cpu_cpuset_cpus();
char * cpu_cpuset_memory_nodes();
char * cpu_cpuset_cpus() override;
char * cpu_cpuset_memory_nodes() override;
jlong pids_max();
jlong pids_current();
bool pids_max(uint64_t& result) override;
bool pids_current(uint64_t& result) override;
bool is_containerized();
const char * container_type() {

View File

@ -44,24 +44,35 @@ CgroupV2Controller::CgroupV2Controller(const CgroupV2Controller& o) :
_mount_point = o._mount_point;
}
static
bool read_cpu_shares_value(CgroupV2Controller* ctrl, uint64_t& value) {
CONTAINER_READ_NUMBER_CHECKED(ctrl, "/cpu.weight", "Raw value for CPU Shares", value);
}
/* cpu_shares
*
* Return the amount of cpu shares available to the process
* Return the amount of cpu shares available to the process in the
* 'result' reference.
*
* 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
*
* return:
* true if the result reference got updated
* false if there was an error
*/
int CgroupV2CpuController::cpu_shares() {
julong shares;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpu.weight", "Raw value for CPU Shares", shares);
int shares_int = (int)shares;
bool CgroupV2CpuController::cpu_shares(int& result) {
uint64_t shares = 0;
bool is_ok = read_cpu_shares_value(reader(), shares);
if (!is_ok) {
return false;
}
int shares_int = static_cast<int>(shares);
// Convert default value of 100 to no shares setup
if (shares_int == 100) {
log_debug(os, container)("CPU Shares is: %d", -1);
return -1;
log_debug(os, container)("CPU Shares is: unlimited");
result = -1;
return true;
}
// cg v2 values must be in range [1-10000]
assert(shares_int >= 1 && shares_int <= 10000, "invariant");
@ -97,7 +108,8 @@ int CgroupV2CpuController::cpu_shares() {
// Don't do the multiples of PER_CPU_SHARES mapping since we
// have a value <= PER_CPU_SHARES
log_debug(os, container)("CPU Shares is: %d", x);
return x;
result = x;
return true;
}
int f = x/PER_CPU_SHARES;
int lower_multiple = f * PER_CPU_SHARES;
@ -107,28 +119,33 @@ int CgroupV2CpuController::cpu_shares() {
x = distance_lower <= distance_upper ? lower_multiple : upper_multiple;
log_trace(os, container)("Closest multiple of %d of the CPU Shares value is: %d", PER_CPU_SHARES, x);
log_debug(os, container)("CPU Shares is: %d", x);
return x;
result = x;
return true;
}
/* cpu_quota
*
* Return the number of microseconds per period
* process is guaranteed to run.
* process is guaranteed to run in the passed in 'result' reference.
*
* return:
* quota time in microseconds
* -1 for no quota
* OSCONTAINER_ERROR for not supported
* true if the result reference has been set
* false on error
*/
int CgroupV2CpuController::cpu_quota() {
jlong quota_val;
bool is_ok = reader()->read_numerical_tuple_value("/cpu.max", true /* use_first */, &quota_val);
if (!is_ok) {
return OSCONTAINER_ERROR;
bool CgroupV2CpuController::cpu_quota(int& result) {
uint64_t quota_val = 0;
if (!reader()->read_numerical_tuple_value("/cpu.max", true /* use_first */, quota_val)) {
return false;
}
int limit = -1;
// The read first tuple value might be 'max' which maps
// to value_unlimited. Keep that at -1;
if (quota_val != value_unlimited) {
limit = static_cast<int>(quota_val);
}
int limit = (int)quota_val;
log_trace(os, container)("CPU Quota is: %d", limit);
return limit;
result = limit;
return true;
}
// Constructor
@ -162,80 +179,67 @@ char* CgroupV2Subsystem::cpu_cpuset_memory_nodes() {
return os::strdup(mems);
}
int CgroupV2CpuController::cpu_period() {
jlong period_val;
bool is_ok = reader()->read_numerical_tuple_value("/cpu.max", false /* use_first */, &period_val);
if (!is_ok) {
log_trace(os, container)("CPU Period failed: %d", OSCONTAINER_ERROR);
return OSCONTAINER_ERROR;
bool CgroupV2CpuController::cpu_period(int& result) {
uint64_t cpu_period = 0;
if (!reader()->read_numerical_tuple_value("/cpu.max", false /* use_first */, cpu_period)) {
log_trace(os, container)("CPU Period failed");
return false;
}
int period = (int)period_val;
log_trace(os, container)("CPU Period is: %d", period);
return period;
int period_int = static_cast<int>(cpu_period);
log_trace(os, container)("CPU Period is: %d", period_int);
result = period_int;
return true;
}
jlong CgroupV2CpuController::cpu_usage_in_micros() {
julong cpu_usage;
bool is_ok = reader()->read_numerical_key_value("/cpu.stat", "usage_usec", &cpu_usage);
bool CgroupV2CpuController::cpu_usage_in_micros(uint64_t& value) {
bool is_ok = reader()->read_numerical_key_value("/cpu.stat", "usage_usec", value);
if (!is_ok) {
log_trace(os, container)("CPU Usage failed: %d", OSCONTAINER_ERROR);
return OSCONTAINER_ERROR;
log_trace(os, container)("CPU Usage failed");
return false;
}
log_trace(os, container)("CPU Usage is: " JULONG_FORMAT, cpu_usage);
return (jlong)cpu_usage;
log_trace(os, container)("CPU Usage is: " UINT64_FORMAT, value);
return true;
}
/* memory_usage_in_bytes
*
* Return the amount of used memory used by this cgroup and descendents
* read the amount of used memory used by this cgroup and descendents
* into the passed in 'value' reference.
*
* return:
* memory usage in bytes or
* -1 for unlimited
* OSCONTAINER_ERROR for not supported
* false on failure, true otherwise.
*/
jlong CgroupV2MemoryController::memory_usage_in_bytes() {
julong memusage;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.current", "Memory Usage", memusage);
return (jlong)memusage;
bool CgroupV2MemoryController::memory_usage_in_bytes(physical_memory_size_type& value) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.current", "Memory Usage", value);
}
jlong CgroupV2MemoryController::memory_soft_limit_in_bytes(julong upper_bound) {
jlong mem_soft_limit;
CONTAINER_READ_NUMBER_CHECKED_MAX(reader(), "/memory.low", "Memory Soft Limit", mem_soft_limit);
return mem_soft_limit;
bool CgroupV2MemoryController::memory_soft_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& value) {
CONTAINER_READ_NUMBER_CHECKED_MAX(reader(), "/memory.low", "Memory Soft Limit", value);
}
jlong CgroupV2MemoryController::memory_throttle_limit_in_bytes() {
jlong mem_throttle_limit;
CONTAINER_READ_NUMBER_CHECKED_MAX(reader(), "/memory.high", "Memory Throttle Limit", mem_throttle_limit);
return mem_throttle_limit;
bool CgroupV2MemoryController::memory_throttle_limit_in_bytes(physical_memory_size_type& value) {
CONTAINER_READ_NUMBER_CHECKED_MAX(reader(), "/memory.high", "Memory Throttle Limit", value);
}
jlong CgroupV2MemoryController::memory_max_usage_in_bytes() {
julong mem_max_usage;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.peak", "Maximum Memory Usage", mem_max_usage);
return mem_max_usage;
bool CgroupV2MemoryController::memory_max_usage_in_bytes(physical_memory_size_type& value) {
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.peak", "Maximum Memory Usage", value);
}
jlong CgroupV2MemoryController::rss_usage_in_bytes() {
julong rss;
bool is_ok = reader()->read_numerical_key_value("/memory.stat", "anon", &rss);
if (!is_ok) {
return OSCONTAINER_ERROR;
bool CgroupV2MemoryController::rss_usage_in_bytes(physical_memory_size_type& value) {
if (!reader()->read_numerical_key_value("/memory.stat", "anon", value)) {
return false;
}
log_trace(os, container)("RSS usage is: " JULONG_FORMAT, rss);
return (jlong)rss;
log_trace(os, container)("RSS usage is: " PHYS_MEM_TYPE_FORMAT, value);
return true;
}
jlong CgroupV2MemoryController::cache_usage_in_bytes() {
julong cache;
bool is_ok = reader()->read_numerical_key_value("/memory.stat", "file", &cache);
if (!is_ok) {
return OSCONTAINER_ERROR;
bool CgroupV2MemoryController::cache_usage_in_bytes(physical_memory_size_type& value) {
if (!reader()->read_numerical_key_value("/memory.stat", "file", value)) {
return false;
}
log_trace(os, container)("Cache usage is: " JULONG_FORMAT, cache);
return (jlong)cache;
log_trace(os, container)("Cache usage is: " PHYS_MEM_TYPE_FORMAT, value);
return true;
}
// Note that for cgroups v2 the actual limits set for swap and
@ -243,91 +247,108 @@ jlong CgroupV2MemoryController::cache_usage_in_bytes() {
// respectively. In order to properly report a cgroup v1 like
// compound value we need to sum the two values. Setting a swap limit
// without also setting a memory limit is not allowed.
jlong CgroupV2MemoryController::memory_and_swap_limit_in_bytes(julong upper_mem_bound,
julong upper_swap_bound /* unused in cg v2 */) {
jlong swap_limit;
bool is_ok = reader()->read_number_handle_max("/memory.swap.max", &swap_limit);
if (!is_ok) {
bool CgroupV2MemoryController::memory_and_swap_limit_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound, /* unused in cg v2 */
physical_memory_size_type& result) {
physical_memory_size_type swap_limit_val = 0;
if (!reader()->read_number_handle_max("/memory.swap.max", swap_limit_val)) {
// Some container tests rely on this trace logging to happen.
log_trace(os, container)("Swap Limit failed: %d", OSCONTAINER_ERROR);
log_trace(os, container)("Swap Limit failed");
// swap disabled at kernel level, treat it as no swap
return read_memory_limit_in_bytes(upper_mem_bound);
physical_memory_size_type mem_limit = value_unlimited;
if (!read_memory_limit_in_bytes(upper_mem_bound, mem_limit)) {
return false;
}
result = mem_limit;
return true;
}
log_trace(os, container)("Swap Limit is: " JLONG_FORMAT, swap_limit);
if (swap_limit >= 0) {
jlong memory_limit = read_memory_limit_in_bytes(upper_mem_bound);
assert(memory_limit >= 0, "swap limit without memory limit?");
return memory_limit + swap_limit;
if (swap_limit_val == value_unlimited) {
log_trace(os, container)("Memory and Swap Limit is: Unlimited");
result = swap_limit_val;
return true;
}
log_trace(os, container)("Swap Limit is: " PHYS_MEM_TYPE_FORMAT, swap_limit_val);
physical_memory_size_type memory_limit = 0;
if (read_memory_limit_in_bytes(upper_mem_bound, memory_limit)) {
assert(memory_limit != value_unlimited, "swap limit without memory limit?");
result = memory_limit + swap_limit_val;
log_trace(os, container)("Memory and Swap Limit is: " PHYS_MEM_TYPE_FORMAT, result);
return true;
} else {
return false;
}
log_trace(os, container)("Memory and Swap Limit is: " JLONG_FORMAT, swap_limit);
return swap_limit;
}
// memory.swap.current : total amount of swap currently used by the cgroup and its descendants
static
jlong memory_swap_current_value(CgroupV2Controller* ctrl) {
julong swap_current;
CONTAINER_READ_NUMBER_CHECKED(ctrl, "/memory.swap.current", "Swap currently used", swap_current);
return (jlong)swap_current;
bool memory_swap_current_value(CgroupV2Controller* ctrl, physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED(ctrl, "/memory.swap.current", "Swap currently used", result);
}
jlong CgroupV2MemoryController::memory_and_swap_usage_in_bytes(julong upper_mem_bound, julong upper_swap_bound) {
jlong memory_usage = memory_usage_in_bytes();
if (memory_usage >= 0) {
jlong swap_current = memory_swap_current_value(reader());
return memory_usage + (swap_current >= 0 ? swap_current : 0);
bool CgroupV2MemoryController::memory_and_swap_usage_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& result) {
physical_memory_size_type memory_usage = 0;
if (!memory_usage_in_bytes(memory_usage)) {
return false;
}
return memory_usage; // not supported or unlimited case
physical_memory_size_type swap_current = 0;
if (!memory_swap_current_value(reader(), swap_current)) {
result = memory_usage; // treat as no swap usage
return true;
}
result = memory_usage + swap_current;
return true;
}
static
jlong memory_limit_value(CgroupV2Controller* ctrl) {
jlong memory_limit;
CONTAINER_READ_NUMBER_CHECKED_MAX(ctrl, "/memory.max", "Memory Limit", memory_limit);
return memory_limit;
bool memory_limit_value(CgroupV2Controller* ctrl, physical_memory_size_type& result) {
CONTAINER_READ_NUMBER_CHECKED_MAX(ctrl, "/memory.max", "Memory Limit", result);
}
/* read_memory_limit_in_bytes
*
* Return the limit of available memory for this process.
* Calculate the limit of available memory for this process. The result will be
* set in the 'result' variable if the function returns true.
*
* return:
* memory limit in bytes or
* -1 for unlimited, OSCONTAINER_ERROR for an error
* true when the limit could be read correctly.
* false in case of any error.
*/
jlong CgroupV2MemoryController::read_memory_limit_in_bytes(julong upper_bound) {
jlong limit = memory_limit_value(reader());
bool CgroupV2MemoryController::read_memory_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& result) {
physical_memory_size_type limit = 0; // default unlimited
if (!memory_limit_value(reader(), limit)) {
log_trace(os, container)("container memory limit failed, using host value " PHYS_MEM_TYPE_FORMAT,
upper_bound);
return false;
}
bool is_unlimited = limit == value_unlimited;
bool exceeds_physical_mem = false;
if (!is_unlimited && limit >= upper_bound) {
exceeds_physical_mem = true;
}
if (log_is_enabled(Trace, os, container)) {
if (limit == -1) {
log_trace(os, container)("Memory Limit is: Unlimited");
} else {
log_trace(os, container)("Memory Limit is: " JLONG_FORMAT, limit);
if (!is_unlimited) {
log_trace(os, container)("Memory Limit is: " PHYS_MEM_TYPE_FORMAT, limit);
}
}
if (log_is_enabled(Debug, os, container)) {
julong read_limit = (julong)limit; // avoid signed/unsigned compare
if (limit < 0 || read_limit >= upper_bound) {
const char* reason;
if (limit == -1) {
reason = "unlimited";
} else if (limit == OSCONTAINER_ERROR) {
reason = "failed";
if (is_unlimited || exceeds_physical_mem) {
if (is_unlimited) {
log_trace(os, container)("Memory Limit is: Unlimited");
log_trace(os, container)("container memory limit unlimited, using upper bound value " PHYS_MEM_TYPE_FORMAT, upper_bound);
} else {
assert(read_limit >= upper_bound, "Expected mem limit to exceed upper memory bound");
reason = "ignored";
log_trace(os, container)("container memory limit ignored: " PHYS_MEM_TYPE_FORMAT ", upper bound is " PHYS_MEM_TYPE_FORMAT,
limit, upper_bound);
}
log_debug(os, container)("container memory limit %s: " JLONG_FORMAT ", upper bound is " JLONG_FORMAT,
reason, limit, upper_bound);
}
}
return limit;
result = limit;
return true;
}
static
jlong memory_swap_limit_value(CgroupV2Controller* ctrl) {
jlong swap_limit;
CONTAINER_READ_NUMBER_CHECKED_MAX(ctrl, "/memory.swap.max", "Swap Limit", swap_limit);
return swap_limit;
bool memory_swap_limit_value(CgroupV2Controller* ctrl, physical_memory_size_type& value) {
CONTAINER_READ_NUMBER_CHECKED_MAX(ctrl, "/memory.swap.max", "Swap Limit", value);
}
void CgroupV2Controller::set_subsystem_path(const char* cgroup_path) {
@ -346,10 +367,17 @@ bool CgroupV2Controller::needs_hierarchy_adjustment() {
return strcmp(_cgroup_path, "/") != 0;
}
void CgroupV2MemoryController::print_version_specific_info(outputStream* st, julong upper_mem_bound) {
jlong swap_current = memory_swap_current_value(reader());
jlong swap_limit = memory_swap_limit_value(reader());
void CgroupV2MemoryController::print_version_specific_info(outputStream* st, physical_memory_size_type upper_mem_bound) {
MetricResult swap_current;
physical_memory_size_type swap_current_val = 0;
if (memory_swap_current_value(reader(), swap_current_val)) {
swap_current.set_value(swap_current_val);
}
MetricResult swap_limit;
physical_memory_size_type swap_limit_val = 0;
if (memory_swap_limit_value(reader(), swap_limit_val)) {
swap_limit.set_value(swap_limit_val);
}
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");
}
@ -365,29 +393,27 @@ char* CgroupV2Controller::construct_path(char* mount_path, const char* cgroup_pa
/* pids_max
*
* Return the maximum number of tasks available to the process
* Calculate the maximum number of tasks available to the process. Set the
* value in the passed in 'value' reference. The value might be 'value_unlimited' when
* there is no limit.
*
* return:
* maximum number of tasks
* -1 for unlimited
* OSCONTAINER_ERROR for not supported
* true if the value has been set appropriately
* false if there was an error
*/
jlong CgroupV2Subsystem::pids_max() {
jlong pids_max;
CONTAINER_READ_NUMBER_CHECKED_MAX(unified(), "/pids.max", "Maximum number of tasks", pids_max);
return pids_max;
bool CgroupV2Subsystem::pids_max(uint64_t& value) {
CONTAINER_READ_NUMBER_CHECKED_MAX(unified(), "/pids.max", "Maximum number of tasks", value);
}
/* pids_current
*
* The number of tasks currently in the cgroup (and its descendants) of the process
* The number of tasks currently in the cgroup (and its descendants) of the process. Set
* in the passed in 'value' reference.
*
* return:
* current number of tasks
* OSCONTAINER_ERROR for not supported
* true on success
* false when there was an error
*/
jlong CgroupV2Subsystem::pids_current() {
julong pids_current;
CONTAINER_READ_NUMBER_CHECKED(unified(), "/pids.current", "Current number of tasks", pids_current);
return pids_current;
bool CgroupV2Subsystem::pids_current(uint64_t& value) {
CONTAINER_READ_NUMBER_CHECKED(unified(), "/pids.current", "Current number of tasks", value);
}

View File

@ -59,10 +59,10 @@ class CgroupV2CpuController: public CgroupCpuController {
public:
CgroupV2CpuController(const CgroupV2Controller& reader) : _reader(reader) {
}
int cpu_quota() override;
int cpu_period() override;
int cpu_shares() override;
jlong cpu_usage_in_micros();
bool cpu_quota(int& value) override;
bool cpu_period(int& value) override;
bool cpu_shares(int& value) override;
bool cpu_usage_in_micros(uint64_t& value);
bool is_read_only() override {
return reader()->is_read_only();
}
@ -87,8 +87,8 @@ class CgroupV2CpuacctController: public CgroupCpuacctController {
CgroupV2CpuacctController(CgroupV2CpuController* reader) : _reader(reader) {
}
// In cgroup v2, cpu usage is a part of the cpu controller.
jlong cpu_usage_in_micros() override {
return reader()->cpu_usage_in_micros();
bool cpu_usage_in_micros(uint64_t& result) override {
return reader()->cpu_usage_in_micros(result);
}
bool is_read_only() override {
return reader()->is_read_only();
@ -110,20 +110,27 @@ class CgroupV2MemoryController final: public CgroupMemoryController {
private:
CgroupV2Controller _reader;
CgroupV2Controller* reader() { return &_reader; }
public:
CgroupV2MemoryController(const CgroupV2Controller& reader) : _reader(reader) {
}
jlong read_memory_limit_in_bytes(julong upper_bound) override;
jlong memory_and_swap_limit_in_bytes(julong upper_mem_bound, julong upper_swap_bound) override;
jlong memory_and_swap_usage_in_bytes(julong upper_mem_bound, julong upper_swap_bound) override;
jlong memory_soft_limit_in_bytes(julong upper_bound) override;
jlong memory_throttle_limit_in_bytes() override;
jlong memory_usage_in_bytes() override;
jlong memory_max_usage_in_bytes() override;
jlong rss_usage_in_bytes() override;
jlong cache_usage_in_bytes() override;
void print_version_specific_info(outputStream* st, julong upper_mem_bound) override;
bool read_memory_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& result) override;
bool memory_and_swap_limit_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& result) override;
bool memory_and_swap_usage_in_bytes(physical_memory_size_type upper_mem_bound,
physical_memory_size_type upper_swap_bound,
physical_memory_size_type& result) override;
bool memory_soft_limit_in_bytes(physical_memory_size_type upper_bound,
physical_memory_size_type& result) override;
bool memory_throttle_limit_in_bytes(physical_memory_size_type& result) override;
bool memory_usage_in_bytes(physical_memory_size_type& result) override;
bool memory_max_usage_in_bytes(physical_memory_size_type& result) override;
bool rss_usage_in_bytes(physical_memory_size_type& result) override;
bool cache_usage_in_bytes(physical_memory_size_type& result) override;
void print_version_specific_info(outputStream* st, physical_memory_size_type upper_mem_bound) override;
bool is_read_only() override {
return reader()->is_read_only();
}
@ -160,8 +167,8 @@ class CgroupV2Subsystem: public CgroupSubsystem {
char * cpu_cpuset_cpus() override;
char * cpu_cpuset_memory_nodes() override;
jlong pids_max() override;
jlong pids_current() override;
bool pids_max(uint64_t& result) override;
bool pids_current(uint64_t& result) override;
bool is_containerized() override;

View File

@ -84,8 +84,12 @@ void OSContainer::init() {
// We can be in one of two cases:
// 1.) On a physical Linux system without any limit
// 2.) On a physical Linux system with a limit enforced by other means (like systemd slice)
any_mem_cpu_limit_present = memory_limit_in_bytes() > 0 ||
os::Linux::active_processor_count() != active_processor_count();
physical_memory_size_type mem_limit_val = value_unlimited;
(void)memory_limit_in_bytes(mem_limit_val); // discard error and use default
int host_cpus = os::Linux::active_processor_count();
int cpus = host_cpus;
(void)active_processor_count(cpus); // discard error and use default
any_mem_cpu_limit_present = mem_limit_val != value_unlimited || host_cpus != cpus;
if (any_mem_cpu_limit_present) {
reason = " because either a cpu or a memory limit is present";
} else {
@ -103,77 +107,138 @@ const char * OSContainer::container_type() {
return cgroup_subsystem->container_type();
}
bool OSContainer::available_memory_in_container(julong& value) {
jlong mem_limit = memory_limit_in_bytes();
jlong mem_usage = memory_usage_in_bytes();
bool OSContainer::memory_limit_in_bytes(physical_memory_size_type& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
physical_memory_size_type phys_mem = os::Linux::physical_memory();
return cgroup_subsystem->memory_limit_in_bytes(phys_mem, value);
}
if (mem_limit > 0 && mem_usage <= 0) {
log_debug(os, container)("container memory usage failed: " JLONG_FORMAT, mem_usage);
bool OSContainer::available_memory_in_bytes(physical_memory_size_type& value) {
physical_memory_size_type mem_limit = value_unlimited;
physical_memory_size_type mem_usage = 0;
if (memory_limit_in_bytes(mem_limit) && memory_usage_in_bytes(mem_usage)) {
assert(mem_usage != value_unlimited, "invariant");
if (mem_limit != value_unlimited) {
value = (mem_limit > mem_usage) ? mem_limit - mem_usage : 0;
return true;
}
}
log_trace(os, container)("calculating available memory in container failed");
return false;
}
if (mem_limit <= 0 || mem_usage <= 0) {
bool OSContainer::available_swap_in_bytes(physical_memory_size_type host_free_swap,
physical_memory_size_type& value) {
physical_memory_size_type mem_limit = 0;
physical_memory_size_type mem_swap_limit = 0;
if (memory_limit_in_bytes(mem_limit) &&
memory_and_swap_limit_in_bytes(mem_swap_limit) &&
mem_limit != value_unlimited &&
mem_swap_limit != value_unlimited) {
if (mem_limit >= mem_swap_limit) {
value = 0; // no swap, thus no free swap
return true;
}
physical_memory_size_type swap_limit = mem_swap_limit - mem_limit;
physical_memory_size_type mem_swap_usage = 0;
physical_memory_size_type mem_usage = 0;
if (memory_and_swap_usage_in_bytes(mem_swap_usage) &&
memory_usage_in_bytes(mem_usage)) {
physical_memory_size_type swap_usage = value_unlimited;
if (mem_usage > mem_swap_usage) {
swap_usage = 0; // delta usage must not be negative
} else {
swap_usage = mem_swap_usage - mem_usage;
}
// free swap is based on swap limit (upper bound) and swap usage
if (swap_usage >= swap_limit) {
value = 0; // free swap must not be negative
return true;
}
value = swap_limit - swap_usage;
return true;
}
}
// unlimited or not supported. Leave an appropriate trace message
if (log_is_enabled(Trace, os, container)) {
char mem_swap_buf[25]; // uint64_t => 20 + 1, 'unlimited' => 9 + 1; 10 < 21 < 25
char mem_limit_buf[25];
int num = 0;
if (mem_swap_limit == value_unlimited) {
num = os::snprintf(mem_swap_buf, sizeof(mem_swap_buf), "%s", "unlimited");
} else {
num = os::snprintf(mem_swap_buf, sizeof(mem_swap_buf), PHYS_MEM_TYPE_FORMAT, mem_swap_limit);
}
assert(num < 25, "buffer too small");
mem_swap_buf[num] = '\0';
if (mem_limit == value_unlimited) {
num = os::snprintf(mem_limit_buf, sizeof(mem_limit_buf), "%s", "unlimited");
} else {
num = os::snprintf(mem_limit_buf, sizeof(mem_limit_buf), PHYS_MEM_TYPE_FORMAT, mem_limit);
}
assert(num < 25, "buffer too small");
mem_limit_buf[num] = '\0';
log_trace(os,container)("OSContainer::available_swap_in_bytes: container_swap_limit=%s"
" container_mem_limit=%s, host_free_swap: " PHYS_MEM_TYPE_FORMAT,
mem_swap_buf, mem_limit_buf, host_free_swap);
}
return false;
}
bool OSContainer::memory_and_swap_limit_in_bytes(physical_memory_size_type& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
physical_memory_size_type phys_mem = os::Linux::physical_memory();
physical_memory_size_type host_swap = 0;
if (!os::Linux::host_swap(host_swap)) {
return false;
}
value = mem_limit > mem_usage ? static_cast<julong>(mem_limit - mem_usage) : 0;
return true;
return cgroup_subsystem->memory_and_swap_limit_in_bytes(phys_mem, host_swap, value);
}
jlong OSContainer::memory_limit_in_bytes() {
bool OSContainer::memory_and_swap_usage_in_bytes(physical_memory_size_type& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
julong phys_mem = static_cast<julong>(os::Linux::physical_memory());
return cgroup_subsystem->memory_limit_in_bytes(phys_mem);
physical_memory_size_type phys_mem = os::Linux::physical_memory();
physical_memory_size_type host_swap = 0;
if (!os::Linux::host_swap(host_swap)) {
return false;
}
return cgroup_subsystem->memory_and_swap_usage_in_bytes(phys_mem, host_swap, value);
}
jlong OSContainer::memory_and_swap_limit_in_bytes() {
bool OSContainer::memory_soft_limit_in_bytes(physical_memory_size_type& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
julong phys_mem = static_cast<julong>(os::Linux::physical_memory());
julong host_swap = os::Linux::host_swap();
return cgroup_subsystem->memory_and_swap_limit_in_bytes(phys_mem, host_swap);
physical_memory_size_type phys_mem = os::Linux::physical_memory();
return cgroup_subsystem->memory_soft_limit_in_bytes(phys_mem, value);
}
jlong OSContainer::memory_and_swap_usage_in_bytes() {
bool OSContainer::memory_throttle_limit_in_bytes(physical_memory_size_type& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
julong phys_mem = static_cast<julong>(os::Linux::physical_memory());
julong host_swap = os::Linux::host_swap();
return cgroup_subsystem->memory_and_swap_usage_in_bytes(phys_mem, host_swap);
return cgroup_subsystem->memory_throttle_limit_in_bytes(value);
}
jlong OSContainer::memory_soft_limit_in_bytes() {
bool OSContainer::memory_usage_in_bytes(physical_memory_size_type& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
julong phys_mem = static_cast<julong>(os::Linux::physical_memory());
return cgroup_subsystem->memory_soft_limit_in_bytes(phys_mem);
return cgroup_subsystem->memory_usage_in_bytes(value);
}
jlong OSContainer::memory_throttle_limit_in_bytes() {
bool OSContainer::memory_max_usage_in_bytes(physical_memory_size_type& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->memory_throttle_limit_in_bytes();
return cgroup_subsystem->memory_max_usage_in_bytes(value);
}
jlong OSContainer::memory_usage_in_bytes() {
bool OSContainer::rss_usage_in_bytes(physical_memory_size_type& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->memory_usage_in_bytes();
return cgroup_subsystem->rss_usage_in_bytes(value);
}
jlong OSContainer::memory_max_usage_in_bytes() {
bool OSContainer::cache_usage_in_bytes(physical_memory_size_type& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->memory_max_usage_in_bytes();
}
jlong OSContainer::rss_usage_in_bytes() {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->rss_usage_in_bytes();
}
jlong OSContainer::cache_usage_in_bytes() {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->cache_usage_in_bytes();
return cgroup_subsystem->cache_usage_in_bytes(value);
}
void OSContainer::print_version_specific_info(outputStream* st) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
julong phys_mem = static_cast<julong>(os::Linux::physical_memory());
physical_memory_size_type phys_mem = os::Linux::physical_memory();
cgroup_subsystem->print_version_specific_info(st, phys_mem);
}
@ -187,50 +252,55 @@ char * OSContainer::cpu_cpuset_memory_nodes() {
return cgroup_subsystem->cpu_cpuset_memory_nodes();
}
int OSContainer::active_processor_count() {
bool OSContainer::active_processor_count(int& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->active_processor_count();
return cgroup_subsystem->active_processor_count(value);
}
int OSContainer::cpu_quota() {
bool OSContainer::cpu_quota(int& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->cpu_quota();
return cgroup_subsystem->cpu_quota(value);
}
int OSContainer::cpu_period() {
bool OSContainer::cpu_period(int& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->cpu_period();
return cgroup_subsystem->cpu_period(value);
}
int OSContainer::cpu_shares() {
bool OSContainer::cpu_shares(int& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->cpu_shares();
return cgroup_subsystem->cpu_shares(value);
}
jlong OSContainer::cpu_usage_in_micros() {
bool OSContainer::cpu_usage_in_micros(uint64_t& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->cpu_usage_in_micros();
return cgroup_subsystem->cpu_usage_in_micros(value);
}
jlong OSContainer::pids_max() {
bool OSContainer::pids_max(uint64_t& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->pids_max();
return cgroup_subsystem->pids_max(value);
}
jlong OSContainer::pids_current() {
bool OSContainer::pids_current(uint64_t& value) {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->pids_current();
return cgroup_subsystem->pids_current(value);
}
void OSContainer::print_container_helper(outputStream* st, jlong j, const char* metrics) {
void OSContainer::print_container_helper(outputStream* st, MetricResult& res, const char* metrics) {
st->print("%s: ", metrics);
if (j >= 0) {
if (j >= 1024) {
st->print_cr(UINT64_FORMAT " k", uint64_t(j) / K);
if (res.success()) {
if (res.value() != value_unlimited) {
if (res.value() >= 1024) {
st->print_cr(PHYS_MEM_TYPE_FORMAT " k", (physical_memory_size_type)(res.value() / K));
} else {
st->print_cr(PHYS_MEM_TYPE_FORMAT, res.value());
}
} else {
st->print_cr(UINT64_FORMAT, uint64_t(j));
st->print_cr("%s", "unlimited");
}
} else {
st->print_cr("%s", j == OSCONTAINER_ERROR ? "not supported" : "unlimited");
// Not supported
st->print_cr("%s", "unavailable");
}
}

View File

@ -30,11 +30,30 @@
#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
#define OSCONTAINER_ERROR (-2)
// Some cgroup interface files define the value 'max' for unlimited.
// Define this constant value to indicate this value.
const uint64_t value_unlimited = std::numeric_limits<uint64_t>::max();
// 20ms timeout between re-reads of memory limit and _active_processor_count.
#define OSCONTAINER_CACHE_TIMEOUT (NANOSECS_PER_SEC/50)
// Carrier object for print_container_helper()
class MetricResult: public StackObj {
private:
static const uint64_t value_unused = 0;
bool _success = false;
physical_memory_size_type _value = value_unused;
public:
void set_value(physical_memory_size_type val) {
// having a value means success
_success = true;
_value = val;
}
bool success() { return _success; }
physical_memory_size_type value() { return _value; }
};
class OSContainer: AllStatic {
private:
@ -45,36 +64,38 @@ class OSContainer: AllStatic {
public:
static void init();
static void print_version_specific_info(outputStream* st);
static void print_container_helper(outputStream* st, jlong j, const char* metrics);
static void print_container_helper(outputStream* st, MetricResult& res, const char* metrics);
static inline bool is_containerized();
static const char * container_type();
static bool available_memory_in_container(julong& value);
static jlong memory_limit_in_bytes();
static jlong memory_and_swap_limit_in_bytes();
static jlong memory_and_swap_usage_in_bytes();
static jlong memory_soft_limit_in_bytes();
static jlong memory_throttle_limit_in_bytes();
static jlong memory_usage_in_bytes();
static jlong memory_max_usage_in_bytes();
static jlong rss_usage_in_bytes();
static jlong cache_usage_in_bytes();
static bool available_memory_in_bytes(physical_memory_size_type& value);
static bool available_swap_in_bytes(physical_memory_size_type host_free_swap,
physical_memory_size_type& value);
static bool memory_limit_in_bytes(physical_memory_size_type& value);
static bool memory_and_swap_limit_in_bytes(physical_memory_size_type& value);
static bool memory_and_swap_usage_in_bytes(physical_memory_size_type& value);
static bool memory_soft_limit_in_bytes(physical_memory_size_type& value);
static bool memory_throttle_limit_in_bytes(physical_memory_size_type& value);
static bool memory_usage_in_bytes(physical_memory_size_type& value);
static bool memory_max_usage_in_bytes(physical_memory_size_type& value);
static bool rss_usage_in_bytes(physical_memory_size_type& value);
static bool cache_usage_in_bytes(physical_memory_size_type& value);
static int active_processor_count();
static bool active_processor_count(int& value);
static char * cpu_cpuset_cpus();
static char * cpu_cpuset_memory_nodes();
static int cpu_quota();
static int cpu_period();
static bool cpu_quota(int& value);
static bool cpu_period(int& value);
static int cpu_shares();
static bool cpu_shares(int& value);
static jlong cpu_usage_in_micros();
static bool cpu_usage_in_micros(uint64_t& value);
static jlong pids_max();
static jlong pids_current();
static bool pids_max(uint64_t& value);
static bool pids_current(uint64_t& value);
};
inline bool OSContainer::is_containerized() {

View File

@ -214,10 +214,8 @@ static bool suppress_primordial_thread_resolution = false;
// utility functions
bool os::available_memory(physical_memory_size_type& value) {
julong avail_mem = 0;
if (OSContainer::is_containerized() && OSContainer::available_memory_in_container(avail_mem)) {
log_trace(os)("available container memory: " JULONG_FORMAT, avail_mem);
value = static_cast<physical_memory_size_type>(avail_mem);
if (OSContainer::is_containerized() && OSContainer::available_memory_in_bytes(value)) {
log_trace(os)("available container memory: " PHYS_MEM_TYPE_FORMAT, value);
return true;
}
@ -225,36 +223,38 @@ bool os::available_memory(physical_memory_size_type& value) {
}
bool os::Linux::available_memory(physical_memory_size_type& value) {
julong avail_mem = static_cast<julong>(-1L);
physical_memory_size_type avail_mem = 0;
bool found_available_mem = false;
FILE *fp = os::fopen("/proc/meminfo", "r");
if (fp != nullptr) {
char buf[80];
do {
if (fscanf(fp, "MemAvailable: " JULONG_FORMAT " kB", &avail_mem) == 1) {
if (fscanf(fp, "MemAvailable: " PHYS_MEM_TYPE_FORMAT " kB", &avail_mem) == 1) {
avail_mem *= K;
found_available_mem = true;
break;
}
} while (fgets(buf, sizeof(buf), fp) != nullptr);
fclose(fp);
}
if (avail_mem == static_cast<julong>(-1L)) {
// Only enter the free memory block if we
// haven't found the available memory
if (!found_available_mem) {
physical_memory_size_type free_mem = 0;
if (!free_memory(free_mem)) {
return false;
}
avail_mem = static_cast<julong>(free_mem);
avail_mem = free_mem;
}
log_trace(os)("available memory: " JULONG_FORMAT, avail_mem);
value = static_cast<physical_memory_size_type>(avail_mem);
log_trace(os)("available memory: " PHYS_MEM_TYPE_FORMAT, avail_mem);
value = avail_mem;
return true;
}
bool os::free_memory(physical_memory_size_type& value) {
julong free_mem = 0;
if (OSContainer::is_containerized() && OSContainer::available_memory_in_container(free_mem)) {
log_trace(os)("free container memory: " JULONG_FORMAT, free_mem);
value = static_cast<physical_memory_size_type>(free_mem);
if (OSContainer::is_containerized() && OSContainer::available_memory_in_bytes(value)) {
log_trace(os)("free container memory: " PHYS_MEM_TYPE_FORMAT, value);
return true;
}
@ -269,29 +269,26 @@ bool os::Linux::free_memory(physical_memory_size_type& value) {
if (ret != 0) {
return false;
}
julong free_mem = (julong)si.freeram * si.mem_unit;
log_trace(os)("free memory: " JULONG_FORMAT, free_mem);
value = static_cast<physical_memory_size_type>(free_mem);
physical_memory_size_type free_mem = (physical_memory_size_type)si.freeram * si.mem_unit;
log_trace(os)("free memory: " PHYS_MEM_TYPE_FORMAT, free_mem);
value = free_mem;
return true;
}
bool os::total_swap_space(physical_memory_size_type& value) {
if (OSContainer::is_containerized()) {
jlong memory_and_swap_limit_in_bytes = OSContainer::memory_and_swap_limit_in_bytes();
jlong memory_limit_in_bytes = OSContainer::memory_limit_in_bytes();
if (memory_limit_in_bytes > 0 && memory_and_swap_limit_in_bytes > 0) {
value = static_cast<physical_memory_size_type>(memory_and_swap_limit_in_bytes - memory_limit_in_bytes);
return true;
physical_memory_size_type mem_swap_limit = value_unlimited;
physical_memory_size_type memory_limit = value_unlimited;
if (OSContainer::memory_and_swap_limit_in_bytes(mem_swap_limit) &&
OSContainer::memory_limit_in_bytes(memory_limit)) {
if (memory_limit != value_unlimited && mem_swap_limit != value_unlimited &&
mem_swap_limit >= memory_limit /* ensure swap is >= 0 */) {
value = mem_swap_limit - memory_limit;
return true;
}
}
} // fallback to the host swap space if the container did return the unbound value of -1
struct sysinfo si;
int ret = sysinfo(&si);
if (ret != 0) {
assert(false, "sysinfo failed in total_swap_space(): %s", os::strerror(errno));
return false;
}
value = static_cast<physical_memory_size_type>(si.totalswap) * si.mem_unit;
return true;
} // fallback to the host swap space if the container returned unlimited
return Linux::host_swap(value);
}
static bool host_free_swap_f(physical_memory_size_type& value) {
@ -315,29 +312,12 @@ bool os::free_swap_space(physical_memory_size_type& value) {
}
physical_memory_size_type host_free_swap_val = MIN2(total_swap_space, host_free_swap);
if (OSContainer::is_containerized()) {
jlong mem_swap_limit = OSContainer::memory_and_swap_limit_in_bytes();
jlong mem_limit = OSContainer::memory_limit_in_bytes();
if (mem_swap_limit >= 0 && mem_limit >= 0) {
jlong delta_limit = mem_swap_limit - mem_limit;
if (delta_limit <= 0) {
value = 0;
return true;
}
jlong mem_swap_usage = OSContainer::memory_and_swap_usage_in_bytes();
jlong mem_usage = OSContainer::memory_usage_in_bytes();
if (mem_swap_usage > 0 && mem_usage > 0) {
jlong delta_usage = mem_swap_usage - mem_usage;
if (delta_usage >= 0) {
jlong free_swap = delta_limit - delta_usage;
value = free_swap >= 0 ? static_cast<physical_memory_size_type>(free_swap) : 0;
return true;
}
}
if (OSContainer::available_swap_in_bytes(host_free_swap_val, value)) {
return true;
}
// unlimited or not supported. Fall through to return host value
log_trace(os,container)("os::free_swap_space: container_swap_limit=" JLONG_FORMAT
" container_mem_limit=" JLONG_FORMAT " returning host value: " PHYS_MEM_TYPE_FORMAT,
mem_swap_limit, mem_limit, host_free_swap_val);
// Fall through to use host value
log_trace(os,container)("os::free_swap_space: containerized value unavailable"
" returning host value: " PHYS_MEM_TYPE_FORMAT, host_free_swap_val);
}
value = host_free_swap_val;
return true;
@ -345,10 +325,10 @@ bool os::free_swap_space(physical_memory_size_type& value) {
physical_memory_size_type os::physical_memory() {
if (OSContainer::is_containerized()) {
jlong mem_limit;
if ((mem_limit = OSContainer::memory_limit_in_bytes()) > 0) {
log_trace(os)("total container memory: " JLONG_FORMAT, mem_limit);
return static_cast<physical_memory_size_type>(mem_limit);
physical_memory_size_type mem_limit = value_unlimited;
if (OSContainer::memory_limit_in_bytes(mem_limit) && mem_limit != value_unlimited) {
log_trace(os)("total container memory: " PHYS_MEM_TYPE_FORMAT, mem_limit);
return mem_limit;
}
}
@ -508,10 +488,15 @@ pid_t os::Linux::gettid() {
// Returns the amount of swap currently configured, in bytes.
// This can change at any time.
julong os::Linux::host_swap() {
bool os::Linux::host_swap(physical_memory_size_type& value) {
struct sysinfo si;
sysinfo(&si);
return (julong)(si.totalswap * si.mem_unit);
int ret = sysinfo(&si);
if (ret != 0) {
assert(false, "sysinfo failed in host_swap(): %s", os::strerror(errno));
return false;
}
value = static_cast<physical_memory_size_type>(si.totalswap) * si.mem_unit;
return true;
}
// Most versions of linux have a bug where the number of processors are
@ -2469,9 +2454,11 @@ bool os::Linux::print_container_info(outputStream* st) {
st->print_cr("cpu_memory_nodes: %s", p != nullptr ? p : "not supported");
free(p);
int i = OSContainer::active_processor_count();
int i = -1;
bool supported = OSContainer::active_processor_count(i);
st->print("active_processor_count: ");
if (i > 0) {
if (supported) {
assert(i > 0, "must be");
if (ActiveProcessorCount > 0) {
st->print_cr("%d, but overridden by -XX:ActiveProcessorCount %d", i, ActiveProcessorCount);
} else {
@ -2481,65 +2468,105 @@ bool os::Linux::print_container_info(outputStream* st) {
st->print_cr("not supported");
}
i = OSContainer::cpu_quota();
supported = OSContainer::cpu_quota(i);
st->print("cpu_quota: ");
if (i > 0) {
if (supported && i > 0) {
st->print_cr("%d", i);
} else {
st->print_cr("%s", i == OSCONTAINER_ERROR ? "not supported" : "no quota");
st->print_cr("%s", !supported ? "not supported" : "no quota");
}
i = OSContainer::cpu_period();
supported = OSContainer::cpu_period(i);
st->print("cpu_period: ");
if (i > 0) {
if (supported && i > 0) {
st->print_cr("%d", i);
} else {
st->print_cr("%s", i == OSCONTAINER_ERROR ? "not supported" : "no period");
st->print_cr("%s", !supported ? "not supported" : "no period");
}
i = OSContainer::cpu_shares();
supported = OSContainer::cpu_shares(i);
st->print("cpu_shares: ");
if (i > 0) {
if (supported && i > 0) {
st->print_cr("%d", i);
} else {
st->print_cr("%s", i == OSCONTAINER_ERROR ? "not supported" : "no shares");
st->print_cr("%s", !supported ? "not supported" : "no shares");
}
jlong j = OSContainer::cpu_usage_in_micros();
uint64_t j = 0;
supported = OSContainer::cpu_usage_in_micros(j);
st->print("cpu_usage_in_micros: ");
if (j >= 0) {
st->print_cr(JLONG_FORMAT, j);
if (supported && j > 0) {
st->print_cr(UINT64_FORMAT, j);
} else {
st->print_cr("%s", j == OSCONTAINER_ERROR ? "not supported" : "no usage");
st->print_cr("%s", !supported ? "not supported" : "no usage");
}
OSContainer::print_container_helper(st, OSContainer::memory_limit_in_bytes(), "memory_limit_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_and_swap_limit_in_bytes(), "memory_and_swap_limit_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_soft_limit_in_bytes(), "memory_soft_limit_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_throttle_limit_in_bytes(), "memory_throttle_limit_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_usage_in_bytes(), "memory_usage_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_max_usage_in_bytes(), "memory_max_usage_in_bytes");
OSContainer::print_container_helper(st, OSContainer::rss_usage_in_bytes(), "rss_usage_in_bytes");
OSContainer::print_container_helper(st, OSContainer::cache_usage_in_bytes(), "cache_usage_in_bytes");
MetricResult memory_limit;
physical_memory_size_type val = value_unlimited;
if (OSContainer::memory_limit_in_bytes(val)) {
memory_limit.set_value(val);
}
MetricResult mem_swap_limit;
val = value_unlimited;
if (OSContainer::memory_and_swap_limit_in_bytes(val)) {
mem_swap_limit.set_value(val);
}
MetricResult mem_soft_limit;
val = value_unlimited;
if (OSContainer::memory_soft_limit_in_bytes(val)) {
mem_soft_limit.set_value(val);
}
MetricResult mem_throttle_limit;
val = value_unlimited;
if (OSContainer::memory_throttle_limit_in_bytes(val)) {
mem_throttle_limit.set_value(val);
}
MetricResult mem_usage;
val = 0;
if (OSContainer::memory_usage_in_bytes(val)) {
mem_usage.set_value(val);
}
MetricResult mem_max_usage;
val = 0;
if (OSContainer::memory_max_usage_in_bytes(val)) {
mem_max_usage.set_value(val);
}
MetricResult rss_usage;
val = 0;
if (OSContainer::rss_usage_in_bytes(val)) {
rss_usage.set_value(val);
}
MetricResult cache_usage;
val = 0;
if (OSContainer::cache_usage_in_bytes(val)) {
cache_usage.set_value(val);
}
OSContainer::print_container_helper(st, memory_limit, "memory_limit_in_bytes");
OSContainer::print_container_helper(st, mem_swap_limit, "memory_and_swap_limit_in_bytes");
OSContainer::print_container_helper(st, mem_soft_limit, "memory_soft_limit_in_bytes");
OSContainer::print_container_helper(st, mem_throttle_limit, "memory_throttle_limit_in_bytes");
OSContainer::print_container_helper(st, mem_usage, "memory_usage_in_bytes");
OSContainer::print_container_helper(st, mem_max_usage, "memory_max_usage_in_bytes");
OSContainer::print_container_helper(st, rss_usage, "rss_usage_in_bytes");
OSContainer::print_container_helper(st, cache_usage, "cache_usage_in_bytes");
OSContainer::print_version_specific_info(st);
j = OSContainer::pids_max();
supported = OSContainer::pids_max(j);
st->print("maximum number of tasks: ");
if (j > 0) {
st->print_cr(JLONG_FORMAT, j);
if (supported && j != value_unlimited) {
st->print_cr(UINT64_FORMAT, j);
} else {
st->print_cr("%s", j == OSCONTAINER_ERROR ? "not supported" : "unlimited");
st->print_cr("%s", !supported ? "not supported" : "unlimited");
}
j = OSContainer::pids_current();
supported = OSContainer::pids_current(j);
st->print("current number of tasks: ");
if (j > 0) {
st->print_cr(JLONG_FORMAT, j);
if (supported && j > 0) {
st->print_cr(UINT64_FORMAT, j);
} else {
if (j == OSCONTAINER_ERROR) {
st->print_cr("not supported");
}
st->print_cr("%s", !supported ? "not supported" : "no current tasks");
}
return true;
@ -4643,7 +4670,7 @@ int os::Linux::active_processor_count() {
//
// 1. User option -XX:ActiveProcessorCount
// 2. kernel os calls (sched_getaffinity or sysconf(_SC_NPROCESSORS_ONLN)
// 3. extracted from cgroup cpu subsystem (shares and quotas)
// 3. extracted from cgroup cpu subsystem (quotas)
//
// Option 1, if specified, will always override.
// If the cgroup subsystem is active and configured, we
@ -4660,9 +4687,8 @@ int os::active_processor_count() {
return ActiveProcessorCount;
}
int active_cpus;
if (OSContainer::is_containerized()) {
active_cpus = OSContainer::active_processor_count();
int active_cpus = -1;
if (OSContainer::is_containerized() && OSContainer::active_processor_count(active_cpus)) {
log_trace(os)("active_processor_count: determined by OSContainer: %d",
active_cpus);
} else {

View File

@ -45,8 +45,6 @@ class os::Linux {
static GrowableArray<int>* _cpu_to_node;
static GrowableArray<int>* _nindex_to_node;
static julong available_memory_in_container();
protected:
static physical_memory_size_type _physical_memory;
@ -117,7 +115,7 @@ class os::Linux {
static uintptr_t initial_thread_stack_size(void) { return _initial_thread_stack_size; }
static physical_memory_size_type physical_memory() { return _physical_memory; }
static julong host_swap();
static bool host_swap(physical_memory_size_type& value);
static intptr_t* ucontext_get_sp(const ucontext_t* uc);
static intptr_t* ucontext_get_fp(const ucontext_t* uc);

View File

@ -421,7 +421,9 @@ JVM_END
JVM_ENTRY_NO_ENV(jlong, jfr_host_total_swap_memory(JNIEnv* env, jclass jvm))
#ifdef LINUX
// We want the host swap memory, not the container value.
return os::Linux::host_swap();
physical_memory_size_type host_swap = 0;
(void)os::Linux::host_swap(host_swap); // Discard return value and treat as no swap
return static_cast<jlong>(host_swap);
#else
physical_memory_size_type total_swap_space = 0;
// Return value ignored - defaulting to 0 on failure.

View File

@ -2590,7 +2590,13 @@ WB_END
// Physical swap of the host machine (including containers), Linux only.
WB_ENTRY(jlong, WB_HostPhysicalSwap(JNIEnv* env, jobject o))
LINUX_ONLY(return (jlong)os::Linux::host_swap();)
#ifdef LINUX
physical_memory_size_type swap_val = 0;
if (!os::Linux::host_swap(swap_val)) {
return -1; // treat as unlimited
}
return static_cast<jlong>(swap_val);
#endif
return -1; // Not used/implemented on other platforms
WB_END

View File

@ -2207,13 +2207,7 @@ static void assert_nonempty_range(const char* addr, size_t bytes) {
bool os::used_memory(physical_memory_size_type& value) {
#ifdef LINUX
if (OSContainer::is_containerized()) {
jlong mem_usage = OSContainer::memory_usage_in_bytes();
if (mem_usage > 0) {
value = static_cast<physical_memory_size_type>(mem_usage);
return true;
} else {
return false;
}
return OSContainer::memory_usage_in_bytes(value);
}
#endif
physical_memory_size_type avail_mem = 0;

View File

@ -104,35 +104,35 @@ TEST(cgroupTest, read_numerical_key_value_failure_cases) {
const char* base_with_slash = path.as_string(true);
TestController* controller = new TestController((char*)os::get_temp_directory());
constexpr julong bad = 0xBAD;
julong x = bad;
constexpr uint64_t bad = 0xBAD;
uint64_t x = bad;
fill_file(test_file, "foo ");
bool is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x);
bool is_ok = controller->read_numerical_key_value(base_with_slash, "foo", x);
EXPECT_FALSE(is_ok) << "Value is missing in key/value case, expecting false";
EXPECT_EQ(bad, x) << "x must be unchanged";
x = bad;
fill_file(test_file, "faulty_start foo 101");
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x);
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", x);
EXPECT_FALSE(is_ok) << "key must be at the start";
EXPECT_EQ(bad, x) << "x must be unchanged";
x = bad;
fill_file(test_file, nullptr);
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x);
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", x);
EXPECT_FALSE(is_ok) << "key not in empty file";
EXPECT_EQ(bad, x) << "x must be unchanged";
x = bad;
fill_file(test_file, "foo\n");
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x);
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", x);
EXPECT_FALSE(is_ok) << "key must have a value";
EXPECT_EQ(bad, x) << "x must be unchanged";
x = bad;
fill_file(test_file, "foof 1002");
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x);
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", x);
EXPECT_FALSE(is_ok) << "key must be exact match";
EXPECT_EQ(bad, x) << "x must be unchanged";
@ -150,43 +150,43 @@ TEST(cgroupTest, read_numerical_key_value_success_cases) {
const char* base_with_slash = path.as_string(true);
TestController* controller = new TestController((char*)os::get_temp_directory());
constexpr julong bad = 0xBAD;
julong x = bad;
constexpr uint64_t bad = 0xBAD;
uint64_t x = bad;
fill_file(test_file, "foo 100");
bool is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x);
bool is_ok = controller->read_numerical_key_value(base_with_slash, "foo", x);
EXPECT_TRUE(is_ok);
EXPECT_EQ((julong)100, x);
EXPECT_EQ((uint64_t)100, x);
x = bad;
fill_file(test_file, "foo\t111");
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x);
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", x);
EXPECT_TRUE(is_ok);
EXPECT_EQ((julong)111, x);
EXPECT_EQ((uint64_t)111, x);
x = bad;
fill_file(test_file, "foo\nbar 333\nfoo\t111");
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x);
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", x);
EXPECT_TRUE(is_ok);
EXPECT_EQ((julong)111, x);
EXPECT_EQ((uint64_t)111, x);
x = bad;
fill_file(test_file, "foof 100\nfoo 133");
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x);
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", x);
EXPECT_TRUE(is_ok);
EXPECT_EQ((julong)133, x);
EXPECT_EQ((uint64_t)133, x);
x = bad;
fill_file(test_file, "foo\t333\nfoot 999");
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x);
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", x);
EXPECT_TRUE(is_ok);
EXPECT_EQ((julong)333, x);
EXPECT_EQ((uint64_t)333, x);
x = bad;
fill_file(test_file, "foo 1\nfoo car");
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x);
is_ok = controller->read_numerical_key_value(base_with_slash, "foo", x);
EXPECT_TRUE(is_ok);
EXPECT_EQ((julong)1, x);
EXPECT_EQ((uint64_t)1, x);
// Cleanup
delete_file(test_file);
@ -195,10 +195,10 @@ TEST(cgroupTest, read_numerical_key_value_success_cases) {
TEST(cgroupTest, read_number_null) {
TestController* null_path_controller = new TestController((char*)nullptr);
const char* test_file_path = "/not-used";
constexpr julong bad = 0xBAD;
julong a = bad;
constexpr uint64_t bad = 0xBAD;
uint64_t a = bad;
// null subsystem_path() case
bool is_ok = null_path_controller->read_number(test_file_path, &a);
bool is_ok = null_path_controller->read_number(test_file_path, a);
EXPECT_FALSE(is_ok) << "Null subsystem path should be an error";
EXPECT_EQ(bad, a) << "Expected untouched scan value";
}
@ -221,9 +221,9 @@ TEST(cgroupTest, read_string_beyond_max_path) {
TEST(cgroupTest, read_number_file_not_exist) {
TestController* unknown_path_ctrl = new TestController((char*)"/do/not/exist");
const char* test_file_path = "/file-not-found";
constexpr julong bad = 0xBAD;
julong result = bad;
bool is_ok = unknown_path_ctrl->read_number(test_file_path, &result);
constexpr uint64_t bad = 0xBAD;
uint64_t result = bad;
bool is_ok = unknown_path_ctrl->read_number(test_file_path, result);
EXPECT_FALSE(is_ok) << "File not found should be an error";
EXPECT_EQ(bad, result) << "Expected untouched scan value";
}
@ -232,10 +232,10 @@ TEST(cgroupTest, read_numerical_key_value_null) {
TestController* null_path_controller = new TestController((char*)nullptr);
const char* test_file_path = "/not-used";
const char* key = "something";
constexpr julong bad = 0xBAD;
julong a = bad;
constexpr uint64_t bad = 0xBAD;
uint64_t a = bad;
// null subsystem_path() case
bool is_ok = null_path_controller->read_numerical_key_value(test_file_path, key, &a);
bool is_ok = null_path_controller->read_numerical_key_value(test_file_path, key, a);
EXPECT_FALSE(is_ok) << "Null subsystem path should be an error";
EXPECT_EQ(bad, a) << "Expected untouched scan value";
}
@ -243,7 +243,7 @@ TEST(cgroupTest, read_numerical_key_value_null) {
TEST(cgroupTest, read_number_tests) {
char* test_file = temp_file("cgroups");
const char* b = basename(test_file);
constexpr julong bad = 0xBAD;
constexpr uint64_t bad = 0xBAD;
EXPECT_TRUE(b != nullptr) << "basename was null";
stringStream path;
path.print_raw(os::file_separator());
@ -252,44 +252,49 @@ TEST(cgroupTest, read_number_tests) {
fill_file(test_file, "8888");
TestController* controller = new TestController((char*)os::get_temp_directory());
julong foo = bad;
bool ok = controller->read_number(base_with_slash, &foo);
uint64_t foo = bad;
bool ok = controller->read_number(base_with_slash, foo);
EXPECT_TRUE(ok) << "Number parsing should have been successful";
EXPECT_EQ((julong)8888, foo) << "Wrong value for 'foo' (NOTE: 0xBAD == " << 0xBAD << ")";
EXPECT_EQ((uint64_t)8888, foo) << "Wrong value for 'foo' (NOTE: 0xBAD == " << 0xBAD << ")";
// Some interface files might have negative values, ensure we can read
// them and manually cast them as needed.
// them and manually cast them as needed. For example, on cgv1, the cpu.cfs_quota_us
// file might be set to -1 to indicate no cpu quota setup.
fill_file(test_file, "-1");
foo = bad;
ok = controller->read_number(base_with_slash, &foo);
ok = controller->read_number(base_with_slash, foo);
EXPECT_TRUE(ok) << "Number parsing should have been successful";
EXPECT_EQ((jlong)-1, (jlong)foo) << "Wrong value for 'foo' (NOTE: 0xBAD == " << 0xBAD << ")";
EXPECT_EQ((int)-1, (int)foo) << "Wrong value for 'foo' (NOTE: 0xBAD == " << 0xBAD << ")";
foo = bad;
fill_file(test_file, nullptr);
ok = controller->read_number(base_with_slash, &foo);
ok = controller->read_number(base_with_slash, foo);
EXPECT_FALSE(ok) << "Empty file should have failed";
EXPECT_EQ(bad, foo) << "foo was altered";
// Some interface files have numbers as well as the string
// 'max', which means unlimited.
jlong result = -10;
uint64_t result = 0;
uint64_t unlimited = std::numeric_limits<uint64_t>::max();
fill_file(test_file, "max\n");
ok = controller->read_number_handle_max(base_with_slash, &result);
ok = controller->read_number_handle_max(base_with_slash, result);
EXPECT_TRUE(ok) << "Number parsing for 'max' string should have been successful";
EXPECT_EQ((jlong)-1, result) << "'max' means unlimited (-1)";
EXPECT_EQ(unlimited, result) << "'max' means unlimited (-1)";
result = -10;
result = 0;
fill_file(test_file, "11114\n");
ok = controller->read_number_handle_max(base_with_slash, &result);
ok = controller->read_number_handle_max(base_with_slash, result);
EXPECT_TRUE(ok) << "Number parsing for should have been successful";
EXPECT_EQ((jlong)11114, result) << "Incorrect result";
EXPECT_EQ((uint64_t)11114, result) << "Incorrect result";
result = -10;
result = 0;
// This is a contrived test case not matching cgroup interface files
// in the wild where numbers are positive. The value is deliberately
// negative. Yet it should work
fill_file(test_file, "-51114\n");
ok = controller->read_number_handle_max(base_with_slash, &result);
ok = controller->read_number_handle_max(base_with_slash, result);
EXPECT_TRUE(ok) << "Number parsing for should have been successful";
EXPECT_EQ((jlong)-51114, result) << "Incorrect result";
EXPECT_EQ((int)-51114, (int)result) << "Incorrect result";
delete_file(test_file);
}
@ -372,28 +377,28 @@ TEST(cgroupTest, read_number_tuple_test) {
fill_file(test_file, "max 10000");
TestController* controller = new TestController((char*)os::get_temp_directory());
jlong result = -10;
bool ok = controller->read_numerical_tuple_value(base_with_slash, true /* use_first */, &result);
uint64_t result = 0;
bool ok = controller->read_numerical_tuple_value(base_with_slash, true /* use_first */, result);
EXPECT_TRUE(ok) << "Should be OK to read value";
EXPECT_EQ((jlong)-1, result) << "max should be unlimited (-1)";
EXPECT_EQ(value_unlimited, result) << "max should be unlimited (-1)";
result = -10;
ok = controller->read_numerical_tuple_value(base_with_slash, false /* use_first */, &result);
result = 0;
ok = controller->read_numerical_tuple_value(base_with_slash, false /* use_first */, result);
EXPECT_TRUE(ok) << "Should be OK to read the value";
EXPECT_EQ((jlong)10000, result);
EXPECT_EQ((uint64_t)10000, result);
// non-max strings
fill_file(test_file, "abc 10000");
result = -10;
ok = controller->read_numerical_tuple_value(base_with_slash, true /* use_first */, &result);
result = 0;
ok = controller->read_numerical_tuple_value(base_with_slash, true /* use_first */, result);
EXPECT_FALSE(ok) << "abc should not be parsable";
EXPECT_EQ((jlong)-10, result) << "result value should be unchanged";
EXPECT_EQ((uint64_t)0, result) << "result value should be unchanged";
fill_file(test_file, nullptr);
result = -10;
ok = controller->read_numerical_tuple_value(base_with_slash, true /* use_first */, &result);
result = 0;
ok = controller->read_numerical_tuple_value(base_with_slash, true /* use_first */, result);
EXPECT_FALSE(ok) << "Empty file should be an error";
EXPECT_EQ((jlong)-10, result) << "result value should be unchanged";
EXPECT_EQ((uint64_t)0, result) << "result value should be unchanged";
delete_file(test_file);
}
@ -407,20 +412,20 @@ TEST(cgroupTest, read_numerical_key_beyond_max_path) {
TestController* too_large_path_controller = new TestController(larger_than_max);
const char* test_file_path = "/file-not-found";
const char* key = "something";
julong a = 0xBAD;
bool is_ok = too_large_path_controller->read_numerical_key_value(test_file_path, key, &a);
uint64_t a = 0xBAD;
bool is_ok = too_large_path_controller->read_numerical_key_value(test_file_path, key, a);
EXPECT_FALSE(is_ok) << "Too long path should be an error";
EXPECT_EQ((julong)0xBAD, a) << "Expected untouched scan value";
EXPECT_EQ((uint64_t)0xBAD, a) << "Expected untouched scan value";
}
TEST(cgroupTest, read_numerical_key_file_not_exist) {
TestController* unknown_path_ctrl = new TestController((char*)"/do/not/exist");
const char* test_file_path = "/file-not-found";
const char* key = "something";
julong a = 0xBAD;
bool is_ok = unknown_path_ctrl->read_numerical_key_value(test_file_path, key, &a);
uint64_t a = 0xBAD;
bool is_ok = unknown_path_ctrl->read_numerical_key_value(test_file_path, key, a);
EXPECT_FALSE(is_ok) << "File not found should be an error";
EXPECT_EQ((julong)0xBAD, a) << "Expected untouched scan value";
EXPECT_EQ((uint64_t)0xBAD, a) << "Expected untouched scan value";
}
TEST(cgroupTest, set_cgroupv1_subsystem_path) {