util/disk_cache: Add new mesa-db cache type
Introduce new cache type, the Mesa-DB. This is a single-file read/write cache that is based on the read-only Fossilize DB cache. Mesa-DB supports cache size capping. It's a much more efficient cache than the multi-file cache because Mesa-DB doesn't have the inode overhead. The plan is to make Mesa-DB the default cache implementation once it will be deemed as stable and well tested. For now users have to set the new MESA_DISK_CACHE_DATABASE environment variable in order to active the Mesa-DB cache. Mesa-DB cache is resilient to corrupted cache files and doesn't require maintenance from users and developers. The size capping is implemented by evicting least recently used cache items and compacting the cache database files with the evicted entries. In order to prevent frequent compaction of the cache, at minimum a half of cache is evicted when cache is full. Acked-by: Timothy Arceri <tarceri@itsqueeze.com> Reviewed-by: Emil Velikov <emil.velikov@collabora.com> Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/16888>
This commit is contained in:

committed by
Marge Bot

parent
f64f74b8f2
commit
32211788d0
@@ -1,2 +1,3 @@
|
||||
GL_ARB_shader_clock on llvmpipe
|
||||
VK_KHR_shader_clock on lavapipe
|
||||
Mesa-DB, the new single file cache type
|
||||
|
@@ -121,6 +121,11 @@ disk_cache_create(const char *gpu_name, const char *driver_id,
|
||||
if (env_var_as_boolean("MESA_DISK_CACHE_SINGLE_FILE", false)) {
|
||||
if (!disk_cache_load_cache_index_foz(local, cache))
|
||||
goto path_fail;
|
||||
} else if (env_var_as_boolean("MESA_DISK_CACHE_DATABASE", false)) {
|
||||
if (!disk_cache_db_load_cache_index(local, cache))
|
||||
goto path_fail;
|
||||
|
||||
cache->use_cache_db = true;
|
||||
}
|
||||
|
||||
if (!disk_cache_mmap_cache_index(local, cache, path))
|
||||
@@ -176,6 +181,9 @@ disk_cache_create(const char *gpu_name, const char *driver_id,
|
||||
|
||||
cache->max_size = max_size;
|
||||
|
||||
if (cache->use_cache_db)
|
||||
mesa_cache_db_set_size_limit(&cache->cache_db, cache->max_size);
|
||||
|
||||
/* 4 threads were chosen below because just about all modern CPUs currently
|
||||
* available that run Mesa have *at least* 4 cores. For these CPUs allowing
|
||||
* more threads can result in the queue being processed faster, thus
|
||||
@@ -252,6 +260,9 @@ disk_cache_destroy(struct disk_cache *cache)
|
||||
if (env_var_as_boolean("MESA_DISK_CACHE_SINGLE_FILE", false))
|
||||
foz_destroy(&cache->foz_db);
|
||||
|
||||
if (cache->use_cache_db)
|
||||
mesa_cache_db_close(&cache->cache_db);
|
||||
|
||||
disk_cache_destroy_mmap(cache);
|
||||
}
|
||||
|
||||
@@ -354,6 +365,8 @@ cache_put(void *job, void *gdata, int thread_index)
|
||||
|
||||
if (env_var_as_boolean("MESA_DISK_CACHE_SINGLE_FILE", false)) {
|
||||
disk_cache_write_item_to_disk_foz(dc_job);
|
||||
} else if (dc_job->cache->use_cache_db) {
|
||||
disk_cache_db_write_item_to_disk(dc_job);
|
||||
} else {
|
||||
filename = disk_cache_get_cache_filename(dc_job->cache, dc_job->key);
|
||||
if (filename == NULL)
|
||||
@@ -452,6 +465,8 @@ disk_cache_get(struct disk_cache *cache, const cache_key key, size_t *size)
|
||||
|
||||
if (env_var_as_boolean("MESA_DISK_CACHE_SINGLE_FILE", false)) {
|
||||
return disk_cache_load_item_foz(cache, key, size);
|
||||
} else if (cache->use_cache_db) {
|
||||
return disk_cache_db_load_item(cache, key, size);
|
||||
} else {
|
||||
char *filename = disk_cache_get_cache_filename(cache, key);
|
||||
if (filename == NULL)
|
||||
|
@@ -45,6 +45,7 @@ extern "C" {
|
||||
|
||||
#define CACHE_DIR_NAME "mesa_shader_cache"
|
||||
#define CACHE_DIR_NAME_SF "mesa_shader_cache_sf"
|
||||
#define CACHE_DIR_NAME_DB "mesa_shader_cache_db"
|
||||
|
||||
typedef uint8_t cache_key[CACHE_KEY_SIZE];
|
||||
|
||||
|
@@ -831,6 +831,8 @@ disk_cache_generate_cache_dir(void *mem_ctx, const char *gpu_name,
|
||||
char *cache_dir_name = CACHE_DIR_NAME;
|
||||
if (env_var_as_boolean("MESA_DISK_CACHE_SINGLE_FILE", false))
|
||||
cache_dir_name = CACHE_DIR_NAME_SF;
|
||||
else if (env_var_as_boolean("MESA_DISK_CACHE_DATABASE", false))
|
||||
cache_dir_name = CACHE_DIR_NAME_DB;
|
||||
|
||||
char *path = getenv("MESA_SHADER_CACHE_DIR");
|
||||
|
||||
@@ -1042,6 +1044,45 @@ disk_cache_destroy_mmap(struct disk_cache *cache)
|
||||
{
|
||||
munmap(cache->index_mmap, cache->index_mmap_size);
|
||||
}
|
||||
|
||||
void *
|
||||
disk_cache_db_load_item(struct disk_cache *cache, const cache_key key,
|
||||
size_t *size)
|
||||
{
|
||||
size_t cache_tem_size = 0;
|
||||
void *cache_item = mesa_cache_db_read_entry(&cache->cache_db, key,
|
||||
&cache_tem_size);
|
||||
if (!cache_item)
|
||||
return NULL;
|
||||
|
||||
uint8_t *uncompressed_data =
|
||||
parse_and_validate_cache_item(cache, cache_item, cache_tem_size, size);
|
||||
free(cache_item);
|
||||
|
||||
return uncompressed_data;
|
||||
}
|
||||
|
||||
bool
|
||||
disk_cache_db_write_item_to_disk(struct disk_cache_put_job *dc_job)
|
||||
{
|
||||
struct blob cache_blob;
|
||||
blob_init(&cache_blob);
|
||||
|
||||
if (!create_cache_item_header_and_blob(dc_job, &cache_blob))
|
||||
return false;
|
||||
|
||||
bool r = mesa_cache_db_entry_write(&dc_job->cache->cache_db, dc_job->key,
|
||||
cache_blob.data, cache_blob.size);
|
||||
|
||||
blob_finish(&cache_blob);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool
|
||||
disk_cache_db_load_cache_index(void *mem_ctx, struct disk_cache *cache)
|
||||
{
|
||||
return mesa_cache_db_open(&cache->cache_db, cache->path);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ENABLE_SHADER_CACHE */
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#else
|
||||
|
||||
#include "util/fossilize_db.h"
|
||||
#include "util/mesa_cache_db.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -57,6 +58,10 @@ struct disk_cache {
|
||||
|
||||
struct foz_db foz_db;
|
||||
|
||||
struct mesa_cache_db cache_db;
|
||||
|
||||
bool use_cache_db;
|
||||
|
||||
/* Seed for rand, which is used to pick a random directory */
|
||||
uint64_t seed_xorshift128plus[2];
|
||||
|
||||
@@ -145,6 +150,16 @@ disk_cache_mmap_cache_index(void *mem_ctx, struct disk_cache *cache,
|
||||
void
|
||||
disk_cache_destroy_mmap(struct disk_cache *cache);
|
||||
|
||||
void *
|
||||
disk_cache_db_load_item(struct disk_cache *cache, const cache_key key,
|
||||
size_t *size);
|
||||
|
||||
bool
|
||||
disk_cache_db_write_item_to_disk(struct disk_cache_put_job *dc_job);
|
||||
|
||||
bool
|
||||
disk_cache_db_load_cache_index(void *mem_ctx, struct disk_cache *cache);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
810
src/util/mesa_cache_db.c
Normal file
810
src/util/mesa_cache_db.c
Normal file
@@ -0,0 +1,810 @@
|
||||
/*
|
||||
* 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 <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 */
|
110
src/util/mesa_cache_db.h
Normal file
110
src/util/mesa_cache_db.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora, Ltd.
|
||||
*
|
||||
* Based on Fossilize DB:
|
||||
* Copyright © 2020 Valve Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MESA_CACHE_DB_H
|
||||
#define MESA_CACHE_DB_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "detect_os.h"
|
||||
#include "simple_mtx.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct mesa_cache_db_file {
|
||||
FILE *file;
|
||||
char *path;
|
||||
off_t offset;
|
||||
uint64_t uuid;
|
||||
};
|
||||
|
||||
struct mesa_cache_db {
|
||||
struct hash_table_u64 *index_db;
|
||||
struct mesa_cache_db_file cache;
|
||||
struct mesa_cache_db_file index;
|
||||
uint64_t max_cache_size;
|
||||
simple_mtx_t flock_mtx;
|
||||
void *mem_ctx;
|
||||
uint64_t uuid;
|
||||
bool alive;
|
||||
};
|
||||
|
||||
#if DETECT_OS_WINDOWS == 0
|
||||
bool
|
||||
mesa_cache_db_open(struct mesa_cache_db *db, const char *cache_path);
|
||||
|
||||
void
|
||||
mesa_cache_db_close(struct mesa_cache_db *db);
|
||||
|
||||
void
|
||||
mesa_cache_db_set_size_limit(struct mesa_cache_db *db,
|
||||
uint64_t max_cache_size);
|
||||
|
||||
unsigned int
|
||||
mesa_cache_db_file_entry_size(void);
|
||||
|
||||
void *
|
||||
mesa_cache_db_read_entry(struct mesa_cache_db *db,
|
||||
const uint8_t *cache_key_160bit,
|
||||
size_t *size);
|
||||
|
||||
bool
|
||||
mesa_cache_db_entry_write(struct mesa_cache_db *db,
|
||||
const uint8_t *cache_key_160bit,
|
||||
const void *blob, size_t blob_size);
|
||||
#else
|
||||
static inline bool
|
||||
mesa_cache_db_open(struct mesa_cache_db *db, const char *cache_path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void
|
||||
mesa_cache_db_close(struct mesa_cache_db *db)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void
|
||||
mesa_cache_db_set_size_limit(struct mesa_cache_db *db,
|
||||
uint64_t max_cache_size)
|
||||
{
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
mesa_cache_db_file_entry_size(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void *
|
||||
mesa_cache_db_read_entry(struct mesa_cache_db *db,
|
||||
const uint8_t *cache_key_160bit,
|
||||
size_t *size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
mesa_cache_db_entry_write(struct mesa_cache_db *db,
|
||||
const uint8_t *cache_key_160bit,
|
||||
const void *blob, size_t blob_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* DETECT_OS_WINDOWS */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MESA_CACHE_DB_H */
|
@@ -159,6 +159,8 @@ files_mesa_util = files(
|
||||
'indices/u_indices_priv.h',
|
||||
'indices/u_primconvert.c',
|
||||
'indices/u_primconvert.h',
|
||||
'mesa_cache_db.c',
|
||||
'mesa_cache_db.h',
|
||||
)
|
||||
|
||||
files_drirc = files('00-mesa-defaults.conf')
|
||||
|
@@ -39,6 +39,7 @@
|
||||
|
||||
#include "util/mesa-sha1.h"
|
||||
#include "util/disk_cache.h"
|
||||
#include "util/disk_cache_os.h"
|
||||
#include "util/ralloc.h"
|
||||
|
||||
#ifdef ENABLE_SHADER_CACHE
|
||||
@@ -520,6 +521,136 @@ test_put_and_get_between_instances(const char *driver_id)
|
||||
disk_cache_destroy(cache1);
|
||||
disk_cache_destroy(cache2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_put_and_get_between_instances_with_eviction(const char *driver_id)
|
||||
{
|
||||
cache_key small_key[8], small_key2, big_key[2];
|
||||
struct disk_cache *cache[2];
|
||||
unsigned int i, n, k;
|
||||
uint8_t *small;
|
||||
uint8_t *big;
|
||||
char *result;
|
||||
size_t size;
|
||||
|
||||
#ifdef SHADER_CACHE_DISABLE_BY_DEFAULT
|
||||
setenv("MESA_SHADER_CACHE_DISABLE", "false", 1);
|
||||
#endif /* SHADER_CACHE_DISABLE_BY_DEFAULT */
|
||||
|
||||
setenv("MESA_SHADER_CACHE_MAX_SIZE", "2K", 1);
|
||||
|
||||
cache[0] = disk_cache_create("test_between_instances_with_eviction", driver_id, 0);
|
||||
cache[1] = disk_cache_create("test_between_instances_with_eviction", driver_id, 0);
|
||||
|
||||
uint8_t two_KB[2048] = { 0 };
|
||||
cache_key two_KB_key = { 'T', 'W', 'O', 'K', 'B' };
|
||||
|
||||
/* Flush the database by adding the dummy 2KB entry */
|
||||
disk_cache_put(cache[0], two_KB_key, two_KB, sizeof(two_KB), NULL);
|
||||
disk_cache_wait_for_idle(cache[0]);
|
||||
|
||||
int size_big = 1000;
|
||||
size_big -= sizeof(struct cache_entry_file_data);
|
||||
size_big -= mesa_cache_db_file_entry_size();
|
||||
size_big -= cache[0]->driver_keys_blob_size;
|
||||
size_big -= 4 + 8; /* cache_item_metadata size + room for alignment */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(big_key); i++) {
|
||||
big = (uint8_t *) malloc(size_big);
|
||||
memset(big, i, size_big);
|
||||
|
||||
disk_cache_compute_key(cache[0], big, size_big, big_key[i]);
|
||||
disk_cache_put(cache[0], big_key[i], big, size_big, NULL);
|
||||
disk_cache_wait_for_idle(cache[0]);
|
||||
|
||||
result = (char *) disk_cache_get(cache[0], big_key[i], &size);
|
||||
EXPECT_NE(result, nullptr) << "disk_cache_get with existent item (pointer)";
|
||||
EXPECT_EQ(size, size_big) << "disk_cache_get with existent item (size)";
|
||||
free(result);
|
||||
|
||||
free(big);
|
||||
}
|
||||
|
||||
int size_small = 256;
|
||||
size_small -= sizeof(struct cache_entry_file_data);
|
||||
size_small -= mesa_cache_db_file_entry_size();
|
||||
size_small -= cache[1]->driver_keys_blob_size;
|
||||
size_small -= 4 + 8; /* cache_item_metadata size + room for alignment */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(small_key); i++) {
|
||||
small = (uint8_t *) malloc(size_small);
|
||||
memset(small, i, size_small);
|
||||
|
||||
disk_cache_compute_key(cache[1], small, size_small, small_key[i]);
|
||||
disk_cache_put(cache[1], small_key[i], small, size_small, NULL);
|
||||
disk_cache_wait_for_idle(cache[1]);
|
||||
|
||||
/*
|
||||
* At first we added two 1000KB entries to cache[0]. Now, when first
|
||||
* 256KB entry is added, the two 1000KB entries are evicted because
|
||||
* at minimum cache_max_size/2 is evicted on overflow.
|
||||
*
|
||||
* All four 256KB entries stay in the cache.
|
||||
*/
|
||||
for (k = 0; k < ARRAY_SIZE(cache); k++) {
|
||||
for (n = 0; n <= i; n++) {
|
||||
result = (char *) disk_cache_get(cache[k], big_key[0], &size);
|
||||
EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
|
||||
EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
|
||||
free(result);
|
||||
|
||||
result = (char *) disk_cache_get(cache[k], big_key[1], &size);
|
||||
EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)";
|
||||
EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)";
|
||||
free(result);
|
||||
|
||||
result = (char *) disk_cache_get(cache[k], small_key[n], &size);
|
||||
EXPECT_NE(result, nullptr) << "disk_cache_get of existing item (pointer)";
|
||||
EXPECT_EQ(size, size_small) << "disk_cache_get of existing item (size)";
|
||||
free(result);
|
||||
|
||||
result = (char *) disk_cache_get(cache[k], small_key[n], &size);
|
||||
EXPECT_NE(result, nullptr) << "disk_cache_get of existing item (pointer)";
|
||||
EXPECT_EQ(size, size_small) << "disk_cache_get of existing item (size)";
|
||||
free(result);
|
||||
}
|
||||
}
|
||||
|
||||
free(small);
|
||||
}
|
||||
|
||||
small = (uint8_t *) malloc(size_small);
|
||||
memset(small, i, size_small);
|
||||
|
||||
/* Add another 256KB entry. This will evict first five 256KB entries
|
||||
* of eight that we added previously. */
|
||||
disk_cache_compute_key(cache[0], small, size_small, small_key2);
|
||||
disk_cache_put(cache[0], small_key2, small, size_small, NULL);
|
||||
disk_cache_wait_for_idle(cache[0]);
|
||||
|
||||
free(small);
|
||||
|
||||
for (k = 0; k < ARRAY_SIZE(cache); k++) {
|
||||
result = (char *) disk_cache_get(cache[k], small_key2, &size);
|
||||
EXPECT_NE(result, nullptr) << "disk_cache_get of existing item (pointer)";
|
||||
EXPECT_EQ(size, size_small) << "disk_cache_get of existing item (size)";
|
||||
free(result);
|
||||
}
|
||||
|
||||
for (i = 0, k = 0; k < ARRAY_SIZE(cache); k++) {
|
||||
for (n = 0; n < ARRAY_SIZE(small_key); n++) {
|
||||
result = (char *) disk_cache_get(cache[k], small_key[n], &size);
|
||||
if (!result)
|
||||
i++;
|
||||
free(result);
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(i, 10) << "2x disk_cache_get with 5 non-existent 256KB items";
|
||||
|
||||
disk_cache_destroy(cache[0]);
|
||||
disk_cache_destroy(cache[1]);
|
||||
}
|
||||
#endif /* ENABLE_SHADER_CACHE */
|
||||
|
||||
class Cache : public ::testing::Test {
|
||||
@@ -604,3 +735,41 @@ run_tests:
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(Cache, Database)
|
||||
{
|
||||
const char *driver_id = "make_check_uncompressed";
|
||||
|
||||
#ifndef ENABLE_SHADER_CACHE
|
||||
GTEST_SKIP() << "ENABLE_SHADER_CACHE not defined.";
|
||||
#else
|
||||
setenv("MESA_DISK_CACHE_DATABASE", "true", 1);
|
||||
|
||||
test_disk_cache_create(mem_ctx, CACHE_DIR_NAME_DB, driver_id);
|
||||
|
||||
/* We skip testing cache size limit as the single file cache compresses
|
||||
* data much better than the multi-file cache, which results in the
|
||||
* failing tests of the cache eviction function. We we will test the
|
||||
* eviction separately with the disabled compression.
|
||||
*/
|
||||
test_put_and_get(false, driver_id);
|
||||
|
||||
int err = rmrf_local(CACHE_TEST_TMP);
|
||||
EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again";
|
||||
|
||||
test_disk_cache_create(mem_ctx, CACHE_DIR_NAME_DB, driver_id);
|
||||
|
||||
test_put_and_get(true, driver_id);
|
||||
|
||||
test_put_key_and_get_key(driver_id);
|
||||
|
||||
test_put_and_get_between_instances(driver_id);
|
||||
|
||||
test_put_and_get_between_instances_with_eviction(driver_id);
|
||||
|
||||
setenv("MESA_DISK_CACHE_DATABASE", "false", 1);
|
||||
|
||||
err = rmrf_local(CACHE_TEST_TMP);
|
||||
EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again";
|
||||
#endif
|
||||
}
|
||||
|
Reference in New Issue
Block a user