drm_gem.c revision 236927
1/*-
2 * Copyright (c) 2011 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Konstantin Belousov under sponsorship from
6 * the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: stable/9/sys/dev/drm2/drm_gem.c 235783 2012-05-22 11:07:44Z kib $");
32
33#include "opt_vm.h"
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/limits.h>
38#include <sys/lock.h>
39#include <sys/mutex.h>
40
41#include <vm/vm.h>
42#include <vm/vm_page.h>
43
44#include <dev/drm2/drmP.h>
45#include <dev/drm2/drm.h>
46#include <dev/drm2/drm_sarea.h>
47
48/*
49 * We make up offsets for buffer objects so we can recognize them at
50 * mmap time.
51 */
52
53/* pgoff in mmap is an unsigned long, so we need to make sure that
54 * the faked up offset will fit
55 */
56
57#if ULONG_MAX == UINT64_MAX
58#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1)
59#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16)
60#else
61#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1)
62#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16)
63#endif
64
65int
66drm_gem_init(struct drm_device *dev)
67{
68	struct drm_gem_mm *mm;
69
70	drm_gem_names_init(&dev->object_names);
71	mm = malloc(sizeof(*mm), DRM_MEM_DRIVER, M_WAITOK);
72	dev->mm_private = mm;
73	if (drm_ht_create(&mm->offset_hash, 19) != 0) {
74		free(mm, DRM_MEM_DRIVER);
75		return (ENOMEM);
76	}
77	mm->idxunr = new_unrhdr(0, DRM_GEM_MAX_IDX, NULL);
78	return (0);
79}
80
81void
82drm_gem_destroy(struct drm_device *dev)
83{
84	struct drm_gem_mm *mm;
85
86	mm = dev->mm_private;
87	dev->mm_private = NULL;
88	drm_ht_remove(&mm->offset_hash);
89	delete_unrhdr(mm->idxunr);
90	free(mm, DRM_MEM_DRIVER);
91	drm_gem_names_fini(&dev->object_names);
92}
93
94int
95drm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj,
96    size_t size)
97{
98
99	KASSERT((size & (PAGE_SIZE - 1)) == 0,
100	    ("Bad size %ju", (uintmax_t)size));
101
102	obj->dev = dev;
103	obj->vm_obj = vm_pager_allocate(OBJT_DEFAULT, NULL, size,
104	    VM_PROT_READ | VM_PROT_WRITE, 0, curthread->td_ucred);
105
106	obj->refcount = 1;
107	obj->handle_count = 0;
108	obj->size = size;
109
110	return (0);
111}
112
113int
114drm_gem_private_object_init(struct drm_device *dev, struct drm_gem_object *obj,
115    size_t size)
116{
117
118	MPASS((size & (PAGE_SIZE - 1)) == 0);
119
120	obj->dev = dev;
121	obj->vm_obj = NULL;
122
123	obj->refcount = 1;
124	atomic_set(&obj->handle_count, 0);
125	obj->size = size;
126
127	return (0);
128}
129
130
131struct drm_gem_object *
132drm_gem_object_alloc(struct drm_device *dev, size_t size)
133{
134	struct drm_gem_object *obj;
135
136	obj = malloc(sizeof(*obj), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
137	if (drm_gem_object_init(dev, obj, size) != 0)
138		goto free;
139
140	if (dev->driver->gem_init_object != NULL &&
141	    dev->driver->gem_init_object(obj) != 0)
142		goto dealloc;
143	return (obj);
144dealloc:
145	vm_object_deallocate(obj->vm_obj);
146free:
147	free(obj, DRM_MEM_DRIVER);
148	return (NULL);
149}
150
151void
152drm_gem_object_free(struct drm_gem_object *obj)
153{
154	struct drm_device *dev;
155
156	dev = obj->dev;
157	DRM_LOCK_ASSERT(dev);
158	if (dev->driver->gem_free_object != NULL)
159		dev->driver->gem_free_object(obj);
160}
161
162void
163drm_gem_object_reference(struct drm_gem_object *obj)
164{
165
166	KASSERT(obj->refcount > 0, ("Dandling obj %p", obj));
167	refcount_acquire(&obj->refcount);
168}
169
170void
171drm_gem_object_unreference(struct drm_gem_object *obj)
172{
173
174	if (obj == NULL)
175		return;
176	if (refcount_release(&obj->refcount))
177		drm_gem_object_free(obj);
178}
179
180void
181drm_gem_object_unreference_unlocked(struct drm_gem_object *obj)
182{
183	struct drm_device *dev;
184
185	if (obj == NULL)
186		return;
187	dev = obj->dev;
188	DRM_LOCK(dev);
189	drm_gem_object_unreference(obj);
190	DRM_UNLOCK(dev);
191}
192
193void
194drm_gem_object_handle_reference(struct drm_gem_object *obj)
195{
196
197	drm_gem_object_reference(obj);
198	atomic_add_rel_int(&obj->handle_count, 1);
199}
200
201void
202drm_gem_object_handle_free(struct drm_gem_object *obj)
203{
204	struct drm_device *dev;
205	struct drm_gem_object *obj1;
206
207	dev = obj->dev;
208	if (obj->name != 0) {
209		obj1 = drm_gem_names_remove(&dev->object_names, obj->name);
210		obj->name = 0;
211		drm_gem_object_unreference(obj1);
212	}
213}
214
215void
216drm_gem_object_handle_unreference(struct drm_gem_object *obj)
217{
218
219	if (obj == NULL ||
220	    atomic_load_acq_int(&obj->handle_count) == 0)
221		return;
222
223	if (atomic_fetchadd_int(&obj->handle_count, -1) == 1)
224		drm_gem_object_handle_free(obj);
225	drm_gem_object_unreference(obj);
226}
227
228void
229drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj)
230{
231
232	if (obj == NULL ||
233	    atomic_load_acq_int(&obj->handle_count) == 0)
234		return;
235
236	if (atomic_fetchadd_int(&obj->handle_count, -1) == 1)
237		drm_gem_object_handle_free(obj);
238	drm_gem_object_unreference_unlocked(obj);
239}
240
241int
242drm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj,
243    uint32_t *handle)
244{
245	int error;
246
247	error = drm_gem_name_create(&file_priv->object_names, obj, handle);
248	if (error != 0)
249		return (error);
250	drm_gem_object_handle_reference(obj);
251	return (0);
252}
253
254int
255drm_gem_handle_delete(struct drm_file *file_priv, uint32_t handle)
256{
257	struct drm_gem_object *obj;
258
259	obj = drm_gem_names_remove(&file_priv->object_names, handle);
260	if (obj == NULL)
261		return (EINVAL);
262	drm_gem_object_handle_unreference_unlocked(obj);
263	return (0);
264}
265
266void
267drm_gem_object_release(struct drm_gem_object *obj)
268{
269
270	/*
271	 * obj->vm_obj can be NULL for private gem objects.
272	 */
273	vm_object_deallocate(obj->vm_obj);
274}
275
276int
277drm_gem_open_ioctl(struct drm_device *dev, void *data,
278    struct drm_file *file_priv)
279{
280	struct drm_gem_open *args;
281	struct drm_gem_object *obj;
282	int ret;
283	uint32_t handle;
284
285	if (!drm_core_check_feature(dev, DRIVER_GEM))
286		return (ENODEV);
287	args = data;
288
289	obj = drm_gem_name_ref(&dev->object_names, args->name,
290	    (void (*)(void *))drm_gem_object_reference);
291	if (obj == NULL)
292		return (ENOENT);
293	handle = 0;
294	ret = drm_gem_handle_create(file_priv, obj, &handle);
295	drm_gem_object_unreference_unlocked(obj);
296	if (ret != 0)
297		return (ret);
298
299	args->handle = handle;
300	args->size = obj->size;
301
302	return (0);
303}
304
305void
306drm_gem_open(struct drm_device *dev, struct drm_file *file_priv)
307{
308
309	drm_gem_names_init(&file_priv->object_names);
310}
311
312static int
313drm_gem_object_release_handle(uint32_t name, void *ptr, void *arg)
314{
315	struct drm_gem_object *obj;
316
317	obj = ptr;
318	drm_gem_object_handle_unreference(obj);
319	return (0);
320}
321
322void
323drm_gem_release(struct drm_device *dev, struct drm_file *file_priv)
324{
325
326	drm_gem_names_foreach(&file_priv->object_names,
327	    drm_gem_object_release_handle, NULL);
328	drm_gem_names_fini(&file_priv->object_names);
329}
330
331int
332drm_gem_close_ioctl(struct drm_device *dev, void *data,
333    struct drm_file *file_priv)
334{
335	struct drm_gem_close *args;
336
337	if (!drm_core_check_feature(dev, DRIVER_GEM))
338		return (ENODEV);
339	args = data;
340
341	return (drm_gem_handle_delete(file_priv, args->handle));
342}
343
344int
345drm_gem_flink_ioctl(struct drm_device *dev, void *data,
346    struct drm_file *file_priv)
347{
348	struct drm_gem_flink *args;
349	struct drm_gem_object *obj;
350	int error;
351
352	if (!drm_core_check_feature(dev, DRIVER_GEM))
353		return (ENODEV);
354	args = data;
355
356	obj = drm_gem_name_ref(&file_priv->object_names, args->handle,
357	    (void (*)(void *))drm_gem_object_reference);
358	if (obj == NULL)
359		return (ENOENT);
360	error = drm_gem_name_create(&dev->object_names, obj, &obj->name);
361	if (error != 0) {
362		if (error == EALREADY)
363			error = 0;
364		drm_gem_object_unreference_unlocked(obj);
365	}
366	if (error == 0)
367		args->name = obj->name;
368	return (error);
369}
370
371struct drm_gem_object *
372drm_gem_object_lookup(struct drm_device *dev, struct drm_file *file_priv,
373    uint32_t handle)
374{
375	struct drm_gem_object *obj;
376
377	obj = drm_gem_name_ref(&file_priv->object_names, handle,
378	    (void (*)(void *))drm_gem_object_reference);
379	return (obj);
380}
381
382static struct drm_gem_object *
383drm_gem_object_from_offset(struct drm_device *dev, vm_ooffset_t offset)
384{
385	struct drm_gem_object *obj;
386	struct drm_gem_mm *mm;
387	struct drm_hash_item *map_list;
388
389	if ((offset & DRM_GEM_MAPPING_MASK) != DRM_GEM_MAPPING_KEY)
390		return (NULL);
391	offset &= ~DRM_GEM_MAPPING_KEY;
392	mm = dev->mm_private;
393	if (drm_ht_find_item(&mm->offset_hash, DRM_GEM_MAPPING_IDX(offset),
394	    &map_list) != 0) {
395	DRM_DEBUG("drm_gem_object_from_offset: offset 0x%jx obj not found\n",
396		    (uintmax_t)offset);
397		return (NULL);
398	}
399	obj = member2struct(drm_gem_object, map_list, map_list);
400	return (obj);
401}
402
403int
404drm_gem_create_mmap_offset(struct drm_gem_object *obj)
405{
406	struct drm_device *dev;
407	struct drm_gem_mm *mm;
408	int ret;
409
410	if (obj->on_map)
411		return (0);
412	dev = obj->dev;
413	mm = dev->mm_private;
414	ret = 0;
415
416	obj->map_list.key = alloc_unr(mm->idxunr);
417	ret = drm_ht_insert_item(&mm->offset_hash, &obj->map_list);
418	if (ret != 0) {
419		DRM_ERROR("failed to add to map hash\n");
420		free_unr(mm->idxunr, obj->map_list.key);
421		return (ret);
422	}
423	obj->on_map = true;
424	return (0);
425}
426
427void
428drm_gem_free_mmap_offset(struct drm_gem_object *obj)
429{
430	struct drm_hash_item *list;
431	struct drm_gem_mm *mm;
432
433	if (!obj->on_map)
434		return;
435	mm = obj->dev->mm_private;
436	list = &obj->map_list;
437
438	drm_ht_remove_item(&mm->offset_hash, list);
439	free_unr(mm->idxunr, list->key);
440	obj->on_map = false;
441}
442
443int
444drm_gem_mmap_single(struct cdev *kdev, vm_ooffset_t *offset, vm_size_t size,
445    struct vm_object **obj_res, int nprot)
446{
447	struct drm_device *dev;
448	struct drm_gem_object *gem_obj;
449	struct vm_object *vm_obj;
450
451	dev = drm_get_device_from_kdev(kdev);
452	if ((dev->driver->driver_features & DRIVER_GEM) == 0)
453		return (ENODEV);
454	DRM_LOCK(dev);
455	gem_obj = drm_gem_object_from_offset(dev, *offset);
456	if (gem_obj == NULL) {
457		DRM_UNLOCK(dev);
458		return (ENODEV);
459	}
460	drm_gem_object_reference(gem_obj);
461	DRM_UNLOCK(dev);
462	vm_obj = cdev_pager_allocate(gem_obj, OBJT_MGTDEVICE,
463	    dev->driver->gem_pager_ops, size, nprot,
464	    DRM_GEM_MAPPING_MAPOFF(*offset), curthread->td_ucred);
465	if (vm_obj == NULL) {
466		drm_gem_object_unreference_unlocked(gem_obj);
467		return (EINVAL);
468	}
469	*offset = DRM_GEM_MAPPING_MAPOFF(*offset);
470	*obj_res = vm_obj;
471	return (0);
472}
473
474void
475drm_gem_pager_dtr(void *handle)
476{
477	struct drm_gem_object *obj;
478	struct drm_device *dev;
479
480	obj = handle;
481	dev = obj->dev;
482
483	DRM_LOCK(dev);
484	drm_gem_free_mmap_offset(obj);
485	drm_gem_object_unreference(obj);
486	DRM_UNLOCK(dev);
487}
488