1
The following changes since commit b98a66201dbc7cf3b962f4bb260f66100cc75578:
1
The following changes since commit ec11dc41eec5142b4776db1296972c6323ba5847:
2
2
3
Merge remote-tracking branch 'remotes/palmer/tags/riscv-for-master-4.0-rc0-2' into staging (2019-03-19 12:55:02 +0000)
3
Merge tag 'pull-misc-2022-05-11' of git://repo.or.cz/qemu/armbru into staging (2022-05-11 09:00:26 -0700)
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 59fba0aaee7438002d9803a86c888f21a1070cc8:
9
for you to fetch changes up to f70625299ecc9ba577c87f3d1d75012c747c7d88:
10
10
11
qemu-iotests: Treat custom TEST_DIR in 051 (2019-03-19 15:51:31 +0100)
11
qemu-iotests: inline common.config into common.rc (2022-05-12 15:42:49 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches
15
15
16
- mirror: Fix early return from drain (could cause deadlocks)
16
- coroutine: Fix crashes due to too large pool batch size
17
- vmdk: Fixed probing for version 3 images
17
- fdc: Prevent end-of-track overrun
18
- vl: Fix to create migration object before block backends again (fixes
18
- nbd: MULTI_CONN for shared writable exports
19
segfault for block drivers that set migration blockers)
19
- iotests test runner improvements
20
- Several minor fixes, documentation and test case improvements
21
20
22
----------------------------------------------------------------
21
----------------------------------------------------------------
23
Alberto Garcia (1):
22
Daniel P. Berrangé (2):
24
block: Make bdrv_{copy_on_read,crypto_luks,replication} static
23
tests/qemu-iotests: print intent to run a test in TAP mode
24
.gitlab-ci.d: export meson testlog.txt as an artifact
25
25
26
Kevin Wolf (3):
26
Eric Blake (2):
27
qcow2: Fix data file error condition in qcow2_co_create()
27
qemu-nbd: Pass max connections to blockdev layer
28
block: Silence Coverity in bdrv_drop_intermediate()
28
nbd/server: Allow MULTI_CONN for shared writable exports
29
qemu-iotests: Fix 232 for non-qcow2
30
29
31
Lukáš Doktor (1):
30
Hanna Reitz (1):
32
qemu-iotests: Treat custom TEST_DIR in 051
31
iotests/testrunner: Flush after run_test()
33
32
34
Markus Armbruster (1):
33
Kevin Wolf (2):
35
vl: Fix to create migration object before block backends again
34
coroutine: Rename qemu_coroutine_inc/dec_pool_size()
35
coroutine: Revert to constant batch size
36
36
37
Max Reitz (1):
37
Paolo Bonzini (1):
38
blockdev: Check @replaces in blockdev_mirror_common
38
qemu-iotests: inline common.config into common.rc
39
39
40
Sam Eiderman (1):
40
Philippe Mathieu-Daudé (2):
41
vmdk: Support version=3 in VMDK descriptor files
41
hw/block/fdc: Prevent end-of-track overrun (CVE-2021-3507)
42
tests/qtest/fdc-test: Add a regression test for CVE-2021-3507
42
43
43
Sergio Lopez (2):
44
qapi/block-export.json | 8 +-
44
mirror: Confirm we're quiesced only if the job is paused or cancelled
45
docs/interop/nbd.txt | 1 +
45
iotests: 153: Wait for an answer to QMP commands
46
docs/tools/qemu-nbd.rst | 3 +-
47
include/block/nbd.h | 5 +-
48
include/qemu/coroutine.h | 6 +-
49
blockdev-nbd.c | 13 +-
50
hw/block/fdc.c | 8 ++
51
hw/block/virtio-blk.c | 6 +-
52
nbd/server.c | 10 +-
53
qemu-nbd.c | 2 +-
54
tests/qtest/fdc-test.c | 21 ++++
55
util/qemu-coroutine.c | 26 ++--
56
tests/qemu-iotests/testrunner.py | 4 +
57
.gitlab-ci.d/buildtest-template.yml | 12 +-
58
MAINTAINERS | 1 +
59
tests/qemu-iotests/common.config | 41 -------
60
tests/qemu-iotests/common.rc | 31 +++--
61
tests/qemu-iotests/tests/nbd-multiconn | 145 +++++++++++++++++++++++
62
tests/qemu-iotests/tests/nbd-multiconn.out | 5 +
63
tests/qemu-iotests/tests/nbd-qemu-allocation.out | 2 +-
64
20 files changed, 261 insertions(+), 89 deletions(-)
65
delete mode 100644 tests/qemu-iotests/common.config
66
create mode 100755 tests/qemu-iotests/tests/nbd-multiconn
67
create mode 100644 tests/qemu-iotests/tests/nbd-multiconn.out
46
68
47
Vladimir Sementsov-Ogievskiy (2):
48
qapi: fix block-latency-histogram-set description and examples
49
blockjob: fix user pause in block_job_error_action
50
69
51
qapi/block-core.json | 10 +++---
52
block.c | 7 ++--
53
block/copy-on-read.c | 2 +-
54
block/crypto.c | 2 +-
55
block/mirror.c | 16 ++++++++++
56
block/qcow2.c | 2 +-
57
block/replication.c | 2 +-
58
block/vmdk.c | 6 ++--
59
blockdev.c | 55 +++++++++++++++++++-------------
60
blockjob.c | 8 +++--
61
vl.c | 15 +++++----
62
tests/qemu-iotests/051 | 2 +-
63
tests/qemu-iotests/153 | 12 +++----
64
tests/qemu-iotests/153.out | 6 ++++
65
tests/qemu-iotests/232 | 30 ------------------
66
tests/qemu-iotests/232.out | 20 ------------
67
tests/qemu-iotests/247 | 79 ++++++++++++++++++++++++++++++++++++++++++++++
68
tests/qemu-iotests/247.out | 22 +++++++++++++
69
tests/qemu-iotests/group | 1 +
70
19 files changed, 193 insertions(+), 104 deletions(-)
71
create mode 100755 tests/qemu-iotests/247
72
create mode 100644 tests/qemu-iotests/247.out
73
diff view generated by jsdifflib
1
From: Lukáš Doktor <ldoktor@redhat.com>
1
It's true that these functions currently affect the batch size in which
2
coroutines are reused (i.e. moved from the global release pool to the
3
allocation pool of a specific thread), but this is a bug and will be
4
fixed in a separate patch.
2
5
3
When custom TEST_DIR is specified the output includes it without leading
6
In fact, the comment in the header file already just promises that it
4
'/':
7
influences the pool size, so reflect this in the name of the functions.
8
As a nice side effect, the shorter function name makes some line
9
wrapping unnecessary.
5
10
6
$ TEST_DIR=/var/tmp ./check -file -qcow2 051
11
Cc: qemu-stable@nongnu.org
7
....
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
-drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file":
13
Message-Id: <20220510151020.105528-2-kwolf@redhat.com>
9
{"driver": "file", "filename": "TEST_DIR/t.qcow2"}}, "driver": "qcow2",
10
"file": {"driver": "file", "filename": SNAPSHOT_PATH}} (qcow2,
11
read-only)
12
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file":
13
{"driver": "file", "filename": "TEST_DIR/t.qcow2"}}, "driver": "qcow2",
14
"file": {"driver": "file", "filename": "TEST_DIR/vl.ziHfeP"}} (qcow2,
15
read-only)
16
17
Let's remove it from the sed regexp.
18
19
Signed-off-by: Lukáš Doktor <ldoktor@redhat.com>
20
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
---
15
---
22
tests/qemu-iotests/051 | 2 +-
16
include/qemu/coroutine.h | 6 +++---
23
1 file changed, 1 insertion(+), 1 deletion(-)
17
hw/block/virtio-blk.c | 6 ++----
18
util/qemu-coroutine.c | 4 ++--
19
3 files changed, 7 insertions(+), 9 deletions(-)
24
20
25
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
21
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
26
index XXXXXXX..XXXXXXX 100755
22
index XXXXXXX..XXXXXXX 100644
27
--- a/tests/qemu-iotests/051
23
--- a/include/qemu/coroutine.h
28
+++ b/tests/qemu-iotests/051
24
+++ b/include/qemu/coroutine.h
29
@@ -XXX,XX +XXX,XX @@ TMPDIR=/nonexistent run_qemu -drive driver=null-co,snapshot=on
25
@@ -XXX,XX +XXX,XX @@ void coroutine_fn yield_until_fd_readable(int fd);
30
echo "info block" |
26
/**
31
run_qemu -drive file="$TEST_IMG",snapshot=on,read-only=on,if=none,id=$device_id |
27
* Increase coroutine pool size
32
_filter_qemu_io |
28
*/
33
- sed -e 's#"/[^"]*/vl\.[A-Za-z0-9]\{6\}"#SNAPSHOT_PATH#g'
29
-void qemu_coroutine_increase_pool_batch_size(unsigned int additional_pool_size);
34
+ sed -e 's#"[^"]*/vl\.[A-Za-z0-9]\{6\}"#SNAPSHOT_PATH#g'
30
+void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size);
35
31
36
32
/**
37
# success, all done
33
- * Devcrease coroutine pool size
34
+ * Decrease coroutine pool size
35
*/
36
-void qemu_coroutine_decrease_pool_batch_size(unsigned int additional_pool_size);
37
+void qemu_coroutine_dec_pool_size(unsigned int additional_pool_size);
38
39
#include "qemu/lockable.h"
40
41
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/hw/block/virtio-blk.c
44
+++ b/hw/block/virtio-blk.c
45
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
46
for (i = 0; i < conf->num_queues; i++) {
47
virtio_add_queue(vdev, conf->queue_size, virtio_blk_handle_output);
48
}
49
- qemu_coroutine_increase_pool_batch_size(conf->num_queues * conf->queue_size
50
- / 2);
51
+ qemu_coroutine_inc_pool_size(conf->num_queues * conf->queue_size / 2);
52
virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err);
53
if (err != NULL) {
54
error_propagate(errp, err);
55
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_unrealize(DeviceState *dev)
56
for (i = 0; i < conf->num_queues; i++) {
57
virtio_del_queue(vdev, i);
58
}
59
- qemu_coroutine_decrease_pool_batch_size(conf->num_queues * conf->queue_size
60
- / 2);
61
+ qemu_coroutine_dec_pool_size(conf->num_queues * conf->queue_size / 2);
62
qemu_del_vm_change_state_handler(s->change);
63
blockdev_mark_auto_del(s->blk);
64
virtio_cleanup(vdev);
65
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
66
index XXXXXXX..XXXXXXX 100644
67
--- a/util/qemu-coroutine.c
68
+++ b/util/qemu-coroutine.c
69
@@ -XXX,XX +XXX,XX @@ AioContext *coroutine_fn qemu_coroutine_get_aio_context(Coroutine *co)
70
return co->ctx;
71
}
72
73
-void qemu_coroutine_increase_pool_batch_size(unsigned int additional_pool_size)
74
+void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size)
75
{
76
qatomic_add(&pool_batch_size, additional_pool_size);
77
}
78
79
-void qemu_coroutine_decrease_pool_batch_size(unsigned int removing_pool_size)
80
+void qemu_coroutine_dec_pool_size(unsigned int removing_pool_size)
81
{
82
qatomic_sub(&pool_batch_size, removing_pool_size);
83
}
38
--
84
--
39
2.20.1
85
2.35.3
40
41
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
Commit 4c41c69e changed the way the coroutine pool is sized because for
2
virtio-blk devices with a large queue size and heavy I/O, it was just
3
too small and caused coroutines to be deleted and reallocated soon
4
afterwards. The change made the size dynamic based on the number of
5
queues and the queue size of virtio-blk devices.
2
6
3
Job (especially mirror) may call block_job_error_action several
7
There are two important numbers here: Slightly simplified, when a
4
times before actual pause if it has several in-flight requests.
8
coroutine terminates, it is generally stored in the global release pool
9
up to a certain pool size, and if the pool is full, it is freed.
10
Conversely, when allocating a new coroutine, the coroutines in the
11
release pool are reused if the pool already has reached a certain
12
minimum size (the batch size), otherwise we allocate new coroutines.
5
13
6
block_job_error_action will call job_pause more than once in this case,
14
The problem after commit 4c41c69e is that it not only increases the
7
which lead to following block-job-resume qmp command can't actually
15
maximum pool size (which is the intended effect), but also the batch
8
resume the job.
16
size for reusing coroutines (which is a bug). It means that in cases
17
with many devices and/or a large queue size (which defaults to the
18
number of vcpus for virtio-blk-pci), many thousand coroutines could be
19
sitting in the release pool without being reused.
9
20
10
Fix it by do not increase pause level in block_job_error_action if
21
This is not only a waste of memory and allocations, but it actually
11
user_paused already set.
22
makes the QEMU process likely to hit the vm.max_map_count limit on Linux
23
because each coroutine requires two mappings (its stack and the guard
24
page for the stack), causing it to abort() in qemu_alloc_stack() because
25
when the limit is hit, mprotect() starts to fail with ENOMEM.
12
26
13
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
27
In order to fix the problem, change the batch size back to 64 to avoid
28
uselessly accumulating coroutines in the release pool, but keep the
29
dynamic maximum pool size so that coroutines aren't freed too early
30
in heavy I/O scenarios.
31
32
Note that this fix doesn't strictly make it impossible to hit the limit,
33
but this would only happen if most of the coroutines are actually in use
34
at the same time, not just sitting in a pool. This is the same behaviour
35
as we already had before commit 4c41c69e. Fully preventing this would
36
require allowing qemu_coroutine_create() to return an error, but it
37
doesn't seem to be a scenario that people hit in practice.
38
39
Cc: qemu-stable@nongnu.org
40
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2079938
41
Fixes: 4c41c69e05fe28c0f95f8abd2ebf407e95a4f04b
42
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
43
Message-Id: <20220510151020.105528-3-kwolf@redhat.com>
44
Tested-by: Hiroki Narukawa <hnarukaw@yahoo-corp.jp>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
45
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
46
---
16
blockjob.c | 8 +++++---
47
util/qemu-coroutine.c | 22 ++++++++++++++--------
17
1 file changed, 5 insertions(+), 3 deletions(-)
48
1 file changed, 14 insertions(+), 8 deletions(-)
18
49
19
diff --git a/blockjob.c b/blockjob.c
50
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
20
index XXXXXXX..XXXXXXX 100644
51
index XXXXXXX..XXXXXXX 100644
21
--- a/blockjob.c
52
--- a/util/qemu-coroutine.c
22
+++ b/blockjob.c
53
+++ b/util/qemu-coroutine.c
23
@@ -XXX,XX +XXX,XX @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
54
@@ -XXX,XX +XXX,XX @@
24
action);
55
#include "qemu/coroutine-tls.h"
25
}
56
#include "block/aio.h"
26
if (action == BLOCK_ERROR_ACTION_STOP) {
57
27
- job_pause(&job->job);
58
-/** Initial batch size is 64, and is increased on demand */
28
- /* make the pause user visible, which will be resumed from QMP. */
59
+/**
29
- job->job.user_paused = true;
60
+ * The minimal batch size is always 64, coroutines from the release_pool are
30
+ if (!job->job.user_paused) {
61
+ * reused as soon as there are 64 coroutines in it. The maximum pool size starts
31
+ job_pause(&job->job);
62
+ * with 64 and is increased on demand so that coroutines are not deleted even if
32
+ /* make the pause user visible, which will be resumed from QMP. */
63
+ * they are not immediately reused.
33
+ job->job.user_paused = true;
64
+ */
34
+ }
65
enum {
35
block_job_iostatus_set_err(job, error);
66
- POOL_INITIAL_BATCH_SIZE = 64,
36
}
67
+ POOL_MIN_BATCH_SIZE = 64,
37
return action;
68
+ POOL_INITIAL_MAX_SIZE = 64,
69
};
70
71
/** Free list to speed up creation */
72
static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool);
73
-static unsigned int pool_batch_size = POOL_INITIAL_BATCH_SIZE;
74
+static unsigned int pool_max_size = POOL_INITIAL_MAX_SIZE;
75
static unsigned int release_pool_size;
76
77
typedef QSLIST_HEAD(, Coroutine) CoroutineQSList;
78
@@ -XXX,XX +XXX,XX @@ Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque)
79
80
co = QSLIST_FIRST(alloc_pool);
81
if (!co) {
82
- if (release_pool_size > qatomic_read(&pool_batch_size)) {
83
+ if (release_pool_size > POOL_MIN_BATCH_SIZE) {
84
/* Slow path; a good place to register the destructor, too. */
85
Notifier *notifier = get_ptr_coroutine_pool_cleanup_notifier();
86
if (!notifier->notify) {
87
@@ -XXX,XX +XXX,XX @@ static void coroutine_delete(Coroutine *co)
88
co->caller = NULL;
89
90
if (CONFIG_COROUTINE_POOL) {
91
- if (release_pool_size < qatomic_read(&pool_batch_size) * 2) {
92
+ if (release_pool_size < qatomic_read(&pool_max_size) * 2) {
93
QSLIST_INSERT_HEAD_ATOMIC(&release_pool, co, pool_next);
94
qatomic_inc(&release_pool_size);
95
return;
96
}
97
- if (get_alloc_pool_size() < qatomic_read(&pool_batch_size)) {
98
+ if (get_alloc_pool_size() < qatomic_read(&pool_max_size)) {
99
QSLIST_INSERT_HEAD(get_ptr_alloc_pool(), co, pool_next);
100
set_alloc_pool_size(get_alloc_pool_size() + 1);
101
return;
102
@@ -XXX,XX +XXX,XX @@ AioContext *coroutine_fn qemu_coroutine_get_aio_context(Coroutine *co)
103
104
void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size)
105
{
106
- qatomic_add(&pool_batch_size, additional_pool_size);
107
+ qatomic_add(&pool_max_size, additional_pool_size);
108
}
109
110
void qemu_coroutine_dec_pool_size(unsigned int removing_pool_size)
111
{
112
- qatomic_sub(&pool_batch_size, removing_pool_size);
113
+ qatomic_sub(&pool_max_size, removing_pool_size);
114
}
38
--
115
--
39
2.20.1
116
2.35.3
40
41
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Hanna Reitz <hreitz@redhat.com>
2
2
3
There no @device parameter, only the @id one.
3
When stdout is not a terminal, the buffer may not be flushed at each end
4
of line, so we should flush after each test is done. This is especially
5
apparent when run by check-block, in two ways:
4
6
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
First, when running make check-block -jX with X > 1, progress indication
8
was missing, even though testrunner.py does theoretically print each
9
test's status once it has been run, even in multi-processing mode.
10
Flushing after each test restores this progress indication.
11
12
Second, sometimes make check-block failed altogether, with an error
13
message that "too few tests [were] run". I presume that's because one
14
worker process in the job pool did not get to flush its stdout before
15
the main process exited, and so meson did not get to see that worker's
16
test results. In any case, by flushing at the end of run_test(), the
17
problem has disappeared for me.
18
19
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
20
Message-Id: <20220506134215.10086-1-hreitz@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
21
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
23
---
9
qapi/block-core.json | 10 +++++-----
24
tests/qemu-iotests/testrunner.py | 1 +
10
1 file changed, 5 insertions(+), 5 deletions(-)
25
1 file changed, 1 insertion(+)
11
26
12
diff --git a/qapi/block-core.json b/qapi/block-core.json
27
diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py
13
index XXXXXXX..XXXXXXX 100644
28
index XXXXXXX..XXXXXXX 100644
14
--- a/qapi/block-core.json
29
--- a/tests/qemu-iotests/testrunner.py
15
+++ b/qapi/block-core.json
30
+++ b/tests/qemu-iotests/testrunner.py
16
@@ -XXX,XX +XXX,XX @@
31
@@ -XXX,XX +XXX,XX @@ def run_test(self, test: str,
17
#
32
else:
18
# Manage read, write and flush latency histograms for the device.
33
print(res.casenotrun)
19
#
34
20
-# If only @device parameter is specified, remove all present latency histograms
35
+ sys.stdout.flush()
21
+# If only @id parameter is specified, remove all present latency histograms
36
return res
22
# for the device. Otherwise, add/reset some of (or all) latency histograms.
37
23
#
38
def run_tests(self, tests: List[str], jobs: int = 1) -> bool:
24
# @id: The name or QOM path of the guest device.
25
@@ -XXX,XX +XXX,XX @@
26
# [0, 10), [10, 50), [50, 100), [100, +inf):
27
#
28
# -> { "execute": "block-latency-histogram-set",
29
-# "arguments": { "device": "drive0",
30
+# "arguments": { "id": "drive0",
31
# "boundaries": [10, 50, 100] } }
32
# <- { "return": {} }
33
#
34
@@ -XXX,XX +XXX,XX @@
35
# not changed (or not created):
36
#
37
# -> { "execute": "block-latency-histogram-set",
38
-# "arguments": { "device": "drive0",
39
+# "arguments": { "id": "drive0",
40
# "boundaries-write": [10, 50, 100] } }
41
# <- { "return": {} }
42
#
43
@@ -XXX,XX +XXX,XX @@
44
# write: [0, 1000), [1000, 5000), [5000, +inf)
45
#
46
# -> { "execute": "block-latency-histogram-set",
47
-# "arguments": { "device": "drive0",
48
+# "arguments": { "id": "drive0",
49
# "boundaries": [10, 50, 100],
50
# "boundaries-write": [1000, 5000] } }
51
# <- { "return": {} }
52
@@ -XXX,XX +XXX,XX @@
53
# Example: remove all latency histograms:
54
#
55
# -> { "execute": "block-latency-histogram-set",
56
-# "arguments": { "device": "drive0" } }
57
+# "arguments": { "id": "drive0" } }
58
# <- { "return": {} }
59
##
60
{ 'command': 'block-latency-histogram-set',
61
--
39
--
62
2.20.1
40
2.35.3
63
64
diff view generated by jsdifflib
1
From: Sergio Lopez <slp@redhat.com>
1
From: Daniel P. Berrangé <berrange@redhat.com>
2
2
3
There are various actions in this test that must be executed
3
When running I/O tests using TAP output mode, we get a single TAP test
4
sequentially, as the result of it depends on the state triggered by the
4
with a sub-test reported for each I/O test that is run. The output looks
5
previous one.
5
something like this:
6
6
7
If the last argument of _send_qemu_cmd() is an empty string, it just
7
1..123
8
sends the QMP commands without waiting for an answer. While unlikely, it
8
ok qcow2 011
9
may happen that the next action in the test gets invoked before QEMU
9
ok qcow2 012
10
processes the QMP request.
10
ok qcow2 013
11
ok qcow2 217
12
...
11
13
12
This issue seems to be easier to reproduce on servers with limited
14
If everything runs or fails normally this is fine, but periodically we
13
resources or highly loaded.
15
have been seeing the test harness abort early before all 123 tests have
16
been run, just leaving a fairly useless message like
14
17
15
With this change, we wait for an answer on all _send_qemu_cmd() calls.
18
TAP parsing error: Too few tests run (expected 123, got 107)
16
19
17
Signed-off-by: Sergio Lopez <slp@redhat.com>
20
we have no idea which tests were running at the time the test harness
21
abruptly exited. This change causes us to print a message about our
22
intent to run each test, so we have a record of what is active at the
23
time the harness exits abnormally.
24
25
1..123
26
# running qcow2 011
27
ok qcow2 011
28
# running qcow2 012
29
ok qcow2 012
30
# running qcow2 013
31
ok qcow2 013
32
# running qcow2 217
33
ok qcow2 217
34
...
35
36
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
37
Message-Id: <20220509124134.867431-2-berrange@redhat.com>
38
Reviewed-by: Thomas Huth <thuth@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
39
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
40
---
20
tests/qemu-iotests/153 | 12 ++++++------
41
tests/qemu-iotests/testrunner.py | 3 +++
21
tests/qemu-iotests/153.out | 6 ++++++
42
1 file changed, 3 insertions(+)
22
2 files changed, 12 insertions(+), 6 deletions(-)
23
43
24
diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153
44
diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py
25
index XXXXXXX..XXXXXXX 100755
26
--- a/tests/qemu-iotests/153
27
+++ b/tests/qemu-iotests/153
28
@@ -XXX,XX +XXX,XX @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do
29
_img_info -U | grep 'file format'
30
fi
31
done
32
- _send_qemu_cmd $h "{ 'execute': 'quit', }" ""
33
+ _send_qemu_cmd $h "{ 'execute': 'quit' }" ''
34
echo
35
echo "Round done"
36
_cleanup_qemu
37
@@ -XXX,XX +XXX,XX @@ echo "Adding drive"
38
_send_qemu_cmd $QEMU_HANDLE \
39
"{ 'execute': 'human-monitor-command',
40
'arguments': { 'command-line': 'drive_add 0 if=none,id=d0,file=${TEST_IMG}' } }" \
41
- ""
42
+ 'return'
43
44
_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
45
46
@@ -XXX,XX +XXX,XX @@ echo "== Closing an image should unlock it =="
47
_send_qemu_cmd $QEMU_HANDLE \
48
"{ 'execute': 'human-monitor-command',
49
'arguments': { 'command-line': 'drive_del d0' } }" \
50
- ""
51
+ 'return'
52
53
_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
54
55
@@ -XXX,XX +XXX,XX @@ for d in d0 d1; do
56
_send_qemu_cmd $QEMU_HANDLE \
57
"{ 'execute': 'human-monitor-command',
58
'arguments': { 'command-line': 'drive_add 0 if=none,id=$d,file=${TEST_IMG},readonly=on' } }" \
59
- ""
60
+ 'return'
61
done
62
63
_run_cmd $QEMU_IMG info "${TEST_IMG}"
64
@@ -XXX,XX +XXX,XX @@ _run_cmd $QEMU_IMG info "${TEST_IMG}"
65
_send_qemu_cmd $QEMU_HANDLE \
66
"{ 'execute': 'human-monitor-command',
67
'arguments': { 'command-line': 'drive_del d0' } }" \
68
- ""
69
+ 'return'
70
71
_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
72
73
@@ -XXX,XX +XXX,XX @@ echo "Closing the other"
74
_send_qemu_cmd $QEMU_HANDLE \
75
"{ 'execute': 'human-monitor-command',
76
'arguments': { 'command-line': 'drive_del d1' } }" \
77
- ""
78
+ 'return'
79
80
_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
81
82
diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out
83
index XXXXXXX..XXXXXXX 100644
45
index XXXXXXX..XXXXXXX 100644
84
--- a/tests/qemu-iotests/153.out
46
--- a/tests/qemu-iotests/testrunner.py
85
+++ b/tests/qemu-iotests/153.out
47
+++ b/tests/qemu-iotests/testrunner.py
86
@@ -XXX,XX +XXX,XX @@ Is another process using the image [TEST_DIR/t.qcow2]?
48
@@ -XXX,XX +XXX,XX @@ def run_test(self, test: str,
87
_qemu_img_wrapper commit -b TEST_DIR/t.qcow2.b TEST_DIR/t.qcow2.c
49
starttime=start,
88
{"return": {}}
50
lasttime=last_el,
89
Adding drive
51
end = '\n' if mp else '\r')
90
+{"return": "OKrn"}
52
+ else:
91
53
+ testname = os.path.basename(test)
92
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
54
+ print(f'# running {self.env.imgfmt} {testname}')
93
can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
55
94
@@ -XXX,XX +XXX,XX @@ Creating overlay with qemu-img when the guest is running should be allowed
56
res = self.do_run_test(test, mp)
95
96
_qemu_img_wrapper create -f qcow2 -b TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.overlay
97
== Closing an image should unlock it ==
98
+{"return": ""}
99
100
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
101
Adding two and closing one
102
+{"return": "OKrn"}
103
+{"return": "OKrn"}
104
105
_qemu_img_wrapper info TEST_DIR/t.qcow2
106
+{"return": ""}
107
108
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
109
can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
110
Is another process using the image [TEST_DIR/t.qcow2]?
111
Closing the other
112
+{"return": ""}
113
114
_qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512
115
57
116
--
58
--
117
2.20.1
59
2.35.3
118
60
119
61
diff view generated by jsdifflib
1
From: Markus Armbruster <armbru@redhat.com>
1
From: Daniel P. Berrangé <berrange@redhat.com>
2
2
3
Recent commit cda4aa9a5a0 moved block backend creation before machine
3
When running 'make check' we only get a summary of progress on the
4
property evaluation. This broke qemu-iotests 055. Turns out we need
4
console. Fortunately meson/ninja have saved the raw test output to a
5
to create the migration object before block backends, so block
5
logfile. Exposing this log will make it easier to debug failures that
6
backends can add migration blockers. Fix by calling
6
happen in CI.
7
migration_object_init() earlier, right before configure_blockdev().
8
7
9
Fixes: cda4aa9a5a08777cf13e164c0543bd4888b8adce
8
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
10
Reported-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20220509124134.867431-3-berrange@redhat.com>
11
Signed-off-by: Markus Armbruster <armbru@redhat.com>
10
Reviewed-by: Thomas Huth <thuth@redhat.com>
11
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
13
---
14
vl.c | 15 ++++++++-------
14
.gitlab-ci.d/buildtest-template.yml | 12 ++++++++++--
15
1 file changed, 8 insertions(+), 7 deletions(-)
15
1 file changed, 10 insertions(+), 2 deletions(-)
16
16
17
diff --git a/vl.c b/vl.c
17
diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml
18
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
19
--- a/vl.c
19
--- a/.gitlab-ci.d/buildtest-template.yml
20
+++ b/vl.c
20
+++ b/.gitlab-ci.d/buildtest-template.yml
21
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
21
@@ -XXX,XX +XXX,XX @@
22
exit(0);
22
make -j"$JOBS" $MAKE_CHECK_ARGS ;
23
}
23
fi
24
24
25
+ /*
25
-.native_test_job_template:
26
+ * Migration object can only be created after global properties
26
+.common_test_job_template:
27
+ * are applied correctly.
27
stage: test
28
+ */
28
image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
29
+ migration_object_init();
29
script:
30
@@ -XXX,XX +XXX,XX @@
31
# Avoid recompiling by hiding ninja with NINJA=":"
32
- make NINJA=":" $MAKE_CHECK_ARGS
33
34
+.native_test_job_template:
35
+ extends: .common_test_job_template
36
+ artifacts:
37
+ name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
38
+ expire_in: 7 days
39
+ paths:
40
+ - build/meson-logs/testlog.txt
30
+
41
+
31
/*
42
.avocado_test_job_template:
32
* Note: we need to create block backends before
43
- extends: .native_test_job_template
33
* machine_set_property(), so machine properties can refer to
44
+ extends: .common_test_job_template
34
- * them.
45
cache:
35
+ * them, and after migration_object_init(), so we can create
46
key: "${CI_JOB_NAME}-cache"
36
+ * migration blockers.
47
paths:
37
*/
38
configure_blockdev(&bdo_queue, machine_class, snapshot);
39
40
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
41
machine_class->name, machine_class->deprecation_reason);
42
}
43
44
- /*
45
- * Migration object can only be created after global properties
46
- * are applied correctly.
47
- */
48
- migration_object_init();
49
-
50
if (qtest_chrdev) {
51
qtest_init(qtest_chrdev, qtest_log, &error_fatal);
52
}
53
--
48
--
54
2.20.1
49
2.35.3
55
50
56
51
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Philippe Mathieu-Daudé <philmd@redhat.com>
2
2
3
There is no reason why the constraints we put on @replaces should be
3
Per the 82078 datasheet, if the end-of-track (EOT byte in
4
limited to drive-mirror. Therefore, move the sanity checks from
4
the FIFO) is more than the number of sectors per side, the
5
qmp_drive_mirror() to blockdev_mirror_common() so they apply to
5
command is terminated unsuccessfully:
6
blockdev-mirror as well.
7
6
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
* 5.2.5 DATA TRANSFER TERMINATION
9
Reviewed-by: Eric Blake <eblake@redhat.com>
8
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
9
The 82078 supports terminal count explicitly through
10
the TC pin and implicitly through the underrun/over-
11
run and end-of-track (EOT) functions. For full sector
12
transfers, the EOT parameter can define the last
13
sector to be transferred in a single or multisector
14
transfer. If the last sector to be transferred is a par-
15
tial sector, the host can stop transferring the data in
16
mid-sector, and the 82078 will continue to complete
17
the sector as if a hardware TC was received. The
18
only difference between these implicit functions and
19
TC is that they return "abnormal termination" result
20
status. Such status indications can be ignored if they
21
were expected.
22
23
* 6.1.3 READ TRACK
24
25
This command terminates when the EOT specified
26
number of sectors have been read. If the 82078
27
does not find an I D Address Mark on the diskette
28
after the second· occurrence of a pulse on the
29
INDX# pin, then it sets the IC code in Status Regis-
30
ter 0 to "01" (Abnormal termination), sets the MA bit
31
in Status Register 1 to "1", and terminates the com-
32
mand.
33
34
* 6.1.6 VERIFY
35
36
Refer to Table 6-6 and Table 6-7 for information
37
concerning the values of MT and EC versus SC and
38
EOT value.
39
40
* Table 6·6. Result Phase Table
41
42
* Table 6-7. Verify Command Result Phase Table
43
44
Fix by aborting the transfer when EOT > # Sectors Per Side.
45
46
Cc: qemu-stable@nongnu.org
47
Cc: Hervé Poussineau <hpoussin@reactos.org>
48
Fixes: baca51faff0 ("floppy driver: disk geometry auto detect")
49
Reported-by: Alexander Bulekov <alxndr@bu.edu>
50
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/339
51
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
52
Message-Id: <20211118115733.4038610-2-philmd@redhat.com>
53
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
54
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
55
---
13
blockdev.c | 55 ++++++++++++++++++++++++++++++++----------------------
56
hw/block/fdc.c | 8 ++++++++
14
1 file changed, 33 insertions(+), 22 deletions(-)
57
1 file changed, 8 insertions(+)
15
58
16
diff --git a/blockdev.c b/blockdev.c
59
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
17
index XXXXXXX..XXXXXXX 100644
60
index XXXXXXX..XXXXXXX 100644
18
--- a/blockdev.c
61
--- a/hw/block/fdc.c
19
+++ b/blockdev.c
62
+++ b/hw/block/fdc.c
20
@@ -XXX,XX +XXX,XX @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
63
@@ -XXX,XX +XXX,XX @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
21
sync = MIRROR_SYNC_MODE_FULL;
64
int tmp;
22
}
65
fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]);
23
66
tmp = (fdctrl->fifo[6] - ks + 1);
24
+ if (has_replaces) {
67
+ if (tmp < 0) {
25
+ BlockDriverState *to_replace_bs;
68
+ FLOPPY_DPRINTF("invalid EOT: %d\n", tmp);
26
+ AioContext *replace_aio_context;
69
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
27
+ int64_t bs_size, replace_size;
70
+ fdctrl->fifo[3] = kt;
28
+
71
+ fdctrl->fifo[4] = kh;
29
+ bs_size = bdrv_getlength(bs);
72
+ fdctrl->fifo[5] = ks;
30
+ if (bs_size < 0) {
31
+ error_setg_errno(errp, -bs_size, "Failed to query device's size");
32
+ return;
73
+ return;
33
+ }
74
+ }
34
+
75
if (fdctrl->fifo[0] & 0x80)
35
+ to_replace_bs = check_to_replace_node(bs, replaces, errp);
76
tmp += fdctrl->fifo[6];
36
+ if (!to_replace_bs) {
77
fdctrl->data_len *= tmp;
37
+ return;
38
+ }
39
+
40
+ replace_aio_context = bdrv_get_aio_context(to_replace_bs);
41
+ aio_context_acquire(replace_aio_context);
42
+ replace_size = bdrv_getlength(to_replace_bs);
43
+ aio_context_release(replace_aio_context);
44
+
45
+ if (replace_size < 0) {
46
+ error_setg_errno(errp, -replace_size,
47
+ "Failed to query the replacement node's size");
48
+ return;
49
+ }
50
+ if (bs_size != replace_size) {
51
+ error_setg(errp, "cannot replace image with a mirror image of "
52
+ "different size");
53
+ return;
54
+ }
55
+ }
56
+
57
/* pass the node name to replace to mirror start since it's loose coupling
58
* and will allow to check whether the node still exist at mirror completion
59
*/
60
@@ -XXX,XX +XXX,XX @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
61
}
62
63
if (arg->has_replaces) {
64
- BlockDriverState *to_replace_bs;
65
- AioContext *replace_aio_context;
66
- int64_t replace_size;
67
-
68
if (!arg->has_node_name) {
69
error_setg(errp, "a node-name must be provided when replacing a"
70
" named node of the graph");
71
goto out;
72
}
73
-
74
- to_replace_bs = check_to_replace_node(bs, arg->replaces, &local_err);
75
-
76
- if (!to_replace_bs) {
77
- error_propagate(errp, local_err);
78
- goto out;
79
- }
80
-
81
- replace_aio_context = bdrv_get_aio_context(to_replace_bs);
82
- aio_context_acquire(replace_aio_context);
83
- replace_size = bdrv_getlength(to_replace_bs);
84
- aio_context_release(replace_aio_context);
85
-
86
- if (size != replace_size) {
87
- error_setg(errp, "cannot replace image with a mirror image of "
88
- "different size");
89
- goto out;
90
- }
91
}
92
93
if (arg->mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) {
94
--
78
--
95
2.20.1
79
2.35.3
96
80
97
81
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Philippe Mathieu-Daudé <philmd@redhat.com>
2
2
3
Signed-off-by: Alberto Garcia <berto@igalia.com>
3
Add the reproducer from https://gitlab.com/qemu-project/qemu/-/issues/339
4
5
Without the previous commit, when running 'make check-qtest-i386'
6
with QEMU configured with '--enable-sanitizers' we get:
7
8
==4028352==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x619000062a00 at pc 0x5626d03c491a bp 0x7ffdb4199410 sp 0x7ffdb4198bc0
9
READ of size 786432 at 0x619000062a00 thread T0
10
#0 0x5626d03c4919 in __asan_memcpy (qemu-system-i386+0x1e65919)
11
#1 0x5626d1c023cc in flatview_write_continue softmmu/physmem.c:2787:13
12
#2 0x5626d1bf0c0f in flatview_write softmmu/physmem.c:2822:14
13
#3 0x5626d1bf0798 in address_space_write softmmu/physmem.c:2914:18
14
#4 0x5626d1bf0f37 in address_space_rw softmmu/physmem.c:2924:16
15
#5 0x5626d1bf14c8 in cpu_physical_memory_rw softmmu/physmem.c:2933:5
16
#6 0x5626d0bd5649 in cpu_physical_memory_write include/exec/cpu-common.h:82:5
17
#7 0x5626d0bd0a07 in i8257_dma_write_memory hw/dma/i8257.c:452:9
18
#8 0x5626d09f825d in fdctrl_transfer_handler hw/block/fdc.c:1616:13
19
#9 0x5626d0a048b4 in fdctrl_start_transfer hw/block/fdc.c:1539:13
20
#10 0x5626d09f4c3e in fdctrl_write_data hw/block/fdc.c:2266:13
21
#11 0x5626d09f22f7 in fdctrl_write hw/block/fdc.c:829:9
22
#12 0x5626d1c20bc5 in portio_write softmmu/ioport.c:207:17
23
24
0x619000062a00 is located 0 bytes to the right of 512-byte region [0x619000062800,0x619000062a00)
25
allocated by thread T0 here:
26
#0 0x5626d03c66ec in posix_memalign (qemu-system-i386+0x1e676ec)
27
#1 0x5626d2b988d4 in qemu_try_memalign util/oslib-posix.c:210:11
28
#2 0x5626d2b98b0c in qemu_memalign util/oslib-posix.c:226:27
29
#3 0x5626d09fbaf0 in fdctrl_realize_common hw/block/fdc.c:2341:20
30
#4 0x5626d0a150ed in isabus_fdc_realize hw/block/fdc-isa.c:113:5
31
#5 0x5626d2367935 in device_set_realized hw/core/qdev.c:531:13
32
33
SUMMARY: AddressSanitizer: heap-buffer-overflow (qemu-system-i386+0x1e65919) in __asan_memcpy
34
Shadow bytes around the buggy address:
35
0x0c32800044f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
36
0x0c3280004500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
37
0x0c3280004510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
38
0x0c3280004520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
39
0x0c3280004530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
40
=>0x0c3280004540:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
41
0x0c3280004550: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
42
0x0c3280004560: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
43
0x0c3280004570: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
44
0x0c3280004580: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
45
0x0c3280004590: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
46
Shadow byte legend (one shadow byte represents 8 application bytes):
47
Addressable: 00
48
Heap left redzone: fa
49
Freed heap region: fd
50
==4028352==ABORTING
51
52
[ kwolf: Added snapshot=on to prevent write file lock failure ]
53
54
Reported-by: Alexander Bulekov <alxndr@bu.edu>
55
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
56
Reviewed-by: Alexander Bulekov <alxndr@bu.edu>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
57
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
58
---
6
block/copy-on-read.c | 2 +-
59
tests/qtest/fdc-test.c | 21 +++++++++++++++++++++
7
block/crypto.c | 2 +-
60
1 file changed, 21 insertions(+)
8
block/replication.c | 2 +-
9
3 files changed, 3 insertions(+), 3 deletions(-)
10
61
11
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
62
diff --git a/tests/qtest/fdc-test.c b/tests/qtest/fdc-test.c
12
index XXXXXXX..XXXXXXX 100644
63
index XXXXXXX..XXXXXXX 100644
13
--- a/block/copy-on-read.c
64
--- a/tests/qtest/fdc-test.c
14
+++ b/block/copy-on-read.c
65
+++ b/tests/qtest/fdc-test.c
15
@@ -XXX,XX +XXX,XX @@ static bool cor_recurse_is_first_non_filter(BlockDriverState *bs,
66
@@ -XXX,XX +XXX,XX @@ static void test_cve_2021_20196(void)
67
qtest_quit(s);
16
}
68
}
17
69
18
70
+static void test_cve_2021_3507(void)
19
-BlockDriver bdrv_copy_on_read = {
71
+{
20
+static BlockDriver bdrv_copy_on_read = {
72
+ QTestState *s;
21
.format_name = "copy-on-read",
73
+
22
74
+ s = qtest_initf("-nographic -m 32M -nodefaults "
23
.bdrv_open = cor_open,
75
+ "-drive file=%s,format=raw,if=floppy,snapshot=on",
24
diff --git a/block/crypto.c b/block/crypto.c
76
+ test_image);
25
index XXXXXXX..XXXXXXX 100644
77
+ qtest_outl(s, 0x9, 0x0a0206);
26
--- a/block/crypto.c
78
+ qtest_outw(s, 0x3f4, 0x1600);
27
+++ b/block/crypto.c
79
+ qtest_outw(s, 0x3f4, 0x0000);
28
@@ -XXX,XX +XXX,XX @@ static const char *const block_crypto_strong_runtime_opts[] = {
80
+ qtest_outw(s, 0x3f4, 0x0000);
29
NULL
81
+ qtest_outw(s, 0x3f4, 0x0000);
30
};
82
+ qtest_outw(s, 0x3f4, 0x0200);
31
83
+ qtest_outw(s, 0x3f4, 0x0200);
32
-BlockDriver bdrv_crypto_luks = {
84
+ qtest_outw(s, 0x3f4, 0x0000);
33
+static BlockDriver bdrv_crypto_luks = {
85
+ qtest_outw(s, 0x3f4, 0x0000);
34
.format_name = "luks",
86
+ qtest_outw(s, 0x3f4, 0x0000);
35
.instance_size = sizeof(BlockCrypto),
87
+ qtest_quit(s);
36
.bdrv_probe = block_crypto_probe_luks,
88
+}
37
diff --git a/block/replication.c b/block/replication.c
89
+
38
index XXXXXXX..XXXXXXX 100644
90
int main(int argc, char **argv)
39
--- a/block/replication.c
91
{
40
+++ b/block/replication.c
92
int fd;
41
@@ -XXX,XX +XXX,XX @@ static const char *const replication_strong_runtime_opts[] = {
93
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
42
NULL
94
qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19);
43
};
95
qtest_add_func("/fdc/fuzz-registers", fuzz_registers);
44
96
qtest_add_func("/fdc/fuzz/cve_2021_20196", test_cve_2021_20196);
45
-BlockDriver bdrv_replication = {
97
+ qtest_add_func("/fdc/fuzz/cve_2021_3507", test_cve_2021_3507);
46
+static BlockDriver bdrv_replication = {
98
47
.format_name = "replication",
99
ret = g_test_run();
48
.instance_size = sizeof(BDRVReplicationState),
49
100
50
--
101
--
51
2.20.1
102
2.35.3
52
103
53
104
diff view generated by jsdifflib
1
From: Sam Eiderman <shmuel.eiderman@oracle.com>
1
From: Eric Blake <eblake@redhat.com>
2
2
3
Commit 509d39aa22909c0ed1aabf896865f19c81fb38a1 added support for read
3
The next patch wants to adjust whether the NBD server code advertises
4
only VMDKs of version 3.
4
MULTI_CONN based on whether it is known if the server limits to
5
exactly one client. For a server started by QMP, this information is
6
obtained through nbd_server_start (which can support more than one
7
export); but for qemu-nbd (which supports exactly one export), it is
8
controlled only by the command-line option -e/--shared. Since we
9
already have a hook function used by qemu-nbd, it's easiest to just
10
alter its signature to fit our needs.
5
11
6
This commit fixes the probe function to correctly handle descriptors of
12
Signed-off-by: Eric Blake <eblake@redhat.com>
7
version 3.
13
Message-Id: <20220512004924.417153-2-eblake@redhat.com>
8
9
This commit has two effects:
10
1. We no longer need to supply '-f vmdk' when pointing to descriptor
11
files of version 3 in qemu/qemu-img command line arguments.
12
2. This fixes the scenario where a VMDK points to a parent version 3
13
descriptor file which is being probed as "raw" instead of "vmdk".
14
15
Reviewed-by: Arbel Moshe <arbel.moshe@oracle.com>
16
Reviewed-by: Mark Kanda <mark.kanda@oracle.com>
17
Signed-off-by: Shmuel Eiderman <shmuel.eiderman@oracle.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
15
---
20
block/vmdk.c | 6 ++++--
16
include/block/nbd.h | 2 +-
21
1 file changed, 4 insertions(+), 2 deletions(-)
17
blockdev-nbd.c | 8 ++++----
18
qemu-nbd.c | 2 +-
19
3 files changed, 6 insertions(+), 6 deletions(-)
22
20
23
diff --git a/block/vmdk.c b/block/vmdk.c
21
diff --git a/include/block/nbd.h b/include/block/nbd.h
24
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
25
--- a/block/vmdk.c
23
--- a/include/block/nbd.h
26
+++ b/block/vmdk.c
24
+++ b/include/block/nbd.h
27
@@ -XXX,XX +XXX,XX @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
25
@@ -XXX,XX +XXX,XX @@ void nbd_client_new(QIOChannelSocket *sioc,
28
}
26
void nbd_client_get(NBDClient *client);
29
if (end - p >= strlen("version=X\n")) {
27
void nbd_client_put(NBDClient *client);
30
if (strncmp("version=1\n", p, strlen("version=1\n")) == 0 ||
28
31
- strncmp("version=2\n", p, strlen("version=2\n")) == 0) {
29
-void nbd_server_is_qemu_nbd(bool value);
32
+ strncmp("version=2\n", p, strlen("version=2\n")) == 0 ||
30
+void nbd_server_is_qemu_nbd(int max_connections);
33
+ strncmp("version=3\n", p, strlen("version=3\n")) == 0) {
31
bool nbd_server_is_running(void);
34
return 100;
32
void nbd_server_start(SocketAddress *addr, const char *tls_creds,
35
}
33
const char *tls_authz, uint32_t max_connections,
36
}
34
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
37
if (end - p >= strlen("version=X\r\n")) {
35
index XXXXXXX..XXXXXXX 100644
38
if (strncmp("version=1\r\n", p, strlen("version=1\r\n")) == 0 ||
36
--- a/blockdev-nbd.c
39
- strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0) {
37
+++ b/blockdev-nbd.c
40
+ strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0 ||
38
@@ -XXX,XX +XXX,XX @@ typedef struct NBDServerData {
41
+ strncmp("version=3\r\n", p, strlen("version=3\r\n")) == 0) {
39
} NBDServerData;
42
return 100;
40
43
}
41
static NBDServerData *nbd_server;
44
}
42
-static bool is_qemu_nbd;
43
+static int qemu_nbd_connections = -1; /* Non-negative if this is qemu-nbd */
44
45
static void nbd_update_server_watch(NBDServerData *s);
46
47
-void nbd_server_is_qemu_nbd(bool value)
48
+void nbd_server_is_qemu_nbd(int max_connections)
49
{
50
- is_qemu_nbd = value;
51
+ qemu_nbd_connections = max_connections;
52
}
53
54
bool nbd_server_is_running(void)
55
{
56
- return nbd_server || is_qemu_nbd;
57
+ return nbd_server || qemu_nbd_connections >= 0;
58
}
59
60
static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
61
diff --git a/qemu-nbd.c b/qemu-nbd.c
62
index XXXXXXX..XXXXXXX 100644
63
--- a/qemu-nbd.c
64
+++ b/qemu-nbd.c
65
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
66
67
bs->detect_zeroes = detect_zeroes;
68
69
- nbd_server_is_qemu_nbd(true);
70
+ nbd_server_is_qemu_nbd(shared);
71
72
export_opts = g_new(BlockExportOptions, 1);
73
*export_opts = (BlockExportOptions) {
45
--
74
--
46
2.20.1
75
2.35.3
47
48
diff view generated by jsdifflib
1
232 is marked as generic, but commit 12efe428c9e added code that assumes
1
From: Eric Blake <eblake@redhat.com>
2
qcow2. What the new test really needs is backing files and support for
2
3
updating the backing file link (.bdrv_change_backing_file).
3
According to the NBD spec, a server that advertises
4
4
NBD_FLAG_CAN_MULTI_CONN promises that multiple client connections will
5
Split the non-generic code into a new test case 247 and make it work
5
not see any cache inconsistencies: when properly separated by a single
6
with qed, too.
6
flush, actions performed by one client will be visible to another
7
7
client, regardless of which client did the flush.
8
9
We always satisfy these conditions in qemu - even when we support
10
multiple clients, ALL clients go through a single point of reference
11
into the block layer, with no local caching. The effect of one client
12
is instantly visible to the next client. Even if our backend were a
13
network device, we argue that any multi-path caching effects that
14
would cause inconsistencies in back-to-back actions not seeing the
15
effect of previous actions would be a bug in that backend, and not the
16
fault of caching in qemu. As such, it is safe to unconditionally
17
advertise CAN_MULTI_CONN for any qemu NBD server situation that
18
supports parallel clients.
19
20
Note, however, that we don't want to advertise CAN_MULTI_CONN when we
21
know that a second client cannot connect (for historical reasons,
22
qemu-nbd defaults to a single connection while nbd-server-add and QMP
23
commands default to unlimited connections; but we already have
24
existing means to let either style of NBD server creation alter those
25
defaults). This is visible by no longer advertising MULTI_CONN for
26
'qemu-nbd -r' without -e, as in the iotest nbd-qemu-allocation.
27
28
The harder part of this patch is setting up an iotest to demonstrate
29
behavior of multiple NBD clients to a single server. It might be
30
possible with parallel qemu-io processes, but I found it easier to do
31
in python with the help of libnbd, and help from Nir and Vladimir in
32
writing the test.
33
34
Signed-off-by: Eric Blake <eblake@redhat.com>
35
Suggested-by: Nir Soffer <nsoffer@redhat.com>
36
Suggested-by: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
37
Message-Id: <20220512004924.417153-3-eblake@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
38
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
39
---
10
tests/qemu-iotests/232 | 30 ---------------
40
qapi/block-export.json | 8 +-
11
tests/qemu-iotests/232.out | 20 ----------
41
docs/interop/nbd.txt | 1 +
12
tests/qemu-iotests/247 | 79 ++++++++++++++++++++++++++++++++++++++
42
docs/tools/qemu-nbd.rst | 3 +-
13
tests/qemu-iotests/247.out | 22 +++++++++++
43
include/block/nbd.h | 3 +-
14
tests/qemu-iotests/group | 1 +
44
blockdev-nbd.c | 5 +
15
5 files changed, 102 insertions(+), 50 deletions(-)
45
nbd/server.c | 10 +-
16
create mode 100755 tests/qemu-iotests/247
46
MAINTAINERS | 1 +
17
create mode 100644 tests/qemu-iotests/247.out
47
tests/qemu-iotests/tests/nbd-multiconn | 145 ++++++++++++++++++
18
48
tests/qemu-iotests/tests/nbd-multiconn.out | 5 +
19
diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232
49
.../tests/nbd-qemu-allocation.out | 2 +-
20
index XXXXXXX..XXXXXXX 100755
50
10 files changed, 172 insertions(+), 11 deletions(-)
21
--- a/tests/qemu-iotests/232
51
create mode 100755 tests/qemu-iotests/tests/nbd-multiconn
22
+++ b/tests/qemu-iotests/232
52
create mode 100644 tests/qemu-iotests/tests/nbd-multiconn.out
23
@@ -XXX,XX +XXX,XX @@ run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,a
53
24
run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=on
54
diff --git a/qapi/block-export.json b/qapi/block-export.json
25
run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0
55
index XXXXXXX..XXXXXXX 100644
26
56
--- a/qapi/block-export.json
27
-echo
57
+++ b/qapi/block-export.json
28
-echo "=== Try commit to backing file with auto-read-only ==="
58
@@ -XXX,XX +XXX,XX @@
29
-echo
59
# recreated on the fly while the NBD server is active.
30
-
60
# If missing, it will default to denying access (since 4.0).
31
-TEST_IMG="$TEST_IMG.0" _make_test_img $size
61
# @max-connections: The maximum number of connections to allow at the same
32
-TEST_IMG="$TEST_IMG.1" _make_test_img $size
62
-# time, 0 for unlimited. (since 5.2; default: 0)
33
-TEST_IMG="$TEST_IMG.2" _make_test_img $size
63
+# time, 0 for unlimited. Setting this to 1 also stops
34
-TEST_IMG="$TEST_IMG.3" _make_test_img $size
64
+# the server from advertising multiple client support
35
-TEST_IMG="$TEST_IMG.4" _make_test_img $size
65
+# (since 5.2; default: 0)
36
-
66
#
37
-(cat <<EOF
67
# Since: 4.2
38
-{"execute":"qmp_capabilities"}
68
##
39
-{"execute":"block-commit",
69
@@ -XXX,XX +XXX,XX @@
40
- "arguments":{"device":"format-4", "top-node": "format-2", "base-node":"format-0", "job-id":"job0"}}
70
# recreated on the fly while the NBD server is active.
41
-EOF
71
# If missing, it will default to denying access (since 4.0).
42
-sleep 1
72
# @max-connections: The maximum number of connections to allow at the same
43
-echo '{"execute":"quit"}'
73
-# time, 0 for unlimited. (since 5.2; default: 0)
44
-) | $QEMU -qmp stdio -nographic -nodefaults \
74
+# time, 0 for unlimited. Setting this to 1 also stops
45
- -blockdev file,node-name=file-0,filename=$TEST_IMG.0,auto-read-only=on \
75
+# the server from advertising multiple client support
46
- -blockdev qcow2,node-name=format-0,file=file-0,read-only=on \
76
+# (since 5.2; default: 0).
47
- -blockdev file,node-name=file-1,filename=$TEST_IMG.1,auto-read-only=on \
77
#
48
- -blockdev qcow2,node-name=format-1,file=file-1,read-only=on,backing=format-0 \
78
# Returns: error if the server is already running.
49
- -blockdev file,node-name=file-2,filename=$TEST_IMG.2,auto-read-only=on \
79
#
50
- -blockdev qcow2,node-name=format-2,file=file-2,read-only=on,backing=format-1 \
80
diff --git a/docs/interop/nbd.txt b/docs/interop/nbd.txt
51
- -blockdev file,node-name=file-3,filename=$TEST_IMG.3,auto-read-only=on \
81
index XXXXXXX..XXXXXXX 100644
52
- -blockdev qcow2,node-name=format-3,file=file-3,read-only=on,backing=format-2 \
82
--- a/docs/interop/nbd.txt
53
- -blockdev file,node-name=file-4,filename=$TEST_IMG.4,auto-read-only=on \
83
+++ b/docs/interop/nbd.txt
54
- -blockdev qcow2,node-name=format-4,file=file-4,read-only=on,backing=format-3 |
84
@@ -XXX,XX +XXX,XX @@ NBD_CMD_BLOCK_STATUS for "qemu:dirty-bitmap:", NBD_CMD_CACHE
55
- _filter_qmp
85
* 4.2: NBD_FLAG_CAN_MULTI_CONN for shareable read-only exports,
56
-
86
NBD_CMD_FLAG_FAST_ZERO
57
# success, all done
87
* 5.2: NBD_CMD_BLOCK_STATUS for "qemu:allocation-depth"
58
echo "*** done"
88
+* 7.1: NBD_FLAG_CAN_MULTI_CONN for shareable writable exports
59
rm -f $seq.full
89
diff --git a/docs/tools/qemu-nbd.rst b/docs/tools/qemu-nbd.rst
60
diff --git a/tests/qemu-iotests/232.out b/tests/qemu-iotests/232.out
90
index XXXXXXX..XXXXXXX 100644
61
index XXXXXXX..XXXXXXX 100644
91
--- a/docs/tools/qemu-nbd.rst
62
--- a/tests/qemu-iotests/232.out
92
+++ b/docs/tools/qemu-nbd.rst
63
+++ b/tests/qemu-iotests/232.out
93
@@ -XXX,XX +XXX,XX @@ driver options if :option:`--image-opts` is specified.
64
@@ -XXX,XX +XXX,XX @@ QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read
94
.. option:: -e, --shared=NUM
65
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
95
66
node0: TEST_DIR/t.IMGFMT (file)
96
Allow up to *NUM* clients to share the device (default
67
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
97
- ``1``), 0 for unlimited. Safe for readers, but for now,
68
-
98
- consistency is not guaranteed between multiple writers.
69
-=== Try commit to backing file with auto-read-only ===
99
+ ``1``), 0 for unlimited.
70
-
100
71
-Formatting 'TEST_DIR/t.IMGFMT.0', fmt=IMGFMT size=134217728
101
.. option:: -t, --persistent
72
-Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=134217728
102
73
-Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=134217728
103
diff --git a/include/block/nbd.h b/include/block/nbd.h
74
-Formatting 'TEST_DIR/t.IMGFMT.3', fmt=IMGFMT size=134217728
104
index XXXXXXX..XXXXXXX 100644
75
-Formatting 'TEST_DIR/t.IMGFMT.4', fmt=IMGFMT size=134217728
105
--- a/include/block/nbd.h
76
-QMP_VERSION
106
+++ b/include/block/nbd.h
77
-{"return": {}}
107
@@ -XXX,XX +XXX,XX @@
78
-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
108
/*
79
-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
109
- * Copyright (C) 2016-2020 Red Hat, Inc.
80
-{"return": {}}
110
+ * Copyright (C) 2016-2022 Red Hat, Inc.
81
-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
111
* Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
82
-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
112
*
83
-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 134217728, "offset": 134217728, "speed": 0, "type": "commit"}}
113
* Network Block Device
84
-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
114
@@ -XXX,XX +XXX,XX @@ void nbd_client_put(NBDClient *client);
85
-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
115
86
-{"return": {}}
116
void nbd_server_is_qemu_nbd(int max_connections);
87
-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
117
bool nbd_server_is_running(void);
88
*** done
118
+int nbd_server_max_connections(void);
89
diff --git a/tests/qemu-iotests/247 b/tests/qemu-iotests/247
119
void nbd_server_start(SocketAddress *addr, const char *tls_creds,
120
const char *tls_authz, uint32_t max_connections,
121
Error **errp);
122
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
123
index XXXXXXX..XXXXXXX 100644
124
--- a/blockdev-nbd.c
125
+++ b/blockdev-nbd.c
126
@@ -XXX,XX +XXX,XX @@ bool nbd_server_is_running(void)
127
return nbd_server || qemu_nbd_connections >= 0;
128
}
129
130
+int nbd_server_max_connections(void)
131
+{
132
+ return nbd_server ? nbd_server->max_connections : qemu_nbd_connections;
133
+}
134
+
135
static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
136
{
137
nbd_client_put(client);
138
diff --git a/nbd/server.c b/nbd/server.c
139
index XXXXXXX..XXXXXXX 100644
140
--- a/nbd/server.c
141
+++ b/nbd/server.c
142
@@ -XXX,XX +XXX,XX @@
143
/*
144
- * Copyright (C) 2016-2021 Red Hat, Inc.
145
+ * Copyright (C) 2016-2022 Red Hat, Inc.
146
* Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
147
*
148
* Network Block Device Server Side
149
@@ -XXX,XX +XXX,XX @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args,
150
int64_t size;
151
uint64_t perm, shared_perm;
152
bool readonly = !exp_args->writable;
153
- bool shared = !exp_args->writable;
154
BlockDirtyBitmapOrStrList *bitmaps;
155
size_t i;
156
int ret;
157
@@ -XXX,XX +XXX,XX @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args,
158
exp->description = g_strdup(arg->description);
159
exp->nbdflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_FLUSH |
160
NBD_FLAG_SEND_FUA | NBD_FLAG_SEND_CACHE);
161
+
162
+ if (nbd_server_max_connections() != 1) {
163
+ exp->nbdflags |= NBD_FLAG_CAN_MULTI_CONN;
164
+ }
165
if (readonly) {
166
exp->nbdflags |= NBD_FLAG_READ_ONLY;
167
- if (shared) {
168
- exp->nbdflags |= NBD_FLAG_CAN_MULTI_CONN;
169
- }
170
} else {
171
exp->nbdflags |= (NBD_FLAG_SEND_TRIM | NBD_FLAG_SEND_WRITE_ZEROES |
172
NBD_FLAG_SEND_FAST_ZERO);
173
diff --git a/MAINTAINERS b/MAINTAINERS
174
index XXXXXXX..XXXXXXX 100644
175
--- a/MAINTAINERS
176
+++ b/MAINTAINERS
177
@@ -XXX,XX +XXX,XX @@ F: qemu-nbd.*
178
F: blockdev-nbd.c
179
F: docs/interop/nbd.txt
180
F: docs/tools/qemu-nbd.rst
181
+F: tests/qemu-iotests/tests/*nbd*
182
T: git https://repo.or.cz/qemu/ericb.git nbd
183
T: git https://src.openvz.org/scm/~vsementsov/qemu.git nbd
184
185
diff --git a/tests/qemu-iotests/tests/nbd-multiconn b/tests/qemu-iotests/tests/nbd-multiconn
90
new file mode 100755
186
new file mode 100755
91
index XXXXXXX..XXXXXXX
187
index XXXXXXX..XXXXXXX
92
--- /dev/null
188
--- /dev/null
93
+++ b/tests/qemu-iotests/247
189
+++ b/tests/qemu-iotests/tests/nbd-multiconn
94
@@ -XXX,XX +XXX,XX @@
190
@@ -XXX,XX +XXX,XX @@
95
+#!/usr/bin/env bash
191
+#!/usr/bin/env python3
96
+#
192
+# group: rw auto quick
97
+# Test for auto-read-only with commit block job
193
+#
98
+#
194
+# Test cases for NBD multi-conn advertisement
99
+# Copyright (C) 2019 Red Hat, Inc.
195
+#
196
+# Copyright (C) 2022 Red Hat, Inc.
100
+#
197
+#
101
+# This program is free software; you can redistribute it and/or modify
198
+# This program is free software; you can redistribute it and/or modify
102
+# it under the terms of the GNU General Public License as published by
199
+# it under the terms of the GNU General Public License as published by
103
+# the Free Software Foundation; either version 2 of the License, or
200
+# the Free Software Foundation; either version 2 of the License, or
104
+# (at your option) any later version.
201
+# (at your option) any later version.
...
...
108
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
205
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
109
+# GNU General Public License for more details.
206
+# GNU General Public License for more details.
110
+#
207
+#
111
+# You should have received a copy of the GNU General Public License
208
+# You should have received a copy of the GNU General Public License
112
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
209
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
113
+#
210
+
114
+
211
+import os
115
+# creator
212
+from contextlib import contextmanager
116
+owner=kwolf@redhat.com
213
+import iotests
117
+
214
+from iotests import qemu_img_create, qemu_io
118
+seq=`basename $0`
215
+
119
+echo "QA output created by $seq"
216
+
120
+
217
+disk = os.path.join(iotests.test_dir, 'disk')
121
+status=1    # failure is the default!
218
+size = '4M'
122
+
219
+nbd_sock = os.path.join(iotests.sock_dir, 'nbd_sock')
123
+_cleanup()
220
+nbd_uri = 'nbd+unix:///{}?socket=' + nbd_sock
124
+{
221
+
125
+ _cleanup_test_img
222
+
126
+ rm -f $TEST_IMG.[01234]
223
+@contextmanager
127
+}
224
+def open_nbd(export_name):
128
+trap "_cleanup; exit \$status" 0 1 2 3 15
225
+ h = nbd.NBD()
129
+
226
+ try:
130
+# get standard environment, filters and checks
227
+ h.connect_uri(nbd_uri.format(export_name))
131
+. ./common.rc
228
+ yield h
132
+. ./common.filter
229
+ finally:
133
+
230
+ h.shutdown()
134
+# Requires backing files and .bdrv_change_backing_file support
231
+
135
+_supported_fmt qcow2 qed
232
+class TestNbdMulticonn(iotests.QMPTestCase):
136
+_supported_proto file
233
+ def setUp(self):
137
+_supported_os Linux
234
+ qemu_img_create('-f', iotests.imgfmt, disk, size)
138
+
235
+ qemu_io('-c', 'w -P 1 0 2M', '-c', 'w -P 2 2M 2M', disk)
139
+size=128M
236
+
140
+
237
+ self.vm = iotests.VM()
141
+echo
238
+ self.vm.launch()
142
+echo "=== Try commit to backing file with auto-read-only ==="
239
+ result = self.vm.qmp('blockdev-add', {
143
+echo
240
+ 'driver': 'qcow2',
144
+TEST_IMG="$TEST_IMG.0" _make_test_img $size
241
+ 'node-name': 'n',
145
+TEST_IMG="$TEST_IMG.1" _make_test_img $size
242
+ 'file': {'driver': 'file', 'filename': disk}
146
+TEST_IMG="$TEST_IMG.2" _make_test_img $size
243
+ })
147
+TEST_IMG="$TEST_IMG.3" _make_test_img $size
244
+ self.assert_qmp(result, 'return', {})
148
+TEST_IMG="$TEST_IMG.4" _make_test_img $size
245
+
149
+
246
+ def tearDown(self):
150
+(cat <<EOF
247
+ self.vm.shutdown()
151
+{"execute":"qmp_capabilities"}
248
+ os.remove(disk)
152
+{"execute":"block-commit",
249
+ try:
153
+ "arguments":{"device":"format-4", "top-node": "format-2", "base-node":"format-0", "job-id":"job0"}}
250
+ os.remove(nbd_sock)
154
+EOF
251
+ except OSError:
155
+sleep 1
252
+ pass
156
+echo '{"execute":"quit"}'
253
+
157
+) | $QEMU -qmp stdio -nographic -nodefaults \
254
+ @contextmanager
158
+ -blockdev file,node-name=file-0,filename=$TEST_IMG.0,auto-read-only=on \
255
+ def run_server(self, max_connections=None):
159
+ -blockdev $IMGFMT,node-name=format-0,file=file-0,read-only=on \
256
+ args = {
160
+ -blockdev file,node-name=file-1,filename=$TEST_IMG.1,auto-read-only=on \
257
+ 'addr': {
161
+ -blockdev $IMGFMT,node-name=format-1,file=file-1,read-only=on,backing=format-0 \
258
+ 'type': 'unix',
162
+ -blockdev file,node-name=file-2,filename=$TEST_IMG.2,auto-read-only=on \
259
+ 'data': {'path': nbd_sock}
163
+ -blockdev $IMGFMT,node-name=format-2,file=file-2,read-only=on,backing=format-1 \
260
+ }
164
+ -blockdev file,node-name=file-3,filename=$TEST_IMG.3,auto-read-only=on \
261
+ }
165
+ -blockdev $IMGFMT,node-name=format-3,file=file-3,read-only=on,backing=format-2 \
262
+ if max_connections is not None:
166
+ -blockdev file,node-name=file-4,filename=$TEST_IMG.4,auto-read-only=on \
263
+ args['max-connections'] = max_connections
167
+ -blockdev $IMGFMT,node-name=format-4,file=file-4,read-only=on,backing=format-3 |
264
+
168
+ _filter_qmp
265
+ result = self.vm.qmp('nbd-server-start', args)
169
+
266
+ self.assert_qmp(result, 'return', {})
170
+# success, all done
267
+ yield
171
+echo "*** done"
268
+
172
+rm -f $seq.full
269
+ result = self.vm.qmp('nbd-server-stop')
173
+status=0
270
+ self.assert_qmp(result, 'return', {})
174
diff --git a/tests/qemu-iotests/247.out b/tests/qemu-iotests/247.out
271
+
272
+ def add_export(self, name, writable=None):
273
+ args = {
274
+ 'type': 'nbd',
275
+ 'id': name,
276
+ 'node-name': 'n',
277
+ 'name': name,
278
+ }
279
+ if writable is not None:
280
+ args['writable'] = writable
281
+
282
+ result = self.vm.qmp('block-export-add', args)
283
+ self.assert_qmp(result, 'return', {})
284
+
285
+ def test_default_settings(self):
286
+ with self.run_server():
287
+ self.add_export('r')
288
+ self.add_export('w', writable=True)
289
+ with open_nbd('r') as h:
290
+ self.assertTrue(h.can_multi_conn())
291
+ with open_nbd('w') as h:
292
+ self.assertTrue(h.can_multi_conn())
293
+
294
+ def test_limited_connections(self):
295
+ with self.run_server(max_connections=1):
296
+ self.add_export('r')
297
+ self.add_export('w', writable=True)
298
+ with open_nbd('r') as h:
299
+ self.assertFalse(h.can_multi_conn())
300
+ with open_nbd('w') as h:
301
+ self.assertFalse(h.can_multi_conn())
302
+
303
+ def test_parallel_writes(self):
304
+ with self.run_server():
305
+ self.add_export('w', writable=True)
306
+
307
+ clients = [nbd.NBD() for _ in range(3)]
308
+ for c in clients:
309
+ c.connect_uri(nbd_uri.format('w'))
310
+ self.assertTrue(c.can_multi_conn())
311
+
312
+ initial_data = clients[0].pread(1024 * 1024, 0)
313
+ self.assertEqual(initial_data, b'\x01' * 1024 * 1024)
314
+
315
+ updated_data = b'\x03' * 1024 * 1024
316
+ clients[1].pwrite(updated_data, 0)
317
+ clients[2].flush()
318
+ current_data = clients[0].pread(1024 * 1024, 0)
319
+
320
+ self.assertEqual(updated_data, current_data)
321
+
322
+ for i in range(3):
323
+ clients[i].shutdown()
324
+
325
+
326
+if __name__ == '__main__':
327
+ try:
328
+ # Easier to use libnbd than to try and set up parallel
329
+ # 'qemu-nbd --list' or 'qemu-io' processes, but not all systems
330
+ # have libnbd installed.
331
+ import nbd # type: ignore
332
+
333
+ iotests.main(supported_fmts=['qcow2'])
334
+ except ImportError:
335
+ iotests.notrun('libnbd not installed')
336
diff --git a/tests/qemu-iotests/tests/nbd-multiconn.out b/tests/qemu-iotests/tests/nbd-multiconn.out
175
new file mode 100644
337
new file mode 100644
176
index XXXXXXX..XXXXXXX
338
index XXXXXXX..XXXXXXX
177
--- /dev/null
339
--- /dev/null
178
+++ b/tests/qemu-iotests/247.out
340
+++ b/tests/qemu-iotests/tests/nbd-multiconn.out
179
@@ -XXX,XX +XXX,XX @@
341
@@ -XXX,XX +XXX,XX @@
180
+QA output created by 247
342
+...
181
+
343
+----------------------------------------------------------------------
182
+=== Try commit to backing file with auto-read-only ===
344
+Ran 3 tests
183
+
345
+
184
+Formatting 'TEST_DIR/t.IMGFMT.0', fmt=IMGFMT size=134217728
346
+OK
185
+Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=134217728
347
diff --git a/tests/qemu-iotests/tests/nbd-qemu-allocation.out b/tests/qemu-iotests/tests/nbd-qemu-allocation.out
186
+Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=134217728
348
index XXXXXXX..XXXXXXX 100644
187
+Formatting 'TEST_DIR/t.IMGFMT.3', fmt=IMGFMT size=134217728
349
--- a/tests/qemu-iotests/tests/nbd-qemu-allocation.out
188
+Formatting 'TEST_DIR/t.IMGFMT.4', fmt=IMGFMT size=134217728
350
+++ b/tests/qemu-iotests/tests/nbd-qemu-allocation.out
189
+QMP_VERSION
351
@@ -XXX,XX +XXX,XX @@ wrote 2097152/2097152 bytes at offset 1048576
190
+{"return": {}}
352
exports available: 1
191
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
353
export: ''
192
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
354
size: 4194304
193
+{"return": {}}
355
- flags: 0x58f ( readonly flush fua df multi cache )
194
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
356
+ flags: 0x48f ( readonly flush fua df cache )
195
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
357
min block: 1
196
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 134217728, "offset": 134217728, "speed": 0, "type": "commit"}}
358
opt block: 4096
197
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
359
max block: 33554432
198
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
199
+{"return": {}}
200
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
201
+*** done
202
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
203
index XXXXXXX..XXXXXXX 100644
204
--- a/tests/qemu-iotests/group
205
+++ b/tests/qemu-iotests/group
206
@@ -XXX,XX +XXX,XX @@
207
244 rw auto quick
208
245 rw auto
209
246 rw auto quick
210
+247 rw auto quick
211
--
360
--
212
2.20.1
361
2.35.3
213
214
diff view generated by jsdifflib
1
From: Sergio Lopez <slp@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
While child_job_drained_begin() calls to job_pause(), the job doesn't
3
common.rc has some complicated logic to find the common.config that
4
actually transition between states until it runs again and reaches a
4
dates back to xfstests and is completely unnecessary now. Just include
5
pause point. This means bdrv_drained_begin() may return with some jobs
5
the contents of the file.
6
using the node still having 'busy == true'.
7
6
8
As a consequence, block_job_detach_aio_context() may get into a
7
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9
deadlock, waiting for the job to be actually paused, while the coroutine
8
Message-Id: <20220505094723.732116-1-pbonzini@redhat.com>
10
servicing the job is yielding and doesn't get the opportunity to get
11
scheduled again. This situation can be reproduced by issuing a
12
'block-commit' immediately followed by a 'device_del'.
13
14
To ensure bdrv_drained_begin() only returns when the jobs have been
15
paused, we change mirror_drained_poll() to only confirm it's quiesced
16
when job->paused == true and there aren't any in-flight requests, except
17
if we reached that point by a drained section initiated by the
18
mirror/commit job itself.
19
20
The other block jobs shouldn't need any changes, as the default
21
drained_poll() behavior is to only confirm it's quiesced if the job is
22
not busy or completed.
23
24
Signed-off-by: Sergio Lopez <slp@redhat.com>
25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26
---
10
---
27
block/mirror.c | 16 ++++++++++++++++
11
tests/qemu-iotests/common.config | 41 --------------------------------
28
1 file changed, 16 insertions(+)
12
tests/qemu-iotests/common.rc | 31 ++++++++++++++----------
13
2 files changed, 19 insertions(+), 53 deletions(-)
14
delete mode 100644 tests/qemu-iotests/common.config
29
15
30
diff --git a/block/mirror.c b/block/mirror.c
16
diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config
17
deleted file mode 100644
18
index XXXXXXX..XXXXXXX
19
--- a/tests/qemu-iotests/common.config
20
+++ /dev/null
21
@@ -XXX,XX +XXX,XX @@
22
-#!/usr/bin/env bash
23
-#
24
-# Copyright (C) 2009 Red Hat, Inc.
25
-# Copyright (c) 2000-2003,2006 Silicon Graphics, Inc. All Rights Reserved.
26
-#
27
-# This program is free software; you can redistribute it and/or
28
-# modify it under the terms of the GNU General Public License as
29
-# published by the Free Software Foundation.
30
-#
31
-# This program is distributed in the hope that it would be useful,
32
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
33
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34
-# GNU General Public License for more details.
35
-#
36
-# You should have received a copy of the GNU General Public License
37
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
38
-#
39
-# all tests should use a common language setting to prevent golden
40
-# output mismatches.
41
-export LANG=C
42
-
43
-PATH=".:$PATH"
44
-
45
-HOSTOS=$(uname -s)
46
-arch=$(uname -m)
47
-[[ "$arch" =~ "ppc64" ]] && qemu_arch=ppc64 || qemu_arch="$arch"
48
-
49
-# make sure we have a standard umask
50
-umask 022
51
-
52
-_optstr_add()
53
-{
54
- if [ -n "$1" ]; then
55
- echo "$1,$2"
56
- else
57
- echo "$2"
58
- fi
59
-}
60
-
61
-# make sure this script returns success
62
-true
63
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
31
index XXXXXXX..XXXXXXX 100644
64
index XXXXXXX..XXXXXXX 100644
32
--- a/block/mirror.c
65
--- a/tests/qemu-iotests/common.rc
33
+++ b/block/mirror.c
66
+++ b/tests/qemu-iotests/common.rc
34
@@ -XXX,XX +XXX,XX @@ typedef struct MirrorBlockJob {
67
@@ -XXX,XX +XXX,XX @@
35
bool initial_zeroing_ongoing;
68
# along with this program. If not, see <http://www.gnu.org/licenses/>.
36
int in_active_write_counter;
69
#
37
bool prepared;
70
38
+ bool in_drain;
71
+export LANG=C
39
} MirrorBlockJob;
72
+
40
73
+PATH=".:$PATH"
41
typedef struct MirrorBDSOpaque {
74
+
42
@@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job)
75
+HOSTOS=$(uname -s)
43
76
+arch=$(uname -m)
44
/* The mirror job has no requests in flight any more, but we need to
77
+[[ "$arch" =~ "ppc64" ]] && qemu_arch=ppc64 || qemu_arch="$arch"
45
* drain potential other users of the BDS before changing the graph. */
78
+
46
+ assert(s->in_drain);
79
+# make sure we have a standard umask
47
bdrv_drained_begin(target_bs);
80
+umask 022
48
bdrv_replace_node(to_replace, target_bs, &local_err);
81
+
49
bdrv_drained_end(target_bs);
82
# bail out, setting up .notrun file
50
@@ -XXX,XX +XXX,XX @@ static int mirror_exit_common(Job *job)
83
_notrun()
51
bs_opaque->job = NULL;
52
53
bdrv_drained_end(src);
54
+ s->in_drain = false;
55
bdrv_unref(mirror_top_bs);
56
bdrv_unref(src);
57
58
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
59
*/
60
trace_mirror_before_drain(s, cnt);
61
62
+ s->in_drain = true;
63
bdrv_drained_begin(bs);
64
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
65
if (cnt > 0 || mirror_flush(s) < 0) {
66
bdrv_drained_end(bs);
67
+ s->in_drain = false;
68
continue;
69
}
70
71
@@ -XXX,XX +XXX,XX @@ immediate_exit:
72
bdrv_dirty_iter_free(s->dbi);
73
74
if (need_drain) {
75
+ s->in_drain = true;
76
bdrv_drained_begin(bs);
77
}
78
79
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_pause(Job *job)
80
static bool mirror_drained_poll(BlockJob *job)
81
{
84
{
82
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
85
@@ -XXX,XX +XXX,XX @@ peek_file_raw()
83
+
86
dd if="$1" bs=1 skip="$2" count="$3" status=none
84
+ /* If the job isn't paused nor cancelled, we can't be sure that it won't
85
+ * issue more requests. We make an exception if we've reached this point
86
+ * from one of our own drain sections, to avoid a deadlock waiting for
87
+ * ourselves.
88
+ */
89
+ if (!s->common.job.paused && !s->common.job.cancelled && !s->in_drain) {
90
+ return true;
91
+ }
92
+
93
return !!s->in_flight;
94
}
87
}
95
88
89
-config=common.config
90
-test -f $config || config=../common.config
91
-if ! test -f $config
92
-then
93
- echo "$0: failed to find common.config"
94
- exit 1
95
-fi
96
-if ! . $config
97
- then
98
- echo "$0: failed to source common.config"
99
- exit 1
100
-fi
101
+_optstr_add()
102
+{
103
+ if [ -n "$1" ]; then
104
+ echo "$1,$2"
105
+ else
106
+ echo "$2"
107
+ fi
108
+}
109
110
# Set the variables to the empty string to turn Valgrind off
111
# for specific processes, e.g.
96
--
112
--
97
2.20.1
113
2.35.3
98
99
diff view generated by jsdifflib
Deleted patch
1
We were trying to check whether bdrv_open_blockdev_ref() returned
2
success, but accidentally checked the wrong variable. Spotted by
3
Coverity (CID 1399703).
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
8
---
9
block/qcow2.c | 2 +-
10
1 file changed, 1 insertion(+), 1 deletion(-)
11
12
diff --git a/block/qcow2.c b/block/qcow2.c
13
index XXXXXXX..XXXXXXX 100644
14
--- a/block/qcow2.c
15
+++ b/block/qcow2.c
16
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
17
goto out;
18
}
19
data_bs = bdrv_open_blockdev_ref(qcow2_opts->data_file, errp);
20
- if (bs == NULL) {
21
+ if (data_bs == NULL) {
22
ret = -EIO;
23
goto out;
24
}
25
--
26
2.20.1
27
28
diff view generated by jsdifflib
Deleted patch
1
Coverity doesn't like that the return value of bdrv_check_update_perm()
2
stays unused only in this place (CID 1399710).
3
1
4
Even if checking local_err should be equivalent to checking ret < 0,
5
let's switch to using the return value to be more consistent (and in
6
case of a bug somewhere down the call chain, forgetting to assign errp
7
is more likely than returning 0 for an error case).
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
11
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
12
Reviewed-by: Markus Armbruster <armbru@redhat.com>
13
---
14
block.c | 7 +++----
15
1 file changed, 3 insertions(+), 4 deletions(-)
16
17
diff --git a/block.c b/block.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/block.c
20
+++ b/block.c
21
@@ -XXX,XX +XXX,XX @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
22
QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) {
23
/* Check whether we are allowed to switch c from top to base */
24
GSList *ignore_children = g_slist_prepend(NULL, c);
25
- bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
26
- ignore_children, &local_err);
27
+ ret = bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
28
+ ignore_children, &local_err);
29
g_slist_free(ignore_children);
30
- if (local_err) {
31
- ret = -EPERM;
32
+ if (ret < 0) {
33
error_report_err(local_err);
34
goto exit;
35
}
36
--
37
2.20.1
38
39
diff view generated by jsdifflib