summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/xe/xe_range_fence.c
blob: 372378e89e989239833879e23d2e62a2fd573b54 (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
155
156
157
158
159
160
161
// 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,
};