[d363634] | 1 | // |
---|
| 2 | // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo |
---|
| 3 | // |
---|
| 4 | // The contents of this file are covered under the licence agreement in the |
---|
| 5 | // file "LICENCE" distributed with Cforall. |
---|
| 6 | // |
---|
| 7 | // cpu.cfa -- checks that libcfa correctly counts the number of cpus. |
---|
| 8 | // |
---|
| 9 | // Author : Thierry Delisle |
---|
| 10 | // Created On : Mon Jun 14 13:59:01 2021 |
---|
| 11 | // Last Modified By : |
---|
| 12 | // Last Modified On : |
---|
| 13 | // Update Count : |
---|
| 14 | // |
---|
| 15 | |
---|
| 16 | |
---|
| 17 | #include <device/cpu.hfa> |
---|
[145dcd5] | 18 | #include <limits.hfa> |
---|
| 19 | #include <fstream.hfa> |
---|
[cf85f96] | 20 | #include <stdlib.hfa> |
---|
| 21 | |
---|
| 22 | #include <errno.h> |
---|
| 23 | #include <stdio.h> |
---|
| 24 | #include <string.h> |
---|
| 25 | #include <unistd.h> |
---|
| 26 | |
---|
[d363634] | 27 | extern "C" { |
---|
[cf85f96] | 28 | #include <dirent.h> |
---|
| 29 | #include <sys/types.h> |
---|
| 30 | #include <sys/stat.h> |
---|
[d363634] | 31 | #include <sys/sysinfo.h> |
---|
[cf85f96] | 32 | #include <fcntl.h> |
---|
| 33 | } |
---|
| 34 | |
---|
| 35 | // go through a directory calling fn on each file |
---|
| 36 | static int iterate_dir( const char * path, void (*fn)(struct dirent * ent) ) { |
---|
| 37 | // open the directory |
---|
| 38 | DIR *dir = opendir(path); |
---|
| 39 | if(dir == 0p) { return ENOTDIR; } |
---|
| 40 | |
---|
| 41 | // call fn for each |
---|
| 42 | struct dirent * ent; |
---|
| 43 | while ((ent = readdir(dir)) != 0p) { |
---|
| 44 | fn( ent ); |
---|
| 45 | } |
---|
| 46 | |
---|
| 47 | // no longer need this |
---|
| 48 | closedir(dir); |
---|
| 49 | return 0; |
---|
| 50 | } |
---|
| 51 | |
---|
| 52 | // count the number of directories with the specified prefix |
---|
| 53 | // the directories counted have the form '[prefix]N' where prefix is the parameter |
---|
| 54 | // and N is an base 10 integer. |
---|
| 55 | static int count_prefix_dirs(const char * path, const char * prefix) { |
---|
| 56 | // read the directory and find the cpu count |
---|
| 57 | // and make sure everything is as expected |
---|
| 58 | int max = -1; |
---|
| 59 | int count = 0; |
---|
| 60 | void lambda(struct dirent * ent) { |
---|
| 61 | // were are looking for prefixX, where X is a number |
---|
| 62 | // check that it starts with 'cpu |
---|
| 63 | char * s = strstr(ent->d_name, prefix); |
---|
| 64 | if(s == 0p) { return; } |
---|
| 65 | if(s != ent->d_name) { return; } |
---|
| 66 | |
---|
| 67 | // check that the next part is a number |
---|
| 68 | s += strlen(prefix); |
---|
| 69 | char * end; |
---|
| 70 | long int val = strtol(s, &end, 10); |
---|
| 71 | if(*end != '\0' || val < 0) { return; } |
---|
| 72 | |
---|
| 73 | // check that it's a directory |
---|
| 74 | if(ent->d_type != DT_DIR) { return; } |
---|
| 75 | |
---|
| 76 | // it's a match! |
---|
| 77 | max = max(val, max); |
---|
| 78 | count++; |
---|
| 79 | } |
---|
| 80 | iterate_dir(path, lambda); |
---|
| 81 | |
---|
| 82 | /* paranoid */ verifyf(count == max + 1, "Inconsistent %s count, counted %d, but max %s was %d", prefix, count, prefix, (int)max); |
---|
| 83 | |
---|
| 84 | return count; |
---|
| 85 | } |
---|
| 86 | |
---|
| 87 | // Count number of cache *indexes* in the system |
---|
| 88 | // cache indexes are distinct from cache level as Data or Instruction cache |
---|
| 89 | // can share a level but not an index |
---|
| 90 | // PITFALL: assumes all cpus have the same indexes as cpu0 |
---|
| 91 | static int count_cache_indexes(void) { |
---|
| 92 | return count_prefix_dirs("/sys/devices/system/cpu/cpu0/cache", "index"); |
---|
| 93 | } |
---|
| 94 | |
---|
| 95 | // read information about a spcficic cache index/cpu file into the output buffer |
---|
| 96 | static size_t read_cpuidxinfo_into(unsigned cpu, unsigned idx, const char * file, char * out, size_t out_len) { |
---|
| 97 | // Pick the file we want and read it |
---|
| 98 | char buf[128]; |
---|
| 99 | /* paranoid */ __attribute__((unused)) int len = |
---|
| 100 | snprintf(buf, 128, "/sys/devices/system/cpu/cpu%u/cache/index%u/%s", cpu, idx, file); |
---|
| 101 | /* paranoid */ verifyf(len > 0, "Could not generate '%s' filename for cpu %u, index %u", file, cpu, idx); |
---|
| 102 | |
---|
| 103 | int fd = open(buf, 0, O_RDONLY); |
---|
| 104 | /* paranoid */ verifyf(fd > 0, "Could not open file '%s'", buf); |
---|
| 105 | |
---|
| 106 | ssize_t r = read(fd, out, out_len); |
---|
| 107 | /* paranoid */ verifyf(r > 0, "Could not read file '%s'", buf); |
---|
| 108 | |
---|
| 109 | /* paranoid */ __attribute__((unused)) int ret = |
---|
| 110 | close(fd); |
---|
| 111 | /* paranoid */ verifyf(ret == 0, "Could not close file '%s'", buf); |
---|
| 112 | |
---|
| 113 | out[r-1] = '\0'; |
---|
| 114 | return r-1; |
---|
| 115 | } |
---|
| 116 | |
---|
| 117 | unsigned find_idx() { |
---|
| 118 | int idxs = count_cache_indexes(); |
---|
[72b5805e] | 119 | if( 0 == idxs ) return 0; |
---|
[cf85f96] | 120 | |
---|
| 121 | unsigned found_level = 0; |
---|
[145dcd5] | 122 | unsigned found = MAX; |
---|
[cf85f96] | 123 | for(i; idxs) { |
---|
| 124 | unsigned idx = idxs - 1 - i; |
---|
| 125 | char buf[32]; |
---|
| 126 | |
---|
| 127 | // Level is the cache level: higher means bigger and slower |
---|
| 128 | read_cpuidxinfo_into(0, idx, "level", buf, 32); |
---|
| 129 | char * end; |
---|
| 130 | unsigned long level = strtoul(buf, &end, 10); |
---|
| 131 | /* paranoid */ verifyf(level <= 250, "Cpu %u has more than 250 levels of cache, that doesn't sound right", 0); |
---|
| 132 | /* paranoid */ verify(*end == '\0'); |
---|
| 133 | |
---|
| 134 | if(found_level < level) { |
---|
| 135 | found_level = level; |
---|
| 136 | found = idx; |
---|
| 137 | } |
---|
| 138 | } |
---|
| 139 | |
---|
[145dcd5] | 140 | /* paranoid */ verify(found != MAX); |
---|
[cf85f96] | 141 | return found; |
---|
[d363634] | 142 | } |
---|
| 143 | |
---|
| 144 | int main() { |
---|
[cf85f96] | 145 | //----------------------------------------------------------------------- |
---|
[d363634] | 146 | int ret1 = get_nprocs(); |
---|
| 147 | int ret2 = cpu_info.hthrd_count; |
---|
| 148 | if(ret1 != ret2) { |
---|
| 149 | sout | "No match! libcfa says" | ret2 | "hardware threads but linux counts" | ret1 | "processors"; |
---|
| 150 | } |
---|
| 151 | else { |
---|
| 152 | sout | "Match!"; |
---|
| 153 | } |
---|
| 154 | |
---|
[cf85f96] | 155 | //----------------------------------------------------------------------- |
---|
| 156 | // Make sure no one has the same self |
---|
| 157 | for(ime; cpu_info.hthrd_count) { |
---|
| 158 | unsigned me = cpu_info.llc_map[ime].self; |
---|
| 159 | { |
---|
| 160 | unsigned s = cpu_info.llc_map[ime].start; |
---|
| 161 | unsigned e = s + cpu_info.llc_map[ime].count; |
---|
| 162 | if(me < s || me >= e) { |
---|
| 163 | sout | "CPU" | ime | "outside of it's own map: " | s | "<=" | me | "<" | e; |
---|
| 164 | } |
---|
| 165 | } |
---|
| 166 | |
---|
| 167 | |
---|
| 168 | for(ithem; cpu_info.hthrd_count) { |
---|
| 169 | if(ime == ithem) continue; |
---|
| 170 | |
---|
| 171 | unsigned them = cpu_info.llc_map[ithem].self; |
---|
| 172 | if(me == them) { |
---|
| 173 | sout | "CPU" | ime | "has conflicting self id with" | ithem | "(" | me | ")"; |
---|
| 174 | } |
---|
| 175 | } |
---|
| 176 | } |
---|
| 177 | |
---|
| 178 | |
---|
| 179 | //----------------------------------------------------------------------- |
---|
| 180 | unsigned idx = find_idx(); |
---|
| 181 | // For all procs check mapping is consistent |
---|
[72b5805e] | 182 | if( idx > 0 ) for(cpu_me; cpu_info.hthrd_count) { |
---|
[cf85f96] | 183 | char buf_me[32]; |
---|
| 184 | size_t len_me = read_cpuidxinfo_into(cpu_me, idx, "shared_cpu_list", buf_me, 32); |
---|
| 185 | for(cpu_them; cpu_info.hthrd_count) { |
---|
| 186 | if(cpu_me == cpu_them) continue; |
---|
| 187 | char buf_them[32]; |
---|
| 188 | size_t len_them = read_cpuidxinfo_into(cpu_them, idx, "shared_cpu_list", buf_them, 32); |
---|
| 189 | |
---|
| 190 | bool match_file = len_them == len_me && 0 == strncmp(buf_them, buf_me, len_me); |
---|
| 191 | bool match_info = cpu_info.llc_map[cpu_me].start == cpu_info.llc_map[cpu_them].start && cpu_info.llc_map[cpu_me].count == cpu_info.llc_map[cpu_them].count; |
---|
| 192 | |
---|
| 193 | if(match_file != match_info) { |
---|
| 194 | sout | "CPU" | cpu_me | "and" | cpu_them | "have inconsitent file and cpu_info"; |
---|
| 195 | sout | cpu_me | ": <" | cpu_info.llc_map[cpu_me ].start | "," | cpu_info.llc_map[cpu_me ].count | "> '" | buf_me | "'"; |
---|
| 196 | sout | cpu_me | ": <" | cpu_info.llc_map[cpu_them].start | "," | cpu_info.llc_map[cpu_them].count | "> '" | buf_them | "'"; |
---|
| 197 | } |
---|
| 198 | } |
---|
| 199 | } |
---|
[72b5805e] | 200 | } |
---|