1 /* Copyright (C) 2008 Intel Corporation
2 Author: Andi Kleen
3 Parse sysfs exported CPU cache topology
4
5 mcelog is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; version
8 2.
9
10 mcelog is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should find a copy of v2 of the GNU General Public License somewhere
16 on your Linux system; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18 #define _GNU_SOURCE 1
19 #include <string.h>
20 #include <dirent.h>
21 #include <stdio.h>
22 #include <sys/stat.h>
23 #include <assert.h>
24 #include <ctype.h>
25 #include "mcelog.h"
26 #include "memutil.h"
27 #include "sysfs.h"
28 #include "cache.h"
29
30 #ifdef __Linux__
31 struct cache {
32 unsigned level;
33 /* Numerical values must match MCACOD */
34 enum { INSTR, DATA, UNIFIED } type;
35 unsigned *cpumap;
36 unsigned cpumaplen;
37 };
38
39 struct cache **caches;
40 static unsigned cachelen;
41
42 #define PREFIX "/sys/devices/system/cpu"
43 #define MIN_CPUS 8
44 #define MIN_INDEX 4
45
46 static struct map type_map[] = {
47 { "Instruction", INSTR },
48 { "Data", DATA },
49 { "Unified", UNIFIED },
50 { },
51 };
52
more_cpus(unsigned cpu)53 static void more_cpus(unsigned cpu)
54 {
55 int old = cachelen;
56 if (!cachelen)
57 cachelen = MIN_CPUS/2;
58 if (cachelen < cpu)
59 cachelen = cpu + 1;
60 cachelen = cachelen * 2;
61 caches = xrealloc(caches, cachelen * sizeof(struct cache *));
62 memset(caches + old, 0, (cachelen - old) * sizeof(struct cache *));
63 }
64
cpumap_len(char * s)65 static unsigned cpumap_len(char *s)
66 {
67 unsigned len = 0, width = 0;
68 do {
69 if (isxdigit(*s))
70 width++;
71 else {
72 len += round_up(width * 4, BITS_PER_INT) / 8;
73 width = 0;
74 }
75 } while (*s++);
76 return len;
77 }
78
parse_cpumap(char * map,unsigned * buf,unsigned len)79 static void parse_cpumap(char *map, unsigned *buf, unsigned len)
80 {
81 char *s;
82 int c;
83
84 c = 0;
85 s = map + strlen(map);
86 for (;;) {
87 s = memrchr(map, ',', s - map);
88 if (!s)
89 s = map;
90 else
91 s++;
92 buf[c++] = strtoul(s, NULL, 16);
93 if (s == map)
94 break;
95 s--;
96 }
97 assert(len == c * sizeof(unsigned));
98 }
99
read_cpu_map(struct cache * c,char * cfn)100 static void read_cpu_map(struct cache *c, char *cfn)
101 {
102 char *map = read_field(cfn, "shared_cpu_map");
103 if (map[0] == 0) {
104 c->cpumap = NULL;
105 goto out;
106 }
107 c->cpumaplen = cpumap_len(map);
108 c->cpumap = xalloc(c->cpumaplen);
109 parse_cpumap(map, c->cpumap, c->cpumaplen);
110 out:
111 free(map);
112 }
113
read_caches(void)114 static int read_caches(void)
115 {
116 DIR *cpus = opendir(PREFIX);
117 struct dirent *de;
118 if (!cpus) {
119 Wprintf("Cannot read cache topology from %s", PREFIX);
120 return -1;
121 }
122 while ((de = readdir(cpus)) != NULL) {
123 unsigned cpu;
124 if (sscanf(de->d_name, "cpu%u", &cpu) == 1) {
125 struct stat st;
126 char *fn;
127 int i;
128 int numindex;
129
130 xasprintf(&fn, "%s/%s/cache", PREFIX, de->d_name);
131 if (!stat(fn, &st)) {
132 numindex = st.st_nlink - 2;
133 if (numindex < 0)
134 numindex = MIN_INDEX;
135 if (cpu >= cachelen)
136 more_cpus(cpu);
137 assert(cpu < cachelen);
138 caches[cpu] = xalloc(sizeof(struct cache) *
139 (numindex+1));
140 for (i = 0; i < numindex; i++) {
141 char *cfn;
142 struct cache *c = caches[cpu] + i;
143 xasprintf(&cfn, "%s/index%d", fn, i);
144 c->type = read_field_map(cfn, "type", type_map);
145 c->level = read_field_num(cfn, "level");
146 read_cpu_map(c, cfn);
147 free(cfn);
148 }
149 }
150 free(fn);
151 }
152 }
153 closedir(cpus);
154 return 0;
155 }
156
cache_to_cpus(int cpu,unsigned level,unsigned type,int * cpulen,unsigned ** cpumap)157 int cache_to_cpus(int cpu, unsigned level, unsigned type,
158 int *cpulen, unsigned **cpumap)
159 {
160 struct cache *c;
161 if (!caches) {
162 if (read_caches() < 0)
163 return -1;
164 if (!caches) {
165 Wprintf("No caches found in sysfs");
166 return -1;
167 }
168 }
169 for (c = caches[cpu]; c && c->cpumap; c++) {
170 //printf("%d level %d type %d\n", cpu, c->level, c->type);
171 if (c->level == level && (c->type == type || c->type == UNIFIED)) {
172 *cpumap = c->cpumap;
173 *cpulen = c->cpumaplen;
174 return 0;
175 }
176 }
177 Wprintf("Cannot find sysfs cache for CPU %d", cpu);
178 return -1;
179 }
180 #endif
181
182 #ifdef __FreeBSD__
cache_to_cpus(int cpu,unsigned level,unsigned type,int * cpulen,unsigned ** cpumap)183 int cache_to_cpus(int cpu, unsigned level, unsigned type,
184 int *cpulen, unsigned **cpumap)
185 {
186 return -1;
187 }
188 #endif
189
190 #ifdef TEST
main()191 main()
192 {
193 int cpulen;
194 unsigned *cpumap;
195 cache_to_cpus(1, 1, INSTR, &cpulen, &cpumap); printf("%d %x\n", cpulen, cpumap[0]);
196 cache_to_cpus(1, 1, DATA, &cpulen, &cpumap); printf("%d %x\n", cpulen, cpumap[0]);
197 cache_to_cpus(1, 2, UNIFIED, &cpulen, &cpumap); printf("%d %x\n", cpulen, cpumap[0]);
198 cache_to_cpus(0, 1, INSTR, &cpulen, &cpumap); printf("%d %x\n", cpulen, cpumap[0]);
199 cache_to_cpus(0, 1, DATA, &cpulen, &cpumap); printf("%d %x\n", cpulen, cpumap[0]);
200 cache_to_cpus(0, 2, UNIFIED, &cpulen, &cpumap); printf("%d %x\n", cpulen, cpumap[0]);
201 }
202 #endif
203
204