2 * Copyright (C) 2012-2018 Rob Clark <robclark@freedesktop.org>
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
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 NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * Rob Clark <robclark@freedesktop.org>
27 #include "freedreno_drmif.h"
28 #include "freedreno_priv.h"
30 void bo_del(struct fd_bo
*bo
);
31 extern pthread_mutex_t table_lock
;
34 add_bucket(struct fd_bo_cache
*cache
, int size
)
36 unsigned int i
= cache
->num_buckets
;
38 assert(i
< ARRAY_SIZE(cache
->cache_bucket
));
40 list_inithead(&cache
->cache_bucket
[i
].list
);
41 cache
->cache_bucket
[i
].size
= size
;
46 * @coarse: if true, only power-of-two bucket sizes, otherwise
47 * fill in for a bit smoother size curve..
50 fd_bo_cache_init(struct fd_bo_cache
*cache
, int coarse
)
52 unsigned long size
, cache_max_size
= 64 * 1024 * 1024;
54 /* OK, so power of two buckets was too wasteful of memory.
55 * Give 3 other sizes between each power of two, to hopefully
56 * cover things accurately enough. (The alternative is
57 * probably to just go for exact matching of sizes, and assume
58 * that for things like composited window resize the tiled
59 * width/height alignment and rounding of sizes to pages will
60 * get us useful cache hit rates anyway)
62 add_bucket(cache
, 4096);
63 add_bucket(cache
, 4096 * 2);
65 add_bucket(cache
, 4096 * 3);
67 /* Initialize the linked lists for BO reuse cache. */
68 for (size
= 4 * 4096; size
<= cache_max_size
; size
*= 2) {
69 add_bucket(cache
, size
);
71 add_bucket(cache
, size
+ size
* 1 / 4);
72 add_bucket(cache
, size
+ size
* 2 / 4);
73 add_bucket(cache
, size
+ size
* 3 / 4);
78 /* Frees older cached buffers. Called under table_lock */
80 fd_bo_cache_cleanup(struct fd_bo_cache
*cache
, time_t time
)
84 if (cache
->time
== time
)
87 for (i
= 0; i
< cache
->num_buckets
; i
++) {
88 struct fd_bo_bucket
*bucket
= &cache
->cache_bucket
[i
];
91 while (!LIST_IS_EMPTY(&bucket
->list
)) {
92 bo
= LIST_ENTRY(struct fd_bo
, bucket
->list
.next
, list
);
94 /* keep things in cache for at least 1 second: */
95 if (time
&& ((time
- bo
->free_time
) <= 1))
107 static struct fd_bo_bucket
* get_bucket(struct fd_bo_cache
*cache
, uint32_t size
)
111 /* hmm, this is what intel does, but I suppose we could calculate our
112 * way to the correct bucket size rather than looping..
114 for (i
= 0; i
< cache
->num_buckets
; i
++) {
115 struct fd_bo_bucket
*bucket
= &cache
->cache_bucket
[i
];
116 if (bucket
->size
>= size
) {
124 static int is_idle(struct fd_bo
*bo
)
126 return fd_bo_cpu_prep(bo
, NULL
,
127 DRM_FREEDRENO_PREP_READ
|
128 DRM_FREEDRENO_PREP_WRITE
|
129 DRM_FREEDRENO_PREP_NOSYNC
) == 0;
132 static struct fd_bo
*find_in_bucket(struct fd_bo_bucket
*bucket
, uint32_t flags
)
134 struct fd_bo
*bo
= NULL
;
136 /* TODO .. if we had an ALLOC_FOR_RENDER flag like intel, we could
137 * skip the busy check.. if it is only going to be a render target
138 * then we probably don't need to stall..
140 * NOTE that intel takes ALLOC_FOR_RENDER bo's from the list tail
141 * (MRU, since likely to be in GPU cache), rather than head (LRU)..
143 pthread_mutex_lock(&table_lock
);
144 if (!LIST_IS_EMPTY(&bucket
->list
)) {
145 bo
= LIST_ENTRY(struct fd_bo
, bucket
->list
.next
, list
);
146 /* TODO check for compatible flags? */
153 pthread_mutex_unlock(&table_lock
);
158 /* NOTE: size is potentially rounded up to bucket size: */
160 fd_bo_cache_alloc(struct fd_bo_cache
*cache
, uint32_t *size
, uint32_t flags
)
162 struct fd_bo
*bo
= NULL
;
163 struct fd_bo_bucket
*bucket
;
165 *size
= align(*size
, 4096);
166 bucket
= get_bucket(cache
, *size
);
168 /* see if we can be green and recycle: */
171 *size
= bucket
->size
;
172 bo
= find_in_bucket(bucket
, flags
);
175 if (bo
->funcs
->madvise(bo
, TRUE
) <= 0) {
176 /* we've lost the backing pages, delete and try again: */
177 pthread_mutex_lock(&table_lock
);
179 pthread_mutex_unlock(&table_lock
);
182 p_atomic_set(&bo
->refcnt
, 1);
183 fd_device_ref(bo
->dev
);
192 fd_bo_cache_free(struct fd_bo_cache
*cache
, struct fd_bo
*bo
)
194 struct fd_bo_bucket
*bucket
= get_bucket(cache
, bo
->size
);
196 /* see if we can be green and recycle: */
198 struct timespec time
;
200 bo
->funcs
->madvise(bo
, FALSE
);
202 clock_gettime(CLOCK_MONOTONIC
, &time
);
204 bo
->free_time
= time
.tv_sec
;
206 list_addtail(&bo
->list
, &bucket
->list
);
207 fd_bo_cache_cleanup(cache
, time
.tv_sec
);
209 /* bo's in the bucket cache don't have a ref and
210 * don't hold a ref to the dev:
212 fd_device_del_locked(bo
->dev
);