1 /**************************************************************************
3 * Copyright © 2007 Red Hat Inc.
4 * Copyright © 2007 Intel Corporation
5 * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sub license, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
22 * USE OR OTHER DEALINGS IN THE SOFTWARE.
24 * The above copyright notice and this permission notice (including the
25 * next paragraph) shall be included in all copies or substantial portions
29 **************************************************************************/
31 * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com>
32 * Keith Whitwell <keithw-at-tungstengraphics-dot-com>
33 * Eric Anholt <eric@anholt.net>
34 * Dave Airlie <airlied@linux.ie>
46 #include "dri_bufmgr.h"
52 #include "intel_bufmgr_ttm.h"
54 #define DBG(...) do { \
55 if (bufmgr_ttm->bufmgr.debug) \
56 fprintf(stderr, __VA_ARGS__); \
60 * These bits are always specified in each validation
61 * request. Other bits are not supported at this point
62 * as it would require a bit of investigation to figure
63 * out what mask value should be used.
65 #define INTEL_BO_MASK (DRM_BO_MASK_MEM | \
70 struct intel_validate_entry
{
72 struct drm_i915_op_arg bo_arg
;
75 struct dri_ttm_bo_bucket_entry
{
77 struct dri_ttm_bo_bucket_entry
*next
;
80 struct dri_ttm_bo_bucket
{
81 struct dri_ttm_bo_bucket_entry
*head
;
82 struct dri_ttm_bo_bucket_entry
**tail
;
84 * Limit on the number of entries in this bucket.
86 * 0 means that this caching at this bucket size is disabled.
87 * -1 means that there is no limit to caching at this size.
93 /* Arbitrarily chosen, 16 means that the maximum size we'll cache for reuse
94 * is 1 << 16 pages, or 256MB.
96 #define INTEL_TTM_BO_BUCKETS 16
97 typedef struct _dri_bufmgr_ttm
{
101 unsigned int fence_type
;
102 unsigned int fence_type_flush
;
106 struct intel_validate_entry
*validate_array
;
107 int validate_array_size
;
110 /** Array of lists of cached drmBOs of power-of-two sizes */
111 struct dri_ttm_bo_bucket cache_bucket
[INTEL_TTM_BO_BUCKETS
];
115 * Private information associated with a relocation that isn't already stored
116 * in the relocation buffer to be passed to the kernel.
118 struct dri_ttm_reloc
{
120 uint64_t validate_flags
;
121 /** Offset of target_buf after last execution of this relocation entry. */
122 unsigned int last_target_offset
;
125 typedef struct _dri_bo_ttm
{
129 unsigned int map_count
;
136 * Index of the buffer within the validation list while preparing a
137 * batchbuffer execution.
141 /** DRM buffer object containing relocation list */
142 uint32_t *reloc_buf_data
;
143 struct dri_ttm_reloc
*relocs
;
146 * Indicates that the buffer may be shared with other processes, so we
147 * can't hold maps beyond when the user does.
151 GLboolean delayed_unmap
;
152 /* Virtual address from the dri_bo_map whose unmap was delayed. */
156 typedef struct _dri_fence_ttm
179 static struct dri_ttm_bo_bucket
*
180 dri_ttm_bo_bucket_for_size(dri_bufmgr_ttm
*bufmgr_ttm
, unsigned long size
)
184 /* We only do buckets in power of two increments */
185 if ((size
& (size
- 1)) != 0)
188 /* We should only see sizes rounded to pages. */
189 assert((size
% 4096) == 0);
191 /* We always allocate in units of pages */
192 i
= ffs(size
/ 4096) - 1;
193 if (i
>= INTEL_TTM_BO_BUCKETS
)
196 return &bufmgr_ttm
->cache_bucket
[i
];
200 static void dri_ttm_dump_validation_list(dri_bufmgr_ttm
*bufmgr_ttm
)
204 for (i
= 0; i
< bufmgr_ttm
->validate_count
; i
++) {
205 dri_bo
*bo
= bufmgr_ttm
->validate_array
[i
].bo
;
206 dri_bo_ttm
*bo_ttm
= (dri_bo_ttm
*)bo
;
208 if (bo_ttm
->reloc_buf_data
!= NULL
) {
209 for (j
= 0; j
< (bo_ttm
->reloc_buf_data
[0] & 0xffff); j
++) {
210 uint32_t *reloc_entry
= bo_ttm
->reloc_buf_data
+
212 j
* I915_RELOC0_STRIDE
;
213 dri_bo
*target_bo
= bo_ttm
->relocs
[j
].target_buf
;
214 dri_bo_ttm
*target_ttm
= (dri_bo_ttm
*)target_bo
;
216 DBG("%2d: %s@0x%08x -> %s@0x%08lx + 0x%08x\n",
218 bo_ttm
->name
, reloc_entry
[0],
219 target_ttm
->name
, target_bo
->offset
,
223 DBG("%2d: %s\n", i
, bo_ttm
->name
);
229 * Adds the given buffer to the list of buffers to be validated (moved into the
230 * appropriate memory type) with the next batch submission.
232 * If a buffer is validated multiple times in a batch submission, it ends up
233 * with the intersection of the memory type flags and the union of the
237 intel_add_validate_buffer(dri_bo
*buf
,
240 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)buf
->bufmgr
;
241 dri_bo_ttm
*ttm_buf
= (dri_bo_ttm
*)buf
;
243 /* If we delayed doing an unmap to mitigate map/unmap syscall thrashing,
246 if (ttm_buf
->delayed_unmap
) {
247 drmBOUnmap(bufmgr_ttm
->fd
, &ttm_buf
->drm_bo
);
248 ttm_buf
->delayed_unmap
= GL_FALSE
;
251 if (ttm_buf
->validate_index
== -1) {
252 struct intel_validate_entry
*entry
;
253 struct drm_i915_op_arg
*arg
;
254 struct drm_bo_op_req
*req
;
257 /* Extend the array of validation entries as necessary. */
258 if (bufmgr_ttm
->validate_count
== bufmgr_ttm
->validate_array_size
) {
259 int i
, new_size
= bufmgr_ttm
->validate_array_size
* 2;
264 bufmgr_ttm
->validate_array
=
265 realloc(bufmgr_ttm
->validate_array
,
266 sizeof(struct intel_validate_entry
) * new_size
);
267 bufmgr_ttm
->validate_array_size
= new_size
;
269 /* Update pointers for realloced mem. */
270 for (i
= 0; i
< bufmgr_ttm
->validate_count
- 1; i
++) {
271 bufmgr_ttm
->validate_array
[i
].bo_arg
.next
= (unsigned long)
272 &bufmgr_ttm
->validate_array
[i
+ 1].bo_arg
;
276 /* Pick out the new array entry for ourselves */
277 index
= bufmgr_ttm
->validate_count
;
278 ttm_buf
->validate_index
= index
;
279 entry
= &bufmgr_ttm
->validate_array
[index
];
280 bufmgr_ttm
->validate_count
++;
282 /* Fill in array entry */
284 dri_bo_reference(buf
);
286 /* Fill in kernel arg */
287 arg
= &entry
->bo_arg
;
290 memset(arg
, 0, sizeof(*arg
));
291 req
->bo_req
.handle
= ttm_buf
->drm_bo
.handle
;
292 req
->op
= drm_bo_validate
;
293 req
->bo_req
.flags
= flags
;
294 req
->bo_req
.hint
= 0;
295 #ifdef DRM_BO_HINT_PRESUMED_OFFSET
296 /* PRESUMED_OFFSET indicates that all relocations pointing at this
297 * buffer have the correct offset. If any of our relocations don't,
298 * this flag will be cleared off the buffer later in the relocation
301 req
->bo_req
.hint
|= DRM_BO_HINT_PRESUMED_OFFSET
;
302 req
->bo_req
.presumed_offset
= buf
->offset
;
304 req
->bo_req
.mask
= INTEL_BO_MASK
;
305 req
->bo_req
.fence_class
= 0; /* Backwards compat. */
307 if (ttm_buf
->reloc_buf_data
!= NULL
)
308 arg
->reloc_ptr
= (unsigned long)(void *)ttm_buf
->reloc_buf_data
;
312 /* Hook up the linked list of args for the kernel */
315 bufmgr_ttm
->validate_array
[index
- 1].bo_arg
.next
=
319 struct intel_validate_entry
*entry
=
320 &bufmgr_ttm
->validate_array
[ttm_buf
->validate_index
];
321 struct drm_i915_op_arg
*arg
= &entry
->bo_arg
;
322 struct drm_bo_op_req
*req
= &arg
->d
.req
;
323 uint64_t memFlags
= req
->bo_req
.flags
& flags
& DRM_BO_MASK_MEM
;
324 uint64_t modeFlags
= (req
->bo_req
.flags
| flags
) & ~DRM_BO_MASK_MEM
;
326 /* Buffer was already in the validate list. Extend its flags as
332 "%s: No shared memory types between "
333 "0x%16llx and 0x%16llx\n",
334 __FUNCTION__
, req
->bo_req
.flags
, flags
);
337 if (flags
& ~INTEL_BO_MASK
) {
339 "%s: Flags bits 0x%16llx are not supposed to be used in a relocation\n",
340 __FUNCTION__
, flags
& ~INTEL_BO_MASK
);
343 req
->bo_req
.flags
= memFlags
| modeFlags
;
348 #define RELOC_BUF_SIZE(x) ((I915_RELOC_HEADER + x * I915_RELOC0_STRIDE) * \
352 intel_setup_reloc_list(dri_bo
*bo
)
354 dri_bo_ttm
*bo_ttm
= (dri_bo_ttm
*)bo
;
355 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)bo
->bufmgr
;
357 bo_ttm
->relocs
= calloc(bufmgr_ttm
->max_relocs
,
358 sizeof(struct dri_ttm_reloc
));
359 bo_ttm
->reloc_buf_data
= calloc(1, RELOC_BUF_SIZE(bufmgr_ttm
->max_relocs
));
361 /* Initialize the relocation list with the header:
362 * DWORD 0: relocation count
363 * DWORD 1: relocation type
364 * DWORD 2+3: handle to next relocation list (currently none) 64-bits
366 bo_ttm
->reloc_buf_data
[0] = 0;
367 bo_ttm
->reloc_buf_data
[1] = I915_RELOC_TYPE_0
;
368 bo_ttm
->reloc_buf_data
[2] = 0;
369 bo_ttm
->reloc_buf_data
[3] = 0;
376 driFenceSignaled(DriFenceObject
* fence
, unsigned type
)
384 ret
= drmFenceSignaled(bufmgr_ttm
->fd
, &fence
->fence
, type
, &signaled
);
391 dri_ttm_alloc(dri_bufmgr
*bufmgr
, const char *name
,
392 unsigned long size
, unsigned int alignment
,
393 uint64_t location_mask
)
395 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)bufmgr
;
397 unsigned int pageSize
= getpagesize();
401 unsigned long alloc_size
;
402 struct dri_ttm_bo_bucket
*bucket
;
403 GLboolean alloc_from_cache
= GL_FALSE
;
405 ttm_buf
= calloc(1, sizeof(*ttm_buf
));
409 /* The mask argument doesn't do anything for us that we want other than
410 * determine which pool (TTM or local) the buffer is allocated into, so
411 * just pass all of the allocation class flags.
413 flags
= location_mask
| DRM_BO_FLAG_READ
| DRM_BO_FLAG_WRITE
|
415 /* No hints we want to use. */
418 /* Round the allocated size up to a power of two number of pages. */
419 alloc_size
= 1 << logbase2(size
);
420 if (alloc_size
< pageSize
)
421 alloc_size
= pageSize
;
422 bucket
= dri_ttm_bo_bucket_for_size(bufmgr_ttm
, alloc_size
);
424 /* If we don't have caching at this size, don't actually round the
427 if (bucket
== NULL
|| bucket
->max_entries
== 0)
430 /* Get a buffer out of the cache if available */
431 if (bucket
!= NULL
&& bucket
->num_entries
> 0) {
432 struct dri_ttm_bo_bucket_entry
*entry
= bucket
->head
;
435 /* Check if the buffer is still in flight. If not, reuse it. */
436 ret
= drmBOBusy(bufmgr_ttm
->fd
, &entry
->drm_bo
, &busy
);
437 alloc_from_cache
= (ret
== 0 && busy
== 0);
439 if (alloc_from_cache
) {
440 bucket
->head
= entry
->next
;
441 if (entry
->next
== NULL
)
442 bucket
->tail
= &bucket
->head
;
443 bucket
->num_entries
--;
445 ttm_buf
->drm_bo
= entry
->drm_bo
;
450 if (!alloc_from_cache
) {
451 ret
= drmBOCreate(bufmgr_ttm
->fd
, alloc_size
, alignment
/ pageSize
,
452 NULL
, flags
, hint
, &ttm_buf
->drm_bo
);
459 ttm_buf
->bo
.size
= size
;
460 ttm_buf
->bo
.offset
= ttm_buf
->drm_bo
.offset
;
461 ttm_buf
->bo
.virtual = NULL
;
462 ttm_buf
->bo
.bufmgr
= bufmgr
;
463 ttm_buf
->name
= name
;
464 ttm_buf
->refcount
= 1;
465 ttm_buf
->reloc_buf_data
= NULL
;
466 ttm_buf
->relocs
= NULL
;
467 ttm_buf
->last_flags
= ttm_buf
->drm_bo
.flags
;
468 ttm_buf
->shared
= GL_FALSE
;
469 ttm_buf
->delayed_unmap
= GL_FALSE
;
470 ttm_buf
->validate_index
= -1;
472 DBG("bo_create: %p (%s) %ldb\n", &ttm_buf
->bo
, ttm_buf
->name
, size
);
477 /* Our TTM backend doesn't allow creation of static buffers, as that requires
478 * privelege for the non-fake case, and the lock in the fake case where we were
479 * working around the X Server not creating buffers and passing handles to us.
482 dri_ttm_alloc_static(dri_bufmgr
*bufmgr
, const char *name
,
483 unsigned long offset
, unsigned long size
, void *virtual,
484 uint64_t location_mask
)
490 * Returns a dri_bo wrapping the given buffer object handle.
492 * This can be used when one application needs to pass a buffer object
496 intel_ttm_bo_create_from_handle(dri_bufmgr
*bufmgr
, const char *name
,
499 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)bufmgr
;
503 ttm_buf
= calloc(1, sizeof(*ttm_buf
));
507 ret
= drmBOReference(bufmgr_ttm
->fd
, handle
, &ttm_buf
->drm_bo
);
509 fprintf(stderr
, "Couldn't reference %s handle 0x%08x: %s\n",
510 name
, handle
, strerror(-ret
));
514 ttm_buf
->bo
.size
= ttm_buf
->drm_bo
.size
;
515 ttm_buf
->bo
.offset
= ttm_buf
->drm_bo
.offset
;
516 ttm_buf
->bo
.virtual = NULL
;
517 ttm_buf
->bo
.bufmgr
= bufmgr
;
518 ttm_buf
->name
= name
;
519 ttm_buf
->refcount
= 1;
520 ttm_buf
->reloc_buf_data
= NULL
;
521 ttm_buf
->relocs
= NULL
;
522 ttm_buf
->last_flags
= ttm_buf
->drm_bo
.flags
;
523 ttm_buf
->shared
= GL_TRUE
;
524 ttm_buf
->delayed_unmap
= GL_FALSE
;
525 ttm_buf
->validate_index
= -1;
527 DBG("bo_create_from_handle: %p %08x (%s)\n",
528 &ttm_buf
->bo
, handle
, ttm_buf
->name
);
534 dri_ttm_bo_reference(dri_bo
*buf
)
536 dri_bo_ttm
*ttm_buf
= (dri_bo_ttm
*)buf
;
542 dri_ttm_bo_unreference(dri_bo
*buf
)
544 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)buf
->bufmgr
;
545 dri_bo_ttm
*ttm_buf
= (dri_bo_ttm
*)buf
;
550 if (--ttm_buf
->refcount
== 0) {
551 struct dri_ttm_bo_bucket
*bucket
;
554 assert(ttm_buf
->map_count
== 0);
556 if (ttm_buf
->reloc_buf_data
) {
559 /* Unreference all the target buffers */
560 for (i
= 0; i
< (ttm_buf
->reloc_buf_data
[0] & 0xffff); i
++)
561 dri_bo_unreference(ttm_buf
->relocs
[i
].target_buf
);
562 free(ttm_buf
->relocs
);
564 /* Free the kernel BO containing relocation entries */
565 free(ttm_buf
->reloc_buf_data
);
566 ttm_buf
->reloc_buf_data
= NULL
;
569 if (ttm_buf
->delayed_unmap
) {
570 int ret
= drmBOUnmap(bufmgr_ttm
->fd
, &ttm_buf
->drm_bo
);
573 fprintf(stderr
, "%s:%d: Error unmapping buffer %s: %s.\n",
574 __FILE__
, __LINE__
, ttm_buf
->name
, strerror(-ret
));
578 bucket
= dri_ttm_bo_bucket_for_size(bufmgr_ttm
, ttm_buf
->drm_bo
.size
);
579 /* Put the buffer into our internal cache for reuse if we can. */
580 if (!ttm_buf
->shared
&&
582 (bucket
->max_entries
== -1 ||
583 (bucket
->max_entries
> 0 &&
584 bucket
->num_entries
< bucket
->max_entries
)))
586 struct dri_ttm_bo_bucket_entry
*entry
;
588 entry
= calloc(1, sizeof(*entry
));
589 entry
->drm_bo
= ttm_buf
->drm_bo
;
592 *bucket
->tail
= entry
;
593 bucket
->tail
= &entry
->next
;
594 bucket
->num_entries
++;
596 /* Decrement the kernel refcount for the buffer. */
597 ret
= drmBOUnreference(bufmgr_ttm
->fd
, &ttm_buf
->drm_bo
);
599 fprintf(stderr
, "drmBOUnreference failed (%s): %s\n",
600 ttm_buf
->name
, strerror(-ret
));
604 DBG("bo_unreference final: %p (%s)\n", &ttm_buf
->bo
, ttm_buf
->name
);
612 dri_ttm_bo_map(dri_bo
*buf
, GLboolean write_enable
)
614 dri_bufmgr_ttm
*bufmgr_ttm
;
615 dri_bo_ttm
*ttm_buf
= (dri_bo_ttm
*)buf
;
619 bufmgr_ttm
= (dri_bufmgr_ttm
*)buf
->bufmgr
;
621 flags
= DRM_BO_FLAG_READ
;
623 flags
|= DRM_BO_FLAG_WRITE
;
625 /* Allow recursive mapping. Mesa may recursively map buffers with
626 * nested display loops.
628 if (ttm_buf
->map_count
++ != 0)
631 assert(buf
->virtual == NULL
);
633 DBG("bo_map: %p (%s)\n", &ttm_buf
->bo
, ttm_buf
->name
);
635 /* XXX: What about if we're upgrading from READ to WRITE? */
636 if (ttm_buf
->delayed_unmap
) {
637 buf
->virtual = ttm_buf
->saved_virtual
;
641 ret
= drmBOMap(bufmgr_ttm
->fd
, &ttm_buf
->drm_bo
, flags
, 0, &buf
->virtual);
643 fprintf(stderr
, "%s:%d: Error mapping buffer %s: %s .\n",
644 __FILE__
, __LINE__
, ttm_buf
->name
, strerror(-ret
));
651 dri_ttm_bo_unmap(dri_bo
*buf
)
653 dri_bufmgr_ttm
*bufmgr_ttm
;
654 dri_bo_ttm
*ttm_buf
= (dri_bo_ttm
*)buf
;
660 assert(ttm_buf
->map_count
!= 0);
661 if (--ttm_buf
->map_count
!= 0)
664 bufmgr_ttm
= (dri_bufmgr_ttm
*)buf
->bufmgr
;
666 assert(buf
->virtual != NULL
);
668 DBG("bo_unmap: %p (%s)\n", &ttm_buf
->bo
, ttm_buf
->name
);
670 if (!ttm_buf
->shared
) {
671 ttm_buf
->saved_virtual
= buf
->virtual;
672 ttm_buf
->delayed_unmap
= GL_TRUE
;
680 ret
= drmBOUnmap(bufmgr_ttm
->fd
, &ttm_buf
->drm_bo
);
682 fprintf(stderr
, "%s:%d: Error unmapping buffer %s: %s.\n",
683 __FILE__
, __LINE__
, ttm_buf
->name
, strerror(-ret
));
690 * Returns a dri_bo wrapping the given buffer object handle.
692 * This can be used when one application needs to pass a buffer object
696 intel_ttm_fence_create_from_arg(dri_bufmgr
*bufmgr
, const char *name
,
697 drm_fence_arg_t
*arg
)
699 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)bufmgr
;
700 dri_fence_ttm
*ttm_fence
;
702 ttm_fence
= malloc(sizeof(*ttm_fence
));
706 ttm_fence
->drm_fence
.handle
= arg
->handle
;
707 ttm_fence
->drm_fence
.fence_class
= arg
->fence_class
;
708 ttm_fence
->drm_fence
.type
= arg
->type
;
709 ttm_fence
->drm_fence
.flags
= arg
->flags
;
710 ttm_fence
->drm_fence
.signaled
= 0;
711 ttm_fence
->drm_fence
.sequence
= arg
->sequence
;
713 ttm_fence
->fence
.bufmgr
= bufmgr
;
714 ttm_fence
->name
= name
;
715 ttm_fence
->refcount
= 1;
717 DBG("fence_create_from_handle: %p (%s)\n",
718 &ttm_fence
->fence
, ttm_fence
->name
);
720 return &ttm_fence
->fence
;
725 dri_ttm_fence_reference(dri_fence
*fence
)
727 dri_fence_ttm
*fence_ttm
= (dri_fence_ttm
*)fence
;
728 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)fence
->bufmgr
;
730 ++fence_ttm
->refcount
;
731 DBG("fence_reference: %p (%s)\n", &fence_ttm
->fence
, fence_ttm
->name
);
735 dri_ttm_fence_unreference(dri_fence
*fence
)
737 dri_fence_ttm
*fence_ttm
= (dri_fence_ttm
*)fence
;
738 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)fence
->bufmgr
;
743 DBG("fence_unreference: %p (%s)\n", &fence_ttm
->fence
, fence_ttm
->name
);
745 if (--fence_ttm
->refcount
== 0) {
748 ret
= drmFenceUnreference(bufmgr_ttm
->fd
, &fence_ttm
->drm_fence
);
750 fprintf(stderr
, "drmFenceUnreference failed (%s): %s\n",
751 fence_ttm
->name
, strerror(-ret
));
760 dri_ttm_fence_wait(dri_fence
*fence
)
762 dri_fence_ttm
*fence_ttm
= (dri_fence_ttm
*)fence
;
763 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)fence
->bufmgr
;
766 ret
= drmFenceWait(bufmgr_ttm
->fd
, DRM_FENCE_FLAG_WAIT_LAZY
, &fence_ttm
->drm_fence
, 0);
768 fprintf(stderr
, "%s:%d: Error waiting for fence %s: %s.\n",
769 __FILE__
, __LINE__
, fence_ttm
->name
, strerror(-ret
));
773 DBG("fence_wait: %p (%s)\n", &fence_ttm
->fence
, fence_ttm
->name
);
777 dri_bufmgr_ttm_destroy(dri_bufmgr
*bufmgr
)
779 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)bufmgr
;
782 free(bufmgr_ttm
->validate_array
);
784 /* Free any cached buffer objects we were going to reuse */
785 for (i
= 0; i
< INTEL_TTM_BO_BUCKETS
; i
++) {
786 struct dri_ttm_bo_bucket
*bucket
= &bufmgr_ttm
->cache_bucket
[i
];
787 struct dri_ttm_bo_bucket_entry
*entry
;
789 while ((entry
= bucket
->head
) != NULL
) {
792 bucket
->head
= entry
->next
;
793 if (entry
->next
== NULL
)
794 bucket
->tail
= &bucket
->head
;
795 bucket
->num_entries
--;
797 /* Decrement the kernel refcount for the buffer. */
798 ret
= drmBOUnreference(bufmgr_ttm
->fd
, &entry
->drm_bo
);
800 fprintf(stderr
, "drmBOUnreference failed: %s\n",
812 * Adds the target buffer to the validation list and adds the relocation
813 * to the reloc_buffer's relocation list.
815 * The relocation entry at the given offset must already contain the
816 * precomputed relocation value, because the kernel will optimize out
817 * the relocation entry write when the buffer hasn't moved from the
818 * last known offset in target_buf.
821 dri_ttm_emit_reloc(dri_bo
*reloc_buf
, uint64_t flags
, GLuint delta
,
822 GLuint offset
, dri_bo
*target_buf
)
824 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)reloc_buf
->bufmgr
;
825 dri_bo_ttm
*reloc_buf_ttm
= (dri_bo_ttm
*)reloc_buf
;
826 dri_bo_ttm
*target_buf_ttm
= (dri_bo_ttm
*)target_buf
;
828 uint32_t *this_reloc
;
830 /* Create a new relocation list if needed */
831 if (reloc_buf_ttm
->reloc_buf_data
== NULL
)
832 intel_setup_reloc_list(reloc_buf
);
834 num_relocs
= reloc_buf_ttm
->reloc_buf_data
[0];
837 assert(num_relocs
< bufmgr_ttm
->max_relocs
);
839 this_reloc
= reloc_buf_ttm
->reloc_buf_data
+ I915_RELOC_HEADER
+
840 num_relocs
* I915_RELOC0_STRIDE
;
842 this_reloc
[0] = offset
;
843 this_reloc
[1] = delta
;
844 this_reloc
[2] = target_buf_ttm
->drm_bo
.handle
; /* To be filled in at exec time */
847 reloc_buf_ttm
->relocs
[num_relocs
].validate_flags
= flags
;
848 reloc_buf_ttm
->relocs
[num_relocs
].target_buf
= target_buf
;
849 dri_bo_reference(target_buf
);
851 reloc_buf_ttm
->reloc_buf_data
[0]++; /* Increment relocation count */
852 /* Check wraparound */
853 assert(reloc_buf_ttm
->reloc_buf_data
[0] != 0);
858 * Walk the tree of relocations rooted at BO and accumulate the list of
859 * validations to be performed and update the relocation buffers with
860 * index values into the validation list.
863 dri_ttm_bo_process_reloc(dri_bo
*bo
)
865 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)bo
->bufmgr
;
866 dri_bo_ttm
*bo_ttm
= (dri_bo_ttm
*)bo
;
867 unsigned int nr_relocs
;
870 if (bo_ttm
->reloc_buf_data
== NULL
)
873 nr_relocs
= bo_ttm
->reloc_buf_data
[0] & 0xffff;
875 for (i
= 0; i
< nr_relocs
; i
++) {
876 struct dri_ttm_reloc
*r
= &bo_ttm
->relocs
[i
];
878 /* Continue walking the tree depth-first. */
879 dri_ttm_bo_process_reloc(r
->target_buf
);
881 /* Add the target to the validate list */
882 intel_add_validate_buffer(r
->target_buf
, r
->validate_flags
);
884 /* Clear the PRESUMED_OFFSET flag from the validate list entry of the
885 * target if this buffer has a stale relocated pointer at it.
887 if (r
->last_target_offset
!= r
->target_buf
->offset
) {
888 dri_bo_ttm
*target_buf_ttm
= (dri_bo_ttm
*)r
->target_buf
;
889 struct intel_validate_entry
*entry
=
890 &bufmgr_ttm
->validate_array
[target_buf_ttm
->validate_index
];
892 entry
->bo_arg
.d
.req
.bo_req
.flags
&= ~DRM_BO_HINT_PRESUMED_OFFSET
;
898 dri_ttm_process_reloc(dri_bo
*batch_buf
, GLuint
*count
)
900 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)batch_buf
->bufmgr
;
902 /* Update indices and set up the validate list. */
903 dri_ttm_bo_process_reloc(batch_buf
);
905 /* Add the batch buffer to the validation list. There are no relocations
908 intel_add_validate_buffer(batch_buf
,
909 DRM_BO_FLAG_MEM_TT
| DRM_BO_FLAG_EXE
);
911 *count
= bufmgr_ttm
->validate_count
;
912 return &bufmgr_ttm
->validate_array
[0].bo_arg
;
916 intel_get_flags_mem_type_string(uint64_t flags
)
918 switch (flags
& DRM_BO_MASK_MEM
) {
919 case DRM_BO_FLAG_MEM_LOCAL
: return "local";
920 case DRM_BO_FLAG_MEM_TT
: return "ttm";
921 case DRM_BO_FLAG_MEM_VRAM
: return "vram";
922 case DRM_BO_FLAG_MEM_PRIV0
: return "priv0";
923 case DRM_BO_FLAG_MEM_PRIV1
: return "priv1";
924 case DRM_BO_FLAG_MEM_PRIV2
: return "priv2";
925 case DRM_BO_FLAG_MEM_PRIV3
: return "priv3";
926 case DRM_BO_FLAG_MEM_PRIV4
: return "priv4";
927 default: return NULL
;
932 intel_get_flags_caching_string(uint64_t flags
)
934 switch (flags
& (DRM_BO_FLAG_CACHED
| DRM_BO_FLAG_CACHED_MAPPED
)) {
936 case DRM_BO_FLAG_CACHED
: return "CU";
937 case DRM_BO_FLAG_CACHED_MAPPED
: return "UC";
938 case DRM_BO_FLAG_CACHED
| DRM_BO_FLAG_CACHED_MAPPED
: return "CC";
939 default: return NULL
;
944 intel_update_buffer_offsets (dri_bufmgr_ttm
*bufmgr_ttm
)
948 for (i
= 0; i
< bufmgr_ttm
->validate_count
; i
++) {
949 dri_bo
*bo
= bufmgr_ttm
->validate_array
[i
].bo
;
950 dri_bo_ttm
*bo_ttm
= (dri_bo_ttm
*)bo
;
951 struct drm_i915_op_arg
*arg
= &bufmgr_ttm
->validate_array
[i
].bo_arg
;
952 struct drm_bo_arg_rep
*rep
= &arg
->d
.rep
;
954 /* Update the flags */
955 if (rep
->bo_info
.flags
!= bo_ttm
->last_flags
) {
956 DBG("BO %s migrated: %s/%s -> %s/%s\n",
958 intel_get_flags_mem_type_string(bo_ttm
->last_flags
),
959 intel_get_flags_caching_string(bo_ttm
->last_flags
),
960 intel_get_flags_mem_type_string(rep
->bo_info
.flags
),
961 intel_get_flags_caching_string(rep
->bo_info
.flags
));
963 bo_ttm
->last_flags
= rep
->bo_info
.flags
;
965 /* Update the buffer offset */
966 if (rep
->bo_info
.offset
!= bo
->offset
) {
967 DBG("BO %s migrated: 0x%08lx -> 0x%08lx\n",
968 bo_ttm
->name
, bo
->offset
, (unsigned long)rep
->bo_info
.offset
);
969 bo
->offset
= rep
->bo_info
.offset
;
975 * Update the last target offset field of relocation entries for PRESUMED_OFFSET
979 dri_ttm_bo_post_submit(dri_bo
*bo
)
981 dri_bo_ttm
*bo_ttm
= (dri_bo_ttm
*)bo
;
982 unsigned int nr_relocs
;
985 if (bo_ttm
->reloc_buf_data
== NULL
)
988 nr_relocs
= bo_ttm
->reloc_buf_data
[0] & 0xffff;
990 for (i
= 0; i
< nr_relocs
; i
++) {
991 struct dri_ttm_reloc
*r
= &bo_ttm
->relocs
[i
];
993 /* Continue walking the tree depth-first. */
994 dri_ttm_bo_post_submit(r
->target_buf
);
996 r
->last_target_offset
= bo
->offset
;
1001 dri_ttm_post_submit(dri_bo
*batch_buf
, dri_fence
**last_fence
)
1003 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)batch_buf
->bufmgr
;
1006 intel_update_buffer_offsets (bufmgr_ttm
);
1008 dri_ttm_bo_post_submit(batch_buf
);
1010 if (bufmgr_ttm
->bufmgr
.debug
)
1011 dri_ttm_dump_validation_list(bufmgr_ttm
);
1013 for (i
= 0; i
< bufmgr_ttm
->validate_count
; i
++) {
1014 dri_bo
*bo
= bufmgr_ttm
->validate_array
[i
].bo
;
1015 dri_bo_ttm
*bo_ttm
= (dri_bo_ttm
*)bo
;
1017 /* Disconnect the buffer from the validate list */
1018 bo_ttm
->validate_index
= -1;
1019 dri_bo_unreference(bo
);
1020 bufmgr_ttm
->validate_array
[i
].bo
= NULL
;
1022 bufmgr_ttm
->validate_count
= 0;
1026 * Enables unlimited caching of buffer objects for reuse.
1028 * This is potentially very memory expensive, as the cache at each bucket
1029 * size is only bounded by how many buffers of that size we've managed to have
1030 * in flight at once.
1033 intel_ttm_enable_bo_reuse(dri_bufmgr
*bufmgr
)
1035 dri_bufmgr_ttm
*bufmgr_ttm
= (dri_bufmgr_ttm
*)bufmgr
;
1038 for (i
= 0; i
< INTEL_TTM_BO_BUCKETS
; i
++) {
1039 bufmgr_ttm
->cache_bucket
[i
].max_entries
= -1;
1047 dri_ttm_check_aperture_space(dri_bo
*bo
)
1053 * Initializes the TTM buffer manager, which uses the kernel to allocate, map,
1054 * and manage map buffer objections.
1056 * \param fd File descriptor of the opened DRM device.
1057 * \param fence_type Driver-specific fence type used for fences with no flush.
1058 * \param fence_type_flush Driver-specific fence type used for fences with a
1062 intel_bufmgr_ttm_init(int fd
, unsigned int fence_type
,
1063 unsigned int fence_type_flush
, int batch_size
)
1065 dri_bufmgr_ttm
*bufmgr_ttm
;
1068 bufmgr_ttm
= calloc(1, sizeof(*bufmgr_ttm
));
1069 bufmgr_ttm
->fd
= fd
;
1070 bufmgr_ttm
->fence_type
= fence_type
;
1071 bufmgr_ttm
->fence_type_flush
= fence_type_flush
;
1073 /* Let's go with one relocation per every 2 dwords (but round down a bit
1074 * since a power of two will mean an extra page allocation for the reloc
1077 * Every 4 was too few for the blender benchmark.
1079 bufmgr_ttm
->max_relocs
= batch_size
/ sizeof(uint32_t) / 2 - 2;
1081 bufmgr_ttm
->bufmgr
.bo_alloc
= dri_ttm_alloc
;
1082 bufmgr_ttm
->bufmgr
.bo_alloc_static
= dri_ttm_alloc_static
;
1083 bufmgr_ttm
->bufmgr
.bo_reference
= dri_ttm_bo_reference
;
1084 bufmgr_ttm
->bufmgr
.bo_unreference
= dri_ttm_bo_unreference
;
1085 bufmgr_ttm
->bufmgr
.bo_map
= dri_ttm_bo_map
;
1086 bufmgr_ttm
->bufmgr
.bo_unmap
= dri_ttm_bo_unmap
;
1087 bufmgr_ttm
->bufmgr
.fence_reference
= dri_ttm_fence_reference
;
1088 bufmgr_ttm
->bufmgr
.fence_unreference
= dri_ttm_fence_unreference
;
1089 bufmgr_ttm
->bufmgr
.fence_wait
= dri_ttm_fence_wait
;
1090 bufmgr_ttm
->bufmgr
.destroy
= dri_bufmgr_ttm_destroy
;
1091 bufmgr_ttm
->bufmgr
.emit_reloc
= dri_ttm_emit_reloc
;
1092 bufmgr_ttm
->bufmgr
.process_relocs
= dri_ttm_process_reloc
;
1093 bufmgr_ttm
->bufmgr
.post_submit
= dri_ttm_post_submit
;
1094 bufmgr_ttm
->bufmgr
.debug
= GL_FALSE
;
1095 bufmgr_ttm
->bufmgr
.check_aperture_space
= dri_ttm_check_aperture_space
;
1096 /* Initialize the linked lists for BO reuse cache. */
1097 for (i
= 0; i
< INTEL_TTM_BO_BUCKETS
; i
++)
1098 bufmgr_ttm
->cache_bucket
[i
].tail
= &bufmgr_ttm
->cache_bucket
[i
].head
;
1100 return &bufmgr_ttm
->bufmgr
;