From ef1c9359bc14c3e90ed207c44d9a609d4b8654da Mon Sep 17 00:00:00 2001 From: "nick.j.sanders" Date: Wed, 9 Jan 2013 09:10:08 +0000 Subject: [PATCH] 1.0.6 update: Memory mapping features and stability fixes 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 Add autoconf support for pthread barriers, substituting for POSIX defines. Signed-off-by: Nick Sanders --- configure | 63 +++++++++++++++++++++++++++++++++------- configure.ac | 23 ++++++++++++++- src/os.cc | 37 +++++++++++++++++++----- src/os.h | 11 +++++++ src/sat.cc | 80 +++++++++++++++++++++++++++++++++++++++++++++++---- src/sat.h | 4 +++ src/worker.cc | 6 ++-- src/worker.h | 4 +-- 8 files changed, 200 insertions(+), 28 deletions(-) diff --git a/configure b/configure index 3f27d49..12bc16b 100755 --- 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 . # @@ -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 + 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\\" diff --git a/configure.ac b/configure.ac index 80649a7..aba8791 100644 --- a/configure.ac +++ b/configure.ac @@ -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 + 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 diff --git a/src/os.cc b/src/os.cc index 225e86c..b73b18c 100644 --- 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& 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; } diff --git a/src/os.h b/src/os.h index 3f3d3e9..86e0c4e 100644 --- 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 > *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 > *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" diff --git a/src/sat.cc b/src/sat.cc index ede951d..0679ea1 100644 --- a/src/sat.cc +++ b/src/sat.cc @@ -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()); + 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) { diff --git a/src/sat.h b/src/sat.h index b48f519..e867151 100644 --- 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 > 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 diff --git a/src/worker.cc b/src/worker.cc index eddea6c..d24b5cd 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -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 diff --git a/src/worker.h b/src/worker.h index 0ec4c1d..31e0225 100644 --- a/src/worker.h +++ b/src/worker.h @@ -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 -- 2.30.2