#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <sys/statvfs.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include "util/mesa-sha1.h"
#include "util/ralloc.h"
#include "main/errors.h"
-#include "util/macros.h"
#include "disk_cache.h"
/* The number of keys that can be stored in the index. */
#define CACHE_INDEX_MAX_KEYS (1 << CACHE_INDEX_KEY_BITS)
+/* The cache version should be bumped whenever a change is made to the
+ * structure of cache entries or the index. This will give any 3rd party
+ * applications reading the cache entries a chance to adjust to the changes.
+ *
+ * - The cache version is checked internally when reading a cache entry. If we
+ * ever have a mismatch we are in big trouble as this means we had a cache
+ * collision. In case of such an event please check the skys for giant
+ * asteroids and that the entire Mesa team hasn't been eaten by wolves.
+ *
+ * - There is no strict requirement that cache versions be backwards
+ * compatible but effort should be taken to limit disruption where possible.
+ */
+#define CACHE_VERSION 1
+
struct disk_cache {
/* The path to the cache directory. */
char *path;
return NULL;
}
+#define DRV_KEY_CPY(_dst, _src, _src_size) \
+do { \
+ memcpy(_dst, _src, _src_size); \
+ _dst += _src_size; \
+} while (0);
+
struct disk_cache *
-disk_cache_create(const char *gpu_name, const char *timestamp)
+disk_cache_create(const char *gpu_name, const char *timestamp,
+ uint64_t driver_flags)
{
void *local;
struct disk_cache *cache = NULL;
uint64_t max_size;
int fd = -1;
struct stat sb;
- struct statvfs vfs = { 0 };
size_t size;
/* If running as a users other than the real user disable cache */
/* Determine path for cache based on the first defined name as follows:
*
* $MESA_GLSL_CACHE_DIR
- * $XDG_CACHE_HOME/mesa
- * <pwd.pw_dir>/.cache/mesa
+ * $XDG_CACHE_HOME/mesa_shader_cache
+ * <pwd.pw_dir>/.cache/mesa_shader_cache
*/
path = getenv("MESA_GLSL_CACHE_DIR");
if (path) {
if (mkdir_if_needed(path) == -1)
goto fail;
- path = concatenate_and_mkdir(local, path, "mesa");
+ path = concatenate_and_mkdir(local, path, CACHE_DIR_NAME);
if (path == NULL)
goto fail;
}
if (mkdir_if_needed(xdg_cache_home) == -1)
goto fail;
- path = concatenate_and_mkdir(local, xdg_cache_home, "mesa");
+ path = concatenate_and_mkdir(local, xdg_cache_home, CACHE_DIR_NAME);
if (path == NULL)
goto fail;
}
if (path == NULL)
goto fail;
- path = concatenate_and_mkdir(local, path, "mesa");
+ path = concatenate_and_mkdir(local, path, CACHE_DIR_NAME);
if (path == NULL)
goto fail;
}
}
}
- /* Default to 1GB or 10% of filesystem for maximum cache size. */
+ /* Default to 1GB for maximum cache size. */
if (max_size == 0) {
- statvfs(path, &vfs);
- max_size = MAX2(1024*1024*1024, vfs.f_blocks * vfs.f_bsize / 10);
+ max_size = 1024*1024*1024;
}
cache->max_size = max_size;
* really care about getting things to disk quickly just that it's not
* blocking other tasks.
*/
- util_queue_init(&cache->cache_queue, "disk_cache", 32, 1);
+ util_queue_init(&cache->cache_queue, "disk_cache", 32, 1, 0);
+
+ uint8_t cache_version = CACHE_VERSION;
+ size_t cv_size = sizeof(cache_version);
+ cache->driver_keys_blob_size = cv_size;
/* Create driver id keys */
size_t ts_size = strlen(timestamp) + 1;
size_t gpu_name_size = strlen(gpu_name) + 1;
- cache->driver_keys_blob_size = ts_size;
+ cache->driver_keys_blob_size += ts_size;
cache->driver_keys_blob_size += gpu_name_size;
/* We sometimes store entire structs that contains a pointers in the cache,
size_t ptr_size_size = sizeof(ptr_size);
cache->driver_keys_blob_size += ptr_size_size;
+ size_t driver_flags_size = sizeof(driver_flags);
+ cache->driver_keys_blob_size += driver_flags_size;
+
cache->driver_keys_blob =
ralloc_size(cache, cache->driver_keys_blob_size);
if (!cache->driver_keys_blob)
goto fail;
- memcpy(cache->driver_keys_blob, timestamp, ts_size);
- memcpy(cache->driver_keys_blob + ts_size, gpu_name, gpu_name_size);
- memcpy(cache->driver_keys_blob + ts_size + gpu_name_size, &ptr_size,
- ptr_size_size);
+ uint8_t *drv_key_blob = cache->driver_keys_blob;
+ DRV_KEY_CPY(drv_key_blob, &cache_version, cv_size)
+ DRV_KEY_CPY(drv_key_blob, timestamp, ts_size)
+ DRV_KEY_CPY(drv_key_blob, gpu_name, gpu_name_size)
+ DRV_KEY_CPY(drv_key_blob, &ptr_size, ptr_size_size)
+ DRV_KEY_CPY(drv_key_blob, &driver_flags, driver_flags_size)
/* Seed our rand function */
s_rand_xorshift128plus(cache->seed_xorshift128plus, true);
unlink(filename);
free (filename);
- return sb.st_size;
+ return sb.st_blocks * 512;
}
/* Is entry a directory with a two-character name, (and not the
unlink(filename);
free(filename);
- if (sb.st_size)
- p_atomic_add(cache->size, - (uint64_t)sb.st_size);
+ if (sb.st_blocks)
+ p_atomic_add(cache->size, - (uint64_t)sb.st_blocks * 512);
}
static ssize_t
goto done;
}
- file_size += cf_data_size + dc_job->cache->driver_keys_blob_size;
- p_atomic_add(dc_job->cache->size, file_size);
+ struct stat sb;
+ if (stat(filename, &sb) == -1) {
+ /* Something went wrong remove the file */
+ unlink(filename);
+ goto done;
+ }
+
+ p_atomic_add(dc_job->cache->size, sb.st_blocks * 512);
done:
if (fd_final != -1)
char *filename = NULL;
uint8_t *data = NULL;
uint8_t *uncompressed_data = NULL;
+ uint8_t *file_header = NULL;
if (size)
*size = 0;
goto fail;
size_t ck_size = cache->driver_keys_blob_size;
-#ifndef NDEBUG
- uint8_t *file_header = malloc(ck_size);
+ file_header = malloc(ck_size);
if (!file_header)
goto fail;
- assert(sb.st_size > ck_size);
- ret = read_all(fd, file_header, ck_size);
- if (ret == -1) {
- free(file_header);
+ if (sb.st_size < ck_size)
goto fail;
- }
-
- assert(memcmp(cache->driver_keys_blob, file_header, ck_size) == 0);
- free(file_header);
-#else
- /* The cache keys are currently just used for distributing precompiled
- * shaders, they are not used by Mesa so just skip them for now.
- */
- ret = lseek(fd, ck_size, SEEK_CUR);
+ ret = read_all(fd, file_header, ck_size);
if (ret == -1)
goto fail;
-#endif
+
+ /* Check for extremely unlikely hash collisions */
+ if (memcmp(cache->driver_keys_blob, file_header, ck_size) != 0)
+ goto fail;
/* Load the CRC that was created when the file was written. */
struct cache_entry_file_data cf_data;
free(uncompressed_data);
if (filename)
free(filename);
+ if (file_header)
+ free(file_header);
if (fd != -1)
close(fd);