1
The following changes since commit 7bc8f9734213b76e76631a483be13d6737c2adbc:
1
The following changes since commit 9a7beaad3dbba982f7a461d676b55a5c3851d312:
2
2
3
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20191025' into staging (2019-10-25 13:12:16 +0100)
3
Merge remote-tracking branch 'remotes/alistair/tags/pull-riscv-to-apply-20210304' into staging (2021-03-05 10:47:46 +0000)
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 5e9785505210e2477e590e61b1ab100d0ec22b01:
9
for you to fetch changes up to 67bedc3aed5c455b629c2cb5f523b536c46adff9:
10
10
11
qcow2: Fix corruption bug in qcow2_detect_metadata_preallocation() (2019-10-25 15:18:55 +0200)
11
docs: qsd: Explain --export nbd,name=... default (2021-03-05 17:09:46 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches:
15
15
16
- qcow2: Fix data corruption bug that is triggered in partial cluster
16
- qemu-storage-daemon: add --pidfile option
17
allocation with default options
17
- qemu-storage-daemon: CLI error messages include the option name now
18
- qapi: add support for blkreplay driver
18
- vhost-user-blk export: Misc fixes, added test cases
19
- doc: Describe missing generic -blockdev options
19
- docs: Improvements for qemu-storage-daemon documentation
20
- iotests: Fix 118 when run as root
20
- parallels: load bitmap extension
21
- Minor code cleanups
21
- backup-top: Don't crash on post-finalize accesses
22
- iotests improvements
22
23
23
----------------------------------------------------------------
24
----------------------------------------------------------------
24
Kevin Wolf (5):
25
Alberto Garcia (1):
25
iotests: Skip read-only cases in 118 when run as root
26
iotests: Drop deprecated 'props' from object-add
26
blockdev: Use error_report() in hmp_commit()
27
doc: Describe missing generic -blockdev options
28
coroutine: Add qemu_co_mutex_assert_locked()
29
qcow2: Fix corruption bug in qcow2_detect_metadata_preallocation()
30
27
31
Pavel Dovgaluk (1):
28
Coiby Xu (1):
32
qapi: add support for blkreplay driver
29
test: new qTest case to test the vhost-user-blk-server
33
30
34
Vladimir Sementsov-Ogievskiy (1):
31
Eric Blake (1):
35
block/backup: drop dead code from backup_job_create
32
iotests: Fix up python style in 300
36
33
37
qapi/block-core.json | 18 ++++++++++++++++--
34
Kevin Wolf (1):
38
include/qemu/coroutine.h | 15 +++++++++++++++
35
docs: qsd: Explain --export nbd,name=... default
39
block/backup.c | 5 +----
36
40
block/qcow2-refcount.c | 2 ++
37
Max Reitz (3):
41
block/qcow2.c | 3 ++-
38
backup: Remove nodes from job in .clean()
42
blockdev.c | 7 +++----
39
backup-top: Refuse I/O in inactive state
43
qemu-options.hx | 22 +++++++++++++++++++++-
40
iotests/283: Check that finalize drops backup-top
44
tests/qemu-iotests/118 | 3 +++
41
45
tests/qemu-iotests/iotests.py | 10 ++++++++++
42
Paolo Bonzini (2):
46
9 files changed, 73 insertions(+), 12 deletions(-)
43
storage-daemon: report unexpected arguments on the fly
44
storage-daemon: include current command line option in the errors
45
46
Stefan Hajnoczi (14):
47
qemu-storage-daemon: add --pidfile option
48
docs: show how to spawn qemu-storage-daemon with fd passing
49
docs: replace insecure /tmp examples in qsd docs
50
vhost-user-blk: fix blkcfg->num_queues endianness
51
libqtest: add qtest_socket_server()
52
libqtest: add qtest_kill_qemu()
53
libqtest: add qtest_remove_abrt_handler()
54
tests/qtest: add multi-queue test case to vhost-user-blk-test
55
block/export: fix blk_size double byteswap
56
block/export: use VIRTIO_BLK_SECTOR_BITS
57
block/export: fix vhost-user-blk export sector number calculation
58
block/export: port virtio-blk discard/write zeroes input validation
59
vhost-user-blk-test: test discard/write zeroes invalid inputs
60
block/export: port virtio-blk read/write range check
61
62
Stefano Garzarella (1):
63
blockjob: report a better error message
64
65
Vladimir Sementsov-Ogievskiy (7):
66
qcow2-bitmap: make bytes_covered_by_bitmap_cluster() public
67
parallels.txt: fix bitmap L1 table description
68
block/parallels: BDRVParallelsState: add cluster_size field
69
parallels: support bitmap extension for read-only mode
70
iotests.py: add unarchive_sample_image() helper
71
iotests: add parallels-read-bitmap test
72
MAINTAINERS: update parallels block driver
73
74
docs/interop/parallels.txt | 28 +-
75
docs/tools/qemu-storage-daemon.rst | 68 +-
76
block/parallels.h | 7 +-
77
include/block/dirty-bitmap.h | 2 +
78
tests/qtest/libqos/libqtest.h | 37 +
79
tests/qtest/libqos/vhost-user-blk.h | 48 +
80
block/backup-top.c | 10 +
81
block/backup.c | 1 +
82
block/dirty-bitmap.c | 13 +
83
block/export/vhost-user-blk-server.c | 150 +++-
84
block/parallels-ext.c | 300 +++++++
85
block/parallels.c | 26 +-
86
block/qcow2-bitmap.c | 16 +-
87
blockjob.c | 10 +-
88
hw/block/vhost-user-blk.c | 7 +-
89
storage-daemon/qemu-storage-daemon.c | 56 +-
90
tests/qtest/libqos/vhost-user-blk.c | 130 +++
91
tests/qtest/libqtest.c | 82 +-
92
tests/qtest/vhost-user-blk-test.c | 983 +++++++++++++++++++++
93
tests/qemu-iotests/iotests.py | 10 +
94
MAINTAINERS | 5 +
95
block/meson.build | 3 +-
96
tests/qemu-iotests/087 | 8 +-
97
tests/qemu-iotests/184 | 18 +-
98
tests/qemu-iotests/218 | 2 +-
99
tests/qemu-iotests/235 | 2 +-
100
tests/qemu-iotests/245 | 4 +-
101
tests/qemu-iotests/258 | 6 +-
102
tests/qemu-iotests/258.out | 4 +-
103
tests/qemu-iotests/283 | 53 ++
104
tests/qemu-iotests/283.out | 15 +
105
tests/qemu-iotests/295 | 2 +-
106
tests/qemu-iotests/296 | 2 +-
107
tests/qemu-iotests/300 | 10 +-
108
.../sample_images/parallels-with-bitmap.bz2 | Bin 0 -> 203 bytes
109
.../sample_images/parallels-with-bitmap.sh | 51 ++
110
tests/qemu-iotests/tests/parallels-read-bitmap | 55 ++
111
tests/qemu-iotests/tests/parallels-read-bitmap.out | 6 +
112
tests/qtest/libqos/meson.build | 1 +
113
tests/qtest/meson.build | 4 +
114
40 files changed, 2098 insertions(+), 137 deletions(-)
115
create mode 100644 tests/qtest/libqos/vhost-user-blk.h
116
create mode 100644 block/parallels-ext.c
117
create mode 100644 tests/qtest/libqos/vhost-user-blk.c
118
create mode 100644 tests/qtest/vhost-user-blk-test.c
119
create mode 100644 tests/qemu-iotests/sample_images/parallels-with-bitmap.bz2
120
create mode 100755 tests/qemu-iotests/sample_images/parallels-with-bitmap.sh
121
create mode 100755 tests/qemu-iotests/tests/parallels-read-bitmap
122
create mode 100644 tests/qemu-iotests/tests/parallels-read-bitmap.out
47
123
48
124
diff view generated by jsdifflib
New patch
1
1
From: Alberto Garcia <berto@igalia.com>
2
3
Signed-off-by: Alberto Garcia <berto@igalia.com>
4
Message-Id: <20210222115737.2993-1-berto@igalia.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
7
tests/qemu-iotests/087 | 8 ++------
8
tests/qemu-iotests/184 | 18 ++++++------------
9
tests/qemu-iotests/218 | 2 +-
10
tests/qemu-iotests/235 | 2 +-
11
tests/qemu-iotests/245 | 4 ++--
12
tests/qemu-iotests/258 | 6 +++---
13
tests/qemu-iotests/258.out | 4 ++--
14
tests/qemu-iotests/295 | 2 +-
15
tests/qemu-iotests/296 | 2 +-
16
9 files changed, 19 insertions(+), 29 deletions(-)
17
18
diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087
19
index XXXXXXX..XXXXXXX 100755
20
--- a/tests/qemu-iotests/087
21
+++ b/tests/qemu-iotests/087
22
@@ -XXX,XX +XXX,XX @@ run_qemu <<EOF
23
"arguments": {
24
"qom-type": "secret",
25
"id": "sec0",
26
- "props": {
27
- "data": "123456"
28
- }
29
+ "data": "123456"
30
}
31
}
32
{ "execute": "blockdev-add",
33
@@ -XXX,XX +XXX,XX @@ run_qemu <<EOF
34
"arguments": {
35
"qom-type": "secret",
36
"id": "sec0",
37
- "props": {
38
- "data": "123456"
39
- }
40
+ "data": "123456"
41
}
42
}
43
{ "execute": "blockdev-add",
44
diff --git a/tests/qemu-iotests/184 b/tests/qemu-iotests/184
45
index XXXXXXX..XXXXXXX 100755
46
--- a/tests/qemu-iotests/184
47
+++ b/tests/qemu-iotests/184
48
@@ -XXX,XX +XXX,XX @@ run_qemu <<EOF
49
"arguments": {
50
"qom-type": "throttle-group",
51
"id": "group0",
52
- "props": {
53
- "limits" : {
54
- "iops-total": 1000
55
- }
56
+ "limits" : {
57
+ "iops-total": 1000
58
}
59
}
60
}
61
@@ -XXX,XX +XXX,XX @@ run_qemu <<EOF
62
"arguments": {
63
"qom-type": "throttle-group",
64
"id": "group0",
65
- "props" : {
66
- "limits": {
67
- "iops-total": 1000
68
- }
69
+ "limits": {
70
+ "iops-total": 1000
71
}
72
}
73
}
74
@@ -XXX,XX +XXX,XX @@ run_qemu <<EOF
75
"arguments": {
76
"qom-type": "throttle-group",
77
"id": "group0",
78
- "props" : {
79
- "limits": {
80
- "iops-total": 1000
81
- }
82
+ "limits": {
83
+ "iops-total": 1000
84
}
85
}
86
}
87
diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218
88
index XXXXXXX..XXXXXXX 100755
89
--- a/tests/qemu-iotests/218
90
+++ b/tests/qemu-iotests/218
91
@@ -XXX,XX +XXX,XX @@ with iotests.VM() as vm, \
92
vm.launch()
93
94
ret = vm.qmp('object-add', qom_type='throttle-group', id='tg',
95
- props={'x-bps-read': 4096})
96
+ limits={'bps-read': 4096})
97
assert ret['return'] == {}
98
99
ret = vm.qmp('blockdev-add',
100
diff --git a/tests/qemu-iotests/235 b/tests/qemu-iotests/235
101
index XXXXXXX..XXXXXXX 100755
102
--- a/tests/qemu-iotests/235
103
+++ b/tests/qemu-iotests/235
104
@@ -XXX,XX +XXX,XX @@ vm.add_args('-drive', 'id=src,file=' + disk)
105
vm.launch()
106
107
log(vm.qmp('object-add', qom_type='throttle-group', id='tg0',
108
- props={ 'x-bps-total': size }))
109
+ limits={'bps-total': size}))
110
111
log(vm.qmp('blockdev-add',
112
**{ 'node-name': 'target',
113
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
114
index XXXXXXX..XXXXXXX 100755
115
--- a/tests/qemu-iotests/245
116
+++ b/tests/qemu-iotests/245
117
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
118
###### throttle ######
119
######################
120
opts = { 'qom-type': 'throttle-group', 'id': 'group0',
121
- 'props': { 'limits': { 'iops-total': 1000 } } }
122
+ 'limits': { 'iops-total': 1000 } }
123
result = self.vm.qmp('object-add', conv_keys = False, **opts)
124
self.assert_qmp(result, 'return', {})
125
126
opts = { 'qom-type': 'throttle-group', 'id': 'group1',
127
- 'props': { 'limits': { 'iops-total': 2000 } } }
128
+ 'limits': { 'iops-total': 2000 } }
129
result = self.vm.qmp('object-add', conv_keys = False, **opts)
130
self.assert_qmp(result, 'return', {})
131
132
diff --git a/tests/qemu-iotests/258 b/tests/qemu-iotests/258
133
index XXXXXXX..XXXXXXX 100755
134
--- a/tests/qemu-iotests/258
135
+++ b/tests/qemu-iotests/258
136
@@ -XXX,XX +XXX,XX @@ def test_concurrent_finish(write_to_stream_node):
137
vm.qmp_log('object-add',
138
qom_type='throttle-group',
139
id='tg',
140
- props={
141
- 'x-iops-write': 1,
142
- 'x-iops-write-max': 1
143
+ limits={
144
+ 'iops-write': 1,
145
+ 'iops-write-max': 1
146
})
147
148
vm.qmp_log('blockdev-add',
149
diff --git a/tests/qemu-iotests/258.out b/tests/qemu-iotests/258.out
150
index XXXXXXX..XXXXXXX 100644
151
--- a/tests/qemu-iotests/258.out
152
+++ b/tests/qemu-iotests/258.out
153
@@ -XXX,XX +XXX,XX @@ Running tests:
154
155
=== Commit and stream finish concurrently (letting stream write) ===
156
157
-{"execute": "object-add", "arguments": {"id": "tg", "props": {"x-iops-write": 1, "x-iops-write-max": 1}, "qom-type": "throttle-group"}}
158
+{"execute": "object-add", "arguments": {"id": "tg", "limits": {"iops-write": 1, "iops-write-max": 1}, "qom-type": "throttle-group"}}
159
{"return": {}}
160
{"execute": "blockdev-add", "arguments": {"backing": {"backing": {"backing": {"backing": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-node0.img"}, "node-name": "node0"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node1.img"}, "node-name": "node1"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node2.img"}, "node-name": "node2"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node3.img"}, "node-name": "node3"}, "driver": "IMGFMT", "file": {"driver": "throttle", "file": {"driver": "file", "filename": "TEST_DIR/PID-node4.img"}, "throttle-group": "tg"}, "node-name": "node4"}}
161
{"return": {}}
162
@@ -XXX,XX +XXX,XX @@ Running tests:
163
164
=== Commit and stream finish concurrently (letting commit write) ===
165
166
-{"execute": "object-add", "arguments": {"id": "tg", "props": {"x-iops-write": 1, "x-iops-write-max": 1}, "qom-type": "throttle-group"}}
167
+{"execute": "object-add", "arguments": {"id": "tg", "limits": {"iops-write": 1, "iops-write-max": 1}, "qom-type": "throttle-group"}}
168
{"return": {}}
169
{"execute": "blockdev-add", "arguments": {"backing": {"backing": {"backing": {"backing": {"driver": "raw", "file": {"driver": "throttle", "file": {"driver": "file", "filename": "TEST_DIR/PID-node0.img"}, "throttle-group": "tg"}, "node-name": "node0"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node1.img"}, "node-name": "node1"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node2.img"}, "node-name": "node2"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node3.img"}, "node-name": "node3"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-node4.img"}, "node-name": "node4"}}
170
{"return": {}}
171
diff --git a/tests/qemu-iotests/295 b/tests/qemu-iotests/295
172
index XXXXXXX..XXXXXXX 100755
173
--- a/tests/qemu-iotests/295
174
+++ b/tests/qemu-iotests/295
175
@@ -XXX,XX +XXX,XX @@ class Secret:
176
177
def to_qmp_object(self):
178
return { "qom_type" : "secret", "id": self.id(),
179
- "props": { "data": self.secret() } }
180
+ "data": self.secret() }
181
182
################################################################################
183
class EncryptionSetupTestCase(iotests.QMPTestCase):
184
diff --git a/tests/qemu-iotests/296 b/tests/qemu-iotests/296
185
index XXXXXXX..XXXXXXX 100755
186
--- a/tests/qemu-iotests/296
187
+++ b/tests/qemu-iotests/296
188
@@ -XXX,XX +XXX,XX @@ class Secret:
189
190
def to_qmp_object(self):
191
return { "qom_type" : "secret", "id": self.id(),
192
- "props": { "data": self.secret() } }
193
+ "data": self.secret() }
194
195
################################################################################
196
197
--
198
2.29.2
199
200
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Max Reitz <mreitz@redhat.com>
2
2
3
After commit 00e30f05de1d195, there is no more "goto error" points
3
The block job holds a reference to the backup-top node (because it is
4
after job creation, so after "error:" @job is always NULL and we don't
4
passed as the main job BDS to block_job_create()). Therefore,
5
need roll-back job creation.
5
bdrv_backup_top_drop() cannot delete the backup-top node (replacing it
6
by its child does not affect the job parent, because that has
7
.stay_at_node set). That is a problem, because all of its I/O functions
8
assume the BlockCopyState (s->bcs) to be valid and that it has a
9
filtered child; but after bdrv_backup_top_drop(), neither of those
10
things are true.
6
11
7
Reported-by: Coverity (CID 1406402)
12
It does not make sense to add new parents to backup-top after
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
backup_clean(), so we should detach it from the job before
9
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
14
bdrv_backup_top_drop(). Because there is no function to do that for a
15
single node, just detach all of the job's nodes -- the job does not do
16
anything past backup_clean() anyway.
17
18
Signed-off-by: Max Reitz <mreitz@redhat.com>
19
Message-Id: <20210219153348.41861-2-mreitz@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
21
---
12
block/backup.c | 5 +----
22
block/backup.c | 1 +
13
1 file changed, 1 insertion(+), 4 deletions(-)
23
1 file changed, 1 insertion(+)
14
24
15
diff --git a/block/backup.c b/block/backup.c
25
diff --git a/block/backup.c b/block/backup.c
16
index XXXXXXX..XXXXXXX 100644
26
index XXXXXXX..XXXXXXX 100644
17
--- a/block/backup.c
27
--- a/block/backup.c
18
+++ b/block/backup.c
28
+++ b/block/backup.c
19
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
29
@@ -XXX,XX +XXX,XX @@ static void backup_abort(Job *job)
20
if (sync_bitmap) {
30
static void backup_clean(Job *job)
21
bdrv_reclaim_dirty_bitmap(sync_bitmap, NULL);
31
{
22
}
32
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
23
- if (job) {
33
+ block_job_remove_all_bdrv(&s->common);
24
- backup_clean(&job->common.job);
34
bdrv_backup_top_drop(s->backup_top);
25
- job_early_fail(&job->common.job);
35
}
26
- } else if (backup_top) {
27
+ if (backup_top) {
28
bdrv_backup_top_drop(backup_top);
29
}
30
36
31
--
37
--
32
2.20.1
38
2.29.2
33
39
34
40
diff view generated by jsdifflib
New patch
1
From: Max Reitz <mreitz@redhat.com>
1
2
3
When the backup-top node transitions from active to inactive in
4
bdrv_backup_top_drop(), the BlockCopyState is freed and the filtered
5
child is removed, so the node effectively becomes unusable.
6
7
However, noone told its I/O functions this, so they will happily
8
continue accessing bs->backing and s->bcs. Prevent that by aborting
9
early when s->active is false.
10
11
(After the preceding patch, the node should be gone after
12
bdrv_backup_top_drop(), so this should largely be a theoretical problem.
13
But still, better to be safe than sorry, and also I think it just makes
14
sense to check s->active in the I/O functions.)
15
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
17
Message-Id: <20210219153348.41861-3-mreitz@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
20
block/backup-top.c | 10 ++++++++++
21
1 file changed, 10 insertions(+)
22
23
diff --git a/block/backup-top.c b/block/backup-top.c
24
index XXXXXXX..XXXXXXX 100644
25
--- a/block/backup-top.c
26
+++ b/block/backup-top.c
27
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_co_preadv(
28
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
29
QEMUIOVector *qiov, int flags)
30
{
31
+ BDRVBackupTopState *s = bs->opaque;
32
+
33
+ if (!s->active) {
34
+ return -EIO;
35
+ }
36
+
37
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
38
}
39
40
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
41
BDRVBackupTopState *s = bs->opaque;
42
uint64_t off, end;
43
44
+ if (!s->active) {
45
+ return -EIO;
46
+ }
47
+
48
if (flags & BDRV_REQ_WRITE_UNCHANGED) {
49
return 0;
50
}
51
--
52
2.29.2
53
54
diff view generated by jsdifflib
New patch
1
From: Max Reitz <mreitz@redhat.com>
1
2
3
Without any of HEAD^ or HEAD^^ applied, qemu will most likely crash on
4
the qemu-io invocation, for a variety of immediate reasons. The
5
underlying problem is generally a use-after-free access into
6
backup-top's BlockCopyState.
7
8
With only HEAD^ applied, qemu-io will run into an EIO (which is not
9
capture by the output, but you can see that the qemu-io invocation will
10
be accepted (i.e., qemu-io will run) in contrast to the reference
11
output, where the node name cannot be found), and qemu will then crash
12
in query-named-block-nodes: bdrv_get_allocated_file_size() detects
13
backup-top to be a filter and passes the request through to its child.
14
However, after bdrv_backup_top_drop(), that child is NULL, so the
15
recursive call crashes.
16
17
With HEAD^^ applied, this test should pass.
18
19
Signed-off-by: Max Reitz <mreitz@redhat.com>
20
Message-Id: <20210219153348.41861-4-mreitz@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
23
tests/qemu-iotests/283 | 53 ++++++++++++++++++++++++++++++++++++++
24
tests/qemu-iotests/283.out | 15 +++++++++++
25
2 files changed, 68 insertions(+)
26
27
diff --git a/tests/qemu-iotests/283 b/tests/qemu-iotests/283
28
index XXXXXXX..XXXXXXX 100755
29
--- a/tests/qemu-iotests/283
30
+++ b/tests/qemu-iotests/283
31
@@ -XXX,XX +XXX,XX @@ vm.qmp_log('blockdev-add', **{
32
vm.qmp_log('blockdev-backup', sync='full', device='source', target='target')
33
34
vm.shutdown()
35
+
36
+
37
+print('\n=== backup-top should be gone after job-finalize ===\n')
38
+
39
+# Check that the backup-top node is gone after job-finalize.
40
+#
41
+# During finalization, the node becomes inactive and can no longer
42
+# function. If it is still present, new parents might be attached, and
43
+# there would be no meaningful way to handle their I/O requests.
44
+
45
+vm = iotests.VM()
46
+vm.launch()
47
+
48
+vm.qmp_log('blockdev-add', **{
49
+ 'node-name': 'source',
50
+ 'driver': 'null-co',
51
+})
52
+
53
+vm.qmp_log('blockdev-add', **{
54
+ 'node-name': 'target',
55
+ 'driver': 'null-co',
56
+})
57
+
58
+vm.qmp_log('blockdev-backup',
59
+ job_id='backup',
60
+ device='source',
61
+ target='target',
62
+ sync='full',
63
+ filter_node_name='backup-filter',
64
+ auto_finalize=False,
65
+ auto_dismiss=False)
66
+
67
+vm.event_wait('BLOCK_JOB_PENDING', 5.0)
68
+
69
+# The backup-top filter should still be present prior to finalization
70
+assert vm.node_info('backup-filter') is not None
71
+
72
+vm.qmp_log('job-finalize', id='backup')
73
+vm.event_wait('BLOCK_JOB_COMPLETED', 5.0)
74
+
75
+# The filter should be gone now. Check that by trying to access it
76
+# with qemu-io (which will most likely crash qemu if it is still
77
+# there.).
78
+vm.qmp_log('human-monitor-command',
79
+ command_line='qemu-io backup-filter "write 0 1M"')
80
+
81
+# (Also, do an explicit check.)
82
+assert vm.node_info('backup-filter') is None
83
+
84
+vm.qmp_log('job-dismiss', id='backup')
85
+vm.event_wait('JOB_STATUS_CHANGE', 5.0, {'data': {'status': 'null'}})
86
+
87
+vm.shutdown()
88
diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out
89
index XXXXXXX..XXXXXXX 100644
90
--- a/tests/qemu-iotests/283.out
91
+++ b/tests/qemu-iotests/283.out
92
@@ -XXX,XX +XXX,XX @@
93
{"return": {}}
94
{"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}}
95
{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by other as 'image', which uses 'write' on base"}}
96
+
97
+=== backup-top should be gone after job-finalize ===
98
+
99
+{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "source"}}
100
+{"return": {}}
101
+{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "target"}}
102
+{"return": {}}
103
+{"execute": "blockdev-backup", "arguments": {"auto-dismiss": false, "auto-finalize": false, "device": "source", "filter-node-name": "backup-filter", "job-id": "backup", "sync": "full", "target": "target"}}
104
+{"return": {}}
105
+{"execute": "job-finalize", "arguments": {"id": "backup"}}
106
+{"return": {}}
107
+{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io backup-filter \"write 0 1M\""}}
108
+{"return": "Error: Cannot find device= nor node_name=backup-filter\r\n"}
109
+{"execute": "job-dismiss", "arguments": {"id": "backup"}}
110
+{"return": {}}
111
--
112
2.29.2
113
114
diff view generated by jsdifflib
New patch
1
From: Eric Blake <eblake@redhat.com>
1
2
3
Break some long lines, and relax our type hints to be more generic to
4
any JSON, in order to more easily permit the additional JSON depth now
5
possible in migration parameters. Detected by iotest 297.
6
7
Fixes: ca4bfec41d56
8
(qemu-iotests: 300: Add test case for modifying persistence of bitmap)
9
Reported-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Eric Blake <eblake@redhat.com>
11
Message-Id: <20210215220518.1745469-1-eblake@redhat.com>
12
Reviewed-by: John Snow <jsnow@redhat.com>
13
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
16
tests/qemu-iotests/300 | 10 ++++++----
17
1 file changed, 6 insertions(+), 4 deletions(-)
18
19
diff --git a/tests/qemu-iotests/300 b/tests/qemu-iotests/300
20
index XXXXXXX..XXXXXXX 100755
21
--- a/tests/qemu-iotests/300
22
+++ b/tests/qemu-iotests/300
23
@@ -XXX,XX +XXX,XX @@
24
import os
25
import random
26
import re
27
-from typing import Dict, List, Optional, Union
28
+from typing import Dict, List, Optional
29
30
import iotests
31
32
@@ -XXX,XX +XXX,XX @@ import iotests
33
# pylint: disable=wrong-import-order
34
import qemu
35
36
-BlockBitmapMapping = List[Dict[str, Union[str, List[Dict[str, str]]]]]
37
+BlockBitmapMapping = List[Dict[str, object]]
38
39
mig_sock = os.path.join(iotests.sock_dir, 'mig_sock')
40
41
@@ -XXX,XX +XXX,XX @@ class TestCrossAliasMigration(TestDirtyBitmapMigration):
42
43
class TestAliasTransformMigration(TestDirtyBitmapMigration):
44
"""
45
- Tests the 'transform' option which modifies bitmap persistence on migration.
46
+ Tests the 'transform' option which modifies bitmap persistence on
47
+ migration.
48
"""
49
50
src_node_name = 'node-a'
51
@@ -XXX,XX +XXX,XX @@ class TestAliasTransformMigration(TestDirtyBitmapMigration):
52
bitmaps = self.vm_b.query_bitmaps()
53
54
for node in bitmaps:
55
- bitmaps[node] = sorted(((bmap['name'], bmap['persistent']) for bmap in bitmaps[node]))
56
+ bitmaps[node] = sorted(((bmap['name'], bmap['persistent'])
57
+ for bmap in bitmaps[node]))
58
59
self.assertEqual(bitmaps,
60
{'node-a': [('bmap-a', True), ('bmap-b', False)],
61
--
62
2.29.2
63
64
diff view generated by jsdifflib
New patch
1
From: Stefano Garzarella <sgarzare@redhat.com>
1
2
3
When a block job fails, we report strerror(-job->job.ret) error
4
message, also if the job set an error object.
5
Let's report a better error message using error_get_pretty(job->job.err).
6
7
If an error object was not set, strerror(-job->ret) is used as fallback,
8
as explained in include/qemu/job.h:
9
10
typedef struct Job {
11
...
12
/**
13
* Error object for a failed job.
14
* If job->ret is nonzero and an error object was not set, it will be set
15
* to strerror(-job->ret) during job_completed.
16
*/
17
Error *err;
18
}
19
20
In block_job_query() there can be a transient where 'job.err' is not set
21
by a scheduled bottom half. In that case we use strerror(-job->ret) as it
22
was before.
23
24
Suggested-by: Kevin Wolf <kwolf@redhat.com>
25
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
26
Message-Id: <20210225103633.76746-1-sgarzare@redhat.com>
27
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
28
---
29
blockjob.c | 10 +++++++---
30
1 file changed, 7 insertions(+), 3 deletions(-)
31
32
diff --git a/blockjob.c b/blockjob.c
33
index XXXXXXX..XXXXXXX 100644
34
--- a/blockjob.c
35
+++ b/blockjob.c
36
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
37
info->status = job->job.status;
38
info->auto_finalize = job->job.auto_finalize;
39
info->auto_dismiss = job->job.auto_dismiss;
40
- info->has_error = job->job.ret != 0;
41
- info->error = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL;
42
+ if (job->job.ret) {
43
+ info->has_error = true;
44
+ info->error = job->job.err ?
45
+ g_strdup(error_get_pretty(job->job.err)) :
46
+ g_strdup(strerror(-job->job.ret));
47
+ }
48
return info;
49
}
50
51
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(Notifier *n, void *opaque)
52
}
53
54
if (job->job.ret < 0) {
55
- msg = strerror(-job->job.ret);
56
+ msg = error_get_pretty(job->job.err);
57
}
58
59
qapi_event_send_block_job_completed(job_type(&job->job),
60
--
61
2.29.2
62
63
diff view generated by jsdifflib
New patch
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
2
3
If the first character of optstring is '-', then each nonoption argv
4
element is handled as if it were the argument of an option with character
5
code 1. This removes the reordering of the argv array, and enables usage
6
of loc_set_cmdline to provide better error messages.
7
8
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9
Message-Id: <20210301152844.291799-2-pbonzini@redhat.com>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
storage-daemon/qemu-storage-daemon.c | 9 ++++-----
14
1 file changed, 4 insertions(+), 5 deletions(-)
15
16
diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/storage-daemon/qemu-storage-daemon.c
19
+++ b/storage-daemon/qemu-storage-daemon.c
20
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
21
* they are given on the command lines. This means that things must be
22
* defined first before they can be referenced in another option.
23
*/
24
- while ((c = getopt_long(argc, argv, "hT:V", long_options, NULL)) != -1) {
25
+ while ((c = getopt_long(argc, argv, "-hT:V", long_options, NULL)) != -1) {
26
switch (c) {
27
case '?':
28
exit(EXIT_FAILURE);
29
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
30
qobject_unref(args);
31
break;
32
}
33
+ case 1:
34
+ error_report("Unexpected argument: %s", optarg);
35
+ exit(EXIT_FAILURE);
36
default:
37
g_assert_not_reached();
38
}
39
}
40
- if (optind != argc) {
41
- error_report("Unexpected argument: %s", argv[optind]);
42
- exit(EXIT_FAILURE);
43
- }
44
}
45
46
int main(int argc, char *argv[])
47
--
48
2.29.2
49
50
diff view generated by jsdifflib
New patch
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
2
3
Use the location management facilities that the emulator uses, so that
4
the current command line option appears in the error message.
5
6
Before:
7
8
$ storage-daemon/qemu-storage-daemon --nbd key..=
9
qemu-storage-daemon: Invalid parameter 'key..'
10
11
After:
12
13
$ storage-daemon/qemu-storage-daemon --nbd key..=
14
qemu-storage-daemon: --nbd key..=: Invalid parameter 'key..'
15
16
Reviewed-by: Eric Blake <eblake@redhat.com>
17
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
18
Message-Id: <20210301152844.291799-3-pbonzini@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
---
21
storage-daemon/qemu-storage-daemon.c | 19 +++++++++++++++++--
22
1 file changed, 17 insertions(+), 2 deletions(-)
23
24
diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c
25
index XXXXXXX..XXXXXXX 100644
26
--- a/storage-daemon/qemu-storage-daemon.c
27
+++ b/storage-daemon/qemu-storage-daemon.c
28
@@ -XXX,XX +XXX,XX @@ static void init_qmp_commands(void)
29
qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
30
}
31
32
+static int getopt_set_loc(int argc, char **argv, const char *optstring,
33
+ const struct option *longopts)
34
+{
35
+ int c, save_index;
36
+
37
+ optarg = NULL;
38
+ save_index = optind;
39
+ c = getopt_long(argc, argv, optstring, longopts, NULL);
40
+ if (optarg) {
41
+ loc_set_cmdline(argv, save_index, MAX(1, optind - save_index));
42
+ }
43
+ return c;
44
+}
45
+
46
static void process_options(int argc, char *argv[])
47
{
48
int c;
49
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
50
* they are given on the command lines. This means that things must be
51
* defined first before they can be referenced in another option.
52
*/
53
- while ((c = getopt_long(argc, argv, "-hT:V", long_options, NULL)) != -1) {
54
+ while ((c = getopt_set_loc(argc, argv, "-hT:V", long_options)) != -1) {
55
switch (c) {
56
case '?':
57
exit(EXIT_FAILURE);
58
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
59
break;
60
}
61
case 1:
62
- error_report("Unexpected argument: %s", optarg);
63
+ error_report("Unexpected argument");
64
exit(EXIT_FAILURE);
65
default:
66
g_assert_not_reached();
67
}
68
}
69
+ loc_set_none();
70
}
71
72
int main(int argc, char *argv[])
73
--
74
2.29.2
75
76
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
Daemons often have a --pidfile option where the pid is written to a file
4
so that scripts can stop the daemon by sending a signal.
5
6
The pid file also acts as a lock to prevent multiple instances of the
7
daemon from launching for a given pid file.
8
9
QEMU, qemu-nbd, qemu-ga, virtiofsd, and qemu-pr-helper all support the
10
--pidfile option. Add it to qemu-storage-daemon too.
11
12
Reported-by: Richard W.M. Jones <rjones@redhat.com>
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Message-Id: <20210302142746.170535-1-stefanha@redhat.com>
15
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
18
docs/tools/qemu-storage-daemon.rst | 14 +++++++++++
19
storage-daemon/qemu-storage-daemon.c | 36 ++++++++++++++++++++++++++++
20
2 files changed, 50 insertions(+)
21
22
diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst
23
index XXXXXXX..XXXXXXX 100644
24
--- a/docs/tools/qemu-storage-daemon.rst
25
+++ b/docs/tools/qemu-storage-daemon.rst
26
@@ -XXX,XX +XXX,XX @@ Standard options:
27
List object properties with ``<type>,help``. See the :manpage:`qemu(1)`
28
manual page for a description of the object properties.
29
30
+.. option:: --pidfile PATH
31
+
32
+ is the path to a file where the daemon writes its pid. This allows scripts to
33
+ stop the daemon by sending a signal::
34
+
35
+ $ kill -SIGTERM $(<path/to/qsd.pid)
36
+
37
+ A file lock is applied to the file so only one instance of the daemon can run
38
+ with a given pid file path. The daemon unlinks its pid file when terminating.
39
+
40
+ The pid file is written after chardevs, exports, and NBD servers have been
41
+ created but before accepting connections. The daemon has started successfully
42
+ when the pid file is written and clients may begin connecting.
43
+
44
Examples
45
--------
46
Launch the daemon with QMP monitor socket ``qmp.sock`` so clients can execute
47
diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c
48
index XXXXXXX..XXXXXXX 100644
49
--- a/storage-daemon/qemu-storage-daemon.c
50
+++ b/storage-daemon/qemu-storage-daemon.c
51
@@ -XXX,XX +XXX,XX @@
52
#include "sysemu/runstate.h"
53
#include "trace/control.h"
54
55
+static const char *pid_file;
56
static volatile bool exit_requested = false;
57
58
void qemu_system_killed(int signal, pid_t pid)
59
@@ -XXX,XX +XXX,XX @@ static void help(void)
60
" See the qemu(1) man page for documentation of the\n"
61
" objects that can be added.\n"
62
"\n"
63
+" --pidfile <path> write process ID to a file after startup\n"
64
+"\n"
65
QEMU_HELP_BOTTOM "\n",
66
error_get_progname());
67
}
68
@@ -XXX,XX +XXX,XX @@ enum {
69
OPTION_MONITOR,
70
OPTION_NBD_SERVER,
71
OPTION_OBJECT,
72
+ OPTION_PIDFILE,
73
};
74
75
extern QemuOptsList qemu_chardev_opts;
76
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
77
{"monitor", required_argument, NULL, OPTION_MONITOR},
78
{"nbd-server", required_argument, NULL, OPTION_NBD_SERVER},
79
{"object", required_argument, NULL, OPTION_OBJECT},
80
+ {"pidfile", required_argument, NULL, OPTION_PIDFILE},
81
{"trace", required_argument, NULL, 'T'},
82
{"version", no_argument, NULL, 'V'},
83
{0, 0, 0, 0}
84
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
85
qobject_unref(args);
86
break;
87
}
88
+ case OPTION_PIDFILE:
89
+ pid_file = optarg;
90
+ break;
91
case 1:
92
error_report("Unexpected argument");
93
exit(EXIT_FAILURE);
94
@@ -XXX,XX +XXX,XX @@ static void process_options(int argc, char *argv[])
95
loc_set_none();
96
}
97
98
+static void pid_file_cleanup(void)
99
+{
100
+ unlink(pid_file);
101
+}
102
+
103
+static void pid_file_init(void)
104
+{
105
+ Error *err = NULL;
106
+
107
+ if (!pid_file) {
108
+ return;
109
+ }
110
+
111
+ if (!qemu_write_pidfile(pid_file, &err)) {
112
+ error_reportf_err(err, "cannot create PID file: ");
113
+ exit(EXIT_FAILURE);
114
+ }
115
+
116
+ atexit(pid_file_cleanup);
117
+}
118
+
119
int main(int argc, char *argv[])
120
{
121
#ifdef CONFIG_POSIX
122
@@ -XXX,XX +XXX,XX @@ int main(int argc, char *argv[])
123
qemu_init_main_loop(&error_fatal);
124
process_options(argc, argv);
125
126
+ /*
127
+ * Write the pid file after creating chardevs, exports, and NBD servers but
128
+ * before accepting connections. This ordering is documented. Do not change
129
+ * it.
130
+ */
131
+ pid_file_init();
132
+
133
while (!exit_requested) {
134
main_loop_wait(false);
135
}
136
--
137
2.29.2
138
139
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
The QMP monitor, NBD server, and vhost-user-blk export all support file
4
descriptor passing. This is a useful technique because it allows the
5
parent process to spawn and wait for qemu-storage-daemon without busy
6
waiting, which may delay startup due to arbitrary sleep() calls.
7
8
This Python example is inspired by the test case written for libnbd by
9
Richard W.M. Jones <rjones@redhat.com>:
10
https://gitlab.com/nbdkit/libnbd/-/commit/89113f484effb0e6c322314ba75c1cbe07a04543
11
12
Thanks to Daniel P. Berrangé <berrange@redhat.com> for suggestions on
13
how to get this working. Now let's document it!
14
15
Reported-by: Richard W.M. Jones <rjones@redhat.com>
16
Cc: Kevin Wolf <kwolf@redhat.com>
17
Cc: Daniel P. Berrangé <berrange@redhat.com>
18
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Message-Id: <20210301172728.135331-2-stefanha@redhat.com>
20
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
21
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
---
24
docs/tools/qemu-storage-daemon.rst | 42 ++++++++++++++++++++++++++++--
25
1 file changed, 40 insertions(+), 2 deletions(-)
26
27
diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst
28
index XXXXXXX..XXXXXXX 100644
29
--- a/docs/tools/qemu-storage-daemon.rst
30
+++ b/docs/tools/qemu-storage-daemon.rst
31
@@ -XXX,XX +XXX,XX @@ Standard options:
32
33
.. option:: --nbd-server addr.type=inet,addr.host=<host>,addr.port=<port>[,tls-creds=<id>][,tls-authz=<id>][,max-connections=<n>]
34
--nbd-server addr.type=unix,addr.path=<path>[,tls-creds=<id>][,tls-authz=<id>][,max-connections=<n>]
35
+ --nbd-server addr.type=fd,addr.str=<fd>[,tls-creds=<id>][,tls-authz=<id>][,max-connections=<n>]
36
37
is a server for NBD exports. Both TCP and UNIX domain sockets are supported.
38
- TLS encryption can be configured using ``--object`` tls-creds-* and authz-*
39
- secrets (see below).
40
+ A listen socket can be provided via file descriptor passing (see Examples
41
+ below). TLS encryption can be configured using ``--object`` tls-creds-* and
42
+ authz-* secrets (see below).
43
44
To configure an NBD server on UNIX domain socket path ``/tmp/nbd.sock``::
45
46
@@ -XXX,XX +XXX,XX @@ QMP commands::
47
--chardev socket,path=qmp.sock,server=on,wait=off,id=char1 \
48
--monitor chardev=char1
49
50
+Launch the daemon from Python with a QMP monitor socket using file descriptor
51
+passing so there is no need to busy wait for the QMP monitor to become
52
+available::
53
+
54
+ #!/usr/bin/env python3
55
+ import subprocess
56
+ import socket
57
+
58
+ sock_path = '/var/run/qmp.sock'
59
+
60
+ with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as listen_sock:
61
+ listen_sock.bind(sock_path)
62
+ listen_sock.listen()
63
+
64
+ fd = listen_sock.fileno()
65
+
66
+ subprocess.Popen(
67
+ ['qemu-storage-daemon',
68
+ '--chardev', f'socket,fd={fd},server=on,id=char1',
69
+ '--monitor', 'chardev=char1'],
70
+ pass_fds=[fd],
71
+ )
72
+
73
+ # listen_sock was automatically closed when leaving the 'with' statement
74
+ # body. If the daemon process terminated early then the following connect()
75
+ # will fail with "Connection refused" because no process has the listen
76
+ # socket open anymore. Launch errors can be detected this way.
77
+
78
+ qmp_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
79
+ qmp_sock.connect(sock_path)
80
+ ...QMP interaction...
81
+
82
+The same socket spawning approach also works with the ``--nbd-server
83
+addr.type=fd,addr.str=<fd>`` and ``--export
84
+type=vhost-user-blk,addr.type=fd,addr.str=<fd>`` options.
85
+
86
Export raw image file ``disk.img`` over NBD UNIX domain socket ``nbd.sock``::
87
88
$ qemu-storage-daemon \
89
--
90
2.29.2
91
92
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
World-writeable directories have security issues. Avoid showing them in
4
the documentation since someone might accidentally use them in
5
situations where they are insecure.
6
7
There tend to be 3 security problems:
8
1. Denial of service. An adversary may be able to create the file
9
beforehand, consume all space/inodes, etc to sabotage us.
10
2. Impersonation. An adversary may be able to create a listen socket and
11
accept incoming connections that were meant for us.
12
3. Unauthenticated client access. An adversary may be able to connect to
13
us if we did not set the uid/gid and permissions correctly.
14
15
These can be prevented or mitigated with private /tmp, carefully setting
16
the umask, etc but that requires special action and does not apply to
17
all situations. Just avoid using /tmp in examples.
18
19
Reported-by: Richard W.M. Jones <rjones@redhat.com>
20
Reported-by: Daniel P. Berrangé <berrange@redhat.com>
21
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
22
Message-Id: <20210301172728.135331-3-stefanha@redhat.com>
23
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
24
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26
---
27
docs/tools/qemu-storage-daemon.rst | 7 ++++---
28
1 file changed, 4 insertions(+), 3 deletions(-)
29
30
diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst
31
index XXXXXXX..XXXXXXX 100644
32
--- a/docs/tools/qemu-storage-daemon.rst
33
+++ b/docs/tools/qemu-storage-daemon.rst
34
@@ -XXX,XX +XXX,XX @@ Standard options:
35
a description of character device properties. A common character device
36
definition configures a UNIX domain socket::
37
38
- --chardev socket,id=char1,path=/tmp/qmp.sock,server=on,wait=off
39
+ --chardev socket,id=char1,path=/var/run/qsd-qmp.sock,server=on,wait=off
40
41
.. option:: --export [type=]nbd,id=<id>,node-name=<node-name>[,name=<export-name>][,writable=on|off][,bitmap=<name>]
42
--export [type=]vhost-user-blk,id=<id>,node-name=<node-name>,addr.type=unix,addr.path=<socket-path>[,writable=on|off][,logical-block-size=<block-size>][,num-queues=<num-queues>]
43
@@ -XXX,XX +XXX,XX @@ Standard options:
44
below). TLS encryption can be configured using ``--object`` tls-creds-* and
45
authz-* secrets (see below).
46
47
- To configure an NBD server on UNIX domain socket path ``/tmp/nbd.sock``::
48
+ To configure an NBD server on UNIX domain socket path
49
+ ``/var/run/qsd-nbd.sock``::
50
51
- --nbd-server addr.type=unix,addr.path=/tmp/nbd.sock
52
+ --nbd-server addr.type=unix,addr.path=/var/run/qsd-nbd.sock
53
54
.. option:: --object help
55
--object <type>,help
56
--
57
2.29.2
58
59
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
Treat the num_queues field as virtio-endian. On big-endian hosts the
4
vhost-user-blk num_queues field was in the wrong endianness.
5
6
Move the blkcfg.num_queues store operation from realize to
7
vhost_user_blk_update_config() so feature negotiation has finished and
8
we know the endianness of the device. VIRTIO 1.0 devices are
9
little-endian, but in case someone wants to use legacy VIRTIO we support
10
all endianness cases.
11
12
Cc: qemu-stable@nongnu.org
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
15
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
16
Message-Id: <20210223144653.811468-2-stefanha@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
19
hw/block/vhost-user-blk.c | 7 +++----
20
1 file changed, 3 insertions(+), 4 deletions(-)
21
22
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
23
index XXXXXXX..XXXXXXX 100644
24
--- a/hw/block/vhost-user-blk.c
25
+++ b/hw/block/vhost-user-blk.c
26
@@ -XXX,XX +XXX,XX @@ static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config)
27
{
28
VHostUserBlk *s = VHOST_USER_BLK(vdev);
29
30
+ /* Our num_queues overrides the device backend */
31
+ virtio_stw_p(vdev, &s->blkcfg.num_queues, s->num_queues);
32
+
33
memcpy(config, &s->blkcfg, sizeof(struct virtio_blk_config));
34
}
35
36
@@ -XXX,XX +XXX,XX @@ reconnect:
37
goto reconnect;
38
}
39
40
- if (s->blkcfg.num_queues != s->num_queues) {
41
- s->blkcfg.num_queues = s->num_queues;
42
- }
43
-
44
return;
45
46
virtio_err:
47
--
48
2.29.2
49
50
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
Add an API that returns a new UNIX domain socket in the listen state.
4
The code for this was already there but only used internally in
5
init_socket().
6
7
This new API will be used by vhost-user-blk-test.
8
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Reviewed-by: Thomas Huth <thuth@redhat.com>
11
Reviewed-by: Wainer dos Santos Moschetta <wainersm@redhat.com>
12
Message-Id: <20210223144653.811468-3-stefanha@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
15
tests/qtest/libqos/libqtest.h | 8 +++++++
16
tests/qtest/libqtest.c | 40 ++++++++++++++++++++---------------
17
2 files changed, 31 insertions(+), 17 deletions(-)
18
19
diff --git a/tests/qtest/libqos/libqtest.h b/tests/qtest/libqos/libqtest.h
20
index XXXXXXX..XXXXXXX 100644
21
--- a/tests/qtest/libqos/libqtest.h
22
+++ b/tests/qtest/libqos/libqtest.h
23
@@ -XXX,XX +XXX,XX @@ void qtest_qmp_send(QTestState *s, const char *fmt, ...)
24
void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...)
25
GCC_FMT_ATTR(2, 3);
26
27
+/**
28
+ * qtest_socket_server:
29
+ * @socket_path: the UNIX domain socket path
30
+ *
31
+ * Create and return a listen socket file descriptor, or abort on failure.
32
+ */
33
+int qtest_socket_server(const char *socket_path);
34
+
35
/**
36
* qtest_vqmp_fds:
37
* @s: #QTestState instance to operate on.
38
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
39
index XXXXXXX..XXXXXXX 100644
40
--- a/tests/qtest/libqtest.c
41
+++ b/tests/qtest/libqtest.c
42
@@ -XXX,XX +XXX,XX @@ static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv);
43
44
static int init_socket(const char *socket_path)
45
{
46
- struct sockaddr_un addr;
47
- int sock;
48
- int ret;
49
-
50
- sock = socket(PF_UNIX, SOCK_STREAM, 0);
51
- g_assert_cmpint(sock, !=, -1);
52
-
53
- addr.sun_family = AF_UNIX;
54
- snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path);
55
+ int sock = qtest_socket_server(socket_path);
56
qemu_set_cloexec(sock);
57
-
58
- do {
59
- ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
60
- } while (ret == -1 && errno == EINTR);
61
- g_assert_cmpint(ret, !=, -1);
62
- ret = listen(sock, 1);
63
- g_assert_cmpint(ret, !=, -1);
64
-
65
return sock;
66
}
67
68
@@ -XXX,XX +XXX,XX @@ QDict *qtest_qmp_receive_dict(QTestState *s)
69
return qmp_fd_receive(s->qmp_fd);
70
}
71
72
+int qtest_socket_server(const char *socket_path)
73
+{
74
+ struct sockaddr_un addr;
75
+ int sock;
76
+ int ret;
77
+
78
+ sock = socket(PF_UNIX, SOCK_STREAM, 0);
79
+ g_assert_cmpint(sock, !=, -1);
80
+
81
+ addr.sun_family = AF_UNIX;
82
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path);
83
+
84
+ do {
85
+ ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
86
+ } while (ret == -1 && errno == EINTR);
87
+ g_assert_cmpint(ret, !=, -1);
88
+ ret = listen(sock, 1);
89
+ g_assert_cmpint(ret, !=, -1);
90
+
91
+ return sock;
92
+}
93
+
94
/**
95
* Allow users to send a message without waiting for the reply,
96
* in the case that they choose to discard all replies up until
97
--
98
2.29.2
99
100
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
Tests that manage multiple processes may wish to kill QEMU before
4
destroying the QTestState. Expose a function to do that.
5
6
The vhost-user-blk-test testcase will need this.
7
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Reviewed-by: Wainer dos Santos Moschetta <wainersm@redhat.com>
10
Message-Id: <20210223144653.811468-4-stefanha@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
---
13
tests/qtest/libqos/libqtest.h | 11 +++++++++++
14
tests/qtest/libqtest.c | 7 ++++---
15
2 files changed, 15 insertions(+), 3 deletions(-)
16
17
diff --git a/tests/qtest/libqos/libqtest.h b/tests/qtest/libqos/libqtest.h
18
index XXXXXXX..XXXXXXX 100644
19
--- a/tests/qtest/libqos/libqtest.h
20
+++ b/tests/qtest/libqos/libqtest.h
21
@@ -XXX,XX +XXX,XX @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args);
22
*/
23
QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd);
24
25
+/**
26
+ * qtest_kill_qemu:
27
+ * @s: #QTestState instance to operate on.
28
+ *
29
+ * Kill the QEMU process and wait for it to terminate. It is safe to call this
30
+ * function multiple times. Normally qtest_quit() is used instead because it
31
+ * also frees QTestState. Use qtest_kill_qemu() when you just want to kill QEMU
32
+ * and qtest_quit() will be called later.
33
+ */
34
+void qtest_kill_qemu(QTestState *s);
35
+
36
/**
37
* qtest_quit:
38
* @s: #QTestState instance to operate on.
39
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/tests/qtest/libqtest.c
42
+++ b/tests/qtest/libqtest.c
43
@@ -XXX,XX +XXX,XX @@ void qtest_set_expected_status(QTestState *s, int status)
44
s->expected_status = status;
45
}
46
47
-static void kill_qemu(QTestState *s)
48
+void qtest_kill_qemu(QTestState *s)
49
{
50
pid_t pid = s->qemu_pid;
51
int wstatus;
52
@@ -XXX,XX +XXX,XX @@ static void kill_qemu(QTestState *s)
53
kill(pid, SIGTERM);
54
TFR(pid = waitpid(s->qemu_pid, &s->wstatus, 0));
55
assert(pid == s->qemu_pid);
56
+ s->qemu_pid = -1;
57
}
58
59
/*
60
@@ -XXX,XX +XXX,XX @@ static void kill_qemu(QTestState *s)
61
62
static void kill_qemu_hook_func(void *s)
63
{
64
- kill_qemu(s);
65
+ qtest_kill_qemu(s);
66
}
67
68
static void sigabrt_handler(int signo)
69
@@ -XXX,XX +XXX,XX @@ void qtest_quit(QTestState *s)
70
/* Uninstall SIGABRT handler on last instance */
71
cleanup_sigabrt_handler();
72
73
- kill_qemu(s);
74
+ qtest_kill_qemu(s);
75
close(s->fd);
76
close(s->qmp_fd);
77
g_string_free(s->rx, true);
78
--
79
2.29.2
80
81
diff view generated by jsdifflib
1
Some functions require that the caller holds a certain CoMutex for them
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
to operate correctly. Add a function so that they can assert the lock is
3
really held.
4
2
5
Cc: qemu-stable@nongnu.org
3
Add a function to remove previously-added abrt handler functions.
4
5
Now that a symmetric pair of add/remove functions exists we can also
6
balance the SIGABRT handler installation. The signal handler was
7
installed each time qtest_add_abrt_handler() was called. Now it is
8
installed when the abrt handler list becomes non-empty and removed again
9
when the list becomes empty.
10
11
The qtest_remove_abrt_handler() function will be used by
12
vhost-user-blk-test.
13
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Reviewed-by: Wainer dos Santos Moschetta <wainersm@redhat.com>
16
Message-Id: <20210223144653.811468-5-stefanha@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Tested-by: Michael Weiser <michael.weiser@gmx.de>
8
Reviewed-by: Michael Weiser <michael.weiser@gmx.de>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Reviewed-by: Denis V. Lunev <den@openvz.org>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
---
18
---
13
include/qemu/coroutine.h | 15 +++++++++++++++
19
tests/qtest/libqos/libqtest.h | 18 ++++++++++++++++++
14
1 file changed, 15 insertions(+)
20
tests/qtest/libqtest.c | 35 +++++++++++++++++++++++++++++------
21
2 files changed, 47 insertions(+), 6 deletions(-)
15
22
16
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
23
diff --git a/tests/qtest/libqos/libqtest.h b/tests/qtest/libqos/libqtest.h
17
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
18
--- a/include/qemu/coroutine.h
25
--- a/tests/qtest/libqos/libqtest.h
19
+++ b/include/qemu/coroutine.h
26
+++ b/tests/qtest/libqos/libqtest.h
20
@@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex);
27
@@ -XXX,XX +XXX,XX @@ void qtest_add_data_func_full(const char *str, void *data,
21
*/
28
g_free(path); \
22
void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex);
29
} while (0)
23
30
24
+/**
31
+/**
25
+ * Assert that the current coroutine holds @mutex.
32
+ * qtest_add_abrt_handler:
33
+ * @fn: Handler function
34
+ * @data: Argument that is passed to the handler
35
+ *
36
+ * Add a handler function that is invoked on SIGABRT. This can be used to
37
+ * terminate processes and perform other cleanup. The handler can be removed
38
+ * with qtest_remove_abrt_handler().
26
+ */
39
+ */
27
+static inline coroutine_fn void qemu_co_mutex_assert_locked(CoMutex *mutex)
40
void qtest_add_abrt_handler(GHookFunc fn, const void *data);
41
42
+/**
43
+ * qtest_remove_abrt_handler:
44
+ * @data: Argument previously passed to qtest_add_abrt_handler()
45
+ *
46
+ * Remove an abrt handler that was previously added with
47
+ * qtest_add_abrt_handler().
48
+ */
49
+void qtest_remove_abrt_handler(void *data);
50
+
51
/**
52
* qtest_qmp_assert_success:
53
* @qts: QTestState instance to operate on
54
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
55
index XXXXXXX..XXXXXXX 100644
56
--- a/tests/qtest/libqtest.c
57
+++ b/tests/qtest/libqtest.c
58
@@ -XXX,XX +XXX,XX @@ static void cleanup_sigabrt_handler(void)
59
sigaction(SIGABRT, &sigact_old, NULL);
60
}
61
62
+static bool hook_list_is_empty(GHookList *hook_list)
28
+{
63
+{
29
+ /*
64
+ GHook *hook = g_hook_first_valid(hook_list, TRUE);
30
+ * mutex->holder doesn't need any synchronisation if the assertion holds
65
+
31
+ * true because the mutex protects it. If it doesn't hold true, we still
66
+ if (!hook) {
32
+ * don't mind if another thread takes or releases mutex behind our back,
67
+ return false;
33
+ * because the condition will be false no matter whether we read NULL or
68
+ }
34
+ * the pointer for any other coroutine.
69
+
35
+ */
70
+ g_hook_unref(hook_list, hook);
36
+ assert(atomic_read(&mutex->locked) &&
71
+ return true;
37
+ mutex->holder == qemu_coroutine_self());
38
+}
72
+}
39
73
+
40
/**
74
void qtest_add_abrt_handler(GHookFunc fn, const void *data)
41
* CoQueues are a mechanism to queue coroutines in order to continue executing
75
{
76
GHook *hook;
77
78
- /* Only install SIGABRT handler once */
79
if (!abrt_hooks.is_setup) {
80
g_hook_list_init(&abrt_hooks, sizeof(GHook));
81
}
82
- setup_sigabrt_handler();
83
+
84
+ /* Only install SIGABRT handler once */
85
+ if (hook_list_is_empty(&abrt_hooks)) {
86
+ setup_sigabrt_handler();
87
+ }
88
89
hook = g_hook_alloc(&abrt_hooks);
90
hook->func = fn;
91
@@ -XXX,XX +XXX,XX @@ void qtest_add_abrt_handler(GHookFunc fn, const void *data)
92
g_hook_prepend(&abrt_hooks, hook);
93
}
94
95
+void qtest_remove_abrt_handler(void *data)
96
+{
97
+ GHook *hook = g_hook_find_data(&abrt_hooks, TRUE, data);
98
+ g_hook_destroy_link(&abrt_hooks, hook);
99
+
100
+ /* Uninstall SIGABRT handler on last instance */
101
+ if (hook_list_is_empty(&abrt_hooks)) {
102
+ cleanup_sigabrt_handler();
103
+ }
104
+}
105
+
106
static const char *qtest_qemu_binary(void)
107
{
108
const char *qemu_bin;
109
@@ -XXX,XX +XXX,XX @@ QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd)
110
111
void qtest_quit(QTestState *s)
112
{
113
- g_hook_destroy_link(&abrt_hooks, g_hook_find_data(&abrt_hooks, TRUE, s));
114
-
115
- /* Uninstall SIGABRT handler on last instance */
116
- cleanup_sigabrt_handler();
117
+ qtest_remove_abrt_handler(s);
118
119
qtest_kill_qemu(s);
120
close(s->fd);
42
--
121
--
43
2.20.1
122
2.29.2
44
123
45
124
diff view generated by jsdifflib
New patch
1
From: Coiby Xu <coiby.xu@gmail.com>
1
2
3
This test case has the same tests as tests/virtio-blk-test.c except for
4
tests have block_resize. Since the vhost-user-blk export only serves one
5
client one time, two exports are started by qemu-storage-daemon for the
6
hotplug test.
7
8
Suggested-by: Thomas Huth <thuth@redhat.com>
9
Signed-off-by: Coiby Xu <coiby.xu@gmail.com>
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Message-Id: <20210223144653.811468-6-stefanha@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
14
tests/qtest/libqos/vhost-user-blk.h | 48 ++
15
tests/qtest/libqos/vhost-user-blk.c | 130 +++++
16
tests/qtest/vhost-user-blk-test.c | 788 ++++++++++++++++++++++++++++
17
MAINTAINERS | 2 +
18
tests/qtest/libqos/meson.build | 1 +
19
tests/qtest/meson.build | 4 +
20
6 files changed, 973 insertions(+)
21
create mode 100644 tests/qtest/libqos/vhost-user-blk.h
22
create mode 100644 tests/qtest/libqos/vhost-user-blk.c
23
create mode 100644 tests/qtest/vhost-user-blk-test.c
24
25
diff --git a/tests/qtest/libqos/vhost-user-blk.h b/tests/qtest/libqos/vhost-user-blk.h
26
new file mode 100644
27
index XXXXXXX..XXXXXXX
28
--- /dev/null
29
+++ b/tests/qtest/libqos/vhost-user-blk.h
30
@@ -XXX,XX +XXX,XX @@
31
+/*
32
+ * libqos driver framework
33
+ *
34
+ * Based on tests/qtest/libqos/virtio-blk.c
35
+ *
36
+ * Copyright (c) 2020 Coiby Xu <coiby.xu@gmail.com>
37
+ *
38
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
39
+ *
40
+ * This library is free software; you can redistribute it and/or
41
+ * modify it under the terms of the GNU Lesser General Public
42
+ * License version 2 as published by the Free Software Foundation.
43
+ *
44
+ * This library is distributed in the hope that it will be useful,
45
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
46
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
47
+ * Lesser General Public License for more details.
48
+ *
49
+ * You should have received a copy of the GNU Lesser General Public
50
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
51
+ */
52
+
53
+#ifndef TESTS_LIBQOS_VHOST_USER_BLK_H
54
+#define TESTS_LIBQOS_VHOST_USER_BLK_H
55
+
56
+#include "qgraph.h"
57
+#include "virtio.h"
58
+#include "virtio-pci.h"
59
+
60
+typedef struct QVhostUserBlk QVhostUserBlk;
61
+typedef struct QVhostUserBlkPCI QVhostUserBlkPCI;
62
+typedef struct QVhostUserBlkDevice QVhostUserBlkDevice;
63
+
64
+struct QVhostUserBlk {
65
+ QVirtioDevice *vdev;
66
+};
67
+
68
+struct QVhostUserBlkPCI {
69
+ QVirtioPCIDevice pci_vdev;
70
+ QVhostUserBlk blk;
71
+};
72
+
73
+struct QVhostUserBlkDevice {
74
+ QOSGraphObject obj;
75
+ QVhostUserBlk blk;
76
+};
77
+
78
+#endif
79
diff --git a/tests/qtest/libqos/vhost-user-blk.c b/tests/qtest/libqos/vhost-user-blk.c
80
new file mode 100644
81
index XXXXXXX..XXXXXXX
82
--- /dev/null
83
+++ b/tests/qtest/libqos/vhost-user-blk.c
84
@@ -XXX,XX +XXX,XX @@
85
+/*
86
+ * libqos driver framework
87
+ *
88
+ * Based on tests/qtest/libqos/virtio-blk.c
89
+ *
90
+ * Copyright (c) 2020 Coiby Xu <coiby.xu@gmail.com>
91
+ *
92
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
93
+ *
94
+ * This library is free software; you can redistribute it and/or
95
+ * modify it under the terms of the GNU Lesser General Public
96
+ * License version 2.1 as published by the Free Software Foundation.
97
+ *
98
+ * This library is distributed in the hope that it will be useful,
99
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
100
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
101
+ * Lesser General Public License for more details.
102
+ *
103
+ * You should have received a copy of the GNU Lesser General Public
104
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
105
+ */
106
+
107
+#include "qemu/osdep.h"
108
+#include "libqtest.h"
109
+#include "qemu/module.h"
110
+#include "standard-headers/linux/virtio_blk.h"
111
+#include "vhost-user-blk.h"
112
+
113
+#define PCI_SLOT 0x04
114
+#define PCI_FN 0x00
115
+
116
+/* virtio-blk-device */
117
+static void *qvhost_user_blk_get_driver(QVhostUserBlk *v_blk,
118
+ const char *interface)
119
+{
120
+ if (!g_strcmp0(interface, "vhost-user-blk")) {
121
+ return v_blk;
122
+ }
123
+ if (!g_strcmp0(interface, "virtio")) {
124
+ return v_blk->vdev;
125
+ }
126
+
127
+ fprintf(stderr, "%s not present in vhost-user-blk-device\n", interface);
128
+ g_assert_not_reached();
129
+}
130
+
131
+static void *qvhost_user_blk_device_get_driver(void *object,
132
+ const char *interface)
133
+{
134
+ QVhostUserBlkDevice *v_blk = object;
135
+ return qvhost_user_blk_get_driver(&v_blk->blk, interface);
136
+}
137
+
138
+static void *vhost_user_blk_device_create(void *virtio_dev,
139
+ QGuestAllocator *t_alloc,
140
+ void *addr)
141
+{
142
+ QVhostUserBlkDevice *vhost_user_blk = g_new0(QVhostUserBlkDevice, 1);
143
+ QVhostUserBlk *interface = &vhost_user_blk->blk;
144
+
145
+ interface->vdev = virtio_dev;
146
+
147
+ vhost_user_blk->obj.get_driver = qvhost_user_blk_device_get_driver;
148
+
149
+ return &vhost_user_blk->obj;
150
+}
151
+
152
+/* virtio-blk-pci */
153
+static void *qvhost_user_blk_pci_get_driver(void *object, const char *interface)
154
+{
155
+ QVhostUserBlkPCI *v_blk = object;
156
+ if (!g_strcmp0(interface, "pci-device")) {
157
+ return v_blk->pci_vdev.pdev;
158
+ }
159
+ return qvhost_user_blk_get_driver(&v_blk->blk, interface);
160
+}
161
+
162
+static void *vhost_user_blk_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
163
+ void *addr)
164
+{
165
+ QVhostUserBlkPCI *vhost_user_blk = g_new0(QVhostUserBlkPCI, 1);
166
+ QVhostUserBlk *interface = &vhost_user_blk->blk;
167
+ QOSGraphObject *obj = &vhost_user_blk->pci_vdev.obj;
168
+
169
+ virtio_pci_init(&vhost_user_blk->pci_vdev, pci_bus, addr);
170
+ interface->vdev = &vhost_user_blk->pci_vdev.vdev;
171
+
172
+ g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_BLOCK);
173
+
174
+ obj->get_driver = qvhost_user_blk_pci_get_driver;
175
+
176
+ return obj;
177
+}
178
+
179
+static void vhost_user_blk_register_nodes(void)
180
+{
181
+ /*
182
+ * FIXME: every test using these two nodes needs to setup a
183
+ * -drive,id=drive0 otherwise QEMU is not going to start.
184
+ * Therefore, we do not include "produces" edge for virtio
185
+ * and pci-device yet.
186
+ */
187
+
188
+ char *arg = g_strdup_printf("id=drv0,chardev=char1,addr=%x.%x",
189
+ PCI_SLOT, PCI_FN);
190
+
191
+ QPCIAddress addr = {
192
+ .devfn = QPCI_DEVFN(PCI_SLOT, PCI_FN),
193
+ };
194
+
195
+ QOSGraphEdgeOptions opts = { };
196
+
197
+ /* virtio-blk-device */
198
+ /** opts.extra_device_opts = "drive=drive0"; */
199
+ qos_node_create_driver("vhost-user-blk-device",
200
+ vhost_user_blk_device_create);
201
+ qos_node_consumes("vhost-user-blk-device", "virtio-bus", &opts);
202
+ qos_node_produces("vhost-user-blk-device", "vhost-user-blk");
203
+
204
+ /* virtio-blk-pci */
205
+ opts.extra_device_opts = arg;
206
+ add_qpci_address(&opts, &addr);
207
+ qos_node_create_driver("vhost-user-blk-pci", vhost_user_blk_pci_create);
208
+ qos_node_consumes("vhost-user-blk-pci", "pci-bus", &opts);
209
+ qos_node_produces("vhost-user-blk-pci", "vhost-user-blk");
210
+
211
+ g_free(arg);
212
+}
213
+
214
+libqos_init(vhost_user_blk_register_nodes);
215
diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c
216
new file mode 100644
217
index XXXXXXX..XXXXXXX
218
--- /dev/null
219
+++ b/tests/qtest/vhost-user-blk-test.c
220
@@ -XXX,XX +XXX,XX @@
221
+/*
222
+ * QTest testcase for Vhost-user Block Device
223
+ *
224
+ * Based on tests/qtest//virtio-blk-test.c
225
+
226
+ * Copyright (c) 2014 SUSE LINUX Products GmbH
227
+ * Copyright (c) 2014 Marc Marí
228
+ * Copyright (c) 2020 Coiby Xu
229
+ *
230
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
231
+ * See the COPYING file in the top-level directory.
232
+ */
233
+
234
+#include "qemu/osdep.h"
235
+#include "libqtest-single.h"
236
+#include "qemu/bswap.h"
237
+#include "qemu/module.h"
238
+#include "standard-headers/linux/virtio_blk.h"
239
+#include "standard-headers/linux/virtio_pci.h"
240
+#include "libqos/qgraph.h"
241
+#include "libqos/vhost-user-blk.h"
242
+#include "libqos/libqos-pc.h"
243
+
244
+#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
245
+#define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000)
246
+#define PCI_SLOT_HP 0x06
247
+
248
+typedef struct {
249
+ pid_t pid;
250
+} QemuStorageDaemonState;
251
+
252
+typedef struct QVirtioBlkReq {
253
+ uint32_t type;
254
+ uint32_t ioprio;
255
+ uint64_t sector;
256
+ char *data;
257
+ uint8_t status;
258
+} QVirtioBlkReq;
259
+
260
+#ifdef HOST_WORDS_BIGENDIAN
261
+static const bool host_is_big_endian = true;
262
+#else
263
+static const bool host_is_big_endian; /* false */
264
+#endif
265
+
266
+static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
267
+{
268
+ if (qvirtio_is_big_endian(d) != host_is_big_endian) {
269
+ req->type = bswap32(req->type);
270
+ req->ioprio = bswap32(req->ioprio);
271
+ req->sector = bswap64(req->sector);
272
+ }
273
+}
274
+
275
+static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d,
276
+ struct virtio_blk_discard_write_zeroes *dwz_hdr)
277
+{
278
+ if (qvirtio_is_big_endian(d) != host_is_big_endian) {
279
+ dwz_hdr->sector = bswap64(dwz_hdr->sector);
280
+ dwz_hdr->num_sectors = bswap32(dwz_hdr->num_sectors);
281
+ dwz_hdr->flags = bswap32(dwz_hdr->flags);
282
+ }
283
+}
284
+
285
+static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d,
286
+ QVirtioBlkReq *req, uint64_t data_size)
287
+{
288
+ uint64_t addr;
289
+ uint8_t status = 0xFF;
290
+ QTestState *qts = global_qtest;
291
+
292
+ switch (req->type) {
293
+ case VIRTIO_BLK_T_IN:
294
+ case VIRTIO_BLK_T_OUT:
295
+ g_assert_cmpuint(data_size % 512, ==, 0);
296
+ break;
297
+ case VIRTIO_BLK_T_DISCARD:
298
+ case VIRTIO_BLK_T_WRITE_ZEROES:
299
+ g_assert_cmpuint(data_size %
300
+ sizeof(struct virtio_blk_discard_write_zeroes), ==, 0);
301
+ break;
302
+ default:
303
+ g_assert_cmpuint(data_size, ==, 0);
304
+ }
305
+
306
+ addr = guest_alloc(alloc, sizeof(*req) + data_size);
307
+
308
+ virtio_blk_fix_request(d, req);
309
+
310
+ qtest_memwrite(qts, addr, req, 16);
311
+ qtest_memwrite(qts, addr + 16, req->data, data_size);
312
+ qtest_memwrite(qts, addr + 16 + data_size, &status, sizeof(status));
313
+
314
+ return addr;
315
+}
316
+
317
+/* Returns the request virtqueue so the caller can perform further tests */
318
+static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc)
319
+{
320
+ QVirtioBlkReq req;
321
+ uint64_t req_addr;
322
+ uint64_t capacity;
323
+ uint64_t features;
324
+ uint32_t free_head;
325
+ uint8_t status;
326
+ char *data;
327
+ QTestState *qts = global_qtest;
328
+ QVirtQueue *vq;
329
+
330
+ features = qvirtio_get_features(dev);
331
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
332
+ (1u << VIRTIO_RING_F_INDIRECT_DESC) |
333
+ (1u << VIRTIO_RING_F_EVENT_IDX) |
334
+ (1u << VIRTIO_BLK_F_SCSI));
335
+ qvirtio_set_features(dev, features);
336
+
337
+ capacity = qvirtio_config_readq(dev, 0);
338
+ g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
339
+
340
+ vq = qvirtqueue_setup(dev, alloc, 0);
341
+
342
+ qvirtio_set_driver_ok(dev);
343
+
344
+ /* Write and read with 3 descriptor layout */
345
+ /* Write request */
346
+ req.type = VIRTIO_BLK_T_OUT;
347
+ req.ioprio = 1;
348
+ req.sector = 0;
349
+ req.data = g_malloc0(512);
350
+ strcpy(req.data, "TEST");
351
+
352
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
353
+
354
+ g_free(req.data);
355
+
356
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
357
+ qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
358
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
359
+
360
+ qvirtqueue_kick(qts, dev, vq, free_head);
361
+
362
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
363
+ QVIRTIO_BLK_TIMEOUT_US);
364
+ status = readb(req_addr + 528);
365
+ g_assert_cmpint(status, ==, 0);
366
+
367
+ guest_free(alloc, req_addr);
368
+
369
+ /* Read request */
370
+ req.type = VIRTIO_BLK_T_IN;
371
+ req.ioprio = 1;
372
+ req.sector = 0;
373
+ req.data = g_malloc0(512);
374
+
375
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
376
+
377
+ g_free(req.data);
378
+
379
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
380
+ qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
381
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
382
+
383
+ qvirtqueue_kick(qts, dev, vq, free_head);
384
+
385
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
386
+ QVIRTIO_BLK_TIMEOUT_US);
387
+ status = readb(req_addr + 528);
388
+ g_assert_cmpint(status, ==, 0);
389
+
390
+ data = g_malloc0(512);
391
+ qtest_memread(qts, req_addr + 16, data, 512);
392
+ g_assert_cmpstr(data, ==, "TEST");
393
+ g_free(data);
394
+
395
+ guest_free(alloc, req_addr);
396
+
397
+ if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) {
398
+ struct virtio_blk_discard_write_zeroes dwz_hdr;
399
+ void *expected;
400
+
401
+ /*
402
+ * WRITE_ZEROES request on the same sector of previous test where
403
+ * we wrote "TEST".
404
+ */
405
+ req.type = VIRTIO_BLK_T_WRITE_ZEROES;
406
+ req.data = (char *) &dwz_hdr;
407
+ dwz_hdr.sector = 0;
408
+ dwz_hdr.num_sectors = 1;
409
+ dwz_hdr.flags = 0;
410
+
411
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
412
+
413
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
414
+
415
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
416
+ qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
417
+ qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
418
+ false);
419
+
420
+ qvirtqueue_kick(qts, dev, vq, free_head);
421
+
422
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
423
+ QVIRTIO_BLK_TIMEOUT_US);
424
+ status = readb(req_addr + 16 + sizeof(dwz_hdr));
425
+ g_assert_cmpint(status, ==, 0);
426
+
427
+ guest_free(alloc, req_addr);
428
+
429
+ /* Read request to check if the sector contains all zeroes */
430
+ req.type = VIRTIO_BLK_T_IN;
431
+ req.ioprio = 1;
432
+ req.sector = 0;
433
+ req.data = g_malloc0(512);
434
+
435
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
436
+
437
+ g_free(req.data);
438
+
439
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
440
+ qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
441
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
442
+
443
+ qvirtqueue_kick(qts, dev, vq, free_head);
444
+
445
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
446
+ QVIRTIO_BLK_TIMEOUT_US);
447
+ status = readb(req_addr + 528);
448
+ g_assert_cmpint(status, ==, 0);
449
+
450
+ data = g_malloc(512);
451
+ expected = g_malloc0(512);
452
+ qtest_memread(qts, req_addr + 16, data, 512);
453
+ g_assert_cmpmem(data, 512, expected, 512);
454
+ g_free(expected);
455
+ g_free(data);
456
+
457
+ guest_free(alloc, req_addr);
458
+ }
459
+
460
+ if (features & (1u << VIRTIO_BLK_F_DISCARD)) {
461
+ struct virtio_blk_discard_write_zeroes dwz_hdr;
462
+
463
+ req.type = VIRTIO_BLK_T_DISCARD;
464
+ req.data = (char *) &dwz_hdr;
465
+ dwz_hdr.sector = 0;
466
+ dwz_hdr.num_sectors = 1;
467
+ dwz_hdr.flags = 0;
468
+
469
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
470
+
471
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
472
+
473
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
474
+ qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
475
+ qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr),
476
+ 1, true, false);
477
+
478
+ qvirtqueue_kick(qts, dev, vq, free_head);
479
+
480
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
481
+ QVIRTIO_BLK_TIMEOUT_US);
482
+ status = readb(req_addr + 16 + sizeof(dwz_hdr));
483
+ g_assert_cmpint(status, ==, 0);
484
+
485
+ guest_free(alloc, req_addr);
486
+ }
487
+
488
+ if (features & (1u << VIRTIO_F_ANY_LAYOUT)) {
489
+ /* Write and read with 2 descriptor layout */
490
+ /* Write request */
491
+ req.type = VIRTIO_BLK_T_OUT;
492
+ req.ioprio = 1;
493
+ req.sector = 1;
494
+ req.data = g_malloc0(512);
495
+ strcpy(req.data, "TEST");
496
+
497
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
498
+
499
+ g_free(req.data);
500
+
501
+ free_head = qvirtqueue_add(qts, vq, req_addr, 528, false, true);
502
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
503
+ qvirtqueue_kick(qts, dev, vq, free_head);
504
+
505
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
506
+ QVIRTIO_BLK_TIMEOUT_US);
507
+ status = readb(req_addr + 528);
508
+ g_assert_cmpint(status, ==, 0);
509
+
510
+ guest_free(alloc, req_addr);
511
+
512
+ /* Read request */
513
+ req.type = VIRTIO_BLK_T_IN;
514
+ req.ioprio = 1;
515
+ req.sector = 1;
516
+ req.data = g_malloc0(512);
517
+
518
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
519
+
520
+ g_free(req.data);
521
+
522
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
523
+ qvirtqueue_add(qts, vq, req_addr + 16, 513, true, false);
524
+
525
+ qvirtqueue_kick(qts, dev, vq, free_head);
526
+
527
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
528
+ QVIRTIO_BLK_TIMEOUT_US);
529
+ status = readb(req_addr + 528);
530
+ g_assert_cmpint(status, ==, 0);
531
+
532
+ data = g_malloc0(512);
533
+ qtest_memread(qts, req_addr + 16, data, 512);
534
+ g_assert_cmpstr(data, ==, "TEST");
535
+ g_free(data);
536
+
537
+ guest_free(alloc, req_addr);
538
+ }
539
+
540
+ return vq;
541
+}
542
+
543
+static void basic(void *obj, void *data, QGuestAllocator *t_alloc)
544
+{
545
+ QVhostUserBlk *blk_if = obj;
546
+ QVirtQueue *vq;
547
+
548
+ vq = test_basic(blk_if->vdev, t_alloc);
549
+ qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc);
550
+
551
+}
552
+
553
+static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc)
554
+{
555
+ QVirtQueue *vq;
556
+ QVhostUserBlk *blk_if = obj;
557
+ QVirtioDevice *dev = blk_if->vdev;
558
+ QVirtioBlkReq req;
559
+ QVRingIndirectDesc *indirect;
560
+ uint64_t req_addr;
561
+ uint64_t capacity;
562
+ uint64_t features;
563
+ uint32_t free_head;
564
+ uint8_t status;
565
+ char *data;
566
+ QTestState *qts = global_qtest;
567
+
568
+ features = qvirtio_get_features(dev);
569
+ g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0);
570
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
571
+ (1u << VIRTIO_RING_F_EVENT_IDX) |
572
+ (1u << VIRTIO_BLK_F_SCSI));
573
+ qvirtio_set_features(dev, features);
574
+
575
+ capacity = qvirtio_config_readq(dev, 0);
576
+ g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
577
+
578
+ vq = qvirtqueue_setup(dev, t_alloc, 0);
579
+ qvirtio_set_driver_ok(dev);
580
+
581
+ /* Write request */
582
+ req.type = VIRTIO_BLK_T_OUT;
583
+ req.ioprio = 1;
584
+ req.sector = 0;
585
+ req.data = g_malloc0(512);
586
+ strcpy(req.data, "TEST");
587
+
588
+ req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
589
+
590
+ g_free(req.data);
591
+
592
+ indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
593
+ qvring_indirect_desc_add(dev, qts, indirect, req_addr, 528, false);
594
+ qvring_indirect_desc_add(dev, qts, indirect, req_addr + 528, 1, true);
595
+ free_head = qvirtqueue_add_indirect(qts, vq, indirect);
596
+ qvirtqueue_kick(qts, dev, vq, free_head);
597
+
598
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
599
+ QVIRTIO_BLK_TIMEOUT_US);
600
+ status = readb(req_addr + 528);
601
+ g_assert_cmpint(status, ==, 0);
602
+
603
+ g_free(indirect);
604
+ guest_free(t_alloc, req_addr);
605
+
606
+ /* Read request */
607
+ req.type = VIRTIO_BLK_T_IN;
608
+ req.ioprio = 1;
609
+ req.sector = 0;
610
+ req.data = g_malloc0(512);
611
+ strcpy(req.data, "TEST");
612
+
613
+ req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
614
+
615
+ g_free(req.data);
616
+
617
+ indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2);
618
+ qvring_indirect_desc_add(dev, qts, indirect, req_addr, 16, false);
619
+ qvring_indirect_desc_add(dev, qts, indirect, req_addr + 16, 513, true);
620
+ free_head = qvirtqueue_add_indirect(qts, vq, indirect);
621
+ qvirtqueue_kick(qts, dev, vq, free_head);
622
+
623
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
624
+ QVIRTIO_BLK_TIMEOUT_US);
625
+ status = readb(req_addr + 528);
626
+ g_assert_cmpint(status, ==, 0);
627
+
628
+ data = g_malloc0(512);
629
+ qtest_memread(qts, req_addr + 16, data, 512);
630
+ g_assert_cmpstr(data, ==, "TEST");
631
+ g_free(data);
632
+
633
+ g_free(indirect);
634
+ guest_free(t_alloc, req_addr);
635
+ qvirtqueue_cleanup(dev->bus, vq, t_alloc);
636
+}
637
+
638
+static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc)
639
+{
640
+ QVirtQueue *vq;
641
+ QVhostUserBlkPCI *blk = obj;
642
+ QVirtioPCIDevice *pdev = &blk->pci_vdev;
643
+ QVirtioDevice *dev = &pdev->vdev;
644
+ QVirtioBlkReq req;
645
+ uint64_t req_addr;
646
+ uint64_t capacity;
647
+ uint64_t features;
648
+ uint32_t free_head;
649
+ uint32_t write_head;
650
+ uint32_t desc_idx;
651
+ uint8_t status;
652
+ char *data;
653
+ QOSGraphObject *blk_object = obj;
654
+ QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
655
+ QTestState *qts = global_qtest;
656
+
657
+ if (qpci_check_buggy_msi(pci_dev)) {
658
+ return;
659
+ }
660
+
661
+ qpci_msix_enable(pdev->pdev);
662
+ qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
663
+
664
+ features = qvirtio_get_features(dev);
665
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
666
+ (1u << VIRTIO_RING_F_INDIRECT_DESC) |
667
+ (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
668
+ (1u << VIRTIO_BLK_F_SCSI));
669
+ qvirtio_set_features(dev, features);
670
+
671
+ capacity = qvirtio_config_readq(dev, 0);
672
+ g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
673
+
674
+ vq = qvirtqueue_setup(dev, t_alloc, 0);
675
+ qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
676
+
677
+ qvirtio_set_driver_ok(dev);
678
+
679
+ /* Write request */
680
+ req.type = VIRTIO_BLK_T_OUT;
681
+ req.ioprio = 1;
682
+ req.sector = 0;
683
+ req.data = g_malloc0(512);
684
+ strcpy(req.data, "TEST");
685
+
686
+ req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
687
+
688
+ g_free(req.data);
689
+
690
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
691
+ qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
692
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
693
+ qvirtqueue_kick(qts, dev, vq, free_head);
694
+
695
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
696
+ QVIRTIO_BLK_TIMEOUT_US);
697
+
698
+ /* Write request */
699
+ req.type = VIRTIO_BLK_T_OUT;
700
+ req.ioprio = 1;
701
+ req.sector = 1;
702
+ req.data = g_malloc0(512);
703
+ strcpy(req.data, "TEST");
704
+
705
+ req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
706
+
707
+ g_free(req.data);
708
+
709
+ /* Notify after processing the third request */
710
+ qvirtqueue_set_used_event(qts, vq, 2);
711
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
712
+ qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true);
713
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
714
+ qvirtqueue_kick(qts, dev, vq, free_head);
715
+ write_head = free_head;
716
+
717
+ /* No notification expected */
718
+ status = qvirtio_wait_status_byte_no_isr(qts, dev,
719
+ vq, req_addr + 528,
720
+ QVIRTIO_BLK_TIMEOUT_US);
721
+ g_assert_cmpint(status, ==, 0);
722
+
723
+ guest_free(t_alloc, req_addr);
724
+
725
+ /* Read request */
726
+ req.type = VIRTIO_BLK_T_IN;
727
+ req.ioprio = 1;
728
+ req.sector = 1;
729
+ req.data = g_malloc0(512);
730
+
731
+ req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
732
+
733
+ g_free(req.data);
734
+
735
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
736
+ qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true);
737
+ qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false);
738
+
739
+ qvirtqueue_kick(qts, dev, vq, free_head);
740
+
741
+ /* We get just one notification for both requests */
742
+ qvirtio_wait_used_elem(qts, dev, vq, write_head, NULL,
743
+ QVIRTIO_BLK_TIMEOUT_US);
744
+ g_assert(qvirtqueue_get_buf(qts, vq, &desc_idx, NULL));
745
+ g_assert_cmpint(desc_idx, ==, free_head);
746
+
747
+ status = readb(req_addr + 528);
748
+ g_assert_cmpint(status, ==, 0);
749
+
750
+ data = g_malloc0(512);
751
+ qtest_memread(qts, req_addr + 16, data, 512);
752
+ g_assert_cmpstr(data, ==, "TEST");
753
+ g_free(data);
754
+
755
+ guest_free(t_alloc, req_addr);
756
+
757
+ /* End test */
758
+ qpci_msix_disable(pdev->pdev);
759
+
760
+ qvirtqueue_cleanup(dev->bus, vq, t_alloc);
761
+}
762
+
763
+static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
764
+{
765
+ QVirtioPCIDevice *dev1 = obj;
766
+ QVirtioPCIDevice *dev;
767
+ QTestState *qts = dev1->pdev->bus->qts;
768
+
769
+ /* plug secondary disk */
770
+ qtest_qmp_device_add(qts, "vhost-user-blk-pci", "drv1",
771
+ "{'addr': %s, 'chardev': 'char2'}",
772
+ stringify(PCI_SLOT_HP) ".0");
773
+
774
+ dev = virtio_pci_new(dev1->pdev->bus,
775
+ &(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0)
776
+ });
777
+ g_assert_nonnull(dev);
778
+ g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
779
+ qvirtio_pci_device_disable(dev);
780
+ qos_object_destroy((QOSGraphObject *)dev);
781
+
782
+ /* unplug secondary disk */
783
+ qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP);
784
+}
785
+
786
+/*
787
+ * Check that setting the vring addr on a non-existent virtqueue does
788
+ * not crash.
789
+ */
790
+static void test_nonexistent_virtqueue(void *obj, void *data,
791
+ QGuestAllocator *t_alloc)
792
+{
793
+ QVhostUserBlkPCI *blk = obj;
794
+ QVirtioPCIDevice *pdev = &blk->pci_vdev;
795
+ QPCIBar bar0;
796
+ QPCIDevice *dev;
797
+
798
+ dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0));
799
+ g_assert(dev != NULL);
800
+ qpci_device_enable(dev);
801
+
802
+ bar0 = qpci_iomap(dev, 0, NULL);
803
+
804
+ qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2);
805
+ qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1);
806
+
807
+ g_free(dev);
808
+}
809
+
810
+static const char *qtest_qemu_storage_daemon_binary(void)
811
+{
812
+ const char *qemu_storage_daemon_bin;
813
+
814
+ qemu_storage_daemon_bin = getenv("QTEST_QEMU_STORAGE_DAEMON_BINARY");
815
+ if (!qemu_storage_daemon_bin) {
816
+ fprintf(stderr, "Environment variable "
817
+ "QTEST_QEMU_STORAGE_DAEMON_BINARY required\n");
818
+ exit(0);
819
+ }
820
+
821
+ return qemu_storage_daemon_bin;
822
+}
823
+
824
+/* g_test_queue_destroy() cleanup function for files */
825
+static void destroy_file(void *path)
826
+{
827
+ unlink(path);
828
+ g_free(path);
829
+ qos_invalidate_command_line();
830
+}
831
+
832
+static char *drive_create(void)
833
+{
834
+ int fd, ret;
835
+ /** vhost-user-blk won't recognize drive located in /tmp */
836
+ char *t_path = g_strdup("qtest.XXXXXX");
837
+
838
+ /** Create a temporary raw image */
839
+ fd = mkstemp(t_path);
840
+ g_assert_cmpint(fd, >=, 0);
841
+ ret = ftruncate(fd, TEST_IMAGE_SIZE);
842
+ g_assert_cmpint(ret, ==, 0);
843
+ close(fd);
844
+
845
+ g_test_queue_destroy(destroy_file, t_path);
846
+ return t_path;
847
+}
848
+
849
+static char *create_listen_socket(int *fd)
850
+{
851
+ int tmp_fd;
852
+ char *path;
853
+
854
+ /* No race because our pid makes the path unique */
855
+ path = g_strdup_printf("/tmp/qtest-%d-sock.XXXXXX", getpid());
856
+ tmp_fd = mkstemp(path);
857
+ g_assert_cmpint(tmp_fd, >=, 0);
858
+ close(tmp_fd);
859
+ unlink(path);
860
+
861
+ *fd = qtest_socket_server(path);
862
+ g_test_queue_destroy(destroy_file, path);
863
+ return path;
864
+}
865
+
866
+/*
867
+ * g_test_queue_destroy() and qtest_add_abrt_handler() cleanup function for
868
+ * qemu-storage-daemon.
869
+ */
870
+static void quit_storage_daemon(void *data)
871
+{
872
+ QemuStorageDaemonState *qsd = data;
873
+ int wstatus;
874
+ pid_t pid;
875
+
876
+ /*
877
+ * If we were invoked as a g_test_queue_destroy() cleanup function we need
878
+ * to remove the abrt handler to avoid being called again if the code below
879
+ * aborts. Also, we must not leave the abrt handler installed after
880
+ * cleanup.
881
+ */
882
+ qtest_remove_abrt_handler(data);
883
+
884
+ /* Before quitting storage-daemon, quit qemu to avoid dubious messages */
885
+ qtest_kill_qemu(global_qtest);
886
+
887
+ kill(qsd->pid, SIGTERM);
888
+ pid = waitpid(qsd->pid, &wstatus, 0);
889
+ g_assert_cmpint(pid, ==, qsd->pid);
890
+ if (!WIFEXITED(wstatus)) {
891
+ fprintf(stderr, "%s: expected qemu-storage-daemon to exit\n",
892
+ __func__);
893
+ abort();
894
+ }
895
+ if (WEXITSTATUS(wstatus) != 0) {
896
+ fprintf(stderr, "%s: expected qemu-storage-daemon to exit "
897
+ "successfully, got %d\n",
898
+ __func__, WEXITSTATUS(wstatus));
899
+ abort();
900
+ }
901
+
902
+ g_free(data);
903
+}
904
+
905
+static void start_vhost_user_blk(GString *cmd_line, int vus_instances)
906
+{
907
+ const char *vhost_user_blk_bin = qtest_qemu_storage_daemon_binary();
908
+ int i;
909
+ gchar *img_path;
910
+ GString *storage_daemon_command = g_string_new(NULL);
911
+ QemuStorageDaemonState *qsd;
912
+
913
+ g_string_append_printf(storage_daemon_command,
914
+ "exec %s ",
915
+ vhost_user_blk_bin);
916
+
917
+ g_string_append_printf(cmd_line,
918
+ " -object memory-backend-memfd,id=mem,size=256M,share=on "
919
+ " -M memory-backend=mem -m 256M ");
920
+
921
+ for (i = 0; i < vus_instances; i++) {
922
+ int fd;
923
+ char *sock_path = create_listen_socket(&fd);
924
+
925
+ /* create image file */
926
+ img_path = drive_create();
927
+ g_string_append_printf(storage_daemon_command,
928
+ "--blockdev driver=file,node-name=disk%d,filename=%s "
929
+ "--export type=vhost-user-blk,id=disk%d,addr.type=unix,addr.path=%s,"
930
+ "node-name=disk%i,writable=on ",
931
+ i, img_path, i, sock_path, i);
932
+
933
+ g_string_append_printf(cmd_line, "-chardev socket,id=char%d,path=%s ",
934
+ i + 1, sock_path);
935
+ }
936
+
937
+ g_test_message("starting vhost-user backend: %s",
938
+ storage_daemon_command->str);
939
+ pid_t pid = fork();
940
+ if (pid == 0) {
941
+ /*
942
+ * Close standard file descriptors so tap-driver.pl pipe detects when
943
+ * our parent terminates.
944
+ */
945
+ close(0);
946
+ close(1);
947
+ open("/dev/null", O_RDONLY);
948
+ open("/dev/null", O_WRONLY);
949
+
950
+ execlp("/bin/sh", "sh", "-c", storage_daemon_command->str, NULL);
951
+ exit(1);
952
+ }
953
+ g_string_free(storage_daemon_command, true);
954
+
955
+ qsd = g_new(QemuStorageDaemonState, 1);
956
+ qsd->pid = pid;
957
+
958
+ /* Make sure qemu-storage-daemon is stopped */
959
+ qtest_add_abrt_handler(quit_storage_daemon, qsd);
960
+ g_test_queue_destroy(quit_storage_daemon, qsd);
961
+}
962
+
963
+static void *vhost_user_blk_test_setup(GString *cmd_line, void *arg)
964
+{
965
+ start_vhost_user_blk(cmd_line, 1);
966
+ return arg;
967
+}
968
+
969
+/*
970
+ * Setup for hotplug.
971
+ *
972
+ * Since vhost-user server only serves one vhost-user client one time,
973
+ * another exprot
974
+ *
975
+ */
976
+static void *vhost_user_blk_hotplug_test_setup(GString *cmd_line, void *arg)
977
+{
978
+ /* "-chardev socket,id=char2" is used for pci_hotplug*/
979
+ start_vhost_user_blk(cmd_line, 2);
980
+ return arg;
981
+}
982
+
983
+static void register_vhost_user_blk_test(void)
984
+{
985
+ QOSGraphTestOptions opts = {
986
+ .before = vhost_user_blk_test_setup,
987
+ };
988
+
989
+ /*
990
+ * tests for vhost-user-blk and vhost-user-blk-pci
991
+ * The tests are borrowed from tests/virtio-blk-test.c. But some tests
992
+ * regarding block_resize don't work for vhost-user-blk.
993
+ * vhost-user-blk device doesn't have -drive, so tests containing
994
+ * block_resize are also abandoned,
995
+ * - config
996
+ * - resize
997
+ */
998
+ qos_add_test("basic", "vhost-user-blk", basic, &opts);
999
+ qos_add_test("indirect", "vhost-user-blk", indirect, &opts);
1000
+ qos_add_test("idx", "vhost-user-blk-pci", idx, &opts);
1001
+ qos_add_test("nxvirtq", "vhost-user-blk-pci",
1002
+ test_nonexistent_virtqueue, &opts);
1003
+
1004
+ opts.before = vhost_user_blk_hotplug_test_setup;
1005
+ qos_add_test("hotplug", "vhost-user-blk-pci", pci_hotplug, &opts);
1006
+}
1007
+
1008
+libqos_init(register_vhost_user_blk_test);
1009
diff --git a/MAINTAINERS b/MAINTAINERS
1010
index XXXXXXX..XXXXXXX 100644
1011
--- a/MAINTAINERS
1012
+++ b/MAINTAINERS
1013
@@ -XXX,XX +XXX,XX @@ F: block/export/vhost-user-blk-server.c
1014
F: block/export/vhost-user-blk-server.h
1015
F: include/qemu/vhost-user-server.h
1016
F: tests/qtest/libqos/vhost-user-blk.c
1017
+F: tests/qtest/libqos/vhost-user-blk.h
1018
+F: tests/qtest/vhost-user-blk-test.c
1019
F: util/vhost-user-server.c
1020
1021
FUSE block device exports
1022
diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build
1023
index XXXXXXX..XXXXXXX 100644
1024
--- a/tests/qtest/libqos/meson.build
1025
+++ b/tests/qtest/libqos/meson.build
1026
@@ -XXX,XX +XXX,XX @@ libqos_srcs = files('../libqtest.c',
1027
'virtio-9p.c',
1028
'virtio-balloon.c',
1029
'virtio-blk.c',
1030
+ 'vhost-user-blk.c',
1031
'virtio-mmio.c',
1032
'virtio-net.c',
1033
'virtio-pci.c',
1034
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
1035
index XXXXXXX..XXXXXXX 100644
1036
--- a/tests/qtest/meson.build
1037
+++ b/tests/qtest/meson.build
1038
@@ -XXX,XX +XXX,XX @@ if have_virtfs
1039
qos_test_ss.add(files('virtio-9p-test.c'))
1040
endif
1041
qos_test_ss.add(when: 'CONFIG_VHOST_USER', if_true: files('vhost-user-test.c'))
1042
+if have_vhost_user_blk_server
1043
+ qos_test_ss.add(files('vhost-user-blk-test.c'))
1044
+endif
1045
1046
tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c']
1047
1048
@@ -XXX,XX +XXX,XX @@ foreach dir : target_dirs
1049
endif
1050
qtest_env.set('G_TEST_DBUS_DAEMON', meson.source_root() / 'tests/dbus-vmstate-daemon.sh')
1051
qtest_env.set('QTEST_QEMU_BINARY', './qemu-system-' + target_base)
1052
+ qtest_env.set('QTEST_QEMU_STORAGE_DAEMON_BINARY', './storage-daemon/qemu-storage-daemon')
1053
1054
foreach test : target_qtests
1055
# Executables are shared across targets, declare them only the first time we
1056
--
1057
2.29.2
1058
1059
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
4
Message-Id: <20210223144653.811468-7-stefanha@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
7
tests/qtest/vhost-user-blk-test.c | 81 +++++++++++++++++++++++++++++--
8
1 file changed, 76 insertions(+), 5 deletions(-)
9
10
diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c
11
index XXXXXXX..XXXXXXX 100644
12
--- a/tests/qtest/vhost-user-blk-test.c
13
+++ b/tests/qtest/vhost-user-blk-test.c
14
@@ -XXX,XX +XXX,XX @@ static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
15
qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP);
16
}
17
18
+static void multiqueue(void *obj, void *data, QGuestAllocator *t_alloc)
19
+{
20
+ QVirtioPCIDevice *pdev1 = obj;
21
+ QVirtioDevice *dev1 = &pdev1->vdev;
22
+ QVirtioPCIDevice *pdev8;
23
+ QVirtioDevice *dev8;
24
+ QTestState *qts = pdev1->pdev->bus->qts;
25
+ uint64_t features;
26
+ uint16_t num_queues;
27
+
28
+ /*
29
+ * The primary device has 1 queue and VIRTIO_BLK_F_MQ is not enabled. The
30
+ * VIRTIO specification allows VIRTIO_BLK_F_MQ to be enabled when there is
31
+ * only 1 virtqueue, but --device vhost-user-blk-pci doesn't do this (which
32
+ * is also spec-compliant).
33
+ */
34
+ features = qvirtio_get_features(dev1);
35
+ g_assert_cmpint(features & (1u << VIRTIO_BLK_F_MQ), ==, 0);
36
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
37
+ (1u << VIRTIO_RING_F_INDIRECT_DESC) |
38
+ (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
39
+ (1u << VIRTIO_BLK_F_SCSI));
40
+ qvirtio_set_features(dev1, features);
41
+
42
+ /* Hotplug a secondary device with 8 queues */
43
+ qtest_qmp_device_add(qts, "vhost-user-blk-pci", "drv1",
44
+ "{'addr': %s, 'chardev': 'char2', 'num-queues': 8}",
45
+ stringify(PCI_SLOT_HP) ".0");
46
+
47
+ pdev8 = virtio_pci_new(pdev1->pdev->bus,
48
+ &(QPCIAddress) {
49
+ .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0)
50
+ });
51
+ g_assert_nonnull(pdev8);
52
+ g_assert_cmpint(pdev8->vdev.device_type, ==, VIRTIO_ID_BLOCK);
53
+
54
+ qos_object_start_hw(&pdev8->obj);
55
+
56
+ dev8 = &pdev8->vdev;
57
+ features = qvirtio_get_features(dev8);
58
+ g_assert_cmpint(features & (1u << VIRTIO_BLK_F_MQ),
59
+ ==,
60
+ (1u << VIRTIO_BLK_F_MQ));
61
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
62
+ (1u << VIRTIO_RING_F_INDIRECT_DESC) |
63
+ (1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
64
+ (1u << VIRTIO_BLK_F_SCSI) |
65
+ (1u << VIRTIO_BLK_F_MQ));
66
+ qvirtio_set_features(dev8, features);
67
+
68
+ num_queues = qvirtio_config_readw(dev8,
69
+ offsetof(struct virtio_blk_config, num_queues));
70
+ g_assert_cmpint(num_queues, ==, 8);
71
+
72
+ qvirtio_pci_device_disable(pdev8);
73
+ qos_object_destroy(&pdev8->obj);
74
+
75
+ /* unplug secondary disk */
76
+ qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP);
77
+}
78
+
79
/*
80
* Check that setting the vring addr on a non-existent virtqueue does
81
* not crash.
82
@@ -XXX,XX +XXX,XX @@ static void quit_storage_daemon(void *data)
83
g_free(data);
84
}
85
86
-static void start_vhost_user_blk(GString *cmd_line, int vus_instances)
87
+static void start_vhost_user_blk(GString *cmd_line, int vus_instances,
88
+ int num_queues)
89
{
90
const char *vhost_user_blk_bin = qtest_qemu_storage_daemon_binary();
91
int i;
92
@@ -XXX,XX +XXX,XX @@ static void start_vhost_user_blk(GString *cmd_line, int vus_instances)
93
g_string_append_printf(storage_daemon_command,
94
"--blockdev driver=file,node-name=disk%d,filename=%s "
95
"--export type=vhost-user-blk,id=disk%d,addr.type=unix,addr.path=%s,"
96
- "node-name=disk%i,writable=on ",
97
- i, img_path, i, sock_path, i);
98
+ "node-name=disk%i,writable=on,num-queues=%d ",
99
+ i, img_path, i, sock_path, i, num_queues);
100
101
g_string_append_printf(cmd_line, "-chardev socket,id=char%d,path=%s ",
102
i + 1, sock_path);
103
@@ -XXX,XX +XXX,XX @@ static void start_vhost_user_blk(GString *cmd_line, int vus_instances)
104
105
static void *vhost_user_blk_test_setup(GString *cmd_line, void *arg)
106
{
107
- start_vhost_user_blk(cmd_line, 1);
108
+ start_vhost_user_blk(cmd_line, 1, 1);
109
return arg;
110
}
111
112
@@ -XXX,XX +XXX,XX @@ static void *vhost_user_blk_test_setup(GString *cmd_line, void *arg)
113
static void *vhost_user_blk_hotplug_test_setup(GString *cmd_line, void *arg)
114
{
115
/* "-chardev socket,id=char2" is used for pci_hotplug*/
116
- start_vhost_user_blk(cmd_line, 2);
117
+ start_vhost_user_blk(cmd_line, 2, 1);
118
+ return arg;
119
+}
120
+
121
+static void *vhost_user_blk_multiqueue_test_setup(GString *cmd_line, void *arg)
122
+{
123
+ start_vhost_user_blk(cmd_line, 2, 8);
124
return arg;
125
}
126
127
@@ -XXX,XX +XXX,XX @@ static void register_vhost_user_blk_test(void)
128
129
opts.before = vhost_user_blk_hotplug_test_setup;
130
qos_add_test("hotplug", "vhost-user-blk-pci", pci_hotplug, &opts);
131
+
132
+ opts.before = vhost_user_blk_multiqueue_test_setup;
133
+ qos_add_test("multiqueue", "vhost-user-blk-pci", multiqueue, &opts);
134
}
135
136
libqos_init(register_vhost_user_blk_test);
137
--
138
2.29.2
139
140
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
The config->blk_size field is little-endian. Use the native-endian
4
blk_size variable to avoid double byteswapping.
5
6
Fixes: 11f60f7eaee2630dd6fa0c3a8c49f792e46c4cf1 ("block/export: make vhost-user-blk config space little-endian")
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Message-Id: <20210223144653.811468-8-stefanha@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
block/export/vhost-user-blk-server.c | 2 +-
12
1 file changed, 1 insertion(+), 1 deletion(-)
13
14
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/block/export/vhost-user-blk-server.c
17
+++ b/block/export/vhost-user-blk-server.c
18
@@ -XXX,XX +XXX,XX @@ vu_blk_initialize_config(BlockDriverState *bs,
19
config->num_queues = cpu_to_le16(num_queues);
20
config->max_discard_sectors = cpu_to_le32(32768);
21
config->max_discard_seg = cpu_to_le32(1);
22
- config->discard_sector_alignment = cpu_to_le32(config->blk_size >> 9);
23
+ config->discard_sector_alignment = cpu_to_le32(blk_size >> 9);
24
config->max_write_zeroes_sectors = cpu_to_le32(32768);
25
config->max_write_zeroes_seg = cpu_to_le32(1);
26
}
27
--
28
2.29.2
29
30
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
Use VIRTIO_BLK_SECTOR_BITS and VIRTIO_BLK_SECTOR_SIZE when dealing with
4
virtio-blk sector numbers. Although the values happen to be the same as
5
BDRV_SECTOR_BITS and BDRV_SECTOR_SIZE, they are conceptually different.
6
This makes it clearer when we are dealing with virtio-blk sector units.
7
8
Use VIRTIO_BLK_SECTOR_BITS in vu_blk_initialize_config(). Later patches
9
will use it the new constants the virtqueue request processing code
10
path.
11
12
Suggested-by: Max Reitz <mreitz@redhat.com>
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Message-Id: <20210223144653.811468-9-stefanha@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
17
block/export/vhost-user-blk-server.c | 15 ++++++++++++---
18
1 file changed, 12 insertions(+), 3 deletions(-)
19
20
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/block/export/vhost-user-blk-server.c
23
+++ b/block/export/vhost-user-blk-server.c
24
@@ -XXX,XX +XXX,XX @@
25
#include "sysemu/block-backend.h"
26
#include "util/block-helpers.h"
27
28
+/*
29
+ * Sector units are 512 bytes regardless of the
30
+ * virtio_blk_config->blk_size value.
31
+ */
32
+#define VIRTIO_BLK_SECTOR_BITS 9
33
+#define VIRTIO_BLK_SECTOR_SIZE (1ull << VIRTIO_BLK_SECTOR_BITS)
34
+
35
enum {
36
VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1,
37
};
38
@@ -XXX,XX +XXX,XX @@ vu_blk_initialize_config(BlockDriverState *bs,
39
uint32_t blk_size,
40
uint16_t num_queues)
41
{
42
- config->capacity = cpu_to_le64(bdrv_getlength(bs) >> BDRV_SECTOR_BITS);
43
+ config->capacity =
44
+ cpu_to_le64(bdrv_getlength(bs) >> VIRTIO_BLK_SECTOR_BITS);
45
config->blk_size = cpu_to_le32(blk_size);
46
config->size_max = cpu_to_le32(0);
47
config->seg_max = cpu_to_le32(128 - 2);
48
@@ -XXX,XX +XXX,XX @@ vu_blk_initialize_config(BlockDriverState *bs,
49
config->num_queues = cpu_to_le16(num_queues);
50
config->max_discard_sectors = cpu_to_le32(32768);
51
config->max_discard_seg = cpu_to_le32(1);
52
- config->discard_sector_alignment = cpu_to_le32(blk_size >> 9);
53
+ config->discard_sector_alignment =
54
+ cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS);
55
config->max_write_zeroes_sectors = cpu_to_le32(32768);
56
config->max_write_zeroes_seg = cpu_to_le32(1);
57
}
58
@@ -XXX,XX +XXX,XX @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
59
if (vu_opts->has_logical_block_size) {
60
logical_block_size = vu_opts->logical_block_size;
61
} else {
62
- logical_block_size = BDRV_SECTOR_SIZE;
63
+ logical_block_size = VIRTIO_BLK_SECTOR_SIZE;
64
}
65
check_block_size(exp->id, "logical-block-size", logical_block_size,
66
&local_err);
67
--
68
2.29.2
69
70
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
The driver is supposed to honor the blk_size field but the protocol
4
still uses 512-byte sector numbers. It is incorrect to multiply
5
req->sector_num by blk_size.
6
7
VIRTIO 1.1 5.2.5 Device Initialization says:
8
9
blk_size can be read to determine the optimal sector size for the
10
driver to use. This does not affect the units used in the protocol
11
(always 512 bytes), but awareness of the correct value can affect
12
performance.
13
14
Fixes: 3578389bcf76c824a5d82e6586a6f0c71e56f2aa ("block/export: vhost-user block device backend server")
15
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
16
Message-Id: <20210223144653.811468-10-stefanha@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
---
19
block/export/vhost-user-blk-server.c | 2 +-
20
1 file changed, 1 insertion(+), 1 deletion(-)
21
22
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
23
index XXXXXXX..XXXXXXX 100644
24
--- a/block/export/vhost-user-blk-server.c
25
+++ b/block/export/vhost-user-blk-server.c
26
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
27
break;
28
}
29
30
- int64_t offset = req->sector_num * vexp->blk_size;
31
+ int64_t offset = req->sector_num << VIRTIO_BLK_SECTOR_BITS;
32
QEMUIOVector qiov;
33
if (is_write) {
34
qemu_iovec_init_external(&qiov, out_iov, out_num);
35
--
36
2.29.2
37
38
diff view generated by jsdifflib
1
Instead of using monitor_printf() to report errors, hmp_commit() should
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
use error_report() like other places do.
3
2
3
Validate discard/write zeroes the same way we do for virtio-blk. Some of
4
these checks are mandated by the VIRTIO specification, others are
5
internal to QEMU.
6
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Message-Id: <20210223144653.811468-11-stefanha@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
7
---
10
---
8
blockdev.c | 7 +++----
11
block/export/vhost-user-blk-server.c | 116 +++++++++++++++++++++------
9
1 file changed, 3 insertions(+), 4 deletions(-)
12
1 file changed, 93 insertions(+), 23 deletions(-)
10
13
11
diff --git a/blockdev.c b/blockdev.c
14
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
12
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
13
--- a/blockdev.c
16
--- a/block/export/vhost-user-blk-server.c
14
+++ b/blockdev.c
17
+++ b/block/export/vhost-user-blk-server.c
15
@@ -XXX,XX +XXX,XX @@ void hmp_commit(Monitor *mon, const QDict *qdict)
18
@@ -XXX,XX +XXX,XX @@
16
19
17
blk = blk_by_name(device);
20
enum {
18
if (!blk) {
21
VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1,
19
- monitor_printf(mon, "Device '%s' not found\n", device);
22
+ VHOST_USER_BLK_MAX_DISCARD_SECTORS = 32768,
20
+ error_report("Device '%s' not found", device);
23
+ VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS = 32768,
21
return;
24
};
25
struct virtio_blk_inhdr {
26
unsigned char status;
27
@@ -XXX,XX +XXX,XX @@ static void vu_blk_req_complete(VuBlkReq *req)
28
free(req);
29
}
30
31
+static bool vu_blk_sect_range_ok(VuBlkExport *vexp, uint64_t sector,
32
+ size_t size)
33
+{
34
+ uint64_t nb_sectors = size >> BDRV_SECTOR_BITS;
35
+ uint64_t total_sectors;
36
+
37
+ if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
38
+ return false;
39
+ }
40
+ if ((sector << VIRTIO_BLK_SECTOR_BITS) % vexp->blk_size) {
41
+ return false;
42
+ }
43
+ blk_get_geometry(vexp->export.blk, &total_sectors);
44
+ if (sector > total_sectors || nb_sectors > total_sectors - sector) {
45
+ return false;
46
+ }
47
+ return true;
48
+}
49
+
50
static int coroutine_fn
51
-vu_blk_discard_write_zeroes(BlockBackend *blk, struct iovec *iov,
52
+vu_blk_discard_write_zeroes(VuBlkExport *vexp, struct iovec *iov,
53
uint32_t iovcnt, uint32_t type)
54
{
55
+ BlockBackend *blk = vexp->export.blk;
56
struct virtio_blk_discard_write_zeroes desc;
57
- ssize_t size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc));
58
+ ssize_t size;
59
+ uint64_t sector;
60
+ uint32_t num_sectors;
61
+ uint32_t max_sectors;
62
+ uint32_t flags;
63
+ int bytes;
64
+
65
+ /* Only one desc is currently supported */
66
+ if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) {
67
+ return VIRTIO_BLK_S_UNSUPP;
68
+ }
69
+
70
+ size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc));
71
if (unlikely(size != sizeof(desc))) {
72
- error_report("Invalid size %zd, expect %zu", size, sizeof(desc));
73
- return -EINVAL;
74
+ error_report("Invalid size %zd, expected %zu", size, sizeof(desc));
75
+ return VIRTIO_BLK_S_IOERR;
76
}
77
78
- uint64_t range[2] = { le64_to_cpu(desc.sector) << 9,
79
- le32_to_cpu(desc.num_sectors) << 9 };
80
- if (type == VIRTIO_BLK_T_DISCARD) {
81
- if (blk_co_pdiscard(blk, range[0], range[1]) == 0) {
82
- return 0;
83
+ sector = le64_to_cpu(desc.sector);
84
+ num_sectors = le32_to_cpu(desc.num_sectors);
85
+ flags = le32_to_cpu(desc.flags);
86
+ max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ?
87
+ VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS :
88
+ VHOST_USER_BLK_MAX_DISCARD_SECTORS;
89
+
90
+ /* This check ensures that 'bytes' fits in an int */
91
+ if (unlikely(num_sectors > max_sectors)) {
92
+ return VIRTIO_BLK_S_IOERR;
93
+ }
94
+
95
+ bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS;
96
+
97
+ if (unlikely(!vu_blk_sect_range_ok(vexp, sector, bytes))) {
98
+ return VIRTIO_BLK_S_IOERR;
99
+ }
100
+
101
+ /*
102
+ * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard
103
+ * and write zeroes commands if any unknown flag is set.
104
+ */
105
+ if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
106
+ return VIRTIO_BLK_S_UNSUPP;
107
+ }
108
+
109
+ if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
110
+ int blk_flags = 0;
111
+
112
+ if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) {
113
+ blk_flags |= BDRV_REQ_MAY_UNMAP;
114
+ }
115
+
116
+ if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS,
117
+ bytes, blk_flags) == 0) {
118
+ return VIRTIO_BLK_S_OK;
22
}
119
}
23
if (!blk_is_available(blk)) {
120
- } else if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
24
- monitor_printf(mon, "Device '%s' has no medium\n", device);
121
- if (blk_co_pwrite_zeroes(blk, range[0], range[1], 0) == 0) {
25
+ error_report("Device '%s' has no medium", device);
122
- return 0;
26
return;
123
+ } else if (type == VIRTIO_BLK_T_DISCARD) {
124
+ /*
125
+ * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for
126
+ * discard commands if the unmap flag is set.
127
+ */
128
+ if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
129
+ return VIRTIO_BLK_S_UNSUPP;
130
+ }
131
+
132
+ if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS,
133
+ bytes) == 0) {
134
+ return VIRTIO_BLK_S_OK;
27
}
135
}
28
29
@@ -XXX,XX +XXX,XX @@ void hmp_commit(Monitor *mon, const QDict *qdict)
30
aio_context_release(aio_context);
31
}
136
}
32
if (ret < 0) {
137
33
- monitor_printf(mon, "'commit' error for '%s': %s\n", device,
138
- return -EINVAL;
34
- strerror(-ret));
139
+ return VIRTIO_BLK_S_IOERR;
35
+ error_report("'commit' error for '%s': %s", device, strerror(-ret));
140
}
141
142
static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
143
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
36
}
144
}
145
case VIRTIO_BLK_T_DISCARD:
146
case VIRTIO_BLK_T_WRITE_ZEROES: {
147
- int rc;
148
-
149
if (!vexp->writable) {
150
req->in->status = VIRTIO_BLK_S_IOERR;
151
break;
152
}
153
154
- rc = vu_blk_discard_write_zeroes(blk, &elem->out_sg[1], out_num, type);
155
- if (rc == 0) {
156
- req->in->status = VIRTIO_BLK_S_OK;
157
- } else {
158
- req->in->status = VIRTIO_BLK_S_IOERR;
159
- }
160
+ req->in->status = vu_blk_discard_write_zeroes(vexp, out_iov, out_num,
161
+ type);
162
break;
163
}
164
default:
165
@@ -XXX,XX +XXX,XX @@ vu_blk_initialize_config(BlockDriverState *bs,
166
config->min_io_size = cpu_to_le16(1);
167
config->opt_io_size = cpu_to_le32(1);
168
config->num_queues = cpu_to_le16(num_queues);
169
- config->max_discard_sectors = cpu_to_le32(32768);
170
+ config->max_discard_sectors =
171
+ cpu_to_le32(VHOST_USER_BLK_MAX_DISCARD_SECTORS);
172
config->max_discard_seg = cpu_to_le32(1);
173
config->discard_sector_alignment =
174
cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS);
175
- config->max_write_zeroes_sectors = cpu_to_le32(32768);
176
+ config->max_write_zeroes_sectors
177
+ = cpu_to_le32(VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS);
178
config->max_write_zeroes_seg = cpu_to_le32(1);
37
}
179
}
38
180
39
--
181
--
40
2.20.1
182
2.29.2
41
183
42
184
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
Exercise input validation code paths in
4
block/export/vhost-user-blk-server.c.
5
6
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Message-Id: <20210223144653.811468-12-stefanha@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
tests/qtest/vhost-user-blk-test.c | 124 ++++++++++++++++++++++++++++++
11
1 file changed, 124 insertions(+)
12
13
diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/tests/qtest/vhost-user-blk-test.c
16
+++ b/tests/qtest/vhost-user-blk-test.c
17
@@ -XXX,XX +XXX,XX @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d,
18
return addr;
19
}
20
21
+static void test_invalid_discard_write_zeroes(QVirtioDevice *dev,
22
+ QGuestAllocator *alloc,
23
+ QTestState *qts,
24
+ QVirtQueue *vq,
25
+ uint32_t type)
26
+{
27
+ QVirtioBlkReq req;
28
+ struct virtio_blk_discard_write_zeroes dwz_hdr;
29
+ struct virtio_blk_discard_write_zeroes dwz_hdr2[2];
30
+ uint64_t req_addr;
31
+ uint32_t free_head;
32
+ uint8_t status;
33
+
34
+ /* More than one dwz is not supported */
35
+ req.type = type;
36
+ req.data = (char *) dwz_hdr2;
37
+ dwz_hdr2[0].sector = 0;
38
+ dwz_hdr2[0].num_sectors = 1;
39
+ dwz_hdr2[0].flags = 0;
40
+ dwz_hdr2[1].sector = 1;
41
+ dwz_hdr2[1].num_sectors = 1;
42
+ dwz_hdr2[1].flags = 0;
43
+
44
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr2[0]);
45
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr2[1]);
46
+
47
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr2));
48
+
49
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
50
+ qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr2), false, true);
51
+ qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr2), 1, true,
52
+ false);
53
+
54
+ qvirtqueue_kick(qts, dev, vq, free_head);
55
+
56
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
57
+ QVIRTIO_BLK_TIMEOUT_US);
58
+ status = readb(req_addr + 16 + sizeof(dwz_hdr2));
59
+ g_assert_cmpint(status, ==, VIRTIO_BLK_S_UNSUPP);
60
+
61
+ guest_free(alloc, req_addr);
62
+
63
+ /* num_sectors must be less than config->max_write_zeroes_sectors */
64
+ req.type = type;
65
+ req.data = (char *) &dwz_hdr;
66
+ dwz_hdr.sector = 0;
67
+ dwz_hdr.num_sectors = 0xffffffff;
68
+ dwz_hdr.flags = 0;
69
+
70
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
71
+
72
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
73
+
74
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
75
+ qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
76
+ qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
77
+ false);
78
+
79
+ qvirtqueue_kick(qts, dev, vq, free_head);
80
+
81
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
82
+ QVIRTIO_BLK_TIMEOUT_US);
83
+ status = readb(req_addr + 16 + sizeof(dwz_hdr));
84
+ g_assert_cmpint(status, ==, VIRTIO_BLK_S_IOERR);
85
+
86
+ guest_free(alloc, req_addr);
87
+
88
+ /* sector must be less than the device capacity */
89
+ req.type = type;
90
+ req.data = (char *) &dwz_hdr;
91
+ dwz_hdr.sector = TEST_IMAGE_SIZE / 512 + 1;
92
+ dwz_hdr.num_sectors = 1;
93
+ dwz_hdr.flags = 0;
94
+
95
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
96
+
97
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
98
+
99
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
100
+ qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
101
+ qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
102
+ false);
103
+
104
+ qvirtqueue_kick(qts, dev, vq, free_head);
105
+
106
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
107
+ QVIRTIO_BLK_TIMEOUT_US);
108
+ status = readb(req_addr + 16 + sizeof(dwz_hdr));
109
+ g_assert_cmpint(status, ==, VIRTIO_BLK_S_IOERR);
110
+
111
+ guest_free(alloc, req_addr);
112
+
113
+ /* reserved flag bits must be zero */
114
+ req.type = type;
115
+ req.data = (char *) &dwz_hdr;
116
+ dwz_hdr.sector = 0;
117
+ dwz_hdr.num_sectors = 1;
118
+ dwz_hdr.flags = ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP;
119
+
120
+ virtio_blk_fix_dwz_hdr(dev, &dwz_hdr);
121
+
122
+ req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr));
123
+
124
+ free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true);
125
+ qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true);
126
+ qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true,
127
+ false);
128
+
129
+ qvirtqueue_kick(qts, dev, vq, free_head);
130
+
131
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
132
+ QVIRTIO_BLK_TIMEOUT_US);
133
+ status = readb(req_addr + 16 + sizeof(dwz_hdr));
134
+ g_assert_cmpint(status, ==, VIRTIO_BLK_S_UNSUPP);
135
+
136
+ guest_free(alloc, req_addr);
137
+}
138
+
139
/* Returns the request virtqueue so the caller can perform further tests */
140
static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc)
141
{
142
@@ -XXX,XX +XXX,XX @@ static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc)
143
g_free(data);
144
145
guest_free(alloc, req_addr);
146
+
147
+ test_invalid_discard_write_zeroes(dev, alloc, qts, vq,
148
+ VIRTIO_BLK_T_WRITE_ZEROES);
149
}
150
151
if (features & (1u << VIRTIO_BLK_F_DISCARD)) {
152
@@ -XXX,XX +XXX,XX @@ static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc)
153
g_assert_cmpint(status, ==, 0);
154
155
guest_free(alloc, req_addr);
156
+
157
+ test_invalid_discard_write_zeroes(dev, alloc, qts, vq,
158
+ VIRTIO_BLK_T_DISCARD);
159
}
160
161
if (features & (1u << VIRTIO_F_ANY_LAYOUT)) {
162
--
163
2.29.2
164
165
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
Check that the sector number and byte count are valid.
4
5
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
6
Message-Id: <20210223144653.811468-13-stefanha@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
9
block/export/vhost-user-blk-server.c | 19 ++++++++++++++++---
10
1 file changed, 16 insertions(+), 3 deletions(-)
11
12
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
13
index XXXXXXX..XXXXXXX 100644
14
--- a/block/export/vhost-user-blk-server.c
15
+++ b/block/export/vhost-user-blk-server.c
16
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
17
switch (type & ~VIRTIO_BLK_T_BARRIER) {
18
case VIRTIO_BLK_T_IN:
19
case VIRTIO_BLK_T_OUT: {
20
+ QEMUIOVector qiov;
21
+ int64_t offset;
22
ssize_t ret = 0;
23
bool is_write = type & VIRTIO_BLK_T_OUT;
24
req->sector_num = le64_to_cpu(req->out.sector);
25
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
26
break;
27
}
28
29
- int64_t offset = req->sector_num << VIRTIO_BLK_SECTOR_BITS;
30
- QEMUIOVector qiov;
31
if (is_write) {
32
qemu_iovec_init_external(&qiov, out_iov, out_num);
33
- ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0);
34
} else {
35
qemu_iovec_init_external(&qiov, in_iov, in_num);
36
+ }
37
+
38
+ if (unlikely(!vu_blk_sect_range_ok(vexp,
39
+ req->sector_num,
40
+ qiov.size))) {
41
+ req->in->status = VIRTIO_BLK_S_IOERR;
42
+ break;
43
+ }
44
+
45
+ offset = req->sector_num << VIRTIO_BLK_SECTOR_BITS;
46
+
47
+ if (is_write) {
48
+ ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0);
49
+ } else {
50
ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0);
51
}
52
if (ret >= 0) {
53
--
54
2.29.2
55
56
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
Rename bytes_covered_by_bitmap_cluster() to
4
bdrv_dirty_bitmap_serialization_coverage() and make it public.
5
It is needed as we are going to share it with bitmap loading in
6
parallels format.
7
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Reviewed-by: Denis V. Lunev <den@openvz.org>
11
Message-Id: <20210224104707.88430-2-vsementsov@virtuozzo.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
14
include/block/dirty-bitmap.h | 2 ++
15
block/dirty-bitmap.c | 13 +++++++++++++
16
block/qcow2-bitmap.c | 16 ++--------------
17
3 files changed, 17 insertions(+), 14 deletions(-)
18
19
diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
20
index XXXXXXX..XXXXXXX 100644
21
--- a/include/block/dirty-bitmap.h
22
+++ b/include/block/dirty-bitmap.h
23
@@ -XXX,XX +XXX,XX @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter);
24
uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap,
25
uint64_t offset, uint64_t bytes);
26
uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap);
27
+uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size,
28
+ const BdrvDirtyBitmap *bitmap);
29
void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap,
30
uint8_t *buf, uint64_t offset,
31
uint64_t bytes);
32
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
33
index XXXXXXX..XXXXXXX 100644
34
--- a/block/dirty-bitmap.c
35
+++ b/block/dirty-bitmap.c
36
@@ -XXX,XX +XXX,XX @@ uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap)
37
return hbitmap_serialization_align(bitmap->bitmap);
38
}
39
40
+/* Return the disk size covered by a chunk of serialized bitmap data. */
41
+uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size,
42
+ const BdrvDirtyBitmap *bitmap)
43
+{
44
+ uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
45
+ uint64_t limit = granularity * (serialized_chunk_size << 3);
46
+
47
+ assert(QEMU_IS_ALIGNED(limit,
48
+ bdrv_dirty_bitmap_serialization_align(bitmap)));
49
+ return limit;
50
+}
51
+
52
+
53
void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap,
54
uint8_t *buf, uint64_t offset,
55
uint64_t bytes)
56
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
57
index XXXXXXX..XXXXXXX 100644
58
--- a/block/qcow2-bitmap.c
59
+++ b/block/qcow2-bitmap.c
60
@@ -XXX,XX +XXX,XX @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb)
61
return 0;
62
}
63
64
-/* Return the disk size covered by a single qcow2 cluster of bitmap data. */
65
-static uint64_t bytes_covered_by_bitmap_cluster(const BDRVQcow2State *s,
66
- const BdrvDirtyBitmap *bitmap)
67
-{
68
- uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
69
- uint64_t limit = granularity * (s->cluster_size << 3);
70
-
71
- assert(QEMU_IS_ALIGNED(limit,
72
- bdrv_dirty_bitmap_serialization_align(bitmap)));
73
- return limit;
74
-}
75
-
76
/* load_bitmap_data
77
* @bitmap_table entries must satisfy specification constraints.
78
* @bitmap must be cleared */
79
@@ -XXX,XX +XXX,XX @@ static int load_bitmap_data(BlockDriverState *bs,
80
}
81
82
buf = g_malloc(s->cluster_size);
83
- limit = bytes_covered_by_bitmap_cluster(s, bitmap);
84
+ limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
85
for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) {
86
uint64_t count = MIN(bm_size - offset, limit);
87
uint64_t entry = bitmap_table[i];
88
@@ -XXX,XX +XXX,XX @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,
89
}
90
91
buf = g_malloc(s->cluster_size);
92
- limit = bytes_covered_by_bitmap_cluster(s, bitmap);
93
+ limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
94
assert(DIV_ROUND_UP(bm_size, limit) == tb_size);
95
96
offset = 0;
97
--
98
2.29.2
99
100
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
Actually L1 table entry offset is in 512 bytes sectors. Fix the spec.
4
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Message-Id: <20210224104707.88430-3-vsementsov@virtuozzo.com>
7
Reviewed-by: Denis V. Lunev <den@openvz.org>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
docs/interop/parallels.txt | 28 ++++++++++++++++------------
11
1 file changed, 16 insertions(+), 12 deletions(-)
12
13
diff --git a/docs/interop/parallels.txt b/docs/interop/parallels.txt
14
index XXXXXXX..XXXXXXX 100644
15
--- a/docs/interop/parallels.txt
16
+++ b/docs/interop/parallels.txt
17
@@ -XXX,XX +XXX,XX @@ of its data area are:
18
28 - 31: l1_size
19
The number of entries in the L1 table of the bitmap.
20
21
- variable: l1_table (8 * l1_size bytes)
22
- L1 offset table (in bytes)
23
+ variable: L1 offset table (l1_table), size: 8 * l1_size bytes
24
25
-A dirty bitmap is stored using a one-level structure for the mapping to host
26
-clusters - an L1 table.
27
+The dirty bitmap described by this feature extension is stored in a set of
28
+clusters inside the Parallels image file. The offsets of these clusters are
29
+saved in the L1 offset table specified by the feature extension. Each L1 table
30
+entry is a 64 bit integer as described below:
31
32
-Given an offset in bytes into the bitmap data, the offset in bytes into the
33
-image file can be obtained as follows:
34
+Given an offset in bytes into the bitmap data, corresponding L1 entry is
35
36
- offset = l1_table[offset / cluster_size] + (offset % cluster_size)
37
+ l1_table[offset / cluster_size]
38
39
-If an L1 table entry is 0, the corresponding cluster of the bitmap is assumed
40
-to be zero.
41
+If an L1 table entry is 0, all bits in the corresponding cluster of the bitmap
42
+are assumed to be 0.
43
44
-If an L1 table entry is 1, the corresponding cluster of the bitmap is assumed
45
-to have all bits set.
46
+If an L1 table entry is 1, all bits in the corresponding cluster of the bitmap
47
+are assumed to be 1.
48
49
-If an L1 table entry is not 0 or 1, it allocates a cluster from the data area.
50
+If an L1 table entry is not 0 or 1, it contains the corresponding cluster
51
+offset (in 512b sectors). Given an offset in bytes into the bitmap data the
52
+offset in bytes into the image file can be obtained as follows:
53
+
54
+ offset = l1_table[offset / cluster_size] * 512 + (offset % cluster_size)
55
--
56
2.29.2
57
58
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
We are going to use it in more places, calculating
4
"s->tracks << BDRV_SECTOR_BITS" doesn't look good.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Message-Id: <20210224104707.88430-4-vsementsov@virtuozzo.com>
8
Reviewed-by: Denis V. Lunev <den@openvz.org>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
block/parallels.h | 1 +
12
block/parallels.c | 8 ++++----
13
2 files changed, 5 insertions(+), 4 deletions(-)
14
15
diff --git a/block/parallels.h b/block/parallels.h
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/parallels.h
18
+++ b/block/parallels.h
19
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVParallelsState {
20
ParallelsPreallocMode prealloc_mode;
21
22
unsigned int tracks;
23
+ unsigned int cluster_size;
24
25
unsigned int off_multiplier;
26
Error *migration_blocker;
27
diff --git a/block/parallels.c b/block/parallels.c
28
index XXXXXXX..XXXXXXX 100644
29
--- a/block/parallels.c
30
+++ b/block/parallels.c
31
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs,
32
int ret;
33
uint32_t i;
34
bool flush_bat = false;
35
- int cluster_size = s->tracks << BDRV_SECTOR_BITS;
36
37
size = bdrv_getlength(bs->file->bs);
38
if (size < 0) {
39
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs,
40
high_off = off;
41
}
42
43
- if (prev_off != 0 && (prev_off + cluster_size) != off) {
44
+ if (prev_off != 0 && (prev_off + s->cluster_size) != off) {
45
res->bfi.fragmented_clusters++;
46
}
47
prev_off = off;
48
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs,
49
}
50
}
51
52
- res->image_end_offset = high_off + cluster_size;
53
+ res->image_end_offset = high_off + s->cluster_size;
54
if (size > res->image_end_offset) {
55
int64_t count;
56
- count = DIV_ROUND_UP(size - res->image_end_offset, cluster_size);
57
+ count = DIV_ROUND_UP(size - res->image_end_offset, s->cluster_size);
58
fprintf(stderr, "%s space leaked at the end of the image %" PRId64 "\n",
59
fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR",
60
size - res->image_end_offset);
61
@@ -XXX,XX +XXX,XX @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
62
ret = -EFBIG;
63
goto fail;
64
}
65
+ s->cluster_size = s->tracks << BDRV_SECTOR_BITS;
66
67
s->bat_size = le32_to_cpu(ph.bat_entries);
68
if (s->bat_size > INT_MAX / sizeof(uint32_t)) {
69
--
70
2.29.2
71
72
diff view generated by jsdifflib
1
qcow2_detect_metadata_preallocation() calls qcow2_get_refcount() which
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
requires s->lock to be taken to protect its accesses to the refcount
3
table and refcount blocks. However, nothing in this code path actually
4
took the lock. This could cause the same cache entry to be used by two
5
requests at the same time, for different tables at different offsets,
6
resulting in image corruption.
7
2
8
As it would be preferable to base the detection on consistent data (even
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
though it's just heuristics), let's take the lock not only around the
4
Message-Id: <20210224104707.88430-5-vsementsov@virtuozzo.com>
10
qcow2_get_refcount() calls, but around the whole function.
5
Reviewed-by: Denis V. Lunev <den@openvz.org>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
8
block/parallels.h | 6 +-
9
block/parallels-ext.c | 300 ++++++++++++++++++++++++++++++++++++++++++
10
block/parallels.c | 18 +++
11
block/meson.build | 3 +-
12
4 files changed, 325 insertions(+), 2 deletions(-)
13
create mode 100644 block/parallels-ext.c
11
14
12
This patch takes the lock in qcow2_co_block_status() earlier and asserts
15
diff --git a/block/parallels.h b/block/parallels.h
13
in qcow2_detect_metadata_preallocation() that we hold the lock.
14
15
Fixes: 69f47505ee66afaa513305de0c1895a224e52c45
16
Cc: qemu-stable@nongnu.org
17
Reported-by: Michael Weiser <michael.weiser@gmx.de>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
Tested-by: Michael Weiser <michael.weiser@gmx.de>
20
Reviewed-by: Michael Weiser <michael.weiser@gmx.de>
21
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
22
Reviewed-by: Max Reitz <mreitz@redhat.com>
23
---
24
block/qcow2-refcount.c | 2 ++
25
block/qcow2.c | 3 ++-
26
2 files changed, 4 insertions(+), 1 deletion(-)
27
28
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
29
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
30
--- a/block/qcow2-refcount.c
17
--- a/block/parallels.h
31
+++ b/block/qcow2-refcount.c
18
+++ b/block/parallels.h
32
@@ -XXX,XX +XXX,XX @@ int qcow2_detect_metadata_preallocation(BlockDriverState *bs)
19
@@ -XXX,XX +XXX,XX @@ typedef struct ParallelsHeader {
33
int64_t i, end_cluster, cluster_count = 0, threshold;
20
uint64_t nb_sectors;
34
int64_t file_length, real_allocation, real_clusters;
21
uint32_t inuse;
35
22
uint32_t data_off;
36
+ qemu_co_mutex_assert_locked(&s->lock);
23
- char padding[12];
37
+
24
+ uint32_t flags;
38
file_length = bdrv_getlength(bs->file->bs);
25
+ uint64_t ext_off;
39
if (file_length < 0) {
26
} QEMU_PACKED ParallelsHeader;
40
return file_length;
27
41
diff --git a/block/qcow2.c b/block/qcow2.c
28
typedef enum ParallelsPreallocMode {
29
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVParallelsState {
30
Error *migration_blocker;
31
} BDRVParallelsState;
32
33
+int parallels_read_format_extension(BlockDriverState *bs,
34
+ int64_t ext_off, Error **errp);
35
+
36
#endif
37
diff --git a/block/parallels-ext.c b/block/parallels-ext.c
38
new file mode 100644
39
index XXXXXXX..XXXXXXX
40
--- /dev/null
41
+++ b/block/parallels-ext.c
42
@@ -XXX,XX +XXX,XX @@
43
+/*
44
+ * Support of Parallels Format Extension. It's a part of Parallels format
45
+ * driver.
46
+ *
47
+ * Copyright (c) 2021 Virtuozzo International GmbH
48
+ *
49
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
50
+ * of this software and associated documentation files (the "Software"), to deal
51
+ * in the Software without restriction, including without limitation the rights
52
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
53
+ * copies of the Software, and to permit persons to whom the Software is
54
+ * furnished to do so, subject to the following conditions:
55
+ *
56
+ * The above copyright notice and this permission notice shall be included in
57
+ * all copies or substantial portions of the Software.
58
+ *
59
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
60
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
61
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
62
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
63
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
64
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
65
+ * THE SOFTWARE.
66
+ */
67
+
68
+#include "qemu/osdep.h"
69
+#include "qapi/error.h"
70
+#include "block/block_int.h"
71
+#include "parallels.h"
72
+#include "crypto/hash.h"
73
+#include "qemu/uuid.h"
74
+
75
+#define PARALLELS_FORMAT_EXTENSION_MAGIC 0xAB234CEF23DCEA87ULL
76
+
77
+#define PARALLELS_END_OF_FEATURES_MAGIC 0x0ULL
78
+#define PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC 0x20385FAE252CB34AULL
79
+
80
+typedef struct ParallelsFormatExtensionHeader {
81
+ uint64_t magic; /* PARALLELS_FORMAT_EXTENSION_MAGIC */
82
+ uint8_t check_sum[16];
83
+} QEMU_PACKED ParallelsFormatExtensionHeader;
84
+
85
+typedef struct ParallelsFeatureHeader {
86
+ uint64_t magic;
87
+ uint64_t flags;
88
+ uint32_t data_size;
89
+ uint32_t _unused;
90
+} QEMU_PACKED ParallelsFeatureHeader;
91
+
92
+typedef struct ParallelsDirtyBitmapFeature {
93
+ uint64_t size;
94
+ uint8_t id[16];
95
+ uint32_t granularity;
96
+ uint32_t l1_size;
97
+ /* L1 table follows */
98
+} QEMU_PACKED ParallelsDirtyBitmapFeature;
99
+
100
+/* Given L1 table read bitmap data from the image and populate @bitmap */
101
+static int parallels_load_bitmap_data(BlockDriverState *bs,
102
+ const uint64_t *l1_table,
103
+ uint32_t l1_size,
104
+ BdrvDirtyBitmap *bitmap,
105
+ Error **errp)
106
+{
107
+ BDRVParallelsState *s = bs->opaque;
108
+ int ret = 0;
109
+ uint64_t offset, limit;
110
+ uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
111
+ uint8_t *buf = NULL;
112
+ uint64_t i, tab_size =
113
+ DIV_ROUND_UP(bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size),
114
+ s->cluster_size);
115
+
116
+ if (tab_size != l1_size) {
117
+ error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond "
118
+ "to bitmap size and cluster size. Expected %" PRIu64,
119
+ l1_size, tab_size);
120
+ return -EINVAL;
121
+ }
122
+
123
+ buf = qemu_blockalign(bs, s->cluster_size);
124
+ limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
125
+ for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) {
126
+ uint64_t count = MIN(bm_size - offset, limit);
127
+ uint64_t entry = l1_table[i];
128
+
129
+ if (entry == 0) {
130
+ /* No need to deserialize zeros because @bitmap is cleared. */
131
+ continue;
132
+ }
133
+
134
+ if (entry == 1) {
135
+ bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, false);
136
+ } else {
137
+ ret = bdrv_pread(bs->file, entry << BDRV_SECTOR_BITS, buf,
138
+ s->cluster_size);
139
+ if (ret < 0) {
140
+ error_setg_errno(errp, -ret,
141
+ "Failed to read bitmap data cluster");
142
+ goto finish;
143
+ }
144
+ bdrv_dirty_bitmap_deserialize_part(bitmap, buf, offset, count,
145
+ false);
146
+ }
147
+ }
148
+ ret = 0;
149
+
150
+ bdrv_dirty_bitmap_deserialize_finish(bitmap);
151
+
152
+finish:
153
+ qemu_vfree(buf);
154
+
155
+ return ret;
156
+}
157
+
158
+/*
159
+ * @data buffer (of @data_size size) is the Dirty bitmaps feature which
160
+ * consists of ParallelsDirtyBitmapFeature followed by L1 table.
161
+ */
162
+static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs,
163
+ uint8_t *data,
164
+ size_t data_size,
165
+ Error **errp)
166
+{
167
+ int ret;
168
+ ParallelsDirtyBitmapFeature bf;
169
+ g_autofree uint64_t *l1_table = NULL;
170
+ BdrvDirtyBitmap *bitmap;
171
+ QemuUUID uuid;
172
+ char uuidstr[UUID_FMT_LEN + 1];
173
+ int i;
174
+
175
+ if (data_size < sizeof(bf)) {
176
+ error_setg(errp, "Too small Bitmap Feature area in Parallels Format "
177
+ "Extension: %zu bytes, expected at least %zu bytes",
178
+ data_size, sizeof(bf));
179
+ return NULL;
180
+ }
181
+ memcpy(&bf, data, sizeof(bf));
182
+ bf.size = le64_to_cpu(bf.size);
183
+ bf.granularity = le32_to_cpu(bf.granularity) << BDRV_SECTOR_BITS;
184
+ bf.l1_size = le32_to_cpu(bf.l1_size);
185
+ data += sizeof(bf);
186
+ data_size -= sizeof(bf);
187
+
188
+ if (bf.size != bs->total_sectors) {
189
+ error_setg(errp, "Bitmap size (in sectors) %" PRId64 " differs from "
190
+ "disk size in sectors %" PRId64, bf.size, bs->total_sectors);
191
+ return NULL;
192
+ }
193
+
194
+ if (bf.l1_size * sizeof(uint64_t) > data_size) {
195
+ error_setg(errp, "Bitmaps feature corrupted: l1 table exceeds "
196
+ "extension data_size");
197
+ return NULL;
198
+ }
199
+
200
+ memcpy(&uuid, bf.id, sizeof(uuid));
201
+ qemu_uuid_unparse(&uuid, uuidstr);
202
+ bitmap = bdrv_create_dirty_bitmap(bs, bf.granularity, uuidstr, errp);
203
+ if (!bitmap) {
204
+ return NULL;
205
+ }
206
+
207
+ l1_table = g_new(uint64_t, bf.l1_size);
208
+ for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) {
209
+ l1_table[i] = ldq_le_p(data);
210
+ }
211
+
212
+ ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, errp);
213
+ if (ret < 0) {
214
+ bdrv_release_dirty_bitmap(bitmap);
215
+ return NULL;
216
+ }
217
+
218
+ /* We support format extension only for RO parallels images. */
219
+ assert(!(bs->open_flags & BDRV_O_RDWR));
220
+ bdrv_dirty_bitmap_set_readonly(bitmap, true);
221
+
222
+ return bitmap;
223
+}
224
+
225
+static int parallels_parse_format_extension(BlockDriverState *bs,
226
+ uint8_t *ext_cluster, Error **errp)
227
+{
228
+ BDRVParallelsState *s = bs->opaque;
229
+ int ret;
230
+ int remaining = s->cluster_size;
231
+ uint8_t *pos = ext_cluster;
232
+ ParallelsFormatExtensionHeader eh;
233
+ g_autofree uint8_t *hash = NULL;
234
+ size_t hash_len = 0;
235
+ GSList *bitmaps = NULL, *el;
236
+
237
+ memcpy(&eh, pos, sizeof(eh));
238
+ eh.magic = le64_to_cpu(eh.magic);
239
+ pos += sizeof(eh);
240
+ remaining -= sizeof(eh);
241
+
242
+ if (eh.magic != PARALLELS_FORMAT_EXTENSION_MAGIC) {
243
+ error_setg(errp, "Wrong parallels Format Extension magic: 0x%" PRIx64
244
+ ", expected: 0x%llx", eh.magic,
245
+ PARALLELS_FORMAT_EXTENSION_MAGIC);
246
+ goto fail;
247
+ }
248
+
249
+ ret = qcrypto_hash_bytes(QCRYPTO_HASH_ALG_MD5, (char *)pos, remaining,
250
+ &hash, &hash_len, errp);
251
+ if (ret < 0) {
252
+ goto fail;
253
+ }
254
+
255
+ if (hash_len != sizeof(eh.check_sum) ||
256
+ memcmp(hash, eh.check_sum, sizeof(eh.check_sum)) != 0) {
257
+ error_setg(errp, "Wrong checksum in Format Extension header. Format "
258
+ "extension is corrupted.");
259
+ goto fail;
260
+ }
261
+
262
+ while (true) {
263
+ ParallelsFeatureHeader fh;
264
+ BdrvDirtyBitmap *bitmap;
265
+
266
+ if (remaining < sizeof(fh)) {
267
+ error_setg(errp, "Can not read feature header, as remaining bytes "
268
+ "(%d) in Format Extension is less than Feature header "
269
+ "size (%zu)", remaining, sizeof(fh));
270
+ goto fail;
271
+ }
272
+
273
+ memcpy(&fh, pos, sizeof(fh));
274
+ pos += sizeof(fh);
275
+ remaining -= sizeof(fh);
276
+
277
+ fh.magic = le64_to_cpu(fh.magic);
278
+ fh.flags = le64_to_cpu(fh.flags);
279
+ fh.data_size = le32_to_cpu(fh.data_size);
280
+
281
+ if (fh.flags) {
282
+ error_setg(errp, "Flags for extension feature are unsupported");
283
+ goto fail;
284
+ }
285
+
286
+ if (fh.data_size > remaining) {
287
+ error_setg(errp, "Feature data_size exceedes Format Extension "
288
+ "cluster");
289
+ goto fail;
290
+ }
291
+
292
+ switch (fh.magic) {
293
+ case PARALLELS_END_OF_FEATURES_MAGIC:
294
+ return 0;
295
+
296
+ case PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC:
297
+ bitmap = parallels_load_bitmap(bs, pos, fh.data_size, errp);
298
+ if (!bitmap) {
299
+ goto fail;
300
+ }
301
+ bitmaps = g_slist_append(bitmaps, bitmap);
302
+ break;
303
+
304
+ default:
305
+ error_setg(errp, "Unknown feature: 0x%" PRIu64, fh.magic);
306
+ goto fail;
307
+ }
308
+
309
+ pos = ext_cluster + QEMU_ALIGN_UP(pos + fh.data_size - ext_cluster, 8);
310
+ }
311
+
312
+fail:
313
+ for (el = bitmaps; el; el = el->next) {
314
+ bdrv_release_dirty_bitmap(el->data);
315
+ }
316
+ g_slist_free(bitmaps);
317
+
318
+ return -EINVAL;
319
+}
320
+
321
+int parallels_read_format_extension(BlockDriverState *bs,
322
+ int64_t ext_off, Error **errp)
323
+{
324
+ BDRVParallelsState *s = bs->opaque;
325
+ int ret;
326
+ uint8_t *ext_cluster = qemu_blockalign(bs, s->cluster_size);
327
+
328
+ assert(ext_off > 0);
329
+
330
+ ret = bdrv_pread(bs->file, ext_off, ext_cluster, s->cluster_size);
331
+ if (ret < 0) {
332
+ error_setg_errno(errp, -ret, "Failed to read Format Extension cluster");
333
+ goto out;
334
+ }
335
+
336
+ ret = parallels_parse_format_extension(bs, ext_cluster, errp);
337
+
338
+out:
339
+ qemu_vfree(ext_cluster);
340
+
341
+ return ret;
342
+}
343
diff --git a/block/parallels.c b/block/parallels.c
42
index XXXXXXX..XXXXXXX 100644
344
index XXXXXXX..XXXXXXX 100644
43
--- a/block/qcow2.c
345
--- a/block/parallels.c
44
+++ b/block/qcow2.c
346
+++ b/block/parallels.c
45
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
347
@@ -XXX,XX +XXX,XX @@
46
unsigned int bytes;
348
*/
47
int status = 0;
349
48
350
#include "qemu/osdep.h"
49
+ qemu_co_mutex_lock(&s->lock);
351
+#include "qemu/error-report.h"
50
+
352
#include "qapi/error.h"
51
if (!s->metadata_preallocation_checked) {
353
#include "block/block_int.h"
52
ret = qcow2_detect_metadata_preallocation(bs);
354
#include "block/qdict.h"
53
s->metadata_preallocation = (ret == 1);
355
@@ -XXX,XX +XXX,XX @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
54
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
356
goto fail_options;
55
}
357
}
56
358
57
bytes = MIN(INT_MAX, count);
359
+ if (ph.ext_off) {
58
- qemu_co_mutex_lock(&s->lock);
360
+ if (flags & BDRV_O_RDWR) {
59
ret = qcow2_get_cluster_offset(bs, offset, &bytes, &cluster_offset);
361
+ /*
60
qemu_co_mutex_unlock(&s->lock);
362
+ * It's unsafe to open image RW if there is an extension (as we
61
if (ret < 0) {
363
+ * don't support it). But parallels driver in QEMU historically
364
+ * ignores the extension, so print warning and don't care.
365
+ */
366
+ warn_report("Format Extension ignored in RW mode");
367
+ } else {
368
+ ret = parallels_read_format_extension(
369
+ bs, le64_to_cpu(ph.ext_off) << BDRV_SECTOR_BITS, errp);
370
+ if (ret < 0) {
371
+ goto fail;
372
+ }
373
+ }
374
+ }
375
+
376
if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_INACTIVE)) {
377
s->header->inuse = cpu_to_le32(HEADER_INUSE_MAGIC);
378
ret = parallels_update_header(bs);
379
diff --git a/block/meson.build b/block/meson.build
380
index XXXXXXX..XXXXXXX 100644
381
--- a/block/meson.build
382
+++ b/block/meson.build
383
@@ -XXX,XX +XXX,XX @@ block_ss.add(when: 'CONFIG_QED', if_true: files(
384
'qed-table.c',
385
'qed.c',
386
))
387
-block_ss.add(when: [libxml2, 'CONFIG_PARALLELS'], if_true: files('parallels.c'))
388
+block_ss.add(when: [libxml2, 'CONFIG_PARALLELS'],
389
+ if_true: files('parallels.c', 'parallels-ext.c'))
390
block_ss.add(when: 'CONFIG_WIN32', if_true: files('file-win32.c', 'win32-aio.c'))
391
block_ss.add(when: 'CONFIG_POSIX', if_true: [files('file-posix.c'), coref, iokit])
392
block_ss.add(when: libiscsi, if_true: files('iscsi-opts.c'))
62
--
393
--
63
2.20.1
394
2.29.2
64
395
65
396
diff view generated by jsdifflib
1
Some tests in 118 use chmod to remove write permissions from the file
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
and assume that the image can indeed not be opened read-write
3
afterwards. This doesn't work when the test is run as root, because root
4
can still open the file as writable even when the permission bit isn't
5
set.
6
2
7
Introduce a @skip_if_root decorator and use it in 118 to skip the tests
3
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
in question when the script is run as root.
4
Message-Id: <20210224104707.88430-6-vsementsov@virtuozzo.com>
5
Reviewed-by: Denis V. Lunev <den@openvz.org>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
8
tests/qemu-iotests/iotests.py | 10 ++++++++++
9
1 file changed, 10 insertions(+)
9
10
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
12
---
13
tests/qemu-iotests/118 | 3 +++
14
tests/qemu-iotests/iotests.py | 10 ++++++++++
15
2 files changed, 13 insertions(+)
16
17
diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118
18
index XXXXXXX..XXXXXXX 100755
19
--- a/tests/qemu-iotests/118
20
+++ b/tests/qemu-iotests/118
21
@@ -XXX,XX +XXX,XX @@ class TestChangeReadOnly(ChangeBaseClass):
22
self.assert_qmp(result, 'return[0]/inserted/ro', True)
23
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
24
25
+ @iotests.skip_if_user_is_root
26
def test_rw_ro_retain(self):
27
os.chmod(new_img, 0o444)
28
self.vm.add_drive(old_img, 'media=disk', 'none')
29
@@ -XXX,XX +XXX,XX @@ class TestChangeReadOnly(ChangeBaseClass):
30
self.assert_qmp(result, 'return[0]/inserted/ro', True)
31
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
32
33
+ @iotests.skip_if_user_is_root
34
def test_make_ro_rw(self):
35
os.chmod(new_img, 0o444)
36
self.vm.add_drive(old_img, 'media=disk', 'none')
37
@@ -XXX,XX +XXX,XX @@ class TestChangeReadOnly(ChangeBaseClass):
38
self.assert_qmp(result, 'return[0]/inserted/ro', True)
39
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
40
41
+ @iotests.skip_if_user_is_root
42
def test_make_ro_rw_by_retain(self):
43
os.chmod(new_img, 0o444)
44
self.vm.add_drive(old_img, 'media=disk', 'none')
45
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
11
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
46
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
47
--- a/tests/qemu-iotests/iotests.py
13
--- a/tests/qemu-iotests/iotests.py
48
+++ b/tests/qemu-iotests/iotests.py
14
+++ b/tests/qemu-iotests/iotests.py
49
@@ -XXX,XX +XXX,XX @@ def skip_if_unsupported(required_formats=[], read_only=False):
15
@@ -XXX,XX +XXX,XX @@
50
return func_wrapper
16
#
51
return skip_test_decorator
17
52
18
import atexit
53
+def skip_if_user_is_root(func):
19
+import bz2
54
+ '''Skip Test Decorator
20
from collections import OrderedDict
55
+ Runs the test only without root permissions'''
21
import faulthandler
56
+ def func_wrapper(*args, **kwargs):
22
import io
57
+ if os.getuid() == 0:
23
@@ -XXX,XX +XXX,XX @@
58
+ case_notrun('{}: cannot be run as root'.format(args[0]))
24
import logging
59
+ else:
25
import os
60
+ return func(*args, **kwargs)
26
import re
61
+ return func_wrapper
27
+import shutil
28
import signal
29
import struct
30
import subprocess
31
@@ -XXX,XX +XXX,XX @@
32
os.environ.get('IMGKEYSECRET', '')
33
luks_default_key_secret_opt = 'key-secret=keysec0'
34
35
+sample_img_dir = os.environ['SAMPLE_IMG_DIR']
62
+
36
+
63
def execute_unittest(output, verbosity, debug):
37
+
64
runner = unittest.TextTestRunner(stream=output, descriptions=True,
38
+def unarchive_sample_image(sample, fname):
65
verbosity=verbosity)
39
+ sample_fname = os.path.join(sample_img_dir, sample + '.bz2')
40
+ with bz2.open(sample_fname) as f_in, open(fname, 'wb') as f_out:
41
+ shutil.copyfileobj(f_in, f_out)
42
+
43
44
def qemu_tool_pipe_and_status(tool: str, args: Sequence[str],
45
connect_stderr: bool = True) -> Tuple[str, int]:
66
--
46
--
67
2.20.1
47
2.29.2
68
48
69
49
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
Test support for reading bitmap from parallels image format.
4
parallels-with-bitmap.bz2 is generated on Virtuozzo by
5
parallels-with-bitmap.sh
6
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Message-Id: <20210224104707.88430-7-vsementsov@virtuozzo.com>
9
Reviewed-by: Denis V. Lunev <den@openvz.org>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
.../sample_images/parallels-with-bitmap.bz2 | Bin 0 -> 203 bytes
13
.../sample_images/parallels-with-bitmap.sh | 51 ++++++++++++++++
14
.../qemu-iotests/tests/parallels-read-bitmap | 55 ++++++++++++++++++
15
.../tests/parallels-read-bitmap.out | 6 ++
16
4 files changed, 112 insertions(+)
17
create mode 100644 tests/qemu-iotests/sample_images/parallels-with-bitmap.bz2
18
create mode 100755 tests/qemu-iotests/sample_images/parallels-with-bitmap.sh
19
create mode 100755 tests/qemu-iotests/tests/parallels-read-bitmap
20
create mode 100644 tests/qemu-iotests/tests/parallels-read-bitmap.out
21
22
diff --git a/tests/qemu-iotests/sample_images/parallels-with-bitmap.bz2 b/tests/qemu-iotests/sample_images/parallels-with-bitmap.bz2
23
new file mode 100644
24
index XXXXXXX..XXXXXXX
25
GIT binary patch
26
literal 203
27
zcmV;+05tzXT4*^jL0KkKS@=;0bpT+Hf7|^?Km<xfFyKQJ7=Y^F-%vt;00~Ysa6|-=
28
zk&7Szk`SoS002EkfMftPG<ipnsiCK}K_sNmm}me3FiZr%Oaf_u5F8kD;mB_~cxD-r
29
z5P$(X{&Tq5C`<xK02D?NNdN+t$~z$m00O|zFh^ynq*yaCtkn+NZzWom<#OEoF`?zb
30
zv(i3x^K~wt!aLPcRBP+PckUsIh6*LgjYSh0`}#7hMC9NR5D)+W0d&8Mxgwk>NPH-R
31
Fx`3oHQ9u9y
32
33
literal 0
34
HcmV?d00001
35
36
diff --git a/tests/qemu-iotests/sample_images/parallels-with-bitmap.sh b/tests/qemu-iotests/sample_images/parallels-with-bitmap.sh
37
new file mode 100755
38
index XXXXXXX..XXXXXXX
39
--- /dev/null
40
+++ b/tests/qemu-iotests/sample_images/parallels-with-bitmap.sh
41
@@ -XXX,XX +XXX,XX @@
42
+#!/bin/bash
43
+#
44
+# Test parallels load bitmap
45
+#
46
+# Copyright (c) 2021 Virtuozzo International GmbH.
47
+#
48
+# This program is free software; you can redistribute it and/or modify
49
+# it under the terms of the GNU General Public License as published by
50
+# the Free Software Foundation; either version 2 of the License, or
51
+# (at your option) any later version.
52
+#
53
+# This program is distributed in the hope that it will be useful,
54
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
55
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
56
+# GNU General Public License for more details.
57
+#
58
+# You should have received a copy of the GNU General Public License
59
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
60
+#
61
+
62
+CT=parallels-with-bitmap-ct
63
+DIR=$PWD/parallels-with-bitmap-dir
64
+IMG=$DIR/root.hds
65
+XML=$DIR/DiskDescriptor.xml
66
+TARGET=parallels-with-bitmap.bz2
67
+
68
+rm -rf $DIR
69
+
70
+prlctl create $CT --vmtype ct
71
+prlctl set $CT --device-add hdd --image $DIR --recreate --size 2G
72
+
73
+# cleanup the image
74
+qemu-img create -f parallels $IMG 64G
75
+
76
+# create bitmap
77
+prlctl backup $CT
78
+
79
+prlctl set $CT --device-del hdd1
80
+prlctl destroy $CT
81
+
82
+dev=$(ploop mount $XML | sed -n 's/^Adding delta dev=\(\/dev\/ploop[0-9]\+\).*/\1/p')
83
+dd if=/dev/zero of=$dev bs=64K seek=5 count=2 oflag=direct
84
+dd if=/dev/zero of=$dev bs=64K seek=30 count=1 oflag=direct
85
+dd if=/dev/zero of=$dev bs=64K seek=10 count=3 oflag=direct
86
+ploop umount $XML # bitmap name will be in the output
87
+
88
+bzip2 -z $IMG
89
+
90
+mv $IMG.bz2 $TARGET
91
+
92
+rm -rf $DIR
93
diff --git a/tests/qemu-iotests/tests/parallels-read-bitmap b/tests/qemu-iotests/tests/parallels-read-bitmap
94
new file mode 100755
95
index XXXXXXX..XXXXXXX
96
--- /dev/null
97
+++ b/tests/qemu-iotests/tests/parallels-read-bitmap
98
@@ -XXX,XX +XXX,XX @@
99
+#!/usr/bin/env python3
100
+#
101
+# Test parallels load bitmap
102
+#
103
+# Copyright (c) 2021 Virtuozzo International GmbH.
104
+#
105
+# This program is free software; you can redistribute it and/or modify
106
+# it under the terms of the GNU General Public License as published by
107
+# the Free Software Foundation; either version 2 of the License, or
108
+# (at your option) any later version.
109
+#
110
+# This program is distributed in the hope that it will be useful,
111
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
112
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
113
+# GNU General Public License for more details.
114
+#
115
+# You should have received a copy of the GNU General Public License
116
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
117
+#
118
+
119
+import json
120
+import iotests
121
+from iotests import qemu_nbd_popen, qemu_img_pipe, log, file_path
122
+
123
+iotests.script_initialize(supported_fmts=['parallels'])
124
+
125
+nbd_sock = file_path('nbd-sock', base_dir=iotests.sock_dir)
126
+disk = iotests.file_path('disk')
127
+bitmap = 'e4f2eed0-37fe-4539-b50b-85d2e7fd235f'
128
+nbd_opts = f'driver=nbd,server.type=unix,server.path={nbd_sock}' \
129
+ f',x-dirty-bitmap=qemu:dirty-bitmap:{bitmap}'
130
+
131
+
132
+iotests.unarchive_sample_image('parallels-with-bitmap', disk)
133
+
134
+
135
+with qemu_nbd_popen('--read-only', f'--socket={nbd_sock}',
136
+ f'--bitmap={bitmap}', '-f', iotests.imgfmt, disk):
137
+ out = qemu_img_pipe('map', '--output=json', '--image-opts', nbd_opts)
138
+ chunks = json.loads(out)
139
+ cluster = 64 * 1024
140
+
141
+ log('dirty clusters (cluster size is 64K):')
142
+ for c in chunks:
143
+ assert c['start'] % cluster == 0
144
+ assert c['length'] % cluster == 0
145
+ if c['data']:
146
+ continue
147
+
148
+ a = c['start'] // cluster
149
+ b = (c['start'] + c['length']) // cluster
150
+ if b - a > 1:
151
+ log(f'{a}-{b-1}')
152
+ else:
153
+ log(a)
154
diff --git a/tests/qemu-iotests/tests/parallels-read-bitmap.out b/tests/qemu-iotests/tests/parallels-read-bitmap.out
155
new file mode 100644
156
index XXXXXXX..XXXXXXX
157
--- /dev/null
158
+++ b/tests/qemu-iotests/tests/parallels-read-bitmap.out
159
@@ -XXX,XX +XXX,XX @@
160
+Start NBD server
161
+dirty clusters (cluster size is 64K):
162
+5-6
163
+10-12
164
+30
165
+Kill NBD server
166
--
167
2.29.2
168
169
diff view generated by jsdifflib
1
From: Pavel Dovgalyuk <pavel.dovgaluk@gmail.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
This patch adds support for blkreplay driver to the blockdev options.
3
Add new parallels-ext.c and myself as co-maintainer.
4
Now blkreplay can be used with -blockdev command line option
5
in the following format:
6
-blockdev driver=blkreplay,image=file-node-name,node-name=replay-node-name
7
4
8
This option makes possible implementation of the better command
5
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
line support for record/replay invocations.
6
Message-Id: <20210304095151.19358-1-vsementsov@virtuozzo.com>
10
7
Reviewed-by: Denis V. Lunev <den@openvz.org>
11
Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
9
---
14
qapi/block-core.json | 18 ++++++++++++++++--
10
MAINTAINERS | 3 +++
15
1 file changed, 16 insertions(+), 2 deletions(-)
11
1 file changed, 3 insertions(+)
16
12
17
diff --git a/qapi/block-core.json b/qapi/block-core.json
13
diff --git a/MAINTAINERS b/MAINTAINERS
18
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
19
--- a/qapi/block-core.json
15
--- a/MAINTAINERS
20
+++ b/qapi/block-core.json
16
+++ b/MAINTAINERS
21
@@ -XXX,XX +XXX,XX @@
17
@@ -XXX,XX +XXX,XX @@ F: block/dmg.c
22
# @nvme: Since 2.12
18
parallels
23
# @copy-on-read: Since 3.0
19
M: Stefan Hajnoczi <stefanha@redhat.com>
24
# @blklogwrites: Since 3.0
20
M: Denis V. Lunev <den@openvz.org>
25
+# @blkreplay: Since 4.2
21
+M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
26
#
22
L: qemu-block@nongnu.org
27
# Since: 2.9
23
S: Supported
28
##
24
F: block/parallels.c
29
{ 'enum': 'BlockdevDriver',
25
+F: block/parallels-ext.c
30
- 'data': [ 'blkdebug', 'blklogwrites', 'blkverify', 'bochs', 'cloop',
26
F: docs/interop/parallels.txt
31
- 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
27
+T: git https://src.openvz.org/scm/~vsementsov/qemu.git parallels
32
+ 'data': [ 'blkdebug', 'blklogwrites', 'blkreplay', 'blkverify', 'bochs',
28
33
+ 'cloop', 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
29
qed
34
'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks',
30
M: Stefan Hajnoczi <stefanha@redhat.com>
35
'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow',
36
'qcow2', 'qed', 'quorum', 'raw', 'rbd',
37
@@ -XXX,XX +XXX,XX @@
38
'data': { 'test': 'BlockdevRef',
39
'raw': 'BlockdevRef' } }
40
41
+##
42
+# @BlockdevOptionsBlkreplay:
43
+#
44
+# Driver specific block device options for blkreplay.
45
+#
46
+# @image: disk image which should be controlled with blkreplay
47
+#
48
+# Since: 4.2
49
+##
50
+{ 'struct': 'BlockdevOptionsBlkreplay',
51
+ 'data': { 'image': 'BlockdevRef' } }
52
+
53
##
54
# @QuorumReadPattern:
55
#
56
@@ -XXX,XX +XXX,XX @@
57
'blkdebug': 'BlockdevOptionsBlkdebug',
58
'blklogwrites':'BlockdevOptionsBlklogwrites',
59
'blkverify': 'BlockdevOptionsBlkverify',
60
+ 'blkreplay': 'BlockdevOptionsBlkreplay',
61
'bochs': 'BlockdevOptionsGenericFormat',
62
'cloop': 'BlockdevOptionsGenericFormat',
63
'copy-on-read':'BlockdevOptionsGenericFormat',
64
--
31
--
65
2.20.1
32
2.29.2
66
33
67
34
diff view generated by jsdifflib
1
We added more generic options after introducing -blockdev and forgot to
1
The 'name' option for NBD exports is optional. Add a note that the
2
update the documentation (man page and --help output) accordingly. Do
2
default for the option is the node name (people could otherwise expect
3
that now.
3
that it's the empty string like for qemu-nbd).
4
4
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
6
Message-Id: <20210305094856.18964-1-kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
9
---
8
qemu-options.hx | 22 +++++++++++++++++++++-
10
docs/tools/qemu-storage-daemon.rst | 5 +++--
9
1 file changed, 21 insertions(+), 1 deletion(-)
11
1 file changed, 3 insertions(+), 2 deletions(-)
10
12
11
diff --git a/qemu-options.hx b/qemu-options.hx
13
diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst
12
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
13
--- a/qemu-options.hx
15
--- a/docs/tools/qemu-storage-daemon.rst
14
+++ b/qemu-options.hx
16
+++ b/docs/tools/qemu-storage-daemon.rst
15
@@ -XXX,XX +XXX,XX @@ ETEXI
17
@@ -XXX,XX +XXX,XX @@ Standard options:
16
DEF("blockdev", HAS_ARG, QEMU_OPTION_blockdev,
18
requests for modifying data (the default is off).
17
"-blockdev [driver=]driver[,node-name=N][,discard=ignore|unmap]\n"
19
18
" [,cache.direct=on|off][,cache.no-flush=on|off]\n"
20
The ``nbd`` export type requires ``--nbd-server`` (see below). ``name`` is
19
- " [,read-only=on|off][,detect-zeroes=on|off|unmap]\n"
21
- the NBD export name. ``bitmap`` is the name of a dirty bitmap reachable from
20
+ " [,read-only=on|off][,auto-read-only=on|off]\n"
22
- the block node, so the NBD client can use NBD_OPT_SET_META_CONTEXT with the
21
+ " [,force-share=on|off][,detect-zeroes=on|off|unmap]\n"
23
+ the NBD export name (if not specified, it defaults to the given
22
" [,driver specific parameters...]\n"
24
+ ``node-name``). ``bitmap`` is the name of a dirty bitmap reachable from the
23
" configure a block backend\n", QEMU_ARCH_ALL)
25
+ block node, so the NBD client can use NBD_OPT_SET_META_CONTEXT with the
24
STEXI
26
metadata context name "qemu:dirty-bitmap:BITMAP" to inspect the bitmap.
25
@@ -XXX,XX +XXX,XX @@ name is not intended to be predictable and changes between QEMU invocations.
27
26
For the top level, an explicit node name must be specified.
28
The ``vhost-user-blk`` export type takes a vhost-user socket address on which
27
@item read-only
28
Open the node read-only. Guest write attempts will fail.
29
+
30
+Note that some block drivers support only read-only access, either generally or
31
+in certain configurations. In this case, the default value
32
+@option{read-only=off} does not work and the option must be specified
33
+explicitly.
34
+@item auto-read-only
35
+If @option{auto-read-only=on} is set, QEMU may fall back to read-only usage
36
+even when @option{read-only=off} is requested, or even switch between modes as
37
+needed, e.g. depending on whether the image file is writable or whether a
38
+writing user is attached to the node.
39
+@item force-share
40
+Override the image locking system of QEMU by forcing the node to utilize
41
+weaker shared access for permissions where it would normally request exclusive
42
+access. When there is the potential for multiple instances to have the same
43
+file open (whether this invocation of QEMU is the first or the second
44
+instance), both instances must permit shared access for the second instance to
45
+succeed at opening the file.
46
+
47
+Enabling @option{force-share=on} requires @option{read-only=on}.
48
@item cache.direct
49
The host page cache can be avoided with @option{cache.direct=on}. This will
50
attempt to do disk IO directly to the guest's memory. QEMU may still perform an
51
--
29
--
52
2.20.1
30
2.29.2
53
31
54
32
diff view generated by jsdifflib