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 262988 2014-03-10 23:16:19Z dumbbell $");
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
65 int
drm_gem_init(struct drm_device * dev)66 drm_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
81 void
drm_gem_destroy(struct drm_device * dev)82 drm_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
94 int
drm_gem_object_init(struct drm_device * dev,struct drm_gem_object * obj,size_t size)95 drm_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
113 int
drm_gem_private_object_init(struct drm_device * dev,struct drm_gem_object * obj,size_t size)114 drm_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_store_rel_int(&obj->handle_count, 0);
125 obj->size = size;
126
127 return (0);
128 }
129
130
131 struct drm_gem_object *
drm_gem_object_alloc(struct drm_device * dev,size_t size)132 drm_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);
144 dealloc:
145 vm_object_deallocate(obj->vm_obj);
146 free:
147 free(obj, DRM_MEM_DRIVER);
148 return (NULL);
149 }
150
151 void
drm_gem_object_free(struct drm_gem_object * obj)152 drm_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
162 void
drm_gem_object_reference(struct drm_gem_object * obj)163 drm_gem_object_reference(struct drm_gem_object *obj)
164 {
165
166 KASSERT(obj->refcount > 0, ("Dangling obj %p", obj));
167 refcount_acquire(&obj->refcount);
168 }
169
170 void
drm_gem_object_unreference(struct drm_gem_object * obj)171 drm_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
180 void
drm_gem_object_unreference_unlocked(struct drm_gem_object * obj)181 drm_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
193 void
drm_gem_object_handle_reference(struct drm_gem_object * obj)194 drm_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
201 void
drm_gem_object_handle_free(struct drm_gem_object * obj)202 drm_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
215 void
drm_gem_object_handle_unreference(struct drm_gem_object * obj)216 drm_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
228 void
drm_gem_object_handle_unreference_unlocked(struct drm_gem_object * obj)229 drm_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
241 int
drm_gem_handle_create(struct drm_file * file_priv,struct drm_gem_object * obj,uint32_t * handle)242 drm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj,
243 uint32_t *handle)
244 {
245 struct drm_device *dev = obj->dev;
246 int ret;
247
248 ret = drm_gem_name_create(&file_priv->object_names, obj, handle);
249 if (ret != 0)
250 return (ret);
251 drm_gem_object_handle_reference(obj);
252
253 if (dev->driver->gem_open_object) {
254 ret = dev->driver->gem_open_object(obj, file_priv);
255 if (ret) {
256 drm_gem_handle_delete(file_priv, *handle);
257 return ret;
258 }
259 }
260
261 return (0);
262 }
263
264 int
drm_gem_handle_delete(struct drm_file * file_priv,uint32_t handle)265 drm_gem_handle_delete(struct drm_file *file_priv, uint32_t handle)
266 {
267 struct drm_device *dev;
268 struct drm_gem_object *obj;
269
270 obj = drm_gem_names_remove(&file_priv->object_names, handle);
271 if (obj == NULL)
272 return (EINVAL);
273
274 dev = obj->dev;
275 if (dev->driver->gem_close_object)
276 dev->driver->gem_close_object(obj, file_priv);
277 drm_gem_object_handle_unreference_unlocked(obj);
278
279 return (0);
280 }
281
282 void
drm_gem_object_release(struct drm_gem_object * obj)283 drm_gem_object_release(struct drm_gem_object *obj)
284 {
285
286 /*
287 * obj->vm_obj can be NULL for private gem objects.
288 */
289 vm_object_deallocate(obj->vm_obj);
290 }
291
292 int
drm_gem_open_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)293 drm_gem_open_ioctl(struct drm_device *dev, void *data,
294 struct drm_file *file_priv)
295 {
296 struct drm_gem_open *args;
297 struct drm_gem_object *obj;
298 int ret;
299 uint32_t handle;
300
301 if (!drm_core_check_feature(dev, DRIVER_GEM))
302 return (ENODEV);
303 args = data;
304
305 obj = drm_gem_name_ref(&dev->object_names, args->name,
306 (void (*)(void *))drm_gem_object_reference);
307 if (obj == NULL)
308 return (ENOENT);
309 handle = 0;
310 ret = drm_gem_handle_create(file_priv, obj, &handle);
311 drm_gem_object_unreference_unlocked(obj);
312 if (ret != 0)
313 return (ret);
314
315 args->handle = handle;
316 args->size = obj->size;
317
318 return (0);
319 }
320
321 void
drm_gem_open(struct drm_device * dev,struct drm_file * file_priv)322 drm_gem_open(struct drm_device *dev, struct drm_file *file_priv)
323 {
324
325 drm_gem_names_init(&file_priv->object_names);
326 }
327
328 static int
drm_gem_object_release_handle(uint32_t name,void * ptr,void * arg)329 drm_gem_object_release_handle(uint32_t name, void *ptr, void *arg)
330 {
331 struct drm_file *file_priv;
332 struct drm_gem_object *obj;
333 struct drm_device *dev;
334
335 file_priv = arg;
336 obj = ptr;
337 dev = obj->dev;
338
339 if (dev->driver->gem_close_object)
340 dev->driver->gem_close_object(obj, file_priv);
341
342 drm_gem_object_handle_unreference(obj);
343 return (0);
344 }
345
346 void
drm_gem_release(struct drm_device * dev,struct drm_file * file_priv)347 drm_gem_release(struct drm_device *dev, struct drm_file *file_priv)
348 {
349
350 drm_gem_names_foreach(&file_priv->object_names,
351 drm_gem_object_release_handle, file_priv);
352 drm_gem_names_fini(&file_priv->object_names);
353 }
354
355 int
drm_gem_close_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)356 drm_gem_close_ioctl(struct drm_device *dev, void *data,
357 struct drm_file *file_priv)
358 {
359 struct drm_gem_close *args;
360
361 if (!drm_core_check_feature(dev, DRIVER_GEM))
362 return (ENODEV);
363 args = data;
364
365 return (drm_gem_handle_delete(file_priv, args->handle));
366 }
367
368 int
drm_gem_flink_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)369 drm_gem_flink_ioctl(struct drm_device *dev, void *data,
370 struct drm_file *file_priv)
371 {
372 struct drm_gem_flink *args;
373 struct drm_gem_object *obj;
374 int error;
375
376 if (!drm_core_check_feature(dev, DRIVER_GEM))
377 return (ENODEV);
378 args = data;
379
380 obj = drm_gem_name_ref(&file_priv->object_names, args->handle,
381 (void (*)(void *))drm_gem_object_reference);
382 if (obj == NULL)
383 return (ENOENT);
384 error = drm_gem_name_create(&dev->object_names, obj, &obj->name);
385 if (error != 0) {
386 if (error == EALREADY)
387 error = 0;
388 drm_gem_object_unreference_unlocked(obj);
389 }
390 if (error == 0)
391 args->name = obj->name;
392 return (error);
393 }
394
395 struct drm_gem_object *
drm_gem_object_lookup(struct drm_device * dev,struct drm_file * file_priv,uint32_t handle)396 drm_gem_object_lookup(struct drm_device *dev, struct drm_file *file_priv,
397 uint32_t handle)
398 {
399 struct drm_gem_object *obj;
400
401 obj = drm_gem_name_ref(&file_priv->object_names, handle,
402 (void (*)(void *))drm_gem_object_reference);
403 return (obj);
404 }
405
406 static struct drm_gem_object *
drm_gem_object_from_offset(struct drm_device * dev,vm_ooffset_t offset)407 drm_gem_object_from_offset(struct drm_device *dev, vm_ooffset_t offset)
408 {
409 struct drm_gem_object *obj;
410 struct drm_gem_mm *mm;
411 struct drm_hash_item *map_list;
412
413 if ((offset & DRM_GEM_MAPPING_MASK) != DRM_GEM_MAPPING_KEY)
414 return (NULL);
415 offset &= ~DRM_GEM_MAPPING_KEY;
416 mm = dev->mm_private;
417 if (drm_ht_find_item(&mm->offset_hash, DRM_GEM_MAPPING_IDX(offset),
418 &map_list) != 0) {
419 DRM_DEBUG("drm_gem_object_from_offset: offset 0x%jx obj not found\n",
420 (uintmax_t)offset);
421 return (NULL);
422 }
423 obj = member2struct(drm_gem_object, map_list, map_list);
424 return (obj);
425 }
426
427 int
drm_gem_create_mmap_offset(struct drm_gem_object * obj)428 drm_gem_create_mmap_offset(struct drm_gem_object *obj)
429 {
430 struct drm_device *dev;
431 struct drm_gem_mm *mm;
432 int ret;
433
434 if (obj->on_map)
435 return (0);
436 dev = obj->dev;
437 mm = dev->mm_private;
438 ret = 0;
439
440 obj->map_list.key = alloc_unr(mm->idxunr);
441 ret = drm_ht_insert_item(&mm->offset_hash, &obj->map_list);
442 if (ret != 0) {
443 DRM_ERROR("failed to add to map hash\n");
444 free_unr(mm->idxunr, obj->map_list.key);
445 return (ret);
446 }
447 obj->on_map = true;
448 return (0);
449 }
450
451 void
drm_gem_free_mmap_offset(struct drm_gem_object * obj)452 drm_gem_free_mmap_offset(struct drm_gem_object *obj)
453 {
454 struct drm_hash_item *list;
455 struct drm_gem_mm *mm;
456
457 if (!obj->on_map)
458 return;
459 mm = obj->dev->mm_private;
460 list = &obj->map_list;
461
462 drm_ht_remove_item(&mm->offset_hash, list);
463 free_unr(mm->idxunr, list->key);
464 obj->on_map = false;
465 }
466
467 int
drm_gem_mmap_single(struct drm_device * dev,vm_ooffset_t * offset,vm_size_t size,struct vm_object ** obj_res,int nprot)468 drm_gem_mmap_single(struct drm_device *dev, vm_ooffset_t *offset, vm_size_t size,
469 struct vm_object **obj_res, int nprot)
470 {
471 struct drm_gem_object *gem_obj;
472 struct vm_object *vm_obj;
473
474 DRM_LOCK(dev);
475 gem_obj = drm_gem_object_from_offset(dev, *offset);
476 if (gem_obj == NULL) {
477 DRM_UNLOCK(dev);
478 return (ENODEV);
479 }
480 drm_gem_object_reference(gem_obj);
481 DRM_UNLOCK(dev);
482 vm_obj = cdev_pager_allocate(gem_obj, OBJT_MGTDEVICE,
483 dev->driver->gem_pager_ops, size, nprot,
484 DRM_GEM_MAPPING_MAPOFF(*offset), curthread->td_ucred);
485 if (vm_obj == NULL) {
486 drm_gem_object_unreference_unlocked(gem_obj);
487 return (EINVAL);
488 }
489 *offset = DRM_GEM_MAPPING_MAPOFF(*offset);
490 *obj_res = vm_obj;
491 return (0);
492 }
493
494 void
drm_gem_pager_dtr(void * handle)495 drm_gem_pager_dtr(void *handle)
496 {
497 struct drm_gem_object *obj;
498 struct drm_device *dev;
499
500 obj = handle;
501 dev = obj->dev;
502
503 DRM_LOCK(dev);
504 drm_gem_free_mmap_offset(obj);
505 drm_gem_object_unreference(obj);
506 DRM_UNLOCK(dev);
507 }
508