Scott Anderson | b0114cb | 2012-04-09 14:08:22 -0700 | [diff] [blame] | 1 | // Copyright 2006 Google Inc. All Rights Reserved. |
| 2 | // Author: nsanders, menderico |
| 3 | |
| 4 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | // you may not use this file except in compliance with the License. |
| 6 | // You may obtain a copy of the License at |
| 7 | |
| 8 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | // Unless required by applicable law or agreed to in writing, software |
| 11 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | // See the License for the specific language governing permissions and |
| 14 | // limitations under the License. |
| 15 | |
| 16 | #ifndef STRESSAPPTEST_OS_H_ // NOLINT |
| 17 | #define STRESSAPPTEST_OS_H_ |
| 18 | |
| 19 | #include <dirent.h> |
| 20 | #include <string> |
| 21 | #include <list> |
| 22 | #include <map> |
| 23 | #include <vector> |
| 24 | |
| 25 | // This file must work with autoconf on its public version, |
| 26 | // so these includes are correct. |
| 27 | #include "adler32memcpy.h" // NOLINT |
| 28 | #include "sattypes.h" // NOLINT |
| 29 | |
| 30 | const char kSysfsPath[] = "/sys/bus/pci/devices"; |
| 31 | |
| 32 | struct PCIDevice { |
| 33 | int32 domain; |
| 34 | uint16 bus; |
| 35 | uint8 dev; |
| 36 | uint8 func; |
| 37 | uint16 vendor_id; |
| 38 | uint16 device_id; |
| 39 | uint64 base_addr[6]; |
| 40 | uint64 size[6]; |
| 41 | }; |
| 42 | |
| 43 | typedef vector<PCIDevice*> PCIDevices; |
| 44 | |
| 45 | class ErrorDiag; |
| 46 | |
| 47 | // This class implements OS/Platform specific funtions. |
| 48 | class OsLayer { |
| 49 | public: |
| 50 | OsLayer(); |
| 51 | virtual ~OsLayer(); |
| 52 | |
| 53 | // Set the minimum amount of hugepages that should be available for testing. |
| 54 | // Must be set before Initialize(). |
| 55 | void SetMinimumHugepagesSize(int64 min_bytes) { |
| 56 | min_hugepages_bytes_ = min_bytes; |
| 57 | } |
| 58 | |
| 59 | // Initializes data strctures and open files. |
| 60 | // Returns false on error. |
| 61 | virtual bool Initialize(); |
| 62 | |
| 63 | // Virtual to physical. This implementation is optional for |
| 64 | // subclasses to implement. |
| 65 | // Takes a pointer, and returns the corresponding bus address. |
| 66 | virtual uint64 VirtualToPhysical(void *vaddr); |
| 67 | |
| 68 | // Prints failed dimm. This implementation is optional for |
| 69 | // subclasses to implement. |
| 70 | // Takes a bus address and string, and prints the DIMM name |
| 71 | // into the string. Returns error status. |
| 72 | virtual int FindDimm(uint64 addr, char *buf, int len); |
| 73 | // Print dimm info, plus more available info. |
| 74 | virtual int FindDimmExtended(uint64 addr, char *buf, int len) { |
| 75 | return FindDimm(addr, buf, len); |
| 76 | } |
| 77 | |
| 78 | |
| 79 | // Classifies addresses according to "regions" |
| 80 | // This may mean different things on different platforms. |
| 81 | virtual int32 FindRegion(uint64 paddr); |
| 82 | // Find cpu cores associated with a region. Either NUMA or arbitrary. |
| 83 | virtual cpu_set_t *FindCoreMask(int32 region); |
| 84 | // Return cpu cores associated with a region in a hex string. |
| 85 | virtual string FindCoreMaskFormat(int32 region); |
| 86 | |
| 87 | // Returns the HD device that contains this file. |
| 88 | virtual string FindFileDevice(string filename); |
| 89 | |
| 90 | // Returns a list of paths coresponding to HD devices found on this machine. |
| 91 | virtual list<string> FindFileDevices(); |
| 92 | |
| 93 | // Polls for errors. This implementation is optional. |
| 94 | // This will poll once for errors and return zero iff no errors were found. |
| 95 | virtual int ErrorPoll(); |
| 96 | |
| 97 | // Delay an appropriate amount of time between polling. |
| 98 | virtual void ErrorWait(); |
| 99 | |
| 100 | // Report errors. This implementation is mandatory. |
| 101 | // This will output a machine readable line regarding the error. |
| 102 | virtual bool ErrorReport(const char *part, const char *symptom, int count); |
| 103 | |
Scott Anderson | 613ee1f | 2012-04-24 16:35:57 -0700 | [diff] [blame] | 104 | // Flushes page cache. Used to circumvent the page cache when doing disk |
| 105 | // I/O. This will be a NOP until ActivateFlushPageCache() is called, which |
| 106 | // is typically done when opening a file with O_DIRECT fails. |
| 107 | // Returns false on error, true on success or NOP. |
| 108 | // Subclasses may implement this in machine specific ways.. |
| 109 | virtual bool FlushPageCache(void); |
| 110 | // Enable FlushPageCache() to actually do the flush instead of being a NOP. |
| 111 | virtual void ActivateFlushPageCache(void); |
| 112 | |
Scott Anderson | b0114cb | 2012-04-09 14:08:22 -0700 | [diff] [blame] | 113 | // Flushes cacheline. Used to distinguish read or write errors. |
| 114 | // Subclasses may implement this in machine specific ways.. |
| 115 | // Takes a pointer, and flushed the cacheline containing that pointer. |
| 116 | virtual void Flush(void *vaddr); |
| 117 | |
| 118 | // Fast flush, for use in performance critical code. |
| 119 | // This is bound at compile time, and will not pick up |
| 120 | // any runtime machine configuration info. |
| 121 | inline static void FastFlush(void *vaddr) { |
| 122 | #ifdef STRESSAPPTEST_CPU_PPC |
| 123 | asm volatile("dcbf 0,%0; sync" : : "r" (vaddr)); |
| 124 | #elif defined(STRESSAPPTEST_CPU_X86_64) || defined(STRESSAPPTEST_CPU_I686) |
| 125 | // Put mfence before and after clflush to make sure: |
| 126 | // 1. The write before the clflush is committed to memory bus; |
| 127 | // 2. The read after the clflush is hitting the memory bus. |
| 128 | // |
| 129 | // From Intel manual: |
| 130 | // CLFLUSH is only ordered by the MFENCE instruction. It is not guaranteed |
| 131 | // to be ordered by any other fencing, serializing or other CLFLUSH |
| 132 | // instruction. For example, software can use an MFENCE instruction to |
| 133 | // insure that previous stores are included in the write-back. |
| 134 | asm volatile("mfence"); |
| 135 | asm volatile("clflush (%0)" :: "r" (vaddr)); |
| 136 | asm volatile("mfence"); |
| 137 | #elif defined(STRESSAPPTEST_CPU_ARMV7A) |
| 138 | #warning "Unsupported CPU type ARMV7A: Unable to force cache flushes." |
| 139 | #else |
| 140 | #warning "Unsupported CPU type: Unable to force cache flushes." |
| 141 | #endif |
| 142 | } |
| 143 | |
| 144 | // Get time in cpu timer ticks. Useful for matching MCEs with software |
| 145 | // actions. |
| 146 | inline static uint64 GetTimestamp(void) { |
| 147 | uint64 tsc; |
| 148 | #ifdef STRESSAPPTEST_CPU_PPC |
| 149 | uint32 tbl, tbu, temp; |
| 150 | __asm __volatile( |
| 151 | "1:\n" |
| 152 | "mftbu %2\n" |
| 153 | "mftb %0\n" |
| 154 | "mftbu %1\n" |
| 155 | "cmpw %2,%1\n" |
| 156 | "bne 1b\n" |
| 157 | : "=r"(tbl), "=r"(tbu), "=r"(temp) |
| 158 | : |
| 159 | : "cc"); |
| 160 | |
| 161 | tsc = (static_cast<uint64>(tbu) << 32) | static_cast<uint64>(tbl); |
| 162 | #elif defined(STRESSAPPTEST_CPU_X86_64) || defined(STRESSAPPTEST_CPU_I686) |
| 163 | datacast_t data; |
| 164 | __asm __volatile("rdtsc" : "=a" (data.l32.l), "=d"(data.l32.h)); |
| 165 | tsc = data.l64; |
| 166 | #elif defined(STRESSAPPTEST_CPU_ARMV7A) |
| 167 | #warning "Unsupported CPU type ARMV7A: your build may not function correctly" |
| 168 | tsc = 0; |
| 169 | #else |
| 170 | #warning "Unsupported CPU type: your build may not function correctly" |
| 171 | tsc = 0; |
| 172 | #endif |
| 173 | return (tsc); |
| 174 | } |
| 175 | |
| 176 | // Find the free memory on the machine. |
| 177 | virtual int64 FindFreeMemSize(); |
| 178 | |
| 179 | // Allocates test memory of length bytes. |
| 180 | // Subclasses must implement this. |
| 181 | // Call PepareTestMem to get a pointer. |
| 182 | virtual int64 AllocateAllMem(); // Returns length. |
| 183 | // Returns success. |
| 184 | virtual bool AllocateTestMem(int64 length, uint64 paddr_base); |
| 185 | virtual void FreeTestMem(); |
| 186 | |
| 187 | // Prepares the memory for use. You must call this |
| 188 | // before using test memory, and after you are done. |
| 189 | virtual void *PrepareTestMem(uint64 offset, uint64 length); |
| 190 | virtual void ReleaseTestMem(void *addr, uint64 offset, uint64 length); |
| 191 | |
| 192 | // Machine type detected. Can we implement all these functions correctly? |
| 193 | // Returns true if machine type is detected and implemented. |
| 194 | virtual bool IsSupported(); |
| 195 | |
| 196 | // Returns 32 for 32-bit, 64 for 64-bit. |
| 197 | virtual int AddressMode(); |
| 198 | // Update OsLayer state regarding cpu support for various features. |
| 199 | virtual void GetFeatures(); |
| 200 | |
| 201 | // Open, read, write pci cfg through /proc/bus/pci. fd is /proc/pci file. |
| 202 | virtual int PciOpen(int bus, int device, int function); |
| 203 | virtual void PciWrite(int fd, uint32 offset, uint32 value, int width); |
| 204 | virtual uint32 PciRead(int fd, uint32 offset, int width); |
| 205 | |
| 206 | // Read MSRs |
| 207 | virtual bool ReadMSR(uint32 core, uint32 address, uint64 *data); |
| 208 | virtual bool WriteMSR(uint32 core, uint32 address, uint64 *data); |
| 209 | |
| 210 | // Extract bits [n+len-1, n] from a 32 bit word. |
| 211 | // so GetBitField(0x0f00, 8, 4) == 0xf. |
| 212 | virtual uint32 GetBitField(uint32 val, uint32 n, uint32 len); |
| 213 | |
| 214 | // Platform and CPU specific CPU-stressing function. |
| 215 | // Returns true on success, false otherwise. |
| 216 | virtual bool CpuStressWorkload(); |
| 217 | |
| 218 | // Causes false errors for unittesting. |
| 219 | // Setting to "true" causes errors to be injected. |
| 220 | void set_error_injection(bool errors) { error_injection_ = errors; } |
| 221 | bool error_injection() const { return error_injection_; } |
| 222 | |
| 223 | // Is SAT using normal malloc'd memory, or exotic mmap'd memory. |
| 224 | bool normal_mem() const { return normal_mem_; } |
| 225 | |
| 226 | // Get numa config, if available.. |
| 227 | int num_nodes() const { return num_nodes_; } |
| 228 | int num_cpus() const { return num_cpus_; } |
| 229 | |
| 230 | // Handle to platform-specific error diagnoser. |
| 231 | ErrorDiag *error_diagnoser_; |
| 232 | |
| 233 | // Detect all PCI Devices. |
| 234 | virtual PCIDevices GetPCIDevices(); |
| 235 | |
| 236 | // Disambiguate between different "warm" memcopies. |
| 237 | virtual bool AdlerMemcpyWarm(uint64 *dstmem, uint64 *srcmem, |
| 238 | unsigned int size_in_bytes, |
| 239 | AdlerChecksum *checksum); |
| 240 | |
| 241 | // Store a callback to use to print |
| 242 | // app-specific info about the last error location. |
| 243 | // This call back is called with a physical address, and the app can fill in |
| 244 | // the most recent transaction that occurred at that address. |
| 245 | typedef bool (*ErrCallback)(uint64 paddr, string *buf); |
| 246 | void set_err_log_callback( |
| 247 | ErrCallback err_log_callback) { |
| 248 | err_log_callback_ = err_log_callback; |
| 249 | } |
| 250 | ErrCallback get_err_log_callback() { return err_log_callback_; } |
| 251 | |
| 252 | protected: |
| 253 | void *testmem_; // Location of test memory. |
| 254 | uint64 testmemsize_; // Size of test memory. |
| 255 | int64 totalmemsize_; // Size of available memory. |
| 256 | int64 min_hugepages_bytes_; // Minimum hugepages size. |
| 257 | bool error_injection_; // Do error injection? |
| 258 | bool normal_mem_; // Memory DMA capable? |
| 259 | bool use_hugepages_; // Use hugepage shmem? |
| 260 | bool use_posix_shm_; // Use 4k page shmem? |
| 261 | bool dynamic_mapped_shmem_; // Conserve virtual address space. |
| 262 | int shmid_; // Handle to shmem |
| 263 | |
| 264 | int64 regionsize_; // Size of memory "regions" |
| 265 | int regioncount_; // Number of memory "regions" |
| 266 | int num_cpus_; // Number of cpus in the system. |
| 267 | int num_nodes_; // Number of nodes in the system. |
| 268 | int num_cpus_per_node_; // Number of cpus per node in the system. |
| 269 | int address_mode_; // Are we running 32 or 64 bit? |
| 270 | bool has_sse2_; // Do we have sse2 instructions? |
| 271 | bool has_clflush_; // Do we have clflush instructions? |
Scott Anderson | 613ee1f | 2012-04-24 16:35:57 -0700 | [diff] [blame] | 272 | bool use_flush_page_cache_; // Do we need to flush the page cache? |
Scott Anderson | b0114cb | 2012-04-09 14:08:22 -0700 | [diff] [blame] | 273 | |
| 274 | |
| 275 | time_t time_initialized_; // Start time of test. |
| 276 | |
| 277 | vector<cpu_set_t> cpu_sets_; // Cache for cpu masks. |
| 278 | vector<bool> cpu_sets_valid_; // If the cpu mask cache is valid. |
| 279 | |
| 280 | // Get file descriptor for dev msr. |
| 281 | virtual int OpenMSR(uint32 core, uint32 address); |
| 282 | // Auxiliary methods for PCI device configuration |
| 283 | int PCIGetValue(string name, string object); |
| 284 | int PCIGetResources(string name, PCIDevice *device); |
| 285 | |
| 286 | // Look up how many hugepages there are. |
| 287 | virtual int64 FindHugePages(); |
| 288 | |
| 289 | // Link to find last transaction at an error location. |
| 290 | ErrCallback err_log_callback_; |
| 291 | |
| 292 | private: |
| 293 | DISALLOW_COPY_AND_ASSIGN(OsLayer); |
| 294 | }; |
| 295 | |
| 296 | // Selects and returns the proper OS and hardware interface. Does not call |
| 297 | // OsLayer::Initialize() on the new object. |
| 298 | OsLayer *OsLayerFactory(const std::map<std::string, std::string> &options); |
| 299 | |
| 300 | #endif // STRESSAPPTEST_OS_H_ NOLINT |