From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:59863) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Upfpk-00018U-9g for qemu-devel@nongnu.org; Thu, 20 Jun 2013 10:27:40 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Upfpc-00010c-Nl for qemu-devel@nongnu.org; Thu, 20 Jun 2013 10:27:32 -0400 Received: from nodalink.pck.nerim.net ([62.212.105.220]:37558 helo=paradis.irqsave.net) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Upfpb-00010U-Ej for qemu-devel@nongnu.org; Thu, 20 Jun 2013 10:27:24 -0400 From: =?UTF-8?q?Beno=C3=AEt=20Canet?= Date: Thu, 20 Jun 2013 16:26:14 +0200 Message-Id: <1371738392-9594-7-git-send-email-benoit@irqsave.net> In-Reply-To: <1371738392-9594-1-git-send-email-benoit@irqsave.net> References: <1371738392-9594-1-git-send-email-benoit@irqsave.net> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: [Qemu-devel] =?utf-8?q?=5BRFC_V8_06/24=5D_qcow2=3A_Add_the_dedupl?= =?utf-8?q?ication_store=2E?= List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: kwolf@redhat.com, =?UTF-8?q?Beno=C3=AEt=20Canet?= , stefanha@redhat.com The key value store is an SSD optimised store used to store deduplication= 's QCowHashInfo. It binds together the QCowJournal, the QCowLogStore and the QCowHashStore= and must be called be the deduplication code. Signed-off-by: Benoit Canet --- block/Makefile.objs | 2 +- block/qcow2-store.c | 771 +++++++++++++++++++++++++++++++++++++++++++++= ++++++ block/qcow2.h | 20 +- 3 files changed, 791 insertions(+), 2 deletions(-) create mode 100644 block/qcow2-store.c diff --git a/block/Makefile.objs b/block/Makefile.objs index cafd4f8..2d1a269 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -1,6 +1,6 @@ block-obj-y +=3D raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o v= pc.o vvfat.o block-obj-y +=3D qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot= .o qcow2-cache.o -block-obj-y +=3D qcow2-journal.o qcow2-log-store.o qcow2-hash-store.o +block-obj-y +=3D qcow2-journal.o qcow2-log-store.o qcow2-hash-store.o qc= ow2-store.o block-obj-y +=3D qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluste= r.o block-obj-y +=3D qed-check.o block-obj-y +=3D vhdx.o diff --git a/block/qcow2-store.c b/block/qcow2-store.c new file mode 100644 index 0000000..8e3fad5 --- /dev/null +++ b/block/qcow2-store.c @@ -0,0 +1,771 @@ +/* + * QCOW2 key value store for SSD storage + * + * Copyright (C) Nodalink, SARL. 2013 + * + * Author: + * Beno=C3=AEt Canet + * + * Permission is hereby granted, free of charge, to any person obtaining= a copy + * of this software and associated documentation files (the "Software"),= to deal + * in the Software without restriction, including without limitation the= rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or = sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be includ= ed in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRE= SS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILI= TY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHA= LL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR = OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISI= NG FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING= S IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "block/block_int.h" +#include "block/qcow2.h" + +/* The qcow2_store combine the qcow2_log_store with the qcow2_hash_store= -> + * + * insertions: + * Entries are inserted in the qcow2_log_store which delegate writing to= disk + * to the qcow2_journal. + * When full the qcow2_log_store is frozen into an incarnation and given= to the + * qcow2_hash_store-> A new qcow2_log store is then created + * + * deletions: + * Deletions are insertions with an additional flag set and a mandatory + * write flush of the journal: loosing an insertion is not a problem whe= reas + * loosing a deletion is a problem. + * + * queries: + * The log store is first queried and if the entry is not found in it th= e hash + * store is queried. + * When queried the hash store it will check for the presence of the + * QCowHashInfo in every incarnation starting from the youngest to the o= ldest. + * + * fifo: + * A user configurable setting allow the qcow2_store to act as a fifo. + * the oldest QCowHashInfos stored in it will be dropped an incarnation = at once. + * Loosing the oldest QCowHashInfo won't corrupt the dedup metadata the = only + * risk if to make to dedup ratio lower. + * + * algorithm: + * This code is an implementation of the first two stages of the SILT pa= per. + * The third stage was dropped to lower the complexity of the code + * The idea of behaving as a FIFO and the notion of incarnations comes f= rom + * BufferHash and is handy to help reducing RAM consumption. + * + * SILT: www.cs.cmu.edu/~dga/papers/silt-sosp2011.pdf + * BufferHash: research.microsoft.com/en-us/people/sumann/bufferhash-nsd= i10.pdf + */ + +/* This function initialize a key value store + * + * @store: the store to initialize + * @order: the bit order + */ +static void qcow2_store_init(BlockDriverState *bs, + QCowStore *store, + uint32_t order) +{ + store->order =3D order; + qemu_co_mutex_init(&store->insert_lock); + qcow2_log_store_init(bs, &store->log_store, order); + qcow2_log_store_init(bs, &store->frozen_log_store, order); + qcow2_hash_store_init(&store->hash_store, order); +} + +/* This function cleanup a key value store + * + * @store: the store to cleanup + */ +void qcow2_store_cleanup(QCowStore *store) +{ + qcow2_log_store_cleanup(&store->log_store); + qcow2_log_store_cleanup(&store->frozen_log_store); + qcow2_hash_store_cleanup(&store->hash_store); +} + +/* This function look for a given hash info in the store + * + * + * @store: the QCowStore to lookup into + * @hash_info: the QCowHashInfo to complete : at last the hash must be f= illed. + * @ret: 1 on successfull lookup, 0 if not found, -errno on error + */ +int qcow2_store_get(BlockDriverState *bs, QCowStore *store, + QCowHashInfo *hash_info) +{ + bool found; + int ret =3D 0; + + /* we do the same query from the youngest to the oldest store */ + + found =3D qcow2_log_store_get(&store->log_store, hash_info); + + if (found) { + goto exit; + } + + if (store->freezing) { + found =3D qcow2_log_store_get(&store->frozen_log_store, hash_in= fo); + } + + /* found or error */ + if (found) { + goto exit; + } + + ret =3D qcow2_hash_store_get(bs, &store->hash_store, hash_info); + +exit: + /* if nothing found or error */ + if (!found && ret <=3D 0) { + return ret; + } + + /* if hash info found but deleted from store return not found */ + if (hash_info->first_logical_sect & QCOW_DEDUP_DELETED) { + return 0; + } + + return 1; +} + +/* this structure is used between the two following functions */ +typedef struct { + BlockDriverState *bs; + QCowStore *store; +} QCowIncarnateArgs; + +/* This coroutine convert a log store into an incarnation + * + * While doing this convertion we take care of two things : + * + * -not allocating QCOW2 disk space while the guest is frozen + * -not reseting the freezing flag to false while the guest is frozen + * + * All operations in between these two steps can be done freely as they = are only + * writes on an already allocated disk space area. + * + * As long a the freezing flag is set to true the target of the migratio= n would + * restart the freeze cleanly. + * + * @opaque: the function arguments (bs, log_store and hash_store) + */ +static void coroutine_fn qcow2_store_co_incarnate(void *opaque) +{ + QCowIncarnateArgs *args =3D (QCowIncarnateArgs *) opaque; + int64_t ret =3D 0; + int64_t offset; + uint64_t filter_size; + uint64_t incarnation_size; + uint8_t *filter; + + BlockDriverState *bs =3D args->bs; + BDRVQcowState *s =3D bs->opaque; + QCowStore *store =3D args->store; + QCowLogStore *frozen_log_store =3D &store->frozen_log_store; + QCowHashStore *hash_store =3D &store->hash_store; + + /* avoid disk allocations while the guest is suspended or migrating = */ + co_sleep_ns(vm_clock, 1); + + /* allocate a new incarnation disk space or get it from limbo */ + offset =3D qcow2_hash_store_get_incarnation_offset(bs, + hash_store); + + if (offset < 0) { + /* save the errno so the rest of QCOW2 will know */ + s->freeze_errno =3D offset; + return; + } + + /* save new config */ + ret =3D qcow2_store_save(bs, store); + + if (ret < 0) { + goto deallocate_exit; + } + + /* build the in ram filter and dump it to disk */ + ret =3D qcow2_log_store_freeze_filter(bs, + frozen_log_store, + offset, + &filter); + + if (ret < 0) { + goto deallocate_exit; + } + + /* reorder the journal contained hashes and write them to disk */ + filter_size =3D qcow2_hash_store_filter_size(hash_store->order); + ret =3D qcow2_log_store_freeze_hash_table(bs, + frozen_log_store, + offset + filter_size); + + if (ret < 0) { + goto free_exit; + } + + /* delegate the new frozen hash table to the hash store */ + qcow2_hash_store_add_incarnation(bs, + hash_store, + filter, + offset); + + /* reset the log store and clear journal */ + ret =3D qcow2_log_store_recycle(bs, &store->frozen_log_store); + + if (ret < 0) { + goto exit; + } + + /* we finished to incarnate the log -> remember it */ + store->freezing =3D false; + /* avoid modifying the freezing flag while the guest is suspended or + * migrating + */ + co_sleep_ns(vm_clock, 1); + /* save new config */ + ret =3D qcow2_store_save(bs, store); + if (ret < 0) { + goto free_exit; + } + + free(args); + return; + +free_exit: + qemu_vfree(filter); +deallocate_exit: + incarnation_size =3D qcow2_hash_store_incarnation_size(hash_store->o= rder); + /* postpone deallocation if guest is suspended */ + co_sleep_ns(vm_clock, 1); + /* FIXME: why freeing this does match well with limbo */ + qcow2_free_clusters(bs, offset, incarnation_size); +exit: + /* save the errno so the rest of QCOW2 will know */ + s->freeze_errno =3D ret; + free(args); +} + +/* This function start a coroutine convertion a log store into and incar= nation + * + * As the function return void the coroutine store error status in + * s->freeze_errno + * + * @store: the store to work on + */ +static void qcow2_store_incarnate(BlockDriverState *bs, + QCowStore *store) +{ + BDRVQcowState *s =3D bs->opaque; + + QCowIncarnateArgs *args =3D g_new0(QCowIncarnateArgs, 1); + args->bs =3D bs; + args->store =3D store; + + s->freeze_co =3D qemu_coroutine_create(qcow2_store_co_incarnate); + qemu_coroutine_enter(s->freeze_co, args); +} + +/* This function freeze the current log store and create a new one + * + * This swap the two log store + * + * @store: the QCowStore we work with + */ +static int qcow2_store_swap_log_store(BlockDriverState *bs, QCowStore *s= tore) +{ + QCowStore swap; + int ret =3D 0; + + /* flush and stop the journal */ + ret =3D qcow2_log_store_stop(bs, &store->log_store); + + if (ret < 0) { + return ret; + } + + /* backup frozen log store */ + memcpy(&swap, + &store->frozen_log_store, + sizeof(QCowLogStore)); + + /* freeze current log store */ + memcpy(&store->frozen_log_store, + &store->log_store, + sizeof(QCowLogStore)); + + /* finish the swap */ + memcpy(&store->log_store, + &swap, + sizeof(QCowLogStore)); + + /* no parallel freeze can occur */ + assert(!store->freezing); + store->freezing =3D true; + + return 0; +} + +/* this function start the freeze of the current log store into and inca= rnation + * + * @store: the QCowStore to work on + * @hash_info: the hash_info to insert into the new log store + *=C2=A0@ret: 0 on succes, -errno on error + */ +static int qcow2_store_start_freeze(BlockDriverState *bs, + QCowStore *store, + QCowHashInfo *hash_info) +{ + int ret =3D 0; + bool need_save =3D false; + + /* if we are already are freezing a log store wait for the operation= to end + * before starting a new one + */ + while (store->freezing) { + co_sleep_ns(rt_clock, QCOW2_STORE_CO_SLEEP_NS); + } + + /* log store is full -> prepare the log store freeze and create new = one */ + ret =3D qcow2_store_swap_log_store(bs, store); + + if (ret < 0) { + return ret; + } + + /* save new configuration */ + ret =3D qcow2_store_save(bs, store); + + /* on error */ + if (ret < 0) { + return ret; + } + + /* delegate the insert to the new log store */ + ret =3D qcow2_log_store_insert(bs, &store->log_store, hash_info, &ne= ed_save); + + /* on error */ + if (ret < 0) { + return ret; + } + + /* ret =3D=3D 1 case is not tested because the log store is new and = can't + * possibly be full right now + */ + + /* start to incarnate the previous log store into a hash store + * This is the responsibility of the coroutine to get rid of the fro= zen log + * store and save the new configuration to disk. + */ + store->freezing =3D true; + qcow2_store_incarnate(bs, store); + + return 0; +} + +/* This function insert a QCowHashInfo into the store + * + * @store: The QCowStore to work with + * @hash_info: the QCowHashInfo to insert into the sore + * @ret: 0 on success, -errno on error + */ +int qcow2_store_insert(BlockDriverState *bs, QCowStore *store, + QCowHashInfo *hash_info) +{ + int ret =3D 0; + bool need_save =3D false; + + /* we take the insert_lock here to prevent concurents inserts to tri= gger + * multiple concurent log store freeze if the log store is full + */ + qemu_co_mutex_lock(&store->insert_lock); + + /* delegate the insert to the log store */ + ret =3D qcow2_log_store_insert(bs, &store->log_store, hash_info, &ne= ed_save); + + /* on error */ + if (ret < 0) { + goto unlock_exit; + } + + /* need_save is true only if case of success -> no need to check ret= */ + if (need_save) { + ret =3D qcow2_store_save(bs, store); + } + + /* on success */ + if (!ret) { + goto unlock_exit; + } + + /* ret =3D=3D 1 -> log store is full -> freeze in into an incarnatio= n */ + ret =3D qcow2_store_start_freeze(bs, store, hash_info); + +unlock_exit: + qemu_co_mutex_unlock(&store->insert_lock); + return ret; +} + +/* This delegate a flush to the log store + * + * @store: The QCowStore to work with + * @ret: 0 on success, -errno on error + */ +int qcow2_store_flush(BlockDriverState *bs, QCowStore *store) +{ + return qcow2_log_store_flush(bs, &store->log_store); +} + +/* This function delete a QCowHashInfo from the store + * + * @store: The QCowStore to work with + * @hash_info: the QCowHashInfo to insert into the sore + * @ret: 0 on success, -errno on error + */ +int qcow2_store_delete(BlockDriverState *bs, QCowStore *store, + QCowHashInfo *hash_info) +{ + hash_info->first_logical_sect |=3D QCOW_DEDUP_DELETED; + return qcow2_store_insert(bs, store, hash_info); +} + +/* this function compute the size of a dump of a store + * + * @store: the store to work on + * @ret: the size of a dump + */ +static size_t qcow2_store_dump_size(QCowStore *store) +{ + return sizeof(store->order) + 1 + + qcow2_log_store_dump_size() * 2 + /* current + frozen log sto= re */ + qcow2_hash_store_dump_size(&store->hash_store); +} + +/* this function dump a store in a buffer + * + * @buf: the buffer to dump into + * @store: the store to dump + * @ret: the size used for the dump + */ +static size_t qcow2_store_dump(uint8_t *buf, QCowStore *store) +{ + uint32_t *buf32 =3D (uint32_t *) buf; + size_t offset; + + /* dump order */ + buf32[0] =3D cpu_to_be32(store->order); + offset =3D sizeof(store->order); + + /* dump freeze status */ + buf[offset] =3D store->freezing; + offset++; + + /* dump current log store */ + offset +=3D qcow2_log_store_dump(buf + offset, + &store->log_store); + + + /* dump frozen log store */ + offset +=3D qcow2_log_store_dump(buf + offset, + &store->frozen_log_store); + + /* dump hash store */ + offset +=3D qcow2_hash_store_dump(buf + offset, + &store->hash_store); + + return offset; +} + +/* this function parse a buffer in a given store + * + * note: As the parse method is used only on startup it also init the st= ore + * + * @store: the store to parse into + * @buf: the buffer to parse from + * @size: the size of the buffer + * @ret: 0 on success, -1 when buffer is too small to parse all data f= rom + */ +static int qcow2_store_parse(BlockDriverState *bs, + QCowStore *store, + uint8_t *buf, + uint32_t size) +{ + size_t offset; + uint32_t *buf32 =3D (uint32_t *) buf; + uint32_t order; + int ret; + + /* parse order and init store */ + order =3D be32_to_cpu(buf32[0]); + qcow2_store_init(bs, store, order); + offset =3D sizeof(store->order); + + /* parse freeze status */ + store->freezing =3D buf[offset]; + offset++; + + /* parse current log store */ + offset +=3D qcow2_log_store_parse(&store->log_store, buf + offset); + + /* parse frozen log store i its not NULL */ + offset +=3D qcow2_log_store_parse(&store->frozen_log_store, buf + of= fset); + + /* parse hash store */ + ret =3D qcow2_hash_store_parse(&store->hash_store, + buf + offset, + buf + size); + + /* buffer was too small -> cleanup and return error */ + if (ret =3D=3D -1) { + /* free various buffers */ + qcow2_store_cleanup(store); + return -1; + } + + return 0; +} + +static int32_t qcow2_store_compute_order(BlockDriverState *bs, + uint64_t total_size) +{ + BDRVQcowState *s =3D bs->opaque; + uint64_t nb_clusters =3D total_size / s->cluster_size; + uint64_t nb_hash_per_incarnation =3D nb_clusters / QCOW2_NB_INCARNAT= ION_GOAL; + return qcow2_log_store_compute_order(nb_hash_per_incarnation); +} + +static int64_t qcow2_store_alloc_disk_conf(BlockDriverState *bs, + QCowStore *store, + size_t *psize) +{ + /* compute configuration space dump size * 2 with room for growth */ + *psize =3D qcow2_store_dump_size(store) * 2; + + /* allocate the store configuration space */ + return qcow2_alloc_clusters(bs, *psize); +} + +/* this function save a QCowStore to disk and update header if needeed + * + * The function handle the need to grow the on disk dump area + * The function does rewrite the QCOW2 header extension if needed. + * + * @store: the store to save + * @ret: 0 on success, -errno on error + */ +int64_t qcow2_store_save(BlockDriverState *bs, QCowStore *store) +{ + BDRVQcowState *s =3D bs->opaque; + int64_t offset, old_offset =3D 0; + size_t size, old_size =3D 0; + bool alloc =3D false; + uint8_t *buf; + int ret =3D 0; + + /* we use these temporary variable to commit atomically */ + offset =3D s->dedup_conf_offset; + size =3D s->dedup_conf_size; + + /* check if we need to grow on disk dump area */ + if (size < qcow2_store_dump_size(store)) { + alloc =3D true; + old_offset =3D offset; + old_size =3D size; + offset =3D qcow2_store_alloc_disk_conf(bs, store, &size); + + if (offset < 0) { + return offset; + } + } + + /* allocate buffer */ + buf =3D qemu_blockalign(bs, size); + memset(buf, 0, size); + + /* serialize store config in buffer */ + ret =3D qcow2_store_dump(buf, store); + + if (ret < 0) { + goto free_dealloc_exit; + } + + /* write the conf to disk */ + ret =3D bdrv_pwrite(bs->file, offset, buf, size); + + /* no error happened and we grew the config disk space -> save in he= ader */ + if (alloc && ret > 0) { + s->dedup_conf_offset =3D offset; + s->dedup_conf_size =3D size; + qcow2_update_header(bs); + /* do not free old disk space on creation */ + if (old_size) { + qcow2_free_clusters(bs, old_offset, old_size); + } + } + ret =3D 0; + +free_dealloc_exit: + /* on alloc and error free newly allocated disk space */ + if (alloc && ret < 0) { + qcow2_free_clusters(bs, offset, size); + } + qemu_vfree(buf); + return ret; +} + +/* This function will allocate a journal, init the store and save the co= nfig + * + * this function will be used at QCOW2 image creation time + * This code don't bother cleaning up failure since error would make ima= ge + * creation fail. It just free memory. + * + * note: s->cluster_size must be filled with correct value for + * qcow2_store_compute_order + * + * @store: the store to create + * @total_size: the total size of the image + * @ret: 0 on success, -errno on error + */ +int64_t qcow2_store_create(BlockDriverState *bs, + QCowStore *store, + uint64_t total_size) +{ + BDRVQcowState *s =3D bs->opaque; + int ret =3D 0; + + /* compute the bit order required to store the journal and hash tabl= es */ + uint32_t order =3D qcow2_store_compute_order(bs, total_size); + + /* initialize the store state */ + qcow2_store_init(bs, store, order); + + /* create the journal */ + ret =3D qcow2_log_store_create(bs, &store->log_store, order); + + if (ret < 0) { + return ret; + } + + /* create frozen log store we will swap at runtime */ + ret =3D qcow2_log_store_create(bs, &store->frozen_log_store, order); + + if (ret < 0) { + return ret; + } + + /* set size to zero so the save function will start allocating + * and will not try to free anything + */ + s->dedup_conf_size =3D 0; + + /* save new config */ + return qcow2_store_save(bs, store); +} + + +/* this function initialize a store and load it's configuration from dis= k + * + * note: the journal and the incarnations filters are not loaded from di= sk + * this work is to be done later. + * + * @store: the store to load the configuration into + * @ret: 0 on success, -errno on error, -1 if the on disk dump was in= valid + */ +int qcow2_store_load(BlockDriverState *bs, QCowStore *store) +{ + BDRVQcowState *s =3D bs->opaque; + int ret =3D 0; + + /* allocate read buffer */ + uint8_t *buf =3D qemu_blockalign(bs, s->dedup_conf_size); + + /* read conf from disk */ + ret =3D bdrv_pread(bs->file, + s->dedup_conf_offset, + buf, + s->dedup_conf_size); + + if (ret < 0) { + goto free_exit; + } + + /* parse the buffer into a store */ + ret =3D qcow2_store_parse(bs, store, buf, s->dedup_conf_size); +free_exit: + qemu_vfree(buf); + + return ret; +} + +/* this function load the in ram store data at startup + * + * It loads the current journal synchronously so after this the deduplic= ation + * will be operational. + * + * @store: the store to work on + * @ret: 0 on succes, -errno on error + */ +int qcow2_store_start(BlockDriverState *bs, QCowStore *store) +{ + int ret =3D 0; + + /* reload current journal */ + ret =3D qcow2_log_store_rebuild_from_journal(bs, &store->log_store); + + if (ret < 0) { + return ret; + } + + /* resume freeze in progress */ + if(store->freezing) { + ret =3D qcow2_log_store_rebuild_from_journal(bs, + &store->frozen_log_st= ore); + + if (ret < 0) { + return ret; + } + + /* do the incarnation in a coroutine */ + qcow2_store_incarnate(bs, store); + } + + /* load incarnations in ram filters in a coroutine */ + qcow2_hash_store_load_filters(bs, &store->hash_store); + + return 0; +} + +int qcow2_store_forget(BlockDriverState *bs, QCowStore *store) +{ + int ret =3D 0; + store->freezing =3D false; + + /* reset hash store */ + qcow2_hash_store_forget_all_incarnations(&store->hash_store); + + /* reset log store */ + ret =3D qcow2_log_store_create(bs, &store->log_store, store->order)= ; + + if (ret < 0) { + return ret; + } + + /* reset frozen log store */ + ret =3D qcow2_log_store_create(bs, &store->frozen_log_store, store-= >order); + + if (ret < 0) { + return ret; + } + + /* save config to disk */ + return qcow2_store_save(bs, store); +} diff --git a/block/qcow2.h b/block/qcow2.h index d0037bf..c0dbfe3 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -226,9 +226,9 @@ typedef struct { =20 typedef struct { uint32_t order; + bool freezing; /* is a log store freeze in progres= s */ QCowLogStore log_store; /* the current log store */ QCowLogStore frozen_log_store; /* the log store to incarnate */ - bool freezing; /* are we incarnating a log store *= / QCowHashStore hash_store; CoMutex insert_lock; /* used to prevent multiple freeze = attempts * at the same time @@ -699,4 +699,22 @@ size_t qcow2_hash_store_parse(QCowHashStore *store, uint8_t *buf, uint8_t *buf_end); =20 +/* qcow2-store.c functions (Should be called by dedup.c) */ + +void qcow2_store_cleanup(QCowStore *store); +int qcow2_store_get(BlockDriverState *bs, QCowStore *store, + QCowHashInfo *hash_info); +int qcow2_store_insert(BlockDriverState *bs, QCowStore *store, + QCowHashInfo *hash_info); +int qcow2_store_flush(BlockDriverState *bs, QCowStore *store); +int qcow2_store_delete(BlockDriverState *bs, QCowStore *store, + QCowHashInfo *hash_info); +int64_t qcow2_store_save(BlockDriverState *bs, QCowStore *store); +int64_t qcow2_store_create(BlockDriverState *bs, + QCowStore *store, + uint64_t total_size); +int qcow2_store_load(BlockDriverState *bs, QCowStore *store); +int qcow2_store_start(BlockDriverState *bs, QCowStore *store); +int qcow2_store_forget(BlockDriverState *bs, QCowStore *store); + #endif --=20 1.7.10.4