Module: Mesa
Branch: main
Commit: 32fe60e8c429a070f890840422a64f1e7795eb5a
URL:    
http://cgit.freedesktop.org/mesa/mesa/commit/?id=32fe60e8c429a070f890840422a64f1e7795eb5a

Author: Dmitry Osipenko <[email protected]>
Date:   Mon Sep 12 00:32:30 2022 +0300

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 <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/18551>

---

 src/util/disk_cache.c         |  22 ++++++
 src/util/disk_cache_os.h      |   3 +
 src/util/fossilize_db.c       |  38 ++++++----
 src/util/tests/cache_test.cpp | 166 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 213 insertions(+), 16 deletions(-)

diff --git a/src/util/disk_cache.c b/src/util/disk_cache.c
index e2133372185..2280f53ac97 100644
--- a/src/util/disk_cache.c
+++ b/src/util/disk_cache.c
@@ -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) {
diff --git a/src/util/disk_cache_os.h b/src/util/disk_cache_os.h
index d36bf3d343e..b54af0b38aa 100644
--- a/src/util/disk_cache_os.h
+++ b/src/util/disk_cache_os.h
@@ -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 {
diff --git a/src/util/fossilize_db.c b/src/util/fossilize_db.c
index 6c2bf0c58e4..bfedc42e101 100644
--- a/src/util/fossilize_db.c
+++ b/src/util/fossilize_db.c
@@ -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;
+
+   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);
 
    /* 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");
+   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;
 
-   free(filename);
-   free(idx_filename);
+      foz_db->file[0] = fopen(filename, "a+b");
+      foz_db->db_idx = fopen(idx_filename, "a+b");
 
-   if (!check_files_opened_successfully(foz_db->file[0], foz_db->db_idx))
-      goto fail;
+      free(filename);
+      free(idx_filename);
 
-   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 (!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;
+      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
diff --git a/src/util/tests/cache_test.cpp b/src/util/tests/cache_test.cpp
index f0164b4fc72..5d0e7d9b293 100644
--- a/src/util/tests/cache_test.cpp
+++ b/src/util/tests/cache_test.cpp
@@ -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
+}

Reply via email to