2 * Copyright 2019 Collabora, Ltd.
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
23 * Authors (Collabora):
24 * Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
28 #include "drm-uapi/panfrost_drm.h"
30 #include "pan_screen.h"
31 #include "util/u_math.h"
33 /* This file implements a userspace BO cache. Allocating and freeing
34 * GPU-visible buffers is very expensive, and even the extra kernel roundtrips
35 * adds more work than we would like at this point. So caching BOs in userspace
36 * solves both of these problems and does not require kernel updates.
38 * Cached BOs are sorted into a bucket based on rounding their size down to the
39 * nearest power-of-two. Each bucket contains a linked list of free panfrost_bo
40 * objects. Putting a BO into the cache is accomplished by adding it to the
41 * corresponding bucket. Getting a BO from the cache consists of finding the
42 * appropriate bucket and sorting. A cache eviction is a kernel-level free of a
43 * BO and removing it from the bucket. We special case evicting all BOs from
44 * the cache, since that's what helpful in practice and avoids extra logic
45 * around the linked list.
48 /* Helper to calculate the bucket index of a BO */
51 pan_bucket_index(unsigned size
)
53 /* Round down to POT to compute a bucket index */
55 unsigned bucket_index
= util_logbase2(size
);
57 /* Clamp the bucket index; all huge allocations will be
58 * sorted into the largest bucket */
60 bucket_index
= MIN2(bucket_index
, MAX_BO_CACHE_BUCKET
);
62 /* The minimum bucket size must equal the minimum allocation
63 * size; the maximum we clamped */
65 assert(bucket_index
>= MIN_BO_CACHE_BUCKET
);
66 assert(bucket_index
<= MAX_BO_CACHE_BUCKET
);
69 return (bucket_index
- MIN_BO_CACHE_BUCKET
);
72 static struct list_head
*
73 pan_bucket(struct panfrost_screen
*screen
, unsigned size
)
75 return &screen
->bo_cache
[pan_bucket_index(size
)];
78 /* Tries to fetch a BO of sufficient size with the appropriate flags from the
79 * BO cache. If it succeeds, it returns that BO and removes the BO from the
80 * cache. If it fails, it returns NULL signaling the caller to allocate a new
84 panfrost_bo_cache_fetch(
85 struct panfrost_screen
*screen
,
86 size_t size
, uint32_t flags
)
88 pthread_mutex_lock(&screen
->bo_cache_lock
);
89 struct list_head
*bucket
= pan_bucket(screen
, size
);
90 struct panfrost_bo
*bo
= NULL
;
92 /* Iterate the bucket looking for something suitable */
93 list_for_each_entry_safe(struct panfrost_bo
, entry
, bucket
, link
) {
94 if (entry
->size
>= size
&&
95 entry
->flags
== flags
) {
97 struct drm_panfrost_madvise madv
;
99 /* This one works, splice it out of the cache */
100 list_del(&entry
->link
);
102 madv
.handle
= entry
->gem_handle
;
103 madv
.madv
= PANFROST_MADV_WILLNEED
;
106 ret
= drmIoctl(screen
->fd
, DRM_IOCTL_PANFROST_MADVISE
, &madv
);
107 if (!ret
&& !madv
.retained
) {
108 panfrost_drm_release_bo(screen
, entry
, false);
116 pthread_mutex_unlock(&screen
->bo_cache_lock
);
121 /* Tries to add a BO to the cache. Returns if it was
125 panfrost_bo_cache_put(
126 struct panfrost_screen
*screen
,
127 struct panfrost_bo
*bo
)
129 pthread_mutex_lock(&screen
->bo_cache_lock
);
130 struct list_head
*bucket
= pan_bucket(screen
, bo
->size
);
131 struct drm_panfrost_madvise madv
;
133 madv
.handle
= bo
->gem_handle
;
134 madv
.madv
= PANFROST_MADV_DONTNEED
;
137 drmIoctl(screen
->fd
, DRM_IOCTL_PANFROST_MADVISE
, &madv
);
139 /* Add us to the bucket */
140 list_addtail(&bo
->link
, bucket
);
141 pthread_mutex_unlock(&screen
->bo_cache_lock
);
146 /* Evicts all BOs from the cache. Called during context
147 * destroy or during low-memory situations (to free up
148 * memory that may be unused by us just sitting in our
149 * cache, but still reserved from the perspective of the
153 panfrost_bo_cache_evict_all(
154 struct panfrost_screen
*screen
)
156 pthread_mutex_lock(&screen
->bo_cache_lock
);
157 for (unsigned i
= 0; i
< ARRAY_SIZE(screen
->bo_cache
); ++i
) {
158 struct list_head
*bucket
= &screen
->bo_cache
[i
];
160 list_for_each_entry_safe(struct panfrost_bo
, entry
, bucket
, link
) {
161 list_del(&entry
->link
);
162 panfrost_drm_release_bo(screen
, entry
, false);
165 pthread_mutex_unlock(&screen
->bo_cache_lock
);