1 /*
2 * Copyright © 2022 Collabora, Ltd.
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7 #include <sys/stat.h>
8
9 #include "detect_os.h"
10 #include "string.h"
11 #include "mesa_cache_db_multipart.h"
12 #include "u_debug.h"
13
14 bool
mesa_cache_db_multipart_open(struct mesa_cache_db_multipart * db,const char * cache_path)15 mesa_cache_db_multipart_open(struct mesa_cache_db_multipart *db,
16 const char *cache_path)
17 {
18 #if DETECT_OS_WINDOWS
19 return false;
20 #else
21 char *part_path = NULL;
22 unsigned int i;
23
24 db->num_parts = debug_get_num_option("MESA_DISK_CACHE_DATABASE_NUM_PARTS", 50);
25
26 db->parts = calloc(db->num_parts, sizeof(*db->parts));
27 if (!db->parts)
28 return false;
29
30 for (i = 0; i < db->num_parts; i++) {
31 bool db_opened = false;
32
33 if (asprintf(&part_path, "%s/part%u", cache_path, i) == -1)
34 goto close_db;
35
36 if (mkdir(part_path, 0755) == -1 && errno != EEXIST)
37 goto free_path;
38
39 /* DB opening may fail only in a case of a severe problem,
40 * like IO error.
41 */
42 db_opened = mesa_cache_db_open(&db->parts[i], part_path);
43 if (!db_opened)
44 goto free_path;
45
46 free(part_path);
47 }
48
49 /* remove old pre multi-part cache */
50 mesa_db_wipe_path(cache_path);
51
52 return true;
53
54 free_path:
55 free(part_path);
56 close_db:
57 while (i--)
58 mesa_cache_db_close(&db->parts[i]);
59
60 free(db->parts);
61
62 return false;
63 #endif
64 }
65
66 void
mesa_cache_db_multipart_close(struct mesa_cache_db_multipart * db)67 mesa_cache_db_multipart_close(struct mesa_cache_db_multipart *db)
68 {
69 while (db->num_parts--)
70 mesa_cache_db_close(&db->parts[db->num_parts]);
71
72 free(db->parts);
73 }
74
75 void
mesa_cache_db_multipart_set_size_limit(struct mesa_cache_db_multipart * db,uint64_t max_cache_size)76 mesa_cache_db_multipart_set_size_limit(struct mesa_cache_db_multipart *db,
77 uint64_t max_cache_size)
78 {
79 for (unsigned int i = 0; i < db->num_parts; i++)
80 mesa_cache_db_set_size_limit(&db->parts[i],
81 max_cache_size / db->num_parts);
82 }
83
84 void *
mesa_cache_db_multipart_read_entry(struct mesa_cache_db_multipart * db,const uint8_t * cache_key_160bit,size_t * size)85 mesa_cache_db_multipart_read_entry(struct mesa_cache_db_multipart *db,
86 const uint8_t *cache_key_160bit,
87 size_t *size)
88 {
89 unsigned last_read_part = db->last_read_part;
90
91 for (unsigned int i = 0; i < db->num_parts; i++) {
92 unsigned int part = (last_read_part + i) % db->num_parts;
93
94 void *cache_item = mesa_cache_db_read_entry(&db->parts[part],
95 cache_key_160bit, size);
96 if (cache_item) {
97 /* Likely that the next entry lookup will hit the same DB part. */
98 db->last_read_part = part;
99 return cache_item;
100 }
101 }
102
103 return NULL;
104 }
105
106 static unsigned
mesa_cache_db_multipart_select_victim_part(struct mesa_cache_db_multipart * db)107 mesa_cache_db_multipart_select_victim_part(struct mesa_cache_db_multipart *db)
108 {
109 double best_score = 0, score;
110 unsigned victim = 0;
111
112 for (unsigned int i = 0; i < db->num_parts; i++) {
113 score = mesa_cache_db_eviction_score(&db->parts[i]);
114 if (score > best_score) {
115 best_score = score;
116 victim = i;
117 }
118 }
119
120 return victim;
121 }
122
123 bool
mesa_cache_db_multipart_entry_write(struct mesa_cache_db_multipart * db,const uint8_t * cache_key_160bit,const void * blob,size_t blob_size)124 mesa_cache_db_multipart_entry_write(struct mesa_cache_db_multipart *db,
125 const uint8_t *cache_key_160bit,
126 const void *blob, size_t blob_size)
127 {
128 unsigned last_written_part = db->last_written_part;
129 int wpart = -1;
130
131 for (unsigned int i = 0; i < db->num_parts; i++) {
132 unsigned int part = (last_written_part + i) % db->num_parts;
133
134 /* Note that each DB part has own locking. */
135 if (mesa_cache_db_has_space(&db->parts[part], blob_size)) {
136 wpart = part;
137 break;
138 }
139 }
140
141 /* All DB parts are full. Writing to a full DB part will auto-trigger
142 * eviction of LRU cache entries from the part. Select DB part that
143 * contains majority of LRU cache entries.
144 */
145 if (wpart < 0)
146 wpart = mesa_cache_db_multipart_select_victim_part(db);
147
148 db->last_written_part = wpart;
149
150 return mesa_cache_db_entry_write(&db->parts[wpart], cache_key_160bit,
151 blob, blob_size);
152 }
153
154 void
mesa_cache_db_multipart_entry_remove(struct mesa_cache_db_multipart * db,const uint8_t * cache_key_160bit)155 mesa_cache_db_multipart_entry_remove(struct mesa_cache_db_multipart *db,
156 const uint8_t *cache_key_160bit)
157 {
158 for (unsigned int i = 0; i < db->num_parts; i++)
159 mesa_cache_db_entry_remove(&db->parts[i], cache_key_160bit);
160 }
161