chiark / gitweb /
1.0.6 update: Memory mapping features and stability fixes
authornick.j.sanders <nick.j.sanders@93e54ea4-8218-11de-8aaf-8d8425684b44>
Wed, 9 Jan 2013 09:10:08 +0000 (09:10 +0000)
committernick.j.sanders <nick.j.sanders@93e54ea4-8218-11de-8aaf-8d8425684b44>
Wed, 9 Jan 2013 09:10:08 +0000 (09:10 +0000)
Allow mapping of dram chips based on simple memory controllers
Fix memory corruption caused by define include bug on some toolchains

This patch adds a primitive mechanism to map physical addresses to memory
chips/modules in stressapptest. It assumes that the memory controller
uses channel interleaving of equal-sized chunks in a simple round-robin
fashion (like recent Intel chipsets in dual-channel mode seem to do).

The chunk size and the amount of memory channels must be configured on the
command line. In addition, you can supply a comma-separated list of
chip/module names per memory channel, between which the bytes of an
aligned machine word are evenly distributed. If you want to disable
this sub-feature and only detect channel interleaving, you can just
provide only one name per channel.

Signed-off-by: Julius Werner <jwerner@chromium.org>
Add autoconf support for pthread barriers, substituting for POSIX defines.

Signed-off-by: Nick Sanders <nsanders@chromium.org>
configure
configure.ac
src/os.cc
src/os.h
src/sat.cc
src/sat.h
src/worker.cc
src/worker.h

index 3f27d497a4d112ac4893c39665fa69e66c1f2394..12bc16b49c24c1ddd85508bdff0d452fbfa6f9b8 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.65 for stressapptest 1.0.4_autoconf.
+# Generated by GNU Autoconf 2.65 for stressapptest 1.0.6_autoconf.
 #
 # Report bugs to <opensource@google.com>.
 #
@@ -552,8 +552,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='stressapptest'
 PACKAGE_TARNAME='stressapptest'
-PACKAGE_VERSION='1.0.4_autoconf'
-PACKAGE_STRING='stressapptest 1.0.4_autoconf'
+PACKAGE_VERSION='1.0.6_autoconf'
+PACKAGE_STRING='stressapptest 1.0.6_autoconf'
 PACKAGE_BUGREPORT='opensource@google.com'
 PACKAGE_URL=''
 
@@ -1255,7 +1255,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures stressapptest 1.0.4_autoconf to adapt to many kinds of systems.
+\`configure' configures stressapptest 1.0.6_autoconf to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1326,7 +1326,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of stressapptest 1.0.4_autoconf:";;
+     short | recursive ) echo "Configuration of stressapptest 1.0.6_autoconf:";;
    esac
   cat <<\_ACEOF
 
@@ -1420,7 +1420,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-stressapptest configure 1.0.4_autoconf
+stressapptest configure 1.0.6_autoconf
 generated by GNU Autoconf 2.65
 
 Copyright (C) 2009 Free Software Foundation, Inc.
@@ -1976,7 +1976,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by stressapptest $as_me 1.0.4_autoconf, which was
+It was created by stressapptest $as_me 1.0.6_autoconf, which was
 generated by GNU Autoconf 2.65.  Invocation command line was
 
   $ $0 $@
@@ -2974,7 +2974,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='stressapptest'
- VERSION='1.0.4_autoconf'
+ VERSION='1.0.6_autoconf'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -5201,6 +5201,49 @@ if test "$ac_res" != no; then :
 fi
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_barrier" >&5
+$as_echo_n "checking for pthread_barrier... " >&6; }
+if test "${ac_cv_func_pthread_barrier+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test "$cross_compiling" = yes; then :
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error "cannot run test program while cross compiling
+See \`config.log' for more details." "$LINENO" 5; }
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+      #include <pthread.h>
+      int main(void)
+      {
+        pthread_barrier_t t;
+        return 0;
+      }
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  ac_cv_func_pthread_barrier=yes
+else
+  ac_cv_func_pthread_barrier=no
+
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_pthread_barrier" >&5
+$as_echo "$ac_cv_func_pthread_barrier" >&6; }
+if test "$ac_cv_func_pthread_barrier" = "yes"; then
+
+$as_echo "#define HAVE_PTHREAD_BARRIER 1" >>confdefs.h
+
+fi
+
 # Checks for typedefs, structures, and compiler characteristics.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5
 $as_echo_n "checking for stdbool.h that conforms to C99... " >&6; }
@@ -6455,7 +6498,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by stressapptest $as_me 1.0.4_autoconf, which was
+This file was extended by stressapptest $as_me 1.0.6_autoconf, which was
 generated by GNU Autoconf 2.65.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -6521,7 +6564,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-stressapptest config.status 1.0.4_autoconf
+stressapptest config.status 1.0.6_autoconf
 configured by $0, generated by GNU Autoconf 2.65,
   with options \\"\$ac_cs_config\\"
 
index 80649a7e61c2ba5aa7ff1a814700875e926c4818..aba87916ab131799125b9399a016a3ab0a6b1e9e 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.61)
-AC_INIT([stressapptest], [1.0.5_autoconf], [opensource@google.com])
+AC_INIT([stressapptest], [1.0.6_autoconf], [opensource@google.com])
 
 AC_ARG_WITH(static, [  --with-static            enable static linking])
 
@@ -112,6 +112,27 @@ AC_SEARCH_LIBS([io_setup], [aio])
 AC_CHECK_HEADERS([sys/shm.h])
 AC_SEARCH_LIBS([shm_open], [rt])
 
+AC_MSG_CHECKING(for pthread_barrier)
+AC_CACHE_VAL(
+  ac_cv_func_pthread_barrier,
+  AC_TRY_RUN(
+    [
+      #include <pthread.h>
+      int main(void)
+      {
+        pthread_barrier_t t;
+        return 0;
+      }
+    ],
+    ac_cv_func_pthread_barrier=yes,
+    ac_cv_func_pthread_barrier=no
+  )
+)
+AC_MSG_RESULT($ac_cv_func_pthread_barrier)
+if test "$ac_cv_func_pthread_barrier" = "yes"; then
+  AC_DEFINE(HAVE_PTHREAD_BARRIER, [1], [Define to 1 if the system has `pthread_barrier'.])
+fi
+
 # Checks for typedefs, structures, and compiler characteristics.
 AC_HEADER_STDBOOL
 AC_C_CONST
index 225e86cac61ce3ff2b2332cca776c34b77d3b3ae..b73b18c3048fd67bcd7f3f72d8825fe5522d7c38 100644 (file)
--- a/src/os.cc
+++ b/src/os.cc
@@ -132,10 +132,14 @@ uint64 OsLayer::VirtualToPhysical(void *vaddr) {
   uint64 frame, shift;
   off64_t off = ((uintptr_t)vaddr) / getpagesize() * 8;
   int fd = open(kPagemapPath, O_RDONLY);
-  if (fd < 0 || lseek64(fd, off, SEEK_SET) != off || read(fd, &frame, 8) != 8) {
+  // /proc/self/pagemap is available in kernel >= 2.6.25
+  if (fd < 0)
+    return 0;
+
+  if (lseek64(fd, off, SEEK_SET) != off || read(fd, &frame, 8) != 8) {
     int err = errno;
     string errtxt = ErrorString(err);
-    logprintf(0, "Error: failed to access %s with errno %d (%s)\n",
+    logprintf(0, "Process Error: failed to access %s with errno %d (%s)\n",
               kPagemapPath, err, errtxt.c_str());
     if (fd >= 0)
       close(fd);
@@ -257,12 +261,31 @@ bool OsLayer::AdlerMemcpyWarm(uint64 *dstmem, uint64 *srcmem,
 }
 
 
-// Translate user virtual to physical address.
+// Translate physical address to memory module name.
+// Assumes simple round-robin interleaving between memory channels of
+// 'interleave_size_' sized chunks, with repeated 'channel_width_'
+// blocks with bits distributed from each chip in that channel.
 int OsLayer::FindDimm(uint64 addr, char *buf, int len) {
-  char tmpbuf[256];
-  snprintf(tmpbuf, sizeof(tmpbuf), "DIMM Unknown");
-  snprintf(buf, len, "%s", tmpbuf);
-  return 0;
+  static const string unknown = "DIMM Unknown";
+  if (!modules_) {
+    snprintf(buf, len, "%s", unknown.c_str());
+    return 0;
+  }
+
+  // Find channel by counting interleave units (typically cachelines),
+  // and mod by number of channels.
+  vector<string>& channel = (*modules_)[
+      (addr / interleave_size_) % modules_->size()];
+
+  // Find dram chip by finding which byte within the channel
+  // by address mod channel width, then divide the channel
+  // evenly among the listed dram chips. Note, this will not work
+  // with x4 dram.
+  int chip = (addr % (channel_width_ / 8)) /
+             ((channel_width_ / 8) / channel.size());
+  string name = channel[chip];
+  snprintf(buf, len, "%s", name.c_str());
+  return 1;
 }
 
 
index 3f3d3e97aca86414377e26ff0a13356070b27707..86e0c4ec7153e1cff923f24e55f09f3e9c35762b 100644 (file)
--- a/src/os.h
+++ b/src/os.h
@@ -57,6 +57,14 @@ class OsLayer {
     min_hugepages_bytes_ = min_bytes;
   }
 
+  // Set parameters needed to translate physical address to memory module.
+  void SetDramMappingParams(int interleave_size, int channel_width,
+                            vector< vector<string> > *modules) {
+    interleave_size_ = interleave_size;
+    channel_width_ = channel_width;
+    modules_ = modules;
+  }
+
   // Initializes data strctures and open files.
   // Returns false on error.
   virtual bool Initialize();
@@ -261,6 +269,9 @@ class OsLayer {
   bool  use_posix_shm_;          // Use 4k page shmem?
   bool  dynamic_mapped_shmem_;   // Conserve virtual address space.
   int   shmid_;                  // Handle to shmem
+  vector< vector<string> > *modules_;  // Memory module names per channel.
+  int interleave_size_;          // Channel interleaving chunk size.
+  int channel_width_;            // Channel width in bits.
 
   int64 regionsize_;             // Size of memory "regions"
   int   regioncount_;            // Number of memory "regions"
index ede951da917d389f8b3484d3fcf0a1123d885078..0679ea11a4e593bae35ff78803a85d8d175ae4b1 100644 (file)
@@ -488,13 +488,13 @@ bool Sat::InitializePages() {
   for (int64 i = 0; i < pages_; i++) {
     struct page_entry pe;
     // Only get valid pages with uninitialized tags here.
-    char buf[256];
     if (GetValid(&pe, kInvalidTag)) {
       int64 paddr = os_->VirtualToPhysical(pe.addr);
       int32 region = os_->FindRegion(paddr);
 
-      os_->FindDimm(paddr, buf, sizeof(buf));
       if (i < 256) {
+        char buf[256];
+        os_->FindDimm(paddr, buf, sizeof(buf));
         logprintf(12, "Log: address: %#llx, %s\n", paddr, buf);
       }
       region_[region]++;
@@ -572,6 +572,13 @@ bool Sat::Initialize() {
 
   if (min_hugepages_mbytes_ > 0)
     os_->SetMinimumHugepagesSize(min_hugepages_mbytes_ * kMegabyte);
+  if (modules_.size() > 0) {
+    logprintf(6, "Log: Decoding memory: %dx%d bit channels,"
+        " %d byte burst size, %d modules per channel (x%d)\n",
+        modules_.size(), channel_width_, interleave_size_, modules_[0].size(),
+        channel_width_/modules_[0].size());
+    os_->SetDramMappingParams(interleave_size_, channel_width_, &modules_);
+  }
 
   if (!os_->Initialize()) {
     logprintf(0, "Process Error: Failed to initialize OS layer\n");
@@ -643,6 +650,8 @@ Sat::Sat() {
   min_hugepages_mbytes_ = 0;
   freepages_ = 0;
   paddr_base_ = 0;
+  interleave_size_ = kCacheLineSize;
+  channel_width_ = 64;
 
   user_break_ = false;
   verbosity_ = 8;
@@ -918,6 +927,23 @@ bool Sat::ParseArgs(int argc, char **argv) {
       continue;
     }
 
+    ARG_IVALUE("--interleave_size", interleave_size_);
+    ARG_IVALUE("--channel_width", channel_width_);
+
+    if (!strcmp(argv[i], "--memory_channel")) {
+      i++;
+      if (i < argc) {
+        char *module = argv[i];
+        modules_.push_back(vector<string>());
+        while (char* next = strchr(module, ',')) {
+          modules_.back().push_back(string(module, next - module));
+          module = next + 1;
+        }
+        modules_.back().push_back(string(module));
+      }
+      continue;
+    }
+
     // Default:
     PrintVersion();
     PrintHelp();
@@ -963,6 +989,44 @@ bool Sat::ParseArgs(int argc, char **argv) {
       disk_pages_ = 1;
   }
 
+  // Validate memory channel parameters if supplied
+  if (modules_.size()) {
+    if (interleave_size_ <= 0 ||
+        interleave_size_ & (interleave_size_ - 1)) {
+      logprintf(6, "Process Error: "
+          "Interleave size %d is not a power of 2.\n", interleave_size_);
+      bad_status();
+      return false;
+    }
+    for (uint i = 0; i < modules_.size(); i++)
+      if (modules_[i].size() != modules_[0].size()) {
+        logprintf(6, "Process Error: "
+            "Channels 0 and %d have a different amount of modules.\n",i);
+        bad_status();
+        return false;
+      }
+    if (modules_[0].size() & (modules_[0].size() - 1)) {
+      logprintf(6, "Process Error: "
+          "Amount of modules per memory channel is not a power of 2.\n");
+      bad_status();
+      return false;
+    }
+    if (channel_width_ < 16
+        || channel_width_ & (channel_width_ - 1)) {
+      logprintf(6, "Process Error: "
+          "Channel width %d is invalid.\n", channel_width_);
+      bad_status();
+      return false;
+    }
+    if (channel_width_ / modules_[0].size() < 8) {
+      logprintf(6, "Process Error: "
+          "Chip width x%d must be x8 or greater.\n", channel_width_ / modules_[0].size());
+      bad_status();
+      return false;
+    }
+  }
+
+
   // Print each argument.
   for (int i = 0; i < argc; i++) {
     if (i)
@@ -1027,10 +1091,16 @@ void Sat::PrintHelp() {
          " --paddr_base     allocate memory starting from this address\n"
          " --pause_delay    delay (in seconds) between power spikes\n"
          " --pause_duration duration (in seconds) of each pause\n"
-         " --local_numa : choose memory regions associated with "
+         " --local_numa     choose memory regions associated with "
+         "each CPU to be tested by that CPU\n"
+         " --remote_numa    choose memory regions not associated with "
          "each CPU to be tested by that CPU\n"
-         " --remote_numa : choose memory regions not associated with "
-         "each CPU to be tested by that CPU\n");
+         " --interleave_size bytes  size in bytes of each channel's data as interleaved "
+         "between memory channels\n"
+         " --channel_width bits     width in bits of each memory channel\n"
+         " --memory_channel u1,u2   defines a comma-separated list of names\n"
+         "                          for dram packages in a memory channel.\n"
+         "                          Use multiple times to define multiple channels.\n");
 }
 
 bool Sat::CheckGoogleSpecificArgs(int argc, char **argv, int *i) {
index b48f51918650f6f3af4621cc7526588a1a1c2963..e867151a66de2920f163070414c0d39f621f8126 100644 (file)
--- a/src/sat.h
+++ b/src/sat.h
@@ -151,6 +151,10 @@ class Sat {
   int64 freepages_;                   // How many invalid pages we need.
   int disk_pages_;                    // Number of pages per temp file.
   uint64 paddr_base_;                 // Physical address base.
+  vector< vector<string> > modules_;  // Memory module names per channel.
+  int interleave_size_;               // Channel interleaving   chunk size in bytes.
+                                      // Usually cacheline sized.
+  int channel_width_;                 // Channel width in bits.
 
   // Control flags.
   volatile sig_atomic_t user_break_;  // User has signalled early exit.  Used as
index eddea6c12bb38a1ea6a3663eb803d047be94ef02..d24b5cd2c077b30881385871bd61d7bbced29e6b 100644 (file)
@@ -154,7 +154,7 @@ static void *ThreadSpawnerGeneric(void *ptr) {
 void WorkerStatus::Initialize() {
   sat_assert(0 == pthread_mutex_init(&num_workers_mutex_, NULL));
   sat_assert(0 == pthread_rwlock_init(&status_rwlock_, NULL));
-#ifdef _POSIX_BARRIERS
+#ifdef HAVE_PTHREAD_BARRIERS
   sat_assert(0 == pthread_barrier_init(&pause_barrier_, NULL,
                                        num_workers_ + 1));
 #endif
@@ -163,7 +163,7 @@ void WorkerStatus::Initialize() {
 void WorkerStatus::Destroy() {
   sat_assert(0 == pthread_mutex_destroy(&num_workers_mutex_));
   sat_assert(0 == pthread_rwlock_destroy(&status_rwlock_));
-#ifdef _POSIX_BARRIERS
+#ifdef HAVE_PTHREAD_BARRIERS
   sat_assert(0 == pthread_barrier_destroy(&pause_barrier_));
 #endif
 }
@@ -230,7 +230,7 @@ void WorkerStatus::RemoveSelf() {
   AcquireNumWorkersLock();
   // Decrement num_workers_ and reinitialize pause_barrier_, which we know isn't
   // in use because (status != PAUSE).
-#ifdef _POSIX_BARRIERS
+#ifdef HAVE_PTHREAD_BARRIERS
   sat_assert(0 == pthread_barrier_destroy(&pause_barrier_));
   sat_assert(0 == pthread_barrier_init(&pause_barrier_, NULL, num_workers_));
 #endif
index 0ec4c1d1fabef23c00104061ebc900f6d53f37dd..31e02258c9d0be4f05ed7b5d9ddbf96e1ef3fe4f 100644 (file)
@@ -140,7 +140,7 @@ class WorkerStatus {
   enum Status { RUN, PAUSE, STOP };
 
   void WaitOnPauseBarrier() {
-#ifdef _POSIX_BARRIERS
+#ifdef HAVE_PTHREAD_BARRIERS
     int error = pthread_barrier_wait(&pause_barrier_);
     if (error != PTHREAD_BARRIER_SERIAL_THREAD)
       sat_assert(error == 0);
@@ -189,7 +189,7 @@ class WorkerStatus {
   pthread_rwlock_t status_rwlock_;
   Status status_;
 
-#ifdef _POSIX_BARRIERS
+#ifdef HAVE_PTHREAD_BARRIERS
   // Guaranteed to not be in use when (status_ != PAUSE).
   pthread_barrier_t pause_barrier_;
 #endif