#!/usr/bin/env python3 # # Test bitmap merges. # # Copyright (c) 2018 John Snow for Red Hat, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # owner=jsnow@redhat.com import iotests from iotests import log iotests.verify_image_format(supported_fmts=['generic']) size = 64 * 1024 * 1024 granularity = 64 * 1024 patterns = [("0x5d", "0", "64k"), ("0xd5", "1M", "64k"), ("0xdc", "32M", "64k"), ("0xcd", "0x3ff0000", "64k")] # 64M - 64K overwrite = [("0xab", "0", "64k"), # Full overwrite ("0xad", "0x00f8000", "64k"), # Partial-left (1M-32K) ("0x1d", "0x2008000", "64k"), # Partial-right (32M+32K) ("0xea", "0x3fe0000", "64k")] # Adjacent-left (64M - 128K) def query_bitmaps(vm): res = vm.qmp("query-block") return { "bitmaps": { device['device']: device.get('dirty-bitmaps', []) for device in res['return'] } } with iotests.FilePath('img') as img_path, \ iotests.VM() as vm: log('--- Preparing image & VM ---\n') iotests.qemu_img_create('-f', iotests.imgfmt, img_path, str(size)) vm.add_drive(img_path) vm.launch() log('\n--- Adding preliminary bitmaps A & B ---\n') vm.qmp_log("block-dirty-bitmap-add", node="drive0", name="bitmapA", granularity=granularity) vm.qmp_log("block-dirty-bitmap-add", node="drive0", name="bitmapB", granularity=granularity) # Dirties 4 clusters. count=262144 log('\n--- Emulating writes ---\n') for p in patterns: cmd = "write -P%s %s %s" % p log(cmd) log(vm.hmp_qemu_io("drive0", cmd)) log(query_bitmaps(vm), indent=2) log('\n--- Submitting & Aborting Transaction ---\n') vm.qmp_log("transaction", indent=2, actions=[ { "type": "block-dirty-bitmap-disable", "data": { "node": "drive0", "name": "bitmapB" }}, { "type": "block-dirty-bitmap-add", "data": { "node": "drive0", "name": "bitmapC", "granularity": granularity }}, { "type": "block-dirty-bitmap-clear", "data": { "node": "drive0", "name": "bitmapA" }}, { "type": "abort", "data": {}} ]) log(query_bitmaps(vm), indent=2) log('\n--- Disabling B & Adding C ---\n') vm.qmp_log("transaction", indent=2, actions=[ { "type": "block-dirty-bitmap-disable", "data": { "node": "drive0", "name": "bitmapB" }}, { "type": "block-dirty-bitmap-add", "data": { "node": "drive0", "name": "bitmapC", "granularity": granularity }}, # Purely extraneous, but test that it works: { "type": "block-dirty-bitmap-disable", "data": { "node": "drive0", "name": "bitmapC" }}, { "type": "block-dirty-bitmap-enable", "data": { "node": "drive0", "name": "bitmapC" }}, ]) log('\n--- Emulating further writes ---\n') # Dirties 6 clusters, 3 of which are new in contrast to "A". # A = 64 * 1024 * (4 + 3) = 458752 # C = 64 * 1024 * 6 = 393216 for p in overwrite: cmd = "write -P%s %s %s" % p log(cmd) log(vm.hmp_qemu_io("drive0", cmd)) log('\n--- Disabling A & C ---\n') vm.qmp_log("transaction", indent=2, actions=[ { "type": "block-dirty-bitmap-disable", "data": { "node": "drive0", "name": "bitmapA" }}, { "type": "block-dirty-bitmap-disable", "data": { "node": "drive0", "name": "bitmapC" }} ]) # A: 7 clusters # B: 4 clusters # C: 6 clusters log(query_bitmaps(vm), indent=2) log('\n--- Submitting & Aborting Merge Transaction ---\n') vm.qmp_log("transaction", indent=2, actions=[ { "type": "block-dirty-bitmap-add", "data": { "node": "drive0", "name": "bitmapD", "disabled": True, "granularity": granularity }}, { "type": "block-dirty-bitmap-merge", "data": { "node": "drive0", "target": "bitmapD", "bitmaps": ["bitmapB", "bitmapC"] }}, { "type": "abort", "data": {}} ]) log(query_bitmaps(vm), indent=2) log('\n--- Creating D as a merge of B & C ---\n') # Good hygiene: create a disabled bitmap as a merge target. vm.qmp_log("transaction", indent=2, actions=[ { "type": "block-dirty-bitmap-add", "data": { "node": "drive0", "name": "bitmapD", "disabled": True, "granularity": granularity }}, { "type": "block-dirty-bitmap-merge", "data": { "node": "drive0", "target": "bitmapD", "bitmaps": ["bitmapB", "bitmapC"] }} ]) # A and D should now both have 7 clusters apiece. # B and C remain unchanged with 4 and 6 respectively. log(query_bitmaps(vm), indent=2) # A and D should be equivalent. # Some formats round the size of the disk, so don't print the checksums. check_a = vm.qmp('x-debug-block-dirty-bitmap-sha256', node="drive0", name="bitmapA")['return']['sha256'] check_d = vm.qmp('x-debug-block-dirty-bitmap-sha256', node="drive0", name="bitmapD")['return']['sha256'] assert(check_a == check_d) log('\n--- Removing bitmaps A, B, C, and D ---\n') vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapA") vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapB") vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapC") vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapD") log('\n--- Final Query ---\n') log(query_bitmaps(vm), indent=2) log('\n--- Done ---\n') vm.shutdown()