8056124: Hotspot should use PICL interface to get cacheline size on SPARC
authoriveresov
Fri, 05 Sep 2014 11:23:47 -0700
changeset 26579 522d6486f410
parent 26578 1cb9e59a06ab
child 26580 8cca7fbe77b8
8056124: Hotspot should use PICL interface to get cacheline size on SPARC Summary: Using libpicl to get L1 data and L2 cache line sizes Reviewed-by: kvn, roland, morris
hotspot/make/solaris/makefiles/vm.make
hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp
hotspot/src/cpu/sparc/vm/vm_version_sparc.hpp
hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp
--- a/hotspot/make/solaris/makefiles/vm.make	Fri Sep 05 16:07:22 2014 +0200
+++ b/hotspot/make/solaris/makefiles/vm.make	Fri Sep 05 11:23:47 2014 -0700
@@ -143,7 +143,7 @@
 LIBS += -lsocket -lsched -ldl $(LIBM) -lthread -lc -ldemangle
 endif # sparcWorks
 
-LIBS += -lkstat
+LIBS += -lkstat -lpicl
 
 # By default, link the *.o into the library, not the executable.
 LINK_INTO$(LINK_INTO) = LIBJVM
--- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp	Fri Sep 05 16:07:22 2014 +0200
+++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp	Fri Sep 05 11:23:47 2014 -0700
@@ -32,6 +32,7 @@
 
 int VM_Version::_features = VM_Version::unknown_m;
 const char* VM_Version::_features_str = "";
+unsigned int VM_Version::_L2_cache_line_size = 0;
 
 void VM_Version::initialize() {
   _features = determine_features();
@@ -192,7 +193,7 @@
   }
 
   assert(BlockZeroingLowLimit > 0, "invalid value");
-  if (has_block_zeroing()) {
+  if (has_block_zeroing() && cache_line_size > 0) {
     if (FLAG_IS_DEFAULT(UseBlockZeroing)) {
       FLAG_SET_DEFAULT(UseBlockZeroing, true);
     }
@@ -202,7 +203,7 @@
   }
 
   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)) {
       FLAG_SET_DEFAULT(UseBlockCopy, true);
     }
@@ -252,49 +253,6 @@
   // buf is started with ", " or is empty
   _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
   // the command line requires.  I.e., you cannot set UseVIS to 3 on
   // older UltraSparc which do not support it.
@@ -401,6 +359,7 @@
 #ifndef PRODUCT
   if (PrintMiscellaneous && Verbose) {
     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");
     if (AllocatePrefetchStyle <= 0) {
       tty->print_cr(": no prefetching");
--- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.hpp	Fri Sep 05 16:07:22 2014 +0200
+++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.hpp	Fri Sep 05 11:23:47 2014 -0700
@@ -96,6 +96,9 @@
   static int  _features;
   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 int  determine_features();
   static int  platform_features(int features);
@@ -167,9 +170,8 @@
 
   static const char* cpu_features()     { return _features_str; }
 
-  static intx prefetch_data_size()  {
-    return is_T4() && !is_T7() ? 32 : 64;  // default prefetch block size on sparc
-  }
+  // default prefetch block size on sparc
+  static intx prefetch_data_size()      { return L2_cache_line_size();  }
 
   // Prefetch
   static intx prefetch_copy_interval_in_bytes() {
--- a/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp	Fri Sep 05 16:07:22 2014 +0200
+++ b/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp	Fri Sep 05 11:23:47 2014 -0700
@@ -28,10 +28,140 @@
 #include "runtime/os.hpp"
 #include "vm_version_sparc.hpp"
 
-# include <sys/auxv.h>
-# include <sys/auxv_SPARC.h>
-# include <sys/systeminfo.h>
-# include <kstat.h>
+#include <sys/auxv.h>
+#include <sys/auxv_SPARC.h>
+#include <sys/systeminfo.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
 // versions before 10.
@@ -211,5 +341,10 @@
     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;
 }