de8e128ec3f35be5685b82c6e5348603d1b3b1b6
[mesa.git] / src / 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 bo->iova = bo->funcs->iova(bo);
76 bo->flags = FD_RELOC_FLAGS_INIT;
77
78 p_atomic_set(&bo->refcnt, 1);
79 list_inithead(&bo->list);
80 /* add ourself into the handle table: */
81 _mesa_hash_table_insert(dev->handle_table, &bo->handle, bo);
82 return bo;
83 }
84
85 static struct fd_bo *
86 bo_new(struct fd_device *dev, uint32_t size, uint32_t flags,
87 struct fd_bo_cache *cache)
88 {
89 struct fd_bo *bo = NULL;
90 uint32_t handle;
91 int ret;
92
93 bo = fd_bo_cache_alloc(cache, &size, flags);
94 if (bo)
95 return bo;
96
97 ret = dev->funcs->bo_new_handle(dev, size, flags, &handle);
98 if (ret)
99 return NULL;
100
101 pthread_mutex_lock(&table_lock);
102 bo = bo_from_handle(dev, size, handle);
103 pthread_mutex_unlock(&table_lock);
104
105 VG_BO_ALLOC(bo);
106
107 return bo;
108 }
109
110 struct fd_bo *
111 _fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
112 {
113 struct fd_bo *bo = bo_new(dev, size, flags, &dev->bo_cache);
114 if (bo)
115 bo->bo_reuse = BO_CACHE;
116 return bo;
117 }
118
119 void
120 _fd_bo_set_name(struct fd_bo *bo, const char *fmt, va_list ap)
121 {
122 bo->funcs->set_name(bo, fmt, ap);
123 }
124
125 /* internal function to allocate bo's that use the ringbuffer cache
126 * instead of the normal bo_cache. The purpose is, because cmdstream
127 * bo's get vmap'd on the kernel side, and that is expensive, we want
128 * to re-use cmdstream bo's for cmdstream and not unrelated purposes.
129 */
130 struct fd_bo *
131 fd_bo_new_ring(struct fd_device *dev, uint32_t size)
132 {
133 uint32_t flags = DRM_FREEDRENO_GEM_GPUREADONLY;
134 struct fd_bo *bo = bo_new(dev, size, flags, &dev->ring_cache);
135 if (bo)
136 bo->bo_reuse = RING_CACHE;
137 fd_bo_set_name(bo, "cmdstream");
138 return bo;
139 }
140
141 struct fd_bo *
142 fd_bo_from_handle(struct fd_device *dev, uint32_t handle, uint32_t size)
143 {
144 struct fd_bo *bo = NULL;
145
146 pthread_mutex_lock(&table_lock);
147
148 bo = lookup_bo(dev->handle_table, handle);
149 if (bo)
150 goto out_unlock;
151
152 bo = bo_from_handle(dev, size, handle);
153
154 VG_BO_ALLOC(bo);
155
156 out_unlock:
157 pthread_mutex_unlock(&table_lock);
158
159 return bo;
160 }
161
162 struct fd_bo *
163 fd_bo_from_dmabuf(struct fd_device *dev, int fd)
164 {
165 int ret, size;
166 uint32_t handle;
167 struct fd_bo *bo;
168
169 pthread_mutex_lock(&table_lock);
170 ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
171 if (ret) {
172 pthread_mutex_unlock(&table_lock);
173 return NULL;
174 }
175
176 bo = lookup_bo(dev->handle_table, handle);
177 if (bo)
178 goto out_unlock;
179
180 /* lseek() to get bo size */
181 size = lseek(fd, 0, SEEK_END);
182 lseek(fd, 0, SEEK_CUR);
183
184 bo = bo_from_handle(dev, size, handle);
185
186 VG_BO_ALLOC(bo);
187
188 out_unlock:
189 pthread_mutex_unlock(&table_lock);
190
191 return bo;
192 }
193
194 struct fd_bo * fd_bo_from_name(struct fd_device *dev, uint32_t name)
195 {
196 struct drm_gem_open req = {
197 .name = name,
198 };
199 struct fd_bo *bo;
200
201 pthread_mutex_lock(&table_lock);
202
203 /* check name table first, to see if bo is already open: */
204 bo = lookup_bo(dev->name_table, name);
205 if (bo)
206 goto out_unlock;
207
208 if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
209 ERROR_MSG("gem-open failed: %s", strerror(errno));
210 goto out_unlock;
211 }
212
213 bo = lookup_bo(dev->handle_table, req.handle);
214 if (bo)
215 goto out_unlock;
216
217 bo = bo_from_handle(dev, req.size, req.handle);
218 if (bo) {
219 set_name(bo, name);
220 VG_BO_ALLOC(bo);
221 }
222
223 out_unlock:
224 pthread_mutex_unlock(&table_lock);
225
226 return bo;
227 }
228
229 uint64_t fd_bo_get_iova(struct fd_bo *bo)
230 {
231 return bo->iova;
232 }
233
234 struct fd_bo * fd_bo_ref(struct fd_bo *bo)
235 {
236 p_atomic_inc(&bo->refcnt);
237 return bo;
238 }
239
240 void fd_bo_del(struct fd_bo *bo)
241 {
242 struct fd_device *dev = bo->dev;
243
244 if (!atomic_dec_and_test(&bo->refcnt))
245 return;
246
247 pthread_mutex_lock(&table_lock);
248
249 if ((bo->bo_reuse == BO_CACHE) && (fd_bo_cache_free(&dev->bo_cache, bo) == 0))
250 goto out;
251 if ((bo->bo_reuse == RING_CACHE) && (fd_bo_cache_free(&dev->ring_cache, bo) == 0))
252 goto out;
253
254 bo_del(bo);
255 fd_device_del_locked(dev);
256 out:
257 pthread_mutex_unlock(&table_lock);
258 }
259
260 /* Called under table_lock */
261 void bo_del(struct fd_bo *bo)
262 {
263 VG_BO_FREE(bo);
264
265 if (bo->map)
266 os_munmap(bo->map, bo->size);
267
268 /* TODO probably bo's in bucket list get removed from
269 * handle table??
270 */
271
272 if (bo->handle) {
273 struct drm_gem_close req = {
274 .handle = bo->handle,
275 };
276 _mesa_hash_table_remove_key(bo->dev->handle_table, &bo->handle);
277 if (bo->name)
278 _mesa_hash_table_remove_key(bo->dev->name_table, &bo->name);
279 drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
280 }
281
282 bo->funcs->destroy(bo);
283 }
284
285 int fd_bo_get_name(struct fd_bo *bo, uint32_t *name)
286 {
287 if (!bo->name) {
288 struct drm_gem_flink req = {
289 .handle = bo->handle,
290 };
291 int ret;
292
293 ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
294 if (ret) {
295 return ret;
296 }
297
298 pthread_mutex_lock(&table_lock);
299 set_name(bo, req.name);
300 pthread_mutex_unlock(&table_lock);
301 bo->bo_reuse = NO_CACHE;
302 }
303
304 *name = bo->name;
305
306 return 0;
307 }
308
309 uint32_t fd_bo_handle(struct fd_bo *bo)
310 {
311 return bo->handle;
312 }
313
314 int fd_bo_dmabuf(struct fd_bo *bo)
315 {
316 int ret, prime_fd;
317
318 ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
319 &prime_fd);
320 if (ret) {
321 ERROR_MSG("failed to get dmabuf fd: %d", ret);
322 return ret;
323 }
324
325 bo->bo_reuse = NO_CACHE;
326
327 return prime_fd;
328 }
329
330 uint32_t fd_bo_size(struct fd_bo *bo)
331 {
332 return bo->size;
333 }
334
335 void * fd_bo_map(struct fd_bo *bo)
336 {
337 if (!bo->map) {
338 uint64_t offset;
339 int ret;
340
341 ret = bo->funcs->offset(bo, &offset);
342 if (ret) {
343 return NULL;
344 }
345
346 bo->map = os_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
347 bo->dev->fd, offset);
348 if (bo->map == MAP_FAILED) {
349 ERROR_MSG("mmap failed: %s", strerror(errno));
350 bo->map = NULL;
351 }
352 }
353 return bo->map;
354 }
355
356 /* a bit odd to take the pipe as an arg, but it's a, umm, quirk of kgsl.. */
357 int fd_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
358 {
359 return bo->funcs->cpu_prep(bo, pipe, op);
360 }
361
362 void fd_bo_cpu_fini(struct fd_bo *bo)
363 {
364 bo->funcs->cpu_fini(bo);
365 }