diff options
Diffstat (limited to 'arch/ia64/kernel/topology.c')
-rw-r--r-- | arch/ia64/kernel/topology.c | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c index 3b6fd798c4d6..b47476d655f1 100644 --- a/arch/ia64/kernel/topology.c +++ b/arch/ia64/kernel/topology.c | |||
@@ -9,6 +9,8 @@ | |||
9 | * 2002/08/07 Erich Focht <efocht@ess.nec.de> | 9 | * 2002/08/07 Erich Focht <efocht@ess.nec.de> |
10 | * Populate cpu entries in sysfs for non-numa systems as well | 10 | * Populate cpu entries in sysfs for non-numa systems as well |
11 | * Intel Corporation - Ashok Raj | 11 | * Intel Corporation - Ashok Raj |
12 | * 02/27/2006 Zhang, Yanmin | ||
13 | * Populate cpu cache entries in sysfs for cpu cache info | ||
12 | */ | 14 | */ |
13 | 15 | ||
14 | #include <linux/config.h> | 16 | #include <linux/config.h> |
@@ -19,6 +21,7 @@ | |||
19 | #include <linux/init.h> | 21 | #include <linux/init.h> |
20 | #include <linux/bootmem.h> | 22 | #include <linux/bootmem.h> |
21 | #include <linux/nodemask.h> | 23 | #include <linux/nodemask.h> |
24 | #include <linux/notifier.h> | ||
22 | #include <asm/mmzone.h> | 25 | #include <asm/mmzone.h> |
23 | #include <asm/numa.h> | 26 | #include <asm/numa.h> |
24 | #include <asm/cpu.h> | 27 | #include <asm/cpu.h> |
@@ -101,3 +104,367 @@ out: | |||
101 | } | 104 | } |
102 | 105 | ||
103 | subsys_initcall(topology_init); | 106 | subsys_initcall(topology_init); |
107 | |||
108 | |||
109 | /* | ||
110 | * Export cpu cache information through sysfs | ||
111 | */ | ||
112 | |||
113 | /* | ||
114 | * A bunch of string array to get pretty printing | ||
115 | */ | ||
116 | static const char *cache_types[] = { | ||
117 | "", /* not used */ | ||
118 | "Instruction", | ||
119 | "Data", | ||
120 | "Unified" /* unified */ | ||
121 | }; | ||
122 | |||
123 | static const char *cache_mattrib[]={ | ||
124 | "WriteThrough", | ||
125 | "WriteBack", | ||
126 | "", /* reserved */ | ||
127 | "" /* reserved */ | ||
128 | }; | ||
129 | |||
130 | struct cache_info { | ||
131 | pal_cache_config_info_t cci; | ||
132 | cpumask_t shared_cpu_map; | ||
133 | int level; | ||
134 | int type; | ||
135 | struct kobject kobj; | ||
136 | }; | ||
137 | |||
138 | struct cpu_cache_info { | ||
139 | struct cache_info *cache_leaves; | ||
140 | int num_cache_leaves; | ||
141 | struct kobject kobj; | ||
142 | }; | ||
143 | |||
144 | static struct cpu_cache_info all_cpu_cache_info[NR_CPUS]; | ||
145 | #define LEAF_KOBJECT_PTR(x,y) (&all_cpu_cache_info[x].cache_leaves[y]) | ||
146 | |||
147 | #ifdef CONFIG_SMP | ||
148 | static void cache_shared_cpu_map_setup( unsigned int cpu, | ||
149 | struct cache_info * this_leaf) | ||
150 | { | ||
151 | pal_cache_shared_info_t csi; | ||
152 | int num_shared, i = 0; | ||
153 | unsigned int j; | ||
154 | |||
155 | if (cpu_data(cpu)->threads_per_core <= 1 && | ||
156 | cpu_data(cpu)->cores_per_socket <= 1) { | ||
157 | cpu_set(cpu, this_leaf->shared_cpu_map); | ||
158 | return; | ||
159 | } | ||
160 | |||
161 | if (ia64_pal_cache_shared_info(this_leaf->level, | ||
162 | this_leaf->type, | ||
163 | 0, | ||
164 | &csi) != PAL_STATUS_SUCCESS) | ||
165 | return; | ||
166 | |||
167 | num_shared = (int) csi.num_shared; | ||
168 | do { | ||
169 | for_each_cpu(j) | ||
170 | if (cpu_data(cpu)->socket_id == cpu_data(j)->socket_id | ||
171 | && cpu_data(j)->core_id == csi.log1_cid | ||
172 | && cpu_data(j)->thread_id == csi.log1_tid) | ||
173 | cpu_set(j, this_leaf->shared_cpu_map); | ||
174 | |||
175 | i++; | ||
176 | } while (i < num_shared && | ||
177 | ia64_pal_cache_shared_info(this_leaf->level, | ||
178 | this_leaf->type, | ||
179 | i, | ||
180 | &csi) == PAL_STATUS_SUCCESS); | ||
181 | } | ||
182 | #else | ||
183 | static void cache_shared_cpu_map_setup(unsigned int cpu, | ||
184 | struct cache_info * this_leaf) | ||
185 | { | ||
186 | cpu_set(cpu, this_leaf->shared_cpu_map); | ||
187 | return; | ||
188 | } | ||
189 | #endif | ||
190 | |||
191 | static ssize_t show_coherency_line_size(struct cache_info *this_leaf, | ||
192 | char *buf) | ||
193 | { | ||
194 | return sprintf(buf, "%u\n", 1 << this_leaf->cci.pcci_line_size); | ||
195 | } | ||
196 | |||
197 | static ssize_t show_ways_of_associativity(struct cache_info *this_leaf, | ||
198 | char *buf) | ||
199 | { | ||
200 | return sprintf(buf, "%u\n", this_leaf->cci.pcci_assoc); | ||
201 | } | ||
202 | |||
203 | static ssize_t show_attributes(struct cache_info *this_leaf, char *buf) | ||
204 | { | ||
205 | return sprintf(buf, | ||
206 | "%s\n", | ||
207 | cache_mattrib[this_leaf->cci.pcci_cache_attr]); | ||
208 | } | ||
209 | |||
210 | static ssize_t show_size(struct cache_info *this_leaf, char *buf) | ||
211 | { | ||
212 | return sprintf(buf, "%uK\n", this_leaf->cci.pcci_cache_size / 1024); | ||
213 | } | ||
214 | |||
215 | static ssize_t show_number_of_sets(struct cache_info *this_leaf, char *buf) | ||
216 | { | ||
217 | unsigned number_of_sets = this_leaf->cci.pcci_cache_size; | ||
218 | number_of_sets /= this_leaf->cci.pcci_assoc; | ||
219 | number_of_sets /= 1 << this_leaf->cci.pcci_line_size; | ||
220 | |||
221 | return sprintf(buf, "%u\n", number_of_sets); | ||
222 | } | ||
223 | |||
224 | static ssize_t show_shared_cpu_map(struct cache_info *this_leaf, char *buf) | ||
225 | { | ||
226 | ssize_t len; | ||
227 | cpumask_t shared_cpu_map; | ||
228 | |||
229 | cpus_and(shared_cpu_map, this_leaf->shared_cpu_map, cpu_online_map); | ||
230 | len = cpumask_scnprintf(buf, NR_CPUS+1, shared_cpu_map); | ||
231 | len += sprintf(buf+len, "\n"); | ||
232 | return len; | ||
233 | } | ||
234 | |||
235 | static ssize_t show_type(struct cache_info *this_leaf, char *buf) | ||
236 | { | ||
237 | int type = this_leaf->type + this_leaf->cci.pcci_unified; | ||
238 | return sprintf(buf, "%s\n", cache_types[type]); | ||
239 | } | ||
240 | |||
241 | static ssize_t show_level(struct cache_info *this_leaf, char *buf) | ||
242 | { | ||
243 | return sprintf(buf, "%u\n", this_leaf->level); | ||
244 | } | ||
245 | |||
246 | struct cache_attr { | ||
247 | struct attribute attr; | ||
248 | ssize_t (*show)(struct cache_info *, char *); | ||
249 | ssize_t (*store)(struct cache_info *, const char *, size_t count); | ||
250 | }; | ||
251 | |||
252 | #ifdef define_one_ro | ||
253 | #undef define_one_ro | ||
254 | #endif | ||
255 | #define define_one_ro(_name) \ | ||
256 | static struct cache_attr _name = \ | ||
257 | __ATTR(_name, 0444, show_##_name, NULL) | ||
258 | |||
259 | define_one_ro(level); | ||
260 | define_one_ro(type); | ||
261 | define_one_ro(coherency_line_size); | ||
262 | define_one_ro(ways_of_associativity); | ||
263 | define_one_ro(size); | ||
264 | define_one_ro(number_of_sets); | ||
265 | define_one_ro(shared_cpu_map); | ||
266 | define_one_ro(attributes); | ||
267 | |||
268 | static struct attribute * cache_default_attrs[] = { | ||
269 | &type.attr, | ||
270 | &level.attr, | ||
271 | &coherency_line_size.attr, | ||
272 | &ways_of_associativity.attr, | ||
273 | &attributes.attr, | ||
274 | &size.attr, | ||
275 | &number_of_sets.attr, | ||
276 | &shared_cpu_map.attr, | ||
277 | NULL | ||
278 | }; | ||
279 | |||
280 | #define to_object(k) container_of(k, struct cache_info, kobj) | ||
281 | #define to_attr(a) container_of(a, struct cache_attr, attr) | ||
282 | |||
283 | static ssize_t cache_show(struct kobject * kobj, struct attribute * attr, char * buf) | ||
284 | { | ||
285 | struct cache_attr *fattr = to_attr(attr); | ||
286 | struct cache_info *this_leaf = to_object(kobj); | ||
287 | ssize_t ret; | ||
288 | |||
289 | ret = fattr->show ? fattr->show(this_leaf, buf) : 0; | ||
290 | return ret; | ||
291 | } | ||
292 | |||
293 | static struct sysfs_ops cache_sysfs_ops = { | ||
294 | .show = cache_show | ||
295 | }; | ||
296 | |||
297 | static struct kobj_type cache_ktype = { | ||
298 | .sysfs_ops = &cache_sysfs_ops, | ||
299 | .default_attrs = cache_default_attrs, | ||
300 | }; | ||
301 | |||
302 | static struct kobj_type cache_ktype_percpu_entry = { | ||
303 | .sysfs_ops = &cache_sysfs_ops, | ||
304 | }; | ||
305 | |||
306 | static void __cpuinit cpu_cache_sysfs_exit(unsigned int cpu) | ||
307 | { | ||
308 | if (all_cpu_cache_info[cpu].cache_leaves) { | ||
309 | kfree(all_cpu_cache_info[cpu].cache_leaves); | ||
310 | all_cpu_cache_info[cpu].cache_leaves = NULL; | ||
311 | } | ||
312 | all_cpu_cache_info[cpu].num_cache_leaves = 0; | ||
313 | memset(&all_cpu_cache_info[cpu].kobj, 0, sizeof(struct kobject)); | ||
314 | |||
315 | return; | ||
316 | } | ||
317 | |||
318 | static int __cpuinit cpu_cache_sysfs_init(unsigned int cpu) | ||
319 | { | ||
320 | u64 i, levels, unique_caches; | ||
321 | pal_cache_config_info_t cci; | ||
322 | int j; | ||
323 | s64 status; | ||
324 | struct cache_info *this_cache; | ||
325 | int num_cache_leaves = 0; | ||
326 | |||
327 | if ((status = ia64_pal_cache_summary(&levels, &unique_caches)) != 0) { | ||
328 | printk(KERN_ERR "ia64_pal_cache_summary=%ld\n", status); | ||
329 | return -1; | ||
330 | } | ||
331 | |||
332 | this_cache=kzalloc(sizeof(struct cache_info)*unique_caches, | ||
333 | GFP_KERNEL); | ||
334 | if (this_cache == NULL) | ||
335 | return -ENOMEM; | ||
336 | |||
337 | for (i=0; i < levels; i++) { | ||
338 | for (j=2; j >0 ; j--) { | ||
339 | if ((status=ia64_pal_cache_config_info(i,j, &cci)) != | ||
340 | PAL_STATUS_SUCCESS) | ||
341 | continue; | ||
342 | |||
343 | this_cache[num_cache_leaves].cci = cci; | ||
344 | this_cache[num_cache_leaves].level = i + 1; | ||
345 | this_cache[num_cache_leaves].type = j; | ||
346 | |||
347 | cache_shared_cpu_map_setup(cpu, | ||
348 | &this_cache[num_cache_leaves]); | ||
349 | num_cache_leaves ++; | ||
350 | } | ||
351 | } | ||
352 | |||
353 | all_cpu_cache_info[cpu].cache_leaves = this_cache; | ||
354 | all_cpu_cache_info[cpu].num_cache_leaves = num_cache_leaves; | ||
355 | |||
356 | memset(&all_cpu_cache_info[cpu].kobj, 0, sizeof(struct kobject)); | ||
357 | |||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | /* Add cache interface for CPU device */ | ||
362 | static int __cpuinit cache_add_dev(struct sys_device * sys_dev) | ||
363 | { | ||
364 | unsigned int cpu = sys_dev->id; | ||
365 | unsigned long i, j; | ||
366 | struct cache_info *this_object; | ||
367 | int retval = 0; | ||
368 | cpumask_t oldmask; | ||
369 | |||
370 | if (all_cpu_cache_info[cpu].kobj.parent) | ||
371 | return 0; | ||
372 | |||
373 | oldmask = current->cpus_allowed; | ||
374 | retval = set_cpus_allowed(current, cpumask_of_cpu(cpu)); | ||
375 | if (unlikely(retval)) | ||
376 | return retval; | ||
377 | |||
378 | retval = cpu_cache_sysfs_init(cpu); | ||
379 | set_cpus_allowed(current, oldmask); | ||
380 | if (unlikely(retval < 0)) | ||
381 | return retval; | ||
382 | |||
383 | all_cpu_cache_info[cpu].kobj.parent = &sys_dev->kobj; | ||
384 | kobject_set_name(&all_cpu_cache_info[cpu].kobj, "%s", "cache"); | ||
385 | all_cpu_cache_info[cpu].kobj.ktype = &cache_ktype_percpu_entry; | ||
386 | retval = kobject_register(&all_cpu_cache_info[cpu].kobj); | ||
387 | |||
388 | for (i = 0; i < all_cpu_cache_info[cpu].num_cache_leaves; i++) { | ||
389 | this_object = LEAF_KOBJECT_PTR(cpu,i); | ||
390 | this_object->kobj.parent = &all_cpu_cache_info[cpu].kobj; | ||
391 | kobject_set_name(&(this_object->kobj), "index%1lu", i); | ||
392 | this_object->kobj.ktype = &cache_ktype; | ||
393 | retval = kobject_register(&(this_object->kobj)); | ||
394 | if (unlikely(retval)) { | ||
395 | for (j = 0; j < i; j++) { | ||
396 | kobject_unregister( | ||
397 | &(LEAF_KOBJECT_PTR(cpu,j)->kobj)); | ||
398 | } | ||
399 | kobject_unregister(&all_cpu_cache_info[cpu].kobj); | ||
400 | cpu_cache_sysfs_exit(cpu); | ||
401 | break; | ||
402 | } | ||
403 | } | ||
404 | return retval; | ||
405 | } | ||
406 | |||
407 | /* Remove cache interface for CPU device */ | ||
408 | static int __cpuinit cache_remove_dev(struct sys_device * sys_dev) | ||
409 | { | ||
410 | unsigned int cpu = sys_dev->id; | ||
411 | unsigned long i; | ||
412 | |||
413 | for (i = 0; i < all_cpu_cache_info[cpu].num_cache_leaves; i++) | ||
414 | kobject_unregister(&(LEAF_KOBJECT_PTR(cpu,i)->kobj)); | ||
415 | |||
416 | if (all_cpu_cache_info[cpu].kobj.parent) { | ||
417 | kobject_unregister(&all_cpu_cache_info[cpu].kobj); | ||
418 | memset(&all_cpu_cache_info[cpu].kobj, | ||
419 | 0, | ||
420 | sizeof(struct kobject)); | ||
421 | } | ||
422 | |||
423 | cpu_cache_sysfs_exit(cpu); | ||
424 | |||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | /* | ||
429 | * When a cpu is hot-plugged, do a check and initiate | ||
430 | * cache kobject if necessary | ||
431 | */ | ||
432 | static int __cpuinit cache_cpu_callback(struct notifier_block *nfb, | ||
433 | unsigned long action, void *hcpu) | ||
434 | { | ||
435 | unsigned int cpu = (unsigned long)hcpu; | ||
436 | struct sys_device *sys_dev; | ||
437 | |||
438 | sys_dev = get_cpu_sysdev(cpu); | ||
439 | switch (action) { | ||
440 | case CPU_ONLINE: | ||
441 | cache_add_dev(sys_dev); | ||
442 | break; | ||
443 | case CPU_DEAD: | ||
444 | cache_remove_dev(sys_dev); | ||
445 | break; | ||
446 | } | ||
447 | return NOTIFY_OK; | ||
448 | } | ||
449 | |||
450 | static struct notifier_block cache_cpu_notifier = | ||
451 | { | ||
452 | .notifier_call = cache_cpu_callback | ||
453 | }; | ||
454 | |||
455 | static int __cpuinit cache_sysfs_init(void) | ||
456 | { | ||
457 | int i; | ||
458 | |||
459 | for_each_online_cpu(i) { | ||
460 | cache_cpu_callback(&cache_cpu_notifier, CPU_ONLINE, | ||
461 | (void *)(long)i); | ||
462 | } | ||
463 | |||
464 | register_cpu_notifier(&cache_cpu_notifier); | ||
465 | |||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | device_initcall(cache_sysfs_init); | ||
470 | |||