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