Annotation of qemu/block/qcow2-cache.c, revision 1.1.1.3

1.1       root        1: /*
                      2:  * L2/refcount table cache for the QCOW2 format
                      3:  *
                      4:  * Copyright (c) 2010 Kevin Wolf <[email protected]>
                      5:  *
                      6:  * Permission is hereby granted, free of charge, to any person obtaining a copy
                      7:  * of this software and associated documentation files (the "Software"), to deal
                      8:  * in the Software without restriction, including without limitation the rights
                      9:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                     10:  * copies of the Software, and to permit persons to whom the Software is
                     11:  * furnished to do so, subject to the following conditions:
                     12:  *
                     13:  * The above copyright notice and this permission notice shall be included in
                     14:  * all copies or substantial portions of the Software.
                     15:  *
                     16:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                     17:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                     18:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
                     19:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                     20:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                     21:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
                     22:  * THE SOFTWARE.
                     23:  */
                     24: 
                     25: #include "block_int.h"
                     26: #include "qemu-common.h"
                     27: #include "qcow2.h"
                     28: 
                     29: typedef struct Qcow2CachedTable {
                     30:     void*   table;
                     31:     int64_t offset;
                     32:     bool    dirty;
                     33:     int     cache_hits;
                     34:     int     ref;
                     35: } Qcow2CachedTable;
                     36: 
                     37: struct Qcow2Cache {
                     38:     Qcow2CachedTable*       entries;
                     39:     struct Qcow2Cache*      depends;
                     40:     int                     size;
                     41:     bool                    depends_on_flush;
                     42:     bool                    writethrough;
                     43: };
                     44: 
                     45: Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
                     46:     bool writethrough)
                     47: {
                     48:     BDRVQcowState *s = bs->opaque;
                     49:     Qcow2Cache *c;
                     50:     int i;
                     51: 
1.1.1.3 ! root       52:     c = g_malloc0(sizeof(*c));
1.1       root       53:     c->size = num_tables;
1.1.1.3 ! root       54:     c->entries = g_malloc0(sizeof(*c->entries) * num_tables);
1.1       root       55:     c->writethrough = writethrough;
                     56: 
                     57:     for (i = 0; i < c->size; i++) {
                     58:         c->entries[i].table = qemu_blockalign(bs, s->cluster_size);
                     59:     }
                     60: 
                     61:     return c;
                     62: }
                     63: 
                     64: int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c)
                     65: {
                     66:     int i;
                     67: 
                     68:     for (i = 0; i < c->size; i++) {
                     69:         assert(c->entries[i].ref == 0);
                     70:         qemu_vfree(c->entries[i].table);
                     71:     }
                     72: 
1.1.1.3 ! root       73:     g_free(c->entries);
        !            74:     g_free(c);
1.1       root       75: 
                     76:     return 0;
                     77: }
                     78: 
                     79: static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c)
                     80: {
                     81:     int ret;
                     82: 
                     83:     ret = qcow2_cache_flush(bs, c->depends);
                     84:     if (ret < 0) {
                     85:         return ret;
                     86:     }
                     87: 
                     88:     c->depends = NULL;
                     89:     c->depends_on_flush = false;
                     90: 
                     91:     return 0;
                     92: }
                     93: 
                     94: static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
                     95: {
                     96:     BDRVQcowState *s = bs->opaque;
                     97:     int ret = 0;
                     98: 
                     99:     if (!c->entries[i].dirty || !c->entries[i].offset) {
                    100:         return 0;
                    101:     }
                    102: 
                    103:     if (c->depends) {
                    104:         ret = qcow2_cache_flush_dependency(bs, c);
                    105:     } else if (c->depends_on_flush) {
                    106:         ret = bdrv_flush(bs->file);
                    107:         if (ret >= 0) {
                    108:             c->depends_on_flush = false;
                    109:         }
                    110:     }
                    111: 
                    112:     if (ret < 0) {
                    113:         return ret;
                    114:     }
                    115: 
                    116:     if (c == s->refcount_block_cache) {
                    117:         BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
                    118:     } else if (c == s->l2_table_cache) {
                    119:         BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
                    120:     }
                    121: 
                    122:     ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->entries[i].table,
                    123:         s->cluster_size);
                    124:     if (ret < 0) {
                    125:         return ret;
                    126:     }
                    127: 
                    128:     c->entries[i].dirty = false;
                    129: 
                    130:     return 0;
                    131: }
                    132: 
                    133: int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
                    134: {
                    135:     int result = 0;
                    136:     int ret;
                    137:     int i;
                    138: 
                    139:     for (i = 0; i < c->size; i++) {
                    140:         ret = qcow2_cache_entry_flush(bs, c, i);
                    141:         if (ret < 0 && result != -ENOSPC) {
                    142:             result = ret;
                    143:         }
                    144:     }
                    145: 
                    146:     if (result == 0) {
                    147:         ret = bdrv_flush(bs->file);
                    148:         if (ret < 0) {
                    149:             result = ret;
                    150:         }
                    151:     }
                    152: 
                    153:     return result;
                    154: }
                    155: 
                    156: int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
                    157:     Qcow2Cache *dependency)
                    158: {
                    159:     int ret;
                    160: 
                    161:     if (dependency->depends) {
                    162:         ret = qcow2_cache_flush_dependency(bs, dependency);
                    163:         if (ret < 0) {
                    164:             return ret;
                    165:         }
                    166:     }
                    167: 
                    168:     if (c->depends && (c->depends != dependency)) {
                    169:         ret = qcow2_cache_flush_dependency(bs, c);
                    170:         if (ret < 0) {
                    171:             return ret;
                    172:         }
                    173:     }
                    174: 
                    175:     c->depends = dependency;
                    176:     return 0;
                    177: }
                    178: 
                    179: void qcow2_cache_depends_on_flush(Qcow2Cache *c)
                    180: {
                    181:     c->depends_on_flush = true;
                    182: }
                    183: 
                    184: static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c)
                    185: {
                    186:     int i;
                    187:     int min_count = INT_MAX;
                    188:     int min_index = -1;
                    189: 
                    190: 
                    191:     for (i = 0; i < c->size; i++) {
                    192:         if (c->entries[i].ref) {
                    193:             continue;
                    194:         }
                    195: 
                    196:         if (c->entries[i].cache_hits < min_count) {
                    197:             min_index = i;
                    198:             min_count = c->entries[i].cache_hits;
                    199:         }
                    200: 
                    201:         /* Give newer hits priority */
                    202:         /* TODO Check how to optimize the replacement strategy */
                    203:         c->entries[i].cache_hits /= 2;
                    204:     }
                    205: 
                    206:     if (min_index == -1) {
                    207:         /* This can't happen in current synchronous code, but leave the check
                    208:          * here as a reminder for whoever starts using AIO with the cache */
                    209:         abort();
                    210:     }
                    211:     return min_index;
                    212: }
                    213: 
                    214: static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
                    215:     uint64_t offset, void **table, bool read_from_disk)
                    216: {
                    217:     BDRVQcowState *s = bs->opaque;
                    218:     int i;
                    219:     int ret;
                    220: 
                    221:     /* Check if the table is already cached */
                    222:     for (i = 0; i < c->size; i++) {
                    223:         if (c->entries[i].offset == offset) {
                    224:             goto found;
                    225:         }
                    226:     }
                    227: 
                    228:     /* If not, write a table back and replace it */
                    229:     i = qcow2_cache_find_entry_to_replace(c);
                    230:     if (i < 0) {
                    231:         return i;
                    232:     }
                    233: 
                    234:     ret = qcow2_cache_entry_flush(bs, c, i);
                    235:     if (ret < 0) {
                    236:         return ret;
                    237:     }
                    238: 
                    239:     c->entries[i].offset = 0;
                    240:     if (read_from_disk) {
                    241:         if (c == s->l2_table_cache) {
                    242:             BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD);
                    243:         }
                    244: 
                    245:         ret = bdrv_pread(bs->file, offset, c->entries[i].table, s->cluster_size);
                    246:         if (ret < 0) {
                    247:             return ret;
                    248:         }
                    249:     }
                    250: 
                    251:     /* Give the table some hits for the start so that it won't be replaced
                    252:      * immediately. The number 32 is completely arbitrary. */
                    253:     c->entries[i].cache_hits = 32;
                    254:     c->entries[i].offset = offset;
                    255: 
                    256:     /* And return the right table */
                    257: found:
                    258:     c->entries[i].cache_hits++;
                    259:     c->entries[i].ref++;
                    260:     *table = c->entries[i].table;
                    261:     return 0;
                    262: }
                    263: 
                    264: int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
                    265:     void **table)
                    266: {
                    267:     return qcow2_cache_do_get(bs, c, offset, table, true);
                    268: }
                    269: 
                    270: int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
                    271:     void **table)
                    272: {
                    273:     return qcow2_cache_do_get(bs, c, offset, table, false);
                    274: }
                    275: 
                    276: int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table)
                    277: {
                    278:     int i;
                    279: 
                    280:     for (i = 0; i < c->size; i++) {
                    281:         if (c->entries[i].table == *table) {
                    282:             goto found;
                    283:         }
                    284:     }
                    285:     return -ENOENT;
                    286: 
                    287: found:
                    288:     c->entries[i].ref--;
                    289:     *table = NULL;
                    290: 
                    291:     assert(c->entries[i].ref >= 0);
                    292: 
                    293:     if (c->writethrough) {
                    294:         return qcow2_cache_entry_flush(bs, c, i);
                    295:     } else {
                    296:         return 0;
                    297:     }
                    298: }
                    299: 
                    300: void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table)
                    301: {
                    302:     int i;
                    303: 
                    304:     for (i = 0; i < c->size; i++) {
                    305:         if (c->entries[i].table == table) {
                    306:             goto found;
                    307:         }
                    308:     }
                    309:     abort();
                    310: 
                    311: found:
                    312:     c->entries[i].dirty = true;
                    313: }
                    314: 
1.1.1.2   root      315: bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c,
                    316:     bool enable)
                    317: {
                    318:     bool old = c->writethrough;
                    319: 
                    320:     if (!old && enable) {
                    321:         qcow2_cache_flush(bs, c);
                    322:     }
                    323: 
                    324:     c->writethrough = enable;
                    325:     return old;
                    326: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.