1
The following changes since commit 9ac5df20f51fabcba0d902025df4bd7ea987c158:
1
The following changes since commit c6a5fc2ac76c5ab709896ee1b0edd33685a67ed1:
2
2
3
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20200221-1' into staging (2020-02-21 16:18:38 +0000)
3
decodetree: Add --output-null for meson testing (2023-05-31 19:56:42 -0700)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
https://github.com/stefanha/qemu.git tags/block-pull-request
7
https://gitlab.com/stefanha/qemu.git tags/block-pull-request
8
8
9
for you to fetch changes up to e5c59355ae9f724777c61c859292ec9db2c8c2ab:
9
for you to fetch changes up to 98b126f5e3228a346c774e569e26689943b401dd:
10
10
11
fuzz: add documentation to docs/devel/ (2020-02-22 08:26:48 +0000)
11
qapi: add '@fdset' feature for BlockdevOptionsVirtioBlkVhostVdpa (2023-06-01 11:08:21 -0400)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Pull request
14
Pull request
15
15
16
This pull request contains a virtio-blk/scsi performance optimization, event
16
- Stefano Garzarella's blkio block driver 'fd' parameter
17
loop scalability improvements, and a qtest-based device fuzzing framework. I
17
- My thread-local blk_io_plug() series
18
am including the fuzzing patches because I have reviewed them and Thomas Huth
19
is currently away on leave.
20
18
21
----------------------------------------------------------------
19
----------------------------------------------------------------
22
20
23
Alexander Bulekov (22):
21
Stefan Hajnoczi (6):
24
softmmu: move vl.c to softmmu/
22
block: add blk_io_plug_call() API
25
softmmu: split off vl.c:main() into main.c
23
block/nvme: convert to blk_io_plug_call() API
26
module: check module wasn't already initialized
24
block/blkio: convert to blk_io_plug_call() API
27
fuzz: add FUZZ_TARGET module type
25
block/io_uring: convert to blk_io_plug_call() API
28
qtest: add qtest_server_send abstraction
26
block/linux-aio: convert to blk_io_plug_call() API
29
libqtest: add a layer of abstraction to send/recv
27
block: remove bdrv_co_io_plug() API
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
28
47
Denis Plotnikov (1):
29
Stefano Garzarella (2):
48
virtio: increase virtqueue size for virtio-scsi and virtio-blk
30
block/blkio: use qemu_open() to support fd passing for virtio-blk
31
qapi: add '@fdset' feature for BlockdevOptionsVirtioBlkVhostVdpa
49
32
50
Paolo Bonzini (1):
33
MAINTAINERS | 1 +
51
rcu_queue: add QSLIST functions
34
qapi/block-core.json | 6 ++
52
35
meson.build | 4 +
53
Stefan Hajnoczi (7):
36
include/block/block-io.h | 3 -
54
aio-posix: avoid reacquiring rcu_read_lock() when polling
37
include/block/block_int-common.h | 11 ---
55
util/async: make bh_aio_poll() O(1)
38
include/block/raw-aio.h | 14 ---
56
aio-posix: fix use after leaving scope in aio_poll()
39
include/sysemu/block-backend-io.h | 13 +--
57
aio-posix: don't pass ns timeout to epoll_wait()
40
block/blkio.c | 96 ++++++++++++------
58
qemu/queue.h: add QLIST_SAFE_REMOVE()
41
block/block-backend.c | 22 -----
59
aio-posix: make AioHandler deletion O(1)
42
block/file-posix.c | 38 -------
60
aio-posix: make AioHandler dispatch O(1) with epoll
43
block/io.c | 37 -------
61
44
block/io_uring.c | 44 ++++-----
62
MAINTAINERS | 11 +-
45
block/linux-aio.c | 41 +++-----
63
Makefile | 15 +-
46
block/nvme.c | 44 +++------
64
Makefile.objs | 2 -
47
block/plug.c | 159 ++++++++++++++++++++++++++++++
65
Makefile.target | 19 ++-
48
hw/block/dataplane/xen-block.c | 8 +-
66
block.c | 5 +-
49
hw/block/virtio-blk.c | 4 +-
67
chardev/spice.c | 4 +-
50
hw/scsi/virtio-scsi.c | 6 +-
68
configure | 39 +++++
51
block/meson.build | 1 +
69
docs/devel/fuzzing.txt | 116 ++++++++++++++
52
block/trace-events | 6 +-
70
exec.c | 12 +-
53
20 files changed, 293 insertions(+), 265 deletions(-)
71
hw/block/virtio-blk.c | 2 +-
54
create mode 100644 block/plug.c
72
hw/core/machine.c | 2 +
73
hw/scsi/virtio-scsi.c | 2 +-
74
include/block/aio.h | 26 ++-
75
include/qemu/module.h | 4 +-
76
include/qemu/queue.h | 32 +++-
77
include/qemu/rcu_queue.h | 47 ++++++
78
include/sysemu/qtest.h | 4 +
79
include/sysemu/sysemu.h | 4 +
80
qtest.c | 31 +++-
81
scripts/checkpatch.pl | 2 +-
82
scripts/get_maintainer.pl | 3 +-
83
softmmu/Makefile.objs | 3 +
84
softmmu/main.c | 53 +++++++
85
vl.c => softmmu/vl.c | 48 +++---
86
tests/Makefile.include | 2 +
87
tests/qtest/Makefile.include | 72 +++++----
88
tests/qtest/fuzz/Makefile.include | 18 +++
89
tests/qtest/fuzz/fork_fuzz.c | 55 +++++++
90
tests/qtest/fuzz/fork_fuzz.h | 23 +++
91
tests/qtest/fuzz/fork_fuzz.ld | 37 +++++
92
tests/qtest/fuzz/fuzz.c | 179 +++++++++++++++++++++
93
tests/qtest/fuzz/fuzz.h | 95 +++++++++++
94
tests/qtest/fuzz/i440fx_fuzz.c | 193 ++++++++++++++++++++++
95
tests/qtest/fuzz/qos_fuzz.c | 234 +++++++++++++++++++++++++++
96
tests/qtest/fuzz/qos_fuzz.h | 33 ++++
97
tests/qtest/fuzz/virtio_net_fuzz.c | 198 +++++++++++++++++++++++
98
tests/qtest/fuzz/virtio_scsi_fuzz.c | 213 +++++++++++++++++++++++++
99
tests/qtest/libqos/i2c.c | 10 +-
100
tests/qtest/libqos/i2c.h | 4 +-
101
tests/qtest/libqos/qos_external.c | 168 ++++++++++++++++++++
102
tests/qtest/libqos/qos_external.h | 28 ++++
103
tests/qtest/libqtest.c | 119 ++++++++++++--
104
tests/qtest/libqtest.h | 4 +
105
tests/qtest/pca9552-test.c | 10 +-
106
tests/qtest/qos-test.c | 132 +---------------
107
tests/test-aio.c | 3 +-
108
tests/test-rcu-list.c | 16 ++
109
tests/test-rcu-slist.c | 2 +
110
util/aio-posix.c | 187 +++++++++++++++-------
111
util/async.c | 237 ++++++++++++++++------------
112
util/module.c | 7 +
113
51 files changed, 2365 insertions(+), 400 deletions(-)
114
create mode 100644 docs/devel/fuzzing.txt
115
create mode 100644 softmmu/Makefile.objs
116
create mode 100644 softmmu/main.c
117
rename vl.c => softmmu/vl.c (99%)
118
create mode 100644 tests/qtest/fuzz/Makefile.include
119
create mode 100644 tests/qtest/fuzz/fork_fuzz.c
120
create mode 100644 tests/qtest/fuzz/fork_fuzz.h
121
create mode 100644 tests/qtest/fuzz/fork_fuzz.ld
122
create mode 100644 tests/qtest/fuzz/fuzz.c
123
create mode 100644 tests/qtest/fuzz/fuzz.h
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
132
55
133
--
56
--
134
2.24.1
57
2.40.1
135
diff view generated by jsdifflib
Deleted patch
1
From: Denis Plotnikov <dplotnikov@virtuozzo.com>
2
1
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
Deleted patch
1
The first rcu_read_lock/unlock() is expensive. Nested calls are cheap.
2
1
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
Deleted patch
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
1
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: Alexander Bulekov <alxndr@bu.edu>
1
Introduce a new API for thread-local blk_io_plug() that does not
2
2
traverse the block graph. The goal is to make blk_io_plug() multi-queue
3
tests/fuzz/fuzz.c serves as the entry point for the virtual-device
3
friendly.
4
fuzzer. Namely, libfuzzer invokes the LLVMFuzzerInitialize and
4
5
LLVMFuzzerTestOneInput functions, both of which are defined in this
5
Instead of having block drivers track whether or not we're in a plugged
6
file. This change adds a "FuzzTarget" struct, along with the
6
section, provide an API that allows them to defer a function call until
7
fuzz_add_target function, which should be used to define new fuzz
7
we're unplugged: blk_io_plug_call(fn, opaque). If blk_io_plug_call() is
8
targets.
8
called multiple times with the same fn/opaque pair, then fn() is only
9
9
called once at the end of the function - resulting in batching.
10
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
10
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
This patch introduces the API and changes blk_io_plug()/blk_io_unplug().
12
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
12
blk_io_plug()/blk_io_unplug() no longer require a BlockBackend argument
13
Message-id: 20200220041118.23264-13-alxndr@bu.edu
13
because the plug state is now thread-local.
14
15
Later patches convert block drivers to blk_io_plug_call() and then we
16
can finally remove .bdrv_co_io_plug() once all block drivers have been
17
converted.
18
19
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
20
Reviewed-by: Eric Blake <eblake@redhat.com>
21
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
22
Acked-by: Kevin Wolf <kwolf@redhat.com>
23
Message-id: 20230530180959.1108766-2-stefanha@redhat.com
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
24
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
---
25
---
16
MAINTAINERS | 8 ++
26
MAINTAINERS | 1 +
17
tests/qtest/fuzz/Makefile.include | 6 +
27
include/sysemu/block-backend-io.h | 13 +--
18
tests/qtest/fuzz/fuzz.c | 179 ++++++++++++++++++++++++++++++
28
block/block-backend.c | 22 -----
19
tests/qtest/fuzz/fuzz.h | 95 ++++++++++++++++
29
block/plug.c | 159 ++++++++++++++++++++++++++++++
20
4 files changed, 288 insertions(+)
30
hw/block/dataplane/xen-block.c | 8 +-
21
create mode 100644 tests/qtest/fuzz/Makefile.include
31
hw/block/virtio-blk.c | 4 +-
22
create mode 100644 tests/qtest/fuzz/fuzz.c
32
hw/scsi/virtio-scsi.c | 6 +-
23
create mode 100644 tests/qtest/fuzz/fuzz.h
33
block/meson.build | 1 +
34
8 files changed, 173 insertions(+), 41 deletions(-)
35
create mode 100644 block/plug.c
24
36
25
diff --git a/MAINTAINERS b/MAINTAINERS
37
diff --git a/MAINTAINERS b/MAINTAINERS
26
index XXXXXXX..XXXXXXX 100644
38
index XXXXXXX..XXXXXXX 100644
27
--- a/MAINTAINERS
39
--- a/MAINTAINERS
28
+++ b/MAINTAINERS
40
+++ b/MAINTAINERS
29
@@ -XXX,XX +XXX,XX @@ F: qtest.c
41
@@ -XXX,XX +XXX,XX @@ F: util/aio-*.c
30
F: accel/qtest.c
42
F: util/aio-*.h
31
F: tests/qtest/
43
F: util/fdmon-*.c
32
44
F: block/io.c
33
+Device Fuzzing
45
+F: block/plug.c
34
+M: Alexander Bulekov <alxndr@bu.edu>
46
F: migration/block*
35
+R: Paolo Bonzini <pbonzini@redhat.com>
47
F: include/block/aio.h
36
+R: Bandan Das <bsd@redhat.com>
48
F: include/block/aio-wait.h
37
+R: Stefan Hajnoczi <stefanha@redhat.com>
49
diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h
38
+S: Maintained
50
index XXXXXXX..XXXXXXX 100644
39
+F: tests/qtest/fuzz/
51
--- a/include/sysemu/block-backend-io.h
40
+
52
+++ b/include/sysemu/block-backend-io.h
41
Register API
53
@@ -XXX,XX +XXX,XX @@ void blk_iostatus_set_err(BlockBackend *blk, int error);
42
M: Alistair Francis <alistair@alistair23.me>
54
int blk_get_max_iov(BlockBackend *blk);
43
S: Maintained
55
int blk_get_max_hw_iov(BlockBackend *blk);
44
diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
56
57
-/*
58
- * blk_io_plug/unplug are thread-local operations. This means that multiple
59
- * IOThreads can simultaneously call plug/unplug, but the caller must ensure
60
- * that each unplug() is called in the same IOThread of the matching plug().
61
- */
62
-void coroutine_fn blk_co_io_plug(BlockBackend *blk);
63
-void co_wrapper blk_io_plug(BlockBackend *blk);
64
-
65
-void coroutine_fn blk_co_io_unplug(BlockBackend *blk);
66
-void co_wrapper blk_io_unplug(BlockBackend *blk);
67
+void blk_io_plug(void);
68
+void blk_io_unplug(void);
69
+void blk_io_plug_call(void (*fn)(void *), void *opaque);
70
71
AioContext *blk_get_aio_context(BlockBackend *blk);
72
BlockAcctStats *blk_get_stats(BlockBackend *blk);
73
diff --git a/block/block-backend.c b/block/block-backend.c
74
index XXXXXXX..XXXXXXX 100644
75
--- a/block/block-backend.c
76
+++ b/block/block-backend.c
77
@@ -XXX,XX +XXX,XX @@ void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify)
78
notifier_list_add(&blk->insert_bs_notifiers, notify);
79
}
80
81
-void coroutine_fn blk_co_io_plug(BlockBackend *blk)
82
-{
83
- BlockDriverState *bs = blk_bs(blk);
84
- IO_CODE();
85
- GRAPH_RDLOCK_GUARD();
86
-
87
- if (bs) {
88
- bdrv_co_io_plug(bs);
89
- }
90
-}
91
-
92
-void coroutine_fn blk_co_io_unplug(BlockBackend *blk)
93
-{
94
- BlockDriverState *bs = blk_bs(blk);
95
- IO_CODE();
96
- GRAPH_RDLOCK_GUARD();
97
-
98
- if (bs) {
99
- bdrv_co_io_unplug(bs);
100
- }
101
-}
102
-
103
BlockAcctStats *blk_get_stats(BlockBackend *blk)
104
{
105
IO_CODE();
106
diff --git a/block/plug.c b/block/plug.c
45
new file mode 100644
107
new file mode 100644
46
index XXXXXXX..XXXXXXX
108
index XXXXXXX..XXXXXXX
47
--- /dev/null
109
--- /dev/null
48
+++ b/tests/qtest/fuzz/Makefile.include
110
+++ b/block/plug.c
49
@@ -XXX,XX +XXX,XX @@
111
@@ -XXX,XX +XXX,XX @@
50
+QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF)
112
+/* SPDX-License-Identifier: GPL-2.0-or-later */
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
+/*
113
+/*
63
+ * fuzzing driver
114
+ * Block I/O plugging
64
+ *
115
+ *
65
+ * Copyright Red Hat Inc., 2019
116
+ * Copyright Red Hat.
66
+ *
117
+ *
67
+ * Authors:
118
+ * This API defers a function call within a blk_io_plug()/blk_io_unplug()
68
+ * Alexander Bulekov <alxndr@bu.edu>
119
+ * section, allowing multiple calls to batch up. This is a performance
69
+ *
120
+ * optimization that is used in the block layer to submit several I/O requests
70
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
121
+ * at once instead of individually:
71
+ * See the COPYING file in the top-level directory.
122
+ *
72
+ *
123
+ * blk_io_plug(); <-- start of plugged region
124
+ * ...
125
+ * blk_io_plug_call(my_func, my_obj); <-- deferred my_func(my_obj) call
126
+ * blk_io_plug_call(my_func, my_obj); <-- another
127
+ * blk_io_plug_call(my_func, my_obj); <-- another
128
+ * ...
129
+ * blk_io_unplug(); <-- end of plugged region, my_func(my_obj) is called once
130
+ *
131
+ * This code is actually generic and not tied to the block layer. If another
132
+ * subsystem needs this functionality, it could be renamed.
73
+ */
133
+ */
74
+
134
+
75
+#include "qemu/osdep.h"
135
+#include "qemu/osdep.h"
76
+
136
+#include "qemu/coroutine-tls.h"
77
+#include <wordexp.h>
137
+#include "qemu/notify.h"
78
+
138
+#include "qemu/thread.h"
79
+#include "sysemu/qtest.h"
139
+#include "sysemu/block-backend.h"
80
+#include "sysemu/runstate.h"
140
+
81
+#include "sysemu/sysemu.h"
141
+/* A function call that has been deferred until unplug() */
82
+#include "qemu/main-loop.h"
142
+typedef struct {
83
+#include "tests/qtest/libqtest.h"
143
+ void (*fn)(void *);
84
+#include "tests/qtest/libqos/qgraph.h"
144
+ void *opaque;
85
+#include "fuzz.h"
145
+} UnplugFn;
86
+
146
+
87
+#define MAX_EVENT_LOOPS 10
147
+/* Per-thread state */
88
+
148
+typedef struct {
89
+typedef struct FuzzTargetState {
149
+ unsigned count; /* how many times has plug() been called? */
90
+ FuzzTarget *target;
150
+ GArray *unplug_fns; /* functions to call at unplug time */
91
+ QSLIST_ENTRY(FuzzTargetState) target_list;
151
+} Plug;
92
+} FuzzTargetState;
152
+
93
+
153
+/* Use get_ptr_plug() to fetch this thread-local value */
94
+typedef QSLIST_HEAD(, FuzzTargetState) FuzzTargetList;
154
+QEMU_DEFINE_STATIC_CO_TLS(Plug, plug);
95
+
155
+
96
+static const char *fuzz_arch = TARGET_NAME;
156
+/* Called at thread cleanup time */
97
+
157
+static void blk_io_plug_atexit(Notifier *n, void *value)
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
+{
158
+{
106
+ int i = MAX_EVENT_LOOPS;
159
+ Plug *plug = get_ptr_plug();
107
+ while (g_main_context_pending(NULL) && i-- > 0) {
160
+ g_array_free(plug->unplug_fns, TRUE);
108
+ main_loop_wait(false);
109
+ }
110
+}
161
+}
111
+
162
+
112
+static QTestState *qtest_setup(void)
163
+/* This won't involve coroutines, so use __thread */
164
+static __thread Notifier blk_io_plug_atexit_notifier;
165
+
166
+/**
167
+ * blk_io_plug_call:
168
+ * @fn: a function pointer to be invoked
169
+ * @opaque: a user-defined argument to @fn()
170
+ *
171
+ * Call @fn(@opaque) immediately if not within a blk_io_plug()/blk_io_unplug()
172
+ * section.
173
+ *
174
+ * Otherwise defer the call until the end of the outermost
175
+ * blk_io_plug()/blk_io_unplug() section in this thread. If the same
176
+ * @fn/@opaque pair has already been deferred, it will only be called once upon
177
+ * blk_io_unplug() so that accumulated calls are batched into a single call.
178
+ *
179
+ * The caller must ensure that @opaque is not freed before @fn() is invoked.
180
+ */
181
+void blk_io_plug_call(void (*fn)(void *), void *opaque)
113
+{
182
+{
114
+ qtest_server_set_send_handler(&qtest_client_inproc_recv, &fuzz_qts);
183
+ Plug *plug = get_ptr_plug();
115
+ return qtest_inproc_init(&fuzz_qts, false, fuzz_arch,
184
+
116
+ &qtest_server_inproc_recv);
185
+ /* Call immediately if we're not plugged */
186
+ if (plug->count == 0) {
187
+ fn(opaque);
188
+ return;
189
+ }
190
+
191
+ GArray *array = plug->unplug_fns;
192
+ if (!array) {
193
+ array = g_array_new(FALSE, FALSE, sizeof(UnplugFn));
194
+ plug->unplug_fns = array;
195
+ blk_io_plug_atexit_notifier.notify = blk_io_plug_atexit;
196
+ qemu_thread_atexit_add(&blk_io_plug_atexit_notifier);
197
+ }
198
+
199
+ UnplugFn *fns = (UnplugFn *)array->data;
200
+ UnplugFn new_fn = {
201
+ .fn = fn,
202
+ .opaque = opaque,
203
+ };
204
+
205
+ /*
206
+ * There won't be many, so do a linear search. If this becomes a bottleneck
207
+ * then a binary search (glib 2.62+) or different data structure could be
208
+ * used.
209
+ */
210
+ for (guint i = 0; i < array->len; i++) {
211
+ if (memcmp(&fns[i], &new_fn, sizeof(new_fn)) == 0) {
212
+ return; /* already exists */
213
+ }
214
+ }
215
+
216
+ g_array_append_val(array, new_fn);
117
+}
217
+}
118
+
218
+
119
+void fuzz_add_target(const FuzzTarget *target)
219
+/**
220
+ * blk_io_plug: Defer blk_io_plug_call() functions until blk_io_unplug()
221
+ *
222
+ * blk_io_plug/unplug are thread-local operations. This means that multiple
223
+ * threads can simultaneously call plug/unplug, but the caller must ensure that
224
+ * each unplug() is called in the same thread of the matching plug().
225
+ *
226
+ * Nesting is supported. blk_io_plug_call() functions are only called at the
227
+ * outermost blk_io_unplug().
228
+ */
229
+void blk_io_plug(void)
120
+{
230
+{
121
+ FuzzTargetState *tmp;
231
+ Plug *plug = get_ptr_plug();
122
+ FuzzTargetState *target_state;
232
+
123
+ if (!fuzz_target_list) {
233
+ assert(plug->count < UINT32_MAX);
124
+ fuzz_target_list = g_new0(FuzzTargetList, 1);
234
+
125
+ }
235
+ plug->count++;
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
+}
236
+}
139
+
237
+
140
+
238
+/**
141
+
239
+ * blk_io_unplug: Run any pending blk_io_plug_call() functions
142
+static void usage(char *path)
240
+ *
241
+ * There must have been a matching blk_io_plug() call in the same thread prior
242
+ * to this blk_io_unplug() call.
243
+ */
244
+void blk_io_unplug(void)
143
+{
245
+{
144
+ printf("Usage: %s --fuzz-target=FUZZ_TARGET [LIBFUZZER ARGUMENTS]\n", path);
246
+ Plug *plug = get_ptr_plug();
145
+ printf("where FUZZ_TARGET is one of:\n");
247
+
146
+ FuzzTargetState *tmp;
248
+ assert(plug->count > 0);
147
+ if (!fuzz_target_list) {
249
+
148
+ fprintf(stderr, "Fuzz target list not initialized\n");
250
+ if (--plug->count > 0) {
149
+ abort();
251
+ return;
150
+ }
252
+ }
151
+ QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
253
+
152
+ printf(" * %s : %s\n", tmp->target->name,
254
+ GArray *array = plug->unplug_fns;
153
+ tmp->target->description);
255
+ if (!array) {
154
+ }
256
+ return;
155
+ exit(0);
257
+ }
258
+
259
+ UnplugFn *fns = (UnplugFn *)array->data;
260
+
261
+ for (guint i = 0; i < array->len; i++) {
262
+ fns[i].fn(fns[i].opaque);
263
+ }
264
+
265
+ /*
266
+ * This resets the array without freeing memory so that appending is cheap
267
+ * in the future.
268
+ */
269
+ g_array_set_size(array, 0);
156
+}
270
+}
157
+
271
diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c
158
+static FuzzTarget *fuzz_get_target(char* name)
272
index XXXXXXX..XXXXXXX 100644
159
+{
273
--- a/hw/block/dataplane/xen-block.c
160
+ FuzzTargetState *tmp;
274
+++ b/hw/block/dataplane/xen-block.c
161
+ if (!fuzz_target_list) {
275
@@ -XXX,XX +XXX,XX @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane)
162
+ fprintf(stderr, "Fuzz target list not initialized\n");
276
* is below us.
163
+ abort();
277
*/
164
+ }
278
if (inflight_atstart > IO_PLUG_THRESHOLD) {
165
+
279
- blk_io_plug(dataplane->blk);
166
+ QSLIST_FOREACH(tmp, fuzz_target_list, target_list) {
280
+ blk_io_plug();
167
+ if (strcmp(tmp->target->name, name) == 0) {
281
}
168
+ return tmp->target;
282
while (rc != rp) {
169
+ }
283
/* pull request from ring */
170
+ }
284
@@ -XXX,XX +XXX,XX @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane)
171
+ return NULL;
285
172
+}
286
if (inflight_atstart > IO_PLUG_THRESHOLD &&
173
+
287
batched >= inflight_atstart) {
174
+
288
- blk_io_unplug(dataplane->blk);
175
+/* Executed for each fuzzing-input */
289
+ blk_io_unplug();
176
+int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size)
290
}
177
+{
291
xen_block_do_aio(request);
178
+ /*
292
if (inflight_atstart > IO_PLUG_THRESHOLD) {
179
+ * Do the pre-fuzz-initialization before the first fuzzing iteration,
293
if (batched >= inflight_atstart) {
180
+ * instead of before the actual fuzz loop. This is needed since libfuzzer
294
- blk_io_plug(dataplane->blk);
181
+ * may fork off additional workers, prior to the fuzzing loop, and if
295
+ blk_io_plug();
182
+ * pre_fuzz() sets up e.g. shared memory, this should be done for the
296
batched = 0;
183
+ * individual worker processes
297
} else {
184
+ */
298
batched++;
185
+ static int pre_fuzz_done;
299
@@ -XXX,XX +XXX,XX @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane)
186
+ if (!pre_fuzz_done && fuzz_target->pre_fuzz) {
300
}
187
+ fuzz_target->pre_fuzz(fuzz_qts);
301
}
188
+ pre_fuzz_done = true;
302
if (inflight_atstart > IO_PLUG_THRESHOLD) {
189
+ }
303
- blk_io_unplug(dataplane->blk);
190
+
304
+ blk_io_unplug();
191
+ fuzz_target->fuzz(fuzz_qts, Data, Size);
305
}
192
+ return 0;
306
193
+}
307
return done_something;
194
+
308
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
195
+/* Executed once, prior to fuzzing */
309
index XXXXXXX..XXXXXXX 100644
196
+int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp)
310
--- a/hw/block/virtio-blk.c
197
+{
311
+++ b/hw/block/virtio-blk.c
198
+
312
@@ -XXX,XX +XXX,XX @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq)
199
+ char *target_name;
313
bool suppress_notifications = virtio_queue_get_notification(vq);
200
+
314
201
+ /* Initialize qgraph and modules */
315
aio_context_acquire(blk_get_aio_context(s->blk));
202
+ qos_graph_init();
316
- blk_io_plug(s->blk);
203
+ module_call_init(MODULE_INIT_FUZZ_TARGET);
317
+ blk_io_plug();
204
+ module_call_init(MODULE_INIT_QOM);
318
205
+ module_call_init(MODULE_INIT_LIBQOS);
319
do {
206
+
320
if (suppress_notifications) {
207
+ if (*argc <= 1) {
321
@@ -XXX,XX +XXX,XX @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq)
208
+ usage(**argv);
322
virtio_blk_submit_multireq(s, &mrb);
209
+ }
323
}
210
+
324
211
+ /* Identify the fuzz target */
325
- blk_io_unplug(s->blk);
212
+ target_name = (*argv)[1];
326
+ blk_io_unplug();
213
+ if (!strstr(target_name, "--fuzz-target=")) {
327
aio_context_release(blk_get_aio_context(s->blk));
214
+ usage(**argv);
328
}
215
+ }
329
216
+
330
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
217
+ target_name += strlen("--fuzz-target=");
331
index XXXXXXX..XXXXXXX 100644
218
+
332
--- a/hw/scsi/virtio-scsi.c
219
+ fuzz_target = fuzz_get_target(target_name);
333
+++ b/hw/scsi/virtio-scsi.c
220
+ if (!fuzz_target) {
334
@@ -XXX,XX +XXX,XX @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
221
+ usage(**argv);
335
return -ENOBUFS;
222
+ }
336
}
223
+
337
scsi_req_ref(req->sreq);
224
+ fuzz_qts = qtest_setup();
338
- blk_io_plug(d->conf.blk);
225
+
339
+ blk_io_plug();
226
+ if (fuzz_target->pre_vm_init) {
340
object_unref(OBJECT(d));
227
+ fuzz_target->pre_vm_init();
341
return 0;
228
+ }
342
}
229
+
343
@@ -XXX,XX +XXX,XX @@ static void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req)
230
+ /* Run QEMU's softmmu main with the fuzz-target dependent arguments */
344
if (scsi_req_enqueue(sreq)) {
231
+ const char *init_cmdline = fuzz_target->get_init_cmdline(fuzz_target);
345
scsi_req_continue(sreq);
232
+
346
}
233
+ /* Split the runcmd into an argv and argc */
347
- blk_io_unplug(sreq->dev->conf.blk);
234
+ wordexp_t result;
348
+ blk_io_unplug();
235
+ wordexp(init_cmdline, &result, 0);
349
scsi_req_unref(sreq);
236
+
350
}
237
+ qemu_init(result.we_wordc, result.we_wordv, NULL);
351
238
+
352
@@ -XXX,XX +XXX,XX @@ static void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq)
239
+ return 0;
353
while (!QTAILQ_EMPTY(&reqs)) {
240
+}
354
req = QTAILQ_FIRST(&reqs);
241
diff --git a/tests/qtest/fuzz/fuzz.h b/tests/qtest/fuzz/fuzz.h
355
QTAILQ_REMOVE(&reqs, req, next);
242
new file mode 100644
356
- blk_io_unplug(req->sreq->dev->conf.blk);
243
index XXXXXXX..XXXXXXX
357
+ blk_io_unplug();
244
--- /dev/null
358
scsi_req_unref(req->sreq);
245
+++ b/tests/qtest/fuzz/fuzz.h
359
virtqueue_detach_element(req->vq, &req->elem, 0);
246
@@ -XXX,XX +XXX,XX @@
360
virtio_scsi_free_req(req);
247
+/*
361
diff --git a/block/meson.build b/block/meson.build
248
+ * fuzzing driver
362
index XXXXXXX..XXXXXXX 100644
249
+ *
363
--- a/block/meson.build
250
+ * Copyright Red Hat Inc., 2019
364
+++ b/block/meson.build
251
+ *
365
@@ -XXX,XX +XXX,XX @@ block_ss.add(files(
252
+ * Authors:
366
'mirror.c',
253
+ * Alexander Bulekov <alxndr@bu.edu>
367
'nbd.c',
254
+ *
368
'null.c',
255
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
369
+ 'plug.c',
256
+ * See the COPYING file in the top-level directory.
370
'qapi.c',
257
+ *
371
'qcow2-bitmap.c',
258
+ */
372
'qcow2-cache.c',
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
--
373
--
343
2.24.1
374
2.40.1
344
diff view generated by jsdifflib
1
File descriptor monitoring is O(1) with epoll(7), but
1
Stop using the .bdrv_co_io_plug() API because it is not multi-queue
2
aio_dispatch_handlers() still scans all AioHandlers instead of
2
block layer friendly. Use the new blk_io_plug_call() API to batch I/O
3
dispatching just those that are ready. This makes aio_poll() O(n) with
3
submission instead.
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
4
20
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Reviewed-by: Sergio Lopez <slp@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
22
Message-id: 20200214171712.541358-6-stefanha@redhat.com
7
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
23
[Fix compilation error on macOS where there is no epoll(87). The
8
Acked-by: Kevin Wolf <kwolf@redhat.com>
24
aio_epoll() prototype was out of date and aio_add_ready_list() needed to
9
Message-id: 20230530180959.1108766-3-stefanha@redhat.com
25
be moved outside the ifdef.
26
--Stefan]
27
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
28
---
11
---
29
util/aio-posix.c | 110 +++++++++++++++++++++++++++++++++--------------
12
block/nvme.c | 44 ++++++++++++--------------------------------
30
1 file changed, 78 insertions(+), 32 deletions(-)
13
block/trace-events | 1 -
14
2 files changed, 12 insertions(+), 33 deletions(-)
31
15
32
diff --git a/util/aio-posix.c b/util/aio-posix.c
16
diff --git a/block/nvme.c b/block/nvme.c
33
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
34
--- a/util/aio-posix.c
18
--- a/block/nvme.c
35
+++ b/util/aio-posix.c
19
+++ b/block/nvme.c
36
@@ -XXX,XX +XXX,XX @@ struct AioHandler
20
@@ -XXX,XX +XXX,XX @@
37
void *opaque;
21
#include "qemu/vfio-helpers.h"
38
bool is_external;
22
#include "block/block-io.h"
39
QLIST_ENTRY(AioHandler) node;
23
#include "block/block_int.h"
40
+ QLIST_ENTRY(AioHandler) node_ready; /* only used during aio_poll() */
24
+#include "sysemu/block-backend.h"
41
QLIST_ENTRY(AioHandler) node_deleted;
25
#include "sysemu/replay.h"
42
};
26
#include "trace.h"
43
27
44
+/* Add a handler to a ready list */
28
@@ -XXX,XX +XXX,XX @@ struct BDRVNVMeState {
45
+static void add_ready_handler(AioHandlerList *ready_list,
29
int blkshift;
46
+ AioHandler *node,
30
47
+ int revents)
31
uint64_t max_transfer;
32
- bool plugged;
33
34
bool supports_write_zeroes;
35
bool supports_discard;
36
@@ -XXX,XX +XXX,XX @@ static void nvme_kick(NVMeQueuePair *q)
37
{
38
BDRVNVMeState *s = q->s;
39
40
- if (s->plugged || !q->need_kick) {
41
+ if (!q->need_kick) {
42
return;
43
}
44
trace_nvme_kick(s, q->index);
45
@@ -XXX,XX +XXX,XX @@ static bool nvme_process_completion(NVMeQueuePair *q)
46
NvmeCqe *c;
47
48
trace_nvme_process_completion(s, q->index, q->inflight);
49
- if (s->plugged) {
50
- trace_nvme_process_completion_queue_plugged(s, q->index);
51
- return false;
52
- }
53
54
/*
55
* Support re-entrancy when a request cb() function invokes aio_poll().
56
@@ -XXX,XX +XXX,XX @@ static void nvme_trace_command(const NvmeCmd *cmd)
57
}
58
}
59
60
+static void nvme_unplug_fn(void *opaque)
48
+{
61
+{
49
+ QLIST_SAFE_REMOVE(node, node_ready); /* remove from nested parent's list */
62
+ NVMeQueuePair *q = opaque;
50
+ node->pfd.revents = revents;
63
+
51
+ QLIST_INSERT_HEAD(ready_list, node, node_ready);
64
+ QEMU_LOCK_GUARD(&q->lock);
65
+ nvme_kick(q);
66
+ nvme_process_completion(q);
52
+}
67
+}
53
+
68
+
54
#ifdef CONFIG_EPOLL_CREATE1
69
static void nvme_submit_command(NVMeQueuePair *q, NVMeRequest *req,
55
70
NvmeCmd *cmd, BlockCompletionFunc cb,
56
/* The fd number threshold to switch to epoll */
71
void *opaque)
57
@@ -XXX,XX +XXX,XX @@ static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new)
72
@@ -XXX,XX +XXX,XX @@ static void nvme_submit_command(NVMeQueuePair *q, NVMeRequest *req,
73
q->sq.tail * NVME_SQ_ENTRY_BYTES, cmd, sizeof(*cmd));
74
q->sq.tail = (q->sq.tail + 1) % NVME_QUEUE_SIZE;
75
q->need_kick++;
76
- nvme_kick(q);
77
- nvme_process_completion(q);
78
+ blk_io_plug_call(nvme_unplug_fn, q);
79
qemu_mutex_unlock(&q->lock);
80
}
81
82
@@ -XXX,XX +XXX,XX @@ static void nvme_attach_aio_context(BlockDriverState *bs,
58
}
83
}
59
}
84
}
60
85
61
-static int aio_epoll(AioContext *ctx, int64_t timeout)
86
-static void coroutine_fn nvme_co_io_plug(BlockDriverState *bs)
62
+static int aio_epoll(AioContext *ctx, AioHandlerList *ready_list,
87
-{
63
+ int64_t timeout)
88
- BDRVNVMeState *s = bs->opaque;
89
- assert(!s->plugged);
90
- s->plugged = true;
91
-}
92
-
93
-static void coroutine_fn nvme_co_io_unplug(BlockDriverState *bs)
94
-{
95
- BDRVNVMeState *s = bs->opaque;
96
- assert(s->plugged);
97
- s->plugged = false;
98
- for (unsigned i = INDEX_IO(0); i < s->queue_count; i++) {
99
- NVMeQueuePair *q = s->queues[i];
100
- qemu_mutex_lock(&q->lock);
101
- nvme_kick(q);
102
- nvme_process_completion(q);
103
- qemu_mutex_unlock(&q->lock);
104
- }
105
-}
106
-
107
static bool nvme_register_buf(BlockDriverState *bs, void *host, size_t size,
108
Error **errp)
64
{
109
{
65
GPollFD pfd = {
110
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_nvme = {
66
.fd = ctx->epollfd,
111
.bdrv_detach_aio_context = nvme_detach_aio_context,
67
@@ -XXX,XX +XXX,XX @@ static int aio_epoll(AioContext *ctx, int64_t timeout)
112
.bdrv_attach_aio_context = nvme_attach_aio_context,
68
}
113
69
for (i = 0; i < ret; i++) {
114
- .bdrv_co_io_plug = nvme_co_io_plug,
70
int ev = events[i].events;
115
- .bdrv_co_io_unplug = nvme_co_io_unplug,
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
-
116
-
126
- /* aio_notify() does not count as progress */
117
.bdrv_register_buf = nvme_register_buf,
127
- if (node->opaque != &ctx->notifier) {
118
.bdrv_unregister_buf = nvme_unregister_buf,
128
- progress = true;
119
};
129
- }
120
diff --git a/block/trace-events b/block/trace-events
130
- }
121
index XXXXXXX..XXXXXXX 100644
131
- if (!QLIST_IS_INSERTED(node, node_deleted) &&
122
--- a/block/trace-events
132
- (revents & (G_IO_OUT | G_IO_ERR)) &&
123
+++ b/block/trace-events
133
- aio_node_check(ctx, node->is_external) &&
124
@@ -XXX,XX +XXX,XX @@ nvme_kick(void *s, unsigned q_index) "s %p q #%u"
134
- node->io_write) {
125
nvme_dma_flush_queue_wait(void *s) "s %p"
135
- node->io_write(node->opaque);
126
nvme_error(int cmd_specific, int sq_head, int sqid, int cid, int status) "cmd_specific %d sq_head %d sqid %d cid %d status 0x%x"
136
+ /* aio_notify() does not count as progress */
127
nvme_process_completion(void *s, unsigned q_index, int inflight) "s %p q #%u inflight %d"
137
+ if (node->opaque != &ctx->notifier) {
128
-nvme_process_completion_queue_plugged(void *s, unsigned q_index) "s %p q #%u"
138
progress = true;
129
nvme_complete_command(void *s, unsigned q_index, int cid) "s %p q #%u cid %d"
139
}
130
nvme_submit_command(void *s, unsigned q_index, int cid) "s %p q #%u cid %d"
140
}
131
nvme_submit_command_raw(int c0, int c1, int c2, int c3, int c4, int c5, int c6, int c7) "%02x %02x %02x %02x %02x %02x %02x %02x"
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
--
132
--
222
2.24.1
133
2.40.1
223
diff view generated by jsdifflib
1
epoll_handler is a stack variable and must not be accessed after it goes
1
Stop using the .bdrv_co_io_plug() API because it is not multi-queue
2
out of scope:
2
block layer friendly. Use the new blk_io_plug_call() API to batch I/O
3
3
submission instead.
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
4
25
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
26
Reviewed-by: Sergio Lopez <slp@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
27
Message-id: 20200214171712.541358-2-stefanha@redhat.com
7
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
8
Acked-by: Kevin Wolf <kwolf@redhat.com>
9
Message-id: 20230530180959.1108766-4-stefanha@redhat.com
28
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
29
---
11
---
30
util/aio-posix.c | 20 ++++++++------------
12
block/blkio.c | 43 ++++++++++++++++++++++++-------------------
31
1 file changed, 8 insertions(+), 12 deletions(-)
13
1 file changed, 24 insertions(+), 19 deletions(-)
32
14
33
diff --git a/util/aio-posix.c b/util/aio-posix.c
15
diff --git a/block/blkio.c b/block/blkio.c
34
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
35
--- a/util/aio-posix.c
17
--- a/block/blkio.c
36
+++ b/util/aio-posix.c
18
+++ b/block/blkio.c
37
@@ -XXX,XX +XXX,XX @@ static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new)
19
@@ -XXX,XX +XXX,XX @@
20
#include "qemu/error-report.h"
21
#include "qapi/qmp/qdict.h"
22
#include "qemu/module.h"
23
+#include "sysemu/block-backend.h"
24
#include "exec/memory.h" /* for ram_block_discard_disable() */
25
26
#include "block/block-io.h"
27
@@ -XXX,XX +XXX,XX @@ static void blkio_detach_aio_context(BlockDriverState *bs)
28
NULL, NULL, NULL);
29
}
30
31
-/* Call with s->blkio_lock held to submit I/O after enqueuing a new request */
32
-static void blkio_submit_io(BlockDriverState *bs)
33
+/*
34
+ * Called by blk_io_unplug() or immediately if not plugged. Called without
35
+ * blkio_lock.
36
+ */
37
+static void blkio_unplug_fn(void *opaque)
38
{
39
- if (qatomic_read(&bs->io_plugged) == 0) {
40
- BDRVBlkioState *s = bs->opaque;
41
+ BDRVBlkioState *s = opaque;
42
43
+ WITH_QEMU_LOCK_GUARD(&s->blkio_lock) {
44
blkioq_do_io(s->blkioq, NULL, 0, 0, NULL);
38
}
45
}
39
}
46
}
40
47
41
-static int aio_epoll(AioContext *ctx, GPollFD *pfds,
48
+/*
42
- unsigned npfd, int64_t timeout)
49
+ * Schedule I/O submission after enqueuing a new request. Called without
43
+static int aio_epoll(AioContext *ctx, int64_t timeout)
50
+ * blkio_lock.
51
+ */
52
+static void blkio_submit_io(BlockDriverState *bs)
53
+{
54
+ BDRVBlkioState *s = bs->opaque;
55
+
56
+ blk_io_plug_call(blkio_unplug_fn, s);
57
+}
58
+
59
static int coroutine_fn
60
blkio_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
44
{
61
{
45
+ GPollFD pfd = {
62
@@ -XXX,XX +XXX,XX @@ blkio_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
46
+ .fd = ctx->epollfd,
63
47
+ .events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR,
64
WITH_QEMU_LOCK_GUARD(&s->blkio_lock) {
48
+ };
65
blkioq_discard(s->blkioq, offset, bytes, &cod, 0);
49
AioHandler *node;
66
- blkio_submit_io(bs);
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
}
67
}
59
if (timeout <= 0 || ret > 0) {
68
60
ret = epoll_wait(ctx->epollfd, events,
69
+ blkio_submit_io(bs);
61
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
70
qemu_coroutine_yield();
62
71
return cod.ret;
63
/* wait until next event */
72
}
64
if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) {
73
@@ -XXX,XX +XXX,XX @@ blkio_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
65
- AioHandler epoll_handler;
74
75
WITH_QEMU_LOCK_GUARD(&s->blkio_lock) {
76
blkioq_readv(s->blkioq, offset, iov, iovcnt, &cod, 0);
77
- blkio_submit_io(bs);
78
}
79
80
+ blkio_submit_io(bs);
81
qemu_coroutine_yield();
82
83
if (use_bounce_buffer) {
84
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkio_co_pwritev(BlockDriverState *bs, int64_t offset,
85
86
WITH_QEMU_LOCK_GUARD(&s->blkio_lock) {
87
blkioq_writev(s->blkioq, offset, iov, iovcnt, &cod, blkio_flags);
88
- blkio_submit_io(bs);
89
}
90
91
+ blkio_submit_io(bs);
92
qemu_coroutine_yield();
93
94
if (use_bounce_buffer) {
95
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkio_co_flush(BlockDriverState *bs)
96
97
WITH_QEMU_LOCK_GUARD(&s->blkio_lock) {
98
blkioq_flush(s->blkioq, &cod, 0);
99
- blkio_submit_io(bs);
100
}
101
102
+ blkio_submit_io(bs);
103
qemu_coroutine_yield();
104
return cod.ret;
105
}
106
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkio_co_pwrite_zeroes(BlockDriverState *bs,
107
108
WITH_QEMU_LOCK_GUARD(&s->blkio_lock) {
109
blkioq_write_zeroes(s->blkioq, offset, bytes, &cod, blkio_flags);
110
- blkio_submit_io(bs);
111
}
112
113
+ blkio_submit_io(bs);
114
qemu_coroutine_yield();
115
return cod.ret;
116
}
117
118
-static void coroutine_fn blkio_co_io_unplug(BlockDriverState *bs)
119
-{
120
- BDRVBlkioState *s = bs->opaque;
66
-
121
-
67
- epoll_handler.pfd.fd = ctx->epollfd;
122
- WITH_QEMU_LOCK_GUARD(&s->blkio_lock) {
68
- epoll_handler.pfd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR;
123
- blkio_submit_io(bs);
69
- npfd = 0;
124
- }
70
- add_pollfd(&epoll_handler);
125
-}
71
- ret = aio_epoll(ctx, pollfds, npfd, timeout);
126
-
72
+ npfd = 0; /* pollfds[] is not being used */
127
typedef enum {
73
+ ret = aio_epoll(ctx, timeout);
128
BMRR_OK,
74
} else {
129
BMRR_SKIP,
75
ret = qemu_poll_ns(pollfds, npfd, timeout);
130
@@ -XXX,XX +XXX,XX @@ static void blkio_refresh_limits(BlockDriverState *bs, Error **errp)
76
}
131
.bdrv_co_pwritev = blkio_co_pwritev, \
132
.bdrv_co_flush_to_disk = blkio_co_flush, \
133
.bdrv_co_pwrite_zeroes = blkio_co_pwrite_zeroes, \
134
- .bdrv_co_io_unplug = blkio_co_io_unplug, \
135
.bdrv_refresh_limits = blkio_refresh_limits, \
136
.bdrv_register_buf = blkio_register_buf, \
137
.bdrv_unregister_buf = blkio_unregister_buf, \
77
--
138
--
78
2.24.1
139
2.40.1
79
diff view generated by jsdifflib
1
The ctx->first_bh list contains all created BHs, including those that
1
Stop using the .bdrv_co_io_plug() API because it is not multi-queue
2
are not scheduled. The list is iterated by the event loop and therefore
2
block layer friendly. Use the new blk_io_plug_call() API to batch I/O
3
has O(n) time complexity with respected to the number of created BHs.
3
submission instead.
4
5
Rewrite BHs so that only scheduled or deleted BHs are enqueued.
6
Only BHs that actually require action will be iterated.
7
8
One semantic change is required: qemu_bh_delete() enqueues the BH and
9
therefore invokes aio_notify(). The
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
4
20
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
22
Message-id: 20200221093951.1414693-1-stefanha@redhat.com
7
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
8
Acked-by: Kevin Wolf <kwolf@redhat.com>
9
Message-id: 20230530180959.1108766-5-stefanha@redhat.com
23
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
24
---
11
---
25
include/block/aio.h | 20 +++-
12
include/block/raw-aio.h | 7 -------
26
tests/test-aio.c | 3 +-
13
block/file-posix.c | 10 ----------
27
util/async.c | 237 ++++++++++++++++++++++++++------------------
14
block/io_uring.c | 44 ++++++++++++++++-------------------------
28
3 files changed, 158 insertions(+), 102 deletions(-)
15
block/trace-events | 5 ++---
16
4 files changed, 19 insertions(+), 47 deletions(-)
29
17
30
diff --git a/include/block/aio.h b/include/block/aio.h
18
diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h
31
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
32
--- a/include/block/aio.h
20
--- a/include/block/raw-aio.h
33
+++ b/include/block/aio.h
21
+++ b/include/block/raw-aio.h
34
@@ -XXX,XX +XXX,XX @@ struct ThreadPool;
22
@@ -XXX,XX +XXX,XX @@ int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset,
35
struct LinuxAioState;
23
QEMUIOVector *qiov, int type);
36
struct LuringState;
24
void luring_detach_aio_context(LuringState *s, AioContext *old_context);
37
25
void luring_attach_aio_context(LuringState *s, AioContext *new_context);
38
+/*
26
-
39
+ * Each aio_bh_poll() call carves off a slice of the BH list, so that newly
27
-/*
40
+ * scheduled BHs are not processed until the next aio_bh_poll() call. All
28
- * luring_io_plug/unplug work in the thread's current AioContext, therefore the
41
+ * active aio_bh_poll() calls chain their slices together in a list, so that
29
- * caller must ensure that they are paired in the same IOThread.
42
+ * nested aio_bh_poll() calls process all scheduled bottom halves.
30
- */
43
+ */
31
-void luring_io_plug(void);
44
+typedef QSLIST_HEAD(, QEMUBH) BHList;
32
-void luring_io_unplug(void);
45
+typedef struct BHListSlice BHListSlice;
33
#endif
46
+struct BHListSlice {
34
47
+ BHList bh_list;
35
#ifdef _WIN32
48
+ QSIMPLEQ_ENTRY(BHListSlice) next;
36
diff --git a/block/file-posix.c b/block/file-posix.c
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
37
index XXXXXXX..XXXXXXX 100644
70
--- a/tests/test-aio.c
38
--- a/block/file-posix.c
71
+++ b/tests/test-aio.c
39
+++ b/block/file-posix.c
72
@@ -XXX,XX +XXX,XX @@ static void test_source_bh_delete_from_cb(void)
40
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn raw_co_io_plug(BlockDriverState *bs)
73
g_assert_cmpint(data1.n, ==, data1.max);
41
laio_io_plug();
74
g_assert(data1.bh == NULL);
42
}
75
43
#endif
76
- g_assert(!g_main_context_iteration(NULL, false));
44
-#ifdef CONFIG_LINUX_IO_URING
77
+ assert(g_main_context_iteration(NULL, false));
45
- if (s->use_linux_io_uring) {
78
+ assert(!g_main_context_iteration(NULL, false));
46
- luring_io_plug();
47
- }
48
-#endif
79
}
49
}
80
50
81
static void test_source_bh_delete_from_cb_many(void)
51
static void coroutine_fn raw_co_io_unplug(BlockDriverState *bs)
82
diff --git a/util/async.c b/util/async.c
52
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn raw_co_io_unplug(BlockDriverState *bs)
53
laio_io_unplug(s->aio_max_batch);
54
}
55
#endif
56
-#ifdef CONFIG_LINUX_IO_URING
57
- if (s->use_linux_io_uring) {
58
- luring_io_unplug();
59
- }
60
-#endif
61
}
62
63
static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs)
64
diff --git a/block/io_uring.c b/block/io_uring.c
83
index XXXXXXX..XXXXXXX 100644
65
index XXXXXXX..XXXXXXX 100644
84
--- a/util/async.c
66
--- a/block/io_uring.c
85
+++ b/util/async.c
67
+++ b/block/io_uring.c
86
@@ -XXX,XX +XXX,XX @@
68
@@ -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"
69
#include "block/raw-aio.h"
92
#include "qemu/coroutine_int.h"
70
#include "qemu/coroutine.h"
71
#include "qapi/error.h"
72
+#include "sysemu/block-backend.h"
93
#include "trace.h"
73
#include "trace.h"
94
@@ -XXX,XX +XXX,XX @@
74
95
/***********************************************************/
75
/* Only used for assertions. */
96
/* bottom halves (can be seen as timers which expire ASAP) */
76
@@ -XXX,XX +XXX,XX @@ typedef struct LuringAIOCB {
97
77
} LuringAIOCB;
98
+/* QEMUBH::flags values */
78
99
+enum {
79
typedef struct LuringQueue {
100
+ /* Already enqueued and waiting for aio_bh_poll() */
80
- int plugged;
101
+ BH_PENDING = (1 << 0),
81
unsigned int in_queue;
102
+
82
unsigned int in_flight;
103
+ /* Invoke the callback */
83
bool blocked;
104
+ BH_SCHEDULED = (1 << 1),
84
@@ -XXX,XX +XXX,XX @@ static void luring_process_completions_and_submit(LuringState *s)
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)
130
+{
131
+ AioContext *ctx = bh->ctx;
132
+ unsigned old_flags;
133
+
134
+ /*
135
+ * The memory barrier implicit in atomic_fetch_or makes sure that:
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) {
155
+ return NULL;
156
+ }
157
+
158
+ QSLIST_REMOVE_HEAD(head, next);
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
{
85
{
174
QEMUBH *bh;
86
luring_process_completions(s);
175
@@ -XXX,XX +XXX,XX @@ void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
87
176
.cb = cb,
88
- if (!s->io_q.plugged && s->io_q.in_queue > 0) {
177
.opaque = opaque,
89
+ if (s->io_q.in_queue > 0) {
178
};
90
ioq_submit(s);
179
- qemu_lockcnt_lock(&ctx->list_lock);
91
}
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
}
92
}
190
93
@@ -XXX,XX +XXX,XX @@ static void qemu_luring_poll_ready(void *opaque)
191
QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
94
static void ioq_init(LuringQueue *io_q)
192
@@ -XXX,XX +XXX,XX @@ QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
95
{
193
.cb = cb,
96
QSIMPLEQ_INIT(&io_q->submit_queue);
194
.opaque = opaque,
97
- io_q->plugged = 0;
195
};
98
io_q->in_queue = 0;
196
- qemu_lockcnt_lock(&ctx->list_lock);
99
io_q->in_flight = 0;
197
- bh->next = ctx->first_bh;
100
io_q->blocked = false;
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
}
101
}
204
102
205
@@ -XXX,XX +XXX,XX @@ void aio_bh_call(QEMUBH *bh)
103
-void luring_io_plug(void)
206
bh->cb(bh->opaque);
104
+static void luring_unplug_fn(void *opaque)
105
{
106
- AioContext *ctx = qemu_get_current_aio_context();
107
- LuringState *s = aio_get_linux_io_uring(ctx);
108
- trace_luring_io_plug(s);
109
- s->io_q.plugged++;
110
-}
111
-
112
-void luring_io_unplug(void)
113
-{
114
- AioContext *ctx = qemu_get_current_aio_context();
115
- LuringState *s = aio_get_linux_io_uring(ctx);
116
- assert(s->io_q.plugged);
117
- trace_luring_io_unplug(s, s->io_q.blocked, s->io_q.plugged,
118
- s->io_q.in_queue, s->io_q.in_flight);
119
- if (--s->io_q.plugged == 0 &&
120
- !s->io_q.blocked && s->io_q.in_queue > 0) {
121
+ LuringState *s = opaque;
122
+ trace_luring_unplug_fn(s, s->io_q.blocked, s->io_q.in_queue,
123
+ s->io_q.in_flight);
124
+ if (!s->io_q.blocked && s->io_q.in_queue > 0) {
125
ioq_submit(s);
126
}
207
}
127
}
208
128
@@ -XXX,XX +XXX,XX @@ static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s,
209
-/* Multiple occurrences of aio_bh_poll cannot be called concurrently.
129
210
- * The count in ctx->list_lock is incremented before the call, and is
130
QSIMPLEQ_INSERT_TAIL(&s->io_q.submit_queue, luringcb, next);
211
- * not affected by the call.
131
s->io_q.in_queue++;
212
- */
132
- trace_luring_do_submit(s, s->io_q.blocked, s->io_q.plugged,
213
+/* Multiple occurrences of aio_bh_poll cannot be called concurrently. */
133
- s->io_q.in_queue, s->io_q.in_flight);
214
int aio_bh_poll(AioContext *ctx)
134
- if (!s->io_q.blocked &&
215
{
135
- (!s->io_q.plugged ||
216
- QEMUBH *bh, **bhp, *next;
136
- s->io_q.in_flight + s->io_q.in_queue >= MAX_ENTRIES)) {
217
- int ret;
137
- ret = ioq_submit(s);
218
- bool deleted = false;
138
- trace_luring_do_submit_done(s, ret);
219
-
139
- return ret;
220
- ret = 0;
140
+ trace_luring_do_submit(s, s->io_q.blocked, s->io_q.in_queue,
221
- for (bh = atomic_rcu_read(&ctx->first_bh); bh; bh = next) {
141
+ s->io_q.in_flight);
222
- next = atomic_rcu_read(&bh->next);
142
+ if (!s->io_q.blocked) {
223
- /* The atomic_xchg is paired with the one in qemu_bh_schedule. The
143
+ if (s->io_q.in_flight + s->io_q.in_queue >= MAX_ENTRIES) {
224
- * implicit memory barrier ensures that the callback sees all writes
144
+ ret = ioq_submit(s);
225
- * done by the scheduling thread. It also ensures that the scheduling
145
+ trace_luring_do_submit_done(s, ret);
226
- * thread sees the zero before bh->cb has run, and thus will call
146
+ return ret;
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
+ }
147
+ }
246
+
148
+
247
+ if ((flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
149
+ blk_io_plug_call(luring_unplug_fn, s);
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
}
150
}
262
151
return 0;
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
}
152
}
283
153
diff --git a/block/trace-events b/block/trace-events
284
void qemu_bh_schedule_idle(QEMUBH *bh)
154
index XXXXXXX..XXXXXXX 100644
285
{
155
--- a/block/trace-events
286
- bh->idle = 1;
156
+++ b/block/trace-events
287
- /* Make sure that idle & any writes needed by the callback are done
157
@@ -XXX,XX +XXX,XX @@ file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "
288
- * before the locations are read in the aio_bh_poll.
158
# io_uring.c
289
- */
159
luring_init_state(void *s, size_t size) "s %p size %zu"
290
- atomic_mb_set(&bh->scheduled, 1);
160
luring_cleanup_state(void *s) "%p freed"
291
+ aio_bh_enqueue(bh, BH_SCHEDULED | BH_IDLE);
161
-luring_io_plug(void *s) "LuringState %p plug"
292
}
162
-luring_io_unplug(void *s, int blocked, int plugged, int queued, int inflight) "LuringState %p blocked %d plugged %d queued %d inflight %d"
293
163
-luring_do_submit(void *s, int blocked, int plugged, int queued, int inflight) "LuringState %p blocked %d plugged %d queued %d inflight %d"
294
void qemu_bh_schedule(QEMUBH *bh)
164
+luring_unplug_fn(void *s, int blocked, int queued, int inflight) "LuringState %p blocked %d queued %d inflight %d"
295
{
165
+luring_do_submit(void *s, int blocked, int queued, int inflight) "LuringState %p blocked %d queued %d inflight %d"
296
- AioContext *ctx;
166
luring_do_submit_done(void *s, int ret) "LuringState %p submitted to kernel %d"
297
-
167
luring_co_submit(void *bs, void *s, void *luringcb, int fd, uint64_t offset, size_t nbytes, int type) "bs %p s %p luringcb %p fd %d offset %" PRId64 " nbytes %zd type %d"
298
- ctx = bh->ctx;
168
luring_process_completion(void *s, void *aiocb, int ret) "LuringState %p luringcb %p ret %d"
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;
366
+ }
367
+
368
+ QSIMPLEQ_FOREACH(s, &ctx->bh_slice_list, next) {
369
+ timeout = aio_compute_bh_timeout(&s->bh_list, timeout);
370
+ if (timeout == 0) {
371
+ return 0;
372
+ }
373
+ }
374
+
375
deadline = timerlistgroup_deadline_ns(&ctx->tlg);
376
if (deadline == 0) {
377
return 0;
378
@@ -XXX,XX +XXX,XX @@ aio_ctx_check(GSource *source)
379
{
380
AioContext *ctx = (AioContext *) source;
381
QEMUBH *bh;
382
+ BHListSlice *s;
383
384
atomic_and(&ctx->notify_me, ~1);
385
aio_notify_accept(ctx);
386
387
- for (bh = ctx->first_bh; bh; bh = bh->next) {
388
- if (bh->scheduled) {
389
+ QSLIST_FOREACH_RCU(bh, &ctx->bh_list, next) {
390
+ if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
391
return true;
392
}
393
}
394
+
395
+ QSIMPLEQ_FOREACH(s, &ctx->bh_slice_list, next) {
396
+ QSLIST_FOREACH_RCU(bh, &s->bh_list, next) {
397
+ if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) {
398
+ return true;
399
+ }
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);
433
}
434
- qemu_lockcnt_unlock(&ctx->list_lock);
435
436
aio_set_event_notifier(ctx, &ctx->notifier, false, NULL, NULL);
437
event_notifier_cleanup(&ctx->notifier);
438
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
439
AioContext *ctx;
440
441
ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext));
442
+ QSLIST_INIT(&ctx->bh_list);
443
+ QSIMPLEQ_INIT(&ctx->bh_slice_list);
444
aio_context_setup(ctx);
445
446
ret = event_notifier_init(&ctx->notifier, false);
447
--
169
--
448
2.24.1
170
2.40.1
449
diff view generated by jsdifflib
Deleted patch
1
Don't pass the nanosecond timeout into epoll_wait(), which expects
2
milliseconds.
3
1
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
Deleted patch
1
QLIST_REMOVE() assumes the element is in a list. It also leaves the
2
element's linked list pointers dangling.
3
1
4
Introduce a safe version of QLIST_REMOVE() and convert open-coded
5
instances of this pattern.
6
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Reviewed-by: Sergio Lopez <slp@redhat.com>
9
Message-id: 20200214171712.541358-4-stefanha@redhat.com
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
12
block.c | 5 +----
13
chardev/spice.c | 4 +---
14
include/qemu/queue.h | 14 ++++++++++++++
15
3 files changed, 16 insertions(+), 7 deletions(-)
16
17
diff --git a/block.c b/block.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/block.c
20
+++ b/block.c
21
@@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
22
23
static void bdrv_detach_child(BdrvChild *child)
24
{
25
- if (child->next.le_prev) {
26
- QLIST_REMOVE(child, next);
27
- child->next.le_prev = NULL;
28
- }
29
+ QLIST_SAFE_REMOVE(child, next);
30
31
bdrv_replace_child(child, NULL);
32
33
diff --git a/chardev/spice.c b/chardev/spice.c
34
index XXXXXXX..XXXXXXX 100644
35
--- a/chardev/spice.c
36
+++ b/chardev/spice.c
37
@@ -XXX,XX +XXX,XX @@ static void char_spice_finalize(Object *obj)
38
39
vmc_unregister_interface(s);
40
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); \
73
--
74
2.24.1
75
diff view generated by jsdifflib
1
It is not necessary to scan all AioHandlers for deletion. Keep a list
1
Stop using the .bdrv_co_io_plug() API because it is not multi-queue
2
of deleted handlers instead of scanning the full list of all handlers.
2
block layer friendly. Use the new blk_io_plug_call() API to batch I/O
3
3
submission instead.
4
The AioHandler->deleted field can be dropped. Let's check if the
4
5
handler has been inserted into the deleted list instead. Add a new
5
Note that a dev_max_batch check is dropped in laio_io_unplug() because
6
QLIST_IS_INSERTED() API for this check.
6
the semantics of unplug_fn() are different from .bdrv_co_unplug():
7
1. unplug_fn() is only called when the last blk_io_unplug() call occurs,
8
not every time blk_io_unplug() is called.
9
2. unplug_fn() is per-thread, not per-BlockDriverState, so there is no
10
way to get per-BlockDriverState fields like dev_max_batch.
11
12
Therefore this condition cannot be moved to laio_unplug_fn(). It is not
13
obvious that this condition affects performance in practice, so I am
14
removing it instead of trying to come up with a more complex mechanism
15
to preserve the condition.
7
16
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Reviewed-by: Sergio Lopez <slp@redhat.com>
18
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Message-id: 20200214171712.541358-5-stefanha@redhat.com
19
Acked-by: Kevin Wolf <kwolf@redhat.com>
20
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
21
Message-id: 20230530180959.1108766-6-stefanha@redhat.com
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
22
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
---
23
---
13
include/block/aio.h | 6 ++++-
24
include/block/raw-aio.h | 7 -------
14
include/qemu/queue.h | 3 +++
25
block/file-posix.c | 28 ----------------------------
15
util/aio-posix.c | 53 +++++++++++++++++++++++++++++---------------
26
block/linux-aio.c | 41 +++++++++++------------------------------
16
3 files changed, 43 insertions(+), 19 deletions(-)
27
3 files changed, 11 insertions(+), 65 deletions(-)
17
28
18
diff --git a/include/block/aio.h b/include/block/aio.h
29
diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h
19
index XXXXXXX..XXXXXXX 100644
30
index XXXXXXX..XXXXXXX 100644
20
--- a/include/block/aio.h
31
--- a/include/block/raw-aio.h
21
+++ b/include/block/aio.h
32
+++ b/include/block/raw-aio.h
22
@@ -XXX,XX +XXX,XX @@ void qemu_aio_unref(void *p);
33
@@ -XXX,XX +XXX,XX @@ int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
23
void qemu_aio_ref(void *p);
34
24
35
void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context);
25
typedef struct AioHandler AioHandler;
36
void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context);
26
+typedef QLIST_HEAD(, AioHandler) AioHandlerList;
37
-
27
typedef void QEMUBHFunc(void *opaque);
38
-/*
28
typedef bool AioPollFn(void *opaque);
39
- * laio_io_plug/unplug work in the thread's current AioContext, therefore the
29
typedef void IOHandler(void *opaque);
40
- * caller must ensure that they are paired in the same IOThread.
30
@@ -XXX,XX +XXX,XX @@ struct AioContext {
41
- */
31
QemuRecMutex lock;
42
-void laio_io_plug(void);
32
43
-void laio_io_unplug(uint64_t dev_max_batch);
33
/* The list of registered AIO handlers. Protected by ctx->list_lock. */
44
#endif
34
- QLIST_HEAD(, AioHandler) aio_handlers;
45
/* io_uring.c - Linux io_uring implementation */
35
+ AioHandlerList aio_handlers;
46
#ifdef CONFIG_LINUX_IO_URING
36
+
47
diff --git a/block/file-posix.c b/block/file-posix.c
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
48
index XXXXXXX..XXXXXXX 100644
44
--- a/include/qemu/queue.h
49
--- a/block/file-posix.c
45
+++ b/include/qemu/queue.h
50
+++ b/block/file-posix.c
46
@@ -XXX,XX +XXX,XX @@ struct { \
51
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset,
47
} \
52
return raw_co_prw(bs, offset, bytes, qiov, QEMU_AIO_WRITE);
48
} while (/*CONSTCOND*/0)
53
}
49
54
50
+/* Is elm in a list? */
55
-static void coroutine_fn raw_co_io_plug(BlockDriverState *bs)
51
+#define QLIST_IS_INSERTED(elm, field) ((elm)->field.le_prev != NULL)
56
-{
52
+
57
- BDRVRawState __attribute__((unused)) *s = bs->opaque;
53
#define QLIST_FOREACH(var, head, field) \
58
-#ifdef CONFIG_LINUX_AIO
54
for ((var) = ((head)->lh_first); \
59
- if (s->use_linux_aio) {
55
(var); \
60
- laio_io_plug();
56
diff --git a/util/aio-posix.c b/util/aio-posix.c
61
- }
62
-#endif
63
-}
64
-
65
-static void coroutine_fn raw_co_io_unplug(BlockDriverState *bs)
66
-{
67
- BDRVRawState __attribute__((unused)) *s = bs->opaque;
68
-#ifdef CONFIG_LINUX_AIO
69
- if (s->use_linux_aio) {
70
- laio_io_unplug(s->aio_max_batch);
71
- }
72
-#endif
73
-}
74
-
75
static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs)
76
{
77
BDRVRawState *s = bs->opaque;
78
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
79
.bdrv_co_copy_range_from = raw_co_copy_range_from,
80
.bdrv_co_copy_range_to = raw_co_copy_range_to,
81
.bdrv_refresh_limits = raw_refresh_limits,
82
- .bdrv_co_io_plug = raw_co_io_plug,
83
- .bdrv_co_io_unplug = raw_co_io_unplug,
84
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
85
86
.bdrv_co_truncate = raw_co_truncate,
87
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_device = {
88
.bdrv_co_copy_range_from = raw_co_copy_range_from,
89
.bdrv_co_copy_range_to = raw_co_copy_range_to,
90
.bdrv_refresh_limits = raw_refresh_limits,
91
- .bdrv_co_io_plug = raw_co_io_plug,
92
- .bdrv_co_io_unplug = raw_co_io_unplug,
93
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
94
95
.bdrv_co_truncate = raw_co_truncate,
96
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
97
.bdrv_co_pwritev = raw_co_pwritev,
98
.bdrv_co_flush_to_disk = raw_co_flush_to_disk,
99
.bdrv_refresh_limits = cdrom_refresh_limits,
100
- .bdrv_co_io_plug = raw_co_io_plug,
101
- .bdrv_co_io_unplug = raw_co_io_unplug,
102
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
103
104
.bdrv_co_truncate = raw_co_truncate,
105
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
106
.bdrv_co_pwritev = raw_co_pwritev,
107
.bdrv_co_flush_to_disk = raw_co_flush_to_disk,
108
.bdrv_refresh_limits = cdrom_refresh_limits,
109
- .bdrv_co_io_plug = raw_co_io_plug,
110
- .bdrv_co_io_unplug = raw_co_io_unplug,
111
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
112
113
.bdrv_co_truncate = raw_co_truncate,
114
diff --git a/block/linux-aio.c b/block/linux-aio.c
57
index XXXXXXX..XXXXXXX 100644
115
index XXXXXXX..XXXXXXX 100644
58
--- a/util/aio-posix.c
116
--- a/block/linux-aio.c
59
+++ b/util/aio-posix.c
117
+++ b/block/linux-aio.c
60
@@ -XXX,XX +XXX,XX @@ struct AioHandler
118
@@ -XXX,XX +XXX,XX @@
61
AioPollFn *io_poll;
119
#include "qemu/event_notifier.h"
62
IOHandler *io_poll_begin;
120
#include "qemu/coroutine.h"
63
IOHandler *io_poll_end;
121
#include "qapi/error.h"
64
- int deleted;
122
+#include "sysemu/block-backend.h"
65
void *opaque;
123
66
bool is_external;
124
/* Only used for assertions. */
67
QLIST_ENTRY(AioHandler) node;
125
#include "qemu/coroutine_int.h"
68
+ QLIST_ENTRY(AioHandler) node_deleted;
126
@@ -XXX,XX +XXX,XX @@ struct qemu_laiocb {
69
};
127
};
70
128
71
#ifdef CONFIG_EPOLL_CREATE1
129
typedef struct {
72
@@ -XXX,XX +XXX,XX @@ static bool aio_epoll_try_enable(AioContext *ctx)
130
- int plugged;
73
131
unsigned int in_queue;
74
QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
132
unsigned int in_flight;
75
int r;
133
bool blocked;
76
- if (node->deleted || !node->pfd.events) {
134
@@ -XXX,XX +XXX,XX @@ static void qemu_laio_process_completions_and_submit(LinuxAioState *s)
77
+ if (QLIST_IS_INSERTED(node, node_deleted) || !node->pfd.events) {
135
{
78
continue;
136
qemu_laio_process_completions(s);
79
}
137
80
event.events = epoll_events_from_pfd(node->pfd.events);
138
- if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) {
81
@@ -XXX,XX +XXX,XX @@ static AioHandler *find_aio_handler(AioContext *ctx, int fd)
139
+ if (!QSIMPLEQ_EMPTY(&s->io_q.pending)) {
82
AioHandler *node;
140
ioq_submit(s);
83
141
}
84
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
142
}
85
- if (node->pfd.fd == fd)
143
@@ -XXX,XX +XXX,XX @@ static void qemu_laio_poll_ready(EventNotifier *opaque)
86
- if (!node->deleted)
144
static void ioq_init(LaioQueue *io_q)
87
+ if (node->pfd.fd == fd) {
145
{
88
+ if (!QLIST_IS_INSERTED(node, node_deleted)) {
146
QSIMPLEQ_INIT(&io_q->pending);
89
return node;
147
- io_q->plugged = 0;
90
+ }
148
io_q->in_queue = 0;
149
io_q->in_flight = 0;
150
io_q->blocked = false;
151
@@ -XXX,XX +XXX,XX @@ static uint64_t laio_max_batch(LinuxAioState *s, uint64_t dev_max_batch)
152
return max_batch;
153
}
154
155
-void laio_io_plug(void)
156
+static void laio_unplug_fn(void *opaque)
157
{
158
- AioContext *ctx = qemu_get_current_aio_context();
159
- LinuxAioState *s = aio_get_linux_aio(ctx);
160
+ LinuxAioState *s = opaque;
161
162
- s->io_q.plugged++;
163
-}
164
-
165
-void laio_io_unplug(uint64_t dev_max_batch)
166
-{
167
- AioContext *ctx = qemu_get_current_aio_context();
168
- LinuxAioState *s = aio_get_linux_aio(ctx);
169
-
170
- assert(s->io_q.plugged);
171
- s->io_q.plugged--;
172
-
173
- /*
174
- * Why max batch checking is performed here:
175
- * Another BDS may have queued requests with a higher dev_max_batch and
176
- * therefore in_queue could now exceed our dev_max_batch. Re-check the max
177
- * batch so we can honor our device's dev_max_batch.
178
- */
179
- if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch) ||
180
- (!s->io_q.plugged &&
181
- !s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending))) {
182
+ if (!s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending)) {
183
ioq_submit(s);
184
}
185
}
186
@@ -XXX,XX +XXX,XX @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
187
188
QSIMPLEQ_INSERT_TAIL(&s->io_q.pending, laiocb, next);
189
s->io_q.in_queue++;
190
- if (!s->io_q.blocked &&
191
- (!s->io_q.plugged ||
192
- s->io_q.in_queue >= laio_max_batch(s, dev_max_batch))) {
193
- ioq_submit(s);
194
+ if (!s->io_q.blocked) {
195
+ if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch)) {
196
+ ioq_submit(s);
197
+ } else {
198
+ blk_io_plug_call(laio_unplug_fn, s);
91
+ }
199
+ }
92
}
200
}
93
201
94
return NULL;
202
return 0;
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;
115
}
116
117
+static void aio_free_deleted_handlers(AioContext *ctx)
118
+{
119
+ AioHandler *node;
120
+
121
+ if (QLIST_EMPTY_RCU(&ctx->deleted_aio_handlers)) {
122
+ return;
123
+ }
124
+ if (!qemu_lockcnt_dec_if_lock(&ctx->list_lock)) {
125
+ return; /* we are nested, let the parent do the freeing */
126
+ }
127
+
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);
135
+}
136
+
137
static bool aio_dispatch_handlers(AioContext *ctx)
138
{
139
AioHandler *node, *tmp;
140
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx)
141
revents = node->pfd.revents & node->pfd.events;
142
node->pfd.revents = 0;
143
144
- if (!node->deleted &&
145
+ if (!QLIST_IS_INSERTED(node, node_deleted) &&
146
(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) &&
147
aio_node_check(ctx, node->is_external) &&
148
node->io_read) {
149
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx)
150
progress = true;
151
}
152
}
153
- if (!node->deleted &&
154
+ if (!QLIST_IS_INSERTED(node, node_deleted) &&
155
(revents & (G_IO_OUT | G_IO_ERR)) &&
156
aio_node_check(ctx, node->is_external) &&
157
node->io_write) {
158
node->io_write(node->opaque);
159
progress = true;
160
}
161
-
162
- if (node->deleted) {
163
- if (qemu_lockcnt_dec_if_lock(&ctx->list_lock)) {
164
- QLIST_REMOVE(node, node);
165
- g_free(node);
166
- qemu_lockcnt_inc_and_unlock(&ctx->list_lock);
167
- }
168
- }
169
}
170
171
return progress;
172
@@ -XXX,XX +XXX,XX @@ void aio_dispatch(AioContext *ctx)
173
qemu_lockcnt_inc(&ctx->list_lock);
174
aio_bh_poll(ctx);
175
aio_dispatch_handlers(ctx);
176
+ aio_free_deleted_handlers(ctx);
177
qemu_lockcnt_dec(&ctx->list_lock);
178
179
timerlistgroup_run_timers(&ctx->tlg);
180
@@ -XXX,XX +XXX,XX @@ static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout)
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);
207
--
203
--
208
2.24.1
204
2.40.1
209
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
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
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
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
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
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
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
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
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
3
qtest_server_send is a function pointer specifying the handler used to
4
transmit data to the qtest client. In the standard configuration, this
5
calls the CharBackend handler, but now it is possible for other types of
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>
15
---
16
include/sysemu/qtest.h | 3 +++
17
qtest.c | 18 ++++++++++++++++--
18
2 files changed, 19 insertions(+), 2 deletions(-)
19
20
diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
21
index XXXXXXX..XXXXXXX 100644
22
--- a/include/sysemu/qtest.h
23
+++ b/include/sysemu/qtest.h
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);
47
}
48
49
-static void do_qtest_send(CharBackend *chr, const char *str, size_t len)
50
+static void qtest_server_char_be_send(void *opaque, const char *str)
51
{
52
+ size_t len = strlen(str);
53
+ CharBackend* chr = (CharBackend *)opaque;
54
qemu_chr_fe_write_all(chr, (uint8_t *)str, len);
55
if (qtest_log_fp && qtest_opened) {
56
fprintf(qtest_log_fp, "%s", str);
57
@@ -XXX,XX +XXX,XX @@ static void do_qtest_send(CharBackend *chr, const char *str, size_t len)
58
59
static void qtest_send(CharBackend *chr, const char *str)
60
{
61
- do_qtest_send(chr, str, strlen(str));
62
+ qtest_server_send(qtest_server_send_opaque, str);
63
}
64
65
static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharBackend *chr,
66
@@ -XXX,XX +XXX,XX @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **
67
qemu_chr_fe_set_echo(&qtest_chr, true);
68
69
inbuf = g_string_new("");
70
+
71
+ if (!qtest_server_send) {
72
+ qtest_server_set_send_handler(qtest_server_char_be_send, &qtest_chr);
73
+ }
74
+}
75
+
76
+void qtest_server_set_send_handler(void (*send)(void*, const char*), void *opaque)
77
+{
78
+ qtest_server_send = send;
79
+ qtest_server_send_opaque = opaque;
80
}
81
82
bool qtest_driver(void)
83
--
84
2.24.1
85
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
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
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
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
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
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
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
3
The names i2c_send and i2c_recv collide with functions defined in
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.
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
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>
14
---
15
tests/qtest/libqos/i2c.c | 10 +++++-----
16
tests/qtest/libqos/i2c.h | 4 ++--
17
tests/qtest/pca9552-test.c | 10 +++++-----
18
3 files changed, 12 insertions(+), 12 deletions(-)
19
20
diff --git a/tests/qtest/libqos/i2c.c b/tests/qtest/libqos/i2c.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/tests/qtest/libqos/i2c.c
23
+++ b/tests/qtest/libqos/i2c.c
24
@@ -XXX,XX +XXX,XX @@
25
#include "libqos/i2c.h"
26
#include "libqtest.h"
27
28
-void i2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len)
29
+void qi2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len)
30
{
31
i2cdev->bus->send(i2cdev->bus, i2cdev->addr, buf, len);
32
}
33
34
-void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
35
+void qi2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
36
{
37
i2cdev->bus->recv(i2cdev->bus, i2cdev->addr, buf, len);
38
}
39
@@ -XXX,XX +XXX,XX @@ void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
40
void i2c_read_block(QI2CDevice *i2cdev, uint8_t reg,
41
uint8_t *buf, uint16_t len)
42
{
43
- i2c_send(i2cdev, &reg, 1);
44
- i2c_recv(i2cdev, buf, len);
45
+ qi2c_send(i2cdev, &reg, 1);
46
+ qi2c_recv(i2cdev, buf, len);
47
}
48
49
void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg,
50
@@ -XXX,XX +XXX,XX @@ void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg,
51
uint8_t *cmd = g_malloc(len + 1);
52
cmd[0] = reg;
53
memcpy(&cmd[1], buf, len);
54
- i2c_send(i2cdev, cmd, len + 1);
55
+ qi2c_send(i2cdev, cmd, len + 1);
56
g_free(cmd);
57
}
58
59
diff --git a/tests/qtest/libqos/i2c.h b/tests/qtest/libqos/i2c.h
60
index XXXXXXX..XXXXXXX 100644
61
--- a/tests/qtest/libqos/i2c.h
62
+++ b/tests/qtest/libqos/i2c.h
63
@@ -XXX,XX +XXX,XX @@ struct QI2CDevice {
64
void *i2c_device_create(void *i2c_bus, QGuestAllocator *alloc, void *addr);
65
void add_qi2c_address(QOSGraphEdgeOptions *opts, QI2CAddress *addr);
66
67
-void i2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len);
68
-void i2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len);
69
+void qi2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len);
70
+void qi2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len);
71
72
void i2c_read_block(QI2CDevice *dev, uint8_t reg,
73
uint8_t *buf, uint16_t len);
74
diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
75
index XXXXXXX..XXXXXXX 100644
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
}
105
106
--
107
2.24.1
108
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
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: Alexander Bulekov <alxndr@bu.edu>
1
No block driver implements .bdrv_co_io_plug() anymore. Get rid of the
2
function pointers.
2
3
3
The moved functions are not specific to qos-test and might be useful
4
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
4
elsewhere. For example the virtual-device fuzzer makes use of them for
5
Reviewed-by: Eric Blake <eblake@redhat.com>
5
qos-assisted fuzz-targets.
6
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
6
7
Acked-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
8
Message-id: 20230530180959.1108766-7-stefanha@redhat.com
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
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>
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
10
---
14
tests/qtest/Makefile.include | 1 +
11
include/block/block-io.h | 3 ---
15
tests/qtest/libqos/qos_external.c | 168 ++++++++++++++++++++++++++++++
12
include/block/block_int-common.h | 11 ----------
16
tests/qtest/libqos/qos_external.h | 28 +++++
13
block/io.c | 37 --------------------------------
17
tests/qtest/qos-test.c | 132 +----------------------
14
3 files changed, 51 deletions(-)
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
21
15
22
diff --git a/tests/qtest/Makefile.include b/tests/qtest/Makefile.include
16
diff --git a/include/block/block-io.h b/include/block/block-io.h
23
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
24
--- a/tests/qtest/Makefile.include
18
--- a/include/block/block-io.h
25
+++ b/tests/qtest/Makefile.include
19
+++ b/include/block/block-io.h
26
@@ -XXX,XX +XXX,XX @@ libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/qtest/libqos/u
20
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx);
27
# qos devices:
21
28
libqos-obj-y = $(libqgraph-obj-y)
22
AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c);
29
libqos-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
23
30
+libqos-obj-y += tests/qtest/libqos/qos_external.o
24
-void coroutine_fn GRAPH_RDLOCK bdrv_co_io_plug(BlockDriverState *bs);
31
libqos-obj-y += tests/qtest/libqos/e1000e.o
25
-void coroutine_fn GRAPH_RDLOCK bdrv_co_io_unplug(BlockDriverState *bs);
32
libqos-obj-y += tests/qtest/libqos/i2c.o
26
-
33
libqos-obj-y += tests/qtest/libqos/i2c-imx.o
27
bool coroutine_fn GRAPH_RDLOCK
34
diff --git a/tests/qtest/libqos/qos_external.c b/tests/qtest/libqos/qos_external.c
28
bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
35
new file mode 100644
29
uint32_t granularity, Error **errp);
36
index XXXXXXX..XXXXXXX
30
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
37
--- /dev/null
38
+++ b/tests/qtest/libqos/qos_external.c
39
@@ -XXX,XX +XXX,XX @@
40
+/*
41
+ * libqos driver framework
42
+ *
43
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
44
+ *
45
+ * This library is free software; you can redistribute it and/or
46
+ * modify it under the terms of the GNU Lesser General Public
47
+ * License version 2 as published by the Free Software Foundation.
48
+ *
49
+ * This library is distributed in the hope that it will be useful,
50
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
51
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
52
+ * Lesser General Public License for more details.
53
+ *
54
+ * You should have received a copy of the GNU Lesser General Public
55
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
56
+ */
57
+
58
+#include "qemu/osdep.h"
59
+#include <getopt.h>
60
+#include "libqtest.h"
61
+#include "qapi/qmp/qdict.h"
62
+#include "qapi/qmp/qbool.h"
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
31
index XXXXXXX..XXXXXXX 100644
244
--- a/tests/qtest/qos-test.c
32
--- a/include/block/block_int-common.h
245
+++ b/tests/qtest/qos-test.c
33
+++ b/include/block/block_int-common.h
246
@@ -XXX,XX +XXX,XX @@
34
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
247
#include "libqos/malloc.h"
35
void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_debug_event)(
248
#include "libqos/qgraph.h"
36
BlockDriverState *bs, BlkdebugEvent event);
249
#include "libqos/qgraph_internal.h"
37
250
+#include "libqos/qos_external.h"
38
- /* io queue for linux-aio */
251
39
- void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_io_plug)(BlockDriverState *bs);
252
static char *old_path;
40
- void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_io_unplug)(
253
41
- BlockDriverState *bs);
254
-static void apply_to_node(const char *name, bool is_machine, bool is_abstract)
42
-
43
bool (*bdrv_supports_persistent_dirty_bitmap)(BlockDriverState *bs);
44
45
bool coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_can_store_new_dirty_bitmap)(
46
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
47
unsigned int in_flight;
48
unsigned int serialising_in_flight;
49
50
- /*
51
- * counter for nested bdrv_io_plug.
52
- * Accessed with atomic ops.
53
- */
54
- unsigned io_plugged;
55
-
56
/* do we need to tell the quest if we have a volatile write cache? */
57
int enable_write_cache;
58
59
diff --git a/block/io.c b/block/io.c
60
index XXXXXXX..XXXXXXX 100644
61
--- a/block/io.c
62
+++ b/block/io.c
63
@@ -XXX,XX +XXX,XX @@ void *qemu_try_blockalign0(BlockDriverState *bs, size_t size)
64
return mem;
65
}
66
67
-void coroutine_fn bdrv_co_io_plug(BlockDriverState *bs)
255
-{
68
-{
256
- char *machine_name = NULL;
69
- BdrvChild *child;
257
- if (is_machine) {
70
- IO_CODE();
258
- const char *arch = qtest_get_arch();
71
- assert_bdrv_graph_readable();
259
- machine_name = g_strconcat(arch, "/", name, NULL);
72
-
260
- name = machine_name;
73
- QLIST_FOREACH(child, &bs->children, next) {
74
- bdrv_co_io_plug(child->bs);
261
- }
75
- }
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
-
76
-
287
- for (p = qlist_first(list); p; p = qlist_next(p)) {
77
- if (qatomic_fetch_inc(&bs->io_plugged) == 0) {
288
- minfo = qobject_to(QDict, qlist_entry_obj(p));
78
- BlockDriver *drv = bs->drv;
289
- qobj = qdict_get(minfo, "name");
79
- if (drv && drv->bdrv_co_io_plug) {
290
- qstr = qobject_to(QString, qobj);
80
- drv->bdrv_co_io_plug(bs);
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
- }
81
- }
308
- }
82
- }
309
-}
83
-}
310
84
-
311
/**
85
-void coroutine_fn bdrv_co_io_unplug(BlockDriverState *bs)
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
-{
86
-{
319
- return obj->get_driver(obj, "memory");
87
- BdrvChild *child;
320
-}
88
- IO_CODE();
321
89
- assert_bdrv_graph_readable();
322
static void restart_qemu_or_continue(char *path)
323
{
324
@@ -XXX,XX +XXX,XX @@ void qos_invalidate_command_line(void)
325
old_path = NULL;
326
}
327
328
-/**
329
- * allocate_objects(): given an array of nodes @arg,
330
- * walks the path invoking all constructors and
331
- * passing the corresponding parameter in order to
332
- * continue the objects allocation.
333
- * Once the test is reached, return the object it consumes.
334
- *
335
- * Since the machine and QEDGE_CONSUMED_BY nodes allocate
336
- * memory in the constructor, g_test_queue_destroy is used so
337
- * that after execution they can be safely free'd. (The test's
338
- * ->before callback is also welcome to use g_test_queue_destroy).
339
- *
340
- * Note: as specified in walk_path() too, @arg is an array of
341
- * char *, where arg[0] is a pointer to the command line
342
- * string that will be used to properly start QEMU when executing
343
- * the test, and the remaining elements represent the actual objects
344
- * that will be allocated.
345
- */
346
-static void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc)
347
-{
348
- int current = 0;
349
- QGuestAllocator *alloc;
350
- QOSGraphObject *parent = NULL;
351
- QOSGraphEdge *edge;
352
- QOSGraphNode *node;
353
- void *edge_arg;
354
- void *obj;
355
-
90
-
356
- node = qos_graph_get_node(path[current]);
91
- assert(bs->io_plugged);
357
- g_assert(node->type == QNODE_MACHINE);
92
- if (qatomic_fetch_dec(&bs->io_plugged) == 1) {
358
-
93
- BlockDriver *drv = bs->drv;
359
- obj = qos_machine_new(node, qts);
94
- if (drv && drv->bdrv_co_io_unplug) {
360
- qos_object_queue_destroy(obj);
95
- drv->bdrv_co_io_unplug(bs);
361
-
96
- }
362
- alloc = get_machine_allocator(obj);
363
- if (p_alloc) {
364
- *p_alloc = alloc;
365
- }
97
- }
366
-
98
-
367
- for (;;) {
99
- QLIST_FOREACH(child, &bs->children, next) {
368
- if (node->type != QNODE_INTERFACE) {
100
- bdrv_co_io_unplug(child->bs);
369
- qos_object_start_hw(obj);
370
- parent = obj;
371
- }
372
-
373
- /* follow edge and get object for next node constructor */
374
- current++;
375
- edge = qos_graph_get_edge(path[current - 1], path[current]);
376
- node = qos_graph_get_node(path[current]);
377
-
378
- if (node->type == QNODE_TEST) {
379
- g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY);
380
- return obj;
381
- }
382
-
383
- switch (qos_graph_edge_get_type(edge)) {
384
- case QEDGE_PRODUCES:
385
- obj = parent->get_driver(parent, path[current]);
386
- break;
387
-
388
- case QEDGE_CONSUMED_BY:
389
- edge_arg = qos_graph_edge_get_arg(edge);
390
- obj = qos_driver_new(node, obj, alloc, edge_arg);
391
- qos_object_queue_destroy(obj);
392
- break;
393
-
394
- case QEDGE_CONTAINS:
395
- obj = parent->get_device(parent, path[current]);
396
- break;
397
- }
398
- }
101
- }
399
-}
102
-}
400
103
-
401
/* The argument to run_one_test, which is the test function that is registered
104
/* Helper that undoes bdrv_register_buf() when it fails partway through */
402
* with GTest, is a vector of strings. The first item is the initial command
105
static void GRAPH_RDLOCK
106
bdrv_register_buf_rollback(BlockDriverState *bs, void *host, size_t size,
403
--
107
--
404
2.24.1
108
2.40.1
405
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
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
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
3
The qtest-based fuzzer makes use of forking to reset-state between
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
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
---
13
softmmu/vl.c | 12 +++++++++++-
14
1 file changed, 11 insertions(+), 1 deletion(-)
15
16
diff --git a/softmmu/vl.c b/softmmu/vl.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/softmmu/vl.c
19
+++ b/softmmu/vl.c
20
@@ -XXX,XX +XXX,XX @@ void qemu_init(int argc, char **argv, char **envp)
21
set_memory_options(&ram_slots, &maxram_size, machine_class);
22
23
os_daemonize();
24
- rcu_disable_atfork();
25
+
26
+ /*
27
+ * If QTest is enabled, keep the rcu_atfork enabled, since system processes
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: ");
39
--
40
2.24.1
41
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
3
fork() is a simple way to ensure that state does not leak in between
4
fuzzing runs. Unfortunately, the fuzzer mutation engine relies on
5
bitmaps which contain coverage information for each fuzzing run, and
6
these bitmaps should be copied from the child to the parent(where the
7
mutation occurs). These bitmaps are created through compile-time
8
instrumentation and they are not shared with fork()-ed processes, by
9
default. To address this, we create a shared memory region, adjust its
10
size and map it _over_ the counter region. Furthermore, libfuzzer
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.
15
16
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
17
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
18
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
19
Message-id: 20200220041118.23264-16-alxndr@bu.edu
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
30
31
diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
32
index XXXXXXX..XXXXXXX 100644
33
--- a/tests/qtest/fuzz/Makefile.include
34
+++ b/tests/qtest/fuzz/Makefile.include
35
@@ -XXX,XX +XXX,XX @@ QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF)
36
37
fuzz-obj-y += tests/qtest/libqtest.o
38
fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
39
+fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
40
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)
70
+{
71
+ char *shm_path = g_strdup_printf("/qemu-fuzz-cntrs.%d", getpid());
72
+ int fd = shm_open(shm_path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
73
+ g_free(shm_path);
74
+
75
+ if (fd == -1) {
76
+ perror("Error: ");
77
+ exit(1);
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
+ }
104
+}
105
+
106
+
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;
179
--
180
2.24.1
181
diff view generated by jsdifflib
1
From: Alexander Bulekov <alxndr@bu.edu>
1
From: Stefano Garzarella <sgarzare@redhat.com>
2
2
3
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
3
Some virtio-blk drivers (e.g. virtio-blk-vhost-vdpa) supports the fd
4
passing. Let's expose this to the user, so the management layer
5
can pass the file descriptor of an already opened path.
6
7
If the libblkio virtio-blk driver supports fd passing, let's always
8
use qemu_open() to open the `path`, so we can handle fd passing
9
from the management layer through the "/dev/fdset/N" special path.
10
4
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
12
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
6
Message-id: 20200220041118.23264-17-alxndr@bu.edu
13
Message-id: 20230530071941.8954-2-sgarzare@redhat.com
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
---
15
---
9
tests/qtest/fuzz/Makefile.include | 2 +
16
block/blkio.c | 53 ++++++++++++++++++++++++++++++++++++++++++---------
10
tests/qtest/fuzz/qos_fuzz.c | 234 ++++++++++++++++++++++++++++++
17
1 file changed, 44 insertions(+), 9 deletions(-)
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
15
18
16
diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
19
diff --git a/block/blkio.c b/block/blkio.c
17
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
18
--- a/tests/qtest/fuzz/Makefile.include
21
--- a/block/blkio.c
19
+++ b/tests/qtest/fuzz/Makefile.include
22
+++ b/block/blkio.c
20
@@ -XXX,XX +XXX,XX @@
23
@@ -XXX,XX +XXX,XX @@ static int blkio_virtio_blk_common_open(BlockDriverState *bs,
21
QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF)
24
{
22
25
const char *path = qdict_get_try_str(options, "path");
23
fuzz-obj-y += tests/qtest/libqtest.o
26
BDRVBlkioState *s = bs->opaque;
24
+fuzz-obj-y += $(libqos-obj-y)
27
- int ret;
25
fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
28
+ bool fd_supported = false;
26
fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
29
+ int fd, ret;
27
+fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
30
28
31
if (!path) {
29
FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
32
error_setg(errp, "missing 'path' option");
30
33
return -EINVAL;
31
diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c
34
}
32
new file mode 100644
35
33
index XXXXXXX..XXXXXXX
36
- ret = blkio_set_str(s->blkio, "path", path);
34
--- /dev/null
37
- qdict_del(options, "path");
35
+++ b/tests/qtest/fuzz/qos_fuzz.c
38
- if (ret < 0) {
36
@@ -XXX,XX +XXX,XX @@
39
- error_setg_errno(errp, -ret, "failed to set path: %s",
37
+/*
40
- blkio_get_error_msg());
38
+ * QOS-assisted fuzzing helpers
41
- return ret;
39
+ *
42
- }
40
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
43
-
41
+ *
44
if (!(flags & BDRV_O_NOCACHE)) {
42
+ * This library is free software; you can redistribute it and/or
45
error_setg(errp, "cache.direct=off is not supported");
43
+ * modify it under the terms of the GNU Lesser General Public
46
return -EINVAL;
44
+ * License version 2 as published by the Free Software Foundation.
47
}
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
+
48
+
55
+#include "qemu/osdep.h"
49
+ if (blkio_get_int(s->blkio, "fd", &fd) == 0) {
56
+#include "qemu/units.h"
50
+ fd_supported = true;
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
+ }
51
+ }
135
+
52
+
136
+ /* Before test */
53
+ /*
137
+ current_path = path;
54
+ * If the libblkio driver supports fd passing, let's always use qemu_open()
138
+ test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]);
55
+ * to open the `path`, so we can handle fd passing from the management
139
+ test_arg = test_node->u.test.arg;
56
+ * layer through the "/dev/fdset/N" special path.
140
+ if (test_node->u.test.before) {
57
+ */
141
+ test_arg = test_node->u.test.before(cmd_line, test_arg);
58
+ if (fd_supported) {
142
+ }
59
+ int open_flags;
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
+
60
+
149
+/*
61
+ if (flags & BDRV_O_RDWR) {
150
+ * This function is largely a copy of qos-test.c:walk_path. Since walk_path
62
+ open_flags = O_RDWR;
151
+ * is itself a callback, its a little annoying to add another argument/layer of
63
+ } else {
152
+ * indirection
64
+ open_flags = O_RDONLY;
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
+ }
65
+ }
184
+
66
+
185
+ node_name = qos_graph_edge_get_dest(path->path_edge);
67
+ fd = qemu_open(path, open_flags, errp);
186
+
68
+ if (fd < 0) {
187
+ /* append node command line + previous edge command line */
69
+ return -EINVAL;
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
+ }
70
+ }
193
+
71
+
194
+ path_vec[path_vec_size++] = qos_graph_edge_get_name(path->path_edge);
72
+ ret = blkio_set_int(s->blkio, "fd", fd);
195
+ /* detect if edge has command line args */
73
+ if (ret < 0) {
196
+ after_cmd = qos_graph_edge_get_after_cmd_line(path->path_edge);
74
+ error_setg_errno(errp, -ret, "failed to set fd: %s",
197
+ after_device = qos_graph_edge_get_extra_device_opts(path->path_edge);
75
+ blkio_get_error_msg());
198
+ before_cmd = qos_graph_edge_get_before_cmd_line(path->path_edge);
76
+ qemu_close(fd);
199
+ edge = qos_graph_get_edge(path->name, node_name);
77
+ return ret;
200
+ etype = qos_graph_edge_get_type(edge);
201
+
202
+ if (before_cmd) {
203
+ g_string_append(cmd_line, before_cmd);
204
+ }
78
+ }
205
+ if (after_cmd) {
79
+ } else {
206
+ g_string_append(cmd_line2, after_cmd);
80
+ ret = blkio_set_str(s->blkio, "path", path);
207
+ }
81
+ if (ret < 0) {
208
+ if (after_device) {
82
+ error_setg_errno(errp, -ret, "failed to set path: %s",
209
+ g_string_append(after_device_str, after_device);
83
+ blkio_get_error_msg());
84
+ return ret;
210
+ }
85
+ }
211
+ }
86
+ }
212
+
87
+
213
+ path_vec[path_vec_size++] = NULL;
88
+ qdict_del(options, "path");
214
+ g_string_append(cmd_line, after_device_str->str);
215
+ g_string_free(after_device_str, true);
216
+
89
+
217
+ g_string_append(cmd_line, cmd_line2->str);
90
return 0;
218
+ g_string_free(cmd_line2, true);
91
}
219
+
92
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
310
--
93
--
311
2.24.1
94
2.40.1
312
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
3
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
4
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
Message-id: 20200220041118.23264-18-alxndr@bu.edu
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
---
9
Makefile | 15 ++++++++++++++-
10
Makefile.target | 16 ++++++++++++++++
11
2 files changed, 30 insertions(+), 1 deletion(-)
12
13
diff --git a/Makefile b/Makefile
14
index XXXXXXX..XXXXXXX 100644
15
--- a/Makefile
16
+++ b/Makefile
17
@@ -XXX,XX +XXX,XX @@ config-host.h-timestamp: config-host.mak
18
qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
19
    $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
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)
38
+
39
.PHONY: $(TARGET_DIRS_RULES)
40
# The $(TARGET_DIRS_RULES) are of the form SUBDIR/GOAL, so that
41
# $(dir $@) yields the sub-directory, and $(notdir $@) yields the sub-goal
42
@@ -XXX,XX +XXX,XX @@ subdir-slirp: slirp/all
43
$(filter %/all, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
44
    $(qom-obj-y)
45
46
+$(filter %/fuzz, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
47
+    $(qom-obj-y) $(crypto-user-obj-$(CONFIG_USER_ONLY))
48
+
49
ROM_DIRS = $(addprefix pc-bios/, $(ROMS))
50
ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS)))
51
# Only keep -O and -g cflags
52
@@ -XXX,XX +XXX,XX @@ $(ROM_DIRS_RULES):
53
54
.PHONY: recurse-all recurse-clean recurse-install
55
recurse-all: $(addsuffix /all, $(TARGET_DIRS) $(ROM_DIRS))
56
+recurse-fuzz: $(addsuffix /fuzz, $(TARGET_DIRS) $(ROM_DIRS))
57
recurse-clean: $(addsuffix /clean, $(TARGET_DIRS) $(ROM_DIRS))
58
recurse-install: $(addsuffix /install, $(TARGET_DIRS))
59
$(addsuffix /install, $(TARGET_DIRS)): all
60
diff --git a/Makefile.target b/Makefile.target
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
71
+
72
+fuzz: fuzz-vars
73
+fuzz-vars: QEMU_CFLAGS := $(FUZZ_CFLAGS) $(QEMU_CFLAGS)
74
+fuzz-vars: QEMU_LDFLAGS := $(FUZZ_LDFLAGS) $(QEMU_LDFLAGS)
75
+fuzz-vars: $(QEMU_PROG_FUZZ)
76
+dummy := $(call unnest-vars,, fuzz-obj-y)
77
+
78
+
79
+$(QEMU_PROG_FUZZ): config-devices.mak $(all-obj-y) $(COMMON_LDADDS) $(fuzz-obj-y)
80
+    $(call LINK, $(filter-out %.mak, $^))
81
+
82
+endif
83
+
84
install: all
85
ifneq ($(PROGS),)
86
    $(call install-prog,$(PROGS),$(DESTDIR)$(bindir))
87
--
88
2.24.1
89
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
3
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
4
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
6
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
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(+)
12
13
diff --git a/configure b/configure
14
index XXXXXXX..XXXXXXX 100755
15
--- a/configure
16
+++ b/configure
17
@@ -XXX,XX +XXX,XX @@ debug_mutex="no"
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
54
fi
55
56
+##########################################
57
+# checks for fuzzer
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
63
+fi
64
+
65
##########################################
66
# check for libpmem
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
105
--
106
2.24.1
107
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
3
These three targets should simply fuzz reads/writes to a couple ioports,
4
but they mostly serve as examples of different ways to write targets.
5
They demonstrate using qtest and qos for fuzzing, as well as using
6
rebooting and forking to reset state, or not resetting it at all.
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-20-alxndr@bu.edu
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
14
tests/qtest/fuzz/Makefile.include | 3 +
15
tests/qtest/fuzz/i440fx_fuzz.c | 193 ++++++++++++++++++++++++++++++
16
2 files changed, 196 insertions(+)
17
create mode 100644 tests/qtest/fuzz/i440fx_fuzz.c
18
19
diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
20
index XXXXXXX..XXXXXXX 100644
21
--- a/tests/qtest/fuzz/Makefile.include
22
+++ b/tests/qtest/fuzz/Makefile.include
23
@@ -XXX,XX +XXX,XX @@ fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton
24
fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o
25
fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
26
27
+# Targets
28
+fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
29
+
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);
232
--
233
2.24.1
234
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
3
The virtio-net fuzz target feeds inputs to all three virtio-net
4
virtqueues, and uses forking to avoid leaking state between fuzz runs.
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-21-alxndr@bu.edu
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
12
tests/qtest/fuzz/Makefile.include | 1 +
13
tests/qtest/fuzz/virtio_net_fuzz.c | 198 +++++++++++++++++++++++++++++
14
2 files changed, 199 insertions(+)
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
18
index XXXXXXX..XXXXXXX 100644
19
--- a/tests/qtest/fuzz/Makefile.include
20
+++ b/tests/qtest/fuzz/Makefile.include
21
@@ -XXX,XX +XXX,XX @@ fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
22
23
# Targets
24
fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
25
+fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o
26
27
FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
28
29
diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c b/tests/qtest/fuzz/virtio_net_fuzz.c
30
new file mode 100644
31
index XXXXXXX..XXXXXXX
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);
113
+ }
114
+
115
+ /* Run the main loop */
116
+ qtest_clock_step(s, 100);
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);
233
--
234
2.24.1
235
diff view generated by jsdifflib
Deleted patch
1
From: Alexander Bulekov <alxndr@bu.edu>
2
1
3
The virtio-scsi fuzz target sets up and fuzzes the available virtio-scsi
4
queues. After an element is placed on a queue, the fuzzer can select
5
whether to perform a kick, or continue adding elements.
6
7
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
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>
11
---
12
tests/qtest/fuzz/Makefile.include | 1 +
13
tests/qtest/fuzz/virtio_scsi_fuzz.c | 213 ++++++++++++++++++++++++++++
14
2 files changed, 214 insertions(+)
15
create mode 100644 tests/qtest/fuzz/virtio_scsi_fuzz.c
16
17
diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include
18
index XXXXXXX..XXXXXXX 100644
19
--- a/tests/qtest/fuzz/Makefile.include
20
+++ b/tests/qtest/fuzz/Makefile.include
21
@@ -XXX,XX +XXX,XX @@ fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o
22
# Targets
23
fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o
24
fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o
25
+fuzz-obj-y += tests/qtest/fuzz/virtio_scsi_fuzz.o
26
27
FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
28
29
diff --git a/tests/qtest/fuzz/virtio_scsi_fuzz.c b/tests/qtest/fuzz/virtio_scsi_fuzz.c
30
new file mode 100644
31
index XXXXXXX..XXXXXXX
32
--- /dev/null
33
+++ b/tests/qtest/fuzz/virtio_scsi_fuzz.c
34
@@ -XXX,XX +XXX,XX @@
35
+/*
36
+ * virtio-serial 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 "tests/qtest/libqtest.h"
50
+#include "libqos/virtio-scsi.h"
51
+#include "libqos/virtio.h"
52
+#include "libqos/virtio-pci.h"
53
+#include "standard-headers/linux/virtio_ids.h"
54
+#include "standard-headers/linux/virtio_pci.h"
55
+#include "standard-headers/linux/virtio_scsi.h"
56
+#include "fuzz.h"
57
+#include "fork_fuzz.h"
58
+#include "qos_fuzz.h"
59
+
60
+#define PCI_SLOT 0x02
61
+#define PCI_FN 0x00
62
+#define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000)
63
+
64
+#define MAX_NUM_QUEUES 64
65
+
66
+/* Based on tests/virtio-scsi-test.c */
67
+typedef struct {
68
+ int num_queues;
69
+ QVirtQueue *vq[MAX_NUM_QUEUES + 2];
70
+} QVirtioSCSIQueues;
71
+
72
+static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev, uint64_t mask)
73
+{
74
+ QVirtioSCSIQueues *vs;
75
+ uint64_t feat;
76
+ int i;
77
+
78
+ vs = g_new0(QVirtioSCSIQueues, 1);
79
+
80
+ feat = qvirtio_get_features(dev);
81
+ if (mask) {
82
+ feat &= ~QVIRTIO_F_BAD_FEATURE | mask;
83
+ } else {
84
+ feat &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX));
85
+ }
86
+ qvirtio_set_features(dev, feat);
87
+
88
+ vs->num_queues = qvirtio_config_readl(dev, 0);
89
+
90
+ for (i = 0; i < vs->num_queues + 2; i++) {
91
+ vs->vq[i] = qvirtqueue_setup(dev, fuzz_qos_alloc, i);
92
+ }
93
+
94
+ qvirtio_set_driver_ok(dev);
95
+
96
+ return vs;
97
+}
98
+
99
+static void virtio_scsi_fuzz(QTestState *s, QVirtioSCSIQueues* queues,
100
+ const unsigned char *Data, size_t Size)
101
+{
102
+ /*
103
+ * Data is a sequence of random bytes. We split them up into "actions",
104
+ * followed by data:
105
+ * [vqa][dddddddd][vqa][dddd][vqa][dddddddddddd] ...
106
+ * The length of the data is specified by the preceding vqa.length
107
+ */
108
+ typedef struct vq_action {
109
+ uint8_t queue;
110
+ uint8_t length;
111
+ uint8_t write;
112
+ uint8_t next;
113
+ uint8_t kick;
114
+ } vq_action;
115
+
116
+ /* Keep track of the free head for each queue we interact with */
117
+ bool vq_touched[MAX_NUM_QUEUES + 2] = {0};
118
+ uint32_t free_head[MAX_NUM_QUEUES + 2];
119
+
120
+ QGuestAllocator *t_alloc = fuzz_qos_alloc;
121
+
122
+ QVirtioSCSI *scsi = fuzz_qos_obj;
123
+ QVirtioDevice *dev = scsi->vdev;
124
+ QVirtQueue *q;
125
+ vq_action vqa;
126
+ while (Size >= sizeof(vqa)) {
127
+ /* Copy the action, so we can normalize length, queue and flags */
128
+ memcpy(&vqa, Data, sizeof(vqa));
129
+
130
+ Data += sizeof(vqa);
131
+ Size -= sizeof(vqa);
132
+
133
+ vqa.queue = vqa.queue % queues->num_queues;
134
+ /* Cap length at the number of remaining bytes in data */
135
+ vqa.length = vqa.length >= Size ? Size : vqa.length;
136
+ vqa.write = vqa.write & 1;
137
+ vqa.next = vqa.next & 1;
138
+ vqa.kick = vqa.kick & 1;
139
+
140
+
141
+ q = queues->vq[vqa.queue];
142
+
143
+ /* Copy the data into ram, and place it on the virtqueue */
144
+ uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
145
+ qtest_memwrite(s, req_addr, Data, vqa.length);
146
+ if (vq_touched[vqa.queue] == 0) {
147
+ vq_touched[vqa.queue] = 1;
148
+ free_head[vqa.queue] = qvirtqueue_add(s, q, req_addr, vqa.length,
149
+ vqa.write, vqa.next);
150
+ } else {
151
+ qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
152
+ }
153
+
154
+ if (vqa.kick) {
155
+ qvirtqueue_kick(s, dev, q, free_head[vqa.queue]);
156
+ free_head[vqa.queue] = 0;
157
+ }
158
+ Data += vqa.length;
159
+ Size -= vqa.length;
160
+ }
161
+ /* In the end, kick each queue we interacted with */
162
+ for (int i = 0; i < MAX_NUM_QUEUES + 2; i++) {
163
+ if (vq_touched[i]) {
164
+ qvirtqueue_kick(s, dev, queues->vq[i], free_head[i]);
165
+ }
166
+ }
167
+}
168
+
169
+static void virtio_scsi_fork_fuzz(QTestState *s,
170
+ const unsigned char *Data, size_t Size)
171
+{
172
+ QVirtioSCSI *scsi = fuzz_qos_obj;
173
+ static QVirtioSCSIQueues *queues;
174
+ if (!queues) {
175
+ queues = qvirtio_scsi_init(scsi->vdev, 0);
176
+ }
177
+ if (fork() == 0) {
178
+ virtio_scsi_fuzz(s, queues, Data, Size);
179
+ flush_events(s);
180
+ _Exit(0);
181
+ } else {
182
+ wait(NULL);
183
+ }
184
+}
185
+
186
+static void virtio_scsi_with_flag_fuzz(QTestState *s,
187
+ const unsigned char *Data, size_t Size)
188
+{
189
+ QVirtioSCSI *scsi = fuzz_qos_obj;
190
+ static QVirtioSCSIQueues *queues;
191
+
192
+ if (fork() == 0) {
193
+ if (Size >= sizeof(uint64_t)) {
194
+ queues = qvirtio_scsi_init(scsi->vdev, *(uint64_t *)Data);
195
+ virtio_scsi_fuzz(s, queues,
196
+ Data + sizeof(uint64_t), Size - sizeof(uint64_t));
197
+ flush_events(s);
198
+ }
199
+ _Exit(0);
200
+ } else {
201
+ wait(NULL);
202
+ }
203
+}
204
+
205
+static void virtio_scsi_pre_fuzz(QTestState *s)
206
+{
207
+ qos_init_path(s);
208
+ counter_shm_init();
209
+}
210
+
211
+static void *virtio_scsi_test_setup(GString *cmd_line, void *arg)
212
+{
213
+ g_string_append(cmd_line,
214
+ " -drive file=blkdebug::null-co://,"
215
+ "file.image.read-zeroes=on,"
216
+ "if=none,id=dr1,format=raw,file.align=4k "
217
+ "-device scsi-hd,drive=dr1,lun=0,scsi-id=1");
218
+ return arg;
219
+}
220
+
221
+
222
+static void register_virtio_scsi_fuzz_targets(void)
223
+{
224
+ fuzz_add_qos_target(&(FuzzTarget){
225
+ .name = "virtio-scsi-fuzz",
226
+ .description = "Fuzz the virtio-scsi virtual queues, forking"
227
+ "for each fuzz run",
228
+ .pre_vm_init = &counter_shm_init,
229
+ .pre_fuzz = &virtio_scsi_pre_fuzz,
230
+ .fuzz = virtio_scsi_fork_fuzz,},
231
+ "virtio-scsi",
232
+ &(QOSGraphTestOptions){.before = virtio_scsi_test_setup}
233
+ );
234
+
235
+ fuzz_add_qos_target(&(FuzzTarget){
236
+ .name = "virtio-scsi-flags-fuzz",
237
+ .description = "Fuzz the virtio-scsi virtual queues, forking"
238
+ "for each fuzz run (also fuzzes the virtio flags)",
239
+ .pre_vm_init = &counter_shm_init,
240
+ .pre_fuzz = &virtio_scsi_pre_fuzz,
241
+ .fuzz = virtio_scsi_with_flag_fuzz,},
242
+ "virtio-scsi",
243
+ &(QOSGraphTestOptions){.before = virtio_scsi_test_setup}
244
+ );
245
+}
246
+
247
+fuzz_target_init(register_virtio_scsi_fuzz_targets);
248
--
249
2.24.1
250
diff view generated by jsdifflib
1
From: Alexander Bulekov <alxndr@bu.edu>
1
From: Stefano Garzarella <sgarzare@redhat.com>
2
2
3
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
3
The virtio-blk-vhost-vdpa driver in libblkio 1.3.0 supports the fd
4
passing through the new 'fd' property.
5
6
Since now we are using qemu_open() on '@path' if the virtio-blk driver
7
supports the fd passing, let's announce it.
8
In this way, the management layer can pass the file descriptor of an
9
already opened vhost-vdpa character device. This is useful especially
10
when the device can only be accessed with certain privileges.
11
12
Add the '@fdset' feature only when the virtio-blk-vhost-vdpa driver
13
in libblkio supports it.
14
15
Suggested-by: Markus Armbruster <armbru@redhat.com>
4
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
16
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
17
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
6
Message-id: 20200220041118.23264-23-alxndr@bu.edu
18
Message-id: 20230530071941.8954-3-sgarzare@redhat.com
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
---
20
---
9
docs/devel/fuzzing.txt | 116 +++++++++++++++++++++++++++++++++++++++++
21
qapi/block-core.json | 6 ++++++
10
1 file changed, 116 insertions(+)
22
meson.build | 4 ++++
11
create mode 100644 docs/devel/fuzzing.txt
23
2 files changed, 10 insertions(+)
12
24
13
diff --git a/docs/devel/fuzzing.txt b/docs/devel/fuzzing.txt
25
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
new file mode 100644
26
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX
27
--- a/qapi/block-core.json
16
--- /dev/null
28
+++ b/qapi/block-core.json
17
+++ b/docs/devel/fuzzing.txt
18
@@ -XXX,XX +XXX,XX @@
29
@@ -XXX,XX +XXX,XX @@
19
+= Fuzzing =
30
#
20
+
31
# @path: path to the vhost-vdpa character device.
21
+== Introduction ==
32
#
22
+
33
+# Features:
23
+This document describes the virtual-device fuzzing infrastructure in QEMU and
34
+# @fdset: Member @path supports the special "/dev/fdset/N" path
24
+how to use it to implement additional fuzzers.
35
+# (since 8.1)
25
+
36
+#
26
+== Basics ==
37
# Since: 7.2
27
+
38
##
28
+Fuzzing operates by passing inputs to an entry point/target function. The
39
{ 'struct': 'BlockdevOptionsVirtioBlkVhostVdpa',
29
+fuzzer tracks the code coverage triggered by the input. Based on these
40
'data': { 'path': 'str' },
30
+findings, the fuzzer mutates the input and repeats the fuzzing.
41
+ 'features': [ { 'name' :'fdset',
31
+
42
+ 'if': 'CONFIG_BLKIO_VHOST_VDPA_FD' } ],
32
+To fuzz QEMU, we rely on libfuzzer. Unlike other fuzzers such as AFL, libfuzzer
43
'if': 'CONFIG_BLKIO' }
33
+is an _in-process_ fuzzer. For the developer, this means that it is their
44
34
+responsibility to ensure that state is reset between fuzzing-runs.
45
##
35
+
46
diff --git a/meson.build b/meson.build
36
+== Building the fuzzers ==
47
index XXXXXXX..XXXXXXX 100644
37
+
48
--- a/meson.build
38
+NOTE: If possible, build a 32-bit binary. When forking, the 32-bit fuzzer is
49
+++ b/meson.build
39
+much faster, since the page-map has a smaller size. This is due to the fact that
50
@@ -XXX,XX +XXX,XX @@ config_host_data.set('CONFIG_LZO', lzo.found())
40
+AddressSanitizer mmaps ~20TB of memory, as part of its detection. This results
51
config_host_data.set('CONFIG_MPATH', mpathpersist.found())
41
+in a large page-map, and a much slower fork().
52
config_host_data.set('CONFIG_MPATH_NEW_API', mpathpersist_new_api)
42
+
53
config_host_data.set('CONFIG_BLKIO', blkio.found())
43
+To build the fuzzers, install a recent version of clang:
54
+if blkio.found()
44
+Configure with (substitute the clang binaries with the version you installed):
55
+ config_host_data.set('CONFIG_BLKIO_VHOST_VDPA_FD',
45
+
56
+ blkio.version().version_compare('>=1.3.0'))
46
+ CC=clang-8 CXX=clang++-8 /path/to/configure --enable-fuzzing
57
+endif
47
+
58
config_host_data.set('CONFIG_CURL', curl.found())
48
+Fuzz targets are built similarly to system/softmmu:
59
config_host_data.set('CONFIG_CURSES', curses.found())
49
+
60
config_host_data.set('CONFIG_GBM', gbm.found())
50
+ make i386-softmmu/fuzz
51
+
52
+This builds ./i386-softmmu/qemu-fuzz-i386
53
+
54
+The first option to this command is: --fuzz_taget=FUZZ_NAME
55
+To list all of the available fuzzers run qemu-fuzz-i386 with no arguments.
56
+
57
+eg:
58
+ ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=virtio-net-fork-fuzz
59
+
60
+Internally, libfuzzer parses all arguments that do not begin with "--".
61
+Information about these is available by passing -help=1
62
+
63
+Now the only thing left to do is wait for the fuzzer to trigger potential
64
+crashes.
65
+
66
+== Adding a new fuzzer ==
67
+Coverage over virtual devices can be improved by adding additional fuzzers.
68
+Fuzzers are kept in tests/qtest/fuzz/ and should be added to
69
+tests/qtest/fuzz/Makefile.include
70
+
71
+Fuzzers can rely on both qtest and libqos to communicate with virtual devices.
72
+
73
+1. Create a new source file. For example ``tests/qtest/fuzz/foo-device-fuzz.c``.
74
+
75
+2. Write the fuzzing code using the libqtest/libqos API. See existing fuzzers
76
+for reference.
77
+
78
+3. Register the fuzzer in ``tests/fuzz/Makefile.include`` by appending the
79
+corresponding object to fuzz-obj-y
80
+
81
+Fuzzers can be more-or-less thought of as special qtest programs which can
82
+modify the qtest commands and/or qtest command arguments based on inputs
83
+provided by libfuzzer. Libfuzzer passes a byte array and length. Commonly the
84
+fuzzer loops over the byte-array interpreting it as a list of qtest commands,
85
+addresses, or values.
86
+
87
+= Implementation Details =
88
+
89
+== The Fuzzer's Lifecycle ==
90
+
91
+The fuzzer has two entrypoints that libfuzzer calls. libfuzzer provides it's
92
+own main(), which performs some setup, and calls the entrypoints:
93
+
94
+LLVMFuzzerInitialize: called prior to fuzzing. Used to initialize all of the
95
+necessary state
96
+
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
135
--
61
--
136
2.24.1
62
2.40.1
137
diff view generated by jsdifflib