1#!/usr/bin/env python3 2# 3# Test bitmap merges. 4# 5# Copyright (c) 2018 John Snow for Red Hat, Inc. 6# 7# This program is free software; you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 2 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program. If not, see <http://www.gnu.org/licenses/>. 19# 20# owner=jsnow@redhat.com 21 22import iotests 23from iotests import log 24 25iotests.verify_image_format(supported_fmts=['generic']) 26size = 64 * 1024 * 1024 27granularity = 64 * 1024 28 29patterns = [("0x5d", "0", "64k"), 30 ("0xd5", "1M", "64k"), 31 ("0xdc", "32M", "64k"), 32 ("0xcd", "0x3ff0000", "64k")] # 64M - 64K 33 34overwrite = [("0xab", "0", "64k"), # Full overwrite 35 ("0xad", "0x00f8000", "64k"), # Partial-left (1M-32K) 36 ("0x1d", "0x2008000", "64k"), # Partial-right (32M+32K) 37 ("0xea", "0x3fe0000", "64k")] # Adjacent-left (64M - 128K) 38 39def query_bitmaps(vm): 40 res = vm.qmp("query-block") 41 return { "bitmaps": { device['device']: device.get('dirty-bitmaps', []) for 42 device in res['return'] } } 43 44with iotests.FilePath('img') as img_path, \ 45 iotests.VM() as vm: 46 47 log('--- Preparing image & VM ---\n') 48 iotests.qemu_img_create('-f', iotests.imgfmt, img_path, str(size)) 49 vm.add_drive(img_path) 50 vm.launch() 51 52 log('\n--- Adding preliminary bitmaps A & B ---\n') 53 vm.qmp_log("block-dirty-bitmap-add", node="drive0", 54 name="bitmapA", granularity=granularity) 55 vm.qmp_log("block-dirty-bitmap-add", node="drive0", 56 name="bitmapB", granularity=granularity) 57 58 # Dirties 4 clusters. count=262144 59 log('\n--- Emulating writes ---\n') 60 for p in patterns: 61 cmd = "write -P%s %s %s" % p 62 log(cmd) 63 log(vm.hmp_qemu_io("drive0", cmd)) 64 65 log(query_bitmaps(vm), indent=2) 66 67 log('\n--- Submitting & Aborting Transaction ---\n') 68 vm.qmp_log("transaction", indent=2, actions=[ 69 { "type": "block-dirty-bitmap-disable", 70 "data": { "node": "drive0", "name": "bitmapB" }}, 71 { "type": "block-dirty-bitmap-add", 72 "data": { "node": "drive0", "name": "bitmapC", 73 "granularity": granularity }}, 74 { "type": "block-dirty-bitmap-clear", 75 "data": { "node": "drive0", "name": "bitmapA" }}, 76 { "type": "abort", "data": {}} 77 ]) 78 log(query_bitmaps(vm), indent=2) 79 80 log('\n--- Disabling B & Adding C ---\n') 81 vm.qmp_log("transaction", indent=2, actions=[ 82 { "type": "block-dirty-bitmap-disable", 83 "data": { "node": "drive0", "name": "bitmapB" }}, 84 { "type": "block-dirty-bitmap-add", 85 "data": { "node": "drive0", "name": "bitmapC", 86 "granularity": granularity }}, 87 # Purely extraneous, but test that it works: 88 { "type": "block-dirty-bitmap-disable", 89 "data": { "node": "drive0", "name": "bitmapC" }}, 90 { "type": "block-dirty-bitmap-enable", 91 "data": { "node": "drive0", "name": "bitmapC" }}, 92 ]) 93 94 log('\n--- Emulating further writes ---\n') 95 # Dirties 6 clusters, 3 of which are new in contrast to "A". 96 # A = 64 * 1024 * (4 + 3) = 458752 97 # C = 64 * 1024 * 6 = 393216 98 for p in overwrite: 99 cmd = "write -P%s %s %s" % p 100 log(cmd) 101 log(vm.hmp_qemu_io("drive0", cmd)) 102 103 log('\n--- Disabling A & C ---\n') 104 vm.qmp_log("transaction", indent=2, actions=[ 105 { "type": "block-dirty-bitmap-disable", 106 "data": { "node": "drive0", "name": "bitmapA" }}, 107 { "type": "block-dirty-bitmap-disable", 108 "data": { "node": "drive0", "name": "bitmapC" }} 109 ]) 110 111 # A: 7 clusters 112 # B: 4 clusters 113 # C: 6 clusters 114 log(query_bitmaps(vm), indent=2) 115 116 log('\n--- Submitting & Aborting Merge Transaction ---\n') 117 vm.qmp_log("transaction", indent=2, actions=[ 118 { "type": "block-dirty-bitmap-add", 119 "data": { "node": "drive0", "name": "bitmapD", 120 "disabled": True, "granularity": granularity }}, 121 { "type": "block-dirty-bitmap-merge", 122 "data": { "node": "drive0", "target": "bitmapD", 123 "bitmaps": ["bitmapB", "bitmapC"] }}, 124 { "type": "abort", "data": {}} 125 ]) 126 log(query_bitmaps(vm), indent=2) 127 128 log('\n--- Creating D as a merge of B & C ---\n') 129 # Good hygiene: create a disabled bitmap as a merge target. 130 vm.qmp_log("transaction", indent=2, actions=[ 131 { "type": "block-dirty-bitmap-add", 132 "data": { "node": "drive0", "name": "bitmapD", 133 "disabled": True, "granularity": granularity }}, 134 { "type": "block-dirty-bitmap-merge", 135 "data": { "node": "drive0", "target": "bitmapD", 136 "bitmaps": ["bitmapB", "bitmapC"] }} 137 ]) 138 139 # A and D should now both have 7 clusters apiece. 140 # B and C remain unchanged with 4 and 6 respectively. 141 log(query_bitmaps(vm), indent=2) 142 143 # A and D should be equivalent. 144 # Some formats round the size of the disk, so don't print the checksums. 145 check_a = vm.qmp('x-debug-block-dirty-bitmap-sha256', 146 node="drive0", name="bitmapA")['return']['sha256'] 147 check_d = vm.qmp('x-debug-block-dirty-bitmap-sha256', 148 node="drive0", name="bitmapD")['return']['sha256'] 149 assert(check_a == check_d) 150 151 log('\n--- Removing bitmaps A, B, C, and D ---\n') 152 vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapA") 153 vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapB") 154 vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapC") 155 vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapD") 156 157 log('\n--- Final Query ---\n') 158 log(query_bitmaps(vm), indent=2) 159 160 log('\n--- Done ---\n') 161 vm.shutdown() 162