1 /*
2 * Copyright 2019 Collabora Ltd.
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 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the 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 NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include "virgl_resource_cache.h"
25 #include "util/os_time.h"
26
27 /* Checks whether the resource represented by a cache entry is able to hold
28 * data of the specified size, bind and format.
29 */
30 static bool
virgl_resource_cache_entry_is_compatible(struct virgl_resource_cache_entry * entry,uint32_t size,uint32_t bind,uint32_t format,uint32_t flags)31 virgl_resource_cache_entry_is_compatible(struct virgl_resource_cache_entry *entry,
32 uint32_t size, uint32_t bind,
33 uint32_t format, uint32_t flags)
34 {
35 return (entry->bind == bind &&
36 entry->format == format &&
37 entry->size >= size &&
38 entry->flags == flags &&
39 /* We don't want to waste space, so don't reuse resource storage to
40 * hold much smaller (< 50%) sizes.
41 */
42 entry->size <= size * 2);
43 }
44
45 static void
virgl_resource_cache_entry_release(struct virgl_resource_cache * cache,struct virgl_resource_cache_entry * entry)46 virgl_resource_cache_entry_release(struct virgl_resource_cache *cache,
47 struct virgl_resource_cache_entry *entry)
48 {
49 list_del(&entry->head);
50 cache->entry_release_func(entry, cache->user_data);
51 }
52
53 static void
virgl_resource_cache_destroy_expired(struct virgl_resource_cache * cache,int64_t now)54 virgl_resource_cache_destroy_expired(struct virgl_resource_cache *cache, int64_t now)
55 {
56 list_for_each_entry_safe(struct virgl_resource_cache_entry,
57 entry, &cache->resources, head) {
58 /* Entries are in non-decreasing timeout order, so we can stop
59 * at the first entry which hasn't expired.
60 */
61 if (!os_time_timeout(entry->timeout_start, entry->timeout_end, now))
62 break;
63 virgl_resource_cache_entry_release(cache, entry);
64 }
65 }
66
67 void
virgl_resource_cache_init(struct virgl_resource_cache * cache,unsigned timeout_usecs,virgl_resource_cache_entry_is_busy_func is_busy_func,virgl_resource_cache_entry_release_func destroy_func,void * user_data)68 virgl_resource_cache_init(struct virgl_resource_cache *cache,
69 unsigned timeout_usecs,
70 virgl_resource_cache_entry_is_busy_func is_busy_func,
71 virgl_resource_cache_entry_release_func destroy_func,
72 void *user_data)
73 {
74 list_inithead(&cache->resources);
75 cache->timeout_usecs = timeout_usecs;
76 cache->entry_is_busy_func = is_busy_func;
77 cache->entry_release_func = destroy_func;
78 cache->user_data = user_data;
79 }
80
81 void
virgl_resource_cache_add(struct virgl_resource_cache * cache,struct virgl_resource_cache_entry * entry)82 virgl_resource_cache_add(struct virgl_resource_cache *cache,
83 struct virgl_resource_cache_entry *entry)
84 {
85 const int64_t now = os_time_get();
86
87 /* Entry should not already be in the cache. */
88 assert(entry->head.next == NULL);
89 assert(entry->head.prev == NULL);
90
91 virgl_resource_cache_destroy_expired(cache, now);
92
93 entry->timeout_start = now;
94 entry->timeout_end = entry->timeout_start + cache->timeout_usecs;
95 list_addtail(&entry->head, &cache->resources);
96 }
97
98 struct virgl_resource_cache_entry *
virgl_resource_cache_remove_compatible(struct virgl_resource_cache * cache,uint32_t size,uint32_t bind,uint32_t format,uint32_t flags)99 virgl_resource_cache_remove_compatible(struct virgl_resource_cache *cache,
100 uint32_t size, uint32_t bind,
101 uint32_t format, uint32_t flags)
102 {
103 const int64_t now = os_time_get();
104 struct virgl_resource_cache_entry *compat_entry = NULL;
105 bool check_expired = true;
106
107 /* Iterate through the cache to find a compatible resource, while also
108 * destroying any expired resources we come across.
109 */
110 list_for_each_entry_safe(struct virgl_resource_cache_entry,
111 entry, &cache->resources, head) {
112 const bool compatible =
113 virgl_resource_cache_entry_is_compatible(entry, size, bind, format,
114 flags);
115
116 if (compatible) {
117 if (!cache->entry_is_busy_func(entry, cache->user_data))
118 compat_entry = entry;
119
120 /* We either have found a compatible resource, in which case we are
121 * done, or the resource is busy, which means resources later in
122 * the cache list will also be busy, so there is no point in
123 * searching further.
124 */
125 break;
126 }
127
128 /* If we aren't using this resource, check to see if it has expired.
129 * Once we have found the first non-expired resource, we can stop checking
130 * since the cache holds resources in non-decreasing timeout order.
131 */
132 if (check_expired) {
133 if (os_time_timeout(entry->timeout_start, entry->timeout_end, now))
134 virgl_resource_cache_entry_release(cache, entry);
135 else
136 check_expired = false;
137 }
138 }
139
140 if (compat_entry)
141 list_del(&compat_entry->head);
142
143 return compat_entry;
144 }
145
146 void
virgl_resource_cache_flush(struct virgl_resource_cache * cache)147 virgl_resource_cache_flush(struct virgl_resource_cache *cache)
148 {
149 list_for_each_entry_safe(struct virgl_resource_cache_entry,
150 entry, &cache->resources, head) {
151 virgl_resource_cache_entry_release(cache, entry);
152 }
153 }
154