diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index f5c4abeb4ca..f9c6f794ebd 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -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* 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* 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); } diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index bf0ad03fc56..522b64f8816 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -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 { @@ -124,21 +131,22 @@ class CgroupController: public CHeapObj { 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 { * 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{ 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 { // Pure virtual class representing version agnostic CPU controllers class CgroupCpuController: public CHeapObj { 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 { // Pure virtual class representing version agnostic CPU accounting controllers class CgroupCpuacctController: public CHeapObj { 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 { // Pure virtual class representing version agnostic memory controllers class CgroupMemoryController: public CHeapObj { 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 { class CgroupSubsystem: public CHeapObj { 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 { virtual CachingCgroupController* 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, diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp index de027db812a..7aa07d53148 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.cpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp @@ -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(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, " diff --git a/src/hotspot/os/linux/cgroupUtil_linux.hpp b/src/hotspot/os/linux/cgroupUtil_linux.hpp index aa63a7457cc..d72bbd1cf1e 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.hpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.hpp @@ -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 diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 87870e647eb..ddcb0db2161 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -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(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(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(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(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; } diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index ce3184992e8..8aeb64ef18c 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -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() { diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 41fc8db7e81..f435e53c02c 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -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(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(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(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); } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 07db126ce90..39a4fabe9f6 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -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; diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp index 561f2d4926c..d86bbf7428a 100644 --- a/src/hotspot/os/linux/osContainer_linux.cpp +++ b/src/hotspot/os/linux/osContainer_linux.cpp @@ -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(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(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(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(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(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(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"); } } diff --git a/src/hotspot/os/linux/osContainer_linux.hpp b/src/hotspot/os/linux/osContainer_linux.hpp index 6258714c48b..895c99ba167 100644 --- a/src/hotspot/os/linux/osContainer_linux.hpp +++ b/src/hotspot/os/linux/osContainer_linux.hpp @@ -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::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() { diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 69ef8ce7c33..a345663dd5b 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -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(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(-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(-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(free_mem); + avail_mem = free_mem; } - log_trace(os)("available memory: " JULONG_FORMAT, avail_mem); - value = static_cast(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(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(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(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(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(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(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(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 { diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp index b77cd9f3c81..df96a17d8e9 100644 --- a/src/hotspot/os/linux/os_linux.hpp +++ b/src/hotspot/os/linux/os_linux.hpp @@ -45,8 +45,6 @@ class os::Linux { static GrowableArray* _cpu_to_node; static GrowableArray* _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); diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp index cc5bbe1fc60..6a1146587bc 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp @@ -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(host_swap); #else physical_memory_size_type total_swap_space = 0; // Return value ignored - defaulting to 0 on failure. diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 8f0a2320288..5514f7d3260 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -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(swap_val); +#endif return -1; // Not used/implemented on other platforms WB_END diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 674b0a55841..ceff9b54c33 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -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(mem_usage); - return true; - } else { - return false; - } + return OSContainer::memory_usage_in_bytes(value); } #endif physical_memory_size_type avail_mem = 0; diff --git a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp index b33d6454477..7a4f7bcb99e 100644 --- a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp +++ b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp @@ -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::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) {