// 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