1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright © 2014 Intel Corporation 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10bf215546Sopenharmony_ci * 11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13bf215546Sopenharmony_ci * Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21bf215546Sopenharmony_ci * IN THE SOFTWARE. 22bf215546Sopenharmony_ci */ 23bf215546Sopenharmony_ci 24bf215546Sopenharmony_ci 25bf215546Sopenharmony_ci#include <assert.h> 26bf215546Sopenharmony_ci#include <inttypes.h> 27bf215546Sopenharmony_ci#include <stdbool.h> 28bf215546Sopenharmony_ci#include <stddef.h> 29bf215546Sopenharmony_ci#include <stdlib.h> 30bf215546Sopenharmony_ci#include <sys/types.h> 31bf215546Sopenharmony_ci#include <sys/stat.h> 32bf215546Sopenharmony_ci#include <fcntl.h> 33bf215546Sopenharmony_ci 34bf215546Sopenharmony_ci#include "util/compress.h" 35bf215546Sopenharmony_ci#include "util/crc32.h" 36bf215546Sopenharmony_ci#include "util/disk_cache.h" 37bf215546Sopenharmony_ci#include "util/disk_cache_os.h" 38bf215546Sopenharmony_ci 39bf215546Sopenharmony_cistruct cache_entry_file_data { 40bf215546Sopenharmony_ci uint32_t crc32; 41bf215546Sopenharmony_ci uint32_t uncompressed_size; 42bf215546Sopenharmony_ci}; 43bf215546Sopenharmony_ci 44bf215546Sopenharmony_ci#if DETECT_OS_WINDOWS 45bf215546Sopenharmony_ci 46bf215546Sopenharmony_cibool 47bf215546Sopenharmony_cidisk_cache_get_function_identifier(void *ptr, struct mesa_sha1 *ctx) 48bf215546Sopenharmony_ci{ 49bf215546Sopenharmony_ci HMODULE mod = NULL; 50bf215546Sopenharmony_ci GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 51bf215546Sopenharmony_ci (LPCWSTR)ptr, 52bf215546Sopenharmony_ci &mod); 53bf215546Sopenharmony_ci if (!mod) 54bf215546Sopenharmony_ci return false; 55bf215546Sopenharmony_ci 56bf215546Sopenharmony_ci WCHAR filename[MAX_PATH]; 57bf215546Sopenharmony_ci DWORD filename_length = GetModuleFileNameW(mod, filename, ARRAY_SIZE(filename)); 58bf215546Sopenharmony_ci 59bf215546Sopenharmony_ci if (filename_length == 0 || filename_length == ARRAY_SIZE(filename)) 60bf215546Sopenharmony_ci return false; 61bf215546Sopenharmony_ci 62bf215546Sopenharmony_ci HANDLE mod_as_file = CreateFileW( 63bf215546Sopenharmony_ci filename, 64bf215546Sopenharmony_ci GENERIC_READ, 65bf215546Sopenharmony_ci FILE_SHARE_READ, 66bf215546Sopenharmony_ci NULL, 67bf215546Sopenharmony_ci OPEN_EXISTING, 68bf215546Sopenharmony_ci FILE_ATTRIBUTE_NORMAL, 69bf215546Sopenharmony_ci NULL); 70bf215546Sopenharmony_ci if (mod_as_file == INVALID_HANDLE_VALUE) 71bf215546Sopenharmony_ci return false; 72bf215546Sopenharmony_ci 73bf215546Sopenharmony_ci FILETIME time; 74bf215546Sopenharmony_ci bool ret = GetFileTime(mod_as_file, NULL, NULL, &time); 75bf215546Sopenharmony_ci if (ret) 76bf215546Sopenharmony_ci _mesa_sha1_update(ctx, &time, sizeof(time)); 77bf215546Sopenharmony_ci CloseHandle(mod_as_file); 78bf215546Sopenharmony_ci return ret; 79bf215546Sopenharmony_ci} 80bf215546Sopenharmony_ci 81bf215546Sopenharmony_ci#endif 82bf215546Sopenharmony_ci 83bf215546Sopenharmony_ci#ifdef ENABLE_SHADER_CACHE 84bf215546Sopenharmony_ci 85bf215546Sopenharmony_ci#if DETECT_OS_WINDOWS 86bf215546Sopenharmony_ci/* TODO: implement disk cache support on windows */ 87bf215546Sopenharmony_ci 88bf215546Sopenharmony_ci#else 89bf215546Sopenharmony_ci 90bf215546Sopenharmony_ci#include <dirent.h> 91bf215546Sopenharmony_ci#include <errno.h> 92bf215546Sopenharmony_ci#include <pwd.h> 93bf215546Sopenharmony_ci#include <stdio.h> 94bf215546Sopenharmony_ci#include <string.h> 95bf215546Sopenharmony_ci#include <sys/file.h> 96bf215546Sopenharmony_ci#include <sys/mman.h> 97bf215546Sopenharmony_ci#include <sys/types.h> 98bf215546Sopenharmony_ci#include <sys/stat.h> 99bf215546Sopenharmony_ci#include <unistd.h> 100bf215546Sopenharmony_ci 101bf215546Sopenharmony_ci#include "util/blob.h" 102bf215546Sopenharmony_ci#include "util/crc32.h" 103bf215546Sopenharmony_ci#include "util/debug.h" 104bf215546Sopenharmony_ci#include "util/ralloc.h" 105bf215546Sopenharmony_ci#include "util/rand_xor.h" 106bf215546Sopenharmony_ci 107bf215546Sopenharmony_ci/* Create a directory named 'path' if it does not already exist. 108bf215546Sopenharmony_ci * 109bf215546Sopenharmony_ci * Returns: 0 if path already exists as a directory or if created. 110bf215546Sopenharmony_ci * -1 in all other cases. 111bf215546Sopenharmony_ci */ 112bf215546Sopenharmony_cistatic int 113bf215546Sopenharmony_cimkdir_if_needed(const char *path) 114bf215546Sopenharmony_ci{ 115bf215546Sopenharmony_ci struct stat sb; 116bf215546Sopenharmony_ci 117bf215546Sopenharmony_ci /* If the path exists already, then our work is done if it's a 118bf215546Sopenharmony_ci * directory, but it's an error if it is not. 119bf215546Sopenharmony_ci */ 120bf215546Sopenharmony_ci if (stat(path, &sb) == 0) { 121bf215546Sopenharmony_ci if (S_ISDIR(sb.st_mode)) { 122bf215546Sopenharmony_ci return 0; 123bf215546Sopenharmony_ci } else { 124bf215546Sopenharmony_ci fprintf(stderr, "Cannot use %s for shader cache (not a directory)" 125bf215546Sopenharmony_ci "---disabling.\n", path); 126bf215546Sopenharmony_ci return -1; 127bf215546Sopenharmony_ci } 128bf215546Sopenharmony_ci } 129bf215546Sopenharmony_ci 130bf215546Sopenharmony_ci int ret = mkdir(path, 0755); 131bf215546Sopenharmony_ci if (ret == 0 || (ret == -1 && errno == EEXIST)) 132bf215546Sopenharmony_ci return 0; 133bf215546Sopenharmony_ci 134bf215546Sopenharmony_ci fprintf(stderr, "Failed to create %s for shader cache (%s)---disabling.\n", 135bf215546Sopenharmony_ci path, strerror(errno)); 136bf215546Sopenharmony_ci 137bf215546Sopenharmony_ci return -1; 138bf215546Sopenharmony_ci} 139bf215546Sopenharmony_ci 140bf215546Sopenharmony_ci/* Concatenate an existing path and a new name to form a new path. If the new 141bf215546Sopenharmony_ci * path does not exist as a directory, create it then return the resulting 142bf215546Sopenharmony_ci * name of the new path (ralloc'ed off of 'ctx'). 143bf215546Sopenharmony_ci * 144bf215546Sopenharmony_ci * Returns NULL on any error, such as: 145bf215546Sopenharmony_ci * 146bf215546Sopenharmony_ci * <path> does not exist or is not a directory 147bf215546Sopenharmony_ci * <path>/<name> exists but is not a directory 148bf215546Sopenharmony_ci * <path>/<name> cannot be created as a directory 149bf215546Sopenharmony_ci */ 150bf215546Sopenharmony_cistatic char * 151bf215546Sopenharmony_ciconcatenate_and_mkdir(void *ctx, const char *path, const char *name) 152bf215546Sopenharmony_ci{ 153bf215546Sopenharmony_ci char *new_path; 154bf215546Sopenharmony_ci struct stat sb; 155bf215546Sopenharmony_ci 156bf215546Sopenharmony_ci if (stat(path, &sb) != 0 || ! S_ISDIR(sb.st_mode)) 157bf215546Sopenharmony_ci return NULL; 158bf215546Sopenharmony_ci 159bf215546Sopenharmony_ci new_path = ralloc_asprintf(ctx, "%s/%s", path, name); 160bf215546Sopenharmony_ci 161bf215546Sopenharmony_ci if (mkdir_if_needed(new_path) == 0) 162bf215546Sopenharmony_ci return new_path; 163bf215546Sopenharmony_ci else 164bf215546Sopenharmony_ci return NULL; 165bf215546Sopenharmony_ci} 166bf215546Sopenharmony_ci 167bf215546Sopenharmony_cistruct lru_file { 168bf215546Sopenharmony_ci struct list_head node; 169bf215546Sopenharmony_ci char *lru_name; 170bf215546Sopenharmony_ci size_t lru_file_size; 171bf215546Sopenharmony_ci time_t lru_atime; 172bf215546Sopenharmony_ci}; 173bf215546Sopenharmony_ci 174bf215546Sopenharmony_cistatic void 175bf215546Sopenharmony_cifree_lru_file_list(struct list_head *lru_file_list) 176bf215546Sopenharmony_ci{ 177bf215546Sopenharmony_ci struct lru_file *e, *next; 178bf215546Sopenharmony_ci LIST_FOR_EACH_ENTRY_SAFE(e, next, lru_file_list, node) { 179bf215546Sopenharmony_ci free(e->lru_name); 180bf215546Sopenharmony_ci free(e); 181bf215546Sopenharmony_ci } 182bf215546Sopenharmony_ci free(lru_file_list); 183bf215546Sopenharmony_ci} 184bf215546Sopenharmony_ci 185bf215546Sopenharmony_ci/* Given a directory path and predicate function, create a linked list of entrys 186bf215546Sopenharmony_ci * with the oldest access time in that directory for which the predicate 187bf215546Sopenharmony_ci * returns true. 188bf215546Sopenharmony_ci * 189bf215546Sopenharmony_ci * Returns: A malloc'ed linkd list for the paths of chosen files, (or 190bf215546Sopenharmony_ci * NULL on any error). The caller should free the linked list via 191bf215546Sopenharmony_ci * free_lru_file_list() when finished. 192bf215546Sopenharmony_ci */ 193bf215546Sopenharmony_cistatic struct list_head * 194bf215546Sopenharmony_cichoose_lru_file_matching(const char *dir_path, 195bf215546Sopenharmony_ci bool (*predicate)(const char *dir_path, 196bf215546Sopenharmony_ci const struct stat *, 197bf215546Sopenharmony_ci const char *, const size_t)) 198bf215546Sopenharmony_ci{ 199bf215546Sopenharmony_ci DIR *dir; 200bf215546Sopenharmony_ci struct dirent *dir_ent; 201bf215546Sopenharmony_ci 202bf215546Sopenharmony_ci dir = opendir(dir_path); 203bf215546Sopenharmony_ci if (dir == NULL) 204bf215546Sopenharmony_ci return NULL; 205bf215546Sopenharmony_ci 206bf215546Sopenharmony_ci /* First count the number of files in the directory */ 207bf215546Sopenharmony_ci unsigned total_file_count = 0; 208bf215546Sopenharmony_ci while ((dir_ent = readdir(dir)) != NULL) { 209bf215546Sopenharmony_ci if (dir_ent->d_type == DT_REG) { /* If the entry is a regular file */ 210bf215546Sopenharmony_ci total_file_count++; 211bf215546Sopenharmony_ci } 212bf215546Sopenharmony_ci } 213bf215546Sopenharmony_ci 214bf215546Sopenharmony_ci /* Reset to the start of the directory */ 215bf215546Sopenharmony_ci rewinddir(dir); 216bf215546Sopenharmony_ci 217bf215546Sopenharmony_ci /* Collect 10% of files in this directory for removal. Note: This should work 218bf215546Sopenharmony_ci * out to only be around 0.04% of total cache items. 219bf215546Sopenharmony_ci */ 220bf215546Sopenharmony_ci unsigned lru_file_count = total_file_count > 10 ? total_file_count / 10 : 1; 221bf215546Sopenharmony_ci struct list_head *lru_file_list = malloc(sizeof(struct list_head)); 222bf215546Sopenharmony_ci list_inithead(lru_file_list); 223bf215546Sopenharmony_ci 224bf215546Sopenharmony_ci unsigned processed_files = 0; 225bf215546Sopenharmony_ci while (1) { 226bf215546Sopenharmony_ci dir_ent = readdir(dir); 227bf215546Sopenharmony_ci if (dir_ent == NULL) 228bf215546Sopenharmony_ci break; 229bf215546Sopenharmony_ci 230bf215546Sopenharmony_ci struct stat sb; 231bf215546Sopenharmony_ci if (fstatat(dirfd(dir), dir_ent->d_name, &sb, 0) == 0) { 232bf215546Sopenharmony_ci struct lru_file *entry = NULL; 233bf215546Sopenharmony_ci if (!list_is_empty(lru_file_list)) 234bf215546Sopenharmony_ci entry = list_first_entry(lru_file_list, struct lru_file, node); 235bf215546Sopenharmony_ci 236bf215546Sopenharmony_ci if (!entry|| sb.st_atime < entry->lru_atime) { 237bf215546Sopenharmony_ci size_t len = strlen(dir_ent->d_name); 238bf215546Sopenharmony_ci if (!predicate(dir_path, &sb, dir_ent->d_name, len)) 239bf215546Sopenharmony_ci continue; 240bf215546Sopenharmony_ci 241bf215546Sopenharmony_ci bool new_entry = false; 242bf215546Sopenharmony_ci if (processed_files < lru_file_count) { 243bf215546Sopenharmony_ci entry = calloc(1, sizeof(struct lru_file)); 244bf215546Sopenharmony_ci new_entry = true; 245bf215546Sopenharmony_ci } 246bf215546Sopenharmony_ci processed_files++; 247bf215546Sopenharmony_ci 248bf215546Sopenharmony_ci char *tmp = realloc(entry->lru_name, len + 1); 249bf215546Sopenharmony_ci if (tmp) { 250bf215546Sopenharmony_ci /* Find location to insert new lru item. We want to keep the 251bf215546Sopenharmony_ci * list ordering from most recently used to least recently used. 252bf215546Sopenharmony_ci * This allows us to just evict the head item from the list as 253bf215546Sopenharmony_ci * we process the directory and find older entrys. 254bf215546Sopenharmony_ci */ 255bf215546Sopenharmony_ci struct list_head *list_node = lru_file_list; 256bf215546Sopenharmony_ci struct lru_file *e; 257bf215546Sopenharmony_ci LIST_FOR_EACH_ENTRY(e, lru_file_list, node) { 258bf215546Sopenharmony_ci if (sb.st_atime < entry->lru_atime) { 259bf215546Sopenharmony_ci list_node = &e->node; 260bf215546Sopenharmony_ci break; 261bf215546Sopenharmony_ci } 262bf215546Sopenharmony_ci } 263bf215546Sopenharmony_ci 264bf215546Sopenharmony_ci if (new_entry) { 265bf215546Sopenharmony_ci list_addtail(&entry->node, list_node); 266bf215546Sopenharmony_ci } else { 267bf215546Sopenharmony_ci if (list_node != lru_file_list) { 268bf215546Sopenharmony_ci list_del(lru_file_list); 269bf215546Sopenharmony_ci list_addtail(lru_file_list, list_node); 270bf215546Sopenharmony_ci } 271bf215546Sopenharmony_ci } 272bf215546Sopenharmony_ci 273bf215546Sopenharmony_ci entry->lru_name = tmp; 274bf215546Sopenharmony_ci memcpy(entry->lru_name, dir_ent->d_name, len + 1); 275bf215546Sopenharmony_ci entry->lru_atime = sb.st_atime; 276bf215546Sopenharmony_ci entry->lru_file_size = sb.st_blocks * 512; 277bf215546Sopenharmony_ci } 278bf215546Sopenharmony_ci } 279bf215546Sopenharmony_ci } 280bf215546Sopenharmony_ci } 281bf215546Sopenharmony_ci 282bf215546Sopenharmony_ci if (list_is_empty(lru_file_list)) { 283bf215546Sopenharmony_ci closedir(dir); 284bf215546Sopenharmony_ci free(lru_file_list); 285bf215546Sopenharmony_ci return NULL; 286bf215546Sopenharmony_ci } 287bf215546Sopenharmony_ci 288bf215546Sopenharmony_ci /* Create the full path for the file list we found */ 289bf215546Sopenharmony_ci struct lru_file *e; 290bf215546Sopenharmony_ci LIST_FOR_EACH_ENTRY(e, lru_file_list, node) { 291bf215546Sopenharmony_ci char *filename = e->lru_name; 292bf215546Sopenharmony_ci if (asprintf(&e->lru_name, "%s/%s", dir_path, filename) < 0) 293bf215546Sopenharmony_ci e->lru_name = NULL; 294bf215546Sopenharmony_ci 295bf215546Sopenharmony_ci free(filename); 296bf215546Sopenharmony_ci } 297bf215546Sopenharmony_ci 298bf215546Sopenharmony_ci closedir(dir); 299bf215546Sopenharmony_ci 300bf215546Sopenharmony_ci return lru_file_list; 301bf215546Sopenharmony_ci} 302bf215546Sopenharmony_ci 303bf215546Sopenharmony_ci/* Is entry a regular file, and not having a name with a trailing 304bf215546Sopenharmony_ci * ".tmp" 305bf215546Sopenharmony_ci */ 306bf215546Sopenharmony_cistatic bool 307bf215546Sopenharmony_ciis_regular_non_tmp_file(const char *path, const struct stat *sb, 308bf215546Sopenharmony_ci const char *d_name, const size_t len) 309bf215546Sopenharmony_ci{ 310bf215546Sopenharmony_ci if (!S_ISREG(sb->st_mode)) 311bf215546Sopenharmony_ci return false; 312bf215546Sopenharmony_ci 313bf215546Sopenharmony_ci if (len >= 4 && strcmp(&d_name[len-4], ".tmp") == 0) 314bf215546Sopenharmony_ci return false; 315bf215546Sopenharmony_ci 316bf215546Sopenharmony_ci return true; 317bf215546Sopenharmony_ci} 318bf215546Sopenharmony_ci 319bf215546Sopenharmony_ci/* Returns the size of the deleted file, (or 0 on any error). */ 320bf215546Sopenharmony_cistatic size_t 321bf215546Sopenharmony_ciunlink_lru_file_from_directory(const char *path) 322bf215546Sopenharmony_ci{ 323bf215546Sopenharmony_ci struct list_head *lru_file_list = 324bf215546Sopenharmony_ci choose_lru_file_matching(path, is_regular_non_tmp_file); 325bf215546Sopenharmony_ci if (lru_file_list == NULL) 326bf215546Sopenharmony_ci return 0; 327bf215546Sopenharmony_ci 328bf215546Sopenharmony_ci assert(!list_is_empty(lru_file_list)); 329bf215546Sopenharmony_ci 330bf215546Sopenharmony_ci size_t total_unlinked_size = 0; 331bf215546Sopenharmony_ci struct lru_file *e; 332bf215546Sopenharmony_ci LIST_FOR_EACH_ENTRY(e, lru_file_list, node) { 333bf215546Sopenharmony_ci if (unlink(e->lru_name) == 0) 334bf215546Sopenharmony_ci total_unlinked_size += e->lru_file_size; 335bf215546Sopenharmony_ci } 336bf215546Sopenharmony_ci free_lru_file_list(lru_file_list); 337bf215546Sopenharmony_ci 338bf215546Sopenharmony_ci return total_unlinked_size; 339bf215546Sopenharmony_ci} 340bf215546Sopenharmony_ci 341bf215546Sopenharmony_ci/* Is entry a directory with a two-character name, (and not the 342bf215546Sopenharmony_ci * special name of ".."). We also return false if the dir is empty. 343bf215546Sopenharmony_ci */ 344bf215546Sopenharmony_cistatic bool 345bf215546Sopenharmony_ciis_two_character_sub_directory(const char *path, const struct stat *sb, 346bf215546Sopenharmony_ci const char *d_name, const size_t len) 347bf215546Sopenharmony_ci{ 348bf215546Sopenharmony_ci if (!S_ISDIR(sb->st_mode)) 349bf215546Sopenharmony_ci return false; 350bf215546Sopenharmony_ci 351bf215546Sopenharmony_ci if (len != 2) 352bf215546Sopenharmony_ci return false; 353bf215546Sopenharmony_ci 354bf215546Sopenharmony_ci if (strcmp(d_name, "..") == 0) 355bf215546Sopenharmony_ci return false; 356bf215546Sopenharmony_ci 357bf215546Sopenharmony_ci char *subdir; 358bf215546Sopenharmony_ci if (asprintf(&subdir, "%s/%s", path, d_name) == -1) 359bf215546Sopenharmony_ci return false; 360bf215546Sopenharmony_ci DIR *dir = opendir(subdir); 361bf215546Sopenharmony_ci free(subdir); 362bf215546Sopenharmony_ci 363bf215546Sopenharmony_ci if (dir == NULL) 364bf215546Sopenharmony_ci return false; 365bf215546Sopenharmony_ci 366bf215546Sopenharmony_ci unsigned subdir_entries = 0; 367bf215546Sopenharmony_ci struct dirent *d; 368bf215546Sopenharmony_ci while ((d = readdir(dir)) != NULL) { 369bf215546Sopenharmony_ci if(++subdir_entries > 2) 370bf215546Sopenharmony_ci break; 371bf215546Sopenharmony_ci } 372bf215546Sopenharmony_ci closedir(dir); 373bf215546Sopenharmony_ci 374bf215546Sopenharmony_ci /* If dir only contains '.' and '..' it must be empty */ 375bf215546Sopenharmony_ci if (subdir_entries <= 2) 376bf215546Sopenharmony_ci return false; 377bf215546Sopenharmony_ci 378bf215546Sopenharmony_ci return true; 379bf215546Sopenharmony_ci} 380bf215546Sopenharmony_ci 381bf215546Sopenharmony_ci/* Create the directory that will be needed for the cache file for \key. 382bf215546Sopenharmony_ci * 383bf215546Sopenharmony_ci * Obviously, the implementation here must closely match 384bf215546Sopenharmony_ci * _get_cache_file above. 385bf215546Sopenharmony_ci*/ 386bf215546Sopenharmony_cistatic void 387bf215546Sopenharmony_cimake_cache_file_directory(struct disk_cache *cache, const cache_key key) 388bf215546Sopenharmony_ci{ 389bf215546Sopenharmony_ci char *dir; 390bf215546Sopenharmony_ci char buf[41]; 391bf215546Sopenharmony_ci 392bf215546Sopenharmony_ci _mesa_sha1_format(buf, key); 393bf215546Sopenharmony_ci if (asprintf(&dir, "%s/%c%c", cache->path, buf[0], buf[1]) == -1) 394bf215546Sopenharmony_ci return; 395bf215546Sopenharmony_ci 396bf215546Sopenharmony_ci mkdir_if_needed(dir); 397bf215546Sopenharmony_ci free(dir); 398bf215546Sopenharmony_ci} 399bf215546Sopenharmony_ci 400bf215546Sopenharmony_cistatic ssize_t 401bf215546Sopenharmony_ciread_all(int fd, void *buf, size_t count) 402bf215546Sopenharmony_ci{ 403bf215546Sopenharmony_ci char *in = buf; 404bf215546Sopenharmony_ci ssize_t read_ret; 405bf215546Sopenharmony_ci size_t done; 406bf215546Sopenharmony_ci 407bf215546Sopenharmony_ci for (done = 0; done < count; done += read_ret) { 408bf215546Sopenharmony_ci read_ret = read(fd, in + done, count - done); 409bf215546Sopenharmony_ci if (read_ret == -1 || read_ret == 0) 410bf215546Sopenharmony_ci return -1; 411bf215546Sopenharmony_ci } 412bf215546Sopenharmony_ci return done; 413bf215546Sopenharmony_ci} 414bf215546Sopenharmony_ci 415bf215546Sopenharmony_cistatic ssize_t 416bf215546Sopenharmony_ciwrite_all(int fd, const void *buf, size_t count) 417bf215546Sopenharmony_ci{ 418bf215546Sopenharmony_ci const char *out = buf; 419bf215546Sopenharmony_ci ssize_t written; 420bf215546Sopenharmony_ci size_t done; 421bf215546Sopenharmony_ci 422bf215546Sopenharmony_ci for (done = 0; done < count; done += written) { 423bf215546Sopenharmony_ci written = write(fd, out + done, count - done); 424bf215546Sopenharmony_ci if (written == -1) 425bf215546Sopenharmony_ci return -1; 426bf215546Sopenharmony_ci } 427bf215546Sopenharmony_ci return done; 428bf215546Sopenharmony_ci} 429bf215546Sopenharmony_ci 430bf215546Sopenharmony_ci/* Evict least recently used cache item */ 431bf215546Sopenharmony_civoid 432bf215546Sopenharmony_cidisk_cache_evict_lru_item(struct disk_cache *cache) 433bf215546Sopenharmony_ci{ 434bf215546Sopenharmony_ci char *dir_path; 435bf215546Sopenharmony_ci 436bf215546Sopenharmony_ci /* With a reasonably-sized, full cache, (and with keys generated 437bf215546Sopenharmony_ci * from a cryptographic hash), we can choose two random hex digits 438bf215546Sopenharmony_ci * and reasonably expect the directory to exist with a file in it. 439bf215546Sopenharmony_ci * Provides pseudo-LRU eviction to reduce checking all cache files. 440bf215546Sopenharmony_ci */ 441bf215546Sopenharmony_ci uint64_t rand64 = rand_xorshift128plus(cache->seed_xorshift128plus); 442bf215546Sopenharmony_ci if (asprintf(&dir_path, "%s/%02" PRIx64 , cache->path, rand64 & 0xff) < 0) 443bf215546Sopenharmony_ci return; 444bf215546Sopenharmony_ci 445bf215546Sopenharmony_ci size_t size = unlink_lru_file_from_directory(dir_path); 446bf215546Sopenharmony_ci 447bf215546Sopenharmony_ci free(dir_path); 448bf215546Sopenharmony_ci 449bf215546Sopenharmony_ci if (size) { 450bf215546Sopenharmony_ci p_atomic_add(cache->size, - (uint64_t)size); 451bf215546Sopenharmony_ci return; 452bf215546Sopenharmony_ci } 453bf215546Sopenharmony_ci 454bf215546Sopenharmony_ci /* In the case where the random choice of directory didn't find 455bf215546Sopenharmony_ci * something, we choose the least recently accessed from the 456bf215546Sopenharmony_ci * existing directories. 457bf215546Sopenharmony_ci * 458bf215546Sopenharmony_ci * Really, the only reason this code exists is to allow the unit 459bf215546Sopenharmony_ci * tests to work, (which use an artificially-small cache to be able 460bf215546Sopenharmony_ci * to force a single cached item to be evicted). 461bf215546Sopenharmony_ci */ 462bf215546Sopenharmony_ci struct list_head *lru_file_list = 463bf215546Sopenharmony_ci choose_lru_file_matching(cache->path, is_two_character_sub_directory); 464bf215546Sopenharmony_ci if (lru_file_list == NULL) 465bf215546Sopenharmony_ci return; 466bf215546Sopenharmony_ci 467bf215546Sopenharmony_ci assert(!list_is_empty(lru_file_list)); 468bf215546Sopenharmony_ci 469bf215546Sopenharmony_ci struct lru_file *lru_file_dir = 470bf215546Sopenharmony_ci list_first_entry(lru_file_list, struct lru_file, node); 471bf215546Sopenharmony_ci 472bf215546Sopenharmony_ci size = unlink_lru_file_from_directory(lru_file_dir->lru_name); 473bf215546Sopenharmony_ci 474bf215546Sopenharmony_ci free_lru_file_list(lru_file_list); 475bf215546Sopenharmony_ci 476bf215546Sopenharmony_ci if (size) 477bf215546Sopenharmony_ci p_atomic_add(cache->size, - (uint64_t)size); 478bf215546Sopenharmony_ci} 479bf215546Sopenharmony_ci 480bf215546Sopenharmony_civoid 481bf215546Sopenharmony_cidisk_cache_evict_item(struct disk_cache *cache, char *filename) 482bf215546Sopenharmony_ci{ 483bf215546Sopenharmony_ci struct stat sb; 484bf215546Sopenharmony_ci if (stat(filename, &sb) == -1) { 485bf215546Sopenharmony_ci free(filename); 486bf215546Sopenharmony_ci return; 487bf215546Sopenharmony_ci } 488bf215546Sopenharmony_ci 489bf215546Sopenharmony_ci unlink(filename); 490bf215546Sopenharmony_ci free(filename); 491bf215546Sopenharmony_ci 492bf215546Sopenharmony_ci if (sb.st_blocks) 493bf215546Sopenharmony_ci p_atomic_add(cache->size, - (uint64_t)sb.st_blocks * 512); 494bf215546Sopenharmony_ci} 495bf215546Sopenharmony_ci 496bf215546Sopenharmony_cistatic void * 497bf215546Sopenharmony_ciparse_and_validate_cache_item(struct disk_cache *cache, void *cache_item, 498bf215546Sopenharmony_ci size_t cache_item_size, size_t *size) 499bf215546Sopenharmony_ci{ 500bf215546Sopenharmony_ci uint8_t *uncompressed_data = NULL; 501bf215546Sopenharmony_ci 502bf215546Sopenharmony_ci struct blob_reader ci_blob_reader; 503bf215546Sopenharmony_ci blob_reader_init(&ci_blob_reader, cache_item, cache_item_size); 504bf215546Sopenharmony_ci 505bf215546Sopenharmony_ci size_t header_size = cache->driver_keys_blob_size; 506bf215546Sopenharmony_ci const void *keys_blob = blob_read_bytes(&ci_blob_reader, header_size); 507bf215546Sopenharmony_ci if (ci_blob_reader.overrun) 508bf215546Sopenharmony_ci goto fail; 509bf215546Sopenharmony_ci 510bf215546Sopenharmony_ci /* Check for extremely unlikely hash collisions */ 511bf215546Sopenharmony_ci if (memcmp(cache->driver_keys_blob, keys_blob, header_size) != 0) { 512bf215546Sopenharmony_ci assert(!"Mesa cache keys mismatch!"); 513bf215546Sopenharmony_ci goto fail; 514bf215546Sopenharmony_ci } 515bf215546Sopenharmony_ci 516bf215546Sopenharmony_ci uint32_t md_type = blob_read_uint32(&ci_blob_reader); 517bf215546Sopenharmony_ci if (ci_blob_reader.overrun) 518bf215546Sopenharmony_ci goto fail; 519bf215546Sopenharmony_ci 520bf215546Sopenharmony_ci if (md_type == CACHE_ITEM_TYPE_GLSL) { 521bf215546Sopenharmony_ci uint32_t num_keys = blob_read_uint32(&ci_blob_reader); 522bf215546Sopenharmony_ci if (ci_blob_reader.overrun) 523bf215546Sopenharmony_ci goto fail; 524bf215546Sopenharmony_ci 525bf215546Sopenharmony_ci /* The cache item metadata is currently just used for distributing 526bf215546Sopenharmony_ci * precompiled shaders, they are not used by Mesa so just skip them for 527bf215546Sopenharmony_ci * now. 528bf215546Sopenharmony_ci * TODO: pass the metadata back to the caller and do some basic 529bf215546Sopenharmony_ci * validation. 530bf215546Sopenharmony_ci */ 531bf215546Sopenharmony_ci const void UNUSED *metadata = 532bf215546Sopenharmony_ci blob_read_bytes(&ci_blob_reader, num_keys * sizeof(cache_key)); 533bf215546Sopenharmony_ci if (ci_blob_reader.overrun) 534bf215546Sopenharmony_ci goto fail; 535bf215546Sopenharmony_ci } 536bf215546Sopenharmony_ci 537bf215546Sopenharmony_ci /* Load the CRC that was created when the file was written. */ 538bf215546Sopenharmony_ci struct cache_entry_file_data *cf_data = 539bf215546Sopenharmony_ci (struct cache_entry_file_data *) 540bf215546Sopenharmony_ci blob_read_bytes(&ci_blob_reader, sizeof(struct cache_entry_file_data)); 541bf215546Sopenharmony_ci if (ci_blob_reader.overrun) 542bf215546Sopenharmony_ci goto fail; 543bf215546Sopenharmony_ci 544bf215546Sopenharmony_ci size_t cache_data_size = ci_blob_reader.end - ci_blob_reader.current; 545bf215546Sopenharmony_ci const uint8_t *data = (uint8_t *) blob_read_bytes(&ci_blob_reader, cache_data_size); 546bf215546Sopenharmony_ci 547bf215546Sopenharmony_ci /* Check the data for corruption */ 548bf215546Sopenharmony_ci if (cf_data->crc32 != util_hash_crc32(data, cache_data_size)) 549bf215546Sopenharmony_ci goto fail; 550bf215546Sopenharmony_ci 551bf215546Sopenharmony_ci /* Uncompress the cache data */ 552bf215546Sopenharmony_ci uncompressed_data = malloc(cf_data->uncompressed_size); 553bf215546Sopenharmony_ci if (!util_compress_inflate(data, cache_data_size, uncompressed_data, 554bf215546Sopenharmony_ci cf_data->uncompressed_size)) 555bf215546Sopenharmony_ci goto fail; 556bf215546Sopenharmony_ci 557bf215546Sopenharmony_ci if (size) 558bf215546Sopenharmony_ci *size = cf_data->uncompressed_size; 559bf215546Sopenharmony_ci 560bf215546Sopenharmony_ci return uncompressed_data; 561bf215546Sopenharmony_ci 562bf215546Sopenharmony_ci fail: 563bf215546Sopenharmony_ci if (uncompressed_data) 564bf215546Sopenharmony_ci free(uncompressed_data); 565bf215546Sopenharmony_ci 566bf215546Sopenharmony_ci return NULL; 567bf215546Sopenharmony_ci} 568bf215546Sopenharmony_ci 569bf215546Sopenharmony_civoid * 570bf215546Sopenharmony_cidisk_cache_load_item(struct disk_cache *cache, char *filename, size_t *size) 571bf215546Sopenharmony_ci{ 572bf215546Sopenharmony_ci uint8_t *data = NULL; 573bf215546Sopenharmony_ci 574bf215546Sopenharmony_ci int fd = open(filename, O_RDONLY | O_CLOEXEC); 575bf215546Sopenharmony_ci if (fd == -1) 576bf215546Sopenharmony_ci goto fail; 577bf215546Sopenharmony_ci 578bf215546Sopenharmony_ci struct stat sb; 579bf215546Sopenharmony_ci if (fstat(fd, &sb) == -1) 580bf215546Sopenharmony_ci goto fail; 581bf215546Sopenharmony_ci 582bf215546Sopenharmony_ci data = malloc(sb.st_size); 583bf215546Sopenharmony_ci if (data == NULL) 584bf215546Sopenharmony_ci goto fail; 585bf215546Sopenharmony_ci 586bf215546Sopenharmony_ci /* Read entire file into memory */ 587bf215546Sopenharmony_ci int ret = read_all(fd, data, sb.st_size); 588bf215546Sopenharmony_ci if (ret == -1) 589bf215546Sopenharmony_ci goto fail; 590bf215546Sopenharmony_ci 591bf215546Sopenharmony_ci uint8_t *uncompressed_data = 592bf215546Sopenharmony_ci parse_and_validate_cache_item(cache, data, sb.st_size, size); 593bf215546Sopenharmony_ci if (!uncompressed_data) 594bf215546Sopenharmony_ci goto fail; 595bf215546Sopenharmony_ci 596bf215546Sopenharmony_ci free(data); 597bf215546Sopenharmony_ci free(filename); 598bf215546Sopenharmony_ci close(fd); 599bf215546Sopenharmony_ci 600bf215546Sopenharmony_ci return uncompressed_data; 601bf215546Sopenharmony_ci 602bf215546Sopenharmony_ci fail: 603bf215546Sopenharmony_ci if (data) 604bf215546Sopenharmony_ci free(data); 605bf215546Sopenharmony_ci if (filename) 606bf215546Sopenharmony_ci free(filename); 607bf215546Sopenharmony_ci if (fd != -1) 608bf215546Sopenharmony_ci close(fd); 609bf215546Sopenharmony_ci 610bf215546Sopenharmony_ci return NULL; 611bf215546Sopenharmony_ci} 612bf215546Sopenharmony_ci 613bf215546Sopenharmony_ci/* Return a filename within the cache's directory corresponding to 'key'. 614bf215546Sopenharmony_ci * 615bf215546Sopenharmony_ci * Returns NULL if out of memory. 616bf215546Sopenharmony_ci */ 617bf215546Sopenharmony_cichar * 618bf215546Sopenharmony_cidisk_cache_get_cache_filename(struct disk_cache *cache, const cache_key key) 619bf215546Sopenharmony_ci{ 620bf215546Sopenharmony_ci char buf[41]; 621bf215546Sopenharmony_ci char *filename; 622bf215546Sopenharmony_ci 623bf215546Sopenharmony_ci if (cache->path_init_failed) 624bf215546Sopenharmony_ci return NULL; 625bf215546Sopenharmony_ci 626bf215546Sopenharmony_ci _mesa_sha1_format(buf, key); 627bf215546Sopenharmony_ci if (asprintf(&filename, "%s/%c%c/%s", cache->path, buf[0], 628bf215546Sopenharmony_ci buf[1], buf + 2) == -1) 629bf215546Sopenharmony_ci return NULL; 630bf215546Sopenharmony_ci 631bf215546Sopenharmony_ci return filename; 632bf215546Sopenharmony_ci} 633bf215546Sopenharmony_ci 634bf215546Sopenharmony_cistatic bool 635bf215546Sopenharmony_cicreate_cache_item_header_and_blob(struct disk_cache_put_job *dc_job, 636bf215546Sopenharmony_ci struct blob *cache_blob) 637bf215546Sopenharmony_ci{ 638bf215546Sopenharmony_ci 639bf215546Sopenharmony_ci /* Compress the cache item data */ 640bf215546Sopenharmony_ci size_t max_buf = util_compress_max_compressed_len(dc_job->size); 641bf215546Sopenharmony_ci void *compressed_data = malloc(max_buf); 642bf215546Sopenharmony_ci if (compressed_data == NULL) 643bf215546Sopenharmony_ci return false; 644bf215546Sopenharmony_ci 645bf215546Sopenharmony_ci size_t compressed_size = 646bf215546Sopenharmony_ci util_compress_deflate(dc_job->data, dc_job->size, 647bf215546Sopenharmony_ci compressed_data, max_buf); 648bf215546Sopenharmony_ci if (compressed_size == 0) 649bf215546Sopenharmony_ci goto fail; 650bf215546Sopenharmony_ci 651bf215546Sopenharmony_ci /* Copy the driver_keys_blob, this can be used find information about the 652bf215546Sopenharmony_ci * mesa version that produced the entry or deal with hash collisions, 653bf215546Sopenharmony_ci * should that ever become a real problem. 654bf215546Sopenharmony_ci */ 655bf215546Sopenharmony_ci if (!blob_write_bytes(cache_blob, dc_job->cache->driver_keys_blob, 656bf215546Sopenharmony_ci dc_job->cache->driver_keys_blob_size)) 657bf215546Sopenharmony_ci goto fail; 658bf215546Sopenharmony_ci 659bf215546Sopenharmony_ci /* Write the cache item metadata. This data can be used to deal with 660bf215546Sopenharmony_ci * hash collisions, as well as providing useful information to 3rd party 661bf215546Sopenharmony_ci * tools reading the cache files. 662bf215546Sopenharmony_ci */ 663bf215546Sopenharmony_ci if (!blob_write_uint32(cache_blob, dc_job->cache_item_metadata.type)) 664bf215546Sopenharmony_ci goto fail; 665bf215546Sopenharmony_ci 666bf215546Sopenharmony_ci if (dc_job->cache_item_metadata.type == CACHE_ITEM_TYPE_GLSL) { 667bf215546Sopenharmony_ci if (!blob_write_uint32(cache_blob, dc_job->cache_item_metadata.num_keys)) 668bf215546Sopenharmony_ci goto fail; 669bf215546Sopenharmony_ci 670bf215546Sopenharmony_ci size_t metadata_keys_size = 671bf215546Sopenharmony_ci dc_job->cache_item_metadata.num_keys * sizeof(cache_key); 672bf215546Sopenharmony_ci if (!blob_write_bytes(cache_blob, dc_job->cache_item_metadata.keys[0], 673bf215546Sopenharmony_ci metadata_keys_size)) 674bf215546Sopenharmony_ci goto fail; 675bf215546Sopenharmony_ci } 676bf215546Sopenharmony_ci 677bf215546Sopenharmony_ci /* Create CRC of the compressed data. We will read this when restoring the 678bf215546Sopenharmony_ci * cache and use it to check for corruption. 679bf215546Sopenharmony_ci */ 680bf215546Sopenharmony_ci struct cache_entry_file_data cf_data; 681bf215546Sopenharmony_ci cf_data.crc32 = util_hash_crc32(compressed_data, compressed_size); 682bf215546Sopenharmony_ci cf_data.uncompressed_size = dc_job->size; 683bf215546Sopenharmony_ci 684bf215546Sopenharmony_ci if (!blob_write_bytes(cache_blob, &cf_data, sizeof(cf_data))) 685bf215546Sopenharmony_ci goto fail; 686bf215546Sopenharmony_ci 687bf215546Sopenharmony_ci /* Finally copy the compressed cache blob */ 688bf215546Sopenharmony_ci if (!blob_write_bytes(cache_blob, compressed_data, compressed_size)) 689bf215546Sopenharmony_ci goto fail; 690bf215546Sopenharmony_ci 691bf215546Sopenharmony_ci free(compressed_data); 692bf215546Sopenharmony_ci return true; 693bf215546Sopenharmony_ci 694bf215546Sopenharmony_ci fail: 695bf215546Sopenharmony_ci free(compressed_data); 696bf215546Sopenharmony_ci return false; 697bf215546Sopenharmony_ci} 698bf215546Sopenharmony_ci 699bf215546Sopenharmony_civoid 700bf215546Sopenharmony_cidisk_cache_write_item_to_disk(struct disk_cache_put_job *dc_job, 701bf215546Sopenharmony_ci char *filename) 702bf215546Sopenharmony_ci{ 703bf215546Sopenharmony_ci int fd = -1, fd_final = -1; 704bf215546Sopenharmony_ci struct blob cache_blob; 705bf215546Sopenharmony_ci blob_init(&cache_blob); 706bf215546Sopenharmony_ci 707bf215546Sopenharmony_ci /* Write to a temporary file to allow for an atomic rename to the 708bf215546Sopenharmony_ci * final destination filename, (to prevent any readers from seeing 709bf215546Sopenharmony_ci * a partially written file). 710bf215546Sopenharmony_ci */ 711bf215546Sopenharmony_ci char *filename_tmp = NULL; 712bf215546Sopenharmony_ci if (asprintf(&filename_tmp, "%s.tmp", filename) == -1) 713bf215546Sopenharmony_ci goto done; 714bf215546Sopenharmony_ci 715bf215546Sopenharmony_ci fd = open(filename_tmp, O_WRONLY | O_CLOEXEC | O_CREAT, 0644); 716bf215546Sopenharmony_ci 717bf215546Sopenharmony_ci /* Make the two-character subdirectory within the cache as needed. */ 718bf215546Sopenharmony_ci if (fd == -1) { 719bf215546Sopenharmony_ci if (errno != ENOENT) 720bf215546Sopenharmony_ci goto done; 721bf215546Sopenharmony_ci 722bf215546Sopenharmony_ci make_cache_file_directory(dc_job->cache, dc_job->key); 723bf215546Sopenharmony_ci 724bf215546Sopenharmony_ci fd = open(filename_tmp, O_WRONLY | O_CLOEXEC | O_CREAT, 0644); 725bf215546Sopenharmony_ci if (fd == -1) 726bf215546Sopenharmony_ci goto done; 727bf215546Sopenharmony_ci } 728bf215546Sopenharmony_ci 729bf215546Sopenharmony_ci /* With the temporary file open, we take an exclusive flock on 730bf215546Sopenharmony_ci * it. If the flock fails, then another process still has the file 731bf215546Sopenharmony_ci * open with the flock held. So just let that file be responsible 732bf215546Sopenharmony_ci * for writing the file. 733bf215546Sopenharmony_ci */ 734bf215546Sopenharmony_ci#ifdef HAVE_FLOCK 735bf215546Sopenharmony_ci int err = flock(fd, LOCK_EX | LOCK_NB); 736bf215546Sopenharmony_ci#else 737bf215546Sopenharmony_ci struct flock lock = { 738bf215546Sopenharmony_ci .l_start = 0, 739bf215546Sopenharmony_ci .l_len = 0, /* entire file */ 740bf215546Sopenharmony_ci .l_type = F_WRLCK, 741bf215546Sopenharmony_ci .l_whence = SEEK_SET 742bf215546Sopenharmony_ci }; 743bf215546Sopenharmony_ci int err = fcntl(fd, F_SETLK, &lock); 744bf215546Sopenharmony_ci#endif 745bf215546Sopenharmony_ci if (err == -1) 746bf215546Sopenharmony_ci goto done; 747bf215546Sopenharmony_ci 748bf215546Sopenharmony_ci /* Now that we have the lock on the open temporary file, we can 749bf215546Sopenharmony_ci * check to see if the destination file already exists. If so, 750bf215546Sopenharmony_ci * another process won the race between when we saw that the file 751bf215546Sopenharmony_ci * didn't exist and now. In this case, we don't do anything more, 752bf215546Sopenharmony_ci * (to ensure the size accounting of the cache doesn't get off). 753bf215546Sopenharmony_ci */ 754bf215546Sopenharmony_ci fd_final = open(filename, O_RDONLY | O_CLOEXEC); 755bf215546Sopenharmony_ci if (fd_final != -1) { 756bf215546Sopenharmony_ci unlink(filename_tmp); 757bf215546Sopenharmony_ci goto done; 758bf215546Sopenharmony_ci } 759bf215546Sopenharmony_ci 760bf215546Sopenharmony_ci /* OK, we're now on the hook to write out a file that we know is 761bf215546Sopenharmony_ci * not in the cache, and is also not being written out to the cache 762bf215546Sopenharmony_ci * by some other process. 763bf215546Sopenharmony_ci */ 764bf215546Sopenharmony_ci if (!create_cache_item_header_and_blob(dc_job, &cache_blob)) { 765bf215546Sopenharmony_ci unlink(filename_tmp); 766bf215546Sopenharmony_ci goto done; 767bf215546Sopenharmony_ci } 768bf215546Sopenharmony_ci 769bf215546Sopenharmony_ci /* Now, finally, write out the contents to the temporary file, then 770bf215546Sopenharmony_ci * rename them atomically to the destination filename, and also 771bf215546Sopenharmony_ci * perform an atomic increment of the total cache size. 772bf215546Sopenharmony_ci */ 773bf215546Sopenharmony_ci int ret = write_all(fd, cache_blob.data, cache_blob.size); 774bf215546Sopenharmony_ci if (ret == -1) { 775bf215546Sopenharmony_ci unlink(filename_tmp); 776bf215546Sopenharmony_ci goto done; 777bf215546Sopenharmony_ci } 778bf215546Sopenharmony_ci 779bf215546Sopenharmony_ci ret = rename(filename_tmp, filename); 780bf215546Sopenharmony_ci if (ret == -1) { 781bf215546Sopenharmony_ci unlink(filename_tmp); 782bf215546Sopenharmony_ci goto done; 783bf215546Sopenharmony_ci } 784bf215546Sopenharmony_ci 785bf215546Sopenharmony_ci struct stat sb; 786bf215546Sopenharmony_ci if (stat(filename, &sb) == -1) { 787bf215546Sopenharmony_ci /* Something went wrong remove the file */ 788bf215546Sopenharmony_ci unlink(filename); 789bf215546Sopenharmony_ci goto done; 790bf215546Sopenharmony_ci } 791bf215546Sopenharmony_ci 792bf215546Sopenharmony_ci p_atomic_add(dc_job->cache->size, sb.st_blocks * 512); 793bf215546Sopenharmony_ci 794bf215546Sopenharmony_ci done: 795bf215546Sopenharmony_ci if (fd_final != -1) 796bf215546Sopenharmony_ci close(fd_final); 797bf215546Sopenharmony_ci /* This close finally releases the flock, (now that the final file 798bf215546Sopenharmony_ci * has been renamed into place and the size has been added). 799bf215546Sopenharmony_ci */ 800bf215546Sopenharmony_ci if (fd != -1) 801bf215546Sopenharmony_ci close(fd); 802bf215546Sopenharmony_ci free(filename_tmp); 803bf215546Sopenharmony_ci blob_finish(&cache_blob); 804bf215546Sopenharmony_ci} 805bf215546Sopenharmony_ci 806bf215546Sopenharmony_ci/* Determine path for cache based on the first defined name as follows: 807bf215546Sopenharmony_ci * 808bf215546Sopenharmony_ci * $MESA_SHADER_CACHE_DIR 809bf215546Sopenharmony_ci * $XDG_CACHE_HOME/mesa_shader_cache 810bf215546Sopenharmony_ci * <pwd.pw_dir>/.cache/mesa_shader_cache 811bf215546Sopenharmony_ci */ 812bf215546Sopenharmony_cichar * 813bf215546Sopenharmony_cidisk_cache_generate_cache_dir(void *mem_ctx, const char *gpu_name, 814bf215546Sopenharmony_ci const char *driver_id) 815bf215546Sopenharmony_ci{ 816bf215546Sopenharmony_ci char *cache_dir_name = CACHE_DIR_NAME; 817bf215546Sopenharmony_ci if (env_var_as_boolean("MESA_DISK_CACHE_SINGLE_FILE", false)) 818bf215546Sopenharmony_ci cache_dir_name = CACHE_DIR_NAME_SF; 819bf215546Sopenharmony_ci 820bf215546Sopenharmony_ci char *path = getenv("MESA_SHADER_CACHE_DIR"); 821bf215546Sopenharmony_ci 822bf215546Sopenharmony_ci if (!path) { 823bf215546Sopenharmony_ci path = getenv("MESA_GLSL_CACHE_DIR"); 824bf215546Sopenharmony_ci if (path) 825bf215546Sopenharmony_ci fprintf(stderr, 826bf215546Sopenharmony_ci "*** MESA_GLSL_CACHE_DIR is deprecated; " 827bf215546Sopenharmony_ci "use MESA_SHADER_CACHE_DIR instead ***\n"); 828bf215546Sopenharmony_ci } 829bf215546Sopenharmony_ci 830bf215546Sopenharmony_ci if (path) { 831bf215546Sopenharmony_ci if (mkdir_if_needed(path) == -1) 832bf215546Sopenharmony_ci return NULL; 833bf215546Sopenharmony_ci 834bf215546Sopenharmony_ci path = concatenate_and_mkdir(mem_ctx, path, cache_dir_name); 835bf215546Sopenharmony_ci if (!path) 836bf215546Sopenharmony_ci return NULL; 837bf215546Sopenharmony_ci } 838bf215546Sopenharmony_ci 839bf215546Sopenharmony_ci if (path == NULL) { 840bf215546Sopenharmony_ci char *xdg_cache_home = getenv("XDG_CACHE_HOME"); 841bf215546Sopenharmony_ci 842bf215546Sopenharmony_ci if (xdg_cache_home) { 843bf215546Sopenharmony_ci if (mkdir_if_needed(xdg_cache_home) == -1) 844bf215546Sopenharmony_ci return NULL; 845bf215546Sopenharmony_ci 846bf215546Sopenharmony_ci path = concatenate_and_mkdir(mem_ctx, xdg_cache_home, cache_dir_name); 847bf215546Sopenharmony_ci if (!path) 848bf215546Sopenharmony_ci return NULL; 849bf215546Sopenharmony_ci } 850bf215546Sopenharmony_ci } 851bf215546Sopenharmony_ci 852bf215546Sopenharmony_ci if (!path) { 853bf215546Sopenharmony_ci char *buf; 854bf215546Sopenharmony_ci size_t buf_size; 855bf215546Sopenharmony_ci struct passwd pwd, *result; 856bf215546Sopenharmony_ci 857bf215546Sopenharmony_ci buf_size = sysconf(_SC_GETPW_R_SIZE_MAX); 858bf215546Sopenharmony_ci if (buf_size == -1) 859bf215546Sopenharmony_ci buf_size = 512; 860bf215546Sopenharmony_ci 861bf215546Sopenharmony_ci /* Loop until buf_size is large enough to query the directory */ 862bf215546Sopenharmony_ci while (1) { 863bf215546Sopenharmony_ci buf = ralloc_size(mem_ctx, buf_size); 864bf215546Sopenharmony_ci 865bf215546Sopenharmony_ci getpwuid_r(getuid(), &pwd, buf, buf_size, &result); 866bf215546Sopenharmony_ci if (result) 867bf215546Sopenharmony_ci break; 868bf215546Sopenharmony_ci 869bf215546Sopenharmony_ci if (errno == ERANGE) { 870bf215546Sopenharmony_ci ralloc_free(buf); 871bf215546Sopenharmony_ci buf = NULL; 872bf215546Sopenharmony_ci buf_size *= 2; 873bf215546Sopenharmony_ci } else { 874bf215546Sopenharmony_ci return NULL; 875bf215546Sopenharmony_ci } 876bf215546Sopenharmony_ci } 877bf215546Sopenharmony_ci 878bf215546Sopenharmony_ci path = concatenate_and_mkdir(mem_ctx, pwd.pw_dir, ".cache"); 879bf215546Sopenharmony_ci if (!path) 880bf215546Sopenharmony_ci return NULL; 881bf215546Sopenharmony_ci 882bf215546Sopenharmony_ci path = concatenate_and_mkdir(mem_ctx, path, cache_dir_name); 883bf215546Sopenharmony_ci if (!path) 884bf215546Sopenharmony_ci return NULL; 885bf215546Sopenharmony_ci } 886bf215546Sopenharmony_ci 887bf215546Sopenharmony_ci if (env_var_as_boolean("MESA_DISK_CACHE_SINGLE_FILE", false)) { 888bf215546Sopenharmony_ci path = concatenate_and_mkdir(mem_ctx, path, driver_id); 889bf215546Sopenharmony_ci if (!path) 890bf215546Sopenharmony_ci return NULL; 891bf215546Sopenharmony_ci 892bf215546Sopenharmony_ci path = concatenate_and_mkdir(mem_ctx, path, gpu_name); 893bf215546Sopenharmony_ci if (!path) 894bf215546Sopenharmony_ci return NULL; 895bf215546Sopenharmony_ci } 896bf215546Sopenharmony_ci 897bf215546Sopenharmony_ci return path; 898bf215546Sopenharmony_ci} 899bf215546Sopenharmony_ci 900bf215546Sopenharmony_cibool 901bf215546Sopenharmony_cidisk_cache_enabled() 902bf215546Sopenharmony_ci{ 903bf215546Sopenharmony_ci /* If running as a users other than the real user disable cache */ 904bf215546Sopenharmony_ci if (geteuid() != getuid()) 905bf215546Sopenharmony_ci return false; 906bf215546Sopenharmony_ci 907bf215546Sopenharmony_ci /* At user request, disable shader cache entirely. */ 908bf215546Sopenharmony_ci#ifdef SHADER_CACHE_DISABLE_BY_DEFAULT 909bf215546Sopenharmony_ci bool disable_by_default = true; 910bf215546Sopenharmony_ci#else 911bf215546Sopenharmony_ci bool disable_by_default = false; 912bf215546Sopenharmony_ci#endif 913bf215546Sopenharmony_ci char *envvar_name = "MESA_SHADER_CACHE_DISABLE"; 914bf215546Sopenharmony_ci if (!getenv(envvar_name)) { 915bf215546Sopenharmony_ci envvar_name = "MESA_GLSL_CACHE_DISABLE"; 916bf215546Sopenharmony_ci if (getenv(envvar_name)) 917bf215546Sopenharmony_ci fprintf(stderr, 918bf215546Sopenharmony_ci "*** MESA_GLSL_CACHE_DISABLE is deprecated; " 919bf215546Sopenharmony_ci "use MESA_SHADER_CACHE_DISABLE instead ***\n"); 920bf215546Sopenharmony_ci } 921bf215546Sopenharmony_ci 922bf215546Sopenharmony_ci if (env_var_as_boolean(envvar_name, disable_by_default)) 923bf215546Sopenharmony_ci return false; 924bf215546Sopenharmony_ci 925bf215546Sopenharmony_ci return true; 926bf215546Sopenharmony_ci} 927bf215546Sopenharmony_ci 928bf215546Sopenharmony_civoid * 929bf215546Sopenharmony_cidisk_cache_load_item_foz(struct disk_cache *cache, const cache_key key, 930bf215546Sopenharmony_ci size_t *size) 931bf215546Sopenharmony_ci{ 932bf215546Sopenharmony_ci size_t cache_tem_size = 0; 933bf215546Sopenharmony_ci void *cache_item = foz_read_entry(&cache->foz_db, key, &cache_tem_size); 934bf215546Sopenharmony_ci if (!cache_item) 935bf215546Sopenharmony_ci return NULL; 936bf215546Sopenharmony_ci 937bf215546Sopenharmony_ci uint8_t *uncompressed_data = 938bf215546Sopenharmony_ci parse_and_validate_cache_item(cache, cache_item, cache_tem_size, size); 939bf215546Sopenharmony_ci free(cache_item); 940bf215546Sopenharmony_ci 941bf215546Sopenharmony_ci return uncompressed_data; 942bf215546Sopenharmony_ci} 943bf215546Sopenharmony_ci 944bf215546Sopenharmony_cibool 945bf215546Sopenharmony_cidisk_cache_write_item_to_disk_foz(struct disk_cache_put_job *dc_job) 946bf215546Sopenharmony_ci{ 947bf215546Sopenharmony_ci struct blob cache_blob; 948bf215546Sopenharmony_ci blob_init(&cache_blob); 949bf215546Sopenharmony_ci 950bf215546Sopenharmony_ci if (!create_cache_item_header_and_blob(dc_job, &cache_blob)) 951bf215546Sopenharmony_ci return false; 952bf215546Sopenharmony_ci 953bf215546Sopenharmony_ci bool r = foz_write_entry(&dc_job->cache->foz_db, dc_job->key, 954bf215546Sopenharmony_ci cache_blob.data, cache_blob.size); 955bf215546Sopenharmony_ci 956bf215546Sopenharmony_ci blob_finish(&cache_blob); 957bf215546Sopenharmony_ci return r; 958bf215546Sopenharmony_ci} 959bf215546Sopenharmony_ci 960bf215546Sopenharmony_cibool 961bf215546Sopenharmony_cidisk_cache_load_cache_index(void *mem_ctx, struct disk_cache *cache) 962bf215546Sopenharmony_ci{ 963bf215546Sopenharmony_ci /* Load cache index into a hash map (from fossilise files) */ 964bf215546Sopenharmony_ci return foz_prepare(&cache->foz_db, cache->path); 965bf215546Sopenharmony_ci} 966bf215546Sopenharmony_ci 967bf215546Sopenharmony_cibool 968bf215546Sopenharmony_cidisk_cache_mmap_cache_index(void *mem_ctx, struct disk_cache *cache, 969bf215546Sopenharmony_ci char *path) 970bf215546Sopenharmony_ci{ 971bf215546Sopenharmony_ci int fd = -1; 972bf215546Sopenharmony_ci bool mapped = false; 973bf215546Sopenharmony_ci 974bf215546Sopenharmony_ci path = ralloc_asprintf(mem_ctx, "%s/index", cache->path); 975bf215546Sopenharmony_ci if (path == NULL) 976bf215546Sopenharmony_ci goto path_fail; 977bf215546Sopenharmony_ci 978bf215546Sopenharmony_ci fd = open(path, O_RDWR | O_CREAT | O_CLOEXEC, 0644); 979bf215546Sopenharmony_ci if (fd == -1) 980bf215546Sopenharmony_ci goto path_fail; 981bf215546Sopenharmony_ci 982bf215546Sopenharmony_ci struct stat sb; 983bf215546Sopenharmony_ci if (fstat(fd, &sb) == -1) 984bf215546Sopenharmony_ci goto path_fail; 985bf215546Sopenharmony_ci 986bf215546Sopenharmony_ci /* Force the index file to be the expected size. */ 987bf215546Sopenharmony_ci size_t size = sizeof(*cache->size) + CACHE_INDEX_MAX_KEYS * CACHE_KEY_SIZE; 988bf215546Sopenharmony_ci if (sb.st_size != size) { 989bf215546Sopenharmony_ci if (ftruncate(fd, size) == -1) 990bf215546Sopenharmony_ci goto path_fail; 991bf215546Sopenharmony_ci } 992bf215546Sopenharmony_ci 993bf215546Sopenharmony_ci /* We map this shared so that other processes see updates that we 994bf215546Sopenharmony_ci * make. 995bf215546Sopenharmony_ci * 996bf215546Sopenharmony_ci * Note: We do use atomic addition to ensure that multiple 997bf215546Sopenharmony_ci * processes don't scramble the cache size recorded in the 998bf215546Sopenharmony_ci * index. But we don't use any locking to prevent multiple 999bf215546Sopenharmony_ci * processes from updating the same entry simultaneously. The idea 1000bf215546Sopenharmony_ci * is that if either result lands entirely in the index, then 1001bf215546Sopenharmony_ci * that's equivalent to a well-ordered write followed by an 1002bf215546Sopenharmony_ci * eviction and a write. On the other hand, if the simultaneous 1003bf215546Sopenharmony_ci * writes result in a corrupt entry, that's not really any 1004bf215546Sopenharmony_ci * different than both entries being evicted, (since within the 1005bf215546Sopenharmony_ci * guarantees of the cryptographic hash, a corrupt entry is 1006bf215546Sopenharmony_ci * unlikely to ever match a real cache key). 1007bf215546Sopenharmony_ci */ 1008bf215546Sopenharmony_ci cache->index_mmap = mmap(NULL, size, PROT_READ | PROT_WRITE, 1009bf215546Sopenharmony_ci MAP_SHARED, fd, 0); 1010bf215546Sopenharmony_ci if (cache->index_mmap == MAP_FAILED) 1011bf215546Sopenharmony_ci goto path_fail; 1012bf215546Sopenharmony_ci cache->index_mmap_size = size; 1013bf215546Sopenharmony_ci 1014bf215546Sopenharmony_ci cache->size = (uint64_t *) cache->index_mmap; 1015bf215546Sopenharmony_ci cache->stored_keys = cache->index_mmap + sizeof(uint64_t); 1016bf215546Sopenharmony_ci mapped = true; 1017bf215546Sopenharmony_ci 1018bf215546Sopenharmony_cipath_fail: 1019bf215546Sopenharmony_ci if (fd != -1) 1020bf215546Sopenharmony_ci close(fd); 1021bf215546Sopenharmony_ci 1022bf215546Sopenharmony_ci return mapped; 1023bf215546Sopenharmony_ci} 1024bf215546Sopenharmony_ci 1025bf215546Sopenharmony_civoid 1026bf215546Sopenharmony_cidisk_cache_destroy_mmap(struct disk_cache *cache) 1027bf215546Sopenharmony_ci{ 1028bf215546Sopenharmony_ci munmap(cache->index_mmap, cache->index_mmap_size); 1029bf215546Sopenharmony_ci} 1030bf215546Sopenharmony_ci#endif 1031bf215546Sopenharmony_ci 1032bf215546Sopenharmony_ci#endif /* ENABLE_SHADER_CACHE */ 1033