chiark / gitweb /
5c8c8e06e82b74f65e091995652ffe759dceaec7
[stressapptest] / src / os.cc
1 // Copyright 2006 Google Inc. All Rights Reserved.
2 // Author: nsanders
3 //
4 // os.cc : os and machine specific implementation
5 // Copyright 2006 Google Inc.
6 // for open source release under GPL
7
8 // This file includes an abstracted interface
9 // for linux-distro specific and HW specific
10 // interfaces.
11
12 #include "os.h"
13
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <linux/types.h>
17 #include <malloc.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/mman.h>
22 #include <sys/ioctl.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <sys/ipc.h>
26 #include <sys/shm.h>
27 #include <unistd.h>
28
29 #ifndef SHM_HUGETLB
30 #define SHM_HUGETLB      04000  // remove when glibc defines it
31 #endif
32
33 #include <string>
34 #include <list>
35
36 // This file must work with autoconf on its public version,
37 // so these includes are correct.
38 #include "sattypes.h"
39 #include "error_diag.h"
40
41 // OsLayer initialization.
42 OsLayer::OsLayer() {
43   testmem_ = 0;
44   testmemsize_ = 0;
45   totalmemsize_ = 0;
46   error_injection_ = false;
47   normal_mem_ = true;
48   time_initialized_ = 0;
49
50   regionsize_ = 0;
51   regioncount_ = 1;
52   num_cpus_ = 0;
53   num_nodes_ = 0;
54   num_cpus_per_node_ = 0;
55   error_diagnoser_ = 0;
56   err_log_callback_ = 0;
57 }
58
59 // OsLayer cleanup.
60 OsLayer::~OsLayer() {
61   if (error_diagnoser_)
62     delete error_diagnoser_;
63 }
64
65 // OsLayer initialization.
66 bool OsLayer::Initialize() {
67   time_initialized_ = time(NULL);
68   use_hugepages_ = false;
69   shmid_ = 0;
70   if (num_cpus_ == 0) {
71     num_nodes_ = 1;
72     num_cpus_ = sysconf(_SC_NPROCESSORS_ONLN);
73     num_cpus_per_node_ = num_cpus_ / num_nodes_;
74   }
75   logprintf(5, "Log: %d nodes, %d cpus.\n", num_nodes_, num_cpus_);
76   sat_assert(CPU_SETSIZE >= num_cpus_);
77   cpu_sets_.resize(num_nodes_);
78   cpu_sets_valid_.resize(num_nodes_);
79   // Create error diagnoser.
80   error_diagnoser_ = new ErrorDiag();
81   if (!error_diagnoser_->set_os(this))
82     return false;
83   return true;
84 }
85
86 // Machine type detected. Can we implement all these functions correctly?
87 bool OsLayer::IsSupported() {
88   // This is the default empty implementation.
89   // SAT won't really run correctly.
90   return false;
91 }
92
93 int OsLayer::AddressMode() {
94   // Detect 32/64 bit binary.
95   void *pvoid = 0;
96   return sizeof(pvoid) * 8;
97 }
98
99 // Translates user virtual to physical address.
100 uint64 OsLayer::VirtualToPhysical(void *vaddr) {
101   // Needs platform specific implementation.
102   return 0;
103 }
104
105 // Returns the HD device that contains this file.
106 string OsLayer::FindFileDevice(string filename) {
107   return "hdUnknown";
108 }
109
110 // Returns a list of locations corresponding to HD devices.
111 list<string> OsLayer::FindFileDevices() {
112   // No autodetection on unknown systems.
113   list<string> locations;
114   return locations;
115 }
116
117 // We need to flush the cacheline here.
118 void OsLayer::Flush(void *vaddr) {
119   // Use the generic flush. This function is just so we can override
120   // this if we are so inclined.
121   FastFlush(vaddr);
122 }
123
124 // Translate user virtual to physical address.
125 int OsLayer::FindDimm(uint64 addr, char *buf, int len) {
126   char tmpbuf[256];
127   snprintf(tmpbuf, sizeof(tmpbuf), "DIMM Unknown");
128   snprintf(buf, len, "%s", tmpbuf);
129   return 0;
130 }
131
132
133 // Classifies addresses according to "regions"
134 // This isn't really implemented meaningfully here..
135 int32 OsLayer::FindRegion(uint64 addr) {
136   static bool warned = false;
137
138   if (regionsize_ == 0) {
139     regionsize_ = totalmemsize_ / 8;
140     if (regionsize_ < 512 * kMegabyte)
141       regionsize_ = 512 * kMegabyte;
142     regioncount_ = totalmemsize_ / regionsize_;
143     if (regioncount_ < 1) regioncount_ = 1;
144   }
145
146   int32 region_num = addr / regionsize_;
147   if (region_num >= regioncount_) {
148     if (!warned) {
149         logprintf(0, "Log: region number %d exceeds region count %d\n",
150                   region_num, regioncount_);
151         warned = true;
152     }
153     region_num = region_num % regioncount_;
154   }
155   return region_num;
156 }
157
158 // Report which cores are associated with a given region.
159 cpu_set_t *OsLayer::FindCoreMask(int32 region) {
160   sat_assert(region >= 0);
161   region %= num_nodes_;
162   if (!cpu_sets_valid_[region]) {
163     CPU_ZERO(&cpu_sets_[region]);
164     for (int i = 0; i < num_cpus_per_node_; ++i) {
165       CPU_SET(i + region * num_cpus_per_node_, &cpu_sets_[region]);
166     }
167     logprintf(5, "Log: Region %d mask 0x%08X\n",
168                  region, cpuset_to_uint32(&cpu_sets_[region]));
169     cpu_sets_valid_[region] = true;
170   }
171   return &cpu_sets_[region];
172 }
173
174 // Report an error in an easily parseable way.
175 bool OsLayer::ErrorReport(const char *part, const char *symptom, int count) {
176   time_t now = time(NULL);
177   int ttf = now - time_initialized_;
178   logprintf(0, "Report Error: %s : %s : %d : %ds\n", symptom, part, count, ttf);
179   return true;
180 }
181
182 // Read the number of hugepages out of the kernel interface in proc.
183 int64 OsLayer::FindHugePages() {
184   char buf[65] = "0";
185
186   // This is a kernel interface to query the numebr of hugepages
187   // available in the system.
188   static const char *hugepages_info_file = "/proc/sys/vm/nr_hugepages";
189   int hpfile = open(hugepages_info_file, O_RDONLY);
190
191   ssize_t bytes_read = read(hpfile, buf, 64);
192   close(hpfile);
193
194   if (bytes_read <= 0) {
195     logprintf(12, "Log: /proc/sys/vm/nr_hugepages "
196                   "read did not provide data\n");
197     return 0;
198   }
199
200   if (bytes_read == 64) {
201     logprintf(0, "Process Error: /proc/sys/vm/nr_hugepages "
202                  "is surprisingly large\n");
203     return 0;
204   }
205
206   // Add a null termintation to be string safe.
207   buf[bytes_read] = '\0';
208   // Read the page count.
209   int64 pages = strtoull(buf, NULL, 10);  // NOLINT
210
211   return pages;
212 }
213
214 int64 OsLayer::FindFreeMemSize() {
215   int64 size = 0;
216   int64 minsize = 0;
217   if (totalmemsize_ > 0)
218     return totalmemsize_;
219
220   int64 pages = sysconf(_SC_PHYS_PAGES);
221   int64 avpages = sysconf(_SC_AVPHYS_PAGES);
222   int64 pagesize = sysconf(_SC_PAGESIZE);
223   int64 physsize = pages * pagesize;
224   int64 avphyssize = avpages * pagesize;
225
226   // Assume 2MB hugepages.
227   int64 hugepagesize = FindHugePages() * 2 * kMegabyte;
228
229   if ((pages == -1) || (pagesize == -1)) {
230     logprintf(0, "Process Error: sysconf could not determine memory size.\n");
231     return 0;
232   }
233
234   // We want to leave enough stuff for things to run.
235   // If more than 2GB is present, leave 192M + 5% for other stuff.
236   // If less than 2GB is present use 85% of what's available.
237   // These are fairly arbitrary numbers that seem to work OK.
238   //
239   // TODO(nsanders): is there a more correct way to determine target
240   // memory size?
241   if (physsize < 2048LL * kMegabyte)
242     minsize = ((pages * 85) / 100) * pagesize;
243   else
244     minsize = ((pages * 95) / 100) * pagesize - (192 * kMegabyte);
245
246   // Use hugepage sizing if available.
247   if (hugepagesize > 0) {
248     if (hugepagesize < minsize) {
249       logprintf(0, "Procedural Error: Not enough hugepages. "
250                    "%lldMB available < %lldMB required.\n",
251                 hugepagesize / kMegabyte,
252                 minsize / kMegabyte);
253       // Require the calculated minimum amount of memory.
254       size = minsize;
255     } else {
256       // Require that we get all hugepages.
257       size = hugepagesize;
258     }
259   } else {
260     // Require the calculated minimum amount of memory.
261     size = minsize;
262   }
263
264   logprintf(5, "Log: Total %lld MB. Free %lld MB. Hugepages %lld MB. "
265                "Targeting %lld MB (%lld%%)\n",
266             physsize / kMegabyte,
267             avphyssize / kMegabyte,
268             hugepagesize / kMegabyte,
269             size / kMegabyte,
270             size * 100 / physsize);
271
272   totalmemsize_ = size;
273   return size;
274 }
275
276 // Allocates all memory available.
277 int64 OsLayer::AllocateAllMem() {
278   int64 length = FindFreeMemSize();
279   bool retval = AllocateTestMem(length, 0);
280   if (retval)
281     return length;
282   else
283     return 0;
284 }
285
286 // Allocate the target memory. This may be from malloc, hugepage pool
287 // or other platform specific sources.
288 bool OsLayer::AllocateTestMem(int64 length, uint64 paddr_base) {
289   // Try hugepages first.
290   void *buf = 0;
291
292   if (paddr_base)
293     logprintf(0, "Process Error: non zero paddr_base %#llx is not supported,"
294               " ignore.\n", paddr_base);
295
296   {  // Allocate hugepage mapped memory.
297     int shmid;
298     void *shmaddr;
299
300     if ((shmid = shmget(2, length,
301             SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W)) < 0) {
302       int err = errno;
303       char errtxt[256] = "";
304       strerror_r(err, errtxt, sizeof(errtxt));
305       logprintf(12, "Log: failed to allocate shared mem object - err %d (%s)\n",
306                 err, errtxt);
307       goto hugepage_failover;
308     }
309
310     shmaddr = shmat(shmid, NULL, NULL);
311     if (shmaddr == reinterpret_cast<void*>(-1)) {
312       int err = errno;
313       char errtxt[256] = "";
314       shmctl(shmid, IPC_RMID, NULL);
315       strerror_r(err, errtxt, sizeof(errtxt));
316       logprintf(0, "Log: failed to attach shared mem object - err %d (%s).\n",
317                 err, errtxt);
318       goto hugepage_failover;
319     }
320     use_hugepages_ = true;
321     shmid_ = shmid;
322     buf = shmaddr;
323     logprintf(0, "Log: Using hugepages 0x%x at %p.\n", shmid, shmaddr);
324   }
325   hugepage_failover:
326
327
328   if (!use_hugepages_) {
329     // Use memalign to ensure that blocks are aligned enough for disk direct IO.
330     buf = static_cast<char*>(memalign(4096, length));
331     if (buf)
332       logprintf(0, "Log: Using memaligned allocation at %p.\n", buf);
333     else
334       logprintf(0, "Process Error: memalign returned 0\n");
335   }
336
337   testmem_ = buf;
338   if (buf) {
339     testmemsize_ = length;
340   } else {
341     testmemsize_ = 0;
342   }
343
344   return (buf != 0);
345 }
346
347 // Free the test memory.
348 void OsLayer::FreeTestMem() {
349   if (testmem_) {
350     if (use_hugepages_) {
351       shmdt(testmem_);
352       shmctl(shmid_, IPC_RMID, NULL);
353     } else {
354       free(testmem_);
355     }
356     testmem_ = 0;
357     testmemsize_ = 0;
358   }
359 }
360
361
362 // Prepare the target memory. It may requre mapping in, or this may be a noop.
363 void *OsLayer::PrepareTestMem(uint64 offset, uint64 length) {
364   sat_assert((offset + length) <= testmemsize_);
365   return reinterpret_cast<void*>(reinterpret_cast<char*>(testmem_) + offset);
366 }
367
368 // Release the test memory resources, if any.
369 void OsLayer::ReleaseTestMem(void *addr, uint64 offset, uint64 length) {
370 }
371
372 // No error polling on unknown systems.
373 int OsLayer::ErrorPoll() {
374   return 0;
375 }
376
377 // Generally, poll for errors once per second.
378 void OsLayer::ErrorWait() {
379   sat_sleep(1);
380   return;
381 }
382
383 // Open a PCI bus-dev-func as a file and return its file descriptor.
384 // Error is indicated by return value less than zero.
385 int OsLayer::PciOpen(int bus, int device, int function) {
386   char dev_file[256];
387
388   snprintf(dev_file, sizeof(dev_file), "/proc/bus/pci/%02x/%02x.%x",
389            bus, device, function);
390
391   int fd = open(dev_file, O_RDWR);
392   if (fd == -1) {
393     logprintf(0, "Process Error: Unable to open PCI bus %d, device %d, "
394                  "function %d (errno %d).\n",
395               bus, device, function, errno);
396     return -1;
397   }
398
399   return fd;
400 }
401
402
403 // Read and write functions to access PCI config.
404 uint32 OsLayer::PciRead(int fd, uint32 offset, int width) {
405   // Strict aliasing rules lawyers will cause data corruption
406   // on cast pointers in some gccs.
407   union {
408     uint32 l32;
409     uint16 l16;
410     uint8 l8;
411   } datacast;
412   datacast.l32 = 0;
413   uint32 size = width / 8;
414
415   sat_assert((width == 32) || (width == 16) || (width == 8));
416   sat_assert(offset <= (256 - size));
417
418   if (lseek(fd, offset, SEEK_SET) < 0) {
419     logprintf(0, "Process Error: Can't seek %x\n", offset);
420     return 0;
421   }
422   if (read(fd, &datacast, size) != size) {
423     logprintf(0, "Process Error: Can't read %x\n", offset);
424     return 0;
425   }
426
427   // Extract the data.
428   switch (width) {
429     case 8:
430       sat_assert(&(datacast.l8) == reinterpret_cast<uint8*>(&datacast));
431       return datacast.l8;
432     case 16:
433       sat_assert(&(datacast.l16) == reinterpret_cast<uint16*>(&datacast));
434       return datacast.l16;
435     case 32:
436       return datacast.l32;
437   }
438   return 0;
439 }
440
441 void OsLayer::PciWrite(int fd, uint32 offset, uint32 value, int width) {
442   // Strict aliasing rules lawyers will cause data corruption
443   // on cast pointers in some gccs.
444   union {
445     uint32 l32;
446     uint16 l16;
447     uint8 l8;
448   } datacast;
449   datacast.l32 = 0;
450   uint32 size = width / 8;
451
452   sat_assert((width == 32) || (width == 16) || (width == 8));
453   sat_assert(offset <= (256 - size));
454
455   // Cram the data into the right alignment.
456   switch (width) {
457     case 8:
458       sat_assert(&(datacast.l8) == reinterpret_cast<uint8*>(&datacast));
459       datacast.l8 = value;
460     case 16:
461       sat_assert(&(datacast.l16) == reinterpret_cast<uint16*>(&datacast));
462       datacast.l16 = value;
463     case 32:
464       datacast.l32 = value;
465   }
466
467   if (lseek(fd, offset, SEEK_SET) < 0) {
468     logprintf(0, "Process Error: Can't seek %x\n", offset);
469     return;
470   }
471   if (write(fd, &datacast, size) != size) {
472     logprintf(0, "Process Error: Can't write %x to %x\n", datacast.l32, offset);
473     return;
474   }
475
476   return;
477 }
478
479
480
481 // Open dev msr.
482 int OsLayer::OpenMSR(uint32 core, uint32 address) {
483   char buf[256];
484   snprintf(buf, sizeof(buf), "/dev/cpu/%d/msr", core);
485   int fd = open(buf, O_RDWR);
486   if (fd < 0)
487     return fd;
488
489   uint32 pos = lseek(fd, address, SEEK_SET);
490   if (pos != address) {
491     close(fd);
492     logprintf(5, "Log: can't seek to msr %x, cpu %d\n", address, core);
493     return -1;
494   }
495
496   return fd;
497 }
498
499 bool OsLayer::ReadMSR(uint32 core, uint32 address, uint64 *data) {
500   int fd = OpenMSR(core, address);
501   if (fd < 0)
502     return false;
503
504   // Read from the msr.
505   bool res = (sizeof(*data) == read(fd, data, sizeof(*data)));
506
507   if (!res)
508     logprintf(5, "Log: Failed to read msr %x core %d\n", address, core);
509
510   close(fd);
511
512   return res;
513 }
514
515 bool OsLayer::WriteMSR(uint32 core, uint32 address, uint64 *data) {
516   int fd = OpenMSR(core, address);
517   if (fd < 0)
518     return false;
519
520   // Write to the msr
521   bool res = (sizeof(*data) == write(fd, data, sizeof(*data)));
522
523   if (!res)
524     logprintf(5, "Log: Failed to write msr %x core %d\n", address, core);
525
526   close(fd);
527
528   return res;
529 }
530
531 // Extract bits [n+len-1, n] from a 32 bit word.
532 // so GetBitField(0x0f00, 8, 4) == 0xf.
533 uint32 OsLayer::GetBitField(uint32 val, uint32 n, uint32 len) {
534   return (val >> n) & ((1<<len) - 1);
535 }
536
537 // Generic CPU stress workload that would work on any CPU/Platform.
538 // Float-point array moving average calculation.
539 bool OsLayer::CpuStressWorkload() {
540   double float_arr[100];
541   double sum = 0;
542   unsigned int seed = 12345;
543
544   // Initialize array with random numbers.
545   for (int i = 0; i < 100; i++) {
546     float_arr[i] = rand_r(&seed);
547     if (rand_r(&seed) % 2)
548       float_arr[i] *= -1.0;
549   }
550
551   // Calculate moving average.
552   for (int i = 0; i < 100000000; i++) {
553     float_arr[i % 100] =
554       (float_arr[i % 100] + float_arr[(i + 1) % 100] +
555        float_arr[(i + 99) % 100]) / 3;
556     sum += float_arr[i % 100];
557   }
558
559   // Artificial printf so the loops do not get optimized away.
560   if (sum == 0.0)
561     logprintf(12, "Log: I'm Feeling Lucky!\n");
562   return true;
563 }
564
565 PCIDevices OsLayer::GetPCIDevices() {
566   PCIDevices device_list;
567   DIR *dir;
568   struct dirent *buf = new struct dirent();
569   struct dirent *entry;
570   dir = opendir(kSysfsPath);
571   if (!dir)
572     logprintf(0, "Process Error: Cannot open %s", kSysfsPath);
573   while (readdir_r(dir, buf, &entry) == 0 && entry) {
574     PCIDevice *device;
575     unsigned int dev, func;
576     // ".", ".." or a special non-device perhaps.
577     if (entry->d_name[0] == '.')
578       continue;
579
580     device = new PCIDevice();
581     if (sscanf(entry->d_name, "%04x:%02hx:%02x.%d",
582                &device->domain, &device->bus, &dev, &func) < 4) {
583       logprintf(0, "Process Error: Couldn't parse %s", entry->d_name);
584       free(device);
585       continue;
586     }
587     device->dev = dev;
588     device->func = func;
589     device->vendor_id = PCIGetValue(entry->d_name, "vendor");
590     device->device_id = PCIGetValue(entry->d_name, "device");
591     PCIGetResources(entry->d_name, device);
592     device_list.insert(device_list.end(), device);
593   }
594   closedir(dir);
595   delete buf;
596   return device_list;
597 }
598
599 int OsLayer::PCIGetValue(string name, string object) {
600   int fd, len;
601   char filename[256];
602   char buf[256];
603   snprintf(filename, sizeof(filename), "%s/%s/%s", kSysfsPath,
604            name.c_str(), object.c_str());
605   fd = open(filename, O_RDONLY);
606   if (fd < 0)
607     return 0;
608   len = read(fd, buf, 256);
609   close(fd);
610   buf[len] = '\0';
611   return strtol(buf, NULL, 0);  // NOLINT
612 }
613
614 int OsLayer::PCIGetResources(string name, PCIDevice *device) {
615   char filename[256];
616   char buf[256];
617   FILE *file;
618   int64 start;
619   int64 end;
620   int64 size;
621   int i;
622   snprintf(filename, sizeof(filename), "%s/%s/%s", kSysfsPath,
623            name.c_str(), "resource");
624   file = fopen(filename, "r");
625   if (!file) {
626     logprintf(0, "Process Error: impossible to find resource file for %s",
627               filename);
628     return errno;
629   }
630   for (i = 0; i < 6; i++) {
631     if (!fgets(buf, 256, file))
632       break;
633     sscanf(buf, "%llx %llx", &start, &end);  // NOLINT
634     size = 0;
635     if (start)
636       size = end - start + 1;
637     device->base_addr[i] = start;
638     device->size[i] = size;
639   }
640   fclose(file);
641   return 0;
642 }