summaryrefslogtreecommitdiff
path: root/src/util/mesa_cache_db_multipart.c
blob: 15257eaa50f7d03cc7570ea691e13a1f2a46557c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*
 * Copyright © 2022 Collabora, Ltd.
 *
 * SPDX-License-Identifier: MIT
 */

#include <sys/stat.h>

#include "detect_os.h"
#include "string.h"
#include "mesa_cache_db_multipart.h"
#include "u_debug.h"

bool
mesa_cache_db_multipart_open(struct mesa_cache_db_multipart *db,
                             const char *cache_path)
{
#if DETECT_OS_WINDOWS
   return false;
#else
   char *part_path = NULL;
   unsigned int i;

   db->num_parts = debug_get_num_option("MESA_DISK_CACHE_DATABASE_NUM_PARTS", 50);

   db->parts = calloc(db->num_parts, sizeof(*db->parts));
   if (!db->parts)
      return false;

   for (i = 0; i < db->num_parts; i++) {
      bool db_opened = false;

      if (asprintf(&part_path, "%s/part%u", cache_path, i) == -1)
         goto close_db;

      if (mkdir(part_path, 0755) == -1 && errno != EEXIST)
         goto free_path;

      /* DB opening may fail only in a case of a severe problem,
       * like IO error.
       */
      db_opened = mesa_cache_db_open(&db->parts[i], part_path);
      if (!db_opened)
         goto free_path;

      free(part_path);
   }

   /* remove old pre multi-part cache */
   mesa_db_wipe_path(cache_path);

   return true;

free_path:
   free(part_path);
close_db:
   while (i--)
      mesa_cache_db_close(&db->parts[i]);

   free(db->parts);

   return false;
#endif
}

void
mesa_cache_db_multipart_close(struct mesa_cache_db_multipart *db)
{
   while (db->num_parts--)
      mesa_cache_db_close(&db->parts[db->num_parts]);

   free(db->parts);
}

void
mesa_cache_db_multipart_set_size_limit(struct mesa_cache_db_multipart *db,
                                       uint64_t max_cache_size)
{
   for (unsigned int i = 0; i < db->num_parts; i++)
      mesa_cache_db_set_size_limit(&db->parts[i],
                                   max_cache_size / db->num_parts);
}

void *
mesa_cache_db_multipart_read_entry(struct mesa_cache_db_multipart *db,
                                   const uint8_t *cache_key_160bit,
                                   size_t *size)
{
   unsigned last_read_part = db->last_read_part;

   for (unsigned int i = 0; i < db->num_parts; i++) {
      unsigned int part = (last_read_part + i) % db->num_parts;

      void *cache_item = mesa_cache_db_read_entry(&db->parts[part],
                                                  cache_key_160bit, size);
      if (cache_item) {
         /* Likely that the next entry lookup will hit the same DB part. */
         db->last_read_part = part;
         return cache_item;
      }
   }

   return NULL;
}

static unsigned
mesa_cache_db_multipart_select_victim_part(struct mesa_cache_db_multipart *db)
{
   double best_score = 0, score;
   unsigned victim = 0;

   for (unsigned int i = 0; i < db->num_parts; i++) {
      score = mesa_cache_db_eviction_score(&db->parts[i]);
      if (score > best_score) {
         best_score = score;
         victim = i;
      }
   }

   return victim;
}

bool
mesa_cache_db_multipart_entry_write(struct mesa_cache_db_multipart *db,
                                    const uint8_t *cache_key_160bit,
                                    const void *blob, size_t blob_size)
{
   unsigned last_written_part = db->last_written_part;
   int wpart = -1;

   for (unsigned int i = 0; i < db->num_parts; i++) {
      unsigned int part = (last_written_part + i) % db->num_parts;

      /* Note that each DB part has own locking. */
      if (mesa_cache_db_has_space(&db->parts[part], blob_size)) {
         wpart = part;
         break;
      }
   }

   /* All DB parts are full. Writing to a full DB part will auto-trigger
    * eviction of LRU cache entries from the part. Select DB part that
    * contains majority of LRU cache entries.
    */
   if (wpart < 0)
      wpart = mesa_cache_db_multipart_select_victim_part(db);

   db->last_written_part = wpart;

   return mesa_cache_db_entry_write(&db->parts[wpart], cache_key_160bit,
                                    blob, blob_size);
}

void
mesa_cache_db_multipart_entry_remove(struct mesa_cache_db_multipart *db,
                                     const uint8_t *cache_key_160bit)
{
   for (unsigned int i = 0; i < db->num_parts; i++)
      mesa_cache_db_entry_remove(&db->parts[i], cache_key_160bit);
}