blob: fbffcce86bbffc511ad602e0d5c4aae700a54dcf [file] [log] [blame] [edit]
// 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);
}
size_t edgetpu_mappings_total_size(struct edgetpu_mapping_root *mappings)
{
struct rb_node *node;
size_t total = 0;
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);
total += map->map_size;
}
edgetpu_mapping_unlock(mappings);
return total;
}