1
The following changes since commit 23919ddfd56135cad3cb468a8f54d5a595f024f4:
1
The following changes since commit 9ac5df20f51fabcba0d902025df4bd7ea987c158:
2
2
3
Merge remote-tracking branch 'remotes/aperard/tags/pull-xen-20190827' into staging (2019-08-27 15:52:36 +0100)
3
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20200221-1' into staging (2020-02-21 16:18:38 +0000)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
https://github.com/XanClic/qemu.git tags/pull-block-2019-08-27
7
https://github.com/stefanha/qemu.git tags/block-pull-request
8
8
9
for you to fetch changes up to bb043c056cffcc2f3ce88bfdaf2e76e455c09e2c:
9
for you to fetch changes up to e5c59355ae9f724777c61c859292ec9db2c8c2ab:
10
10
11
iotests: Unify cache mode quoting (2019-08-27 19:48:44 +0200)
11
fuzz: add documentation to docs/devel/ (2020-02-22 08:26:48 +0000)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block patches:
14
Pull request
15
- qemu-io now accepts a file to read a write pattern from
15
16
- Ensure that raw files have their first block allocated so we can probe
16
This pull request contains a virtio-blk/scsi performance optimization, event
17
the O_DIRECT alignment if necessary
17
loop scalability improvements, and a qtest-based device fuzzing framework. I
18
- Various fixes
18
am including the fuzzing patches because I have reviewed them and Thomas Huth
19
is currently away on leave.
19
20
20
----------------------------------------------------------------
21
----------------------------------------------------------------
22
23
Alexander Bulekov (22):
24
softmmu: move vl.c to softmmu/
25
softmmu: split off vl.c:main() into main.c
26
module: check module wasn't already initialized
27
fuzz: add FUZZ_TARGET module type
28
qtest: add qtest_server_send abstraction
29
libqtest: add a layer of abstraction to send/recv
30
libqtest: make bufwrite rely on the TransportOps
31
qtest: add in-process incoming command handler
32
libqos: rename i2c_send and i2c_recv
33
libqos: split qos-test and libqos makefile vars
34
libqos: move useful qos-test funcs to qos_external
35
fuzz: add fuzzer skeleton
36
exec: keep ram block across fork when using qtest
37
main: keep rcu_atfork callback enabled for qtest
38
fuzz: support for fork-based fuzzing.
39
fuzz: add support for qos-assisted fuzz targets
40
fuzz: add target/fuzz makefile rules
41
fuzz: add configure flag --enable-fuzzing
42
fuzz: add i440fx fuzz targets
43
fuzz: add virtio-net fuzz target
44
fuzz: add virtio-scsi fuzz target
45
fuzz: add documentation to docs/devel/
46
21
Denis Plotnikov (1):
47
Denis Plotnikov (1):
22
qemu-io: add pattern file for write command
48
virtio: increase virtqueue size for virtio-scsi and virtio-blk
23
49
24
Max Reitz (7):
50
Paolo Bonzini (1):
25
iotests: Fix _filter_img_create()
51
rcu_queue: add QSLIST functions
26
vmdk: Use bdrv_dirname() for relative extent paths
27
iotests: Keep testing broken relative extent paths
28
vmdk: Reject invalid compressed writes
29
iotests: Disable broken streamOptimized tests
30
iotests: Disable 110 for vmdk.twoGbMaxExtentSparse
31
iotests: Disable 126 for flat vmdk subformats
32
52
33
Nir Soffer (3):
53
Stefan Hajnoczi (7):
34
block: posix: Always allocate the first block
54
aio-posix: avoid reacquiring rcu_read_lock() when polling
35
iotests: Test allocate_first_block() with O_DIRECT
55
util/async: make bh_aio_poll() O(1)
36
iotests: Unify cache mode quoting
56
aio-posix: fix use after leaving scope in aio_poll()
57
aio-posix: don't pass ns timeout to epoll_wait()
58
qemu/queue.h: add QLIST_SAFE_REMOVE()
59
aio-posix: make AioHandler deletion O(1)
60
aio-posix: make AioHandler dispatch O(1) with epoll
37
61
38
Stefan Hajnoczi (1):
62
MAINTAINERS | 11 +-
39
file-posix: fix request_alignment typo
63
Makefile | 15 +-
40
64
Makefile.objs | 2 -
41
Thomas Huth (2):
65
Makefile.target | 19 ++-
42
iotests: Check for enabled drivers before testing them
66
block.c | 5 +-
43
tests/check-block: Skip iotests when sanitizers are enabled
67
chardev/spice.c | 4 +-
44
68
configure | 39 +++++
45
Vladimir Sementsov-Ogievskiy (1):
69
docs/devel/fuzzing.txt | 116 ++++++++++++++
46
block: fix permission update in bdrv_replace_node
70
exec.c | 12 +-
47
71
hw/block/virtio-blk.c | 2 +-
48
block.c | 5 +-
72
hw/core/machine.c | 2 +
49
block/file-posix.c | 53 +++++++++-
73
hw/scsi/virtio-scsi.c | 2 +-
50
block/vmdk.c | 64 ++++++++----
74
include/block/aio.h | 26 ++-
51
qemu-io-cmds.c | 99 +++++++++++++++++--
75
include/qemu/module.h | 4 +-
52
tests/check-block.sh | 5 +
76
include/qemu/queue.h | 32 +++-
53
tests/qemu-iotests/002 | 1 +
77
include/qemu/rcu_queue.h | 47 ++++++
54
tests/qemu-iotests/003 | 1 +
78
include/sysemu/qtest.h | 4 +
55
tests/qemu-iotests/005 | 3 +-
79
include/sysemu/sysemu.h | 4 +
56
tests/qemu-iotests/009 | 1 +
80
qtest.c | 31 +++-
57
tests/qemu-iotests/010 | 1 +
81
scripts/checkpatch.pl | 2 +-
58
tests/qemu-iotests/011 | 1 +
82
scripts/get_maintainer.pl | 3 +-
59
tests/qemu-iotests/017 | 3 +-
83
softmmu/Makefile.objs | 3 +
60
tests/qemu-iotests/018 | 3 +-
84
softmmu/main.c | 53 +++++++
61
tests/qemu-iotests/019 | 3 +-
85
vl.c => softmmu/vl.c | 48 +++---
62
tests/qemu-iotests/020 | 3 +-
86
tests/Makefile.include | 2 +
63
tests/qemu-iotests/026 | 4 +-
87
tests/qtest/Makefile.include | 72 +++++----
64
tests/qemu-iotests/027 | 1 +
88
tests/qtest/fuzz/Makefile.include | 18 +++
65
tests/qemu-iotests/032 | 1 +
89
tests/qtest/fuzz/fork_fuzz.c | 55 +++++++
66
tests/qemu-iotests/033 | 1 +
90
tests/qtest/fuzz/fork_fuzz.h | 23 +++
67
tests/qemu-iotests/034 | 3 +-
91
tests/qtest/fuzz/fork_fuzz.ld | 37 +++++
68
tests/qemu-iotests/037 | 3 +-
92
tests/qtest/fuzz/fuzz.c | 179 +++++++++++++++++++++
69
tests/qemu-iotests/039 | 4 +-
93
tests/qtest/fuzz/fuzz.h | 95 +++++++++++
70
tests/qemu-iotests/052 | 2 +-
94
tests/qtest/fuzz/i440fx_fuzz.c | 193 ++++++++++++++++++++++
71
tests/qemu-iotests/059 | 34 ++++++-
95
tests/qtest/fuzz/qos_fuzz.c | 234 +++++++++++++++++++++++++++
72
tests/qemu-iotests/059.out | 26 +++--
96
tests/qtest/fuzz/qos_fuzz.h | 33 ++++
73
tests/qemu-iotests/063 | 3 +-
97
tests/qtest/fuzz/virtio_net_fuzz.c | 198 +++++++++++++++++++++++
74
tests/qemu-iotests/071 | 1 +
98
tests/qtest/fuzz/virtio_scsi_fuzz.c | 213 +++++++++++++++++++++++++
75
tests/qemu-iotests/072 | 1 +
99
tests/qtest/libqos/i2c.c | 10 +-
76
tests/qemu-iotests/081 | 4 +-
100
tests/qtest/libqos/i2c.h | 4 +-
77
tests/qemu-iotests/091 | 4 +-
101
tests/qtest/libqos/qos_external.c | 168 ++++++++++++++++++++
78
tests/qemu-iotests/099 | 1 +
102
tests/qtest/libqos/qos_external.h | 28 ++++
79
tests/qemu-iotests/105 | 3 +-
103
tests/qtest/libqtest.c | 119 ++++++++++++--
80
tests/qemu-iotests/110 | 3 +-
104
tests/qtest/libqtest.h | 4 +
81
tests/qemu-iotests/120 | 1 +
105
tests/qtest/pca9552-test.c | 10 +-
82
tests/qemu-iotests/126 | 2 +
106
tests/qtest/qos-test.c | 132 +---------------
83
tests/qemu-iotests/{150.out => 150.out.qcow2} | 0
107
tests/test-aio.c | 3 +-
84
tests/qemu-iotests/150.out.raw | 12 +++
108
tests/test-rcu-list.c | 16 ++
85
tests/qemu-iotests/162 | 4 +-
109
tests/test-rcu-slist.c | 2 +
86
tests/qemu-iotests/175 | 47 +++++++--
110
util/aio-posix.c | 187 +++++++++++++++-------
87
tests/qemu-iotests/175.out | 16 ++-
111
util/async.c | 237 ++++++++++++++++------------
88
tests/qemu-iotests/178.out.qcow2 | 4 +-
112
util/module.c | 7 +
89
tests/qemu-iotests/184 | 1 +
113
51 files changed, 2365 insertions(+), 400 deletions(-)
90
tests/qemu-iotests/186 | 1 +
114
create mode 100644 docs/devel/fuzzing.txt
91
tests/qemu-iotests/197 | 1 +
115
create mode 100644 softmmu/Makefile.objs
92
tests/qemu-iotests/215 | 1 +
116
create mode 100644 softmmu/main.c
93
tests/qemu-iotests/221.out | 12 ++-
117
rename vl.c => softmmu/vl.c (99%)
94
tests/qemu-iotests/251 | 1 +
118
create mode 100644 tests/qtest/fuzz/Makefile.include
95
tests/qemu-iotests/253.out | 12 ++-
119
create mode 100644 tests/qtest/fuzz/fork_fuzz.c
96
tests/qemu-iotests/common.filter | 4 +-
120
create mode 100644 tests/qtest/fuzz/fork_fuzz.h
97
tests/qemu-iotests/common.rc | 14 +++
121
create mode 100644 tests/qtest/fuzz/fork_fuzz.ld
98
50 files changed, 391 insertions(+), 87 deletions(-)
122
create mode 100644 tests/qtest/fuzz/fuzz.c
99
rename tests/qemu-iotests/{150.out => 150.out.qcow2} (100%)
123
create mode 100644 tests/qtest/fuzz/fuzz.h
100
create mode 100644 tests/qemu-iotests/150.out.raw
124
create mode 100644 tests/qtest/fuzz/i440fx_fuzz.c
125
create mode 100644 tests/qtest/fuzz/qos_fuzz.c
126
create mode 100644 tests/qtest/fuzz/qos_fuzz.h
127
create mode 100644 tests/qtest/fuzz/virtio_net_fuzz.c
128
create mode 100644 tests/qtest/fuzz/virtio_scsi_fuzz.c
129
create mode 100644 tests/qtest/libqos/qos_external.c
130
create mode 100644 tests/qtest/libqos/qos_external.h
131
create mode 100644 tests/test-rcu-slist.c
101
132
102
--
133
--
103
2.21.0
134
2.24.1
104
135
105
diff view generated by jsdifflib
New patch
1
From: Denis Plotnikov <dplotnikov@virtuozzo.com>
1
2
3
The goal is to reduce the amount of requests issued by a guest on
4
1M reads/writes. This rises the performance up to 4% on that kind of
5
disk access pattern.
6
7
The maximum chunk size to be used for the guest disk accessing is
8
limited with seg_max parameter, which represents the max amount of
9
pices in the scatter-geather list in one guest disk request.
10
11
Since seg_max is virqueue_size dependent, increasing the virtqueue
12
size increases seg_max, which, in turn, increases the maximum size
13
of data to be read/write from a guest disk.
14
15
More details in the original problem statment:
16
https://lists.gnu.org/archive/html/qemu-devel/2017-12/msg03721.html
17
18
Suggested-by: Denis V. Lunev <den@openvz.org>
19
Signed-off-by: Denis Plotnikov <dplotnikov@virtuozzo.com>
20
Message-id: 20200214074648.958-1-dplotnikov@virtuozzo.com
21
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
22
---
23
hw/block/virtio-blk.c | 2 +-
24
hw/core/machine.c | 2 ++
25
hw/scsi/virtio-scsi.c | 2 +-
26
3 files changed, 4 insertions(+), 2 deletions(-)
27
28
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
29
index XXXXXXX..XXXXXXX 100644
30
--- a/hw/block/virtio-blk.c
31
+++ b/hw/block/virtio-blk.c
32
@@ -XXX,XX +XXX,XX @@ static Property virtio_blk_properties[] = {
33
DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0,
34
true),
35
DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1),
36
- DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 128),
37
+ DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 256),
38
DEFINE_PROP_BOOL("seg-max-adjust", VirtIOBlock, conf.seg_max_adjust, true),
39
DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD,
40
IOThread *),
41
diff --git a/hw/core/machine.c b/hw/core/machine.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/hw/core/machine.c
44
+++ b/hw/core/machine.c
45
@@ -XXX,XX +XXX,XX @@
46
#include "hw/mem/nvdimm.h"
47
48
GlobalProperty hw_compat_4_2[] = {
49
+ { "virtio-blk-device", "queue-size", "128"},
50
+ { "virtio-scsi-device", "virtqueue_size", "128"},
51
{ "virtio-blk-device", "x-enable-wce-if-config-wce", "off" },
52
{ "virtio-blk-device", "seg-max-adjust", "off"},
53
{ "virtio-scsi-device", "seg_max_adjust", "off"},
54
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
55
index XXXXXXX..XXXXXXX 100644
56
--- a/hw/scsi/virtio-scsi.c
57
+++ b/hw/scsi/virtio-scsi.c
58
@@ -XXX,XX +XXX,XX @@ static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp)
59
static Property virtio_scsi_properties[] = {
60
DEFINE_PROP_UINT32("num_queues", VirtIOSCSI, parent_obj.conf.num_queues, 1),
61
DEFINE_PROP_UINT32("virtqueue_size", VirtIOSCSI,
62
- parent_obj.conf.virtqueue_size, 128),
63
+ parent_obj.conf.virtqueue_size, 256),
64
DEFINE_PROP_BOOL("seg_max_adjust", VirtIOSCSI,
65
parent_obj.conf.seg_max_adjust, true),
66
DEFINE_PROP_UINT32("max_sectors", VirtIOSCSI, parent_obj.conf.max_sectors,
67
--
68
2.24.1
69
diff view generated by jsdifflib
New patch
1
The first rcu_read_lock/unlock() is expensive. Nested calls are cheap.
1
2
3
This optimization increases IOPS from 73k to 162k with a Linux guest
4
that has 2 virtio-blk,num-queues=1 and 99 virtio-blk,num-queues=32
5
devices.
6
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
9
Message-id: 20200218182708.914552-1-stefanha@redhat.com
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
12
util/aio-posix.c | 11 +++++++++++
13
1 file changed, 11 insertions(+)
14
15
diff --git a/util/aio-posix.c b/util/aio-posix.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/util/aio-posix.c
18
+++ b/util/aio-posix.c
19
@@ -XXX,XX +XXX,XX @@
20
21
#include "qemu/osdep.h"
22
#include "block/block.h"
23
+#include "qemu/rcu.h"
24
#include "qemu/rcu_queue.h"
25
#include "qemu/sockets.h"
26
#include "qemu/cutils.h"
27
@@ -XXX,XX +XXX,XX @@ static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout)
28
bool progress = false;
29
AioHandler *node;
30
31
+ /*
32
+ * Optimization: ->io_poll() handlers often contain RCU read critical
33
+ * sections and we therefore see many rcu_read_lock() -> rcu_read_unlock()
34
+ * -> rcu_read_lock() -> ... sequences with expensive memory
35
+ * synchronization primitives. Make the entire polling loop an RCU
36
+ * critical section because nested rcu_read_lock()/rcu_read_unlock() calls
37
+ * are cheap.
38
+ */
39
+ RCU_READ_LOCK_GUARD();
40
+
41
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
42
if (!node->deleted && node->io_poll &&
43
aio_node_check(ctx, node->is_external) &&
44
--
45
2.24.1
46
diff view generated by jsdifflib
New patch
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
2
3
QSLIST is the only family of lists for which we do not have RCU-friendly accessors,
4
add them.
5
6
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Message-id: 20200220103828.24525-1-pbonzini@redhat.com
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
11
include/qemu/queue.h | 15 +++++++++++--
12
include/qemu/rcu_queue.h | 47 ++++++++++++++++++++++++++++++++++++++++
13
tests/Makefile.include | 2 ++
14
tests/test-rcu-list.c | 16 ++++++++++++++
15
tests/test-rcu-slist.c | 2 ++
16
5 files changed, 80 insertions(+), 2 deletions(-)
17
create mode 100644 tests/test-rcu-slist.c
18
19
diff --git a/include/qemu/queue.h b/include/qemu/queue.h
20
index XXXXXXX..XXXXXXX 100644
21
--- a/include/qemu/queue.h
22
+++ b/include/qemu/queue.h
23
@@ -XXX,XX +XXX,XX @@ struct { \
24
(head)->slh_first = (head)->slh_first->field.sle_next; \
25
} while (/*CONSTCOND*/0)
26
27
-#define QSLIST_REMOVE_AFTER(slistelm, field) do { \
28
+#define QSLIST_REMOVE_AFTER(slistelm, field) do { \
29
(slistelm)->field.sle_next = \
30
- QSLIST_NEXT(QSLIST_NEXT((slistelm), field), field); \
31
+ QSLIST_NEXT(QSLIST_NEXT((slistelm), field), field); \
32
+} while (/*CONSTCOND*/0)
33
+
34
+#define QSLIST_REMOVE(head, elm, type, field) do { \
35
+ if ((head)->slh_first == (elm)) { \
36
+ QSLIST_REMOVE_HEAD((head), field); \
37
+ } else { \
38
+ struct type *curelm = (head)->slh_first; \
39
+ while (curelm->field.sle_next != (elm)) \
40
+ curelm = curelm->field.sle_next; \
41
+ curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \
42
+ } \
43
} while (/*CONSTCOND*/0)
44
45
#define QSLIST_FOREACH(var, head, field) \
46
diff --git a/include/qemu/rcu_queue.h b/include/qemu/rcu_queue.h
47
index XXXXXXX..XXXXXXX 100644
48
--- a/include/qemu/rcu_queue.h
49
+++ b/include/qemu/rcu_queue.h
50
@@ -XXX,XX +XXX,XX @@ extern "C" {
51
(var) && ((next) = atomic_rcu_read(&(var)->field.tqe_next), 1); \
52
(var) = (next))
53
54
+/*
55
+ * RCU singly-linked list
56
+ */
57
+
58
+/* Singly-linked list access methods */
59
+#define QSLIST_EMPTY_RCU(head) (atomic_read(&(head)->slh_first) == NULL)
60
+#define QSLIST_FIRST_RCU(head) atomic_rcu_read(&(head)->slh_first)
61
+#define QSLIST_NEXT_RCU(elm, field) atomic_rcu_read(&(elm)->field.sle_next)
62
+
63
+/* Singly-linked list functions */
64
+#define QSLIST_INSERT_HEAD_RCU(head, elm, field) do { \
65
+ (elm)->field.sle_next = (head)->slh_first; \
66
+ atomic_rcu_set(&(head)->slh_first, (elm)); \
67
+} while (/*CONSTCOND*/0)
68
+
69
+#define QSLIST_INSERT_AFTER_RCU(head, listelm, elm, field) do { \
70
+ (elm)->field.sle_next = (listelm)->field.sle_next; \
71
+ atomic_rcu_set(&(listelm)->field.sle_next, (elm)); \
72
+} while (/*CONSTCOND*/0)
73
+
74
+#define QSLIST_REMOVE_HEAD_RCU(head, field) do { \
75
+ atomic_set(&(head)->slh_first, (head)->slh_first->field.sle_next); \
76
+} while (/*CONSTCOND*/0)
77
+
78
+#define QSLIST_REMOVE_RCU(head, elm, type, field) do { \
79
+ if ((head)->slh_first == (elm)) { \
80
+ QSLIST_REMOVE_HEAD_RCU((head), field); \
81
+ } else { \
82
+ struct type *curr = (head)->slh_first; \
83
+ while (curr->field.sle_next != (elm)) { \
84
+ curr = curr->field.sle_next; \
85
+ } \
86
+ atomic_set(&curr->field.sle_next, \
87
+ curr->field.sle_next->field.sle_next); \
88
+ } \
89
+} while (/*CONSTCOND*/0)
90
+
91
+#define QSLIST_FOREACH_RCU(var, head, field) \
92
+ for ((var) = atomic_rcu_read(&(head)->slh_first); \
93
+ (var); \
94
+ (var) = atomic_rcu_read(&(var)->field.sle_next))
95
+
96
+#define QSLIST_FOREACH_SAFE_RCU(var, head, field, next) \
97
+ for ((var) = atomic_rcu_read(&(head)->slh_first); \
98
+ (var) && ((next) = atomic_rcu_read(&(var)->field.sle_next), 1); \
99
+ (var) = (next))
100
+
101
#ifdef __cplusplus
102
}
103
#endif
104
diff --git a/tests/Makefile.include b/tests/Makefile.include
105
index XXXXXXX..XXXXXXX 100644
106
--- a/tests/Makefile.include
107
+++ b/tests/Makefile.include
108
@@ -XXX,XX +XXX,XX @@ check-unit-y += tests/rcutorture$(EXESUF)
109
check-unit-y += tests/test-rcu-list$(EXESUF)
110
check-unit-y += tests/test-rcu-simpleq$(EXESUF)
111
check-unit-y += tests/test-rcu-tailq$(EXESUF)
112
+check-unit-y += tests/test-rcu-slist$(EXESUF)
113
check-unit-y += tests/test-qdist$(EXESUF)
114
check-unit-y += tests/test-qht$(EXESUF)
115
check-unit-y += tests/test-qht-par$(EXESUF)
116
@@ -XXX,XX +XXX,XX @@ tests/rcutorture$(EXESUF): tests/rcutorture.o $(test-util-obj-y)
117
tests/test-rcu-list$(EXESUF): tests/test-rcu-list.o $(test-util-obj-y)
118
tests/test-rcu-simpleq$(EXESUF): tests/test-rcu-simpleq.o $(test-util-obj-y)
119
tests/test-rcu-tailq$(EXESUF): tests/test-rcu-tailq.o $(test-util-obj-y)
120
+tests/test-rcu-slist$(EXESUF): tests/test-rcu-slist.o $(test-util-obj-y)
121
tests/test-qdist$(EXESUF): tests/test-qdist.o $(test-util-obj-y)
122
tests/test-qht$(EXESUF): tests/test-qht.o $(test-util-obj-y)
123
tests/test-qht-par$(EXESUF): tests/test-qht-par.o tests/qht-bench$(EXESUF) $(test-util-obj-y)
124
diff --git a/tests/test-rcu-list.c b/tests/test-rcu-list.c
125
index XXXXXXX..XXXXXXX 100644
126
--- a/tests/test-rcu-list.c
127
+++ b/tests/test-rcu-list.c
128
@@ -XXX,XX +XXX,XX @@ struct list_element {
129
QSIMPLEQ_ENTRY(list_element) entry;
130
#elif TEST_LIST_TYPE == 3
131
QTAILQ_ENTRY(list_element) entry;
132
+#elif TEST_LIST_TYPE == 4
133
+ QSLIST_ENTRY(list_element) entry;
134
#else
135
#error Invalid TEST_LIST_TYPE
136
#endif
137
@@ -XXX,XX +XXX,XX @@ static QTAILQ_HEAD(, list_element) Q_list_head;
138
#define TEST_LIST_INSERT_HEAD_RCU QTAILQ_INSERT_HEAD_RCU
139
#define TEST_LIST_FOREACH_RCU QTAILQ_FOREACH_RCU
140
#define TEST_LIST_FOREACH_SAFE_RCU QTAILQ_FOREACH_SAFE_RCU
141
+
142
+#elif TEST_LIST_TYPE == 4
143
+static QSLIST_HEAD(, list_element) Q_list_head;
144
+
145
+#define TEST_NAME "qslist"
146
+#define TEST_LIST_REMOVE_RCU(el, f) \
147
+     QSLIST_REMOVE_RCU(&Q_list_head, el, list_element, f)
148
+
149
+#define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \
150
+ QSLIST_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f)
151
+
152
+#define TEST_LIST_INSERT_HEAD_RCU QSLIST_INSERT_HEAD_RCU
153
+#define TEST_LIST_FOREACH_RCU QSLIST_FOREACH_RCU
154
+#define TEST_LIST_FOREACH_SAFE_RCU QSLIST_FOREACH_SAFE_RCU
155
#else
156
#error Invalid TEST_LIST_TYPE
157
#endif
158
diff --git a/tests/test-rcu-slist.c b/tests/test-rcu-slist.c
159
new file mode 100644
160
index XXXXXXX..XXXXXXX
161
--- /dev/null
162
+++ b/tests/test-rcu-slist.c
163
@@ -XXX,XX +XXX,XX @@
164
+#define TEST_LIST_TYPE 4
165
+#include "test-rcu-list.c"
166
--
167
2.24.1
168
diff view generated by jsdifflib
1
From: Denis Plotnikov <dplotnikov@virtuozzo.com>
1
The ctx->first_bh list contains all created BHs, including those that
2
2
are not scheduled. The list is iterated by the event loop and therefore
3
The patch allows to provide a pattern file for write
3
has O(n) time complexity with respected to the number of created BHs.
4
command. There was no similar ability before.
4
5
5
Rewrite BHs so that only scheduled or deleted BHs are enqueued.
6
Signed-off-by: Denis Plotnikov <dplotnikov@virtuozzo.com>
6
Only BHs that actually require action will be iterated.
7
Message-id: 20190820164616.4072-1-dplotnikov@virtuozzo.com
7
8
Reviewed-by: Eric Blake <eblake@redhat.com>
8
One semantic change is required: qemu_bh_delete() enqueues the BH and
9
[mreitz: Keep optstring in alphabetical order]
9
therefore invokes aio_notify(). The
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
tests/test-aio.c:test_source_bh_delete_from_cb() test case assumed that
11
g_main_context_iteration(NULL, false) returns false after
12
qemu_bh_delete() but it now returns true for one iteration. Fix up the
13
test case.
14
15
This patch makes aio_compute_timeout() and aio_bh_poll() drop from a CPU
16
profile reported by perf-top(1). Previously they combined to 9% CPU
17
utilization when AioContext polling is commented out and the guest has 2
18
virtio-blk,num-queues=1 and 99 virtio-blk,num-queues=32 devices.
19
20
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
22
Message-id: 20200221093951.1414693-1-stefanha@redhat.com
23
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
24
---
12
qemu-io-cmds.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++---
25
include/block/aio.h | 20 +++-
13
1 file changed, 93 insertions(+), 6 deletions(-)
26
tests/test-aio.c | 3 +-
14
27
util/async.c | 237 ++++++++++++++++++++++++++------------------
15
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
28
3 files changed, 158 insertions(+), 102 deletions(-)
29
30
diff --git a/include/block/aio.h b/include/block/aio.h
16
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
17
--- a/qemu-io-cmds.c
32
--- a/include/block/aio.h
18
+++ b/qemu-io-cmds.c
33
+++ b/include/block/aio.h
19
@@ -XXX,XX +XXX,XX @@ static void qemu_io_free(void *p)
34
@@ -XXX,XX +XXX,XX @@ struct ThreadPool;
20
qemu_vfree(p);
35
struct LinuxAioState;
21
}
36
struct LuringState;
22
37
23
+/*
38
+/*
24
+ * qemu_io_alloc_from_file()
39
+ * Each aio_bh_poll() call carves off a slice of the BH list, so that newly
25
+ *
40
+ * scheduled BHs are not processed until the next aio_bh_poll() call. All
26
+ * Allocates the buffer and populates it with the content of the given file
41
+ * active aio_bh_poll() calls chain their slices together in a list, so that
27
+ * up to @len bytes. If the file length is less than @len, then the buffer
42
+ * nested aio_bh_poll() calls process all scheduled bottom halves.
28
+ * is populated with the file content cyclically.
29
+ *
30
+ * @blk - the block backend where the buffer content is going to be written to
31
+ * @len - the buffer length
32
+ * @file_name - the file to read the content from
33
+ *
34
+ * Returns: the buffer pointer on success
35
+ * NULL on error
36
+ */
43
+ */
37
+static void *qemu_io_alloc_from_file(BlockBackend *blk, size_t len,
44
+typedef QSLIST_HEAD(, QEMUBH) BHList;
38
+ const char *file_name)
45
+typedef struct BHListSlice BHListSlice;
46
+struct BHListSlice {
47
+ BHList bh_list;
48
+ QSIMPLEQ_ENTRY(BHListSlice) next;
49
+};
50
+
51
struct AioContext {
52
GSource source;
53
54
@@ -XXX,XX +XXX,XX @@ struct AioContext {
55
*/
56
QemuLockCnt list_lock;
57
58
- /* Anchor of the list of Bottom Halves belonging to the context */
59
- struct QEMUBH *first_bh;
60
+ /* Bottom Halves pending aio_bh_poll() processing */
61
+ BHList bh_list;
62
+
63
+ /* Chained BH list slices for each nested aio_bh_poll() call */
64
+ QSIMPLEQ_HEAD(, BHListSlice) bh_slice_list;
65
66
/* Used by aio_notify.
67
*
68
diff --git a/tests/test-aio.c b/tests/test-aio.c
69
index XXXXXXX..XXXXXXX 100644
70
--- a/tests/test-aio.c
71
+++ b/tests/test-aio.c
72
@@ -XXX,XX +XXX,XX @@ static void test_source_bh_delete_from_cb(void)
73
g_assert_cmpint(data1.n, ==, data1.max);
74
g_assert(data1.bh == NULL);
75
76
- g_assert(!g_main_context_iteration(NULL, false));
77
+ assert(g_main_context_iteration(NULL, false));
78
+ assert(!g_main_context_iteration(NULL, false));
79
}
80
81
static void test_source_bh_delete_from_cb_many(void)
82
diff --git a/util/async.c b/util/async.c
83
index XXXXXXX..XXXXXXX 100644
84
--- a/util/async.c
85
+++ b/util/async.c
86
@@ -XXX,XX +XXX,XX @@
87
#include "block/thread-pool.h"
88
#include "qemu/main-loop.h"
89
#include "qemu/atomic.h"
90
+#include "qemu/rcu_queue.h"
91
#include "block/raw-aio.h"
92
#include "qemu/coroutine_int.h"
93
#include "trace.h"
94
@@ -XXX,XX +XXX,XX @@
95
/***********************************************************/
96
/* bottom halves (can be seen as timers which expire ASAP) */
97
98
+/* QEMUBH::flags values */
99
+enum {
100
+ /* Already enqueued and waiting for aio_bh_poll() */
101
+ BH_PENDING = (1 << 0),
102
+
103
+ /* Invoke the callback */
104
+ BH_SCHEDULED = (1 << 1),
105
+
106
+ /* Delete without invoking callback */
107
+ BH_DELETED = (1 << 2),
108
+
109
+ /* Delete after invoking callback */
110
+ BH_ONESHOT = (1 << 3),
111
+
112
+ /* Schedule periodically when the event loop is idle */
113
+ BH_IDLE = (1 << 4),
114
+};
115
+
116
struct QEMUBH {
117
AioContext *ctx;
118
QEMUBHFunc *cb;
119
void *opaque;
120
- QEMUBH *next;
121
- bool scheduled;
122
- bool idle;
123
- bool deleted;
124
+ QSLIST_ENTRY(QEMUBH) next;
125
+ unsigned flags;
126
};
127
128
+/* Called concurrently from any thread */
129
+static void aio_bh_enqueue(QEMUBH *bh, unsigned new_flags)
39
+{
130
+{
40
+ char *buf, *buf_origin;
131
+ AioContext *ctx = bh->ctx;
41
+ FILE *f = fopen(file_name, "r");
132
+ unsigned old_flags;
42
+ int pattern_len;
133
+
43
+
134
+ /*
44
+ if (!f) {
135
+ * The memory barrier implicit in atomic_fetch_or makes sure that:
45
+ perror(file_name);
136
+ * 1. idle & any writes needed by the callback are done before the
137
+ * locations are read in the aio_bh_poll.
138
+ * 2. ctx is loaded before the callback has a chance to execute and bh
139
+ * could be freed.
140
+ */
141
+ old_flags = atomic_fetch_or(&bh->flags, BH_PENDING | new_flags);
142
+ if (!(old_flags & BH_PENDING)) {
143
+ QSLIST_INSERT_HEAD_ATOMIC(&ctx->bh_list, bh, next);
144
+ }
145
+
146
+ aio_notify(ctx);
147
+}
148
+
149
+/* Only called from aio_bh_poll() and aio_ctx_finalize() */
150
+static QEMUBH *aio_bh_dequeue(BHList *head, unsigned *flags)
151
+{
152
+ QEMUBH *bh = QSLIST_FIRST_RCU(head);
153
+
154
+ if (!bh) {
46
+ return NULL;
155
+ return NULL;
47
+ }
156
+ }
48
+
157
+
49
+ if (qemuio_misalign) {
158
+ QSLIST_REMOVE_HEAD(head, next);
50
+ len += MISALIGN_OFFSET;
159
+
160
+ /*
161
+ * The atomic_and is paired with aio_bh_enqueue(). The implicit memory
162
+ * barrier ensures that the callback sees all writes done by the scheduling
163
+ * thread. It also ensures that the scheduling thread sees the cleared
164
+ * flag before bh->cb has run, and thus will call aio_notify again if
165
+ * necessary.
166
+ */
167
+ *flags = atomic_fetch_and(&bh->flags,
168
+ ~(BH_PENDING | BH_SCHEDULED | BH_IDLE));
169
+ return bh;
170
+}
171
+
172
void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
173
{
174
QEMUBH *bh;
175
@@ -XXX,XX +XXX,XX @@ void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
176
.cb = cb,
177
.opaque = opaque,
178
};
179
- qemu_lockcnt_lock(&ctx->list_lock);
180
- bh->next = ctx->first_bh;
181
- bh->scheduled = 1;
182
- bh->deleted = 1;
183
- /* Make sure that the members are ready before putting bh into list */
184
- smp_wmb();
185
- ctx->first_bh = bh;
186
- qemu_lockcnt_unlock(&ctx->list_lock);
187
- aio_notify(ctx);
188
+ aio_bh_enqueue(bh, BH_SCHEDULED | BH_ONESHOT);
189
}
190
191
QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
192
@@ -XXX,XX +XXX,XX @@ QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
193
.cb = cb,
194
.opaque = opaque,
195
};
196
- qemu_lockcnt_lock(&ctx->list_lock);
197
- bh->next = ctx->first_bh;
198
- /* Make sure that the members are ready before putting bh into list */
199
- smp_wmb();
200
- ctx->first_bh = bh;
201
- qemu_lockcnt_unlock(&ctx->list_lock);
202
return bh;
203
}
204
205
@@ -XXX,XX +XXX,XX @@ void aio_bh_call(QEMUBH *bh)
206
bh->cb(bh->opaque);
207
}
208
209
-/* Multiple occurrences of aio_bh_poll cannot be called concurrently.
210
- * The count in ctx->list_lock is incremented before the call, and is
211
- * not affected by the call.
212
- */
213
+/* Multiple occurrences of aio_bh_poll cannot be called concurrently. */
214
int aio_bh_poll(AioContext *ctx)
215
{
216
- QEMUBH *bh, **bhp, *next;
217
- int ret;
218
- bool deleted = false;
219
-
220
- ret = 0;
221
- for (bh = atomic_rcu_read(&ctx->first_bh); bh; bh = next) {
222
- next = atomic_rcu_read(&bh->next);
223
- /* The atomic_xchg is paired with the one in qemu_bh_schedule. The
224
- * implicit memory barrier ensures that the callback sees all writes
225
- * done by the scheduling thread. It also ensures that the scheduling
226
- * thread sees the zero before bh->cb has run, and thus will call
227
- * aio_notify again if necessary.
228
- */
229
- if (atomic_xchg(&bh->scheduled, 0)) {
230
+ BHListSlice slice;
231
+ BHListSlice *s;
232
+ int ret = 0;
233
+
234
+ QSLIST_MOVE_ATOMIC(&slice.bh_list, &ctx->bh_list);
235
+ QSIMPLEQ_INSERT_TAIL(&ctx->bh_slice_list, &slice, next);
236
+
237
+ while ((s = QSIMPLEQ_FIRST(&ctx->bh_slice_list))) {
238
+ QEMUBH *bh;
239
+ unsigned flags;
240
+
241
+ bh = aio_bh_dequeue(&s->bh_list, &flags);
242
+ if (!bh) {
243
+ QSIMPLEQ_REMOVE_HEAD(&ctx->bh_slice_list, next);
244
+ continue;
245
+ }
246
+
247
+ if ((flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
248
/* Idle BHs don't count as progress */
249
- if (!bh->idle) {
250
+ if (!(flags & BH_IDLE)) {
251
ret = 1;
252
}
253
- bh->idle = 0;
254
aio_bh_call(bh);
255
}
256
- if (bh->deleted) {
257
- deleted = true;
258
+ if (flags & (BH_DELETED | BH_ONESHOT)) {
259
+ g_free(bh);
260
}
261
}
262
263
- /* remove deleted bhs */
264
- if (!deleted) {
265
- return ret;
266
- }
267
-
268
- if (qemu_lockcnt_dec_if_lock(&ctx->list_lock)) {
269
- bhp = &ctx->first_bh;
270
- while (*bhp) {
271
- bh = *bhp;
272
- if (bh->deleted && !bh->scheduled) {
273
- *bhp = bh->next;
274
- g_free(bh);
275
- } else {
276
- bhp = &bh->next;
277
- }
278
- }
279
- qemu_lockcnt_inc_and_unlock(&ctx->list_lock);
280
- }
281
return ret;
282
}
283
284
void qemu_bh_schedule_idle(QEMUBH *bh)
285
{
286
- bh->idle = 1;
287
- /* Make sure that idle & any writes needed by the callback are done
288
- * before the locations are read in the aio_bh_poll.
289
- */
290
- atomic_mb_set(&bh->scheduled, 1);
291
+ aio_bh_enqueue(bh, BH_SCHEDULED | BH_IDLE);
292
}
293
294
void qemu_bh_schedule(QEMUBH *bh)
295
{
296
- AioContext *ctx;
297
-
298
- ctx = bh->ctx;
299
- bh->idle = 0;
300
- /* The memory barrier implicit in atomic_xchg makes sure that:
301
- * 1. idle & any writes needed by the callback are done before the
302
- * locations are read in the aio_bh_poll.
303
- * 2. ctx is loaded before scheduled is set and the callback has a chance
304
- * to execute.
305
- */
306
- if (atomic_xchg(&bh->scheduled, 1) == 0) {
307
- aio_notify(ctx);
308
- }
309
+ aio_bh_enqueue(bh, BH_SCHEDULED);
310
}
311
312
-
313
/* This func is async.
314
*/
315
void qemu_bh_cancel(QEMUBH *bh)
316
{
317
- atomic_mb_set(&bh->scheduled, 0);
318
+ atomic_and(&bh->flags, ~BH_SCHEDULED);
319
}
320
321
/* This func is async.The bottom half will do the delete action at the finial
322
@@ -XXX,XX +XXX,XX @@ void qemu_bh_cancel(QEMUBH *bh)
323
*/
324
void qemu_bh_delete(QEMUBH *bh)
325
{
326
- bh->scheduled = 0;
327
- bh->deleted = 1;
328
+ aio_bh_enqueue(bh, BH_DELETED);
329
}
330
331
-int64_t
332
-aio_compute_timeout(AioContext *ctx)
333
+static int64_t aio_compute_bh_timeout(BHList *head, int timeout)
334
{
335
- int64_t deadline;
336
- int timeout = -1;
337
QEMUBH *bh;
338
339
- for (bh = atomic_rcu_read(&ctx->first_bh); bh;
340
- bh = atomic_rcu_read(&bh->next)) {
341
- if (bh->scheduled) {
342
- if (bh->idle) {
343
+ QSLIST_FOREACH_RCU(bh, head, next) {
344
+ if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
345
+ if (bh->flags & BH_IDLE) {
346
/* idle bottom halves will be polled at least
347
* every 10ms */
348
timeout = 10000000;
349
@@ -XXX,XX +XXX,XX @@ aio_compute_timeout(AioContext *ctx)
350
}
351
}
352
353
+ return timeout;
354
+}
355
+
356
+int64_t
357
+aio_compute_timeout(AioContext *ctx)
358
+{
359
+ BHListSlice *s;
360
+ int64_t deadline;
361
+ int timeout = -1;
362
+
363
+ timeout = aio_compute_bh_timeout(&ctx->bh_list, timeout);
364
+ if (timeout == 0) {
365
+ return 0;
51
+ }
366
+ }
52
+
367
+
53
+ buf_origin = buf = blk_blockalign(blk, len);
368
+ QSIMPLEQ_FOREACH(s, &ctx->bh_slice_list, next) {
54
+
369
+ timeout = aio_compute_bh_timeout(&s->bh_list, timeout);
55
+ if (qemuio_misalign) {
370
+ if (timeout == 0) {
56
+ buf_origin += MISALIGN_OFFSET;
371
+ return 0;
57
+ buf += MISALIGN_OFFSET;
58
+ len -= MISALIGN_OFFSET;
59
+ }
60
+
61
+ pattern_len = fread(buf_origin, 1, len, f);
62
+
63
+ if (ferror(f)) {
64
+ perror(file_name);
65
+ goto error;
66
+ }
67
+
68
+ if (pattern_len == 0) {
69
+ fprintf(stderr, "%s: file is empty\n", file_name);
70
+ goto error;
71
+ }
72
+
73
+ fclose(f);
74
+
75
+ if (len > pattern_len) {
76
+ len -= pattern_len;
77
+ buf += pattern_len;
78
+
79
+ while (len > 0) {
80
+ size_t len_to_copy = MIN(pattern_len, len);
81
+
82
+ memcpy(buf, buf_origin, len_to_copy);
83
+
84
+ len -= len_to_copy;
85
+ buf += len_to_copy;
86
+ }
372
+ }
87
+ }
373
+ }
88
+
374
+
89
+ return buf_origin;
375
deadline = timerlistgroup_deadline_ns(&ctx->tlg);
90
+
376
if (deadline == 0) {
91
+error:
377
return 0;
92
+ qemu_io_free(buf_origin);
378
@@ -XXX,XX +XXX,XX @@ aio_ctx_check(GSource *source)
93
+ return NULL;
379
{
94
+}
380
AioContext *ctx = (AioContext *) source;
95
+
381
QEMUBH *bh;
96
static void dump_buffer(const void *buffer, int64_t offset, int64_t len)
382
+ BHListSlice *s;
97
{
383
98
uint64_t i;
384
atomic_and(&ctx->notify_me, ~1);
99
@@ -XXX,XX +XXX,XX @@ static void write_help(void)
385
aio_notify_accept(ctx);
100
" -n, -- with -z, don't allow slow fallback\n"
386
101
" -p, -- ignored for backwards compatibility\n"
387
- for (bh = ctx->first_bh; bh; bh = bh->next) {
102
" -P, -- use different pattern to fill file\n"
388
- if (bh->scheduled) {
103
+" -s, -- use a pattern file to fill the write buffer\n"
389
+ QSLIST_FOREACH_RCU(bh, &ctx->bh_list, next) {
104
" -C, -- report statistics in a machine parsable format\n"
390
+ if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
105
" -q, -- quiet mode, do not show I/O statistics\n"
391
return true;
106
" -u, -- with -z, allow unmapping\n"
392
}
107
@@ -XXX,XX +XXX,XX @@ static const cmdinfo_t write_cmd = {
108
.perm = BLK_PERM_WRITE,
109
.argmin = 2,
110
.argmax = -1,
111
- .args = "[-bcCfnquz] [-P pattern] off len",
112
+ .args = "[-bcCfnquz] [-P pattern | -s source_file] off len",
113
.oneline = "writes a number of bytes at a specified offset",
114
.help = write_help,
115
};
116
@@ -XXX,XX +XXX,XX @@ static int write_f(BlockBackend *blk, int argc, char **argv)
117
{
118
struct timespec t1, t2;
119
bool Cflag = false, qflag = false, bflag = false;
120
- bool Pflag = false, zflag = false, cflag = false;
121
+ bool Pflag = false, zflag = false, cflag = false, sflag = false;
122
int flags = 0;
123
int c, cnt, ret;
124
char *buf = NULL;
125
@@ -XXX,XX +XXX,XX @@ static int write_f(BlockBackend *blk, int argc, char **argv)
126
/* Some compilers get confused and warn if this is not initialized. */
127
int64_t total = 0;
128
int pattern = 0xcd;
129
+ const char *file_name = NULL;
130
131
- while ((c = getopt(argc, argv, "bcCfnpP:quz")) != -1) {
132
+ while ((c = getopt(argc, argv, "bcCfnpP:qs:uz")) != -1) {
133
switch (c) {
134
case 'b':
135
bflag = true;
136
@@ -XXX,XX +XXX,XX @@ static int write_f(BlockBackend *blk, int argc, char **argv)
137
case 'q':
138
qflag = true;
139
break;
140
+ case 's':
141
+ sflag = true;
142
+ file_name = optarg;
143
+ break;
144
case 'u':
145
flags |= BDRV_REQ_MAY_UNMAP;
146
break;
147
@@ -XXX,XX +XXX,XX @@ static int write_f(BlockBackend *blk, int argc, char **argv)
148
return -EINVAL;
149
}
393
}
150
394
+
151
- if (zflag && Pflag) {
395
+ QSIMPLEQ_FOREACH(s, &ctx->bh_slice_list, next) {
152
- printf("-z and -P cannot be specified at the same time\n");
396
+ QSLIST_FOREACH_RCU(bh, &s->bh_list, next) {
153
+ if (zflag + Pflag + sflag > 1) {
397
+ if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
154
+ printf("Only one of -z, -P, and -s "
398
+ return true;
155
+ "can be specified at the same time\n");
399
+ }
156
return -EINVAL;
400
+ }
401
+ }
402
return aio_pending(ctx) || (timerlistgroup_deadline_ns(&ctx->tlg) == 0);
403
}
404
405
@@ -XXX,XX +XXX,XX @@ static void
406
aio_ctx_finalize(GSource *source)
407
{
408
AioContext *ctx = (AioContext *) source;
409
+ QEMUBH *bh;
410
+ unsigned flags;
411
412
thread_pool_free(ctx->thread_pool);
413
414
@@ -XXX,XX +XXX,XX @@ aio_ctx_finalize(GSource *source)
415
assert(QSLIST_EMPTY(&ctx->scheduled_coroutines));
416
qemu_bh_delete(ctx->co_schedule_bh);
417
418
- qemu_lockcnt_lock(&ctx->list_lock);
419
- assert(!qemu_lockcnt_count(&ctx->list_lock));
420
- while (ctx->first_bh) {
421
- QEMUBH *next = ctx->first_bh->next;
422
+ /* There must be no aio_bh_poll() calls going on */
423
+ assert(QSIMPLEQ_EMPTY(&ctx->bh_slice_list));
424
425
+ while ((bh = aio_bh_dequeue(&ctx->bh_list, &flags))) {
426
/* qemu_bh_delete() must have been called on BHs in this AioContext */
427
- assert(ctx->first_bh->deleted);
428
+ assert(flags & BH_DELETED);
429
430
- g_free(ctx->first_bh);
431
- ctx->first_bh = next;
432
+ g_free(bh);
157
}
433
}
158
434
- qemu_lockcnt_unlock(&ctx->list_lock);
159
@@ -XXX,XX +XXX,XX @@ static int write_f(BlockBackend *blk, int argc, char **argv)
435
160
}
436
aio_set_event_notifier(ctx, &ctx->notifier, false, NULL, NULL);
161
437
event_notifier_cleanup(&ctx->notifier);
162
if (!zflag) {
438
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
163
- buf = qemu_io_alloc(blk, count, pattern);
439
AioContext *ctx;
164
+ if (sflag) {
440
165
+ buf = qemu_io_alloc_from_file(blk, count, file_name);
441
ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext));
166
+ if (!buf) {
442
+ QSLIST_INIT(&ctx->bh_list);
167
+ return -EINVAL;
443
+ QSIMPLEQ_INIT(&ctx->bh_slice_list);
168
+ }
444
aio_context_setup(ctx);
169
+ } else {
445
170
+ buf = qemu_io_alloc(blk, count, pattern);
446
ret = event_notifier_init(&ctx->notifier, false);
171
+ }
172
}
173
174
clock_gettime(CLOCK_MONOTONIC, &t1);
175
--
447
--
176
2.21.0
448
2.24.1
177
449
178
diff view generated by jsdifflib
New patch
1
epoll_handler is a stack variable and must not be accessed after it goes
2
out of scope:
1
3
4
if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) {
5
AioHandler epoll_handler;
6
...
7
add_pollfd(&epoll_handler);
8
ret = aio_epoll(ctx, pollfds, npfd, timeout);
9
} ...
10
11
...
12
13
/* if we have any readable fds, dispatch event */
14
if (ret > 0) {
15
for (i = 0; i < npfd; i++) {
16
nodes[i]->pfd.revents = pollfds[i].revents;
17
}
18
}
19
20
nodes[0] is &epoll_handler, which has already gone out of scope.
21
22
There is no need to use pollfds[] for epoll. We don't need an
23
AioHandler for the epoll fd.
24
25
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
26
Reviewed-by: Sergio Lopez <slp@redhat.com>
27
Message-id: 20200214171712.541358-2-stefanha@redhat.com
28
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
29
---
30
util/aio-posix.c | 20 ++++++++------------
31
1 file changed, 8 insertions(+), 12 deletions(-)
32
33
diff --git a/util/aio-posix.c b/util/aio-posix.c
34
index XXXXXXX..XXXXXXX 100644
35
--- a/util/aio-posix.c
36
+++ b/util/aio-posix.c
37
@@ -XXX,XX +XXX,XX @@ static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new)
38
}
39
}
40
41
-static int aio_epoll(AioContext *ctx, GPollFD *pfds,
42
- unsigned npfd, int64_t timeout)
43
+static int aio_epoll(AioContext *ctx, int64_t timeout)
44
{
45
+ GPollFD pfd = {
46
+ .fd = ctx->epollfd,
47
+ .events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR,
48
+ };
49
AioHandler *node;
50
int i, ret = 0;
51
struct epoll_event events[128];
52
53
- assert(npfd == 1);
54
- assert(pfds[0].fd == ctx->epollfd);
55
if (timeout > 0) {
56
- ret = qemu_poll_ns(pfds, npfd, timeout);
57
+ ret = qemu_poll_ns(&pfd, 1, timeout);
58
}
59
if (timeout <= 0 || ret > 0) {
60
ret = epoll_wait(ctx->epollfd, events,
61
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
62
63
/* wait until next event */
64
if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) {
65
- AioHandler epoll_handler;
66
-
67
- epoll_handler.pfd.fd = ctx->epollfd;
68
- epoll_handler.pfd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR;
69
- npfd = 0;
70
- add_pollfd(&epoll_handler);
71
- ret = aio_epoll(ctx, pollfds, npfd, timeout);
72
+ npfd = 0; /* pollfds[] is not being used */
73
+ ret = aio_epoll(ctx, timeout);
74
} else {
75
ret = qemu_poll_ns(pollfds, npfd, timeout);
76
}
77
--
78
2.24.1
79
diff view generated by jsdifflib
New patch
1
Don't pass the nanosecond timeout into epoll_wait(), which expects
2
milliseconds.
1
3
4
The epoll_wait() timeout value does not matter if qemu_poll_ns()
5
determined that the poll fd is ready, but passing a value in the wrong
6
units is still ugly. Pass a 0 timeout to epoll_wait() instead.
7
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Reviewed-by: Sergio Lopez <slp@redhat.com>
10
Message-id: 20200214171712.541358-3-stefanha@redhat.com
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
---
13
util/aio-posix.c | 3 +++
14
1 file changed, 3 insertions(+)
15
16
diff --git a/util/aio-posix.c b/util/aio-posix.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/util/aio-posix.c
19
+++ b/util/aio-posix.c
20
@@ -XXX,XX +XXX,XX @@ static int aio_epoll(AioContext *ctx, int64_t timeout)
21
22
if (timeout > 0) {
23
ret = qemu_poll_ns(&pfd, 1, timeout);
24
+ if (ret > 0) {
25
+ timeout = 0;
26
+ }
27
}
28
if (timeout <= 0 || ret > 0) {
29
ret = epoll_wait(ctx->epollfd, events,
30
--
31
2.24.1
32
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
QLIST_REMOVE() assumes the element is in a list. It also leaves the
2
element's linked list pointers dangling.
2
3
3
It's wrong to OR shared permissions. It may lead to crash on further
4
Introduce a safe version of QLIST_REMOVE() and convert open-coded
4
permission updates.
5
instances of this pattern.
5
Also, no needs to consider previously calculated permissions, as at
6
this point we already bind all new parents and bdrv_get_cumulative_perm
7
result is enough. So fix the bug by just set permissions by
8
bdrv_get_cumulative_perm result.
9
6
10
Bug was introduced in long ago 234ac1a9025, in 2.9.
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
8
Reviewed-by: Sergio Lopez <slp@redhat.com>
12
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Message-id: 20200214171712.541358-4-stefanha@redhat.com
13
Message-id: 20190824100740.61635-1-vsementsov@virtuozzo.com
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
11
---
16
block.c | 5 ++---
12
block.c | 5 +----
17
1 file changed, 2 insertions(+), 3 deletions(-)
13
chardev/spice.c | 4 +---
14
include/qemu/queue.h | 14 ++++++++++++++
15
3 files changed, 16 insertions(+), 7 deletions(-)
18
16
19
diff --git a/block.c b/block.c
17
diff --git a/block.c b/block.c
20
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
21
--- a/block.c
19
--- a/block.c
22
+++ b/block.c
20
+++ b/block.c
23
@@ -XXX,XX +XXX,XX @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
21
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
22
23
static void bdrv_detach_child(BdrvChild *child)
24
{
24
{
25
BdrvChild *c, *next;
25
- if (child->next.le_prev) {
26
GSList *list = NULL, *p;
26
- QLIST_REMOVE(child, next);
27
- uint64_t old_perm, old_shared;
27
- child->next.le_prev = NULL;
28
uint64_t perm = 0, shared = BLK_PERM_ALL;
28
- }
29
int ret;
29
+ QLIST_SAFE_REMOVE(child, next);
30
30
31
@@ -XXX,XX +XXX,XX @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
31
bdrv_replace_child(child, NULL);
32
bdrv_unref(from);
32
33
}
33
diff --git a/chardev/spice.c b/chardev/spice.c
34
34
index XXXXXXX..XXXXXXX 100644
35
- bdrv_get_cumulative_perm(to, &old_perm, &old_shared);
35
--- a/chardev/spice.c
36
- bdrv_set_perm(to, old_perm | perm, old_shared | shared);
36
+++ b/chardev/spice.c
37
+ bdrv_get_cumulative_perm(to, &perm, &shared);
37
@@ -XXX,XX +XXX,XX @@ static void char_spice_finalize(Object *obj)
38
+ bdrv_set_perm(to, perm, shared);
38
39
39
vmc_unregister_interface(s);
40
out:
40
41
g_slist_free(list);
41
- if (s->next.le_prev) {
42
- QLIST_REMOVE(s, next);
43
- }
44
+ QLIST_SAFE_REMOVE(s, next);
45
46
g_free((char *)s->sin.subtype);
47
g_free((char *)s->sin.portname);
48
diff --git a/include/qemu/queue.h b/include/qemu/queue.h
49
index XXXXXXX..XXXXXXX 100644
50
--- a/include/qemu/queue.h
51
+++ b/include/qemu/queue.h
52
@@ -XXX,XX +XXX,XX @@ struct { \
53
*(elm)->field.le_prev = (elm)->field.le_next; \
54
} while (/*CONSTCOND*/0)
55
56
+/*
57
+ * Like QLIST_REMOVE() but safe to call when elm is not in a list
58
+ */
59
+#define QLIST_SAFE_REMOVE(elm, field) do { \
60
+ if ((elm)->field.le_prev != NULL) { \
61
+ if ((elm)->field.le_next != NULL) \
62
+ (elm)->field.le_next->field.le_prev = \
63
+ (elm)->field.le_prev; \
64
+ *(elm)->field.le_prev = (elm)->field.le_next; \
65
+ (elm)->field.le_next = NULL; \
66
+ (elm)->field.le_prev = NULL; \
67
+ } \
68
+} while (/*CONSTCOND*/0)
69
+
70
#define QLIST_FOREACH(var, head, field) \
71
for ((var) = ((head)->lh_first); \
72
(var); \
42
--
73
--
43
2.21.0
74
2.24.1
44
75
45
diff view generated by jsdifflib
1
From: Nir Soffer <nirsof@gmail.com>
1
It is not necessary to scan all AioHandlers for deletion. Keep a list
2
2
of deleted handlers instead of scanning the full list of all handlers.
3
Using block_resize we can test allocate_first_block() with file
3
4
descriptor opened with O_DIRECT, ensuring that it works for any size
4
The AioHandler->deleted field can be dropped. Let's check if the
5
larger than 4096 bytes.
5
handler has been inserted into the deleted list instead. Add a new
6
6
QLIST_IS_INSERTED() API for this check.
7
Testing smaller sizes is tricky as the result depends on the filesystem
7
8
used for testing. For example on NFS any size will work since O_DIRECT
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
does not require any alignment.
9
Reviewed-by: Sergio Lopez <slp@redhat.com>
10
10
Message-id: 20200214171712.541358-5-stefanha@redhat.com
11
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Message-id: 20190827010528.8818-3-nsoffer@redhat.com
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
12
---
16
tests/qemu-iotests/175 | 28 ++++++++++++++++++++++++++++
13
include/block/aio.h | 6 ++++-
17
tests/qemu-iotests/175.out | 8 ++++++++
14
include/qemu/queue.h | 3 +++
18
2 files changed, 36 insertions(+)
15
util/aio-posix.c | 53 +++++++++++++++++++++++++++++---------------
19
16
3 files changed, 43 insertions(+), 19 deletions(-)
20
diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175
17
21
index XXXXXXX..XXXXXXX 100755
18
diff --git a/include/block/aio.h b/include/block/aio.h
22
--- a/tests/qemu-iotests/175
19
index XXXXXXX..XXXXXXX 100644
23
+++ b/tests/qemu-iotests/175
20
--- a/include/block/aio.h
24
@@ -XXX,XX +XXX,XX @@ _filter_blocks()
21
+++ b/include/block/aio.h
25
-e "s/blocks=$((extra_blocks + img_size / 512))\\(\$\\|[^0-9]\\)/max allocation/"
22
@@ -XXX,XX +XXX,XX @@ void qemu_aio_unref(void *p);
23
void qemu_aio_ref(void *p);
24
25
typedef struct AioHandler AioHandler;
26
+typedef QLIST_HEAD(, AioHandler) AioHandlerList;
27
typedef void QEMUBHFunc(void *opaque);
28
typedef bool AioPollFn(void *opaque);
29
typedef void IOHandler(void *opaque);
30
@@ -XXX,XX +XXX,XX @@ struct AioContext {
31
QemuRecMutex lock;
32
33
/* The list of registered AIO handlers. Protected by ctx->list_lock. */
34
- QLIST_HEAD(, AioHandler) aio_handlers;
35
+ AioHandlerList aio_handlers;
36
+
37
+ /* The list of AIO handlers to be deleted. Protected by ctx->list_lock. */
38
+ AioHandlerList deleted_aio_handlers;
39
40
/* Used to avoid unnecessary event_notifier_set calls in aio_notify;
41
* accessed with atomic primitives. If this field is 0, everything
42
diff --git a/include/qemu/queue.h b/include/qemu/queue.h
43
index XXXXXXX..XXXXXXX 100644
44
--- a/include/qemu/queue.h
45
+++ b/include/qemu/queue.h
46
@@ -XXX,XX +XXX,XX @@ struct { \
47
} \
48
} while (/*CONSTCOND*/0)
49
50
+/* Is elm in a list? */
51
+#define QLIST_IS_INSERTED(elm, field) ((elm)->field.le_prev != NULL)
52
+
53
#define QLIST_FOREACH(var, head, field) \
54
for ((var) = ((head)->lh_first); \
55
(var); \
56
diff --git a/util/aio-posix.c b/util/aio-posix.c
57
index XXXXXXX..XXXXXXX 100644
58
--- a/util/aio-posix.c
59
+++ b/util/aio-posix.c
60
@@ -XXX,XX +XXX,XX @@ struct AioHandler
61
AioPollFn *io_poll;
62
IOHandler *io_poll_begin;
63
IOHandler *io_poll_end;
64
- int deleted;
65
void *opaque;
66
bool is_external;
67
QLIST_ENTRY(AioHandler) node;
68
+ QLIST_ENTRY(AioHandler) node_deleted;
69
};
70
71
#ifdef CONFIG_EPOLL_CREATE1
72
@@ -XXX,XX +XXX,XX @@ static bool aio_epoll_try_enable(AioContext *ctx)
73
74
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
75
int r;
76
- if (node->deleted || !node->pfd.events) {
77
+ if (QLIST_IS_INSERTED(node, node_deleted) || !node->pfd.events) {
78
continue;
79
}
80
event.events = epoll_events_from_pfd(node->pfd.events);
81
@@ -XXX,XX +XXX,XX @@ static AioHandler *find_aio_handler(AioContext *ctx, int fd)
82
AioHandler *node;
83
84
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
85
- if (node->pfd.fd == fd)
86
- if (!node->deleted)
87
+ if (node->pfd.fd == fd) {
88
+ if (!QLIST_IS_INSERTED(node, node_deleted)) {
89
return node;
90
+ }
91
+ }
92
}
93
94
return NULL;
95
@@ -XXX,XX +XXX,XX @@ static bool aio_remove_fd_handler(AioContext *ctx, AioHandler *node)
96
97
/* If a read is in progress, just mark the node as deleted */
98
if (qemu_lockcnt_count(&ctx->list_lock)) {
99
- node->deleted = 1;
100
+ QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_deleted);
101
node->pfd.revents = 0;
102
return false;
103
}
104
@@ -XXX,XX +XXX,XX @@ static void poll_set_started(AioContext *ctx, bool started)
105
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
106
IOHandler *fn;
107
108
- if (node->deleted) {
109
+ if (QLIST_IS_INSERTED(node, node_deleted)) {
110
continue;
111
}
112
113
@@ -XXX,XX +XXX,XX @@ bool aio_pending(AioContext *ctx)
114
return result;
26
}
115
}
27
116
28
+# Resize image using block_resize.
117
+static void aio_free_deleted_handlers(AioContext *ctx)
29
+# Parameter 1: image path
30
+# Parameter 2: new size
31
+_block_resize()
32
+{
118
+{
33
+ local path=$1
119
+ AioHandler *node;
34
+ local size=$2
120
+
35
+
121
+ if (QLIST_EMPTY_RCU(&ctx->deleted_aio_handlers)) {
36
+ $QEMU -qmp stdio -nographic -nodefaults \
122
+ return;
37
+ -blockdev file,node-name=file,filename=$path,cache.direct=on \
123
+ }
38
+ <<EOF
124
+ if (!qemu_lockcnt_dec_if_lock(&ctx->list_lock)) {
39
+{'execute': 'qmp_capabilities'}
125
+ return; /* we are nested, let the parent do the freeing */
40
+{'execute': 'block_resize', 'arguments': {'node-name': 'file', 'size': $size}}
126
+ }
41
+{'execute': 'quit'}
127
+
42
+EOF
128
+ while ((node = QLIST_FIRST_RCU(&ctx->deleted_aio_handlers))) {
129
+ QLIST_REMOVE(node, node);
130
+ QLIST_REMOVE(node, node_deleted);
131
+ g_free(node);
132
+ }
133
+
134
+ qemu_lockcnt_inc_and_unlock(&ctx->list_lock);
43
+}
135
+}
44
+
136
+
45
# get standard environment, filters and checks
137
static bool aio_dispatch_handlers(AioContext *ctx)
46
. ./common.rc
138
{
47
. ./common.filter
139
AioHandler *node, *tmp;
48
@@ -XXX,XX +XXX,XX @@ _supported_fmt raw
140
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx)
49
_supported_proto file
141
revents = node->pfd.revents & node->pfd.events;
50
_supported_os Linux
142
node->pfd.revents = 0;
51
143
52
+_default_cache_mode none
144
- if (!node->deleted &&
53
+_supported_cache_modes none directsync
145
+ if (!QLIST_IS_INSERTED(node, node_deleted) &&
54
+
146
(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) &&
55
size=$((1 * 1024 * 1024))
147
aio_node_check(ctx, node->is_external) &&
56
148
node->io_read) {
57
touch "$TEST_DIR/empty"
149
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx)
58
@@ -XXX,XX +XXX,XX @@ for mode in off full falloc; do
150
progress = true;
59
stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $size
151
}
60
done
152
}
61
153
- if (!node->deleted &&
62
+for new_size in 4096 1048576; do
154
+ if (!QLIST_IS_INSERTED(node, node_deleted) &&
63
+ echo
155
(revents & (G_IO_OUT | G_IO_ERR)) &&
64
+ echo "== resize empty image with block_resize =="
156
aio_node_check(ctx, node->is_external) &&
65
+ _make_test_img 0 | _filter_imgfmt
157
node->io_write) {
66
+ _block_resize $TEST_IMG $new_size >/dev/null
158
node->io_write(node->opaque);
67
+ stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $new_size
159
progress = true;
68
+done
160
}
69
+
161
-
70
# success, all done
162
- if (node->deleted) {
71
echo "*** done"
163
- if (qemu_lockcnt_dec_if_lock(&ctx->list_lock)) {
72
rm -f $seq.full
164
- QLIST_REMOVE(node, node);
73
diff --git a/tests/qemu-iotests/175.out b/tests/qemu-iotests/175.out
165
- g_free(node);
74
index XXXXXXX..XXXXXXX 100644
166
- qemu_lockcnt_inc_and_unlock(&ctx->list_lock);
75
--- a/tests/qemu-iotests/175.out
167
- }
76
+++ b/tests/qemu-iotests/175.out
168
- }
77
@@ -XXX,XX +XXX,XX @@ size=1048576, max allocation
169
}
78
== creating image with preallocation falloc ==
170
79
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=falloc
171
return progress;
80
size=1048576, max allocation
172
@@ -XXX,XX +XXX,XX @@ void aio_dispatch(AioContext *ctx)
81
+
173
qemu_lockcnt_inc(&ctx->list_lock);
82
+== resize empty image with block_resize ==
174
aio_bh_poll(ctx);
83
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=0
175
aio_dispatch_handlers(ctx);
84
+size=4096, min allocation
176
+ aio_free_deleted_handlers(ctx);
85
+
177
qemu_lockcnt_dec(&ctx->list_lock);
86
+== resize empty image with block_resize ==
178
87
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=0
179
timerlistgroup_run_timers(&ctx->tlg);
88
+size=1048576, min allocation
180
@@ -XXX,XX +XXX,XX @@ static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout)
89
*** done
181
RCU_READ_LOCK_GUARD();
182
183
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
184
- if (!node->deleted && node->io_poll &&
185
+ if (!QLIST_IS_INSERTED(node, node_deleted) && node->io_poll &&
186
aio_node_check(ctx, node->is_external) &&
187
node->io_poll(node->opaque)) {
188
/*
189
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
190
191
if (!aio_epoll_enabled(ctx)) {
192
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
193
- if (!node->deleted && node->pfd.events
194
+ if (!QLIST_IS_INSERTED(node, node_deleted) && node->pfd.events
195
&& aio_node_check(ctx, node->is_external)) {
196
add_pollfd(node);
197
}
198
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
199
progress |= aio_dispatch_handlers(ctx);
200
}
201
202
+ aio_free_deleted_handlers(ctx);
203
+
204
qemu_lockcnt_dec(&ctx->list_lock);
205
206
progress |= timerlistgroup_run_timers(&ctx->tlg);
90
--
207
--
91
2.21.0
208
2.24.1
92
209
93
diff view generated by jsdifflib
New patch
1
1
File descriptor monitoring is O(1) with epoll(7), but
2
aio_dispatch_handlers() still scans all AioHandlers instead of
3
dispatching just those that are ready. This makes aio_poll() O(n) with
4
respect to the total number of registered handlers.
5
6
Add a local ready_list to aio_poll() so that each nested aio_poll()
7
builds a list of handlers ready to be dispatched. Since file descriptor
8
polling is level-triggered, nested aio_poll() calls also see fds that
9
were ready in the parent but not yet dispatched. This guarantees that
10
nested aio_poll() invocations will dispatch all fds, even those that
11
became ready before the nested invocation.
12
13
Since only handlers ready to be dispatched are placed onto the
14
ready_list, the new aio_dispatch_ready_handlers() function provides O(1)
15
dispatch.
16
17
Note that AioContext polling is still O(n) and currently cannot be fully
18
disabled. This still needs to be fixed before aio_poll() is fully O(1).
19
20
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Reviewed-by: Sergio Lopez <slp@redhat.com>
22
Message-id: 20200214171712.541358-6-stefanha@redhat.com
23
[Fix compilation error on macOS where there is no epoll(87). The
24
aio_epoll() prototype was out of date and aio_add_ready_list() needed to
25
be moved outside the ifdef.
26
--Stefan]
27
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
28
---
29
util/aio-posix.c | 110 +++++++++++++++++++++++++++++++++--------------
30
1 file changed, 78 insertions(+), 32 deletions(-)
31
32
diff --git a/util/aio-posix.c b/util/aio-posix.c
33
index XXXXXXX..XXXXXXX 100644
34
--- a/util/aio-posix.c
35
+++ b/util/aio-posix.c
36
@@ -XXX,XX +XXX,XX @@ struct AioHandler
37
void *opaque;
38
bool is_external;
39
QLIST_ENTRY(AioHandler) node;
40
+ QLIST_ENTRY(AioHandler) node_ready; /* only used during aio_poll() */
41
QLIST_ENTRY(AioHandler) node_deleted;
42
};
43
44
+/* Add a handler to a ready list */
45
+static void add_ready_handler(AioHandlerList *ready_list,
46
+ AioHandler *node,
47
+ int revents)
48
+{
49
+ QLIST_SAFE_REMOVE(node, node_ready); /* remove from nested parent's list */
50
+ node->pfd.revents = revents;
51
+ QLIST_INSERT_HEAD(ready_list, node, node_ready);
52
+}
53
+
54
#ifdef CONFIG_EPOLL_CREATE1
55
56
/* The fd number threshold to switch to epoll */
57
@@ -XXX,XX +XXX,XX @@ static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new)
58
}
59
}
60
61
-static int aio_epoll(AioContext *ctx, int64_t timeout)
62
+static int aio_epoll(AioContext *ctx, AioHandlerList *ready_list,
63
+ int64_t timeout)
64
{
65
GPollFD pfd = {
66
.fd = ctx->epollfd,
67
@@ -XXX,XX +XXX,XX @@ static int aio_epoll(AioContext *ctx, int64_t timeout)
68
}
69
for (i = 0; i < ret; i++) {
70
int ev = events[i].events;
71
+ int revents = (ev & EPOLLIN ? G_IO_IN : 0) |
72
+ (ev & EPOLLOUT ? G_IO_OUT : 0) |
73
+ (ev & EPOLLHUP ? G_IO_HUP : 0) |
74
+ (ev & EPOLLERR ? G_IO_ERR : 0);
75
+
76
node = events[i].data.ptr;
77
- node->pfd.revents = (ev & EPOLLIN ? G_IO_IN : 0) |
78
- (ev & EPOLLOUT ? G_IO_OUT : 0) |
79
- (ev & EPOLLHUP ? G_IO_HUP : 0) |
80
- (ev & EPOLLERR ? G_IO_ERR : 0);
81
+ add_ready_handler(ready_list, node, revents);
82
}
83
}
84
out:
85
@@ -XXX,XX +XXX,XX @@ static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new)
86
{
87
}
88
89
-static int aio_epoll(AioContext *ctx, GPollFD *pfds,
90
- unsigned npfd, int64_t timeout)
91
+static int aio_epoll(AioContext *ctx, AioHandlerList *ready_list,
92
+ int64_t timeout)
93
{
94
assert(false);
95
}
96
@@ -XXX,XX +XXX,XX @@ static void aio_free_deleted_handlers(AioContext *ctx)
97
qemu_lockcnt_inc_and_unlock(&ctx->list_lock);
98
}
99
100
-static bool aio_dispatch_handlers(AioContext *ctx)
101
+static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node)
102
{
103
- AioHandler *node, *tmp;
104
bool progress = false;
105
+ int revents;
106
107
- QLIST_FOREACH_SAFE_RCU(node, &ctx->aio_handlers, node, tmp) {
108
- int revents;
109
+ revents = node->pfd.revents & node->pfd.events;
110
+ node->pfd.revents = 0;
111
112
- revents = node->pfd.revents & node->pfd.events;
113
- node->pfd.revents = 0;
114
+ if (!QLIST_IS_INSERTED(node, node_deleted) &&
115
+ (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) &&
116
+ aio_node_check(ctx, node->is_external) &&
117
+ node->io_read) {
118
+ node->io_read(node->opaque);
119
120
- if (!QLIST_IS_INSERTED(node, node_deleted) &&
121
- (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) &&
122
- aio_node_check(ctx, node->is_external) &&
123
- node->io_read) {
124
- node->io_read(node->opaque);
125
-
126
- /* aio_notify() does not count as progress */
127
- if (node->opaque != &ctx->notifier) {
128
- progress = true;
129
- }
130
- }
131
- if (!QLIST_IS_INSERTED(node, node_deleted) &&
132
- (revents & (G_IO_OUT | G_IO_ERR)) &&
133
- aio_node_check(ctx, node->is_external) &&
134
- node->io_write) {
135
- node->io_write(node->opaque);
136
+ /* aio_notify() does not count as progress */
137
+ if (node->opaque != &ctx->notifier) {
138
progress = true;
139
}
140
}
141
+ if (!QLIST_IS_INSERTED(node, node_deleted) &&
142
+ (revents & (G_IO_OUT | G_IO_ERR)) &&
143
+ aio_node_check(ctx, node->is_external) &&
144
+ node->io_write) {
145
+ node->io_write(node->opaque);
146
+ progress = true;
147
+ }
148
+
149
+ return progress;
150
+}
151
+
152
+/*
153
+ * If we have a list of ready handlers then this is more efficient than
154
+ * scanning all handlers with aio_dispatch_handlers().
155
+ */
156
+static bool aio_dispatch_ready_handlers(AioContext *ctx,
157
+ AioHandlerList *ready_list)
158
+{
159
+ bool progress = false;
160
+ AioHandler *node;
161
+
162
+ while ((node = QLIST_FIRST(ready_list))) {
163
+ QLIST_SAFE_REMOVE(node, node_ready);
164
+ progress = aio_dispatch_handler(ctx, node) || progress;
165
+ }
166
+
167
+ return progress;
168
+}
169
+
170
+/* Slower than aio_dispatch_ready_handlers() but only used via glib */
171
+static bool aio_dispatch_handlers(AioContext *ctx)
172
+{
173
+ AioHandler *node, *tmp;
174
+ bool progress = false;
175
+
176
+ QLIST_FOREACH_SAFE_RCU(node, &ctx->aio_handlers, node, tmp) {
177
+ progress = aio_dispatch_handler(ctx, node) || progress;
178
+ }
179
180
return progress;
181
}
182
@@ -XXX,XX +XXX,XX @@ static bool try_poll_mode(AioContext *ctx, int64_t *timeout)
183
184
bool aio_poll(AioContext *ctx, bool blocking)
185
{
186
+ AioHandlerList ready_list = QLIST_HEAD_INITIALIZER(ready_list);
187
AioHandler *node;
188
int i;
189
int ret = 0;
190
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
191
/* wait until next event */
192
if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) {
193
npfd = 0; /* pollfds[] is not being used */
194
- ret = aio_epoll(ctx, timeout);
195
+ ret = aio_epoll(ctx, &ready_list, timeout);
196
} else {
197
ret = qemu_poll_ns(pollfds, npfd, timeout);
198
}
199
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
200
/* if we have any readable fds, dispatch event */
201
if (ret > 0) {
202
for (i = 0; i < npfd; i++) {
203
- nodes[i]->pfd.revents = pollfds[i].revents;
204
+ int revents = pollfds[i].revents;
205
+
206
+ if (revents) {
207
+ add_ready_handler(&ready_list, nodes[i], revents);
208
+ }
209
}
210
}
211
212
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
213
progress |= aio_bh_poll(ctx);
214
215
if (ret > 0) {
216
- progress |= aio_dispatch_handlers(ctx);
217
+ progress |= aio_dispatch_ready_handlers(ctx, &ready_list);
218
}
219
220
aio_free_deleted_handlers(ctx);
221
--
222
2.24.1
223
diff view generated by jsdifflib
New patch
1
From: Alexander Bulekov <alxndr@bu.edu>
1
2
3
Move vl.c to a separate directory, similar to linux-user/
4
Update the chechpatch and get_maintainer scripts, since they relied on
5
/vl.c for top_of_tree checks.
6
7
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
8
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
9
Message-id: 20200220041118.23264-2-alxndr@bu.edu
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
12
MAINTAINERS | 2 +-
13
Makefile.objs | 2 --
14
Makefile.target | 1 +
15
scripts/checkpatch.pl | 2 +-
16
scripts/get_maintainer.pl | 3 ++-
17
softmmu/Makefile.objs | 2 ++
18
vl.c => softmmu/vl.c | 0
19
7 files changed, 7 insertions(+), 5 deletions(-)
20
create mode 100644 softmmu/Makefile.objs
21
rename vl.c => softmmu/vl.c (100%)
22
23
diff --git a/MAINTAINERS b/MAINTAINERS
24
index XXXXXXX..XXXXXXX 100644
25
--- a/MAINTAINERS
26
+++ b/MAINTAINERS
27
@@ -XXX,XX +XXX,XX @@ F: include/qemu/main-loop.h
28
F: include/sysemu/runstate.h
29
F: util/main-loop.c
30
F: util/qemu-timer.c
31
-F: vl.c
32
+F: softmmu/vl.c
33
F: qapi/run-state.json
34
35
Human Monitor (HMP)
36
diff --git a/Makefile.objs b/Makefile.objs
37
index XXXXXXX..XXXXXXX 100644
38
--- a/Makefile.objs
39
+++ b/Makefile.objs
40
@@ -XXX,XX +XXX,XX @@ common-obj-y += ui/
41
common-obj-m += ui/
42
43
common-obj-y += dma-helpers.o
44
-common-obj-y += vl.o
45
-vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
46
common-obj-$(CONFIG_TPM) += tpm.o
47
48
common-obj-y += backends/
49
diff --git a/Makefile.target b/Makefile.target
50
index XXXXXXX..XXXXXXX 100644
51
--- a/Makefile.target
52
+++ b/Makefile.target
53
@@ -XXX,XX +XXX,XX @@ obj-y += qapi/
54
obj-y += memory.o
55
obj-y += memory_mapping.o
56
obj-y += migration/ram.o
57
+obj-y += softmmu/
58
LIBS := $(libs_softmmu) $(LIBS)
59
60
# Hardware support
61
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
62
index XXXXXXX..XXXXXXX 100755
63
--- a/scripts/checkpatch.pl
64
+++ b/scripts/checkpatch.pl
65
@@ -XXX,XX +XXX,XX @@ sub top_of_kernel_tree {
66
    my @tree_check = (
67
        "COPYING", "MAINTAINERS", "Makefile",
68
        "README.rst", "docs", "VERSION",
69
-        "vl.c"
70
+        "linux-user", "softmmu"
71
    );
72
73
    foreach my $check (@tree_check) {
74
diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl
75
index XXXXXXX..XXXXXXX 100755
76
--- a/scripts/get_maintainer.pl
77
+++ b/scripts/get_maintainer.pl
78
@@ -XXX,XX +XXX,XX @@ sub top_of_tree {
79
&& (-f "${lk_path}Makefile")
80
&& (-d "${lk_path}docs")
81
&& (-f "${lk_path}VERSION")
82
- && (-f "${lk_path}vl.c")) {
83
+ && (-d "${lk_path}linux-user/")
84
+ && (-d "${lk_path}softmmu/")) {
85
    return 1;
86
}
87
return 0;
88
diff --git a/softmmu/Makefile.objs b/softmmu/Makefile.objs
89
new file mode 100644
90
index XXXXXXX..XXXXXXX
91
--- /dev/null
92
+++ b/softmmu/Makefile.objs
93
@@ -XXX,XX +XXX,XX @@
94
+obj-y += vl.o
95
+vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
96
diff --git a/vl.c b/softmmu/vl.c
97
similarity index 100%
98
rename from vl.c
99
rename to softmmu/vl.c
100
--
101
2.24.1
102
diff view generated by jsdifflib
New patch
1
1
From: Alexander Bulekov <alxndr@bu.edu>
2
3
A program might rely on functions implemented in vl.c, but implement its
4
own main(). By placing main into a separate source file, there are no
5
complaints about duplicate main()s when linking against vl.o. For
6
example, the virtual-device fuzzer uses a main() provided by libfuzzer,
7
and needs to perform some initialization before running the softmmu
8
initialization. Now, main simply calls three vl.c functions which
9
handle the guest initialization, main loop and cleanup.
10
11
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
12
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
14
Message-id: 20200220041118.23264-3-alxndr@bu.edu
15
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
16
---
17
MAINTAINERS | 1 +
18
Makefile.target | 2 +-
19
include/sysemu/sysemu.h | 4 ++++
20
softmmu/Makefile.objs | 1 +
21
softmmu/main.c | 53 +++++++++++++++++++++++++++++++++++++++++
22
softmmu/vl.c | 36 +++++++---------------------
23
6 files changed, 69 insertions(+), 28 deletions(-)
24
create mode 100644 softmmu/main.c
25
26
diff --git a/MAINTAINERS b/MAINTAINERS
27
index XXXXXXX..XXXXXXX 100644
28
--- a/MAINTAINERS
29
+++ b/MAINTAINERS
30
@@ -XXX,XX +XXX,XX @@ F: include/sysemu/runstate.h
31
F: util/main-loop.c
32
F: util/qemu-timer.c
33
F: softmmu/vl.c
34
+F: softmmu/main.c
35
F: qapi/run-state.json
36
37
Human Monitor (HMP)
38
diff --git a/Makefile.target b/Makefile.target
39
index XXXXXXX..XXXXXXX 100644
40
--- a/Makefile.target
41
+++ b/Makefile.target
42
@@ -XXX,XX +XXX,XX @@ endif
43
COMMON_LDADDS = ../libqemuutil.a
44
45
# build either PROG or PROGW
46
-$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS)
47
+$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) $(softmmu-main-y)
48
    $(call LINK, $(filter-out %.mak, $^))
49
ifdef CONFIG_DARWIN
50
    $(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@")
51
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
52
index XXXXXXX..XXXXXXX 100644
53
--- a/include/sysemu/sysemu.h
54
+++ b/include/sysemu/sysemu.h
55
@@ -XXX,XX +XXX,XX @@ QemuOpts *qemu_get_machine_opts(void);
56
57
bool defaults_enabled(void);
58
59
+void qemu_init(int argc, char **argv, char **envp);
60
+void qemu_main_loop(void);
61
+void qemu_cleanup(void);
62
+
63
extern QemuOptsList qemu_legacy_drive_opts;
64
extern QemuOptsList qemu_common_drive_opts;
65
extern QemuOptsList qemu_drive_opts;
66
diff --git a/softmmu/Makefile.objs b/softmmu/Makefile.objs
67
index XXXXXXX..XXXXXXX 100644
68
--- a/softmmu/Makefile.objs
69
+++ b/softmmu/Makefile.objs
70
@@ -XXX,XX +XXX,XX @@
71
+softmmu-main-y = softmmu/main.o
72
obj-y += vl.o
73
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
74
diff --git a/softmmu/main.c b/softmmu/main.c
75
new file mode 100644
76
index XXXXXXX..XXXXXXX
77
--- /dev/null
78
+++ b/softmmu/main.c
79
@@ -XXX,XX +XXX,XX @@
80
+/*
81
+ * QEMU System Emulator
82
+ *
83
+ * Copyright (c) 2003-2020 Fabrice Bellard
84
+ *
85
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
86
+ * of this software and associated documentation files (the "Software"), to deal
87
+ * in the Software without restriction, including without limitation the rights
88
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
89
+ * copies of the Software, and to permit persons to whom the Software is
90
+ * furnished to do so, subject to the following conditions:
91
+ *
92
+ * The above copyright notice and this permission notice shall be included in
93
+ * all copies or substantial portions of the Software.
94
+ *
95
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
96
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
97
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
98
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
99
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
100
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
101
+ * THE SOFTWARE.
102
+ */
103
+
104
+#include "qemu/osdep.h"
105
+#include "qemu-common.h"
106
+#include "sysemu/sysemu.h"
107
+
108
+#ifdef CONFIG_SDL
109
+#if defined(__APPLE__) || defined(main)
110
+#include <SDL.h>
111
+int main(int argc, char **argv)
112
+{
113
+ return qemu_main(argc, argv, NULL);
114
+}
115
+#undef main
116
+#define main qemu_main
117
+#endif
118
+#endif /* CONFIG_SDL */
119
+
120
+#ifdef CONFIG_COCOA
121
+#undef main
122
+#define main qemu_main
123
+#endif /* CONFIG_COCOA */
124
+
125
+int main(int argc, char **argv, char **envp)
126
+{
127
+ qemu_init(argc, argv, envp);
128
+ qemu_main_loop();
129
+ qemu_cleanup();
130
+
131
+ return 0;
132
+}
133
diff --git a/softmmu/vl.c b/softmmu/vl.c
134
index XXXXXXX..XXXXXXX 100644
135
--- a/softmmu/vl.c
136
+++ b/softmmu/vl.c
137
@@ -XXX,XX +XXX,XX @@
138
#include "sysemu/seccomp.h"
139
#include "sysemu/tcg.h"
140
141
-#ifdef CONFIG_SDL
142
-#if defined(__APPLE__) || defined(main)
143
-#include <SDL.h>
144
-int qemu_main(int argc, char **argv, char **envp);
145
-int main(int argc, char **argv)
146
-{
147
- return qemu_main(argc, argv, NULL);
148
-}
149
-#undef main
150
-#define main qemu_main
151
-#endif
152
-#endif /* CONFIG_SDL */
153
-
154
-#ifdef CONFIG_COCOA
155
-#undef main
156
-#define main qemu_main
157
-#endif /* CONFIG_COCOA */
158
-
159
-
160
#include "qemu/error-report.h"
161
#include "qemu/sockets.h"
162
#include "sysemu/accel.h"
163
@@ -XXX,XX +XXX,XX @@ static bool main_loop_should_exit(void)
164
return false;
165
}
166
167
-static void main_loop(void)
168
+void qemu_main_loop(void)
169
{
170
#ifdef CONFIG_PROFILER
171
int64_t ti;
172
@@ -XXX,XX +XXX,XX @@ static void configure_accelerators(const char *progname)
173
}
174
}
175
176
-int main(int argc, char **argv, char **envp)
177
+void qemu_init(int argc, char **argv, char **envp)
178
{
179
int i;
180
int snapshot, linux_boot;
181
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
182
case QEMU_OPTION_watchdog:
183
if (watchdog) {
184
error_report("only one watchdog option may be given");
185
- return 1;
186
+ exit(1);
187
}
188
watchdog = optarg;
189
break;
190
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
191
parse_numa_opts(current_machine);
192
193
/* do monitor/qmp handling at preconfig state if requested */
194
- main_loop();
195
+ qemu_main_loop();
196
197
audio_init_audiodevs();
198
199
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
200
if (vmstate_dump_file) {
201
/* dump and exit */
202
dump_vmstate_json_to_file(vmstate_dump_file);
203
- return 0;
204
+ exit(0);
205
}
206
207
if (incoming) {
208
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
209
accel_setup_post(current_machine);
210
os_setup_post();
211
212
- main_loop();
213
+ return;
214
+}
215
216
+void qemu_cleanup(void)
217
+{
218
gdbserver_cleanup();
219
220
/*
221
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
222
qemu_chr_cleanup();
223
user_creatable_cleanup();
224
/* TODO: unref root container, check all devices are ok */
225
-
226
- return 0;
227
}
228
--
229
2.24.1
230
diff view generated by jsdifflib
New patch
1
From: Alexander Bulekov <alxndr@bu.edu>
1
2
3
The virtual-device fuzzer must initialize QOM, prior to running
4
vl:qemu_init, so that it can use the qos_graph to identify the arguments
5
required to initialize a guest for libqos-assisted fuzzing. This change
6
prevents errors when vl:qemu_init tries to (re)initialize the previously
7
initialized QOM module.
8
9
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
12
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
13
Message-id: 20200220041118.23264-4-alxndr@bu.edu
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
---
16
util/module.c | 7 +++++++
17
1 file changed, 7 insertions(+)
18
19
diff --git a/util/module.c b/util/module.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/util/module.c
22
+++ b/util/module.c
23
@@ -XXX,XX +XXX,XX @@ typedef struct ModuleEntry
24
typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList;
25
26
static ModuleTypeList init_type_list[MODULE_INIT_MAX];
27
+static bool modules_init_done[MODULE_INIT_MAX];
28
29
static ModuleTypeList dso_init_list;
30
31
@@ -XXX,XX +XXX,XX @@ void module_call_init(module_init_type type)
32
ModuleTypeList *l;
33
ModuleEntry *e;
34
35
+ if (modules_init_done[type]) {
36
+ return;
37
+ }
38
+
39
l = find_type(type);
40
41
QTAILQ_FOREACH(e, l, node) {
42
e->init();
43
}
44
+
45
+ modules_init_done[type] = true;
46
}
47
48
#ifdef CONFIG_MODULES
49
--
50
2.24.1
51
diff view generated by jsdifflib
New patch
1
From: Alexander Bulekov <alxndr@bu.edu>
1
2
3
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
4
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
6
Message-id: 20200220041118.23264-5-alxndr@bu.edu
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
---
9
include/qemu/module.h | 4 +++-
10
1 file changed, 3 insertions(+), 1 deletion(-)
11
12
diff --git a/include/qemu/module.h b/include/qemu/module.h
13
index XXXXXXX..XXXXXXX 100644
14
--- a/include/qemu/module.h
15
+++ b/include/qemu/module.h
16
@@ -XXX,XX +XXX,XX @@ typedef enum {
17
MODULE_INIT_TRACE,
18
MODULE_INIT_XEN_BACKEND,
19
MODULE_INIT_LIBQOS,
20
+ MODULE_INIT_FUZZ_TARGET,
21
MODULE_INIT_MAX
22
} module_init_type;
23
24
@@ -XXX,XX +XXX,XX @@ typedef enum {
25
#define xen_backend_init(function) module_init(function, \
26
MODULE_INIT_XEN_BACKEND)
27
#define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS)
28
-
29
+#define fuzz_target_init(function) module_init(function, \
30
+ MODULE_INIT_FUZZ_TARGET)
31
#define block_module_load_one(lib) module_load_one("block-", lib)
32
#define ui_module_load_one(lib) module_load_one("ui-", lib)
33
#define audio_module_load_one(lib) module_load_one("audio-", lib)
34
--
35
2.24.1
36
diff view generated by jsdifflib
1
This makes iotest 033 pass with e.g. subformat=monolithicFlat. It also
1
From: Alexander Bulekov <alxndr@bu.edu>
2
turns a former error in 059 into success.
3
2
4
Signed-off-by: Max Reitz <mreitz@redhat.com>
3
qtest_server_send is a function pointer specifying the handler used to
5
Message-id: 20190815153638.4600-3-mreitz@redhat.com
4
transmit data to the qtest client. In the standard configuration, this
6
Reviewed-by: John Snow <jsnow@redhat.com>
5
calls the CharBackend handler, but now it is possible for other types of
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
handlers, e.g direct-function calls if the qtest client and server
7
exist within the same process (inproc)
8
9
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
12
Acked-by: Thomas Huth <thuth@redhat.com>
13
Message-id: 20200220041118.23264-6-alxndr@bu.edu
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
---
15
---
9
block/vmdk.c | 54 ++++++++++++++++++++++++--------------
16
include/sysemu/qtest.h | 3 +++
10
tests/qemu-iotests/059 | 7 +++--
17
qtest.c | 18 ++++++++++++++++--
11
tests/qemu-iotests/059.out | 4 ++-
18
2 files changed, 19 insertions(+), 2 deletions(-)
12
3 files changed, 42 insertions(+), 23 deletions(-)
13
19
14
diff --git a/block/vmdk.c b/block/vmdk.c
20
diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
15
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
16
--- a/block/vmdk.c
22
--- a/include/sysemu/qtest.h
17
+++ b/block/vmdk.c
23
+++ b/include/sysemu/qtest.h
18
@@ -XXX,XX +XXX,XX @@ static const char *next_line(const char *s)
24
@@ -XXX,XX +XXX,XX @@ bool qtest_driver(void);
25
26
void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **errp);
27
28
+void qtest_server_set_send_handler(void (*send)(void *, const char *),
29
+ void *opaque);
30
+
31
#endif
32
diff --git a/qtest.c b/qtest.c
33
index XXXXXXX..XXXXXXX 100644
34
--- a/qtest.c
35
+++ b/qtest.c
36
@@ -XXX,XX +XXX,XX @@ static GString *inbuf;
37
static int irq_levels[MAX_IRQ];
38
static qemu_timeval start_time;
39
static bool qtest_opened;
40
+static void (*qtest_server_send)(void*, const char*);
41
+static void *qtest_server_send_opaque;
42
43
#define FMT_timeval "%ld.%06ld"
44
45
@@ -XXX,XX +XXX,XX @@ static void GCC_FMT_ATTR(1, 2) qtest_log_send(const char *fmt, ...)
46
va_end(ap);
19
}
47
}
20
48
21
static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
49
-static void do_qtest_send(CharBackend *chr, const char *str, size_t len)
22
- const char *desc_file_path, QDict *options,
50
+static void qtest_server_char_be_send(void *opaque, const char *str)
23
- Error **errp)
24
+ QDict *options, Error **errp)
25
{
51
{
26
int ret;
52
+ size_t len = strlen(str);
27
int matches;
53
+ CharBackend* chr = (CharBackend *)opaque;
28
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
54
qemu_chr_fe_write_all(chr, (uint8_t *)str, len);
29
const char *p, *np;
55
if (qtest_log_fp && qtest_opened) {
30
int64_t sectors = 0;
56
fprintf(qtest_log_fp, "%s", str);
31
int64_t flat_offset;
57
@@ -XXX,XX +XXX,XX @@ static void do_qtest_send(CharBackend *chr, const char *str, size_t len)
32
+ char *desc_file_dir = NULL;
58
33
char *extent_path;
59
static void qtest_send(CharBackend *chr, const char *str)
34
BdrvChild *extent_file;
60
{
35
BDRVVmdkState *s = bs->opaque;
61
- do_qtest_send(chr, str, strlen(str));
36
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
62
+ qtest_server_send(qtest_server_send_opaque, str);
37
continue;
63
}
38
}
64
39
65
static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharBackend *chr,
40
- if (!path_is_absolute(fname) && !path_has_protocol(fname) &&
66
@@ -XXX,XX +XXX,XX @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **
41
- !desc_file_path[0])
67
qemu_chr_fe_set_echo(&qtest_chr, true);
42
- {
68
43
- bdrv_refresh_filename(bs->file->bs);
69
inbuf = g_string_new("");
44
- error_setg(errp, "Cannot use relative extent paths with VMDK "
45
- "descriptor file '%s'", bs->file->bs->filename);
46
- return -EINVAL;
47
- }
48
+ if (path_is_absolute(fname)) {
49
+ extent_path = g_strdup(fname);
50
+ } else {
51
+ if (!desc_file_dir) {
52
+ desc_file_dir = bdrv_dirname(bs->file->bs, errp);
53
+ if (!desc_file_dir) {
54
+ bdrv_refresh_filename(bs->file->bs);
55
+ error_prepend(errp, "Cannot use relative paths with VMDK "
56
+ "descriptor file '%s': ",
57
+ bs->file->bs->filename);
58
+ ret = -EINVAL;
59
+ goto out;
60
+ }
61
+ }
62
63
- extent_path = path_combine(desc_file_path, fname);
64
+ extent_path = g_strconcat(desc_file_dir, fname, NULL);
65
+ }
66
67
ret = snprintf(extent_opt_prefix, 32, "extents.%d", s->num_extents);
68
assert(ret < 32);
69
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
70
g_free(extent_path);
71
if (local_err) {
72
error_propagate(errp, local_err);
73
- return -EINVAL;
74
+ ret = -EINVAL;
75
+ goto out;
76
}
77
78
/* save to extents array */
79
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
80
0, 0, 0, 0, 0, &extent, errp);
81
if (ret < 0) {
82
bdrv_unref_child(bs, extent_file);
83
- return ret;
84
+ goto out;
85
}
86
extent->flat_start_offset = flat_offset << 9;
87
} else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) {
88
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
89
g_free(buf);
90
if (ret) {
91
bdrv_unref_child(bs, extent_file);
92
- return ret;
93
+ goto out;
94
}
95
extent = &s->extents[s->num_extents - 1];
96
} else if (!strcmp(type, "SESPARSE")) {
97
ret = vmdk_open_se_sparse(bs, extent_file, bs->open_flags, errp);
98
if (ret) {
99
bdrv_unref_child(bs, extent_file);
100
- return ret;
101
+ goto out;
102
}
103
extent = &s->extents[s->num_extents - 1];
104
} else {
105
error_setg(errp, "Unsupported extent type '%s'", type);
106
bdrv_unref_child(bs, extent_file);
107
- return -ENOTSUP;
108
+ ret = -ENOTSUP;
109
+ goto out;
110
}
111
extent->type = g_strdup(type);
112
}
113
- return 0;
114
+
70
+
115
+ ret = 0;
71
+ if (!qtest_server_send) {
116
+ goto out;
72
+ qtest_server_set_send_handler(qtest_server_char_be_send, &qtest_chr);
117
73
+ }
118
invalid:
74
+}
119
np = next_line(p);
120
@@ -XXX,XX +XXX,XX @@ invalid:
121
np--;
122
}
123
error_setg(errp, "Invalid extent line: %.*s", (int)(np - p), p);
124
- return -EINVAL;
125
+ ret = -EINVAL;
126
+
75
+
127
+out:
76
+void qtest_server_set_send_handler(void (*send)(void*, const char*), void *opaque)
128
+ g_free(desc_file_dir);
77
+{
129
+ return ret;
78
+ qtest_server_send = send;
79
+ qtest_server_send_opaque = opaque;
130
}
80
}
131
81
132
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
82
bool qtest_driver(void)
133
@@ -XXX,XX +XXX,XX @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
134
}
135
s->create_type = g_strdup(ct);
136
s->desc_offset = 0;
137
- ret = vmdk_parse_extents(buf, bs, bs->file->bs->exact_filename, options,
138
- errp);
139
+ ret = vmdk_parse_extents(buf, bs, options, errp);
140
exit:
141
return ret;
142
}
143
diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059
144
index XXXXXXX..XXXXXXX 100755
145
--- a/tests/qemu-iotests/059
146
+++ b/tests/qemu-iotests/059
147
@@ -XXX,XX +XXX,XX @@ $QEMU_IMG convert -f qcow2 -O vmdk -o subformat=streamOptimized "$TEST_IMG.qcow2
148
149
echo
150
echo "=== Testing monolithicFlat with internally generated JSON file name ==="
151
+# Should work, because bdrv_dirname() works fine with blkdebug
152
IMGOPTS="subformat=monolithicFlat" _make_test_img 64M
153
-$QEMU_IO -c "open -o driver=$IMGFMT,file.driver=blkdebug,file.image.filename=$TEST_IMG,file.inject-error.0.event=read_aio" 2>&1 \
154
- | _filter_testdir | _filter_imgfmt
155
+$QEMU_IO -c "open -o driver=$IMGFMT,file.driver=blkdebug,file.image.filename=$TEST_IMG,file.inject-error.0.event=read_aio" \
156
+ -c info \
157
+ 2>&1 \
158
+ | _filter_testdir | _filter_imgfmt | _filter_img_info
159
_cleanup_test_img
160
161
echo
162
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
163
index XXXXXXX..XXXXXXX 100644
164
--- a/tests/qemu-iotests/059.out
165
+++ b/tests/qemu-iotests/059.out
166
@@ -XXX,XX +XXX,XX @@ wrote 512/512 bytes at offset 10240
167
168
=== Testing monolithicFlat with internally generated JSON file name ===
169
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
170
-qemu-io: can't open: Cannot use relative extent paths with VMDK descriptor file 'json:{"image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "inject-error.0.event": "read_aio"}'
171
+format name: IMGFMT
172
+cluster size: 0 bytes
173
+vm state offset: 0 bytes
174
175
=== Testing version 3 ===
176
image: TEST_DIR/iotest-version3.IMGFMT
177
--
83
--
178
2.21.0
84
2.24.1
179
85
180
diff view generated by jsdifflib
New patch
1
From: Alexander Bulekov <alxndr@bu.edu>
1
2
3
This makes it simple to swap the transport functions for qtest commands
4
to and from the qtest client. For example, now it is possible to
5
directly pass qtest commands to a server handler that exists within the
6
same process, without the standard way of writing to a file descriptor.
7
8
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
11
Message-id: 20200220041118.23264-7-alxndr@bu.edu
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
14
tests/qtest/libqtest.c | 48 ++++++++++++++++++++++++++++++++++--------
15
1 file changed, 39 insertions(+), 9 deletions(-)
16
17
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/tests/qtest/libqtest.c
20
+++ b/tests/qtest/libqtest.c
21
@@ -XXX,XX +XXX,XX @@
22
#define SOCKET_TIMEOUT 50
23
#define SOCKET_MAX_FDS 16
24
25
+
26
+typedef void (*QTestSendFn)(QTestState *s, const char *buf);
27
+typedef GString* (*QTestRecvFn)(QTestState *);
28
+
29
+typedef struct QTestClientTransportOps {
30
+ QTestSendFn send; /* for sending qtest commands */
31
+ QTestRecvFn recv_line; /* for receiving qtest command responses */
32
+} QTestTransportOps;
33
+
34
struct QTestState
35
{
36
int fd;
37
@@ -XXX,XX +XXX,XX @@ struct QTestState
38
bool big_endian;
39
bool irq_level[MAX_IRQ];
40
GString *rx;
41
+ QTestTransportOps ops;
42
};
43
44
static GHookList abrt_hooks;
45
@@ -XXX,XX +XXX,XX @@ static struct sigaction sigact_old;
46
47
static int qtest_query_target_endianness(QTestState *s);
48
49
+static void qtest_client_socket_send(QTestState*, const char *buf);
50
+static void socket_send(int fd, const char *buf, size_t size);
51
+
52
+static GString *qtest_client_socket_recv_line(QTestState *);
53
+
54
+static void qtest_client_set_tx_handler(QTestState *s, QTestSendFn send);
55
+static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv);
56
+
57
static int init_socket(const char *socket_path)
58
{
59
struct sockaddr_un addr;
60
@@ -XXX,XX +XXX,XX @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
61
sock = init_socket(socket_path);
62
qmpsock = init_socket(qmp_socket_path);
63
64
+ qtest_client_set_rx_handler(s, qtest_client_socket_recv_line);
65
+ qtest_client_set_tx_handler(s, qtest_client_socket_send);
66
+
67
qtest_add_abrt_handler(kill_qemu_hook_func, s);
68
69
command = g_strdup_printf("exec %s "
70
@@ -XXX,XX +XXX,XX @@ static void socket_send(int fd, const char *buf, size_t size)
71
}
72
}
73
74
-static void socket_sendf(int fd, const char *fmt, va_list ap)
75
+static void qtest_client_socket_send(QTestState *s, const char *buf)
76
{
77
- gchar *str = g_strdup_vprintf(fmt, ap);
78
- size_t size = strlen(str);
79
-
80
- socket_send(fd, str, size);
81
- g_free(str);
82
+ socket_send(s->fd, buf, strlen(buf));
83
}
84
85
static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...)
86
@@ -XXX,XX +XXX,XX @@ static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...)
87
va_list ap;
88
89
va_start(ap, fmt);
90
- socket_sendf(s->fd, fmt, ap);
91
+ gchar *str = g_strdup_vprintf(fmt, ap);
92
va_end(ap);
93
+
94
+ s->ops.send(s, str);
95
+ g_free(str);
96
}
97
98
/* Sends a message and file descriptors to the socket.
99
@@ -XXX,XX +XXX,XX @@ static void socket_send_fds(int socket_fd, int *fds, size_t fds_num,
100
g_assert_cmpint(ret, >, 0);
101
}
102
103
-static GString *qtest_recv_line(QTestState *s)
104
+static GString *qtest_client_socket_recv_line(QTestState *s)
105
{
106
GString *line;
107
size_t offset;
108
@@ -XXX,XX +XXX,XX @@ static gchar **qtest_rsp(QTestState *s, int expected_args)
109
int i;
110
111
redo:
112
- line = qtest_recv_line(s);
113
+ line = s->ops.recv_line(s);
114
words = g_strsplit(line->str, " ", 0);
115
g_string_free(line, TRUE);
116
117
@@ -XXX,XX +XXX,XX @@ void qmp_assert_error_class(QDict *rsp, const char *class)
118
119
qobject_unref(rsp);
120
}
121
+
122
+static void qtest_client_set_tx_handler(QTestState *s,
123
+ QTestSendFn send)
124
+{
125
+ s->ops.send = send;
126
+}
127
+static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv)
128
+{
129
+ s->ops.recv_line = recv;
130
+}
131
--
132
2.24.1
133
diff view generated by jsdifflib
New patch
1
From: Alexander Bulekov <alxndr@bu.edu>
1
2
3
When using qtest "in-process" communication, qtest_sendf directly calls
4
a function in the server (qtest.c). Previously, bufwrite used
5
socket_send, which bypasses the TransportOps enabling the call into
6
qtest.c. This change replaces the socket_send calls with ops->send,
7
maintaining the benefits of the direct socket_send call, while adding
8
support for in-process qtest calls.
9
10
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
13
Message-id: 20200220041118.23264-8-alxndr@bu.edu
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
---
16
tests/qtest/libqtest.c | 71 ++++++++++++++++++++++++++++++++++++++++--
17
tests/qtest/libqtest.h | 4 +++
18
2 files changed, 73 insertions(+), 2 deletions(-)
19
20
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/tests/qtest/libqtest.c
23
+++ b/tests/qtest/libqtest.c
24
@@ -XXX,XX +XXX,XX @@
25
26
27
typedef void (*QTestSendFn)(QTestState *s, const char *buf);
28
+typedef void (*ExternalSendFn)(void *s, const char *buf);
29
typedef GString* (*QTestRecvFn)(QTestState *);
30
31
typedef struct QTestClientTransportOps {
32
QTestSendFn send; /* for sending qtest commands */
33
+
34
+ /*
35
+ * use external_send to send qtest command strings through functions which
36
+ * do not accept a QTestState as the first parameter.
37
+ */
38
+ ExternalSendFn external_send;
39
+
40
QTestRecvFn recv_line; /* for receiving qtest command responses */
41
} QTestTransportOps;
42
43
@@ -XXX,XX +XXX,XX @@ void qtest_bufwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
44
45
bdata = g_base64_encode(data, size);
46
qtest_sendf(s, "b64write 0x%" PRIx64 " 0x%zx ", addr, size);
47
- socket_send(s->fd, bdata, strlen(bdata));
48
- socket_send(s->fd, "\n", 1);
49
+ s->ops.send(s, bdata);
50
+ s->ops.send(s, "\n");
51
qtest_rsp(s, 0);
52
g_free(bdata);
53
}
54
@@ -XXX,XX +XXX,XX @@ static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv)
55
{
56
s->ops.recv_line = recv;
57
}
58
+/* A type-safe wrapper for s->send() */
59
+static void send_wrapper(QTestState *s, const char *buf)
60
+{
61
+ s->ops.external_send(s, buf);
62
+}
63
+
64
+static GString *qtest_client_inproc_recv_line(QTestState *s)
65
+{
66
+ GString *line;
67
+ size_t offset;
68
+ char *eol;
69
+
70
+ eol = strchr(s->rx->str, '\n');
71
+ offset = eol - s->rx->str;
72
+ line = g_string_new_len(s->rx->str, offset);
73
+ g_string_erase(s->rx, 0, offset + 1);
74
+ return line;
75
+}
76
+
77
+QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch,
78
+ void (*send)(void*, const char*))
79
+{
80
+ QTestState *qts;
81
+ qts = g_new0(QTestState, 1);
82
+ *s = qts; /* Expose qts early on, since the query endianness relies on it */
83
+ qts->wstatus = 0;
84
+ for (int i = 0; i < MAX_IRQ; i++) {
85
+ qts->irq_level[i] = false;
86
+ }
87
+
88
+ qtest_client_set_rx_handler(qts, qtest_client_inproc_recv_line);
89
+
90
+ /* send() may not have a matching protoype, so use a type-safe wrapper */
91
+ qts->ops.external_send = send;
92
+ qtest_client_set_tx_handler(qts, send_wrapper);
93
+
94
+ qts->big_endian = qtest_query_target_endianness(qts);
95
+
96
+ /*
97
+ * Set a dummy path for QTEST_QEMU_BINARY. Doesn't need to exist, but this
98
+ * way, qtest_get_arch works for inproc qtest.
99
+ */
100
+ gchar *bin_path = g_strconcat("/qemu-system-", arch, NULL);
101
+ setenv("QTEST_QEMU_BINARY", bin_path, 0);
102
+ g_free(bin_path);
103
+
104
+ return qts;
105
+}
106
+
107
+void qtest_client_inproc_recv(void *opaque, const char *str)
108
+{
109
+ QTestState *qts = *(QTestState **)opaque;
110
+
111
+ if (!qts->rx) {
112
+ qts->rx = g_string_new(NULL);
113
+ }
114
+ g_string_append(qts->rx, str);
115
+ return;
116
+}
117
diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h
118
index XXXXXXX..XXXXXXX 100644
119
--- a/tests/qtest/libqtest.h
120
+++ b/tests/qtest/libqtest.h
121
@@ -XXX,XX +XXX,XX @@ bool qtest_probe_child(QTestState *s);
122
*/
123
void qtest_set_expected_status(QTestState *s, int status);
124
125
+QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch,
126
+ void (*send)(void*, const char*));
127
+
128
+void qtest_client_inproc_recv(void *opaque, const char *str);
129
#endif
130
--
131
2.24.1
132
diff view generated by jsdifflib
New patch
1
From: Alexander Bulekov <alxndr@bu.edu>
1
2
3
The handler allows a qtest client to send commands to the server by
4
directly calling a function, rather than using a file/CharBackend
5
6
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
9
Message-id: 20200220041118.23264-9-alxndr@bu.edu
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
12
include/sysemu/qtest.h | 1 +
13
qtest.c | 13 +++++++++++++
14
2 files changed, 14 insertions(+)
15
16
diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
17
index XXXXXXX..XXXXXXX 100644
18
--- a/include/sysemu/qtest.h
19
+++ b/include/sysemu/qtest.h
20
@@ -XXX,XX +XXX,XX @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **
21
22
void qtest_server_set_send_handler(void (*send)(void *, const char *),
23
void *opaque);
24
+void qtest_server_inproc_recv(void *opaque, const char *buf);
25
26
#endif
27
diff --git a/qtest.c b/qtest.c
28
index XXXXXXX..XXXXXXX 100644
29
--- a/qtest.c
30
+++ b/qtest.c
31
@@ -XXX,XX +XXX,XX @@ bool qtest_driver(void)
32
{
33
return qtest_chr.chr != NULL;
34
}
35
+
36
+void qtest_server_inproc_recv(void *dummy, const char *buf)
37
+{
38
+ static GString *gstr;
39
+ if (!gstr) {
40
+ gstr = g_string_new(NULL);
41
+ }
42
+ g_string_append(gstr, buf);
43
+ if (gstr->str[gstr->len - 1] == '\n') {
44
+ qtest_process_inbuf(NULL, gstr);
45
+ g_string_truncate(gstr, 0);
46
+ }
47
+}
48
--
49
2.24.1
50
diff view generated by jsdifflib
1
From: Nir Soffer <nirsof@gmail.com>
1
From: Alexander Bulekov <alxndr@bu.edu>
2
2
3
Quoting cache mode is not needed, and most tests use unquoted values.
3
The names i2c_send and i2c_recv collide with functions defined in
4
Unify all test to use the same style.
4
hw/i2c/core.c. This causes an error when linking against libqos and
5
softmmu simultaneously (for example when using qtest inproc). Rename the
6
libqos functions to avoid this.
5
7
6
Message-id: 20190827173432.7656-1-nsoffer@redhat.com
8
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
7
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
11
Acked-by: Thomas Huth <thuth@redhat.com>
12
Message-id: 20200220041118.23264-10-alxndr@bu.edu
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
14
---
10
tests/qemu-iotests/026 | 4 ++--
15
tests/qtest/libqos/i2c.c | 10 +++++-----
11
tests/qemu-iotests/039 | 4 ++--
16
tests/qtest/libqos/i2c.h | 4 ++--
12
tests/qemu-iotests/052 | 2 +-
17
tests/qtest/pca9552-test.c | 10 +++++-----
13
tests/qemu-iotests/091 | 4 ++--
18
3 files changed, 12 insertions(+), 12 deletions(-)
14
4 files changed, 7 insertions(+), 7 deletions(-)
15
19
16
diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026
20
diff --git a/tests/qtest/libqos/i2c.c b/tests/qtest/libqos/i2c.c
17
index XXXXXXX..XXXXXXX 100755
21
index XXXXXXX..XXXXXXX 100644
18
--- a/tests/qemu-iotests/026
22
--- a/tests/qtest/libqos/i2c.c
19
+++ b/tests/qemu-iotests/026
23
+++ b/tests/qtest/libqos/i2c.c
20
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
24
@@ -XXX,XX +XXX,XX @@
21
# Currently only qcow2 supports rebasing
25
#include "libqos/i2c.h"
22
_supported_fmt qcow2
26
#include "libqtest.h"
23
_supported_proto file
27
24
-_default_cache_mode "writethrough"
28
-void i2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len)
25
-_supported_cache_modes "writethrough" "none"
29
+void qi2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len)
26
+_default_cache_mode writethrough
30
{
27
+_supported_cache_modes writethrough none
31
i2cdev->bus->send(i2cdev->bus, i2cdev->addr, buf, len);
28
# The refcount table tests expect a certain minimum width for refcount entries
32
}
29
# (so that the refcount table actually needs to grow); that minimum is 16 bits,
33
30
# being the default refcount entry width.
34
-void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
31
diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039
35
+void qi2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
32
index XXXXXXX..XXXXXXX 100755
36
{
33
--- a/tests/qemu-iotests/039
37
i2cdev->bus->recv(i2cdev->bus, i2cdev->addr, buf, len);
34
+++ b/tests/qemu-iotests/039
38
}
35
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
39
@@ -XXX,XX +XXX,XX @@ void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
36
_supported_fmt qcow2
40
void i2c_read_block(QI2CDevice *i2cdev, uint8_t reg,
37
_supported_proto file
41
uint8_t *buf, uint16_t len)
38
_supported_os Linux
42
{
39
-_default_cache_mode "writethrough"
43
- i2c_send(i2cdev, &reg, 1);
40
-_supported_cache_modes "writethrough"
44
- i2c_recv(i2cdev, buf, len);
41
+_default_cache_mode writethrough
45
+ qi2c_send(i2cdev, &reg, 1);
42
+_supported_cache_modes writethrough
46
+ qi2c_recv(i2cdev, buf, len);
43
47
}
44
size=128M
48
45
49
void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg,
46
diff --git a/tests/qemu-iotests/052 b/tests/qemu-iotests/052
50
@@ -XXX,XX +XXX,XX @@ void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg,
47
index XXXXXXX..XXXXXXX 100755
51
uint8_t *cmd = g_malloc(len + 1);
48
--- a/tests/qemu-iotests/052
52
cmd[0] = reg;
49
+++ b/tests/qemu-iotests/052
53
memcpy(&cmd[1], buf, len);
50
@@ -XXX,XX +XXX,XX @@ _supported_fmt generic
54
- i2c_send(i2cdev, cmd, len + 1);
51
_supported_proto file
55
+ qi2c_send(i2cdev, cmd, len + 1);
52
56
g_free(cmd);
53
# Don't do O_DIRECT on tmpfs
57
}
54
-_supported_cache_modes "writeback" "writethrough" "unsafe"
58
55
+_supported_cache_modes writeback writethrough unsafe
59
diff --git a/tests/qtest/libqos/i2c.h b/tests/qtest/libqos/i2c.h
56
60
index XXXXXXX..XXXXXXX 100644
57
size=128M
61
--- a/tests/qtest/libqos/i2c.h
58
_make_test_img $size
62
+++ b/tests/qtest/libqos/i2c.h
59
diff --git a/tests/qemu-iotests/091 b/tests/qemu-iotests/091
63
@@ -XXX,XX +XXX,XX @@ struct QI2CDevice {
60
index XXXXXXX..XXXXXXX 100755
64
void *i2c_device_create(void *i2c_bus, QGuestAllocator *alloc, void *addr);
61
--- a/tests/qemu-iotests/091
65
void add_qi2c_address(QOSGraphEdgeOptions *opts, QI2CAddress *addr);
62
+++ b/tests/qemu-iotests/091
66
63
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
67
-void i2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len);
64
_supported_fmt qcow2
68
-void i2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len);
65
_supported_proto file
69
+void qi2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len);
66
_supported_os Linux
70
+void qi2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len);
67
-_default_cache_mode "none"
71
68
-_supported_cache_modes "writethrough" "none" "writeback"
72
void i2c_read_block(QI2CDevice *dev, uint8_t reg,
69
+_default_cache_mode none
73
uint8_t *buf, uint16_t len);
70
+_supported_cache_modes writethrough none writeback
74
diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
71
75
index XXXXXXX..XXXXXXX 100644
72
size=1G
76
--- a/tests/qtest/pca9552-test.c
77
+++ b/tests/qtest/pca9552-test.c
78
@@ -XXX,XX +XXX,XX @@ static void receive_autoinc(void *obj, void *data, QGuestAllocator *alloc)
79
80
pca9552_init(i2cdev);
81
82
- i2c_send(i2cdev, &reg, 1);
83
+ qi2c_send(i2cdev, &reg, 1);
84
85
/* PCA9552_LS0 */
86
- i2c_recv(i2cdev, &resp, 1);
87
+ qi2c_recv(i2cdev, &resp, 1);
88
g_assert_cmphex(resp, ==, 0x54);
89
90
/* PCA9552_LS1 */
91
- i2c_recv(i2cdev, &resp, 1);
92
+ qi2c_recv(i2cdev, &resp, 1);
93
g_assert_cmphex(resp, ==, 0x55);
94
95
/* PCA9552_LS2 */
96
- i2c_recv(i2cdev, &resp, 1);
97
+ qi2c_recv(i2cdev, &resp, 1);
98
g_assert_cmphex(resp, ==, 0x55);
99
100
/* PCA9552_LS3 */
101
- i2c_recv(i2cdev, &resp, 1);
102
+ qi2c_recv(i2cdev, &resp, 1);
103
g_assert_cmphex(resp, ==, 0x54);
104
}
73
105
74
--
106
--
75
2.21.0
107
2.24.1
76
108
77
diff view generated by jsdifflib
New patch
1
From: Alexander Bulekov <alxndr@bu.edu>
1
2
3
Most qos-related objects were specified in the qos-test-obj-y variable.
4
qos-test-obj-y also included qos-test.o which defines a main().
5
This made it difficult to repurpose qos-test-obj-y to link anything
6
beside tests/qos-test against libqos. This change separates objects that
7
are libqos-specific and ones that are qos-test specific into different
8
variables.
9
10
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
11
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
12
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
14
Message-id: 20200220041118.23264-11-alxndr@bu.edu
15
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
16
---
17
tests/qtest/Makefile.include | 71 ++++++++++++++++++------------------
18
1 file changed, 36 insertions(+), 35 deletions(-)
19
20
diff --git a/tests/qtest/Makefile.include b/tests/qtest/Makefile.include
21
index XXXXXXX..XXXXXXX 100644
22
--- a/tests/qtest/Makefile.include
23
+++ b/tests/qtest/Makefile.include
24
@@ -XXX,XX +XXX,XX @@ check-qtest-s390x-y += migration-test
25
# libqos / qgraph :
26
libqgraph-obj-y = tests/qtest/libqos/qgraph.o
27
28
-libqos-obj-y = $(libqgraph-obj-y) tests/qtest/libqos/pci.o tests/qtest/libqos/fw_cfg.o
29
-libqos-obj-y += tests/qtest/libqos/malloc.o
30
-libqos-obj-y += tests/qtest/libqos/libqos.o
31
-libqos-spapr-obj-y = $(libqos-obj-y) tests/qtest/libqos/malloc-spapr.o
32
+libqos-core-obj-y = $(libqgraph-obj-y) tests/qtest/libqos/pci.o tests/qtest/libqos/fw_cfg.o
33
+libqos-core-obj-y += tests/qtest/libqos/malloc.o
34
+libqos-core-obj-y += tests/qtest/libqos/libqos.o
35
+libqos-spapr-obj-y = $(libqos-core-obj-y) tests/qtest/libqos/malloc-spapr.o
36
libqos-spapr-obj-y += tests/qtest/libqos/libqos-spapr.o
37
libqos-spapr-obj-y += tests/qtest/libqos/rtas.o
38
libqos-spapr-obj-y += tests/qtest/libqos/pci-spapr.o
39
-libqos-pc-obj-y = $(libqos-obj-y) tests/qtest/libqos/pci-pc.o
40
+libqos-pc-obj-y = $(libqos-core-obj-y) tests/qtest/libqos/pci-pc.o
41
libqos-pc-obj-y += tests/qtest/libqos/malloc-pc.o tests/qtest/libqos/libqos-pc.o
42
libqos-pc-obj-y += tests/qtest/libqos/ahci.o
43
libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/qtest/libqos/usb.o
44
45
# qos devices:
46
-qos-test-obj-y = tests/qtest/qos-test.o $(libqgraph-obj-y)
47
-qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
48
-qos-test-obj-y += tests/qtest/libqos/e1000e.o
49
-qos-test-obj-y += tests/qtest/libqos/i2c.o
50
-qos-test-obj-y += tests/qtest/libqos/i2c-imx.o
51
-qos-test-obj-y += tests/qtest/libqos/i2c-omap.o
52
-qos-test-obj-y += tests/qtest/libqos/sdhci.o
53
-qos-test-obj-y += tests/qtest/libqos/tpci200.o
54
-qos-test-obj-y += tests/qtest/libqos/virtio.o
55
-qos-test-obj-$(CONFIG_VIRTFS) += tests/qtest/libqos/virtio-9p.o
56
-qos-test-obj-y += tests/qtest/libqos/virtio-balloon.o
57
-qos-test-obj-y += tests/qtest/libqos/virtio-blk.o
58
-qos-test-obj-y += tests/qtest/libqos/virtio-mmio.o
59
-qos-test-obj-y += tests/qtest/libqos/virtio-net.o
60
-qos-test-obj-y += tests/qtest/libqos/virtio-pci.o
61
-qos-test-obj-y += tests/qtest/libqos/virtio-pci-modern.o
62
-qos-test-obj-y += tests/qtest/libqos/virtio-rng.o
63
-qos-test-obj-y += tests/qtest/libqos/virtio-scsi.o
64
-qos-test-obj-y += tests/qtest/libqos/virtio-serial.o
65
+libqos-obj-y = $(libqgraph-obj-y)
66
+libqos-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
67
+libqos-obj-y += tests/qtest/libqos/e1000e.o
68
+libqos-obj-y += tests/qtest/libqos/i2c.o
69
+libqos-obj-y += tests/qtest/libqos/i2c-imx.o
70
+libqos-obj-y += tests/qtest/libqos/i2c-omap.o
71
+libqos-obj-y += tests/qtest/libqos/sdhci.o
72
+libqos-obj-y += tests/qtest/libqos/tpci200.o
73
+libqos-obj-y += tests/qtest/libqos/virtio.o
74
+libqos-obj-$(CONFIG_VIRTFS) += tests/qtest/libqos/virtio-9p.o
75
+libqos-obj-y += tests/qtest/libqos/virtio-balloon.o
76
+libqos-obj-y += tests/qtest/libqos/virtio-blk.o
77
+libqos-obj-y += tests/qtest/libqos/virtio-mmio.o
78
+libqos-obj-y += tests/qtest/libqos/virtio-net.o
79
+libqos-obj-y += tests/qtest/libqos/virtio-pci.o
80
+libqos-obj-y += tests/qtest/libqos/virtio-pci-modern.o
81
+libqos-obj-y += tests/qtest/libqos/virtio-rng.o
82
+libqos-obj-y += tests/qtest/libqos/virtio-scsi.o
83
+libqos-obj-y += tests/qtest/libqos/virtio-serial.o
84
85
# qos machines:
86
-qos-test-obj-y += tests/qtest/libqos/aarch64-xlnx-zcu102-machine.o
87
-qos-test-obj-y += tests/qtest/libqos/arm-imx25-pdk-machine.o
88
-qos-test-obj-y += tests/qtest/libqos/arm-n800-machine.o
89
-qos-test-obj-y += tests/qtest/libqos/arm-raspi2-machine.o
90
-qos-test-obj-y += tests/qtest/libqos/arm-sabrelite-machine.o
91
-qos-test-obj-y += tests/qtest/libqos/arm-smdkc210-machine.o
92
-qos-test-obj-y += tests/qtest/libqos/arm-virt-machine.o
93
-qos-test-obj-y += tests/qtest/libqos/arm-xilinx-zynq-a9-machine.o
94
-qos-test-obj-y += tests/qtest/libqos/ppc64_pseries-machine.o
95
-qos-test-obj-y += tests/qtest/libqos/x86_64_pc-machine.o
96
+libqos-obj-y += tests/qtest/libqos/aarch64-xlnx-zcu102-machine.o
97
+libqos-obj-y += tests/qtest/libqos/arm-imx25-pdk-machine.o
98
+libqos-obj-y += tests/qtest/libqos/arm-n800-machine.o
99
+libqos-obj-y += tests/qtest/libqos/arm-raspi2-machine.o
100
+libqos-obj-y += tests/qtest/libqos/arm-sabrelite-machine.o
101
+libqos-obj-y += tests/qtest/libqos/arm-smdkc210-machine.o
102
+libqos-obj-y += tests/qtest/libqos/arm-virt-machine.o
103
+libqos-obj-y += tests/qtest/libqos/arm-xilinx-zynq-a9-machine.o
104
+libqos-obj-y += tests/qtest/libqos/ppc64_pseries-machine.o
105
+libqos-obj-y += tests/qtest/libqos/x86_64_pc-machine.o
106
107
# qos tests:
108
+qos-test-obj-y += tests/qtest/qos-test.o
109
qos-test-obj-y += tests/qtest/ac97-test.o
110
qos-test-obj-y += tests/qtest/ds1338-test.o
111
qos-test-obj-y += tests/qtest/e1000-test.o
112
@@ -XXX,XX +XXX,XX @@ check-unit-y += tests/test-qgraph$(EXESUF)
113
tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y)
114
115
check-qtest-generic-y += qos-test
116
-tests/qtest/qos-test$(EXESUF): $(qos-test-obj-y)
117
+tests/qtest/qos-test$(EXESUF): $(qos-test-obj-y) $(libqos-obj-y)
118
119
# QTest dependencies:
120
tests/qtest/qmp-test$(EXESUF): tests/qtest/qmp-test.o
121
--
122
2.24.1
123
diff view generated by jsdifflib
1
From: Nir Soffer <nirsof@gmail.com>
1
From: Alexander Bulekov <alxndr@bu.edu>
2
2
3
When creating an image with preallocation "off" or "falloc", the first
3
The moved functions are not specific to qos-test and might be useful
4
block of the image is typically not allocated. When using Gluster
4
elsewhere. For example the virtual-device fuzzer makes use of them for
5
storage backed by XFS filesystem, reading this block using direct I/O
5
qos-assisted fuzz-targets.
6
succeeds regardless of request length, fooling alignment detection.
7
6
8
In this case we fallback to a safe value (4096) instead of the optimal
7
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
9
value (512), which may lead to unneeded data copying when aligning
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
requests. Allocating the first block avoids the fallback.
9
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
10
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
11
Message-id: 20200220041118.23264-12-alxndr@bu.edu
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
14
tests/qtest/Makefile.include | 1 +
15
tests/qtest/libqos/qos_external.c | 168 ++++++++++++++++++++++++++++++
16
tests/qtest/libqos/qos_external.h | 28 +++++
17
tests/qtest/qos-test.c | 132 +----------------------
18
4 files changed, 198 insertions(+), 131 deletions(-)
19
create mode 100644 tests/qtest/libqos/qos_external.c
20
create mode 100644 tests/qtest/libqos/qos_external.h
11
21
12
Since we allocate the first block even with preallocation=off, we no
22
diff --git a/tests/qtest/Makefile.include b/tests/qtest/Makefile.include
13
longer create images with zero disk size:
14
15
$ ./qemu-img create -f raw test.raw 1g
16
Formatting 'test.raw', fmt=raw size=1073741824
17
18
$ ls -lhs test.raw
19
4.0K -rw-r--r--. 1 nsoffer nsoffer 1.0G Aug 16 23:48 test.raw
20
21
And converting the image requires additional cluster:
22
23
$ ./qemu-img measure -f raw -O qcow2 test.raw
24
required size: 458752
25
fully allocated size: 1074135040
26
27
When using format like vmdk with multiple files per image, we allocate
28
one block per file:
29
30
$ ./qemu-img create -f vmdk -o subformat=twoGbMaxExtentFlat test.vmdk 4g
31
Formatting 'test.vmdk', fmt=vmdk size=4294967296 compat6=off hwversion=undefined subformat=twoGbMaxExtentFlat
32
33
$ ls -lhs test*.vmdk
34
4.0K -rw-r--r--. 1 nsoffer nsoffer 2.0G Aug 27 03:23 test-f001.vmdk
35
4.0K -rw-r--r--. 1 nsoffer nsoffer 2.0G Aug 27 03:23 test-f002.vmdk
36
4.0K -rw-r--r--. 1 nsoffer nsoffer 353 Aug 27 03:23 test.vmdk
37
38
I did quick performance test for copying disks with qemu-img convert to
39
new raw target image to Gluster storage with sector size of 512 bytes:
40
41
for i in $(seq 10); do
42
rm -f dst.raw
43
sleep 10
44
time ./qemu-img convert -f raw -O raw -t none -T none src.raw dst.raw
45
done
46
47
Here is a table comparing the total time spent:
48
49
Type Before(s) After(s) Diff(%)
50
---------------------------------------
51
real 530.028 469.123 -11.4
52
user 17.204 10.768 -37.4
53
sys 17.881 7.011 -60.7
54
55
We can see very clear improvement in CPU usage.
56
57
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
58
Message-id: 20190827010528.8818-2-nsoffer@redhat.com
59
Reviewed-by: Max Reitz <mreitz@redhat.com>
60
Signed-off-by: Max Reitz <mreitz@redhat.com>
61
---
62
block/file-posix.c | 51 +++++++++++++++++++
63
tests/qemu-iotests/059.out | 2 +-
64
tests/qemu-iotests/{150.out => 150.out.qcow2} | 0
65
tests/qemu-iotests/150.out.raw | 12 +++++
66
tests/qemu-iotests/175 | 19 ++++---
67
tests/qemu-iotests/175.out | 8 +--
68
tests/qemu-iotests/178.out.qcow2 | 4 +-
69
tests/qemu-iotests/221.out | 12 +++--
70
tests/qemu-iotests/253.out | 12 +++--
71
9 files changed, 99 insertions(+), 21 deletions(-)
72
rename tests/qemu-iotests/{150.out => 150.out.qcow2} (100%)
73
create mode 100644 tests/qemu-iotests/150.out.raw
74
75
diff --git a/block/file-posix.c b/block/file-posix.c
76
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
77
--- a/block/file-posix.c
24
--- a/tests/qtest/Makefile.include
78
+++ b/block/file-posix.c
25
+++ b/tests/qtest/Makefile.include
79
@@ -XXX,XX +XXX,XX @@ static int handle_aiocb_discard(void *opaque)
26
@@ -XXX,XX +XXX,XX @@ libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/qtest/libqos/u
80
return ret;
27
# qos devices:
81
}
28
libqos-obj-y = $(libqgraph-obj-y)
82
29
libqos-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
83
+/*
30
+libqos-obj-y += tests/qtest/libqos/qos_external.o
84
+ * Help alignment probing by allocating the first block.
31
libqos-obj-y += tests/qtest/libqos/e1000e.o
85
+ *
32
libqos-obj-y += tests/qtest/libqos/i2c.o
86
+ * When reading with direct I/O from unallocated area on Gluster backed by XFS,
33
libqos-obj-y += tests/qtest/libqos/i2c-imx.o
87
+ * reading succeeds regardless of request length. In this case we fallback to
34
diff --git a/tests/qtest/libqos/qos_external.c b/tests/qtest/libqos/qos_external.c
88
+ * safe alignment which is not optimal. Allocating the first block avoids this
89
+ * fallback.
90
+ *
91
+ * fd may be opened with O_DIRECT, but we don't know the buffer alignment or
92
+ * request alignment, so we use safe values.
93
+ *
94
+ * Returns: 0 on success, -errno on failure. Since this is an optimization,
95
+ * caller may ignore failures.
96
+ */
97
+static int allocate_first_block(int fd, size_t max_size)
98
+{
99
+ size_t write_size = (max_size < MAX_BLOCKSIZE)
100
+ ? BDRV_SECTOR_SIZE
101
+ : MAX_BLOCKSIZE;
102
+ size_t max_align = MAX(MAX_BLOCKSIZE, getpagesize());
103
+ void *buf;
104
+ ssize_t n;
105
+ int ret;
106
+
107
+ buf = qemu_memalign(max_align, write_size);
108
+ memset(buf, 0, write_size);
109
+
110
+ do {
111
+ n = pwrite(fd, buf, write_size, 0);
112
+ } while (n == -1 && errno == EINTR);
113
+
114
+ ret = (n == -1) ? -errno : 0;
115
+
116
+ qemu_vfree(buf);
117
+ return ret;
118
+}
119
+
120
static int handle_aiocb_truncate(void *opaque)
121
{
122
RawPosixAIOData *aiocb = opaque;
123
@@ -XXX,XX +XXX,XX @@ static int handle_aiocb_truncate(void *opaque)
124
/* posix_fallocate() doesn't set errno. */
125
error_setg_errno(errp, -result,
126
"Could not preallocate new data");
127
+ } else if (current_length == 0) {
128
+ /*
129
+ * posix_fallocate() uses fallocate() if the filesystem
130
+ * supports it, or fallback to manually writing zeroes. If
131
+ * fallocate() was used, unaligned reads from the fallocated
132
+ * area in raw_probe_alignment() will succeed, hence we need to
133
+ * allocate the first block.
134
+ *
135
+ * Optimize future alignment probing; ignore failures.
136
+ */
137
+ allocate_first_block(fd, offset);
138
}
139
} else {
140
result = 0;
141
@@ -XXX,XX +XXX,XX @@ static int handle_aiocb_truncate(void *opaque)
142
if (ftruncate(fd, offset) != 0) {
143
result = -errno;
144
error_setg_errno(errp, -result, "Could not resize file");
145
+ } else if (current_length == 0 && offset > current_length) {
146
+ /* Optimize future alignment probing; ignore failures. */
147
+ allocate_first_block(fd, offset);
148
}
149
return result;
150
default:
151
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
152
index XXXXXXX..XXXXXXX 100644
153
--- a/tests/qemu-iotests/059.out
154
+++ b/tests/qemu-iotests/059.out
155
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824000 subformat=twoGbMax
156
image: TEST_DIR/t.vmdk
157
file format: vmdk
158
virtual size: 0.977 TiB (1073741824000 bytes)
159
-disk size: 16 KiB
160
+disk size: 1.97 MiB
161
Format specific information:
162
cid: XXXXXXXX
163
parent cid: XXXXXXXX
164
diff --git a/tests/qemu-iotests/150.out b/tests/qemu-iotests/150.out.qcow2
165
similarity index 100%
166
rename from tests/qemu-iotests/150.out
167
rename to tests/qemu-iotests/150.out.qcow2
168
diff --git a/tests/qemu-iotests/150.out.raw b/tests/qemu-iotests/150.out.raw
169
new file mode 100644
35
new file mode 100644
170
index XXXXXXX..XXXXXXX
36
index XXXXXXX..XXXXXXX
171
--- /dev/null
37
--- /dev/null
172
+++ b/tests/qemu-iotests/150.out.raw
38
+++ b/tests/qtest/libqos/qos_external.c
173
@@ -XXX,XX +XXX,XX @@
39
@@ -XXX,XX +XXX,XX @@
174
+QA output created by 150
40
+/*
175
+
41
+ * libqos driver framework
176
+=== Mapping sparse conversion ===
42
+ *
177
+
43
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
178
+Offset Length File
44
+ *
179
+0 0x1000 TEST_DIR/t.IMGFMT
45
+ * This library is free software; you can redistribute it and/or
180
+
46
+ * modify it under the terms of the GNU Lesser General Public
181
+=== Mapping non-sparse conversion ===
47
+ * License version 2 as published by the Free Software Foundation.
182
+
48
+ *
183
+Offset Length File
49
+ * This library is distributed in the hope that it will be useful,
184
+0 0x100000 TEST_DIR/t.IMGFMT
50
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
185
+*** done
51
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
186
diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175
52
+ * Lesser General Public License for more details.
187
index XXXXXXX..XXXXXXX 100755
53
+ *
188
--- a/tests/qemu-iotests/175
54
+ * You should have received a copy of the GNU Lesser General Public
189
+++ b/tests/qemu-iotests/175
55
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
190
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
56
+ */
191
# the file size. This function hides the resulting difference in the
57
+
192
# stat -c '%b' output.
58
+#include "qemu/osdep.h"
193
# Parameter 1: Number of blocks an empty file occupies
59
+#include <getopt.h>
194
-# Parameter 2: Image size in bytes
60
+#include "libqtest.h"
195
+# Parameter 2: Minimal number of blocks in an image
61
+#include "qapi/qmp/qdict.h"
196
+# Parameter 3: Image size in bytes
62
+#include "qapi/qmp/qbool.h"
197
_filter_blocks()
63
+#include "qapi/qmp/qstring.h"
64
+#include "qemu/module.h"
65
+#include "qapi/qmp/qlist.h"
66
+#include "libqos/malloc.h"
67
+#include "libqos/qgraph.h"
68
+#include "libqos/qgraph_internal.h"
69
+#include "libqos/qos_external.h"
70
+
71
+
72
+
73
+void apply_to_node(const char *name, bool is_machine, bool is_abstract)
74
+{
75
+ char *machine_name = NULL;
76
+ if (is_machine) {
77
+ const char *arch = qtest_get_arch();
78
+ machine_name = g_strconcat(arch, "/", name, NULL);
79
+ name = machine_name;
80
+ }
81
+ qos_graph_node_set_availability(name, true);
82
+ if (is_abstract) {
83
+ qos_delete_cmd_line(name);
84
+ }
85
+ g_free(machine_name);
86
+}
87
+
88
+/**
89
+ * apply_to_qlist(): using QMP queries QEMU for a list of
90
+ * machines and devices available, and sets the respective node
91
+ * as true. If a node is found, also all its produced and contained
92
+ * child are marked available.
93
+ *
94
+ * See qos_graph_node_set_availability() for more info
95
+ */
96
+void apply_to_qlist(QList *list, bool is_machine)
97
+{
98
+ const QListEntry *p;
99
+ const char *name;
100
+ bool abstract;
101
+ QDict *minfo;
102
+ QObject *qobj;
103
+ QString *qstr;
104
+ QBool *qbool;
105
+
106
+ for (p = qlist_first(list); p; p = qlist_next(p)) {
107
+ minfo = qobject_to(QDict, qlist_entry_obj(p));
108
+ qobj = qdict_get(minfo, "name");
109
+ qstr = qobject_to(QString, qobj);
110
+ name = qstring_get_str(qstr);
111
+
112
+ qobj = qdict_get(minfo, "abstract");
113
+ if (qobj) {
114
+ qbool = qobject_to(QBool, qobj);
115
+ abstract = qbool_get_bool(qbool);
116
+ } else {
117
+ abstract = false;
118
+ }
119
+
120
+ apply_to_node(name, is_machine, abstract);
121
+ qobj = qdict_get(minfo, "alias");
122
+ if (qobj) {
123
+ qstr = qobject_to(QString, qobj);
124
+ name = qstring_get_str(qstr);
125
+ apply_to_node(name, is_machine, abstract);
126
+ }
127
+ }
128
+}
129
+
130
+QGuestAllocator *get_machine_allocator(QOSGraphObject *obj)
131
+{
132
+ return obj->get_driver(obj, "memory");
133
+}
134
+
135
+/**
136
+ * allocate_objects(): given an array of nodes @arg,
137
+ * walks the path invoking all constructors and
138
+ * passing the corresponding parameter in order to
139
+ * continue the objects allocation.
140
+ * Once the test is reached, return the object it consumes.
141
+ *
142
+ * Since the machine and QEDGE_CONSUMED_BY nodes allocate
143
+ * memory in the constructor, g_test_queue_destroy is used so
144
+ * that after execution they can be safely free'd. (The test's
145
+ * ->before callback is also welcome to use g_test_queue_destroy).
146
+ *
147
+ * Note: as specified in walk_path() too, @arg is an array of
148
+ * char *, where arg[0] is a pointer to the command line
149
+ * string that will be used to properly start QEMU when executing
150
+ * the test, and the remaining elements represent the actual objects
151
+ * that will be allocated.
152
+ */
153
+void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc)
154
+{
155
+ int current = 0;
156
+ QGuestAllocator *alloc;
157
+ QOSGraphObject *parent = NULL;
158
+ QOSGraphEdge *edge;
159
+ QOSGraphNode *node;
160
+ void *edge_arg;
161
+ void *obj;
162
+
163
+ node = qos_graph_get_node(path[current]);
164
+ g_assert(node->type == QNODE_MACHINE);
165
+
166
+ obj = qos_machine_new(node, qts);
167
+ qos_object_queue_destroy(obj);
168
+
169
+ alloc = get_machine_allocator(obj);
170
+ if (p_alloc) {
171
+ *p_alloc = alloc;
172
+ }
173
+
174
+ for (;;) {
175
+ if (node->type != QNODE_INTERFACE) {
176
+ qos_object_start_hw(obj);
177
+ parent = obj;
178
+ }
179
+
180
+ /* follow edge and get object for next node constructor */
181
+ current++;
182
+ edge = qos_graph_get_edge(path[current - 1], path[current]);
183
+ node = qos_graph_get_node(path[current]);
184
+
185
+ if (node->type == QNODE_TEST) {
186
+ g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY);
187
+ return obj;
188
+ }
189
+
190
+ switch (qos_graph_edge_get_type(edge)) {
191
+ case QEDGE_PRODUCES:
192
+ obj = parent->get_driver(parent, path[current]);
193
+ break;
194
+
195
+ case QEDGE_CONSUMED_BY:
196
+ edge_arg = qos_graph_edge_get_arg(edge);
197
+ obj = qos_driver_new(node, obj, alloc, edge_arg);
198
+ qos_object_queue_destroy(obj);
199
+ break;
200
+
201
+ case QEDGE_CONTAINS:
202
+ obj = parent->get_device(parent, path[current]);
203
+ break;
204
+ }
205
+ }
206
+}
207
+
208
diff --git a/tests/qtest/libqos/qos_external.h b/tests/qtest/libqos/qos_external.h
209
new file mode 100644
210
index XXXXXXX..XXXXXXX
211
--- /dev/null
212
+++ b/tests/qtest/libqos/qos_external.h
213
@@ -XXX,XX +XXX,XX @@
214
+/*
215
+ * libqos driver framework
216
+ *
217
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
218
+ *
219
+ * This library is free software; you can redistribute it and/or
220
+ * modify it under the terms of the GNU Lesser General Public
221
+ * License version 2 as published by the Free Software Foundation.
222
+ *
223
+ * This library is distributed in the hope that it will be useful,
224
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
225
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
226
+ * Lesser General Public License for more details.
227
+ *
228
+ * You should have received a copy of the GNU Lesser General Public
229
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
230
+ */
231
+
232
+#ifndef QOS_EXTERNAL_H
233
+#define QOS_EXTERNAL_H
234
+#include "libqos/qgraph.h"
235
+
236
+void apply_to_node(const char *name, bool is_machine, bool is_abstract);
237
+void apply_to_qlist(QList *list, bool is_machine);
238
+QGuestAllocator *get_machine_allocator(QOSGraphObject *obj);
239
+void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc);
240
+
241
+#endif
242
diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c
243
index XXXXXXX..XXXXXXX 100644
244
--- a/tests/qtest/qos-test.c
245
+++ b/tests/qtest/qos-test.c
246
@@ -XXX,XX +XXX,XX @@
247
#include "libqos/malloc.h"
248
#include "libqos/qgraph.h"
249
#include "libqos/qgraph_internal.h"
250
+#include "libqos/qos_external.h"
251
252
static char *old_path;
253
254
-static void apply_to_node(const char *name, bool is_machine, bool is_abstract)
255
-{
256
- char *machine_name = NULL;
257
- if (is_machine) {
258
- const char *arch = qtest_get_arch();
259
- machine_name = g_strconcat(arch, "/", name, NULL);
260
- name = machine_name;
261
- }
262
- qos_graph_node_set_availability(name, true);
263
- if (is_abstract) {
264
- qos_delete_cmd_line(name);
265
- }
266
- g_free(machine_name);
267
-}
268
269
-/**
270
- * apply_to_qlist(): using QMP queries QEMU for a list of
271
- * machines and devices available, and sets the respective node
272
- * as true. If a node is found, also all its produced and contained
273
- * child are marked available.
274
- *
275
- * See qos_graph_node_set_availability() for more info
276
- */
277
-static void apply_to_qlist(QList *list, bool is_machine)
278
-{
279
- const QListEntry *p;
280
- const char *name;
281
- bool abstract;
282
- QDict *minfo;
283
- QObject *qobj;
284
- QString *qstr;
285
- QBool *qbool;
286
-
287
- for (p = qlist_first(list); p; p = qlist_next(p)) {
288
- minfo = qobject_to(QDict, qlist_entry_obj(p));
289
- qobj = qdict_get(minfo, "name");
290
- qstr = qobject_to(QString, qobj);
291
- name = qstring_get_str(qstr);
292
-
293
- qobj = qdict_get(minfo, "abstract");
294
- if (qobj) {
295
- qbool = qobject_to(QBool, qobj);
296
- abstract = qbool_get_bool(qbool);
297
- } else {
298
- abstract = false;
299
- }
300
-
301
- apply_to_node(name, is_machine, abstract);
302
- qobj = qdict_get(minfo, "alias");
303
- if (qobj) {
304
- qstr = qobject_to(QString, qobj);
305
- name = qstring_get_str(qstr);
306
- apply_to_node(name, is_machine, abstract);
307
- }
308
- }
309
-}
310
311
/**
312
* qos_set_machines_devices_available(): sets availability of qgraph
313
@@ -XXX,XX +XXX,XX @@ static void qos_set_machines_devices_available(void)
314
qobject_unref(response);
315
}
316
317
-static QGuestAllocator *get_machine_allocator(QOSGraphObject *obj)
318
-{
319
- return obj->get_driver(obj, "memory");
320
-}
321
322
static void restart_qemu_or_continue(char *path)
198
{
323
{
199
extra_blocks=$1
324
@@ -XXX,XX +XXX,XX @@ void qos_invalidate_command_line(void)
200
- img_size=$2
325
old_path = NULL;
201
+ min_blocks=$2
202
+ img_size=$3
203
204
- sed -e "s/blocks=$extra_blocks\\(\$\\|[^0-9]\\)/nothing allocated/" \
205
- -e "s/blocks=$((extra_blocks + img_size / 512))\\(\$\\|[^0-9]\\)/everything allocated/"
206
+ sed -e "s/blocks=$min_blocks\\(\$\\|[^0-9]\\)/min allocation/" \
207
+ -e "s/blocks=$((extra_blocks + img_size / 512))\\(\$\\|[^0-9]\\)/max allocation/"
208
}
326
}
209
327
210
# get standard environment, filters and checks
328
-/**
211
@@ -XXX,XX +XXX,XX @@ size=$((1 * 1024 * 1024))
329
- * allocate_objects(): given an array of nodes @arg,
212
touch "$TEST_DIR/empty"
330
- * walks the path invoking all constructors and
213
extra_blocks=$(stat -c '%b' "$TEST_DIR/empty")
331
- * passing the corresponding parameter in order to
214
332
- * continue the objects allocation.
215
+# We always write the first byte; check how many blocks this filesystem
333
- * Once the test is reached, return the object it consumes.
216
+# allocates to match empty image alloation.
334
- *
217
+printf "\0" > "$TEST_DIR/empty"
335
- * Since the machine and QEDGE_CONSUMED_BY nodes allocate
218
+min_blocks=$(stat -c '%b' "$TEST_DIR/empty")
336
- * memory in the constructor, g_test_queue_destroy is used so
219
+
337
- * that after execution they can be safely free'd. (The test's
220
echo
338
- * ->before callback is also welcome to use g_test_queue_destroy).
221
echo "== creating image with default preallocation =="
339
- *
222
_make_test_img $size | _filter_imgfmt
340
- * Note: as specified in walk_path() too, @arg is an array of
223
-stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $size
341
- * char *, where arg[0] is a pointer to the command line
224
+stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $size
342
- * string that will be used to properly start QEMU when executing
225
343
- * the test, and the remaining elements represent the actual objects
226
for mode in off full falloc; do
344
- * that will be allocated.
227
echo
345
- */
228
echo "== creating image with preallocation $mode =="
346
-static void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc)
229
IMGOPTS=preallocation=$mode _make_test_img $size | _filter_imgfmt
347
-{
230
- stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $size
348
- int current = 0;
231
+ stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $size
349
- QGuestAllocator *alloc;
232
done
350
- QOSGraphObject *parent = NULL;
233
351
- QOSGraphEdge *edge;
234
# success, all done
352
- QOSGraphNode *node;
235
diff --git a/tests/qemu-iotests/175.out b/tests/qemu-iotests/175.out
353
- void *edge_arg;
236
index XXXXXXX..XXXXXXX 100644
354
- void *obj;
237
--- a/tests/qemu-iotests/175.out
355
-
238
+++ b/tests/qemu-iotests/175.out
356
- node = qos_graph_get_node(path[current]);
239
@@ -XXX,XX +XXX,XX @@ QA output created by 175
357
- g_assert(node->type == QNODE_MACHINE);
240
358
-
241
== creating image with default preallocation ==
359
- obj = qos_machine_new(node, qts);
242
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
360
- qos_object_queue_destroy(obj);
243
-size=1048576, nothing allocated
361
-
244
+size=1048576, min allocation
362
- alloc = get_machine_allocator(obj);
245
363
- if (p_alloc) {
246
== creating image with preallocation off ==
364
- *p_alloc = alloc;
247
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=off
365
- }
248
-size=1048576, nothing allocated
366
-
249
+size=1048576, min allocation
367
- for (;;) {
250
368
- if (node->type != QNODE_INTERFACE) {
251
== creating image with preallocation full ==
369
- qos_object_start_hw(obj);
252
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=full
370
- parent = obj;
253
-size=1048576, everything allocated
371
- }
254
+size=1048576, max allocation
372
-
255
373
- /* follow edge and get object for next node constructor */
256
== creating image with preallocation falloc ==
374
- current++;
257
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=falloc
375
- edge = qos_graph_get_edge(path[current - 1], path[current]);
258
-size=1048576, everything allocated
376
- node = qos_graph_get_node(path[current]);
259
+size=1048576, max allocation
377
-
260
*** done
378
- if (node->type == QNODE_TEST) {
261
diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2
379
- g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY);
262
index XXXXXXX..XXXXXXX 100644
380
- return obj;
263
--- a/tests/qemu-iotests/178.out.qcow2
381
- }
264
+++ b/tests/qemu-iotests/178.out.qcow2
382
-
265
@@ -XXX,XX +XXX,XX @@ converted image file size in bytes: 196608
383
- switch (qos_graph_edge_get_type(edge)) {
266
== raw input image with data (human) ==
384
- case QEDGE_PRODUCES:
267
385
- obj = parent->get_driver(parent, path[current]);
268
Formatting 'TEST_DIR/t.qcow2', fmt=IMGFMT size=1073741824
386
- break;
269
-required size: 393216
387
-
270
+required size: 458752
388
- case QEDGE_CONSUMED_BY:
271
fully allocated size: 1074135040
389
- edge_arg = qos_graph_edge_get_arg(edge);
272
wrote 512/512 bytes at offset 512
390
- obj = qos_driver_new(node, obj, alloc, edge_arg);
273
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
391
- qos_object_queue_destroy(obj);
274
@@ -XXX,XX +XXX,XX @@ converted image file size in bytes: 196608
392
- break;
275
393
-
276
Formatting 'TEST_DIR/t.qcow2', fmt=IMGFMT size=1073741824
394
- case QEDGE_CONTAINS:
277
{
395
- obj = parent->get_device(parent, path[current]);
278
- "required": 393216,
396
- break;
279
+ "required": 458752,
397
- }
280
"fully-allocated": 1074135040
398
- }
281
}
399
-}
282
wrote 512/512 bytes at offset 512
400
283
diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out
401
/* The argument to run_one_test, which is the test function that is registered
284
index XXXXXXX..XXXXXXX 100644
402
* with GTest, is a vector of strings. The first item is the initial command
285
--- a/tests/qemu-iotests/221.out
286
+++ b/tests/qemu-iotests/221.out
287
@@ -XXX,XX +XXX,XX @@ QA output created by 221
288
=== Check mapping of unaligned raw image ===
289
290
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65537
291
-[{ "start": 0, "length": 66048, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
292
-[{ "start": 0, "length": 66048, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
293
+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
294
+{ "start": 4096, "length": 61952, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
295
+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
296
+{ "start": 4096, "length": 61952, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
297
wrote 1/1 bytes at offset 65536
298
1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
299
-[{ "start": 0, "length": 65536, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
300
+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
301
+{ "start": 4096, "length": 61440, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
302
{ "start": 65536, "length": 1, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
303
{ "start": 65537, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
304
-[{ "start": 0, "length": 65536, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
305
+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
306
+{ "start": 4096, "length": 61440, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
307
{ "start": 65536, "length": 1, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
308
{ "start": 65537, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
309
*** done
310
diff --git a/tests/qemu-iotests/253.out b/tests/qemu-iotests/253.out
311
index XXXXXXX..XXXXXXX 100644
312
--- a/tests/qemu-iotests/253.out
313
+++ b/tests/qemu-iotests/253.out
314
@@ -XXX,XX +XXX,XX @@ QA output created by 253
315
=== Check mapping of unaligned raw image ===
316
317
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048575
318
-[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
319
-[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
320
+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
321
+{ "start": 4096, "length": 1044480, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
322
+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
323
+{ "start": 4096, "length": 1044480, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
324
wrote 65535/65535 bytes at offset 983040
325
63.999 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
326
-[{ "start": 0, "length": 983040, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
327
+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
328
+{ "start": 4096, "length": 978944, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
329
{ "start": 983040, "length": 65536, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
330
-[{ "start": 0, "length": 983040, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
331
+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
332
+{ "start": 4096, "length": 978944, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
333
{ "start": 983040, "length": 65536, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
334
*** done
335
--
403
--
336
2.21.0
404
2.24.1
337
405
338
diff view generated by jsdifflib
New patch
1
From: Alexander Bulekov <alxndr@bu.edu>
1
2
3
tests/fuzz/fuzz.c serves as the entry point for the virtual-device
4
fuzzer. Namely, libfuzzer invokes the LLVMFuzzerInitialize and
5
LLVMFuzzerTestOneInput functions, both of which are defined in this
6
file. This change adds a "FuzzTarget" struct, along with the
7
fuzz_add_target function, which should be used to define new fuzz
8
targets.
9
10
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
13
Message-id: 20200220041118.23264-13-alxndr@bu.edu
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
---
16
MAINTAINERS | 8 ++
17
tests/qtest/fuzz/Makefile.include | 6 +
18
tests/qtest/fuzz/fuzz.c | 179 ++++++++++++++++++++++++++++++
19
tests/qtest/fuzz/fuzz.h | 95 ++++++++++++++++
20
4 files changed, 288 insertions(+)
21
create mode 100644 tests/qtest/fuzz/Makefile.include
22
create mode 100644 tests/qtest/fuzz/fuzz.c
23
create mode 100644 tests/qtest/fuzz/fuzz.h
24
25
diff --git a/MAINTAINERS b/MAINTAINERS
26
index XXXXXXX..XXXXXXX 100644
27
--- a/MAINTAINERS
28
+++ b/MAINTAINERS
29
@@ -XXX,XX +XXX,XX @@ F: qtest.c
30
F: accel/qtest.c
31
F: tests/qtest/
32
33
+Device Fuzzing
34
+M: Alexander Bulekov <alxndr@bu.edu>
35
+R: Paolo Bonzini <pbonzini@redhat.com>
36
+R: Bandan Das <bsd@redhat.com>
37
+R: Stefan Hajnoczi <stefanha@redhat.com>
38
+S: Maintained
39
+F: tests/qtest/fuzz/
40
+
41
Register API
42
M: Alistair Francis <alistair@alistair23.me>
43
S: Maintained
44
diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
45
new file mode 100644
46
index XXXXXXX..XXXXXXX
47
--- /dev/null
48
+++ b/tests/qtest/fuzz/Makefile.include
49
@@ -XXX,XX +XXX,XX @@
50
+QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF)
51
+
52
+fuzz-obj-y += tests/qtest/libqtest.o
53
+fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
54
+
55
+FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
56
diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c
57
new file mode 100644
58
index XXXXXXX..XXXXXXX
59
--- /dev/null
60
+++ b/tests/qtest/fuzz/fuzz.c
61
@@ -XXX,XX +XXX,XX @@
62
+/*
63
+ * fuzzing driver
64
+ *
65
+ * Copyright Red Hat Inc., 2019
66
+ *
67
+ * Authors:
68
+ * Alexander Bulekov <alxndr@bu.edu>
69
+ *
70
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
71
+ * See the COPYING file in the top-level directory.
72
+ *
73
+ */
74
+
75
+#include "qemu/osdep.h"
76
+
77
+#include <wordexp.h>
78
+
79
+#include "sysemu/qtest.h"
80
+#include "sysemu/runstate.h"
81
+#include "sysemu/sysemu.h"
82
+#include "qemu/main-loop.h"
83
+#include "tests/qtest/libqtest.h"
84
+#include "tests/qtest/libqos/qgraph.h"
85
+#include "fuzz.h"
86
+
87
+#define MAX_EVENT_LOOPS 10
88
+
89
+typedef struct FuzzTargetState {
90
+ FuzzTarget *target;
91
+ QSLIST_ENTRY(FuzzTargetState) target_list;
92
+} FuzzTargetState;
93
+
94
+typedef QSLIST_HEAD(, FuzzTargetState) FuzzTargetList;
95
+
96
+static const char *fuzz_arch = TARGET_NAME;
97
+
98
+static FuzzTargetList *fuzz_target_list;
99
+static FuzzTarget *fuzz_target;
100
+static QTestState *fuzz_qts;
101
+
102
+
103
+
104
+void flush_events(QTestState *s)
105
+{
106
+ int i = MAX_EVENT_LOOPS;
107
+ while (g_main_context_pending(NULL) && i-- > 0) {
108
+ main_loop_wait(false);
109
+ }
110
+}
111
+
112
+static QTestState *qtest_setup(void)
113
+{
114
+ qtest_server_set_send_handler(&qtest_client_inproc_recv, &fuzz_qts);
115
+ return qtest_inproc_init(&fuzz_qts, false, fuzz_arch,
116
+ &qtest_server_inproc_recv);
117
+}
118
+
119
+void fuzz_add_target(const FuzzTarget *target)
120
+{
121
+ FuzzTargetState *tmp;
122
+ FuzzTargetState *target_state;
123
+ if (!fuzz_target_list) {
124
+ fuzz_target_list = g_new0(FuzzTargetList, 1);
125
+ }
126
+
127
+ QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
128
+ if (g_strcmp0(tmp->target->name, target->name) == 0) {
129
+ fprintf(stderr, "Error: Fuzz target name %s already in use\n",
130
+ target->name);
131
+ abort();
132
+ }
133
+ }
134
+ target_state = g_new0(FuzzTargetState, 1);
135
+ target_state->target = g_new0(FuzzTarget, 1);
136
+ *(target_state->target) = *target;
137
+ QSLIST_INSERT_HEAD(fuzz_target_list, target_state, target_list);
138
+}
139
+
140
+
141
+
142
+static void usage(char *path)
143
+{
144
+ printf("Usage: %s --fuzz-target=FUZZ_TARGET [LIBFUZZER ARGUMENTS]\n", path);
145
+ printf("where FUZZ_TARGET is one of:\n");
146
+ FuzzTargetState *tmp;
147
+ if (!fuzz_target_list) {
148
+ fprintf(stderr, "Fuzz target list not initialized\n");
149
+ abort();
150
+ }
151
+ QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
152
+ printf(" * %s : %s\n", tmp->target->name,
153
+ tmp->target->description);
154
+ }
155
+ exit(0);
156
+}
157
+
158
+static FuzzTarget *fuzz_get_target(char* name)
159
+{
160
+ FuzzTargetState *tmp;
161
+ if (!fuzz_target_list) {
162
+ fprintf(stderr, "Fuzz target list not initialized\n");
163
+ abort();
164
+ }
165
+
166
+ QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
167
+ if (strcmp(tmp->target->name, name) == 0) {
168
+ return tmp->target;
169
+ }
170
+ }
171
+ return NULL;
172
+}
173
+
174
+
175
+/* Executed for each fuzzing-input */
176
+int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size)
177
+{
178
+ /*
179
+ * Do the pre-fuzz-initialization before the first fuzzing iteration,
180
+ * instead of before the actual fuzz loop. This is needed since libfuzzer
181
+ * may fork off additional workers, prior to the fuzzing loop, and if
182
+ * pre_fuzz() sets up e.g. shared memory, this should be done for the
183
+ * individual worker processes
184
+ */
185
+ static int pre_fuzz_done;
186
+ if (!pre_fuzz_done && fuzz_target->pre_fuzz) {
187
+ fuzz_target->pre_fuzz(fuzz_qts);
188
+ pre_fuzz_done = true;
189
+ }
190
+
191
+ fuzz_target->fuzz(fuzz_qts, Data, Size);
192
+ return 0;
193
+}
194
+
195
+/* Executed once, prior to fuzzing */
196
+int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp)
197
+{
198
+
199
+ char *target_name;
200
+
201
+ /* Initialize qgraph and modules */
202
+ qos_graph_init();
203
+ module_call_init(MODULE_INIT_FUZZ_TARGET);
204
+ module_call_init(MODULE_INIT_QOM);
205
+ module_call_init(MODULE_INIT_LIBQOS);
206
+
207
+ if (*argc <= 1) {
208
+ usage(**argv);
209
+ }
210
+
211
+ /* Identify the fuzz target */
212
+ target_name = (*argv)[1];
213
+ if (!strstr(target_name, "--fuzz-target=")) {
214
+ usage(**argv);
215
+ }
216
+
217
+ target_name += strlen("--fuzz-target=");
218
+
219
+ fuzz_target = fuzz_get_target(target_name);
220
+ if (!fuzz_target) {
221
+ usage(**argv);
222
+ }
223
+
224
+ fuzz_qts = qtest_setup();
225
+
226
+ if (fuzz_target->pre_vm_init) {
227
+ fuzz_target->pre_vm_init();
228
+ }
229
+
230
+ /* Run QEMU's softmmu main with the fuzz-target dependent arguments */
231
+ const char *init_cmdline = fuzz_target->get_init_cmdline(fuzz_target);
232
+
233
+ /* Split the runcmd into an argv and argc */
234
+ wordexp_t result;
235
+ wordexp(init_cmdline, &result, 0);
236
+
237
+ qemu_init(result.we_wordc, result.we_wordv, NULL);
238
+
239
+ return 0;
240
+}
241
diff --git a/tests/qtest/fuzz/fuzz.h b/tests/qtest/fuzz/fuzz.h
242
new file mode 100644
243
index XXXXXXX..XXXXXXX
244
--- /dev/null
245
+++ b/tests/qtest/fuzz/fuzz.h
246
@@ -XXX,XX +XXX,XX @@
247
+/*
248
+ * fuzzing driver
249
+ *
250
+ * Copyright Red Hat Inc., 2019
251
+ *
252
+ * Authors:
253
+ * Alexander Bulekov <alxndr@bu.edu>
254
+ *
255
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
256
+ * See the COPYING file in the top-level directory.
257
+ *
258
+ */
259
+
260
+#ifndef FUZZER_H_
261
+#define FUZZER_H_
262
+
263
+#include "qemu/osdep.h"
264
+#include "qemu/units.h"
265
+#include "qapi/error.h"
266
+
267
+#include "tests/qtest/libqtest.h"
268
+
269
+/**
270
+ * A libfuzzer fuzzing target
271
+ *
272
+ * The QEMU fuzzing binary is built with all available targets, each
273
+ * with a unique @name that can be specified on the command-line to
274
+ * select which target should run.
275
+ *
276
+ * A target must implement ->fuzz() to process a random input. If QEMU
277
+ * crashes in ->fuzz() then libfuzzer will record a failure.
278
+ *
279
+ * Fuzzing targets are registered with fuzz_add_target():
280
+ *
281
+ * static const FuzzTarget fuzz_target = {
282
+ * .name = "my-device-fifo",
283
+ * .description = "Fuzz the FIFO buffer registers of my-device",
284
+ * ...
285
+ * };
286
+ *
287
+ * static void register_fuzz_target(void)
288
+ * {
289
+ * fuzz_add_target(&fuzz_target);
290
+ * }
291
+ * fuzz_target_init(register_fuzz_target);
292
+ */
293
+typedef struct FuzzTarget {
294
+ const char *name; /* target identifier (passed to --fuzz-target=)*/
295
+ const char *description; /* help text */
296
+
297
+
298
+ /*
299
+ * returns the arg-list that is passed to qemu/softmmu init()
300
+ * Cannot be NULL
301
+ */
302
+ const char* (*get_init_cmdline)(struct FuzzTarget *);
303
+
304
+ /*
305
+ * will run once, prior to running qemu/softmmu init.
306
+ * eg: set up shared-memory for communication with the child-process
307
+ * Can be NULL
308
+ */
309
+ void(*pre_vm_init)(void);
310
+
311
+ /*
312
+ * will run once, after QEMU has been initialized, prior to the fuzz-loop.
313
+ * eg: detect the memory map
314
+ * Can be NULL
315
+ */
316
+ void(*pre_fuzz)(QTestState *);
317
+
318
+ /*
319
+ * accepts and executes an input from libfuzzer. this is repeatedly
320
+ * executed during the fuzzing loop. Its should handle setup, input
321
+ * execution and cleanup.
322
+ * Cannot be NULL
323
+ */
324
+ void(*fuzz)(QTestState *, const unsigned char *, size_t);
325
+
326
+} FuzzTarget;
327
+
328
+void flush_events(QTestState *);
329
+void reboot(QTestState *);
330
+
331
+/*
332
+ * makes a copy of *target and adds it to the target-list.
333
+ * i.e. fine to set up target on the caller's stack
334
+ */
335
+void fuzz_add_target(const FuzzTarget *target);
336
+
337
+int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size);
338
+int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp);
339
+
340
+#endif
341
+
342
--
343
2.24.1
344
diff view generated by jsdifflib
New patch
1
From: Alexander Bulekov <alxndr@bu.edu>
1
2
3
Ram blocks were marked MADV_DONTFORK breaking fuzzing-tests which
4
execute each test-input in a forked process.
5
6
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
9
Message-id: 20200220041118.23264-14-alxndr@bu.edu
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
12
exec.c | 12 ++++++++++--
13
1 file changed, 10 insertions(+), 2 deletions(-)
14
15
diff --git a/exec.c b/exec.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/exec.c
18
+++ b/exec.c
19
@@ -XXX,XX +XXX,XX @@
20
#include "sysemu/kvm.h"
21
#include "sysemu/sysemu.h"
22
#include "sysemu/tcg.h"
23
+#include "sysemu/qtest.h"
24
#include "qemu/timer.h"
25
#include "qemu/config-file.h"
26
#include "qemu/error-report.h"
27
@@ -XXX,XX +XXX,XX @@ static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared)
28
if (new_block->host) {
29
qemu_ram_setup_dump(new_block->host, new_block->max_length);
30
qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_HUGEPAGE);
31
- /* MADV_DONTFORK is also needed by KVM in absence of synchronous MMU */
32
- qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_DONTFORK);
33
+ /*
34
+ * MADV_DONTFORK is also needed by KVM in absence of synchronous MMU
35
+ * Configure it unless the machine is a qtest server, in which case
36
+ * KVM is not used and it may be forked (eg for fuzzing purposes).
37
+ */
38
+ if (!qtest_enabled()) {
39
+ qemu_madvise(new_block->host, new_block->max_length,
40
+ QEMU_MADV_DONTFORK);
41
+ }
42
ram_block_notify_add(new_block->host, new_block->max_length);
43
}
44
}
45
--
46
2.24.1
47
diff view generated by jsdifflib
1
From: Stefan Hajnoczi <stefanha@redhat.com>
1
From: Alexander Bulekov <alxndr@bu.edu>
2
2
3
Fixes: a6b257a08e3d72219f03e461a52152672fec0612
3
The qtest-based fuzzer makes use of forking to reset-state between
4
("file-posix: Handle undetectable alignment")
4
tests. Keep the callback enabled, so the call_rcu thread gets created
5
within the child process.
6
7
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
8
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
9
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Message-id: 20200220041118.23264-15-alxndr@bu.edu
5
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
6
Message-id: 20190827101328.4062-1-stefanha@redhat.com
7
Reviewed-by: Thomas Huth <thuth@redhat.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
---
12
---
10
block/file-posix.c | 2 +-
13
softmmu/vl.c | 12 +++++++++++-
11
1 file changed, 1 insertion(+), 1 deletion(-)
14
1 file changed, 11 insertions(+), 1 deletion(-)
12
15
13
diff --git a/block/file-posix.c b/block/file-posix.c
16
diff --git a/softmmu/vl.c b/softmmu/vl.c
14
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
15
--- a/block/file-posix.c
18
--- a/softmmu/vl.c
16
+++ b/block/file-posix.c
19
+++ b/softmmu/vl.c
17
@@ -XXX,XX +XXX,XX @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp)
20
@@ -XXX,XX +XXX,XX @@ void qemu_init(int argc, char **argv, char **envp)
18
for (i = 0; i < ARRAY_SIZE(alignments); i++) {
21
set_memory_options(&ram_slots, &maxram_size, machine_class);
19
align = alignments[i];
22
20
if (raw_is_io_aligned(fd, buf + align, max_align)) {
23
os_daemonize();
21
- /* Fallback to request_aligment. */
24
- rcu_disable_atfork();
22
+ /* Fallback to request_alignment. */
25
+
23
s->buf_align = (align != 1) ? align : bs->bl.request_alignment;
26
+ /*
24
break;
27
+ * If QTest is enabled, keep the rcu_atfork enabled, since system processes
25
}
28
+ * may be forked testing purposes (e.g. fork-server based fuzzing) The fork
29
+ * should happen before a signle cpu instruction is executed, to prevent
30
+ * deadlocks. See commit 73c6e40, rcu: "completely disable pthread_atfork
31
+ * callbacks as soon as possible"
32
+ */
33
+ if (!qtest_enabled()) {
34
+ rcu_disable_atfork();
35
+ }
36
37
if (pid_file && !qemu_write_pidfile(pid_file, &err)) {
38
error_reportf_err(err, "cannot create PID file: ");
26
--
39
--
27
2.21.0
40
2.24.1
28
41
29
diff view generated by jsdifflib
1
From: Thomas Huth <thuth@redhat.com>
1
From: Alexander Bulekov <alxndr@bu.edu>
2
2
3
It is possible to enable only a subset of the block drivers with the
3
fork() is a simple way to ensure that state does not leak in between
4
"--block-drv-rw-whitelist" option of the "configure" script. All other
4
fuzzing runs. Unfortunately, the fuzzer mutation engine relies on
5
drivers are marked as unusable (or only included as read-only with the
5
bitmaps which contain coverage information for each fuzzing run, and
6
"--block-drv-ro-whitelist" option). If an iotest is now using such a
6
these bitmaps should be copied from the child to the parent(where the
7
disabled block driver, it is failing - which is bad, since at least the
7
mutation occurs). These bitmaps are created through compile-time
8
tests in the "auto" group should be able to deal with this situation.
8
instrumentation and they are not shared with fork()-ed processes, by
9
Thus let's introduce a "_require_drivers" function that can be used by
9
default. To address this, we create a shared memory region, adjust its
10
the shell tests to check for the availability of certain drivers first,
10
size and map it _over_ the counter region. Furthermore, libfuzzer
11
and marks the test as "not run" if one of the drivers is missing.
11
doesn't generally expose the globals that specify the location of the
12
counters/coverage bitmap. As a workaround, we rely on a custom linker
13
script which forces all of the bitmaps we care about to be placed in a
14
contiguous region, which is easy to locate and mmap over.
12
15
13
This patch mainly targets the test in the "auto" group which should
16
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
14
never fail in such a case, but also improves some of the other tests
17
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
15
along the way. Note that we also assume that the "qcow2" and "file"
18
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
16
drivers are always available - otherwise it does not make sense to
19
Message-id: 20200220041118.23264-16-alxndr@bu.edu
17
run "make check-block" at all (which only tests with qcow2 by default).
20
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
---
22
tests/qtest/fuzz/Makefile.include | 5 +++
23
tests/qtest/fuzz/fork_fuzz.c | 55 +++++++++++++++++++++++++++++++
24
tests/qtest/fuzz/fork_fuzz.h | 23 +++++++++++++
25
tests/qtest/fuzz/fork_fuzz.ld | 37 +++++++++++++++++++++
26
4 files changed, 120 insertions(+)
27
create mode 100644 tests/qtest/fuzz/fork_fuzz.c
28
create mode 100644 tests/qtest/fuzz/fork_fuzz.h
29
create mode 100644 tests/qtest/fuzz/fork_fuzz.ld
18
30
19
Signed-off-by: Thomas Huth <thuth@redhat.com>
31
diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
20
Message-id: 20190823133552.11680-1-thuth@redhat.com
21
Signed-off-by: Max Reitz <mreitz@redhat.com>
22
---
23
tests/qemu-iotests/071 | 1 +
24
tests/qemu-iotests/081 | 4 +---
25
tests/qemu-iotests/099 | 1 +
26
tests/qemu-iotests/120 | 1 +
27
tests/qemu-iotests/162 | 4 +---
28
tests/qemu-iotests/184 | 1 +
29
tests/qemu-iotests/186 | 1 +
30
tests/qemu-iotests/common.rc | 14 ++++++++++++++
31
8 files changed, 21 insertions(+), 6 deletions(-)
32
33
diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071
34
index XXXXXXX..XXXXXXX 100755
35
--- a/tests/qemu-iotests/071
36
+++ b/tests/qemu-iotests/071
37
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
38
39
_supported_fmt qcow2
40
_supported_proto file
41
+_require_drivers blkdebug blkverify
42
43
do_run_qemu()
44
{
45
diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081
46
index XXXXXXX..XXXXXXX 100755
47
--- a/tests/qemu-iotests/081
48
+++ b/tests/qemu-iotests/081
49
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
50
_supported_fmt raw
51
_supported_proto file
52
_supported_os Linux
53
+_require_drivers quorum
54
55
do_run_qemu()
56
{
57
@@ -XXX,XX +XXX,XX @@ run_qemu()
58
| _filter_qemu_io | _filter_generated_node_ids
59
}
60
61
-test_quorum=$($QEMU_IMG --help|grep quorum)
62
-[ "$test_quorum" = "" ] && _supported_fmt quorum
63
-
64
quorum="driver=raw,file.driver=quorum,file.vote-threshold=2"
65
quorum="$quorum,file.children.0.file.filename=$TEST_DIR/1.raw"
66
quorum="$quorum,file.children.1.file.filename=$TEST_DIR/2.raw"
67
diff --git a/tests/qemu-iotests/099 b/tests/qemu-iotests/099
68
index XXXXXXX..XXXXXXX 100755
69
--- a/tests/qemu-iotests/099
70
+++ b/tests/qemu-iotests/099
71
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
72
_supported_fmt qcow qcow2 qed vdi vhdx vmdk vpc
73
_supported_proto file
74
_supported_os Linux
75
+_require_drivers blkdebug blkverify
76
_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" \
77
"subformat=twoGbMaxExtentSparse"
78
79
diff --git a/tests/qemu-iotests/120 b/tests/qemu-iotests/120
80
index XXXXXXX..XXXXXXX 100755
81
--- a/tests/qemu-iotests/120
82
+++ b/tests/qemu-iotests/120
83
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
84
_supported_fmt generic
85
_supported_proto file
86
_unsupported_fmt luks
87
+_require_drivers raw
88
89
_make_test_img 64M
90
91
diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162
92
index XXXXXXX..XXXXXXX 100755
93
--- a/tests/qemu-iotests/162
94
+++ b/tests/qemu-iotests/162
95
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
96
. ./common.filter
97
98
_supported_fmt generic
99
-
100
-test_ssh=$($QEMU_IMG --help | grep '^Supported formats:.* ssh\( \|$\)')
101
-[ "$test_ssh" = "" ] && _notrun "ssh support required"
102
+_require_drivers ssh
103
104
echo
105
echo '=== NBD ==='
106
diff --git a/tests/qemu-iotests/184 b/tests/qemu-iotests/184
107
index XXXXXXX..XXXXXXX 100755
108
--- a/tests/qemu-iotests/184
109
+++ b/tests/qemu-iotests/184
110
@@ -XXX,XX +XXX,XX @@ trap "exit \$status" 0 1 2 3 15
111
. ./common.filter
112
113
_supported_os Linux
114
+_require_drivers throttle
115
116
do_run_qemu()
117
{
118
diff --git a/tests/qemu-iotests/186 b/tests/qemu-iotests/186
119
index XXXXXXX..XXXXXXX 100755
120
--- a/tests/qemu-iotests/186
121
+++ b/tests/qemu-iotests/186
122
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
123
124
_supported_fmt qcow2
125
_supported_proto file
126
+_require_drivers null-co
127
128
if [ "$QEMU_DEFAULT_MACHINE" != "pc" ]; then
129
_notrun "Requires a PC machine"
130
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
131
index XXXXXXX..XXXXXXX 100644
32
index XXXXXXX..XXXXXXX 100644
132
--- a/tests/qemu-iotests/common.rc
33
--- a/tests/qtest/fuzz/Makefile.include
133
+++ b/tests/qemu-iotests/common.rc
34
+++ b/tests/qtest/fuzz/Makefile.include
134
@@ -XXX,XX +XXX,XX @@ _require_command()
35
@@ -XXX,XX +XXX,XX @@ QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF)
135
[ -x "$c" ] || _notrun "$1 utility required, skipped this test"
36
136
}
37
fuzz-obj-y += tests/qtest/libqtest.o
137
38
fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
138
+# Check that a set of drivers has been whitelisted in the QEMU binary
39
+fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
139
+#
40
140
+_require_drivers()
41
FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
42
+
43
+# Linker Script to force coverage-counters into known regions which we can mark
44
+# shared
45
+FUZZ_LDFLAGS += -Xlinker -T$(SRC_PATH)/tests/qtest/fuzz/fork_fuzz.ld
46
diff --git a/tests/qtest/fuzz/fork_fuzz.c b/tests/qtest/fuzz/fork_fuzz.c
47
new file mode 100644
48
index XXXXXXX..XXXXXXX
49
--- /dev/null
50
+++ b/tests/qtest/fuzz/fork_fuzz.c
51
@@ -XXX,XX +XXX,XX @@
52
+/*
53
+ * Fork-based fuzzing helpers
54
+ *
55
+ * Copyright Red Hat Inc., 2019
56
+ *
57
+ * Authors:
58
+ * Alexander Bulekov <alxndr@bu.edu>
59
+ *
60
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
61
+ * See the COPYING file in the top-level directory.
62
+ *
63
+ */
64
+
65
+#include "qemu/osdep.h"
66
+#include "fork_fuzz.h"
67
+
68
+
69
+void counter_shm_init(void)
141
+{
70
+{
142
+ available=$($QEMU -drive format=help | \
71
+ char *shm_path = g_strdup_printf("/qemu-fuzz-cntrs.%d", getpid());
143
+ sed -e '/Supported formats:/!d' -e 's/Supported formats://')
72
+ int fd = shm_open(shm_path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
144
+ for driver
73
+ g_free(shm_path);
145
+ do
74
+
146
+ if ! echo "$available" | grep -q " $driver\( \|$\)"; then
75
+ if (fd == -1) {
147
+ _notrun "$driver not available"
76
+ perror("Error: ");
148
+ fi
77
+ exit(1);
149
+ done
78
+ }
79
+ if (ftruncate(fd, &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START) == -1) {
80
+ perror("Error: ");
81
+ exit(1);
82
+ }
83
+ /* Copy what's in the counter region to the shm.. */
84
+ void *rptr = mmap(NULL ,
85
+ &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START,
86
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
87
+ memcpy(rptr,
88
+ &__FUZZ_COUNTERS_START,
89
+ &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START);
90
+
91
+ munmap(rptr, &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START);
92
+
93
+ /* And map the shm over the counter region */
94
+ rptr = mmap(&__FUZZ_COUNTERS_START,
95
+ &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START,
96
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
97
+
98
+ close(fd);
99
+
100
+ if (!rptr) {
101
+ perror("Error: ");
102
+ exit(1);
103
+ }
150
+}
104
+}
151
+
105
+
152
# make sure this script returns success
106
+
153
true
107
diff --git a/tests/qtest/fuzz/fork_fuzz.h b/tests/qtest/fuzz/fork_fuzz.h
108
new file mode 100644
109
index XXXXXXX..XXXXXXX
110
--- /dev/null
111
+++ b/tests/qtest/fuzz/fork_fuzz.h
112
@@ -XXX,XX +XXX,XX @@
113
+/*
114
+ * Fork-based fuzzing helpers
115
+ *
116
+ * Copyright Red Hat Inc., 2019
117
+ *
118
+ * Authors:
119
+ * Alexander Bulekov <alxndr@bu.edu>
120
+ *
121
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
122
+ * See the COPYING file in the top-level directory.
123
+ *
124
+ */
125
+
126
+#ifndef FORK_FUZZ_H
127
+#define FORK_FUZZ_H
128
+
129
+extern uint8_t __FUZZ_COUNTERS_START;
130
+extern uint8_t __FUZZ_COUNTERS_END;
131
+
132
+void counter_shm_init(void);
133
+
134
+#endif
135
+
136
diff --git a/tests/qtest/fuzz/fork_fuzz.ld b/tests/qtest/fuzz/fork_fuzz.ld
137
new file mode 100644
138
index XXXXXXX..XXXXXXX
139
--- /dev/null
140
+++ b/tests/qtest/fuzz/fork_fuzz.ld
141
@@ -XXX,XX +XXX,XX @@
142
+/* We adjust linker script modification to place all of the stuff that needs to
143
+ * persist across fuzzing runs into a contiguous seciton of memory. Then, it is
144
+ * easy to re-map the counter-related memory as shared.
145
+*/
146
+
147
+SECTIONS
148
+{
149
+ .data.fuzz_start : ALIGN(4K)
150
+ {
151
+ __FUZZ_COUNTERS_START = .;
152
+ __start___sancov_cntrs = .;
153
+ *(_*sancov_cntrs);
154
+ __stop___sancov_cntrs = .;
155
+
156
+ /* Lowest stack counter */
157
+ *(__sancov_lowest_stack);
158
+ }
159
+ .data.fuzz_ordered :
160
+ {
161
+ /* Coverage counters. They're not necessary for fuzzing, but are useful
162
+ * for analyzing the fuzzing performance
163
+ */
164
+ __start___llvm_prf_cnts = .;
165
+ *(*llvm_prf_cnts);
166
+ __stop___llvm_prf_cnts = .;
167
+
168
+ /* Internal Libfuzzer TracePC object which contains the ValueProfileMap */
169
+ FuzzerTracePC*(.bss*);
170
+ }
171
+ .data.fuzz_end : ALIGN(4K)
172
+ {
173
+ __FUZZ_COUNTERS_END = .;
174
+ }
175
+}
176
+/* Dont overwrite the SECTIONS in the default linker script. Instead insert the
177
+ * above into the default script */
178
+INSERT AFTER .data;
154
--
179
--
155
2.21.0
180
2.24.1
156
181
157
diff view generated by jsdifflib
1
iotest 126 requires backing file support, which flat vmdks cannot offer.
1
From: Alexander Bulekov <alxndr@bu.edu>
2
Skip this test for such subformats.
3
2
4
Signed-off-by: Max Reitz <mreitz@redhat.com>
3
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
5
Message-id: 20190815153638.4600-8-mreitz@redhat.com
4
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
Reviewed-by: John Snow <jsnow@redhat.com>
5
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
Message-id: 20200220041118.23264-17-alxndr@bu.edu
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
---
8
---
9
tests/qemu-iotests/126 | 2 ++
9
tests/qtest/fuzz/Makefile.include | 2 +
10
1 file changed, 2 insertions(+)
10
tests/qtest/fuzz/qos_fuzz.c | 234 ++++++++++++++++++++++++++++++
11
tests/qtest/fuzz/qos_fuzz.h | 33 +++++
12
3 files changed, 269 insertions(+)
13
create mode 100644 tests/qtest/fuzz/qos_fuzz.c
14
create mode 100644 tests/qtest/fuzz/qos_fuzz.h
11
15
12
diff --git a/tests/qemu-iotests/126 b/tests/qemu-iotests/126
16
diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
13
index XXXXXXX..XXXXXXX 100755
17
index XXXXXXX..XXXXXXX 100644
14
--- a/tests/qemu-iotests/126
18
--- a/tests/qtest/fuzz/Makefile.include
15
+++ b/tests/qemu-iotests/126
19
+++ b/tests/qtest/fuzz/Makefile.include
16
@@ -XXX,XX +XXX,XX @@ status=1    # failure is the default!
20
@@ -XXX,XX +XXX,XX @@
17
21
QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF)
18
# Needs backing file support
22
19
_supported_fmt qcow qcow2 qed vmdk
23
fuzz-obj-y += tests/qtest/libqtest.o
20
+_unsupported_imgopts "subformat=monolithicFlat" \
24
+fuzz-obj-y += $(libqos-obj-y)
21
+ "subformat=twoGbMaxExtentFlat"
25
fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
22
# This is the default protocol (and we want to test the difference between
26
fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
23
# colons which separate a protocol prefix from the rest and colons which are
27
+fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
24
# just part of the filename, so we cannot test protocols which require a prefix)
28
29
FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
30
31
diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c
32
new file mode 100644
33
index XXXXXXX..XXXXXXX
34
--- /dev/null
35
+++ b/tests/qtest/fuzz/qos_fuzz.c
36
@@ -XXX,XX +XXX,XX @@
37
+/*
38
+ * QOS-assisted fuzzing helpers
39
+ *
40
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
41
+ *
42
+ * This library is free software; you can redistribute it and/or
43
+ * modify it under the terms of the GNU Lesser General Public
44
+ * License version 2 as published by the Free Software Foundation.
45
+ *
46
+ * This library is distributed in the hope that it will be useful,
47
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
48
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
49
+ * Lesser General Public License for more details.
50
+ *
51
+ * You should have received a copy of the GNU Lesser General Public
52
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
53
+ */
54
+
55
+#include "qemu/osdep.h"
56
+#include "qemu/units.h"
57
+#include "qapi/error.h"
58
+#include "qemu-common.h"
59
+#include "exec/memory.h"
60
+#include "exec/address-spaces.h"
61
+#include "sysemu/sysemu.h"
62
+#include "qemu/main-loop.h"
63
+
64
+#include "tests/qtest/libqtest.h"
65
+#include "tests/qtest/libqos/malloc.h"
66
+#include "tests/qtest/libqos/qgraph.h"
67
+#include "tests/qtest/libqos/qgraph_internal.h"
68
+#include "tests/qtest/libqos/qos_external.h"
69
+
70
+#include "fuzz.h"
71
+#include "qos_fuzz.h"
72
+
73
+#include "qapi/qapi-commands-machine.h"
74
+#include "qapi/qapi-commands-qom.h"
75
+#include "qapi/qmp/qlist.h"
76
+
77
+
78
+void *fuzz_qos_obj;
79
+QGuestAllocator *fuzz_qos_alloc;
80
+
81
+static const char *fuzz_target_name;
82
+static char **fuzz_path_vec;
83
+
84
+/*
85
+ * Replaced the qmp commands with direct qmp_marshal calls.
86
+ * Probably there is a better way to do this
87
+ */
88
+static void qos_set_machines_devices_available(void)
89
+{
90
+ QDict *req = qdict_new();
91
+ QObject *response;
92
+ QDict *args = qdict_new();
93
+ QList *lst;
94
+ Error *err = NULL;
95
+
96
+ qmp_marshal_query_machines(NULL, &response, &err);
97
+ assert(!err);
98
+ lst = qobject_to(QList, response);
99
+ apply_to_qlist(lst, true);
100
+
101
+ qobject_unref(response);
102
+
103
+
104
+ qdict_put_str(req, "execute", "qom-list-types");
105
+ qdict_put_str(args, "implements", "device");
106
+ qdict_put_bool(args, "abstract", true);
107
+ qdict_put_obj(req, "arguments", (QObject *) args);
108
+
109
+ qmp_marshal_qom_list_types(args, &response, &err);
110
+ assert(!err);
111
+ lst = qobject_to(QList, response);
112
+ apply_to_qlist(lst, false);
113
+ qobject_unref(response);
114
+ qobject_unref(req);
115
+}
116
+
117
+static char **current_path;
118
+
119
+void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc)
120
+{
121
+ return allocate_objects(qts, current_path + 1, p_alloc);
122
+}
123
+
124
+static const char *qos_build_main_args(void)
125
+{
126
+ char **path = fuzz_path_vec;
127
+ QOSGraphNode *test_node;
128
+ GString *cmd_line = g_string_new(path[0]);
129
+ void *test_arg;
130
+
131
+ if (!path) {
132
+ fprintf(stderr, "QOS Path not found\n");
133
+ abort();
134
+ }
135
+
136
+ /* Before test */
137
+ current_path = path;
138
+ test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]);
139
+ test_arg = test_node->u.test.arg;
140
+ if (test_node->u.test.before) {
141
+ test_arg = test_node->u.test.before(cmd_line, test_arg);
142
+ }
143
+ /* Prepend the arguments that we need */
144
+ g_string_prepend(cmd_line,
145
+ TARGET_NAME " -display none -machine accel=qtest -m 64 ");
146
+ return cmd_line->str;
147
+}
148
+
149
+/*
150
+ * This function is largely a copy of qos-test.c:walk_path. Since walk_path
151
+ * is itself a callback, its a little annoying to add another argument/layer of
152
+ * indirection
153
+ */
154
+static void walk_path(QOSGraphNode *orig_path, int len)
155
+{
156
+ QOSGraphNode *path;
157
+ QOSGraphEdge *edge;
158
+
159
+ /* etype set to QEDGE_CONSUMED_BY so that machine can add to the command line */
160
+ QOSEdgeType etype = QEDGE_CONSUMED_BY;
161
+
162
+ /* twice QOS_PATH_MAX_ELEMENT_SIZE since each edge can have its arg */
163
+ char **path_vec = g_new0(char *, (QOS_PATH_MAX_ELEMENT_SIZE * 2));
164
+ int path_vec_size = 0;
165
+
166
+ char *after_cmd, *before_cmd, *after_device;
167
+ GString *after_device_str = g_string_new("");
168
+ char *node_name = orig_path->name, *path_str;
169
+
170
+ GString *cmd_line = g_string_new("");
171
+ GString *cmd_line2 = g_string_new("");
172
+
173
+ path = qos_graph_get_node(node_name); /* root */
174
+ node_name = qos_graph_edge_get_dest(path->path_edge); /* machine name */
175
+
176
+ path_vec[path_vec_size++] = node_name;
177
+ path_vec[path_vec_size++] = qos_get_machine_type(node_name);
178
+
179
+ for (;;) {
180
+ path = qos_graph_get_node(node_name);
181
+ if (!path->path_edge) {
182
+ break;
183
+ }
184
+
185
+ node_name = qos_graph_edge_get_dest(path->path_edge);
186
+
187
+ /* append node command line + previous edge command line */
188
+ if (path->command_line && etype == QEDGE_CONSUMED_BY) {
189
+ g_string_append(cmd_line, path->command_line);
190
+ g_string_append(cmd_line, after_device_str->str);
191
+ g_string_truncate(after_device_str, 0);
192
+ }
193
+
194
+ path_vec[path_vec_size++] = qos_graph_edge_get_name(path->path_edge);
195
+ /* detect if edge has command line args */
196
+ after_cmd = qos_graph_edge_get_after_cmd_line(path->path_edge);
197
+ after_device = qos_graph_edge_get_extra_device_opts(path->path_edge);
198
+ before_cmd = qos_graph_edge_get_before_cmd_line(path->path_edge);
199
+ edge = qos_graph_get_edge(path->name, node_name);
200
+ etype = qos_graph_edge_get_type(edge);
201
+
202
+ if (before_cmd) {
203
+ g_string_append(cmd_line, before_cmd);
204
+ }
205
+ if (after_cmd) {
206
+ g_string_append(cmd_line2, after_cmd);
207
+ }
208
+ if (after_device) {
209
+ g_string_append(after_device_str, after_device);
210
+ }
211
+ }
212
+
213
+ path_vec[path_vec_size++] = NULL;
214
+ g_string_append(cmd_line, after_device_str->str);
215
+ g_string_free(after_device_str, true);
216
+
217
+ g_string_append(cmd_line, cmd_line2->str);
218
+ g_string_free(cmd_line2, true);
219
+
220
+ /*
221
+ * here position 0 has <arch>/<machine>, position 1 has <machine>.
222
+ * The path must not have the <arch>, qtest_add_data_func adds it.
223
+ */
224
+ path_str = g_strjoinv("/", path_vec + 1);
225
+
226
+ /* Check that this is the test we care about: */
227
+ char *test_name = strrchr(path_str, '/') + 1;
228
+ if (strcmp(test_name, fuzz_target_name) == 0) {
229
+ /*
230
+ * put arch/machine in position 1 so run_one_test can do its work
231
+ * and add the command line at position 0.
232
+ */
233
+ path_vec[1] = path_vec[0];
234
+ path_vec[0] = g_string_free(cmd_line, false);
235
+
236
+ fuzz_path_vec = path_vec;
237
+ } else {
238
+ g_free(path_vec);
239
+ }
240
+
241
+ g_free(path_str);
242
+}
243
+
244
+static const char *qos_get_cmdline(FuzzTarget *t)
245
+{
246
+ /*
247
+ * Set a global variable that we use to identify the qos_path for our
248
+ * fuzz_target
249
+ */
250
+ fuzz_target_name = t->name;
251
+ qos_set_machines_devices_available();
252
+ qos_graph_foreach_test_path(walk_path);
253
+ return qos_build_main_args();
254
+}
255
+
256
+void fuzz_add_qos_target(
257
+ FuzzTarget *fuzz_opts,
258
+ const char *interface,
259
+ QOSGraphTestOptions *opts
260
+ )
261
+{
262
+ qos_add_test(fuzz_opts->name, interface, NULL, opts);
263
+ fuzz_opts->get_init_cmdline = qos_get_cmdline;
264
+ fuzz_add_target(fuzz_opts);
265
+}
266
+
267
+void qos_init_path(QTestState *s)
268
+{
269
+ fuzz_qos_obj = qos_allocate_objects(s , &fuzz_qos_alloc);
270
+}
271
diff --git a/tests/qtest/fuzz/qos_fuzz.h b/tests/qtest/fuzz/qos_fuzz.h
272
new file mode 100644
273
index XXXXXXX..XXXXXXX
274
--- /dev/null
275
+++ b/tests/qtest/fuzz/qos_fuzz.h
276
@@ -XXX,XX +XXX,XX @@
277
+/*
278
+ * QOS-assisted fuzzing helpers
279
+ *
280
+ * Copyright Red Hat Inc., 2019
281
+ *
282
+ * Authors:
283
+ * Alexander Bulekov <alxndr@bu.edu>
284
+ *
285
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
286
+ * See the COPYING file in the top-level directory.
287
+ */
288
+
289
+#ifndef _QOS_FUZZ_H_
290
+#define _QOS_FUZZ_H_
291
+
292
+#include "tests/qtest/fuzz/fuzz.h"
293
+#include "tests/qtest/libqos/qgraph.h"
294
+
295
+int qos_fuzz(const unsigned char *Data, size_t Size);
296
+void qos_setup(void);
297
+
298
+extern void *fuzz_qos_obj;
299
+extern QGuestAllocator *fuzz_qos_alloc;
300
+
301
+void fuzz_add_qos_target(
302
+ FuzzTarget *fuzz_opts,
303
+ const char *interface,
304
+ QOSGraphTestOptions *opts
305
+ );
306
+
307
+void qos_init_path(QTestState *);
308
+
309
+#endif
25
--
310
--
26
2.21.0
311
2.24.1
27
312
28
diff view generated by jsdifflib
1
We had a test for a case where relative extent paths did not work, but
1
From: Alexander Bulekov <alxndr@bu.edu>
2
unfortunately we just fixed the underlying problem, so it works now.
3
This patch adds a new test case that still fails.
4
2
5
Signed-off-by: Max Reitz <mreitz@redhat.com>
3
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
6
Reviewed-by: John Snow <jsnow@redhat.com>
4
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
7
Message-id: 20190815153638.4600-4-mreitz@redhat.com
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Reviewed-by: John Snow <jsnow@redhat.com>
6
Message-id: 20200220041118.23264-18-alxndr@bu.edu
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
8
---
11
tests/qemu-iotests/059 | 27 +++++++++++++++++++++++++++
9
Makefile | 15 ++++++++++++++-
12
tests/qemu-iotests/059.out | 4 ++++
10
Makefile.target | 16 ++++++++++++++++
13
2 files changed, 31 insertions(+)
11
2 files changed, 30 insertions(+), 1 deletion(-)
14
12
15
diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059
13
diff --git a/Makefile b/Makefile
16
index XXXXXXX..XXXXXXX 100755
14
index XXXXXXX..XXXXXXX 100644
17
--- a/tests/qemu-iotests/059
15
--- a/Makefile
18
+++ b/tests/qemu-iotests/059
16
+++ b/Makefile
19
@@ -XXX,XX +XXX,XX @@ $QEMU_IMG convert -f qcow2 -O vmdk -o subformat=streamOptimized "$TEST_IMG.qcow2
17
@@ -XXX,XX +XXX,XX @@ config-host.h-timestamp: config-host.mak
20
18
qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
21
echo
19
    $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
22
echo "=== Testing monolithicFlat with internally generated JSON file name ==="
20
21
-TARGET_DIRS_RULES := $(foreach t, all clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
22
+TARGET_DIRS_RULES := $(foreach t, all fuzz clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
23
24
SOFTMMU_ALL_RULES=$(filter %-softmmu/all, $(TARGET_DIRS_RULES))
25
$(SOFTMMU_ALL_RULES): $(authz-obj-y)
26
@@ -XXX,XX +XXX,XX @@ ifdef DECOMPRESS_EDK2_BLOBS
27
$(SOFTMMU_ALL_RULES): $(edk2-decompressed)
28
endif
29
30
+SOFTMMU_FUZZ_RULES=$(filter %-softmmu/fuzz, $(TARGET_DIRS_RULES))
31
+$(SOFTMMU_FUZZ_RULES): $(authz-obj-y)
32
+$(SOFTMMU_FUZZ_RULES): $(block-obj-y)
33
+$(SOFTMMU_FUZZ_RULES): $(chardev-obj-y)
34
+$(SOFTMMU_FUZZ_RULES): $(crypto-obj-y)
35
+$(SOFTMMU_FUZZ_RULES): $(io-obj-y)
36
+$(SOFTMMU_FUZZ_RULES): config-all-devices.mak
37
+$(SOFTMMU_FUZZ_RULES): $(edk2-decompressed)
23
+
38
+
24
+echo '--- blkdebug ---'
39
.PHONY: $(TARGET_DIRS_RULES)
25
# Should work, because bdrv_dirname() works fine with blkdebug
40
# The $(TARGET_DIRS_RULES) are of the form SUBDIR/GOAL, so that
26
IMGOPTS="subformat=monolithicFlat" _make_test_img 64M
41
# $(dir $@) yields the sub-directory, and $(notdir $@) yields the sub-goal
27
$QEMU_IO -c "open -o driver=$IMGFMT,file.driver=blkdebug,file.image.filename=$TEST_IMG,file.inject-error.0.event=read_aio" \
42
@@ -XXX,XX +XXX,XX @@ subdir-slirp: slirp/all
28
@@ -XXX,XX +XXX,XX @@ $QEMU_IO -c "open -o driver=$IMGFMT,file.driver=blkdebug,file.image.filename=$TE
43
$(filter %/all, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
29
| _filter_testdir | _filter_imgfmt | _filter_img_info
44
    $(qom-obj-y)
30
_cleanup_test_img
45
31
46
+$(filter %/fuzz, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
32
+echo '--- quorum ---'
47
+    $(qom-obj-y) $(crypto-user-obj-$(CONFIG_USER_ONLY))
33
+# Should not work, because bdrv_dirname() does not work with quorum
34
+IMGOPTS="subformat=monolithicFlat" _make_test_img 64M
35
+cp "$TEST_IMG" "$TEST_IMG.orig"
36
+
48
+
37
+filename="json:{
49
ROM_DIRS = $(addprefix pc-bios/, $(ROMS))
38
+ \"driver\": \"$IMGFMT\",
50
ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS)))
39
+ \"file\": {
51
# Only keep -O and -g cflags
40
+ \"driver\": \"quorum\",
52
@@ -XXX,XX +XXX,XX @@ $(ROM_DIRS_RULES):
41
+ \"children\": [ {
53
42
+ \"driver\": \"file\",
54
.PHONY: recurse-all recurse-clean recurse-install
43
+ \"filename\": \"$TEST_IMG\"
55
recurse-all: $(addsuffix /all, $(TARGET_DIRS) $(ROM_DIRS))
44
+ }, {
56
+recurse-fuzz: $(addsuffix /fuzz, $(TARGET_DIRS) $(ROM_DIRS))
45
+ \"driver\": \"file\",
57
recurse-clean: $(addsuffix /clean, $(TARGET_DIRS) $(ROM_DIRS))
46
+ \"filename\": \"$TEST_IMG.orig\"
58
recurse-install: $(addsuffix /install, $(TARGET_DIRS))
47
+ } ],
59
$(addsuffix /install, $(TARGET_DIRS)): all
48
+ \"vote-threshold\": 1
60
diff --git a/Makefile.target b/Makefile.target
49
+ } }"
61
index XXXXXXX..XXXXXXX 100644
62
--- a/Makefile.target
63
+++ b/Makefile.target
64
@@ -XXX,XX +XXX,XX @@ ifdef CONFIG_TRACE_SYSTEMTAP
65
    rm -f *.stp
66
endif
67
68
+ifdef CONFIG_FUZZ
69
+include $(SRC_PATH)/tests/qtest/fuzz/Makefile.include
70
+include $(SRC_PATH)/tests/qtest/Makefile.include
50
+
71
+
51
+filename=$(echo "$filename" | tr '\n' ' ' | sed -e 's/\s\+/ /g')
72
+fuzz: fuzz-vars
52
+$QEMU_IMG info "$filename" 2>&1 \
73
+fuzz-vars: QEMU_CFLAGS := $(FUZZ_CFLAGS) $(QEMU_CFLAGS)
53
+ | sed -e "s/'json:[^']*'/\$QUORUM_FILE/g" \
74
+fuzz-vars: QEMU_LDFLAGS := $(FUZZ_LDFLAGS) $(QEMU_LDFLAGS)
54
+ | _filter_testdir | _filter_imgfmt | _filter_img_info
75
+fuzz-vars: $(QEMU_PROG_FUZZ)
76
+dummy := $(call unnest-vars,, fuzz-obj-y)
55
+
77
+
56
+
78
+
57
echo
79
+$(QEMU_PROG_FUZZ): config-devices.mak $(all-obj-y) $(COMMON_LDADDS) $(fuzz-obj-y)
58
echo "=== Testing version 3 ==="
80
+    $(call LINK, $(filter-out %.mak, $^))
59
_use_sample_img iotest-version3.vmdk.bz2
81
+
60
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
82
+endif
61
index XXXXXXX..XXXXXXX 100644
83
+
62
--- a/tests/qemu-iotests/059.out
84
install: all
63
+++ b/tests/qemu-iotests/059.out
85
ifneq ($(PROGS),)
64
@@ -XXX,XX +XXX,XX @@ wrote 512/512 bytes at offset 10240
86
    $(call install-prog,$(PROGS),$(DESTDIR)$(bindir))
65
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
66
67
=== Testing monolithicFlat with internally generated JSON file name ===
68
+--- blkdebug ---
69
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
70
format name: IMGFMT
71
cluster size: 0 bytes
72
vm state offset: 0 bytes
73
+--- quorum ---
74
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
75
+qemu-img: Could not open $QUORUM_FILE: Cannot use relative paths with VMDK descriptor file $QUORUM_FILE: Cannot generate a base directory for quorum nodes
76
77
=== Testing version 3 ===
78
image: TEST_DIR/iotest-version3.IMGFMT
79
--
87
--
80
2.21.0
88
2.24.1
81
89
82
diff view generated by jsdifflib
1
From: Thomas Huth <thuth@redhat.com>
1
From: Alexander Bulekov <alxndr@bu.edu>
2
2
3
The sanitizers (especially the address sanitizer from Clang) are
3
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
4
sometimes printing out warnings or false positives - this spoils
4
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
5
the output of the iotests, causing some of the tests to fail.
5
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
6
Thus let's skip the automatic iotests during "make check" when the
6
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
7
user configured QEMU with --enable-sanitizers.
7
Message-id: 20200220041118.23264-19-alxndr@bu.edu
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
10
configure | 39 +++++++++++++++++++++++++++++++++++++++
11
1 file changed, 39 insertions(+)
8
12
9
Signed-off-by: Thomas Huth <thuth@redhat.com>
13
diff --git a/configure b/configure
10
Message-id: 20190823084203.29734-1-thuth@redhat.com
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
13
tests/check-block.sh | 5 +++++
14
1 file changed, 5 insertions(+)
15
16
diff --git a/tests/check-block.sh b/tests/check-block.sh
17
index XXXXXXX..XXXXXXX 100755
14
index XXXXXXX..XXXXXXX 100755
18
--- a/tests/check-block.sh
15
--- a/configure
19
+++ b/tests/check-block.sh
16
+++ b/configure
20
@@ -XXX,XX +XXX,XX @@ if grep -q "TARGET_GPROF=y" *-softmmu/config-target.mak 2>/dev/null ; then
17
@@ -XXX,XX +XXX,XX @@ debug_mutex="no"
21
exit 0
18
libpmem=""
19
default_devices="yes"
20
plugins="no"
21
+fuzzing="no"
22
23
supported_cpu="no"
24
supported_os="no"
25
@@ -XXX,XX +XXX,XX @@ int main(void) { return 0; }
26
EOF
27
}
28
29
+write_c_fuzzer_skeleton() {
30
+ cat > $TMPC <<EOF
31
+#include <stdint.h>
32
+#include <sys/types.h>
33
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
34
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; }
35
+EOF
36
+}
37
+
38
if check_define __linux__ ; then
39
targetos="Linux"
40
elif check_define _WIN32 ; then
41
@@ -XXX,XX +XXX,XX @@ for opt do
42
;;
43
--disable-containers) use_containers="no"
44
;;
45
+ --enable-fuzzing) fuzzing=yes
46
+ ;;
47
+ --disable-fuzzing) fuzzing=no
48
+ ;;
49
*)
50
echo "ERROR: unknown option $opt"
51
echo "Try '$0 --help' for more information"
52
@@ -XXX,XX +XXX,XX @@ EOF
53
fi
22
fi
54
fi
23
55
24
+if grep -q "CFLAGS.*-fsanitize" config-host.mak 2>/dev/null ; then
56
+##########################################
25
+ echo "Sanitizers are enabled ==> Not running the qemu-iotests."
57
+# checks for fuzzer
26
+ exit 0
58
+if test "$fuzzing" = "yes" ; then
59
+ write_c_fuzzer_skeleton
60
+ if compile_prog "$CPU_CFLAGS -Werror -fsanitize=address,fuzzer" ""; then
61
+ have_fuzzer=yes
62
+ fi
27
+fi
63
+fi
28
+
64
+
29
if [ -z "$(find . -name 'qemu-system-*' -print)" ]; then
65
##########################################
30
echo "No qemu-system binary available ==> Not running the qemu-iotests."
66
# check for libpmem
31
exit 0
67
68
@@ -XXX,XX +XXX,XX @@ echo "libpmem support $libpmem"
69
echo "libudev $libudev"
70
echo "default devices $default_devices"
71
echo "plugin support $plugins"
72
+echo "fuzzing support $fuzzing"
73
74
if test "$supported_cpu" = "no"; then
75
echo
76
@@ -XXX,XX +XXX,XX @@ fi
77
if test "$sheepdog" = "yes" ; then
78
echo "CONFIG_SHEEPDOG=y" >> $config_host_mak
79
fi
80
+if test "$fuzzing" = "yes" ; then
81
+ if test "$have_fuzzer" = "yes"; then
82
+ FUZZ_LDFLAGS=" -fsanitize=address,fuzzer"
83
+ FUZZ_CFLAGS=" -fsanitize=address,fuzzer"
84
+ CFLAGS=" -fsanitize=address,fuzzer-no-link"
85
+ else
86
+ error_exit "Your compiler doesn't support -fsanitize=address,fuzzer"
87
+ exit 1
88
+ fi
89
+fi
90
91
if test "$plugins" = "yes" ; then
92
echo "CONFIG_PLUGIN=y" >> $config_host_mak
93
@@ -XXX,XX +XXX,XX @@ if test "$libudev" != "no"; then
94
echo "CONFIG_LIBUDEV=y" >> $config_host_mak
95
echo "LIBUDEV_LIBS=$libudev_libs" >> $config_host_mak
96
fi
97
+if test "$fuzzing" != "no"; then
98
+ echo "CONFIG_FUZZ=y" >> $config_host_mak
99
+ echo "FUZZ_CFLAGS=$FUZZ_CFLAGS" >> $config_host_mak
100
+ echo "FUZZ_LDFLAGS=$FUZZ_LDFLAGS" >> $config_host_mak
101
+fi
102
103
if test "$edk2_blobs" = "yes" ; then
104
echo "DECOMPRESS_EDK2_BLOBS=y" >> $config_host_mak
32
--
105
--
33
2.21.0
106
2.24.1
34
107
35
diff view generated by jsdifflib
1
The error message for the test case where we have a quorum node for
1
From: Alexander Bulekov <alxndr@bu.edu>
2
which no directory name can be generated is different: For
2
3
twoGbMaxExtentSparse, it complains that it cannot open the extent file.
3
These three targets should simply fuzz reads/writes to a couple ioports,
4
For other (sub)formats, it just notes that it cannot determine the
4
but they mostly serve as examples of different ways to write targets.
5
backing file path. Both are fine, but just disable twoGbMaxExtentSparse
5
They demonstrate using qtest and qos for fuzzing, as well as using
6
for simplicity's sake.
6
rebooting and forking to reset state, or not resetting it at all.
7
7
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
9
Reviewed-by: John Snow <jsnow@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Message-id: 20190815153638.4600-7-mreitz@redhat.com
10
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
11
Reviewed-by: John Snow <jsnow@redhat.com>
11
Message-id: 20200220041118.23264-20-alxndr@bu.edu
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
13
---
14
tests/qemu-iotests/110 | 3 ++-
14
tests/qtest/fuzz/Makefile.include | 3 +
15
1 file changed, 2 insertions(+), 1 deletion(-)
15
tests/qtest/fuzz/i440fx_fuzz.c | 193 ++++++++++++++++++++++++++++++
16
16
2 files changed, 196 insertions(+)
17
diff --git a/tests/qemu-iotests/110 b/tests/qemu-iotests/110
17
create mode 100644 tests/qtest/fuzz/i440fx_fuzz.c
18
index XXXXXXX..XXXXXXX 100755
18
19
--- a/tests/qemu-iotests/110
19
diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
20
+++ b/tests/qemu-iotests/110
20
index XXXXXXX..XXXXXXX 100644
21
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
21
--- a/tests/qtest/fuzz/Makefile.include
22
# Any format supporting backing files
22
+++ b/tests/qtest/fuzz/Makefile.include
23
_supported_fmt qed qcow qcow2 vmdk
23
@@ -XXX,XX +XXX,XX @@ fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
24
_supported_proto file
24
fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
25
-_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
25
fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
26
+_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" \
26
27
+ "subformat=twoGbMaxExtentSparse"
27
+# Targets
28
28
+fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
29
TEST_IMG_REL=$(basename "$TEST_IMG")
29
+
30
30
FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
31
32
# Linker Script to force coverage-counters into known regions which we can mark
33
diff --git a/tests/qtest/fuzz/i440fx_fuzz.c b/tests/qtest/fuzz/i440fx_fuzz.c
34
new file mode 100644
35
index XXXXXXX..XXXXXXX
36
--- /dev/null
37
+++ b/tests/qtest/fuzz/i440fx_fuzz.c
38
@@ -XXX,XX +XXX,XX @@
39
+/*
40
+ * I440FX Fuzzing Target
41
+ *
42
+ * Copyright Red Hat Inc., 2019
43
+ *
44
+ * Authors:
45
+ * Alexander Bulekov <alxndr@bu.edu>
46
+ *
47
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
48
+ * See the COPYING file in the top-level directory.
49
+ */
50
+
51
+#include "qemu/osdep.h"
52
+
53
+#include "qemu/main-loop.h"
54
+#include "tests/qtest/libqtest.h"
55
+#include "tests/qtest/libqos/pci.h"
56
+#include "tests/qtest/libqos/pci-pc.h"
57
+#include "fuzz.h"
58
+#include "fuzz/qos_fuzz.h"
59
+#include "fuzz/fork_fuzz.h"
60
+
61
+
62
+#define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8
63
+#define I440FX_PCI_HOST_BRIDGE_DATA 0xcfc
64
+
65
+/*
66
+ * the input to the fuzzing functions below is a buffer of random bytes. we
67
+ * want to convert these bytes into a sequence of qtest or qos calls. to do
68
+ * this we define some opcodes:
69
+ */
70
+enum action_id {
71
+ WRITEB,
72
+ WRITEW,
73
+ WRITEL,
74
+ READB,
75
+ READW,
76
+ READL,
77
+ ACTION_MAX
78
+};
79
+
80
+static void i440fx_fuzz_qtest(QTestState *s,
81
+ const unsigned char *Data, size_t Size) {
82
+ /*
83
+ * loop over the Data, breaking it up into actions. each action has an
84
+ * opcode, address offset and value
85
+ */
86
+ typedef struct QTestFuzzAction {
87
+ uint8_t opcode;
88
+ uint8_t addr;
89
+ uint32_t value;
90
+ } QTestFuzzAction;
91
+ QTestFuzzAction a;
92
+
93
+ while (Size >= sizeof(a)) {
94
+ /* make a copy of the action so we can normalize the values in-place */
95
+ memcpy(&a, Data, sizeof(a));
96
+ /* select between two i440fx Port IO addresses */
97
+ uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG :
98
+ I440FX_PCI_HOST_BRIDGE_DATA;
99
+ switch (a.opcode % ACTION_MAX) {
100
+ case WRITEB:
101
+ qtest_outb(s, addr, (uint8_t)a.value);
102
+ break;
103
+ case WRITEW:
104
+ qtest_outw(s, addr, (uint16_t)a.value);
105
+ break;
106
+ case WRITEL:
107
+ qtest_outl(s, addr, (uint32_t)a.value);
108
+ break;
109
+ case READB:
110
+ qtest_inb(s, addr);
111
+ break;
112
+ case READW:
113
+ qtest_inw(s, addr);
114
+ break;
115
+ case READL:
116
+ qtest_inl(s, addr);
117
+ break;
118
+ }
119
+ /* Move to the next operation */
120
+ Size -= sizeof(a);
121
+ Data += sizeof(a);
122
+ }
123
+ flush_events(s);
124
+}
125
+
126
+static void i440fx_fuzz_qos(QTestState *s,
127
+ const unsigned char *Data, size_t Size) {
128
+ /*
129
+ * Same as i440fx_fuzz_qtest, but using QOS. devfn is incorporated into the
130
+ * value written over Port IO
131
+ */
132
+ typedef struct QOSFuzzAction {
133
+ uint8_t opcode;
134
+ uint8_t offset;
135
+ int devfn;
136
+ uint32_t value;
137
+ } QOSFuzzAction;
138
+
139
+ static QPCIBus *bus;
140
+ if (!bus) {
141
+ bus = qpci_new_pc(s, fuzz_qos_alloc);
142
+ }
143
+
144
+ QOSFuzzAction a;
145
+ while (Size >= sizeof(a)) {
146
+ memcpy(&a, Data, sizeof(a));
147
+ switch (a.opcode % ACTION_MAX) {
148
+ case WRITEB:
149
+ bus->config_writeb(bus, a.devfn, a.offset, (uint8_t)a.value);
150
+ break;
151
+ case WRITEW:
152
+ bus->config_writew(bus, a.devfn, a.offset, (uint16_t)a.value);
153
+ break;
154
+ case WRITEL:
155
+ bus->config_writel(bus, a.devfn, a.offset, (uint32_t)a.value);
156
+ break;
157
+ case READB:
158
+ bus->config_readb(bus, a.devfn, a.offset);
159
+ break;
160
+ case READW:
161
+ bus->config_readw(bus, a.devfn, a.offset);
162
+ break;
163
+ case READL:
164
+ bus->config_readl(bus, a.devfn, a.offset);
165
+ break;
166
+ }
167
+ Size -= sizeof(a);
168
+ Data += sizeof(a);
169
+ }
170
+ flush_events(s);
171
+}
172
+
173
+static void i440fx_fuzz_qos_fork(QTestState *s,
174
+ const unsigned char *Data, size_t Size) {
175
+ if (fork() == 0) {
176
+ i440fx_fuzz_qos(s, Data, Size);
177
+ _Exit(0);
178
+ } else {
179
+ wait(NULL);
180
+ }
181
+}
182
+
183
+static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest"
184
+ "-m 0 -display none";
185
+static const char *i440fx_argv(FuzzTarget *t)
186
+{
187
+ return i440fx_qtest_argv;
188
+}
189
+
190
+static void fork_init(void)
191
+{
192
+ counter_shm_init();
193
+}
194
+
195
+static void register_pci_fuzz_targets(void)
196
+{
197
+ /* Uses simple qtest commands and reboots to reset state */
198
+ fuzz_add_target(&(FuzzTarget){
199
+ .name = "i440fx-qtest-reboot-fuzz",
200
+ .description = "Fuzz the i440fx using raw qtest commands and"
201
+ "rebooting after each run",
202
+ .get_init_cmdline = i440fx_argv,
203
+ .fuzz = i440fx_fuzz_qtest});
204
+
205
+ /* Uses libqos and forks to prevent state leakage */
206
+ fuzz_add_qos_target(&(FuzzTarget){
207
+ .name = "i440fx-qos-fork-fuzz",
208
+ .description = "Fuzz the i440fx using raw qtest commands and"
209
+ "rebooting after each run",
210
+ .pre_vm_init = &fork_init,
211
+ .fuzz = i440fx_fuzz_qos_fork,},
212
+ "i440FX-pcihost",
213
+ &(QOSGraphTestOptions){}
214
+ );
215
+
216
+ /*
217
+ * Uses libqos. Doesn't do anything to reset state. Note that if we were to
218
+ * reboot after each run, we would also have to redo the qos-related
219
+ * initialization (qos_init_path)
220
+ */
221
+ fuzz_add_qos_target(&(FuzzTarget){
222
+ .name = "i440fx-qos-noreset-fuzz",
223
+ .description = "Fuzz the i440fx using raw qtest commands and"
224
+ "rebooting after each run",
225
+ .fuzz = i440fx_fuzz_qos,},
226
+ "i440FX-pcihost",
227
+ &(QOSGraphTestOptions){}
228
+ );
229
+}
230
+
231
+fuzz_target_init(register_pci_fuzz_targets);
31
--
232
--
32
2.21.0
233
2.24.1
33
234
34
diff view generated by jsdifflib
1
Compressed writes generally have to write full clusters, not just in
1
From: Alexander Bulekov <alxndr@bu.edu>
2
theory but also in practice when it comes to vmdk's streamOptimized
2
3
subformat. It currently is just silently broken for writes with
3
The virtio-net fuzz target feeds inputs to all three virtio-net
4
non-zero in-cluster offsets:
4
virtqueues, and uses forking to avoid leaking state between fuzz runs.
5
5
6
$ qemu-img create -f vmdk -o subformat=streamOptimized foo.vmdk 1M
6
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
7
$ qemu-io -c 'write 4k 4k' -c 'read 4k 4k' foo.vmdk
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
wrote 4096/4096 bytes at offset 4096
8
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
9
4 KiB, 1 ops; 00.01 sec (443.724 KiB/sec and 110.9309 ops/sec)
9
Message-id: 20200220041118.23264-21-alxndr@bu.edu
10
read failed: Invalid argument
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
12
(The technical reason is that vmdk_write_extent() just writes the
13
incomplete compressed data actually to offset 4k. When reading the
14
data, vmdk_read_extent() looks at offset 0 and finds the compressed data
15
size to be 0, because that is what it reads from there. This yields an
16
error.)
17
18
For incomplete writes with zero in-cluster offsets, the error path when
19
reading the rest of the cluster is a bit different, but the result is
20
the same:
21
22
$ qemu-img create -f vmdk -o subformat=streamOptimized foo.vmdk 1M
23
$ qemu-io -c 'write 0k 4k' -c 'read 4k 4k' foo.vmdk
24
wrote 4096/4096 bytes at offset 0
25
4 KiB, 1 ops; 00.01 sec (362.641 KiB/sec and 90.6603 ops/sec)
26
read failed: Invalid argument
27
28
(Here, vmdk_read_extent() finds the data and then sees that the
29
uncompressed data is short.)
30
31
It is better to reject invalid writes than to make the user believe they
32
might have succeeded and then fail when trying to read it back.
33
34
Signed-off-by: Max Reitz <mreitz@redhat.com>
35
Reviewed-by: John Snow <jsnow@redhat.com>
36
Message-id: 20190815153638.4600-5-mreitz@redhat.com
37
Reviewed-by: John Snow <jsnow@redhat.com>
38
Signed-off-by: Max Reitz <mreitz@redhat.com>
39
---
11
---
40
block/vmdk.c | 10 ++++++++++
12
tests/qtest/fuzz/Makefile.include | 1 +
41
1 file changed, 10 insertions(+)
13
tests/qtest/fuzz/virtio_net_fuzz.c | 198 +++++++++++++++++++++++++++++
42
14
2 files changed, 199 insertions(+)
43
diff --git a/block/vmdk.c b/block/vmdk.c
15
create mode 100644 tests/qtest/fuzz/virtio_net_fuzz.c
16
17
diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
44
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
45
--- a/block/vmdk.c
19
--- a/tests/qtest/fuzz/Makefile.include
46
+++ b/block/vmdk.c
20
+++ b/tests/qtest/fuzz/Makefile.include
47
@@ -XXX,XX +XXX,XX @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
21
@@ -XXX,XX +XXX,XX @@ fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
48
if (extent->compressed) {
22
49
void *compressed_data;
23
# Targets
50
24
fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
51
+ /* Only whole clusters */
25
+fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o
52
+ if (offset_in_cluster ||
26
53
+ n_bytes > (extent->cluster_sectors * SECTOR_SIZE) ||
27
FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
54
+ (n_bytes < (extent->cluster_sectors * SECTOR_SIZE) &&
28
55
+ offset + n_bytes != extent->end_sector * SECTOR_SIZE))
29
diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c b/tests/qtest/fuzz/virtio_net_fuzz.c
56
+ {
30
new file mode 100644
57
+ ret = -EINVAL;
31
index XXXXXXX..XXXXXXX
58
+ goto out;
32
--- /dev/null
33
+++ b/tests/qtest/fuzz/virtio_net_fuzz.c
34
@@ -XXX,XX +XXX,XX @@
35
+/*
36
+ * virtio-net Fuzzing Target
37
+ *
38
+ * Copyright Red Hat Inc., 2019
39
+ *
40
+ * Authors:
41
+ * Alexander Bulekov <alxndr@bu.edu>
42
+ *
43
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
44
+ * See the COPYING file in the top-level directory.
45
+ */
46
+
47
+#include "qemu/osdep.h"
48
+
49
+#include "standard-headers/linux/virtio_config.h"
50
+#include "tests/qtest/libqtest.h"
51
+#include "tests/qtest/libqos/virtio-net.h"
52
+#include "fuzz.h"
53
+#include "fork_fuzz.h"
54
+#include "qos_fuzz.h"
55
+
56
+
57
+#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000)
58
+#define QVIRTIO_RX_VQ 0
59
+#define QVIRTIO_TX_VQ 1
60
+#define QVIRTIO_CTRL_VQ 2
61
+
62
+static int sockfds[2];
63
+static bool sockfds_initialized;
64
+
65
+static void virtio_net_fuzz_multi(QTestState *s,
66
+ const unsigned char *Data, size_t Size, bool check_used)
67
+{
68
+ typedef struct vq_action {
69
+ uint8_t queue;
70
+ uint8_t length;
71
+ uint8_t write;
72
+ uint8_t next;
73
+ uint8_t rx;
74
+ } vq_action;
75
+
76
+ uint32_t free_head = 0;
77
+
78
+ QGuestAllocator *t_alloc = fuzz_qos_alloc;
79
+
80
+ QVirtioNet *net_if = fuzz_qos_obj;
81
+ QVirtioDevice *dev = net_if->vdev;
82
+ QVirtQueue *q;
83
+ vq_action vqa;
84
+ while (Size >= sizeof(vqa)) {
85
+ memcpy(&vqa, Data, sizeof(vqa));
86
+ Data += sizeof(vqa);
87
+ Size -= sizeof(vqa);
88
+
89
+ q = net_if->queues[vqa.queue % 3];
90
+
91
+ vqa.length = vqa.length >= Size ? Size : vqa.length;
92
+
93
+ /*
94
+ * Only attempt to write incoming packets, when using the socket
95
+ * backend. Otherwise, always place the input on a virtqueue.
96
+ */
97
+ if (vqa.rx && sockfds_initialized) {
98
+ write(sockfds[0], Data, vqa.length);
99
+ } else {
100
+ vqa.rx = 0;
101
+ uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
102
+ /*
103
+ * If checking used ring, ensure that the fuzzer doesn't trigger
104
+ * trivial asserion failure on zero-zied buffer
105
+ */
106
+ qtest_memwrite(s, req_addr, Data, vqa.length);
107
+
108
+
109
+ free_head = qvirtqueue_add(s, q, req_addr, vqa.length,
110
+ vqa.write, vqa.next);
111
+ qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
112
+ qvirtqueue_kick(s, dev, q, free_head);
59
+ }
113
+ }
60
+
114
+
61
if (!extent->has_marker) {
115
+ /* Run the main loop */
62
ret = -EINVAL;
116
+ qtest_clock_step(s, 100);
63
goto out;
117
+ flush_events(s);
118
+
119
+ /* Wait on used descriptors */
120
+ if (check_used && !vqa.rx) {
121
+ gint64 start_time = g_get_monotonic_time();
122
+ /*
123
+ * normally, we could just use qvirtio_wait_used_elem, but since we
124
+ * must manually run the main-loop for all the bhs to run, we use
125
+ * this hack with flush_events(), to run the main_loop
126
+ */
127
+ while (!vqa.rx && q != net_if->queues[QVIRTIO_RX_VQ]) {
128
+ uint32_t got_desc_idx;
129
+ /* Input led to a virtio_error */
130
+ if (dev->bus->get_status(dev) & VIRTIO_CONFIG_S_NEEDS_RESET) {
131
+ break;
132
+ }
133
+ if (dev->bus->get_queue_isr_status(dev, q) &&
134
+ qvirtqueue_get_buf(s, q, &got_desc_idx, NULL)) {
135
+ g_assert_cmpint(got_desc_idx, ==, free_head);
136
+ break;
137
+ }
138
+ g_assert(g_get_monotonic_time() - start_time
139
+ <= QVIRTIO_NET_TIMEOUT_US);
140
+
141
+ /* Run the main loop */
142
+ qtest_clock_step(s, 100);
143
+ flush_events(s);
144
+ }
145
+ }
146
+ Data += vqa.length;
147
+ Size -= vqa.length;
148
+ }
149
+}
150
+
151
+static void virtio_net_fork_fuzz(QTestState *s,
152
+ const unsigned char *Data, size_t Size)
153
+{
154
+ if (fork() == 0) {
155
+ virtio_net_fuzz_multi(s, Data, Size, false);
156
+ flush_events(s);
157
+ _Exit(0);
158
+ } else {
159
+ wait(NULL);
160
+ }
161
+}
162
+
163
+static void virtio_net_fork_fuzz_check_used(QTestState *s,
164
+ const unsigned char *Data, size_t Size)
165
+{
166
+ if (fork() == 0) {
167
+ virtio_net_fuzz_multi(s, Data, Size, true);
168
+ flush_events(s);
169
+ _Exit(0);
170
+ } else {
171
+ wait(NULL);
172
+ }
173
+}
174
+
175
+static void virtio_net_pre_fuzz(QTestState *s)
176
+{
177
+ qos_init_path(s);
178
+ counter_shm_init();
179
+}
180
+
181
+static void *virtio_net_test_setup_socket(GString *cmd_line, void *arg)
182
+{
183
+ int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds);
184
+ g_assert_cmpint(ret, !=, -1);
185
+ fcntl(sockfds[0], F_SETFL, O_NONBLOCK);
186
+ sockfds_initialized = true;
187
+ g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ",
188
+ sockfds[1]);
189
+ return arg;
190
+}
191
+
192
+static void *virtio_net_test_setup_user(GString *cmd_line, void *arg)
193
+{
194
+ g_string_append_printf(cmd_line, " -netdev user,id=hs0 ");
195
+ return arg;
196
+}
197
+
198
+static void register_virtio_net_fuzz_targets(void)
199
+{
200
+ fuzz_add_qos_target(&(FuzzTarget){
201
+ .name = "virtio-net-socket",
202
+ .description = "Fuzz the virtio-net virtual queues. Fuzz incoming "
203
+ "traffic using the socket backend",
204
+ .pre_fuzz = &virtio_net_pre_fuzz,
205
+ .fuzz = virtio_net_fork_fuzz,},
206
+ "virtio-net",
207
+ &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket}
208
+ );
209
+
210
+ fuzz_add_qos_target(&(FuzzTarget){
211
+ .name = "virtio-net-socket-check-used",
212
+ .description = "Fuzz the virtio-net virtual queues. Wait for the "
213
+ "descriptors to be used. Timeout may indicate improperly handled "
214
+ "input",
215
+ .pre_fuzz = &virtio_net_pre_fuzz,
216
+ .fuzz = virtio_net_fork_fuzz_check_used,},
217
+ "virtio-net",
218
+ &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket}
219
+ );
220
+ fuzz_add_qos_target(&(FuzzTarget){
221
+ .name = "virtio-net-slirp",
222
+ .description = "Fuzz the virtio-net virtual queues with the slirp "
223
+ " backend. Warning: May result in network traffic emitted from the "
224
+ " process. Run in an isolated network environment.",
225
+ .pre_fuzz = &virtio_net_pre_fuzz,
226
+ .fuzz = virtio_net_fork_fuzz,},
227
+ "virtio-net",
228
+ &(QOSGraphTestOptions){.before = virtio_net_test_setup_user}
229
+ );
230
+}
231
+
232
+fuzz_target_init(register_virtio_net_fuzz_targets);
64
--
233
--
65
2.21.0
234
2.24.1
66
235
67
diff view generated by jsdifflib
1
streamOptimized does not support writes that do not span exactly one
1
From: Alexander Bulekov <alxndr@bu.edu>
2
cluster. Furthermore, it cannot rewrite already allocated clusters.
2
3
As such, many iotests do not work with it. Disable them.
3
The virtio-scsi fuzz target sets up and fuzzes the available virtio-scsi
4
4
queues. After an element is placed on a queue, the fuzzer can select
5
Signed-off-by: Max Reitz <mreitz@redhat.com>
5
whether to perform a kick, or continue adding elements.
6
Message-id: 20190815153638.4600-6-mreitz@redhat.com
6
7
Reviewed-by: John Snow <jsnow@redhat.com>
7
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
9
Message-id: 20200220041118.23264-22-alxndr@bu.edu
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
11
---
10
tests/qemu-iotests/002 | 1 +
12
tests/qtest/fuzz/Makefile.include | 1 +
11
tests/qemu-iotests/003 | 1 +
13
tests/qtest/fuzz/virtio_scsi_fuzz.c | 213 ++++++++++++++++++++++++++++
12
tests/qemu-iotests/005 | 3 ++-
14
2 files changed, 214 insertions(+)
13
tests/qemu-iotests/009 | 1 +
15
create mode 100644 tests/qtest/fuzz/virtio_scsi_fuzz.c
14
tests/qemu-iotests/010 | 1 +
16
15
tests/qemu-iotests/011 | 1 +
17
diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
16
tests/qemu-iotests/017 | 3 ++-
18
index XXXXXXX..XXXXXXX 100644
17
tests/qemu-iotests/018 | 3 ++-
19
--- a/tests/qtest/fuzz/Makefile.include
18
tests/qemu-iotests/019 | 3 ++-
20
+++ b/tests/qtest/fuzz/Makefile.include
19
tests/qemu-iotests/020 | 3 ++-
21
@@ -XXX,XX +XXX,XX @@ fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
20
tests/qemu-iotests/027 | 1 +
22
# Targets
21
tests/qemu-iotests/032 | 1 +
23
fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
22
tests/qemu-iotests/033 | 1 +
24
fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o
23
tests/qemu-iotests/034 | 3 ++-
25
+fuzz-obj-y += tests/qtest/fuzz/virtio_scsi_fuzz.o
24
tests/qemu-iotests/037 | 3 ++-
26
25
tests/qemu-iotests/063 | 3 ++-
27
FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
26
tests/qemu-iotests/072 | 1 +
28
27
tests/qemu-iotests/105 | 3 ++-
29
diff --git a/tests/qtest/fuzz/virtio_scsi_fuzz.c b/tests/qtest/fuzz/virtio_scsi_fuzz.c
28
tests/qemu-iotests/197 | 1 +
30
new file mode 100644
29
tests/qemu-iotests/215 | 1 +
31
index XXXXXXX..XXXXXXX
30
tests/qemu-iotests/251 | 1 +
32
--- /dev/null
31
21 files changed, 30 insertions(+), 9 deletions(-)
33
+++ b/tests/qtest/fuzz/virtio_scsi_fuzz.c
32
34
@@ -XXX,XX +XXX,XX @@
33
diff --git a/tests/qemu-iotests/002 b/tests/qemu-iotests/002
35
+/*
34
index XXXXXXX..XXXXXXX 100755
36
+ * virtio-serial Fuzzing Target
35
--- a/tests/qemu-iotests/002
37
+ *
36
+++ b/tests/qemu-iotests/002
38
+ * Copyright Red Hat Inc., 2019
37
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
39
+ *
38
40
+ * Authors:
39
_supported_fmt generic
41
+ * Alexander Bulekov <alxndr@bu.edu>
40
_supported_proto generic
42
+ *
41
+_unsupported_imgopts "subformat=streamOptimized"
43
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
42
44
+ * See the COPYING file in the top-level directory.
43
45
+ */
44
size=128M
46
+
45
diff --git a/tests/qemu-iotests/003 b/tests/qemu-iotests/003
47
+#include "qemu/osdep.h"
46
index XXXXXXX..XXXXXXX 100755
48
+
47
--- a/tests/qemu-iotests/003
49
+#include "tests/qtest/libqtest.h"
48
+++ b/tests/qemu-iotests/003
50
+#include "libqos/virtio-scsi.h"
49
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
51
+#include "libqos/virtio.h"
50
52
+#include "libqos/virtio-pci.h"
51
_supported_fmt generic
53
+#include "standard-headers/linux/virtio_ids.h"
52
_supported_proto generic
54
+#include "standard-headers/linux/virtio_pci.h"
53
+_unsupported_imgopts "subformat=streamOptimized"
55
+#include "standard-headers/linux/virtio_scsi.h"
54
56
+#include "fuzz.h"
55
size=128M
57
+#include "fork_fuzz.h"
56
offset=67M
58
+#include "qos_fuzz.h"
57
diff --git a/tests/qemu-iotests/005 b/tests/qemu-iotests/005
59
+
58
index XXXXXXX..XXXXXXX 100755
60
+#define PCI_SLOT 0x02
59
--- a/tests/qemu-iotests/005
61
+#define PCI_FN 0x00
60
+++ b/tests/qemu-iotests/005
62
+#define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000)
61
@@ -XXX,XX +XXX,XX @@ _supported_fmt generic
63
+
62
_supported_proto generic
64
+#define MAX_NUM_QUEUES 64
63
_supported_os Linux
65
+
64
_unsupported_imgopts "subformat=twoGbMaxExtentFlat" \
66
+/* Based on tests/virtio-scsi-test.c */
65
- "subformat=twoGbMaxExtentSparse"
67
+typedef struct {
66
+ "subformat=twoGbMaxExtentSparse" \
68
+ int num_queues;
67
+ "subformat=streamOptimized"
69
+ QVirtQueue *vq[MAX_NUM_QUEUES + 2];
68
70
+} QVirtioSCSIQueues;
69
# vpc is limited to 127GB, so we can't test it here
71
+
70
if [ "$IMGFMT" = "vpc" ]; then
72
+static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev, uint64_t mask)
71
diff --git a/tests/qemu-iotests/009 b/tests/qemu-iotests/009
73
+{
72
index XXXXXXX..XXXXXXX 100755
74
+ QVirtioSCSIQueues *vs;
73
--- a/tests/qemu-iotests/009
75
+ uint64_t feat;
74
+++ b/tests/qemu-iotests/009
76
+ int i;
75
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
77
+
76
78
+ vs = g_new0(QVirtioSCSIQueues, 1);
77
_supported_fmt generic
79
+
78
_supported_proto generic
80
+ feat = qvirtio_get_features(dev);
79
+_unsupported_imgopts "subformat=streamOptimized"
81
+ if (mask) {
80
82
+ feat &= ~QVIRTIO_F_BAD_FEATURE | mask;
81
83
+ } else {
82
size=6G
84
+ feat &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX));
83
diff --git a/tests/qemu-iotests/010 b/tests/qemu-iotests/010
85
+ }
84
index XXXXXXX..XXXXXXX 100755
86
+ qvirtio_set_features(dev, feat);
85
--- a/tests/qemu-iotests/010
87
+
86
+++ b/tests/qemu-iotests/010
88
+ vs->num_queues = qvirtio_config_readl(dev, 0);
87
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
89
+
88
90
+ for (i = 0; i < vs->num_queues + 2; i++) {
89
_supported_fmt generic
91
+ vs->vq[i] = qvirtqueue_setup(dev, fuzz_qos_alloc, i);
90
_supported_proto generic
92
+ }
91
+_unsupported_imgopts "subformat=streamOptimized"
93
+
92
94
+ qvirtio_set_driver_ok(dev);
93
95
+
94
size=6G
96
+ return vs;
95
diff --git a/tests/qemu-iotests/011 b/tests/qemu-iotests/011
97
+}
96
index XXXXXXX..XXXXXXX 100755
98
+
97
--- a/tests/qemu-iotests/011
99
+static void virtio_scsi_fuzz(QTestState *s, QVirtioSCSIQueues* queues,
98
+++ b/tests/qemu-iotests/011
100
+ const unsigned char *Data, size_t Size)
99
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
101
+{
100
102
+ /*
101
_supported_fmt generic
103
+ * Data is a sequence of random bytes. We split them up into "actions",
102
_supported_proto generic
104
+ * followed by data:
103
+_unsupported_imgopts "subformat=streamOptimized"
105
+ * [vqa][dddddddd][vqa][dddd][vqa][dddddddddddd] ...
104
106
+ * The length of the data is specified by the preceding vqa.length
105
107
+ */
106
size=6G
108
+ typedef struct vq_action {
107
diff --git a/tests/qemu-iotests/017 b/tests/qemu-iotests/017
109
+ uint8_t queue;
108
index XXXXXXX..XXXXXXX 100755
110
+ uint8_t length;
109
--- a/tests/qemu-iotests/017
111
+ uint8_t write;
110
+++ b/tests/qemu-iotests/017
112
+ uint8_t next;
111
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
113
+ uint8_t kick;
112
_supported_fmt qcow qcow2 vmdk qed
114
+ } vq_action;
113
_supported_proto generic
115
+
114
_unsupported_proto vxhs
116
+ /* Keep track of the free head for each queue we interact with */
115
-_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
117
+ bool vq_touched[MAX_NUM_QUEUES + 2] = {0};
116
+_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" \
118
+ uint32_t free_head[MAX_NUM_QUEUES + 2];
117
+ "subformat=streamOptimized"
119
+
118
120
+ QGuestAllocator *t_alloc = fuzz_qos_alloc;
119
TEST_OFFSETS="0 4294967296"
121
+
120
122
+ QVirtioSCSI *scsi = fuzz_qos_obj;
121
diff --git a/tests/qemu-iotests/018 b/tests/qemu-iotests/018
123
+ QVirtioDevice *dev = scsi->vdev;
122
index XXXXXXX..XXXXXXX 100755
124
+ QVirtQueue *q;
123
--- a/tests/qemu-iotests/018
125
+ vq_action vqa;
124
+++ b/tests/qemu-iotests/018
126
+ while (Size >= sizeof(vqa)) {
125
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
127
+ /* Copy the action, so we can normalize length, queue and flags */
126
_supported_fmt qcow qcow2 vmdk qed
128
+ memcpy(&vqa, Data, sizeof(vqa));
127
_supported_proto file
129
+
128
_supported_os Linux
130
+ Data += sizeof(vqa);
129
-_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
131
+ Size -= sizeof(vqa);
130
+_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" \
132
+
131
+ "streamOptimized"
133
+ vqa.queue = vqa.queue % queues->num_queues;
132
134
+ /* Cap length at the number of remaining bytes in data */
133
TEST_OFFSETS="0 4294967296"
135
+ vqa.length = vqa.length >= Size ? Size : vqa.length;
134
136
+ vqa.write = vqa.write & 1;
135
diff --git a/tests/qemu-iotests/019 b/tests/qemu-iotests/019
137
+ vqa.next = vqa.next & 1;
136
index XXXXXXX..XXXXXXX 100755
138
+ vqa.kick = vqa.kick & 1;
137
--- a/tests/qemu-iotests/019
139
+
138
+++ b/tests/qemu-iotests/019
140
+
139
@@ -XXX,XX +XXX,XX @@ _supported_proto file
141
+ q = queues->vq[vqa.queue];
140
_supported_os Linux
142
+
141
_unsupported_imgopts "subformat=monolithicFlat" \
143
+ /* Copy the data into ram, and place it on the virtqueue */
142
"subformat=twoGbMaxExtentFlat" \
144
+ uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
143
- "subformat=twoGbMaxExtentSparse"
145
+ qtest_memwrite(s, req_addr, Data, vqa.length);
144
+ "subformat=twoGbMaxExtentSparse" \
146
+ if (vq_touched[vqa.queue] == 0) {
145
+ "subformat=streamOptimized"
147
+ vq_touched[vqa.queue] = 1;
146
148
+ free_head[vqa.queue] = qvirtqueue_add(s, q, req_addr, vqa.length,
147
TEST_OFFSETS="0 4294967296"
149
+ vqa.write, vqa.next);
148
CLUSTER_SIZE=65536
150
+ } else {
149
diff --git a/tests/qemu-iotests/020 b/tests/qemu-iotests/020
151
+ qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
150
index XXXXXXX..XXXXXXX 100755
152
+ }
151
--- a/tests/qemu-iotests/020
153
+
152
+++ b/tests/qemu-iotests/020
154
+ if (vqa.kick) {
153
@@ -XXX,XX +XXX,XX @@ _supported_fmt qcow qcow2 vmdk qed
155
+ qvirtqueue_kick(s, dev, q, free_head[vqa.queue]);
154
_supported_proto file
156
+ free_head[vqa.queue] = 0;
155
_unsupported_imgopts "subformat=monolithicFlat" \
157
+ }
156
"subformat=twoGbMaxExtentFlat" \
158
+ Data += vqa.length;
157
- "subformat=twoGbMaxExtentSparse"
159
+ Size -= vqa.length;
158
+ "subformat=twoGbMaxExtentSparse" \
160
+ }
159
+ "subformat=streamOptimized"
161
+ /* In the end, kick each queue we interacted with */
160
162
+ for (int i = 0; i < MAX_NUM_QUEUES + 2; i++) {
161
TEST_OFFSETS="0 4294967296"
163
+ if (vq_touched[i]) {
162
164
+ qvirtqueue_kick(s, dev, queues->vq[i], free_head[i]);
163
diff --git a/tests/qemu-iotests/027 b/tests/qemu-iotests/027
165
+ }
164
index XXXXXXX..XXXXXXX 100755
166
+ }
165
--- a/tests/qemu-iotests/027
167
+}
166
+++ b/tests/qemu-iotests/027
168
+
167
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
169
+static void virtio_scsi_fork_fuzz(QTestState *s,
168
170
+ const unsigned char *Data, size_t Size)
169
_supported_fmt vmdk qcow qcow2 qed
171
+{
170
_supported_proto generic
172
+ QVirtioSCSI *scsi = fuzz_qos_obj;
171
+_unsupported_imgopts "subformat=streamOptimized"
173
+ static QVirtioSCSIQueues *queues;
172
174
+ if (!queues) {
173
175
+ queues = qvirtio_scsi_init(scsi->vdev, 0);
174
size=128M
176
+ }
175
diff --git a/tests/qemu-iotests/032 b/tests/qemu-iotests/032
177
+ if (fork() == 0) {
176
index XXXXXXX..XXXXXXX 100755
178
+ virtio_scsi_fuzz(s, queues, Data, Size);
177
--- a/tests/qemu-iotests/032
179
+ flush_events(s);
178
+++ b/tests/qemu-iotests/032
180
+ _Exit(0);
179
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
181
+ } else {
180
# This works for any image format (though unlikely to segfault for raw)
182
+ wait(NULL);
181
_supported_fmt generic
183
+ }
182
_supported_proto generic
184
+}
183
+_unsupported_imgopts "subformat=streamOptimized"
185
+
184
186
+static void virtio_scsi_with_flag_fuzz(QTestState *s,
185
echo
187
+ const unsigned char *Data, size_t Size)
186
echo === Prepare image ===
188
+{
187
diff --git a/tests/qemu-iotests/033 b/tests/qemu-iotests/033
189
+ QVirtioSCSI *scsi = fuzz_qos_obj;
188
index XXXXXXX..XXXXXXX 100755
190
+ static QVirtioSCSIQueues *queues;
189
--- a/tests/qemu-iotests/033
191
+
190
+++ b/tests/qemu-iotests/033
192
+ if (fork() == 0) {
191
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
193
+ if (Size >= sizeof(uint64_t)) {
192
194
+ queues = qvirtio_scsi_init(scsi->vdev, *(uint64_t *)Data);
193
_supported_fmt generic
195
+ virtio_scsi_fuzz(s, queues,
194
_supported_proto generic
196
+ Data + sizeof(uint64_t), Size - sizeof(uint64_t));
195
+_unsupported_imgopts "subformat=streamOptimized"
197
+ flush_events(s);
196
198
+ }
197
199
+ _Exit(0);
198
size=128M
200
+ } else {
199
diff --git a/tests/qemu-iotests/034 b/tests/qemu-iotests/034
201
+ wait(NULL);
200
index XXXXXXX..XXXXXXX 100755
202
+ }
201
--- a/tests/qemu-iotests/034
203
+}
202
+++ b/tests/qemu-iotests/034
204
+
203
@@ -XXX,XX +XXX,XX @@ _supported_proto file
205
+static void virtio_scsi_pre_fuzz(QTestState *s)
204
_supported_os Linux
206
+{
205
_unsupported_imgopts "subformat=monolithicFlat" \
207
+ qos_init_path(s);
206
"subformat=twoGbMaxExtentFlat" \
208
+ counter_shm_init();
207
- "subformat=twoGbMaxExtentSparse"
209
+}
208
+ "subformat=twoGbMaxExtentSparse" \
210
+
209
+ "subformat=streamOptimized"
211
+static void *virtio_scsi_test_setup(GString *cmd_line, void *arg)
210
212
+{
211
CLUSTER_SIZE=4k
213
+ g_string_append(cmd_line,
212
size=128M
214
+ " -drive file=blkdebug::null-co://,"
213
diff --git a/tests/qemu-iotests/037 b/tests/qemu-iotests/037
215
+ "file.image.read-zeroes=on,"
214
index XXXXXXX..XXXXXXX 100755
216
+ "if=none,id=dr1,format=raw,file.align=4k "
215
--- a/tests/qemu-iotests/037
217
+ "-device scsi-hd,drive=dr1,lun=0,scsi-id=1");
216
+++ b/tests/qemu-iotests/037
218
+ return arg;
217
@@ -XXX,XX +XXX,XX @@ _supported_fmt qcow qcow2 vmdk qed
219
+}
218
_supported_proto file
220
+
219
_unsupported_imgopts "subformat=monolithicFlat" \
221
+
220
"subformat=twoGbMaxExtentFlat" \
222
+static void register_virtio_scsi_fuzz_targets(void)
221
- "subformat=twoGbMaxExtentSparse"
223
+{
222
+ "subformat=twoGbMaxExtentSparse" \
224
+ fuzz_add_qos_target(&(FuzzTarget){
223
+ "subformat=streamOptimized"
225
+ .name = "virtio-scsi-fuzz",
224
226
+ .description = "Fuzz the virtio-scsi virtual queues, forking"
225
CLUSTER_SIZE=4k
227
+ "for each fuzz run",
226
size=128M
228
+ .pre_vm_init = &counter_shm_init,
227
diff --git a/tests/qemu-iotests/063 b/tests/qemu-iotests/063
229
+ .pre_fuzz = &virtio_scsi_pre_fuzz,
228
index XXXXXXX..XXXXXXX 100755
230
+ .fuzz = virtio_scsi_fork_fuzz,},
229
--- a/tests/qemu-iotests/063
231
+ "virtio-scsi",
230
+++ b/tests/qemu-iotests/063
232
+ &(QOSGraphTestOptions){.before = virtio_scsi_test_setup}
231
@@ -XXX,XX +XXX,XX @@ _supported_fmt qcow qcow2 vmdk qed raw
233
+ );
232
_supported_proto file
234
+
233
_unsupported_imgopts "subformat=monolithicFlat" \
235
+ fuzz_add_qos_target(&(FuzzTarget){
234
"subformat=twoGbMaxExtentFlat" \
236
+ .name = "virtio-scsi-flags-fuzz",
235
- "subformat=twoGbMaxExtentSparse"
237
+ .description = "Fuzz the virtio-scsi virtual queues, forking"
236
+ "subformat=twoGbMaxExtentSparse" \
238
+ "for each fuzz run (also fuzzes the virtio flags)",
237
+ "subformat=streamOptimized"
239
+ .pre_vm_init = &counter_shm_init,
238
240
+ .pre_fuzz = &virtio_scsi_pre_fuzz,
239
_make_test_img 4M
241
+ .fuzz = virtio_scsi_with_flag_fuzz,},
240
242
+ "virtio-scsi",
241
diff --git a/tests/qemu-iotests/072 b/tests/qemu-iotests/072
243
+ &(QOSGraphTestOptions){.before = virtio_scsi_test_setup}
242
index XXXXXXX..XXXXXXX 100755
244
+ );
243
--- a/tests/qemu-iotests/072
245
+}
244
+++ b/tests/qemu-iotests/072
246
+
245
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
247
+fuzz_target_init(register_virtio_scsi_fuzz_targets);
246
247
_supported_fmt vpc vmdk vhdx vdi qed qcow2 qcow
248
_supported_proto file
249
+_unsupported_imgopts "subformat=streamOptimized"
250
251
IMG_SIZE=64M
252
253
diff --git a/tests/qemu-iotests/105 b/tests/qemu-iotests/105
254
index XXXXXXX..XXXXXXX 100755
255
--- a/tests/qemu-iotests/105
256
+++ b/tests/qemu-iotests/105
257
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
258
_supported_fmt qcow2 vmdk vhdx qed
259
_supported_proto generic
260
_unsupported_imgopts "subformat=twoGbMaxExtentFlat" \
261
- "subformat=twoGbMaxExtentSparse"
262
+ "subformat=twoGbMaxExtentSparse" \
263
+ "subformat=streamOptimized"
264
265
echo
266
echo "creating large image"
267
diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197
268
index XXXXXXX..XXXXXXX 100755
269
--- a/tests/qemu-iotests/197
270
+++ b/tests/qemu-iotests/197
271
@@ -XXX,XX +XXX,XX @@ _supported_fmt generic
272
_supported_proto generic
273
# LUKS support may be possible, but it complicates things.
274
_unsupported_fmt luks
275
+_unsupported_imgopts "subformat=streamOptimized"
276
277
echo
278
echo '=== Copy-on-read ==='
279
diff --git a/tests/qemu-iotests/215 b/tests/qemu-iotests/215
280
index XXXXXXX..XXXXXXX 100755
281
--- a/tests/qemu-iotests/215
282
+++ b/tests/qemu-iotests/215
283
@@ -XXX,XX +XXX,XX @@ _supported_fmt generic
284
_supported_proto generic
285
# LUKS support may be possible, but it complicates things.
286
_unsupported_fmt luks
287
+_unsupported_imgopts "subformat=streamOptimized"
288
289
echo
290
echo '=== Copy-on-read ==='
291
diff --git a/tests/qemu-iotests/251 b/tests/qemu-iotests/251
292
index XXXXXXX..XXXXXXX 100755
293
--- a/tests/qemu-iotests/251
294
+++ b/tests/qemu-iotests/251
295
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
296
_supported_fmt generic
297
_supported_proto file
298
_supported_os Linux
299
+_unsupported_imgopts "subformat=streamOptimized"
300
301
if [ "$IMGOPTSSYNTAX" = "true" ]; then
302
# We use json:{} filenames here, so we cannot work with additional options.
303
--
248
--
304
2.21.0
249
2.24.1
305
250
306
diff view generated by jsdifflib
1
fe646693acc changed qemu-img create's output so that it no longer prints
1
From: Alexander Bulekov <alxndr@bu.edu>
2
single quotes around parameter values. The subformat and adapter_type
3
filters in _filter_img_create() have never been adapted to that change.
4
2
5
Fixes: fe646693acc13ac48b98435d14149ab04dc597bc
3
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
5
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
8
Message-id: 20190815153638.4600-2-mreitz@redhat.com
6
Message-id: 20200220041118.23264-23-alxndr@bu.edu
9
Reviewed-by: John Snow <jsnow@redhat.com>
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
8
---
12
tests/qemu-iotests/059.out | 16 ++++++++--------
9
docs/devel/fuzzing.txt | 116 +++++++++++++++++++++++++++++++++++++++++
13
tests/qemu-iotests/common.filter | 4 ++--
10
1 file changed, 116 insertions(+)
14
2 files changed, 10 insertions(+), 10 deletions(-)
11
create mode 100644 docs/devel/fuzzing.txt
15
12
16
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
13
diff --git a/docs/devel/fuzzing.txt b/docs/devel/fuzzing.txt
17
index XXXXXXX..XXXXXXX 100644
14
new file mode 100644
18
--- a/tests/qemu-iotests/059.out
15
index XXXXXXX..XXXXXXX
19
+++ b/tests/qemu-iotests/059.out
16
--- /dev/null
20
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
17
+++ b/docs/devel/fuzzing.txt
21
qemu-io: can't open device TEST_DIR/t.vmdk: L1 size too big
18
@@ -XXX,XX +XXX,XX @@
22
19
+= Fuzzing =
23
=== Testing monolithicFlat creation and opening ===
20
+
24
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648 subformat=monolithicFlat
21
+== Introduction ==
25
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648
22
+
26
image: TEST_DIR/t.IMGFMT
23
+This document describes the virtual-device fuzzing infrastructure in QEMU and
27
file format: IMGFMT
24
+how to use it to implement additional fuzzers.
28
virtual size: 2 GiB (2147483648 bytes)
25
+
29
26
+== Basics ==
30
=== Testing monolithicFlat with zeroed_grain ===
27
+
31
qemu-img: TEST_DIR/t.IMGFMT: Flat image can't enable zeroed grain
28
+Fuzzing operates by passing inputs to an entry point/target function. The
32
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648 subformat=monolithicFlat
29
+fuzzer tracks the code coverage triggered by the input. Based on these
33
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648
30
+findings, the fuzzer mutates the input and repeats the fuzzing.
34
31
+
35
=== Testing big twoGbMaxExtentFlat ===
32
+To fuzz QEMU, we rely on libfuzzer. Unlike other fuzzers such as AFL, libfuzzer
36
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824000 subformat=twoGbMaxExtentFlat
33
+is an _in-process_ fuzzer. For the developer, this means that it is their
37
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824000
34
+responsibility to ensure that state is reset between fuzzing-runs.
38
image: TEST_DIR/t.vmdk
35
+
39
file format: vmdk
36
+== Building the fuzzers ==
40
virtual size: 0.977 TiB (1073741824000 bytes)
37
+
41
@@ -XXX,XX +XXX,XX @@ Format specific information:
38
+NOTE: If possible, build a 32-bit binary. When forking, the 32-bit fuzzer is
42
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Invalid extent line: RW 12582912 VMFS "dummy.IMGFMT" 1
39
+much faster, since the page-map has a smaller size. This is due to the fact that
43
40
+AddressSanitizer mmaps ~20TB of memory, as part of its detection. This results
44
=== Testing truncated sparse ===
41
+in a large page-map, and a much slower fork().
45
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=107374182400 subformat=monolithicSparse
42
+
46
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=107374182400
43
+To build the fuzzers, install a recent version of clang:
47
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': File truncated, expecting at least 13172736 bytes
44
+Configure with (substitute the clang binaries with the version you installed):
48
45
+
49
=== Converting to streamOptimized from image with small cluster size===
46
+ CC=clang-8 CXX=clang++-8 /path/to/configure --enable-fuzzing
50
@@ -XXX,XX +XXX,XX @@ wrote 512/512 bytes at offset 10240
47
+
51
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
48
+Fuzz targets are built similarly to system/softmmu:
52
49
+
53
=== Testing monolithicFlat with internally generated JSON file name ===
50
+ make i386-softmmu/fuzz
54
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 subformat=monolithicFlat
51
+
55
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
52
+This builds ./i386-softmmu/qemu-fuzz-i386
56
qemu-io: can't open: Cannot use relative extent paths with VMDK descriptor file 'json:{"image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "inject-error.0.event": "read_aio"}'
53
+
57
54
+The first option to this command is: --fuzz_taget=FUZZ_NAME
58
=== Testing version 3 ===
55
+To list all of the available fuzzers run qemu-fuzz-i386 with no arguments.
59
@@ -XXX,XX +XXX,XX @@ read 512/512 bytes at offset 64931328
56
+
60
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
57
+eg:
61
58
+ ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=virtio-net-fork-fuzz
62
=== Testing 4TB monolithicFlat creation and IO ===
59
+
63
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4398046511104 subformat=monolithicFlat
60
+Internally, libfuzzer parses all arguments that do not begin with "--".
64
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4398046511104
61
+Information about these is available by passing -help=1
65
image: TEST_DIR/t.IMGFMT
62
+
66
file format: IMGFMT
63
+Now the only thing left to do is wait for the fuzzer to trigger potential
67
virtual size: 4 TiB (4398046511104 bytes)
64
+crashes.
68
@@ -XXX,XX +XXX,XX @@ read 1024/1024 bytes at offset 966367641600
65
+
69
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
66
+== Adding a new fuzzer ==
70
67
+Coverage over virtual devices can be improved by adding additional fuzzers.
71
=== Testing qemu-img map on extents ===
68
+Fuzzers are kept in tests/qtest/fuzz/ and should be added to
72
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33285996544 subformat=monolithicSparse
69
+tests/qtest/fuzz/Makefile.include
73
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33285996544
70
+
74
wrote 1024/1024 bytes at offset 65024
71
+Fuzzers can rely on both qtest and libqos to communicate with virtual devices.
75
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
72
+
76
wrote 1024/1024 bytes at offset 2147483136
73
+1. Create a new source file. For example ``tests/qtest/fuzz/foo-device-fuzz.c``.
77
@@ -XXX,XX +XXX,XX @@ Offset Length Mapped to File
74
+
78
0 0x20000 0x3f0000 TEST_DIR/t.vmdk
75
+2. Write the fuzzing code using the libqtest/libqos API. See existing fuzzers
79
0x7fff0000 0x20000 0x410000 TEST_DIR/t.vmdk
76
+for reference.
80
0x140000000 0x10000 0x430000 TEST_DIR/t.vmdk
77
+
81
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33285996544 subformat=twoGbMaxExtentSparse
78
+3. Register the fuzzer in ``tests/fuzz/Makefile.include`` by appending the
82
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33285996544
79
+corresponding object to fuzz-obj-y
83
wrote 1024/1024 bytes at offset 65024
80
+
84
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
81
+Fuzzers can be more-or-less thought of as special qtest programs which can
85
wrote 1024/1024 bytes at offset 2147483136
82
+modify the qtest commands and/or qtest command arguments based on inputs
86
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
83
+provided by libfuzzer. Libfuzzer passes a byte array and length. Commonly the
87
index XXXXXXX..XXXXXXX 100644
84
+fuzzer loops over the byte-array interpreting it as a list of qtest commands,
88
--- a/tests/qemu-iotests/common.filter
85
+addresses, or values.
89
+++ b/tests/qemu-iotests/common.filter
86
+
90
@@ -XXX,XX +XXX,XX @@ _filter_img_create()
87
+= Implementation Details =
91
-e "s# compat6=\\(on\\|off\\)##g" \
88
+
92
-e "s# static=\\(on\\|off\\)##g" \
89
+== The Fuzzer's Lifecycle ==
93
-e "s# zeroed_grain=\\(on\\|off\\)##g" \
90
+
94
- -e "s# subformat='[^']*'##g" \
91
+The fuzzer has two entrypoints that libfuzzer calls. libfuzzer provides it's
95
- -e "s# adapter_type='[^']*'##g" \
92
+own main(), which performs some setup, and calls the entrypoints:
96
+ -e "s# subformat=[^ ]*##g" \
93
+
97
+ -e "s# adapter_type=[^ ]*##g" \
94
+LLVMFuzzerInitialize: called prior to fuzzing. Used to initialize all of the
98
-e "s# hwversion=[^ ]*##g" \
95
+necessary state
99
-e "s# lazy_refcounts=\\(on\\|off\\)##g" \
96
+
100
-e "s# block_size=[0-9]\\+##g" \
97
+LLVMFuzzerTestOneInput: called for each fuzzing run. Processes the input and
98
+resets the state at the end of each run.
99
+
100
+In more detail:
101
+
102
+LLVMFuzzerInitialize parses the arguments to the fuzzer (must start with two
103
+dashes, so they are ignored by libfuzzer main()). Currently, the arguments
104
+select the fuzz target. Then, the qtest client is initialized. If the target
105
+requires qos, qgraph is set up and the QOM/LIBQOS modules are initialized.
106
+Then the QGraph is walked and the QEMU cmd_line is determined and saved.
107
+
108
+After this, the vl.c:qemu__main is called to set up the guest. There are
109
+target-specific hooks that can be called before and after qemu_main, for
110
+additional setup(e.g. PCI setup, or VM snapshotting).
111
+
112
+LLVMFuzzerTestOneInput: Uses qtest/qos functions to act based on the fuzz
113
+input. It is also responsible for manually calling the main loop/main_loop_wait
114
+to ensure that bottom halves are executed and any cleanup required before the
115
+next input.
116
+
117
+Since the same process is reused for many fuzzing runs, QEMU state needs to
118
+be reset at the end of each run. There are currently two implemented
119
+options for resetting state:
120
+1. Reboot the guest between runs.
121
+ Pros: Straightforward and fast for simple fuzz targets.
122
+ Cons: Depending on the device, does not reset all device state. If the
123
+ device requires some initialization prior to being ready for fuzzing
124
+ (common for QOS-based targets), this initialization needs to be done after
125
+ each reboot.
126
+ Example target: i440fx-qtest-reboot-fuzz
127
+2. Run each test case in a separate forked process and copy the coverage
128
+ information back to the parent. This is fairly similar to AFL's "deferred"
129
+ fork-server mode [3]
130
+ Pros: Relatively fast. Devices only need to be initialized once. No need
131
+ to do slow reboots or vmloads.
132
+ Cons: Not officially supported by libfuzzer. Does not work well for devices
133
+ that rely on dedicated threads.
134
+ Example target: virtio-net-fork-fuzz
101
--
135
--
102
2.21.0
136
2.24.1
103
137
104
diff view generated by jsdifflib