1881730adSHeiko Carstens /* 2881730adSHeiko Carstens * Extract CPU cache information and expose them via sysfs. 3881730adSHeiko Carstens * 4881730adSHeiko Carstens * Copyright IBM Corp. 2012 5881730adSHeiko Carstens * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> 6881730adSHeiko Carstens */ 7881730adSHeiko Carstens 8881730adSHeiko Carstens #include <linux/notifier.h> 9*6668022cSHeiko Carstens #include <linux/seq_file.h> 10881730adSHeiko Carstens #include <linux/init.h> 11881730adSHeiko Carstens #include <linux/list.h> 12881730adSHeiko Carstens #include <linux/slab.h> 13881730adSHeiko Carstens #include <linux/cpu.h> 14881730adSHeiko Carstens #include <asm/facility.h> 15881730adSHeiko Carstens 16881730adSHeiko Carstens struct cache { 17881730adSHeiko Carstens unsigned long size; 18881730adSHeiko Carstens unsigned int line_size; 19881730adSHeiko Carstens unsigned int associativity; 20881730adSHeiko Carstens unsigned int nr_sets; 21*6668022cSHeiko Carstens unsigned int level : 3; 22*6668022cSHeiko Carstens unsigned int type : 2; 23*6668022cSHeiko Carstens unsigned int private : 1; 24881730adSHeiko Carstens struct list_head list; 25881730adSHeiko Carstens }; 26881730adSHeiko Carstens 27881730adSHeiko Carstens struct cache_dir { 28881730adSHeiko Carstens struct kobject *kobj; 29881730adSHeiko Carstens struct cache_index_dir *index; 30881730adSHeiko Carstens }; 31881730adSHeiko Carstens 32881730adSHeiko Carstens struct cache_index_dir { 33881730adSHeiko Carstens struct kobject kobj; 34881730adSHeiko Carstens int cpu; 35881730adSHeiko Carstens struct cache *cache; 36881730adSHeiko Carstens struct cache_index_dir *next; 37881730adSHeiko Carstens }; 38881730adSHeiko Carstens 39881730adSHeiko Carstens enum { 40881730adSHeiko Carstens CACHE_SCOPE_NOTEXISTS, 41881730adSHeiko Carstens CACHE_SCOPE_PRIVATE, 42881730adSHeiko Carstens CACHE_SCOPE_SHARED, 43881730adSHeiko Carstens CACHE_SCOPE_RESERVED, 44881730adSHeiko Carstens }; 45881730adSHeiko Carstens 46881730adSHeiko Carstens enum { 47881730adSHeiko Carstens CACHE_TYPE_SEPARATE, 48881730adSHeiko Carstens CACHE_TYPE_DATA, 49881730adSHeiko Carstens CACHE_TYPE_INSTRUCTION, 50881730adSHeiko Carstens CACHE_TYPE_UNIFIED, 51881730adSHeiko Carstens }; 52881730adSHeiko Carstens 53881730adSHeiko Carstens enum { 54881730adSHeiko Carstens EXTRACT_TOPOLOGY, 55881730adSHeiko Carstens EXTRACT_LINE_SIZE, 56881730adSHeiko Carstens EXTRACT_SIZE, 57881730adSHeiko Carstens EXTRACT_ASSOCIATIVITY, 58881730adSHeiko Carstens }; 59881730adSHeiko Carstens 60881730adSHeiko Carstens enum { 61881730adSHeiko Carstens CACHE_TI_UNIFIED = 0, 62881730adSHeiko Carstens CACHE_TI_INSTRUCTION = 0, 63881730adSHeiko Carstens CACHE_TI_DATA, 64881730adSHeiko Carstens }; 65881730adSHeiko Carstens 66881730adSHeiko Carstens struct cache_info { 67881730adSHeiko Carstens unsigned char : 4; 68881730adSHeiko Carstens unsigned char scope : 2; 69881730adSHeiko Carstens unsigned char type : 2; 70881730adSHeiko Carstens }; 71881730adSHeiko Carstens 72881730adSHeiko Carstens #define CACHE_MAX_LEVEL 8 73881730adSHeiko Carstens 74881730adSHeiko Carstens union cache_topology { 75881730adSHeiko Carstens struct cache_info ci[CACHE_MAX_LEVEL]; 76881730adSHeiko Carstens unsigned long long raw; 77881730adSHeiko Carstens }; 78881730adSHeiko Carstens 79881730adSHeiko Carstens static const char * const cache_type_string[] = { 80881730adSHeiko Carstens "Data", 81881730adSHeiko Carstens "Instruction", 82881730adSHeiko Carstens "Unified", 83881730adSHeiko Carstens }; 84881730adSHeiko Carstens 85881730adSHeiko Carstens static struct cache_dir *cache_dir_cpu[NR_CPUS]; 86881730adSHeiko Carstens static LIST_HEAD(cache_list); 87881730adSHeiko Carstens 88*6668022cSHeiko Carstens void show_cacheinfo(struct seq_file *m) 89*6668022cSHeiko Carstens { 90*6668022cSHeiko Carstens struct cache *cache; 91*6668022cSHeiko Carstens int index = 0; 92*6668022cSHeiko Carstens 93*6668022cSHeiko Carstens list_for_each_entry(cache, &cache_list, list) { 94*6668022cSHeiko Carstens seq_printf(m, "cache%-11d: ", index); 95*6668022cSHeiko Carstens seq_printf(m, "level=%d ", cache->level); 96*6668022cSHeiko Carstens seq_printf(m, "type=%s ", cache_type_string[cache->type]); 97*6668022cSHeiko Carstens seq_printf(m, "scope=%s ", cache->private ? "Private" : "Shared"); 98*6668022cSHeiko Carstens seq_printf(m, "size=%luK ", cache->size >> 10); 99*6668022cSHeiko Carstens seq_printf(m, "line_size=%u ", cache->line_size); 100*6668022cSHeiko Carstens seq_printf(m, "associativity=%d", cache->associativity); 101*6668022cSHeiko Carstens seq_puts(m, "\n"); 102*6668022cSHeiko Carstens index++; 103*6668022cSHeiko Carstens } 104*6668022cSHeiko Carstens } 105*6668022cSHeiko Carstens 106881730adSHeiko Carstens static inline unsigned long ecag(int ai, int li, int ti) 107881730adSHeiko Carstens { 108881730adSHeiko Carstens unsigned long cmd, val; 109881730adSHeiko Carstens 110881730adSHeiko Carstens cmd = ai << 4 | li << 1 | ti; 111881730adSHeiko Carstens asm volatile(".insn rsy,0xeb000000004c,%0,0,0(%1)" /* ecag */ 112881730adSHeiko Carstens : "=d" (val) : "a" (cmd)); 113881730adSHeiko Carstens return val; 114881730adSHeiko Carstens } 115881730adSHeiko Carstens 116*6668022cSHeiko Carstens static int __init cache_add(int level, int private, int type) 117881730adSHeiko Carstens { 118881730adSHeiko Carstens struct cache *cache; 119881730adSHeiko Carstens int ti; 120881730adSHeiko Carstens 121881730adSHeiko Carstens cache = kzalloc(sizeof(*cache), GFP_KERNEL); 122881730adSHeiko Carstens if (!cache) 123881730adSHeiko Carstens return -ENOMEM; 124881730adSHeiko Carstens ti = type == CACHE_TYPE_DATA ? CACHE_TI_DATA : CACHE_TI_UNIFIED; 125881730adSHeiko Carstens cache->size = ecag(EXTRACT_SIZE, level, ti); 126881730adSHeiko Carstens cache->line_size = ecag(EXTRACT_LINE_SIZE, level, ti); 127881730adSHeiko Carstens cache->associativity = ecag(EXTRACT_ASSOCIATIVITY, level, ti); 128881730adSHeiko Carstens cache->nr_sets = cache->size / cache->associativity; 129881730adSHeiko Carstens cache->nr_sets /= cache->line_size; 130*6668022cSHeiko Carstens cache->private = private; 131881730adSHeiko Carstens cache->level = level + 1; 132*6668022cSHeiko Carstens cache->type = type - 1; 133881730adSHeiko Carstens list_add_tail(&cache->list, &cache_list); 134881730adSHeiko Carstens return 0; 135881730adSHeiko Carstens } 136881730adSHeiko Carstens 137881730adSHeiko Carstens static void __init cache_build_info(void) 138881730adSHeiko Carstens { 139881730adSHeiko Carstens struct cache *cache, *next; 140881730adSHeiko Carstens union cache_topology ct; 141*6668022cSHeiko Carstens int level, private, rc; 142881730adSHeiko Carstens 143881730adSHeiko Carstens ct.raw = ecag(EXTRACT_TOPOLOGY, 0, 0); 144881730adSHeiko Carstens for (level = 0; level < CACHE_MAX_LEVEL; level++) { 145881730adSHeiko Carstens switch (ct.ci[level].scope) { 146881730adSHeiko Carstens case CACHE_SCOPE_NOTEXISTS: 147881730adSHeiko Carstens case CACHE_SCOPE_RESERVED: 148881730adSHeiko Carstens return; 149*6668022cSHeiko Carstens case CACHE_SCOPE_SHARED: 150*6668022cSHeiko Carstens private = 0; 151*6668022cSHeiko Carstens break; 152881730adSHeiko Carstens case CACHE_SCOPE_PRIVATE: 153*6668022cSHeiko Carstens private = 1; 154881730adSHeiko Carstens break; 155881730adSHeiko Carstens } 156881730adSHeiko Carstens if (ct.ci[level].type == CACHE_TYPE_SEPARATE) { 157*6668022cSHeiko Carstens rc = cache_add(level, private, CACHE_TYPE_DATA); 158*6668022cSHeiko Carstens rc |= cache_add(level, private, CACHE_TYPE_INSTRUCTION); 159881730adSHeiko Carstens } else { 160*6668022cSHeiko Carstens rc = cache_add(level, private, ct.ci[level].type); 161881730adSHeiko Carstens } 162881730adSHeiko Carstens if (rc) 163881730adSHeiko Carstens goto error; 164881730adSHeiko Carstens } 165881730adSHeiko Carstens return; 166881730adSHeiko Carstens error: 167881730adSHeiko Carstens list_for_each_entry_safe(cache, next, &cache_list, list) { 168881730adSHeiko Carstens list_del(&cache->list); 169881730adSHeiko Carstens kfree(cache); 170881730adSHeiko Carstens } 171881730adSHeiko Carstens } 172881730adSHeiko Carstens 173881730adSHeiko Carstens static struct cache_dir *__cpuinit cache_create_cache_dir(int cpu) 174881730adSHeiko Carstens { 175881730adSHeiko Carstens struct cache_dir *cache_dir; 176881730adSHeiko Carstens struct kobject *kobj = NULL; 177881730adSHeiko Carstens struct device *dev; 178881730adSHeiko Carstens 179881730adSHeiko Carstens dev = get_cpu_device(cpu); 180881730adSHeiko Carstens if (!dev) 181881730adSHeiko Carstens goto out; 182881730adSHeiko Carstens kobj = kobject_create_and_add("cache", &dev->kobj); 183881730adSHeiko Carstens if (!kobj) 184881730adSHeiko Carstens goto out; 185881730adSHeiko Carstens cache_dir = kzalloc(sizeof(*cache_dir), GFP_KERNEL); 186881730adSHeiko Carstens if (!cache_dir) 187881730adSHeiko Carstens goto out; 188881730adSHeiko Carstens cache_dir->kobj = kobj; 189881730adSHeiko Carstens cache_dir_cpu[cpu] = cache_dir; 190881730adSHeiko Carstens return cache_dir; 191881730adSHeiko Carstens out: 192881730adSHeiko Carstens kobject_put(kobj); 193881730adSHeiko Carstens return NULL; 194881730adSHeiko Carstens } 195881730adSHeiko Carstens 196881730adSHeiko Carstens static struct cache_index_dir *kobj_to_cache_index_dir(struct kobject *kobj) 197881730adSHeiko Carstens { 198881730adSHeiko Carstens return container_of(kobj, struct cache_index_dir, kobj); 199881730adSHeiko Carstens } 200881730adSHeiko Carstens 201881730adSHeiko Carstens static void cache_index_release(struct kobject *kobj) 202881730adSHeiko Carstens { 203881730adSHeiko Carstens struct cache_index_dir *index; 204881730adSHeiko Carstens 205881730adSHeiko Carstens index = kobj_to_cache_index_dir(kobj); 206881730adSHeiko Carstens kfree(index); 207881730adSHeiko Carstens } 208881730adSHeiko Carstens 209881730adSHeiko Carstens static ssize_t cache_index_show(struct kobject *kobj, 210881730adSHeiko Carstens struct attribute *attr, char *buf) 211881730adSHeiko Carstens { 212881730adSHeiko Carstens struct kobj_attribute *kobj_attr; 213881730adSHeiko Carstens 214881730adSHeiko Carstens kobj_attr = container_of(attr, struct kobj_attribute, attr); 215881730adSHeiko Carstens return kobj_attr->show(kobj, kobj_attr, buf); 216881730adSHeiko Carstens } 217881730adSHeiko Carstens 218881730adSHeiko Carstens #define DEFINE_CACHE_ATTR(_name, _format, _value) \ 219881730adSHeiko Carstens static ssize_t cache_##_name##_show(struct kobject *kobj, \ 220881730adSHeiko Carstens struct kobj_attribute *attr, \ 221881730adSHeiko Carstens char *buf) \ 222881730adSHeiko Carstens { \ 223881730adSHeiko Carstens struct cache_index_dir *index; \ 224881730adSHeiko Carstens \ 225881730adSHeiko Carstens index = kobj_to_cache_index_dir(kobj); \ 226881730adSHeiko Carstens return sprintf(buf, _format, _value); \ 227881730adSHeiko Carstens } \ 228881730adSHeiko Carstens static struct kobj_attribute cache_##_name##_attr = \ 229881730adSHeiko Carstens __ATTR(_name, 0444, cache_##_name##_show, NULL); 230881730adSHeiko Carstens 231881730adSHeiko Carstens DEFINE_CACHE_ATTR(size, "%luK\n", index->cache->size >> 10); 232881730adSHeiko Carstens DEFINE_CACHE_ATTR(coherency_line_size, "%u\n", index->cache->line_size); 233881730adSHeiko Carstens DEFINE_CACHE_ATTR(number_of_sets, "%u\n", index->cache->nr_sets); 234881730adSHeiko Carstens DEFINE_CACHE_ATTR(ways_of_associativity, "%u\n", index->cache->associativity); 235*6668022cSHeiko Carstens DEFINE_CACHE_ATTR(type, "%s\n", cache_type_string[index->cache->type]); 236881730adSHeiko Carstens DEFINE_CACHE_ATTR(level, "%d\n", index->cache->level); 237881730adSHeiko Carstens 238881730adSHeiko Carstens static ssize_t shared_cpu_map_func(struct kobject *kobj, int type, char *buf) 239881730adSHeiko Carstens { 240881730adSHeiko Carstens struct cache_index_dir *index; 241881730adSHeiko Carstens int len; 242881730adSHeiko Carstens 243881730adSHeiko Carstens index = kobj_to_cache_index_dir(kobj); 244881730adSHeiko Carstens len = type ? 245881730adSHeiko Carstens cpulist_scnprintf(buf, PAGE_SIZE - 2, cpumask_of(index->cpu)) : 246881730adSHeiko Carstens cpumask_scnprintf(buf, PAGE_SIZE - 2, cpumask_of(index->cpu)); 247881730adSHeiko Carstens len += sprintf(&buf[len], "\n"); 248881730adSHeiko Carstens return len; 249881730adSHeiko Carstens } 250881730adSHeiko Carstens 251881730adSHeiko Carstens static ssize_t shared_cpu_map_show(struct kobject *kobj, 252881730adSHeiko Carstens struct kobj_attribute *attr, char *buf) 253881730adSHeiko Carstens { 254881730adSHeiko Carstens return shared_cpu_map_func(kobj, 0, buf); 255881730adSHeiko Carstens } 256881730adSHeiko Carstens static struct kobj_attribute cache_shared_cpu_map_attr = 257881730adSHeiko Carstens __ATTR(shared_cpu_map, 0444, shared_cpu_map_show, NULL); 258881730adSHeiko Carstens 259881730adSHeiko Carstens static ssize_t shared_cpu_list_show(struct kobject *kobj, 260881730adSHeiko Carstens struct kobj_attribute *attr, char *buf) 261881730adSHeiko Carstens { 262881730adSHeiko Carstens return shared_cpu_map_func(kobj, 1, buf); 263881730adSHeiko Carstens } 264881730adSHeiko Carstens static struct kobj_attribute cache_shared_cpu_list_attr = 265881730adSHeiko Carstens __ATTR(shared_cpu_list, 0444, shared_cpu_list_show, NULL); 266881730adSHeiko Carstens 267881730adSHeiko Carstens static struct attribute *cache_index_default_attrs[] = { 268881730adSHeiko Carstens &cache_type_attr.attr, 269881730adSHeiko Carstens &cache_size_attr.attr, 270881730adSHeiko Carstens &cache_number_of_sets_attr.attr, 271881730adSHeiko Carstens &cache_ways_of_associativity_attr.attr, 272881730adSHeiko Carstens &cache_level_attr.attr, 273881730adSHeiko Carstens &cache_coherency_line_size_attr.attr, 274881730adSHeiko Carstens &cache_shared_cpu_map_attr.attr, 275881730adSHeiko Carstens &cache_shared_cpu_list_attr.attr, 276881730adSHeiko Carstens NULL, 277881730adSHeiko Carstens }; 278881730adSHeiko Carstens 279881730adSHeiko Carstens static const struct sysfs_ops cache_index_ops = { 280881730adSHeiko Carstens .show = cache_index_show, 281881730adSHeiko Carstens }; 282881730adSHeiko Carstens 283881730adSHeiko Carstens static struct kobj_type cache_index_type = { 284881730adSHeiko Carstens .sysfs_ops = &cache_index_ops, 285881730adSHeiko Carstens .release = cache_index_release, 286881730adSHeiko Carstens .default_attrs = cache_index_default_attrs, 287881730adSHeiko Carstens }; 288881730adSHeiko Carstens 289881730adSHeiko Carstens static int __cpuinit cache_create_index_dir(struct cache_dir *cache_dir, 290881730adSHeiko Carstens struct cache *cache, int index, 291881730adSHeiko Carstens int cpu) 292881730adSHeiko Carstens { 293881730adSHeiko Carstens struct cache_index_dir *index_dir; 294881730adSHeiko Carstens int rc; 295881730adSHeiko Carstens 296881730adSHeiko Carstens index_dir = kzalloc(sizeof(*index_dir), GFP_KERNEL); 297881730adSHeiko Carstens if (!index_dir) 298881730adSHeiko Carstens return -ENOMEM; 299881730adSHeiko Carstens index_dir->cache = cache; 300881730adSHeiko Carstens index_dir->cpu = cpu; 301881730adSHeiko Carstens rc = kobject_init_and_add(&index_dir->kobj, &cache_index_type, 302881730adSHeiko Carstens cache_dir->kobj, "index%d", index); 303881730adSHeiko Carstens if (rc) 304881730adSHeiko Carstens goto out; 305881730adSHeiko Carstens index_dir->next = cache_dir->index; 306881730adSHeiko Carstens cache_dir->index = index_dir; 307881730adSHeiko Carstens return 0; 308881730adSHeiko Carstens out: 309881730adSHeiko Carstens kfree(index_dir); 310881730adSHeiko Carstens return rc; 311881730adSHeiko Carstens } 312881730adSHeiko Carstens 313881730adSHeiko Carstens static int __cpuinit cache_add_cpu(int cpu) 314881730adSHeiko Carstens { 315881730adSHeiko Carstens struct cache_dir *cache_dir; 316881730adSHeiko Carstens struct cache *cache; 317881730adSHeiko Carstens int rc, index = 0; 318881730adSHeiko Carstens 319881730adSHeiko Carstens if (list_empty(&cache_list)) 320881730adSHeiko Carstens return 0; 321881730adSHeiko Carstens cache_dir = cache_create_cache_dir(cpu); 322881730adSHeiko Carstens if (!cache_dir) 323881730adSHeiko Carstens return -ENOMEM; 324881730adSHeiko Carstens list_for_each_entry(cache, &cache_list, list) { 325*6668022cSHeiko Carstens if (!cache->private) 326*6668022cSHeiko Carstens break; 327881730adSHeiko Carstens rc = cache_create_index_dir(cache_dir, cache, index, cpu); 328881730adSHeiko Carstens if (rc) 329881730adSHeiko Carstens return rc; 330881730adSHeiko Carstens index++; 331881730adSHeiko Carstens } 332881730adSHeiko Carstens return 0; 333881730adSHeiko Carstens } 334881730adSHeiko Carstens 335881730adSHeiko Carstens static void __cpuinit cache_remove_cpu(int cpu) 336881730adSHeiko Carstens { 337881730adSHeiko Carstens struct cache_index_dir *index, *next; 338881730adSHeiko Carstens struct cache_dir *cache_dir; 339881730adSHeiko Carstens 340881730adSHeiko Carstens cache_dir = cache_dir_cpu[cpu]; 341881730adSHeiko Carstens if (!cache_dir) 342881730adSHeiko Carstens return; 343881730adSHeiko Carstens index = cache_dir->index; 344881730adSHeiko Carstens while (index) { 345881730adSHeiko Carstens next = index->next; 346881730adSHeiko Carstens kobject_put(&index->kobj); 347881730adSHeiko Carstens index = next; 348881730adSHeiko Carstens } 349881730adSHeiko Carstens kobject_put(cache_dir->kobj); 350881730adSHeiko Carstens kfree(cache_dir); 351881730adSHeiko Carstens cache_dir_cpu[cpu] = NULL; 352881730adSHeiko Carstens } 353881730adSHeiko Carstens 354881730adSHeiko Carstens static int __cpuinit cache_hotplug(struct notifier_block *nfb, 355881730adSHeiko Carstens unsigned long action, void *hcpu) 356881730adSHeiko Carstens { 357881730adSHeiko Carstens int cpu = (long)hcpu; 358881730adSHeiko Carstens int rc = 0; 359881730adSHeiko Carstens 360881730adSHeiko Carstens switch (action & ~CPU_TASKS_FROZEN) { 361881730adSHeiko Carstens case CPU_ONLINE: 362881730adSHeiko Carstens rc = cache_add_cpu(cpu); 363881730adSHeiko Carstens if (rc) 364881730adSHeiko Carstens cache_remove_cpu(cpu); 365881730adSHeiko Carstens break; 366881730adSHeiko Carstens case CPU_DEAD: 367881730adSHeiko Carstens cache_remove_cpu(cpu); 368881730adSHeiko Carstens break; 369881730adSHeiko Carstens } 370881730adSHeiko Carstens return rc ? NOTIFY_BAD : NOTIFY_OK; 371881730adSHeiko Carstens } 372881730adSHeiko Carstens 373881730adSHeiko Carstens static int __init cache_init(void) 374881730adSHeiko Carstens { 375881730adSHeiko Carstens int cpu; 376881730adSHeiko Carstens 377881730adSHeiko Carstens if (!test_facility(34)) 378881730adSHeiko Carstens return 0; 379881730adSHeiko Carstens cache_build_info(); 380881730adSHeiko Carstens for_each_online_cpu(cpu) 381881730adSHeiko Carstens cache_add_cpu(cpu); 382881730adSHeiko Carstens hotcpu_notifier(cache_hotplug, 0); 383881730adSHeiko Carstens return 0; 384881730adSHeiko Carstens } 385881730adSHeiko Carstens device_initcall(cache_init); 386