aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/sysfs.c
diff options
context:
space:
mode:
authorNathan Lynch <ntl@pobox.com>2008-07-27 01:24:55 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2008-07-28 02:30:52 -0400
commit124c27d375f72dd857eac27f2932f9f01df76bf4 (patch)
tree927a58733ee4b35eca6958f42af19e0d97124964 /arch/powerpc/kernel/sysfs.c
parente9efed3b80a83e44b98fc626f3268ae072550b84 (diff)
powerpc: Show processor cache information in sysfs
Collect cache information from the OF device tree and display it in the cpu hierarchy in sysfs. This is intended to be compatible at the userspace level with x86's implementation[1], hence some of the funny attribute names. The arrangement of cache info is not immediately intuitive, but (again) it's for compatibility's sake. The cache attributes exposed are: type (Data, Instruction, or Unified) level (1, 2, 3...) size coherency_line_size number_of_sets ways_of_associativity All of these can be derived on platforms that follow the OF PowerPC Processor binding. The code "publishes" only those attributes for which it is able to determine values; attributes for values which cannot be determined are not created at all. [1] arch/x86/kernel/cpu/intel_cacheinfo.c BenH: Turned some printk's into pr_debug, added better NULL checking in a couple of places. Signed-off-by: Nathan Lynch <ntl@pobox.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel/sysfs.c')
-rw-r--r--arch/powerpc/kernel/sysfs.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
index 156808086cac..56d172d16e56 100644
--- a/arch/powerpc/kernel/sysfs.c
+++ b/arch/powerpc/kernel/sysfs.c
@@ -22,6 +22,8 @@
22 22
23static DEFINE_PER_CPU(struct cpu, cpu_devices); 23static DEFINE_PER_CPU(struct cpu, cpu_devices);
24 24
25static DEFINE_PER_CPU(struct kobject *, cache_toplevel);
26
25/* SMT stuff */ 27/* SMT stuff */
26 28
27#ifdef CONFIG_PPC_MULTIPLATFORM 29#ifdef CONFIG_PPC_MULTIPLATFORM
@@ -297,6 +299,287 @@ static struct sysdev_attribute pa6t_attrs[] = {
297#endif /* CONFIG_DEBUG_KERNEL */ 299#endif /* CONFIG_DEBUG_KERNEL */
298}; 300};
299 301
302struct cache_desc {
303 struct kobject kobj;
304 struct cache_desc *next;
305 const char *type; /* Instruction, Data, or Unified */
306 u32 size; /* total cache size in KB */
307 u32 line_size; /* in bytes */
308 u32 nr_sets; /* number of sets */
309 u32 level; /* e.g. 1, 2, 3... */
310 u32 associativity; /* e.g. 8-way... 0 is fully associative */
311};
312
313DEFINE_PER_CPU(struct cache_desc *, cache_desc);
314
315static struct cache_desc *kobj_to_cache_desc(struct kobject *k)
316{
317 return container_of(k, struct cache_desc, kobj);
318}
319
320static void cache_desc_release(struct kobject *k)
321{
322 struct cache_desc *desc = kobj_to_cache_desc(k);
323
324 pr_debug("%s: releasing %s\n", __func__, kobject_name(k));
325
326 if (desc->next)
327 kobject_put(&desc->next->kobj);
328
329 kfree(kobj_to_cache_desc(k));
330}
331
332static ssize_t cache_desc_show(struct kobject *k, struct attribute *attr, char *buf)
333{
334 struct kobj_attribute *kobj_attr;
335
336 kobj_attr = container_of(attr, struct kobj_attribute, attr);
337
338 return kobj_attr->show(k, kobj_attr, buf);
339}
340
341static struct sysfs_ops cache_desc_sysfs_ops = {
342 .show = cache_desc_show,
343};
344
345static struct kobj_type cache_desc_type = {
346 .release = cache_desc_release,
347 .sysfs_ops = &cache_desc_sysfs_ops,
348};
349
350static ssize_t cache_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
351{
352 struct cache_desc *cache = kobj_to_cache_desc(k);
353
354 return sprintf(buf, "%uK\n", cache->size);
355}
356
357static struct kobj_attribute cache_size_attr =
358 __ATTR(size, 0444, cache_size_show, NULL);
359
360static ssize_t cache_line_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
361{
362 struct cache_desc *cache = kobj_to_cache_desc(k);
363
364 return sprintf(buf, "%u\n", cache->line_size);
365}
366
367static struct kobj_attribute cache_line_size_attr =
368 __ATTR(coherency_line_size, 0444, cache_line_size_show, NULL);
369
370static ssize_t cache_nr_sets_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
371{
372 struct cache_desc *cache = kobj_to_cache_desc(k);
373
374 return sprintf(buf, "%u\n", cache->nr_sets);
375}
376
377static struct kobj_attribute cache_nr_sets_attr =
378 __ATTR(number_of_sets, 0444, cache_nr_sets_show, NULL);
379
380static ssize_t cache_type_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
381{
382 struct cache_desc *cache = kobj_to_cache_desc(k);
383
384 return sprintf(buf, "%s\n", cache->type);
385}
386
387static struct kobj_attribute cache_type_attr =
388 __ATTR(type, 0444, cache_type_show, NULL);
389
390static ssize_t cache_level_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
391{
392 struct cache_desc *cache = kobj_to_cache_desc(k);
393
394 return sprintf(buf, "%u\n", cache->level);
395}
396
397static struct kobj_attribute cache_level_attr =
398 __ATTR(level, 0444, cache_level_show, NULL);
399
400static ssize_t cache_assoc_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
401{
402 struct cache_desc *cache = kobj_to_cache_desc(k);
403
404 return sprintf(buf, "%u\n", cache->associativity);
405}
406
407static struct kobj_attribute cache_assoc_attr =
408 __ATTR(ways_of_associativity, 0444, cache_assoc_show, NULL);
409
410struct cache_desc_info {
411 const char *type;
412 const char *size_prop;
413 const char *line_size_prop;
414 const char *nr_sets_prop;
415};
416
417/* PowerPC Processor binding says the [di]-cache-* must be equal on
418 * unified caches, so just use d-cache properties. */
419static struct cache_desc_info ucache_info = {
420 .type = "Unified",
421 .size_prop = "d-cache-size",
422 .line_size_prop = "d-cache-line-size",
423 .nr_sets_prop = "d-cache-sets",
424};
425
426static struct cache_desc_info dcache_info = {
427 .type = "Data",
428 .size_prop = "d-cache-size",
429 .line_size_prop = "d-cache-line-size",
430 .nr_sets_prop = "d-cache-sets",
431};
432
433static struct cache_desc_info icache_info = {
434 .type = "Instruction",
435 .size_prop = "i-cache-size",
436 .line_size_prop = "i-cache-line-size",
437 .nr_sets_prop = "i-cache-sets",
438};
439
440static struct cache_desc * __cpuinit create_cache_desc(struct device_node *np, struct kobject *parent, int index, int level, struct cache_desc_info *info)
441{
442 const u32 *cache_line_size;
443 struct cache_desc *new;
444 const u32 *cache_size;
445 const u32 *nr_sets;
446 int rc;
447
448 new = kzalloc(sizeof(*new), GFP_KERNEL);
449 if (!new)
450 return NULL;
451
452 rc = kobject_init_and_add(&new->kobj, &cache_desc_type, parent,
453 "index%d", index);
454 if (rc)
455 goto err;
456
457 /* type */
458 new->type = info->type;
459 rc = sysfs_create_file(&new->kobj, &cache_type_attr.attr);
460 WARN_ON(rc);
461
462 /* level */
463 new->level = level;
464 rc = sysfs_create_file(&new->kobj, &cache_level_attr.attr);
465 WARN_ON(rc);
466
467 /* size */
468 cache_size = of_get_property(np, info->size_prop, NULL);
469 if (cache_size) {
470 new->size = *cache_size / 1024;
471 rc = sysfs_create_file(&new->kobj,
472 &cache_size_attr.attr);
473 WARN_ON(rc);
474 }
475
476 /* coherency_line_size */
477 cache_line_size = of_get_property(np, info->line_size_prop, NULL);
478 if (cache_line_size) {
479 new->line_size = *cache_line_size;
480 rc = sysfs_create_file(&new->kobj,
481 &cache_line_size_attr.attr);
482 WARN_ON(rc);
483 }
484
485 /* number_of_sets */
486 nr_sets = of_get_property(np, info->nr_sets_prop, NULL);
487 if (nr_sets) {
488 new->nr_sets = *nr_sets;
489 rc = sysfs_create_file(&new->kobj,
490 &cache_nr_sets_attr.attr);
491 WARN_ON(rc);
492 }
493
494 /* ways_of_associativity */
495 if (new->nr_sets == 1) {
496 /* fully associative */
497 new->associativity = 0;
498 goto create_assoc;
499 }
500
501 if (new->nr_sets && new->size && new->line_size) {
502 /* If we have values for all of these we can derive
503 * the associativity. */
504 new->associativity =
505 ((new->size * 1024) / new->nr_sets) / new->line_size;
506create_assoc:
507 rc = sysfs_create_file(&new->kobj,
508 &cache_assoc_attr.attr);
509 WARN_ON(rc);
510 }
511
512 return new;
513err:
514 kfree(new);
515 return NULL;
516}
517
518static bool cache_is_unified(struct device_node *np)
519{
520 return of_get_property(np, "cache-unified", NULL);
521}
522
523static struct cache_desc * __cpuinit create_cache_index_info(struct device_node *np, struct kobject *parent, int index, int level)
524{
525 const phandle *next_cache_phandle;
526 struct device_node *next_cache;
527 struct cache_desc *new, **end;
528
529 pr_debug("%s(node = %s, index = %d)\n", __func__, np->full_name, index);
530
531 if (cache_is_unified(np)) {
532 new = create_cache_desc(np, parent, index, level,
533 &ucache_info);
534 } else {
535 new = create_cache_desc(np, parent, index, level,
536 &dcache_info);
537 if (new) {
538 index++;
539 new->next = create_cache_desc(np, parent, index, level,
540 &icache_info);
541 }
542 }
543 if (!new)
544 return NULL;
545
546 end = &new->next;
547 while (*end)
548 end = &(*end)->next;
549
550 next_cache_phandle = of_get_property(np, "l2-cache", NULL);
551 if (!next_cache_phandle)
552 goto out;
553
554 next_cache = of_find_node_by_phandle(*next_cache_phandle);
555 if (!next_cache)
556 goto out;
557
558 *end = create_cache_index_info(next_cache, parent, ++index, ++level);
559
560 of_node_put(next_cache);
561out:
562 return new;
563}
564
565static void __cpuinit create_cache_info(struct sys_device *sysdev)
566{
567 struct kobject *cache_toplevel;
568 struct device_node *np = NULL;
569 int cpu = sysdev->id;
570
571 cache_toplevel = kobject_create_and_add("cache", &sysdev->kobj);
572 if (!cache_toplevel)
573 return;
574 per_cpu(cache_toplevel, cpu) = cache_toplevel;
575 np = of_get_cpu_node(cpu, NULL);
576 if (np != NULL) {
577 per_cpu(cache_desc, cpu) =
578 create_cache_index_info(np, cache_toplevel, 0, 1);
579 of_node_put(np);
580 }
581 return;
582}
300 583
301static void __cpuinit register_cpu_online(unsigned int cpu) 584static void __cpuinit register_cpu_online(unsigned int cpu)
302{ 585{
@@ -346,9 +629,33 @@ static void __cpuinit register_cpu_online(unsigned int cpu)
346 629
347 if (cpu_has_feature(CPU_FTR_DSCR)) 630 if (cpu_has_feature(CPU_FTR_DSCR))
348 sysdev_create_file(s, &attr_dscr); 631 sysdev_create_file(s, &attr_dscr);
632
633 create_cache_info(s);
349} 634}
350 635
351#ifdef CONFIG_HOTPLUG_CPU 636#ifdef CONFIG_HOTPLUG_CPU
637static void remove_cache_info(struct sys_device *sysdev)
638{
639 struct kobject *cache_toplevel;
640 struct cache_desc *cache_desc;
641 int cpu = sysdev->id;
642
643 cache_desc = per_cpu(cache_desc, cpu);
644 if (cache_desc != NULL) {
645 sysfs_remove_file(&cache_desc->kobj, &cache_size_attr.attr);
646 sysfs_remove_file(&cache_desc->kobj, &cache_line_size_attr.attr);
647 sysfs_remove_file(&cache_desc->kobj, &cache_type_attr.attr);
648 sysfs_remove_file(&cache_desc->kobj, &cache_level_attr.attr);
649 sysfs_remove_file(&cache_desc->kobj, &cache_nr_sets_attr.attr);
650 sysfs_remove_file(&cache_desc->kobj, &cache_assoc_attr.attr);
651
652 kobject_put(&cache_desc->kobj);
653 }
654 cache_toplevel = per_cpu(cache_toplevel, cpu);
655 if (cache_toplevel != NULL)
656 kobject_put(cache_toplevel);
657}
658
352static void unregister_cpu_online(unsigned int cpu) 659static void unregister_cpu_online(unsigned int cpu)
353{ 660{
354 struct cpu *c = &per_cpu(cpu_devices, cpu); 661 struct cpu *c = &per_cpu(cpu_devices, cpu);
@@ -399,6 +706,8 @@ static void unregister_cpu_online(unsigned int cpu)
399 706
400 if (cpu_has_feature(CPU_FTR_DSCR)) 707 if (cpu_has_feature(CPU_FTR_DSCR))
401 sysdev_remove_file(s, &attr_dscr); 708 sysdev_remove_file(s, &attr_dscr);
709
710 remove_cache_info(s);
402} 711}
403#endif /* CONFIG_HOTPLUG_CPU */ 712#endif /* CONFIG_HOTPLUG_CPU */
404 713