// Copyright 2006 Google Inc. All Rights Reserved.
|
// Author: nsanders, menderico
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
// you may not use this file except in compliance with the License.
|
// You may obtain a copy of the License at
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
// Unless required by applicable law or agreed to in writing, software
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// See the License for the specific language governing permissions and
|
// limitations under the License.
|
|
#ifndef STRESSAPPTEST_OS_H_ // NOLINT
|
#define STRESSAPPTEST_OS_H_
|
|
#include <dirent.h>
|
#include <unistd.h>
|
#include <sys/syscall.h>
|
|
#include <string>
|
#include <list>
|
#include <map>
|
#include <vector>
|
|
// This file must work with autoconf on its public version,
|
// so these includes are correct.
|
#include "adler32memcpy.h" // NOLINT
|
#include "sattypes.h" // NOLINT
|
#include "clock.h" // NOLINT
|
|
const char kPagemapPath[] = "/proc/self/pagemap";
|
|
struct PCIDevice {
|
int32 domain;
|
uint16 bus;
|
uint8 dev;
|
uint8 func;
|
uint16 vendor_id;
|
uint16 device_id;
|
uint64 base_addr[6];
|
uint64 size[6];
|
};
|
|
typedef vector<PCIDevice*> PCIDevices;
|
|
class ErrorDiag;
|
|
class Clock;
|
|
// This class implements OS/Platform specific funtions.
|
class OsLayer {
|
public:
|
OsLayer();
|
virtual ~OsLayer();
|
|
// Set the minimum amount of hugepages that should be available for testing.
|
// Must be set before Initialize().
|
void SetMinimumHugepagesSize(int64 min_bytes) {
|
min_hugepages_bytes_ = min_bytes;
|
}
|
|
// Set the minium amount of memory that should not be allocated. This only
|
// has any affect if hugepages are not used.
|
// Must be set before Initialize().
|
void SetReserveSize(int64 reserve_mb) {
|
reserve_mb_ = reserve_mb;
|
}
|
|
// Set parameters needed to translate physical address to memory module.
|
void SetDramMappingParams(uintptr_t channel_hash, int channel_width,
|
vector< vector<string> > *channels) {
|
channel_hash_ = channel_hash;
|
channel_width_ = channel_width;
|
channels_ = channels;
|
}
|
|
// Initializes data strctures and open files.
|
// Returns false on error.
|
virtual bool Initialize();
|
|
// Virtual to physical. This implementation is optional for
|
// subclasses to implement.
|
// Takes a pointer, and returns the corresponding bus address.
|
virtual uint64 VirtualToPhysical(void *vaddr);
|
|
// Prints failed dimm. This implementation is optional for
|
// subclasses to implement.
|
// Takes a bus address and string, and prints the DIMM name
|
// into the string. Returns the DIMM number that corresponds to the
|
// address given, or -1 if unable to identify the DIMM number.
|
// Note that subclass implementations of FindDimm() MUST fill
|
// buf with at LEAST one non-whitespace character (provided len > 0).
|
virtual int FindDimm(uint64 addr, char *buf, int len);
|
|
// Classifies addresses according to "regions"
|
// This may mean different things on different platforms.
|
virtual int32 FindRegion(uint64 paddr);
|
// Find cpu cores associated with a region. Either NUMA or arbitrary.
|
virtual cpu_set_t *FindCoreMask(int32 region);
|
// Return cpu cores associated with a region in a hex string.
|
virtual string FindCoreMaskFormat(int32 region);
|
|
// Returns the HD device that contains this file.
|
virtual string FindFileDevice(string filename);
|
|
// Returns a list of paths coresponding to HD devices found on this machine.
|
virtual list<string> FindFileDevices();
|
|
// Polls for errors. This implementation is optional.
|
// This will poll once for errors and return zero iff no errors were found.
|
virtual int ErrorPoll();
|
|
// Delay an appropriate amount of time between polling.
|
virtual void ErrorWait();
|
|
// Report errors. This implementation is mandatory.
|
// This will output a machine readable line regarding the error.
|
virtual bool ErrorReport(const char *part, const char *symptom, int count);
|
|
// Flushes page cache. Used to circumvent the page cache when doing disk
|
// I/O. This will be a NOP until ActivateFlushPageCache() is called, which
|
// is typically done when opening a file with O_DIRECT fails.
|
// Returns false on error, true on success or NOP.
|
// Subclasses may implement this in machine specific ways..
|
virtual bool FlushPageCache(void);
|
// Enable FlushPageCache() to actually do the flush instead of being a NOP.
|
virtual void ActivateFlushPageCache(void);
|
|
// Flushes cacheline. Used to distinguish read or write errors.
|
// Subclasses may implement this in machine specific ways..
|
// Takes a pointer, and flushed the cacheline containing that pointer.
|
virtual void Flush(void *vaddr);
|
|
// Fast flush, for use in performance critical code.
|
// This is bound at compile time, and will not pick up
|
// any runtime machine configuration info.
|
inline static void FastFlush(void *vaddr) {
|
#ifdef STRESSAPPTEST_CPU_PPC
|
asm volatile("dcbf 0,%0; sync" : : "r" (vaddr));
|
#elif defined(STRESSAPPTEST_CPU_X86_64) || defined(STRESSAPPTEST_CPU_I686)
|
// Put mfence before and after clflush to make sure:
|
// 1. The write before the clflush is committed to memory bus;
|
// 2. The read after the clflush is hitting the memory bus.
|
//
|
// From Intel manual:
|
// CLFLUSH is only ordered by the MFENCE instruction. It is not guaranteed
|
// to be ordered by any other fencing, serializing or other CLFLUSH
|
// instruction. For example, software can use an MFENCE instruction to
|
// insure that previous stores are included in the write-back.
|
asm volatile("mfence");
|
asm volatile("clflush (%0)" : : "r" (vaddr));
|
asm volatile("mfence");
|
#elif defined(STRESSAPPTEST_CPU_ARMV7A) && !defined(__aarch64__)
|
// ARMv7a cachelines are 8 words (32 bytes).
|
syscall(__ARM_NR_cacheflush, vaddr, reinterpret_cast<char*>(vaddr) + 32, 0);
|
#else
|
#warning "Unsupported CPU type: Unable to force cache flushes."
|
#endif
|
}
|
|
// Fast flush, for use in performance critical code.
|
// This is bound at compile time, and will not pick up
|
// any runtime machine configuration info. Takes a NULL-terminated
|
// array of addresses to flush.
|
inline static void FastFlushList(void **vaddrs) {
|
#ifdef STRESSAPPTEST_CPU_PPC
|
while (*vaddrs) {
|
asm volatile("dcbf 0,%0" : : "r" (*vaddrs++));
|
}
|
asm volatile("sync");
|
#elif defined(STRESSAPPTEST_CPU_X86_64) || defined(STRESSAPPTEST_CPU_I686)
|
// Put mfence before and after clflush to make sure:
|
// 1. The write before the clflush is committed to memory bus;
|
// 2. The read after the clflush is hitting the memory bus.
|
//
|
// From Intel manual:
|
// CLFLUSH is only ordered by the MFENCE instruction. It is not guaranteed
|
// to be ordered by any other fencing, serializing or other CLFLUSH
|
// instruction. For example, software can use an MFENCE instruction to
|
// insure that previous stores are included in the write-back.
|
asm volatile("mfence");
|
while (*vaddrs) {
|
asm volatile("clflush (%0)" : : "r" (*vaddrs++));
|
}
|
asm volatile("mfence");
|
#elif defined(STRESSAPPTEST_CPU_ARMV7A)
|
while (*vaddrs) {
|
FastFlush(*vaddrs++);
|
}
|
#else
|
#warning "Unsupported CPU type: Unable to force cache flushes."
|
#endif
|
}
|
|
// Fast flush hint, for use in performance critical code.
|
// This is bound at compile time, and will not pick up
|
// any runtime machine configuration info. Note that this
|
// will not guarantee that a flush happens, but will at least
|
// hint that it should. This is useful for speeding up
|
// parallel march algorithms.
|
inline static void FastFlushHint(void *vaddr) {
|
#ifdef STRESSAPPTEST_CPU_PPC
|
asm volatile("dcbf 0,%0" : : "r" (vaddr));
|
#elif defined(STRESSAPPTEST_CPU_X86_64) || defined(STRESSAPPTEST_CPU_I686)
|
// From Intel manual:
|
// CLFLUSH is only ordered by the MFENCE instruction. It is not guaranteed
|
// to be ordered by any other fencing, serializing or other CLFLUSH
|
// instruction. For example, software can use an MFENCE instruction to
|
// insure that previous stores are included in the write-back.
|
asm volatile("clflush (%0)" : : "r" (vaddr));
|
#elif defined(STRESSAPPTEST_CPU_ARMV7A)
|
FastFlush(vaddr);
|
#else
|
#warning "Unsupported CPU type: Unable to force cache flushes."
|
#endif
|
}
|
|
// Fast flush, for use in performance critical code.
|
// This is bound at compile time, and will not pick up
|
// any runtime machine configuration info. Sync's any
|
// transactions for ordering FastFlushHints.
|
inline static void FastFlushSync() {
|
#ifdef STRESSAPPTEST_CPU_PPC
|
asm volatile("sync");
|
#elif defined(STRESSAPPTEST_CPU_X86_64) || defined(STRESSAPPTEST_CPU_I686)
|
// Put mfence before and after clflush to make sure:
|
// 1. The write before the clflush is committed to memory bus;
|
// 2. The read after the clflush is hitting the memory bus.
|
//
|
// From Intel manual:
|
// CLFLUSH is only ordered by the MFENCE instruction. It is not guaranteed
|
// to be ordered by any other fencing, serializing or other CLFLUSH
|
// instruction. For example, software can use an MFENCE instruction to
|
// insure that previous stores are included in the write-back.
|
asm volatile("mfence");
|
#elif defined(STRESSAPPTEST_CPU_ARMV7A)
|
// This is a NOP, FastFlushHint() always does a full flush, so there's
|
// nothing to do for FastFlushSync().
|
#else
|
#warning "Unsupported CPU type: Unable to force cache flushes."
|
#endif
|
}
|
|
// Get time in cpu timer ticks. Useful for matching MCEs with software
|
// actions.
|
inline static uint64 GetTimestamp(void) {
|
uint64 tsc;
|
#ifdef STRESSAPPTEST_CPU_PPC
|
uint32 tbl, tbu, temp;
|
__asm __volatile(
|
"1:\n"
|
"mftbu %2\n"
|
"mftb %0\n"
|
"mftbu %1\n"
|
"cmpw %2,%1\n"
|
"bne 1b\n"
|
: "=r"(tbl), "=r"(tbu), "=r"(temp)
|
:
|
: "cc");
|
|
tsc = (static_cast<uint64>(tbu) << 32) | static_cast<uint64>(tbl);
|
#elif defined(STRESSAPPTEST_CPU_X86_64) || defined(STRESSAPPTEST_CPU_I686)
|
datacast_t data;
|
__asm __volatile("rdtsc" : "=a" (data.l32.l), "=d"(data.l32.h));
|
tsc = data.l64;
|
#elif defined(STRESSAPPTEST_CPU_ARMV7A)
|
#warning "Unsupported CPU type ARMV7A: your timer may not function correctly"
|
tsc = 0;
|
#else
|
#warning "Unsupported CPU type: your timer may not function correctly"
|
tsc = 0;
|
#endif
|
return (tsc);
|
}
|
|
// Find the free memory on the machine.
|
virtual int64 FindFreeMemSize();
|
|
// Allocates test memory of length bytes.
|
// Subclasses must implement this.
|
// Call PepareTestMem to get a pointer.
|
virtual int64 AllocateAllMem(); // Returns length.
|
// Returns success.
|
virtual bool AllocateTestMem(int64 length, uint64 paddr_base);
|
virtual void FreeTestMem();
|
|
// Prepares the memory for use. You must call this
|
// before using test memory, and after you are done.
|
virtual void *PrepareTestMem(uint64 offset, uint64 length);
|
virtual void ReleaseTestMem(void *addr, uint64 offset, uint64 length);
|
|
// Machine type detected. Can we implement all these functions correctly?
|
// Returns true if machine type is detected and implemented.
|
virtual bool IsSupported();
|
|
// Returns 32 for 32-bit, 64 for 64-bit.
|
virtual int AddressMode();
|
// Update OsLayer state regarding cpu support for various features.
|
virtual void GetFeatures();
|
|
// Open, read, write pci cfg through /proc/bus/pci. fd is /proc/pci file.
|
virtual int PciOpen(int bus, int device, int function);
|
virtual void PciWrite(int fd, uint32 offset, uint32 value, int width);
|
virtual uint32 PciRead(int fd, uint32 offset, int width);
|
|
// Read MSRs
|
virtual bool ReadMSR(uint32 core, uint32 address, uint64 *data);
|
virtual bool WriteMSR(uint32 core, uint32 address, uint64 *data);
|
|
// Extract bits [n+len-1, n] from a 32 bit word.
|
// so GetBitField(0x0f00, 8, 4) == 0xf.
|
virtual uint32 GetBitField(uint32 val, uint32 n, uint32 len);
|
|
// Platform and CPU specific CPU-stressing function.
|
// Returns true on success, false otherwise.
|
virtual bool CpuStressWorkload();
|
|
// Causes false errors for unittesting.
|
// Setting to "true" causes errors to be injected.
|
void set_error_injection(bool errors) { error_injection_ = errors; }
|
bool error_injection() const { return error_injection_; }
|
|
// Is SAT using normal malloc'd memory, or exotic mmap'd memory.
|
bool normal_mem() const { return normal_mem_; }
|
|
// Get numa config, if available..
|
int num_nodes() const { return num_nodes_; }
|
int num_cpus() const { return num_cpus_; }
|
|
// Handle to platform-specific error diagnoser.
|
ErrorDiag *error_diagnoser_;
|
|
// Disambiguate between different "warm" memcopies.
|
virtual bool AdlerMemcpyWarm(uint64 *dstmem, uint64 *srcmem,
|
unsigned int size_in_bytes,
|
AdlerChecksum *checksum);
|
|
// Store a callback to use to print
|
// app-specific info about the last error location.
|
// This call back is called with a physical address, and the app can fill in
|
// the most recent transaction that occurred at that address.
|
typedef bool (*ErrCallback)(uint64 paddr, string *buf);
|
void set_err_log_callback(
|
ErrCallback err_log_callback) {
|
err_log_callback_ = err_log_callback;
|
}
|
ErrCallback get_err_log_callback() { return err_log_callback_; }
|
|
// Set a clock object that can be overridden for use with unit tests.
|
void SetClock(Clock *clock) {
|
if (clock_) {
|
delete clock_;
|
}
|
clock_ = clock;
|
time_initialized_ = clock_->Now();
|
}
|
|
protected:
|
void *testmem_; // Location of test memory.
|
uint64 testmemsize_; // Size of test memory.
|
int64 totalmemsize_; // Size of available memory.
|
int64 min_hugepages_bytes_; // Minimum hugepages size.
|
int64 reserve_mb_; // Minimum amount of memory to reserve in MB.
|
bool error_injection_; // Do error injection?
|
bool normal_mem_; // Memory DMA capable?
|
bool use_hugepages_; // Use hugepage shmem?
|
bool use_posix_shm_; // Use 4k page shmem?
|
bool dynamic_mapped_shmem_; // Conserve virtual address space.
|
bool mmapped_allocation_; // Was memory allocated using mmap()?
|
int shmid_; // Handle to shmem
|
vector< vector<string> > *channels_; // Memory module names per channel.
|
uint64 channel_hash_; // Mask of address bits XORed for channel.
|
int channel_width_; // Channel width in bits.
|
|
int64 regionsize_; // Size of memory "regions"
|
int regioncount_; // Number of memory "regions"
|
int num_cpus_; // Number of cpus in the system.
|
int num_nodes_; // Number of nodes in the system.
|
int num_cpus_per_node_; // Number of cpus per node in the system.
|
int address_mode_; // Are we running 32 or 64 bit?
|
bool has_vector_; // Do we have sse2/neon instructions?
|
bool has_clflush_; // Do we have clflush instructions?
|
bool use_flush_page_cache_; // Do we need to flush the page cache?
|
|
|
time_t time_initialized_; // Start time of test.
|
|
vector<cpu_set_t> cpu_sets_; // Cache for cpu masks.
|
vector<bool> cpu_sets_valid_; // If the cpu mask cache is valid.
|
|
// Get file descriptor for dev msr.
|
virtual int OpenMSR(uint32 core, uint32 address);
|
|
// Look up how many hugepages there are.
|
virtual int64 FindHugePages();
|
|
// Link to find last transaction at an error location.
|
ErrCallback err_log_callback_;
|
|
// Object to wrap the time function.
|
Clock *clock_;
|
|
private:
|
DISALLOW_COPY_AND_ASSIGN(OsLayer);
|
};
|
|
// Selects and returns the proper OS and hardware interface. Does not call
|
// OsLayer::Initialize() on the new object.
|
OsLayer *OsLayerFactory(const std::map<std::string, std::string> &options);
|
|
#endif // STRESSAPPTEST_OS_H_ NOLINT
|