chiark / gitweb /
63583984984f050b92c6e8c3ff935d9a8be39a0c
[stressapptest] / src / os.cc
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 // os.cc : os and machine specific implementation
17 // This file includes an abstracted interface
18 // for linux-distro specific and HW specific
19 // interfaces.
20
21 #include "os.h"
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <linux/types.h>
26 #include <malloc.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/mman.h>
31 #include <sys/ioctl.h>
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/ipc.h>
35 #ifdef HAVE_SYS_SHM_H
36 #include <sys/shm.h>
37 #endif
38 #include <unistd.h>
39
40 #ifndef SHM_HUGETLB
41 #define SHM_HUGETLB      04000  // remove when glibc defines it
42 #endif
43
44 #include <string>
45 #include <list>
46
47 // This file must work with autoconf on its public version,
48 // so these includes are correct.
49 #include "sattypes.h"
50 #include "error_diag.h"
51 #include "clock.h"
52
53 // OsLayer initialization.
54 OsLayer::OsLayer() {
55   testmem_ = 0;
56   testmemsize_ = 0;
57   totalmemsize_ = 0;
58   min_hugepages_bytes_ = 0;
59   reserve_mb_ = 0;
60   normal_mem_ = true;
61   use_hugepages_ = false;
62   use_posix_shm_ = false;
63   dynamic_mapped_shmem_ = false;
64   mmapped_allocation_ = false;
65   shmid_ = 0;
66
67   time_initialized_ = 0;
68
69   regionsize_ = 0;
70   regioncount_ = 1;
71   num_cpus_ = 0;
72   num_nodes_ = 0;
73   num_cpus_per_node_ = 0;
74   error_diagnoser_ = 0;
75   err_log_callback_ = 0;
76   error_injection_ = false;
77
78   void *pvoid = 0;
79   address_mode_ = sizeof(pvoid) * 8;
80
81   has_clflush_ = false;
82   has_sse2_ = false;
83
84   use_flush_page_cache_ = false;
85
86   clock_ = NULL;
87 }
88
89 // OsLayer cleanup.
90 OsLayer::~OsLayer() {
91   if (error_diagnoser_)
92     delete error_diagnoser_;
93   if (clock_)
94     delete clock_;
95 }
96
97 // OsLayer initialization.
98 bool OsLayer::Initialize() {
99   if (!clock_) {
100     clock_ = new Clock();
101   }
102
103   time_initialized_ = clock_->Now();
104   // Detect asm support.
105   GetFeatures();
106
107   if (num_cpus_ == 0) {
108     num_nodes_ = 1;
109     num_cpus_ = sysconf(_SC_NPROCESSORS_ONLN);
110     num_cpus_per_node_ = num_cpus_ / num_nodes_;
111   }
112   logprintf(5, "Log: %d nodes, %d cpus.\n", num_nodes_, num_cpus_);
113   sat_assert(CPU_SETSIZE >= num_cpus_);
114   cpu_sets_.resize(num_nodes_);
115   cpu_sets_valid_.resize(num_nodes_);
116   // Create error diagnoser.
117   error_diagnoser_ = new ErrorDiag();
118   if (!error_diagnoser_->set_os(this))
119     return false;
120   return true;
121 }
122
123 // Machine type detected. Can we implement all these functions correctly?
124 bool OsLayer::IsSupported() {
125   if (kOpenSource) {
126     // There are no explicitly supported systems in open source version.
127     return true;
128   }
129
130   // This is the default empty implementation.
131   // SAT won't report full error information.
132   return false;
133 }
134
135 int OsLayer::AddressMode() {
136   // Detect 32/64 bit binary.
137   void *pvoid = 0;
138   return sizeof(pvoid) * 8;
139 }
140
141 // Translates user virtual to physical address.
142 uint64 OsLayer::VirtualToPhysical(void *vaddr) {
143   uint64 frame, shift;
144   off64_t off = ((uintptr_t)vaddr) / sysconf(_SC_PAGESIZE) * 8;
145   int fd = open(kPagemapPath, O_RDONLY);
146   // /proc/self/pagemap is available in kernel >= 2.6.25
147   if (fd < 0)
148     return 0;
149
150   if (lseek64(fd, off, SEEK_SET) != off || read(fd, &frame, 8) != 8) {
151     int err = errno;
152     string errtxt = ErrorString(err);
153     logprintf(0, "Process Error: failed to access %s with errno %d (%s)\n",
154               kPagemapPath, err, errtxt.c_str());
155     if (fd >= 0)
156       close(fd);
157     return 0;
158   }
159   close(fd);
160   if (!(frame & (1LL << 63)) || (frame & (1LL << 62)))
161     return 0;
162   shift = (frame >> 55) & 0x3f;
163   frame = (frame & 0x007fffffffffffffLL) << shift;
164   return frame | ((uintptr_t)vaddr & ((1LL << shift) - 1));
165 }
166
167 // Returns the HD device that contains this file.
168 string OsLayer::FindFileDevice(string filename) {
169   return "hdUnknown";
170 }
171
172 // Returns a list of locations corresponding to HD devices.
173 list<string> OsLayer::FindFileDevices() {
174   // No autodetection on unknown systems.
175   list<string> locations;
176   return locations;
177 }
178
179
180 // Get HW core features from cpuid instruction.
181 void OsLayer::GetFeatures() {
182 #if defined(STRESSAPPTEST_CPU_X86_64) || defined(STRESSAPPTEST_CPU_I686)
183   unsigned int eax = 1, ebx, ecx, edx;
184   cpuid(&eax, &ebx, &ecx, &edx);
185   has_clflush_ = (edx >> 19) & 1;
186   has_sse2_ = (edx >> 26) & 1;
187
188   logprintf(9, "Log: has clflush: %s, has sse2: %s\n",
189             has_clflush_ ? "true" : "false",
190             has_sse2_ ? "true" : "false");
191 #elif defined(STRESSAPPTEST_CPU_PPC)
192   // All PPC implementations have cache flush instructions.
193   has_clflush_ = true;
194 #elif defined(STRESSAPPTEST_CPU_ARMV7A)
195 #warning "Unsupported CPU type ARMV7A: unable to determine feature set."
196 #else
197 #warning "Unsupported CPU type: unable to determine feature set."
198 #endif
199 }
200
201
202 // Enable FlushPageCache to be functional instead of a NOP.
203 void OsLayer::ActivateFlushPageCache(void) {
204   logprintf(9, "Log: page cache will be flushed as needed\n");
205   use_flush_page_cache_ = true;
206 }
207
208 // Flush the page cache to ensure reads come from the disk.
209 bool OsLayer::FlushPageCache(void) {
210   if (!use_flush_page_cache_)
211     return true;
212
213   // First, ask the kernel to write the cache to the disk.
214   sync();
215
216   // Second, ask the kernel to empty the cache by writing "1" to
217   // "/proc/sys/vm/drop_caches".
218   static const char *drop_caches_file = "/proc/sys/vm/drop_caches";
219   int dcfile = open(drop_caches_file, O_WRONLY);
220   if (dcfile < 0) {
221     int err = errno;
222     string errtxt = ErrorString(err);
223     logprintf(3, "Log: failed to open %s - err %d (%s)\n",
224               drop_caches_file, err, errtxt.c_str());
225     return false;
226   }
227
228   ssize_t bytes_written = write(dcfile, "1", 1);
229   close(dcfile);
230
231   if (bytes_written != 1) {
232     int err = errno;
233     string errtxt = ErrorString(err);
234     logprintf(3, "Log: failed to write %s - err %d (%s)\n",
235               drop_caches_file, err, errtxt.c_str());
236     return false;
237   }
238   return true;
239 }
240
241
242 // We need to flush the cacheline here.
243 void OsLayer::Flush(void *vaddr) {
244   // Use the generic flush. This function is just so we can override
245   // this if we are so inclined.
246   if (has_clflush_) {
247     OsLayer::FastFlush(vaddr);
248   }
249 }
250
251
252 // Run C or ASM copy as appropriate..
253 bool OsLayer::AdlerMemcpyWarm(uint64 *dstmem, uint64 *srcmem,
254                               unsigned int size_in_bytes,
255                               AdlerChecksum *checksum) {
256   if (has_sse2_) {
257     return AdlerMemcpyAsm(dstmem, srcmem, size_in_bytes, checksum);
258   } else {
259     return AdlerMemcpyWarmC(dstmem, srcmem, size_in_bytes, checksum);
260   }
261 }
262
263
264 // Translate physical address to memory module/chip name.
265 // Assumes interleaving between two memory channels based on the XOR of
266 // all address bits in the 'channel_hash' mask, with repeated 'channel_width_'
267 // blocks with bits distributed from each chip in that channel.
268 int OsLayer::FindDimm(uint64 addr, char *buf, int len) {
269   if (!channels_) {
270     snprintf(buf, len, "DIMM Unknown");
271     return -1;
272   }
273
274   // Find channel by XORing address bits in channel_hash mask.
275   uint32 low = static_cast<uint32>(addr & channel_hash_);
276   uint32 high = static_cast<uint32>((addr & channel_hash_) >> 32);
277   vector<string>& channel = (*channels_)[
278       __builtin_parity(high) ^ __builtin_parity(low)];
279
280   // Find dram chip by finding which byte within the channel
281   // by address mod channel width, then divide the channel
282   // evenly among the listed dram chips. Note, this will not work
283   // with x4 dram.
284   int chip = (addr % (channel_width_ / 8)) /
285              ((channel_width_ / 8) / channel.size());
286   string name = channel[chip];
287   snprintf(buf, len, "%s", name.c_str());
288   return 1;
289 }
290
291
292 // Classifies addresses according to "regions"
293 // This isn't really implemented meaningfully here..
294 int32 OsLayer::FindRegion(uint64 addr) {
295   static bool warned = false;
296
297   if (regionsize_ == 0) {
298     regionsize_ = totalmemsize_ / 8;
299     if (regionsize_ < 512 * kMegabyte)
300       regionsize_ = 512 * kMegabyte;
301     regioncount_ = totalmemsize_ / regionsize_;
302     if (regioncount_ < 1) regioncount_ = 1;
303   }
304
305   int32 region_num = addr / regionsize_;
306   if (region_num >= regioncount_) {
307     if (!warned) {
308         logprintf(0, "Log: region number %d exceeds region count %d\n",
309                   region_num, regioncount_);
310         warned = true;
311     }
312     region_num = region_num % regioncount_;
313   }
314   return region_num;
315 }
316
317 // Report which cores are associated with a given region.
318 cpu_set_t *OsLayer::FindCoreMask(int32 region) {
319   sat_assert(region >= 0);
320   region %= num_nodes_;
321   if (!cpu_sets_valid_[region]) {
322     CPU_ZERO(&cpu_sets_[region]);
323     for (int i = 0; i < num_cpus_per_node_; ++i) {
324       CPU_SET(i + region * num_cpus_per_node_, &cpu_sets_[region]);
325     }
326     cpu_sets_valid_[region] = true;
327     logprintf(5, "Log: Region %d mask 0x%s\n",
328                  region, FindCoreMaskFormat(region).c_str());
329   }
330   return &cpu_sets_[region];
331 }
332
333 // Return cores associated with a given region in hex string.
334 string OsLayer::FindCoreMaskFormat(int32 region) {
335   cpu_set_t* mask = FindCoreMask(region);
336   string format = cpuset_format(mask);
337   if (format.size() < 8)
338     format = string(8 - format.size(), '0') + format;
339   return format;
340 }
341
342 // Report an error in an easily parseable way.
343 bool OsLayer::ErrorReport(const char *part, const char *symptom, int count) {
344   time_t now = clock_->Now();
345   int ttf = now - time_initialized_;
346   if (strlen(symptom) && strlen(part)) {
347     logprintf(0, "Report Error: %s : %s : %d : %ds\n",
348               symptom, part, count, ttf);
349   } else {
350     // Log something so the error still shows up, but this won't break the
351     // parser.
352     logprintf(0, "Warning: Invalid Report Error: "
353               "%s : %s : %d : %ds\n", symptom, part, count, ttf);
354   }
355   return true;
356 }
357
358 // Read the number of hugepages out of the kernel interface in proc.
359 int64 OsLayer::FindHugePages() {
360   char buf[65] = "0";
361
362   // This is a kernel interface to query the numebr of hugepages
363   // available in the system.
364   static const char *hugepages_info_file = "/proc/sys/vm/nr_hugepages";
365   int hpfile = open(hugepages_info_file, O_RDONLY);
366
367   ssize_t bytes_read = read(hpfile, buf, 64);
368   close(hpfile);
369
370   if (bytes_read <= 0) {
371     logprintf(12, "Log: /proc/sys/vm/nr_hugepages "
372                   "read did not provide data\n");
373     return 0;
374   }
375
376   if (bytes_read == 64) {
377     logprintf(0, "Process Error: /proc/sys/vm/nr_hugepages "
378                  "is surprisingly large\n");
379     return 0;
380   }
381
382   // Add a null termintation to be string safe.
383   buf[bytes_read] = '\0';
384   // Read the page count.
385   int64 pages = strtoull(buf, NULL, 10);  // NOLINT
386
387   return pages;
388 }
389
390 int64 OsLayer::FindFreeMemSize() {
391   int64 size = 0;
392   int64 minsize = 0;
393   if (totalmemsize_ > 0)
394     return totalmemsize_;
395
396   int64 pages = sysconf(_SC_PHYS_PAGES);
397   int64 avpages = sysconf(_SC_AVPHYS_PAGES);
398   int64 pagesize = sysconf(_SC_PAGESIZE);
399   int64 physsize = pages * pagesize;
400   int64 avphyssize = avpages * pagesize;
401
402   // Assume 2MB hugepages.
403   int64 hugepagesize = FindHugePages() * 2 * kMegabyte;
404
405   if ((pages == -1) || (pagesize == -1)) {
406     logprintf(0, "Process Error: sysconf could not determine memory size.\n");
407     return 0;
408   }
409
410   // We want to leave enough stuff for things to run.
411   // If the user specified a minimum amount of memory to expect, require that.
412   // Otherwise, if more than 2GB is present, leave 192M + 5% for other stuff.
413   // If less than 2GB is present use 85% of what's available.
414   // These are fairly arbitrary numbers that seem to work OK.
415   //
416   // TODO(nsanders): is there a more correct way to determine target
417   // memory size?
418   if (hugepagesize > 0) {
419     if (min_hugepages_bytes_ > 0) {
420       minsize = min_hugepages_bytes_;
421     } else {
422       minsize = hugepagesize;
423     }
424   } else {
425     if (physsize < 2048LL * kMegabyte) {
426       minsize = ((pages * 85) / 100) * pagesize;
427     } else {
428       minsize = ((pages * 95) / 100) * pagesize - (192 * kMegabyte);
429     }
430     // Make sure that at least reserve_mb_ is left for the system.
431     if (reserve_mb_ > 0) {
432       int64 totalsize = pages * pagesize;
433       int64 reserve_kb = reserve_mb_ * kMegabyte;
434       if (reserve_kb > totalsize) {
435         logprintf(0, "Procedural Error: %lld is bigger than the total memory "
436                   "available %lld\n", reserve_kb, totalsize);
437       } else if (reserve_kb > totalsize - minsize) {
438         logprintf(5, "Warning: Overriding memory to use: original %lld, "
439                   "current %lld\n", minsize, totalsize - reserve_kb);
440         minsize = totalsize - reserve_kb;
441       }
442     }
443   }
444
445   // Use hugepage sizing if available.
446   if (hugepagesize > 0) {
447     if (hugepagesize < minsize) {
448       logprintf(0, "Procedural Error: Not enough hugepages. "
449                    "%lldMB available < %lldMB required.\n",
450                 hugepagesize / kMegabyte,
451                 minsize / kMegabyte);
452       // Require the calculated minimum amount of memory.
453       size = minsize;
454     } else {
455       // Require that we get all hugepages.
456       size = hugepagesize;
457     }
458   } else {
459     // Require the calculated minimum amount of memory.
460     size = minsize;
461   }
462
463   logprintf(5, "Log: Total %lld MB. Free %lld MB. Hugepages %lld MB. "
464                "Targeting %lld MB (%lld%%)\n",
465             physsize / kMegabyte,
466             avphyssize / kMegabyte,
467             hugepagesize / kMegabyte,
468             size / kMegabyte,
469             size * 100 / physsize);
470
471   totalmemsize_ = size;
472   return size;
473 }
474
475 // Allocates all memory available.
476 int64 OsLayer::AllocateAllMem() {
477   int64 length = FindFreeMemSize();
478   bool retval = AllocateTestMem(length, 0);
479   if (retval)
480     return length;
481   else
482     return 0;
483 }
484
485 // Allocate the target memory. This may be from malloc, hugepage pool
486 // or other platform specific sources.
487 bool OsLayer::AllocateTestMem(int64 length, uint64 paddr_base) {
488   // Try hugepages first.
489   void *buf = 0;
490
491   sat_assert(length >= 0);
492
493   if (paddr_base)
494     logprintf(0, "Process Error: non zero paddr_base %#llx is not supported,"
495               " ignore.\n", paddr_base);
496
497   // Determine optimal memory allocation path.
498   bool prefer_hugepages = false;
499   bool prefer_posix_shm = false;
500   bool prefer_dynamic_mapping = false;
501
502   // Are there enough hugepages?
503   int64 hugepagesize = FindHugePages() * 2 * kMegabyte;
504   // TODO(nsanders): Is there enough /dev/shm? Is there enough free memeory?
505   if ((length >= 1400LL * kMegabyte) && (address_mode_ == 32)) {
506     prefer_dynamic_mapping = true;
507     prefer_posix_shm = true;
508     logprintf(3, "Log: Prefer POSIX shared memory allocation.\n");
509     logprintf(3, "Log: You may need to run "
510                  "'sudo mount -o remount,size=100\% /dev/shm.'\n");
511   } else if (hugepagesize >= length) {
512     prefer_hugepages = true;
513     logprintf(3, "Log: Prefer using hugepage allocation.\n");
514   } else {
515     logprintf(3, "Log: Prefer plain malloc memory allocation.\n");
516   }
517
518 #ifdef HAVE_SYS_SHM_H
519   // Allocate hugepage mapped memory.
520   if (prefer_hugepages) {
521     do { // Allow break statement.
522       int shmid;
523       void *shmaddr;
524
525       if ((shmid = shmget(2, length,
526               SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W)) < 0) {
527         int err = errno;
528         string errtxt = ErrorString(err);
529         logprintf(3, "Log: failed to allocate shared hugepage "
530                       "object - err %d (%s)\n",
531                   err, errtxt.c_str());
532         logprintf(3, "Log: sysctl -w vm.nr_hugepages=XXX allows hugepages.\n");
533         break;
534       }
535
536       shmaddr = shmat(shmid, NULL, 0);
537       if (shmaddr == reinterpret_cast<void*>(-1)) {
538         int err = errno;
539         string errtxt = ErrorString(err);
540         logprintf(0, "Log: failed to attach shared "
541                      "hugepage object - err %d (%s).\n",
542                   err, errtxt.c_str());
543         if (shmctl(shmid, IPC_RMID, NULL) < 0) {
544           int err = errno;
545           string errtxt = ErrorString(err);
546           logprintf(0, "Log: failed to remove shared "
547                        "hugepage object - err %d (%s).\n",
548                     err, errtxt.c_str());
549         }
550         break;
551       }
552       use_hugepages_ = true;
553       shmid_ = shmid;
554       buf = shmaddr;
555       logprintf(0, "Log: Using shared hugepage object 0x%x at %p.\n",
556                 shmid, shmaddr);
557     } while (0);
558   }
559
560   if ((!use_hugepages_) && prefer_posix_shm) {
561     do {
562       int shm_object;
563       void *shmaddr = NULL;
564
565       shm_object = shm_open("/stressapptest", O_CREAT | O_RDWR, S_IRWXU);
566       if (shm_object < 0) {
567         int err = errno;
568         string errtxt = ErrorString(err);
569         logprintf(3, "Log: failed to allocate shared "
570                       "smallpage object - err %d (%s)\n",
571                   err, errtxt.c_str());
572         break;
573       }
574
575       if (0 > ftruncate(shm_object, length)) {
576         int err = errno;
577         string errtxt = ErrorString(err);
578         logprintf(3, "Log: failed to ftruncate shared "
579                       "smallpage object - err %d (%s)\n",
580                   err, errtxt.c_str());
581         break;
582       }
583
584       // 32 bit linux apps can only use ~1.4G of address space.
585       // Use dynamic mapping for allocations larger than that.
586       // Currently perf hit is ~10% for this.
587       if (prefer_dynamic_mapping) {
588         dynamic_mapped_shmem_ = true;
589       } else {
590         // Do a full mapping here otherwise.
591         shmaddr = mmap64(NULL, length, PROT_READ | PROT_WRITE,
592                          MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
593                          shm_object, 0);
594         if (shmaddr == reinterpret_cast<void*>(-1)) {
595           int err = errno;
596           string errtxt = ErrorString(err);
597           logprintf(0, "Log: failed to map shared "
598                        "smallpage object - err %d (%s).\n",
599                     err, errtxt.c_str());
600           break;
601         }
602       }
603
604       use_posix_shm_ = true;
605       shmid_ = shm_object;
606       buf = shmaddr;
607       char location_message[256] = "";
608       if (dynamic_mapped_shmem_) {
609         sprintf(location_message, "mapped as needed");
610       } else {
611         sprintf(location_message, "at %p", shmaddr);
612       }
613       logprintf(0, "Log: Using posix shared memory object 0x%x %s.\n",
614                 shm_object, location_message);
615     } while (0);
616     shm_unlink("/stressapptest");
617   }
618 #endif  // HAVE_SYS_SHM_H
619
620   if (!use_hugepages_ && !use_posix_shm_) {
621     // If the page size is what SAT is expecting explicitly perform mmap()
622     // allocation.
623     if (sysconf(_SC_PAGESIZE) >= 4096) {
624       void *map_buf = mmap(NULL, length, PROT_READ | PROT_WRITE,
625                            MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
626       if (map_buf != MAP_FAILED) {
627         buf = map_buf;
628         mmapped_allocation_ = true;
629         logprintf(0, "Log: Using mmap() allocation at %p.\n", buf);
630       }
631     }
632     if (!mmapped_allocation_) {
633       // Use memalign to ensure that blocks are aligned enough for disk direct
634       // IO.
635       buf = static_cast<char*>(memalign(4096, length));
636       if (buf) {
637         logprintf(0, "Log: Using memaligned allocation at %p.\n", buf);
638       } else {
639         logprintf(0, "Process Error: memalign returned 0\n");
640         if ((length >= 1499LL * kMegabyte) && (address_mode_ == 32)) {
641           logprintf(0, "Log: You are trying to allocate > 1.4G on a 32 "
642                        "bit process. Please setup shared memory.\n");
643         }
644       }
645     }
646   }
647
648   testmem_ = buf;
649   if (buf || dynamic_mapped_shmem_) {
650     testmemsize_ = length;
651   } else {
652     testmemsize_ = 0;
653   }
654
655   return (buf != 0) || dynamic_mapped_shmem_;
656 }
657
658 // Free the test memory.
659 void OsLayer::FreeTestMem() {
660   if (testmem_) {
661     if (use_hugepages_) {
662 #ifdef HAVE_SYS_SHM_H
663       shmdt(testmem_);
664       shmctl(shmid_, IPC_RMID, NULL);
665 #endif
666     } else if (use_posix_shm_) {
667       if (!dynamic_mapped_shmem_) {
668         munmap(testmem_, testmemsize_);
669       }
670       close(shmid_);
671     } else if (mmapped_allocation_) {
672       munmap(testmem_, testmemsize_);
673     } else {
674       free(testmem_);
675     }
676     testmem_ = 0;
677     testmemsize_ = 0;
678   }
679 }
680
681
682 // Prepare the target memory. It may requre mapping in, or this may be a noop.
683 void *OsLayer::PrepareTestMem(uint64 offset, uint64 length) {
684   sat_assert((offset + length) <= testmemsize_);
685   if (dynamic_mapped_shmem_) {
686     // TODO(nsanders): Check if we can support MAP_NONBLOCK,
687     // and evaluate performance hit from not using it.
688 #ifdef HAVE_MMAP64
689     void * mapping = mmap64(NULL, length, PROT_READ | PROT_WRITE,
690                      MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
691                      shmid_, offset);
692 #else
693     void * mapping = mmap(NULL, length, PROT_READ | PROT_WRITE,
694                      MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
695                      shmid_, offset);
696 #endif
697     if (mapping == MAP_FAILED) {
698       string errtxt = ErrorString(errno);
699       logprintf(0, "Process Error: PrepareTestMem mmap64(%llx, %llx) failed. "
700                    "error: %s.\n",
701                 offset, length, errtxt.c_str());
702       sat_assert(0);
703     }
704     return mapping;
705   }
706
707   return reinterpret_cast<void*>(reinterpret_cast<char*>(testmem_) + offset);
708 }
709
710 // Release the test memory resources, if any.
711 void OsLayer::ReleaseTestMem(void *addr, uint64 offset, uint64 length) {
712   if (dynamic_mapped_shmem_) {
713     int retval = munmap(addr, length);
714     if (retval == -1) {
715       string errtxt = ErrorString(errno);
716       logprintf(0, "Process Error: ReleaseTestMem munmap(%p, %llx) failed. "
717                    "error: %s.\n",
718                 addr, length, errtxt.c_str());
719       sat_assert(0);
720     }
721   }
722 }
723
724 // No error polling on unknown systems.
725 int OsLayer::ErrorPoll() {
726   return 0;
727 }
728
729 // Generally, poll for errors once per second.
730 void OsLayer::ErrorWait() {
731   sat_sleep(1);
732   return;
733 }
734
735 // Open a PCI bus-dev-func as a file and return its file descriptor.
736 // Error is indicated by return value less than zero.
737 int OsLayer::PciOpen(int bus, int device, int function) {
738   char dev_file[256];
739
740   snprintf(dev_file, sizeof(dev_file), "/proc/bus/pci/%02x/%02x.%x",
741            bus, device, function);
742
743   int fd = open(dev_file, O_RDWR);
744   if (fd == -1) {
745     logprintf(0, "Process Error: Unable to open PCI bus %d, device %d, "
746                  "function %d (errno %d).\n",
747               bus, device, function, errno);
748     return -1;
749   }
750
751   return fd;
752 }
753
754
755 // Read and write functions to access PCI config.
756 uint32 OsLayer::PciRead(int fd, uint32 offset, int width) {
757   // Strict aliasing rules lawyers will cause data corruption
758   // on cast pointers in some gccs.
759   union {
760     uint32 l32;
761     uint16 l16;
762     uint8 l8;
763   } datacast;
764   datacast.l32 = 0;
765   uint32 size = width / 8;
766
767   sat_assert((width == 32) || (width == 16) || (width == 8));
768   sat_assert(offset <= (256 - size));
769
770   if (lseek(fd, offset, SEEK_SET) < 0) {
771     logprintf(0, "Process Error: Can't seek %x\n", offset);
772     return 0;
773   }
774   if (read(fd, &datacast, size) != static_cast<ssize_t>(size)) {
775     logprintf(0, "Process Error: Can't read %x\n", offset);
776     return 0;
777   }
778
779   // Extract the data.
780   switch (width) {
781     case 8:
782       sat_assert(&(datacast.l8) == reinterpret_cast<uint8*>(&datacast));
783       return datacast.l8;
784     case 16:
785       sat_assert(&(datacast.l16) == reinterpret_cast<uint16*>(&datacast));
786       return datacast.l16;
787     case 32:
788       return datacast.l32;
789   }
790   return 0;
791 }
792
793 void OsLayer::PciWrite(int fd, uint32 offset, uint32 value, int width) {
794   // Strict aliasing rules lawyers will cause data corruption
795   // on cast pointers in some gccs.
796   union {
797     uint32 l32;
798     uint16 l16;
799     uint8 l8;
800   } datacast;
801   datacast.l32 = 0;
802   uint32 size = width / 8;
803
804   sat_assert((width == 32) || (width == 16) || (width == 8));
805   sat_assert(offset <= (256 - size));
806
807   // Cram the data into the right alignment.
808   switch (width) {
809     case 8:
810       sat_assert(&(datacast.l8) == reinterpret_cast<uint8*>(&datacast));
811       datacast.l8 = value;
812     case 16:
813       sat_assert(&(datacast.l16) == reinterpret_cast<uint16*>(&datacast));
814       datacast.l16 = value;
815     case 32:
816       datacast.l32 = value;
817   }
818
819   if (lseek(fd, offset, SEEK_SET) < 0) {
820     logprintf(0, "Process Error: Can't seek %x\n", offset);
821     return;
822   }
823   if (write(fd, &datacast, size) != static_cast<ssize_t>(size)) {
824     logprintf(0, "Process Error: Can't write %x to %x\n", datacast.l32, offset);
825     return;
826   }
827
828   return;
829 }
830
831
832
833 // Open dev msr.
834 int OsLayer::OpenMSR(uint32 core, uint32 address) {
835   char buf[256];
836   snprintf(buf, sizeof(buf), "/dev/cpu/%d/msr", core);
837   int fd = open(buf, O_RDWR);
838   if (fd < 0)
839     return fd;
840
841   uint32 pos = lseek(fd, address, SEEK_SET);
842   if (pos != address) {
843     close(fd);
844     logprintf(5, "Log: can't seek to msr %x, cpu %d\n", address, core);
845     return -1;
846   }
847
848   return fd;
849 }
850
851 bool OsLayer::ReadMSR(uint32 core, uint32 address, uint64 *data) {
852   int fd = OpenMSR(core, address);
853   if (fd < 0)
854     return false;
855
856   // Read from the msr.
857   bool res = (sizeof(*data) == read(fd, data, sizeof(*data)));
858
859   if (!res)
860     logprintf(5, "Log: Failed to read msr %x core %d\n", address, core);
861
862   close(fd);
863
864   return res;
865 }
866
867 bool OsLayer::WriteMSR(uint32 core, uint32 address, uint64 *data) {
868   int fd = OpenMSR(core, address);
869   if (fd < 0)
870     return false;
871
872   // Write to the msr
873   bool res = (sizeof(*data) == write(fd, data, sizeof(*data)));
874
875   if (!res)
876     logprintf(5, "Log: Failed to write msr %x core %d\n", address, core);
877
878   close(fd);
879
880   return res;
881 }
882
883 // Extract bits [n+len-1, n] from a 32 bit word.
884 // so GetBitField(0x0f00, 8, 4) == 0xf.
885 uint32 OsLayer::GetBitField(uint32 val, uint32 n, uint32 len) {
886   return (val >> n) & ((1<<len) - 1);
887 }
888
889 // Generic CPU stress workload that would work on any CPU/Platform.
890 // Float-point array moving average calculation.
891 bool OsLayer::CpuStressWorkload() {
892   double float_arr[100];
893   double sum = 0;
894 #ifdef HAVE_RAND_R
895   unsigned int seed = 12345;
896 #endif
897
898   // Initialize array with random numbers.
899   for (int i = 0; i < 100; i++) {
900 #ifdef HAVE_RAND_R
901     float_arr[i] = rand_r(&seed);
902     if (rand_r(&seed) % 2)
903       float_arr[i] *= -1.0;
904 #else
905     srand(time(NULL));
906     float_arr[i] = rand();  // NOLINT
907     if (rand() % 2)         // NOLINT
908       float_arr[i] *= -1.0;
909 #endif
910   }
911
912   // Calculate moving average.
913   for (int i = 0; i < 100000000; i++) {
914     float_arr[i % 100] =
915       (float_arr[i % 100] + float_arr[(i + 1) % 100] +
916        float_arr[(i + 99) % 100]) / 3;
917     sum += float_arr[i % 100];
918   }
919
920   // Artificial printf so the loops do not get optimized away.
921   if (sum == 0.0)
922     logprintf(12, "Log: I'm Feeling Lucky!\n");
923   return true;
924 }