| 1 | |
| 2 | |
| 3 | * Processor cache information made available to userspace via sysfs;
|
| 4 | * intended to be compatible with x86 intel_cacheinfo implementation.
|
| 5 | *
|
| 6 | * Copyright 2008 IBM Corporation
|
| 7 | * Author: Nathan Lynch
|
| 8 |
|
| 9 |
|
| 10 | #define pr_fmt(fmt) "cacheinfo: " fmt
|
| 11 |
|
| 12 | #include <linux/cpu.h>
|
| 13 | #include <linux/cpumask.h>
|
| 14 | #include <linux/kernel.h>
|
| 15 | #include <linux/kobject.h>
|
| 16 | #include <linux/list.h>
|
| 17 | #include <linux/notifier.h>
|
| 18 | #include <linux/of.h>
|
| 19 | #include <linux/percpu.h>
|
| 20 | #include <linux/slab.h>
|
| 21 | #include <linux/sysfs.h>
|
| 22 | #include <asm/cputhreads.h>
|
| 23 | #include <asm/smp.h>
|
| 24 |
|
| 25 | #include "cacheinfo.h"
|
| 26 |
|
| 27 | |
| 28 | * - a "cache" kobject for the top-level directory
|
| 29 | * - a list of "index" objects representing the cpu's local cache hierarchy
|
| 30 |
|
| 31 | struct cache_dir {
|
| 32 | struct kobject *kobj; |
| 33 | * directory
|
| 34 | struct cache_index_dir *index;
|
| 35 | };
|
| 36 |
|
| 37 | |
| 38 | * subdirectory corresponding to a cache object associated with the
|
| 39 | * cpu. This object's lifetime is managed via the embedded kobject.
|
| 40 |
|
| 41 | struct cache_index_dir {
|
| 42 | struct kobject kobj;
|
| 43 | struct cache_index_dir *next;
|
| 44 | struct cache *cache;
|
| 45 | };
|
| 46 |
|
| 47 | |
| 48 | * cache type
|
| 49 | struct cache_type_info {
|
| 50 | const char *name;
|
| 51 | const char *size_prop;
|
| 52 |
|
| 53 | |
| 54 | * [di]-cache-block-size properties. According to the PowerPC
|
| 55 | * Processor binding, -line-size should be provided if it
|
| 56 | * differs from the cache block size (that which is operated
|
| 57 | * on by cache instructions), so we look for -line-size first.
|
| 58 | * See cache_get_line_size().
|
| 59 |
|
| 60 | const char *line_size_props[2];
|
| 61 | const char *nr_sets_prop;
|
| 62 | };
|
| 63 |
|
| 64 |
|
| 65 | #define CACHE_TYPE_UNIFIED 0
|
| 66 | #define CACHE_TYPE_UNIFIED_D 1
|
| 67 | #define CACHE_TYPE_INSTRUCTION 2
|
| 68 | #define CACHE_TYPE_DATA 3
|
| 69 |
|
| 70 | static const struct cache_type_info cache_type_info[] = {
|
| 71 | {
|
| 72 | |
| 73 | * etc. for the Unified (typically L2) cache.
|
| 74 | .name = "Unified",
|
| 75 | .size_prop = "cache-size",
|
| 76 | .line_size_props = { "cache-line-size",
|
| 77 | "cache-block-size", },
|
| 78 | .nr_sets_prop = "cache-sets",
|
| 79 | },
|
| 80 | {
|
| 81 | |
| 82 | * must be equal on unified caches, so just use
|
| 83 | * d-cache properties.
|
| 84 | .name = "Unified",
|
| 85 | .size_prop = "d-cache-size",
|
| 86 | .line_size_props = { "d-cache-line-size",
|
| 87 | "d-cache-block-size", },
|
| 88 | .nr_sets_prop = "d-cache-sets",
|
| 89 | },
|
| 90 | {
|
| 91 | .name = "Instruction",
|
| 92 | .size_prop = "i-cache-size",
|
| 93 | .line_size_props = { "i-cache-line-size",
|
| 94 | "i-cache-block-size", },
|
| 95 | .nr_sets_prop = "i-cache-sets",
|
| 96 | },
|
| 97 | {
|
| 98 | .name = "Data",
|
| 99 | .size_prop = "d-cache-size",
|
| 100 | .line_size_props = { "d-cache-line-size",
|
| 101 | "d-cache-block-size", },
|
| 102 | .nr_sets_prop = "d-cache-sets",
|
| 103 | },
|
| 104 | };
|
| 105 |
|
| 106 | |
| 107 | * in the system. There are separate objects for Harvard caches: one
|
| 108 | * each for instruction and data, and each refers to the same OF node.
|
| 109 | * The refcount of the OF node is elevated for the lifetime of the
|
| 110 | * cache object. A cache object is released when its shared_cpu_map
|
| 111 | * is cleared (see cache_cpu_clear).
|
| 112 | *
|
| 113 | * A cache object is on two lists: an unsorted global list
|
| 114 | * (cache_list) of cache objects; and a singly-linked list
|
| 115 | * representing the local cache hierarchy, which is ordered by level
|
| 116 | * (e.g. L1d -> L1i -> L2 -> L3).
|
| 117 |
|
| 118 | struct cache {
|
| 119 | struct device_node *ofnode;
|
| 120 | struct cpumask shared_cpu_map;
|
| 121 | int type;
|
| 122 | int level;
|
| 123 | int group_id;
|
| 124 | struct list_head list;
|
| 125 | struct cache *next_local;
|
| 126 | };
|
| 127 |
|
| 128 | static DEFINE_PER_CPU(struct cache_dir *, cache_dir_pcpu);
|
| 129 |
|
| 130 | |
| 131 | * access is serialized by cpu hotplug locking
|
| 132 |
|
| 133 | static LIST_HEAD(cache_list);
|
| 134 |
|
| 135 | static struct cache_index_dir *kobj_to_cache_index_dir(struct kobject *k)
|
| 136 | {
|
| 137 | return container_of(k, struct cache_index_dir, kobj);
|
| 138 | }
|
| 139 |
|
| 140 | static const char *cache_type_string(const struct cache *cache)
|
| 141 | {
|
| 142 | return cache_type_info[cache->type].name;
|
| 143 | }
|
| 144 |
|
| 145 | static void cache_init(struct cache *cache, int type, int level,
|
| 146 | struct device_node *ofnode, int group_id)
|
| 147 | {
|
| 148 | cache->type = type;
|
| 149 | cache->level = level;
|
| 150 | cache->ofnode = of_node_get(ofnode);
|
| 151 | cache->group_id = group_id;
|
| 152 | INIT_LIST_HEAD(&cache->list);
|
| 153 | list_add(&cache->list, &cache_list);
|
| 154 | }
|
| 155 |
|
| 156 | static struct cache *new_cache(int type, int level,
|
| 157 | struct device_node *ofnode, int group_id)
|
| 158 | {
|
| 159 | struct cache *cache;
|
| 160 |
|
| 161 | cache = kzalloc_obj(*cache);
|
| 162 | if (cache)
|
| 163 | cache_init(cache, type, level, ofnode, group_id);
|
| 164 |
|
| 165 | return cache;
|
| 166 | }
|
| 167 |
|
| 168 | static void release_cache_debugcheck(struct cache *cache)
|
| 169 | {
|
| 170 | struct cache *iter;
|
| 171 |
|
| 172 | list_for_each_entry(iter, &cache_list, list)
|
| 173 | WARN_ONCE(iter->next_local == cache,
|
| 174 | "cache for %pOFP(%s) refers to cache for %pOFP(%s)\n",
|
| 175 | iter->ofnode,
|
| 176 | cache_type_string(iter),
|
| 177 | cache->ofnode,
|
| 178 | cache_type_string(cache));
|
| 179 | }
|
| 180 |
|
| 181 | static void release_cache(struct cache *cache)
|
| 182 | {
|
| 183 | if (!cache)
|
| 184 | return;
|
| 185 |
|
| 186 | pr_debug("freeing L%d %s cache for %pOFP\n", cache->level,
|
| 187 | cache_type_string(cache), cache->ofnode);
|
| 188 |
|
| 189 | release_cache_debugcheck(cache);
|
| 190 | list_del(&cache->list);
|
| 191 | of_node_put(cache->ofnode);
|
| 192 | kfree(cache);
|
| 193 | }
|
| 194 |
|
| 195 | static void cache_cpu_set(struct cache *cache, int cpu)
|
| 196 | {
|
| 197 | struct cache *next = cache;
|
| 198 |
|
| 199 | while (next) {
|
| 200 | WARN_ONCE(cpumask_test_cpu(cpu, &next->shared_cpu_map),
|
| 201 | "CPU %i already accounted in %pOFP(%s)\n",
|
| 202 | cpu, next->ofnode,
|
| 203 | cache_type_string(next));
|
| 204 | cpumask_set_cpu(cpu, &next->shared_cpu_map);
|
| 205 | next = next->next_local;
|
| 206 | }
|
| 207 | }
|
| 208 |
|
| 209 | static int cache_size(const struct cache *cache, unsigned int *ret)
|
| 210 | {
|
| 211 | const char *propname;
|
| 212 | const __be32 *cache_size;
|
| 213 |
|
| 214 | propname = cache_type_info[cache->type].size_prop;
|
| 215 |
|
| 216 | cache_size = of_get_property(cache->ofnode, propname, NULL);
|
| 217 | if (!cache_size)
|
| 218 | return -ENODEV;
|
| 219 |
|
| 220 | *ret = of_read_number(cache_size, 1);
|
| 221 | return 0;
|
| 222 | }
|
| 223 |
|
| 224 | static int cache_size_kb(const struct cache *cache, unsigned int *ret)
|
| 225 | {
|
| 226 | unsigned int size;
|
| 227 |
|
| 228 | if (cache_size(cache, &size))
|
| 229 | return -ENODEV;
|
| 230 |
|
| 231 | *ret = size / 1024;
|
| 232 | return 0;
|
| 233 | }
|
| 234 |
|
| 235 |
|
| 236 | static int cache_get_line_size(const struct cache *cache, unsigned int *ret)
|
| 237 | {
|
| 238 | const __be32 *line_size;
|
| 239 | int i, lim;
|
| 240 |
|
| 241 | lim = ARRAY_SIZE(cache_type_info[cache->type].line_size_props);
|
| 242 |
|
| 243 | for (i = 0; i < lim; i++) {
|
| 244 | const char *propname;
|
| 245 |
|
| 246 | propname = cache_type_info[cache->type].line_size_props[i];
|
| 247 | line_size = of_get_property(cache->ofnode, propname, NULL);
|
| 248 | if (line_size)
|
| 249 | break;
|
| 250 | }
|
| 251 |
|
| 252 | if (!line_size)
|
| 253 | return -ENODEV;
|
| 254 |
|
| 255 | *ret = of_read_number(line_size, 1);
|
| 256 | return 0;
|
| 257 | }
|
| 258 |
|
| 259 | static int cache_nr_sets(const struct cache *cache, unsigned int *ret)
|
| 260 | {
|
| 261 | const char *propname;
|
| 262 | const __be32 *nr_sets;
|
| 263 |
|
| 264 | propname = cache_type_info[cache->type].nr_sets_prop;
|
| 265 |
|
| 266 | nr_sets = of_get_property(cache->ofnode, propname, NULL);
|
| 267 | if (!nr_sets)
|
| 268 | return -ENODEV;
|
| 269 |
|
| 270 | *ret = of_read_number(nr_sets, 1);
|
| 271 | return 0;
|
| 272 | }
|
| 273 |
|
| 274 | static int cache_associativity(const struct cache *cache, unsigned int *ret)
|
| 275 | {
|
| 276 | unsigned int line_size;
|
| 277 | unsigned int nr_sets;
|
| 278 | unsigned int size;
|
| 279 |
|
| 280 | if (cache_nr_sets(cache, &nr_sets))
|
| 281 | goto err;
|
| 282 |
|
| 283 | |
| 284 | * check the other properties.
|
| 285 |
|
| 286 | if (nr_sets == 1) {
|
| 287 | *ret = 0;
|
| 288 | return 0;
|
| 289 | }
|
| 290 |
|
| 291 | if (cache_get_line_size(cache, &line_size))
|
| 292 | goto err;
|
| 293 | if (cache_size(cache, &size))
|
| 294 | goto err;
|
| 295 |
|
| 296 | if (!(nr_sets > 0 && size > 0 && line_size > 0))
|
| 297 | goto err;
|
| 298 |
|
| 299 | *ret = (size / nr_sets) / line_size;
|
| 300 | return 0;
|
| 301 | err:
|
| 302 | return -ENODEV;
|
| 303 | }
|
| 304 |
|
| 305 |
|
| 306 | static struct cache *cache_find_first_sibling(struct cache *cache)
|
| 307 | {
|
| 308 | struct cache *iter;
|
| 309 |
|
| 310 | if (cache->type == CACHE_TYPE_UNIFIED ||
|
| 311 | cache->type == CACHE_TYPE_UNIFIED_D)
|
| 312 | return cache;
|
| 313 |
|
| 314 | list_for_each_entry(iter, &cache_list, list)
|
| 315 | if (iter->ofnode == cache->ofnode &&
|
| 316 | iter->group_id == cache->group_id &&
|
| 317 | iter->next_local == cache)
|
| 318 | return iter;
|
| 319 |
|
| 320 | return cache;
|
| 321 | }
|
| 322 |
|
| 323 |
|
| 324 | static struct cache *cache_lookup_by_node_group(const struct device_node *node,
|
| 325 | int group_id)
|
| 326 | {
|
| 327 | struct cache *cache = NULL;
|
| 328 | struct cache *iter;
|
| 329 |
|
| 330 | list_for_each_entry(iter, &cache_list, list) {
|
| 331 | if (iter->ofnode != node ||
|
| 332 | iter->group_id != group_id)
|
| 333 | continue;
|
| 334 | cache = cache_find_first_sibling(iter);
|
| 335 | break;
|
| 336 | }
|
| 337 |
|
| 338 | return cache;
|
| 339 | }
|
| 340 |
|
| 341 | static bool cache_node_is_unified(const struct device_node *np)
|
| 342 | {
|
| 343 | return of_get_property(np, "cache-unified", NULL);
|
| 344 | }
|
| 345 |
|
| 346 | |
| 347 | * Unified caches can have two different sets of tags. Most embedded
|
| 348 | * use cache-size, etc. for the unified cache size, but open firmware systems
|
| 349 | * use d-cache-size, etc. Check on initialization for which type we have, and
|
| 350 | * return the appropriate structure type. Assume it's embedded if it isn't
|
| 351 | * open firmware. If it's yet a 3rd type, then there will be missing entries
|
| 352 | * in /sys/devices/system/cpu/cpu0/cache/index2/, and this code will need
|
| 353 | * to be extended further.
|
| 354 |
|
| 355 | static int cache_is_unified_d(const struct device_node *np)
|
| 356 | {
|
| 357 | return of_get_property(np,
|
| 358 | cache_type_info[CACHE_TYPE_UNIFIED_D].size_prop, NULL) ?
|
| 359 | CACHE_TYPE_UNIFIED_D : CACHE_TYPE_UNIFIED;
|
| 360 | }
|
| 361 |
|
| 362 | static struct cache *cache_do_one_devnode_unified(struct device_node *node, int group_id,
|
| 363 | int level)
|
| 364 | {
|
| 365 | pr_debug("creating L%d ucache for %pOFP\n", level, node);
|
| 366 |
|
| 367 | return new_cache(cache_is_unified_d(node), level, node, group_id);
|
| 368 | }
|
| 369 |
|
| 370 | static struct cache *cache_do_one_devnode_split(struct device_node *node, int group_id,
|
| 371 | int level)
|
| 372 | {
|
| 373 | struct cache *dcache, *icache;
|
| 374 |
|
| 375 | pr_debug("creating L%d dcache and icache for %pOFP\n", level,
|
| 376 | node);
|
| 377 |
|
| 378 | dcache = new_cache(CACHE_TYPE_DATA, level, node, group_id);
|
| 379 | icache = new_cache(CACHE_TYPE_INSTRUCTION, level, node, group_id);
|
| 380 |
|
| 381 | if (!dcache || !icache)
|
| 382 | goto err;
|
| 383 |
|
| 384 | dcache->next_local = icache;
|
| 385 |
|
| 386 | return dcache;
|
| 387 | err:
|
| 388 | release_cache(dcache);
|
| 389 | release_cache(icache);
|
| 390 | return NULL;
|
| 391 | }
|
| 392 |
|
| 393 | static struct cache *cache_do_one_devnode(struct device_node *node, int group_id, int level)
|
| 394 | {
|
| 395 | struct cache *cache;
|
| 396 |
|
| 397 | if (cache_node_is_unified(node))
|
| 398 | cache = cache_do_one_devnode_unified(node, group_id, level);
|
| 399 | else
|
| 400 | cache = cache_do_one_devnode_split(node, group_id, level);
|
| 401 |
|
| 402 | return cache;
|
| 403 | }
|
| 404 |
|
| 405 | static struct cache *cache_lookup_or_instantiate(struct device_node *node,
|
| 406 | int group_id,
|
| 407 | int level)
|
| 408 | {
|
| 409 | struct cache *cache;
|
| 410 |
|
| 411 | cache = cache_lookup_by_node_group(node, group_id);
|
| 412 |
|
| 413 | WARN_ONCE(cache && cache->level != level,
|
| 414 | "cache level mismatch on lookup (got %d, expected %d)\n",
|
| 415 | cache->level, level);
|
| 416 |
|
| 417 | if (!cache)
|
| 418 | cache = cache_do_one_devnode(node, group_id, level);
|
| 419 |
|
| 420 | return cache;
|
| 421 | }
|
| 422 |
|
| 423 | static void link_cache_lists(struct cache *smaller, struct cache *bigger)
|
| 424 | {
|
| 425 | while (smaller->next_local) {
|
| 426 | if (smaller->next_local == bigger)
|
| 427 | return;
|
| 428 | smaller = smaller->next_local;
|
| 429 | }
|
| 430 |
|
| 431 | smaller->next_local = bigger;
|
| 432 |
|
| 433 | |
| 434 | * The cache->next_local list sorts by level ascending:
|
| 435 | * L1d -> L1i -> L2 -> L3 ...
|
| 436 |
|
| 437 | WARN_ONCE((smaller->level == 1 && bigger->level > 2) ||
|
| 438 | (smaller->level > 1 && bigger->level != smaller->level + 1),
|
| 439 | "linking L%i cache %pOFP to L%i cache %pOFP; skipped a level?\n",
|
| 440 | smaller->level, smaller->ofnode, bigger->level, bigger->ofnode);
|
| 441 | }
|
| 442 |
|
| 443 | static void do_subsidiary_caches_debugcheck(struct cache *cache)
|
| 444 | {
|
| 445 | WARN_ONCE(cache->level != 1,
|
| 446 | "instantiating cache chain from L%d %s cache for "
|
| 447 | "%pOFP instead of an L1\n", cache->level,
|
| 448 | cache_type_string(cache), cache->ofnode);
|
| 449 | WARN_ONCE(!of_node_is_type(cache->ofnode, "cpu"),
|
| 450 | "instantiating cache chain from node %pOFP of type '%s' "
|
| 451 | "instead of a cpu node\n", cache->ofnode,
|
| 452 | of_node_get_device_type(cache->ofnode));
|
| 453 | }
|
| 454 |
|
| 455 | |
| 456 | * If sub-groups of threads in a core containing @cpu_id share the
|
| 457 | * L@level-cache (information obtained via "ibm,thread-groups"
|
| 458 | * device-tree property), then we identify the group by the first
|
| 459 | * thread-sibling in the group. We define this to be the group-id.
|
| 460 | *
|
| 461 | * In the absence of any thread-group information for L@level-cache,
|
| 462 | * this function returns -1.
|
| 463 |
|
| 464 | static int get_group_id(unsigned int cpu_id, int level)
|
| 465 | {
|
| 466 | if (has_big_cores && level == 1)
|
| 467 | return cpumask_first(per_cpu(thread_group_l1_cache_map,
|
| 468 | cpu_id));
|
| 469 | else if (thread_group_shares_l2 && level == 2)
|
| 470 | return cpumask_first(per_cpu(thread_group_l2_cache_map,
|
| 471 | cpu_id));
|
| 472 | else if (thread_group_shares_l3 && level == 3)
|
| 473 | return cpumask_first(per_cpu(thread_group_l3_cache_map,
|
| 474 | cpu_id));
|
| 475 | return -1;
|
| 476 | }
|
| 477 |
|
| 478 | static void do_subsidiary_caches(struct cache *cache, unsigned int cpu_id)
|
| 479 | {
|
| 480 | struct device_node *subcache_node;
|
| 481 | int level = cache->level;
|
| 482 |
|
| 483 | do_subsidiary_caches_debugcheck(cache);
|
| 484 |
|
| 485 | while ((subcache_node = of_find_next_cache_node(cache->ofnode))) {
|
| 486 | struct cache *subcache;
|
| 487 | int group_id;
|
| 488 |
|
| 489 | level++;
|
| 490 | group_id = get_group_id(cpu_id, level);
|
| 491 | subcache = cache_lookup_or_instantiate(subcache_node, group_id, level);
|
| 492 | of_node_put(subcache_node);
|
| 493 | if (!subcache)
|
| 494 | break;
|
| 495 |
|
| 496 | link_cache_lists(cache, subcache);
|
| 497 | cache = subcache;
|
| 498 | }
|
| 499 | }
|
| 500 |
|
| 501 | static struct cache *cache_chain_instantiate(unsigned int cpu_id)
|
| 502 | {
|
| 503 | struct device_node *cpu_node;
|
| 504 | struct cache *cpu_cache = NULL;
|
| 505 | int group_id;
|
| 506 |
|
| 507 | pr_debug("creating cache object(s) for CPU %i\n", cpu_id);
|
| 508 |
|
| 509 | cpu_node = of_get_cpu_node(cpu_id, NULL);
|
| 510 | WARN_ONCE(!cpu_node, "no OF node found for CPU %i\n", cpu_id);
|
| 511 | if (!cpu_node)
|
| 512 | goto out;
|
| 513 |
|
| 514 | group_id = get_group_id(cpu_id, 1);
|
| 515 |
|
| 516 | cpu_cache = cache_lookup_or_instantiate(cpu_node, group_id, 1);
|
| 517 | if (!cpu_cache)
|
| 518 | goto out;
|
| 519 |
|
| 520 | do_subsidiary_caches(cpu_cache, cpu_id);
|
| 521 |
|
| 522 | cache_cpu_set(cpu_cache, cpu_id);
|
| 523 | out:
|
| 524 | of_node_put(cpu_node);
|
| 525 |
|
| 526 | return cpu_cache;
|
| 527 | }
|
| 528 |
|
| 529 | static struct cache_dir *cacheinfo_create_cache_dir(unsigned int cpu_id)
|
| 530 | {
|
| 531 | struct cache_dir *cache_dir;
|
| 532 | struct device *dev;
|
| 533 | struct kobject *kobj = NULL;
|
| 534 |
|
| 535 | dev = get_cpu_device(cpu_id);
|
| 536 | WARN_ONCE(!dev, "no dev for CPU %i\n", cpu_id);
|
| 537 | if (!dev)
|
| 538 | goto err;
|
| 539 |
|
| 540 | kobj = kobject_create_and_add("cache", &dev->kobj);
|
| 541 | if (!kobj)
|
| 542 | goto err;
|
| 543 |
|
| 544 | cache_dir = kzalloc_obj(*cache_dir);
|
| 545 | if (!cache_dir)
|
| 546 | goto err;
|
| 547 |
|
| 548 | cache_dir->kobj = kobj;
|
| 549 |
|
| 550 | WARN_ON_ONCE(per_cpu(cache_dir_pcpu, cpu_id) != NULL);
|
| 551 |
|
| 552 | per_cpu(cache_dir_pcpu, cpu_id) = cache_dir;
|
| 553 |
|
| 554 | return cache_dir;
|
| 555 | err:
|
| 556 | kobject_put(kobj);
|
| 557 | return NULL;
|
| 558 | }
|
| 559 |
|
| 560 | static void cache_index_release(struct kobject *kobj)
|
| 561 | {
|
| 562 | struct cache_index_dir *index;
|
| 563 |
|
| 564 | index = kobj_to_cache_index_dir(kobj);
|
| 565 |
|
| 566 | pr_debug("freeing index directory for L%d %s cache\n",
|
| 567 | index->cache->level, cache_type_string(index->cache));
|
| 568 |
|
| 569 | kfree(index);
|
| 570 | }
|
| 571 |
|
| 572 | static ssize_t cache_index_show(struct kobject *k, struct attribute *attr, char *buf)
|
| 573 | {
|
| 574 | struct kobj_attribute *kobj_attr;
|
| 575 |
|
| 576 | kobj_attr = container_of(attr, struct kobj_attribute, attr);
|
| 577 |
|
| 578 | return kobj_attr->show(k, kobj_attr, buf);
|
| 579 | }
|
| 580 |
|
| 581 | static struct cache *index_kobj_to_cache(struct kobject *k)
|
| 582 | {
|
| 583 | struct cache_index_dir *index;
|
| 584 |
|
| 585 | index = kobj_to_cache_index_dir(k);
|
| 586 |
|
| 587 | return index->cache;
|
| 588 | }
|
| 589 |
|
| 590 | static ssize_t size_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
|
| 591 | {
|
| 592 | unsigned int size_kb;
|
| 593 | struct cache *cache;
|
| 594 |
|
| 595 | cache = index_kobj_to_cache(k);
|
| 596 |
|
| 597 | if (cache_size_kb(cache, &size_kb))
|
| 598 | return -ENODEV;
|
| 599 |
|
| 600 | return sysfs_emit(buf, "%uK\n", size_kb);
|
| 601 | }
|
| 602 |
|
| 603 | static struct kobj_attribute cache_size_attr =
|
| 604 | __ATTR(size, 0444, size_show, NULL);
|
| 605 |
|
| 606 |
|
| 607 | static ssize_t line_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
|
| 608 | {
|
| 609 | unsigned int line_size;
|
| 610 | struct cache *cache;
|
| 611 |
|
| 612 | cache = index_kobj_to_cache(k);
|
| 613 |
|
| 614 | if (cache_get_line_size(cache, &line_size))
|
| 615 | return -ENODEV;
|
| 616 |
|
| 617 | return sysfs_emit(buf, "%u\n", line_size);
|
| 618 | }
|
| 619 |
|
| 620 | static struct kobj_attribute cache_line_size_attr =
|
| 621 | __ATTR(coherency_line_size, 0444, line_size_show, NULL);
|
| 622 |
|
| 623 | static ssize_t nr_sets_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
|
| 624 | {
|
| 625 | unsigned int nr_sets;
|
| 626 | struct cache *cache;
|
| 627 |
|
| 628 | cache = index_kobj_to_cache(k);
|
| 629 |
|
| 630 | if (cache_nr_sets(cache, &nr_sets))
|
| 631 | return -ENODEV;
|
| 632 |
|
| 633 | return sysfs_emit(buf, "%u\n", nr_sets);
|
| 634 | }
|
| 635 |
|
| 636 | static struct kobj_attribute cache_nr_sets_attr =
|
| 637 | __ATTR(number_of_sets, 0444, nr_sets_show, NULL);
|
| 638 |
|
| 639 | static ssize_t associativity_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
|
| 640 | {
|
| 641 | unsigned int associativity;
|
| 642 | struct cache *cache;
|
| 643 |
|
| 644 | cache = index_kobj_to_cache(k);
|
| 645 |
|
| 646 | if (cache_associativity(cache, &associativity))
|
| 647 | return -ENODEV;
|
| 648 |
|
| 649 | return sysfs_emit(buf, "%u\n", associativity);
|
| 650 | }
|
| 651 |
|
| 652 | static struct kobj_attribute cache_assoc_attr =
|
| 653 | __ATTR(ways_of_associativity, 0444, associativity_show, NULL);
|
| 654 |
|
| 655 | static ssize_t type_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
|
| 656 | {
|
| 657 | struct cache *cache;
|
| 658 |
|
| 659 | cache = index_kobj_to_cache(k);
|
| 660 |
|
| 661 | return sysfs_emit(buf, "%s\n", cache_type_string(cache));
|
| 662 | }
|
| 663 |
|
| 664 | static struct kobj_attribute cache_type_attr =
|
| 665 | __ATTR(type, 0444, type_show, NULL);
|
| 666 |
|
| 667 | static ssize_t level_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
|
| 668 | {
|
| 669 | struct cache_index_dir *index;
|
| 670 | struct cache *cache;
|
| 671 |
|
| 672 | index = kobj_to_cache_index_dir(k);
|
| 673 | cache = index->cache;
|
| 674 |
|
| 675 | return sysfs_emit(buf, "%d\n", cache->level);
|
| 676 | }
|
| 677 |
|
| 678 | static struct kobj_attribute cache_level_attr =
|
| 679 | __ATTR(level, 0444, level_show, NULL);
|
| 680 |
|
| 681 | static ssize_t
|
| 682 | show_shared_cpumap(struct kobject *k, struct kobj_attribute *attr, char *buf, bool list)
|
| 683 | {
|
| 684 | struct cache_index_dir *index;
|
| 685 | struct cache *cache;
|
| 686 | const struct cpumask *mask;
|
| 687 |
|
| 688 | index = kobj_to_cache_index_dir(k);
|
| 689 | cache = index->cache;
|
| 690 |
|
| 691 | mask = &cache->shared_cpu_map;
|
| 692 |
|
| 693 | return sysfs_emit(buf, list ? "%*pbl\n" : "%*pb\n",
|
| 694 | cpumask_pr_args(mask));
|
| 695 | }
|
| 696 |
|
| 697 | static ssize_t shared_cpu_map_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
|
| 698 | {
|
| 699 | return show_shared_cpumap(k, attr, buf, false);
|
| 700 | }
|
| 701 |
|
| 702 | static ssize_t shared_cpu_list_show(struct kobject *k, struct kobj_attribute *attr, char *buf)
|
| 703 | {
|
| 704 | return show_shared_cpumap(k, attr, buf, true);
|
| 705 | }
|
| 706 |
|
| 707 | static struct kobj_attribute cache_shared_cpu_map_attr =
|
| 708 | __ATTR(shared_cpu_map, 0444, shared_cpu_map_show, NULL);
|
| 709 |
|
| 710 | static struct kobj_attribute cache_shared_cpu_list_attr =
|
| 711 | __ATTR(shared_cpu_list, 0444, shared_cpu_list_show, NULL);
|
| 712 |
|
| 713 | |
| 714 | * does this automatically via kobj_type->default_groups. This is the
|
| 715 | * minimum data required to uniquely identify a cache.
|
| 716 |
|
| 717 | static struct attribute *cache_index_default_attrs[] = {
|
| 718 | &cache_type_attr.attr,
|
| 719 | &cache_level_attr.attr,
|
| 720 | &cache_shared_cpu_map_attr.attr,
|
| 721 | &cache_shared_cpu_list_attr.attr,
|
| 722 | NULL,
|
| 723 | };
|
| 724 | ATTRIBUTE_GROUPS(cache_index_default);
|
| 725 |
|
| 726 | |
| 727 | * right properties -- see cacheinfo_create_index_opt_attrs
|
| 728 |
|
| 729 | static struct kobj_attribute *cache_index_opt_attrs[] = {
|
| 730 | &cache_size_attr,
|
| 731 | &cache_line_size_attr,
|
| 732 | &cache_nr_sets_attr,
|
| 733 | &cache_assoc_attr,
|
| 734 | };
|
| 735 |
|
| 736 | static const struct sysfs_ops cache_index_ops = {
|
| 737 | .show = cache_index_show,
|
| 738 | };
|
| 739 |
|
| 740 | static const struct kobj_type cache_index_type = {
|
| 741 | .release = cache_index_release,
|
| 742 | .sysfs_ops = &cache_index_ops,
|
| 743 | .default_groups = cache_index_default_groups,
|
| 744 | };
|
| 745 |
|
| 746 | static void cacheinfo_create_index_opt_attrs(struct cache_index_dir *dir)
|
| 747 | {
|
| 748 | const char *cache_type;
|
| 749 | struct cache *cache;
|
| 750 | char *buf;
|
| 751 | int i;
|
| 752 |
|
| 753 | buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
| 754 | if (!buf)
|
| 755 | return;
|
| 756 |
|
| 757 | cache = dir->cache;
|
| 758 | cache_type = cache_type_string(cache);
|
| 759 |
|
| 760 | |
| 761 | * meaningful value. Check the return value of each optional
|
| 762 | * attribute's ->show method before registering the
|
| 763 | * attribute.
|
| 764 |
|
| 765 | for (i = 0; i < ARRAY_SIZE(cache_index_opt_attrs); i++) {
|
| 766 | struct kobj_attribute *attr;
|
| 767 | ssize_t rc;
|
| 768 |
|
| 769 | attr = cache_index_opt_attrs[i];
|
| 770 |
|
| 771 | rc = attr->show(&dir->kobj, attr, buf);
|
| 772 | if (rc <= 0) {
|
| 773 | pr_debug("not creating %s attribute for "
|
| 774 | "%pOFP(%s) (rc = %zd)\n",
|
| 775 | attr->attr.name, cache->ofnode,
|
| 776 | cache_type, rc);
|
| 777 | continue;
|
| 778 | }
|
| 779 | if (sysfs_create_file(&dir->kobj, &attr->attr))
|
| 780 | pr_debug("could not create %s attribute for %pOFP(%s)\n",
|
| 781 | attr->attr.name, cache->ofnode, cache_type);
|
| 782 | }
|
| 783 |
|
| 784 | kfree(buf);
|
| 785 | }
|
| 786 |
|
| 787 | static void cacheinfo_create_index_dir(struct cache *cache, int index,
|
| 788 | struct cache_dir *cache_dir)
|
| 789 | {
|
| 790 | struct cache_index_dir *index_dir;
|
| 791 | int rc;
|
| 792 |
|
| 793 | index_dir = kzalloc_obj(*index_dir);
|
| 794 | if (!index_dir)
|
| 795 | return;
|
| 796 |
|
| 797 | index_dir->cache = cache;
|
| 798 |
|
| 799 | rc = kobject_init_and_add(&index_dir->kobj, &cache_index_type,
|
| 800 | cache_dir->kobj, "index%d", index);
|
| 801 | if (rc) {
|
| 802 | kobject_put(&index_dir->kobj);
|
| 803 | return;
|
| 804 | }
|
| 805 |
|
| 806 | index_dir->next = cache_dir->index;
|
| 807 | cache_dir->index = index_dir;
|
| 808 |
|
| 809 | cacheinfo_create_index_opt_attrs(index_dir);
|
| 810 | }
|
| 811 |
|
| 812 | static void cacheinfo_sysfs_populate(unsigned int cpu_id,
|
| 813 | struct cache *cache_list)
|
| 814 | {
|
| 815 | struct cache_dir *cache_dir;
|
| 816 | struct cache *cache;
|
| 817 | int index = 0;
|
| 818 |
|
| 819 | cache_dir = cacheinfo_create_cache_dir(cpu_id);
|
| 820 | if (!cache_dir)
|
| 821 | return;
|
| 822 |
|
| 823 | cache = cache_list;
|
| 824 | while (cache) {
|
| 825 | cacheinfo_create_index_dir(cache, index, cache_dir);
|
| 826 | index++;
|
| 827 | cache = cache->next_local;
|
| 828 | }
|
| 829 | }
|
| 830 |
|
| 831 | void cacheinfo_cpu_online(unsigned int cpu_id)
|
| 832 | {
|
| 833 | struct cache *cache;
|
| 834 |
|
| 835 | cache = cache_chain_instantiate(cpu_id);
|
| 836 | if (!cache)
|
| 837 | return;
|
| 838 |
|
| 839 | cacheinfo_sysfs_populate(cpu_id, cache);
|
| 840 | }
|
| 841 |
|
| 842 |
|
| 843 |
|
| 844 | #if (defined(CONFIG_PPC_PSERIES) && defined(CONFIG_SUSPEND)) || \
|
| 845 | defined(CONFIG_HOTPLUG_CPU)
|
| 846 |
|
| 847 | static struct cache *cache_lookup_by_cpu(unsigned int cpu_id)
|
| 848 | {
|
| 849 | struct device_node *cpu_node;
|
| 850 | struct cache *cache;
|
| 851 | int group_id;
|
| 852 |
|
| 853 | cpu_node = of_get_cpu_node(cpu_id, NULL);
|
| 854 | WARN_ONCE(!cpu_node, "no OF node found for CPU %i\n", cpu_id);
|
| 855 | if (!cpu_node)
|
| 856 | return NULL;
|
| 857 |
|
| 858 | group_id = get_group_id(cpu_id, 1);
|
| 859 | cache = cache_lookup_by_node_group(cpu_node, group_id);
|
| 860 | of_node_put(cpu_node);
|
| 861 |
|
| 862 | return cache;
|
| 863 | }
|
| 864 |
|
| 865 | static void remove_index_dirs(struct cache_dir *cache_dir)
|
| 866 | {
|
| 867 | struct cache_index_dir *index;
|
| 868 |
|
| 869 | index = cache_dir->index;
|
| 870 |
|
| 871 | while (index) {
|
| 872 | struct cache_index_dir *next;
|
| 873 |
|
| 874 | next = index->next;
|
| 875 | kobject_put(&index->kobj);
|
| 876 | index = next;
|
| 877 | }
|
| 878 | }
|
| 879 |
|
| 880 | static void remove_cache_dir(struct cache_dir *cache_dir)
|
| 881 | {
|
| 882 | remove_index_dirs(cache_dir);
|
| 883 |
|
| 884 |
|
| 885 | kobject_del(cache_dir->kobj);
|
| 886 |
|
| 887 | kobject_put(cache_dir->kobj);
|
| 888 |
|
| 889 | kfree(cache_dir);
|
| 890 | }
|
| 891 |
|
| 892 | static void cache_cpu_clear(struct cache *cache, int cpu)
|
| 893 | {
|
| 894 | while (cache) {
|
| 895 | struct cache *next = cache->next_local;
|
| 896 |
|
| 897 | WARN_ONCE(!cpumask_test_cpu(cpu, &cache->shared_cpu_map),
|
| 898 | "CPU %i not accounted in %pOFP(%s)\n",
|
| 899 | cpu, cache->ofnode,
|
| 900 | cache_type_string(cache));
|
| 901 |
|
| 902 | cpumask_clear_cpu(cpu, &cache->shared_cpu_map);
|
| 903 |
|
| 904 | |
| 905 | * are offline
|
| 906 | if (cpumask_empty(&cache->shared_cpu_map))
|
| 907 | release_cache(cache);
|
| 908 |
|
| 909 | cache = next;
|
| 910 | }
|
| 911 | }
|
| 912 |
|
| 913 | void cacheinfo_cpu_offline(unsigned int cpu_id)
|
| 914 | {
|
| 915 | struct cache_dir *cache_dir;
|
| 916 | struct cache *cache;
|
| 917 |
|
| 918 | |
| 919 | * the sysfs hierarchy first
|
| 920 | cache_dir = per_cpu(cache_dir_pcpu, cpu_id);
|
| 921 |
|
| 922 |
|
| 923 | if (cache_dir)
|
| 924 | remove_cache_dir(cache_dir);
|
| 925 |
|
| 926 | per_cpu(cache_dir_pcpu, cpu_id) = NULL;
|
| 927 |
|
| 928 | |
| 929 | * cache objects
|
| 930 | cache = cache_lookup_by_cpu(cpu_id);
|
| 931 | if (cache)
|
| 932 | cache_cpu_clear(cache, cpu_id);
|
| 933 | }
|
| 934 |
|
| 935 | void cacheinfo_teardown(void)
|
| 936 | {
|
| 937 | unsigned int cpu;
|
| 938 |
|
| 939 | lockdep_assert_cpus_held();
|
| 940 |
|
| 941 | for_each_online_cpu(cpu)
|
| 942 | cacheinfo_cpu_offline(cpu);
|
| 943 | }
|
| 944 |
|
| 945 | void cacheinfo_rebuild(void)
|
| 946 | {
|
| 947 | unsigned int cpu;
|
| 948 |
|
| 949 | lockdep_assert_cpus_held();
|
| 950 |
|
| 951 | for_each_online_cpu(cpu)
|
| 952 | cacheinfo_cpu_online(cpu);
|
| 953 | }
|
| 954 |
|
| 955 | #endif
|