1 | The following changes since commit af352675efb7e92a1f5f6461a042a12015ab3d12: | 1 | The following changes since commit 7b1db0908d88f0c9cfac24e214ff72a860692e23: |
---|---|---|---|
2 | 2 | ||
3 | Merge remote-tracking branch 'remotes/aurel/tags/pull-target-sh4-20171218' into staging (2017-12-19 19:11:11 +0000) | 3 | Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180323' into staging (2018-03-25 13:51:33 +0100) |
4 | 4 | ||
5 | are available in the git repository at: | 5 | are available in the git repository at: |
6 | 6 | ||
7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream | 7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream |
8 | 8 | ||
9 | for you to fetch changes up to b4d526c87b26aff6d8b353951aa175a1236ad887: | 9 | for you to fetch changes up to 0b7e7f66813a7e346e12d47be977a32a530a6316: |
10 | 10 | ||
11 | nvme: Add tracing (2017-12-20 11:05:49 +0100) | 11 | qemu-iotests: Test vhdx image creation with QMP (2018-03-26 12:17:43 +0200) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Block layer patches | 14 | Block layer patches |
15 | 15 | ||
16 | ---------------------------------------------------------------- | 16 | ---------------------------------------------------------------- |
17 | Doug Gale (1): | 17 | Alberto Garcia (1): |
18 | nvme: Add tracing | 18 | qcow2: Reset free_cluster_index when allocating a new refcount block |
19 | 19 | ||
20 | Edgar Kaziakhmedov (1): | 20 | Eric Blake (1): |
21 | qcow2: get rid of qcow2_backing_read1 routine | 21 | iotests: 163 is not quick |
22 | 22 | ||
23 | Fam Zheng (1): | 23 | Fabiano Rosas (5): |
24 | qemu-img: Document --force-share / -U | 24 | block/replication: Remove protocol_name field |
25 | block/quorum: Remove protocol-related fields | ||
26 | block/throttle: Remove protocol-related fields | ||
27 | block/blkreplay: Remove protocol-related fields | ||
28 | include/block/block_int: Document protocol related functions | ||
25 | 29 | ||
26 | John Snow (1): | 30 | Kevin Wolf (12): |
27 | iotests: fix 197 for vpc | 31 | vdi: Change 'static' create option to 'preallocation' in QMP |
32 | vdi: Fix build with CONFIG_VDI_DEBUG | ||
33 | qemu-iotests: Test vdi image creation with QMP | ||
34 | qemu-iotests: Enable 025 for luks | ||
35 | luks: Turn another invalid assertion into check | ||
36 | qemu-iotests: Test invalid resize on luks | ||
37 | parallels: Check maximum cluster size on create | ||
38 | qemu-iotests: Test parallels image creation with QMP | ||
39 | vhdx: Require power-of-two block size on create | ||
40 | vhdx: Don't use error_setg_errno() with constant errno | ||
41 | vhdx: Check for 4 GB maximum log size on creation | ||
42 | qemu-iotests: Test vhdx image creation with QMP | ||
28 | 43 | ||
29 | Kevin Wolf (9): | 44 | qapi/block-core.json | 7 +- |
30 | block: Formats don't need CONSISTENT_READ with NO_IO | 45 | include/block/block_int.h | 8 ++ |
31 | block: Make bdrv_drain_invoke() recursive | 46 | replication.h | 1 - |
32 | block: Call .drain_begin only once in bdrv_drain_all_begin() | 47 | block/blkreplay.c | 3 +- |
33 | test-bdrv-drain: Test BlockDriver callbacks for drain | 48 | block/crypto.c | 6 +- |
34 | block: bdrv_drain_recurse(): Remove unused begin parameter | 49 | block/parallels.c | 5 + |
35 | block: Don't wait for requests in bdrv_drain*_end() | 50 | block/qcow2-refcount.c | 7 + |
36 | block: Unify order in drain functions | 51 | block/quorum.c | 3 +- |
37 | block: Don't acquire AioContext in hmp_qemu_io() | 52 | block/replication.c | 1 - |
38 | block: Document that x-blockdev-change breaks quorum children list | 53 | block/throttle.c | 3 +- |
54 | block/vdi.c | 46 ++++-- | ||
55 | block/vhdx.c | 17 ++- | ||
56 | tests/qemu-iotests/025 | 9 +- | ||
57 | tests/qemu-iotests/026.out | 6 +- | ||
58 | tests/qemu-iotests/121 | 20 +++ | ||
59 | tests/qemu-iotests/121.out | 10 ++ | ||
60 | tests/qemu-iotests/210 | 37 +++++ | ||
61 | tests/qemu-iotests/210.out | 16 +++ | ||
62 | tests/qemu-iotests/211 | 246 ++++++++++++++++++++++++++++++++ | ||
63 | tests/qemu-iotests/211.out | 97 +++++++++++++ | ||
64 | tests/qemu-iotests/212 | 326 ++++++++++++++++++++++++++++++++++++++++++ | ||
65 | tests/qemu-iotests/212.out | 111 ++++++++++++++ | ||
66 | tests/qemu-iotests/213 | 349 +++++++++++++++++++++++++++++++++++++++++++++ | ||
67 | tests/qemu-iotests/213.out | 121 ++++++++++++++++ | ||
68 | tests/qemu-iotests/group | 5 +- | ||
69 | 25 files changed, 1423 insertions(+), 37 deletions(-) | ||
70 | create mode 100755 tests/qemu-iotests/211 | ||
71 | create mode 100644 tests/qemu-iotests/211.out | ||
72 | create mode 100755 tests/qemu-iotests/212 | ||
73 | create mode 100644 tests/qemu-iotests/212.out | ||
74 | create mode 100755 tests/qemu-iotests/213 | ||
75 | create mode 100644 tests/qemu-iotests/213.out | ||
39 | 76 | ||
40 | Mao Zhongyi (1): | ||
41 | hw/block/nvme: Convert to realize | ||
42 | |||
43 | qapi/block-core.json | 4 + | ||
44 | block/qcow2.h | 3 - | ||
45 | block.c | 6 +- | ||
46 | block/io.c | 31 ++-- | ||
47 | block/qcow2.c | 51 +----- | ||
48 | hmp.c | 6 - | ||
49 | hw/block/nvme.c | 367 ++++++++++++++++++++++++++++++++------- | ||
50 | tests/test-bdrv-drain.c | 137 +++++++++++++++ | ||
51 | hw/block/trace-events | 93 ++++++++++ | ||
52 | qemu-img.texi | 9 + | ||
53 | tests/Makefile.include | 2 + | ||
54 | tests/qemu-iotests/197 | 4 + | ||
55 | tests/qemu-iotests/common.filter | 3 +- | ||
56 | 13 files changed, 591 insertions(+), 125 deletions(-) | ||
57 | create mode 100644 tests/test-bdrv-drain.c | ||
58 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Eric Blake <eblake@redhat.com> | ||
1 | 2 | ||
3 | Testing on ext4, most 'quick' qcow2 tests took less than 5 seconds, | ||
4 | but 163 took more than 20. Let's remove it from the quick set. | ||
5 | |||
6 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | --- | ||
9 | tests/qemu-iotests/group | 2 +- | ||
10 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
11 | |||
12 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/tests/qemu-iotests/group | ||
15 | +++ b/tests/qemu-iotests/group | ||
16 | @@ -XXX,XX +XXX,XX @@ | ||
17 | 159 rw auto quick | ||
18 | 160 rw auto quick | ||
19 | 162 auto quick | ||
20 | -163 rw auto quick | ||
21 | +163 rw auto | ||
22 | 165 rw auto quick | ||
23 | 169 rw auto quick | ||
24 | 170 rw auto quick | ||
25 | -- | ||
26 | 2.13.6 | ||
27 | |||
28 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Fabiano Rosas <farosas@linux.vnet.ibm.com> | ||
1 | 2 | ||
3 | The protocol_name field is used when selecting a driver via protocol | ||
4 | syntax (i.e. <protocol_name>:<filename:options:...>). Drivers that are | ||
5 | only selected explicitly (e.g. driver=replication,mode=primary,...) | ||
6 | should not have a protocol_name. | ||
7 | |||
8 | This patch removes the protocol_name field from the brdv_replication | ||
9 | structure so that attempts to invoke this driver using protocol syntax | ||
10 | will fail gracefully: | ||
11 | |||
12 | $ qemu-img info replication:foo | ||
13 | qemu-img: Could not open 'replication:': Unknown protocol 'replication' | ||
14 | |||
15 | Buglink: https://bugs.launchpad.net/qemu/+bug/1726733 | ||
16 | Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com> | ||
17 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
18 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
19 | --- | ||
20 | replication.h | 1 - | ||
21 | block/replication.c | 1 - | ||
22 | 2 files changed, 2 deletions(-) | ||
23 | |||
24 | diff --git a/replication.h b/replication.h | ||
25 | index XXXXXXX..XXXXXXX 100644 | ||
26 | --- a/replication.h | ||
27 | +++ b/replication.h | ||
28 | @@ -XXX,XX +XXX,XX @@ typedef struct ReplicationState ReplicationState; | ||
29 | * | ||
30 | * BlockDriver bdrv_replication = { | ||
31 | * .format_name = "replication", | ||
32 | - * .protocol_name = "replication", | ||
33 | * .instance_size = sizeof(BDRVReplicationState), | ||
34 | * | ||
35 | * .bdrv_open = replication_open, | ||
36 | diff --git a/block/replication.c b/block/replication.c | ||
37 | index XXXXXXX..XXXXXXX 100644 | ||
38 | --- a/block/replication.c | ||
39 | +++ b/block/replication.c | ||
40 | @@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) | ||
41 | |||
42 | BlockDriver bdrv_replication = { | ||
43 | .format_name = "replication", | ||
44 | - .protocol_name = "replication", | ||
45 | .instance_size = sizeof(BDRVReplicationState), | ||
46 | |||
47 | .bdrv_open = replication_open, | ||
48 | -- | ||
49 | 2.13.6 | ||
50 | |||
51 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Fabiano Rosas <farosas@linux.vnet.ibm.com> | ||
1 | 2 | ||
3 | The quorum driver is not a protocol so it should implement bdrv_open | ||
4 | instead of bdrv_file_open and not provide a protocol_name. | ||
5 | |||
6 | Attempts to invoke this driver using protocol syntax | ||
7 | (i.e. quorum:<filename:options:...>) will now fail gracefully: | ||
8 | |||
9 | $ qemu-img info quorum:foo | ||
10 | qemu-img: Could not open 'quorum:foo': Unknown protocol 'quorum' | ||
11 | |||
12 | Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com> | ||
13 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
14 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
16 | --- | ||
17 | block/quorum.c | 3 +-- | ||
18 | 1 file changed, 1 insertion(+), 2 deletions(-) | ||
19 | |||
20 | diff --git a/block/quorum.c b/block/quorum.c | ||
21 | index XXXXXXX..XXXXXXX 100644 | ||
22 | --- a/block/quorum.c | ||
23 | +++ b/block/quorum.c | ||
24 | @@ -XXX,XX +XXX,XX @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) | ||
25 | |||
26 | static BlockDriver bdrv_quorum = { | ||
27 | .format_name = "quorum", | ||
28 | - .protocol_name = "quorum", | ||
29 | |||
30 | .instance_size = sizeof(BDRVQuorumState), | ||
31 | |||
32 | - .bdrv_file_open = quorum_open, | ||
33 | + .bdrv_open = quorum_open, | ||
34 | .bdrv_close = quorum_close, | ||
35 | .bdrv_refresh_filename = quorum_refresh_filename, | ||
36 | |||
37 | -- | ||
38 | 2.13.6 | ||
39 | |||
40 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Fabiano Rosas <farosas@linux.vnet.ibm.com> | ||
1 | 2 | ||
3 | The throttle driver is not a protocol so it should implement bdrv_open | ||
4 | instead of bdrv_file_open and not provide a protocol_name. | ||
5 | |||
6 | Attempts to invoke this driver using protocol syntax | ||
7 | (i.e. throttle:<filename:options:...>) will now fail gracefully: | ||
8 | |||
9 | $ qemu-img info throttle:foo | ||
10 | qemu-img: Could not open 'throttle:foo': Unknown protocol 'throttle' | ||
11 | |||
12 | Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com> | ||
13 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
14 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
16 | --- | ||
17 | block/throttle.c | 3 +-- | ||
18 | 1 file changed, 1 insertion(+), 2 deletions(-) | ||
19 | |||
20 | diff --git a/block/throttle.c b/block/throttle.c | ||
21 | index XXXXXXX..XXXXXXX 100644 | ||
22 | --- a/block/throttle.c | ||
23 | +++ b/block/throttle.c | ||
24 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs) | ||
25 | |||
26 | static BlockDriver bdrv_throttle = { | ||
27 | .format_name = "throttle", | ||
28 | - .protocol_name = "throttle", | ||
29 | .instance_size = sizeof(ThrottleGroupMember), | ||
30 | |||
31 | - .bdrv_file_open = throttle_open, | ||
32 | + .bdrv_open = throttle_open, | ||
33 | .bdrv_close = throttle_close, | ||
34 | .bdrv_co_flush = throttle_co_flush, | ||
35 | |||
36 | -- | ||
37 | 2.13.6 | ||
38 | |||
39 | diff view generated by jsdifflib |
1 | bdrv_drain_all_begin() used to call the .bdrv_co_drain_begin() driver | 1 | From: Fabiano Rosas <farosas@linux.vnet.ibm.com> |
---|---|---|---|
2 | callback inside its polling loop. This means that how many times it got | ||
3 | called for each node depended on long it had to poll the event loop. | ||
4 | 2 | ||
5 | This is obviously not right and results in nodes that stay drained even | 3 | The blkreplay driver is not a protocol so it should implement bdrv_open |
6 | after bdrv_drain_all_end(), which calls .bdrv_co_drain_begin() once per | 4 | instead of bdrv_file_open and not provide a protocol_name. |
7 | node. | ||
8 | 5 | ||
9 | Fix bdrv_drain_all_begin() to call the callback only once, too. | 6 | Attempts to invoke this driver using protocol syntax |
7 | (i.e. blkreplay:<filename:options:...>) will now fail gracefully: | ||
10 | 8 | ||
11 | Cc: qemu-stable@nongnu.org | 9 | $ qemu-img info blkreplay:foo |
10 | qemu-img: Could not open 'blkreplay:foo': Unknown protocol 'blkreplay' | ||
11 | |||
12 | Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com> | ||
13 | Reviewed-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru> | ||
14 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
13 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
14 | --- | 16 | --- |
15 | block/io.c | 3 +-- | 17 | block/blkreplay.c | 3 +-- |
16 | 1 file changed, 1 insertion(+), 2 deletions(-) | 18 | 1 file changed, 1 insertion(+), 2 deletions(-) |
17 | 19 | ||
18 | diff --git a/block/io.c b/block/io.c | 20 | diff --git a/block/blkreplay.c b/block/blkreplay.c |
19 | index XXXXXXX..XXXXXXX 100644 | 21 | index XXXXXXX..XXXXXXX 100755 |
20 | --- a/block/io.c | 22 | --- a/block/blkreplay.c |
21 | +++ b/block/io.c | 23 | +++ b/block/blkreplay.c |
22 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) | 24 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs) |
23 | aio_context_acquire(aio_context); | 25 | |
24 | bdrv_parent_drained_begin(bs); | 26 | static BlockDriver bdrv_blkreplay = { |
25 | aio_disable_external(aio_context); | 27 | .format_name = "blkreplay", |
26 | + bdrv_drain_invoke(bs, true); | 28 | - .protocol_name = "blkreplay", |
27 | aio_context_release(aio_context); | 29 | .instance_size = 0, |
28 | 30 | ||
29 | if (!g_slist_find(aio_ctxs, aio_context)) { | 31 | - .bdrv_file_open = blkreplay_open, |
30 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) | 32 | + .bdrv_open = blkreplay_open, |
31 | aio_context_acquire(aio_context); | 33 | .bdrv_close = blkreplay_close, |
32 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | 34 | .bdrv_child_perm = bdrv_filter_default_perms, |
33 | if (aio_context == bdrv_get_aio_context(bs)) { | 35 | .bdrv_getlength = blkreplay_getlength, |
34 | - /* FIXME Calling this multiple times is wrong */ | ||
35 | - bdrv_drain_invoke(bs, true); | ||
36 | waited |= bdrv_drain_recurse(bs, true); | ||
37 | } | ||
38 | } | ||
39 | -- | 36 | -- |
40 | 2.13.6 | 37 | 2.13.6 |
41 | 38 | ||
42 | 39 | diff view generated by jsdifflib |
1 | From: Doug Gale <doug16k@gmail.com> | 1 | From: Fabiano Rosas <farosas@linux.vnet.ibm.com> |
---|---|---|---|
2 | 2 | ||
3 | Add trace output for commands, errors, and undefined behavior. | 3 | Clarify that: |
4 | Add guest error log output for undefined behavior. | ||
5 | Report invalid undefined accesses to MMIO. | ||
6 | Annotate unlikely error checks with unlikely. | ||
7 | 4 | ||
8 | Signed-off-by: Doug Gale <doug16k@gmail.com> | 5 | - for protocols the brdv_file_open function is used instead |
9 | Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> | 6 | of bdrv_open; |
10 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 7 | |
8 | - when protocol_name is set, a driver should expect | ||
9 | to be given only a filename and no other options. | ||
10 | |||
11 | Signed-off-by: Fabiano Rosas <farosas@linux.vnet.ibm.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | 13 | --- |
13 | hw/block/nvme.c | 349 ++++++++++++++++++++++++++++++++++++++++++-------- | 14 | include/block/block_int.h | 8 ++++++++ |
14 | hw/block/trace-events | 93 ++++++++++++++ | 15 | 1 file changed, 8 insertions(+) |
15 | 2 files changed, 390 insertions(+), 52 deletions(-) | ||
16 | 16 | ||
17 | diff --git a/hw/block/nvme.c b/hw/block/nvme.c | 17 | diff --git a/include/block/block_int.h b/include/block/block_int.h |
18 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/hw/block/nvme.c | 19 | --- a/include/block/block_int.h |
20 | +++ b/hw/block/nvme.c | 20 | +++ b/include/block/block_int.h |
21 | @@ -XXX,XX +XXX,XX @@ | 21 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { |
22 | #include "qapi/visitor.h" | 22 | |
23 | #include "sysemu/block-backend.h" | 23 | int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags, |
24 | 24 | Error **errp); | |
25 | +#include "qemu/log.h" | ||
26 | +#include "trace.h" | ||
27 | #include "nvme.h" | ||
28 | |||
29 | +#define NVME_GUEST_ERR(trace, fmt, ...) \ | ||
30 | + do { \ | ||
31 | + (trace_##trace)(__VA_ARGS__); \ | ||
32 | + qemu_log_mask(LOG_GUEST_ERROR, #trace \ | ||
33 | + " in %s: " fmt "\n", __func__, ## __VA_ARGS__); \ | ||
34 | + } while (0) | ||
35 | + | 25 | + |
36 | static void nvme_process_sq(void *opaque); | 26 | + /* Protocol drivers should implement this instead of bdrv_open */ |
37 | 27 | int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags, | |
38 | static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size) | 28 | Error **errp); |
39 | @@ -XXX,XX +XXX,XX @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq) | 29 | void (*bdrv_close)(BlockDriverState *bs); |
40 | { | 30 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { |
41 | if (cq->irq_enabled) { | 31 | */ |
42 | if (msix_enabled(&(n->parent_obj))) { | 32 | int coroutine_fn (*bdrv_co_flush_to_os)(BlockDriverState *bs); |
43 | + trace_nvme_irq_msix(cq->vector); | 33 | |
44 | msix_notify(&(n->parent_obj), cq->vector); | 34 | + /* |
45 | } else { | 35 | + * Drivers setting this field must be able to work with just a plain |
46 | + trace_nvme_irq_pin(); | 36 | + * filename with '<protocol_name>:' as a prefix, and no other options. |
47 | pci_irq_pulse(&n->parent_obj); | 37 | + * Options may be extracted from the filename by implementing |
48 | } | 38 | + * bdrv_parse_filename. |
49 | + } else { | 39 | + */ |
50 | + trace_nvme_irq_masked(); | 40 | const char *protocol_name; |
51 | } | 41 | int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset, |
52 | } | 42 | PreallocMode prealloc, Error **errp); |
53 | |||
54 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, | ||
55 | trans_len = MIN(len, trans_len); | ||
56 | int num_prps = (len >> n->page_bits) + 1; | ||
57 | |||
58 | - if (!prp1) { | ||
59 | + if (unlikely(!prp1)) { | ||
60 | + trace_nvme_err_invalid_prp(); | ||
61 | return NVME_INVALID_FIELD | NVME_DNR; | ||
62 | } else if (n->cmbsz && prp1 >= n->ctrl_mem.addr && | ||
63 | prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) { | ||
64 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, | ||
65 | } | ||
66 | len -= trans_len; | ||
67 | if (len) { | ||
68 | - if (!prp2) { | ||
69 | + if (unlikely(!prp2)) { | ||
70 | + trace_nvme_err_invalid_prp2_missing(); | ||
71 | goto unmap; | ||
72 | } | ||
73 | if (len > n->page_size) { | ||
74 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, | ||
75 | uint64_t prp_ent = le64_to_cpu(prp_list[i]); | ||
76 | |||
77 | if (i == n->max_prp_ents - 1 && len > n->page_size) { | ||
78 | - if (!prp_ent || prp_ent & (n->page_size - 1)) { | ||
79 | + if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) { | ||
80 | + trace_nvme_err_invalid_prplist_ent(prp_ent); | ||
81 | goto unmap; | ||
82 | } | ||
83 | |||
84 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, | ||
85 | prp_ent = le64_to_cpu(prp_list[i]); | ||
86 | } | ||
87 | |||
88 | - if (!prp_ent || prp_ent & (n->page_size - 1)) { | ||
89 | + if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) { | ||
90 | + trace_nvme_err_invalid_prplist_ent(prp_ent); | ||
91 | goto unmap; | ||
92 | } | ||
93 | |||
94 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, | ||
95 | i++; | ||
96 | } | ||
97 | } else { | ||
98 | - if (prp2 & (n->page_size - 1)) { | ||
99 | + if (unlikely(prp2 & (n->page_size - 1))) { | ||
100 | + trace_nvme_err_invalid_prp2_align(prp2); | ||
101 | goto unmap; | ||
102 | } | ||
103 | if (qsg->nsg) { | ||
104 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len, | ||
105 | QEMUIOVector iov; | ||
106 | uint16_t status = NVME_SUCCESS; | ||
107 | |||
108 | + trace_nvme_dma_read(prp1, prp2); | ||
109 | + | ||
110 | if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) { | ||
111 | return NVME_INVALID_FIELD | NVME_DNR; | ||
112 | } | ||
113 | if (qsg.nsg > 0) { | ||
114 | - if (dma_buf_read(ptr, len, &qsg)) { | ||
115 | + if (unlikely(dma_buf_read(ptr, len, &qsg))) { | ||
116 | + trace_nvme_err_invalid_dma(); | ||
117 | status = NVME_INVALID_FIELD | NVME_DNR; | ||
118 | } | ||
119 | qemu_sglist_destroy(&qsg); | ||
120 | } else { | ||
121 | - if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) { | ||
122 | + if (unlikely(qemu_iovec_to_buf(&iov, 0, ptr, len) != len)) { | ||
123 | + trace_nvme_err_invalid_dma(); | ||
124 | status = NVME_INVALID_FIELD | NVME_DNR; | ||
125 | } | ||
126 | qemu_iovec_destroy(&iov); | ||
127 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, | ||
128 | uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS); | ||
129 | uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS); | ||
130 | |||
131 | - if (slba + nlb > ns->id_ns.nsze) { | ||
132 | + if (unlikely(slba + nlb > ns->id_ns.nsze)) { | ||
133 | + trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze); | ||
134 | return NVME_LBA_RANGE | NVME_DNR; | ||
135 | } | ||
136 | |||
137 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, | ||
138 | int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0; | ||
139 | enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ; | ||
140 | |||
141 | - if ((slba + nlb) > ns->id_ns.nsze) { | ||
142 | + trace_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba); | ||
143 | + | ||
144 | + if (unlikely((slba + nlb) > ns->id_ns.nsze)) { | ||
145 | block_acct_invalid(blk_get_stats(n->conf.blk), acct); | ||
146 | + trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze); | ||
147 | return NVME_LBA_RANGE | NVME_DNR; | ||
148 | } | ||
149 | |||
150 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) | ||
151 | NvmeNamespace *ns; | ||
152 | uint32_t nsid = le32_to_cpu(cmd->nsid); | ||
153 | |||
154 | - if (nsid == 0 || nsid > n->num_namespaces) { | ||
155 | + if (unlikely(nsid == 0 || nsid > n->num_namespaces)) { | ||
156 | + trace_nvme_err_invalid_ns(nsid, n->num_namespaces); | ||
157 | return NVME_INVALID_NSID | NVME_DNR; | ||
158 | } | ||
159 | |||
160 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) | ||
161 | case NVME_CMD_READ: | ||
162 | return nvme_rw(n, ns, cmd, req); | ||
163 | default: | ||
164 | + trace_nvme_err_invalid_opc(cmd->opcode); | ||
165 | return NVME_INVALID_OPCODE | NVME_DNR; | ||
166 | } | ||
167 | } | ||
168 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd) | ||
169 | NvmeCQueue *cq; | ||
170 | uint16_t qid = le16_to_cpu(c->qid); | ||
171 | |||
172 | - if (!qid || nvme_check_sqid(n, qid)) { | ||
173 | + if (unlikely(!qid || nvme_check_sqid(n, qid))) { | ||
174 | + trace_nvme_err_invalid_del_sq(qid); | ||
175 | return NVME_INVALID_QID | NVME_DNR; | ||
176 | } | ||
177 | |||
178 | + trace_nvme_del_sq(qid); | ||
179 | + | ||
180 | sq = n->sq[qid]; | ||
181 | while (!QTAILQ_EMPTY(&sq->out_req_list)) { | ||
182 | req = QTAILQ_FIRST(&sq->out_req_list); | ||
183 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd) | ||
184 | uint16_t qflags = le16_to_cpu(c->sq_flags); | ||
185 | uint64_t prp1 = le64_to_cpu(c->prp1); | ||
186 | |||
187 | - if (!cqid || nvme_check_cqid(n, cqid)) { | ||
188 | + trace_nvme_create_sq(prp1, sqid, cqid, qsize, qflags); | ||
189 | + | ||
190 | + if (unlikely(!cqid || nvme_check_cqid(n, cqid))) { | ||
191 | + trace_nvme_err_invalid_create_sq_cqid(cqid); | ||
192 | return NVME_INVALID_CQID | NVME_DNR; | ||
193 | } | ||
194 | - if (!sqid || !nvme_check_sqid(n, sqid)) { | ||
195 | + if (unlikely(!sqid || !nvme_check_sqid(n, sqid))) { | ||
196 | + trace_nvme_err_invalid_create_sq_sqid(sqid); | ||
197 | return NVME_INVALID_QID | NVME_DNR; | ||
198 | } | ||
199 | - if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) { | ||
200 | + if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) { | ||
201 | + trace_nvme_err_invalid_create_sq_size(qsize); | ||
202 | return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR; | ||
203 | } | ||
204 | - if (!prp1 || prp1 & (n->page_size - 1)) { | ||
205 | + if (unlikely(!prp1 || prp1 & (n->page_size - 1))) { | ||
206 | + trace_nvme_err_invalid_create_sq_addr(prp1); | ||
207 | return NVME_INVALID_FIELD | NVME_DNR; | ||
208 | } | ||
209 | - if (!(NVME_SQ_FLAGS_PC(qflags))) { | ||
210 | + if (unlikely(!(NVME_SQ_FLAGS_PC(qflags)))) { | ||
211 | + trace_nvme_err_invalid_create_sq_qflags(NVME_SQ_FLAGS_PC(qflags)); | ||
212 | return NVME_INVALID_FIELD | NVME_DNR; | ||
213 | } | ||
214 | sq = g_malloc0(sizeof(*sq)); | ||
215 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd) | ||
216 | NvmeCQueue *cq; | ||
217 | uint16_t qid = le16_to_cpu(c->qid); | ||
218 | |||
219 | - if (!qid || nvme_check_cqid(n, qid)) { | ||
220 | + if (unlikely(!qid || nvme_check_cqid(n, qid))) { | ||
221 | + trace_nvme_err_invalid_del_cq_cqid(qid); | ||
222 | return NVME_INVALID_CQID | NVME_DNR; | ||
223 | } | ||
224 | |||
225 | cq = n->cq[qid]; | ||
226 | - if (!QTAILQ_EMPTY(&cq->sq_list)) { | ||
227 | + if (unlikely(!QTAILQ_EMPTY(&cq->sq_list))) { | ||
228 | + trace_nvme_err_invalid_del_cq_notempty(qid); | ||
229 | return NVME_INVALID_QUEUE_DEL; | ||
230 | } | ||
231 | + trace_nvme_del_cq(qid); | ||
232 | nvme_free_cq(cq, n); | ||
233 | return NVME_SUCCESS; | ||
234 | } | ||
235 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd) | ||
236 | uint16_t qflags = le16_to_cpu(c->cq_flags); | ||
237 | uint64_t prp1 = le64_to_cpu(c->prp1); | ||
238 | |||
239 | - if (!cqid || !nvme_check_cqid(n, cqid)) { | ||
240 | + trace_nvme_create_cq(prp1, cqid, vector, qsize, qflags, | ||
241 | + NVME_CQ_FLAGS_IEN(qflags) != 0); | ||
242 | + | ||
243 | + if (unlikely(!cqid || !nvme_check_cqid(n, cqid))) { | ||
244 | + trace_nvme_err_invalid_create_cq_cqid(cqid); | ||
245 | return NVME_INVALID_CQID | NVME_DNR; | ||
246 | } | ||
247 | - if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) { | ||
248 | + if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) { | ||
249 | + trace_nvme_err_invalid_create_cq_size(qsize); | ||
250 | return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR; | ||
251 | } | ||
252 | - if (!prp1) { | ||
253 | + if (unlikely(!prp1)) { | ||
254 | + trace_nvme_err_invalid_create_cq_addr(prp1); | ||
255 | return NVME_INVALID_FIELD | NVME_DNR; | ||
256 | } | ||
257 | - if (vector > n->num_queues) { | ||
258 | + if (unlikely(vector > n->num_queues)) { | ||
259 | + trace_nvme_err_invalid_create_cq_vector(vector); | ||
260 | return NVME_INVALID_IRQ_VECTOR | NVME_DNR; | ||
261 | } | ||
262 | - if (!(NVME_CQ_FLAGS_PC(qflags))) { | ||
263 | + if (unlikely(!(NVME_CQ_FLAGS_PC(qflags)))) { | ||
264 | + trace_nvme_err_invalid_create_cq_qflags(NVME_CQ_FLAGS_PC(qflags)); | ||
265 | return NVME_INVALID_FIELD | NVME_DNR; | ||
266 | } | ||
267 | |||
268 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeIdentify *c) | ||
269 | uint64_t prp1 = le64_to_cpu(c->prp1); | ||
270 | uint64_t prp2 = le64_to_cpu(c->prp2); | ||
271 | |||
272 | + trace_nvme_identify_ctrl(); | ||
273 | + | ||
274 | return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl), | ||
275 | prp1, prp2); | ||
276 | } | ||
277 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c) | ||
278 | uint64_t prp1 = le64_to_cpu(c->prp1); | ||
279 | uint64_t prp2 = le64_to_cpu(c->prp2); | ||
280 | |||
281 | - if (nsid == 0 || nsid > n->num_namespaces) { | ||
282 | + trace_nvme_identify_ns(nsid); | ||
283 | + | ||
284 | + if (unlikely(nsid == 0 || nsid > n->num_namespaces)) { | ||
285 | + trace_nvme_err_invalid_ns(nsid, n->num_namespaces); | ||
286 | return NVME_INVALID_NSID | NVME_DNR; | ||
287 | } | ||
288 | |||
289 | ns = &n->namespaces[nsid - 1]; | ||
290 | + | ||
291 | return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns), | ||
292 | prp1, prp2); | ||
293 | } | ||
294 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c) | ||
295 | uint16_t ret; | ||
296 | int i, j = 0; | ||
297 | |||
298 | + trace_nvme_identify_nslist(min_nsid); | ||
299 | + | ||
300 | list = g_malloc0(data_len); | ||
301 | for (i = 0; i < n->num_namespaces; i++) { | ||
302 | if (i < min_nsid) { | ||
303 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd) | ||
304 | case 0x02: | ||
305 | return nvme_identify_nslist(n, c); | ||
306 | default: | ||
307 | + trace_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns)); | ||
308 | return NVME_INVALID_FIELD | NVME_DNR; | ||
309 | } | ||
310 | } | ||
311 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) | ||
312 | switch (dw10) { | ||
313 | case NVME_VOLATILE_WRITE_CACHE: | ||
314 | result = blk_enable_write_cache(n->conf.blk); | ||
315 | + trace_nvme_getfeat_vwcache(result ? "enabled" : "disabled"); | ||
316 | break; | ||
317 | case NVME_NUMBER_OF_QUEUES: | ||
318 | result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16)); | ||
319 | + trace_nvme_getfeat_numq(result); | ||
320 | break; | ||
321 | default: | ||
322 | + trace_nvme_err_invalid_getfeat(dw10); | ||
323 | return NVME_INVALID_FIELD | NVME_DNR; | ||
324 | } | ||
325 | |||
326 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) | ||
327 | blk_set_enable_write_cache(n->conf.blk, dw11 & 1); | ||
328 | break; | ||
329 | case NVME_NUMBER_OF_QUEUES: | ||
330 | + trace_nvme_setfeat_numq((dw11 & 0xFFFF) + 1, | ||
331 | + ((dw11 >> 16) & 0xFFFF) + 1, | ||
332 | + n->num_queues - 1, n->num_queues - 1); | ||
333 | req->cqe.result = | ||
334 | cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16)); | ||
335 | break; | ||
336 | default: | ||
337 | + trace_nvme_err_invalid_setfeat(dw10); | ||
338 | return NVME_INVALID_FIELD | NVME_DNR; | ||
339 | } | ||
340 | return NVME_SUCCESS; | ||
341 | @@ -XXX,XX +XXX,XX @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) | ||
342 | case NVME_ADM_CMD_GET_FEATURES: | ||
343 | return nvme_get_feature(n, cmd, req); | ||
344 | default: | ||
345 | + trace_nvme_err_invalid_admin_opc(cmd->opcode); | ||
346 | return NVME_INVALID_OPCODE | NVME_DNR; | ||
347 | } | ||
348 | } | ||
349 | @@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n) | ||
350 | uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12; | ||
351 | uint32_t page_size = 1 << page_bits; | ||
352 | |||
353 | - if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq || | ||
354 | - n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) || | ||
355 | - NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) || | ||
356 | - NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) || | ||
357 | - NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) || | ||
358 | - NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) || | ||
359 | - NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) || | ||
360 | - NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) || | ||
361 | - !NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) { | ||
362 | + if (unlikely(n->cq[0])) { | ||
363 | + trace_nvme_err_startfail_cq(); | ||
364 | + return -1; | ||
365 | + } | ||
366 | + if (unlikely(n->sq[0])) { | ||
367 | + trace_nvme_err_startfail_sq(); | ||
368 | + return -1; | ||
369 | + } | ||
370 | + if (unlikely(!n->bar.asq)) { | ||
371 | + trace_nvme_err_startfail_nbarasq(); | ||
372 | + return -1; | ||
373 | + } | ||
374 | + if (unlikely(!n->bar.acq)) { | ||
375 | + trace_nvme_err_startfail_nbaracq(); | ||
376 | + return -1; | ||
377 | + } | ||
378 | + if (unlikely(n->bar.asq & (page_size - 1))) { | ||
379 | + trace_nvme_err_startfail_asq_misaligned(n->bar.asq); | ||
380 | + return -1; | ||
381 | + } | ||
382 | + if (unlikely(n->bar.acq & (page_size - 1))) { | ||
383 | + trace_nvme_err_startfail_acq_misaligned(n->bar.acq); | ||
384 | + return -1; | ||
385 | + } | ||
386 | + if (unlikely(NVME_CC_MPS(n->bar.cc) < | ||
387 | + NVME_CAP_MPSMIN(n->bar.cap))) { | ||
388 | + trace_nvme_err_startfail_page_too_small( | ||
389 | + NVME_CC_MPS(n->bar.cc), | ||
390 | + NVME_CAP_MPSMIN(n->bar.cap)); | ||
391 | + return -1; | ||
392 | + } | ||
393 | + if (unlikely(NVME_CC_MPS(n->bar.cc) > | ||
394 | + NVME_CAP_MPSMAX(n->bar.cap))) { | ||
395 | + trace_nvme_err_startfail_page_too_large( | ||
396 | + NVME_CC_MPS(n->bar.cc), | ||
397 | + NVME_CAP_MPSMAX(n->bar.cap)); | ||
398 | + return -1; | ||
399 | + } | ||
400 | + if (unlikely(NVME_CC_IOCQES(n->bar.cc) < | ||
401 | + NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) { | ||
402 | + trace_nvme_err_startfail_cqent_too_small( | ||
403 | + NVME_CC_IOCQES(n->bar.cc), | ||
404 | + NVME_CTRL_CQES_MIN(n->bar.cap)); | ||
405 | + return -1; | ||
406 | + } | ||
407 | + if (unlikely(NVME_CC_IOCQES(n->bar.cc) > | ||
408 | + NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) { | ||
409 | + trace_nvme_err_startfail_cqent_too_large( | ||
410 | + NVME_CC_IOCQES(n->bar.cc), | ||
411 | + NVME_CTRL_CQES_MAX(n->bar.cap)); | ||
412 | + return -1; | ||
413 | + } | ||
414 | + if (unlikely(NVME_CC_IOSQES(n->bar.cc) < | ||
415 | + NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) { | ||
416 | + trace_nvme_err_startfail_sqent_too_small( | ||
417 | + NVME_CC_IOSQES(n->bar.cc), | ||
418 | + NVME_CTRL_SQES_MIN(n->bar.cap)); | ||
419 | + return -1; | ||
420 | + } | ||
421 | + if (unlikely(NVME_CC_IOSQES(n->bar.cc) > | ||
422 | + NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) { | ||
423 | + trace_nvme_err_startfail_sqent_too_large( | ||
424 | + NVME_CC_IOSQES(n->bar.cc), | ||
425 | + NVME_CTRL_SQES_MAX(n->bar.cap)); | ||
426 | + return -1; | ||
427 | + } | ||
428 | + if (unlikely(!NVME_AQA_ASQS(n->bar.aqa))) { | ||
429 | + trace_nvme_err_startfail_asqent_sz_zero(); | ||
430 | + return -1; | ||
431 | + } | ||
432 | + if (unlikely(!NVME_AQA_ACQS(n->bar.aqa))) { | ||
433 | + trace_nvme_err_startfail_acqent_sz_zero(); | ||
434 | return -1; | ||
435 | } | ||
436 | |||
437 | @@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n) | ||
438 | static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, | ||
439 | unsigned size) | ||
440 | { | ||
441 | + if (unlikely(offset & (sizeof(uint32_t) - 1))) { | ||
442 | + NVME_GUEST_ERR(nvme_ub_mmiowr_misaligned32, | ||
443 | + "MMIO write not 32-bit aligned," | ||
444 | + " offset=0x%"PRIx64"", offset); | ||
445 | + /* should be ignored, fall through for now */ | ||
446 | + } | ||
447 | + | ||
448 | + if (unlikely(size < sizeof(uint32_t))) { | ||
449 | + NVME_GUEST_ERR(nvme_ub_mmiowr_toosmall, | ||
450 | + "MMIO write smaller than 32-bits," | ||
451 | + " offset=0x%"PRIx64", size=%u", | ||
452 | + offset, size); | ||
453 | + /* should be ignored, fall through for now */ | ||
454 | + } | ||
455 | + | ||
456 | switch (offset) { | ||
457 | - case 0xc: | ||
458 | + case 0xc: /* INTMS */ | ||
459 | + if (unlikely(msix_enabled(&(n->parent_obj)))) { | ||
460 | + NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix, | ||
461 | + "undefined access to interrupt mask set" | ||
462 | + " when MSI-X is enabled"); | ||
463 | + /* should be ignored, fall through for now */ | ||
464 | + } | ||
465 | n->bar.intms |= data & 0xffffffff; | ||
466 | n->bar.intmc = n->bar.intms; | ||
467 | + trace_nvme_mmio_intm_set(data & 0xffffffff, | ||
468 | + n->bar.intmc); | ||
469 | break; | ||
470 | - case 0x10: | ||
471 | + case 0x10: /* INTMC */ | ||
472 | + if (unlikely(msix_enabled(&(n->parent_obj)))) { | ||
473 | + NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix, | ||
474 | + "undefined access to interrupt mask clr" | ||
475 | + " when MSI-X is enabled"); | ||
476 | + /* should be ignored, fall through for now */ | ||
477 | + } | ||
478 | n->bar.intms &= ~(data & 0xffffffff); | ||
479 | n->bar.intmc = n->bar.intms; | ||
480 | + trace_nvme_mmio_intm_clr(data & 0xffffffff, | ||
481 | + n->bar.intmc); | ||
482 | break; | ||
483 | - case 0x14: | ||
484 | + case 0x14: /* CC */ | ||
485 | + trace_nvme_mmio_cfg(data & 0xffffffff); | ||
486 | /* Windows first sends data, then sends enable bit */ | ||
487 | if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) && | ||
488 | !NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc)) | ||
489 | @@ -XXX,XX +XXX,XX @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, | ||
490 | |||
491 | if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) { | ||
492 | n->bar.cc = data; | ||
493 | - if (nvme_start_ctrl(n)) { | ||
494 | + if (unlikely(nvme_start_ctrl(n))) { | ||
495 | + trace_nvme_err_startfail(); | ||
496 | n->bar.csts = NVME_CSTS_FAILED; | ||
497 | } else { | ||
498 | + trace_nvme_mmio_start_success(); | ||
499 | n->bar.csts = NVME_CSTS_READY; | ||
500 | } | ||
501 | } else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) { | ||
502 | + trace_nvme_mmio_stopped(); | ||
503 | nvme_clear_ctrl(n); | ||
504 | n->bar.csts &= ~NVME_CSTS_READY; | ||
505 | } | ||
506 | if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) { | ||
507 | - nvme_clear_ctrl(n); | ||
508 | - n->bar.cc = data; | ||
509 | - n->bar.csts |= NVME_CSTS_SHST_COMPLETE; | ||
510 | + trace_nvme_mmio_shutdown_set(); | ||
511 | + nvme_clear_ctrl(n); | ||
512 | + n->bar.cc = data; | ||
513 | + n->bar.csts |= NVME_CSTS_SHST_COMPLETE; | ||
514 | } else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) { | ||
515 | - n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE; | ||
516 | - n->bar.cc = data; | ||
517 | + trace_nvme_mmio_shutdown_cleared(); | ||
518 | + n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE; | ||
519 | + n->bar.cc = data; | ||
520 | + } | ||
521 | + break; | ||
522 | + case 0x1C: /* CSTS */ | ||
523 | + if (data & (1 << 4)) { | ||
524 | + NVME_GUEST_ERR(nvme_ub_mmiowr_ssreset_w1c_unsupported, | ||
525 | + "attempted to W1C CSTS.NSSRO" | ||
526 | + " but CAP.NSSRS is zero (not supported)"); | ||
527 | + } else if (data != 0) { | ||
528 | + NVME_GUEST_ERR(nvme_ub_mmiowr_ro_csts, | ||
529 | + "attempted to set a read only bit" | ||
530 | + " of controller status"); | ||
531 | + } | ||
532 | + break; | ||
533 | + case 0x20: /* NSSR */ | ||
534 | + if (data == 0x4E564D65) { | ||
535 | + trace_nvme_ub_mmiowr_ssreset_unsupported(); | ||
536 | + } else { | ||
537 | + /* The spec says that writes of other values have no effect */ | ||
538 | + return; | ||
539 | } | ||
540 | break; | ||
541 | - case 0x24: | ||
542 | + case 0x24: /* AQA */ | ||
543 | n->bar.aqa = data & 0xffffffff; | ||
544 | + trace_nvme_mmio_aqattr(data & 0xffffffff); | ||
545 | break; | ||
546 | - case 0x28: | ||
547 | + case 0x28: /* ASQ */ | ||
548 | n->bar.asq = data; | ||
549 | + trace_nvme_mmio_asqaddr(data); | ||
550 | break; | ||
551 | - case 0x2c: | ||
552 | + case 0x2c: /* ASQ hi */ | ||
553 | n->bar.asq |= data << 32; | ||
554 | + trace_nvme_mmio_asqaddr_hi(data, n->bar.asq); | ||
555 | break; | ||
556 | - case 0x30: | ||
557 | + case 0x30: /* ACQ */ | ||
558 | + trace_nvme_mmio_acqaddr(data); | ||
559 | n->bar.acq = data; | ||
560 | break; | ||
561 | - case 0x34: | ||
562 | + case 0x34: /* ACQ hi */ | ||
563 | n->bar.acq |= data << 32; | ||
564 | + trace_nvme_mmio_acqaddr_hi(data, n->bar.acq); | ||
565 | break; | ||
566 | + case 0x38: /* CMBLOC */ | ||
567 | + NVME_GUEST_ERR(nvme_ub_mmiowr_cmbloc_reserved, | ||
568 | + "invalid write to reserved CMBLOC" | ||
569 | + " when CMBSZ is zero, ignored"); | ||
570 | + return; | ||
571 | + case 0x3C: /* CMBSZ */ | ||
572 | + NVME_GUEST_ERR(nvme_ub_mmiowr_cmbsz_readonly, | ||
573 | + "invalid write to read only CMBSZ, ignored"); | ||
574 | + return; | ||
575 | default: | ||
576 | + NVME_GUEST_ERR(nvme_ub_mmiowr_invalid, | ||
577 | + "invalid MMIO write," | ||
578 | + " offset=0x%"PRIx64", data=%"PRIx64"", | ||
579 | + offset, data); | ||
580 | break; | ||
581 | } | ||
582 | } | ||
583 | @@ -XXX,XX +XXX,XX @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size) | ||
584 | uint8_t *ptr = (uint8_t *)&n->bar; | ||
585 | uint64_t val = 0; | ||
586 | |||
587 | + if (unlikely(addr & (sizeof(uint32_t) - 1))) { | ||
588 | + NVME_GUEST_ERR(nvme_ub_mmiord_misaligned32, | ||
589 | + "MMIO read not 32-bit aligned," | ||
590 | + " offset=0x%"PRIx64"", addr); | ||
591 | + /* should RAZ, fall through for now */ | ||
592 | + } else if (unlikely(size < sizeof(uint32_t))) { | ||
593 | + NVME_GUEST_ERR(nvme_ub_mmiord_toosmall, | ||
594 | + "MMIO read smaller than 32-bits," | ||
595 | + " offset=0x%"PRIx64"", addr); | ||
596 | + /* should RAZ, fall through for now */ | ||
597 | + } | ||
598 | + | ||
599 | if (addr < sizeof(n->bar)) { | ||
600 | memcpy(&val, ptr + addr, size); | ||
601 | + } else { | ||
602 | + NVME_GUEST_ERR(nvme_ub_mmiord_invalid_ofs, | ||
603 | + "MMIO read beyond last register," | ||
604 | + " offset=0x%"PRIx64", returning 0", addr); | ||
605 | } | ||
606 | + | ||
607 | return val; | ||
608 | } | ||
609 | |||
610 | @@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) | ||
611 | { | ||
612 | uint32_t qid; | ||
613 | |||
614 | - if (addr & ((1 << 2) - 1)) { | ||
615 | + if (unlikely(addr & ((1 << 2) - 1))) { | ||
616 | + NVME_GUEST_ERR(nvme_ub_db_wr_misaligned, | ||
617 | + "doorbell write not 32-bit aligned," | ||
618 | + " offset=0x%"PRIx64", ignoring", addr); | ||
619 | return; | ||
620 | } | ||
621 | |||
622 | if (((addr - 0x1000) >> 2) & 1) { | ||
623 | + /* Completion queue doorbell write */ | ||
624 | + | ||
625 | uint16_t new_head = val & 0xffff; | ||
626 | int start_sqs; | ||
627 | NvmeCQueue *cq; | ||
628 | |||
629 | qid = (addr - (0x1000 + (1 << 2))) >> 3; | ||
630 | - if (nvme_check_cqid(n, qid)) { | ||
631 | + if (unlikely(nvme_check_cqid(n, qid))) { | ||
632 | + NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cq, | ||
633 | + "completion queue doorbell write" | ||
634 | + " for nonexistent queue," | ||
635 | + " sqid=%"PRIu32", ignoring", qid); | ||
636 | return; | ||
637 | } | ||
638 | |||
639 | cq = n->cq[qid]; | ||
640 | - if (new_head >= cq->size) { | ||
641 | + if (unlikely(new_head >= cq->size)) { | ||
642 | + NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cqhead, | ||
643 | + "completion queue doorbell write value" | ||
644 | + " beyond queue size, sqid=%"PRIu32"," | ||
645 | + " new_head=%"PRIu16", ignoring", | ||
646 | + qid, new_head); | ||
647 | return; | ||
648 | } | ||
649 | |||
650 | @@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) | ||
651 | nvme_isr_notify(n, cq); | ||
652 | } | ||
653 | } else { | ||
654 | + /* Submission queue doorbell write */ | ||
655 | + | ||
656 | uint16_t new_tail = val & 0xffff; | ||
657 | NvmeSQueue *sq; | ||
658 | |||
659 | qid = (addr - 0x1000) >> 3; | ||
660 | - if (nvme_check_sqid(n, qid)) { | ||
661 | + if (unlikely(nvme_check_sqid(n, qid))) { | ||
662 | + NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sq, | ||
663 | + "submission queue doorbell write" | ||
664 | + " for nonexistent queue," | ||
665 | + " sqid=%"PRIu32", ignoring", qid); | ||
666 | return; | ||
667 | } | ||
668 | |||
669 | sq = n->sq[qid]; | ||
670 | - if (new_tail >= sq->size) { | ||
671 | + if (unlikely(new_tail >= sq->size)) { | ||
672 | + NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sqtail, | ||
673 | + "submission queue doorbell write value" | ||
674 | + " beyond queue size, sqid=%"PRIu32"," | ||
675 | + " new_tail=%"PRIu16", ignoring", | ||
676 | + qid, new_tail); | ||
677 | return; | ||
678 | } | ||
679 | |||
680 | diff --git a/hw/block/trace-events b/hw/block/trace-events | ||
681 | index XXXXXXX..XXXXXXX 100644 | ||
682 | --- a/hw/block/trace-events | ||
683 | +++ b/hw/block/trace-events | ||
684 | @@ -XXX,XX +XXX,XX @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6 | ||
685 | hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d" | ||
686 | hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d" | ||
687 | |||
688 | +# hw/block/nvme.c | ||
689 | +# nvme traces for successful events | ||
690 | +nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u" | ||
691 | +nvme_irq_pin(void) "pulsing IRQ pin" | ||
692 | +nvme_irq_masked(void) "IRQ is masked" | ||
693 | +nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64"" | ||
694 | +nvme_rw(char const *verb, uint32_t blk_count, uint64_t byte_count, uint64_t lba) "%s %"PRIu32" blocks (%"PRIu64" bytes) from LBA %"PRIu64"" | ||
695 | +nvme_create_sq(uint64_t addr, uint16_t sqid, uint16_t cqid, uint16_t qsize, uint16_t qflags) "create submission queue, addr=0x%"PRIx64", sqid=%"PRIu16", cqid=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16"" | ||
696 | +nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t qflags, int ien) "create completion queue, addr=0x%"PRIx64", cqid=%"PRIu16", vector=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16", ien=%d" | ||
697 | +nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16"" | ||
698 | +nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16"" | ||
699 | +nvme_identify_ctrl(void) "identify controller" | ||
700 | +nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16"" | ||
701 | +nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16"" | ||
702 | +nvme_getfeat_vwcache(char const* result) "get feature volatile write cache, result=%s" | ||
703 | +nvme_getfeat_numq(int result) "get feature number of queues, result=%d" | ||
704 | +nvme_setfeat_numq(int reqcq, int reqsq, int gotcq, int gotsq) "requested cq_count=%d sq_count=%d, responding with cq_count=%d sq_count=%d" | ||
705 | +nvme_mmio_intm_set(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask set, data=0x%"PRIx64", new_mask=0x%"PRIx64"" | ||
706 | +nvme_mmio_intm_clr(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask clr, data=0x%"PRIx64", new_mask=0x%"PRIx64"" | ||
707 | +nvme_mmio_cfg(uint64_t data) "wrote MMIO, config controller config=0x%"PRIx64"" | ||
708 | +nvme_mmio_aqattr(uint64_t data) "wrote MMIO, admin queue attributes=0x%"PRIx64"" | ||
709 | +nvme_mmio_asqaddr(uint64_t data) "wrote MMIO, admin submission queue address=0x%"PRIx64"" | ||
710 | +nvme_mmio_acqaddr(uint64_t data) "wrote MMIO, admin completion queue address=0x%"PRIx64"" | ||
711 | +nvme_mmio_asqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin submission queue high half=0x%"PRIx64", new_address=0x%"PRIx64"" | ||
712 | +nvme_mmio_acqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin completion queue high half=0x%"PRIx64", new_address=0x%"PRIx64"" | ||
713 | +nvme_mmio_start_success(void) "setting controller enable bit succeeded" | ||
714 | +nvme_mmio_stopped(void) "cleared controller enable bit" | ||
715 | +nvme_mmio_shutdown_set(void) "shutdown bit set" | ||
716 | +nvme_mmio_shutdown_cleared(void) "shutdown bit cleared" | ||
717 | + | ||
718 | +# nvme traces for error conditions | ||
719 | +nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size" | ||
720 | +nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64"" | ||
721 | +nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64"" | ||
722 | +nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred" | ||
723 | +nvme_err_invalid_field(void) "invalid field" | ||
724 | +nvme_err_invalid_prp(void) "invalid PRP" | ||
725 | +nvme_err_invalid_sgl(void) "invalid SGL" | ||
726 | +nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u" | ||
727 | +nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8"" | ||
728 | +nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8"" | ||
729 | +nvme_err_invalid_lba_range(uint64_t start, uint64_t len, uint64_t limit) "Invalid LBA start=%"PRIu64" len=%"PRIu64" limit=%"PRIu64"" | ||
730 | +nvme_err_invalid_del_sq(uint16_t qid) "invalid submission queue deletion, sid=%"PRIu16"" | ||
731 | +nvme_err_invalid_create_sq_cqid(uint16_t cqid) "failed creating submission queue, invalid cqid=%"PRIu16"" | ||
732 | +nvme_err_invalid_create_sq_sqid(uint16_t sqid) "failed creating submission queue, invalid sqid=%"PRIu16"" | ||
733 | +nvme_err_invalid_create_sq_size(uint16_t qsize) "failed creating submission queue, invalid qsize=%"PRIu16"" | ||
734 | +nvme_err_invalid_create_sq_addr(uint64_t addr) "failed creating submission queue, addr=0x%"PRIx64"" | ||
735 | +nvme_err_invalid_create_sq_qflags(uint16_t qflags) "failed creating submission queue, qflags=%"PRIu16"" | ||
736 | +nvme_err_invalid_del_cq_cqid(uint16_t cqid) "failed deleting completion queue, cqid=%"PRIu16"" | ||
737 | +nvme_err_invalid_del_cq_notempty(uint16_t cqid) "failed deleting completion queue, it is not empty, cqid=%"PRIu16"" | ||
738 | +nvme_err_invalid_create_cq_cqid(uint16_t cqid) "failed creating completion queue, cqid=%"PRIu16"" | ||
739 | +nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion queue, size=%"PRIu16"" | ||
740 | +nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64"" | ||
741 | +nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16"" | ||
742 | +nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16"" | ||
743 | +nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16"" | ||
744 | +nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32"" | ||
745 | +nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32"" | ||
746 | +nvme_err_startfail_cq(void) "nvme_start_ctrl failed because there are non-admin completion queues" | ||
747 | +nvme_err_startfail_sq(void) "nvme_start_ctrl failed because there are non-admin submission queues" | ||
748 | +nvme_err_startfail_nbarasq(void) "nvme_start_ctrl failed because the admin submission queue address is null" | ||
749 | +nvme_err_startfail_nbaracq(void) "nvme_start_ctrl failed because the admin completion queue address is null" | ||
750 | +nvme_err_startfail_asq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin submission queue address is misaligned: 0x%"PRIx64"" | ||
751 | +nvme_err_startfail_acq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin completion queue address is misaligned: 0x%"PRIx64"" | ||
752 | +nvme_err_startfail_page_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too small: log2size=%u, min=%u" | ||
753 | +nvme_err_startfail_page_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too large: log2size=%u, max=%u" | ||
754 | +nvme_err_startfail_cqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too small: log2size=%u, min=%u" | ||
755 | +nvme_err_startfail_cqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too large: log2size=%u, max=%u" | ||
756 | +nvme_err_startfail_sqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too small: log2size=%u, min=%u" | ||
757 | +nvme_err_startfail_sqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too large: log2size=%u, max=%u" | ||
758 | +nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the admin submission queue size is zero" | ||
759 | +nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero" | ||
760 | +nvme_err_startfail(void) "setting controller enable bit failed" | ||
761 | + | ||
762 | +# Traces for undefined behavior | ||
763 | +nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64"" | ||
764 | +nvme_ub_mmiowr_toosmall(uint64_t offset, unsigned size) "MMIO write smaller than 32 bits, offset=0x%"PRIx64", size=%u" | ||
765 | +nvme_ub_mmiowr_intmask_with_msix(void) "undefined access to interrupt mask set when MSI-X is enabled" | ||
766 | +nvme_ub_mmiowr_ro_csts(void) "attempted to set a read only bit of controller status" | ||
767 | +nvme_ub_mmiowr_ssreset_w1c_unsupported(void) "attempted to W1C CSTS.NSSRO but CAP.NSSRS is zero (not supported)" | ||
768 | +nvme_ub_mmiowr_ssreset_unsupported(void) "attempted NVM subsystem reset but CAP.NSSRS is zero (not supported)" | ||
769 | +nvme_ub_mmiowr_cmbloc_reserved(void) "invalid write to reserved CMBLOC when CMBSZ is zero, ignored" | ||
770 | +nvme_ub_mmiowr_cmbsz_readonly(void) "invalid write to read only CMBSZ, ignored" | ||
771 | +nvme_ub_mmiowr_invalid(uint64_t offset, uint64_t data) "invalid MMIO write, offset=0x%"PRIx64", data=0x%"PRIx64"" | ||
772 | +nvme_ub_mmiord_misaligned32(uint64_t offset) "MMIO read not 32-bit aligned, offset=0x%"PRIx64"" | ||
773 | +nvme_ub_mmiord_toosmall(uint64_t offset) "MMIO read smaller than 32-bits, offset=0x%"PRIx64"" | ||
774 | +nvme_ub_mmiord_invalid_ofs(uint64_t offset) "MMIO read beyond last register, offset=0x%"PRIx64", returning 0" | ||
775 | +nvme_ub_db_wr_misaligned(uint64_t offset) "doorbell write not 32-bit aligned, offset=0x%"PRIx64", ignoring" | ||
776 | +nvme_ub_db_wr_invalid_cq(uint32_t qid) "completion queue doorbell write for nonexistent queue, cqid=%"PRIu32", ignoring" | ||
777 | +nvme_ub_db_wr_invalid_cqhead(uint32_t qid, uint16_t new_head) "completion queue doorbell write value beyond queue size, cqid=%"PRIu32", new_head=%"PRIu16", ignoring" | ||
778 | +nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for nonexistent queue, sqid=%"PRIu32", ignoring" | ||
779 | +nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring" | ||
780 | + | ||
781 | # hw/block/xen_disk.c | ||
782 | xen_disk_alloc(char *name) "%s" | ||
783 | xen_disk_init(char *name) "%s" | ||
784 | -- | 43 | -- |
785 | 2.13.6 | 44 | 2.13.6 |
786 | 45 | ||
787 | 46 | diff view generated by jsdifflib |
1 | From: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com> | 1 | From: Alberto Garcia <berto@igalia.com> |
---|---|---|---|
2 | 2 | ||
3 | Since bdrv_co_preadv does all neccessary checks including | 3 | When we try to allocate new clusters we first look for available ones |
4 | reading after the end of the backing file, avoid duplication | 4 | starting from s->free_cluster_index and once we find them we increase |
5 | of verification before bdrv_co_preadv call. | 5 | their reference counts. Before we get to call update_refcount() to do |
6 | this last step s->free_cluster_index is already pointing to the next | ||
7 | cluster after the ones we are trying to allocate. | ||
6 | 8 | ||
7 | Signed-off-by: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com> | 9 | During update_refcount() it may happen however that we also need to |
8 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 10 | allocate a new refcount block in order to store the refcounts of these |
11 | new clusters (and to complicate things further that may also require | ||
12 | us to grow the refcount table). After all this we don't know if the | ||
13 | clusters that we originally tried to allocate are still available, so | ||
14 | we return -EAGAIN to ask the caller to restart the search for free | ||
15 | clusters. | ||
16 | |||
17 | This is what can happen in a common scenario: | ||
18 | |||
19 | 1) We want to allocate a new cluster and we see that cluster N is | ||
20 | free. | ||
21 | |||
22 | 2) We try to increase N's refcount but all refcount blocks are full, | ||
23 | so we allocate a new one at N+1 (where s->free_cluster_index was | ||
24 | pointing at). | ||
25 | |||
26 | 3) Once we're done we return -EAGAIN to look again for a free | ||
27 | cluster, but now s->free_cluster_index points at N+2, so that's | ||
28 | the one we allocate. Cluster N remains unallocated and we have a | ||
29 | hole in the qcow2 file. | ||
30 | |||
31 | This can be reproduced easily: | ||
32 | |||
33 | qemu-img create -f qcow2 -o cluster_size=512 hd.qcow2 1M | ||
34 | qemu-io -c 'write 0 124k' hd.qcow2 | ||
35 | |||
36 | After this the image has 132608 bytes (256 clusters), and the refcount | ||
37 | block is full. If we write 512 more bytes it should allocate two new | ||
38 | clusters: the data cluster itself and a new refcount block. | ||
39 | |||
40 | qemu-io -c 'write 124k 512' hd.qcow2 | ||
41 | |||
42 | However the image has now three new clusters (259 in total), and the | ||
43 | first one of them is empty (and unallocated): | ||
44 | |||
45 | dd if=hd.qcow2 bs=512c skip=256 count=1 | hexdump -C | ||
46 | |||
47 | If we write larger amounts of data in the last step instead of the 512 | ||
48 | bytes used in this example we can create larger holes in the qcow2 | ||
49 | file. | ||
50 | |||
51 | What this patch does is reset s->free_cluster_index to its previous | ||
52 | value when alloc_refcount_block() returns -EAGAIN. This way the caller | ||
53 | will try to allocate again the original clusters if they are still | ||
54 | free. | ||
55 | |||
56 | The output of iotest 026 also needs to be updated because now that | ||
57 | images have no holes some tests fail at a different point and the | ||
58 | number of leaked clusters is different. | ||
59 | |||
60 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
9 | Reviewed-by: Eric Blake <eblake@redhat.com> | 61 | Reviewed-by: Eric Blake <eblake@redhat.com> |
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 62 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
11 | --- | 63 | --- |
12 | block/qcow2.h | 3 --- | 64 | block/qcow2-refcount.c | 7 +++++++ |
13 | block/qcow2.c | 51 ++++++++------------------------------------------- | 65 | tests/qemu-iotests/026.out | 6 +++--- |
14 | 2 files changed, 8 insertions(+), 46 deletions(-) | 66 | tests/qemu-iotests/121 | 20 ++++++++++++++++++++ |
67 | tests/qemu-iotests/121.out | 10 ++++++++++ | ||
68 | 4 files changed, 40 insertions(+), 3 deletions(-) | ||
15 | 69 | ||
16 | diff --git a/block/qcow2.h b/block/qcow2.h | 70 | diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c |
17 | index XXXXXXX..XXXXXXX 100644 | 71 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/block/qcow2.h | 72 | --- a/block/qcow2-refcount.c |
19 | +++ b/block/qcow2.h | 73 | +++ b/block/qcow2-refcount.c |
20 | @@ -XXX,XX +XXX,XX @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset) | 74 | @@ -XXX,XX +XXX,XX @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, |
21 | } | 75 | qcow2_cache_put(s->refcount_block_cache, &refcount_block); |
22 | 76 | } | |
23 | /* qcow2.c functions */ | 77 | ret = alloc_refcount_block(bs, cluster_index, &refcount_block); |
24 | -int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, | 78 | + /* If the caller needs to restart the search for free clusters, |
25 | - int64_t sector_num, int nb_sectors); | 79 | + * try the same ones first to see if they're still free. */ |
26 | - | 80 | + if (ret == -EAGAIN) { |
27 | int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size, | 81 | + if (s->free_cluster_index > (start >> s->cluster_bits)) { |
28 | int refcount_order, bool generous_increase, | 82 | + s->free_cluster_index = (start >> s->cluster_bits); |
29 | uint64_t *refblock_count); | 83 | + } |
30 | diff --git a/block/qcow2.c b/block/qcow2.c | 84 | + } |
85 | if (ret < 0) { | ||
86 | goto fail; | ||
87 | } | ||
88 | diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out | ||
31 | index XXXXXXX..XXXXXXX 100644 | 89 | index XXXXXXX..XXXXXXX 100644 |
32 | --- a/block/qcow2.c | 90 | --- a/tests/qemu-iotests/026.out |
33 | +++ b/block/qcow2.c | 91 | +++ b/tests/qemu-iotests/026.out |
34 | @@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, | 92 | @@ -XXX,XX +XXX,XX @@ Failed to flush the L2 table cache: No space left on device |
35 | return status; | 93 | Failed to flush the refcount block cache: No space left on device |
36 | } | 94 | write failed: No space left on device |
37 | 95 | ||
38 | -/* handle reading after the end of the backing file */ | 96 | -11 leaked clusters were found on the image. |
39 | -int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, | 97 | +10 leaked clusters were found on the image. |
40 | - int64_t offset, int bytes) | 98 | This means waste of disk space, but no harm to data. |
41 | -{ | 99 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 |
42 | - uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE; | 100 | |
43 | - int n1; | 101 | @@ -XXX,XX +XXX,XX @@ Failed to flush the L2 table cache: No space left on device |
44 | - | 102 | Failed to flush the refcount block cache: No space left on device |
45 | - if ((offset + bytes) <= bs_size) { | 103 | write failed: No space left on device |
46 | - return bytes; | 104 | |
47 | - } | 105 | -11 leaked clusters were found on the image. |
48 | - | 106 | +10 leaked clusters were found on the image. |
49 | - if (offset >= bs_size) { | 107 | This means waste of disk space, but no harm to data. |
50 | - n1 = 0; | 108 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 |
51 | - } else { | 109 | |
52 | - n1 = bs_size - offset; | 110 | @@ -XXX,XX +XXX,XX @@ Failed to flush the L2 table cache: No space left on device |
53 | - } | 111 | Failed to flush the refcount block cache: No space left on device |
54 | - | 112 | write failed: No space left on device |
55 | - qemu_iovec_memset(qiov, n1, 0, bytes - n1); | 113 | |
56 | - | 114 | -11 leaked clusters were found on the image. |
57 | - return n1; | 115 | +10 leaked clusters were found on the image. |
58 | -} | 116 | This means waste of disk space, but no harm to data. |
59 | - | 117 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 |
60 | static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, | 118 | |
61 | uint64_t bytes, QEMUIOVector *qiov, | 119 | diff --git a/tests/qemu-iotests/121 b/tests/qemu-iotests/121 |
62 | int flags) | 120 | index XXXXXXX..XXXXXXX 100755 |
63 | { | 121 | --- a/tests/qemu-iotests/121 |
64 | BDRVQcow2State *s = bs->opaque; | 122 | +++ b/tests/qemu-iotests/121 |
65 | - int offset_in_cluster, n1; | 123 | @@ -XXX,XX +XXX,XX @@ $QEMU_IO -c 'write 63M 130K' "$TEST_IMG" | _filter_qemu_io |
66 | + int offset_in_cluster; | 124 | |
67 | int ret; | 125 | _check_test_img |
68 | unsigned int cur_bytes; /* number of bytes in current iteration */ | 126 | |
69 | uint64_t cluster_offset = 0; | 127 | +echo |
70 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, | 128 | +echo '=== Allocating a new refcount block must not leave holes in the image ===' |
71 | case QCOW2_CLUSTER_UNALLOCATED: | 129 | +echo |
72 | 130 | + | |
73 | if (bs->backing) { | 131 | +IMGOPTS='cluster_size=512,refcount_bits=16' _make_test_img 1M |
74 | - /* read from the base image */ | 132 | + |
75 | - n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov, | 133 | +# This results in an image with 256 used clusters: the qcow2 header, |
76 | - offset, cur_bytes); | 134 | +# the refcount table, one refcount block, the L1 table, four L2 tables |
77 | - if (n1 > 0) { | 135 | +# and 248 data clusters |
78 | - QEMUIOVector local_qiov; | 136 | +$QEMU_IO -c 'write 0 124k' "$TEST_IMG" | _filter_qemu_io |
79 | - | 137 | + |
80 | - qemu_iovec_init(&local_qiov, hd_qiov.niov); | 138 | +# 256 clusters of 512 bytes each give us a 128K image |
81 | - qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1); | 139 | +stat -c "size=%s (expected 131072)" $TEST_IMG |
82 | - | 140 | + |
83 | - BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); | 141 | +# All 256 entries of the refcount block are used, so writing a new |
84 | - qemu_co_mutex_unlock(&s->lock); | 142 | +# data cluster also allocates a new refcount block |
85 | - ret = bdrv_co_preadv(bs->backing, offset, n1, | 143 | +$QEMU_IO -c 'write 124k 512' "$TEST_IMG" | _filter_qemu_io |
86 | - &local_qiov, 0); | 144 | + |
87 | - qemu_co_mutex_lock(&s->lock); | 145 | +# Two more clusters, the image size should be 129K now |
88 | - | 146 | +stat -c "size=%s (expected 132096)" $TEST_IMG |
89 | - qemu_iovec_destroy(&local_qiov); | 147 | |
90 | - | 148 | # success, all done |
91 | - if (ret < 0) { | 149 | echo |
92 | - goto fail; | 150 | diff --git a/tests/qemu-iotests/121.out b/tests/qemu-iotests/121.out |
93 | - } | 151 | index XXXXXXX..XXXXXXX 100644 |
94 | + BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); | 152 | --- a/tests/qemu-iotests/121.out |
95 | + qemu_co_mutex_unlock(&s->lock); | 153 | +++ b/tests/qemu-iotests/121.out |
96 | + ret = bdrv_co_preadv(bs->backing, offset, cur_bytes, | 154 | @@ -XXX,XX +XXX,XX @@ wrote 133120/133120 bytes at offset 66060288 |
97 | + &hd_qiov, 0); | 155 | 130 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
98 | + qemu_co_mutex_lock(&s->lock); | 156 | No errors were found on the image. |
99 | + if (ret < 0) { | 157 | |
100 | + goto fail; | 158 | +=== Allocating a new refcount block must not leave holes in the image === |
101 | } | 159 | + |
102 | } else { | 160 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 |
103 | /* Note: in this case, no need to wait */ | 161 | +wrote 126976/126976 bytes at offset 0 |
162 | +124 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
163 | +size=131072 (expected 131072) | ||
164 | +wrote 512/512 bytes at offset 126976 | ||
165 | +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
166 | +size=132096 (expected 132096) | ||
167 | + | ||
168 | *** done | ||
104 | -- | 169 | -- |
105 | 2.13.6 | 170 | 2.13.6 |
106 | 171 | ||
107 | 172 | diff view generated by jsdifflib |
1 | Removing a quorum child node with x-blockdev-change results in a quorum | 1 | What static=on really does is what we call metadata preallocation for |
---|---|---|---|
2 | driver state that cannot be recreated with create options because it | 2 | other block drivers. While we can still change the QMP interface, make |
3 | would require a list with gaps. This causes trouble in at least | 3 | it more consistent by using 'preallocation' for VDI, too. |
4 | .bdrv_refresh_filename(). | ||
5 | 4 | ||
6 | Document this problem so that we won't accidentally mark the command | 5 | This doesn't implement any new functionality, so the only supported |
7 | stable without having addressed it. | 6 | preallocation modes are 'off' and 'metadata' for now. |
8 | 7 | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | Reviewed-by: Alberto Garcia <berto@igalia.com> | 9 | Reviewed-by: Eric Blake <eblake@redhat.com> |
11 | --- | 10 | --- |
12 | qapi/block-core.json | 4 ++++ | 11 | qapi/block-core.json | 7 +++---- |
13 | 1 file changed, 4 insertions(+) | 12 | block/vdi.c | 24 ++++++++++++++++++++++-- |
13 | 2 files changed, 25 insertions(+), 6 deletions(-) | ||
14 | 14 | ||
15 | diff --git a/qapi/block-core.json b/qapi/block-core.json | 15 | diff --git a/qapi/block-core.json b/qapi/block-core.json |
16 | index XXXXXXX..XXXXXXX 100644 | 16 | index XXXXXXX..XXXXXXX 100644 |
17 | --- a/qapi/block-core.json | 17 | --- a/qapi/block-core.json |
18 | +++ b/qapi/block-core.json | 18 | +++ b/qapi/block-core.json |
19 | @@ -XXX,XX +XXX,XX @@ | 19 | @@ -XXX,XX +XXX,XX @@ |
20 | # does not support all kinds of operations, all kinds of children, nor | ||
21 | # all block drivers. | ||
22 | # | 20 | # |
23 | +# FIXME Removing children from a quorum node means introducing gaps in the | 21 | # @file Node to create the image format on |
24 | +# child indices. This cannot be represented in the 'children' list of | 22 | # @size Size of the virtual disk in bytes |
25 | +# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename(). | 23 | -# @static Whether to create a statically (true) or |
26 | +# | 24 | -# dynamically (false) allocated image |
27 | # Warning: The data in a new quorum child MUST be consistent with that of | 25 | -# (default: false, i.e. dynamic) |
28 | # the rest of the array. | 26 | +# @preallocation Preallocation mode for the new image (allowed values: off, |
27 | +# metadata; default: off) | ||
29 | # | 28 | # |
29 | # Since: 2.12 | ||
30 | ## | ||
31 | { 'struct': 'BlockdevCreateOptionsVdi', | ||
32 | 'data': { 'file': 'BlockdevRef', | ||
33 | 'size': 'size', | ||
34 | - '*static': 'bool' } } | ||
35 | + '*preallocation': 'PreallocMode' } } | ||
36 | |||
37 | ## | ||
38 | # @BlockdevVhdxSubformat: | ||
39 | diff --git a/block/vdi.c b/block/vdi.c | ||
40 | index XXXXXXX..XXXXXXX 100644 | ||
41 | --- a/block/vdi.c | ||
42 | +++ b/block/vdi.c | ||
43 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, | ||
44 | int ret = 0; | ||
45 | uint64_t bytes = 0; | ||
46 | uint32_t blocks; | ||
47 | - uint32_t image_type = VDI_TYPE_DYNAMIC; | ||
48 | + uint32_t image_type; | ||
49 | VdiHeader header; | ||
50 | size_t i; | ||
51 | size_t bmap_size; | ||
52 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, | ||
53 | |||
54 | /* Validate options and set default values */ | ||
55 | bytes = vdi_opts->size; | ||
56 | - if (vdi_opts->q_static) { | ||
57 | + | ||
58 | + if (!vdi_opts->has_preallocation) { | ||
59 | + vdi_opts->preallocation = PREALLOC_MODE_OFF; | ||
60 | + } | ||
61 | + switch (vdi_opts->preallocation) { | ||
62 | + case PREALLOC_MODE_OFF: | ||
63 | + image_type = VDI_TYPE_DYNAMIC; | ||
64 | + break; | ||
65 | + case PREALLOC_MODE_METADATA: | ||
66 | image_type = VDI_TYPE_STATIC; | ||
67 | + break; | ||
68 | + default: | ||
69 | + error_setg(errp, "Preallocation mode not supported for vdi"); | ||
70 | + return -EINVAL; | ||
71 | } | ||
72 | + | ||
73 | #ifndef CONFIG_VDI_STATIC_IMAGE | ||
74 | if (image_type == VDI_TYPE_STATIC) { | ||
75 | ret = -ENOTSUP; | ||
76 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, | ||
77 | BlockdevCreateOptions *create_options = NULL; | ||
78 | BlockDriverState *bs_file = NULL; | ||
79 | uint64_t block_size = DEFAULT_CLUSTER_SIZE; | ||
80 | + bool is_static = false; | ||
81 | Visitor *v; | ||
82 | Error *local_err = NULL; | ||
83 | int ret; | ||
84 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, | ||
85 | goto done; | ||
86 | } | ||
87 | #endif | ||
88 | + if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) { | ||
89 | + is_static = true; | ||
90 | + } | ||
91 | |||
92 | qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true); | ||
93 | |||
94 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, | ||
95 | |||
96 | qdict_put_str(qdict, "driver", "vdi"); | ||
97 | qdict_put_str(qdict, "file", bs_file->node_name); | ||
98 | + if (is_static) { | ||
99 | + qdict_put_str(qdict, "preallocation", "metadata"); | ||
100 | + } | ||
101 | |||
102 | /* Get the QAPI object */ | ||
103 | v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); | ||
30 | -- | 104 | -- |
31 | 2.13.6 | 105 | 2.13.6 |
32 | 106 | ||
33 | 107 | diff view generated by jsdifflib |
1 | The device is drained, so there is no point in waiting for requests at | 1 | Use qemu_uuid_unparse() instead of uuid_unparse() to make vdi.c compile |
---|---|---|---|
2 | the end of the drained section. Remove the bdrv_drain_recurse() calls | 2 | again when CONFIG_VDI_DEBUG is set. In order to prevent future bitrot, |
3 | there. | 3 | replace '#ifdef CONFIG_VDI_DEBUG' by 'if (VDI_DEBUG)' so that the |
4 | 4 | compiler always sees the code. | |
5 | The bdrv_drain_recurse() calls were introduced in commit 481cad48e5e | ||
6 | in order to call the .bdrv_co_drain_end() driver callback. This is now | ||
7 | done by a separate bdrv_drain_invoke() call. | ||
8 | 5 | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> | 7 | Reviewed-by: Eric Blake <eblake@redhat.com> |
11 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
12 | --- | 8 | --- |
13 | block/io.c | 2 -- | 9 | block/vdi.c | 22 ++++++++++------------ |
14 | 1 file changed, 2 deletions(-) | 10 | 1 file changed, 10 insertions(+), 12 deletions(-) |
15 | 11 | ||
16 | diff --git a/block/io.c b/block/io.c | 12 | diff --git a/block/vdi.c b/block/vdi.c |
17 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/block/io.c | 14 | --- a/block/vdi.c |
19 | +++ b/block/io.c | 15 | +++ b/block/vdi.c |
20 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs) | 16 | @@ -XXX,XX +XXX,XX @@ static void vdi_header_to_le(VdiHeader *header) |
21 | 17 | qemu_uuid_bswap(&header->uuid_parent); | |
22 | bdrv_parent_drained_end(bs); | ||
23 | bdrv_drain_invoke(bs, false); | ||
24 | - bdrv_drain_recurse(bs); | ||
25 | aio_enable_external(bdrv_get_aio_context(bs)); | ||
26 | } | 18 | } |
27 | 19 | ||
28 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) | 20 | -#if defined(CONFIG_VDI_DEBUG) |
29 | aio_enable_external(aio_context); | 21 | static void vdi_header_print(VdiHeader *header) |
30 | bdrv_parent_drained_end(bs); | 22 | { |
31 | bdrv_drain_invoke(bs, false); | 23 | char uuid[37]; |
32 | - bdrv_drain_recurse(bs); | 24 | @@ -XXX,XX +XXX,XX @@ static void vdi_header_print(VdiHeader *header) |
33 | aio_context_release(aio_context); | 25 | logout("block extra 0x%04x\n", header->block_extra); |
26 | logout("blocks tot. 0x%04x\n", header->blocks_in_image); | ||
27 | logout("blocks all. 0x%04x\n", header->blocks_allocated); | ||
28 | - uuid_unparse(header->uuid_image, uuid); | ||
29 | + qemu_uuid_unparse(&header->uuid_image, uuid); | ||
30 | logout("uuid image %s\n", uuid); | ||
31 | - uuid_unparse(header->uuid_last_snap, uuid); | ||
32 | + qemu_uuid_unparse(&header->uuid_last_snap, uuid); | ||
33 | logout("uuid snap %s\n", uuid); | ||
34 | - uuid_unparse(header->uuid_link, uuid); | ||
35 | + qemu_uuid_unparse(&header->uuid_link, uuid); | ||
36 | logout("uuid link %s\n", uuid); | ||
37 | - uuid_unparse(header->uuid_parent, uuid); | ||
38 | + qemu_uuid_unparse(&header->uuid_parent, uuid); | ||
39 | logout("uuid parent %s\n", uuid); | ||
40 | } | ||
41 | -#endif | ||
42 | |||
43 | static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res, | ||
44 | BdrvCheckMode fix) | ||
45 | @@ -XXX,XX +XXX,XX @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, | ||
34 | } | 46 | } |
35 | 47 | ||
48 | vdi_header_to_cpu(&header); | ||
49 | -#if defined(CONFIG_VDI_DEBUG) | ||
50 | - vdi_header_print(&header); | ||
51 | -#endif | ||
52 | + if (VDI_DEBUG) { | ||
53 | + vdi_header_print(&header); | ||
54 | + } | ||
55 | |||
56 | if (header.disk_size > VDI_DISK_SIZE_MAX) { | ||
57 | error_setg(errp, "Unsupported VDI image size (size is 0x%" PRIx64 | ||
58 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, | ||
59 | qemu_uuid_generate(&header.uuid_image); | ||
60 | qemu_uuid_generate(&header.uuid_last_snap); | ||
61 | /* There is no need to set header.uuid_link or header.uuid_parent here. */ | ||
62 | -#if defined(CONFIG_VDI_DEBUG) | ||
63 | - vdi_header_print(&header); | ||
64 | -#endif | ||
65 | + if (VDI_DEBUG) { | ||
66 | + vdi_header_print(&header); | ||
67 | + } | ||
68 | vdi_header_to_le(&header); | ||
69 | ret = blk_pwrite(blk, offset, &header, sizeof(header), 0); | ||
70 | if (ret < 0) { | ||
36 | -- | 71 | -- |
37 | 2.13.6 | 72 | 2.13.6 |
38 | 73 | ||
39 | 74 | diff view generated by jsdifflib |
1 | This adds a test case that the BlockDriver callbacks for drain are | ||
---|---|---|---|
2 | called in bdrv_drained_all_begin/end(), and that both of them are called | ||
3 | exactly once. | ||
4 | |||
5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 1 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
7 | Reviewed-by: Eric Blake <eblake@redhat.com> | 2 | Reviewed-by: Eric Blake <eblake@redhat.com> |
8 | --- | 3 | --- |
9 | tests/test-bdrv-drain.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++ | 4 | tests/qemu-iotests/211 | 246 +++++++++++++++++++++++++++++++++++++++++++++ |
10 | tests/Makefile.include | 2 + | 5 | tests/qemu-iotests/211.out | 97 ++++++++++++++++++ |
11 | 2 files changed, 139 insertions(+) | 6 | tests/qemu-iotests/group | 1 + |
12 | create mode 100644 tests/test-bdrv-drain.c | 7 | 3 files changed, 344 insertions(+) |
8 | create mode 100755 tests/qemu-iotests/211 | ||
9 | create mode 100644 tests/qemu-iotests/211.out | ||
13 | 10 | ||
14 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c | 11 | diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 |
12 | new file mode 100755 | ||
13 | index XXXXXXX..XXXXXXX | ||
14 | --- /dev/null | ||
15 | +++ b/tests/qemu-iotests/211 | ||
16 | @@ -XXX,XX +XXX,XX @@ | ||
17 | +#!/bin/bash | ||
18 | +# | ||
19 | +# Test VDI and file image creation | ||
20 | +# | ||
21 | +# Copyright (C) 2018 Red Hat, Inc. | ||
22 | +# | ||
23 | +# This program is free software; you can redistribute it and/or modify | ||
24 | +# it under the terms of the GNU General Public License as published by | ||
25 | +# the Free Software Foundation; either version 2 of the License, or | ||
26 | +# (at your option) any later version. | ||
27 | +# | ||
28 | +# This program is distributed in the hope that it will be useful, | ||
29 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
30 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
31 | +# GNU General Public License for more details. | ||
32 | +# | ||
33 | +# You should have received a copy of the GNU General Public License | ||
34 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
35 | +# | ||
36 | + | ||
37 | +# creator | ||
38 | +owner=kwolf@redhat.com | ||
39 | + | ||
40 | +seq=`basename $0` | ||
41 | +echo "QA output created by $seq" | ||
42 | + | ||
43 | +here=`pwd` | ||
44 | +status=1 # failure is the default! | ||
45 | + | ||
46 | +# get standard environment, filters and checks | ||
47 | +. ./common.rc | ||
48 | +. ./common.filter | ||
49 | + | ||
50 | +_supported_fmt vdi | ||
51 | +_supported_proto file | ||
52 | +_supported_os Linux | ||
53 | + | ||
54 | +function do_run_qemu() | ||
55 | +{ | ||
56 | + echo Testing: "$@" | ||
57 | + $QEMU -nographic -qmp stdio -serial none "$@" | ||
58 | + echo | ||
59 | +} | ||
60 | + | ||
61 | +function run_qemu() | ||
62 | +{ | ||
63 | + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ | ||
64 | + | _filter_qemu | _filter_imgfmt \ | ||
65 | + | _filter_actual_image_size | ||
66 | +} | ||
67 | + | ||
68 | +echo | ||
69 | +echo "=== Successful image creation (defaults) ===" | ||
70 | +echo | ||
71 | + | ||
72 | +size=$((128 * 1024 * 1024)) | ||
73 | + | ||
74 | +run_qemu <<EOF | ||
75 | +{ "execute": "qmp_capabilities" } | ||
76 | +{ "execute": "x-blockdev-create", | ||
77 | + "arguments": { | ||
78 | + "driver": "file", | ||
79 | + "filename": "$TEST_IMG", | ||
80 | + "size": 0 | ||
81 | + } | ||
82 | +} | ||
83 | +{ "execute": "blockdev-add", | ||
84 | + "arguments": { | ||
85 | + "driver": "file", | ||
86 | + "node-name": "imgfile", | ||
87 | + "filename": "$TEST_IMG" | ||
88 | + } | ||
89 | +} | ||
90 | +{ "execute": "x-blockdev-create", | ||
91 | + "arguments": { | ||
92 | + "driver": "$IMGFMT", | ||
93 | + "file": "imgfile", | ||
94 | + "size": $size | ||
95 | + } | ||
96 | +} | ||
97 | +{ "execute": "quit" } | ||
98 | +EOF | ||
99 | + | ||
100 | +_img_info --format-specific | _filter_img_info --format-specific | ||
101 | +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map | ||
102 | + | ||
103 | +echo | ||
104 | +echo "=== Successful image creation (explicit defaults) ===" | ||
105 | +echo | ||
106 | + | ||
107 | +# Choose a different size to show that we got a new image | ||
108 | +size=$((64 * 1024 * 1024)) | ||
109 | + | ||
110 | +run_qemu <<EOF | ||
111 | +{ "execute": "qmp_capabilities" } | ||
112 | +{ "execute": "x-blockdev-create", | ||
113 | + "arguments": { | ||
114 | + "driver": "file", | ||
115 | + "filename": "$TEST_IMG", | ||
116 | + "size": 0 | ||
117 | + } | ||
118 | +} | ||
119 | +{ "execute": "x-blockdev-create", | ||
120 | + "arguments": { | ||
121 | + "driver": "$IMGFMT", | ||
122 | + "file": { | ||
123 | + "driver": "file", | ||
124 | + "filename": "$TEST_IMG" | ||
125 | + }, | ||
126 | + "size": $size, | ||
127 | + "preallocation": "off" | ||
128 | + } | ||
129 | +} | ||
130 | +{ "execute": "quit" } | ||
131 | +EOF | ||
132 | + | ||
133 | +_img_info --format-specific | _filter_img_info --format-specific | ||
134 | +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map | ||
135 | + | ||
136 | +echo | ||
137 | +echo "=== Successful image creation (with non-default options) ===" | ||
138 | +echo | ||
139 | + | ||
140 | +# Choose a different size to show that we got a new image | ||
141 | +size=$((32 * 1024 * 1024)) | ||
142 | + | ||
143 | +run_qemu <<EOF | ||
144 | +{ "execute": "qmp_capabilities" } | ||
145 | +{ "execute": "x-blockdev-create", | ||
146 | + "arguments": { | ||
147 | + "driver": "file", | ||
148 | + "filename": "$TEST_IMG", | ||
149 | + "size": 0 | ||
150 | + } | ||
151 | +} | ||
152 | +{ "execute": "x-blockdev-create", | ||
153 | + "arguments": { | ||
154 | + "driver": "$IMGFMT", | ||
155 | + "file": { | ||
156 | + "driver": "file", | ||
157 | + "filename": "$TEST_IMG" | ||
158 | + }, | ||
159 | + "size": $size, | ||
160 | + "preallocation": "metadata" | ||
161 | + } | ||
162 | +} | ||
163 | +{ "execute": "quit" } | ||
164 | +EOF | ||
165 | + | ||
166 | +_img_info --format-specific | _filter_img_info --format-specific | ||
167 | +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map | ||
168 | + | ||
169 | +echo | ||
170 | +echo "=== Invalid BlockdevRef ===" | ||
171 | +echo | ||
172 | + | ||
173 | +run_qemu <<EOF | ||
174 | +{ "execute": "qmp_capabilities" } | ||
175 | +{ "execute": "x-blockdev-create", | ||
176 | + "arguments": { | ||
177 | + "driver": "$IMGFMT", | ||
178 | + "file": "this doesn't exist", | ||
179 | + "size": $size | ||
180 | + } | ||
181 | +} | ||
182 | +{ "execute": "quit" } | ||
183 | +EOF | ||
184 | + | ||
185 | +echo | ||
186 | +echo "=== Zero size ===" | ||
187 | +echo | ||
188 | + | ||
189 | +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF | ||
190 | +{ "execute": "qmp_capabilities" } | ||
191 | +{ "execute": "x-blockdev-create", | ||
192 | + "arguments": { | ||
193 | + "driver": "$IMGFMT", | ||
194 | + "file": "node0", | ||
195 | + "size": 0 | ||
196 | + } | ||
197 | +} | ||
198 | +{ "execute": "quit" } | ||
199 | +EOF | ||
200 | + | ||
201 | +_img_info | _filter_img_info | ||
202 | + | ||
203 | +echo | ||
204 | +echo "=== Maximum size ===" | ||
205 | +echo | ||
206 | + | ||
207 | +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF | ||
208 | +{ "execute": "qmp_capabilities" } | ||
209 | +{ "execute": "x-blockdev-create", | ||
210 | + "arguments": { | ||
211 | + "driver": "$IMGFMT", | ||
212 | + "file": "node0", | ||
213 | + "size": 562949819203584 | ||
214 | + } | ||
215 | +} | ||
216 | +{ "execute": "quit" } | ||
217 | +EOF | ||
218 | + | ||
219 | +_img_info | _filter_img_info | ||
220 | + | ||
221 | +echo | ||
222 | +echo "=== Invalid sizes ===" | ||
223 | +echo | ||
224 | + | ||
225 | +# TODO Negative image sizes aren't handled correctly, but this is a problem | ||
226 | +# with QAPI's implementation of the 'size' type and affects other commands as | ||
227 | +# well. Once this is fixed, we may want to add a test case here. | ||
228 | + | ||
229 | +# 1. 2^64 - 512 | ||
230 | +# 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this) | ||
231 | +# 3. 0x1fffff8000001 (one byte more than maximum image size for VDI) | ||
232 | + | ||
233 | +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF | ||
234 | +{ "execute": "qmp_capabilities" } | ||
235 | +{ "execute": "x-blockdev-create", | ||
236 | + "arguments": { | ||
237 | + "driver": "$IMGFMT", | ||
238 | + "file": "node0", | ||
239 | + "size": 18446744073709551104 | ||
240 | + } | ||
241 | +} | ||
242 | +{ "execute": "x-blockdev-create", | ||
243 | + "arguments": { | ||
244 | + "driver": "$IMGFMT", | ||
245 | + "file": "node0", | ||
246 | + "size": 9223372036854775808 | ||
247 | + } | ||
248 | +} | ||
249 | +{ "execute": "x-blockdev-create", | ||
250 | + "arguments": { | ||
251 | + "driver": "$IMGFMT", | ||
252 | + "file": "node0", | ||
253 | + "size": 562949819203585 | ||
254 | + } | ||
255 | +} | ||
256 | +{ "execute": "quit" } | ||
257 | +EOF | ||
258 | + | ||
259 | +# success, all done | ||
260 | +echo "*** done" | ||
261 | +rm -f $seq.full | ||
262 | +status=0 | ||
263 | diff --git a/tests/qemu-iotests/211.out b/tests/qemu-iotests/211.out | ||
15 | new file mode 100644 | 264 | new file mode 100644 |
16 | index XXXXXXX..XXXXXXX | 265 | index XXXXXXX..XXXXXXX |
17 | --- /dev/null | 266 | --- /dev/null |
18 | +++ b/tests/test-bdrv-drain.c | 267 | +++ b/tests/qemu-iotests/211.out |
19 | @@ -XXX,XX +XXX,XX @@ | 268 | @@ -XXX,XX +XXX,XX @@ |
20 | +/* | 269 | +QA output created by 211 |
21 | + * Block node draining tests | 270 | + |
22 | + * | 271 | +=== Successful image creation (defaults) === |
23 | + * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com> | 272 | + |
24 | + * | 273 | +Testing: |
25 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | 274 | +QMP_VERSION |
26 | + * of this software and associated documentation files (the "Software"), to deal | 275 | +{"return": {}} |
27 | + * in the Software without restriction, including without limitation the rights | 276 | +{"return": {}} |
28 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 277 | +{"return": {}} |
29 | + * copies of the Software, and to permit persons to whom the Software is | 278 | +{"return": {}} |
30 | + * furnished to do so, subject to the following conditions: | 279 | +{"return": {}} |
31 | + * | 280 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} |
32 | + * The above copyright notice and this permission notice shall be included in | 281 | + |
33 | + * all copies or substantial portions of the Software. | 282 | +image: TEST_DIR/t.IMGFMT |
34 | + * | 283 | +file format: IMGFMT |
35 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 284 | +virtual size: 128M (134217728 bytes) |
36 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 285 | +[{ "start": 0, "length": 134217728, "depth": 0, "zero": true, "data": false}] |
37 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 286 | + |
38 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 287 | +=== Successful image creation (explicit defaults) === |
39 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 288 | + |
40 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 289 | +Testing: |
41 | + * THE SOFTWARE. | 290 | +QMP_VERSION |
42 | + */ | 291 | +{"return": {}} |
43 | + | 292 | +{"return": {}} |
44 | +#include "qemu/osdep.h" | 293 | +{"return": {}} |
45 | +#include "block/block.h" | 294 | +{"return": {}} |
46 | +#include "sysemu/block-backend.h" | 295 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} |
47 | +#include "qapi/error.h" | 296 | + |
48 | + | 297 | +image: TEST_DIR/t.IMGFMT |
49 | +typedef struct BDRVTestState { | 298 | +file format: IMGFMT |
50 | + int drain_count; | 299 | +virtual size: 64M (67108864 bytes) |
51 | +} BDRVTestState; | 300 | +[{ "start": 0, "length": 67108864, "depth": 0, "zero": true, "data": false}] |
52 | + | 301 | + |
53 | +static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs) | 302 | +=== Successful image creation (with non-default options) === |
54 | +{ | 303 | + |
55 | + BDRVTestState *s = bs->opaque; | 304 | +Testing: |
56 | + s->drain_count++; | 305 | +QMP_VERSION |
57 | +} | 306 | +{"return": {}} |
58 | + | 307 | +{"return": {}} |
59 | +static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs) | 308 | +{"return": {}} |
60 | +{ | 309 | +{"return": {}} |
61 | + BDRVTestState *s = bs->opaque; | 310 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} |
62 | + s->drain_count--; | 311 | + |
63 | +} | 312 | +image: TEST_DIR/t.IMGFMT |
64 | + | 313 | +file format: IMGFMT |
65 | +static void bdrv_test_close(BlockDriverState *bs) | 314 | +virtual size: 32M (33554432 bytes) |
66 | +{ | 315 | +[{ "start": 0, "length": 3072, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, |
67 | + BDRVTestState *s = bs->opaque; | 316 | +{ "start": 3072, "length": 33551360, "depth": 0, "zero": true, "data": true, "offset": OFFSET}] |
68 | + g_assert_cmpint(s->drain_count, >, 0); | 317 | + |
69 | +} | 318 | +=== Invalid BlockdevRef === |
70 | + | 319 | + |
71 | +static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, | 320 | +Testing: |
72 | + uint64_t offset, uint64_t bytes, | 321 | +QMP_VERSION |
73 | + QEMUIOVector *qiov, int flags) | 322 | +{"return": {}} |
74 | +{ | 323 | +{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}} |
75 | + /* We want this request to stay until the polling loop in drain waits for | 324 | +{"return": {}} |
76 | + * it to complete. We need to sleep a while as bdrv_drain_invoke() comes | 325 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} |
77 | + * first and polls its result, too, but it shouldn't accidentally complete | 326 | + |
78 | + * this request yet. */ | 327 | + |
79 | + co_aio_sleep_ns(qemu_get_aio_context(), QEMU_CLOCK_REALTIME, 100000); | 328 | +=== Zero size === |
80 | + | 329 | + |
81 | + return 0; | 330 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 |
82 | +} | 331 | +QMP_VERSION |
83 | + | 332 | +{"return": {}} |
84 | +static BlockDriver bdrv_test = { | 333 | +{"return": {}} |
85 | + .format_name = "test", | 334 | +{"return": {}} |
86 | + .instance_size = sizeof(BDRVTestState), | 335 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} |
87 | + | 336 | + |
88 | + .bdrv_close = bdrv_test_close, | 337 | +image: TEST_DIR/t.IMGFMT |
89 | + .bdrv_co_preadv = bdrv_test_co_preadv, | 338 | +file format: IMGFMT |
90 | + | 339 | +virtual size: 0 (0 bytes) |
91 | + .bdrv_co_drain_begin = bdrv_test_co_drain_begin, | 340 | + |
92 | + .bdrv_co_drain_end = bdrv_test_co_drain_end, | 341 | +=== Maximum size === |
93 | +}; | 342 | + |
94 | + | 343 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 |
95 | +static void aio_ret_cb(void *opaque, int ret) | 344 | +QMP_VERSION |
96 | +{ | 345 | +{"return": {}} |
97 | + int *aio_ret = opaque; | 346 | +{"return": {}} |
98 | + *aio_ret = ret; | 347 | +{"return": {}} |
99 | +} | 348 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} |
100 | + | 349 | + |
101 | +static void test_drv_cb_drain_all(void) | 350 | +image: TEST_DIR/t.IMGFMT |
102 | +{ | 351 | +file format: IMGFMT |
103 | + BlockBackend *blk; | 352 | +virtual size: 512T (562949819203584 bytes) |
104 | + BlockDriverState *bs; | 353 | + |
105 | + BDRVTestState *s; | 354 | +=== Invalid sizes === |
106 | + BlockAIOCB *acb; | 355 | + |
107 | + int aio_ret; | 356 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 |
108 | + | 357 | +QMP_VERSION |
109 | + QEMUIOVector qiov; | 358 | +{"return": {}} |
110 | + struct iovec iov = { | 359 | +{"error": {"class": "GenericError", "desc": "Unsupported VDI image size (size is 0xfffffffffffffe00, max supported is 0x1fffff8000000)"}} |
111 | + .iov_base = NULL, | 360 | +{"error": {"class": "GenericError", "desc": "Unsupported VDI image size (size is 0x8000000000000000, max supported is 0x1fffff8000000)"}} |
112 | + .iov_len = 0, | 361 | +{"error": {"class": "GenericError", "desc": "Unsupported VDI image size (size is 0x1fffff8000001, max supported is 0x1fffff8000000)"}} |
113 | + }; | 362 | +{"return": {}} |
114 | + qemu_iovec_init_external(&qiov, &iov, 1); | 363 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} |
115 | + | 364 | + |
116 | + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); | 365 | +*** done |
117 | + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, | 366 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group |
118 | + &error_abort); | ||
119 | + s = bs->opaque; | ||
120 | + blk_insert_bs(blk, bs, &error_abort); | ||
121 | + | ||
122 | + /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */ | ||
123 | + g_assert_cmpint(s->drain_count, ==, 0); | ||
124 | + bdrv_drain_all_begin(); | ||
125 | + g_assert_cmpint(s->drain_count, ==, 1); | ||
126 | + bdrv_drain_all_end(); | ||
127 | + g_assert_cmpint(s->drain_count, ==, 0); | ||
128 | + | ||
129 | + /* Now do the same while a request is pending */ | ||
130 | + aio_ret = -EINPROGRESS; | ||
131 | + acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret); | ||
132 | + g_assert(acb != NULL); | ||
133 | + g_assert_cmpint(aio_ret, ==, -EINPROGRESS); | ||
134 | + | ||
135 | + g_assert_cmpint(s->drain_count, ==, 0); | ||
136 | + bdrv_drain_all_begin(); | ||
137 | + g_assert_cmpint(aio_ret, ==, 0); | ||
138 | + g_assert_cmpint(s->drain_count, ==, 1); | ||
139 | + bdrv_drain_all_end(); | ||
140 | + g_assert_cmpint(s->drain_count, ==, 0); | ||
141 | + | ||
142 | + bdrv_unref(bs); | ||
143 | + blk_unref(blk); | ||
144 | +} | ||
145 | + | ||
146 | +int main(int argc, char **argv) | ||
147 | +{ | ||
148 | + bdrv_init(); | ||
149 | + qemu_init_main_loop(&error_abort); | ||
150 | + | ||
151 | + g_test_init(&argc, &argv, NULL); | ||
152 | + | ||
153 | + g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); | ||
154 | + | ||
155 | + return g_test_run(); | ||
156 | +} | ||
157 | diff --git a/tests/Makefile.include b/tests/Makefile.include | ||
158 | index XXXXXXX..XXXXXXX 100644 | 367 | index XXXXXXX..XXXXXXX 100644 |
159 | --- a/tests/Makefile.include | 368 | --- a/tests/qemu-iotests/group |
160 | +++ b/tests/Makefile.include | 369 | +++ b/tests/qemu-iotests/group |
161 | @@ -XXX,XX +XXX,XX @@ gcov-files-test-thread-pool-y = thread-pool.c | 370 | @@ -XXX,XX +XXX,XX @@ |
162 | gcov-files-test-hbitmap-y = util/hbitmap.c | 371 | 208 rw auto quick |
163 | check-unit-y += tests/test-hbitmap$(EXESUF) | 372 | 209 rw auto quick |
164 | gcov-files-test-hbitmap-y = blockjob.c | 373 | 210 rw auto |
165 | +check-unit-y += tests/test-bdrv-drain$(EXESUF) | 374 | +211 rw auto quick |
166 | check-unit-y += tests/test-blockjob$(EXESUF) | ||
167 | check-unit-y += tests/test-blockjob-txn$(EXESUF) | ||
168 | check-unit-y += tests/test-x86-cpuid$(EXESUF) | ||
169 | @@ -XXX,XX +XXX,XX @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y) | ||
170 | tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y) | ||
171 | tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y) | ||
172 | tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y) | ||
173 | +tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y) | ||
174 | tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y) | ||
175 | tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y) | ||
176 | tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y) | ||
177 | -- | 375 | -- |
178 | 2.13.6 | 376 | 2.13.6 |
179 | 377 | ||
180 | 378 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | We want to test resizing even for luks. The only change that is needed | ||
2 | is to explicitly zero out new space for luks because it's undefined. | ||
1 | 3 | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
5 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
6 | Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> | ||
7 | --- | ||
8 | tests/qemu-iotests/025 | 9 ++++++++- | ||
9 | 1 file changed, 8 insertions(+), 1 deletion(-) | ||
10 | |||
11 | diff --git a/tests/qemu-iotests/025 b/tests/qemu-iotests/025 | ||
12 | index XXXXXXX..XXXXXXX 100755 | ||
13 | --- a/tests/qemu-iotests/025 | ||
14 | +++ b/tests/qemu-iotests/025 | ||
15 | @@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
16 | . ./common.filter | ||
17 | . ./common.pattern | ||
18 | |||
19 | -_supported_fmt raw qcow2 qed | ||
20 | +_supported_fmt raw qcow2 qed luks | ||
21 | _supported_proto file sheepdog rbd nfs | ||
22 | _supported_os Linux | ||
23 | |||
24 | @@ -XXX,XX +XXX,XX @@ length | ||
25 | EOF | ||
26 | _check_test_img | ||
27 | |||
28 | +# bdrv_truncate() doesn't zero the new space, so we need to do that explicitly. | ||
29 | +# We still want to test automatic zeroing for other formats even though | ||
30 | +# bdrv_truncate() doesn't guarantee it. | ||
31 | +if [ "$IMGFMT" == "luks" ]; then | ||
32 | + $QEMU_IO -c "write -z $small_size $((big_size - small_size))" "$TEST_IMG" > /dev/null | ||
33 | +fi | ||
34 | + | ||
35 | echo | ||
36 | echo "=== Verifying image size after reopen" | ||
37 | $QEMU_IO -c "length" "$TEST_IMG" | ||
38 | -- | ||
39 | 2.13.6 | ||
40 | |||
41 | diff view generated by jsdifflib |
1 | Commit 1f4ad7d fixed 'qemu-img info' for raw images that are currently | 1 | Commit e39e959e fixed an invalid assertion in the .bdrv_length |
---|---|---|---|
2 | in use as a mirror target. It is not enough for image formats, though, | 2 | implementation, but left a similar assertion in place for |
3 | as these still unconditionally request BLK_PERM_CONSISTENT_READ. | 3 | .bdrv_truncate. Instead of crashing when the user requests a too large |
4 | image size, fail gracefully. | ||
4 | 5 | ||
5 | As this permission is geared towards whether the guest-visible data is | 6 | A file size of exactly INT64_MAX caused failure before, but is actually |
6 | consistent, and has no impact on whether the metadata is sane, and | 7 | legal. |
7 | 'qemu-img info' does not read guest-visible data (except for the raw | ||
8 | format), it makes sense to not require BLK_PERM_CONSISTENT_READ if there | ||
9 | is not going to be any guest I/O performed, regardless of image format. | ||
10 | 8 | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
11 | Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> | ||
12 | --- | 12 | --- |
13 | block.c | 6 +++++- | 13 | block/crypto.c | 6 +++++- |
14 | 1 file changed, 5 insertions(+), 1 deletion(-) | 14 | 1 file changed, 5 insertions(+), 1 deletion(-) |
15 | 15 | ||
16 | diff --git a/block.c b/block.c | 16 | diff --git a/block/crypto.c b/block/crypto.c |
17 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/block.c | 18 | --- a/block/crypto.c |
19 | +++ b/block.c | 19 | +++ b/block/crypto.c |
20 | @@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, | 20 | @@ -XXX,XX +XXX,XX @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, |
21 | assert(role == &child_backing || role == &child_file); | 21 | BlockCrypto *crypto = bs->opaque; |
22 | 22 | uint64_t payload_offset = | |
23 | if (!backing) { | 23 | qcrypto_block_get_payload_offset(crypto->block); |
24 | + int flags = bdrv_reopen_get_flags(reopen_queue, bs); | 24 | - assert(payload_offset < (INT64_MAX - offset)); |
25 | + | 25 | + |
26 | /* Apart from the modifications below, the same permissions are | 26 | + if (payload_offset > INT64_MAX - offset) { |
27 | * forwarded and left alone as for filters */ | 27 | + error_setg(errp, "The requested file size is too large"); |
28 | bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared, | 28 | + return -EFBIG; |
29 | @@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, | 29 | + } |
30 | 30 | ||
31 | /* bs->file always needs to be consistent because of the metadata. We | 31 | offset += payload_offset; |
32 | * can never allow other users to resize or write to it. */ | 32 | |
33 | - perm |= BLK_PERM_CONSISTENT_READ; | ||
34 | + if (!(flags & BDRV_O_NO_IO)) { | ||
35 | + perm |= BLK_PERM_CONSISTENT_READ; | ||
36 | + } | ||
37 | shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); | ||
38 | } else { | ||
39 | /* We want consistent read from backing files if the parent needs it. | ||
40 | -- | 33 | -- |
41 | 2.13.6 | 34 | 2.13.6 |
42 | 35 | ||
43 | 36 | diff view generated by jsdifflib |
1 | This change separates bdrv_drain_invoke(), which calls the BlockDriver | 1 | This tests that the .bdrv_truncate implementation for luks doesn't crash |
---|---|---|---|
2 | drain callbacks, from bdrv_drain_recurse(). Instead, the function | 2 | for invalid image sizes. |
3 | performs its own recursion now. | ||
4 | 3 | ||
5 | One reason for this is that bdrv_drain_recurse() can be called multiple | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | times by bdrv_drain_all_begin(), but the callbacks may only be called | 5 | Reviewed-by: Eric Blake <eblake@redhat.com> |
7 | once. The separation is necessary to fix this bug. | 6 | --- |
7 | tests/qemu-iotests/210 | 37 +++++++++++++++++++++++++++++++++++++ | ||
8 | tests/qemu-iotests/210.out | 16 ++++++++++++++++ | ||
9 | 2 files changed, 53 insertions(+) | ||
8 | 10 | ||
9 | The other reason is that we intend to go to a model where we call all | 11 | diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210 |
10 | driver callbacks first, and only then start polling. This is not fully | 12 | index XXXXXXX..XXXXXXX 100755 |
11 | achieved yet with this patch, as bdrv_drain_invoke() contains a | 13 | --- a/tests/qemu-iotests/210 |
12 | BDRV_POLL_WHILE() loop for the block driver callbacks, which can still | 14 | +++ b/tests/qemu-iotests/210 |
13 | call callbacks for any unrelated event. It's a step in this direction | 15 | @@ -XXX,XX +XXX,XX @@ run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \ |
14 | anyway. | 16 | { "execute": "quit" } |
15 | 17 | EOF | |
16 | Cc: qemu-stable@nongnu.org | 18 | |
17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 19 | +echo |
18 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 20 | +echo "=== Resize image with invalid sizes ===" |
19 | --- | 21 | +echo |
20 | block/io.c | 14 +++++++++++--- | 22 | + |
21 | 1 file changed, 11 insertions(+), 3 deletions(-) | 23 | +run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \ |
22 | 24 | + -blockdev driver=luks,file=node0,key-secret=keysec0,node-name=node1 \ | |
23 | diff --git a/block/io.c b/block/io.c | 25 | + -object secret,id=keysec0,data="foo" <<EOF |
26 | +{ "execute": "qmp_capabilities" } | ||
27 | +{ "execute": "block_resize", | ||
28 | + "arguments": { | ||
29 | + "node-name": "node1", | ||
30 | + "size": 9223372036854775296 | ||
31 | + } | ||
32 | +} | ||
33 | +{ "execute": "block_resize", | ||
34 | + "arguments": { | ||
35 | + "node-name": "node1", | ||
36 | + "size": 9223372036854775808 | ||
37 | + } | ||
38 | +} | ||
39 | +{ "execute": "block_resize", | ||
40 | + "arguments": { | ||
41 | + "node-name": "node1", | ||
42 | + "size": 18446744073709551104 | ||
43 | + } | ||
44 | +} | ||
45 | +{ "execute": "block_resize", | ||
46 | + "arguments": { | ||
47 | + "node-name": "node1", | ||
48 | + "size": -9223372036854775808 | ||
49 | + } | ||
50 | +} | ||
51 | +{ "execute": "quit" } | ||
52 | +EOF | ||
53 | + | ||
54 | +_img_info | _filter_img_info | ||
55 | + | ||
56 | # success, all done | ||
57 | echo "*** done" | ||
58 | rm -f $seq.full | ||
59 | diff --git a/tests/qemu-iotests/210.out b/tests/qemu-iotests/210.out | ||
24 | index XXXXXXX..XXXXXXX 100644 | 60 | index XXXXXXX..XXXXXXX 100644 |
25 | --- a/block/io.c | 61 | --- a/tests/qemu-iotests/210.out |
26 | +++ b/block/io.c | 62 | +++ b/tests/qemu-iotests/210.out |
27 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) | 63 | @@ -XXX,XX +XXX,XX @@ QMP_VERSION |
28 | bdrv_wakeup(bs); | 64 | {"return": {}} |
29 | } | 65 | {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} |
30 | 66 | ||
31 | +/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */ | ||
32 | static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) | ||
33 | { | ||
34 | + BdrvChild *child, *tmp; | ||
35 | BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin}; | ||
36 | |||
37 | if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) || | ||
38 | @@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) | ||
39 | data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data); | ||
40 | bdrv_coroutine_enter(bs, data.co); | ||
41 | BDRV_POLL_WHILE(bs, !data.done); | ||
42 | + | 67 | + |
43 | + QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { | 68 | +=== Resize image with invalid sizes === |
44 | + bdrv_drain_invoke(child->bs, begin); | 69 | + |
45 | + } | 70 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -blockdev driver=IMGFMT,file=node0,key-secret=keysec0,node-name=node1 -object secret,id=keysec0,data=foo |
46 | } | 71 | +QMP_VERSION |
47 | 72 | +{"return": {}} | |
48 | static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) | 73 | +{"error": {"class": "GenericError", "desc": "The requested file size is too large"}} |
49 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) | 74 | +{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}} |
50 | BdrvChild *child, *tmp; | 75 | +{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}} |
51 | bool waited; | 76 | +{"error": {"class": "GenericError", "desc": "Parameter 'size' expects a >0 size"}} |
52 | 77 | +{"return": {}} | |
53 | - /* Ensure any pending metadata writes are submitted to bs->file. */ | 78 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} |
54 | - bdrv_drain_invoke(bs, begin); | 79 | + |
55 | - | 80 | +image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"} |
56 | /* Wait for drained requests to finish */ | 81 | +file format: IMGFMT |
57 | waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0); | 82 | +virtual size: 0 (0 bytes) |
58 | 83 | *** done | |
59 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs) | ||
60 | bdrv_parent_drained_begin(bs); | ||
61 | } | ||
62 | |||
63 | + bdrv_drain_invoke(bs, true); | ||
64 | bdrv_drain_recurse(bs, true); | ||
65 | } | ||
66 | |||
67 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs) | ||
68 | } | ||
69 | |||
70 | bdrv_parent_drained_end(bs); | ||
71 | + bdrv_drain_invoke(bs, false); | ||
72 | bdrv_drain_recurse(bs, false); | ||
73 | aio_enable_external(bdrv_get_aio_context(bs)); | ||
74 | } | ||
75 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) | ||
76 | aio_context_acquire(aio_context); | ||
77 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
78 | if (aio_context == bdrv_get_aio_context(bs)) { | ||
79 | + /* FIXME Calling this multiple times is wrong */ | ||
80 | + bdrv_drain_invoke(bs, true); | ||
81 | waited |= bdrv_drain_recurse(bs, true); | ||
82 | } | ||
83 | } | ||
84 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) | ||
85 | aio_context_acquire(aio_context); | ||
86 | aio_enable_external(aio_context); | ||
87 | bdrv_parent_drained_end(bs); | ||
88 | + bdrv_drain_invoke(bs, false); | ||
89 | bdrv_drain_recurse(bs, false); | ||
90 | aio_context_release(aio_context); | ||
91 | } | ||
92 | -- | 84 | -- |
93 | 2.13.6 | 85 | 2.13.6 |
94 | 86 | ||
95 | 87 | diff view generated by jsdifflib |
1 | Commit 15afd94a047 added code to acquire and release the AioContext in | 1 | It's unclear what the real maximum cluster size is for the Parallels |
---|---|---|---|
2 | qemuio_command(). This means that the lock is taken twice now in the | 2 | format, but let's at least make sure that we don't get integer |
3 | call path from hmp_qemu_io(). This causes BDRV_POLL_WHILE() to hang for | 3 | overflows in our .bdrv_co_create implementation. |
4 | any requests issued to nodes in a non-mainloop AioContext. | ||
5 | |||
6 | Dropping the first locking from hmp_qemu_io() fixes the problem. | ||
7 | 4 | ||
8 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
9 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 6 | Reviewed-by: Eric Blake <eblake@redhat.com> |
10 | --- | 7 | --- |
11 | hmp.c | 6 ------ | 8 | block/parallels.c | 5 +++++ |
12 | 1 file changed, 6 deletions(-) | 9 | 1 file changed, 5 insertions(+) |
13 | 10 | ||
14 | diff --git a/hmp.c b/hmp.c | 11 | diff --git a/block/parallels.c b/block/parallels.c |
15 | index XXXXXXX..XXXXXXX 100644 | 12 | index XXXXXXX..XXXXXXX 100644 |
16 | --- a/hmp.c | 13 | --- a/block/parallels.c |
17 | +++ b/hmp.c | 14 | +++ b/block/parallels.c |
18 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) | 15 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts, |
19 | { | 16 | cl_size = DEFAULT_CLUSTER_SIZE; |
20 | BlockBackend *blk; | ||
21 | BlockBackend *local_blk = NULL; | ||
22 | - AioContext *aio_context; | ||
23 | const char* device = qdict_get_str(qdict, "device"); | ||
24 | const char* command = qdict_get_str(qdict, "command"); | ||
25 | Error *err = NULL; | ||
26 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) | ||
27 | } | ||
28 | } | 17 | } |
29 | 18 | ||
30 | - aio_context = blk_get_aio_context(blk); | 19 | + /* XXX What is the real limit here? This is an insanely large maximum. */ |
31 | - aio_context_acquire(aio_context); | 20 | + if (cl_size >= INT64_MAX / MAX_PARALLELS_IMAGE_FACTOR) { |
32 | - | 21 | + error_setg(errp, "Cluster size is too large"); |
33 | /* | 22 | + return -EINVAL; |
34 | * Notably absent: Proper permission management. This is sad, but it seems | 23 | + } |
35 | * almost impossible to achieve without changing the semantics and thereby | 24 | if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) { |
36 | @@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict) | 25 | error_setg(errp, "Image size is too large for this cluster size"); |
37 | */ | 26 | return -E2BIG; |
38 | qemuio_command(blk, command); | ||
39 | |||
40 | - aio_context_release(aio_context); | ||
41 | - | ||
42 | fail: | ||
43 | blk_unref(local_blk); | ||
44 | hmp_handle_error(mon, &err); | ||
45 | -- | 27 | -- |
46 | 2.13.6 | 28 | 2.13.6 |
47 | 29 | ||
48 | 30 | diff view generated by jsdifflib |
1 | From: Fam Zheng <famz@redhat.com> | ||
---|---|---|---|
2 | |||
3 | Signed-off-by: Fam Zheng <famz@redhat.com> | ||
4 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
5 | Reviewed-by: Kashyap Chamarthy <kchamart@redhat.com> | ||
6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 1 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
7 | --- | 2 | --- |
8 | qemu-img.texi | 9 +++++++++ | 3 | tests/qemu-iotests/212 | 326 +++++++++++++++++++++++++++++++++++++++++++++ |
9 | 1 file changed, 9 insertions(+) | 4 | tests/qemu-iotests/212.out | 111 +++++++++++++++ |
5 | tests/qemu-iotests/group | 1 + | ||
6 | 3 files changed, 438 insertions(+) | ||
7 | create mode 100755 tests/qemu-iotests/212 | ||
8 | create mode 100644 tests/qemu-iotests/212.out | ||
10 | 9 | ||
11 | diff --git a/qemu-img.texi b/qemu-img.texi | 10 | diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212 |
11 | new file mode 100755 | ||
12 | index XXXXXXX..XXXXXXX | ||
13 | --- /dev/null | ||
14 | +++ b/tests/qemu-iotests/212 | ||
15 | @@ -XXX,XX +XXX,XX @@ | ||
16 | +#!/bin/bash | ||
17 | +# | ||
18 | +# Test parallels and file image creation | ||
19 | +# | ||
20 | +# Copyright (C) 2018 Red Hat, Inc. | ||
21 | +# | ||
22 | +# This program is free software; you can redistribute it and/or modify | ||
23 | +# it under the terms of the GNU General Public License as published by | ||
24 | +# the Free Software Foundation; either version 2 of the License, or | ||
25 | +# (at your option) any later version. | ||
26 | +# | ||
27 | +# This program is distributed in the hope that it will be useful, | ||
28 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
29 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
30 | +# GNU General Public License for more details. | ||
31 | +# | ||
32 | +# You should have received a copy of the GNU General Public License | ||
33 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
34 | +# | ||
35 | + | ||
36 | +# creator | ||
37 | +owner=kwolf@redhat.com | ||
38 | + | ||
39 | +seq=`basename $0` | ||
40 | +echo "QA output created by $seq" | ||
41 | + | ||
42 | +here=`pwd` | ||
43 | +status=1 # failure is the default! | ||
44 | + | ||
45 | +# get standard environment, filters and checks | ||
46 | +. ./common.rc | ||
47 | +. ./common.filter | ||
48 | + | ||
49 | +_supported_fmt parallels | ||
50 | +_supported_proto file | ||
51 | +_supported_os Linux | ||
52 | + | ||
53 | +function do_run_qemu() | ||
54 | +{ | ||
55 | + echo Testing: "$@" | ||
56 | + $QEMU -nographic -qmp stdio -serial none "$@" | ||
57 | + echo | ||
58 | +} | ||
59 | + | ||
60 | +function run_qemu() | ||
61 | +{ | ||
62 | + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ | ||
63 | + | _filter_qemu | _filter_imgfmt \ | ||
64 | + | _filter_actual_image_size | ||
65 | +} | ||
66 | + | ||
67 | +echo | ||
68 | +echo "=== Successful image creation (defaults) ===" | ||
69 | +echo | ||
70 | + | ||
71 | +size=$((128 * 1024 * 1024)) | ||
72 | + | ||
73 | +run_qemu <<EOF | ||
74 | +{ "execute": "qmp_capabilities" } | ||
75 | +{ "execute": "x-blockdev-create", | ||
76 | + "arguments": { | ||
77 | + "driver": "file", | ||
78 | + "filename": "$TEST_IMG", | ||
79 | + "size": 0 | ||
80 | + } | ||
81 | +} | ||
82 | +{ "execute": "blockdev-add", | ||
83 | + "arguments": { | ||
84 | + "driver": "file", | ||
85 | + "node-name": "imgfile", | ||
86 | + "filename": "$TEST_IMG" | ||
87 | + } | ||
88 | +} | ||
89 | +{ "execute": "x-blockdev-create", | ||
90 | + "arguments": { | ||
91 | + "driver": "$IMGFMT", | ||
92 | + "file": "imgfile", | ||
93 | + "size": $size | ||
94 | + } | ||
95 | +} | ||
96 | +{ "execute": "quit" } | ||
97 | +EOF | ||
98 | + | ||
99 | +_img_info --format-specific | _filter_img_info --format-specific | ||
100 | + | ||
101 | +echo | ||
102 | +echo "=== Successful image creation (explicit defaults) ===" | ||
103 | +echo | ||
104 | + | ||
105 | +# Choose a different size to show that we got a new image | ||
106 | +size=$((64 * 1024 * 1024)) | ||
107 | + | ||
108 | +run_qemu <<EOF | ||
109 | +{ "execute": "qmp_capabilities" } | ||
110 | +{ "execute": "x-blockdev-create", | ||
111 | + "arguments": { | ||
112 | + "driver": "file", | ||
113 | + "filename": "$TEST_IMG", | ||
114 | + "size": 0 | ||
115 | + } | ||
116 | +} | ||
117 | +{ "execute": "x-blockdev-create", | ||
118 | + "arguments": { | ||
119 | + "driver": "$IMGFMT", | ||
120 | + "file": { | ||
121 | + "driver": "file", | ||
122 | + "filename": "$TEST_IMG" | ||
123 | + }, | ||
124 | + "size": $size, | ||
125 | + "cluster-size": 1048576 | ||
126 | + } | ||
127 | +} | ||
128 | +{ "execute": "quit" } | ||
129 | +EOF | ||
130 | + | ||
131 | +_img_info --format-specific | _filter_img_info --format-specific | ||
132 | + | ||
133 | +echo | ||
134 | +echo "=== Successful image creation (with non-default options) ===" | ||
135 | +echo | ||
136 | + | ||
137 | +# Choose a different size to show that we got a new image | ||
138 | +size=$((32 * 1024 * 1024)) | ||
139 | + | ||
140 | +run_qemu <<EOF | ||
141 | +{ "execute": "qmp_capabilities" } | ||
142 | +{ "execute": "x-blockdev-create", | ||
143 | + "arguments": { | ||
144 | + "driver": "file", | ||
145 | + "filename": "$TEST_IMG", | ||
146 | + "size": 0 | ||
147 | + } | ||
148 | +} | ||
149 | +{ "execute": "x-blockdev-create", | ||
150 | + "arguments": { | ||
151 | + "driver": "$IMGFMT", | ||
152 | + "file": { | ||
153 | + "driver": "file", | ||
154 | + "filename": "$TEST_IMG" | ||
155 | + }, | ||
156 | + "size": $size, | ||
157 | + "cluster-size": 65536 | ||
158 | + } | ||
159 | +} | ||
160 | +{ "execute": "quit" } | ||
161 | +EOF | ||
162 | + | ||
163 | +_img_info --format-specific | _filter_img_info --format-specific | ||
164 | + | ||
165 | +echo | ||
166 | +echo "=== Invalid BlockdevRef ===" | ||
167 | +echo | ||
168 | + | ||
169 | +run_qemu <<EOF | ||
170 | +{ "execute": "qmp_capabilities" } | ||
171 | +{ "execute": "x-blockdev-create", | ||
172 | + "arguments": { | ||
173 | + "driver": "$IMGFMT", | ||
174 | + "file": "this doesn't exist", | ||
175 | + "size": $size | ||
176 | + } | ||
177 | +} | ||
178 | +{ "execute": "quit" } | ||
179 | +EOF | ||
180 | + | ||
181 | +echo | ||
182 | +echo "=== Zero size ===" | ||
183 | +echo | ||
184 | + | ||
185 | +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF | ||
186 | +{ "execute": "qmp_capabilities" } | ||
187 | +{ "execute": "x-blockdev-create", | ||
188 | + "arguments": { | ||
189 | + "driver": "$IMGFMT", | ||
190 | + "file": "node0", | ||
191 | + "size": 0 | ||
192 | + } | ||
193 | +} | ||
194 | +{ "execute": "quit" } | ||
195 | +EOF | ||
196 | + | ||
197 | +_img_info | _filter_img_info | ||
198 | + | ||
199 | +echo | ||
200 | +echo "=== Maximum size ===" | ||
201 | +echo | ||
202 | + | ||
203 | +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF | ||
204 | +{ "execute": "qmp_capabilities" } | ||
205 | +{ "execute": "x-blockdev-create", | ||
206 | + "arguments": { | ||
207 | + "driver": "$IMGFMT", | ||
208 | + "file": "node0", | ||
209 | + "size": 4503599627369984 | ||
210 | + } | ||
211 | +} | ||
212 | +{ "execute": "quit" } | ||
213 | +EOF | ||
214 | + | ||
215 | +_img_info | _filter_img_info | ||
216 | + | ||
217 | +echo | ||
218 | +echo "=== Invalid sizes ===" | ||
219 | +echo | ||
220 | + | ||
221 | +# TODO Negative image sizes aren't handled correctly, but this is a problem | ||
222 | +# with QAPI's implementation of the 'size' type and affects other commands as | ||
223 | +# well. Once this is fixed, we may want to add a test case here. | ||
224 | + | ||
225 | +# 1. Misaligned image size | ||
226 | +# 2. 2^64 - 512 | ||
227 | +# 3. 2^63 = 8 EB (qemu-img enforces image sizes less than this) | ||
228 | +# 4. 2^63 - 512 (generally valid, but with the image header the file will | ||
229 | +# exceed 63 bits) | ||
230 | +# 5. 2^52 (512 bytes more than maximum image size) | ||
231 | + | ||
232 | +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF | ||
233 | +{ "execute": "qmp_capabilities" } | ||
234 | +{ "execute": "x-blockdev-create", | ||
235 | + "arguments": { | ||
236 | + "driver": "$IMGFMT", | ||
237 | + "file": "node0", | ||
238 | + "size": 1234 | ||
239 | + } | ||
240 | +} | ||
241 | +{ "execute": "x-blockdev-create", | ||
242 | + "arguments": { | ||
243 | + "driver": "$IMGFMT", | ||
244 | + "file": "node0", | ||
245 | + "size": 18446744073709551104 | ||
246 | + } | ||
247 | +} | ||
248 | +{ "execute": "x-blockdev-create", | ||
249 | + "arguments": { | ||
250 | + "driver": "$IMGFMT", | ||
251 | + "file": "node0", | ||
252 | + "size": 9223372036854775808 | ||
253 | + } | ||
254 | +} | ||
255 | +{ "execute": "x-blockdev-create", | ||
256 | + "arguments": { | ||
257 | + "driver": "$IMGFMT", | ||
258 | + "file": "node0", | ||
259 | + "size": 9223372036854775296 | ||
260 | + } | ||
261 | +} | ||
262 | +{ "execute": "x-blockdev-create", | ||
263 | + "arguments": { | ||
264 | + "driver": "$IMGFMT", | ||
265 | + "file": "node0", | ||
266 | + "size": 4503599627370497 | ||
267 | + } | ||
268 | +} | ||
269 | +{ "execute": "quit" } | ||
270 | +EOF | ||
271 | + | ||
272 | +echo | ||
273 | +echo "=== Invalid cluster size ===" | ||
274 | +echo | ||
275 | + | ||
276 | +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF | ||
277 | +{ "execute": "qmp_capabilities" } | ||
278 | +{ "execute": "x-blockdev-create", | ||
279 | + "arguments": { | ||
280 | + "driver": "$IMGFMT", | ||
281 | + "file": "node0", | ||
282 | + "size": 67108864, | ||
283 | + "cluster-size": 1234 | ||
284 | + } | ||
285 | +} | ||
286 | +{ "execute": "x-blockdev-create", | ||
287 | + "arguments": { | ||
288 | + "driver": "$IMGFMT", | ||
289 | + "file": "node0", | ||
290 | + "size": 67108864, | ||
291 | + "cluster-size": 128 | ||
292 | + } | ||
293 | +} | ||
294 | +{ "execute": "x-blockdev-create", | ||
295 | + "arguments": { | ||
296 | + "driver": "$IMGFMT", | ||
297 | + "file": "node0", | ||
298 | + "size": 67108864, | ||
299 | + "cluster-size": 4294967296 | ||
300 | + } | ||
301 | +} | ||
302 | +{ "execute": "x-blockdev-create", | ||
303 | + "arguments": { | ||
304 | + "driver": "$IMGFMT", | ||
305 | + "file": "node0", | ||
306 | + "size": 67108864, | ||
307 | + "cluster-size": 9223372036854775808 | ||
308 | + } | ||
309 | +} | ||
310 | +{ "execute": "x-blockdev-create", | ||
311 | + "arguments": { | ||
312 | + "driver": "$IMGFMT", | ||
313 | + "file": "node0", | ||
314 | + "size": 67108864, | ||
315 | + "cluster-size": 18446744073709551104 | ||
316 | + } | ||
317 | +} | ||
318 | +{ "execute": "x-blockdev-create", | ||
319 | + "arguments": { | ||
320 | + "driver": "$IMGFMT", | ||
321 | + "file": "node0", | ||
322 | + "size": 67108864, | ||
323 | + "cluster-size": 0 | ||
324 | + } | ||
325 | +} | ||
326 | +{ "execute": "x-blockdev-create", | ||
327 | + "arguments": { | ||
328 | + "driver": "$IMGFMT", | ||
329 | + "file": "node0", | ||
330 | + "size": 281474976710656, | ||
331 | + "cluster-size": 512 | ||
332 | + } | ||
333 | +} | ||
334 | +{ "execute": "quit" } | ||
335 | +EOF | ||
336 | + | ||
337 | + | ||
338 | +# success, all done | ||
339 | +echo "*** done" | ||
340 | +rm -f $seq.full | ||
341 | +status=0 | ||
342 | diff --git a/tests/qemu-iotests/212.out b/tests/qemu-iotests/212.out | ||
343 | new file mode 100644 | ||
344 | index XXXXXXX..XXXXXXX | ||
345 | --- /dev/null | ||
346 | +++ b/tests/qemu-iotests/212.out | ||
347 | @@ -XXX,XX +XXX,XX @@ | ||
348 | +QA output created by 212 | ||
349 | + | ||
350 | +=== Successful image creation (defaults) === | ||
351 | + | ||
352 | +Testing: | ||
353 | +QMP_VERSION | ||
354 | +{"return": {}} | ||
355 | +{"return": {}} | ||
356 | +{"return": {}} | ||
357 | +{"return": {}} | ||
358 | +{"return": {}} | ||
359 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
360 | + | ||
361 | +image: TEST_DIR/t.IMGFMT | ||
362 | +file format: IMGFMT | ||
363 | +virtual size: 128M (134217728 bytes) | ||
364 | + | ||
365 | +=== Successful image creation (explicit defaults) === | ||
366 | + | ||
367 | +Testing: | ||
368 | +QMP_VERSION | ||
369 | +{"return": {}} | ||
370 | +{"return": {}} | ||
371 | +{"return": {}} | ||
372 | +{"return": {}} | ||
373 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
374 | + | ||
375 | +image: TEST_DIR/t.IMGFMT | ||
376 | +file format: IMGFMT | ||
377 | +virtual size: 64M (67108864 bytes) | ||
378 | + | ||
379 | +=== Successful image creation (with non-default options) === | ||
380 | + | ||
381 | +Testing: | ||
382 | +QMP_VERSION | ||
383 | +{"return": {}} | ||
384 | +{"return": {}} | ||
385 | +{"return": {}} | ||
386 | +{"return": {}} | ||
387 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
388 | + | ||
389 | +image: TEST_DIR/t.IMGFMT | ||
390 | +file format: IMGFMT | ||
391 | +virtual size: 32M (33554432 bytes) | ||
392 | + | ||
393 | +=== Invalid BlockdevRef === | ||
394 | + | ||
395 | +Testing: | ||
396 | +QMP_VERSION | ||
397 | +{"return": {}} | ||
398 | +{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}} | ||
399 | +{"return": {}} | ||
400 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
401 | + | ||
402 | + | ||
403 | +=== Zero size === | ||
404 | + | ||
405 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 | ||
406 | +QMP_VERSION | ||
407 | +{"return": {}} | ||
408 | +{"return": {}} | ||
409 | +{"return": {}} | ||
410 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
411 | + | ||
412 | +image: TEST_DIR/t.IMGFMT | ||
413 | +file format: IMGFMT | ||
414 | +virtual size: 0 (0 bytes) | ||
415 | + | ||
416 | +=== Maximum size === | ||
417 | + | ||
418 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 | ||
419 | +QMP_VERSION | ||
420 | +{"return": {}} | ||
421 | +{"return": {}} | ||
422 | +{"return": {}} | ||
423 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
424 | + | ||
425 | +image: TEST_DIR/t.IMGFMT | ||
426 | +file format: IMGFMT | ||
427 | +virtual size: 4096T (4503599627369984 bytes) | ||
428 | + | ||
429 | +=== Invalid sizes === | ||
430 | + | ||
431 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 | ||
432 | +QMP_VERSION | ||
433 | +{"return": {}} | ||
434 | +{"error": {"class": "GenericError", "desc": "Image size must be a multiple of 512 bytes"}} | ||
435 | +{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}} | ||
436 | +{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}} | ||
437 | +{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}} | ||
438 | +{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}} | ||
439 | +{"return": {}} | ||
440 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
441 | + | ||
442 | + | ||
443 | +=== Invalid cluster size === | ||
444 | + | ||
445 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 | ||
446 | +QMP_VERSION | ||
447 | +{"return": {}} | ||
448 | +{"error": {"class": "GenericError", "desc": "Cluster size must be a multiple of 512 bytes"}} | ||
449 | +{"error": {"class": "GenericError", "desc": "Cluster size must be a multiple of 512 bytes"}} | ||
450 | +{"error": {"class": "GenericError", "desc": "Cluster size is too large"}} | ||
451 | +{"error": {"class": "GenericError", "desc": "Cluster size is too large"}} | ||
452 | +{"error": {"class": "GenericError", "desc": "Cluster size is too large"}} | ||
453 | +{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}} | ||
454 | +{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}} | ||
455 | +{"return": {}} | ||
456 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
457 | + | ||
458 | +*** done | ||
459 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group | ||
12 | index XXXXXXX..XXXXXXX 100644 | 460 | index XXXXXXX..XXXXXXX 100644 |
13 | --- a/qemu-img.texi | 461 | --- a/tests/qemu-iotests/group |
14 | +++ b/qemu-img.texi | 462 | +++ b/tests/qemu-iotests/group |
15 | @@ -XXX,XX +XXX,XX @@ exclusive with the @var{-O} parameters. It is currently required to also use | 463 | @@ -XXX,XX +XXX,XX @@ |
16 | the @var{-n} parameter to skip image creation. This restriction may be relaxed | 464 | 209 rw auto quick |
17 | in a future release. | 465 | 210 rw auto |
18 | 466 | 211 rw auto quick | |
19 | +@item --force-share (-U) | 467 | +212 rw auto quick |
20 | + | ||
21 | +If specified, @code{qemu-img} will open the image with shared permissions, | ||
22 | +which makes it less likely to conflict with a running guest's permissions due | ||
23 | +to image locking. For example, this can be used to get the image information | ||
24 | +(with 'info' subcommand) when the image is used by a running guest. Note that | ||
25 | +this could produce inconsistent results because of concurrent metadata changes, | ||
26 | +etc. This option is only allowed when opening images in read-only mode. | ||
27 | + | ||
28 | @item fmt | ||
29 | is the disk image format. It is guessed automatically in most cases. See below | ||
30 | for a description of the supported disk formats. | ||
31 | -- | 468 | -- |
32 | 2.13.6 | 469 | 2.13.6 |
33 | 470 | ||
34 | 471 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | Images with a non-power-of-two block size are invalid and cannot be |
---|---|---|---|
2 | opened. Reject such block sizes when creating an image. | ||
2 | 3 | ||
3 | VPC has some difficulty creating geometries of particular size. | 4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
4 | However, we can indeed force it to use a literal one, so let's | 5 | Reviewed-by: Eric Blake <eblake@redhat.com> |
5 | do that for the sake of test 197, which is testing some specific | 6 | Reviewed-by: Jeff Cody <jcody@redhat.com> |
6 | offsets. | 7 | --- |
8 | block/vhdx.c | 4 ++++ | ||
9 | 1 file changed, 4 insertions(+) | ||
7 | 10 | ||
8 | Signed-off-by: John Snow <jsnow@redhat.com> | 11 | diff --git a/block/vhdx.c b/block/vhdx.c |
9 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
10 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
12 | Reviewed-by: Lukáš Doktor <ldoktor@redhat.com> | ||
13 | --- | ||
14 | tests/qemu-iotests/197 | 4 ++++ | ||
15 | tests/qemu-iotests/common.filter | 3 ++- | ||
16 | 2 files changed, 6 insertions(+), 1 deletion(-) | ||
17 | |||
18 | diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 | ||
19 | index XXXXXXX..XXXXXXX 100755 | ||
20 | --- a/tests/qemu-iotests/197 | ||
21 | +++ b/tests/qemu-iotests/197 | ||
22 | @@ -XXX,XX +XXX,XX @@ echo '=== Copy-on-read ===' | ||
23 | echo | ||
24 | |||
25 | # Prep the images | ||
26 | +# VPC rounds image sizes to a specific geometry, force a specific size. | ||
27 | +if [ "$IMGFMT" = "vpc" ]; then | ||
28 | + IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size") | ||
29 | +fi | ||
30 | _make_test_img 4G | ||
31 | $QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io | ||
32 | IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \ | ||
33 | diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter | ||
34 | index XXXXXXX..XXXXXXX 100644 | 12 | index XXXXXXX..XXXXXXX 100644 |
35 | --- a/tests/qemu-iotests/common.filter | 13 | --- a/block/vhdx.c |
36 | +++ b/tests/qemu-iotests/common.filter | 14 | +++ b/block/vhdx.c |
37 | @@ -XXX,XX +XXX,XX @@ _filter_img_create() | 15 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, |
38 | -e "s# log_size=[0-9]\\+##g" \ | 16 | error_setg_errno(errp, EINVAL, "Block size must be a multiple of 1 MB"); |
39 | -e "s# refcount_bits=[0-9]\\+##g" \ | 17 | return -EINVAL; |
40 | -e "s# key-secret=[a-zA-Z0-9]\\+##g" \ | 18 | } |
41 | - -e "s# iter-time=[0-9]\\+##g" | 19 | + if (!is_power_of_2(block_size)) { |
42 | + -e "s# iter-time=[0-9]\\+##g" \ | 20 | + error_setg(errp, "Block size must be a power of two"); |
43 | + -e "s# force_size=\\(on\\|off\\)##g" | 21 | + return -EINVAL; |
44 | } | 22 | + } |
45 | 23 | if (block_size > VHDX_BLOCK_SIZE_MAX) { | |
46 | _filter_img_info() | 24 | error_setg_errno(errp, EINVAL, "Block size must not exceed %d", |
25 | VHDX_BLOCK_SIZE_MAX); | ||
47 | -- | 26 | -- |
48 | 2.13.6 | 27 | 2.13.6 |
49 | 28 | ||
50 | 29 | diff view generated by jsdifflib |
1 | Drain requests are propagated to child nodes, parent nodes and directly | 1 | error_setg_errno() is meant for cases where we got an errno from the OS |
---|---|---|---|
2 | to the AioContext. The order in which this happened was different | 2 | that can add useful extra information to an error message. It's |
3 | between all combinations of drain/drain_all and begin/end. | 3 | pointless if we pass a constant errno, these cases should use plain |
4 | 4 | error_setg(). | |
5 | The correct order is to keep children only drained when their parents | ||
6 | are also drained. This means that at the start of a drained section, the | ||
7 | AioContext needs to be drained first, the parents second and only then | ||
8 | the children. The correct order for the end of a drained section is the | ||
9 | opposite. | ||
10 | |||
11 | This patch changes the three other functions to follow the example of | ||
12 | bdrv_drained_begin(), which is the only one that got it right. | ||
13 | 5 | ||
14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 6 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
15 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 7 | Reviewed-by: Eric Blake <eblake@redhat.com> |
8 | Reviewed-by: Jeff Cody <jcody@redhat.com> | ||
16 | --- | 9 | --- |
17 | block/io.c | 12 ++++++++---- | 10 | block/vhdx.c | 9 ++++----- |
18 | 1 file changed, 8 insertions(+), 4 deletions(-) | 11 | 1 file changed, 4 insertions(+), 5 deletions(-) |
19 | 12 | ||
20 | diff --git a/block/io.c b/block/io.c | 13 | diff --git a/block/vhdx.c b/block/vhdx.c |
21 | index XXXXXXX..XXXXXXX 100644 | 14 | index XXXXXXX..XXXXXXX 100644 |
22 | --- a/block/io.c | 15 | --- a/block/vhdx.c |
23 | +++ b/block/io.c | 16 | +++ b/block/vhdx.c |
24 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs) | 17 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, |
25 | return; | 18 | /* Validate options and set default values */ |
19 | image_size = vhdx_opts->size; | ||
20 | if (image_size > VHDX_MAX_IMAGE_SIZE) { | ||
21 | - error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB"); | ||
22 | + error_setg(errp, "Image size too large; max of 64TB"); | ||
23 | return -EINVAL; | ||
26 | } | 24 | } |
27 | 25 | ||
28 | + /* Stop things in parent-to-child order */ | 26 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, |
29 | if (atomic_fetch_inc(&bs->quiesce_counter) == 0) { | 27 | log_size = vhdx_opts->log_size; |
30 | aio_disable_external(bdrv_get_aio_context(bs)); | ||
31 | bdrv_parent_drained_begin(bs); | ||
32 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs) | ||
33 | return; | ||
34 | } | 28 | } |
35 | 29 | if (log_size < MiB || (log_size % MiB) != 0) { | |
36 | - bdrv_parent_drained_end(bs); | 30 | - error_setg_errno(errp, EINVAL, "Log size must be a multiple of 1 MB"); |
37 | + /* Re-enable things in child-to-parent order */ | 31 | + error_setg(errp, "Log size must be a multiple of 1 MB"); |
38 | bdrv_drain_invoke(bs, false); | 32 | return -EINVAL; |
39 | + bdrv_parent_drained_end(bs); | ||
40 | aio_enable_external(bdrv_get_aio_context(bs)); | ||
41 | } | ||
42 | |||
43 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) | ||
44 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
45 | AioContext *aio_context = bdrv_get_aio_context(bs); | ||
46 | |||
47 | + /* Stop things in parent-to-child order */ | ||
48 | aio_context_acquire(aio_context); | ||
49 | - bdrv_parent_drained_begin(bs); | ||
50 | aio_disable_external(aio_context); | ||
51 | + bdrv_parent_drained_begin(bs); | ||
52 | bdrv_drain_invoke(bs, true); | ||
53 | aio_context_release(aio_context); | ||
54 | |||
55 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) | ||
56 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
57 | AioContext *aio_context = bdrv_get_aio_context(bs); | ||
58 | |||
59 | + /* Re-enable things in child-to-parent order */ | ||
60 | aio_context_acquire(aio_context); | ||
61 | - aio_enable_external(aio_context); | ||
62 | - bdrv_parent_drained_end(bs); | ||
63 | bdrv_drain_invoke(bs, false); | ||
64 | + bdrv_parent_drained_end(bs); | ||
65 | + aio_enable_external(aio_context); | ||
66 | aio_context_release(aio_context); | ||
67 | } | 33 | } |
34 | |||
35 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, | ||
36 | } | ||
37 | |||
38 | if (block_size < MiB || (block_size % MiB) != 0) { | ||
39 | - error_setg_errno(errp, EINVAL, "Block size must be a multiple of 1 MB"); | ||
40 | + error_setg(errp, "Block size must be a multiple of 1 MB"); | ||
41 | return -EINVAL; | ||
42 | } | ||
43 | if (!is_power_of_2(block_size)) { | ||
44 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, | ||
45 | return -EINVAL; | ||
46 | } | ||
47 | if (block_size > VHDX_BLOCK_SIZE_MAX) { | ||
48 | - error_setg_errno(errp, EINVAL, "Block size must not exceed %d", | ||
49 | - VHDX_BLOCK_SIZE_MAX); | ||
50 | + error_setg(errp, "Block size must not exceed %d", VHDX_BLOCK_SIZE_MAX); | ||
51 | return -EINVAL; | ||
52 | } | ||
68 | 53 | ||
69 | -- | 54 | -- |
70 | 2.13.6 | 55 | 2.13.6 |
71 | 56 | ||
72 | 57 | diff view generated by jsdifflib |
1 | Now that the bdrv_drain_invoke() calls are pulled up to the callers of | 1 | It's unclear what the real maximum is, but we use an uint32_t to store |
---|---|---|---|
2 | bdrv_drain_recurse(), the 'begin' parameter isn't needed any more. | 2 | the log size in vhdx_co_create(), so we should check that the given |
3 | value fits in 32 bits. | ||
3 | 4 | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 5 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
5 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | 6 | Reviewed-by: Eric Blake <eblake@redhat.com> |
7 | Reviewed-by: Jeff Cody <jcody@redhat.com> | ||
6 | --- | 8 | --- |
7 | block/io.c | 12 ++++++------ | 9 | block/vhdx.c | 4 ++++ |
8 | 1 file changed, 6 insertions(+), 6 deletions(-) | 10 | 1 file changed, 4 insertions(+) |
9 | 11 | ||
10 | diff --git a/block/io.c b/block/io.c | 12 | diff --git a/block/vhdx.c b/block/vhdx.c |
11 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
12 | --- a/block/io.c | 14 | --- a/block/vhdx.c |
13 | +++ b/block/io.c | 15 | +++ b/block/vhdx.c |
14 | @@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) | 16 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, |
17 | if (!vhdx_opts->has_log_size) { | ||
18 | log_size = DEFAULT_LOG_SIZE; | ||
19 | } else { | ||
20 | + if (vhdx_opts->log_size > UINT32_MAX) { | ||
21 | + error_setg(errp, "Log size must be smaller than 4 GB"); | ||
22 | + return -EINVAL; | ||
23 | + } | ||
24 | log_size = vhdx_opts->log_size; | ||
15 | } | 25 | } |
16 | } | 26 | if (log_size < MiB || (log_size % MiB) != 0) { |
17 | |||
18 | -static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) | ||
19 | +static bool bdrv_drain_recurse(BlockDriverState *bs) | ||
20 | { | ||
21 | BdrvChild *child, *tmp; | ||
22 | bool waited; | ||
23 | @@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) | ||
24 | */ | ||
25 | bdrv_ref(bs); | ||
26 | } | ||
27 | - waited |= bdrv_drain_recurse(bs, begin); | ||
28 | + waited |= bdrv_drain_recurse(bs); | ||
29 | if (in_main_loop) { | ||
30 | bdrv_unref(bs); | ||
31 | } | ||
32 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs) | ||
33 | } | ||
34 | |||
35 | bdrv_drain_invoke(bs, true); | ||
36 | - bdrv_drain_recurse(bs, true); | ||
37 | + bdrv_drain_recurse(bs); | ||
38 | } | ||
39 | |||
40 | void bdrv_drained_end(BlockDriverState *bs) | ||
41 | @@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs) | ||
42 | |||
43 | bdrv_parent_drained_end(bs); | ||
44 | bdrv_drain_invoke(bs, false); | ||
45 | - bdrv_drain_recurse(bs, false); | ||
46 | + bdrv_drain_recurse(bs); | ||
47 | aio_enable_external(bdrv_get_aio_context(bs)); | ||
48 | } | ||
49 | |||
50 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void) | ||
51 | aio_context_acquire(aio_context); | ||
52 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ||
53 | if (aio_context == bdrv_get_aio_context(bs)) { | ||
54 | - waited |= bdrv_drain_recurse(bs, true); | ||
55 | + waited |= bdrv_drain_recurse(bs); | ||
56 | } | ||
57 | } | ||
58 | aio_context_release(aio_context); | ||
59 | @@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void) | ||
60 | aio_enable_external(aio_context); | ||
61 | bdrv_parent_drained_end(bs); | ||
62 | bdrv_drain_invoke(bs, false); | ||
63 | - bdrv_drain_recurse(bs, false); | ||
64 | + bdrv_drain_recurse(bs); | ||
65 | aio_context_release(aio_context); | ||
66 | } | ||
67 | |||
68 | -- | 27 | -- |
69 | 2.13.6 | 28 | 2.13.6 |
70 | 29 | ||
71 | 30 | diff view generated by jsdifflib |
1 | From: Mao Zhongyi <maozy.fnst@cn.fujitsu.com> | ||
---|---|---|---|
2 | |||
3 | Convert nvme_init() to realize and rename it to nvme_realize(). | ||
4 | |||
5 | Cc: John Snow <jsnow@redhat.com> | ||
6 | Cc: Keith Busch <keith.busch@intel.com> | ||
7 | Cc: Kevin Wolf <kwolf@redhat.com> | ||
8 | Cc: Max Reitz <mreitz@redhat.com> | ||
9 | Cc: Markus Armbruster <armbru@redhat.com> | ||
10 | |||
11 | Signed-off-by: Mao Zhongyi <maozy.fnst@cn.fujitsu.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 1 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
13 | --- | 2 | --- |
14 | hw/block/nvme.c | 18 ++++++++++-------- | 3 | tests/qemu-iotests/213 | 349 +++++++++++++++++++++++++++++++++++++++++++++ |
15 | 1 file changed, 10 insertions(+), 8 deletions(-) | 4 | tests/qemu-iotests/213.out | 121 ++++++++++++++++ |
5 | tests/qemu-iotests/group | 1 + | ||
6 | 3 files changed, 471 insertions(+) | ||
7 | create mode 100755 tests/qemu-iotests/213 | ||
8 | create mode 100644 tests/qemu-iotests/213.out | ||
16 | 9 | ||
17 | diff --git a/hw/block/nvme.c b/hw/block/nvme.c | 10 | diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213 |
11 | new file mode 100755 | ||
12 | index XXXXXXX..XXXXXXX | ||
13 | --- /dev/null | ||
14 | +++ b/tests/qemu-iotests/213 | ||
15 | @@ -XXX,XX +XXX,XX @@ | ||
16 | +#!/bin/bash | ||
17 | +# | ||
18 | +# Test vhdx and file image creation | ||
19 | +# | ||
20 | +# Copyright (C) 2018 Red Hat, Inc. | ||
21 | +# | ||
22 | +# This program is free software; you can redistribute it and/or modify | ||
23 | +# it under the terms of the GNU General Public License as published by | ||
24 | +# the Free Software Foundation; either version 2 of the License, or | ||
25 | +# (at your option) any later version. | ||
26 | +# | ||
27 | +# This program is distributed in the hope that it will be useful, | ||
28 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
29 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
30 | +# GNU General Public License for more details. | ||
31 | +# | ||
32 | +# You should have received a copy of the GNU General Public License | ||
33 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
34 | +# | ||
35 | + | ||
36 | +# creator | ||
37 | +owner=kwolf@redhat.com | ||
38 | + | ||
39 | +seq=`basename $0` | ||
40 | +echo "QA output created by $seq" | ||
41 | + | ||
42 | +here=`pwd` | ||
43 | +status=1 # failure is the default! | ||
44 | + | ||
45 | +# get standard environment, filters and checks | ||
46 | +. ./common.rc | ||
47 | +. ./common.filter | ||
48 | + | ||
49 | +_supported_fmt vhdx | ||
50 | +_supported_proto file | ||
51 | +_supported_os Linux | ||
52 | + | ||
53 | +function do_run_qemu() | ||
54 | +{ | ||
55 | + echo Testing: "$@" | ||
56 | + $QEMU -nographic -qmp stdio -serial none "$@" | ||
57 | + echo | ||
58 | +} | ||
59 | + | ||
60 | +function run_qemu() | ||
61 | +{ | ||
62 | + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ | ||
63 | + | _filter_qemu | _filter_imgfmt \ | ||
64 | + | _filter_actual_image_size | ||
65 | +} | ||
66 | + | ||
67 | +echo | ||
68 | +echo "=== Successful image creation (defaults) ===" | ||
69 | +echo | ||
70 | + | ||
71 | +size=$((128 * 1024 * 1024)) | ||
72 | + | ||
73 | +run_qemu <<EOF | ||
74 | +{ "execute": "qmp_capabilities" } | ||
75 | +{ "execute": "x-blockdev-create", | ||
76 | + "arguments": { | ||
77 | + "driver": "file", | ||
78 | + "filename": "$TEST_IMG", | ||
79 | + "size": 0 | ||
80 | + } | ||
81 | +} | ||
82 | +{ "execute": "blockdev-add", | ||
83 | + "arguments": { | ||
84 | + "driver": "file", | ||
85 | + "node-name": "imgfile", | ||
86 | + "filename": "$TEST_IMG" | ||
87 | + } | ||
88 | +} | ||
89 | +{ "execute": "x-blockdev-create", | ||
90 | + "arguments": { | ||
91 | + "driver": "$IMGFMT", | ||
92 | + "file": "imgfile", | ||
93 | + "size": $size | ||
94 | + } | ||
95 | +} | ||
96 | +{ "execute": "quit" } | ||
97 | +EOF | ||
98 | + | ||
99 | +_img_info --format-specific | _filter_img_info --format-specific | ||
100 | + | ||
101 | +echo | ||
102 | +echo "=== Successful image creation (explicit defaults) ===" | ||
103 | +echo | ||
104 | + | ||
105 | +# Choose a different size to show that we got a new image | ||
106 | +size=$((64 * 1024 * 1024)) | ||
107 | + | ||
108 | +run_qemu <<EOF | ||
109 | +{ "execute": "qmp_capabilities" } | ||
110 | +{ "execute": "x-blockdev-create", | ||
111 | + "arguments": { | ||
112 | + "driver": "file", | ||
113 | + "filename": "$TEST_IMG", | ||
114 | + "size": 0 | ||
115 | + } | ||
116 | +} | ||
117 | +{ "execute": "x-blockdev-create", | ||
118 | + "arguments": { | ||
119 | + "driver": "$IMGFMT", | ||
120 | + "file": { | ||
121 | + "driver": "file", | ||
122 | + "filename": "$TEST_IMG" | ||
123 | + }, | ||
124 | + "size": $size, | ||
125 | + "log-size": 1048576, | ||
126 | + "block-size": 8388608, | ||
127 | + "subformat": "dynamic", | ||
128 | + "block-state-zero": true | ||
129 | + } | ||
130 | +} | ||
131 | +{ "execute": "quit" } | ||
132 | +EOF | ||
133 | + | ||
134 | +_img_info --format-specific | _filter_img_info --format-specific | ||
135 | + | ||
136 | +echo | ||
137 | +echo "=== Successful image creation (with non-default options) ===" | ||
138 | +echo | ||
139 | + | ||
140 | +# Choose a different size to show that we got a new image | ||
141 | +size=$((32 * 1024 * 1024)) | ||
142 | + | ||
143 | +run_qemu <<EOF | ||
144 | +{ "execute": "qmp_capabilities" } | ||
145 | +{ "execute": "x-blockdev-create", | ||
146 | + "arguments": { | ||
147 | + "driver": "file", | ||
148 | + "filename": "$TEST_IMG", | ||
149 | + "size": 0 | ||
150 | + } | ||
151 | +} | ||
152 | +{ "execute": "x-blockdev-create", | ||
153 | + "arguments": { | ||
154 | + "driver": "$IMGFMT", | ||
155 | + "file": { | ||
156 | + "driver": "file", | ||
157 | + "filename": "$TEST_IMG" | ||
158 | + }, | ||
159 | + "size": $size, | ||
160 | + "log-size": 8388608, | ||
161 | + "block-size": 268435456, | ||
162 | + "subformat": "fixed", | ||
163 | + "block-state-zero": false | ||
164 | + } | ||
165 | +} | ||
166 | +{ "execute": "quit" } | ||
167 | +EOF | ||
168 | + | ||
169 | +_img_info --format-specific | _filter_img_info --format-specific | ||
170 | + | ||
171 | +echo | ||
172 | +echo "=== Invalid BlockdevRef ===" | ||
173 | +echo | ||
174 | + | ||
175 | +run_qemu <<EOF | ||
176 | +{ "execute": "qmp_capabilities" } | ||
177 | +{ "execute": "x-blockdev-create", | ||
178 | + "arguments": { | ||
179 | + "driver": "$IMGFMT", | ||
180 | + "file": "this doesn't exist", | ||
181 | + "size": $size | ||
182 | + } | ||
183 | +} | ||
184 | +{ "execute": "quit" } | ||
185 | +EOF | ||
186 | + | ||
187 | +echo | ||
188 | +echo "=== Zero size ===" | ||
189 | +echo | ||
190 | + | ||
191 | +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF | ||
192 | +{ "execute": "qmp_capabilities" } | ||
193 | +{ "execute": "x-blockdev-create", | ||
194 | + "arguments": { | ||
195 | + "driver": "$IMGFMT", | ||
196 | + "file": "node0", | ||
197 | + "size": 0 | ||
198 | + } | ||
199 | +} | ||
200 | +{ "execute": "quit" } | ||
201 | +EOF | ||
202 | + | ||
203 | +_img_info | _filter_img_info | ||
204 | + | ||
205 | +echo | ||
206 | +echo "=== Maximum size ===" | ||
207 | +echo | ||
208 | + | ||
209 | +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF | ||
210 | +{ "execute": "qmp_capabilities" } | ||
211 | +{ "execute": "x-blockdev-create", | ||
212 | + "arguments": { | ||
213 | + "driver": "$IMGFMT", | ||
214 | + "file": "node0", | ||
215 | + "size": 70368744177664 | ||
216 | + } | ||
217 | +} | ||
218 | +{ "execute": "quit" } | ||
219 | +EOF | ||
220 | + | ||
221 | +_img_info | _filter_img_info | ||
222 | + | ||
223 | +echo | ||
224 | +echo "=== Invalid sizes ===" | ||
225 | +echo | ||
226 | + | ||
227 | +# TODO Negative image sizes aren't handled correctly, but this is a problem | ||
228 | +# with QAPI's implementation of the 'size' type and affects other commands as | ||
229 | +# well. Once this is fixed, we may want to add a test case here. | ||
230 | + | ||
231 | +# 1. 2^64 - 512 | ||
232 | +# 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this) | ||
233 | +# 3. 2^63 - 512 (generally valid, but with the image header the file will | ||
234 | +# exceed 63 bits) | ||
235 | +# 4. 2^46 + 1 (one byte more than maximum image size) | ||
236 | + | ||
237 | +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF | ||
238 | +{ "execute": "qmp_capabilities" } | ||
239 | +{ "execute": "x-blockdev-create", | ||
240 | + "arguments": { | ||
241 | + "driver": "$IMGFMT", | ||
242 | + "file": "node0", | ||
243 | + "size": 18446744073709551104 | ||
244 | + } | ||
245 | +} | ||
246 | +{ "execute": "x-blockdev-create", | ||
247 | + "arguments": { | ||
248 | + "driver": "$IMGFMT", | ||
249 | + "file": "node0", | ||
250 | + "size": 9223372036854775808 | ||
251 | + } | ||
252 | +} | ||
253 | +{ "execute": "x-blockdev-create", | ||
254 | + "arguments": { | ||
255 | + "driver": "$IMGFMT", | ||
256 | + "file": "node0", | ||
257 | + "size": 9223372036854775296 | ||
258 | + } | ||
259 | +} | ||
260 | +{ "execute": "x-blockdev-create", | ||
261 | + "arguments": { | ||
262 | + "driver": "$IMGFMT", | ||
263 | + "file": "node0", | ||
264 | + "size": 70368744177665 | ||
265 | + } | ||
266 | +} | ||
267 | +{ "execute": "quit" } | ||
268 | +EOF | ||
269 | + | ||
270 | +echo | ||
271 | +echo "=== Invalid block size ===" | ||
272 | +echo | ||
273 | + | ||
274 | +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF | ||
275 | +{ "execute": "qmp_capabilities" } | ||
276 | +{ "execute": "x-blockdev-create", | ||
277 | + "arguments": { | ||
278 | + "driver": "$IMGFMT", | ||
279 | + "file": "node0", | ||
280 | + "size": 67108864, | ||
281 | + "block-size": 1234567 | ||
282 | + } | ||
283 | +} | ||
284 | +{ "execute": "x-blockdev-create", | ||
285 | + "arguments": { | ||
286 | + "driver": "$IMGFMT", | ||
287 | + "file": "node0", | ||
288 | + "size": 67108864, | ||
289 | + "block-size": 128 | ||
290 | + } | ||
291 | +} | ||
292 | +{ "execute": "x-blockdev-create", | ||
293 | + "arguments": { | ||
294 | + "driver": "$IMGFMT", | ||
295 | + "file": "node0", | ||
296 | + "size": 67108864, | ||
297 | + "block-size": 3145728 | ||
298 | + } | ||
299 | +} | ||
300 | +{ "execute": "x-blockdev-create", | ||
301 | + "arguments": { | ||
302 | + "driver": "$IMGFMT", | ||
303 | + "file": "node0", | ||
304 | + "size": 67108864, | ||
305 | + "block-size": 536870912 | ||
306 | + } | ||
307 | +} | ||
308 | +{ "execute": "x-blockdev-create", | ||
309 | + "arguments": { | ||
310 | + "driver": "$IMGFMT", | ||
311 | + "file": "node0", | ||
312 | + "size": 67108864, | ||
313 | + "block-size": 0 | ||
314 | + } | ||
315 | +} | ||
316 | +{ "execute": "quit" } | ||
317 | +EOF | ||
318 | + | ||
319 | +echo | ||
320 | +echo "=== Invalid log size ===" | ||
321 | +echo | ||
322 | + | ||
323 | +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF | ||
324 | +{ "execute": "qmp_capabilities" } | ||
325 | +{ "execute": "x-blockdev-create", | ||
326 | + "arguments": { | ||
327 | + "driver": "$IMGFMT", | ||
328 | + "file": "node0", | ||
329 | + "size": 67108864, | ||
330 | + "log-size": 1234567 | ||
331 | + } | ||
332 | +} | ||
333 | +{ "execute": "x-blockdev-create", | ||
334 | + "arguments": { | ||
335 | + "driver": "$IMGFMT", | ||
336 | + "file": "node0", | ||
337 | + "size": 67108864, | ||
338 | + "log-size": 128 | ||
339 | + } | ||
340 | +} | ||
341 | +{ "execute": "x-blockdev-create", | ||
342 | + "arguments": { | ||
343 | + "driver": "$IMGFMT", | ||
344 | + "file": "node0", | ||
345 | + "size": 67108864, | ||
346 | + "log-size": 4294967296 | ||
347 | + } | ||
348 | +} | ||
349 | +{ "execute": "x-blockdev-create", | ||
350 | + "arguments": { | ||
351 | + "driver": "$IMGFMT", | ||
352 | + "file": "node0", | ||
353 | + "size": 67108864, | ||
354 | + "log-size": 0 | ||
355 | + } | ||
356 | +} | ||
357 | +{ "execute": "quit" } | ||
358 | +EOF | ||
359 | + | ||
360 | + | ||
361 | +# success, all done | ||
362 | +echo "*** done" | ||
363 | +rm -f $seq.full | ||
364 | +status=0 | ||
365 | diff --git a/tests/qemu-iotests/213.out b/tests/qemu-iotests/213.out | ||
366 | new file mode 100644 | ||
367 | index XXXXXXX..XXXXXXX | ||
368 | --- /dev/null | ||
369 | +++ b/tests/qemu-iotests/213.out | ||
370 | @@ -XXX,XX +XXX,XX @@ | ||
371 | +QA output created by 213 | ||
372 | + | ||
373 | +=== Successful image creation (defaults) === | ||
374 | + | ||
375 | +Testing: | ||
376 | +QMP_VERSION | ||
377 | +{"return": {}} | ||
378 | +{"return": {}} | ||
379 | +{"return": {}} | ||
380 | +{"return": {}} | ||
381 | +{"return": {}} | ||
382 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
383 | + | ||
384 | +image: TEST_DIR/t.IMGFMT | ||
385 | +file format: IMGFMT | ||
386 | +virtual size: 128M (134217728 bytes) | ||
387 | + | ||
388 | +=== Successful image creation (explicit defaults) === | ||
389 | + | ||
390 | +Testing: | ||
391 | +QMP_VERSION | ||
392 | +{"return": {}} | ||
393 | +{"return": {}} | ||
394 | +{"return": {}} | ||
395 | +{"return": {}} | ||
396 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
397 | + | ||
398 | +image: TEST_DIR/t.IMGFMT | ||
399 | +file format: IMGFMT | ||
400 | +virtual size: 64M (67108864 bytes) | ||
401 | + | ||
402 | +=== Successful image creation (with non-default options) === | ||
403 | + | ||
404 | +Testing: | ||
405 | +QMP_VERSION | ||
406 | +{"return": {}} | ||
407 | +{"return": {}} | ||
408 | +{"return": {}} | ||
409 | +{"return": {}} | ||
410 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
411 | + | ||
412 | +image: TEST_DIR/t.IMGFMT | ||
413 | +file format: IMGFMT | ||
414 | +virtual size: 32M (33554432 bytes) | ||
415 | + | ||
416 | +=== Invalid BlockdevRef === | ||
417 | + | ||
418 | +Testing: | ||
419 | +QMP_VERSION | ||
420 | +{"return": {}} | ||
421 | +{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}} | ||
422 | +{"return": {}} | ||
423 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
424 | + | ||
425 | + | ||
426 | +=== Zero size === | ||
427 | + | ||
428 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 | ||
429 | +QMP_VERSION | ||
430 | +{"return": {}} | ||
431 | +{"return": {}} | ||
432 | +{"return": {}} | ||
433 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
434 | + | ||
435 | +image: TEST_DIR/t.IMGFMT | ||
436 | +file format: IMGFMT | ||
437 | +virtual size: 0 (0 bytes) | ||
438 | + | ||
439 | +=== Maximum size === | ||
440 | + | ||
441 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 | ||
442 | +QMP_VERSION | ||
443 | +{"return": {}} | ||
444 | +{"return": {}} | ||
445 | +{"return": {}} | ||
446 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
447 | + | ||
448 | +image: TEST_DIR/t.IMGFMT | ||
449 | +file format: IMGFMT | ||
450 | +virtual size: 64T (70368744177664 bytes) | ||
451 | + | ||
452 | +=== Invalid sizes === | ||
453 | + | ||
454 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 | ||
455 | +QMP_VERSION | ||
456 | +{"return": {}} | ||
457 | +{"error": {"class": "GenericError", "desc": "Image size too large; max of 64TB"}} | ||
458 | +{"error": {"class": "GenericError", "desc": "Image size too large; max of 64TB"}} | ||
459 | +{"error": {"class": "GenericError", "desc": "Image size too large; max of 64TB"}} | ||
460 | +{"error": {"class": "GenericError", "desc": "Image size too large; max of 64TB"}} | ||
461 | +{"return": {}} | ||
462 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
463 | + | ||
464 | + | ||
465 | +=== Invalid block size === | ||
466 | + | ||
467 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 | ||
468 | +QMP_VERSION | ||
469 | +{"return": {}} | ||
470 | +{"error": {"class": "GenericError", "desc": "Block size must be a multiple of 1 MB"}} | ||
471 | +{"error": {"class": "GenericError", "desc": "Block size must be a multiple of 1 MB"}} | ||
472 | +{"error": {"class": "GenericError", "desc": "Block size must be a power of two"}} | ||
473 | +{"error": {"class": "GenericError", "desc": "Block size must not exceed 268435456"}} | ||
474 | +{"error": {"class": "GenericError", "desc": "Block size must be a multiple of 1 MB"}} | ||
475 | +{"return": {}} | ||
476 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
477 | + | ||
478 | + | ||
479 | +=== Invalid log size === | ||
480 | + | ||
481 | +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 | ||
482 | +QMP_VERSION | ||
483 | +{"return": {}} | ||
484 | +{"error": {"class": "GenericError", "desc": "Log size must be a multiple of 1 MB"}} | ||
485 | +{"error": {"class": "GenericError", "desc": "Log size must be a multiple of 1 MB"}} | ||
486 | +{"error": {"class": "GenericError", "desc": "Log size must be smaller than 4 GB"}} | ||
487 | +{"error": {"class": "GenericError", "desc": "Log size must be a multiple of 1 MB"}} | ||
488 | +{"return": {}} | ||
489 | +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} | ||
490 | + | ||
491 | +*** done | ||
492 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group | ||
18 | index XXXXXXX..XXXXXXX 100644 | 493 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/hw/block/nvme.c | 494 | --- a/tests/qemu-iotests/group |
20 | +++ b/hw/block/nvme.c | 495 | +++ b/tests/qemu-iotests/group |
21 | @@ -XXX,XX +XXX,XX @@ static const MemoryRegionOps nvme_cmb_ops = { | 496 | @@ -XXX,XX +XXX,XX @@ |
22 | }, | 497 | 210 rw auto |
23 | }; | 498 | 211 rw auto quick |
24 | 499 | 212 rw auto quick | |
25 | -static int nvme_init(PCIDevice *pci_dev) | 500 | +213 rw auto quick |
26 | +static void nvme_realize(PCIDevice *pci_dev, Error **errp) | ||
27 | { | ||
28 | NvmeCtrl *n = NVME(pci_dev); | ||
29 | NvmeIdCtrl *id = &n->id_ctrl; | ||
30 | @@ -XXX,XX +XXX,XX @@ static int nvme_init(PCIDevice *pci_dev) | ||
31 | Error *local_err = NULL; | ||
32 | |||
33 | if (!n->conf.blk) { | ||
34 | - return -1; | ||
35 | + error_setg(errp, "drive property not set"); | ||
36 | + return; | ||
37 | } | ||
38 | |||
39 | bs_size = blk_getlength(n->conf.blk); | ||
40 | if (bs_size < 0) { | ||
41 | - return -1; | ||
42 | + error_setg(errp, "could not get backing file size"); | ||
43 | + return; | ||
44 | } | ||
45 | |||
46 | blkconf_serial(&n->conf, &n->serial); | ||
47 | if (!n->serial) { | ||
48 | - return -1; | ||
49 | + error_setg(errp, "serial property not set"); | ||
50 | + return; | ||
51 | } | ||
52 | blkconf_blocksizes(&n->conf); | ||
53 | blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk), | ||
54 | false, &local_err); | ||
55 | if (local_err) { | ||
56 | - error_report_err(local_err); | ||
57 | - return -1; | ||
58 | + error_propagate(errp, local_err); | ||
59 | + return; | ||
60 | } | ||
61 | |||
62 | pci_conf = pci_dev->config; | ||
63 | @@ -XXX,XX +XXX,XX @@ static int nvme_init(PCIDevice *pci_dev) | ||
64 | cpu_to_le64(n->ns_size >> | ||
65 | id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas)].ds); | ||
66 | } | ||
67 | - return 0; | ||
68 | } | ||
69 | |||
70 | static void nvme_exit(PCIDevice *pci_dev) | ||
71 | @@ -XXX,XX +XXX,XX @@ static void nvme_class_init(ObjectClass *oc, void *data) | ||
72 | DeviceClass *dc = DEVICE_CLASS(oc); | ||
73 | PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); | ||
74 | |||
75 | - pc->init = nvme_init; | ||
76 | + pc->realize = nvme_realize; | ||
77 | pc->exit = nvme_exit; | ||
78 | pc->class_id = PCI_CLASS_STORAGE_EXPRESS; | ||
79 | pc->vendor_id = PCI_VENDOR_ID_INTEL; | ||
80 | -- | 501 | -- |
81 | 2.13.6 | 502 | 2.13.6 |
82 | 503 | ||
83 | 504 | diff view generated by jsdifflib |