util/disk_cache: Support combined foz ro and non-foz rw caches

Mesa utilizes only one type of cache at a time. This patch enables support
for combined reading from read-only Fossilize cache + non-foz read-write
caches.

From now on, a non-foz read-write caches will first try to retrieve data
from a read-only foz cache if new MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ
environment variable is set to true, otherwise the caching behaviour is
unchanged. The new flag has no effect when MESA_DISK_CACHE_SINGLE_FILE=1,
i.e. when the single-file foz cache is used.

This change allows us to ship a prebuilt RO caches for a certain
applications, while the rest of applications will benefit from the
regular RW caching that supports cache-size limitation. This feature
will be used by ChromeOS.

Usage example #1:

MESA_DISK_CACHE_DATABASE=0
MESA_DISK_CACHE_SINGLE_FILE=0
MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ=1
MESA_DISK_CACHE_READ_ONLY_FOZ_DBS=rocache1,rocache2

Usage example #2:

MESA_DISK_CACHE_DATABASE=1
MESA_DISK_CACHE_SINGLE_FILE=0
MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ=1
MESA_DISK_CACHE_READ_ONLY_FOZ_DBS=rocache1,rocache2

Reviewed-by: Timothy Arceri <tarceri@itsqueeze.com>
Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/18551>
This commit is contained in:
Dmitry Osipenko
2022-09-12 00:32:30 +03:00
committed by Marge Bot
parent 75dae4f8e3
commit 32fe60e8c4
4 changed files with 215 additions and 18 deletions

View File

@@ -278,6 +278,23 @@ disk_cache_create(const char *gpu_name, const char *driver_id,
if (!cache)
return NULL;
/* If MESA_DISK_CACHE_SINGLE_FILE is unset and MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ
* is set, then enable additional Fossilize RO caches together with the RW
* cache. At first we will check cache entry presence in the RO caches and
* if entry isn't found there, then we'll fall back to the RW cache.
*/
if (cache_type != DISK_CACHE_SINGLE_FILE && !cache->path_init_failed &&
debug_get_bool_option("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", false)) {
/* Create read-only cache used for sharing prebuilt shaders.
* If cache entry will be found in this cache, then the main cache
* will be bypassed.
*/
cache->foz_ro_cache = disk_cache_type_create(gpu_name, driver_id,
driver_flags,
DISK_CACHE_SINGLE_FILE);
}
return cache;
}
@@ -294,6 +311,9 @@ disk_cache_destroy(struct disk_cache *cache)
util_queue_finish(&cache->cache_queue);
util_queue_destroy(&cache->cache_queue);
if (cache->foz_ro_cache)
disk_cache_destroy(cache->foz_ro_cache);
if (cache->type == DISK_CACHE_SINGLE_FILE)
foz_destroy(&cache->foz_db);
@@ -565,6 +585,8 @@ disk_cache_get(struct disk_cache *cache, const cache_key key, size_t *size)
if (cache->blob_get_cb) {
buf = blob_get_compressed(cache, key, size);
} else if (cache->foz_ro_cache) {
buf = disk_cache_load_item_foz(cache->foz_ro_cache, key, size);
} else if (cache->type == DISK_CACHE_SINGLE_FILE) {
buf = disk_cache_load_item_foz(cache, key, size);
} else if (cache->type == DISK_CACHE_DATABASE) {

View File

@@ -99,6 +99,9 @@ struct disk_cache {
unsigned hits;
unsigned misses;
} stats;
/* Internal RO FOZ cache for combined use of RO and RW caches. */
struct disk_cache *foz_ro_cache;
};
struct cache_entry_file_data {

View File

@@ -40,6 +40,8 @@
#include <sys/types.h>
#include <unistd.h>
#include "util/u_debug.h"
#include "crc32.h"
#include "hash_table.h"
#include "mesa-sha1.h"
@@ -270,28 +272,32 @@ foz_prepare(struct foz_db *foz_db, char *cache_path)
{
char *filename = NULL;
char *idx_filename = NULL;
if (!create_foz_db_filenames(cache_path, "foz_cache", &filename, &idx_filename))
goto fail;
/* Open the default foz dbs for read/write. If the files didn't already exist
* create them.
*/
foz_db->file[0] = fopen(filename, "a+b");
foz_db->db_idx = fopen(idx_filename, "a+b");
free(filename);
free(idx_filename);
if (!check_files_opened_successfully(foz_db->file[0], foz_db->db_idx))
goto fail;
simple_mtx_init(&foz_db->mtx, mtx_plain);
simple_mtx_init(&foz_db->flock_mtx, mtx_plain);
foz_db->mem_ctx = ralloc_context(NULL);
foz_db->index_db = _mesa_hash_table_u64_create(NULL);
if (!load_foz_dbs(foz_db, foz_db->db_idx, 0, false))
goto fail;
/* Open the default foz dbs for read/write. If the files didn't already exist
* create them.
*/
if (debug_get_bool_option("MESA_DISK_CACHE_SINGLE_FILE", false)) {
if (!create_foz_db_filenames(cache_path, "foz_cache",
&filename, &idx_filename))
goto fail;
foz_db->file[0] = fopen(filename, "a+b");
foz_db->db_idx = fopen(idx_filename, "a+b");
free(filename);
free(idx_filename);
if (!check_files_opened_successfully(foz_db->file[0], foz_db->db_idx))
goto fail;
if (!load_foz_dbs(foz_db, foz_db->db_idx, 0, false))
goto fail;
}
uint8_t file_idx = 1;
char *foz_dbs = getenv("MESA_DISK_CACHE_READ_ONLY_FOZ_DBS");
@@ -383,7 +389,7 @@ foz_read_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit,
struct foz_db_entry *entry =
_mesa_hash_table_u64_search(foz_db->index_db, hash);
if (!entry) {
if (!entry && foz_db->db_idx) {
update_foz_index(foz_db, foz_db->db_idx, 0);
entry = _mesa_hash_table_u64_search(foz_db->index_db, hash);
}
@@ -444,7 +450,7 @@ foz_write_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit,
{
uint64_t hash = truncate_hash_to_64bits(cache_key_160bit);
if (!foz_db->alive)
if (!foz_db->alive || !foz_db->file[0])
return false;
/* The flock is per-fd, not per thread, we do it outside of the main mutex to avoid having to

View File

@@ -773,3 +773,169 @@ TEST_F(Cache, Database)
EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again";
#endif
}
TEST_F(Cache, Combined)
{
const char *driver_id = "make_check";
char blob[] = "This is a RO blob";
uint8_t dummy_key[20] = { 0 };
uint8_t blob_key[20];
char foz_rw_idx_file[1024];
char foz_ro_idx_file[1024];
char foz_rw_file[1024];
char foz_ro_file[1024];
char *result;
size_t size;
#ifndef ENABLE_SHADER_CACHE
GTEST_SKIP() << "ENABLE_SHADER_CACHE not defined.";
#else
setenv("MESA_DISK_CACHE_SINGLE_FILE", "true", 1);
setenv("MESA_DISK_CACHE_DATABASE", "false", 1);
#ifdef SHADER_CACHE_DISABLE_BY_DEFAULT
setenv("MESA_SHADER_CACHE_DISABLE", "false", 1);
#endif /* SHADER_CACHE_DISABLE_BY_DEFAULT */
/* Enable Fossilize read-write cache. */
setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "true", 1);
test_disk_cache_create(mem_ctx, CACHE_DIR_NAME_SF, driver_id);
/* Create Fossilize writable cache. */
struct disk_cache *cache_sf_wr = disk_cache_create("combined_test",
driver_id, 0);
disk_cache_compute_key(cache_sf_wr, blob, sizeof(blob), blob_key);
/* Ensure that disk_cache_get returns nothing before anything is added. */
result = (char *) disk_cache_get(cache_sf_wr, blob_key, &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)";
/* Put blob entry to the cache. */
disk_cache_put(cache_sf_wr, blob_key, blob, sizeof(blob), NULL);
disk_cache_wait_for_idle(cache_sf_wr);
result = (char *) disk_cache_get(cache_sf_wr, blob_key, &size);
EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
free(result);
/* Rename file foz_cache.foz -> ro_cache.foz */
sprintf(foz_rw_file, "%s/foz_cache.foz", cache_sf_wr->path);
sprintf(foz_ro_file, "%s/ro_cache.foz", cache_sf_wr->path);
EXPECT_EQ(rename(foz_rw_file, foz_ro_file), 0) << "foz_cache.foz renaming failed";
/* Rename file foz_cache_idx.foz -> ro_cache_idx.foz */
sprintf(foz_rw_idx_file, "%s/foz_cache_idx.foz", cache_sf_wr->path);
sprintf(foz_ro_idx_file, "%s/ro_cache_idx.foz", cache_sf_wr->path);
EXPECT_EQ(rename(foz_rw_idx_file, foz_ro_idx_file), 0) << "foz_cache_idx.foz renaming failed";
disk_cache_destroy(cache_sf_wr);
/* Disable Fossilize read-write cache. */
setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "false", 1);
/* Set up Fossilize read-only cache. */
setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "true", 1);
setenv("MESA_DISK_CACHE_READ_ONLY_FOZ_DBS", "ro_cache", 1);
/* Create FOZ cache that fetches the RO cache. Note that this produces
* empty RW cache files. */
struct disk_cache *cache_sf_ro = disk_cache_create("combined_test",
driver_id, 0);
/* Blob entry must present because it shall be retrieved from the
* ro_cache.foz */
result = (char *) disk_cache_get(cache_sf_ro, blob_key, &size);
EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
free(result);
disk_cache_destroy(cache_sf_ro);
/* Remove empty FOZ RW cache files created above. We only need RO cache. */
EXPECT_EQ(unlink(foz_rw_file), 0);
EXPECT_EQ(unlink(foz_rw_idx_file), 0);
setenv("MESA_DISK_CACHE_SINGLE_FILE", "false", 1);
setenv("MESA_DISK_CACHE_DATABASE", "true", 1);
/* Create MESA-DB cache with enabled retrieval from the read-only
* cache. */
struct disk_cache *cache_mesa_db = disk_cache_create("combined_test",
driver_id, 0);
/* Dummy entry must not present in any of the caches. Foz cache
* reloads index if cache entry is missing. This is a sanity-check
* for foz_read_entry(), it should work properly with a disabled
* FOZ RW cache. */
result = (char *) disk_cache_get(cache_mesa_db, dummy_key, &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)";
/* Blob entry must present because it shall be retrieved from the
* read-only cache. */
result = (char *) disk_cache_get(cache_mesa_db, blob_key, &size);
EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
free(result);
disk_cache_destroy(cache_mesa_db);
/* Disable read-only cache. */
setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "false", 1);
/* Create MESA-DB cache with disabled retrieval from the read-only
* cache. */
cache_mesa_db = disk_cache_create("combined_test", driver_id, 0);
/* Blob entry must not present in the cache because we disable the
* read-only cache. */
result = (char *) disk_cache_get(cache_mesa_db, blob_key, &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)";
disk_cache_destroy(cache_mesa_db);
/* Create default multi-file cache. */
setenv("MESA_DISK_CACHE_DATABASE", "false", 1);
/* Enable read-only cache. */
setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "true", 1);
/* Create multi-file cache with enabled retrieval from the
* read-only cache. */
struct disk_cache *cache_multifile = disk_cache_create("combined_test",
driver_id, 0);
/* Blob entry must present because it shall be retrieved from the
* read-only cache. */
result = (char *) disk_cache_get(cache_multifile, blob_key, &size);
EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)";
EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)";
free(result);
disk_cache_destroy(cache_multifile);
/* Disable read-only cache. */
setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "false", 1);
unsetenv("MESA_DISK_CACHE_READ_ONLY_FOZ_DBS");
/* Create multi-file cache with disabled retrieval from the
* read-only cache. */
cache_multifile = disk_cache_create("combined_test", driver_id, 0);
/* Blob entry must not present in the cache because we disabled the
* read-only cache. */
result = (char *) disk_cache_get(cache_multifile, blob_key, &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)";
disk_cache_destroy(cache_multifile);
int err = rmrf_local(CACHE_TEST_TMP);
EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again";
#endif
}