diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 0c0c2808fa10f..3b9cfb7e4d392 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -258,10 +258,18 @@ bool os::free_memory(physical_memory_size_type& value) { return Aix::available_memory(value); } +bool os::machine_free_memory(physical_memory_size_type& value) { + return Aix::available_memory(value); +} + bool os::available_memory(physical_memory_size_type& value) { return Aix::available_memory(value); } +bool os::machine_available_memory(physical_memory_size_type& value) { + return Aix::available_memory(value); +} + bool os::Aix::available_memory(physical_memory_size_type& value) { os::Aix::meminfo_t mi; if (os::Aix::get_meminfo(&mi)) { @@ -273,6 +281,10 @@ bool os::Aix::available_memory(physical_memory_size_type& value) { } bool os::total_swap_space(physical_memory_size_type& value) { + return machine_total_swap_space(value); +} + +bool os::machine_total_swap_space(physical_memory_size_type& value) { perfstat_memory_total_t memory_info; if (libperfstat::perfstat_memory_total(nullptr, &memory_info, sizeof(perfstat_memory_total_t), 1) == -1) { return false; @@ -282,6 +294,10 @@ bool os::total_swap_space(physical_memory_size_type& value) { } bool os::free_swap_space(physical_memory_size_type& value) { + return machine_free_swap_space(value); +} + +bool os::machine_free_swap_space(physical_memory_size_type& value) { perfstat_memory_total_t memory_info; if (libperfstat::perfstat_memory_total(nullptr, &memory_info, sizeof(perfstat_memory_total_t), 1) == -1) { return false; @@ -294,6 +310,10 @@ physical_memory_size_type os::physical_memory() { return Aix::physical_memory(); } +physical_memory_size_type os::machine_physical_memory() { + return Aix::physical_memory(); +} + size_t os::rss() { return (size_t)0; } // Cpu architecture string @@ -2259,6 +2279,10 @@ int os::active_processor_count() { return ActiveProcessorCount; } + return machine_active_processor_count(); +} + +int os::machine_active_processor_count() { int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); assert(online_cpus > 0 && online_cpus <= processor_count(), "sanity check"); return online_cpus; diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 8c5bbd58a8437..8096c7e51e422 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -137,10 +137,18 @@ bool os::available_memory(physical_memory_size_type& value) { return Bsd::available_memory(value); } +bool os::machine_available_memory(physical_memory_size_type& value) { + return Bsd::available_memory(value); +} + bool os::free_memory(physical_memory_size_type& value) { return Bsd::available_memory(value); } +bool os::machine_free_memory(physical_memory_size_type& value) { + return Bsd::available_memory(value); +} + // Available here means free. Note that this number is of no much use. As an estimate // for future memory pressure it is far too conservative, since MacOS will use a lot // of unused memory for caches, and return it willingly in case of needs. @@ -181,6 +189,10 @@ void os::Bsd::print_uptime_info(outputStream* st) { } bool os::total_swap_space(physical_memory_size_type& value) { + return machine_total_swap_space(value); +} + +bool os::machine_total_swap_space(physical_memory_size_type& value) { #if defined(__APPLE__) struct xsw_usage vmusage; size_t size = sizeof(vmusage); @@ -195,6 +207,10 @@ bool os::total_swap_space(physical_memory_size_type& value) { } bool os::free_swap_space(physical_memory_size_type& value) { + return machine_free_swap_space(value); +} + +bool os::machine_free_swap_space(physical_memory_size_type& value) { #if defined(__APPLE__) struct xsw_usage vmusage; size_t size = sizeof(vmusage); @@ -212,6 +228,10 @@ physical_memory_size_type os::physical_memory() { return Bsd::physical_memory(); } +physical_memory_size_type os::machine_physical_memory() { + return Bsd::physical_memory(); +} + size_t os::rss() { size_t rss = 0; #ifdef __APPLE__ @@ -2112,6 +2132,10 @@ int os::active_processor_count() { return ActiveProcessorCount; } + return machine_active_processor_count(); +} + +int os::machine_active_processor_count() { return _processor_count; } diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index f5c4abeb4cad0..bbad21f5b087a 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -632,24 +632,20 @@ void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) { * return: * number of CPUs */ -int CgroupSubsystem::active_processor_count() { - int quota_count = 0; - int cpu_count; - int result; - +double CgroupSubsystem::active_processor_count() { // We use a cache with a timeout to avoid performing expensive // computations in the event this function is called frequently. // [See 8227006]. - CachingCgroupController* contrl = cpu_controller(); - CachedMetric* cpu_limit = contrl->metrics_cache(); + 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); + double val = cpu_limit->value(); + log_trace(os, container)("CgroupSubsystem::active_processor_count (cached): %.2f", val); return val; } - cpu_count = os::Linux::active_processor_count(); - result = CgroupUtil::processor_count(contrl->controller(), cpu_count); + int cpu_count = os::Linux::active_processor_count(); + double result = CgroupUtil::processor_count(contrl->controller(), cpu_count); // Update cached metric to avoid re-reading container settings too often cpu_limit->set_value(result, OSCONTAINER_CACHE_TIMEOUT); @@ -666,8 +662,8 @@ int CgroupSubsystem::active_processor_count() { * OSCONTAINER_ERROR for not supported */ jlong CgroupSubsystem::memory_limit_in_bytes(julong upper_bound) { - CachingCgroupController* contrl = memory_controller(); - CachedMetric* memory_limit = contrl->metrics_cache(); + CachingCgroupController* contrl = memory_controller(); + CachedMetric* memory_limit = contrl->metrics_cache(); if (!memory_limit->should_check_metric()) { return memory_limit->value(); } diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 62a614326657f..3429e625a1fb4 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -162,9 +162,10 @@ class CgroupController: public CHeapObj { static jlong limit_from_str(char* limit_str); }; +template class CachedMetric : public CHeapObj{ private: - volatile jlong _metric; + volatile MetricType _metric; volatile jlong _next_check_counter; public: CachedMetric() { @@ -174,8 +175,8 @@ class CachedMetric : public CHeapObj{ bool should_check_metric() { return os::elapsed_counter() > _next_check_counter; } - jlong value() { return _metric; } - void set_value(jlong value, jlong timeout) { + MetricType value() { return _metric; } + void set_value(MetricType value, jlong timeout) { _metric = value; // Metric is unlikely to change, but we want to remain // responsive to configuration changes. A very short grace time @@ -186,19 +187,19 @@ class CachedMetric : public CHeapObj{ } }; -template +template class CachingCgroupController : public CHeapObj { private: T* _controller; - CachedMetric* _metrics_cache; + CachedMetric* _metrics_cache; public: CachingCgroupController(T* cont) { _controller = cont; - _metrics_cache = new CachedMetric(); + _metrics_cache = new CachedMetric(); } - CachedMetric* metrics_cache() { return _metrics_cache; } + CachedMetric* metrics_cache() { return _metrics_cache; } T* controller() { return _controller; } }; @@ -252,7 +253,7 @@ class CgroupMemoryController: public CHeapObj { class CgroupSubsystem: public CHeapObj { public: jlong memory_limit_in_bytes(julong upper_bound); - int active_processor_count(); + double active_processor_count(); virtual jlong pids_max() = 0; virtual jlong pids_current() = 0; @@ -261,8 +262,8 @@ class CgroupSubsystem: public CHeapObj { virtual char * cpu_cpuset_cpus() = 0; virtual char * cpu_cpuset_memory_nodes() = 0; virtual const char * container_type() = 0; - virtual CachingCgroupController* memory_controller() = 0; - virtual CachingCgroupController* cpu_controller() = 0; + virtual CachingCgroupController* memory_controller() = 0; + virtual CachingCgroupController* cpu_controller() = 0; virtual CgroupCpuacctController* cpuacct_controller() = 0; int cpu_quota(); diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp index de027db812a56..32f2a937d262d 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.cpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp @@ -25,26 +25,19 @@ #include "cgroupUtil_linux.hpp" #include "os_linux.hpp" -int CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int host_cpus) { +double 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(); - int quota_count = 0; - int result = 0; + double result = static_cast(host_cpus); - if (quota > -1 && period > 0) { - quota_count = ceilf((float)quota / (float)period); - log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count); + if (quota > 0 && period > 0) { // Use quotas + double cpu_quota = static_cast(quota) / period; + log_trace(os, container)("CPU Quota based on quota/period: %.2f", cpu_quota); + result = MIN2(result, cpu_quota); } - // Use quotas - if (quota_count != 0) { - limit_count = quota_count; - } - - result = MIN2(host_cpus, limit_count); - log_trace(os, container)("OSContainer::active_processor_count: %d", result); + log_trace(os, container)("OSContainer::active_processor_count: %.2f", result); return result; } diff --git a/src/hotspot/os/linux/cgroupUtil_linux.hpp b/src/hotspot/os/linux/cgroupUtil_linux.hpp index aa63a7457ccd0..d6ba96299d14e 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.hpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.hpp @@ -31,7 +31,7 @@ class CgroupUtil: AllStatic { public: - static int processor_count(CgroupCpuController* cpu, int host_cpus); + static double processor_count(CgroupCpuController* cpu, int host_cpus); // 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); diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 90f01565b84c5..429b98cbda7e0 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -266,8 +266,8 @@ CgroupV1Subsystem::CgroupV1Subsystem(CgroupV1Controller* cpuset, _pids(pids) { CgroupUtil::adjust_controller(memory); CgroupUtil::adjust_controller(cpu); - _memory = new CachingCgroupController(memory); - _cpu = new CachingCgroupController(cpu); + _memory = new CachingCgroupController(memory); + _cpu = new CachingCgroupController(cpu); } bool CgroupV1Subsystem::is_containerized() { diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index 02b2c6a9fcebf..2c7c67ae1c41e 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -193,15 +193,15 @@ class CgroupV1Subsystem: public CgroupSubsystem { const char * container_type() { return "cgroupv1"; } - CachingCgroupController* memory_controller() { return _memory; } - CachingCgroupController* cpu_controller() { return _cpu; } + CachingCgroupController* memory_controller() { return _memory; } + CachingCgroupController* cpu_controller() { return _cpu; } CgroupCpuacctController* cpuacct_controller() { return _cpuacct; } private: /* controllers */ - CachingCgroupController* _memory = nullptr; + CachingCgroupController* _memory = nullptr; CgroupV1Controller* _cpuset = nullptr; - CachingCgroupController* _cpu = nullptr; + CachingCgroupController* _cpu = nullptr; CgroupV1CpuacctController* _cpuacct = nullptr; CgroupV1Controller* _pids = nullptr; diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 38258a1f049ab..14b19a5473e26 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -120,8 +120,8 @@ CgroupV2Subsystem::CgroupV2Subsystem(CgroupV2MemoryController * memory, _unified(unified) { CgroupUtil::adjust_controller(memory); CgroupUtil::adjust_controller(cpu); - _memory = new CachingCgroupController(memory); - _cpu = new CachingCgroupController(cpu); + _memory = new CachingCgroupController(memory); + _cpu = new CachingCgroupController(cpu); _cpuacct = cpuacct; } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 07db126ce901f..8e8db24e4af3f 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -145,8 +145,8 @@ class CgroupV2Subsystem: public CgroupSubsystem { /* One unified controller */ CgroupV2Controller _unified; /* Caching wrappers for cpu/memory metrics */ - CachingCgroupController* _memory = nullptr; - CachingCgroupController* _cpu = nullptr; + CachingCgroupController* _memory = nullptr; + CachingCgroupController* _cpu = nullptr; CgroupCpuacctController* _cpuacct = nullptr; @@ -168,8 +168,8 @@ class CgroupV2Subsystem: public CgroupSubsystem { const char * container_type() override { return "cgroupv2"; } - CachingCgroupController* memory_controller() override { return _memory; } - CachingCgroupController* cpu_controller() override { return _cpu; } + CachingCgroupController* memory_controller() override { return _memory; } + CachingCgroupController* cpu_controller() override { return _cpu; } CgroupCpuacctController* cpuacct_controller() override { return _cpuacct; }; }; diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp index 561f2d4926c6a..55dbb1c36c146 100644 --- a/src/hotspot/os/linux/osContainer_linux.cpp +++ b/src/hotspot/os/linux/osContainer_linux.cpp @@ -85,7 +85,7 @@ void OSContainer::init() { // 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(); + os::Linux::active_processor_count() != ceilf(active_processor_count()); if (any_mem_cpu_limit_present) { reason = " because either a cpu or a memory limit is present"; } else { @@ -122,7 +122,7 @@ bool OSContainer::available_memory_in_container(julong& value) { jlong OSContainer::memory_limit_in_bytes() { assert(cgroup_subsystem != nullptr, "cgroup subsystem not available"); - julong phys_mem = static_cast(os::Linux::physical_memory()); + julong phys_mem = static_cast(os::machine_physical_memory()); return cgroup_subsystem->memory_limit_in_bytes(phys_mem); } @@ -187,7 +187,7 @@ char * OSContainer::cpu_cpuset_memory_nodes() { return cgroup_subsystem->cpu_cpuset_memory_nodes(); } -int OSContainer::active_processor_count() { +double OSContainer::active_processor_count() { assert(cgroup_subsystem != nullptr, "cgroup subsystem not available"); return cgroup_subsystem->active_processor_count(); } diff --git a/src/hotspot/os/linux/osContainer_linux.hpp b/src/hotspot/os/linux/osContainer_linux.hpp index 6258714c48b1d..6da68ab2ad8c2 100644 --- a/src/hotspot/os/linux/osContainer_linux.hpp +++ b/src/hotspot/os/linux/osContainer_linux.hpp @@ -61,7 +61,7 @@ class OSContainer: AllStatic { static jlong rss_usage_in_bytes(); static jlong cache_usage_in_bytes(); - static int active_processor_count(); + static double active_processor_count(); static char * cpu_cpuset_cpus(); static char * cpu_cpuset_memory_nodes(); diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 772b170d11ca9..1639ca808b37f 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -213,17 +213,76 @@ static bool suppress_primordial_thread_resolution = false; // utility functions +bool os::is_containerized() { + return OSContainer::is_containerized(); +} + +bool os::container_memory_limit(physical_memory_size_type& value) { + assert(is_containerized(), "must be running containerized"); + jlong mem_limit = OSContainer::memory_limit_in_bytes(); + if (mem_limit <= 0) { + return false; + } + + value = static_cast(mem_limit); + return true; +} +bool os::container_memory_soft_limit(physical_memory_size_type& value) { + assert(is_containerized(), "must be running containerized"); + jlong mem_limit = OSContainer::memory_soft_limit_in_bytes(); + if (mem_limit <= 0) { + return false; + } + + value = static_cast(mem_limit); + return true; +} + +bool os::container_memory_throttle_limit(physical_memory_size_type& value) { + assert(is_containerized(), "must be running containerized"); + jlong mem_limit = OSContainer::memory_throttle_limit_in_bytes(); + if (mem_limit <= 0) { + return false; + } + + value = static_cast(mem_limit); + return true; +} + +bool os::container_used_memory(physical_memory_size_type& value) { + assert(is_containerized(), "must be running containerized"); + jlong mem_usage = OSContainer::memory_usage_in_bytes(); + if (mem_usage <= 0) { + return false; + } + + value = static_cast(mem_usage); + return true; +} + 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 (is_containerized() && container_available_memory(value)) { + log_trace(os)("available container memory: " PHYS_MEM_TYPE_FORMAT, value); return true; } + return machine_available_memory(value); +} + +bool os::machine_available_memory(physical_memory_size_type& value) { return Linux::available_memory(value); } +bool os::container_available_memory(physical_memory_size_type& value) { + assert(is_containerized(), "must be running containerized"); + julong avail_mem = 0; + if (OSContainer::available_memory_in_container(avail_mem)) { + value = static_cast(avail_mem); + return true; + } + return false; +} + bool os::Linux::available_memory(physical_memory_size_type& value) { julong avail_mem = static_cast(-1L); @@ -251,13 +310,15 @@ bool os::Linux::available_memory(physical_memory_size_type& value) { } 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 (is_containerized() && container_available_memory(value)) { + log_trace(os)("free container memory: " PHYS_MEM_TYPE_FORMAT, value); return true; } + return machine_free_memory(value); +} + +bool os::machine_free_memory(physical_memory_size_type& value) { return Linux::free_memory(value); } @@ -276,14 +337,13 @@ bool os::Linux::free_memory(physical_memory_size_type& value) { } 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; - } - } // fallback to the host swap space if the container did return the unbound value of -1 + if (is_containerized() && container_total_swap_space(value)) { + return true; + } // fallback to the host swap space if the container value fails + return machine_total_swap_space(value); +} + +bool os::machine_total_swap_space(physical_memory_size_type& value) { struct sysinfo si; int ret = sysinfo(&si); if (ret != 0) { @@ -294,6 +354,17 @@ bool os::total_swap_space(physical_memory_size_type& value) { return true; } +bool os::container_total_swap_space(physical_memory_size_type& value) { + assert(is_containerized(), "must be running 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; + } + return false; +} + static bool host_free_swap_f(physical_memory_size_type& value) { struct sysinfo si; int ret = sysinfo(&si); @@ -313,50 +384,69 @@ bool os::free_swap_space(physical_memory_size_type& value) { if (!os::total_swap_space(total_swap_space) || !host_free_swap_f(host_free_swap)) { return false; } - 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; - } - } + size_t host_free_swap_val = MIN2(total_swap_space, host_free_swap); + if (is_containerized()) { + if (container_free_swap_space(value)) { + return true; + } else { + // 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=" PHYS_MEM_TYPE_FORMAT " returning host value: %zu", + OSContainer::memory_and_swap_limit_in_bytes(), OSContainer::memory_limit_in_bytes(), + host_free_swap_val); } - // 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); } + value = host_free_swap_val; return true; } +bool os::machine_free_swap_space(physical_memory_size_type& value) { + return host_free_swap_f(value); +} + +bool os::container_free_swap_space(physical_memory_size_type& value) { + assert(is_containerized(), "must be running 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; + } + } + } + return false; +} + 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); + if (is_containerized()) { + physical_memory_size_type mem_limit; + if (container_memory_limit(mem_limit)) { + log_trace(os)("total container memory: " PHYS_MEM_TYPE_FORMAT, mem_limit); + return mem_limit; } } - physical_memory_size_type phys_mem = Linux::physical_memory(); + physical_memory_size_type phys_mem = machine_physical_memory(); log_trace(os)("total system memory: " PHYS_MEM_TYPE_FORMAT, phys_mem); return phys_mem; } +physical_memory_size_type os::machine_physical_memory() { + return Linux::physical_memory(); +} + // Returns the resident set size (RSS) of the process. // Falls back to using VmRSS from /proc/self/status if /proc/self/smaps_rollup is unavailable. // Note: On kernels with memory cgroups or shared memory, VmRSS may underreport RSS. @@ -4779,16 +4869,25 @@ int os::active_processor_count() { return ActiveProcessorCount; } - int active_cpus; - if (OSContainer::is_containerized()) { - active_cpus = OSContainer::active_processor_count(); + if (is_containerized()) { + double cpu_quota = container_processor_count(); + int active_cpus = ceilf(cpu_quota); // Round fractional CPU quota up. + assert(active_cpus <= machine_active_processor_count(), "must be"); log_trace(os)("active_processor_count: determined by OSContainer: %d", active_cpus); - } else { - active_cpus = os::Linux::active_processor_count(); + return active_cpus; } - return active_cpus; + return machine_active_processor_count(); +} + +int os::machine_active_processor_count() { + return os::Linux::active_processor_count(); +} + +double os::container_processor_count() { + assert(is_containerized(), "must be running containerized"); + return OSContainer::active_processor_count(); } static bool should_warn_invalid_processor_id() { diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 875e97ce03837..f77d8c90d74d7 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -838,10 +838,18 @@ bool os::available_memory(physical_memory_size_type& value) { return win32::available_memory(value); } +bool os::machine_available_memory(physical_memory_size_type& value) { + return win32::available_memory(value); +} + bool os::free_memory(physical_memory_size_type& value) { return win32::available_memory(value); } +bool os::machine_free_memory(physical_memory_size_type& value) { + return win32::available_memory(value); +} + bool os::win32::available_memory(physical_memory_size_type& value) { // Use GlobalMemoryStatusEx() because GlobalMemoryStatus() may return incorrect // value if total memory is larger than 4GB @@ -857,7 +865,11 @@ bool os::win32::available_memory(physical_memory_size_type& value) { } } -bool os::total_swap_space(physical_memory_size_type& value) { +bool os::total_swap_space(physical_memory_size_type& value) { + return machine_total_swap_space(value); +} + +bool os::machine_total_swap_space(physical_memory_size_type& value) { MEMORYSTATUSEX ms; ms.dwLength = sizeof(ms); BOOL res = GlobalMemoryStatusEx(&ms); @@ -871,6 +883,10 @@ bool os::total_swap_space(physical_memory_size_type& value) { } bool os::free_swap_space(physical_memory_size_type& value) { + return machine_free_swap_space(value); +} + +bool os::machine_free_swap_space(physical_memory_size_type& value) { MEMORYSTATUSEX ms; ms.dwLength = sizeof(ms); BOOL res = GlobalMemoryStatusEx(&ms); @@ -887,6 +903,10 @@ physical_memory_size_type os::physical_memory() { return win32::physical_memory(); } +physical_memory_size_type os::machine_physical_memory() { + return win32::physical_memory(); +} + size_t os::rss() { size_t rss = 0; PROCESS_MEMORY_COUNTERS_EX pmex; @@ -910,6 +930,10 @@ int os::active_processor_count() { return ActiveProcessorCount; } + return machine_active_processor_count(); +} + +int os::machine_active_processor_count() { bool schedules_all_processor_groups = win32::is_windows_11_or_greater() || win32::is_windows_server_2022_or_greater(); if (UseAllWindowsProcessorGroups && !schedules_all_processor_groups && !win32::processor_group_warning_displayed()) { win32::set_processor_group_warning_displayed(true); diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp index cc5bbe1fc6059..808ebb3e23fab 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp @@ -66,10 +66,6 @@ #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" -#ifdef LINUX -#include "os_linux.hpp" -#include "osContainer_linux.hpp" -#endif #define NO_TRANSITION(result_type, header) extern "C" { result_type JNICALL header { #define NO_TRANSITION_END } } @@ -401,33 +397,18 @@ JVM_ENTRY_NO_ENV(jboolean, jfr_is_class_instrumented(JNIEnv* env, jclass jvm, jc JVM_END JVM_ENTRY_NO_ENV(jboolean, jfr_is_containerized(JNIEnv* env, jclass jvm)) -#ifdef LINUX - return OSContainer::is_containerized(); -#else - return false; -#endif + return os::is_containerized(); JVM_END JVM_ENTRY_NO_ENV(jlong, jfr_host_total_memory(JNIEnv* env, jclass jvm)) -#ifdef LINUX - // We want the host memory, not the container limit. - // os::physical_memory() would return the container limit. - return static_cast(os::Linux::physical_memory()); -#else - return static_cast(os::physical_memory()); -#endif + return static_cast(os::machine_physical_memory()); 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(); -#else physical_memory_size_type total_swap_space = 0; // Return value ignored - defaulting to 0 on failure. - (void)os::total_swap_space(total_swap_space); + (void)os::machine_total_swap_space(total_swap_space); return static_cast(total_swap_space); -#endif JVM_END JVM_ENTRY_NO_ENV(void, jfr_emit_data_loss(JNIEnv* env, jclass jvm, jlong bytes)) diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index ade9b45e2eb62..53858b3f2e046 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -114,9 +114,6 @@ #if INCLUDE_MANAGEMENT #include "services/finalizerService.hpp" #endif -#ifdef LINUX -#include "osContainer_linux.hpp" -#endif #include @@ -486,11 +483,9 @@ JVM_LEAF(jboolean, JVM_IsUseContainerSupport(void)) JVM_END JVM_LEAF(jboolean, JVM_IsContainerized(void)) -#ifdef LINUX - if (OSContainer::is_containerized()) { + if (os::is_containerized()) { return JNI_TRUE; } -#endif return JNI_FALSE; JVM_END diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index f77b648ba952c..c6c5a8d74e11b 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -129,7 +129,6 @@ #ifdef LINUX #include "cgroupSubsystem_linux.hpp" #include "os_linux.hpp" -#include "osContainer_linux.hpp" #endif #define CHECK_JNI_EXCEPTION_(env, value) \ @@ -2498,14 +2497,12 @@ WB_ENTRY(jboolean, WB_CheckLibSpecifiesNoexecstack(JNIEnv* env, jobject o, jstri WB_END WB_ENTRY(jboolean, WB_IsContainerized(JNIEnv* env, jobject o)) - LINUX_ONLY(return OSContainer::is_containerized();) - return false; + return os::is_containerized(); WB_END // Physical memory of the host machine (including containers) WB_ENTRY(jlong, WB_HostPhysicalMemory(JNIEnv* env, jobject o)) - LINUX_ONLY(return static_cast(os::Linux::physical_memory());) - return static_cast(os::physical_memory()); + return static_cast(os::machine_physical_memory()); WB_END // Available memory of the host machine (container-aware) diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 674b0a558414c..c74d8ff1e34ec 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -81,10 +81,6 @@ #include "utilities/permitForbiddenFunctions.hpp" #include "utilities/powerOfTwo.hpp" -#ifdef LINUX -#include "osContainer_linux.hpp" -#endif - #ifndef _WINDOWS # include #endif @@ -2205,17 +2201,14 @@ 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; - } + if (is_containerized()) { + return container_used_memory(value); } -#endif + + return machine_used_memory(value); +} + +bool os::machine_used_memory(physical_memory_size_type& value) { physical_memory_size_type avail_mem = 0; // Return value ignored - defaulting to 0 on failure. (void)os::available_memory(avail_mem); @@ -2224,6 +2217,52 @@ bool os::used_memory(physical_memory_size_type& value) { return true; } +#ifndef LINUX +bool os::is_containerized() { + return false; +} + +double os::container_processor_count() { + assert(is_containerized(), "must be running containerized"); + return 0.0; +} + +bool os::container_available_memory(physical_memory_size_type& value) { + assert(is_containerized(), "must be running containerized"); + return false; +} + +bool os::container_used_memory(physical_memory_size_type& value) { + assert(is_containerized(), "must be running containerized"); + return false; +} + +bool os::container_total_swap_space(physical_memory_size_type& value) { + assert(is_containerized(), "must be running containerized"); + return false; +} + +bool os::container_free_swap_space(physical_memory_size_type& value) { + assert(is_containerized(), "must be running containerized"); + return false; +} + +bool os::container_memory_limit(physical_memory_size_type& value) { + assert(is_containerized(), "must be running containerized"); + return false; +} + +bool os::container_memory_soft_limit(physical_memory_size_type& value) { + assert(is_containerized(), "must be running containerized"); + return false; +} + +bool os::container_memory_throttle_limit(physical_memory_size_type& value) { + assert(is_containerized(), "must be running containerized"); + return false; +} +#endif + bool os::commit_memory(char* addr, size_t bytes, bool executable) { assert_nonempty_range(addr, bytes); diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index e008f29eecc3d..a04e3dc0df40e 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -342,6 +342,36 @@ class os: AllStatic { static bool is_server_class_machine(); static size_t rss(); + // On platforms with container support (currently only Linux) + // we combine machine values with potential container values, abstracting + // which value is actually used. These functions are used to get machine + // values and container values (when available) separately. + static bool is_containerized(); + + static int machine_active_processor_count(); + + [[nodiscard]] static bool machine_available_memory(physical_memory_size_type& value); + [[nodiscard]] static bool machine_used_memory(physical_memory_size_type& value); + [[nodiscard]] static bool machine_free_memory(physical_memory_size_type& value); + + [[nodiscard]] static bool machine_total_swap_space(physical_memory_size_type& value); + [[nodiscard]] static bool machine_free_swap_space(physical_memory_size_type& value); + + static physical_memory_size_type machine_physical_memory(); + + // Container values, not safe to use unless `is_containerized()` is true. + static double container_processor_count(); + + [[nodiscard]] static bool container_available_memory(physical_memory_size_type& value); + [[nodiscard]] static bool container_used_memory(physical_memory_size_type& value); + + [[nodiscard]] static bool container_total_swap_space(physical_memory_size_type& value); + [[nodiscard]] static bool container_free_swap_space(physical_memory_size_type& value); + + [[nodiscard]] static bool container_memory_limit(physical_memory_size_type& value); + [[nodiscard]] static bool container_memory_soft_limit(physical_memory_size_type& value); + [[nodiscard]] static bool container_memory_throttle_limit(physical_memory_size_type& value); + // Returns the id of the processor on which the calling thread is currently executing. // The returned value is guaranteed to be between 0 and (os::processor_count() - 1). static uint processor_id(); diff --git a/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java b/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java index 99220201f6669..51e17f29de42d 100644 --- a/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java +++ b/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java @@ -146,9 +146,9 @@ private static void testCpus(int valueToSet, int expectedTraceValue) throws Exce // Expected active processor count can not exceed available CPU count - private static int adjustExpectedAPCForAvailableCPUs(int expectedAPC) { - if (expectedAPC > availableCPUs) { - expectedAPC = availableCPUs; + private static double adjustExpectedAPCForAvailableCPUs(double expectedAPC) { + if (expectedAPC > (double)availableCPUs) { + expectedAPC = (double)availableCPUs; System.out.println("Adjusted expectedAPC = " + expectedAPC); } return expectedAPC; @@ -161,7 +161,7 @@ private static void testCpuQuotaAndPeriod(int quota, int period, boolean addCgmo System.out.println("quota = " + quota); System.out.println("period = " + period); - int expectedAPC = (int) Math.ceil((float) quota / (float) period); + double expectedAPC = (double) quota / (double) period; System.out.println("expectedAPC = " + expectedAPC); expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC); @@ -181,7 +181,7 @@ private static void testCpuQuotaAndPeriod(int quota, int period, boolean addCgmo private static void testAPCCombo(String cpuset, int quota, int period, int shares, - int expectedAPC) throws Exception { + double expectedAPC) throws Exception { Common.logNewTestCase("test APC Combo"); System.out.println("cpuset = " + cpuset); System.out.println("quota = " + quota);