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

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: 
        !            52:     c = qemu_mallocz(sizeof(*c));
        !            53:     c->size = num_tables;
        !            54:     c->entries = qemu_mallocz(sizeof(*c->entries) * num_tables);
        !            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: 
        !            73:     qemu_free(c->entries);
        !            74:     qemu_free(c);
        !            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: 

unix.superglobalmegacorp.com

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