summaryrefslogtreecommitdiff
path: root/drivers/edgetpu/edgetpu-mapping.c
blob: 136cf2946ec89fcc34d7c12f90608437e1cfcce6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// SPDX-License-Identifier: GPL-2.0
/*
 * Records and maintains the mapped TPU IOVA in a device group.
 *
 * Copyright (C) 2019 Google, Inc.
 */

#include <linux/mutex.h>
#include <linux/rbtree.h>
#include <linux/scatterlist.h>
#include <linux/seq_file.h>

#include "edgetpu-internal.h"
#include "edgetpu-mapping.h"

/*
 * Compare pairs (@map->die_index, @map->device_address) with
 * (@die_index, @iova).
 *
 * Returns -1, 0, 1 if @map is "less than", "equal to", or "larger than"
 * (@die_index, @iova), respectively.
 */
static int compare(struct edgetpu_mapping *map, u32 die_index, tpu_addr_t iova)
{
	if (map->die_index != die_index) {
		if (map->die_index < die_index)
			return -1;
		else
			return 1;
	}
	if (map->device_address != iova) {
		if (map->device_address < iova)
			return -1;
		else
			return 1;
	}
	return 0;
}

void edgetpu_mapping_init(struct edgetpu_mapping_root *mappings)
{
	mappings->rb = RB_ROOT;
	mappings->count = 0;
	mutex_init(&mappings->lock);
}

int edgetpu_mapping_add(struct edgetpu_mapping_root *mappings,
			struct edgetpu_mapping *map)
{
	struct rb_node **new;
	struct rb_node *parent = NULL;
	int ret = -EBUSY;

	if (!map->release)
		return -EINVAL;
	edgetpu_mapping_lock(mappings);
	new = &mappings->rb.rb_node;

	while (*new) {
		struct edgetpu_mapping *this =
			container_of(*new, struct edgetpu_mapping, node);
		const int cmp =
			compare(this, map->die_index, map->device_address);

		parent = *new;
		if (cmp > 0)
			new = &((*new)->rb_left);
		else if (cmp < 0)
			new = &((*new)->rb_right);
		else
			goto out;
	}

	rb_link_node(&map->node, parent, new);
	rb_insert_color(&map->node, &mappings->rb);
	mappings->count++;
	ret = 0;

out:
	edgetpu_mapping_unlock(mappings);
	return ret;
}

struct edgetpu_mapping *
edgetpu_mapping_find_locked(struct edgetpu_mapping_root *mappings,
			    u32 die_index, tpu_addr_t iova)
{
	struct rb_node *node = mappings->rb.rb_node;

	while (node) {
		struct edgetpu_mapping *map =
			container_of(node, struct edgetpu_mapping, node);
		const int cmp = compare(map, die_index, iova);

		if (cmp > 0)
			node = node->rb_left;
		else if (cmp < 0)
			node = node->rb_right;
		else
			return map;
	}

	return NULL;
}

void edgetpu_mapping_unlink(struct edgetpu_mapping_root *mappings,
			    struct edgetpu_mapping *map)
{
	rb_erase(&map->node, &mappings->rb);
	mappings->count--;
}

struct edgetpu_mapping *
edgetpu_mapping_first_locked(struct edgetpu_mapping_root *mappings)
{
	struct rb_node *node = rb_first(&mappings->rb);

	if (!node)
		return NULL;
	return container_of(node, struct edgetpu_mapping, node);
}

void edgetpu_mapping_clear(struct edgetpu_mapping_root *mappings)
{
	struct edgetpu_mapping *map;

	edgetpu_mapping_lock(mappings);

	for (map = edgetpu_mapping_first_locked(mappings); map;
	     map = edgetpu_mapping_first_locked(mappings)) {
		edgetpu_mapping_unlink(mappings, map);
		map->release(map);
	}

	edgetpu_mapping_unlock(mappings);
}

void edgetpu_mappings_show(struct edgetpu_mapping_root *mappings,
			   struct seq_file *s)
{
	struct rb_node *node;

	edgetpu_mapping_lock(mappings);

	for (node = rb_first(&mappings->rb); node; node = rb_next(node)) {
		struct edgetpu_mapping *map =
			container_of(node, struct edgetpu_mapping, node);

		if (map->show)
			map->show(map, s);
	}

	edgetpu_mapping_unlock(mappings);
}