xref: /qemu/block/qcow2-cache.c (revision 0bb79c97)
149381094SKevin Wolf /*
249381094SKevin Wolf  * L2/refcount table cache for the QCOW2 format
349381094SKevin Wolf  *
449381094SKevin Wolf  * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
549381094SKevin Wolf  *
649381094SKevin Wolf  * Permission is hereby granted, free of charge, to any person obtaining a copy
749381094SKevin Wolf  * of this software and associated documentation files (the "Software"), to deal
849381094SKevin Wolf  * in the Software without restriction, including without limitation the rights
949381094SKevin Wolf  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1049381094SKevin Wolf  * copies of the Software, and to permit persons to whom the Software is
1149381094SKevin Wolf  * furnished to do so, subject to the following conditions:
1249381094SKevin Wolf  *
1349381094SKevin Wolf  * The above copyright notice and this permission notice shall be included in
1449381094SKevin Wolf  * all copies or substantial portions of the Software.
1549381094SKevin Wolf  *
1649381094SKevin Wolf  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1749381094SKevin Wolf  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1849381094SKevin Wolf  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1949381094SKevin Wolf  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2049381094SKevin Wolf  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2149381094SKevin Wolf  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2249381094SKevin Wolf  * THE SOFTWARE.
2349381094SKevin Wolf  */
2449381094SKevin Wolf 
2580c71a24SPeter Maydell #include "qemu/osdep.h"
26e2c1c34fSMarkus Armbruster #include "block/block-io.h"
275df022cfSPeter Maydell #include "qemu/memalign.h"
2849381094SKevin Wolf #include "qcow2.h"
293cce16f4SKevin Wolf #include "trace.h"
3049381094SKevin Wolf 
3149381094SKevin Wolf typedef struct Qcow2CachedTable {
3249381094SKevin Wolf     int64_t  offset;
332693310eSAlberto Garcia     uint64_t lru_counter;
3449381094SKevin Wolf     int      ref;
35909c260cSAlberto Garcia     bool     dirty;
3649381094SKevin Wolf } Qcow2CachedTable;
3749381094SKevin Wolf 
3849381094SKevin Wolf struct Qcow2Cache {
3949381094SKevin Wolf     Qcow2CachedTable       *entries;
4049381094SKevin Wolf     struct Qcow2Cache      *depends;
41bf595021SJes Sorensen     int                     size;
4203019d73SAlberto Garcia     int                     table_size;
433de0a294SKevin Wolf     bool                    depends_on_flush;
4472e80b89SAlberto Garcia     void                   *table_array;
452693310eSAlberto Garcia     uint64_t                lru_counter;
46279621c0SAlberto Garcia     uint64_t                cache_clean_lru_counter;
4749381094SKevin Wolf };
4849381094SKevin Wolf 
qcow2_cache_get_table_addr(Qcow2Cache * c,int table)499869b27bSAlberto Garcia static inline void *qcow2_cache_get_table_addr(Qcow2Cache *c, int table)
5072e80b89SAlberto Garcia {
5103019d73SAlberto Garcia     return (uint8_t *) c->table_array + (size_t) table * c->table_size;
5272e80b89SAlberto Garcia }
5372e80b89SAlberto Garcia 
qcow2_cache_get_table_idx(Qcow2Cache * c,void * table)54b3b8b6d9SAlberto Garcia static inline int qcow2_cache_get_table_idx(Qcow2Cache *c, void *table)
55baf07d60SAlberto Garcia {
56baf07d60SAlberto Garcia     ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array;
5703019d73SAlberto Garcia     int idx = table_offset / c->table_size;
5803019d73SAlberto Garcia     assert(idx >= 0 && idx < c->size && table_offset % c->table_size == 0);
59baf07d60SAlberto Garcia     return idx;
60baf07d60SAlberto Garcia }
61baf07d60SAlberto Garcia 
qcow2_cache_get_name(BDRVQcow2State * s,Qcow2Cache * c)624efb1f7cSMax Reitz static inline const char *qcow2_cache_get_name(BDRVQcow2State *s, Qcow2Cache *c)
634efb1f7cSMax Reitz {
644efb1f7cSMax Reitz     if (c == s->refcount_block_cache) {
654efb1f7cSMax Reitz         return "refcount block";
664efb1f7cSMax Reitz     } else if (c == s->l2_table_cache) {
674efb1f7cSMax Reitz         return "L2 table";
684efb1f7cSMax Reitz     } else {
694efb1f7cSMax Reitz         /* Do not abort, because this is not critical */
704efb1f7cSMax Reitz         return "unknown";
714efb1f7cSMax Reitz     }
724efb1f7cSMax Reitz }
734efb1f7cSMax Reitz 
qcow2_cache_table_release(Qcow2Cache * c,int i,int num_tables)74ebe988f3SAlberto Garcia static void qcow2_cache_table_release(Qcow2Cache *c, int i, int num_tables)
75355ee2d0SAlberto Garcia {
762f2c8d6bSAlberto Garcia /* Using MADV_DONTNEED to discard memory is a Linux-specific feature */
772f2c8d6bSAlberto Garcia #ifdef CONFIG_LINUX
789869b27bSAlberto Garcia     void *t = qcow2_cache_get_table_addr(c, i);
798e3b0cbbSMarc-André Lureau     int align = qemu_real_host_page_size();
8003019d73SAlberto Garcia     size_t mem_size = (size_t) c->table_size * num_tables;
81355ee2d0SAlberto Garcia     size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t;
82355ee2d0SAlberto Garcia     size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align);
8308546bcfSMax Reitz     if (mem_size > offset && length > 0) {
842f2c8d6bSAlberto Garcia         madvise((uint8_t *) t + offset, length, MADV_DONTNEED);
85355ee2d0SAlberto Garcia     }
86355ee2d0SAlberto Garcia #endif
87355ee2d0SAlberto Garcia }
88355ee2d0SAlberto Garcia 
can_clean_entry(Qcow2Cache * c,int i)89279621c0SAlberto Garcia static inline bool can_clean_entry(Qcow2Cache *c, int i)
90279621c0SAlberto Garcia {
91279621c0SAlberto Garcia     Qcow2CachedTable *t = &c->entries[i];
92279621c0SAlberto Garcia     return t->ref == 0 && !t->dirty && t->offset != 0 &&
93279621c0SAlberto Garcia         t->lru_counter <= c->cache_clean_lru_counter;
94279621c0SAlberto Garcia }
95279621c0SAlberto Garcia 
qcow2_cache_clean_unused(Qcow2Cache * c)96b2f68bffSAlberto Garcia void qcow2_cache_clean_unused(Qcow2Cache *c)
97279621c0SAlberto Garcia {
98279621c0SAlberto Garcia     int i = 0;
99279621c0SAlberto Garcia     while (i < c->size) {
100279621c0SAlberto Garcia         int to_clean = 0;
101279621c0SAlberto Garcia 
102279621c0SAlberto Garcia         /* Skip the entries that we don't need to clean */
103279621c0SAlberto Garcia         while (i < c->size && !can_clean_entry(c, i)) {
104279621c0SAlberto Garcia             i++;
105279621c0SAlberto Garcia         }
106279621c0SAlberto Garcia 
107279621c0SAlberto Garcia         /* And count how many we can clean in a row */
108279621c0SAlberto Garcia         while (i < c->size && can_clean_entry(c, i)) {
109279621c0SAlberto Garcia             c->entries[i].offset = 0;
110279621c0SAlberto Garcia             c->entries[i].lru_counter = 0;
111279621c0SAlberto Garcia             i++;
112279621c0SAlberto Garcia             to_clean++;
113279621c0SAlberto Garcia         }
114279621c0SAlberto Garcia 
115279621c0SAlberto Garcia         if (to_clean > 0) {
116ebe988f3SAlberto Garcia             qcow2_cache_table_release(c, i - to_clean, to_clean);
117279621c0SAlberto Garcia         }
118279621c0SAlberto Garcia     }
119279621c0SAlberto Garcia 
120279621c0SAlberto Garcia     c->cache_clean_lru_counter = c->lru_counter;
121279621c0SAlberto Garcia }
122279621c0SAlberto Garcia 
qcow2_cache_create(BlockDriverState * bs,int num_tables,unsigned table_size)1231221fe6fSAlberto Garcia Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
1241221fe6fSAlberto Garcia                                unsigned table_size)
12549381094SKevin Wolf {
126ff99129aSKevin Wolf     BDRVQcow2State *s = bs->opaque;
12749381094SKevin Wolf     Qcow2Cache *c;
12849381094SKevin Wolf 
1291221fe6fSAlberto Garcia     assert(num_tables > 0);
1301221fe6fSAlberto Garcia     assert(is_power_of_2(table_size));
1311221fe6fSAlberto Garcia     assert(table_size >= (1 << MIN_CLUSTER_BITS));
1321221fe6fSAlberto Garcia     assert(table_size <= s->cluster_size);
1331221fe6fSAlberto Garcia 
13402004bd4SMax Reitz     c = g_new0(Qcow2Cache, 1);
13549381094SKevin Wolf     c->size = num_tables;
1361221fe6fSAlberto Garcia     c->table_size = table_size;
13702004bd4SMax Reitz     c->entries = g_try_new0(Qcow2CachedTable, num_tables);
1389a4f4c31SKevin Wolf     c->table_array = qemu_try_blockalign(bs->file->bs,
13903019d73SAlberto Garcia                                          (size_t) num_tables * c->table_size);
14049381094SKevin Wolf 
14172e80b89SAlberto Garcia     if (!c->entries || !c->table_array) {
14272e80b89SAlberto Garcia         qemu_vfree(c->table_array);
14372e80b89SAlberto Garcia         g_free(c->entries);
14472e80b89SAlberto Garcia         g_free(c);
14572e80b89SAlberto Garcia         c = NULL;
14649381094SKevin Wolf     }
14749381094SKevin Wolf 
14849381094SKevin Wolf     return c;
14949381094SKevin Wolf }
15049381094SKevin Wolf 
qcow2_cache_destroy(Qcow2Cache * c)151e64d4072SAlberto Garcia int qcow2_cache_destroy(Qcow2Cache *c)
15249381094SKevin Wolf {
15349381094SKevin Wolf     int i;
15449381094SKevin Wolf 
15549381094SKevin Wolf     for (i = 0; i < c->size; i++) {
15649381094SKevin Wolf         assert(c->entries[i].ref == 0);
15749381094SKevin Wolf     }
15849381094SKevin Wolf 
15972e80b89SAlberto Garcia     qemu_vfree(c->table_array);
1607267c094SAnthony Liguori     g_free(c->entries);
1617267c094SAnthony Liguori     g_free(c);
16249381094SKevin Wolf 
16349381094SKevin Wolf     return 0;
16449381094SKevin Wolf }
16549381094SKevin Wolf 
1660bb79c97SKevin Wolf static int GRAPH_RDLOCK
qcow2_cache_flush_dependency(BlockDriverState * bs,Qcow2Cache * c)1670bb79c97SKevin Wolf qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c)
16849381094SKevin Wolf {
16949381094SKevin Wolf     int ret;
17049381094SKevin Wolf 
17149381094SKevin Wolf     ret = qcow2_cache_flush(bs, c->depends);
17249381094SKevin Wolf     if (ret < 0) {
17349381094SKevin Wolf         return ret;
17449381094SKevin Wolf     }
17549381094SKevin Wolf 
17649381094SKevin Wolf     c->depends = NULL;
1773de0a294SKevin Wolf     c->depends_on_flush = false;
1783de0a294SKevin Wolf 
17949381094SKevin Wolf     return 0;
18049381094SKevin Wolf }
18149381094SKevin Wolf 
1820bb79c97SKevin Wolf static int GRAPH_RDLOCK
qcow2_cache_entry_flush(BlockDriverState * bs,Qcow2Cache * c,int i)1830bb79c97SKevin Wolf qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
18449381094SKevin Wolf {
185ff99129aSKevin Wolf     BDRVQcow2State *s = bs->opaque;
1863de0a294SKevin Wolf     int ret = 0;
18749381094SKevin Wolf 
18849381094SKevin Wolf     if (!c->entries[i].dirty || !c->entries[i].offset) {
18949381094SKevin Wolf         return 0;
19049381094SKevin Wolf     }
19149381094SKevin Wolf 
1923cce16f4SKevin Wolf     trace_qcow2_cache_entry_flush(qemu_coroutine_self(),
1933cce16f4SKevin Wolf                                   c == s->l2_table_cache, i);
1943cce16f4SKevin Wolf 
19549381094SKevin Wolf     if (c->depends) {
19649381094SKevin Wolf         ret = qcow2_cache_flush_dependency(bs, c);
1973de0a294SKevin Wolf     } else if (c->depends_on_flush) {
1989a4f4c31SKevin Wolf         ret = bdrv_flush(bs->file->bs);
1993de0a294SKevin Wolf         if (ret >= 0) {
2003de0a294SKevin Wolf             c->depends_on_flush = false;
2013de0a294SKevin Wolf         }
2023de0a294SKevin Wolf     }
2033de0a294SKevin Wolf 
20449381094SKevin Wolf     if (ret < 0) {
20549381094SKevin Wolf         return ret;
20649381094SKevin Wolf     }
20749381094SKevin Wolf 
20829c1a730SKevin Wolf     if (c == s->refcount_block_cache) {
209231bb267SMax Reitz         ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK,
210966b000fSKevin Wolf                 c->entries[i].offset, c->table_size, false);
211cf93980eSMax Reitz     } else if (c == s->l2_table_cache) {
212231bb267SMax Reitz         ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
213966b000fSKevin Wolf                 c->entries[i].offset, c->table_size, false);
214cf93980eSMax Reitz     } else {
215231bb267SMax Reitz         ret = qcow2_pre_write_overlap_check(bs, 0,
216966b000fSKevin Wolf                 c->entries[i].offset, c->table_size, false);
217cf93980eSMax Reitz     }
218cf93980eSMax Reitz 
219cf93980eSMax Reitz     if (ret < 0) {
220cf93980eSMax Reitz         return ret;
221cf93980eSMax Reitz     }
222cf93980eSMax Reitz 
223cf93980eSMax Reitz     if (c == s->refcount_block_cache) {
22429c1a730SKevin Wolf         BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
22529c1a730SKevin Wolf     } else if (c == s->l2_table_cache) {
22629c1a730SKevin Wolf         BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
22729c1a730SKevin Wolf     }
22829c1a730SKevin Wolf 
22932cc71deSAlberto Faria     ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->table_size,
23032cc71deSAlberto Faria                       qcow2_cache_get_table_addr(c, i), 0);
23149381094SKevin Wolf     if (ret < 0) {
23249381094SKevin Wolf         return ret;
23349381094SKevin Wolf     }
23449381094SKevin Wolf 
23549381094SKevin Wolf     c->entries[i].dirty = false;
23649381094SKevin Wolf 
23749381094SKevin Wolf     return 0;
23849381094SKevin Wolf }
23949381094SKevin Wolf 
qcow2_cache_write(BlockDriverState * bs,Qcow2Cache * c)240f3c3b87dSDenis V. Lunev int qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c)
24149381094SKevin Wolf {
242ff99129aSKevin Wolf     BDRVQcow2State *s = bs->opaque;
24349381094SKevin Wolf     int result = 0;
24449381094SKevin Wolf     int ret;
24549381094SKevin Wolf     int i;
24649381094SKevin Wolf 
2473cce16f4SKevin Wolf     trace_qcow2_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache);
2483cce16f4SKevin Wolf 
24949381094SKevin Wolf     for (i = 0; i < c->size; i++) {
25049381094SKevin Wolf         ret = qcow2_cache_entry_flush(bs, c, i);
25149381094SKevin Wolf         if (ret < 0 && result != -ENOSPC) {
25249381094SKevin Wolf             result = ret;
25349381094SKevin Wolf         }
25449381094SKevin Wolf     }
25549381094SKevin Wolf 
256f3c3b87dSDenis V. Lunev     return result;
257f3c3b87dSDenis V. Lunev }
258f3c3b87dSDenis V. Lunev 
qcow2_cache_flush(BlockDriverState * bs,Qcow2Cache * c)259f3c3b87dSDenis V. Lunev int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
260f3c3b87dSDenis V. Lunev {
261f3c3b87dSDenis V. Lunev     int result = qcow2_cache_write(bs, c);
262f3c3b87dSDenis V. Lunev 
26349381094SKevin Wolf     if (result == 0) {
264f3c3b87dSDenis V. Lunev         int ret = bdrv_flush(bs->file->bs);
26549381094SKevin Wolf         if (ret < 0) {
26649381094SKevin Wolf             result = ret;
26749381094SKevin Wolf         }
26849381094SKevin Wolf     }
26949381094SKevin Wolf 
27049381094SKevin Wolf     return result;
27149381094SKevin Wolf }
27249381094SKevin Wolf 
qcow2_cache_set_dependency(BlockDriverState * bs,Qcow2Cache * c,Qcow2Cache * dependency)27349381094SKevin Wolf int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
27449381094SKevin Wolf     Qcow2Cache *dependency)
27549381094SKevin Wolf {
27649381094SKevin Wolf     int ret;
27749381094SKevin Wolf 
27849381094SKevin Wolf     if (dependency->depends) {
27949381094SKevin Wolf         ret = qcow2_cache_flush_dependency(bs, dependency);
28049381094SKevin Wolf         if (ret < 0) {
28149381094SKevin Wolf             return ret;
28249381094SKevin Wolf         }
28349381094SKevin Wolf     }
28449381094SKevin Wolf 
28549381094SKevin Wolf     if (c->depends && (c->depends != dependency)) {
28649381094SKevin Wolf         ret = qcow2_cache_flush_dependency(bs, c);
28749381094SKevin Wolf         if (ret < 0) {
28849381094SKevin Wolf             return ret;
28949381094SKevin Wolf         }
29049381094SKevin Wolf     }
29149381094SKevin Wolf 
29249381094SKevin Wolf     c->depends = dependency;
29349381094SKevin Wolf     return 0;
29449381094SKevin Wolf }
29549381094SKevin Wolf 
qcow2_cache_depends_on_flush(Qcow2Cache * c)2963de0a294SKevin Wolf void qcow2_cache_depends_on_flush(Qcow2Cache *c)
2973de0a294SKevin Wolf {
2983de0a294SKevin Wolf     c->depends_on_flush = true;
2993de0a294SKevin Wolf }
3003de0a294SKevin Wolf 
qcow2_cache_empty(BlockDriverState * bs,Qcow2Cache * c)301e7108feaSMax Reitz int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c)
302e7108feaSMax Reitz {
303e7108feaSMax Reitz     int ret, i;
304e7108feaSMax Reitz 
305e7108feaSMax Reitz     ret = qcow2_cache_flush(bs, c);
306e7108feaSMax Reitz     if (ret < 0) {
307e7108feaSMax Reitz         return ret;
308e7108feaSMax Reitz     }
309e7108feaSMax Reitz 
310e7108feaSMax Reitz     for (i = 0; i < c->size; i++) {
311e7108feaSMax Reitz         assert(c->entries[i].ref == 0);
312e7108feaSMax Reitz         c->entries[i].offset = 0;
3132693310eSAlberto Garcia         c->entries[i].lru_counter = 0;
314e7108feaSMax Reitz     }
315e7108feaSMax Reitz 
316ebe988f3SAlberto Garcia     qcow2_cache_table_release(c, 0, c->size);
317355ee2d0SAlberto Garcia 
3182693310eSAlberto Garcia     c->lru_counter = 0;
3192693310eSAlberto Garcia 
320e7108feaSMax Reitz     return 0;
321e7108feaSMax Reitz }
322e7108feaSMax Reitz 
3230bb79c97SKevin Wolf static int GRAPH_RDLOCK
qcow2_cache_do_get(BlockDriverState * bs,Qcow2Cache * c,uint64_t offset,void ** table,bool read_from_disk)3240bb79c97SKevin Wolf qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
3250bb79c97SKevin Wolf                    void **table, bool read_from_disk)
32649381094SKevin Wolf {
327ff99129aSKevin Wolf     BDRVQcow2State *s = bs->opaque;
32849381094SKevin Wolf     int i;
32949381094SKevin Wolf     int ret;
330812e4082SAlberto Garcia     int lookup_index;
331fdfbca82SAlberto Garcia     uint64_t min_lru_counter = UINT64_MAX;
332fdfbca82SAlberto Garcia     int min_lru_index = -1;
33349381094SKevin Wolf 
3344efb1f7cSMax Reitz     assert(offset != 0);
3354efb1f7cSMax Reitz 
3363cce16f4SKevin Wolf     trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache,
3373cce16f4SKevin Wolf                           offset, read_from_disk);
3383cce16f4SKevin Wolf 
33903019d73SAlberto Garcia     if (!QEMU_IS_ALIGNED(offset, c->table_size)) {
3404efb1f7cSMax Reitz         qcow2_signal_corruption(bs, true, -1, -1, "Cannot get entry from %s "
3414efb1f7cSMax Reitz                                 "cache: Offset %#" PRIx64 " is unaligned",
3424efb1f7cSMax Reitz                                 qcow2_cache_get_name(s, c), offset);
3434efb1f7cSMax Reitz         return -EIO;
3444efb1f7cSMax Reitz     }
3454efb1f7cSMax Reitz 
34649381094SKevin Wolf     /* Check if the table is already cached */
34703019d73SAlberto Garcia     i = lookup_index = (offset / c->table_size * 4) % c->size;
348812e4082SAlberto Garcia     do {
349fdfbca82SAlberto Garcia         const Qcow2CachedTable *t = &c->entries[i];
350fdfbca82SAlberto Garcia         if (t->offset == offset) {
35149381094SKevin Wolf             goto found;
35249381094SKevin Wolf         }
353fdfbca82SAlberto Garcia         if (t->ref == 0 && t->lru_counter < min_lru_counter) {
354fdfbca82SAlberto Garcia             min_lru_counter = t->lru_counter;
355fdfbca82SAlberto Garcia             min_lru_index = i;
356fdfbca82SAlberto Garcia         }
357812e4082SAlberto Garcia         if (++i == c->size) {
358812e4082SAlberto Garcia             i = 0;
35949381094SKevin Wolf         }
360812e4082SAlberto Garcia     } while (i != lookup_index);
36149381094SKevin Wolf 
362fdfbca82SAlberto Garcia     if (min_lru_index == -1) {
363fdfbca82SAlberto Garcia         /* This can't happen in current synchronous code, but leave the check
364fdfbca82SAlberto Garcia          * here as a reminder for whoever starts using AIO with the cache */
365fdfbca82SAlberto Garcia         abort();
366fdfbca82SAlberto Garcia     }
367fdfbca82SAlberto Garcia 
368fdfbca82SAlberto Garcia     /* Cache miss: write a table back and replace it */
369fdfbca82SAlberto Garcia     i = min_lru_index;
3703cce16f4SKevin Wolf     trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(),
3713cce16f4SKevin Wolf                                         c == s->l2_table_cache, i);
37249381094SKevin Wolf 
37349381094SKevin Wolf     ret = qcow2_cache_entry_flush(bs, c, i);
37449381094SKevin Wolf     if (ret < 0) {
37549381094SKevin Wolf         return ret;
37649381094SKevin Wolf     }
37749381094SKevin Wolf 
3783cce16f4SKevin Wolf     trace_qcow2_cache_get_read(qemu_coroutine_self(),
3793cce16f4SKevin Wolf                                c == s->l2_table_cache, i);
38049381094SKevin Wolf     c->entries[i].offset = 0;
38149381094SKevin Wolf     if (read_from_disk) {
38229c1a730SKevin Wolf         if (c == s->l2_table_cache) {
38329c1a730SKevin Wolf             BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD);
38429c1a730SKevin Wolf         }
38529c1a730SKevin Wolf 
38632cc71deSAlberto Faria         ret = bdrv_pread(bs->file, offset, c->table_size,
38732cc71deSAlberto Faria                          qcow2_cache_get_table_addr(c, i), 0);
38849381094SKevin Wolf         if (ret < 0) {
38949381094SKevin Wolf             return ret;
39049381094SKevin Wolf         }
39149381094SKevin Wolf     }
39249381094SKevin Wolf 
39349381094SKevin Wolf     c->entries[i].offset = offset;
39449381094SKevin Wolf 
39549381094SKevin Wolf     /* And return the right table */
39649381094SKevin Wolf found:
39749381094SKevin Wolf     c->entries[i].ref++;
3989869b27bSAlberto Garcia     *table = qcow2_cache_get_table_addr(c, i);
3993cce16f4SKevin Wolf 
4003cce16f4SKevin Wolf     trace_qcow2_cache_get_done(qemu_coroutine_self(),
4013cce16f4SKevin Wolf                                c == s->l2_table_cache, i);
4023cce16f4SKevin Wolf 
40349381094SKevin Wolf     return 0;
40449381094SKevin Wolf }
40549381094SKevin Wolf 
qcow2_cache_get(BlockDriverState * bs,Qcow2Cache * c,uint64_t offset,void ** table)40649381094SKevin Wolf int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
40749381094SKevin Wolf     void **table)
40849381094SKevin Wolf {
40949381094SKevin Wolf     return qcow2_cache_do_get(bs, c, offset, table, true);
41049381094SKevin Wolf }
41149381094SKevin Wolf 
qcow2_cache_get_empty(BlockDriverState * bs,Qcow2Cache * c,uint64_t offset,void ** table)41249381094SKevin Wolf int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
41349381094SKevin Wolf     void **table)
41449381094SKevin Wolf {
41549381094SKevin Wolf     return qcow2_cache_do_get(bs, c, offset, table, false);
41649381094SKevin Wolf }
41749381094SKevin Wolf 
qcow2_cache_put(Qcow2Cache * c,void ** table)4182013c3d4SAlberto Garcia void qcow2_cache_put(Qcow2Cache *c, void **table)
41949381094SKevin Wolf {
420b3b8b6d9SAlberto Garcia     int i = qcow2_cache_get_table_idx(c, *table);
42149381094SKevin Wolf 
42249381094SKevin Wolf     c->entries[i].ref--;
42349381094SKevin Wolf     *table = NULL;
42449381094SKevin Wolf 
4252693310eSAlberto Garcia     if (c->entries[i].ref == 0) {
4262693310eSAlberto Garcia         c->entries[i].lru_counter = ++c->lru_counter;
4272693310eSAlberto Garcia     }
4282693310eSAlberto Garcia 
42949381094SKevin Wolf     assert(c->entries[i].ref >= 0);
43049381094SKevin Wolf }
43149381094SKevin Wolf 
qcow2_cache_entry_mark_dirty(Qcow2Cache * c,void * table)4322d135ee9SAlberto Garcia void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table)
43349381094SKevin Wolf {
434b3b8b6d9SAlberto Garcia     int i = qcow2_cache_get_table_idx(c, table);
435baf07d60SAlberto Garcia     assert(c->entries[i].offset != 0);
43649381094SKevin Wolf     c->entries[i].dirty = true;
43749381094SKevin Wolf }
438f71c08eaSPavel Butsykin 
qcow2_cache_is_table_offset(Qcow2Cache * c,uint64_t offset)4396e6fa760SAlberto Garcia void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset)
440f71c08eaSPavel Butsykin {
441f71c08eaSPavel Butsykin     int i;
442f71c08eaSPavel Butsykin 
443f71c08eaSPavel Butsykin     for (i = 0; i < c->size; i++) {
444f71c08eaSPavel Butsykin         if (c->entries[i].offset == offset) {
4459869b27bSAlberto Garcia             return qcow2_cache_get_table_addr(c, i);
446f71c08eaSPavel Butsykin         }
447f71c08eaSPavel Butsykin     }
448f71c08eaSPavel Butsykin     return NULL;
449f71c08eaSPavel Butsykin }
450f71c08eaSPavel Butsykin 
qcow2_cache_discard(Qcow2Cache * c,void * table)45177aadd7bSAlberto Garcia void qcow2_cache_discard(Qcow2Cache *c, void *table)
452f71c08eaSPavel Butsykin {
453b3b8b6d9SAlberto Garcia     int i = qcow2_cache_get_table_idx(c, table);
454f71c08eaSPavel Butsykin 
455f71c08eaSPavel Butsykin     assert(c->entries[i].ref == 0);
456f71c08eaSPavel Butsykin 
457f71c08eaSPavel Butsykin     c->entries[i].offset = 0;
458f71c08eaSPavel Butsykin     c->entries[i].lru_counter = 0;
459f71c08eaSPavel Butsykin     c->entries[i].dirty = false;
460f71c08eaSPavel Butsykin 
461ebe988f3SAlberto Garcia     qcow2_cache_table_release(c, i, 1);
462f71c08eaSPavel Butsykin }
463