1
The following changes since commit ba29883206d92a29ad5a466e679ccfc2ee6132ef:
1
The following changes since commit 028f2361d0c2d28d6f918fe618f389228ac22b60:
2
2
3
Merge remote-tracking branch 'remotes/borntraeger/tags/s390x-20200310' into staging (2020-03-10 16:50:28 +0000)
3
Merge tag 'pull-target-arm-20220609' of https://git.linaro.org/people/pmaydell/qemu-arm into staging (2022-06-09 06:47:03 -0700)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
8
8
9
for you to fetch changes up to 8bb3b023f2055054ee119cb45b42d2b14be7fc8a:
9
for you to fetch changes up to 7f9a8b3342ff00d3398fdc08264948762d748edb:
10
10
11
qemu-iotests: adding LUKS cleanup for non-UTF8 secret error (2020-03-11 15:54:38 +0100)
11
nbd: Drop dead code spotted by Coverity (2022-06-09 18:07:17 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches
15
15
16
- Relax restrictions for blockdev-snapshot (allows libvirt to do live
16
- Add vduse-blk export
17
storage migration with blockdev-mirror)
17
- Dirty bitmaps: Fix and improve bitmap merge
18
- luks: Delete created files when block_crypto_co_create_opts_luks fails
18
- gluster: correctly set max_pdiscard
19
- Fix memleaks in qmp_object_add
19
- rbd: report a better error when namespace does not exist
20
- aio_wait_kick: add missing memory barrier
21
- Code cleanups
20
22
21
----------------------------------------------------------------
23
----------------------------------------------------------------
22
Daniel Henrique Barboza (4):
24
Emanuele Giuseppe Esposito (1):
23
block: introducing 'bdrv_co_delete_file' interface
25
aio_wait_kick: add missing memory barrier
24
block.c: adding bdrv_co_delete_file
25
crypto.c: cleanup created file when block_crypto_co_create_opts_luks fails
26
qemu-iotests: adding LUKS cleanup for non-UTF8 secret error
27
26
28
Kevin Wolf (6):
27
Eric Blake (1):
29
block: Make bdrv_get_cumulative_perm() public
28
nbd: Drop dead code spotted by Coverity
30
block: Relax restrictions for blockdev-snapshot
31
iotests: Fix run_job() with use_log=False
32
iotests: Test mirror with temporarily disabled target backing file
33
block: Fix cross-AioContext blockdev-snapshot
34
iotests: Add iothread cases to 155
35
29
36
Pan Nengyuan (1):
30
Fabian Ebner (1):
37
qom-qmp-cmds: fix two memleaks in qmp_object_add
31
block/gluster: correctly set max_pdiscard
38
32
39
Peter Krempa (1):
33
Stefan Hajnoczi (3):
40
qapi: Add '@allow-write-only-overlay' feature for 'blockdev-snapshot'
34
block: drop unused bdrv_co_drain() API
35
block: get rid of blk->guest_block_size
36
qsd: document vduse-blk exports
41
37
42
Philippe Mathieu-Daudé (1):
38
Stefano Garzarella (1):
43
tests/qemu-iotests: Fix socket_scm_helper build path
39
block/rbd: report a better error when namespace does not exist
44
40
45
qapi/block-core.json | 9 ++++-
41
Vladimir Sementsov-Ogievskiy (3):
46
include/block/block.h | 1 +
42
block: block_dirty_bitmap_merge(): fix error path
47
include/block/block_int.h | 7 ++++
43
block: improve block_dirty_bitmap_merge(): don't allocate extra bitmap
48
block.c | 33 ++++++++++++++--
44
block: simplify handling of try to merge different sized bitmaps
49
block/crypto.c | 18 +++++++++
50
block/file-posix.c | 23 +++++++++++
51
blockdev.c | 30 ++++-----------
52
qom/qom-qmp-cmds.c | 16 +++-----
53
tests/qemu-iotests/iotests.py | 5 ++-
54
tests/Makefile.include | 1 +
55
tests/qemu-iotests/085.out | 4 +-
56
tests/qemu-iotests/155 | 88 ++++++++++++++++++++++++++++++++++++-------
57
tests/qemu-iotests/155.out | 4 +-
58
tests/qemu-iotests/282 | 67 ++++++++++++++++++++++++++++++++
59
tests/qemu-iotests/282.out | 11 ++++++
60
tests/qemu-iotests/group | 1 +
61
tests/qtest/Makefile.include | 1 -
62
17 files changed, 262 insertions(+), 57 deletions(-)
63
create mode 100755 tests/qemu-iotests/282
64
create mode 100644 tests/qemu-iotests/282.out
65
45
46
Xie Yongji (8):
47
block: Support passing NULL ops to blk_set_dev_ops()
48
block/export: Fix incorrect length passed to vu_queue_push()
49
block/export: Abstract out the logic of virtio-blk I/O process
50
linux-headers: Add vduse.h
51
libvduse: Add VDUSE (vDPA Device in Userspace) library
52
vduse-blk: Implement vduse-blk export
53
vduse-blk: Add vduse-blk resize support
54
libvduse: Add support for reconnecting
66
55
56
qapi/block-export.json | 28 +-
57
docs/tools/qemu-storage-daemon.rst | 21 +
58
meson_options.txt | 4 +
59
block/export/vduse-blk.h | 20 +
60
block/export/virtio-blk-handler.h | 37 +
61
include/block/aio-wait.h | 2 +
62
include/block/block-io.h | 1 -
63
include/block/block_int-io.h | 2 +-
64
include/qemu/hbitmap.h | 15 +-
65
include/sysemu/block-backend-io.h | 1 -
66
linux-headers/linux/vduse.h | 306 ++++++
67
subprojects/libvduse/include/atomic.h | 1 +
68
subprojects/libvduse/include/compiler.h | 1 +
69
subprojects/libvduse/libvduse.h | 247 +++++
70
block/backup.c | 6 +-
71
block/block-backend.c | 12 +-
72
block/dirty-bitmap.c | 26 +-
73
block/export/export.c | 6 +
74
block/export/vduse-blk.c | 341 +++++++
75
block/export/vhost-user-blk-server.c | 261 +----
76
block/export/virtio-blk-handler.c | 240 +++++
77
block/gluster.c | 2 +-
78
block/io.c | 15 -
79
block/monitor/bitmap-qmp-cmds.c | 40 +-
80
block/nbd.c | 8 +-
81
block/rbd.c | 24 +
82
hw/block/virtio-blk.c | 1 -
83
hw/block/xen-block.c | 1 -
84
hw/ide/core.c | 1 -
85
hw/scsi/scsi-disk.c | 1 -
86
hw/scsi/scsi-generic.c | 1 -
87
storage-daemon/qemu-storage-daemon.c | 9 +
88
subprojects/libvduse/libvduse.c | 1392 +++++++++++++++++++++++++++
89
util/aio-wait.c | 16 +-
90
util/hbitmap.c | 25 +-
91
MAINTAINERS | 9 +
92
block/export/meson.build | 7 +-
93
meson.build | 34 +
94
scripts/meson-buildoptions.sh | 7 +
95
scripts/update-linux-headers.sh | 2 +-
96
subprojects/libvduse/linux-headers/linux | 1 +
97
subprojects/libvduse/meson.build | 10 +
98
subprojects/libvduse/standard-headers/linux | 1 +
99
43 files changed, 2830 insertions(+), 355 deletions(-)
100
create mode 100644 block/export/vduse-blk.h
101
create mode 100644 block/export/virtio-blk-handler.h
102
create mode 100644 linux-headers/linux/vduse.h
103
create mode 120000 subprojects/libvduse/include/atomic.h
104
create mode 120000 subprojects/libvduse/include/compiler.h
105
create mode 100644 subprojects/libvduse/libvduse.h
106
create mode 100644 block/export/vduse-blk.c
107
create mode 100644 block/export/virtio-blk-handler.c
108
create mode 100644 subprojects/libvduse/libvduse.c
109
create mode 120000 subprojects/libvduse/linux-headers/linux
110
create mode 100644 subprojects/libvduse/meson.build
111
create mode 120000 subprojects/libvduse/standard-headers/linux
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
bdrv_co_drain() has not been used since commit 9a0cec664eef ("mirror:
4
use bdrv_drained_begin/bdrv_drained_end") in 2016. Remove it so there
5
are fewer drain scenarios to worry about.
6
7
Use bdrv_drained_begin()/bdrv_drained_end() instead. They are "mixed"
8
functions that can be called from coroutine context. Unlike
9
bdrv_co_drain(), these functions provide control of the length of the
10
drained section, which is usually the right thing.
11
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Message-Id: <20220521122714.3837731-1-stefanha@redhat.com>
14
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
15
Reviewed-by: Alberto Faria <afaria@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
18
include/block/block-io.h | 1 -
19
block/io.c | 15 ---------------
20
2 files changed, 16 deletions(-)
21
22
diff --git a/include/block/block-io.h b/include/block/block-io.h
23
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/block-io.h
25
+++ b/include/block/block-io.h
26
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter);
27
cond); })
28
29
void bdrv_drain(BlockDriverState *bs);
30
-void coroutine_fn bdrv_co_drain(BlockDriverState *bs);
31
32
int generated_co_wrapper
33
bdrv_truncate(BdrvChild *child, int64_t offset, bool exact,
34
diff --git a/block/io.c b/block/io.c
35
index XXXXXXX..XXXXXXX 100644
36
--- a/block/io.c
37
+++ b/block/io.c
38
@@ -XXX,XX +XXX,XX @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
39
BDRV_POLL_WHILE(child->bs, qatomic_read(&drained_end_counter) > 0);
40
}
41
42
-/*
43
- * Wait for pending requests to complete on a single BlockDriverState subtree,
44
- * and suspend block driver's internal I/O until next request arrives.
45
- *
46
- * Note that unlike bdrv_drain_all(), the caller must hold the BlockDriverState
47
- * AioContext.
48
- */
49
-void coroutine_fn bdrv_co_drain(BlockDriverState *bs)
50
-{
51
- IO_OR_GS_CODE();
52
- assert(qemu_in_coroutine());
53
- bdrv_drained_begin(bs);
54
- bdrv_drained_end(bs);
55
-}
56
-
57
void bdrv_drain(BlockDriverState *bs)
58
{
59
IO_OR_GS_CODE();
60
--
61
2.35.3
diff view generated by jsdifflib
New patch
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
2
3
Commit 1b7fd729559c ("block: rename buffer_alignment to
4
guest_block_size") noted:
5
6
At this point, the field is set by the device emulation, but completely
7
ignored by the block layer.
8
9
The last time the value of buffer_alignment/guest_block_size was
10
actually used was before commit 339064d50639 ("block: Don't use guest
11
sector size for qemu_blockalign()").
12
13
This value has not been used since 2013. Get rid of it.
14
15
Cc: Xie Yongji <xieyongji@bytedance.com>
16
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Message-Id: <20220518130945.2657905-1-stefanha@redhat.com>
18
Reviewed-by: Paul Durrant <paul@xen.org>
19
Reviewed-by: Eric Blake <eblake@redhat.com>
20
Reviewed-by: Alberto Faria <afaria@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
23
include/sysemu/block-backend-io.h | 1 -
24
block/block-backend.c | 10 ----------
25
block/export/vhost-user-blk-server.c | 1 -
26
hw/block/virtio-blk.c | 1 -
27
hw/block/xen-block.c | 1 -
28
hw/ide/core.c | 1 -
29
hw/scsi/scsi-disk.c | 1 -
30
hw/scsi/scsi-generic.c | 1 -
31
8 files changed, 17 deletions(-)
32
33
diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h
34
index XXXXXXX..XXXXXXX 100644
35
--- a/include/sysemu/block-backend-io.h
36
+++ b/include/sysemu/block-backend-io.h
37
@@ -XXX,XX +XXX,XX @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action,
38
void blk_iostatus_set_err(BlockBackend *blk, int error);
39
int blk_get_max_iov(BlockBackend *blk);
40
int blk_get_max_hw_iov(BlockBackend *blk);
41
-void blk_set_guest_block_size(BlockBackend *blk, int align);
42
43
void blk_io_plug(BlockBackend *blk);
44
void blk_io_unplug(BlockBackend *blk);
45
diff --git a/block/block-backend.c b/block/block-backend.c
46
index XXXXXXX..XXXXXXX 100644
47
--- a/block/block-backend.c
48
+++ b/block/block-backend.c
49
@@ -XXX,XX +XXX,XX @@ struct BlockBackend {
50
const BlockDevOps *dev_ops;
51
void *dev_opaque;
52
53
- /* the block size for which the guest device expects atomicity */
54
- int guest_block_size;
55
-
56
/* If the BDS tree is removed, some of its options are stored here (which
57
* can be used to restore those options in the new BDS on insert) */
58
BlockBackendRootState root_state;
59
@@ -XXX,XX +XXX,XX @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev)
60
blk->dev = NULL;
61
blk->dev_ops = NULL;
62
blk->dev_opaque = NULL;
63
- blk->guest_block_size = 512;
64
blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort);
65
blk_unref(blk);
66
}
67
@@ -XXX,XX +XXX,XX @@ int blk_get_max_iov(BlockBackend *blk)
68
return blk->root->bs->bl.max_iov;
69
}
70
71
-void blk_set_guest_block_size(BlockBackend *blk, int align)
72
-{
73
- IO_CODE();
74
- blk->guest_block_size = align;
75
-}
76
-
77
void *blk_try_blockalign(BlockBackend *blk, size_t size)
78
{
79
IO_CODE();
80
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
81
index XXXXXXX..XXXXXXX 100644
82
--- a/block/export/vhost-user-blk-server.c
83
+++ b/block/export/vhost-user-blk-server.c
84
@@ -XXX,XX +XXX,XX @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
85
return -EINVAL;
86
}
87
vexp->blk_size = logical_block_size;
88
- blk_set_guest_block_size(exp->blk, logical_block_size);
89
90
if (vu_opts->has_num_queues) {
91
num_queues = vu_opts->num_queues;
92
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
93
index XXXXXXX..XXXXXXX 100644
94
--- a/hw/block/virtio-blk.c
95
+++ b/hw/block/virtio-blk.c
96
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
97
98
s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
99
blk_set_dev_ops(s->blk, &virtio_block_ops, s);
100
- blk_set_guest_block_size(s->blk, s->conf.conf.logical_block_size);
101
102
blk_iostatus_enable(s->blk);
103
104
diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c
105
index XXXXXXX..XXXXXXX 100644
106
--- a/hw/block/xen-block.c
107
+++ b/hw/block/xen-block.c
108
@@ -XXX,XX +XXX,XX @@ static void xen_block_realize(XenDevice *xendev, Error **errp)
109
}
110
111
blk_set_dev_ops(blk, &xen_block_dev_ops, blockdev);
112
- blk_set_guest_block_size(blk, conf->logical_block_size);
113
114
if (conf->discard_granularity == -1) {
115
conf->discard_granularity = conf->physical_block_size;
116
diff --git a/hw/ide/core.c b/hw/ide/core.c
117
index XXXXXXX..XXXXXXX 100644
118
--- a/hw/ide/core.c
119
+++ b/hw/ide/core.c
120
@@ -XXX,XX +XXX,XX @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind,
121
s->smart_selftest_count = 0;
122
if (kind == IDE_CD) {
123
blk_set_dev_ops(blk, &ide_cd_block_ops, s);
124
- blk_set_guest_block_size(blk, 2048);
125
} else {
126
if (!blk_is_inserted(s->blk)) {
127
error_setg(errp, "Device needs media, but drive is empty");
128
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
129
index XXXXXXX..XXXXXXX 100644
130
--- a/hw/scsi/scsi-disk.c
131
+++ b/hw/scsi/scsi-disk.c
132
@@ -XXX,XX +XXX,XX @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
133
} else {
134
blk_set_dev_ops(s->qdev.conf.blk, &scsi_disk_block_ops, s);
135
}
136
- blk_set_guest_block_size(s->qdev.conf.blk, s->qdev.blocksize);
137
138
blk_iostatus_enable(s->qdev.conf.blk);
139
140
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
141
index XXXXXXX..XXXXXXX 100644
142
--- a/hw/scsi/scsi-generic.c
143
+++ b/hw/scsi/scsi-generic.c
144
@@ -XXX,XX +XXX,XX @@ static void scsi_read_complete(void * opaque, int ret)
145
s->blocksize = ldl_be_p(&r->buf[8]);
146
s->max_lba = ldq_be_p(&r->buf[0]);
147
}
148
- blk_set_guest_block_size(s->conf.blk, s->blocksize);
149
150
/*
151
* Patch MODE SENSE device specific parameters if the BDS is opened
152
--
153
2.35.3
diff view generated by jsdifflib
1
The 'job-complete' QMP command should be run with qmp() rather than
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@openvz.org>
2
qmp_log() if use_log=False is passed.
3
2
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
At the end we ignore failure of bdrv_merge_dirty_bitmap() and report
5
Message-Id: <20200310113831.27293-4-kwolf@redhat.com>
4
success. And still set errp. That's wrong.
6
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
7
Reviewed-by: Nikita Lapshin <nikita.lapshin@virtuozzo.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Message-Id: <20220517111206.23585-2-v.sementsov-og@mail.ru>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
11
---
9
tests/qemu-iotests/iotests.py | 5 ++++-
12
block/monitor/bitmap-qmp-cmds.c | 5 ++++-
10
1 file changed, 4 insertions(+), 1 deletion(-)
13
1 file changed, 4 insertions(+), 1 deletion(-)
11
14
12
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
15
diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c
13
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
14
--- a/tests/qemu-iotests/iotests.py
17
--- a/block/monitor/bitmap-qmp-cmds.c
15
+++ b/tests/qemu-iotests/iotests.py
18
+++ b/block/monitor/bitmap-qmp-cmds.c
16
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
19
@@ -XXX,XX +XXX,XX @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target,
17
if use_log:
20
}
18
log('Job failed: %s' % (j['error']))
21
19
elif status == 'ready':
22
/* Merge into dst; dst is unchanged on failure. */
20
- self.qmp_log('job-complete', id=job)
23
- bdrv_merge_dirty_bitmap(dst, anon, backup, errp);
21
+ if use_log:
24
+ if (!bdrv_merge_dirty_bitmap(dst, anon, backup, errp)) {
22
+ self.qmp_log('job-complete', id=job)
25
+ dst = NULL;
23
+ else:
26
+ goto out;
24
+ self.qmp('job-complete', id=job)
27
+ }
25
elif status == 'pending' and not auto_finalize:
28
26
if pre_finalize:
29
out:
27
pre_finalize()
30
bdrv_release_dirty_bitmap(anon);
28
--
31
--
29
2.20.1
32
2.35.3
30
31
diff view generated by jsdifflib
1
external_snapshot_prepare() tries to move the overlay to the AioContext
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@openvz.org>
2
of the backing file (the snapshotted node). However, it's possible that
3
this doesn't work, but the backing file can instead be moved to the
4
overlay's AioContext (e.g. opening the backing chain for a mirror
5
target).
6
2
7
bdrv_append() already indirectly uses bdrv_attach_node(), which takes
3
We don't need extra bitmap. All we need is to backup the original
8
care to move nodes to make sure they use the same AioContext and which
4
bitmap when we do first merge. So, drop extra temporary bitmap and work
9
tries both directions.
5
directly with target and backup.
10
6
11
So the problem has a simple fix: Just delete the unnecessary extra
7
Still to keep old semantics, that on failure target is unchanged and
12
bdrv_try_set_aio_context() call in external_snapshot_prepare() and
8
user don't need to restore, we need a local_backup variable and do
13
instead assert in bdrv_append() that both nodes were indeed moved to the
9
restore ourselves on failure path.
14
same AioContext.
15
10
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
17
Message-Id: <20200310113831.27293-6-kwolf@redhat.com>
12
Message-Id: <20220517111206.23585-3-v.sementsov-og@mail.ru>
18
Tested-by: Peter Krempa <pkrempa@redhat.com>
13
Reviewed-by: Eric Blake <eblake@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
---
15
---
21
block.c | 1 +
16
block/monitor/bitmap-qmp-cmds.c | 41 +++++++++++++++++----------------
22
blockdev.c | 16 ----------------
17
1 file changed, 21 insertions(+), 20 deletions(-)
23
2 files changed, 1 insertion(+), 16 deletions(-)
24
18
25
diff --git a/block.c b/block.c
19
diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c
26
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
27
--- a/block.c
21
--- a/block/monitor/bitmap-qmp-cmds.c
28
+++ b/block.c
22
+++ b/block/monitor/bitmap-qmp-cmds.c
29
@@ -XXX,XX +XXX,XX @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
23
@@ -XXX,XX +XXX,XX @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target,
30
bdrv_ref(from);
24
HBitmap **backup, Error **errp)
31
25
{
32
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
26
BlockDriverState *bs;
33
+ assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
27
- BdrvDirtyBitmap *dst, *src, *anon;
34
bdrv_drained_begin(from);
28
+ BdrvDirtyBitmap *dst, *src;
35
29
BlockDirtyBitmapOrStrList *lst;
36
/* Put all parents into @list and calculate their cumulative permissions */
30
+ HBitmap *local_backup = NULL;
37
diff --git a/blockdev.c b/blockdev.c
31
38
index XXXXXXX..XXXXXXX 100644
32
GLOBAL_STATE_CODE();
39
--- a/blockdev.c
33
40
+++ b/blockdev.c
34
@@ -XXX,XX +XXX,XX @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target,
41
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
35
return NULL;
42
DO_UPCAST(ExternalSnapshotState, common, common);
43
TransactionAction *action = common->action;
44
AioContext *aio_context;
45
- AioContext *old_context;
46
uint64_t perm, shared;
47
- int ret;
48
49
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
50
* purpose but a different set of parameters */
51
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
52
goto out;
53
}
36
}
54
37
55
- /* Honor bdrv_try_set_aio_context() context acquisition requirements. */
38
- anon = bdrv_create_dirty_bitmap(bs, bdrv_dirty_bitmap_granularity(dst),
56
- old_context = bdrv_get_aio_context(state->new_bs);
39
- NULL, errp);
57
- aio_context_release(aio_context);
40
- if (!anon) {
58
- aio_context_acquire(old_context);
41
- return NULL;
59
-
60
- ret = bdrv_try_set_aio_context(state->new_bs, aio_context, errp);
61
-
62
- aio_context_release(old_context);
63
- aio_context_acquire(aio_context);
64
-
65
- if (ret < 0) {
66
- goto out;
67
- }
42
- }
68
-
43
-
69
/* This removes our old bs and adds the new bs. This is an operation that
44
for (lst = bms; lst; lst = lst->next) {
70
* can fail, so we need to do it in .prepare; undoing it for abort is
45
switch (lst->value->type) {
71
* always possible. */
46
const char *name, *node;
47
@@ -XXX,XX +XXX,XX @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target,
48
src = bdrv_find_dirty_bitmap(bs, name);
49
if (!src) {
50
error_setg(errp, "Dirty bitmap '%s' not found", name);
51
- dst = NULL;
52
- goto out;
53
+ goto fail;
54
}
55
break;
56
case QTYPE_QDICT:
57
@@ -XXX,XX +XXX,XX @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target,
58
name = lst->value->u.external.name;
59
src = block_dirty_bitmap_lookup(node, name, NULL, errp);
60
if (!src) {
61
- dst = NULL;
62
- goto out;
63
+ goto fail;
64
}
65
break;
66
default:
67
abort();
68
}
69
70
- if (!bdrv_merge_dirty_bitmap(anon, src, NULL, errp)) {
71
- dst = NULL;
72
- goto out;
73
+ /* We do backup only for first merge operation */
74
+ if (!bdrv_merge_dirty_bitmap(dst, src,
75
+ local_backup ? NULL : &local_backup,
76
+ errp))
77
+ {
78
+ goto fail;
79
}
80
}
81
82
- /* Merge into dst; dst is unchanged on failure. */
83
- if (!bdrv_merge_dirty_bitmap(dst, anon, backup, errp)) {
84
- dst = NULL;
85
- goto out;
86
+ if (backup) {
87
+ *backup = local_backup;
88
+ } else {
89
+ hbitmap_free(local_backup);
90
}
91
92
- out:
93
- bdrv_release_dirty_bitmap(anon);
94
return dst;
95
+
96
+fail:
97
+ if (local_backup) {
98
+ bdrv_restore_dirty_bitmap(dst, local_backup);
99
+ }
100
+
101
+ return NULL;
102
}
103
104
void qmp_block_dirty_bitmap_merge(const char *node, const char *target,
72
--
105
--
73
2.20.1
106
2.35.3
74
75
diff view generated by jsdifflib
1
blockdev-snapshot returned an error if the overlay was already in use,
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@openvz.org>
2
which it defined as having any BlockBackend parent. This is in fact both
2
3
too strict (some parents can tolerate the change of visible data caused
3
We have too much logic to simply check that bitmaps are of the same
4
by attaching a backing file) and too loose (some non-BlockBackend
4
size. Let's just define that hbitmap_merge() and
5
parents may not be happy with it).
5
bdrv_dirty_bitmap_merge_internal() require their argument bitmaps be of
6
6
same size, this simplifies things.
7
One important use case that is prevented by the too strict check is live
7
8
storage migration with blockdev-mirror. Here, the target node is
8
Let's look through the callers:
9
usually opened without a backing file so that the active layer is
9
10
mirrored while its backing chain can be copied in the background.
10
For backup_init_bcs_bitmap() we already assert that merge can't fail.
11
11
12
The backing chain should be attached to the mirror target node when
12
In bdrv_reclaim_dirty_bitmap_locked() we gracefully handle the error
13
finalising the job, just before switching the users of the source node
13
that can't happen: successor always has same size as its parent, drop
14
to the new copy (at which point the mirror job still has a reference to
14
this logic.
15
the node). drive-mirror did this automatically, but with blockdev-mirror
15
16
this is the job of the QMP client, so it needs a way to do this.
16
In bdrv_merge_dirty_bitmap() we already has assertion and separate
17
17
check. Make the check explicit and improve error message.
18
blockdev-snapshot is the obvious way, so this patch makes it work in
18
19
this scenario. The new condition is that no parent uses CONSISTENT_READ
19
Signed-off-by: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
20
permissions. This will ensure that the operation will still be blocked
20
Reviewed-by: Nikita Lapshin <nikita.lapshin@virtuozzo.com>
21
when the node is attached to the guest device, so blockdev-snapshot
21
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
22
remains safe.
22
Message-Id: <20220517111206.23585-4-v.sementsov-og@mail.ru>
23
24
(For the sake of completeness, x-blockdev-reopen can be used to achieve
25
the same, however it is a big hammer, performs the graph change
26
completely unchecked and is still experimental. So even with the option
27
of using x-blockdev-reopen, there are reasons why blockdev-snapshot
28
should be able to perform this operation.)
29
30
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
31
Message-Id: <20200310113831.27293-3-kwolf@redhat.com>
32
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
33
Tested-by: Peter Krempa <pkrempa@redhat.com>
34
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
35
---
24
---
36
blockdev.c | 14 ++++++++------
25
include/block/block_int-io.h | 2 +-
37
tests/qemu-iotests/085.out | 4 ++--
26
include/qemu/hbitmap.h | 15 ++-------------
38
2 files changed, 10 insertions(+), 8 deletions(-)
27
block/backup.c | 6 ++----
39
28
block/dirty-bitmap.c | 26 +++++++++++---------------
40
diff --git a/blockdev.c b/blockdev.c
29
util/hbitmap.c | 25 +++++++------------------
41
index XXXXXXX..XXXXXXX 100644
30
5 files changed, 23 insertions(+), 51 deletions(-)
42
--- a/blockdev.c
31
43
+++ b/blockdev.c
32
diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h
44
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
33
index XXXXXXX..XXXXXXX 100644
45
TransactionAction *action = common->action;
34
--- a/include/block/block_int-io.h
46
AioContext *aio_context;
35
+++ b/include/block/block_int-io.h
47
AioContext *old_context;
36
@@ -XXX,XX +XXX,XX @@ bool blk_dev_is_tray_open(BlockBackend *blk);
48
+ uint64_t perm, shared;
37
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes);
49
int ret;
38
50
39
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
51
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
40
-bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest,
52
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
41
+void bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest,
42
const BdrvDirtyBitmap *src,
43
HBitmap **backup, bool lock);
44
45
diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h
46
index XXXXXXX..XXXXXXX 100644
47
--- a/include/qemu/hbitmap.h
48
+++ b/include/qemu/hbitmap.h
49
@@ -XXX,XX +XXX,XX @@ void hbitmap_truncate(HBitmap *hb, uint64_t size);
50
*
51
* Store result of merging @a and @b into @result.
52
* @result is allowed to be equal to @a or @b.
53
- *
54
- * Return true if the merge was successful,
55
- * false if it was not attempted.
56
- */
57
-bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result);
58
-
59
-/**
60
- * hbitmap_can_merge:
61
- *
62
- * hbitmap_can_merge(a, b) && hbitmap_can_merge(a, result) is sufficient and
63
- * necessary for hbitmap_merge will not fail.
64
- *
65
+ * All bitmaps must have same size.
66
*/
67
-bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b);
68
+void hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result);
69
70
/**
71
* hbitmap_empty:
72
diff --git a/block/backup.c b/block/backup.c
73
index XXXXXXX..XXXXXXX 100644
74
--- a/block/backup.c
75
+++ b/block/backup.c
76
@@ -XXX,XX +XXX,XX @@ out:
77
78
static void backup_init_bcs_bitmap(BackupBlockJob *job)
79
{
80
- bool ret;
81
uint64_t estimate;
82
BdrvDirtyBitmap *bcs_bitmap = block_copy_dirty_bitmap(job->bcs);
83
84
if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) {
85
bdrv_clear_dirty_bitmap(bcs_bitmap, NULL);
86
- ret = bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap,
87
- NULL, true);
88
- assert(ret);
89
+ bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap, NULL,
90
+ true);
91
} else if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
92
/*
93
* We can't hog the coroutine to initialize this thoroughly.
94
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
95
index XXXXXXX..XXXXXXX 100644
96
--- a/block/dirty-bitmap.c
97
+++ b/block/dirty-bitmap.c
98
@@ -XXX,XX +XXX,XX @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *parent,
99
return NULL;
100
}
101
102
- if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) {
103
- error_setg(errp, "Merging of parent and successor bitmap failed");
104
- return NULL;
105
- }
106
+ hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap);
107
108
parent->disabled = successor->disabled;
109
parent->busy = false;
110
@@ -XXX,XX +XXX,XX @@ bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
53
goto out;
111
goto out;
54
}
112
}
55
113
56
- if (bdrv_has_blk(state->new_bs)) {
114
- if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) {
57
+ /*
115
- error_setg(errp, "Bitmaps are incompatible and can't be merged");
58
+ * Allow attaching a backing file to an overlay that's already in use only
116
+ if (bdrv_dirty_bitmap_size(src) != bdrv_dirty_bitmap_size(dest)) {
59
+ * if the parents don't assume that they are already seeing a valid image.
117
+ error_setg(errp, "Bitmaps are of different sizes (destination size is %"
60
+ * (Specifically, allow it as a mirror target, which is write-only access.)
118
+ PRId64 ", source size is %" PRId64 ") and can't be merged",
61
+ */
119
+ bdrv_dirty_bitmap_size(dest), bdrv_dirty_bitmap_size(src));
62
+ bdrv_get_cumulative_perm(state->new_bs, &perm, &shared);
63
+ if (perm & BLK_PERM_CONSISTENT_READ) {
64
error_setg(errp, "The overlay is already in use");
65
goto out;
120
goto out;
66
}
121
}
67
122
68
- if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
123
- ret = bdrv_dirty_bitmap_merge_internal(dest, src, backup, false);
69
- errp)) {
124
- assert(ret);
70
- goto out;
125
+ bdrv_dirty_bitmap_merge_internal(dest, src, backup, false);
126
+ ret = true;
127
128
out:
129
bdrv_dirty_bitmaps_unlock(dest->bs);
130
@@ -XXX,XX +XXX,XX @@ out:
131
/**
132
* bdrv_dirty_bitmap_merge_internal: merge src into dest.
133
* Does NOT check bitmap permissions; not suitable for use as public API.
134
+ * @dest, @src and @backup (if not NULL) must have same size.
135
*
136
* @backup: If provided, make a copy of dest here prior to merge.
137
* @lock: If true, lock and unlock bitmaps on the way in/out.
138
- * returns true if the merge succeeded; false if unattempted.
139
*/
140
-bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest,
141
+void bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest,
142
const BdrvDirtyBitmap *src,
143
HBitmap **backup,
144
bool lock)
145
{
146
- bool ret;
147
IO_CODE();
148
149
assert(!bdrv_dirty_bitmap_readonly(dest));
150
@@ -XXX,XX +XXX,XX @@ bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest,
151
if (backup) {
152
*backup = dest->bitmap;
153
dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup));
154
- ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap);
155
+ hbitmap_merge(*backup, src->bitmap, dest->bitmap);
156
} else {
157
- ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap);
158
+ hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap);
159
}
160
161
if (lock) {
162
@@ -XXX,XX +XXX,XX @@ bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest,
163
bdrv_dirty_bitmaps_unlock(src->bs);
164
}
165
}
166
-
167
- return ret;
168
}
169
diff --git a/util/hbitmap.c b/util/hbitmap.c
170
index XXXXXXX..XXXXXXX 100644
171
--- a/util/hbitmap.c
172
+++ b/util/hbitmap.c
173
@@ -XXX,XX +XXX,XX @@ void hbitmap_truncate(HBitmap *hb, uint64_t size)
174
}
175
}
176
177
-bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b)
178
-{
179
- return (a->orig_size == b->orig_size);
180
-}
181
-
182
/**
183
* hbitmap_sparse_merge: performs dst = dst | src
184
* works with differing granularities.
185
@@ -XXX,XX +XXX,XX @@ static void hbitmap_sparse_merge(HBitmap *dst, const HBitmap *src)
186
* Given HBitmaps A and B, let R := A (BITOR) B.
187
* Bitmaps A and B will not be modified,
188
* except when bitmap R is an alias of A or B.
189
- *
190
- * @return true if the merge was successful,
191
- * false if it was not attempted.
192
+ * Bitmaps must have same size.
193
*/
194
-bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result)
195
+void hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result)
196
{
197
int i;
198
uint64_t j;
199
200
- if (!hbitmap_can_merge(a, b) || !hbitmap_can_merge(a, result)) {
201
- return false;
71
- }
202
- }
72
-
203
- assert(hbitmap_can_merge(b, result));
73
if (state->new_bs->backing != NULL) {
204
+ assert(a->orig_size == result->orig_size);
74
error_setg(errp, "The overlay already has a backing image");
205
+ assert(b->orig_size == result->orig_size);
75
goto out;
206
76
diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out
207
if ((!hbitmap_count(a) && result == b) ||
77
index XXXXXXX..XXXXXXX 100644
208
(!hbitmap_count(b) && result == a)) {
78
--- a/tests/qemu-iotests/085.out
209
- return true;
79
+++ b/tests/qemu-iotests/085.out
210
+ return;
80
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/12-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_f
211
}
81
=== Invalid command - cannot create a snapshot using a file BDS ===
212
82
213
if (!hbitmap_count(a) && !hbitmap_count(b)) {
83
{ 'execute': 'blockdev-snapshot', 'arguments': { 'node':'virtio0', 'overlay':'file_12' } }
214
hbitmap_reset_all(result);
84
-{"error": {"class": "GenericError", "desc": "The overlay does not support backing images"}}
215
- return true;
85
+{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
216
+ return;
86
217
}
87
=== Invalid command - snapshot node used as active layer ===
218
88
219
if (a->granularity != b->granularity) {
89
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/12-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_f
220
@@ -XXX,XX +XXX,XX @@ bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result)
90
=== Invalid command - snapshot node used as backing hd ===
221
if (b != result) {
91
222
hbitmap_sparse_merge(result, b);
92
{ 'execute': 'blockdev-snapshot', 'arguments': { 'node': 'virtio0', 'overlay':'snap_11' } }
223
}
93
-{"error": {"class": "GenericError", "desc": "Node 'snap_11' is busy: node is used as backing hd of 'snap_12'"}}
224
- return true;
94
+{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
225
+ return;
95
226
}
96
=== Invalid command - snapshot node has a backing image ===
227
97
228
/* This merge is O(size), as BITS_PER_LONG and HBITMAP_LEVELS are constant.
229
@@ -XXX,XX +XXX,XX @@ bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result)
230
231
/* Recompute the dirty count */
232
result->count = hb_count_between(result, 0, result->size - 1);
233
-
234
- return true;
235
}
236
237
char *hbitmap_sha256(const HBitmap *bitmap, Error **errp)
98
--
238
--
99
2.20.1
239
2.35.3
100
101
diff view generated by jsdifflib
New patch
1
From: Xie Yongji <xieyongji@bytedance.com>
1
2
3
This supports passing NULL ops to blk_set_dev_ops()
4
so that we can remove stale ops in some cases.
5
6
Signed-off-by: Xie Yongji <xieyongji@bytedance.com>
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Message-Id: <20220523084611.91-2-xieyongji@bytedance.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
block/block-backend.c | 2 +-
12
1 file changed, 1 insertion(+), 1 deletion(-)
13
14
diff --git a/block/block-backend.c b/block/block-backend.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/block/block-backend.c
17
+++ b/block/block-backend.c
18
@@ -XXX,XX +XXX,XX @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
19
blk->dev_opaque = opaque;
20
21
/* Are we currently quiesced? Should we enforce this right now? */
22
- if (blk->quiesce_counter && ops->drained_begin) {
23
+ if (blk->quiesce_counter && ops && ops->drained_begin) {
24
ops->drained_begin(opaque);
25
}
26
}
27
--
28
2.35.3
diff view generated by jsdifflib
New patch
1
From: Xie Yongji <xieyongji@bytedance.com>
1
2
3
Now the req->size is set to the correct value only
4
when handling VIRTIO_BLK_T_GET_ID request. This patch
5
fixes it.
6
7
Signed-off-by: Xie Yongji <xieyongji@bytedance.com>
8
Message-Id: <20220523084611.91-3-xieyongji@bytedance.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block/export/vhost-user-blk-server.c | 5 ++---
13
1 file changed, 2 insertions(+), 3 deletions(-)
14
15
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/export/vhost-user-blk-server.c
18
+++ b/block/export/vhost-user-blk-server.c
19
@@ -XXX,XX +XXX,XX @@ static void vu_blk_req_complete(VuBlkReq *req)
20
{
21
VuDev *vu_dev = &req->server->vu_dev;
22
23
- /* IO size with 1 extra status byte */
24
- vu_queue_push(vu_dev, req->vq, &req->elem, req->size + 1);
25
+ vu_queue_push(vu_dev, req->vq, &req->elem, req->size);
26
vu_queue_notify(vu_dev, req->vq);
27
28
free(req);
29
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
30
goto err;
31
}
32
33
+ req->size = iov_size(in_iov, in_num);
34
/* We always touch the last byte, so just see how big in_iov is. */
35
req->in = (void *)in_iov[in_num - 1].iov_base
36
+ in_iov[in_num - 1].iov_len
37
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
38
VIRTIO_BLK_ID_BYTES);
39
snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk");
40
req->in->status = VIRTIO_BLK_S_OK;
41
- req->size = elem->in_sg[0].iov_len;
42
break;
43
}
44
case VIRTIO_BLK_T_DISCARD:
45
--
46
2.35.3
diff view generated by jsdifflib
New patch
1
From: Xie Yongji <xieyongji@bytedance.com>
1
2
3
Abstract the common logic of virtio-blk I/O process to a function
4
named virtio_blk_process_req(). It's needed for the following commit.
5
6
Signed-off-by: Xie Yongji <xieyongji@bytedance.com>
7
Message-Id: <20220523084611.91-4-xieyongji@bytedance.com>
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
11
block/export/virtio-blk-handler.h | 37 ++++
12
block/export/vhost-user-blk-server.c | 259 +++------------------------
13
block/export/virtio-blk-handler.c | 240 +++++++++++++++++++++++++
14
MAINTAINERS | 2 +
15
block/export/meson.build | 2 +-
16
5 files changed, 301 insertions(+), 239 deletions(-)
17
create mode 100644 block/export/virtio-blk-handler.h
18
create mode 100644 block/export/virtio-blk-handler.c
19
20
diff --git a/block/export/virtio-blk-handler.h b/block/export/virtio-blk-handler.h
21
new file mode 100644
22
index XXXXXXX..XXXXXXX
23
--- /dev/null
24
+++ b/block/export/virtio-blk-handler.h
25
@@ -XXX,XX +XXX,XX @@
26
+/*
27
+ * Handler for virtio-blk I/O
28
+ *
29
+ * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved.
30
+ *
31
+ * Author:
32
+ * Xie Yongji <xieyongji@bytedance.com>
33
+ *
34
+ * This work is licensed under the terms of the GNU GPL, version 2 or
35
+ * later. See the COPYING file in the top-level directory.
36
+ */
37
+
38
+#ifndef VIRTIO_BLK_HANDLER_H
39
+#define VIRTIO_BLK_HANDLER_H
40
+
41
+#include "sysemu/block-backend.h"
42
+
43
+#define VIRTIO_BLK_SECTOR_BITS 9
44
+#define VIRTIO_BLK_SECTOR_SIZE (1ULL << VIRTIO_BLK_SECTOR_BITS)
45
+
46
+#define VIRTIO_BLK_MAX_DISCARD_SECTORS 32768
47
+#define VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS 32768
48
+
49
+typedef struct {
50
+ BlockBackend *blk;
51
+ const char *serial;
52
+ uint32_t logical_block_size;
53
+ bool writable;
54
+} VirtioBlkHandler;
55
+
56
+int coroutine_fn virtio_blk_process_req(VirtioBlkHandler *handler,
57
+ struct iovec *in_iov,
58
+ struct iovec *out_iov,
59
+ unsigned int in_num,
60
+ unsigned int out_num);
61
+
62
+#endif /* VIRTIO_BLK_HANDLER_H */
63
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
64
index XXXXXXX..XXXXXXX 100644
65
--- a/block/export/vhost-user-blk-server.c
66
+++ b/block/export/vhost-user-blk-server.c
67
@@ -XXX,XX +XXX,XX @@
68
#include "vhost-user-blk-server.h"
69
#include "qapi/error.h"
70
#include "qom/object_interfaces.h"
71
-#include "sysemu/block-backend.h"
72
#include "util/block-helpers.h"
73
-
74
-/*
75
- * Sector units are 512 bytes regardless of the
76
- * virtio_blk_config->blk_size value.
77
- */
78
-#define VIRTIO_BLK_SECTOR_BITS 9
79
-#define VIRTIO_BLK_SECTOR_SIZE (1ull << VIRTIO_BLK_SECTOR_BITS)
80
+#include "virtio-blk-handler.h"
81
82
enum {
83
VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1,
84
- VHOST_USER_BLK_MAX_DISCARD_SECTORS = 32768,
85
- VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS = 32768,
86
-};
87
-struct virtio_blk_inhdr {
88
- unsigned char status;
89
};
90
91
typedef struct VuBlkReq {
92
VuVirtqElement elem;
93
- int64_t sector_num;
94
- size_t size;
95
- struct virtio_blk_inhdr *in;
96
- struct virtio_blk_outhdr out;
97
VuServer *server;
98
struct VuVirtq *vq;
99
} VuBlkReq;
100
@@ -XXX,XX +XXX,XX @@ typedef struct VuBlkReq {
101
typedef struct {
102
BlockExport export;
103
VuServer vu_server;
104
- uint32_t blk_size;
105
+ VirtioBlkHandler handler;
106
QIOChannelSocket *sioc;
107
struct virtio_blk_config blkcfg;
108
- bool writable;
109
} VuBlkExport;
110
111
-static void vu_blk_req_complete(VuBlkReq *req)
112
+static void vu_blk_req_complete(VuBlkReq *req, size_t in_len)
113
{
114
VuDev *vu_dev = &req->server->vu_dev;
115
116
- vu_queue_push(vu_dev, req->vq, &req->elem, req->size);
117
+ vu_queue_push(vu_dev, req->vq, &req->elem, in_len);
118
vu_queue_notify(vu_dev, req->vq);
119
120
free(req);
121
}
122
123
-static bool vu_blk_sect_range_ok(VuBlkExport *vexp, uint64_t sector,
124
- size_t size)
125
-{
126
- uint64_t nb_sectors;
127
- uint64_t total_sectors;
128
-
129
- if (size % VIRTIO_BLK_SECTOR_SIZE) {
130
- return false;
131
- }
132
-
133
- nb_sectors = size >> VIRTIO_BLK_SECTOR_BITS;
134
-
135
- QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != VIRTIO_BLK_SECTOR_SIZE);
136
- if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
137
- return false;
138
- }
139
- if ((sector << VIRTIO_BLK_SECTOR_BITS) % vexp->blk_size) {
140
- return false;
141
- }
142
- blk_get_geometry(vexp->export.blk, &total_sectors);
143
- if (sector > total_sectors || nb_sectors > total_sectors - sector) {
144
- return false;
145
- }
146
- return true;
147
-}
148
-
149
-static int coroutine_fn
150
-vu_blk_discard_write_zeroes(VuBlkExport *vexp, struct iovec *iov,
151
- uint32_t iovcnt, uint32_t type)
152
-{
153
- BlockBackend *blk = vexp->export.blk;
154
- struct virtio_blk_discard_write_zeroes desc;
155
- ssize_t size;
156
- uint64_t sector;
157
- uint32_t num_sectors;
158
- uint32_t max_sectors;
159
- uint32_t flags;
160
- int bytes;
161
-
162
- /* Only one desc is currently supported */
163
- if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) {
164
- return VIRTIO_BLK_S_UNSUPP;
165
- }
166
-
167
- size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc));
168
- if (unlikely(size != sizeof(desc))) {
169
- error_report("Invalid size %zd, expected %zu", size, sizeof(desc));
170
- return VIRTIO_BLK_S_IOERR;
171
- }
172
-
173
- sector = le64_to_cpu(desc.sector);
174
- num_sectors = le32_to_cpu(desc.num_sectors);
175
- flags = le32_to_cpu(desc.flags);
176
- max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ?
177
- VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS :
178
- VHOST_USER_BLK_MAX_DISCARD_SECTORS;
179
-
180
- /* This check ensures that 'bytes' fits in an int */
181
- if (unlikely(num_sectors > max_sectors)) {
182
- return VIRTIO_BLK_S_IOERR;
183
- }
184
-
185
- bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS;
186
-
187
- if (unlikely(!vu_blk_sect_range_ok(vexp, sector, bytes))) {
188
- return VIRTIO_BLK_S_IOERR;
189
- }
190
-
191
- /*
192
- * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard
193
- * and write zeroes commands if any unknown flag is set.
194
- */
195
- if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
196
- return VIRTIO_BLK_S_UNSUPP;
197
- }
198
-
199
- if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
200
- int blk_flags = 0;
201
-
202
- if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) {
203
- blk_flags |= BDRV_REQ_MAY_UNMAP;
204
- }
205
-
206
- if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS,
207
- bytes, blk_flags) == 0) {
208
- return VIRTIO_BLK_S_OK;
209
- }
210
- } else if (type == VIRTIO_BLK_T_DISCARD) {
211
- /*
212
- * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for
213
- * discard commands if the unmap flag is set.
214
- */
215
- if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
216
- return VIRTIO_BLK_S_UNSUPP;
217
- }
218
-
219
- if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS,
220
- bytes) == 0) {
221
- return VIRTIO_BLK_S_OK;
222
- }
223
- }
224
-
225
- return VIRTIO_BLK_S_IOERR;
226
-}
227
-
228
/* Called with server refcount increased, must decrease before returning */
229
static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
230
{
231
VuBlkReq *req = opaque;
232
VuServer *server = req->server;
233
VuVirtqElement *elem = &req->elem;
234
- uint32_t type;
235
-
236
VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server);
237
- BlockBackend *blk = vexp->export.blk;
238
-
239
+ VirtioBlkHandler *handler = &vexp->handler;
240
struct iovec *in_iov = elem->in_sg;
241
struct iovec *out_iov = elem->out_sg;
242
unsigned in_num = elem->in_num;
243
unsigned out_num = elem->out_num;
244
-
245
- /* refer to hw/block/virtio_blk.c */
246
- if (elem->out_num < 1 || elem->in_num < 1) {
247
- error_report("virtio-blk request missing headers");
248
- goto err;
249
- }
250
-
251
- if (unlikely(iov_to_buf(out_iov, out_num, 0, &req->out,
252
- sizeof(req->out)) != sizeof(req->out))) {
253
- error_report("virtio-blk request outhdr too short");
254
- goto err;
255
- }
256
-
257
- iov_discard_front(&out_iov, &out_num, sizeof(req->out));
258
-
259
- if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) {
260
- error_report("virtio-blk request inhdr too short");
261
- goto err;
262
- }
263
-
264
- req->size = iov_size(in_iov, in_num);
265
- /* We always touch the last byte, so just see how big in_iov is. */
266
- req->in = (void *)in_iov[in_num - 1].iov_base
267
- + in_iov[in_num - 1].iov_len
268
- - sizeof(struct virtio_blk_inhdr);
269
- iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr));
270
-
271
- type = le32_to_cpu(req->out.type);
272
- switch (type & ~VIRTIO_BLK_T_BARRIER) {
273
- case VIRTIO_BLK_T_IN:
274
- case VIRTIO_BLK_T_OUT: {
275
- QEMUIOVector qiov;
276
- int64_t offset;
277
- ssize_t ret = 0;
278
- bool is_write = type & VIRTIO_BLK_T_OUT;
279
- req->sector_num = le64_to_cpu(req->out.sector);
280
-
281
- if (is_write && !vexp->writable) {
282
- req->in->status = VIRTIO_BLK_S_IOERR;
283
- break;
284
- }
285
-
286
- if (is_write) {
287
- qemu_iovec_init_external(&qiov, out_iov, out_num);
288
- } else {
289
- qemu_iovec_init_external(&qiov, in_iov, in_num);
290
- }
291
-
292
- if (unlikely(!vu_blk_sect_range_ok(vexp,
293
- req->sector_num,
294
- qiov.size))) {
295
- req->in->status = VIRTIO_BLK_S_IOERR;
296
- break;
297
- }
298
-
299
- offset = req->sector_num << VIRTIO_BLK_SECTOR_BITS;
300
-
301
- if (is_write) {
302
- ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0);
303
- } else {
304
- ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0);
305
- }
306
- if (ret >= 0) {
307
- req->in->status = VIRTIO_BLK_S_OK;
308
- } else {
309
- req->in->status = VIRTIO_BLK_S_IOERR;
310
- }
311
- break;
312
- }
313
- case VIRTIO_BLK_T_FLUSH:
314
- if (blk_co_flush(blk) == 0) {
315
- req->in->status = VIRTIO_BLK_S_OK;
316
- } else {
317
- req->in->status = VIRTIO_BLK_S_IOERR;
318
- }
319
- break;
320
- case VIRTIO_BLK_T_GET_ID: {
321
- size_t size = MIN(iov_size(&elem->in_sg[0], in_num),
322
- VIRTIO_BLK_ID_BYTES);
323
- snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk");
324
- req->in->status = VIRTIO_BLK_S_OK;
325
- break;
326
+ int in_len;
327
+
328
+ in_len = virtio_blk_process_req(handler, in_iov, out_iov,
329
+ in_num, out_num);
330
+ if (in_len < 0) {
331
+ free(req);
332
+ vhost_user_server_unref(server);
333
+ return;
334
}
335
- case VIRTIO_BLK_T_DISCARD:
336
- case VIRTIO_BLK_T_WRITE_ZEROES: {
337
- if (!vexp->writable) {
338
- req->in->status = VIRTIO_BLK_S_IOERR;
339
- break;
340
- }
341
-
342
- req->in->status = vu_blk_discard_write_zeroes(vexp, out_iov, out_num,
343
- type);
344
- break;
345
- }
346
- default:
347
- req->in->status = VIRTIO_BLK_S_UNSUPP;
348
- break;
349
- }
350
-
351
- vu_blk_req_complete(req);
352
- vhost_user_server_unref(server);
353
- return;
354
355
-err:
356
- free(req);
357
+ vu_blk_req_complete(req, in_len);
358
vhost_user_server_unref(server);
359
}
360
361
@@ -XXX,XX +XXX,XX @@ static uint64_t vu_blk_get_features(VuDev *dev)
362
1ull << VIRTIO_RING_F_EVENT_IDX |
363
1ull << VHOST_USER_F_PROTOCOL_FEATURES;
364
365
- if (!vexp->writable) {
366
+ if (!vexp->handler.writable) {
367
features |= 1ull << VIRTIO_BLK_F_RO;
368
}
369
370
@@ -XXX,XX +XXX,XX @@ vu_blk_initialize_config(BlockDriverState *bs,
371
config->opt_io_size = cpu_to_le32(1);
372
config->num_queues = cpu_to_le16(num_queues);
373
config->max_discard_sectors =
374
- cpu_to_le32(VHOST_USER_BLK_MAX_DISCARD_SECTORS);
375
+ cpu_to_le32(VIRTIO_BLK_MAX_DISCARD_SECTORS);
376
config->max_discard_seg = cpu_to_le32(1);
377
config->discard_sector_alignment =
378
cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS);
379
config->max_write_zeroes_sectors
380
- = cpu_to_le32(VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS);
381
+ = cpu_to_le32(VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS);
382
config->max_write_zeroes_seg = cpu_to_le32(1);
383
}
384
385
@@ -XXX,XX +XXX,XX @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
386
uint64_t logical_block_size;
387
uint16_t num_queues = VHOST_USER_BLK_NUM_QUEUES_DEFAULT;
388
389
- vexp->writable = opts->writable;
390
vexp->blkcfg.wce = 0;
391
392
if (vu_opts->has_logical_block_size) {
393
@@ -XXX,XX +XXX,XX @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
394
error_propagate(errp, local_err);
395
return -EINVAL;
396
}
397
- vexp->blk_size = logical_block_size;
398
399
if (vu_opts->has_num_queues) {
400
num_queues = vu_opts->num_queues;
401
@@ -XXX,XX +XXX,XX @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
402
error_setg(errp, "num-queues must be greater than 0");
403
return -EINVAL;
404
}
405
+ vexp->handler.blk = exp->blk;
406
+ vexp->handler.serial = "vhost_user_blk";
407
+ vexp->handler.logical_block_size = logical_block_size;
408
+ vexp->handler.writable = opts->writable;
409
410
vu_blk_initialize_config(blk_bs(exp->blk), &vexp->blkcfg,
411
logical_block_size, num_queues);
412
diff --git a/block/export/virtio-blk-handler.c b/block/export/virtio-blk-handler.c
413
new file mode 100644
414
index XXXXXXX..XXXXXXX
415
--- /dev/null
416
+++ b/block/export/virtio-blk-handler.c
417
@@ -XXX,XX +XXX,XX @@
418
+/*
419
+ * Handler for virtio-blk I/O
420
+ *
421
+ * Copyright (c) 2020 Red Hat, Inc.
422
+ * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved.
423
+ *
424
+ * Author:
425
+ * Coiby Xu <coiby.xu@gmail.com>
426
+ * Xie Yongji <xieyongji@bytedance.com>
427
+ *
428
+ * This work is licensed under the terms of the GNU GPL, version 2 or
429
+ * later. See the COPYING file in the top-level directory.
430
+ */
431
+
432
+#include "qemu/osdep.h"
433
+#include "qemu/error-report.h"
434
+#include "virtio-blk-handler.h"
435
+
436
+#include "standard-headers/linux/virtio_blk.h"
437
+
438
+struct virtio_blk_inhdr {
439
+ unsigned char status;
440
+};
441
+
442
+static bool virtio_blk_sect_range_ok(BlockBackend *blk, uint32_t block_size,
443
+ uint64_t sector, size_t size)
444
+{
445
+ uint64_t nb_sectors;
446
+ uint64_t total_sectors;
447
+
448
+ if (size % VIRTIO_BLK_SECTOR_SIZE) {
449
+ return false;
450
+ }
451
+
452
+ nb_sectors = size >> VIRTIO_BLK_SECTOR_BITS;
453
+
454
+ QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != VIRTIO_BLK_SECTOR_SIZE);
455
+ if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
456
+ return false;
457
+ }
458
+ if ((sector << VIRTIO_BLK_SECTOR_BITS) % block_size) {
459
+ return false;
460
+ }
461
+ blk_get_geometry(blk, &total_sectors);
462
+ if (sector > total_sectors || nb_sectors > total_sectors - sector) {
463
+ return false;
464
+ }
465
+ return true;
466
+}
467
+
468
+static int coroutine_fn
469
+virtio_blk_discard_write_zeroes(VirtioBlkHandler *handler, struct iovec *iov,
470
+ uint32_t iovcnt, uint32_t type)
471
+{
472
+ BlockBackend *blk = handler->blk;
473
+ struct virtio_blk_discard_write_zeroes desc;
474
+ ssize_t size;
475
+ uint64_t sector;
476
+ uint32_t num_sectors;
477
+ uint32_t max_sectors;
478
+ uint32_t flags;
479
+ int bytes;
480
+
481
+ /* Only one desc is currently supported */
482
+ if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) {
483
+ return VIRTIO_BLK_S_UNSUPP;
484
+ }
485
+
486
+ size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc));
487
+ if (unlikely(size != sizeof(desc))) {
488
+ error_report("Invalid size %zd, expected %zu", size, sizeof(desc));
489
+ return VIRTIO_BLK_S_IOERR;
490
+ }
491
+
492
+ sector = le64_to_cpu(desc.sector);
493
+ num_sectors = le32_to_cpu(desc.num_sectors);
494
+ flags = le32_to_cpu(desc.flags);
495
+ max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ?
496
+ VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS :
497
+ VIRTIO_BLK_MAX_DISCARD_SECTORS;
498
+
499
+ /* This check ensures that 'bytes' fits in an int */
500
+ if (unlikely(num_sectors > max_sectors)) {
501
+ return VIRTIO_BLK_S_IOERR;
502
+ }
503
+
504
+ bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS;
505
+
506
+ if (unlikely(!virtio_blk_sect_range_ok(blk, handler->logical_block_size,
507
+ sector, bytes))) {
508
+ return VIRTIO_BLK_S_IOERR;
509
+ }
510
+
511
+ /*
512
+ * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard
513
+ * and write zeroes commands if any unknown flag is set.
514
+ */
515
+ if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
516
+ return VIRTIO_BLK_S_UNSUPP;
517
+ }
518
+
519
+ if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
520
+ int blk_flags = 0;
521
+
522
+ if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) {
523
+ blk_flags |= BDRV_REQ_MAY_UNMAP;
524
+ }
525
+
526
+ if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS,
527
+ bytes, blk_flags) == 0) {
528
+ return VIRTIO_BLK_S_OK;
529
+ }
530
+ } else if (type == VIRTIO_BLK_T_DISCARD) {
531
+ /*
532
+ * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for
533
+ * discard commands if the unmap flag is set.
534
+ */
535
+ if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
536
+ return VIRTIO_BLK_S_UNSUPP;
537
+ }
538
+
539
+ if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS,
540
+ bytes) == 0) {
541
+ return VIRTIO_BLK_S_OK;
542
+ }
543
+ }
544
+
545
+ return VIRTIO_BLK_S_IOERR;
546
+}
547
+
548
+int coroutine_fn virtio_blk_process_req(VirtioBlkHandler *handler,
549
+ struct iovec *in_iov,
550
+ struct iovec *out_iov,
551
+ unsigned int in_num,
552
+ unsigned int out_num)
553
+{
554
+ BlockBackend *blk = handler->blk;
555
+ struct virtio_blk_inhdr *in;
556
+ struct virtio_blk_outhdr out;
557
+ uint32_t type;
558
+ int in_len;
559
+
560
+ if (out_num < 1 || in_num < 1) {
561
+ error_report("virtio-blk request missing headers");
562
+ return -EINVAL;
563
+ }
564
+
565
+ if (unlikely(iov_to_buf(out_iov, out_num, 0, &out,
566
+ sizeof(out)) != sizeof(out))) {
567
+ error_report("virtio-blk request outhdr too short");
568
+ return -EINVAL;
569
+ }
570
+
571
+ iov_discard_front(&out_iov, &out_num, sizeof(out));
572
+
573
+ if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) {
574
+ error_report("virtio-blk request inhdr too short");
575
+ return -EINVAL;
576
+ }
577
+
578
+ /* We always touch the last byte, so just see how big in_iov is. */
579
+ in_len = iov_size(in_iov, in_num);
580
+ in = (void *)in_iov[in_num - 1].iov_base
581
+ + in_iov[in_num - 1].iov_len
582
+ - sizeof(struct virtio_blk_inhdr);
583
+ iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr));
584
+
585
+ type = le32_to_cpu(out.type);
586
+ switch (type & ~VIRTIO_BLK_T_BARRIER) {
587
+ case VIRTIO_BLK_T_IN:
588
+ case VIRTIO_BLK_T_OUT: {
589
+ QEMUIOVector qiov;
590
+ int64_t offset;
591
+ ssize_t ret = 0;
592
+ bool is_write = type & VIRTIO_BLK_T_OUT;
593
+ int64_t sector_num = le64_to_cpu(out.sector);
594
+
595
+ if (is_write && !handler->writable) {
596
+ in->status = VIRTIO_BLK_S_IOERR;
597
+ break;
598
+ }
599
+
600
+ if (is_write) {
601
+ qemu_iovec_init_external(&qiov, out_iov, out_num);
602
+ } else {
603
+ qemu_iovec_init_external(&qiov, in_iov, in_num);
604
+ }
605
+
606
+ if (unlikely(!virtio_blk_sect_range_ok(blk,
607
+ handler->logical_block_size,
608
+ sector_num, qiov.size))) {
609
+ in->status = VIRTIO_BLK_S_IOERR;
610
+ break;
611
+ }
612
+
613
+ offset = sector_num << VIRTIO_BLK_SECTOR_BITS;
614
+
615
+ if (is_write) {
616
+ ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0);
617
+ } else {
618
+ ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0);
619
+ }
620
+ if (ret >= 0) {
621
+ in->status = VIRTIO_BLK_S_OK;
622
+ } else {
623
+ in->status = VIRTIO_BLK_S_IOERR;
624
+ }
625
+ break;
626
+ }
627
+ case VIRTIO_BLK_T_FLUSH:
628
+ if (blk_co_flush(blk) == 0) {
629
+ in->status = VIRTIO_BLK_S_OK;
630
+ } else {
631
+ in->status = VIRTIO_BLK_S_IOERR;
632
+ }
633
+ break;
634
+ case VIRTIO_BLK_T_GET_ID: {
635
+ size_t size = MIN(strlen(handler->serial) + 1,
636
+ MIN(iov_size(in_iov, in_num),
637
+ VIRTIO_BLK_ID_BYTES));
638
+ iov_from_buf(in_iov, in_num, 0, handler->serial, size);
639
+ in->status = VIRTIO_BLK_S_OK;
640
+ break;
641
+ }
642
+ case VIRTIO_BLK_T_DISCARD:
643
+ case VIRTIO_BLK_T_WRITE_ZEROES:
644
+ if (!handler->writable) {
645
+ in->status = VIRTIO_BLK_S_IOERR;
646
+ break;
647
+ }
648
+ in->status = virtio_blk_discard_write_zeroes(handler, out_iov,
649
+ out_num, type);
650
+ break;
651
+ default:
652
+ in->status = VIRTIO_BLK_S_UNSUPP;
653
+ break;
654
+ }
655
+
656
+ return in_len;
657
+}
658
diff --git a/MAINTAINERS b/MAINTAINERS
659
index XXXXXXX..XXXXXXX 100644
660
--- a/MAINTAINERS
661
+++ b/MAINTAINERS
662
@@ -XXX,XX +XXX,XX @@ M: Coiby Xu <Coiby.Xu@gmail.com>
663
S: Maintained
664
F: block/export/vhost-user-blk-server.c
665
F: block/export/vhost-user-blk-server.h
666
+F: block/export/virtio-blk-handler.c
667
+F: block/export/virtio-blk-handler.h
668
F: include/qemu/vhost-user-server.h
669
F: tests/qtest/libqos/vhost-user-blk.c
670
F: tests/qtest/libqos/vhost-user-blk.h
671
diff --git a/block/export/meson.build b/block/export/meson.build
672
index XXXXXXX..XXXXXXX 100644
673
--- a/block/export/meson.build
674
+++ b/block/export/meson.build
675
@@ -XXX,XX +XXX,XX @@
676
blockdev_ss.add(files('export.c'))
677
678
if have_vhost_user_blk_server
679
- blockdev_ss.add(files('vhost-user-blk-server.c'))
680
+ blockdev_ss.add(files('vhost-user-blk-server.c', 'virtio-blk-handler.c'))
681
endif
682
683
blockdev_ss.add(when: fuse, if_true: files('fuse.c'))
684
--
685
2.35.3
diff view generated by jsdifflib
1
From: Philippe Mathieu-Daudé <philmd@redhat.com>
1
From: Xie Yongji <xieyongji@bytedance.com>
2
2
3
The socket_scm_helper path got corrupted during the mechanical
3
This adds vduse header to linux headers so that the
4
refactor moving the qtests files into their own sub-directory.
4
relevant VDUSE API can be used in subsequent patches.
5
5
6
Fixes: 1e8a1fae7 ("test: Move qtests to a separate directory")
6
Signed-off-by: Xie Yongji <xieyongji@bytedance.com>
7
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Message-Id: <20200306165751.18986-1-philmd@redhat.com>
8
Message-Id: <20220523084611.91-5-xieyongji@bytedance.com>
9
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
10
---
12
tests/Makefile.include | 1 +
11
linux-headers/linux/vduse.h | 306 ++++++++++++++++++++++++++++++++
13
tests/qtest/Makefile.include | 1 -
12
scripts/update-linux-headers.sh | 2 +-
14
2 files changed, 1 insertion(+), 1 deletion(-)
13
2 files changed, 307 insertions(+), 1 deletion(-)
14
create mode 100644 linux-headers/linux/vduse.h
15
15
16
diff --git a/tests/Makefile.include b/tests/Makefile.include
16
diff --git a/linux-headers/linux/vduse.h b/linux-headers/linux/vduse.h
17
index XXXXXXX..XXXXXXX 100644
17
new file mode 100644
18
--- a/tests/Makefile.include
18
index XXXXXXX..XXXXXXX
19
+++ b/tests/Makefile.include
19
--- /dev/null
20
@@ -XXX,XX +XXX,XX @@ include $(SRC_PATH)/tests/qtest/Makefile.include
20
+++ b/linux-headers/linux/vduse.h
21
tests/test-qga$(EXESUF): qemu-ga$(EXESUF)
21
@@ -XXX,XX +XXX,XX @@
22
tests/test-qga$(EXESUF): tests/test-qga.o $(qtest-obj-y)
22
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
23
tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a
23
+#ifndef _VDUSE_H_
24
+tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
24
+#define _VDUSE_H_
25
25
+
26
SPEED = quick
26
+#include <linux/types.h>
27
27
+
28
diff --git a/tests/qtest/Makefile.include b/tests/qtest/Makefile.include
28
+#define VDUSE_BASE    0x81
29
index XXXXXXX..XXXXXXX 100644
29
+
30
--- a/tests/qtest/Makefile.include
30
+/* The ioctls for control device (/dev/vduse/control) */
31
+++ b/tests/qtest/Makefile.include
31
+
32
@@ -XXX,XX +XXX,XX @@ tests/qtest/usb-hcd-ehci-test$(EXESUF): tests/qtest/usb-hcd-ehci-test.o $(libqos
32
+#define VDUSE_API_VERSION    0
33
tests/qtest/usb-hcd-xhci-test$(EXESUF): tests/qtest/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
33
+
34
tests/qtest/cpu-plug-test$(EXESUF): tests/qtest/cpu-plug-test.o
34
+/*
35
tests/qtest/migration-test$(EXESUF): tests/qtest/migration-test.o tests/qtest/migration-helpers.o
35
+ * Get the version of VDUSE API that kernel supported (VDUSE_API_VERSION).
36
-tests/qtest/qemu-iotests/qtest/socket_scm_helper$(EXESUF): tests/qtest/qemu-iotests/qtest/socket_scm_helper.o
36
+ * This is used for future extension.
37
tests/qtest/test-netfilter$(EXESUF): tests/qtest/test-netfilter.o $(qtest-obj-y)
37
+ */
38
tests/qtest/test-filter-mirror$(EXESUF): tests/qtest/test-filter-mirror.o $(qtest-obj-y)
38
+#define VDUSE_GET_API_VERSION    _IOR(VDUSE_BASE, 0x00, __u64)
39
tests/qtest/test-filter-redirector$(EXESUF): tests/qtest/test-filter-redirector.o $(qtest-obj-y)
39
+
40
+/* Set the version of VDUSE API that userspace supported. */
41
+#define VDUSE_SET_API_VERSION    _IOW(VDUSE_BASE, 0x01, __u64)
42
+
43
+/**
44
+ * struct vduse_dev_config - basic configuration of a VDUSE device
45
+ * @name: VDUSE device name, needs to be NUL terminated
46
+ * @vendor_id: virtio vendor id
47
+ * @device_id: virtio device id
48
+ * @features: virtio features
49
+ * @vq_num: the number of virtqueues
50
+ * @vq_align: the allocation alignment of virtqueue's metadata
51
+ * @reserved: for future use, needs to be initialized to zero
52
+ * @config_size: the size of the configuration space
53
+ * @config: the buffer of the configuration space
54
+ *
55
+ * Structure used by VDUSE_CREATE_DEV ioctl to create VDUSE device.
56
+ */
57
+struct vduse_dev_config {
58
+#define VDUSE_NAME_MAX    256
59
+    char name[VDUSE_NAME_MAX];
60
+    __u32 vendor_id;
61
+    __u32 device_id;
62
+    __u64 features;
63
+    __u32 vq_num;
64
+    __u32 vq_align;
65
+    __u32 reserved[13];
66
+    __u32 config_size;
67
+    __u8 config[];
68
+};
69
+
70
+/* Create a VDUSE device which is represented by a char device (/dev/vduse/$NAME) */
71
+#define VDUSE_CREATE_DEV    _IOW(VDUSE_BASE, 0x02, struct vduse_dev_config)
72
+
73
+/*
74
+ * Destroy a VDUSE device. Make sure there are no more references
75
+ * to the char device (/dev/vduse/$NAME).
76
+ */
77
+#define VDUSE_DESTROY_DEV    _IOW(VDUSE_BASE, 0x03, char[VDUSE_NAME_MAX])
78
+
79
+/* The ioctls for VDUSE device (/dev/vduse/$NAME) */
80
+
81
+/**
82
+ * struct vduse_iotlb_entry - entry of IOTLB to describe one IOVA region [start, last]
83
+ * @offset: the mmap offset on returned file descriptor
84
+ * @start: start of the IOVA region
85
+ * @last: last of the IOVA region
86
+ * @perm: access permission of the IOVA region
87
+ *
88
+ * Structure used by VDUSE_IOTLB_GET_FD ioctl to find an overlapped IOVA region.
89
+ */
90
+struct vduse_iotlb_entry {
91
+    __u64 offset;
92
+    __u64 start;
93
+    __u64 last;
94
+#define VDUSE_ACCESS_RO 0x1
95
+#define VDUSE_ACCESS_WO 0x2
96
+#define VDUSE_ACCESS_RW 0x3
97
+    __u8 perm;
98
+};
99
+
100
+/*
101
+ * Find the first IOVA region that overlaps with the range [start, last]
102
+ * and return the corresponding file descriptor. Return -EINVAL means the
103
+ * IOVA region doesn't exist. Caller should set start and last fields.
104
+ */
105
+#define VDUSE_IOTLB_GET_FD    _IOWR(VDUSE_BASE, 0x10, struct vduse_iotlb_entry)
106
+
107
+/*
108
+ * Get the negotiated virtio features. It's a subset of the features in
109
+ * struct vduse_dev_config which can be accepted by virtio driver. It's
110
+ * only valid after FEATURES_OK status bit is set.
111
+ */
112
+#define VDUSE_DEV_GET_FEATURES    _IOR(VDUSE_BASE, 0x11, __u64)
113
+
114
+/**
115
+ * struct vduse_config_data - data used to update configuration space
116
+ * @offset: the offset from the beginning of configuration space
117
+ * @length: the length to write to configuration space
118
+ * @buffer: the buffer used to write from
119
+ *
120
+ * Structure used by VDUSE_DEV_SET_CONFIG ioctl to update device
121
+ * configuration space.
122
+ */
123
+struct vduse_config_data {
124
+    __u32 offset;
125
+    __u32 length;
126
+    __u8 buffer[];
127
+};
128
+
129
+/* Set device configuration space */
130
+#define VDUSE_DEV_SET_CONFIG    _IOW(VDUSE_BASE, 0x12, struct vduse_config_data)
131
+
132
+/*
133
+ * Inject a config interrupt. It's usually used to notify virtio driver
134
+ * that device configuration space has changed.
135
+ */
136
+#define VDUSE_DEV_INJECT_CONFIG_IRQ    _IO(VDUSE_BASE, 0x13)
137
+
138
+/**
139
+ * struct vduse_vq_config - basic configuration of a virtqueue
140
+ * @index: virtqueue index
141
+ * @max_size: the max size of virtqueue
142
+ * @reserved: for future use, needs to be initialized to zero
143
+ *
144
+ * Structure used by VDUSE_VQ_SETUP ioctl to setup a virtqueue.
145
+ */
146
+struct vduse_vq_config {
147
+    __u32 index;
148
+    __u16 max_size;
149
+    __u16 reserved[13];
150
+};
151
+
152
+/*
153
+ * Setup the specified virtqueue. Make sure all virtqueues have been
154
+ * configured before the device is attached to vDPA bus.
155
+ */
156
+#define VDUSE_VQ_SETUP        _IOW(VDUSE_BASE, 0x14, struct vduse_vq_config)
157
+
158
+/**
159
+ * struct vduse_vq_state_split - split virtqueue state
160
+ * @avail_index: available index
161
+ */
162
+struct vduse_vq_state_split {
163
+    __u16 avail_index;
164
+};
165
+
166
+/**
167
+ * struct vduse_vq_state_packed - packed virtqueue state
168
+ * @last_avail_counter: last driver ring wrap counter observed by device
169
+ * @last_avail_idx: device available index
170
+ * @last_used_counter: device ring wrap counter
171
+ * @last_used_idx: used index
172
+ */
173
+struct vduse_vq_state_packed {
174
+    __u16 last_avail_counter;
175
+    __u16 last_avail_idx;
176
+    __u16 last_used_counter;
177
+    __u16 last_used_idx;
178
+};
179
+
180
+/**
181
+ * struct vduse_vq_info - information of a virtqueue
182
+ * @index: virtqueue index
183
+ * @num: the size of virtqueue
184
+ * @desc_addr: address of desc area
185
+ * @driver_addr: address of driver area
186
+ * @device_addr: address of device area
187
+ * @split: split virtqueue state
188
+ * @packed: packed virtqueue state
189
+ * @ready: ready status of virtqueue
190
+ *
191
+ * Structure used by VDUSE_VQ_GET_INFO ioctl to get virtqueue's information.
192
+ */
193
+struct vduse_vq_info {
194
+    __u32 index;
195
+    __u32 num;
196
+    __u64 desc_addr;
197
+    __u64 driver_addr;
198
+    __u64 device_addr;
199
+    union {
200
+        struct vduse_vq_state_split split;
201
+        struct vduse_vq_state_packed packed;
202
+    };
203
+    __u8 ready;
204
+};
205
+
206
+/* Get the specified virtqueue's information. Caller should set index field. */
207
+#define VDUSE_VQ_GET_INFO    _IOWR(VDUSE_BASE, 0x15, struct vduse_vq_info)
208
+
209
+/**
210
+ * struct vduse_vq_eventfd - eventfd configuration for a virtqueue
211
+ * @index: virtqueue index
212
+ * @fd: eventfd, -1 means de-assigning the eventfd
213
+ *
214
+ * Structure used by VDUSE_VQ_SETUP_KICKFD ioctl to setup kick eventfd.
215
+ */
216
+struct vduse_vq_eventfd {
217
+    __u32 index;
218
+#define VDUSE_EVENTFD_DEASSIGN -1
219
+    int fd;
220
+};
221
+
222
+/*
223
+ * Setup kick eventfd for specified virtqueue. The kick eventfd is used
224
+ * by VDUSE kernel module to notify userspace to consume the avail vring.
225
+ */
226
+#define VDUSE_VQ_SETUP_KICKFD    _IOW(VDUSE_BASE, 0x16, struct vduse_vq_eventfd)
227
+
228
+/*
229
+ * Inject an interrupt for specific virtqueue. It's used to notify virtio driver
230
+ * to consume the used vring.
231
+ */
232
+#define VDUSE_VQ_INJECT_IRQ    _IOW(VDUSE_BASE, 0x17, __u32)
233
+
234
+/* The control messages definition for read(2)/write(2) on /dev/vduse/$NAME */
235
+
236
+/**
237
+ * enum vduse_req_type - request type
238
+ * @VDUSE_GET_VQ_STATE: get the state for specified virtqueue from userspace
239
+ * @VDUSE_SET_STATUS: set the device status
240
+ * @VDUSE_UPDATE_IOTLB: Notify userspace to update the memory mapping for
241
+ * specified IOVA range via VDUSE_IOTLB_GET_FD ioctl
242
+ */
243
+enum vduse_req_type {
244
+    VDUSE_GET_VQ_STATE,
245
+    VDUSE_SET_STATUS,
246
+    VDUSE_UPDATE_IOTLB,
247
+};
248
+
249
+/**
250
+ * struct vduse_vq_state - virtqueue state
251
+ * @index: virtqueue index
252
+ * @split: split virtqueue state
253
+ * @packed: packed virtqueue state
254
+ */
255
+struct vduse_vq_state {
256
+    __u32 index;
257
+    union {
258
+        struct vduse_vq_state_split split;
259
+        struct vduse_vq_state_packed packed;
260
+    };
261
+};
262
+
263
+/**
264
+ * struct vduse_dev_status - device status
265
+ * @status: device status
266
+ */
267
+struct vduse_dev_status {
268
+    __u8 status;
269
+};
270
+
271
+/**
272
+ * struct vduse_iova_range - IOVA range [start, last]
273
+ * @start: start of the IOVA range
274
+ * @last: last of the IOVA range
275
+ */
276
+struct vduse_iova_range {
277
+    __u64 start;
278
+    __u64 last;
279
+};
280
+
281
+/**
282
+ * struct vduse_dev_request - control request
283
+ * @type: request type
284
+ * @request_id: request id
285
+ * @reserved: for future use
286
+ * @vq_state: virtqueue state, only index field is available
287
+ * @s: device status
288
+ * @iova: IOVA range for updating
289
+ * @padding: padding
290
+ *
291
+ * Structure used by read(2) on /dev/vduse/$NAME.
292
+ */
293
+struct vduse_dev_request {
294
+    __u32 type;
295
+    __u32 request_id;
296
+    __u32 reserved[4];
297
+    union {
298
+        struct vduse_vq_state vq_state;
299
+        struct vduse_dev_status s;
300
+        struct vduse_iova_range iova;
301
+        __u32 padding[32];
302
+    };
303
+};
304
+
305
+/**
306
+ * struct vduse_dev_response - response to control request
307
+ * @request_id: corresponding request id
308
+ * @result: the result of request
309
+ * @reserved: for future use, needs to be initialized to zero
310
+ * @vq_state: virtqueue state
311
+ * @padding: padding
312
+ *
313
+ * Structure used by write(2) on /dev/vduse/$NAME.
314
+ */
315
+struct vduse_dev_response {
316
+    __u32 request_id;
317
+#define VDUSE_REQ_RESULT_OK    0x00
318
+#define VDUSE_REQ_RESULT_FAILED    0x01
319
+    __u32 result;
320
+    __u32 reserved[4];
321
+    union {
322
+        struct vduse_vq_state vq_state;
323
+        __u32 padding[32];
324
+    };
325
+};
326
+
327
+#endif /* _VDUSE_H_ */
328
diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh
329
index XXXXXXX..XXXXXXX 100755
330
--- a/scripts/update-linux-headers.sh
331
+++ b/scripts/update-linux-headers.sh
332
@@ -XXX,XX +XXX,XX @@ done
333
rm -rf "$output/linux-headers/linux"
334
mkdir -p "$output/linux-headers/linux"
335
for header in kvm.h vfio.h vfio_ccw.h vfio_zdev.h vhost.h \
336
- psci.h psp-sev.h userfaultfd.h mman.h; do
337
+ psci.h psp-sev.h userfaultfd.h mman.h vduse.h; do
338
cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux"
339
done
340
40
--
341
--
41
2.20.1
342
2.35.3
42
43
diff view generated by jsdifflib
1
From: Daniel Henrique Barboza <danielhb413@gmail.com>
1
From: Xie Yongji <xieyongji@bytedance.com>
2
2
3
This patch adds a new test file to exercise the case where
3
VDUSE [1] is a linux framework that makes it possible to implement
4
qemu-img fails to complete for the LUKS format when a non-UTF8
4
software-emulated vDPA devices in userspace. This adds a library
5
secret is used.
5
as a subproject to help implementing VDUSE backends in QEMU.
6
6
7
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
7
[1] https://www.kernel.org/doc/html/latest/userspace-api/vduse.html
8
Message-Id: <20200130213907.2830642-5-danielhb413@gmail.com>
8
9
Signed-off-by: Xie Yongji <xieyongji@bytedance.com>
10
Message-Id: <20220523084611.91-6-xieyongji@bytedance.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
13
---
11
tests/qemu-iotests/282 | 67 ++++++++++++++++++++++++++++++++++++++
14
meson_options.txt | 2 +
12
tests/qemu-iotests/282.out | 11 +++++++
15
subprojects/libvduse/include/atomic.h | 1 +
13
tests/qemu-iotests/group | 1 +
16
subprojects/libvduse/include/compiler.h | 1 +
14
3 files changed, 79 insertions(+)
17
subprojects/libvduse/libvduse.h | 235 ++++
15
create mode 100755 tests/qemu-iotests/282
18
subprojects/libvduse/libvduse.c | 1167 +++++++++++++++++++
16
create mode 100644 tests/qemu-iotests/282.out
19
MAINTAINERS | 5 +
20
meson.build | 15 +
21
scripts/meson-buildoptions.sh | 3 +
22
subprojects/libvduse/linux-headers/linux | 1 +
23
subprojects/libvduse/meson.build | 10 +
24
subprojects/libvduse/standard-headers/linux | 1 +
25
11 files changed, 1441 insertions(+)
26
create mode 120000 subprojects/libvduse/include/atomic.h
27
create mode 120000 subprojects/libvduse/include/compiler.h
28
create mode 100644 subprojects/libvduse/libvduse.h
29
create mode 100644 subprojects/libvduse/libvduse.c
30
create mode 120000 subprojects/libvduse/linux-headers/linux
31
create mode 100644 subprojects/libvduse/meson.build
32
create mode 120000 subprojects/libvduse/standard-headers/linux
17
33
18
diff --git a/tests/qemu-iotests/282 b/tests/qemu-iotests/282
34
diff --git a/meson_options.txt b/meson_options.txt
19
new file mode 100755
35
index XXXXXXX..XXXXXXX 100644
36
--- a/meson_options.txt
37
+++ b/meson_options.txt
38
@@ -XXX,XX +XXX,XX @@ option('virtfs', type: 'feature', value: 'auto',
39
description: 'virtio-9p support')
40
option('virtiofsd', type: 'feature', value: 'auto',
41
description: 'build virtiofs daemon (virtiofsd)')
42
+option('libvduse', type: 'feature', value: 'auto',
43
+ description: 'build VDUSE Library')
44
45
option('capstone', type: 'feature', value: 'auto',
46
description: 'Whether and how to find the capstone library')
47
diff --git a/subprojects/libvduse/include/atomic.h b/subprojects/libvduse/include/atomic.h
48
new file mode 120000
20
index XXXXXXX..XXXXXXX
49
index XXXXXXX..XXXXXXX
21
--- /dev/null
50
--- /dev/null
22
+++ b/tests/qemu-iotests/282
51
+++ b/subprojects/libvduse/include/atomic.h
23
@@ -XXX,XX +XXX,XX @@
52
@@ -0,0 +1 @@
24
+#!/usr/bin/env bash
53
+../../../include/qemu/atomic.h
25
+#
54
\ No newline at end of file
26
+# Test qemu-img file cleanup for LUKS when using a non-UTF8 secret
55
diff --git a/subprojects/libvduse/include/compiler.h b/subprojects/libvduse/include/compiler.h
27
+#
56
new file mode 120000
28
+# Copyright (C) 2020, IBM Corporation.
57
index XXXXXXX..XXXXXXX
29
+#
58
--- /dev/null
30
+# This program is free software; you can redistribute it and/or modify
59
+++ b/subprojects/libvduse/include/compiler.h
31
+# it under the terms of the GNU General Public License as published by
60
@@ -0,0 +1 @@
32
+# the Free Software Foundation; either version 2 of the License, or
61
+../../../include/qemu/compiler.h
33
+# (at your option) any later version.
62
\ No newline at end of file
34
+#
63
diff --git a/subprojects/libvduse/libvduse.h b/subprojects/libvduse/libvduse.h
35
+# This program is distributed in the hope that it will be useful,
36
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
37
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38
+# GNU General Public License for more details.
39
+#
40
+# You should have received a copy of the GNU General Public License
41
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
42
+#
43
+
44
+seq=`basename $0`
45
+echo "QA output created by $seq"
46
+
47
+status=1    # failure is the default!
48
+TEST_IMAGE_FILE='vol.img'
49
+
50
+_cleanup()
51
+{
52
+ _cleanup_test_img
53
+ rm non_utf8_secret
54
+ rm -f $TEST_IMAGE_FILE
55
+}
56
+trap "_cleanup; exit \$status" 0 1 2 3 15
57
+
58
+# get standard environment, filters and checks
59
+. ./common.rc
60
+. ./common.filter
61
+
62
+_supported_fmt luks
63
+_supported_proto generic
64
+_unsupported_proto vxhs
65
+
66
+echo "== Create non-UTF8 secret =="
67
+echo -n -e '\x3a\x3c\x3b\xff' > non_utf8_secret
68
+SECRET="secret,id=sec0,file=non_utf8_secret"
69
+
70
+echo "== Throws an error because of invalid UTF-8 secret =="
71
+$QEMU_IMG create -f $IMGFMT --object $SECRET -o "key-secret=sec0" $TEST_IMAGE_FILE 4M
72
+
73
+echo "== Image file should not exist after the error =="
74
+if test -f "$TEST_IMAGE_FILE"; then
75
+ exit 1
76
+fi
77
+
78
+echo "== Create a stub image file and run qemu-img again =="
79
+touch $TEST_IMAGE_FILE
80
+$QEMU_IMG create -f $IMGFMT --object $SECRET -o "key-secret=sec0" $TEST_IMAGE_FILE 4M
81
+
82
+echo "== Pre-existing image file should also be deleted after the error =="
83
+if test -f "$TEST_IMAGE_FILE"; then
84
+ exit 1
85
+fi
86
+
87
+# success, all done
88
+echo "*** done"
89
+rm -f $seq.full
90
+status=0
91
diff --git a/tests/qemu-iotests/282.out b/tests/qemu-iotests/282.out
92
new file mode 100644
64
new file mode 100644
93
index XXXXXXX..XXXXXXX
65
index XXXXXXX..XXXXXXX
94
--- /dev/null
66
--- /dev/null
95
+++ b/tests/qemu-iotests/282.out
67
+++ b/subprojects/libvduse/libvduse.h
96
@@ -XXX,XX +XXX,XX @@
68
@@ -XXX,XX +XXX,XX @@
97
+QA output created by 282
69
+/*
98
+== Create non-UTF8 secret ==
70
+ * VDUSE (vDPA Device in Userspace) library
99
+== Throws an error because of invalid UTF-8 secret ==
71
+ *
100
+qemu-img: vol.img: Data from secret sec0 is not valid UTF-8
72
+ * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved.
101
+Formatting 'vol.img', fmt=luks size=4194304 key-secret=sec0
73
+ *
102
+== Image file should not exist after the error ==
74
+ * Author:
103
+== Create a stub image file and run qemu-img again ==
75
+ * Xie Yongji <xieyongji@bytedance.com>
104
+qemu-img: vol.img: Data from secret sec0 is not valid UTF-8
76
+ *
105
+Formatting 'vol.img', fmt=luks size=4194304 key-secret=sec0
77
+ * This work is licensed under the terms of the GNU GPL, version 2 or
106
+== Pre-existing image file should also be deleted after the error ==
78
+ * later. See the COPYING file in the top-level directory.
107
+ *** done
79
+ */
108
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
80
+
81
+#ifndef LIBVDUSE_H
82
+#define LIBVDUSE_H
83
+
84
+#include <stdint.h>
85
+#include <sys/uio.h>
86
+
87
+#define VIRTQUEUE_MAX_SIZE 1024
88
+
89
+/* VDUSE device structure */
90
+typedef struct VduseDev VduseDev;
91
+
92
+/* Virtqueue structure */
93
+typedef struct VduseVirtq VduseVirtq;
94
+
95
+/* Some operation of VDUSE backend */
96
+typedef struct VduseOps {
97
+ /* Called when virtqueue can be processed */
98
+ void (*enable_queue)(VduseDev *dev, VduseVirtq *vq);
99
+ /* Called when virtqueue processing should be stopped */
100
+ void (*disable_queue)(VduseDev *dev, VduseVirtq *vq);
101
+} VduseOps;
102
+
103
+/* Describing elements of the I/O buffer */
104
+typedef struct VduseVirtqElement {
105
+ /* Descriptor table index */
106
+ unsigned int index;
107
+ /* Number of physically-contiguous device-readable descriptors */
108
+ unsigned int out_num;
109
+ /* Number of physically-contiguous device-writable descriptors */
110
+ unsigned int in_num;
111
+ /* Array to store physically-contiguous device-writable descriptors */
112
+ struct iovec *in_sg;
113
+ /* Array to store physically-contiguous device-readable descriptors */
114
+ struct iovec *out_sg;
115
+} VduseVirtqElement;
116
+
117
+
118
+/**
119
+ * vduse_get_virtio_features:
120
+ *
121
+ * Get supported virtio features
122
+ *
123
+ * Returns: supported feature bits
124
+ */
125
+uint64_t vduse_get_virtio_features(void);
126
+
127
+/**
128
+ * vduse_queue_get_dev:
129
+ * @vq: specified virtqueue
130
+ *
131
+ * Get corresponding VDUSE device from the virtqueue.
132
+ *
133
+ * Returns: a pointer to VDUSE device on success, NULL on failure.
134
+ */
135
+VduseDev *vduse_queue_get_dev(VduseVirtq *vq);
136
+
137
+/**
138
+ * vduse_queue_get_fd:
139
+ * @vq: specified virtqueue
140
+ *
141
+ * Get the kick fd for the virtqueue.
142
+ *
143
+ * Returns: file descriptor on success, -1 on failure.
144
+ */
145
+int vduse_queue_get_fd(VduseVirtq *vq);
146
+
147
+/**
148
+ * vduse_queue_pop:
149
+ * @vq: specified virtqueue
150
+ * @sz: the size of struct to return (must be >= VduseVirtqElement)
151
+ *
152
+ * Pop an element from virtqueue available ring.
153
+ *
154
+ * Returns: a pointer to a structure containing VduseVirtqElement on success,
155
+ * NULL on failure.
156
+ */
157
+void *vduse_queue_pop(VduseVirtq *vq, size_t sz);
158
+
159
+/**
160
+ * vduse_queue_push:
161
+ * @vq: specified virtqueue
162
+ * @elem: pointer to VduseVirtqElement returned by vduse_queue_pop()
163
+ * @len: length in bytes to write
164
+ *
165
+ * Push an element to virtqueue used ring.
166
+ */
167
+void vduse_queue_push(VduseVirtq *vq, const VduseVirtqElement *elem,
168
+ unsigned int len);
169
+/**
170
+ * vduse_queue_notify:
171
+ * @vq: specified virtqueue
172
+ *
173
+ * Request to notify the queue.
174
+ */
175
+void vduse_queue_notify(VduseVirtq *vq);
176
+
177
+/**
178
+ * vduse_dev_get_priv:
179
+ * @dev: VDUSE device
180
+ *
181
+ * Get the private pointer passed to vduse_dev_create().
182
+ *
183
+ * Returns: private pointer on success, NULL on failure.
184
+ */
185
+void *vduse_dev_get_priv(VduseDev *dev);
186
+
187
+/**
188
+ * vduse_dev_get_queue:
189
+ * @dev: VDUSE device
190
+ * @index: virtqueue index
191
+ *
192
+ * Get the specified virtqueue.
193
+ *
194
+ * Returns: a pointer to the virtqueue on success, NULL on failure.
195
+ */
196
+VduseVirtq *vduse_dev_get_queue(VduseDev *dev, int index);
197
+
198
+/**
199
+ * vduse_dev_get_fd:
200
+ * @dev: VDUSE device
201
+ *
202
+ * Get the control message fd for the VDUSE device.
203
+ *
204
+ * Returns: file descriptor on success, -1 on failure.
205
+ */
206
+int vduse_dev_get_fd(VduseDev *dev);
207
+
208
+/**
209
+ * vduse_dev_handler:
210
+ * @dev: VDUSE device
211
+ *
212
+ * Used to process the control message.
213
+ *
214
+ * Returns: file descriptor on success, -errno on failure.
215
+ */
216
+int vduse_dev_handler(VduseDev *dev);
217
+
218
+/**
219
+ * vduse_dev_update_config:
220
+ * @dev: VDUSE device
221
+ * @size: the size to write to configuration space
222
+ * @offset: the offset from the beginning of configuration space
223
+ * @buffer: the buffer used to write from
224
+ *
225
+ * Update device configuration space and inject a config interrupt.
226
+ *
227
+ * Returns: 0 on success, -errno on failure.
228
+ */
229
+int vduse_dev_update_config(VduseDev *dev, uint32_t size,
230
+ uint32_t offset, char *buffer);
231
+
232
+/**
233
+ * vduse_dev_setup_queue:
234
+ * @dev: VDUSE device
235
+ * @index: virtqueue index
236
+ * @max_size: the max size of virtqueue
237
+ *
238
+ * Setup the specified virtqueue.
239
+ *
240
+ * Returns: 0 on success, -errno on failure.
241
+ */
242
+int vduse_dev_setup_queue(VduseDev *dev, int index, int max_size);
243
+
244
+/**
245
+ * vduse_dev_create_by_fd:
246
+ * @fd: passed file descriptor
247
+ * @num_queues: the number of virtqueues
248
+ * @ops: the operation of VDUSE backend
249
+ * @priv: private pointer
250
+ *
251
+ * Create VDUSE device from a passed file descriptor.
252
+ *
253
+ * Returns: pointer to VDUSE device on success, NULL on failure.
254
+ */
255
+VduseDev *vduse_dev_create_by_fd(int fd, uint16_t num_queues,
256
+ const VduseOps *ops, void *priv);
257
+
258
+/**
259
+ * vduse_dev_create_by_name:
260
+ * @name: VDUSE device name
261
+ * @num_queues: the number of virtqueues
262
+ * @ops: the operation of VDUSE backend
263
+ * @priv: private pointer
264
+ *
265
+ * Create VDUSE device on /dev/vduse/$NAME.
266
+ *
267
+ * Returns: pointer to VDUSE device on success, NULL on failure.
268
+ */
269
+VduseDev *vduse_dev_create_by_name(const char *name, uint16_t num_queues,
270
+ const VduseOps *ops, void *priv);
271
+
272
+/**
273
+ * vduse_dev_create:
274
+ * @name: VDUSE device name
275
+ * @device_id: virtio device id
276
+ * @vendor_id: virtio vendor id
277
+ * @features: virtio features
278
+ * @num_queues: the number of virtqueues
279
+ * @config_size: the size of the configuration space
280
+ * @config: the buffer of the configuration space
281
+ * @ops: the operation of VDUSE backend
282
+ * @priv: private pointer
283
+ *
284
+ * Create VDUSE device.
285
+ *
286
+ * Returns: pointer to VDUSE device on success, NULL on failure.
287
+ */
288
+VduseDev *vduse_dev_create(const char *name, uint32_t device_id,
289
+ uint32_t vendor_id, uint64_t features,
290
+ uint16_t num_queues, uint32_t config_size,
291
+ char *config, const VduseOps *ops, void *priv);
292
+
293
+/**
294
+ * vduse_dev_destroy:
295
+ * @dev: VDUSE device
296
+ *
297
+ * Destroy the VDUSE device.
298
+ *
299
+ * Returns: 0 on success, -errno on failure.
300
+ */
301
+int vduse_dev_destroy(VduseDev *dev);
302
+
303
+#endif
304
diff --git a/subprojects/libvduse/libvduse.c b/subprojects/libvduse/libvduse.c
305
new file mode 100644
306
index XXXXXXX..XXXXXXX
307
--- /dev/null
308
+++ b/subprojects/libvduse/libvduse.c
309
@@ -XXX,XX +XXX,XX @@
310
+/*
311
+ * VDUSE (vDPA Device in Userspace) library
312
+ *
313
+ * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved.
314
+ * Portions of codes and concepts borrowed from libvhost-user.c, so:
315
+ * Copyright IBM, Corp. 2007
316
+ * Copyright (c) 2016 Red Hat, Inc.
317
+ *
318
+ * Author:
319
+ * Xie Yongji <xieyongji@bytedance.com>
320
+ * Anthony Liguori <aliguori@us.ibm.com>
321
+ * Marc-André Lureau <mlureau@redhat.com>
322
+ * Victor Kaplansky <victork@redhat.com>
323
+ *
324
+ * This work is licensed under the terms of the GNU GPL, version 2 or
325
+ * later. See the COPYING file in the top-level directory.
326
+ */
327
+
328
+#include <stdlib.h>
329
+#include <stdio.h>
330
+#include <stdbool.h>
331
+#include <stddef.h>
332
+#include <errno.h>
333
+#include <string.h>
334
+#include <assert.h>
335
+#include <endian.h>
336
+#include <unistd.h>
337
+#include <limits.h>
338
+#include <fcntl.h>
339
+
340
+#include <sys/ioctl.h>
341
+#include <sys/eventfd.h>
342
+#include <sys/mman.h>
343
+
344
+#include "include/atomic.h"
345
+#include "linux-headers/linux/virtio_ring.h"
346
+#include "linux-headers/linux/virtio_config.h"
347
+#include "linux-headers/linux/vduse.h"
348
+#include "libvduse.h"
349
+
350
+#define VDUSE_VQ_ALIGN 4096
351
+#define MAX_IOVA_REGIONS 256
352
+
353
+/* Round number down to multiple */
354
+#define ALIGN_DOWN(n, m) ((n) / (m) * (m))
355
+
356
+/* Round number up to multiple */
357
+#define ALIGN_UP(n, m) ALIGN_DOWN((n) + (m) - 1, (m))
358
+
359
+#ifndef unlikely
360
+#define unlikely(x) __builtin_expect(!!(x), 0)
361
+#endif
362
+
363
+typedef struct VduseRing {
364
+ unsigned int num;
365
+ uint64_t desc_addr;
366
+ uint64_t avail_addr;
367
+ uint64_t used_addr;
368
+ struct vring_desc *desc;
369
+ struct vring_avail *avail;
370
+ struct vring_used *used;
371
+} VduseRing;
372
+
373
+struct VduseVirtq {
374
+ VduseRing vring;
375
+ uint16_t last_avail_idx;
376
+ uint16_t shadow_avail_idx;
377
+ uint16_t used_idx;
378
+ uint16_t signalled_used;
379
+ bool signalled_used_valid;
380
+ int index;
381
+ int inuse;
382
+ bool ready;
383
+ int fd;
384
+ VduseDev *dev;
385
+};
386
+
387
+typedef struct VduseIovaRegion {
388
+ uint64_t iova;
389
+ uint64_t size;
390
+ uint64_t mmap_offset;
391
+ uint64_t mmap_addr;
392
+} VduseIovaRegion;
393
+
394
+struct VduseDev {
395
+ VduseVirtq *vqs;
396
+ VduseIovaRegion regions[MAX_IOVA_REGIONS];
397
+ int num_regions;
398
+ char *name;
399
+ uint32_t device_id;
400
+ uint32_t vendor_id;
401
+ uint16_t num_queues;
402
+ uint16_t queue_size;
403
+ uint64_t features;
404
+ const VduseOps *ops;
405
+ int fd;
406
+ int ctrl_fd;
407
+ void *priv;
408
+};
409
+
410
+static inline bool has_feature(uint64_t features, unsigned int fbit)
411
+{
412
+ assert(fbit < 64);
413
+ return !!(features & (1ULL << fbit));
414
+}
415
+
416
+static inline bool vduse_dev_has_feature(VduseDev *dev, unsigned int fbit)
417
+{
418
+ return has_feature(dev->features, fbit);
419
+}
420
+
421
+uint64_t vduse_get_virtio_features(void)
422
+{
423
+ return (1ULL << VIRTIO_F_IOMMU_PLATFORM) |
424
+ (1ULL << VIRTIO_F_VERSION_1) |
425
+ (1ULL << VIRTIO_F_NOTIFY_ON_EMPTY) |
426
+ (1ULL << VIRTIO_RING_F_EVENT_IDX) |
427
+ (1ULL << VIRTIO_RING_F_INDIRECT_DESC);
428
+}
429
+
430
+VduseDev *vduse_queue_get_dev(VduseVirtq *vq)
431
+{
432
+ return vq->dev;
433
+}
434
+
435
+int vduse_queue_get_fd(VduseVirtq *vq)
436
+{
437
+ return vq->fd;
438
+}
439
+
440
+void *vduse_dev_get_priv(VduseDev *dev)
441
+{
442
+ return dev->priv;
443
+}
444
+
445
+VduseVirtq *vduse_dev_get_queue(VduseDev *dev, int index)
446
+{
447
+ return &dev->vqs[index];
448
+}
449
+
450
+int vduse_dev_get_fd(VduseDev *dev)
451
+{
452
+ return dev->fd;
453
+}
454
+
455
+static int vduse_inject_irq(VduseDev *dev, int index)
456
+{
457
+ return ioctl(dev->fd, VDUSE_VQ_INJECT_IRQ, &index);
458
+}
459
+
460
+static void vduse_iova_remove_region(VduseDev *dev, uint64_t start,
461
+ uint64_t last)
462
+{
463
+ int i;
464
+
465
+ if (last == start) {
466
+ return;
467
+ }
468
+
469
+ for (i = 0; i < MAX_IOVA_REGIONS; i++) {
470
+ if (!dev->regions[i].mmap_addr) {
471
+ continue;
472
+ }
473
+
474
+ if (start <= dev->regions[i].iova &&
475
+ last >= (dev->regions[i].iova + dev->regions[i].size - 1)) {
476
+ munmap((void *)dev->regions[i].mmap_addr,
477
+ dev->regions[i].mmap_offset + dev->regions[i].size);
478
+ dev->regions[i].mmap_addr = 0;
479
+ dev->num_regions--;
480
+ }
481
+ }
482
+}
483
+
484
+static int vduse_iova_add_region(VduseDev *dev, int fd,
485
+ uint64_t offset, uint64_t start,
486
+ uint64_t last, int prot)
487
+{
488
+ int i;
489
+ uint64_t size = last - start + 1;
490
+ void *mmap_addr = mmap(0, size + offset, prot, MAP_SHARED, fd, 0);
491
+
492
+ if (mmap_addr == MAP_FAILED) {
493
+ close(fd);
494
+ return -EINVAL;
495
+ }
496
+
497
+ for (i = 0; i < MAX_IOVA_REGIONS; i++) {
498
+ if (!dev->regions[i].mmap_addr) {
499
+ dev->regions[i].mmap_addr = (uint64_t)(uintptr_t)mmap_addr;
500
+ dev->regions[i].mmap_offset = offset;
501
+ dev->regions[i].iova = start;
502
+ dev->regions[i].size = size;
503
+ dev->num_regions++;
504
+ break;
505
+ }
506
+ }
507
+ assert(i < MAX_IOVA_REGIONS);
508
+ close(fd);
509
+
510
+ return 0;
511
+}
512
+
513
+static int perm_to_prot(uint8_t perm)
514
+{
515
+ int prot = 0;
516
+
517
+ switch (perm) {
518
+ case VDUSE_ACCESS_WO:
519
+ prot |= PROT_WRITE;
520
+ break;
521
+ case VDUSE_ACCESS_RO:
522
+ prot |= PROT_READ;
523
+ break;
524
+ case VDUSE_ACCESS_RW:
525
+ prot |= PROT_READ | PROT_WRITE;
526
+ break;
527
+ default:
528
+ break;
529
+ }
530
+
531
+ return prot;
532
+}
533
+
534
+static inline void *iova_to_va(VduseDev *dev, uint64_t *plen, uint64_t iova)
535
+{
536
+ int i, ret;
537
+ struct vduse_iotlb_entry entry;
538
+
539
+ for (i = 0; i < MAX_IOVA_REGIONS; i++) {
540
+ VduseIovaRegion *r = &dev->regions[i];
541
+
542
+ if (!r->mmap_addr) {
543
+ continue;
544
+ }
545
+
546
+ if ((iova >= r->iova) && (iova < (r->iova + r->size))) {
547
+ if ((iova + *plen) > (r->iova + r->size)) {
548
+ *plen = r->iova + r->size - iova;
549
+ }
550
+ return (void *)(uintptr_t)(iova - r->iova +
551
+ r->mmap_addr + r->mmap_offset);
552
+ }
553
+ }
554
+
555
+ entry.start = iova;
556
+ entry.last = iova + 1;
557
+ ret = ioctl(dev->fd, VDUSE_IOTLB_GET_FD, &entry);
558
+ if (ret < 0) {
559
+ return NULL;
560
+ }
561
+
562
+ if (!vduse_iova_add_region(dev, ret, entry.offset, entry.start,
563
+ entry.last, perm_to_prot(entry.perm))) {
564
+ return iova_to_va(dev, plen, iova);
565
+ }
566
+
567
+ return NULL;
568
+}
569
+
570
+static inline uint16_t vring_avail_flags(VduseVirtq *vq)
571
+{
572
+ return le16toh(vq->vring.avail->flags);
573
+}
574
+
575
+static inline uint16_t vring_avail_idx(VduseVirtq *vq)
576
+{
577
+ vq->shadow_avail_idx = le16toh(vq->vring.avail->idx);
578
+
579
+ return vq->shadow_avail_idx;
580
+}
581
+
582
+static inline uint16_t vring_avail_ring(VduseVirtq *vq, int i)
583
+{
584
+ return le16toh(vq->vring.avail->ring[i]);
585
+}
586
+
587
+static inline uint16_t vring_get_used_event(VduseVirtq *vq)
588
+{
589
+ return vring_avail_ring(vq, vq->vring.num);
590
+}
591
+
592
+static bool vduse_queue_get_head(VduseVirtq *vq, unsigned int idx,
593
+ unsigned int *head)
594
+{
595
+ /*
596
+ * Grab the next descriptor number they're advertising, and increment
597
+ * the index we've seen.
598
+ */
599
+ *head = vring_avail_ring(vq, idx % vq->vring.num);
600
+
601
+ /* If their number is silly, that's a fatal mistake. */
602
+ if (*head >= vq->vring.num) {
603
+ fprintf(stderr, "Guest says index %u is available\n", *head);
604
+ return false;
605
+ }
606
+
607
+ return true;
608
+}
609
+
610
+static int
611
+vduse_queue_read_indirect_desc(VduseDev *dev, struct vring_desc *desc,
612
+ uint64_t addr, size_t len)
613
+{
614
+ struct vring_desc *ori_desc;
615
+ uint64_t read_len;
616
+
617
+ if (len > (VIRTQUEUE_MAX_SIZE * sizeof(struct vring_desc))) {
618
+ return -1;
619
+ }
620
+
621
+ if (len == 0) {
622
+ return -1;
623
+ }
624
+
625
+ while (len) {
626
+ read_len = len;
627
+ ori_desc = iova_to_va(dev, &read_len, addr);
628
+ if (!ori_desc) {
629
+ return -1;
630
+ }
631
+
632
+ memcpy(desc, ori_desc, read_len);
633
+ len -= read_len;
634
+ addr += read_len;
635
+ desc += read_len;
636
+ }
637
+
638
+ return 0;
639
+}
640
+
641
+enum {
642
+ VIRTQUEUE_READ_DESC_ERROR = -1,
643
+ VIRTQUEUE_READ_DESC_DONE = 0, /* end of chain */
644
+ VIRTQUEUE_READ_DESC_MORE = 1, /* more buffers in chain */
645
+};
646
+
647
+static int vduse_queue_read_next_desc(struct vring_desc *desc, int i,
648
+ unsigned int max, unsigned int *next)
649
+{
650
+ /* If this descriptor says it doesn't chain, we're done. */
651
+ if (!(le16toh(desc[i].flags) & VRING_DESC_F_NEXT)) {
652
+ return VIRTQUEUE_READ_DESC_DONE;
653
+ }
654
+
655
+ /* Check they're not leading us off end of descriptors. */
656
+ *next = desc[i].next;
657
+ /* Make sure compiler knows to grab that: we don't want it changing! */
658
+ smp_wmb();
659
+
660
+ if (*next >= max) {
661
+ fprintf(stderr, "Desc next is %u\n", *next);
662
+ return VIRTQUEUE_READ_DESC_ERROR;
663
+ }
664
+
665
+ return VIRTQUEUE_READ_DESC_MORE;
666
+}
667
+
668
+/*
669
+ * Fetch avail_idx from VQ memory only when we really need to know if
670
+ * guest has added some buffers.
671
+ */
672
+static bool vduse_queue_empty(VduseVirtq *vq)
673
+{
674
+ if (unlikely(!vq->vring.avail)) {
675
+ return true;
676
+ }
677
+
678
+ if (vq->shadow_avail_idx != vq->last_avail_idx) {
679
+ return false;
680
+ }
681
+
682
+ return vring_avail_idx(vq) == vq->last_avail_idx;
683
+}
684
+
685
+static bool vduse_queue_should_notify(VduseVirtq *vq)
686
+{
687
+ VduseDev *dev = vq->dev;
688
+ uint16_t old, new;
689
+ bool v;
690
+
691
+ /* We need to expose used array entries before checking used event. */
692
+ smp_mb();
693
+
694
+ /* Always notify when queue is empty (when feature acknowledge) */
695
+ if (vduse_dev_has_feature(dev, VIRTIO_F_NOTIFY_ON_EMPTY) &&
696
+ !vq->inuse && vduse_queue_empty(vq)) {
697
+ return true;
698
+ }
699
+
700
+ if (!vduse_dev_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) {
701
+ return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT);
702
+ }
703
+
704
+ v = vq->signalled_used_valid;
705
+ vq->signalled_used_valid = true;
706
+ old = vq->signalled_used;
707
+ new = vq->signalled_used = vq->used_idx;
708
+ return !v || vring_need_event(vring_get_used_event(vq), new, old);
709
+}
710
+
711
+void vduse_queue_notify(VduseVirtq *vq)
712
+{
713
+ VduseDev *dev = vq->dev;
714
+
715
+ if (unlikely(!vq->vring.avail)) {
716
+ return;
717
+ }
718
+
719
+ if (!vduse_queue_should_notify(vq)) {
720
+ return;
721
+ }
722
+
723
+ if (vduse_inject_irq(dev, vq->index) < 0) {
724
+ fprintf(stderr, "Error inject irq for vq %d: %s\n",
725
+ vq->index, strerror(errno));
726
+ }
727
+}
728
+
729
+static inline void vring_used_flags_set_bit(VduseVirtq *vq, int mask)
730
+{
731
+ uint16_t *flags;
732
+
733
+ flags = (uint16_t *)((char*)vq->vring.used +
734
+ offsetof(struct vring_used, flags));
735
+ *flags = htole16(le16toh(*flags) | mask);
736
+}
737
+
738
+static inline void vring_used_flags_unset_bit(VduseVirtq *vq, int mask)
739
+{
740
+ uint16_t *flags;
741
+
742
+ flags = (uint16_t *)((char*)vq->vring.used +
743
+ offsetof(struct vring_used, flags));
744
+ *flags = htole16(le16toh(*flags) & ~mask);
745
+}
746
+
747
+static inline void vring_set_avail_event(VduseVirtq *vq, uint16_t val)
748
+{
749
+ *((uint16_t *)&vq->vring.used->ring[vq->vring.num]) = htole16(val);
750
+}
751
+
752
+static bool vduse_queue_map_single_desc(VduseVirtq *vq, unsigned int *p_num_sg,
753
+ struct iovec *iov, unsigned int max_num_sg,
754
+ bool is_write, uint64_t pa, size_t sz)
755
+{
756
+ unsigned num_sg = *p_num_sg;
757
+ VduseDev *dev = vq->dev;
758
+
759
+ assert(num_sg <= max_num_sg);
760
+
761
+ if (!sz) {
762
+ fprintf(stderr, "virtio: zero sized buffers are not allowed\n");
763
+ return false;
764
+ }
765
+
766
+ while (sz) {
767
+ uint64_t len = sz;
768
+
769
+ if (num_sg == max_num_sg) {
770
+ fprintf(stderr,
771
+ "virtio: too many descriptors in indirect table\n");
772
+ return false;
773
+ }
774
+
775
+ iov[num_sg].iov_base = iova_to_va(dev, &len, pa);
776
+ if (iov[num_sg].iov_base == NULL) {
777
+ fprintf(stderr, "virtio: invalid address for buffers\n");
778
+ return false;
779
+ }
780
+ iov[num_sg++].iov_len = len;
781
+ sz -= len;
782
+ pa += len;
783
+ }
784
+
785
+ *p_num_sg = num_sg;
786
+ return true;
787
+}
788
+
789
+static void *vduse_queue_alloc_element(size_t sz, unsigned out_num,
790
+ unsigned in_num)
791
+{
792
+ VduseVirtqElement *elem;
793
+ size_t in_sg_ofs = ALIGN_UP(sz, __alignof__(elem->in_sg[0]));
794
+ size_t out_sg_ofs = in_sg_ofs + in_num * sizeof(elem->in_sg[0]);
795
+ size_t out_sg_end = out_sg_ofs + out_num * sizeof(elem->out_sg[0]);
796
+
797
+ assert(sz >= sizeof(VduseVirtqElement));
798
+ elem = malloc(out_sg_end);
799
+ if (!elem) {
800
+ return NULL;
801
+ }
802
+ elem->out_num = out_num;
803
+ elem->in_num = in_num;
804
+ elem->in_sg = (void *)elem + in_sg_ofs;
805
+ elem->out_sg = (void *)elem + out_sg_ofs;
806
+ return elem;
807
+}
808
+
809
+static void *vduse_queue_map_desc(VduseVirtq *vq, unsigned int idx, size_t sz)
810
+{
811
+ struct vring_desc *desc = vq->vring.desc;
812
+ VduseDev *dev = vq->dev;
813
+ uint64_t desc_addr, read_len;
814
+ unsigned int desc_len;
815
+ unsigned int max = vq->vring.num;
816
+ unsigned int i = idx;
817
+ VduseVirtqElement *elem;
818
+ struct iovec iov[VIRTQUEUE_MAX_SIZE];
819
+ struct vring_desc desc_buf[VIRTQUEUE_MAX_SIZE];
820
+ unsigned int out_num = 0, in_num = 0;
821
+ int rc;
822
+
823
+ if (le16toh(desc[i].flags) & VRING_DESC_F_INDIRECT) {
824
+ if (le32toh(desc[i].len) % sizeof(struct vring_desc)) {
825
+ fprintf(stderr, "Invalid size for indirect buffer table\n");
826
+ return NULL;
827
+ }
828
+
829
+ /* loop over the indirect descriptor table */
830
+ desc_addr = le64toh(desc[i].addr);
831
+ desc_len = le32toh(desc[i].len);
832
+ max = desc_len / sizeof(struct vring_desc);
833
+ read_len = desc_len;
834
+ desc = iova_to_va(dev, &read_len, desc_addr);
835
+ if (unlikely(desc && read_len != desc_len)) {
836
+ /* Failed to use zero copy */
837
+ desc = NULL;
838
+ if (!vduse_queue_read_indirect_desc(dev, desc_buf,
839
+ desc_addr,
840
+ desc_len)) {
841
+ desc = desc_buf;
842
+ }
843
+ }
844
+ if (!desc) {
845
+ fprintf(stderr, "Invalid indirect buffer table\n");
846
+ return NULL;
847
+ }
848
+ i = 0;
849
+ }
850
+
851
+ /* Collect all the descriptors */
852
+ do {
853
+ if (le16toh(desc[i].flags) & VRING_DESC_F_WRITE) {
854
+ if (!vduse_queue_map_single_desc(vq, &in_num, iov + out_num,
855
+ VIRTQUEUE_MAX_SIZE - out_num,
856
+ true, le64toh(desc[i].addr),
857
+ le32toh(desc[i].len))) {
858
+ return NULL;
859
+ }
860
+ } else {
861
+ if (in_num) {
862
+ fprintf(stderr, "Incorrect order for descriptors\n");
863
+ return NULL;
864
+ }
865
+ if (!vduse_queue_map_single_desc(vq, &out_num, iov,
866
+ VIRTQUEUE_MAX_SIZE, false,
867
+ le64toh(desc[i].addr),
868
+ le32toh(desc[i].len))) {
869
+ return NULL;
870
+ }
871
+ }
872
+
873
+ /* If we've got too many, that implies a descriptor loop. */
874
+ if ((in_num + out_num) > max) {
875
+ fprintf(stderr, "Looped descriptor\n");
876
+ return NULL;
877
+ }
878
+ rc = vduse_queue_read_next_desc(desc, i, max, &i);
879
+ } while (rc == VIRTQUEUE_READ_DESC_MORE);
880
+
881
+ if (rc == VIRTQUEUE_READ_DESC_ERROR) {
882
+ fprintf(stderr, "read descriptor error\n");
883
+ return NULL;
884
+ }
885
+
886
+ /* Now copy what we have collected and mapped */
887
+ elem = vduse_queue_alloc_element(sz, out_num, in_num);
888
+ if (!elem) {
889
+ fprintf(stderr, "read descriptor error\n");
890
+ return NULL;
891
+ }
892
+ elem->index = idx;
893
+ for (i = 0; i < out_num; i++) {
894
+ elem->out_sg[i] = iov[i];
895
+ }
896
+ for (i = 0; i < in_num; i++) {
897
+ elem->in_sg[i] = iov[out_num + i];
898
+ }
899
+
900
+ return elem;
901
+}
902
+
903
+void *vduse_queue_pop(VduseVirtq *vq, size_t sz)
904
+{
905
+ unsigned int head;
906
+ VduseVirtqElement *elem;
907
+ VduseDev *dev = vq->dev;
908
+
909
+ if (unlikely(!vq->vring.avail)) {
910
+ return NULL;
911
+ }
912
+
913
+ if (vduse_queue_empty(vq)) {
914
+ return NULL;
915
+ }
916
+ /* Needed after virtio_queue_empty() */
917
+ smp_rmb();
918
+
919
+ if (vq->inuse >= vq->vring.num) {
920
+ fprintf(stderr, "Virtqueue size exceeded: %d\n", vq->inuse);
921
+ return NULL;
922
+ }
923
+
924
+ if (!vduse_queue_get_head(vq, vq->last_avail_idx++, &head)) {
925
+ return NULL;
926
+ }
927
+
928
+ if (vduse_dev_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) {
929
+ vring_set_avail_event(vq, vq->last_avail_idx);
930
+ }
931
+
932
+ elem = vduse_queue_map_desc(vq, head, sz);
933
+
934
+ if (!elem) {
935
+ return NULL;
936
+ }
937
+
938
+ vq->inuse++;
939
+
940
+ return elem;
941
+}
942
+
943
+static inline void vring_used_write(VduseVirtq *vq,
944
+ struct vring_used_elem *uelem, int i)
945
+{
946
+ struct vring_used *used = vq->vring.used;
947
+
948
+ used->ring[i] = *uelem;
949
+}
950
+
951
+static void vduse_queue_fill(VduseVirtq *vq, const VduseVirtqElement *elem,
952
+ unsigned int len, unsigned int idx)
953
+{
954
+ struct vring_used_elem uelem;
955
+
956
+ if (unlikely(!vq->vring.used)) {
957
+ return;
958
+ }
959
+
960
+ idx = (idx + vq->used_idx) % vq->vring.num;
961
+
962
+ uelem.id = htole32(elem->index);
963
+ uelem.len = htole32(len);
964
+ vring_used_write(vq, &uelem, idx);
965
+}
966
+
967
+static inline void vring_used_idx_set(VduseVirtq *vq, uint16_t val)
968
+{
969
+ vq->vring.used->idx = htole16(val);
970
+ vq->used_idx = val;
971
+}
972
+
973
+static void vduse_queue_flush(VduseVirtq *vq, unsigned int count)
974
+{
975
+ uint16_t old, new;
976
+
977
+ if (unlikely(!vq->vring.used)) {
978
+ return;
979
+ }
980
+
981
+ /* Make sure buffer is written before we update index. */
982
+ smp_wmb();
983
+
984
+ old = vq->used_idx;
985
+ new = old + count;
986
+ vring_used_idx_set(vq, new);
987
+ vq->inuse -= count;
988
+ if (unlikely((int16_t)(new - vq->signalled_used) < (uint16_t)(new - old))) {
989
+ vq->signalled_used_valid = false;
990
+ }
991
+}
992
+
993
+void vduse_queue_push(VduseVirtq *vq, const VduseVirtqElement *elem,
994
+ unsigned int len)
995
+{
996
+ vduse_queue_fill(vq, elem, len, 0);
997
+ vduse_queue_flush(vq, 1);
998
+}
999
+
1000
+static int vduse_queue_update_vring(VduseVirtq *vq, uint64_t desc_addr,
1001
+ uint64_t avail_addr, uint64_t used_addr)
1002
+{
1003
+ struct VduseDev *dev = vq->dev;
1004
+ uint64_t len;
1005
+
1006
+ len = sizeof(struct vring_desc);
1007
+ vq->vring.desc = iova_to_va(dev, &len, desc_addr);
1008
+ if (len != sizeof(struct vring_desc)) {
1009
+ return -EINVAL;
1010
+ }
1011
+
1012
+ len = sizeof(struct vring_avail);
1013
+ vq->vring.avail = iova_to_va(dev, &len, avail_addr);
1014
+ if (len != sizeof(struct vring_avail)) {
1015
+ return -EINVAL;
1016
+ }
1017
+
1018
+ len = sizeof(struct vring_used);
1019
+ vq->vring.used = iova_to_va(dev, &len, used_addr);
1020
+ if (len != sizeof(struct vring_used)) {
1021
+ return -EINVAL;
1022
+ }
1023
+
1024
+ if (!vq->vring.desc || !vq->vring.avail || !vq->vring.used) {
1025
+ fprintf(stderr, "Failed to get vq[%d] iova mapping\n", vq->index);
1026
+ return -EINVAL;
1027
+ }
1028
+
1029
+ return 0;
1030
+}
1031
+
1032
+static void vduse_queue_enable(VduseVirtq *vq)
1033
+{
1034
+ struct VduseDev *dev = vq->dev;
1035
+ struct vduse_vq_info vq_info;
1036
+ struct vduse_vq_eventfd vq_eventfd;
1037
+ int fd;
1038
+
1039
+ vq_info.index = vq->index;
1040
+ if (ioctl(dev->fd, VDUSE_VQ_GET_INFO, &vq_info)) {
1041
+ fprintf(stderr, "Failed to get vq[%d] info: %s\n",
1042
+ vq->index, strerror(errno));
1043
+ return;
1044
+ }
1045
+
1046
+ if (!vq_info.ready) {
1047
+ return;
1048
+ }
1049
+
1050
+ vq->vring.num = vq_info.num;
1051
+ vq->vring.desc_addr = vq_info.desc_addr;
1052
+ vq->vring.avail_addr = vq_info.driver_addr;
1053
+ vq->vring.used_addr = vq_info.device_addr;
1054
+
1055
+ if (vduse_queue_update_vring(vq, vq_info.desc_addr,
1056
+ vq_info.driver_addr, vq_info.device_addr)) {
1057
+ fprintf(stderr, "Failed to update vring for vq[%d]\n", vq->index);
1058
+ return;
1059
+ }
1060
+
1061
+ fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
1062
+ if (fd < 0) {
1063
+ fprintf(stderr, "Failed to init eventfd for vq[%d]\n", vq->index);
1064
+ return;
1065
+ }
1066
+
1067
+ vq_eventfd.index = vq->index;
1068
+ vq_eventfd.fd = fd;
1069
+ if (ioctl(dev->fd, VDUSE_VQ_SETUP_KICKFD, &vq_eventfd)) {
1070
+ fprintf(stderr, "Failed to setup kick fd for vq[%d]\n", vq->index);
1071
+ close(fd);
1072
+ return;
1073
+ }
1074
+
1075
+ vq->fd = fd;
1076
+ vq->shadow_avail_idx = vq->last_avail_idx = vq_info.split.avail_index;
1077
+ vq->inuse = 0;
1078
+ vq->used_idx = 0;
1079
+ vq->signalled_used_valid = false;
1080
+ vq->ready = true;
1081
+
1082
+ dev->ops->enable_queue(dev, vq);
1083
+}
1084
+
1085
+static void vduse_queue_disable(VduseVirtq *vq)
1086
+{
1087
+ struct VduseDev *dev = vq->dev;
1088
+ struct vduse_vq_eventfd eventfd;
1089
+
1090
+ if (!vq->ready) {
1091
+ return;
1092
+ }
1093
+
1094
+ dev->ops->disable_queue(dev, vq);
1095
+
1096
+ eventfd.index = vq->index;
1097
+ eventfd.fd = VDUSE_EVENTFD_DEASSIGN;
1098
+ ioctl(dev->fd, VDUSE_VQ_SETUP_KICKFD, &eventfd);
1099
+ close(vq->fd);
1100
+
1101
+ assert(vq->inuse == 0);
1102
+
1103
+ vq->vring.num = 0;
1104
+ vq->vring.desc_addr = 0;
1105
+ vq->vring.avail_addr = 0;
1106
+ vq->vring.used_addr = 0;
1107
+ vq->vring.desc = 0;
1108
+ vq->vring.avail = 0;
1109
+ vq->vring.used = 0;
1110
+ vq->ready = false;
1111
+ vq->fd = -1;
1112
+}
1113
+
1114
+static void vduse_dev_start_dataplane(VduseDev *dev)
1115
+{
1116
+ int i;
1117
+
1118
+ if (ioctl(dev->fd, VDUSE_DEV_GET_FEATURES, &dev->features)) {
1119
+ fprintf(stderr, "Failed to get features: %s\n", strerror(errno));
1120
+ return;
1121
+ }
1122
+ assert(vduse_dev_has_feature(dev, VIRTIO_F_VERSION_1));
1123
+
1124
+ for (i = 0; i < dev->num_queues; i++) {
1125
+ vduse_queue_enable(&dev->vqs[i]);
1126
+ }
1127
+}
1128
+
1129
+static void vduse_dev_stop_dataplane(VduseDev *dev)
1130
+{
1131
+ int i;
1132
+
1133
+ for (i = 0; i < dev->num_queues; i++) {
1134
+ vduse_queue_disable(&dev->vqs[i]);
1135
+ }
1136
+ dev->features = 0;
1137
+ vduse_iova_remove_region(dev, 0, ULONG_MAX);
1138
+}
1139
+
1140
+int vduse_dev_handler(VduseDev *dev)
1141
+{
1142
+ struct vduse_dev_request req;
1143
+ struct vduse_dev_response resp = { 0 };
1144
+ VduseVirtq *vq;
1145
+ int i, ret;
1146
+
1147
+ ret = read(dev->fd, &req, sizeof(req));
1148
+ if (ret != sizeof(req)) {
1149
+ fprintf(stderr, "Read request error [%d]: %s\n",
1150
+ ret, strerror(errno));
1151
+ return -errno;
1152
+ }
1153
+ resp.request_id = req.request_id;
1154
+
1155
+ switch (req.type) {
1156
+ case VDUSE_GET_VQ_STATE:
1157
+ vq = &dev->vqs[req.vq_state.index];
1158
+ resp.vq_state.split.avail_index = vq->last_avail_idx;
1159
+ resp.result = VDUSE_REQ_RESULT_OK;
1160
+ break;
1161
+ case VDUSE_SET_STATUS:
1162
+ if (req.s.status & VIRTIO_CONFIG_S_DRIVER_OK) {
1163
+ vduse_dev_start_dataplane(dev);
1164
+ } else if (req.s.status == 0) {
1165
+ vduse_dev_stop_dataplane(dev);
1166
+ }
1167
+ resp.result = VDUSE_REQ_RESULT_OK;
1168
+ break;
1169
+ case VDUSE_UPDATE_IOTLB:
1170
+ /* The iova will be updated by iova_to_va() later, so just remove it */
1171
+ vduse_iova_remove_region(dev, req.iova.start, req.iova.last);
1172
+ for (i = 0; i < dev->num_queues; i++) {
1173
+ VduseVirtq *vq = &dev->vqs[i];
1174
+ if (vq->ready) {
1175
+ if (vduse_queue_update_vring(vq, vq->vring.desc_addr,
1176
+ vq->vring.avail_addr,
1177
+ vq->vring.used_addr)) {
1178
+ fprintf(stderr, "Failed to update vring for vq[%d]\n",
1179
+ vq->index);
1180
+ }
1181
+ }
1182
+ }
1183
+ resp.result = VDUSE_REQ_RESULT_OK;
1184
+ break;
1185
+ default:
1186
+ resp.result = VDUSE_REQ_RESULT_FAILED;
1187
+ break;
1188
+ }
1189
+
1190
+ ret = write(dev->fd, &resp, sizeof(resp));
1191
+ if (ret != sizeof(resp)) {
1192
+ fprintf(stderr, "Write request %d error [%d]: %s\n",
1193
+ req.type, ret, strerror(errno));
1194
+ return -errno;
1195
+ }
1196
+ return 0;
1197
+}
1198
+
1199
+int vduse_dev_update_config(VduseDev *dev, uint32_t size,
1200
+ uint32_t offset, char *buffer)
1201
+{
1202
+ int ret;
1203
+ struct vduse_config_data *data;
1204
+
1205
+ data = malloc(offsetof(struct vduse_config_data, buffer) + size);
1206
+ if (!data) {
1207
+ return -ENOMEM;
1208
+ }
1209
+
1210
+ data->offset = offset;
1211
+ data->length = size;
1212
+ memcpy(data->buffer, buffer, size);
1213
+
1214
+ ret = ioctl(dev->fd, VDUSE_DEV_SET_CONFIG, data);
1215
+ free(data);
1216
+
1217
+ if (ret) {
1218
+ return -errno;
1219
+ }
1220
+
1221
+ if (ioctl(dev->fd, VDUSE_DEV_INJECT_CONFIG_IRQ)) {
1222
+ return -errno;
1223
+ }
1224
+
1225
+ return 0;
1226
+}
1227
+
1228
+int vduse_dev_setup_queue(VduseDev *dev, int index, int max_size)
1229
+{
1230
+ VduseVirtq *vq = &dev->vqs[index];
1231
+ struct vduse_vq_config vq_config = { 0 };
1232
+
1233
+ if (max_size > VIRTQUEUE_MAX_SIZE) {
1234
+ return -EINVAL;
1235
+ }
1236
+
1237
+ vq_config.index = vq->index;
1238
+ vq_config.max_size = max_size;
1239
+
1240
+ if (ioctl(dev->fd, VDUSE_VQ_SETUP, &vq_config)) {
1241
+ return -errno;
1242
+ }
1243
+
1244
+ return 0;
1245
+}
1246
+
1247
+static int vduse_dev_init_vqs(VduseDev *dev, uint16_t num_queues)
1248
+{
1249
+ VduseVirtq *vqs;
1250
+ int i;
1251
+
1252
+ vqs = calloc(sizeof(VduseVirtq), num_queues);
1253
+ if (!vqs) {
1254
+ return -ENOMEM;
1255
+ }
1256
+
1257
+ for (i = 0; i < num_queues; i++) {
1258
+ vqs[i].index = i;
1259
+ vqs[i].dev = dev;
1260
+ vqs[i].fd = -1;
1261
+ }
1262
+ dev->vqs = vqs;
1263
+
1264
+ return 0;
1265
+}
1266
+
1267
+static int vduse_dev_init(VduseDev *dev, const char *name,
1268
+ uint16_t num_queues, const VduseOps *ops,
1269
+ void *priv)
1270
+{
1271
+ char *dev_path, *dev_name;
1272
+ int ret, fd;
1273
+
1274
+ dev_path = malloc(strlen(name) + strlen("/dev/vduse/") + 1);
1275
+ if (!dev_path) {
1276
+ return -ENOMEM;
1277
+ }
1278
+ sprintf(dev_path, "/dev/vduse/%s", name);
1279
+
1280
+ fd = open(dev_path, O_RDWR);
1281
+ free(dev_path);
1282
+ if (fd < 0) {
1283
+ fprintf(stderr, "Failed to open vduse dev %s: %s\n",
1284
+ name, strerror(errno));
1285
+ return -errno;
1286
+ }
1287
+
1288
+ dev_name = strdup(name);
1289
+ if (!dev_name) {
1290
+ close(fd);
1291
+ return -ENOMEM;
1292
+ }
1293
+
1294
+ ret = vduse_dev_init_vqs(dev, num_queues);
1295
+ if (ret) {
1296
+ free(dev_name);
1297
+ close(fd);
1298
+ return ret;
1299
+ }
1300
+
1301
+ dev->name = dev_name;
1302
+ dev->num_queues = num_queues;
1303
+ dev->fd = fd;
1304
+ dev->ops = ops;
1305
+ dev->priv = priv;
1306
+
1307
+ return 0;
1308
+}
1309
+
1310
+static inline bool vduse_name_is_valid(const char *name)
1311
+{
1312
+ return strlen(name) >= VDUSE_NAME_MAX || strstr(name, "..");
1313
+}
1314
+
1315
+VduseDev *vduse_dev_create_by_fd(int fd, uint16_t num_queues,
1316
+ const VduseOps *ops, void *priv)
1317
+{
1318
+ VduseDev *dev;
1319
+ int ret;
1320
+
1321
+ if (!ops || !ops->enable_queue || !ops->disable_queue) {
1322
+ fprintf(stderr, "Invalid parameter for vduse\n");
1323
+ return NULL;
1324
+ }
1325
+
1326
+ dev = calloc(sizeof(VduseDev), 1);
1327
+ if (!dev) {
1328
+ fprintf(stderr, "Failed to allocate vduse device\n");
1329
+ return NULL;
1330
+ }
1331
+
1332
+ ret = vduse_dev_init_vqs(dev, num_queues);
1333
+ if (ret) {
1334
+ fprintf(stderr, "Failed to init vqs\n");
1335
+ free(dev);
1336
+ return NULL;
1337
+ }
1338
+
1339
+ dev->num_queues = num_queues;
1340
+ dev->fd = fd;
1341
+ dev->ops = ops;
1342
+ dev->priv = priv;
1343
+
1344
+ return dev;
1345
+}
1346
+
1347
+VduseDev *vduse_dev_create_by_name(const char *name, uint16_t num_queues,
1348
+ const VduseOps *ops, void *priv)
1349
+{
1350
+ VduseDev *dev;
1351
+ int ret;
1352
+
1353
+ if (!name || vduse_name_is_valid(name) || !ops ||
1354
+ !ops->enable_queue || !ops->disable_queue) {
1355
+ fprintf(stderr, "Invalid parameter for vduse\n");
1356
+ return NULL;
1357
+ }
1358
+
1359
+ dev = calloc(sizeof(VduseDev), 1);
1360
+ if (!dev) {
1361
+ fprintf(stderr, "Failed to allocate vduse device\n");
1362
+ return NULL;
1363
+ }
1364
+
1365
+ ret = vduse_dev_init(dev, name, num_queues, ops, priv);
1366
+ if (ret < 0) {
1367
+ fprintf(stderr, "Failed to init vduse device %s: %s\n",
1368
+ name, strerror(ret));
1369
+ free(dev);
1370
+ return NULL;
1371
+ }
1372
+
1373
+ return dev;
1374
+}
1375
+
1376
+VduseDev *vduse_dev_create(const char *name, uint32_t device_id,
1377
+ uint32_t vendor_id, uint64_t features,
1378
+ uint16_t num_queues, uint32_t config_size,
1379
+ char *config, const VduseOps *ops, void *priv)
1380
+{
1381
+ VduseDev *dev;
1382
+ int ret, ctrl_fd;
1383
+ uint64_t version;
1384
+ struct vduse_dev_config *dev_config;
1385
+ size_t size = offsetof(struct vduse_dev_config, config);
1386
+
1387
+ if (!name || vduse_name_is_valid(name) ||
1388
+ !has_feature(features, VIRTIO_F_VERSION_1) || !config ||
1389
+ !config_size || !ops || !ops->enable_queue || !ops->disable_queue) {
1390
+ fprintf(stderr, "Invalid parameter for vduse\n");
1391
+ return NULL;
1392
+ }
1393
+
1394
+ dev = calloc(sizeof(VduseDev), 1);
1395
+ if (!dev) {
1396
+ fprintf(stderr, "Failed to allocate vduse device\n");
1397
+ return NULL;
1398
+ }
1399
+
1400
+ ctrl_fd = open("/dev/vduse/control", O_RDWR);
1401
+ if (ctrl_fd < 0) {
1402
+ fprintf(stderr, "Failed to open /dev/vduse/control: %s\n",
1403
+ strerror(errno));
1404
+ goto err_ctrl;
1405
+ }
1406
+
1407
+ version = VDUSE_API_VERSION;
1408
+ if (ioctl(ctrl_fd, VDUSE_SET_API_VERSION, &version)) {
1409
+ fprintf(stderr, "Failed to set api version %lu: %s\n",
1410
+ version, strerror(errno));
1411
+ goto err_dev;
1412
+ }
1413
+
1414
+ dev_config = calloc(size + config_size, 1);
1415
+ if (!dev_config) {
1416
+ fprintf(stderr, "Failed to allocate config space\n");
1417
+ goto err_dev;
1418
+ }
1419
+
1420
+ strcpy(dev_config->name, name);
1421
+ dev_config->device_id = device_id;
1422
+ dev_config->vendor_id = vendor_id;
1423
+ dev_config->features = features;
1424
+ dev_config->vq_num = num_queues;
1425
+ dev_config->vq_align = VDUSE_VQ_ALIGN;
1426
+ dev_config->config_size = config_size;
1427
+ memcpy(dev_config->config, config, config_size);
1428
+
1429
+ ret = ioctl(ctrl_fd, VDUSE_CREATE_DEV, dev_config);
1430
+ free(dev_config);
1431
+ if (ret < 0) {
1432
+ fprintf(stderr, "Failed to create vduse device %s: %s\n",
1433
+ name, strerror(errno));
1434
+ goto err_dev;
1435
+ }
1436
+ dev->ctrl_fd = ctrl_fd;
1437
+
1438
+ ret = vduse_dev_init(dev, name, num_queues, ops, priv);
1439
+ if (ret < 0) {
1440
+ fprintf(stderr, "Failed to init vduse device %s: %s\n",
1441
+ name, strerror(ret));
1442
+ goto err;
1443
+ }
1444
+
1445
+ return dev;
1446
+err:
1447
+ ioctl(ctrl_fd, VDUSE_DESTROY_DEV, name);
1448
+err_dev:
1449
+ close(ctrl_fd);
1450
+err_ctrl:
1451
+ free(dev);
1452
+
1453
+ return NULL;
1454
+}
1455
+
1456
+int vduse_dev_destroy(VduseDev *dev)
1457
+{
1458
+ int ret = 0;
1459
+
1460
+ free(dev->vqs);
1461
+ if (dev->fd > 0) {
1462
+ close(dev->fd);
1463
+ dev->fd = -1;
1464
+ }
1465
+ if (dev->ctrl_fd > 0) {
1466
+ if (ioctl(dev->ctrl_fd, VDUSE_DESTROY_DEV, dev->name)) {
1467
+ ret = -errno;
1468
+ }
1469
+ close(dev->ctrl_fd);
1470
+ dev->ctrl_fd = -1;
1471
+ }
1472
+ free(dev->name);
1473
+ free(dev);
1474
+
1475
+ return ret;
1476
+}
1477
diff --git a/MAINTAINERS b/MAINTAINERS
109
index XXXXXXX..XXXXXXX 100644
1478
index XXXXXXX..XXXXXXX 100644
110
--- a/tests/qemu-iotests/group
1479
--- a/MAINTAINERS
111
+++ b/tests/qemu-iotests/group
1480
+++ b/MAINTAINERS
1481
@@ -XXX,XX +XXX,XX @@ L: qemu-block@nongnu.org
1482
S: Supported
1483
F: block/export/fuse.c
1484
1485
+VDUSE library
1486
+M: Xie Yongji <xieyongji@bytedance.com>
1487
+S: Maintained
1488
+F: subprojects/libvduse/
1489
+
1490
Replication
1491
M: Wen Congyang <wencongyang2@huawei.com>
1492
M: Xie Changlong <xiechanglong.d@gmail.com>
1493
diff --git a/meson.build b/meson.build
1494
index XXXXXXX..XXXXXXX 100644
1495
--- a/meson.build
1496
+++ b/meson.build
1497
@@ -XXX,XX +XXX,XX @@ if get_option('fuse_lseek').allowed()
1498
endif
1499
endif
1500
1501
+have_libvduse = (targetos == 'linux')
1502
+if get_option('libvduse').enabled()
1503
+ if targetos != 'linux'
1504
+ error('libvduse requires linux')
1505
+ endif
1506
+elif get_option('libvduse').disabled()
1507
+ have_libvduse = false
1508
+endif
1509
+
1510
+libvduse = not_found
1511
+if have_libvduse
1512
+ libvduse_proj = subproject('libvduse')
1513
+ libvduse = libvduse_proj.get_variable('libvduse_dep')
1514
+endif
1515
+
1516
# libbpf
1517
libbpf = dependency('libbpf', required: get_option('bpf'), method: 'pkg-config')
1518
if libbpf.found() and not cc.links('''
1519
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
1520
index XXXXXXX..XXXXXXX 100644
1521
--- a/scripts/meson-buildoptions.sh
1522
+++ b/scripts/meson-buildoptions.sh
1523
@@ -XXX,XX +XXX,XX @@ meson_options_help() {
1524
printf "%s\n" ' libssh ssh block device support'
1525
printf "%s\n" ' libudev Use libudev to enumerate host devices'
1526
printf "%s\n" ' libusb libusb support for USB passthrough'
1527
+ printf "%s\n" ' libvduse build VDUSE Library'
1528
printf "%s\n" ' linux-aio Linux AIO support'
1529
printf "%s\n" ' linux-io-uring Linux io_uring support'
1530
printf "%s\n" ' live-block-migration'
1531
@@ -XXX,XX +XXX,XX @@ _meson_option_parse() {
1532
--disable-libudev) printf "%s" -Dlibudev=disabled ;;
1533
--enable-libusb) printf "%s" -Dlibusb=enabled ;;
1534
--disable-libusb) printf "%s" -Dlibusb=disabled ;;
1535
+ --enable-libvduse) printf "%s" -Dlibvduse=enabled ;;
1536
+ --disable-libvduse) printf "%s" -Dlibvduse=disabled ;;
1537
--enable-linux-aio) printf "%s" -Dlinux_aio=enabled ;;
1538
--disable-linux-aio) printf "%s" -Dlinux_aio=disabled ;;
1539
--enable-linux-io-uring) printf "%s" -Dlinux_io_uring=enabled ;;
1540
diff --git a/subprojects/libvduse/linux-headers/linux b/subprojects/libvduse/linux-headers/linux
1541
new file mode 120000
1542
index XXXXXXX..XXXXXXX
1543
--- /dev/null
1544
+++ b/subprojects/libvduse/linux-headers/linux
1545
@@ -0,0 +1 @@
1546
+../../../linux-headers/linux/
1547
\ No newline at end of file
1548
diff --git a/subprojects/libvduse/meson.build b/subprojects/libvduse/meson.build
1549
new file mode 100644
1550
index XXXXXXX..XXXXXXX
1551
--- /dev/null
1552
+++ b/subprojects/libvduse/meson.build
112
@@ -XXX,XX +XXX,XX @@
1553
@@ -XXX,XX +XXX,XX @@
113
279 rw backing quick
1554
+project('libvduse', 'c',
114
280 rw migration quick
1555
+ license: 'GPL-2.0-or-later',
115
281 rw quick
1556
+ default_options: ['c_std=gnu99'])
116
+282 rw img quick
1557
+
117
283 auto quick
1558
+libvduse = static_library('vduse',
118
284 rw
1559
+ files('libvduse.c'),
119
286 rw quick
1560
+ c_args: '-D_GNU_SOURCE')
1561
+
1562
+libvduse_dep = declare_dependency(link_with: libvduse,
1563
+ include_directories: include_directories('.'))
1564
diff --git a/subprojects/libvduse/standard-headers/linux b/subprojects/libvduse/standard-headers/linux
1565
new file mode 120000
1566
index XXXXXXX..XXXXXXX
1567
--- /dev/null
1568
+++ b/subprojects/libvduse/standard-headers/linux
1569
@@ -0,0 +1 @@
1570
+../../../include/standard-headers/linux/
1571
\ No newline at end of file
120
--
1572
--
121
2.20.1
1573
2.35.3
122
1574
123
1575
diff view generated by jsdifflib
1
From: Peter Krempa <pkrempa@redhat.com>
1
From: Xie Yongji <xieyongji@bytedance.com>
2
2
3
Anounce that 'blockdev-snapshot' command's permissions allow changing
3
This implements a VDUSE block backends based on
4
of the backing file if the 'consistent_read' permission is not required.
4
the libvduse library. We can use it to export the BDSs
5
5
for both VM and container (host) usage.
6
This is useful for libvirt to allow late opening of the backing chain
6
7
during a blockdev-mirror.
7
The new command-line syntax is:
8
8
9
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
9
$ qemu-storage-daemon \
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
--blockdev file,node-name=drive0,filename=test.img \
11
Message-Id: <20200310113831.27293-8-kwolf@redhat.com>
11
--export vduse-blk,node-name=drive0,id=vduse-export0,writable=on
12
13
After the qemu-storage-daemon started, we need to use
14
the "vdpa" command to attach the device to vDPA bus:
15
16
$ vdpa dev add name vduse-export0 mgmtdev vduse
17
18
Also the device must be removed via the "vdpa" command
19
before we stop the qemu-storage-daemon.
20
21
Signed-off-by: Xie Yongji <xieyongji@bytedance.com>
22
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
23
Message-Id: <20220523084611.91-7-xieyongji@bytedance.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
24
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
25
---
14
qapi/block-core.json | 9 ++++++++-
26
qapi/block-export.json | 28 +++-
15
1 file changed, 8 insertions(+), 1 deletion(-)
27
meson_options.txt | 2 +
16
28
block/export/vduse-blk.h | 20 +++
17
diff --git a/qapi/block-core.json b/qapi/block-core.json
29
block/export/export.c | 6 +
18
index XXXXXXX..XXXXXXX 100644
30
block/export/vduse-blk.c | 307 ++++++++++++++++++++++++++++++++++
19
--- a/qapi/block-core.json
31
MAINTAINERS | 4 +-
20
+++ b/qapi/block-core.json
32
block/export/meson.build | 5 +
21
@@ -XXX,XX +XXX,XX @@
33
meson.build | 13 ++
34
scripts/meson-buildoptions.sh | 4 +
35
9 files changed, 385 insertions(+), 4 deletions(-)
36
create mode 100644 block/export/vduse-blk.h
37
create mode 100644 block/export/vduse-blk.c
38
39
diff --git a/qapi/block-export.json b/qapi/block-export.json
40
index XXXXXXX..XXXXXXX 100644
41
--- a/qapi/block-export.json
42
+++ b/qapi/block-export.json
43
@@ -XXX,XX +XXX,XX @@
44
'*allow-other': 'FuseExportAllowOther' },
45
'if': 'CONFIG_FUSE' }
46
47
+##
48
+# @BlockExportOptionsVduseBlk:
49
+#
50
+# A vduse-blk block export.
51
+#
52
+# @num-queues: the number of virtqueues. Defaults to 1.
53
+# @queue-size: the size of virtqueue. Defaults to 256.
54
+# @logical-block-size: Logical block size in bytes. Range [512, PAGE_SIZE]
55
+# and must be power of 2. Defaults to 512 bytes.
56
+#
57
+# Since: 7.1
58
+##
59
+{ 'struct': 'BlockExportOptionsVduseBlk',
60
+ 'data': { '*num-queues': 'uint16',
61
+ '*queue-size': 'uint16',
62
+ '*logical-block-size': 'size'} }
63
+
64
##
65
# @NbdServerAddOptions:
22
#
66
#
23
# For the arguments, see the documentation of BlockdevSnapshot.
67
@@ -XXX,XX +XXX,XX @@
68
# @nbd: NBD export
69
# @vhost-user-blk: vhost-user-blk export (since 5.2)
70
# @fuse: FUSE export (since: 6.0)
71
+# @vduse-blk: vduse-blk export (since 7.1)
24
#
72
#
25
+# Features:
73
# Since: 4.2
26
+# @allow-write-only-overlay: If present, the check whether this operation is safe
74
##
27
+# was relaxed so that it can be used to change
75
@@ -XXX,XX +XXX,XX @@
28
+# backing file of a destination of a blockdev-mirror.
76
'data': [ 'nbd',
29
+# (since 5.0)
77
{ 'name': 'vhost-user-blk',
30
+#
78
'if': 'CONFIG_VHOST_USER_BLK_SERVER' },
31
# Since: 2.5
79
- { 'name': 'fuse', 'if': 'CONFIG_FUSE' } ] }
80
+ { 'name': 'fuse', 'if': 'CONFIG_FUSE' },
81
+ { 'name': 'vduse-blk', 'if': 'CONFIG_VDUSE_BLK_EXPORT' } ] }
82
83
##
84
# @BlockExportOptions:
85
@@ -XXX,XX +XXX,XX @@
86
# Describes a block export, i.e. how single node should be exported on an
87
# external interface.
32
#
88
#
33
# Example:
89
-# @id: A unique identifier for the block export (across all export types)
34
@@ -XXX,XX +XXX,XX @@
90
+# @id: A unique identifier for the block export (across the host for vduse-blk
91
+# export type or across all export types for other types)
35
#
92
#
93
# @node-name: The node name of the block node to be exported (since: 5.2)
94
#
95
@@ -XXX,XX +XXX,XX @@
96
'vhost-user-blk': { 'type': 'BlockExportOptionsVhostUserBlk',
97
'if': 'CONFIG_VHOST_USER_BLK_SERVER' },
98
'fuse': { 'type': 'BlockExportOptionsFuse',
99
- 'if': 'CONFIG_FUSE' }
100
+ 'if': 'CONFIG_FUSE' },
101
+ 'vduse-blk': { 'type': 'BlockExportOptionsVduseBlk',
102
+ 'if': 'CONFIG_VDUSE_BLK_EXPORT' }
103
} }
104
36
##
105
##
37
{ 'command': 'blockdev-snapshot',
106
diff --git a/meson_options.txt b/meson_options.txt
38
- 'data': 'BlockdevSnapshot' }
107
index XXXXXXX..XXXXXXX 100644
39
+ 'data': 'BlockdevSnapshot',
108
--- a/meson_options.txt
40
+ 'features': [ 'allow-write-only-overlay' ] }
109
+++ b/meson_options.txt
41
110
@@ -XXX,XX +XXX,XX @@ option('virtiofsd', type: 'feature', value: 'auto',
42
##
111
description: 'build virtiofs daemon (virtiofsd)')
43
# @change-backing-file:
112
option('libvduse', type: 'feature', value: 'auto',
113
description: 'build VDUSE Library')
114
+option('vduse_blk_export', type: 'feature', value: 'auto',
115
+ description: 'VDUSE block export support')
116
117
option('capstone', type: 'feature', value: 'auto',
118
description: 'Whether and how to find the capstone library')
119
diff --git a/block/export/vduse-blk.h b/block/export/vduse-blk.h
120
new file mode 100644
121
index XXXXXXX..XXXXXXX
122
--- /dev/null
123
+++ b/block/export/vduse-blk.h
124
@@ -XXX,XX +XXX,XX @@
125
+/*
126
+ * Export QEMU block device via VDUSE
127
+ *
128
+ * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved.
129
+ *
130
+ * Author:
131
+ * Xie Yongji <xieyongji@bytedance.com>
132
+ *
133
+ * This work is licensed under the terms of the GNU GPL, version 2 or
134
+ * later. See the COPYING file in the top-level directory.
135
+ */
136
+
137
+#ifndef VDUSE_BLK_H
138
+#define VDUSE_BLK_H
139
+
140
+#include "block/export.h"
141
+
142
+extern const BlockExportDriver blk_exp_vduse_blk;
143
+
144
+#endif /* VDUSE_BLK_H */
145
diff --git a/block/export/export.c b/block/export/export.c
146
index XXXXXXX..XXXXXXX 100644
147
--- a/block/export/export.c
148
+++ b/block/export/export.c
149
@@ -XXX,XX +XXX,XX @@
150
#ifdef CONFIG_VHOST_USER_BLK_SERVER
151
#include "vhost-user-blk-server.h"
152
#endif
153
+#ifdef CONFIG_VDUSE_BLK_EXPORT
154
+#include "vduse-blk.h"
155
+#endif
156
157
static const BlockExportDriver *blk_exp_drivers[] = {
158
&blk_exp_nbd,
159
@@ -XXX,XX +XXX,XX @@ static const BlockExportDriver *blk_exp_drivers[] = {
160
#ifdef CONFIG_FUSE
161
&blk_exp_fuse,
162
#endif
163
+#ifdef CONFIG_VDUSE_BLK_EXPORT
164
+ &blk_exp_vduse_blk,
165
+#endif
166
};
167
168
/* Only accessed from the main thread */
169
diff --git a/block/export/vduse-blk.c b/block/export/vduse-blk.c
170
new file mode 100644
171
index XXXXXXX..XXXXXXX
172
--- /dev/null
173
+++ b/block/export/vduse-blk.c
174
@@ -XXX,XX +XXX,XX @@
175
+/*
176
+ * Export QEMU block device via VDUSE
177
+ *
178
+ * Copyright (C) 2022 Bytedance Inc. and/or its affiliates. All rights reserved.
179
+ *
180
+ * Author:
181
+ * Xie Yongji <xieyongji@bytedance.com>
182
+ *
183
+ * This work is licensed under the terms of the GNU GPL, version 2 or
184
+ * later. See the COPYING file in the top-level directory.
185
+ */
186
+
187
+#include <sys/eventfd.h>
188
+
189
+#include "qemu/osdep.h"
190
+#include "qapi/error.h"
191
+#include "block/export.h"
192
+#include "qemu/error-report.h"
193
+#include "util/block-helpers.h"
194
+#include "subprojects/libvduse/libvduse.h"
195
+#include "virtio-blk-handler.h"
196
+
197
+#include "standard-headers/linux/virtio_blk.h"
198
+
199
+#define VDUSE_DEFAULT_NUM_QUEUE 1
200
+#define VDUSE_DEFAULT_QUEUE_SIZE 256
201
+
202
+typedef struct VduseBlkExport {
203
+ BlockExport export;
204
+ VirtioBlkHandler handler;
205
+ VduseDev *dev;
206
+ uint16_t num_queues;
207
+} VduseBlkExport;
208
+
209
+typedef struct VduseBlkReq {
210
+ VduseVirtqElement elem;
211
+ VduseVirtq *vq;
212
+} VduseBlkReq;
213
+
214
+static void vduse_blk_req_complete(VduseBlkReq *req, size_t in_len)
215
+{
216
+ vduse_queue_push(req->vq, &req->elem, in_len);
217
+ vduse_queue_notify(req->vq);
218
+
219
+ free(req);
220
+}
221
+
222
+static void coroutine_fn vduse_blk_virtio_process_req(void *opaque)
223
+{
224
+ VduseBlkReq *req = opaque;
225
+ VduseVirtq *vq = req->vq;
226
+ VduseDev *dev = vduse_queue_get_dev(vq);
227
+ VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev);
228
+ VirtioBlkHandler *handler = &vblk_exp->handler;
229
+ VduseVirtqElement *elem = &req->elem;
230
+ struct iovec *in_iov = elem->in_sg;
231
+ struct iovec *out_iov = elem->out_sg;
232
+ unsigned in_num = elem->in_num;
233
+ unsigned out_num = elem->out_num;
234
+ int in_len;
235
+
236
+ in_len = virtio_blk_process_req(handler, in_iov,
237
+ out_iov, in_num, out_num);
238
+ if (in_len < 0) {
239
+ free(req);
240
+ return;
241
+ }
242
+
243
+ vduse_blk_req_complete(req, in_len);
244
+}
245
+
246
+static void vduse_blk_vq_handler(VduseDev *dev, VduseVirtq *vq)
247
+{
248
+ while (1) {
249
+ VduseBlkReq *req;
250
+
251
+ req = vduse_queue_pop(vq, sizeof(VduseBlkReq));
252
+ if (!req) {
253
+ break;
254
+ }
255
+ req->vq = vq;
256
+
257
+ Coroutine *co =
258
+ qemu_coroutine_create(vduse_blk_virtio_process_req, req);
259
+ qemu_coroutine_enter(co);
260
+ }
261
+}
262
+
263
+static void on_vduse_vq_kick(void *opaque)
264
+{
265
+ VduseVirtq *vq = opaque;
266
+ VduseDev *dev = vduse_queue_get_dev(vq);
267
+ int fd = vduse_queue_get_fd(vq);
268
+ eventfd_t kick_data;
269
+
270
+ if (eventfd_read(fd, &kick_data) == -1) {
271
+ error_report("failed to read data from eventfd");
272
+ return;
273
+ }
274
+
275
+ vduse_blk_vq_handler(dev, vq);
276
+}
277
+
278
+static void vduse_blk_enable_queue(VduseDev *dev, VduseVirtq *vq)
279
+{
280
+ VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev);
281
+
282
+ aio_set_fd_handler(vblk_exp->export.ctx, vduse_queue_get_fd(vq),
283
+ true, on_vduse_vq_kick, NULL, NULL, NULL, vq);
284
+}
285
+
286
+static void vduse_blk_disable_queue(VduseDev *dev, VduseVirtq *vq)
287
+{
288
+ VduseBlkExport *vblk_exp = vduse_dev_get_priv(dev);
289
+
290
+ aio_set_fd_handler(vblk_exp->export.ctx, vduse_queue_get_fd(vq),
291
+ true, NULL, NULL, NULL, NULL, NULL);
292
+}
293
+
294
+static const VduseOps vduse_blk_ops = {
295
+ .enable_queue = vduse_blk_enable_queue,
296
+ .disable_queue = vduse_blk_disable_queue,
297
+};
298
+
299
+static void on_vduse_dev_kick(void *opaque)
300
+{
301
+ VduseDev *dev = opaque;
302
+
303
+ vduse_dev_handler(dev);
304
+}
305
+
306
+static void vduse_blk_attach_ctx(VduseBlkExport *vblk_exp, AioContext *ctx)
307
+{
308
+ int i;
309
+
310
+ aio_set_fd_handler(vblk_exp->export.ctx, vduse_dev_get_fd(vblk_exp->dev),
311
+ true, on_vduse_dev_kick, NULL, NULL, NULL,
312
+ vblk_exp->dev);
313
+
314
+ for (i = 0; i < vblk_exp->num_queues; i++) {
315
+ VduseVirtq *vq = vduse_dev_get_queue(vblk_exp->dev, i);
316
+ int fd = vduse_queue_get_fd(vq);
317
+
318
+ if (fd < 0) {
319
+ continue;
320
+ }
321
+ aio_set_fd_handler(vblk_exp->export.ctx, fd, true,
322
+ on_vduse_vq_kick, NULL, NULL, NULL, vq);
323
+ }
324
+}
325
+
326
+static void vduse_blk_detach_ctx(VduseBlkExport *vblk_exp)
327
+{
328
+ int i;
329
+
330
+ for (i = 0; i < vblk_exp->num_queues; i++) {
331
+ VduseVirtq *vq = vduse_dev_get_queue(vblk_exp->dev, i);
332
+ int fd = vduse_queue_get_fd(vq);
333
+
334
+ if (fd < 0) {
335
+ continue;
336
+ }
337
+ aio_set_fd_handler(vblk_exp->export.ctx, fd,
338
+ true, NULL, NULL, NULL, NULL, NULL);
339
+ }
340
+ aio_set_fd_handler(vblk_exp->export.ctx, vduse_dev_get_fd(vblk_exp->dev),
341
+ true, NULL, NULL, NULL, NULL, NULL);
342
+}
343
+
344
+
345
+static void blk_aio_attached(AioContext *ctx, void *opaque)
346
+{
347
+ VduseBlkExport *vblk_exp = opaque;
348
+
349
+ vblk_exp->export.ctx = ctx;
350
+ vduse_blk_attach_ctx(vblk_exp, ctx);
351
+}
352
+
353
+static void blk_aio_detach(void *opaque)
354
+{
355
+ VduseBlkExport *vblk_exp = opaque;
356
+
357
+ vduse_blk_detach_ctx(vblk_exp);
358
+ vblk_exp->export.ctx = NULL;
359
+}
360
+
361
+static int vduse_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
362
+ Error **errp)
363
+{
364
+ VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export);
365
+ BlockExportOptionsVduseBlk *vblk_opts = &opts->u.vduse_blk;
366
+ uint64_t logical_block_size = VIRTIO_BLK_SECTOR_SIZE;
367
+ uint16_t num_queues = VDUSE_DEFAULT_NUM_QUEUE;
368
+ uint16_t queue_size = VDUSE_DEFAULT_QUEUE_SIZE;
369
+ Error *local_err = NULL;
370
+ struct virtio_blk_config config = { 0 };
371
+ uint64_t features;
372
+ int i;
373
+
374
+ if (vblk_opts->has_num_queues) {
375
+ num_queues = vblk_opts->num_queues;
376
+ if (num_queues == 0) {
377
+ error_setg(errp, "num-queues must be greater than 0");
378
+ return -EINVAL;
379
+ }
380
+ }
381
+
382
+ if (vblk_opts->has_queue_size) {
383
+ queue_size = vblk_opts->queue_size;
384
+ if (queue_size <= 2 || !is_power_of_2(queue_size) ||
385
+ queue_size > VIRTQUEUE_MAX_SIZE) {
386
+ error_setg(errp, "queue-size is invalid");
387
+ return -EINVAL;
388
+ }
389
+ }
390
+
391
+ if (vblk_opts->has_logical_block_size) {
392
+ logical_block_size = vblk_opts->logical_block_size;
393
+ check_block_size(exp->id, "logical-block-size", logical_block_size,
394
+ &local_err);
395
+ if (local_err) {
396
+ error_propagate(errp, local_err);
397
+ return -EINVAL;
398
+ }
399
+ }
400
+ vblk_exp->num_queues = num_queues;
401
+ vblk_exp->handler.blk = exp->blk;
402
+ vblk_exp->handler.serial = exp->id;
403
+ vblk_exp->handler.logical_block_size = logical_block_size;
404
+ vblk_exp->handler.writable = opts->writable;
405
+
406
+ config.capacity =
407
+ cpu_to_le64(blk_getlength(exp->blk) >> VIRTIO_BLK_SECTOR_BITS);
408
+ config.seg_max = cpu_to_le32(queue_size - 2);
409
+ config.min_io_size = cpu_to_le16(1);
410
+ config.opt_io_size = cpu_to_le32(1);
411
+ config.num_queues = cpu_to_le16(num_queues);
412
+ config.blk_size = cpu_to_le32(logical_block_size);
413
+ config.max_discard_sectors = cpu_to_le32(VIRTIO_BLK_MAX_DISCARD_SECTORS);
414
+ config.max_discard_seg = cpu_to_le32(1);
415
+ config.discard_sector_alignment =
416
+ cpu_to_le32(logical_block_size >> VIRTIO_BLK_SECTOR_BITS);
417
+ config.max_write_zeroes_sectors =
418
+ cpu_to_le32(VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS);
419
+ config.max_write_zeroes_seg = cpu_to_le32(1);
420
+
421
+ features = vduse_get_virtio_features() |
422
+ (1ULL << VIRTIO_BLK_F_SEG_MAX) |
423
+ (1ULL << VIRTIO_BLK_F_TOPOLOGY) |
424
+ (1ULL << VIRTIO_BLK_F_BLK_SIZE) |
425
+ (1ULL << VIRTIO_BLK_F_FLUSH) |
426
+ (1ULL << VIRTIO_BLK_F_DISCARD) |
427
+ (1ULL << VIRTIO_BLK_F_WRITE_ZEROES);
428
+
429
+ if (num_queues > 1) {
430
+ features |= 1ULL << VIRTIO_BLK_F_MQ;
431
+ }
432
+ if (!opts->writable) {
433
+ features |= 1ULL << VIRTIO_BLK_F_RO;
434
+ }
435
+
436
+ vblk_exp->dev = vduse_dev_create(exp->id, VIRTIO_ID_BLOCK, 0,
437
+ features, num_queues,
438
+ sizeof(struct virtio_blk_config),
439
+ (char *)&config, &vduse_blk_ops,
440
+ vblk_exp);
441
+ if (!vblk_exp->dev) {
442
+ error_setg(errp, "failed to create vduse device");
443
+ return -ENOMEM;
444
+ }
445
+
446
+ for (i = 0; i < num_queues; i++) {
447
+ vduse_dev_setup_queue(vblk_exp->dev, i, queue_size);
448
+ }
449
+
450
+ aio_set_fd_handler(exp->ctx, vduse_dev_get_fd(vblk_exp->dev), true,
451
+ on_vduse_dev_kick, NULL, NULL, NULL, vblk_exp->dev);
452
+
453
+ blk_add_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach,
454
+ vblk_exp);
455
+
456
+ return 0;
457
+}
458
+
459
+static void vduse_blk_exp_delete(BlockExport *exp)
460
+{
461
+ VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export);
462
+
463
+ blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach,
464
+ vblk_exp);
465
+ vduse_dev_destroy(vblk_exp->dev);
466
+}
467
+
468
+static void vduse_blk_exp_request_shutdown(BlockExport *exp)
469
+{
470
+ VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export);
471
+
472
+ vduse_blk_detach_ctx(vblk_exp);
473
+}
474
+
475
+const BlockExportDriver blk_exp_vduse_blk = {
476
+ .type = BLOCK_EXPORT_TYPE_VDUSE_BLK,
477
+ .instance_size = sizeof(VduseBlkExport),
478
+ .create = vduse_blk_exp_create,
479
+ .delete = vduse_blk_exp_delete,
480
+ .request_shutdown = vduse_blk_exp_request_shutdown,
481
+};
482
diff --git a/MAINTAINERS b/MAINTAINERS
483
index XXXXXXX..XXXXXXX 100644
484
--- a/MAINTAINERS
485
+++ b/MAINTAINERS
486
@@ -XXX,XX +XXX,XX @@ L: qemu-block@nongnu.org
487
S: Supported
488
F: block/export/fuse.c
489
490
-VDUSE library
491
+VDUSE library and block device exports
492
M: Xie Yongji <xieyongji@bytedance.com>
493
S: Maintained
494
F: subprojects/libvduse/
495
+F: block/export/vduse-blk.c
496
+F: block/export/vduse-blk.h
497
498
Replication
499
M: Wen Congyang <wencongyang2@huawei.com>
500
diff --git a/block/export/meson.build b/block/export/meson.build
501
index XXXXXXX..XXXXXXX 100644
502
--- a/block/export/meson.build
503
+++ b/block/export/meson.build
504
@@ -XXX,XX +XXX,XX @@ if have_vhost_user_blk_server
505
endif
506
507
blockdev_ss.add(when: fuse, if_true: files('fuse.c'))
508
+
509
+if have_vduse_blk_export
510
+ blockdev_ss.add(files('vduse-blk.c', 'virtio-blk-handler.c'))
511
+ blockdev_ss.add(libvduse)
512
+endif
513
diff --git a/meson.build b/meson.build
514
index XXXXXXX..XXXXXXX 100644
515
--- a/meson.build
516
+++ b/meson.build
517
@@ -XXX,XX +XXX,XX @@ if have_libvduse
518
libvduse = libvduse_proj.get_variable('libvduse_dep')
519
endif
520
521
+have_vduse_blk_export = (have_libvduse and targetos == 'linux')
522
+if get_option('vduse_blk_export').enabled()
523
+ if targetos != 'linux'
524
+ error('vduse_blk_export requires linux')
525
+ elif not have_libvduse
526
+ error('vduse_blk_export requires libvduse support')
527
+ endif
528
+elif get_option('vduse_blk_export').disabled()
529
+ have_vduse_blk_export = false
530
+endif
531
+
532
# libbpf
533
libbpf = dependency('libbpf', required: get_option('bpf'), method: 'pkg-config')
534
if libbpf.found() and not cc.links('''
535
@@ -XXX,XX +XXX,XX @@ config_host_data.set('CONFIG_VHOST_CRYPTO', have_vhost_user_crypto)
536
config_host_data.set('CONFIG_VHOST_VDPA', have_vhost_vdpa)
537
config_host_data.set('CONFIG_VMNET', vmnet.found())
538
config_host_data.set('CONFIG_VHOST_USER_BLK_SERVER', have_vhost_user_blk_server)
539
+config_host_data.set('CONFIG_VDUSE_BLK_EXPORT', have_vduse_blk_export)
540
config_host_data.set('CONFIG_PNG', png.found())
541
config_host_data.set('CONFIG_VNC', vnc.found())
542
config_host_data.set('CONFIG_VNC_JPEG', jpeg.found())
543
@@ -XXX,XX +XXX,XX @@ if have_block
544
summary_info += {'qed support': get_option('qed').allowed()}
545
summary_info += {'parallels support': get_option('parallels').allowed()}
546
summary_info += {'FUSE exports': fuse}
547
+ summary_info += {'VDUSE block exports': have_vduse_blk_export}
548
endif
549
summary(summary_info, bool_yn: true, section: 'Block layer support')
550
551
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
552
index XXXXXXX..XXXXXXX 100644
553
--- a/scripts/meson-buildoptions.sh
554
+++ b/scripts/meson-buildoptions.sh
555
@@ -XXX,XX +XXX,XX @@ meson_options_help() {
556
printf "%s\n" ' vhost-user vhost-user backend support'
557
printf "%s\n" ' vhost-user-blk-server'
558
printf "%s\n" ' build vhost-user-blk server'
559
+ printf "%s\n" ' vduse-blk-export'
560
+ printf "%s\n" ' VDUSE block export support'
561
printf "%s\n" ' vhost-vdpa vhost-vdpa kernel backend support'
562
printf "%s\n" ' virglrenderer virgl rendering support'
563
printf "%s\n" ' virtfs virtio-9p support'
564
@@ -XXX,XX +XXX,XX @@ _meson_option_parse() {
565
--disable-vhost-user) printf "%s" -Dvhost_user=disabled ;;
566
--enable-vhost-user-blk-server) printf "%s" -Dvhost_user_blk_server=enabled ;;
567
--disable-vhost-user-blk-server) printf "%s" -Dvhost_user_blk_server=disabled ;;
568
+ --enable-vduse-blk-export) printf "%s" -Dvduse_blk_export=enabled ;;
569
+ --disable-vduse-blk-export) printf "%s" -Dvduse_blk_export=disabled ;;
570
--enable-vhost-vdpa) printf "%s" -Dvhost_vdpa=enabled ;;
571
--disable-vhost-vdpa) printf "%s" -Dvhost_vdpa=disabled ;;
572
--enable-virglrenderer) printf "%s" -Dvirglrenderer=enabled ;;
44
--
573
--
45
2.20.1
574
2.35.3
46
47
diff view generated by jsdifflib
1
From: Daniel Henrique Barboza <danielhb413@gmail.com>
1
From: Xie Yongji <xieyongji@bytedance.com>
2
2
3
Using the new 'bdrv_co_delete_file' interface, a pure co_routine function
3
To support block resize, this uses vduse_dev_update_config()
4
'bdrv_co_delete_file' inside block.c can can be used in a way similar of
4
to update the capacity field in configuration space and inject
5
the existing bdrv_create_file to to clean up a created file.
5
config interrupt on the block resize callback.
6
6
7
We're creating a pure co_routine because the only caller of
7
Signed-off-by: Xie Yongji <xieyongji@bytedance.com>
8
'bdrv_co_delete_file' will be already in co_routine context, thus there
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
is no need to add all the machinery to check for qemu_in_coroutine() and
9
Message-Id: <20220523084611.91-8-xieyongji@bytedance.com>
10
create a separated co_routine to do the job.
11
12
Suggested-by: Daniel P. Berrangé <berrange@redhat.com>
13
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
14
Message-Id: <20200130213907.2830642-3-danielhb413@gmail.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
11
---
17
include/block/block.h | 1 +
12
block/export/vduse-blk.c | 20 ++++++++++++++++++++
18
block.c | 26 ++++++++++++++++++++++++++
13
1 file changed, 20 insertions(+)
19
2 files changed, 27 insertions(+)
20
14
21
diff --git a/include/block/block.h b/include/block/block.h
15
diff --git a/block/export/vduse-blk.c b/block/export/vduse-blk.c
22
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/block.h
17
--- a/block/export/vduse-blk.c
24
+++ b/include/block/block.h
18
+++ b/block/export/vduse-blk.c
25
@@ -XXX,XX +XXX,XX @@ bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base,
19
@@ -XXX,XX +XXX,XX @@ static void blk_aio_detach(void *opaque)
26
int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base,
20
vblk_exp->export.ctx = NULL;
27
Error **errp);
28
void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base);
29
+int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp);
30
31
32
typedef struct BdrvCheckResult {
33
diff --git a/block.c b/block.c
34
index XXXXXXX..XXXXXXX 100644
35
--- a/block.c
36
+++ b/block.c
37
@@ -XXX,XX +XXX,XX @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
38
}
39
}
21
}
40
22
41
+int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp)
23
+static void vduse_blk_resize(void *opaque)
42
+{
24
+{
43
+ Error *local_err = NULL;
25
+ BlockExport *exp = opaque;
44
+ int ret;
26
+ VduseBlkExport *vblk_exp = container_of(exp, VduseBlkExport, export);
27
+ struct virtio_blk_config config;
45
+
28
+
46
+ assert(bs != NULL);
29
+ config.capacity =
47
+
30
+ cpu_to_le64(blk_getlength(exp->blk) >> VIRTIO_BLK_SECTOR_BITS);
48
+ if (!bs->drv) {
31
+ vduse_dev_update_config(vblk_exp->dev, sizeof(config.capacity),
49
+ error_setg(errp, "Block node '%s' is not opened", bs->filename);
32
+ offsetof(struct virtio_blk_config, capacity),
50
+ return -ENOMEDIUM;
33
+ (char *)&config.capacity);
51
+ }
52
+
53
+ if (!bs->drv->bdrv_co_delete_file) {
54
+ error_setg(errp, "Driver '%s' does not support image deletion",
55
+ bs->drv->format_name);
56
+ return -ENOTSUP;
57
+ }
58
+
59
+ ret = bs->drv->bdrv_co_delete_file(bs, &local_err);
60
+ if (ret < 0) {
61
+ error_propagate(errp, local_err);
62
+ }
63
+
64
+ return ret;
65
+}
34
+}
66
+
35
+
67
/**
36
+static const BlockDevOps vduse_block_ops = {
68
* Try to get @bs's logical and physical block size.
37
+ .resize_cb = vduse_blk_resize,
69
* On success, store them in @bsz struct and return 0.
38
+};
39
+
40
static int vduse_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
41
Error **errp)
42
{
43
@@ -XXX,XX +XXX,XX @@ static int vduse_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
44
blk_add_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach,
45
vblk_exp);
46
47
+ blk_set_dev_ops(exp->blk, &vduse_block_ops, exp);
48
+
49
return 0;
50
}
51
52
@@ -XXX,XX +XXX,XX @@ static void vduse_blk_exp_delete(BlockExport *exp)
53
54
blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach,
55
vblk_exp);
56
+ blk_set_dev_ops(exp->blk, NULL, NULL);
57
vduse_dev_destroy(vblk_exp->dev);
58
}
59
70
--
60
--
71
2.20.1
61
2.35.3
72
73
diff view generated by jsdifflib
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
From: Xie Yongji <xieyongji@bytedance.com>
2
Message-Id: <20200310113831.27293-2-kwolf@redhat.com>
2
3
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
3
To support reconnecting after restart or crash, VDUSE backend
4
might need to resubmit inflight I/Os. This stores the metadata
5
such as the index of inflight I/O's descriptors to a shm file so
6
that VDUSE backend can restore them during reconnecting.
7
8
Signed-off-by: Xie Yongji <xieyongji@bytedance.com>
9
Message-Id: <20220523084611.91-9-xieyongji@bytedance.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
12
---
6
include/block/block_int.h | 3 +++
13
subprojects/libvduse/libvduse.h | 12 ++
7
block.c | 6 ++----
14
block/export/vduse-blk.c | 14 ++
8
2 files changed, 5 insertions(+), 4 deletions(-)
15
subprojects/libvduse/libvduse.c | 235 +++++++++++++++++++++++++++++++-
16
3 files changed, 256 insertions(+), 5 deletions(-)
9
17
10
diff --git a/include/block/block_int.h b/include/block/block_int.h
18
diff --git a/subprojects/libvduse/libvduse.h b/subprojects/libvduse/libvduse.h
11
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
12
--- a/include/block/block_int.h
20
--- a/subprojects/libvduse/libvduse.h
13
+++ b/include/block/block_int.h
21
+++ b/subprojects/libvduse/libvduse.h
14
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
22
@@ -XXX,XX +XXX,XX @@ int vduse_dev_update_config(VduseDev *dev, uint32_t size,
15
void *opaque, Error **errp);
23
*/
16
void bdrv_root_unref_child(BdrvChild *child);
24
int vduse_dev_setup_queue(VduseDev *dev, int index, int max_size);
17
25
18
+void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
26
+/**
19
+ uint64_t *shared_perm);
27
+ * vduse_set_reconnect_log_file:
28
+ * @dev: VDUSE device
29
+ * @file: filename of reconnect log
30
+ *
31
+ * Specify the file to store log for reconnecting. It should
32
+ * be called before vduse_dev_setup_queue().
33
+ *
34
+ * Returns: 0 on success, -errno on failure.
35
+ */
36
+int vduse_set_reconnect_log_file(VduseDev *dev, const char *filename);
20
+
37
+
21
/**
38
/**
22
* Sets a BdrvChild's permissions. Avoid if the parent is a BDS; use
39
* vduse_dev_create_by_fd:
23
* bdrv_child_refresh_perms() instead and make the parent's
40
* @fd: passed file descriptor
24
diff --git a/block.c b/block.c
41
diff --git a/block/export/vduse-blk.c b/block/export/vduse-blk.c
25
index XXXXXXX..XXXXXXX 100644
42
index XXXXXXX..XXXXXXX 100644
26
--- a/block.c
43
--- a/block/export/vduse-blk.c
27
+++ b/block.c
44
+++ b/block/export/vduse-blk.c
28
@@ -XXX,XX +XXX,XX @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
45
@@ -XXX,XX +XXX,XX @@ typedef struct VduseBlkExport {
29
bool *tighten_restrictions, Error **errp);
46
VirtioBlkHandler handler;
30
static void bdrv_child_abort_perm_update(BdrvChild *c);
47
VduseDev *dev;
31
static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
48
uint16_t num_queues;
32
-static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
49
+ char *recon_file;
33
- uint64_t *shared_perm);
50
} VduseBlkExport;
34
51
35
typedef struct BlockReopenQueueEntry {
52
typedef struct VduseBlkReq {
36
bool prepared;
53
@@ -XXX,XX +XXX,XX @@ static void vduse_blk_enable_queue(VduseDev *dev, VduseVirtq *vq)
37
@@ -XXX,XX +XXX,XX @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms,
54
38
}
55
aio_set_fd_handler(vblk_exp->export.ctx, vduse_queue_get_fd(vq),
39
}
56
true, on_vduse_vq_kick, NULL, NULL, NULL, vq);
40
57
+ /* Make sure we don't miss any kick afer reconnecting */
41
-static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
58
+ eventfd_write(vduse_queue_get_fd(vq), 1);
42
- uint64_t *shared_perm)
59
}
43
+void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
60
44
+ uint64_t *shared_perm)
61
static void vduse_blk_disable_queue(VduseDev *dev, VduseVirtq *vq)
62
@@ -XXX,XX +XXX,XX @@ static int vduse_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
63
return -ENOMEM;
64
}
65
66
+ vblk_exp->recon_file = g_strdup_printf("%s/vduse-blk-%s",
67
+ g_get_tmp_dir(), exp->id);
68
+ if (vduse_set_reconnect_log_file(vblk_exp->dev, vblk_exp->recon_file)) {
69
+ error_setg(errp, "failed to set reconnect log file");
70
+ vduse_dev_destroy(vblk_exp->dev);
71
+ g_free(vblk_exp->recon_file);
72
+ return -EINVAL;
73
+ }
74
+
75
for (i = 0; i < num_queues; i++) {
76
vduse_dev_setup_queue(vblk_exp->dev, i, queue_size);
77
}
78
@@ -XXX,XX +XXX,XX @@ static void vduse_blk_exp_delete(BlockExport *exp)
79
vblk_exp);
80
blk_set_dev_ops(exp->blk, NULL, NULL);
81
vduse_dev_destroy(vblk_exp->dev);
82
+ unlink(vblk_exp->recon_file);
83
+ g_free(vblk_exp->recon_file);
84
}
85
86
static void vduse_blk_exp_request_shutdown(BlockExport *exp)
87
diff --git a/subprojects/libvduse/libvduse.c b/subprojects/libvduse/libvduse.c
88
index XXXXXXX..XXXXXXX 100644
89
--- a/subprojects/libvduse/libvduse.c
90
+++ b/subprojects/libvduse/libvduse.c
91
@@ -XXX,XX +XXX,XX @@
92
#define VDUSE_VQ_ALIGN 4096
93
#define MAX_IOVA_REGIONS 256
94
95
+#define LOG_ALIGNMENT 64
96
+
97
/* Round number down to multiple */
98
#define ALIGN_DOWN(n, m) ((n) / (m) * (m))
99
100
@@ -XXX,XX +XXX,XX @@
101
#define unlikely(x) __builtin_expect(!!(x), 0)
102
#endif
103
104
+typedef struct VduseDescStateSplit {
105
+ uint8_t inflight;
106
+ uint8_t padding[5];
107
+ uint16_t next;
108
+ uint64_t counter;
109
+} VduseDescStateSplit;
110
+
111
+typedef struct VduseVirtqLogInflight {
112
+ uint64_t features;
113
+ uint16_t version;
114
+ uint16_t desc_num;
115
+ uint16_t last_batch_head;
116
+ uint16_t used_idx;
117
+ VduseDescStateSplit desc[];
118
+} VduseVirtqLogInflight;
119
+
120
+typedef struct VduseVirtqLog {
121
+ VduseVirtqLogInflight inflight;
122
+} VduseVirtqLog;
123
+
124
+typedef struct VduseVirtqInflightDesc {
125
+ uint16_t index;
126
+ uint64_t counter;
127
+} VduseVirtqInflightDesc;
128
+
129
typedef struct VduseRing {
130
unsigned int num;
131
uint64_t desc_addr;
132
@@ -XXX,XX +XXX,XX @@ struct VduseVirtq {
133
bool ready;
134
int fd;
135
VduseDev *dev;
136
+ VduseVirtqInflightDesc *resubmit_list;
137
+ uint16_t resubmit_num;
138
+ uint64_t counter;
139
+ VduseVirtqLog *log;
140
};
141
142
typedef struct VduseIovaRegion {
143
@@ -XXX,XX +XXX,XX @@ struct VduseDev {
144
int fd;
145
int ctrl_fd;
146
void *priv;
147
+ void *log;
148
};
149
150
+static inline size_t vduse_vq_log_size(uint16_t queue_size)
151
+{
152
+ return ALIGN_UP(sizeof(VduseDescStateSplit) * queue_size +
153
+ sizeof(VduseVirtqLogInflight), LOG_ALIGNMENT);
154
+}
155
+
156
+static void *vduse_log_get(const char *filename, size_t size)
157
+{
158
+ void *ptr = MAP_FAILED;
159
+ int fd;
160
+
161
+ fd = open(filename, O_RDWR | O_CREAT, 0600);
162
+ if (fd == -1) {
163
+ return MAP_FAILED;
164
+ }
165
+
166
+ if (ftruncate(fd, size) == -1) {
167
+ goto out;
168
+ }
169
+
170
+ ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
171
+
172
+out:
173
+ close(fd);
174
+ return ptr;
175
+}
176
+
177
static inline bool has_feature(uint64_t features, unsigned int fbit)
45
{
178
{
46
BdrvChild *c;
179
assert(fbit < 64);
47
uint64_t cumulative_perms = 0;
180
@@ -XXX,XX +XXX,XX @@ static int vduse_inject_irq(VduseDev *dev, int index)
181
return ioctl(dev->fd, VDUSE_VQ_INJECT_IRQ, &index);
182
}
183
184
+static int inflight_desc_compare(const void *a, const void *b)
185
+{
186
+ VduseVirtqInflightDesc *desc0 = (VduseVirtqInflightDesc *)a,
187
+ *desc1 = (VduseVirtqInflightDesc *)b;
188
+
189
+ if (desc1->counter > desc0->counter &&
190
+ (desc1->counter - desc0->counter) < VIRTQUEUE_MAX_SIZE * 2) {
191
+ return 1;
192
+ }
193
+
194
+ return -1;
195
+}
196
+
197
+static int vduse_queue_check_inflights(VduseVirtq *vq)
198
+{
199
+ int i = 0;
200
+ VduseDev *dev = vq->dev;
201
+
202
+ vq->used_idx = le16toh(vq->vring.used->idx);
203
+ vq->resubmit_num = 0;
204
+ vq->resubmit_list = NULL;
205
+ vq->counter = 0;
206
+
207
+ if (unlikely(vq->log->inflight.used_idx != vq->used_idx)) {
208
+ if (vq->log->inflight.last_batch_head > VIRTQUEUE_MAX_SIZE) {
209
+ return -1;
210
+ }
211
+
212
+ vq->log->inflight.desc[vq->log->inflight.last_batch_head].inflight = 0;
213
+
214
+ barrier();
215
+
216
+ vq->log->inflight.used_idx = vq->used_idx;
217
+ }
218
+
219
+ for (i = 0; i < vq->log->inflight.desc_num; i++) {
220
+ if (vq->log->inflight.desc[i].inflight == 1) {
221
+ vq->inuse++;
222
+ }
223
+ }
224
+
225
+ vq->shadow_avail_idx = vq->last_avail_idx = vq->inuse + vq->used_idx;
226
+
227
+ if (vq->inuse) {
228
+ vq->resubmit_list = calloc(vq->inuse, sizeof(VduseVirtqInflightDesc));
229
+ if (!vq->resubmit_list) {
230
+ return -1;
231
+ }
232
+
233
+ for (i = 0; i < vq->log->inflight.desc_num; i++) {
234
+ if (vq->log->inflight.desc[i].inflight) {
235
+ vq->resubmit_list[vq->resubmit_num].index = i;
236
+ vq->resubmit_list[vq->resubmit_num].counter =
237
+ vq->log->inflight.desc[i].counter;
238
+ vq->resubmit_num++;
239
+ }
240
+ }
241
+
242
+ if (vq->resubmit_num > 1) {
243
+ qsort(vq->resubmit_list, vq->resubmit_num,
244
+ sizeof(VduseVirtqInflightDesc), inflight_desc_compare);
245
+ }
246
+ vq->counter = vq->resubmit_list[0].counter + 1;
247
+ }
248
+
249
+ vduse_inject_irq(dev, vq->index);
250
+
251
+ return 0;
252
+}
253
+
254
+static int vduse_queue_inflight_get(VduseVirtq *vq, int desc_idx)
255
+{
256
+ vq->log->inflight.desc[desc_idx].counter = vq->counter++;
257
+
258
+ barrier();
259
+
260
+ vq->log->inflight.desc[desc_idx].inflight = 1;
261
+
262
+ return 0;
263
+}
264
+
265
+static int vduse_queue_inflight_pre_put(VduseVirtq *vq, int desc_idx)
266
+{
267
+ vq->log->inflight.last_batch_head = desc_idx;
268
+
269
+ return 0;
270
+}
271
+
272
+static int vduse_queue_inflight_post_put(VduseVirtq *vq, int desc_idx)
273
+{
274
+ vq->log->inflight.desc[desc_idx].inflight = 0;
275
+
276
+ barrier();
277
+
278
+ vq->log->inflight.used_idx = vq->used_idx;
279
+
280
+ return 0;
281
+}
282
+
283
static void vduse_iova_remove_region(VduseDev *dev, uint64_t start,
284
uint64_t last)
285
{
286
@@ -XXX,XX +XXX,XX @@ void *vduse_queue_pop(VduseVirtq *vq, size_t sz)
287
unsigned int head;
288
VduseVirtqElement *elem;
289
VduseDev *dev = vq->dev;
290
+ int i;
291
292
if (unlikely(!vq->vring.avail)) {
293
return NULL;
294
}
295
296
+ if (unlikely(vq->resubmit_list && vq->resubmit_num > 0)) {
297
+ i = (--vq->resubmit_num);
298
+ elem = vduse_queue_map_desc(vq, vq->resubmit_list[i].index, sz);
299
+
300
+ if (!vq->resubmit_num) {
301
+ free(vq->resubmit_list);
302
+ vq->resubmit_list = NULL;
303
+ }
304
+
305
+ return elem;
306
+ }
307
+
308
if (vduse_queue_empty(vq)) {
309
return NULL;
310
}
311
@@ -XXX,XX +XXX,XX @@ void *vduse_queue_pop(VduseVirtq *vq, size_t sz)
312
313
vq->inuse++;
314
315
+ vduse_queue_inflight_get(vq, head);
316
+
317
return elem;
318
}
319
320
@@ -XXX,XX +XXX,XX @@ void vduse_queue_push(VduseVirtq *vq, const VduseVirtqElement *elem,
321
unsigned int len)
322
{
323
vduse_queue_fill(vq, elem, len, 0);
324
+ vduse_queue_inflight_pre_put(vq, elem->index);
325
vduse_queue_flush(vq, 1);
326
+ vduse_queue_inflight_post_put(vq, elem->index);
327
}
328
329
static int vduse_queue_update_vring(VduseVirtq *vq, uint64_t desc_addr,
330
@@ -XXX,XX +XXX,XX @@ static void vduse_queue_enable(VduseVirtq *vq)
331
}
332
333
vq->fd = fd;
334
- vq->shadow_avail_idx = vq->last_avail_idx = vq_info.split.avail_index;
335
- vq->inuse = 0;
336
- vq->used_idx = 0;
337
vq->signalled_used_valid = false;
338
vq->ready = true;
339
340
+ if (vduse_queue_check_inflights(vq)) {
341
+ fprintf(stderr, "Failed to check inflights for vq[%d]\n", vq->index);
342
+ close(fd);
343
+ return;
344
+ }
345
+
346
dev->ops->enable_queue(dev, vq);
347
}
348
349
@@ -XXX,XX +XXX,XX @@ static void vduse_dev_start_dataplane(VduseDev *dev)
350
351
static void vduse_dev_stop_dataplane(VduseDev *dev)
352
{
353
+ size_t log_size = dev->num_queues * vduse_vq_log_size(VIRTQUEUE_MAX_SIZE);
354
int i;
355
356
for (i = 0; i < dev->num_queues; i++) {
357
vduse_queue_disable(&dev->vqs[i]);
358
}
359
+ if (dev->log) {
360
+ memset(dev->log, 0, log_size);
361
+ }
362
dev->features = 0;
363
vduse_iova_remove_region(dev, 0, ULONG_MAX);
364
}
365
@@ -XXX,XX +XXX,XX @@ int vduse_dev_setup_queue(VduseDev *dev, int index, int max_size)
366
return -errno;
367
}
368
369
+ vduse_queue_enable(vq);
370
+
371
+ return 0;
372
+}
373
+
374
+int vduse_set_reconnect_log_file(VduseDev *dev, const char *filename)
375
+{
376
+
377
+ size_t log_size = dev->num_queues * vduse_vq_log_size(VIRTQUEUE_MAX_SIZE);
378
+ void *log;
379
+ int i;
380
+
381
+ dev->log = log = vduse_log_get(filename, log_size);
382
+ if (log == MAP_FAILED) {
383
+ fprintf(stderr, "Failed to get vduse log\n");
384
+ return -EINVAL;
385
+ }
386
+
387
+ for (i = 0; i < dev->num_queues; i++) {
388
+ dev->vqs[i].log = log;
389
+ dev->vqs[i].log->inflight.desc_num = VIRTQUEUE_MAX_SIZE;
390
+ log = (void *)((char *)log + vduse_vq_log_size(VIRTQUEUE_MAX_SIZE));
391
+ }
392
+
393
return 0;
394
}
395
396
@@ -XXX,XX +XXX,XX @@ static int vduse_dev_init(VduseDev *dev, const char *name,
397
return -errno;
398
}
399
400
+ if (ioctl(fd, VDUSE_DEV_GET_FEATURES, &dev->features)) {
401
+ fprintf(stderr, "Failed to get features: %s\n", strerror(errno));
402
+ close(fd);
403
+ return -errno;
404
+ }
405
+
406
dev_name = strdup(name);
407
if (!dev_name) {
408
close(fd);
409
@@ -XXX,XX +XXX,XX @@ VduseDev *vduse_dev_create_by_fd(int fd, uint16_t num_queues,
410
return NULL;
411
}
412
413
+ if (ioctl(fd, VDUSE_DEV_GET_FEATURES, &dev->features)) {
414
+ fprintf(stderr, "Failed to get features: %s\n", strerror(errno));
415
+ free(dev);
416
+ return NULL;
417
+ }
418
+
419
ret = vduse_dev_init_vqs(dev, num_queues);
420
if (ret) {
421
fprintf(stderr, "Failed to init vqs\n");
422
@@ -XXX,XX +XXX,XX @@ VduseDev *vduse_dev_create(const char *name, uint32_t device_id,
423
424
ret = ioctl(ctrl_fd, VDUSE_CREATE_DEV, dev_config);
425
free(dev_config);
426
- if (ret < 0) {
427
+ if (ret && errno != EEXIST) {
428
fprintf(stderr, "Failed to create vduse device %s: %s\n",
429
name, strerror(errno));
430
goto err_dev;
431
@@ -XXX,XX +XXX,XX @@ err_ctrl:
432
433
int vduse_dev_destroy(VduseDev *dev)
434
{
435
- int ret = 0;
436
+ size_t log_size = dev->num_queues * vduse_vq_log_size(VIRTQUEUE_MAX_SIZE);
437
+ int i, ret = 0;
438
439
+ if (dev->log) {
440
+ munmap(dev->log, log_size);
441
+ }
442
+ for (i = 0; i < dev->num_queues; i++) {
443
+ free(dev->vqs[i].resubmit_list);
444
+ }
445
free(dev->vqs);
446
if (dev->fd > 0) {
447
close(dev->fd);
48
--
448
--
49
2.20.1
449
2.35.3
50
51
diff view generated by jsdifflib
1
This patch adds test cases for attaching the backing chain to a mirror
1
From: Stefan Hajnoczi <stefanha@redhat.com>
2
job target right before finalising the job, where the image is in a
3
non-mainloop AioContext (i.e. the backing chain needs to be moved to the
4
AioContext of the mirror target).
5
2
6
This requires switching the test case from virtio-blk to virtio-scsi
3
Document vduse-blk exports in qemu-storage-daemon --help and the
7
because virtio-blk only actually starts using the iothreads when the
4
qemu-storage-daemon(1) man page.
8
guest driver initialises the device (which never happens in a test case
9
without a guest OS). virtio-scsi always keeps its block nodes in the
10
AioContext of the the requested iothread without guest interaction.
11
5
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Based-on: <20220523084611.91-1-xieyongji@bytedance.com>
13
Message-Id: <20200310113831.27293-7-kwolf@redhat.com>
7
Cc: Xie Yongji <xieyongji@bytedance.com>
14
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Message-Id: <20220525121947.859820-1-stefanha@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
11
---
17
tests/qemu-iotests/155 | 32 +++++++++++++++++++++++---------
12
docs/tools/qemu-storage-daemon.rst | 21 +++++++++++++++++++++
18
tests/qemu-iotests/155.out | 4 ++--
13
storage-daemon/qemu-storage-daemon.c | 9 +++++++++
19
2 files changed, 25 insertions(+), 11 deletions(-)
14
2 files changed, 30 insertions(+)
20
15
21
diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155
16
diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst
22
index XXXXXXX..XXXXXXX 100755
17
index XXXXXXX..XXXXXXX 100644
23
--- a/tests/qemu-iotests/155
18
--- a/docs/tools/qemu-storage-daemon.rst
24
+++ b/tests/qemu-iotests/155
19
+++ b/docs/tools/qemu-storage-daemon.rst
25
@@ -XXX,XX +XXX,XX @@ target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt)
20
@@ -XXX,XX +XXX,XX @@ Standard options:
26
# chain opened right away. If False, blockdev-add
21
--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>]
27
# opens it without a backing file and job completion
22
--export [type=]vhost-user-blk,id=<id>,node-name=<node-name>,addr.type=fd,addr.str=<fd>[,writable=on|off][,logical-block-size=<block-size>][,num-queues=<num-queues>]
28
# is supposed to open the backing chain.
23
--export [type=]fuse,id=<id>,node-name=<node-name>,mountpoint=<file>[,growable=on|off][,writable=on|off][,allow-other=on|off|auto]
29
+# use_iothread: If True, an iothread is configured for the virtio-blk device
24
+ --export [type=]vduse-blk,id=<id>,node-name=<node-name>[,writable=on|off][,num-queues=<num-queues>][,queue-size=<queue-size>][,logical-block-size=<block-size>]
30
+# that uses the image being mirrored
25
31
26
is a block export definition. ``node-name`` is the block node that should be
32
class BaseClass(iotests.QMPTestCase):
27
exported. ``writable`` determines whether or not the export allows write
33
target_blockdev_backing = None
28
@@ -XXX,XX +XXX,XX @@ Standard options:
34
target_real_backing = None
29
``allow-other`` to auto (the default) will try enabling this option, and on
35
target_open_with_backing = True
30
error fall back to disabling it.
36
+ use_iothread = False
31
37
32
+ The ``vduse-blk`` export type uses the ``id`` as the VDUSE device name.
38
def setUp(self):
33
+ ``num-queues`` sets the number of virtqueues (the default is 1).
39
qemu_img('create', '-f', iotests.imgfmt, back0_img, '1440K')
34
+ ``queue-size`` sets the virtqueue descriptor table size (the default is 256).
40
@@ -XXX,XX +XXX,XX @@ class BaseClass(iotests.QMPTestCase):
41
'file': {'driver': 'file',
42
'filename': source_img}}
43
self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev))
44
- self.vm.add_device('virtio-blk,id=qdev0,drive=source')
45
+
35
+
46
+ if self.use_iothread:
36
+ The instantiated VDUSE device must then be added to the vDPA bus using the
47
+ self.vm.add_object('iothread,id=iothread0')
37
+ vdpa(8) command from the iproute2 project::
48
+ iothread = ",iothread=iothread0"
49
+ else:
50
+ iothread = ""
51
+
38
+
52
+ self.vm.add_device('virtio-scsi%s' % iothread)
39
+ # vdpa dev add name <id> mgmtdev vduse
53
+ self.vm.add_device('scsi-hd,id=qdev0,drive=source')
54
+
40
+
55
self.vm.launch()
41
+ The device can be removed from the vDPA bus later as follows::
56
57
self.assertIntactSourceBackingChain()
58
@@ -XXX,XX +XXX,XX @@ class MirrorBaseClass(BaseClass):
59
def testFull(self):
60
self.runMirror('full')
61
62
- node = self.findBlockNode('target',
63
- '/machine/peripheral/qdev0/virtio-backend')
64
+ node = self.findBlockNode('target', 'qdev0')
65
self.assertCorrectBackingImage(node, None)
66
self.assertIntactSourceBackingChain()
67
68
def testTop(self):
69
self.runMirror('top')
70
71
- node = self.findBlockNode('target',
72
- '/machine/peripheral/qdev0/virtio-backend')
73
+ node = self.findBlockNode('target', 'qdev0')
74
self.assertCorrectBackingImage(node, back2_img)
75
self.assertIntactSourceBackingChain()
76
77
def testNone(self):
78
self.runMirror('none')
79
80
- node = self.findBlockNode('target',
81
- '/machine/peripheral/qdev0/virtio-backend')
82
+ node = self.findBlockNode('target', 'qdev0')
83
self.assertCorrectBackingImage(node, source_img)
84
self.assertIntactSourceBackingChain()
85
86
@@ -XXX,XX +XXX,XX @@ class TestBlockdevMirrorReopen(MirrorBaseClass):
87
backing="backing")
88
self.assert_qmp(result, 'return', {})
89
90
+class TestBlockdevMirrorReopenIothread(TestBlockdevMirrorReopen):
91
+ use_iothread = True
92
+
42
+
93
# Attach the backing chain only during completion, with blockdev-snapshot
43
+ # vdpa dev del <id>
94
class TestBlockdevMirrorSnapshot(MirrorBaseClass):
95
cmd = 'blockdev-mirror'
96
@@ -XXX,XX +XXX,XX @@ class TestBlockdevMirrorSnapshot(MirrorBaseClass):
97
overlay="target")
98
self.assert_qmp(result, 'return', {})
99
100
+class TestBlockdevMirrorSnapshotIothread(TestBlockdevMirrorSnapshot):
101
+ use_iothread = True
102
+
44
+
103
class TestCommit(BaseClass):
45
+ For more information about attaching vDPA devices to the host with
104
existing = False
46
+ virtio_vdpa.ko or attaching them to guests with vhost_vdpa.ko, see
105
47
+ https://vdpa-dev.gitlab.io/.
106
@@ -XXX,XX +XXX,XX @@ class TestCommit(BaseClass):
48
+
107
49
+ For more information about VDUSE, see
108
self.vm.event_wait('BLOCK_JOB_COMPLETED')
50
+ https://docs.kernel.org/userspace-api/vduse.html.
109
51
+
110
- node = self.findBlockNode(None,
52
.. option:: --monitor MONITORDEF
111
- '/machine/peripheral/qdev0/virtio-backend')
53
112
+ node = self.findBlockNode(None, 'qdev0')
54
is a QMP monitor definition. See the :manpage:`qemu(1)` manual page for
113
self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename',
55
diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c
114
back1_img)
115
self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename',
116
diff --git a/tests/qemu-iotests/155.out b/tests/qemu-iotests/155.out
117
index XXXXXXX..XXXXXXX 100644
56
index XXXXXXX..XXXXXXX 100644
118
--- a/tests/qemu-iotests/155.out
57
--- a/storage-daemon/qemu-storage-daemon.c
119
+++ b/tests/qemu-iotests/155.out
58
+++ b/storage-daemon/qemu-storage-daemon.c
120
@@ -XXX,XX +XXX,XX @@
59
@@ -XXX,XX +XXX,XX @@ static void help(void)
121
-.........................
60
" vhost-user-blk device over file descriptor\n"
122
+...............................
61
"\n"
123
----------------------------------------------------------------------
62
#endif /* CONFIG_VHOST_USER_BLK_SERVER */
124
-Ran 25 tests
63
+#ifdef CONFIG_VDUSE_BLK_EXPORT
125
+Ran 31 tests
64
+" --export [type=]vduse-blk,id=<id>,node-name=<node-name>\n"
126
65
+" [,writable=on|off][,num-queues=<num-queues>]\n"
127
OK
66
+" [,queue-size=<queue-size>]\n"
67
+" [,logical-block-size=<logical-block-size>]\n"
68
+" export the specified block node as a vduse-blk\n"
69
+" device using the id as the VDUSE device name\n"
70
+"\n"
71
+#endif /* CONFIG_VDUSE_BLK_EXPORT */
72
" --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n"
73
" configure a QMP monitor\n"
74
"\n"
128
--
75
--
129
2.20.1
76
2.35.3
130
131
diff view generated by jsdifflib
1
From: Daniel Henrique Barboza <danielhb413@gmail.com>
1
From: Stefano Garzarella <sgarzare@redhat.com>
2
2
3
When using a non-UTF8 secret to create a volume using qemu-img, the
3
If the namespace does not exist, rbd_create() fails with -ENOENT and
4
following error happens:
4
QEMU reports a generic "error rbd create: No such file or directory":
5
5
6
$ qemu-img create -f luks --object secret,id=vol_1_encrypt0,file=vol_resize_pool.vol_1.secret.qzVQrI -o key-secret=vol_1_encrypt0 /var/tmp/pool_target/vol_1 10240K
6
$ qemu-img create rbd:rbd/namespace/image 1M
7
Formatting 'rbd:rbd/namespace/image', fmt=raw size=1048576
8
qemu-img: rbd:rbd/namespace/image: error rbd create: No such file or directory
7
9
8
Formatting '/var/tmp/pool_target/vol_1', fmt=luks size=10485760 key-secret=vol_1_encrypt0
10
Unfortunately rados_ioctx_set_namespace() does not fail if the namespace
9
qemu-img: /var/tmp/pool_target/vol_1: Data from secret vol_1_encrypt0 is not valid UTF-8
11
does not exist, so let's use rbd_namespace_exists() in qemu_rbd_connect()
12
to check if the namespace exists, reporting a more understandable error:
10
13
11
However, the created file '/var/tmp/pool_target/vol_1' is left behind in the
14
$ qemu-img create rbd:rbd/namespace/image 1M
12
file system after the failure. This behavior can be observed when creating
15
Formatting 'rbd:rbd/namespace/image', fmt=raw size=1048576
13
the volume using Libvirt, via 'virsh vol-create', and then getting "volume
16
qemu-img: rbd:rbd/namespace/image: namespace 'namespace' does not exist
14
target path already exist" errors when trying to re-create the volume.
15
17
16
The volume file is created inside block_crypto_co_create_opts_luks(), in
18
Reported-by: Tingting Mao <timao@redhat.com>
17
block/crypto.c. If the bdrv_create_file() call is successful but any
19
Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
18
succeeding step fails*, the existing 'fail' label does not take into
20
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
19
account the created file, leaving it behind.
21
Message-Id: <20220517071012.6120-1-sgarzare@redhat.com>
20
21
This patch changes block_crypto_co_create_opts_luks() to delete
22
'filename' in case of failure. A failure in this point means that
23
the volume is now truncated/corrupted, so even if 'filename' was an
24
existing volume before calling qemu-img, it is now unusable. Deleting
25
the file it is not much worse than leaving it in the filesystem in
26
this scenario, and we don't have to deal with checking the file
27
pre-existence in the code.
28
29
* in our case, block_crypto_co_create_generic calls qcrypto_block_create,
30
which calls qcrypto_block_luks_create, and this function fails when
31
calling qcrypto_secret_lookup_as_utf8.
32
33
Reported-by: Srikanth Aithal <bssrikanth@in.ibm.com>
34
Suggested-by: Kevin Wolf <kwolf@redhat.com>
35
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
36
Message-Id: <20200130213907.2830642-4-danielhb413@gmail.com>
37
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
38
---
23
---
39
block/crypto.c | 18 ++++++++++++++++++
24
block/rbd.c | 24 ++++++++++++++++++++++++
40
1 file changed, 18 insertions(+)
25
meson.build | 6 ++++++
26
2 files changed, 30 insertions(+)
41
27
42
diff --git a/block/crypto.c b/block/crypto.c
28
diff --git a/block/rbd.c b/block/rbd.c
43
index XXXXXXX..XXXXXXX 100644
29
index XXXXXXX..XXXXXXX 100644
44
--- a/block/crypto.c
30
--- a/block/rbd.c
45
+++ b/block/crypto.c
31
+++ b/block/rbd.c
46
@@ -XXX,XX +XXX,XX @@
32
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
47
#include "qapi/error.h"
33
error_setg_errno(errp, -r, "error opening pool %s", opts->pool);
48
#include "qemu/module.h"
34
goto failed_shutdown;
49
#include "qemu/option.h"
35
}
50
+#include "qemu/cutils.h"
36
+
51
#include "crypto.h"
37
+#ifdef HAVE_RBD_NAMESPACE_EXISTS
52
38
+ if (opts->has_q_namespace && strlen(opts->q_namespace) > 0) {
53
typedef struct BlockCrypto BlockCrypto;
39
+ bool exists;
54
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
40
+
55
41
+ r = rbd_namespace_exists(*io_ctx, opts->q_namespace, &exists);
56
ret = 0;
42
+ if (r < 0) {
57
fail:
43
+ error_setg_errno(errp, -r, "error checking namespace");
58
+ /*
44
+ goto failed_ioctx_destroy;
59
+ * If an error occurred, delete 'filename'. Even if the file existed
45
+ }
60
+ * beforehand, it has been truncated and corrupted in the process.
46
+
61
+ */
47
+ if (!exists) {
62
+ if (ret && bs) {
48
+ error_setg(errp, "namespace '%s' does not exist",
63
+ Error *local_delete_err = NULL;
49
+ opts->q_namespace);
64
+ int r_del = bdrv_co_delete_file(bs, &local_delete_err);
50
+ r = -ENOENT;
65
+ /*
51
+ goto failed_ioctx_destroy;
66
+ * ENOTSUP will happen if the block driver doesn't support
67
+ * the 'bdrv_co_delete_file' interface. This is a predictable
68
+ * scenario and shouldn't be reported back to the user.
69
+ */
70
+ if ((r_del < 0) && (r_del != -ENOTSUP)) {
71
+ error_report_err(local_delete_err);
72
+ }
52
+ }
73
+ }
53
+ }
54
+#endif
74
+
55
+
75
bdrv_unref(bs);
56
/*
76
qapi_free_QCryptoBlockCreateOptions(create_opts);
57
* Set the namespace after opening the io context on the pool,
77
qobject_unref(cryptoopts);
58
* if nspace == NULL or if nspace == "", it is just as we did nothing
59
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
60
r = 0;
61
goto out;
62
63
+#ifdef HAVE_RBD_NAMESPACE_EXISTS
64
+failed_ioctx_destroy:
65
+ rados_ioctx_destroy(*io_ctx);
66
+#endif
67
failed_shutdown:
68
rados_shutdown(*cluster);
69
out:
70
diff --git a/meson.build b/meson.build
71
index XXXXXXX..XXXXXXX 100644
72
--- a/meson.build
73
+++ b/meson.build
74
@@ -XXX,XX +XXX,XX @@ config_host_data.set('HAVE_GETIFADDRS', cc.has_function('getifaddrs'))
75
config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util))
76
config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul'))
77
config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include <stdlib.h>'))
78
+if rbd.found()
79
+ config_host_data.set('HAVE_RBD_NAMESPACE_EXISTS',
80
+ cc.has_function('rbd_namespace_exists',
81
+ dependencies: rbd,
82
+ prefix: '#include <rbd/librbd.h>'))
83
+endif
84
if rdma.found()
85
config_host_data.set('HAVE_IBV_ADVISE_MR',
86
cc.has_function('ibv_advise_mr',
78
--
87
--
79
2.20.1
88
2.35.3
80
81
diff view generated by jsdifflib
1
From: Daniel Henrique Barboza <danielhb413@gmail.com>
1
From: Fabian Ebner <f.ebner@proxmox.com>
2
2
3
Adding to Block Drivers the capability of being able to clean up
3
On 64-bit platforms, assigning SIZE_MAX to the int64_t max_pdiscard
4
its created files can be useful in certain situations. For the
4
results in a negative value, and the following assertion would trigger
5
LUKS driver, for instance, a failure in one of its authentication
5
down the line (it's not the same max_pdiscard, but computed from the
6
steps can leave files in the host that weren't there before.
6
other one):
7
qemu-system-x86_64: ../block/io.c:3166: bdrv_co_pdiscard: Assertion
8
`max_pdiscard >= bs->bl.request_alignment' failed.
7
9
8
This patch adds the 'bdrv_co_delete_file' interface to block
10
On 32-bit platforms, it's fine to keep using SIZE_MAX.
9
drivers and add it to the 'file' driver in file-posix.c. The
10
implementation is given by 'raw_co_delete_file'.
11
11
12
Suggested-by: Daniel P. Berrangé <berrange@redhat.com>
12
The assertion in qemu_gluster_co_pdiscard() is checking that the value
13
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
13
of 'bytes' can safely be passed to glfs_discard_async(), which takes a
14
Message-Id: <20200130213907.2830642-2-danielhb413@gmail.com>
14
size_t for the argument in question, so it is kept as is. And since
15
max_pdiscard is still <= SIZE_MAX, relying on max_pdiscard is still
16
fine.
17
18
Fixes: 0c8022876f ("block: use int64_t instead of int in driver discard handlers")
19
Cc: qemu-stable@nongnu.org
20
Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
21
Message-Id: <20220520075922.43972-1-f.ebner@proxmox.com>
22
Reviewed-by: Eric Blake <eblake@redhat.com>
23
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
24
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
---
26
---
17
include/block/block_int.h | 4 ++++
27
block/gluster.c | 2 +-
18
block/file-posix.c | 23 +++++++++++++++++++++++
28
1 file changed, 1 insertion(+), 1 deletion(-)
19
2 files changed, 27 insertions(+)
20
29
21
diff --git a/include/block/block_int.h b/include/block/block_int.h
30
diff --git a/block/gluster.c b/block/gluster.c
22
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/block_int.h
32
--- a/block/gluster.c
24
+++ b/include/block/block_int.h
33
+++ b/block/gluster.c
25
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
34
@@ -XXX,XX +XXX,XX @@ out:
26
*/
35
static void qemu_gluster_refresh_limits(BlockDriverState *bs, Error **errp)
27
int coroutine_fn (*bdrv_co_flush)(BlockDriverState *bs);
36
{
28
37
bs->bl.max_transfer = GLUSTER_MAX_TRANSFER;
29
+ /* Delete a created file. */
38
- bs->bl.max_pdiscard = SIZE_MAX;
30
+ int coroutine_fn (*bdrv_co_delete_file)(BlockDriverState *bs,
39
+ bs->bl.max_pdiscard = MIN(SIZE_MAX, INT64_MAX);
31
+ Error **errp);
32
+
33
/*
34
* Flushes all data that was already written to the OS all the way down to
35
* the disk (for example file-posix.c calls fsync()).
36
diff --git a/block/file-posix.c b/block/file-posix.c
37
index XXXXXXX..XXXXXXX 100644
38
--- a/block/file-posix.c
39
+++ b/block/file-posix.c
40
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
41
return raw_co_create(&options, errp);
42
}
40
}
43
41
44
+static int coroutine_fn raw_co_delete_file(BlockDriverState *bs,
42
static int qemu_gluster_reopen_prepare(BDRVReopenState *state,
45
+ Error **errp)
46
+{
47
+ struct stat st;
48
+ int ret;
49
+
50
+ if (!(stat(bs->filename, &st) == 0) || !S_ISREG(st.st_mode)) {
51
+ error_setg_errno(errp, ENOENT, "%s is not a regular file",
52
+ bs->filename);
53
+ return -ENOENT;
54
+ }
55
+
56
+ ret = unlink(bs->filename);
57
+ if (ret < 0) {
58
+ ret = -errno;
59
+ error_setg_errno(errp, -ret, "Error when deleting file %s",
60
+ bs->filename);
61
+ }
62
+
63
+ return ret;
64
+}
65
+
66
/*
67
* Find allocation range in @bs around offset @start.
68
* May change underlying file descriptor's file offset.
69
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
70
.bdrv_co_block_status = raw_co_block_status,
71
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
72
.bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes,
73
+ .bdrv_co_delete_file = raw_co_delete_file,
74
75
.bdrv_co_preadv = raw_co_preadv,
76
.bdrv_co_pwritev = raw_co_pwritev,
77
--
43
--
78
2.20.1
44
2.35.3
79
80
diff view generated by jsdifflib
1
The newly tested scenario is a common live storage migration scenario:
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
The target node is opened without a backing file so that the active
3
layer is mirrored while its backing chain can be copied in the
4
background.
5
2
6
The backing chain should be attached to the mirror target node when
3
It seems that aio_wait_kick always required a memory barrier
7
finalising the job, just before switching the users of the source node
4
or atomic operation in the caller, but nobody actually
8
to the new copy (at which point the mirror job still has a reference to
5
took care of doing it.
9
the node). drive-mirror did this automatically, but with blockdev-mirror
10
this is the job of the QMP client.
11
6
12
This patch adds test cases for two ways to achieve the desired result,
7
Let's put the barrier in the function instead, and pair it
13
using either x-blockdev-reopen or blockdev-snapshot.
8
with another one in AIO_WAIT_WHILE. Read aio_wait_kick()
9
comment for further explanation.
14
10
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
16
Message-Id: <20200310113831.27293-5-kwolf@redhat.com>
12
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
17
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
13
Message-Id: <20220524173054.12651-1-eesposit@redhat.com>
14
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
17
---
20
tests/qemu-iotests/155 | 56 ++++++++++++++++++++++++++++++++++----
18
include/block/aio-wait.h | 2 ++
21
tests/qemu-iotests/155.out | 4 +--
19
util/aio-wait.c | 16 +++++++++++++++-
22
2 files changed, 53 insertions(+), 7 deletions(-)
20
2 files changed, 17 insertions(+), 1 deletion(-)
23
21
24
diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155
22
diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h
25
index XXXXXXX..XXXXXXX 100755
23
index XXXXXXX..XXXXXXX 100644
26
--- a/tests/qemu-iotests/155
24
--- a/include/block/aio-wait.h
27
+++ b/tests/qemu-iotests/155
25
+++ b/include/block/aio-wait.h
28
@@ -XXX,XX +XXX,XX @@ target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt)
26
@@ -XXX,XX +XXX,XX @@ extern AioWait global_aio_wait;
29
# image during runtime, only makes sense if
27
AioContext *ctx_ = (ctx); \
30
# target_blockdev_backing is not None
28
/* Increment wait_->num_waiters before evaluating cond. */ \
31
# (None: same as target_backing)
29
qatomic_inc(&wait_->num_waiters); \
32
+# target_open_with_backing: If True, the target image is added with its backing
30
+ /* Paired with smp_mb in aio_wait_kick(). */ \
33
+# chain opened right away. If False, blockdev-add
31
+ smp_mb(); \
34
+# opens it without a backing file and job completion
32
if (ctx_ && in_aio_context_home_thread(ctx_)) { \
35
+# is supposed to open the backing chain.
33
while ((cond)) { \
36
34
aio_poll(ctx_, true); \
37
class BaseClass(iotests.QMPTestCase):
35
diff --git a/util/aio-wait.c b/util/aio-wait.c
38
target_blockdev_backing = None
36
index XXXXXXX..XXXXXXX 100644
39
target_real_backing = None
37
--- a/util/aio-wait.c
40
+ target_open_with_backing = True
38
+++ b/util/aio-wait.c
41
39
@@ -XXX,XX +XXX,XX @@ static void dummy_bh_cb(void *opaque)
42
def setUp(self):
40
43
qemu_img('create', '-f', iotests.imgfmt, back0_img, '1440K')
41
void aio_wait_kick(void)
44
@@ -XXX,XX +XXX,XX @@ class BaseClass(iotests.QMPTestCase):
42
{
45
options = { 'node-name': 'target',
43
- /* The barrier (or an atomic op) is in the caller. */
46
'driver': iotests.imgfmt,
44
+ /*
47
'file': { 'driver': 'file',
45
+ * Paired with smp_mb in AIO_WAIT_WHILE. Here we have:
48
+ 'node-name': 'target-file',
46
+ * write(condition);
49
'filename': target_img } }
47
+ * aio_wait_kick() {
50
- if self.target_blockdev_backing:
48
+ * smp_mb();
51
- options['backing'] = self.target_blockdev_backing
49
+ * read(num_waiters);
50
+ * }
51
+ *
52
+ * And in AIO_WAIT_WHILE:
53
+ * write(num_waiters);
54
+ * smp_mb();
55
+ * read(condition);
56
+ */
57
+ smp_mb();
52
+
58
+
53
+ if not self.target_open_with_backing:
59
if (qatomic_read(&global_aio_wait.num_waiters)) {
54
+ options['backing'] = None
60
aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL);
55
+ elif self.target_blockdev_backing:
61
}
56
+ options['backing'] = self.target_blockdev_backing
57
58
result = self.vm.qmp('blockdev-add', **options)
59
self.assert_qmp(result, 'return', {})
60
@@ -XXX,XX +XXX,XX @@ class BaseClass(iotests.QMPTestCase):
61
# cmd: Mirroring command to execute, either drive-mirror or blockdev-mirror
62
63
class MirrorBaseClass(BaseClass):
64
+ def openBacking(self):
65
+ pass
66
+
67
def runMirror(self, sync):
68
if self.cmd == 'blockdev-mirror':
69
result = self.vm.qmp(self.cmd, job_id='mirror-job', device='source',
70
- sync=sync, target='target')
71
+ sync=sync, target='target',
72
+ auto_finalize=False)
73
else:
74
if self.existing:
75
mode = 'existing'
76
@@ -XXX,XX +XXX,XX @@ class MirrorBaseClass(BaseClass):
77
result = self.vm.qmp(self.cmd, job_id='mirror-job', device='source',
78
sync=sync, target=target_img,
79
format=iotests.imgfmt, mode=mode,
80
- node_name='target')
81
+ node_name='target', auto_finalize=False)
82
83
self.assert_qmp(result, 'return', {})
84
85
- self.complete_and_wait('mirror-job')
86
+ self.vm.run_job('mirror-job', use_log=False, auto_finalize=False,
87
+ pre_finalize=self.openBacking, auto_dismiss=True)
88
89
def testFull(self):
90
self.runMirror('full')
91
@@ -XXX,XX +XXX,XX @@ class TestBlockdevMirrorForcedBacking(MirrorBaseClass):
92
target_blockdev_backing = { 'driver': 'null-co' }
93
target_real_backing = 'null-co://'
94
95
+# Attach the backing chain only during completion, with blockdev-reopen
96
+class TestBlockdevMirrorReopen(MirrorBaseClass):
97
+ cmd = 'blockdev-mirror'
98
+ existing = True
99
+ target_backing = 'null-co://'
100
+ target_open_with_backing = False
101
+
102
+ def openBacking(self):
103
+ if not self.target_open_with_backing:
104
+ result = self.vm.qmp('blockdev-add', node_name="backing",
105
+ driver="null-co")
106
+ self.assert_qmp(result, 'return', {})
107
+ result = self.vm.qmp('x-blockdev-reopen', node_name="target",
108
+ driver=iotests.imgfmt, file="target-file",
109
+ backing="backing")
110
+ self.assert_qmp(result, 'return', {})
111
+
112
+# Attach the backing chain only during completion, with blockdev-snapshot
113
+class TestBlockdevMirrorSnapshot(MirrorBaseClass):
114
+ cmd = 'blockdev-mirror'
115
+ existing = True
116
+ target_backing = 'null-co://'
117
+ target_open_with_backing = False
118
+
119
+ def openBacking(self):
120
+ if not self.target_open_with_backing:
121
+ result = self.vm.qmp('blockdev-add', node_name="backing",
122
+ driver="null-co")
123
+ self.assert_qmp(result, 'return', {})
124
+ result = self.vm.qmp('blockdev-snapshot', node="backing",
125
+ overlay="target")
126
+ self.assert_qmp(result, 'return', {})
127
128
class TestCommit(BaseClass):
129
existing = False
130
diff --git a/tests/qemu-iotests/155.out b/tests/qemu-iotests/155.out
131
index XXXXXXX..XXXXXXX 100644
132
--- a/tests/qemu-iotests/155.out
133
+++ b/tests/qemu-iotests/155.out
134
@@ -XXX,XX +XXX,XX @@
135
-...................
136
+.........................
137
----------------------------------------------------------------------
138
-Ran 19 tests
139
+Ran 25 tests
140
141
OK
142
--
62
--
143
2.20.1
63
2.35.3
144
145
diff view generated by jsdifflib
1
From: Pan Nengyuan <pannengyuan@huawei.com>
1
From: Eric Blake <eblake@redhat.com>
2
2
3
'type/id' forgot to free in qmp_object_add, this patch fix that.
3
CID 1488362 points out that the second 'rc >= 0' check is now dead
4
code.
4
5
5
The leak stack:
6
Reported-by: Peter Maydell <peter.maydell@linaro.org>
6
Direct leak of 84 byte(s) in 6 object(s) allocated from:
7
Fixes: 172f5f1a40(nbd: remove peppering of nbd_client_connected)
7
#0 0x7fe2a5ebf768 in __interceptor_malloc (/lib64/libasan.so.5+0xef768)
8
Signed-off-by: Eric Blake <eblake@redhat.com>
8
#1 0x7fe2a5044445 in g_malloc (/lib64/libglib-2.0.so.0+0x52445)
9
Message-Id: <20220516210519.76135-1-eblake@redhat.com>
9
#2 0x7fe2a505dd92 in g_strdup (/lib64/libglib-2.0.so.0+0x6bd92)
10
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
10
#3 0x56344954e692 in qmp_object_add /mnt/sdb/qemu-new/qemu_test/qemu/qom/qom-qmp-cmds.c:258
11
Reviewed-by: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
11
#4 0x563449960f5a in do_qmp_dispatch /mnt/sdb/qemu-new/qemu_test/qemu/qapi/qmp-dispatch.c:132
12
#5 0x563449960f5a in qmp_dispatch /mnt/sdb/qemu-new/qemu_test/qemu/qapi/qmp-dispatch.c:175
13
#6 0x563449498a30 in monitor_qmp_dispatch /mnt/sdb/qemu-new/qemu_test/qemu/monitor/qmp.c:145
14
#7 0x56344949a64f in monitor_qmp_bh_dispatcher /mnt/sdb/qemu-new/qemu_test/qemu/monitor/qmp.c:234
15
#8 0x563449a92a3a in aio_bh_call /mnt/sdb/qemu-new/qemu_test/qemu/util/async.c:136
16
17
Direct leak of 54 byte(s) in 6 object(s) allocated from:
18
#0 0x7fe2a5ebf768 in __interceptor_malloc (/lib64/libasan.so.5+0xef768)
19
#1 0x7fe2a5044445 in g_malloc (/lib64/libglib-2.0.so.0+0x52445)
20
#2 0x7fe2a505dd92 in g_strdup (/lib64/libglib-2.0.so.0+0x6bd92)
21
#3 0x56344954e6c4 in qmp_object_add /mnt/sdb/qemu-new/qemu_test/qemu/qom/qom-qmp-cmds.c:267
22
#4 0x563449960f5a in do_qmp_dispatch /mnt/sdb/qemu-new/qemu_test/qemu/qapi/qmp-dispatch.c:132
23
#5 0x563449960f5a in qmp_dispatch /mnt/sdb/qemu-new/qemu_test/qemu/qapi/qmp-dispatch.c:175
24
#6 0x563449498a30 in monitor_qmp_dispatch /mnt/sdb/qemu-new/qemu_test/qemu/monitor/qmp.c:145
25
#7 0x56344949a64f in monitor_qmp_bh_dispatcher /mnt/sdb/qemu-new/qemu_test/qemu/monitor/qmp.c:234
26
#8 0x563449a92a3a in aio_bh_call /mnt/sdb/qemu-new/qemu_test/qemu/util/async.c:136
27
28
Fixes: 5f07c4d60d091320186e7b0edaf9ed2cc16b2d1e
29
Reported-by: Euler Robot <euler.robot@huawei.com>
30
Signed-off-by: Pan Nengyuan <pannengyuan@huawei.com>
31
Message-Id: <20200310064640.5059-1-pannengyuan@huawei.com>
32
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
33
Acked-by: Igor Mammedov <imammedo@redhat.com>
34
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
35
---
13
---
36
qom/qom-qmp-cmds.c | 16 ++++++----------
14
block/nbd.c | 8 ++------
37
1 file changed, 6 insertions(+), 10 deletions(-)
15
1 file changed, 2 insertions(+), 6 deletions(-)
38
16
39
diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
17
diff --git a/block/nbd.c b/block/nbd.c
40
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
41
--- a/qom/qom-qmp-cmds.c
19
--- a/block/nbd.c
42
+++ b/qom/qom-qmp-cmds.c
20
+++ b/block/nbd.c
43
@@ -XXX,XX +XXX,XX @@ void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp)
21
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn nbd_co_send_request(BlockDriverState *bs,
44
QDict *pdict;
22
if (qiov) {
45
Visitor *v;
23
qio_channel_set_cork(s->ioc, true);
46
Object *obj;
24
rc = nbd_send_request(s->ioc, request);
47
- const char *type;
25
- if (rc >= 0) {
48
- const char *id;
26
- if (qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov,
49
+ g_autofree char *type = NULL;
27
- NULL) < 0) {
50
+ g_autofree char *id = NULL;
28
- rc = -EIO;
51
29
- }
52
- type = qdict_get_try_str(qdict, "qom-type");
30
- } else if (rc >= 0) {
53
+ type = g_strdup(qdict_get_try_str(qdict, "qom-type"));
31
+ if (rc >= 0 && qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov,
54
if (!type) {
32
+ NULL) < 0) {
55
error_setg(errp, QERR_MISSING_PARAMETER, "qom-type");
33
rc = -EIO;
56
return;
34
}
57
- } else {
35
qio_channel_set_cork(s->ioc, false);
58
- type = g_strdup(type);
59
- qdict_del(qdict, "qom-type");
60
}
61
+ qdict_del(qdict, "qom-type");
62
63
- id = qdict_get_try_str(qdict, "id");
64
+ id = g_strdup(qdict_get_try_str(qdict, "id"));
65
if (!id) {
66
error_setg(errp, QERR_MISSING_PARAMETER, "id");
67
return;
68
- } else {
69
- id = g_strdup(id);
70
- qdict_del(qdict, "id");
71
}
72
+ qdict_del(qdict, "id");
73
74
props = qdict_get(qdict, "props");
75
if (props) {
76
--
36
--
77
2.20.1
37
2.35.3
78
79
diff view generated by jsdifflib