ec46e16e9e9780c0f4c162df28c75fe7c2c361fb
[mesa.git] / src / gallium / drivers / freedreno / drm / freedreno_bo.c
1 /*
2 * Copyright (C) 2012-2018 Rob Clark <robclark@freedesktop.org>
3 *
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:
10 *
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
13 * Software.
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 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
21 * SOFTWARE.
22 *
23 * Authors:
24 * Rob Clark <robclark@freedesktop.org>
25 */
26
27 #include "os/os_mman.h"
28
29 #include "freedreno_drmif.h"
30 #include "freedreno_priv.h"
31
32 pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
33 void bo_del(struct fd_bo *bo);
34
35 /* set buffer name, and add to table, call w/ table_lock held: */
36 static void set_name(struct fd_bo *bo, uint32_t name)
37 {
38 bo->name = name;
39 /* add ourself into the handle table: */
40 _mesa_hash_table_insert(bo->dev->name_table, &bo->name, bo);
41 }
42
43 /* lookup a buffer, call w/ table_lock held: */
44 static struct fd_bo * lookup_bo(struct hash_table *tbl, uint32_t key)
45 {
46 struct fd_bo *bo = NULL;
47 struct hash_entry *entry = _mesa_hash_table_search(tbl, &key);
48 if (entry) {
49 /* found, incr refcnt and return: */
50 bo = fd_bo_ref(entry->data);
51
52 /* don't break the bucket if this bo was found in one */
53 list_delinit(&bo->list);
54 }
55 return bo;
56 }
57
58 /* allocate a new buffer object, call w/ table_lock held */
59 static struct fd_bo * bo_from_handle(struct fd_device *dev,
60 uint32_t size, uint32_t handle)
61 {
62 struct fd_bo *bo;
63
64 bo = dev->funcs->bo_from_handle(dev, size, handle);
65 if (!bo) {
66 struct drm_gem_close req = {
67 .handle = handle,
68 };
69 drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
70 return NULL;
71 }
72 bo->dev = fd_device_ref(dev);
73 bo->size = size;
74 bo->handle = handle;
75 p_atomic_set(&bo->refcnt, 1);
76 list_inithead(&bo->list);
77 /* add ourself into the handle table: */
78 _mesa_hash_table_insert(dev->handle_table, &bo->handle, bo);
79 return bo;
80 }
81
82 static struct fd_bo *
83 bo_new(struct fd_device *dev, uint32_t size, uint32_t flags,
84 struct fd_bo_cache *cache)
85 {
86 struct fd_bo *bo = NULL;
87 uint32_t handle;
88 int ret;
89
90 bo = fd_bo_cache_alloc(cache, &size, flags);
91 if (bo)
92 return bo;
93
94 ret = dev->funcs->bo_new_handle(dev, size, flags, &handle);
95 if (ret)
96 return NULL;
97
98 pthread_mutex_lock(&table_lock);
99 bo = bo_from_handle(dev, size, handle);
100 pthread_mutex_unlock(&table_lock);
101
102 VG_BO_ALLOC(bo);
103
104 return bo;
105 }
106
107 struct fd_bo *
108 fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
109 {
110 struct fd_bo *bo = bo_new(dev, size, flags, &dev->bo_cache);
111 if (bo)
112 bo->bo_reuse = BO_CACHE;
113 return bo;
114 }
115
116 /* internal function to allocate bo's that use the ringbuffer cache
117 * instead of the normal bo_cache. The purpose is, because cmdstream
118 * bo's get vmap'd on the kernel side, and that is expensive, we want
119 * to re-use cmdstream bo's for cmdstream and not unrelated purposes.
120 */
121 struct fd_bo *
122 fd_bo_new_ring(struct fd_device *dev, uint32_t size, uint32_t flags)
123 {
124 struct fd_bo *bo = bo_new(dev, size, flags, &dev->ring_cache);
125 if (bo)
126 bo->bo_reuse = RING_CACHE;
127 return bo;
128 }
129
130 struct fd_bo *
131 fd_bo_from_handle(struct fd_device *dev, uint32_t handle, uint32_t size)
132 {
133 struct fd_bo *bo = NULL;
134
135 pthread_mutex_lock(&table_lock);
136
137 bo = lookup_bo(dev->handle_table, handle);
138 if (bo)
139 goto out_unlock;
140
141 bo = bo_from_handle(dev, size, handle);
142
143 VG_BO_ALLOC(bo);
144
145 out_unlock:
146 pthread_mutex_unlock(&table_lock);
147
148 return bo;
149 }
150
151 struct fd_bo *
152 fd_bo_from_dmabuf(struct fd_device *dev, int fd)
153 {
154 int ret, size;
155 uint32_t handle;
156 struct fd_bo *bo;
157
158 pthread_mutex_lock(&table_lock);
159 ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
160 if (ret) {
161 pthread_mutex_unlock(&table_lock);
162 return NULL;
163 }
164
165 bo = lookup_bo(dev->handle_table, handle);
166 if (bo)
167 goto out_unlock;
168
169 /* lseek() to get bo size */
170 size = lseek(fd, 0, SEEK_END);
171 lseek(fd, 0, SEEK_CUR);
172
173 bo = bo_from_handle(dev, size, handle);
174
175 VG_BO_ALLOC(bo);
176
177 out_unlock:
178 pthread_mutex_unlock(&table_lock);
179
180 return bo;
181 }
182
183 struct fd_bo * fd_bo_from_name(struct fd_device *dev, uint32_t name)
184 {
185 struct drm_gem_open req = {
186 .name = name,
187 };
188 struct fd_bo *bo;
189
190 pthread_mutex_lock(&table_lock);
191
192 /* check name table first, to see if bo is already open: */
193 bo = lookup_bo(dev->name_table, name);
194 if (bo)
195 goto out_unlock;
196
197 if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
198 ERROR_MSG("gem-open failed: %s", strerror(errno));
199 goto out_unlock;
200 }
201
202 bo = lookup_bo(dev->handle_table, req.handle);
203 if (bo)
204 goto out_unlock;
205
206 bo = bo_from_handle(dev, req.size, req.handle);
207 if (bo) {
208 set_name(bo, name);
209 VG_BO_ALLOC(bo);
210 }
211
212 out_unlock:
213 pthread_mutex_unlock(&table_lock);
214
215 return bo;
216 }
217
218 uint64_t fd_bo_get_iova(struct fd_bo *bo)
219 {
220 if (!bo->iova)
221 bo->iova = bo->funcs->iova(bo);
222 return bo->iova;
223 }
224
225 void fd_bo_put_iova(struct fd_bo *bo)
226 {
227 /* currently a no-op */
228 }
229
230 struct fd_bo * fd_bo_ref(struct fd_bo *bo)
231 {
232 p_atomic_inc(&bo->refcnt);
233 return bo;
234 }
235
236 void fd_bo_del(struct fd_bo *bo)
237 {
238 struct fd_device *dev = bo->dev;
239
240 if (!atomic_dec_and_test(&bo->refcnt))
241 return;
242
243 pthread_mutex_lock(&table_lock);
244
245 if ((bo->bo_reuse == BO_CACHE) && (fd_bo_cache_free(&dev->bo_cache, bo) == 0))
246 goto out;
247 if ((bo->bo_reuse == RING_CACHE) && (fd_bo_cache_free(&dev->ring_cache, bo) == 0))
248 goto out;
249
250 bo_del(bo);
251 fd_device_del_locked(dev);
252 out:
253 pthread_mutex_unlock(&table_lock);
254 }
255
256 /* Called under table_lock */
257 void bo_del(struct fd_bo *bo)
258 {
259 VG_BO_FREE(bo);
260
261 if (bo->map)
262 os_munmap(bo->map, bo->size);
263
264 /* TODO probably bo's in bucket list get removed from
265 * handle table??
266 */
267
268 if (bo->handle) {
269 struct drm_gem_close req = {
270 .handle = bo->handle,
271 };
272 _mesa_hash_table_remove_key(bo->dev->handle_table, &bo->handle);
273 if (bo->name)
274 _mesa_hash_table_remove_key(bo->dev->name_table, &bo->name);
275 drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
276 }
277
278 bo->funcs->destroy(bo);
279 }
280
281 int fd_bo_get_name(struct fd_bo *bo, uint32_t *name)
282 {
283 if (!bo->name) {
284 struct drm_gem_flink req = {
285 .handle = bo->handle,
286 };
287 int ret;
288
289 ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
290 if (ret) {
291 return ret;
292 }
293
294 pthread_mutex_lock(&table_lock);
295 set_name(bo, req.name);
296 pthread_mutex_unlock(&table_lock);
297 bo->bo_reuse = NO_CACHE;
298 }
299
300 *name = bo->name;
301
302 return 0;
303 }
304
305 uint32_t fd_bo_handle(struct fd_bo *bo)
306 {
307 return bo->handle;
308 }
309
310 int fd_bo_dmabuf(struct fd_bo *bo)
311 {
312 int ret, prime_fd;
313
314 ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
315 &prime_fd);
316 if (ret) {
317 ERROR_MSG("failed to get dmabuf fd: %d", ret);
318 return ret;
319 }
320
321 bo->bo_reuse = NO_CACHE;
322
323 return prime_fd;
324 }
325
326 uint32_t fd_bo_size(struct fd_bo *bo)
327 {
328 return bo->size;
329 }
330
331 void * fd_bo_map(struct fd_bo *bo)
332 {
333 if (!bo->map) {
334 uint64_t offset;
335 int ret;
336
337 ret = bo->funcs->offset(bo, &offset);
338 if (ret) {
339 return NULL;
340 }
341
342 bo->map = os_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
343 bo->dev->fd, offset);
344 if (bo->map == MAP_FAILED) {
345 ERROR_MSG("mmap failed: %s", strerror(errno));
346 bo->map = NULL;
347 }
348 }
349 return bo->map;
350 }
351
352 /* a bit odd to take the pipe as an arg, but it's a, umm, quirk of kgsl.. */
353 int fd_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
354 {
355 return bo->funcs->cpu_prep(bo, pipe, op);
356 }
357
358 void fd_bo_cpu_fini(struct fd_bo *bo)
359 {
360 bo->funcs->cpu_fini(bo);
361 }