2dcbb11b39
Allow a client to request a subset of negotiated meta contexts. For example, a client may ask to use a single connection to learn about both block status and dirty bitmaps, but where the dirty bitmap queries only need to be performed on a subset of the disk; forcing the server to compute that information on block status queries in the rest of the disk is wasted effort (both at the server, and on the amount of traffic sent over the wire to be parsed and ignored by the client). Qemu as an NBD client never requests to use more than one meta context, so it has no need to use block status payloads. Testing this instead requires support from libnbd, which CAN access multiple meta contexts in parallel from a single NBD connection; an interop test submitted to the libnbd project at the same time as this patch demonstrates the feature working, as well as testing some corner cases (for example, when the payload length is longer than the export length), although other corner cases (like passing the same id duplicated) requires a protocol fuzzer because libnbd is not wired up to break the protocol that badly. This also includes tweaks to 'qemu-nbd --list' to show when a server is advertising the capability, and to the testsuite to reflect the addition to that output. Of note: qemu will always advertise the new feature bit during NBD_OPT_INFO if extended headers have alreay been negotiated (regardless of whether any NBD_OPT_SET_META_CONTEXT negotiation has occurred); but for NBD_OPT_GO, qemu only advertises the feature if block status is also enabled (that is, if the client does not negotiate any contexts, then NBD_CMD_BLOCK_STATUS cannot be used, so the feature is not advertised). Signed-off-by: Eric Blake <eblake@redhat.com> Message-ID: <20230925192229.3186470-26-eblake@redhat.com> [eblake: fix logic to reject unnegotiated contexts] Signed-off-by: Eric Blake <eblake@redhat.com>
299 lines
13 KiB
Plaintext
299 lines
13 KiB
Plaintext
QA output created by 223
|
|
|
|
=== Create partially sparse image, then add dirty bitmaps ===
|
|
|
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304
|
|
wrote 2097152/2097152 bytes at offset 1048576
|
|
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
|
Testing:
|
|
QMP_VERSION
|
|
{"return": {}}
|
|
{"return": {}}
|
|
{"return": {}}
|
|
{"return": {}}
|
|
{"return": {}}
|
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
|
|
|
|
|
|
=== Write part of the file under active bitmap ===
|
|
|
|
wrote 512/512 bytes at offset 512
|
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
|
wrote 2097152/2097152 bytes at offset 2097152
|
|
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
|
|
|
=== End dirty bitmaps, and start serving image over NBD ===
|
|
|
|
{"execute":"qmp_capabilities"}
|
|
{"return": {}}
|
|
{"execute":"blockdev-add",
|
|
"arguments":{"driver":"IMGFMT", "node-name":"n",
|
|
"file":{"driver":"file", "filename":"TEST_DIR/t.IMGFMT"}}}
|
|
{"return": {}}
|
|
{"execute":"block-dirty-bitmap-disable",
|
|
"arguments":{"node":"n", "name":"b"}}
|
|
{"return": {}}
|
|
{"execute":"blockdev-add",
|
|
"arguments":{"driver":"null-co", "node-name":"null",
|
|
"size": 4194304}}
|
|
{"return": {}}
|
|
{"execute":"block-dirty-bitmap-add",
|
|
"arguments":{"node":"null", "name":"b3"}}
|
|
{"return": {}}
|
|
|
|
=== Set up NBD with normal access ===
|
|
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"n"}}
|
|
{"error": {"class": "GenericError", "desc": "NBD server not running"}}
|
|
{"execute":"nbd-server-start",
|
|
"arguments":{"addr":{"type":"unix",
|
|
"data":{"path":"SOCK_DIR/nbd"}}}}
|
|
{"return": {}}
|
|
{"execute":"nbd-server-start",
|
|
"arguments":{"addr":{"type":"unix",
|
|
"data":{"path":"SOCK_DIR/nbd1"}}}}
|
|
{"error": {"class": "GenericError", "desc": "NBD server already running"}}
|
|
exports available: 0
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"n", "bitmap":"b"}}
|
|
{"return": {}}
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"nosuch"}}
|
|
{"error": {"class": "GenericError", "desc": "Cannot find device='nosuch' nor node-name='nosuch'"}}
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"n"}}
|
|
{"error": {"class": "GenericError", "desc": "Block export id 'n' is already in use"}}
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"n", "name":"n2",
|
|
"bitmap":"b2"}}
|
|
{"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}}
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"n", "name":"n2",
|
|
"bitmap":"b3"}}
|
|
{"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}}
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"n", "name":"n2", "writable":true,
|
|
"description":"some text", "bitmap":"b2"}}
|
|
{"return": {}}
|
|
{"execute":"block-export-add",
|
|
"arguments":{"type": "nbd", "node-name":"n", "id":"n3", "name": "n3",
|
|
"bitmaps":[{"node":"null","name":"b3"}]}}
|
|
{"return": {}}
|
|
exports available: 3
|
|
export: 'n'
|
|
size: 4194304
|
|
flags: 0x158f ( readonly flush fua df multi cache block-status-payload )
|
|
min block: 1
|
|
opt block: 4096
|
|
max block: 33554432
|
|
transaction size: 64-bit
|
|
available meta contexts: 2
|
|
base:allocation
|
|
qemu:dirty-bitmap:b
|
|
export: 'n2'
|
|
description: some text
|
|
size: 4194304
|
|
flags: 0x1ded ( flush fua trim zeroes df multi cache fast-zero block-status-payload )
|
|
min block: 1
|
|
opt block: 4096
|
|
max block: 33554432
|
|
transaction size: 64-bit
|
|
available meta contexts: 2
|
|
base:allocation
|
|
qemu:dirty-bitmap:b2
|
|
export: 'n3'
|
|
size: 4194304
|
|
flags: 0x158f ( readonly flush fua df multi cache block-status-payload )
|
|
min block: 1
|
|
opt block: 4096
|
|
max block: 33554432
|
|
transaction size: 64-bit
|
|
available meta contexts: 2
|
|
base:allocation
|
|
qemu:dirty-bitmap:b3
|
|
|
|
=== Contrast normal status to large granularity dirty-bitmap ===
|
|
|
|
read 512/512 bytes at offset 512
|
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
|
read 524288/524288 bytes at offset 524288
|
|
512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
|
read 1048576/1048576 bytes at offset 1048576
|
|
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
|
read 2097152/2097152 bytes at offset 2097152
|
|
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
|
[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET},
|
|
{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET},
|
|
{ "start": 1048576, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}]
|
|
[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false},
|
|
{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET},
|
|
{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}]
|
|
|
|
=== Contrast to small granularity dirty-bitmap ===
|
|
|
|
[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET},
|
|
{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false},
|
|
{ "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET},
|
|
{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}]
|
|
|
|
=== Check bitmap taken from another node ===
|
|
|
|
[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}]
|
|
|
|
=== End qemu NBD server ===
|
|
|
|
{"execute":"nbd-server-remove",
|
|
"arguments":{"name":"n"}}
|
|
{"return": {}}
|
|
{"execute":"nbd-server-remove",
|
|
"arguments":{"name":"n2"}}
|
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}}
|
|
{"return": {}}
|
|
{"execute":"nbd-server-remove",
|
|
"arguments":{"name":"n2"}}
|
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}}
|
|
{"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}}
|
|
{"execute":"nbd-server-stop"}
|
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}}
|
|
{"return": {}}
|
|
{"execute":"nbd-server-stop"}
|
|
{"error": {"class": "GenericError", "desc": "NBD server not running"}}
|
|
|
|
=== Set up NBD with iothread access ===
|
|
|
|
{"execute":"x-blockdev-set-iothread",
|
|
"arguments":{"node-name":"n", "iothread":"io0"}}
|
|
{"return": {}}
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"n"}}
|
|
{"error": {"class": "GenericError", "desc": "NBD server not running"}}
|
|
{"execute":"nbd-server-start",
|
|
"arguments":{"addr":{"type":"unix",
|
|
"data":{"path":"SOCK_DIR/nbd"}}}}
|
|
{"return": {}}
|
|
{"execute":"nbd-server-start",
|
|
"arguments":{"addr":{"type":"unix",
|
|
"data":{"path":"SOCK_DIR/nbd1"}}}}
|
|
{"error": {"class": "GenericError", "desc": "NBD server already running"}}
|
|
exports available: 0
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"n", "bitmap":"b"}}
|
|
{"return": {}}
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"nosuch"}}
|
|
{"error": {"class": "GenericError", "desc": "Cannot find device='nosuch' nor node-name='nosuch'"}}
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"n"}}
|
|
{"error": {"class": "GenericError", "desc": "Block export id 'n' is already in use"}}
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"n", "name":"n2",
|
|
"bitmap":"b2"}}
|
|
{"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}}
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"n", "name":"n2",
|
|
"bitmap":"b3"}}
|
|
{"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}}
|
|
{"execute":"nbd-server-add",
|
|
"arguments":{"device":"n", "name":"n2", "writable":true,
|
|
"description":"some text", "bitmap":"b2"}}
|
|
{"return": {}}
|
|
{"execute":"block-export-add",
|
|
"arguments":{"type": "nbd", "node-name":"n", "id":"n3", "name": "n3",
|
|
"bitmaps":[{"node":"null","name":"b3"}]}}
|
|
{"return": {}}
|
|
exports available: 3
|
|
export: 'n'
|
|
size: 4194304
|
|
flags: 0x158f ( readonly flush fua df multi cache block-status-payload )
|
|
min block: 1
|
|
opt block: 4096
|
|
max block: 33554432
|
|
transaction size: 64-bit
|
|
available meta contexts: 2
|
|
base:allocation
|
|
qemu:dirty-bitmap:b
|
|
export: 'n2'
|
|
description: some text
|
|
size: 4194304
|
|
flags: 0x1ded ( flush fua trim zeroes df multi cache fast-zero block-status-payload )
|
|
min block: 1
|
|
opt block: 4096
|
|
max block: 33554432
|
|
transaction size: 64-bit
|
|
available meta contexts: 2
|
|
base:allocation
|
|
qemu:dirty-bitmap:b2
|
|
export: 'n3'
|
|
size: 4194304
|
|
flags: 0x158f ( readonly flush fua df multi cache block-status-payload )
|
|
min block: 1
|
|
opt block: 4096
|
|
max block: 33554432
|
|
transaction size: 64-bit
|
|
available meta contexts: 2
|
|
base:allocation
|
|
qemu:dirty-bitmap:b3
|
|
|
|
=== Contrast normal status to large granularity dirty-bitmap ===
|
|
|
|
read 512/512 bytes at offset 512
|
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
|
read 524288/524288 bytes at offset 524288
|
|
512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
|
read 1048576/1048576 bytes at offset 1048576
|
|
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
|
read 2097152/2097152 bytes at offset 2097152
|
|
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
|
[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET},
|
|
{ "start": 4096, "length": 1044480, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false, "offset": OFFSET},
|
|
{ "start": 1048576, "length": 3145728, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}]
|
|
[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false},
|
|
{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET},
|
|
{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}]
|
|
|
|
=== Contrast to small granularity dirty-bitmap ===
|
|
|
|
[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET},
|
|
{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false},
|
|
{ "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET},
|
|
{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}]
|
|
|
|
=== Check bitmap taken from another node ===
|
|
|
|
[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}]
|
|
|
|
=== End qemu NBD server ===
|
|
|
|
{"execute":"nbd-server-remove",
|
|
"arguments":{"name":"n"}}
|
|
{"return": {}}
|
|
{"execute":"nbd-server-remove",
|
|
"arguments":{"name":"n2"}}
|
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}}
|
|
{"return": {}}
|
|
{"execute":"nbd-server-remove",
|
|
"arguments":{"name":"n2"}}
|
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}}
|
|
{"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}}
|
|
{"execute":"nbd-server-stop"}
|
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}}
|
|
{"return": {}}
|
|
{"execute":"nbd-server-stop"}
|
|
{"error": {"class": "GenericError", "desc": "NBD server not running"}}
|
|
{"execute":"quit"}
|
|
{"return": {}}
|
|
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
|
|
|
|
=== Use qemu-nbd as server ===
|
|
|
|
[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false},
|
|
{ "start": 65536, "length": 2031616, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET},
|
|
{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}]
|
|
[{ "start": 0, "length": 512, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET},
|
|
{ "start": 512, "length": 512, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false},
|
|
{ "start": 1024, "length": 11321, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}]
|
|
[{ "start": 12345, "length": 2084807, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET},
|
|
{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false, "compressed": false}]
|
|
*** done
|