diff options
author | Rob Clark | 2016-05-31 09:46:59 -0500 |
---|---|---|
committer | Rob Clark | 2016-07-20 18:42:21 -0500 |
commit | 82780c87f9e39ab19dce6ec619ad744a26a0f886 (patch) | |
tree | 475f51dcb29e89b328c0ba5ae200d420a1ee1c45 /freedreno | |
parent | 0b34b683071901e1ffa82a2762e71c185217c1bc (diff) | |
download | external-libdrm-82780c87f9e39ab19dce6ec619ad744a26a0f886.tar.gz external-libdrm-82780c87f9e39ab19dce6ec619ad744a26a0f886.tar.xz external-libdrm-82780c87f9e39ab19dce6ec619ad744a26a0f886.zip |
freedreno: move bo-cache to it's own file
Signed-off-by: Rob Clark <robclark@freedesktop.org>
Diffstat (limited to 'freedreno')
-rw-r--r-- | freedreno/Makefile.sources | 1 | ||||
-rw-r--r-- | freedreno/freedreno_bo.c | 143 | ||||
-rw-r--r-- | freedreno/freedreno_bo_cache.c | 205 | ||||
-rw-r--r-- | freedreno/freedreno_device.c | 38 |
4 files changed, 209 insertions, 178 deletions
diff --git a/freedreno/Makefile.sources b/freedreno/Makefile.sources index 57a8bf1b..68a679bf 100644 --- a/freedreno/Makefile.sources +++ b/freedreno/Makefile.sources | |||
@@ -4,6 +4,7 @@ LIBDRM_FREEDRENO_FILES := \ | |||
4 | freedreno_priv.h \ | 4 | freedreno_priv.h \ |
5 | freedreno_ringbuffer.c \ | 5 | freedreno_ringbuffer.c \ |
6 | freedreno_bo.c \ | 6 | freedreno_bo.c \ |
7 | freedreno_bo_cache.c \ | ||
7 | msm/msm_bo.c \ | 8 | msm/msm_bo.c \ |
8 | msm/msm_device.c \ | 9 | msm/msm_device.c \ |
9 | msm/msm_drm.h \ | 10 | msm/msm_drm.h \ |
diff --git a/freedreno/freedreno_bo.c b/freedreno/freedreno_bo.c index da563983..cf2d7cb9 100644 --- a/freedreno/freedreno_bo.c +++ b/freedreno/freedreno_bo.c | |||
@@ -33,9 +33,8 @@ | |||
33 | #include "freedreno_drmif.h" | 33 | #include "freedreno_drmif.h" |
34 | #include "freedreno_priv.h" | 34 | #include "freedreno_priv.h" |
35 | 35 | ||
36 | static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER; | 36 | drm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER; |
37 | 37 | drm_private void bo_del(struct fd_bo *bo); | |
38 | static void bo_del(struct fd_bo *bo); | ||
39 | 38 | ||
40 | /* set buffer name, and add to table, call w/ table_lock held: */ | 39 | /* set buffer name, and add to table, call w/ table_lock held: */ |
41 | static void set_name(struct fd_bo *bo, uint32_t name) | 40 | static void set_name(struct fd_bo *bo, uint32_t name) |
@@ -83,116 +82,6 @@ static struct fd_bo * bo_from_handle(struct fd_device *dev, | |||
83 | return bo; | 82 | return bo; |
84 | } | 83 | } |
85 | 84 | ||
86 | /* Frees older cached buffers. Called under table_lock */ | ||
87 | drm_private void | ||
88 | fd_bo_cache_cleanup(struct fd_bo_cache *cache, time_t time) | ||
89 | { | ||
90 | int i; | ||
91 | |||
92 | if (cache->time == time) | ||
93 | return; | ||
94 | |||
95 | for (i = 0; i < cache->num_buckets; i++) { | ||
96 | struct fd_bo_bucket *bucket = &cache->cache_bucket[i]; | ||
97 | struct fd_bo *bo; | ||
98 | |||
99 | while (!LIST_IS_EMPTY(&bucket->list)) { | ||
100 | bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list); | ||
101 | |||
102 | /* keep things in cache for at least 1 second: */ | ||
103 | if (time && ((time - bo->free_time) <= 1)) | ||
104 | break; | ||
105 | |||
106 | list_del(&bo->list); | ||
107 | bo_del(bo); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | cache->time = time; | ||
112 | } | ||
113 | |||
114 | static struct fd_bo_bucket * get_bucket(struct fd_bo_cache *cache, uint32_t size) | ||
115 | { | ||
116 | int i; | ||
117 | |||
118 | /* hmm, this is what intel does, but I suppose we could calculate our | ||
119 | * way to the correct bucket size rather than looping.. | ||
120 | */ | ||
121 | for (i = 0; i < cache->num_buckets; i++) { | ||
122 | struct fd_bo_bucket *bucket = &cache->cache_bucket[i]; | ||
123 | if (bucket->size >= size) { | ||
124 | return bucket; | ||
125 | } | ||
126 | } | ||
127 | |||
128 | return NULL; | ||
129 | } | ||
130 | |||
131 | static int is_idle(struct fd_bo *bo) | ||
132 | { | ||
133 | return fd_bo_cpu_prep(bo, NULL, | ||
134 | DRM_FREEDRENO_PREP_READ | | ||
135 | DRM_FREEDRENO_PREP_WRITE | | ||
136 | DRM_FREEDRENO_PREP_NOSYNC) == 0; | ||
137 | } | ||
138 | |||
139 | static struct fd_bo *find_in_bucket(struct fd_bo_bucket *bucket, uint32_t flags) | ||
140 | { | ||
141 | struct fd_bo *bo = NULL; | ||
142 | |||
143 | /* TODO .. if we had an ALLOC_FOR_RENDER flag like intel, we could | ||
144 | * skip the busy check.. if it is only going to be a render target | ||
145 | * then we probably don't need to stall.. | ||
146 | * | ||
147 | * NOTE that intel takes ALLOC_FOR_RENDER bo's from the list tail | ||
148 | * (MRU, since likely to be in GPU cache), rather than head (LRU).. | ||
149 | */ | ||
150 | pthread_mutex_lock(&table_lock); | ||
151 | while (!LIST_IS_EMPTY(&bucket->list)) { | ||
152 | bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list); | ||
153 | if (0 /* TODO: if madvise tells us bo is gone... */) { | ||
154 | list_del(&bo->list); | ||
155 | bo_del(bo); | ||
156 | bo = NULL; | ||
157 | continue; | ||
158 | } | ||
159 | /* TODO check for compatible flags? */ | ||
160 | if (is_idle(bo)) { | ||
161 | list_del(&bo->list); | ||
162 | break; | ||
163 | } | ||
164 | bo = NULL; | ||
165 | break; | ||
166 | } | ||
167 | pthread_mutex_unlock(&table_lock); | ||
168 | |||
169 | return bo; | ||
170 | } | ||
171 | |||
172 | /* NOTE: size is potentially rounded up to bucket size: */ | ||
173 | drm_private struct fd_bo * | ||
174 | fd_bo_cache_alloc(struct fd_bo_cache *cache, uint32_t *size, uint32_t flags) | ||
175 | { | ||
176 | struct fd_bo *bo = NULL; | ||
177 | struct fd_bo_bucket *bucket; | ||
178 | |||
179 | *size = ALIGN(*size, 4096); | ||
180 | bucket = get_bucket(cache, *size); | ||
181 | |||
182 | /* see if we can be green and recycle: */ | ||
183 | if (bucket) { | ||
184 | *size = bucket->size; | ||
185 | bo = find_in_bucket(bucket, flags); | ||
186 | if (bo) { | ||
187 | atomic_set(&bo->refcnt, 1); | ||
188 | fd_device_ref(bo->dev); | ||
189 | return bo; | ||
190 | } | ||
191 | } | ||
192 | |||
193 | return NULL; | ||
194 | } | ||
195 | |||
196 | struct fd_bo * | 85 | struct fd_bo * |
197 | fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags) | 86 | fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags) |
198 | { | 87 | { |
@@ -303,32 +192,6 @@ struct fd_bo * fd_bo_ref(struct fd_bo *bo) | |||
303 | return bo; | 192 | return bo; |
304 | } | 193 | } |
305 | 194 | ||
306 | drm_private int | ||
307 | fd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo) | ||
308 | { | ||
309 | struct fd_bo_bucket *bucket = get_bucket(cache, bo->size); | ||
310 | |||
311 | /* see if we can be green and recycle: */ | ||
312 | if (bucket) { | ||
313 | struct timespec time; | ||
314 | |||
315 | clock_gettime(CLOCK_MONOTONIC, &time); | ||
316 | |||
317 | bo->free_time = time.tv_sec; | ||
318 | list_addtail(&bo->list, &bucket->list); | ||
319 | fd_bo_cache_cleanup(cache, time.tv_sec); | ||
320 | |||
321 | /* bo's in the bucket cache don't have a ref and | ||
322 | * don't hold a ref to the dev: | ||
323 | */ | ||
324 | fd_device_del_locked(bo->dev); | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | return -1; | ||
330 | } | ||
331 | |||
332 | void fd_bo_del(struct fd_bo *bo) | 195 | void fd_bo_del(struct fd_bo *bo) |
333 | { | 196 | { |
334 | struct fd_device *dev = bo->dev; | 197 | struct fd_device *dev = bo->dev; |
@@ -348,7 +211,7 @@ out: | |||
348 | } | 211 | } |
349 | 212 | ||
350 | /* Called under table_lock */ | 213 | /* Called under table_lock */ |
351 | static void bo_del(struct fd_bo *bo) | 214 | drm_private void bo_del(struct fd_bo *bo) |
352 | { | 215 | { |
353 | if (bo->map) | 216 | if (bo->map) |
354 | drm_munmap(bo->map, bo->size); | 217 | drm_munmap(bo->map, bo->size); |
diff --git a/freedreno/freedreno_bo_cache.c b/freedreno/freedreno_bo_cache.c new file mode 100644 index 00000000..17199d27 --- /dev/null +++ b/freedreno/freedreno_bo_cache.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */ | ||
2 | |||
3 | /* | ||
4 | * Copyright (C) 2016 Rob Clark <robclark@freedesktop.org> | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | * copy of this software and associated documentation files (the "Software"), | ||
8 | * to deal in the Software without restriction, including without limitation | ||
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
10 | * and/or sell copies of the Software, and to permit persons to whom the | ||
11 | * Software is furnished to do so, subject to the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the next | ||
14 | * paragraph) shall be included in all copies or substantial portions of the | ||
15 | * Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
23 | * SOFTWARE. | ||
24 | * | ||
25 | * Authors: | ||
26 | * Rob Clark <robclark@freedesktop.org> | ||
27 | */ | ||
28 | |||
29 | #ifdef HAVE_CONFIG_H | ||
30 | # include <config.h> | ||
31 | #endif | ||
32 | |||
33 | #include "freedreno_drmif.h" | ||
34 | #include "freedreno_priv.h" | ||
35 | |||
36 | |||
37 | drm_private void bo_del(struct fd_bo *bo); | ||
38 | drm_private extern pthread_mutex_t table_lock; | ||
39 | |||
40 | static void | ||
41 | add_bucket(struct fd_bo_cache *cache, int size) | ||
42 | { | ||
43 | unsigned int i = cache->num_buckets; | ||
44 | |||
45 | assert(i < ARRAY_SIZE(cache->cache_bucket)); | ||
46 | |||
47 | list_inithead(&cache->cache_bucket[i].list); | ||
48 | cache->cache_bucket[i].size = size; | ||
49 | cache->num_buckets++; | ||
50 | } | ||
51 | |||
52 | drm_private void | ||
53 | fd_bo_cache_init(struct fd_bo_cache *cache) | ||
54 | { | ||
55 | unsigned long size, cache_max_size = 64 * 1024 * 1024; | ||
56 | |||
57 | /* OK, so power of two buckets was too wasteful of memory. | ||
58 | * Give 3 other sizes between each power of two, to hopefully | ||
59 | * cover things accurately enough. (The alternative is | ||
60 | * probably to just go for exact matching of sizes, and assume | ||
61 | * that for things like composited window resize the tiled | ||
62 | * width/height alignment and rounding of sizes to pages will | ||
63 | * get us useful cache hit rates anyway) | ||
64 | */ | ||
65 | add_bucket(cache, 4096); | ||
66 | add_bucket(cache, 4096 * 2); | ||
67 | add_bucket(cache, 4096 * 3); | ||
68 | |||
69 | /* Initialize the linked lists for BO reuse cache. */ | ||
70 | for (size = 4 * 4096; size <= cache_max_size; size *= 2) { | ||
71 | add_bucket(cache, size); | ||
72 | add_bucket(cache, size + size * 1 / 4); | ||
73 | add_bucket(cache, size + size * 2 / 4); | ||
74 | add_bucket(cache, size + size * 3 / 4); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | /* Frees older cached buffers. Called under table_lock */ | ||
79 | drm_private void | ||
80 | fd_bo_cache_cleanup(struct fd_bo_cache *cache, time_t time) | ||
81 | { | ||
82 | int i; | ||
83 | |||
84 | if (cache->time == time) | ||
85 | return; | ||
86 | |||
87 | for (i = 0; i < cache->num_buckets; i++) { | ||
88 | struct fd_bo_bucket *bucket = &cache->cache_bucket[i]; | ||
89 | struct fd_bo *bo; | ||
90 | |||
91 | while (!LIST_IS_EMPTY(&bucket->list)) { | ||
92 | bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list); | ||
93 | |||
94 | /* keep things in cache for at least 1 second: */ | ||
95 | if (time && ((time - bo->free_time) <= 1)) | ||
96 | break; | ||
97 | |||
98 | list_del(&bo->list); | ||
99 | bo_del(bo); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | cache->time = time; | ||
104 | } | ||
105 | |||
106 | static struct fd_bo_bucket * get_bucket(struct fd_bo_cache *cache, uint32_t size) | ||
107 | { | ||
108 | int i; | ||
109 | |||
110 | /* hmm, this is what intel does, but I suppose we could calculate our | ||
111 | * way to the correct bucket size rather than looping.. | ||
112 | */ | ||
113 | for (i = 0; i < cache->num_buckets; i++) { | ||
114 | struct fd_bo_bucket *bucket = &cache->cache_bucket[i]; | ||
115 | if (bucket->size >= size) { | ||
116 | return bucket; | ||
117 | } | ||
118 | } | ||
119 | |||
120 | return NULL; | ||
121 | } | ||
122 | |||
123 | static int is_idle(struct fd_bo *bo) | ||
124 | { | ||
125 | return fd_bo_cpu_prep(bo, NULL, | ||
126 | DRM_FREEDRENO_PREP_READ | | ||
127 | DRM_FREEDRENO_PREP_WRITE | | ||
128 | DRM_FREEDRENO_PREP_NOSYNC) == 0; | ||
129 | } | ||
130 | |||
131 | static struct fd_bo *find_in_bucket(struct fd_bo_bucket *bucket, uint32_t flags) | ||
132 | { | ||
133 | struct fd_bo *bo = NULL; | ||
134 | |||
135 | /* TODO .. if we had an ALLOC_FOR_RENDER flag like intel, we could | ||
136 | * skip the busy check.. if it is only going to be a render target | ||
137 | * then we probably don't need to stall.. | ||
138 | * | ||
139 | * NOTE that intel takes ALLOC_FOR_RENDER bo's from the list tail | ||
140 | * (MRU, since likely to be in GPU cache), rather than head (LRU).. | ||
141 | */ | ||
142 | pthread_mutex_lock(&table_lock); | ||
143 | if (!LIST_IS_EMPTY(&bucket->list)) { | ||
144 | bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list); | ||
145 | /* TODO check for compatible flags? */ | ||
146 | if (is_idle(bo)) { | ||
147 | list_del(&bo->list); | ||
148 | } else { | ||
149 | bo = NULL; | ||
150 | } | ||
151 | } | ||
152 | pthread_mutex_unlock(&table_lock); | ||
153 | |||
154 | return bo; | ||
155 | } | ||
156 | |||
157 | /* NOTE: size is potentially rounded up to bucket size: */ | ||
158 | drm_private struct fd_bo * | ||
159 | fd_bo_cache_alloc(struct fd_bo_cache *cache, uint32_t *size, uint32_t flags) | ||
160 | { | ||
161 | struct fd_bo *bo = NULL; | ||
162 | struct fd_bo_bucket *bucket; | ||
163 | |||
164 | *size = ALIGN(*size, 4096); | ||
165 | bucket = get_bucket(cache, *size); | ||
166 | |||
167 | /* see if we can be green and recycle: */ | ||
168 | if (bucket) { | ||
169 | *size = bucket->size; | ||
170 | bo = find_in_bucket(bucket, flags); | ||
171 | if (bo) { | ||
172 | atomic_set(&bo->refcnt, 1); | ||
173 | fd_device_ref(bo->dev); | ||
174 | return bo; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | return NULL; | ||
179 | } | ||
180 | |||
181 | drm_private int | ||
182 | fd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo) | ||
183 | { | ||
184 | struct fd_bo_bucket *bucket = get_bucket(cache, bo->size); | ||
185 | |||
186 | /* see if we can be green and recycle: */ | ||
187 | if (bucket) { | ||
188 | struct timespec time; | ||
189 | |||
190 | clock_gettime(CLOCK_MONOTONIC, &time); | ||
191 | |||
192 | bo->free_time = time.tv_sec; | ||
193 | list_addtail(&bo->list, &bucket->list); | ||
194 | fd_bo_cache_cleanup(cache, time.tv_sec); | ||
195 | |||
196 | /* bo's in the bucket cache don't have a ref and | ||
197 | * don't hold a ref to the dev: | ||
198 | */ | ||
199 | fd_device_del_locked(bo->dev); | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | return -1; | ||
205 | } | ||
diff --git a/freedreno/freedreno_device.c b/freedreno/freedreno_device.c index bd57c249..15e41f0e 100644 --- a/freedreno/freedreno_device.c +++ b/freedreno/freedreno_device.c | |||
@@ -42,44 +42,6 @@ static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER; | |||
42 | struct fd_device * kgsl_device_new(int fd); | 42 | struct fd_device * kgsl_device_new(int fd); |
43 | struct fd_device * msm_device_new(int fd); | 43 | struct fd_device * msm_device_new(int fd); |
44 | 44 | ||
45 | static void | ||
46 | add_bucket(struct fd_bo_cache *cache, int size) | ||
47 | { | ||
48 | unsigned int i = cache->num_buckets; | ||
49 | |||
50 | assert(i < ARRAY_SIZE(cache->cache_bucket)); | ||
51 | |||
52 | list_inithead(&cache->cache_bucket[i].list); | ||
53 | cache->cache_bucket[i].size = size; | ||
54 | cache->num_buckets++; | ||
55 | } | ||
56 | |||
57 | drm_private void | ||
58 | fd_bo_cache_init(struct fd_bo_cache *cache) | ||
59 | { | ||
60 | unsigned long size, cache_max_size = 64 * 1024 * 1024; | ||
61 | |||
62 | /* OK, so power of two buckets was too wasteful of memory. | ||
63 | * Give 3 other sizes between each power of two, to hopefully | ||
64 | * cover things accurately enough. (The alternative is | ||
65 | * probably to just go for exact matching of sizes, and assume | ||
66 | * that for things like composited window resize the tiled | ||
67 | * width/height alignment and rounding of sizes to pages will | ||
68 | * get us useful cache hit rates anyway) | ||
69 | */ | ||
70 | add_bucket(cache, 4096); | ||
71 | add_bucket(cache, 4096 * 2); | ||
72 | add_bucket(cache, 4096 * 3); | ||
73 | |||
74 | /* Initialize the linked lists for BO reuse cache. */ | ||
75 | for (size = 4 * 4096; size <= cache_max_size; size *= 2) { | ||
76 | add_bucket(cache, size); | ||
77 | add_bucket(cache, size + size * 1 / 4); | ||
78 | add_bucket(cache, size + size * 2 / 4); | ||
79 | add_bucket(cache, size + size * 3 / 4); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | struct fd_device * fd_device_new(int fd) | 45 | struct fd_device * fd_device_new(int fd) |
84 | { | 46 | { |
85 | struct fd_device *dev; | 47 | struct fd_device *dev; |