7fabfda3eace1c630af3b6471f1a0de62ecfe2cb
[dragonfly.git] / sys / dev / drm / 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  * $FreeBSD: head/sys/dev/drm2/drm_gem.c 247835 2013-03-05 09:49:34Z kib $"
30  */
31
32 #include "opt_vm.h"
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/limits.h>
37 #include <sys/lock.h>
38 #include <sys/mutex.h>
39 #include <sys/conf.h>
40
41 #include <vm/vm.h>
42 #include <vm/vm_page.h>
43
44 #include <drm/drmP.h>
45
46 /*
47  * We make up offsets for buffer objects so we can recognize them at
48  * mmap time.
49  */
50
51 /* pgoff in mmap is an unsigned long, so we need to make sure that
52  * the faked up offset will fit
53  */
54
55 #if ULONG_MAX == UINT64_MAX
56 #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1)
57 #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16)
58 #else
59 #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1)
60 #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16)
61 #endif
62
63 /**
64  * Initialize the GEM device fields
65  */
66
67 int
68 drm_gem_init(struct drm_device *dev)
69 {
70         struct drm_gem_mm *mm;
71
72         spin_init(&dev->object_name_lock);
73         idr_init(&dev->object_name_idr);
74
75         mm = kmalloc(sizeof(*mm), DRM_MEM_DRIVER, M_WAITOK);
76         if (!mm) {
77                 DRM_ERROR("out of memory\n");
78                 return -ENOMEM;
79         }
80
81         dev->mm_private = mm;
82
83         if (drm_ht_create(&mm->offset_hash, 12)) {
84                 drm_free(mm, DRM_MEM_DRIVER);
85                 return -ENOMEM;
86         }
87
88         mm->idxunr = new_unrhdr(0, DRM_GEM_MAX_IDX, NULL);
89         return 0;
90 }
91
92 void
93 drm_gem_destroy(struct drm_device *dev)
94 {
95         struct drm_gem_mm *mm = dev->mm_private;
96
97         drm_ht_remove(&mm->offset_hash);
98         delete_unrhdr(mm->idxunr);
99         drm_free(mm, DRM_MEM_DRIVER);
100         dev->mm_private = NULL;
101 }
102
103 /**
104  * Initialize an already allocated GEM object of the specified size with
105  * shmfs backing store.
106  */
107 int drm_gem_object_init(struct drm_device *dev,
108                         struct drm_gem_object *obj, size_t size)
109 {
110
111         KASSERT((size & (PAGE_SIZE - 1)) == 0,
112             ("Bad size %ju", (uintmax_t)size));
113
114         obj->dev = dev;
115         obj->vm_obj = default_pager_alloc(NULL, size,
116             VM_PROT_READ | VM_PROT_WRITE, 0);
117
118         kref_init(&obj->refcount);
119         atomic_set(&obj->handle_count, 0);
120         obj->size = size;
121
122         return (0);
123 }
124
125 /**
126  * Initialize an already allocated GEM object of the specified size with
127  * no GEM provided backing store. Instead the caller is responsible for
128  * backing the object and handling it.
129  */
130 int drm_gem_private_object_init(struct drm_device *dev,
131                         struct drm_gem_object *obj, size_t size)
132 {
133
134         KASSERT((size & (PAGE_SIZE - 1)) == 0,
135             ("Bad size %ju", (uintmax_t)size));
136
137         obj->dev = dev;
138         obj->vm_obj = NULL;
139
140         kref_init(&obj->refcount);
141         atomic_set(&obj->handle_count, 0);
142         obj->size = size;
143
144         return (0);
145 }
146
147
148 struct drm_gem_object *
149 drm_gem_object_alloc(struct drm_device *dev, size_t size)
150 {
151         struct drm_gem_object *obj;
152
153         obj = kmalloc(sizeof(*obj), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
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         return (obj);
161 dealloc:
162         vm_object_deallocate(obj->vm_obj);
163 free:
164         drm_free(obj, DRM_MEM_DRIVER);
165         return (NULL);
166 }
167
168 /**
169  * Called after the last reference to the object has been lost.
170  * Must be called holding struct_ mutex
171  *
172  * Frees the object
173  */
174 void
175 drm_gem_object_free(struct kref *kref)
176 {
177         struct drm_gem_object *obj = (struct drm_gem_object *) kref;
178         struct drm_device *dev = obj->dev;
179
180         DRM_LOCK_ASSERT(dev);
181         if (dev->driver->gem_free_object != NULL)
182                 dev->driver->gem_free_object(obj);
183 }
184
185 static void drm_gem_object_ref_bug(struct kref *list_kref)
186 {
187         panic("BUG");
188 }
189
190 /**
191  * Called after the last handle to the object has been closed
192  *
193  * Removes any name for the object. Note that this must be
194  * called before drm_gem_object_free or we'll be touching
195  * freed memory
196  */
197 void drm_gem_object_handle_free(struct drm_gem_object *obj)
198 {
199         struct drm_device *dev = obj->dev;
200
201         /* Remove any name for this object */
202         spin_lock(&dev->object_name_lock);
203         if (obj->name) {
204                 idr_remove(&dev->object_name_idr, obj->name);
205                 obj->name = 0;
206                 spin_unlock(&dev->object_name_lock);
207                 /*
208                  * The object name held a reference to this object, drop
209                  * that now.
210                 *
211                 * This cannot be the last reference, since the handle holds one too.
212                  */
213                 kref_put(&obj->refcount, drm_gem_object_ref_bug);
214         } else
215                 spin_unlock(&dev->object_name_lock);
216
217 }
218
219 /**
220  * Removes the mapping from handle to filp for this object.
221  */
222 int
223 drm_gem_handle_delete(struct drm_file *filp, u32 handle)
224 {
225         struct drm_device *dev;
226         struct drm_gem_object *obj;
227
228         /* This is gross. The idr system doesn't let us try a delete and
229          * return an error code.  It just spews if you fail at deleting.
230          * So, we have to grab a lock around finding the object and then
231          * doing the delete on it and dropping the refcount, or the user
232          * could race us to double-decrement the refcount and cause a
233          * use-after-free later.  Given the frequency of our handle lookups,
234          * we may want to use ida for number allocation and a hash table
235          * for the pointers, anyway.
236          */
237         spin_lock(&filp->table_lock);
238
239         /* Check if we currently have a reference on the object */
240         obj = idr_find(&filp->object_idr, handle);
241         if (obj == NULL) {
242                 spin_unlock(&filp->table_lock);
243                 return -EINVAL;
244         }
245         dev = obj->dev;
246
247         /* Release reference and decrement refcount. */
248         idr_remove(&filp->object_idr, handle);
249         spin_unlock(&filp->table_lock);
250
251         if (dev->driver->gem_close_object)
252                 dev->driver->gem_close_object(obj, filp);
253         drm_gem_object_handle_unreference_unlocked(obj);
254
255         return 0;
256 }
257
258 /**
259  * Create a handle for this object. This adds a handle reference
260  * to the object, which includes a regular reference count. Callers
261  * will likely want to dereference the object afterwards.
262  */
263 int
264 drm_gem_handle_create(struct drm_file *file_priv,
265                        struct drm_gem_object *obj,
266                        u32 *handlep)
267 {
268         struct drm_device *dev = obj->dev;
269         int ret;
270
271         /*
272          * Get the user-visible handle using idr.
273          */
274 again:
275         /* ensure there is space available to allocate a handle */
276         if (idr_pre_get(&file_priv->object_idr, GFP_KERNEL) == 0)
277                 return -ENOMEM;
278
279         /* do the allocation under our spinlock */
280         spin_lock(&file_priv->table_lock);
281         ret = idr_get_new_above(&file_priv->object_idr, obj, 1, (int *)handlep);
282         spin_unlock(&file_priv->table_lock);
283         if (ret == -EAGAIN)
284                 goto again;
285         else if (ret)
286                 return ret;
287
288         drm_gem_object_handle_reference(obj);
289
290         if (dev->driver->gem_open_object) {
291                 ret = dev->driver->gem_open_object(obj, file_priv);
292                 if (ret) {
293                         drm_gem_handle_delete(file_priv, *handlep);
294                         return ret;
295                 }
296         }
297
298         return 0;
299 }
300
301 /** Returns a reference to the object named by the handle. */
302 struct drm_gem_object *
303 drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp,
304                       u32 handle)
305 {
306         struct drm_gem_object *obj;
307
308         spin_lock(&filp->table_lock);
309
310         /* Check if we currently have a reference on the object */
311         obj = idr_find(&filp->object_idr, handle);
312         if (obj == NULL) {
313                 spin_unlock(&filp->table_lock);
314                 return NULL;
315         }
316
317         drm_gem_object_reference(obj);
318
319         spin_unlock(&filp->table_lock);
320
321         return obj;
322 }
323
324 int
325 drm_gem_close_ioctl(struct drm_device *dev, void *data,
326     struct drm_file *file_priv)
327 {
328         struct drm_gem_close *args;
329
330         if (!drm_core_check_feature(dev, DRIVER_GEM))
331                 return (ENODEV);
332         args = data;
333
334         return (drm_gem_handle_delete(file_priv, args->handle));
335 }
336
337 /**
338  * Create a global name for an object, returning the name.
339  *
340  * Note that the name does not hold a reference; when the object
341  * is freed, the name goes away.
342  */
343 int
344 drm_gem_flink_ioctl(struct drm_device *dev, void *data,
345                     struct drm_file *file_priv)
346 {
347         struct drm_gem_flink *args = data;
348         struct drm_gem_object *obj;
349         int ret;
350
351         if (!drm_core_check_feature(dev, DRIVER_GEM))
352                 return -ENODEV;
353
354         obj = drm_gem_object_lookup(dev, file_priv, args->handle);
355         if (obj == NULL)
356                 return -ENOENT;
357
358 again:
359         if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) {
360                 ret = -ENOMEM;
361                 goto err;
362         }
363
364         spin_lock(&dev->object_name_lock);
365         if (!obj->name) {
366                 ret = idr_get_new_above(&dev->object_name_idr, obj, 1,
367                                         &obj->name);
368                 args->name = (uint64_t) obj->name;
369                 spin_unlock(&dev->object_name_lock);
370
371                 if (ret == -EAGAIN)
372                         goto again;
373                 else if (ret)
374                         goto err;
375
376                 /* Allocate a reference for the name table.  */
377                 drm_gem_object_reference(obj);
378         } else {
379                 args->name = (uint64_t) obj->name;
380                 spin_unlock(&dev->object_name_lock);
381                 ret = 0;
382         }
383
384 err:
385         drm_gem_object_unreference_unlocked(obj);
386         return ret;
387 }
388
389 /**
390  * Open an object using the global name, returning a handle and the size.
391  *
392  * This handle (of course) holds a reference to the object, so the object
393  * will not go away until the handle is deleted.
394  */
395 int
396 drm_gem_open_ioctl(struct drm_device *dev, void *data,
397                    struct drm_file *file_priv)
398 {
399         struct drm_gem_open *args = data;
400         struct drm_gem_object *obj;
401         int ret;
402         u32 handle;
403
404 #if 0
405         if (!drm_core_check_feature(dev, DRIVER_GEM))
406 #endif
407         if (!(dev->driver->driver_features & DRIVER_GEM))
408                 return -ENODEV;
409
410         spin_lock(&dev->object_name_lock);
411         obj = idr_find(&dev->object_name_idr, (int) args->name);
412         if (obj)
413                 drm_gem_object_reference(obj);
414         spin_unlock(&dev->object_name_lock);
415         if (!obj)
416                 return -ENOENT;
417
418         ret = drm_gem_handle_create(file_priv, obj, &handle);
419         drm_gem_object_unreference_unlocked(obj);
420         if (ret)
421                 return ret;
422
423         args->handle = handle;
424         args->size = obj->size;
425
426         return 0;
427 }
428
429 /**
430  * Called at device open time, sets up the structure for handling refcounting
431  * of mm objects.
432  */
433 void
434 drm_gem_open(struct drm_device *dev, struct drm_file *file_private)
435 {
436         idr_init(&file_private->object_idr);
437         spin_init(&file_private->table_lock);
438 }
439
440 /**
441  * Called at device close to release the file's
442  * handle references on objects.
443  */
444 static int
445 drm_gem_object_release_handle(int id, void *ptr, void *data)
446 {
447         struct drm_file *file_priv = data;
448         struct drm_gem_object *obj = ptr;
449         struct drm_device *dev = obj->dev;
450
451         if (dev->driver->gem_close_object)
452                 dev->driver->gem_close_object(obj, file_priv);
453
454         drm_gem_object_handle_unreference_unlocked(obj);
455
456         return 0;
457 }
458
459 void
460 drm_gem_object_release(struct drm_gem_object *obj)
461 {
462
463         /*
464          * obj->vm_obj can be NULL for private gem objects.
465          */
466         vm_object_deallocate(obj->vm_obj);
467 }
468
469 /**
470  * Called at close time when the filp is going away.
471  *
472  * Releases any remaining references on objects by this filp.
473  */
474 void
475 drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
476 {
477         idr_for_each(&file_private->object_idr,
478                      &drm_gem_object_release_handle, file_private);
479
480         idr_remove_all(&file_private->object_idr);
481         idr_destroy(&file_private->object_idr);
482 }
483
484 static struct drm_gem_object *
485 drm_gem_object_from_offset(struct drm_device *dev, vm_ooffset_t offset)
486 {
487         struct drm_gem_object *obj;
488         struct drm_gem_mm *mm;
489         struct drm_hash_item *map_list;
490
491         if ((offset & DRM_GEM_MAPPING_MASK) != DRM_GEM_MAPPING_KEY)
492                 return (NULL);
493         offset &= ~DRM_GEM_MAPPING_KEY;
494         mm = dev->mm_private;
495         if (drm_ht_find_item(&mm->offset_hash, DRM_GEM_MAPPING_IDX(offset),
496             &map_list) != 0) {
497         DRM_DEBUG("drm_gem_object_from_offset: offset 0x%jx obj not found\n",
498                     (uintmax_t)offset);
499                 return (NULL);
500         }
501         obj = container_of(map_list, struct drm_gem_object, map_list);
502         return (obj);
503 }
504
505 int
506 drm_gem_create_mmap_offset(struct drm_gem_object *obj)
507 {
508         struct drm_device *dev;
509         struct drm_gem_mm *mm;
510         int ret;
511
512         if (obj->on_map)
513                 return (0);
514         dev = obj->dev;
515         mm = dev->mm_private;
516         ret = 0;
517
518         obj->map_list.key = alloc_unr(mm->idxunr);
519         ret = drm_ht_insert_item(&mm->offset_hash, &obj->map_list);
520         if (ret != 0) {
521                 DRM_ERROR("failed to add to map hash\n");
522                 free_unr(mm->idxunr, obj->map_list.key);
523                 return (ret);
524         }
525         obj->on_map = true;
526         return (0);
527 }
528
529 void
530 drm_gem_free_mmap_offset(struct drm_gem_object *obj)
531 {
532         struct drm_hash_item *list;
533         struct drm_gem_mm *mm;
534
535         if (!obj->on_map)
536                 return;
537         mm = obj->dev->mm_private;
538         list = &obj->map_list;
539
540         drm_ht_remove_item(&mm->offset_hash, list);
541         free_unr(mm->idxunr, list->key);
542         obj->on_map = false;
543 }
544
545 int
546 drm_gem_mmap_single(struct drm_device *dev, vm_ooffset_t *offset, vm_size_t size,
547     struct vm_object **obj_res, int nprot)
548 {
549         struct drm_gem_object *gem_obj;
550         struct vm_object *vm_obj;
551
552         DRM_LOCK(dev);
553         gem_obj = drm_gem_object_from_offset(dev, *offset);
554         if (gem_obj == NULL) {
555                 DRM_UNLOCK(dev);
556                 return (ENODEV);
557         }
558         drm_gem_object_reference(gem_obj);
559         DRM_UNLOCK(dev);
560         vm_obj = cdev_pager_allocate(gem_obj, OBJT_MGTDEVICE,
561             dev->driver->gem_pager_ops, size, nprot,
562             DRM_GEM_MAPPING_MAPOFF(*offset), curthread->td_ucred);
563         if (vm_obj == NULL) {
564                 drm_gem_object_unreference_unlocked(gem_obj);
565                 return (EINVAL);
566         }
567         *offset = DRM_GEM_MAPPING_MAPOFF(*offset);
568         *obj_res = vm_obj;
569         return (0);
570 }
571
572 void
573 drm_gem_pager_dtr(void *handle)
574 {
575         struct drm_gem_object *obj;
576         struct drm_device *dev;
577
578         obj = handle;
579         dev = obj->dev;
580
581         DRM_LOCK(dev);
582         drm_gem_free_mmap_offset(obj);
583         drm_gem_object_unreference(obj);
584         DRM_UNLOCK(dev);
585 }