17ec681f3Smrg/* 27ec681f3Smrg * Copyright © 2014 Intel Corporation 37ec681f3Smrg * 47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 57ec681f3Smrg * copy of this software and associated documentation files (the "Software"), 67ec681f3Smrg * to deal in the Software without restriction, including without limitation 77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the 97ec681f3Smrg * Software is furnished to do so, subject to the following conditions: 107ec681f3Smrg * 117ec681f3Smrg * The above copyright notice and this permission notice (including the next 127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the 137ec681f3Smrg * Software. 147ec681f3Smrg * 157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 207ec681f3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 217ec681f3Smrg * IN THE SOFTWARE. 227ec681f3Smrg */ 237ec681f3Smrg 247ec681f3Smrg#ifdef ENABLE_SHADER_CACHE 257ec681f3Smrg 267ec681f3Smrg#include <assert.h> 277ec681f3Smrg#include <inttypes.h> 287ec681f3Smrg#include <stdbool.h> 297ec681f3Smrg#include <stddef.h> 307ec681f3Smrg#include <stdlib.h> 317ec681f3Smrg#include <sys/types.h> 327ec681f3Smrg#include <sys/stat.h> 337ec681f3Smrg#include <dirent.h> 347ec681f3Smrg#include <fcntl.h> 357ec681f3Smrg 367ec681f3Smrg#include "util/compress.h" 377ec681f3Smrg#include "util/crc32.h" 387ec681f3Smrg 397ec681f3Smrgstruct cache_entry_file_data { 407ec681f3Smrg uint32_t crc32; 417ec681f3Smrg uint32_t uncompressed_size; 427ec681f3Smrg}; 437ec681f3Smrg 447ec681f3Smrg#if DETECT_OS_WINDOWS 457ec681f3Smrg/* TODO: implement disk cache support on windows */ 467ec681f3Smrg 477ec681f3Smrg#else 487ec681f3Smrg 497ec681f3Smrg#include <dirent.h> 507ec681f3Smrg#include <errno.h> 517ec681f3Smrg#include <pwd.h> 527ec681f3Smrg#include <stdio.h> 537ec681f3Smrg#include <string.h> 547ec681f3Smrg#include <sys/file.h> 557ec681f3Smrg#include <sys/mman.h> 567ec681f3Smrg#include <sys/types.h> 577ec681f3Smrg#include <sys/stat.h> 587ec681f3Smrg#include <unistd.h> 597ec681f3Smrg 607ec681f3Smrg#include "util/blob.h" 617ec681f3Smrg#include "util/crc32.h" 627ec681f3Smrg#include "util/debug.h" 637ec681f3Smrg#include "util/disk_cache.h" 647ec681f3Smrg#include "util/disk_cache_os.h" 657ec681f3Smrg#include "util/ralloc.h" 667ec681f3Smrg#include "util/rand_xor.h" 677ec681f3Smrg 687ec681f3Smrg/* Create a directory named 'path' if it does not already exist. 697ec681f3Smrg * 707ec681f3Smrg * Returns: 0 if path already exists as a directory or if created. 717ec681f3Smrg * -1 in all other cases. 727ec681f3Smrg */ 737ec681f3Smrgstatic int 747ec681f3Smrgmkdir_if_needed(const char *path) 757ec681f3Smrg{ 767ec681f3Smrg struct stat sb; 777ec681f3Smrg 787ec681f3Smrg /* If the path exists already, then our work is done if it's a 797ec681f3Smrg * directory, but it's an error if it is not. 807ec681f3Smrg */ 817ec681f3Smrg if (stat(path, &sb) == 0) { 827ec681f3Smrg if (S_ISDIR(sb.st_mode)) { 837ec681f3Smrg return 0; 847ec681f3Smrg } else { 857ec681f3Smrg fprintf(stderr, "Cannot use %s for shader cache (not a directory)" 867ec681f3Smrg "---disabling.\n", path); 877ec681f3Smrg return -1; 887ec681f3Smrg } 897ec681f3Smrg } 907ec681f3Smrg 917ec681f3Smrg int ret = mkdir(path, 0755); 927ec681f3Smrg if (ret == 0 || (ret == -1 && errno == EEXIST)) 937ec681f3Smrg return 0; 947ec681f3Smrg 957ec681f3Smrg fprintf(stderr, "Failed to create %s for shader cache (%s)---disabling.\n", 967ec681f3Smrg path, strerror(errno)); 977ec681f3Smrg 987ec681f3Smrg return -1; 997ec681f3Smrg} 1007ec681f3Smrg 1017ec681f3Smrg/* Concatenate an existing path and a new name to form a new path. If the new 1027ec681f3Smrg * path does not exist as a directory, create it then return the resulting 1037ec681f3Smrg * name of the new path (ralloc'ed off of 'ctx'). 1047ec681f3Smrg * 1057ec681f3Smrg * Returns NULL on any error, such as: 1067ec681f3Smrg * 1077ec681f3Smrg * <path> does not exist or is not a directory 1087ec681f3Smrg * <path>/<name> exists but is not a directory 1097ec681f3Smrg * <path>/<name> cannot be created as a directory 1107ec681f3Smrg */ 1117ec681f3Smrgstatic char * 1127ec681f3Smrgconcatenate_and_mkdir(void *ctx, const char *path, const char *name) 1137ec681f3Smrg{ 1147ec681f3Smrg char *new_path; 1157ec681f3Smrg struct stat sb; 1167ec681f3Smrg 1177ec681f3Smrg if (stat(path, &sb) != 0 || ! S_ISDIR(sb.st_mode)) 1187ec681f3Smrg return NULL; 1197ec681f3Smrg 1207ec681f3Smrg new_path = ralloc_asprintf(ctx, "%s/%s", path, name); 1217ec681f3Smrg 1227ec681f3Smrg if (mkdir_if_needed(new_path) == 0) 1237ec681f3Smrg return new_path; 1247ec681f3Smrg else 1257ec681f3Smrg return NULL; 1267ec681f3Smrg} 1277ec681f3Smrg 1287ec681f3Smrgstruct lru_file { 1297ec681f3Smrg struct list_head node; 1307ec681f3Smrg char *lru_name; 1317ec681f3Smrg size_t lru_file_size; 1327ec681f3Smrg time_t lru_atime; 1337ec681f3Smrg}; 1347ec681f3Smrg 1357ec681f3Smrgstatic void 1367ec681f3Smrgfree_lru_file_list(struct list_head *lru_file_list) 1377ec681f3Smrg{ 1387ec681f3Smrg struct lru_file *e, *next; 1397ec681f3Smrg LIST_FOR_EACH_ENTRY_SAFE(e, next, lru_file_list, node) { 1407ec681f3Smrg free(e->lru_name); 1417ec681f3Smrg free(e); 1427ec681f3Smrg } 1437ec681f3Smrg free(lru_file_list); 1447ec681f3Smrg} 1457ec681f3Smrg 1467ec681f3Smrg/* Given a directory path and predicate function, create a linked list of entrys 1477ec681f3Smrg * with the oldest access time in that directory for which the predicate 1487ec681f3Smrg * returns true. 1497ec681f3Smrg * 1507ec681f3Smrg * Returns: A malloc'ed linkd list for the paths of chosen files, (or 1517ec681f3Smrg * NULL on any error). The caller should free the linked list via 1527ec681f3Smrg * free_lru_file_list() when finished. 1537ec681f3Smrg */ 1547ec681f3Smrgstatic struct list_head * 1557ec681f3Smrgchoose_lru_file_matching(const char *dir_path, 1567ec681f3Smrg bool (*predicate)(const char *dir_path, 1577ec681f3Smrg const struct stat *, 1587ec681f3Smrg const char *, const size_t)) 1597ec681f3Smrg{ 1607ec681f3Smrg DIR *dir; 1617ec681f3Smrg struct dirent *dir_ent; 1627ec681f3Smrg 1637ec681f3Smrg dir = opendir(dir_path); 1647ec681f3Smrg if (dir == NULL) 1657ec681f3Smrg return NULL; 1667ec681f3Smrg 1677ec681f3Smrg /* First count the number of files in the directory */ 1687ec681f3Smrg unsigned total_file_count = 0; 1697ec681f3Smrg while ((dir_ent = readdir(dir)) != NULL) { 1707ec681f3Smrg if (dir_ent->d_type == DT_REG) { /* If the entry is a regular file */ 1717ec681f3Smrg total_file_count++; 1727ec681f3Smrg } 1737ec681f3Smrg } 1747ec681f3Smrg 1757ec681f3Smrg /* Reset to the start of the directory */ 1767ec681f3Smrg rewinddir(dir); 1777ec681f3Smrg 1787ec681f3Smrg /* Collect 10% of files in this directory for removal. Note: This should work 1797ec681f3Smrg * out to only be around 0.04% of total cache items. 1807ec681f3Smrg */ 1817ec681f3Smrg unsigned lru_file_count = total_file_count > 10 ? total_file_count / 10 : 1; 1827ec681f3Smrg struct list_head *lru_file_list = malloc(sizeof(struct list_head)); 1837ec681f3Smrg list_inithead(lru_file_list); 1847ec681f3Smrg 1857ec681f3Smrg unsigned processed_files = 0; 1867ec681f3Smrg while (1) { 1877ec681f3Smrg dir_ent = readdir(dir); 1887ec681f3Smrg if (dir_ent == NULL) 1897ec681f3Smrg break; 1907ec681f3Smrg 1917ec681f3Smrg struct stat sb; 1927ec681f3Smrg if (fstatat(dirfd(dir), dir_ent->d_name, &sb, 0) == 0) { 1937ec681f3Smrg struct lru_file *entry = NULL; 1947ec681f3Smrg if (!list_is_empty(lru_file_list)) 1957ec681f3Smrg entry = list_first_entry(lru_file_list, struct lru_file, node); 1967ec681f3Smrg 1977ec681f3Smrg if (!entry|| sb.st_atime < entry->lru_atime) { 1987ec681f3Smrg size_t len = strlen(dir_ent->d_name); 1997ec681f3Smrg if (!predicate(dir_path, &sb, dir_ent->d_name, len)) 2007ec681f3Smrg continue; 2017ec681f3Smrg 2027ec681f3Smrg bool new_entry = false; 2037ec681f3Smrg if (processed_files < lru_file_count) { 2047ec681f3Smrg entry = calloc(1, sizeof(struct lru_file)); 2057ec681f3Smrg new_entry = true; 2067ec681f3Smrg } 2077ec681f3Smrg processed_files++; 2087ec681f3Smrg 2097ec681f3Smrg char *tmp = realloc(entry->lru_name, len + 1); 2107ec681f3Smrg if (tmp) { 2117ec681f3Smrg /* Find location to insert new lru item. We want to keep the 2127ec681f3Smrg * list ordering from most recently used to least recently used. 2137ec681f3Smrg * This allows us to just evict the head item from the list as 2147ec681f3Smrg * we process the directory and find older entrys. 2157ec681f3Smrg */ 2167ec681f3Smrg struct list_head *list_node = lru_file_list; 2177ec681f3Smrg struct lru_file *e; 2187ec681f3Smrg LIST_FOR_EACH_ENTRY(e, lru_file_list, node) { 2197ec681f3Smrg if (sb.st_atime < entry->lru_atime) { 2207ec681f3Smrg list_node = &e->node; 2217ec681f3Smrg break; 2227ec681f3Smrg } 2237ec681f3Smrg } 2247ec681f3Smrg 2257ec681f3Smrg if (new_entry) { 2267ec681f3Smrg list_addtail(&entry->node, list_node); 2277ec681f3Smrg } else { 2287ec681f3Smrg if (list_node != lru_file_list) { 2297ec681f3Smrg list_del(lru_file_list); 2307ec681f3Smrg list_addtail(lru_file_list, list_node); 2317ec681f3Smrg } 2327ec681f3Smrg } 2337ec681f3Smrg 2347ec681f3Smrg entry->lru_name = tmp; 2357ec681f3Smrg memcpy(entry->lru_name, dir_ent->d_name, len + 1); 2367ec681f3Smrg entry->lru_atime = sb.st_atime; 2377ec681f3Smrg entry->lru_file_size = sb.st_blocks * 512; 2387ec681f3Smrg } 2397ec681f3Smrg } 2407ec681f3Smrg } 2417ec681f3Smrg } 2427ec681f3Smrg 2437ec681f3Smrg if (list_is_empty(lru_file_list)) { 2447ec681f3Smrg closedir(dir); 2457ec681f3Smrg free(lru_file_list); 2467ec681f3Smrg return NULL; 2477ec681f3Smrg } 2487ec681f3Smrg 2497ec681f3Smrg /* Create the full path for the file list we found */ 2507ec681f3Smrg struct lru_file *e; 2517ec681f3Smrg LIST_FOR_EACH_ENTRY(e, lru_file_list, node) { 2527ec681f3Smrg char *filename = e->lru_name; 2537ec681f3Smrg if (asprintf(&e->lru_name, "%s/%s", dir_path, filename) < 0) 2547ec681f3Smrg e->lru_name = NULL; 2557ec681f3Smrg 2567ec681f3Smrg free(filename); 2577ec681f3Smrg } 2587ec681f3Smrg 2597ec681f3Smrg closedir(dir); 2607ec681f3Smrg 2617ec681f3Smrg return lru_file_list; 2627ec681f3Smrg} 2637ec681f3Smrg 2647ec681f3Smrg/* Is entry a regular file, and not having a name with a trailing 2657ec681f3Smrg * ".tmp" 2667ec681f3Smrg */ 2677ec681f3Smrgstatic bool 2687ec681f3Smrgis_regular_non_tmp_file(const char *path, const struct stat *sb, 2697ec681f3Smrg const char *d_name, const size_t len) 2707ec681f3Smrg{ 2717ec681f3Smrg if (!S_ISREG(sb->st_mode)) 2727ec681f3Smrg return false; 2737ec681f3Smrg 2747ec681f3Smrg if (len >= 4 && strcmp(&d_name[len-4], ".tmp") == 0) 2757ec681f3Smrg return false; 2767ec681f3Smrg 2777ec681f3Smrg return true; 2787ec681f3Smrg} 2797ec681f3Smrg 2807ec681f3Smrg/* Returns the size of the deleted file, (or 0 on any error). */ 2817ec681f3Smrgstatic size_t 2827ec681f3Smrgunlink_lru_file_from_directory(const char *path) 2837ec681f3Smrg{ 2847ec681f3Smrg struct list_head *lru_file_list = 2857ec681f3Smrg choose_lru_file_matching(path, is_regular_non_tmp_file); 2867ec681f3Smrg if (lru_file_list == NULL) 2877ec681f3Smrg return 0; 2887ec681f3Smrg 2897ec681f3Smrg assert(!list_is_empty(lru_file_list)); 2907ec681f3Smrg 2917ec681f3Smrg size_t total_unlinked_size = 0; 2927ec681f3Smrg struct lru_file *e; 2937ec681f3Smrg LIST_FOR_EACH_ENTRY(e, lru_file_list, node) { 2947ec681f3Smrg if (unlink(e->lru_name) == 0) 2957ec681f3Smrg total_unlinked_size += e->lru_file_size; 2967ec681f3Smrg } 2977ec681f3Smrg free_lru_file_list(lru_file_list); 2987ec681f3Smrg 2997ec681f3Smrg return total_unlinked_size; 3007ec681f3Smrg} 3017ec681f3Smrg 3027ec681f3Smrg/* Is entry a directory with a two-character name, (and not the 3037ec681f3Smrg * special name of ".."). We also return false if the dir is empty. 3047ec681f3Smrg */ 3057ec681f3Smrgstatic bool 3067ec681f3Smrgis_two_character_sub_directory(const char *path, const struct stat *sb, 3077ec681f3Smrg const char *d_name, const size_t len) 3087ec681f3Smrg{ 3097ec681f3Smrg if (!S_ISDIR(sb->st_mode)) 3107ec681f3Smrg return false; 3117ec681f3Smrg 3127ec681f3Smrg if (len != 2) 3137ec681f3Smrg return false; 3147ec681f3Smrg 3157ec681f3Smrg if (strcmp(d_name, "..") == 0) 3167ec681f3Smrg return false; 3177ec681f3Smrg 3187ec681f3Smrg char *subdir; 3197ec681f3Smrg if (asprintf(&subdir, "%s/%s", path, d_name) == -1) 3207ec681f3Smrg return false; 3217ec681f3Smrg DIR *dir = opendir(subdir); 3227ec681f3Smrg free(subdir); 3237ec681f3Smrg 3247ec681f3Smrg if (dir == NULL) 3257ec681f3Smrg return false; 3267ec681f3Smrg 3277ec681f3Smrg unsigned subdir_entries = 0; 3287ec681f3Smrg struct dirent *d; 3297ec681f3Smrg while ((d = readdir(dir)) != NULL) { 3307ec681f3Smrg if(++subdir_entries > 2) 3317ec681f3Smrg break; 3327ec681f3Smrg } 3337ec681f3Smrg closedir(dir); 3347ec681f3Smrg 3357ec681f3Smrg /* If dir only contains '.' and '..' it must be empty */ 3367ec681f3Smrg if (subdir_entries <= 2) 3377ec681f3Smrg return false; 3387ec681f3Smrg 3397ec681f3Smrg return true; 3407ec681f3Smrg} 3417ec681f3Smrg 3427ec681f3Smrg/* Create the directory that will be needed for the cache file for \key. 3437ec681f3Smrg * 3447ec681f3Smrg * Obviously, the implementation here must closely match 3457ec681f3Smrg * _get_cache_file above. 3467ec681f3Smrg*/ 3477ec681f3Smrgstatic void 3487ec681f3Smrgmake_cache_file_directory(struct disk_cache *cache, const cache_key key) 3497ec681f3Smrg{ 3507ec681f3Smrg char *dir; 3517ec681f3Smrg char buf[41]; 3527ec681f3Smrg 3537ec681f3Smrg _mesa_sha1_format(buf, key); 3547ec681f3Smrg if (asprintf(&dir, "%s/%c%c", cache->path, buf[0], buf[1]) == -1) 3557ec681f3Smrg return; 3567ec681f3Smrg 3577ec681f3Smrg mkdir_if_needed(dir); 3587ec681f3Smrg free(dir); 3597ec681f3Smrg} 3607ec681f3Smrg 3617ec681f3Smrgstatic ssize_t 3627ec681f3Smrgread_all(int fd, void *buf, size_t count) 3637ec681f3Smrg{ 3647ec681f3Smrg char *in = buf; 3657ec681f3Smrg ssize_t read_ret; 3667ec681f3Smrg size_t done; 3677ec681f3Smrg 3687ec681f3Smrg for (done = 0; done < count; done += read_ret) { 3697ec681f3Smrg read_ret = read(fd, in + done, count - done); 3707ec681f3Smrg if (read_ret == -1 || read_ret == 0) 3717ec681f3Smrg return -1; 3727ec681f3Smrg } 3737ec681f3Smrg return done; 3747ec681f3Smrg} 3757ec681f3Smrg 3767ec681f3Smrgstatic ssize_t 3777ec681f3Smrgwrite_all(int fd, const void *buf, size_t count) 3787ec681f3Smrg{ 3797ec681f3Smrg const char *out = buf; 3807ec681f3Smrg ssize_t written; 3817ec681f3Smrg size_t done; 3827ec681f3Smrg 3837ec681f3Smrg for (done = 0; done < count; done += written) { 3847ec681f3Smrg written = write(fd, out + done, count - done); 3857ec681f3Smrg if (written == -1) 3867ec681f3Smrg return -1; 3877ec681f3Smrg } 3887ec681f3Smrg return done; 3897ec681f3Smrg} 3907ec681f3Smrg 3917ec681f3Smrg/* Evict least recently used cache item */ 3927ec681f3Smrgvoid 3937ec681f3Smrgdisk_cache_evict_lru_item(struct disk_cache *cache) 3947ec681f3Smrg{ 3957ec681f3Smrg char *dir_path; 3967ec681f3Smrg 3977ec681f3Smrg /* With a reasonably-sized, full cache, (and with keys generated 3987ec681f3Smrg * from a cryptographic hash), we can choose two random hex digits 3997ec681f3Smrg * and reasonably expect the directory to exist with a file in it. 4007ec681f3Smrg * Provides pseudo-LRU eviction to reduce checking all cache files. 4017ec681f3Smrg */ 4027ec681f3Smrg uint64_t rand64 = rand_xorshift128plus(cache->seed_xorshift128plus); 4037ec681f3Smrg if (asprintf(&dir_path, "%s/%02" PRIx64 , cache->path, rand64 & 0xff) < 0) 4047ec681f3Smrg return; 4057ec681f3Smrg 4067ec681f3Smrg size_t size = unlink_lru_file_from_directory(dir_path); 4077ec681f3Smrg 4087ec681f3Smrg free(dir_path); 4097ec681f3Smrg 4107ec681f3Smrg if (size) { 4117ec681f3Smrg p_atomic_add(cache->size, - (uint64_t)size); 4127ec681f3Smrg return; 4137ec681f3Smrg } 4147ec681f3Smrg 4157ec681f3Smrg /* In the case where the random choice of directory didn't find 4167ec681f3Smrg * something, we choose the least recently accessed from the 4177ec681f3Smrg * existing directories. 4187ec681f3Smrg * 4197ec681f3Smrg * Really, the only reason this code exists is to allow the unit 4207ec681f3Smrg * tests to work, (which use an artificially-small cache to be able 4217ec681f3Smrg * to force a single cached item to be evicted). 4227ec681f3Smrg */ 4237ec681f3Smrg struct list_head *lru_file_list = 4247ec681f3Smrg choose_lru_file_matching(cache->path, is_two_character_sub_directory); 4257ec681f3Smrg if (lru_file_list == NULL) 4267ec681f3Smrg return; 4277ec681f3Smrg 4287ec681f3Smrg assert(!list_is_empty(lru_file_list)); 4297ec681f3Smrg 4307ec681f3Smrg struct lru_file *lru_file_dir = 4317ec681f3Smrg list_first_entry(lru_file_list, struct lru_file, node); 4327ec681f3Smrg 4337ec681f3Smrg size = unlink_lru_file_from_directory(lru_file_dir->lru_name); 4347ec681f3Smrg 4357ec681f3Smrg free_lru_file_list(lru_file_list); 4367ec681f3Smrg 4377ec681f3Smrg if (size) 4387ec681f3Smrg p_atomic_add(cache->size, - (uint64_t)size); 4397ec681f3Smrg} 4407ec681f3Smrg 4417ec681f3Smrgvoid 4427ec681f3Smrgdisk_cache_evict_item(struct disk_cache *cache, char *filename) 4437ec681f3Smrg{ 4447ec681f3Smrg struct stat sb; 4457ec681f3Smrg if (stat(filename, &sb) == -1) { 4467ec681f3Smrg free(filename); 4477ec681f3Smrg return; 4487ec681f3Smrg } 4497ec681f3Smrg 4507ec681f3Smrg unlink(filename); 4517ec681f3Smrg free(filename); 4527ec681f3Smrg 4537ec681f3Smrg if (sb.st_blocks) 4547ec681f3Smrg p_atomic_add(cache->size, - (uint64_t)sb.st_blocks * 512); 4557ec681f3Smrg} 4567ec681f3Smrg 4577ec681f3Smrgstatic void * 4587ec681f3Smrgparse_and_validate_cache_item(struct disk_cache *cache, void *cache_item, 4597ec681f3Smrg size_t cache_item_size, size_t *size) 4607ec681f3Smrg{ 4617ec681f3Smrg uint8_t *uncompressed_data = NULL; 4627ec681f3Smrg 4637ec681f3Smrg struct blob_reader ci_blob_reader; 4647ec681f3Smrg blob_reader_init(&ci_blob_reader, cache_item, cache_item_size); 4657ec681f3Smrg 4667ec681f3Smrg size_t header_size = cache->driver_keys_blob_size; 4677ec681f3Smrg const void *keys_blob = blob_read_bytes(&ci_blob_reader, header_size); 4687ec681f3Smrg if (ci_blob_reader.overrun) 4697ec681f3Smrg goto fail; 4707ec681f3Smrg 4717ec681f3Smrg /* Check for extremely unlikely hash collisions */ 4727ec681f3Smrg if (memcmp(cache->driver_keys_blob, keys_blob, header_size) != 0) { 4737ec681f3Smrg assert(!"Mesa cache keys mismatch!"); 4747ec681f3Smrg goto fail; 4757ec681f3Smrg } 4767ec681f3Smrg 4777ec681f3Smrg uint32_t md_type = blob_read_uint32(&ci_blob_reader); 4787ec681f3Smrg if (ci_blob_reader.overrun) 4797ec681f3Smrg goto fail; 4807ec681f3Smrg 4817ec681f3Smrg if (md_type == CACHE_ITEM_TYPE_GLSL) { 4827ec681f3Smrg uint32_t num_keys = blob_read_uint32(&ci_blob_reader); 4837ec681f3Smrg if (ci_blob_reader.overrun) 4847ec681f3Smrg goto fail; 4857ec681f3Smrg 4867ec681f3Smrg /* The cache item metadata is currently just used for distributing 4877ec681f3Smrg * precompiled shaders, they are not used by Mesa so just skip them for 4887ec681f3Smrg * now. 4897ec681f3Smrg * TODO: pass the metadata back to the caller and do some basic 4907ec681f3Smrg * validation. 4917ec681f3Smrg */ 4927ec681f3Smrg const void UNUSED *metadata = 4937ec681f3Smrg blob_read_bytes(&ci_blob_reader, num_keys * sizeof(cache_key)); 4947ec681f3Smrg if (ci_blob_reader.overrun) 4957ec681f3Smrg goto fail; 4967ec681f3Smrg } 4977ec681f3Smrg 4987ec681f3Smrg /* Load the CRC that was created when the file was written. */ 4997ec681f3Smrg struct cache_entry_file_data *cf_data = 5007ec681f3Smrg (struct cache_entry_file_data *) 5017ec681f3Smrg blob_read_bytes(&ci_blob_reader, sizeof(struct cache_entry_file_data)); 5027ec681f3Smrg if (ci_blob_reader.overrun) 5037ec681f3Smrg goto fail; 5047ec681f3Smrg 5057ec681f3Smrg size_t cache_data_size = ci_blob_reader.end - ci_blob_reader.current; 5067ec681f3Smrg const uint8_t *data = (uint8_t *) blob_read_bytes(&ci_blob_reader, cache_data_size); 5077ec681f3Smrg 5087ec681f3Smrg /* Check the data for corruption */ 5097ec681f3Smrg if (cf_data->crc32 != util_hash_crc32(data, cache_data_size)) 5107ec681f3Smrg goto fail; 5117ec681f3Smrg 5127ec681f3Smrg /* Uncompress the cache data */ 5137ec681f3Smrg uncompressed_data = malloc(cf_data->uncompressed_size); 5147ec681f3Smrg if (!util_compress_inflate(data, cache_data_size, uncompressed_data, 5157ec681f3Smrg cf_data->uncompressed_size)) 5167ec681f3Smrg goto fail; 5177ec681f3Smrg 5187ec681f3Smrg if (size) 5197ec681f3Smrg *size = cf_data->uncompressed_size; 5207ec681f3Smrg 5217ec681f3Smrg return uncompressed_data; 5227ec681f3Smrg 5237ec681f3Smrg fail: 5247ec681f3Smrg if (uncompressed_data) 5257ec681f3Smrg free(uncompressed_data); 5267ec681f3Smrg 5277ec681f3Smrg return NULL; 5287ec681f3Smrg} 5297ec681f3Smrg 5307ec681f3Smrgvoid * 5317ec681f3Smrgdisk_cache_load_item(struct disk_cache *cache, char *filename, size_t *size) 5327ec681f3Smrg{ 5337ec681f3Smrg uint8_t *data = NULL; 5347ec681f3Smrg 5357ec681f3Smrg int fd = open(filename, O_RDONLY | O_CLOEXEC); 5367ec681f3Smrg if (fd == -1) 5377ec681f3Smrg goto fail; 5387ec681f3Smrg 5397ec681f3Smrg struct stat sb; 5407ec681f3Smrg if (fstat(fd, &sb) == -1) 5417ec681f3Smrg goto fail; 5427ec681f3Smrg 5437ec681f3Smrg data = malloc(sb.st_size); 5447ec681f3Smrg if (data == NULL) 5457ec681f3Smrg goto fail; 5467ec681f3Smrg 5477ec681f3Smrg /* Read entire file into memory */ 5487ec681f3Smrg int ret = read_all(fd, data, sb.st_size); 5497ec681f3Smrg if (ret == -1) 5507ec681f3Smrg goto fail; 5517ec681f3Smrg 5527ec681f3Smrg uint8_t *uncompressed_data = 5537ec681f3Smrg parse_and_validate_cache_item(cache, data, sb.st_size, size); 5547ec681f3Smrg if (!uncompressed_data) 5557ec681f3Smrg goto fail; 5567ec681f3Smrg 5577ec681f3Smrg free(data); 5587ec681f3Smrg free(filename); 5597ec681f3Smrg close(fd); 5607ec681f3Smrg 5617ec681f3Smrg return uncompressed_data; 5627ec681f3Smrg 5637ec681f3Smrg fail: 5647ec681f3Smrg if (data) 5657ec681f3Smrg free(data); 5667ec681f3Smrg if (filename) 5677ec681f3Smrg free(filename); 5687ec681f3Smrg if (fd != -1) 5697ec681f3Smrg close(fd); 5707ec681f3Smrg 5717ec681f3Smrg return NULL; 5727ec681f3Smrg} 5737ec681f3Smrg 5747ec681f3Smrg/* Return a filename within the cache's directory corresponding to 'key'. 5757ec681f3Smrg * 5767ec681f3Smrg * Returns NULL if out of memory. 5777ec681f3Smrg */ 5787ec681f3Smrgchar * 5797ec681f3Smrgdisk_cache_get_cache_filename(struct disk_cache *cache, const cache_key key) 5807ec681f3Smrg{ 5817ec681f3Smrg char buf[41]; 5827ec681f3Smrg char *filename; 5837ec681f3Smrg 5847ec681f3Smrg if (cache->path_init_failed) 5857ec681f3Smrg return NULL; 5867ec681f3Smrg 5877ec681f3Smrg _mesa_sha1_format(buf, key); 5887ec681f3Smrg if (asprintf(&filename, "%s/%c%c/%s", cache->path, buf[0], 5897ec681f3Smrg buf[1], buf + 2) == -1) 5907ec681f3Smrg return NULL; 5917ec681f3Smrg 5927ec681f3Smrg return filename; 5937ec681f3Smrg} 5947ec681f3Smrg 5957ec681f3Smrgstatic bool 5967ec681f3Smrgcreate_cache_item_header_and_blob(struct disk_cache_put_job *dc_job, 5977ec681f3Smrg struct blob *cache_blob) 5987ec681f3Smrg{ 5997ec681f3Smrg 6007ec681f3Smrg /* Compress the cache item data */ 6017ec681f3Smrg size_t max_buf = util_compress_max_compressed_len(dc_job->size); 6027ec681f3Smrg void *compressed_data = malloc(max_buf); 6037ec681f3Smrg if (compressed_data == NULL) 6047ec681f3Smrg return false; 6057ec681f3Smrg 6067ec681f3Smrg size_t compressed_size = 6077ec681f3Smrg util_compress_deflate(dc_job->data, dc_job->size, 6087ec681f3Smrg compressed_data, max_buf); 6097ec681f3Smrg if (compressed_size == 0) 6107ec681f3Smrg goto fail; 6117ec681f3Smrg 6127ec681f3Smrg /* Copy the driver_keys_blob, this can be used find information about the 6137ec681f3Smrg * mesa version that produced the entry or deal with hash collisions, 6147ec681f3Smrg * should that ever become a real problem. 6157ec681f3Smrg */ 6167ec681f3Smrg if (!blob_write_bytes(cache_blob, dc_job->cache->driver_keys_blob, 6177ec681f3Smrg dc_job->cache->driver_keys_blob_size)) 6187ec681f3Smrg goto fail; 6197ec681f3Smrg 6207ec681f3Smrg /* Write the cache item metadata. This data can be used to deal with 6217ec681f3Smrg * hash collisions, as well as providing useful information to 3rd party 6227ec681f3Smrg * tools reading the cache files. 6237ec681f3Smrg */ 6247ec681f3Smrg if (!blob_write_uint32(cache_blob, dc_job->cache_item_metadata.type)) 6257ec681f3Smrg goto fail; 6267ec681f3Smrg 6277ec681f3Smrg if (dc_job->cache_item_metadata.type == CACHE_ITEM_TYPE_GLSL) { 6287ec681f3Smrg if (!blob_write_uint32(cache_blob, dc_job->cache_item_metadata.num_keys)) 6297ec681f3Smrg goto fail; 6307ec681f3Smrg 6317ec681f3Smrg size_t metadata_keys_size = 6327ec681f3Smrg dc_job->cache_item_metadata.num_keys * sizeof(cache_key); 6337ec681f3Smrg if (!blob_write_bytes(cache_blob, dc_job->cache_item_metadata.keys[0], 6347ec681f3Smrg metadata_keys_size)) 6357ec681f3Smrg goto fail; 6367ec681f3Smrg } 6377ec681f3Smrg 6387ec681f3Smrg /* Create CRC of the compressed data. We will read this when restoring the 6397ec681f3Smrg * cache and use it to check for corruption. 6407ec681f3Smrg */ 6417ec681f3Smrg struct cache_entry_file_data cf_data; 6427ec681f3Smrg cf_data.crc32 = util_hash_crc32(compressed_data, compressed_size); 6437ec681f3Smrg cf_data.uncompressed_size = dc_job->size; 6447ec681f3Smrg 6457ec681f3Smrg if (!blob_write_bytes(cache_blob, &cf_data, sizeof(cf_data))) 6467ec681f3Smrg goto fail; 6477ec681f3Smrg 6487ec681f3Smrg /* Finally copy the compressed cache blob */ 6497ec681f3Smrg if (!blob_write_bytes(cache_blob, compressed_data, compressed_size)) 6507ec681f3Smrg goto fail; 6517ec681f3Smrg 6527ec681f3Smrg free(compressed_data); 6537ec681f3Smrg return true; 6547ec681f3Smrg 6557ec681f3Smrg fail: 6567ec681f3Smrg free(compressed_data); 6577ec681f3Smrg return false; 6587ec681f3Smrg} 6597ec681f3Smrg 6607ec681f3Smrgvoid 6617ec681f3Smrgdisk_cache_write_item_to_disk(struct disk_cache_put_job *dc_job, 6627ec681f3Smrg char *filename) 6637ec681f3Smrg{ 6647ec681f3Smrg int fd = -1, fd_final = -1; 6657ec681f3Smrg struct blob cache_blob; 6667ec681f3Smrg blob_init(&cache_blob); 6677ec681f3Smrg 6687ec681f3Smrg /* Write to a temporary file to allow for an atomic rename to the 6697ec681f3Smrg * final destination filename, (to prevent any readers from seeing 6707ec681f3Smrg * a partially written file). 6717ec681f3Smrg */ 6727ec681f3Smrg char *filename_tmp = NULL; 6737ec681f3Smrg if (asprintf(&filename_tmp, "%s.tmp", filename) == -1) 6747ec681f3Smrg goto done; 6757ec681f3Smrg 6767ec681f3Smrg fd = open(filename_tmp, O_WRONLY | O_CLOEXEC | O_CREAT, 0644); 6777ec681f3Smrg 6787ec681f3Smrg /* Make the two-character subdirectory within the cache as needed. */ 6797ec681f3Smrg if (fd == -1) { 6807ec681f3Smrg if (errno != ENOENT) 6817ec681f3Smrg goto done; 6827ec681f3Smrg 6837ec681f3Smrg make_cache_file_directory(dc_job->cache, dc_job->key); 6847ec681f3Smrg 6857ec681f3Smrg fd = open(filename_tmp, O_WRONLY | O_CLOEXEC | O_CREAT, 0644); 6867ec681f3Smrg if (fd == -1) 6877ec681f3Smrg goto done; 6887ec681f3Smrg } 6897ec681f3Smrg 6907ec681f3Smrg /* With the temporary file open, we take an exclusive flock on 6917ec681f3Smrg * it. If the flock fails, then another process still has the file 6927ec681f3Smrg * open with the flock held. So just let that file be responsible 6937ec681f3Smrg * for writing the file. 6947ec681f3Smrg */ 6957ec681f3Smrg#ifdef HAVE_FLOCK 6967ec681f3Smrg int err = flock(fd, LOCK_EX | LOCK_NB); 6977ec681f3Smrg#else 6987ec681f3Smrg struct flock lock = { 6997ec681f3Smrg .l_start = 0, 7007ec681f3Smrg .l_len = 0, /* entire file */ 7017ec681f3Smrg .l_type = F_WRLCK, 7027ec681f3Smrg .l_whence = SEEK_SET 7037ec681f3Smrg }; 7047ec681f3Smrg int err = fcntl(fd, F_SETLK, &lock); 7057ec681f3Smrg#endif 7067ec681f3Smrg if (err == -1) 7077ec681f3Smrg goto done; 7087ec681f3Smrg 7097ec681f3Smrg /* Now that we have the lock on the open temporary file, we can 7107ec681f3Smrg * check to see if the destination file already exists. If so, 7117ec681f3Smrg * another process won the race between when we saw that the file 7127ec681f3Smrg * didn't exist and now. In this case, we don't do anything more, 7137ec681f3Smrg * (to ensure the size accounting of the cache doesn't get off). 7147ec681f3Smrg */ 7157ec681f3Smrg fd_final = open(filename, O_RDONLY | O_CLOEXEC); 7167ec681f3Smrg if (fd_final != -1) { 7177ec681f3Smrg unlink(filename_tmp); 7187ec681f3Smrg goto done; 7197ec681f3Smrg } 7207ec681f3Smrg 7217ec681f3Smrg /* OK, we're now on the hook to write out a file that we know is 7227ec681f3Smrg * not in the cache, and is also not being written out to the cache 7237ec681f3Smrg * by some other process. 7247ec681f3Smrg */ 7257ec681f3Smrg if (!create_cache_item_header_and_blob(dc_job, &cache_blob)) { 7267ec681f3Smrg unlink(filename_tmp); 7277ec681f3Smrg goto done; 7287ec681f3Smrg } 7297ec681f3Smrg 7307ec681f3Smrg /* Now, finally, write out the contents to the temporary file, then 7317ec681f3Smrg * rename them atomically to the destination filename, and also 7327ec681f3Smrg * perform an atomic increment of the total cache size. 7337ec681f3Smrg */ 7347ec681f3Smrg int ret = write_all(fd, cache_blob.data, cache_blob.size); 7357ec681f3Smrg if (ret == -1) { 7367ec681f3Smrg unlink(filename_tmp); 7377ec681f3Smrg goto done; 7387ec681f3Smrg } 7397ec681f3Smrg 7407ec681f3Smrg ret = rename(filename_tmp, filename); 7417ec681f3Smrg if (ret == -1) { 7427ec681f3Smrg unlink(filename_tmp); 7437ec681f3Smrg goto done; 7447ec681f3Smrg } 7457ec681f3Smrg 7467ec681f3Smrg struct stat sb; 7477ec681f3Smrg if (stat(filename, &sb) == -1) { 7487ec681f3Smrg /* Something went wrong remove the file */ 7497ec681f3Smrg unlink(filename); 7507ec681f3Smrg goto done; 7517ec681f3Smrg } 7527ec681f3Smrg 7537ec681f3Smrg p_atomic_add(dc_job->cache->size, sb.st_blocks * 512); 7547ec681f3Smrg 7557ec681f3Smrg done: 7567ec681f3Smrg if (fd_final != -1) 7577ec681f3Smrg close(fd_final); 7587ec681f3Smrg /* This close finally releases the flock, (now that the final file 7597ec681f3Smrg * has been renamed into place and the size has been added). 7607ec681f3Smrg */ 7617ec681f3Smrg if (fd != -1) 7627ec681f3Smrg close(fd); 7637ec681f3Smrg free(filename_tmp); 7647ec681f3Smrg blob_finish(&cache_blob); 7657ec681f3Smrg} 7667ec681f3Smrg 7677ec681f3Smrg/* Determine path for cache based on the first defined name as follows: 7687ec681f3Smrg * 7697ec681f3Smrg * $MESA_GLSL_CACHE_DIR 7707ec681f3Smrg * $XDG_CACHE_HOME/mesa_shader_cache 7717ec681f3Smrg * <pwd.pw_dir>/.cache/mesa_shader_cache 7727ec681f3Smrg */ 7737ec681f3Smrgchar * 7747ec681f3Smrgdisk_cache_generate_cache_dir(void *mem_ctx, const char *gpu_name, 7757ec681f3Smrg const char *driver_id) 7767ec681f3Smrg{ 7777ec681f3Smrg char *cache_dir_name = CACHE_DIR_NAME; 7787ec681f3Smrg if (env_var_as_boolean("MESA_DISK_CACHE_SINGLE_FILE", false)) 7797ec681f3Smrg cache_dir_name = CACHE_DIR_NAME_SF; 7807ec681f3Smrg 7817ec681f3Smrg char *path = getenv("MESA_GLSL_CACHE_DIR"); 7827ec681f3Smrg if (path) { 7837ec681f3Smrg if (mkdir_if_needed(path) == -1) 7847ec681f3Smrg return NULL; 7857ec681f3Smrg 7867ec681f3Smrg path = concatenate_and_mkdir(mem_ctx, path, cache_dir_name); 7877ec681f3Smrg if (!path) 7887ec681f3Smrg return NULL; 7897ec681f3Smrg } 7907ec681f3Smrg 7917ec681f3Smrg if (path == NULL) { 7927ec681f3Smrg char *xdg_cache_home = getenv("XDG_CACHE_HOME"); 7937ec681f3Smrg 7947ec681f3Smrg if (xdg_cache_home) { 7957ec681f3Smrg if (mkdir_if_needed(xdg_cache_home) == -1) 7967ec681f3Smrg return NULL; 7977ec681f3Smrg 7987ec681f3Smrg path = concatenate_and_mkdir(mem_ctx, xdg_cache_home, cache_dir_name); 7997ec681f3Smrg if (!path) 8007ec681f3Smrg return NULL; 8017ec681f3Smrg } 8027ec681f3Smrg } 8037ec681f3Smrg 8047ec681f3Smrg if (!path) { 8057ec681f3Smrg char *buf; 8067ec681f3Smrg size_t buf_size; 8077ec681f3Smrg struct passwd pwd, *result; 8087ec681f3Smrg 8097ec681f3Smrg buf_size = sysconf(_SC_GETPW_R_SIZE_MAX); 8107ec681f3Smrg if (buf_size == -1) 8117ec681f3Smrg buf_size = 512; 8127ec681f3Smrg 8137ec681f3Smrg /* Loop until buf_size is large enough to query the directory */ 8147ec681f3Smrg while (1) { 8157ec681f3Smrg buf = ralloc_size(mem_ctx, buf_size); 8167ec681f3Smrg 8177ec681f3Smrg getpwuid_r(getuid(), &pwd, buf, buf_size, &result); 8187ec681f3Smrg if (result) 8197ec681f3Smrg break; 8207ec681f3Smrg 8217ec681f3Smrg if (errno == ERANGE) { 8227ec681f3Smrg ralloc_free(buf); 8237ec681f3Smrg buf = NULL; 8247ec681f3Smrg buf_size *= 2; 8257ec681f3Smrg } else { 8267ec681f3Smrg return NULL; 8277ec681f3Smrg } 8287ec681f3Smrg } 8297ec681f3Smrg 8307ec681f3Smrg path = concatenate_and_mkdir(mem_ctx, pwd.pw_dir, ".cache"); 8317ec681f3Smrg if (!path) 8327ec681f3Smrg return NULL; 8337ec681f3Smrg 8347ec681f3Smrg path = concatenate_and_mkdir(mem_ctx, path, cache_dir_name); 8357ec681f3Smrg if (!path) 8367ec681f3Smrg return NULL; 8377ec681f3Smrg } 8387ec681f3Smrg 8397ec681f3Smrg if (env_var_as_boolean("MESA_DISK_CACHE_SINGLE_FILE", false)) { 8407ec681f3Smrg path = concatenate_and_mkdir(mem_ctx, path, driver_id); 8417ec681f3Smrg if (!path) 8427ec681f3Smrg return NULL; 8437ec681f3Smrg 8447ec681f3Smrg path = concatenate_and_mkdir(mem_ctx, path, gpu_name); 8457ec681f3Smrg if (!path) 8467ec681f3Smrg return NULL; 8477ec681f3Smrg } 8487ec681f3Smrg 8497ec681f3Smrg return path; 8507ec681f3Smrg} 8517ec681f3Smrg 8527ec681f3Smrgbool 8537ec681f3Smrgdisk_cache_enabled() 8547ec681f3Smrg{ 8557ec681f3Smrg /* If running as a users other than the real user disable cache */ 8561463c08dSmrg if (issetugid()) 8577ec681f3Smrg return false; 8587ec681f3Smrg 8597ec681f3Smrg /* At user request, disable shader cache entirely. */ 8607ec681f3Smrg#ifdef SHADER_CACHE_DISABLE_BY_DEFAULT 8617ec681f3Smrg bool disable_by_default = true; 8627ec681f3Smrg#else 8637ec681f3Smrg bool disable_by_default = false; 8647ec681f3Smrg#endif 8657ec681f3Smrg if (env_var_as_boolean("MESA_GLSL_CACHE_DISABLE", disable_by_default)) 8667ec681f3Smrg return false; 8677ec681f3Smrg 8687ec681f3Smrg return true; 8697ec681f3Smrg} 8707ec681f3Smrg 8717ec681f3Smrgvoid * 8727ec681f3Smrgdisk_cache_load_item_foz(struct disk_cache *cache, const cache_key key, 8737ec681f3Smrg size_t *size) 8747ec681f3Smrg{ 8757ec681f3Smrg size_t cache_tem_size = 0; 8767ec681f3Smrg void *cache_item = foz_read_entry(&cache->foz_db, key, &cache_tem_size); 8777ec681f3Smrg if (!cache_item) 8787ec681f3Smrg return NULL; 8797ec681f3Smrg 8807ec681f3Smrg uint8_t *uncompressed_data = 8817ec681f3Smrg parse_and_validate_cache_item(cache, cache_item, cache_tem_size, size); 8827ec681f3Smrg free(cache_item); 8837ec681f3Smrg 8847ec681f3Smrg return uncompressed_data; 8857ec681f3Smrg} 8867ec681f3Smrg 8877ec681f3Smrgbool 8887ec681f3Smrgdisk_cache_write_item_to_disk_foz(struct disk_cache_put_job *dc_job) 8897ec681f3Smrg{ 8907ec681f3Smrg struct blob cache_blob; 8917ec681f3Smrg blob_init(&cache_blob); 8927ec681f3Smrg 8937ec681f3Smrg if (!create_cache_item_header_and_blob(dc_job, &cache_blob)) 8947ec681f3Smrg return false; 8957ec681f3Smrg 8967ec681f3Smrg bool r = foz_write_entry(&dc_job->cache->foz_db, dc_job->key, 8977ec681f3Smrg cache_blob.data, cache_blob.size); 8987ec681f3Smrg 8997ec681f3Smrg blob_finish(&cache_blob); 9007ec681f3Smrg return r; 9017ec681f3Smrg} 9027ec681f3Smrg 9037ec681f3Smrgbool 9047ec681f3Smrgdisk_cache_load_cache_index(void *mem_ctx, struct disk_cache *cache) 9057ec681f3Smrg{ 9067ec681f3Smrg /* Load cache index into a hash map (from fossilise files) */ 9077ec681f3Smrg return foz_prepare(&cache->foz_db, cache->path); 9087ec681f3Smrg} 9097ec681f3Smrg 9107ec681f3Smrgbool 9117ec681f3Smrgdisk_cache_mmap_cache_index(void *mem_ctx, struct disk_cache *cache, 9127ec681f3Smrg char *path) 9137ec681f3Smrg{ 9147ec681f3Smrg int fd = -1; 9157ec681f3Smrg bool mapped = false; 9167ec681f3Smrg 9177ec681f3Smrg path = ralloc_asprintf(mem_ctx, "%s/index", cache->path); 9187ec681f3Smrg if (path == NULL) 9197ec681f3Smrg goto path_fail; 9207ec681f3Smrg 9217ec681f3Smrg fd = open(path, O_RDWR | O_CREAT | O_CLOEXEC, 0644); 9227ec681f3Smrg if (fd == -1) 9237ec681f3Smrg goto path_fail; 9247ec681f3Smrg 9257ec681f3Smrg struct stat sb; 9267ec681f3Smrg if (fstat(fd, &sb) == -1) 9277ec681f3Smrg goto path_fail; 9287ec681f3Smrg 9297ec681f3Smrg /* Force the index file to be the expected size. */ 9307ec681f3Smrg size_t size = sizeof(*cache->size) + CACHE_INDEX_MAX_KEYS * CACHE_KEY_SIZE; 9317ec681f3Smrg if (sb.st_size != size) { 9327ec681f3Smrg if (ftruncate(fd, size) == -1) 9337ec681f3Smrg goto path_fail; 9347ec681f3Smrg } 9357ec681f3Smrg 9367ec681f3Smrg /* We map this shared so that other processes see updates that we 9377ec681f3Smrg * make. 9387ec681f3Smrg * 9397ec681f3Smrg * Note: We do use atomic addition to ensure that multiple 9407ec681f3Smrg * processes don't scramble the cache size recorded in the 9417ec681f3Smrg * index. But we don't use any locking to prevent multiple 9427ec681f3Smrg * processes from updating the same entry simultaneously. The idea 9437ec681f3Smrg * is that if either result lands entirely in the index, then 9447ec681f3Smrg * that's equivalent to a well-ordered write followed by an 9457ec681f3Smrg * eviction and a write. On the other hand, if the simultaneous 9467ec681f3Smrg * writes result in a corrupt entry, that's not really any 9477ec681f3Smrg * different than both entries being evicted, (since within the 9487ec681f3Smrg * guarantees of the cryptographic hash, a corrupt entry is 9497ec681f3Smrg * unlikely to ever match a real cache key). 9507ec681f3Smrg */ 9517ec681f3Smrg cache->index_mmap = mmap(NULL, size, PROT_READ | PROT_WRITE, 9527ec681f3Smrg MAP_SHARED, fd, 0); 9537ec681f3Smrg if (cache->index_mmap == MAP_FAILED) 9547ec681f3Smrg goto path_fail; 9557ec681f3Smrg cache->index_mmap_size = size; 9567ec681f3Smrg 9577ec681f3Smrg cache->size = (uint64_t *) cache->index_mmap; 9587ec681f3Smrg cache->stored_keys = cache->index_mmap + sizeof(uint64_t); 9597ec681f3Smrg mapped = true; 9607ec681f3Smrg 9617ec681f3Smrgpath_fail: 9627ec681f3Smrg if (fd != -1) 9637ec681f3Smrg close(fd); 9647ec681f3Smrg 9657ec681f3Smrg return mapped; 9667ec681f3Smrg} 9677ec681f3Smrg 9687ec681f3Smrgvoid 9697ec681f3Smrgdisk_cache_destroy_mmap(struct disk_cache *cache) 9707ec681f3Smrg{ 9717ec681f3Smrg munmap(cache->index_mmap, cache->index_mmap_size); 9727ec681f3Smrg} 9737ec681f3Smrg#endif 9747ec681f3Smrg 9757ec681f3Smrg#endif /* ENABLE_SHADER_CACHE */ 976