chiark / gitweb /
Allow ./configure for cross compile
[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
52 // OsLayer initialization.
53 OsLayer::OsLayer() {
54   testmem_ = 0;
55   testmemsize_ = 0;
56   totalmemsize_ = 0;
57   min_hugepages_bytes_ = 0;
58   normal_mem_ = true;
59   use_hugepages_ = false;
60   use_posix_shm_ = false;
61   dynamic_mapped_shmem_ = false;
62   shmid_ = 0;
63
64   time_initialized_ = 0;
65
66   regionsize_ = 0;
67   regioncount_ = 1;
68   num_cpus_ = 0;
69   num_nodes_ = 0;
70   num_cpus_per_node_ = 0;
71   error_diagnoser_ = 0;
72   err_log_callback_ = 0;
73   error_injection_ = false;
74
75   void *pvoid = 0;
76   address_mode_ = sizeof(pvoid) * 8;
77
78   has_clflush_ = false;
79   has_sse2_ = false;
80
81   use_flush_page_cache_ = false;
82 }
83
84 // OsLayer cleanup.
85 OsLayer::~OsLayer() {
86   if (error_diagnoser_)
87     delete error_diagnoser_;
88 }
89
90 // OsLayer initialization.
91 bool OsLayer::Initialize() {
92   time_initialized_ = time(NULL);
93   // Detect asm support.
94   GetFeatures();
95
96   if (num_cpus_ == 0) {
97     num_nodes_ = 1;
98     num_cpus_ = sysconf(_SC_NPROCESSORS_ONLN);
99     num_cpus_per_node_ = num_cpus_ / num_nodes_;
100   }
101   logprintf(5, "Log: %d nodes, %d cpus.\n", num_nodes_, num_cpus_);
102   sat_assert(CPU_SETSIZE >= num_cpus_);
103   cpu_sets_.resize(num_nodes_);
104   cpu_sets_valid_.resize(num_nodes_);
105   // Create error diagnoser.
106   error_diagnoser_ = new ErrorDiag();
107   if (!error_diagnoser_->set_os(this))
108     return false;
109   return true;
110 }
111
112 // Machine type detected. Can we implement all these functions correctly?
113 bool OsLayer::IsSupported() {
114   if (kOpenSource) {
115     // There are no explicitly supported systems in open source version.
116     return true;
117   }
118
119   // This is the default empty implementation.
120   // SAT won't report full error information.
121   return false;
122 }
123
124 int OsLayer::AddressMode() {
125   // Detect 32/64 bit binary.
126   void *pvoid = 0;
127   return sizeof(pvoid) * 8;
128 }
129
130 // Translates user virtual to physical address.
131 uint64 OsLayer::VirtualToPhysical(void *vaddr) {
132   uint64 frame, shift;
133   off64_t off = ((uintptr_t)vaddr) / getpagesize() * 8;
134   int fd = open(kPagemapPath, O_RDONLY);
135   // /proc/self/pagemap is available in kernel >= 2.6.25
136   if (fd < 0)
137     return 0;
138
139   if (lseek64(fd, off, SEEK_SET) != off || read(fd, &frame, 8) != 8) {
140     int err = errno;
141     string errtxt = ErrorString(err);
142     logprintf(0, "Process Error: failed to access %s with errno %d (%s)\n",
143               kPagemapPath, err, errtxt.c_str());
144     if (fd >= 0)
145       close(fd);
146     return 0;
147   }
148   close(fd);
149   if (!(frame & (1LL << 63)) || (frame & (1LL << 62)))
150     return 0;
151   shift = (frame >> 55) & 0x3f;
152   frame = (frame & 0x007fffffffffffffLL) << shift;
153   return frame | ((uintptr_t)vaddr & ((1LL << shift) - 1));
154 }
155
156 // Returns the HD device that contains this file.
157 string OsLayer::FindFileDevice(string filename) {
158   return "hdUnknown";
159 }
160
161 // Returns a list of locations corresponding to HD devices.
162 list<string> OsLayer::FindFileDevices() {
163   // No autodetection on unknown systems.
164   list<string> locations;
165   return locations;
166 }
167
168
169 // Get HW core features from cpuid instruction.
170 void OsLayer::GetFeatures() {
171 #if defined(STRESSAPPTEST_CPU_X86_64) || defined(STRESSAPPTEST_CPU_I686)
172   // CPUID features documented at:
173   // http://www.sandpile.org/ia32/cpuid.htm
174   int ax, bx, cx, dx;
175   __asm__ __volatile__ (
176 # if defined(STRESSAPPTEST_CPU_I686) && defined(__PIC__)
177       "xchg %%ebx, %%esi;"
178       "cpuid;"
179       "xchg %%esi, %%ebx;"
180       : "=S" (bx),
181 # else
182       "cpuid;"
183       : "=b" (bx),
184 # endif
185         "=a" (ax), "=c" (cx), "=d" (dx) : "a" (1));
186   has_clflush_ = (dx >> 19) & 1;
187   has_sse2_ = (dx >> 26) & 1;
188
189   logprintf(9, "Log: has clflush: %s, has sse2: %s\n",
190             has_clflush_ ? "true" : "false",
191             has_sse2_ ? "true" : "false");
192 #elif defined(STRESSAPPTEST_CPU_PPC)
193   // All PPC implementations have cache flush instructions.
194   has_clflush_ = true;
195 #elif defined(STRESSAPPTEST_CPU_ARMV7A)
196 #warning "Unsupported CPU type ARMV7A: unable to determine feature set."
197 #else
198 #warning "Unsupported CPU type: unable to determine feature set."
199 #endif
200 }
201
202
203 // Enable FlushPageCache to be functional instead of a NOP.
204 void OsLayer::ActivateFlushPageCache(void) {
205   logprintf(9, "Log: page cache will be flushed as needed\n");
206   use_flush_page_cache_ = true;
207 }
208
209 // Flush the page cache to ensure reads come from the disk.
210 bool OsLayer::FlushPageCache(void) {
211   if (!use_flush_page_cache_)
212     return true;
213
214   // First, ask the kernel to write the cache to the disk.
215   sync();
216
217   // Second, ask the kernel to empty the cache by writing "1" to
218   // "/proc/sys/vm/drop_caches".
219   static const char *drop_caches_file = "/proc/sys/vm/drop_caches";
220   int dcfile = open(drop_caches_file, O_WRONLY);
221   if (dcfile < 0) {
222     int err = errno;
223     string errtxt = ErrorString(err);
224     logprintf(3, "Log: failed to open %s - err %d (%s)\n",
225               drop_caches_file, err, errtxt.c_str());
226     return false;
227   }
228
229   ssize_t bytes_written = write(dcfile, "1", 1);
230   close(dcfile);
231
232   if (bytes_written != 1) {
233     int err = errno;
234     string errtxt = ErrorString(err);
235     logprintf(3, "Log: failed to write %s - err %d (%s)\n",
236               drop_caches_file, err, errtxt.c_str());
237     return false;
238   }
239   return true;
240 }
241
242
243 // We need to flush the cacheline here.
244 void OsLayer::Flush(void *vaddr) {
245   // Use the generic flush. This function is just so we can override
246   // this if we are so inclined.
247   if (has_clflush_)
248     FastFlush(vaddr);
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 name.
265 // Assumes simple round-robin interleaving between memory channels of
266 // 'interleave_size_' sized chunks, 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   static const string unknown = "DIMM Unknown";
270   if (!modules_) {
271     snprintf(buf, len, "%s", unknown.c_str());
272     return 0;
273   }
274
275   // Find channel by counting interleave units (typically cachelines),
276   // and mod by number of channels.
277   vector<string>& channel = (*modules_)[
278       (addr / interleave_size_) % modules_->size()];
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 = time(NULL);
345   int ttf = now - time_initialized_;
346   logprintf(0, "Report Error: %s : %s : %d : %ds\n", symptom, part, count, ttf);
347   return true;
348 }
349
350 // Read the number of hugepages out of the kernel interface in proc.
351 int64 OsLayer::FindHugePages() {
352   char buf[65] = "0";
353
354   // This is a kernel interface to query the numebr of hugepages
355   // available in the system.
356   static const char *hugepages_info_file = "/proc/sys/vm/nr_hugepages";
357   int hpfile = open(hugepages_info_file, O_RDONLY);
358
359   ssize_t bytes_read = read(hpfile, buf, 64);
360   close(hpfile);
361
362   if (bytes_read <= 0) {
363     logprintf(12, "Log: /proc/sys/vm/nr_hugepages "
364                   "read did not provide data\n");
365     return 0;
366   }
367
368   if (bytes_read == 64) {
369     logprintf(0, "Process Error: /proc/sys/vm/nr_hugepages "
370                  "is surprisingly large\n");
371     return 0;
372   }
373
374   // Add a null termintation to be string safe.
375   buf[bytes_read] = '\0';
376   // Read the page count.
377   int64 pages = strtoull(buf, NULL, 10);  // NOLINT
378
379   return pages;
380 }
381
382 int64 OsLayer::FindFreeMemSize() {
383   int64 size = 0;
384   int64 minsize = 0;
385   if (totalmemsize_ > 0)
386     return totalmemsize_;
387
388   int64 pages = sysconf(_SC_PHYS_PAGES);
389   int64 avpages = sysconf(_SC_AVPHYS_PAGES);
390   int64 pagesize = sysconf(_SC_PAGESIZE);
391   int64 physsize = pages * pagesize;
392   int64 avphyssize = avpages * pagesize;
393
394   // Assume 2MB hugepages.
395   int64 hugepagesize = FindHugePages() * 2 * kMegabyte;
396
397   if ((pages == -1) || (pagesize == -1)) {
398     logprintf(0, "Process Error: sysconf could not determine memory size.\n");
399     return 0;
400   }
401
402   // We want to leave enough stuff for things to run.
403   // If the user specified a minimum amount of memory to expect, require that.
404   // Otherwise, if more than 2GB is present, leave 192M + 5% for other stuff.
405   // If less than 2GB is present use 85% of what's available.
406   // These are fairly arbitrary numbers that seem to work OK.
407   //
408   // TODO(nsanders): is there a more correct way to determine target
409   // memory size?
410   if (hugepagesize > 0 && min_hugepages_bytes_ > 0) {
411     minsize = min_hugepages_bytes_;
412   } else if (physsize < 2048LL * kMegabyte) {
413     minsize = ((pages * 85) / 100) * pagesize;
414   } else {
415     minsize = ((pages * 95) / 100) * pagesize - (192 * kMegabyte);
416   }
417
418   // Use hugepage sizing if available.
419   if (hugepagesize > 0) {
420     if (hugepagesize < minsize) {
421       logprintf(0, "Procedural Error: Not enough hugepages. "
422                    "%lldMB available < %lldMB required.\n",
423                 hugepagesize / kMegabyte,
424                 minsize / kMegabyte);
425       // Require the calculated minimum amount of memory.
426       size = minsize;
427     } else {
428       // Require that we get all hugepages.
429       size = hugepagesize;
430     }
431   } else {
432     // Require the calculated minimum amount of memory.
433     size = minsize;
434   }
435
436   logprintf(5, "Log: Total %lld MB. Free %lld MB. Hugepages %lld MB. "
437                "Targeting %lld MB (%lld%%)\n",
438             physsize / kMegabyte,
439             avphyssize / kMegabyte,
440             hugepagesize / kMegabyte,
441             size / kMegabyte,
442             size * 100 / physsize);
443
444   totalmemsize_ = size;
445   return size;
446 }
447
448 // Allocates all memory available.
449 int64 OsLayer::AllocateAllMem() {
450   int64 length = FindFreeMemSize();
451   bool retval = AllocateTestMem(length, 0);
452   if (retval)
453     return length;
454   else
455     return 0;
456 }
457
458 // Allocate the target memory. This may be from malloc, hugepage pool
459 // or other platform specific sources.
460 bool OsLayer::AllocateTestMem(int64 length, uint64 paddr_base) {
461   // Try hugepages first.
462   void *buf = 0;
463
464   sat_assert(length >= 0);
465
466   if (paddr_base)
467     logprintf(0, "Process Error: non zero paddr_base %#llx is not supported,"
468               " ignore.\n", paddr_base);
469
470   // Determine optimal memory allocation path.
471   bool prefer_hugepages = false;
472   bool prefer_posix_shm = false;
473   bool prefer_dynamic_mapping = false;
474
475   // Are there enough hugepages?
476   int64 hugepagesize = FindHugePages() * 2 * kMegabyte;
477   // TODO(nsanders): Is there enough /dev/shm? Is there enough free memeory?
478   if ((length >= 1400LL * kMegabyte) && (address_mode_ == 32)) {
479     prefer_dynamic_mapping = true;
480     prefer_posix_shm = true;
481     logprintf(3, "Log: Prefer POSIX shared memory allocation.\n");
482     logprintf(3, "Log: You may need to run "
483                  "'sudo mount -o remount,size=100\% /dev/shm.'\n");
484   } else if (hugepagesize >= length) {
485     prefer_hugepages = true;
486     logprintf(3, "Log: Prefer using hugepace allocation.\n");
487   } else {
488     logprintf(3, "Log: Prefer plain malloc memory allocation.\n");
489   }
490
491 #ifdef HAVE_SYS_SHM_H
492   // Allocate hugepage mapped memory.
493   if (prefer_hugepages) {
494     do { // Allow break statement.
495       int shmid;
496       void *shmaddr;
497
498       if ((shmid = shmget(2, length,
499               SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W)) < 0) {
500         int err = errno;
501         string errtxt = ErrorString(err);
502         logprintf(3, "Log: failed to allocate shared hugepage "
503                       "object - err %d (%s)\n",
504                   err, errtxt.c_str());
505         logprintf(3, "Log: sysctl -w vm.nr_hugepages=XXX allows hugepages.\n");
506         break;
507       }
508
509       shmaddr = shmat(shmid, NULL, NULL);
510       if (shmaddr == reinterpret_cast<void*>(-1)) {
511         int err = errno;
512         string errtxt = ErrorString(err);
513         logprintf(0, "Log: failed to attach shared "
514                      "hugepage object - err %d (%s).\n",
515                   err, errtxt.c_str());
516         if (shmctl(shmid, IPC_RMID, NULL) < 0) {
517           int err = errno;
518           string errtxt = ErrorString(err);
519           logprintf(0, "Log: failed to remove shared "
520                        "hugepage object - err %d (%s).\n",
521                     err, errtxt.c_str());
522         }
523         break;
524       }
525       use_hugepages_ = true;
526       shmid_ = shmid;
527       buf = shmaddr;
528       logprintf(0, "Log: Using shared hugepage object 0x%x at %p.\n",
529                 shmid, shmaddr);
530     } while (0);
531   }
532
533   if ((!use_hugepages_) && prefer_posix_shm) {
534     do {
535       int shm_object;
536       void *shmaddr = NULL;
537
538       shm_object = shm_open("/stressapptest", O_CREAT | O_RDWR, S_IRWXU);
539       if (shm_object < 0) {
540         int err = errno;
541         string errtxt = ErrorString(err);
542         logprintf(3, "Log: failed to allocate shared "
543                       "smallpage object - err %d (%s)\n",
544                   err, errtxt.c_str());
545         break;
546       }
547
548       if (0 > ftruncate(shm_object, length)) {
549         int err = errno;
550         string errtxt = ErrorString(err);
551         logprintf(3, "Log: failed to ftruncate shared "
552                       "smallpage object - err %d (%s)\n",
553                   err, errtxt.c_str());
554         break;
555       }
556
557       // 32 bit linux apps can only use ~1.4G of address space.
558       // Use dynamic mapping for allocations larger than that.
559       // Currently perf hit is ~10% for this.
560       if (prefer_dynamic_mapping) {
561         dynamic_mapped_shmem_ = true;
562       } else {
563         // Do a full mapping here otherwise.
564         shmaddr = mmap64(NULL, length, PROT_READ | PROT_WRITE,
565                          MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
566                          shm_object, NULL);
567         if (shmaddr == reinterpret_cast<void*>(-1)) {
568           int err = errno;
569           string errtxt = ErrorString(err);
570           logprintf(0, "Log: failed to map shared "
571                        "smallpage object - err %d (%s).\n",
572                     err, errtxt.c_str());
573           break;
574         }
575       }
576
577       use_posix_shm_ = true;
578       shmid_ = shm_object;
579       buf = shmaddr;
580       char location_message[256] = "";
581       if (dynamic_mapped_shmem_) {
582         sprintf(location_message, "mapped as needed");
583       } else {
584         sprintf(location_message, "at %p", shmaddr);
585       }
586       logprintf(0, "Log: Using posix shared memory object 0x%x %s.\n",
587                 shm_object, location_message);
588     } while (0);
589     shm_unlink("/stressapptest");
590   }
591 #endif // HAVE_SYS_SHM_H
592
593   if (!use_hugepages_ && !use_posix_shm_) {
594     // Use memalign to ensure that blocks are aligned enough for disk direct IO.
595     buf = static_cast<char*>(memalign(4096, length));
596     if (buf) {
597       logprintf(0, "Log: Using memaligned allocation at %p.\n", buf);
598     } else {
599       logprintf(0, "Process Error: memalign returned 0\n");
600       if ((length >= 1499LL * kMegabyte) && (address_mode_ == 32)) {
601         logprintf(0, "Log: You are trying to allocate > 1.4G on a 32 "
602                      "bit process. Please setup shared memory.\n");
603       }
604     }
605   }
606
607   testmem_ = buf;
608   if (buf || dynamic_mapped_shmem_) {
609     testmemsize_ = length;
610   } else {
611     testmemsize_ = 0;
612   }
613
614   return (buf != 0) || dynamic_mapped_shmem_;
615 }
616
617 // Free the test memory.
618 void OsLayer::FreeTestMem() {
619   if (testmem_) {
620     if (use_hugepages_) {
621 #ifdef HAVE_SYS_SHM_H
622       shmdt(testmem_);
623       shmctl(shmid_, IPC_RMID, NULL);
624 #endif
625     } else if (use_posix_shm_) {
626       if (!dynamic_mapped_shmem_) {
627         munmap(testmem_, testmemsize_);
628       }
629       close(shmid_);
630     } else {
631       free(testmem_);
632     }
633     testmem_ = 0;
634     testmemsize_ = 0;
635   }
636 }
637
638
639 // Prepare the target memory. It may requre mapping in, or this may be a noop.
640 void *OsLayer::PrepareTestMem(uint64 offset, uint64 length) {
641   sat_assert((offset + length) <= testmemsize_);
642   if (dynamic_mapped_shmem_) {
643     // TODO(nsanders): Check if we can support MAP_NONBLOCK,
644     // and evaluate performance hit from not using it.
645 #ifdef HAVE_MMAP64
646     void * mapping = mmap64(NULL, length, PROT_READ | PROT_WRITE,
647                      MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
648                      shmid_, offset);
649 #else
650     void * mapping = mmap(NULL, length, PROT_READ | PROT_WRITE,
651                      MAP_SHARED | MAP_NORESERVE | MAP_LOCKED | MAP_POPULATE,
652                      shmid_, offset);
653 #endif
654     if (mapping == MAP_FAILED) {
655       string errtxt = ErrorString(errno);
656       logprintf(0, "Process Error: PrepareTestMem mmap64(%llx, %llx) failed. "
657                    "error: %s.\n",
658                 offset, length, errtxt.c_str());
659       sat_assert(0);
660     }
661     return mapping;
662   }
663
664   return reinterpret_cast<void*>(reinterpret_cast<char*>(testmem_) + offset);
665 }
666
667 // Release the test memory resources, if any.
668 void OsLayer::ReleaseTestMem(void *addr, uint64 offset, uint64 length) {
669   if (dynamic_mapped_shmem_) {
670     int retval = munmap(addr, length);
671     if (retval == -1) {
672       string errtxt = ErrorString(errno);
673       logprintf(0, "Process Error: ReleaseTestMem munmap(%p, %llx) failed. "
674                    "error: %s.\n",
675                 addr, length, errtxt.c_str());
676       sat_assert(0);
677     }
678   }
679 }
680
681 // No error polling on unknown systems.
682 int OsLayer::ErrorPoll() {
683   return 0;
684 }
685
686 // Generally, poll for errors once per second.
687 void OsLayer::ErrorWait() {
688   sat_sleep(1);
689   return;
690 }
691
692 // Open a PCI bus-dev-func as a file and return its file descriptor.
693 // Error is indicated by return value less than zero.
694 int OsLayer::PciOpen(int bus, int device, int function) {
695   char dev_file[256];
696
697   snprintf(dev_file, sizeof(dev_file), "/proc/bus/pci/%02x/%02x.%x",
698            bus, device, function);
699
700   int fd = open(dev_file, O_RDWR);
701   if (fd == -1) {
702     logprintf(0, "Process Error: Unable to open PCI bus %d, device %d, "
703                  "function %d (errno %d).\n",
704               bus, device, function, errno);
705     return -1;
706   }
707
708   return fd;
709 }
710
711
712 // Read and write functions to access PCI config.
713 uint32 OsLayer::PciRead(int fd, uint32 offset, int width) {
714   // Strict aliasing rules lawyers will cause data corruption
715   // on cast pointers in some gccs.
716   union {
717     uint32 l32;
718     uint16 l16;
719     uint8 l8;
720   } datacast;
721   datacast.l32 = 0;
722   uint32 size = width / 8;
723
724   sat_assert((width == 32) || (width == 16) || (width == 8));
725   sat_assert(offset <= (256 - size));
726
727   if (lseek(fd, offset, SEEK_SET) < 0) {
728     logprintf(0, "Process Error: Can't seek %x\n", offset);
729     return 0;
730   }
731   if (read(fd, &datacast, size) != static_cast<ssize_t>(size)) {
732     logprintf(0, "Process Error: Can't read %x\n", offset);
733     return 0;
734   }
735
736   // Extract the data.
737   switch (width) {
738     case 8:
739       sat_assert(&(datacast.l8) == reinterpret_cast<uint8*>(&datacast));
740       return datacast.l8;
741     case 16:
742       sat_assert(&(datacast.l16) == reinterpret_cast<uint16*>(&datacast));
743       return datacast.l16;
744     case 32:
745       return datacast.l32;
746   }
747   return 0;
748 }
749
750 void OsLayer::PciWrite(int fd, uint32 offset, uint32 value, int width) {
751   // Strict aliasing rules lawyers will cause data corruption
752   // on cast pointers in some gccs.
753   union {
754     uint32 l32;
755     uint16 l16;
756     uint8 l8;
757   } datacast;
758   datacast.l32 = 0;
759   uint32 size = width / 8;
760
761   sat_assert((width == 32) || (width == 16) || (width == 8));
762   sat_assert(offset <= (256 - size));
763
764   // Cram the data into the right alignment.
765   switch (width) {
766     case 8:
767       sat_assert(&(datacast.l8) == reinterpret_cast<uint8*>(&datacast));
768       datacast.l8 = value;
769     case 16:
770       sat_assert(&(datacast.l16) == reinterpret_cast<uint16*>(&datacast));
771       datacast.l16 = value;
772     case 32:
773       datacast.l32 = value;
774   }
775
776   if (lseek(fd, offset, SEEK_SET) < 0) {
777     logprintf(0, "Process Error: Can't seek %x\n", offset);
778     return;
779   }
780   if (write(fd, &datacast, size) != static_cast<ssize_t>(size)) {
781     logprintf(0, "Process Error: Can't write %x to %x\n", datacast.l32, offset);
782     return;
783   }
784
785   return;
786 }
787
788
789
790 // Open dev msr.
791 int OsLayer::OpenMSR(uint32 core, uint32 address) {
792   char buf[256];
793   snprintf(buf, sizeof(buf), "/dev/cpu/%d/msr", core);
794   int fd = open(buf, O_RDWR);
795   if (fd < 0)
796     return fd;
797
798   uint32 pos = lseek(fd, address, SEEK_SET);
799   if (pos != address) {
800     close(fd);
801     logprintf(5, "Log: can't seek to msr %x, cpu %d\n", address, core);
802     return -1;
803   }
804
805   return fd;
806 }
807
808 bool OsLayer::ReadMSR(uint32 core, uint32 address, uint64 *data) {
809   int fd = OpenMSR(core, address);
810   if (fd < 0)
811     return false;
812
813   // Read from the msr.
814   bool res = (sizeof(*data) == read(fd, data, sizeof(*data)));
815
816   if (!res)
817     logprintf(5, "Log: Failed to read msr %x core %d\n", address, core);
818
819   close(fd);
820
821   return res;
822 }
823
824 bool OsLayer::WriteMSR(uint32 core, uint32 address, uint64 *data) {
825   int fd = OpenMSR(core, address);
826   if (fd < 0)
827     return false;
828
829   // Write to the msr
830   bool res = (sizeof(*data) == write(fd, data, sizeof(*data)));
831
832   if (!res)
833     logprintf(5, "Log: Failed to write msr %x core %d\n", address, core);
834
835   close(fd);
836
837   return res;
838 }
839
840 // Extract bits [n+len-1, n] from a 32 bit word.
841 // so GetBitField(0x0f00, 8, 4) == 0xf.
842 uint32 OsLayer::GetBitField(uint32 val, uint32 n, uint32 len) {
843   return (val >> n) & ((1<<len) - 1);
844 }
845
846 // Generic CPU stress workload that would work on any CPU/Platform.
847 // Float-point array moving average calculation.
848 bool OsLayer::CpuStressWorkload() {
849   double float_arr[100];
850   double sum = 0;
851   unsigned int seed = 12345;
852
853   // Initialize array with random numbers.
854   for (int i = 0; i < 100; i++) {
855 #ifdef HAVE_RAND_R
856     float_arr[i] = rand_r(&seed);
857     if (rand_r(&seed) % 2)
858       float_arr[i] *= -1.0;
859 #else
860     float_arr[i] = rand();
861     if (rand() % 2)
862       float_arr[i] *= -1.0;
863 #endif
864   }
865
866   // Calculate moving average.
867   for (int i = 0; i < 100000000; i++) {
868     float_arr[i % 100] =
869       (float_arr[i % 100] + float_arr[(i + 1) % 100] +
870        float_arr[(i + 99) % 100]) / 3;
871     sum += float_arr[i % 100];
872   }
873
874   // Artificial printf so the loops do not get optimized away.
875   if (sum == 0.0)
876     logprintf(12, "Log: I'm Feeling Lucky!\n");
877   return true;
878 }
879
880 PCIDevices OsLayer::GetPCIDevices() {
881   PCIDevices device_list;
882   DIR *dir;
883   struct dirent *buf = new struct dirent();
884   struct dirent *entry;
885   dir = opendir(kSysfsPath);
886   if (!dir)
887     logprintf(0, "Process Error: Cannot open %s", kSysfsPath);
888   while (readdir_r(dir, buf, &entry) == 0 && entry) {
889     PCIDevice *device;
890     unsigned int dev, func;
891     // ".", ".." or a special non-device perhaps.
892     if (entry->d_name[0] == '.')
893       continue;
894
895     device = new PCIDevice();
896     if (sscanf(entry->d_name, "%04x:%02hx:%02x.%d",
897                &device->domain, &device->bus, &dev, &func) < 4) {
898       logprintf(0, "Process Error: Couldn't parse %s", entry->d_name);
899       free(device);
900       continue;
901     }
902     device->dev = dev;
903     device->func = func;
904     device->vendor_id = PCIGetValue(entry->d_name, "vendor");
905     device->device_id = PCIGetValue(entry->d_name, "device");
906     PCIGetResources(entry->d_name, device);
907     device_list.insert(device_list.end(), device);
908   }
909   closedir(dir);
910   delete buf;
911   return device_list;
912 }
913
914 int OsLayer::PCIGetValue(string name, string object) {
915   int fd, len;
916   char filename[256];
917   char buf[256];
918   snprintf(filename, sizeof(filename), "%s/%s/%s", kSysfsPath,
919            name.c_str(), object.c_str());
920   fd = open(filename, O_RDONLY);
921   if (fd < 0)
922     return 0;
923   len = read(fd, buf, 256);
924   close(fd);
925   buf[len] = '\0';
926   return strtol(buf, NULL, 0);  // NOLINT
927 }
928
929 int OsLayer::PCIGetResources(string name, PCIDevice *device) {
930   char filename[256];
931   char buf[256];
932   FILE *file;
933   int64 start;
934   int64 end;
935   int64 size;
936   int i;
937   snprintf(filename, sizeof(filename), "%s/%s/%s", kSysfsPath,
938            name.c_str(), "resource");
939   file = fopen(filename, "r");
940   if (!file) {
941     logprintf(0, "Process Error: impossible to find resource file for %s",
942               filename);
943     return errno;
944   }
945   for (i = 0; i < 6; i++) {
946     if (!fgets(buf, 256, file))
947       break;
948     sscanf(buf, "%llx %llx", &start, &end);  // NOLINT
949     size = 0;
950     if (start)
951       size = end - start + 1;
952     device->base_addr[i] = start;
953     device->size[i] = size;
954   }
955   fclose(file);
956   return 0;
957 }