drm: update bufmgr code to reflect changes in drm interface
[mesa.git] / src / mesa / drivers / dri / common / dri_bufmgr_ttm.c
1 /**************************************************************************
2 *
3 * Copyright © 2007 Intel Corporation
4 * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sub license, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 * The above copyright notice and this permission notice (including the
24 * next paragraph) shall be included in all copies or substantial portions
25 * of the Software.
26 *
27 *
28 **************************************************************************/
29 /*
30 * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com>
31 * Keith Whitwell <keithw-at-tungstengraphics-dot-com>
32 * Eric Anholt <eric@anholt.net>
33 */
34
35 #include <xf86drm.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include "glthread.h"
39 #include "errno.h"
40 #include "mtypes.h"
41 #include "dri_bufmgr.h"
42 #include "string.h"
43 #include "imports.h"
44
45 #define BUFMGR_DEBUG 0
46
47 typedef struct _dri_bufmgr_ttm {
48 dri_bufmgr bufmgr;
49
50 int fd;
51 _glthread_Mutex mutex;
52 unsigned int fence_type;
53 unsigned int fence_type_flush;
54 } dri_bufmgr_ttm;
55
56 typedef struct _dri_bo_ttm {
57 dri_bo bo;
58
59 int refcount; /* Protected by bufmgr->mutex */
60 drmBO drm_bo;
61 const char *name;
62 /**
63 * Note whether we are the owner of the buffer, to determine if we must
64 * drmBODestroy or drmBOUnreference to unreference the buffer.
65 */
66 GLboolean owner;
67 } dri_bo_ttm;
68
69 typedef struct _dri_fence_ttm
70 {
71 dri_fence fence;
72
73 int refcount; /* Protected by bufmgr->mutex */
74 const char *name;
75 drmFence drm_fence;
76 } dri_fence_ttm;
77
78 #if 0
79 int
80 driFenceSignaled(DriFenceObject * fence, unsigned type)
81 {
82 int signaled;
83 int ret;
84
85 if (fence == NULL)
86 return GL_TRUE;
87
88 _glthread_LOCK_MUTEX(fence->mutex);
89 ret = drmFenceSignaled(bufmgr_ttm->fd, &fence->fence, type, &signaled);
90 _glthread_UNLOCK_MUTEX(fence->mutex);
91 BM_CKFATAL(ret);
92 return signaled;
93 }
94 #endif
95
96 static dri_bo *
97 dri_ttm_alloc(dri_bufmgr *bufmgr, const char *name,
98 unsigned long size, unsigned int alignment,
99 unsigned int location_mask)
100 {
101 dri_bufmgr_ttm *ttm_bufmgr;
102 dri_bo_ttm *ttm_buf;
103 unsigned int pageSize = getpagesize();
104 int ret;
105 unsigned int flags, hint;
106
107 ttm_bufmgr = (dri_bufmgr_ttm *)bufmgr;
108
109 ttm_buf = malloc(sizeof(*ttm_buf));
110 if (!ttm_buf)
111 return NULL;
112
113 /* The mask argument doesn't do anything for us that we want other than
114 * determine which pool (TTM or local) the buffer is allocated into, so just
115 * pass all of the allocation class flags.
116 */
117 flags = location_mask | DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE |
118 DRM_BO_FLAG_EXE;
119 /* No hints we want to use. */
120 hint = 0;
121
122 ret = drmBOCreate(ttm_bufmgr->fd, 0, size, alignment / pageSize,
123 NULL, drm_bo_type_dc,
124 flags, hint, &ttm_buf->drm_bo);
125 if (ret != 0) {
126 free(ttm_buf);
127 return NULL;
128 }
129 ttm_buf->bo.size = ttm_buf->drm_bo.size;
130 ttm_buf->bo.offset = ttm_buf->drm_bo.offset;
131 ttm_buf->bo.virtual = NULL;
132 ttm_buf->bo.bufmgr = bufmgr;
133 ttm_buf->name = name;
134 ttm_buf->refcount = 1;
135 ttm_buf->owner = GL_TRUE;
136
137 #if BUFMGR_DEBUG
138 fprintf(stderr, "bo_create: %p (%s)\n", &ttm_buf->bo, ttm_buf->name);
139 #endif
140
141 return &ttm_buf->bo;
142 }
143
144 /* Our TTM backend doesn't allow creation of static buffers, as that requires
145 * privelege for the non-fake case, and the lock in the fake case where we were
146 * working around the X Server not creating buffers and passing handles to us.
147 */
148 static dri_bo *
149 dri_ttm_alloc_static(dri_bufmgr *bufmgr, const char *name,
150 unsigned long offset, unsigned long size, void *virtual,
151 unsigned int location_mask)
152 {
153 return NULL;
154 }
155
156 /** Returns a dri_bo wrapping the given buffer object handle.
157 *
158 * This can be used when one application needs to pass a buffer object
159 * to another.
160 */
161 dri_bo *
162 dri_ttm_bo_create_from_handle(dri_bufmgr *bufmgr, const char *name,
163 unsigned int handle)
164 {
165 dri_bufmgr_ttm *ttm_bufmgr;
166 dri_bo_ttm *ttm_buf;
167 int ret;
168
169 ttm_bufmgr = (dri_bufmgr_ttm *)bufmgr;
170
171 ttm_buf = malloc(sizeof(*ttm_buf));
172 if (!ttm_buf)
173 return NULL;
174
175 ret = drmBOReference(ttm_bufmgr->fd, handle, &ttm_buf->drm_bo);
176 if (ret != 0) {
177 free(ttm_buf);
178 return NULL;
179 }
180 ttm_buf->bo.size = ttm_buf->drm_bo.size;
181 ttm_buf->bo.offset = ttm_buf->drm_bo.offset;
182 ttm_buf->bo.virtual = NULL;
183 ttm_buf->bo.bufmgr = bufmgr;
184 ttm_buf->name = name;
185 ttm_buf->refcount = 1;
186 ttm_buf->owner = GL_FALSE;
187
188 #if BUFMGR_DEBUG
189 fprintf(stderr, "bo_create_from_handle: %p (%s)\n", &ttm_buf->bo,
190 ttm_buf->name);
191 #endif
192
193 return &ttm_buf->bo;
194 }
195
196 static void
197 dri_ttm_bo_reference(dri_bo *buf)
198 {
199 dri_bufmgr_ttm *bufmgr_ttm = (dri_bufmgr_ttm *)buf->bufmgr;
200 dri_bo_ttm *ttm_buf = (dri_bo_ttm *)buf;
201
202 _glthread_LOCK_MUTEX(bufmgr_ttm->mutex);
203 ttm_buf->refcount++;
204 _glthread_UNLOCK_MUTEX(bufmgr_ttm->mutex);
205 }
206
207 static void
208 dri_ttm_bo_unreference(dri_bo *buf)
209 {
210 dri_bufmgr_ttm *bufmgr_ttm = (dri_bufmgr_ttm *)buf->bufmgr;
211 dri_bo_ttm *ttm_buf = (dri_bo_ttm *)buf;
212
213 if (!buf)
214 return;
215
216 _glthread_LOCK_MUTEX(bufmgr_ttm->mutex);
217 if (--ttm_buf->refcount == 0) {
218 int ret;
219
220 /* XXX Having to use drmBODestroy as the opposite of drmBOCreate instead
221 * of simply unreferencing is madness, and leads to behaviors we may not
222 * want (making the buffer unsharable).
223 */
224 if (ttm_buf->owner)
225 ret = drmBODestroy(bufmgr_ttm->fd, &ttm_buf->drm_bo);
226 else
227 ret = drmBOUnReference(bufmgr_ttm->fd, &ttm_buf->drm_bo);
228 if (ret != 0) {
229 fprintf(stderr, "drmBOUnReference failed (%s): %s\n", ttm_buf->name,
230 strerror(-ret));
231 }
232 #if BUFMGR_DEBUG
233 fprintf(stderr, "bo_unreference final: %p (%s)\n",
234 &ttm_buf->bo, ttm_buf->name);
235 #endif
236 _glthread_UNLOCK_MUTEX(bufmgr_ttm->mutex);
237 free(buf);
238 return;
239 }
240 _glthread_UNLOCK_MUTEX(bufmgr_ttm->mutex);
241 }
242
243 static int
244 dri_ttm_bo_map(dri_bo *buf, GLboolean write_enable)
245 {
246 dri_bufmgr_ttm *bufmgr_ttm;
247 dri_bo_ttm *ttm_buf = (dri_bo_ttm *)buf;
248 unsigned int flags;
249
250 bufmgr_ttm = (dri_bufmgr_ttm *)buf->bufmgr;
251
252 flags = DRM_BO_FLAG_READ;
253 if (write_enable)
254 flags |= DRM_BO_FLAG_WRITE;
255
256 assert(buf->virtual == NULL);
257
258 #if BUFMGR_DEBUG
259 fprintf(stderr, "bo_map: %p (%s)\n", &ttm_buf->bo, ttm_buf->name);
260 #endif
261
262 return drmBOMap(bufmgr_ttm->fd, &ttm_buf->drm_bo, flags, 0, &buf->virtual);
263 }
264
265 static int
266 dri_ttm_bo_unmap(dri_bo *buf)
267 {
268 dri_bufmgr_ttm *bufmgr_ttm;
269 dri_bo_ttm *ttm_buf = (dri_bo_ttm *)buf;
270
271 if (buf == NULL)
272 return 0;
273
274 bufmgr_ttm = (dri_bufmgr_ttm *)buf->bufmgr;
275
276 assert(buf->virtual != NULL);
277
278 buf->virtual = NULL;
279
280 #if BUFMGR_DEBUG
281 fprintf(stderr, "bo_unmap: %p (%s)\n", &ttm_buf->bo, ttm_buf->name);
282 #endif
283
284 return drmBOUnmap(bufmgr_ttm->fd, &ttm_buf->drm_bo);
285 }
286
287 static int
288 dri_ttm_validate(dri_bo *buf, unsigned int flags)
289 {
290 dri_bufmgr_ttm *bufmgr_ttm;
291 dri_bo_ttm *ttm_buf = (dri_bo_ttm *)buf;
292 unsigned int mask;
293 int err;
294
295 /* XXX: Sanity-check whether we've already validated this one under
296 * different flags. See drmAddValidateItem().
297 */
298
299 bufmgr_ttm = (dri_bufmgr_ttm *)buf->bufmgr;
300
301 /* Calculate the appropriate mask to pass to the DRM. There appears to be
302 * be a direct relationship to flags, so it's unnecessary to have it passed
303 * in as an argument.
304 */
305 mask = DRM_BO_MASK_MEM;
306 mask |= flags & (DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE | DRM_BO_FLAG_EXE);
307
308 err = drmBOValidate(bufmgr_ttm->fd, &ttm_buf->drm_bo, 0, flags, mask, 0);
309
310 if (err == 0) {
311 /* XXX: add to fence list for sanity checking */
312 } else {
313 fprintf(stderr, "failed to validate buffer (%s): %s\n",
314 ttm_buf->name, strerror(-err));
315 }
316
317 buf->offset = ttm_buf->drm_bo.offset;
318
319 #if BUFMGR_DEBUG
320 fprintf(stderr, "bo_validate: %p (%s)\n", &ttm_buf->bo, ttm_buf->name);
321 #endif
322
323 return err;
324 }
325
326 static dri_fence *
327 dri_ttm_fence_validated(dri_bufmgr *bufmgr, const char *name,
328 GLboolean flushed)
329 {
330 dri_fence_ttm *fence_ttm = malloc(sizeof(*fence_ttm));
331 dri_bufmgr_ttm *bufmgr_ttm = (dri_bufmgr_ttm *)bufmgr;
332 int ret;
333 unsigned int type;
334
335 if (!fence_ttm)
336 return NULL;
337
338 if (flushed)
339 type = bufmgr_ttm->fence_type_flush;
340 else
341 type = bufmgr_ttm->fence_type;
342
343 fence_ttm->refcount = 1;
344 fence_ttm->name = name;
345 fence_ttm->fence.bufmgr = bufmgr;
346 ret = drmFenceBuffers(bufmgr_ttm->fd, type, 0, &fence_ttm->drm_fence);
347 if (ret) {
348 fprintf(stderr, "failed to fence (%s): %s\n", name, strerror(-ret));
349 free(fence_ttm);
350 return NULL;
351 }
352
353 #if BUFMGR_DEBUG
354 fprintf(stderr, "fence_validated: %p (%s)\n", &fence_ttm->fence,
355 fence_ttm->name);
356 #endif
357
358 return &fence_ttm->fence;
359 }
360
361 static void
362 dri_ttm_fence_reference(dri_fence *fence)
363 {
364 dri_fence_ttm *fence_ttm = (dri_fence_ttm *)fence;
365 dri_bufmgr_ttm *bufmgr_ttm = (dri_bufmgr_ttm *)fence->bufmgr;
366
367 _glthread_LOCK_MUTEX(bufmgr_ttm->mutex);
368 ++fence_ttm->refcount;
369 _glthread_UNLOCK_MUTEX(bufmgr_ttm->mutex);
370 }
371
372 static void
373 dri_ttm_fence_unreference(dri_fence *fence)
374 {
375 dri_fence_ttm *fence_ttm = (dri_fence_ttm *)fence;
376 dri_bufmgr_ttm *bufmgr_ttm = (dri_bufmgr_ttm *)fence->bufmgr;
377
378 if (!fence)
379 return;
380
381 _glthread_LOCK_MUTEX(bufmgr_ttm->mutex);
382 if (--fence_ttm->refcount == 0) {
383 int ret;
384
385 /* XXX Having to use drmFenceDestroy as the opposite of drmFenceBuffers
386 * instead of simply unreferencing is madness, and leads to behaviors we
387 * may not want (making the fence unsharable). This behavior by the DRM
388 * ioctls should be fixed, and drmFenceDestroy eliminated.
389 */
390 ret = drmFenceDestroy(bufmgr_ttm->fd, &fence_ttm->drm_fence);
391 if (ret != 0) {
392 fprintf(stderr, "drmFenceDestroy failed (%s): %s\n",
393 fence_ttm->name, strerror(-ret));
394 }
395
396 _glthread_UNLOCK_MUTEX(bufmgr_ttm->mutex);
397 free(fence);
398 return;
399 }
400 _glthread_UNLOCK_MUTEX(bufmgr_ttm->mutex);
401 }
402
403 static void
404 dri_ttm_fence_wait(dri_fence *fence)
405 {
406 dri_fence_ttm *fence_ttm = (dri_fence_ttm *)fence;
407 dri_bufmgr_ttm *bufmgr_ttm = (dri_bufmgr_ttm *)fence->bufmgr;
408 int ret;
409
410 _glthread_LOCK_MUTEX(bufmgr_ttm->mutex);
411 ret = drmFenceWait(bufmgr_ttm->fd, 0, &fence_ttm->drm_fence, 0);
412 _glthread_UNLOCK_MUTEX(bufmgr_ttm->mutex);
413 if (ret != 0) {
414 _mesa_printf("%s:%d: Error %d waiting for fence %s.\n",
415 __FILE__, __LINE__, ret, fence_ttm->name);
416 abort();
417 }
418
419 #if BUFMGR_DEBUG
420 fprintf(stderr, "fence_wait: %p (%s)\n", &fence_ttm->fence,
421 fence_ttm->name);
422 #endif
423 }
424
425 static void
426 dri_bufmgr_ttm_destroy(dri_bufmgr *bufmgr)
427 {
428 dri_bufmgr_ttm *bufmgr_ttm = (dri_bufmgr_ttm *)bufmgr;
429
430 _glthread_DESTROY_MUTEX(bufmgr_ttm->mutex);
431 free(bufmgr);
432 }
433
434 /**
435 * Initializes the TTM buffer manager, which uses the kernel to allocate, map,
436 * and manage map buffer objections.
437 *
438 * \param fd File descriptor of the opened DRM device.
439 * \param fence_type Driver-specific fence type used for fences with no flush.
440 * \param fence_type_flush Driver-specific fence type used for fences with a
441 * flush.
442 */
443 dri_bufmgr *
444 dri_bufmgr_ttm_init(int fd, unsigned int fence_type,
445 unsigned int fence_type_flush)
446 {
447 dri_bufmgr_ttm *bufmgr_ttm;
448
449 bufmgr_ttm = malloc(sizeof(*bufmgr_ttm));
450 bufmgr_ttm->fd = fd;
451 bufmgr_ttm->fence_type = fence_type;
452 bufmgr_ttm->fence_type_flush = fence_type_flush;
453 _glthread_INIT_MUTEX(bufmgr_ttm->mutex);
454
455 bufmgr_ttm->bufmgr.bo_alloc = dri_ttm_alloc;
456 bufmgr_ttm->bufmgr.bo_alloc_static = dri_ttm_alloc_static;
457 bufmgr_ttm->bufmgr.bo_reference = dri_ttm_bo_reference;
458 bufmgr_ttm->bufmgr.bo_unreference = dri_ttm_bo_unreference;
459 bufmgr_ttm->bufmgr.bo_map = dri_ttm_bo_map;
460 bufmgr_ttm->bufmgr.bo_unmap = dri_ttm_bo_unmap;
461 bufmgr_ttm->bufmgr.bo_validate = dri_ttm_validate;
462 bufmgr_ttm->bufmgr.fence_validated = dri_ttm_fence_validated;
463 bufmgr_ttm->bufmgr.fence_reference = dri_ttm_fence_reference;
464 bufmgr_ttm->bufmgr.fence_unreference = dri_ttm_fence_unreference;
465 bufmgr_ttm->bufmgr.fence_wait = dri_ttm_fence_wait;
466 bufmgr_ttm->bufmgr.destroy = dri_bufmgr_ttm_destroy;
467
468 return &bufmgr_ttm->bufmgr;
469 }