• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "vn_renderer_internal.h"
7 
8 /* 3 seconds */
9 #define VN_RENDERER_SHMEM_CACHE_EXPIRACY (3ll * 1000 * 1000)
10 
11 void
vn_renderer_shmem_cache_init(struct vn_renderer_shmem_cache * cache,struct vn_renderer * renderer,vn_renderer_shmem_cache_destroy_func destroy_func)12 vn_renderer_shmem_cache_init(struct vn_renderer_shmem_cache *cache,
13                              struct vn_renderer *renderer,
14                              vn_renderer_shmem_cache_destroy_func destroy_func)
15 {
16    /* cache->bucket_mask is 32-bit and u_bit_scan is used */
17    static_assert(ARRAY_SIZE(cache->buckets) <= 32, "");
18 
19    cache->renderer = renderer;
20    cache->destroy_func = destroy_func;
21 
22    simple_mtx_init(&cache->mutex, mtx_plain);
23 
24    for (uint32_t i = 0; i < ARRAY_SIZE(cache->buckets); i++) {
25       struct vn_renderer_shmem_bucket *bucket = &cache->buckets[i];
26       list_inithead(&bucket->shmems);
27    }
28 
29    cache->initialized = true;
30 }
31 
32 void
vn_renderer_shmem_cache_fini(struct vn_renderer_shmem_cache * cache)33 vn_renderer_shmem_cache_fini(struct vn_renderer_shmem_cache *cache)
34 {
35    if (!cache->initialized)
36       return;
37 
38    while (cache->bucket_mask) {
39       const int idx = u_bit_scan(&cache->bucket_mask);
40       struct vn_renderer_shmem_bucket *bucket = &cache->buckets[idx];
41 
42       list_for_each_entry_safe(struct vn_renderer_shmem, shmem,
43                                &bucket->shmems, cache_head)
44          cache->destroy_func(cache->renderer, shmem);
45    }
46 
47    simple_mtx_destroy(&cache->mutex);
48 }
49 
50 static struct vn_renderer_shmem_bucket *
choose_bucket(struct vn_renderer_shmem_cache * cache,size_t size,int * out_idx)51 choose_bucket(struct vn_renderer_shmem_cache *cache,
52               size_t size,
53               int *out_idx)
54 {
55    assert(size);
56    if (unlikely(!util_is_power_of_two_or_zero64(size)))
57       return NULL;
58 
59    const uint32_t idx = ffsll(size) - 1;
60    if (unlikely(idx >= ARRAY_SIZE(cache->buckets)))
61       return NULL;
62 
63    *out_idx = idx;
64    return &cache->buckets[idx];
65 }
66 
67 static void
vn_renderer_shmem_cache_remove_expired_locked(struct vn_renderer_shmem_cache * cache,int64_t now)68 vn_renderer_shmem_cache_remove_expired_locked(
69    struct vn_renderer_shmem_cache *cache, int64_t now)
70 {
71    uint32_t bucket_mask = cache->bucket_mask;
72    while (bucket_mask) {
73       const int idx = u_bit_scan(&bucket_mask);
74       struct vn_renderer_shmem_bucket *bucket = &cache->buckets[idx];
75 
76       assert(!list_is_empty(&bucket->shmems));
77       const struct vn_renderer_shmem *last_shmem = list_last_entry(
78          &bucket->shmems, struct vn_renderer_shmem, cache_head);
79 
80       /* remove expired shmems but keep at least the last one */
81       list_for_each_entry_safe(struct vn_renderer_shmem, shmem,
82                                &bucket->shmems, cache_head) {
83          if (shmem == last_shmem ||
84              now - shmem->cache_timestamp < VN_RENDERER_SHMEM_CACHE_EXPIRACY)
85             break;
86 
87          list_del(&shmem->cache_head);
88          cache->destroy_func(cache->renderer, shmem);
89       }
90    }
91 }
92 
93 bool
vn_renderer_shmem_cache_add(struct vn_renderer_shmem_cache * cache,struct vn_renderer_shmem * shmem)94 vn_renderer_shmem_cache_add(struct vn_renderer_shmem_cache *cache,
95                             struct vn_renderer_shmem *shmem)
96 {
97    assert(!vn_refcount_is_valid(&shmem->refcount));
98 
99    int idx;
100    struct vn_renderer_shmem_bucket *bucket =
101       choose_bucket(cache, shmem->mmap_size, &idx);
102    if (!bucket)
103       return false;
104 
105    const int64_t now = os_time_get();
106    shmem->cache_timestamp = now;
107 
108    simple_mtx_lock(&cache->mutex);
109 
110    vn_renderer_shmem_cache_remove_expired_locked(cache, now);
111 
112    list_addtail(&shmem->cache_head, &bucket->shmems);
113    cache->bucket_mask |= 1 << idx;
114 
115    simple_mtx_unlock(&cache->mutex);
116 
117    return true;
118 }
119 
120 struct vn_renderer_shmem *
vn_renderer_shmem_cache_get(struct vn_renderer_shmem_cache * cache,size_t size)121 vn_renderer_shmem_cache_get(struct vn_renderer_shmem_cache *cache,
122                             size_t size)
123 {
124    int idx;
125    struct vn_renderer_shmem_bucket *bucket = choose_bucket(cache, size, &idx);
126    if (!bucket) {
127       VN_TRACE_SCOPE("shmem cache skip");
128       simple_mtx_lock(&cache->mutex);
129       cache->debug.cache_skip_count++;
130       simple_mtx_unlock(&cache->mutex);
131       return NULL;
132    }
133 
134    struct vn_renderer_shmem *shmem = NULL;
135 
136    simple_mtx_lock(&cache->mutex);
137    if (cache->bucket_mask & (1 << idx)) {
138       assert(!list_is_empty(&bucket->shmems));
139       shmem = list_first_entry(&bucket->shmems, struct vn_renderer_shmem,
140                                cache_head);
141       list_del(&shmem->cache_head);
142 
143       if (list_is_empty(&bucket->shmems))
144          cache->bucket_mask &= ~(1 << idx);
145 
146       cache->debug.cache_hit_count++;
147    } else {
148       VN_TRACE_SCOPE("shmem cache miss");
149       cache->debug.cache_miss_count++;
150    }
151    simple_mtx_unlock(&cache->mutex);
152 
153    return shmem;
154 }
155 
156 /* for debugging only */
157 void
vn_renderer_shmem_cache_debug_dump(struct vn_renderer_shmem_cache * cache)158 vn_renderer_shmem_cache_debug_dump(struct vn_renderer_shmem_cache *cache)
159 {
160    simple_mtx_lock(&cache->mutex);
161 
162    vn_log(NULL, "dumping shmem cache");
163    vn_log(NULL, "  cache skip: %d", cache->debug.cache_skip_count);
164    vn_log(NULL, "  cache hit: %d", cache->debug.cache_hit_count);
165    vn_log(NULL, "  cache miss: %d", cache->debug.cache_miss_count);
166 
167    uint32_t bucket_mask = cache->bucket_mask;
168    while (bucket_mask) {
169       const int idx = u_bit_scan(&bucket_mask);
170       const struct vn_renderer_shmem_bucket *bucket = &cache->buckets[idx];
171       uint32_t count = 0;
172       list_for_each_entry(struct vn_renderer_shmem, shmem, &bucket->shmems,
173                           cache_head)
174          count++;
175       if (count)
176          vn_log(NULL, "  buckets[%d]: %d shmems", idx, count);
177    }
178 
179    simple_mtx_unlock(&cache->mutex);
180 }
181