]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/dev/drm2/drm_gem.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / dev / drm2 / drm_gem.c
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$");
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 BITS_PER_LONG == 64
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
65 /**
66  * Initialize the GEM device fields
67  */
68
69 int
70 drm_gem_init(struct drm_device *dev)
71 {
72         struct drm_gem_mm *mm;
73
74         drm_gem_names_init(&dev->object_names);
75
76         mm = malloc(sizeof(*mm), DRM_MEM_DRIVER, M_NOWAIT);
77         if (!mm) {
78                 DRM_ERROR("out of memory\n");
79                 return -ENOMEM;
80         }
81
82         dev->mm_private = mm;
83
84         if (drm_ht_create(&mm->offset_hash, 19)) {
85                 free(mm, DRM_MEM_DRIVER);
86                 return -ENOMEM;
87         }
88
89         mm->idxunr = new_unrhdr(0, DRM_GEM_MAX_IDX, NULL);
90
91         return 0;
92 }
93
94 void
95 drm_gem_destroy(struct drm_device *dev)
96 {
97         struct drm_gem_mm *mm = dev->mm_private;
98
99         dev->mm_private = NULL;
100         drm_ht_remove(&mm->offset_hash);
101         delete_unrhdr(mm->idxunr);
102         free(mm, DRM_MEM_DRIVER);
103         drm_gem_names_fini(&dev->object_names);
104 }
105
106 int drm_gem_object_init(struct drm_device *dev,
107                         struct drm_gem_object *obj, size_t size)
108 {
109         KASSERT((size & (PAGE_SIZE - 1)) == 0,
110             ("Bad size %ju", (uintmax_t)size));
111
112         obj->dev = dev;
113         obj->vm_obj = vm_pager_allocate(OBJT_DEFAULT, NULL, size,
114             VM_PROT_READ | VM_PROT_WRITE, 0, curthread->td_ucred);
115
116         obj->refcount = 1;
117         obj->handle_count = 0;
118         obj->size = size;
119
120         return 0;
121 }
122 EXPORT_SYMBOL(drm_gem_object_init);
123
124 /**
125  * Initialize an already allocated GEM object of the specified size with
126  * no GEM provided backing store. Instead the caller is responsible for
127  * backing the object and handling it.
128  */
129 int drm_gem_private_object_init(struct drm_device *dev,
130                         struct drm_gem_object *obj, size_t size)
131 {
132         MPASS((size & (PAGE_SIZE - 1)) == 0);
133
134         obj->dev = dev;
135         obj->vm_obj = NULL;
136
137         obj->refcount = 1;
138         atomic_store_rel_int(&obj->handle_count, 0);
139         obj->size = size;
140
141         return 0;
142 }
143 EXPORT_SYMBOL(drm_gem_private_object_init);
144
145 struct drm_gem_object *
146 drm_gem_object_alloc(struct drm_device *dev, size_t size)
147 {
148         struct drm_gem_object *obj;
149
150         obj = malloc(sizeof(*obj), DRM_MEM_DRIVER, M_NOWAIT | M_ZERO);
151         if (!obj)
152                 goto free;
153
154         if (drm_gem_object_init(dev, obj, size) != 0)
155                 goto free;
156
157         if (dev->driver->gem_init_object != NULL &&
158             dev->driver->gem_init_object(obj) != 0) {
159                 goto dealloc;
160         }
161         return obj;
162 dealloc:
163         vm_object_deallocate(obj->vm_obj);
164 free:
165         free(obj, DRM_MEM_DRIVER);
166         return NULL;
167 }
168 EXPORT_SYMBOL(drm_gem_object_alloc);
169
170 #if defined(FREEBSD_NOTYET)
171 static void
172 drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp)
173 {
174         if (obj->import_attach) {
175                 drm_prime_remove_buf_handle(&filp->prime,
176                                 obj->import_attach->dmabuf);
177         }
178         if (obj->export_dma_buf) {
179                 drm_prime_remove_buf_handle(&filp->prime,
180                                 obj->export_dma_buf);
181         }
182 }
183 #endif
184
185 /**
186  * Removes the mapping from handle to filp for this object.
187  */
188 int
189 drm_gem_handle_delete(struct drm_file *filp, u32 handle)
190 {
191         struct drm_device *dev;
192         struct drm_gem_object *obj;
193
194         obj = drm_gem_names_remove(&filp->object_names, handle);
195         if (obj == NULL) {
196                 return -EINVAL;
197         }
198         dev = obj->dev;
199
200 #if defined(FREEBSD_NOTYET)
201         drm_gem_remove_prime_handles(obj, filp);
202 #endif
203
204         if (dev->driver->gem_close_object)
205                 dev->driver->gem_close_object(obj, filp);
206         drm_gem_object_handle_unreference_unlocked(obj);
207
208         return 0;
209 }
210 EXPORT_SYMBOL(drm_gem_handle_delete);
211
212 /**
213  * Create a handle for this object. This adds a handle reference
214  * to the object, which includes a regular reference count. Callers
215  * will likely want to dereference the object afterwards.
216  */
217 int
218 drm_gem_handle_create(struct drm_file *file_priv,
219                        struct drm_gem_object *obj,
220                        u32 *handlep)
221 {
222         struct drm_device *dev = obj->dev;
223         int ret;
224
225         *handlep = 0;
226         ret = drm_gem_name_create(&file_priv->object_names, obj, handlep);
227         if (ret != 0)
228                 return ret;
229
230         drm_gem_object_handle_reference(obj);
231
232         if (dev->driver->gem_open_object) {
233                 ret = dev->driver->gem_open_object(obj, file_priv);
234                 if (ret) {
235                         drm_gem_handle_delete(file_priv, *handlep);
236                         return ret;
237                 }
238         }
239
240         return 0;
241 }
242 EXPORT_SYMBOL(drm_gem_handle_create);
243
244 void
245 drm_gem_free_mmap_offset(struct drm_gem_object *obj)
246 {
247         struct drm_device *dev = obj->dev;
248         struct drm_gem_mm *mm = dev->mm_private;
249         struct drm_hash_item *list = &obj->map_list;
250
251         if (!obj->on_map)
252                 return;
253
254         drm_ht_remove_item(&mm->offset_hash, list);
255         free_unr(mm->idxunr, list->key);
256         obj->on_map = false;
257 }
258 EXPORT_SYMBOL(drm_gem_free_mmap_offset);
259
260 int
261 drm_gem_create_mmap_offset(struct drm_gem_object *obj)
262 {
263         struct drm_device *dev = obj->dev;
264         struct drm_gem_mm *mm = dev->mm_private;
265         int ret;
266
267         if (obj->on_map)
268                 return 0;
269
270         obj->map_list.key = alloc_unr(mm->idxunr);
271         ret = drm_ht_insert_item(&mm->offset_hash, &obj->map_list);
272         if (ret) {
273                 DRM_ERROR("failed to add to map hash\n");
274                 free_unr(mm->idxunr, obj->map_list.key);
275                 return ret;
276         }
277         obj->on_map = true;
278
279         return 0;
280 }
281 EXPORT_SYMBOL(drm_gem_create_mmap_offset);
282
283 /** Returns a reference to the object named by the handle. */
284 struct drm_gem_object *
285 drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp,
286                       u32 handle)
287 {
288         struct drm_gem_object *obj;
289
290         obj = drm_gem_name_ref(&filp->object_names, handle,
291             (void (*)(void *))drm_gem_object_reference);
292
293         return obj;
294 }
295 EXPORT_SYMBOL(drm_gem_object_lookup);
296
297 int
298 drm_gem_close_ioctl(struct drm_device *dev, void *data,
299                     struct drm_file *file_priv)
300 {
301         struct drm_gem_close *args = data;
302         int ret;
303
304         if (!(dev->driver->driver_features & DRIVER_GEM))
305                 return -ENODEV;
306
307         ret = drm_gem_handle_delete(file_priv, args->handle);
308
309         return ret;
310 }
311
312 int
313 drm_gem_flink_ioctl(struct drm_device *dev, void *data,
314                     struct drm_file *file_priv)
315 {
316         struct drm_gem_flink *args = data;
317         struct drm_gem_object *obj;
318         int ret;
319
320         if (!(dev->driver->driver_features & DRIVER_GEM))
321                 return -ENODEV;
322
323         obj = drm_gem_object_lookup(dev, file_priv, args->handle);
324         if (obj == NULL)
325                 return -ENOENT;
326
327         ret = drm_gem_name_create(&dev->object_names, obj, &obj->name);
328         if (ret != 0) {
329                 if (ret == -EALREADY)
330                         ret = 0;
331                 drm_gem_object_unreference_unlocked(obj);
332         }
333         if (ret == 0)
334                 args->name = obj->name;
335         return ret;
336 }
337
338 int
339 drm_gem_open_ioctl(struct drm_device *dev, void *data,
340                    struct drm_file *file_priv)
341 {
342         struct drm_gem_open *args = data;
343         struct drm_gem_object *obj;
344         int ret;
345         u32 handle;
346
347         if (!(dev->driver->driver_features & DRIVER_GEM))
348                 return -ENODEV;
349
350         obj = drm_gem_name_ref(&dev->object_names, args->name,
351             (void (*)(void *))drm_gem_object_reference);
352         if (!obj)
353                 return -ENOENT;
354
355         ret = drm_gem_handle_create(file_priv, obj, &handle);
356         drm_gem_object_unreference_unlocked(obj);
357         if (ret)
358                 return ret;
359
360         args->handle = handle;
361         args->size = obj->size;
362
363         return 0;
364 }
365
366 void
367 drm_gem_open(struct drm_device *dev, struct drm_file *file_private)
368 {
369
370         drm_gem_names_init(&file_private->object_names);
371 }
372
373 static int
374 drm_gem_object_release_handle(uint32_t name, void *ptr, void *data)
375 {
376         struct drm_file *file_priv = data;
377         struct drm_gem_object *obj = ptr;
378         struct drm_device *dev = obj->dev;
379
380 #if defined(FREEBSD_NOTYET)
381         drm_gem_remove_prime_handles(obj, file_priv);
382 #endif
383
384         if (dev->driver->gem_close_object)
385                 dev->driver->gem_close_object(obj, file_priv);
386
387         drm_gem_object_handle_unreference_unlocked(obj);
388
389         return 0;
390 }
391
392 void
393 drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
394 {
395         drm_gem_names_foreach(&file_private->object_names,
396             drm_gem_object_release_handle, file_private);
397
398         drm_gem_names_fini(&file_private->object_names);
399 }
400
401 void
402 drm_gem_object_release(struct drm_gem_object *obj)
403 {
404
405         /*
406          * obj->vm_obj can be NULL for private gem objects.
407          */
408         vm_object_deallocate(obj->vm_obj);
409 }
410 EXPORT_SYMBOL(drm_gem_object_release);
411
412 void
413 drm_gem_object_free(struct drm_gem_object *obj)
414 {
415         struct drm_device *dev = obj->dev;
416
417         DRM_LOCK_ASSERT(dev);
418         if (dev->driver->gem_free_object != NULL)
419                 dev->driver->gem_free_object(obj);
420 }
421 EXPORT_SYMBOL(drm_gem_object_free);
422
423 void drm_gem_object_handle_free(struct drm_gem_object *obj)
424 {
425         struct drm_device *dev = obj->dev;
426         struct drm_gem_object *obj1;
427
428         if (obj->name) {
429                 obj1 = drm_gem_names_remove(&dev->object_names, obj->name);
430                 obj->name = 0;
431                 drm_gem_object_unreference(obj1);
432         }
433 }
434
435 static struct drm_gem_object *
436 drm_gem_object_from_offset(struct drm_device *dev, vm_ooffset_t offset)
437 {
438         struct drm_gem_object *obj;
439         struct drm_gem_mm *mm;
440         struct drm_hash_item *map_list;
441
442         if ((offset & DRM_GEM_MAPPING_MASK) != DRM_GEM_MAPPING_KEY)
443                 return (NULL);
444         offset &= ~DRM_GEM_MAPPING_KEY;
445         mm = dev->mm_private;
446         if (drm_ht_find_item(&mm->offset_hash, DRM_GEM_MAPPING_IDX(offset),
447             &map_list) != 0) {
448         DRM_DEBUG("drm_gem_object_from_offset: offset 0x%jx obj not found\n",
449                     (uintmax_t)offset);
450                 return (NULL);
451         }
452         obj = __containerof(map_list, struct drm_gem_object, map_list);
453         return (obj);
454 }
455
456 int
457 drm_gem_mmap_single(struct drm_device *dev, vm_ooffset_t *offset, vm_size_t size,
458     struct vm_object **obj_res, int nprot)
459 {
460         struct drm_gem_object *gem_obj;
461         struct vm_object *vm_obj;
462
463         DRM_LOCK(dev);
464         gem_obj = drm_gem_object_from_offset(dev, *offset);
465         if (gem_obj == NULL) {
466                 DRM_UNLOCK(dev);
467                 return (-ENODEV);
468         }
469         drm_gem_object_reference(gem_obj);
470         DRM_UNLOCK(dev);
471         vm_obj = cdev_pager_allocate(gem_obj, OBJT_MGTDEVICE,
472             dev->driver->gem_pager_ops, size, nprot,
473             DRM_GEM_MAPPING_MAPOFF(*offset), curthread->td_ucred);
474         if (vm_obj == NULL) {
475                 drm_gem_object_unreference_unlocked(gem_obj);
476                 return (-EINVAL);
477         }
478         *offset = DRM_GEM_MAPPING_MAPOFF(*offset);
479         *obj_res = vm_obj;
480         return (0);
481 }
482
483 void
484 drm_gem_pager_dtr(void *handle)
485 {
486         struct drm_gem_object *obj;
487         struct drm_device *dev;
488
489         obj = handle;
490         dev = obj->dev;
491
492         DRM_LOCK(dev);
493         drm_gem_free_mmap_offset(obj);
494         drm_gem_object_unreference(obj);
495         DRM_UNLOCK(dev);
496 }