mirror of https://gitlab.freedesktop.org/mesa/mesa
812 lines
21 KiB
C
812 lines
21 KiB
C
/*
|
|
* Copyright © 2022 Collabora, Ltd.
|
|
*
|
|
* Based on Fossilize DB:
|
|
* Copyright © 2020 Valve Corporation
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include "detect_os.h"
|
|
|
|
#if DETECT_OS_WINDOWS == 0
|
|
|
|
#include <fcntl.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/file.h>
|
|
#include <unistd.h>
|
|
|
|
#include "crc32.h"
|
|
#include "disk_cache.h"
|
|
#include "hash_table.h"
|
|
#include "mesa-sha1.h"
|
|
#include "mesa_cache_db.h"
|
|
#include "os_time.h"
|
|
#include "ralloc.h"
|
|
#include "u_qsort.h"
|
|
|
|
#define MESA_CACHE_DB_VERSION 1
|
|
#define MESA_CACHE_DB_MAGIC "MESA_DB"
|
|
|
|
struct PACKED mesa_db_file_header {
|
|
char magic[8];
|
|
uint32_t version;
|
|
uint64_t uuid;
|
|
};
|
|
|
|
struct PACKED mesa_cache_db_file_entry {
|
|
cache_key key;
|
|
uint32_t crc;
|
|
uint32_t size;
|
|
};
|
|
|
|
struct PACKED mesa_index_db_file_entry {
|
|
uint64_t hash;
|
|
uint32_t size;
|
|
uint64_t last_access_time;
|
|
uint64_t cache_db_file_offset;
|
|
};
|
|
|
|
struct mesa_index_db_hash_entry {
|
|
uint64_t cache_db_file_offset;
|
|
uint64_t index_db_file_offset;
|
|
uint64_t last_access_time;
|
|
uint32_t size;
|
|
bool evicted;
|
|
};
|
|
|
|
static inline bool mesa_db_seek_end(FILE *file)
|
|
{
|
|
return !fseek(file, 0, SEEK_END);
|
|
}
|
|
|
|
static inline bool mesa_db_seek(FILE *file, long pos)
|
|
{
|
|
return !fseek(file, pos, SEEK_SET);
|
|
}
|
|
|
|
static inline bool mesa_db_seek_cur(FILE *file, long pos)
|
|
{
|
|
return !fseek(file, pos, SEEK_CUR);
|
|
}
|
|
|
|
static inline bool mesa_db_read_data(FILE *file, void *data, size_t size)
|
|
{
|
|
return fread(data, 1, size, file) == size;
|
|
}
|
|
#define mesa_db_read(file, var) mesa_db_read_data(file, var, sizeof(*(var)))
|
|
|
|
static inline bool mesa_db_write_data(FILE *file, const void *data, size_t size)
|
|
{
|
|
return fwrite(data, 1, size, file) == size;
|
|
}
|
|
#define mesa_db_write(file, var) mesa_db_write_data(file, var, sizeof(*(var)))
|
|
|
|
static inline bool mesa_db_truncate(FILE *file, long pos)
|
|
{
|
|
return !ftruncate(fileno(file), pos);
|
|
}
|
|
|
|
static bool
|
|
mesa_db_lock(struct mesa_cache_db *db)
|
|
{
|
|
simple_mtx_lock(&db->flock_mtx);
|
|
|
|
if (flock(fileno(db->cache.file), LOCK_EX) == -1)
|
|
goto unlock_mtx;
|
|
|
|
if (flock(fileno(db->index.file), LOCK_EX) == -1)
|
|
goto unlock_cache;
|
|
|
|
return true;
|
|
|
|
unlock_cache:
|
|
flock(fileno(db->cache.file), LOCK_UN);
|
|
unlock_mtx:
|
|
simple_mtx_unlock(&db->flock_mtx);
|
|
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
mesa_db_unlock(struct mesa_cache_db *db)
|
|
{
|
|
flock(fileno(db->index.file), LOCK_UN);
|
|
flock(fileno(db->cache.file), LOCK_UN);
|
|
simple_mtx_unlock(&db->flock_mtx);
|
|
}
|
|
|
|
static uint64_t to_mesa_cache_db_hash(const uint8_t *cache_key_160bit)
|
|
{
|
|
uint64_t hash = 0;
|
|
|
|
for (unsigned i = 0; i < 8; i++)
|
|
hash |= ((uint64_t)cache_key_160bit[i]) << i * 8;
|
|
|
|
return hash;
|
|
}
|
|
|
|
static uint64_t
|
|
mesa_db_generate_uuid(void)
|
|
{
|
|
/* This simple UUID implementation is sufficient for our needs
|
|
* because UUID is updated rarely. It's nice to make UUID meaningful
|
|
* and incremental by adding the timestamp to it, which also prevents
|
|
* the potential collisions. */
|
|
return ((os_time_get() / 1000000) << 32) | rand();
|
|
}
|
|
|
|
static bool
|
|
mesa_db_read_header(FILE *file, struct mesa_db_file_header *header)
|
|
{
|
|
rewind(file);
|
|
fflush(file);
|
|
|
|
if (!mesa_db_read(file, header))
|
|
return false;
|
|
|
|
if (strncmp(header->magic, MESA_CACHE_DB_MAGIC, sizeof(header->magic)) ||
|
|
header->version != MESA_CACHE_DB_VERSION || !header->uuid)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
mesa_db_load_header(struct mesa_cache_db_file *db_file)
|
|
{
|
|
struct mesa_db_file_header header;
|
|
|
|
if (!mesa_db_read_header(db_file->file, &header))
|
|
return false;
|
|
|
|
db_file->uuid = header.uuid;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool mesa_db_uuid_changed(struct mesa_cache_db *db)
|
|
{
|
|
struct mesa_db_file_header cache_header;
|
|
struct mesa_db_file_header index_header;
|
|
|
|
if (!mesa_db_read_header(db->cache.file, &cache_header) ||
|
|
!mesa_db_read_header(db->index.file, &index_header) ||
|
|
cache_header.uuid != index_header.uuid ||
|
|
cache_header.uuid != db->uuid)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
mesa_db_write_header(struct mesa_cache_db_file *db_file,
|
|
uint64_t uuid, bool reset)
|
|
{
|
|
struct mesa_db_file_header header;
|
|
|
|
rewind(db_file->file);
|
|
|
|
sprintf(header.magic, "MESA_DB");
|
|
header.version = MESA_CACHE_DB_VERSION;
|
|
header.uuid = uuid;
|
|
|
|
if (!mesa_db_write(db_file->file, &header))
|
|
return false;
|
|
|
|
if (reset) {
|
|
if (!mesa_db_truncate(db_file->file, ftell(db_file->file)))
|
|
return false;
|
|
}
|
|
|
|
fflush(db_file->file);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Wipe out all database cache files.
|
|
*
|
|
* Whenever we get an unmanageable error on reading or writing to the
|
|
* database file, wipe out the whole database and start over. All the
|
|
* cached entries will be lost, but the broken cache will be auto-repaired
|
|
* reliably. Normally cache shall never get corrupted and losing cache
|
|
* entries is acceptable, hence it's more practical to repair DB using
|
|
* the simplest method.
|
|
*/
|
|
static bool
|
|
mesa_db_zap(struct mesa_cache_db *db)
|
|
{
|
|
/* Disable cache to prevent the recurring faults */
|
|
db->alive = false;
|
|
|
|
/* Zap corrupted database files to start over from a clean slate */
|
|
if (!mesa_db_truncate(db->cache.file, 0) ||
|
|
!mesa_db_truncate(db->index.file, 0))
|
|
return false;
|
|
|
|
fflush(db->cache.file);
|
|
fflush(db->index.file);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
mesa_db_index_entry_valid(struct mesa_index_db_file_entry *entry)
|
|
{
|
|
return entry->size && entry->hash &&
|
|
(int64_t)entry->cache_db_file_offset >= sizeof(struct mesa_db_file_header);
|
|
}
|
|
|
|
static bool
|
|
mesa_db_cache_entry_valid(struct mesa_cache_db_file_entry *entry)
|
|
{
|
|
return entry->size && entry->crc;
|
|
}
|
|
|
|
static bool
|
|
mesa_db_update_index(struct mesa_cache_db *db)
|
|
{
|
|
struct mesa_index_db_hash_entry *hash_entry;
|
|
struct mesa_index_db_file_entry index_entry;
|
|
size_t file_length;
|
|
|
|
if (!mesa_db_seek_end(db->index.file))
|
|
return false;
|
|
|
|
file_length = ftell(db->index.file);
|
|
|
|
if (!mesa_db_seek(db->index.file, db->index.offset))
|
|
return false;
|
|
|
|
while (db->index.offset < file_length) {
|
|
if (!mesa_db_read(db->index.file, &index_entry))
|
|
break;
|
|
|
|
/* Check whether the index entry looks valid or we have a corrupted DB */
|
|
if (!mesa_db_index_entry_valid(&index_entry))
|
|
break;
|
|
|
|
hash_entry = ralloc(db->mem_ctx, struct mesa_index_db_hash_entry);
|
|
if (!hash_entry)
|
|
break;
|
|
|
|
hash_entry->cache_db_file_offset = index_entry.cache_db_file_offset;
|
|
hash_entry->index_db_file_offset = db->index.offset;
|
|
hash_entry->last_access_time = index_entry.last_access_time;
|
|
hash_entry->size = index_entry.size;
|
|
|
|
_mesa_hash_table_u64_insert(db->index_db, index_entry.hash, hash_entry);
|
|
|
|
db->index.offset += sizeof(index_entry);
|
|
}
|
|
|
|
if (!mesa_db_seek(db->index.file, db->index.offset))
|
|
return false;
|
|
|
|
return db->index.offset == file_length;
|
|
}
|
|
|
|
static void
|
|
mesa_db_hash_table_reset(struct mesa_cache_db *db)
|
|
{
|
|
_mesa_hash_table_u64_clear(db->index_db);
|
|
ralloc_free(db->mem_ctx);
|
|
db->mem_ctx = ralloc_context(NULL);
|
|
}
|
|
|
|
static bool
|
|
mesa_db_recreate_files(struct mesa_cache_db *db)
|
|
{
|
|
db->uuid = mesa_db_generate_uuid();
|
|
|
|
if (!mesa_db_write_header(&db->cache, db->uuid, true) ||
|
|
!mesa_db_write_header(&db->index, db->uuid, true))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
mesa_db_load(struct mesa_cache_db *db, bool reload)
|
|
{
|
|
/* reloading must be done under the held lock */
|
|
if (!reload) {
|
|
if (!mesa_db_lock(db))
|
|
return false;
|
|
}
|
|
|
|
/* If file headers are invalid, then zap database files and start over */
|
|
if (!mesa_db_load_header(&db->cache) ||
|
|
!mesa_db_load_header(&db->index) ||
|
|
db->cache.uuid != db->index.uuid) {
|
|
|
|
/* This is unexpected to happen on reload, bail out */
|
|
if (reload)
|
|
goto fail;
|
|
|
|
if (!mesa_db_recreate_files(db))
|
|
goto fail;
|
|
} else {
|
|
db->uuid = db->cache.uuid;
|
|
}
|
|
|
|
db->index.offset = ftell(db->index.file);
|
|
|
|
if (reload)
|
|
mesa_db_hash_table_reset(db);
|
|
|
|
if (!mesa_db_update_index(db))
|
|
goto fail;
|
|
|
|
if (!reload)
|
|
mesa_db_unlock(db);
|
|
|
|
db->alive = true;
|
|
|
|
return true;
|
|
|
|
fail:
|
|
if (!reload)
|
|
mesa_db_unlock(db);
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
mesa_db_reload(struct mesa_cache_db *db)
|
|
{
|
|
fflush(db->cache.file);
|
|
fflush(db->index.file);
|
|
|
|
return mesa_db_load(db, true);
|
|
}
|
|
|
|
static void
|
|
touch_file(const char* path)
|
|
{
|
|
close(open(path, O_CREAT | O_CLOEXEC, 0644));
|
|
}
|
|
|
|
static bool
|
|
mesa_db_open_file(struct mesa_cache_db_file *db_file,
|
|
const char *cache_path,
|
|
const char *filename)
|
|
{
|
|
if (asprintf(&db_file->path, "%s/%s", cache_path, filename) == -1)
|
|
return false;
|
|
|
|
/* The fopen("r+b") mode doesn't auto-create new file, hence we need to
|
|
* explicitly create the file first.
|
|
*/
|
|
touch_file(db_file->path);
|
|
|
|
db_file->file = fopen(db_file->path, "r+b");
|
|
if (!db_file->file) {
|
|
free(db_file->path);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
mesa_db_close_file(struct mesa_cache_db_file *db_file)
|
|
{
|
|
fclose(db_file->file);
|
|
free(db_file->path);
|
|
}
|
|
|
|
static int
|
|
entry_sort_lru(const void *_a, const void *_b, void *arg)
|
|
{
|
|
const struct mesa_index_db_hash_entry *a = *((const struct mesa_index_db_hash_entry **)_a);
|
|
const struct mesa_index_db_hash_entry *b = *((const struct mesa_index_db_hash_entry **)_b);
|
|
|
|
/* In practice it's unlikely that we will get two entries with the
|
|
* same timestamp, but technically it's possible to happen if OS
|
|
* timer's resolution is low. */
|
|
if (a->last_access_time == b->last_access_time)
|
|
return 0;
|
|
|
|
return a->last_access_time > b->last_access_time ? 1 : -1;
|
|
}
|
|
|
|
static int
|
|
entry_sort_offset(const void *_a, const void *_b, void *arg)
|
|
{
|
|
const struct mesa_index_db_hash_entry *a = *((const struct mesa_index_db_hash_entry **)_a);
|
|
const struct mesa_index_db_hash_entry *b = *((const struct mesa_index_db_hash_entry **)_b);
|
|
struct mesa_cache_db *db = arg;
|
|
|
|
/* Two entries will never have the identical offset, otherwise DB is
|
|
* corrupted. */
|
|
if (a->cache_db_file_offset == b->cache_db_file_offset)
|
|
mesa_db_zap(db);
|
|
|
|
return a->cache_db_file_offset > b->cache_db_file_offset ? 1 : -1;
|
|
}
|
|
|
|
static uint32_t blob_file_size(uint32_t blob_size)
|
|
{
|
|
return sizeof(struct mesa_cache_db_file_entry) + blob_size;
|
|
}
|
|
|
|
static bool
|
|
mesa_db_compact(struct mesa_cache_db *db, int64_t blob_size)
|
|
{
|
|
uint32_t num_entries, buffer_size = sizeof(struct mesa_index_db_file_entry);
|
|
struct mesa_db_file_header cache_header, index_header;
|
|
FILE *compacted_cache = NULL, *compacted_index = NULL;
|
|
struct mesa_index_db_file_entry index_entry;
|
|
struct mesa_index_db_hash_entry **entries;
|
|
bool success = false, compact = false;
|
|
void *buffer = NULL;
|
|
unsigned int i = 0;
|
|
|
|
/* reload index to sync the last access times */
|
|
if (!mesa_db_reload(db))
|
|
return false;
|
|
|
|
num_entries = _mesa_hash_table_num_entries(db->index_db->table);
|
|
entries = calloc(num_entries, sizeof(*entries));
|
|
if (!entries)
|
|
return false;
|
|
|
|
compacted_cache = fopen(db->cache.path, "r+b");
|
|
compacted_index = fopen(db->index.path, "r+b");
|
|
if (!compacted_cache || !compacted_index)
|
|
goto cleanup;
|
|
|
|
/* The database file has been replaced if UUID changed. We opened
|
|
* some other cache, stop processing this database. */
|
|
if (!mesa_db_read_header(compacted_cache, &cache_header) ||
|
|
!mesa_db_read_header(compacted_index, &index_header) ||
|
|
cache_header.uuid != db->uuid ||
|
|
index_header.uuid != db->uuid)
|
|
goto cleanup;
|
|
|
|
hash_table_foreach(db->index_db->table, entry) {
|
|
entries[i] = entry->data;
|
|
entries[i]->evicted = false;
|
|
buffer_size = MAX2(buffer_size, blob_file_size(entries[i]->size));
|
|
i++;
|
|
}
|
|
|
|
util_qsort_r(entries, num_entries, sizeof(*entries),
|
|
entry_sort_lru, db);
|
|
|
|
for (i = 0; blob_size > 0 && i < num_entries; i++) {
|
|
blob_size -= blob_file_size(entries[i]->size);
|
|
entries[i]->evicted = true;
|
|
}
|
|
|
|
util_qsort_r(entries, num_entries, sizeof(*entries),
|
|
entry_sort_offset, db);
|
|
|
|
/* entry_sort_offset() may zap the database */
|
|
if (!db->alive)
|
|
goto cleanup;
|
|
|
|
buffer = malloc(buffer_size);
|
|
if (!buffer)
|
|
goto cleanup;
|
|
|
|
/* Mark cache file invalid by writing zero-UUID header. If compaction will
|
|
* fail, then the file will remain to be invalid since we can't repair it. */
|
|
if (!mesa_db_write_header(&db->cache, 0, false) ||
|
|
!mesa_db_write_header(&db->index, 0, false))
|
|
goto cleanup;
|
|
|
|
/* Sync the file pointers */
|
|
if (!mesa_db_seek(compacted_cache, ftell(db->cache.file)) ||
|
|
!mesa_db_seek(compacted_index, ftell(db->index.file)))
|
|
goto cleanup;
|
|
|
|
/* Do the compaction */
|
|
for (i = 0; i < num_entries; i++) {
|
|
blob_size = blob_file_size(entries[i]->size);
|
|
|
|
/* Sanity-check the cache-read offset */
|
|
if (ftell(db->cache.file) != entries[i]->cache_db_file_offset)
|
|
goto cleanup;
|
|
|
|
if (entries[i]->evicted) {
|
|
/* Jump over the evicted entry */
|
|
if (!mesa_db_seek_cur(db->cache.file, blob_size) ||
|
|
!mesa_db_seek_cur(db->index.file, sizeof(index_entry)))
|
|
goto cleanup;
|
|
|
|
compact = true;
|
|
continue;
|
|
}
|
|
|
|
if (compact) {
|
|
/* Compact the cache file */
|
|
if (!mesa_db_read_data(db->cache.file, buffer, blob_size) ||
|
|
!mesa_db_cache_entry_valid(buffer) ||
|
|
!mesa_db_write_data(compacted_cache, buffer, blob_size))
|
|
goto cleanup;
|
|
|
|
/* Compact the index file */
|
|
if (!mesa_db_read(db->index.file, &index_entry) ||
|
|
!mesa_db_index_entry_valid(&index_entry) ||
|
|
index_entry.cache_db_file_offset != entries[i]->cache_db_file_offset ||
|
|
index_entry.size != entries[i]->size)
|
|
goto cleanup;
|
|
|
|
index_entry.cache_db_file_offset = ftell(compacted_cache) - blob_size;
|
|
|
|
if (!mesa_db_write(compacted_index, &index_entry))
|
|
goto cleanup;
|
|
} else {
|
|
/* Sanity-check the cache-write offset */
|
|
if (ftell(compacted_cache) != entries[i]->cache_db_file_offset)
|
|
goto cleanup;
|
|
|
|
/* Jump over the unchanged entry */
|
|
if (!mesa_db_seek_cur(db->index.file, sizeof(index_entry)) ||
|
|
!mesa_db_seek_cur(compacted_index, sizeof(index_entry)) ||
|
|
!mesa_db_seek_cur(db->cache.file, blob_size) ||
|
|
!mesa_db_seek_cur(compacted_cache, blob_size))
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
fflush(compacted_cache);
|
|
fflush(compacted_index);
|
|
|
|
/* Cut off the the freed space left after compaction */
|
|
if (!mesa_db_truncate(db->cache.file, ftell(compacted_cache)) ||
|
|
!mesa_db_truncate(db->index.file, ftell(compacted_index)))
|
|
goto cleanup;
|
|
|
|
/* Set the new UUID to let all cache readers know that the cache was changed */
|
|
db->uuid = mesa_db_generate_uuid();
|
|
|
|
if (!mesa_db_write_header(&db->cache, db->uuid, false) ||
|
|
!mesa_db_write_header(&db->index, db->uuid, false))
|
|
goto cleanup;
|
|
|
|
success = true;
|
|
|
|
cleanup:
|
|
free(buffer);
|
|
if (compacted_index)
|
|
fclose(compacted_index);
|
|
if (compacted_cache)
|
|
fclose(compacted_cache);
|
|
free(entries);
|
|
|
|
/* reload compacted index */
|
|
if (success && !mesa_db_reload(db))
|
|
success = false;
|
|
|
|
return success;
|
|
}
|
|
|
|
bool
|
|
mesa_cache_db_open(struct mesa_cache_db *db, const char *cache_path)
|
|
{
|
|
if (!mesa_db_open_file(&db->cache, cache_path, "mesa_cache.db"))
|
|
return false;
|
|
|
|
if (!mesa_db_open_file(&db->index, cache_path, "mesa_cache.idx"))
|
|
goto close_cache;
|
|
|
|
db->mem_ctx = ralloc_context(NULL);
|
|
if (!db->mem_ctx)
|
|
goto close_index;
|
|
|
|
simple_mtx_init(&db->flock_mtx, mtx_plain);
|
|
|
|
db->index_db = _mesa_hash_table_u64_create(NULL);
|
|
if (!db->index_db)
|
|
goto destroy_mtx;
|
|
|
|
if (!mesa_db_load(db, false))
|
|
goto destroy_hash;
|
|
|
|
return true;
|
|
|
|
destroy_hash:
|
|
_mesa_hash_table_u64_destroy(db->index_db);
|
|
destroy_mtx:
|
|
simple_mtx_destroy(&db->flock_mtx);
|
|
|
|
ralloc_free(db->mem_ctx);
|
|
close_index:
|
|
mesa_db_close_file(&db->index);
|
|
close_cache:
|
|
mesa_db_close_file(&db->cache);
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
mesa_cache_db_close(struct mesa_cache_db *db)
|
|
{
|
|
_mesa_hash_table_u64_destroy(db->index_db);
|
|
simple_mtx_destroy(&db->flock_mtx);
|
|
ralloc_free(db->mem_ctx);
|
|
|
|
mesa_db_close_file(&db->index);
|
|
mesa_db_close_file(&db->cache);
|
|
}
|
|
|
|
void
|
|
mesa_cache_db_set_size_limit(struct mesa_cache_db *db,
|
|
uint64_t max_cache_size)
|
|
{
|
|
db->max_cache_size = max_cache_size;
|
|
}
|
|
|
|
unsigned int
|
|
mesa_cache_db_file_entry_size(void)
|
|
{
|
|
return sizeof(struct mesa_cache_db_file_entry);
|
|
}
|
|
|
|
void *
|
|
mesa_cache_db_read_entry(struct mesa_cache_db *db,
|
|
const uint8_t *cache_key_160bit,
|
|
size_t *size)
|
|
{
|
|
uint64_t hash = to_mesa_cache_db_hash(cache_key_160bit);
|
|
struct mesa_cache_db_file_entry cache_entry;
|
|
struct mesa_index_db_file_entry index_entry;
|
|
struct mesa_index_db_hash_entry *hash_entry;
|
|
void *data = NULL;
|
|
|
|
if (!mesa_db_lock(db))
|
|
return NULL;
|
|
|
|
if (!db->alive)
|
|
goto fail;
|
|
|
|
if (mesa_db_uuid_changed(db) && !mesa_db_reload(db))
|
|
goto fail_fatal;
|
|
|
|
if (!mesa_db_update_index(db))
|
|
goto fail_fatal;
|
|
|
|
hash_entry = _mesa_hash_table_u64_search(db->index_db, hash);
|
|
if (!hash_entry)
|
|
goto fail;
|
|
|
|
if (!mesa_db_seek(db->cache.file, hash_entry->cache_db_file_offset) ||
|
|
!mesa_db_read(db->cache.file, &cache_entry) ||
|
|
!mesa_db_cache_entry_valid(&cache_entry))
|
|
goto fail_fatal;
|
|
|
|
if (memcmp(cache_entry.key, cache_key_160bit, sizeof(cache_entry.key)))
|
|
goto fail;
|
|
|
|
data = malloc(cache_entry.size);
|
|
if (!data)
|
|
goto fail;
|
|
|
|
if (!mesa_db_read_data(db->cache.file, data, cache_entry.size) ||
|
|
util_hash_crc32(data, cache_entry.size) != cache_entry.crc)
|
|
goto fail_fatal;
|
|
|
|
if (!mesa_db_seek(db->index.file, hash_entry->index_db_file_offset) ||
|
|
!mesa_db_read(db->index.file, &index_entry) ||
|
|
!mesa_db_index_entry_valid(&index_entry) ||
|
|
index_entry.cache_db_file_offset != hash_entry->cache_db_file_offset ||
|
|
index_entry.size != hash_entry->size)
|
|
goto fail_fatal;
|
|
|
|
index_entry.last_access_time = os_time_get_nano();
|
|
hash_entry->last_access_time = index_entry.last_access_time;
|
|
|
|
if (!mesa_db_seek(db->index.file, hash_entry->index_db_file_offset) ||
|
|
!mesa_db_write(db->index.file, &index_entry))
|
|
goto fail_fatal;
|
|
|
|
fflush(db->index.file);
|
|
|
|
mesa_db_unlock(db);
|
|
|
|
*size = cache_entry.size;
|
|
|
|
return data;
|
|
|
|
fail_fatal:
|
|
mesa_db_zap(db);
|
|
fail:
|
|
free(data);
|
|
|
|
mesa_db_unlock(db);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool
|
|
mesa_cache_db_entry_write(struct mesa_cache_db *db,
|
|
const uint8_t *cache_key_160bit,
|
|
const void *blob, size_t blob_size)
|
|
{
|
|
uint64_t hash = to_mesa_cache_db_hash(cache_key_160bit);
|
|
struct mesa_index_db_hash_entry *hash_entry = NULL;
|
|
struct mesa_cache_db_file_entry cache_entry;
|
|
struct mesa_index_db_file_entry index_entry;
|
|
|
|
if (!mesa_db_lock(db))
|
|
return false;
|
|
|
|
if (!db->alive)
|
|
goto fail;
|
|
|
|
if (mesa_db_uuid_changed(db) && !mesa_db_reload(db))
|
|
goto fail_fatal;
|
|
|
|
if (!mesa_db_seek_end(db->cache.file))
|
|
goto fail_fatal;
|
|
|
|
if (ftell(db->cache.file) + blob_file_size(blob_size) > db->max_cache_size) {
|
|
if (!mesa_db_compact(db, MAX2(blob_size, db->max_cache_size / 2)))
|
|
goto fail_fatal;
|
|
} else {
|
|
if (!mesa_db_update_index(db))
|
|
goto fail_fatal;
|
|
}
|
|
|
|
hash_entry = _mesa_hash_table_u64_search(db->index_db, hash);
|
|
if (hash_entry) {
|
|
hash_entry = NULL;
|
|
goto fail;
|
|
}
|
|
|
|
if (!mesa_db_seek_end(db->cache.file) ||
|
|
!mesa_db_seek_end(db->index.file))
|
|
goto fail_fatal;
|
|
|
|
memcpy(cache_entry.key, cache_key_160bit, sizeof(cache_entry.key));
|
|
cache_entry.crc = util_hash_crc32(blob, blob_size);
|
|
cache_entry.size = blob_size;
|
|
|
|
index_entry.hash = hash;
|
|
index_entry.size = blob_size;
|
|
index_entry.last_access_time = os_time_get_nano();
|
|
index_entry.cache_db_file_offset = ftell(db->cache.file);
|
|
|
|
hash_entry = ralloc(db->mem_ctx, struct mesa_index_db_hash_entry);
|
|
if (!hash_entry)
|
|
goto fail;
|
|
|
|
hash_entry->cache_db_file_offset = index_entry.cache_db_file_offset;
|
|
hash_entry->index_db_file_offset = ftell(db->index.file);
|
|
hash_entry->last_access_time = index_entry.last_access_time;
|
|
hash_entry->size = index_entry.size;
|
|
|
|
if (!mesa_db_write(db->cache.file, &cache_entry) ||
|
|
!mesa_db_write_data(db->cache.file, blob, blob_size) ||
|
|
!mesa_db_write(db->index.file, &index_entry))
|
|
goto fail_fatal;
|
|
|
|
fflush(db->cache.file);
|
|
fflush(db->index.file);
|
|
|
|
db->index.offset = ftell(db->index.file);
|
|
|
|
_mesa_hash_table_u64_insert(db->index_db, hash, hash_entry);
|
|
|
|
mesa_db_unlock(db);
|
|
|
|
return true;
|
|
|
|
fail_fatal:
|
|
mesa_db_zap(db);
|
|
fail:
|
|
mesa_db_unlock(db);
|
|
|
|
if (hash_entry)
|
|
ralloc_free(hash_entry);
|
|
|
|
return false;
|
|
}
|
|
|
|
#endif /* DETECT_OS_WINDOWS */
|