8056124: Hotspot should use PICL interface to get cacheline size on SPARC

Using libpicl to get L1 data and L2 cache line sizes

Reviewed-by: kvn, roland, morris
This commit is contained in:
Igor Veresov 2014-09-05 11:23:47 -07:00
parent 35bca0df61
commit 5db7b3a4e1
4 changed files with 149 additions and 53 deletions

View file

@ -143,7 +143,7 @@ else
LIBS += -lsocket -lsched -ldl $(LIBM) -lthread -lc -ldemangle LIBS += -lsocket -lsched -ldl $(LIBM) -lthread -lc -ldemangle
endif # sparcWorks endif # sparcWorks
LIBS += -lkstat LIBS += -lkstat -lpicl
# By default, link the *.o into the library, not the executable. # By default, link the *.o into the library, not the executable.
LINK_INTO$(LINK_INTO) = LIBJVM LINK_INTO$(LINK_INTO) = LIBJVM

View file

@ -32,6 +32,7 @@
int VM_Version::_features = VM_Version::unknown_m; int VM_Version::_features = VM_Version::unknown_m;
const char* VM_Version::_features_str = ""; const char* VM_Version::_features_str = "";
unsigned int VM_Version::_L2_cache_line_size = 0;
void VM_Version::initialize() { void VM_Version::initialize() {
_features = determine_features(); _features = determine_features();
@ -192,7 +193,7 @@ void VM_Version::initialize() {
} }
assert(BlockZeroingLowLimit > 0, "invalid value"); assert(BlockZeroingLowLimit > 0, "invalid value");
if (has_block_zeroing()) { if (has_block_zeroing() && cache_line_size > 0) {
if (FLAG_IS_DEFAULT(UseBlockZeroing)) { if (FLAG_IS_DEFAULT(UseBlockZeroing)) {
FLAG_SET_DEFAULT(UseBlockZeroing, true); FLAG_SET_DEFAULT(UseBlockZeroing, true);
} }
@ -202,7 +203,7 @@ void VM_Version::initialize() {
} }
assert(BlockCopyLowLimit > 0, "invalid value"); assert(BlockCopyLowLimit > 0, "invalid value");
if (has_block_zeroing()) { // has_blk_init() && is_T4(): core's local L2 cache if (has_block_zeroing() && cache_line_size > 0) { // has_blk_init() && is_T4(): core's local L2 cache
if (FLAG_IS_DEFAULT(UseBlockCopy)) { if (FLAG_IS_DEFAULT(UseBlockCopy)) {
FLAG_SET_DEFAULT(UseBlockCopy, true); FLAG_SET_DEFAULT(UseBlockCopy, true);
} }
@ -252,49 +253,6 @@ void VM_Version::initialize() {
// buf is started with ", " or is empty // buf is started with ", " or is empty
_features_str = os::strdup(strlen(buf) > 2 ? buf + 2 : buf); _features_str = os::strdup(strlen(buf) > 2 ? buf + 2 : buf);
// There are three 64-bit SPARC families that do not overlap, e.g.,
// both is_ultra3() and is_sparc64() cannot be true at the same time.
// Within these families, there can be more than one chip, e.g.,
// is_T4() and is_T7() machines are also is_niagara().
if (is_ultra3()) {
assert(_L1_data_cache_line_size == 0, "overlap with Ultra3 family");
// Ref: UltraSPARC III Cu Processor
_L1_data_cache_line_size = 64;
}
if (is_niagara()) {
assert(_L1_data_cache_line_size == 0, "overlap with niagara family");
// All Niagara's are sun4v's, but not all sun4v's are Niagaras, e.g.,
// Fujitsu SPARC64 is sun4v, but we don't want it in this block.
//
// Ref: UltraSPARC T1 Supplement to the UltraSPARC Architecture 2005
// Appendix F.1.3.1 Cacheable Accesses
// -> 16-byte L1 cache line size
//
// Ref: UltraSPARC T2: A Highly-Threaded, Power-Efficient, SPARC SOC
// Section III: SPARC Processor Core
// -> 16-byte L1 cache line size
//
// Ref: Oracle's SPARC T4-1, SPARC T4-2, SPARC T4-4, and SPARC T4-1B Server Architecture
// Section SPARC T4 Processor Cache Architecture
// -> 32-byte L1 cache line size (no longer see that info on this ref)
//
// XXX - still need a T7 reference here
//
if (is_T7()) { // T7 or newer
_L1_data_cache_line_size = 64;
} else if (is_T4()) { // T4 or newer (until T7)
_L1_data_cache_line_size = 32;
} else { // T1 or newer (until T4)
_L1_data_cache_line_size = 16;
}
}
if (is_sparc64()) {
guarantee(_L1_data_cache_line_size == 0, "overlap with SPARC64 family");
// Ref: Fujitsu SPARC64 VII Processor
// Section 4 Cache System
_L1_data_cache_line_size = 64;
}
// UseVIS is set to the smallest of what hardware supports and what // UseVIS is set to the smallest of what hardware supports and what
// the command line requires. I.e., you cannot set UseVIS to 3 on // the command line requires. I.e., you cannot set UseVIS to 3 on
// older UltraSparc which do not support it. // older UltraSparc which do not support it.
@ -401,6 +359,7 @@ void VM_Version::initialize() {
#ifndef PRODUCT #ifndef PRODUCT
if (PrintMiscellaneous && Verbose) { if (PrintMiscellaneous && Verbose) {
tty->print_cr("L1 data cache line size: %u", L1_data_cache_line_size()); tty->print_cr("L1 data cache line size: %u", L1_data_cache_line_size());
tty->print_cr("L2 cache line size: %u", L2_cache_line_size());
tty->print("Allocation"); tty->print("Allocation");
if (AllocatePrefetchStyle <= 0) { if (AllocatePrefetchStyle <= 0) {
tty->print_cr(": no prefetching"); tty->print_cr(": no prefetching");

View file

@ -96,6 +96,9 @@ protected:
static int _features; static int _features;
static const char* _features_str; static const char* _features_str;
static unsigned int _L2_cache_line_size;
static unsigned int L2_cache_line_size() { return _L2_cache_line_size; }
static void print_features(); static void print_features();
static int determine_features(); static int determine_features();
static int platform_features(int features); static int platform_features(int features);
@ -167,9 +170,8 @@ public:
static const char* cpu_features() { return _features_str; } static const char* cpu_features() { return _features_str; }
static intx prefetch_data_size() { // default prefetch block size on sparc
return is_T4() && !is_T7() ? 32 : 64; // default prefetch block size on sparc static intx prefetch_data_size() { return L2_cache_line_size(); }
}
// Prefetch // Prefetch
static intx prefetch_copy_interval_in_bytes() { static intx prefetch_copy_interval_in_bytes() {

View file

@ -28,10 +28,140 @@
#include "runtime/os.hpp" #include "runtime/os.hpp"
#include "vm_version_sparc.hpp" #include "vm_version_sparc.hpp"
# include <sys/auxv.h> #include <sys/auxv.h>
# include <sys/auxv_SPARC.h> #include <sys/auxv_SPARC.h>
# include <sys/systeminfo.h> #include <sys/systeminfo.h>
# include <kstat.h> #include <kstat.h>
#include <picl.h>
extern "C" static int PICL_get_l1_data_cache_line_size_helper(picl_nodehdl_t nodeh, void *result);
extern "C" static int PICL_get_l2_cache_line_size_helper(picl_nodehdl_t nodeh, void *result);
class PICL {
// Get a value of the integer property. The value in the tree can be either 32 or 64 bit
// depending on the platform. The result is converted to int.
static int get_int_property(picl_nodehdl_t nodeh, const char* name, int* result) {
picl_propinfo_t pinfo;
picl_prophdl_t proph;
if (picl_get_prop_by_name(nodeh, name, &proph) != PICL_SUCCESS ||
picl_get_propinfo(proph, &pinfo) != PICL_SUCCESS) {
return PICL_FAILURE;
}
if (pinfo.type != PICL_PTYPE_INT && pinfo.type != PICL_PTYPE_UNSIGNED_INT) {
assert(false, "Invalid property type");
return PICL_FAILURE;
}
if (pinfo.size == sizeof(int64_t)) {
int64_t val;
if (picl_get_propval(proph, &val, sizeof(int64_t)) != PICL_SUCCESS) {
return PICL_FAILURE;
}
*result = static_cast<int>(val);
} else if (pinfo.size == sizeof(int32_t)) {
int32_t val;
if (picl_get_propval(proph, &val, sizeof(int32_t)) != PICL_SUCCESS) {
return PICL_FAILURE;
}
*result = static_cast<int>(val);
} else {
assert(false, "Unexpected integer property size");
return PICL_FAILURE;
}
return PICL_SUCCESS;
}
// Visitor and a state machine that visits integer properties and verifies that the
// values are the same. Stores the unique value observed.
class UniqueValueVisitor {
enum {
INITIAL, // Start state, no assignments happened
ASSIGNED, // Assigned a value
INCONSISTENT // Inconsistent value seen
} _state;
int _value;
public:
UniqueValueVisitor() : _state(INITIAL) { }
int value() {
assert(_state == ASSIGNED, "Precondition");
return _value;
}
void set_value(int value) {
assert(_state == INITIAL, "Precondition");
_value = value;
_state = ASSIGNED;
}
bool is_initial() { return _state == INITIAL; }
bool is_assigned() { return _state == ASSIGNED; }
bool is_inconsistent() { return _state == INCONSISTENT; }
void set_inconsistent() { _state = INCONSISTENT; }
static int visit(picl_nodehdl_t nodeh, const char* name, void *arg) {
UniqueValueVisitor *state = static_cast<UniqueValueVisitor*>(arg);
assert(!state->is_inconsistent(), "Precondition");
int curr;
if (PICL::get_int_property(nodeh, name, &curr) == PICL_SUCCESS) {
if (!state->is_assigned()) { // first iteration
state->set_value(curr);
} else if (curr != state->value()) { // following iterations
state->set_inconsistent();
}
}
if (state->is_inconsistent()) {
return PICL_WALK_TERMINATE;
}
return PICL_WALK_CONTINUE;
}
};
int _L1_data_cache_line_size;
int _L2_cache_line_size;
public:
static int get_l1_data_cache_line_size(picl_nodehdl_t nodeh, void *state) {
return UniqueValueVisitor::visit(nodeh, "l1-dcache-line-size", state);
}
static int get_l2_cache_line_size(picl_nodehdl_t nodeh, void *state) {
return UniqueValueVisitor::visit(nodeh, "l2-cache-line-size", state);
}
PICL() : _L1_data_cache_line_size(0), _L2_cache_line_size(0) {
if (picl_initialize() == PICL_SUCCESS) {
picl_nodehdl_t rooth;
if (picl_get_root(&rooth) == PICL_SUCCESS) {
UniqueValueVisitor L1_state;
// Visit all "cpu" class instances
picl_walk_tree_by_class(rooth, "cpu", &L1_state, PICL_get_l1_data_cache_line_size_helper);
if (L1_state.is_initial()) { // Still initial, iteration found no values
// Try walk all "core" class instances, it might be a Fujitsu machine
picl_walk_tree_by_class(rooth, "core", &L1_state, PICL_get_l1_data_cache_line_size_helper);
}
if (L1_state.is_assigned()) { // Is there a value?
_L1_data_cache_line_size = L1_state.value();
}
UniqueValueVisitor L2_state;
picl_walk_tree_by_class(rooth, "cpu", &L2_state, PICL_get_l2_cache_line_size_helper);
if (L2_state.is_initial()) {
picl_walk_tree_by_class(rooth, "core", &L2_state, PICL_get_l2_cache_line_size_helper);
}
if (L2_state.is_assigned()) {
_L2_cache_line_size = L2_state.value();
}
}
picl_shutdown();
}
}
unsigned int L1_data_cache_line_size() const { return _L1_data_cache_line_size; }
unsigned int L2_cache_line_size() const { return _L2_cache_line_size; }
};
extern "C" static int PICL_get_l1_data_cache_line_size_helper(picl_nodehdl_t nodeh, void *result) {
return PICL::get_l1_data_cache_line_size(nodeh, result);
}
extern "C" static int PICL_get_l2_cache_line_size_helper(picl_nodehdl_t nodeh, void *result) {
return PICL::get_l2_cache_line_size(nodeh, result);
}
// We need to keep these here as long as we have to build on Solaris // We need to keep these here as long as we have to build on Solaris
// versions before 10. // versions before 10.
@ -211,5 +341,10 @@ int VM_Version::platform_features(int features) {
kstat_close(kc); kstat_close(kc);
} }
// Figure out cache line sizes using PICL
PICL picl;
_L1_data_cache_line_size = picl.L1_data_cache_line_size();
_L2_cache_line_size = picl.L2_cache_line_size();
return features; return features;
} }