fix compile error on 32bit systems
[platform/upstream/libdrm.git] / freedreno / freedreno_bo.c
1 /* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
2
3 /*
4  * Copyright (C) 2012 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 static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
37
38 static void bo_del(struct fd_bo *bo);
39
40 /* set buffer name, and add to table, call w/ table_lock held: */
41 static void set_name(struct fd_bo *bo, uint32_t name)
42 {
43         bo->name = name;
44         /* add ourself into the handle table: */
45         drmHashInsert(bo->dev->name_table, name, bo);
46 }
47
48 /* lookup a buffer, call w/ table_lock held: */
49 static struct fd_bo * lookup_bo(void *tbl, uint32_t key)
50 {
51         struct fd_bo *bo = NULL;
52         if (!drmHashLookup(tbl, key, (void **)&bo)) {
53                 /* found, incr refcnt and return: */
54                 bo = fd_bo_ref(bo);
55         }
56         return bo;
57 }
58
59 /* allocate a new buffer object, call w/ table_lock held */
60 static struct fd_bo * bo_from_handle(struct fd_device *dev,
61                 uint32_t size, uint32_t handle)
62 {
63         struct fd_bo *bo;
64
65         bo = dev->funcs->bo_from_handle(dev, size, handle);
66         if (!bo) {
67                 struct drm_gem_close req = {
68                                 .handle = handle,
69                 };
70                 drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
71                 return NULL;
72         }
73         bo->dev = fd_device_ref(dev);
74         bo->size = size;
75         bo->handle = handle;
76         atomic_set(&bo->refcnt, 1);
77         list_inithead(&bo->list);
78         /* add ourself into the handle table: */
79         drmHashInsert(dev->handle_table, handle, bo);
80         return bo;
81 }
82
83 /* Frees older cached buffers.  Called under table_lock */
84 void fd_cleanup_bo_cache(struct fd_device *dev, time_t time)
85 {
86         int i;
87
88         if (dev->time == time)
89                 return;
90
91         for (i = 0; i < dev->num_buckets; i++) {
92                 struct fd_bo_bucket *bucket = &dev->cache_bucket[i];
93                 struct fd_bo *bo;
94
95                 while (!LIST_IS_EMPTY(&bucket->list)) {
96                         bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list);
97
98                         /* keep things in cache for at least 1 second: */
99                         if (time && ((time - bo->free_time) <= 1))
100                                 break;
101
102                         list_del(&bo->list);
103                         bo_del(bo);
104                 }
105         }
106
107         dev->time = time;
108 }
109
110 static struct fd_bo_bucket * get_bucket(struct fd_device *dev, uint32_t size)
111 {
112         int i;
113
114         /* hmm, this is what intel does, but I suppose we could calculate our
115          * way to the correct bucket size rather than looping..
116          */
117         for (i = 0; i < dev->num_buckets; i++) {
118                 struct fd_bo_bucket *bucket = &dev->cache_bucket[i];
119                 if (bucket->size >= size) {
120                         return bucket;
121                 }
122         }
123
124         return NULL;
125 }
126
127 static int is_idle(struct fd_bo *bo)
128 {
129         return fd_bo_cpu_prep(bo, NULL,
130                         DRM_FREEDRENO_PREP_READ |
131                         DRM_FREEDRENO_PREP_WRITE |
132                         DRM_FREEDRENO_PREP_NOSYNC) == 0;
133 }
134
135 static struct fd_bo *find_in_bucket(struct fd_device *dev,
136                 struct fd_bo_bucket *bucket, uint32_t flags)
137 {
138         struct fd_bo *bo = NULL;
139
140         /* TODO .. if we had an ALLOC_FOR_RENDER flag like intel, we could
141          * skip the busy check.. if it is only going to be a render target
142          * then we probably don't need to stall..
143          *
144          * NOTE that intel takes ALLOC_FOR_RENDER bo's from the list tail
145          * (MRU, since likely to be in GPU cache), rather than head (LRU)..
146          */
147         pthread_mutex_lock(&table_lock);
148         while (!LIST_IS_EMPTY(&bucket->list)) {
149                 bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list);
150                 if (0 /* TODO: if madvise tells us bo is gone... */) {
151                         list_del(&bo->list);
152                         bo_del(bo);
153                         bo = NULL;
154                         continue;
155                 }
156                 /* TODO check for compatible flags? */
157                 if (is_idle(bo)) {
158                         list_del(&bo->list);
159                         break;
160                 }
161                 bo = NULL;
162                 break;
163         }
164         pthread_mutex_unlock(&table_lock);
165
166         return bo;
167 }
168
169
170 drm_public struct fd_bo *
171 fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
172 {
173         struct fd_bo *bo = NULL;
174         struct fd_bo_bucket *bucket;
175         uint32_t handle;
176         int ret;
177
178         size = ALIGN(size, 4096);
179         bucket = get_bucket(dev, size);
180
181         /* see if we can be green and recycle: */
182         if (bucket) {
183                 size = bucket->size;
184                 bo = find_in_bucket(dev, bucket, flags);
185                 if (bo) {
186                         atomic_set(&bo->refcnt, 1);
187                         fd_device_ref(bo->dev);
188                         return bo;
189                 }
190         }
191
192         ret = dev->funcs->bo_new_handle(dev, size, flags, &handle);
193         if (ret)
194                 return NULL;
195
196         pthread_mutex_lock(&table_lock);
197         bo = bo_from_handle(dev, size, handle);
198         bo->bo_reuse = 1;
199         pthread_mutex_unlock(&table_lock);
200
201         return bo;
202 }
203
204 drm_public struct fd_bo *
205 fd_bo_from_handle(struct fd_device *dev, uint32_t handle, uint32_t size)
206 {
207         struct fd_bo *bo = NULL;
208
209         pthread_mutex_lock(&table_lock);
210         bo = bo_from_handle(dev, size, handle);
211         pthread_mutex_unlock(&table_lock);
212
213         return bo;
214 }
215
216 drm_public struct fd_bo *
217 fd_bo_from_dmabuf(struct fd_device *dev, int fd)
218 {
219         struct drm_prime_handle req = {
220                         .fd = fd,
221         };
222         int ret, size;
223
224         ret = drmIoctl(dev->fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &req);
225         if (ret) {
226                 return NULL;
227         }
228
229         /* hmm, would be nice if we had a way to figure out the size.. */
230         size = 0;
231
232         return fd_bo_from_handle(dev, req.handle, size);
233 }
234
235 drm_public struct fd_bo * fd_bo_from_name(struct fd_device *dev, uint32_t name)
236 {
237         struct drm_gem_open req = {
238                         .name = name,
239         };
240         struct fd_bo *bo;
241
242         pthread_mutex_lock(&table_lock);
243
244         /* check name table first, to see if bo is already open: */
245         bo = lookup_bo(dev->name_table, name);
246         if (bo)
247                 goto out_unlock;
248
249         if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
250                 ERROR_MSG("gem-open failed: %s", strerror(errno));
251                 goto out_unlock;
252         }
253
254         bo = lookup_bo(dev->handle_table, req.handle);
255         if (bo)
256                 goto out_unlock;
257
258         bo = bo_from_handle(dev, req.size, req.handle);
259         if (bo)
260                 set_name(bo, name);
261
262 out_unlock:
263         pthread_mutex_unlock(&table_lock);
264
265         return bo;
266 }
267
268 drm_public struct fd_bo * fd_bo_ref(struct fd_bo *bo)
269 {
270         atomic_inc(&bo->refcnt);
271         return bo;
272 }
273
274 drm_public void fd_bo_del(struct fd_bo *bo)
275 {
276         struct fd_device *dev = bo->dev;
277
278         if (!atomic_dec_and_test(&bo->refcnt))
279                 return;
280
281         if (bo->fd) {
282                 close(bo->fd);
283                 bo->fd = 0;
284         }
285
286         pthread_mutex_lock(&table_lock);
287
288         if (bo->bo_reuse) {
289                 struct fd_bo_bucket *bucket = get_bucket(dev, bo->size);
290
291                 /* see if we can be green and recycle: */
292                 if (bucket) {
293                         struct timespec time;
294
295                         clock_gettime(CLOCK_MONOTONIC, &time);
296
297                         bo->free_time = time.tv_sec;
298                         list_addtail(&bo->list, &bucket->list);
299                         fd_cleanup_bo_cache(dev, time.tv_sec);
300
301                         /* bo's in the bucket cache don't have a ref and
302                          * don't hold a ref to the dev:
303                          */
304
305                         goto out;
306                 }
307         }
308
309         bo_del(bo);
310 out:
311         fd_device_del_locked(dev);
312         pthread_mutex_unlock(&table_lock);
313 }
314
315 /* Called under table_lock */
316 static void bo_del(struct fd_bo *bo)
317 {
318         if (bo->map)
319                 drm_munmap(bo->map, bo->size);
320
321         /* TODO probably bo's in bucket list get removed from
322          * handle table??
323          */
324
325         if (bo->handle) {
326                 struct drm_gem_close req = {
327                                 .handle = bo->handle,
328                 };
329                 drmHashDelete(bo->dev->handle_table, bo->handle);
330                 if (bo->name)
331                         drmHashDelete(bo->dev->name_table, bo->name);
332                 drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
333         }
334
335         bo->funcs->destroy(bo);
336 }
337
338 drm_public int fd_bo_get_name(struct fd_bo *bo, uint32_t *name)
339 {
340         if (!bo->name) {
341                 struct drm_gem_flink req = {
342                                 .handle = bo->handle,
343                 };
344                 int ret;
345
346                 ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
347                 if (ret) {
348                         return ret;
349                 }
350
351                 pthread_mutex_lock(&table_lock);
352                 set_name(bo, req.name);
353                 pthread_mutex_unlock(&table_lock);
354         }
355
356         *name = bo->name;
357
358         return 0;
359 }
360
361 drm_public uint32_t fd_bo_handle(struct fd_bo *bo)
362 {
363         return bo->handle;
364 }
365
366 drm_public int fd_bo_dmabuf(struct fd_bo *bo)
367 {
368         if (!bo->fd) {
369                 struct drm_prime_handle req = {
370                                 .handle = bo->handle,
371                                 .flags = DRM_CLOEXEC,
372                 };
373                 int ret;
374
375                 ret = drmIoctl(bo->dev->fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &req);
376                 if (ret) {
377                         return ret;
378                 }
379
380                 bo->fd = req.fd;
381         }
382         return dup(bo->fd);
383 }
384
385 drm_public uint32_t fd_bo_size(struct fd_bo *bo)
386 {
387         return bo->size;
388 }
389
390 drm_public void * fd_bo_map(struct fd_bo *bo)
391 {
392         if (!bo->map) {
393                 uint64_t offset;
394                 int ret;
395
396                 ret = bo->funcs->offset(bo, &offset);
397                 if (ret) {
398                         return NULL;
399                 }
400
401                 bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
402                                 bo->dev->fd, offset);
403                 if (bo->map == MAP_FAILED) {
404                         ERROR_MSG("mmap failed: %s", strerror(errno));
405                         bo->map = NULL;
406                 }
407         }
408         return bo->map;
409 }
410
411 /* a bit odd to take the pipe as an arg, but it's a, umm, quirk of kgsl.. */
412 drm_public int fd_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
413 {
414         return bo->funcs->cpu_prep(bo, pipe, op);
415 }
416
417 drm_public void fd_bo_cpu_fini(struct fd_bo *bo)
418 {
419         bo->funcs->cpu_fini(bo);
420 }