diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2019-03-13 17:30:34 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2019-03-13 17:30:34 +0000 |
commit | 523a2a42c3abd65b503610b2a18cd7fc74c6c61e (patch) | |
tree | c342640cd6bdfa783df6f6435021422c2d099551 /block | |
parent | 36fe7709664579c934f014f21e5610d69e5b298f (diff) | |
parent | e2ec4119dc57e9d65e419b2e9071d35300aa1d92 (diff) |
Merge remote-tracking branch 'remotes/jnsnow/tags/bitmaps-pull-request' into staging
Pull request
# gpg: Signature made Tue 12 Mar 2019 20:23:08 GMT
# gpg: using RSA key F9B7ABDBBCACDF95BE76CBD07DEF8106AAFC390E
# gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" [full]
# Primary key fingerprint: FAEB 9711 A12C F475 812F 18F2 88A9 064D 1835 61EB
# Subkey fingerprint: F9B7 ABDB BCAC DF95 BE76 CBD0 7DEF 8106 AAFC 390E
* remotes/jnsnow/tags/bitmaps-pull-request: (22 commits)
tests/qemu-iotests: add bitmap resize test 246
block/qcow2-bitmap: Allow resizes with persistent bitmaps
block/qcow2-bitmap: Don't check size for IN_USE bitmap
docs/interop/qcow2: Improve bitmap flag in_use specification
bitmaps: Fix typo in function name
block/dirty-bitmaps: implement inconsistent bit
block/dirty-bitmaps: disallow busy bitmaps as merge source
block/dirty-bitmaps: prohibit removing readonly bitmaps
block/dirty-bitmaps: prohibit readonly bitmaps for backups
block/dirty-bitmaps: add block_dirty_bitmap_check function
block/dirty-bitmap: add inconsistent status
block/dirty-bitmaps: add inconsistent bit
iotests: add busy/recording bit test to 124
blockdev: remove unused paio parameter documentation
block/dirty-bitmaps: move comment block
block/dirty-bitmaps: unify qmp_locked and user_locked calls
block/dirty-bitmap: explicitly lock bitmaps with successors
nbd: change error checking order for bitmaps
block/dirty-bitmap: change semantics of enabled predicate
block/dirty-bitmap: remove set/reset assertions against enabled bit
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
# Conflicts:
# tests/qemu-iotests/group
Diffstat (limited to 'block')
-rw-r--r-- | block/dirty-bitmap.c | 174 | ||||
-rw-r--r-- | block/qcow2-bitmap.c | 170 | ||||
-rw-r--r-- | block/qcow2.c | 4 | ||||
-rw-r--r-- | block/qcow2.h | 1 |
4 files changed, 236 insertions, 113 deletions
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index c6d4acebfa..59e6ebb861 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -28,29 +28,12 @@ #include "block/block_int.h" #include "block/blockjob.h" -/** - * A BdrvDirtyBitmap can be in four possible user-visible states: - * (1) Active: successor is NULL, and disabled is false: full r/w mode - * (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode, - * guest writes are dropped, but monitor writes are possible, - * through commands like merge and clear. - * (3) Frozen: successor is not NULL. - * A frozen bitmap cannot be renamed, deleted, cleared, set, - * enabled, merged to, etc. A frozen bitmap can only abdicate() - * or reclaim(). - * In this state, the anonymous successor bitmap may be either - * Active and recording writes from the guest (e.g. backup jobs), - * but it can be Disabled and not recording writes. - * (4) Locked: Whether Active or Disabled, the user cannot modify this bitmap - * in any way from the monitor. - */ struct BdrvDirtyBitmap { QemuMutex *mutex; HBitmap *bitmap; /* Dirty bitmap implementation */ HBitmap *meta; /* Meta dirty bitmap */ - bool qmp_locked; /* Bitmap is locked, it can't be modified - through QMP */ - BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ + bool busy; /* Bitmap is busy, it can't be used via QMP */ + BdrvDirtyBitmap *successor; /* Anonymous child, if any. */ char *name; /* Optional non-empty unique ID */ int64_t size; /* Size of the bitmap, in bytes */ bool disabled; /* Bitmap is disabled. It ignores all writes to @@ -63,6 +46,9 @@ struct BdrvDirtyBitmap { and this bitmap must remain unchanged while this flag is set. */ bool persistent; /* bitmap must be saved to owner disk image */ + bool inconsistent; /* bitmap is persistent, but inconsistent. + It cannot be used at all in any way, except + a QMP user can remove it. */ bool migration; /* Bitmap is selected for migration, it should not be stored on the next inactivation (persistent flag doesn't matter until next @@ -183,41 +169,58 @@ const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap) } /* Called with BQL taken. */ -bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) +bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap) { return bitmap->successor; } -/* Both conditions disallow user-modification via QMP. */ -bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) { - return bdrv_dirty_bitmap_frozen(bitmap) || - bdrv_dirty_bitmap_qmp_locked(bitmap); +static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap) +{ + return bitmap->busy; } -void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked) +void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy) { qemu_mutex_lock(bitmap->mutex); - bitmap->qmp_locked = qmp_locked; + bitmap->busy = busy; qemu_mutex_unlock(bitmap->mutex); } -bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap) -{ - return bitmap->qmp_locked; -} - /* Called with BQL taken. */ bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) { - return !(bitmap->disabled || bitmap->successor); + return !bitmap->disabled; } -/* Called with BQL taken. */ +/** + * bdrv_dirty_bitmap_status: This API is now deprecated. + * Called with BQL taken. + * + * A BdrvDirtyBitmap can be in four possible user-visible states: + * (1) Active: successor is NULL, and disabled is false: full r/w mode + * (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode, + * guest writes are dropped, but monitor writes are possible, + * through commands like merge and clear. + * (3) Frozen: successor is not NULL. + * A frozen bitmap cannot be renamed, deleted, cleared, set, + * enabled, merged to, etc. A frozen bitmap can only abdicate() + * or reclaim(). + * In this state, the anonymous successor bitmap may be either + * Active and recording writes from the guest (e.g. backup jobs), + * or it can be Disabled and not recording writes. + * (4) Locked: Whether Active or Disabled, the user cannot modify this bitmap + * in any way from the monitor. + * (5) Inconsistent: This is a persistent bitmap whose "in use" bit is set, and + * is unusable by QEMU. It can be deleted to remove it from + * the qcow2. + */ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) { - if (bdrv_dirty_bitmap_frozen(bitmap)) { + if (bdrv_dirty_bitmap_inconsistent(bitmap)) { + return DIRTY_BITMAP_STATUS_INCONSISTENT; + } else if (bdrv_dirty_bitmap_has_successor(bitmap)) { return DIRTY_BITMAP_STATUS_FROZEN; - } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { + } else if (bdrv_dirty_bitmap_busy(bitmap)) { return DIRTY_BITMAP_STATUS_LOCKED; } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { return DIRTY_BITMAP_STATUS_DISABLED; @@ -226,9 +229,44 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) } } +/* Called with BQL taken. */ +static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap) +{ + return !bitmap->disabled || (bitmap->successor && + !bitmap->successor->disabled); +} + +int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags, + Error **errp) +{ + if ((flags & BDRV_BITMAP_BUSY) && bdrv_dirty_bitmap_busy(bitmap)) { + error_setg(errp, "Bitmap '%s' is currently in use by another" + " operation and cannot be used", bitmap->name); + return -1; + } + + if ((flags & BDRV_BITMAP_RO) && bdrv_dirty_bitmap_readonly(bitmap)) { + error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", + bitmap->name); + return -1; + } + + if ((flags & BDRV_BITMAP_INCONSISTENT) && + bdrv_dirty_bitmap_inconsistent(bitmap)) { + error_setg(errp, "Bitmap '%s' is inconsistent and cannot be used", + bitmap->name); + error_append_hint(errp, "Try block-dirty-bitmap-remove to delete" + " this bitmap from disk"); + return -1; + } + + return 0; +} + /** * Create a successor bitmap destined to replace this bitmap after an operation. - * Requires that the bitmap is not frozen and has no successor. + * Requires that the bitmap is not marked busy and has no successor. + * The successor will be enabled if the parent bitmap was. * Called with BQL taken. */ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, @@ -237,12 +275,14 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, uint64_t granularity; BdrvDirtyBitmap *child; - if (bdrv_dirty_bitmap_frozen(bitmap)) { - error_setg(errp, "Cannot create a successor for a bitmap that is " - "currently frozen"); + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { + return -1; + } + if (bdrv_dirty_bitmap_has_successor(bitmap)) { + error_setg(errp, "Cannot create a successor for a bitmap that already " + "has one"); return -1; } - assert(!bitmap->successor); /* Create an anonymous successor */ granularity = bdrv_dirty_bitmap_granularity(bitmap); @@ -253,15 +293,16 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, /* Successor will be on or off based on our current state. */ child->disabled = bitmap->disabled; + bitmap->disabled = true; - /* Install the successor and freeze the parent */ + /* Install the successor and mark the parent as busy */ bitmap->successor = child; + bitmap->busy = true; return 0; } void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) { - assert(!bdrv_dirty_bitmap_frozen(bitmap)); bitmap->disabled = false; } @@ -278,7 +319,8 @@ void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) { assert(!bitmap->active_iterators); - assert(!bdrv_dirty_bitmap_frozen(bitmap)); + assert(!bdrv_dirty_bitmap_busy(bitmap)); + assert(!bdrv_dirty_bitmap_has_successor(bitmap)); assert(!bitmap->meta); QLIST_REMOVE(bitmap, list); hbitmap_free(bitmap->bitmap); @@ -310,6 +352,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, bitmap->successor = NULL; successor->persistent = bitmap->persistent; bitmap->persistent = false; + bitmap->busy = false; bdrv_release_dirty_bitmap(bs, bitmap); return successor; @@ -318,7 +361,8 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, /** * In cases of failure where we can no longer safely delete the parent, * we may wish to re-join the parent and child/successor. - * The merged parent will be un-frozen, but not explicitly re-enabled. + * The merged parent will be marked as not busy. + * The marged parent will be enabled if and only if the successor was enabled. * Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, @@ -336,6 +380,9 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, error_setg(errp, "Merging of parent and successor bitmap failed"); return NULL; } + + parent->disabled = successor->disabled; + parent->busy = false; bdrv_release_dirty_bitmap_locked(successor); parent->successor = NULL; @@ -366,7 +413,8 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) bdrv_dirty_bitmaps_lock(bs); QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { - assert(!bdrv_dirty_bitmap_frozen(bitmap)); + assert(!bdrv_dirty_bitmap_busy(bitmap)); + assert(!bdrv_dirty_bitmap_has_successor(bitmap)); assert(!bitmap->active_iterators); hbitmap_truncate(bitmap->bitmap, bytes); bitmap->size = bytes; @@ -384,7 +432,7 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) /** * Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()). - * There must not be any frozen bitmaps attached. + * There must not be any busy bitmaps attached. * This function does not remove persistent bitmaps from the storage. * Called with BQL taken. */ @@ -421,7 +469,6 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) { bdrv_dirty_bitmap_lock(bitmap); - assert(!bdrv_dirty_bitmap_frozen(bitmap)); bitmap->disabled = true; bdrv_dirty_bitmap_unlock(bitmap); } @@ -448,7 +495,11 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) info->has_name = !!bm->name; info->name = g_strdup(bm->name); info->status = bdrv_dirty_bitmap_status(bm); + info->recording = bdrv_dirty_bitmap_recording(bm); + info->busy = bdrv_dirty_bitmap_busy(bm); info->persistent = bm->persistent; + info->has_inconsistent = bm->inconsistent; + info->inconsistent = bm->inconsistent; entry->value = info; *plist = entry; plist = &entry->next; @@ -531,7 +582,6 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, int64_t offset, int64_t bytes) { - assert(bdrv_dirty_bitmap_enabled(bitmap)); assert(!bdrv_dirty_bitmap_readonly(bitmap)); hbitmap_set(bitmap->bitmap, offset, bytes); } @@ -548,7 +598,6 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, int64_t offset, int64_t bytes) { - assert(bdrv_dirty_bitmap_enabled(bitmap)); assert(!bdrv_dirty_bitmap_readonly(bitmap)); hbitmap_reset(bitmap->bitmap, offset, bytes); } @@ -691,7 +740,7 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs) } /* Called with BQL taken. */ -void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) +void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent) { qemu_mutex_lock(bitmap->mutex); bitmap->persistent = persistent; @@ -699,6 +748,16 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) } /* Called with BQL taken. */ +void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap) +{ + qemu_mutex_lock(bitmap->mutex); + assert(bitmap->persistent == true); + bitmap->inconsistent = true; + bitmap->disabled = true; + qemu_mutex_unlock(bitmap->mutex); +} + +/* Called with BQL taken. */ void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration) { qemu_mutex_lock(bitmap->mutex); @@ -706,11 +765,16 @@ void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration) qemu_mutex_unlock(bitmap->mutex); } -bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap) +bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap) { return bitmap->persistent && !bitmap->migration; } +bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap) +{ + return bitmap->inconsistent; +} + bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs) { BdrvDirtyBitmap *bm; @@ -757,15 +821,11 @@ void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, qemu_mutex_lock(dest->mutex); - if (bdrv_dirty_bitmap_user_locked(dest)) { - error_setg(errp, "Bitmap '%s' is currently in use by another" - " operation and cannot be modified", dest->name); + if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) { goto out; } - if (bdrv_dirty_bitmap_readonly(dest)) { - error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", - dest->name); + if (bdrv_dirty_bitmap_check(src, BDRV_BITMAP_ALLOW_RO, errp)) { goto out; } diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 9d968bdcda..e53a1609d7 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -343,11 +343,17 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs, uint32_t granularity; BdrvDirtyBitmap *bitmap = NULL; - if (bm->flags & BME_FLAG_IN_USE) { - error_setg(errp, "Bitmap '%s' is in use", bm->name); + granularity = 1U << bm->granularity_bits; + bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp); + if (bitmap == NULL) { goto fail; } + if (bm->flags & BME_FLAG_IN_USE) { + /* Data is unusable, skip loading it */ + return bitmap; + } + ret = bitmap_table_load(bs, &bm->table, &bitmap_table); if (ret < 0) { error_setg_errno(errp, -ret, @@ -356,12 +362,6 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs, goto fail; } - granularity = 1U << bm->granularity_bits; - bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp); - if (bitmap == NULL) { - goto fail; - } - ret = load_bitmap_data(bs, bitmap_table, bm->table.size, bitmap); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read bitmap '%s' from image", @@ -462,10 +462,25 @@ static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry) return len; } - fail = (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) || - (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits)); + if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) { + return -EINVAL; + } + + if (!(entry->flags & BME_FLAG_IN_USE) && + (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits))) + { + /* + * We've loaded a valid bitmap (IN_USE not set) or we are going to + * store a valid bitmap, but the allocated bitmap table size is not + * enough to store this bitmap. + * + * Note, that it's OK to have an invalid bitmap with invalid size due + * to a bitmap that was not correctly saved after image resize. + */ + return -EINVAL; + } - return fail ? -EINVAL : 0; + return 0; } static inline void bitmap_directory_to_be(uint8_t *dir, size_t size) @@ -950,6 +965,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) Qcow2Bitmap *bm; GSList *created_dirty_bitmaps = NULL; bool header_updated = false; + bool needs_update = false; if (s->nb_bitmaps == 0) { /* No bitmaps - nothing to do */ @@ -963,35 +979,39 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) } QSIMPLEQ_FOREACH(bm, bm_list, entry) { - if (!(bm->flags & BME_FLAG_IN_USE)) { - BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp); - if (bitmap == NULL) { - goto fail; - } + BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp); + if (bitmap == NULL) { + goto fail; + } - if (!(bm->flags & BME_FLAG_AUTO)) { - bdrv_disable_dirty_bitmap(bitmap); - } - bdrv_dirty_bitmap_set_persistance(bitmap, true); + bdrv_dirty_bitmap_set_persistence(bitmap, true); + if (bm->flags & BME_FLAG_IN_USE) { + bdrv_dirty_bitmap_set_inconsistent(bitmap); + } else { + /* NB: updated flags only get written if can_write(bs) is true. */ bm->flags |= BME_FLAG_IN_USE; - created_dirty_bitmaps = - g_slist_append(created_dirty_bitmaps, bitmap); + needs_update = true; + } + if (!(bm->flags & BME_FLAG_AUTO)) { + bdrv_disable_dirty_bitmap(bitmap); } + created_dirty_bitmaps = + g_slist_append(created_dirty_bitmaps, bitmap); } - if (created_dirty_bitmaps != NULL) { - if (can_write(bs)) { - /* in_use flags must be updated */ - int ret = update_ext_header_and_dir_in_place(bs, bm_list); - if (ret < 0) { - error_setg_errno(errp, -ret, "Can't update bitmap directory"); - goto fail; - } - header_updated = true; - } else { - g_slist_foreach(created_dirty_bitmaps, set_readonly_helper, - (gpointer)true); + if (needs_update && can_write(bs)) { + /* in_use flags must be updated */ + int ret = update_ext_header_and_dir_in_place(bs, bm_list); + if (ret < 0) { + error_setg_errno(errp, -ret, "Can't update bitmap directory"); + goto fail; } + header_updated = true; + } + + if (!can_write(bs)) { + g_slist_foreach(created_dirty_bitmaps, set_readonly_helper, + (gpointer)true); } g_slist_free(created_dirty_bitmaps); @@ -1113,23 +1133,21 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, } QSIMPLEQ_FOREACH(bm, bm_list, entry) { - if (!(bm->flags & BME_FLAG_IN_USE)) { - BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); - if (bitmap == NULL) { - continue; - } - - if (!bdrv_dirty_bitmap_readonly(bitmap)) { - error_setg(errp, "Bitmap %s is not readonly but not marked" - "'IN_USE' in the image. Something went wrong," - "all the bitmaps may be corrupted", bm->name); - ret = -EINVAL; - goto out; - } + BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); + if (bitmap == NULL) { + continue; + } - bm->flags |= BME_FLAG_IN_USE; - ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap); + if (!bdrv_dirty_bitmap_readonly(bitmap)) { + error_setg(errp, "Bitmap %s was loaded prior to rw-reopen, but was " + "not marked as readonly. This is a bug, something went " + "wrong. All of the bitmaps may be corrupted", bm->name); + ret = -EINVAL; + goto out; } + + bm->flags |= BME_FLAG_IN_USE; + ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap); } if (ro_dirty_bitmaps != NULL) { @@ -1157,6 +1175,52 @@ int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) return qcow2_reopen_bitmaps_rw_hint(bs, NULL, errp); } +/* Checks to see if it's safe to resize bitmaps */ +int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp) +{ + BDRVQcow2State *s = bs->opaque; + Qcow2BitmapList *bm_list; + Qcow2Bitmap *bm; + int ret = 0; + + if (s->nb_bitmaps == 0) { + return 0; + } + + bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, + s->bitmap_directory_size, errp); + if (bm_list == NULL) { + return -EINVAL; + } + + QSIMPLEQ_FOREACH(bm, bm_list, entry) { + BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); + if (bitmap == NULL) { + /* + * We rely on all bitmaps being in-memory to be able to resize them, + * Otherwise, we'd need to resize them on disk explicitly + */ + error_setg(errp, "Cannot resize qcow2 with persistent bitmaps that " + "were not loaded into memory"); + ret = -ENOTSUP; + goto out; + } + + /* + * The checks against readonly and busy are redundant, but certainly + * do no harm. checks against inconsistent are crucial: + */ + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { + ret = -ENOTSUP; + goto out; + } + } + +out: + bitmap_list_free(bm_list); + return ret; +} + /* store_bitmap_data() * Store bitmap to image, filling bitmap table accordingly. */ @@ -1424,9 +1488,9 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap); Qcow2Bitmap *bm; - if (!bdrv_dirty_bitmap_get_persistance(bitmap) || - bdrv_dirty_bitmap_readonly(bitmap)) - { + if (!bdrv_dirty_bitmap_get_persistence(bitmap) || + bdrv_dirty_bitmap_readonly(bitmap) || + bdrv_dirty_bitmap_inconsistent(bitmap)) { continue; } @@ -1542,7 +1606,7 @@ int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp) for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap != NULL; bitmap = bdrv_dirty_bitmap_next(bs, bitmap)) { - if (bdrv_dirty_bitmap_get_persistance(bitmap)) { + if (bdrv_dirty_bitmap_get_persistence(bitmap)) { bdrv_dirty_bitmap_set_readonly(bitmap, true); } } diff --git a/block/qcow2.c b/block/qcow2.c index 0fc9b0561e..0dd77c6367 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -3670,9 +3670,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, } /* cannot proceed if image has bitmaps */ - if (s->nb_bitmaps) { - /* TODO: resize bitmaps in the image */ - error_setg(errp, "Can't resize an image which has bitmaps"); + if (qcow2_truncate_bitmaps_check(bs, errp)) { ret = -ENOTSUP; goto fail; } diff --git a/block/qcow2.h b/block/qcow2.h index de2a3bdfc5..fdee297f33 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -723,6 +723,7 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, Error **errp); int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); +int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp); int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs, |