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