162 lines
4.0 KiB
C
162 lines
4.0 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright © 2023 Intel Corporation
|
|
*/
|
|
|
|
#include <linux/dma-fence.h>
|
|
#include <linux/interval_tree_generic.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "xe_macros.h"
|
|
#include "xe_range_fence.h"
|
|
|
|
#define XE_RANGE_TREE_START(_node) ((_node)->start)
|
|
#define XE_RANGE_TREE_LAST(_node) ((_node)->last)
|
|
|
|
INTERVAL_TREE_DEFINE(struct xe_range_fence, rb, u64, __subtree_last,
|
|
XE_RANGE_TREE_START, XE_RANGE_TREE_LAST, static,
|
|
xe_range_fence_tree);
|
|
|
|
static void
|
|
xe_range_fence_signal_notify(struct dma_fence *fence, struct dma_fence_cb *cb)
|
|
{
|
|
struct xe_range_fence *rfence = container_of(cb, typeof(*rfence), cb);
|
|
struct xe_range_fence_tree *tree = rfence->tree;
|
|
|
|
llist_add(&rfence->link, &tree->list);
|
|
}
|
|
|
|
static bool __xe_range_fence_tree_cleanup(struct xe_range_fence_tree *tree)
|
|
{
|
|
struct llist_node *node = llist_del_all(&tree->list);
|
|
struct xe_range_fence *rfence, *next;
|
|
|
|
llist_for_each_entry_safe(rfence, next, node, link) {
|
|
xe_range_fence_tree_remove(rfence, &tree->root);
|
|
dma_fence_put(rfence->fence);
|
|
kfree(rfence);
|
|
}
|
|
|
|
return !!node;
|
|
}
|
|
|
|
/**
|
|
* xe_range_fence_insert() - range fence insert
|
|
* @tree: range fence tree to insert intoi
|
|
* @rfence: range fence
|
|
* @ops: range fence ops
|
|
* @start: start address of range fence
|
|
* @last: last address of range fence
|
|
* @fence: dma fence which signals range fence can be removed + freed
|
|
*
|
|
* Return: 0 on success, non-zero on failure
|
|
*/
|
|
int xe_range_fence_insert(struct xe_range_fence_tree *tree,
|
|
struct xe_range_fence *rfence,
|
|
const struct xe_range_fence_ops *ops,
|
|
u64 start, u64 last, struct dma_fence *fence)
|
|
{
|
|
int err = 0;
|
|
|
|
__xe_range_fence_tree_cleanup(tree);
|
|
|
|
if (dma_fence_is_signaled(fence))
|
|
goto free;
|
|
|
|
rfence->ops = ops;
|
|
rfence->start = start;
|
|
rfence->last = last;
|
|
rfence->tree = tree;
|
|
rfence->fence = dma_fence_get(fence);
|
|
err = dma_fence_add_callback(fence, &rfence->cb,
|
|
xe_range_fence_signal_notify);
|
|
if (err == -ENOENT) {
|
|
dma_fence_put(fence);
|
|
err = 0;
|
|
goto free;
|
|
} else if (err == 0) {
|
|
xe_range_fence_tree_insert(rfence, &tree->root);
|
|
return 0;
|
|
}
|
|
|
|
free:
|
|
if (ops->free)
|
|
ops->free(rfence);
|
|
|
|
return err;
|
|
}
|
|
|
|
static void xe_range_fence_tree_remove_all(struct xe_range_fence_tree *tree)
|
|
{
|
|
struct xe_range_fence *rfence;
|
|
bool retry = true;
|
|
|
|
rfence = xe_range_fence_tree_iter_first(&tree->root, 0, U64_MAX);
|
|
while (rfence) {
|
|
/* Should be ok with the minimalistic callback */
|
|
if (dma_fence_remove_callback(rfence->fence, &rfence->cb))
|
|
llist_add(&rfence->link, &tree->list);
|
|
rfence = xe_range_fence_tree_iter_next(rfence, 0, U64_MAX);
|
|
}
|
|
|
|
while (retry)
|
|
retry = __xe_range_fence_tree_cleanup(tree);
|
|
}
|
|
|
|
/**
|
|
* xe_range_fence_tree_init() - Init range fence tree
|
|
* @tree: range fence tree
|
|
*/
|
|
void xe_range_fence_tree_init(struct xe_range_fence_tree *tree)
|
|
{
|
|
memset(tree, 0, sizeof(*tree));
|
|
}
|
|
|
|
/**
|
|
* xe_range_fence_tree_fini() - Fini range fence tree
|
|
* @tree: range fence tree
|
|
*/
|
|
void xe_range_fence_tree_fini(struct xe_range_fence_tree *tree)
|
|
{
|
|
xe_range_fence_tree_remove_all(tree);
|
|
XE_WARN_ON(!RB_EMPTY_ROOT(&tree->root.rb_root));
|
|
}
|
|
|
|
/**
|
|
* xe_range_fence_tree_first() - range fence tree iterator first
|
|
* @tree: range fence tree
|
|
* @start: start address of range fence
|
|
* @last: last address of range fence
|
|
*
|
|
* Return: first range fence found in range or NULL
|
|
*/
|
|
struct xe_range_fence *
|
|
xe_range_fence_tree_first(struct xe_range_fence_tree *tree, u64 start,
|
|
u64 last)
|
|
{
|
|
return xe_range_fence_tree_iter_first(&tree->root, start, last);
|
|
}
|
|
|
|
/**
|
|
* xe_range_fence_tree_next() - range fence tree iterator next
|
|
* @rfence: current range fence
|
|
* @start: start address of range fence
|
|
* @last: last address of range fence
|
|
*
|
|
* Return: next range fence found in range or NULL
|
|
*/
|
|
struct xe_range_fence *
|
|
xe_range_fence_tree_next(struct xe_range_fence *rfence, u64 start, u64 last)
|
|
{
|
|
return xe_range_fence_tree_iter_next(rfence, start, last);
|
|
}
|
|
|
|
static void xe_range_fence_free(struct xe_range_fence *rfence)
|
|
{
|
|
kfree(rfence);
|
|
}
|
|
|
|
const struct xe_range_fence_ops xe_range_fence_kfree_ops = {
|
|
.free = xe_range_fence_free,
|
|
};
|