diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index f07315a8637..3e0b3600f4f 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -63,6 +63,7 @@ GLIBC_UNSUPPORTED(A53MAC ); \ GLIBC_UNSUPPORTED(ECV ); \ GLIBC_UNSUPPORTED(WFXT ); \ + GLIBC_UNSUPPORTED(SVE256 ); \ GLIBC_UNSUPPORTED(NOTPACA ); \ /**/ #include "runtime/abstract_vm_version.inline.hpp" @@ -930,3 +931,56 @@ void VM_Version::CPUFeatures_apply_arch(VM_Features &parsed, VM_Features &missin missing.clear_feature(CPU_PACA); missing.clear_feature(CPU_NOTPACA); } + +bool VM_Version::process_image_cpu_features(const VM_Features *image_featuresp) { + int want; + if (image_featuresp == nullptr) { + // Cleanup after a failed restore. + want = supports_feature(CPU_SVE256) ? 32 : 16; + } else { + const VM_Features &image_features = *image_featuresp; + bool image_supports_sve256 = image_features.supports_feature(CPU_SVE256); + if (image_supports_sve256 && !_cpu_features.supports_feature(CPU_SVE256)) { + if (!Abstract_VM_Version::should_check_cpu_features()) { + return true; + } + ResourceMark rm; + VM_Features sve256; + sve256.set_feature(CPU_SVE256); + VM_Features use = image_features & _cpu_features; + log_error(crac)("The image has -XX:CPUFeatures=%s with CPU_SVE256=%s, this CPU has CPUFeatures=%s not supporting CPU_SVE256, try using -XX:CPUFeatures=%s on checkpoint.", + image_features.print_numbers(), sve256.print_numbers(), _cpu_features.print_numbers(), use.print_numbers()); + return false; + } + want = image_supports_sve256 ? 32 : 16; + } + if (set_maximum_sve_vector_length(want) == want) { + return true; + } + if (!_cpu_features.supports_feature(CPU_SVE)) { + guarantee(want == 16, "CPU_SVE256 cannot be present without CPU_SVE"); + return true; + } + // Always call PR_SVE_SET_VL, we do not know who did execute this JVM. + errno = 0; + int got = set_and_get_current_sve_vector_length(want); + if (got != want) { + ResourceMark rm; + VM_Features sve256; + sve256.set_feature(CPU_SVE256); + if (image_featuresp != nullptr) { + const VM_Features &image_features = *image_featuresp; + log_error(crac)("The image has -XX:CPUFeatures=%s with CPU_SVE256=%s %s, this CPU has CPUFeatures=%s but PR_SVE_SET_VL reports %d: %s", + image_features.print_numbers(), sve256.print_numbers(), + want == 32 ? "set" : "unset", + _cpu_features.print_numbers(), got, os::strerror(errno)); + } else { + log_error(crac)("Cannot restore this JVM's CPUFeatures=%s with CPU_SVE256=%s %s, this CPU has CPUFeatures=%s but PR_SVE_SET_VL reports %d: %s", + _features.print_numbers(), sve256.print_numbers(), + want == 32 ? "set" : "unset", + _cpu_features.print_numbers(), got, os::strerror(errno)); + } + return false; + } + return true; +} diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index c44c9124523..d31005924ae 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -59,6 +59,7 @@ class VM_Feature_Flag { decl(ECV, ecv, 21) \ decl(WFXT, wfxt, 22) \ /* These features are added for CRaC. */ \ + decl(SVE256, sve256, 62) \ decl(NOTPACA, notpaca, 63) \ /**/ @@ -68,7 +69,7 @@ class VM_Feature_Flag { #undef DECLARE_CPU_FEATURE_FLAG MAX_CPU_FEATURES, LAST_CPU_FEATURE = CPU_WFXT, - FIRST_GLIBC_FEATURE = CPU_NOTPACA + FIRST_GLIBC_FEATURE = CPU_SVE256 }; }; @@ -113,14 +114,17 @@ class VM_Version : public Abstract_VM_Version, public VM_Feature_Flag { // Return the length that will be used, or -ve if an error occurred. static int set_and_get_current_sve_vector_length(int len); static int get_current_sve_vector_length(); + // Limit what set_and_get_current_sve_vector_length is willing to set. + static int set_maximum_sve_vector_length(int length); public: // Initialization typedef ::VM_Features VM_Features; static void initialize(); static bool cpu_features_binary(VM_Features *data); - static bool check_cpu_features_skip() { - return _ignore_glibc_not_using; + static bool _cpu_features_ignore; + static bool can_use_cpu_features() { + return !_cpu_features_ignore; } static void check_virtualizations(); @@ -137,11 +141,12 @@ class VM_Version : public Abstract_VM_Version, public VM_Feature_Flag { static constexpr char glibc_prefix[] = ":glibc.cpu.hwcaps="; static constexpr size_t glibc_prefix_len = sizeof(glibc_prefix) - 1; #endif //LINUX - static bool _ignore_glibc_not_using; static void print_using_features_cr(); static void insert_features_names(VM_Version::VM_Features features, outputStream& os); // The returned string needs a ResourceMark. static const char *restore_failed_check(const VM_Features *image_features, const VM_Features *current_features); + static bool process_image_cpu_features(const VM_Features *image_featuresp); + static constexpr bool process_image_cpu_features_needed = true; static void print_platform_virtualization_info(outputStream*); diff --git a/src/hotspot/cpu/arm/vm_version_arm.hpp b/src/hotspot/cpu/arm/vm_version_arm.hpp index cd16e8c5d45..d2c0f5f65e2 100644 --- a/src/hotspot/cpu/arm/vm_version_arm.hpp +++ b/src/hotspot/cpu/arm/vm_version_arm.hpp @@ -41,10 +41,14 @@ class VM_Version: public Abstract_VM_Version { static bool is_initialized() { return _is_initialized; } struct VM_Features: public Zero_Features {}; static bool cpu_features_binary(VM_Features *data) { return false; } - static bool check_cpu_features_skip() { return true; } + static bool can_use_cpu_features() { return false; } static const char *restore_failed_check(const VM_Features *image_features, const VM_Features *current_features) { return nullptr; } + static bool process_image_cpu_features(const VM_Features *image_featuresp) { + return false; + } + static constexpr bool process_image_cpu_features_needed = false; protected: diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.hpp b/src/hotspot/cpu/ppc/vm_version_ppc.hpp index 628a2493220..5d3f015fc3d 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.hpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.hpp @@ -57,7 +57,7 @@ class VM_Version: public Abstract_VM_Version { static void check_virtualizations(); struct VM_Features: public Zero_Features {}; static bool cpu_features_binary(VM_Features *data) { return false; } - static bool check_cpu_features_skip() { return true; } + static bool can_use_cpu_features() { return false; } // Override Abstract_VM_Version implementation static void print_platform_virtualization_info(outputStream*); diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index f5444bc1d6d..11d1c0e0ba9 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -502,7 +502,7 @@ class VM_Version : public Abstract_VM_Version { static void initialize_cpu_information(); struct VM_Features: public Zero_Features {}; static bool cpu_features_binary(VM_Features *data) { return false; } - static bool check_cpu_features_skip() { return true; } + static bool can_use_cpu_features() { return false; } constexpr static bool supports_stack_watermark_barrier() { return true; } diff --git a/src/hotspot/cpu/s390/vm_version_s390.hpp b/src/hotspot/cpu/s390/vm_version_s390.hpp index 38c116f0ac8..c0a073f3f85 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.hpp +++ b/src/hotspot/cpu/s390/vm_version_s390.hpp @@ -31,7 +31,6 @@ #include "runtime/globals_extension.hpp" class VM_Version: public Abstract_VM_Version { - protected: // z/Architecture is the name of the 64-bit extension of the 31-bit s390 // architecture. @@ -418,7 +417,7 @@ class VM_Version: public Abstract_VM_Version { static bool is_determine_features_test_running() { return _is_determine_features_test_running; } struct VM_Features: public Zero_Features {}; static bool cpu_features_binary(VM_Features *data) { return false; } - static bool check_cpu_features_skip() { return true; } + static bool can_use_cpu_features() { return false; } // Override Abstract_VM_Version implementation static void print_platform_virtualization_info(outputStream*); diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index 64e3011c764..c44be61c9fc 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -726,12 +726,15 @@ class VM_Version : public Abstract_VM_Version, protected VM_Feature_Flag { static constexpr char glibc_prefix[] = ":glibc.cpu.hwcaps="; static constexpr size_t glibc_prefix_len = sizeof(glibc_prefix) - 1; #endif //LINUX - static bool _ignore_glibc_not_using; static void print_using_features_cr(); static void insert_features_names(VM_Version::VM_Features features, outputStream& os); static const char *restore_failed_check(const VM_Features *image_features, const VM_Features *current_features) { return nullptr; } + static bool process_image_cpu_features(const VM_Features *image_featuresp) { + return false; + } + static constexpr bool process_image_cpu_features_needed = false; static bool os_supports_avx_vectors(); static bool os_supports_apx_egprs(); @@ -813,8 +816,9 @@ class VM_Version : public Abstract_VM_Version, protected VM_Feature_Flag { // Initialization static void initialize(); static bool cpu_features_binary(VM_Features *data); - static bool check_cpu_features_skip() { - return _ignore_glibc_not_using; + static bool _cpu_features_ignore; + static bool can_use_cpu_features() { + return !_cpu_features_ignore; } // Override Abstract_VM_Version implementation diff --git a/src/hotspot/cpu/zero/vm_version_zero.hpp b/src/hotspot/cpu/zero/vm_version_zero.hpp index 5ec01e360a1..8718786c79b 100644 --- a/src/hotspot/cpu/zero/vm_version_zero.hpp +++ b/src/hotspot/cpu/zero/vm_version_zero.hpp @@ -34,10 +34,14 @@ class VM_Version : public Abstract_VM_Version { static void initialize(); struct VM_Features: public Zero_Features {}; static bool cpu_features_binary(VM_Features *data) { return false; } - static bool check_cpu_features_skip() { return true; } + static bool can_use_cpu_features() { return false; } static const char *restore_failed_check(const VM_Features *image_features, const VM_Features *current_features) { return nullptr; } + static bool process_image_cpu_features(const VM_Features *image_featuresp) { + return false; + } + static constexpr bool process_image_cpu_features_needed = false; constexpr static bool supports_stack_watermark_barrier() { return true; } diff --git a/src/hotspot/os_cpu/bsd_aarch64/vm_version_bsd_aarch64.cpp b/src/hotspot/os_cpu/bsd_aarch64/vm_version_bsd_aarch64.cpp index 9a2aa75a7b8..253998ad46d 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/vm_version_bsd_aarch64.cpp +++ b/src/hotspot/os_cpu/bsd_aarch64/vm_version_bsd_aarch64.cpp @@ -35,6 +35,11 @@ int VM_Version::get_current_sve_vector_length() { return -1; } +int VM_Version::set_maximum_sve_vector_length(int length) { + ShouldNotCallThis(); + return length; +} + int VM_Version::set_and_get_current_sve_vector_length(int length) { ShouldNotCallThis(); return -1; diff --git a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp index 3f935146084..f1b577fe098 100644 --- a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp @@ -24,6 +24,7 @@ */ #include "memory/resourceArea.hpp" +#include "runtime/globals_extension.hpp" #include "runtime/java.hpp" #include "runtime/os.hpp" #include "runtime/os.inline.hpp" @@ -120,9 +121,17 @@ int VM_Version::get_current_sve_vector_length() { return prctl(PR_SVE_GET_VL); } +static int maximum_sve_vector_length = INT_MAX; + +int VM_Version::set_maximum_sve_vector_length(int length) { + int retval = maximum_sve_vector_length; + maximum_sve_vector_length = length; + return retval; +} + int VM_Version::set_and_get_current_sve_vector_length(int length) { - assert(VM_Version::supports_sve(), "should not call this"); - int new_length = prctl(PR_SVE_SET_VL, length); + assert(_cpu_features.supports_feature(CPU_SVE), "should not call this"); + int new_length = prctl(PR_SVE_SET_VL, MIN2(length, maximum_sve_vector_length)); return new_length; } @@ -163,6 +172,10 @@ void VM_Version::get_os_cpu_info() { update_feature(auxv2, CPU_WFXT, HWCAP2_WFXT ); update_feature(~auxv, CPU_NOTPACA, HWCAP_PACA ); + if (supports_sve() && get_current_sve_vector_length() == 32) { + set_feature(CPU_SVE256); + } + uint64_t ctr_el0; uint64_t dczid_el0; __asm__ ( @@ -226,6 +239,20 @@ void VM_Version::check_os_cpu_info() { // GLIBC_TUNABLES=glibc.cpu.hwcaps is unsupported on aarch64 vm_exit_during_initialization(err_msg("LSE (%s) cannot be disabled via -XX:CPUFeatures on aarch64.", lse.print_numbers())); } + if (FLAG_IS_DEFAULT(CPUFeatures)) { + assert(_cpu_features.supports_feature(CPU_SVE256) == _features.supports_feature(CPU_SVE256), "CPU_SVE256 is not changed"); + } else if (_cpu_features.supports_feature(CPU_SVE256) && !_features.supports_feature(CPU_SVE256)) { + maximum_sve_vector_length = 16; + int got = set_and_get_current_sve_vector_length(maximum_sve_vector_length); + if (got != maximum_sve_vector_length) { + stringStream ss; + VM_Features sve256; + sve256.set_feature(VM_Feature_Flag::CPU_SVE256); + ss.print("Specified -XX:CPUFeatures=%s have unset CPU_SVE256=%s, this CPU has CPUFeatures=%s but it cannot be disabled as PR_SVE_SET_VL reports %d: %m", + _features.print_numbers(), sve256.print_numbers(), _cpu_features.print_numbers(), got); + vm_exit_during_initialization(ss.base()); + } + } } static bool read_fully(const char *fname, char *buf, size_t buflen) { diff --git a/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp b/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp index 963ba06b2fd..e08a2a10e2d 100644 --- a/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp +++ b/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp @@ -61,6 +61,12 @@ int VM_Version::get_current_sve_vector_length() { return VM_Version::supports_sve() ? get_sve_vector_length() : 0; } +int VM_Version::set_maximum_sve_vector_length(int length) { + // CPU_SVE256 cannot be set on Windows. + ShouldNotCallThis(); + return length; +} + int VM_Version::set_and_get_current_sve_vector_length(int length) { assert(VM_Version::supports_sve(), "should not call this"); diff --git a/src/hotspot/share/include/crlib/crlib_image_constraints.h b/src/hotspot/share/include/crlib/crlib_image_constraints.h index 9f0caa935d6..952611b9e14 100644 --- a/src/hotspot/share/include/crlib/crlib_image_constraints.h +++ b/src/hotspot/share/include/crlib/crlib_image_constraints.h @@ -69,6 +69,13 @@ typedef const struct crlib_image_constraints { // Returns the size of the data, in bytes — it can be more, equal to or less than // 'value_size'. Returned value of 0 represents an error. size_t (*get_failed_bitmap)(crlib_conf_t *, const char *name, unsigned char *value_return, size_t value_size); + + // Register callbacks before restore. Callbacks are called only during restore. + // If the callback returns false the restore is aborted. + // name is not copied, its content must remain valid. + // user_data is an arbitrary pointer value which is passed along. + bool (*register_label_hook)(crlib_conf_t *, const char *name, bool (*hook)(const char *value, void *user_data), void *user_data); + bool (*register_bitmap_hook)(crlib_conf_t *, const char *name, bool (*hook)(const unsigned char *value, size_t value_size, void *user_data), void *user_data); } crlib_image_constraints_t; #ifdef __cplusplus diff --git a/src/hotspot/share/runtime/abstract_vm_version.cpp b/src/hotspot/share/runtime/abstract_vm_version.cpp index 33fdb4caa3a..3e39a8622e6 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.cpp +++ b/src/hotspot/share/runtime/abstract_vm_version.cpp @@ -401,3 +401,7 @@ void Abstract_VM_Version::check_cpufeatures_vmoptions() { tty->print_cr("Do not use -XX:ShowCPUFeatures: this architecture does not support any arch-specific strings."); } } + +bool Abstract_VM_Version::should_check_cpu_features() { + return VM_Version::can_use_cpu_features() && (CheckCPUFeatures == nullptr || strcmp(CheckCPUFeatures, "skip") != 0); +} diff --git a/src/hotspot/share/runtime/abstract_vm_version.hpp b/src/hotspot/share/runtime/abstract_vm_version.hpp index 35f623d2363..7c5af8d0f4d 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.hpp +++ b/src/hotspot/share/runtime/abstract_vm_version.hpp @@ -268,6 +268,8 @@ class Abstract_VM_Version: AllStatic { // features_buffer is an opaque object that stores arch specific representation of cpu features static bool verify_aot_code_cache_features(void* features_buffer) { return false; }; + + static bool should_check_cpu_features(); }; #endif // SHARE_RUNTIME_ABSTRACT_VM_VERSION_HPP diff --git a/src/hotspot/share/runtime/abstract_vm_version.inline.hpp b/src/hotspot/share/runtime/abstract_vm_version.inline.hpp index 8f710ef642d..98edee904e7 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.inline.hpp +++ b/src/hotspot/share/runtime/abstract_vm_version.inline.hpp @@ -35,7 +35,7 @@ VM_Features VM_Version::CPUFeatures_parse(const char *str) { if (str == nullptr || strcmp(str, "native") == 0) { return _features; } else if (strcmp(str, "ignore") == 0) { - _ignore_glibc_not_using = true; + _cpu_features_ignore = true; return _features; } else if (strcmp(str, "generic") == 0) { return CPUFeatures_generic(); @@ -80,7 +80,7 @@ VM_Features VM_Version::CPUFeatures_parse_numeric(const char *str) { #endif // LINUX } -bool VM_Version::_ignore_glibc_not_using = LINUX_ONLY(false) NOT_LINUX(true); +bool VM_Version::_cpu_features_ignore = LINUX_ONLY(false) NOT_LINUX(true); #ifdef LINUX bool VM_Version::glibc_env_set(char *disable_str) { #define TUNABLES_NAME "GLIBC_TUNABLES" @@ -196,7 +196,7 @@ void VM_Version::glibc_reexec() { // Returns whether we should have got set a GLIBC_TUNABLES environment variables but did not get any. bool VM_Version::glibc_not_using() { - if (_ignore_glibc_not_using) + if (_cpu_features_ignore) return true; VM_Features features_expected; @@ -290,7 +290,7 @@ bool VM_Version::glibc_not_using() { #endif // LINUX void VM_Version::print_using_features_cr() { - if (_ignore_glibc_not_using) { + if (_cpu_features_ignore) { tty->print_raw_cr("CPU features are being kept intact as requested by -XX:CPUFeatures=ignore"); } else { tty->print_raw("CPU features being used are: -XX:CPUFeatures="); diff --git a/src/hotspot/share/runtime/crac.cpp b/src/hotspot/share/runtime/crac.cpp index b935092d2a1..9e924882bac 100644 --- a/src/hotspot/share/runtime/crac.cpp +++ b/src/hotspot/share/runtime/crac.cpp @@ -328,7 +328,9 @@ int crac::checkpoint_restore(int *shmid) { // Setup CPU arch & features only during the first checkpoint; the feature set // cannot change after initial boot (and we don't support switching the engine). - if (_generation == 1 && !VM_Version::check_cpu_features_skip()) { + // should_check_cpu_features() is not valid here as -XX:CheckCPUFeatures=skip + // does not apply for storing of CPUFeatures. + if (_generation == 1 && VM_Version::can_use_cpu_features()) { VM_Version::VM_Features current_features; if (VM_Version::cpu_features_binary(¤t_features)) { switch (_engine->prepare_image_constraints_api()) { @@ -852,6 +854,14 @@ void crac::prepare_restore(crac_restore_data& restore_data) { restore_data.restore_nanos = os::javaTimeNanos(); } +template +class RestoreCpuInfo { + F _f; +public: + explicit RestoreCpuInfo(F f) : _f(f) {} + ~RestoreCpuInfo() { _f(); } +}; + void crac::restore(crac_restore_data& restore_data) { precond(CRaCRestoreFrom != nullptr); @@ -873,7 +883,7 @@ void crac::restore(crac_restore_data& restore_data) { // Since the check itself is delegated to the C/R Engine we will simply // skip the check here. - bool ignore = VM_Version::check_cpu_features_skip(); + bool ignore = !VM_Version::can_use_cpu_features(); bool exact = false; if (CheckCPUFeatures == nullptr || !strcmp(CheckCPUFeatures, "compatible")) { // default, compatible @@ -890,7 +900,9 @@ void crac::restore(crac_restore_data& restore_data) { case CracEngine::ApiStatus::OK: { VM_Version::VM_Features current_features; if (VM_Version::cpu_features_binary(¤t_features)) { - engine.require_cpuinfo(¤t_features, exact); + if (!engine.require_cpuinfo(¤t_features, exact)) { + return; + } } } break; case CracEngine::ApiStatus::ERR: @@ -901,6 +913,9 @@ void crac::restore(crac_restore_data& restore_data) { break; } } + RestoreCpuInfo restore_cpu_info_obj([&]{ + engine.restore_cpuinfo(); + }); switch (engine.prepare_restore_data_api()) { case CracEngine::ApiStatus::OK: { @@ -939,7 +954,9 @@ void crac::restore(crac_restore_data& restore_data) { } const int ret = engine.restore(); - if (ret != 0) { + if (ret == 0) { + ShouldNotReachHere(); + } else { log_error(crac)("CRaC engine failed to restore from %s: error %d", CRaCRestoreFrom, ret); VM_Version::VM_Features current_features; VM_Version::cpu_features_binary(¤t_features); // ignore return value diff --git a/src/hotspot/share/runtime/crac_engine.cpp b/src/hotspot/share/runtime/crac_engine.cpp index 4207e91eea9..9460a02e865 100644 --- a/src/hotspot/share/runtime/crac_engine.cpp +++ b/src/hotspot/share/runtime/crac_engine.cpp @@ -507,6 +507,16 @@ const crlib_conf_option_t *CracEngine::configuration_options() { return _options; } +bool CracEngine::bitmap_constraint_hook(const unsigned char *value, size_t value_size, void *user_data/*unused*/) { + VM_Version::VM_Features features; + if (value_size != sizeof(features)) { + log_error(crac)("Incompatible CPUFeatures length in the image - got %zu, want %zu", value_size, sizeof(features)); + return false; + } + memcpy(&features, value, value_size); + return VM_Version::process_image_cpu_features(&features); +} + static constexpr char cpuarch_name[] = "cpu.arch"; static constexpr char cpufeatures_name[] = "cpu.features"; @@ -517,6 +527,8 @@ CracEngine::ApiStatus CracEngine::prepare_image_constraints_api() { require_method(require_label) require_method(require_bitmap) require_method(is_failed) + require_method(get_failed_bitmap) + require_method(register_bitmap_hook) complete_extension_api(_image_constraints_api) } @@ -534,11 +546,24 @@ bool CracEngine::store_cpuinfo(const VM_Version::VM_Features *current_features) return true; } -void CracEngine::require_cpuinfo(const VM_Version::VM_Features *current_features, bool exact) const { +// Return success. +bool CracEngine::require_cpuinfo(const VM_Version::VM_Features *current_features, bool exact) const { log_debug(crac)("cpufeatures_load user data %s from %s...", cpufeatures_name, CRaCRestoreFrom); _image_constraints_api->require_label(_conf, cpuarch_name, ARCHPROPNAME); _image_constraints_api->require_bitmap(_conf, cpufeatures_name, reinterpret_cast(current_features), sizeof(*current_features), exact ? EQUALS : SUBSET); + if (VM_Version::process_image_cpu_features_needed + && !_image_constraints_api->register_bitmap_hook(_conf, cpufeatures_name, bitmap_constraint_hook, nullptr /* user_data */)) { + return false; + } + return true; +} + +void CracEngine::restore_cpuinfo() const { + log_debug(crac)("restore_cpuinfo..."); + if (VM_Version::process_image_cpu_features_needed) { + VM_Version::process_image_cpu_features(nullptr); + } } void CracEngine::check_cpuinfo(const VM_Version::VM_Features *current_features, bool exact) const { diff --git a/src/hotspot/share/runtime/crac_engine.hpp b/src/hotspot/share/runtime/crac_engine.hpp index 5866cd8255c..18e6fa6ef93 100644 --- a/src/hotspot/share/runtime/crac_engine.hpp +++ b/src/hotspot/share/runtime/crac_engine.hpp @@ -73,7 +73,8 @@ class CracEngine : public CHeapObj { ApiStatus prepare_image_constraints_api(); bool set_label(const char* label, const char* value); bool store_cpuinfo(const VM_Version::VM_Features *current_features) const; - void require_cpuinfo(const VM_Version::VM_Features *current_features, bool exact) const; + bool require_cpuinfo(const VM_Version::VM_Features *current_features, bool exact) const; + void restore_cpuinfo() const; void check_cpuinfo(const VM_Version::VM_Features *current_features, bool exact) const; ApiStatus prepare_image_score_api(); @@ -92,6 +93,8 @@ class CracEngine : public CHeapObj { crlib_image_score_t *_image_score_api = nullptr; crlib_conf_option_t *_options = nullptr; + + static bool bitmap_constraint_hook(const unsigned char *value, size_t value_size, void *user_data); }; #endif // SHARE_RUNTIME_CRAC_ENGINE_HPP diff --git a/src/java.base/share/native/libcrcommon/crcommon.cpp b/src/java.base/share/native/libcrcommon/crcommon.cpp index 82f0a16666e..baa3b2f4926 100644 --- a/src/java.base/share/native/libcrcommon/crcommon.cpp +++ b/src/java.base/share/native/libcrcommon/crcommon.cpp @@ -63,6 +63,14 @@ static size_t get_failed_bitmap(crlib_conf_t* conf, const char* name, unsigned c return COMMON(conf)->image_constraints.get_failed_bitmap(name, value_return, value_size); } +static bool register_label_hook(crlib_conf_t* conf, const char *name, ImageConstraints::LabelHook hook, void *user_data) { + return COMMON(conf)->image_constraints.register_label_hook(name, hook, user_data); +}; + +static bool register_bitmap_hook(crlib_conf_t* conf, const char *name, ImageConstraints::BitmapHook hook, void *user_data) { + return COMMON(conf)->image_constraints.register_bitmap_hook(name, hook, user_data); +}; + static bool set_score(crlib_conf_t* conf, const char* name, double value) { return COMMON(conf)->image_score.set_score(name, value); } @@ -98,6 +106,8 @@ extern JNIEXPORT crlib_image_constraints_t image_constraints_extension = { require_bitmap, is_failed, get_failed_bitmap, + register_label_hook, + register_bitmap_hook, }; JNIEXPORT bool image_constraints_persist(const crcommon_t* conf, const char* image_location) { diff --git a/src/java.base/share/native/libcrcommon/image_constraints.cpp b/src/java.base/share/native/libcrcommon/image_constraints.cpp index f042fce7dc6..eae8d46ca76 100644 --- a/src/java.base/share/native/libcrcommon/image_constraints.cpp +++ b/src/java.base/share/native/libcrcommon/image_constraints.cpp @@ -197,7 +197,7 @@ static void print_bitmap(const char* name, const unsigned char* data, size_t siz } bool ImageConstraints::validate(const char* image_location) const { - if (_constraints.size() == 0) { + if (_constraints.size() == 0 && _hooks.size() == 0) { // If there are no constraints don't even try to open the file (it's fine if it is missing) return true; } @@ -257,6 +257,35 @@ bool ImageConstraints::validate(const char* image_location) const { return false; } } + bool hooks_result = true; + _hooks.foreach([&](const Hook& hook) { + bool found = false; + tags.foreach([&](const Tag& t) { + if (!hooks_result || strcmp(hook.name, t.name) != 0) { + return; + } + if (t.type != hook.type) { + LOG("Image hook type mismatch for '%s'", hook.name); + return; + } + found = true; + switch (hook.type) { + case TagType::LABEL: + hooks_result = hook.hook.label_hook(static_cast(t.data), hook.user_data); + break; + case TagType::BITMAP: + hooks_result = hook.hook.bitmap_hook(static_cast(t.data), t.data_size, hook.user_data); + break; + } + }); + if (!found) { + LOG("Hook did not find its tag '%s'", hook.name); + hooks_result = false; + } + }); + if (!hooks_result) { + return false; + } const char** keys = new(std::nothrow) const char*[tags.size()]; if (keys == nullptr) { LOG("Insufficient memory"); @@ -302,3 +331,19 @@ bool ImageConstraints::validate(const char* image_location) const { }); return result; } + +bool ImageConstraints::register_label_hook(const char *name, LabelHook hook, void *user_data) { + if (!_hooks.add(Hook(name, hook, user_data))) { + LOG("out of memory"); + return false; + } + return true; +} + +bool ImageConstraints::register_bitmap_hook(const char *name, BitmapHook hook, void *user_data) { + if (!_hooks.add(Hook(name, hook, user_data))) { + LOG("out of memory"); + return false; + } + return true; +} diff --git a/src/java.base/share/native/libcrcommon/image_constraints.hpp b/src/java.base/share/native/libcrcommon/image_constraints.hpp index 445408fd278..9088b1ef1c1 100644 --- a/src/java.base/share/native/libcrcommon/image_constraints.hpp +++ b/src/java.base/share/native/libcrcommon/image_constraints.hpp @@ -33,6 +33,10 @@ #include "linkedlist.hpp" class ImageConstraints { +public: + using LabelHook = bool (*)(const char *value, void *user_data); + using BitmapHook = bool (*)(const unsigned char *value, size_t value_size, void *user_data); + private: enum class TagType: std::uint8_t { LABEL, @@ -108,8 +112,26 @@ class ImageConstraints { bool compare_bitmaps(const unsigned char* bitmap, size_t length) const; }; + struct Hook { + TagType type; + const char* name; + union U { + LabelHook label_hook; + BitmapHook bitmap_hook; + U(LabelHook h) : label_hook(h) {} + U(BitmapHook h) : bitmap_hook(h) {} + } hook; + void *user_data; + + Hook(const char* n, LabelHook h, void *ud): + type(TagType::LABEL), name(n), hook(h), user_data(ud) {} + Hook(const char* n, BitmapHook h, void *ud): + type(TagType::BITMAP), name(n), hook(h), user_data(ud) {} + }; + LinkedList _tags; LinkedList _constraints; + LinkedList _hooks; static constexpr const size_t _MAX_NAME_SIZE = 256; static constexpr const size_t _MAX_VALUE_SIZE = 256; @@ -143,7 +165,7 @@ class ImageConstraints { size_t get_failed_bitmap(const char* name, unsigned char *value_return, size_t value_size) const { size_t result = 0; _constraints.foreach([&](Constraint &c) { - if (!strcmp(c.name, name) && c.failed) { + if (c.type == TagType::BITMAP && !strcmp(c.name, name) && c.failed) { if (c.image_data == nullptr) { result = 0; } else { @@ -157,6 +179,10 @@ class ImageConstraints { return result; } + // name is not copied, its content must remain valid. + bool register_label_hook(const char *name, LabelHook hook, void *user_data); + bool register_bitmap_hook(const char *name, BitmapHook hook, void *user_data); + bool persist(const char* image_location) const; bool validate(const char* image_location) const; }; diff --git a/test/jdk/jdk/crac/CPUFeatures/CPUFeaturesAWS.sh b/test/jdk/jdk/crac/CPUFeatures/CPUFeaturesAWS.sh index 720ff3950b6..8a1f18e9f72 100755 --- a/test/jdk/jdk/crac/CPUFeatures/CPUFeaturesAWS.sh +++ b/test/jdk/jdk/crac/CPUFeatures/CPUFeaturesAWS.sh @@ -57,6 +57,13 @@ internal_checkpoint() { } internal_restore() { javasetup + tid=$(echo cr/core-*.img|tr -cd ' 0-9'|sed 's/^.* //') # highest + if [ -n "$tid" ];then + ls -l /proc/$tid/exe || : + bash -c 'echo $$' + (set +x;while [ $(bash -c 'echo $$') -le $tid ];do :;done) + bash -c 'echo $$' + fi bin/java -XX:CRaCRestoreFrom=cr $* & p=$! (sleep 2;kill $p) & @@ -345,11 +352,12 @@ checkpoint_restore "$LINENO" a1.medium t4g.micro checkpoint_restore "$LINENO" t4g.micro a1.medium "1:Restore failed due to incompatible or missing CPU features, try using -XX:CPUFeatures=0x80000000000000ff on checkpoint." checkpoint_restore "$LINENO" t4g.micro a1.medium "-1:LSE (0x100) cannot be disabled via -XX:CPUFeatures on aarch64." "-XX:CPUFeatures=0x80000000000000ff" "" -# JDK-8385359: checkpoint_restore "$LINENO" c7g.medium c7g.medium -# JDK-8385359: checkpoint_restore "$LINENO" c8g.medium c8g.medium -# JDK-8385359: checkpoint_restore "$LINENO" c7g.medium c8g.medium +checkpoint_restore "$LINENO" c7g.medium c7g.medium +checkpoint_restore "$LINENO" c8g.medium c8g.medium +checkpoint_restore "$LINENO" c7g.medium c8g.medium "1:The image has -XX:CPUFeatures=0x4000000000017fff with CPU_SVE256=0x4000000000000000, this CPU has CPUFeatures=0x77fff not supporting CPU_SVE256, try using -XX:CPUFeatures=0x17fff on checkpoint." +checkpoint_restore "$LINENO" c7g.medium c8g.medium "" "-XX:CPUFeatures=0x17fff" "" checkpoint_restore "$LINENO" c8g.medium c7g.medium "1:Restore failed due to incompatible or missing CPU features, try using -XX:CPUFeatures=0x17fff on checkpoint." -# JDK-8385359: checkpoint_restore "$LINENO" c8g.medium c7g.medium "" "-XX:CPUFeatures=0x17fff" "" +checkpoint_restore "$LINENO" c8g.medium c7g.medium "" "-XX:CPUFeatures=0x17fff" "" checkpoint_restore "$LINENO" t4g.micro c8g.medium "1:Restore failed due to incompatible aarch64 CPU feature PACA (0x10000); these CPUs each require a separate image." checkpoint_restore "$LINENO" c8g.medium t4g.micro "1:Restore failed due to incompatible aarch64 CPU feature PACA (0x10000); these CPUs each require a separate image."