1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright © 2020 Valve Corporation 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10bf215546Sopenharmony_ci * 11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13bf215546Sopenharmony_ci * Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21bf215546Sopenharmony_ci * IN THE SOFTWARE. 22bf215546Sopenharmony_ci */ 23bf215546Sopenharmony_ci 24bf215546Sopenharmony_ci/* This is a basic c implementation of a fossilize db like format intended for 25bf215546Sopenharmony_ci * use with the Mesa shader cache. 26bf215546Sopenharmony_ci * 27bf215546Sopenharmony_ci * The format is compatible enough to allow the fossilize db tools to be used 28bf215546Sopenharmony_ci * to do things like merge db collections. 29bf215546Sopenharmony_ci */ 30bf215546Sopenharmony_ci 31bf215546Sopenharmony_ci#include "fossilize_db.h" 32bf215546Sopenharmony_ci 33bf215546Sopenharmony_ci#ifdef FOZ_DB_UTIL 34bf215546Sopenharmony_ci 35bf215546Sopenharmony_ci#include <assert.h> 36bf215546Sopenharmony_ci#include <stddef.h> 37bf215546Sopenharmony_ci#include <stdlib.h> 38bf215546Sopenharmony_ci#include <string.h> 39bf215546Sopenharmony_ci#include <sys/file.h> 40bf215546Sopenharmony_ci#include <sys/types.h> 41bf215546Sopenharmony_ci#include <unistd.h> 42bf215546Sopenharmony_ci 43bf215546Sopenharmony_ci#include "crc32.h" 44bf215546Sopenharmony_ci#include "hash_table.h" 45bf215546Sopenharmony_ci#include "mesa-sha1.h" 46bf215546Sopenharmony_ci#include "ralloc.h" 47bf215546Sopenharmony_ci 48bf215546Sopenharmony_ci#define FOZ_REF_MAGIC_SIZE 16 49bf215546Sopenharmony_ci 50bf215546Sopenharmony_cistatic const uint8_t stream_reference_magic_and_version[FOZ_REF_MAGIC_SIZE] = { 51bf215546Sopenharmony_ci 0x81, 'F', 'O', 'S', 52bf215546Sopenharmony_ci 'S', 'I', 'L', 'I', 53bf215546Sopenharmony_ci 'Z', 'E', 'D', 'B', 54bf215546Sopenharmony_ci 0, 0, 0, FOSSILIZE_FORMAT_VERSION, /* 4 bytes to use for versioning. */ 55bf215546Sopenharmony_ci}; 56bf215546Sopenharmony_ci 57bf215546Sopenharmony_ci/* Mesa uses 160bit hashes to identify cache entries, a hash of this size 58bf215546Sopenharmony_ci * makes collisions virtually impossible for our use case. However the foz db 59bf215546Sopenharmony_ci * format uses a 64bit hash table to lookup file offsets for reading cache 60bf215546Sopenharmony_ci * entries so we must shorten our hash. 61bf215546Sopenharmony_ci */ 62bf215546Sopenharmony_cistatic uint64_t 63bf215546Sopenharmony_citruncate_hash_to_64bits(const uint8_t *cache_key) 64bf215546Sopenharmony_ci{ 65bf215546Sopenharmony_ci uint64_t hash = 0; 66bf215546Sopenharmony_ci unsigned shift = 7; 67bf215546Sopenharmony_ci for (unsigned i = 0; i < 8; i++) { 68bf215546Sopenharmony_ci hash |= ((uint64_t)cache_key[i]) << shift * 8; 69bf215546Sopenharmony_ci shift--; 70bf215546Sopenharmony_ci } 71bf215546Sopenharmony_ci return hash; 72bf215546Sopenharmony_ci} 73bf215546Sopenharmony_ci 74bf215546Sopenharmony_cistatic bool 75bf215546Sopenharmony_cicheck_files_opened_successfully(FILE *file, FILE *db_idx) 76bf215546Sopenharmony_ci{ 77bf215546Sopenharmony_ci if (!file) { 78bf215546Sopenharmony_ci if (db_idx) 79bf215546Sopenharmony_ci fclose(db_idx); 80bf215546Sopenharmony_ci return false; 81bf215546Sopenharmony_ci } 82bf215546Sopenharmony_ci 83bf215546Sopenharmony_ci if (!db_idx) { 84bf215546Sopenharmony_ci if (file) 85bf215546Sopenharmony_ci fclose(file); 86bf215546Sopenharmony_ci return false; 87bf215546Sopenharmony_ci } 88bf215546Sopenharmony_ci 89bf215546Sopenharmony_ci return true; 90bf215546Sopenharmony_ci} 91bf215546Sopenharmony_ci 92bf215546Sopenharmony_cistatic bool 93bf215546Sopenharmony_cicreate_foz_db_filenames(char *cache_path, char *name, char **filename, 94bf215546Sopenharmony_ci char **idx_filename) 95bf215546Sopenharmony_ci{ 96bf215546Sopenharmony_ci if (asprintf(filename, "%s/%s.foz", cache_path, name) == -1) 97bf215546Sopenharmony_ci return false; 98bf215546Sopenharmony_ci 99bf215546Sopenharmony_ci if (asprintf(idx_filename, "%s/%s_idx.foz", cache_path, name) == -1) { 100bf215546Sopenharmony_ci free(*filename); 101bf215546Sopenharmony_ci return false; 102bf215546Sopenharmony_ci } 103bf215546Sopenharmony_ci 104bf215546Sopenharmony_ci return true; 105bf215546Sopenharmony_ci} 106bf215546Sopenharmony_ci 107bf215546Sopenharmony_ci 108bf215546Sopenharmony_ci/* This looks at stuff that was added to the index since the last time we looked at it. This is safe 109bf215546Sopenharmony_ci * to do without locking the file as we assume the file is append only */ 110bf215546Sopenharmony_cistatic void 111bf215546Sopenharmony_ciupdate_foz_index(struct foz_db *foz_db, FILE *db_idx, unsigned file_idx) 112bf215546Sopenharmony_ci{ 113bf215546Sopenharmony_ci uint64_t offset = ftell(db_idx); 114bf215546Sopenharmony_ci fseek(db_idx, 0, SEEK_END); 115bf215546Sopenharmony_ci uint64_t len = ftell(db_idx); 116bf215546Sopenharmony_ci uint64_t parsed_offset = offset; 117bf215546Sopenharmony_ci 118bf215546Sopenharmony_ci if (offset == len) 119bf215546Sopenharmony_ci return; 120bf215546Sopenharmony_ci 121bf215546Sopenharmony_ci fseek(db_idx, offset, SEEK_SET); 122bf215546Sopenharmony_ci while (offset < len) { 123bf215546Sopenharmony_ci char bytes_to_read[FOSSILIZE_BLOB_HASH_LENGTH + sizeof(struct foz_payload_header)]; 124bf215546Sopenharmony_ci struct foz_payload_header *header; 125bf215546Sopenharmony_ci 126bf215546Sopenharmony_ci /* Corrupt entry. Our process might have been killed before we 127bf215546Sopenharmony_ci * could write all data. 128bf215546Sopenharmony_ci */ 129bf215546Sopenharmony_ci if (offset + sizeof(bytes_to_read) > len) 130bf215546Sopenharmony_ci break; 131bf215546Sopenharmony_ci 132bf215546Sopenharmony_ci /* NAME + HEADER in one read */ 133bf215546Sopenharmony_ci if (fread(bytes_to_read, 1, sizeof(bytes_to_read), db_idx) != 134bf215546Sopenharmony_ci sizeof(bytes_to_read)) 135bf215546Sopenharmony_ci break; 136bf215546Sopenharmony_ci 137bf215546Sopenharmony_ci offset += sizeof(bytes_to_read); 138bf215546Sopenharmony_ci header = (struct foz_payload_header*)&bytes_to_read[FOSSILIZE_BLOB_HASH_LENGTH]; 139bf215546Sopenharmony_ci 140bf215546Sopenharmony_ci /* Corrupt entry. Our process might have been killed before we 141bf215546Sopenharmony_ci * could write all data. 142bf215546Sopenharmony_ci */ 143bf215546Sopenharmony_ci if (offset + header->payload_size > len || 144bf215546Sopenharmony_ci header->payload_size != sizeof(uint64_t)) 145bf215546Sopenharmony_ci break; 146bf215546Sopenharmony_ci 147bf215546Sopenharmony_ci char hash_str[FOSSILIZE_BLOB_HASH_LENGTH + 1] = {0}; 148bf215546Sopenharmony_ci memcpy(hash_str, bytes_to_read, FOSSILIZE_BLOB_HASH_LENGTH); 149bf215546Sopenharmony_ci 150bf215546Sopenharmony_ci /* read cache item offset from index file */ 151bf215546Sopenharmony_ci uint64_t cache_offset; 152bf215546Sopenharmony_ci if (fread(&cache_offset, 1, sizeof(cache_offset), db_idx) != 153bf215546Sopenharmony_ci sizeof(cache_offset)) 154bf215546Sopenharmony_ci break; 155bf215546Sopenharmony_ci 156bf215546Sopenharmony_ci offset += header->payload_size; 157bf215546Sopenharmony_ci parsed_offset = offset; 158bf215546Sopenharmony_ci 159bf215546Sopenharmony_ci struct foz_db_entry *entry = ralloc(foz_db->mem_ctx, 160bf215546Sopenharmony_ci struct foz_db_entry); 161bf215546Sopenharmony_ci entry->header = *header; 162bf215546Sopenharmony_ci entry->file_idx = file_idx; 163bf215546Sopenharmony_ci _mesa_sha1_hex_to_sha1(entry->key, hash_str); 164bf215546Sopenharmony_ci 165bf215546Sopenharmony_ci /* Truncate the entry's hash string to a 64bit hash for use with a 166bf215546Sopenharmony_ci * 64bit hash table for looking up file offsets. 167bf215546Sopenharmony_ci */ 168bf215546Sopenharmony_ci hash_str[16] = '\0'; 169bf215546Sopenharmony_ci uint64_t key = strtoull(hash_str, NULL, 16); 170bf215546Sopenharmony_ci 171bf215546Sopenharmony_ci entry->offset = cache_offset; 172bf215546Sopenharmony_ci 173bf215546Sopenharmony_ci _mesa_hash_table_u64_insert(foz_db->index_db, key, entry); 174bf215546Sopenharmony_ci } 175bf215546Sopenharmony_ci 176bf215546Sopenharmony_ci 177bf215546Sopenharmony_ci fseek(db_idx, parsed_offset, SEEK_SET); 178bf215546Sopenharmony_ci} 179bf215546Sopenharmony_ci 180bf215546Sopenharmony_ci/* exclusive flock with timeout. timeout is in nanoseconds */ 181bf215546Sopenharmony_cistatic int lock_file_with_timeout(FILE *f, int64_t timeout) 182bf215546Sopenharmony_ci{ 183bf215546Sopenharmony_ci int err; 184bf215546Sopenharmony_ci int fd = fileno(f); 185bf215546Sopenharmony_ci int64_t iterations = MAX2(DIV_ROUND_UP(timeout, 1000000), 1); 186bf215546Sopenharmony_ci 187bf215546Sopenharmony_ci /* Since there is no blocking flock with timeout and we don't want to totally spin on getting the 188bf215546Sopenharmony_ci * lock, use a nonblocking method and retry every millisecond. */ 189bf215546Sopenharmony_ci for (int64_t iter = 0; iter < iterations; ++iter) { 190bf215546Sopenharmony_ci err = flock(fd, LOCK_EX | LOCK_NB); 191bf215546Sopenharmony_ci if (err == 0 || errno != EAGAIN) 192bf215546Sopenharmony_ci break; 193bf215546Sopenharmony_ci usleep(1000); 194bf215546Sopenharmony_ci } 195bf215546Sopenharmony_ci return err; 196bf215546Sopenharmony_ci} 197bf215546Sopenharmony_ci 198bf215546Sopenharmony_cistatic bool 199bf215546Sopenharmony_ciload_foz_dbs(struct foz_db *foz_db, FILE *db_idx, uint8_t file_idx, 200bf215546Sopenharmony_ci bool read_only) 201bf215546Sopenharmony_ci{ 202bf215546Sopenharmony_ci /* Scan through the archive and get the list of cache entries. */ 203bf215546Sopenharmony_ci fseek(db_idx, 0, SEEK_END); 204bf215546Sopenharmony_ci size_t len = ftell(db_idx); 205bf215546Sopenharmony_ci rewind(db_idx); 206bf215546Sopenharmony_ci 207bf215546Sopenharmony_ci /* Try not to take the lock if len >= the size of the header, but if it is smaller we take the 208bf215546Sopenharmony_ci * lock to potentially initialize the files. */ 209bf215546Sopenharmony_ci if (len < sizeof(stream_reference_magic_and_version)) { 210bf215546Sopenharmony_ci /* Wait for 100 ms in case of contention, after that we prioritize getting the app started. */ 211bf215546Sopenharmony_ci int err = lock_file_with_timeout(foz_db->file[file_idx], 100000000); 212bf215546Sopenharmony_ci if (err == -1) 213bf215546Sopenharmony_ci goto fail; 214bf215546Sopenharmony_ci 215bf215546Sopenharmony_ci /* Compute length again so we know nobody else did it in the meantime */ 216bf215546Sopenharmony_ci fseek(db_idx, 0, SEEK_END); 217bf215546Sopenharmony_ci len = ftell(db_idx); 218bf215546Sopenharmony_ci rewind(db_idx); 219bf215546Sopenharmony_ci } 220bf215546Sopenharmony_ci 221bf215546Sopenharmony_ci if (len != 0) { 222bf215546Sopenharmony_ci uint8_t magic[FOZ_REF_MAGIC_SIZE]; 223bf215546Sopenharmony_ci if (fread(magic, 1, FOZ_REF_MAGIC_SIZE, db_idx) != FOZ_REF_MAGIC_SIZE) 224bf215546Sopenharmony_ci goto fail; 225bf215546Sopenharmony_ci 226bf215546Sopenharmony_ci if (memcmp(magic, stream_reference_magic_and_version, 227bf215546Sopenharmony_ci FOZ_REF_MAGIC_SIZE - 1)) 228bf215546Sopenharmony_ci goto fail; 229bf215546Sopenharmony_ci 230bf215546Sopenharmony_ci int version = magic[FOZ_REF_MAGIC_SIZE - 1]; 231bf215546Sopenharmony_ci if (version > FOSSILIZE_FORMAT_VERSION || 232bf215546Sopenharmony_ci version < FOSSILIZE_FORMAT_MIN_COMPAT_VERSION) 233bf215546Sopenharmony_ci goto fail; 234bf215546Sopenharmony_ci 235bf215546Sopenharmony_ci } else { 236bf215546Sopenharmony_ci /* Appending to a fresh file. Make sure we have the magic. */ 237bf215546Sopenharmony_ci if (fwrite(stream_reference_magic_and_version, 1, 238bf215546Sopenharmony_ci sizeof(stream_reference_magic_and_version), foz_db->file[file_idx]) != 239bf215546Sopenharmony_ci sizeof(stream_reference_magic_and_version)) 240bf215546Sopenharmony_ci goto fail; 241bf215546Sopenharmony_ci 242bf215546Sopenharmony_ci if (fwrite(stream_reference_magic_and_version, 1, 243bf215546Sopenharmony_ci sizeof(stream_reference_magic_and_version), db_idx) != 244bf215546Sopenharmony_ci sizeof(stream_reference_magic_and_version)) 245bf215546Sopenharmony_ci goto fail; 246bf215546Sopenharmony_ci 247bf215546Sopenharmony_ci fflush(foz_db->file[file_idx]); 248bf215546Sopenharmony_ci fflush(db_idx); 249bf215546Sopenharmony_ci } 250bf215546Sopenharmony_ci 251bf215546Sopenharmony_ci flock(fileno(foz_db->file[file_idx]), LOCK_UN); 252bf215546Sopenharmony_ci 253bf215546Sopenharmony_ci update_foz_index(foz_db, db_idx, file_idx); 254bf215546Sopenharmony_ci 255bf215546Sopenharmony_ci foz_db->alive = true; 256bf215546Sopenharmony_ci return true; 257bf215546Sopenharmony_ci 258bf215546Sopenharmony_cifail: 259bf215546Sopenharmony_ci flock(fileno(foz_db->file[file_idx]), LOCK_UN); 260bf215546Sopenharmony_ci foz_destroy(foz_db); 261bf215546Sopenharmony_ci return false; 262bf215546Sopenharmony_ci} 263bf215546Sopenharmony_ci 264bf215546Sopenharmony_ci/* Here we open mesa cache foz dbs files. If the files exist we load the index 265bf215546Sopenharmony_ci * db into a hash table. The index db contains the offsets needed to later 266bf215546Sopenharmony_ci * read cache entries from the foz db containing the actual cache entries. 267bf215546Sopenharmony_ci */ 268bf215546Sopenharmony_cibool 269bf215546Sopenharmony_cifoz_prepare(struct foz_db *foz_db, char *cache_path) 270bf215546Sopenharmony_ci{ 271bf215546Sopenharmony_ci char *filename = NULL; 272bf215546Sopenharmony_ci char *idx_filename = NULL; 273bf215546Sopenharmony_ci if (!create_foz_db_filenames(cache_path, "foz_cache", &filename, &idx_filename)) 274bf215546Sopenharmony_ci return false; 275bf215546Sopenharmony_ci 276bf215546Sopenharmony_ci /* Open the default foz dbs for read/write. If the files didn't already exist 277bf215546Sopenharmony_ci * create them. 278bf215546Sopenharmony_ci */ 279bf215546Sopenharmony_ci foz_db->file[0] = fopen(filename, "a+b"); 280bf215546Sopenharmony_ci foz_db->db_idx = fopen(idx_filename, "a+b"); 281bf215546Sopenharmony_ci 282bf215546Sopenharmony_ci free(filename); 283bf215546Sopenharmony_ci free(idx_filename); 284bf215546Sopenharmony_ci 285bf215546Sopenharmony_ci if (!check_files_opened_successfully(foz_db->file[0], foz_db->db_idx)) 286bf215546Sopenharmony_ci return false; 287bf215546Sopenharmony_ci 288bf215546Sopenharmony_ci simple_mtx_init(&foz_db->mtx, mtx_plain); 289bf215546Sopenharmony_ci simple_mtx_init(&foz_db->flock_mtx, mtx_plain); 290bf215546Sopenharmony_ci foz_db->mem_ctx = ralloc_context(NULL); 291bf215546Sopenharmony_ci foz_db->index_db = _mesa_hash_table_u64_create(NULL); 292bf215546Sopenharmony_ci 293bf215546Sopenharmony_ci if (!load_foz_dbs(foz_db, foz_db->db_idx, 0, false)) 294bf215546Sopenharmony_ci return false; 295bf215546Sopenharmony_ci 296bf215546Sopenharmony_ci uint8_t file_idx = 1; 297bf215546Sopenharmony_ci char *foz_dbs = getenv("MESA_DISK_CACHE_READ_ONLY_FOZ_DBS"); 298bf215546Sopenharmony_ci if (!foz_dbs) 299bf215546Sopenharmony_ci return true; 300bf215546Sopenharmony_ci 301bf215546Sopenharmony_ci for (unsigned n; n = strcspn(foz_dbs, ","), *foz_dbs; 302bf215546Sopenharmony_ci foz_dbs += MAX2(1, n)) { 303bf215546Sopenharmony_ci char *foz_db_filename = strndup(foz_dbs, n); 304bf215546Sopenharmony_ci 305bf215546Sopenharmony_ci filename = NULL; 306bf215546Sopenharmony_ci idx_filename = NULL; 307bf215546Sopenharmony_ci if (!create_foz_db_filenames(cache_path, foz_db_filename, &filename, 308bf215546Sopenharmony_ci &idx_filename)) { 309bf215546Sopenharmony_ci free(foz_db_filename); 310bf215546Sopenharmony_ci continue; /* Ignore invalid user provided filename and continue */ 311bf215546Sopenharmony_ci } 312bf215546Sopenharmony_ci free(foz_db_filename); 313bf215546Sopenharmony_ci 314bf215546Sopenharmony_ci /* Open files as read only */ 315bf215546Sopenharmony_ci foz_db->file[file_idx] = fopen(filename, "rb"); 316bf215546Sopenharmony_ci FILE *db_idx = fopen(idx_filename, "rb"); 317bf215546Sopenharmony_ci 318bf215546Sopenharmony_ci free(filename); 319bf215546Sopenharmony_ci free(idx_filename); 320bf215546Sopenharmony_ci 321bf215546Sopenharmony_ci if (!check_files_opened_successfully(foz_db->file[file_idx], db_idx)) { 322bf215546Sopenharmony_ci /* Prevent foz_destroy from destroying it a second time. */ 323bf215546Sopenharmony_ci foz_db->file[file_idx] = NULL; 324bf215546Sopenharmony_ci 325bf215546Sopenharmony_ci continue; /* Ignore invalid user provided filename and continue */ 326bf215546Sopenharmony_ci } 327bf215546Sopenharmony_ci 328bf215546Sopenharmony_ci if (!load_foz_dbs(foz_db, db_idx, file_idx, true)) { 329bf215546Sopenharmony_ci fclose(db_idx); 330bf215546Sopenharmony_ci return false; 331bf215546Sopenharmony_ci } 332bf215546Sopenharmony_ci 333bf215546Sopenharmony_ci fclose(db_idx); 334bf215546Sopenharmony_ci file_idx++; 335bf215546Sopenharmony_ci 336bf215546Sopenharmony_ci if (file_idx >= FOZ_MAX_DBS) 337bf215546Sopenharmony_ci break; 338bf215546Sopenharmony_ci } 339bf215546Sopenharmony_ci 340bf215546Sopenharmony_ci return true; 341bf215546Sopenharmony_ci} 342bf215546Sopenharmony_ci 343bf215546Sopenharmony_civoid 344bf215546Sopenharmony_cifoz_destroy(struct foz_db *foz_db) 345bf215546Sopenharmony_ci{ 346bf215546Sopenharmony_ci if (foz_db->db_idx) 347bf215546Sopenharmony_ci fclose(foz_db->db_idx); 348bf215546Sopenharmony_ci for (unsigned i = 0; i < FOZ_MAX_DBS; i++) { 349bf215546Sopenharmony_ci if (foz_db->file[i]) 350bf215546Sopenharmony_ci fclose(foz_db->file[i]); 351bf215546Sopenharmony_ci } 352bf215546Sopenharmony_ci 353bf215546Sopenharmony_ci if (foz_db->mem_ctx) { 354bf215546Sopenharmony_ci _mesa_hash_table_u64_destroy(foz_db->index_db); 355bf215546Sopenharmony_ci ralloc_free(foz_db->mem_ctx); 356bf215546Sopenharmony_ci simple_mtx_destroy(&foz_db->flock_mtx); 357bf215546Sopenharmony_ci simple_mtx_destroy(&foz_db->mtx); 358bf215546Sopenharmony_ci } 359bf215546Sopenharmony_ci} 360bf215546Sopenharmony_ci 361bf215546Sopenharmony_ci/* Here we lookup a cache entry in the index hash table. If an entry is found 362bf215546Sopenharmony_ci * we use the retrieved offset to read the cache entry from disk. 363bf215546Sopenharmony_ci */ 364bf215546Sopenharmony_civoid * 365bf215546Sopenharmony_cifoz_read_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit, 366bf215546Sopenharmony_ci size_t *size) 367bf215546Sopenharmony_ci{ 368bf215546Sopenharmony_ci uint64_t hash = truncate_hash_to_64bits(cache_key_160bit); 369bf215546Sopenharmony_ci 370bf215546Sopenharmony_ci void *data = NULL; 371bf215546Sopenharmony_ci 372bf215546Sopenharmony_ci if (!foz_db->alive) 373bf215546Sopenharmony_ci return NULL; 374bf215546Sopenharmony_ci 375bf215546Sopenharmony_ci simple_mtx_lock(&foz_db->mtx); 376bf215546Sopenharmony_ci 377bf215546Sopenharmony_ci struct foz_db_entry *entry = 378bf215546Sopenharmony_ci _mesa_hash_table_u64_search(foz_db->index_db, hash); 379bf215546Sopenharmony_ci if (!entry) { 380bf215546Sopenharmony_ci update_foz_index(foz_db, foz_db->db_idx, 0); 381bf215546Sopenharmony_ci entry = _mesa_hash_table_u64_search(foz_db->index_db, hash); 382bf215546Sopenharmony_ci } 383bf215546Sopenharmony_ci if (!entry) { 384bf215546Sopenharmony_ci simple_mtx_unlock(&foz_db->mtx); 385bf215546Sopenharmony_ci return NULL; 386bf215546Sopenharmony_ci } 387bf215546Sopenharmony_ci 388bf215546Sopenharmony_ci uint8_t file_idx = entry->file_idx; 389bf215546Sopenharmony_ci if (fseek(foz_db->file[file_idx], entry->offset, SEEK_SET) < 0) 390bf215546Sopenharmony_ci goto fail; 391bf215546Sopenharmony_ci 392bf215546Sopenharmony_ci uint32_t header_size = sizeof(struct foz_payload_header); 393bf215546Sopenharmony_ci if (fread(&entry->header, 1, header_size, foz_db->file[file_idx]) != 394bf215546Sopenharmony_ci header_size) 395bf215546Sopenharmony_ci goto fail; 396bf215546Sopenharmony_ci 397bf215546Sopenharmony_ci /* Check for collision using full 160bit hash for increased assurance 398bf215546Sopenharmony_ci * against potential collisions. 399bf215546Sopenharmony_ci */ 400bf215546Sopenharmony_ci for (int i = 0; i < 20; i++) { 401bf215546Sopenharmony_ci if (cache_key_160bit[i] != entry->key[i]) 402bf215546Sopenharmony_ci goto fail; 403bf215546Sopenharmony_ci } 404bf215546Sopenharmony_ci 405bf215546Sopenharmony_ci uint32_t data_sz = entry->header.payload_size; 406bf215546Sopenharmony_ci data = malloc(data_sz); 407bf215546Sopenharmony_ci if (fread(data, 1, data_sz, foz_db->file[file_idx]) != data_sz) 408bf215546Sopenharmony_ci goto fail; 409bf215546Sopenharmony_ci 410bf215546Sopenharmony_ci /* verify checksum */ 411bf215546Sopenharmony_ci if (entry->header.crc != 0) { 412bf215546Sopenharmony_ci if (util_hash_crc32(data, data_sz) != entry->header.crc) 413bf215546Sopenharmony_ci goto fail; 414bf215546Sopenharmony_ci } 415bf215546Sopenharmony_ci 416bf215546Sopenharmony_ci simple_mtx_unlock(&foz_db->mtx); 417bf215546Sopenharmony_ci 418bf215546Sopenharmony_ci if (size) 419bf215546Sopenharmony_ci *size = data_sz; 420bf215546Sopenharmony_ci 421bf215546Sopenharmony_ci return data; 422bf215546Sopenharmony_ci 423bf215546Sopenharmony_cifail: 424bf215546Sopenharmony_ci free(data); 425bf215546Sopenharmony_ci 426bf215546Sopenharmony_ci /* reading db entry failed. reset the file offset */ 427bf215546Sopenharmony_ci simple_mtx_unlock(&foz_db->mtx); 428bf215546Sopenharmony_ci 429bf215546Sopenharmony_ci return NULL; 430bf215546Sopenharmony_ci} 431bf215546Sopenharmony_ci 432bf215546Sopenharmony_ci/* Here we write the cache entry to disk and store its offset in the index db. 433bf215546Sopenharmony_ci */ 434bf215546Sopenharmony_cibool 435bf215546Sopenharmony_cifoz_write_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit, 436bf215546Sopenharmony_ci const void *blob, size_t blob_size) 437bf215546Sopenharmony_ci{ 438bf215546Sopenharmony_ci uint64_t hash = truncate_hash_to_64bits(cache_key_160bit); 439bf215546Sopenharmony_ci 440bf215546Sopenharmony_ci if (!foz_db->alive) 441bf215546Sopenharmony_ci return false; 442bf215546Sopenharmony_ci 443bf215546Sopenharmony_ci /* The flock is per-fd, not per thread, we do it outside of the main mutex to avoid having to 444bf215546Sopenharmony_ci * wait in the mutex potentially blocking reads. We use the secondary flock_mtx to stop race 445bf215546Sopenharmony_ci * conditions between the write threads sharing the same file descriptor. */ 446bf215546Sopenharmony_ci simple_mtx_lock(&foz_db->flock_mtx); 447bf215546Sopenharmony_ci 448bf215546Sopenharmony_ci /* Wait for 1 second. This is done outside of the main mutex as I believe there is more potential 449bf215546Sopenharmony_ci * for file contention than mtx contention of significant length. */ 450bf215546Sopenharmony_ci int err = lock_file_with_timeout(foz_db->file[0], 1000000000); 451bf215546Sopenharmony_ci if (err == -1) 452bf215546Sopenharmony_ci goto fail_file; 453bf215546Sopenharmony_ci 454bf215546Sopenharmony_ci simple_mtx_lock(&foz_db->mtx); 455bf215546Sopenharmony_ci 456bf215546Sopenharmony_ci update_foz_index(foz_db, foz_db->db_idx, 0); 457bf215546Sopenharmony_ci 458bf215546Sopenharmony_ci struct foz_db_entry *entry = 459bf215546Sopenharmony_ci _mesa_hash_table_u64_search(foz_db->index_db, hash); 460bf215546Sopenharmony_ci if (entry) { 461bf215546Sopenharmony_ci simple_mtx_unlock(&foz_db->mtx); 462bf215546Sopenharmony_ci flock(fileno(foz_db->file[0]), LOCK_UN); 463bf215546Sopenharmony_ci simple_mtx_unlock(&foz_db->flock_mtx); 464bf215546Sopenharmony_ci return NULL; 465bf215546Sopenharmony_ci } 466bf215546Sopenharmony_ci 467bf215546Sopenharmony_ci /* Prepare db entry header and blob ready for writing */ 468bf215546Sopenharmony_ci struct foz_payload_header header; 469bf215546Sopenharmony_ci header.uncompressed_size = blob_size; 470bf215546Sopenharmony_ci header.format = FOSSILIZE_COMPRESSION_NONE; 471bf215546Sopenharmony_ci header.payload_size = blob_size; 472bf215546Sopenharmony_ci header.crc = util_hash_crc32(blob, blob_size); 473bf215546Sopenharmony_ci 474bf215546Sopenharmony_ci fseek(foz_db->file[0], 0, SEEK_END); 475bf215546Sopenharmony_ci 476bf215546Sopenharmony_ci /* Write hash header to db */ 477bf215546Sopenharmony_ci char hash_str[FOSSILIZE_BLOB_HASH_LENGTH + 1]; /* 40 digits + null */ 478bf215546Sopenharmony_ci _mesa_sha1_format(hash_str, cache_key_160bit); 479bf215546Sopenharmony_ci if (fwrite(hash_str, 1, FOSSILIZE_BLOB_HASH_LENGTH, foz_db->file[0]) != 480bf215546Sopenharmony_ci FOSSILIZE_BLOB_HASH_LENGTH) 481bf215546Sopenharmony_ci goto fail; 482bf215546Sopenharmony_ci 483bf215546Sopenharmony_ci off_t offset = ftell(foz_db->file[0]); 484bf215546Sopenharmony_ci 485bf215546Sopenharmony_ci /* Write db entry header */ 486bf215546Sopenharmony_ci if (fwrite(&header, 1, sizeof(header), foz_db->file[0]) != sizeof(header)) 487bf215546Sopenharmony_ci goto fail; 488bf215546Sopenharmony_ci 489bf215546Sopenharmony_ci /* Now write the db entry blob */ 490bf215546Sopenharmony_ci if (fwrite(blob, 1, blob_size, foz_db->file[0]) != blob_size) 491bf215546Sopenharmony_ci goto fail; 492bf215546Sopenharmony_ci 493bf215546Sopenharmony_ci /* Flush everything to file to reduce chance of cache corruption */ 494bf215546Sopenharmony_ci fflush(foz_db->file[0]); 495bf215546Sopenharmony_ci 496bf215546Sopenharmony_ci /* Write hash header to index db */ 497bf215546Sopenharmony_ci if (fwrite(hash_str, 1, FOSSILIZE_BLOB_HASH_LENGTH, foz_db->db_idx) != 498bf215546Sopenharmony_ci FOSSILIZE_BLOB_HASH_LENGTH) 499bf215546Sopenharmony_ci goto fail; 500bf215546Sopenharmony_ci 501bf215546Sopenharmony_ci header.uncompressed_size = sizeof(uint64_t); 502bf215546Sopenharmony_ci header.format = FOSSILIZE_COMPRESSION_NONE; 503bf215546Sopenharmony_ci header.payload_size = sizeof(uint64_t); 504bf215546Sopenharmony_ci header.crc = 0; 505bf215546Sopenharmony_ci 506bf215546Sopenharmony_ci if (fwrite(&header, 1, sizeof(header), foz_db->db_idx) != 507bf215546Sopenharmony_ci sizeof(header)) 508bf215546Sopenharmony_ci goto fail; 509bf215546Sopenharmony_ci 510bf215546Sopenharmony_ci if (fwrite(&offset, 1, sizeof(uint64_t), foz_db->db_idx) != 511bf215546Sopenharmony_ci sizeof(uint64_t)) 512bf215546Sopenharmony_ci goto fail; 513bf215546Sopenharmony_ci 514bf215546Sopenharmony_ci /* Flush everything to file to reduce chance of cache corruption */ 515bf215546Sopenharmony_ci fflush(foz_db->db_idx); 516bf215546Sopenharmony_ci 517bf215546Sopenharmony_ci entry = ralloc(foz_db->mem_ctx, struct foz_db_entry); 518bf215546Sopenharmony_ci entry->header = header; 519bf215546Sopenharmony_ci entry->offset = offset; 520bf215546Sopenharmony_ci entry->file_idx = 0; 521bf215546Sopenharmony_ci _mesa_sha1_hex_to_sha1(entry->key, hash_str); 522bf215546Sopenharmony_ci _mesa_hash_table_u64_insert(foz_db->index_db, hash, entry); 523bf215546Sopenharmony_ci 524bf215546Sopenharmony_ci simple_mtx_unlock(&foz_db->mtx); 525bf215546Sopenharmony_ci flock(fileno(foz_db->file[0]), LOCK_UN); 526bf215546Sopenharmony_ci simple_mtx_unlock(&foz_db->flock_mtx); 527bf215546Sopenharmony_ci 528bf215546Sopenharmony_ci return true; 529bf215546Sopenharmony_ci 530bf215546Sopenharmony_cifail: 531bf215546Sopenharmony_ci simple_mtx_unlock(&foz_db->mtx); 532bf215546Sopenharmony_cifail_file: 533bf215546Sopenharmony_ci flock(fileno(foz_db->file[0]), LOCK_UN); 534bf215546Sopenharmony_ci simple_mtx_unlock(&foz_db->flock_mtx); 535bf215546Sopenharmony_ci return false; 536bf215546Sopenharmony_ci} 537bf215546Sopenharmony_ci#else 538bf215546Sopenharmony_ci 539bf215546Sopenharmony_cibool 540bf215546Sopenharmony_cifoz_prepare(struct foz_db *foz_db, char *filename) 541bf215546Sopenharmony_ci{ 542bf215546Sopenharmony_ci fprintf(stderr, "Warning: Mesa single file cache selected but Mesa wasn't " 543bf215546Sopenharmony_ci "built with single cache file support. Shader cache will be disabled" 544bf215546Sopenharmony_ci "!\n"); 545bf215546Sopenharmony_ci return false; 546bf215546Sopenharmony_ci} 547bf215546Sopenharmony_ci 548bf215546Sopenharmony_civoid 549bf215546Sopenharmony_cifoz_destroy(struct foz_db *foz_db) 550bf215546Sopenharmony_ci{ 551bf215546Sopenharmony_ci} 552bf215546Sopenharmony_ci 553bf215546Sopenharmony_civoid * 554bf215546Sopenharmony_cifoz_read_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit, 555bf215546Sopenharmony_ci size_t *size) 556bf215546Sopenharmony_ci{ 557bf215546Sopenharmony_ci return false; 558bf215546Sopenharmony_ci} 559bf215546Sopenharmony_ci 560bf215546Sopenharmony_cibool 561bf215546Sopenharmony_cifoz_write_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit, 562bf215546Sopenharmony_ci const void *blob, size_t size) 563bf215546Sopenharmony_ci{ 564bf215546Sopenharmony_ci return false; 565bf215546Sopenharmony_ci} 566bf215546Sopenharmony_ci 567bf215546Sopenharmony_ci#endif 568