162306a36Sopenharmony_ci#include <errno.h>
262306a36Sopenharmony_ci#include <inttypes.h>
362306a36Sopenharmony_ci#include <asm/bug.h>
462306a36Sopenharmony_ci#include <linux/bitmap.h>
562306a36Sopenharmony_ci#include <linux/kernel.h>
662306a36Sopenharmony_ci#include <linux/zalloc.h>
762306a36Sopenharmony_ci#include "debug.h"
862306a36Sopenharmony_ci#include "env.h"
962306a36Sopenharmony_ci#include "mem2node.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistruct phys_entry {
1262306a36Sopenharmony_ci	struct rb_node	rb_node;
1362306a36Sopenharmony_ci	u64	start;
1462306a36Sopenharmony_ci	u64	end;
1562306a36Sopenharmony_ci	u64	node;
1662306a36Sopenharmony_ci};
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic void phys_entry__insert(struct phys_entry *entry, struct rb_root *root)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	struct rb_node **p = &root->rb_node;
2162306a36Sopenharmony_ci	struct rb_node *parent = NULL;
2262306a36Sopenharmony_ci	struct phys_entry *e;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	while (*p != NULL) {
2562306a36Sopenharmony_ci		parent = *p;
2662306a36Sopenharmony_ci		e = rb_entry(parent, struct phys_entry, rb_node);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci		if (entry->start < e->start)
2962306a36Sopenharmony_ci			p = &(*p)->rb_left;
3062306a36Sopenharmony_ci		else
3162306a36Sopenharmony_ci			p = &(*p)->rb_right;
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	rb_link_node(&entry->rb_node, parent, p);
3562306a36Sopenharmony_ci	rb_insert_color(&entry->rb_node, root);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void
3962306a36Sopenharmony_ciphys_entry__init(struct phys_entry *entry, u64 start, u64 bsize, u64 node)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	entry->start = start;
4262306a36Sopenharmony_ci	entry->end   = start + bsize;
4362306a36Sopenharmony_ci	entry->node  = node;
4462306a36Sopenharmony_ci	RB_CLEAR_NODE(&entry->rb_node);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciint mem2node__init(struct mem2node *map, struct perf_env *env)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct memory_node *n, *nodes = &env->memory_nodes[0];
5062306a36Sopenharmony_ci	struct phys_entry *entries, *tmp_entries;
5162306a36Sopenharmony_ci	u64 bsize = env->memory_bsize;
5262306a36Sopenharmony_ci	int i, j = 0, max = 0;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	memset(map, 0x0, sizeof(*map));
5562306a36Sopenharmony_ci	map->root = RB_ROOT;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	for (i = 0; i < env->nr_memory_nodes; i++) {
5862306a36Sopenharmony_ci		n = &nodes[i];
5962306a36Sopenharmony_ci		max += bitmap_weight(n->set, n->size);
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	entries = zalloc(sizeof(*entries) * max);
6362306a36Sopenharmony_ci	if (!entries)
6462306a36Sopenharmony_ci		return -ENOMEM;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	for (i = 0; i < env->nr_memory_nodes; i++) {
6762306a36Sopenharmony_ci		u64 bit;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		n = &nodes[i];
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		for (bit = 0; bit < n->size; bit++) {
7262306a36Sopenharmony_ci			u64 start;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci			if (!test_bit(bit, n->set))
7562306a36Sopenharmony_ci				continue;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci			start = bit * bsize;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci			/*
8062306a36Sopenharmony_ci			 * Merge nearby areas, we walk in order
8162306a36Sopenharmony_ci			 * through the bitmap, so no need to sort.
8262306a36Sopenharmony_ci			 */
8362306a36Sopenharmony_ci			if (j > 0) {
8462306a36Sopenharmony_ci				struct phys_entry *prev = &entries[j - 1];
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci				if ((prev->end == start) &&
8762306a36Sopenharmony_ci				    (prev->node == n->node)) {
8862306a36Sopenharmony_ci					prev->end += bsize;
8962306a36Sopenharmony_ci					continue;
9062306a36Sopenharmony_ci				}
9162306a36Sopenharmony_ci			}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci			phys_entry__init(&entries[j++], start, bsize, n->node);
9462306a36Sopenharmony_ci		}
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/* Cut unused entries, due to merging. */
9862306a36Sopenharmony_ci	tmp_entries = realloc(entries, sizeof(*entries) * j);
9962306a36Sopenharmony_ci	if (tmp_entries ||
10062306a36Sopenharmony_ci	    WARN_ONCE(j == 0, "No memory nodes, is CONFIG_MEMORY_HOTPLUG enabled?\n"))
10162306a36Sopenharmony_ci		entries = tmp_entries;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	for (i = 0; i < j; i++) {
10462306a36Sopenharmony_ci		pr_debug("mem2node %03" PRIu64 " [0x%016" PRIx64 "-0x%016" PRIx64 "]\n",
10562306a36Sopenharmony_ci			 entries[i].node, entries[i].start, entries[i].end);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		phys_entry__insert(&entries[i], &map->root);
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	map->entries = entries;
11162306a36Sopenharmony_ci	return 0;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_civoid mem2node__exit(struct mem2node *map)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	zfree(&map->entries);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ciint mem2node__node(struct mem2node *map, u64 addr)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct rb_node **p, *parent = NULL;
12262306a36Sopenharmony_ci	struct phys_entry *entry;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	p = &map->root.rb_node;
12562306a36Sopenharmony_ci	while (*p != NULL) {
12662306a36Sopenharmony_ci		parent = *p;
12762306a36Sopenharmony_ci		entry = rb_entry(parent, struct phys_entry, rb_node);
12862306a36Sopenharmony_ci		if (addr < entry->start)
12962306a36Sopenharmony_ci			p = &(*p)->rb_left;
13062306a36Sopenharmony_ci		else if (addr >= entry->end)
13162306a36Sopenharmony_ci			p = &(*p)->rb_right;
13262306a36Sopenharmony_ci		else
13362306a36Sopenharmony_ci			goto out;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	entry = NULL;
13762306a36Sopenharmony_ciout:
13862306a36Sopenharmony_ci	return entry ? (int) entry->node : -1;
13962306a36Sopenharmony_ci}
140