| #include <errno.h> |
| #include <inttypes.h> |
| #include <asm/bug.h> |
| #include <linux/bitmap.h> |
| #include <linux/kernel.h> |
| #include <linux/zalloc.h> |
| #include "debug.h" |
| #include "env.h" |
| #include "mem2node.h" |
| |
| struct phys_entry { |
| struct rb_node rb_node; |
| u64 start; |
| u64 end; |
| u64 node; |
| }; |
| |
| static void phys_entry__insert(struct phys_entry *entry, struct rb_root *root) |
| { |
| struct rb_node **p = &root->rb_node; |
| struct rb_node *parent = NULL; |
| struct phys_entry *e; |
| |
| while (*p != NULL) { |
| parent = *p; |
| e = rb_entry(parent, struct phys_entry, rb_node); |
| |
| if (entry->start < e->start) |
| p = &(*p)->rb_left; |
| else |
| p = &(*p)->rb_right; |
| } |
| |
| rb_link_node(&entry->rb_node, parent, p); |
| rb_insert_color(&entry->rb_node, root); |
| } |
| |
| static void |
| phys_entry__init(struct phys_entry *entry, u64 start, u64 bsize, u64 node) |
| { |
| entry->start = start; |
| entry->end = start + bsize; |
| entry->node = node; |
| RB_CLEAR_NODE(&entry->rb_node); |
| } |
| |
| int mem2node__init(struct mem2node *map, struct perf_env *env) |
| { |
| struct memory_node *n, *nodes = &env->memory_nodes[0]; |
| struct phys_entry *entries, *tmp_entries; |
| u64 bsize = env->memory_bsize; |
| int i, j = 0, max = 0; |
| |
| memset(map, 0x0, sizeof(*map)); |
| map->root = RB_ROOT; |
| |
| for (i = 0; i < env->nr_memory_nodes; i++) { |
| n = &nodes[i]; |
| max += bitmap_weight(n->set, n->size); |
| } |
| |
| entries = zalloc(sizeof(*entries) * max); |
| if (!entries) |
| return -ENOMEM; |
| |
| for (i = 0; i < env->nr_memory_nodes; i++) { |
| u64 bit; |
| |
| n = &nodes[i]; |
| |
| for (bit = 0; bit < n->size; bit++) { |
| u64 start; |
| |
| if (!test_bit(bit, n->set)) |
| continue; |
| |
| start = bit * bsize; |
| |
| /* |
| * Merge nearby areas, we walk in order |
| * through the bitmap, so no need to sort. |
| */ |
| if (j > 0) { |
| struct phys_entry *prev = &entries[j - 1]; |
| |
| if ((prev->end == start) && |
| (prev->node == n->node)) { |
| prev->end += bsize; |
| continue; |
| } |
| } |
| |
| phys_entry__init(&entries[j++], start, bsize, n->node); |
| } |
| } |
| |
| /* Cut unused entries, due to merging. */ |
| tmp_entries = realloc(entries, sizeof(*entries) * j); |
| if (tmp_entries || |
| WARN_ONCE(j == 0, "No memory nodes, is CONFIG_MEMORY_HOTPLUG enabled?\n")) |
| entries = tmp_entries; |
| |
| for (i = 0; i < j; i++) { |
| pr_debug("mem2node %03" PRIu64 " [0x%016" PRIx64 "-0x%016" PRIx64 "]\n", |
| entries[i].node, entries[i].start, entries[i].end); |
| |
| phys_entry__insert(&entries[i], &map->root); |
| } |
| |
| map->entries = entries; |
| return 0; |
| } |
| |
| void mem2node__exit(struct mem2node *map) |
| { |
| zfree(&map->entries); |
| } |
| |
| int mem2node__node(struct mem2node *map, u64 addr) |
| { |
| struct rb_node **p, *parent = NULL; |
| struct phys_entry *entry; |
| |
| p = &map->root.rb_node; |
| while (*p != NULL) { |
| parent = *p; |
| entry = rb_entry(parent, struct phys_entry, rb_node); |
| if (addr < entry->start) |
| p = &(*p)->rb_left; |
| else if (addr >= entry->end) |
| p = &(*p)->rb_right; |
| else |
| goto out; |
| } |
| |
| entry = NULL; |
| out: |
| return entry ? (int) entry->node : -1; |
| } |