| /* | |
| * QEMU block dirty bitmap QMP commands | |
| * | |
| * Copyright (c) 2003-2008 Fabrice Bellard | |
| * | |
| * This work is licensed under the terms of the GNU GPL, version 2 or | |
| * later. See the COPYING file in the top-level directory. | |
| * | |
| * This file incorporates work covered by the following copyright and | |
| * permission notice: | |
| * | |
| * Copyright (c) 2003-2008 Fabrice Bellard | |
| * | |
| * 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 included in | |
| * all copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| * THE SOFTWARE. | |
| */ | |
| #include "qemu/osdep.h" | |
| #include "block/block-io.h" | |
| #include "block/block_int.h" | |
| #include "block/dirty-bitmap.h" | |
| #include "qapi/qapi-commands-block.h" | |
| #include "qapi/error.h" | |
| /** | |
| * block_dirty_bitmap_lookup: | |
| * Return a dirty bitmap (if present), after validating | |
| * the node reference and bitmap names. | |
| * | |
| * @node: The name of the BDS node to search for bitmaps | |
| * @name: The name of the bitmap to search for | |
| * @pbs: Output pointer for BDS lookup, if desired. Can be NULL. | |
| * @errp: Output pointer for error information. Can be NULL. | |
| * | |
| * @return: A bitmap object on success, or NULL on failure. | |
| */ | |
| BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, | |
| const char *name, | |
| BlockDriverState **pbs, | |
| Error **errp) | |
| { | |
| BlockDriverState *bs; | |
| BdrvDirtyBitmap *bitmap; | |
| GLOBAL_STATE_CODE(); | |
| if (!node) { | |
| error_setg(errp, "Node cannot be NULL"); | |
| return NULL; | |
| } | |
| if (!name) { | |
| error_setg(errp, "Bitmap name cannot be NULL"); | |
| return NULL; | |
| } | |
| bs = bdrv_lookup_bs(node, node, NULL); | |
| if (!bs) { | |
| error_setg(errp, "Node '%s' not found", node); | |
| return NULL; | |
| } | |
| bitmap = bdrv_find_dirty_bitmap(bs, name); | |
| if (!bitmap) { | |
| error_setg(errp, "Dirty bitmap '%s' not found", name); | |
| return NULL; | |
| } | |
| if (pbs) { | |
| *pbs = bs; | |
| } | |
| return bitmap; | |
| } | |
| void qmp_block_dirty_bitmap_add(const char *node, const char *name, | |
| bool has_granularity, uint32_t granularity, | |
| bool has_persistent, bool persistent, | |
| bool has_disabled, bool disabled, | |
| Error **errp) | |
| { | |
| BlockDriverState *bs; | |
| BdrvDirtyBitmap *bitmap; | |
| if (!name || name[0] == '\0') { | |
| error_setg(errp, "Bitmap name cannot be empty"); | |
| return; | |
| } | |
| bs = bdrv_lookup_bs(node, node, errp); | |
| if (!bs) { | |
| return; | |
| } | |
| if (has_granularity) { | |
| if (granularity < 512 || !is_power_of_2(granularity)) { | |
| error_setg(errp, "Granularity must be power of 2 " | |
| "and at least 512"); | |
| return; | |
| } | |
| } else { | |
| /* Default to cluster size, if available: */ | |
| granularity = bdrv_get_default_bitmap_granularity(bs); | |
| } | |
| if (!has_persistent) { | |
| persistent = false; | |
| } | |
| if (!has_disabled) { | |
| disabled = false; | |
| } | |
| if (persistent) { | |
| if (!bdrv_is_writable(bs)) { | |
| error_setg(errp, "Cannot add a persistent bitmap to " | |
| "read-only or inactive node '%s'", | |
| bdrv_get_node_name(bs)); | |
| return; | |
| } | |
| if (!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) { | |
| return; | |
| } | |
| } | |
| bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp); | |
| if (bitmap == NULL) { | |
| return; | |
| } | |
| if (disabled) { | |
| bdrv_disable_dirty_bitmap(bitmap); | |
| } | |
| bdrv_dirty_bitmap_set_persistence(bitmap, persistent); | |
| } | |
| BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, | |
| bool release, | |
| BlockDriverState **bitmap_bs, | |
| Error **errp) | |
| { | |
| BlockDriverState *bs; | |
| BdrvDirtyBitmap *bitmap; | |
| GLOBAL_STATE_CODE(); | |
| bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); | |
| if (!bitmap || !bs) { | |
| return NULL; | |
| } | |
| if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { | |
| return NULL; | |
| } | |
| /* Dropping a bitmap needs no write access unless it is actually stored. */ | |
| if (bdrv_dirty_bitmap_get_persistence(bitmap) && | |
| bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0) | |
| { | |
| return NULL; | |
| } | |
| if (release) { | |
| bdrv_release_dirty_bitmap(bitmap); | |
| } | |
| if (bitmap_bs) { | |
| *bitmap_bs = bs; | |
| } | |
| return release ? NULL : bitmap; | |
| } | |
| void qmp_block_dirty_bitmap_remove(const char *node, const char *name, | |
| Error **errp) | |
| { | |
| block_dirty_bitmap_remove(node, name, true, NULL, errp); | |
| } | |
| /** | |
| * Completely clear a bitmap, for the purposes of synchronizing a bitmap | |
| * immediately after a full backup operation. | |
| */ | |
| void qmp_block_dirty_bitmap_clear(const char *node, const char *name, | |
| Error **errp) | |
| { | |
| BdrvDirtyBitmap *bitmap; | |
| BlockDriverState *bs; | |
| bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); | |
| if (!bitmap || !bs) { | |
| return; | |
| } | |
| if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { | |
| return; | |
| } | |
| bdrv_clear_dirty_bitmap(bitmap, NULL); | |
| } | |
| void qmp_block_dirty_bitmap_enable(const char *node, const char *name, | |
| Error **errp) | |
| { | |
| BlockDriverState *bs; | |
| BdrvDirtyBitmap *bitmap; | |
| bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); | |
| if (!bitmap) { | |
| return; | |
| } | |
| if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { | |
| return; | |
| } | |
| bdrv_enable_dirty_bitmap(bitmap); | |
| } | |
| void qmp_block_dirty_bitmap_disable(const char *node, const char *name, | |
| Error **errp) | |
| { | |
| BlockDriverState *bs; | |
| BdrvDirtyBitmap *bitmap; | |
| bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); | |
| if (!bitmap) { | |
| return; | |
| } | |
| if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { | |
| return; | |
| } | |
| bdrv_disable_dirty_bitmap(bitmap); | |
| } | |
| BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *dst_node, | |
| const char *dst_bitmap, | |
| BlockDirtyBitmapOrStrList *bms, | |
| HBitmap **backup, Error **errp) | |
| { | |
| BlockDriverState *bs; | |
| BdrvDirtyBitmap *dst, *src; | |
| BlockDirtyBitmapOrStrList *lst; | |
| const char *src_node, *src_bitmap; | |
| HBitmap *local_backup = NULL; | |
| GLOBAL_STATE_CODE(); | |
| dst = block_dirty_bitmap_lookup(dst_node, dst_bitmap, &bs, errp); | |
| if (!dst) { | |
| return NULL; | |
| } | |
| for (lst = bms; lst; lst = lst->next) { | |
| switch (lst->value->type) { | |
| case QTYPE_QSTRING: | |
| src_bitmap = lst->value->u.local; | |
| src = bdrv_find_dirty_bitmap(bs, src_bitmap); | |
| if (!src) { | |
| error_setg(errp, "Dirty bitmap '%s' not found", src_bitmap); | |
| goto fail; | |
| } | |
| break; | |
| case QTYPE_QDICT: | |
| src_node = lst->value->u.external.node; | |
| src_bitmap = lst->value->u.external.name; | |
| src = block_dirty_bitmap_lookup(src_node, src_bitmap, NULL, errp); | |
| if (!src) { | |
| goto fail; | |
| } | |
| break; | |
| default: | |
| abort(); | |
| } | |
| /* We do backup only for first merge operation */ | |
| if (!bdrv_merge_dirty_bitmap(dst, src, | |
| local_backup ? NULL : &local_backup, | |
| errp)) | |
| { | |
| goto fail; | |
| } | |
| } | |
| if (backup) { | |
| *backup = local_backup; | |
| } else { | |
| hbitmap_free(local_backup); | |
| } | |
| return dst; | |
| fail: | |
| if (local_backup) { | |
| bdrv_restore_dirty_bitmap(dst, local_backup); | |
| } | |
| return NULL; | |
| } | |
| void qmp_block_dirty_bitmap_merge(const char *node, const char *target, | |
| BlockDirtyBitmapOrStrList *bitmaps, | |
| Error **errp) | |
| { | |
| block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp); | |
| } |