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> |
---|
18 | #include <limits.hfa> |
---|
19 | #include <fstream.hfa> |
---|
20 | #include <stdlib.hfa> |
---|
21 | |
---|
22 | #include <errno.h> |
---|
23 | #include <stdio.h> |
---|
24 | #include <string.h> |
---|
25 | #include <unistd.h> |
---|
26 | |
---|
27 | extern "C" { |
---|
28 | #include <dirent.h> |
---|
29 | #include <sys/types.h> |
---|
30 | #include <sys/stat.h> |
---|
31 | #include <sys/sysinfo.h> |
---|
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(); |
---|
119 | if( 0 == idxs ) return 0; |
---|
120 | |
---|
121 | unsigned found_level = 0; |
---|
122 | unsigned found = MAX; |
---|
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 | |
---|
140 | /* paranoid */ verify(found != MAX); |
---|
141 | return found; |
---|
142 | } |
---|
143 | |
---|
144 | int main() { |
---|
145 | //----------------------------------------------------------------------- |
---|
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 | |
---|
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 |
---|
182 | if( idx > 0 ) for(cpu_me; cpu_info.hthrd_count) { |
---|
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 | } |
---|
200 | } |
---|