blob: e9f0b928b0a9925e534ddeadb3c868493240df58 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Routines to indentify caches on Intel CPU.
3 *
4 * Changes:
5 * Venkatesh Pallipadi : Adding cache identification through cpuid(4)
Ashok Raj1aa1a9f2005-10-30 14:59:50 -08006 * Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure.
Andi Kleen240cd6a802006-06-26 13:56:13 +02007 * Andi Kleen : CPUID4 emulation on AMD.
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 */
9
10#include <linux/init.h>
11#include <linux/slab.h>
12#include <linux/device.h>
13#include <linux/compiler.h>
14#include <linux/cpu.h>
Tim Schmielau4e57b682005-10-30 15:03:48 -080015#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
17#include <asm/processor.h>
18#include <asm/smp.h>
19
20#define LVL_1_INST 1
21#define LVL_1_DATA 2
22#define LVL_2 3
23#define LVL_3 4
24#define LVL_TRACE 5
25
26struct _cache_table
27{
28 unsigned char descriptor;
29 char cache_type;
30 short size;
31};
32
33/* all the cache descriptor types we care about (no TLB or trace cache entries) */
Ashok Raj1aa1a9f2005-10-30 14:59:50 -080034static struct _cache_table cache_table[] __cpuinitdata =
Linus Torvalds1da177e2005-04-16 15:20:36 -070035{
36 { 0x06, LVL_1_INST, 8 }, /* 4-way set assoc, 32 byte line size */
37 { 0x08, LVL_1_INST, 16 }, /* 4-way set assoc, 32 byte line size */
38 { 0x0a, LVL_1_DATA, 8 }, /* 2 way set assoc, 32 byte line size */
39 { 0x0c, LVL_1_DATA, 16 }, /* 4-way set assoc, 32 byte line size */
40 { 0x22, LVL_3, 512 }, /* 4-way set assoc, sectored cache, 64 byte line size */
41 { 0x23, LVL_3, 1024 }, /* 8-way set assoc, sectored cache, 64 byte line size */
42 { 0x25, LVL_3, 2048 }, /* 8-way set assoc, sectored cache, 64 byte line size */
43 { 0x29, LVL_3, 4096 }, /* 8-way set assoc, sectored cache, 64 byte line size */
44 { 0x2c, LVL_1_DATA, 32 }, /* 8-way set assoc, 64 byte line size */
45 { 0x30, LVL_1_INST, 32 }, /* 8-way set assoc, 64 byte line size */
46 { 0x39, LVL_2, 128 }, /* 4-way set assoc, sectored cache, 64 byte line size */
Dave Jones6fe8f472006-01-26 22:40:40 -080047 { 0x3a, LVL_2, 192 }, /* 6-way set assoc, sectored cache, 64 byte line size */
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 { 0x3b, LVL_2, 128 }, /* 2-way set assoc, sectored cache, 64 byte line size */
49 { 0x3c, LVL_2, 256 }, /* 4-way set assoc, sectored cache, 64 byte line size */
Dave Jones6fe8f472006-01-26 22:40:40 -080050 { 0x3d, LVL_2, 384 }, /* 6-way set assoc, sectored cache, 64 byte line size */
51 { 0x3e, LVL_2, 512 }, /* 4-way set assoc, sectored cache, 64 byte line size */
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 { 0x41, LVL_2, 128 }, /* 4-way set assoc, 32 byte line size */
53 { 0x42, LVL_2, 256 }, /* 4-way set assoc, 32 byte line size */
54 { 0x43, LVL_2, 512 }, /* 4-way set assoc, 32 byte line size */
55 { 0x44, LVL_2, 1024 }, /* 4-way set assoc, 32 byte line size */
56 { 0x45, LVL_2, 2048 }, /* 4-way set assoc, 32 byte line size */
Dave Jones6fe8f472006-01-26 22:40:40 -080057 { 0x46, LVL_3, 4096 }, /* 4-way set assoc, 64 byte line size */
58 { 0x47, LVL_3, 8192 }, /* 8-way set assoc, 64 byte line size */
59 { 0x49, LVL_3, 4096 }, /* 16-way set assoc, 64 byte line size */
60 { 0x4a, LVL_3, 6144 }, /* 12-way set assoc, 64 byte line size */
61 { 0x4b, LVL_3, 8192 }, /* 16-way set assoc, 64 byte line size */
62 { 0x4c, LVL_3, 12288 }, /* 12-way set assoc, 64 byte line size */
63 { 0x4d, LVL_3, 16384 }, /* 16-way set assoc, 64 byte line size */
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 { 0x60, LVL_1_DATA, 16 }, /* 8-way set assoc, sectored cache, 64 byte line size */
65 { 0x66, LVL_1_DATA, 8 }, /* 4-way set assoc, sectored cache, 64 byte line size */
66 { 0x67, LVL_1_DATA, 16 }, /* 4-way set assoc, sectored cache, 64 byte line size */
67 { 0x68, LVL_1_DATA, 32 }, /* 4-way set assoc, sectored cache, 64 byte line size */
68 { 0x70, LVL_TRACE, 12 }, /* 8-way set assoc */
69 { 0x71, LVL_TRACE, 16 }, /* 8-way set assoc */
70 { 0x72, LVL_TRACE, 32 }, /* 8-way set assoc */
Dave Jones6fe8f472006-01-26 22:40:40 -080071 { 0x73, LVL_TRACE, 64 }, /* 8-way set assoc */
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 { 0x78, LVL_2, 1024 }, /* 4-way set assoc, 64 byte line size */
73 { 0x79, LVL_2, 128 }, /* 8-way set assoc, sectored cache, 64 byte line size */
74 { 0x7a, LVL_2, 256 }, /* 8-way set assoc, sectored cache, 64 byte line size */
75 { 0x7b, LVL_2, 512 }, /* 8-way set assoc, sectored cache, 64 byte line size */
76 { 0x7c, LVL_2, 1024 }, /* 8-way set assoc, sectored cache, 64 byte line size */
77 { 0x7d, LVL_2, 2048 }, /* 8-way set assoc, 64 byte line size */
78 { 0x7f, LVL_2, 512 }, /* 2-way set assoc, 64 byte line size */
79 { 0x82, LVL_2, 256 }, /* 8-way set assoc, 32 byte line size */
80 { 0x83, LVL_2, 512 }, /* 8-way set assoc, 32 byte line size */
81 { 0x84, LVL_2, 1024 }, /* 8-way set assoc, 32 byte line size */
82 { 0x85, LVL_2, 2048 }, /* 8-way set assoc, 32 byte line size */
83 { 0x86, LVL_2, 512 }, /* 4-way set assoc, 64 byte line size */
84 { 0x87, LVL_2, 1024 }, /* 8-way set assoc, 64 byte line size */
85 { 0x00, 0, 0}
86};
87
88
89enum _cache_type
90{
91 CACHE_TYPE_NULL = 0,
92 CACHE_TYPE_DATA = 1,
93 CACHE_TYPE_INST = 2,
94 CACHE_TYPE_UNIFIED = 3
95};
96
97union _cpuid4_leaf_eax {
98 struct {
99 enum _cache_type type:5;
100 unsigned int level:3;
101 unsigned int is_self_initializing:1;
102 unsigned int is_fully_associative:1;
103 unsigned int reserved:4;
104 unsigned int num_threads_sharing:12;
105 unsigned int num_cores_on_die:6;
106 } split;
107 u32 full;
108};
109
110union _cpuid4_leaf_ebx {
111 struct {
112 unsigned int coherency_line_size:12;
113 unsigned int physical_line_partition:10;
114 unsigned int ways_of_associativity:10;
115 } split;
116 u32 full;
117};
118
119union _cpuid4_leaf_ecx {
120 struct {
121 unsigned int number_of_sets:32;
122 } split;
123 u32 full;
124};
125
126struct _cpuid4_info {
127 union _cpuid4_leaf_eax eax;
128 union _cpuid4_leaf_ebx ebx;
129 union _cpuid4_leaf_ecx ecx;
130 unsigned long size;
131 cpumask_t shared_cpu_map;
132};
133
Andi Kleen240cd6a802006-06-26 13:56:13 +0200134unsigned short num_cache_leaves;
135
136/* AMD doesn't have CPUID4. Emulate it here to report the same
137 information to the user. This makes some assumptions about the machine:
138 No L3, L2 not shared, no SMT etc. that is currently true on AMD CPUs.
139
140 In theory the TLBs could be reported as fake type (they are in "dummy").
141 Maybe later */
142union l1_cache {
143 struct {
144 unsigned line_size : 8;
145 unsigned lines_per_tag : 8;
146 unsigned assoc : 8;
147 unsigned size_in_kb : 8;
148 };
149 unsigned val;
150};
151
152union l2_cache {
153 struct {
154 unsigned line_size : 8;
155 unsigned lines_per_tag : 4;
156 unsigned assoc : 4;
157 unsigned size_in_kb : 16;
158 };
159 unsigned val;
160};
161
Andreas Mohr7f35bf92006-06-27 02:53:45 -0700162static const unsigned short assocs[] = {
Andi Kleen240cd6a802006-06-26 13:56:13 +0200163 [1] = 1, [2] = 2, [4] = 4, [6] = 8,
164 [8] = 16,
165 [0xf] = 0xffff // ??
166 };
Andreas Mohr7f35bf92006-06-27 02:53:45 -0700167static const unsigned char levels[] = { 1, 1, 2 };
168static const unsigned char types[] = { 1, 2, 3 };
Andi Kleen240cd6a802006-06-26 13:56:13 +0200169
170static void __cpuinit amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax,
171 union _cpuid4_leaf_ebx *ebx,
172 union _cpuid4_leaf_ecx *ecx)
173{
174 unsigned dummy;
175 unsigned line_size, lines_per_tag, assoc, size_in_kb;
176 union l1_cache l1i, l1d;
177 union l2_cache l2;
178
179 eax->full = 0;
180 ebx->full = 0;
181 ecx->full = 0;
182
183 cpuid(0x80000005, &dummy, &dummy, &l1d.val, &l1i.val);
184 cpuid(0x80000006, &dummy, &dummy, &l2.val, &dummy);
185
186 if (leaf > 2 || !l1d.val || !l1i.val || !l2.val)
187 return;
188
189 eax->split.is_self_initializing = 1;
190 eax->split.type = types[leaf];
191 eax->split.level = levels[leaf];
192 eax->split.num_threads_sharing = 0;
193 eax->split.num_cores_on_die = current_cpu_data.x86_max_cores - 1;
194
195 if (leaf <= 1) {
196 union l1_cache *l1 = leaf == 0 ? &l1d : &l1i;
197 assoc = l1->assoc;
198 line_size = l1->line_size;
199 lines_per_tag = l1->lines_per_tag;
200 size_in_kb = l1->size_in_kb;
201 } else {
202 assoc = l2.assoc;
203 line_size = l2.line_size;
204 lines_per_tag = l2.lines_per_tag;
205 /* cpu_data has errata corrections for K7 applied */
206 size_in_kb = current_cpu_data.x86_cache_size;
207 }
208
209 if (assoc == 0xf)
210 eax->split.is_fully_associative = 1;
211 ebx->split.coherency_line_size = line_size - 1;
212 ebx->split.ways_of_associativity = assocs[assoc] - 1;
213 ebx->split.physical_line_partition = lines_per_tag - 1;
214 ecx->split.number_of_sets = (size_in_kb * 1024) / line_size /
215 (ebx->split.ways_of_associativity + 1) - 1;
216}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800218static int __cpuinit cpuid4_cache_lookup(int index, struct _cpuid4_info *this_leaf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219{
Andi Kleen240cd6a802006-06-26 13:56:13 +0200220 union _cpuid4_leaf_eax eax;
221 union _cpuid4_leaf_ebx ebx;
222 union _cpuid4_leaf_ecx ecx;
223 unsigned edx;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224
Andi Kleen240cd6a802006-06-26 13:56:13 +0200225 if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
226 amd_cpuid4(index, &eax, &ebx, &ecx);
227 else
228 cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx);
229 if (eax.split.type == CACHE_TYPE_NULL)
Andi Kleene2cac782005-07-28 21:15:46 -0700230 return -EIO; /* better error ? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
Andi Kleen240cd6a802006-06-26 13:56:13 +0200232 this_leaf->eax = eax;
233 this_leaf->ebx = ebx;
234 this_leaf->ecx = ecx;
235 this_leaf->size = (ecx.split.number_of_sets + 1) *
236 (ebx.split.coherency_line_size + 1) *
237 (ebx.split.physical_line_partition + 1) *
238 (ebx.split.ways_of_associativity + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 return 0;
240}
241
Chuck Ebbertfe38d852006-02-04 23:28:03 -0800242/* will only be called once; __init is safe here */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243static int __init find_num_cache_leaves(void)
244{
245 unsigned int eax, ebx, ecx, edx;
246 union _cpuid4_leaf_eax cache_eax;
Siddha, Suresh Bd16aafff2005-10-30 14:59:30 -0800247 int i = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
Siddha, Suresh Bd16aafff2005-10-30 14:59:30 -0800249 do {
250 ++i;
251 /* Do cpuid(4) loop to find out num_cache_leaves */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 cpuid_count(4, i, &eax, &ebx, &ecx, &edx);
253 cache_eax.full = eax;
Siddha, Suresh Bd16aafff2005-10-30 14:59:30 -0800254 } while (cache_eax.split.type != CACHE_TYPE_NULL);
255 return i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256}
257
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800258unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259{
260 unsigned int trace = 0, l1i = 0, l1d = 0, l2 = 0, l3 = 0; /* Cache sizes */
261 unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */
262 unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */
Siddha, Suresh B1e9f28f2006-03-27 01:15:22 -0800263 unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb;
James Bottomley96c52742006-06-27 02:53:49 -0700264#ifdef CONFIG_X86_HT
Siddha, Suresh B1e9f28f2006-03-27 01:15:22 -0800265 unsigned int cpu = (c == &boot_cpu_data) ? 0 : (c - cpu_data);
266#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
Shaohua Lif2d0d262006-03-23 02:59:52 -0800268 if (c->cpuid_level > 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 static int is_initialized;
270
271 if (is_initialized == 0) {
272 /* Init num_cache_leaves from boot CPU */
273 num_cache_leaves = find_num_cache_leaves();
274 is_initialized++;
275 }
276
277 /*
278 * Whenever possible use cpuid(4), deterministic cache
279 * parameters cpuid leaf to find the cache details
280 */
281 for (i = 0; i < num_cache_leaves; i++) {
282 struct _cpuid4_info this_leaf;
283
284 int retval;
285
286 retval = cpuid4_cache_lookup(i, &this_leaf);
287 if (retval >= 0) {
288 switch(this_leaf.eax.split.level) {
289 case 1:
290 if (this_leaf.eax.split.type ==
291 CACHE_TYPE_DATA)
292 new_l1d = this_leaf.size/1024;
293 else if (this_leaf.eax.split.type ==
294 CACHE_TYPE_INST)
295 new_l1i = this_leaf.size/1024;
296 break;
297 case 2:
298 new_l2 = this_leaf.size/1024;
Siddha, Suresh B1e9f28f2006-03-27 01:15:22 -0800299 num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
300 index_msb = get_count_order(num_threads_sharing);
301 l2_id = c->apicid >> index_msb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 break;
303 case 3:
304 new_l3 = this_leaf.size/1024;
Siddha, Suresh B1e9f28f2006-03-27 01:15:22 -0800305 num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
306 index_msb = get_count_order(num_threads_sharing);
307 l3_id = c->apicid >> index_msb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 break;
309 default:
310 break;
311 }
312 }
313 }
314 }
Shaohua Lib06be912006-03-27 01:15:24 -0800315 /*
316 * Don't use cpuid2 if cpuid4 is supported. For P4, we use cpuid2 for
317 * trace cache
318 */
319 if ((num_cache_leaves == 0 || c->x86 == 15) && c->cpuid_level > 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 /* supports eax=2 call */
321 int i, j, n;
322 int regs[4];
323 unsigned char *dp = (unsigned char *)regs;
Shaohua Lib06be912006-03-27 01:15:24 -0800324 int only_trace = 0;
325
326 if (num_cache_leaves != 0 && c->x86 == 15)
327 only_trace = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328
329 /* Number of times to iterate */
330 n = cpuid_eax(2) & 0xFF;
331
332 for ( i = 0 ; i < n ; i++ ) {
333 cpuid(2, &regs[0], &regs[1], &regs[2], &regs[3]);
334
335 /* If bit 31 is set, this is an unknown format */
336 for ( j = 0 ; j < 3 ; j++ ) {
337 if ( regs[j] < 0 ) regs[j] = 0;
338 }
339
340 /* Byte 0 is level count, not a descriptor */
341 for ( j = 1 ; j < 16 ; j++ ) {
342 unsigned char des = dp[j];
343 unsigned char k = 0;
344
345 /* look up this descriptor in the table */
346 while (cache_table[k].descriptor != 0)
347 {
348 if (cache_table[k].descriptor == des) {
Shaohua Lib06be912006-03-27 01:15:24 -0800349 if (only_trace && cache_table[k].cache_type != LVL_TRACE)
350 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 switch (cache_table[k].cache_type) {
352 case LVL_1_INST:
353 l1i += cache_table[k].size;
354 break;
355 case LVL_1_DATA:
356 l1d += cache_table[k].size;
357 break;
358 case LVL_2:
359 l2 += cache_table[k].size;
360 break;
361 case LVL_3:
362 l3 += cache_table[k].size;
363 break;
364 case LVL_TRACE:
365 trace += cache_table[k].size;
366 break;
367 }
368
369 break;
370 }
371
372 k++;
373 }
374 }
375 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 }
377
Shaohua Lib06be912006-03-27 01:15:24 -0800378 if (new_l1d)
379 l1d = new_l1d;
380
381 if (new_l1i)
382 l1i = new_l1i;
383
384 if (new_l2) {
385 l2 = new_l2;
James Bottomley96c52742006-06-27 02:53:49 -0700386#ifdef CONFIG_X86_HT
Shaohua Lib06be912006-03-27 01:15:24 -0800387 cpu_llc_id[cpu] = l2_id;
388#endif
389 }
390
391 if (new_l3) {
392 l3 = new_l3;
James Bottomley96c52742006-06-27 02:53:49 -0700393#ifdef CONFIG_X86_HT
Shaohua Lib06be912006-03-27 01:15:24 -0800394 cpu_llc_id[cpu] = l3_id;
395#endif
396 }
397
398 if (trace)
399 printk (KERN_INFO "CPU: Trace cache: %dK uops", trace);
400 else if ( l1i )
401 printk (KERN_INFO "CPU: L1 I cache: %dK", l1i);
402
403 if (l1d)
404 printk(", L1 D cache: %dK\n", l1d);
405 else
406 printk("\n");
407
408 if (l2)
409 printk(KERN_INFO "CPU: L2 cache: %dK\n", l2);
410
411 if (l3)
412 printk(KERN_INFO "CPU: L3 cache: %dK\n", l3);
413
414 c->x86_cache_size = l3 ? l3 : (l2 ? l2 : (l1i+l1d));
415
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 return l2;
417}
418
419/* pointer to _cpuid4_info array (for each cache leaf) */
420static struct _cpuid4_info *cpuid4_info[NR_CPUS];
421#define CPUID4_INFO_IDX(x,y) (&((cpuid4_info[x])[y]))
422
423#ifdef CONFIG_SMP
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800424static void __cpuinit cache_shared_cpu_map_setup(unsigned int cpu, int index)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425{
Siddha, Suresh B2b091872005-11-05 17:25:54 +0100426 struct _cpuid4_info *this_leaf, *sibling_leaf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 unsigned long num_threads_sharing;
Siddha, Suresh B2b091872005-11-05 17:25:54 +0100428 int index_msb, i;
429 struct cpuinfo_x86 *c = cpu_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430
431 this_leaf = CPUID4_INFO_IDX(cpu, index);
432 num_threads_sharing = 1 + this_leaf->eax.split.num_threads_sharing;
433
434 if (num_threads_sharing == 1)
435 cpu_set(cpu, this_leaf->shared_cpu_map);
Siddha, Suresh B2b091872005-11-05 17:25:54 +0100436 else {
437 index_msb = get_count_order(num_threads_sharing);
438
439 for_each_online_cpu(i) {
440 if (c[i].apicid >> index_msb ==
441 c[cpu].apicid >> index_msb) {
442 cpu_set(i, this_leaf->shared_cpu_map);
443 if (i != cpu && cpuid4_info[i]) {
444 sibling_leaf = CPUID4_INFO_IDX(i, index);
445 cpu_set(cpu, sibling_leaf->shared_cpu_map);
446 }
447 }
448 }
449 }
450}
Chuck Ebbert3bc9b762006-03-23 02:59:33 -0800451static void __cpuinit cache_remove_shared_cpu_map(unsigned int cpu, int index)
Siddha, Suresh B2b091872005-11-05 17:25:54 +0100452{
453 struct _cpuid4_info *this_leaf, *sibling_leaf;
454 int sibling;
455
456 this_leaf = CPUID4_INFO_IDX(cpu, index);
457 for_each_cpu_mask(sibling, this_leaf->shared_cpu_map) {
458 sibling_leaf = CPUID4_INFO_IDX(sibling, index);
459 cpu_clear(cpu, sibling_leaf->shared_cpu_map);
460 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461}
462#else
463static void __init cache_shared_cpu_map_setup(unsigned int cpu, int index) {}
Siddha, Suresh B2b091872005-11-05 17:25:54 +0100464static void __init cache_remove_shared_cpu_map(unsigned int cpu, int index) {}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465#endif
466
467static void free_cache_attributes(unsigned int cpu)
468{
469 kfree(cpuid4_info[cpu]);
470 cpuid4_info[cpu] = NULL;
471}
472
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800473static int __cpuinit detect_cache_attributes(unsigned int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474{
475 struct _cpuid4_info *this_leaf;
476 unsigned long j;
477 int retval;
Andi Kleene2cac782005-07-28 21:15:46 -0700478 cpumask_t oldmask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
480 if (num_cache_leaves == 0)
481 return -ENOENT;
482
483 cpuid4_info[cpu] = kmalloc(
484 sizeof(struct _cpuid4_info) * num_cache_leaves, GFP_KERNEL);
485 if (unlikely(cpuid4_info[cpu] == NULL))
486 return -ENOMEM;
487 memset(cpuid4_info[cpu], 0,
488 sizeof(struct _cpuid4_info) * num_cache_leaves);
489
Andi Kleene2cac782005-07-28 21:15:46 -0700490 oldmask = current->cpus_allowed;
491 retval = set_cpus_allowed(current, cpumask_of_cpu(cpu));
492 if (retval)
493 goto out;
494
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 /* Do cpuid and store the results */
Andi Kleene2cac782005-07-28 21:15:46 -0700496 retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 for (j = 0; j < num_cache_leaves; j++) {
498 this_leaf = CPUID4_INFO_IDX(cpu, j);
499 retval = cpuid4_cache_lookup(j, this_leaf);
500 if (unlikely(retval < 0))
Andi Kleene2cac782005-07-28 21:15:46 -0700501 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 cache_shared_cpu_map_setup(cpu, j);
503 }
Andi Kleene2cac782005-07-28 21:15:46 -0700504 set_cpus_allowed(current, oldmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505
Andi Kleene2cac782005-07-28 21:15:46 -0700506out:
507 if (retval)
508 free_cache_attributes(cpu);
509 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510}
511
512#ifdef CONFIG_SYSFS
513
514#include <linux/kobject.h>
515#include <linux/sysfs.h>
516
517extern struct sysdev_class cpu_sysdev_class; /* from drivers/base/cpu.c */
518
519/* pointer to kobject for cpuX/cache */
520static struct kobject * cache_kobject[NR_CPUS];
521
522struct _index_kobject {
523 struct kobject kobj;
524 unsigned int cpu;
525 unsigned short index;
526};
527
528/* pointer to array of kobjects for cpuX/cache/indexY */
529static struct _index_kobject *index_kobject[NR_CPUS];
530#define INDEX_KOBJECT_PTR(x,y) (&((index_kobject[x])[y]))
531
532#define show_one_plus(file_name, object, val) \
533static ssize_t show_##file_name \
534 (struct _cpuid4_info *this_leaf, char *buf) \
535{ \
536 return sprintf (buf, "%lu\n", (unsigned long)this_leaf->object + val); \
537}
538
539show_one_plus(level, eax.split.level, 0);
540show_one_plus(coherency_line_size, ebx.split.coherency_line_size, 1);
541show_one_plus(physical_line_partition, ebx.split.physical_line_partition, 1);
542show_one_plus(ways_of_associativity, ebx.split.ways_of_associativity, 1);
543show_one_plus(number_of_sets, ecx.split.number_of_sets, 1);
544
545static ssize_t show_size(struct _cpuid4_info *this_leaf, char *buf)
546{
547 return sprintf (buf, "%luK\n", this_leaf->size / 1024);
548}
549
550static ssize_t show_shared_cpu_map(struct _cpuid4_info *this_leaf, char *buf)
551{
552 char mask_str[NR_CPUS];
553 cpumask_scnprintf(mask_str, NR_CPUS, this_leaf->shared_cpu_map);
554 return sprintf(buf, "%s\n", mask_str);
555}
556
557static ssize_t show_type(struct _cpuid4_info *this_leaf, char *buf) {
558 switch(this_leaf->eax.split.type) {
559 case CACHE_TYPE_DATA:
560 return sprintf(buf, "Data\n");
561 break;
562 case CACHE_TYPE_INST:
563 return sprintf(buf, "Instruction\n");
564 break;
565 case CACHE_TYPE_UNIFIED:
566 return sprintf(buf, "Unified\n");
567 break;
568 default:
569 return sprintf(buf, "Unknown\n");
570 break;
571 }
572}
573
574struct _cache_attr {
575 struct attribute attr;
576 ssize_t (*show)(struct _cpuid4_info *, char *);
577 ssize_t (*store)(struct _cpuid4_info *, const char *, size_t count);
578};
579
580#define define_one_ro(_name) \
581static struct _cache_attr _name = \
582 __ATTR(_name, 0444, show_##_name, NULL)
583
584define_one_ro(level);
585define_one_ro(type);
586define_one_ro(coherency_line_size);
587define_one_ro(physical_line_partition);
588define_one_ro(ways_of_associativity);
589define_one_ro(number_of_sets);
590define_one_ro(size);
591define_one_ro(shared_cpu_map);
592
593static struct attribute * default_attrs[] = {
594 &type.attr,
595 &level.attr,
596 &coherency_line_size.attr,
597 &physical_line_partition.attr,
598 &ways_of_associativity.attr,
599 &number_of_sets.attr,
600 &size.attr,
601 &shared_cpu_map.attr,
602 NULL
603};
604
605#define to_object(k) container_of(k, struct _index_kobject, kobj)
606#define to_attr(a) container_of(a, struct _cache_attr, attr)
607
608static ssize_t show(struct kobject * kobj, struct attribute * attr, char * buf)
609{
610 struct _cache_attr *fattr = to_attr(attr);
611 struct _index_kobject *this_leaf = to_object(kobj);
612 ssize_t ret;
613
614 ret = fattr->show ?
615 fattr->show(CPUID4_INFO_IDX(this_leaf->cpu, this_leaf->index),
616 buf) :
617 0;
618 return ret;
619}
620
621static ssize_t store(struct kobject * kobj, struct attribute * attr,
622 const char * buf, size_t count)
623{
624 return 0;
625}
626
627static struct sysfs_ops sysfs_ops = {
628 .show = show,
629 .store = store,
630};
631
632static struct kobj_type ktype_cache = {
633 .sysfs_ops = &sysfs_ops,
634 .default_attrs = default_attrs,
635};
636
637static struct kobj_type ktype_percpu_entry = {
638 .sysfs_ops = &sysfs_ops,
639};
640
641static void cpuid4_cache_sysfs_exit(unsigned int cpu)
642{
643 kfree(cache_kobject[cpu]);
644 kfree(index_kobject[cpu]);
645 cache_kobject[cpu] = NULL;
646 index_kobject[cpu] = NULL;
647 free_cache_attributes(cpu);
648}
649
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800650static int __cpuinit cpuid4_cache_sysfs_init(unsigned int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651{
652
653 if (num_cache_leaves == 0)
654 return -ENOENT;
655
656 detect_cache_attributes(cpu);
657 if (cpuid4_info[cpu] == NULL)
658 return -ENOENT;
659
660 /* Allocate all required memory */
661 cache_kobject[cpu] = kmalloc(sizeof(struct kobject), GFP_KERNEL);
662 if (unlikely(cache_kobject[cpu] == NULL))
663 goto err_out;
664 memset(cache_kobject[cpu], 0, sizeof(struct kobject));
665
666 index_kobject[cpu] = kmalloc(
667 sizeof(struct _index_kobject ) * num_cache_leaves, GFP_KERNEL);
668 if (unlikely(index_kobject[cpu] == NULL))
669 goto err_out;
670 memset(index_kobject[cpu], 0,
671 sizeof(struct _index_kobject) * num_cache_leaves);
672
673 return 0;
674
675err_out:
676 cpuid4_cache_sysfs_exit(cpu);
677 return -ENOMEM;
678}
679
680/* Add/Remove cache interface for CPU device */
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800681static int __cpuinit cache_add_dev(struct sys_device * sys_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682{
683 unsigned int cpu = sys_dev->id;
684 unsigned long i, j;
685 struct _index_kobject *this_object;
686 int retval = 0;
687
688 retval = cpuid4_cache_sysfs_init(cpu);
689 if (unlikely(retval < 0))
690 return retval;
691
692 cache_kobject[cpu]->parent = &sys_dev->kobj;
693 kobject_set_name(cache_kobject[cpu], "%s", "cache");
694 cache_kobject[cpu]->ktype = &ktype_percpu_entry;
695 retval = kobject_register(cache_kobject[cpu]);
696
697 for (i = 0; i < num_cache_leaves; i++) {
698 this_object = INDEX_KOBJECT_PTR(cpu,i);
699 this_object->cpu = cpu;
700 this_object->index = i;
701 this_object->kobj.parent = cache_kobject[cpu];
702 kobject_set_name(&(this_object->kobj), "index%1lu", i);
703 this_object->kobj.ktype = &ktype_cache;
704 retval = kobject_register(&(this_object->kobj));
705 if (unlikely(retval)) {
706 for (j = 0; j < i; j++) {
707 kobject_unregister(
708 &(INDEX_KOBJECT_PTR(cpu,j)->kobj));
709 }
710 kobject_unregister(cache_kobject[cpu]);
711 cpuid4_cache_sysfs_exit(cpu);
712 break;
713 }
714 }
715 return retval;
716}
717
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800718static void __cpuexit cache_remove_dev(struct sys_device * sys_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719{
720 unsigned int cpu = sys_dev->id;
721 unsigned long i;
722
Siddha, Suresh B2b091872005-11-05 17:25:54 +0100723 for (i = 0; i < num_cache_leaves; i++) {
724 cache_remove_shared_cpu_map(cpu, i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 kobject_unregister(&(INDEX_KOBJECT_PTR(cpu,i)->kobj));
Siddha, Suresh B2b091872005-11-05 17:25:54 +0100726 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 kobject_unregister(cache_kobject[cpu]);
728 cpuid4_cache_sysfs_exit(cpu);
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800729 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730}
731
Chandra Seetharaman9c7b2162006-06-27 02:54:07 -0700732static int __cpuinit cacheinfo_cpu_callback(struct notifier_block *nfb,
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800733 unsigned long action, void *hcpu)
734{
735 unsigned int cpu = (unsigned long)hcpu;
736 struct sys_device *sys_dev;
737
738 sys_dev = get_cpu_sysdev(cpu);
739 switch (action) {
740 case CPU_ONLINE:
741 cache_add_dev(sys_dev);
742 break;
743 case CPU_DEAD:
744 cache_remove_dev(sys_dev);
745 break;
746 }
747 return NOTIFY_OK;
748}
749
Chandra Seetharaman74b85f32006-06-27 02:54:09 -0700750static struct notifier_block __cpuinitdata cacheinfo_cpu_notifier =
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800751{
752 .notifier_call = cacheinfo_cpu_callback,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753};
754
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800755static int __cpuinit cache_sysfs_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756{
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800757 int i;
758
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 if (num_cache_leaves == 0)
760 return 0;
761
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800762 register_cpu_notifier(&cacheinfo_cpu_notifier);
763
764 for_each_online_cpu(i) {
765 cacheinfo_cpu_callback(&cacheinfo_cpu_notifier, CPU_ONLINE,
766 (void *)(long)i);
767 }
768
769 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770}
771
Ashok Raj1aa1a9f2005-10-30 14:59:50 -0800772device_initcall(cache_sysfs_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
774#endif