drm_vma_manager.c revision 1.3
1/*	$NetBSD: drm_vma_manager.c,v 1.3 2015/06/19 22:51:57 chs Exp $	*/
2
3/*-
4 * Copyright (c) 2014 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: drm_vma_manager.c,v 1.3 2015/06/19 22:51:57 chs Exp $");
34
35#include <sys/kmem.h>
36#include <sys/rbtree.h>
37#include <sys/vmem.h>
38
39#include <drm/drm_vma_manager.h>
40
41static int
42drm_vma_node_compare(void *cookie __unused, const void *va, const void *vb)
43{
44	const struct drm_vma_offset_node *const na = va;
45	const struct drm_vma_offset_node *const nb = vb;
46
47	if (na->von_startpage < nb->von_startpage)
48		return -1;
49	if (na->von_startpage > nb->von_startpage)
50		return +1;
51	return 0;
52}
53
54static int
55drm_vma_node_compare_key(void *cookie __unused, const void *vn, const void *vk)
56{
57	const struct drm_vma_offset_node *const n = vn;
58	const vmem_addr_t *const k = vk;
59
60	if (n->von_startpage < *k)
61		return -1;
62	if (n->von_startpage > *k)
63		return +1;
64	return 0;
65}
66
67static const rb_tree_ops_t drm_vma_node_rb_ops = {
68	.rbto_compare_nodes = &drm_vma_node_compare,
69	.rbto_compare_key = &drm_vma_node_compare_key,
70	.rbto_node_offset = offsetof(struct drm_vma_offset_node, von_rb_node),
71	.rbto_context = NULL,
72};
73
74static int
75drm_vma_file_compare(void *cookie __unused, const void *va, const void *vb)
76{
77	const struct drm_vma_offset_file *const fa = va;
78	const struct drm_vma_offset_file *const fb = vb;
79
80	if (fa->vof_file < fb->vof_file)
81		return -1;
82	if (fa->vof_file > fb->vof_file)
83		return +1;
84	return 0;
85}
86
87static int
88drm_vma_file_compare_key(void *cookie __unused, const void *vf, const void *vk)
89{
90	const struct drm_vma_offset_file *const f = vf;
91	const struct file *const k = vk;
92
93	if (f->vof_file < k)
94		return -1;
95	if (f->vof_file > k)
96		return +1;
97	return 0;
98}
99
100static const rb_tree_ops_t drm_vma_file_rb_ops = {
101	.rbto_compare_nodes = &drm_vma_file_compare,
102	.rbto_compare_key = &drm_vma_file_compare_key,
103	.rbto_node_offset = offsetof(struct drm_vma_offset_file, vof_rb_node),
104	.rbto_context = NULL,
105};
106
107void
108drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
109    unsigned long startpage, unsigned long npages)
110{
111
112	rw_init(&mgr->vom_lock);
113	rb_tree_init(&mgr->vom_nodes, &drm_vma_node_rb_ops);
114	mgr->vom_vmem = vmem_create("drm_vma", startpage, npages, 1,
115	    NULL, NULL, NULL, 0, VM_SLEEP, IPL_NONE);
116}
117
118void
119drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr)
120{
121
122	vmem_destroy(mgr->vom_vmem);
123#if 0
124	rb_tree_destroy(&mgr->vom_nodes);
125#endif
126	rw_destroy(&mgr->vom_lock);
127}
128
129void
130drm_vma_node_init(struct drm_vma_offset_node *node)
131{
132	static const struct drm_vma_offset_node zero_node;
133
134	*node = zero_node;
135
136	rw_init(&node->von_lock);
137	node->von_startpage = 0;
138	node->von_npages = 0;
139	rb_tree_init(&node->von_files, &drm_vma_file_rb_ops);
140}
141
142void
143drm_vma_node_destroy(struct drm_vma_offset_node *node)
144{
145
146#if 0
147	rb_tree_destroy(&node->von_files);
148#endif
149	KASSERT(node->von_startpage == 0);
150	KASSERT(node->von_npages == 0);
151	rw_destroy(&node->von_lock);
152}
153
154int
155drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
156    struct drm_vma_offset_node *node, unsigned long npages)
157{
158	vmem_size_t startpage;
159	struct drm_vma_offset_node *collision __diagused;
160	int error;
161
162	KASSERT(npages != 0);
163
164	if (0 < node->von_npages)
165		return 0;
166
167	error = vmem_alloc(mgr->vom_vmem, npages, VM_NOSLEEP|VM_BESTFIT,
168	    &startpage);
169	if (error) {
170		if (error == ENOMEM)
171			error = ENOSPC;
172		/* XXX errno NetBSD->Linux */
173		return -error;
174	}
175
176	node->von_startpage = startpage;
177	node->von_npages = npages;
178
179	rw_enter(&node->von_lock, RW_WRITER);
180	collision = rb_tree_insert_node(&mgr->vom_nodes, node);
181	KASSERT(collision == node);
182	rw_exit(&node->von_lock);
183
184	return 0;
185}
186
187void
188drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
189    struct drm_vma_offset_node *node)
190{
191
192	if (node->von_npages == 0)
193		return;
194
195	rw_enter(&node->von_lock, RW_WRITER);
196	rb_tree_remove_node(&mgr->vom_nodes, node);
197	rw_exit(&node->von_lock);
198
199	vmem_free(mgr->vom_vmem, node->von_startpage, node->von_npages);
200
201	node->von_npages = 0;
202	node->von_startpage = 0;
203}
204
205void
206drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr)
207{
208
209	rw_enter(&mgr->vom_lock, RW_READER);
210}
211
212void
213drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr)
214{
215
216	rw_exit(&mgr->vom_lock);
217}
218
219struct drm_vma_offset_node *
220drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr,
221    unsigned long startpage, unsigned long npages)
222{
223	const vmem_addr_t key = startpage;
224	struct drm_vma_offset_node *node;
225
226	KASSERT(rw_lock_held(&mgr->vom_lock));
227
228	node = rb_tree_find_node_leq(&mgr->vom_nodes, &key);
229	if (node == NULL)
230		return NULL;
231	KASSERT(node->von_startpage <= startpage);
232	if (npages < node->von_npages)
233		return NULL;
234	if (node->von_npages - npages < startpage - node->von_startpage)
235		return NULL;
236
237	return node;
238}
239
240struct drm_vma_offset_node *
241drm_vma_offset_exact_lookup(struct drm_vma_offset_manager *mgr,
242    unsigned long startpage, unsigned long npages)
243{
244	const vmem_addr_t key = startpage;
245	struct drm_vma_offset_node *node;
246
247	rw_enter(&mgr->vom_lock, RW_READER);
248
249	node = rb_tree_find_node(&mgr->vom_nodes, &key);
250	if (node == NULL)
251		goto out;
252	KASSERT(node->von_startpage == startpage);
253	if (node->von_npages != npages) {
254		node = NULL;
255		goto out;
256	}
257
258out:	rw_exit(&mgr->vom_lock);
259	return node;
260}
261
262int
263drm_vma_node_allow(struct drm_vma_offset_node *node, struct file *file)
264{
265	struct drm_vma_offset_file *new, *old;
266
267	new = kmem_alloc(sizeof(*new), KM_NOSLEEP);
268	if (new == NULL)
269		return -ENOMEM;
270	new->vof_file = file;
271
272	rw_enter(&node->von_lock, RW_WRITER);
273	old = rb_tree_insert_node(&node->von_files, new);
274	rw_exit(&node->von_lock);
275
276	if (old != new)		/* collision */
277		kmem_free(new, sizeof(*new));
278
279	return 0;
280}
281
282void
283drm_vma_node_revoke(struct drm_vma_offset_node *node, struct file *file)
284{
285
286	rw_enter(&node->von_lock, RW_WRITER);
287	struct drm_vma_offset_file *const found =
288	    rb_tree_find_node(&node->von_files, file);
289	if (found != NULL)
290		rb_tree_remove_node(&node->von_files, found);
291	rw_exit(&node->von_lock);
292	if (found != NULL)
293		kmem_free(found, sizeof(*found));
294}
295
296bool
297drm_vma_node_is_allowed(struct drm_vma_offset_node *node, struct file *file)
298{
299
300	rw_enter(&node->von_lock, RW_READER);
301	const bool allowed =
302	    (rb_tree_find_node(&node->von_files, file) != NULL);
303	rw_exit(&node->von_lock);
304
305	return allowed;
306}
307
308int
309drm_vma_node_verify_access(struct drm_vma_offset_node *node, struct file *file)
310{
311
312	if (!drm_vma_node_is_allowed(node, file))
313		return -EACCES;
314
315	return 0;
316}
317