mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8365606: Container code should not be using jlong/julong
Reviewed-by: stuefe, cnorrbin, fitzsim
This commit is contained in:
parent
50a3049737
commit
72ebca8a0b
@ -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);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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, "
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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", "a);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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 */, "a_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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user